@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,207 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { splitKnownThinkingSuffix } from "../../shared/model-info.js";
|
|
4
|
+
import { TEMP_ROOT_DIR } from "../../shared/types.js";
|
|
5
|
+
export const EXCLUSIONS_PATH_ENV = "PI_MODEL_EXCLUSIONS_PATH";
|
|
6
|
+
let exclusions = [];
|
|
7
|
+
let loaded = false;
|
|
8
|
+
export const DEFAULT_MODEL_EXCLUSION_TTL_MS = 24 * 60 * 60000;
|
|
9
|
+
export const MAX_MODEL_EXCLUSION_TTL_MS = 8000000000000000;
|
|
10
|
+
const MAX_DATE_TIMESTAMP_MS = 8640000000000000;
|
|
11
|
+
let defaultTTLMs = DEFAULT_MODEL_EXCLUSION_TTL_MS;
|
|
12
|
+
let loadedTTLCeilingMs;
|
|
13
|
+
let persistTimer = null;
|
|
14
|
+
let persistSeq = 0;
|
|
15
|
+
export function setDefaultTTL(ms, options) {
|
|
16
|
+
if (!Number.isFinite(ms) || ms <= 0 || ms > MAX_MODEL_EXCLUSION_TTL_MS) {
|
|
17
|
+
throw new Error(`Default model exclusion TTL must be a finite positive number no greater than ${MAX_MODEL_EXCLUSION_TTL_MS}.`);
|
|
18
|
+
}
|
|
19
|
+
defaultTTLMs = ms;
|
|
20
|
+
loadedTTLCeilingMs = options?.shortenExisting ? ms : undefined;
|
|
21
|
+
if (loaded && loadedTTLCeilingMs !== undefined && shortenExclusionsToTTL(exclusions, loadedTTLCeilingMs, Date.now()))
|
|
22
|
+
schedulePersist();
|
|
23
|
+
}
|
|
24
|
+
export function getExclusionsFilePath() {
|
|
25
|
+
const envPath = process.env[EXCLUSIONS_PATH_ENV];
|
|
26
|
+
if (typeof envPath === "string" && envPath.trim())
|
|
27
|
+
return envPath.trim();
|
|
28
|
+
return path.join(TEMP_ROOT_DIR, "model-exclusions.json");
|
|
29
|
+
}
|
|
30
|
+
export function flushPersist() {
|
|
31
|
+
const file = getExclusionsFilePath();
|
|
32
|
+
try {
|
|
33
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
34
|
+
const tmpPath = `${file}.${process.pid}.${persistSeq++}.tmp`;
|
|
35
|
+
fs.writeFileSync(tmpPath, JSON.stringify({
|
|
36
|
+
version: 1,
|
|
37
|
+
exclusions: deduplicate(exclusions)
|
|
38
|
+
}, null, 2), "utf-8");
|
|
39
|
+
fs.renameSync(tmpPath, file);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error(`[model-exclusions] Failed to persist exclusions to ${file}:`, error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function schedulePersist() {
|
|
45
|
+
if (persistTimer)
|
|
46
|
+
clearTimeout(persistTimer);
|
|
47
|
+
persistTimer = setTimeout(() => {
|
|
48
|
+
persistTimer = null;
|
|
49
|
+
flushPersist();
|
|
50
|
+
}, 5000);
|
|
51
|
+
persistTimer.unref?.();
|
|
52
|
+
}
|
|
53
|
+
function ensureLoaded() {
|
|
54
|
+
if (loaded)
|
|
55
|
+
return;
|
|
56
|
+
loaded = true;
|
|
57
|
+
try {
|
|
58
|
+
const raw = fs.readFileSync(getExclusionsFilePath(), "utf-8");
|
|
59
|
+
const data = JSON.parse(raw);
|
|
60
|
+
if (data.version === 1) {
|
|
61
|
+
if (!Array.isArray(data.exclusions))
|
|
62
|
+
throw new Error("Model exclusion store version 1 must contain an exclusions array.");
|
|
63
|
+
for (let index = 0;index < data.exclusions.length; index++) {
|
|
64
|
+
const entry = data.exclusions[index];
|
|
65
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
66
|
+
throw new Error(`Model exclusion store entry ${index} must be an object.`);
|
|
67
|
+
if (entry.reason !== undefined && typeof entry.reason !== "string")
|
|
68
|
+
throw new Error(`Model exclusion store entry ${index} has an invalid reason.`);
|
|
69
|
+
for (const field of ["recordedAt", "expiresAt"]) {
|
|
70
|
+
const timestamp = entry[field];
|
|
71
|
+
if (typeof timestamp !== "number" || !Number.isFinite(timestamp) || timestamp <= 0 || timestamp > MAX_DATE_TIMESTAMP_MS) {
|
|
72
|
+
throw new Error(`Model exclusion store entry ${index} has an invalid ${field}.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
exclusions = (data.exclusions ?? []).filter((e) => e.expiresAt > now);
|
|
78
|
+
const shortened = loadedTTLCeilingMs !== undefined && shortenExclusionsToTTL(exclusions, loadedTTLCeilingMs, now);
|
|
79
|
+
exclusions = deduplicate(exclusions);
|
|
80
|
+
if (shortened)
|
|
81
|
+
schedulePersist();
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error.code !== "ENOENT") {
|
|
85
|
+
console.error(`[model-exclusions] Failed to load exclusions from ${getExclusionsFilePath()}:`, error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function dedupKey(entry) {
|
|
90
|
+
return `${entry.provider ?? ""}|${entry.modelId ?? ""}`;
|
|
91
|
+
}
|
|
92
|
+
function deduplicate(items) {
|
|
93
|
+
const map = new Map;
|
|
94
|
+
for (const entry of items) {
|
|
95
|
+
const key = dedupKey(entry);
|
|
96
|
+
const existing = map.get(key);
|
|
97
|
+
if (!existing || entry.recordedAt > existing.recordedAt) {
|
|
98
|
+
map.set(key, entry);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return Array.from(map.values());
|
|
102
|
+
}
|
|
103
|
+
export function recordModelFailure(options) {
|
|
104
|
+
ensureLoaded();
|
|
105
|
+
const ttl = options.ttlMs ?? defaultTTLMs;
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
const target = options.modelId !== undefined ? { modelId: options.modelId, ...options.provider ? { provider: options.provider } : {} } : { provider: options.provider };
|
|
108
|
+
const exclusion = {
|
|
109
|
+
...target,
|
|
110
|
+
reason: options.reason ?? "runtime-failure",
|
|
111
|
+
recordedAt: now,
|
|
112
|
+
expiresAt: now + ttl
|
|
113
|
+
};
|
|
114
|
+
exclusions.unshift(exclusion);
|
|
115
|
+
exclusions = deduplicate(exclusions);
|
|
116
|
+
if (exclusions.length > 200)
|
|
117
|
+
exclusions.length = 200;
|
|
118
|
+
flushPersist();
|
|
119
|
+
}
|
|
120
|
+
export function clearExpiredExclusions() {
|
|
121
|
+
ensureLoaded();
|
|
122
|
+
prune(exclusions, Date.now());
|
|
123
|
+
schedulePersist();
|
|
124
|
+
}
|
|
125
|
+
export function clearExclusions() {
|
|
126
|
+
ensureLoaded();
|
|
127
|
+
exclusions.length = 0;
|
|
128
|
+
schedulePersist();
|
|
129
|
+
}
|
|
130
|
+
function entryMatches(entry, candidateModelId, candidateProvider, now) {
|
|
131
|
+
if (entry.expiresAt <= now)
|
|
132
|
+
return false;
|
|
133
|
+
if (entry.modelId) {
|
|
134
|
+
if (entry.modelId !== candidateModelId)
|
|
135
|
+
return false;
|
|
136
|
+
return !entry.provider || !candidateProvider || entry.provider === candidateProvider;
|
|
137
|
+
}
|
|
138
|
+
return Boolean(entry.provider) && entry.provider === candidateProvider;
|
|
139
|
+
}
|
|
140
|
+
export function isExcluded(modelId, provider) {
|
|
141
|
+
ensureLoaded();
|
|
142
|
+
return exclusions.some((entry) => entryMatches(entry, modelId, provider, Date.now()));
|
|
143
|
+
}
|
|
144
|
+
export function findModelExclusion(fullId, now = Date.now()) {
|
|
145
|
+
ensureLoaded();
|
|
146
|
+
const { provider, modelId } = parseModelKey(fullId);
|
|
147
|
+
return exclusions.find((entry) => entryMatches(entry, modelId, provider, now));
|
|
148
|
+
}
|
|
149
|
+
export function getExcludedCount() {
|
|
150
|
+
ensureLoaded();
|
|
151
|
+
clearExpiredExclusions();
|
|
152
|
+
return exclusions.length;
|
|
153
|
+
}
|
|
154
|
+
export function parseModelKey(fullId) {
|
|
155
|
+
const base = splitKnownThinkingSuffix(fullId).baseModel;
|
|
156
|
+
if (!base.includes("/"))
|
|
157
|
+
return { modelId: base };
|
|
158
|
+
const slash = base.indexOf("/");
|
|
159
|
+
return { provider: base.slice(0, slash), modelId: base.slice(slash + 1) };
|
|
160
|
+
}
|
|
161
|
+
export function filterFallbackCandidates(candidates, opts) {
|
|
162
|
+
ensureLoaded();
|
|
163
|
+
const timestamp = opts?.now ?? Date.now();
|
|
164
|
+
const seen = new Set;
|
|
165
|
+
const filtered = [];
|
|
166
|
+
for (const raw of candidates) {
|
|
167
|
+
if (!raw || seen.has(raw))
|
|
168
|
+
continue;
|
|
169
|
+
const { provider: candidateProvider, modelId: candidateModelId } = parseModelKey(raw);
|
|
170
|
+
const exclusion = exclusions.find((entry) => entryMatches(entry, candidateModelId, candidateProvider, timestamp));
|
|
171
|
+
if (exclusion) {
|
|
172
|
+
opts?.onExcluded?.(raw, exclusion);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
seen.add(raw);
|
|
176
|
+
filtered.push(raw);
|
|
177
|
+
}
|
|
178
|
+
return filtered;
|
|
179
|
+
}
|
|
180
|
+
export function reloadFromDisk() {
|
|
181
|
+
loaded = false;
|
|
182
|
+
exclusions = [];
|
|
183
|
+
ensureLoaded();
|
|
184
|
+
}
|
|
185
|
+
function prune(items, now) {
|
|
186
|
+
let write = 0;
|
|
187
|
+
for (let i = 0;i < items.length; i++) {
|
|
188
|
+
const entry = items[i];
|
|
189
|
+
if (entry.expiresAt > now) {
|
|
190
|
+
items[write++] = entry;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
items.length = write;
|
|
194
|
+
}
|
|
195
|
+
function shortenExclusionsToTTL(items, ttlMs, now) {
|
|
196
|
+
let changed = false;
|
|
197
|
+
for (const entry of items) {
|
|
198
|
+
const configuredExpiry = entry.recordedAt + ttlMs;
|
|
199
|
+
if (entry.expiresAt > configuredExpiry) {
|
|
200
|
+
entry.expiresAt = configuredExpiry;
|
|
201
|
+
changed = true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const previousLength = items.length;
|
|
205
|
+
prune(items, now);
|
|
206
|
+
return changed || items.length !== previousLength;
|
|
207
|
+
}
|
|
@@ -1,14 +1,39 @@
|
|
|
1
|
+
import { splitKnownThinkingSuffix } from "../../shared/model-info.js";
|
|
2
|
+
import { filterFallbackCandidates, findModelExclusion, parseModelKey, recordModelFailure } from "./model-exclusions.js";
|
|
1
3
|
import { checkModelScope } from "./model-scope.js";
|
|
4
|
+
import { redactSecretValues } from "./permissions.js";
|
|
2
5
|
export function splitThinkingSuffix(model) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
return splitKnownThinkingSuffix(model);
|
|
7
|
+
}
|
|
8
|
+
export function formatSubagentModelVerificationError(expectedModel, observedModel, availableModels) {
|
|
9
|
+
if (!availableModels || availableModels.length === 0)
|
|
10
|
+
return;
|
|
11
|
+
const expectedBase = splitThinkingSuffix(expectedModel).baseModel;
|
|
12
|
+
const observedBase = splitThinkingSuffix(observedModel).baseModel;
|
|
13
|
+
if (expectedBase === observedBase)
|
|
14
|
+
return;
|
|
15
|
+
const expectedEntry = availableModels.find((entry) => entry.fullId === expectedBase);
|
|
16
|
+
if (expectedEntry) {
|
|
17
|
+
if (expectedEntry.id === observedBase)
|
|
18
|
+
return;
|
|
19
|
+
const expectedIdLeaf = expectedEntry.id.slice(expectedEntry.id.lastIndexOf("/") + 1);
|
|
20
|
+
const expectedFullIdLeaf = expectedEntry.fullId.slice(expectedEntry.fullId.lastIndexOf("/") + 1);
|
|
21
|
+
if (expectedIdLeaf === observedBase || expectedFullIdLeaf === observedBase)
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
return `model_verification_failed: child reported a different model than the launch candidate. Expected '${expectedModel}' but observed '${observedModel}'.`;
|
|
10
25
|
}
|
|
11
26
|
export const INHERIT_MODEL = "inherit";
|
|
27
|
+
export function normalizeParentModel(model) {
|
|
28
|
+
if (!model || typeof model !== "object")
|
|
29
|
+
return;
|
|
30
|
+
const candidate = model;
|
|
31
|
+
if (typeof candidate.provider !== "string" || typeof candidate.id !== "string")
|
|
32
|
+
return;
|
|
33
|
+
if (!candidate.provider || !candidate.id)
|
|
34
|
+
return;
|
|
35
|
+
return { provider: candidate.provider, id: candidate.id };
|
|
36
|
+
}
|
|
12
37
|
export function normalizeModelSegment(segment) {
|
|
13
38
|
return segment.toLowerCase().replace(/[._]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
14
39
|
}
|
|
@@ -27,44 +52,56 @@ function stripTrailingDateStamp(segment) {
|
|
|
27
52
|
return compact[1];
|
|
28
53
|
return segment;
|
|
29
54
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (exact)
|
|
34
|
-
return exact.fullId;
|
|
35
|
-
} else {
|
|
36
|
-
const exactMatches = availableModels.filter((entry) => entry.id === baseModel);
|
|
37
|
-
if (preferredProvider) {
|
|
38
|
-
const preferredMatch = exactMatches.find((entry) => entry.provider === preferredProvider);
|
|
39
|
-
if (preferredMatch)
|
|
40
|
-
return preferredMatch.fullId;
|
|
41
|
-
}
|
|
42
|
-
if (exactMatches.length === 1)
|
|
43
|
-
return exactMatches[0].fullId;
|
|
44
|
-
}
|
|
45
|
-
return fuzzyResolveModel(baseModel, availableModels, preferredProvider);
|
|
55
|
+
function isRegisteredProvider(provider, availableModels) {
|
|
56
|
+
const normalized = normalizeModelSegment(provider);
|
|
57
|
+
return availableModels.some((entry) => normalizeModelSegment(entry.provider) === normalized);
|
|
46
58
|
}
|
|
47
|
-
|
|
48
|
-
let queryProvider;
|
|
49
|
-
let queryIdRaw = baseModel;
|
|
59
|
+
function splitQualifiedModelQuery(baseModel, availableModels) {
|
|
50
60
|
const slashIdx = baseModel.indexOf("/");
|
|
51
61
|
if (slashIdx !== -1) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const providerSeparators = [":", "."];
|
|
56
|
-
for (const separator of providerSeparators) {
|
|
57
|
-
const separatorIdx = baseModel.indexOf(separator);
|
|
58
|
-
if (separatorIdx <= 0)
|
|
59
|
-
continue;
|
|
60
|
-
const providerPart = normalizeModelSegment(baseModel.slice(0, separatorIdx));
|
|
61
|
-
if (!availableModels.some((entry) => normalizeModelSegment(entry.provider) === providerPart))
|
|
62
|
-
continue;
|
|
63
|
-
queryProvider = providerPart;
|
|
64
|
-
queryIdRaw = baseModel.slice(separatorIdx + 1);
|
|
65
|
-
break;
|
|
62
|
+
const providerPart = baseModel.slice(0, slashIdx);
|
|
63
|
+
if (isRegisteredProvider(providerPart, availableModels)) {
|
|
64
|
+
return { queryProvider: normalizeModelSegment(providerPart), queryIdRaw: baseModel.slice(slashIdx + 1) };
|
|
66
65
|
}
|
|
66
|
+
return { queryIdRaw: baseModel };
|
|
67
|
+
}
|
|
68
|
+
const providerSeparators = [":", "."];
|
|
69
|
+
for (const separator of providerSeparators) {
|
|
70
|
+
const separatorIdx = baseModel.indexOf(separator);
|
|
71
|
+
if (separatorIdx <= 0)
|
|
72
|
+
continue;
|
|
73
|
+
const providerPart = baseModel.slice(0, separatorIdx);
|
|
74
|
+
if (!isRegisteredProvider(providerPart, availableModels))
|
|
75
|
+
continue;
|
|
76
|
+
return { queryProvider: normalizeModelSegment(providerPart), queryIdRaw: baseModel.slice(separatorIdx + 1) };
|
|
67
77
|
}
|
|
78
|
+
return { queryIdRaw: baseModel };
|
|
79
|
+
}
|
|
80
|
+
function resolveExactIdMatches(baseModel, availableModels, preferredProvider) {
|
|
81
|
+
const exactMatches = availableModels.filter((entry) => entry.id === baseModel);
|
|
82
|
+
if (preferredProvider) {
|
|
83
|
+
const preferredMatch = exactMatches.find((entry) => entry.provider === preferredProvider);
|
|
84
|
+
if (preferredMatch)
|
|
85
|
+
return preferredMatch.fullId;
|
|
86
|
+
}
|
|
87
|
+
if (exactMatches.length === 1)
|
|
88
|
+
return exactMatches[0].fullId;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
function resolveBaseModelCandidate(baseModel, availableModels, preferredProvider) {
|
|
92
|
+
const exact = availableModels.find((entry) => entry.fullId === baseModel);
|
|
93
|
+
if (exact)
|
|
94
|
+
return exact.fullId;
|
|
95
|
+
const { queryProvider } = splitQualifiedModelQuery(baseModel, availableModels);
|
|
96
|
+
if (queryProvider === undefined) {
|
|
97
|
+
const exactId = resolveExactIdMatches(baseModel, availableModels, preferredProvider);
|
|
98
|
+
if (exactId)
|
|
99
|
+
return exactId;
|
|
100
|
+
}
|
|
101
|
+
return fuzzyResolveModel(baseModel, availableModels, preferredProvider);
|
|
102
|
+
}
|
|
103
|
+
export function fuzzyResolveModel(baseModel, availableModels, preferredProvider) {
|
|
104
|
+
const { queryProvider, queryIdRaw } = splitQualifiedModelQuery(baseModel, availableModels);
|
|
68
105
|
const queryId = normalizeModelSegment(queryIdRaw);
|
|
69
106
|
const queryIdNoDate = stripTrailingDateStamp(queryId);
|
|
70
107
|
const candidates = availableModels.filter((entry) => {
|
|
@@ -103,30 +140,102 @@ export function resolveModelCandidate(model, availableModels, preferredProvider)
|
|
|
103
140
|
return `${resolvedBase}${thinkingSuffix}`;
|
|
104
141
|
return model;
|
|
105
142
|
}
|
|
143
|
+
function resolveSubagentModelCandidate(model, availableModels, preferredProvider) {
|
|
144
|
+
if (!availableModels || availableModels.length === 0)
|
|
145
|
+
return model;
|
|
146
|
+
const resolvedWhole = resolveBaseModelCandidate(model, availableModels, preferredProvider);
|
|
147
|
+
if (resolvedWhole)
|
|
148
|
+
return resolvedWhole;
|
|
149
|
+
const { baseModel, thinkingSuffix } = splitThinkingSuffix(model);
|
|
150
|
+
const resolvedBase = thinkingSuffix ? resolveBaseModelCandidate(baseModel, availableModels, preferredProvider) : undefined;
|
|
151
|
+
return resolvedBase ? `${resolvedBase}${thinkingSuffix}` : undefined;
|
|
152
|
+
}
|
|
153
|
+
function suggestAlternateProviderModel(model, availableModels) {
|
|
154
|
+
if (!availableModels || availableModels.length === 0)
|
|
155
|
+
return;
|
|
156
|
+
const { baseModel, thinkingSuffix } = splitThinkingSuffix(model);
|
|
157
|
+
const { queryProvider, queryIdRaw } = splitQualifiedModelQuery(baseModel, availableModels);
|
|
158
|
+
if (queryProvider === undefined)
|
|
159
|
+
return;
|
|
160
|
+
const suggestion = resolveBaseModelCandidate(queryIdRaw, availableModels);
|
|
161
|
+
if (!suggestion)
|
|
162
|
+
return;
|
|
163
|
+
const matched = availableModels.find((entry) => entry.fullId === suggestion);
|
|
164
|
+
if (!matched || normalizeModelSegment(matched.provider) === queryProvider)
|
|
165
|
+
return;
|
|
166
|
+
return `${suggestion}${thinkingSuffix}`;
|
|
167
|
+
}
|
|
168
|
+
function resolveRequiredSubagentModelCandidate(model, availableModels, preferredProvider) {
|
|
169
|
+
const resolved = resolveSubagentModelCandidate(model, availableModels, preferredProvider);
|
|
170
|
+
if (resolved)
|
|
171
|
+
return resolved;
|
|
172
|
+
const suggestion = suggestAlternateProviderModel(model, availableModels);
|
|
173
|
+
throw new Error(`Unknown subagent model '${model}' in the active DM model registry.${suggestion ? ` Did you mean '${suggestion}'?` : ""}`);
|
|
174
|
+
}
|
|
106
175
|
function defaultScopeWarn(violation) {
|
|
107
176
|
console.warn(`[dm-subagents] ${violation.message}`);
|
|
108
177
|
}
|
|
178
|
+
function configuredScopes(scope) {
|
|
179
|
+
return scope ? Array.isArray(scope) ? scope : [scope] : [];
|
|
180
|
+
}
|
|
181
|
+
function throwForUnresolvedEnforcedInheritScope(scope, includeMixed = false) {
|
|
182
|
+
const unresolvedInheritScope = configuredScopes(scope).find((entry) => entry.enforce === true && (includeMixed ? entry.allow?.includes(INHERIT_MODEL) : entry.allow?.length === 1 && entry.allow[0] === INHERIT_MODEL));
|
|
183
|
+
if (!unresolvedInheritScope)
|
|
184
|
+
return;
|
|
185
|
+
const origin = unresolvedInheritScope.origin ?? "modelScope";
|
|
186
|
+
throw new Error(`Cannot enforce subagent model scope (${origin}): 'inherit' requires a current parent session model.`);
|
|
187
|
+
}
|
|
188
|
+
function enforceModelScopes(model, scope, source, onWarn) {
|
|
189
|
+
const violations = configuredScopes(scope).map((entry) => checkModelScope(model, entry, source)).filter((violation) => violation !== undefined);
|
|
190
|
+
const error = violations.find((violation) => violation.severity === "error");
|
|
191
|
+
if (error)
|
|
192
|
+
throw new Error(error.message);
|
|
193
|
+
for (const violation of violations)
|
|
194
|
+
(onWarn ?? defaultScopeWarn)(violation);
|
|
195
|
+
}
|
|
196
|
+
function throwForExplicitModelExclusion(model) {
|
|
197
|
+
const exclusion = findModelExclusion(model);
|
|
198
|
+
if (!exclusion)
|
|
199
|
+
return;
|
|
200
|
+
const reason = redactSecretValues((exclusion.reason ?? "runtime-failure").replace(/[\u0000-\u001f\u007f]+/g, " ")).slice(0, 240);
|
|
201
|
+
const expiry = Number.isFinite(exclusion.expiresAt) ? `; expires: ${new Date(exclusion.expiresAt).toISOString()}` : "";
|
|
202
|
+
throw new Error(`Requested subagent model '${model}' is excluded and cannot be replaced by a fallback (reason: ${reason}${expiry}).`);
|
|
203
|
+
}
|
|
109
204
|
export function resolveSubagentModelOverride(requestedModel, parentModel, availableModels, preferredProvider, options) {
|
|
110
205
|
const trimmed = typeof requestedModel === "string" ? requestedModel.trim() : "";
|
|
111
206
|
const explicit = trimmed && trimmed !== INHERIT_MODEL ? trimmed : undefined;
|
|
207
|
+
if (!parentModel)
|
|
208
|
+
throwForUnresolvedEnforcedInheritScope(options?.scope, explicit === undefined || options?.source === "inherited");
|
|
112
209
|
let resolved;
|
|
113
210
|
if (explicit === undefined) {
|
|
114
211
|
resolved = parentModel ? `${parentModel.provider}/${parentModel.id}` : undefined;
|
|
115
212
|
} else {
|
|
116
|
-
resolved =
|
|
213
|
+
resolved = resolveRequiredSubagentModelCandidate(explicit, availableModels, preferredProvider);
|
|
117
214
|
}
|
|
118
|
-
if (resolved && options?.
|
|
215
|
+
if (resolved && explicit !== undefined && options?.source === "explicit") {
|
|
216
|
+
throwForExplicitModelExclusion(resolved);
|
|
217
|
+
}
|
|
218
|
+
if (resolved && options?.scope) {
|
|
119
219
|
const source = explicit === undefined ? "inherited" : options.source ?? "inherited";
|
|
120
|
-
|
|
121
|
-
if (violation) {
|
|
122
|
-
if (violation.severity === "error")
|
|
123
|
-
throw new Error(violation.message);
|
|
124
|
-
(options.onWarn ?? defaultScopeWarn)(violation);
|
|
125
|
-
}
|
|
220
|
+
enforceModelScopes(resolved, options.scope, source, options.onWarn);
|
|
126
221
|
}
|
|
127
222
|
return resolved;
|
|
128
223
|
}
|
|
224
|
+
export function resolveEffectiveSubagentModel(explicitModel, agentModel, parentModel, availableModels, preferredProvider, options) {
|
|
225
|
+
const source = options?.source ?? (explicitModel !== undefined ? "explicit" : "inherited");
|
|
226
|
+
const resolved = resolveSubagentModelOverride(explicitModel ?? agentModel, parentModel, availableModels, preferredProvider, { ...options, source });
|
|
227
|
+
if (resolved || explicitModel === undefined)
|
|
228
|
+
return resolved;
|
|
229
|
+
return resolveSubagentModelOverride(agentModel, parentModel, availableModels, preferredProvider, { ...options, source: options?.source ?? "inherited" });
|
|
230
|
+
}
|
|
231
|
+
export function inheritsParentModel(explicitModel, agentModel, parentModel) {
|
|
232
|
+
const requestedModel = explicitModel ?? agentModel;
|
|
233
|
+
const trimmed = typeof requestedModel === "string" ? requestedModel.trim() : "";
|
|
234
|
+
return Boolean(parentModel && (!trimmed || trimmed === INHERIT_MODEL));
|
|
235
|
+
}
|
|
129
236
|
export function buildModelCandidates(primaryModel, fallbackModels, availableModels, preferredProvider, options) {
|
|
237
|
+
if (!primaryModel)
|
|
238
|
+
throwForUnresolvedEnforcedInheritScope(options?.scope, true);
|
|
130
239
|
const seen = new Set;
|
|
131
240
|
const candidates = [];
|
|
132
241
|
const rawCandidates = [primaryModel, ...fallbackModels ?? []];
|
|
@@ -134,21 +243,31 @@ export function buildModelCandidates(primaryModel, fallbackModels, availableMode
|
|
|
134
243
|
const raw = rawCandidates[index];
|
|
135
244
|
if (!raw)
|
|
136
245
|
continue;
|
|
137
|
-
const
|
|
138
|
-
|
|
246
|
+
const model = raw.trim();
|
|
247
|
+
const normalized = index === 0 ? options?.primaryModelFromParent ? model : resolveRequiredSubagentModelCandidate(model, availableModels, preferredProvider) : resolveSubagentModelCandidate(model, availableModels, preferredProvider);
|
|
248
|
+
if (!normalized) {
|
|
249
|
+
console.warn(`[dm-subagents] Skipping fallback model '${model}' because it is unavailable in this environment.`);
|
|
139
250
|
continue;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
251
|
+
}
|
|
252
|
+
if (seen.has(normalized))
|
|
253
|
+
continue;
|
|
254
|
+
const scopes = configuredScopes(options?.scope);
|
|
255
|
+
if (index > 0 || scopes.some((scope) => scope.enforce === true && scope.strict === true)) {
|
|
256
|
+
enforceModelScopes(normalized, scopes, "inherited", options?.onWarn);
|
|
144
257
|
}
|
|
145
258
|
seen.add(normalized);
|
|
146
259
|
candidates.push(normalized);
|
|
147
260
|
}
|
|
148
|
-
return candidates
|
|
261
|
+
return filterFallbackCandidates(candidates, {
|
|
262
|
+
onExcluded(candidate, exclusion) {
|
|
263
|
+
const reason = redactSecretValues((exclusion.reason ?? "runtime-failure").replace(/[\u0000-\u001f\u007f]+/g, " ")).slice(0, 240);
|
|
264
|
+
console.warn(`[dm-subagents] Skipping model '${candidate}' due to a cached exclusion (reason: ${reason}; expires: ${new Date(exclusion.expiresAt).toISOString()}).`);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
149
267
|
}
|
|
150
268
|
const RETRYABLE_MODEL_FAILURE_PATTERNS = [
|
|
151
269
|
/rate\s*limit/i,
|
|
270
|
+
/usage\s*limit/i,
|
|
152
271
|
/too many requests/i,
|
|
153
272
|
/\b429\b/,
|
|
154
273
|
/quota/i,
|
|
@@ -168,26 +287,77 @@ const RETRYABLE_MODEL_FAILURE_PATTERNS = [
|
|
|
168
287
|
/overloaded/i,
|
|
169
288
|
/service unavailable/i,
|
|
170
289
|
/temporar(?:ily)? unavailable/i,
|
|
290
|
+
/connection\s+(?:error|reset|closed|aborted)/i,
|
|
171
291
|
/connection refused/i,
|
|
172
292
|
/fetch failed/i,
|
|
173
293
|
/network error/i,
|
|
174
294
|
/socket hang up/i,
|
|
295
|
+
/stream ended without finish_reason/i,
|
|
175
296
|
/upstream/i,
|
|
176
297
|
/timed? out/i,
|
|
177
298
|
/timeout/i,
|
|
299
|
+
/\b500\b/,
|
|
178
300
|
/\b502\b/,
|
|
179
301
|
/\b503\b/,
|
|
180
302
|
/\b504\b/,
|
|
303
|
+
/internal server error/i,
|
|
181
304
|
/cold.?start/i,
|
|
182
305
|
/empty response/i,
|
|
183
306
|
/no output/i,
|
|
184
307
|
/model.*(?:load|fail|error)/i
|
|
185
308
|
];
|
|
309
|
+
const TOOL_FAILURE_PREFIX = /^[\w.:@/-]+ failed (?:(?:\(exit \d+\):)|(?:with exit code \d+))(?:\s|$)/i;
|
|
186
310
|
export function isRetryableModelFailure(error) {
|
|
187
311
|
if (!error)
|
|
188
312
|
return false;
|
|
313
|
+
if (TOOL_FAILURE_PREFIX.test(error.trim()))
|
|
314
|
+
return false;
|
|
189
315
|
return RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error));
|
|
190
316
|
}
|
|
317
|
+
function messageError(message) {
|
|
318
|
+
if (!message || typeof message !== "object")
|
|
319
|
+
return;
|
|
320
|
+
const value = message.errorMessage;
|
|
321
|
+
return typeof value === "string" ? value : undefined;
|
|
322
|
+
}
|
|
323
|
+
export function isRetryableModelFailureAttempt(input) {
|
|
324
|
+
if (!isRetryableModelFailure(input.error))
|
|
325
|
+
return false;
|
|
326
|
+
if ((input.toolCount ?? 0) > 0)
|
|
327
|
+
return false;
|
|
328
|
+
if (input.error === "Subagent produced no output (possible model cold-start or empty response)." || /^Subagent produced no output after terminal assistant stopReason "[^"]+"\.$/.test(input.error ?? ""))
|
|
329
|
+
return true;
|
|
330
|
+
if ((input.toolCount ?? 0) === 0 && (input.messages?.length ?? 0) === 0)
|
|
331
|
+
return true;
|
|
332
|
+
const error = input.error?.trim();
|
|
333
|
+
return Boolean(error && input.messages?.some((message) => messageError(message)?.trim() === error));
|
|
334
|
+
}
|
|
335
|
+
export function recordRetryableModelFailure(model, error) {
|
|
336
|
+
if (!model || !isRetryableModelFailure(error))
|
|
337
|
+
return;
|
|
338
|
+
const { provider, modelId } = parseModelKey(model);
|
|
339
|
+
recordModelFailure({ modelId, reason: error, ...provider ? { provider } : {} });
|
|
340
|
+
}
|
|
341
|
+
const CONTEXT_OVERFLOW_PATTERNS = [
|
|
342
|
+
/context(?: length| window| limit)? (?:exceed|overflow|too long)/i,
|
|
343
|
+
/maximum context length/i,
|
|
344
|
+
/too many tokens/i,
|
|
345
|
+
/token limit/i,
|
|
346
|
+
/context_length_exceeded/i,
|
|
347
|
+
/length_required/i,
|
|
348
|
+
/maximum.*tokens/i,
|
|
349
|
+
/prompt.*too long/i,
|
|
350
|
+
/input.*too long/i,
|
|
351
|
+
/exceeded.*context/i,
|
|
352
|
+
/context.*overflow/i
|
|
353
|
+
];
|
|
354
|
+
export function isContextOverflow(error) {
|
|
355
|
+
if (!error)
|
|
356
|
+
return false;
|
|
357
|
+
if (TOOL_FAILURE_PREFIX.test(error.trim()))
|
|
358
|
+
return false;
|
|
359
|
+
return CONTEXT_OVERFLOW_PATTERNS.some((pattern) => pattern.test(error));
|
|
360
|
+
}
|
|
191
361
|
export function formatModelAttemptNote(attempt, nextModel) {
|
|
192
362
|
const failure = attempt.error?.trim() || `exit ${attempt.exitCode ?? 1}`;
|
|
193
363
|
return nextModel ? `[fallback] ${attempt.model} failed: ${failure}. Retrying with ${nextModel}.` : `[fallback] ${attempt.model} failed: ${failure}.`;
|