Skip to content

feat(agent,coding-agent): per-tool executionMode override for sequential tool execution#3345

Merged
badlogic merged 4 commits into
earendil-works:mainfrom
aliou:feat/tool-execution-modes
Apr 17, 2026
Merged

feat(agent,coding-agent): per-tool executionMode override for sequential tool execution#3345
badlogic merged 4 commits into
earendil-works:mainfrom
aliou:feat/tool-execution-modes

Conversation

@aliou

@aliou aliou commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Hello! This fixes #3274 by adding an optional executionMode to the tool definition types in agent-core and in the coding agent.

Then in the agent loop, if in a message at least one tool has its executionMode set to sequential, all of the tool call in this message are run sequentially.

Also added an tic-tac-toe extension that shows this: running it using the current pi version confuses the agent, as moving and playing on the board happen at roughly the same time1 (see session). When playing with the code that uses executionMode, the agent can actually play normally (see session).

Research: https://pi.dev/session/#60805468761a625c9603daf40aa461a4
Implementation 1/2: https://pi.dev/session/#dddb8787727f3e54487ad08f77d1c01a
Implementation 2/2: https://pi.dev/session/#38ba41433787958645827badff179c5f

Summary by glm-5.1

Summary

Adds executionMode to individual tool definitions, allowing tools to override the global toolExecution config. If any tool call in a batch targets a tool with executionMode: "sequential", the entire batch executes sequentially regardless of the global setting.

Problem

The agent loop supports toolExecution: "parallel" | "sequential" as a global config. Some tools (e.g., tools that mutate shared state) need sequential execution even when the default is parallel. Without per-tool control, extensions had to force the entire session to sequential mode.

Changes

packages/agent/ — Core feature

  • src/types.ts: Added ToolExecutionMode type ("sequential" | "parallel"), executionMode field on AgentTool, and toolExecution field on AgentLoopConfig.
  • src/agent-loop.ts: executeToolCalls() now checks each tool call target tool for executionMode: "sequential". If any match, the batch runs sequentially even when global config is parallel.
  • test/agent-loop.test.ts: Three new tests:
    • Tool with executionMode: "sequential" forces sequential execution under default parallel config
    • Mixed tools: one sequential tool forces the entire batch sequential
    • Tool with executionMode: "parallel" allows concurrent execution
  • README.md: Documented executionMode on AgentTool (example + explanation), added note about per-tool override to the tool execution mode section and low-level API example.

packages/coding-agent/ — Extension API + example

  • src/core/extensions/types.ts: Re-exports ToolExecutionMode from @mariozechner/pi-agent-core. The ToolDefinition interface already had the executionMode field.
  • src/core/extensions/index.ts: Added ToolExecutionMode to re-export list.
  • src/index.ts: Added ToolExecutionMode to top-level exports.
  • examples/extensions/tic-tac-toe.ts: New example extension — tic-tac-toe game where the agent plays via move_up/down/left/right + play tool calls that share a cursor. These tools use executionMode: "sequential" because the 300ms move delay under parallel execution causes play to resolve before earlier moves finish, landing on the wrong cell.
  • examples/extensions/README.md: Added entry for the tic-tac-toe example.

Dispatch logic

executeToolCalls()
  for each toolCall: lookup tool in currentContext.tools
  if config.toolExecution === "sequential" || any tool has executionMode === "sequential"
    -> executeToolCallsSequential()
  else
    -> executeToolCallsParallel()

The lookup is synchronous (in-memory array search), so this adds negligible overhead before branching.

Footnotes

  1. I'm kinda cheating: the move actions have a delay that the play action doesn't have, it shows the issue even better since despite calling the tools in the right order, the lack of sequencing makes the play happen before the moves

aliou added 4 commits April 17, 2026 16:23
…ol and ToolDefinition

Add optional executionMode?: ToolExecutionMode to AgentTool and
ToolDefinition interfaces. Propagate through wrapToolDefinition and
createToolDefinitionFromAgentTool. No behavioral change yet — agent
loop will read this field in a follow-up.
…xecution

When a tool defines executionMode='sequential', the agent loop
forces sequential execution of all tool calls in that batch,
even if the global config is parallel.
…-agent-core

Makes the type available to extensions that want to set
executionMode on tool definitions.
…ode: sequential

Demonstrates per-tool executionMode: the agent plays via move/play
tool calls that share a cursor. Without sequential execution, play
can resolve before earlier moves finish, landing on the wrong cell.
@badlogic

Copy link
Copy Markdown
Collaborator

lgtm cheers!

@badlogic badlogic merged commit bfa11a5 into earendil-works:main Apr 17, 2026
3 checks passed
@aliou aliou deleted the feat/tool-execution-modes branch April 18, 2026 07:32
Luminger added a commit to Luminger/pi-mono that referenced this pull request Apr 20, 2026
Treat both proposals as net-new work on top of current main, not as
prior-shipped designs.

PROPOSAL-RPC-TOOL-HOOKS.md:
- Align afterToolCall sample code with earendil-works#3051 semantics (always pass
  result.details, drop !isError gate on hook application)
- Drop 'existing, unchanged' framing on the extension-hook block

PROPOSAL-RPC-CLIENT-TOOLS.md:
- Add per-tool executionMode to register_tool payload (earendil-works#3345)
- Bridge-tool execute() now takes ignored 5th ExtensionContext arg
- Document client-disconnect / stdin-EOF handling as an automatic
  resolution alongside agent abort
- Note renderShell alongside renderCall/renderResult as not crossing
  the boundary
- Clarify that TypeBox validation runs before the client sees args
durdn pushed a commit to durdn/pi-mono that referenced this pull request Apr 21, 2026
…ial tool execution (earendil-works#3345)

* feat(agent,coding-agent): add per-tool executionMode field to AgentTool and ToolDefinition

Add optional executionMode?: ToolExecutionMode to AgentTool and
ToolDefinition interfaces. Propagate through wrapToolDefinition and
createToolDefinitionFromAgentTool. No behavioral change yet — agent
loop will read this field in a follow-up.

* feat(agent): support per-tool executionMode override for sequential execution

When a tool defines executionMode='sequential', the agent loop
forces sequential execution of all tool calls in that batch,
even if the global config is parallel.

* feat(coding-agent): re-export ToolExecutionMode from @mariozechner/pi-agent-core

Makes the type available to extensions that want to set
executionMode on tool definitions.

* feat(coding-agent): add tic-tac-toe extension example with executionMode: sequential

Demonstrates per-tool executionMode: the agent plays via move/play
tool calls that share a cursor. Without sequential execution, play
can resolve before earlier moves finish, landing on the wrong cell.
byte-rose pushed a commit to byte-rose/pi-mono that referenced this pull request Apr 28, 2026
…ial tool execution (earendil-works#3345)

* feat(agent,coding-agent): add per-tool executionMode field to AgentTool and ToolDefinition

Add optional executionMode?: ToolExecutionMode to AgentTool and
ToolDefinition interfaces. Propagate through wrapToolDefinition and
createToolDefinitionFromAgentTool. No behavioral change yet — agent
loop will read this field in a follow-up.

* feat(agent): support per-tool executionMode override for sequential execution

When a tool defines executionMode='sequential', the agent loop
forces sequential execution of all tool calls in that batch,
even if the global config is parallel.

* feat(coding-agent): re-export ToolExecutionMode from @mariozechner/pi-agent-core

Makes the type available to extensions that want to set
executionMode on tool definitions.

* feat(coding-agent): add tic-tac-toe extension example with executionMode: sequential

Demonstrates per-tool executionMode: the agent plays via move/play
tool calls that share a cursor. Without sequential execution, play
can resolve before earlier moves finish, landing on the wrong cell.
larsboes pushed a commit to larsboes/pi-mono that referenced this pull request Apr 30, 2026
…ial tool execution (earendil-works#3345)

* feat(agent,coding-agent): add per-tool executionMode field to AgentTool and ToolDefinition

Add optional executionMode?: ToolExecutionMode to AgentTool and
ToolDefinition interfaces. Propagate through wrapToolDefinition and
createToolDefinitionFromAgentTool. No behavioral change yet — agent
loop will read this field in a follow-up.

* feat(agent): support per-tool executionMode override for sequential execution

When a tool defines executionMode='sequential', the agent loop
forces sequential execution of all tool calls in that batch,
even if the global config is parallel.

* feat(coding-agent): re-export ToolExecutionMode from @mariozechner/pi-agent-core

Makes the type available to extensions that want to set
executionMode on tool definitions.

* feat(coding-agent): add tic-tac-toe extension example with executionMode: sequential

Demonstrates per-tool executionMode: the agent plays via move/play
tool calls that share a cursor. Without sequential execution, play
can resolve before earlier moves finish, landing on the wrong cell.
PSU3D0 added a commit to PSU3D0/pi-mono that referenced this pull request May 13, 2026
Consolidates 62 upstream commits (v0.67.4 -> v0.67.68) into the fork.
Preserves all fork-local work (OAuth cloak, antigravity pool, context
tiers, OSC 8 file hyperlinks, gemini3 sig skip, codex_cli_rs headers,
compaction context hooks, gpt-5.4 models).

Conflict resolutions (4 files):

- packages/ai/src/providers/google-gemini-cli.ts: layered upstream's
  onResponse hook call into our pool-aware retry loop; kept our
  finally-block pool.saveNow() path.

- packages/tui/src/utils.ts: adopted upstream's AnsiCodeTracker OSC 8
  impl wholesale. It's cleaner than our f16dfd5 version (preserves
  hyperlink across SGR reset via separate clear(), emits ST-terminated
  sequences). Dropped our 7d75fee SEGMENT_RESET fix since upstream's
  design never introduced the bug.

- packages/tui/src/components/markdown.ts: kept our resolveHref hook
  and wrapHyperlink helper but gated OSC 8 emission on
  getCapabilities().hyperlinks; switched to upstream's hyperlink()
  helper for consistency. Merges file path linking (ours) with
  terminal capability detection (upstream earendil-works#3248, #30a8a41f).

- packages/tui/test/{markdown,wrap-ansi}.test.ts: updated our OSC 8
  assertions to ST-terminated sequences (\x1b\\) to match new emitter;
  added setCapabilities({hyperlinks:true}) gating where needed;
  afterEach resetCapabilitiesCache to isolate test state.

Dependency upgrades from upstream:
- @anthropic-ai/sdk 0.73.0 -> 0.90.0
- @aws-sdk/client-bedrock-runtime 3.983.0 -> 3.1030.0
- @mistralai/mistralai 1.14.1 -> 2.2.0

Upstream features folded in:
- fix(ai): Opus 4.7 adaptive thinking + xhigh effort (earendil-works#3286)
- feat(ai): thinkingDisplay option (summarized/omitted/raw)
- feat(coding-agent): after_provider_response hook (earendil-works#3128)
- fix(ai): trust requested Codex service tier (earendil-works#3307)
- feat(bedrock): Bearer token auth for Converse API (earendil-works#3125)
- feat(agent,coding-agent): per-tool executionMode override (earendil-works#3345)
- feat(tui): OSC 8 hyperlinks with terminal capability detection
  (earendil-works#3248, #30a8a41f)
- feat(coding-agent,tui): argument-hint frontmatter in prompts (earendil-works#2780)
- Plus ~40 smaller fixes across ai/coding-agent/tui/agent.

Verification:
- packages/tui: 559/559 tests pass
- packages/ai: 144/144 OAuth cloak tests pass; all fork-local suites
  pass (antigravity-pool, context-tiers, gemini3-unsigned-tool-call,
  codex-stream, supports-xhigh, etc.). Remaining test failures (13)
  are pre-existing live-API E2E tests (no credentials in this env).
- packages/agent: 39/39 tests pass.
- packages/coding-agent: 1020/1021 tests pass; 1 flaky bash timeout
  test (passes in isolation), unrelated to merge.
- All four packages build cleanly.
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.

coding-agent: allow extensions to force sequential execution of tools?

2 participants