@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,220 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { PROJECT_SUBAGENTS_RELATIVE_DIR } from "../shared/artifacts.js";
|
|
7
|
+
const IGNORED_CHANGE_PREFIXES = [`${PROJECT_SUBAGENTS_RELATIVE_DIR}/`, "tmp/", "node_modules/"];
|
|
8
|
+
const IGNORED_CHANGE_PATHS = new Set([PROJECT_SUBAGENTS_RELATIVE_DIR, "tmp", "node_modules"]);
|
|
9
|
+
const IGNORED_CHANGE_SEGMENTS = new Set([".git", "node_modules"]);
|
|
10
|
+
const DEFAULT_MAX_HASH_FILE_BYTES = 64 * 1024 * 1024;
|
|
11
|
+
const DEFAULT_MAX_HASH_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
12
|
+
const DEFAULT_MAX_HASH_ENTRIES = 2000;
|
|
13
|
+
const TRACKED_ENTRY_PROBE_MAX_BYTES = 1024;
|
|
14
|
+
function positiveEnvNumber(name, fallback) {
|
|
15
|
+
const parsed = Number(process.env[name]);
|
|
16
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
17
|
+
}
|
|
18
|
+
function maxHashFileBytes() {
|
|
19
|
+
return positiveEnvNumber("DM_SUBAGENTS_MAX_HASH_FILE_BYTES", DEFAULT_MAX_HASH_FILE_BYTES);
|
|
20
|
+
}
|
|
21
|
+
function maxHashTotalBytes() {
|
|
22
|
+
return positiveEnvNumber("DM_SUBAGENTS_MAX_HASH_TOTAL_BYTES", DEFAULT_MAX_HASH_TOTAL_BYTES);
|
|
23
|
+
}
|
|
24
|
+
function maxHashEntries() {
|
|
25
|
+
return positiveEnvNumber("DM_SUBAGENTS_MAX_HASH_ENTRIES", DEFAULT_MAX_HASH_ENTRIES);
|
|
26
|
+
}
|
|
27
|
+
function git(cwd, args) {
|
|
28
|
+
const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8", maxBuffer: 10 * 1024 * 1024, windowsHide: true });
|
|
29
|
+
if (result.status !== 0)
|
|
30
|
+
return;
|
|
31
|
+
return result.stdout;
|
|
32
|
+
}
|
|
33
|
+
function comparablePath(value) {
|
|
34
|
+
let resolved = path.resolve(value);
|
|
35
|
+
try {
|
|
36
|
+
resolved = fs.realpathSync.native(resolved);
|
|
37
|
+
} catch {}
|
|
38
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
39
|
+
}
|
|
40
|
+
function isHomeRepoRoot(root) {
|
|
41
|
+
return comparablePath(root) === comparablePath(os.homedir());
|
|
42
|
+
}
|
|
43
|
+
function hasTrackedEntries(root) {
|
|
44
|
+
const result = spawnSync("git", ["-C", root, "ls-files", "--cached", "--error-unmatch", "--", "."], {
|
|
45
|
+
encoding: "utf-8",
|
|
46
|
+
maxBuffer: TRACKED_ENTRY_PROBE_MAX_BYTES,
|
|
47
|
+
windowsHide: true
|
|
48
|
+
});
|
|
49
|
+
const errorCode = result.error?.code;
|
|
50
|
+
if (result.status === 0 || errorCode === "ENOBUFS")
|
|
51
|
+
return true;
|
|
52
|
+
if (result.status === 1)
|
|
53
|
+
return false;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
function normalizeRelPath(value) {
|
|
57
|
+
return value.replaceAll(path.sep, "/").replace(/^\.\//, "");
|
|
58
|
+
}
|
|
59
|
+
function ignoredRelPath(relPath) {
|
|
60
|
+
const normalized = normalizeRelPath(relPath);
|
|
61
|
+
return IGNORED_CHANGE_PATHS.has(normalized) || IGNORED_CHANGE_PREFIXES.some((prefix) => normalized.startsWith(prefix)) || normalized.split("/").some((segment) => IGNORED_CHANGE_SEGMENTS.has(segment));
|
|
62
|
+
}
|
|
63
|
+
function useHashEntryBudget(budget) {
|
|
64
|
+
if (budget.entries >= budget.maxEntries)
|
|
65
|
+
return false;
|
|
66
|
+
budget.entries++;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
function hashFile(filePath) {
|
|
70
|
+
return createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
71
|
+
}
|
|
72
|
+
function largeFileHash(stat) {
|
|
73
|
+
return "large:" + stat.size + ":" + Math.floor(stat.mtimeMs);
|
|
74
|
+
}
|
|
75
|
+
function hashFileEntry(normalized, fullPath, stat, budget) {
|
|
76
|
+
let hash;
|
|
77
|
+
if (stat.size > maxHashFileBytes() || budget.bytes + stat.size > budget.maxBytes) {
|
|
78
|
+
hash = largeFileHash(stat);
|
|
79
|
+
} else {
|
|
80
|
+
try {
|
|
81
|
+
hash = hashFile(fullPath);
|
|
82
|
+
budget.bytes += stat.size;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const code = error.code;
|
|
85
|
+
if (code === "ENOENT")
|
|
86
|
+
return { path: normalized, state: "deleted" };
|
|
87
|
+
hash = largeFileHash(stat);
|
|
88
|
+
if (code !== "ERR_FS_FILE_TOO_LARGE") {
|
|
89
|
+
console.warn("[dm-subagents] watchdog hashFile fell back to metadata for", normalized + ":", error?.message);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { path: normalized, state: "file", mode: stat.mode & 511, size: stat.size, hash };
|
|
94
|
+
}
|
|
95
|
+
function gitWorktreeEntry(normalized, fullPath) {
|
|
96
|
+
const status = git(fullPath, ["status", "--porcelain=v1", "-z", "--untracked-files=no"]);
|
|
97
|
+
return {
|
|
98
|
+
path: normalized,
|
|
99
|
+
state: "git-worktree",
|
|
100
|
+
head: git(fullPath, ["rev-parse", "HEAD"])?.trim(),
|
|
101
|
+
dirty: Boolean(status),
|
|
102
|
+
statusKey: status ? createHash("sha256").update(status).digest("hex") : undefined
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function hashPath(root, relPath, budget) {
|
|
106
|
+
const normalized = normalizeRelPath(relPath);
|
|
107
|
+
if (!useHashEntryBudget(budget))
|
|
108
|
+
return { path: normalized, state: "skipped", reason: "entry-limit" };
|
|
109
|
+
const fullPath = path.join(root, normalized);
|
|
110
|
+
let stat;
|
|
111
|
+
try {
|
|
112
|
+
stat = fs.lstatSync(fullPath);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error.code === "ENOENT")
|
|
115
|
+
return { path: normalized, state: "deleted" };
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
if (stat.isSymbolicLink()) {
|
|
119
|
+
return { path: normalized, state: "symlink", target: fs.readlinkSync(fullPath) };
|
|
120
|
+
}
|
|
121
|
+
if (stat.isDirectory()) {
|
|
122
|
+
if (fs.existsSync(path.join(fullPath, ".git")))
|
|
123
|
+
return gitWorktreeEntry(normalized, fullPath);
|
|
124
|
+
const entries = fs.readdirSync(fullPath).map((entry) => normalizeRelPath(path.posix.join(normalized, entry))).filter((entry) => !ignoredRelPath(entry)).sort();
|
|
125
|
+
const remainingEntries = Math.max(0, budget.maxEntries - budget.entries);
|
|
126
|
+
const selectedEntries = entries.slice(0, remainingEntries);
|
|
127
|
+
const childEntries = selectedEntries.map((entry) => hashPath(root, entry, budget));
|
|
128
|
+
if (selectedEntries.length < entries.length) {
|
|
129
|
+
childEntries.push({ path: normalized, state: "skipped-children", reason: "entry-limit", count: entries.length - selectedEntries.length });
|
|
130
|
+
}
|
|
131
|
+
return { path: normalized, state: "dir", entries: childEntries };
|
|
132
|
+
}
|
|
133
|
+
if (stat.isFile())
|
|
134
|
+
return hashFileEntry(normalized, fullPath, stat, budget);
|
|
135
|
+
return { path: normalized, state: "other", mode: stat.mode };
|
|
136
|
+
}
|
|
137
|
+
function parsePorcelainZ(raw) {
|
|
138
|
+
const tokens = raw.split("\x00").filter(Boolean);
|
|
139
|
+
const entries = [];
|
|
140
|
+
for (let index = 0;index < tokens.length; index++) {
|
|
141
|
+
const token = tokens[index];
|
|
142
|
+
if (token.length < 4)
|
|
143
|
+
continue;
|
|
144
|
+
const status = token.slice(0, 2);
|
|
145
|
+
const relPath = token.slice(3);
|
|
146
|
+
const paths = [relPath];
|
|
147
|
+
if (status[0] === "R" || status[0] === "C") {
|
|
148
|
+
const originalPath = tokens[++index];
|
|
149
|
+
if (originalPath)
|
|
150
|
+
paths.push(originalPath);
|
|
151
|
+
}
|
|
152
|
+
entries.push({ status, paths });
|
|
153
|
+
}
|
|
154
|
+
return entries;
|
|
155
|
+
}
|
|
156
|
+
function buildRepoChangeSignature(root, statusOutput) {
|
|
157
|
+
const entries = parsePorcelainZ(statusOutput).map((entry) => ({
|
|
158
|
+
status: entry.status,
|
|
159
|
+
paths: entry.paths.map(normalizeRelPath).filter((relPath) => !ignoredRelPath(relPath))
|
|
160
|
+
})).filter((entry) => entry.paths.length > 0).sort((a, b) => `${a.status} ${a.paths.join("\x00")}`.localeCompare(`${b.status} ${b.paths.join("\x00")}`));
|
|
161
|
+
const changedPaths = [...new Set(entries.flatMap((entry) => entry.paths))].sort();
|
|
162
|
+
const budget = { entries: 0, bytes: 0, maxEntries: maxHashEntries(), maxBytes: maxHashTotalBytes() };
|
|
163
|
+
const payload = entries.map((entry) => ({
|
|
164
|
+
status: entry.status,
|
|
165
|
+
paths: entry.paths,
|
|
166
|
+
content: entry.paths.map((relPath) => hashPath(root, relPath, budget))
|
|
167
|
+
}));
|
|
168
|
+
const key = createHash("sha256").update(JSON.stringify(payload)).digest("hex");
|
|
169
|
+
return { root, key, changedPaths };
|
|
170
|
+
}
|
|
171
|
+
export function computeWatchdogRepoChangeSignature(cwd) {
|
|
172
|
+
const root = git(cwd, ["rev-parse", "--show-toplevel"])?.trim();
|
|
173
|
+
if (!root)
|
|
174
|
+
return;
|
|
175
|
+
const skipUntracked = isHomeRepoRoot(root) || hasTrackedEntries(root) === false;
|
|
176
|
+
const statusOutput = git(root, [
|
|
177
|
+
"status",
|
|
178
|
+
"--porcelain=v1",
|
|
179
|
+
"-z",
|
|
180
|
+
`--untracked-files=${skipUntracked ? "no" : "all"}`
|
|
181
|
+
]);
|
|
182
|
+
if (statusOutput === undefined)
|
|
183
|
+
return;
|
|
184
|
+
try {
|
|
185
|
+
return buildRepoChangeSignature(root, statusOutput);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.warn("[dm-subagents] watchdog repo change signature failed:", error?.message);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function toolNameFromMessage(message) {
|
|
192
|
+
const value = message.toolName ?? message.name;
|
|
193
|
+
return typeof value === "string" ? value : "";
|
|
194
|
+
}
|
|
195
|
+
function toolResultSucceeded(message) {
|
|
196
|
+
return message.isError !== true && message.error === undefined;
|
|
197
|
+
}
|
|
198
|
+
function messageIndicatesRepoEdit(message) {
|
|
199
|
+
if (!message || typeof message !== "object")
|
|
200
|
+
return false;
|
|
201
|
+
const input = message;
|
|
202
|
+
const role = input.role;
|
|
203
|
+
if (role !== "toolResult" && role !== "tool")
|
|
204
|
+
return false;
|
|
205
|
+
const toolName = toolNameFromMessage(input);
|
|
206
|
+
return (toolName === "edit" || toolName === "write") && toolResultSucceeded(input);
|
|
207
|
+
}
|
|
208
|
+
export function eventIndicatesRepoEdit(event) {
|
|
209
|
+
if (!event || typeof event !== "object")
|
|
210
|
+
return false;
|
|
211
|
+
const input = event;
|
|
212
|
+
if (input.type === "turn_end" || input.event === "turn_end") {
|
|
213
|
+
return [input.message, ...Array.isArray(input.toolResults) ? input.toolResults : []].some(messageIndicatesRepoEdit);
|
|
214
|
+
}
|
|
215
|
+
if (input.type === "tool_result" || input.event === "tool_result")
|
|
216
|
+
return messageIndicatesRepoEdit({ role: "toolResult", ...input });
|
|
217
|
+
if (input.type !== "tool_result_end" && input.event !== "tool_result_end")
|
|
218
|
+
return false;
|
|
219
|
+
return messageIndicatesRepoEdit(input.message);
|
|
220
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
export const CHILD_WATCHDOG_CONFIG_ENV = "DM_SUBAGENT_WATCHDOG_CHILD_CONFIG";
|
|
2
|
+
export const CHILD_WATCHDOG_STATUS_EVENT = "subagent.watchdog.status";
|
|
3
|
+
export const CHILD_WATCHDOG_PHASES = ["idle", "reviewing", "autofollow", "settling", "stale", "failed"];
|
|
4
|
+
export function resolveChildWatchdogConfig(input) {
|
|
5
|
+
const override = input.agent ? input.config.children.overrides[input.agent] : undefined;
|
|
6
|
+
const enabled = input.config.enabled && (override?.enabled ?? input.config.children.enabled);
|
|
7
|
+
if (!enabled)
|
|
8
|
+
return;
|
|
9
|
+
const model = override?.model ?? input.config.children.model;
|
|
10
|
+
const thinking = override?.thinking ?? input.config.children.thinking;
|
|
11
|
+
return {
|
|
12
|
+
enabled: true,
|
|
13
|
+
...input.runId ? { runId: input.runId } : {},
|
|
14
|
+
...input.agent ? { agent: input.agent } : {},
|
|
15
|
+
...input.childIndex !== undefined ? { childIndex: input.childIndex } : {},
|
|
16
|
+
watchdogTailTimeoutMs: input.config.children.watchdogTailTimeoutMs,
|
|
17
|
+
agentEndTimeoutMs: input.config.agentEndTimeoutMs,
|
|
18
|
+
maxWarnings: input.config.maxWarnings,
|
|
19
|
+
...model ? { model } : {},
|
|
20
|
+
...thinking !== undefined ? { thinking } : {},
|
|
21
|
+
lsp: { ...input.config.lsp },
|
|
22
|
+
autoFollowBlockers: input.config.children.autoFollow.blockers,
|
|
23
|
+
autoFollowMaxAttempts: input.config.children.autoFollow.maxAttempts,
|
|
24
|
+
stalemateRepeats: input.config.children.autoFollow.stalemateRepeats
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function encodeChildWatchdogConfig(config) {
|
|
28
|
+
return config ? JSON.stringify(config) : undefined;
|
|
29
|
+
}
|
|
30
|
+
function childConfigObject(value, field) {
|
|
31
|
+
if (value && typeof value === "object" && !Array.isArray(value))
|
|
32
|
+
return value;
|
|
33
|
+
throw new Error(`Invalid child watchdog config: ${field} must be an object.`);
|
|
34
|
+
}
|
|
35
|
+
function childConfigOptionalString(input, field) {
|
|
36
|
+
if (!(field in input))
|
|
37
|
+
return;
|
|
38
|
+
const value = input[field];
|
|
39
|
+
if (typeof value === "string" && value.trim())
|
|
40
|
+
return value;
|
|
41
|
+
throw new Error(`Invalid child watchdog config: ${field} must be a non-empty string.`);
|
|
42
|
+
}
|
|
43
|
+
function childConfigOptionalIndex(input, field) {
|
|
44
|
+
if (!(field in input))
|
|
45
|
+
return;
|
|
46
|
+
const value = input[field];
|
|
47
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0)
|
|
48
|
+
return value;
|
|
49
|
+
throw new Error(`Invalid child watchdog config: ${field} must be a non-negative integer.`);
|
|
50
|
+
}
|
|
51
|
+
function childConfigPositiveInteger(input, field) {
|
|
52
|
+
const value = input[field];
|
|
53
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 1)
|
|
54
|
+
return value;
|
|
55
|
+
throw new Error(`Invalid child watchdog config: ${field} must be a positive integer.`);
|
|
56
|
+
}
|
|
57
|
+
function childConfigNullableNonNegativeInteger(input, field) {
|
|
58
|
+
const value = input[field];
|
|
59
|
+
if (value === null)
|
|
60
|
+
return null;
|
|
61
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0)
|
|
62
|
+
return value;
|
|
63
|
+
throw new Error(`Invalid child watchdog config: ${field} must be null or a non-negative integer.`);
|
|
64
|
+
}
|
|
65
|
+
function childConfigBoolean(input, field) {
|
|
66
|
+
const value = input[field];
|
|
67
|
+
if (typeof value === "boolean")
|
|
68
|
+
return value;
|
|
69
|
+
throw new Error(`Invalid child watchdog config: ${field} must be a boolean.`);
|
|
70
|
+
}
|
|
71
|
+
function childConfigLsp(value) {
|
|
72
|
+
const input = childConfigObject(value, "lsp");
|
|
73
|
+
if (typeof input.enabled !== "boolean")
|
|
74
|
+
throw new Error("Invalid child watchdog config: lsp.enabled must be a boolean.");
|
|
75
|
+
if (typeof input.timeoutMs !== "number" || !Number.isInteger(input.timeoutMs) || input.timeoutMs < 1) {
|
|
76
|
+
throw new Error("Invalid child watchdog config: lsp.timeoutMs must be a positive integer.");
|
|
77
|
+
}
|
|
78
|
+
if (typeof input.maxFiles !== "number" || !Number.isInteger(input.maxFiles) || input.maxFiles < 1) {
|
|
79
|
+
throw new Error("Invalid child watchdog config: lsp.maxFiles must be a positive integer.");
|
|
80
|
+
}
|
|
81
|
+
if (typeof input.maxDiagnostics !== "number" || !Number.isInteger(input.maxDiagnostics) || input.maxDiagnostics < 0) {
|
|
82
|
+
throw new Error("Invalid child watchdog config: lsp.maxDiagnostics must be a non-negative integer.");
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
enabled: input.enabled,
|
|
86
|
+
timeoutMs: input.timeoutMs,
|
|
87
|
+
maxFiles: input.maxFiles,
|
|
88
|
+
maxDiagnostics: input.maxDiagnostics
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function decodeChildWatchdogConfig(raw) {
|
|
92
|
+
if (!raw)
|
|
93
|
+
return;
|
|
94
|
+
const parsed = childConfigObject(JSON.parse(raw), "root");
|
|
95
|
+
if (parsed.enabled === false)
|
|
96
|
+
return;
|
|
97
|
+
if (parsed.enabled !== true)
|
|
98
|
+
throw new Error("Invalid child watchdog config: enabled must be true or false.");
|
|
99
|
+
const thinking = parsed.thinking;
|
|
100
|
+
if (thinking !== undefined && typeof thinking !== "string" && thinking !== false) {
|
|
101
|
+
throw new Error("Invalid child watchdog config: thinking must be a string or false.");
|
|
102
|
+
}
|
|
103
|
+
const runId = childConfigOptionalString(parsed, "runId");
|
|
104
|
+
const agent = childConfigOptionalString(parsed, "agent");
|
|
105
|
+
const childIndex = childConfigOptionalIndex(parsed, "childIndex");
|
|
106
|
+
const model = childConfigOptionalString(parsed, "model");
|
|
107
|
+
return {
|
|
108
|
+
enabled: true,
|
|
109
|
+
...runId ? { runId } : {},
|
|
110
|
+
...agent ? { agent } : {},
|
|
111
|
+
...childIndex !== undefined ? { childIndex } : {},
|
|
112
|
+
watchdogTailTimeoutMs: childConfigPositiveInteger(parsed, "watchdogTailTimeoutMs"),
|
|
113
|
+
agentEndTimeoutMs: childConfigPositiveInteger(parsed, "agentEndTimeoutMs"),
|
|
114
|
+
maxWarnings: childConfigNullableNonNegativeInteger(parsed, "maxWarnings"),
|
|
115
|
+
...model ? { model } : {},
|
|
116
|
+
...thinking !== undefined ? { thinking } : {},
|
|
117
|
+
lsp: childConfigLsp(parsed.lsp),
|
|
118
|
+
autoFollowBlockers: childConfigBoolean(parsed, "autoFollowBlockers"),
|
|
119
|
+
autoFollowMaxAttempts: childConfigNullableNonNegativeInteger(parsed, "autoFollowMaxAttempts"),
|
|
120
|
+
stalemateRepeats: childConfigPositiveInteger(parsed, "stalemateRepeats")
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export function isChildWatchdogStatusEvent(value) {
|
|
124
|
+
if (!value || typeof value !== "object")
|
|
125
|
+
return false;
|
|
126
|
+
const event = value;
|
|
127
|
+
return event.type === CHILD_WATCHDOG_STATUS_EVENT && typeof event.seq === "number" && Number.isInteger(event.seq) && event.seq >= 0 && typeof event.ts === "number" && Number.isFinite(event.ts) && typeof event.followUpPending === "boolean" && typeof event.phase === "string" && CHILD_WATCHDOG_PHASES.includes(event.phase);
|
|
128
|
+
}
|
|
129
|
+
export function childWatchdogIsActive(snapshot) {
|
|
130
|
+
if (!snapshot)
|
|
131
|
+
return false;
|
|
132
|
+
return snapshot.followUpPending || snapshot.phase === "reviewing" || snapshot.phase === "autofollow" || snapshot.phase === "settling";
|
|
133
|
+
}
|
|
134
|
+
export function acceptChildWatchdogEvent(input) {
|
|
135
|
+
if (input.runId !== undefined && input.event.runId !== input.runId)
|
|
136
|
+
return;
|
|
137
|
+
if (input.agent !== undefined && input.event.agent !== input.agent)
|
|
138
|
+
return;
|
|
139
|
+
const eventIndex = input.event.childIndex ?? input.event.stepIndex;
|
|
140
|
+
if (input.childIndex !== undefined && eventIndex !== input.childIndex)
|
|
141
|
+
return;
|
|
142
|
+
if (input.current && input.event.seq <= input.current.seq)
|
|
143
|
+
return;
|
|
144
|
+
return {
|
|
145
|
+
phase: input.event.phase,
|
|
146
|
+
seq: input.event.seq,
|
|
147
|
+
lastUpdate: input.event.ts,
|
|
148
|
+
followUpPending: input.event.followUpPending,
|
|
149
|
+
...input.event.reason ? { reason: input.event.reason } : {}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const CONTENT_FREE_PHRASES = new Set([
|
|
2
|
+
"stop",
|
|
3
|
+
"done",
|
|
4
|
+
"complete",
|
|
5
|
+
"completed",
|
|
6
|
+
"no issue",
|
|
7
|
+
"no issues",
|
|
8
|
+
"no concern",
|
|
9
|
+
"no concerns",
|
|
10
|
+
"nothing to add",
|
|
11
|
+
"lgtm",
|
|
12
|
+
"looks good",
|
|
13
|
+
"looks good to me",
|
|
14
|
+
"all good",
|
|
15
|
+
"ok",
|
|
16
|
+
"okay",
|
|
17
|
+
"none",
|
|
18
|
+
"n a"
|
|
19
|
+
]);
|
|
20
|
+
export function normalizeWatchdogEmissionText(value) {
|
|
21
|
+
return value.normalize("NFKC").toLowerCase().replace(/[’'`]/g, "").replace(/[^\p{L}\p{N}]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
22
|
+
}
|
|
23
|
+
export function watchdogWarningUnderlyingIdentity(warning) {
|
|
24
|
+
return [normalizeWatchdogEmissionText(warning.summary), normalizeWatchdogEmissionText(warning.evidence)].join(`
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
export function watchdogWarningIdentity(warning) {
|
|
28
|
+
return [warning.severity, watchdogWarningUnderlyingIdentity(warning)].join(`
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
function isContentFree(value) {
|
|
32
|
+
const normalized = normalizeWatchdogEmissionText(value);
|
|
33
|
+
return !normalized || CONTENT_FREE_PHRASES.has(normalized);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class WatchdogEmissionGuard {
|
|
37
|
+
maxWarnings;
|
|
38
|
+
dedupeHistoryLimit;
|
|
39
|
+
acceptedCount = 0;
|
|
40
|
+
acceptedByUnderlyingIdentity = new Map;
|
|
41
|
+
historyOrder = [];
|
|
42
|
+
updateAcceptedUnderlyingIdentity;
|
|
43
|
+
updateAcceptedSeverity;
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
this.maxWarnings = options.maxWarnings ?? null;
|
|
46
|
+
this.dedupeHistoryLimit = options.dedupeHistoryLimit ?? 200;
|
|
47
|
+
}
|
|
48
|
+
startModelUpdate() {
|
|
49
|
+
this.updateAcceptedUnderlyingIdentity = undefined;
|
|
50
|
+
this.updateAcceptedSeverity = undefined;
|
|
51
|
+
}
|
|
52
|
+
reset() {
|
|
53
|
+
this.acceptedCount = 0;
|
|
54
|
+
this.acceptedByUnderlyingIdentity.clear();
|
|
55
|
+
this.historyOrder = [];
|
|
56
|
+
this.startModelUpdate();
|
|
57
|
+
}
|
|
58
|
+
evaluate(warning) {
|
|
59
|
+
if (isContentFree(warning.summary) || isContentFree(warning.evidence) || isContentFree(warning.recommendedAction)) {
|
|
60
|
+
return { accepted: false, reason: "content-free" };
|
|
61
|
+
}
|
|
62
|
+
const underlyingIdentity = watchdogWarningUnderlyingIdentity(warning);
|
|
63
|
+
const identity = watchdogWarningIdentity(warning);
|
|
64
|
+
const priorSeverity = this.acceptedByUnderlyingIdentity.get(underlyingIdentity);
|
|
65
|
+
const escalation = priorSeverity === "concern" && warning.severity === "blocker";
|
|
66
|
+
if (this.updateAcceptedUnderlyingIdentity !== undefined) {
|
|
67
|
+
const updateEscalation = this.updateAcceptedUnderlyingIdentity === underlyingIdentity && this.updateAcceptedSeverity === "concern" && warning.severity === "blocker";
|
|
68
|
+
if (!updateEscalation)
|
|
69
|
+
return { accepted: false, reason: "update-budget", identity, underlyingIdentity };
|
|
70
|
+
}
|
|
71
|
+
if (priorSeverity !== undefined && !escalation)
|
|
72
|
+
return { accepted: false, reason: "duplicate", identity, underlyingIdentity };
|
|
73
|
+
if (this.maxWarnings !== null && this.acceptedCount >= this.maxWarnings && !escalation) {
|
|
74
|
+
return { accepted: false, reason: "max-warnings", identity, underlyingIdentity };
|
|
75
|
+
}
|
|
76
|
+
this.acceptedByUnderlyingIdentity.set(underlyingIdentity, warning.severity);
|
|
77
|
+
if (!priorSeverity) {
|
|
78
|
+
this.acceptedCount++;
|
|
79
|
+
this.historyOrder.push(underlyingIdentity);
|
|
80
|
+
}
|
|
81
|
+
while (this.historyOrder.length > this.dedupeHistoryLimit) {
|
|
82
|
+
const stale = this.historyOrder.shift();
|
|
83
|
+
if (stale)
|
|
84
|
+
this.acceptedByUnderlyingIdentity.delete(stale);
|
|
85
|
+
}
|
|
86
|
+
this.updateAcceptedUnderlyingIdentity = underlyingIdentity;
|
|
87
|
+
this.updateAcceptedSeverity = warning.severity;
|
|
88
|
+
return { accepted: true, identity, underlyingIdentity, escalation };
|
|
89
|
+
}
|
|
90
|
+
}
|