Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ GitHub Releases page; `0.8.0` is the new starting line.

- **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.
- **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.
- **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.

## 0.58.0 (2026-07-11)

Expand Down
16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,19 @@ If you believe a new runtime dependency is genuinely necessary:
automatically so reviewers know to look for the justification.

Dev-only dependencies under `[dependency-groups]` are not subject to this policy.

## Tool-execution characterization

Changes to `soul/tool_execution.py`, execution scheduling, deduplication, the reader/writer gate, or
MCP publication should run the deterministic local characterization harness:

```bash
uv run python scripts/benchmark_toolset.py --scenario all --runs 5 --output before-or-after.json
```

For before/after evidence, use the same machine, Python environment, fixture matrix, warm-up count,
and five-run command at both revisions. Keep the raw JSON and report timing regressions as well as
improvements; do not discard samples or use `--smoke` for final evidence. Confirm every scenario has
zero leaked tasks/processes/sessions, all cancellation and recovery flags are true, registry hashes
are stable, and deterministic decision states are derived by the harness. Treat results as directional
local engineering evidence, not universal product telemetry.
30 changes: 24 additions & 6 deletions docs/en/customization/agent-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ sequenceDiagram
```mermaid
flowchart LR
Core[pythinker_core.step]
Call[ToolCall]
Toolset[PythinkerToolset.handle]
Calls[Terminal ToolCall batch]
Toolset[PythinkerToolset.handle_batch]
Engine[ToolExecutionEngine]
Parse[Parse JSON arguments]
Pre[PreToolUse hooks]
Execute[tool.call(arguments)]
Expand All @@ -193,10 +194,13 @@ flowchart LR
Post[PostToolUse or PostToolUseFailure hooks]
Telemetry[Telemetry]
Result[ToolResult]
Batch[ToolBatchHandle]
Step[StepResult.tool_results]
Soul[PythinkerSoul]
Context[tool_result_to_message -> Context]
Wire[Wire event stream]

Core --> Call --> Toolset --> Parse --> Pre --> Execute
Core --> Calls --> Toolset --> Engine --> Parse --> Pre --> Execute
Execute --> Builtin
Execute --> Plugin
Execute --> MCP
Expand All @@ -208,12 +212,26 @@ flowchart LR
Builtin --> Result
Approval --> Result
Result --> Post --> Telemetry
Result --> Core
Result --> Batch --> Step --> Soul
Result --> Wire
Result --> Context
Soul --> Context
```

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.
The toolset is both a registry and an execution boundary. For Pythinker Soul, `pythinker_core.step`
passes the complete terminal call batch and immutable `ToolBatchContext` to
`PythinkerToolset.handle_batch`. Its non-exported `ToolExecutionEngine` validates tool names, parses
JSON arguments, applies duplicate/consecutive-call policy, schedules calls through the reader-writer
gate, triggers hooks, converts exceptions to `ToolRuntimeError`, and supervises ordered results and
bounded cancellation through a `ToolBatchHandle`. `StepResult.tool_results()` settles that owned
batch before Soul appends the assistant and tool messages to `Context`. Core's per-call
`Toolset.handle()` path remains only as the compatibility fallback for toolsets that do not
implement `BatchToolset`.

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.

## Subagent graph

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

Expand Down Expand Up @@ -99,7 +101,8 @@ canonical list lives in `src/pythinker_code/wire/types.py` (`Event` union): `Ste
| `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` |
| `src/pythinker_code/soul/agent.py` | `Runtime` and `Agent` construction, system-prompt assembly, AGENTS.md discovery. | `Runtime`, `Agent`, `load_agent`, `load_agents_md`, `BuiltinSystemPromptArgs` |
| `src/pythinker_code/soul/context.py` | Conversation history, checkpoints, JSONL persistence. | `Context` |
| `src/pythinker_code/soul/toolset.py` | Loads built-in + MCP tools, injects deps, executes calls. | `PythinkerToolset` |
| `src/pythinker_code/soul/toolset.py` | Owns built-in + MCP registration, visibility, dependency injection, and the legacy per-call facade. | `PythinkerToolset` |
| `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` |
| `src/pythinker_code/soul/slash.py` | Slash-command registry and dispatch. | `registry` |
| `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` |
| `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` |
Expand Down Expand Up @@ -299,7 +302,7 @@ Both frontends build with `tsc -b && vite build` and are synced into the Python

| Path | Purpose | Key entry points and interfaces |
| --- | --- | --- |
| `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` |
| `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` |
| `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` |
| `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` |
| `packages/pythinker-code/` | Thin distribution package exposing the `pythinker-code` script. | — |
Expand Down
1 change: 1 addition & 0 deletions docs/en/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ GitHub Releases page; `0.8.0` is the new starting line.

- **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.
- **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.
- **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.

## 0.58.0 (2026-07-11)

Expand Down
Loading
Loading