Skip to content

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)BeforeNow
Temporal, at the root sessionunknown_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 sessionaccepted, caller_resumesunchanged
runtime-js, at the companion's own sessionaccepted, caller_resumesaccepted, new_run: the companion continues by itself, as through the root
CF Workflows / DBOS, at the companion's own sessionunknown_tool_callaccepted, new_run: the companion continues by itself, as through the root
A re-send at the companion's own session after its answer was drainedTemporal already_completed, CF Workflows and DBOS unknown_tool_callTemporal 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 runTemporal and DBOS run_stopped, CF Workflows unknown_tool_callrun_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 ​

  1. 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).
  2. runtime-js callers of the companion's own session: the answer now continues the companion by itself (new_run). Don't also call resume() on that session for it. This also holds for a worker whose registry knows only the companion's parent, which used to reject with framework_agent_not_found. If you relied on that rejection, handle new_run here, and note that a child whose type the registry can't resolve reads its completed marker with the default retention (24h).
  3. Don't read a Temporal companion's own clientToolCallOwnership. It is empty; read the root's.
  4. A custom runtime's startContinuation: its input now carries addressedSessionId, the session the submit was made against (the root, or a rooted child's own session). Name a new_run's sessionId whenever its run's session is not that one. A direct caller of resolveSubmitContinuation must pass addressedSessionId: the session its submit was made against.
  5. A custom runtime that returns new_run with sessionId: add relation. 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. A new_run with sessionId and no relation no longer type-checks or parses. Stock useChat is unaffected: the chat stream still carries only data-tool-output-elsewhere and finish for these.

Released under the MIT License.