A companion's client call routes through its root (CP-234, CP-254)
This guide covers the release in which a persistent companion's client-executed call is answerable through its root on Temporal, and a submit addressed to a companion's own session is decided the same way on every runtime. It is for people who submit client-tool results for calls made by persistent companions.
Versions: @helix-agents/runtime-temporal, @helix-agents/runtime-js and @helix-agents/core take a major; @helix-agents/runtime-cloudflare and @helix-agents/runtime-dbos a minor (an answer they refused now succeeds), and @helix-agents/ai-sdk a minor (its chat handler reads a new_run's relation). Upgrade them together.
What changed
| Submit for a persistent companion's call (no live ancestor awaits it) | Before | Now |
|---|---|---|
| Temporal, at the root session | unknown_tool_call (the root never knew the call) | unknown_tool_call with reason: 'owner_not_continuable' (until CP-246) |
| Temporal, at the companion's own session | accepted, caller_resumes | unchanged |
| runtime-js, at the companion's own session | accepted, caller_resumes | accepted, new_run: the companion continues by itself, as through the root |
| CF Workflows / DBOS, at the companion's own session | unknown_tool_call | accepted, new_run: the companion continues by itself, as through the root |
| A re-send at the companion's own session after its answer was drained | Temporal already_completed, CF Workflows and DBOS unknown_tool_call | Temporal and DBOS already_completed, as through the root; CF Workflows unknown_tool_call on both routes (its drain writes no marker yet) |
| An answer at the companion's own session after a stop ended its run | Temporal and DBOS run_stopped, CF Workflows unknown_tool_call | run_stopped (CF Workflows and Temporal as through the root; DBOS from its own stop check, unchanged) |
An answer at the companion's own session is checked against the tool's outputSchema wherever the root route checks it. DBOS checks neither route. The own-session route also requires the tree root's route, so a call is reachable on its own session only once it's routable: until then, an answer there is the plain unknown_tool_call, as through the root.
On CF Workflows, an answer at an awaited sub-agent's own session continues the tree root's run, as through the root. Its new_run names the root's sessionId, because the submit was made against the child, so a caller re-anchors instead of attaching the child's stream.
On Temporal, a companion (spawned, resumed or continued) now records its client calls in the ROOT session's clientToolCallOwnership, as a sub-agent does, and its own session's map stays empty. The write is MR D's field-scoped ownership op, so it never fails the parent's open step.
What to change
- Submit at the root. Clients submit at the root session; on Temporal that now reaches a companion's call. Until Temporal can continue a paused companion by itself (CP-246), it refuses such an answer typed (
owner_not_continuable) and stores nothing. A client that already submits at the companion's own session keeps working there (caller_resumes: the caller resumes that session). - runtime-js callers of the companion's own session: the answer now continues the companion by itself (
new_run). Don't also callresume()on that session for it. This also holds for a worker whose registry knows only the companion's parent, which used to reject withframework_agent_not_found. If you relied on that rejection, handlenew_runhere, and note that a child whose type the registry can't resolve reads its completed marker with the default retention (24h). - Don't read a Temporal companion's own
clientToolCallOwnership. It is empty; read the root's. - A custom runtime's
startContinuation: its input now carriesaddressedSessionId, the session the submit was made against (the root, or a rooted child's own session). Name anew_run'ssessionIdwhenever its run's session is not that one. A direct caller ofresolveSubmitContinuationmust passaddressedSessionId: the session its submit was made against. - A custom runtime that returns
new_runwithsessionId: addrelation. Use'self_wake'when that session continues by itself (a paused companion's self-wake) and'continues_turn'when its run continues the addressed session's turn. Anew_runwithsessionIdand norelationno longer type-checks or parses. StockuseChatis unaffected: the chat stream still carries onlydata-tool-output-elsewhereandfinishfor these.