@duckmind/dm-windows-x64 0.61.4 → 0.61.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dm.exe +0 -0
- package/extensions/.dm-extensions.json +211 -67
- package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
- package/extensions/dm-subagents/agents/claude-code.md +15 -0
- package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
- package/extensions/dm-subagents/agents/codex-exec.md +15 -0
- package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
- package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
- package/extensions/dm-subagents/agents/delegate.md +3 -2
- package/extensions/dm-subagents/agents/oracle.md +10 -5
- package/extensions/dm-subagents/agents/researcher.md +2 -2
- package/extensions/dm-subagents/agents/reviewer.md +17 -7
- package/extensions/dm-subagents/agents/scout.md +5 -5
- package/extensions/dm-subagents/agents/worker.md +6 -2
- package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
- package/extensions/dm-subagents/index.js +4 -0
- package/extensions/dm-subagents/inspector-runner.mjs +10 -0
- package/extensions/dm-subagents/install.mjs +3 -2
- package/extensions/dm-subagents/package.json +2 -2
- package/extensions/dm-subagents/prompts/council.md +60 -0
- package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
- package/extensions/dm-subagents/prompts/review-loop.md +13 -7
- package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
- package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
- package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
- package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
- package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
- package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
- package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
- package/extensions/dm-subagents/src/agents/agents.js +1447 -299
- package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
- package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
- package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
- package/extensions/dm-subagents/src/agents/identity.js +1 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
- package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
- package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
- package/extensions/dm-subagents/src/agents/skills.js +52 -35
- package/extensions/dm-subagents/src/api/agents.js +6 -0
- package/extensions/dm-subagents/src/api/background-work.js +151 -0
- package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
- package/extensions/dm-subagents/src/api/control-channel.js +3 -0
- package/extensions/dm-subagents/src/api/delegation.js +5 -0
- package/extensions/dm-subagents/src/api/dm-args.js +3 -0
- package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
- package/extensions/dm-subagents/src/api/external-runs.js +233 -0
- package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
- package/extensions/dm-subagents/src/api/preflight.js +322 -0
- package/extensions/dm-subagents/src/api/project-panes.js +11 -0
- package/extensions/dm-subagents/src/api/shared-types.js +4 -0
- package/extensions/dm-subagents/src/extension/config.js +175 -0
- package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
- package/extensions/dm-subagents/src/extension/doctor.js +71 -17
- package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
- package/extensions/dm-subagents/src/extension/index.js +711 -265
- package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
- package/extensions/dm-subagents/src/extension/rpc.js +427 -22
- package/extensions/dm-subagents/src/extension/schemas.js +158 -65
- package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
- package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
- package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
- package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
- package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
- package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
- package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
- package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
- package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
- package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
- package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
- package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
- package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
- package/extensions/dm-subagents/src/missions/actions.js +394 -0
- package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
- package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
- package/extensions/dm-subagents/src/missions/store.js +548 -0
- package/extensions/dm-subagents/src/missions/types.js +9 -0
- package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
- package/extensions/dm-subagents/src/policy/authority.js +37 -0
- package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
- package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
- package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
- package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
- package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
- package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
- package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
- package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
- package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
- package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
- package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
- package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
- package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
- package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
- package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
- package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
- package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
- package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
- package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
- package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
- package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
- package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
- package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
- package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
- package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
- package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
- package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
- package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
- package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
- package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
- package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
- package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
- package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
- package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
- package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
- package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
- package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
- package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
- package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
- package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
- package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
- package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
- package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
- package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
- package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
- package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
- package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
- package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
- package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
- package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
- package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
- package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
- package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
- package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
- package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
- package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
- package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
- package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
- package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
- package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
- package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
- package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
- package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
- package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
- package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
- package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
- package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
- package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
- package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
- package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
- package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
- package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
- package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
- package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
- package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
- package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
- package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
- package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
- package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
- package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
- package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
- package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
- package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
- package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
- package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
- package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
- package/extensions/dm-subagents/src/shared/display-text.js +142 -0
- package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
- package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
- package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
- package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
- package/extensions/dm-subagents/src/shared/formatters.js +21 -7
- package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
- package/extensions/dm-subagents/src/shared/model-info.js +10 -5
- package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
- package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
- package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
- package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
- package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
- package/extensions/dm-subagents/src/shared/settings.js +39 -47
- package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
- package/extensions/dm-subagents/src/shared/status-format.js +9 -2
- package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
- package/extensions/dm-subagents/src/shared/types.js +47 -7
- package/extensions/dm-subagents/src/shared/utf8.js +12 -0
- package/extensions/dm-subagents/src/shared/utils.js +151 -131
- package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
- package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
- package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
- package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
- package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
- package/extensions/dm-subagents/src/slash/selector.js +101 -0
- package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
- package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
- package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
- package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
- package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
- package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
- package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
- package/extensions/dm-subagents/src/tui/render.js +1511 -261
- package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
- package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
- package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
- package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
- package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
- package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
- package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
- package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
- package/extensions/dm-subagents/src/watchdog/render.js +54 -0
- package/extensions/dm-subagents/src/watchdog/review.js +251 -0
- package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
- package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
- package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
- package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
- package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
- package/extensions/dm-subagents/src/watchdog/types.js +29 -0
- package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
- package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
- package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
- package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
- package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
- package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
- package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
- package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
- package/package.json +4 -3
- package/extensions/dm-fff/package.json +0 -21
- package/extensions/dm-fff/src/index.js +0 -691
- package/extensions/dm-fff/src/query.js +0 -60
- package/extensions/dm-subagents/agents/context-builder.md +0 -46
- package/extensions/dm-subagents/agents/planner.md +0 -55
- package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
- package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
- package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Agent } from "@duckmind/dm-agent-core";
|
|
2
|
+
import { convertToLlm } from "@duckmind/dm-coding-agent";
|
|
3
|
+
import { streamSimple } from "@duckmind/dm-ai/compat";
|
|
4
|
+
import { Type } from "typebox";
|
|
5
|
+
import { appendPermissionAudit, permissionArgsPreview } from "../runs/shared/permissions.js";
|
|
6
|
+
import { agentStreamOptions } from "../shared/agent-stream-options.js";
|
|
7
|
+
import { decodeChildWatchdogConfig } from "./child-status.js";
|
|
8
|
+
import { childResolvedConfig } from "./register-child.js";
|
|
9
|
+
import { resolveWatchdogReviewModel } from "./review.js";
|
|
10
|
+
const PermissionDecisionParams = Type.Object({
|
|
11
|
+
decision: Type.String({ enum: ["approve", "deny"] }),
|
|
12
|
+
reason: Type.String({ description: "One concise reason for this exact decision." })
|
|
13
|
+
}, { additionalProperties: false });
|
|
14
|
+
function conciseReason(value) {
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
return trimmed ? trimmed.slice(0, 500) : "Watchdog returned an empty reason.";
|
|
17
|
+
}
|
|
18
|
+
export function createWatchdogPermissionArbiter(options = {}) {
|
|
19
|
+
return async (request) => {
|
|
20
|
+
const preview = permissionArgsPreview(request.args);
|
|
21
|
+
const createdAt = Date.now();
|
|
22
|
+
const auditBase = { type: "permission.request", createdAt, toolName: request.toolName, preview, matchedRule: "ask", decisionSource: "watchdog" };
|
|
23
|
+
appendPermissionAudit(request.auditPath, auditBase);
|
|
24
|
+
let completed = false;
|
|
25
|
+
const finish = (approved, reason, decision) => {
|
|
26
|
+
const concise = conciseReason(reason);
|
|
27
|
+
if (completed)
|
|
28
|
+
return { approved, reason: concise, source: "watchdog" };
|
|
29
|
+
completed = true;
|
|
30
|
+
appendPermissionAudit(request.auditPath, {
|
|
31
|
+
type: "permission.decision",
|
|
32
|
+
createdAt: Date.now(),
|
|
33
|
+
requestCreatedAt: createdAt,
|
|
34
|
+
toolName: request.toolName,
|
|
35
|
+
decision,
|
|
36
|
+
approved,
|
|
37
|
+
decisionSource: "watchdog",
|
|
38
|
+
reason: concise
|
|
39
|
+
});
|
|
40
|
+
return { approved, reason: concise, source: "watchdog" };
|
|
41
|
+
};
|
|
42
|
+
let childConfig;
|
|
43
|
+
try {
|
|
44
|
+
childConfig = decodeChildWatchdogConfig(request.rawWatchdogConfig);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
47
|
+
return finish(false, `Watchdog permission arbiter configuration is invalid: ${reason}`, "unavailable");
|
|
48
|
+
}
|
|
49
|
+
if (!childConfig)
|
|
50
|
+
return finish(false, "Watchdog permission arbiter is unavailable because the child watchdog is disabled.", "unavailable");
|
|
51
|
+
if (request.signal?.aborted || request.ctx.signal?.aborted)
|
|
52
|
+
return finish(false, "Watchdog permission decision was cancelled.", "cancelled");
|
|
53
|
+
let decision;
|
|
54
|
+
const tool = {
|
|
55
|
+
name: "watchdog_permission_decision",
|
|
56
|
+
label: "Watchdog permission decision",
|
|
57
|
+
description: "Approve or deny this exact child tool call. Call exactly once.",
|
|
58
|
+
parameters: PermissionDecisionParams,
|
|
59
|
+
executionMode: "sequential",
|
|
60
|
+
async execute(_toolCallId, params) {
|
|
61
|
+
if (!decision)
|
|
62
|
+
decision = params;
|
|
63
|
+
return { content: [{ type: "text", text: "Permission decision recorded." }], details: { recorded: true } };
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
let timeout;
|
|
67
|
+
let agent;
|
|
68
|
+
let abort;
|
|
69
|
+
try {
|
|
70
|
+
const run = async () => {
|
|
71
|
+
const config = childResolvedConfig(childConfig);
|
|
72
|
+
const selection = await resolveWatchdogReviewModel(request.ctx, config);
|
|
73
|
+
const auth = selection.auth;
|
|
74
|
+
const registeredProvider = request.ctx.modelRegistry.getRegisteredProviderConfig?.(selection.model.provider);
|
|
75
|
+
const baseStreamFn = options.streamFn ?? (registeredProvider?.streamSimple && registeredProvider.api === selection.model.api ? registeredProvider.streamSimple : streamSimple);
|
|
76
|
+
const streamFn = (model, context, streamOptions) => baseStreamFn(model, context, {
|
|
77
|
+
...streamOptions,
|
|
78
|
+
...auth.apiKey ? { apiKey: auth.apiKey } : {},
|
|
79
|
+
env: auth.env || streamOptions?.env ? { ...auth.env ?? {}, ...streamOptions?.env ?? {} } : undefined,
|
|
80
|
+
headers: { ...streamOptions?.headers ?? {}, ...auth.headers ?? {} }
|
|
81
|
+
});
|
|
82
|
+
agent = new Agent({
|
|
83
|
+
initialState: {
|
|
84
|
+
systemPrompt: [
|
|
85
|
+
"You are the dm-subagents watchdog permission arbiter.",
|
|
86
|
+
"Decide only whether this exact non-bash child tool call should proceed.",
|
|
87
|
+
"Call watchdog_permission_decision exactly once with approve or deny and a concise reason.",
|
|
88
|
+
"Deny when uncertain. Do not produce freeform advice or ask the parent orchestrator."
|
|
89
|
+
].join(`
|
|
90
|
+
`),
|
|
91
|
+
model: selection.model,
|
|
92
|
+
thinkingLevel: selection.thinkingLevel,
|
|
93
|
+
tools: [tool]
|
|
94
|
+
},
|
|
95
|
+
convertToLlm,
|
|
96
|
+
...agentStreamOptions(streamFn),
|
|
97
|
+
getApiKey: (providerName) => providerName === selection.model.provider ? auth.apiKey : undefined,
|
|
98
|
+
beforeToolCall: async ({ toolCall }) => toolCall.name === tool.name ? undefined : { block: true, reason: `Permission arbiter tool '${toolCall.name}' is not allowed.` },
|
|
99
|
+
toolExecution: "sequential"
|
|
100
|
+
});
|
|
101
|
+
await agent.prompt(`Tool: ${request.toolName}
|
|
102
|
+
Redacted arguments: ${preview}`);
|
|
103
|
+
if (!decision)
|
|
104
|
+
return finish(false, "Watchdog permission arbiter returned no decision.", "malformed");
|
|
105
|
+
const approved = decision.decision === "approve";
|
|
106
|
+
return finish(approved, decision.reason, decision.decision);
|
|
107
|
+
};
|
|
108
|
+
return await Promise.race([
|
|
109
|
+
run(),
|
|
110
|
+
new Promise((resolve) => {
|
|
111
|
+
timeout = setTimeout(() => {
|
|
112
|
+
agent?.abort();
|
|
113
|
+
resolve(finish(false, "Watchdog permission decision timed out.", "timeout"));
|
|
114
|
+
}, childConfig.agentEndTimeoutMs);
|
|
115
|
+
}),
|
|
116
|
+
new Promise((resolve) => {
|
|
117
|
+
abort = () => {
|
|
118
|
+
agent?.abort();
|
|
119
|
+
resolve(finish(false, "Watchdog permission decision was cancelled.", "cancelled"));
|
|
120
|
+
};
|
|
121
|
+
request.signal?.addEventListener("abort", abort, { once: true });
|
|
122
|
+
request.ctx.signal?.addEventListener("abort", abort, { once: true });
|
|
123
|
+
})
|
|
124
|
+
]);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
127
|
+
return finish(false, `Watchdog permission arbiter failed closed: ${reason}`, reason.includes("timed out") ? "timeout" : "error");
|
|
128
|
+
} finally {
|
|
129
|
+
if (timeout)
|
|
130
|
+
clearTimeout(timeout);
|
|
131
|
+
if (abort) {
|
|
132
|
+
request.signal?.removeEventListener("abort", abort);
|
|
133
|
+
request.ctx.signal?.removeEventListener("abort", abort);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export const requestWatchdogPermission = createWatchdogPermissionArbiter();
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { MainWatchdogRuntime } from "./runtime.js";
|
|
2
|
+
import { createMainWatchdogReview } from "./review.js";
|
|
3
|
+
import { DEFAULT_WATCHDOG_CONFIG } from "./settings.js";
|
|
4
|
+
import { createWatchdogWarningMessage } from "./warning-format.js";
|
|
5
|
+
import {
|
|
6
|
+
CHILD_WATCHDOG_CONFIG_ENV,
|
|
7
|
+
CHILD_WATCHDOG_STATUS_EVENT,
|
|
8
|
+
decodeChildWatchdogConfig
|
|
9
|
+
} from "./child-status.js";
|
|
10
|
+
export function childResolvedConfig(config) {
|
|
11
|
+
return {
|
|
12
|
+
...DEFAULT_WATCHDOG_CONFIG,
|
|
13
|
+
enabled: true,
|
|
14
|
+
agentEndTimeoutMs: config.agentEndTimeoutMs,
|
|
15
|
+
maxWarnings: config.maxWarnings,
|
|
16
|
+
main: {
|
|
17
|
+
enabled: true,
|
|
18
|
+
...config.model ? { model: config.model } : {},
|
|
19
|
+
...config.thinking !== undefined ? { thinking: config.thinking } : {}
|
|
20
|
+
},
|
|
21
|
+
autoFollow: {
|
|
22
|
+
blockers: config.autoFollowBlockers,
|
|
23
|
+
maxAttempts: config.autoFollowMaxAttempts,
|
|
24
|
+
stalemateRepeats: config.stalemateRepeats
|
|
25
|
+
},
|
|
26
|
+
children: {
|
|
27
|
+
...DEFAULT_WATCHDOG_CONFIG.children,
|
|
28
|
+
watchdogTailTimeoutMs: config.watchdogTailTimeoutMs
|
|
29
|
+
},
|
|
30
|
+
lsp: { ...config.lsp }
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function childWarningDetails(details, config) {
|
|
34
|
+
return {
|
|
35
|
+
...details,
|
|
36
|
+
source: details.source === "lsp" ? "lsp" : "child",
|
|
37
|
+
...config.agent ? { agent: config.agent } : {},
|
|
38
|
+
...config.runId ? { runId: config.runId } : {}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function writeStatus(event) {
|
|
42
|
+
try {
|
|
43
|
+
process.stdout.write(`${JSON.stringify(event)}
|
|
44
|
+
`);
|
|
45
|
+
} catch {}
|
|
46
|
+
}
|
|
47
|
+
export function registerChildWatchdog(dm, rawConfig = process.env[CHILD_WATCHDOG_CONFIG_ENV]) {
|
|
48
|
+
const childConfig = decodeChildWatchdogConfig(rawConfig);
|
|
49
|
+
if (!childConfig?.enabled)
|
|
50
|
+
return;
|
|
51
|
+
let currentContext;
|
|
52
|
+
let seq = 0;
|
|
53
|
+
const emitStatus = (phase, followUpPending = false, reason) => {
|
|
54
|
+
writeStatus({
|
|
55
|
+
type: CHILD_WATCHDOG_STATUS_EVENT,
|
|
56
|
+
...childConfig.runId ? { runId: childConfig.runId } : {},
|
|
57
|
+
...childConfig.agent ? { agent: childConfig.agent } : {},
|
|
58
|
+
...childConfig.childIndex !== undefined ? { childIndex: childConfig.childIndex, stepIndex: childConfig.childIndex } : {},
|
|
59
|
+
seq: ++seq,
|
|
60
|
+
phase,
|
|
61
|
+
ts: Date.now(),
|
|
62
|
+
followUpPending,
|
|
63
|
+
...reason ? { reason } : {}
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const resolved = childResolvedConfig(childConfig);
|
|
67
|
+
const runtime = new MainWatchdogRuntime({
|
|
68
|
+
resolveConfig: () => ({ ok: true, config: resolved, errors: [], sources: [{ scope: "session", exists: true }] }),
|
|
69
|
+
review: createMainWatchdogReview(() => currentContext, { getThinkingLevel: () => dm.getThinkingLevel() }),
|
|
70
|
+
reviewDescription: "child model review",
|
|
71
|
+
reviewChangesOnly: true,
|
|
72
|
+
displayWarning: (details) => {
|
|
73
|
+
const childDetails = childWarningDetails(details, childConfig);
|
|
74
|
+
dm.sendMessage(createWatchdogWarningMessage(childDetails, { display: true, details: childDetails }));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
const rememberContext = (ctx) => {
|
|
78
|
+
currentContext = ctx;
|
|
79
|
+
};
|
|
80
|
+
const onRuntimeEvent = dm.on;
|
|
81
|
+
onRuntimeEvent("session_start", (_event, ctx) => {
|
|
82
|
+
rememberContext(ctx);
|
|
83
|
+
runtime.bindSession(ctx);
|
|
84
|
+
emitStatus("idle");
|
|
85
|
+
});
|
|
86
|
+
onRuntimeEvent("before_agent_start", (event, ctx) => {
|
|
87
|
+
rememberContext(ctx);
|
|
88
|
+
runtime.handleBeforeAgentStart(event, ctx);
|
|
89
|
+
});
|
|
90
|
+
onRuntimeEvent("turn_end", (event, ctx) => {
|
|
91
|
+
rememberContext(ctx);
|
|
92
|
+
runtime.handleTurnEnd(event, ctx);
|
|
93
|
+
});
|
|
94
|
+
onRuntimeEvent("agent_end", async (event, ctx) => {
|
|
95
|
+
rememberContext(ctx);
|
|
96
|
+
emitStatus("reviewing");
|
|
97
|
+
await runtime.handleAgentEnd(event, ctx);
|
|
98
|
+
const snapshot = runtime.getSnapshot(ctx.cwd);
|
|
99
|
+
if (snapshot.status === "failed")
|
|
100
|
+
emitStatus("failed", false, snapshot.lastError);
|
|
101
|
+
else if (snapshot.status === "stale")
|
|
102
|
+
emitStatus("stale", false, "review stale");
|
|
103
|
+
else
|
|
104
|
+
emitStatus("idle");
|
|
105
|
+
});
|
|
106
|
+
onRuntimeEvent("session_shutdown", () => {
|
|
107
|
+
currentContext = undefined;
|
|
108
|
+
runtime.dispose();
|
|
109
|
+
emitStatus("idle");
|
|
110
|
+
});
|
|
111
|
+
return runtime;
|
|
112
|
+
}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { Text } from "@duckmind/dm-tui";
|
|
2
|
+
import { resolveEffectiveThinking, splitKnownThinkingSuffix, THINKING_LEVELS } from "../shared/model-info.js";
|
|
3
|
+
import { SLASH_TEXT_RESULT_TYPE } from "../shared/types.js";
|
|
4
|
+
import { recommendStrongWatchdogModel, resolveWatchdogModelInput, parseWatchdogThinkingInput } from "./model-selection.js";
|
|
5
|
+
import { renderWatchdogWarning } from "./render.js";
|
|
6
|
+
import { createMainWatchdogReview } from "./review.js";
|
|
7
|
+
import { MainWatchdogRuntime } from "./runtime.js";
|
|
8
|
+
import { getWatchdogUserSettingsPath, writeUserWatchdogEnabled, writeWatchdogModelSettings } from "./settings.js";
|
|
9
|
+
import {
|
|
10
|
+
SUBAGENT_WATCHDOG_WARNING_TYPE
|
|
11
|
+
} from "./types.js";
|
|
12
|
+
import { createWatchdogWarningMessage } from "./warning-format.js";
|
|
13
|
+
function sendSlashText(dm, text) {
|
|
14
|
+
dm.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: true });
|
|
15
|
+
}
|
|
16
|
+
function messageFromError(error) {
|
|
17
|
+
return error instanceof Error ? error.message : String(error);
|
|
18
|
+
}
|
|
19
|
+
function boolLabel(value) {
|
|
20
|
+
return value ? "on" : "off";
|
|
21
|
+
}
|
|
22
|
+
function statusLabel(status) {
|
|
23
|
+
return status.replaceAll("-", " ");
|
|
24
|
+
}
|
|
25
|
+
function sourceLine(source) {
|
|
26
|
+
const location = source.path ? ` ${source.path}` : "";
|
|
27
|
+
return `- ${source.scope}${location}: ${source.exists ? "found" : "not found"}`;
|
|
28
|
+
}
|
|
29
|
+
function currentSessionModelLine(ctx) {
|
|
30
|
+
const model = ctx.model;
|
|
31
|
+
if (model && typeof model.provider === "string" && typeof model.id === "string")
|
|
32
|
+
return `current session (${model.provider}/${model.id})`;
|
|
33
|
+
return "current session (not configured)";
|
|
34
|
+
}
|
|
35
|
+
function mainThinkingLine(snapshot, ctx) {
|
|
36
|
+
const configuredModel = snapshot.config.main.model;
|
|
37
|
+
const configuredThinking = snapshot.config.main.thinking;
|
|
38
|
+
if (configuredModel) {
|
|
39
|
+
const effective = resolveEffectiveThinking(configuredModel, configuredThinking);
|
|
40
|
+
if (effective)
|
|
41
|
+
return effective;
|
|
42
|
+
return "off (default for explicit watchdog model)";
|
|
43
|
+
}
|
|
44
|
+
if (configuredThinking === false)
|
|
45
|
+
return "off";
|
|
46
|
+
if (configuredThinking !== undefined)
|
|
47
|
+
return configuredThinking;
|
|
48
|
+
const currentThinking = ctx.thinkingLevel;
|
|
49
|
+
return typeof currentThinking === "string" ? `current session (${currentThinking})` : "current session";
|
|
50
|
+
}
|
|
51
|
+
function mainModelLine(snapshot, ctx) {
|
|
52
|
+
if (snapshot.config.main.model) {
|
|
53
|
+
const source = snapshot.sessionModelOverride?.model ? "session override" : "configured";
|
|
54
|
+
return `Main model: ${splitKnownThinkingSuffix(snapshot.config.main.model).baseModel} (${source})`;
|
|
55
|
+
}
|
|
56
|
+
return `Main model: ${currentSessionModelLine(ctx)}`;
|
|
57
|
+
}
|
|
58
|
+
function childrenLine(snapshot) {
|
|
59
|
+
const children = snapshot.config.children;
|
|
60
|
+
const model = children.model ? splitKnownThinkingSuffix(children.model).baseModel : "current child session";
|
|
61
|
+
const thinking = children.thinking === undefined ? "current child session" : children.thinking === false ? "off" : children.thinking;
|
|
62
|
+
const overrides = Object.entries(children.overrides);
|
|
63
|
+
const overrideText = overrides.length ? ` · overrides ${overrides.map(([agent, override]) => {
|
|
64
|
+
const bits = [agent];
|
|
65
|
+
if (override.enabled !== undefined)
|
|
66
|
+
bits.push(boolLabel(override.enabled));
|
|
67
|
+
if (override.model)
|
|
68
|
+
bits.push(splitKnownThinkingSuffix(override.model).baseModel);
|
|
69
|
+
if (override.thinking !== undefined)
|
|
70
|
+
bits.push(`thinking ${override.thinking === false ? "off" : override.thinking}`);
|
|
71
|
+
return bits.join(" ");
|
|
72
|
+
}).join("; ")}` : "";
|
|
73
|
+
return `Children: ${boolLabel(snapshot.config.enabled && children.enabled)} · model ${model} · thinking ${thinking}${overrideText}`;
|
|
74
|
+
}
|
|
75
|
+
function recommendationLine(ctx) {
|
|
76
|
+
try {
|
|
77
|
+
const recommendation = recommendStrongWatchdogModel(ctx);
|
|
78
|
+
return `Recommended strong watchdog: ${recommendation.model}:${recommendation.thinking} (${recommendation.label}, complementary reviewer)`;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return `Recommended strong watchdog: unavailable (${messageFromError(error)})`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function lspLine(snapshot) {
|
|
84
|
+
const lsp = snapshot.lsp;
|
|
85
|
+
const provider = lsp.provider ? ` · ${lsp.provider}` : "";
|
|
86
|
+
const counts = lsp.diagnosticCount > 0 || lsp.freshDiagnosticCount > 0 ? ` · ${lsp.freshDiagnosticCount} new/${lsp.diagnosticCount} total` : "";
|
|
87
|
+
const message = lsp.message ? ` · ${lsp.message}` : "";
|
|
88
|
+
return `LSP diagnostics: ${lsp.enabled ? "on" : "off"} · ${lsp.status}${provider}${counts}${message}`;
|
|
89
|
+
}
|
|
90
|
+
export function buildWatchdogStatus(snapshot, ctx) {
|
|
91
|
+
const lines = [
|
|
92
|
+
"Subagent watchdog",
|
|
93
|
+
`Main: ${boolLabel(snapshot.enabled)}${!snapshot.config.enabled && snapshot.sessionOverride === undefined ? " (default off)" : ""}`,
|
|
94
|
+
`Runtime: ${statusLabel(snapshot.status)}${snapshot.bufferedDeltas > 0 ? ` · buffered deltas ${snapshot.bufferedDeltas}` : ""}`,
|
|
95
|
+
`Review trigger: ${snapshot.reviewTrigger === "repo-edits" ? "repo edits only" : "every non-empty turn delta"}`,
|
|
96
|
+
`Scope context: ${snapshot.config.scope.enabled ? "on" : "off"}`,
|
|
97
|
+
`Cadence: ${snapshot.config.cadence.everyNTools === null ? "boundary only" : `every ${snapshot.config.cadence.everyNTools} tools + boundary`}`,
|
|
98
|
+
lspLine(snapshot),
|
|
99
|
+
`Session override: ${snapshot.sessionOverride === undefined ? "none" : boolLabel(snapshot.sessionOverride)}`,
|
|
100
|
+
mainModelLine(snapshot, ctx),
|
|
101
|
+
`Main thinking: ${mainThinkingLine(snapshot, ctx)}`,
|
|
102
|
+
childrenLine(snapshot),
|
|
103
|
+
recommendationLine(ctx),
|
|
104
|
+
`Agent-end timeout: ${snapshot.config.agentEndTimeoutMs}ms`,
|
|
105
|
+
`Auto-follow: ${snapshot.enabled && snapshot.config.autoFollow.blockers ? "on for blockers" : "off"} · attempts ${snapshot.autoFollowAttempts}${snapshot.config.autoFollow.maxAttempts === null ? "" : `/${snapshot.config.autoFollow.maxAttempts}`}${snapshot.autoFollowQueued ? " · queued" : ""}${snapshot.autoFollowStalemate ? " · stalemate" : ""}`,
|
|
106
|
+
`Review model call: ${snapshot.reviewDescription}`
|
|
107
|
+
];
|
|
108
|
+
if (snapshot.failedReviews > 0)
|
|
109
|
+
lines.push(`Failed reviews: ${snapshot.failedReviews}`);
|
|
110
|
+
if (snapshot.staleReviews > 0)
|
|
111
|
+
lines.push(`Stale reviews: ${snapshot.staleReviews}`);
|
|
112
|
+
if (snapshot.changedPaths?.length) {
|
|
113
|
+
lines.push(`Changed paths: ${snapshot.changedPaths.slice(0, 8).join(", ")}${snapshot.changedPaths.length > 8 ? `, +${snapshot.changedPaths.length - 8} more` : ""}`);
|
|
114
|
+
}
|
|
115
|
+
if (snapshot.lastWarning) {
|
|
116
|
+
lines.push(`Last warning: ${snapshot.lastWarning.severity} · ${snapshot.lastWarning.state ?? "candidate"} · ${snapshot.lastWarning.summary}`);
|
|
117
|
+
}
|
|
118
|
+
if (snapshot.lastError)
|
|
119
|
+
lines.push(`Last error: ${snapshot.lastError}`);
|
|
120
|
+
if (!snapshot.configOk) {
|
|
121
|
+
lines.push("", "Config errors:", ...snapshot.errors.map((error) => `- ${error.message}`), "Watchdog is disabled until the config is fixed.");
|
|
122
|
+
} else {
|
|
123
|
+
lines.push("", "Config: ok");
|
|
124
|
+
}
|
|
125
|
+
lines.push("Sources:", ...snapshot.sources.map(sourceLine), "", "Model commands:", "- /subagents-watchdog recommend-model", "- /subagents-watchdog model recommended", "- /subagents-watchdog model <provider/model[:thinking]>", "- /subagents-watchdog model inherit", "- /subagents-watchdog session model recommended", 'Agent action: subagent({ action: "watchdog.configure", model: "recommended", scope: "session" })');
|
|
126
|
+
return lines.join(`
|
|
127
|
+
`);
|
|
128
|
+
}
|
|
129
|
+
function parseTestCommand(input) {
|
|
130
|
+
const match = input.match(/^test\s+(concern|blocker)\s+([\s\S]+)$/);
|
|
131
|
+
if (!match)
|
|
132
|
+
return;
|
|
133
|
+
return { severity: match[1], text: match[2].trim() };
|
|
134
|
+
}
|
|
135
|
+
function formatThinking(value) {
|
|
136
|
+
if (value === undefined)
|
|
137
|
+
return "inherit";
|
|
138
|
+
return value === false ? "off" : value;
|
|
139
|
+
}
|
|
140
|
+
function parseThinkingCommand(raw) {
|
|
141
|
+
const value = raw.trim();
|
|
142
|
+
if (value === "inherit")
|
|
143
|
+
return null;
|
|
144
|
+
return parseWatchdogThinkingInput(value, "/subagents-watchdog thinking") ?? null;
|
|
145
|
+
}
|
|
146
|
+
function resolveModelCommandValue(ctx, raw) {
|
|
147
|
+
const value = raw.trim();
|
|
148
|
+
if (!value)
|
|
149
|
+
throw new Error("Expected a model, 'recommended', or 'inherit'.");
|
|
150
|
+
if (value === "inherit")
|
|
151
|
+
return { model: null, thinking: null, description: "current session model and thinking" };
|
|
152
|
+
if (value === "recommended") {
|
|
153
|
+
const recommendation = recommendStrongWatchdogModel(ctx);
|
|
154
|
+
return {
|
|
155
|
+
model: recommendation.model,
|
|
156
|
+
thinking: recommendation.thinking,
|
|
157
|
+
description: `${recommendation.model}:${recommendation.thinking} (${recommendation.label})`
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const resolved = resolveWatchdogModelInput(ctx, value);
|
|
161
|
+
return {
|
|
162
|
+
model: resolved.model,
|
|
163
|
+
thinking: resolved.thinking ?? null,
|
|
164
|
+
description: `${resolved.model}${resolved.thinking ? `:${resolved.thinking}` : ""}`
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function buildRecommendationText(ctx) {
|
|
168
|
+
const recommendation = recommendStrongWatchdogModel(ctx);
|
|
169
|
+
return [
|
|
170
|
+
"Subagent watchdog recommended model",
|
|
171
|
+
`Current session: ${currentSessionModelLine(ctx)}`,
|
|
172
|
+
`Recommended: ${recommendation.model}:${recommendation.thinking}`,
|
|
173
|
+
`Reason: ${recommendation.reason}`,
|
|
174
|
+
"",
|
|
175
|
+
"Apply for this session:",
|
|
176
|
+
"/subagents-watchdog session model recommended",
|
|
177
|
+
"",
|
|
178
|
+
"Save as your user default:",
|
|
179
|
+
"/subagents-watchdog model recommended"
|
|
180
|
+
].join(`
|
|
181
|
+
`);
|
|
182
|
+
}
|
|
183
|
+
function buildCheckText(runtime, ctx) {
|
|
184
|
+
const snapshot = runtime.getSnapshot(ctx.cwd);
|
|
185
|
+
if (!snapshot.configOk) {
|
|
186
|
+
return ["Subagent watchdog config check", "", "Config errors:", ...snapshot.errors.map((error) => `- ${error.message}`)].join(`
|
|
187
|
+
`);
|
|
188
|
+
}
|
|
189
|
+
const lines = ["Subagent watchdog config check", "", "Config: ok"];
|
|
190
|
+
if (snapshot.config.main.model) {
|
|
191
|
+
const resolved = resolveWatchdogModelInput(ctx, snapshot.config.main.model);
|
|
192
|
+
lines.push(`Main model: ${resolved.model} auth ok`);
|
|
193
|
+
} else {
|
|
194
|
+
lines.push(`Main model: ${currentSessionModelLine(ctx)}`);
|
|
195
|
+
}
|
|
196
|
+
lines.push(`Main thinking: ${mainThinkingLine(snapshot, ctx)}`);
|
|
197
|
+
lines.push(lspLine(snapshot));
|
|
198
|
+
try {
|
|
199
|
+
const recommendation = recommendStrongWatchdogModel(ctx);
|
|
200
|
+
lines.push(`Recommended strong watchdog: ${recommendation.model}:${recommendation.thinking}`);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
lines.push(`Recommended strong watchdog: unavailable (${messageFromError(error)})`);
|
|
203
|
+
}
|
|
204
|
+
return lines.join(`
|
|
205
|
+
`);
|
|
206
|
+
}
|
|
207
|
+
function createTestWarning(severity, text) {
|
|
208
|
+
return {
|
|
209
|
+
severity,
|
|
210
|
+
category: "other",
|
|
211
|
+
confidence: "high",
|
|
212
|
+
source: "main",
|
|
213
|
+
state: "displayed",
|
|
214
|
+
summary: text,
|
|
215
|
+
evidence: `Manual /subagents-watchdog test ${severity} message from the main session.`,
|
|
216
|
+
recommendedAction: severity === "blocker" ? "Verify the renderer, transcript delivery, and auto-follow policy." : "Verify the renderer and transcript delivery; decide manually whether any action is needed."
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async function handleWatchdogCommand(dm, runtime, args, ctx) {
|
|
220
|
+
const input = args.trim();
|
|
221
|
+
if (!input || input === "status") {
|
|
222
|
+
sendSlashText(dm, buildWatchdogStatus(runtime.getSnapshot(ctx.cwd), ctx));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (input === "recommend-model") {
|
|
226
|
+
try {
|
|
227
|
+
sendSlashText(dm, buildRecommendationText(ctx));
|
|
228
|
+
} catch (error) {
|
|
229
|
+
sendSlashText(dm, `Subagent watchdog recommended model
|
|
230
|
+
|
|
231
|
+
${messageFromError(error)}`);
|
|
232
|
+
}
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (input === "check") {
|
|
236
|
+
try {
|
|
237
|
+
sendSlashText(dm, buildCheckText(runtime, ctx));
|
|
238
|
+
} catch (error) {
|
|
239
|
+
sendSlashText(dm, `Subagent watchdog config check
|
|
240
|
+
|
|
241
|
+
${messageFromError(error)}`);
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (input === "on" || input === "off") {
|
|
246
|
+
const enabled = input === "on";
|
|
247
|
+
try {
|
|
248
|
+
const settingsPath = writeUserWatchdogEnabled(enabled);
|
|
249
|
+
const snapshot = runtime.getSnapshot(ctx.cwd);
|
|
250
|
+
sendSlashText(dm, [
|
|
251
|
+
`Subagent watchdog ${boolLabel(enabled)} saved to user settings.`,
|
|
252
|
+
`Updated: ${settingsPath}`,
|
|
253
|
+
`Main now: ${boolLabel(snapshot.enabled)}${snapshot.sessionOverride !== undefined ? ` (session override ${boolLabel(snapshot.sessionOverride)})` : ""}`
|
|
254
|
+
].join(`
|
|
255
|
+
`));
|
|
256
|
+
} catch (error) {
|
|
257
|
+
sendSlashText(dm, `Subagent watchdog
|
|
258
|
+
|
|
259
|
+
Could not update ${getWatchdogUserSettingsPath()}: ${messageFromError(error)}`);
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (input === "session on" || input === "session off") {
|
|
264
|
+
const enabled = input.endsWith("on");
|
|
265
|
+
const snapshot = runtime.setSessionEnabled(enabled, ctx.cwd);
|
|
266
|
+
sendSlashText(dm, [
|
|
267
|
+
`Subagent watchdog session override: ${boolLabel(enabled)}.`,
|
|
268
|
+
"No settings files were changed.",
|
|
269
|
+
"",
|
|
270
|
+
buildWatchdogStatus(snapshot, ctx)
|
|
271
|
+
].join(`
|
|
272
|
+
`));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (input.startsWith("session model ")) {
|
|
276
|
+
const rawModel = input.slice("session model ".length);
|
|
277
|
+
try {
|
|
278
|
+
const value = resolveModelCommandValue(ctx, rawModel);
|
|
279
|
+
const snapshot = value.model === null ? runtime.clearSessionModel(ctx.cwd) : runtime.setSessionModel({ model: value.model, thinking: value.thinking ?? null }, ctx.cwd);
|
|
280
|
+
sendSlashText(dm, [
|
|
281
|
+
`Subagent watchdog session model: ${value.description}.`,
|
|
282
|
+
"No settings files were changed.",
|
|
283
|
+
"",
|
|
284
|
+
buildWatchdogStatus(snapshot, ctx)
|
|
285
|
+
].join(`
|
|
286
|
+
`));
|
|
287
|
+
} catch (error) {
|
|
288
|
+
sendSlashText(dm, `Subagent watchdog session model
|
|
289
|
+
|
|
290
|
+
${messageFromError(error)}`);
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (input.startsWith("model ")) {
|
|
295
|
+
const rawModel = input.slice("model ".length);
|
|
296
|
+
try {
|
|
297
|
+
const value = resolveModelCommandValue(ctx, rawModel);
|
|
298
|
+
const settingsPath = writeWatchdogModelSettings({
|
|
299
|
+
scope: "user",
|
|
300
|
+
target: { kind: "main" },
|
|
301
|
+
model: value.model,
|
|
302
|
+
thinking: value.thinking
|
|
303
|
+
});
|
|
304
|
+
runtime.refreshConfig(ctx.cwd);
|
|
305
|
+
const snapshot = runtime.getSnapshot(ctx.cwd);
|
|
306
|
+
sendSlashText(dm, [
|
|
307
|
+
`Subagent watchdog model saved: ${value.description}.`,
|
|
308
|
+
`Updated: ${settingsPath}`,
|
|
309
|
+
`Main now: ${boolLabel(snapshot.enabled)}`,
|
|
310
|
+
value.model === null ? "The watchdog now inherits the current session model and thinking." : "Run /subagents-watchdog on if the watchdog is still off.",
|
|
311
|
+
"",
|
|
312
|
+
buildWatchdogStatus(snapshot, ctx)
|
|
313
|
+
].join(`
|
|
314
|
+
`));
|
|
315
|
+
} catch (error) {
|
|
316
|
+
sendSlashText(dm, `Subagent watchdog model
|
|
317
|
+
|
|
318
|
+
${messageFromError(error)}
|
|
319
|
+
No settings files were changed.`);
|
|
320
|
+
}
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (input.startsWith("thinking ")) {
|
|
324
|
+
const rawThinking = input.slice("thinking ".length);
|
|
325
|
+
try {
|
|
326
|
+
const thinking = parseThinkingCommand(rawThinking);
|
|
327
|
+
const settingsPath = writeWatchdogModelSettings({
|
|
328
|
+
scope: "user",
|
|
329
|
+
target: { kind: "main" },
|
|
330
|
+
thinking
|
|
331
|
+
});
|
|
332
|
+
runtime.refreshConfig(ctx.cwd);
|
|
333
|
+
sendSlashText(dm, [
|
|
334
|
+
`Subagent watchdog thinking saved: ${formatThinking(thinking ?? undefined)}.`,
|
|
335
|
+
`Updated: ${settingsPath}`,
|
|
336
|
+
"",
|
|
337
|
+
buildWatchdogStatus(runtime.getSnapshot(ctx.cwd), ctx)
|
|
338
|
+
].join(`
|
|
339
|
+
`));
|
|
340
|
+
} catch (error) {
|
|
341
|
+
sendSlashText(dm, `Subagent watchdog thinking
|
|
342
|
+
|
|
343
|
+
${messageFromError(error)}
|
|
344
|
+
No settings files were changed.`);
|
|
345
|
+
}
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const test = parseTestCommand(input);
|
|
349
|
+
if (test) {
|
|
350
|
+
if (!test.text) {
|
|
351
|
+
ctx.ui.notify("Usage: /subagents-watchdog test concern|blocker <text>", "error");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const warning = createTestWarning(test.severity, test.text);
|
|
355
|
+
const details = runtime.recordDisplayedWarning(warning);
|
|
356
|
+
dm.sendMessage(createWatchdogWarningMessage(details, { display: true, details }));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
ctx.ui.notify(`Usage: /subagents-watchdog [status|on|off|session on|session off|recommend-model|model recommended|model <provider/model[:thinking]>|model inherit|thinking ${THINKING_LEVELS.join("|")}|thinking inherit|session model recommended|check|test concern <text>|test blocker <text>]`, "error");
|
|
360
|
+
}
|
|
361
|
+
export function registerMainWatchdog(dm, options = {}) {
|
|
362
|
+
let currentContext;
|
|
363
|
+
const rememberContext = (ctx) => {
|
|
364
|
+
currentContext = ctx;
|
|
365
|
+
};
|
|
366
|
+
const runtime = options.runtime ?? new MainWatchdogRuntime({
|
|
367
|
+
review: options.review ?? createMainWatchdogReview(() => currentContext, { getThinkingLevel: () => dm.getThinkingLevel() }),
|
|
368
|
+
reviewDescription: options.review ? "injected seam" : "real model review",
|
|
369
|
+
reviewChangesOnly: true,
|
|
370
|
+
displayWarning: (details, delivery) => {
|
|
371
|
+
dm.sendMessage(createWatchdogWarningMessage(details, { display: true, details }), delivery?.deliverAs === "steer" ? { deliverAs: "steer" } : undefined);
|
|
372
|
+
},
|
|
373
|
+
sendUserMessage: (message) => dm.sendUserMessage(message)
|
|
374
|
+
});
|
|
375
|
+
dm.registerMessageRenderer(SUBAGENT_WATCHDOG_WARNING_TYPE, (message, renderOptions, theme) => {
|
|
376
|
+
const details = message.details;
|
|
377
|
+
if (!details?.summary || !details.evidence || !details.recommendedAction) {
|
|
378
|
+
const content = typeof message.content === "string" ? message.content : message.content.filter((entry) => entry.type === "text").map((entry) => entry.text).join(`
|
|
379
|
+
`);
|
|
380
|
+
return new Text(content, 0, 0);
|
|
381
|
+
}
|
|
382
|
+
return renderWatchdogWarning(details, renderOptions, theme);
|
|
383
|
+
});
|
|
384
|
+
dm.registerCommand("subagents-watchdog", {
|
|
385
|
+
description: "Show or toggle the default-off subagent watchdog",
|
|
386
|
+
handler: (args, ctx) => {
|
|
387
|
+
rememberContext(ctx);
|
|
388
|
+
return handleWatchdogCommand(dm, runtime, args, ctx);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
dm.on("session_start", (_event, ctx) => {
|
|
392
|
+
rememberContext(ctx);
|
|
393
|
+
runtime.bindSession(ctx);
|
|
394
|
+
});
|
|
395
|
+
dm.on("before_agent_start", (event, ctx) => {
|
|
396
|
+
rememberContext(ctx);
|
|
397
|
+
runtime.handleBeforeAgentStart(event, ctx);
|
|
398
|
+
});
|
|
399
|
+
dm.on("turn_end", (event, ctx) => {
|
|
400
|
+
rememberContext(ctx);
|
|
401
|
+
runtime.handleTurnEnd(event, ctx);
|
|
402
|
+
});
|
|
403
|
+
dm.on("tool_result", (_event, ctx) => {
|
|
404
|
+
rememberContext(ctx);
|
|
405
|
+
runtime.handleToolResult(ctx);
|
|
406
|
+
});
|
|
407
|
+
dm.on("agent_end", (event, ctx) => {
|
|
408
|
+
rememberContext(ctx);
|
|
409
|
+
return runtime.handleAgentEnd(event, ctx);
|
|
410
|
+
});
|
|
411
|
+
dm.on("session_before_switch", () => runtime.reset("session switch", { clearReviewInputSignature: true, clearLspLedger: true, clearScope: true, resetAutoFollow: true }));
|
|
412
|
+
dm.on("session_before_fork", () => runtime.reset("session fork", { clearReviewInputSignature: true, clearLspLedger: true, clearScope: true, resetAutoFollow: true }));
|
|
413
|
+
dm.on("session_compact", () => runtime.reset("session compact", { clearScope: true }));
|
|
414
|
+
dm.on("session_shutdown", () => {
|
|
415
|
+
currentContext = undefined;
|
|
416
|
+
runtime.dispose();
|
|
417
|
+
});
|
|
418
|
+
return runtime;
|
|
419
|
+
}
|