@duckmind/dm-windows-x64 0.61.5 → 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
|
@@ -2,8 +2,10 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { keyText } from "@duckmind/dm-coding-agent";
|
|
5
|
-
import { Key, matchesKey } from "@duckmind/dm-tui";
|
|
6
|
-
import { BUILTIN_AGENT_NAMES, discoverAgents,
|
|
5
|
+
import { Key, matchesKey, truncateToWidth } from "@duckmind/dm-tui";
|
|
6
|
+
import { BUILTIN_AGENT_NAMES, discoverAgentSnapshot, discoverAgents, findBlockingAgentDiagnostic, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext } from "../agents/agents.js";
|
|
7
|
+
import { listRuntimeAgentConfigs, mergeRuntimeAgents } from "../agents/runtime-agent-registry.js";
|
|
8
|
+
import { resolveExistingReadPaths } from "../shared/settings.js";
|
|
7
9
|
import {
|
|
8
10
|
DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS,
|
|
9
11
|
applySubagentProfile,
|
|
@@ -13,12 +15,21 @@ import {
|
|
|
13
15
|
readSubagentProfile,
|
|
14
16
|
refreshProviderModelCatalog
|
|
15
17
|
} from "../profiles/profiles.js";
|
|
16
|
-
import { isDynamicParallelStep, isParallelStep } from "../shared/settings.js";
|
|
17
18
|
import { findModelInfo, toModelInfo } from "../shared/model-info.js";
|
|
18
|
-
import { formatTokens } from "../shared/formatters.js";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import { formatTokens, shortenPath } from "../shared/formatters.js";
|
|
20
|
+
import { listAsyncRuns, formatAsyncRunProgressLabel } from "../runs/background/async-status.js";
|
|
21
|
+
import { encodeInspectReply, handleInspectRpcArgs, INSPECT_WIDGET_KEY } from "../runs/background/inspect-rpc.js";
|
|
22
|
+
import { listScheduledRunSummaries } from "../runs/background/scheduled-runs.js";
|
|
23
|
+
import { SUBAGENT_FANOUT_CHILD_ENV } from "../runs/shared/dm-args.js";
|
|
24
|
+
import { resolveAsyncStatusChild } from "../runs/shared/child-identity.js";
|
|
25
|
+
import { readStatus } from "../shared/utils.js";
|
|
26
|
+
import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.js";
|
|
27
|
+
import { readWorkflowReceipt } from "../workflows/workflow-receipt.js";
|
|
28
|
+
import { FLEET_OPEN_SHORTCUT } from "../shared/shortcuts.js";
|
|
21
29
|
import { registerPromptWorkflowCommands } from "./prompt-workflows.js";
|
|
30
|
+
import { openSubagentsAdmin } from "./subagents-admin.js";
|
|
31
|
+
import { SUBAGENT_GUIDE_TOPICS } from "../extension/subagent-guide.js";
|
|
32
|
+
import { openSubagentFleet } from "../tui/fleet.js";
|
|
22
33
|
import {
|
|
23
34
|
applySlashUpdate,
|
|
24
35
|
buildSlashInitialResult,
|
|
@@ -33,7 +44,8 @@ import {
|
|
|
33
44
|
SLASH_SUBAGENT_REQUEST_EVENT,
|
|
34
45
|
SLASH_SUBAGENT_RESPONSE_EVENT,
|
|
35
46
|
SLASH_SUBAGENT_STARTED_EVENT,
|
|
36
|
-
SLASH_SUBAGENT_UPDATE_EVENT
|
|
47
|
+
SLASH_SUBAGENT_UPDATE_EVENT,
|
|
48
|
+
DIRS
|
|
37
49
|
} from "../shared/types.js";
|
|
38
50
|
const parseInlineConfig = (raw) => {
|
|
39
51
|
const config = {};
|
|
@@ -42,11 +54,8 @@ const parseInlineConfig = (raw) => {
|
|
|
42
54
|
if (!trimmed)
|
|
43
55
|
continue;
|
|
44
56
|
const eq = trimmed.indexOf("=");
|
|
45
|
-
if (eq === -1)
|
|
46
|
-
if (trimmed === "progress")
|
|
47
|
-
config.progress = true;
|
|
57
|
+
if (eq === -1)
|
|
48
58
|
continue;
|
|
49
|
-
}
|
|
50
59
|
const key = trimmed.slice(0, eq).trim();
|
|
51
60
|
const val = trimmed.slice(eq + 1).trim();
|
|
52
61
|
switch (key) {
|
|
@@ -67,33 +76,6 @@ const parseInlineConfig = (raw) => {
|
|
|
67
76
|
case "skills":
|
|
68
77
|
config.skill = val === "false" ? false : val.split("+").filter(Boolean);
|
|
69
78
|
break;
|
|
70
|
-
case "progress":
|
|
71
|
-
config.progress = val !== "false";
|
|
72
|
-
break;
|
|
73
|
-
case "as":
|
|
74
|
-
config.as = val || undefined;
|
|
75
|
-
break;
|
|
76
|
-
case "label":
|
|
77
|
-
config.label = val || undefined;
|
|
78
|
-
break;
|
|
79
|
-
case "phase":
|
|
80
|
-
config.phase = val || undefined;
|
|
81
|
-
break;
|
|
82
|
-
case "cwd":
|
|
83
|
-
config.cwd = val || undefined;
|
|
84
|
-
break;
|
|
85
|
-
case "count": {
|
|
86
|
-
const n = Number(val);
|
|
87
|
-
if (Number.isInteger(n) && n > 0)
|
|
88
|
-
config.count = n;
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
case "outputSchema":
|
|
92
|
-
config.outputSchema = val || undefined;
|
|
93
|
-
break;
|
|
94
|
-
case "acceptance":
|
|
95
|
-
config.acceptance = val || undefined;
|
|
96
|
-
break;
|
|
97
79
|
}
|
|
98
80
|
}
|
|
99
81
|
return config;
|
|
@@ -124,73 +106,27 @@ const extractExecutionFlags = (rawArgs) => {
|
|
|
124
106
|
}
|
|
125
107
|
return { args, bg, fork };
|
|
126
108
|
};
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (!multiAgent) {
|
|
132
|
-
if (prefix.includes(" "))
|
|
133
|
-
return null;
|
|
134
|
-
return agents.filter((a) => a.name.startsWith(prefix)).map((a) => ({ value: a.name, label: a.name }));
|
|
135
|
-
}
|
|
136
|
-
let inSingle = false, inDouble = false, depth = 0, segStart = 0;
|
|
137
|
-
for (let i = 0;i < prefix.length; i++) {
|
|
138
|
-
const ch = prefix[i];
|
|
139
|
-
if (inSingle) {
|
|
140
|
-
if (ch === "'")
|
|
141
|
-
inSingle = false;
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
if (inDouble) {
|
|
145
|
-
if (ch === '"')
|
|
146
|
-
inDouble = false;
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
if (ch === "'") {
|
|
150
|
-
inSingle = true;
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
if (ch === '"') {
|
|
154
|
-
inDouble = true;
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
if (ch === "(") {
|
|
158
|
-
if (!prefix.slice(segStart, i).includes(" -- ")) {
|
|
159
|
-
depth++;
|
|
160
|
-
segStart = i + 1;
|
|
161
|
-
}
|
|
162
|
-
} else if (ch === ")") {
|
|
163
|
-
if (depth > 0) {
|
|
164
|
-
depth--;
|
|
165
|
-
segStart = i + 1;
|
|
166
|
-
}
|
|
167
|
-
} else if (ch === "|" && depth > 0)
|
|
168
|
-
segStart = i + 1;
|
|
169
|
-
else if (ch === ">" && prefix[i - 1] === "-" && depth === 0)
|
|
170
|
-
segStart = i + 1;
|
|
171
|
-
}
|
|
172
|
-
if (inSingle || inDouble)
|
|
173
|
-
return null;
|
|
174
|
-
const segment = prefix.slice(segStart);
|
|
175
|
-
if (segment.includes(" -- ") || segment.includes('"') || segment.includes("'"))
|
|
176
|
-
return null;
|
|
177
|
-
const lastWord = (segment.match(/(\S*)$/) || ["", ""])[1];
|
|
178
|
-
let beforeLastWord = prefix.slice(0, prefix.length - lastWord.length);
|
|
179
|
-
if (lastWord === "" && /[>|]$/.test(beforeLastWord))
|
|
180
|
-
beforeLastWord = `${beforeLastWord} `;
|
|
181
|
-
return agents.filter((a) => a.name.startsWith(lastWord)).map((a) => ({ value: `${beforeLastWord}${a.name}`, label: a.name }));
|
|
182
|
-
};
|
|
183
|
-
const discoverSavedChains = (cwd) => {
|
|
184
|
-
const chainsByName = new Map;
|
|
185
|
-
for (const chain of discoverAgentsAll(cwd).chains) {
|
|
186
|
-
chainsByName.set(chain.name, chain);
|
|
109
|
+
function discoverSlashAgents(dm, cwd, scope) {
|
|
110
|
+
if (listRuntimeAgentConfigs(dm).length === 0) {
|
|
111
|
+
const discovered = discoverAgents(cwd, scope);
|
|
112
|
+
return { ...discovered, unknownAgentDiagnosticContext: unknownAgentDiagnosticContext(discovered) };
|
|
187
113
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
114
|
+
const snapshot = discoverAgentSnapshot(cwd, scope, undefined, { includeChains: false });
|
|
115
|
+
const discovered = snapshot.effective;
|
|
116
|
+
const all = snapshot.all;
|
|
117
|
+
const configuredAgents = [
|
|
118
|
+
...all.builtin,
|
|
119
|
+
...all.package,
|
|
120
|
+
...scope !== "project" ? all.user : [],
|
|
121
|
+
...scope !== "user" ? all.project : []
|
|
122
|
+
];
|
|
123
|
+
const merged = mergeRuntimeAgents(dm, discovered, configuredAgents);
|
|
124
|
+
return { ...merged, unknownAgentDiagnosticContext: { ...unknownAgentDiagnosticContext(discovered), agents: merged.agents } };
|
|
125
|
+
}
|
|
126
|
+
const makeAgentCompletions = (dm, state) => (prefix) => {
|
|
127
|
+
if (!state.baseCwd || prefix.includes(" "))
|
|
192
128
|
return null;
|
|
193
|
-
return
|
|
129
|
+
return discoverSlashAgents(dm, state.baseCwd, "both").agents.filter((agent) => agent.name.startsWith(prefix)).map((agent) => ({ value: agent.name, label: agent.name }));
|
|
194
130
|
};
|
|
195
131
|
const makeBuiltinAgentNameCompletions = () => (prefix) => {
|
|
196
132
|
if (prefix.includes(" "))
|
|
@@ -206,8 +142,8 @@ const makeProviderCompletions = (state) => (prefix) => {
|
|
|
206
142
|
const providers = [...new Set(available.map((model) => typeof model?.provider === "string" ? model.provider : "").filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
207
143
|
return providers.filter((provider) => provider.startsWith(prefix)).map((provider) => ({ value: provider, label: provider }));
|
|
208
144
|
};
|
|
209
|
-
function sendSlashText(
|
|
210
|
-
|
|
145
|
+
function sendSlashText(dm, text) {
|
|
146
|
+
dm.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: true });
|
|
211
147
|
}
|
|
212
148
|
async function withSlashStatus(ctx, text, run) {
|
|
213
149
|
if (ctx.hasUI)
|
|
@@ -219,6 +155,149 @@ async function withSlashStatus(ctx, text, run) {
|
|
|
219
155
|
ctx.ui.setStatus("subagent-slash-text", undefined);
|
|
220
156
|
}
|
|
221
157
|
}
|
|
158
|
+
function commandForTarget(target) {
|
|
159
|
+
return target.kind === "scheduled" ? `subagent({ action: "schedule.pause", id: ${JSON.stringify(target.id)} })` : `subagent({ action: "stop", id: ${JSON.stringify(target.id)} })`;
|
|
160
|
+
}
|
|
161
|
+
function formatAsyncStopTarget(run) {
|
|
162
|
+
const progress = formatAsyncRunProgressLabel(run);
|
|
163
|
+
const cwd = run.cwd ? shortenPath(run.cwd) : shortenPath(run.asyncDir);
|
|
164
|
+
return {
|
|
165
|
+
kind: "async",
|
|
166
|
+
id: run.id,
|
|
167
|
+
label: `${run.id} · ${run.mode} · ${progress}`,
|
|
168
|
+
detail: `${run.state} · ${cwd}`,
|
|
169
|
+
actionLabel: "stop async run"
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function scheduledStopTargets(ctx, _state) {
|
|
173
|
+
try {
|
|
174
|
+
return listScheduledRunSummaries(ctx.cwd).filter((schedule) => !schedule.paused && !schedule.activeRunId && schedule.trigger.nextRunAt).sort((left, right) => left.trigger.nextRunAt.localeCompare(right.trigger.nextRunAt)).map((schedule) => ({
|
|
175
|
+
kind: "scheduled",
|
|
176
|
+
id: schedule.id,
|
|
177
|
+
label: `${schedule.id} · ${schedule.name}`,
|
|
178
|
+
detail: `scheduled · ${schedule.trigger.nextRunAt}`,
|
|
179
|
+
actionLabel: "pause schedule"
|
|
180
|
+
}));
|
|
181
|
+
} catch {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function discoverStopTargets(ctx, state) {
|
|
186
|
+
const sessionId = state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined;
|
|
187
|
+
const asyncTargets = listAsyncRuns(DIRS.async, {
|
|
188
|
+
states: ["queued", "running"],
|
|
189
|
+
...sessionId ? { sessionId } : {}
|
|
190
|
+
}).map(formatAsyncStopTarget);
|
|
191
|
+
return [...asyncTargets, ...scheduledStopTargets(ctx, state)];
|
|
192
|
+
}
|
|
193
|
+
function stopFallbackText(targets) {
|
|
194
|
+
if (targets.length === 0)
|
|
195
|
+
return "No active current-session async runs or scheduled subagent runs to stop.";
|
|
196
|
+
const lines = ["Subagent stop targets:", ""];
|
|
197
|
+
for (const target of targets) {
|
|
198
|
+
lines.push(`- ${target.label}`);
|
|
199
|
+
lines.push(` ${target.detail}`);
|
|
200
|
+
lines.push(` ${target.actionLabel}: ${commandForTarget(target)}`);
|
|
201
|
+
if (target.kind === "async")
|
|
202
|
+
lines.push(` slash: /subagents-stop ${target.id}`);
|
|
203
|
+
}
|
|
204
|
+
return lines.join(`
|
|
205
|
+
`);
|
|
206
|
+
}
|
|
207
|
+
function selectForegroundDetachControl(state, requested) {
|
|
208
|
+
const controls = [...state.foregroundControls.values()];
|
|
209
|
+
if (requested) {
|
|
210
|
+
const matches = controls.filter((control) => control.runId === requested || control.runId.startsWith(requested));
|
|
211
|
+
if (matches.length > 1)
|
|
212
|
+
throw new Error(`Ambiguous foreground run id prefix '${requested}' matched: ${matches.map((control) => control.runId).join(", ")}. Provide a longer id.`);
|
|
213
|
+
return matches[0];
|
|
214
|
+
}
|
|
215
|
+
const singleControls = controls.filter((control) => control.mode === "single");
|
|
216
|
+
if (state.lastForegroundControlId) {
|
|
217
|
+
const latest = state.foregroundControls.get(state.lastForegroundControlId);
|
|
218
|
+
if (latest?.mode === "single")
|
|
219
|
+
return latest;
|
|
220
|
+
}
|
|
221
|
+
return singleControls.sort((left, right) => right.updatedAt - left.updatedAt)[0];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
class SubagentsStopSelector {
|
|
225
|
+
width = 84;
|
|
226
|
+
selected = 0;
|
|
227
|
+
confirming = false;
|
|
228
|
+
tui;
|
|
229
|
+
theme;
|
|
230
|
+
targets;
|
|
231
|
+
done;
|
|
232
|
+
constructor(tui, theme, targets, done) {
|
|
233
|
+
this.tui = tui;
|
|
234
|
+
this.theme = theme;
|
|
235
|
+
this.targets = targets;
|
|
236
|
+
this.done = done;
|
|
237
|
+
}
|
|
238
|
+
handleInput(data) {
|
|
239
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
240
|
+
this.done({ confirmed: false });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (this.confirming) {
|
|
244
|
+
if (matchesKey(data, "return") || data.toLowerCase() === "y") {
|
|
245
|
+
this.done({ confirmed: true, target: this.targets[this.selected] });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (data.toLowerCase() === "n" || matchesKey(data, "backspace")) {
|
|
249
|
+
this.confirming = false;
|
|
250
|
+
this.tui.requestRender();
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (matchesKey(data, "up") || matchesKey(data, "k")) {
|
|
255
|
+
this.selected = Math.max(0, this.selected - 1);
|
|
256
|
+
this.tui.requestRender();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (matchesKey(data, "down") || matchesKey(data, "j")) {
|
|
260
|
+
this.selected = Math.min(this.targets.length - 1, this.selected + 1);
|
|
261
|
+
this.tui.requestRender();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (matchesKey(data, "return")) {
|
|
265
|
+
this.confirming = true;
|
|
266
|
+
this.tui.requestRender();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
invalidate() {}
|
|
270
|
+
render(width) {
|
|
271
|
+
const contentWidth = Math.max(0, Math.min(this.width, Math.floor(width)));
|
|
272
|
+
const lines = [this.theme.bold("Stop subagent run"), this.theme.fg("dim", "Select a current-session async run to stop, or a scheduled run to cancel."), ""];
|
|
273
|
+
const maxRows = 10;
|
|
274
|
+
const start = Math.max(0, Math.min(this.selected - maxRows + 1, Math.max(0, this.targets.length - maxRows)));
|
|
275
|
+
for (let index = start;index < Math.min(this.targets.length, start + maxRows); index++) {
|
|
276
|
+
const target = this.targets[index];
|
|
277
|
+
const selected = index === this.selected;
|
|
278
|
+
const marker = selected ? "›" : " ";
|
|
279
|
+
const actionLabel = target.actionLabel;
|
|
280
|
+
const action = target.kind === "scheduled" ? this.theme.fg("warning", actionLabel) : this.theme.fg("accent", actionLabel);
|
|
281
|
+
const labelWidth = Math.max(0, contentWidth - marker.length - actionLabel.length - 2);
|
|
282
|
+
lines.push(`${marker} ${action} ${target.label.slice(0, labelWidth)}`);
|
|
283
|
+
if (selected)
|
|
284
|
+
lines.push(this.theme.fg("dim", ` ${target.detail}`.slice(0, contentWidth)));
|
|
285
|
+
}
|
|
286
|
+
if (this.targets.length > maxRows)
|
|
287
|
+
lines.push(this.theme.fg("dim", `Showing ${start + 1}-${Math.min(this.targets.length, start + maxRows)} of ${this.targets.length}`));
|
|
288
|
+
lines.push("");
|
|
289
|
+
if (this.confirming) {
|
|
290
|
+
const target = this.targets[this.selected];
|
|
291
|
+
lines.push(this.theme.fg("warning", `Confirm: ${target.actionLabel} ${target.id}?`));
|
|
292
|
+
if (target.kind === "async")
|
|
293
|
+
lines.push(this.theme.fg("dim", "Stop ends this run; use interrupt for a resumable pause."));
|
|
294
|
+
lines.push(this.theme.fg("dim", "Enter/Y confirms · N returns · Esc cancels"));
|
|
295
|
+
} else {
|
|
296
|
+
lines.push(this.theme.fg("dim", "↑↓/jk select · Enter confirm · Esc cancel"));
|
|
297
|
+
}
|
|
298
|
+
return lines.map((line) => truncateToWidth(line, contentWidth));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
222
301
|
function emptyUsage() {
|
|
223
302
|
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
224
303
|
}
|
|
@@ -233,21 +312,59 @@ function addUsage(target, source) {
|
|
|
233
312
|
function usageHasValue(usage) {
|
|
234
313
|
return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0;
|
|
235
314
|
}
|
|
315
|
+
function nonNegativeNumber(value) {
|
|
316
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
317
|
+
}
|
|
318
|
+
function usageFromValue(value, turnsOverride) {
|
|
319
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
320
|
+
return;
|
|
321
|
+
const record = value;
|
|
322
|
+
const costValue = typeof record.cost === "object" && record.cost !== null && !Array.isArray(record.cost) ? record.cost.total : record.cost;
|
|
323
|
+
const turnsValue = turnsOverride ?? record.turns;
|
|
324
|
+
const usage = {
|
|
325
|
+
input: nonNegativeNumber(record.input) ?? 0,
|
|
326
|
+
output: nonNegativeNumber(record.output) ?? 0,
|
|
327
|
+
cacheRead: nonNegativeNumber(record.cacheRead) ?? 0,
|
|
328
|
+
cacheWrite: nonNegativeNumber(record.cacheWrite) ?? 0,
|
|
329
|
+
cost: nonNegativeNumber(costValue) ?? 0,
|
|
330
|
+
turns: nonNegativeNumber(turnsValue) ?? 0
|
|
331
|
+
};
|
|
332
|
+
return Number.isSafeInteger(usage.turns) ? usage : undefined;
|
|
333
|
+
}
|
|
236
334
|
function assistantUsageFromMessage(message) {
|
|
237
335
|
if (!message || typeof message !== "object")
|
|
238
336
|
return;
|
|
239
337
|
const msg = message;
|
|
240
|
-
|
|
338
|
+
return msg.role === "assistant" ? usageFromValue(msg.usage, 1) : undefined;
|
|
339
|
+
}
|
|
340
|
+
function compactionUsageFromEntry(entry) {
|
|
341
|
+
if (!entry || typeof entry !== "object")
|
|
241
342
|
return;
|
|
242
|
-
const
|
|
243
|
-
return
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
343
|
+
const record = entry;
|
|
344
|
+
return record.type === "compaction" ? usageFromValue(record.usage, 0) : undefined;
|
|
345
|
+
}
|
|
346
|
+
const MAX_USAGE_METADATA_BYTES = 2 * 1024 * 1024;
|
|
347
|
+
function readUsageMetadata(metadataPath) {
|
|
348
|
+
let fd;
|
|
349
|
+
try {
|
|
350
|
+
fd = fs.openSync(metadataPath, "r");
|
|
351
|
+
const stat = fs.fstatSync(fd);
|
|
352
|
+
if (!stat.isFile() || stat.size > MAX_USAGE_METADATA_BYTES)
|
|
353
|
+
return;
|
|
354
|
+
const buffer = Buffer.allocUnsafe(stat.size);
|
|
355
|
+
let offset = 0;
|
|
356
|
+
while (offset < buffer.length) {
|
|
357
|
+
const bytesRead = fs.readSync(fd, buffer, offset, buffer.length - offset, null);
|
|
358
|
+
if (bytesRead <= 0)
|
|
359
|
+
return;
|
|
360
|
+
offset += bytesRead;
|
|
361
|
+
}
|
|
362
|
+
const value = JSON.parse(buffer.toString("utf-8"));
|
|
363
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
364
|
+
} finally {
|
|
365
|
+
if (fd !== undefined)
|
|
366
|
+
fs.closeSync(fd);
|
|
367
|
+
}
|
|
251
368
|
}
|
|
252
369
|
function isSubagentDetails(value) {
|
|
253
370
|
if (!value || typeof value !== "object")
|
|
@@ -266,10 +383,33 @@ function detailsFromSessionEntry(entry) {
|
|
|
266
383
|
if (record.type !== "message" || !record.message || typeof record.message !== "object")
|
|
267
384
|
return;
|
|
268
385
|
const message = record.message;
|
|
269
|
-
if (message.role !== "toolResult" || message.toolName !== "subagent")
|
|
386
|
+
if (message.role !== "toolResult" || message.toolName !== "subagent" && message.toolName !== "bg_wait" && message.toolName !== "subagent_wait")
|
|
270
387
|
return;
|
|
271
388
|
return isSubagentDetails(message.details) ? message.details : undefined;
|
|
272
389
|
}
|
|
390
|
+
function metadataUsage(artifactsDirs, input) {
|
|
391
|
+
if (!/^[A-Za-z0-9._-]+$/.test(input.runId))
|
|
392
|
+
return;
|
|
393
|
+
for (const artifactsDir of artifactsDirs) {
|
|
394
|
+
for (const index of [0, undefined]) {
|
|
395
|
+
const metadataPath = getArtifactPaths(artifactsDir, input.runId, input.agent, index).metadataPath;
|
|
396
|
+
try {
|
|
397
|
+
const metadata = readUsageMetadata(metadataPath);
|
|
398
|
+
if (!metadata)
|
|
399
|
+
continue;
|
|
400
|
+
if (metadata.runId !== input.runId || metadata.agent !== input.agent)
|
|
401
|
+
continue;
|
|
402
|
+
const usage = usageFromValue(metadata.usage);
|
|
403
|
+
if (usage && usageHasValue(usage))
|
|
404
|
+
return usage;
|
|
405
|
+
} catch (error) {
|
|
406
|
+
if (error.code !== "ENOENT")
|
|
407
|
+
console.error(`Failed to read subagent usage metadata '${metadataPath}':`, error);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
273
413
|
function formatCostUsage(label, usage) {
|
|
274
414
|
const extras = [
|
|
275
415
|
usage.cacheRead ? `cache read ${formatTokens(usage.cacheRead)}` : "",
|
|
@@ -278,29 +418,98 @@ function formatCostUsage(label, usage) {
|
|
|
278
418
|
].filter(Boolean);
|
|
279
419
|
return `${label}: ↑${formatTokens(usage.input)} ↓${formatTokens(usage.output)} $${usage.cost.toFixed(4)}${extras.length ? ` (${extras.join(", ")})` : ""}`;
|
|
280
420
|
}
|
|
281
|
-
function buildSubagentCostReport(ctx) {
|
|
421
|
+
function buildSubagentCostReport(ctx, state) {
|
|
282
422
|
const parent = emptyUsage();
|
|
283
423
|
const childTotal = emptyUsage();
|
|
284
424
|
const total = emptyUsage();
|
|
285
425
|
const children = [];
|
|
426
|
+
const seenChildren = new Set;
|
|
427
|
+
const workflowRunIds = new Set;
|
|
428
|
+
let unresolvedAsyncChildren = 0;
|
|
429
|
+
const addChild = (input) => {
|
|
430
|
+
if (!input.usage || !usageHasValue(input.usage))
|
|
431
|
+
return false;
|
|
432
|
+
const identity = input.runId ? `run:${input.runId}` : input.sessionFile ? `session:${input.sessionFile}` : undefined;
|
|
433
|
+
if (identity && seenChildren.has(identity))
|
|
434
|
+
return true;
|
|
435
|
+
if (identity)
|
|
436
|
+
seenChildren.add(identity);
|
|
437
|
+
const usage = { ...input.usage };
|
|
438
|
+
children.push({
|
|
439
|
+
label: `Child ${children.length + 1} (${input.agent ?? "unknown"})`,
|
|
440
|
+
usage,
|
|
441
|
+
...input.sessionFile ? { sessionFile: input.sessionFile } : {}
|
|
442
|
+
});
|
|
443
|
+
addUsage(childTotal, usage);
|
|
444
|
+
return true;
|
|
445
|
+
};
|
|
286
446
|
for (const entry of ctx.sessionManager.getBranch()) {
|
|
287
447
|
const message = entry.type === "message" ? entry.message : undefined;
|
|
288
|
-
const parentUsage = assistantUsageFromMessage(message);
|
|
448
|
+
const parentUsage = assistantUsageFromMessage(message) ?? compactionUsageFromEntry(entry);
|
|
289
449
|
if (parentUsage)
|
|
290
450
|
addUsage(parent, parentUsage);
|
|
291
451
|
const details = detailsFromSessionEntry(entry);
|
|
292
452
|
if (!details)
|
|
293
453
|
continue;
|
|
454
|
+
if (details.mode === "workflow" && details.runId)
|
|
455
|
+
workflowRunIds.add(details.runId);
|
|
294
456
|
for (const result of details.results) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
457
|
+
const resultRunId = result.runId;
|
|
458
|
+
addChild({ agent: result.agent, runId: typeof resultRunId === "string" ? resultRunId : undefined, usage: usageFromValue(result.usage), sessionFile: result.sessionFile });
|
|
459
|
+
}
|
|
460
|
+
for (const completion of details.completions ?? []) {
|
|
461
|
+
if (completion.mode === "workflow")
|
|
462
|
+
workflowRunIds.add(completion.runId);
|
|
463
|
+
for (const result of completion.results ?? []) {
|
|
464
|
+
addChild({ agent: result.agent, runId: result.runId, usage: usageFromValue(result.usage), sessionFile: result.sessionFile });
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
let sessionFile = null;
|
|
469
|
+
try {
|
|
470
|
+
sessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
471
|
+
} catch {}
|
|
472
|
+
const artifactsDirs = new Set;
|
|
473
|
+
const addArtifactsDir = (cwd) => {
|
|
474
|
+
try {
|
|
475
|
+
artifactsDirs.add(getArtifactsDir(sessionFile, cwd, state.artifactDirPreference));
|
|
476
|
+
} catch {}
|
|
477
|
+
};
|
|
478
|
+
addArtifactsDir(ctx.cwd);
|
|
479
|
+
addArtifactsDir(state.baseCwd);
|
|
480
|
+
for (const workflowRunId of workflowRunIds) {
|
|
481
|
+
try {
|
|
482
|
+
const status = readStatus(path.join(DIRS.async, workflowRunId));
|
|
483
|
+
addArtifactsDir(status?.cwd);
|
|
484
|
+
const receipt = readWorkflowReceipt(DIRS.async, workflowRunId);
|
|
485
|
+
const summaryChildren = new Map((receipt.workflowChildren?.children ?? []).map((child) => [child.childId, child]));
|
|
486
|
+
const refsByRunId = new Map;
|
|
487
|
+
const addRef = (runId, agent) => {
|
|
488
|
+
if (!runId || !agent || refsByRunId.has(runId))
|
|
489
|
+
return;
|
|
490
|
+
refsByRunId.set(runId, { runId, agent });
|
|
491
|
+
};
|
|
492
|
+
for (const [key, entry] of Object.entries(receipt.entries)) {
|
|
493
|
+
const summaryChild = summaryChildren.get(key);
|
|
494
|
+
const runIds = entry.continuation?.runIds?.length ? entry.continuation.runIds : entry.latestRunId ? [entry.latestRunId] : summaryChild?.runId ? [summaryChild.runId] : [];
|
|
495
|
+
for (const runId of runIds)
|
|
496
|
+
addRef(runId, entry.agent ?? summaryChild?.agent);
|
|
497
|
+
}
|
|
498
|
+
for (const child of receipt.workflowChildren?.children ?? []) {
|
|
499
|
+
if (!Object.hasOwn(receipt.entries, child.childId))
|
|
500
|
+
addRef(child.runId, child.agent);
|
|
501
|
+
}
|
|
502
|
+
const refs = [...refsByRunId.values()];
|
|
503
|
+
for (const ref of refs) {
|
|
504
|
+
if (seenChildren.has(`run:${ref.runId}`))
|
|
505
|
+
continue;
|
|
506
|
+
const usage = metadataUsage([...artifactsDirs], ref);
|
|
507
|
+
if (!usage || !addChild({ ...ref, usage }))
|
|
508
|
+
unresolvedAsyncChildren += 1;
|
|
509
|
+
}
|
|
510
|
+
} catch (error) {
|
|
511
|
+
if (error.code !== "ENOENT")
|
|
512
|
+
console.error(`Failed to resolve async subagent usage for '${workflowRunId}':`, error);
|
|
304
513
|
}
|
|
305
514
|
}
|
|
306
515
|
addUsage(total, parent);
|
|
@@ -319,6 +528,8 @@ function buildSubagentCostReport(ctx) {
|
|
|
319
528
|
lines.push(` Session: ${child.sessionFile}`);
|
|
320
529
|
}
|
|
321
530
|
}
|
|
531
|
+
if (unresolvedAsyncChildren > 0)
|
|
532
|
+
lines.push(`Async child usage unavailable: ${unresolvedAsyncChildren}.`);
|
|
322
533
|
lines.push("────────────────────────────", formatCostUsage("Children", childTotal), formatCostUsage("Total", total));
|
|
323
534
|
return lines.join(`
|
|
324
535
|
`);
|
|
@@ -333,119 +544,121 @@ function getProfileWorkerModel(profile) {
|
|
|
333
544
|
const model = profile.subagents?.agentOverrides?.worker?.model;
|
|
334
545
|
return typeof model === "string" && model.trim() ? model.trim() : undefined;
|
|
335
546
|
}
|
|
336
|
-
function
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
547
|
+
function isStaleExtensionContextError(error) {
|
|
548
|
+
return error instanceof Error && (error.message.includes("This extension ctx is stale") || error.message.includes("Extension context no longer active"));
|
|
549
|
+
}
|
|
550
|
+
function slashHasUI(ctx) {
|
|
551
|
+
try {
|
|
552
|
+
return ctx.hasUI;
|
|
553
|
+
} catch (error) {
|
|
554
|
+
if (isStaleExtensionContextError(error))
|
|
555
|
+
return;
|
|
556
|
+
throw error;
|
|
344
557
|
}
|
|
345
|
-
assertJsonSchemaObject(outputSchema, `outputSchema for chain '${chain.name}' step '${stepAgent}'`);
|
|
346
|
-
return outputSchema;
|
|
347
558
|
}
|
|
348
|
-
|
|
349
|
-
return chain.steps.map((step) => {
|
|
350
|
-
if (isParallelStep(step)) {
|
|
351
|
-
const parallel = step.parallel.map((task) => {
|
|
352
|
-
const { outputSchema: rawOutputSchema, ...rest } = task;
|
|
353
|
-
const outputSchema = loadSavedOutputSchema(chain, task.agent, rawOutputSchema);
|
|
354
|
-
return { ...rest, ...outputSchema ? { outputSchema } : {} };
|
|
355
|
-
});
|
|
356
|
-
return { ...step, parallel, ...worktree ? { worktree: true } : {} };
|
|
357
|
-
}
|
|
358
|
-
if (isDynamicParallelStep(step)) {
|
|
359
|
-
const { outputSchema: rawOutputSchema, ...parallelRest } = step.parallel;
|
|
360
|
-
const outputSchema = loadSavedOutputSchema(chain, step.parallel.agent, rawOutputSchema);
|
|
361
|
-
const collectSchema = loadSavedOutputSchema(chain, `${step.collect.as} collection`, step.collect.outputSchema);
|
|
362
|
-
return {
|
|
363
|
-
...step,
|
|
364
|
-
parallel: { ...parallelRest, ...outputSchema ? { outputSchema } : {} },
|
|
365
|
-
collect: { ...step.collect, ...collectSchema ? { outputSchema: collectSchema } : {} }
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
const outputSchema = loadSavedOutputSchema(chain, step.agent, step.outputSchema);
|
|
369
|
-
return {
|
|
370
|
-
agent: step.agent,
|
|
371
|
-
task: step.task || undefined,
|
|
372
|
-
...step.phase ? { phase: step.phase } : {},
|
|
373
|
-
...step.label ? { label: step.label } : {},
|
|
374
|
-
...step.as ? { as: step.as } : {},
|
|
375
|
-
...outputSchema ? { outputSchema } : {},
|
|
376
|
-
...step.acceptance !== undefined ? { acceptance: step.acceptance } : {},
|
|
377
|
-
output: step.output,
|
|
378
|
-
outputMode: step.outputMode,
|
|
379
|
-
reads: step.reads,
|
|
380
|
-
progress: step.progress,
|
|
381
|
-
skill: step.skill ?? step.skills,
|
|
382
|
-
model: step.model
|
|
383
|
-
};
|
|
384
|
-
});
|
|
385
|
-
};
|
|
386
|
-
async function requestSlashRun(pi, ctx, requestId, params) {
|
|
559
|
+
async function requestSlashRun(dm, ctx, requestId, params, signal) {
|
|
387
560
|
return new Promise((resolve, reject) => {
|
|
388
561
|
let done = false;
|
|
389
562
|
let started = false;
|
|
390
|
-
|
|
391
|
-
const startTimeout = setTimeout(() => {
|
|
392
|
-
finish(() => reject(new Error("Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.")));
|
|
393
|
-
}, startTimeoutMs);
|
|
563
|
+
let startTimeout;
|
|
394
564
|
const onStarted = (data) => {
|
|
395
|
-
if (done || !data || typeof data !== "object")
|
|
565
|
+
if (done || signal?.aborted || !data || typeof data !== "object")
|
|
396
566
|
return;
|
|
397
567
|
if (data.requestId !== requestId)
|
|
398
568
|
return;
|
|
399
569
|
started = true;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
570
|
+
if (startTimeout)
|
|
571
|
+
clearTimeout(startTimeout);
|
|
572
|
+
try {
|
|
573
|
+
if (ctx.hasUI)
|
|
574
|
+
ctx.ui.setStatus("subagent-slash", "running...");
|
|
575
|
+
} catch (error) {
|
|
576
|
+
if (isStaleExtensionContextError(error)) {
|
|
577
|
+
finish(() => reject(error));
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
throw error;
|
|
581
|
+
}
|
|
403
582
|
};
|
|
404
583
|
const onResponse = (data) => {
|
|
405
|
-
if (done || !data || typeof data !== "object")
|
|
584
|
+
if (done || signal?.aborted || !data || typeof data !== "object")
|
|
406
585
|
return;
|
|
407
586
|
const response = data;
|
|
408
587
|
if (response.requestId !== requestId)
|
|
409
588
|
return;
|
|
410
|
-
|
|
589
|
+
if (startTimeout)
|
|
590
|
+
clearTimeout(startTimeout);
|
|
411
591
|
finish(() => resolve(response));
|
|
412
592
|
};
|
|
413
593
|
const onUpdate = (data) => {
|
|
414
|
-
if (done || !data || typeof data !== "object")
|
|
594
|
+
if (done || signal?.aborted || !data || typeof data !== "object")
|
|
415
595
|
return;
|
|
416
596
|
const update = data;
|
|
417
597
|
if (update.requestId !== requestId)
|
|
418
598
|
return;
|
|
419
599
|
applySlashUpdate(requestId, update);
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
600
|
+
try {
|
|
601
|
+
if (!ctx.hasUI)
|
|
602
|
+
return;
|
|
603
|
+
const tool = update.currentTool ? ` ${update.currentTool}` : "";
|
|
604
|
+
const count = update.toolCount ?? 0;
|
|
605
|
+
const liveDetailKey = keyText("app.tools.expand");
|
|
606
|
+
ctx.ui.setStatus("subagent-slash", `${count} tools${tool} | ${liveDetailKey} live detail`);
|
|
607
|
+
} catch (error) {
|
|
608
|
+
if (isStaleExtensionContextError(error)) {
|
|
609
|
+
finish(() => reject(error));
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
throw error;
|
|
613
|
+
}
|
|
426
614
|
};
|
|
427
615
|
const onTerminalInput = ctx.hasUI ? ctx.ui.onTerminalInput((input) => {
|
|
428
616
|
if (!matchesKey(input, Key.escape))
|
|
429
617
|
return;
|
|
430
|
-
|
|
618
|
+
dm.events.emit(SLASH_SUBAGENT_CANCEL_EVENT, { requestId });
|
|
431
619
|
finish(() => reject(new Error("Cancelled")));
|
|
432
620
|
return { consume: true };
|
|
433
621
|
}) : undefined;
|
|
434
|
-
const unsubStarted =
|
|
435
|
-
const unsubResponse =
|
|
436
|
-
const unsubUpdate =
|
|
622
|
+
const unsubStarted = dm.events.on(SLASH_SUBAGENT_STARTED_EVENT, onStarted);
|
|
623
|
+
const unsubResponse = dm.events.on(SLASH_SUBAGENT_RESPONSE_EVENT, onResponse);
|
|
624
|
+
const unsubUpdate = dm.events.on(SLASH_SUBAGENT_UPDATE_EVENT, onUpdate);
|
|
625
|
+
let onAbort;
|
|
437
626
|
const finish = (next) => {
|
|
438
627
|
if (done)
|
|
439
628
|
return;
|
|
440
629
|
done = true;
|
|
441
|
-
|
|
630
|
+
if (startTimeout)
|
|
631
|
+
clearTimeout(startTimeout);
|
|
442
632
|
unsubStarted();
|
|
443
633
|
unsubResponse();
|
|
444
634
|
unsubUpdate();
|
|
445
|
-
|
|
635
|
+
try {
|
|
636
|
+
onTerminalInput?.();
|
|
637
|
+
} catch (error) {
|
|
638
|
+
if (!isStaleExtensionContextError(error))
|
|
639
|
+
throw error;
|
|
640
|
+
}
|
|
641
|
+
signal?.removeEventListener("abort", onAbort);
|
|
446
642
|
next();
|
|
447
643
|
};
|
|
448
|
-
|
|
644
|
+
onAbort = () => finish(() => reject(new Error("Slash subagent request canceled during session replacement or reload.")));
|
|
645
|
+
startTimeout = setTimeout(() => {
|
|
646
|
+
finish(() => reject(new Error("Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.")));
|
|
647
|
+
}, 15000);
|
|
648
|
+
if (signal?.aborted) {
|
|
649
|
+
onAbort();
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
653
|
+
try {
|
|
654
|
+
dm.events.emit(SLASH_SUBAGENT_REQUEST_EVENT, { requestId, params, ctx });
|
|
655
|
+
} catch (error) {
|
|
656
|
+
if (isStaleExtensionContextError(error)) {
|
|
657
|
+
finish(() => reject(error));
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
throw error;
|
|
661
|
+
}
|
|
449
662
|
if (!started && done)
|
|
450
663
|
return;
|
|
451
664
|
if (!started) {
|
|
@@ -500,466 +713,129 @@ function persistSlashSessionSnapshot(ctx) {
|
|
|
500
713
|
console.error("Failed to persist slash session snapshot for export:", error);
|
|
501
714
|
}
|
|
502
715
|
}
|
|
503
|
-
async function runSlashSubagent(
|
|
504
|
-
if (
|
|
505
|
-
|
|
716
|
+
async function runSlashSubagent(dm, ctx, params, signal) {
|
|
717
|
+
if (signal?.aborted)
|
|
718
|
+
return;
|
|
719
|
+
const initialHasUI = slashHasUI(ctx);
|
|
720
|
+
if (initialHasUI === undefined)
|
|
721
|
+
return;
|
|
722
|
+
try {
|
|
723
|
+
if (initialHasUI)
|
|
724
|
+
ctx.ui.setToolsExpanded(false);
|
|
725
|
+
} catch (error) {
|
|
726
|
+
if (isStaleExtensionContextError(error))
|
|
727
|
+
return;
|
|
728
|
+
throw error;
|
|
729
|
+
}
|
|
506
730
|
const requestId = randomUUID();
|
|
507
731
|
const initialDetails = buildSlashInitialResult(requestId, params);
|
|
508
732
|
const initialText = extractSlashMessageText(initialDetails.result.content) || "Running subagent...";
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
733
|
+
try {
|
|
734
|
+
dm.sendMessage({
|
|
735
|
+
customType: SLASH_RESULT_TYPE,
|
|
736
|
+
content: initialText,
|
|
737
|
+
display: true,
|
|
738
|
+
details: initialDetails
|
|
739
|
+
});
|
|
740
|
+
} catch (error) {
|
|
741
|
+
if (isStaleExtensionContextError(error))
|
|
742
|
+
return;
|
|
743
|
+
throw error;
|
|
744
|
+
}
|
|
515
745
|
persistSlashSessionSnapshot(ctx);
|
|
516
746
|
try {
|
|
517
|
-
const response = await requestSlashRun(
|
|
747
|
+
const response = await requestSlashRun(dm, ctx, requestId, params, signal);
|
|
748
|
+
if (signal?.aborted)
|
|
749
|
+
return;
|
|
750
|
+
const hasUI = slashHasUI(ctx);
|
|
751
|
+
if (hasUI === undefined)
|
|
752
|
+
return;
|
|
518
753
|
const finalDetails = finalizeSlashResult(response);
|
|
519
|
-
|
|
754
|
+
dm.sendMessage({
|
|
520
755
|
customType: SLASH_RESULT_TYPE,
|
|
521
756
|
content: buildSlashExportText(response),
|
|
522
|
-
display: !
|
|
757
|
+
display: !hasUI,
|
|
523
758
|
details: finalDetails
|
|
524
759
|
});
|
|
525
760
|
persistSlashSessionSnapshot(ctx);
|
|
526
|
-
if (
|
|
761
|
+
if (hasUI) {
|
|
527
762
|
ctx.ui.setStatus("subagent-slash", undefined);
|
|
528
763
|
}
|
|
529
|
-
if (response.isError &&
|
|
764
|
+
if (response.isError && hasUI) {
|
|
530
765
|
ctx.ui.notify(response.errorText || "Subagent failed", "error");
|
|
531
766
|
}
|
|
532
767
|
} catch (error) {
|
|
768
|
+
if (signal?.aborted || isStaleExtensionContextError(error))
|
|
769
|
+
return;
|
|
533
770
|
const message = error instanceof Error ? error.message : String(error);
|
|
771
|
+
const hasUI = slashHasUI(ctx);
|
|
772
|
+
if (hasUI === undefined)
|
|
773
|
+
return;
|
|
534
774
|
const failedDetails = failSlashResult(requestId, params, message);
|
|
535
|
-
|
|
775
|
+
dm.sendMessage({
|
|
536
776
|
customType: SLASH_RESULT_TYPE,
|
|
537
777
|
content: `## Subagent result
|
|
538
778
|
|
|
539
779
|
${message}`,
|
|
540
|
-
display: !
|
|
780
|
+
display: !hasUI,
|
|
541
781
|
details: failedDetails
|
|
542
782
|
});
|
|
543
783
|
persistSlashSessionSnapshot(ctx);
|
|
544
|
-
if (
|
|
784
|
+
if (hasUI) {
|
|
545
785
|
ctx.ui.setStatus("subagent-slash", undefined);
|
|
546
786
|
}
|
|
547
787
|
if (message === "Cancelled") {
|
|
548
|
-
if (
|
|
788
|
+
if (hasUI)
|
|
549
789
|
ctx.ui.notify("Cancelled", "warning");
|
|
550
790
|
return;
|
|
551
791
|
}
|
|
552
|
-
if (
|
|
792
|
+
if (hasUI)
|
|
553
793
|
ctx.ui.notify(message, "error");
|
|
554
794
|
}
|
|
555
795
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
export class SlashParseError extends Error {
|
|
559
|
-
}
|
|
560
|
-
function findUnmatchedCloseParen(input) {
|
|
561
|
-
let depth = 0, inSingle = false, inDouble = false;
|
|
562
|
-
for (let i = 0;i < input.length; i++) {
|
|
563
|
-
const ch = input[i];
|
|
564
|
-
if (inSingle) {
|
|
565
|
-
if (ch === "'")
|
|
566
|
-
inSingle = false;
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
|
-
if (inDouble) {
|
|
570
|
-
if (ch === '"')
|
|
571
|
-
inDouble = false;
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
if (ch === "'") {
|
|
575
|
-
inSingle = true;
|
|
576
|
-
continue;
|
|
577
|
-
}
|
|
578
|
-
if (ch === '"') {
|
|
579
|
-
inDouble = true;
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
if (ch === "(")
|
|
583
|
-
depth++;
|
|
584
|
-
else if (ch === ")") {
|
|
585
|
-
depth--;
|
|
586
|
-
if (depth < 0)
|
|
587
|
-
return true;
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
return depth !== 0;
|
|
591
|
-
}
|
|
592
|
-
function splitOnArrow(input) {
|
|
593
|
-
const segments = [];
|
|
594
|
-
let depth = 0, inSingle = false, inDouble = false, start = 0;
|
|
595
|
-
for (let i = 0;i < input.length; i++) {
|
|
596
|
-
const ch = input[i];
|
|
597
|
-
if (inSingle) {
|
|
598
|
-
if (ch === "'")
|
|
599
|
-
inSingle = false;
|
|
600
|
-
continue;
|
|
601
|
-
}
|
|
602
|
-
if (inDouble) {
|
|
603
|
-
if (ch === '"')
|
|
604
|
-
inDouble = false;
|
|
605
|
-
continue;
|
|
606
|
-
}
|
|
607
|
-
if (ch === "'") {
|
|
608
|
-
inSingle = true;
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
if (ch === '"') {
|
|
612
|
-
inDouble = true;
|
|
613
|
-
continue;
|
|
614
|
-
}
|
|
615
|
-
if (ch === "(")
|
|
616
|
-
depth++;
|
|
617
|
-
else if (ch === ")")
|
|
618
|
-
depth--;
|
|
619
|
-
else if (depth === 0 && ch === "-" && input[i + 1] === ">" && input[i + 2] === " ") {
|
|
620
|
-
segments.push(input.slice(start, i));
|
|
621
|
-
i += 2;
|
|
622
|
-
start = i + 1;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
segments.push(input.slice(start));
|
|
626
|
-
return segments;
|
|
627
|
-
}
|
|
628
|
-
function splitGroupTasks(inner) {
|
|
629
|
-
const parts = [];
|
|
630
|
-
let depth = 0, inSingle = false, inDouble = false, start = 0;
|
|
631
|
-
for (let i = 0;i < inner.length; i++) {
|
|
632
|
-
const ch = inner[i];
|
|
633
|
-
if (inSingle) {
|
|
634
|
-
if (ch === "'")
|
|
635
|
-
inSingle = false;
|
|
636
|
-
continue;
|
|
637
|
-
}
|
|
638
|
-
if (inDouble) {
|
|
639
|
-
if (ch === '"')
|
|
640
|
-
inDouble = false;
|
|
641
|
-
continue;
|
|
642
|
-
}
|
|
643
|
-
if (ch === "'") {
|
|
644
|
-
inSingle = true;
|
|
645
|
-
continue;
|
|
646
|
-
}
|
|
647
|
-
if (ch === '"') {
|
|
648
|
-
inDouble = true;
|
|
649
|
-
continue;
|
|
650
|
-
}
|
|
651
|
-
if (ch === "(")
|
|
652
|
-
depth++;
|
|
653
|
-
else if (ch === ")")
|
|
654
|
-
depth--;
|
|
655
|
-
else if (ch === "|" && depth === 0) {
|
|
656
|
-
parts.push(inner.slice(start, i));
|
|
657
|
-
start = i + 1;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
parts.push(inner.slice(start));
|
|
661
|
-
return parts;
|
|
662
|
-
}
|
|
663
|
-
export function parseSingleTaskToken(token) {
|
|
664
|
-
let agentPart;
|
|
665
|
-
let task;
|
|
666
|
-
const qMatch = token.match(/^(\S+(?:\[[^\]]*\])?)\s+(?:"([^"]*)"|'([^']*)')$/);
|
|
667
|
-
if (qMatch) {
|
|
668
|
-
agentPart = qMatch[1];
|
|
669
|
-
task = (qMatch[2] ?? qMatch[3]) || undefined;
|
|
670
|
-
} else {
|
|
671
|
-
const dashIdx = token.indexOf(" -- ");
|
|
672
|
-
if (dashIdx !== -1) {
|
|
673
|
-
agentPart = token.slice(0, dashIdx).trim();
|
|
674
|
-
task = token.slice(dashIdx + 4).trim() || undefined;
|
|
675
|
-
} else {
|
|
676
|
-
agentPart = token;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return { kind: "step", ...parseAgentToken(agentPart), task };
|
|
680
|
-
}
|
|
681
|
-
const parseGroupConfig = (raw) => {
|
|
682
|
-
const config = {};
|
|
683
|
-
for (const part of raw.split(",")) {
|
|
684
|
-
const trimmed = part.trim();
|
|
685
|
-
if (!trimmed)
|
|
686
|
-
continue;
|
|
687
|
-
const eq = trimmed.indexOf("=");
|
|
688
|
-
const key = eq === -1 ? trimmed : trimmed.slice(0, eq).trim();
|
|
689
|
-
const val = eq === -1 ? "" : trimmed.slice(eq + 1).trim();
|
|
690
|
-
switch (key) {
|
|
691
|
-
case "concurrency": {
|
|
692
|
-
const n = Number(val);
|
|
693
|
-
if (Number.isInteger(n) && n > 0)
|
|
694
|
-
config.concurrency = n;
|
|
695
|
-
break;
|
|
696
|
-
}
|
|
697
|
-
case "failFast":
|
|
698
|
-
config.failFast = eq === -1 ? true : val !== "false";
|
|
699
|
-
break;
|
|
700
|
-
case "worktree":
|
|
701
|
-
config.worktree = eq === -1 ? true : val !== "false";
|
|
702
|
-
break;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
return config;
|
|
706
|
-
};
|
|
707
|
-
const splitGroupBody = (trimmed) => {
|
|
708
|
-
let depth = 0, inSingle = false, inDouble = false, closeIdx = -1;
|
|
709
|
-
for (let i = 0;i < trimmed.length; i++) {
|
|
710
|
-
const ch = trimmed[i];
|
|
711
|
-
if (inSingle) {
|
|
712
|
-
if (ch === "'")
|
|
713
|
-
inSingle = false;
|
|
714
|
-
continue;
|
|
715
|
-
}
|
|
716
|
-
if (inDouble) {
|
|
717
|
-
if (ch === '"')
|
|
718
|
-
inDouble = false;
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
if (ch === "'") {
|
|
722
|
-
inSingle = true;
|
|
723
|
-
continue;
|
|
724
|
-
}
|
|
725
|
-
if (ch === '"') {
|
|
726
|
-
inDouble = true;
|
|
727
|
-
continue;
|
|
728
|
-
}
|
|
729
|
-
if (ch === "(")
|
|
730
|
-
depth++;
|
|
731
|
-
else if (ch === ")") {
|
|
732
|
-
depth--;
|
|
733
|
-
if (depth === 0) {
|
|
734
|
-
closeIdx = i;
|
|
735
|
-
break;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
if (closeIdx === -1)
|
|
740
|
-
throw new SlashParseError(`Unmatched parentheses in group: '${trimmed}'`);
|
|
741
|
-
const inner = trimmed.slice(1, closeIdx);
|
|
742
|
-
const suffix = trimmed.slice(closeIdx + 1).trim();
|
|
743
|
-
if (!suffix)
|
|
744
|
-
return { inner, config: {} };
|
|
745
|
-
if (!suffix.startsWith("[") || !suffix.endsWith("]")) {
|
|
746
|
-
throw new SlashParseError(`Group options must be wrapped in [...]: '${suffix}'`);
|
|
747
|
-
}
|
|
748
|
-
return { inner, config: parseGroupConfig(suffix.slice(1, -1)) };
|
|
749
|
-
};
|
|
750
|
-
export function parseGroupSegment(segment) {
|
|
751
|
-
const trimmed = segment.trim();
|
|
752
|
-
if (!trimmed.startsWith("(")) {
|
|
753
|
-
throw new SlashParseError(`Parallel group must be wrapped in parentheses: '${trimmed}'`);
|
|
754
|
-
}
|
|
755
|
-
const { inner, config } = splitGroupBody(trimmed);
|
|
756
|
-
const rawParts = splitGroupTasks(inner).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
757
|
-
if (rawParts.length < 2) {
|
|
758
|
-
throw new SlashParseError("Parallel group must contain at least two tasks separated by ' | '");
|
|
759
|
-
}
|
|
760
|
-
return { kind: "group", tasks: rawParts.map((part) => parseSingleTaskToken(part)), config };
|
|
761
|
-
}
|
|
762
|
-
export function hasGroupSyntax(input) {
|
|
763
|
-
return splitOnArrow(input).some((seg) => seg.trim().startsWith("("));
|
|
796
|
+
function slashRunWorkflowScript(key, child) {
|
|
797
|
+
return `return runs.run(${JSON.stringify(key)}, ${JSON.stringify(child)})`;
|
|
764
798
|
}
|
|
765
|
-
export function
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
}
|
|
785
|
-
steps.push(parseSingleTaskToken(t));
|
|
786
|
-
}
|
|
787
|
-
if (steps.length === 0) {
|
|
788
|
-
throw new SlashParseError("/chain expression must include at least one step");
|
|
789
|
-
}
|
|
790
|
-
return { steps };
|
|
791
|
-
}
|
|
792
|
-
const parseAgentArgs = (state, args, command, ctx) => {
|
|
793
|
-
const input = args.trim();
|
|
794
|
-
const usage = `Usage: /${command} agent1 "task1" -> agent2 "task2"`;
|
|
795
|
-
let steps;
|
|
796
|
-
let sharedTask;
|
|
797
|
-
let perStep = false;
|
|
798
|
-
if (input.includes(" -> ")) {
|
|
799
|
-
perStep = true;
|
|
800
|
-
const segments = input.split(" -> ");
|
|
801
|
-
steps = [];
|
|
802
|
-
for (const seg of segments) {
|
|
803
|
-
const trimmed = seg.trim();
|
|
804
|
-
if (!trimmed)
|
|
805
|
-
continue;
|
|
806
|
-
steps.push(parseSingleTaskToken(trimmed));
|
|
807
|
-
}
|
|
808
|
-
sharedTask = steps.find((s) => s.task)?.task ?? "";
|
|
809
|
-
} else {
|
|
810
|
-
const delimiterIndex = input.indexOf(" -- ");
|
|
811
|
-
if (delimiterIndex === -1) {
|
|
812
|
-
ctx.ui.notify(usage, "error");
|
|
813
|
-
return null;
|
|
814
|
-
}
|
|
815
|
-
const agentsPart = input.slice(0, delimiterIndex).trim();
|
|
816
|
-
sharedTask = input.slice(delimiterIndex + 4).trim();
|
|
817
|
-
if (!agentsPart || !sharedTask) {
|
|
818
|
-
ctx.ui.notify(usage, "error");
|
|
819
|
-
return null;
|
|
799
|
+
export function registerSlashCommands(dm, state, options = {}) {
|
|
800
|
+
let fleetOpen = false;
|
|
801
|
+
let disposed = false;
|
|
802
|
+
const pendingRequests = new Set;
|
|
803
|
+
const runCommand = (ctx, params) => {
|
|
804
|
+
if (disposed)
|
|
805
|
+
return Promise.resolve();
|
|
806
|
+
const controller = new AbortController;
|
|
807
|
+
pendingRequests.add(controller);
|
|
808
|
+
return runSlashSubagent(dm, ctx, params, controller.signal).finally(() => pendingRequests.delete(controller));
|
|
809
|
+
};
|
|
810
|
+
const launchCommand = (ctx, params) => {
|
|
811
|
+
runCommand(ctx, params);
|
|
812
|
+
};
|
|
813
|
+
const showFleet = async (ctx) => {
|
|
814
|
+
state.lastUiContext = ctx;
|
|
815
|
+
if (!ctx.hasUI) {
|
|
816
|
+
await runCommand(ctx, { action: "status", view: "fleet" });
|
|
817
|
+
return;
|
|
820
818
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
ctx.ui.notify(usage, "error");
|
|
825
|
-
return null;
|
|
826
|
-
}
|
|
827
|
-
if (!state.baseCwd) {
|
|
828
|
-
ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
|
|
829
|
-
return null;
|
|
830
|
-
}
|
|
831
|
-
const agents = discoverAgents(state.baseCwd, "both").agents;
|
|
832
|
-
for (const step of steps) {
|
|
833
|
-
if (!agents.find((a) => a.name === step.name)) {
|
|
834
|
-
ctx.ui.notify(`Unknown agent: ${step.name}`, "error");
|
|
835
|
-
return null;
|
|
819
|
+
if (fleetOpen) {
|
|
820
|
+
ctx.ui.notify("Subagent fleet inspector is already open.", "info");
|
|
821
|
+
return;
|
|
836
822
|
}
|
|
837
|
-
|
|
838
|
-
if (command === "chain" && !steps[0]?.task && (perStep || !sharedTask)) {
|
|
839
|
-
ctx.ui.notify(`First step must have a task: /chain agent "task" -> agent2`, "error");
|
|
840
|
-
return null;
|
|
841
|
-
}
|
|
842
|
-
if (command === "parallel" && !steps.some((s) => s.task) && !sharedTask) {
|
|
843
|
-
ctx.ui.notify("At least one step must have a task", "error");
|
|
844
|
-
return null;
|
|
845
|
-
}
|
|
846
|
-
return { steps, task: sharedTask };
|
|
847
|
-
};
|
|
848
|
-
const INLINE_ACCEPTANCE_LEVELS = new Set(["auto", "attested", "checked"]);
|
|
849
|
-
function validateInlineAcceptanceInput(value, agent) {
|
|
850
|
-
const errors = validateAcceptanceInput(value, `acceptance for step '${agent}'`);
|
|
851
|
-
if (errors.length > 0)
|
|
852
|
-
throw new SlashParseError(errors[0]);
|
|
853
|
-
if (!INLINE_ACCEPTANCE_LEVELS.has(value)) {
|
|
854
|
-
throw new SlashParseError(`Inline acceptance for step '${agent}' supports auto, attested, or checked. Use the subagent tool API or a saved .chain.json file for none, verified, or reviewed acceptance contracts.`);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
function loadInlineOutputSchema(baseCwd, agent, value) {
|
|
858
|
-
const schemaPath = path.isAbsolute(value) ? value : path.join(baseCwd, value);
|
|
859
|
-
const label = `outputSchema for step '${agent}' (${schemaPath})`;
|
|
860
|
-
let parsed;
|
|
861
|
-
try {
|
|
862
|
-
parsed = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
|
|
863
|
-
} catch (error) {
|
|
864
|
-
throw new SlashParseError(`Cannot read ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
865
|
-
}
|
|
866
|
-
assertJsonSchemaObject(parsed, label);
|
|
867
|
-
return parsed;
|
|
868
|
-
}
|
|
869
|
-
const mapParsedTaskToStepObject = (step, fallbackTask, isFirst, opts) => {
|
|
870
|
-
const { name, config, task: stepTask } = step;
|
|
871
|
-
if (config.acceptance !== undefined)
|
|
872
|
-
validateInlineAcceptanceInput(config.acceptance, name);
|
|
873
|
-
return {
|
|
874
|
-
agent: name,
|
|
875
|
-
...stepTask ? { task: stepTask } : isFirst && fallbackTask ? { task: fallbackTask } : {},
|
|
876
|
-
...config.output !== undefined ? { output: config.output } : {},
|
|
877
|
-
...config.outputMode !== undefined ? { outputMode: config.outputMode } : {},
|
|
878
|
-
...config.reads !== undefined ? { reads: config.reads } : {},
|
|
879
|
-
...config.model ? { model: config.model } : {},
|
|
880
|
-
...config.skill !== undefined ? { skill: config.skill } : {},
|
|
881
|
-
...config.progress !== undefined ? { progress: config.progress } : {},
|
|
882
|
-
...config.as ? { as: config.as } : {},
|
|
883
|
-
...config.label ? { label: config.label } : {},
|
|
884
|
-
...config.phase ? { phase: config.phase } : {},
|
|
885
|
-
...config.cwd ? { cwd: config.cwd } : {},
|
|
886
|
-
...opts.inGroup && config.count !== undefined ? { count: config.count } : {},
|
|
887
|
-
...config.outputSchema ? { outputSchema: loadInlineOutputSchema(opts.baseCwd, name, config.outputSchema) } : {},
|
|
888
|
-
...config.acceptance ? { acceptance: config.acceptance } : {}
|
|
889
|
-
};
|
|
890
|
-
};
|
|
891
|
-
export function buildChainExpressionSteps(state, input, ctx) {
|
|
892
|
-
const notify = (message) => ctx.ui.notify(message, "error");
|
|
893
|
-
if (!hasGroupSyntax(input)) {
|
|
894
|
-
const parsed = parseAgentArgs(state, input, "chain", ctx);
|
|
895
|
-
if (!parsed)
|
|
896
|
-
return null;
|
|
897
|
-
const baseCwd = state.baseCwd;
|
|
823
|
+
fleetOpen = true;
|
|
898
824
|
try {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
notify(error instanceof Error ? error.message : String(error));
|
|
903
|
-
return null;
|
|
825
|
+
await openSubagentFleet(ctx, state, { asyncDirRoot: DIRS.async, resultsDir: DIRS.results, fleetKeybindings: options.fleetKeybindings });
|
|
826
|
+
} finally {
|
|
827
|
+
fleetOpen = false;
|
|
904
828
|
}
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
notify(error instanceof Error ? error.message : String(error));
|
|
911
|
-
return null;
|
|
912
|
-
}
|
|
913
|
-
if (!state.baseCwd) {
|
|
914
|
-
notify("Subagent session cwd is not initialized yet");
|
|
915
|
-
return null;
|
|
916
|
-
}
|
|
917
|
-
const agents = discoverAgents(state.baseCwd, "both").agents;
|
|
918
|
-
const stepAgentNames = expression.steps.flatMap((step) => step.kind === "group" ? step.tasks.map((t) => t.name) : [step.name]);
|
|
919
|
-
for (const name of stepAgentNames) {
|
|
920
|
-
if (!agents.find((a) => a.name === name)) {
|
|
921
|
-
notify(`Unknown agent: ${name}`);
|
|
922
|
-
return null;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
for (const step of expression.steps) {
|
|
926
|
-
if (step.kind === "group" && step.tasks.some((t) => !t.task)) {
|
|
927
|
-
notify('Each task in a parallel group needs a task: (agent "a" | agent "b")');
|
|
928
|
-
return null;
|
|
829
|
+
};
|
|
830
|
+
dm.registerCommand("subagents", {
|
|
831
|
+
description: "Administer subagents: inspect metadata and update models, thinking, or prompts",
|
|
832
|
+
handler: async (args, ctx) => {
|
|
833
|
+
await openSubagentsAdmin(dm, ctx, args);
|
|
929
834
|
}
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
notify('First step must have a task: /chain agent "task" -> agent2');
|
|
935
|
-
return null;
|
|
936
|
-
}
|
|
937
|
-
const sharedTask = firstStep.kind === "group" ? firstStep.tasks.find((t) => t.task)?.task ?? "" : firstStep.task ?? "";
|
|
938
|
-
const baseCwd = state.baseCwd;
|
|
939
|
-
let chain;
|
|
940
|
-
try {
|
|
941
|
-
chain = expression.steps.map((step) => {
|
|
942
|
-
if (step.kind === "group") {
|
|
943
|
-
const parallel = step.tasks.map((t) => mapParsedTaskToStepObject(t, undefined, false, { baseCwd, inGroup: true }));
|
|
944
|
-
return {
|
|
945
|
-
parallel,
|
|
946
|
-
...step.config.concurrency !== undefined ? { concurrency: step.config.concurrency } : {},
|
|
947
|
-
...step.config.failFast !== undefined ? { failFast: step.config.failFast } : {},
|
|
948
|
-
...step.config.worktree !== undefined ? { worktree: step.config.worktree } : {}
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
return mapParsedTaskToStepObject(step, sharedTask || undefined, false, { baseCwd, inGroup: false });
|
|
952
|
-
});
|
|
953
|
-
} catch (error) {
|
|
954
|
-
notify(error instanceof Error ? error.message : String(error));
|
|
955
|
-
return null;
|
|
956
|
-
}
|
|
957
|
-
return { chain, task: sharedTask };
|
|
958
|
-
}
|
|
959
|
-
export function registerSlashCommands(pi, state) {
|
|
960
|
-
pi.registerCommand("run", {
|
|
961
|
-
description: "Run a subagent directly: /run agent[output=file] [task] [--bg] [--fork]",
|
|
962
|
-
getArgumentCompletions: makeAgentCompletions(state, false),
|
|
835
|
+
});
|
|
836
|
+
dm.registerCommand("run", {
|
|
837
|
+
description: "Run one subagent through workflowScript: /run agent[output=file] [task] [--bg] [--fork]",
|
|
838
|
+
getArgumentCompletions: makeAgentCompletions(dm, state),
|
|
963
839
|
handler: async (args, ctx) => {
|
|
964
840
|
const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
|
|
965
841
|
const input = cleanedArgs.trim();
|
|
@@ -974,138 +850,242 @@ export function registerSlashCommands(pi, state) {
|
|
|
974
850
|
ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
|
|
975
851
|
return;
|
|
976
852
|
}
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
|
|
853
|
+
const discovered = discoverSlashAgents(dm, state.baseCwd, "both");
|
|
854
|
+
const resolvedAgent = resolveAgentName(agentName, discovered.agents);
|
|
855
|
+
const candidates = resolvedAgent.error ? discovered.agents.filter((agent) => resolveAgentName(agentName, [agent]).agent) : resolvedAgent.agent;
|
|
856
|
+
const diagnostic = findBlockingAgentDiagnostic(agentName, candidates, discovered.agentDiagnostics);
|
|
857
|
+
if (diagnostic || resolvedAgent.error || !resolvedAgent.agent) {
|
|
858
|
+
ctx.ui.notify(diagnostic ? `Agent '${agentName}' has invalid configuration: ${diagnostic.error}` : resolvedAgent.error ?? formatUnknownAgentError(agentName, discovered.unknownAgentDiagnosticContext), "error");
|
|
980
859
|
return;
|
|
981
860
|
}
|
|
982
861
|
let finalTask = task;
|
|
983
862
|
if (inline.reads && Array.isArray(inline.reads) && inline.reads.length > 0) {
|
|
984
|
-
|
|
863
|
+
const existingReads = inline.reads.filter((read) => resolveExistingReadPaths([read], state.baseCwd).length > 0);
|
|
864
|
+
if (existingReads.length > 0)
|
|
865
|
+
finalTask = `[Read from: ${existingReads.join(", ")}]
|
|
985
866
|
|
|
986
867
|
${finalTask}`;
|
|
987
868
|
}
|
|
988
|
-
const
|
|
869
|
+
const child = { agent: agentName, task: finalTask, agentScope: "both" };
|
|
989
870
|
if (inline.output !== undefined)
|
|
990
|
-
|
|
871
|
+
child.output = inline.output;
|
|
991
872
|
if (inline.outputMode !== undefined)
|
|
992
|
-
|
|
873
|
+
child.outputMode = inline.outputMode;
|
|
993
874
|
if (inline.skill !== undefined)
|
|
994
|
-
|
|
875
|
+
child.skill = inline.skill;
|
|
995
876
|
if (inline.model)
|
|
996
|
-
|
|
997
|
-
if (bg)
|
|
998
|
-
params.async = true;
|
|
877
|
+
child.model = inline.model;
|
|
999
878
|
if (fork)
|
|
1000
|
-
|
|
1001
|
-
|
|
879
|
+
child.context = "fork";
|
|
880
|
+
launchCommand(ctx, { workflowScript: slashRunWorkflowScript("run", child), async: bg ? true : false });
|
|
1002
881
|
}
|
|
1003
882
|
});
|
|
1004
|
-
|
|
1005
|
-
description:
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
|
|
1009
|
-
const built = buildChainExpressionSteps(state, cleanedArgs, ctx);
|
|
1010
|
-
if (!built)
|
|
1011
|
-
return;
|
|
1012
|
-
const params = { chain: built.chain, task: built.task, clarify: false, agentScope: "both" };
|
|
1013
|
-
if (bg)
|
|
1014
|
-
params.async = true;
|
|
1015
|
-
if (fork)
|
|
1016
|
-
params.context = "fork";
|
|
1017
|
-
await runSlashSubagent(pi, ctx, params);
|
|
883
|
+
dm.registerCommand("subagent-cost", {
|
|
884
|
+
description: "Show parent and subagent child usage cost for this session",
|
|
885
|
+
handler: async (_args, ctx) => {
|
|
886
|
+
sendSlashText(dm, buildSubagentCostReport(ctx, state));
|
|
1018
887
|
}
|
|
1019
888
|
});
|
|
1020
|
-
|
|
1021
|
-
description: "
|
|
1022
|
-
|
|
889
|
+
dm.registerCommand("subagents-doctor", {
|
|
890
|
+
description: "Show subagent diagnostics",
|
|
891
|
+
handler: async (_args, ctx) => {
|
|
892
|
+
await runCommand(ctx, { action: "doctor" });
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
dm.registerCommand("subagents-inspect-rpc", {
|
|
896
|
+
description: "Host integration bridge: answer an async child inspection request with a correlated widget payload (no model turn)",
|
|
1023
897
|
handler: async (args, ctx) => {
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
const usage = "Usage: /run-chain <chainName> -- <task> [--bg] [--fork]";
|
|
1027
|
-
if (delimiterIndex === -1) {
|
|
1028
|
-
ctx.ui.notify(usage, "error");
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
const chainName = cleanedArgs.slice(0, delimiterIndex).trim();
|
|
1032
|
-
const task = cleanedArgs.slice(delimiterIndex + 4).trim();
|
|
1033
|
-
if (!chainName || !task) {
|
|
1034
|
-
ctx.ui.notify(usage, "error");
|
|
898
|
+
if (ctx.mode === "tui") {
|
|
899
|
+
ctx.ui.notify('Inspection replies are emitted only on RPC surfaces. Use /subagents or subagent({ action: "status", view: "transcript" }) interactively.', "info");
|
|
1035
900
|
return;
|
|
1036
901
|
}
|
|
1037
|
-
if (!
|
|
1038
|
-
ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
|
|
902
|
+
if (!ctx.hasUI)
|
|
1039
903
|
return;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
904
|
+
const reply = handleInspectRpcArgs(args, { state });
|
|
905
|
+
ctx.ui.setWidget(INSPECT_WIDGET_KEY, encodeInspectReply(reply));
|
|
906
|
+
ctx.ui.setWidget(INSPECT_WIDGET_KEY, undefined);
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
dm.registerCommand("subagents-guide", {
|
|
910
|
+
description: "Show a packaged subagents guide topic",
|
|
911
|
+
getArgumentCompletions: (prefix) => prefix.includes(" ") ? null : SUBAGENT_GUIDE_TOPICS.filter((topic) => topic.startsWith(prefix)).map((topic) => ({ value: topic, label: topic })),
|
|
912
|
+
handler: async (args, ctx) => {
|
|
913
|
+
const topic = args.trim();
|
|
914
|
+
if (topic.includes(" ")) {
|
|
915
|
+
ctx.ui.notify("Usage: /subagents-guide [topic]", "error");
|
|
1044
916
|
return;
|
|
1045
917
|
}
|
|
1046
|
-
|
|
1047
|
-
if (bg)
|
|
1048
|
-
params.async = true;
|
|
1049
|
-
if (fork)
|
|
1050
|
-
params.context = "fork";
|
|
1051
|
-
await runSlashSubagent(pi, ctx, params);
|
|
918
|
+
await runCommand(ctx, { action: "guide", ...topic ? { topic } : {} });
|
|
1052
919
|
}
|
|
1053
920
|
});
|
|
1054
|
-
|
|
1055
|
-
description:
|
|
1056
|
-
getArgumentCompletions: makeAgentCompletions(
|
|
921
|
+
dm.registerCommand("subagents-refine", {
|
|
922
|
+
description: "Generate a bounded project-local refinement overlay for one subagent",
|
|
923
|
+
getArgumentCompletions: makeAgentCompletions(dm, state),
|
|
1057
924
|
handler: async (args, ctx) => {
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
|
|
925
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
926
|
+
if (parts.length !== 1) {
|
|
927
|
+
ctx.ui.notify("Usage: /subagents-refine <agent>", "error");
|
|
1061
928
|
return;
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
task: stepTask ?? parsed.task,
|
|
1065
|
-
...config.output !== undefined ? { output: config.output } : {},
|
|
1066
|
-
...config.outputMode !== undefined ? { outputMode: config.outputMode } : {},
|
|
1067
|
-
...config.reads !== undefined ? { reads: config.reads } : {},
|
|
1068
|
-
...config.model ? { model: config.model } : {},
|
|
1069
|
-
...config.skill !== undefined ? { skill: config.skill } : {},
|
|
1070
|
-
...config.progress !== undefined ? { progress: config.progress } : {}
|
|
1071
|
-
}));
|
|
1072
|
-
const params = { tasks, clarify: false, agentScope: "both" };
|
|
1073
|
-
if (bg)
|
|
1074
|
-
params.async = true;
|
|
1075
|
-
if (fork)
|
|
1076
|
-
params.context = "fork";
|
|
1077
|
-
await runSlashSubagent(pi, ctx, params);
|
|
929
|
+
}
|
|
930
|
+
await runCommand(ctx, { action: "refine", agent: parts[0] });
|
|
1078
931
|
}
|
|
1079
932
|
});
|
|
1080
|
-
|
|
1081
|
-
description: "
|
|
1082
|
-
handler: async (_args, ctx) =>
|
|
1083
|
-
|
|
933
|
+
dm.registerCommand("subagents-fleet", {
|
|
934
|
+
description: "Open the live subagent fleet inspector",
|
|
935
|
+
handler: async (_args, ctx) => showFleet(ctx)
|
|
936
|
+
});
|
|
937
|
+
dm.registerShortcut(FLEET_OPEN_SHORTCUT, {
|
|
938
|
+
description: "Open subagent fleet inspector",
|
|
939
|
+
handler: async (ctx) => showFleet(ctx)
|
|
940
|
+
});
|
|
941
|
+
const detachForegroundRun = (args, ctx) => {
|
|
942
|
+
const id = args.trim();
|
|
943
|
+
let control;
|
|
944
|
+
try {
|
|
945
|
+
control = selectForegroundDetachControl(state, id);
|
|
946
|
+
} catch (error) {
|
|
947
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (!control) {
|
|
951
|
+
ctx.ui.notify(id ? `No active foreground run found for '${id}'.` : "No active foreground single-subagent run to detach.", "info");
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
if (control.mode !== "single") {
|
|
955
|
+
ctx.ui.notify("/subagents-detach currently supports single-subagent runs only.", "error");
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if (!control.detach?.()) {
|
|
959
|
+
ctx.ui.notify(`Foreground run ${control.runId} is not currently detachable.`, "info");
|
|
960
|
+
return;
|
|
1084
961
|
}
|
|
962
|
+
sendSlashText(dm, `Detached foreground run ${control.runId} without terminating its child. Use subagent({ action: "status", id: ${JSON.stringify(control.runId)} }) or bg_wait({ id: ${JSON.stringify(control.runId)} }) to recover the eventual result. This does not daemonize the process or guarantee survival across DM reload/restart.`);
|
|
963
|
+
};
|
|
964
|
+
dm.registerCommand("subagents-detach", {
|
|
965
|
+
description: "Detach the active foreground single-subagent run without terminating it",
|
|
966
|
+
handler: async (args, ctx) => detachForegroundRun(args, ctx)
|
|
1085
967
|
});
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
968
|
+
if (options.foregroundDetachShortcut) {
|
|
969
|
+
dm.registerShortcut(options.foregroundDetachShortcut, {
|
|
970
|
+
description: "Detach the active foreground subagent and keep it running in the background",
|
|
971
|
+
handler: async (ctx) => detachForegroundRun("", ctx)
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
dm.registerCommand("subagents-stop", {
|
|
975
|
+
description: "Stop a current-session async subagent run, or one child of it with /subagents-stop <run-id> <child-id>",
|
|
976
|
+
handler: async (args, ctx) => {
|
|
977
|
+
const [id, childId, ...extra] = args.trim().split(/\s+/).filter(Boolean);
|
|
978
|
+
if (extra.length > 0) {
|
|
979
|
+
sendSlashText(dm, "Usage: /subagents-stop [run-id] [child-id]");
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (id) {
|
|
983
|
+
await runCommand(ctx, childId ? { action: "stop", id, childId } : { action: "stop", id });
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (process.env[SUBAGENT_FANOUT_CHILD_ENV] === "1") {
|
|
987
|
+
sendSlashText(dm, 'Selector unavailable in child-safe fanout mode. Pass an explicit current-session top-level async run id, for example `/subagents-stop <run-id>` or `subagent({ action: "stop", id: "<run-id>" })`.');
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
let targets;
|
|
991
|
+
try {
|
|
992
|
+
targets = discoverStopTargets(ctx, state);
|
|
993
|
+
} catch (error) {
|
|
994
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
995
|
+
ctx.ui.notify(message, "error");
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (!ctx.hasUI) {
|
|
999
|
+
sendSlashText(dm, stopFallbackText(targets));
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (targets.length === 0) {
|
|
1003
|
+
ctx.ui.notify("No active current-session async runs or scheduled subagent runs to stop.", "info");
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
const result = await ctx.ui.custom((tui, theme, _kb, done) => new SubagentsStopSelector(tui, theme, targets, done), { overlay: true, overlayOptions: { anchor: "center", width: 88, maxHeight: "80%" } });
|
|
1007
|
+
if (!result?.confirmed || !result.target)
|
|
1008
|
+
return;
|
|
1009
|
+
if (result.target.kind === "scheduled") {
|
|
1010
|
+
await runCommand(ctx, { action: "schedule.pause", id: result.target.id });
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
await runCommand(ctx, { action: "stop", id: result.target.id });
|
|
1090
1014
|
}
|
|
1091
1015
|
});
|
|
1092
|
-
|
|
1093
|
-
description: "
|
|
1094
|
-
handler: async (
|
|
1095
|
-
|
|
1016
|
+
dm.registerCommand("subagents-steer", {
|
|
1017
|
+
description: "Steer a live async subagent run with a message, or one child of it with --child <child-id>",
|
|
1018
|
+
handler: async (args, ctx) => {
|
|
1019
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
1020
|
+
const usage = "Usage: /subagents-steer <run-id> [--child <child-id>] <message>";
|
|
1021
|
+
const [id, ...rest] = tokens;
|
|
1022
|
+
if (!id || id.startsWith("--")) {
|
|
1023
|
+
sendSlashText(dm, usage);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
let childId;
|
|
1027
|
+
let messageStart = 0;
|
|
1028
|
+
if (rest[0] === "--child" && rest[1] && !rest[1].startsWith("--")) {
|
|
1029
|
+
childId = rest[1];
|
|
1030
|
+
messageStart = 2;
|
|
1031
|
+
}
|
|
1032
|
+
const message = rest.slice(messageStart).join(" ").trim();
|
|
1033
|
+
if (!message) {
|
|
1034
|
+
sendSlashText(dm, usage);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
let targetId = id;
|
|
1038
|
+
let childIndex;
|
|
1039
|
+
if (childId !== undefined) {
|
|
1040
|
+
const sessionId = state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined;
|
|
1041
|
+
const run = listAsyncRuns(DIRS.async, {
|
|
1042
|
+
runId: id,
|
|
1043
|
+
states: ["queued", "running", "paused"],
|
|
1044
|
+
...sessionId ? { sessionId } : {}
|
|
1045
|
+
})[0];
|
|
1046
|
+
const status = run ? readStatus(run.asyncDir) : null;
|
|
1047
|
+
if (!run || !status) {
|
|
1048
|
+
sendSlashText(dm, `No current-session async run found for '${id}'.`);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
const resolutionStatus = {
|
|
1052
|
+
...status,
|
|
1053
|
+
steps: status.steps?.map((step, index) => ({ ...step, children: run.steps[index]?.children ?? step.children }))
|
|
1054
|
+
};
|
|
1055
|
+
const resolution = resolveAsyncStatusChild(resolutionStatus, childId, { includeNested: true });
|
|
1056
|
+
if (!resolution.ok) {
|
|
1057
|
+
sendSlashText(dm, resolution.message);
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
if (resolution.child.nested)
|
|
1061
|
+
targetId = resolution.child.nested.id;
|
|
1062
|
+
else if (resolution.child.step.runId)
|
|
1063
|
+
targetId = resolution.child.step.runId;
|
|
1064
|
+
else
|
|
1065
|
+
childIndex = resolution.child.index;
|
|
1066
|
+
}
|
|
1067
|
+
await runCommand(ctx, {
|
|
1068
|
+
action: "steer",
|
|
1069
|
+
id: targetId,
|
|
1070
|
+
message,
|
|
1071
|
+
...childIndex !== undefined ? { index: childIndex } : {},
|
|
1072
|
+
steeringRecovery: false
|
|
1073
|
+
});
|
|
1096
1074
|
}
|
|
1097
1075
|
});
|
|
1098
1076
|
registerPromptWorkflowCommands({
|
|
1099
|
-
|
|
1100
|
-
run: (params, ctx) =>
|
|
1077
|
+
dm,
|
|
1078
|
+
run: async (params, ctx) => {
|
|
1079
|
+
launchCommand(ctx, params);
|
|
1080
|
+
}
|
|
1101
1081
|
});
|
|
1102
|
-
|
|
1082
|
+
dm.registerCommand("subagents-models", {
|
|
1103
1083
|
description: "Show runtime-loaded builtin subagent models",
|
|
1104
1084
|
getArgumentCompletions: makeBuiltinAgentNameCompletions(),
|
|
1105
1085
|
handler: async (args, ctx) => {
|
|
1106
1086
|
const trimmed = args.trim();
|
|
1107
1087
|
if (!trimmed) {
|
|
1108
|
-
await
|
|
1088
|
+
await runCommand(ctx, { action: "models" });
|
|
1109
1089
|
return;
|
|
1110
1090
|
}
|
|
1111
1091
|
const parts = trimmed.split(/\s+/).filter(Boolean);
|
|
@@ -1118,26 +1098,26 @@ ${finalTask}`;
|
|
|
1118
1098
|
ctx.ui.notify(`Unknown builtin agent: ${agent}`, "error");
|
|
1119
1099
|
return;
|
|
1120
1100
|
}
|
|
1121
|
-
await
|
|
1101
|
+
await runCommand(ctx, { action: "models", agent });
|
|
1122
1102
|
}
|
|
1123
1103
|
});
|
|
1124
|
-
|
|
1104
|
+
dm.registerCommand("subagents-profiles", {
|
|
1125
1105
|
description: "List saved subagent profiles",
|
|
1126
1106
|
handler: async (_args, _ctx) => {
|
|
1127
1107
|
const profiles = listSubagentProfiles();
|
|
1128
1108
|
if (profiles.length === 0) {
|
|
1129
|
-
sendSlashText(
|
|
1109
|
+
sendSlashText(dm, `Subagent profiles
|
|
1130
1110
|
|
|
1131
1111
|
No subagent profiles found in ~/.dm/agent/profiles/dm-subagents/`);
|
|
1132
1112
|
return;
|
|
1133
1113
|
}
|
|
1134
|
-
sendSlashText(
|
|
1114
|
+
sendSlashText(dm, `Subagent profiles
|
|
1135
1115
|
|
|
1136
1116
|
${profiles.join(`
|
|
1137
1117
|
`)}`);
|
|
1138
1118
|
}
|
|
1139
1119
|
});
|
|
1140
|
-
|
|
1120
|
+
dm.registerCommand("subagents-load-profile", {
|
|
1141
1121
|
description: "Load a subagent profile into ~/.dm/agent/settings.json",
|
|
1142
1122
|
getArgumentCompletions: (prefix) => {
|
|
1143
1123
|
if (prefix.includes(" "))
|
|
@@ -1146,7 +1126,7 @@ ${profiles.join(`
|
|
|
1146
1126
|
},
|
|
1147
1127
|
handler: async (args, ctx) => {
|
|
1148
1128
|
const parsed = parseSingleRequiredArg(args, "Usage: /subagents-load-profile <name>");
|
|
1149
|
-
if (
|
|
1129
|
+
if (parsed.ok === false) {
|
|
1150
1130
|
ctx.ui.notify(parsed.message, "error");
|
|
1151
1131
|
return;
|
|
1152
1132
|
}
|
|
@@ -1160,7 +1140,7 @@ ${profiles.join(`
|
|
|
1160
1140
|
`Profile: ${result.filePath}`,
|
|
1161
1141
|
`Updated: ${result.settingsPath}`
|
|
1162
1142
|
];
|
|
1163
|
-
if (workerModel && typeof
|
|
1143
|
+
if (workerModel && typeof dm.setModel === "function" && typeof ctx.modelRegistry?.find === "function" && typeof ctx.modelRegistry?.getAvailable === "function") {
|
|
1164
1144
|
const shouldSwitch = await ctx.ui.confirm("", `Profile loaded. Also switch this session to the profile worker model?
|
|
1165
1145
|
|
|
1166
1146
|
${workerModel}`);
|
|
@@ -1170,7 +1150,7 @@ ${workerModel}`);
|
|
|
1170
1150
|
if (!modelInfo || !model) {
|
|
1171
1151
|
lines.push(`Could not switch current session model: '${workerModel}' is not available in the current model registry.`);
|
|
1172
1152
|
} else {
|
|
1173
|
-
const success = await
|
|
1153
|
+
const success = await dm.setModel(model);
|
|
1174
1154
|
if (success)
|
|
1175
1155
|
lines.push(`Current session model switched to: ${modelInfo.fullId}`);
|
|
1176
1156
|
else
|
|
@@ -1180,7 +1160,7 @@ ${workerModel}`);
|
|
|
1180
1160
|
} else if (workerModel) {
|
|
1181
1161
|
lines.push(`Profile worker model: ${workerModel}`);
|
|
1182
1162
|
}
|
|
1183
|
-
sendSlashText(
|
|
1163
|
+
sendSlashText(dm, lines.join(`
|
|
1184
1164
|
`));
|
|
1185
1165
|
});
|
|
1186
1166
|
} catch (error) {
|
|
@@ -1188,7 +1168,7 @@ ${workerModel}`);
|
|
|
1188
1168
|
}
|
|
1189
1169
|
}
|
|
1190
1170
|
});
|
|
1191
|
-
|
|
1171
|
+
dm.registerCommand("subagents-refresh-provider-models", {
|
|
1192
1172
|
description: "Refresh the cached model catalog for one provider",
|
|
1193
1173
|
getArgumentCompletions: makeProviderCompletions(state),
|
|
1194
1174
|
handler: async (args, ctx) => {
|
|
@@ -1196,13 +1176,13 @@ ${workerModel}`);
|
|
|
1196
1176
|
const force = /(?:^|\s)--force$/.test(trimmed) || /(?:^|\s)force$/.test(trimmed);
|
|
1197
1177
|
const withoutForce = trimmed.replace(/(?:^|\s)(?:--force|force)$/, "").trim();
|
|
1198
1178
|
const parsed = parseSingleRequiredArg(withoutForce, "Usage: /subagents-refresh-provider-models <provider> [--force]");
|
|
1199
|
-
if (
|
|
1179
|
+
if (parsed.ok === false) {
|
|
1200
1180
|
ctx.ui.notify(parsed.message, "error");
|
|
1201
1181
|
return;
|
|
1202
1182
|
}
|
|
1203
1183
|
try {
|
|
1204
1184
|
await withSlashStatus(ctx, `Refreshing provider models for ${parsed.value}…`, async () => {
|
|
1205
|
-
const result = await refreshProviderModelCatalog(
|
|
1185
|
+
const result = await refreshProviderModelCatalog(dm, ctx, parsed.value, { force, maxAgeDays: DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS });
|
|
1206
1186
|
const lines = [
|
|
1207
1187
|
"Provider model catalog",
|
|
1208
1188
|
`Provider: ${parsed.value}`,
|
|
@@ -1214,7 +1194,7 @@ ${workerModel}`);
|
|
|
1214
1194
|
if (result.heuristicFallbackCount > 0) {
|
|
1215
1195
|
lines.push(`Warning: ${result.heuristicFallbackCount} model${result.heuristicFallbackCount === 1 ? " was" : "s were"} classified with name heuristics fallback.`);
|
|
1216
1196
|
}
|
|
1217
|
-
sendSlashText(
|
|
1197
|
+
sendSlashText(dm, lines.join(`
|
|
1218
1198
|
`));
|
|
1219
1199
|
});
|
|
1220
1200
|
} catch (error) {
|
|
@@ -1222,18 +1202,18 @@ ${workerModel}`);
|
|
|
1222
1202
|
}
|
|
1223
1203
|
}
|
|
1224
1204
|
});
|
|
1225
|
-
|
|
1205
|
+
dm.registerCommand("subagents-generate-profiles", {
|
|
1226
1206
|
description: "Generate <provider>.quota and <provider>.quality subagent profiles",
|
|
1227
1207
|
getArgumentCompletions: makeProviderCompletions(state),
|
|
1228
1208
|
handler: async (args, ctx) => {
|
|
1229
1209
|
const parsed = parseSingleRequiredArg(args, "Usage: /subagents-generate-profiles <provider>");
|
|
1230
|
-
if (
|
|
1210
|
+
if (parsed.ok === false) {
|
|
1231
1211
|
ctx.ui.notify(parsed.message, "error");
|
|
1232
1212
|
return;
|
|
1233
1213
|
}
|
|
1234
1214
|
try {
|
|
1235
1215
|
await withSlashStatus(ctx, `Generating profiles for ${parsed.value}…`, async () => {
|
|
1236
|
-
const result = await generateProfilesForProvider(
|
|
1216
|
+
const result = await generateProfilesForProvider(dm, ctx, parsed.value, { maxAgeDays: DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS });
|
|
1237
1217
|
const lines = [
|
|
1238
1218
|
"Generated subagent profiles",
|
|
1239
1219
|
`Provider: ${parsed.value}`,
|
|
@@ -1252,7 +1232,7 @@ ${workerModel}`);
|
|
|
1252
1232
|
} else if (result.heuristicFallbackCount > 0) {
|
|
1253
1233
|
lines.push(`Warning: provider catalog still contains ${result.heuristicFallbackCount} heuristic-classified model${result.heuristicFallbackCount === 1 ? "" : "s"}.`);
|
|
1254
1234
|
}
|
|
1255
|
-
sendSlashText(
|
|
1235
|
+
sendSlashText(dm, lines.join(`
|
|
1256
1236
|
`));
|
|
1257
1237
|
});
|
|
1258
1238
|
} catch (error) {
|
|
@@ -1260,7 +1240,7 @@ ${workerModel}`);
|
|
|
1260
1240
|
}
|
|
1261
1241
|
}
|
|
1262
1242
|
});
|
|
1263
|
-
|
|
1243
|
+
dm.registerCommand("subagents-check-profile", {
|
|
1264
1244
|
description: "Check whether a saved profile still points to usable models",
|
|
1265
1245
|
getArgumentCompletions: (prefix) => {
|
|
1266
1246
|
if (prefix.includes(" "))
|
|
@@ -1269,13 +1249,13 @@ ${workerModel}`);
|
|
|
1269
1249
|
},
|
|
1270
1250
|
handler: async (args, ctx) => {
|
|
1271
1251
|
const parsed = parseSingleRequiredArg(args, "Usage: /subagents-check-profile <name>");
|
|
1272
|
-
if (
|
|
1252
|
+
if (parsed.ok === false) {
|
|
1273
1253
|
ctx.ui.notify(parsed.message, "error");
|
|
1274
1254
|
return;
|
|
1275
1255
|
}
|
|
1276
1256
|
try {
|
|
1277
1257
|
await withSlashStatus(ctx, `Checking profile ${parsed.value}…`, async () => {
|
|
1278
|
-
const result = await checkSubagentProfile(
|
|
1258
|
+
const result = await checkSubagentProfile(dm, ctx, parsed.value);
|
|
1279
1259
|
const lines = [
|
|
1280
1260
|
"Subagent profile check",
|
|
1281
1261
|
`Profile: ${result.profileName}`,
|
|
@@ -1283,7 +1263,7 @@ ${workerModel}`);
|
|
|
1283
1263
|
"",
|
|
1284
1264
|
...result.results.map((entry) => `${entry.agent} → ${entry.model} — registry ${entry.inRegistry ? "ok" : "missing"}; probe ${entry.probe.status}${entry.probe.message ? ` (${entry.probe.message.split(/\r?\n/, 1)[0]})` : ""}`)
|
|
1285
1265
|
];
|
|
1286
|
-
sendSlashText(
|
|
1266
|
+
sendSlashText(dm, lines.join(`
|
|
1287
1267
|
`));
|
|
1288
1268
|
});
|
|
1289
1269
|
} catch (error) {
|
|
@@ -1291,4 +1271,14 @@ ${workerModel}`);
|
|
|
1291
1271
|
}
|
|
1292
1272
|
}
|
|
1293
1273
|
});
|
|
1274
|
+
return {
|
|
1275
|
+
dispose: () => {
|
|
1276
|
+
if (disposed)
|
|
1277
|
+
return;
|
|
1278
|
+
disposed = true;
|
|
1279
|
+
for (const controller of pendingRequests)
|
|
1280
|
+
controller.abort();
|
|
1281
|
+
pendingRequests.clear();
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1294
1284
|
}
|