feat(sessions): full prune-filter set + --redact on sessions export

- export now shares _add_session_filter_args / build_prune_filters with
  prune/archive: AGE grammar (5h/2d/1w/ISO) on --older-than plus the full
  filter set (--model, --provider, --min-messages, --min-cost, --branch,
  --chat-id, ...) for both JSONL and md/qmd bulk exports; --dry-run works
  on JSONL too; removes the one-off list_export_candidates helper.
- new --redact flag runs exported message content and tool output through
  force-mode secret redaction (agent.redact) for jsonl, md, and qmd.
- docs EN + zh-Hans updated; new tests for AGE grammar, extended filters,
  filtered JSONL, and redaction.
This commit is contained in:
teknium1 2026-07-07 06:15:53 -07:00 committed by Teknium
parent 51dd5695ec
commit acfefa4fda
7 changed files with 339 additions and 117 deletions

View file

@ -216,6 +216,34 @@ def verify_export_file(path: Path | str, session: dict[str, Any]) -> tuple[bool,
return True, "ok"
def redact_session_data(session: dict[str, Any]) -> dict[str, Any]:
"""Return a deep copy of a session export dict with secrets redacted.
Runs every message's content and tool-call arguments through the
force-mode redaction pass (``agent.redact.redact_sensitive_text``), so
API keys, tokens, and credentials that appeared in tool output never
land in plaintext export files. Force mode ignores the user's global
``security.redact_secrets`` preference an explicit ``--redact`` export
must never emit raw secrets.
"""
from agent.redact import redact_sensitive_text
def _clean(value: Any) -> Any:
if isinstance(value, str):
return redact_sensitive_text(value, force=True)
if isinstance(value, list):
return [_clean(v) for v in value]
if isinstance(value, dict):
return {k: _clean(v) for k, v in value.items()}
return value
redacted = dict(session)
for key in ("messages", "segments"):
if key in redacted and redacted[key] is not None:
redacted[key] = _clean(redacted[key])
return redacted
def write_session_markdown(
session: dict[str, Any], output_dir: Path | str, *, fmt: str = "md", force: bool = False
) -> Path: