pi-ui-extend 1.0.40 → 1.0.42

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 (186) hide show
  1. package/README.md +10 -2
  2. package/dist/app/app.d.ts +3 -0
  3. package/dist/app/app.js +26 -0
  4. package/dist/app/commands/command-host.d.ts +1 -0
  5. package/dist/app/commands/command-model-actions.js +6 -1
  6. package/dist/app/commands/command-registry.js +3 -3
  7. package/dist/app/commands/command-session-actions.d.ts +0 -1
  8. package/dist/app/commands/command-session-actions.js +30 -15
  9. package/dist/app/commands/reload-context-inventory.d.ts +11 -0
  10. package/dist/app/commands/reload-context-inventory.js +59 -0
  11. package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
  12. package/dist/app/extensions/extension-actions-controller.js +6 -0
  13. package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
  14. package/dist/app/extensions/subagent-catalog-state.js +23 -0
  15. package/dist/app/icons.d.ts +14 -0
  16. package/dist/app/icons.js +33 -0
  17. package/dist/app/rendering/conversation-tool-renderer.js +2 -2
  18. package/dist/app/rendering/dcp-stats.d.ts +6 -1
  19. package/dist/app/rendering/dcp-stats.js +214 -46
  20. package/dist/app/rendering/editor-panels.js +17 -8
  21. package/dist/app/session/lazy-session-manager.js +12 -1
  22. package/dist/app/session/session-lifecycle-controller.d.ts +1 -0
  23. package/dist/app/session/session-lifecycle-controller.js +6 -0
  24. package/dist/app/session/tabs-controller.d.ts +3 -5
  25. package/dist/app/session/tabs-controller.js +31 -22
  26. package/dist/app/subagents/subagents-files.js +60 -1
  27. package/dist/app/subagents/subagents-model.d.ts +15 -1
  28. package/dist/app/subagents/subagents-model.js +52 -17
  29. package/dist/app/types.d.ts +7 -0
  30. package/dist/bundled-extensions/session-title/config.js +1 -1
  31. package/dist/default-pix-config.js +10 -0
  32. package/dist/markdown-format.js +27 -9
  33. package/dist/schemas/index.d.ts +1 -0
  34. package/dist/schemas/index.js +1 -0
  35. package/dist/schemas/pi-tools-suite-schema.d.ts +106 -71
  36. package/dist/schemas/pi-tools-suite-schema.js +68 -88
  37. package/dist/schemas/pix-schema.d.ts +7 -0
  38. package/dist/schemas/pix-schema.js +14 -0
  39. package/dist/schemas/tasks-schema.d.ts +18 -0
  40. package/dist/schemas/tasks-schema.js +41 -0
  41. package/docs/concurrency.md +6 -0
  42. package/docs/desktop-mvp.md +12 -1
  43. package/docs/desktop-task-manager.md +93 -82
  44. package/external/pi-tools-suite/README.md +192 -72
  45. package/external/pi-tools-suite/docs/browser-qa-subagent.md +50 -21
  46. package/external/pi-tools-suite/docs/context-gateway-p00-adr.md +216 -0
  47. package/external/pi-tools-suite/docs/context-gateway-p01n-gate-review.md +122 -0
  48. package/external/pi-tools-suite/docs/context-gateway-p01n-measurement.md +133 -0
  49. package/external/pi-tools-suite/docs/context-gateway-p01r-ra-evidence.md +111 -0
  50. package/external/pi-tools-suite/docs/context-gateway-p01r-rb-evidence.md +100 -0
  51. package/external/pi-tools-suite/docs/context-gateway-p01r-rc-evidence.md +69 -0
  52. package/external/pi-tools-suite/docs/context-gateway-p01r-rd-evidence.md +100 -0
  53. package/external/pi-tools-suite/docs/context-gateway-p01r-re-evidence.md +74 -0
  54. package/external/pi-tools-suite/docs/context-gateway-p01r-rf-evidence.md +153 -0
  55. package/external/pi-tools-suite/docs/context-gateway-p01r-rg-evidence.md +235 -0
  56. package/external/pi-tools-suite/docs/evals.md +1 -1
  57. package/external/pi-tools-suite/docs/session-recovery.md +40 -14
  58. package/external/pi-tools-suite/docs/subagent-model-pools.md +120 -0
  59. package/external/pi-tools-suite/package.json +3 -0
  60. package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/scripts/browser-qa-runner.mjs +82 -1
  61. package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/SKILL.md → agents/browser-qa.md} +261 -12
  62. package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
  63. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +20 -0
  64. package/external/pi-tools-suite/src/async-subagents/agents/oracle.md +16 -0
  65. package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
  66. package/external/pi-tools-suite/src/async-subagents/agents/research.md +20 -0
  67. package/external/pi-tools-suite/src/async-subagents/agents/verify.md +18 -0
  68. package/external/pi-tools-suite/src/async-subagents/commands.ts +20 -72
  69. package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
  70. package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +42 -0
  71. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +13 -93
  72. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +499 -0
  73. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +9 -0
  74. package/external/pi-tools-suite/src/async-subagents/core/config.ts +284 -417
  75. package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +1 -1
  76. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +54 -0
  77. package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +7 -6
  78. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +65 -45
  79. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +18 -6
  80. package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
  81. package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
  82. package/external/pi-tools-suite/src/async-subagents/index.ts +77 -3
  83. package/external/pi-tools-suite/src/async-subagents/lib.ts +12 -9
  84. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +54 -22
  85. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +6 -4
  86. package/external/pi-tools-suite/src/async-subagents/types.ts +3 -0
  87. package/external/pi-tools-suite/src/config.ts +42 -2
  88. package/external/pi-tools-suite/src/context-gateway/accounting.ts +151 -0
  89. package/external/pi-tools-suite/src/context-gateway/config.ts +120 -0
  90. package/external/pi-tools-suite/src/context-gateway/enforcement.ts +102 -0
  91. package/external/pi-tools-suite/src/context-gateway/index.ts +214 -0
  92. package/external/pi-tools-suite/src/context-gateway/metadata-normalization.ts +88 -0
  93. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +89 -0
  94. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +502 -0
  95. package/external/pi-tools-suite/src/context-gateway/test-output-parser.ts +326 -0
  96. package/external/pi-tools-suite/src/context-gateway/types.ts +158 -0
  97. package/external/pi-tools-suite/src/context-inventory.ts +99 -0
  98. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +106 -0
  99. package/external/pi-tools-suite/src/dcp/auto-compress.ts +892 -136
  100. package/external/pi-tools-suite/src/dcp/commands.ts +82 -139
  101. package/external/pi-tools-suite/src/dcp/compress-tool.ts +533 -74
  102. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +488 -94
  103. package/external/pi-tools-suite/src/dcp/compression-preview.ts +122 -0
  104. package/external/pi-tools-suite/src/dcp/compression-progress.ts +70 -0
  105. package/external/pi-tools-suite/src/dcp/config.ts +86 -68
  106. package/external/pi-tools-suite/src/dcp/conversation-index.ts +421 -0
  107. package/external/pi-tools-suite/src/dcp/debug-log.ts +7 -5
  108. package/external/pi-tools-suite/src/dcp/index.ts +617 -203
  109. package/external/pi-tools-suite/src/dcp/journal.ts +566 -0
  110. package/external/pi-tools-suite/src/dcp/progress-controller.ts +244 -0
  111. package/external/pi-tools-suite/src/dcp/prompts.ts +83 -189
  112. package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
  113. package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +189 -0
  114. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +299 -79
  115. package/external/pi-tools-suite/src/dcp/pruner-compression-blocks.ts +173 -281
  116. package/external/pi-tools-suite/src/dcp/pruner-emergency.ts +2 -4
  117. package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +23 -15
  118. package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +11 -1
  119. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +30 -82
  120. package/external/pi-tools-suite/src/dcp/pruner-tools.ts +28 -136
  121. package/external/pi-tools-suite/src/dcp/pruner.ts +18 -33
  122. package/external/pi-tools-suite/src/dcp/recovery.ts +129 -0
  123. package/external/pi-tools-suite/src/dcp/shadow-plan.ts +127 -0
  124. package/external/pi-tools-suite/src/dcp/state-transaction.ts +102 -0
  125. package/external/pi-tools-suite/src/dcp/state.ts +158 -580
  126. package/external/pi-tools-suite/src/dcp/ui.ts +1 -0
  127. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +36 -214
  128. package/external/pi-tools-suite/src/index.ts +16 -1
  129. package/external/pi-tools-suite/src/model-tools/index.ts +76 -42
  130. package/external/pi-tools-suite/src/repo-discovery/index.ts +84 -18
  131. package/external/pi-tools-suite/src/repo-discovery/native-compact.ts +458 -0
  132. package/external/pi-tools-suite/src/resource-registry/index.ts +2290 -0
  133. package/external/pi-tools-suite/src/session-recovery/index.ts +189 -43
  134. package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
  135. package/external/pi-tools-suite/src/tool-descriptions.ts +41 -38
  136. package/external/pi-tools-suite/src/truncation-metadata-normalizer/index.ts +17 -0
  137. package/package.json +6 -3
  138. package/schemas/pi-tools-suite.json +540 -312
  139. package/schemas/pix.json +24 -0
  140. package/schemas/tasks.json +132 -0
  141. package/skills/spec-wiki/SKILL.md +55 -0
  142. package/skills/spec-wiki/evals/evals.json +132 -0
  143. package/skills/spec-wiki/evals/files/bootstrap/architecture/rfc-007.md +14 -0
  144. package/skills/spec-wiki/evals/files/bootstrap/docs/how-to-pay.md +4 -0
  145. package/skills/spec-wiki/evals/files/bootstrap/docs/overview.md +9 -0
  146. package/skills/spec-wiki/evals/files/bootstrap/requirements/payment-behavior.md +17 -0
  147. package/skills/spec-wiki/evals/files/bootstrap/src/payments.ts +12 -0
  148. package/skills/spec-wiki/evals/files/bootstrap/tests/payments.test.ts +11 -0
  149. package/skills/spec-wiki/evals/files/focused/src/session.ts +5 -0
  150. package/skills/spec-wiki/evals/files/focused/tests/session.test.ts +17 -0
  151. package/skills/spec-wiki/evals/files/maintenance/low-signal/incoming/capture.txt +2 -0
  152. package/skills/spec-wiki/evals/files/maintenance/low-signal/src/capture.ts +3 -0
  153. package/skills/spec-wiki/evals/files/maintenance/move/docs/old-job-contract.md +18 -0
  154. package/skills/spec-wiki/evals/files/maintenance/move/src/job.ts +5 -0
  155. package/skills/spec-wiki/evals/files/maintenance/new-relation/docs/session-refresh.md +24 -0
  156. package/skills/spec-wiki/evals/files/maintenance/new-relation/incoming/refresh-worker.ts +7 -0
  157. package/skills/spec-wiki/evals/files/maintenance/new-relation/src/session.ts +7 -0
  158. package/skills/spec-wiki/evals/files/maintenance/no-impact/docs/auth-contract.md +17 -0
  159. package/skills/spec-wiki/evals/files/maintenance/no-impact/incoming/telemetry-format.ts +3 -0
  160. package/skills/spec-wiki/evals/files/maintenance/no-impact/src/auth.ts +3 -0
  161. package/skills/spec-wiki/evals/files/query/docs/how-to-run-jobs.md +4 -0
  162. package/skills/spec-wiki/evals/files/query/docs/spec-overview.md +9 -0
  163. package/skills/spec-wiki/evals/files/query/features/jobs/cancellation-contract.md +15 -0
  164. package/skills/spec-wiki/evals/files/query/features/jobs/retry-spec.md +6 -0
  165. package/skills/spec-wiki/evals/maintenance_regression.py +311 -0
  166. package/skills/spec-wiki/evals/retrieval-eval.md +37 -0
  167. package/skills/spec-wiki/evals/retrieval-evals.json +37 -0
  168. package/skills/spec-wiki/evals/trigger-evals.json +94 -0
  169. package/skills/spec-wiki/references/bootstrap.md +100 -0
  170. package/skills/spec-wiki/references/focused-spec.md +117 -0
  171. package/skills/spec-wiki/references/index-schema.md +330 -0
  172. package/skills/spec-wiki/references/maintenance.md +134 -0
  173. package/skills/spec-wiki/references/query.md +74 -0
  174. package/skills/spec-wiki/scripts/spec_wiki.py +2076 -0
  175. package/docs/desktop-markdown-media.md +0 -77
  176. package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
  177. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -270
  178. package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/auth-scaffold-spec.md +0 -78
  179. package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +0 -223
  180. package/external/pi-tools-suite/src/dcp/state-persistence.ts +0 -195
  181. package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
  182. package/skills/spec-lite/SKILL.md +0 -140
  183. /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-auth.example.jsonc +0 -0
  184. /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-flow.example.jsonc +0 -0
  185. /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.LICENSE +0 -0
  186. /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.mjs +0 -0
@@ -1,15 +1,25 @@
1
1
  ---
2
- name: browser-qa-private
3
- description: Private self-contained workflow for deterministic browser bug reproduction and fix verification with redacted project auth and Playwright evidence.
2
+ description: Use for browser-based visual QA - reproduce UI bugs and verify fixes with deterministic assertions, screenshots, video, and traces.
3
+ icon: globe
4
+ models: [zai/glm-5.3-flash, openai-codex/gpt-5.6-luna]
5
+ thinking: low
6
+ timeoutMs: 300000
7
+ tools: [read, grep, bash]
4
8
  ---
5
9
 
6
10
  # Browser QA
7
11
 
8
- Use this skill's bundled runner as the only browser interface. It already owns
12
+ Use this agent's bundled runner as the only browser interface. It already owns
9
13
  Playwright, browser/context lifecycle, tracing, video, screenshots, origin
10
14
  isolation, authentication, redaction, and cleanup. Do not invoke another browser
11
15
  CLI, create shared/default browser sessions, or generate executable browser code.
12
16
 
17
+ The launcher sets `PI_BROWSER_QA_RUNNER` to the absolute path of the installed
18
+ runner. Invoke it as `node "$PI_BROWSER_QA_RUNNER"`; do not guess a path from
19
+ the project cwd, override this variable, or copy/reimplement the runner.
20
+ The complete workflow and scenario-design guidance are included below. No
21
+ additional skill or instruction-file discovery is needed.
22
+
13
23
  Never read, print, grep, copy, or edit credential values from
14
24
  `.pi/qa_auth.jsonc` yourself.
15
25
 
@@ -37,7 +47,8 @@ inside the preflight, return a structured `BLOCKED` result immediately. Do not
37
47
  consume the launcher budget on open-ended source reading, server polling,
38
48
  capability probing, or retries.
39
49
 
40
- 1. Resolve `scripts/browser-qa-runner.mjs` relative to this skill.
50
+ 1. Use the launcher-provided `PI_BROWSER_QA_RUNNER` path. If it is missing,
51
+ report a launcher configuration blocker rather than selecting another runner.
41
52
  2. Use the launcher-provided `$PI_SUBAGENT_AGENT_DIR/browser-qa/` workspace.
42
53
  The launcher creates its private `flows/` directory and the runner rejects
43
54
  flows or evidence destinations outside this owning sub-agent directory. Do
@@ -48,7 +59,7 @@ capability probing, or retries.
48
59
  be reached or started, report the concrete blocker instead of switching to a
49
60
  mock target or substituting static checks for browser QA.
50
61
  4. If the requested behavior requires authentication, run
51
- `node <runner> profiles`. A missing auth config is valid and returns an empty
62
+ `node "$PI_BROWSER_QA_RUNNER" profiles`. A missing auth config returns an empty
52
63
  list without creating `.pi/qa_auth.jsonc`. Otherwise skip profile discovery
53
64
  and use public mode. Choose an auth profile only when the task names its id,
54
65
  safe profile traits make the choice unambiguous, or a public run proves that
@@ -61,7 +72,8 @@ capability probing, or retries.
61
72
  executable JavaScript in it. The `evaluate` action exposes only the safe
62
73
  operations documented below; it does not accept expressions or scripts.
63
74
  6. Run public QA with
64
- `node <runner> run --base-url <url> --flow <flow.jsonc>`. The URL's exact
75
+ `node "$PI_BROWSER_QA_RUNNER" run --base-url <url> --flow <flow.jsonc>`.
76
+ The URL's exact
65
77
  origin becomes the fail-closed allowlist. When the real app requires known
66
78
  API/CDN origins, add repeatable `--allow-origin <exact-origin>` flags. Each
67
79
  value must be an exact `http(s)` origin with no path, credentials, wildcard,
@@ -128,8 +140,8 @@ capability probing, or retries.
128
140
  product behavior differs from the expectation, preserve the failure evidence
129
141
  and report the mismatch.
130
142
 
131
- Read `references/qa-design.md` when designing a non-trivial flow, diagnosing an
132
- ambiguous failure, or deciding what evidence proves the result.
143
+ Use the detailed scenario-design guidance below for non-trivial flows,
144
+ ambiguous failures, and decisions about what evidence proves the result.
133
145
 
134
146
  ## Flow contract
135
147
 
@@ -293,7 +305,7 @@ When a public HTTPS login page (or loopback HTTP page for local development) is
293
305
  known and there is no usable profile, invoke the trusted runner once:
294
306
 
295
307
  ```sh
296
- node <runner> auth scaffold \
308
+ node "$PI_BROWSER_QA_RUNNER" auth scaffold \
297
309
  --profile <safe-id> \
298
310
  --login-url <public-login-url>
299
311
  ```
@@ -322,7 +334,7 @@ Do not request credentials merely because `.pi/qa_auth.jsonc` is absent. If the
322
334
  task explicitly requires authenticated behavior, or a public run reaches the
323
335
  flow's `authRejectedIf` check, and `profiles` returned no usable profile, run
324
336
  form-auth scaffolding when supported. Otherwise run
325
- `node <runner> profiles --require-auth`. Only these explicit authenticated paths
337
+ `node "$PI_BROWSER_QA_RUNNER" profiles --require-auth`. Only these paths
326
338
  may create the private auth file.
327
339
 
328
340
  If that command or an authenticated run returns `QA_AUTH_UPDATE_REQUIRED`, stop
@@ -345,5 +357,242 @@ These links are mandatory so the user can open the evidence directly.
345
357
  Also include `visualInspection` with `inspected` or `unavailable`; never infer a
346
358
  visual pass from a successful runner status alone.
347
359
 
348
- See `references/qa-auth.example.jsonc`, `references/qa-flow.example.jsonc`,
349
- `references/qa-design.md`, and `references/auth-scaffold-spec.md`.
360
+ ### Scaffold safety and edge cases
361
+
362
+ The trusted runner discovers the most likely form, fillable fields, submit
363
+ control, and form container on the unauthenticated page. It writes only
364
+ discovered selectors and generated placeholders to the private `0600` config.
365
+ The agent treats that config as opaque and user-owned. Scaffold status exposes
366
+ only the file path, profile id, counts, and action, never inspected DOM text,
367
+ input values, URLs, or selectors.
368
+
369
+ Scaffolding rejects pages without a discoverable fillable field or submit
370
+ control, form-less pages without an explicit success condition, remote HTTP
371
+ login pages, cross-origin base URLs, malformed success options, symlinked or
372
+ permissive auth paths, and invalid or non-empty existing configuration. Report
373
+ these blockers; do not bypass them. No generated profile is usable until the
374
+ user replaces its secret placeholders.
375
+
376
+ ## Detailed scenario-design guidance
377
+
378
+ Use the bundled declarative runner throughout. This workflow does not require
379
+ a separate browser CLI or executable Playwright scripts.
380
+
381
+ ### Build the proof before the steps
382
+
383
+ Write down three things first:
384
+
385
+ 1. **Setup:** the page and state needed to expose the behavior.
386
+ 2. **Action:** the smallest user interaction that exercises it.
387
+ 3. **Oracle:** the observable state that proves success or reproduces failure.
388
+
389
+ Good oracles are product-visible and specific: an exact URL, a stable status
390
+ message, a field value, item count, enabled/disabled state, or checked state.
391
+ Avoid treating “the click did not throw” or “the screenshot looks plausible” as
392
+ proof.
393
+
394
+ When verifying a fix, prefer a focused regression flow over a broad tour of the
395
+ application. If multiple independent states matter, assert each one explicitly.
396
+
397
+ For explicit exploratory/manual QA, keep exploration bounded rather than turning
398
+ it into an open-ended crawl. Run at most three minimal rounds. Each round should
399
+ start from one concrete hypothesis, produce a deterministic observation plus a
400
+ meaningful screenshot, and use that evidence to decide whether another round is
401
+ justified. Verification tasks remain one browser run per profile.
402
+
403
+ ### Choose resilient locators
404
+
405
+ Prefer locators that match how users and accessibility APIs identify controls:
406
+
407
+ 1. `testId` when the product exposes a stable test contract.
408
+ 2. `role` with accessible `name` for buttons, links, headings, dialogs, and
409
+ similar semantic elements.
410
+ 3. `label` for form controls.
411
+ 4. `placeholder` or visible `text` when they are stable product copy.
412
+ 5. `css` only when no semantic contract exists.
413
+
414
+ Use `exact: true` when duplicate or substring matches are possible. Avoid CSS
415
+ that encodes DOM depth, generated classes, styling details, or element order.
416
+ If a locator is ambiguous, inspect nearby source or rendered copy and choose a
417
+ more specific product contract rather than adding arbitrary delays.
418
+
419
+ ### Wait for state, not time
420
+
421
+ Runner interactions inherit Playwright auto-waiting. Usually an action followed
422
+ by an assertion is enough. Use `waitFor` only when the next operation depends on
423
+ a distinct attached/detached/visible/hidden transition.
424
+
425
+ After navigation and visible interactions, the runner also waits for DOM
426
+ readiness, tracks requests causally started by that action through a bounded
427
+ readiness window, waits for common visible
428
+ `aria-busy`/progress/loading/spinner/skeleton markers, and keeps a 500 ms stable
429
+ interval. EventSource/WebSocket traffic is ignored, and a long poll is not
430
+ allowed to pin the entire flow timeout. A visible busy indicator may still hold
431
+ readiness until `timeoutMs`. This is a safe baseline, not an application-specific
432
+ oracle: explicitly wait for a custom loader to become hidden and assert the
433
+ loaded content when the application uses different readiness semantics.
434
+
435
+ `waitForTimeout` is bounded to five seconds and should be exceptional—for a
436
+ known animation, debounce, or externally scheduled transition with no
437
+ observable intermediate state. Sleeping longer hides races instead of proving
438
+ behavior. If a normal operation legitimately needs more time, adjust the flow's
439
+ `timeoutMs` rather than inserting repeated sleeps.
440
+
441
+ All assertion actions retry until that timeout. This makes an action followed
442
+ directly by `assertText`, `assertVisible`, `assertURL`, or another assertion
443
+ safe for asynchronously rendered outcomes. `assertText` requires the locator to
444
+ be visible and matches rendered `innerText`; use `assertTextContent` only when
445
+ hidden/raw DOM text is deliberately part of the oracle. Use `assertAttribute` for
446
+ observable state such as `aria-expanded`, `aria-invalid`, or `data-state`
447
+ instead of reading DOM state through executable JavaScript.
448
+
449
+ ### Responsive and scrolling scenarios
450
+
451
+ Set the flow's top-level `viewport` whenever the bug depends on a breakpoint or
452
+ available height. Assert `viewportWidth` or `viewportHeight` with
453
+ `assertDOMMetric` when the dimensions themselves are part of the proof; the
454
+ runner also includes the applied viewport in its result.
455
+
456
+ Use `wheel` to reproduce real pointer-wheel input. Add a locator when the wheel
457
+ must target a nested scrolling container—the runner hovers it before sending
458
+ the input. Because browser scrolling may be scheduled after the wheel event,
459
+ wait only for a short known settling interval when a direct metric assertion is
460
+ otherwise racy.
461
+
462
+ Use safe `evaluate` `scrollTo`/`scrollBy` operations for deterministic setup or
463
+ to distinguish input handling from layout behavior. Use the `metrics` operation
464
+ to retain a named page/element snapshot in result `observations`, and use
465
+ `assertDOMMetric` for pass/fail. Raw JavaScript expressions are intentionally
466
+ excluded: flows remain declarative and cannot inspect authentication storage or
467
+ execute arbitrary same-origin requests.
468
+
469
+ ### Deterministic browser environment
470
+
471
+ Set top-level `environment` when locale, timezone, color scheme, or motion
472
+ preferences can change the behavior. The runner otherwise uses stable defaults
473
+ (`en-US`, `UTC`, `light`, `reduce`) instead of inheriting host settings. Prefer
474
+ asserting product-visible copy or state derived from those settings; do not use
475
+ screenshot pixels as the only oracle.
476
+
477
+ ### Causal network and dialog expectations
478
+
479
+ Put `expectResponse` or `expectDialog` on the interaction that causes the event.
480
+ The runner arms both listeners before the interaction, avoiding the race in a
481
+ separate “click, then wait” sequence. Response expectations deliberately match
482
+ only an exact allowlisted-origin pathname, HTTP method, and status. This proves
483
+ that a matching request started and received a response within the action
484
+ window without retaining its URL query, headers, or body.
485
+
486
+ Dialog expectations match a fixed dialog type and exact/included message, then
487
+ accept or dismiss it declaratively. A mismatch is dismissed before the step
488
+ fails so the page cannot freeze. Actual event metadata is never included in
489
+ failure diagnostics. Do not place secrets in expected messages or response
490
+ paths even though the runner keeps diagnostics generic.
491
+
492
+ ### Drag, upload, and download scenarios
493
+
494
+ Use `dragTo` for native DOM drag/drop and assert the resulting product state.
495
+ Optional source/drop positions are relative bounded coordinates. Canvas-only,
496
+ OS-native, or custom synthetic-event drag protocols remain unsupported; do not
497
+ work around that with executable JavaScript.
498
+
499
+ Uploads are memory-only base64 payloads declared in the flow. This intentionally
500
+ prevents a flow from selecting arbitrary project files, credential config, or
501
+ directories. Keep fixtures minimal and non-secret. An empty file list clears a
502
+ file input.
503
+
504
+ Use the atomic `download` action rather than clicking a download link directly.
505
+ Always match the suggested filename and choose a tight `maxBytes`. Retain a
506
+ download only when its contents are needed as evidence; otherwise the runner
507
+ deletes it after validation. Retained downloads use generated private names,
508
+ not server-provided paths, and are scanned for configured authentication before
509
+ publication. `maxBytes` bounds the runner's private evidence copy and triggers
510
+ cancellation while it grows, but it is not a network-bandwidth guarantee: the
511
+ browser can receive temporary bytes before cancellation.
512
+
513
+ ### Same-origin frames and popups
514
+
515
+ Use a scoped `target` for iframe or named popup interactions. The runner checks
516
+ the live frame origin before every scoped step and checks a popup after it loads;
517
+ both must remain in `allowedOrigins`. This supports embedded application UI and
518
+ same-origin auxiliary windows without opening a route around the network guard.
519
+ Cross-origin login, payment, and third-party widgets remain intentionally out of
520
+ scope. Each popup adds a separate private video artifact, so open only the
521
+ windows needed for the proof.
522
+
523
+ ### Authentication transitions
524
+
525
+ Add `authRejectedIf` directly after initial navigation and after transitions
526
+ that can redirect to login or display an expired-session marker. This converts
527
+ stale credentials into an explicit update request instead of misreporting a
528
+ product regression.
529
+
530
+ Do not encode credentials, tokens, storage values, or login form secrets in the
531
+ flow. The trusted runner applies the selected profile internally. For form auth,
532
+ video starts on the login page and includes field filling and submission; password
533
+ inputs remain masked, but visible identifiers can appear, so treat the video as
534
+ sensitive private evidence. Tracing starts only after login succeeds and is
535
+ sanitized before retention.
536
+
537
+ ### Evidence strategy
538
+
539
+ The runner always attempts a final or failure screenshot, records video from the
540
+ first page, and creates a sanitized post-auth trace. Add named `screenshot`
541
+ steps only at states that materially help explain the result—for example before
542
+ and after a destructive interaction, or when a transient success message is
543
+ the oracle.
544
+
545
+ Use evidence by purpose:
546
+
547
+ - **Screenshot:** quick review of one meaningful visual state.
548
+ - **Video:** chronological confirmation of the complete user flow.
549
+ - **Trace:** action/DOM timing diagnosis for a failed or flaky interaction.
550
+
551
+ Videos automatically show a transient cursor and yellow pulse for clicks and
552
+ double-clicks. After a native `dragTo` gesture completes, its resolved
553
+ source-to-target route is replayed over 450 ms with a large orange cursor and
554
+ progressively drawn high-contrast trail, followed by a green drop marker. These
555
+ annotations are
556
+ runner-owned, pointer-transparent, and accessibility-hidden; they cover the main
557
+ page, same-origin frames, declared popups, and form-auth submission. Their
558
+ bounded animations finish within the normal post-action stable interval, so
559
+ they explain the chronology without becoming screenshot or assertion oracles.
560
+
561
+ Assertions determine pass/fail; evidence explains it. Preserve and link every
562
+ artifact group returned on both passed and failed runs.
563
+
564
+ For visual QA, do not stop at artifact generation. Open at least one meaningful
565
+ PNG with the model's image-capable `read` path and inspect layout, clipping,
566
+ overlap, state styling, and other visual defects relevant to the scenario. If
567
+ the active model cannot read images, explicitly report visual inspection as
568
+ unavailable rather than treating deterministic assertions as a visual pass.
569
+
570
+ ### Diagnose failures without weakening the test
571
+
572
+ Classify the first failing step:
573
+
574
+ - wrong target/setup or service unavailable;
575
+ - authentication rejected or expired;
576
+ - locator no longer matches the product contract;
577
+ - expected state never appeared;
578
+ - actual product behavior contradicts the expectation.
579
+
580
+ Fix the flow only when its setup or locator is wrong. Do not replace a precise
581
+ assertion with a vague one, increase timeouts reflexively, or remove the failing
582
+ step to manufacture a pass. Keep the failure artifacts and state the expected
583
+ versus observed behavior.
584
+
585
+ ### Cleanup and isolation
586
+
587
+ Each runner invocation owns one isolated context and evidence directory and
588
+ closes its browser resources in a `finally` path. Do not create parallel shared
589
+ or default sessions outside the runner. Test multiple auth profiles with
590
+ separate invocations so cookies, storage, traces, and evidence cannot mix.
591
+
592
+ Keep the declarative flow and every generated screenshot, video, trace, and
593
+ result manifest inside `$PI_SUBAGENT_AGENT_DIR/browser-qa/`. The launcher owns
594
+ that path and the runner validates it before opening a browser. Do not override
595
+ the environment path or copy evidence into shared `.pi/qa-runs`/`.pi/qa-flows`
596
+ directories: the agent-local workspace is intentionally removed by the normal
597
+ sub-agent shutdown and cleanup lifecycle. Authentication config remains a
598
+ separate persistent input under project `.pi/`.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: Independent frontier-model code review gate for substantive code changes made by a non-frontier parent. Use after implementation and targeted checks, before finalizing. Read-only review of the actual diff and surrounding code; use research for ordinary evidence gathering and oracle for architecture or high-stakes second opinions.
3
+ icon: eye
4
+ models: [openai-codex/gpt-5.6-sol, zai/glm-5.3]
5
+ notForParentModels: [openai-codex/gpt-5.6-sol*, zai/glm-5.3]
6
+ thinking: high
7
+ tools: [read, grep, bash]
8
+ ---
9
+
10
+ # Frontier code review
11
+
12
+ Review the implementation independently. Inspect the actual diff and the
13
+ surrounding contracts; do not accept the implementer's summary as evidence.
14
+
15
+ Look for correctness bugs, regressions, broken invariants, unsafe edge cases,
16
+ missing error handling, concurrency/state mistakes, security issues, and tests
17
+ that fail to cover a material behavior change. Prefer a small number of
18
+ actionable findings over stylistic commentary or speculative rewrites.
19
+
20
+ Do not edit source or tests. Run narrow read-only checks when they materially
21
+ increase confidence. For each finding, give severity, rationale, and precise
22
+ file/line evidence. If there are no findings, say so explicitly and name any
23
+ important residual risk or verification gap.
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: Make bounded changes to code, docs, tests, or UI using the parent's requirements and acceptance criteria. Follow local conventions and verify the change. Task-specific discipline belongs in the brief, not a separate role.
3
+ icon: code
4
+ models: [zai/glm-5.3-flash, openai-codex/gpt-5.6-terra, openai-codex/gpt-5.6-luna]
5
+ thinking: medium
6
+ ---
7
+
8
+ # Implement
9
+
10
+ Execute the bounded change, including documentation, tests, or frontend work
11
+ when requested. Inspect nearby conventions, preserve unrelated work, and do
12
+ not broaden scope or make product/architecture decisions for the parent.
13
+
14
+ For UI work, preserve the existing design language and inspect supplied visual
15
+ references with an image-capable model. Report unavailable capabilities instead
16
+ of claiming visual verification. Actual browser QA belongs to browser-qa.
17
+
18
+ Run relevant targeted checks and report changed paths plus their results. If
19
+ requirements conflict or the task exceeds your capabilities, stop with a
20
+ concrete blocker and the evidence already gathered; do not re-plan the project.
@@ -0,0 +1,16 @@
1
+ ---
2
+ description: Strong independent second opinion for hard or high-stakes uncertainty. Use sparingly to challenge architecture, plans, root-cause hypotheses, or risk decisions. Prefer another provider within the available pool; read-only advice, not routine execution.
3
+ icon: sparkles
4
+ models: [openai-codex/gpt-5.6-sol, zai/glm-5.3]
5
+ thinking: max
6
+ tools: [read, grep, bash]
7
+ ---
8
+
9
+ # Oracle agent
10
+
11
+ You are an oracle: a strong model giving an independent second opinion to the
12
+ parent agent. A different provider is preferred within the available pool, but
13
+ not guaranteed; do not claim cross-provider independence without checking.
14
+ Give a concise, decisive recommendation with key
15
+ tradeoffs and risks. Disagree when warranted; do not rubber-stamp. Do not edit
16
+ unless explicitly asked.
@@ -0,0 +1,16 @@
1
+ {
2
+ // Presets are available-model pools. Agent candidate order still comes from
3
+ // each agent's Markdown frontmatter; pool order never reorders candidates.
4
+ "cheap": {
5
+ "description": "GLM pool: economical workers plus GLM-5.3 for frontier-review/oracle.",
6
+ "models": ["zai/glm-5-turbo", "zai/glm-5.3-flash", "zai/glm-5.3"]
7
+ },
8
+ "gpt": {
9
+ "description": "GPT pool: Luna/Terra workers; Sol is available to frontier-review/oracle.",
10
+ "models": ["openai-codex/gpt-5.6-luna", "openai-codex/gpt-5.6-terra", "openai-codex/gpt-5.6-sol"]
11
+ },
12
+ "deep": {
13
+ "description": "Mixed pool; workers retain economical candidate order and frontier-review/oracle use strong models.",
14
+ "models": ["zai/glm-5-turbo", "zai/glm-5.3-flash", "openai-codex/gpt-5.6-luna", "openai-codex/gpt-5.6-terra", "openai-codex/gpt-5.6-sol", "zai/glm-5.3"]
15
+ }
16
+ }
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: Read-only evidence gathering - search files, trace behavior, investigate hypotheses, or analyze a focused review question. Return findings with paths, not raw source. Use frontier-review for the post-implementation code-review gate when available, verify for running checks, and implement for changes.
3
+ icon: search
4
+ models: [zai/glm-5-turbo, openai-codex/gpt-5.6-luna]
5
+ thinking: low
6
+ tools: [read, grep]
7
+ ---
8
+
9
+ # Research
10
+
11
+ Investigate the assigned question within scope. Read and compare evidence; do
12
+ not edit files or invent missing facts. When assigned a review question, make
13
+ it a fresh investigation of the relevant code, not approval based on another
14
+ agent's summary. The broad post-implementation review gate belongs to
15
+ frontier-review when that role is available.
16
+
17
+ Return the answer and supporting file:line references, distinguishing confirmed
18
+ findings from hypotheses. Report gaps or a concrete blocker when the available
19
+ evidence is insufficient. Leave architecture decisions and escalation to the
20
+ parent. Do not dump search results or source files unless explicitly requested.
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: Run targeted tests, builds, or checks and diagnose their output in an isolated context. Return pass/fail, relevant failure evidence and log paths. Do not fix source code; creating or changing tests belongs to implement.
3
+ icon: flask
4
+ models: [zai/glm-5-turbo, openai-codex/gpt-5.6-luna]
5
+ thinking: low
6
+ tools: [read, grep, bash]
7
+ ---
8
+
9
+ # Verify
10
+
11
+ Select and run the smallest checks that establish the requested acceptance
12
+ criteria. Do not edit source or tests, weaken assertions, install dependencies,
13
+ or update snapshots to manufacture a pass. Report missing prerequisites.
14
+
15
+ Keep noisy output in artifacts and return the command, exit status, relevant
16
+ failure excerpt, and log path. Separate environment failures from product
17
+ failures and mark unrun checks explicitly. This is a behavioral no-edit
18
+ contract: the shell is not a read-only filesystem sandbox.
@@ -3,15 +3,12 @@ import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import { ignoreStaleExtensionContextError } from "../context-usage.js";
5
5
  import {
6
- copySubagentConfigSample,
7
6
  ensureSessionFileLink,
8
- existingSubagentConfigFiles,
9
7
  findSubagentSessionByFile,
10
8
  getActiveSubagentPresetName,
11
- getDefaultSubagentConfigPath,
9
+ getBuiltinSubagentPresetsPath,
10
+ getProjectSubagentPresetsPath,
12
11
  getSessionSubagentPresetOverride,
13
- getSubagentConfigInitTargetPath,
14
- getSubagentConfigSamplePath,
15
12
  getSubagentPresetSelectionPath,
16
13
  getRunState,
17
14
  listRunDirs,
@@ -49,17 +46,16 @@ type MessageSender = ExtensionAPI & {
49
46
  };
50
47
 
51
48
  const CLEAR_ACTIVE_PRESET_LABEL = "Use no active preset";
52
- const COPY_SAMPLE_CONFIG_LABEL = "Copy sample asyncSubagents config";
53
49
 
54
50
  export const ULTRAWORK_PROMPT = `Run ultrawork mode for the current objective.
55
51
 
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.
52
+ Use subagents when a lower-cost worker or isolation of noisy evidence helps, including one bounded sequential task. Pick subagentType from the effective catalog: research for reading/review, implement for code/docs/tests/UI changes, verify for running checks, browser-qa for real-browser testing, and oracle only for a deliberate strong independent opinion. Prefer an appropriate project-local specialist. Keep decisions and integration in the parent; do not create agents just to assign a discipline.
57
53
 
58
54
  Keep parent context lean: spawn for broad parallel work, read results only when needed, and finish unless genuinely blocked.`;
59
55
 
60
56
  const HYPERPLAN_PROMPT = `Run hyperplan mode for the current objective.
61
57
 
62
- Before implementation, use subagents to pressure-test the plan with configured roles such as deep, implement, frontend, tests, review, and docs. Synthesize the strongest objections into a revised plan before editing.`;
58
+ Before implementation, use bounded research tasks to gather constraints and independently pressure-test the plan. Use oracle only when a strong second opinion is justified. Synthesize the evidence and objections in the parent before editing.`;
63
59
 
64
60
  export function buildUltraworkPrompt(objective: string): string {
65
61
  const trimmed = objective.trim();
@@ -192,12 +188,10 @@ async function triggerOrchestrationPrompt(
192
188
 
193
189
  function registerPresetCommands(pi: ExtensionAPI): void {
194
190
  pi.registerCommand("subagent-preset", {
195
- description: "Select a sub-agent preset defined in asyncSubagents config, run session <name> for a process override, or init to copy the sample config",
191
+ description: "Select a sub-agent model-pool preset, or run session <name> for a process-only override",
196
192
  getArgumentCompletions: (prefix: string) => {
197
- const names = existingSubagentConfigFiles(process.cwd()).length > 0
198
- ? sortedPresetNames(loadSubagentConfig(process.cwd()).presets ?? {}, getActiveSubagentPresetName())
199
- : [];
200
- return [...names, ...names.map((name) => `session ${name}`), "list", "path", "config", "init", "clear", "session", "session-clear"]
193
+ const names = sortedPresetNames(loadSubagentConfig(process.cwd()).presets ?? {}, getActiveSubagentPresetName());
194
+ return [...names, ...names.map((name) => `session ${name}`), "list", "path", "clear", "session", "session-clear"]
201
195
  .filter((name) => name.startsWith(prefix))
202
196
  .map((name) => ({ value: name, label: name }));
203
197
  },
@@ -209,28 +203,19 @@ function registerPresetCommands(pi: ExtensionAPI): void {
209
203
  if (name === "session-clear") return clearSessionActiveSubagentPreset(ctx);
210
204
  if (name === "list") return listSubagentPresets(ctx);
211
205
  if (name === "path") return showSubagentPresetPaths(ctx);
212
- if (name === "config") return showSubagentPresetConfigurator(ctx);
213
- if (name === "init") return initSubagentConfigSample(ctx);
214
206
  if (name === "clear") return clearActiveSubagentPreset(ctx);
215
- if (existingSubagentConfigFiles(ctx.cwd).length === 0) return ctx.ui.notify(missingSubagentConfigMessage(ctx), "warning");
216
207
 
217
208
  const config = loadSubagentConfig(ctx.cwd);
218
209
  const preset = config.presets?.[name];
219
- if (!preset) return ctx.ui.notify(`Unknown sub-agent preset "${name}". Define it in asyncSubagents config or run /subagent-preset list.`, "error");
210
+ if (!preset) return ctx.ui.notify(`Unknown sub-agent preset "${name}". Run /subagent-preset list to see available pools.`, "error");
220
211
  setActiveSubagentPreset(name);
221
212
  notifyActiveSubagentPreset(ctx, name, preset);
222
213
  },
223
214
  });
224
-
225
- pi.registerCommand("subagent-preset-config", {
226
- description: "Select sub-agent presets defined in asyncSubagents config",
227
- handler: async (_args: string, ctx: CommandContext) => showSubagentPresetConfigurator(ctx),
228
- });
229
215
  }
230
216
 
231
217
  async function showSubagentPresetSelector(ctx: CommandContext): Promise<void> {
232
218
  if (!ctx.hasUI) return ctx.ui.notify("Sub-agent preset selector requires interactive UI. Use /subagent-preset <name>.", "warning");
233
- if (existingSubagentConfigFiles(ctx.cwd).length === 0) return showMissingSubagentConfigSelector(ctx);
234
219
 
235
220
  const config = loadSubagentConfig(ctx.cwd);
236
221
  const activePreset = getActiveSubagentPresetName();
@@ -240,12 +225,12 @@ async function showSubagentPresetSelector(ctx: CommandContext): Promise<void> {
240
225
  const labels = [...presetLabels];
241
226
  if (activePreset) labels.push(CLEAR_ACTIVE_PRESET_LABEL);
242
227
  if (labels.length === 0) {
243
- ctx.ui.notify("No sub-agent presets are defined in asyncSubagents config.", "warning");
228
+ ctx.ui.notify("No sub-agent model-pool presets are available.", "warning");
244
229
  showSubagentPresetPaths(ctx);
245
230
  return;
246
231
  }
247
232
  const labelToName = new Map(presetLabels.map((label, index) => [label, names[index]]));
248
- const selected = await ctx.ui.select(names.length > 0 ? "Select active sub-agent preset" : "No sub-agent presets in asyncSubagents config", labels);
233
+ const selected = await ctx.ui.select("Select active sub-agent preset", labels);
249
234
  if (!selected) return;
250
235
  if (selected === CLEAR_ACTIVE_PRESET_LABEL) return clearActiveSubagentPreset(ctx);
251
236
 
@@ -254,69 +239,29 @@ async function showSubagentPresetSelector(ctx: CommandContext): Promise<void> {
254
239
  setActiveSubagentPreset(name);
255
240
  notifyActiveSubagentPreset(ctx, name, presets[name]);
256
241
  }
257
-
258
- async function showSubagentPresetConfigurator(ctx: CommandContext): Promise<void> {
259
- if (!ctx.hasUI) return ctx.ui.notify("Sub-agent preset selector requires interactive UI.", "warning");
260
- return showSubagentPresetSelector(ctx);
261
- }
262
-
263
- async function showMissingSubagentConfigSelector(ctx: CommandContext): Promise<void> {
264
- const selected = await ctx.ui.select("No asyncSubagents config found", [COPY_SAMPLE_CONFIG_LABEL]);
265
- if (selected === COPY_SAMPLE_CONFIG_LABEL) initSubagentConfigSample(ctx);
266
- }
267
-
268
242
  function listSubagentPresets(ctx: CommandContext): void {
269
- if (existingSubagentConfigFiles(ctx.cwd).length === 0) {
270
- ctx.ui.notify(missingSubagentConfigMessage(ctx), "warning");
271
- return;
272
- }
273
243
  const config = loadSubagentConfig(ctx.cwd);
274
244
  const activePreset = getActiveSubagentPresetName();
275
245
  const presets = config.presets ?? {};
276
246
  const names = sortedPresetNames(presets, activePreset);
277
- if (names.length === 0) return ctx.ui.notify("No sub-agent presets are defined in asyncSubagents config.", "warning");
247
+ if (names.length === 0) return ctx.ui.notify("No sub-agent model-pool presets are available.", "warning");
278
248
  ctx.ui.notify([
279
- "Sub-agent presets from asyncSubagents config:",
249
+ "Sub-agent model-pool presets:",
280
250
  ...names.map((name) => `- ${subagentPresetLabel(name, presets[name], activePreset)}`),
281
251
  ].join("\n"), "info");
282
252
  }
283
253
 
284
254
  function showSubagentPresetPaths(ctx: CommandContext): void {
285
255
  ctx.ui.notify([
286
- "Preset definitions: asyncSubagents in ~/.config/pi/pi-tools-suite.jsonc, $PI_CONFIG_DIR/pi-tools-suite.jsonc, or project .pi/pi-tools-suite.jsonc",
287
- "Explicit override files are still supported via ASYNC_SUBAGENTS_CONFIG / PI_SUBAGENTS_CONFIG.",
288
- `Default config target: ${getDefaultSubagentConfigPath()}`,
289
- `Sample config: ${getSubagentConfigSamplePath()}`,
290
- `Copy sample target: ${getSubagentConfigInitTargetPath(ctx.cwd)}`,
256
+ `Bundled presets: ${getBuiltinSubagentPresetsPath()}`,
257
+ `Project preset override: ${getProjectSubagentPresetsPath(ctx.cwd)}`,
258
+ "Project presets are defined as model pools in .pi/agents/presets.jsonc; agent behavior stays in .pi/agents/*.md.",
291
259
  `Active selection state: ${getSubagentPresetSelectionPath()}`,
292
260
  `Session override: ${formatSessionPresetOverride()}`,
293
261
  "Use /subagent-preset session <name> to override only the current Pi process; use /subagent-preset session-clear to clear that runtime override.",
294
- "Run /subagent-preset init to copy the bundled sample only when no config exists.",
295
262
  ].join("\n"), "info");
296
263
  }
297
264
 
298
- function initSubagentConfigSample(ctx: CommandContext): void {
299
- try {
300
- const result = copySubagentConfigSample(ctx.cwd);
301
- if (result.copied) {
302
- ctx.ui.notify(`Copied sample asyncSubagents config:\n${result.samplePath}\n→ ${result.targetPath}`, "info");
303
- return;
304
- }
305
- ctx.ui.notify(`AsyncSubagents config already exists; not overwriting:\n${result.existingFiles.join("\n") || result.targetPath}`, "warning");
306
- } catch (error) {
307
- ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
308
- }
309
- }
310
-
311
- function missingSubagentConfigMessage(ctx: CommandContext): string {
312
- return [
313
- "No asyncSubagents config found in pi-tools-suite config.",
314
- `Sample config: ${getSubagentConfigSamplePath()}`,
315
- `Copy target: ${getSubagentConfigInitTargetPath(ctx.cwd)}`,
316
- "Run /subagent-preset init to copy the bundled sample into the shared config.",
317
- ].join("\n");
318
- }
319
-
320
265
  function clearActiveSubagentPreset(ctx: CommandContext): void {
321
266
  setActiveSubagentPreset(undefined);
322
267
  const override = getSessionSubagentPresetOverride();
@@ -327,10 +272,9 @@ function clearActiveSubagentPreset(ctx: CommandContext): void {
327
272
 
328
273
  function setSessionActiveSubagentPreset(ctx: CommandContext, name: string): void {
329
274
  if (!name) return ctx.ui.notify("Usage: /subagent-preset session <name>", "warning");
330
- if (existingSubagentConfigFiles(ctx.cwd).length === 0) return ctx.ui.notify(missingSubagentConfigMessage(ctx), "warning");
331
275
  const config = loadSubagentConfig(ctx.cwd);
332
276
  const preset = config.presets?.[name];
333
- if (!preset) return ctx.ui.notify(`Unknown sub-agent preset "${name}". Define it in asyncSubagents config or run /subagent-preset list.`, "error");
277
+ if (!preset) return ctx.ui.notify(`Unknown sub-agent preset "${name}". Run /subagent-preset list to see available pools.`, "error");
334
278
  setSessionSubagentPresetOverride(name);
335
279
  ctx.ui.notify(`Session-only sub-agent preset "${name}": ${subagentPresetDescription(preset)}\nApplies to future sub-agent spawns only until this Pi process exits or /subagent-preset session-clear is run. Saved preset selection is unchanged.`, "info");
336
280
  }
@@ -368,6 +312,10 @@ function sortedPresetNames(presets: Record<string, SubagentPreset>, activePreset
368
312
  function subagentPresetDescription(preset: SubagentPreset): string {
369
313
  const parts: string[] = [];
370
314
  if (preset.description) parts.push(preset.description);
315
+ if (preset.models !== undefined) {
316
+ parts.push(`models:${preset.models.join(",") || "(none)"}`);
317
+ return parts.join(", ");
318
+ }
371
319
  if (preset.model) parts.push(`model:${preset.model}`);
372
320
  if (preset.fallbackModels && preset.fallbackModels.length > 0) parts.push(`fallbacks:${preset.fallbackModels.join(",")}`);
373
321
  if (preset.thinking) parts.push(`thinking:${preset.thinking}`);