@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,56 @@
|
|
|
1
|
+
export const WATCHDOG_AUTO_FOLLOW_PROMPT_MARKER = Symbol("subagent-watchdog-auto-follow-prompt");
|
|
2
|
+
const MAX_SCOPE_ENTRIES = 8;
|
|
3
|
+
const MAX_SCOPE_ENTRY_CHARS = 2000;
|
|
4
|
+
const MAX_SCOPE_TOTAL_CHARS = 16000;
|
|
5
|
+
|
|
6
|
+
export class WatchdogScopeArtifact {
|
|
7
|
+
entries = [];
|
|
8
|
+
addPrompt(prompt, options = {}) {
|
|
9
|
+
const normalized = prompt.trim();
|
|
10
|
+
if (!normalized)
|
|
11
|
+
return;
|
|
12
|
+
this.entries.push({
|
|
13
|
+
prompt: normalized.length > MAX_SCOPE_ENTRY_CHARS ? normalized.slice(0, MAX_SCOPE_ENTRY_CHARS) : normalized,
|
|
14
|
+
createdAt: options.createdAt ?? new Date().toISOString()
|
|
15
|
+
});
|
|
16
|
+
this.trim();
|
|
17
|
+
}
|
|
18
|
+
reset() {
|
|
19
|
+
this.entries = [];
|
|
20
|
+
}
|
|
21
|
+
snapshot() {
|
|
22
|
+
return this.entries.map((entry) => ({ ...entry }));
|
|
23
|
+
}
|
|
24
|
+
render() {
|
|
25
|
+
if (!this.entries.length)
|
|
26
|
+
return "";
|
|
27
|
+
return [
|
|
28
|
+
"Current scope:",
|
|
29
|
+
"The following real user prompts are the current scope record, newest last. Newer prompts supersede and mutate older prompts: they may add, modify, or remove requirements. Flag work that serves no current scope item as category 'scope-drift'.",
|
|
30
|
+
...this.entries.map((entry, index) => [
|
|
31
|
+
`Scope prompt ${index + 1} (${entry.createdAt}):`,
|
|
32
|
+
entry.prompt
|
|
33
|
+
].join(`
|
|
34
|
+
`))
|
|
35
|
+
].join(`
|
|
36
|
+
|
|
37
|
+
`);
|
|
38
|
+
}
|
|
39
|
+
trim() {
|
|
40
|
+
while (this.entries.length > MAX_SCOPE_ENTRIES)
|
|
41
|
+
this.entries.shift();
|
|
42
|
+
let total = this.entries.reduce((sum, entry) => sum + entry.prompt.length, 0);
|
|
43
|
+
while (this.entries.length > 1 && total > MAX_SCOPE_TOTAL_CHARS) {
|
|
44
|
+
const removed = this.entries.shift();
|
|
45
|
+
if (removed)
|
|
46
|
+
total -= removed.prompt.length;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function isWatchdogAutoFollowPromptEvent(event) {
|
|
51
|
+
return Boolean(event && typeof event === "object" && event[WATCHDOG_AUTO_FOLLOW_PROMPT_MARKER]);
|
|
52
|
+
}
|
|
53
|
+
export function markWatchdogAutoFollowPromptEvent(event) {
|
|
54
|
+
Object.defineProperty(event, WATCHDOG_AUTO_FOLLOW_PROMPT_MARKER, { value: true });
|
|
55
|
+
return event;
|
|
56
|
+
}
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { THINKING_LEVELS } from "../shared/model-info.js";
|
|
4
|
+
import { getAgentDir, getProjectConfigDir } from "../shared/utils.js";
|
|
5
|
+
import {
|
|
6
|
+
WATCHDOG_DELIVERY_MODES,
|
|
7
|
+
WATCHDOG_LATE_WARNING_POLICIES,
|
|
8
|
+
WATCHDOG_WARNING_SEVERITIES
|
|
9
|
+
} from "./types.js";
|
|
10
|
+
export const DEFAULT_WATCHDOG_CONFIG = {
|
|
11
|
+
enabled: false,
|
|
12
|
+
delivery: "held",
|
|
13
|
+
showDuringRun: false,
|
|
14
|
+
syncBacklog: "off",
|
|
15
|
+
agentEndTimeoutMs: 30000,
|
|
16
|
+
lateWarningPolicy: "show-stale-no-autofollow",
|
|
17
|
+
severityThreshold: "concern",
|
|
18
|
+
maxWarnings: null,
|
|
19
|
+
guidance: {
|
|
20
|
+
watchdogMd: true,
|
|
21
|
+
systemPromptPath: null
|
|
22
|
+
},
|
|
23
|
+
autoFollow: {
|
|
24
|
+
blockers: true,
|
|
25
|
+
maxAttempts: 3,
|
|
26
|
+
stalemateRepeats: 3
|
|
27
|
+
},
|
|
28
|
+
scope: {
|
|
29
|
+
enabled: true
|
|
30
|
+
},
|
|
31
|
+
cadence: {
|
|
32
|
+
everyNTools: null
|
|
33
|
+
},
|
|
34
|
+
main: {
|
|
35
|
+
enabled: false
|
|
36
|
+
},
|
|
37
|
+
children: {
|
|
38
|
+
enabled: false,
|
|
39
|
+
watchdogTailTimeoutMs: 120000,
|
|
40
|
+
autoFollow: {
|
|
41
|
+
blockers: true,
|
|
42
|
+
maxAttempts: 3,
|
|
43
|
+
stalemateRepeats: 3
|
|
44
|
+
},
|
|
45
|
+
overrides: {}
|
|
46
|
+
},
|
|
47
|
+
asyncCompletion: {
|
|
48
|
+
enabled: false,
|
|
49
|
+
autoFollowBlockers: false
|
|
50
|
+
},
|
|
51
|
+
lsp: {
|
|
52
|
+
enabled: true,
|
|
53
|
+
timeoutMs: 3000,
|
|
54
|
+
maxFiles: 20,
|
|
55
|
+
maxDiagnostics: 50
|
|
56
|
+
},
|
|
57
|
+
compactAtPercent: 80,
|
|
58
|
+
reviewRetryDelayMs: 1000,
|
|
59
|
+
maxReviewFailures: 3
|
|
60
|
+
};
|
|
61
|
+
const WATCHDOG_FIELDS = new Set([
|
|
62
|
+
"enabled",
|
|
63
|
+
"delivery",
|
|
64
|
+
"showDuringRun",
|
|
65
|
+
"syncBacklog",
|
|
66
|
+
"agentEndTimeoutMs",
|
|
67
|
+
"lateWarningPolicy",
|
|
68
|
+
"severityThreshold",
|
|
69
|
+
"maxWarnings",
|
|
70
|
+
"guidance",
|
|
71
|
+
"autoFollow",
|
|
72
|
+
"scope",
|
|
73
|
+
"cadence",
|
|
74
|
+
"main",
|
|
75
|
+
"children",
|
|
76
|
+
"asyncCompletion",
|
|
77
|
+
"lsp",
|
|
78
|
+
"compactAtPercent",
|
|
79
|
+
"reviewRetryDelayMs",
|
|
80
|
+
"maxReviewFailures"
|
|
81
|
+
]);
|
|
82
|
+
const GUIDANCE_FIELDS = new Set(["watchdogMd", "systemPromptPath"]);
|
|
83
|
+
const AUTO_FOLLOW_FIELDS = new Set(["blockers", "maxAttempts", "stalemateRepeats"]);
|
|
84
|
+
const SCOPE_FIELDS = new Set(["enabled"]);
|
|
85
|
+
const CADENCE_FIELDS = new Set(["everyNTools"]);
|
|
86
|
+
const ENDPOINT_FIELDS = new Set(["enabled", "model", "thinking"]);
|
|
87
|
+
const CHILDREN_FIELDS = new Set(["enabled", "model", "thinking", "watchdogTailTimeoutMs", "autoFollow", "overrides"]);
|
|
88
|
+
const CHILD_OVERRIDE_FIELDS = new Set(["enabled", "model", "thinking"]);
|
|
89
|
+
const ASYNC_COMPLETION_FIELDS = new Set(["enabled", "autoFollowBlockers"]);
|
|
90
|
+
const LSP_FIELDS = new Set(["enabled", "timeoutMs", "maxFiles", "maxDiagnostics"]);
|
|
91
|
+
function cloneDefaultConfig() {
|
|
92
|
+
return {
|
|
93
|
+
...DEFAULT_WATCHDOG_CONFIG,
|
|
94
|
+
guidance: { ...DEFAULT_WATCHDOG_CONFIG.guidance },
|
|
95
|
+
autoFollow: { ...DEFAULT_WATCHDOG_CONFIG.autoFollow },
|
|
96
|
+
scope: { ...DEFAULT_WATCHDOG_CONFIG.scope },
|
|
97
|
+
cadence: { ...DEFAULT_WATCHDOG_CONFIG.cadence },
|
|
98
|
+
main: { ...DEFAULT_WATCHDOG_CONFIG.main },
|
|
99
|
+
children: {
|
|
100
|
+
...DEFAULT_WATCHDOG_CONFIG.children,
|
|
101
|
+
autoFollow: { ...DEFAULT_WATCHDOG_CONFIG.children.autoFollow },
|
|
102
|
+
overrides: {}
|
|
103
|
+
},
|
|
104
|
+
asyncCompletion: { ...DEFAULT_WATCHDOG_CONFIG.asyncCompletion },
|
|
105
|
+
lsp: { ...DEFAULT_WATCHDOG_CONFIG.lsp }
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function isPlainObject(value) {
|
|
109
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
110
|
+
}
|
|
111
|
+
function sourceName(meta) {
|
|
112
|
+
return meta.path ? `'${meta.path}'` : "session override";
|
|
113
|
+
}
|
|
114
|
+
function invalid(meta, field, expected) {
|
|
115
|
+
return new Error(`Watchdog settings in ${sourceName(meta)} have invalid '${field}'; expected ${expected}.`);
|
|
116
|
+
}
|
|
117
|
+
function unknown(meta, field) {
|
|
118
|
+
return new Error(`Watchdog settings in ${sourceName(meta)} have unknown field '${field}'.`);
|
|
119
|
+
}
|
|
120
|
+
function assertKnownFields(input, allowed, fieldPrefix, meta) {
|
|
121
|
+
for (const key of Object.keys(input)) {
|
|
122
|
+
if (!allowed.has(key))
|
|
123
|
+
throw unknown(meta, `${fieldPrefix}.${key}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function parseObject(value, field, meta) {
|
|
127
|
+
if (isPlainObject(value))
|
|
128
|
+
return value;
|
|
129
|
+
throw invalid(meta, field, "an object");
|
|
130
|
+
}
|
|
131
|
+
function parseBoolean(value, field, meta) {
|
|
132
|
+
if (typeof value === "boolean")
|
|
133
|
+
return value;
|
|
134
|
+
throw invalid(meta, field, "a boolean");
|
|
135
|
+
}
|
|
136
|
+
function parseNonEmptyString(value, field, meta) {
|
|
137
|
+
if (typeof value === "string" && value.trim())
|
|
138
|
+
return value.trim();
|
|
139
|
+
throw invalid(meta, field, "a non-empty string");
|
|
140
|
+
}
|
|
141
|
+
function parseThinking(value, field, meta) {
|
|
142
|
+
if (value === false)
|
|
143
|
+
return false;
|
|
144
|
+
if (typeof value === "string" && THINKING_LEVELS.includes(value))
|
|
145
|
+
return value;
|
|
146
|
+
throw invalid(meta, field, `${THINKING_LEVELS.map((level) => `'${level}'`).join(" or ")} or false`);
|
|
147
|
+
}
|
|
148
|
+
function parseInteger(value, field, meta, expected, check) {
|
|
149
|
+
if (typeof value === "number" && Number.isInteger(value) && check(value))
|
|
150
|
+
return value;
|
|
151
|
+
throw invalid(meta, field, expected);
|
|
152
|
+
}
|
|
153
|
+
function parseNullableInteger(value, field, meta, expected, check) {
|
|
154
|
+
if (value === null)
|
|
155
|
+
return null;
|
|
156
|
+
return parseInteger(value, field, meta, expected, check);
|
|
157
|
+
}
|
|
158
|
+
function parseEnum(value, field, meta, values) {
|
|
159
|
+
if (typeof value === "string" && values.includes(value))
|
|
160
|
+
return value;
|
|
161
|
+
throw invalid(meta, field, values.map((item) => `'${item}'`).join(" or "));
|
|
162
|
+
}
|
|
163
|
+
function parseSyncBacklog(value, field, meta) {
|
|
164
|
+
if (value === "off")
|
|
165
|
+
return "off";
|
|
166
|
+
return parseInteger(value, field, meta, "'off' or a positive integer", (candidate) => candidate >= 1);
|
|
167
|
+
}
|
|
168
|
+
function parseGuidancePatch(value, field, meta) {
|
|
169
|
+
const input = parseObject(value, field, meta);
|
|
170
|
+
assertKnownFields(input, GUIDANCE_FIELDS, field, meta);
|
|
171
|
+
const patch = {};
|
|
172
|
+
if ("watchdogMd" in input)
|
|
173
|
+
patch.watchdogMd = parseBoolean(input.watchdogMd, `${field}.watchdogMd`, meta);
|
|
174
|
+
if ("systemPromptPath" in input) {
|
|
175
|
+
patch.systemPromptPath = input.systemPromptPath === null ? null : parseNonEmptyString(input.systemPromptPath, `${field}.systemPromptPath`, meta);
|
|
176
|
+
}
|
|
177
|
+
return patch;
|
|
178
|
+
}
|
|
179
|
+
function parseAutoFollowPatch(value, field, meta) {
|
|
180
|
+
const input = parseObject(value, field, meta);
|
|
181
|
+
assertKnownFields(input, AUTO_FOLLOW_FIELDS, field, meta);
|
|
182
|
+
const patch = {};
|
|
183
|
+
if ("blockers" in input)
|
|
184
|
+
patch.blockers = parseBoolean(input.blockers, `${field}.blockers`, meta);
|
|
185
|
+
if ("maxAttempts" in input) {
|
|
186
|
+
patch.maxAttempts = parseNullableInteger(input.maxAttempts, `${field}.maxAttempts`, meta, "null or a positive integer", (candidate) => candidate >= 1);
|
|
187
|
+
}
|
|
188
|
+
if ("stalemateRepeats" in input) {
|
|
189
|
+
patch.stalemateRepeats = parseInteger(input.stalemateRepeats, `${field}.stalemateRepeats`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
190
|
+
}
|
|
191
|
+
return patch;
|
|
192
|
+
}
|
|
193
|
+
function parseScopePatch(value, field, meta) {
|
|
194
|
+
const input = parseObject(value, field, meta);
|
|
195
|
+
assertKnownFields(input, SCOPE_FIELDS, field, meta);
|
|
196
|
+
const patch = {};
|
|
197
|
+
if ("enabled" in input)
|
|
198
|
+
patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
199
|
+
return patch;
|
|
200
|
+
}
|
|
201
|
+
function parseCadencePatch(value, field, meta) {
|
|
202
|
+
const input = parseObject(value, field, meta);
|
|
203
|
+
assertKnownFields(input, CADENCE_FIELDS, field, meta);
|
|
204
|
+
const patch = {};
|
|
205
|
+
if ("everyNTools" in input) {
|
|
206
|
+
patch.everyNTools = input.everyNTools === null ? null : parseInteger(input.everyNTools, `${field}.everyNTools`, meta, "null or an integer >= 5", (candidate) => candidate >= 5);
|
|
207
|
+
}
|
|
208
|
+
return patch;
|
|
209
|
+
}
|
|
210
|
+
function parseEndpointPatch(value, field, meta) {
|
|
211
|
+
const input = parseObject(value, field, meta);
|
|
212
|
+
assertKnownFields(input, ENDPOINT_FIELDS, field, meta);
|
|
213
|
+
const patch = {};
|
|
214
|
+
if ("enabled" in input)
|
|
215
|
+
patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
216
|
+
if ("model" in input)
|
|
217
|
+
patch.model = parseNonEmptyString(input.model, `${field}.model`, meta);
|
|
218
|
+
if ("thinking" in input)
|
|
219
|
+
patch.thinking = parseThinking(input.thinking, `${field}.thinking`, meta);
|
|
220
|
+
return patch;
|
|
221
|
+
}
|
|
222
|
+
function parseChildOverridePatch(value, field, meta) {
|
|
223
|
+
const input = parseObject(value, field, meta);
|
|
224
|
+
assertKnownFields(input, CHILD_OVERRIDE_FIELDS, field, meta);
|
|
225
|
+
const patch = {};
|
|
226
|
+
if ("enabled" in input)
|
|
227
|
+
patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
228
|
+
if ("model" in input)
|
|
229
|
+
patch.model = parseNonEmptyString(input.model, `${field}.model`, meta);
|
|
230
|
+
if ("thinking" in input)
|
|
231
|
+
patch.thinking = parseThinking(input.thinking, `${field}.thinking`, meta);
|
|
232
|
+
return patch;
|
|
233
|
+
}
|
|
234
|
+
function parseChildrenPatch(value, field, meta) {
|
|
235
|
+
const input = parseObject(value, field, meta);
|
|
236
|
+
assertKnownFields(input, CHILDREN_FIELDS, field, meta);
|
|
237
|
+
const patch = {};
|
|
238
|
+
if ("enabled" in input)
|
|
239
|
+
patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
240
|
+
if ("model" in input)
|
|
241
|
+
patch.model = parseNonEmptyString(input.model, `${field}.model`, meta);
|
|
242
|
+
if ("thinking" in input)
|
|
243
|
+
patch.thinking = parseThinking(input.thinking, `${field}.thinking`, meta);
|
|
244
|
+
if ("watchdogTailTimeoutMs" in input) {
|
|
245
|
+
patch.watchdogTailTimeoutMs = parseInteger(input.watchdogTailTimeoutMs, `${field}.watchdogTailTimeoutMs`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
246
|
+
}
|
|
247
|
+
if ("autoFollow" in input)
|
|
248
|
+
patch.autoFollow = parseAutoFollowPatch(input.autoFollow, `${field}.autoFollow`, meta);
|
|
249
|
+
if ("overrides" in input) {
|
|
250
|
+
const overrides = parseObject(input.overrides, `${field}.overrides`, meta);
|
|
251
|
+
patch.overrides = {};
|
|
252
|
+
for (const [agent, override] of Object.entries(overrides)) {
|
|
253
|
+
if (!agent.trim())
|
|
254
|
+
throw invalid(meta, `${field}.overrides`, "agent names to be non-empty");
|
|
255
|
+
patch.overrides[agent] = parseChildOverridePatch(override, `${field}.overrides.${agent}`, meta);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return patch;
|
|
259
|
+
}
|
|
260
|
+
function parseAsyncCompletionPatch(value, field, meta) {
|
|
261
|
+
const input = parseObject(value, field, meta);
|
|
262
|
+
assertKnownFields(input, ASYNC_COMPLETION_FIELDS, field, meta);
|
|
263
|
+
const patch = {};
|
|
264
|
+
if ("enabled" in input)
|
|
265
|
+
patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
266
|
+
if ("autoFollowBlockers" in input)
|
|
267
|
+
patch.autoFollowBlockers = parseBoolean(input.autoFollowBlockers, `${field}.autoFollowBlockers`, meta);
|
|
268
|
+
return patch;
|
|
269
|
+
}
|
|
270
|
+
function parseLspPatch(value, field, meta) {
|
|
271
|
+
const input = parseObject(value, field, meta);
|
|
272
|
+
assertKnownFields(input, LSP_FIELDS, field, meta);
|
|
273
|
+
const patch = {};
|
|
274
|
+
if ("enabled" in input)
|
|
275
|
+
patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
276
|
+
if ("timeoutMs" in input)
|
|
277
|
+
patch.timeoutMs = parseInteger(input.timeoutMs, `${field}.timeoutMs`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
278
|
+
if ("maxFiles" in input)
|
|
279
|
+
patch.maxFiles = parseInteger(input.maxFiles, `${field}.maxFiles`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
280
|
+
if ("maxDiagnostics" in input)
|
|
281
|
+
patch.maxDiagnostics = parseInteger(input.maxDiagnostics, `${field}.maxDiagnostics`, meta, "a non-negative integer", (candidate) => candidate >= 0);
|
|
282
|
+
return patch;
|
|
283
|
+
}
|
|
284
|
+
function parseWatchdogPatch(value, field, meta) {
|
|
285
|
+
const input = parseObject(value, field, meta);
|
|
286
|
+
assertKnownFields(input, WATCHDOG_FIELDS, field, meta);
|
|
287
|
+
const patch = {};
|
|
288
|
+
if ("enabled" in input)
|
|
289
|
+
patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
290
|
+
if ("delivery" in input)
|
|
291
|
+
patch.delivery = parseEnum(input.delivery, `${field}.delivery`, meta, WATCHDOG_DELIVERY_MODES);
|
|
292
|
+
if ("showDuringRun" in input)
|
|
293
|
+
patch.showDuringRun = parseBoolean(input.showDuringRun, `${field}.showDuringRun`, meta);
|
|
294
|
+
if ("syncBacklog" in input)
|
|
295
|
+
patch.syncBacklog = parseSyncBacklog(input.syncBacklog, `${field}.syncBacklog`, meta);
|
|
296
|
+
if ("agentEndTimeoutMs" in input) {
|
|
297
|
+
patch.agentEndTimeoutMs = parseInteger(input.agentEndTimeoutMs, `${field}.agentEndTimeoutMs`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
298
|
+
}
|
|
299
|
+
if ("lateWarningPolicy" in input) {
|
|
300
|
+
patch.lateWarningPolicy = parseEnum(input.lateWarningPolicy, `${field}.lateWarningPolicy`, meta, WATCHDOG_LATE_WARNING_POLICIES);
|
|
301
|
+
}
|
|
302
|
+
if ("severityThreshold" in input) {
|
|
303
|
+
patch.severityThreshold = parseEnum(input.severityThreshold, `${field}.severityThreshold`, meta, WATCHDOG_WARNING_SEVERITIES);
|
|
304
|
+
}
|
|
305
|
+
if ("maxWarnings" in input) {
|
|
306
|
+
patch.maxWarnings = parseNullableInteger(input.maxWarnings, `${field}.maxWarnings`, meta, "null or a non-negative integer", (candidate) => candidate >= 0);
|
|
307
|
+
}
|
|
308
|
+
if ("guidance" in input)
|
|
309
|
+
patch.guidance = parseGuidancePatch(input.guidance, `${field}.guidance`, meta);
|
|
310
|
+
if ("autoFollow" in input)
|
|
311
|
+
patch.autoFollow = parseAutoFollowPatch(input.autoFollow, `${field}.autoFollow`, meta);
|
|
312
|
+
if ("scope" in input)
|
|
313
|
+
patch.scope = parseScopePatch(input.scope, `${field}.scope`, meta);
|
|
314
|
+
if ("cadence" in input)
|
|
315
|
+
patch.cadence = parseCadencePatch(input.cadence, `${field}.cadence`, meta);
|
|
316
|
+
if ("main" in input)
|
|
317
|
+
patch.main = parseEndpointPatch(input.main, `${field}.main`, meta);
|
|
318
|
+
if ("children" in input)
|
|
319
|
+
patch.children = parseChildrenPatch(input.children, `${field}.children`, meta);
|
|
320
|
+
if ("asyncCompletion" in input)
|
|
321
|
+
patch.asyncCompletion = parseAsyncCompletionPatch(input.asyncCompletion, `${field}.asyncCompletion`, meta);
|
|
322
|
+
if ("lsp" in input)
|
|
323
|
+
patch.lsp = parseLspPatch(input.lsp, `${field}.lsp`, meta);
|
|
324
|
+
if ("compactAtPercent" in input) {
|
|
325
|
+
patch.compactAtPercent = parseInteger(input.compactAtPercent, `${field}.compactAtPercent`, meta, "an integer from 50 through 95", (candidate) => candidate >= 50 && candidate <= 95);
|
|
326
|
+
}
|
|
327
|
+
if ("reviewRetryDelayMs" in input) {
|
|
328
|
+
patch.reviewRetryDelayMs = parseInteger(input.reviewRetryDelayMs, `${field}.reviewRetryDelayMs`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
329
|
+
}
|
|
330
|
+
if ("maxReviewFailures" in input) {
|
|
331
|
+
patch.maxReviewFailures = parseInteger(input.maxReviewFailures, `${field}.maxReviewFailures`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
332
|
+
}
|
|
333
|
+
return patch;
|
|
334
|
+
}
|
|
335
|
+
function parseSettingsObject(settings, meta) {
|
|
336
|
+
if (!("subagents" in settings))
|
|
337
|
+
return {};
|
|
338
|
+
const subagents = parseObject(settings.subagents, "subagents", meta);
|
|
339
|
+
if (!("watchdog" in subagents))
|
|
340
|
+
return {};
|
|
341
|
+
return parseWatchdogPatch(subagents.watchdog, "subagents.watchdog", meta);
|
|
342
|
+
}
|
|
343
|
+
function readSettingsFileStrict(filePath) {
|
|
344
|
+
if (!fs.existsSync(filePath))
|
|
345
|
+
return {};
|
|
346
|
+
let raw;
|
|
347
|
+
try {
|
|
348
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
349
|
+
} catch (error) {
|
|
350
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
351
|
+
throw new Error(`Failed to read settings file '${filePath}': ${message}`, { cause: error });
|
|
352
|
+
}
|
|
353
|
+
let parsed;
|
|
354
|
+
try {
|
|
355
|
+
parsed = JSON.parse(raw);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
358
|
+
throw new Error(`Failed to parse settings file '${filePath}': ${message}`, { cause: error });
|
|
359
|
+
}
|
|
360
|
+
if (!isPlainObject(parsed)) {
|
|
361
|
+
throw new Error(`Settings file '${filePath}' must contain a JSON object.`);
|
|
362
|
+
}
|
|
363
|
+
return parsed;
|
|
364
|
+
}
|
|
365
|
+
function isDirectory(dir) {
|
|
366
|
+
try {
|
|
367
|
+
return fs.statSync(dir).isDirectory();
|
|
368
|
+
} catch {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function getUserSettingsPath() {
|
|
373
|
+
return path.join(getAgentDir(), "settings.json");
|
|
374
|
+
}
|
|
375
|
+
export function getWatchdogUserSettingsPath() {
|
|
376
|
+
return getUserSettingsPath();
|
|
377
|
+
}
|
|
378
|
+
function getProjectSettingsPath(cwd) {
|
|
379
|
+
let currentDir = cwd;
|
|
380
|
+
while (true) {
|
|
381
|
+
if (isDirectory(getProjectConfigDir(currentDir)) || isDirectory(path.join(currentDir, ".agents"))) {
|
|
382
|
+
return path.join(getProjectConfigDir(currentDir), "settings.json");
|
|
383
|
+
}
|
|
384
|
+
const parentDir = path.dirname(currentDir);
|
|
385
|
+
if (parentDir === currentDir)
|
|
386
|
+
return;
|
|
387
|
+
currentDir = parentDir;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
export function getWatchdogProjectSettingsPath(cwd) {
|
|
391
|
+
return path.join(getProjectConfigDir(cwd), "settings.json");
|
|
392
|
+
}
|
|
393
|
+
function deepMerge(base, patch) {
|
|
394
|
+
const next = { ...base };
|
|
395
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
396
|
+
const current = next[key];
|
|
397
|
+
next[key] = isPlainObject(current) && isPlainObject(value) ? deepMerge(current, value) : value;
|
|
398
|
+
}
|
|
399
|
+
return next;
|
|
400
|
+
}
|
|
401
|
+
function resolvePatch(patch) {
|
|
402
|
+
const config = deepMerge(cloneDefaultConfig(), patch);
|
|
403
|
+
config.enabled = patch.enabled ?? DEFAULT_WATCHDOG_CONFIG.enabled;
|
|
404
|
+
config.main.enabled = patch.main?.enabled ?? config.enabled;
|
|
405
|
+
return config;
|
|
406
|
+
}
|
|
407
|
+
function parseSourceFile(filePath, scope) {
|
|
408
|
+
return parseSettingsObject(readSettingsFileStrict(filePath), { scope, path: filePath });
|
|
409
|
+
}
|
|
410
|
+
function parseSessionOverride(value) {
|
|
411
|
+
if ("subagents" in value)
|
|
412
|
+
return parseSettingsObject(value, { scope: "session" });
|
|
413
|
+
return parseWatchdogPatch(value, "subagents.watchdog", { scope: "session" });
|
|
414
|
+
}
|
|
415
|
+
export function resolveWatchdogConfigStrict(cwd, options = {}) {
|
|
416
|
+
let patch = {};
|
|
417
|
+
patch = deepMerge(patch, parseSourceFile(getUserSettingsPath(), "user"));
|
|
418
|
+
const projectSettingsPath = getProjectSettingsPath(cwd);
|
|
419
|
+
if (projectSettingsPath) {
|
|
420
|
+
patch = deepMerge(patch, parseSourceFile(projectSettingsPath, "project"));
|
|
421
|
+
}
|
|
422
|
+
if (options.session) {
|
|
423
|
+
patch = deepMerge(patch, parseSessionOverride(options.session));
|
|
424
|
+
}
|
|
425
|
+
return resolvePatch(patch);
|
|
426
|
+
}
|
|
427
|
+
function ensureObjectField(parent, key, field, meta) {
|
|
428
|
+
if (!(key in parent))
|
|
429
|
+
parent[key] = {};
|
|
430
|
+
if (!isPlainObject(parent[key]))
|
|
431
|
+
throw invalid(meta, field, "an object");
|
|
432
|
+
return parent[key];
|
|
433
|
+
}
|
|
434
|
+
function ensureWatchdogSettings(settings, meta) {
|
|
435
|
+
const subagents = ensureObjectField(settings, "subagents", "subagents", meta);
|
|
436
|
+
return ensureObjectField(subagents, "watchdog", "subagents.watchdog", meta);
|
|
437
|
+
}
|
|
438
|
+
function settingsPathForWrite(scope, cwd) {
|
|
439
|
+
return scope === "user" ? getUserSettingsPath() : getWatchdogProjectSettingsPath(cwd ?? process.cwd());
|
|
440
|
+
}
|
|
441
|
+
function targetSettingsObject(watchdog, target, meta) {
|
|
442
|
+
if (target.kind === "main")
|
|
443
|
+
return ensureObjectField(watchdog, "main", "subagents.watchdog.main", meta);
|
|
444
|
+
const children = ensureObjectField(watchdog, "children", "subagents.watchdog.children", meta);
|
|
445
|
+
if (target.kind === "children")
|
|
446
|
+
return children;
|
|
447
|
+
if (!target.agent.trim())
|
|
448
|
+
throw invalid(meta, "subagents.watchdog.children.overrides.<agent>", "a non-empty agent name");
|
|
449
|
+
const overrides = ensureObjectField(children, "overrides", "subagents.watchdog.children.overrides", meta);
|
|
450
|
+
return ensureObjectField(overrides, target.agent.trim(), `subagents.watchdog.children.overrides.${target.agent.trim()}`, meta);
|
|
451
|
+
}
|
|
452
|
+
function writeSettingsFile(settingsPath, settings) {
|
|
453
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
454
|
+
fs.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
455
|
+
`, "utf-8");
|
|
456
|
+
return settingsPath;
|
|
457
|
+
}
|
|
458
|
+
export function writeUserWatchdogEnabled(enabled) {
|
|
459
|
+
const settingsPath = getUserSettingsPath();
|
|
460
|
+
const meta = { scope: "user", path: settingsPath };
|
|
461
|
+
const settings = readSettingsFileStrict(settingsPath);
|
|
462
|
+
const watchdog = ensureWatchdogSettings(settings, meta);
|
|
463
|
+
watchdog.enabled = enabled;
|
|
464
|
+
targetSettingsObject(watchdog, { kind: "main" }, meta).enabled = enabled;
|
|
465
|
+
return writeSettingsFile(settingsPath, settings);
|
|
466
|
+
}
|
|
467
|
+
export function writeWatchdogModelSettings(input) {
|
|
468
|
+
const settingsPath = settingsPathForWrite(input.scope, input.cwd);
|
|
469
|
+
const meta = { scope: input.scope, path: settingsPath };
|
|
470
|
+
const settings = readSettingsFileStrict(settingsPath);
|
|
471
|
+
const watchdog = ensureWatchdogSettings(settings, meta);
|
|
472
|
+
const target = targetSettingsObject(watchdog, input.target, meta);
|
|
473
|
+
if (input.model === null)
|
|
474
|
+
delete target.model;
|
|
475
|
+
else if (input.model !== undefined)
|
|
476
|
+
target.model = input.model;
|
|
477
|
+
if (input.thinking === null)
|
|
478
|
+
delete target.thinking;
|
|
479
|
+
else if (input.thinking !== undefined)
|
|
480
|
+
target.thinking = input.thinking;
|
|
481
|
+
return writeSettingsFile(settingsPath, settings);
|
|
482
|
+
}
|
|
483
|
+
export function resolveWatchdogConfig(cwd, options = {}) {
|
|
484
|
+
const sources = [];
|
|
485
|
+
const errors = [];
|
|
486
|
+
let patch = {};
|
|
487
|
+
const sourceSpecs = [
|
|
488
|
+
{ scope: "user", path: getUserSettingsPath() },
|
|
489
|
+
{ scope: "project", path: getProjectSettingsPath(cwd) }
|
|
490
|
+
];
|
|
491
|
+
for (const source of sourceSpecs) {
|
|
492
|
+
if (!source.path)
|
|
493
|
+
continue;
|
|
494
|
+
sources.push({ scope: source.scope, path: source.path, exists: fs.existsSync(source.path) });
|
|
495
|
+
try {
|
|
496
|
+
patch = deepMerge(patch, parseSourceFile(source.path, source.scope));
|
|
497
|
+
} catch (error) {
|
|
498
|
+
errors.push({ scope: source.scope, path: source.path, message: error instanceof Error ? error.message : String(error) });
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (options.session) {
|
|
502
|
+
sources.push({ scope: "session", exists: true });
|
|
503
|
+
try {
|
|
504
|
+
patch = deepMerge(patch, parseSessionOverride(options.session));
|
|
505
|
+
} catch (error) {
|
|
506
|
+
errors.push({ scope: "session", message: error instanceof Error ? error.message : String(error) });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
ok: errors.length === 0,
|
|
511
|
+
config: errors.length === 0 ? resolvePatch(patch) : cloneDefaultConfig(),
|
|
512
|
+
errors,
|
|
513
|
+
sources
|
|
514
|
+
};
|
|
515
|
+
}
|