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
@@ -0,0 +1,285 @@
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 { runtimeProviderId, runtimeModeOf } from "./authority-matrix.js";
9
+ const EXPORT_POLICY_RISK = {
10
+ open: 0.0,
11
+ restricted: 0.4,
12
+ controlled: 0.8,
13
+ unknown: 0.6,
14
+ };
15
+ function clamp01(value) {
16
+ return Math.max(0, Math.min(1, value));
17
+ }
18
+ export function assessJurisdictionRisk(options) {
19
+ let risk = EXPORT_POLICY_RISK[options.exportPolicy] ?? 0.6;
20
+ if (options.userCountry && options.providerCountry !== options.userCountry) {
21
+ risk = Math.max(risk, 0.3);
22
+ }
23
+ if (options.taskRisk === "shell" || options.taskRisk === "merge") {
24
+ risk = Math.min(1, risk * 1.3 + 0.1);
25
+ }
26
+ const incident = (options.incidents ?? []).find((i) => i.kind === "export-control" || i.kind === "jurisdiction" || i.kind === "policy");
27
+ if (incident) {
28
+ const severityMultiplier = incident.severity === "block" ? 1.0 : incident.severity === "warn" ? 0.5 : 0.2;
29
+ risk = Math.min(1, risk + severityMultiplier * 0.3);
30
+ }
31
+ return clamp01(risk);
32
+ }
33
+ export function assessRetentionRisk(options) {
34
+ if (options.zeroDataRetention)
35
+ return 0.0;
36
+ if (!Number.isFinite(options.retentionDays) || options.retentionDays <= 0)
37
+ return 0.5;
38
+ let risk = clamp01(options.retentionDays / 365);
39
+ if (options.trainingUse) {
40
+ risk = Math.min(1, risk + 0.3);
41
+ }
42
+ if (options.maxRetentionDays !== undefined && options.maxRetentionDays >= 0) {
43
+ if (options.retentionDays > options.maxRetentionDays) {
44
+ risk = Math.min(1, risk + 0.4);
45
+ }
46
+ }
47
+ return risk;
48
+ }
49
+ export function assessCutoffRisk(options) {
50
+ let risk = 0;
51
+ risk += clamp01(options.policyInstabilityScore) * 0.35;
52
+ risk += clamp01(options.exportControlExposure) * 0.35;
53
+ risk += clamp01(options.accountDependency) * 0.30;
54
+ if (options.health?.available === false) {
55
+ risk = Math.min(1, risk + 0.5);
56
+ }
57
+ const incident = (options.incidents ?? []).find((i) => i.kind === "availability" || i.kind === "export-control" || i.kind === "policy");
58
+ if (incident) {
59
+ const bump = incident.severity === "block" ? 0.6 : incident.severity === "warn" ? 0.3 : 0.1;
60
+ risk = Math.min(1, risk + bump);
61
+ }
62
+ return clamp01(risk);
63
+ }
64
+ export function assessPortability(options) {
65
+ let score = 0.4;
66
+ if (options.supportsOpenApiCompat)
67
+ score += 0.25;
68
+ if (options.supportsToolContract)
69
+ score += 0.15;
70
+ if (options.supportsEvidenceReturn)
71
+ score += 0.20;
72
+ return clamp01(score);
73
+ }
74
+ export function computeProviderSovereigntyScore(runtime, task, options) {
75
+ const profile = options.profile;
76
+ const providerId = runtimeProviderId(runtime);
77
+ const runtimeMode = runtimeModeOf(runtime);
78
+ const incidents = (options.incidents ?? []).filter((i) => i.providerId === providerId &&
79
+ (!i.runtimeMode || i.runtimeMode === runtimeMode));
80
+ const jurisdictionRisk = assessJurisdictionRisk({
81
+ providerCountry: profile.providerCountry,
82
+ userCountry: options.userCountry,
83
+ taskRisk: options.taskRisk ?? task.safety?.risk,
84
+ exportPolicy: profile.exportPolicy,
85
+ incidents,
86
+ });
87
+ const retentionRisk = assessRetentionRisk({
88
+ retentionDays: profile.retentionDays,
89
+ zeroDataRetention: profile.zeroDataRetention,
90
+ trainingUse: profile.trainingUse,
91
+ });
92
+ const cutoffRisk = assessCutoffRisk({
93
+ policyInstabilityScore: profile.policyInstabilityScore,
94
+ exportControlExposure: profile.exportControlExposure,
95
+ accountDependency: profile.accountDependency,
96
+ incidents,
97
+ });
98
+ const localityScore = options.localAvailable
99
+ ? 1.0
100
+ : profile.supportsSelfHosted
101
+ ? 0.8
102
+ : 0.4;
103
+ const portabilityScore = assessPortability({
104
+ supportsOpenApiCompat: profile.openApiCompatible,
105
+ supportsToolContract: runtime.capabilities?.toolCalling === true || runtime.capabilities?.supportsToolCalling === true,
106
+ supportsEvidenceReturn: profile.metadataEvidenceSupport,
107
+ });
108
+ const score = 0.25 * (1 - jurisdictionRisk) +
109
+ 0.25 * (1 - retentionRisk) +
110
+ 0.20 * (1 - cutoffRisk) +
111
+ 0.15 * localityScore +
112
+ 0.15 * portabilityScore;
113
+ const diagnostics = {
114
+ jurisdictionRisk,
115
+ retentionRisk,
116
+ cutoffRisk,
117
+ localityScore,
118
+ portabilityScore,
119
+ incidentSeverity: incidents.length > 0 ? incidents[0].severity : undefined,
120
+ reason: `jurisdiction=${jurisdictionRisk.toFixed(2)} retention=${retentionRisk.toFixed(2)} cutoff=${cutoffRisk.toFixed(2)} locality=${localityScore.toFixed(2)} portability=${portabilityScore.toFixed(2)}`,
121
+ };
122
+ return { score: clamp01(score), diagnostics };
123
+ }
124
+ export function buildProviderSovereigntyProfiles() {
125
+ const now = new Date().toISOString();
126
+ const profiles = {
127
+ "local-llm": {
128
+ providerId: "local-llm",
129
+ runtimeMode: "api",
130
+ providerCountry: "user-device",
131
+ exportPolicy: "open",
132
+ retentionDays: 0,
133
+ zeroDataRetention: true,
134
+ trainingUse: false,
135
+ supportsSelfHosted: true,
136
+ openApiCompatible: true,
137
+ policyInstabilityScore: 0.05,
138
+ exportControlExposure: 0.0,
139
+ accountDependency: 0.0,
140
+ metadataEvidenceSupport: true,
141
+ },
142
+ codex: {
143
+ providerId: "codex",
144
+ runtimeMode: "cli",
145
+ providerCountry: "US",
146
+ exportPolicy: "restricted",
147
+ retentionDays: 0,
148
+ zeroDataRetention: true,
149
+ trainingUse: false,
150
+ supportsSelfHosted: false,
151
+ openApiCompatible: false,
152
+ policyInstabilityScore: 0.2,
153
+ exportControlExposure: 0.3,
154
+ accountDependency: 0.4,
155
+ metadataEvidenceSupport: true,
156
+ },
157
+ kimi: {
158
+ providerId: "kimi",
159
+ runtimeMode: "api",
160
+ providerCountry: "CN",
161
+ exportPolicy: "restricted",
162
+ retentionDays: 30,
163
+ zeroDataRetention: false,
164
+ trainingUse: false,
165
+ supportsSelfHosted: false,
166
+ openApiCompatible: true,
167
+ policyInstabilityScore: 0.25,
168
+ exportControlExposure: 0.35,
169
+ accountDependency: 0.5,
170
+ metadataEvidenceSupport: true,
171
+ },
172
+ mimo: {
173
+ providerId: "mimo",
174
+ runtimeMode: "api",
175
+ providerCountry: "CN",
176
+ exportPolicy: "restricted",
177
+ retentionDays: 30,
178
+ zeroDataRetention: false,
179
+ trainingUse: false,
180
+ supportsSelfHosted: false,
181
+ openApiCompatible: true,
182
+ policyInstabilityScore: 0.25,
183
+ exportControlExposure: 0.35,
184
+ accountDependency: 0.5,
185
+ metadataEvidenceSupport: true,
186
+ },
187
+ deepseek: {
188
+ providerId: "deepseek",
189
+ runtimeMode: "api",
190
+ providerCountry: "CN",
191
+ exportPolicy: "restricted",
192
+ retentionDays: 30,
193
+ zeroDataRetention: false,
194
+ trainingUse: false,
195
+ supportsSelfHosted: false,
196
+ openApiCompatible: true,
197
+ policyInstabilityScore: 0.3,
198
+ exportControlExposure: 0.4,
199
+ accountDependency: 0.5,
200
+ metadataEvidenceSupport: true,
201
+ },
202
+ opencode: {
203
+ providerId: "opencode",
204
+ runtimeMode: "cli",
205
+ providerCountry: "KR",
206
+ exportPolicy: "open",
207
+ retentionDays: 0,
208
+ zeroDataRetention: true,
209
+ trainingUse: false,
210
+ supportsSelfHosted: false,
211
+ openApiCompatible: false,
212
+ policyInstabilityScore: 0.2,
213
+ exportControlExposure: 0.1,
214
+ accountDependency: 0.3,
215
+ metadataEvidenceSupport: true,
216
+ },
217
+ commandcode: {
218
+ providerId: "commandcode",
219
+ runtimeMode: "cli",
220
+ providerCountry: "US",
221
+ exportPolicy: "open",
222
+ retentionDays: 0,
223
+ zeroDataRetention: true,
224
+ trainingUse: false,
225
+ supportsSelfHosted: false,
226
+ openApiCompatible: false,
227
+ policyInstabilityScore: 0.2,
228
+ exportControlExposure: 0.1,
229
+ accountDependency: 0.3,
230
+ metadataEvidenceSupport: true,
231
+ },
232
+ glm: {
233
+ providerId: "glm",
234
+ runtimeMode: "api",
235
+ providerCountry: "CN",
236
+ exportPolicy: "restricted",
237
+ retentionDays: 30,
238
+ zeroDataRetention: false,
239
+ trainingUse: false,
240
+ supportsSelfHosted: false,
241
+ openApiCompatible: true,
242
+ policyInstabilityScore: 0.3,
243
+ exportControlExposure: 0.4,
244
+ accountDependency: 0.5,
245
+ metadataEvidenceSupport: true,
246
+ },
247
+ openrouter: {
248
+ providerId: "openrouter",
249
+ runtimeMode: "api",
250
+ providerCountry: "US",
251
+ exportPolicy: "restricted",
252
+ retentionDays: 30,
253
+ zeroDataRetention: false,
254
+ trainingUse: false,
255
+ supportsSelfHosted: false,
256
+ openApiCompatible: true,
257
+ policyInstabilityScore: 0.25,
258
+ exportControlExposure: 0.3,
259
+ accountDependency: 0.6,
260
+ metadataEvidenceSupport: true,
261
+ },
262
+ qwen: {
263
+ providerId: "qwen",
264
+ runtimeMode: "api",
265
+ providerCountry: "CN",
266
+ exportPolicy: "restricted",
267
+ retentionDays: 30,
268
+ zeroDataRetention: false,
269
+ trainingUse: false,
270
+ supportsSelfHosted: false,
271
+ openApiCompatible: true,
272
+ policyInstabilityScore: 0.3,
273
+ exportControlExposure: 0.4,
274
+ accountDependency: 0.5,
275
+ metadataEvidenceSupport: true,
276
+ },
277
+ };
278
+ void now;
279
+ return profiles;
280
+ }
281
+ export function lookupSovereigntyProfile(runtime, profiles) {
282
+ const map = profiles ?? buildProviderSovereigntyProfiles();
283
+ const providerId = runtimeProviderId(runtime);
284
+ return map[providerId] ?? map[runtime.id];
285
+ }
@@ -16,5 +16,6 @@ export interface RuntimeBackedTaskRunnerOptions {
16
16
  runId?: string;
17
17
  goal?: string;
18
18
  onOutput?: (text: string) => void;
19
+ headroomCompactor?: (text: string) => Promise<string | null>;
19
20
  }
20
21
  export declare function createRuntimeBackedTaskRunner(options: RuntimeBackedTaskRunnerOptions): Promise<TaskRunner>;
@@ -6,15 +6,24 @@
6
6
  * 2. Route to best AgentRuntime via RuntimeRouter
7
7
  * 3. Convert AgentRunResult -> TaskResult
8
8
  */
9
- import { readFileSync } from "node:fs";
9
+ import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
10
10
  import { join } from "node:path";
11
11
  import { toTaskResult } from "./agent-runtime.js";
12
+ import { checkEvidenceGate, hasDeclaredEvidenceRequirement } from "./contracts/evidence.js";
13
+ function isStrictGuardrailMode() {
14
+ const raw = process.env.OMK_STRICT_GUARDRAIL ?? "";
15
+ const normalized = raw.trim().toLowerCase();
16
+ return normalized === "1" || normalized === "true" || normalized === "on";
17
+ }
12
18
  import { capsuleToTask } from "./context-broker-converter.js";
13
19
  import { applyTaskRunContextToAgentTask, envFromWorkerManifest } from "./worker-manifest.js";
14
20
  import { createRuntimeRegistry } from "./runtime-registry.js";
15
21
  import { createRuntimeRouter } from "./runtime-router.js";
22
+ import { createFreedomdRuntimeRouter } from "./freedomd-router.js";
16
23
  import { createContextBroker } from "./context-broker.js";
24
+ import { estimateCapsuleTokens } from "./context-capsule.js";
17
25
  import { maybeCompactWithHeadroom } from "./headroom-policy.js";
26
+ import { DEFAULT_STRUCTURED_COMPACTION_CONTRACT, buildStructuredCompactionText, buildTypedStructuredCompactionContract, computeCompactionQualityScore, estimateTextTokens, structuredCompactionGuardNote, structuredCompactionInstruction, validateStructuredCompaction, } from "./structured-compaction.js";
18
27
  import { DeepSeekRuntime } from "./deepseek-runtime.js";
19
28
  import { CodexRuntime } from "./codex-runtime.js";
20
29
  import { createOpencodeCliAdapter } from "../adapters/opencode/opencode-cli-adapter.js";
@@ -26,6 +35,7 @@ import { createMimoApiRuntime } from "./mimo-api-runtime.js";
26
35
  import { createKimiApiRuntime } from "./kimi-api-runtime.js";
27
36
  import { createGlmApiRuntime } from "./glm-api-runtime.js";
28
37
  import { getUserHome } from "../util/fs.js";
38
+ import { LocalGraphMemoryStore } from "../memory/local-graph-memory-store.js";
29
39
  async function createDefaultRuntimeRegistry(options) {
30
40
  const registry = createRuntimeRegistry();
31
41
  // ── MiMo API runtime (Xiaomi MiMo — OpenAI-compatible, highest priority) ──
@@ -115,15 +125,182 @@ function readProviderConfig(configDir) {
115
125
  function escapeRegExp(value) {
116
126
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
117
127
  }
128
+ function applyCompactedTextToCapsule(capsule, compactedText) {
129
+ const trimmed = compactedText.trim();
130
+ const beforeChars = JSON.stringify(capsule).length;
131
+ const beforeTokens = estimateCapsuleTokens(capsule);
132
+ const baseDiagnostics = {
133
+ beforeChars,
134
+ beforeTokens,
135
+ compactedTextProduced: trimmed.length > 0,
136
+ };
137
+ if (trimmed.length === 0) {
138
+ return {
139
+ capsule,
140
+ diagnostics: {
141
+ ...baseDiagnostics,
142
+ afterChars: 0,
143
+ afterTokens: 0,
144
+ validated: false,
145
+ applied: false,
146
+ missingSections: [],
147
+ reason: "empty compacted text",
148
+ },
149
+ };
150
+ }
151
+ const afterChars = trimmed.length;
152
+ const afterTokens = estimateTextTokens(trimmed);
153
+ if (afterChars >= beforeChars) {
154
+ return {
155
+ capsule,
156
+ diagnostics: {
157
+ ...baseDiagnostics,
158
+ afterChars,
159
+ afterTokens,
160
+ validated: false,
161
+ applied: false,
162
+ missingSections: [],
163
+ reason: "compacted text is not smaller than original capsule",
164
+ },
165
+ };
166
+ }
167
+ // If compaction stripped required structural context, keep original but note the guard.
168
+ const validation = validateStructuredCompaction(trimmed, capsule);
169
+ if (!validation.ok) {
170
+ return {
171
+ capsule: {
172
+ ...capsule,
173
+ system: [
174
+ capsule.system,
175
+ "",
176
+ "[OMK headroom compacted context]",
177
+ structuredCompactionGuardNote(validation),
178
+ ].filter(Boolean).join("\n"),
179
+ budget: { ...capsule.budget, compression: "summary" },
180
+ },
181
+ diagnostics: {
182
+ ...baseDiagnostics,
183
+ afterChars,
184
+ afterTokens,
185
+ validated: false,
186
+ applied: false,
187
+ missingSections: validation.missing,
188
+ contract: validation.contract.schemaVersion,
189
+ reason: "structured compaction contract validation failed",
190
+ },
191
+ };
192
+ }
193
+ return {
194
+ capsule: {
195
+ ...capsule,
196
+ system: [
197
+ capsule.system,
198
+ "",
199
+ "[OMK headroom compacted context]",
200
+ structuredCompactionInstruction(validation.contract),
201
+ trimmed,
202
+ ].filter(Boolean).join("\n"),
203
+ dependencySummaries: [],
204
+ relevantFiles: [],
205
+ graphMemory: [],
206
+ priorAttempts: [],
207
+ budget: { ...capsule.budget, compression: "summary" },
208
+ },
209
+ diagnostics: {
210
+ ...baseDiagnostics,
211
+ afterChars,
212
+ afterTokens,
213
+ validated: true,
214
+ applied: true,
215
+ missingSections: [],
216
+ contract: validation.contract.schemaVersion,
217
+ reason: "compaction applied",
218
+ },
219
+ };
220
+ }
221
+ function persistHeadroomDecisionArtifact(input) {
222
+ const rawRunId = input.runId?.trim() || "runtime-backed-task-runner";
223
+ const runId = sanitizeArtifactSegment(rawRunId);
224
+ const nodeId = sanitizeArtifactSegment(input.nodeId || "node");
225
+ const relativePath = `.omk/runs/${runId}/headroom-decisions.jsonl`;
226
+ try {
227
+ const dir = join(input.cwd, ".omk", "runs", runId);
228
+ mkdirSync(dir, { recursive: true });
229
+ const record = {
230
+ schemaVersion: "omk.headroom-decision.v1",
231
+ runId,
232
+ nodeId,
233
+ timestamp: new Date().toISOString(),
234
+ ...input.metadata,
235
+ artifactRef: undefined,
236
+ };
237
+ appendFileSync(join(input.cwd, relativePath), `${JSON.stringify(record)}\n`, "utf-8");
238
+ return relativePath;
239
+ }
240
+ catch {
241
+ return undefined;
242
+ }
243
+ }
244
+ async function materializeHeadroomDecisionGraph(input) {
245
+ const runId = input.runId?.trim();
246
+ if (!runId || runId.startsWith("local-"))
247
+ return;
248
+ try {
249
+ const store = await LocalGraphMemoryStore.create({ projectRoot: input.cwd, sessionId: runId, source: "headroom-compaction" });
250
+ await store?.materializeHeadroomDecision({
251
+ runId,
252
+ nodeId: input.nodeId,
253
+ metadata: input.metadata,
254
+ artifactRef: input.metadata.artifactRef,
255
+ });
256
+ }
257
+ catch {
258
+ // Best-effort graph materialization must never block runtime dispatch.
259
+ }
260
+ }
261
+ function buildAgentContextCompaction(input) {
262
+ if (!input.metadata?.applied)
263
+ return undefined;
264
+ return {
265
+ schemaVersion: "omk.task-compaction.v1",
266
+ contract: buildTypedStructuredCompactionContract(input.capsule),
267
+ diagnostics: {
268
+ backend: input.metadata.backend,
269
+ beforeTokens: input.metadata.beforeTokens,
270
+ afterTokens: input.metadata.afterTokens,
271
+ validated: input.metadata.validated,
272
+ applied: input.metadata.applied,
273
+ qualityScore: input.metadata.qualityScore,
274
+ compressionRatio: input.metadata.compressionRatio,
275
+ contract: input.metadata.contract,
276
+ reason: input.metadata.reason,
277
+ },
278
+ artifactRef: input.metadata.artifactRef,
279
+ };
280
+ }
281
+ function sanitizeArtifactSegment(value) {
282
+ return value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 128) || "unknown";
283
+ }
118
284
  export async function createRuntimeBackedTaskRunner(options) {
119
285
  const registry = await createDefaultRuntimeRegistry(options);
120
286
  const runtimes = registry.list();
121
- const runtimeRouter = createRuntimeRouter({
122
- runtimes,
123
- fallbackChain: options.fallbackChain,
124
- });
287
+ const freedomdEnabled = (process.env.OMK_FREEDOMD_MODE ?? "off").trim().toLowerCase() !== "off";
288
+ const runtimeRouter = freedomdEnabled
289
+ ? await createFreedomdRuntimeRouter({
290
+ runtimes,
291
+ fallbackChain: options.fallbackChain,
292
+ projectRoot: options.cwd,
293
+ autoLoadIncidents: true,
294
+ isTty: process.stdin.isTTY,
295
+ })
296
+ : createRuntimeRouter({
297
+ runtimes,
298
+ fallbackChain: options.fallbackChain,
299
+ });
125
300
  registry.onChange((nextRuntimes) => {
126
- runtimeRouter.setRuntimes(nextRuntimes);
301
+ if ("setRuntimes" in runtimeRouter) {
302
+ runtimeRouter.setRuntimes?.(nextRuntimes);
303
+ }
127
304
  });
128
305
  const contextBroker = createContextBroker({
129
306
  projectRoot: options.cwd,
@@ -140,14 +317,67 @@ export async function createRuntimeBackedTaskRunner(options) {
140
317
  }
141
318
  : undefined;
142
319
  const { capsule, headroomDecision } = await contextBroker.buildCapsule(node, runState);
320
+ let effectiveCapsule = capsule;
321
+ let headroomCompaction;
143
322
  // CTX guard: compact via headroom before the context window crosses the threshold (~90%).
144
323
  if (headroomDecision?.shouldCompact) {
145
- await maybeCompactWithHeadroom({
324
+ const compactResult = await maybeCompactWithHeadroom({
146
325
  decision: headroomDecision,
147
326
  text: JSON.stringify(capsule),
327
+ runHeadroom: options.headroomCompactor,
328
+ fallbackText: () => buildStructuredCompactionText(capsule),
148
329
  }).catch(() => undefined);
330
+ if (compactResult) {
331
+ let applyDiagnostics;
332
+ if (compactResult.compactedText) {
333
+ const applied = applyCompactedTextToCapsule(capsule, compactResult.compactedText);
334
+ effectiveCapsule = applied.capsule;
335
+ applyDiagnostics = applied.diagnostics;
336
+ }
337
+ const quality = computeCompactionQualityScore({
338
+ applied: applyDiagnostics?.applied ?? false,
339
+ validated: applyDiagnostics?.validated ?? false,
340
+ beforeTokens: applyDiagnostics?.beforeTokens,
341
+ afterTokens: applyDiagnostics?.afterTokens,
342
+ missingSections: applyDiagnostics?.missingSections ?? [],
343
+ });
344
+ headroomCompaction = {
345
+ attempted: compactResult.attempted,
346
+ compacted: compactResult.compacted,
347
+ via: compactResult.via,
348
+ backend: compactResult.backend,
349
+ compactedTextProduced: compactResult.compactedTextProduced,
350
+ validated: applyDiagnostics?.validated ?? false,
351
+ applied: applyDiagnostics?.applied ?? false,
352
+ beforeChars: applyDiagnostics?.beforeChars,
353
+ afterChars: applyDiagnostics?.afterChars,
354
+ beforeTokens: applyDiagnostics?.beforeTokens,
355
+ afterTokens: applyDiagnostics?.afterTokens,
356
+ threshold: headroomDecision.threshold,
357
+ utilization: headroomDecision.utilization,
358
+ contract: applyDiagnostics?.contract ?? DEFAULT_STRUCTURED_COMPACTION_CONTRACT.schemaVersion,
359
+ missingSections: applyDiagnostics?.missingSections ?? [],
360
+ reason: applyDiagnostics?.reason ?? compactResult.reason,
361
+ ...quality,
362
+ };
363
+ const materializedRunId = options.runId ?? capsule.runId;
364
+ const artifactRef = persistHeadroomDecisionArtifact({
365
+ cwd: options.cwd,
366
+ runId: materializedRunId,
367
+ nodeId: capsule.nodeId,
368
+ metadata: headroomCompaction,
369
+ });
370
+ if (artifactRef)
371
+ headroomCompaction = { ...headroomCompaction, artifactRef };
372
+ await materializeHeadroomDecisionGraph({
373
+ cwd: options.cwd,
374
+ runId: materializedRunId,
375
+ nodeId: capsule.nodeId,
376
+ metadata: headroomCompaction,
377
+ });
378
+ }
149
379
  }
150
- const routing = capsule.node.routing;
380
+ const routing = effectiveCapsule.node.routing;
151
381
  const providerFallbackChain = options.fallbackChain
152
382
  ?? (routing?.fallbackProvider ? [routing.fallbackProvider] : []);
153
383
  const abortSignal = signal ?? new AbortController().signal;
@@ -156,22 +386,61 @@ export async function createRuntimeBackedTaskRunner(options) {
156
386
  ...(env ?? {}),
157
387
  ...(runContext ? envFromWorkerManifest(runContext.worker) : {}),
158
388
  };
159
- const baseTask = applyTaskRunContextToAgentTask(await capsuleToTask(capsule, {
389
+ const rawTask = applyTaskRunContextToAgentTask(await capsuleToTask(effectiveCapsule, {
160
390
  signal: abortSignal,
161
391
  cwd: options.cwd,
162
392
  env: taskEnv,
163
393
  fallbackChain: providerFallbackChain,
164
394
  }), runContext);
395
+ const compactionContext = buildAgentContextCompaction({ capsule, metadata: headroomCompaction });
396
+ const baseTask = compactionContext
397
+ ? { ...rawTask, context: { ...rawTask.context, compaction: compactionContext } }
398
+ : rawTask;
165
399
  const task = options.onOutput
166
400
  ? { ...baseTask, context: { ...baseTask.context, onOutput: options.onOutput } }
167
401
  : baseTask;
168
- const agentResult = await runtimeRouter.executeTask(task, capsule, abortSignal);
402
+ const strictGuardrailMode = isStrictGuardrailMode();
403
+ const evidenceRequired = strictGuardrailMode && (task.safety.evidenceRequired || effectiveCapsule.node.routing?.evidenceRequired === true || isHighRiskTask(task));
404
+ const declaredEvidenceOk = !evidenceRequired || hasDeclaredEvidenceRequirement(effectiveCapsule.node.outputs);
405
+ if (evidenceRequired && !declaredEvidenceOk) {
406
+ return {
407
+ success: false,
408
+ exitCode: 78,
409
+ stdout: "",
410
+ stderr: "[omk] Evidence gate required but missing: high-risk task declares no required evidence output gate",
411
+ metadata: {
412
+ evidenceRequired,
413
+ evidenceDeclarationMissing: true,
414
+ fallbackChain: task.providerPolicy.fallbackChain,
415
+ },
416
+ };
417
+ }
418
+ const agentResult = await runtimeRouter.executeTask(task, effectiveCapsule, abortSignal);
169
419
  const taskResult = toTaskResult(agentResult);
420
+ // Post-execution evidence check for tasks that produced no metadata gate
421
+ if (evidenceRequired && agentResult.success && strictGuardrailMode) {
422
+ const postCheck = checkEvidenceGate(true, effectiveCapsule.node.outputs, taskResult.metadata ?? null, taskResult.stdout);
423
+ if (!postCheck.satisfied) {
424
+ return {
425
+ success: false,
426
+ exitCode: 78,
427
+ stdout: taskResult.stdout,
428
+ stderr: `[omk] Evidence gate required but task produced no replayable evidence: ${postCheck.reason}`,
429
+ metadata: {
430
+ ...taskResult.metadata,
431
+ evidenceRequired,
432
+ evidenceCheck: postCheck,
433
+ },
434
+ };
435
+ }
436
+ }
170
437
  // Ensure routing metadata is present even if the router failed to attach it
171
438
  taskResult.metadata = {
172
439
  ...(taskResult.metadata ?? {}),
173
440
  fallbackChain: task.providerPolicy.fallbackChain,
441
+ ...(headroomCompaction && { headroomCompaction }),
174
442
  ...(runContext && { workerOwner: runContext.worker.owner }),
443
+ ...(evidenceRequired && { evidenceRequired }),
175
444
  };
176
445
  return taskResult;
177
446
  },
@@ -182,3 +451,6 @@ export async function createRuntimeBackedTaskRunner(options) {
182
451
  runner._registry = registry;
183
452
  return runner;
184
453
  }
454
+ function isHighRiskTask(task) {
455
+ return task.capabilities.write === true || task.capabilities.patch === true || task.capabilities.shell === true || task.capabilities.merge === true;
456
+ }
@@ -19,3 +19,15 @@ export declare function resolveRuntimeBootstrap(options: {
19
19
  cwd?: string;
20
20
  env?: Record<string, string | undefined>;
21
21
  }): Promise<RuntimeBootstrap>;
22
+ export type ResolvedProviderPolicy = {
23
+ ok: true;
24
+ provider: string;
25
+ runtimeId?: string;
26
+ reason?: string;
27
+ } | {
28
+ ok: false;
29
+ provider: string;
30
+ reason: string;
31
+ remediation: string[];
32
+ };
33
+ export declare function resolveProviderPolicy(providerPolicy: string, env: Record<string, string | undefined>): Promise<ResolvedProviderPolicy>;