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.
- package/dist/app/app.d.ts +0 -1
- package/dist/app/app.js +22 -21
- package/dist/app/input/input-controller.d.ts +1 -0
- package/dist/app/input/input-controller.js +40 -12
- package/dist/app/model/model-usage-status.js +4 -2
- package/dist/app/session/request-history.js +2 -0
- package/dist/app/session/session-event-controller.d.ts +13 -0
- package/dist/app/session/session-event-controller.js +27 -0
- package/dist/app/session/tabs-controller.d.ts +8 -0
- package/dist/app/session/tabs-controller.js +37 -6
- package/dist/app/workspace/workspace-actions-controller.d.ts +1 -0
- package/dist/app/workspace/workspace-actions-controller.js +2 -1
- package/dist/bundled-extensions/terminal-bell/index.js +1 -1
- package/dist/markdown-format.js +14 -25
- package/dist/terminal-width.d.ts +14 -0
- package/dist/terminal-width.js +31 -2
- package/dist/theme.js +2 -2
- package/external/pi-tools-suite/README.md +34 -9
- package/external/pi-tools-suite/package.json +3 -3
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +35 -21
- package/external/pi-tools-suite/src/async-subagents/commands.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +2 -2
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +70 -12
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/types.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/index.ts +6 -6
- package/external/pi-tools-suite/src/async-subagents/lib.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +4 -2
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +2 -2
- package/external/pi-tools-suite/src/{glm-coding-discipline → coding-discipline}/index.ts +17 -8
- package/external/pi-tools-suite/src/config.ts +1 -1
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +368 -0
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +3 -0
- package/external/pi-tools-suite/src/dcp/config.ts +23 -0
- package/external/pi-tools-suite/src/dcp/index.ts +112 -7
- package/external/pi-tools-suite/src/dcp/prompts.ts +8 -0
- package/external/pi-tools-suite/src/dcp/state.ts +41 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +30 -22
- package/external/pi-tools-suite/src/index.ts +2 -1
- package/external/pi-tools-suite/src/session-name/index.ts +37 -0
- package/external/pi-tools-suite/src/tool-descriptions.ts +16 -4
- 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/
|
|
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
|
|
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`:
|
|
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
|
|
76
|
+
"openai-codex/gpt-5*": {
|
|
75
77
|
"compress": {
|
|
76
|
-
"minContextPercent": "
|
|
77
|
-
"maxContextPercent": "
|
|
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`, `
|
|
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
|
-
|
|
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
|
|
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.
|
|
42
|
-
"@earendil-works/pi-coding-agent": "0.79.
|
|
43
|
-
"@earendil-works/pi-tui": "0.79.
|
|
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
|
|
32
|
-
// after switching models, so async-subagents treats these glob-like
|
|
33
|
-
// as text-only
|
|
34
|
-
//
|
|
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
|
|
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
|
-
"
|
|
216
|
-
"description": "Use
|
|
217
|
-
"model": "openai-codex/gpt-5.
|
|
218
|
-
"
|
|
219
|
-
"
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
"
|
|
223
|
-
"
|
|
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,
|
|
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,
|
|
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
|
-
|
|
205
|
-
description: "Use
|
|
206
|
-
model: "openai-codex/gpt-5.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"
|
|
210
|
-
"
|
|
211
|
-
"
|
|
212
|
-
"
|
|
213
|
-
|
|
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
|
-
:
|
|
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
|
|
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
|
-
"
|
|
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
|
|
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
|
|
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
|
-
|
|
364
|
-
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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: "
|
|
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,
|
|
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: "
|
|
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
|
-
"
|
|
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
|
-
"-
|
|
130
|
-
"-
|
|
131
|
-
"-
|
|
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
|
|
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
|
-
|
|
230
|
-
|
|
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(["
|
|
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 {
|