open-multi-agent-kit 0.78.8 → 0.79.3

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 (162) hide show
  1. package/AGENTS.md +131 -2
  2. package/CHANGELOG.md +32 -0
  3. package/CLAUDE.md +105 -3
  4. package/DESIGN.md +141 -117
  5. package/MATURITY.md +7 -12
  6. package/README.md +66 -347
  7. package/ROADMAP.md +33 -47
  8. package/dist/adapters/kimi/runner.js +2 -2
  9. package/dist/brand/theme.js +2 -2
  10. package/dist/cli/register-basic-commands.js +3 -2
  11. package/dist/cli/register-mcp-dag-cron-screenshot-commands.js +1 -1
  12. package/dist/cli/root.d.ts +1 -0
  13. package/dist/cli/root.js +2 -0
  14. package/dist/cli/runtime/command-bus.js +14 -1
  15. package/dist/cli/theme/terminal-capability.js +1 -1
  16. package/dist/cli/theme/theme-registry.js +90 -2
  17. package/dist/cli/ui/event.d.ts +1 -0
  18. package/dist/cli/ui/neon-grid-renderer.d.ts +8 -0
  19. package/dist/cli/ui/neon-grid-renderer.js +84 -14
  20. package/dist/cli/ui/plain-renderer.js +3 -1
  21. package/dist/cli/ui/system24-renderer.js +3 -1
  22. package/dist/cli/v2/chat-repl.d.ts +2 -1
  23. package/dist/cli/v2/chat-repl.js +58 -2
  24. package/dist/cli/v2/cli-v2-skeleton.js +7 -4
  25. package/dist/cli/v2/interactive-prompt.js +4 -3
  26. package/dist/commands/auth.js +2 -0
  27. package/dist/commands/chat/native-root-loop.d.ts +1 -0
  28. package/dist/commands/chat/native-root-loop.js +162 -13
  29. package/dist/commands/chat/runtime.js +9 -0
  30. package/dist/commands/chat/slash/commands/routing.js +31 -1
  31. package/dist/commands/chat/utils.js +3 -1
  32. package/dist/commands/cockpit/render.js +2 -1
  33. package/dist/commands/doctor/checks.js +2 -2
  34. package/dist/commands/init/core.js +2 -2
  35. package/dist/commands/init.js +2 -2
  36. package/dist/commands/mcp/config.js +38 -16
  37. package/dist/commands/mcp/doctor.js +7 -8
  38. package/dist/commands/mcp/list.js +4 -3
  39. package/dist/commands/mcp/shared.d.ts +4 -1
  40. package/dist/commands/mcp/shared.js +21 -3
  41. package/dist/commands/mcp/test.js +13 -12
  42. package/dist/commands/model.js +4 -0
  43. package/dist/commands/provider.js +5 -1
  44. package/dist/commands/version.d.ts +1 -0
  45. package/dist/commands/version.js +18 -0
  46. package/dist/contracts/command.d.ts +25 -0
  47. package/dist/contracts/command.js +1 -0
  48. package/dist/contracts/evidence-bundle.d.ts +52 -0
  49. package/dist/contracts/evidence-bundle.js +1 -0
  50. package/dist/contracts/index.d.ts +2 -0
  51. package/dist/contracts/index.js +2 -0
  52. package/dist/contracts/web-bridge.d.ts +1 -1
  53. package/dist/contracts/web-bridge.js +1 -1
  54. package/dist/evidence/bundle-validator.d.ts +7 -0
  55. package/dist/evidence/bundle-validator.js +103 -0
  56. package/dist/evidence/index.d.ts +5 -0
  57. package/dist/evidence/index.js +2 -0
  58. package/dist/evidence/proof-trust.js +2 -10
  59. package/dist/evidence/trust-calibration.d.ts +25 -0
  60. package/dist/evidence/trust-calibration.js +151 -0
  61. package/dist/mcp/omk-project-server.js +1 -1
  62. package/dist/metrics/provenance-ratio.d.ts +16 -0
  63. package/dist/metrics/provenance-ratio.js +33 -0
  64. package/dist/orchestration/dag.d.ts +17 -0
  65. package/dist/orchestration/ensemble-decision.d.ts +2 -0
  66. package/dist/orchestration/ensemble-decision.js +4 -0
  67. package/dist/orchestration/hedge-persona-weights.d.ts +25 -0
  68. package/dist/orchestration/hedge-persona-weights.js +43 -0
  69. package/dist/orchestration/parallel-ui.d.ts +1 -0
  70. package/dist/orchestration/parallel-ui.js +12 -3
  71. package/dist/orchestration/route-calibration.d.ts +27 -0
  72. package/dist/orchestration/route-calibration.js +123 -0
  73. package/dist/orchestration/routing/mcp-config.js +5 -1
  74. package/dist/orchestration/routing/types.d.ts +21 -0
  75. package/dist/orchestration/routing.d.ts +10 -0
  76. package/dist/orchestration/routing.js +89 -3
  77. package/dist/providers/model-registry.d.ts +2 -1
  78. package/dist/providers/model-registry.js +76 -4
  79. package/dist/providers/provider-runtime.js +9 -1
  80. package/dist/providers/thinking-levels.js +2 -0
  81. package/dist/providers/types.d.ts +1 -1
  82. package/dist/runtime/glm-api-runtime.d.ts +4 -0
  83. package/dist/runtime/glm-api-runtime.js +15 -0
  84. package/dist/runtime/index.d.ts +1 -0
  85. package/dist/runtime/index.js +1 -0
  86. package/dist/runtime/kimi-api-runtime.d.ts +6 -1
  87. package/dist/runtime/kimi-api-runtime.js +10 -4
  88. package/dist/runtime/recovery-artifact-store.d.ts +39 -0
  89. package/dist/runtime/recovery-artifact-store.js +111 -0
  90. package/dist/runtime/runtime-backed-task-runner.js +10 -0
  91. package/dist/runtime/runtime-bootstrap.js +17 -1
  92. package/dist/runtime/slash-commands.js +38 -5
  93. package/dist/schema/command.schema.d.ts +121 -0
  94. package/dist/schema/command.schema.js +25 -0
  95. package/dist/schema/envelope.schema.d.ts +2 -2
  96. package/dist/schema/evidence-bundle.schema.d.ts +196 -0
  97. package/dist/schema/evidence-bundle.schema.js +38 -0
  98. package/dist/schema/index.d.ts +2 -0
  99. package/dist/schema/index.js +2 -0
  100. package/dist/schema/proof-bundle.schema.d.ts +2 -2
  101. package/dist/theme/external-theme-adapters.d.ts +27 -0
  102. package/dist/theme/external-theme-adapters.js +52 -0
  103. package/dist/theme/library-effects.d.ts +12 -0
  104. package/dist/theme/library-effects.js +47 -0
  105. package/dist/theme/parallel.js +3 -2
  106. package/dist/tui/terminal-frame-renderer.d.ts +9 -1
  107. package/dist/tui/terminal-frame-renderer.js +77 -7
  108. package/dist/util/chat-cockpit.d.ts +3 -1
  109. package/dist/util/chat-cockpit.js +44 -5
  110. package/dist/util/command-envelope.d.ts +19 -0
  111. package/dist/util/command-envelope.js +31 -0
  112. package/dist/util/events-logger.d.ts +1 -1
  113. package/dist/util/events-logger.js +23 -16
  114. package/dist/util/fs/mcp-diagnose.js +2 -1
  115. package/dist/util/fs/mcp-runtime-config.js +14 -2
  116. package/dist/util/i18n.js +2 -2
  117. package/dist/util/screenshot-store.d.ts +7 -0
  118. package/dist/util/screenshot-store.js +30 -0
  119. package/dist/util/terminal-kit-bridge.d.ts +24 -0
  120. package/dist/util/terminal-kit-bridge.js +60 -0
  121. package/dist/util/terminal-owner.d.ts +1 -0
  122. package/dist/util/terminal-owner.js +31 -0
  123. package/dist/version.d.ts +3 -1
  124. package/dist/version.js +4 -0
  125. package/docs/2026-05-18-project-direction.md +1 -1
  126. package/docs/2026-06-14/critical-issues.md +19 -0
  127. package/docs/2026-06-14/improvements.md +15 -0
  128. package/docs/2026-06-14/init-checklist.md +25 -0
  129. package/docs/2026-06-14/plan.md +20 -0
  130. package/docs/2026-06-15/critical-issues.md +19 -0
  131. package/docs/2026-06-15/improvements.md +15 -0
  132. package/docs/2026-06-15/init-checklist.md +25 -0
  133. package/docs/2026-06-15/plan.md +20 -0
  134. package/docs/claims.md +85 -0
  135. package/docs/comparisons/omk-vs-claude-code.md +31 -0
  136. package/docs/comparisons/omk-vs-codex.md +33 -0
  137. package/docs/comparisons/omk-vs-mcp-agent.md +33 -0
  138. package/docs/comparisons/omk-vs-opencode.md +32 -0
  139. package/docs/decision-layer-calibration.md +20 -0
  140. package/docs/geo-eval-prompts.md +53 -0
  141. package/docs/getting-started.md +3 -31
  142. package/docs/headroom-omk-setup-guide.md +2 -2
  143. package/docs/integrations/adaptorch.md +46 -0
  144. package/docs/native-root-runtime-algorithms.md +2 -303
  145. package/docs/native-root-runtime-hardening.md +1 -1
  146. package/docs/provider-maturity.md +1 -1
  147. package/docs/use-cases/evidence-gated-coding-agents.md +52 -0
  148. package/docs/use-cases/no-kimi-mode.md +38 -0
  149. package/docs/use-cases/provider-routing-for-ai-coding.md +36 -0
  150. package/docs/use-cases/replayable-agent-runs.md +40 -0
  151. package/docs/use-cases/scoped-mcp-for-coding-agents.md +34 -0
  152. package/docs/versioning.md +9 -3
  153. package/docs/what-is-omk.md +64 -0
  154. package/llms.txt +28 -18
  155. package/package.json +7 -2
  156. package/readmeasset/.npmignore +3 -0
  157. package/readmeasset/ASSET_INDEX.md +3 -0
  158. package/readmeasset/ASSET_PROVENANCE.md +6 -0
  159. package/readmeasset/omk-adaptorch-ouroboros-supermemory.svg +95 -0
  160. package/readmeasset/omk-init-control-loop.svg +118 -0
  161. package/readmeasset/omk-parallel-subagents.svg +97 -0
  162. package/templates/web-bridge/chrome-extension/background.js +1 -1
@@ -1,7 +1,7 @@
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", "anthropic"];
4
+ export const KNOWN_PROVIDER_IDS = ["mimo", "kimi", "deepseek", "qwen", "codex", "openrouter", "anthropic", "glm"];
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 = {
@@ -121,6 +121,29 @@ const DEFAULT_PROVIDER_CONFIGS = {
121
121
  planKind: "qwen-coding-plan",
122
122
  routing: "advisory",
123
123
  },
124
+ glm: {
125
+ enabled: false,
126
+ kind: "openai-compatible",
127
+ baseUrl: "https://open.bigmodel.cn/api/paas/v1",
128
+ apiKeyEnv: "BIGMODEL_API_KEY",
129
+ defaultModel: "glm-5.2",
130
+ aliases: {
131
+ default: "glm-5.2",
132
+ glm: "glm-5.2",
133
+ "glm-5": "glm-5",
134
+ "glm-5.2": "glm-5.2",
135
+ "glm-5-air": "glm-5-air",
136
+ "glm-5-airx": "glm-5-airx",
137
+ },
138
+ capabilities: ["read", "research", "review", "qa", "advisory"],
139
+ contextWindow: 128_000,
140
+ reservedOutputTokens: 4096,
141
+ wireApi: "openai-chat-completions",
142
+ auth: { method: "api-key-env" },
143
+ profileType: "runtime",
144
+ planKind: "runtime",
145
+ routing: "advisory",
146
+ },
124
147
  openrouter: {
125
148
  enabled: false,
126
149
  kind: "openai-compatible",
@@ -176,6 +199,8 @@ export function normalizeProviderId(value) {
176
199
  return "openrouter";
177
200
  if (lower === "anthropic-api" || lower === "anthropic-direct" || lower === "claude-api")
178
201
  return "anthropic";
202
+ if (lower === "glm" || lower === "bigmodel" || lower === "zhipu")
203
+ return "glm";
179
204
  return lower;
180
205
  }
181
206
  export function parseProviderModelArg(value) {
@@ -202,11 +227,22 @@ export function parseProviderModelArg(value) {
202
227
  const slash = modelPart.indexOf("/");
203
228
  if (slash > 0) {
204
229
  const provider = normalizeProviderId(modelPart.slice(0, slash));
205
- const arg = { model: normalizeModelAlias(modelPart.slice(slash + 1)) };
230
+ let modelSegment = modelPart.slice(slash + 1);
231
+ const inlineThinking = extractInlineThinkingLevel(modelSegment);
232
+ if (inlineThinking && !thinkingLevel) {
233
+ modelSegment = inlineThinking.modelPart;
234
+ thinkingLevel = inlineThinking.thinkingLevel;
235
+ }
236
+ const arg = { model: normalizeModelAlias(modelSegment) };
206
237
  if (provider !== "auto")
207
238
  arg.provider = provider;
208
239
  return withThinking(arg);
209
240
  }
241
+ const inlineThinking = extractInlineThinkingLevel(modelPart);
242
+ if (inlineThinking && !thinkingLevel) {
243
+ modelPart = inlineThinking.modelPart;
244
+ thinkingLevel = inlineThinking.thinkingLevel;
245
+ }
210
246
  const model = normalizeModelAlias(modelPart);
211
247
  if (model === "codex-cli")
212
248
  return withThinking({ provider: "codex", model });
@@ -214,6 +250,8 @@ export function parseProviderModelArg(value) {
214
250
  return withThinking({ provider: "deepseek", model });
215
251
  if (model.startsWith("claude-") || model.startsWith("gpt-") || model.startsWith("anthropic/"))
216
252
  return withThinking({ provider: "openrouter", model });
253
+ if (model.startsWith("glm-"))
254
+ return withThinking({ provider: "glm", model });
217
255
  return withThinking({ model });
218
256
  }
219
257
  function isThinkingLevelSuffix(suffix) {
@@ -225,6 +263,19 @@ function isThinkingLevelSuffix(suffix) {
225
263
  ];
226
264
  return thinkingAliases.includes(suffix);
227
265
  }
266
+ function extractInlineThinkingLevel(value) {
267
+ const lower = value.trim().toLowerCase().replace(/[_\s]+/g, "-");
268
+ const match = lower.match(/^(glm-?5(?:\.\d+)?)-?([a-z_ -]+)$/);
269
+ if (!match)
270
+ return undefined;
271
+ const suffix = match[2]?.replace(/[-\s]+/g, "_") ?? "";
272
+ if (!isThinkingLevelSuffix(suffix))
273
+ return undefined;
274
+ const modelPart = match[1]?.replace(/^glm-?/, "glm-");
275
+ if (!modelPart)
276
+ return undefined;
277
+ return { modelPart, thinkingLevel: suffix.replace(/_/g, "-") };
278
+ }
228
279
  export function normalizeModelAlias(value) {
229
280
  const trimmed = value.trim();
230
281
  const lower = trimmed.toLowerCase().replace(/[_\s]+/g, "-");
@@ -248,6 +299,17 @@ export function normalizeModelAlias(value) {
248
299
  return "qwen3-max";
249
300
  if (lower === "qwen-3.7-max" || lower === "qwen3.7-max" || lower === "qwen-3-7-max")
250
301
  return "qwen3-max";
302
+ const inlineThinking = extractInlineThinkingLevel(trimmed);
303
+ if (inlineThinking)
304
+ return inlineThinking.modelPart;
305
+ if (lower === "glm-5" || lower === "glm5")
306
+ return "glm-5";
307
+ if (lower === "glm-5.2" || lower === "glm5.2")
308
+ return "glm-5.2";
309
+ if (lower === "glm-5-air")
310
+ return "glm-5-air";
311
+ if (lower === "glm-5-airx" || lower === "glm-5-air-x")
312
+ return "glm-5-airx";
251
313
  return trimmed;
252
314
  }
253
315
  export async function readProviderRegistry(options = {}) {
@@ -373,7 +435,8 @@ export async function providerDoctorStatus(provider, options = {}) {
373
435
  : "Codex CLI missing/disabled or authentication not verified; configured authority fallback is active",
374
436
  };
375
437
  }
376
- const apiKeySet = entry.apiKeyEnv ? Boolean((options.env ?? process.env)[entry.apiKeyEnv]?.trim()) : undefined;
438
+ const env = options.env ?? process.env;
439
+ const apiKeySet = entry.apiKeyEnv ? Boolean(resolveProviderApiKeyEnvValue(entry, env)?.trim()) : undefined;
377
440
  const needsKey = entry.kind === "openai-compatible";
378
441
  const available = entry.enabled && (!needsKey || apiKeySet === true);
379
442
  return {
@@ -400,6 +463,14 @@ export async function providerDoctorStatus(provider, options = {}) {
400
463
  : entry.disabledReason ?? "Provider disabled; configured authority fallback is active",
401
464
  };
402
465
  }
466
+ function resolveProviderApiKeyEnvValue(entry, env) {
467
+ const primary = entry.apiKeyEnv ? env[entry.apiKeyEnv] : undefined;
468
+ if (primary?.trim())
469
+ return primary;
470
+ if (entry.id === "glm")
471
+ return env.GLM_API_KEY ?? env.BIGMODEL_API_KEY;
472
+ return primary;
473
+ }
403
474
  function mergeProviderConfig(id, stored) {
404
475
  const known = isKnownProviderId(id) ? DEFAULT_PROVIDER_CONFIGS[id] : undefined;
405
476
  const aliases = { ...(known?.aliases ?? {}), ...(stored?.aliases ?? {}) };
@@ -540,11 +611,12 @@ export async function resolveUserModelAlias(input, options = {}) {
540
611
  const aliased = aliases[normalizeUserAlias(raw)];
541
612
  const candidate = aliased ?? raw;
542
613
  const parsed = parseProviderModelArg(candidate);
543
- if (parsed.provider || parsed.model !== raw) {
614
+ if (parsed.provider || parsed.model !== raw || parsed.thinkingLevel) {
544
615
  return {
545
616
  input: raw,
546
617
  provider: parsed.provider,
547
618
  model: parsed.model ?? candidate,
619
+ thinkingLevel: parsed.thinkingLevel,
548
620
  source: aliased ? "user-alias" : "literal",
549
621
  };
550
622
  }
@@ -69,7 +69,7 @@ export async function createProviderBackedTaskRunner(options) {
69
69
  for (const entry of registry) {
70
70
  if (!shouldUseOpenAICompatibleProvider(entry, providerPolicy))
71
71
  continue;
72
- const apiKey = entry.apiKeyEnv ? process.env[entry.apiKeyEnv] : undefined;
72
+ const apiKey = resolveOpenAICompatibleApiKey(entry, process.env);
73
73
  if (apiKey && entry.baseUrl) {
74
74
  if (entry.wireApi === "anthropic-messages") {
75
75
  providerRunners[entry.id] = createAnthropicMessagesReadOnlyTaskRunner({
@@ -192,6 +192,14 @@ function shouldUseOpenAICompatibleProvider(entry, providerPolicy) {
192
192
  return false;
193
193
  return providerPolicy === "auto" || providerPolicy === entry.id;
194
194
  }
195
+ function resolveOpenAICompatibleApiKey(entry, env) {
196
+ const primary = entry.apiKeyEnv ? env[entry.apiKeyEnv] : undefined;
197
+ if (primary?.trim())
198
+ return primary;
199
+ if (entry.id === "glm")
200
+ return env.GLM_API_KEY ?? env.BIGMODEL_API_KEY;
201
+ return primary;
202
+ }
195
203
  function providerModelDefault(entry) {
196
204
  return {
197
205
  model: entry.defaultModel,
@@ -4,6 +4,7 @@ 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
+ glm: ["off", "medium", "high", "max"],
7
8
  anthropic: ["minimal", "low", "medium", "high", "xhigh", "max"],
8
9
  openrouter: ["minimal", "low", "medium", "high", "xhigh", "max"],
9
10
  duckcoding: ["minimal", "low", "medium", "high", "xhigh", "max"],
@@ -13,6 +14,7 @@ const MODEL_LEVELS = [
13
14
  { pattern: /kimi|mimo/i, levels: PROVIDER_LEVELS.kimi },
14
15
  { pattern: /deepseek/i, levels: PROVIDER_LEVELS.deepseek },
15
16
  { pattern: /qwen/i, levels: PROVIDER_LEVELS.qwen },
17
+ { pattern: /glm/i, levels: PROVIDER_LEVELS.glm },
16
18
  { pattern: /duck[-_ ]?coding/i, levels: PROVIDER_LEVELS.duckcoding },
17
19
  { pattern: /fable|claude|sonnet|opus|haiku|anthropic|gpt|openrouter/i, levels: PROVIDER_LEVELS.anthropic },
18
20
  { pattern: /codex/i, levels: PROVIDER_LEVELS.codex },
@@ -1,5 +1,5 @@
1
1
  import type { TaskResult, TaskRunner } from "../contracts/orchestration.js";
2
- export type KnownProviderId = "mimo" | "kimi" | "deepseek" | "codex" | "qwen" | "openrouter" | "anthropic";
2
+ export type KnownProviderId = "mimo" | "kimi" | "deepseek" | "codex" | "qwen" | "openrouter" | "anthropic" | "glm";
3
3
  export type ProviderId = KnownProviderId | (string & {});
4
4
  export type ProviderPolicy = "auto" | ProviderId;
5
5
  export declare const DEFAULT_AUTHORITY_PROVIDER: ProviderId;
@@ -0,0 +1,4 @@
1
+ import type { AgentRuntime } from "./agent-runtime.js";
2
+ import { type KimiApiRuntimeOptions } from "./kimi-api-runtime.js";
3
+ export type GlmApiRuntimeOptions = KimiApiRuntimeOptions;
4
+ export declare function createGlmApiRuntime(options?: GlmApiRuntimeOptions): AgentRuntime;
@@ -0,0 +1,15 @@
1
+ import { KimiApiRuntime } from "./kimi-api-runtime.js";
2
+ export function createGlmApiRuntime(options = {}) {
3
+ const env = options.env ?? process.env;
4
+ return new KimiApiRuntime({
5
+ ...options,
6
+ id: options.id ?? "glm-api",
7
+ providerId: "glm",
8
+ providerName: "Zhipu GLM",
9
+ apiKeyEnvName: env.GLM_API_KEY && !env.BIGMODEL_API_KEY ? "GLM_API_KEY" : "BIGMODEL_API_KEY",
10
+ priority: options.priority ?? 84,
11
+ apiKey: options.apiKey ?? env.BIGMODEL_API_KEY ?? env.GLM_API_KEY,
12
+ model: options.model ?? env.GLM_MODEL ?? "glm-5.2",
13
+ baseUrl: options.baseUrl ?? env.GLM_BASE_URL ?? env.BIGMODEL_BASE_URL ?? "https://open.bigmodel.cn/api/paas/v1",
14
+ });
15
+ }
@@ -5,4 +5,5 @@ export { type RuntimeRouterOptions, type RuntimeRouteDecision, type NodeIntent,
5
5
  export { type ToolProxyOptions, type ToolResultCompression, createToolProxy, } from "./tool-proxy.js";
6
6
  export { type KimiWireRuntimeOptions, createKimiWireRuntime, } from "./kimi-wire-runtime.js";
7
7
  export { type KimiPrintRuntimeOptions, createKimiPrintRuntime, } from "./kimi-print-runtime.js";
8
+ export { type RecoveryArtifactRef, type RecoveryArtifactStore, type RecoveryArtifactStoreOptions, type RecoveryCaptureInput, type RecoveryFailureKind, createRecoveryArtifactStore, } from "./recovery-artifact-store.js";
8
9
  export { type ContextBudgetReport, type DroppedContextItem, type ContextTokenBreakdown, type ContextBudgetOptimizationResult, type ContextItemEvidenceStats, createContextBudgetOptimizer, estimateTokens, breakdownCapsuleTokens, } from "./context-budget-optimizer.js";
@@ -5,4 +5,5 @@ export { createRuntimeRouter, } from './runtime-router.js';
5
5
  export { createToolProxy, } from "./tool-proxy.js";
6
6
  export { createKimiWireRuntime, } from "./kimi-wire-runtime.js";
7
7
  export { createKimiPrintRuntime, } from "./kimi-print-runtime.js";
8
+ export { createRecoveryArtifactStore, } from "./recovery-artifact-store.js";
8
9
  export { createContextBudgetOptimizer, estimateTokens, breakdownCapsuleTokens, } from "./context-budget-optimizer.js";
@@ -8,6 +8,9 @@ import type { AgentRuntime, AgentRunResult, AgentResult, AgentTask, RuntimeCapab
8
8
  import type { ContextCapsule } from "./context-capsule.js";
9
9
  export interface KimiApiRuntimeOptions {
10
10
  id?: string;
11
+ providerId?: string;
12
+ providerName?: string;
13
+ apiKeyEnvName?: string;
11
14
  priority?: number;
12
15
  apiKey?: string;
13
16
  model?: string;
@@ -34,13 +37,15 @@ export declare function createKimiApiRuntime(options?: KimiApiRuntimeOptions): A
34
37
  export declare const createKimiWireRuntime: typeof createKimiApiRuntime;
35
38
  export declare class KimiApiRuntime implements AgentRuntime {
36
39
  readonly id: string;
37
- readonly providerId = "kimi";
40
+ readonly providerId: string;
38
41
  readonly legacy = false;
39
42
  readonly runtimeMode = "api";
40
43
  readonly kind = "api";
41
44
  readonly priority: number;
42
45
  readonly capabilities: RuntimeCapabilities;
43
46
  private readonly apiKey;
47
+ private readonly apiKeyEnvName;
48
+ private readonly providerName;
44
49
  private readonly model;
45
50
  private readonly baseUrl;
46
51
  constructor(options?: KimiApiRuntimeOptions);
@@ -76,6 +76,7 @@ function mapToolCalls(apiToolCalls, context) {
76
76
  export function createKimiApiRuntime(options = {}) {
77
77
  const env = options.env ?? process.env;
78
78
  return new KimiApiRuntime({
79
+ ...options,
79
80
  apiKey: options.apiKey ?? env.KIMI_API_KEY,
80
81
  model: options.model ?? env.KIMI_MODEL,
81
82
  baseUrl: options.baseUrl ?? env.KIMI_BASE_URL,
@@ -85,7 +86,7 @@ export function createKimiApiRuntime(options = {}) {
85
86
  export const createKimiWireRuntime = createKimiApiRuntime;
86
87
  export class KimiApiRuntime {
87
88
  id;
88
- providerId = "kimi";
89
+ providerId;
89
90
  legacy = false;
90
91
  runtimeMode = "api";
91
92
  kind = "api";
@@ -104,11 +105,16 @@ export class KimiApiRuntime {
104
105
  supportsToolCalling: true,
105
106
  };
106
107
  apiKey;
108
+ apiKeyEnvName;
109
+ providerName;
107
110
  model;
108
111
  baseUrl;
109
112
  constructor(options = {}) {
110
113
  this.id = options.id ?? "kimi-api";
114
+ this.providerId = options.providerId ?? "kimi";
111
115
  this.priority = options.priority ?? 90;
116
+ this.apiKeyEnvName = options.apiKeyEnvName ?? "KIMI_API_KEY";
117
+ this.providerName = options.providerName ?? "Moonshot";
112
118
  this.apiKey = options.apiKey ?? process.env.KIMI_API_KEY;
113
119
  this.model = options.model ?? process.env.KIMI_MODEL ?? "kimi-k2-6";
114
120
  this.baseUrl = (options.baseUrl ?? "https://api.moonshot.cn/v1").replace(/\/+$/, "");
@@ -132,7 +138,7 @@ export class KimiApiRuntime {
132
138
  return {
133
139
  runtimeId: this.id,
134
140
  available,
135
- reason: available ? undefined : "KIMI_API_KEY is not set",
141
+ reason: available ? undefined : `${this.apiKeyEnvName} is not set`,
136
142
  checkedAt: new Date().toISOString(),
137
143
  };
138
144
  }
@@ -171,7 +177,7 @@ export class KimiApiRuntime {
171
177
  output: "",
172
178
  exitCode: 1,
173
179
  thinking: "",
174
- metadata: { error: "KIMI_API_KEY is not set" },
180
+ metadata: { error: `${this.apiKeyEnvName} is not set` },
175
181
  };
176
182
  }
177
183
  if (task.capabilities.write ||
@@ -246,7 +252,7 @@ export class KimiApiRuntime {
246
252
  output: "",
247
253
  exitCode: 1,
248
254
  thinking: "",
249
- metadata: { error: `Moonshot API error ${response.status}: ${errorText}` },
255
+ metadata: { error: `${this.providerName} API error ${response.status}: ${errorText}` },
250
256
  };
251
257
  }
252
258
  if (useStreaming) {
@@ -0,0 +1,39 @@
1
+ export type RecoveryFailureKind = "rate_limit" | "quota" | "timeout" | "provider_crash" | "provider_error" | "abort" | "unknown";
2
+ export interface RecoveryCaptureInput {
3
+ readonly runId: string;
4
+ readonly provider: string;
5
+ readonly nodeId: string;
6
+ readonly attemptId?: string;
7
+ readonly failureKind: RecoveryFailureKind;
8
+ readonly exitCode?: number;
9
+ readonly cwd?: string;
10
+ readonly command?: readonly string[];
11
+ readonly stdout?: string;
12
+ readonly stderr?: string;
13
+ readonly sessionPaths?: readonly string[];
14
+ readonly metadata?: Record<string, unknown>;
15
+ }
16
+ export interface RecoveryArtifactRef {
17
+ readonly schemaVersion: "omk.recovery-artifact.v1";
18
+ readonly runId: string;
19
+ readonly provider: string;
20
+ readonly nodeId: string;
21
+ readonly attemptId?: string;
22
+ readonly failureKind: RecoveryFailureKind;
23
+ readonly dir: string;
24
+ readonly manifestPath: string;
25
+ readonly sessionHandlePath?: string;
26
+ }
27
+ export interface RecoveryArtifactStore {
28
+ captureFailure(input: RecoveryCaptureInput): Promise<RecoveryArtifactRef>;
29
+ cleanup(input: {
30
+ runId: string;
31
+ provider?: string;
32
+ nodeId?: string;
33
+ }): Promise<void>;
34
+ }
35
+ export interface RecoveryArtifactStoreOptions {
36
+ readonly root?: string;
37
+ readonly maxLogBytes?: number;
38
+ }
39
+ export declare function createRecoveryArtifactStore(options?: RecoveryArtifactStoreOptions): RecoveryArtifactStore;
@@ -0,0 +1,111 @@
1
+ import { createHash } from "node:crypto";
2
+ import { lstat, mkdir, readFile, readlink, rm, writeFile } from "node:fs/promises";
3
+ import { basename, join } from "node:path";
4
+ import { redactSecrets } from "../orchestration/state-persister.js";
5
+ import { getRunPath, validateRunId } from "../util/run-store.js";
6
+ const DEFAULT_MAX_LOG_BYTES = 128 * 1024;
7
+ const MAX_SEGMENT_LENGTH = 80;
8
+ function safeSegment(value, fallback) {
9
+ const sanitized = value
10
+ .replace(/[^A-Za-z0-9._-]/g, "-")
11
+ .replace(/-+/g, "-")
12
+ .replace(/^-|-$/g, "")
13
+ .slice(0, MAX_SEGMENT_LENGTH);
14
+ return sanitized && sanitized !== "." && sanitized !== ".." ? sanitized : fallback;
15
+ }
16
+ function redactText(value, maxBytes) {
17
+ if (!value)
18
+ return "";
19
+ const bounded = Buffer.byteLength(value, "utf8") > maxBytes
20
+ ? Buffer.from(value, "utf8").subarray(-maxBytes).toString("utf8")
21
+ : value;
22
+ const redacted = redactSecrets(bounded);
23
+ return typeof redacted === "string" ? redacted : JSON.stringify(redacted);
24
+ }
25
+ function sha256Text(value) {
26
+ return createHash("sha256").update(value).digest("hex");
27
+ }
28
+ async function inspectSessionPath(path) {
29
+ const base = { path, name: basename(path) };
30
+ try {
31
+ const stat = await lstat(path);
32
+ if (stat.isSymbolicLink()) {
33
+ return { ...base, kind: "symlink", target: await readlink(path).catch(() => "unreadable"), copied: false };
34
+ }
35
+ if (stat.isFile()) {
36
+ const bytes = await readFile(path);
37
+ return { ...base, kind: "file", size: bytes.length, sha256: createHash("sha256").update(bytes).digest("hex"), copied: false };
38
+ }
39
+ if (stat.isDirectory()) {
40
+ return { ...base, kind: "directory", copied: false };
41
+ }
42
+ return { ...base, kind: "other", copied: false };
43
+ }
44
+ catch (error) {
45
+ return { ...base, kind: "missing", error: error instanceof Error ? error.message : String(error), copied: false };
46
+ }
47
+ }
48
+ export function createRecoveryArtifactStore(options = {}) {
49
+ const maxLogBytes = options.maxLogBytes ?? DEFAULT_MAX_LOG_BYTES;
50
+ function runDir(runId) {
51
+ return getRunPath(validateRunId(runId), undefined, options.root);
52
+ }
53
+ function recoveryDir(input) {
54
+ const base = join(runDir(input.runId), "recovery");
55
+ const provider = input.provider ? safeSegment(input.provider, "provider") : undefined;
56
+ const nodeId = input.nodeId ? safeSegment(input.nodeId, "node") : undefined;
57
+ return provider ? nodeId ? join(base, provider, nodeId) : join(base, provider) : base;
58
+ }
59
+ return {
60
+ async captureFailure(input) {
61
+ const runId = validateRunId(input.runId);
62
+ const provider = safeSegment(input.provider, "provider");
63
+ const nodeId = safeSegment(input.nodeId, "node");
64
+ const dir = recoveryDir({ runId, provider, nodeId });
65
+ await mkdir(dir, { recursive: true });
66
+ const stdout = redactText(input.stdout, maxLogBytes);
67
+ const stderr = redactText(input.stderr, maxLogBytes);
68
+ const stdoutPath = join(dir, "stdout.log");
69
+ const stderrPath = join(dir, "stderr.redacted.log");
70
+ await writeFile(stdoutPath, stdout, "utf8");
71
+ await writeFile(stderrPath, stderr, "utf8");
72
+ const sessionHandlePath = join(dir, "session-handle.json");
73
+ const sessionHandles = await Promise.all((input.sessionPaths ?? []).map(inspectSessionPath));
74
+ await writeFile(sessionHandlePath, `${JSON.stringify({ schemaVersion: "omk.recovery-session.v1", sessionPaths: sessionHandles }, null, 2)}\n`, "utf8");
75
+ const manifestPath = join(dir, "manifest.json");
76
+ const manifest = {
77
+ schemaVersion: "omk.recovery-artifact.v1",
78
+ runId,
79
+ provider,
80
+ nodeId,
81
+ ...(input.attemptId ? { attemptId: safeSegment(input.attemptId, "attempt") } : {}),
82
+ failureKind: input.failureKind,
83
+ ...(input.exitCode !== undefined ? { exitCode: input.exitCode } : {}),
84
+ capturedAt: new Date().toISOString(),
85
+ ...(input.cwd ? { cwd: input.cwd } : {}),
86
+ command: input.command ? input.command.map((part) => redactText(part, maxLogBytes)) : [],
87
+ logs: {
88
+ stdout: { path: "stdout.log", sha256: sha256Text(stdout), bytes: Buffer.byteLength(stdout, "utf8") },
89
+ stderr: { path: "stderr.redacted.log", sha256: sha256Text(stderr), bytes: Buffer.byteLength(stderr, "utf8"), redacted: true },
90
+ },
91
+ sessionHandle: { path: "session-handle.json", count: sessionHandles.length },
92
+ metadata: redactSecrets(input.metadata ?? {}),
93
+ };
94
+ await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
95
+ return {
96
+ schemaVersion: "omk.recovery-artifact.v1",
97
+ runId,
98
+ provider,
99
+ nodeId,
100
+ ...(input.attemptId ? { attemptId: safeSegment(input.attemptId, "attempt") } : {}),
101
+ failureKind: input.failureKind,
102
+ dir,
103
+ manifestPath,
104
+ sessionHandlePath,
105
+ };
106
+ },
107
+ async cleanup(input) {
108
+ await rm(recoveryDir(input), { recursive: true, force: true });
109
+ },
110
+ };
111
+ }
@@ -24,6 +24,7 @@ import { LocalLlmRuntime } from "./local-llm-runtime.js";
24
24
  import { checkCommand } from "../util/shell.js";
25
25
  import { createMimoApiRuntime } from "./mimo-api-runtime.js";
26
26
  import { createKimiApiRuntime } from "./kimi-api-runtime.js";
27
+ import { createGlmApiRuntime } from "./glm-api-runtime.js";
27
28
  import { getUserHome } from "../util/fs.js";
28
29
  async function createDefaultRuntimeRegistry(options) {
29
30
  const registry = createRuntimeRegistry();
@@ -43,6 +44,15 @@ async function createDefaultRuntimeRegistry(options) {
43
44
  if (kimiApiKey) {
44
45
  registry.register(createKimiApiRuntime({ apiKey: kimiApiKey }));
45
46
  }
47
+ // ── GLM API runtime (Zhipu AI — OpenAI-compatible advisory lane) ──
48
+ const glmKey = options.env?.BIGMODEL_API_KEY
49
+ ?? options.env?.GLM_API_KEY
50
+ ?? process.env.BIGMODEL_API_KEY
51
+ ?? process.env.GLM_API_KEY
52
+ ?? readConfiguredProviderApiKey("glm");
53
+ if (glmKey) {
54
+ registry.register(createGlmApiRuntime({ apiKey: glmKey, env: options.env }));
55
+ }
46
56
  // ── codex-cli ──
47
57
  const codexBin = options.env?.CODEX_BIN ?? process.env.CODEX_BIN ?? "codex";
48
58
  if (await checkCommand(codexBin).catch(() => false)) {
@@ -35,6 +35,16 @@ function detectProvider(provider, env) {
35
35
  authHint: "Set DEEPSEEK_API_KEY env var",
36
36
  modelHint: env.DEEPSEEK_MODEL ?? "deepseek-chat",
37
37
  };
38
+ case "glm":
39
+ case "bigmodel":
40
+ case "zhipu":
41
+ return {
42
+ envKey: env.GLM_API_KEY && !env.BIGMODEL_API_KEY ? "GLM_API_KEY" : "BIGMODEL_API_KEY",
43
+ sessionMode: "api-turn",
44
+ installHint: "export BIGMODEL_API_KEY=...",
45
+ authHint: "Set BIGMODEL_API_KEY env var",
46
+ modelHint: env.GLM_MODEL ?? "glm-5.2",
47
+ };
38
48
  case "local":
39
49
  case "llama":
40
50
  case "local-llm":
@@ -82,11 +92,15 @@ async function resolveAutoProvider(env) {
82
92
  return { provider: "kimi", runtimeId: "kimi-api" };
83
93
  if (defaultModel.startsWith("deepseek") && env.DEEPSEEK_API_KEY)
84
94
  return { provider: "deepseek", runtimeId: "deepseek-api" };
95
+ if (defaultModel.startsWith("glm") && (env.BIGMODEL_API_KEY || env.GLM_API_KEY || providerConfigHasApiKey(configContent, "glm")))
96
+ return { provider: "glm", runtimeId: "glm-api" };
85
97
  }
86
98
  if (providerConfigHasApiKey(configContent, "mimo"))
87
99
  return { provider: "mimo", runtimeId: "mimo-api" };
88
100
  if (providerConfigHasApiKey(configContent, "kimi"))
89
101
  return { provider: "kimi", runtimeId: "kimi-api" };
102
+ if (providerConfigHasApiKey(configContent, "glm"))
103
+ return { provider: "glm", runtimeId: "glm-api" };
90
104
  }
91
105
  // 2. Check for API keys in env
92
106
  if (env.MIMO_API_KEY)
@@ -95,6 +109,8 @@ async function resolveAutoProvider(env) {
95
109
  return { provider: "kimi", runtimeId: "kimi-api" };
96
110
  if (env.DEEPSEEK_API_KEY)
97
111
  return { provider: "deepseek", runtimeId: "deepseek-api" };
112
+ if (env.BIGMODEL_API_KEY || env.GLM_API_KEY)
113
+ return { provider: "glm", runtimeId: "glm-api" };
98
114
  if (env.LOCAL_LLM_BASE_URL)
99
115
  return { provider: "local-llm", runtimeId: "local-llm" };
100
116
  // 3. CLI binary detection (lowest priority)
@@ -145,7 +161,7 @@ export async function resolveRuntimeBootstrap(options) {
145
161
  }
146
162
  else if (info.envKey) {
147
163
  runtimeOk = Boolean(env[info.envKey]);
148
- if (!runtimeOk && (selectedProvider === "mimo" || selectedProvider === "kimi")) {
164
+ if (!runtimeOk && (selectedProvider === "mimo" || selectedProvider === "kimi" || selectedProvider === "glm")) {
149
165
  const configContent = readHomeProviderConfig(env, ".omk");
150
166
  runtimeOk = configContent ? providerConfigHasApiKey(configContent, selectedProvider) : false;
151
167
  }
@@ -91,6 +91,24 @@ function renderSlashResultContent(result, fallback) {
91
91
  lines.push(" Persistent default unchanged; use `omk model use` to persist.\n");
92
92
  return lines.join("\n");
93
93
  }
94
+ case "think.show": {
95
+ const provider = stringValue(payload, "provider", "auto");
96
+ const model = stringValue(payload, "model", "auto");
97
+ const current = stringValue(payload, "currentThinking", "");
98
+ const levels = stringListValue(payload, "supportedThinkingLevels");
99
+ const levelLine = levels
100
+ .map((level) => `${level === current ? "●" : "○"} ${level}`)
101
+ .join(" ");
102
+ return [
103
+ "\n OMK Thinking Control · choose level",
104
+ ` Target: ${provider}/${model}`,
105
+ levelLine ? ` ${levelLine}` : undefined,
106
+ levels.length > 0 ? ` Choose directly: ${levels.map((level) => `/think ${level}`).join(" | ")}` : undefined,
107
+ " Cycle only when explicit: /think next",
108
+ ` Active: ${current || "not selected"}`,
109
+ "",
110
+ ].filter((line) => typeof line === "string").join("\n");
111
+ }
94
112
  case "think.set": {
95
113
  const thinking = stringValue(payload, "thinking");
96
114
  const modelVariant = stringValue(payload, "modelVariant", "auto");
@@ -105,12 +123,12 @@ function renderSlashResultContent(result, fallback) {
105
123
  case "status.show":
106
124
  return `\n Status: ${stringValue(payload, "provider")}/${stringValue(payload, "model")} theme=${stringValue(payload, "theme")}`;
107
125
  case "theme.show": {
108
- const current = stringValue(payload, "current", stringValue(payload, "theme", "omk"));
126
+ const current = stringValue(payload, "current", stringValue(payload, "theme", "omk-control-grid-dark"));
109
127
  const available = stringListValue(payload, "available");
110
128
  return `\n Theme: ${current}${available.length > 0 ? `\n Available: ${available.join(", ")}` : ""}`;
111
129
  }
112
130
  case "theme.set":
113
- return `\n Theme updated: ${stringValue(payload, "theme", "omk")}`;
131
+ return `\n Theme updated: ${stringValue(payload, "theme", "omk-control-grid-dark")}`;
114
132
  case "help": {
115
133
  const commands = stringListValue(payload, "commands");
116
134
  return [`\n OMK Slash Commands`, ...commands.map((command) => ` - ${command}`), ""].join("\n");
@@ -301,7 +319,22 @@ class ThinkCommandHandler {
301
319
  sideEffects: [{ type: "thinking_changed", thinking: variant, modelVariant }],
302
320
  };
303
321
  }
304
- const level = !requested || requested === "next" || requested === "tab"
322
+ if (!requested) {
323
+ return {
324
+ kind: "status",
325
+ command: "think.show",
326
+ payload: {
327
+ provider: input.state.provider ?? "auto",
328
+ model: input.state.model ?? "auto",
329
+ currentThinking: input.state.thinking,
330
+ supportedThinkingLevels: levels,
331
+ usage: `/think ${levels.join(" | ")} | next | variant <name>`,
332
+ },
333
+ renderMode: "theme",
334
+ sideEffects: [],
335
+ };
336
+ }
337
+ const level = requested === "next" || requested === "tab"
305
338
  ? nextThinkingLevel(input.state.thinking, input.state.provider, input.state.model)
306
339
  : normalizeThinkingLevel(requested);
307
340
  if (!level || !levels.includes(level)) {
@@ -346,9 +379,9 @@ class ThemeCommandHandler {
346
379
  kind: "status",
347
380
  command: "theme.show",
348
381
  payload: {
349
- current: input.state.theme ?? "omk",
382
+ current: input.state.theme ?? "omk-control-grid-dark",
350
383
  usage: "/theme <name>",
351
- available: ["omk", "night-city", "omk-control", "green-rain", "rust-forge", "neon-circuit", "minimal", "mono", "dark", "light"],
384
+ available: ["omk-control-grid-dark", "night-city", "omk-control", "green-rain", "rust-forge", "neon-circuit", "omk", "minimal", "mono", "dark", "light"],
352
385
  },
353
386
  renderMode: "theme",
354
387
  sideEffects: [],