Repository navigation
Native usage recovery: agree on identity and per-turn reconciliation #1047
Description
This follows the accounting review in #1029. The filter fix is now independent. Native recovery needs an agreed reconciliation policy before another implementation is proposed.
I reproduced the relevant behavior with claude-agent-acp 0.97.0 and Agent SDK 0.3.310 against a loopback Messages API, using fresh homes and synthetic token counts:
| Case | Result |
|---|---|
| Foreground sub-agent | PromptResponse.usage reports 12,104 main-loop tokens; the native child adds 6,052. |
| Clear-context plan approval | The public ACP id stays fixed. Raw system/init and result frames expose the new private id, which matches the new native file. |
/clear |
The same public/private split occurs, and the raw private id again matches the new file. |
| Close, resume, then a sub-agent | The native id stays unchanged. The first resumed prompt reports 12,136 tokens; another 6,068 child tokens appear in the raw cumulative delta but are missing from that response's _meta.quota.model_usage. |
| Normal load | The private id still matches the requested native id, including with the sub-agent transcript capability enabled. |
| Manual compaction | Prompt usage is zero while _meta.quota.model_usage reports nonzero work. It cannot be treated as the same counter source. |
The existing _claude/sdkMessage opt-in provides useful identity evidence. Codeg already uses it for built-in Claude. ACP 3.4 also preserves ContentChunk.messageId in protocol v1; the transcript batcher needs to keep different ids separate, which is included in the recording follow-up.
My proposed recovery contract is:
- Persist minimal identity and counter provenance for the official adapter: public/private session ids, SDK message/result ids, tool ids and explicitly identified counter sources. Keep the links independently of registration and transcript cleanup, scoped to the conversation and isolated home. Do not retain additional SDK response text.
- Keep native message observations and ACP prompt observations distinct. Deduplicate only proven identical events, including copied fork prefixes. Reconcile main-loop and sub-agent contributions per turn with explicit coverage evidence; do not choose a source for the whole session, take a maximum, or add potentially overlapping totals.
- Missing files must retain prior observations while allowing new turns to add evidence. A rebuild or same-id re-registration must not replace the old sessions with the current session's subset. A different home is a new source identity.
- When historical identities or coverage cannot be established, mark the result partial and retain the unreconciled observations. Do not guess a successor by timestamp or manufacture a complete total. Resume, compaction, cancellation and autonomous work need explicit coverage rules before this is enabled.
Is extending the existing raw SDK identity opt-in to official custom Claude adapters acceptable, with only those minimal fields persisted? I would also like to settle the partial-history behavior before implementing the reconciler; old transcripts without sufficient identity cannot support the same guarantees as newly recorded ones.
The implementation gates would cover a reset-spanning turn, two nearby clears in one project, resume/fork, stopped and autonomous work, compaction, missing native files, and deletion/re-registration. These identities also matter to handoff: a native transfer must not copy a file that no longer represents the live session; custom-agent handoffs can use their complete saved Codeg context.
Activity