@selesai/code 0.8.5 → 0.8.7

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 (231) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/core/agent-session.js +13 -1
  3. package/dist/core/extensions/types.d.ts +2 -0
  4. package/dist/core/provider-composer.js +17 -4
  5. package/dist/core/settings-manager.d.ts +2 -0
  6. package/dist/core/settings-manager.js +3 -0
  7. package/dist/core/system-prompt.js +2 -8
  8. package/dist/core/system-prompt.test.js +6 -0
  9. package/dist/core/tools/index.d.ts +1 -0
  10. package/dist/core/tools/index.js +1 -0
  11. package/dist/core/tools/schema-prune.d.ts +16 -0
  12. package/dist/core/tools/schema-prune.js +39 -0
  13. package/dist/core/tools/schema-prune.test.d.ts +1 -0
  14. package/dist/core/tools/schema-prune.test.js +111 -0
  15. package/dist/defaults/models.json +5 -0
  16. package/dist/defaults/settings.json +1 -0
  17. package/dist/extensions/agent-browser.test.ts +178 -2
  18. package/dist/extensions/agent-browser.ts +2 -0
  19. package/dist/extensions/copy-turn.test.ts +229 -0
  20. package/dist/extensions/copy-turn.ts +11 -0
  21. package/dist/extensions/grep-app/index.test.ts +356 -1
  22. package/dist/extensions/grep-app/index.ts +6 -1
  23. package/dist/extensions/handoff-new.test.ts +351 -161
  24. package/dist/extensions/handoff-new.ts +3 -0
  25. package/dist/extensions/inline-skills.test.ts +93 -0
  26. package/dist/extensions/inline-skills.ts +3 -0
  27. package/dist/extensions/model-prompt-injector/config.json +10 -0
  28. package/dist/extensions/model-prompt-injector/index.test.ts +112 -0
  29. package/dist/extensions/model-prompt-injector/index.ts +151 -0
  30. package/dist/extensions/package.json +1 -0
  31. package/dist/extensions/pi-subagents/CHANGELOG.md +158 -90
  32. package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +172 -0
  33. package/dist/extensions/pi-subagents/agents/builder.md +5 -2
  34. package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
  35. package/dist/extensions/pi-subagents/agents/oracle.md +7 -5
  36. package/dist/extensions/pi-subagents/agents/reviewer.md +2 -2
  37. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  38. package/dist/extensions/pi-subagents/agents/worker.md +1 -1
  39. package/dist/extensions/pi-subagents/docs/configuration.md +78 -11
  40. package/dist/extensions/pi-subagents/docs/extension-api.md +36 -0
  41. package/dist/extensions/pi-subagents/docs/missions.md +5 -3
  42. package/dist/extensions/pi-subagents/docs/models.md +1 -1
  43. package/dist/extensions/pi-subagents/docs/observability.md +42 -2
  44. package/dist/extensions/pi-subagents/docs/tool-reference.md +20 -3
  45. package/dist/extensions/pi-subagents/docs/workflows.md +4 -4
  46. package/dist/extensions/pi-subagents/install.mjs +2 -2
  47. package/dist/extensions/pi-subagents/package-lock.json +1470 -1472
  48. package/dist/extensions/pi-subagents/package.json +1 -1
  49. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +1 -1
  50. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
  51. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +16 -15
  52. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
  53. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +46 -26
  54. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  55. package/dist/extensions/pi-subagents/src/agents/agents.ts +37 -12
  56. package/dist/extensions/pi-subagents/src/api/external-runs.ts +174 -84
  57. package/dist/extensions/pi-subagents/src/api/preflight.ts +13 -7
  58. package/dist/extensions/pi-subagents/src/extension/config.ts +59 -0
  59. package/dist/extensions/pi-subagents/src/extension/index.ts +146 -23
  60. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +34 -6
  61. package/dist/extensions/pi-subagents/src/extension/rpc.ts +5 -1
  62. package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -30
  63. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +10 -9
  64. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +13 -8
  65. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +16 -3
  66. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -6
  67. package/dist/extensions/pi-subagents/src/inspectors/herdr/shell-command.ts +16 -0
  68. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +5 -4
  69. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +19 -42
  70. package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +3 -1
  71. package/dist/extensions/pi-subagents/src/missions/store.ts +8 -3
  72. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +82 -25
  73. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +71 -1
  74. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +73 -43
  75. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +17 -6
  76. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -6
  77. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +277 -0
  78. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +11 -3
  79. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +2 -2
  80. package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +11 -1
  81. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +21 -6
  82. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +5 -2
  83. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
  84. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +224 -42
  85. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +27 -7
  86. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +76 -19
  87. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +35 -26
  88. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +108 -9
  89. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +54 -28
  90. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +27 -13
  91. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +298 -33
  92. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +2 -0
  93. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +5 -2
  94. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +2 -1
  95. package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +2 -1
  96. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +16 -0
  97. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +219 -15
  98. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -4
  99. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
  100. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +368 -50
  101. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +107 -1
  102. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +4 -0
  103. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +39 -23
  104. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +16 -2
  105. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +66 -62
  106. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +376 -0
  107. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
  108. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +15 -0
  109. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
  110. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +12 -0
  111. package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +95 -0
  112. package/dist/extensions/pi-subagents/src/shared/agent-stream-options.ts +5 -0
  113. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +0 -4
  114. package/dist/extensions/pi-subagents/src/shared/display-text.ts +50 -0
  115. package/dist/extensions/pi-subagents/src/shared/node-executable.ts +21 -0
  116. package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +71 -0
  117. package/dist/extensions/pi-subagents/src/shared/types.ts +66 -4
  118. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +34 -25
  119. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +4 -0
  120. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +160 -45
  121. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +1 -48
  122. package/dist/extensions/pi-subagents/src/tui/fleet.ts +129 -17
  123. package/dist/extensions/pi-subagents/src/tui/render.ts +122 -44
  124. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +2 -1
  125. package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -3
  126. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +10 -2
  127. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +272 -76
  128. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +28 -0
  129. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +2 -0
  130. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2 -2
  131. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +68 -9
  132. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +27 -1
  133. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1 -1
  134. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +49 -0
  135. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  136. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +26 -1
  137. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +144 -0
  138. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +26 -0
  139. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +369 -98
  140. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +111 -78
  141. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +49 -1
  142. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +12 -0
  143. package/dist/extensions/pi-subagents/test/support/node-command.ts +15 -0
  144. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +50 -0
  145. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +28 -0
  146. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +61 -0
  147. package/dist/extensions/pi-subagents/test/unit/agent-stream-options.test.ts +14 -0
  148. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +76 -0
  149. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
  150. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +162 -0
  151. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +209 -10
  152. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +41 -1
  153. package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +135 -42
  154. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +252 -1
  155. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +192 -6
  156. package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +103 -0
  157. package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +32 -0
  158. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +59 -0
  159. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +91 -0
  160. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +23 -4
  161. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +56 -1
  162. package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +67 -3
  163. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  164. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +14 -0
  165. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +18 -0
  166. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +2 -2
  167. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +25 -2
  168. package/dist/extensions/pi-subagents/test/unit/node-executable.test.ts +26 -0
  169. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +8 -0
  170. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +348 -0
  171. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +5 -2
  172. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +1 -1
  173. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +34 -0
  174. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +6 -1
  175. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +92 -0
  176. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +175 -0
  177. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +127 -17
  178. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +44 -0
  179. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +247 -0
  180. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +107 -2
  181. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +5 -8
  182. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +456 -4
  183. package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +73 -0
  184. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +128 -0
  185. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +28 -0
  186. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +16 -7
  187. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +15 -0
  188. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +79 -15
  189. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +12 -12
  190. package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +109 -0
  191. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +34 -0
  192. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +45 -0
  193. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +94 -1
  194. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +42 -2
  195. package/dist/extensions/ponytail/index.js +11 -0
  196. package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
  197. package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
  198. package/dist/extensions/ponytail/test/extension.test.js +274 -140
  199. package/dist/extensions/ponytail/test/helpers.test.js +280 -92
  200. package/dist/extensions/question/index.ts +33 -0
  201. package/dist/extensions/question/question-list.ts +28 -0
  202. package/dist/extensions/question/row-layout.ts +3 -0
  203. package/dist/extensions/question/tests/batch.test.ts +103 -65
  204. package/dist/extensions/question/tests/exp.test.ts +70 -0
  205. package/dist/extensions/question/tests/exp2.test.ts +59 -0
  206. package/dist/extensions/question/tests/exp3.test.ts +47 -0
  207. package/dist/extensions/question/tests/helpers.test.ts +75 -35
  208. package/dist/extensions/question/tests/question-list.test.ts +640 -198
  209. package/dist/extensions/question/tests/row-layout.test.ts +193 -114
  210. package/dist/extensions/question/tests/schemas.test.ts +42 -19
  211. package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
  212. package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
  213. package/dist/extensions/question/tests/wizard.test.ts +771 -50
  214. package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
  215. package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
  216. package/dist/extensions/rtk.test.ts +180 -1
  217. package/dist/extensions/tokenin-onboarding.ts +3 -0
  218. package/dist/extensions/undo.test.ts +720 -0
  219. package/dist/extensions/undo.ts +6 -0
  220. package/dist/extensions/web-agent-onboarding.test.ts +415 -0
  221. package/dist/extensions/workflow/extension.ts +3 -3
  222. package/dist/extensions/workflow/modes.ts +41 -19
  223. package/dist/skills/pi-subagents/SKILL.md +1 -1
  224. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
  225. package/dist/skills/pi-subagents/references/execution-controls.md +16 -15
  226. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
  227. package/dist/skills/pi-subagents/references/prompting-and-roles.md +46 -26
  228. package/docs/plans/workflow-autoloop-reference.md +1 -2
  229. package/docs/plans/workflow-handoff-carryover.md +108 -0
  230. package/docs/settings.md +6 -0
  231. package/package.json +4 -1
@@ -16,6 +16,7 @@ import { randomUUID } from "node:crypto";
16
16
  import * as fs from "node:fs";
17
17
  import * as os from "node:os";
18
18
  import * as path from "node:path";
19
+ import { fileURLToPath } from "node:url";
19
20
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
20
21
  import { keyText, type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@selesai/code";
21
22
  import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
@@ -23,6 +24,7 @@ import { discoverAgents } from "../agents/agents.ts";
23
24
  import { ensureAccessibleDir } from "../shared/accessible-dir.ts";
24
25
  import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
25
26
  import { resolveCurrentSessionId } from "../shared/session-identity.ts";
27
+ import { resolveSessionLineage } from "../shared/session-lineage.ts";
26
28
  import { cleanupOldChainDirs } from "../shared/settings.ts";
27
29
  import { clearLegacyResultAnimationTimer, renderSubagentResult, renderSubagentSummary } from "../tui/render.ts";
28
30
  import { openSubagentFleet } from "../tui/fleet.ts";
@@ -32,6 +34,7 @@ import { validateChainInput } from "./chain-validation.ts";
32
34
  import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
33
35
  import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
34
36
  import { getActiveAsyncCapacitySnapshot, resolveMaxActiveAsyncRunsPerSession } from "../runs/background/active-async-capacity.ts";
37
+ import { cleanupResultIndexes } from "../runs/background/result-files.ts";
35
38
  import { createResultWatcher } from "../runs/background/result-watcher.ts";
36
39
  import { createScheduledRunManager } from "../runs/background/scheduled-runs.ts";
37
40
  import { registerSlashCommands } from "../slash/slash-commands.ts";
@@ -60,6 +63,7 @@ import { resolveMissionStoreLocation } from "../missions/store.ts";
60
63
  import { listRetainedChildren } from "../runs/background/retained-children.ts";
61
64
  import {
62
65
  type Details,
66
+ type MainWindowRendererConfig,
63
67
  type SubagentState,
64
68
  DIRS,
65
69
  DEFAULT_ARTIFACT_CONFIG,
@@ -82,6 +86,13 @@ import {
82
86
 
83
87
  export { loadConfig, resolveAsyncByDefault } from "./config.ts";
84
88
 
89
+ const SLOW_RELOAD_PHASE_MS = 250;
90
+
91
+ function logSlowPhase(label: string, startedAt: number): void {
92
+ const elapsed = Date.now() - startedAt;
93
+ if (elapsed >= SLOW_RELOAD_PHASE_MS) console.error(`Subagent reload phase '${label}' took ${elapsed}ms.`);
94
+ }
95
+
85
96
  function workflowLaneKeys(script: string): string[] {
86
97
  const keys: string[] = [];
87
98
  const seen = new Set<string>();
@@ -258,12 +269,14 @@ function rebuildSlashResultContainer(
258
269
  result: AgentToolResult<Details>,
259
270
  options: { expanded: boolean },
260
271
  theme: ExtensionContext["ui"]["theme"],
272
+ rendererConfig?: MainWindowRendererConfig,
273
+ foregroundDetachShortcut?: string,
261
274
  ): void {
262
275
  container.clear();
263
276
  container.addChild(new Spacer(1));
264
277
  const boxTheme = isSlashResultRunning(result) ? "toolPendingBg" : isSlashResultError(result) ? "toolErrorBg" : "toolSuccessBg";
265
278
  const box = new Box(1, 1, (text: string) => theme.bg(boxTheme, text));
266
- box.addChild(renderSubagentResult(result, options, theme));
279
+ box.addChild(renderSubagentResult(result, options, theme, undefined, rendererConfig, foregroundDetachShortcut));
267
280
  container.addChild(box);
268
281
  }
269
282
 
@@ -271,6 +284,8 @@ function createSlashResultComponent(
271
284
  details: SlashMessageDetails,
272
285
  options: { expanded: boolean },
273
286
  theme: ExtensionContext["ui"]["theme"],
287
+ rendererConfig?: MainWindowRendererConfig,
288
+ foregroundDetachShortcut?: string,
274
289
  ): Container {
275
290
  const container = new Container();
276
291
  let lastVersion = -1;
@@ -278,7 +293,7 @@ function createSlashResultComponent(
278
293
  const snapshot = getSlashRenderableSnapshot(details);
279
294
  if (snapshot.version !== lastVersion || isSlashResultRunning(snapshot.result)) {
280
295
  lastVersion = snapshot.version;
281
- rebuildSlashResultContainer(container, snapshot.result, options, theme);
296
+ rebuildSlashResultContainer(container, snapshot.result, options, theme, rendererConfig, foregroundDetachShortcut);
282
297
  }
283
298
  return Container.prototype.render.call(container, width);
284
299
  };
@@ -375,6 +390,20 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
375
390
  const tempArtifactsDir = getArtifactsDir(null);
376
391
  const artifactCleanupDays = config.artifactConfig?.cleanupDays ?? DEFAULT_ARTIFACT_CONFIG.cleanupDays;
377
392
  cleanupAllArtifactDirs(artifactCleanupDays);
393
+ const resultIndexCleanupTimer = setTimeout(() => {
394
+ try {
395
+ cleanupResultIndexes(DIRS.results);
396
+ } catch (error) {
397
+ console.error("Failed to clean stale subagent result indexes:", error);
398
+ }
399
+ }, 30_000);
400
+ resultIndexCleanupTimer.unref?.();
401
+
402
+ // Replay guard for lineage result adoption: accept result files written at
403
+ // most this long before the adoption window opened (covers completions that
404
+ // raced the session switch). Older files were left behind by a dead watcher
405
+ // and are ignored until their owning session is resumed.
406
+ const ADOPTED_RESULT_SLACK_MS = 60_000;
378
407
 
379
408
  const state: SubagentState = {
380
409
  baseCwd: "",
@@ -383,6 +412,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
383
412
  ...(config.authorityPolicy ? { authorityPolicy: config.authorityPolicy } : {}),
384
413
  ...(config.missions ? { missionStoreConfig: config.missions } : {}),
385
414
  parentSessionFile: null,
415
+ trustedSessionRoots: [],
386
416
  subagentInProgress: false,
387
417
  subagentSpawns: {
388
418
  sessionId: null,
@@ -456,6 +486,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
456
486
  );
457
487
 
458
488
  const runtimeCleanup = () => {
489
+ clearTimeout(resultIndexCleanupTimer);
459
490
  stopResultWatcher();
460
491
  state.currentSessionId = null;
461
492
  completionNotifier.dispose();
@@ -486,7 +517,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
486
517
  pi.registerMessageRenderer<SlashMessageDetails>(SLASH_RESULT_TYPE, (message, options, theme) => {
487
518
  const details = resolveSlashMessageDetails(message.details);
488
519
  if (!details) return undefined;
489
- return createSlashResultComponent(details, options, theme);
520
+ return createSlashResultComponent(details, options, theme, config.mainWindowRenderer, config.foregroundDetachShortcut);
490
521
  });
491
522
 
492
523
  pi.registerMessageRenderer<undefined>(SLASH_TEXT_RESULT_TYPE, (message, _options, _theme) => {
@@ -580,28 +611,31 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
580
611
  label: "Subagent",
581
612
  description: buildSubagentToolDescription(config),
582
613
  parameters,
614
+ keepParameterDescriptions: true,
583
615
 
584
616
  execute(id, params, signal, onUpdate, ctx) {
585
617
  return executeSubagentCollapsed(id, params as SubagentParamsLike, signal ?? new AbortController().signal, onUpdate, ctx);
586
618
  },
587
619
 
588
620
  renderCall(args, theme) {
621
+ const gap = " ".repeat(config.mainWindowRenderer?.horizontalSpacing ?? 1);
622
+ const title = theme.fg("toolTitle", theme.bold("subagent"));
589
623
  if (args.action) {
590
624
  const target = args.agent || args.chainName || "";
591
625
  return new Text(
592
- `${theme.fg("toolTitle", theme.bold("subagent "))}${args.action}${target ? ` ${theme.fg("accent", target)}` : ""}`,
626
+ `${title}${gap}${args.action}${target ? `${gap}${theme.fg("accent", target)}` : ""}`,
593
627
  0, 0,
594
628
  );
595
629
  }
596
630
  if (args.workflowScript)
597
631
  return new Text(
598
- `${theme.fg("toolTitle", theme.bold("subagent "))}${formatWorkflowManifest(args.workflowScript, args.async, false)}`,
632
+ `${title}${gap}${formatWorkflowManifest(args.workflowScript, args.async, false)}`,
599
633
  0,
600
634
  0,
601
635
  );
602
- const asyncLabel = args.async === true ? theme.fg("warning", " [async]") : "";
636
+ const asyncLabel = args.async === true ? `${gap}${theme.fg("warning", "[async]")}` : "";
603
637
  return new Text(
604
- `${theme.fg("toolTitle", theme.bold("subagent "))}${theme.fg("accent", args.agent || "?")}${asyncLabel}`,
638
+ `${title}${gap}${theme.fg("accent", args.agent || "?")}${asyncLabel}`,
605
639
  0,
606
640
  0,
607
641
  );
@@ -612,7 +646,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
612
646
  const renderedResult = { ...result, isError: context.isError };
613
647
  return summaryInlineToolDisplay
614
648
  ? renderSubagentSummary(renderedResult, options, theme)
615
- : renderSubagentResult(renderedResult, options, theme);
649
+ : renderSubagentResult(renderedResult, options, theme, undefined, config.mainWindowRenderer, config.foregroundDetachShortcut);
616
650
  },
617
651
 
618
652
  };
@@ -630,26 +664,41 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
630
664
 
631
665
  pi.on("agent_end", async (_event, ctx) => {
632
666
  if (!ctx.hasUI) await drainOutstandingWork({ state, events: pi.events });
633
- const ownerSessionId = state.currentSessionId;
634
- if (!ownerSessionId) return;
667
+ const ownerSessionIds = state.sessionLineage?.length
668
+ ? state.sessionLineage
669
+ : state.currentSessionId ? [state.currentSessionId] : [];
670
+ if (ownerSessionIds.length === 0) return;
635
671
  goalTurnId += 1;
636
672
  try {
637
673
  const location = resolveMissionStoreLocation({ projectRoot: state.baseCwd, ...(config.missions ? { config: config.missions } : {}) });
638
- const retainedChildren = listRetainedChildren(DIRS.async, ownerSessionId);
639
- for (const notice of collectGoalContinuationNotices({ location, ownerSessionId, retainedChildren, turnId: goalTurnId })) {
640
- handleSubagentControlNotice({
641
- pi,
642
- state,
643
- visibleControlNotices: new Set(),
644
- details: { source: "goal", event: notice.event, noticeText: notice.message },
645
- });
674
+ const retainedChildren = listRetainedChildren(DIRS.async, ownerSessionIds);
675
+ const emittedMissions = new Set<string>();
676
+ for (const ownerSessionId of ownerSessionIds) {
677
+ for (const notice of collectGoalContinuationNotices({ location, ownerSessionId, retainedChildren, turnId: goalTurnId })) {
678
+ if (emittedMissions.has(notice.missionId)) continue;
679
+ emittedMissions.add(notice.missionId);
680
+ handleSubagentControlNotice({
681
+ pi,
682
+ state,
683
+ visibleControlNotices: new Set(),
684
+ details: { source: "goal", event: notice.event, noticeText: notice.message },
685
+ });
686
+ }
646
687
  }
647
688
  } catch (error) {
648
689
  console.error("Failed to evaluate goal missions:", error);
649
690
  }
650
691
  });
651
692
 
652
- registerSlashCommands(pi, state, { fleetKeybindings: config.fleetKeybindings });
693
+ registerSlashCommands(pi, state, {
694
+ fleetKeybindings: config.fleetKeybindings,
695
+ foregroundDetachShortcut: config.foregroundDetachShortcut,
696
+ });
697
+ pi.on("resources_discover", () => {
698
+ return {
699
+ promptPaths: [fileURLToPath(new URL("../../prompts", import.meta.url))],
700
+ };
701
+ });
653
702
 
654
703
  const eventUnsubscribeStoreKey = "__piSubagentEventUnsubscribes";
655
704
  const controlNoticeSeenStoreKey = "__piSubagentVisibleControlNotices";
@@ -758,14 +807,51 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
758
807
  resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession),
759
808
  { liveWorkflowRunIds: new Set(state.workflowControllers?.keys() ?? []) },
760
809
  );
810
+ const adoptedActive = [...state.asyncJobs.values()].filter((job) => job.adopted && (job.status === "queued" || job.status === "running")).length;
811
+ if (adoptedActive > 0) {
812
+ state.activeAsyncCapacity = {
813
+ ...state.activeAsyncCapacity,
814
+ used: state.activeAsyncCapacity.used + adoptedActive,
815
+ };
816
+ }
817
+ };
818
+
819
+ const formatActiveWorkflowSummary = (target: SubagentState, intro: string): string => {
820
+ const active = [...target.asyncJobs.values()]
821
+ .filter((job) => job.status === "queued" || job.status === "running")
822
+ .sort((left, right) => (left.startedAt ?? 0) - (right.startedAt ?? 0));
823
+ const lines = active.map((job) => {
824
+ const label = job.mode === "workflow"
825
+ ? `workflow[${job.workflowKey ?? "script"}]`
826
+ : `${job.mode ?? "run"}${job.agents?.length ? ` ${job.agents.join(", ")}` : ""}`;
827
+ const step = job.currentStep !== undefined
828
+ ? ` · step ${job.currentStep + 1}/${job.chainStepCount ?? job.stepsTotal ?? "?"}`
829
+ : "";
830
+ const activity = job.currentTool ? ` · last: ${job.currentTool}` : "";
831
+ const adopted = job.adopted ? " (carried over from a previous session)" : "";
832
+ return `- ${job.asyncId} ${label}${step}${activity}${adopted}`;
833
+ });
834
+ return [
835
+ intro,
836
+ ...lines,
837
+ "Monitor with subagent status <id>; interrupt/resume/steer by run id; children.list lists completed steps. Results arrive automatically.",
838
+ ].join("\n");
761
839
  };
762
840
 
763
- const resetSessionState = (ctx: ExtensionContext, recovering: boolean) => {
841
+ const resetSessionState = (ctx: ExtensionContext, recovering: boolean, previousSessionFile?: string | null) => {
842
+ state.sessionLineage = resolveSessionLineage({ sessionManager: ctx.sessionManager }, { parentHint: previousSessionFile ?? null });
843
+ state.handoffResumePending = false;
844
+ if (state.sessionLineage.length > 1) state.adoptedResultWindowStart = Date.now() - ADOPTED_RESULT_SLACK_MS;
845
+ else state.adoptedResultWindowStart = undefined;
764
846
  state.widgetsSuspended = false;
765
847
  state.baseCwd = ctx.cwd;
766
848
  goalTurnId = 0;
767
849
  state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
768
850
  state.parentSessionFile = ctx.sessionManager.getSessionFile();
851
+ state.trustedSessionRoots = [...new Set([
852
+ ...(config.defaultSessionDir ? [path.resolve(expandTilde(config.defaultSessionDir))] : []),
853
+ ...(state.parentSessionFile ? [getSubagentSessionRoot(state.parentSessionFile)] : []),
854
+ ])];
769
855
  state.subagentSpawns = {
770
856
  sessionId: state.currentSessionId,
771
857
  count: 0,
@@ -785,18 +871,39 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
785
871
  }
786
872
  }
787
873
  state.lastUiContext = ctx;
874
+ let phaseStartedAt = Date.now();
788
875
  refreshActiveAsyncCapacity();
876
+ logSlowPhase("active-capacity", phaseStartedAt);
877
+ phaseStartedAt = Date.now();
789
878
  cleanupSessionArtifacts(ctx);
879
+ logSlowPhase("session-artifact-cleanup", phaseStartedAt);
790
880
  state.foregroundControls.clear();
791
881
  state.lastForegroundControlId = null;
882
+ phaseStartedAt = Date.now();
792
883
  resetJobs(ctx);
884
+ logSlowPhase("reset-jobs", phaseStartedAt);
885
+ phaseStartedAt = Date.now();
793
886
  restoreForegroundRunHistory(state, { resultsDir: DIRS.results });
794
- restoreActiveJobs(ctx);
887
+ logSlowPhase("foreground-history", phaseStartedAt);
888
+ phaseStartedAt = Date.now();
889
+ const adoptedJobs = restoreActiveJobs(ctx);
890
+ logSlowPhase("active-job-restore", phaseStartedAt);
891
+ state.handoffResumePending = adoptedJobs > 0 && state.lastUiContext?.hasUI === true;
892
+ phaseStartedAt = Date.now();
795
893
  scheduledRunManager.bindSession(ctx);
894
+ logSlowPhase("scheduled-runs", phaseStartedAt);
895
+ phaseStartedAt = Date.now();
796
896
  restoreSlashFinalSnapshots(ctx.sessionManager.getEntries());
897
+ logSlowPhase("slash-snapshots", phaseStartedAt);
898
+ phaseStartedAt = Date.now();
797
899
  waitSubscriptionManager.restore();
900
+ logSlowPhase("wait-subscriptions", phaseStartedAt);
901
+ phaseStartedAt = Date.now();
798
902
  startResultWatcher();
903
+ logSlowPhase("result-watcher-start", phaseStartedAt);
904
+ phaseStartedAt = Date.now();
799
905
  primeExistingResults({ triggerTurn: !recovering });
906
+ logSlowPhase("result-prime", phaseStartedAt);
800
907
  fleetStatus?.setContext(ctx);
801
908
  };
802
909
 
@@ -807,6 +914,17 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
807
914
 
808
915
  pi.on("agent_settled", () => {
809
916
  resumeWidgetsAfterCompaction();
917
+ if (state.handoffResumePending && state.lastUiContext?.hasUI === true) {
918
+ state.handoffResumePending = false;
919
+ pi.sendMessage(
920
+ {
921
+ customType: "subagent-handoff-resume",
922
+ content: formatActiveWorkflowSummary(state, "Session handoff detected. The previous session's background workflows were carried over:"),
923
+ display: false,
924
+ },
925
+ { triggerTurn: true },
926
+ );
927
+ }
810
928
  });
811
929
 
812
930
  pi.on("session_before_compact", (event) => {
@@ -819,7 +937,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
819
937
  pi.sendMessage(
820
938
  {
821
939
  customType: "subagent-compaction-resume",
822
- content: "Compaction is complete. Resume the parent task now; background subagent results will arrive separately when ready.",
940
+ content: formatActiveWorkflowSummary(state, "Compaction is complete. Resume the parent task now; results for the runs below arrive separately when ready:"),
823
941
  display: false,
824
942
  },
825
943
  { triggerTurn: true },
@@ -828,7 +946,8 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
828
946
 
829
947
  pi.on("session_start", (event, ctx) => {
830
948
  const recovering = event.reason === "startup" || event.reason === "reload" || event.reason === "resume";
831
- resetSessionState(ctx, recovering);
949
+ const previousSessionFile = event.reason === "new" || event.reason === "fork" ? event.previousSessionFile : null;
950
+ resetSessionState(ctx, recovering, previousSessionFile);
832
951
  herdrStatusBridge.sessionStarted({
833
952
  hasUI: ctx.hasUI === true,
834
953
  runs: activeHerdrRuns(),
@@ -839,9 +958,13 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
839
958
 
840
959
  pi.on("session_shutdown", async () => {
841
960
  state.widgetsSuspended = false;
961
+ clearTimeout(resultIndexCleanupTimer);
842
962
  stopResultWatcher();
843
963
  state.currentSessionId = null;
844
964
  state.parentSessionFile = null;
965
+ state.sessionLineage = undefined;
966
+ state.adoptedResultWindowStart = undefined;
967
+ state.handoffResumePending = false;
845
968
  completionNotifier.dispose();
846
969
  delete process.env[SUBAGENT_PARENT_SESSION_ENV];
847
970
  for (const unsubscribe of eventUnsubscribes) {
@@ -9,6 +9,7 @@ export interface PublicSubagentExecutionParams {
9
9
  concurrency?: unknown;
10
10
  chainDir?: unknown;
11
11
  workflowScript?: unknown;
12
+ output?: unknown;
12
13
  resume?: unknown;
13
14
  clarify?: unknown;
14
15
  runFanoutBudget?: unknown;
@@ -33,7 +34,7 @@ export function normalizePublicSubagentExecution<T extends PublicSubagentExecuti
33
34
  }
34
35
  const action = params.action;
35
36
  if (action !== undefined && (typeof action !== "string" || !action.trim())) {
36
- return { ok: false, error: "action must be a non-empty management/control action, or omit action and execute directly.", mode: "management" };
37
+ return { ok: false, error: "action must be a non-empty management/control action, or omit action and use workflowScript.", mode: "management" };
37
38
  }
38
39
  const normalizedAction = typeof action === "string" ? action.trim() : undefined;
39
40
  if (params.clarify === true && params.workflowScript !== undefined) {
@@ -59,10 +60,16 @@ export function normalizePublicSubagentExecution<T extends PublicSubagentExecuti
59
60
  if (params.workflowScript !== undefined) {
60
61
  return { ok: false, error: "workflowScript execution must omit action; only schedule.create accepts action with workflowScript.", mode: "management" };
61
62
  }
63
+ if (params.task !== undefined) {
64
+ return { ok: false, error: "Structured single-child task cannot be combined with a management/control action.", mode: "management" };
65
+ }
62
66
  return { ok: true, params: { ...params, action: normalizedAction } };
63
67
  }
64
- if (typeof params.workflowScript === "string" && params.workflowScript.trim()) {
65
- return { ok: true, params };
68
+ if (params.step !== undefined) {
69
+ return { ok: false, error: "step is only available with action='append-step'; it is not an execution mode.", mode: "workflow" };
70
+ }
71
+ if (params.workflowScript !== undefined && (params.agent !== undefined || params.task !== undefined)) {
72
+ return { ok: false, error: "Structured single-child execution cannot be combined with workflowScript.", mode: "workflow" };
66
73
  }
67
74
  if (params.chain !== undefined) {
68
75
  return { ok: true, params };
@@ -70,8 +77,29 @@ export function normalizePublicSubagentExecution<T extends PublicSubagentExecuti
70
77
  if (params.tasks !== undefined) {
71
78
  return { ok: true, params };
72
79
  }
73
- if (params.agent !== undefined || params.task !== undefined || params.step !== undefined) {
74
- return { ok: true, params };
80
+ if (params.agent !== undefined || params.task !== undefined) {
81
+ if (typeof params.agent !== "string" || !params.agent.trim()) {
82
+ return { ok: false, error: "Structured single-child execution requires agent to be a non-empty string.", mode: "workflow" };
83
+ }
84
+ if (params.task !== undefined && typeof params.task !== "string") {
85
+ return { ok: false, error: "Structured single-child task must be a string when provided.", mode: "workflow" };
86
+ }
87
+ const { agent: _agent, task: _task, output, ...workflowDefaults } = params;
88
+ const child = {
89
+ agent: params.agent.trim(),
90
+ ...(params.task !== undefined ? { task: params.task } : {}),
91
+ output: output === undefined ? true : output,
92
+ };
93
+ return {
94
+ ok: true,
95
+ params: {
96
+ ...workflowDefaults,
97
+ workflowScript: `console.info("Converted structured single-child request to workflow runs.run('main', ...)."); return runs.run("main", ${JSON.stringify(child)})`,
98
+ } as T,
99
+ };
100
+ }
101
+ if (typeof params.workflowScript !== "string" || !params.workflowScript.trim()) {
102
+ return { ok: false, error: "Execution requires one mode: { agent, task? }, { chain: [...] }, { tasks: [...] }, or { workflowScript }.", mode: "workflow" };
75
103
  }
76
- return { ok: false, error: "Execution requires one mode: { agent, task? }, { chain: [...] }, { tasks: [...] }, or { workflowScript }.", mode: "workflow" };
104
+ return { ok: true, params };
77
105
  }
@@ -21,6 +21,7 @@ import { sanitizeDisplayText, truncateDisplayText } from "../shared/display-text
21
21
  import { readStatus } from "../shared/utils.ts";
22
22
  import { SubagentParams } from "./schemas.ts";
23
23
  import { normalizePublicSubagentExecution } from "./public-execution.ts";
24
+ import { ASYNC_STATUS_SNAPSHOT_KIND, ASYNC_STATUS_SNAPSHOT_VERSION, buildAsyncStatusSnapshotForState } from "../runs/background/async-status-snapshot.ts";
24
25
 
25
26
  export const SUBAGENT_RPC_PROTOCOL_VERSION = 1;
26
27
  export const SUBAGENT_RPC_REQUEST_EVENT = "subagents:rpc:v1:request";
@@ -381,6 +382,7 @@ function pingData(ctx: ExtensionContext | null) {
381
382
  capabilities: {
382
383
  status: true,
383
384
  fleetStatus: { version: 1 },
385
+ asyncStatusSnapshot: { kind: ASYNC_STATUS_SNAPSHOT_KIND, version: ASYNC_STATUS_SNAPSHOT_VERSION },
384
386
  asyncSpawn: true,
385
387
  steer: true,
386
388
  nonRecoveringSteer: true,
@@ -550,13 +552,15 @@ async function handleRequest(
550
552
  request.method,
551
553
  { action: "status", ...normalizeTargetParams(request.params, "status") },
552
554
  );
555
+ const sessionId = resolveCurrentSessionId(ctx.sessionManager);
553
556
  return {
554
557
  ...status,
555
558
  fleet: buildFleetStatus(
556
559
  options.state,
557
560
  fleetKeys,
558
- resolveCurrentSessionId(ctx.sessionManager),
561
+ sessionId,
559
562
  ),
563
+ asyncSnapshot: buildAsyncStatusSnapshotForState(options.state, sessionId),
560
564
  };
561
565
  }
562
566
  if (request.method === "steer") {
@@ -89,11 +89,11 @@ const AcceptanceOverride = Type.Unsafe({
89
89
  { type: "boolean", enum: [false] },
90
90
  { type: "object", additionalProperties: true },
91
91
  ],
92
- description: `Optional acceptance policy. For reviewer/read-only calls, omit acceptance. Example: { level: "checked", evidence: ["commands-run", "changed-files"] }. Supported evidence kinds: ${AcceptanceEvidenceKinds.join(", ")}. Evidence levels end at verified; use acceptance.review.required for review. Omitted means auto-inferred unless agentContract compatibility behavior is enabled.`,
92
+ description: `Optional acceptance policy. Omit for read-only calls. Example: { level: "checked", evidence: ["commands-run", "changed-files"] }. Supported evidence kinds: ${AcceptanceEvidenceKinds.join(", ")}. Evidence levels end at verified; use acceptance.review.required for review.`,
93
93
  });
94
94
 
95
95
  const AgentContractOverride = Type.Object({
96
- version: Type.Integer({ enum: [1], description: "Enable compatibility behavior for this run/child." }),
96
+ version: Type.Integer({ minimum: 1, maximum: 1, description: "Enable compatibility behavior for this run/child." }),
97
97
  }, { additionalProperties: false, description: "Compatibility behavior. Omit for the default behavior." });
98
98
 
99
99
  const ChainGateOverride = Type.String({
@@ -255,49 +255,49 @@ const ControlOverrides = Type.Object({
255
255
  });
256
256
 
257
257
  const SubagentParamProperties = {
258
- agent: Type.Optional(Type.String({ description: "Agent target for management actions such as get, update, delete, and models." })),
259
- task: Type.Optional(Type.String({ description: "Task (SINGLE mode, optional for self-contained agents)" })),
258
+ agent: Type.Optional(Type.String({ description: "Agent for one-child execution, or target for agent management actions." })),
259
+ task: Type.Optional(Type.String({ description: "Optional one-child task. Requires agent; cannot combine with action or workflowScript." })),
260
260
  resume: Type.Optional(Type.String({ description: "Retained child run id for a workflowScript runs.run/runs.all item. Mutually exclusive with agent; task supplies the follow-up." })),
261
261
  // Management action (when present, tool operates in management mode)
262
262
  action: Type.Optional(Type.String({ minLength: 1,
263
- description: "Optional management/control action. Omit this field for workflowScript execution; use it only for management/control actions."
263
+ description: "Optional management/control action. Omit this field for structured single-child or workflowScript execution; use it only for management/control actions."
264
264
  })),
265
265
  name: Type.Optional(Type.String({ description: "Human-readable name for action='schedule.create'." })),
266
266
  id: Type.Optional(Type.String({
267
- description: "Run id or prefix for status, interrupt, stop, dismiss, resume, steer, append-step, approve-checkpoint, reject-checkpoint, mission.attach-run, or the decision id for mission.resolve-decision."
267
+ description: "Run id/prefix for status/debug.run, interrupt, steer, append-step, approve-checkpoint, reject-checkpoint, or mission."
268
268
  })),
269
269
  runId: Type.Optional(Type.String({
270
- description: "Target run ID for interrupt, stop, dismiss, resume, steer, append-step, approve-checkpoint, reject-checkpoint, or mission.attach-run. Prefer id for new calls."
270
+ description: "Target run ID for debug.run, interrupt, steer, append-step, or mission.attach-run. Prefer id."
271
271
  })),
272
272
  dir: Type.Optional(Type.String({
273
- description: "Async run directory for action='status', action='stop', action='resume', or action='steer'."
273
+ description: "Async run directory for status/debug.run, stop, resume, or steer."
274
274
  })),
275
275
  handoffPath: Type.Optional(Type.String({ description: "worktree.discard manifest." })),
276
276
  index: Type.Optional(Type.Integer({ minimum: 0, description: "Zero-based child index for actions that target a specific child or transcript." })),
277
277
  view: Type.Optional(Type.String({
278
278
  enum: ["fleet", "transcript"],
279
- description: "Optional status view. Use view='fleet' for a read-only active foreground/async fleet surface, or view='transcript' with id/dir (and optional index) to tail a run transcript.",
279
+ description: "Optional status view: 'fleet' for a read-only fleet surface, or 'transcript' with id/dir (and optional index) to tail a run transcript.",
280
280
  })),
281
281
  lines: Type.Optional(Type.Integer({ minimum: 1, maximum: 500, description: "Maximum transcript lines for action='status', view='transcript'. Defaults to 80." })),
282
282
  topic: Type.Optional(Type.String()),
283
283
  message: Type.Optional(Type.String({ description: "Follow-up message for resume, live guidance for steer, or optional startup prompt for project.open." })),
284
- mode: Type.Optional(Type.String({ enum: ["steer", "follow_up", "auto"], description: "Delivery mode for action='steer'. steer interrupts at the next safe point (default), follow_up waits for the next turn boundary, and auto follows up mid-turn but delivers immediately between turns." })),
285
- steeringRecovery: Type.Optional(Type.Boolean({ description: "For action='steer', allow pause-and-revive recovery after a missed acknowledgment. Defaults true for direct tool calls in steer mode; extension RPC steering forces false so callers retain exact child ownership." })),
286
- additional: Type.Optional(Type.Integer({ minimum: 1, description: "Positive launches to add with action='grant-spawn-budget'. Root interactive parent with native user confirmation only; total grants cannot exceed the original configured cap." })),
287
- scope: Type.Optional(Type.String({ enum: ["session", "user", "project"], description: "Scope for action='watchdog.configure'. Defaults to session to avoid persistent settings writes unless user/project is explicit." })),
284
+ mode: Type.Optional(Type.String({ enum: ["steer", "follow_up", "auto"], description: "Delivery mode for action='steer': steer interrupts at the next safe point (default), follow_up waits for the next turn boundary, auto follows up mid-turn." })),
285
+ steeringRecovery: Type.Optional(Type.Boolean({ description: "For action='steer', allow pause-and-revive recovery after a missed acknowledgment. Defaults true for direct tool calls; false for extension RPC steering." })),
286
+ additional: Type.Optional(Type.Integer({ minimum: 1, description: "Positive launches to add with action='grant-spawn-budget'. Root interactive parent with native user confirmation only." })),
287
+ scope: Type.Optional(Type.String({ enum: ["session", "user", "project"], description: "Scope for action='watchdog.configure'. Defaults to session." })),
288
288
  target: Type.Optional(Type.String({ enum: ["main", "children", "child"], description: "Target for watchdog actions." })),
289
289
  focus: Type.Optional(Type.Boolean({ description: "Focus the new Herdr pane for inspector.open or project.open." })),
290
290
  thinking: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "boolean", enum: [false] }], description: "Thinking level for action='watchdog.configure' (off/minimal/low/medium/high/xhigh/max, inherit, or false for off)." })),
291
- schedule: Type.Optional(Type.String({ deprecated: true, description: "Removed one-shot schedule field. Use action='schedule.create' with at." })),
292
- scheduleName: Type.Optional(Type.String({ deprecated: true, description: "Removed schedule display field. Use name." })),
291
+ schedule: Type.Optional(Type.String({ deprecated: true, description: "Deprecated. Use action='schedule.create' with at." })),
292
+ scheduleName: Type.Optional(Type.String({ deprecated: true, description: "Deprecated. Use name." })),
293
293
  at: Type.Optional(Type.String({ description: "One-shot trigger for action='schedule.create': a relative delay such as '+10m' or an ISO timestamp with timezone." })),
294
294
  every: Type.Optional(Type.String({ description: "Fixed recurring interval for action='schedule.create', such as '30m', '6h', '2d', or '2w'." })),
295
- on: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "integer" }], description: "Calendar selector reserved for a later schedule slice." })),
296
- timezone: Type.Optional(Type.String({ description: "IANA timezone reserved for a later calendar schedule slice." })),
295
+ on: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "integer" }], description: "Reserved for a later calendar schedule slice." })),
296
+ timezone: Type.Optional(Type.String({ description: "Reserved for a later calendar schedule slice." })),
297
297
  overlap: Type.Optional(Type.String({ enum: ["skip"], description: "Overlap policy. This slice supports skip only." })),
298
298
  catchUp: Type.Optional(Type.String({ enum: ["none", "latest"], description: "Missed occurrence policy for recurring schedules. Defaults to latest." })),
299
299
  missionId: Type.Optional(Type.String({ description: "Mission id." })),
300
- mission: Type.Optional(Type.Unsafe({ ...MissionLaunchOverride, description: "Mission object, or false for no mission. Set exactly one non-empty title or summary; objective and labels are optional. goal may only be true and then requires budget.tokens." })),
300
+ mission: Type.Optional(Type.Unsafe({ ...MissionLaunchOverride, description: "Mission object, or false for no mission. Requires exactly one non-empty title or summary; goal may only be true and then requires budget.tokens." })),
301
301
  missionUpdate: Type.Optional(Type.Unsafe({ ...MissionUpdateOverride, description: "Mission update: objective, goal false or {paused:boolean}, budget, summary, labels, decisions, artifacts, or delivery receipts." })),
302
302
  missionStatus: Type.Optional(Type.String({ description: "Mission status." })),
303
303
  missionScope: Type.Optional(Type.String({ description: "Mission list scope: project (default) or global pointer index." })),
@@ -316,10 +316,10 @@ const SubagentParamProperties = {
316
316
  ],
317
317
  description: "Agent/chain config for create/update. Object or JSON string; presence of steps creates a chain."
318
318
  })),
319
- workflowScript: Type.Optional(Type.String({ minLength: 1, description: "Trusted inline JavaScript statement body. Starts async by default; pass async:false for a small foreground run. Use explicit return for output. Use await prompts.render(ref, vars?) for task text. Use await runs.run(key, {agent, task, worktree?, gate?}) or runs.run(key, {resume, task}), runs.all([...]), runs.status(id), runs.ref(s), emit(value), console, and return. Mission workflows also have async state.get(key) and state.set(key, JSONValue). Compose sequential and parallel phases dynamically. Set worktree:true at workflow or child level for a separate managed worktree; child fields override workflow defaults. gate is one host-run command and cannot be combined with acceptance. runs.run accepts one child only. No filesystem, shell, Pi tools, or host globals." })),
319
+ workflowScript: Type.Optional(Type.String({ minLength: 1, description: "Trusted inline JavaScript statement body; use explicit return for output. Available: runs.run(key, {agent, task, ...}) and runs.run(key, {resume, task}), runs.all([...]), runs.status(id), runs.ref(s), await prompts.render(ref, vars?), emit, console; mission workflows also have async state.get/set. Scripts start async by default. Set worktree:true at workflow or child level for a separate managed worktree; child fields override workflow defaults. Compose sequential and parallel phases dynamically. No filesystem, shell, Pi tools, or host globals." })),
320
320
  chain: Type.Optional(Type.Unsafe({
321
321
  anyOf: [Type.Array(ChainItem, { minItems: 1, description: "Sequential chain of steps" })],
322
- description: "Sequential chain of steps. Steps support template variables {task}, {previous}, {chain_dir}, and {outputs.name}; use as: \"name\" on a step to bind {outputs.name} for later steps, phase/label for status readability, outputSchema for structured results, and { checkpoint: \"name\", message?: \"...\" } for human gates paused until approve-checkpoint / reject-checkpoint. Set gateOn: \"acceptance\" to advance on acceptance instead of execution.",
322
+ description: "Sequential chain of steps. Steps support template variables {task}, {previous}, {chain_dir}, and {outputs.name}; use as: \"name\" on a step to bind {outputs.name} for later steps; { checkpoint: \"name\", message?: \"...\" } pauses for approve-checkpoint / reject-checkpoint; gateOn: \"acceptance\" advances on acceptance instead of execution.",
323
323
  })),
324
324
  tasks: Type.Optional(Type.Unsafe({
325
325
  anyOf: [
@@ -328,27 +328,28 @@ const SubagentParamProperties = {
328
328
  ],
329
329
  description: "Top-level parallel tasks array, or a single dynamic fanout child template when expand/collect are present.",
330
330
  })),
331
- chatProgress: Type.Optional(Type.String({ enum: ["auto", "off", "terminal", "milestones", "live-card"], description: "WorkflowScript chat progress projection. auto shows a live in-chat card for watched foreground workflows in the same Git repository, milestones for background runs, and terminal for other-repo work." })),
332
- worktree: Type.Optional(Type.Boolean({ description: "Managed child isolation. true gives each workflow child a separate git worktree; an individual runs.run/runs.all item can override a workflow default with worktree:false." })),
331
+ chatProgress: Type.Optional(Type.String({ enum: ["auto", "off", "terminal", "milestones", "live-card"], description: "WorkflowScript chat progress projection: auto shows a live in-chat card for watched foreground workflows in the same Git repository, milestones for background runs, terminal otherwise." })),
332
+ worktree: Type.Optional(Type.Boolean({ description: "Managed child isolation. true gives each workflow child a separate git worktree; individual runs.run/runs.all items can override with worktree:false." })),
333
333
  step: Type.Optional(Type.Unsafe({ ...ChainItem, description: "One chain step for action='append-step' only. Not an execution mode." })),
334
334
  context: Type.Optional(Type.String({
335
335
  enum: ["fresh", "fork"],
336
- description: "'fresh' or 'fork' to branch from parent session. Explicit context overrides every child in the invocation. If omitted, each requested agent uses its own defaultContext; agents without defaultContext: 'fork' run fresh.",
336
+ description: "'fresh' or 'fork' to branch from parent session. Explicit context overrides every child in the invocation. If omitted, each requested agent uses its own defaultContext; agents without one run 'fork' fresh.",
337
337
  })),
338
338
  async: Type.Optional(Type.Boolean({ description: "Run in background (default: false, or per config)" })),
339
- clarify: Type.Optional(Type.Boolean({ description: "Show TUI to preview/edit before execution. Explicit clarify: true keeps the run foreground for the clarify UI; omitted clarify can still run in the background when async: true is set." })),
340
- timeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "Timeout. Foreground and single async runs use config timeoutMs, else 30m; async composites have no default parent deadline. Alias maxRuntimeMs." })),
341
- maxRuntimeMs: Type.Optional(Type.Integer({ minimum: 1, description: "Alias timeoutMs. Foreground and single async runs use config timeoutMs, else 30m; async composites have no default parent deadline." })),
339
+ clarify: Type.Optional(Type.Boolean({ description: "Show TUI to preview/edit before execution; explicit true keeps the run foreground." })),
340
+ timeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "Timeout. Defaults to config timeoutMs or 30m for foreground/single async runs. Alias maxRuntimeMs." })),
341
+ maxRuntimeMs: Type.Optional(Type.Integer({ minimum: 1, description: "Alias of timeoutMs." })),
342
+ toolTimeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "Optional hard per-tool-call timeout in milliseconds." })),
342
343
  turnBudget: Type.Optional(TurnBudgetOverride),
343
344
  toolBudget: Type.Optional(ToolBudgetOverride),
344
345
  usageBudget: Type.Optional(UsageBudgetOverride),
345
- agentScope: Type.Optional(Type.String({ description: "Agent discovery scope: 'user', 'project', or 'both' (default: 'both'; project wins on name collisions)" })),
346
+ agentScope: Type.Optional(Type.String({ description: "Agent discovery scope: 'user', 'project', or 'both' (default 'both')." })),
346
347
  cwd: Type.Optional(Type.String({ description: "Execution cwd, or target project directory for project.open/status/close." })),
347
348
  artifacts: Type.Optional(Type.Boolean({ description: "Write debug artifacts (default: true)" })),
348
349
  includeProgress: Type.Optional(Type.Boolean({ description: "Include full progress in result (default: false)" })),
349
350
  share: Type.Optional(Type.Boolean({ description: "Upload session to GitHub Gist for sharing (default: false)" })),
350
351
  sessionDir: Type.Optional(
351
- Type.String({ description: "Directory to store session logs (default: temp; enables sessions even if share=false)" }),
352
+ Type.String({ description: "Directory to store session logs (default: temp)." }),
352
353
  ),
353
354
  control: Type.Optional(ControlOverrides),
354
355
  // Workflow defaults forwarded to each runs.run/runs.all child unless overridden there.
@@ -372,10 +373,10 @@ const { step: _legacyChainStep, ...subagentParamPropertiesWithoutStep } = Subage
372
373
  const trimmedSubagentParamProperties = {
373
374
  ...subagentParamPropertiesWithoutStep,
374
375
  id: Type.Optional(Type.String({
375
- description: "Run id or prefix for status, interrupt, stop, resume, steer, mission.attach-run, or the decision id for mission.resolve-decision."
376
+ description: "Run id/prefix for status/debug.run, interrupt, steer, or mission.attach-run."
376
377
  })),
377
378
  runId: Type.Optional(Type.String({
378
- description: "Target run ID for interrupt, stop, resume, steer, or mission.attach-run. Prefer id for new calls."
379
+ description: "Target run ID for debug.run, interrupt, steer, or mission.attach-run. Prefer id."
379
380
  })),
380
381
  };
381
382
  const SubagentParamsSchema = Type.Object(SubagentParamProperties);