Skip to content

feat: hold the conversation in the client and send it with every turn - #22

Merged
RohanReddy44 merged 3 commits into
mainfrom
feat/experience-api-3-session-history
Sep 24, 2026
Merged

RohanReddy44 merged 3 commits into
mainfrom
feat/experience-api-3-session-history

Conversation

@RohanReddy44

@RohanReddy44 RohanReddy44 commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

Phase 3 of the Experience API work: the client is now a conversation, not a series of one-off questions.

  • A new session id on clear chat. The session id lives exactly as long as the history does (contract §3). It was kept across clear chat before.
  • History is sent with every turn. buildHistory reads it off the message list with one rule: a user text message counts when the next message is an assistant card that is not an error. That gives §4.1 exactly. Failed turns and their questions stay out, so Retry sends the question once. Refusals and questions back stay in. The question being sent now stays out and goes as query. Six tests.
  • Retry reuses the message id. One question, one messageId, however many attempts (§3).

How to check

npm run dev against the local API, or stubs on:

  1. Ask three questions. In the request bodies, history grows by two entries each time.
  2. With stubs on, the fifth reply fails. The next request's history does not contain that question. Press Retry: the request carries the same messageId as the failed attempt.
  3. Clear chat, ask again: a different sessionId, empty history.

Verified

  • Lint, typecheck, knip, tests (54, six new) pass at each commit via the pre-commit hook.
  • Not run in Actions.

🤖 Generated with Claude Code

RohanReddy44 and others added 3 commits September 24, 2026 14:58
Clearing the chat emptied the messages but kept the session id, so
the next question went to the DSS under the old conversation's id.
The contract ties the two together: the session id lives exactly as
long as the history does, and clear chat is one of the two things that
ends it, the other being a refresh (§3).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every turn went out with an empty history, so the DSS saw each
question on its own and a follow-up like "and tomorrow?" had nothing
to refer to. The API stores nothing by design (contract §1), so the
client is the only place the conversation exists, and it has to send
the whole of it each time.

buildHistory() reads it off the message list with one rule: a user
text message counts when the next message is an assistant card that
is not an error. That gives §4.1 exactly. A failed turn and its
question stay out, so Retry sends the question once. The question
being sent now has no answer yet and stays out; it goes as `query`.
Refusals and questions back stay in, because the DSS needs the
question it asked to read the reply.

The list is read after the new user message is appended, at which
point nothing is streaming, so every card body is the final text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Retry re-entered sendText, which minted a fresh user message and with
it a fresh messageId, so the API and DSS saw two unrelated ids for one
question. The contract wants one: a retry of the same message reuses
its messageId and gets a new transactionId from the API, so the two
attempts can be told apart in traces while still being one message
(§3).

Retry now takes the id from the user message it removes and passes it
back into sendText. Nothing else calls sendText with an id.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@RohanReddy44
RohanReddy44 changed the base branch from fix/chat-call-review to main September 24, 2026 16:07
@RohanReddy44
RohanReddy44 merged commit 4899787 into main Sep 24, 2026
4 checks passed
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