Skip to content

feat: add /acp-export command (session handoff export) - #272

Open
ranxianglei wants to merge 3 commits into
masterfrom
2026-09-01_acp-export-command
Open

feat: add /acp-export command (session handoff export)#272
ranxianglei wants to merge 3 commits into
masterfrom
2026-09-01_acp-export-command

Conversation

@ranxianglei

@ranxianglei ranxianglei commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

Ports the upstream billion-context export command (session handoff export) into the Pi adapter, per #271.

What it does

/acp-export (named acp-export to avoid colliding with Pi's built-in /export):

  • No selector — lists ACP-managed sessions with title / label / tokens / blocks.
  • <id> — renders the folded view: block summaries in place of compressed ranges, in conversation order, via prune().
  • <id> --full — renders the original messages from the session.
  • --output <file> — writes the markdown file; without it the document is sent to the transcript (filtered from LLM context) or notified.

Design notes

  • The Pi adapter keeps the full conversation in each session's .jsonl and ACP state in an adjacent <sessionFile>.acp.json (written every turn), so both the folded and full views are always available — no snapshot cache or v2 fallback needed (simpler than the proxy).
  • Sessions are enumerated by scanning the session directory for *.acp.json files (any ACP-active session has one).
  • Export documents are tagged acp-export and excluded from LLM-context projection (same as acp-status panels).
  • Output contains no secrets or environment-specific paths (parity with upstream).

Delegation to acp-kernel (follow-up)

Per the owner's request, the handoff markdown rendering is now shared in the kernel instead of duplicated across the proxy and this adapter:

  • src/export.ts now calls kernel renderHandoff({coreMessages, state, full, meta}) and kernel matchSession(all, selector, labelOf) (added in acp-kernel#183); the local renderHandoff / renderMessage / matchSession copies are removed (net −52 lines).
  • Bumps acp-kernel 0.0.480.0.49 (the release that ships the handoff exports). package-lock.json is refreshed once 0.0.49 is on npm.
  • Companion PRs: acp-kernel#183 (the shared renderer) and billion-context#434 (the proxy delegating to the same renderer).

Verification

  • npm run typecheck — clean
  • npm test — 472 pass, 0 fail, 3 skipped
  • npm run build — success (acp-kernel inlined)

New tests: tests/export-cmd.test.ts (10 tests: arg parsing, list, folded, full, output, no-match, empty store, ambiguous selector, command wiring). Updated tests/integration.test.ts command count to 6.

Dependency / merge order

Blocked on acp-kernel#183 merging + acp-kernel 0.0.49 publishing. Until then CI is red at npm ci (cannot resolve 0.0.49). After 0.0.49 is live, run npm install to refresh the lockfile and the PR goes green.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

📦 Built Extension Artifact

Branch: 2026-09-01_acp-export-command (8a355a7)

Option A — Install from npm PR tag (recommended)

pi install npm:billion-context-pi@pr-272

Each push to this PR publishes a new version under the pr-272 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pi-pr272.tgz
pi install ./package

This comment is automatically updated on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 ework agent · qwen3.8-27b

[bot] 🏷 PR reviewed and verified on 2026-09-01_acp-export-command (head 4b57746).

Verification (local, on the PR branch)

  • npm run typecheck — clean
  • npm test — 472 pass, 0 fail, 3 skipped
  • npm run build — success (acp-kernel inlined into dist/index.js, zero runtime deps preserved)
  • GitHub CI: all 9 checks green (test ubuntu/windows × Node 22/24, e2e ubuntu/windows, docker, build-artifact, pr-validation)

Code review

  • All host APIs used by src/export.ts verified against the installed @earendil-works/pi-coding-agent types: SessionManager.open(), getSessionDir(), getSessionFile(), getSessionId(), getSessionName(), buildContextEntries() all exist with the expected signatures.
  • State-file resolution is consistent: SessionStateStore.load(sessionFile, id) reads <sessionFile>.acp.json (src/state.ts:18-21), which matches the *.acp.json directory scan used for session enumeration.
  • prune(coreMessages, state) matches the kernel signature (prune(messages: CoreMessage[], state: CompressionState, options?)); message matching goes through entry ids + state.messageRefs, same as the live pipeline.
  • The folded-view test is a real end-to-end check (context handler → compress → re-read from disk → prune): summary present, compressed original folded away, uncompressed tail retained. Good.
  • Command wiring mirrors the acp-status panel pattern (issue Show /acp-status as persistent output instead of a notification #255) exactly, including the sendMessage → notify fallback.

One gap found — fixed

The src/messages.ts change adding ACP_EXPORT_CUSTOM_TYPE to CONTEXT_EXCLUDED_CUSTOM_TYPES (keeping export docs out of LLM-context projection) had no regression test — only acp-status was covered in tests/messages.test.ts. Without it, a future refactor could silently let full handoff documents leak into model context.

Extended the existing exclusion test to include an acp-export custom entry alongside the acp-status panel. Pushed as 803e731 ("test: cover acp-export exclusion from LLM-context projection"); full suite re-run: 472 pass, 0 fail, 3 skipped.

Minor notes (no action needed)

  • Each /acp-export without --output persists the full handoff document into the session's .jsonl (custom message, same persistence semantics as acp-status panels). It's excluded from LLM context so there's no context cost, but long sessions grow the session file by the document size per export. Reasonable trade-off given Pi has no stdout surface for slash commands; just worth knowing.
  • Unknown flags (e.g. /acp-export --foo) are treated as a session selector and fail with "no session matches" — acceptable, matches the simple parser style.

Ready for merge (merge stays human-only per repo policy).

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 ework agent · qwen3.8-27b

[bot] 🏷 Follow-up: CI on the updated head 803e731 is fully green — all 9 checks passed (test ubuntu/windows × Node 22/24, e2e ubuntu/windows, docker, build-artifact, pr-validation).

@ranxianglei

Copy link
Copy Markdown
Owner Author

冲突了 解决下

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 ework agent · qwen3.8-27b

[bot] 🏷 冲突已解决 ✅

冲突原因:master 已前进到 v0.1.56(bcd841e,含 delegate fix + release),release commit 把 acp-kernel pin 到了 0.0.50;而本分支的 398d6c8 把 pin 刷到了 0.0.49package.jsonpackage-lock.json 各一处 pin 冲突。

解决方式:取 master 侧的 0.0.50(更新版本,且 d4612c7 依赖的 handoff API renderHandoff/matchSession/defaultCountTokens 是 0.0.49 引入的,0.0.50 是其超集)。merge base 以来两条分支对这两个文件的改动只有 acp-kernel pin,无其他语义冲突;tests/integration.test.ts 自动合并干净。

验证(本地,合并后):

  • npm ci — lockfile 与 package.json 一致,安装 acp-kernel@0.0.50
  • npm run typecheck — clean(证明 0.0.50 类型导出包含全部 handoff API)
  • npm test — 473 pass / 0 fail / 3 skipped(master 带入的新测试也全过)
  • npm run build — success(dist/index.js 548.23 KB,acp-kernel inlined)

已推送 merge commit ec43f082026-09-01_acp-export-command。CI 跑完我再确认一遍结果。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 ework agent · qwen3.8-27b

[bot] 🏷 确认:merge commit ec43f08 上 CI 全绿 —— 9/9 checks passed(test ubuntu/windows × Node 22/24、e2e ubuntu/windows、docker、build-artifact、pr-validation)。PR 状态已变为 mergeable ✅(head ec43f08)。可以合并了(合并操作按仓库规范由人工执行)。

ework-agent added 3 commits September 7, 2026 22:07
Port the upstream billion-context export command into the Pi adapter.

- /acp-export lists ACP-managed sessions (title/label/tokens/blocks).
- /acp-export <id> renders the folded view (block summaries in place of
  compressed ranges, in conversation order) via prune().
- /acp-export <id> --full renders the original messages from the session.
- --output writes the markdown file; without it the document is sent to
  the transcript (filtered from LLM context) or notified.
- Empty store / no-match / ambiguous-selector errors match upstream.
- Output contains no secrets or environment-specific paths.
Replaces the local renderHandoff/renderMessage/matchSession in src/export.ts
with the kernel's shared implementations (acp-kernel PR #183), so the proxy
and the Pi adapter no longer duplicate the handoff markdown logic.

- exportSession now calls kernel renderHandoff({coreMessages, state, full, meta})
  and kernel matchSession(all, selector, labelOf).
- Bumps acp-kernel 0.0.48 -> 0.0.49 (the release that ships the handoff
  exports). package-lock.json is refreshed once 0.0.49 is published on npm.

Local pre-validation against acp-kernel#183's build: typecheck clean,
472/475 tests pass (3 skipped), build success.
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.

1 participant