Repository navigation
hives: a conversation grafts whole whatever shape it comes back in, and a tool the server ran reaches the client in both APIs - #101
Merged
Conversation
… message keeps its sigil A client that hands the model a result it made up, a finished job or a note as a tool call and its answer, sends a conversation that ends in tool results and no new words. The claude-cli nurse refused every such prompt with cannot-grow-on, so wryme's deliveries failed. claude-cli now grows on from a graft that ends in tool results. They stay the stitched transcript's last entries, and the cell asks claude to answer them with a continue line, since claude takes a turn only from a user message. claude, resuming a transcript that ends in tool results, first writes an empty reply of its own, "No response requested."; the line says that reply was not the model's. A prompt with no words claude cannot grow on from, words to continue, plain text or a scion, is refused with cannot-grow-on by the nurse, as ollama's does. A held transcript is given the tool results its last turn lacks, and the continue line opens no turn of its own. openai-server found a conversation's sigil by a trace of each turn's words, calls and answers in the order they grew. A client that keeps a turn as one message, all its words and then all its calls, never matched, so every request began a new sigil and a new claude session. A turn's words, calls and answers are now each read in order but apart, its words whitespace aside.
…nd a tool the server ran reaches the client in both APIs A client that runs its own tools now has its conversation grafted as it was, by every nurse that grows it. claude-cli found the tool results a held transcript lacked by counting them, so answers the client never saw, a tool the server ran or a call the nest gave up on, hid a delivery; it now matches them by the tool and what it answered. openai-server's own nurse reused a sigil it had grown the same number of blooms of, so a delivery added to the last bloom never went upstream; those answers now join it by call id. ollama refused any conversation that ended in tool answers; it now grows on from them through /api/chat. openai-server held a caller's call thirty minutes while the nest gave up after ten and a half, so an answer between went to a call no one waited on; the hold is now ten minutes, and a later answer grows on from the graft. A delivery changed a conversation's trace, so each one began a new sigil and a new claude session. A conversation that ends in tool answers now also finds the sigil it had before them, and grows on in that session. A tool the server ran, an attendant's or the nurse's own, now reaches a chat completions client as it reaches a Responses one: on the message as mcp_calls, each shaped as an mcp_call item, streamed as a delta like tool_calls, and read back as what grew. The server remembers them in the order they grew, and leaves them out of the trace, so a client finds its sigil whether it sends them back or not. The doc comments this branch added are gone; the headers say only what the code cannot.
adiled
added a commit
that referenced
this pull request
Oct 28, 2026
… words, and a call keeps claude's own id (#102) ## Why Testing #101 in wryme showed one conversation spread over four claude sessions in seven minutes. No claude process piled up, and nothing was lost (each new session got the whole conversation stitched in), but every request that missed began a new sigil and a cold claude session. openai-server (and, since #100, anthropic-server and vercel-ai) found a conversation's sigil by a hash of its whole history: system prompt, every turn's words, every tool id. That is not what a sigil was meant to be (the forager picks the sid), and clients reshape history all the time. What made wryme miss: - on the Responses API, wryme sends claude's tool calls between rounds of a turn but not the words claude said before them, then replays the turn with them; - its `[bookkeeping]` note rides in the system prompt for one request; - `tinker_keep` drops older tool pairs, and a turn can come back squashed into one message. ## What changes **A sigil is found by what the client echoes faithfully** (openai-server, anthropic-server, vercel-ai) 1. A held call's sid, then `previous_response_id` / `conversation`, as before. 2. A line the client names: `X-Hum-Sid` (vercel-ai: `providerOptions.hum.sid`), scoped to the caller. 3. The newest tool call id in the request that this caller was given. Ids are unique, and every client echoes them because it must to answer. 4. The words of the last two turns, which compaction of older turns leaves alone; also tried without trailing answers, so a delivery or a late answer finds the sigil it had before them. 5. Otherwise a new sigil. A sigil continues only from where it last left off: the request must still hold the sigil's newest call (never a call the server ran, which a client may not echo), or match its latest tail. A copy of a conversation taken earlier begins its own. No key reads the system prompt. **claude-cli tells a changed system prompt** at the head of the turn it changes in, and reads what a session was told from its transcript when a cell is raised on it. Before, only a new session was told, so a changed prompt reached claude only by starting a new session. **A call keeps the id its cell gave it.** The nest asks the nurse, `Nurse::call_id` (default none), and knows nothing of any cell. claude-cli reads Claude Code's `_meta["claudecode/toolUseId"]`. A caller, the forager and claude's transcript now name a call alike. Held tool results are matched by call and answer, then by tool and answer; a late answer to a call claude already holds an answer for is stitched on as a call of its own, since a call takes one answer. ## Testing - hum-nest: 34 pass, `tools/speech.py` clean, clippy clean. New: a call keeps the id its cell gave it. - claude-cli: all pass. New: the call id read from `_meta`; a changed system prompt told, an unchanged one not; a transcript says what it was told last; a late answer to a held call stitched as its own call. - openai-server 69, anthropic-server 29, vercel-ai 22, end to end included. `Sigils` tests: dropped older pairs, a squashed turn, mid-turn words missing then present, a changed system prompt, a made-up delivery, a tool-less tail, reordered calls, a server-run last call with `mcp_calls` dropped, all keep the sigil; strangers, a growing sigil, and copies taken earlier (with and without tools) do not; `X-Hum-Sid` names a line per caller. - Live (`HUM_LIVE_CLAUDE=1`, `claude-haiku-4-5`): **20/20**, new: a changed system prompt obeyed on the same sigil (`nurse.cell.reused`); `X-Hum-Sid` carrying a codeword across two unrelated requests; every caller tool call arriving as `toolu_…`. ## Known limits - A client that edits an older message keeps its session, so claude remembers the unedited version. - A copy of a tool-less conversation, taken after its latest turn, shares the original's sigil. - A compaction summary sent as a user message right before the last two turns changes the tail; as a system message it does not.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
wryme delivers a finished background job, or another window's note, as a made-up tool call and its result at the end of a request, with no new user words. Through hum to any
claude-*model this failed with400 cannot-grow-on, because the claude-cli nurse refused every prompt with no words. Separately, every request from wryme started a new sigil, so each turn spun up a new claude session and re-stitched the whole history.Two expectations hold after this PR:
What changes
claude-cli grows on from tool results (
hives/claude-cli)grows_on()is now true. A prompt with no words whose graft ends in tool results (graft::ends_in_grasps) keeps those results as the stitched transcript's last entries, and the cell sendsstream::CONTINUEas the user message, since claude only takes a turn from one.claude --resumeloads a transcript that ends in tool results, it first writes its own empty reply,"No response requested.". The continue line tells the model that reply was added by the session, not by it.scion) is refused withcannot-grow-on, so/v1/completionsand prefill still get a 400.graft()stitches on the tool results a held transcript lacks, matched by the tool and its answer, not counted. A transcript can hold answers the client never saw (a tool the server ran, a call the nest gave up on), and counting let those hide a delivery. Ids can't be used: claude names its calls by its own ids, the client by the nest's.A conversation keeps its sigil (
hives/openai-server)Every nurse grafts a delivery
/api/chat.A tool the server ran passes through both APIs
mcp_calls, each shaped as the Responses API'smcp_callitem, streamed as a delta liketool_calls, and reads it back when the client sends the message back.previous_response_idgraft holds it.Code is speech: the doc comments the first commit added are gone,
answeredisends_in_grasps, and headers say only what the code can't.Testing
/api/chat.mcp_callswhole and streamed and read back; the trace with and without served calls; a delivery finding its sigil; late answers in the nurse.HUM_LIVE_CLAUDE=1,claude-haiku-4-5): 18/18 pass, including a delivery in a turn where the server ranlookup, which stays on its sigil, stitches the late result (graft.late-results) and is answered.Not done