Skip to content

Commit ec57659

Browse files
authored
Merge branch 'main' into feat/review-target-resolution
2 parents e3ef446 + 6349497 commit ec57659

23 files changed

Lines changed: 9809 additions & 837 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ GitHub Releases page; `0.8.0` is the new starting line.
2121
across foreground and background runs.
2222
- **Parallel streamed tool calls are now correlated safely.** Interleaved argument chunks stay attached to their indexed calls, malformed or truncated call streams stop before tool execution, and failed attempts are not retried after output has already been shown.
2323
- **Provider compatibility and Z.AI routing are now explicit.** Immutable compatibility profiles keep request-format quirks behind the chat-provider boundary, while independent Z.AI Coding Plan and API login routes use separate credentials, endpoints, model identities, catalog refresh, logout, and usage/rate-limit state. Curated GLM requests now apply exact context/output limits, thinking controls, reasoning replay, and tool-stream support without activating for local or unknown models.
24+
- **Tool execution is now supervised as a terminal batch.** A private execution engine preserves the Toolset registry and legacy per-call API while centralizing ordered results, deduplication, callbacks, and batch summaries; cancellation is bounded, late work stays owned, and new batches fail closed until timed-out cleanup drains.
2425

2526
## 0.58.0 (2026-07-11)
2627

CONTRIBUTING.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,19 @@ If you believe a new runtime dependency is genuinely necessary:
4848
automatically so reviewers know to look for the justification.
4949

5050
Dev-only dependencies under `[dependency-groups]` are not subject to this policy.
51+
52+
## Tool-execution characterization
53+
54+
Changes to `soul/tool_execution.py`, execution scheduling, deduplication, the reader/writer gate, or
55+
MCP publication should run the deterministic local characterization harness:
56+
57+
```bash
58+
uv run python scripts/benchmark_toolset.py --scenario all --runs 5 --output before-or-after.json
59+
```
60+
61+
For before/after evidence, use the same machine, Python environment, fixture matrix, warm-up count,
62+
and five-run command at both revisions. Keep the raw JSON and report timing regressions as well as
63+
improvements; do not discard samples or use `--smoke` for final evidence. Confirm every scenario has
64+
zero leaked tasks/processes/sessions, all cancellation and recovery flags are true, registry hashes
65+
are stable, and deterministic decision states are derived by the harness. Treat results as directional
66+
local engineering evidence, not universal product telemetry.

docs/en/customization/agent-architecture.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,9 @@ sequenceDiagram
180180
```mermaid
181181
flowchart LR
182182
Core[pythinker_core.step]
183-
Call[ToolCall]
184-
Toolset[PythinkerToolset.handle]
183+
Calls[Terminal ToolCall batch]
184+
Toolset[PythinkerToolset.handle_batch]
185+
Engine[ToolExecutionEngine]
185186
Parse[Parse JSON arguments]
186187
Pre[PreToolUse hooks]
187188
Execute[tool.call(arguments)]
@@ -193,10 +194,13 @@ flowchart LR
193194
Post[PostToolUse or PostToolUseFailure hooks]
194195
Telemetry[Telemetry]
195196
Result[ToolResult]
197+
Batch[ToolBatchHandle]
198+
Step[StepResult.tool_results]
199+
Soul[PythinkerSoul]
196200
Context[tool_result_to_message -> Context]
197201
Wire[Wire event stream]
198202
199-
Core --> Call --> Toolset --> Parse --> Pre --> Execute
203+
Core --> Calls --> Toolset --> Engine --> Parse --> Pre --> Execute
200204
Execute --> Builtin
201205
Execute --> Plugin
202206
Execute --> MCP
@@ -208,12 +212,26 @@ flowchart LR
208212
Builtin --> Result
209213
Approval --> Result
210214
Result --> Post --> Telemetry
211-
Result --> Core
215+
Result --> Batch --> Step --> Soul
212216
Result --> Wire
213-
Result --> Context
217+
Soul --> Context
214218
```
215219

216-
The toolset is both a registry and an execution boundary. It hides tools from the LLM when needed, validates tool names, parses JSON arguments, triggers hooks, converts exceptions to `ToolRuntimeError`, and returns async `ToolResult` tasks to `pythinker_core.step`. The advertised tool list is filtered by the active execution profile, subagent/root role, plan-mode state, and hard permission profile before each model call; tool-specific execution guards still run even if a hidden tool is somehow called. MCP tools are registered as local wrappers. Wire external tools are sent to the active Wire client as `ToolCallRequest` messages and wait for a client-provided result.
220+
The toolset is both a registry and an execution boundary. For Pythinker Soul, `pythinker_core.step`
221+
passes the complete terminal call batch and immutable `ToolBatchContext` to
222+
`PythinkerToolset.handle_batch`. Its non-exported `ToolExecutionEngine` validates tool names, parses
223+
JSON arguments, applies duplicate/consecutive-call policy, schedules calls through the reader-writer
224+
gate, triggers hooks, converts exceptions to `ToolRuntimeError`, and supervises ordered results and
225+
bounded cancellation through a `ToolBatchHandle`. `StepResult.tool_results()` settles that owned
226+
batch before Soul appends the assistant and tool messages to `Context`. Core's per-call
227+
`Toolset.handle()` path remains only as the compatibility fallback for toolsets that do not
228+
implement `BatchToolset`.
229+
230+
The advertised tool list is filtered by the active execution profile, subagent/root role,
231+
plan-mode state, and hard permission profile before each model call; tool-specific execution guards
232+
still run even if a hidden tool is somehow called. MCP tools are registered as local wrappers. Wire
233+
external tools are sent to the active Wire client as `ToolCallRequest` messages and wait for a
234+
client-provided result.
217235

218236
## Subagent graph
219237

docs/en/customization/architecture.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@ The end-to-end flow when a session starts and processes a turn:
6666
4. **Core loop**`src/pythinker_code/soul/pythinkersoul.py:PythinkerSoul.run` handles user
6767
input and slash commands, calls the LLM through `pythinker_core.step`, runs tools, gates
6868
side effects through approvals, injects dynamic reminders, and compacts the context.
69-
5. **Tool execution**`src/pythinker_code/soul/toolset.py:PythinkerToolset` loads built-in
70-
and MCP tools, injects dependencies, executes calls, and returns structured results.
69+
5. **Tool execution**`src/pythinker_code/soul/toolset.py:PythinkerToolset` owns the
70+
built-in/MCP registry, visibility, and dependency injection facade. The private
71+
`src/pythinker_code/soul/tool_execution.py:ToolExecutionEngine` executes terminal batches,
72+
deduplicates calls, orders results, and supervises bounded cancellation.
7173
6. **Wire and UI**`src/pythinker_code/soul/run_soul` connects the soul to
7274
`src/pythinker_code/wire/`; Shell, Print, ACP, Web, and Dashboard frontends consume Wire events.
7375

@@ -99,7 +101,8 @@ canonical list lives in `src/pythinker_code/wire/types.py` (`Event` union): `Ste
99101
| `src/pythinker_code/soul/pythinkersoul.py` | Core loop: user input, slash commands, LLM calls, tool runs, compaction, telemetry spans. | `PythinkerSoul`, `PythinkerSoul.run`, `FLOW_COMMAND_PREFIX` |
100102
| `src/pythinker_code/soul/agent.py` | `Runtime` and `Agent` construction, system-prompt assembly, AGENTS.md discovery. | `Runtime`, `Agent`, `load_agent`, `load_agents_md`, `BuiltinSystemPromptArgs` |
101103
| `src/pythinker_code/soul/context.py` | Conversation history, checkpoints, JSONL persistence. | `Context` |
102-
| `src/pythinker_code/soul/toolset.py` | Loads built-in + MCP tools, injects deps, executes calls. | `PythinkerToolset` |
104+
| `src/pythinker_code/soul/toolset.py` | Owns built-in + MCP registration, visibility, dependency injection, and the legacy per-call facade. | `PythinkerToolset` |
105+
| `src/pythinker_code/soul/tool_execution.py` | Private batch execution state machine: preparation, deduplication, reader/writer scheduling, callbacks, ordered results, bounded cancellation, poison, and late-task recovery. | `ToolExecutionEngine` |
103106
| `src/pythinker_code/soul/slash.py` | Slash-command registry and dispatch. | `registry` |
104107
| `src/pythinker_code/soul/dynamic_injection.py` (+ `dynamic_injections/`) | Injects budgeted `<system-reminder>` content per step: plan-mode, auto-mode, model-defense, LSP diagnostics. | `DynamicInjectionProvider` |
105108
| `src/pythinker_code/soul/permission.py` | Per-step permission profiles (`read_only`/`plan`/`ask`/`implement`/`review`/`verify`) and destructiveness classification. | `tool_destructive_reason`, `shell_command_signature` |
@@ -299,7 +302,7 @@ Both frontends build with `tsc -b && vite build` and are synced into the Python
299302

300303
| Path | Purpose | Key entry points and interfaces |
301304
| --- | --- | --- |
302-
| `packages/pythinker-core/` | LLM abstraction: message models, streaming chat providers, tool abstractions, and the `generate`/`step` primitives. Independently versioned (1.x). | `generate`, `step`, `Message`, `ContentPart`, `ToolCall`, `ChatProvider`, `Toolset`, `ToolReturnValue`/`ToolOk`/`ToolError`, `CallableTool2`, `DisplayBlock`; contrib providers (`Anthropic`, `GoogleGenAI`, `OpenAIResponses`) and `LinearContext` |
305+
| `packages/pythinker-core/` | LLM abstraction: message models, streaming chat providers, optional batch/legacy tool dispatch, and the `generate`/`step` primitives. Independently versioned (1.x). | `generate`, `step`, `StepResult`, `Message`, `ContentPart`, `ToolCall`, `ChatProvider`, `Toolset`, `BatchToolset`, `ToolBatchHandle`, `ToolReturnValue`/`ToolOk`/`ToolError`, `CallableTool2`, `DisplayBlock`; contrib providers (`Anthropic`, `GoogleGenAI`, `OpenAIResponses`) and `LinearContext` |
303306
| `packages/pythinker-host/` | OS abstraction for filesystem + shell across local and SSH backends via a context-var-dispatched `Host` protocol. | `Host`, `HostPath`, `LocalHost`, `HostProcess`, `get_current_host`/`set_current_host` |
304307
| `packages/pythinker-review/` | Standalone review/security/debug engine and stateful Reviewflow; strict Pydantic schemas with fail-closed evidence validation. State in `.pythinker-review/` and `.pythinker-review-flow/`. See `packages/pythinker-review/AGENTS.md`. | `run_engine`, `ReviewLLM`, `Finding`, `RawFinding`, `ReviewerOutput`, Reviewflow `init`/`map`/`review`/`fix` |
305308
| `packages/pythinker-code/` | Thin distribution package exposing the `pythinker-code` script. ||

docs/en/release-notes/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ GitHub Releases page; `0.8.0` is the new starting line.
2323
across foreground and background runs.
2424
- **Parallel streamed tool calls are now correlated safely.** Interleaved argument chunks stay attached to their indexed calls, malformed or truncated call streams stop before tool execution, and failed attempts are not retried after output has already been shown.
2525
- **Provider compatibility and Z.AI routing are now explicit.** Immutable compatibility profiles keep request-format quirks behind the chat-provider boundary, while independent Z.AI Coding Plan and API login routes use separate credentials, endpoints, model identities, catalog refresh, logout, and usage/rate-limit state. Curated GLM requests now apply exact context/output limits, thinking controls, reasoning replay, and tool-stream support without activating for local or unknown models.
26+
- **Tool execution is now supervised as a terminal batch.** A private execution engine preserves the Toolset registry and legacy per-call API while centralizing ordered results, deduplication, callbacks, and batch summaries; cancellation is bounded, late work stays owned, and new batches fail closed until timed-out cleanup drains.
2627

2728
## 0.58.0 (2026-07-11)
2829

0 commit comments

Comments
 (0)