@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.
Files changed (279) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +211 -67
  3. package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
  4. package/extensions/dm-subagents/agents/claude-code.md +15 -0
  5. package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
  6. package/extensions/dm-subagents/agents/codex-exec.md +15 -0
  7. package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
  8. package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
  9. package/extensions/dm-subagents/agents/delegate.md +3 -2
  10. package/extensions/dm-subagents/agents/oracle.md +10 -5
  11. package/extensions/dm-subagents/agents/researcher.md +2 -2
  12. package/extensions/dm-subagents/agents/reviewer.md +17 -7
  13. package/extensions/dm-subagents/agents/scout.md +5 -5
  14. package/extensions/dm-subagents/agents/worker.md +6 -2
  15. package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
  16. package/extensions/dm-subagents/index.js +4 -0
  17. package/extensions/dm-subagents/inspector-runner.mjs +10 -0
  18. package/extensions/dm-subagents/install.mjs +3 -2
  19. package/extensions/dm-subagents/package.json +2 -2
  20. package/extensions/dm-subagents/prompts/council.md +60 -0
  21. package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
  22. package/extensions/dm-subagents/prompts/review-loop.md +13 -7
  23. package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
  24. package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
  25. package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
  26. package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
  27. package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
  28. package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
  29. package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
  30. package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
  31. package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
  32. package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
  33. package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
  34. package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
  35. package/extensions/dm-subagents/src/agents/agents.js +1447 -299
  36. package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
  37. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
  38. package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
  39. package/extensions/dm-subagents/src/agents/identity.js +1 -1
  40. package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
  41. package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
  42. package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
  43. package/extensions/dm-subagents/src/agents/skills.js +52 -35
  44. package/extensions/dm-subagents/src/api/agents.js +6 -0
  45. package/extensions/dm-subagents/src/api/background-work.js +151 -0
  46. package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
  47. package/extensions/dm-subagents/src/api/control-channel.js +3 -0
  48. package/extensions/dm-subagents/src/api/delegation.js +5 -0
  49. package/extensions/dm-subagents/src/api/dm-args.js +3 -0
  50. package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
  51. package/extensions/dm-subagents/src/api/external-runs.js +233 -0
  52. package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
  53. package/extensions/dm-subagents/src/api/preflight.js +322 -0
  54. package/extensions/dm-subagents/src/api/project-panes.js +11 -0
  55. package/extensions/dm-subagents/src/api/shared-types.js +4 -0
  56. package/extensions/dm-subagents/src/extension/config.js +175 -0
  57. package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
  58. package/extensions/dm-subagents/src/extension/doctor.js +71 -17
  59. package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
  60. package/extensions/dm-subagents/src/extension/index.js +711 -265
  61. package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
  62. package/extensions/dm-subagents/src/extension/rpc.js +427 -22
  63. package/extensions/dm-subagents/src/extension/schemas.js +158 -65
  64. package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
  65. package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
  66. package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
  67. package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
  68. package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
  69. package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
  70. package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
  71. package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
  72. package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
  73. package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
  74. package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
  75. package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
  76. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
  77. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
  78. package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
  79. package/extensions/dm-subagents/src/missions/actions.js +394 -0
  80. package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
  81. package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
  82. package/extensions/dm-subagents/src/missions/store.js +548 -0
  83. package/extensions/dm-subagents/src/missions/types.js +9 -0
  84. package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
  85. package/extensions/dm-subagents/src/policy/authority.js +37 -0
  86. package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
  87. package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
  88. package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
  89. package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
  90. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
  91. package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
  92. package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
  93. package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
  94. package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
  95. package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
  96. package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
  97. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
  98. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
  99. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
  100. package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
  101. package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
  102. package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
  103. package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
  104. package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
  105. package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
  106. package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
  107. package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
  108. package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
  109. package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
  110. package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
  111. package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
  112. package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
  113. package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
  114. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
  115. package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
  116. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
  117. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
  118. package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
  119. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
  120. package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
  121. package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
  122. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
  123. package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
  124. package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
  125. package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
  126. package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
  127. package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
  128. package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
  129. package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
  130. package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
  131. package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
  132. package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
  133. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
  134. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
  135. package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
  136. package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
  137. package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
  138. package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
  139. package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
  140. package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
  141. package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
  142. package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
  143. package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
  144. package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
  145. package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
  146. package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
  147. package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
  148. package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
  149. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
  150. package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
  151. package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
  152. package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
  153. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
  154. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
  155. package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
  156. package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
  157. package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
  158. package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
  159. package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
  160. package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
  161. package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
  162. package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
  163. package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
  164. package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
  165. package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
  166. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
  167. package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
  168. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
  169. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
  170. package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
  171. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
  172. package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
  173. package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
  174. package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
  175. package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
  176. package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
  177. package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
  178. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
  179. package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
  180. package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
  181. package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
  182. package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
  183. package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
  184. package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
  185. package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
  186. package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
  187. package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
  188. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
  189. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
  190. package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
  191. package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
  192. package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
  193. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
  194. package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
  195. package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
  196. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
  197. package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
  198. package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
  199. package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
  200. package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
  201. package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
  202. package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
  203. package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
  204. package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
  205. package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
  206. package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
  207. package/extensions/dm-subagents/src/shared/display-text.js +142 -0
  208. package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
  209. package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
  210. package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
  211. package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
  212. package/extensions/dm-subagents/src/shared/formatters.js +21 -7
  213. package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
  214. package/extensions/dm-subagents/src/shared/model-info.js +10 -5
  215. package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
  216. package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
  217. package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
  218. package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
  219. package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
  220. package/extensions/dm-subagents/src/shared/settings.js +39 -47
  221. package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
  222. package/extensions/dm-subagents/src/shared/status-format.js +9 -2
  223. package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
  224. package/extensions/dm-subagents/src/shared/types.js +47 -7
  225. package/extensions/dm-subagents/src/shared/utf8.js +12 -0
  226. package/extensions/dm-subagents/src/shared/utils.js +151 -131
  227. package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
  228. package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
  229. package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
  230. package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
  231. package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
  232. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
  233. package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
  234. package/extensions/dm-subagents/src/slash/selector.js +101 -0
  235. package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
  236. package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
  237. package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
  238. package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
  239. package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
  240. package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
  241. package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
  242. package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
  243. package/extensions/dm-subagents/src/tui/render.js +1511 -261
  244. package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
  245. package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
  246. package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
  247. package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
  248. package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
  249. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
  250. package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
  251. package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
  252. package/extensions/dm-subagents/src/watchdog/render.js +54 -0
  253. package/extensions/dm-subagents/src/watchdog/review.js +251 -0
  254. package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
  255. package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
  256. package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
  257. package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
  258. package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
  259. package/extensions/dm-subagents/src/watchdog/types.js +29 -0
  260. package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
  261. package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
  262. package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
  263. package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
  264. package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
  265. package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
  266. package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
  267. package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
  268. package/package.json +4 -3
  269. package/extensions/dm-fff/package.json +0 -21
  270. package/extensions/dm-fff/src/index.js +0 -691
  271. package/extensions/dm-fff/src/query.js +0 -60
  272. package/extensions/dm-subagents/agents/context-builder.md +0 -46
  273. package/extensions/dm-subagents/agents/planner.md +0 -55
  274. package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
  275. package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
  276. package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
  277. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
  278. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
  279. 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
- const colonIdx = model.lastIndexOf(":");
4
- if (colonIdx === -1)
5
- return { baseModel: model, thinkingSuffix: "" };
6
- return {
7
- baseModel: model.substring(0, colonIdx),
8
- thinkingSuffix: model.substring(colonIdx)
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 resolveBaseModelCandidate(baseModel, availableModels, preferredProvider) {
31
- if (baseModel.includes("/")) {
32
- const exact = availableModels.find((entry) => entry.fullId === baseModel);
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
- export function fuzzyResolveModel(baseModel, availableModels, preferredProvider) {
48
- let queryProvider;
49
- let queryIdRaw = baseModel;
59
+ function splitQualifiedModelQuery(baseModel, availableModels) {
50
60
  const slashIdx = baseModel.indexOf("/");
51
61
  if (slashIdx !== -1) {
52
- queryProvider = normalizeModelSegment(baseModel.slice(0, slashIdx));
53
- queryIdRaw = baseModel.slice(slashIdx + 1);
54
- } else {
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 = resolveModelCandidate(explicit, availableModels, preferredProvider);
213
+ resolved = resolveRequiredSubagentModelCandidate(explicit, availableModels, preferredProvider);
117
214
  }
118
- if (resolved && options?.scope?.enforce) {
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
- const violation = checkModelScope(resolved, options.scope, source);
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 normalized = resolveModelCandidate(raw.trim(), availableModels, preferredProvider);
138
- if (!normalized || seen.has(normalized))
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
- if (index > 0 && options?.scope?.enforce) {
141
- const violation = checkModelScope(normalized, options.scope, "inherited");
142
- if (violation)
143
- (options.onWarn ?? defaultScopeWarn)(violation);
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}.`;