open-multi-agent-kit 0.78.6 → 0.78.7

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 (169) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/MATURITY.md +2 -2
  4. package/README.md +19 -4
  5. package/dist/brand/night-city.theme.json +80 -0
  6. package/dist/brand/palette.d.ts +73 -177
  7. package/dist/brand/palette.js +115 -39
  8. package/dist/brand/rust-forge.theme.json +216 -0
  9. package/dist/brand/theme-compiled.d.ts +61 -0
  10. package/dist/brand/theme-compiled.js +241 -0
  11. package/dist/brand/theme.js +50 -37
  12. package/dist/cli/release-promotion-gate.d.ts +8 -1
  13. package/dist/cli/release-promotion-gate.js +20 -9
  14. package/dist/cli/root.d.ts +1 -0
  15. package/dist/cli/root.js +2 -1
  16. package/dist/cli/theme/index.d.ts +11 -2
  17. package/dist/cli/theme/index.js +6 -1
  18. package/dist/cli/theme/oklab-quantize.d.ts +29 -0
  19. package/dist/cli/theme/oklab-quantize.js +105 -0
  20. package/dist/cli/theme/render-table.d.ts +59 -0
  21. package/dist/cli/theme/render-table.js +112 -0
  22. package/dist/cli/theme/status-frame.d.ts +10 -0
  23. package/dist/cli/theme/status-frame.js +22 -0
  24. package/dist/cli/theme/terminal-capability.d.ts +6 -1
  25. package/dist/cli/theme/terminal-capability.js +20 -4
  26. package/dist/cli/theme/theme-doc.d.ts +22 -0
  27. package/dist/cli/theme/theme-doc.js +97 -0
  28. package/dist/cli/theme/theme-registry.js +39 -18
  29. package/dist/cli/theme/tier-explain.d.ts +21 -0
  30. package/dist/cli/theme/tier-explain.js +60 -0
  31. package/dist/cli/ui/green-rain-renderer.js +3 -2
  32. package/dist/cli/ui/neon-grid-renderer.js +4 -3
  33. package/dist/cli/ui/rust-forge-renderer.js +17 -3
  34. package/dist/cli/ui/system24-renderer.d.ts +18 -1
  35. package/dist/cli/ui/system24-renderer.js +39 -16
  36. package/dist/cli/v2/chat-repl.js +3 -1
  37. package/dist/cli/v2/cli-v2-skeleton.d.ts +6 -0
  38. package/dist/cli/v2/cli-v2-skeleton.js +121 -9
  39. package/dist/cli/v2/interactive-prompt.d.ts +1 -0
  40. package/dist/cli/v2/interactive-prompt.js +6 -2
  41. package/dist/commands/chat/core.js +2 -2
  42. package/dist/commands/chat/runtime.js +2 -0
  43. package/dist/commands/chat/utils.js +12 -4
  44. package/dist/commands/cockpit/render.js +102 -64
  45. package/dist/commands/cockpit/utils.d.ts +2 -0
  46. package/dist/commands/design.js +18 -16
  47. package/dist/commands/doctor/checks.d.ts +0 -1
  48. package/dist/commands/doctor/checks.js +1 -36
  49. package/dist/commands/doctor/report.js +0 -11
  50. package/dist/commands/init/content.d.ts +1 -1
  51. package/dist/commands/init/content.js +3 -11
  52. package/dist/commands/init.js +2 -2
  53. package/dist/commands/mcp/config.d.ts +32 -0
  54. package/dist/commands/mcp/config.js +264 -0
  55. package/dist/commands/mcp/doctor-fix.d.ts +12 -0
  56. package/dist/commands/mcp/doctor-fix.js +261 -0
  57. package/dist/commands/mcp/doctor.d.ts +55 -0
  58. package/dist/commands/mcp/doctor.js +401 -0
  59. package/dist/commands/mcp/list.d.ts +1 -0
  60. package/dist/commands/mcp/list.js +56 -0
  61. package/dist/commands/mcp/shared.d.ts +47 -0
  62. package/dist/commands/mcp/shared.js +192 -0
  63. package/dist/commands/mcp/test.d.ts +5 -0
  64. package/dist/commands/mcp/test.js +457 -0
  65. package/dist/commands/mcp.d.ts +8 -104
  66. package/dist/commands/mcp.js +5 -1603
  67. package/dist/contracts/proof.d.ts +1 -1
  68. package/dist/goal/control-loop.d.ts +15 -0
  69. package/dist/goal/control-loop.js +39 -5
  70. package/dist/hud/render.d.ts +2 -0
  71. package/dist/hud/render.js +7 -1
  72. package/dist/mcp/secret-scanner.d.ts +6 -0
  73. package/dist/mcp/secret-scanner.js +44 -11
  74. package/dist/memory/graph-delta-log.d.ts +137 -0
  75. package/dist/memory/graph-delta-log.js +650 -0
  76. package/dist/memory/graph-viewer.js +13 -32
  77. package/dist/memory/local-graph-memory-store.d.ts +36 -3
  78. package/dist/memory/local-graph-memory-store.js +255 -10
  79. package/dist/orchestration/log-streamer.js +13 -8
  80. package/dist/orchestration/routing.d.ts +4 -0
  81. package/dist/orchestration/routing.js +28 -3
  82. package/dist/orchestration/state-persister.js +8 -1
  83. package/dist/providers/anthropic-messages-runner.d.ts +17 -0
  84. package/dist/providers/anthropic-messages-runner.js +197 -0
  85. package/dist/providers/model-registry.d.ts +1 -1
  86. package/dist/providers/model-registry.js +37 -3
  87. package/dist/providers/provider-runtime.js +27 -11
  88. package/dist/providers/thinking-levels.js +4 -1
  89. package/dist/providers/types.d.ts +3 -3
  90. package/dist/runtime/contracts/weakness-remediation.d.ts +9 -1
  91. package/dist/runtime/contracts/weakness-remediation.js +9 -1
  92. package/dist/runtime/router-v2-scoring.d.ts +8 -3
  93. package/dist/runtime/router-v2-scoring.js +27 -22
  94. package/dist/runtime/runtime-router.d.ts +5 -0
  95. package/dist/runtime/runtime-router.js +40 -8
  96. package/dist/runtime/sandbox-profile.d.ts +29 -0
  97. package/dist/runtime/sandbox-profile.js +101 -0
  98. package/dist/runtime/slash-commands.js +300 -2
  99. package/dist/runtime/tool-dispatch-contracts.d.ts +15 -0
  100. package/dist/runtime/tool-dispatch-contracts.js +12 -0
  101. package/dist/runtime/weights-config.d.ts +90 -0
  102. package/dist/runtime/weights-config.js +249 -0
  103. package/dist/schema/envelope.schema.d.ts +18 -18
  104. package/dist/schema/evidence.schema.d.ts +2 -2
  105. package/dist/schema/proof-bundle.schema.d.ts +6 -6
  106. package/dist/schema/proof-bundle.schema.js +0 -1
  107. package/dist/schema/provider.schema.d.ts +2 -2
  108. package/dist/schema/run-manifest.schema.d.ts +16 -16
  109. package/dist/theme/ansi.js +9 -2
  110. package/dist/theme/extended-palette.d.ts +85 -0
  111. package/dist/theme/extended-palette.js +108 -0
  112. package/dist/theme/layout.js +29 -8
  113. package/dist/theme/metrics.js +5 -5
  114. package/dist/theme/parallel.js +25 -15
  115. package/dist/ui/omk-sigil.js +5 -14
  116. package/dist/ui/omk-working-sweep.js +5 -15
  117. package/dist/util/chat-agent-mode.d.ts +11 -0
  118. package/dist/util/chat-agent-mode.js +41 -8
  119. package/dist/util/chat-cockpit.d.ts +9 -0
  120. package/dist/util/chat-cockpit.js +77 -20
  121. package/dist/util/fs/core.d.ts +5 -0
  122. package/dist/util/fs/core.js +35 -0
  123. package/dist/util/fs/internal.d.ts +14 -0
  124. package/dist/util/fs/internal.js +169 -0
  125. package/dist/util/fs/kimi-sync.d.ts +41 -0
  126. package/dist/util/fs/kimi-sync.js +495 -0
  127. package/dist/util/fs/logo.d.ts +2 -0
  128. package/dist/util/fs/logo.js +78 -0
  129. package/dist/util/fs/manifest.d.ts +6 -0
  130. package/dist/util/fs/manifest.js +40 -0
  131. package/dist/util/fs/mcp-diagnose.d.ts +15 -0
  132. package/dist/util/fs/mcp-diagnose.js +204 -0
  133. package/dist/util/fs/mcp-runtime-config.d.ts +23 -0
  134. package/dist/util/fs/mcp-runtime-config.js +412 -0
  135. package/dist/util/fs/paths.d.ts +13 -0
  136. package/dist/util/fs/paths.js +82 -0
  137. package/dist/util/fs/preflight.d.ts +27 -0
  138. package/dist/util/fs/preflight.js +271 -0
  139. package/dist/util/fs.d.ts +8 -128
  140. package/dist/util/fs.js +8 -1757
  141. package/dist/util/terminal-layout.js +5 -1
  142. package/dist/util/version.js +2 -1
  143. package/docs/2026-06-11/critical-issues.md +20 -0
  144. package/docs/2026-06-11/improvements.md +14 -0
  145. package/docs/2026-06-11/init-checklist.md +25 -0
  146. package/docs/2026-06-11/plan.md +20 -0
  147. package/docs/ARCHITECTURE_ANALYSIS_CODEGRAPH.md +223 -0
  148. package/docs/adr/0001-no-native-rust-lane.md +52 -0
  149. package/docs/codex-oauth-setup.md +14 -0
  150. package/docs/decisions/2026-06-10-git-history-bloat.md +61 -0
  151. package/docs/decisions/ADR-theme-dark-only-assets.md +43 -0
  152. package/docs/getting-started.md +1 -1
  153. package/docs/headroom-omk-final-summary.md +188 -0
  154. package/docs/headroom-omk-integration.md +394 -0
  155. package/docs/headroom-omk-setup-guide.md +422 -0
  156. package/docs/headroom-omk-usage-examples.md +371 -0
  157. package/docs/provider-maturity.md +1 -1
  158. package/docs/versioning.md +3 -3
  159. package/package.json +10 -10
  160. package/readmeasset/.npmignore +2 -0
  161. package/readmeasset/ASSET_INDEX.md +7 -5
  162. package/readmeasset/ASSET_PROVENANCE.md +21 -5
  163. package/readmeasset/omk-control-surfaces.svg +55 -0
  164. package/readmeasset/omk-core-loop.svg +1 -0
  165. package/readmeasset/omk-logo-mark.svg +1 -0
  166. package/readmeasset/omk-release-assertions.svg +51 -0
  167. package/dist/native/linux-x64/omk-safety +0 -0
  168. package/dist/util/native-safety.d.ts +0 -28
  169. package/dist/util/native-safety.js +0 -118
@@ -0,0 +1,197 @@
1
+ import { renderPromptDigest } from "../goal/prompt-digest.js";
2
+ import { inferNodeRisk } from "./router.js";
3
+ import { contextPreflightErrorMessage, preflightProviderMessages, } from "./context-preflight.js";
4
+ export function createAnthropicMessagesReadOnlyTaskRunner(options) {
5
+ let currentOnThinking;
6
+ const runner = {
7
+ get onThinking() {
8
+ return currentOnThinking;
9
+ },
10
+ set onThinking(fn) {
11
+ currentOnThinking = fn;
12
+ },
13
+ fork(onThinking) {
14
+ const forked = createAnthropicMessagesReadOnlyTaskRunner(options);
15
+ forked.onThinking = onThinking;
16
+ return forked;
17
+ },
18
+ async run(node, env, signal) {
19
+ const risk = inferNodeRisk(node);
20
+ const advisoryMode = env.OMK_PROVIDER_AUTHORITY === "advisory" && risk === "write";
21
+ if (risk !== "read" && !advisoryMode) {
22
+ return deny(node, `${options.provider} runner is read-only/advisory; write/shell/merge authority stays on the authority provider`);
23
+ }
24
+ if (node.routing?.requiresToolCalling === true || node.routing?.requiresMcp === true) {
25
+ return deny(node, `${options.provider} runner does not receive tool or MCP authority`);
26
+ }
27
+ if (!options.apiKey) {
28
+ return deny(node, `${options.provider} API key is missing (${options.apiKeyEnv ?? "provider env"}); provider-neutral authority fallback is required`);
29
+ }
30
+ const model = env.OMK_PROVIDER_MODEL || options.model;
31
+ currentOnThinking?.(`${options.provider} ${env.OMK_PROVIDER_AUTHORITY ?? "direct"} worker: ${node.name}`);
32
+ try {
33
+ const messages = [
34
+ {
35
+ role: "system",
36
+ content: [
37
+ `You are a ${options.provider} read-only/advisory worker inside OMK.`,
38
+ "OMK and the configured authority provider are the root orchestrator and final authority.",
39
+ "Do not claim file writes, shell execution, secret access, MCP access, or merge authority.",
40
+ advisoryMode ? "For this file-affecting node, provide advisory strategy only." : "",
41
+ "Return concise findings, evidence, risks, and recommended authority-provider follow-up.",
42
+ ].filter(Boolean).join(" "),
43
+ },
44
+ { role: "user", content: buildNodePrompt(node, env, options) },
45
+ ];
46
+ const preflight = await preflightProviderMessages(messages, {
47
+ provider: options.provider,
48
+ model,
49
+ contextWindow: options.contextWindow,
50
+ reservedOutputTokens: options.reservedOutputTokens ?? options.maxTokens ?? 4096,
51
+ safetyMarginTokens: options.safetyMarginTokens,
52
+ runId: env.OMK_RUN_ID,
53
+ nodeId: node.id,
54
+ projectRoot: env.OMK_PROJECT_ROOT,
55
+ });
56
+ if (!preflight.ok) {
57
+ return {
58
+ success: false,
59
+ exitCode: 1,
60
+ stdout: "",
61
+ stderr: contextPreflightErrorMessage(preflight.report),
62
+ metadata: { contextPreflight: preflight.report },
63
+ };
64
+ }
65
+ const content = await completeMessages({
66
+ ...options,
67
+ model,
68
+ messages: preflight.messages,
69
+ signal,
70
+ });
71
+ return {
72
+ success: true,
73
+ exitCode: 0,
74
+ stdout: `[${node.id}:${node.role}:${options.provider}] ${content}\n`,
75
+ stderr: "",
76
+ metadata: preflight.report.compacted ? { contextPreflight: preflight.report } : undefined,
77
+ };
78
+ }
79
+ catch (err) {
80
+ return {
81
+ success: false,
82
+ exitCode: 1,
83
+ stdout: "",
84
+ stderr: err instanceof Error ? err.message : String(err),
85
+ };
86
+ }
87
+ },
88
+ };
89
+ return runner;
90
+ }
91
+ async function completeMessages(options) {
92
+ const credential = options.apiKey?.trim();
93
+ if (!credential)
94
+ throw new Error(`${options.provider} API key is missing`);
95
+ const baseUrl = options.baseUrl.replace(/\/+$/, "");
96
+ const fetchImpl = options.fetchImpl ?? fetch;
97
+ // Anthropic Messages API separates system messages from the messages array
98
+ const systemMessages = [];
99
+ const chatMessages = options.messages
100
+ .filter((m) => {
101
+ if (m.role === "system") {
102
+ systemMessages.push(m.content);
103
+ return false;
104
+ }
105
+ return true;
106
+ })
107
+ .map((m) => ({ role: m.role, content: m.content }));
108
+ const body = {
109
+ model: options.model,
110
+ max_tokens: options.maxTokens ?? 4096,
111
+ messages: chatMessages,
112
+ };
113
+ if (systemMessages.length > 0) {
114
+ body.system = systemMessages.join("\n");
115
+ }
116
+ const response = await fetchImpl(`${baseUrl}/v1/messages`, {
117
+ method: "POST",
118
+ headers: {
119
+ "content-type": "application/json",
120
+ "x-api-key": credential,
121
+ "anthropic-version": "2023-06-01",
122
+ ...safeExtraHeaders(options.headers),
123
+ },
124
+ body: JSON.stringify(body),
125
+ signal: options.signal,
126
+ });
127
+ const text = await response.text();
128
+ if (!response.ok) {
129
+ throw new Error(`${options.provider} HTTP ${response.status}: ${sanitizeProviderErrorText(text, credential)}`);
130
+ }
131
+ const parsed = JSON.parse(text);
132
+ const contentBlock = parsed.content?.find((c) => c.type === "text" || typeof c.text === "string");
133
+ const content = contentBlock?.text;
134
+ if (typeof content !== "string")
135
+ throw new Error(`${options.provider} response missing assistant content`);
136
+ return content;
137
+ }
138
+ function buildNodePrompt(node, env, options) {
139
+ return [
140
+ options.promptPrefix?.trim(),
141
+ `DAG node: ${node.id}`,
142
+ `Name: ${node.name}`,
143
+ `Role: ${node.role}`,
144
+ `Task type: ${env.OMK_TASK_TYPE ?? "general"}`,
145
+ `Complexity: ${env.OMK_COMPLEXITY ?? "moderate"}`,
146
+ `Provider: ${options.provider}`,
147
+ `Model: ${env.OMK_PROVIDER_MODEL ?? options.model}`,
148
+ `Authority: ${env.OMK_PROVIDER_AUTHORITY ?? "direct"}`,
149
+ `Provider route reason: ${env.OMK_PROVIDER_ROUTE_REASON ?? ""}`,
150
+ `Routing rationale: ${node.routing?.rationale ?? ""}`,
151
+ renderPromptDigest("Goal context digest from authority provider", env.OMK_GOAL_CONTEXT ?? env.OMK_GOAL, {
152
+ maxKeywords: 18,
153
+ maxPhrases: 3,
154
+ }),
155
+ renderList("Skills visible to authority provider", node.routing?.skills ?? []),
156
+ renderList("MCP hints visible to authority provider only", node.routing?.mcpServers ?? [], { showWhenEmpty: true }),
157
+ renderList("Tool hints visible to authority provider only", node.routing?.tools ?? [], { showWhenEmpty: true }),
158
+ "Required output:",
159
+ "- Summary",
160
+ "- Evidence or file/symbol references if known",
161
+ "- Risks/unknowns",
162
+ "- Recommended authority-provider follow-up",
163
+ ].filter((section) => Boolean(section)).join("\n").trim() || `Analyze DAG node ${node.id}.`;
164
+ }
165
+ function renderList(title, items, options = {}) {
166
+ if (items.length === 0)
167
+ return options.showWhenEmpty === true ? `${title}:\n- none` : "";
168
+ return `${title}:\n${items.map((item) => `- ${item}`).join("\n")}`;
169
+ }
170
+ function safeExtraHeaders(headers) {
171
+ const safe = {};
172
+ for (const [name, value] of Object.entries(headers ?? {})) {
173
+ const normalized = name.trim();
174
+ const lower = normalized.toLowerCase();
175
+ const trimmedValue = value?.trim();
176
+ if (!normalized || !trimmedValue)
177
+ continue;
178
+ if (lower === "x-api-key" || lower === "content-type" || lower === "anthropic-version")
179
+ continue;
180
+ safe[normalized] = trimmedValue;
181
+ }
182
+ return safe;
183
+ }
184
+ function sanitizeProviderErrorText(text, credential) {
185
+ return text
186
+ .replaceAll(credential, "[redacted]")
187
+ .replace(/x-api-key\s+\S+/g, "x-api-key [redacted]")
188
+ .slice(0, 500);
189
+ }
190
+ function deny(node, reason) {
191
+ return {
192
+ success: false,
193
+ exitCode: 1,
194
+ stdout: "",
195
+ stderr: `[${node.id}:${node.role}] ${reason}`,
196
+ };
197
+ }
@@ -1,6 +1,6 @@
1
1
  import { type DeepSeekConfigPathOptions } from "./deepseek/deepseek-config.js";
2
2
  import type { ProviderAuthMethod, ProviderId, ProviderKind, ProviderModelRef, ProviderPlanKind, ProviderPolicy, ProviderProfileType, ProviderWireApi } from "./types.js";
3
- export declare const KNOWN_PROVIDER_IDS: readonly ["mimo", "kimi", "deepseek", "qwen", "codex", "openrouter"];
3
+ export declare const KNOWN_PROVIDER_IDS: readonly ["mimo", "kimi", "deepseek", "qwen", "codex", "openrouter", "anthropic"];
4
4
  export declare const QWEN_DASHSCOPE_COMPAT_BASE_URL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
5
5
  export declare const OPENROUTER_COMPAT_BASE_URL = "https://openrouter.ai/api/v1";
6
6
  export interface ProviderRegistryEntry {
@@ -1,10 +1,34 @@
1
1
  import { runShell } from "../util/shell.js";
2
2
  import { readOmkProvidersConfig, writeOmkProvidersConfig, } from "./deepseek/deepseek-config.js";
3
3
  import { DEFAULT_AUTHORITY_PROVIDER } from "./types.js";
4
- export const KNOWN_PROVIDER_IDS = ["mimo", "kimi", "deepseek", "qwen", "codex", "openrouter"];
4
+ export const KNOWN_PROVIDER_IDS = ["mimo", "kimi", "deepseek", "qwen", "codex", "openrouter", "anthropic"];
5
5
  export const QWEN_DASHSCOPE_COMPAT_BASE_URL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
6
6
  export const OPENROUTER_COMPAT_BASE_URL = "https://openrouter.ai/api/v1";
7
7
  const DEFAULT_PROVIDER_CONFIGS = {
8
+ anthropic: {
9
+ enabled: false,
10
+ kind: "openai-compatible",
11
+ baseUrl: "https://api.anthropic.com",
12
+ apiKeyEnv: "ANTHROPIC_API_KEY",
13
+ defaultModel: "claude-fable-5",
14
+ aliases: {
15
+ default: "claude-fable-5",
16
+ fable: "claude-fable-5",
17
+ "fable-5": "claude-fable-5",
18
+ "claude-fable-5": "claude-fable-5",
19
+ "opus-4-8": "claude-opus-4-8",
20
+ "claude-opus-4-8": "claude-opus-4-8",
21
+ },
22
+ capabilities: ["read", "review", "qa", "research", "advisory"],
23
+ contextWindow: 200_000,
24
+ reservedOutputTokens: 8192,
25
+ wireApi: "anthropic-messages",
26
+ auth: { method: "oauth" },
27
+ profileType: "runtime",
28
+ planKind: "openai-api",
29
+ routing: "advisory",
30
+ headers: { "anthropic-version": "2023-06-01" },
31
+ },
8
32
  mimo: {
9
33
  enabled: true,
10
34
  kind: "openai-compatible",
@@ -108,6 +132,10 @@ const DEFAULT_PROVIDER_CONFIGS = {
108
132
  auto: "openrouter/auto",
109
133
  "openrouter-default": "openrouter/auto",
110
134
  "openrouter/auto": "openrouter/auto",
135
+ fable: "anthropic/claude-fable-5",
136
+ "fable-5": "anthropic/claude-fable-5",
137
+ "claude-fable-5": "anthropic/claude-fable-5",
138
+ "anthropic/claude-fable-5": "anthropic/claude-fable-5",
111
139
  },
112
140
  capabilities: ["read", "research", "review", "qa", "advisory"],
113
141
  contextWindow: 128_000,
@@ -146,6 +174,8 @@ export function normalizeProviderId(value) {
146
174
  return "qwen";
147
175
  if (lower === "openrouter" || lower === "openrouter-ai" || lower === "claude" || lower === "anthropic")
148
176
  return "openrouter";
177
+ if (lower === "anthropic-api" || lower === "anthropic-direct" || lower === "claude-api")
178
+ return "anthropic";
149
179
  return lower;
150
180
  }
151
181
  export function parseProviderModelArg(value) {
@@ -182,7 +212,7 @@ export function parseProviderModelArg(value) {
182
212
  return withThinking({ provider: "codex", model });
183
213
  if (model.startsWith("deepseek-v4-"))
184
214
  return withThinking({ provider: "deepseek", model });
185
- if (model.startsWith("claude-") || model.startsWith("gpt-"))
215
+ if (model.startsWith("claude-") || model.startsWith("gpt-") || model.startsWith("anthropic/"))
186
216
  return withThinking({ provider: "openrouter", model });
187
217
  return withThinking({ model });
188
218
  }
@@ -202,8 +232,12 @@ export function normalizeModelAlias(value) {
202
232
  return "claude-sonnet";
203
233
  if (lower === "opus")
204
234
  return "claude-opus";
235
+ if (lower === "opus-4-8" || lower === "claude-opus-4-8")
236
+ return "claude-opus-4-8";
205
237
  if (lower === "haiku")
206
238
  return "claude-haiku";
239
+ if (lower === "fable" || lower === "fable-5" || lower === "claude-fable-5")
240
+ return "anthropic/claude-fable-5";
207
241
  if (lower === "flash" || lower === "thinking" || lower === "non-thinking" || lower === "reasoner")
208
242
  return "deepseek-v4-flash";
209
243
  if (lower === "pro")
@@ -403,7 +437,7 @@ function isKnownProviderId(value) {
403
437
  return KNOWN_PROVIDER_IDS.includes(value);
404
438
  }
405
439
  function normalizeWireApi(value) {
406
- if (value === "kimi-native" || value === "openai-chat-completions" || value === "openai-responses" || value === "external-cli")
440
+ if (value === "kimi-native" || value === "openai-chat-completions" || value === "openai-responses" || value === "anthropic-messages" || value === "external-cli")
407
441
  return value;
408
442
  return undefined;
409
443
  }
@@ -9,6 +9,7 @@ import { DEFAULT_AUTHORITY_PROVIDER } from "./types.js";
9
9
  import { createDeepSeekProvider } from "./deepseek-provider.js";
10
10
  import { createProviderRouter } from "./provider-router.js";
11
11
  import { createOpenAICompatibleReadOnlyTaskRunner } from "./openai-compatible-runner.js";
12
+ import { createAnthropicMessagesReadOnlyTaskRunner } from "./anthropic-messages-runner.js";
12
13
  import { createCodexCliAdvisoryTaskRunner } from "./codex-cli-runner.js";
13
14
  import { providerDoctorStatus, readProviderRegistry } from "./model-registry.js";
14
15
  import { createContextBroker } from "../runtime/context-broker.js";
@@ -70,17 +71,32 @@ export async function createProviderBackedTaskRunner(options) {
70
71
  continue;
71
72
  const apiKey = entry.apiKeyEnv ? process.env[entry.apiKeyEnv] : undefined;
72
73
  if (apiKey && entry.baseUrl) {
73
- providerRunners[entry.id] = createOpenAICompatibleReadOnlyTaskRunner({
74
- provider: entry.id,
75
- baseUrl: entry.baseUrl,
76
- apiKey,
77
- apiKeyEnv: entry.apiKeyEnv,
78
- model: entry.defaultModel,
79
- promptPrefix: options.deepseekPromptPrefix,
80
- headers: openAICompatibleHeaders(entry, process.env),
81
- contextWindow: entry.contextWindow,
82
- reservedOutputTokens: entry.reservedOutputTokens,
83
- });
74
+ if (entry.wireApi === "anthropic-messages") {
75
+ providerRunners[entry.id] = createAnthropicMessagesReadOnlyTaskRunner({
76
+ provider: entry.id,
77
+ baseUrl: entry.baseUrl,
78
+ apiKey,
79
+ apiKeyEnv: entry.apiKeyEnv,
80
+ model: entry.defaultModel,
81
+ promptPrefix: options.deepseekPromptPrefix,
82
+ headers: openAICompatibleHeaders(entry, process.env),
83
+ contextWindow: entry.contextWindow,
84
+ reservedOutputTokens: entry.reservedOutputTokens,
85
+ });
86
+ }
87
+ else {
88
+ providerRunners[entry.id] = createOpenAICompatibleReadOnlyTaskRunner({
89
+ provider: entry.id,
90
+ baseUrl: entry.baseUrl,
91
+ apiKey,
92
+ apiKeyEnv: entry.apiKeyEnv,
93
+ model: entry.defaultModel,
94
+ promptPrefix: options.deepseekPromptPrefix,
95
+ headers: openAICompatibleHeaders(entry, process.env),
96
+ contextWindow: entry.contextWindow,
97
+ reservedOutputTokens: entry.reservedOutputTokens,
98
+ });
99
+ }
84
100
  providerModels[entry.id] = providerModelDefault(entry);
85
101
  }
86
102
  else if (providerPolicy === entry.id) {
@@ -4,14 +4,17 @@ const PROVIDER_LEVELS = {
4
4
  mimo: ["medium", "high", "xhigh", "max"],
5
5
  deepseek: ["off", "high", "xhigh", "max"],
6
6
  qwen: ["off", "medium", "high", "max"],
7
+ anthropic: ["minimal", "low", "medium", "high", "xhigh", "max"],
7
8
  openrouter: ["minimal", "low", "medium", "high", "xhigh", "max"],
9
+ duckcoding: ["minimal", "low", "medium", "high", "xhigh", "max"],
8
10
  codex: ["minimal", "low", "medium", "high", "xhigh"],
9
11
  };
10
12
  const MODEL_LEVELS = [
11
13
  { pattern: /kimi|mimo/i, levels: PROVIDER_LEVELS.kimi },
12
14
  { pattern: /deepseek/i, levels: PROVIDER_LEVELS.deepseek },
13
15
  { pattern: /qwen/i, levels: PROVIDER_LEVELS.qwen },
14
- { pattern: /claude|sonnet|opus|haiku|gpt|openrouter/i, levels: PROVIDER_LEVELS.openrouter },
16
+ { pattern: /duck[-_ ]?coding/i, levels: PROVIDER_LEVELS.duckcoding },
17
+ { pattern: /fable|claude|sonnet|opus|haiku|anthropic|gpt|openrouter/i, levels: PROVIDER_LEVELS.anthropic },
15
18
  { pattern: /codex/i, levels: PROVIDER_LEVELS.codex },
16
19
  ];
17
20
  export function thinkingLevelsFor(provider, model) {
@@ -1,5 +1,5 @@
1
1
  import type { TaskResult, TaskRunner } from "../contracts/orchestration.js";
2
- export type KnownProviderId = "mimo" | "kimi" | "deepseek" | "codex" | "qwen" | "openrouter";
2
+ export type KnownProviderId = "mimo" | "kimi" | "deepseek" | "codex" | "qwen" | "openrouter" | "anthropic";
3
3
  export type ProviderId = KnownProviderId | (string & {});
4
4
  export type ProviderPolicy = "auto" | ProviderId;
5
5
  export declare const DEFAULT_AUTHORITY_PROVIDER: ProviderId;
@@ -12,7 +12,7 @@ export declare function resolveRuntimeFallbackChain(availableRuntimes?: readonly
12
12
  export type ProviderRisk = "read" | "write" | "shell" | "merge";
13
13
  export type ProviderComplexity = "simple" | "moderate" | "complex";
14
14
  export type ProviderKind = "kimi-native" | "openai-compatible" | "external-cli" | "codex-cli" | "local";
15
- export type ProviderWireApi = "kimi-native" | "openai-chat-completions" | "openai-responses" | "external-cli";
15
+ export type ProviderWireApi = "kimi-native" | "openai-chat-completions" | "openai-responses" | "anthropic-messages" | "external-cli";
16
16
  export type ProviderAuthMethod = "api-key-env" | "oauth" | "external-cli" | "none";
17
17
  export type ProviderProfileType = "runtime" | "compatibility";
18
18
  export type ProviderPlanKind = "runtime" | "openai-api" | "chatgpt-plan" | "claude-code-plan" | "gemini-cli-plan" | "qwen-coding-plan" | "openrouter-credits" | "openrouter-byok";
@@ -85,7 +85,7 @@ export interface ProviderRouteDecision {
85
85
  /** Decision trace entries for forensic replay */
86
86
  trace?: import("../contracts/replay.js").DecisionTraceEntry[];
87
87
  }
88
- export type ProviderFailureKind = "availability" | "transient" | "policy" | "quota" | "unknown";
88
+ export type ProviderFailureKind = "availability" | "transient" | "policy" | "quota" | "refusal" | "unknown";
89
89
  export interface ProviderAvailability {
90
90
  provider: ProviderId;
91
91
  available: boolean;
@@ -19,7 +19,15 @@ export declare const BETA_PRIOR_ALPHA0 = 1;
19
19
  export declare const BETA_PRIOR_BETA0 = 1;
20
20
  /** Default public surface budget K (max items). */
21
21
  export declare const SURFACE_BUDGET_K = 8;
22
- /** Algorithm 8 release gate weights. */
22
+ /**
23
+ * Algorithm 8 release gate weights — raw (unnormalized) historical values.
24
+ *
25
+ * @deprecated Source of truth is the omk.weights.v1 contract
26
+ * (schemas/omk.weights.v1.json, mirrored by DEFAULT_WEIGHTS in
27
+ * src/runtime/weights-config.ts). The release promotion gate now consumes
28
+ * normalized effective weights via releaseGateEffective(); these raw values
29
+ * (positive Σ = 1.05) are kept only for backward compatibility.
30
+ */
23
31
  export declare const RELEASE_GATE_WEIGHTS: {
24
32
  readonly ci: 0.15;
25
33
  readonly build: 0.1;
@@ -21,7 +21,15 @@ export const BETA_PRIOR_BETA0 = 1;
21
21
  /** Default public surface budget K (max items). */
22
22
  export const SURFACE_BUDGET_K = 8;
23
23
  // ── Phase 5 Release Promotion Gate ──────────────────────────────
24
- /** Algorithm 8 release gate weights. */
24
+ /**
25
+ * Algorithm 8 release gate weights — raw (unnormalized) historical values.
26
+ *
27
+ * @deprecated Source of truth is the omk.weights.v1 contract
28
+ * (schemas/omk.weights.v1.json, mirrored by DEFAULT_WEIGHTS in
29
+ * src/runtime/weights-config.ts). The release promotion gate now consumes
30
+ * normalized effective weights via releaseGateEffective(); these raw values
31
+ * (positive Σ = 1.05) are kept only for backward compatibility.
32
+ */
25
33
  export const RELEASE_GATE_WEIGHTS = {
26
34
  ci: 0.15,
27
35
  build: 0.10,
@@ -1,9 +1,14 @@
1
1
  /**
2
2
  * Router V2 Scoring Engine — Bayesian-smoothed evidence calibration (Algorithm 6).
3
3
  *
4
- * Composite formula:
5
- * 0.25*E + 0.15*conf + 0.20*cap + 0.15*mat + 0.10*lat + 0.10*cost
6
- * - 0.15*pen - 0.10*blast
4
+ * Composite formula (omk.weights.v1 routerV2Composite vector, normalize:true):
5
+ * ŵ_E*E + ŵ_conf*conf + ŵ_cap*cap + ŵ_mat*mat + ŵ_lat*lat + ŵ_cost*cost
6
+ * - p̂_fail*pen - p̂_blast*blast
7
+ *
8
+ * Historical raw weights (0.25/0.15/0.20/0.15/0.10/0.10 − 0.15/0.10) had a
9
+ * positive Σ = 0.95; normalization divides weights AND penalties by the same
10
+ * factor (1/0.95), a pure uniform scaling. The composite is ranking-only
11
+ * (no absolute threshold compares it), so rankings are identical.
7
12
  */
8
13
  import type { AgentRuntime } from "./agent-runtime.js";
9
14
  import type { EvidenceHistoryEntry, NodeIntent, RuntimeScoreV2, RouterV2Options, RouterV2ScoringEngine, BlastRadiusParams } from "./contracts/router-v2.js";
@@ -1,24 +1,27 @@
1
1
  /**
2
2
  * Router V2 Scoring Engine — Bayesian-smoothed evidence calibration (Algorithm 6).
3
3
  *
4
- * Composite formula:
5
- * 0.25*E + 0.15*conf + 0.20*cap + 0.15*mat + 0.10*lat + 0.10*cost
6
- * - 0.15*pen - 0.10*blast
4
+ * Composite formula (omk.weights.v1 routerV2Composite vector, normalize:true):
5
+ * ŵ_E*E + ŵ_conf*conf + ŵ_cap*cap + ŵ_mat*mat + ŵ_lat*lat + ŵ_cost*cost
6
+ * - p̂_fail*pen - p̂_blast*blast
7
+ *
8
+ * Historical raw weights (0.25/0.15/0.20/0.15/0.10/0.10 − 0.15/0.10) had a
9
+ * positive Σ = 0.95; normalization divides weights AND penalties by the same
10
+ * factor (1/0.95), a pure uniform scaling. The composite is ranking-only
11
+ * (no absolute threshold compares it), so rankings are identical.
7
12
  */
8
13
  import { computeBlastRadiusPenalty } from "./blast-radius.js";
14
+ import { intentCapabilityWeights, routerV2CompositeEffective } from "./weights-config.js";
9
15
  const ALPHA_0 = 1;
10
16
  const BETA_0 = 1;
11
- const INTENT_CAPABILITY_WEIGHTS = {
12
- research: [["read", 0.35], ["review", 0.2], ["toolCalling", 0.15], ["vision", 0.1]],
13
- planning: [["read", 0.3], ["review", 0.2], ["toolCalling", 0.15]],
14
- coding: [["write", 0.3], ["patch", 0.25], ["shell", 0.15], ["toolCalling", 0.1]],
15
- debugging: [["read", 0.2], ["write", 0.2], ["patch", 0.2], ["shell", 0.15], ["toolCalling", 0.1]],
16
- refactor: [["write", 0.25], ["patch", 0.25], ["review", 0.15], ["toolCalling", 0.1]],
17
- review: [["review", 0.35], ["read", 0.25], ["toolCalling", 0.1]],
18
- "test-generation": [["write", 0.25], ["patch", 0.2], ["review", 0.15], ["toolCalling", 0.1]],
19
- documentation: [["read", 0.25], ["write", 0.15], ["review", 0.15], ["toolCalling", 0.1]],
20
- "shell-operation": [["shell", 0.4], ["read", 0.15], ["write", 0.1]],
21
- };
17
+ /**
18
+ * Intent capability emphasis vectors, sourced verbatim from the
19
+ * omk.weights.v1 intentCapability vector family (normalize:false — sub-unit
20
+ * sums are intentional feature-fit emphasis templates).
21
+ */
22
+ const INTENT_CAPABILITY_WEIGHTS = intentCapabilityWeights();
23
+ /** Effective (normalized) composite weights/penalties raw values ÷ 0.95. */
24
+ const COMPOSITE_EFFECTIVE = routerV2CompositeEffective();
22
25
  function runtimeCapabilityEnabled(capabilities, capability) {
23
26
  if (capability === "toolCalling") {
24
27
  return capabilities.toolCalling === true || capabilities.supportsToolCalling === true;
@@ -93,14 +96,16 @@ export function createRouterV2ScoringEngine(options = {}, blastRadiusFn = comput
93
96
  const latencyScore = computeLatencyScore(runtime);
94
97
  const costScore = computeCostScore(runtime);
95
98
  const blastRadiusPenalty = enableBlastRadius ? blastRadiusFn(blastRadiusParams) : 0;
96
- const composite = 0.25 * bayesianEvidenceScore +
97
- 0.15 * confidence +
98
- 0.20 * capabilityFit +
99
- 0.15 * maturityScore +
100
- 0.10 * latencyScore +
101
- 0.10 * costScore -
102
- 0.15 * recentFailurePenalty -
103
- 0.10 * blastRadiusPenalty;
99
+ const cw = COMPOSITE_EFFECTIVE.weights;
100
+ const cp = COMPOSITE_EFFECTIVE.penalties;
101
+ const composite = cw.bayesianEvidence * bayesianEvidenceScore +
102
+ cw.confidence * confidence +
103
+ cw.capabilityFit * capabilityFit +
104
+ cw.maturity * maturityScore +
105
+ cw.latency * latencyScore +
106
+ cw.cost * costScore -
107
+ cp.recentFailure * recentFailurePenalty -
108
+ cp.blastRadius * blastRadiusPenalty;
104
109
  return {
105
110
  runtimeId: runtime.id,
106
111
  bayesianEvidenceScore,
@@ -48,4 +48,9 @@ export declare function createRuntimeRouter(options?: RuntimeRouterOptions): {
48
48
  setRuntimes(nextRuntimes: AgentRuntime[]): void;
49
49
  invalidateCache: () => void;
50
50
  };
51
+ /** Test-only: deterministic capability-ordered sort using the precomputed score cache.
52
+ * Mirrors the production comparator so tests can assert identical ordering to the
53
+ * pre-change recompute-in-comparator reference. */
54
+ export declare function sortRuntimesByCapabilityScore(runtimes: readonly AgentRuntime[], intent: NodeIntent): AgentRuntime[];
55
+ export declare function computeRuntimeCapabilityScore(runtime: AgentRuntime, intent: NodeIntent): number;
51
56
  export {};
@@ -137,7 +137,8 @@ export function createRuntimeRouter(options = {}) {
137
137
  score: scores[i],
138
138
  composite: computeComposite(scores[i], r, intent),
139
139
  }));
140
- scored.sort((a, b) => compareScoredRuntimes(a, b, intent));
140
+ const capabilityScoreCache = buildCapabilityScoreCache(supporting, intent);
141
+ scored.sort((a, b) => compareScoredRuntimes(a, b, intent, capabilityScoreCache));
141
142
  const primary = scored[0].runtime;
142
143
  const fallbacks = scored.slice(1).map((s) => s.runtime);
143
144
  const bestScore = scored[0].score;
@@ -161,7 +162,10 @@ export function createRuntimeRouter(options = {}) {
161
162
  runtime: r,
162
163
  composite: computeRuntimeCapabilityScore(r, intent),
163
164
  }));
164
- scored.sort((a, b) => compareRuntimeCandidates(a.runtime, b.runtime, intent));
165
+ // Reuse the capability scores already computed above instead of recomputing
166
+ // them inside the comparator on every comparison.
167
+ const capabilityScoreCache = new Map(scored.map((s) => [s.runtime, s.composite]));
168
+ scored.sort((a, b) => compareRuntimeCandidates(a.runtime, b.runtime, intent, capabilityScoreCache));
165
169
  const primary = scored[0].runtime;
166
170
  const fallbacks = scored.slice(1).map((s) => s.runtime);
167
171
  return {
@@ -337,6 +341,7 @@ export function createRuntimeRouter(options = {}) {
337
341
  throw new UnsupportedRuntimeError(capsule, detectedRuntimeLabels(runtimes));
338
342
  }
339
343
  const preferredRuntimeIds = task.providerPolicy?.fallbackChain ?? options.fallbackChain ?? [];
344
+ const capabilityScoreCache = buildCapabilityScoreCache(candidates, intent);
340
345
  candidates.sort((a, b) => {
341
346
  const runtimeDelta = runtimePreferenceIndex(a.id, preferredRuntimeIds)
342
347
  - runtimePreferenceIndex(b.id, preferredRuntimeIds);
@@ -348,7 +353,7 @@ export function createRuntimeRouter(options = {}) {
348
353
  if (providerDelta !== 0)
349
354
  return providerDelta;
350
355
  }
351
- return compareRuntimeCandidates(a, b, intent);
356
+ return compareRuntimeCandidates(a, b, intent, capabilityScoreCache);
352
357
  });
353
358
  let lastError;
354
359
  for (const runtime of candidates) {
@@ -574,14 +579,14 @@ function computeComposite(score, runtime, intent) {
574
579
  0.15 * computeRuntimeCapabilityScore(runtime, intent) +
575
580
  0.05 * runtimePriorityScore(runtime));
576
581
  }
577
- function compareScoredRuntimes(a, b, intent) {
582
+ function compareScoredRuntimes(a, b, intent, scoreCache) {
578
583
  const compositeDelta = b.composite - a.composite;
579
584
  if (compositeDelta !== 0)
580
585
  return compositeDelta;
581
- return compareRuntimeCandidates(a.runtime, b.runtime, intent);
586
+ return compareRuntimeCandidates(a.runtime, b.runtime, intent, scoreCache);
582
587
  }
583
- function compareRuntimeCandidates(a, b, intent) {
584
- const capabilityDelta = computeRuntimeCapabilityScore(b, intent) - computeRuntimeCapabilityScore(a, intent);
588
+ function compareRuntimeCandidates(a, b, intent, scoreCache) {
589
+ const capabilityDelta = capabilityScoreFromCache(scoreCache, b, intent) - capabilityScoreFromCache(scoreCache, a, intent);
585
590
  if (capabilityDelta !== 0)
586
591
  return capabilityDelta;
587
592
  const priorityDelta = b.priority - a.priority;
@@ -589,7 +594,34 @@ function compareRuntimeCandidates(a, b, intent) {
589
594
  return priorityDelta;
590
595
  return a.id.localeCompare(b.id);
591
596
  }
592
- function computeRuntimeCapabilityScore(runtime, intent) {
597
+ // Precompute each candidate's capability score ONCE before sorting. Without this the
598
+ // comparator recomputed `computeRuntimeCapabilityScore` for both operands on every
599
+ // comparison, making the sort O(r log r * c) instead of O(r log r). Keyed by runtime
600
+ // object reference so identical ids with distinct objects can never alias.
601
+ function buildCapabilityScoreCache(runtimes, intent) {
602
+ const cache = new Map();
603
+ for (const runtime of runtimes) {
604
+ if (!cache.has(runtime))
605
+ cache.set(runtime, computeRuntimeCapabilityScore(runtime, intent));
606
+ }
607
+ return cache;
608
+ }
609
+ function capabilityScoreFromCache(cache, runtime, intent) {
610
+ const cached = cache.get(runtime);
611
+ if (cached !== undefined)
612
+ return cached;
613
+ const computed = computeRuntimeCapabilityScore(runtime, intent);
614
+ cache.set(runtime, computed);
615
+ return computed;
616
+ }
617
+ /** Test-only: deterministic capability-ordered sort using the precomputed score cache.
618
+ * Mirrors the production comparator so tests can assert identical ordering to the
619
+ * pre-change recompute-in-comparator reference. */
620
+ export function sortRuntimesByCapabilityScore(runtimes, intent) {
621
+ const cache = buildCapabilityScoreCache(runtimes, intent);
622
+ return [...runtimes].sort((a, b) => compareRuntimeCandidates(a, b, intent, cache));
623
+ }
624
+ export function computeRuntimeCapabilityScore(runtime, intent) {
593
625
  const capabilities = runtime.capabilities;
594
626
  if (capabilities == null)
595
627
  return 0;
@@ -23,3 +23,32 @@ export interface CreateRuntimeSandboxProfileOptions {
23
23
  readonly notes?: readonly string[];
24
24
  }
25
25
  export declare function createRuntimeSandboxProfile(options: CreateRuntimeSandboxProfileOptions): RuntimeSandboxProfile;
26
+ /**
27
+ * Error thrown by {@link assertWritable} when a path is outside every writable
28
+ * root. Carries only the resolved target and roots (no secret values).
29
+ */
30
+ export declare class SandboxWriteDeniedError extends Error {
31
+ readonly target: string;
32
+ readonly roots: readonly string[];
33
+ constructor(target: string, roots: readonly string[]);
34
+ }
35
+ /**
36
+ * Return true when `p` is writable under `roots`.
37
+ *
38
+ * Rules:
39
+ * - Empty/undefined `roots` => unrestricted (returns true). This preserves the
40
+ * current "metadata only" behavior; enforcement activates only when a
41
+ * sandbox profile explicitly sets non-empty `writableRoots`.
42
+ * - Both target and roots are resolved via {@link resolveRealPathBestEffort},
43
+ * which (a) collapses `.`/`..` segments and (b) realpath-resolves the deepest
44
+ * existing ancestor so a SYMLINK whose real target escapes every root is
45
+ * DENIED — even when the final write target does not exist yet.
46
+ * - Prefix matching is segment-boundary safe: target must equal a root or sit
47
+ * under `root + path.sep`, so root `/a/b` does NOT match sibling `/a/bc`.
48
+ */
49
+ export declare function isPathWritable(p: string, roots: readonly string[] | undefined): boolean;
50
+ /**
51
+ * Throw {@link SandboxWriteDeniedError} when `p` is not writable under `roots`.
52
+ * No-op when `roots` is empty/undefined (safe default => unrestricted).
53
+ */
54
+ export declare function assertWritable(p: string, roots: readonly string[] | undefined): void;