Skip to content

feat: import codex sessions - #1602

Open
curryxjh wants to merge 6 commits into
oceanbase:masterfrom
curryxjh:feat/import-codex-sessions
Open

curryxjh wants to merge 6 commits into
oceanbase:masterfrom
curryxjh:feat/import-codex-sessions

Conversation

@curryxjh

Copy link
Copy Markdown
Contributor

Which issue or RFC does this PR close?

Part of #1300.
Refs #1574.

Closes #.

Rationale for this change

This PR adds the first opt-in import path for pre-install agent session history, starting with Codex. It lets users import historical Codex user prompts as ordinary Content Sources so existing prompt history can participate in PowerContext processing without bypassing the Source pipeline.

The implementation follows the constraints from #1574: Codex-only, explicit/default-off, user prompts only, no assistant replies, no direct Memory writes, no default Scope fallback, and idempotent overlap with live Codex prompt capture.

What changes are included in this PR?

  • Adds powercontext import-sessions --host codex.
  • Reads Codex JSONL session files from CODEX_HOME or ~/.codex.
  • Imports only real user prompts and skips synthetic environment context and assistant messages.
  • Resolves Scope using Codex session/workspace bindings, or imports into --scope-id when provided.
  • Writes prompts through the existing Content Source capture API.
  • Uses the same Codex source identity and metadata shape as the live Codex hook to keep repeated imports and live/import overlap idempotent.
  • Adds --dry-run, --flush, and --checkpoint-file.
  • Records per-item accepted/skipped/failed results and keeps a persistent checkpoint for resume.
  • Skips malformed session files while reporting file-level failures.
  • Updates English and Chinese Sources documentation.
  • Adds focused tests for reader behavior, scope resolution, checkpoint resume, dry-run, bad files, CLI JSON output, and live capture overlap.

Are there any user-facing changes?

Yes. A new explicit CLI command is available:

powercontext import-sessions --host codex

The command is not run automatically. It only imports when the user invokes it. It writes ordinary Content Sources only; Memory extraction is requested only when --flush is passed.
There are no breaking API changes and no changes to existing public persisted formats.

How was this change tested?

Added and updated tests in tests/test_session_import.py.
Commands run:

UV_CACHE_DIR=/private/tmp/powercontext-uv-cache uv run pytest tests/test_session_import.py
UV_CACHE_DIR=/private/tmp/powercontext-uv-cache uv run pytest tests/codex_plugin/test_recall.py -k capture
UV_CACHE_DIR=/private/tmp/powercontext-uv-cache uv run ruff check src/powercontext/client/session_import.py src/powercontext/client/cli.py tests/test_session_import.py
UV_CACHE_DIR=/private/tmp/powercontext-uv-cache uv run ruff format --check src/powercontext/client/session_import.py src/powercontext/client/cli.py tests/test_session_import.py
UV_CACHE_DIR=/private/tmp/powercontext-uv-cache uv run ty check src/powercontext/client/session_import.py src/powercontext/client/cli.py tests/test_session_import.py
UV_CACHE_DIR=/private/tmp/powercontext-uv-cache uv run powercontext import-sessions --help

AI usage statement

Implemented with assistance from OpenAI Codex.

@curryxjh

Copy link
Copy Markdown
Contributor Author

Hardened R8 e2e polling against transient TransportError.

resolved_scope_id = scope_id or await _resolve_codex_scope(client, prompt)
if resolved_scope_id is None:
return _PromptProcessingResult(_item_result(prompt, SessionImportItemStatus.SKIPPED, reason="unresolved_scope"))
source_id = prompt.source_id_for_scope(resolved_scope_id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 修复 redaction 后的 live/import 重叠冲突
这里的 source_id 仍由原始 prompt.content 生成,但下面真正写入的是 redact_known_secrets(prompt.content)。live Codex hook 用同一组 scope_id/session_id/turn_id/prompt 生成相同 source_id,却提交原始 prompt;如果同一 prompt 已被 live capture 写入且包含会被 redaction 替换的 secret,导入器会用同一个 source_id 提交 [REDACTED] 后的不同 content,服务端会返回 409 source_conflict,该项会被记为 capture_failed,与 PR 描述中的 live/import overlap idempotent 不一致。建议让 identity 和实际写入内容使用同一份规范化文本,或显式处理这个 redaction conflict,并补一个“已存在 raw live capture + 导入 redacted prompt”的回归测试。

@Teingi Teingi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed dd2a9635. The 53 focused tests, Ruff checks, and type checks passed. Additional SQLite/HTTP probes and checks against local Codex session formats reproduced the issues below.

Comment thread src/powercontext/client/session_import.py Outdated
Comment thread src/powercontext/client/session_import.py Outdated
Comment thread src/powercontext/client/session_import.py Outdated
Comment thread src/powercontext/client/session_import.py Outdated
if resolved_scope_id is None:
return _PromptProcessingResult(_item_result(prompt, SessionImportItemStatus.SKIPPED, reason="unresolved_scope"))
source_id = prompt.source_id_for_scope(resolved_scope_id)
if _checkpoint_status(checkpoint, source_id) == SessionImportItemStatus.ACCEPTED.value:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Keep pending flush work separate from capture checkpoints

Accepted checkpoint entries return without adding their Scope to touched_scopes. Importing without --flush and then rerunning with it therefore requests no extraction, despite the CLI suggesting that workflow. The same happens after a flush failure: I injected a 503 after successful capture, and rerunning with --flush made no second flush attempt. Please preserve pending extraction work independently of accepted Source writes so adding or retrying --flush can finish it without recapturing Sources.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Recover pending flush work after session archival

Rechecked at eab34c16: adding or retrying --flush works while the session file remains under sessions. Pending positions are still rebuilt only from the current scan, though, which excludes archived_sessions. In a SQLite-backed HTTP probe, I imported three Sources without flushing, moved the session to archived_sessions, and reran with --flush. It reported zero flushed Scopes and left all three Sources unprocessed; a manual flush then started processing them. Please restore pending Scope/position targets directly from the checkpoint, independently of whether the original session file is still discoverable, and cover this archive-and-retry case.

Comment thread src/powercontext/client/session_import.py Outdated

@Teingi Teingi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed eab34c16. All 60 related tests, Ruff checks, formatting, and targeted type checks passed. Additional SQLite-backed HTTP probes reproduced the turn-identity issue below and the archive-and-flush recovery issue described in the existing checkpoint thread.

)
cwd = _string(typed_payload.get("cwd")) or cwd
continue
if record_type in {"turn_context", "task_started"}:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Read task_started from the event payload

Codex logs encode this as type: "event_msg" with payload.type: "task_started", so the top-level check never updates the turn ID. When a user message has no turn ID of its own and the new ID is supplied by this event, the reader retains the previous turn ID. A SQLite-backed HTTP probe with the same prompt in two turns produced turn-a, turn-a: only one Source was stored, and the second prompt was skipped as checkpoint_accepted. Please recognize the nested event and use that structure in the regression fixture, which currently puts task_started at the top level.

@PsiACE PsiACE left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep this PR focused on importing historical user prompts as Content Sources. Two changes should block merging:

  • Remove the --flush loop from the importer. It consumes the Scope’s shared journal, potentially processing unrelated pending Sources, without a total processing budget. Extraction should remain a separate operation.
  • Validate the checkpoint’s destination before trusting accepted entries. Switching servers with the same Scope and Source IDs can otherwise skip Sources that the destination has never received. Stored positions also belong to the original destination.
Read local sessions
        |
        v
Filter + redact + resolve Scope
        |
        v
Validate checkpoint destination
        |
        v
Capture Sources + save receipts
        |
        v
Report results

Please document that scanning happens on the CLI machine and supports a remote Server. Also clarify that omitting --flush does not prevent configured background processing.

Session/time filters and finer limits can follow separately. Host adapter separation is tracked in #1680 rather than expanding this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants