Skip to content

Canonicalize tool call identity so it settles activeToolCalls once - #384

Merged
TheGreatAxios merged 1 commit into
mainfrom
cl-5645-duplicate-call-ids-leak-into-activetoolcalls-pinning-the
Aug 8, 2026
Merged

Canonicalize tool call identity so it settles activeToolCalls once#384
TheGreatAxios merged 1 commit into
mainfrom
cl-5645-duplicate-call-ids-leak-into-activetoolcalls-pinning-the

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes CL-5645: /goal (and other surfaces less often) sat at "working" forever after finishing.
  • src/tui-opentui/turn-state.ts tracked the same logical tool call under two different keys — a name-only streamed inference.tool_call.start/.end under the tool's name, the id-bearing tool.start under a real id — so one tool.done only cleared one entry and activeToolCalls never emptied, blocking both turn terminators (inference.done, connector.reply).
  • Identity is now canonicalized once at the event boundary via a callIdByName mapping: a name-only placeholder is promoted in place once a real id is seen, and the mapping is cleared once that call resolves so a later call reusing the same tool name doesn't inherit a stale id.

Test plan

  • New tests in src/tui-opentui/turn-state.test.ts: the exact CL-5645 repro (name-only start -> id-bearing tool.start -> one tool.done settles), a same-call start+end idempotency case, concurrent same-named calls resolving independently, and a second same-named call not inheriting a finished call's stale id.
  • bun run typecheck (pre-existing arktype ArkErrors narrowing errors unrelated to this change, present throughout the codebase before this branch)
  • bun run build
  • bun run test (4082 pass, 1 pre-existing unrelated failure: src/agent/lsp-availability.test.ts, an environment-dependent language-server detection check untouched by this diff)

Note

Agents on CL-5641/CL-5642 are concurrently editing turn-state.ts and runtime-bridge.ts in separate worktrees. This diff is isolated but expect possible merge overlap in turn-state.ts.

@linear-code

linear-code Bot commented Aug 8, 2026

Copy link
Copy Markdown

CL-5645

A streamed inference.tool_call.start/end with no callId registered the
call under its name, while the executed tool.start for the same call
registered it under a real id. One tool.done only removed the
id-keyed entry, so the name-keyed duplicate leaked and pinned
activeToolCalls above zero forever, blocking inference.done and
connector.reply from ever settling the turn. Goal mode surfaced this
worst since its self-continuing governor has no other terminator.

Identity is now resolved once at the event boundary: the first id seen
for a tool name is recorded, so a later id-bearing announcement for
the same call replaces an earlier name-only placeholder in place
instead of adding a second entry. The mapping is cleared once its call
resolves, so a later call reusing the same tool name in one turn
starts clean rather than inheriting a finished call's id.
@TheGreatAxios
TheGreatAxios force-pushed the cl-5645-duplicate-call-ids-leak-into-activetoolcalls-pinning-the branch from 1ef7f47 to 3532ea2 Compare August 8, 2026 16:47
@TheGreatAxios
TheGreatAxios merged commit 47beb46 into main Aug 8, 2026
3 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