pi-ui-extend 0.1.37 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/app/app.d.ts +0 -1
  2. package/dist/app/app.js +22 -21
  3. package/dist/app/input/input-controller.d.ts +1 -0
  4. package/dist/app/input/input-controller.js +40 -12
  5. package/dist/app/model/model-usage-status.js +4 -2
  6. package/dist/app/session/request-history.js +2 -0
  7. package/dist/app/session/session-event-controller.d.ts +13 -0
  8. package/dist/app/session/session-event-controller.js +27 -0
  9. package/dist/app/session/tabs-controller.d.ts +8 -0
  10. package/dist/app/session/tabs-controller.js +37 -6
  11. package/dist/app/workspace/workspace-actions-controller.d.ts +1 -0
  12. package/dist/app/workspace/workspace-actions-controller.js +2 -1
  13. package/dist/bundled-extensions/terminal-bell/index.js +1 -1
  14. package/dist/markdown-format.js +14 -25
  15. package/dist/terminal-width.d.ts +14 -0
  16. package/dist/terminal-width.js +31 -2
  17. package/dist/theme.js +2 -2
  18. package/external/pi-tools-suite/README.md +34 -9
  19. package/external/pi-tools-suite/package.json +3 -3
  20. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +35 -21
  21. package/external/pi-tools-suite/src/async-subagents/commands.ts +1 -1
  22. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +2 -2
  23. package/external/pi-tools-suite/src/async-subagents/core/config.ts +70 -12
  24. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +1 -1
  25. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +1 -1
  26. package/external/pi-tools-suite/src/async-subagents/core/types.ts +1 -1
  27. package/external/pi-tools-suite/src/async-subagents/index.ts +6 -6
  28. package/external/pi-tools-suite/src/async-subagents/lib.ts +1 -1
  29. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +4 -2
  30. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +2 -2
  31. package/external/pi-tools-suite/src/{glm-coding-discipline → coding-discipline}/index.ts +17 -8
  32. package/external/pi-tools-suite/src/config.ts +1 -1
  33. package/external/pi-tools-suite/src/dcp/auto-compress.ts +368 -0
  34. package/external/pi-tools-suite/src/dcp/compress-tool.ts +3 -0
  35. package/external/pi-tools-suite/src/dcp/config.ts +23 -0
  36. package/external/pi-tools-suite/src/dcp/index.ts +112 -7
  37. package/external/pi-tools-suite/src/dcp/prompts.ts +8 -0
  38. package/external/pi-tools-suite/src/dcp/state.ts +41 -0
  39. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +30 -22
  40. package/external/pi-tools-suite/src/index.ts +2 -1
  41. package/external/pi-tools-suite/src/session-name/index.ts +37 -0
  42. package/external/pi-tools-suite/src/tool-descriptions.ts +16 -4
  43. package/package.json +4 -4
@@ -4,11 +4,12 @@ Local all-in-one Pi extension package.
4
4
 
5
5
  This package keeps shared Pi tools as ordinary source folders under `src/` and registers them through one entrypoint.
6
6
 
7
- - `src/glm-coding-discipline` — injects a deduplicated silent-mode and quality-discipline block at the very top of the main-session per-turn system prompt for GLM-family models immediately before the LLM request; disabled for async sub-agents
7
+ - `src/coding-discipline` — injects a deduplicated silent-mode and quality-discipline block at the very top of the main-session per-turn system prompt for all main-session models immediately before the LLM request; disabled for async sub-agents
8
8
  - `src/ast-grep` — `ast_grep` / `ast_apply`
9
- - `src/async-subagents` — `subagents` tool and sub-agent slash commands, including oh-my-openagent-style `/ultrawork` (`/ulw`) and `/hyperplan` orchestration prompts, plus config-defined sub-agent model/thinking/args presets selected via `/subagent-preset` from `asyncSubagents` in `~/.config/pi/pi-tools-suite.jsonc`; includes the `frontend` profile for Gemini-friendly UI/UX and visual frontend work plus the `vision` profile for screenshot/image description via `openai-codex/gpt-5.4-mini`; enforces a 30-minute per-agent execution timeout, project-wide `maxConcurrent` queueing, optional retry/backoff, and `result.json` structured metadata/chaining fields next to raw `result.md`; stores project-local run files and a registry under `.pi/subagents/` so result/status collection can recover after compaction or reload while the main session remains alive
9
+ - `src/async-subagents` — `subagents` tool and sub-agent slash commands, including oh-my-openagent-style `/ultrawork` (`/ulw`) and `/hyperplan` orchestration prompts, plus config-defined sub-agent model/thinking/args presets selected via `/subagent-preset` from `asyncSubagents` in `~/.config/pi/pi-tools-suite.jsonc`; includes the `frontend` profile for Gemini-friendly UI/UX and visual frontend work and the `oracle` profile for cross-provider second opinions; enforces a 30-minute per-agent execution timeout, project-wide `maxConcurrent` queueing, optional retry/backoff, and `result.json` structured metadata/chaining fields next to raw `result.md`; stores project-local run files and a registry under `.pi/subagents/` so result/status collection can recover after compaction or reload while the main session remains alive
10
10
  - `src/lsp` — shared LSP diagnostics hook/library that enriches mutating tool results with diagnostics and shuts down language servers on session shutdown
11
11
  - `src/comment-checker` — AI-slop comment guard that listens to the `tool_result` event for `write` / `edit` / `apply_patch` mutations, extracts net-new code comment lines, classifies them (filler phrasing, restating code, decorative separators, generic paraphrasing, or — under aggressive strictness — any non-valuable comment), and appends a short nudge to the tool result so the agent removes unnecessary comments on its next turn; TODO/FIXME, license headers, docstrings, pragmas, linter directives, shebangs, and decorators are never flagged; language-agnostic across `//` / `/* */` / `#` / `--` / `<!-- -->` / triple-quote comment styles; per-session deduplication (at most one nudge per 30 s) prevents fix/remark loops; configured via the `commentChecker` section (`enabled`, `strictness`: `conservative` | `balanced` | `aggressive`, default `balanced`) or `PI_COMMENT_CHECKER_ENABLED` / `PI_COMMENT_CHECKER_STRICTNESS`
12
+ - `src/session-name` — `session_name` tool for reading or setting the current session title directly from tool calls, without relying on slash-command parsing
12
13
  - `src/repo-discovery` — `/idx-init`, `/idx-update`, and indexed-only `repo_architecture` / `repo_structure` / `repo_ast` / `repo_search` / `repo_explain` / `repo_deps`; tools register only when the launch project has `.indexer-cli`
13
14
  - `src/antigravity-auth` — `antigravity` custom provider with Google Antigravity OAuth login, startup account list, auth.json-only runtime account loading, `/antigravity-add-account` OAuth append into rotation, `/antigravity-account` status display, account rotation/failover, Antigravity plus Gemini CLI model registration, and streaming through the Cloud Code Assist unified gateway
14
15
  - `src/todo` — `todo` tool, `/todos`, `/todos-persist`, and `/todos-scope`; supports parent/subtask hierarchy, blockers, ready-task filtering, deferred out-of-scope items, batch operations, JSON/Markdown import/export, automatic clearing when all visible todos are completed, and optional project persistence via `/todos persist on` or `/todos-persist on`; localization/i18n has been removed
@@ -20,7 +21,7 @@ This package keeps shared Pi tools as ordinary source folders under `src/` and r
20
21
 
21
22
  `index.ts` is intentionally only a thin auto-discovery shim that re-exports `src/index.ts`. There is no `pi.extensions` manifest here, so local Pi auto-discovery loads the suite once via `~/.pi/agent/extensions/pi-tools-suite/index.ts` and does not double-register tools.
22
23
 
23
- Registration order is preserved in `src/index.ts`: glm-coding-discipline, ast-grep, async-subagents, lsp, comment-checker, repo-discovery command/tool gate, antigravity-auth provider, todo, model-tools, usage, web-search, dcp, then prompt-commands. Tool metadata and active model-specific tool sets have two modes: standard and repo-aware. When `.indexer-cli` enables `repo_*`, those tools stay active ahead of overlapping lower-level aliases so the indexed discovery surface has priority.
24
+ Registration order is preserved in `src/index.ts`: coding-discipline, ast-grep, async-subagents, lsp, comment-checker, session-name, repo-discovery command/tool gate, antigravity-auth provider, todo, model-tools, usage, web-search, dcp, then prompt-commands. Tool metadata and active model-specific tool sets have two modes: standard and repo-aware. When `.indexer-cli` enables `repo_*`, those tools stay active ahead of overlapping lower-level aliases so the indexed discovery surface has priority.
24
25
 
25
26
  ## Disabling modules
26
27
 
@@ -68,13 +69,14 @@ DCP settings are stored only under `dcp` in the user shared config file `~/.conf
68
69
  "maxContextLimit": 160000,
69
70
  "nudgeFrequency": 1,
70
71
  "iterationNudgeThreshold": 6,
72
+ "nudgeForce": "strong",
71
73
  "protectedTools": ["compress", "write", "edit", "subagents"]
72
74
  },
73
75
  "modelOverrides": {
74
- "openai-codex/gpt-5.5": {
76
+ "openai-codex/gpt-5*": {
75
77
  "compress": {
76
- "minContextPercent": "28%",
77
- "maxContextPercent": "48%"
78
+ "minContextPercent": "26%",
79
+ "maxContextPercent": "46%"
78
80
  }
79
81
  },
80
82
  "openai-codex/gpt-5.4-mini": {
@@ -208,11 +210,11 @@ Project-local overrides can be added in `.pi/pi-tools-suite.jsonc`; pi-tools-sui
208
210
 
209
211
  Sub-agent model routing normally follows task overrides, subagent type config, then `ASYNC_SUBAGENTS_MODEL` / `PI_SUBAGENTS_MODEL` fallbacks. Set `ASYNC_SUBAGENTS_FORCE_CURRENT_MODEL=1` (or `PI_SUBAGENTS_FORCE_CURRENT_MODEL=1`) to ignore task/config/env model choices and launch every sub-agent with the current parent session model. When this flag is enabled, any `--model` entries in sub-agent extra args are stripped so they cannot override the current model.
210
212
 
211
- For an oh-my-openagent-style workflow, run `/ultrawork` or `/ulw` to ask the parent agent to split broad work into configured async-subagents roles (`quick`, `scan`, `research`, `docs`, `frontend`, `implement`, `tests`, `review`, `deep`, `vision`). Set `ULTRAWORK=1` before launching Pi to apply that compact routing prompt to normal non-slash user inputs automatically. Set `ULTRAWORK_AUTO=1` to ask the lightweight router model to classify only the first normal user input on non-GPT parent models: clear broad/parallel work is transformed into ultrawork, vague potentially-complex work gets a soft delegation hint, and narrow work is left unchanged. GPT-like parent models skip only this automatic transform; they can still use `/ultrawork` and `subagents` normally. `frontend` is for UI/UX, styling, layout, responsive behavior, and visual component polish; `review` covers security/performance/audit tracks; `implement` covers refactors; `deep` covers debugging/root-cause; `vision` is only for screenshots/images when the parent model is a non-vision GLM-series model. Run `/hyperplan` to pressure-test a plan before implementation.
213
+ For an oh-my-openagent-style workflow, run `/ultrawork` or `/ulw` to ask the parent agent to split broad work into configured async-subagents roles (`quick`, `scan`, `research`, `docs`, `frontend`, `implement`, `tests`, `review`, `deep`, `oracle`). Set `ULTRAWORK=1` before launching Pi to apply that compact routing prompt to normal non-slash user inputs automatically. Set `ULTRAWORK_AUTO=1` to ask the lightweight router model to classify only the first normal user input on non-GPT parent models: clear broad/parallel work is transformed into ultrawork, vague potentially-complex work gets a soft delegation hint, and narrow work is left unchanged. GPT-like parent models skip only this automatic transform; they can still use `/ultrawork` and `subagents` normally. `frontend` is for UI/UX, styling, layout, responsive behavior, and visual component polish; `review` covers security/performance/audit tracks; `implement` covers refactors; `deep` covers debugging/root-cause; `oracle` is for sparse cross-provider second opinions on high-stakes uncertainty. Run `/hyperplan` to pressure-test a plan before implementation.
212
214
 
213
215
  Async-subagents also injects a lightweight oh-my-openagent-style system-prompt strategy by model: non-GPT parents get `parallel-first`, an orchestration-first hint that favors ultrawork/subagents for broad work, while GPT-like parents get `deep-work`, a direct deep-worker hint that uses subagents only when clearly useful. Explicit custom system prompts (`--system-prompt`, `SYSTEM.md`, custom templates) are respected and skip this injection by default. Disable it with `PI_AGENT_STRATEGY=off`; force a strategy with `PI_AGENT_STRATEGY=parallel-first` or `PI_AGENT_STRATEGY=deep-work`; set `PI_AGENT_STRATEGY_WITH_CUSTOM_PROMPT=1` to append it even when a custom prompt is present.
214
216
 
215
- When the parent model cannot inspect images, async-subagents adds vision-delegation guidance and can save current-turn image attachments under `.pi/subagents/attachments/` so a `vision` sub-agent can receive them as `imagePaths`. Dynamic provider capabilities can be missing or stale after switching models, so blind parent models can still be configured explicitly with case-insensitive `*` masks under `asyncSubagents.vision.blindModelPatterns` in `~/.config/pi/pi-tools-suite.jsonc`. GLM is no longer treated as blind by async-subagents by default; the main-session `glm-coding-discipline` lookup tool is the preferred path for GLM visual lookups.
217
+ For blind-model screenshot/image inspection, use the main-session `coding-discipline` lookup tool. Async-subagents still supports `imagePaths` on tasks when a broader delegated track genuinely needs images, but it no longer ships a dedicated `vision` role. Dynamic provider capabilities can be missing or stale after switching models, so blind parent models can still be configured explicitly with case-insensitive `*` masks under `asyncSubagents.vision.blindModelPatterns` in `~/.config/pi/pi-tools-suite.jsonc`; this keeps guidance honest, not a sub-agent role.
216
218
 
217
219
  When a task omits `subagentType`, async-subagents asks a lightweight router model to choose one configured type for each task from the task text/scope and the `types.<name>.description` metadata. Explicit task `subagentType` still wins. Keep type descriptions short, literal, and distinct because they are inserted into the router prompt for a small model. Router settings live under `asyncSubagents.routing` (`enabled`, `model`, `maxTaskChars`, `maxTokens`, `maxRetries`, `timeoutMs`, `debug`); the default router model is `zai/glm-4.5-air`. If the router is disabled, unavailable, aborted, or returns invalid JSON, omitted types fall back to `defaultType`.
218
220
 
@@ -231,7 +233,7 @@ Example shared async-subagents config section:
231
233
  },
232
234
  "presets": {
233
235
  "cheap": {
234
- "description": "Use GLM for text/code roles; keep vision on its dedicated model.",
236
+ "description": "Use GLM for text/code roles.",
235
237
  "types": {
236
238
  "quick": { "model": "zai/glm-5.2", "thinking": "off" },
237
239
  "frontend": { "model": "antigravity/gemini-3-flash-preview", "fallbackModels": ["zai/glm-5.2"], "thinking": "medium" },
@@ -253,6 +255,29 @@ Example shared async-subagents config section:
253
255
  }
254
256
  ```
255
257
 
258
+ ### Parent-model-aware model selection (`modelByParent`)
259
+
260
+ Any type profile can carry `modelByParent`: a map from glob model refs (matched against the **current parent model**, e.g. `"zai/*"`) to a model for that role. The first matching key wins. Values may be a model string or `{ "model": "...", "fallbackModels": [...] }`. It is resolved after an explicit task `model` / `forcedModel`, but **before** the preset/static profile `model`, so a role can always pick a model based on who the parent is — independent of the active preset.
261
+
262
+ The canonical use case is an **`oracle`** role that consults a flagship model from a *different* provider than the parent for a second opinion:
263
+
264
+ ```jsonc
265
+ "oracle": {
266
+ "description": "Cross-provider second opinion: consult a flagship from a different provider than the parent to pressure-test a hard decision. Read-only; advise, do not edit.",
267
+ "model": "openai-codex/gpt-5.5",
268
+ "fallbackModels": ["zai/glm-5.2"],
269
+ "thinking": "xhigh",
270
+ "modelByParent": {
271
+ "zai/*": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"] },
272
+ "openai-codex/*": "zai/glm-5.2",
273
+ "antigravity/*": { "model": "zai/glm-5.2", "fallbackModels": ["openai-codex/gpt-5.5"] },
274
+ "anthropic/*": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"] }
275
+ }
276
+ }
277
+ ```
278
+
279
+ With this config a GLM parent (`zai/*`) spawns the oracle on `gpt-5.5`, a GPT parent (`openai-codex/*`) spawns it on `glm-5.2`, and so on — automatically, at spawn time, with no `task.model` needed. The parent model ref is read from the spawn context (`ctx.model`) and passed into resolution. Pattern matching is case-insensitive `*` glob (same engine as `vision.blindModelPatterns`). When no key matches (or no parent model is known), the role falls back to its static `model` + `fallbackModels`. An explicit `task.model` or `ASYNC_SUBAGENTS_FORCE_CURRENT_MODEL=1` still overrides the match.
280
+
256
281
  Sub-agents run with `--no-session` by default to avoid writing duplicate Pi session JSONL files for fire-and-forget background work. Set `ASYNC_SUBAGENTS_ENABLE_SESSIONS=1` to restore persisted per-agent sessions under each agent's `sessions/` directory; this also registers the session-navigation slash commands (`/sub-open`, `/sub-back`, `/sub-where`) needed for switching and deeper post-mortem navigation.
257
282
 
258
283
  Sub-agent runs are stored in the current project's `.pi/subagents/` directory while the main session is alive. Each spawn updates `.pi/subagents/registry.json` with the latest run and `agentId -> runDir` mappings. Because of that, `subagents({ action: "status" })`, `wait`, and `stop` can omit `runDir` to target the latest run, and `subagents({ action: "result", agentId: "..." })` can resolve the run from the registry even if the exact `runDir` was lost during compaction. Result reads always return a summary-first response with artifact paths; raw `result.md` and `stderr.log` are not inlined, which avoids IPC/socket buffer overflows. Include `runDir` when you need an older or non-latest run, and use `cleanup` with `delete=true` to remove collected old runs before the session ends. On normal main-session shutdown, Pi stops sub-agents and removes the project-local run files/registry to avoid leaving `.pi/subagents/` clutter behind; reload and fork shutdowns preserve them so in-process recovery still works.
@@ -38,9 +38,9 @@
38
38
  "vscode-languageserver-protocol": "^3.17.5"
39
39
  },
40
40
  "peerDependencies": {
41
- "@earendil-works/pi-ai": "0.79.5",
42
- "@earendil-works/pi-coding-agent": "0.79.5",
43
- "@earendil-works/pi-tui": "0.79.5",
41
+ "@earendil-works/pi-ai": "0.79.7",
42
+ "@earendil-works/pi-coding-agent": "0.79.7",
43
+ "@earendil-works/pi-tui": "0.79.7",
44
44
  "typebox": "*"
45
45
  },
46
46
  "devDependencies": {
@@ -28,10 +28,11 @@
28
28
  // for short synthetic tests.
29
29
  // "timeoutMs": 1800000,
30
30
 
31
- // Parent-model vision overrides. Pi provider metadata can be missing or stale
32
- // after switching models, so async-subagents treats these glob-like model refs
33
- // as text-only even if dynamic capability detection is inconclusive. Matching
34
- // is case-insensitive and supports `*` wildcards. Set [] to disable built-ins.
31
+ // Parent-model image-capability overrides. Pi provider metadata can be missing
32
+ // or stale after switching models, so async-subagents treats these glob-like
33
+ // model refs as text-only when dynamic detection is inconclusive. Blind-model
34
+ // screenshot inspection should use lookup; this only keeps guidance honest.
35
+ // Matching is case-insensitive and supports `*` wildcards.
35
36
  "vision": {
36
37
  "blindModelPatterns": ["zai/glm*", "glm*", "*/glm*"]
37
38
  },
@@ -47,6 +48,19 @@
47
48
  // ],
48
49
  // "promptOverride": "You are a specialized {subagentType} sub-agent.\nParent: {parentObjective}\nTask: {task}\nScope: {scope}",
49
50
 
51
+ // Parent-model-aware model selection (optional, any type profile).
52
+ // modelByParent maps glob model refs (matched against the current parent
53
+ // model, e.g. "zai/*") to a model for this role. The first matching key wins.
54
+ // Values may be a model string or { model, fallbackModels? }. It is resolved
55
+ // after an explicit task.model / forcedModel, but before the preset/static
56
+ // profile model, so a role like an "oracle" can always pick a flagship from a
57
+ // DIFFERENT provider than the parent. Example:
58
+ // "modelByParent": {
59
+ // "zai/*": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"] },
60
+ // "openai-codex/*": "zai/glm-5.2",
61
+ // "antigravity/*": { "model": "zai/glm-5.2", "fallbackModels": ["openai-codex/gpt-5.5"] }
62
+ // }
63
+
50
64
  // Role auto-selection when subagentType is omitted is handled by a small LLM
51
65
  // router request. The router receives each task plus the configured type
52
66
  // descriptions, then returns one allowed subagentType per task. Explicit
@@ -95,7 +109,7 @@
95
109
 
96
110
  "presets": {
97
111
  "cheap": {
98
- "description": "Use cheap GLM/Gemini Flash models for text/code roles; keep vision on the enabled GPT vision model.",
112
+ "description": "Use cheap GLM/Gemini Flash models for text/code roles.",
99
113
  "types": {
100
114
  "quick": { "model": "zai/glm-4.5-air", "thinking": "off" },
101
115
  "scan": { "model": "zai/glm-4.5-air", "thinking": "off" },
@@ -105,8 +119,7 @@
105
119
  "tests": { "model": "zai/glm-5-turbo", "thinking": "medium" },
106
120
  "review": { "model": "zai/glm-5.2", "thinking": "high" },
107
121
  "implement": { "model": "zai/glm-5.2", "thinking": "high" },
108
- "deep": { "model": "zai/glm-5.2", "thinking": "high" },
109
- "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
122
+ "deep": { "model": "zai/glm-5.2", "thinking": "high" }
110
123
  }
111
124
  },
112
125
 
@@ -121,8 +134,7 @@
121
134
  "tests": { "model": "openai-codex/gpt-5.4-mini", "fallbackModels": ["zai/glm-5-turbo"], "thinking": "medium" },
122
135
  "review": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"], "thinking": "high" },
123
136
  "implement": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"], "thinking": "high" },
124
- "deep": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"], "thinking": "high" },
125
- "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
137
+ "deep": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"], "thinking": "high" }
126
138
  }
127
139
  },
128
140
 
@@ -137,8 +149,7 @@
137
149
  "tests": { "model": "antigravity/antigravity-claude-sonnet-4-6", "fallbackModels": ["openai-codex/gpt-5.4-mini", "zai/glm-5-turbo"], "thinking": "high" },
138
150
  "review": { "model": "antigravity/antigravity-claude-sonnet-4-6", "fallbackModels": ["openai-codex/gpt-5.5", "zai/glm-5.2"], "thinking": "high" },
139
151
  "implement": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"], "thinking": "high" },
140
- "deep": { "model": "antigravity/antigravity-claude-opus-4-6-thinking", "fallbackModels": ["openai-codex/gpt-5.5", "zai/glm-5.2"], "thinking": "high" },
141
- "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
152
+ "deep": { "model": "antigravity/antigravity-claude-opus-4-6-thinking", "fallbackModels": ["openai-codex/gpt-5.5", "zai/glm-5.2"], "thinking": "high" }
142
153
  }
143
154
  }
144
155
  },
@@ -212,16 +223,19 @@
212
223
  "thinking": "high"
213
224
  },
214
225
 
215
- "vision": {
216
- "description": "Use only when task has imagePaths, screenshots, or asks to inspect visible UI/image content for a text-only parent.",
217
- "model": "openai-codex/gpt-5.4-mini",
218
- "thinking": "off",
219
- "promptAppend": [
220
- "You are a vision helper for a parent model that may not be able to see images.",
221
- "Inspect any attached images and any image paths mentioned in the task/scope. Describe concrete visible details, UI state, text, layout, errors, and uncertainties.",
222
- "If focus instructions are provided, prioritize them, but still mention other important visible findings.",
223
- "Do not make code changes. Return a compact visual description that the parent agent can rely on."
224
- ]
226
+ "oracle": {
227
+ "description": "Oracle: cross-provider flagship second opinion for hard or high-stakes uncertainty. Use sparingly to pressure-test architecture, plans, root-cause hypotheses, risk/security calls, or final recommendations when independent disagreement is valuable. Read-only; advise, do not edit.",
228
+ "model": "openai-codex/gpt-5.5",
229
+ "fallbackModels": ["zai/glm-5.2"],
230
+ "thinking": "xhigh",
231
+ "tools": ["read", "grep", "bash"],
232
+ "modelByParent": {
233
+ "zai/*": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"] },
234
+ "openai-codex/*": { "model": "zai/glm-5.2", "fallbackModels": ["openai-codex/gpt-5.5"] },
235
+ "antigravity/*": { "model": "zai/glm-5.2", "fallbackModels": ["openai-codex/gpt-5.5"] },
236
+ "anthropic/*": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"] }
237
+ },
238
+ "promptAppend": "You are an oracle: a flagship model from a different provider giving a second opinion to the parent agent. Give a concise, decisive recommendation with key tradeoffs and risks. Disagree when warranted; do not rubber-stamp. Do not edit unless explicitly asked."
225
239
  }
226
240
  }
227
241
  }
@@ -53,7 +53,7 @@ const COPY_SAMPLE_CONFIG_LABEL = "Copy sample asyncSubagents config";
53
53
 
54
54
  export const ULTRAWORK_PROMPT = `Run ultrawork mode for the current objective.
55
55
 
56
- Use subagents when independent parallel tracks help. Pick subagentType from configured roles: quick, scan, research, docs, frontend, implement, tests, review, deep, vision. Use review for security/performance/audit tracks, implement for refactors, deep for debugging/root-cause. Use frontend for UI/UX and visual frontend implementation; use vision only for screenshots/images when the parent model is a non-vision GLM-series model.
56
+ Use subagents when independent parallel tracks help. Pick subagentType from configured roles: quick, scan, research, docs, frontend, implement, tests, review, deep, oracle. Use review for security/performance/audit tracks, implement for refactors, deep for debugging/root-cause. Use frontend for UI/UX and visual frontend implementation; use oracle sparingly for cross-provider second opinions on high-stakes uncertainty.
57
57
 
58
58
  Keep parent context lean: spawn for broad parallel work, read results only when needed, and finish unless genuinely blocked.`;
59
59
 
@@ -14,7 +14,7 @@ const TRUE_ENV_PATTERN = /^(1|true|on|yes|auto)$/i;
14
14
  const PARALLEL_FIRST_STRATEGY_PROMPT = `<agent_strategy name="parallel-first">
15
15
  This is an orchestration hint for Pi, not a replacement for the user's instructions.
16
16
 
17
- Default posture: orchestration-first for non-GPT models. For broad, multi-file, ambiguous, review/audit, frontend, test-strategy, architecture, or root-cause work, prefer ultrawork mode: split independent tracks and spawn focused async subagents with the configured roles. Keep the parent context lean, collect compact results only when needed, synthesize the findings, then verify before finishing.
17
+ Default posture: orchestration-first for non-GPT models. For broad, multi-file, ambiguous, review/audit, frontend, test-strategy, architecture, or root-cause work, prefer ultrawork mode: split independent tracks and spawn focused async subagents with the configured roles. For high-stakes uncertainty, add one oracle track for a cross-provider second opinion. Keep the parent context lean, collect compact results only when needed, synthesize the findings, then verify before finishing.
18
18
 
19
19
  Before DCP/compress while work is unfinished, keep one in_progress todo with objective + next step; compression summaries must preserve Active objective and Next step.
20
20
 
@@ -28,7 +28,7 @@ Default posture: autonomous deep worker. Build context directly, make concrete p
28
28
 
29
29
  Before DCP/compress while work is unfinished, keep one in_progress todo with objective + next step; compression summaries must preserve Active objective and Next step.
30
30
 
31
- For broad work, keep delegation explicit and bounded: spawn focused review/research/tests/frontend/deep tracks, read compact results, make the final decisions in the parent session, and report only what matters.
31
+ For broad work, keep delegation explicit and bounded: spawn focused review/research/tests/frontend/deep tracks, plus one oracle track only for high-stakes uncertainty or final plan checks. Read compact results, make the final decisions in the parent session, and report only what matters.
32
32
  </agent_strategy>`;
33
33
 
34
34
  export function agentStrategyPrompt(options: AgentStrategyOptions = {}): string | undefined {
@@ -6,11 +6,25 @@ import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser";
6
6
  import { ensurePiToolsSuiteUserConfig, getPiToolsSuiteUserConfigPath } from "../../config.js";
7
7
  import type { AgentTask, RetryConfig } from "./types.js";
8
8
 
9
+ export interface ModelByParentEntry {
10
+ /** Model ref to use when the parent model matches the entry's pattern. */
11
+ model: string;
12
+ /** Ordered fallbacks used when this entry's model hits quota/rate limits; replaces the normal fallback chain. */
13
+ fallbackModels?: string[];
14
+ }
15
+
9
16
  export interface SubagentTypeConfig {
10
17
  description?: string;
11
18
  model?: string;
12
19
  /** Ordered model fallbacks used when the selected model hits quota/rate limits. */
13
20
  fallbackModels?: string[];
21
+ /**
22
+ * Parent-model-aware model selection. Keys are glob model refs (e.g. "zai/*")
23
+ * matched against the current parent model; the first matching key wins.
24
+ * Values may be a model ref string or { model, fallbackModels? }.
25
+ * Resolved after explicit task.model / forcedModel, before preset/static model.
26
+ */
27
+ modelByParent?: Record<string, ModelByParentEntry>;
14
28
  thinking?: string;
15
29
  tools?: string[];
16
30
  extraArgs?: string[];
@@ -127,6 +141,8 @@ export interface ResolveAgentTaskOptions {
127
141
  extraArgs?: string[];
128
142
  /** Force every sub-agent to use this model, ignoring task/profile/env model selection. */
129
143
  forcedModel?: string;
144
+ /** Current parent model ref (provider/model). Enables type.modelByParent matching. */
145
+ parentModel?: string;
130
146
  /** Force a wall-clock timeout for every sub-agent spawned by this call. */
131
147
  timeoutMs?: number;
132
148
  }
@@ -201,16 +217,19 @@ const BUILTIN_CONFIG: SubagentConfig = {
201
217
  description: "Use for broad hard reasoning: architecture, root-cause analysis, cross-module impact, complex debugging or tradeoffs.",
202
218
  thinking: "high",
203
219
  },
204
- vision: {
205
- description: "Use only when task has imagePaths, screenshots, or asks to inspect visible UI/image content for a text-only parent.",
206
- model: "openai-codex/gpt-5.4-mini",
207
- thinking: "off",
208
- promptAppend: [
209
- "You are a vision helper for a parent model that may not be able to see images.",
210
- "Inspect any attached images and any image paths mentioned in the task/scope. Describe concrete visible details, UI state, text, layout, errors, and uncertainties.",
211
- "If focus instructions are provided, prioritize them, but still mention other important visible findings.",
212
- "Do not make code changes. Return a compact visual description that the parent agent can rely on.",
213
- ].join("\n"),
220
+ oracle: {
221
+ description: "Oracle: cross-provider flagship second opinion for hard or high-stakes uncertainty. Use sparingly to pressure-test architecture, plans, root-cause hypotheses, risk/security calls, or final recommendations when independent disagreement is valuable. Read-only; advise, do not edit.",
222
+ model: "openai-codex/gpt-5.5",
223
+ fallbackModels: ["zai/glm-5.2"],
224
+ modelByParent: {
225
+ "zai/*": { model: "openai-codex/gpt-5.5", fallbackModels: ["zai/glm-5.2"] },
226
+ "openai-codex/*": { model: "zai/glm-5.2", fallbackModels: ["openai-codex/gpt-5.5"] },
227
+ "antigravity/*": { model: "zai/glm-5.2", fallbackModels: ["openai-codex/gpt-5.5"] },
228
+ "anthropic/*": { model: "openai-codex/gpt-5.5", fallbackModels: ["zai/glm-5.2"] },
229
+ },
230
+ thinking: "xhigh",
231
+ tools: ["read", "grep", "bash"],
232
+ promptAppend: "You are an oracle: a flagship model from a different provider giving a second opinion to the parent agent. Give a concise, decisive recommendation with key tradeoffs and risks. Disagree when warranted; do not rubber-stamp. Do not edit unless explicitly asked.",
214
233
  },
215
234
  },
216
235
  };
@@ -325,14 +344,19 @@ export function resolveAgentTaskConfig(
325
344
  const promptAppend = joinTextBlocks(profile?.promptAppend, task.promptAppend);
326
345
  const forcedModel = trimString(globalOptions.forcedModel);
327
346
  const taskModel = trimString(task.model);
347
+ const parentMatch = resolveModelByParent(profile, trimString(globalOptions.parentModel));
348
+ const parentMatchModel = trimString(parentMatch?.model);
328
349
  const presetTypeModel = trimString(presetType?.model);
329
350
  const globalModel = trimString(globalOptions.model);
330
351
  const presetModel = trimString(preset?.model);
331
352
  const profileModel = trimString(profile?.model);
332
- const model = forcedModel || taskModel || presetTypeModel || globalModel || presetModel || profileModel;
353
+ const model = forcedModel || taskModel || parentMatchModel || presetTypeModel || globalModel || presetModel || profileModel;
354
+ const usedParentMatch = Boolean(parentMatchModel) && model === parentMatchModel;
333
355
  const fallbackModels = forcedModel || taskModel
334
356
  ? []
335
- : resolveFallbackModels({ model, presetType, preset, profile });
357
+ : usedParentMatch && parentMatch?.fallbackModels && parentMatch.fallbackModels.length > 0
358
+ ? parentMatch.fallbackModels
359
+ : resolveFallbackModels({ model, presetType, preset, profile });
336
360
  const extraArgs = forcedModel
337
361
  ? stripModelArgs([...profileExtraArgs, ...presetTypeExtraArgs, ...taskExtraArgs, ...presetExtraArgs, ...globalExtraArgs])
338
362
  : [...profileExtraArgs, ...presetTypeExtraArgs, ...taskExtraArgs, ...presetExtraArgs, ...globalExtraArgs];
@@ -480,6 +504,7 @@ function normalizeConfig(value: Record<string, unknown>, file: string): Partial<
480
504
  description: trimString(rawProfile.description),
481
505
  model: trimString(rawProfile.model),
482
506
  fallbackModels: modelList(rawProfile.fallbackModels, rawProfile.fallbackModel),
507
+ modelByParent: normalizeModelByParent(rawProfile.modelByParent, name, file),
483
508
  thinking: trimString(rawProfile.thinking),
484
509
  tools: arrayOfStrings(rawProfile.tools),
485
510
  extraArgs: arrayOfStrings(rawProfile.extraArgs),
@@ -555,6 +580,7 @@ function compactProfile(profile: SubagentTypeConfig): SubagentTypeConfig {
555
580
  if (profile.description) compact.description = profile.description;
556
581
  if (profile.model) compact.model = profile.model;
557
582
  if (profile.fallbackModels && profile.fallbackModels.length > 0) compact.fallbackModels = profile.fallbackModels;
583
+ if (profile.modelByParent) compact.modelByParent = profile.modelByParent;
558
584
  if (profile.thinking) compact.thinking = profile.thinking;
559
585
  if (profile.tools && profile.tools.length > 0) compact.tools = profile.tools;
560
586
  if (profile.extraArgs && profile.extraArgs.length > 0) compact.extraArgs = profile.extraArgs;
@@ -606,6 +632,38 @@ function normalizePresetTypeOverrides(value: unknown, file: string, presetName:
606
632
  return Object.keys(types).length > 0 ? types : undefined;
607
633
  }
608
634
 
635
+ function normalizeModelByParent(value: unknown, typeName: string, file: string): Record<string, ModelByParentEntry> | undefined {
636
+ if (value === undefined || value === null) return undefined;
637
+ if (!isRecord(value)) throw new Error(`Subagent type "${typeName}" modelByParent must be an object: ${file}`);
638
+ const out: Record<string, ModelByParentEntry> = {};
639
+ for (const [pattern, raw] of Object.entries(value)) {
640
+ const pat = trimString(pattern);
641
+ if (!pat) continue;
642
+ if (typeof raw === "string") {
643
+ const model = trimString(raw);
644
+ if (model) out[pat] = { model };
645
+ continue;
646
+ }
647
+ if (isRecord(raw)) {
648
+ const model = trimString(raw.model);
649
+ if (!model) throw new Error(`Subagent type "${typeName}" modelByParent["${pat}"].model must be a non-empty string: ${file}`);
650
+ out[pat] = { model, fallbackModels: modelList(raw.fallbackModels, raw.fallbackModel) };
651
+ continue;
652
+ }
653
+ throw new Error(`Subagent type "${typeName}" modelByParent["${pat}"] must be a string or object: ${file}`);
654
+ }
655
+ return Object.keys(out).length > 0 ? out : undefined;
656
+ }
657
+
658
+ function resolveModelByParent(profile: SubagentTypeConfig | undefined, parentModelRef: string | undefined): ModelByParentEntry | undefined {
659
+ const entries = profile?.modelByParent;
660
+ if (!entries || !parentModelRef) return undefined;
661
+ for (const [pattern, entry] of Object.entries(entries)) {
662
+ if (modelPatternRegExp(pattern).test(parentModelRef)) return entry;
663
+ }
664
+ return undefined;
665
+ }
666
+
609
667
  function resolveFallbackModels(options: {
610
668
  model?: string;
611
669
  presetType?: SubagentPresetTypeOverride;
@@ -32,7 +32,7 @@ export interface RoutedSubagentTasks {
32
32
  const ROUTER_SYSTEM_PROMPT = [
33
33
  "You route Pi async sub-agent tasks to the best configured subagentType.",
34
34
  "Choose exactly one allowed type for each task. Use the allowed type descriptions as the source of truth.",
35
- "Prefer the most specific matching type over generic quick/deep. Use frontend for UI/UX implementation or visual frontend polish; use vision only for attached images/screenshots or image inspection.",
35
+ "Prefer the most specific matching type over generic quick/deep. Use frontend for UI/UX implementation or visual frontend polish. For pure image inspection, use the lookup tool rather than subagents.",
36
36
  "Return only strict JSON with this shape: {\"routes\":[{\"id\":\"task-id\",\"subagentType\":\"type\"}]}",
37
37
  "Do not include markdown, comments, explanations, or unknown types.",
38
38
  ].join("\n");
@@ -677,7 +677,7 @@ function subagentEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
677
677
  PI_TERMINAL_BELL_DISABLED: "1",
678
678
  PI_TOOLS_SUITE_DISABLED_MODULES: appendEnvList(env.PI_TOOLS_SUITE_DISABLED_MODULES, [
679
679
  "async-subagents",
680
- "glm-coding-discipline",
680
+ "coding-discipline",
681
681
  "question",
682
682
  ]),
683
683
  };
@@ -14,7 +14,7 @@ export interface AgentTask {
14
14
  promptAppend?: string;
15
15
  /** Full prompt replacement for this task. Supports prompt template variables. */
16
16
  promptOverride?: string;
17
- /** Optional visual attention instructions for vision-capable sub-agents. */
17
+ /** Optional visual attention instructions for agents that receive imagePaths. */
18
18
  focus?: string;
19
19
  /** Local image files to attach to the sub-agent RPC prompt. */
20
20
  imagePaths?: string[];
@@ -351,7 +351,7 @@ function visionCapabilityPrompt(event: unknown, ctx: unknown): string | undefine
351
351
  : "The current parent model cannot inspect images/screenshots directly.";
352
352
  const delegation = subagentsAvailable
353
353
  ? visionSubagentDelegationText(bridge?.attachments ?? [])
354
- : "If visual understanding is required, ask the user to switch to a vision-capable model or provide a path that can be inspected by a vision-capable helper.";
354
+ : "If visual understanding is required, use the lookup tool if available; otherwise ask the user to switch to a vision-capable model or provide an inspectable image path.";
355
355
  const bridgeWarning = visionBridgeWarning(bridge);
356
356
  return [
357
357
  "Vision capability constraint:",
@@ -360,8 +360,8 @@ function visionCapabilityPrompt(event: unknown, ctx: unknown): string | undefine
360
360
  bridgeWarning,
361
361
  delegation,
362
362
  bridge?.attachments.length
363
- ? "Use those bridged paths exactly as imagePaths if delegating."
364
- : "If an image only arrived as an attachment and no local file path/reference is available to subagents, ask the user for a file path or to switch the parent model to one with image input support.",
363
+ ? "Use those bridged paths exactly as lookup imagePaths if needed."
364
+ : "If an image only arrived as an attachment and no local file path/reference is available to lookup, ask the user for a file path or to switch the parent model to one with image input support.",
365
365
  ].filter(Boolean).join(" ");
366
366
  }
367
367
 
@@ -380,16 +380,16 @@ function visionCapableParentPrompt(event: unknown): string | undefined {
380
380
  "Vision capability note:",
381
381
  "The current parent model supports image input.",
382
382
  "If the user provided image attachments or local image file paths, inspect them directly first; for local paths, use the read tool on the image path.",
383
- "Do not delegate to a vision sub-agent solely to gain visual access; use a vision sub-agent only when the user explicitly asks to delegate/parallelize or a separate visual review is useful.",
383
+ "Do not delegate solely to gain visual access; use lookup for focused visual checks and subagents only for broader independent tracks.",
384
384
  ].join(" ");
385
385
  }
386
386
 
387
387
  function visionSubagentDelegationText(attachments: BridgedImageAttachment[]): string {
388
388
  if (attachments.length === 0) {
389
- return "If visual understanding is required, delegate to the subagents tool with subagentType='vision' plus imagePaths/focus when the image is available as a local file path.";
389
+ return "If visual understanding is required, use the lookup tool with imagePaths/focus when the image is available as a local file path.";
390
390
  }
391
391
  const imagePaths = attachments.map((attachment) => attachment.relativePath);
392
- return `Attached images were saved for vision delegation. If visual understanding is required, delegate to the subagents tool with subagentType='vision' and imagePaths=${JSON.stringify(imagePaths)} plus a focused task/focus.`;
392
+ return `Attached images were saved for lookup. If visual understanding is required, call lookup with imagePaths=${JSON.stringify(imagePaths)} and a focused question.`;
393
393
  }
394
394
 
395
395
  function visionBridgeWarning(bridge: BridgeImageAttachmentsResult | undefined): string | undefined {
@@ -12,7 +12,7 @@ export type {
12
12
  } from "./core/types.js";
13
13
 
14
14
  export { createRunDir, getRunRoot, resolveRunDir, validateBasename } from "./core/paths.js";
15
- export type { CopySubagentConfigSampleResult, ResolvedAgentTaskConfig, ResolvedSubagentRoutingConfig, ResolveAgentTaskOptions, SubagentConfig, SubagentPreset, SubagentRoutingConfig, SubagentTypeConfig, SubagentVisionConfig } from "./core/config.js";
15
+ export type { CopySubagentConfigSampleResult, ModelByParentEntry, ResolvedAgentTaskConfig, ResolvedSubagentRoutingConfig, ResolveAgentTaskOptions, SubagentConfig, SubagentPreset, SubagentRoutingConfig, SubagentTypeConfig, SubagentVisionConfig } from "./core/config.js";
16
16
  export {
17
17
  configFiles,
18
18
  copySubagentConfigSample,
@@ -167,12 +167,12 @@ const AgentTaskSchema = Type.Object({
167
167
  id: Type.Optional(Type.String({ description: "Short identifier for this agent (used as directory name). If omitted, the spawn action assigns agent-1, agent-2, etc." })),
168
168
  task: Type.String({ description: "Focused task description for the sub-agent" }),
169
169
  scope: Type.Optional(Type.String({ description: "Relevant files/areas for this task" })),
170
- subagentType: Type.Optional(Type.String({ description: "Logical sub-agent type/profile from config. Usually omit this so the router selects from the current config; set only for an explicit user-requested role, vision/image handling, deterministic tests, or another concrete override." })),
170
+ subagentType: Type.Optional(Type.String({ description: "Logical sub-agent type/profile from config. Usually omit this so the router selects from the current config; set only for an explicit user-requested role, deterministic tests, or another concrete override." })),
171
171
  model: Type.Optional(Type.String({ description: "Explicit model override for this sub-agent. Prefer subagentType for reusable routing." })),
172
172
  thinking: Type.Optional(Type.String({ description: "Per-agent thinking level override (off, minimal, low, medium, high, xhigh)." })),
173
173
  promptAppend: Type.Optional(Type.String({ description: "Extra prompt instructions appended after the generated/type prompt." })),
174
174
  promptOverride: Type.Optional(Type.String({ description: "Full prompt replacement for this sub-agent. Prefer configuring this per subagentType." })),
175
- focus: Type.Optional(Type.String({ description: "For vision sub-agents: what to pay special attention to while inspecting attached images." })),
175
+ focus: Type.Optional(Type.String({ description: "Optional focus/attention instructions for attached images or scoped inspection." })),
176
176
  attention: Type.Optional(Type.String({ description: "Alias for focus, accepted for compatibility." })),
177
177
  imagePaths: Type.Optional(Type.Array(Type.String(), { description: "Local image paths to attach to this sub-agent prompt (jpg, png, gif, or webp). Relative paths resolve from cwd." })),
178
178
  tools: Type.Optional(Type.Array(Type.String(), { description: "Tool names to enable (e.g. ['read','grep','bash'])" })),
@@ -237,12 +237,14 @@ export function registerSpawnTool(
237
237
  }
238
238
  const routed = await routeSubagentTasks(normalized.tasks ?? [], config, ctx as any, signal ?? undefined);
239
239
  const timeoutMs = timeoutMsFromSeconds(params.timeoutSeconds);
240
+ const parentModel = currentModelRef((ctx as { model?: unknown }).model);
240
241
  const resolvedTasks = routed.tasks.map((task) => applySessionModelFallback(
241
242
  resolveAgentTaskConfig(task, config, {
242
243
  preset: activePreset,
243
244
  thinking: params.thinking,
244
245
  extraArgs: Array.isArray(params.extraArgs) ? params.extraArgs : [],
245
246
  forcedModel,
247
+ parentModel,
246
248
  timeoutMs,
247
249
  }),
248
250
  ));
@@ -19,12 +19,12 @@ const AgentTaskSchema = Type.Object({
19
19
  id: Type.Optional(Type.String({ description: "Short identifier for this agent (used as directory name). If omitted, assigns agent-1, agent-2, etc." })),
20
20
  task: Type.String({ description: "Focused task description for the sub-agent" }),
21
21
  scope: Type.Optional(Type.String({ description: "Relevant files/areas for this task" })),
22
- subagentType: Type.Optional(Type.String({ description: "Logical sub-agent type/profile from config. Usually omit this so the router selects from the current config; set only for an explicit user-requested role, vision/image handling, deterministic tests, or another concrete override." })),
22
+ subagentType: Type.Optional(Type.String({ description: "Logical sub-agent type/profile from config. Usually omit this so the router selects from the current config; set only for an explicit user-requested role, deterministic tests, or another concrete override." })),
23
23
  model: Type.Optional(Type.String({ description: "Explicit model override for this sub-agent. Prefer subagentType for reusable routing." })),
24
24
  thinking: Type.Optional(Type.String({ description: "Per-agent thinking level override (off, minimal, low, medium, high, xhigh)." })),
25
25
  promptAppend: Type.Optional(Type.String({ description: "Extra prompt instructions appended after the generated/type prompt." })),
26
26
  promptOverride: Type.Optional(Type.String({ description: "Full prompt replacement for this sub-agent. Prefer configuring this per subagentType." })),
27
- focus: Type.Optional(Type.String({ description: "For vision sub-agents: what to pay special attention to while inspecting attached images." })),
27
+ focus: Type.Optional(Type.String({ description: "Optional focus/attention instructions for attached images or scoped inspection." })),
28
28
  attention: Type.Optional(Type.String({ description: "Alias for focus, accepted for compatibility." })),
29
29
  imagePaths: Type.Optional(Type.Array(Type.String(), { description: "Local image paths to attach to this sub-agent prompt (jpg, png, gif, or webp). Relative paths resolve from cwd." })),
30
30
  tools: Type.Optional(Type.Array(Type.String(), { description: "Tool names to enable (e.g. ['read','grep','bash'])" })),
@@ -59,7 +59,7 @@ const LOOKUP_TOOL_PARAMS = Type.Object(
59
59
  );
60
60
 
61
61
  const QUALITY_DISCIPLINE_LINES = [
62
- "GLM TOOL-ONLY CODING AGENT CONTRACT.",
62
+ "TOOL-ONLY CODING AGENT CONTRACT.",
63
63
  "",
64
64
  "This contract controls the assistant output channel. Follow it literally.",
65
65
  "Treat every user coding request as a tool-driven task, not a chat conversation.",
@@ -126,9 +126,14 @@ const QUALITY_DISCIPLINE_LINES = [
126
126
  "While WORKING, this behavior is internal and expressed only through tool choices, not prose.",
127
127
  "",
128
128
  "Maintain these invariants:",
129
- "- preserve existing behavior unless the user asked to change it;",
130
- "- make minimal, localized changes;",
131
- "- respect project conventions already present in nearby code;",
129
+ "- make the smallest correct change;",
130
+ "- keep diffs local; no unrelated refactors, renames, moves, reformatting, or dependency changes;",
131
+ "- inspect code before editing; do not invent APIs, files, commands, or behavior;",
132
+ "- before non-trivial edits, know the verification path;",
133
+ "- for bugs, prefer a failing test or repro first; then make the minimal fix; then verify;",
134
+ "- high-risk changes need a short spec before coding: goal, scope, behavior, risks, verification;",
135
+ "- high-risk includes security, privacy, auth/authz, data/schema/migrations, public APIs, external integrations, payments, jobs, concurrency, and irreversible or cross-cutting changes;",
136
+ "- follow nearby conventions; preserve existing behavior unless explicitly changing it;",
132
137
  "- handle edge cases, errors, cancellation, and async behavior;",
133
138
  "- avoid blocking UI/event loops;",
134
139
  "- avoid duplicate state, duplicate prompts, and repeated side effects.",
@@ -147,6 +152,9 @@ const FINAL_DISCIPLINE_LINES = [
147
152
  "",
148
153
  "When uncertain, test or inspect instead of assuming.",
149
154
  "If blocked by missing required information, ask exactly one concise question.",
155
+ "Verify every non-trivial change. Never claim tests passed unless they were actually run.",
156
+ "Report: what changed, what was verified, what was not verified, and any risks.",
157
+ "Ask at most one blocking question; otherwise proceed with grounded best effort.",
150
158
  ];
151
159
 
152
160
  const SILENCE_REMINDER_TEXT = [
@@ -175,7 +183,7 @@ const LOOKUP_SYSTEM_PROMPT = [
175
183
  "Return concise factual observations and practical implications for the parent agent.",
176
184
  ].join("\n");
177
185
 
178
- export default function glmCodingDiscipline(pi: ExtensionAPI) {
186
+ export default function codingDiscipline(pi: ExtensionAPI) {
179
187
  let selectedModelRef: string | undefined;
180
188
  let lookupRegistered = false;
181
189
  let silenceViolationCount = 0;
@@ -226,8 +234,9 @@ export default function glmCodingDiscipline(pi: ExtensionAPI) {
226
234
 
227
235
  pi.on("before_provider_request", async (event: { payload?: unknown }, ctx: unknown) => {
228
236
  const modelRef = modelRefFromPayload(event.payload) ?? selectedModelRef ?? modelRefFromContext(ctx);
229
- if (!isGlmModel(modelRef)) return undefined;
230
- return injectCodingDisciplineIntoPayload(event.payload, { lookupEnabled: Boolean(lookupModelFromConfig(contextCwd(ctx))) });
237
+ return injectCodingDisciplineIntoPayload(event.payload, {
238
+ lookupEnabled: isGlmModel(modelRef) && Boolean(lookupModelFromConfig(contextCwd(ctx))),
239
+ });
231
240
  });
232
241
 
233
242
  pi.on("context", async (event: { messages?: unknown[] }, ctx: unknown) => {
@@ -454,7 +463,7 @@ function createSilenceReminderMessage() {
454
463
  }
455
464
 
456
465
  function lookupModelFromConfig(cwd?: string): string | undefined {
457
- return loadPiToolsSuiteConfig(["glm-coding-discipline"], { cwd: cwd ?? process.cwd() }).lookupModel;
466
+ return loadPiToolsSuiteConfig(["coding-discipline"], { cwd: cwd ?? process.cwd() }).lookupModel;
458
467
  }
459
468
 
460
469
  function buildLookupPrompt(params: LookupParams, recentContext: string, imageCount: number, warnings: string[]): string {