Skip to content

Commit 7ec4ad7

Browse files
authored
feat: Glob pagination, read-only tools in side questions, and MCP structured results (#303)
## Related Issue None — routine maintenance batch. ## Problem Eight independent gaps across the agent engine, CLI and telemetry: - Glob capped every search at 100 matches with no way to reach the rest. - MCP tools that return both a text summary and a structured payload lost the structured half whenever the text was non-empty, so the agent never saw the records. - Side questions started with `/btw` vetoed every tool call, so a question about the codebase could only be answered from memory. - The subagent model list annotated `primary` with its alias only when the caller's model happened to be in the pool — that is, it stayed anonymous exactly when the annotation was needed — and `AgentDynamicWorkflow` repeated the whole block. - A `[models]` entry written without a `model` field (most often an unquoted dotted alias that TOML parsed as a nested table) failed silently. - `turn_ended` never carried the error code, session attribution keys were dropped by payload flattening, and properties a sink could not accept vanished with no report. - Tower missions carried only the tower's paraphrase to workers and reviewers, spawned tasks ignored the configured subagent timeout, and `/tasks` did not show which model a background agent runs on. - Sessions could not be deleted from the session picker. ## What changed Per commit: - **`bc6abb3` MCP structured results + malformed `[models]` warning.** The structured payload is dropped only when a text block parses to the identical JSON value — compared with `isDeepStrictEqual` behind a reviver that refuses any number `JSON.parse` would round, so a summary, a media block or a rounded copy never suppresses it. Serialized extras now escape `<` instead of stripping literal closing tags, which preserves the values. Config sections gained a `collectDiagnostics` hook (mirroring the existing deprecations contribution); the models section uses it to warn on an entry with no `model`, with a hint naming the quoted table when an unquoted dotted alias produced a nested table. Also scrubs every ambient `PYTHINKER_CODE_*` variable in the engine and gateway test setups, and gives the SDK, client and extension projects an explicit integration-test timeout. - **`2e3f6d7` `/btw` read-only tools + subagent model disclosure.** `BTW_READONLY_TOOLS` (Read, Grep, Glob) passes the veto listener; every write and execute tool still returns the deny message. Mirrored into the v1 engine — `DenyAllPermissionPolicy` takes an allowlist — because the v1↔v2 parity suite compares both side-question contexts. `primary` now always renders as `primary (= <alias>)`, the duplicate `[main model]` marker is gone, a closing line states that pool entries do not inherit the caller's thinking level, and `AgentDynamicWorkflow` renders a one-line summary. - **`6e89460` Glob pagination.** `offset` and `head_limit` (default 100, `0` lifts the match-count limit). Every page still fits the output character limit, ends on a complete path, reports the range it covers and gives the next offset when matches remain; a page that cannot fit one complete path plus its diagnostics returns an error instead of a partial path. The tool card counts only paths and marks a page that is not the last. - **`434e97d` telemetry attributes.** `turn_ended` carries the engine error code; session attribution keys emit empty strings so they survive flattening; a new `onUnexpectedError` hook reports properties the sink could not accept. Properties are sanitized when the sink accepts the event, so drops on events queued before initialization are reported once a handler is installed, and the hook runs behind a guard so a faulty host handler cannot abort `track()`. - **`c84f5df` tower.** Missions take a `context` field holding the user's own sentences verbatim, rendered into the mission file and every briefing. Reviewer briefings embed the mission text and the author's own review-request behind an intent-first checklist; workers escalate substantive ambiguity with a `clarify-request` instead of guessing, and reconcile completion reports task by task. `TowerSpawnTool` resolves the timeout through the `[subagent]` config instead of a hardcoded two hours, and reports the bound model and effective thinking effort in the task info, which `/tasks` renders. - **`38ac69f` session deletion.** `Ctrl+X` on a picker row asks for confirmation and deletes. The picker stays mounted and holds the input lock until the replacement session is ready, so no prompt races the swap; deleting the current session closes it first (no events from a dying session reach the UI) and starts a fresh one; a failed delete reattaches to the session the engine kept, falling back to a new session only if it is gone. - **`4aee74c`** changesets, **`90e3495`** docs. Scope deliberately excluded: the gateway-side half of session deletion (`event.session.deleted` over WS and the serialized cleanup route) is not in this PR — the CLI half stands alone on the existing `harness.deleteSession`. ## Risk Medium. Three behaviour changes are visible to the model rather than to the user: MCP results now carry more content, the subagent model prose changed, and `/btw` can read files. The `/btw` change widens what a side-question agent may do — it is limited to three read-only tools, and the deny path is unchanged for everything else. The telemetry change alters emitted values (`''` instead of `null`); the `pfc_` event prefix and ingest hosts are untouched. ## Verification Each exit code captured separately, all 0 unless noted: - `pnpm run typecheck`, `pnpm run lint`, `pnpm run sherif`, `pnpm run build`, `pnpm run check:web`, `pnpm -C apps/vscode run typecheck`, `pnpm -C apps/vscode test`, `node scripts/check-nix-workspace.mjs`, `nix build .#pythinker-code`. - `pnpm test`: 1241 files, 21,526 passed / 2 failed / 3 expected fail / 79 skipped / 2 todo. Both failures are `packages/agent-gateway/test/fs-watch.e2e.test.ts` under load — a temp-dir `rename` ENOENT in `instanceRegistry.writeFileAtomic` plus its 10s `afterEach` timeout. That file passes 10/10 in isolation and is untouched by this diff. Tests added or updated: 5 config-diagnostics cases, 15 MCP output cases (including a 6-way difference matrix), 2 `/btw` cases plus 3 v1 parity/prompt assertions, 6 subagent-description assertions, 4 Glob chip cases and the engine glob suite, 1 `turn_ended` error-code case plus telemetry/SDK fixture updates, tower spawn timeout and metadata cases, and the session-picker delete flow. Three guards were proven by mutation rather than by passing: removing the `collectDiagnostics` registration fails 4 tests, an unconditional btw veto fails 1, and the pre-change `hasUsableContent` rule fails 15. ## Rollback and review Every commit is independent and revertible on its own. Residual risk is the model-facing prose and the wider MCP payload; both are covered by tests that assert the exact rendered text. Human review wanted on the `/btw` read-only allowance — it is the one change that widens what an agent may do. ## Checklist - [x] I have read the CONTRIBUTING document. - [ ] I have linked a related issue — none; routine maintenance batch. - [x] I have added tests that prove my feature works. - [x] Ran `gen-changesets` skill. - [x] Ran `gen-docs` — `docs/customization/mcp.md`, `docs/reference/tools.md`, `docs/guides/sessions.md`, `docs/reference/slash-commands.md` and `docs/configuration/config-files.md` updated. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - `/btw` side questions can use read-only `Read`, `Grep`, and `Glob` tools. - Delete sessions with `Ctrl+X`, including confirmation and active-session replacement. - Glob supports paginated results with offsets and configurable limits. - Background task lists display agent model names. - Tower missions preserve context, enforce worker timeouts, and require tasks for build missions. - MCP structured results remain available alongside text and media output. - **Bug Fixes** - Added warnings for malformed model configuration entries, with refreshed diagnostics after settings changes. - Improved telemetry reporting for dropped properties and turn errors. - Improved Glob result counts and empty-result handling. - Addressed a TOML parser denial-of-service issue. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 0ae4200 commit 7ec4ad7

94 files changed

Lines changed: 2592 additions & 329 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/btw-readonly-tools.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": minor
3+
---
4+
5+
Side questions started with /btw can call the read-only tools Read, Grep and Glob.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": minor
3+
---
4+
5+
Delete a session from the session picker with Ctrl+X.

.changeset/glob-pagination.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": minor
3+
---
4+
5+
Glob accepts `offset` and `head_limit` to page through matching paths, and `head_limit: 0` returns every match.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Keep an MCP tool's structured result alongside its text and media output.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Refresh configuration warnings after a settings change instead of keeping them until the next restart.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
The subagent model list now names the model `primary` is bound to and states that pool entries do not inherit your thinking level.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Accept `0` from the subagent timeout environment variable to disable the timeout, matching the config file.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Show each background agent's model in the /tasks list.

.changeset/toml-parser-security.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Update the TOML parser to a version that is not affected by a denial-of-service advisory.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Require at least one task on a tower build mission; read-only survey missions still need none.

0 commit comments

Comments
 (0)