@selesai/code 0.13.12 → 0.13.14

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 (203) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/extensions/pi-subagents/CHANGELOG.md +77 -1
  3. package/dist/extensions/pi-subagents/VISION.md +12 -1
  4. package/dist/extensions/pi-subagents/docs/agents.md +13 -6
  5. package/dist/extensions/pi-subagents/docs/configuration.md +35 -9
  6. package/dist/extensions/pi-subagents/docs/extension-api.md +1 -1
  7. package/dist/extensions/pi-subagents/docs/missions.md +1 -1
  8. package/dist/extensions/pi-subagents/docs/models.md +6 -6
  9. package/dist/extensions/pi-subagents/docs/observability.md +6 -3
  10. package/dist/extensions/pi-subagents/docs/tool-reference.md +3 -3
  11. package/dist/extensions/pi-subagents/docs/watchdog.md +92 -114
  12. package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
  13. package/dist/extensions/pi-subagents/install.mjs +1 -2
  14. package/dist/extensions/pi-subagents/package.json +1 -1
  15. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  16. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +6 -6
  17. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  18. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
  19. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +4 -4
  20. package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +1 -1
  21. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -4
  22. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +3 -0
  23. package/dist/extensions/pi-subagents/src/agents/agents.ts +120 -124
  24. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +5 -1
  25. package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -0
  26. package/dist/extensions/pi-subagents/src/api/shared-types.ts +3 -0
  27. package/dist/extensions/pi-subagents/src/extension/config.ts +20 -0
  28. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
  29. package/dist/extensions/pi-subagents/src/extension/schemas.ts +6 -2
  30. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
  31. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +19 -13
  32. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +26 -8
  33. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -23
  34. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -2
  35. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
  36. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +54 -3
  37. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +16 -0
  38. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +22 -2
  39. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +63 -6
  40. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -1
  41. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +69 -12
  42. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +13 -0
  43. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +3 -1
  44. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -8
  45. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +59 -6
  46. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +95 -18
  47. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +11 -43
  48. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -0
  49. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  50. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +24 -3
  51. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +4 -0
  52. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -6
  53. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +1 -0
  54. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +33 -18
  55. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +73 -37
  56. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +33 -6
  57. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +4 -2
  58. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +20 -3
  59. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -7
  60. package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -1
  61. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +467 -63
  62. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +3 -1
  63. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +0 -12
  64. package/dist/extensions/pi-subagents/src/shared/fork-session-cwd.ts +27 -0
  65. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +3 -0
  66. package/dist/extensions/pi-subagents/src/shared/types.ts +42 -4
  67. package/dist/extensions/pi-subagents/src/shared/utils.ts +18 -7
  68. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
  69. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +26 -12
  70. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +61 -2
  71. package/dist/extensions/pi-subagents/src/tui/fleet.ts +12 -7
  72. package/dist/extensions/pi-subagents/src/tui/render.ts +227 -14
  73. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +56 -34
  74. package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +77 -0
  75. package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +5 -3
  76. package/dist/extensions/pi-subagents/src/watchdog/guidance.ts +20 -0
  77. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +16 -13
  78. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +10 -9
  79. package/dist/extensions/pi-subagents/src/watchdog/render.ts +4 -5
  80. package/dist/extensions/pi-subagents/src/watchdog/review.ts +15 -4
  81. package/dist/extensions/pi-subagents/src/watchdog/rules.ts +70 -0
  82. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +75 -92
  83. package/dist/extensions/pi-subagents/src/watchdog/scope.ts +2 -1
  84. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +48 -104
  85. package/dist/extensions/pi-subagents/src/watchdog/types.ts +18 -32
  86. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +0 -1
  87. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -2
  88. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +439 -0
  89. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +28 -1
  90. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +335 -21
  91. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +4 -1
  92. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +1 -1
  93. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +22 -18
  94. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +0 -1
  95. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +21 -18
  96. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +307 -22
  97. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -0
  98. package/dist/extensions/pi-subagents/test/support/helpers.ts +25 -0
  99. package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +15 -4
  100. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -2
  101. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +96 -15
  102. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +53 -5
  103. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +20 -0
  104. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +221 -8
  105. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +45 -24
  106. package/dist/extensions/pi-subagents/test/unit/agent-scan-dirs.test.ts +133 -0
  107. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +28 -2
  108. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +1 -1
  109. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
  110. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +4 -1
  111. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +18 -4
  112. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +16 -0
  113. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +38 -0
  114. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
  115. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +21 -0
  116. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +19 -6
  117. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +26 -0
  118. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +22 -1
  119. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +2 -6
  120. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +66 -1
  121. package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +1 -1
  122. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -10
  123. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +136 -18
  124. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +20 -0
  125. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +22 -0
  126. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +1 -0
  127. package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +8 -0
  128. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +127 -48
  129. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -0
  130. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +17 -0
  131. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +88 -0
  132. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -2
  133. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +9 -9
  134. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -2
  135. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +9 -3
  136. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +22 -9
  137. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +91 -8
  138. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +27 -0
  139. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +50 -0
  140. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +0 -1
  141. package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +2 -2
  142. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +34 -0
  143. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -2
  144. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +73 -13
  145. package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +79 -0
  146. package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +2 -4
  147. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -8
  148. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +48 -3
  149. package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +37 -0
  150. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +36 -118
  151. package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +1 -1
  152. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +37 -14
  153. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +45 -26
  154. package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +223 -0
  155. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +50 -1
  156. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +22 -0
  157. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +136 -4
  158. package/dist/extensions/pi-zentui/README.md +19 -5
  159. package/dist/extensions/pi-zentui/docs/configuration.md +37 -9
  160. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +29 -0
  161. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +31 -2
  162. package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +55 -11
  163. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +115 -3
  164. package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +119 -2
  165. package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +1768 -0
  166. package/dist/extensions/pi-zentui/extensions/zentui/thinking-status.ts +69 -0
  167. package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +155 -0
  168. package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +2 -1
  169. package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +16 -2
  170. package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +1 -1
  171. package/dist/extensions/pi-zentui/test/config-load-lifecycle.test.ts +124 -0
  172. package/dist/extensions/pi-zentui/test/config.test.ts +82 -2
  173. package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +19 -4
  174. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +58 -9
  175. package/dist/extensions/pi-zentui/test/package-contents.mjs +33 -0
  176. package/dist/extensions/pi-zentui/test/prototype-patch-registry.test.ts +131 -0
  177. package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +4 -4
  178. package/dist/extensions/pi-zentui/test/settings-command.test.ts +289 -7
  179. package/dist/extensions/pi-zentui/test/settings-previews.test.ts +106 -0
  180. package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +51 -0
  181. package/dist/extensions/pi-zentui/test/thinking-experimental-loader-diagnostics.mjs +54 -0
  182. package/dist/extensions/pi-zentui/test/thinking-experimental-missing-export.test.ts +44 -0
  183. package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +297 -0
  184. package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +2329 -0
  185. package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +1668 -0
  186. package/dist/extensions/pi-zentui/test/thinking-status.test.ts +85 -0
  187. package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +122 -0
  188. package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +58 -0
  189. package/dist/skills/brandkit/SKILL.md +0 -2
  190. package/dist/skills/{industrial-brutalist-ui → brutalist}/SKILL.md +1 -3
  191. package/dist/skills/gpt-taste/SKILL.md +0 -2
  192. package/dist/skills/image-to-code/SKILL.md +0 -2
  193. package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -2
  194. package/dist/skills/imagegen-frontend-web/SKILL.md +0 -2
  195. package/dist/skills/{minimalist-ui → minimalist}/SKILL.md +1 -3
  196. package/dist/skills/{full-output-enforcement → output}/SKILL.md +1 -3
  197. package/dist/skills/{redesign-existing-projects → redesign}/SKILL.md +1 -3
  198. package/dist/skills/{high-end-visual-design → soft}/SKILL.md +1 -3
  199. package/dist/skills/stitch/DESIGN.md +121 -0
  200. package/dist/skills/{stitch-design-taste → stitch}/SKILL.md +1 -3
  201. package/dist/skills/{design-taste-frontend → taste}/SKILL.md +1 -3
  202. package/dist/skills/{design-taste-frontend-v1 → taste-v1}/SKILL.md +2 -4
  203. package/package.json +1 -1
@@ -28,6 +28,7 @@ import { normalizePublicSubagentExecution, validateWorkflowCapacityOverrides } f
28
28
  import { runSync } from "./execution.ts";
29
29
  import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
30
30
  import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
31
+ import { applyWatchdogLaunchRules } from "../../watchdog/rules.ts";
31
32
  import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, type ModelOrigin, type ParentModel } from "../shared/model-fallback.ts";
32
33
  import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
33
34
  import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
@@ -51,7 +52,7 @@ import { acquireActiveAsyncCapacity, ActiveAsyncCapacityError, getActiveAsyncCap
51
52
  import { isScheduledRunAction } from "../background/scheduled-runs.ts";
52
53
  import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
53
54
  import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
54
- import { normalizeGateAcceptance, validateExecutionAcceptance } from "../shared/acceptance.ts";
55
+ import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateExecutionAcceptance } from "../shared/acceptance.ts";
55
56
  import { canPreferFork, createForkContextResolver, forkedChildRequiresThinkingOff, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
56
57
  import { createPrunedForkSessionWriter } from "../../shared/pruned-fork.ts";
57
58
  import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
@@ -394,6 +395,7 @@ export interface SubagentParamsLike {
394
395
  agentContract?: AgentContract;
395
396
  at?: string;
396
397
  every?: string;
398
+ sessionOnly?: boolean;
397
399
  on?: string | number;
398
400
  timezone?: string;
399
401
  overlap?: "skip";
@@ -1314,6 +1316,9 @@ function appendStepToAsyncChain(input: {
1314
1316
  waitToolEnabled: input.deps.waitToolEnabled,
1315
1317
  waitToolDefaultTimeoutMs: input.deps.waitToolDefaultTimeoutMs,
1316
1318
  contextForAgent: contextPolicy.contextForAgent,
1319
+ worktreeBaseDir: input.deps.config.worktreeBaseDir,
1320
+ worktreeProvider: input.deps.config.worktreeProvider,
1321
+ worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
1317
1322
  asyncDir: resolved.location.asyncDir,
1318
1323
  validateOutputBindings: false,
1319
1324
  capabilityCeiling: intersectSubagentCapabilityCeilings(status.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(asyncCtx.currentSessionId)),
@@ -1687,6 +1692,8 @@ async function resumeExternalJobFollowUp(input: {
1687
1692
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
1688
1693
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
1689
1694
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
1695
+ worktreeProvider: input.deps.config.worktreeProvider,
1696
+ worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
1690
1697
  controlConfig: resolveControlConfig(input.deps.config.control, undefined),
1691
1698
  controlIntercomTarget: input.intercomBridge.active ? input.intercomBridge.orchestratorTarget : undefined,
1692
1699
  childIntercomTarget: input.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
@@ -1943,6 +1950,8 @@ async function resumeAsyncRun(input: {
1943
1950
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
1944
1951
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
1945
1952
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
1953
+ worktreeProvider: input.deps.config.worktreeProvider,
1954
+ worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
1946
1955
  controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
1947
1956
  controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
1948
1957
  childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
@@ -2056,9 +2065,13 @@ async function resumeAsyncRun(input: {
2056
2065
  worktreeSetupHook: input.deps.config.worktreeSetupHook,
2057
2066
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
2058
2067
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
2059
- worktree: input.params.worktree === true,
2068
+ worktreeProvider: input.deps.config.worktreeProvider,
2069
+ worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
2070
+ // A retained async child already owns the recorded worktree. Resume it in
2071
+ // place rather than allocating a second provider worktree around it.
2072
+ worktree: input.params.worktree === true && !("managedWorktree" in target && target.managedWorktree === true),
2060
2073
  lane: input.params.lane ?? recoveryDescriptor?.lane,
2061
- controlConfig: recoveryDescriptor?.controlConfig ?? resolveControlConfig(input.deps.config.control, input.params.control),
2074
+ controlConfig: resolveRevivalControlConfig({ globalConfig: input.deps.config.control, requestedControl: input.params.control, recoveryControlConfig: recoveryDescriptor?.controlConfig }),
2062
2075
  intercomBridge: input.params.intercomBridge ?? recoveryDescriptor?.intercomBridge,
2063
2076
  controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
2064
2077
  childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
@@ -3218,6 +3231,8 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3218
3231
  source: modelOrigin === "explicit" ? "explicit" : "inherited",
3219
3232
  });
3220
3233
  const modelOverrideFromParent = modelOrigin === "inherited";
3234
+ const launchRuleError = applyWatchdogLaunchRules({ cwd: effectiveCwd, agent: a.name, model: modelOverride ?? (parentModel && `${parentModel.provider}/${parentModel.id}`), warn: (violation) => deps.watchdog?.displayRuleWarning(violation) });
3235
+ if (launchRuleError) return toExecutionErrorResult(params, new Error(launchRuleError), data.contextPolicy.contextSummary);
3221
3236
  const asyncResult = executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
3222
3237
  agent: params.agent!,
3223
3238
  task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
@@ -3255,6 +3270,8 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3255
3270
  worktreeSetupHook: deps.config.worktreeSetupHook,
3256
3271
  worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
3257
3272
  worktreeBaseDir: deps.config.worktreeBaseDir,
3273
+ worktreeProvider: deps.config.worktreeProvider,
3274
+ worktreeBranchPrefix: deps.config.worktreeBranchPrefix,
3258
3275
  controlConfig,
3259
3276
  intercomBridge: params.intercomBridge,
3260
3277
  controlIntercomTarget,
@@ -3291,6 +3308,10 @@ function createSingleWorktreeSetup(
3291
3308
  setupHook: ExtensionConfig["worktreeSetupHook"],
3292
3309
  setupHookTimeoutMs: ExtensionConfig["worktreeSetupHookTimeoutMs"],
3293
3310
  baseDir: ExtensionConfig["worktreeBaseDir"],
3311
+ provider: ExtensionConfig["worktreeProvider"],
3312
+ branchPrefix: ExtensionConfig["worktreeBranchPrefix"],
3313
+ label?: string,
3314
+ task?: string,
3294
3315
  beforeCreate?: (setup: WorktreeSetup) => void,
3295
3316
  ): { setup?: WorktreeSetup; errorResult?: AgentToolResult<Details> } {
3296
3317
  if (!enabled) return {};
@@ -3302,6 +3323,10 @@ function createSingleWorktreeSetup(
3302
3323
  ? { hookPath: setupHook, ...(setupHookTimeoutMs === undefined ? {} : { timeoutMs: setupHookTimeoutMs }) }
3303
3324
  : undefined,
3304
3325
  baseDir,
3326
+ provider,
3327
+ branchPrefix,
3328
+ labels: [label],
3329
+ tasks: [task],
3305
3330
  beforeCreate,
3306
3331
  })),
3307
3332
  };
@@ -3643,6 +3668,8 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3643
3668
  },
3644
3669
  );
3645
3670
  const modelOverrideFromParent = modelOrigin === "inherited";
3671
+ const launchRuleError = applyWatchdogLaunchRules({ cwd: effectiveCwd, agent: agentConfig.name, model: modelOverride ?? (parentModel && `${parentModel.provider}/${parentModel.id}`), warn: (violation) => deps.watchdog?.displayRuleWarning(violation) });
3672
+ if (launchRuleError) return toExecutionErrorResult(params, new Error(launchRuleError), data.contextPolicy.contextSummary);
3646
3673
  let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
3647
3674
  let readsOverride: string[] | false | undefined = params.reads;
3648
3675
  const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
@@ -3662,6 +3689,10 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3662
3689
  deps.config.worktreeSetupHook,
3663
3690
  deps.config.worktreeSetupHookTimeoutMs,
3664
3691
  deps.config.worktreeBaseDir,
3692
+ deps.config.worktreeProvider,
3693
+ deps.config.worktreeBranchPrefix,
3694
+ params.lane?.key ?? params.workflowKey,
3695
+ task,
3665
3696
  (plannedSetup) => {
3666
3697
  pendingHandoff = writePendingParallelHandoff({
3667
3698
  manifestPath: parallelHandoffPath(artifactsDir, runId),
@@ -3691,7 +3722,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3691
3722
  return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [] } };
3692
3723
  }
3693
3724
  const structuredRuntime = params.outputSchema
3694
- ? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined, { captureAcceptanceReport: params.acceptance !== false })
3725
+ ? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined, { acceptanceReport: resolveAcceptanceReportMode(params.acceptance) })
3695
3726
  : undefined;
3696
3727
  // Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
3697
3728
  // absolute paths pass through; relative paths resolve against the child cwd.
@@ -4164,6 +4195,10 @@ function workflowOutputPathMappingSummary(children: WorkflowScriptChildResult[])
4164
4195
  return mappings.length > 0 ? ` Output path mappings: ${mappings.join("; ")}.` : "";
4165
4196
  }
4166
4197
 
4198
+ function workflowDetailsResults(children: WorkflowScriptChildResult[]): SingleResult[] {
4199
+ return children.flatMap((child) => (child.results ?? []).map((result) => result.workflowKey ? result : { ...result, workflowKey: child.key }));
4200
+ }
4201
+
4167
4202
  function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): WorkflowSteerResult {
4168
4203
  const steering = result.details.steering;
4169
4204
  const error = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n") || undefined;
@@ -4409,6 +4444,7 @@ export function prepareWorkflowLaunchParams(
4409
4444
  if (output !== undefined && typeof output !== "string" && typeof output !== "boolean") throw new Error("output must be a path string or boolean.");
4410
4445
  const outputMode = Object.hasOwn(childParams, "outputMode") ? childParams.outputMode : workflowDefaults.outputMode;
4411
4446
  if (outputMode !== undefined && outputMode !== "inline" && outputMode !== "file-only") throw new Error("outputMode must be 'inline' or 'file-only'.");
4447
+ const control = mergeWorkflowControlOverrides(workflowDefaults.control, childParams.control as ControlConfig | undefined);
4412
4448
  return {
4413
4449
  action: "resume",
4414
4450
  id: childParams.resume.trim(),
@@ -4427,14 +4463,17 @@ export function prepareWorkflowLaunchParams(
4427
4463
  ...(options.missionDetached ? { mission: false } : {}),
4428
4464
  ...(timeoutMs !== undefined ? { timeoutMs: timeoutMs as number } : {}),
4429
4465
  ...(toolBudget !== undefined ? { toolBudget: toolBudget as ToolBudgetConfig } : {}),
4466
+ ...(control !== undefined ? { control } : {}),
4430
4467
  ...(intercomBridge !== undefined ? { intercomBridge: intercomBridge as IntercomBridgeConfig } : {}),
4431
4468
  ...(capabilityCeiling ? { capabilityCeiling } : {}),
4432
4469
  };
4433
4470
  }
4471
+ const control = mergeWorkflowControlOverrides(workflowDefaults.control, childParams.control as ControlConfig | undefined);
4434
4472
  const launchParams = {
4435
4473
  ...workflowDefaults,
4436
4474
  async: options.externalAsyncRequired === true && childParams.async === undefined && workflowDefaults.async === undefined ? true : false,
4437
4475
  ...childParams,
4476
+ ...(control !== undefined ? { control } : {}),
4438
4477
  ...(options.externalAsyncRequired === true && childParams.async === undefined && workflowDefaults.async === undefined ? { workflowAwaitAsync: true } : {}),
4439
4478
  ...(options.missionDetached ? { mission: false } : {}),
4440
4479
  workflowParentRunId: parentWorkflowRunId,
@@ -4453,6 +4492,17 @@ export function prepareWorkflowLaunchParams(
4453
4492
  return normalizedGate.params;
4454
4493
  }
4455
4494
 
4495
+ function mergeWorkflowControlOverrides(workflowControl: ControlConfig | undefined, childControl: ControlConfig | undefined): ControlConfig | undefined {
4496
+ if (childControl === undefined) return workflowControl;
4497
+ if (workflowControl === undefined) return childControl;
4498
+ return { ...workflowControl, ...childControl };
4499
+ }
4500
+
4501
+ export function resolveRevivalControlConfig(input: { globalConfig?: ControlConfig; requestedControl?: ControlConfig; recoveryControlConfig?: ResolvedControlConfig }): ResolvedControlConfig {
4502
+ if (input.requestedControl === undefined) return input.recoveryControlConfig ?? resolveControlConfig(input.globalConfig, undefined);
4503
+ return resolveControlConfig(input.recoveryControlConfig ?? input.globalConfig, input.requestedControl);
4504
+ }
4505
+
4456
4506
  type GateParamsNormalizationResult =
4457
4507
  | { ok: true; params: SubagentParamsLike }
4458
4508
  | { ok: false; error: string };
@@ -5519,7 +5569,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5519
5569
  const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
5520
5570
  return attachWorkflowMission(withRunFanoutBudget({
5521
5571
  content: [{ type: "text", text: displayText }],
5522
- details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5572
+ details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflowDetailsResults(workflow.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5523
5573
  }, workflowFanoutBudget));
5524
5574
  } catch (error) {
5525
5575
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
@@ -5546,7 +5596,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5546
5596
  return attachWorkflowMission(withRunFanoutBudget({
5547
5597
  content: [{ type: "text", text: displayText }],
5548
5598
  isError: true,
5549
- details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5599
+ details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflowDetailsResults(partial.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5550
5600
  }, workflowFanoutBudget));
5551
5601
  }
5552
5602
  }
@@ -6247,6 +6297,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6247
6297
  const canonicalParams = canonicalizeExecutionParams(effectiveParams, discoveredAgents, discovered.agentDiagnostics, unknownAgentDiagnosticContext);
6248
6298
  if (canonicalParams.error) return buildRequestedModeError(effectiveParams, canonicalParams.error);
6249
6299
  effectiveParams = canonicalParams.params!;
6300
+ if (effectiveParams.worktree === undefined && deps.config.worktree !== undefined) {
6301
+ effectiveParams = { ...effectiveParams, worktree: deps.config.worktree };
6302
+ }
6250
6303
  const modelScope = discovered.modelScope;
6251
6304
  effectiveParams = applySingleAgentLaunchDefaults(effectiveParams, discoveredAgents);
6252
6305
  // An agent-level defaultContext is a preference, unlike an explicit request.
@@ -21,7 +21,9 @@ import type {
21
21
  ResolvedAcceptanceGate,
22
22
  SingleResult,
23
23
  SubagentRunMode,
24
+ ChildWatchdogProgress,
24
25
  } from "../../shared/types.ts";
26
+ import { unresolvedChildWatchdogBlockers } from "../../watchdog/child-status.ts";
25
27
  import { isAgentContractV1 } from "./agent-contract.ts";
26
28
  import { classifyTaskMutationIntent, stripSeverityCompounds, taskMayMutate } from "./task-intent.ts";
27
29
 
@@ -47,7 +49,7 @@ const VALID_EVIDENCE_KINDS: AcceptanceEvidenceKind[] = [
47
49
  const VALID_EVIDENCE = new Set<AcceptanceEvidenceKind>(VALID_EVIDENCE_KINDS);
48
50
  const ACCEPTANCE_EVIDENCE_HELP = `Supported evidence kinds: ${VALID_EVIDENCE_KINDS.join(", ")}. Example: { level: "checked", evidence: ["commands-run", "changed-files"] }.`;
49
51
  const ACCEPTANCE_OBJECT_EXAMPLE = "Example: { level: \"checked\", evidence: [\"commands-run\", \"changed-files\"] }.";
50
- const ACCEPTANCE_CONFIG_KEYS = new Set(["level", "criteria", "evidence", "verify", "review", "stopRules", "reason"]);
52
+ const ACCEPTANCE_CONFIG_KEYS = new Set(["level", "report", "criteria", "evidence", "verify", "review", "stopRules", "reason"]);
51
53
  const ACCEPTANCE_GATE_KEYS = new Set(["id", "must", "evidence", "severity"]);
52
54
  const ACCEPTANCE_VERIFY_KEYS = new Set(["id", "command", "timeoutMs", "cwd", "env", "allowFailure"]);
53
55
  const ACCEPTANCE_REVIEW_KEYS = new Set(["agent", "focus", "required"]);
@@ -100,12 +102,13 @@ function inferLevel(input: {
100
102
  const writeTask = taskMayWrite
101
103
  || (input.acceptanceRole === "writer" && !readOnlyTask)
102
104
  || (input.acceptanceRole === undefined && /\bworker\b/.test(agent) && !readOnlyTask);
103
- const inferredReadOnly = readOnlyTask || (input.acceptanceRole === "read-only" && !taskMayWrite);
105
+ const inferredReadOnly = readOnlyTask || ((readOnlyAgent || input.acceptanceRole === "read-only") && !taskMayWrite);
104
106
  const roleResolvesReadOnly = input.acceptanceRole !== undefined && inferredReadOnly;
107
+ const dynamicResolvesReadOnly = inferredReadOnly && !writeTask;
105
108
  const keywordRiskReadOnly = input.acceptanceRole === undefined ? intent.kind === "read-only" : inferredReadOnly;
106
109
  const risky = Boolean(input.async && writeTask)
107
- || (Boolean(input.dynamic) && !roleResolvesReadOnly)
108
- || (Boolean(input.dynamicGroup) && !roleResolvesReadOnly)
110
+ || (Boolean(input.dynamic) && !roleResolvesReadOnly && !dynamicResolvesReadOnly)
111
+ || (Boolean(input.dynamicGroup) && !roleResolvesReadOnly && !dynamicResolvesReadOnly)
109
112
  || (!keywordRiskReadOnly && /\b(?:release|migration|migrate|security|data[- ]loss|destructive|post-review|fix pass)\b/.test(task));
110
113
 
111
114
  if (risky) {
@@ -131,7 +134,7 @@ function inferLevel(input: {
131
134
  if (readOnlyAgent || readOnlyTask) {
132
135
  reasons.push(input.acceptanceRole === "read-only" && !readOnlyTask ? "declared read-only acceptance role" : readOnlyAgent ? "read-only/reviewer-style agent" : "read-only task wording");
133
136
  return {
134
- level: "attested",
137
+ level: "none",
135
138
  reasons,
136
139
  criteria: ["Return concrete findings with file paths and severity when applicable"],
137
140
  evidence: ["review-findings", "residual-risks"],
@@ -146,11 +149,46 @@ function inferLevel(input: {
146
149
  };
147
150
  }
148
151
 
149
- export function normalizeAcceptanceInput(input: AcceptanceInput | undefined): AcceptanceConfig {
152
+ type AcceptanceInputNormalizationResult = { value: unknown; error?: string };
153
+
154
+ function normalizeAcceptanceValue(input: unknown, pathLabel = "acceptance"): AcceptanceInputNormalizationResult {
155
+ if (typeof input !== "string") return { value: input };
156
+ const trimmed = input.trim();
157
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return { value: input };
158
+ let parsed: unknown;
159
+ try {
160
+ parsed = JSON.parse(trimmed);
161
+ } catch (error) {
162
+ return {
163
+ value: input,
164
+ error: `${pathLabel} JSON string must encode a valid acceptance object: ${error instanceof Error ? error.message : String(error)}`,
165
+ };
166
+ }
167
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
168
+ const kind = parsed === null ? "null" : Array.isArray(parsed) ? "an array" : typeof parsed;
169
+ return { value: input, error: `${pathLabel} JSON string must encode an object; got ${kind}.` };
170
+ }
171
+ return { value: parsed };
172
+ }
173
+
174
+ export function normalizeAcceptanceInput(input: unknown): AcceptanceConfig {
175
+ const normalized = normalizeAcceptanceValue(input);
176
+ if (normalized.error) throw new Error(normalized.error);
177
+ input = normalized.value;
150
178
  if (input === undefined || input === "auto") return { level: "auto" };
151
179
  if (input === false) return { level: "none", reason: "disabled by deprecated false shorthand" };
152
- if (typeof input === "string") return { level: input };
153
- return { ...input };
180
+ if (typeof input === "string") return { level: input as AcceptanceConfig["level"] };
181
+ return { ...(input as AcceptanceConfig) };
182
+ }
183
+
184
+ export type ResolvedAcceptanceReportMode = "off" | "optional" | "required";
185
+
186
+ export function resolveAcceptanceReportMode(input: unknown): ResolvedAcceptanceReportMode {
187
+ const normalized = normalizeAcceptanceValue(input);
188
+ if (normalized.error) throw new Error(normalized.error);
189
+ const value = normalized.value;
190
+ const report = value && typeof value === "object" && !Array.isArray(value) ? (value as AcceptanceConfig).report : undefined;
191
+ return value === false || report === "off" ? "off" : report === "on" ? "required" : "optional";
154
192
  }
155
193
 
156
194
  type GateAcceptanceNormalizationResult =
@@ -158,7 +196,11 @@ type GateAcceptanceNormalizationResult =
158
196
  | { ok: false; error: string };
159
197
 
160
198
  export function normalizeGateAcceptance(gate: unknown, acceptance: AcceptanceInput | undefined): GateAcceptanceNormalizationResult {
161
- if (gate === undefined) return acceptance === undefined ? { ok: true } : { ok: true, acceptance };
199
+ if (gate === undefined) {
200
+ if (acceptance === undefined) return { ok: true };
201
+ const normalized = normalizeAcceptanceValue(acceptance);
202
+ return normalized.error ? { ok: false, error: normalized.error } : { ok: true, acceptance: normalized.value as AcceptanceInput };
203
+ }
162
204
  if (typeof gate !== "string" || !gate.trim()) return { ok: false, error: "gate must be a non-empty command string." };
163
205
  if (acceptance !== undefined) return { ok: false, error: "gate cannot be combined with acceptance; use one gate command or acceptance.verify." };
164
206
  return { ok: true, acceptance: { level: "verified", verify: [{ id: "gate", command: gate.trim() }] } };
@@ -168,6 +210,10 @@ function explicitAcceptanceCanDisable(explicit: AcceptanceConfig): boolean {
168
210
  return explicit.level === "none" && typeof explicit.reason === "string" && explicit.reason.trim().length > 0;
169
211
  }
170
212
 
213
+ function explicitAcceptanceRequestsPolicy(explicit: AcceptanceConfig): boolean {
214
+ return (explicit.level !== undefined && explicit.level !== "auto") || Object.keys(explicit).some((key) => key !== "level");
215
+ }
216
+
171
217
  function unsupportedEvidenceKindMessage(pathLabel: string, item: unknown): string {
172
218
  const value = typeof item === "string" ? ` "${item}"` : "";
173
219
  return `${pathLabel}${value} is not a supported evidence kind. ${ACCEPTANCE_EVIDENCE_HELP}`;
@@ -177,6 +223,9 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
177
223
  const errors: string[] = [];
178
224
  if (input === undefined) return errors;
179
225
  if (input === false) return errors;
226
+ const normalized = normalizeAcceptanceValue(input, pathLabel);
227
+ if (normalized.error) return [normalized.error];
228
+ input = normalized.value;
180
229
  if (typeof input === "string") {
181
230
  if (input === "reviewed") errors.push(`${pathLabel} ${EXPLICIT_REVIEWED_UNAVAILABLE}`);
182
231
  else if (!VALID_LEVELS.has(input as AcceptanceLevel)) errors.push(`${pathLabel} has invalid level '${input}'.`);
@@ -197,6 +246,9 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
197
246
  } else if (value.level !== undefined && (typeof value.level !== "string" || !VALID_LEVELS.has(value.level as AcceptanceLevel))) {
198
247
  errors.push(`${pathLabel}.level must be one of auto, none, attested, checked, verified.`);
199
248
  }
249
+ if (value.report !== undefined && value.report !== "on" && value.report !== "off") {
250
+ errors.push(`${pathLabel}.report must be on or off.`);
251
+ }
200
252
  if (value.level === "none" && (typeof value.reason !== "string" || !value.reason.trim())) {
201
253
  errors.push(`${pathLabel}.reason is required when level is none.`);
202
254
  }
@@ -304,29 +356,45 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
304
356
 
305
357
  export function validateExecutionAcceptance(input: {
306
358
  acceptance?: unknown;
307
- tasks?: Array<{ acceptance?: unknown }>;
359
+ outputSchema?: unknown;
360
+ tasks?: Array<{ acceptance?: unknown; outputSchema?: unknown }>;
308
361
  chain?: Array<{
309
362
  acceptance?: unknown;
310
- parallel?: Array<{ acceptance?: unknown }> | { acceptance?: unknown };
363
+ outputSchema?: unknown;
364
+ parallel?: Array<{ acceptance?: unknown; outputSchema?: unknown }> | { acceptance?: unknown; outputSchema?: unknown };
311
365
  }>;
312
366
  }): string[] {
313
367
  const errors = validateAcceptanceInput(input.acceptance, "acceptance");
368
+ errors.push(...validateAcceptanceReportMode(input.acceptance, input.outputSchema, "acceptance"));
314
369
  for (const [index, task] of (input.tasks ?? []).entries()) {
315
370
  errors.push(...validateAcceptanceInput(task.acceptance, `tasks[${index}].acceptance`));
371
+ errors.push(...validateAcceptanceReportMode(task.acceptance, task.outputSchema, `tasks[${index}].acceptance`));
316
372
  }
317
373
  for (const [stepIndex, step] of (input.chain ?? []).entries()) {
318
374
  errors.push(...validateAcceptanceInput(step.acceptance, `chain[${stepIndex}].acceptance`));
375
+ errors.push(...validateAcceptanceReportMode(step.acceptance, step.outputSchema, `chain[${stepIndex}].acceptance`));
319
376
  if (Array.isArray(step.parallel)) {
320
377
  for (const [taskIndex, task] of step.parallel.entries()) {
321
378
  errors.push(...validateAcceptanceInput(task.acceptance, `chain[${stepIndex}].parallel[${taskIndex}].acceptance`));
379
+ errors.push(...validateAcceptanceReportMode(task.acceptance, task.outputSchema, `chain[${stepIndex}].parallel[${taskIndex}].acceptance`));
322
380
  }
323
381
  } else if (step.parallel) {
324
382
  errors.push(...validateAcceptanceInput(step.parallel.acceptance, `chain[${stepIndex}].parallel.acceptance`));
383
+ errors.push(...validateAcceptanceReportMode(step.parallel.acceptance, step.parallel.outputSchema, `chain[${stepIndex}].parallel.acceptance`));
325
384
  }
326
385
  }
327
386
  return errors;
328
387
  }
329
388
 
389
+ function validateAcceptanceReportMode(acceptance: unknown, outputSchema: unknown, pathLabel: string): string[] {
390
+ const normalized = normalizeAcceptanceValue(acceptance, pathLabel);
391
+ if (normalized.error) return [];
392
+ acceptance = normalized.value;
393
+ if (!acceptance || typeof acceptance !== "object" || Array.isArray(acceptance)) return [];
394
+ if (!("report" in acceptance)) return [];
395
+ return outputSchema === undefined ? [`${pathLabel}.report requires outputSchema.`] : [];
396
+ }
397
+
330
398
  function normalizeCriteria(criteria: Array<string | { id?: string; must?: string; evidence?: AcceptanceEvidenceKind[]; severity?: "required" | "recommended" }> | undefined, evidence: AcceptanceEvidenceKind[]): ResolvedAcceptanceGate[] {
331
399
  return (criteria ?? []).map((criterion, index): ResolvedAcceptanceGate => {
332
400
  if (typeof criterion === "string") {
@@ -376,12 +444,13 @@ export function resolveEffectiveAcceptance(input: {
376
444
  };
377
445
  }
378
446
  const inferred = inferLevel(input);
447
+ const inferredLevel = inferred.level === "none" && explicitAcceptanceRequestsPolicy(explicit) ? "attested" : inferred.level;
379
448
  const level = explicitAcceptanceCanDisable(explicit)
380
449
  ? "none"
381
450
  : explicitLevel === "auto"
382
- ? inferred.level
383
- : (LEVEL_RANK[explicitLevel] >= LEVEL_RANK[inferred.level] ? explicitLevel : inferred.level);
384
- const evidence = unique([...(level === inferred.level ? inferred.evidence : requiredEvidenceForLevel(level)), ...(explicit.evidence ?? [])]);
451
+ ? inferredLevel
452
+ : (LEVEL_RANK[explicitLevel] >= LEVEL_RANK[inferredLevel] ? explicitLevel : inferredLevel);
453
+ const evidence = unique([...(level === inferredLevel ? inferred.evidence : requiredEvidenceForLevel(level)), ...(explicit.evidence ?? [])]);
385
454
  const criteria = normalizeCriteria(
386
455
  (explicit.criteria?.length ? explicit.criteria : inferred.criteria) as Array<string | { id?: string; must?: string; evidence?: AcceptanceEvidenceKind[]; severity?: "required" | "recommended" }>,
387
456
  evidence,
@@ -391,8 +460,8 @@ export function resolveEffectiveAcceptance(input: {
391
460
  level,
392
461
  explicit: input.explicit !== undefined,
393
462
  inferredReason: inferred.reasons,
394
- criteria,
395
- evidence,
463
+ criteria: level === "none" ? [] : criteria,
464
+ evidence: level === "none" ? [] : evidence,
396
465
  verify: explicit.verify ?? [],
397
466
  review,
398
467
  stopRules: explicit.stopRules ?? [],
@@ -851,7 +920,7 @@ function validateStringArrayField(errors: string[], value: unknown, pathLabel: s
851
920
  }
852
921
  }
853
922
 
854
- function validateAcceptanceReport(value: unknown, pathLabel = ""): { report?: AcceptanceReport; errors: string[] } {
923
+ export function validateAcceptanceReport(value: unknown, pathLabel = ""): { report?: AcceptanceReport; errors: string[] } {
855
924
  const normalized = normalizeAcceptanceReportValue(value, pathLabel);
856
925
  value = normalized.value;
857
926
  pathLabel = normalized.pathLabel;
@@ -1293,6 +1362,7 @@ export async function evaluateAcceptance(input: {
1293
1362
  reportOptional?: boolean;
1294
1363
  artifactsDir?: string;
1295
1364
  runId?: string;
1365
+ watchdog?: ChildWatchdogProgress;
1296
1366
  }): Promise<AcceptanceLedger> {
1297
1367
  const acceptance = input.acceptance;
1298
1368
  const initialStatus = acceptance.level === "none" ? "not-required" : "claimed";
@@ -1308,9 +1378,16 @@ export async function evaluateAcceptance(input: {
1308
1378
  };
1309
1379
  if (acceptance.level === "none") return ledger;
1310
1380
 
1381
+ if (input.watchdog) {
1382
+ const unresolved = unresolvedChildWatchdogBlockers(input.watchdog);
1383
+ ledger.runtimeChecks.push(unresolved.length
1384
+ ? { id: "watchdog-blocker", status: "failed", message: `Unresolved watchdog blocker: ${unresolved[0]!.summary}` }
1385
+ : { id: "watchdog-blocker", status: "passed", message: "No unresolved watchdog blockers." });
1386
+ }
1387
+
1311
1388
  const parsed: AcceptanceReportParseResult = input.reportError
1312
1389
  ? { error: input.reportError }
1313
- : input.report
1390
+ : input.report !== undefined
1314
1391
  ? (() => {
1315
1392
  const validation = validateAcceptanceReport(input.report);
1316
1393
  return validation.report
@@ -2,6 +2,7 @@ import { sanitizeDisplayText, truncateDisplayText } from "../../shared/display-t
2
2
  import { formatModelThinking } from "../../shared/formatters.ts";
3
3
  import type { AsyncJobState, AsyncJobStep, HostStepFreshnessV1, HostStepMonitorKind, HostStepNodeV1, HostStepState, HostStepVerdict, NestedRunSummary, NestedStepSummary, SubagentRunMode, WorkflowGraphSnapshot, WorkflowPreflightLaneV1, WorkflowPreflightV1 } from "../../shared/types.ts";
4
4
  import { HOST_STEP_MAX_COUNT, HOST_STEP_MAX_DETAIL_CHARS, HOST_STEP_MAX_LABEL_CHARS, HOST_STEP_MAX_PROVIDER_CHARS, HOST_STEP_MAX_REASON_CHARS, HOST_STEP_MAX_REF_CHARS, HOST_STEP_MAX_ROLE_CHARS, HOST_STEP_MAX_TARGET_CHARS, hostStepReportName, parseHostStepNode, validHostStepNodes } from "./host-step-status.ts";
5
+ import { workflowPreflightLaneForRuntimeKey } from "../../workflows/workflow-preflight.ts";
5
6
  import { workflowGraphStageNodes } from "./workflow-graph.ts";
6
7
 
7
8
  export const ASYNC_STATUS_SNAPSHOT_KIND = "pi-subagents.async-status-snapshot";
@@ -472,7 +473,7 @@ function projectWorkflowGraphRow(node: WorkflowGraphSnapshot["nodes"][number], p
472
473
  };
473
474
  }
474
475
 
475
- /** Project loaded workflow child facts plus stored preflight hints into compact rows. */
476
+ /** Project authoritative workflow facts into compact rows, annotated by preflight hints. */
476
477
  export function projectAsyncWorkflowRows(
477
478
  steps: readonly AsyncJobStep[] | undefined,
478
479
  hostStepsOrPreflight?: readonly HostStepNodeV1[] | WorkflowGraphSnapshot | WorkflowPreflightV1,
@@ -482,7 +483,8 @@ export function projectAsyncWorkflowRows(
482
483
  const graph = isWorkflowGraph(hostStepsOrPreflight) ? hostStepsOrPreflight : undefined;
483
484
  const hostSteps = isWorkflowPreflight(hostStepsOrPreflight) || graph ? undefined : hostStepsOrPreflight;
484
485
  const loaded = steps ?? [];
485
- const declared = new Map<string, WorkflowPreflightLaneV1>();
486
+ const preflightForKey = (key: string, groupKeys: readonly (string | undefined)[] = []): WorkflowPreflightLaneV1 | undefined =>
487
+ workflowPreflightLaneForRuntimeKey(preflight, key, groupKeys);
486
488
  if (graph) {
487
489
  const loadedIndexesByKey = new Map<string, number[]>();
488
490
  for (const [index, step] of loaded.entries()) {
@@ -493,7 +495,6 @@ export function projectAsyncWorkflowRows(
493
495
  }
494
496
  const consumed = new Set<number>();
495
497
  const childRows: AsyncStatusWorkflowRow[] = [];
496
- for (const lane of preflight?.lanes ?? []) declared.set(lane.key, lane);
497
498
  const graphStages = workflowGraphStageNodes(graph);
498
499
  const graphKeys = new Set(graphStages.map((node) => node.id));
499
500
  const graphPhaseByNodeId = new Map<string, string>();
@@ -502,60 +503,27 @@ export function projectAsyncWorkflowRows(
502
503
  if (graphKeys.has(nodeId) && !graphPhaseByNodeId.has(nodeId)) graphPhaseByNodeId.set(nodeId, phase.title);
503
504
  }
504
505
  }
505
- const graphLaneKeys = new Set(graphPhaseByNodeId.values());
506
- const preflightForNode = (node: WorkflowGraphSnapshot["nodes"][number]): WorkflowPreflightLaneV1 | undefined => {
507
- const phaseTitle = graphPhaseByNodeId.get(node.id);
508
- return declared.get(node.id) ?? (node.phase ? declared.get(node.phase) : undefined) ?? (phaseTitle ? declared.get(phaseTitle) : undefined);
509
- };
510
506
  for (const node of graphStages) {
507
+ const lane = preflightForKey(node.id, [node.phase, graphPhaseByNodeId.get(node.id)]);
511
508
  const indexes = (loadedIndexesByKey.get(node.id) ?? []).filter((index) => !consumed.has(index));
512
- if (indexes.length > 0) {
513
- for (const index of indexes) {
514
- consumed.add(index);
515
- childRows.push(projectLoadedWorkflowRow(loaded[index]!, index, preflightForNode(node)));
516
- }
517
- } else {
518
- childRows.push(projectWorkflowGraphRow(node, preflightForNode(node)));
519
- }
520
- }
521
- for (const lane of preflight?.lanes ?? []) {
522
- if (graphKeys.has(lane.key) || graphLaneKeys.has(lane.key)) continue;
523
- const indexes = (loadedIndexesByKey.get(lane.key) ?? []).filter((index) => !consumed.has(index));
524
509
  if (indexes.length > 0) {
525
510
  for (const index of indexes) {
526
511
  consumed.add(index);
527
512
  childRows.push(projectLoadedWorkflowRow(loaded[index]!, index, lane));
528
513
  }
529
514
  } else {
530
- childRows.push({ name: lane.key, state: "planned", preflight: lane });
515
+ childRows.push(projectWorkflowGraphRow(node, lane));
531
516
  }
532
517
  }
533
518
  for (const [index, step] of loaded.entries()) {
534
- if (!consumed.has(index)) childRows.push(projectLoadedWorkflowRow(step, index, step.workflowKey ? declared.get(step.workflowKey) : undefined));
519
+ if (!consumed.has(index)) childRows.push(projectLoadedWorkflowRow(step, index, step.workflowKey ? preflightForKey(step.workflowKey, [step.phase]) : undefined));
535
520
  }
536
521
  return [...childRows, ...validHostStepList(graph).map(hostStepRow)];
537
522
  }
538
- const loadedIndexByKey = new Map<string, number>();
539
- for (const [index, step] of loaded.entries()) {
540
- if (step.workflowKey !== undefined && !loadedIndexByKey.has(step.workflowKey)) loadedIndexByKey.set(step.workflowKey, index);
541
- }
542
- const consumed = new Set<number>();
543
- const childRows: AsyncStatusWorkflowRow[] = [];
544
- for (const lane of preflight?.lanes ?? []) {
545
- declared.set(lane.key, lane);
546
- const index = loadedIndexByKey.get(lane.key);
547
- if (index === undefined) {
548
- childRows.push({ name: lane.key, state: "planned", preflight: lane });
549
- continue;
550
- }
551
- consumed.add(index);
552
- childRows.push(projectLoadedWorkflowRow(loaded[index]!, index, lane));
553
- }
554
- for (const [index, step] of loaded.entries()) {
555
- if (consumed.has(index)) continue;
556
- childRows.push(projectLoadedWorkflowRow(step, index, step.workflowKey ? declared.get(step.workflowKey) : undefined));
557
- }
558
- return [...childRows, ...validHostStepList(hostSteps).map(hostStepRow)];
523
+ return [
524
+ ...loaded.map((step, index) => projectLoadedWorkflowRow(step, index, step.workflowKey ? preflightForKey(step.workflowKey, [step.phase]) : undefined)),
525
+ ...validHostStepList(hostSteps).map(hostStepRow),
526
+ ];
559
527
  }
560
528
 
561
529
  function projectLoadedWorkflowRow(step: AsyncJobStep, index: number, preflight?: WorkflowPreflightLaneV1): AsyncStatusWorkflowRow {
@@ -22,6 +22,7 @@ export interface SubagentCapabilityAudit {
22
22
  requestedTools?: string[];
23
23
  effectiveTools: string[];
24
24
  removedTools: string[];
25
+ excludeTools?: string[];
25
26
  internalTools: string[];
26
27
  extensionsDenied: boolean;
27
28
  removedExtensionCount: number;
@@ -50,7 +50,7 @@ const DYNAMIC_PARALLEL_KEYS = new Set(["agent", "task", "phase", "label", "outpu
50
50
  const RUNNER_DYNAMIC_PARALLEL_KEYS = new Set([
51
51
  ...DYNAMIC_PARALLEL_KEYS,
52
52
  "outputName", "structured", "inheritProjectContext", "inheritGlobalContext", "inheritSkills", "skills", "outputPath", "namespaceOutputPath", "maxSubagentDepth", "timeoutMs", "waitToolEnabled", "waitToolDefaultTimeoutMs",
53
- "structuredOutput", "structuredOutputSchema", "tools", "allowNestedSubagents", "extensions", "subagentOnlyExtensions", "mcpDirectTools", "mutationTools", "capabilityCeiling", "completionGuard", "systemPrompt",
53
+ "structuredOutput", "structuredOutputSchema", "tools", "excludeTools", "allowNestedSubagents", "extensions", "subagentOnlyExtensions", "mcpDirectTools", "mutationTools", "capabilityCeiling", "completionGuard", "systemPrompt",
54
54
  "systemPromptMode", "thinking", "modelCandidates", "sessionFile", "effectiveAcceptance", "acceptanceInput", "acceptanceRole", "parentSessionId", "launchResolvedExtensions", "requestedCwd",
55
55
  ]);
56
56
  const DYNAMIC_COLLECT_KEYS = new Set(["as", "outputSchema"]);