Skip to content

fix(chat): history paging, cursor errors, gap reload, full snapshots, prompt outcome - #75

Merged
guohai merged 3 commits into
mainfrom
fix/chat-history
Oct 17, 2026
Merged

guohai merged 3 commits into
mainfrom
fix/chat-history

Conversation

@guohai

@guohai guohai commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Fixes six agent chat defects found while answering Astation's agent-control API proposal (#74). They stay inside the current protocol; the r8 additions wait for agreement with Astation.

  1. Paging. open{before} on an open conversation no longer resets live sequence numbers. The page arrives as message{history:true} at the next sequence numbers and ends with a sequenced status carrying page{before, count, start}. Cards are filtered before the page limit.
  2. Unknown cursor. A before cursor that is no longer retained, or that comes from another generation, gets error{code:"stale_target"} instead of an empty page.
  3. Backlog gaps. When the worker reports a gap, the broker reloads the full history and the turn state from the worker. Open conversations get "history incomplete … reopen" and are closed. A worker that is lost, or a reload that is too big, also falls back to "incomplete" and never leaves a silent hole.
  4. Receipt outcome. A completed prompt receipt's detail is done or interrupted, and only that word is journaled. Chat complete.outcome follows it, also after a broker restart and on duplicate submits.
  5. Broker restart. The worker's snapshot carries its full retained history (500 messages / 8 MiB, in chunks of at most 1 MiB) and the approval cards. A card still pending at adoption is closed with "Atem's runtime restarted; answer in the terminal".
  6. Docs. Codex open before its first prompt is documented correctly.

Docs: agent-chat-host.md, agent-chat-api.md, the handoff, contract §6/§7, AGENTS.md.

Tests:

  • New tests for every fix; fixes 1, 2 and 4 were checked to fail on the old code.
  • Full cargo test passed twice (1661 unit tests plus every binary suite).
  • runtime_fixtures_binary passed 3/3.
  • Real Claude and Codex runs passed 3/3.
  • Reviewed. No critical issues; the one important finding (empty pages) was fixed.

For Astation:

  • Read status.page to know where a page ends.
  • Treat stale_target on open{before} as "reopen without before".
  • Read the completed prompt receipt's detail as the outcome.

Generated with SMT smt@agora.build

…ots, prompt outcomes

- open{before} on an open conversation pages without `opened` or a reset:
  the older page continues the conversation's live sequence.
- A before cursor that is not retained is refused with
  error{code:"stale_target"} instead of an empty page.
- A worker chat batch with gap > 0 makes the broker reload the worker's
  whole history (request_chat_history -> chat_history), then open
  conversations get error "the chat history was incomplete ..." and close.
- The worker's chat_snapshot carries its whole retained history
  (500 messages / 8 MiB) in chat_history_part lines of at most 1 MiB, with
  approval cards: the broker copies every card to its worker (chat_card);
  a card still pending at adoption becomes closed.
- A completed prompt receipt's detail is the turn's outcome, "done" or
  "interrupted" (journaled as that word only); chat complete.outcome
  follows it, also for a duplicate after a broker restart.

🤖 Built with SMT <smt@agora.build>
…outcome

Update agent-chat-host.md (also: Codex open before its first hook opens
with status unknown, not busy), agent-chat-api.md, the host handoff,
contract §6/§7 (completed prompt receipt detail) and AGENTS.md.

🤖 Built with SMT <smt@agora.build>
…eload

- open{before} on an open conversation ends its page with a sequenced
  status carrying page {before, count, start}; an empty page is the
  marker alone (start: true), never silence.
- Cards are filtered for view-only attachments before the page limits
  (ChatSession::page), also for the newest page on opened.
- open may name the generation of its before; another one is
  stale_target (ChatSession::retains checks it).
- chat_history carries the worker's chat state; ChatSession::catch_up
  completes a turn end the broker missed (and its prompt receipt) and
  follows the worker's status.
- A reload lost with its worker, or over 1000 messages, drops its parts
  and falls back to "the chat history was incomplete" error and close.
- Tests: page marker (non-empty and empty start), view-only card pages,
  gap reload restores missed messages with the broker's card state
  winning, worker lost mid-reload, oversized reload, catch-up, a
  duplicate chat prompt after a restart stays interrupted, and no prompt
  text in the journal files.

🤖 Built with SMT <smt@agora.build>
@guohai
guohai merged commit 53577a7 into main Oct 17, 2026
3 checks passed
@guohai
guohai deleted the fix/chat-history branch October 17, 2026 17:34
guohai added a commit that referenced this pull request Oct 17, 2026
…fixtures

Answer Astation's review of response r1 with the controller's rulings:
- Version admission: hello carries revision 8; an r8 host refuses a hello
  without it with unsupported_version before any attachment; welcome
  echoes revision and features; removed forms (open.before,
  open.generation, status.page) are refused explicitly. Envelope stays v:1.
- Ordering: every history page is snapshotted and enqueued in one broker
  step in the conversation's sequenced stream; a message's sequence is its
  position, so a page never replaces a newer version.
- Bounded recovery: one selector of before/turn_id/request_id/next,
  validated; pages bounded by 50 messages, 256 KiB content and 1 MiB
  serialized; opaque context-bound next tokens (stale_target); end and
  coverage complete|partial|unavailable; reply_message_ids <= 32 + more.
- Truncation is persistent coverage information; backlog holes are
  persistent holes; resync only on a sequence gap, fence, generation
  change or close with a reason. The gap reload's error + close (#75)
  becomes reload + coverage event in r8.

atem-session-v1.md gains an "r8 draft" appendix (r7.2 stays
authoritative). Twelve r8-* fixtures, marked requires: "r8" in schema.md;
the fixture runners parse them and report them NOT RUN (requires r8).

🤖 Built with SMT <smt@agora.build>
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