@duckmind/dm-windows-x64 0.61.4 → 0.61.9
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +211 -67
- package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
- package/extensions/dm-subagents/agents/claude-code.md +15 -0
- package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
- package/extensions/dm-subagents/agents/codex-exec.md +15 -0
- package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
- package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
- package/extensions/dm-subagents/agents/delegate.md +3 -2
- package/extensions/dm-subagents/agents/oracle.md +10 -5
- package/extensions/dm-subagents/agents/researcher.md +2 -2
- package/extensions/dm-subagents/agents/reviewer.md +17 -7
- package/extensions/dm-subagents/agents/scout.md +5 -5
- package/extensions/dm-subagents/agents/worker.md +6 -2
- package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
- package/extensions/dm-subagents/index.js +4 -0
- package/extensions/dm-subagents/inspector-runner.mjs +10 -0
- package/extensions/dm-subagents/install.mjs +3 -2
- package/extensions/dm-subagents/package.json +2 -2
- package/extensions/dm-subagents/prompts/council.md +60 -0
- package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
- package/extensions/dm-subagents/prompts/review-loop.md +13 -7
- package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
- package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
- package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
- package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
- package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
- package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
- package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
- package/extensions/dm-subagents/src/agents/agents.js +1447 -299
- package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
- package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
- package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
- package/extensions/dm-subagents/src/agents/identity.js +1 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
- package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
- package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
- package/extensions/dm-subagents/src/agents/skills.js +52 -35
- package/extensions/dm-subagents/src/api/agents.js +6 -0
- package/extensions/dm-subagents/src/api/background-work.js +151 -0
- package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
- package/extensions/dm-subagents/src/api/control-channel.js +3 -0
- package/extensions/dm-subagents/src/api/delegation.js +5 -0
- package/extensions/dm-subagents/src/api/dm-args.js +3 -0
- package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
- package/extensions/dm-subagents/src/api/external-runs.js +233 -0
- package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
- package/extensions/dm-subagents/src/api/preflight.js +322 -0
- package/extensions/dm-subagents/src/api/project-panes.js +11 -0
- package/extensions/dm-subagents/src/api/shared-types.js +4 -0
- package/extensions/dm-subagents/src/extension/config.js +175 -0
- package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
- package/extensions/dm-subagents/src/extension/doctor.js +71 -17
- package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
- package/extensions/dm-subagents/src/extension/index.js +711 -265
- package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
- package/extensions/dm-subagents/src/extension/rpc.js +427 -22
- package/extensions/dm-subagents/src/extension/schemas.js +158 -65
- package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
- package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
- package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
- package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
- package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
- package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
- package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
- package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
- package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
- package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
- package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
- package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
- package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
- package/extensions/dm-subagents/src/missions/actions.js +394 -0
- package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
- package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
- package/extensions/dm-subagents/src/missions/store.js +548 -0
- package/extensions/dm-subagents/src/missions/types.js +9 -0
- package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
- package/extensions/dm-subagents/src/policy/authority.js +37 -0
- package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
- package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
- package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
- package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
- package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
- package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
- package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
- package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
- package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
- package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
- package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
- package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
- package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
- package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
- package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
- package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
- package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
- package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
- package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
- package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
- package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
- package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
- package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
- package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
- package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
- package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
- package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
- package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
- package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
- package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
- package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
- package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
- package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
- package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
- package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
- package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
- package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
- package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
- package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
- package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
- package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
- package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
- package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
- package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
- package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
- package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
- package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
- package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
- package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
- package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
- package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
- package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
- package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
- package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
- package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
- package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
- package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
- package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
- package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
- package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
- package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
- package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
- package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
- package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
- package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
- package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
- package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
- package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
- package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
- package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
- package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
- package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
- package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
- package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
- package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
- package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
- package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
- package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
- package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
- package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
- package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
- package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
- package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
- package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
- package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
- package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
- package/extensions/dm-subagents/src/shared/display-text.js +142 -0
- package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
- package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
- package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
- package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
- package/extensions/dm-subagents/src/shared/formatters.js +21 -7
- package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
- package/extensions/dm-subagents/src/shared/model-info.js +10 -5
- package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
- package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
- package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
- package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
- package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
- package/extensions/dm-subagents/src/shared/settings.js +39 -47
- package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
- package/extensions/dm-subagents/src/shared/status-format.js +9 -2
- package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
- package/extensions/dm-subagents/src/shared/types.js +47 -7
- package/extensions/dm-subagents/src/shared/utf8.js +12 -0
- package/extensions/dm-subagents/src/shared/utils.js +151 -131
- package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
- package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
- package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
- package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
- package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
- package/extensions/dm-subagents/src/slash/selector.js +101 -0
- package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
- package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
- package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
- package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
- package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
- package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
- package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
- package/extensions/dm-subagents/src/tui/render.js +1511 -261
- package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
- package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
- package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
- package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
- package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
- package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
- package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
- package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
- package/extensions/dm-subagents/src/watchdog/render.js +54 -0
- package/extensions/dm-subagents/src/watchdog/review.js +251 -0
- package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
- package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
- package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
- package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
- package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
- package/extensions/dm-subagents/src/watchdog/types.js +29 -0
- package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
- package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
- package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
- package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
- package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
- package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
- package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
- package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
- package/package.json +4 -3
- package/extensions/dm-fff/package.json +0 -21
- package/extensions/dm-fff/src/index.js +0 -691
- package/extensions/dm-fff/src/query.js +0 -60
- package/extensions/dm-subagents/agents/context-builder.md +0 -46
- package/extensions/dm-subagents/agents/planner.md +0 -55
- package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
- package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
- package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { existsSync, unlinkSync } from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { discoverAgents, formatUnknownAgentError, unknownAgentDiagnosticContext } from "../../agents/agents.js";
|
|
5
|
+
import { appendAgentRefinementOverlay } from "../../agents/agent-refinements.js";
|
|
6
|
+
import { alignForkedSessionCwd } from "../../shared/fork-context.js";
|
|
3
7
|
import {
|
|
4
8
|
ensureArtifactsDir,
|
|
9
|
+
formatOutputArtifactContent,
|
|
5
10
|
getArtifactPaths,
|
|
6
11
|
writeArtifact,
|
|
7
12
|
writeMetadata
|
|
@@ -19,29 +24,61 @@ import {
|
|
|
19
24
|
buildControlEvent,
|
|
20
25
|
claimControlNotification,
|
|
21
26
|
deriveActivityState,
|
|
27
|
+
shouldEmitOpenToolAttention,
|
|
22
28
|
shouldNotifyControlEvent
|
|
23
29
|
} from "../shared/subagent-control.js";
|
|
24
30
|
import {
|
|
25
31
|
getFinalOutput,
|
|
26
32
|
findLatestSessionFile,
|
|
27
33
|
detectSubagentError,
|
|
34
|
+
hasEmptyTerminalAssistantResponse,
|
|
35
|
+
formatEmptyTerminalAssistantResponseError,
|
|
28
36
|
extractToolArgsPreview,
|
|
29
|
-
extractTextFromContent
|
|
37
|
+
extractTextFromContent,
|
|
38
|
+
boundStreamedRecentTools,
|
|
39
|
+
boundStreamedRecentOutput,
|
|
40
|
+
boundStreamedToolCalls
|
|
30
41
|
} from "../../shared/utils.js";
|
|
31
42
|
import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.js";
|
|
32
43
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.js";
|
|
33
|
-
import {
|
|
44
|
+
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, resolveToolTimeoutMs, toolTimeoutCallKey, toolTimeoutFromEnv } from "../shared/tool-timeout.js";
|
|
45
|
+
import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.js";
|
|
46
|
+
import { planCompletionEvidence } from "../shared/completion-evidence.js";
|
|
47
|
+
import { planAbortRecovery } from "../shared/abort-recovery.js";
|
|
48
|
+
import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.js";
|
|
34
49
|
import { getPiSpawnCommand } from "../shared/dm-spawn.js";
|
|
50
|
+
import { preflightLaunchCwd } from "../shared/launch-cwd.js";
|
|
35
51
|
import { createJsonlWriter } from "../../shared/jsonl-writer.js";
|
|
52
|
+
import { createOrcaProgressTab } from "../shared/orca-progress-tabs.js";
|
|
36
53
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.js";
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
54
|
+
import { resolvePermissionRules } from "../shared/permissions.js";
|
|
55
|
+
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, deriveForkPromptCacheKey, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/dm-args.js";
|
|
56
|
+
import { deriveChildSessionName } from "../../shared/child-session-name.js";
|
|
57
|
+
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.js";
|
|
58
|
+
import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV } from "../shared/capability-ceiling.js";
|
|
59
|
+
import { resolveEffectiveThinking } from "../../shared/model-info.js";
|
|
60
|
+
import { assertThinkingWithinCeiling, decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV } from "../../shared/thinking-ceiling.js";
|
|
61
|
+
import { MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.js";
|
|
62
|
+
import { formatMidToolExitError, formatProcessSignalError, isOrdinaryToolForMidToolExit, isUnexplainedProcessSignal } from "../shared/process-signal.js";
|
|
63
|
+
import { readChildToolDiagnosticError } from "../shared/tool-availability.js";
|
|
64
|
+
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.js";
|
|
65
|
+
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, hasSingleOutputChangedSinceSnapshot, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode } from "../shared/single-output.js";
|
|
40
66
|
import {
|
|
41
67
|
buildModelCandidates,
|
|
68
|
+
formatSubagentModelVerificationError,
|
|
42
69
|
formatModelAttemptNote,
|
|
43
|
-
|
|
70
|
+
isContextOverflow,
|
|
71
|
+
isRetryableModelFailureAttempt,
|
|
72
|
+
recordRetryableModelFailure
|
|
44
73
|
} from "../shared/model-fallback.js";
|
|
74
|
+
import {
|
|
75
|
+
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
76
|
+
formatSubagentExtensionConflictError,
|
|
77
|
+
formatSubagentStartupRetryExhaustedError,
|
|
78
|
+
formatSubagentStartupRetryNote,
|
|
79
|
+
isRetryableSubagentStartupFailure,
|
|
80
|
+
waitForSubagentStartupRetry
|
|
81
|
+
} from "../shared/subagent-startup-retry.js";
|
|
45
82
|
import {
|
|
46
83
|
createMutatingFailureState,
|
|
47
84
|
didMutatingToolFail,
|
|
@@ -53,14 +90,37 @@ import {
|
|
|
53
90
|
shouldEscalateMutatingFailures,
|
|
54
91
|
summarizeRecentMutatingFailures
|
|
55
92
|
} from "../shared/long-running-guard.js";
|
|
56
|
-
import { acceptanceFailureMessage, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport } from "../shared/acceptance.js";
|
|
57
|
-
import {
|
|
93
|
+
import { acceptanceFailureMessage, buildSkippedAcceptanceLedger, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport, validateAcceptanceInput } from "../shared/acceptance.js";
|
|
94
|
+
import { PROMPT_REDACTED } from "../../shared/utils.js";
|
|
95
|
+
import { attachContractProjections, isAgentContractV1 } from "../shared/agent-contract.js";
|
|
58
96
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.js";
|
|
97
|
+
import { resolveWatchdogConfig } from "../../watchdog/settings.js";
|
|
98
|
+
import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.js";
|
|
99
|
+
import { consumeWorkflowChildPermit } from "../../shared/workflow-child-permit.js";
|
|
100
|
+
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, PI_AGGREGATE_EVENT_PROJECTOR, projectChildLifecycle } from "../shared/child-protocol.js";
|
|
101
|
+
import {
|
|
102
|
+
acceptChildWatchdogEvent,
|
|
103
|
+
childWatchdogIsActive,
|
|
104
|
+
isChildWatchdogStatusEvent,
|
|
105
|
+
resolveChildWatchdogConfig
|
|
106
|
+
} from "../../watchdog/child-status.js";
|
|
59
107
|
const artifactOutputByResult = new WeakMap;
|
|
60
108
|
const acceptanceOutputByResult = new WeakMap;
|
|
61
109
|
function emptyUsage() {
|
|
62
110
|
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
63
111
|
}
|
|
112
|
+
function withRunContext(result, context) {
|
|
113
|
+
if (!context)
|
|
114
|
+
return result;
|
|
115
|
+
result.context = context;
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
function redactResultPrompt(result) {
|
|
119
|
+
result.task = PROMPT_REDACTED;
|
|
120
|
+
if (result.progress)
|
|
121
|
+
result.progress.task = PROMPT_REDACTED;
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
64
124
|
function sumUsage(target, source) {
|
|
65
125
|
target.input += source.input;
|
|
66
126
|
target.output += source.output;
|
|
@@ -69,6 +129,40 @@ function sumUsage(target, source) {
|
|
|
69
129
|
target.cost += source.cost;
|
|
70
130
|
target.turns += source.turns;
|
|
71
131
|
}
|
|
132
|
+
function persistSingleResultMetadata(input) {
|
|
133
|
+
if (!input.enabled || !input.metadataPath)
|
|
134
|
+
return;
|
|
135
|
+
const target = input.result;
|
|
136
|
+
writeMetadata(input.metadataPath, {
|
|
137
|
+
runId: input.runId,
|
|
138
|
+
agent: input.agent,
|
|
139
|
+
task: PROMPT_REDACTED,
|
|
140
|
+
exitCode: target.exitCode,
|
|
141
|
+
processSignal: target.processSignal,
|
|
142
|
+
usage: target.usage,
|
|
143
|
+
model: target.model,
|
|
144
|
+
attemptedModels: target.attemptedModels,
|
|
145
|
+
modelAttempts: target.modelAttempts,
|
|
146
|
+
durationMs: target.progressSummary?.durationMs,
|
|
147
|
+
toolCount: target.progressSummary?.toolCount,
|
|
148
|
+
error: target.error,
|
|
149
|
+
agentContract: target.agentContract,
|
|
150
|
+
launchContractDigest: target.launchContractDigest,
|
|
151
|
+
launchResolvedExtensions: target.launchResolvedExtensions,
|
|
152
|
+
runtimeAcknowledgedExtensions: target.runtimeAcknowledgedExtensions,
|
|
153
|
+
execution: target.execution,
|
|
154
|
+
acceptance: target.acceptance,
|
|
155
|
+
capabilityCeiling: target.capabilityCeiling,
|
|
156
|
+
capabilityAudit: target.capabilityAudit,
|
|
157
|
+
review: target.review,
|
|
158
|
+
effects: target.effects,
|
|
159
|
+
transcriptPath: target.transcriptPath,
|
|
160
|
+
transcriptError: target.transcriptError,
|
|
161
|
+
skills: target.skills,
|
|
162
|
+
skillsWarning: target.skillsWarning,
|
|
163
|
+
timestamp: Date.now()
|
|
164
|
+
});
|
|
165
|
+
}
|
|
72
166
|
function formatTimeoutMessage(timeoutMs) {
|
|
73
167
|
return `Subagent timed out after ${timeoutMs}ms.`;
|
|
74
168
|
}
|
|
@@ -82,17 +176,32 @@ function resolveAttemptTimeout(options) {
|
|
|
82
176
|
message: formatTimeoutMessage(options.timeoutMs)
|
|
83
177
|
};
|
|
84
178
|
}
|
|
85
|
-
function
|
|
179
|
+
function buildPendingAcceptanceLedger(acceptance) {
|
|
86
180
|
return {
|
|
87
|
-
status:
|
|
181
|
+
status: "pending",
|
|
182
|
+
evidenceStatus: "pending",
|
|
88
183
|
explicit: acceptance.explicit,
|
|
89
184
|
effectiveAcceptance: acceptance,
|
|
90
185
|
inferredReason: acceptance.inferredReason,
|
|
91
186
|
criteria: acceptance.criteria,
|
|
92
|
-
runtimeChecks:
|
|
187
|
+
runtimeChecks: [],
|
|
93
188
|
verifyRuns: []
|
|
94
189
|
};
|
|
95
190
|
}
|
|
191
|
+
function buildInterruptedAcceptanceLedger(acceptance) {
|
|
192
|
+
const pending = buildPendingAcceptanceLedger(acceptance);
|
|
193
|
+
if (acceptance.level === "none") {
|
|
194
|
+
pending.status = "not-required";
|
|
195
|
+
pending.evidenceStatus = "not-required";
|
|
196
|
+
} else {
|
|
197
|
+
pending.runtimeChecks = [{
|
|
198
|
+
id: "interrupted",
|
|
199
|
+
status: "not-applicable",
|
|
200
|
+
message: "Acceptance was not evaluated because the subagent was interrupted."
|
|
201
|
+
}];
|
|
202
|
+
}
|
|
203
|
+
return pending;
|
|
204
|
+
}
|
|
96
205
|
function appendRecentOutput(progress, lines) {
|
|
97
206
|
if (lines.length === 0)
|
|
98
207
|
return;
|
|
@@ -115,14 +224,16 @@ function stripAcceptanceReportsFromMessages(messages) {
|
|
|
115
224
|
function snapshotProgress(progress) {
|
|
116
225
|
return {
|
|
117
226
|
...progress,
|
|
227
|
+
task: PROMPT_REDACTED,
|
|
118
228
|
skills: progress.skills ? [...progress.skills] : undefined,
|
|
119
|
-
recentTools: progress.recentTools
|
|
120
|
-
recentOutput:
|
|
229
|
+
recentTools: boundStreamedRecentTools(progress.recentTools),
|
|
230
|
+
recentOutput: boundStreamedRecentOutput(progress.recentOutput)
|
|
121
231
|
};
|
|
122
232
|
}
|
|
123
233
|
function snapshotResult(result, progress) {
|
|
124
234
|
return {
|
|
125
235
|
...result,
|
|
236
|
+
task: PROMPT_REDACTED,
|
|
126
237
|
messages: result.outputMode === "file-only" && result.savedOutputPath ? undefined : result.messages ? [...result.messages] : undefined,
|
|
127
238
|
usage: { ...result.usage },
|
|
128
239
|
skills: result.skills ? [...result.skills] : undefined,
|
|
@@ -139,12 +250,33 @@ function snapshotResult(result, progress) {
|
|
|
139
250
|
outputReference: result.outputReference ? { ...result.outputReference } : undefined
|
|
140
251
|
};
|
|
141
252
|
}
|
|
253
|
+
function snapshotStreamResult(result, progress) {
|
|
254
|
+
const snapshot = snapshotResult(result, progress);
|
|
255
|
+
snapshot.messages = undefined;
|
|
256
|
+
snapshot.toolCalls = boundStreamedToolCalls(result);
|
|
257
|
+
return snapshot;
|
|
258
|
+
}
|
|
259
|
+
const AFTER_COMPACTION_SETTLEMENT = Symbol("afterCompactionSettlement");
|
|
142
260
|
async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared) {
|
|
143
261
|
const effectiveThinking = options.thinkingOverride ?? agent.thinking;
|
|
144
262
|
const modelArg = applyThinkingSuffix(model, effectiveThinking, options.thinkingOverride !== undefined);
|
|
145
|
-
|
|
263
|
+
assertThinkingWithinCeiling({ model: modelArg, configThinking: effectiveThinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
264
|
+
const expectedModelForVerification = shared.verifyModel ? modelArg : undefined;
|
|
265
|
+
const resolvedThinking = resolveEffectiveThinking(modelArg, effectiveThinking);
|
|
266
|
+
const childSessionName = deriveChildSessionName({ agent: agent.name, task: shared.originalTask ?? task });
|
|
267
|
+
const watchdogConfig = resolveWatchdogConfig(options.cwd ?? runtimeCwd);
|
|
268
|
+
const childWatchdog = watchdogConfig.ok ? resolveChildWatchdogConfig({
|
|
269
|
+
config: watchdogConfig.config,
|
|
270
|
+
agent: agent.name,
|
|
271
|
+
runId: options.runId,
|
|
272
|
+
childIndex: options.index ?? 0
|
|
273
|
+
}) : undefined;
|
|
274
|
+
const permissionRules = resolvePermissionRules(options.permissions, agent.permissions);
|
|
275
|
+
const permissionAuditPath = permissionRules && options.artifactsDir ? path.join(options.artifactsDir, "permission-audit", `${options.runId}-${options.index ?? 0}.jsonl`) : undefined;
|
|
276
|
+
const { args, env: sharedEnv, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit, warnings } = buildPiArgs({
|
|
146
277
|
baseArgs: ["--mode", "json", "-p"],
|
|
147
278
|
task,
|
|
279
|
+
taskDelivery: shared.taskDelivery,
|
|
148
280
|
sessionEnabled: shared.sessionEnabled,
|
|
149
281
|
sessionDir: options.sessionDir,
|
|
150
282
|
sessionFile: options.sessionFile,
|
|
@@ -152,16 +284,19 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
152
284
|
thinking: effectiveThinking,
|
|
153
285
|
systemPromptMode: agent.systemPromptMode,
|
|
154
286
|
inheritProjectContext: agent.inheritProjectContext,
|
|
287
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
155
288
|
inheritSkills: agent.inheritSkills,
|
|
156
289
|
requireReadTool: Boolean(shared.resolvedSkillNames?.length),
|
|
157
290
|
tools: agent.tools,
|
|
291
|
+
allowNestedSubagents: agent.allowNestedSubagents,
|
|
158
292
|
extensions: agent.extensions,
|
|
159
293
|
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
160
|
-
systemPrompt:
|
|
294
|
+
systemPrompt: shared.systemPrompt,
|
|
161
295
|
mcpDirectTools: agent.mcpDirectTools,
|
|
162
296
|
cwd: options.cwd ?? runtimeCwd,
|
|
163
297
|
promptFileStem: agent.name,
|
|
164
298
|
intercomSessionName: options.intercomSessionName,
|
|
299
|
+
sessionName: childSessionName,
|
|
165
300
|
orchestratorIntercomTarget: options.orchestratorIntercomTarget,
|
|
166
301
|
runId: options.runId,
|
|
167
302
|
childAgentName: agent.name,
|
|
@@ -170,24 +305,127 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
170
305
|
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
171
306
|
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
172
307
|
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
308
|
+
runFanoutBudget: options.runFanoutBudget,
|
|
173
309
|
parentSessionId: options.parentSessionId,
|
|
310
|
+
forkCacheKey: options.context === "fork" ? deriveForkPromptCacheKey(options.parentSessionId) : undefined,
|
|
311
|
+
steerInboxDir: options.steerInboxDir,
|
|
312
|
+
steerCapabilityPath: options.steerCapabilityPath,
|
|
313
|
+
steerAckDir: options.steerAckDir,
|
|
174
314
|
structuredOutput: options.structuredOutput,
|
|
175
|
-
|
|
315
|
+
fast: options.fast ?? agent.fast,
|
|
316
|
+
modelCandidates: shared.modelCandidates,
|
|
317
|
+
toolBudget: options.toolBudget,
|
|
318
|
+
allowZeroToolBudget: options.allowZeroToolBudget,
|
|
319
|
+
permissionRules,
|
|
320
|
+
permissionAuditPath,
|
|
321
|
+
childWatchdog,
|
|
322
|
+
waitToolEnabled: options.waitToolEnabled,
|
|
323
|
+
waitToolDefaultTimeoutMs: options.waitToolDefaultTimeoutMs,
|
|
324
|
+
capabilityCeiling: options.capabilityCeiling,
|
|
325
|
+
thinkingCeiling: options.thinkingCeiling,
|
|
326
|
+
extensionBindings: options.extensionBindings,
|
|
327
|
+
runtimeSnapshotHost: options.runtimeSnapshotHost
|
|
328
|
+
});
|
|
329
|
+
if (!shared.launchWarnings.emitted && warnings.length > 0) {
|
|
330
|
+
for (const warning of warnings)
|
|
331
|
+
console.warn(`[dm-subagents] ${warning}`);
|
|
332
|
+
shared.launchWarnings.emitted = true;
|
|
333
|
+
}
|
|
334
|
+
const effectiveSystemPrompt = shared.systemPrompt;
|
|
335
|
+
const toolPlan = resolvePiLaunchToolPlan({
|
|
336
|
+
tools: agent.tools,
|
|
337
|
+
allowNestedSubagents: agent.allowNestedSubagents,
|
|
338
|
+
extensions: agent.extensions,
|
|
339
|
+
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
340
|
+
mcpDirectTools: agent.mcpDirectTools,
|
|
341
|
+
cwd: options.cwd ?? runtimeCwd,
|
|
342
|
+
requireReadTool: Boolean(shared.resolvedSkillNames?.length),
|
|
343
|
+
structuredOutput: Boolean(options.structuredOutput),
|
|
344
|
+
fast: options.fast ?? agent.fast,
|
|
345
|
+
model: modelArg,
|
|
346
|
+
modelCandidates: shared.modelCandidates,
|
|
347
|
+
capabilityCeiling: options.capabilityCeiling,
|
|
348
|
+
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
349
|
+
agentName: agent.name,
|
|
350
|
+
permissionRules,
|
|
351
|
+
runtimeSnapshotHost: options.runtimeSnapshotHost
|
|
176
352
|
});
|
|
177
|
-
const
|
|
353
|
+
const contractTools = toolPlan.explicitToolAllowlist ? toolPlan.effectiveToolAllowlist : undefined;
|
|
354
|
+
const contractError = validateImplementationToolContract({
|
|
178
355
|
agent: agent.name,
|
|
179
356
|
task: shared.originalTask ?? task,
|
|
357
|
+
tools: contractTools,
|
|
358
|
+
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
359
|
+
configuredExtensions: toolPlan.configuredExtensions,
|
|
360
|
+
requestedTools: toolPlan.requestedBuiltinTools,
|
|
361
|
+
acceptanceRole: agent.acceptanceRole,
|
|
362
|
+
completionGuard: agent.completionGuard
|
|
363
|
+
});
|
|
364
|
+
if (contractError) {
|
|
365
|
+
cleanupTempDir(tempDir);
|
|
366
|
+
return {
|
|
367
|
+
index: options.index ?? 0,
|
|
368
|
+
agent: agent.name,
|
|
369
|
+
task,
|
|
370
|
+
...childSessionName ? { sessionName: childSessionName } : {},
|
|
371
|
+
messages: [],
|
|
372
|
+
finalOutput: "",
|
|
373
|
+
exitCode: 1,
|
|
374
|
+
error: contractError,
|
|
375
|
+
usage: emptyUsage(),
|
|
376
|
+
model: modelArg,
|
|
377
|
+
modelAttempts: [],
|
|
378
|
+
attemptedModels: [],
|
|
379
|
+
progressSummary: { status: "failed", toolCount: 0, tokens: 0, durationMs: 0 },
|
|
380
|
+
...toolPlan.capabilityCeiling ? { capabilityCeiling: toolPlan.capabilityCeiling } : {},
|
|
381
|
+
...toolPlan.capabilityAudit ? { capabilityAudit: toolPlan.capabilityAudit } : {}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
const launchResolvedExtensions = projectLaunchResolvedChildExtensions(toolPlan);
|
|
385
|
+
const launchContractDigest = launchBindingDigest({
|
|
386
|
+
definitionDigest: agentDefinitionDigest(agent),
|
|
387
|
+
task: shared.originalTask ?? task,
|
|
388
|
+
...modelArg ? { model: modelArg } : {},
|
|
389
|
+
modelCandidates: shared.modelCandidates,
|
|
390
|
+
...(options.fast ?? agent.fast) !== undefined ? { fast: options.fast ?? agent.fast } : {},
|
|
391
|
+
...resolvedThinking ? { thinking: resolvedThinking } : {},
|
|
392
|
+
...options.thinkingCeiling ? { thinkingCeiling: options.thinkingCeiling } : {},
|
|
393
|
+
systemPrompt: effectiveSystemPrompt,
|
|
394
|
+
systemPromptMode: agent.systemPromptMode,
|
|
395
|
+
inheritProjectContext: agent.inheritProjectContext,
|
|
396
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
397
|
+
inheritSkills: agent.inheritSkills,
|
|
398
|
+
skills: shared.resolvedSkillNames ?? [],
|
|
399
|
+
tools: toolPlan.effectiveToolAllowlist,
|
|
400
|
+
extensions: toolPlan.extensionArgs,
|
|
401
|
+
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
402
|
+
...options.outputPath ? { outputPath: options.outputPath } : {},
|
|
403
|
+
outputMode: options.outputMode ?? "inline",
|
|
404
|
+
...options.structuredOutput ? { structuredOutputSchema: options.structuredOutput.schema } : {},
|
|
405
|
+
...options.extensionBindings ? { extensionBindings: options.extensionBindings } : {}
|
|
406
|
+
});
|
|
407
|
+
const result = withRunContext({
|
|
408
|
+
index: options.index ?? 0,
|
|
409
|
+
agent: agent.name,
|
|
410
|
+
task: shared.originalTask ?? task,
|
|
411
|
+
...childSessionName ? { sessionName: childSessionName } : {},
|
|
412
|
+
...options.agentContract ? { agentContract: options.agentContract } : {},
|
|
413
|
+
launchContractDigest,
|
|
414
|
+
launchResolvedExtensions,
|
|
180
415
|
exitCode: 0,
|
|
416
|
+
outputState: "absent",
|
|
181
417
|
messages: [],
|
|
182
418
|
usage: emptyUsage(),
|
|
183
419
|
model: modelArg,
|
|
420
|
+
...resolvedThinking ? { thinking: resolvedThinking } : {},
|
|
184
421
|
artifactPaths: shared.artifactPaths,
|
|
185
422
|
transcriptPath: shared.transcriptWriter ? shared.artifactPaths?.transcriptPath : undefined,
|
|
186
423
|
skills: shared.resolvedSkillNames,
|
|
187
424
|
skillsWarning: shared.skillsWarning,
|
|
188
|
-
...options.
|
|
189
|
-
...options.
|
|
190
|
-
|
|
425
|
+
...options.toolBudget ? { toolBudget: initialToolBudgetState(options.toolBudget) } : {},
|
|
426
|
+
...options.capabilityCeiling ? { capabilityCeiling: options.capabilityCeiling } : {},
|
|
427
|
+
...capabilityAudit ? { capabilityAudit } : {}
|
|
428
|
+
}, options.context);
|
|
191
429
|
const startTime = Date.now();
|
|
192
430
|
if (options.structuredOutput) {
|
|
193
431
|
try {
|
|
@@ -212,6 +450,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
212
450
|
const progress = {
|
|
213
451
|
index: options.index ?? 0,
|
|
214
452
|
agent: agent.name,
|
|
453
|
+
...childSessionName ? { sessionName: childSessionName } : {},
|
|
215
454
|
status: "running",
|
|
216
455
|
task,
|
|
217
456
|
skills: shared.resolvedSkillNames,
|
|
@@ -219,12 +458,17 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
219
458
|
recentOutput: [...shared.attemptNotes],
|
|
220
459
|
toolCount: 0,
|
|
221
460
|
tokens: 0,
|
|
461
|
+
...modelArg ? { model: modelArg } : {},
|
|
462
|
+
...resolvedThinking ? { thinking: resolvedThinking } : {},
|
|
463
|
+
inputTokens: 0,
|
|
464
|
+
outputTokens: 0,
|
|
222
465
|
durationMs: 0,
|
|
223
466
|
lastActivityAt: startTime
|
|
224
467
|
};
|
|
225
468
|
result.progress = progress;
|
|
226
469
|
const attemptTimeout = resolveAttemptTimeout(options);
|
|
227
470
|
if (attemptTimeout?.remainingMs === 0) {
|
|
471
|
+
cleanupTempDir(tempDir);
|
|
228
472
|
result.exitCode = 1;
|
|
229
473
|
result.timedOut = true;
|
|
230
474
|
result.error = attemptTimeout.message;
|
|
@@ -239,9 +483,34 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
239
483
|
return result;
|
|
240
484
|
}
|
|
241
485
|
const spawnEnv = { ...process.env, ...sharedEnv, ...getSubagentDepthEnv(options.maxSubagentDepth) };
|
|
486
|
+
const mutationSnapshot = snapshotTrackedMutations(options.cwd ?? runtimeCwd);
|
|
242
487
|
let observedMutationAttempt = false;
|
|
488
|
+
let structuredOutputToolInvoked = false;
|
|
489
|
+
let structuredOutputMessageStartIndex;
|
|
490
|
+
let toolAvailabilityError;
|
|
491
|
+
let abortedBySignal = options.signal?.aborted === true;
|
|
492
|
+
const spawnSpec = getPiSpawnCommand(args);
|
|
493
|
+
if (options.workflowChildPermitLaunch) {
|
|
494
|
+
const permitError = consumeWorkflowChildPermit(options.workflowChildPermitLaunch.permit, {
|
|
495
|
+
workflowRunId: options.workflowChildPermitLaunch.workflowRunId,
|
|
496
|
+
childKey: options.workflowChildPermitLaunch.childKey,
|
|
497
|
+
agent: agent.name,
|
|
498
|
+
launchContractDigest,
|
|
499
|
+
context: options.context ?? "fresh",
|
|
500
|
+
runner: "dm"
|
|
501
|
+
});
|
|
502
|
+
if (permitError) {
|
|
503
|
+
cleanupTempDir(tempDir);
|
|
504
|
+
result.exitCode = 1;
|
|
505
|
+
result.error = permitError;
|
|
506
|
+
result.finalOutput = permitError;
|
|
507
|
+
progress.status = "failed";
|
|
508
|
+
progress.error = permitError;
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
let afterCompactionSettlement = false;
|
|
243
513
|
const exitCode = await new Promise((resolve) => {
|
|
244
|
-
const spawnSpec = getPiSpawnCommand(args);
|
|
245
514
|
const proc = spawn(spawnSpec.command, spawnSpec.args, {
|
|
246
515
|
cwd: options.cwd ?? runtimeCwd,
|
|
247
516
|
env: spawnEnv,
|
|
@@ -249,9 +518,8 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
249
518
|
windowsHide: true
|
|
250
519
|
});
|
|
251
520
|
const jsonlWriter = createJsonlWriter(shared.jsonlPath, proc.stdout);
|
|
252
|
-
let buf = "";
|
|
253
521
|
let processClosed = false;
|
|
254
|
-
let
|
|
522
|
+
let lifecycleFinished = false;
|
|
255
523
|
let detached = false;
|
|
256
524
|
let intercomStarted = false;
|
|
257
525
|
let assistantError;
|
|
@@ -261,19 +529,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
261
529
|
let timeoutTimer;
|
|
262
530
|
let timeoutTerminationTimer;
|
|
263
531
|
let timeoutHardKillTimer;
|
|
264
|
-
let
|
|
265
|
-
let turnBudgetTerminationTimer;
|
|
266
|
-
let turnBudgetHardKillTimer;
|
|
267
|
-
const clearTurnBudgetTimers = () => {
|
|
268
|
-
if (turnBudgetTerminationTimer) {
|
|
269
|
-
clearTimeout(turnBudgetTerminationTimer);
|
|
270
|
-
turnBudgetTerminationTimer = undefined;
|
|
271
|
-
}
|
|
272
|
-
if (turnBudgetHardKillTimer) {
|
|
273
|
-
clearTimeout(turnBudgetHardKillTimer);
|
|
274
|
-
turnBudgetHardKillTimer = undefined;
|
|
275
|
-
}
|
|
276
|
-
};
|
|
532
|
+
let protocolHardKillTimer;
|
|
277
533
|
const clearTimeoutTimers = () => {
|
|
278
534
|
if (timeoutTimer) {
|
|
279
535
|
clearTimeout(timeoutTimer);
|
|
@@ -288,27 +544,59 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
288
544
|
timeoutHardKillTimer = undefined;
|
|
289
545
|
}
|
|
290
546
|
};
|
|
291
|
-
const
|
|
292
|
-
detached
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
547
|
+
const detachForeground = (reason) => {
|
|
548
|
+
if (detached || processClosed || lifecycleFinished || options.signal?.aborted)
|
|
549
|
+
return false;
|
|
550
|
+
const receiptProgress = snapshotProgress(progress);
|
|
551
|
+
receiptProgress.status = "detached";
|
|
552
|
+
receiptProgress.durationMs = Date.now() - startTime;
|
|
553
|
+
const receipt = snapshotResult(result, receiptProgress);
|
|
554
|
+
receipt.exitCode = -2;
|
|
555
|
+
receipt.detached = true;
|
|
556
|
+
receipt.detachedReason = reason;
|
|
557
|
+
receipt.finalOutput = reason === "intercom coordination" ? "Detached for intercom coordination before task completion." : reason === "user request" ? "Detached at user request before task completion." : `Detached for ${reason} before task completion.`;
|
|
558
|
+
receipt.outputMode = options.outputMode ?? "inline";
|
|
559
|
+
if (options.outputPath) {
|
|
560
|
+
receipt.outputSaveError = reason === "user request" ? "Output file was not finalized because the subagent detached at user request." : `Output file was not finalized because the subagent detached for ${reason}.`;
|
|
561
|
+
}
|
|
562
|
+
receipt.progressSummary = {
|
|
563
|
+
toolCount: receiptProgress.toolCount,
|
|
564
|
+
tokens: receiptProgress.tokens,
|
|
565
|
+
durationMs: receiptProgress.durationMs
|
|
302
566
|
};
|
|
303
|
-
|
|
567
|
+
let accepted = false;
|
|
568
|
+
try {
|
|
569
|
+
accepted = options.onDetachReceipt?.(receipt) === true;
|
|
570
|
+
} catch {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
if (!accepted)
|
|
574
|
+
return false;
|
|
575
|
+
detached = true;
|
|
576
|
+
return true;
|
|
304
577
|
};
|
|
305
578
|
const FINAL_STOP_GRACE_MS = 1000;
|
|
306
579
|
const HARD_KILL_MS = 3000;
|
|
307
580
|
let childExited = false;
|
|
308
581
|
let forcedTerminationSignal = false;
|
|
309
582
|
let cleanTerminalAssistantStopReceived = false;
|
|
583
|
+
let agentSettledReceived = false;
|
|
584
|
+
let compactionStartedReceived = false;
|
|
310
585
|
let finalDrainTimer;
|
|
311
586
|
let finalHardKillTimer;
|
|
587
|
+
let watchdogTailTimer;
|
|
588
|
+
let childWatchdogState;
|
|
589
|
+
const updateChildWatchdogState = (snapshot) => {
|
|
590
|
+
childWatchdogState = snapshot;
|
|
591
|
+
result.watchdog = snapshot;
|
|
592
|
+
progress.watchdog = snapshot;
|
|
593
|
+
};
|
|
594
|
+
const clearWatchdogTailTimer = () => {
|
|
595
|
+
if (watchdogTailTimer) {
|
|
596
|
+
clearTimeout(watchdogTailTimer);
|
|
597
|
+
watchdogTailTimer = undefined;
|
|
598
|
+
}
|
|
599
|
+
};
|
|
312
600
|
const clearFinalDrainTimers = () => {
|
|
313
601
|
if (finalDrainTimer) {
|
|
314
602
|
clearTimeout(finalDrainTimer);
|
|
@@ -320,20 +608,24 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
320
608
|
}
|
|
321
609
|
};
|
|
322
610
|
const startFinalDrain = () => {
|
|
323
|
-
if (
|
|
611
|
+
if (childWatchdogIsActive(childWatchdogState)) {
|
|
612
|
+
armWatchdogTail();
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (childExited || finalDrainTimer || lifecycleFinished || processClosed)
|
|
324
616
|
return;
|
|
325
617
|
finalDrainTimer = setTimeout(() => {
|
|
326
|
-
if (
|
|
618
|
+
if (lifecycleFinished || processClosed)
|
|
327
619
|
return;
|
|
328
620
|
const termSent = trySignalChild(proc, "SIGTERM");
|
|
329
621
|
if (!termSent)
|
|
330
622
|
return;
|
|
331
623
|
forcedTerminationSignal = true;
|
|
332
|
-
if (!cleanTerminalAssistantStopReceived && !assistantError) {
|
|
333
|
-
result.error = result.error ?? `Subagent process did not exit within ${FINAL_STOP_GRACE_MS}ms after its
|
|
624
|
+
if (!cleanTerminalAssistantStopReceived && !agentSettledReceived && !assistantError) {
|
|
625
|
+
result.error = result.error ?? `Subagent process did not exit within ${FINAL_STOP_GRACE_MS}ms after its terminal event. Forcing termination.`;
|
|
334
626
|
}
|
|
335
627
|
finalHardKillTimer = setTimeout(() => {
|
|
336
|
-
if (
|
|
628
|
+
if (lifecycleFinished || processClosed)
|
|
337
629
|
return;
|
|
338
630
|
forcedTerminationSignal = trySignalChild(proc, "SIGKILL") || forcedTerminationSignal;
|
|
339
631
|
}, HARD_KILL_MS);
|
|
@@ -341,25 +633,69 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
341
633
|
}, FINAL_STOP_GRACE_MS);
|
|
342
634
|
finalDrainTimer.unref?.();
|
|
343
635
|
};
|
|
636
|
+
function armWatchdogTail() {
|
|
637
|
+
if (!cleanTerminalAssistantStopReceived && !agentSettledReceived || watchdogTailTimer || lifecycleFinished || processClosed)
|
|
638
|
+
return;
|
|
639
|
+
watchdogTailTimer = setTimeout(() => {
|
|
640
|
+
watchdogTailTimer = undefined;
|
|
641
|
+
updateChildWatchdogState({
|
|
642
|
+
phase: "stale",
|
|
643
|
+
seq: (childWatchdogState?.seq ?? 0) + 1,
|
|
644
|
+
lastUpdate: Date.now(),
|
|
645
|
+
followUpPending: false,
|
|
646
|
+
reason: "child watchdog tail timeout",
|
|
647
|
+
timedOut: true
|
|
648
|
+
});
|
|
649
|
+
startFinalDrain();
|
|
650
|
+
fireUpdate();
|
|
651
|
+
}, childWatchdog?.watchdogTailTimeoutMs ?? 120000);
|
|
652
|
+
watchdogTailTimer.unref?.();
|
|
653
|
+
}
|
|
654
|
+
const applyChildLifecycle = (action) => {
|
|
655
|
+
if (action === "cancel-drain") {
|
|
656
|
+
clearFinalDrainTimers();
|
|
657
|
+
clearWatchdogTailTimer();
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (action === "start-drain")
|
|
661
|
+
startFinalDrain();
|
|
662
|
+
};
|
|
663
|
+
const childLifecycleState = { compactionRetryActive: false };
|
|
344
664
|
const unsubscribeIntercomDetach = options.intercomEvents?.on?.(INTERCOM_DETACH_REQUEST_EVENT, (payload) => {
|
|
345
|
-
if (!options.allowIntercomDetach ||
|
|
665
|
+
if (!options.allowIntercomDetach || processClosed)
|
|
346
666
|
return;
|
|
347
667
|
if (!payload || typeof payload !== "object")
|
|
348
668
|
return;
|
|
349
|
-
const
|
|
669
|
+
const event = payload;
|
|
670
|
+
const requestId = event.requestId;
|
|
350
671
|
if (typeof requestId !== "string" || requestId.length === 0)
|
|
351
672
|
return;
|
|
352
|
-
|
|
353
|
-
|
|
673
|
+
const hasRoute = event.runId !== undefined || event.agent !== undefined || event.childIndex !== undefined;
|
|
674
|
+
if (hasRoute) {
|
|
675
|
+
if (typeof event.runId === "string" && event.runId !== options.runId)
|
|
676
|
+
return;
|
|
677
|
+
if (typeof event.agent === "string" && event.agent !== agent.name)
|
|
678
|
+
return;
|
|
679
|
+
if (typeof event.childIndex === "number" && event.childIndex !== (options.index ?? 0))
|
|
680
|
+
return;
|
|
681
|
+
} else if (!intercomStarted)
|
|
682
|
+
return;
|
|
683
|
+
const accepted = detachForeground("intercom coordination");
|
|
684
|
+
options.intercomEvents?.emit(INTERCOM_DETACH_RESPONSE_EVENT, { requestId, accepted, runId: options.runId, agent: agent.name, childIndex: options.index ?? 0 });
|
|
354
685
|
});
|
|
355
686
|
const finish = (code) => {
|
|
356
|
-
if (
|
|
687
|
+
if (lifecycleFinished)
|
|
357
688
|
return;
|
|
358
|
-
|
|
689
|
+
lifecycleFinished = true;
|
|
359
690
|
clearFinalDrainTimers();
|
|
691
|
+
clearWatchdogTailTimer();
|
|
360
692
|
clearStdioGuard();
|
|
361
693
|
clearTimeoutTimers();
|
|
362
|
-
|
|
694
|
+
clearAllToolTimeouts();
|
|
695
|
+
if (protocolHardKillTimer) {
|
|
696
|
+
clearTimeout(protocolHardKillTimer);
|
|
697
|
+
protocolHardKillTimer = undefined;
|
|
698
|
+
}
|
|
363
699
|
if (activityTimer) {
|
|
364
700
|
clearInterval(activityTimer);
|
|
365
701
|
activityTimer = undefined;
|
|
@@ -378,6 +714,58 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
378
714
|
};
|
|
379
715
|
let activeLongRunningNotified = false;
|
|
380
716
|
let pendingToolResult;
|
|
717
|
+
let activeToolSequence = 0;
|
|
718
|
+
const activeToolCalls = new Map;
|
|
719
|
+
const activeToolKeysByName = new Map;
|
|
720
|
+
const latestActiveToolCall = () => [...activeToolCalls.values()].sort((left, right) => right.startedAt - left.startedAt)[0];
|
|
721
|
+
const refreshCurrentTool = () => {
|
|
722
|
+
const active = latestActiveToolCall();
|
|
723
|
+
if (!active) {
|
|
724
|
+
progress.currentTool = undefined;
|
|
725
|
+
progress.currentToolArgs = undefined;
|
|
726
|
+
progress.currentToolStartedAt = undefined;
|
|
727
|
+
progress.currentPath = undefined;
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
progress.currentTool = active.tool;
|
|
731
|
+
progress.currentToolArgs = active.args;
|
|
732
|
+
progress.currentToolStartedAt = active.startedAt;
|
|
733
|
+
progress.currentPath = active.path;
|
|
734
|
+
};
|
|
735
|
+
const recordActiveToolCall = (event, args, now) => {
|
|
736
|
+
const key = toolTimeoutCallKey(event, ++activeToolSequence);
|
|
737
|
+
const path = resolveCurrentPath(event.toolName, args);
|
|
738
|
+
const active = {
|
|
739
|
+
key,
|
|
740
|
+
tool: event.toolName,
|
|
741
|
+
args: extractToolArgsPreview(args),
|
|
742
|
+
startedAt: now,
|
|
743
|
+
...path !== undefined ? { path } : {}
|
|
744
|
+
};
|
|
745
|
+
activeToolCalls.set(key, active);
|
|
746
|
+
const keys = activeToolKeysByName.get(active.tool) ?? [];
|
|
747
|
+
keys.push(key);
|
|
748
|
+
activeToolKeysByName.set(active.tool, keys);
|
|
749
|
+
refreshCurrentTool();
|
|
750
|
+
return active;
|
|
751
|
+
};
|
|
752
|
+
const removeActiveToolCallKey = (key) => {
|
|
753
|
+
const active = activeToolCalls.get(key);
|
|
754
|
+
if (!active)
|
|
755
|
+
return;
|
|
756
|
+
activeToolCalls.delete(key);
|
|
757
|
+
const keys = activeToolKeysByName.get(active.tool)?.filter((candidate) => candidate !== key) ?? [];
|
|
758
|
+
if (keys.length > 0)
|
|
759
|
+
activeToolKeysByName.set(active.tool, keys);
|
|
760
|
+
else
|
|
761
|
+
activeToolKeysByName.delete(active.tool);
|
|
762
|
+
return active;
|
|
763
|
+
};
|
|
764
|
+
const removeActiveToolCall = (event) => {
|
|
765
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0 ? `id:${event.toolCallId}` : typeof event.toolName === "string" ? activeToolKeysByName.get(event.toolName)?.[0] : activeToolCalls.size === 1 ? [...activeToolCalls.keys()][0] : undefined;
|
|
766
|
+
return key ? removeActiveToolCallKey(key) : undefined;
|
|
767
|
+
};
|
|
768
|
+
const openToolAttentionTarget = (now) => [...activeToolCalls.values()].filter((active) => shouldEmitOpenToolAttention({ config: controlConfig, currentTool: active.tool, currentToolStartedAt: active.startedAt, now })).sort((left, right) => left.startedAt - right.startedAt)[0];
|
|
381
769
|
const mutatingFailures = createMutatingFailureState();
|
|
382
770
|
const mutatingFailureWindowMs = 5 * 60000;
|
|
383
771
|
const currentToolDurationMs = (now) => progress.currentToolStartedAt ? Math.max(0, now - progress.currentToolStartedAt) : undefined;
|
|
@@ -403,7 +791,8 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
403
791
|
currentTool: input.currentTool ?? progress.currentTool,
|
|
404
792
|
currentToolDurationMs: input.currentToolDurationMs ?? currentToolDurationMs(now),
|
|
405
793
|
currentPath: input.currentPath ?? progress.currentPath,
|
|
406
|
-
recentFailureSummary: input.recentFailureSummary
|
|
794
|
+
recentFailureSummary: input.recentFailureSummary,
|
|
795
|
+
taskPreview: task
|
|
407
796
|
});
|
|
408
797
|
emitControlEvent(event);
|
|
409
798
|
return previous !== "needs_attention";
|
|
@@ -430,56 +819,11 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
430
819
|
currentTool: progress.currentTool,
|
|
431
820
|
currentToolDurationMs: currentToolDurationMs(now),
|
|
432
821
|
currentPath: progress.currentPath,
|
|
433
|
-
elapsedMs: now - startTime
|
|
822
|
+
elapsedMs: now - startTime,
|
|
823
|
+
taskPreview: task
|
|
434
824
|
}));
|
|
435
825
|
return true;
|
|
436
826
|
};
|
|
437
|
-
const requestTurnBudgetAbort = (turnCount) => {
|
|
438
|
-
const budget = options.turnBudget;
|
|
439
|
-
if (!budget || result.timedOut || result.turnBudgetExceeded || interruptedByControl || processClosed || settled || detached)
|
|
440
|
-
return;
|
|
441
|
-
const message = turnBudgetExceededMessage(budget, turnCount);
|
|
442
|
-
result.turnBudgetExceeded = true;
|
|
443
|
-
result.wrapUpRequested = true;
|
|
444
|
-
result.turnBudget = turnBudgetState(budget, turnCount, true);
|
|
445
|
-
result.error = message;
|
|
446
|
-
result.finalOutput = message;
|
|
447
|
-
progress.status = "failed";
|
|
448
|
-
progress.error = message;
|
|
449
|
-
progress.durationMs = Date.now() - startTime;
|
|
450
|
-
fireUpdate();
|
|
451
|
-
trySignalChild(proc, "SIGINT");
|
|
452
|
-
turnBudgetTerminationTimer = setTimeout(() => {
|
|
453
|
-
if (processClosed || settled || detached || result.timedOut)
|
|
454
|
-
return;
|
|
455
|
-
trySignalChild(proc, "SIGTERM");
|
|
456
|
-
}, 1000);
|
|
457
|
-
turnBudgetTerminationTimer.unref?.();
|
|
458
|
-
turnBudgetHardKillTimer = setTimeout(() => {
|
|
459
|
-
if (processClosed || settled || detached || result.timedOut)
|
|
460
|
-
return;
|
|
461
|
-
trySignalChild(proc, "SIGKILL");
|
|
462
|
-
}, 4000);
|
|
463
|
-
turnBudgetHardKillTimer.unref?.();
|
|
464
|
-
};
|
|
465
|
-
const updateTurnBudget = (turnCount, terminalAssistantStop) => {
|
|
466
|
-
const budget = options.turnBudget;
|
|
467
|
-
if (!budget || result.timedOut || result.turnBudgetExceeded)
|
|
468
|
-
return;
|
|
469
|
-
if (turnCount < budget.maxTurns) {
|
|
470
|
-
result.turnBudget = { ...budget, outcome: "within-budget", turnCount };
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
if (!turnBudgetSoftReached) {
|
|
474
|
-
turnBudgetSoftReached = true;
|
|
475
|
-
result.wrapUpRequested = true;
|
|
476
|
-
appendRecentOutput(progress, [turnBudgetSoftNote(budget, turnCount)]);
|
|
477
|
-
}
|
|
478
|
-
result.turnBudget = turnBudgetState(budget, turnCount, false);
|
|
479
|
-
if (shouldAbortForTurnBudget(budget, turnCount, terminalAssistantStop)) {
|
|
480
|
-
requestTurnBudgetAbort(turnCount);
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
827
|
const updateActivityState = (now) => {
|
|
484
828
|
if (!controlConfig.enabled)
|
|
485
829
|
return false;
|
|
@@ -487,11 +831,24 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
487
831
|
config: controlConfig,
|
|
488
832
|
startedAt: startTime,
|
|
489
833
|
lastActivityAt: progress.lastActivityAt,
|
|
834
|
+
currentTool: progress.currentTool,
|
|
835
|
+
thinking: resolvedThinking,
|
|
490
836
|
now
|
|
491
837
|
});
|
|
492
838
|
if (idleState === "needs_attention") {
|
|
493
839
|
return progress.activityState === "needs_attention" ? false : emitNeedsAttention(now);
|
|
494
840
|
}
|
|
841
|
+
const toolAttentionTarget = progress.activityState !== "needs_attention" ? openToolAttentionTarget(now) : undefined;
|
|
842
|
+
if (toolAttentionTarget) {
|
|
843
|
+
const durationMs = Math.max(0, now - toolAttentionTarget.startedAt);
|
|
844
|
+
return emitNeedsAttention(now, {
|
|
845
|
+
message: `${agent.name} has had tool '${toolAttentionTarget.tool}' open for ${Math.floor(durationMs / 1000)}s`,
|
|
846
|
+
reason: "tool_open_threshold",
|
|
847
|
+
currentTool: toolAttentionTarget.tool,
|
|
848
|
+
currentPath: toolAttentionTarget.path,
|
|
849
|
+
currentToolDurationMs: durationMs
|
|
850
|
+
});
|
|
851
|
+
}
|
|
495
852
|
const activeReason = nextLongRunningTrigger(controlConfig, {
|
|
496
853
|
startedAt: startTime,
|
|
497
854
|
now,
|
|
@@ -504,7 +861,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
504
861
|
if (!options.onUpdate || processClosed)
|
|
505
862
|
return;
|
|
506
863
|
const progressSnapshot = snapshotProgress(progress);
|
|
507
|
-
const resultSnapshot =
|
|
864
|
+
const resultSnapshot = snapshotStreamResult(result, progressSnapshot);
|
|
508
865
|
const controlEvents = drainPendingControlEvents();
|
|
509
866
|
options.onUpdate({
|
|
510
867
|
content: [{ type: "text", text }],
|
|
@@ -520,9 +877,10 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
520
877
|
if (!options.onUpdate || processClosed)
|
|
521
878
|
return;
|
|
522
879
|
progress.durationMs = Date.now() - startTime;
|
|
523
|
-
const output =
|
|
880
|
+
const output = result.timedOut && result.finalOutput ? result.finalOutput : getFinalOutput(result.messages ?? []);
|
|
524
881
|
emitUpdateSnapshot(output || "(running...)");
|
|
525
882
|
};
|
|
883
|
+
const rawStdoutTail = createBoundedByteTail();
|
|
526
884
|
const processLine = (line) => {
|
|
527
885
|
if (!line.trim())
|
|
528
886
|
return;
|
|
@@ -531,49 +889,91 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
531
889
|
try {
|
|
532
890
|
evt = JSON.parse(line);
|
|
533
891
|
} catch {
|
|
892
|
+
rawStdoutTail.push(`${line}
|
|
893
|
+
`);
|
|
534
894
|
shared.transcriptWriter?.writeStdoutLine(line);
|
|
895
|
+
shared.orcaProgressTab?.append(`${line}
|
|
896
|
+
`);
|
|
535
897
|
return;
|
|
536
898
|
}
|
|
537
899
|
shared.transcriptWriter?.writeChildEvent(evt);
|
|
900
|
+
shared.orcaProgressTab?.event(evt);
|
|
901
|
+
if (evt.type === "compaction_start")
|
|
902
|
+
compactionStartedReceived = true;
|
|
903
|
+
if (evt.type === "compaction_end" && evt.willRetry === true) {
|
|
904
|
+
compactionStartedReceived = false;
|
|
905
|
+
afterCompactionSettlement = false;
|
|
906
|
+
}
|
|
907
|
+
if (evt.type === "agent_start" || evt.type === "auto_retry_start") {
|
|
908
|
+
compactionStartedReceived = false;
|
|
909
|
+
afterCompactionSettlement = false;
|
|
910
|
+
}
|
|
911
|
+
const lifecycleAction = projectChildLifecycle(evt, false, childLifecycleState);
|
|
912
|
+
if (evt.type === "agent_settled" && lifecycleAction === "start-drain") {
|
|
913
|
+
agentSettledReceived = true;
|
|
914
|
+
afterCompactionSettlement = compactionStartedReceived;
|
|
915
|
+
}
|
|
916
|
+
applyChildLifecycle(lifecycleAction);
|
|
917
|
+
if (isChildWatchdogStatusEvent(evt)) {
|
|
918
|
+
if (!childWatchdog)
|
|
919
|
+
return;
|
|
920
|
+
const next = acceptChildWatchdogEvent({
|
|
921
|
+
current: childWatchdogState,
|
|
922
|
+
event: evt,
|
|
923
|
+
runId: options.runId,
|
|
924
|
+
agent: agent.name,
|
|
925
|
+
childIndex: options.index ?? 0
|
|
926
|
+
});
|
|
927
|
+
if (!next)
|
|
928
|
+
return;
|
|
929
|
+
updateChildWatchdogState(next);
|
|
930
|
+
if (childWatchdogIsActive(next)) {
|
|
931
|
+
clearFinalDrainTimers();
|
|
932
|
+
armWatchdogTail();
|
|
933
|
+
} else {
|
|
934
|
+
clearWatchdogTailTimer();
|
|
935
|
+
if (cleanTerminalAssistantStopReceived || agentSettledReceived)
|
|
936
|
+
startFinalDrain();
|
|
937
|
+
}
|
|
938
|
+
fireUpdate();
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
538
941
|
const now = Date.now();
|
|
539
942
|
progress.durationMs = now - startTime;
|
|
540
943
|
progress.lastActivityAt = now;
|
|
541
944
|
updateActivityState(now);
|
|
542
945
|
if (evt.type === "tool_execution_start") {
|
|
543
946
|
const toolArgs = evt.args && typeof evt.args === "object" && !Array.isArray(evt.args) ? evt.args : {};
|
|
544
|
-
|
|
947
|
+
const activeTool = evt.toolName !== undefined ? recordActiveToolCall(evt, toolArgs, now) : undefined;
|
|
948
|
+
if (evt.toolName !== undefined)
|
|
949
|
+
armToolTimeout(evt);
|
|
950
|
+
if (options.structuredOutput && evt.toolName === "structured_output") {
|
|
951
|
+
structuredOutputToolInvoked = true;
|
|
952
|
+
structuredOutputMessageStartIndex = result.messages?.length ?? 0;
|
|
953
|
+
}
|
|
545
954
|
if (options.allowIntercomDetach && (evt.toolName === "intercom" || evt.toolName === "contact_supervisor")) {
|
|
546
955
|
intercomStarted = true;
|
|
547
|
-
shouldDetachForBlockingIntercom = evt.toolName === "intercom" && toolArgs.action === "ask" || evt.toolName === "contact_supervisor" && (toolArgs.reason === "need_decision" || toolArgs.reason === "interview_request");
|
|
548
956
|
}
|
|
549
957
|
progress.toolCount++;
|
|
550
958
|
if (options.toolBudget) {
|
|
551
959
|
result.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount);
|
|
552
960
|
}
|
|
553
|
-
|
|
554
|
-
progress.currentToolArgs = extractToolArgsPreview(toolArgs);
|
|
555
|
-
progress.currentToolStartedAt = now;
|
|
556
|
-
progress.currentPath = resolveCurrentPath(evt.toolName, toolArgs);
|
|
557
|
-
const mutates = isMutatingTool(evt.toolName, toolArgs);
|
|
961
|
+
const mutates = isMutatingTool(evt.toolName, toolArgs, agent.mutationTools);
|
|
558
962
|
observedMutationAttempt = observedMutationAttempt || mutates;
|
|
559
|
-
pendingToolResult = { tool: evt.toolName ?? "tool", path:
|
|
963
|
+
pendingToolResult = { tool: evt.toolName ?? "tool", path: activeTool?.path, mutates, startedAt: now };
|
|
560
964
|
fireUpdate();
|
|
561
|
-
if (shouldDetachForBlockingIntercom && !detached && !processClosed) {
|
|
562
|
-
detachForIntercom();
|
|
563
|
-
}
|
|
564
965
|
}
|
|
565
966
|
if (evt.type === "tool_execution_end") {
|
|
566
|
-
|
|
967
|
+
clearActiveToolTimeout(evt);
|
|
968
|
+
const endedTool = removeActiveToolCall(evt);
|
|
969
|
+
if (endedTool) {
|
|
567
970
|
progress.recentTools.push({
|
|
568
|
-
tool:
|
|
569
|
-
args:
|
|
971
|
+
tool: endedTool.tool,
|
|
972
|
+
args: endedTool.args,
|
|
570
973
|
endMs: now
|
|
571
974
|
});
|
|
572
975
|
}
|
|
573
|
-
|
|
574
|
-
progress.currentToolArgs = undefined;
|
|
575
|
-
progress.currentToolStartedAt = undefined;
|
|
576
|
-
progress.currentPath = undefined;
|
|
976
|
+
refreshCurrentTool();
|
|
577
977
|
fireUpdate();
|
|
578
978
|
}
|
|
579
979
|
if (evt.type === "message_end" && evt.message) {
|
|
@@ -581,21 +981,33 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
581
981
|
if (evt.message.role === "assistant") {
|
|
582
982
|
result.usage.turns++;
|
|
583
983
|
progress.turnCount = result.usage.turns;
|
|
584
|
-
const
|
|
585
|
-
const hasToolCall =
|
|
586
|
-
const terminalAssistantStop = stopReason === "stop" && !hasToolCall;
|
|
587
|
-
updateTurnBudget(result.usage.turns, terminalAssistantStop);
|
|
984
|
+
const toolCalls = Array.isArray(evt.message.content) ? evt.message.content.filter((part) => part.type === "toolCall") : [];
|
|
985
|
+
const hasToolCall = toolCalls.length > 0;
|
|
986
|
+
const terminalAssistantStop = evt.message.stopReason === "stop" && !hasToolCall;
|
|
588
987
|
const u = evt.message.usage;
|
|
589
988
|
if (u) {
|
|
989
|
+
const window = (u.input || 0) + (u.cacheRead || 0);
|
|
590
990
|
result.usage.input += u.input || 0;
|
|
591
991
|
result.usage.output += u.output || 0;
|
|
592
992
|
result.usage.cacheRead += u.cacheRead || 0;
|
|
593
993
|
result.usage.cacheWrite += u.cacheWrite || 0;
|
|
594
994
|
result.usage.cost += u.cost?.total || 0;
|
|
595
995
|
progress.tokens = result.usage.input + result.usage.output;
|
|
996
|
+
progress.inputTokens = result.usage.input;
|
|
997
|
+
progress.outputTokens = result.usage.output;
|
|
998
|
+
progress.window = window;
|
|
999
|
+
progress.windowPeak = Math.max(progress.windowPeak ?? 0, window);
|
|
1000
|
+
}
|
|
1001
|
+
if (evt.message.model) {
|
|
1002
|
+
progress.model = evt.message.model;
|
|
1003
|
+
if (!result.model)
|
|
1004
|
+
result.model = evt.message.model;
|
|
1005
|
+
if (expectedModelForVerification && !hasToolCall) {
|
|
1006
|
+
const modelVerificationError = formatSubagentModelVerificationError(expectedModelForVerification, evt.message.model, options.availableModels);
|
|
1007
|
+
if (modelVerificationError && !result.error)
|
|
1008
|
+
result.error = modelVerificationError;
|
|
1009
|
+
}
|
|
596
1010
|
}
|
|
597
|
-
if (!result.model && evt.message.model)
|
|
598
|
-
result.model = evt.message.model;
|
|
599
1011
|
if (evt.message.errorMessage)
|
|
600
1012
|
assistantError = evt.message.errorMessage;
|
|
601
1013
|
const assistantText = extractTextFromContent(evt.message.content);
|
|
@@ -605,13 +1017,31 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
605
1017
|
if (!evt.message.errorMessage && assistantText.trim())
|
|
606
1018
|
assistantError = undefined;
|
|
607
1019
|
cleanTerminalAssistantStopReceived ||= !evt.message.errorMessage;
|
|
608
|
-
|
|
1020
|
+
clearAllToolTimeouts();
|
|
1021
|
+
activeToolCalls.clear();
|
|
1022
|
+
activeToolKeysByName.clear();
|
|
1023
|
+
refreshCurrentTool();
|
|
1024
|
+
applyChildLifecycle(projectChildLifecycle(evt, true, childLifecycleState));
|
|
609
1025
|
}
|
|
610
1026
|
}
|
|
611
1027
|
updateActivityState(now);
|
|
612
1028
|
fireUpdate();
|
|
613
1029
|
}
|
|
614
1030
|
if (evt.type === "tool_result_end" && evt.message) {
|
|
1031
|
+
const toolResultCompletion = {
|
|
1032
|
+
toolCallId: evt.message.toolCallId ?? evt.toolCallId,
|
|
1033
|
+
toolName: evt.message.toolName ?? evt.toolName
|
|
1034
|
+
};
|
|
1035
|
+
clearActiveToolTimeout(toolResultCompletion);
|
|
1036
|
+
const endedTool = removeActiveToolCall(toolResultCompletion);
|
|
1037
|
+
if (endedTool) {
|
|
1038
|
+
progress.recentTools.push({
|
|
1039
|
+
tool: endedTool.tool,
|
|
1040
|
+
args: endedTool.args,
|
|
1041
|
+
endMs: now
|
|
1042
|
+
});
|
|
1043
|
+
refreshCurrentTool();
|
|
1044
|
+
}
|
|
615
1045
|
result.messages.push(evt.message);
|
|
616
1046
|
const resultText = extractTextFromContent(evt.message.content);
|
|
617
1047
|
if (options.toolBudget && pendingToolResult && resultText.includes("Tool budget hard limit reached")) {
|
|
@@ -646,23 +1076,23 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
646
1076
|
fireUpdate();
|
|
647
1077
|
}
|
|
648
1078
|
};
|
|
649
|
-
|
|
1079
|
+
fireUpdate();
|
|
1080
|
+
if (controlConfig.enabled || options.onUpdate) {
|
|
650
1081
|
activityTimer = setInterval(() => {
|
|
651
|
-
if (processClosed ||
|
|
1082
|
+
if (processClosed || lifecycleFinished) {
|
|
652
1083
|
return;
|
|
653
|
-
const now = Date.now();
|
|
654
|
-
if (updateActivityState(now)) {
|
|
655
|
-
progress.durationMs = now - startTime;
|
|
656
|
-
fireUpdate();
|
|
657
1084
|
}
|
|
1085
|
+
updateActivityState(Date.now());
|
|
1086
|
+
fireUpdate();
|
|
658
1087
|
}, 1000);
|
|
659
1088
|
activityTimer.unref?.();
|
|
660
1089
|
}
|
|
661
1090
|
if (attemptTimeout) {
|
|
662
1091
|
timeoutTimer = setTimeout(() => {
|
|
663
|
-
if (processClosed ||
|
|
1092
|
+
if (processClosed || lifecycleFinished || interruptedByControl)
|
|
664
1093
|
return;
|
|
665
1094
|
result.timedOut = true;
|
|
1095
|
+
clearAllToolTimeouts();
|
|
666
1096
|
result.error = attemptTimeout.message;
|
|
667
1097
|
result.finalOutput = attemptTimeout.message;
|
|
668
1098
|
progress.status = "failed";
|
|
@@ -671,13 +1101,13 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
671
1101
|
fireUpdate();
|
|
672
1102
|
trySignalChild(proc, "SIGINT");
|
|
673
1103
|
timeoutTerminationTimer = setTimeout(() => {
|
|
674
|
-
if (processClosed ||
|
|
1104
|
+
if (processClosed || lifecycleFinished)
|
|
675
1105
|
return;
|
|
676
1106
|
trySignalChild(proc, "SIGTERM");
|
|
677
1107
|
}, 1000);
|
|
678
1108
|
timeoutTerminationTimer.unref?.();
|
|
679
1109
|
timeoutHardKillTimer = setTimeout(() => {
|
|
680
|
-
if (processClosed ||
|
|
1110
|
+
if (processClosed || lifecycleFinished)
|
|
681
1111
|
return;
|
|
682
1112
|
trySignalChild(proc, "SIGKILL");
|
|
683
1113
|
}, 4000);
|
|
@@ -685,70 +1115,178 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
685
1115
|
}, attemptTimeout.remainingMs);
|
|
686
1116
|
timeoutTimer.unref?.();
|
|
687
1117
|
}
|
|
688
|
-
let
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
1118
|
+
let toolTimeoutSequence = 0;
|
|
1119
|
+
const activeToolTimeouts = new Map;
|
|
1120
|
+
const activeToolTimeoutKeysByName = new Map;
|
|
1121
|
+
let toolTimeoutTerminationTimer;
|
|
1122
|
+
let toolTimeoutHardKillTimer;
|
|
1123
|
+
const removeToolTimeoutKey = (key) => {
|
|
1124
|
+
const active = activeToolTimeouts.get(key);
|
|
1125
|
+
if (!active)
|
|
1126
|
+
return;
|
|
1127
|
+
clearTimeout(active.timer);
|
|
1128
|
+
activeToolTimeouts.delete(key);
|
|
1129
|
+
const keys = activeToolTimeoutKeysByName.get(active.toolName)?.filter((candidate) => candidate !== key) ?? [];
|
|
1130
|
+
if (keys.length > 0)
|
|
1131
|
+
activeToolTimeoutKeysByName.set(active.toolName, keys);
|
|
1132
|
+
else
|
|
1133
|
+
activeToolTimeoutKeysByName.delete(active.toolName);
|
|
1134
|
+
};
|
|
1135
|
+
const clearActiveToolTimeout = (event) => {
|
|
1136
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0 ? `id:${event.toolCallId}` : typeof event.toolName === "string" ? activeToolTimeoutKeysByName.get(event.toolName)?.[0] : activeToolTimeouts.size === 1 ? [...activeToolTimeouts.keys()][0] : undefined;
|
|
1137
|
+
if (key)
|
|
1138
|
+
removeToolTimeoutKey(key);
|
|
1139
|
+
};
|
|
1140
|
+
const clearAllToolTimeouts = () => {
|
|
1141
|
+
for (const key of [...activeToolTimeouts.keys()])
|
|
1142
|
+
removeToolTimeoutKey(key);
|
|
1143
|
+
if (toolTimeoutTerminationTimer) {
|
|
1144
|
+
clearTimeout(toolTimeoutTerminationTimer);
|
|
1145
|
+
toolTimeoutTerminationTimer = undefined;
|
|
1146
|
+
}
|
|
1147
|
+
if (toolTimeoutHardKillTimer) {
|
|
1148
|
+
clearTimeout(toolTimeoutHardKillTimer);
|
|
1149
|
+
toolTimeoutHardKillTimer = undefined;
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
const terminateForToolTimeout = (message) => {
|
|
1153
|
+
if (processClosed || lifecycleFinished || interruptedByControl)
|
|
1154
|
+
return;
|
|
1155
|
+
result.timedOut = true;
|
|
1156
|
+
result.error = message;
|
|
1157
|
+
result.finalOutput = message;
|
|
1158
|
+
progress.status = "failed";
|
|
1159
|
+
progress.error = message;
|
|
1160
|
+
progress.durationMs = Date.now() - startTime;
|
|
1161
|
+
fireUpdate();
|
|
1162
|
+
trySignalChild(proc, "SIGINT");
|
|
1163
|
+
toolTimeoutTerminationTimer = setTimeout(() => {
|
|
1164
|
+
if (processClosed || lifecycleFinished)
|
|
1165
|
+
return;
|
|
1166
|
+
trySignalChild(proc, "SIGTERM");
|
|
1167
|
+
}, 1000);
|
|
1168
|
+
toolTimeoutTerminationTimer.unref?.();
|
|
1169
|
+
toolTimeoutHardKillTimer = setTimeout(() => {
|
|
1170
|
+
if (processClosed || lifecycleFinished)
|
|
1171
|
+
return;
|
|
1172
|
+
trySignalChild(proc, "SIGKILL");
|
|
1173
|
+
}, 4000);
|
|
1174
|
+
toolTimeoutHardKillTimer.unref?.();
|
|
1175
|
+
};
|
|
1176
|
+
const armToolTimeout = (event) => {
|
|
1177
|
+
const timeoutForTool = effectiveToolTimeoutMs(event.toolName, options.toolTimeoutMs);
|
|
1178
|
+
if (timeoutForTool === undefined)
|
|
1179
|
+
return;
|
|
1180
|
+
const elapsed = Date.now() - startTime;
|
|
1181
|
+
const runRemaining = attemptTimeout ? Math.max(0, attemptTimeout.remainingMs - elapsed) : undefined;
|
|
1182
|
+
if (runRemaining !== undefined && timeoutForTool >= runRemaining)
|
|
1183
|
+
return;
|
|
1184
|
+
const key = toolTimeoutCallKey(event, ++toolTimeoutSequence);
|
|
1185
|
+
const toolName = event.toolName;
|
|
1186
|
+
const timer = setTimeout(() => {
|
|
1187
|
+
removeToolTimeoutKey(key);
|
|
1188
|
+
terminateForToolTimeout(formatToolTimeoutMessage(toolName, timeoutForTool));
|
|
1189
|
+
}, timeoutForTool);
|
|
1190
|
+
timer.unref?.();
|
|
1191
|
+
activeToolTimeouts.set(key, { toolName, timer });
|
|
1192
|
+
const keys = activeToolTimeoutKeysByName.get(toolName) ?? [];
|
|
1193
|
+
keys.push(key);
|
|
1194
|
+
activeToolTimeoutKeysByName.set(toolName, keys);
|
|
1195
|
+
};
|
|
1196
|
+
const stderrTail = createBoundedByteTail();
|
|
1197
|
+
const failProtocol = (limit) => {
|
|
1198
|
+
if (result.protocolError)
|
|
1199
|
+
return;
|
|
1200
|
+
result.protocolError = limit;
|
|
1201
|
+
result.error = formatProtocolOutputLimit(limit);
|
|
1202
|
+
progress.status = "failed";
|
|
1203
|
+
progress.error = result.error;
|
|
1204
|
+
progress.durationMs = Date.now() - startTime;
|
|
1205
|
+
fireUpdate();
|
|
1206
|
+
if (!childExited) {
|
|
1207
|
+
trySignalChild(proc, "SIGTERM");
|
|
1208
|
+
protocolHardKillTimer = setTimeout(() => {
|
|
1209
|
+
if (!childExited)
|
|
1210
|
+
trySignalChild(proc, "SIGKILL");
|
|
1211
|
+
}, 3000);
|
|
1212
|
+
protocolHardKillTimer.unref?.();
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
const stdoutReader = createBoundedLineReader({
|
|
1216
|
+
oversizedLineProjector: PI_AGGREGATE_EVENT_PROJECTOR,
|
|
1217
|
+
onLine: processLine,
|
|
1218
|
+
onLimit: failProtocol
|
|
1219
|
+
});
|
|
1220
|
+
const stderrReader = createBoundedLineReader({
|
|
1221
|
+
stream: "stderr",
|
|
1222
|
+
maxPendingLineBytes: MAX_CHILD_STDERR_BYTES,
|
|
1223
|
+
onLine: (line) => shared.transcriptWriter?.writeStderrLine(line),
|
|
1224
|
+
onLimit: (limit) => shared.transcriptWriter?.writeStderrLine(formatProtocolOutputLimit(limit))
|
|
696
1225
|
});
|
|
697
|
-
proc
|
|
698
|
-
|
|
1226
|
+
const clearStdioGuard = attachPostExitStdioGuard(proc, { idleMs: 2000, hardMs: 8000 });
|
|
1227
|
+
proc.stdout.on("data", (chunk) => stdoutReader.push(chunk));
|
|
1228
|
+
proc.stderr.on("data", (chunk) => {
|
|
1229
|
+
stderrTail.push(chunk);
|
|
1230
|
+
stderrReader.push(chunk);
|
|
1231
|
+
shared.orcaProgressTab?.append(chunk.toString("utf-8"));
|
|
699
1232
|
});
|
|
700
1233
|
proc.on("exit", () => {
|
|
701
1234
|
childExited = true;
|
|
702
1235
|
clearFinalDrainTimers();
|
|
703
1236
|
});
|
|
704
1237
|
proc.on("close", (code, signal) => {
|
|
1238
|
+
if (lifecycleFinished)
|
|
1239
|
+
return;
|
|
1240
|
+
processClosed = true;
|
|
705
1241
|
clearFinalDrainTimers();
|
|
706
1242
|
clearStdioGuard();
|
|
707
1243
|
jsonlWriter.close().catch(() => {});
|
|
1244
|
+
const toolDiagnosticError = readChildToolDiagnosticError(toolDiagnosticPath);
|
|
1245
|
+
toolAvailabilityError = toolDiagnosticError;
|
|
1246
|
+
result.runtimeAcknowledgedExtensions = readRuntimeAcknowledgedExtensions(runtimeAcknowledgedExtensionsPath);
|
|
708
1247
|
cleanupTempDir(tempDir);
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
const
|
|
716
|
-
if (
|
|
717
|
-
result.
|
|
1248
|
+
stdoutReader.end();
|
|
1249
|
+
stderrReader.end();
|
|
1250
|
+
const stderr = stderrTail.text();
|
|
1251
|
+
const rawStdout = rawStdoutTail.text();
|
|
1252
|
+
let closeError = result.error ?? toolDiagnosticError ?? assistantError;
|
|
1253
|
+
const forcedDrainAfterFinalSuccess = Boolean(forcedTerminationSignal || signal) && (cleanTerminalAssistantStopReceived || agentSettledReceived) && !closeError;
|
|
1254
|
+
const forcedDrainAfterEmptyTerminal = forcedDrainAfterFinalSuccess && hasEmptyTerminalAssistantResponse(result.messages ?? []);
|
|
1255
|
+
if (signal)
|
|
1256
|
+
result.processSignal = signal;
|
|
1257
|
+
if (!closeError && forcedDrainAfterEmptyTerminal && stderr.trim()) {
|
|
1258
|
+
closeError = stderr.trim();
|
|
718
1259
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
toolCount: progress.toolCount,
|
|
728
|
-
tokens: progress.tokens,
|
|
729
|
-
durationMs: progress.durationMs
|
|
730
|
-
};
|
|
731
|
-
const finalOutput = getFinalOutput(result.messages);
|
|
732
|
-
result.finalOutput = finalOutput.trim() || result.error || result.finalOutput || "Detached child exited without final output.";
|
|
733
|
-
if (result.artifactPaths && options.artifactConfig?.enabled !== false && options.artifactConfig?.includeOutput !== false) {
|
|
734
|
-
try {
|
|
735
|
-
writeArtifact(result.artifactPaths.outputPath, result.finalOutput);
|
|
736
|
-
} catch {}
|
|
737
|
-
}
|
|
738
|
-
options.onDetachedExit?.(snapshotResult(result, snapshotProgress(progress)));
|
|
739
|
-
finish(-2);
|
|
740
|
-
return;
|
|
1260
|
+
if (!closeError && isUnexplainedProcessSignal({
|
|
1261
|
+
processSignal: signal,
|
|
1262
|
+
interrupted: result.interrupted,
|
|
1263
|
+
timedOut: result.timedOut,
|
|
1264
|
+
stopped: result.stopped,
|
|
1265
|
+
forcedDrainAfterFinalSuccess: forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal
|
|
1266
|
+
})) {
|
|
1267
|
+
closeError = formatProcessSignalError(signal);
|
|
741
1268
|
}
|
|
742
|
-
|
|
1269
|
+
if (code !== 0 && rawStdout.trim() && !closeError && !forcedDrainAfterFinalSuccess) {
|
|
1270
|
+
closeError = rawStdout.trim();
|
|
1271
|
+
}
|
|
1272
|
+
if (code !== 0 && stderr.trim() && !closeError && !forcedDrainAfterFinalSuccess) {
|
|
1273
|
+
closeError = stderr.trim();
|
|
1274
|
+
}
|
|
1275
|
+
const finalCode = forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal ? 0 : forcedTerminationSignal || signal ? code ?? 1 : code ?? 0;
|
|
1276
|
+
if (!result.error && closeError)
|
|
1277
|
+
result.error = closeError;
|
|
743
1278
|
finish(finalCode);
|
|
744
1279
|
});
|
|
745
1280
|
proc.on("error", (error) => {
|
|
1281
|
+
if (lifecycleFinished)
|
|
1282
|
+
return;
|
|
1283
|
+
processClosed = true;
|
|
746
1284
|
clearFinalDrainTimers();
|
|
747
1285
|
clearStdioGuard();
|
|
748
1286
|
jsonlWriter.close().catch(() => {});
|
|
749
1287
|
cleanupTempDir(tempDir);
|
|
750
|
-
|
|
751
|
-
|
|
1288
|
+
stdoutReader.end();
|
|
1289
|
+
stderrReader.end();
|
|
752
1290
|
if (!result.error) {
|
|
753
1291
|
result.error = error instanceof Error ? error.message : String(error);
|
|
754
1292
|
}
|
|
@@ -756,12 +1294,9 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
756
1294
|
});
|
|
757
1295
|
if (options.signal) {
|
|
758
1296
|
const kill = () => {
|
|
759
|
-
if (processClosed ||
|
|
1297
|
+
if (processClosed || lifecycleFinished)
|
|
760
1298
|
return;
|
|
761
|
-
|
|
762
|
-
detachForIntercom();
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
1299
|
+
abortedBySignal = true;
|
|
765
1300
|
proc.kill("SIGTERM");
|
|
766
1301
|
setTimeout(() => !proc.killed && proc.kill("SIGKILL"), 3000);
|
|
767
1302
|
};
|
|
@@ -774,12 +1309,13 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
774
1309
|
}
|
|
775
1310
|
if (options.interruptSignal) {
|
|
776
1311
|
const interrupt = () => {
|
|
777
|
-
if (processClosed ||
|
|
1312
|
+
if (processClosed || lifecycleFinished)
|
|
778
1313
|
return;
|
|
779
1314
|
if (result.timedOut)
|
|
780
1315
|
return;
|
|
781
1316
|
interruptedByControl = true;
|
|
782
1317
|
clearTimeoutTimers();
|
|
1318
|
+
clearAllToolTimeouts();
|
|
783
1319
|
progress.status = "running";
|
|
784
1320
|
progress.durationMs = Date.now() - startTime;
|
|
785
1321
|
result.interrupted = true;
|
|
@@ -788,7 +1324,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
788
1324
|
fireUpdate();
|
|
789
1325
|
trySignalChild(proc, "SIGINT");
|
|
790
1326
|
setTimeout(() => {
|
|
791
|
-
if (
|
|
1327
|
+
if (lifecycleFinished || processClosed)
|
|
792
1328
|
return;
|
|
793
1329
|
trySignalChild(proc, "SIGTERM");
|
|
794
1330
|
}, 1000).unref?.();
|
|
@@ -800,8 +1336,16 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
800
1336
|
removeInterruptListener = () => options.interruptSignal?.removeEventListener("abort", interrupt);
|
|
801
1337
|
}
|
|
802
1338
|
}
|
|
1339
|
+
try {
|
|
1340
|
+
options.onDetachReady?.((reason = "user request") => detachForeground(reason));
|
|
1341
|
+
} catch (error) {
|
|
1342
|
+
appendRecentOutput(progress, [`Foreground detach callback failed: ${error instanceof Error ? error.message : String(error)}`]);
|
|
1343
|
+
}
|
|
803
1344
|
});
|
|
804
1345
|
result.exitCode = exitCode;
|
|
1346
|
+
if (afterCompactionSettlement) {
|
|
1347
|
+
result[AFTER_COMPACTION_SETTLEMENT] = true;
|
|
1348
|
+
}
|
|
805
1349
|
if (interruptedByControl) {
|
|
806
1350
|
result.exitCode = 0;
|
|
807
1351
|
result.interrupted = true;
|
|
@@ -817,42 +1361,62 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
817
1361
|
};
|
|
818
1362
|
return result;
|
|
819
1363
|
}
|
|
820
|
-
if (result.
|
|
821
|
-
result.exitCode
|
|
822
|
-
result.
|
|
823
|
-
|
|
1364
|
+
if (progress.currentTool && isOrdinaryToolForMidToolExit(progress.currentTool) && !abortedBySignal && !result.timedOut && !result.stopped && !result.protocolError && !toolAvailabilityError) {
|
|
1365
|
+
const processExitCode = result.exitCode;
|
|
1366
|
+
result.exitCode = 1;
|
|
1367
|
+
result.error = formatMidToolExitError({
|
|
1368
|
+
toolName: progress.currentTool,
|
|
1369
|
+
exitCode: processExitCode,
|
|
1370
|
+
processSignal: result.processSignal
|
|
1371
|
+
});
|
|
824
1372
|
}
|
|
1373
|
+
result.error = formatSubagentExtensionConflictError(result.error, {
|
|
1374
|
+
agent: agent.name,
|
|
1375
|
+
ambientExtensionsEnabled: !launchResolvedExtensions.disableAmbientExtensions
|
|
1376
|
+
});
|
|
825
1377
|
if (result.error && result.exitCode === 0) {
|
|
826
1378
|
result.exitCode = 1;
|
|
827
1379
|
}
|
|
828
|
-
|
|
829
|
-
const errInfo = detectSubagentError(result.messages);
|
|
830
|
-
if (errInfo.hasError) {
|
|
831
|
-
result.exitCode = errInfo.exitCode ?? 1;
|
|
832
|
-
result.error = errInfo.details ? `${errInfo.errorType} failed (exit ${errInfo.exitCode}): ${errInfo.details}` : `${errInfo.errorType} failed with exit code ${errInfo.exitCode}`;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
if (result.exitCode === 0 && !result.error) {
|
|
836
|
-
const finalText = getFinalOutput(result.messages);
|
|
837
|
-
const missingStructuredOutput = options.structuredOutput ? !existsSync(options.structuredOutput.outputPath) : false;
|
|
838
|
-
if (!finalText?.trim() && (!options.structuredOutput || missingStructuredOutput)) {
|
|
839
|
-
result.exitCode = 1;
|
|
840
|
-
result.error = "Subagent produced no output (possible model cold-start or empty response).";
|
|
841
|
-
}
|
|
842
|
-
}
|
|
1380
|
+
let validatedStructuredOutput = false;
|
|
843
1381
|
if (options.structuredOutput && result.exitCode === 0 && !result.error) {
|
|
844
|
-
const structured = readStructuredOutput({
|
|
845
|
-
schema: options.structuredOutput.schema,
|
|
846
|
-
schemaPath: options.structuredOutput.schemaPath,
|
|
847
|
-
outputPath: options.structuredOutput.outputPath
|
|
848
|
-
});
|
|
849
1382
|
result.structuredOutputSchemaPath = options.structuredOutput.schemaPath;
|
|
850
1383
|
result.structuredOutputPath = options.structuredOutput.outputPath;
|
|
851
|
-
if (
|
|
1384
|
+
if (!structuredOutputToolInvoked) {
|
|
852
1385
|
result.exitCode = 1;
|
|
853
|
-
result.error =
|
|
1386
|
+
result.error = MISSING_STRUCTURED_OUTPUT_CALL_ERROR;
|
|
1387
|
+
result.structuredOutputFailed = true;
|
|
854
1388
|
} else {
|
|
855
|
-
|
|
1389
|
+
const structured = await readStructuredOutput({
|
|
1390
|
+
schema: options.structuredOutput.schema,
|
|
1391
|
+
schemaPath: options.structuredOutput.schemaPath,
|
|
1392
|
+
outputPath: options.structuredOutput.outputPath
|
|
1393
|
+
});
|
|
1394
|
+
if (structured.error) {
|
|
1395
|
+
result.exitCode = 1;
|
|
1396
|
+
result.error = structured.error;
|
|
1397
|
+
result.structuredOutputFailed = true;
|
|
1398
|
+
} else {
|
|
1399
|
+
result.structuredOutput = structured.value;
|
|
1400
|
+
const acceptanceReport = readStructuredOutputAcceptanceReport(options.structuredOutput);
|
|
1401
|
+
result.structuredAcceptanceReport = acceptanceReport.value;
|
|
1402
|
+
result.structuredAcceptanceReportError = acceptanceReport.error;
|
|
1403
|
+
validatedStructuredOutput = true;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
if (result.exitCode === 0 && !result.error) {
|
|
1408
|
+
const messages = result.messages ?? [];
|
|
1409
|
+
const finalText = getFinalOutput(messages);
|
|
1410
|
+
const errorMessages = validatedStructuredOutput ? messages.slice(structuredOutputMessageStartIndex ?? messages.length) : messages;
|
|
1411
|
+
const errInfo = detectSubagentError(errorMessages);
|
|
1412
|
+
const missingOutput = !finalText?.trim() && !validatedStructuredOutput;
|
|
1413
|
+
const terminalEmptyAfterUsefulWork = !validatedStructuredOutput && hasEmptyTerminalAssistantResponse(messages) && (progress.toolCount > 0 || Boolean(finalText?.trim()));
|
|
1414
|
+
if ((missingOutput || terminalEmptyAfterUsefulWork) && (!errInfo.hasError || hasEmptyTerminalAssistantResponse(messages))) {
|
|
1415
|
+
result.exitCode = 1;
|
|
1416
|
+
result.error = formatEmptyTerminalAssistantResponseError(messages);
|
|
1417
|
+
} else if (errInfo.hasError) {
|
|
1418
|
+
result.exitCode = errInfo.exitCode ?? 1;
|
|
1419
|
+
result.error = errInfo.details ? `${errInfo.errorType} failed (exit ${errInfo.exitCode}): ${errInfo.details}` : `${errInfo.errorType} failed with exit code ${errInfo.exitCode}`;
|
|
856
1420
|
}
|
|
857
1421
|
}
|
|
858
1422
|
progress.status = result.exitCode === 0 ? "completed" : "failed";
|
|
@@ -864,37 +1428,89 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
864
1428
|
}
|
|
865
1429
|
}
|
|
866
1430
|
result.progressSummary = {
|
|
1431
|
+
...childSessionName ? { sessionName: childSessionName } : {},
|
|
867
1432
|
toolCount: progress.toolCount,
|
|
868
1433
|
tokens: progress.tokens,
|
|
869
1434
|
durationMs: progress.durationMs
|
|
870
1435
|
};
|
|
871
|
-
const
|
|
1436
|
+
const mutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, options.cwd ?? runtimeCwd);
|
|
1437
|
+
const acceptanceOutput = getFinalOutput(result.messages ?? []);
|
|
872
1438
|
let fullOutput = stripAcceptanceReport(acceptanceOutput);
|
|
1439
|
+
if (!fullOutput.trim() && result.structuredOutput !== undefined)
|
|
1440
|
+
fullOutput = JSON.stringify(result.structuredOutput, null, 2);
|
|
1441
|
+
result.outputState = fullOutput.trim() || result.structuredOutput !== undefined ? "present" : "absent";
|
|
873
1442
|
if (result.timedOut) {
|
|
874
1443
|
const timeoutMessage = formatTimeoutMessage(options.timeoutMs ?? 0);
|
|
1444
|
+
let requiredOutputMissing;
|
|
1445
|
+
if (options.outputMode === "file-only" && options.outputPath) {
|
|
1446
|
+
const outputChanged = hasSingleOutputChangedSinceSnapshot(options.outputPath, shared.outputSnapshot);
|
|
1447
|
+
requiredOutputMissing = outputChanged === undefined ? undefined : !outputChanged;
|
|
1448
|
+
} else if (options.structuredOutput) {
|
|
1449
|
+
requiredOutputMissing = !existsSync(options.structuredOutput.outputPath);
|
|
1450
|
+
}
|
|
1451
|
+
result.timeoutRecovery = buildTimeoutRecoverySummary({
|
|
1452
|
+
termination: "timed-out",
|
|
1453
|
+
evidence: mutationEvidence,
|
|
1454
|
+
requiredOutputMissing,
|
|
1455
|
+
currentTool: progress.currentTool,
|
|
1456
|
+
currentToolArgs: progress.currentToolArgs,
|
|
1457
|
+
currentPath: progress.currentPath,
|
|
1458
|
+
sessionFile: options.sessionFile,
|
|
1459
|
+
transcriptPath: shared.transcriptWriter ? shared.artifactPaths?.transcriptPath : undefined,
|
|
1460
|
+
artifactPaths: shared.artifactPaths
|
|
1461
|
+
});
|
|
875
1462
|
fullOutput = fullOutput.trim() ? `${timeoutMessage}
|
|
876
1463
|
|
|
1464
|
+
${result.timeoutRecovery.message}
|
|
1465
|
+
|
|
877
1466
|
Partial output before timeout:
|
|
878
|
-
${fullOutput}` : timeoutMessage
|
|
879
|
-
} else if (result.turnBudgetExceeded && result.turnBudget) {
|
|
880
|
-
fullOutput = formatTurnBudgetOutput(turnBudgetExceededMessage(result.turnBudget, result.turnBudget.turnCount), fullOutput);
|
|
881
|
-
} else if (result.wrapUpRequested && result.turnBudget?.outcome === "wrap-up-requested") {
|
|
882
|
-
const note = turnBudgetSoftNote(result.turnBudget, result.turnBudget.wrapUpRequestedAtTurn ?? result.turnBudget.turnCount);
|
|
883
|
-
fullOutput = fullOutput.trim() ? `${note}
|
|
1467
|
+
${fullOutput}` : `${timeoutMessage}
|
|
884
1468
|
|
|
885
|
-
${
|
|
1469
|
+
${result.timeoutRecovery.message}`;
|
|
886
1470
|
}
|
|
887
|
-
const
|
|
1471
|
+
const completionGuardEnabled = isAgentContractV1(options.agentContract) ? agent.completionGuard === true : agent.completionGuard !== false;
|
|
1472
|
+
const completionGuard = (result.exitCode === 0 && !result.error || toolAvailabilityError) && completionGuardEnabled ? evaluateCompletionMutationGuard({
|
|
888
1473
|
agent: agent.name,
|
|
889
1474
|
task: shared.originalTask ?? task,
|
|
890
|
-
messages: result.messages,
|
|
891
|
-
tools:
|
|
892
|
-
mcpDirectTools:
|
|
1475
|
+
messages: result.messages ?? [],
|
|
1476
|
+
tools: contractTools,
|
|
1477
|
+
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
1478
|
+
mutationTools: agent.mutationTools,
|
|
1479
|
+
toolAvailabilityError,
|
|
1480
|
+
mutationEvidence
|
|
893
1481
|
}) : undefined;
|
|
894
|
-
|
|
1482
|
+
const mutationAttemptObserved = observedMutationAttempt || mutationEvidence.attemptedMutation;
|
|
1483
|
+
let completionGuardTriggered = completionGuard?.triggered === true && !mutationAttemptObserved;
|
|
1484
|
+
let arbiterRescued = false;
|
|
1485
|
+
if (completionGuardTriggered) {
|
|
1486
|
+
const arbitration = await arbitrateCompletionGuardRescue({
|
|
1487
|
+
guardTriggered: true,
|
|
1488
|
+
task: shared.originalTask ?? task,
|
|
1489
|
+
arbiter: options.llmIntentArbiter
|
|
1490
|
+
});
|
|
1491
|
+
completionGuardTriggered = arbitration.triggered;
|
|
1492
|
+
arbiterRescued = arbitration.rescued;
|
|
1493
|
+
}
|
|
1494
|
+
const completionEvidence = planCompletionEvidence({
|
|
1495
|
+
guard: completionGuard,
|
|
1496
|
+
guardTriggered: completionGuardTriggered,
|
|
1497
|
+
completionGuardEnabled,
|
|
1498
|
+
mutationCapable: hasMutationToolCapability(contractTools, toolPlan.effectiveMcpTools),
|
|
1499
|
+
implementationMutationExpected: expectsImplementationMutation(agent.name, shared.originalTask ?? task),
|
|
1500
|
+
mutationAttemptObserved,
|
|
1501
|
+
mutationEvidence,
|
|
1502
|
+
arbiterRescued,
|
|
1503
|
+
agentContractV1: isAgentContractV1(options.agentContract)
|
|
1504
|
+
});
|
|
1505
|
+
if (completionEvidence.fileMutation) {
|
|
1506
|
+
result.effects = {
|
|
1507
|
+
...result.effects ?? {},
|
|
1508
|
+
fileMutation: completionEvidence.fileMutation
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
if (completionEvidence.legacyFailureError) {
|
|
895
1512
|
result.exitCode = 1;
|
|
896
|
-
result.error =
|
|
897
|
-
It appears to have returned planning or scratchpad output instead of applying changes.`;
|
|
1513
|
+
result.error = completionEvidence.legacyFailureError;
|
|
898
1514
|
progress.status = "failed";
|
|
899
1515
|
progress.error = result.error;
|
|
900
1516
|
emitControlEvent(buildControlEvent({
|
|
@@ -909,12 +1525,19 @@ It appears to have returned planning or scratchpad output instead of applying ch
|
|
|
909
1525
|
}));
|
|
910
1526
|
}
|
|
911
1527
|
if (options.outputPath && result.exitCode === 0) {
|
|
912
|
-
const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot);
|
|
1528
|
+
const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot, options.outputClaimPath);
|
|
913
1529
|
fullOutput = stripAcceptanceReport(resolvedOutput.fullOutput);
|
|
914
1530
|
result.savedOutputPath = resolvedOutput.savedPath;
|
|
915
1531
|
result.outputSaveError = resolvedOutput.saveError;
|
|
1532
|
+
if (resolvedOutput.fatalError) {
|
|
1533
|
+
result.exitCode = 1;
|
|
1534
|
+
result.error = result.error ? `${result.error}
|
|
1535
|
+
${resolvedOutput.saveError}` : resolvedOutput.saveError;
|
|
1536
|
+
}
|
|
916
1537
|
if (resolvedOutput.savedPath) {
|
|
917
1538
|
result.outputReference = formatSavedOutputReference(resolvedOutput.savedPath, fullOutput);
|
|
1539
|
+
if (result.outputState === "absent")
|
|
1540
|
+
result.outputState = "unknown";
|
|
918
1541
|
}
|
|
919
1542
|
}
|
|
920
1543
|
artifactOutputByResult.set(result, fullOutput);
|
|
@@ -925,7 +1548,7 @@ It appears to have returned planning or scratchpad output instead of applying ch
|
|
|
925
1548
|
if (options.onUpdate) {
|
|
926
1549
|
const finalText = result.finalOutput || result.error || "(no output)";
|
|
927
1550
|
const progressSnapshot = snapshotProgress(progress);
|
|
928
|
-
const resultSnapshot =
|
|
1551
|
+
const resultSnapshot = snapshotStreamResult(result, progressSnapshot);
|
|
929
1552
|
options.onUpdate({
|
|
930
1553
|
content: [{ type: "text", text: finalText }],
|
|
931
1554
|
details: {
|
|
@@ -938,21 +1561,90 @@ It appears to have returned planning or scratchpad output instead of applying ch
|
|
|
938
1561
|
}
|
|
939
1562
|
return result;
|
|
940
1563
|
}
|
|
941
|
-
|
|
1564
|
+
async function runSyncCompletionInner(runtimeCwd, agents, agentName, task, options) {
|
|
1565
|
+
const effectiveCwd = options.cwd ?? runtimeCwd;
|
|
1566
|
+
const cwdError = preflightLaunchCwd(options.requestedCwd ?? effectiveCwd, effectiveCwd);
|
|
1567
|
+
if (cwdError) {
|
|
1568
|
+
return redactResultPrompt(withRunContext({
|
|
1569
|
+
index: options.index ?? 0,
|
|
1570
|
+
agent: agentName,
|
|
1571
|
+
task,
|
|
1572
|
+
exitCode: 1,
|
|
1573
|
+
messages: [],
|
|
1574
|
+
usage: emptyUsage(),
|
|
1575
|
+
error: cwdError
|
|
1576
|
+
}, options.context));
|
|
1577
|
+
}
|
|
1578
|
+
options = {
|
|
1579
|
+
...options,
|
|
1580
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings(options.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(options.parentSessionId), decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]))
|
|
1581
|
+
};
|
|
1582
|
+
const childSessionName = deriveChildSessionName({ agent: agentName, task });
|
|
942
1583
|
const agent = agents.find((a) => a.name === agentName);
|
|
943
1584
|
if (!agent) {
|
|
944
|
-
|
|
1585
|
+
const diagnosticContext = options.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(path.resolve(options.cwd ?? runtimeCwd), "both"));
|
|
1586
|
+
return redactResultPrompt(withRunContext({
|
|
1587
|
+
index: options.index ?? 0,
|
|
945
1588
|
agent: agentName,
|
|
946
1589
|
task,
|
|
947
1590
|
exitCode: 1,
|
|
948
1591
|
messages: [],
|
|
949
1592
|
usage: emptyUsage(),
|
|
950
|
-
error:
|
|
951
|
-
};
|
|
1593
|
+
error: formatUnknownAgentError(agentName, diagnosticContext)
|
|
1594
|
+
}, options.context));
|
|
952
1595
|
}
|
|
1596
|
+
options = {
|
|
1597
|
+
...options,
|
|
1598
|
+
thinkingCeiling: intersectThinkingCeilings(options.thinkingCeiling, agent.maxThinking, decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]))
|
|
1599
|
+
};
|
|
1600
|
+
try {
|
|
1601
|
+
assertAgentAllowedByCapabilityCeiling(agent.name, options.capabilityCeiling);
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
return redactResultPrompt(withRunContext({
|
|
1604
|
+
index: options.index ?? 0,
|
|
1605
|
+
agent: agent.name,
|
|
1606
|
+
task,
|
|
1607
|
+
exitCode: 1,
|
|
1608
|
+
messages: [],
|
|
1609
|
+
usage: emptyUsage(),
|
|
1610
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1611
|
+
...options.capabilityCeiling ? { capabilityCeiling: options.capabilityCeiling } : {}
|
|
1612
|
+
}, options.context));
|
|
1613
|
+
}
|
|
1614
|
+
const acceptanceErrors = validateAcceptanceInput(options.acceptance);
|
|
1615
|
+
if (acceptanceErrors.length > 0) {
|
|
1616
|
+
return redactResultPrompt(withRunContext({
|
|
1617
|
+
index: options.index ?? 0,
|
|
1618
|
+
agent: agentName,
|
|
1619
|
+
task,
|
|
1620
|
+
exitCode: 1,
|
|
1621
|
+
messages: [],
|
|
1622
|
+
usage: emptyUsage(),
|
|
1623
|
+
error: acceptanceErrors.join(" ")
|
|
1624
|
+
}, options.context));
|
|
1625
|
+
}
|
|
1626
|
+
const toolTimeout = resolveToolTimeoutMs({
|
|
1627
|
+
callValue: options.toolTimeoutMs,
|
|
1628
|
+
agentValue: agent.defaultToolTimeoutMs,
|
|
1629
|
+
configValue: options.configToolTimeoutMs,
|
|
1630
|
+
envValue: toolTimeoutFromEnv()
|
|
1631
|
+
});
|
|
1632
|
+
if (toolTimeout.error) {
|
|
1633
|
+
return redactResultPrompt(withRunContext({
|
|
1634
|
+
index: options.index ?? 0,
|
|
1635
|
+
agent: agentName,
|
|
1636
|
+
task,
|
|
1637
|
+
exitCode: 1,
|
|
1638
|
+
messages: [],
|
|
1639
|
+
usage: emptyUsage(),
|
|
1640
|
+
error: toolTimeout.error
|
|
1641
|
+
}, options.context));
|
|
1642
|
+
}
|
|
1643
|
+
options = { ...options, toolTimeoutMs: toolTimeout.toolTimeoutMs };
|
|
953
1644
|
const outputModeValidationError = validateFileOnlyOutputMode(options.outputMode, options.outputPath, `Single run (${agentName})`);
|
|
954
1645
|
if (outputModeValidationError) {
|
|
955
|
-
return {
|
|
1646
|
+
return redactResultPrompt(withRunContext({
|
|
1647
|
+
index: options.index ?? 0,
|
|
956
1648
|
agent: agentName,
|
|
957
1649
|
task,
|
|
958
1650
|
exitCode: 1,
|
|
@@ -960,34 +1652,41 @@ export async function runSync(runtimeCwd, agents, agentName, task, options) {
|
|
|
960
1652
|
usage: emptyUsage(),
|
|
961
1653
|
outputMode: options.outputMode,
|
|
962
1654
|
error: outputModeValidationError
|
|
963
|
-
};
|
|
1655
|
+
}, options.context));
|
|
964
1656
|
}
|
|
965
1657
|
const shareEnabled = options.share === true;
|
|
966
1658
|
const effectiveAcceptance = resolveEffectiveAcceptance({
|
|
967
1659
|
explicit: options.acceptance,
|
|
968
1660
|
agentName,
|
|
1661
|
+
acceptanceRole: agent.acceptanceRole,
|
|
969
1662
|
task,
|
|
970
1663
|
mode: options.acceptanceContext?.mode ?? "single",
|
|
971
1664
|
async: options.acceptanceContext?.async,
|
|
972
1665
|
dynamic: options.acceptanceContext?.dynamic,
|
|
973
|
-
dynamicGroup: options.acceptanceContext?.dynamicGroup
|
|
1666
|
+
dynamicGroup: options.acceptanceContext?.dynamicGroup,
|
|
1667
|
+
agentContract: options.agentContract
|
|
974
1668
|
});
|
|
975
|
-
const acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance);
|
|
1669
|
+
const acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance, { reportOptional: isAgentContractV1(options.agentContract), structuredOutput: Boolean(options.structuredOutput?.acceptanceReportPath) });
|
|
976
1670
|
const taskWithAcceptance = acceptancePrompt ? `${task}
|
|
977
1671
|
${acceptancePrompt}` : task;
|
|
1672
|
+
options.onEffectivePrompt?.(taskWithAcceptance);
|
|
978
1673
|
const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
|
|
1674
|
+
if (options.context === "fork" && options.sessionFile && existsSync(options.sessionFile)) {
|
|
1675
|
+
alignForkedSessionCwd(options.sessionFile, options.cwd ?? runtimeCwd);
|
|
1676
|
+
}
|
|
979
1677
|
const skillNames = options.skills ?? agent.skills ?? [];
|
|
980
1678
|
const skillCwd = options.cwd ?? runtimeCwd;
|
|
981
|
-
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, skillCwd, runtimeCwd);
|
|
1679
|
+
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, skillCwd, runtimeCwd, agent.skillPath, agent.filePath ? path.dirname(agent.filePath) : skillCwd);
|
|
982
1680
|
if (skillNames.some((skill) => skill.trim() === "dm-subagents") && missingSkills.includes("dm-subagents")) {
|
|
983
|
-
return {
|
|
1681
|
+
return redactResultPrompt(withRunContext({
|
|
1682
|
+
index: options.index ?? 0,
|
|
984
1683
|
agent: agentName,
|
|
985
1684
|
task,
|
|
986
1685
|
exitCode: 1,
|
|
987
1686
|
messages: [],
|
|
988
1687
|
usage: emptyUsage(),
|
|
989
1688
|
error: "Skills not found: dm-subagents"
|
|
990
|
-
};
|
|
1689
|
+
}, options.context));
|
|
991
1690
|
}
|
|
992
1691
|
let systemPrompt = agent.systemPrompt?.trim() || "";
|
|
993
1692
|
if (resolvedSkills.length > 0) {
|
|
@@ -1002,12 +1701,42 @@ ${skillInjection}` : skillInjection;
|
|
|
1002
1701
|
|
|
1003
1702
|
${memoryInjection}` : memoryInjection;
|
|
1004
1703
|
}
|
|
1005
|
-
systemPrompt =
|
|
1006
|
-
|
|
1704
|
+
systemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: skillCwd, agentName });
|
|
1705
|
+
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, options.outputPath, agent);
|
|
1706
|
+
const candidates = buildModelCandidates(options.modelOverride ?? agent.model, agent.fallbackModels, options.availableModels, agent.modelProvider ?? options.preferredModelProvider, { scope: options.modelScope, primaryModelFromParent: options.modelOverrideFromParent });
|
|
1707
|
+
if (options.workflowChildPermitLaunch && candidates.length > 1) {
|
|
1708
|
+
const error = "Workflow child permit does not support model fallback.";
|
|
1709
|
+
return redactResultPrompt(withRunContext({
|
|
1710
|
+
index: options.index ?? 0,
|
|
1711
|
+
agent: agent.name,
|
|
1712
|
+
task,
|
|
1713
|
+
exitCode: 1,
|
|
1714
|
+
messages: [],
|
|
1715
|
+
usage: emptyUsage(),
|
|
1716
|
+
error
|
|
1717
|
+
}, options.context));
|
|
1718
|
+
}
|
|
1719
|
+
try {
|
|
1720
|
+
for (const candidate of candidates) {
|
|
1721
|
+
const model = applyThinkingSuffix(candidate, options.thinkingOverride ?? agent.thinking, options.thinkingOverride !== undefined);
|
|
1722
|
+
assertThinkingWithinCeiling({ model, configThinking: options.thinkingOverride ?? agent.thinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
1723
|
+
}
|
|
1724
|
+
} catch (error) {
|
|
1725
|
+
return redactResultPrompt(withRunContext({
|
|
1726
|
+
index: options.index ?? 0,
|
|
1727
|
+
agent: agent.name,
|
|
1728
|
+
task,
|
|
1729
|
+
exitCode: 1,
|
|
1730
|
+
messages: [],
|
|
1731
|
+
usage: emptyUsage(),
|
|
1732
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1733
|
+
}, options.context));
|
|
1734
|
+
}
|
|
1007
1735
|
const attemptedModels = [];
|
|
1008
1736
|
const modelAttempts = [];
|
|
1009
1737
|
const aggregateUsage = emptyUsage();
|
|
1010
1738
|
const attemptNotes = [];
|
|
1739
|
+
const launchWarnings = { emitted: false };
|
|
1011
1740
|
let totalToolCount = 0;
|
|
1012
1741
|
let totalDurationMs = 0;
|
|
1013
1742
|
let artifactPathsResult;
|
|
@@ -1019,7 +1748,8 @@ ${memoryInjection}` : memoryInjection;
|
|
|
1019
1748
|
if (options.artifactConfig?.includeInput !== false) {
|
|
1020
1749
|
writeArtifact(artifactPathsResult.inputPath, `# Task for ${agentName}
|
|
1021
1750
|
|
|
1022
|
-
${
|
|
1751
|
+
${PROMPT_REDACTED}; live Prompt Audit only.
|
|
1752
|
+
`);
|
|
1023
1753
|
}
|
|
1024
1754
|
if (options.artifactConfig?.includeJsonl !== false) {
|
|
1025
1755
|
jsonlPath = artifactPathsResult.jsonlPath;
|
|
@@ -1033,116 +1763,265 @@ ${taskWithAcceptance}`);
|
|
|
1033
1763
|
childIndex: options.index,
|
|
1034
1764
|
cwd: options.cwd ?? runtimeCwd
|
|
1035
1765
|
});
|
|
1036
|
-
transcriptWriter.writeInitialUserMessage(
|
|
1766
|
+
transcriptWriter.writeInitialUserMessage(`${PROMPT_REDACTED}; live Prompt Audit only.`);
|
|
1037
1767
|
}
|
|
1038
1768
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1769
|
+
const orcaProgressTab = createOrcaProgressTab({
|
|
1770
|
+
cwd: options.cwd ?? runtimeCwd,
|
|
1771
|
+
runId: options.runId,
|
|
1772
|
+
agent: agentName,
|
|
1773
|
+
index: options.index ?? 0
|
|
1774
|
+
});
|
|
1775
|
+
if (orcaProgressTab)
|
|
1776
|
+
options.onOrcaProgressTabCreated?.(orcaProgressTab);
|
|
1777
|
+
const persistResultMetadata = (target) => {
|
|
1778
|
+
persistSingleResultMetadata({
|
|
1779
|
+
metadataPath: artifactPathsResult?.metadataPath,
|
|
1780
|
+
enabled: options.artifactConfig?.enabled !== false && options.artifactConfig?.includeMetadata !== false,
|
|
1781
|
+
runId: options.runId,
|
|
1782
|
+
agent: agentName,
|
|
1783
|
+
task,
|
|
1784
|
+
result: target
|
|
1055
1785
|
});
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
}
|
|
1076
|
-
if (attemptSucceeded) {
|
|
1077
|
-
break;
|
|
1786
|
+
};
|
|
1787
|
+
let intercomDetached = false;
|
|
1788
|
+
let detachedReason;
|
|
1789
|
+
const attemptOptions = {
|
|
1790
|
+
...options,
|
|
1791
|
+
onDetachReceipt: (receipt) => {
|
|
1792
|
+
receipt.acceptance = buildPendingAcceptanceLedger(effectiveAcceptance);
|
|
1793
|
+
try {
|
|
1794
|
+
persistResultMetadata(receipt);
|
|
1795
|
+
} catch (error) {
|
|
1796
|
+
receipt.metadataSaveError = error instanceof Error ? error.message : String(error);
|
|
1797
|
+
}
|
|
1798
|
+
const accepted = options.onDetachReceipt?.(receipt) === true;
|
|
1799
|
+
if (accepted) {
|
|
1800
|
+
detachedReason = receipt.detachedReason;
|
|
1801
|
+
if (receipt.detachedReason === "intercom coordination")
|
|
1802
|
+
intercomDetached = true;
|
|
1803
|
+
}
|
|
1804
|
+
return accepted;
|
|
1078
1805
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1806
|
+
};
|
|
1807
|
+
let lastResult;
|
|
1808
|
+
const modelsToTry = candidates.length > 0 ? candidates : [undefined];
|
|
1809
|
+
let taskDeliveryOverride;
|
|
1810
|
+
let abortRecoveryAttempted = false;
|
|
1811
|
+
let nextAttemptTask = taskWithAcceptance;
|
|
1812
|
+
modelAttemptsLoop:
|
|
1813
|
+
for (let modelIndex = 0;modelIndex < modelsToTry.length; modelIndex++) {
|
|
1814
|
+
const candidate = modelsToTry[modelIndex];
|
|
1815
|
+
for (let startupAttemptIndex = 0;; startupAttemptIndex++) {
|
|
1816
|
+
const recoveringAbort = abortRecoveryAttempted;
|
|
1817
|
+
const attemptTask = nextAttemptTask;
|
|
1818
|
+
const verifyModel = Boolean(candidate) && !(options.modelOverrideFromParent && modelIndex === 0);
|
|
1819
|
+
const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
|
|
1820
|
+
const result = await runSingleAttempt(runtimeCwd, agent, attemptTask, candidate, attemptOptions, {
|
|
1821
|
+
sessionEnabled,
|
|
1822
|
+
systemPrompt,
|
|
1823
|
+
resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
|
|
1824
|
+
skillsWarning: missingSkills.length > 0 ? `Skills not found: ${missingSkills.join(", ")}` : undefined,
|
|
1825
|
+
jsonlPath,
|
|
1826
|
+
artifactPaths: artifactPathsResult,
|
|
1827
|
+
transcriptWriter,
|
|
1828
|
+
attemptNotes,
|
|
1829
|
+
modelCandidates: candidates.map((modelCandidate) => applyThinkingSuffix(modelCandidate, options.thinkingOverride ?? agent.thinking, options.thinkingOverride !== undefined)).filter((modelCandidate) => Boolean(modelCandidate)),
|
|
1830
|
+
outputSnapshot,
|
|
1831
|
+
originalTask: task,
|
|
1832
|
+
taskDelivery: taskDeliveryOverride,
|
|
1833
|
+
orcaProgressTab,
|
|
1834
|
+
launchWarnings,
|
|
1835
|
+
verifyModel
|
|
1836
|
+
});
|
|
1837
|
+
lastResult = result;
|
|
1838
|
+
if (!recoveringAbort && startupAttemptIndex === 0) {
|
|
1839
|
+
if (result.model)
|
|
1840
|
+
attemptedModels.push(result.model);
|
|
1841
|
+
else if (candidate)
|
|
1842
|
+
attemptedModels.push(candidate);
|
|
1843
|
+
}
|
|
1844
|
+
sumUsage(aggregateUsage, result.usage);
|
|
1845
|
+
totalToolCount += result.progressSummary?.toolCount ?? 0;
|
|
1846
|
+
totalDurationMs += result.progressSummary?.durationMs ?? 0;
|
|
1847
|
+
const attemptSucceeded = result.exitCode === 0 && !result.error;
|
|
1848
|
+
const attempt = {
|
|
1849
|
+
model: result.model ?? candidate ?? agent.model ?? "default",
|
|
1850
|
+
success: attemptSucceeded,
|
|
1851
|
+
exitCode: result.exitCode,
|
|
1852
|
+
error: result.error,
|
|
1853
|
+
usage: { ...result.usage }
|
|
1854
|
+
};
|
|
1855
|
+
modelAttempts.push(attempt);
|
|
1856
|
+
if (!attemptSucceeded) {
|
|
1857
|
+
const afterCompactionSettlement = result[AFTER_COMPACTION_SETTLEMENT];
|
|
1858
|
+
const abortRecovery = planAbortRecovery({
|
|
1859
|
+
messages: result.messages ?? [],
|
|
1860
|
+
error: result.error,
|
|
1861
|
+
processSignal: result.processSignal,
|
|
1862
|
+
sessionAvailable: Boolean(options.sessionFile && existsSync(options.sessionFile)),
|
|
1863
|
+
alreadyResumed: abortRecoveryAttempted,
|
|
1864
|
+
stopped: result.stopped || result.detached || options.signal?.aborted,
|
|
1865
|
+
interrupted: result.interrupted || intercomDetached || options.interruptSignal?.aborted,
|
|
1866
|
+
timedOut: result.timedOut,
|
|
1867
|
+
toolBudgetExhausted: result.toolBudgetBlocked,
|
|
1868
|
+
usageBudgetExhausted: false,
|
|
1869
|
+
structuredOutputFailed: result.structuredOutputFailed,
|
|
1870
|
+
acceptanceFailed: false,
|
|
1871
|
+
currentTool: result.progress?.currentTool,
|
|
1872
|
+
afterCompactionSettlement
|
|
1873
|
+
});
|
|
1874
|
+
if (abortRecovery.action === "resume") {
|
|
1875
|
+
abortRecoveryAttempted = true;
|
|
1876
|
+
nextAttemptTask = abortRecovery.prompt;
|
|
1877
|
+
attemptNotes.push("[abort-recovery] provider/transport abort after useful progress; resuming the retained child session once.");
|
|
1878
|
+
continue;
|
|
1879
|
+
}
|
|
1880
|
+
if (abortRecovery.diagnostic) {
|
|
1881
|
+
result.error = result.error ? `${result.error}
|
|
1882
|
+
${abortRecovery.diagnostic}` : abortRecovery.diagnostic;
|
|
1883
|
+
attempt.error = result.error;
|
|
1884
|
+
break modelAttemptsLoop;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
if (recoveringAbort && !attemptSucceeded)
|
|
1888
|
+
break modelAttemptsLoop;
|
|
1889
|
+
if (options.workflowChildPermitLaunch && !attemptSucceeded)
|
|
1890
|
+
break modelAttemptsLoop;
|
|
1891
|
+
if (intercomDetached || result.timedOut)
|
|
1892
|
+
break modelAttemptsLoop;
|
|
1893
|
+
if (attemptSucceeded)
|
|
1894
|
+
break modelAttemptsLoop;
|
|
1895
|
+
const startupFailure = isRetryableSubagentStartupFailure({
|
|
1896
|
+
exitCode: result.exitCode,
|
|
1897
|
+
error: result.error,
|
|
1898
|
+
finalOutput: result.finalOutput,
|
|
1899
|
+
messageCount: result.messages?.length ?? 0,
|
|
1900
|
+
toolCount: result.progressSummary?.toolCount ?? 0,
|
|
1901
|
+
usage: result.usage,
|
|
1902
|
+
durationMs: result.progressSummary?.durationMs ?? Number.POSITIVE_INFINITY,
|
|
1903
|
+
protocolError: result.protocolError,
|
|
1904
|
+
processSignal: result.processSignal,
|
|
1905
|
+
detached: result.detached,
|
|
1906
|
+
interrupted: result.interrupted,
|
|
1907
|
+
timedOut: result.timedOut,
|
|
1908
|
+
stopped: result.stopped
|
|
1909
|
+
});
|
|
1910
|
+
const retryDelayMs = SUBAGENT_STARTUP_RETRY_DELAYS_MS[startupAttemptIndex];
|
|
1911
|
+
if (startupFailure && retryDelayMs !== undefined) {
|
|
1912
|
+
const retryNote = formatSubagentStartupRetryNote({
|
|
1913
|
+
model: attempt.model,
|
|
1914
|
+
attempt: startupAttemptIndex + 1,
|
|
1915
|
+
maxAttempts: SUBAGENT_STARTUP_RETRY_DELAYS_MS.length + 1,
|
|
1916
|
+
delayMs: retryDelayMs
|
|
1917
|
+
});
|
|
1918
|
+
const shouldRetry = await waitForSubagentStartupRetry(retryDelayMs, [options.signal, options.interruptSignal]);
|
|
1919
|
+
if (!shouldRetry) {
|
|
1920
|
+
if (options.interruptSignal?.aborted) {
|
|
1921
|
+
result.exitCode = 0;
|
|
1922
|
+
result.interrupted = true;
|
|
1923
|
+
result.error = undefined;
|
|
1924
|
+
result.finalOutput = "Interrupted. Waiting for explicit next action.";
|
|
1925
|
+
if (result.progress) {
|
|
1926
|
+
result.progress.status = "running";
|
|
1927
|
+
result.progress.error = undefined;
|
|
1928
|
+
}
|
|
1929
|
+
} else {
|
|
1930
|
+
const cancellationError = "Subagent startup retry cancelled before relaunch.";
|
|
1931
|
+
result.error = cancellationError;
|
|
1932
|
+
result.finalOutput = cancellationError;
|
|
1933
|
+
attempt.error = cancellationError;
|
|
1934
|
+
if (result.progress)
|
|
1935
|
+
result.progress.error = cancellationError;
|
|
1936
|
+
}
|
|
1937
|
+
break modelAttemptsLoop;
|
|
1938
|
+
}
|
|
1939
|
+
if (!taskDeliveryOverride && result.processSignal === "SIGKILL") {
|
|
1940
|
+
taskDeliveryOverride = "file";
|
|
1941
|
+
attemptNotes.push("[startup-retry] retrying with file task delivery to keep the task text out of the child process argv.");
|
|
1942
|
+
}
|
|
1943
|
+
attempt.error = retryNote;
|
|
1944
|
+
attemptNotes.push(retryNote);
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
if (startupFailure) {
|
|
1948
|
+
const startupError = formatSubagentStartupRetryExhaustedError({
|
|
1949
|
+
model: attempt.model,
|
|
1950
|
+
attempts: startupAttemptIndex + 1
|
|
1951
|
+
});
|
|
1952
|
+
result.error = startupError;
|
|
1953
|
+
result.finalOutput = startupError;
|
|
1954
|
+
if (result.progress) {
|
|
1955
|
+
result.progress.error = startupError;
|
|
1956
|
+
result.progress.status = "failed";
|
|
1957
|
+
}
|
|
1958
|
+
attempt.error = startupError;
|
|
1959
|
+
break modelAttemptsLoop;
|
|
1960
|
+
}
|
|
1961
|
+
const retryableModelFailure = isRetryableModelFailureAttempt({ error: result.error, messages: result.messages, toolCount: result.progressSummary?.toolCount });
|
|
1962
|
+
if (retryableModelFailure)
|
|
1963
|
+
recordRetryableModelFailure(result.model ?? candidate, result.error);
|
|
1964
|
+
if (isContextOverflow(result.error)) {
|
|
1965
|
+
result.contextOverflow = true;
|
|
1966
|
+
attemptNotes.push(`[fallback] ${attempt.model} failed: context overflow — the input exceeds this model's context window. Reduce the task input or use a model with a larger context window.`);
|
|
1967
|
+
break modelAttemptsLoop;
|
|
1968
|
+
}
|
|
1969
|
+
if (!retryableModelFailure || modelIndex === modelsToTry.length - 1)
|
|
1970
|
+
break modelAttemptsLoop;
|
|
1971
|
+
attemptNotes.push(formatModelAttemptNote(attempt, modelsToTry[modelIndex + 1]));
|
|
1972
|
+
break;
|
|
1973
|
+
}
|
|
1081
1974
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
const result = lastResult ?? {
|
|
1975
|
+
const result = withRunContext(lastResult ?? {
|
|
1976
|
+
index: options.index ?? 0,
|
|
1085
1977
|
agent: agentName,
|
|
1086
1978
|
task,
|
|
1087
1979
|
exitCode: 1,
|
|
1088
1980
|
messages: [],
|
|
1089
1981
|
usage: emptyUsage(),
|
|
1090
1982
|
error: "Subagent did not produce a result."
|
|
1091
|
-
};
|
|
1983
|
+
}, options.context);
|
|
1984
|
+
result.task = task;
|
|
1092
1985
|
result.usage = aggregateUsage;
|
|
1093
1986
|
result.attemptedModels = attemptedModels.length > 0 ? attemptedModels : undefined;
|
|
1094
1987
|
result.modelAttempts = modelAttempts.length > 0 ? modelAttempts : undefined;
|
|
1095
1988
|
result.progressSummary = {
|
|
1989
|
+
...childSessionName ? { sessionName: childSessionName } : {},
|
|
1096
1990
|
toolCount: totalToolCount,
|
|
1097
1991
|
tokens: aggregateUsage.input + aggregateUsage.output,
|
|
1098
1992
|
durationMs: totalDurationMs
|
|
1099
1993
|
};
|
|
1100
|
-
if (attemptNotes.length > 0 && result.progress) {
|
|
1101
|
-
result.progress.recentOutput = [...attemptNotes, ...result.progress.recentOutput];
|
|
1102
|
-
if (result.progress.recentOutput.length > 50) {
|
|
1103
|
-
result.progress.recentOutput.splice(50);
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
1994
|
if (transcriptWriter)
|
|
1107
1995
|
result.transcriptPath = artifactPathsResult?.transcriptPath;
|
|
1108
1996
|
if (transcriptWriter?.getError())
|
|
1109
1997
|
result.transcriptError = transcriptWriter.getError();
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
...transcriptWriter ? { transcriptPath: artifactPathsResult.transcriptPath } : {},
|
|
1129
|
-
transcriptError: result.transcriptError,
|
|
1130
|
-
skills: result.skills,
|
|
1131
|
-
skillsWarning: result.skillsWarning,
|
|
1132
|
-
timestamp: Date.now()
|
|
1133
|
-
});
|
|
1134
|
-
}
|
|
1135
|
-
if (options.maxOutput) {
|
|
1998
|
+
try {
|
|
1999
|
+
if (artifactPathsResult && options.artifactConfig?.enabled !== false) {
|
|
2000
|
+
result.artifactPaths = artifactPathsResult;
|
|
2001
|
+
if (options.artifactConfig?.includeOutput !== false) {
|
|
2002
|
+
writeArtifact(artifactPathsResult.outputPath, formatOutputArtifactContent({
|
|
2003
|
+
output: artifactOutputByResult.get(result) ?? result.finalOutput ?? "",
|
|
2004
|
+
error: result.error,
|
|
2005
|
+
transcriptPath: result.transcriptPath,
|
|
2006
|
+
metadataPath: options.artifactConfig?.includeMetadata === false ? undefined : artifactPathsResult.metadataPath
|
|
2007
|
+
}));
|
|
2008
|
+
}
|
|
2009
|
+
if (options.maxOutput) {
|
|
2010
|
+
const config = { ...DEFAULT_MAX_OUTPUT, ...options.maxOutput };
|
|
2011
|
+
const truncationResult = truncateOutput(result.finalOutput ?? "", config, artifactPathsResult.outputPath);
|
|
2012
|
+
if (truncationResult.truncated)
|
|
2013
|
+
result.truncation = truncationResult;
|
|
2014
|
+
}
|
|
2015
|
+
} else if (options.maxOutput) {
|
|
1136
2016
|
const config = { ...DEFAULT_MAX_OUTPUT, ...options.maxOutput };
|
|
1137
|
-
const truncationResult = truncateOutput(result.finalOutput ?? "", config
|
|
2017
|
+
const truncationResult = truncateOutput(result.finalOutput ?? "", config);
|
|
1138
2018
|
if (truncationResult.truncated)
|
|
1139
2019
|
result.truncation = truncationResult;
|
|
1140
2020
|
}
|
|
1141
|
-
}
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
result.truncation = truncationResult;
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
const message = `Artifact output post-processing failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
2023
|
+
result.outputSaveError = result.outputSaveError ? `${result.outputSaveError}
|
|
2024
|
+
${message}` : message;
|
|
1146
2025
|
}
|
|
1147
2026
|
if (options.sessionFile && (existsSync(options.sessionFile) || result.messages?.length)) {
|
|
1148
2027
|
result.sessionFile = options.sessionFile;
|
|
@@ -1151,21 +2030,201 @@ ${taskWithAcceptance}`);
|
|
|
1151
2030
|
if (sessionFile)
|
|
1152
2031
|
result.sessionFile = sessionFile;
|
|
1153
2032
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
2033
|
+
const childWrittenOutput = options.outputPath ? extractChildWrittenOutput(result.messages, options.outputPath, options.cwd ?? runtimeCwd) : undefined;
|
|
2034
|
+
try {
|
|
2035
|
+
if (result.interrupted && detachedReason === "user request") {
|
|
2036
|
+
result.acceptance = buildInterruptedAcceptanceLedger(effectiveAcceptance);
|
|
2037
|
+
} else if (result.stopped) {
|
|
2038
|
+
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "stopped", message: "Acceptance was not evaluated because the subagent was stopped." });
|
|
2039
|
+
} else if (result.timedOut) {
|
|
2040
|
+
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "timeout", message: "Acceptance was not evaluated because the subagent timed out." });
|
|
2041
|
+
} else {
|
|
2042
|
+
result.acceptance = await evaluateAcceptance({
|
|
2043
|
+
acceptance: effectiveAcceptance,
|
|
2044
|
+
output: acceptanceOutputByResult.get(result) ?? result.finalOutput ?? "",
|
|
2045
|
+
report: result.structuredAcceptanceReport,
|
|
2046
|
+
reportError: result.structuredAcceptanceReportError,
|
|
2047
|
+
fileOutput: childWrittenOutput !== undefined && options.outputPath ? { content: childWrittenOutput, path: options.outputPath, authoritative: options.outputMode === "file-only", durable: result.savedOutputPath !== undefined } : undefined,
|
|
2048
|
+
cwd: options.cwd ?? runtimeCwd,
|
|
2049
|
+
reportOptional: isAgentContractV1(options.agentContract),
|
|
2050
|
+
artifactsDir: options.artifactsDir,
|
|
2051
|
+
runId: options.runId
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
} catch (error) {
|
|
2055
|
+
const message = `Acceptance evaluation failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
2056
|
+
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "acceptance-evaluation", message });
|
|
2057
|
+
}
|
|
1159
2058
|
const acceptanceFailure = acceptanceFailureMessage(result.acceptance);
|
|
1160
2059
|
stripAcceptanceReportsFromMessages(result.messages);
|
|
1161
|
-
if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.
|
|
2060
|
+
if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.interrupted && !result.timedOut && !isAgentContractV1(options.agentContract)) {
|
|
1162
2061
|
result.exitCode = 1;
|
|
2062
|
+
if (result.savedOutputPath) {
|
|
2063
|
+
result.finalOutput = finalizeSingleOutput({
|
|
2064
|
+
fullOutput: result.finalOutput ?? "",
|
|
2065
|
+
outputPath: options.outputPath,
|
|
2066
|
+
outputMode: result.outputMode,
|
|
2067
|
+
exitCode: result.exitCode,
|
|
2068
|
+
preserveSavedOutput: true,
|
|
2069
|
+
savedPath: result.savedOutputPath,
|
|
2070
|
+
outputReference: result.outputReference
|
|
2071
|
+
}).displayOutput;
|
|
2072
|
+
artifactOutputByResult.set(result, result.finalOutput);
|
|
2073
|
+
}
|
|
1163
2074
|
result.error = result.error ? `${result.error}
|
|
1164
2075
|
${acceptanceFailure}` : acceptanceFailure;
|
|
2076
|
+
if (artifactPathsResult && options.artifactConfig?.enabled !== false && options.artifactConfig?.includeOutput !== false) {
|
|
2077
|
+
try {
|
|
2078
|
+
writeArtifact(artifactPathsResult.outputPath, formatOutputArtifactContent({
|
|
2079
|
+
output: artifactOutputByResult.get(result) ?? result.finalOutput ?? "",
|
|
2080
|
+
error: result.error,
|
|
2081
|
+
transcriptPath: result.transcriptPath,
|
|
2082
|
+
metadataPath: options.artifactConfig?.includeMetadata === false ? undefined : artifactPathsResult.metadataPath
|
|
2083
|
+
}));
|
|
2084
|
+
} catch (error) {
|
|
2085
|
+
const message = `Artifact output post-processing failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
2086
|
+
result.outputSaveError = result.outputSaveError ? `${result.outputSaveError}
|
|
2087
|
+
${message}` : message;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
1165
2090
|
if (result.progress) {
|
|
1166
2091
|
result.progress.status = "failed";
|
|
1167
2092
|
result.progress.error = result.error;
|
|
1168
2093
|
}
|
|
1169
2094
|
}
|
|
2095
|
+
if (isAgentContractV1(options.agentContract))
|
|
2096
|
+
attachContractProjections(result);
|
|
2097
|
+
redactResultPrompt(result);
|
|
2098
|
+
try {
|
|
2099
|
+
persistResultMetadata(result);
|
|
2100
|
+
} catch (error) {
|
|
2101
|
+
result.metadataSaveError = error instanceof Error ? error.message : String(error);
|
|
2102
|
+
}
|
|
1170
2103
|
return result;
|
|
1171
2104
|
}
|
|
2105
|
+
async function runSyncCompletion(runtimeCwd, agents, agentName, task, options) {
|
|
2106
|
+
let orcaProgressTab;
|
|
2107
|
+
try {
|
|
2108
|
+
const result = await runSyncCompletionInner(runtimeCwd, agents, agentName, task, {
|
|
2109
|
+
...options,
|
|
2110
|
+
onOrcaProgressTabCreated: (tab) => {
|
|
2111
|
+
orcaProgressTab = tab;
|
|
2112
|
+
}
|
|
2113
|
+
});
|
|
2114
|
+
orcaProgressTab?.finish(result.stopped ? "stopped" : result.exitCode === 0 && !result.error ? "completed" : "failed", result.sessionFile);
|
|
2115
|
+
return result;
|
|
2116
|
+
} catch (error) {
|
|
2117
|
+
orcaProgressTab?.finish("failed");
|
|
2118
|
+
throw error;
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
export async function runSync(runtimeCwd, agents, agentName, task, options) {
|
|
2122
|
+
const strictContract = isAgentContractV1(options.agentContract);
|
|
2123
|
+
let detachedReason;
|
|
2124
|
+
let publishedReceipt;
|
|
2125
|
+
let activeDetachAttempt;
|
|
2126
|
+
let detachReadyPublished = false;
|
|
2127
|
+
let resolveReceipt;
|
|
2128
|
+
const receipt = new Promise((resolve) => {
|
|
2129
|
+
resolveReceipt = resolve;
|
|
2130
|
+
});
|
|
2131
|
+
const originSignal = options.signal;
|
|
2132
|
+
const originController = new AbortController;
|
|
2133
|
+
const forwardOriginAbort = () => {
|
|
2134
|
+
if (!detachedReason)
|
|
2135
|
+
originController.abort(originSignal?.reason);
|
|
2136
|
+
};
|
|
2137
|
+
if (originSignal?.aborted)
|
|
2138
|
+
forwardOriginAbort();
|
|
2139
|
+
else
|
|
2140
|
+
originSignal?.addEventListener("abort", forwardOriginAbort, { once: true });
|
|
2141
|
+
const completion = runSyncCompletion(runtimeCwd, agents, agentName, task, {
|
|
2142
|
+
...options,
|
|
2143
|
+
signal: originController.signal,
|
|
2144
|
+
onDetachedExit: undefined,
|
|
2145
|
+
onDetachReceipt: (detachedReceipt) => {
|
|
2146
|
+
if (detachedReason || publishedReceipt)
|
|
2147
|
+
return false;
|
|
2148
|
+
publishedReceipt = structuredClone(detachedReceipt);
|
|
2149
|
+
const callerReceipt = structuredClone(detachedReceipt);
|
|
2150
|
+
publishedReceipt.agentContract = strictContract ? { version: 1 } : undefined;
|
|
2151
|
+
callerReceipt.agentContract = strictContract ? { version: 1 } : undefined;
|
|
2152
|
+
detachedReason = detachedReceipt.detachedReason ?? "user request";
|
|
2153
|
+
resolveReceipt(callerReceipt);
|
|
2154
|
+
return true;
|
|
2155
|
+
},
|
|
2156
|
+
onDetachReady: (detachAttempt) => {
|
|
2157
|
+
activeDetachAttempt = detachAttempt;
|
|
2158
|
+
if (detachReadyPublished)
|
|
2159
|
+
return;
|
|
2160
|
+
detachReadyPublished = true;
|
|
2161
|
+
options.onDetachReady?.((reason = "user request") => {
|
|
2162
|
+
if (detachedReason || originController.signal.aborted)
|
|
2163
|
+
return false;
|
|
2164
|
+
return activeDetachAttempt?.(reason) ?? false;
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
});
|
|
2168
|
+
const authoritativeCompletion = completion.catch((error) => {
|
|
2169
|
+
if (!publishedReceipt || !detachedReason)
|
|
2170
|
+
throw error;
|
|
2171
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2172
|
+
const failureMessage = `Detached completion pipeline failed after receipt: ${message}`;
|
|
2173
|
+
const failedProgress = publishedReceipt.progress ? { ...publishedReceipt.progress, status: "failed", error: failureMessage } : {
|
|
2174
|
+
index: options.index ?? 0,
|
|
2175
|
+
agent: publishedReceipt.agent,
|
|
2176
|
+
status: "failed",
|
|
2177
|
+
task: publishedReceipt.task,
|
|
2178
|
+
recentTools: [],
|
|
2179
|
+
recentOutput: [],
|
|
2180
|
+
toolCount: publishedReceipt.progressSummary?.toolCount ?? 0,
|
|
2181
|
+
tokens: publishedReceipt.progressSummary?.tokens ?? 0,
|
|
2182
|
+
durationMs: publishedReceipt.progressSummary?.durationMs ?? 0,
|
|
2183
|
+
error: failureMessage
|
|
2184
|
+
};
|
|
2185
|
+
const failedResult = redactResultPrompt({
|
|
2186
|
+
...publishedReceipt,
|
|
2187
|
+
detached: undefined,
|
|
2188
|
+
detachedReason,
|
|
2189
|
+
exitCode: 1,
|
|
2190
|
+
error: failureMessage,
|
|
2191
|
+
finalOutput: failureMessage,
|
|
2192
|
+
progress: failedProgress,
|
|
2193
|
+
progressSummary: {
|
|
2194
|
+
toolCount: failedProgress.toolCount,
|
|
2195
|
+
tokens: failedProgress.tokens,
|
|
2196
|
+
durationMs: failedProgress.durationMs
|
|
2197
|
+
},
|
|
2198
|
+
acceptance: publishedReceipt.acceptance ? buildSkippedAcceptanceLedger(publishedReceipt.acceptance.effectiveAcceptance, { id: "completion-pipeline", message: failureMessage }) : undefined
|
|
2199
|
+
});
|
|
2200
|
+
if (strictContract)
|
|
2201
|
+
attachContractProjections(failedResult);
|
|
2202
|
+
try {
|
|
2203
|
+
persistSingleResultMetadata({
|
|
2204
|
+
metadataPath: failedResult.artifactPaths?.metadataPath,
|
|
2205
|
+
enabled: options.artifactConfig?.enabled !== false && options.artifactConfig?.includeMetadata !== false,
|
|
2206
|
+
runId: options.runId,
|
|
2207
|
+
agent: failedResult.agent,
|
|
2208
|
+
task: failedResult.task,
|
|
2209
|
+
result: failedResult
|
|
2210
|
+
});
|
|
2211
|
+
} catch (metadataError) {
|
|
2212
|
+
failedResult.metadataSaveError = metadataError instanceof Error ? metadataError.message : String(metadataError);
|
|
2213
|
+
}
|
|
2214
|
+
return failedResult;
|
|
2215
|
+
});
|
|
2216
|
+
let terminalCallbackInvoked = false;
|
|
2217
|
+
authoritativeCompletion.then((terminalResult) => {
|
|
2218
|
+
if (!detachedReason || terminalCallbackInvoked)
|
|
2219
|
+
return;
|
|
2220
|
+
terminalCallbackInvoked = true;
|
|
2221
|
+
terminalResult.detached = undefined;
|
|
2222
|
+
terminalResult.detachedReason = detachedReason;
|
|
2223
|
+
try {
|
|
2224
|
+
options.onDetachedExit?.(terminalResult);
|
|
2225
|
+
} catch {}
|
|
2226
|
+
}).catch(() => {}).finally(() => {
|
|
2227
|
+
originSignal?.removeEventListener("abort", forwardOriginAbort);
|
|
2228
|
+
});
|
|
2229
|
+
return Promise.race([authoritativeCompletion, receipt]);
|
|
2230
|
+
}
|