pi-subagents 0.76.0 → 0.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -0
- package/README.md +1 -1
- package/agents/oracle.md +8 -19
- package/agents/researcher.md +0 -3
- package/agents/reviewer.md +17 -61
- package/agents/scout.md +1 -4
- package/agents/worker.md +8 -25
- package/docs/agents.md +70 -3
- package/docs/configuration.md +55 -6
- package/docs/extension-api.md +36 -11
- package/docs/missions.md +24 -17
- package/docs/models.md +1 -1
- package/docs/observability.md +10 -5
- package/docs/tool-reference.md +101 -83
- package/docs/watchdog.md +8 -6
- package/docs/workflows.md +15 -6
- package/index.d.ts.map +1 -1
- package/index.js +5 -0
- package/index.js.map +1 -1
- package/package.json +2 -1
- package/prompts/council.md +1 -1
- package/skills/pi-subagents/SKILL.md +1 -1
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +16 -16
- package/skills/pi-subagents/references/management-authoring-rpc.md +16 -12
- package/skills/pi-subagents/references/prompting-and-roles.md +3 -3
- package/src/agents/advertised-agent-prompt.d.ts +5 -1
- package/src/agents/advertised-agent-prompt.d.ts.map +1 -1
- package/src/agents/advertised-agent-prompt.js +8 -2
- package/src/agents/advertised-agent-prompt.js.map +1 -1
- package/src/agents/agent-management.d.ts.map +1 -1
- package/src/agents/agent-management.js +5 -0
- package/src/agents/agent-management.js.map +1 -1
- package/src/agents/agent-scope.d.ts +1 -0
- package/src/agents/agent-scope.d.ts.map +1 -1
- package/src/agents/agent-scope.js +3 -0
- package/src/agents/agent-scope.js.map +1 -1
- package/src/agents/agent-serializer.d.ts.map +1 -1
- package/src/agents/agent-serializer.js +3 -0
- package/src/agents/agent-serializer.js.map +1 -1
- package/src/agents/agents.d.ts +14 -2
- package/src/agents/agents.d.ts.map +1 -1
- package/src/agents/agents.js +94 -36
- package/src/agents/agents.js.map +1 -1
- package/src/api/preflight.d.ts +2 -0
- package/src/api/preflight.d.ts.map +1 -1
- package/src/api/preflight.js +6 -2
- package/src/api/preflight.js.map +1 -1
- package/src/extension/config.d.ts.map +1 -1
- package/src/extension/config.js +23 -2
- package/src/extension/config.js.map +1 -1
- package/src/extension/control-notices.d.ts +0 -2
- package/src/extension/control-notices.d.ts.map +1 -1
- package/src/extension/control-notices.js +1 -7
- package/src/extension/control-notices.js.map +1 -1
- package/src/extension/declaration-pinning.d.ts +14 -0
- package/src/extension/declaration-pinning.d.ts.map +1 -0
- package/src/extension/declaration-pinning.js +147 -0
- package/src/extension/declaration-pinning.js.map +1 -0
- package/src/extension/doctor.d.ts +3 -1
- package/src/extension/doctor.d.ts.map +1 -1
- package/src/extension/doctor.js +3 -9
- package/src/extension/doctor.js.map +1 -1
- package/src/extension/fanout-child.d.ts.map +1 -1
- package/src/extension/fanout-child.js +6 -8
- package/src/extension/fanout-child.js.map +1 -1
- package/src/extension/index.d.ts +1 -1
- package/src/extension/index.d.ts.map +1 -1
- package/src/extension/index.js +68 -116
- package/src/extension/index.js.map +1 -1
- package/src/extension/rpc.d.ts.map +1 -1
- package/src/extension/rpc.js +12 -6
- package/src/extension/rpc.js.map +1 -1
- package/src/extension/schemas.d.ts +137 -1
- package/src/extension/schemas.d.ts.map +1 -1
- package/src/extension/schemas.js +22 -22
- package/src/extension/schemas.js.map +1 -1
- package/src/extension/subagent-guide.d.ts +1 -0
- package/src/extension/subagent-guide.d.ts.map +1 -1
- package/src/extension/subagent-guide.js +80 -6
- package/src/extension/subagent-guide.js.map +1 -1
- package/src/extension/subagent-options.d.ts +8 -0
- package/src/extension/subagent-options.d.ts.map +1 -0
- package/src/extension/subagent-options.js +52 -0
- package/src/extension/subagent-options.js.map +1 -0
- package/src/extension/tool-activation.d.ts.map +1 -1
- package/src/extension/tool-activation.js +3 -2
- package/src/extension/tool-activation.js.map +1 -1
- package/src/extension/tool-description.d.ts +4 -7
- package/src/extension/tool-description.d.ts.map +1 -1
- package/src/extension/tool-description.js +45 -59
- package/src/extension/tool-description.js.map +1 -1
- package/src/inspectors/actions.d.ts +2 -0
- package/src/inspectors/actions.d.ts.map +1 -1
- package/src/inspectors/actions.js +41 -13
- package/src/inspectors/actions.js.map +1 -1
- package/src/inspectors/plugins.d.ts.map +1 -1
- package/src/inspectors/plugins.js +3 -2
- package/src/inspectors/plugins.js.map +1 -1
- package/src/inspectors/tmux/actions.d.ts +26 -0
- package/src/inspectors/tmux/actions.d.ts.map +1 -0
- package/src/inspectors/tmux/actions.js +182 -0
- package/src/inspectors/tmux/actions.js.map +1 -0
- package/src/inspectors/tmux/client.d.ts +28 -0
- package/src/inspectors/tmux/client.d.ts.map +1 -0
- package/src/inspectors/tmux/client.js +65 -0
- package/src/inspectors/tmux/client.js.map +1 -0
- package/src/inspectors/tmux/plugin.d.ts +8 -0
- package/src/inspectors/tmux/plugin.d.ts.map +1 -0
- package/src/inspectors/tmux/plugin.js +15 -0
- package/src/inspectors/tmux/plugin.js.map +1 -0
- package/src/integrations/herdr-status.d.ts +0 -1
- package/src/integrations/herdr-status.d.ts.map +1 -1
- package/src/integrations/herdr-status.js +18 -52
- package/src/integrations/herdr-status.js.map +1 -1
- package/src/integrations/program-status.d.ts +31 -0
- package/src/integrations/program-status.d.ts.map +1 -0
- package/src/integrations/program-status.js +189 -0
- package/src/integrations/program-status.js.map +1 -0
- package/src/intercom/native-supervisor-channel.d.ts +1 -0
- package/src/intercom/native-supervisor-channel.d.ts.map +1 -1
- package/src/intercom/native-supervisor-channel.js +77 -3
- package/src/intercom/native-supervisor-channel.js.map +1 -1
- package/src/intercom/result-intercom.js +1 -1
- package/src/intercom/result-intercom.js.map +1 -1
- package/src/intercom/supervisor-ui.d.ts +2 -12
- package/src/intercom/supervisor-ui.d.ts.map +1 -1
- package/src/intercom/supervisor-ui.js +2 -154
- package/src/intercom/supervisor-ui.js.map +1 -1
- package/src/missions/store.d.ts.map +1 -1
- package/src/missions/store.js +27 -5
- package/src/missions/store.js.map +1 -1
- package/src/runs/background/async-execution.d.ts +9 -1
- package/src/runs/background/async-execution.d.ts.map +1 -1
- package/src/runs/background/async-execution.js +250 -141
- package/src/runs/background/async-execution.js.map +1 -1
- package/src/runs/background/async-job-tracker.d.ts +4 -1
- package/src/runs/background/async-job-tracker.d.ts.map +1 -1
- package/src/runs/background/async-job-tracker.js +3 -2
- package/src/runs/background/async-job-tracker.js.map +1 -1
- package/src/runs/background/async-resume.d.ts.map +1 -1
- package/src/runs/background/async-resume.js +6 -2
- package/src/runs/background/async-resume.js.map +1 -1
- package/src/runs/background/await-async-run.d.ts +8 -4
- package/src/runs/background/await-async-run.d.ts.map +1 -1
- package/src/runs/background/await-async-run.js +14 -8
- package/src/runs/background/await-async-run.js.map +1 -1
- package/src/runs/background/binary-bootstrap.d.ts.map +1 -1
- package/src/runs/background/binary-bootstrap.js +2 -1
- package/src/runs/background/binary-bootstrap.js.map +1 -1
- package/src/runs/background/chain-root-attachment.d.ts +2 -0
- package/src/runs/background/chain-root-attachment.d.ts.map +1 -1
- package/src/runs/background/chain-root-attachment.js +5 -2
- package/src/runs/background/chain-root-attachment.js.map +1 -1
- package/src/runs/background/fleet-view.js +5 -5
- package/src/runs/background/fleet-view.js.map +1 -1
- package/src/runs/background/notify.d.ts +12 -2
- package/src/runs/background/notify.d.ts.map +1 -1
- package/src/runs/background/notify.js +148 -13
- package/src/runs/background/notify.js.map +1 -1
- package/src/runs/background/result-files.d.ts +29 -0
- package/src/runs/background/result-files.d.ts.map +1 -1
- package/src/runs/background/result-files.js +104 -6
- package/src/runs/background/result-files.js.map +1 -1
- package/src/runs/background/result-watcher.d.ts.map +1 -1
- package/src/runs/background/result-watcher.js +60 -64
- package/src/runs/background/result-watcher.js.map +1 -1
- package/src/runs/background/resume-guidance.js +2 -2
- package/src/runs/background/resume-guidance.js.map +1 -1
- package/src/runs/background/run-child-session.d.ts.map +1 -1
- package/src/runs/background/run-child-session.js +2 -1
- package/src/runs/background/run-child-session.js.map +1 -1
- package/src/runs/background/run-id-resolver.d.ts.map +1 -1
- package/src/runs/background/run-id-resolver.js +9 -6
- package/src/runs/background/run-id-resolver.js.map +1 -1
- package/src/runs/background/run-status.d.ts.map +1 -1
- package/src/runs/background/run-status.js +19 -15
- package/src/runs/background/run-status.js.map +1 -1
- package/src/runs/background/runner-child-launch.d.ts.map +1 -1
- package/src/runs/background/runner-child-launch.js +1 -0
- package/src/runs/background/runner-child-launch.js.map +1 -1
- package/src/runs/background/runner-http-dispatcher.d.ts +3 -0
- package/src/runs/background/runner-http-dispatcher.d.ts.map +1 -1
- package/src/runs/background/runner-http-dispatcher.js +2 -0
- package/src/runs/background/runner-http-dispatcher.js.map +1 -1
- package/src/runs/background/scheduled-runs.d.ts +9 -0
- package/src/runs/background/scheduled-runs.d.ts.map +1 -1
- package/src/runs/background/scheduled-runs.js +124 -36
- package/src/runs/background/scheduled-runs.js.map +1 -1
- package/src/runs/background/subagent-runner-bootstrap.d.ts.map +1 -1
- package/src/runs/background/subagent-runner-bootstrap.js +5 -1
- package/src/runs/background/subagent-runner-bootstrap.js.map +1 -1
- package/src/runs/background/subagent-runner.d.ts +3 -1
- package/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/src/runs/background/subagent-runner.js +15 -6
- package/src/runs/background/subagent-runner.js.map +1 -1
- package/src/runs/background/subagent-wait.d.ts +2 -0
- package/src/runs/background/subagent-wait.d.ts.map +1 -1
- package/src/runs/background/subagent-wait.js +34 -20
- package/src/runs/background/subagent-wait.js.map +1 -1
- package/src/runs/background/terminal-run-index.d.ts +1 -0
- package/src/runs/background/terminal-run-index.d.ts.map +1 -1
- package/src/runs/background/terminal-run-index.js +47 -1
- package/src/runs/background/terminal-run-index.js.map +1 -1
- package/src/runs/background/wait-tool.d.ts.map +1 -1
- package/src/runs/background/wait-tool.js +36 -23
- package/src/runs/background/wait-tool.js.map +1 -1
- package/src/runs/foreground/async-stop-action.d.ts +7 -1
- package/src/runs/foreground/async-stop-action.d.ts.map +1 -1
- package/src/runs/foreground/async-stop-action.js +49 -25
- package/src/runs/foreground/async-stop-action.js.map +1 -1
- package/src/runs/foreground/execution.d.ts.map +1 -1
- package/src/runs/foreground/execution.js +50 -24
- package/src/runs/foreground/execution.js.map +1 -1
- package/src/runs/foreground/subagent-executor.d.ts +5 -1
- package/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/src/runs/foreground/subagent-executor.js +174 -65
- package/src/runs/foreground/subagent-executor.js.map +1 -1
- package/src/runs/shared/child-hooks.js +6 -5
- package/src/runs/shared/child-hooks.js.map +1 -1
- package/src/runs/shared/child-launch.d.ts +2 -0
- package/src/runs/shared/child-launch.d.ts.map +1 -1
- package/src/runs/shared/child-launch.js +1 -0
- package/src/runs/shared/child-launch.js.map +1 -1
- package/src/runs/shared/child-session.d.ts +4 -0
- package/src/runs/shared/child-session.d.ts.map +1 -1
- package/src/runs/shared/child-session.js +28 -23
- package/src/runs/shared/child-session.js.map +1 -1
- package/src/runs/shared/child-tool-plan.d.ts.map +1 -1
- package/src/runs/shared/child-tool-plan.js +11 -10
- package/src/runs/shared/child-tool-plan.js.map +1 -1
- package/src/runs/shared/dynamic-fanout.js +1 -1
- package/src/runs/shared/dynamic-fanout.js.map +1 -1
- package/src/runs/shared/herdr-placed-run.d.ts +1 -0
- package/src/runs/shared/herdr-placed-run.d.ts.map +1 -1
- package/src/runs/shared/herdr-placed-run.js +10 -6
- package/src/runs/shared/herdr-placed-run.js.map +1 -1
- package/src/runs/shared/model-resolution-diagnostic.d.ts +10 -0
- package/src/runs/shared/model-resolution-diagnostic.d.ts.map +1 -1
- package/src/runs/shared/model-resolution-diagnostic.js +24 -0
- package/src/runs/shared/model-resolution-diagnostic.js.map +1 -1
- package/src/runs/shared/model-resolution.d.ts.map +1 -1
- package/src/runs/shared/model-resolution.js +29 -1
- package/src/runs/shared/model-resolution.js.map +1 -1
- package/src/runs/shared/nested-events.d.ts +2 -0
- package/src/runs/shared/nested-events.d.ts.map +1 -1
- package/src/runs/shared/nested-events.js +4 -0
- package/src/runs/shared/nested-events.js.map +1 -1
- package/src/runs/shared/parallel-handoff.js +2 -2
- package/src/runs/shared/parallel-handoff.js.map +1 -1
- package/src/runs/shared/parallel-utils.d.ts +2 -0
- package/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/src/runs/shared/parallel-utils.js.map +1 -1
- package/src/runs/shared/runner-launcher.d.ts +12 -0
- package/src/runs/shared/runner-launcher.d.ts.map +1 -0
- package/src/runs/shared/runner-launcher.js +25 -0
- package/src/runs/shared/runner-launcher.js.map +1 -0
- package/src/runs/shared/single-output.d.ts +20 -0
- package/src/runs/shared/single-output.d.ts.map +1 -1
- package/src/runs/shared/single-output.js +57 -0
- package/src/runs/shared/single-output.js.map +1 -1
- package/src/runs/shared/structured-output.d.ts.map +1 -1
- package/src/runs/shared/structured-output.js +17 -8
- package/src/runs/shared/structured-output.js.map +1 -1
- package/src/runs/shared/subagent-control.js +3 -3
- package/src/runs/shared/subagent-control.js.map +1 -1
- package/src/runs/shared/subagent-prompt-runtime.d.ts +8 -2
- package/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
- package/src/runs/shared/subagent-prompt-runtime.js +43 -20
- package/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
- package/src/runs/shared/workflow-async-child-guidance.js +1 -1
- package/src/runs/shared/workflow-async-child-guidance.js.map +1 -1
- package/src/runs/shared/worktree-cleanup-plan.js +1 -1
- package/src/runs/shared/worktree-cleanup-plan.js.map +1 -1
- package/src/shared/atomic-json.d.ts +4 -3
- package/src/shared/atomic-json.d.ts.map +1 -1
- package/src/shared/atomic-json.js +3 -1
- package/src/shared/atomic-json.js.map +1 -1
- package/src/shared/edit-distance.d.ts +5 -0
- package/src/shared/edit-distance.d.ts.map +1 -0
- package/src/shared/edit-distance.js +33 -0
- package/src/shared/edit-distance.js.map +1 -0
- package/src/shared/formatters.d.ts.map +1 -1
- package/src/shared/formatters.js +1 -6
- package/src/shared/formatters.js.map +1 -1
- package/src/shared/model-info.d.ts +8 -0
- package/src/shared/model-info.d.ts.map +1 -1
- package/src/shared/model-info.js +22 -0
- package/src/shared/model-info.js.map +1 -1
- package/src/shared/package-version.d.ts +3 -0
- package/src/shared/package-version.d.ts.map +1 -0
- package/src/shared/package-version.js +15 -0
- package/src/shared/package-version.js.map +1 -0
- package/src/shared/parent-wake.d.ts +18 -0
- package/src/shared/parent-wake.d.ts.map +1 -0
- package/src/shared/parent-wake.js +60 -0
- package/src/shared/parent-wake.js.map +1 -0
- package/src/shared/pruned-fork.d.ts +1 -1
- package/src/shared/pruned-fork.d.ts.map +1 -1
- package/src/shared/pruned-fork.js +6 -0
- package/src/shared/pruned-fork.js.map +1 -1
- package/src/shared/runtime-module-path.d.ts +11 -0
- package/src/shared/runtime-module-path.d.ts.map +1 -0
- package/src/shared/runtime-module-path.js +17 -0
- package/src/shared/runtime-module-path.js.map +1 -0
- package/src/shared/types.d.ts +26 -2
- package/src/shared/types.d.ts.map +1 -1
- package/src/shared/types.js +3 -0
- package/src/shared/types.js.map +1 -1
- package/src/shared/utils.d.ts.map +1 -1
- package/src/shared/utils.js +8 -7
- package/src/shared/utils.js.map +1 -1
- package/src/slash/slash-commands.js +3 -3
- package/src/slash/slash-commands.js.map +1 -1
- package/src/slash/subagents-admin.d.ts.map +1 -1
- package/src/slash/subagents-admin.js +2 -1
- package/src/slash/subagents-admin.js.map +1 -1
- package/src/tui/fleet-transcript.d.ts +2 -0
- package/src/tui/fleet-transcript.d.ts.map +1 -1
- package/src/tui/fleet-transcript.js +7 -2
- package/src/tui/fleet-transcript.js.map +1 -1
- package/src/tui/fleet.d.ts.map +1 -1
- package/src/tui/fleet.js +9 -4
- package/src/tui/fleet.js.map +1 -1
- package/src/tui/render.d.ts +2 -2
- package/src/tui/render.d.ts.map +1 -1
- package/src/tui/render.js +19 -17
- package/src/tui/render.js.map +1 -1
- package/src/tui/subagent-messages.d.ts +23 -0
- package/src/tui/subagent-messages.d.ts.map +1 -0
- package/src/tui/subagent-messages.js +270 -0
- package/src/tui/subagent-messages.js.map +1 -0
- package/src/watchdog/register-main.d.ts.map +1 -1
- package/src/watchdog/register-main.js +1 -19
- package/src/watchdog/register-main.js.map +1 -1
- package/src/watchdog/render.d.ts +2 -10
- package/src/watchdog/render.d.ts.map +1 -1
- package/src/watchdog/render.js +1 -19
- package/src/watchdog/render.js.map +1 -1
- package/src/watchdog/tool-actions.js +2 -2
- package/src/watchdog/tool-actions.js.map +1 -1
- package/src/workflows/scripted-workflow.d.ts.map +1 -1
- package/src/workflows/scripted-workflow.js +20 -6
- package/src/workflows/scripted-workflow.js.map +1 -1
- package/src/workflows/workflow-resources.d.ts.map +1 -1
- package/src/workflows/workflow-resources.js +12 -0
- package/src/workflows/workflow-resources.js.map +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { discoverAgents, findBlockingAgentDiagnostic, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext } from "../../agents/agents.js";
|
|
4
|
+
import { discoverAgents, findBlockingAgentDiagnostic, formatAvailableAgentLines, formatUnknownAgentError, resolveAgentName, suggestAgentName, unknownAgentDiagnosticContext } from "../../agents/agents.js";
|
|
5
5
|
import { getArtifactsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.js";
|
|
6
6
|
import { writeAtomicJson } from "../../shared/atomic-json.js";
|
|
7
7
|
import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilient-json.js";
|
|
@@ -10,7 +10,7 @@ import { resolveEffectiveThinking, toModelInfo } from "../../shared/model-info.j
|
|
|
10
10
|
import { beginForegroundChild, finishForegroundChild, foregroundSchedulingSettled, settleForegroundSchedulingOwner, updateForegroundChild, } from "./foreground-control.js";
|
|
11
11
|
import { getLivePromptAudit, rewritePromptWithGuidance, updateLiveEffectivePrompt } from "./prompt-audit.js";
|
|
12
12
|
import { persistForegroundRunHistory, MAX_REMEMBERED_FOREGROUND_RUNS } from "./foreground-history.js";
|
|
13
|
-
import { resolveExecutionAgentScope } from "../../agents/agent-scope.js";
|
|
13
|
+
import { projectScopeRequiresTrustMessage, resolveExecutionAgentScope } from "../../agents/agent-scope.js";
|
|
14
14
|
import { handleManagementAction } from "../../agents/agent-management.js";
|
|
15
15
|
import { handleRefinementAction } from "../../agents/agent-refinements.js";
|
|
16
16
|
import { buildDoctorReport } from "../../extension/doctor.js";
|
|
@@ -56,7 +56,7 @@ import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeili
|
|
|
56
56
|
import { isAgentContract } from "../shared/agent-contract.js";
|
|
57
57
|
import { normalizeExtensionBindings } from "../shared/extension-bindings.js";
|
|
58
58
|
import { assertRequiredChildExtensionsAdmitted, hasMandatoryRequiredChildExtensions, readRetainedRequiredChildExtensions, resolveRequiredChildExtensions } from "../../shared/required-child-extensions.js";
|
|
59
|
-
import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.js";
|
|
59
|
+
import { capInlineOutput, finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.js";
|
|
60
60
|
import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.js";
|
|
61
61
|
import { compactForegroundDetails, getSingleResultOutput, PROMPT_REDACTED, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.js";
|
|
62
62
|
import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, protectRetainedWorktreeForResume, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.js";
|
|
@@ -73,8 +73,8 @@ import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletio
|
|
|
73
73
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.js";
|
|
74
74
|
import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.js";
|
|
75
75
|
import { awaitExistingAsyncRun, claimWorkflowAwaitedResult } from "../background/await-async-run.js";
|
|
76
|
-
import { fallbackResultPayloadPathForSessionRun,
|
|
77
|
-
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, nestedRunScope, resolveNestedAsyncDir, retainNestedLookupRoute, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent } from "../shared/nested-events.js";
|
|
76
|
+
import { fallbackResultPayloadPathForSessionRun, resultFilePath, retireResultSnapshot, writeAsyncResultFile } from "../background/result-files.js";
|
|
77
|
+
import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, nestedRunNotActiveMessage, nestedRunScope, resolveNestedAsyncDir, retainNestedLookupRoute, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent } from "../shared/nested-events.js";
|
|
78
78
|
import { resolveSubagentRunId } from "../background/run-id-resolver.js";
|
|
79
79
|
import { formatNestedRunStatusLines } from "../shared/nested-render.js";
|
|
80
80
|
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.js";
|
|
@@ -83,6 +83,7 @@ import { getExternalJobProvider } from "../../api/external-job-provider.js";
|
|
|
83
83
|
import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.js";
|
|
84
84
|
import { externalCliReceiptMetadata, normalizeExternalCliRunnerStatus } from "../shared/external-cli-contract.js";
|
|
85
85
|
import { formatHerdrMachineRunnerUnsupported, resolveHerdrMachinePlacement } from "../shared/herdr-machine.js";
|
|
86
|
+
import { lookupRunnerLauncher, resolveAgentRunnerLauncher } from "../shared/runner-launcher.js";
|
|
86
87
|
import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.js";
|
|
87
88
|
import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.js";
|
|
88
89
|
import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding } from "../../missions/lifecycle.js";
|
|
@@ -111,35 +112,12 @@ import { applyReviewedCleanupPlan, formatCleanupReceipt, loadReviewedCleanupPlan
|
|
|
111
112
|
import { createWorktreeCleanupPlan, formatWorktreeCleanupPlan } from "../shared/worktree-cleanup-plan.js";
|
|
112
113
|
import { DIRS, DEFAULT_ARTIFACT_CONFIG, DEFAULT_MAX_OUTPUT, DEFAULT_FORK_PREAMBLE, SUBAGENT_ACTIONS, SUBAGENT_ASYNC_STARTED_EVENT, SUBAGENT_CHILD_STATUS_EVENT, SUBAGENT_CONTROL_EVENT, SUBAGENT_CONTROL_INTERCOM_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, SUBAGENT_LIFECYCLE_ARTIFACT_VERSION, checkSubagentDepth, resolveChildMaxSubagentDepth, resolveCurrentMaxSubagentDepth, resolveMaxSubagentSpawnsPerRun, truncateOutput, wrapForkTask, } from "../../shared/types.js";
|
|
113
114
|
import { deriveChildSessionName } from "../../shared/child-session-name.js";
|
|
115
|
+
import { editDistance, hasSingleAdjacentTransposition } from "../../shared/edit-distance.js";
|
|
114
116
|
const MUTATING_MANAGEMENT_ACTIONS = new Set(["command.yield", "command.cancel", "create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "worktree.cleanup", "lane.recordMerge", "lane.recordSupersession", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
|
|
115
117
|
const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["command.cancel", "delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "schedule.delete"]);
|
|
116
118
|
function resolveSteerDeliveryMode(mode) {
|
|
117
119
|
return mode === "steer" || mode === "follow_up" || mode === "auto" ? mode : undefined;
|
|
118
120
|
}
|
|
119
|
-
function editDistance(left, right) {
|
|
120
|
-
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
121
|
-
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
122
|
-
let diagonal = previous[0];
|
|
123
|
-
previous[0] = leftIndex;
|
|
124
|
-
for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
|
|
125
|
-
const above = previous[rightIndex];
|
|
126
|
-
previous[rightIndex] = left[leftIndex - 1] === right[rightIndex - 1]
|
|
127
|
-
? diagonal
|
|
128
|
-
: Math.min(diagonal, above, previous[rightIndex - 1]) + 1;
|
|
129
|
-
diagonal = above;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return previous[right.length];
|
|
133
|
-
}
|
|
134
|
-
function hasSingleAdjacentTransposition(left, right) {
|
|
135
|
-
if (left.length !== right.length)
|
|
136
|
-
return false;
|
|
137
|
-
const mismatch = [...left].findIndex((character, index) => character !== right[index]);
|
|
138
|
-
return mismatch >= 0
|
|
139
|
-
&& left[mismatch] === right[mismatch + 1]
|
|
140
|
-
&& left[mismatch + 1] === right[mismatch]
|
|
141
|
-
&& left.slice(mismatch + 2) === right.slice(mismatch + 2);
|
|
142
|
-
}
|
|
143
121
|
export function unknownSubagentActionMessage(action, disabled) {
|
|
144
122
|
const requested = action.toLowerCase();
|
|
145
123
|
const enabledActions = SUBAGENT_ACTIONS.filter((candidate) => !disabled?.actions.has(candidate));
|
|
@@ -304,6 +282,21 @@ function trustedSessionRootsForStatus(ctx, deps) {
|
|
|
304
282
|
function sessionProjectTrust(ctx) {
|
|
305
283
|
return typeof ctx.isProjectTrusted === "function" ? ctx.isProjectTrusted() : undefined;
|
|
306
284
|
}
|
|
285
|
+
/** Agent and settings discovery follows the launching session's trust, like the child itself. */
|
|
286
|
+
function withSessionProjectTrust(deps, ctx) {
|
|
287
|
+
if (sessionProjectTrust(ctx) !== false)
|
|
288
|
+
return deps;
|
|
289
|
+
return { ...deps, discoverAgents: (cwd, scope, preferredModelProvider) => deps.discoverAgents(cwd, scope, preferredModelProvider, { projectTrusted: false }) };
|
|
290
|
+
}
|
|
291
|
+
function projectScopeTrustError(params, ctx) {
|
|
292
|
+
if (sessionProjectTrust(ctx) !== false || resolveExecutionAgentScope(params.agentScope) !== "project")
|
|
293
|
+
return undefined;
|
|
294
|
+
const action = typeof params.action === "string" ? params.action.trim() : undefined;
|
|
295
|
+
// These actions resolve agents in the requested scope; management actions are left to their own handlers.
|
|
296
|
+
if (action !== undefined && action !== "resume" && action !== "validate" && action !== "append-step")
|
|
297
|
+
return undefined;
|
|
298
|
+
return buildRequestedModeError(params, projectScopeRequiresTrustMessage(resolveRequestedCwd(ctx.cwd, params.cwd)));
|
|
299
|
+
}
|
|
307
300
|
function spawnBudgetErrorResult(message, mode) {
|
|
308
301
|
return {
|
|
309
302
|
content: [{ type: "text", text: message }],
|
|
@@ -824,7 +817,7 @@ function emitControlNotification(input) {
|
|
|
824
817
|
}
|
|
825
818
|
}
|
|
826
819
|
function interruptAsyncRun(state, runId, kill, location) {
|
|
827
|
-
const target = getAsyncInterruptTarget(state, runId, location);
|
|
820
|
+
const target = getAsyncInterruptTarget(state, runId, location, { fallbackToNewest: runId === undefined });
|
|
828
821
|
if (!target)
|
|
829
822
|
return null;
|
|
830
823
|
const status = reconcileAsyncRun(target.asyncDir, omitUndefinedProperties({ kill })).status;
|
|
@@ -1016,6 +1009,7 @@ function appendStepToAsyncChain(input) {
|
|
|
1016
1009
|
cwd: input.ctx.cwd,
|
|
1017
1010
|
currentSessionId: resolveCurrentSessionId(input.ctx.sessionManager),
|
|
1018
1011
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1012
|
+
parentSessionFile: input.ctx.sessionManager.getSessionFile() ?? undefined,
|
|
1019
1013
|
currentModelProvider: parentModel?.provider,
|
|
1020
1014
|
currentModel: parentModel,
|
|
1021
1015
|
scopedModelIds: scopedModelIdsFromContext(input.ctx),
|
|
@@ -1025,6 +1019,17 @@ function appendStepToAsyncChain(input) {
|
|
|
1025
1019
|
permissions: input.deps.config.permissions,
|
|
1026
1020
|
childRuntime: input.deps.childRuntime,
|
|
1027
1021
|
});
|
|
1022
|
+
// Appended steps run inside the already-running runner, so they must use its launcher (or none).
|
|
1023
|
+
// Checked before building, which can write progress files. A mix is refused by the builder.
|
|
1024
|
+
const appendedLaunchers = new Set(chain.flatMap(getStepAgents).map((name) => agents.find((agent) => agent.name === name)?.launcher));
|
|
1025
|
+
const appendedLauncher = [...appendedLaunchers][0];
|
|
1026
|
+
if (appendedLaunchers.size === 1 && appendedLauncher !== status.launcher?.name) {
|
|
1027
|
+
return {
|
|
1028
|
+
content: [{ type: "text", text: `Cannot append step to run '${resolved.id}': its runner uses ${status.launcher ? `launcher '${status.launcher.name}'` : "no launcher"}, but the appended agents use ${appendedLauncher ? `launcher '${appendedLauncher}'` : "no launcher"}.` }],
|
|
1029
|
+
isError: true,
|
|
1030
|
+
details: { mode: "management", results: [] },
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1028
1033
|
const built = buildAsyncRunnerSteps(resolved.id, compactOptional({
|
|
1029
1034
|
chain: wrapChainTasksForFork(chain, contextPolicy),
|
|
1030
1035
|
task: input.params.task,
|
|
@@ -1332,11 +1337,18 @@ async function interruptNestedRun(target) {
|
|
|
1332
1337
|
if (run.state === "paused")
|
|
1333
1338
|
return { content: [{ type: "text", text: `Nested run ${run.id} is already paused.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1334
1339
|
const result = await sendNestedControlRequest(target, "interrupt");
|
|
1335
|
-
|
|
1340
|
+
// The owner's foreground map cannot address a detached async child. Only
|
|
1341
|
+
// that missing-route response may use the existing authorized async inbox;
|
|
1342
|
+
// other owner refusals must remain authoritative.
|
|
1343
|
+
const missingForegroundRoute = result?.ok === false && result.message === nestedRunNotActiveMessage(run.id);
|
|
1344
|
+
if (result && !missingForegroundRoute)
|
|
1336
1345
|
return { content: [{ type: "text", text: result.message }], ...(result.ok ? {} : { isError: true }), details: { mode: "management", results: [] } };
|
|
1337
1346
|
const direct = directNestedAsyncInterrupt(target);
|
|
1338
1347
|
if (direct)
|
|
1339
1348
|
return direct;
|
|
1349
|
+
// No async inbox either: the owner's own answer is the accurate one.
|
|
1350
|
+
if (result)
|
|
1351
|
+
return { content: [{ type: "text", text: result.message }], isError: true, details: { mode: "management", results: [] } };
|
|
1340
1352
|
return { content: [{ type: "text", text: `Nested run ${run.id} owner is not reachable and no safe direct async interrupt fallback is available.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1341
1353
|
}
|
|
1342
1354
|
async function resumeLiveNestedRun(input) {
|
|
@@ -1467,6 +1479,7 @@ async function resumeExternalJobFollowUp(input) {
|
|
|
1467
1479
|
cwd: input.requestCwd,
|
|
1468
1480
|
currentSessionId,
|
|
1469
1481
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1482
|
+
parentSessionFile: input.ctx.sessionManager.getSessionFile() ?? undefined,
|
|
1470
1483
|
currentModelProvider: parentModel?.provider,
|
|
1471
1484
|
currentModel: parentModel,
|
|
1472
1485
|
scopedModelIds: scopedModelIdsFromContext(input.ctx),
|
|
@@ -1587,7 +1600,7 @@ async function resumeAsyncRun(input) {
|
|
|
1587
1600
|
type: "text",
|
|
1588
1601
|
text: [
|
|
1589
1602
|
`Async child '${target.runId}' index ${target.index} is still running. action='resume' only revives paused, completed, or failed children.`,
|
|
1590
|
-
`Send live input with subagent({ action: "steer", id: "${target.runId}", index: ${target.index}
|
|
1603
|
+
`Send live input with subagent({ action: "steer", id: "${target.runId}", message: "...", options: { index: ${target.index} } }).`,
|
|
1591
1604
|
].join("\n"),
|
|
1592
1605
|
}],
|
|
1593
1606
|
isError: true,
|
|
@@ -1727,7 +1740,7 @@ async function resumeAsyncRun(input) {
|
|
|
1727
1740
|
const chain = wrapChainTasksForFork(attachChain, contextPolicy);
|
|
1728
1741
|
const normalized = normalizeSkillInput(input.params.skill);
|
|
1729
1742
|
const parentModel = input.parentModel;
|
|
1730
|
-
const result = executeAsyncChain(runId, compactOptional({
|
|
1743
|
+
const result = await executeAsyncChain(runId, compactOptional({
|
|
1731
1744
|
chain,
|
|
1732
1745
|
task: workflowTask,
|
|
1733
1746
|
goal,
|
|
@@ -1746,11 +1759,13 @@ async function resumeAsyncRun(input) {
|
|
|
1746
1759
|
cwd: input.requestCwd,
|
|
1747
1760
|
currentSessionId: input.deps.state.currentSessionId,
|
|
1748
1761
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1762
|
+
parentSessionFile: input.ctx.sessionManager.getSessionFile() ?? undefined,
|
|
1749
1763
|
currentModelProvider: parentModel?.provider,
|
|
1750
1764
|
currentModel: parentModel,
|
|
1751
1765
|
scopedModelIds: scopedModelIdsFromContext(input.ctx),
|
|
1752
1766
|
modelScope,
|
|
1753
1767
|
modelResponseAliases: input.deps.config.modelResponseAliases,
|
|
1768
|
+
runnerLaunchers: input.deps.config.runnerLaunchers,
|
|
1754
1769
|
interactive: input.ctx.hasUI,
|
|
1755
1770
|
permissions: input.deps.config.permissions,
|
|
1756
1771
|
childRuntime: input.deps.childRuntime,
|
|
@@ -1814,7 +1829,8 @@ async function resumeAsyncRun(input) {
|
|
|
1814
1829
|
if (target.source === "nested" && !target.recoveryDescriptor) {
|
|
1815
1830
|
return { content: [{ type: "text", text: `Nested child '${target.runId}' is missing its required recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1816
1831
|
}
|
|
1817
|
-
|
|
1832
|
+
// Runs without a recovery descriptor were never wrapped; a launcher added to the agent file later does not apply.
|
|
1833
|
+
const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : { ...baseAgentConfig, launcher: undefined };
|
|
1818
1834
|
const agentConfig = intercomBridge.active ? applyIntercomBridgeToAgent(recoveryAgentConfig, intercomBridge) : recoveryAgentConfig;
|
|
1819
1835
|
const foregroundContract = target.source === "foreground" ? target.resumeContract : undefined;
|
|
1820
1836
|
const outputSchema = Object.hasOwn(input.params, "outputSchema")
|
|
@@ -1831,6 +1847,10 @@ async function resumeAsyncRun(input) {
|
|
|
1831
1847
|
if (acceptanceErrors.length > 0) {
|
|
1832
1848
|
return { content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }], isError: true, details: { mode: "management", results: [] } };
|
|
1833
1849
|
}
|
|
1850
|
+
// Refuse before capacity moves so the retained run stays resumable once the config is fixed.
|
|
1851
|
+
if (agentConfig.launcher !== undefined && !lookupRunnerLauncher(input.deps.config.runnerLaunchers, agentConfig.launcher)) {
|
|
1852
|
+
return { content: [{ type: "text", text: `Cannot resume: run '${target.runId}' was launched with launcher '${agentConfig.launcher}', which is no longer defined in runnerLaunchers in the user subagent config.` }], isError: true, details: { mode: "management", results: [] } };
|
|
1853
|
+
}
|
|
1834
1854
|
const runId = randomUUID();
|
|
1835
1855
|
const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
|
|
1836
1856
|
let activeAsyncCapacity;
|
|
@@ -1873,12 +1893,14 @@ async function resumeAsyncRun(input) {
|
|
|
1873
1893
|
cwd: input.requestCwd,
|
|
1874
1894
|
currentSessionId: input.deps.state.currentSessionId,
|
|
1875
1895
|
parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
|
|
1896
|
+
parentSessionFile: input.ctx.sessionManager.getSessionFile() ?? undefined,
|
|
1876
1897
|
currentModelProvider: parentModel?.provider,
|
|
1877
1898
|
currentModel: parentModel,
|
|
1878
1899
|
scopedModelIds: scopedModelIdsFromContext(input.ctx),
|
|
1879
1900
|
modelScope,
|
|
1880
1901
|
// Absence in the retained contract is meaningful; never acquire current aliases.
|
|
1881
1902
|
modelResponseAliases: recoveryDescriptor ? recoveryDescriptor.modelResponseAliases : foregroundContract?.modelResponseAliases,
|
|
1903
|
+
runnerLaunchers: input.deps.config.runnerLaunchers,
|
|
1882
1904
|
interactive: input.ctx.hasUI,
|
|
1883
1905
|
permissions: input.deps.config.permissions,
|
|
1884
1906
|
childRuntime: input.deps.childRuntime,
|
|
@@ -2028,10 +2050,12 @@ function formatFailedSingleRunOutput(result, displayOutput) {
|
|
|
2028
2050
|
const error = result.error || "Failed";
|
|
2029
2051
|
const output = displayOutput.trim();
|
|
2030
2052
|
const lines = [error];
|
|
2053
|
+
let cappedOutput = output;
|
|
2031
2054
|
if (output && output !== error.trim()) {
|
|
2032
|
-
|
|
2055
|
+
cappedOutput = capInlineOutput(output, { artifactPath: result.artifactPaths?.outputPath, savedPath: result.savedOutputPath });
|
|
2056
|
+
lines.push("", "Output:", cappedOutput);
|
|
2033
2057
|
}
|
|
2034
|
-
if (result.artifactPaths?.outputPath && fs.existsSync(result.artifactPaths.outputPath)) {
|
|
2058
|
+
if (cappedOutput === output && result.artifactPaths?.outputPath && fs.existsSync(result.artifactPaths.outputPath)) {
|
|
2035
2059
|
lines.push("", `Output artifact: ${result.artifactPaths.outputPath}`);
|
|
2036
2060
|
}
|
|
2037
2061
|
return lines.join("\n");
|
|
@@ -2156,6 +2180,8 @@ function workflowValidationOptions(deps, params, runtimeCwd, parentModelProvider
|
|
|
2156
2180
|
const cwd = resolveRequestedCwd(runtimeCwd, params.cwd);
|
|
2157
2181
|
const scope = resolveExecutionAgentScope(params.agentScope);
|
|
2158
2182
|
let discovered;
|
|
2183
|
+
// Validation reports every bad agent name; only the first one carries the agent list.
|
|
2184
|
+
let listedAgents = false;
|
|
2159
2185
|
return {
|
|
2160
2186
|
maxSubagentSpawnsPerRun: params.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
|
|
2161
2187
|
agentNameError: (name) => {
|
|
@@ -2164,12 +2190,13 @@ function workflowValidationOptions(deps, params, runtimeCwd, parentModelProvider
|
|
|
2164
2190
|
const resolved = resolveAgentName(name, agents);
|
|
2165
2191
|
if (resolved.agent || resolved.error)
|
|
2166
2192
|
return canonicalizeAgentName(name, agents, discovered.agentDiagnostics, diagnosticContextFromDiscovery(discovered, cwd, scope)).error;
|
|
2167
|
-
const
|
|
2168
|
-
const suggestion =
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2193
|
+
const disabled = discovered.disabledAgents?.includes(name.trim()) === true;
|
|
2194
|
+
const suggestion = disabled ? undefined : suggestAgentName(name, agents);
|
|
2195
|
+
const problem = disabled ? `Agent '${name}' is disabled by a settings override.` : `Unknown agent '${name}'.${suggestion ? ` Did you mean '${suggestion}'?` : ""}`;
|
|
2196
|
+
if (listedAgents)
|
|
2197
|
+
return problem;
|
|
2198
|
+
listedAgents = true;
|
|
2199
|
+
return [`${problem} Available agents:`, ...formatAvailableAgentLines(agents)].join("\n");
|
|
2173
2200
|
},
|
|
2174
2201
|
};
|
|
2175
2202
|
}
|
|
@@ -2904,12 +2931,12 @@ function importedAsyncRootUsage(completed) {
|
|
|
2904
2931
|
}
|
|
2905
2932
|
function removeWorkflowAwaitedResult(asyncDir, resultPath, runId, publication) {
|
|
2906
2933
|
try {
|
|
2907
|
-
|
|
2934
|
+
retireResultSnapshot(workflowAwaitedAsyncResultPath(asyncDir), { ...publication, runId }, resultPath);
|
|
2908
2935
|
}
|
|
2909
|
-
catch {
|
|
2910
|
-
//
|
|
2936
|
+
catch (error) {
|
|
2937
|
+
// Cleanup must not replace an already imported delivery.
|
|
2938
|
+
console.error(`Failed to remove imported workflow child result '${resultPath}'; leaving it in place:`, error);
|
|
2911
2939
|
}
|
|
2912
|
-
removeResultIndex(asyncDir, publication.sessionId, runId, publication.toolCallId);
|
|
2913
2940
|
}
|
|
2914
2941
|
function emitWorkflowAwaitedChildComplete(pi, state, runId, asyncDir, parentWorkflowRunId, completed) {
|
|
2915
2942
|
let status;
|
|
@@ -3037,11 +3064,13 @@ async function runAsyncPath(data, deps) {
|
|
|
3037
3064
|
cwd: ctx.cwd,
|
|
3038
3065
|
currentSessionId: data.parentSessionId,
|
|
3039
3066
|
parentSessionId: data.parentPiSessionId,
|
|
3067
|
+
parentSessionFile: ctx.sessionManager.getSessionFile() ?? undefined,
|
|
3040
3068
|
currentModelProvider: parentModel?.provider,
|
|
3041
3069
|
currentModel: parentModel,
|
|
3042
3070
|
scopedModelIds: data.scopedModelIds,
|
|
3043
3071
|
modelScope: data.modelScope,
|
|
3044
3072
|
modelResponseAliases: deps.config.modelResponseAliases,
|
|
3073
|
+
runnerLaunchers: deps.config.runnerLaunchers,
|
|
3045
3074
|
interactive: ctx.hasUI,
|
|
3046
3075
|
permissions: deps.config.permissions,
|
|
3047
3076
|
childRuntime: deps.childRuntime,
|
|
@@ -3091,6 +3120,7 @@ async function runAsyncPath(data, deps) {
|
|
|
3091
3120
|
if (launchRuleError)
|
|
3092
3121
|
return toExecutionErrorResult(params, new Error(launchRuleError), data.contextPolicy.contextSummary);
|
|
3093
3122
|
const asyncResult = await executeAsyncSingle(id, compactOptional({
|
|
3123
|
+
toolCallId: data.toolCallId,
|
|
3094
3124
|
agent: params.agent,
|
|
3095
3125
|
task: shouldForkAgent(contextPolicy, params.agent) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
|
|
3096
3126
|
goal: params.task ?? "",
|
|
@@ -3295,7 +3325,12 @@ function resolveWorkflowChildOutputPath(input) {
|
|
|
3295
3325
|
id: input.params.resume.trim(),
|
|
3296
3326
|
...(typeof index === "number" && Number.isInteger(index) ? { index } : {}),
|
|
3297
3327
|
}, input.state);
|
|
3298
|
-
|
|
3328
|
+
const recoveryOutput = "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined;
|
|
3329
|
+
if (hasExplicitOutput)
|
|
3330
|
+
return { path: recoveryOutput, inherited: false };
|
|
3331
|
+
// An output-less foreground resume reuses its recorded output; only an absolute path is known before the new run id exists.
|
|
3332
|
+
const foregroundOutput = target.source === "foreground" ? target.resumeContract?.output : undefined;
|
|
3333
|
+
return { path: recoveryOutput, inherited: false, resumedOutput: recoveryOutput ?? (typeof foregroundOutput === "string" && path.isAbsolute(foregroundOutput) ? foregroundOutput : undefined) };
|
|
3299
3334
|
}
|
|
3300
3335
|
const childCwd = resolveWorkflowChildLocalCwd(input);
|
|
3301
3336
|
let agentOutput;
|
|
@@ -3335,7 +3370,9 @@ function workflowChildOutputClaims(input) {
|
|
|
3335
3370
|
}
|
|
3336
3371
|
const overrides = new Map();
|
|
3337
3372
|
for (const entry of resolvedEntries) {
|
|
3338
|
-
|
|
3373
|
+
// An output-less resume keeps its retained path unless an earlier child of this workflow already owns it.
|
|
3374
|
+
const resumeCollides = entry.resumedOutput !== undefined && input.claimedOutputPaths.has(resolveWorkflowHostOutputClaimPath(entry.resumedOutput));
|
|
3375
|
+
if (resumeCollides || (entry.inherited && entry.path && (paths.get(resolveWorkflowHostOutputClaimPath(entry.path)) ?? 0) > 1)) {
|
|
3339
3376
|
const output = workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, entry.key);
|
|
3340
3377
|
overrides.set(entry.key, output);
|
|
3341
3378
|
entry.path = output;
|
|
@@ -3353,6 +3390,16 @@ function workflowChildOutputClaims(input) {
|
|
|
3353
3390
|
claims.set(claimPath, key);
|
|
3354
3391
|
childClaims.set(key, claimPath);
|
|
3355
3392
|
}
|
|
3393
|
+
// Record a discovered output-less resume path for later admissions without adding same-batch duplicate errors.
|
|
3394
|
+
for (const { key, path: resolved, resumedOutput } of resolvedEntries) {
|
|
3395
|
+
if (resolved || !resumedOutput)
|
|
3396
|
+
continue;
|
|
3397
|
+
const claimPath = resolveWorkflowHostOutputClaimPath(resumedOutput);
|
|
3398
|
+
if (claims.has(claimPath))
|
|
3399
|
+
continue;
|
|
3400
|
+
claims.set(claimPath, key);
|
|
3401
|
+
childClaims.set(key, claimPath);
|
|
3402
|
+
}
|
|
3356
3403
|
return { claims, childClaims, overrides };
|
|
3357
3404
|
}
|
|
3358
3405
|
function applyWorkflowChildOutputClaims(target, claims) {
|
|
@@ -3363,7 +3410,7 @@ function applyWorkflowChildOutputClaims(target, claims) {
|
|
|
3363
3410
|
function prepareWorkflowChildLaunchParams(input) {
|
|
3364
3411
|
let childParams = input.childParams;
|
|
3365
3412
|
const usesDefaultOutput = input.childParams.output === undefined && input.childParams.resume === undefined;
|
|
3366
|
-
if (
|
|
3413
|
+
if (input.childParams.output === undefined && input.outputOverride !== undefined) {
|
|
3367
3414
|
childParams = { ...input.childParams, output: input.outputOverride };
|
|
3368
3415
|
}
|
|
3369
3416
|
else if (usesDefaultOutput && input.aggregateOutputPath !== undefined) {
|
|
@@ -3380,7 +3427,7 @@ function prepareWorkflowChildLaunchParams(input) {
|
|
|
3380
3427
|
const agent = typeof childParams.agent === "string"
|
|
3381
3428
|
? resolveAgentName(childParams.agent, discoveredAgents).agent ?? resolveAgentName(childParams.agent, input.agents).agent
|
|
3382
3429
|
: undefined;
|
|
3383
|
-
const externalAsyncRequired = agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job";
|
|
3430
|
+
const externalAsyncRequired = agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job" || agent?.launcher !== undefined;
|
|
3384
3431
|
return prepareWorkflowLaunchParams(input.workflowDefaults, childParams, input.parentWorkflowRunId, input.workflowKey, { ...input.options, externalAsyncRequired, outputClaimPath: input.outputClaimPath });
|
|
3385
3432
|
}
|
|
3386
3433
|
async function finalizeSingleWorktreeHandoff(input) {
|
|
@@ -3604,6 +3651,7 @@ async function runSinglePath(data, deps) {
|
|
|
3604
3651
|
const interruptController = new AbortController();
|
|
3605
3652
|
let detachForeground;
|
|
3606
3653
|
let childSessionControls;
|
|
3654
|
+
let pendingRuntimeStop;
|
|
3607
3655
|
const foregroundControl = deps.state.foregroundControls.get(runId);
|
|
3608
3656
|
const syncHerdrForegroundChild = () => {
|
|
3609
3657
|
if (!foregroundControl?.parentWorkflowRunId)
|
|
@@ -3679,6 +3727,7 @@ async function runSinglePath(data, deps) {
|
|
|
3679
3727
|
? new Promise((resolve) => { resolveDetachedWorkflowChild = resolve; })
|
|
3680
3728
|
: undefined;
|
|
3681
3729
|
try {
|
|
3730
|
+
const childSessionFile = sessionFileForTask(params.agent, 0, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
3682
3731
|
const launched = await runSync(ctx.cwd, agents, params.agent, task, compactOptional({
|
|
3683
3732
|
machine: foregroundMachine,
|
|
3684
3733
|
parentProviderRegistry: ctx.modelRegistry,
|
|
@@ -3687,9 +3736,14 @@ async function runSinglePath(data, deps) {
|
|
|
3687
3736
|
permissions: deps.config.permissions,
|
|
3688
3737
|
runtimeSnapshotHost: deps.pi,
|
|
3689
3738
|
parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
|
|
3739
|
+
parentSessionFile: ctx.sessionManager.getSessionFile() ?? undefined,
|
|
3690
3740
|
requiredExtensions,
|
|
3691
3741
|
childRuntime: deps.childRuntime,
|
|
3692
|
-
onChildSession: (controls) => {
|
|
3742
|
+
onChildSession: (controls) => {
|
|
3743
|
+
childSessionControls = controls;
|
|
3744
|
+
if (pendingRuntimeStop)
|
|
3745
|
+
controls.stop(pendingRuntimeStop);
|
|
3746
|
+
},
|
|
3693
3747
|
context: data.contextPolicy.contextForAgent(params.agent),
|
|
3694
3748
|
unknownAgentDiagnosticContext: data.unknownAgentDiagnosticContext,
|
|
3695
3749
|
runFanoutBudget: params.runFanoutAdmitted ? data.runFanoutBudget : { ...data.runFanoutBudget, parentPath: `${data.runFanoutBudget.parentPath ? `${data.runFanoutBudget.parentPath}/` : ""}single` },
|
|
@@ -3702,7 +3756,7 @@ async function runSinglePath(data, deps) {
|
|
|
3702
3756
|
intercomEvents: deps.pi.events,
|
|
3703
3757
|
runId,
|
|
3704
3758
|
sessionDir: sessionDirForIndex(0),
|
|
3705
|
-
sessionFile:
|
|
3759
|
+
sessionFile: childSessionFile,
|
|
3706
3760
|
share: shareEnabled,
|
|
3707
3761
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
3708
3762
|
artifactConfig,
|
|
@@ -3743,6 +3797,8 @@ async function runSinglePath(data, deps) {
|
|
|
3743
3797
|
detachForeground = detach;
|
|
3744
3798
|
},
|
|
3745
3799
|
onDetachedExit: async (result) => {
|
|
3800
|
+
if (foregroundControl)
|
|
3801
|
+
foregroundControl.stopForRuntimeReplacement = undefined;
|
|
3746
3802
|
if (resolveDetachedWorkflowChild) {
|
|
3747
3803
|
resolveDetachedWorkflowChild(result);
|
|
3748
3804
|
return;
|
|
@@ -3794,6 +3850,20 @@ async function runSinglePath(data, deps) {
|
|
|
3794
3850
|
capabilityCeiling: data.capabilityCeiling,
|
|
3795
3851
|
allowZeroToolBudget: data.allowZeroToolBudget && effectiveToolBudget.toolBudget === data.toolBudget,
|
|
3796
3852
|
}));
|
|
3853
|
+
if (launched.detached && foregroundControl) {
|
|
3854
|
+
// Record the stop before aborting so the next runtime's history restore finds it.
|
|
3855
|
+
foregroundControl.stopForRuntimeReplacement = () => {
|
|
3856
|
+
// The detach receipt predates the session file that resume needs.
|
|
3857
|
+
const sessionFile = childSessionFile && fs.existsSync(childSessionFile) ? childSessionFile : undefined;
|
|
3858
|
+
const stopped = { ...launched, detached: undefined, stopped: true, exitCode: 1, error: RUNTIME_REPLACED_STOP_ERROR, finalOutput: RUNTIME_REPLACED_STOP_ERROR, sessionFile };
|
|
3859
|
+
rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: [stopped], params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings, requiredExtensions });
|
|
3860
|
+
// A child detached during creation has no controls yet; stop it as soon as they arrive.
|
|
3861
|
+
if (childSessionControls)
|
|
3862
|
+
childSessionControls.stop(RUNTIME_REPLACED_STOP_ERROR);
|
|
3863
|
+
else
|
|
3864
|
+
pendingRuntimeStop = RUNTIME_REPLACED_STOP_ERROR;
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3797
3867
|
r = launched.detached && detachedWorkflowChild ? await detachedWorkflowChild : launched;
|
|
3798
3868
|
}
|
|
3799
3869
|
catch (error) {
|
|
@@ -3904,7 +3974,7 @@ async function runSinglePath(data, deps) {
|
|
|
3904
3974
|
isError: true,
|
|
3905
3975
|
};
|
|
3906
3976
|
return {
|
|
3907
|
-
content: [{ type: "text", text: `${finalizedOutput.displayOutput || "(no output)"}${worktreeSuffix}` }],
|
|
3977
|
+
content: [{ type: "text", text: `${capInlineOutput(finalizedOutput.displayOutput, { artifactPath: r.artifactPaths?.outputPath, savedPath: finalizedOutput.savedPath }) || "(no output)"}${worktreeSuffix}` }],
|
|
3908
3978
|
details,
|
|
3909
3979
|
};
|
|
3910
3980
|
}
|
|
@@ -4107,7 +4177,7 @@ function workflowRunningChildrenSummary(children) {
|
|
|
4107
4177
|
function workflowResultChildren(children, status, includeFailureFields) {
|
|
4108
4178
|
return withWorkflowRevivals(DIRS.async, status.runId, children).map((child) => {
|
|
4109
4179
|
const sessionName = status.steps?.find((step) => step.workflowKey === child.key)?.sessionName;
|
|
4110
|
-
return { workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(child.revival ? { revival: formatWorkflowKeyRevival(child.revival) } : {}), ...(sessionName ? { sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, ...(child.state === "running" ? { state: "running" } : { success: child.ok }), ...(child.asyncDir ? { asyncDir: child.asyncDir } : {}), ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(includeFailureFields && child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(includeFailureFields && child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.outputArtifactPath || child.outputReference ? { artifactPaths: { outputPath: child.outputArtifactPath ?? child.outputReference } } : {}) };
|
|
4180
|
+
return { workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(child.revival ? { revival: formatWorkflowKeyRevival(child.revival) } : {}), ...(sessionName ? { sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, ...(child.state === "running" ? { state: "running" } : { success: child.ok }), ...(!child.ok && child.error ? { error: child.error } : {}), ...(child.asyncDir ? { asyncDir: child.asyncDir } : {}), ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(includeFailureFields && child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(includeFailureFields && child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.outputArtifactPath || child.outputReference ? { artifactPaths: { outputPath: child.outputArtifactPath ?? child.outputReference } } : {}) };
|
|
4111
4181
|
});
|
|
4112
4182
|
}
|
|
4113
4183
|
function workflowOutputPathMappingSummary(children) {
|
|
@@ -4141,6 +4211,7 @@ function workflowSteerReceipt(key, result) {
|
|
|
4141
4211
|
};
|
|
4142
4212
|
}
|
|
4143
4213
|
const CHILD_SESSION_NOT_RUNNING_YET = "Child session is not running yet.";
|
|
4214
|
+
const RUNTIME_REPLACED_STOP_ERROR = "Subagent stopped because the pi-subagents runtime that ran it was replaced (reload or session switch).";
|
|
4144
4215
|
const MAX_WORKFLOW_RESUME_HINT_BYTES = 1024;
|
|
4145
4216
|
const MAX_WORKFLOW_CHILD_RUN_ID_BYTES = 256;
|
|
4146
4217
|
const WORKFLOW_RESUME_HINT_PARENT_STATES = new Set(["complete", "failed", "partial"]);
|
|
@@ -4634,6 +4705,7 @@ function createScheduledOwnerState(source, ownerSessionId, ctx) {
|
|
|
4634
4705
|
};
|
|
4635
4706
|
}
|
|
4636
4707
|
export function createSubagentExecutor(deps) {
|
|
4708
|
+
const executorDeps = deps;
|
|
4637
4709
|
const delegatedThinkingOverrides = new WeakMap();
|
|
4638
4710
|
const delegatedZeroToolBudgets = new WeakSet();
|
|
4639
4711
|
const delegatedExecutions = new WeakSet();
|
|
@@ -4657,6 +4729,10 @@ export function createSubagentExecutor(deps) {
|
|
|
4657
4729
|
}
|
|
4658
4730
|
};
|
|
4659
4731
|
const execute = async (_id, params, signal, onUpdate, ctx, preserveActiveSession = false, parentModelOverride) => {
|
|
4732
|
+
const projectScopeError = projectScopeTrustError(params, ctx);
|
|
4733
|
+
if (projectScopeError)
|
|
4734
|
+
return projectScopeError;
|
|
4735
|
+
const deps = withSessionProjectTrust(executorDeps, ctx);
|
|
4660
4736
|
const workflowLaunchObserver = workflowLaunchObservers.get(params);
|
|
4661
4737
|
const inheritedUsageBudget = workflowOwnedUsageBudgets.get(params);
|
|
4662
4738
|
const retainedRequiredExtensions = workflowRetainedRequiredExtensions.get(params);
|
|
@@ -4985,7 +5061,7 @@ export function createSubagentExecutor(deps) {
|
|
|
4985
5061
|
if (deps.state.currentSessionId !== currentSessionId || deps.state.completionOwnerId !== completionOwnerId)
|
|
4986
5062
|
return false;
|
|
4987
5063
|
try {
|
|
4988
|
-
deps.pi.sendMessage({
|
|
5064
|
+
(deps.parentWake ?? deps.pi).sendMessage({
|
|
4989
5065
|
customType: "subagent-workflow-result-write-failed",
|
|
4990
5066
|
content: message,
|
|
4991
5067
|
display: true,
|
|
@@ -5529,6 +5605,16 @@ export function createSubagentExecutor(deps) {
|
|
|
5529
5605
|
const journalFingerprint = workflowJournalFingerprints.get(notification.childKey);
|
|
5530
5606
|
if (journalFingerprint)
|
|
5531
5607
|
appendWorkflowChildJournal(asyncDir, { type: "settle", key: notification.childKey, fingerprint: journalFingerprint, result: notification.result });
|
|
5608
|
+
// Fresh, journal-reused and re-attached children all settle here, so their rows take usage from the same result.
|
|
5609
|
+
const settledResults = notification.result.results ?? [];
|
|
5610
|
+
const settledStep = settledResults.length ? status.steps?.find((candidate) => candidate.workflowKey === notification.childKey) : undefined;
|
|
5611
|
+
if (settledStep) {
|
|
5612
|
+
const usage = sumResultsUsage(settledResults);
|
|
5613
|
+
settledStep.tokens = { input: usage.input, output: usage.output, total: usage.input + usage.output };
|
|
5614
|
+
if (usage.turns > 0)
|
|
5615
|
+
settledStep.turnCount = usage.turns;
|
|
5616
|
+
persist({ tolerateStatusWriteFailure: true });
|
|
5617
|
+
}
|
|
5532
5618
|
appendWorkflowEvent({
|
|
5533
5619
|
type: "subagent.workflow.child_settled",
|
|
5534
5620
|
childKey: notification.childKey,
|
|
@@ -5537,10 +5623,12 @@ export function createSubagentExecutor(deps) {
|
|
|
5537
5623
|
workflowRunning: notification.workflowRunning,
|
|
5538
5624
|
});
|
|
5539
5625
|
try {
|
|
5540
|
-
deps.pi.sendMessage({
|
|
5626
|
+
(deps.parentWake ?? deps.pi).sendMessage({
|
|
5541
5627
|
customType: "subagent-incremental-child-notify",
|
|
5542
5628
|
content: formatIncrementalChildCompletion(notification),
|
|
5543
5629
|
display: notification.outcome !== "completed",
|
|
5630
|
+
// Render-only: Pi does not send details to the model.
|
|
5631
|
+
details: { childKey: notification.childKey, outcome: notification.outcome },
|
|
5544
5632
|
}, { triggerTurn: incrementalChildCompletionTriggersTurn(notification, requestParams.scheduleOrigin) });
|
|
5545
5633
|
}
|
|
5546
5634
|
catch (sendError) {
|
|
@@ -5600,7 +5688,7 @@ export function createSubagentExecutor(deps) {
|
|
|
5600
5688
|
appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: priorRunId });
|
|
5601
5689
|
const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps.state, priorRunId, priorAsyncDir, deps.kill);
|
|
5602
5690
|
const reattached = await awaitExistingAsyncRun(priorAsyncDir, priorRunId, workflowSignal).finally(stopListener.remove);
|
|
5603
|
-
const claimedPath = reattached.status === "settled" ? claimWorkflowAwaitedResult(priorAsyncDir, workflowRunId) : undefined;
|
|
5691
|
+
const claimedPath = reattached.status === "settled" ? claimWorkflowAwaitedResult(priorAsyncDir, workflowRunId, { ...reattached.result.importedPublication, runId: priorRunId }) : undefined;
|
|
5604
5692
|
if (reattached.status === "settled" && claimedPath) {
|
|
5605
5693
|
const imported = importWorkflowAwaitedChildResult(reattached.result, { runId: priorRunId, asyncDir: priorAsyncDir, resultPath: claimedPath, task: typeof childParams.task === "string" ? childParams.task : "", parentWorkflowRunId: workflowReuseSource?.runId, details: { mode: "single", asyncId: priorRunId, asyncDir: priorAsyncDir, results: [] }, state: deps.state, pi: deps.pi });
|
|
5606
5694
|
const reattachedResult = workflowChildResult(key, imported, childParams, deps.state);
|
|
@@ -6408,7 +6496,10 @@ export function createSubagentExecutor(deps) {
|
|
|
6408
6496
|
if (action === "guide") {
|
|
6409
6497
|
try {
|
|
6410
6498
|
const guide = readSubagentGuide(paramsWithResolvedCwd.topic);
|
|
6411
|
-
const
|
|
6499
|
+
const [topic, section] = paramsWithResolvedCwd.topic?.split("/") ?? [];
|
|
6500
|
+
const fullNotice = topic === "tool-reference" ? disabledFeatureNotice(disabledFeatures) : undefined;
|
|
6501
|
+
// Sections are capped at 8,000 chars, so they get a one-line pointer instead of the full list.
|
|
6502
|
+
const notice = fullNotice && section !== undefined ? "Some options and actions documented here are disabled by config in this session; guide topic tool-reference lists them." : fullNotice;
|
|
6412
6503
|
return {
|
|
6413
6504
|
content: [{ type: "text", text: notice ? `${notice}\n\n${guide}` : guide }],
|
|
6414
6505
|
details: { mode: "management", results: [] },
|
|
@@ -6433,10 +6524,13 @@ export function createSubagentExecutor(deps) {
|
|
|
6433
6524
|
if (action === "doctor") {
|
|
6434
6525
|
let currentSessionFile = null;
|
|
6435
6526
|
let currentSessionId = deps.state.currentSessionId;
|
|
6527
|
+
let budgetSessionId = deps.state.currentSessionId;
|
|
6436
6528
|
let sessionError;
|
|
6437
6529
|
try {
|
|
6438
6530
|
currentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
6439
6531
|
currentSessionId = ctx.sessionManager.getSessionId();
|
|
6532
|
+
// Launch admission's key (resolveCurrentSessionId) without its throw for an unbound session.
|
|
6533
|
+
budgetSessionId = currentSessionFile ?? currentSessionId;
|
|
6440
6534
|
}
|
|
6441
6535
|
catch (error) {
|
|
6442
6536
|
sessionError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
@@ -6449,9 +6543,9 @@ export function createSubagentExecutor(deps) {
|
|
|
6449
6543
|
if (!sessionError)
|
|
6450
6544
|
sessionError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
6451
6545
|
}
|
|
6452
|
-
const spawnBudget = getSpawnBudgetSnapshot(deps.state, deps.config,
|
|
6453
|
-
const activeAsyncCapacity =
|
|
6454
|
-
? getActiveAsyncCapacitySnapshot(
|
|
6546
|
+
const spawnBudget = getSpawnBudgetSnapshot(deps.state, deps.config, budgetSessionId);
|
|
6547
|
+
const activeAsyncCapacity = budgetSessionId
|
|
6548
|
+
? getActiveAsyncCapacitySnapshot(budgetSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })
|
|
6455
6549
|
: { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession) ?? 0 };
|
|
6456
6550
|
deps.state.activeAsyncCapacity = activeAsyncCapacity;
|
|
6457
6551
|
return {
|
|
@@ -6465,6 +6559,8 @@ export function createSubagentExecutor(deps) {
|
|
|
6465
6559
|
requestedSessionDir: paramsWithResolvedCwd.sessionDir,
|
|
6466
6560
|
currentSessionFile,
|
|
6467
6561
|
currentSessionId,
|
|
6562
|
+
spawnBudget,
|
|
6563
|
+
activeAsyncCapacity,
|
|
6468
6564
|
orchestratorTarget,
|
|
6469
6565
|
sessionError,
|
|
6470
6566
|
expandTilde: deps.expandTilde,
|
|
@@ -6965,16 +7061,28 @@ export function createSubagentExecutor(deps) {
|
|
|
6965
7061
|
: hasTasks
|
|
6966
7062
|
? (effectiveParams.tasks ?? []).map((task) => task.agent)
|
|
6967
7063
|
: (effectiveParams.chain ?? []).flatMap((step) => getStepAgents(step));
|
|
6968
|
-
const
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
7064
|
+
const selectedAgents = selectedAgentNames.map((name) => agents.find((agent) => agent.name === name));
|
|
7065
|
+
const externalAgent = selectedAgents.find((agent) => agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job");
|
|
7066
|
+
const launcherAgent = selectedAgents.find((agent) => agent?.launcher !== undefined);
|
|
7067
|
+
// A launcher agent goes to the background whenever the caller omitted async, even if the agent file defaults to foreground.
|
|
7068
|
+
const externalAsyncRequired = effectiveParams.foregroundOnly !== true && ((Boolean(externalAgent) && effectiveParams.async === undefined && effectiveParams.clarify !== true)
|
|
7069
|
+
|| (Boolean(launcherAgent) && normalizedParams.async === undefined && normalizedParams.clarify !== true));
|
|
6972
7070
|
const requestedAsync = externalAsyncRequired ? true : effectiveParams.async ?? deps.asyncByDefault;
|
|
6973
7071
|
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
6974
7072
|
const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
|
|
6975
7073
|
if (externalAgent && (!effectiveAsync || effectiveParams.foregroundOnly === true)) {
|
|
6976
7074
|
return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='${externalAgent.runner?.type}', which currently supports async/background execution only. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
|
|
6977
7075
|
}
|
|
7076
|
+
// forceTopLevelAsync rewrites async:false and clarify, so check what the caller asked for.
|
|
7077
|
+
if (launcherAgent && (!effectiveAsync || normalizedParams.async === false || normalizedParams.clarify === true || effectiveParams.foregroundOnly === true)) {
|
|
7078
|
+
return buildRequestedModeError(effectiveParams, `Agent '${launcherAgent.name}' uses launcher '${launcherAgent.launcher}', which wraps the background runner only. Foreground children run inside the parent process, so a launcher cannot wrap them. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
|
|
7079
|
+
}
|
|
7080
|
+
// Refuse a launcher launch that cannot start before fan-out admission or a session directory exists.
|
|
7081
|
+
for (const agent of selectedAgents) {
|
|
7082
|
+
const launcherError = agent ? resolveAgentRunnerLauncher(agent, deps.config.runnerLaunchers, effectiveParams.machine ?? agent.machine).error : undefined;
|
|
7083
|
+
if (launcherError)
|
|
7084
|
+
return buildRequestedModeError(effectiveParams, launcherError);
|
|
7085
|
+
}
|
|
6978
7086
|
if (effectiveAsync && hasSingle && effectiveParams.resume === undefined && effectiveParams.worktree === true) {
|
|
6979
7087
|
try {
|
|
6980
7088
|
await preflightWorktreeSource(effectiveCwd, { signal });
|
|
@@ -7168,6 +7276,7 @@ export function createSubagentExecutor(deps) {
|
|
|
7168
7276
|
backgroundRequestedWhileClarifying,
|
|
7169
7277
|
effectiveAsync,
|
|
7170
7278
|
asyncRunId,
|
|
7279
|
+
toolCallId: _id,
|
|
7171
7280
|
controlConfig,
|
|
7172
7281
|
...(delegatedExecution ? { suppressUnchangedDelegationUpdates: true } : {}),
|
|
7173
7282
|
intercomBridge,
|