@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
|
@@ -4,46 +4,251 @@ import * as os from "node:os";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { keyText } from "@duckmind/dm-coding-agent";
|
|
6
6
|
import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@duckmind/dm-tui";
|
|
7
|
-
import { discoverAgents } from "../agents/agents.js";
|
|
7
|
+
import { discoverAgentSnapshot, discoverAgents } from "../agents/agents.js";
|
|
8
|
+
import { clearRuntimeAgentsForPi, listRuntimeAgentConfigs, mergeRuntimeAgents } from "../agents/runtime-agent-registry.js";
|
|
9
|
+
import { registerRuntimeAgentEventListener } from "../agents/runtime-agent-events.js";
|
|
10
|
+
import { ensureAccessibleDir } from "../shared/accessible-dir.js";
|
|
8
11
|
import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.js";
|
|
9
12
|
import { resolveCurrentSessionId } from "../shared/session-identity.js";
|
|
13
|
+
import { getAgentDir } from "../shared/utils.js";
|
|
14
|
+
import { isStaleExtensionContextError, withCachedUiContext } from "../shared/extension-context.js";
|
|
15
|
+
import { currentCompletionOwnerId } from "../shared/completion-owner.js";
|
|
10
16
|
import { cleanupOldChainDirs } from "../shared/settings.js";
|
|
11
|
-
import { clearLegacyResultAnimationTimer,
|
|
12
|
-
import {
|
|
17
|
+
import { clearLegacyResultAnimationTimer, renderSubagentResult, renderSubagentSummary } from "../tui/render.js";
|
|
18
|
+
import { openSubagentFleet } from "../tui/fleet.js";
|
|
19
|
+
import { SubagentFleetStatus, resolveFleetViewPlacement } from "../tui/fleet-status.js";
|
|
20
|
+
import { createSubagentParamsSchema } from "./schemas.js";
|
|
13
21
|
import { createSubagentExecutor } from "../runs/foreground/subagent-executor.js";
|
|
14
22
|
import { createAsyncJobTracker } from "../runs/background/async-job-tracker.js";
|
|
23
|
+
import { getActiveAsyncCapacitySnapshot, resolveAbandonedSlotReleaseAfterMs, resolveMaxActiveAsyncRunsPerSession } from "../runs/background/active-async-capacity.js";
|
|
24
|
+
import { cleanupResultIndexes, missionObserverResultCandidateFiles } from "../runs/background/result-files.js";
|
|
25
|
+
import { ASYNC_RETENTION_DELAY_MS, cleanupAsyncRetention } from "../runs/background/async-retention.js";
|
|
15
26
|
import { createResultWatcher } from "../runs/background/result-watcher.js";
|
|
27
|
+
import { createResultDeliveryOwnership } from "../runs/background/result-delivery-ownership.js";
|
|
16
28
|
import { createScheduledRunManager } from "../runs/background/scheduled-runs.js";
|
|
17
29
|
import { registerSlashCommands } from "../slash/slash-commands.js";
|
|
18
30
|
import { registerPromptTemplateDelegationBridge } from "../slash/prompt-template-bridge.js";
|
|
31
|
+
import { registerMainWatchdog } from "../watchdog/register-main.js";
|
|
19
32
|
import { registerSlashSubagentBridge } from "../slash/slash-bridge.js";
|
|
20
33
|
import { createNativeSupervisorChannel } from "../intercom/native-supervisor-channel.js";
|
|
34
|
+
import { registerHerdrStatusBridge } from "../integrations/herdr-status.js";
|
|
35
|
+
import { listHerdrProjectPaneRoots, restoreHerdrProjectPaneSnapshots } from "../inspectors/herdr/project-panes.js";
|
|
21
36
|
import { registerSubagentRpcBridge } from "./rpc.js";
|
|
22
37
|
import { clearSlashSnapshots, getSlashRenderableSnapshot, resolveSlashMessageDetails, restoreSlashFinalSnapshots } from "../slash/slash-live-state.js";
|
|
23
|
-
import {
|
|
24
|
-
import
|
|
38
|
+
import { resolveWaitToolConfig } from "../runs/background/subagent-wait.js";
|
|
39
|
+
import { registerWaitTool } from "../runs/background/wait-tool.js";
|
|
40
|
+
import { createWaitSubscriptionManager } from "../runs/background/wait-subscriptions.js";
|
|
41
|
+
import { drainOutstandingWork } from "../runs/background/auto-drain.js";
|
|
42
|
+
import registerSubagentNotify, { parseSubagentNotifyContent } from "../runs/background/notify.js";
|
|
43
|
+
import { formatSteeringNotice, handleSubagentSteeringNotice, SUBAGENT_STEERING_MESSAGE_TYPE } from "./steering-notices.js";
|
|
25
44
|
import { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from "../runs/shared/dm-args.js";
|
|
45
|
+
import { resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.js";
|
|
26
46
|
import { formatDuration, shortenPath } from "../shared/formatters.js";
|
|
27
|
-
import { loadConfig } from "./config.js";
|
|
28
|
-
import { buildSubagentToolDescription } from "./tool-description.js";
|
|
47
|
+
import { applyModelExclusionsConfig, loadConfig, resolveAsyncByDefault, resolveScheduledStoreRoot } from "./config.js";
|
|
48
|
+
import { buildSubagentToolDescription, buildSubagentToolPromptMetadata } from "./tool-description.js";
|
|
49
|
+
import { formatWorkflowPreflightSummary, normalizeWorkflowPreflight } from "../workflows/workflow-preflight.js";
|
|
50
|
+
import { finalizeToolResult } from "./tool-result.js";
|
|
51
|
+
import { collectGoalContinuationNotices } from "../missions/goal-driver.js";
|
|
52
|
+
import { restoreForegroundRunHistory } from "../runs/foreground/foreground-history.js";
|
|
53
|
+
import { resolveMissionStoreLocation } from "../missions/store.js";
|
|
54
|
+
import { listRetainedChildren } from "../runs/background/retained-children.js";
|
|
29
55
|
import {
|
|
30
|
-
|
|
56
|
+
DIRS,
|
|
31
57
|
DEFAULT_ARTIFACT_CONFIG,
|
|
32
|
-
RESULTS_DIR,
|
|
33
58
|
SLASH_RESULT_TYPE,
|
|
34
59
|
SLASH_TEXT_RESULT_TYPE,
|
|
35
60
|
SUBAGENT_ASYNC_COMPLETE_EVENT,
|
|
36
61
|
SUBAGENT_ASYNC_STARTED_EVENT,
|
|
62
|
+
SUBAGENT_PROCESS_TERMINAL_EVENT,
|
|
37
63
|
SUBAGENT_CONTROL_EVENT,
|
|
38
|
-
|
|
64
|
+
SUBAGENT_STEERING_NOTICE_EVENT,
|
|
65
|
+
WIDGET_KEY,
|
|
66
|
+
resolveMaxSubagentSpawnsPerSession
|
|
39
67
|
} from "../shared/types.js";
|
|
40
68
|
import {
|
|
41
|
-
clearPendingForegroundControlNotices,
|
|
42
69
|
formatSubagentControlNotice,
|
|
43
70
|
handleSubagentControlNotice,
|
|
44
71
|
SUBAGENT_CONTROL_MESSAGE_TYPE
|
|
45
72
|
} from "./control-notices.js";
|
|
46
|
-
export { loadConfig } from "./config.js";
|
|
73
|
+
export { loadConfig, resolveAsyncByDefault } from "./config.js";
|
|
74
|
+
const SLOW_RELOAD_PHASE_MS = 250;
|
|
75
|
+
const RUNTIME_REGISTRY_STORE_KEY = "__piSubagentRuntimeRegistry";
|
|
76
|
+
function getRuntimeRegistry() {
|
|
77
|
+
const globalStore = globalThis;
|
|
78
|
+
const existing = globalStore[RUNTIME_REGISTRY_STORE_KEY];
|
|
79
|
+
if (existing?.bySessionManager instanceof WeakMap && existing.activeEntries instanceof Set) {
|
|
80
|
+
if (!(existing.visibleControlNoticesBySessionManager instanceof WeakMap)) {
|
|
81
|
+
existing.visibleControlNoticesBySessionManager = new WeakMap;
|
|
82
|
+
}
|
|
83
|
+
return existing;
|
|
84
|
+
}
|
|
85
|
+
const registry = {
|
|
86
|
+
bySessionManager: new WeakMap,
|
|
87
|
+
visibleControlNoticesBySessionManager: new WeakMap,
|
|
88
|
+
activeEntries: new Set
|
|
89
|
+
};
|
|
90
|
+
globalStore[RUNTIME_REGISTRY_STORE_KEY] = registry;
|
|
91
|
+
return registry;
|
|
92
|
+
}
|
|
93
|
+
function logSlowPhase(label, startedAt) {
|
|
94
|
+
const elapsed = Date.now() - startedAt;
|
|
95
|
+
if (elapsed >= SLOW_RELOAD_PHASE_MS)
|
|
96
|
+
console.error(`Subagent reload phase '${label}' took ${elapsed}ms.`);
|
|
97
|
+
}
|
|
98
|
+
function workflowLaneKeys(script) {
|
|
99
|
+
const keys = [];
|
|
100
|
+
const seen = new Set;
|
|
101
|
+
const add = (key) => {
|
|
102
|
+
if (!seen.has(key)) {
|
|
103
|
+
seen.add(key);
|
|
104
|
+
keys.push(key);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const isIdentifier = (char) => char !== undefined && /[\w$]/.test(char);
|
|
108
|
+
const skipTrivia = (start) => {
|
|
109
|
+
let index = start;
|
|
110
|
+
while (index < script.length) {
|
|
111
|
+
if (/\s/.test(script[index]))
|
|
112
|
+
index += 1;
|
|
113
|
+
else if (script.startsWith("//", index)) {
|
|
114
|
+
const end = script.indexOf(`
|
|
115
|
+
`, index + 2);
|
|
116
|
+
index = end === -1 ? script.length : end + 1;
|
|
117
|
+
} else if (script.startsWith("/*", index)) {
|
|
118
|
+
const end = script.indexOf("*/", index + 2);
|
|
119
|
+
index = end === -1 ? script.length : end + 2;
|
|
120
|
+
} else
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
return index;
|
|
124
|
+
};
|
|
125
|
+
const readLiteral = (start) => {
|
|
126
|
+
const quote = script[start];
|
|
127
|
+
if (quote !== "'" && quote !== '"' && quote !== "`")
|
|
128
|
+
return;
|
|
129
|
+
let index = start + 1;
|
|
130
|
+
let dynamicTemplate = false;
|
|
131
|
+
while (index < script.length) {
|
|
132
|
+
if (script[index] === "\\") {
|
|
133
|
+
index += 2;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (quote === "`" && script.startsWith("${", index))
|
|
137
|
+
dynamicTemplate = true;
|
|
138
|
+
if (script[index] === quote)
|
|
139
|
+
return { key: dynamicTemplate ? undefined : script.slice(start + 1, index), end: index + 1 };
|
|
140
|
+
if (quote !== "`" && /[\r\n]/.test(script[index]))
|
|
141
|
+
return { end: index + 1 };
|
|
142
|
+
index += 1;
|
|
143
|
+
}
|
|
144
|
+
return { end: script.length };
|
|
145
|
+
};
|
|
146
|
+
const collectRunsAllKeys = (start) => {
|
|
147
|
+
let index = skipTrivia(start);
|
|
148
|
+
if (script[index] !== "(")
|
|
149
|
+
return start;
|
|
150
|
+
index = skipTrivia(index + 1);
|
|
151
|
+
if (script[index] !== "[")
|
|
152
|
+
return start;
|
|
153
|
+
let arrayDepth = 1;
|
|
154
|
+
let objectDepth = 0;
|
|
155
|
+
let directChildObject = false;
|
|
156
|
+
let expectingElement = true;
|
|
157
|
+
for (index += 1;index < script.length; index += 1) {
|
|
158
|
+
index = skipTrivia(index);
|
|
159
|
+
const literal = readLiteral(index);
|
|
160
|
+
if (literal) {
|
|
161
|
+
index = literal.end - 1;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (script[index] === "[") {
|
|
165
|
+
arrayDepth += 1;
|
|
166
|
+
expectingElement = false;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (script[index] === "]") {
|
|
170
|
+
arrayDepth -= 1;
|
|
171
|
+
if (arrayDepth === 0)
|
|
172
|
+
return index + 1;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (script[index] === "{") {
|
|
176
|
+
objectDepth += 1;
|
|
177
|
+
if (objectDepth === 1)
|
|
178
|
+
directChildObject = arrayDepth === 1 && expectingElement;
|
|
179
|
+
expectingElement = false;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (script[index] === "}") {
|
|
183
|
+
objectDepth -= 1;
|
|
184
|
+
if (objectDepth === 0)
|
|
185
|
+
directChildObject = false;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (script[index] === "," && arrayDepth === 1 && objectDepth === 0) {
|
|
189
|
+
expectingElement = true;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (directChildObject && objectDepth === 1 && !isIdentifier(script[index - 1]) && script.startsWith("key", index) && !isIdentifier(script[index + 3])) {
|
|
193
|
+
const colon = skipTrivia(index + 3);
|
|
194
|
+
const key = script[colon] === ":" ? readLiteral(skipTrivia(colon + 1)) : undefined;
|
|
195
|
+
if (key) {
|
|
196
|
+
const next = skipTrivia(key.end);
|
|
197
|
+
if (key.key !== undefined && (script[next] === "," || script[next] === "}"))
|
|
198
|
+
add(key.key);
|
|
199
|
+
index = key.end - 1;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return index;
|
|
204
|
+
};
|
|
205
|
+
for (let index = 0;index < script.length; ) {
|
|
206
|
+
index = skipTrivia(index);
|
|
207
|
+
const literal = readLiteral(index);
|
|
208
|
+
if (literal) {
|
|
209
|
+
index = literal.end;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (!isIdentifier(script[index - 1]) && script.startsWith("runs.run", index) && !isIdentifier(script[index + 8])) {
|
|
213
|
+
const open = skipTrivia(index + 8);
|
|
214
|
+
const key = script[open] === "(" ? readLiteral(skipTrivia(open + 1)) : undefined;
|
|
215
|
+
if (key) {
|
|
216
|
+
const next = skipTrivia(key.end);
|
|
217
|
+
if (key.key !== undefined && (script[next] === "," || script[next] === ")"))
|
|
218
|
+
add(key.key);
|
|
219
|
+
index = key.end;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!isIdentifier(script[index - 1]) && script.startsWith("runs.all", index) && !isIdentifier(script[index + 8])) {
|
|
224
|
+
index = collectRunsAllKeys(index + 8);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
index += 1;
|
|
228
|
+
}
|
|
229
|
+
return keys;
|
|
230
|
+
}
|
|
231
|
+
function formatWorkflowPreflightCall(input) {
|
|
232
|
+
if (input === undefined)
|
|
233
|
+
return "";
|
|
234
|
+
try {
|
|
235
|
+
return formatWorkflowPreflightSummary(normalizeWorkflowPreflight(input));
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return `preflight · rejected: ${error instanceof Error ? error.message : String(error)}`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function formatWorkflowManifest(script, async, clarify, preflightInput) {
|
|
241
|
+
if (clarify === true)
|
|
242
|
+
return "workflow script · rejected: clarify UI unsupported";
|
|
243
|
+
const keys = workflowLaneKeys(script);
|
|
244
|
+
const mode = async === false ? "foreground" : "background";
|
|
245
|
+
const preflight = formatWorkflowPreflightCall(preflightInput);
|
|
246
|
+
if (keys.length === 0)
|
|
247
|
+
return `workflow script · ${mode}${preflight ? ` · ${preflight}` : ""}`;
|
|
248
|
+
const visibleKeys = keys.slice(0, 4).join(", ");
|
|
249
|
+
const remainder = keys.length > 4 ? `, +${keys.length - 4}` : "";
|
|
250
|
+
return `workflow · ${mode} · ${keys.length} lane${keys.length === 1 ? "" : "s"}: ${visibleKeys}${remainder}${preflight ? ` · ${preflight}` : ""}`;
|
|
251
|
+
}
|
|
47
252
|
function getSubagentSessionRoot(parentSessionFile) {
|
|
48
253
|
if (parentSessionFile) {
|
|
49
254
|
const baseName = path.basename(parentSessionFile, ".jsonl");
|
|
@@ -55,100 +260,33 @@ function getSubagentSessionRoot(parentSessionFile) {
|
|
|
55
260
|
function expandTilde(p) {
|
|
56
261
|
return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
|
|
57
262
|
}
|
|
58
|
-
function ensureAccessibleDir(dirPath) {
|
|
59
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
60
|
-
try {
|
|
61
|
-
fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
62
|
-
} catch {
|
|
63
|
-
try {
|
|
64
|
-
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
65
|
-
} catch {}
|
|
66
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
67
|
-
fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
263
|
function isSlashResultRunning(result) {
|
|
71
264
|
return result.details?.progress?.some((entry) => entry.status === "running") || result.details?.results.some((entry) => entry.progress?.status === "running") || false;
|
|
72
265
|
}
|
|
73
|
-
function subagentResultIsRunning(result) {
|
|
74
|
-
return result.details?.progress?.some((entry) => entry.status === "running") || result.details?.results.some((entry) => entry.progress?.status === "running") || false;
|
|
75
|
-
}
|
|
76
|
-
function ensureSubagentResultAnimation(context) {
|
|
77
|
-
const state = context.state;
|
|
78
|
-
if (state.subagentResultAnimationTimer)
|
|
79
|
-
return;
|
|
80
|
-
if (typeof context.invalidate !== "function")
|
|
81
|
-
return;
|
|
82
|
-
if (state.frame === undefined)
|
|
83
|
-
state.frame = 0;
|
|
84
|
-
state.subagentResultAnimationTimer = setInterval(() => {
|
|
85
|
-
state.frame = ((state.frame ?? 0) + 1) % 10;
|
|
86
|
-
try {
|
|
87
|
-
context.invalidate();
|
|
88
|
-
} catch {}
|
|
89
|
-
}, 80);
|
|
90
|
-
}
|
|
91
266
|
function isSlashResultError(result) {
|
|
92
267
|
return result.details?.results.some((entry) => entry.exitCode !== 0 && entry.progress?.status !== "running") || false;
|
|
93
268
|
}
|
|
94
|
-
function
|
|
95
|
-
return error instanceof Error && error.message.includes("Extension context no longer active");
|
|
96
|
-
}
|
|
97
|
-
function rebuildSlashResultContainer(container, result, options, theme) {
|
|
269
|
+
function rebuildSlashResultContainer(container, result, options, theme, rendererConfig, foregroundDetachShortcut) {
|
|
98
270
|
container.clear();
|
|
99
271
|
container.addChild(new Spacer(1));
|
|
100
272
|
const boxTheme = isSlashResultRunning(result) ? "toolPendingBg" : isSlashResultError(result) ? "toolErrorBg" : "toolSuccessBg";
|
|
101
273
|
const box = new Box(1, 1, (text) => theme.bg(boxTheme, text));
|
|
102
|
-
box.addChild(renderSubagentResult(result, options, theme));
|
|
274
|
+
box.addChild(renderSubagentResult(result, options, theme, undefined, rendererConfig, foregroundDetachShortcut));
|
|
103
275
|
container.addChild(box);
|
|
104
276
|
}
|
|
105
|
-
function createSlashResultComponent(details, options, theme) {
|
|
277
|
+
function createSlashResultComponent(details, options, theme, rendererConfig, foregroundDetachShortcut) {
|
|
106
278
|
const container = new Container;
|
|
107
279
|
let lastVersion = -1;
|
|
108
280
|
container.render = (width) => {
|
|
109
281
|
const snapshot = getSlashRenderableSnapshot(details);
|
|
110
282
|
if (snapshot.version !== lastVersion || isSlashResultRunning(snapshot.result)) {
|
|
111
283
|
lastVersion = snapshot.version;
|
|
112
|
-
rebuildSlashResultContainer(container, snapshot.result, options, theme);
|
|
284
|
+
rebuildSlashResultContainer(container, snapshot.result, options, theme, rendererConfig, foregroundDetachShortcut);
|
|
113
285
|
}
|
|
114
286
|
return Container.prototype.render.call(container, width);
|
|
115
287
|
};
|
|
116
288
|
return container;
|
|
117
289
|
}
|
|
118
|
-
function parseSubagentNotifyContent(content) {
|
|
119
|
-
const lines = content.split(`
|
|
120
|
-
`);
|
|
121
|
-
const header = lines[0] ?? "";
|
|
122
|
-
const match = header.match(/^Background task (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
123
|
-
if (!match)
|
|
124
|
-
return;
|
|
125
|
-
const body = lines.slice(2);
|
|
126
|
-
let sessionIndex = -1;
|
|
127
|
-
for (let i = body.length - 1;i >= 1; i--) {
|
|
128
|
-
if (body[i - 1]?.trim() === "" && /^(Session|Session file|Session share error):\s+/.test(body[i])) {
|
|
129
|
-
sessionIndex = i;
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
const sessionLine = sessionIndex >= 0 ? body[sessionIndex] : undefined;
|
|
134
|
-
const resultLines = sessionIndex >= 0 ? body.slice(0, sessionIndex) : body;
|
|
135
|
-
const resultPreview = resultLines.join(`
|
|
136
|
-
`).trim() || "(no output)";
|
|
137
|
-
let sessionLabel;
|
|
138
|
-
let sessionValue;
|
|
139
|
-
if (sessionLine) {
|
|
140
|
-
const separator = sessionLine.indexOf(":");
|
|
141
|
-
sessionLabel = sessionLine.slice(0, separator).toLowerCase();
|
|
142
|
-
sessionValue = sessionLine.slice(separator + 1).trim();
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
agent: match[2],
|
|
146
|
-
status: match[1],
|
|
147
|
-
...match[3] ? { taskInfo: match[3] } : {},
|
|
148
|
-
resultPreview,
|
|
149
|
-
...sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
290
|
|
|
153
291
|
class SubagentControlNoticeComponent {
|
|
154
292
|
details;
|
|
@@ -177,39 +315,90 @@ class SubagentControlNoticeComponent {
|
|
|
177
315
|
return lines;
|
|
178
316
|
}
|
|
179
317
|
}
|
|
180
|
-
export
|
|
318
|
+
export function projectActiveHerdrRuns(state) {
|
|
319
|
+
const active = (status) => status === "queued" || status === "running";
|
|
320
|
+
const foregroundChildrenByWorkflow = new Map;
|
|
321
|
+
for (const control of state.foregroundControls.values()) {
|
|
322
|
+
if (!control.parentWorkflowRunId)
|
|
323
|
+
continue;
|
|
324
|
+
const children = control.activeChildren?.size ? [...control.activeChildren.values()].map((child) => ({
|
|
325
|
+
agent: child.agent,
|
|
326
|
+
needsAttention: child.currentActivityState === "needs_attention"
|
|
327
|
+
})) : control.currentAgent ? [{ agent: control.currentAgent, needsAttention: control.currentActivityState === "needs_attention" }] : [];
|
|
328
|
+
if (children.length === 0)
|
|
329
|
+
continue;
|
|
330
|
+
const existing = foregroundChildrenByWorkflow.get(control.parentWorkflowRunId) ?? [];
|
|
331
|
+
existing.push(...children);
|
|
332
|
+
foregroundChildrenByWorkflow.set(control.parentWorkflowRunId, existing);
|
|
333
|
+
}
|
|
334
|
+
return [...state.asyncJobs.values()].filter((job) => active(job.status)).map((job) => {
|
|
335
|
+
const children = job.mode === "workflow" ? foregroundChildrenByWorkflow.get(job.asyncId) : undefined;
|
|
336
|
+
const currentStep = job.steps?.find((step) => step.status === "running") ?? (job.currentStep !== undefined ? job.steps?.[job.currentStep] : undefined) ?? job.steps?.find((step) => step.status === "pending");
|
|
337
|
+
return {
|
|
338
|
+
id: job.asyncId,
|
|
339
|
+
agents: children?.length ? children.map((child) => child.agent) : job.agents,
|
|
340
|
+
...currentStep?.label ? { taskLabel: currentStep.label } : {},
|
|
341
|
+
needsAttention: job.activityState === "needs_attention" || children?.some((child) => child.needsAttention)
|
|
342
|
+
};
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
export default function registerSubagentExtension(dm) {
|
|
181
346
|
if (process.env[SUBAGENT_CHILD_ENV] === "1") {
|
|
182
347
|
return;
|
|
183
348
|
}
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (typeof previousRuntimeCleanup === "function") {
|
|
188
|
-
try {
|
|
189
|
-
previousRuntimeCleanup();
|
|
190
|
-
} catch {}
|
|
191
|
-
}
|
|
192
|
-
ensureAccessibleDir(RESULTS_DIR);
|
|
193
|
-
ensureAccessibleDir(ASYNC_DIR);
|
|
349
|
+
const runtimeRegistry = getRuntimeRegistry();
|
|
350
|
+
DIRS.results = ensureAccessibleDir(DIRS.results);
|
|
351
|
+
DIRS.async = ensureAccessibleDir(DIRS.async);
|
|
194
352
|
cleanupOldChainDirs();
|
|
195
353
|
const config = loadConfig();
|
|
196
|
-
|
|
354
|
+
applyModelExclusionsConfig(config);
|
|
355
|
+
const waitToolConfig = resolveWaitToolConfig(config.waitTool);
|
|
356
|
+
const asyncByDefault = resolveAsyncByDefault(config);
|
|
357
|
+
const fleetViewEnabled = config.fleetView !== false;
|
|
358
|
+
const fleetViewPlacement = resolveFleetViewPlacement(config.fleetViewPlacement);
|
|
359
|
+
const asyncWidgetEnabled = config.asyncWidget !== false;
|
|
360
|
+
const summaryInlineToolDisplay = config.inlineToolDisplay === "summary";
|
|
197
361
|
const tempArtifactsDir = getArtifactsDir(null);
|
|
198
|
-
|
|
362
|
+
const artifactCleanupDays = config.artifactConfig?.cleanupDays ?? DEFAULT_ARTIFACT_CONFIG.cleanupDays;
|
|
363
|
+
cleanupAllArtifactDirs(artifactCleanupDays);
|
|
364
|
+
const resultIndexCleanupTimer = setTimeout(() => {
|
|
365
|
+
try {
|
|
366
|
+
cleanupResultIndexes(DIRS.results);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error("Failed to clean stale subagent result indexes:", error);
|
|
369
|
+
}
|
|
370
|
+
}, 30000);
|
|
371
|
+
resultIndexCleanupTimer.unref?.();
|
|
199
372
|
const state = {
|
|
200
373
|
baseCwd: "",
|
|
201
374
|
currentSessionId: null,
|
|
375
|
+
statusProjectionSessionId: null,
|
|
376
|
+
completionOwnerId: currentCompletionOwnerId(),
|
|
377
|
+
artifactDirPreference: config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
|
|
378
|
+
...config.authorityPolicy ? { authorityPolicy: config.authorityPolicy } : {},
|
|
379
|
+
...config.missions ? { missionStoreConfig: config.missions } : {},
|
|
380
|
+
parentSessionFile: null,
|
|
381
|
+
trustedSessionRoots: [],
|
|
202
382
|
subagentInProgress: false,
|
|
203
|
-
subagentSpawns: {
|
|
383
|
+
subagentSpawns: {
|
|
384
|
+
sessionId: null,
|
|
385
|
+
count: 0,
|
|
386
|
+
configuredLimit: resolveMaxSubagentSpawnsPerSession(config.maxSubagentSpawnsPerSession) ?? null,
|
|
387
|
+
granted: 0,
|
|
388
|
+
grantHistory: []
|
|
389
|
+
},
|
|
390
|
+
activeAsyncCapacity: { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession) ?? 0 },
|
|
391
|
+
herdrProjectPanes: new Map,
|
|
204
392
|
asyncJobs: new Map,
|
|
393
|
+
fleetJobs: new Map,
|
|
205
394
|
foregroundRuns: new Map,
|
|
206
395
|
foregroundControls: new Map,
|
|
207
396
|
lastForegroundControlId: null,
|
|
208
|
-
pendingForegroundControlNotices: new Map,
|
|
209
397
|
cleanupTimers: new Map,
|
|
210
398
|
lastUiContext: null,
|
|
211
399
|
poller: null,
|
|
212
400
|
completionSeen: new Map,
|
|
401
|
+
widgetsSuspended: false,
|
|
213
402
|
watcher: null,
|
|
214
403
|
watcherRestartTimer: null,
|
|
215
404
|
resultFileCoalescer: {
|
|
@@ -217,60 +406,144 @@ export default function registerSubagentExtension(pi) {
|
|
|
217
406
|
clear: () => {}
|
|
218
407
|
}
|
|
219
408
|
};
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
scheduledRunManager.stop();
|
|
227
|
-
supervisorChannel.dispose();
|
|
228
|
-
clearPendingForegroundControlNotices(state);
|
|
229
|
-
if (state.poller) {
|
|
230
|
-
clearInterval(state.poller);
|
|
231
|
-
state.poller = null;
|
|
232
|
-
}
|
|
409
|
+
const withLastUiContext = (run) => {
|
|
410
|
+
const cached = state.lastUiContext;
|
|
411
|
+
return withCachedUiContext(cached, () => {
|
|
412
|
+
if (state.lastUiContext === cached)
|
|
413
|
+
state.lastUiContext = null;
|
|
414
|
+
}, run);
|
|
233
415
|
};
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
416
|
+
const supervisorChannel = createNativeSupervisorChannel(dm, state);
|
|
417
|
+
const waitSubscriptionManager = createWaitSubscriptionManager(dm, state);
|
|
418
|
+
const mainWatchdog = registerMainWatchdog(dm);
|
|
419
|
+
const resultDeliveryOwnership = createResultDeliveryOwnership(state);
|
|
420
|
+
const completionNotifier = registerSubagentNotify(dm, state, { batchConfig: config.completionBatch, ownership: resultDeliveryOwnership });
|
|
421
|
+
const fleetStatus = fleetViewEnabled ? new SubagentFleetStatus(state, async (itemKey) => {
|
|
422
|
+
const ctx = withLastUiContext((current) => current);
|
|
423
|
+
if (!ctx)
|
|
424
|
+
return;
|
|
425
|
+
try {
|
|
426
|
+
await openSubagentFleet(ctx, state, { initialKey: itemKey, asyncDirRoot: DIRS.async, resultsDir: DIRS.results, fleetKeybindings: config.fleetKeybindings });
|
|
427
|
+
} catch (error) {
|
|
428
|
+
if (isStaleExtensionContextError(error)) {
|
|
429
|
+
if (state.lastUiContext === ctx)
|
|
430
|
+
state.lastUiContext = null;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
}, { placement: fleetViewPlacement }) : undefined;
|
|
436
|
+
let executorScheduled;
|
|
437
|
+
let goalTurnId = 0;
|
|
438
|
+
let parentSessionEnvValue = null;
|
|
439
|
+
const scheduledStoreRoot = config.scheduledRuns?.storeRoot === undefined ? undefined : resolveScheduledStoreRoot(config.scheduledRuns.storeRoot);
|
|
237
440
|
const scheduledRunManager = createScheduledRunManager({
|
|
238
441
|
config,
|
|
442
|
+
storeRoot: scheduledStoreRoot,
|
|
239
443
|
launch: (params, ctx, signal) => {
|
|
240
|
-
if (!
|
|
444
|
+
if (!executorScheduled) {
|
|
241
445
|
return Promise.resolve({
|
|
242
446
|
content: [{ type: "text", text: "Scheduled subagent launch is unavailable (executor not ready)." }],
|
|
243
447
|
isError: true,
|
|
244
448
|
details: { mode: "management", results: [] }
|
|
245
449
|
});
|
|
246
450
|
}
|
|
247
|
-
return
|
|
248
|
-
}
|
|
451
|
+
return executorScheduled(randomUUID(), params, signal, ctx);
|
|
452
|
+
},
|
|
453
|
+
resolveCapabilityCeiling: (sessionId) => resolveCurrentSubagentCapabilityCeiling(sessionId)
|
|
454
|
+
});
|
|
455
|
+
let refreshResultDelivery = () => {};
|
|
456
|
+
const hasResultDeliveryDemand = () => {
|
|
457
|
+
if ([...state.asyncJobs.values()].some((job) => job.status === "queued" || job.status === "running"))
|
|
458
|
+
return true;
|
|
459
|
+
if (state.foregroundControls.size > 0)
|
|
460
|
+
return true;
|
|
461
|
+
if (scheduledRunManager.observedCompletionRunIds().size > 0)
|
|
462
|
+
return true;
|
|
463
|
+
return missionObserverResultCandidateFiles(DIRS.results).length > 0;
|
|
464
|
+
};
|
|
465
|
+
const discoverAgentsForRuntime = (cwd, scope, preferredModelProvider) => {
|
|
466
|
+
if (listRuntimeAgentConfigs(dm).length === 0)
|
|
467
|
+
return discoverAgents(cwd, scope, preferredModelProvider);
|
|
468
|
+
const snapshot = discoverAgentSnapshot(cwd, scope, preferredModelProvider, { includeChains: false });
|
|
469
|
+
const discovered = snapshot.effective;
|
|
470
|
+
const all = snapshot.all;
|
|
471
|
+
const configuredAgents = [
|
|
472
|
+
...all.builtin,
|
|
473
|
+
...all.package,
|
|
474
|
+
...all.user,
|
|
475
|
+
...all.project
|
|
476
|
+
];
|
|
477
|
+
const merged = mergeRuntimeAgents(dm, discovered, configuredAgents);
|
|
478
|
+
if (discovered.maxThinking === undefined)
|
|
479
|
+
return merged;
|
|
480
|
+
return {
|
|
481
|
+
...merged,
|
|
482
|
+
agents: merged.agents.map((agent) => agent.maxThinking === discovered.maxThinking ? agent : { ...agent, maxThinking: discovered.maxThinking })
|
|
483
|
+
};
|
|
484
|
+
};
|
|
485
|
+
const { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs, dispose: disposeAsyncJobTracker } = createAsyncJobTracker(dm, state, DIRS.async, {
|
|
486
|
+
widgetEnabled: asyncWidgetEnabled,
|
|
487
|
+
onJobTerminal: () => refreshResultDelivery()
|
|
249
488
|
});
|
|
489
|
+
const resultWatcher = createResultWatcher(dm, state, DIRS.results, 10 * 60 * 1000, {
|
|
490
|
+
notifier: completionNotifier,
|
|
491
|
+
ownership: resultDeliveryOwnership,
|
|
492
|
+
observeCompletion: (result) => scheduledRunManager.handleAsyncCompletion(result),
|
|
493
|
+
observedCompletionRunIds: () => scheduledRunManager.observedCompletionRunIds(),
|
|
494
|
+
hasDeliveryDemand: hasResultDeliveryDemand,
|
|
495
|
+
deliverIntercomResults: config.intercomBridge?.resultDelivery === true,
|
|
496
|
+
resultScanLogging: config.resultScanLogging
|
|
497
|
+
});
|
|
498
|
+
const { startResultWatcher, transitionResultDelivery, primeExistingResults, stopResultWatcher } = resultWatcher;
|
|
499
|
+
refreshResultDelivery = resultWatcher.refreshResultDelivery;
|
|
500
|
+
const asyncRetentionAbort = new AbortController;
|
|
501
|
+
const asyncRetentionTimer = setTimeout(async () => {
|
|
502
|
+
try {
|
|
503
|
+
await cleanupAsyncRetention({
|
|
504
|
+
asyncDirRoot: DIRS.async,
|
|
505
|
+
resultsDir: DIRS.results,
|
|
506
|
+
signal: asyncRetentionAbort.signal,
|
|
507
|
+
protectedRunIds: new Set([
|
|
508
|
+
...state.asyncJobs.keys(),
|
|
509
|
+
...state.workflowControllers?.keys() ?? [],
|
|
510
|
+
...scheduledRunManager.referencedAsyncRunIds()
|
|
511
|
+
])
|
|
512
|
+
});
|
|
513
|
+
} catch (error) {
|
|
514
|
+
console.error("Failed to clean retained async subagent state:", error);
|
|
515
|
+
}
|
|
516
|
+
}, ASYNC_RETENTION_DELAY_MS);
|
|
517
|
+
asyncRetentionTimer.unref?.();
|
|
250
518
|
const executor = createSubagentExecutor({
|
|
251
|
-
|
|
519
|
+
dm,
|
|
252
520
|
state,
|
|
253
521
|
config,
|
|
254
522
|
asyncByDefault,
|
|
523
|
+
waitToolEnabled: waitToolConfig.enabled,
|
|
524
|
+
waitToolDefaultTimeoutMs: waitToolConfig.defaultTimeoutMs,
|
|
255
525
|
handleScheduledRunAction: (params, ctx) => scheduledRunManager.handleToolCall(params, ctx),
|
|
526
|
+
watchdog: mainWatchdog,
|
|
256
527
|
tempArtifactsDir,
|
|
257
528
|
getSubagentSessionRoot,
|
|
258
529
|
expandTilde,
|
|
259
|
-
discoverAgents
|
|
530
|
+
discoverAgents: discoverAgentsForRuntime,
|
|
531
|
+
activateSupervisorTransport: () => supervisorChannel.activateTransport(),
|
|
532
|
+
refreshResultDelivery: () => refreshResultDelivery()
|
|
260
533
|
});
|
|
261
|
-
|
|
262
|
-
|
|
534
|
+
executorScheduled = executor.executeScheduled;
|
|
535
|
+
dm.registerMessageRenderer(SLASH_RESULT_TYPE, (message, options, theme) => {
|
|
263
536
|
const details = resolveSlashMessageDetails(message.details);
|
|
264
537
|
if (!details)
|
|
265
538
|
return;
|
|
266
|
-
return createSlashResultComponent(details, options, theme);
|
|
539
|
+
return createSlashResultComponent(details, options, theme, config.mainWindowRenderer, config.foregroundDetachShortcut);
|
|
267
540
|
});
|
|
268
|
-
|
|
541
|
+
dm.registerMessageRenderer(SLASH_TEXT_RESULT_TYPE, (message, _options, _theme) => {
|
|
269
542
|
const content = typeof message.content === "string" ? message.content : message.content.filter((entry) => entry.type === "text").map((entry) => entry.text).join(`
|
|
270
543
|
`);
|
|
271
544
|
return new Text(content, 0, 0);
|
|
272
545
|
});
|
|
273
|
-
|
|
546
|
+
dm.registerMessageRenderer("subagent-notify", (message, options, theme) => {
|
|
274
547
|
const content = typeof message.content === "string" ? message.content : "";
|
|
275
548
|
const details = message.details ?? parseSubagentNotifyContent(content);
|
|
276
549
|
if (!details)
|
|
@@ -297,6 +570,18 @@ export default function registerSubagentExtension(pi) {
|
|
|
297
570
|
const expandKey = keyText("app.tools.expand");
|
|
298
571
|
text += `
|
|
299
572
|
${theme.fg("dim", `${expandKey} full notification`)}`;
|
|
573
|
+
}
|
|
574
|
+
if (details.workflowRunId) {
|
|
575
|
+
text += `
|
|
576
|
+
${theme.fg("muted", `workflow: ${details.workflowRunId}`)}`;
|
|
577
|
+
}
|
|
578
|
+
if (details.childRuns?.length) {
|
|
579
|
+
text += `
|
|
580
|
+
${theme.fg("muted", `children: ${details.childRuns.map((child) => `${child.workflowKey ?? child.agent ?? "child"}=${child.runId}`).join(", ")}`)}`;
|
|
581
|
+
}
|
|
582
|
+
if (details.reconciledFromDetachedChild) {
|
|
583
|
+
text += `
|
|
584
|
+
${theme.fg("muted", `reconciled child: ${details.reconciledFromDetachedChild}`)}`;
|
|
300
585
|
}
|
|
301
586
|
if (details.sessionLabel && details.sessionValue) {
|
|
302
587
|
text += `
|
|
@@ -304,7 +589,13 @@ export default function registerSubagentExtension(pi) {
|
|
|
304
589
|
}
|
|
305
590
|
return new Text(text, 0, 0);
|
|
306
591
|
});
|
|
307
|
-
|
|
592
|
+
dm.registerMessageRenderer(SUBAGENT_STEERING_MESSAGE_TYPE, (message, _options, theme) => {
|
|
593
|
+
const details = message.details;
|
|
594
|
+
if (!details)
|
|
595
|
+
return;
|
|
596
|
+
return new Text(theme.fg(details.state === "recovered" ? "warning" : "error", formatSteeringNotice(details)), 0, 0);
|
|
597
|
+
});
|
|
598
|
+
dm.registerMessageRenderer(SUBAGENT_CONTROL_MESSAGE_TYPE, (message, _options, theme) => {
|
|
308
599
|
const details = message.details;
|
|
309
600
|
if (!details?.event)
|
|
310
601
|
return;
|
|
@@ -314,144 +605,148 @@ export default function registerSubagentExtension(pi) {
|
|
|
314
605
|
const executeSubagentCollapsed = (id, params, signal, onUpdate, ctx) => {
|
|
315
606
|
if (ctx.hasUI)
|
|
316
607
|
ctx.ui.setToolsExpanded(false);
|
|
317
|
-
return executor.
|
|
608
|
+
return executor.executePublic(id, params, signal, onUpdate, ctx);
|
|
318
609
|
};
|
|
319
610
|
const slashBridge = registerSlashSubagentBridge({
|
|
320
|
-
events:
|
|
611
|
+
events: dm.events,
|
|
321
612
|
getContext: () => state.lastUiContext,
|
|
322
613
|
execute: (id, params, signal, onUpdate, ctx) => executeSubagentCollapsed(id, params, signal, onUpdate, ctx)
|
|
323
614
|
});
|
|
324
615
|
const promptTemplateBridge = registerPromptTemplateDelegationBridge({
|
|
325
|
-
events:
|
|
616
|
+
events: dm.events,
|
|
326
617
|
getContext: () => state.lastUiContext,
|
|
327
|
-
execute:
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
cwd: request.cwd,
|
|
333
|
-
worktree: request.worktree,
|
|
334
|
-
async: false,
|
|
335
|
-
clarify: false
|
|
336
|
-
}, signal, onUpdate, ctx);
|
|
337
|
-
}
|
|
338
|
-
return executeSubagentCollapsed(requestId, {
|
|
339
|
-
agent: request.agent,
|
|
340
|
-
task: request.task,
|
|
341
|
-
context: request.context,
|
|
342
|
-
cwd: request.cwd,
|
|
343
|
-
model: request.model,
|
|
344
|
-
async: false,
|
|
345
|
-
clarify: false
|
|
346
|
-
}, signal, onUpdate, ctx);
|
|
618
|
+
execute: (requestId, params, signal, ctx, onUpdate) => executeSubagentCollapsed(requestId, params, signal, onUpdate, ctx),
|
|
619
|
+
executeStructured: (requestId, params, signal, ctx, onUpdate) => {
|
|
620
|
+
if (ctx.hasUI)
|
|
621
|
+
ctx.ui.setToolsExpanded(false);
|
|
622
|
+
return executor.executeDelegated(requestId, params, signal, onUpdate, ctx);
|
|
347
623
|
}
|
|
348
624
|
});
|
|
349
625
|
const rpcBridge = registerSubagentRpcBridge({
|
|
350
|
-
events:
|
|
626
|
+
events: dm.events,
|
|
351
627
|
getContext: () => state.lastUiContext,
|
|
352
|
-
execute: (id, params, signal, onUpdate, ctx) => executor.
|
|
628
|
+
execute: (id, params, signal, onUpdate, ctx) => executor.executePublic(id, params, signal, onUpdate, ctx),
|
|
629
|
+
state
|
|
353
630
|
});
|
|
354
|
-
|
|
355
|
-
if (!tasks || tasks.length === 0)
|
|
356
|
-
return 0;
|
|
357
|
-
return tasks.reduce((total, task) => {
|
|
358
|
-
const count = typeof task.count === "number" && Number.isInteger(task.count) && task.count >= 1 ? task.count : 1;
|
|
359
|
-
return total + count;
|
|
360
|
-
}, 0);
|
|
361
|
-
}
|
|
631
|
+
const parameters = createSubagentParamsSchema();
|
|
362
632
|
const tool = {
|
|
363
633
|
name: "subagent",
|
|
364
634
|
label: "Subagent",
|
|
365
635
|
description: buildSubagentToolDescription(config),
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
636
|
+
...buildSubagentToolPromptMetadata(config),
|
|
637
|
+
parameters,
|
|
638
|
+
async execute(id, params, signal, onUpdate, ctx) {
|
|
639
|
+
return finalizeToolResult(await executeSubagentCollapsed(id, params, signal ?? new AbortController().signal, onUpdate, ctx));
|
|
369
640
|
},
|
|
370
641
|
renderCall(args, theme) {
|
|
642
|
+
const gap = " ".repeat(config.mainWindowRenderer?.horizontalSpacing ?? 1);
|
|
643
|
+
const title = theme.fg("toolTitle", theme.bold("subagent"));
|
|
371
644
|
if (args.action) {
|
|
372
|
-
const target = args.agent ||
|
|
373
|
-
return new Text(`${
|
|
645
|
+
const target = args.agent || "";
|
|
646
|
+
return new Text(`${title}${gap}${args.action}${target ? `${gap}${theme.fg("accent", target)}` : ""}`, 0, 0);
|
|
374
647
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
return new Text(`${theme.fg("toolTitle", theme.bold("subagent "))}parallel (${parallelCount})${asyncLabel}`, 0, 0);
|
|
382
|
-
return new Text(`${theme.fg("toolTitle", theme.bold("subagent "))}${theme.fg("accent", args.agent || "?")}${asyncLabel}`, 0, 0);
|
|
648
|
+
if (args.workflowScript)
|
|
649
|
+
return new Text(`${title}${gap}${formatWorkflowManifest(args.workflowScript, args.async, false, args.preflight)}`, 0, 0);
|
|
650
|
+
if (args.workflowScriptPath)
|
|
651
|
+
return new Text(`${title}${gap}${theme.fg("accent", args.workflowScriptPath)}${args.async === true ? `${gap}${theme.fg("warning", "[async]")}` : ""}${args.preflight !== undefined ? `${gap}${theme.fg("dim", formatWorkflowPreflightCall(args.preflight))}` : ""}`, 0, 0);
|
|
652
|
+
const asyncLabel = args.async === true ? `${gap}${theme.fg("warning", "[async]")}` : "";
|
|
653
|
+
return new Text(`${title}${gap}${theme.fg("accent", args.agent || "?")}${asyncLabel}`, 0, 0);
|
|
383
654
|
},
|
|
384
655
|
renderResult(result, options, theme, context) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
clearLegacyResultAnimationTimer(context);
|
|
389
|
-
}
|
|
390
|
-
const frame = context.state?.frame ?? 0;
|
|
391
|
-
return renderSubagentResult(result, options, theme, frame);
|
|
656
|
+
clearLegacyResultAnimationTimer(context);
|
|
657
|
+
const renderedResult = { ...result, isError: context.isError };
|
|
658
|
+
return summaryInlineToolDisplay ? renderSubagentSummary(renderedResult, options, theme) : renderSubagentResult(renderedResult, options, theme, undefined, config.mainWindowRenderer, config.foregroundDetachShortcut);
|
|
392
659
|
}
|
|
393
660
|
};
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
661
|
+
dm.registerTool(tool);
|
|
662
|
+
registerWaitTool(dm, state, waitToolConfig.enabled, waitSubscriptionManager, waitToolConfig.defaultTimeoutMs);
|
|
663
|
+
dm.on("agent_end", async (_event, ctx) => {
|
|
664
|
+
if (!ctx.hasUI)
|
|
665
|
+
await drainOutstandingWork({ state, events: dm.events });
|
|
666
|
+
const ownerSessionId = state.currentSessionId;
|
|
667
|
+
if (!ownerSessionId)
|
|
668
|
+
return;
|
|
669
|
+
goalTurnId += 1;
|
|
670
|
+
try {
|
|
671
|
+
const location = resolveMissionStoreLocation({ projectRoot: state.baseCwd, ...config.missions ? { config: config.missions } : {} });
|
|
672
|
+
const retainedChildren = listRetainedChildren(DIRS.async, ownerSessionId);
|
|
673
|
+
for (const notice of collectGoalContinuationNotices({ location, ownerSessionId, retainedChildren, turnId: goalTurnId })) {
|
|
674
|
+
handleSubagentControlNotice({
|
|
675
|
+
dm,
|
|
676
|
+
state,
|
|
677
|
+
visibleControlNotices: new Set,
|
|
678
|
+
details: { source: "goal", event: notice.event, noticeText: notice.message }
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
} catch (error) {
|
|
682
|
+
console.error("Failed to evaluate goal missions:", error);
|
|
411
683
|
}
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
684
|
+
});
|
|
685
|
+
const disposeSlashCommands = registerSlashCommands(dm, state, {
|
|
686
|
+
fleetKeybindings: config.fleetKeybindings,
|
|
687
|
+
foregroundDetachShortcut: config.foregroundDetachShortcut
|
|
688
|
+
});
|
|
689
|
+
let visibleControlNotices = new Set;
|
|
690
|
+
const activeHerdrRuns = () => projectActiveHerdrRuns(state);
|
|
691
|
+
const herdrStatusBridge = registerHerdrStatusBridge({
|
|
692
|
+
events: dm.events,
|
|
693
|
+
getRuns: activeHerdrRuns,
|
|
694
|
+
getProjectPaneCount: () => [...state.herdrProjectPanes?.values() ?? []].filter((pane) => pane.state === "open").length,
|
|
695
|
+
async runHerdr(args) {
|
|
696
|
+
await dm.exec(process.env.HERDR_BIN || "herdr", [...args], { timeout: 5000 });
|
|
425
697
|
}
|
|
426
|
-
}
|
|
427
|
-
registerSubagentNotify(pi, state, { batchConfig: config.completionBatch });
|
|
428
|
-
const existingVisibleControlNotices = globalStore[controlNoticeSeenStoreKey];
|
|
429
|
-
const visibleControlNotices = existingVisibleControlNotices instanceof Set ? existingVisibleControlNotices : new Set;
|
|
430
|
-
globalStore[controlNoticeSeenStoreKey] = visibleControlNotices;
|
|
698
|
+
});
|
|
431
699
|
const controlEventHandler = (payload) => {
|
|
432
700
|
handleSubagentControlNotice({
|
|
433
|
-
|
|
701
|
+
dm,
|
|
434
702
|
state,
|
|
435
703
|
visibleControlNotices,
|
|
436
704
|
details: payload
|
|
437
705
|
});
|
|
438
706
|
};
|
|
707
|
+
const steeringNoticeHandler = (payload) => {
|
|
708
|
+
handleSubagentSteeringNotice({ dm, state, details: payload });
|
|
709
|
+
};
|
|
710
|
+
const asyncStartedHandler = (payload) => {
|
|
711
|
+
handleStarted(payload);
|
|
712
|
+
supervisorChannel.activateTransport();
|
|
713
|
+
refreshResultDelivery();
|
|
714
|
+
fleetStatus?.refresh();
|
|
715
|
+
};
|
|
716
|
+
const asyncCompleteHandler = (payload) => {
|
|
717
|
+
handleComplete(payload);
|
|
718
|
+
refreshResultDelivery();
|
|
719
|
+
refreshActiveAsyncCapacity();
|
|
720
|
+
scheduledRunManager.handleAsyncCompletion(payload);
|
|
721
|
+
fleetStatus?.refresh();
|
|
722
|
+
};
|
|
439
723
|
const eventUnsubscribes = [
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
724
|
+
registerRuntimeAgentEventListener(dm),
|
|
725
|
+
dm.events.on(SUBAGENT_ASYNC_STARTED_EVENT, asyncStartedHandler),
|
|
726
|
+
dm.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, asyncCompleteHandler),
|
|
727
|
+
dm.events.on(SUBAGENT_PROCESS_TERMINAL_EVENT, () => {
|
|
728
|
+
refreshActiveAsyncCapacity();
|
|
729
|
+
fleetStatus?.refresh();
|
|
730
|
+
}),
|
|
731
|
+
dm.events.on(SUBAGENT_CONTROL_EVENT, controlEventHandler),
|
|
732
|
+
dm.events.on(SUBAGENT_STEERING_NOTICE_EVENT, steeringNoticeHandler),
|
|
733
|
+
herdrStatusBridge.dispose,
|
|
443
734
|
rpcBridge.dispose
|
|
444
735
|
];
|
|
445
|
-
|
|
446
|
-
pi.on("tool_result", (event, ctx) => {
|
|
736
|
+
dm.on("tool_result", (event, ctx) => {
|
|
447
737
|
if (event.toolName !== "subagent")
|
|
448
738
|
return;
|
|
449
739
|
if (!ctx.hasUI)
|
|
450
740
|
return;
|
|
451
741
|
state.lastUiContext = ctx;
|
|
742
|
+
const activeJobCount = state.asyncJobs.size;
|
|
743
|
+
restoreActiveJobs(ctx);
|
|
744
|
+
if (state.asyncJobs.size > activeJobCount)
|
|
745
|
+
herdrStatusBridge.syncRuns();
|
|
746
|
+
fleetStatus?.setContext(ctx);
|
|
747
|
+
fleetStatus?.refresh();
|
|
452
748
|
if (state.asyncJobs.size > 0) {
|
|
453
|
-
|
|
454
|
-
ctx.ui.requestRender?.();
|
|
749
|
+
refreshWidget(ctx);
|
|
455
750
|
ensurePoller();
|
|
456
751
|
}
|
|
457
752
|
});
|
|
@@ -459,71 +754,222 @@ wait also returns when a run needs attention (a child that went idle or blocked
|
|
|
459
754
|
try {
|
|
460
755
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
461
756
|
if (sessionFile) {
|
|
462
|
-
cleanupOldArtifacts(getArtifactsDir(sessionFile),
|
|
757
|
+
cleanupOldArtifacts(getArtifactsDir(sessionFile), artifactCleanupDays);
|
|
463
758
|
}
|
|
464
759
|
} catch {}
|
|
465
760
|
};
|
|
466
|
-
const
|
|
761
|
+
const suspendWidgetsForCompaction = () => {
|
|
762
|
+
if (state.widgetsSuspended)
|
|
763
|
+
return;
|
|
764
|
+
state.widgetsSuspended = true;
|
|
765
|
+
withLastUiContext((ctx) => ctx.ui.setWidget(WIDGET_KEY, undefined));
|
|
766
|
+
fleetStatus?.refresh();
|
|
767
|
+
};
|
|
768
|
+
const resumeWidgetsAfterCompaction = () => {
|
|
769
|
+
if (!state.widgetsSuspended)
|
|
770
|
+
return;
|
|
771
|
+
state.widgetsSuspended = false;
|
|
772
|
+
withLastUiContext((ctx) => refreshWidget(ctx));
|
|
773
|
+
fleetStatus?.refresh();
|
|
774
|
+
};
|
|
775
|
+
const refreshActiveAsyncCapacity = () => {
|
|
776
|
+
if (!state.currentSessionId) {
|
|
777
|
+
state.activeAsyncCapacity = { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession) ?? 0 };
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(state.currentSessionId, resolveMaxActiveAsyncRunsPerSession(config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
781
|
+
};
|
|
782
|
+
const resetSessionState = (ctx, recovering, previousSessionFile) => {
|
|
783
|
+
state.widgetsSuspended = false;
|
|
467
784
|
state.baseCwd = ctx.cwd;
|
|
785
|
+
goalTurnId = 0;
|
|
786
|
+
const previousRuntimeSessionId = state.currentSessionId;
|
|
787
|
+
resultDeliveryOwnership.claimPredecessor(previousSessionFile, previousRuntimeSessionId);
|
|
468
788
|
state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
469
|
-
|
|
789
|
+
transitionResultDelivery();
|
|
790
|
+
state.parentSessionFile = ctx.sessionManager.getSessionFile();
|
|
791
|
+
state.trustedSessionFileRoot = state.parentSessionFile ? path.join(getAgentDir(), "sessions") : undefined;
|
|
792
|
+
state.trustedSessionRoots = [...new Set([
|
|
793
|
+
...config.defaultSessionDir ? [path.resolve(expandTilde(config.defaultSessionDir))] : [],
|
|
794
|
+
...state.parentSessionFile ? [getSubagentSessionRoot(state.parentSessionFile)] : []
|
|
795
|
+
])];
|
|
796
|
+
state.subagentSpawns = {
|
|
797
|
+
sessionId: state.currentSessionId,
|
|
798
|
+
count: 0,
|
|
799
|
+
configuredLimit: resolveMaxSubagentSpawnsPerSession(config.maxSubagentSpawnsPerSession) ?? null,
|
|
800
|
+
granted: 0,
|
|
801
|
+
grantHistory: []
|
|
802
|
+
};
|
|
803
|
+
const projectPaneOwnerRoot = path.resolve(ctx.cwd);
|
|
804
|
+
restoreHerdrProjectPaneSnapshots(state, [...new Set([...state.herdrProjectPanes?.keys() ?? [], ...listHerdrProjectPaneRoots(projectPaneOwnerRoot), projectPaneOwnerRoot])]);
|
|
470
805
|
if (!process.env[SUBAGENT_CHILD_ENV]) {
|
|
471
806
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
472
807
|
if (sessionId) {
|
|
473
808
|
process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId;
|
|
809
|
+
parentSessionEnvValue = sessionId;
|
|
474
810
|
}
|
|
475
811
|
}
|
|
476
812
|
state.lastUiContext = ctx;
|
|
813
|
+
let phaseStartedAt = Date.now();
|
|
814
|
+
refreshActiveAsyncCapacity();
|
|
815
|
+
logSlowPhase("active-capacity", phaseStartedAt);
|
|
816
|
+
phaseStartedAt = Date.now();
|
|
477
817
|
cleanupSessionArtifacts(ctx);
|
|
478
|
-
|
|
818
|
+
logSlowPhase("session-artifact-cleanup", phaseStartedAt);
|
|
819
|
+
state.foregroundControls.clear();
|
|
820
|
+
state.lastForegroundControlId = null;
|
|
821
|
+
phaseStartedAt = Date.now();
|
|
479
822
|
resetJobs(ctx);
|
|
823
|
+
logSlowPhase("reset-jobs", phaseStartedAt);
|
|
824
|
+
phaseStartedAt = Date.now();
|
|
825
|
+
restoreForegroundRunHistory(state, { resultsDir: DIRS.results });
|
|
826
|
+
logSlowPhase("foreground-history", phaseStartedAt);
|
|
827
|
+
phaseStartedAt = Date.now();
|
|
480
828
|
restoreActiveJobs(ctx);
|
|
829
|
+
logSlowPhase("active-job-restore", phaseStartedAt);
|
|
830
|
+
phaseStartedAt = Date.now();
|
|
481
831
|
scheduledRunManager.bindSession(ctx);
|
|
832
|
+
logSlowPhase("scheduled-runs", phaseStartedAt);
|
|
833
|
+
phaseStartedAt = Date.now();
|
|
482
834
|
restoreSlashFinalSnapshots(ctx.sessionManager.getEntries());
|
|
483
|
-
|
|
835
|
+
logSlowPhase("slash-snapshots", phaseStartedAt);
|
|
836
|
+
phaseStartedAt = Date.now();
|
|
837
|
+
waitSubscriptionManager.restore();
|
|
838
|
+
logSlowPhase("wait-subscriptions", phaseStartedAt);
|
|
839
|
+
phaseStartedAt = Date.now();
|
|
840
|
+
startResultWatcher();
|
|
841
|
+
logSlowPhase("result-watcher-start", phaseStartedAt);
|
|
842
|
+
phaseStartedAt = Date.now();
|
|
843
|
+
primeExistingResults({ triggerTurn: !recovering });
|
|
844
|
+
logSlowPhase("result-prime", phaseStartedAt);
|
|
845
|
+
fleetStatus?.setContext(ctx);
|
|
484
846
|
};
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
847
|
+
let runtimeCleaned = false;
|
|
848
|
+
const runtimeEntry = {
|
|
849
|
+
sessionManager: null,
|
|
850
|
+
visibleControlNotices,
|
|
851
|
+
cleanup() {
|
|
852
|
+
if (runtimeCleaned)
|
|
853
|
+
return;
|
|
854
|
+
runtimeCleaned = true;
|
|
855
|
+
const shuttingDownParentSession = parentSessionEnvValue;
|
|
856
|
+
for (const controller of state.workflowControllers?.values() ?? []) {
|
|
857
|
+
if (!controller.signal.aborted)
|
|
858
|
+
controller.abort(new Error("Workflow stopped because the extension session was replaced or reloaded."));
|
|
859
|
+
}
|
|
860
|
+
state.workflowControllers?.clear();
|
|
861
|
+
state.workflowChildStops?.clear();
|
|
862
|
+
clearRuntimeAgentsForPi(dm);
|
|
863
|
+
clearTimeout(resultIndexCleanupTimer);
|
|
864
|
+
clearTimeout(asyncRetentionTimer);
|
|
865
|
+
asyncRetentionAbort.abort();
|
|
866
|
+
stopResultWatcher();
|
|
867
|
+
resultDeliveryOwnership.clear();
|
|
868
|
+
completionNotifier.dispose();
|
|
869
|
+
mainWatchdog.dispose();
|
|
870
|
+
scheduledRunManager.stop();
|
|
871
|
+
supervisorChannel.dispose();
|
|
872
|
+
waitSubscriptionManager.dispose();
|
|
873
|
+
fleetStatus?.dispose();
|
|
874
|
+
disposeAsyncJobTracker();
|
|
875
|
+
for (const timer of state.cleanupTimers.values())
|
|
876
|
+
clearTimeout(timer);
|
|
877
|
+
state.cleanupTimers.clear();
|
|
878
|
+
state.asyncJobs.clear();
|
|
879
|
+
for (const unsubscribe of eventUnsubscribes) {
|
|
880
|
+
try {
|
|
881
|
+
unsubscribe();
|
|
882
|
+
} catch {}
|
|
883
|
+
}
|
|
884
|
+
disposeSlashCommands.dispose();
|
|
885
|
+
slashBridge.cancelAll();
|
|
886
|
+
slashBridge.dispose();
|
|
887
|
+
promptTemplateBridge.cancelAll();
|
|
888
|
+
promptTemplateBridge.dispose();
|
|
889
|
+
state.widgetsSuspended = false;
|
|
890
|
+
state.currentSessionId = null;
|
|
891
|
+
state.statusProjectionSessionId = null;
|
|
892
|
+
state.parentSessionFile = null;
|
|
893
|
+
parentSessionEnvValue = null;
|
|
894
|
+
if (shuttingDownParentSession && process.env[SUBAGENT_PARENT_SESSION_ENV] === shuttingDownParentSession) {
|
|
895
|
+
delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
896
|
+
}
|
|
897
|
+
if (runtimeEntry.sessionManager && runtimeRegistry.bySessionManager.get(runtimeEntry.sessionManager) === runtimeEntry) {
|
|
898
|
+
runtimeRegistry.bySessionManager.delete(runtimeEntry.sessionManager);
|
|
899
|
+
}
|
|
900
|
+
runtimeRegistry.activeEntries.delete(runtimeEntry);
|
|
901
|
+
if (runtimeRegistry.activeEntries.size === 0)
|
|
902
|
+
clearSlashSnapshots();
|
|
493
903
|
try {
|
|
494
|
-
|
|
495
|
-
|
|
904
|
+
if (state.lastUiContext?.hasUI)
|
|
905
|
+
state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
|
|
906
|
+
} catch (error) {
|
|
907
|
+
if (!isStaleExtensionContextError(error))
|
|
908
|
+
throw error;
|
|
909
|
+
}
|
|
910
|
+
state.lastUiContext = null;
|
|
496
911
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
scheduledRunManager.stop();
|
|
502
|
-
if (state.poller)
|
|
503
|
-
clearInterval(state.poller);
|
|
504
|
-
state.poller = null;
|
|
505
|
-
clearPendingForegroundControlNotices(state);
|
|
506
|
-
for (const timer of state.cleanupTimers.values()) {
|
|
507
|
-
clearTimeout(timer);
|
|
508
|
-
}
|
|
509
|
-
state.cleanupTimers.clear();
|
|
510
|
-
state.asyncJobs.clear();
|
|
511
|
-
clearSlashSnapshots();
|
|
512
|
-
slashBridge.cancelAll();
|
|
513
|
-
slashBridge.dispose();
|
|
514
|
-
promptTemplateBridge.cancelAll();
|
|
515
|
-
promptTemplateBridge.dispose();
|
|
516
|
-
supervisorChannel.dispose();
|
|
517
|
-
if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
|
|
518
|
-
delete globalStore[runtimeCleanupStoreKey];
|
|
912
|
+
};
|
|
913
|
+
const installRuntime = (ctx) => {
|
|
914
|
+
if (runtimeCleaned) {
|
|
915
|
+
throw new Error("Cannot restart a cleaned dm-subagents extension runtime; register a new extension instance.");
|
|
519
916
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
917
|
+
const sessionManager = ctx.sessionManager;
|
|
918
|
+
const previousRuntime = runtimeRegistry.bySessionManager.get(sessionManager);
|
|
919
|
+
const existingVisibleControlNotices = runtimeRegistry.visibleControlNoticesBySessionManager.get(sessionManager);
|
|
920
|
+
if (existingVisibleControlNotices) {
|
|
921
|
+
visibleControlNotices = existingVisibleControlNotices;
|
|
922
|
+
} else {
|
|
923
|
+
runtimeRegistry.visibleControlNoticesBySessionManager.set(sessionManager, visibleControlNotices);
|
|
527
924
|
}
|
|
925
|
+
runtimeEntry.visibleControlNotices = visibleControlNotices;
|
|
926
|
+
if (runtimeEntry.sessionManager && runtimeEntry.sessionManager !== sessionManager && runtimeRegistry.bySessionManager.get(runtimeEntry.sessionManager) === runtimeEntry) {
|
|
927
|
+
runtimeRegistry.bySessionManager.delete(runtimeEntry.sessionManager);
|
|
928
|
+
}
|
|
929
|
+
runtimeEntry.sessionManager = sessionManager;
|
|
930
|
+
runtimeRegistry.bySessionManager.set(sessionManager, runtimeEntry);
|
|
931
|
+
runtimeRegistry.activeEntries.add(runtimeEntry);
|
|
932
|
+
if (previousRuntime && previousRuntime !== runtimeEntry) {
|
|
933
|
+
try {
|
|
934
|
+
previousRuntime.cleanup();
|
|
935
|
+
} catch {}
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
dm.on("agent_start", () => {
|
|
939
|
+
resumeWidgetsAfterCompaction();
|
|
940
|
+
herdrStatusBridge.agentStarted();
|
|
941
|
+
});
|
|
942
|
+
dm.on("agent_end", () => {
|
|
943
|
+
resumeWidgetsAfterCompaction();
|
|
944
|
+
});
|
|
945
|
+
dm.on("session_before_compact", (event) => {
|
|
946
|
+
if (event.reason !== "manual")
|
|
947
|
+
suspendWidgetsForCompaction();
|
|
948
|
+
});
|
|
949
|
+
dm.on("session_compact", () => {
|
|
950
|
+
const hasActiveAsyncWork = [...state.asyncJobs.values()].some((job) => job.status === "queued" || job.status === "running");
|
|
951
|
+
if (!hasActiveAsyncWork || !withLastUiContext(() => true))
|
|
952
|
+
return;
|
|
953
|
+
dm.sendMessage({
|
|
954
|
+
customType: "subagent-compaction-resume",
|
|
955
|
+
content: "Compaction is complete. Resume the parent task now; background subagent results will arrive separately when ready.",
|
|
956
|
+
display: false
|
|
957
|
+
}, { triggerTurn: true });
|
|
958
|
+
});
|
|
959
|
+
dm.on("session_start", (event, ctx) => {
|
|
960
|
+
installRuntime(ctx);
|
|
961
|
+
const recovering = event.reason === "startup" || event.reason === "reload" || event.reason === "resume";
|
|
962
|
+
resetSessionState(ctx, recovering, event.previousSessionFile);
|
|
963
|
+
herdrStatusBridge.sessionStarted({
|
|
964
|
+
hasUI: ctx.hasUI === true,
|
|
965
|
+
runs: activeHerdrRuns()
|
|
966
|
+
});
|
|
967
|
+
rpcBridge.emitReady(ctx);
|
|
968
|
+
supervisorChannel.start();
|
|
969
|
+
supervisorChannel.activateTransport();
|
|
970
|
+
});
|
|
971
|
+
dm.on("session_shutdown", async () => {
|
|
972
|
+
runtimeEntry.cleanup();
|
|
973
|
+
await herdrStatusBridge.flush();
|
|
528
974
|
});
|
|
529
975
|
}
|