open-multi-agent-kit 0.79.3 → 0.80.0

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 (124) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/MATURITY.md +2 -2
  3. package/README.md +86 -39
  4. package/ROADMAP.md +20 -17
  5. package/dist/brand/night-city.theme.json +122 -24
  6. package/dist/brand/rust-forge.theme.json +1 -1
  7. package/dist/commands/chat/native-root-loop.d.ts +27 -3
  8. package/dist/commands/chat/native-root-loop.js +375 -49
  9. package/dist/commands/dag.js +45 -0
  10. package/dist/commands/init/content.d.ts +1 -1
  11. package/dist/commands/init/content.js +3 -3
  12. package/dist/commands/init.js +3 -3
  13. package/dist/commands/verify.js +59 -1
  14. package/dist/contracts/dag.d.ts +9 -1
  15. package/dist/evidence/graph-ontology-relation-gate.d.ts +10 -0
  16. package/dist/evidence/graph-ontology-relation-gate.js +46 -0
  17. package/dist/evidence/headroom-replay-verifier.d.ts +19 -0
  18. package/dist/evidence/headroom-replay-verifier.js +86 -0
  19. package/dist/memory/local-graph-memory-store.d.ts +75 -0
  20. package/dist/memory/local-graph-memory-store.js +570 -0
  21. package/dist/orchestration/dag.d.ts +22 -1
  22. package/dist/orchestration/evidence-gate.js +12 -0
  23. package/dist/orchestration/executor.js +108 -15
  24. package/dist/orchestration/loop-controller-headroom.d.ts +6 -0
  25. package/dist/orchestration/loop-controller-headroom.js +33 -0
  26. package/dist/orchestration/scheduler.d.ts +2 -0
  27. package/dist/orchestration/scheduler.js +3 -0
  28. package/dist/orchestration/task-graph.d.ts +17 -0
  29. package/dist/orchestration/task-graph.js +38 -0
  30. package/dist/providers/codex-cli-runner.js +17 -2
  31. package/dist/runtime/agent-runtime.d.ts +32 -3
  32. package/dist/runtime/agent-runtime.js +20 -7
  33. package/dist/runtime/authority-matrix.d.ts +23 -0
  34. package/dist/runtime/authority-matrix.js +159 -0
  35. package/dist/runtime/chat-advisory-runtime.js +3 -0
  36. package/dist/runtime/codex-cli-runtime.js +2 -0
  37. package/dist/runtime/codex-runtime.d.ts +4 -1
  38. package/dist/runtime/codex-runtime.js +34 -12
  39. package/dist/runtime/context-broker-converter.js +32 -2
  40. package/dist/runtime/context-broker.js +18 -0
  41. package/dist/runtime/contracts/evidence.d.ts +52 -0
  42. package/dist/runtime/contracts/evidence.js +128 -1
  43. package/dist/runtime/contracts/shared.d.ts +27 -0
  44. package/dist/runtime/data-retention-gate.d.ts +42 -0
  45. package/dist/runtime/data-retention-gate.js +123 -0
  46. package/dist/runtime/deepseek-runtime.d.ts +5 -1
  47. package/dist/runtime/deepseek-runtime.js +13 -7
  48. package/dist/runtime/external-cli-adapter.js +20 -7
  49. package/dist/runtime/freedomd-evidence-envelope.d.ts +57 -0
  50. package/dist/runtime/freedomd-evidence-envelope.js +110 -0
  51. package/dist/runtime/freedomd-exception.d.ts +32 -0
  52. package/dist/runtime/freedomd-exception.js +95 -0
  53. package/dist/runtime/freedomd-incidents.d.ts +23 -0
  54. package/dist/runtime/freedomd-incidents.js +103 -0
  55. package/dist/runtime/freedomd-policy.d.ts +35 -0
  56. package/dist/runtime/freedomd-policy.js +114 -0
  57. package/dist/runtime/freedomd-router.d.ts +66 -0
  58. package/dist/runtime/freedomd-router.js +433 -0
  59. package/dist/runtime/headroom-aware-loop-decision.d.ts +21 -0
  60. package/dist/runtime/headroom-aware-loop-decision.js +67 -0
  61. package/dist/runtime/headroom-loop-risk.d.ts +19 -0
  62. package/dist/runtime/headroom-loop-risk.js +43 -0
  63. package/dist/runtime/headroom-policy.d.ts +14 -1
  64. package/dist/runtime/headroom-policy.js +74 -12
  65. package/dist/runtime/kimi-api-runtime.d.ts +4 -1
  66. package/dist/runtime/kimi-api-runtime.js +35 -11
  67. package/dist/runtime/kimi-print-runtime.js +21 -0
  68. package/dist/runtime/kimi-wire-protocol-runtime.js +17 -1
  69. package/dist/runtime/local-llm-runtime.d.ts +5 -1
  70. package/dist/runtime/local-llm-runtime.js +14 -23
  71. package/dist/runtime/private-stderr.d.ts +13 -0
  72. package/dist/runtime/private-stderr.js +69 -0
  73. package/dist/runtime/prompt-envelope.d.ts +1 -1
  74. package/dist/runtime/provider-sovereignty.d.ts +81 -0
  75. package/dist/runtime/provider-sovereignty.js +285 -0
  76. package/dist/runtime/runtime-backed-task-runner.d.ts +1 -0
  77. package/dist/runtime/runtime-backed-task-runner.js +282 -10
  78. package/dist/runtime/runtime-bootstrap.d.ts +12 -0
  79. package/dist/runtime/runtime-bootstrap.js +56 -3
  80. package/dist/runtime/runtime-failure-classifier.d.ts +15 -0
  81. package/dist/runtime/runtime-failure-classifier.js +107 -0
  82. package/dist/runtime/runtime-health-probes.d.ts +30 -0
  83. package/dist/runtime/runtime-health-probes.js +102 -0
  84. package/dist/runtime/runtime-router.d.ts +6 -2
  85. package/dist/runtime/runtime-router.js +575 -53
  86. package/dist/runtime/structured-compaction.d.ts +107 -0
  87. package/dist/runtime/structured-compaction.js +414 -0
  88. package/dist/runtime/tool-dispatch-contracts.d.ts +7 -5
  89. package/dist/runtime/tool-dispatch-contracts.js +15 -4
  90. package/dist/runtime/tool-plane.d.ts +1 -1
  91. package/dist/runtime/tool-plane.js +49 -1
  92. package/dist/runtime/worker-manifest.js +1 -1
  93. package/dist/schema/evidence-bundle.schema.d.ts +8 -8
  94. package/dist/schema/proof-bundle.schema.d.ts +2 -2
  95. package/dist/util/hash.d.ts +2 -0
  96. package/dist/util/hash.js +13 -0
  97. package/dist/util/session.js +6 -5
  98. package/docs/2026-06-16/critical-issues.md +19 -0
  99. package/docs/2026-06-16/improvements.md +15 -0
  100. package/docs/2026-06-16/init-checklist.md +25 -0
  101. package/docs/2026-06-16/plan.md +20 -0
  102. package/docs/ARCHITECTURE_ANALYSIS_CODEGRAPH.md +2 -1
  103. package/docs/GSTACK_MIGRATION.md +1 -1
  104. package/docs/algorithm-hardening-playbook.md +219 -0
  105. package/docs/claims.md +1 -1
  106. package/docs/getting-started.md +1 -1
  107. package/docs/native-root-runtime-hardening.md +42 -32
  108. package/docs/post-0793-hardening-roadmap.md +123 -0
  109. package/docs/provider-maturity.md +18 -16
  110. package/docs/versioning.md +3 -3
  111. package/docs/what-is-omk.md +1 -1
  112. package/package.json +8 -4
  113. package/readmeasset/.npmignore +2 -0
  114. package/readmeasset/ASSET_INDEX.md +2 -0
  115. package/readmeasset/ASSET_PROVENANCE.md +40 -20
  116. package/readmeasset/omk-adaptorch-ouroboros-supermemory.svg +1 -1
  117. package/readmeasset/omk-control-surfaces.svg +1 -1
  118. package/readmeasset/omk-core-loop.svg +1 -1
  119. package/readmeasset/omk-freedomd-control-plane.svg +59 -0
  120. package/readmeasset/omk-freedomd-control-plane.webp +0 -0
  121. package/readmeasset/omk-init-control-loop.svg +1 -1
  122. package/readmeasset/omk-logo-mark.svg +1 -1
  123. package/readmeasset/omk-parallel-subagents.svg +1 -1
  124. package/readmeasset/omk-release-assertions.svg +1 -1
@@ -6,7 +6,8 @@
6
6
  * a configurable threshold (default 90%).
7
7
  *
8
8
  * Prefers the external `headroom` CLI compressor when available;
9
- * falls back to the built-in `optimizeContextBudget` when not.
9
+ * falls back to caller-provided structured text and finally to legacy
10
+ * side-effect optimizers when not.
10
11
  *
11
12
  * Deterministic except for the injectable `runHeadroom` runner.
12
13
  */
@@ -18,9 +19,20 @@ export interface HeadroomDecision {
18
19
  readonly contextWindow: number;
19
20
  readonly reason: string;
20
21
  }
22
+ export type HeadroomBackend = "headroom" | "structured-fallback" | "side-effect-fallback" | "none";
21
23
  export interface HeadroomCompactResult {
24
+ /** Backward-compatible summary: a backend produced compacted text or completed a legacy fallback. */
22
25
  readonly compacted: boolean;
26
+ /** Backward-compatible backend family. */
23
27
  readonly via: "headroom" | "fallback" | "none";
28
+ readonly compactedText?: string;
29
+ readonly reason?: string;
30
+ /** Whether a compaction attempt was made because the policy threshold was crossed. */
31
+ readonly attempted: boolean;
32
+ /** Specific backend that produced this result. */
33
+ readonly backend: HeadroomBackend;
34
+ /** Whether a textual backend produced non-empty compacted text. */
35
+ readonly compactedTextProduced: boolean;
24
36
  }
25
37
  export declare function resolveHeadroomThreshold(env?: NodeJS.ProcessEnv | Record<string, string | undefined>): number;
26
38
  export declare function isHeadroomEnabled(env?: NodeJS.ProcessEnv | Record<string, string | undefined>): boolean;
@@ -34,4 +46,5 @@ export declare function maybeCompactWithHeadroom(args: {
34
46
  text?: string;
35
47
  runHeadroom?: (text: string) => Promise<string | null>;
36
48
  fallback?: () => Promise<void>;
49
+ fallbackText?: () => string | null | Promise<string | null>;
37
50
  }): Promise<HeadroomCompactResult>;
@@ -6,7 +6,8 @@
6
6
  * a configurable threshold (default 90%).
7
7
  *
8
8
  * Prefers the external `headroom` CLI compressor when available;
9
- * falls back to the built-in `optimizeContextBudget` when not.
9
+ * falls back to caller-provided structured text and finally to legacy
10
+ * side-effect optimizers when not.
10
11
  *
11
12
  * Deterministic except for the injectable `runHeadroom` runner.
12
13
  */
@@ -50,14 +51,14 @@ export function evaluateHeadroom(input) {
50
51
  reason: "headroom disabled via OMK_HEADROOM",
51
52
  };
52
53
  }
53
- if (contextWindow <= 0) {
54
+ if (!Number.isFinite(contextWindow) || contextWindow <= 0) {
54
55
  return {
55
56
  shouldCompact: false,
56
57
  utilization: 0,
57
58
  threshold,
58
59
  usedTokens,
59
60
  contextWindow,
60
- reason: "context window size unknown (0)",
61
+ reason: `context window size unknown (${String(contextWindow)})`,
61
62
  };
62
63
  }
63
64
  const utilization = usedTokens / contextWindow;
@@ -93,30 +94,91 @@ async function defaultRunHeadroom(text) {
93
94
  }
94
95
  export async function maybeCompactWithHeadroom(args) {
95
96
  if (!args.decision.shouldCompact) {
96
- return { compacted: false, via: "none" };
97
+ return {
98
+ compacted: false,
99
+ via: "none",
100
+ attempted: false,
101
+ backend: "none",
102
+ compactedTextProduced: false,
103
+ reason: args.decision.reason,
104
+ };
97
105
  }
98
- // Try headroom first
106
+ // Try headroom first.
99
107
  if (args.text) {
100
108
  try {
101
109
  const runner = args.runHeadroom ?? defaultRunHeadroom;
102
110
  const result = await runner(args.text);
103
- if (result !== null) {
104
- return { compacted: true, via: "headroom" };
111
+ if (typeof result === "string" && result.trim().length > 0) {
112
+ return {
113
+ compacted: true,
114
+ via: "headroom",
115
+ backend: "headroom",
116
+ compactedText: result,
117
+ attempted: true,
118
+ compactedTextProduced: true,
119
+ reason: "headroom CLI compaction produced text",
120
+ };
105
121
  }
106
122
  }
107
123
  catch {
108
- // Headroom threw — fall through to fallback
124
+ // Headroom threw — fall through to fallback.
109
125
  }
110
126
  }
111
- // Fall back to built-in optimizer
127
+ // Fall back to a caller-provided textual compaction. This keeps autocompact
128
+ // effective even when the installed headroom CLI exposes proxy/MCP commands
129
+ // but no direct `compact --stdin` subcommand.
130
+ if (args.fallbackText) {
131
+ try {
132
+ const text = await args.fallbackText();
133
+ if (typeof text === "string" && text.trim().length > 0) {
134
+ return {
135
+ compacted: true,
136
+ via: "fallback",
137
+ backend: "structured-fallback",
138
+ compactedText: text,
139
+ attempted: true,
140
+ compactedTextProduced: true,
141
+ reason: "headroom CLI compaction unavailable; used structured fallback text",
142
+ };
143
+ }
144
+ if (typeof text === "string") {
145
+ return {
146
+ compacted: false,
147
+ via: "none",
148
+ backend: "none",
149
+ attempted: true,
150
+ compactedTextProduced: false,
151
+ reason: "empty fallback text",
152
+ };
153
+ }
154
+ }
155
+ catch {
156
+ // Text fallback failed — try side-effect fallback below.
157
+ }
158
+ }
159
+ // Fall back to built-in optimizer side effects when supplied by older callers.
112
160
  if (args.fallback) {
113
161
  try {
114
162
  await args.fallback();
115
- return { compacted: true, via: "fallback" };
163
+ return {
164
+ compacted: true,
165
+ via: "fallback",
166
+ backend: "side-effect-fallback",
167
+ attempted: true,
168
+ compactedTextProduced: false,
169
+ reason: "side-effect fallback completed",
170
+ };
116
171
  }
117
172
  catch {
118
- // Fallback also failed — graceful degradation
173
+ // Fallback also failed — graceful degradation.
119
174
  }
120
175
  }
121
- return { compacted: false, via: "none" };
176
+ return {
177
+ compacted: false,
178
+ via: "none",
179
+ backend: "none",
180
+ attempted: true,
181
+ compactedTextProduced: false,
182
+ reason: "no compaction backend succeeded",
183
+ };
122
184
  }
@@ -5,6 +5,7 @@
5
5
  * Calls https://api.moonshot.cn/v1/chat/completions directly.
6
6
  */
7
7
  import type { AgentRuntime, AgentRunResult, AgentResult, AgentTask, RuntimeCapabilities, RuntimeHealth } from "./agent-runtime.js";
8
+ import type { RuntimeHealthProbeRequest } from "./contracts/shared.js";
8
9
  import type { ContextCapsule } from "./context-capsule.js";
9
10
  export interface KimiApiRuntimeOptions {
10
11
  id?: string;
@@ -37,6 +38,7 @@ export declare function createKimiApiRuntime(options?: KimiApiRuntimeOptions): A
37
38
  export declare const createKimiWireRuntime: typeof createKimiApiRuntime;
38
39
  export declare class KimiApiRuntime implements AgentRuntime {
39
40
  readonly id: string;
41
+ readonly advisory = true;
40
42
  readonly providerId: string;
41
43
  readonly legacy = false;
42
44
  readonly runtimeMode = "api";
@@ -50,8 +52,9 @@ export declare class KimiApiRuntime implements AgentRuntime {
50
52
  private readonly baseUrl;
51
53
  constructor(options?: KimiApiRuntimeOptions);
52
54
  supports(capsule: ContextCapsule): boolean;
53
- health(): Promise<RuntimeHealth>;
55
+ health(input?: RuntimeHealthProbeRequest): Promise<RuntimeHealth>;
54
56
  runNode(capsule: ContextCapsule, signal: AbortSignal): Promise<AgentRunResult>;
55
57
  execute(task: AgentTask): Promise<AgentResult>;
58
+ private consumeCompactionContext;
56
59
  private parseStreamResponse;
57
60
  }
@@ -9,6 +9,7 @@ import { resolve } from "node:path";
9
9
  import { capsuleToTask } from "./context-broker-converter.js";
10
10
  import { buildProviderToolPayload } from "./provider-tool-contracts.js";
11
11
  import { repairToolCalls } from "./tool-call-repair.js";
12
+ import { probeOpenAiCompatibleModels } from "./runtime-health-probes.js";
12
13
  /**
13
14
  * Detect "Image file: <path>" patterns in the prompt text (inserted by /paste
14
15
  * or Ctrl+V clipboard image) and load the referenced images as base64 data URIs
@@ -86,6 +87,7 @@ export function createKimiApiRuntime(options = {}) {
86
87
  export const createKimiWireRuntime = createKimiApiRuntime;
87
88
  export class KimiApiRuntime {
88
89
  id;
90
+ advisory = true;
89
91
  providerId;
90
92
  legacy = false;
91
93
  runtimeMode = "api";
@@ -133,14 +135,16 @@ export class KimiApiRuntime {
133
135
  return false;
134
136
  return true;
135
137
  }
136
- async health() {
137
- const available = Boolean(this.apiKey);
138
- return {
138
+ async health(input = { probeKind: "static", highRisk: false }) {
139
+ return probeOpenAiCompatibleModels({
139
140
  runtimeId: this.id,
140
- available,
141
- reason: available ? undefined : `${this.apiKeyEnvName} is not set`,
142
- checkedAt: new Date().toISOString(),
143
- };
141
+ baseUrl: this.baseUrl,
142
+ apiKey: this.apiKey,
143
+ apiKeyName: this.apiKeyEnvName,
144
+ model: this.model,
145
+ providerName: this.providerName,
146
+ probeKind: input.probeKind,
147
+ });
144
148
  }
145
149
  async runNode(capsule, signal) {
146
150
  try {
@@ -172,12 +176,13 @@ export class KimiApiRuntime {
172
176
  }
173
177
  }
174
178
  async execute(task) {
179
+ const compactionMeta = this.consumeCompactionContext(task);
175
180
  if (!this.apiKey) {
176
181
  return {
177
182
  output: "",
178
183
  exitCode: 1,
179
184
  thinking: "",
180
- metadata: { error: `${this.apiKeyEnvName} is not set` },
185
+ metadata: { error: `${this.apiKeyEnvName} is not set`, ...compactionMeta },
181
186
  };
182
187
  }
183
188
  if (task.capabilities.write ||
@@ -192,6 +197,7 @@ export class KimiApiRuntime {
192
197
  metadata: {
193
198
  error: `${this.id} is advisory/read-only and does not receive write, shell, MCP, merge, or patch authority`,
194
199
  authorityMode: "advisory",
200
+ ...compactionMeta,
195
201
  },
196
202
  };
197
203
  }
@@ -252,13 +258,14 @@ export class KimiApiRuntime {
252
258
  output: "",
253
259
  exitCode: 1,
254
260
  thinking: "",
255
- metadata: { error: `${this.providerName} API error ${response.status}: ${errorText}` },
261
+ metadata: { error: `${this.providerName} API error ${response.status}: ${errorText}`, ...compactionMeta },
256
262
  };
257
263
  }
258
264
  if (useStreaming) {
259
265
  return this.parseStreamResponse(response, {
260
266
  toolPlaneHash: providerTools.toolPlaneHash,
261
267
  toolContracts: providerTools.contracts,
268
+ compactionMeta,
262
269
  });
263
270
  }
264
271
  const contentType = response.headers.get("content-type") ?? "";
@@ -266,6 +273,7 @@ export class KimiApiRuntime {
266
273
  return this.parseStreamResponse(response, {
267
274
  toolPlaneHash: providerTools.toolPlaneHash,
268
275
  toolContracts: providerTools.contracts,
276
+ compactionMeta,
269
277
  });
270
278
  }
271
279
  const payload = (await response.json());
@@ -297,6 +305,7 @@ export class KimiApiRuntime {
297
305
  toolPlaneHash: providerTools.toolPlaneHash,
298
306
  toolContracts: providerTools.contracts,
299
307
  toolCallRepair: mappedToolCalls.repair,
308
+ ...compactionMeta,
300
309
  },
301
310
  };
302
311
  }
@@ -306,17 +315,31 @@ export class KimiApiRuntime {
306
315
  output: "",
307
316
  exitCode: 130,
308
317
  thinking: "",
309
- metadata: { error: "Request aborted" },
318
+ metadata: { error: "Request aborted", ...compactionMeta },
310
319
  };
311
320
  }
312
321
  return {
313
322
  output: "",
314
323
  exitCode: 1,
315
324
  thinking: "",
316
- metadata: { error: String(err) },
325
+ metadata: { error: String(err), ...compactionMeta },
317
326
  };
318
327
  }
319
328
  }
329
+ consumeCompactionContext(task) {
330
+ const compaction = task.context.compaction;
331
+ if (!compaction || compaction.schemaVersion !== "omk.task-compaction.v1") {
332
+ return { compactionConsumed: false };
333
+ }
334
+ return {
335
+ compactionConsumed: true,
336
+ compactionContract: typeof compaction.contract.schemaVersion === "string"
337
+ ? compaction.contract.schemaVersion
338
+ : "unknown",
339
+ compactionArtifactRef: compaction.artifactRef,
340
+ compactionQualityScore: compaction.diagnostics?.qualityScore,
341
+ };
342
+ }
320
343
  async parseStreamResponse(response, metadata) {
321
344
  const reader = response.body?.getReader();
322
345
  if (!reader) {
@@ -424,6 +447,7 @@ export class KimiApiRuntime {
424
447
  model,
425
448
  finishReason,
426
449
  ...metadata,
450
+ ...(metadata.compactionMeta ?? {}),
427
451
  toolCallRepair: mappedToolCalls.repair,
428
452
  },
429
453
  };
@@ -30,6 +30,25 @@ export function createKimiPrintRuntime(options = {}) {
30
30
  metadata: { runtime: "kimi-print", error: errorMsg },
31
31
  };
32
32
  }
33
+ const routing = capsule.node?.routing;
34
+ const approvalPolicy = routing?.approvalPolicy ?? routing?.executionPrompt;
35
+ const sandboxMode = routing?.sandboxMode;
36
+ const capabilities = routing?.assignedProviderCapabilities ?? [];
37
+ const isAdvisory = sandboxMode === "read-only" ||
38
+ capabilities.length === 0 ||
39
+ capabilities.every((c) => ["read", "review"].includes(c));
40
+ const requiresWriteShellMerge = capabilities.some((c) => ["write", "patch", "shell", "merge"].includes(c));
41
+ if (requiresWriteShellMerge && isAdvisory) {
42
+ const errorMsg = `[omk] kimi-print runtime is advisory/read-only and does not receive write, patch, shell, or merge authority`;
43
+ process.stderr.write(`${errorMsg}\n`);
44
+ return {
45
+ success: false,
46
+ exitCode: 1,
47
+ stdout: `[ERROR] ${errorMsg}`,
48
+ stderr: errorMsg,
49
+ metadata: { runtime: "kimi-print", error: errorMsg, authorityMode: "advisory" },
50
+ };
51
+ }
33
52
  const runner = createKimiTaskRunner(options);
34
53
  const resources = await getOmkResourceSettings();
35
54
  const env = {
@@ -41,6 +60,8 @@ export function createKimiPrintRuntime(options = {}) {
41
60
  OMK_HOOKS_ENABLED: resources.hooksScope === "none" ? "false" : "true",
42
61
  OMK_CONTEXT_BUDGET: capsule.budget.compression,
43
62
  OMK_TOTAL_TOKENS: String(capsule.budget.maxInputTokens),
63
+ OMK_APPROVAL_POLICY: approvalPolicy ?? "",
64
+ OMK_SANDBOX_MODE: sandboxMode ?? (isAdvisory ? "read-only" : "workspace-write"),
44
65
  ...(capsule.node ? dagNodeRoutingEnv(capsule.node) : {}),
45
66
  };
46
67
  const node = {
@@ -66,11 +66,27 @@ export class KimiWireProtocolRuntime {
66
66
  const kimiBin = resolveKimiBin();
67
67
  const available = await checkCommand(kimiBin);
68
68
  this.kimiAvailable = available;
69
+ const now = new Date();
69
70
  return {
70
71
  runtimeId: this.id,
71
72
  available,
72
73
  reason: available ? undefined : "`kimi` command not found in PATH",
73
- checkedAt: new Date().toISOString(),
74
+ checkedAt: now.toISOString(),
75
+ vector: {
76
+ runtimeOk: available,
77
+ authOk: available,
78
+ modelOk: true,
79
+ quotaOk: true,
80
+ rateLimitOk: true,
81
+ runtime: available ? "pass" : "fail",
82
+ auth: available ? "unknown" : "fail",
83
+ model: "unknown",
84
+ quota: "unknown",
85
+ rateLimit: "unknown",
86
+ lastProbeKind: "static",
87
+ checkedAt: now.toISOString(),
88
+ expiresAt: new Date(now.getTime() + 60_000).toISOString(),
89
+ },
74
90
  };
75
91
  }
76
92
  async runNode(capsule, signal) {
@@ -6,6 +6,7 @@
6
6
  * Supports SSE streaming and reasoning_content.
7
7
  */
8
8
  import type { AgentRuntime, AgentRunResult, AgentResult, AgentTask, RuntimeCapabilities, RuntimeHealth } from "./agent-runtime.js";
9
+ import type { RuntimeHealthProbeRequest } from "./contracts/shared.js";
9
10
  import type { ContextCapsule } from "./context-capsule.js";
10
11
  export interface LocalLlmRuntimeOptions {
11
12
  apiKey?: string;
@@ -14,6 +15,9 @@ export interface LocalLlmRuntimeOptions {
14
15
  }
15
16
  export declare class LocalLlmRuntime implements AgentRuntime {
16
17
  readonly id = "local-llm";
18
+ readonly providerId = "local-llm";
19
+ readonly advisory = true;
20
+ readonly runtimeMode = "api";
17
21
  readonly kind = "api";
18
22
  readonly priority = 35;
19
23
  readonly capabilities: RuntimeCapabilities;
@@ -22,7 +26,7 @@ export declare class LocalLlmRuntime implements AgentRuntime {
22
26
  private readonly baseUrl;
23
27
  constructor(options?: LocalLlmRuntimeOptions);
24
28
  supports(capsule: ContextCapsule): boolean;
25
- health(): Promise<RuntimeHealth>;
29
+ health(input?: RuntimeHealthProbeRequest): Promise<RuntimeHealth>;
26
30
  runNode(capsule: ContextCapsule, signal: AbortSignal): Promise<AgentRunResult>;
27
31
  execute(task: AgentTask): Promise<AgentResult>;
28
32
  private parseStreamResponse;
@@ -7,8 +7,12 @@
7
7
  */
8
8
  import { capsuleToTask } from "./context-broker-converter.js";
9
9
  import { sanitizeUserVisibleOutput } from "../util/user-visible-output.js";
10
+ import { probeOpenAiCompatibleModels } from "./runtime-health-probes.js";
10
11
  export class LocalLlmRuntime {
11
12
  id = "local-llm";
13
+ providerId = "local-llm";
14
+ advisory = true;
15
+ runtimeMode = "api";
12
16
  kind = "api";
13
17
  priority = 35;
14
18
  capabilities = {
@@ -40,29 +44,16 @@ export class LocalLlmRuntime {
40
44
  return false;
41
45
  return true;
42
46
  }
43
- async health() {
44
- try {
45
- const controller = new AbortController();
46
- const timeout = setTimeout(() => controller.abort(), 5000);
47
- const resp = await fetch(`${this.baseUrl}/models`, {
48
- signal: controller.signal,
49
- });
50
- clearTimeout(timeout);
51
- return {
52
- runtimeId: this.id,
53
- available: resp.ok,
54
- reason: resp.ok ? undefined : `Local LLM returned ${resp.status}`,
55
- checkedAt: new Date().toISOString(),
56
- };
57
- }
58
- catch (err) {
59
- return {
60
- runtimeId: this.id,
61
- available: false,
62
- reason: `Local LLM not reachable: ${err instanceof Error ? err.message : String(err)}`,
63
- checkedAt: new Date().toISOString(),
64
- };
65
- }
47
+ async health(input = { probeKind: "static", highRisk: false }) {
48
+ return probeOpenAiCompatibleModels({
49
+ runtimeId: this.id,
50
+ baseUrl: this.baseUrl,
51
+ apiKey: this.apiKey,
52
+ apiKeyName: "LOCAL_LLM_API_KEY",
53
+ model: this.model,
54
+ providerName: "Local LLM",
55
+ probeKind: input.probeKind === "static" ? "static" : input.probeKind,
56
+ });
66
57
  }
67
58
  async runNode(capsule, signal) {
68
59
  try {
@@ -0,0 +1,13 @@
1
+ export interface PrivateStderrRetentionOptions {
2
+ readonly runId?: string;
3
+ readonly nodeId?: string;
4
+ readonly runtimeId?: string;
5
+ readonly root?: string;
6
+ readonly env?: Record<string, string | undefined>;
7
+ }
8
+ export interface StderrSanitizableResult {
9
+ readonly stderr: string;
10
+ readonly metadata?: Record<string, unknown>;
11
+ }
12
+ export declare function privateStderrRetentionEnabled(env?: Record<string, string | undefined>): boolean;
13
+ export declare function sanitizeRuntimeStderrResult<T extends StderrSanitizableResult>(result: T, options?: PrivateStderrRetentionOptions): T;
@@ -0,0 +1,69 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdirSync, writeFileSync } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { getRunArtifactPath } from "../util/run-store.js";
5
+ import { maskSensitiveText } from "../util/secret-mask.js";
6
+ export function privateStderrRetentionEnabled(env) {
7
+ const value = env?.OMK_PRIVATE_STDERR_ARTIFACTS ?? env?.OMK_DEBUG;
8
+ return /^(1|true|yes|on)$/i.test(value ?? "");
9
+ }
10
+ export function sanitizeRuntimeStderrResult(result, options = {}) {
11
+ const originalStderr = result.stderr ?? "";
12
+ const sanitizedStderr = maskSensitiveText(originalStderr);
13
+ const redacted = sanitizedStderr !== originalStderr;
14
+ const privateArtifact = writePrivateStderrArtifact(sanitizedStderr, originalStderr, options);
15
+ if (!redacted && !privateArtifact && sanitizedStderr === result.stderr)
16
+ return result;
17
+ return {
18
+ ...result,
19
+ stderr: sanitizedStderr,
20
+ metadata: {
21
+ ...(result.metadata ?? {}),
22
+ stderrRedacted: true,
23
+ secretLikeContentRedacted: redacted,
24
+ stderrPreview: sanitizedStderr.slice(0, 500),
25
+ ...(privateArtifact && {
26
+ stderrPrivateArtifact: privateArtifact.path,
27
+ stderrPrivateArtifactSha256: privateArtifact.sha256,
28
+ stderrRetainedPrivately: true,
29
+ }),
30
+ },
31
+ };
32
+ }
33
+ function writePrivateStderrArtifact(sanitizedStderr, originalStderr, options) {
34
+ if (!privateStderrRetentionEnabled(options.env))
35
+ return undefined;
36
+ if (sanitizedStderr.trim().length === 0)
37
+ return undefined;
38
+ const runId = options.runId;
39
+ if (!runId || runId.startsWith("local-"))
40
+ return undefined;
41
+ try {
42
+ const node = safeArtifactSegment(options.nodeId, "node");
43
+ const runtime = safeArtifactSegment(options.runtimeId, "runtime");
44
+ const sha256 = createHash("sha256").update(sanitizedStderr).digest("hex");
45
+ const rel = `private/stderr/${node}-${runtime}-${sha256.slice(0, 12)}.json`;
46
+ const full = getRunArtifactPath(runId, rel, options.root);
47
+ mkdirSync(dirname(full), { recursive: true });
48
+ writeFileSync(full, JSON.stringify({
49
+ schemaVersion: "omk.private-stderr.v1",
50
+ runId,
51
+ nodeId: options.nodeId,
52
+ runtimeId: options.runtimeId,
53
+ redaction: "secret-like-content-masked-before-retention",
54
+ publicPreviewLength: 500,
55
+ sha256,
56
+ rawSha256: createHash("sha256").update(originalStderr).digest("hex"),
57
+ stderr: sanitizedStderr,
58
+ timestamp: new Date().toISOString(),
59
+ }, null, 2), "utf-8");
60
+ return { path: rel, sha256 };
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
65
+ }
66
+ function safeArtifactSegment(value, fallback) {
67
+ const sanitized = (value ?? fallback).replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 48);
68
+ return sanitized.length > 0 ? sanitized : fallback;
69
+ }
@@ -9,7 +9,7 @@ export interface PromptEnvelopeInput {
9
9
  readonly nodeId?: string;
10
10
  readonly runId?: string;
11
11
  readonly executionPrompt?: string;
12
- readonly turnRisk?: "read" | "write" | "shell" | "merge";
12
+ readonly turnRisk?: "read" | "write" | "shell" | "merge" | "ask";
13
13
  readonly sandboxMode?: "read-only" | "workspace-write";
14
14
  }
15
15
  export interface PromptEnvelopeSection {
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Provider sovereignty scoring for Freedomd.
3
+ *
4
+ * Translates provider jurisdiction, retention, cutoff/portability, and locality
5
+ * properties into a normalized score that feeds runtime routing. The score is
6
+ * intentionally conservative: unknowns are treated as risk.
7
+ */
8
+ import type { AgentRuntime, AgentTask } from "./agent-runtime.js";
9
+ import type { RuntimeHealth } from "./contracts/shared.js";
10
+ export type ExportPolicy = "open" | "restricted" | "controlled" | "unknown";
11
+ export interface ProviderSovereigntyProfile {
12
+ readonly providerId: string;
13
+ readonly runtimeMode: string;
14
+ readonly providerCountry: string;
15
+ readonly exportPolicy: ExportPolicy;
16
+ readonly retentionDays: number;
17
+ readonly zeroDataRetention: boolean;
18
+ readonly trainingUse: boolean;
19
+ readonly supportsSelfHosted: boolean;
20
+ readonly openApiCompatible: boolean;
21
+ readonly policyInstabilityScore: number;
22
+ readonly exportControlExposure: number;
23
+ readonly accountDependency: number;
24
+ readonly metadataEvidenceSupport: boolean;
25
+ }
26
+ export interface ProviderIncidentState {
27
+ readonly providerId: string;
28
+ readonly runtimeMode?: string;
29
+ readonly kind: "availability" | "policy" | "export-control" | "retention" | "jurisdiction";
30
+ readonly severity: "info" | "warn" | "block";
31
+ readonly reason: string;
32
+ readonly updatedAt: string;
33
+ }
34
+ export interface SovereigntyDiagnostics {
35
+ readonly jurisdictionRisk: number;
36
+ readonly retentionRisk: number;
37
+ readonly cutoffRisk: number;
38
+ readonly localityScore: number;
39
+ readonly portabilityScore: number;
40
+ readonly incidentSeverity?: string;
41
+ readonly reason: string;
42
+ }
43
+ export interface SovereigntyScore {
44
+ readonly score: number;
45
+ readonly diagnostics: SovereigntyDiagnostics;
46
+ }
47
+ export interface ComputeSovereigntyOptions {
48
+ readonly profile: ProviderSovereigntyProfile;
49
+ readonly userCountry?: string;
50
+ readonly taskRisk?: string;
51
+ readonly incidents?: readonly ProviderIncidentState[];
52
+ readonly localAvailable?: boolean;
53
+ }
54
+ export declare function assessJurisdictionRisk(options: {
55
+ readonly providerCountry: string;
56
+ readonly userCountry?: string;
57
+ readonly taskRisk?: string;
58
+ readonly exportPolicy: ExportPolicy;
59
+ readonly incidents?: readonly ProviderIncidentState[];
60
+ }): number;
61
+ export declare function assessRetentionRisk(options: {
62
+ readonly retentionDays: number;
63
+ readonly zeroDataRetention: boolean;
64
+ readonly trainingUse: boolean;
65
+ readonly maxRetentionDays?: number;
66
+ }): number;
67
+ export declare function assessCutoffRisk(options: {
68
+ readonly health?: RuntimeHealth;
69
+ readonly policyInstabilityScore: number;
70
+ readonly exportControlExposure: number;
71
+ readonly accountDependency: number;
72
+ readonly incidents?: readonly ProviderIncidentState[];
73
+ }): number;
74
+ export declare function assessPortability(options: {
75
+ readonly supportsOpenApiCompat: boolean;
76
+ readonly supportsToolContract: boolean;
77
+ readonly supportsEvidenceReturn: boolean;
78
+ }): number;
79
+ export declare function computeProviderSovereigntyScore(runtime: AgentRuntime, task: AgentTask, options: ComputeSovereigntyOptions): SovereigntyScore;
80
+ export declare function buildProviderSovereigntyProfiles(): Readonly<Record<string, ProviderSovereigntyProfile>>;
81
+ export declare function lookupSovereigntyProfile(runtime: AgentRuntime, profiles?: Readonly<Record<string, ProviderSovereigntyProfile>>): ProviderSovereigntyProfile | undefined;