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,66 @@
1
+ /**
2
+ * Freedomd runtime router.
3
+ *
4
+ * Wraps the base RuntimeRouter with provider-sovereignty scoring, data-retention
5
+ * gating, cutoff-resilient fallback, degraded-mode planning, and local-first
6
+ * evidence envelopes. This is the control-plane boundary that keeps OMK loops
7
+ * alive when a provider becomes unavailable or policy-blocked.
8
+ */
9
+ import type { AgentRunResult, AgentRuntime, AgentTask, AgentTaskSovereignty } from "./agent-runtime.js";
10
+ import type { ContextCapsule } from "./context-capsule.js";
11
+ import type { LocalGraphMemoryStore } from "../memory/local-graph-memory-store.js";
12
+ import { type RuntimeRouterOptions } from "./runtime-router.js";
13
+ import { type FreedomdMode, type FreedomdPolicy } from "./freedomd-policy.js";
14
+ import { type ProviderIncidentState, type ProviderSovereigntyProfile, type SovereigntyScore } from "./provider-sovereignty.js";
15
+ import { type DataRetentionGateResult } from "./data-retention-gate.js";
16
+ import { type ProviderExceptionResult } from "./freedomd-exception.js";
17
+ export type { FreedomdMode };
18
+ export interface FreedomdRouterOptions extends RuntimeRouterOptions {
19
+ readonly freedomdMode?: FreedomdMode;
20
+ readonly userCountry?: string;
21
+ readonly sovereigntyProfiles?: Readonly<Record<string, ProviderSovereigntyProfile>>;
22
+ readonly incidents?: readonly ProviderIncidentState[];
23
+ readonly graphStore?: LocalGraphMemoryStore;
24
+ readonly allowRedaction?: boolean;
25
+ readonly projectRoot?: string;
26
+ readonly autoLoadIncidents?: boolean;
27
+ readonly promptUser?: (message: string) => Promise<string>;
28
+ readonly isTty?: boolean;
29
+ }
30
+ export interface FreedomdScoredRuntime {
31
+ readonly runtime: AgentRuntime;
32
+ readonly score: number;
33
+ readonly sovereignty: SovereigntyScore;
34
+ readonly retention: DataRetentionGateResult;
35
+ }
36
+ export interface FreedomdDegradedPlan {
37
+ readonly mode: string;
38
+ readonly runtime?: AgentRuntime;
39
+ readonly allowedCapabilities: readonly string[];
40
+ readonly reason: string;
41
+ readonly blockedOriginalAuthority?: readonly string[];
42
+ }
43
+ export interface FreedomdRouteResult {
44
+ readonly selectedRuntime?: AgentRuntime;
45
+ readonly fallbackChain: readonly AgentRuntime[];
46
+ readonly degradedPlan?: FreedomdDegradedPlan;
47
+ readonly sovereignty: AgentTaskSovereignty;
48
+ readonly diagnostics: string;
49
+ readonly providerException?: ProviderExceptionResult;
50
+ }
51
+ export declare function evaluateTaskSovereignty(task: AgentTask, policy: FreedomdPolicy, runtimes: readonly AgentRuntime[], incidents?: readonly ProviderIncidentState[]): AgentTaskSovereignty;
52
+ export declare function scoreRuntimesWithSovereignty(task: AgentTask, runtimes: readonly AgentRuntime[], policy: FreedomdPolicy, options?: {
53
+ readonly profiles?: Readonly<Record<string, ProviderSovereigntyProfile>>;
54
+ readonly incidents?: readonly ProviderIncidentState[];
55
+ readonly userCountry?: string;
56
+ readonly allowRedaction?: boolean;
57
+ readonly capsule?: ContextCapsule;
58
+ }): FreedomdScoredRuntime[];
59
+ export declare function buildFreedomdDegradedPlan(task: AgentTask, reason: string, runtimes: readonly AgentRuntime[]): FreedomdDegradedPlan;
60
+ export declare function createFreedomdRuntimeRouter(options?: FreedomdRouterOptions): Promise<{
61
+ readonly policy: FreedomdPolicy;
62
+ readonly baseRouter: unknown;
63
+ readonly freedomdRoute: (task: AgentTask, capsule: ContextCapsule) => Promise<FreedomdRouteResult>;
64
+ readonly scoreRuntimesWithSovereignty: (task: AgentTask) => FreedomdScoredRuntime[];
65
+ readonly executeTask: (task: AgentTask, capsule: ContextCapsule, signal: AbortSignal) => Promise<AgentRunResult>;
66
+ }>;
@@ -0,0 +1,433 @@
1
+ /**
2
+ * Freedomd runtime router.
3
+ *
4
+ * Wraps the base RuntimeRouter with provider-sovereignty scoring, data-retention
5
+ * gating, cutoff-resilient fallback, degraded-mode planning, and local-first
6
+ * evidence envelopes. This is the control-plane boundary that keeps OMK loops
7
+ * alive when a provider becomes unavailable or policy-blocked.
8
+ */
9
+ import { createRuntimeRouter } from "./runtime-router.js";
10
+ import { computeRuntimeCapabilityScore } from "./runtime-router.js";
11
+ import { compileFreedomdPolicy } from "./freedomd-policy.js";
12
+ import { buildProviderSovereigntyProfiles, computeProviderSovereigntyScore, lookupSovereigntyProfile, } from "./provider-sovereignty.js";
13
+ import { evaluateDataRetentionGate } from "./data-retention-gate.js";
14
+ import { buildLocalFirstEvidenceEnvelope } from "./freedomd-evidence-envelope.js";
15
+ import { runtimeProviderId, runtimeModeOf } from "./authority-matrix.js";
16
+ import { maskSensitiveText } from "../util/secret-mask.js";
17
+ import { loadProviderIncidents } from "./freedomd-incidents.js";
18
+ import { requestProviderException } from "./freedomd-exception.js";
19
+ function severityOrdinal(severity) {
20
+ if (severity === "block")
21
+ return 2;
22
+ if (severity === "warn")
23
+ return 1;
24
+ return 0;
25
+ }
26
+ function hasLocalRuntime(runtimes) {
27
+ return runtimes.some((r) => runtimeProviderId(r) === "local-llm" || r.id.startsWith("local-"));
28
+ }
29
+ export function evaluateTaskSovereignty(task, policy, runtimes, incidents) {
30
+ const localAvailable = hasLocalRuntime(runtimes);
31
+ const candidateProviderIds = new Set(runtimes.map((r) => runtimeProviderId(r)));
32
+ const matchedIncidents = (incidents ?? []).filter((i) => candidateProviderIds.has(i.providerId));
33
+ const incident = matchedIncidents.sort((a, b) => severityOrdinal(b.severity) - severityOrdinal(a.severity))[0];
34
+ const cutoffRisk = incident?.severity === "block" ? 1.0 : incident?.severity === "warn" ? 0.5 : 0.0;
35
+ let retentionDecision = "allow";
36
+ if (policy.mode === "strict" && !localAvailable) {
37
+ retentionDecision = "block";
38
+ }
39
+ else if (!policy.allowExportRestrictedProvider && incident?.kind === "export-control") {
40
+ retentionDecision = "block";
41
+ }
42
+ let jurisdictionDecision = "allow";
43
+ if (incident?.kind === "jurisdiction" || incident?.kind === "export-control") {
44
+ jurisdictionDecision = incident.severity === "block" ? "block" : "downgrade";
45
+ }
46
+ const reasonParts = [`mode=${policy.mode}`];
47
+ if (incident)
48
+ reasonParts.push(`incident=${incident.kind}:${incident.severity}`);
49
+ if (!localAvailable)
50
+ reasonParts.push("no-local-fallback");
51
+ return {
52
+ mode: policy.mode === "off" ? "standard" : "freedomd",
53
+ dataBoundary: "internal",
54
+ retentionDecision,
55
+ jurisdictionDecision,
56
+ providerCutoffRisk: cutoffRisk,
57
+ localFallbackAvailable: localAvailable,
58
+ reason: reasonParts.join("; "),
59
+ };
60
+ }
61
+ function classifyFreedomdIntent(task, capsule) {
62
+ const text = `${task.context.nodeId} ${task.context.goal ?? ""} ${task.prompt} ${task.context.role ?? ""} ${capsule?.node?.role ?? ""}`.toLowerCase();
63
+ const role = (task.context.role ?? capsule?.node?.role ?? "").toLowerCase();
64
+ if (/debug|fix|error|failure|bug|trace/.test(text) || role === "debugger")
65
+ return "debugging";
66
+ if (/review|audit|check|validate|verify/.test(text) || role === "reviewer")
67
+ return "review";
68
+ if (/test|spec|coverage|assertion/.test(text) || role === "tester")
69
+ return "test-generation";
70
+ if (/refactor|optimize|clean|improve|simplify/.test(text) || role === "refactor")
71
+ return "refactor";
72
+ if (/research|investigate|explore|search|discover|analyze/.test(text) || role === "researcher")
73
+ return "research";
74
+ if (/plan|design|architect|strategy|roadmap/.test(text) || role === "planner")
75
+ return "planning";
76
+ if (/doc|readme|changelog|comment/.test(text) || role === "documenter")
77
+ return "documentation";
78
+ if (/shell|command|run|exec|script/.test(text) || role === "shell")
79
+ return "shell-operation";
80
+ return "coding";
81
+ }
82
+ function runtimePriorityScore(runtime) {
83
+ return Math.max(0, Math.min(1, runtime.priority / 100));
84
+ }
85
+ export function scoreRuntimesWithSovereignty(task, runtimes, policy, options = {}) {
86
+ const profiles = options.profiles ?? buildProviderSovereigntyProfiles();
87
+ const incidents = options.incidents ?? [];
88
+ const intent = classifyFreedomdIntent(task, options.capsule);
89
+ const scored = [];
90
+ for (const runtime of runtimes) {
91
+ const profile = lookupSovereigntyProfile(runtime, profiles);
92
+ if (!profile)
93
+ continue;
94
+ const sovereignty = computeProviderSovereigntyScore(runtime, task, {
95
+ profile,
96
+ userCountry: options.userCountry,
97
+ taskRisk: task.safety?.risk,
98
+ incidents,
99
+ localAvailable: runtimeProviderId(runtime) === "local-llm" || runtime.id.startsWith("local-"),
100
+ });
101
+ if (policy.mode !== "off") {
102
+ if (sovereignty.diagnostics.jurisdictionRisk > policy.maxJurisdictionRisk)
103
+ continue;
104
+ if (sovereignty.diagnostics.retentionRisk > policy.maxRetentionRisk)
105
+ continue;
106
+ if (sovereignty.diagnostics.cutoffRisk > policy.maxCutoffRisk)
107
+ continue;
108
+ }
109
+ const retention = evaluateDataRetentionGate({
110
+ task,
111
+ runtime,
112
+ providerProfile: profile,
113
+ orgMaxRetentionDays: policy.maxRetentionDays,
114
+ allowRedaction: options.allowRedaction ?? false,
115
+ });
116
+ if (retention.decision === "block")
117
+ continue;
118
+ const capabilityScore = computeRuntimeCapabilityScore(runtime, intent);
119
+ const priorityScore = runtimePriorityScore(runtime);
120
+ let capabilityWeight;
121
+ let priorityWeight;
122
+ let sovereigntyWeight;
123
+ if (policy.mode === "strict") {
124
+ capabilityWeight = 0.3;
125
+ priorityWeight = 0.1;
126
+ sovereigntyWeight = 0.6;
127
+ const isLocal = runtimeProviderId(runtime) === "local-llm" || runtime.id.startsWith("local-");
128
+ if (isLocal) {
129
+ sovereigntyWeight = 0.5;
130
+ capabilityWeight = 0.4;
131
+ }
132
+ }
133
+ else if (policy.mode === "balanced") {
134
+ capabilityWeight = 0.5;
135
+ priorityWeight = 0.2;
136
+ sovereigntyWeight = 0.3;
137
+ }
138
+ else {
139
+ capabilityWeight = 0.7;
140
+ priorityWeight = 0.3;
141
+ sovereigntyWeight = 0.0;
142
+ }
143
+ const score = capabilityWeight * capabilityScore +
144
+ priorityWeight * priorityScore +
145
+ sovereigntyWeight * sovereignty.score;
146
+ scored.push({ runtime, score, sovereignty, retention });
147
+ }
148
+ return scored.sort((a, b) => b.score - a.score);
149
+ }
150
+ export function buildFreedomdDegradedPlan(task, reason, runtimes) {
151
+ const localAuthority = runtimes.find((r) => {
152
+ const pid = runtimeProviderId(r);
153
+ return (pid === "local-llm" || r.id.startsWith("local-")) && (r.capabilities?.write || r.capabilities?.shell);
154
+ });
155
+ if (localAuthority) {
156
+ return {
157
+ mode: "local-authority",
158
+ runtime: localAuthority,
159
+ allowedCapabilities: ["read", "write", "patch", "shell"],
160
+ reason,
161
+ };
162
+ }
163
+ const localRead = runtimes.find((r) => runtimeProviderId(r) === "local-llm" || r.id.startsWith("local-"));
164
+ if (localRead) {
165
+ return {
166
+ mode: "read-only-local-review",
167
+ runtime: localRead,
168
+ allowedCapabilities: ["read", "review"],
169
+ reason,
170
+ blockedOriginalAuthority: Object.entries(task.capabilities)
171
+ .filter(([, v]) => v === true)
172
+ .map(([k]) => k),
173
+ };
174
+ }
175
+ return {
176
+ mode: "blocked",
177
+ allowedCapabilities: [],
178
+ reason,
179
+ };
180
+ }
181
+ function agentResultToRunResult(result, runtimeId) {
182
+ return {
183
+ success: result.exitCode === 0,
184
+ exitCode: result.exitCode,
185
+ stdout: result.output,
186
+ stderr: "",
187
+ metadata: { ...result.metadata, runtime: runtimeId },
188
+ tokenUsage: result.tokenUsage,
189
+ toolCalls: result.toolCalls,
190
+ };
191
+ }
192
+ export async function createFreedomdRuntimeRouter(options = {}) {
193
+ const policy = compileFreedomdPolicy({
194
+ taskFlags: options.freedomdMode ? { mode: options.freedomdMode } : undefined,
195
+ });
196
+ const profiles = options.sovereigntyProfiles ?? buildProviderSovereigntyProfiles();
197
+ const loadedIncidents = options.autoLoadIncidents !== false ? await loadProviderIncidents({ projectRoot: options.projectRoot, feedUrls: policy.incidentFeedUrls }) : [];
198
+ const incidents = options.incidents ?? loadedIncidents;
199
+ const graphStore = options.graphStore;
200
+ const projectRoot = options.projectRoot ?? process.cwd();
201
+ const baseOptions = {
202
+ runtimes: options.runtimes,
203
+ fallbackChain: options.fallbackChain,
204
+ memoryPath: options.memoryPath,
205
+ };
206
+ const baseRouter = createRuntimeRouter(baseOptions);
207
+ async function materialize(runId, nodeId, route, envelope) {
208
+ if (!graphStore)
209
+ return;
210
+ try {
211
+ await graphStore.materializeFreedomdSovereignty({
212
+ runId,
213
+ nodeId,
214
+ providerId: route.selectedRuntime ? runtimeProviderId(route.selectedRuntime) : "none",
215
+ runtimeMode: route.selectedRuntime ? runtimeModeOf(route.selectedRuntime) : "none",
216
+ sovereignty: route.sovereignty,
217
+ degradedMode: route.degradedPlan?.mode,
218
+ incident: route.sovereignty.providerCutoffRisk >= 0.5
219
+ ? { kind: "cutoff", severity: route.sovereignty.providerCutoffRisk >= 1 ? "block" : "warn", reason: route.sovereignty.reason }
220
+ : undefined,
221
+ });
222
+ if (envelope) {
223
+ await graphStore.materializeFreedomdEvidenceEnvelope({
224
+ runId,
225
+ nodeId,
226
+ envelopePath: `.omk/runs/${runId}/freedomd/${nodeId}-evidence-envelope.json`,
227
+ });
228
+ }
229
+ }
230
+ catch {
231
+ // Graph materialization is best-effort; do not block turn execution.
232
+ }
233
+ }
234
+ async function freedomdRoute(task, capsule) {
235
+ const sovereignty = evaluateTaskSovereignty(task, policy, options.runtimes ?? [], incidents);
236
+ if (sovereignty.retentionDecision === "block" || sovereignty.jurisdictionDecision === "block") {
237
+ const degraded = buildFreedomdDegradedPlan(task, sovereignty.reason, options.runtimes ?? []);
238
+ return {
239
+ fallbackChain: [],
240
+ degradedPlan: degraded,
241
+ sovereignty,
242
+ diagnostics: `blocked; degraded=${degraded.mode}`,
243
+ };
244
+ }
245
+ const scored = scoreRuntimesWithSovereignty(task, options.runtimes ?? [], policy, {
246
+ profiles,
247
+ incidents,
248
+ userCountry: options.userCountry,
249
+ allowRedaction: options.allowRedaction,
250
+ capsule,
251
+ });
252
+ if (scored.length === 0) {
253
+ const degraded = buildFreedomdDegradedPlan(task, "NO_FREEDOMD_COMPATIBLE_RUNTIME", options.runtimes ?? []);
254
+ if (policy.allowProviderExceptions && options.isTty !== false) {
255
+ const blocked = options.runtimes?.[0];
256
+ if (blocked) {
257
+ const profile = lookupSovereigntyProfile(blocked, profiles);
258
+ const exception = await requestProviderException({
259
+ providerId: runtimeProviderId(blocked),
260
+ runtimeMode: runtimeModeOf(blocked),
261
+ blockedReason: "NO_FREEDOMD_COMPATIBLE_RUNTIME",
262
+ retentionDays: profile?.retentionDays ?? 0,
263
+ jurisdictionRisk: profile ? computeProviderSovereigntyScore(blocked, task, { profile, incidents }).diagnostics.jurisdictionRisk : 0,
264
+ localFallbackAvailable: hasLocalRuntime(options.runtimes ?? []),
265
+ taskSensitivity: task.safety?.risk ?? "unknown",
266
+ }, {
267
+ runId: task.context.runId,
268
+ nodeId: task.context.nodeId,
269
+ projectRoot,
270
+ graphStore,
271
+ isTty: options.isTty,
272
+ promptUser: options.promptUser,
273
+ });
274
+ if (exception.decision === "once" || exception.decision === "run") {
275
+ return {
276
+ selectedRuntime: blocked,
277
+ fallbackChain: [],
278
+ sovereignty,
279
+ diagnostics: `provider exception ${exception.decision} for ${blocked.id}`,
280
+ providerException: exception,
281
+ };
282
+ }
283
+ }
284
+ }
285
+ return {
286
+ fallbackChain: [],
287
+ degradedPlan: degraded,
288
+ sovereignty,
289
+ diagnostics: "no compatible runtime; degraded",
290
+ };
291
+ }
292
+ const selected = scored[0];
293
+ const fallbackChain = scored.slice(1).map((s) => s.runtime);
294
+ if (selected.retention.decision === "downgrade") {
295
+ const degraded = buildFreedomdDegradedPlan(task, selected.retention.reason, options.runtimes ?? []);
296
+ return {
297
+ selectedRuntime: selected.runtime,
298
+ fallbackChain,
299
+ degradedPlan: degraded,
300
+ sovereignty,
301
+ diagnostics: `retention downgrade; selected=${selected.runtime.id}; fallback=${fallbackChain.map((r) => r.id).join(",")}`,
302
+ };
303
+ }
304
+ return {
305
+ selectedRuntime: selected.runtime,
306
+ fallbackChain,
307
+ sovereignty,
308
+ diagnostics: `selected=${selected.runtime.id}; sovereignty=${selected.sovereignty.score.toFixed(2)}; fallback=${fallbackChain.map((r) => r.id).join(",")}`,
309
+ };
310
+ }
311
+ async function executeTask(task, capsule, signal) {
312
+ const route = await freedomdRoute(task, capsule);
313
+ return runFreedomdRoute(task, capsule, route, signal);
314
+ }
315
+ async function runFreedomdRoute(task, capsule, route, signal) {
316
+ const runId = task.context.runId;
317
+ const nodeId = task.context.nodeId;
318
+ if (route.degradedPlan && !route.selectedRuntime) {
319
+ const result = {
320
+ success: false,
321
+ exitCode: 78,
322
+ stdout: "",
323
+ stderr: `Freedomd degraded mode: ${route.degradedPlan.mode}. ${route.degradedPlan.reason}`,
324
+ metadata: {
325
+ freedomd: true,
326
+ degradedMode: route.degradedPlan.mode,
327
+ sovereignty: route.sovereignty,
328
+ remediation: [
329
+ "Enable local-llm runtime",
330
+ "Configure an authority-capable CLI runtime",
331
+ "Lower task authority to read/review",
332
+ "Provide user-approved provider exception",
333
+ ],
334
+ },
335
+ };
336
+ await materialize(runId, nodeId, route);
337
+ return result;
338
+ }
339
+ const primary = route.selectedRuntime;
340
+ const allCandidates = [primary, ...route.fallbackChain];
341
+ let lastError;
342
+ for (const runtime of allCandidates) {
343
+ if (signal.aborted) {
344
+ return {
345
+ success: false,
346
+ exitCode: 130,
347
+ stdout: "",
348
+ stderr: "Aborted before execution",
349
+ metadata: { runtime: runtime.id, aborted: true, sovereignty: route.sovereignty },
350
+ };
351
+ }
352
+ try {
353
+ const rawResult = runtime.execute
354
+ ? await runtime.execute(task)
355
+ : await runtime.runNode(capsule, signal);
356
+ const result = "stdout" in rawResult ? rawResult : agentResultToRunResult(rawResult, runtime.id);
357
+ const safeResult = {
358
+ success: result.success,
359
+ exitCode: result.exitCode,
360
+ stdout: result.stdout,
361
+ stderr: maskSensitiveText(result.stderr),
362
+ metadata: {
363
+ ...result.metadata,
364
+ selectedRuntime: runtime.id,
365
+ sovereignty: route.sovereignty,
366
+ fallbackChain: allCandidates.map((r) => r.id),
367
+ freedomd: true,
368
+ },
369
+ tokenUsage: result.tokenUsage,
370
+ toolCalls: result.toolCalls,
371
+ };
372
+ const envelope = await buildLocalFirstEvidenceEnvelope({
373
+ task,
374
+ selectedRuntime: runtime,
375
+ runContext: { runId, nodeId, projectRoot },
376
+ providerResponse: safeResult,
377
+ sovereignty: route.sovereignty,
378
+ });
379
+ await materialize(runId, nodeId, route, envelope);
380
+ if (safeResult.success) {
381
+ return {
382
+ ...safeResult,
383
+ metadata: {
384
+ ...safeResult.metadata,
385
+ freedomdEvidenceEnvelope: envelope.localArtifacts[envelope.localArtifacts.length - 1]?.path,
386
+ },
387
+ };
388
+ }
389
+ lastError = safeResult;
390
+ }
391
+ catch (err) {
392
+ const error = maskSensitiveText(String(err));
393
+ lastError = {
394
+ success: false,
395
+ exitCode: 1,
396
+ stdout: "",
397
+ stderr: error,
398
+ metadata: { runtime: runtime.id, error, sovereignty: route.sovereignty },
399
+ };
400
+ }
401
+ }
402
+ const final = lastError ?? {
403
+ success: false,
404
+ exitCode: 1,
405
+ stdout: "",
406
+ stderr: "No Freedomd runtime available",
407
+ metadata: { attempted: allCandidates.map((r) => r.id), sovereignty: route.sovereignty },
408
+ };
409
+ const envelope = await buildLocalFirstEvidenceEnvelope({
410
+ task,
411
+ selectedRuntime: primary,
412
+ runContext: { runId, nodeId, projectRoot },
413
+ providerResponse: final,
414
+ sovereignty: route.sovereignty,
415
+ });
416
+ await materialize(runId, nodeId, route, envelope);
417
+ return final;
418
+ }
419
+ return {
420
+ policy,
421
+ // baseRouter intentionally omitted from public return type to avoid leaking
422
+ // internal RuntimeRouter history types; use executeTask for routing.
423
+ baseRouter: baseRouter,
424
+ freedomdRoute,
425
+ scoreRuntimesWithSovereignty: (task) => scoreRuntimesWithSovereignty(task, options.runtimes ?? [], policy, {
426
+ profiles,
427
+ incidents,
428
+ userCountry: options.userCountry,
429
+ allowRedaction: options.allowRedaction,
430
+ }),
431
+ executeTask,
432
+ };
433
+ }
@@ -0,0 +1,21 @@
1
+ import { type HeadroomDecisionHistoryEntry, type HeadroomLoopRiskSignal } from "./headroom-loop-risk.js";
2
+ export type HeadroomLoopAction = "continue" | "block" | "replan" | "context-adjustment";
3
+ export interface HeadroomAwareLoopDecision {
4
+ readonly action: HeadroomLoopAction;
5
+ readonly reason: string;
6
+ readonly confidence: number;
7
+ readonly contextAdjustment?: {
8
+ readonly dropLowPriorityGraphMemory: boolean;
9
+ readonly reduceFileSlices: boolean;
10
+ readonly forceStructuredFallback: boolean;
11
+ };
12
+ readonly risk?: {
13
+ readonly headroom: HeadroomLoopRiskSignal;
14
+ };
15
+ }
16
+ export declare function evaluateHeadroomAwareLoopDecision(input: {
17
+ readonly baseAction: HeadroomLoopAction;
18
+ readonly baseReason: string;
19
+ readonly baseConfidence: number;
20
+ readonly headroomHistory: readonly HeadroomDecisionHistoryEntry[];
21
+ }): HeadroomAwareLoopDecision;
@@ -0,0 +1,67 @@
1
+ import { computeHeadroomLoopRiskSignal } from "./headroom-loop-risk.js";
2
+ function isStrictGuardrailMode() {
3
+ const raw = process.env.OMK_STRICT_GUARDRAIL ?? "";
4
+ const normalized = raw.trim().toLowerCase();
5
+ return normalized === "1" || normalized === "true" || normalized === "on";
6
+ }
7
+ export function evaluateHeadroomAwareLoopDecision(input) {
8
+ const headroomRisk = computeHeadroomLoopRiskSignal(input.headroomHistory);
9
+ if (headroomRisk.kind === "none") {
10
+ return {
11
+ action: input.baseAction,
12
+ reason: input.baseReason,
13
+ confidence: input.baseConfidence,
14
+ risk: { headroom: headroomRisk },
15
+ };
16
+ }
17
+ if (!isStrictGuardrailMode()) {
18
+ return {
19
+ action: "continue",
20
+ reason: `${input.baseReason}; headroom risk noted under agent-freedom mode: ${headroomRisk.reason}`,
21
+ confidence: Math.min(input.baseConfidence, headroomRisk.severity),
22
+ contextAdjustment: headroomRisk.recommendedAction === "summarize-or-drop-low-priority-context"
23
+ ? {
24
+ dropLowPriorityGraphMemory: true,
25
+ reduceFileSlices: true,
26
+ forceStructuredFallback: true,
27
+ }
28
+ : undefined,
29
+ risk: { headroom: headroomRisk },
30
+ };
31
+ }
32
+ if (headroomRisk.recommendedAction === "block") {
33
+ return {
34
+ action: "block",
35
+ reason: headroomRisk.reason,
36
+ confidence: Math.max(input.baseConfidence, headroomRisk.severity),
37
+ risk: { headroom: headroomRisk },
38
+ };
39
+ }
40
+ if (headroomRisk.recommendedAction === "replan") {
41
+ return {
42
+ action: input.baseAction === "continue" || input.baseAction === "context-adjustment" ? "replan" : input.baseAction,
43
+ reason: `${input.baseReason}; ${headroomRisk.reason}`,
44
+ confidence: Math.max(input.baseConfidence, headroomRisk.severity),
45
+ risk: { headroom: headroomRisk },
46
+ };
47
+ }
48
+ if (headroomRisk.recommendedAction === "summarize-or-drop-low-priority-context") {
49
+ return {
50
+ action: "continue",
51
+ reason: `${input.baseReason}; ${headroomRisk.reason}`,
52
+ confidence: Math.max(input.baseConfidence, headroomRisk.severity),
53
+ contextAdjustment: {
54
+ dropLowPriorityGraphMemory: true,
55
+ reduceFileSlices: true,
56
+ forceStructuredFallback: true,
57
+ },
58
+ risk: { headroom: headroomRisk },
59
+ };
60
+ }
61
+ return {
62
+ action: input.baseAction,
63
+ reason: input.baseReason,
64
+ confidence: input.baseConfidence,
65
+ risk: { headroom: headroomRisk },
66
+ };
67
+ }
@@ -0,0 +1,19 @@
1
+ export type HeadroomLoopRiskKind = "none" | "compaction-contract-drift" | "headroom-no-apply" | "low-compaction-yield";
2
+ export type HeadroomLoopRecommendedAction = "continue" | "block" | "replan" | "summarize-or-drop-low-priority-context";
3
+ export interface HeadroomDecisionHistoryEntry {
4
+ readonly attempted?: boolean;
5
+ readonly applied?: boolean;
6
+ readonly validated?: boolean;
7
+ readonly compactedTextProduced?: boolean;
8
+ readonly beforeTokens?: number | null;
9
+ readonly afterTokens?: number | null;
10
+ readonly reason?: string;
11
+ readonly missingSections?: readonly string[];
12
+ }
13
+ export interface HeadroomLoopRiskSignal {
14
+ readonly kind: HeadroomLoopRiskKind;
15
+ readonly severity: number;
16
+ readonly recommendedAction: HeadroomLoopRecommendedAction;
17
+ readonly reason: string;
18
+ }
19
+ export declare function computeHeadroomLoopRiskSignal(history: readonly HeadroomDecisionHistoryEntry[]): HeadroomLoopRiskSignal;
@@ -0,0 +1,43 @@
1
+ export function computeHeadroomLoopRiskSignal(history) {
2
+ const recent = history.slice(-3);
3
+ if (recent.length < 2) {
4
+ return noHeadroomLoopRisk("not enough headroom decision history");
5
+ }
6
+ const failedApplyCount = recent.filter((entry) => entry.attempted === true && entry.applied !== true).length;
7
+ const validationFailureCount = recent.filter((entry) => entry.compactedTextProduced === true && entry.validated !== true).length;
8
+ const lowYieldCount = recent.filter((entry) => {
9
+ if (entry.applied !== true)
10
+ return false;
11
+ const before = Math.max(1, entry.beforeTokens ?? 0);
12
+ const after = Math.max(0, entry.afterTokens ?? 0);
13
+ return after / before > 0.85;
14
+ }).length;
15
+ if (validationFailureCount >= 2) {
16
+ return {
17
+ kind: "compaction-contract-drift",
18
+ severity: 0.9,
19
+ recommendedAction: "block",
20
+ reason: "structured compaction repeatedly stripped or mismatched required contract sections",
21
+ };
22
+ }
23
+ if (failedApplyCount >= 3) {
24
+ return {
25
+ kind: "headroom-no-apply",
26
+ severity: 0.8,
27
+ recommendedAction: "replan",
28
+ reason: "headroom crossed the threshold repeatedly but no compacted capsule was applied",
29
+ };
30
+ }
31
+ if (lowYieldCount >= 2) {
32
+ return {
33
+ kind: "low-compaction-yield",
34
+ severity: 0.6,
35
+ recommendedAction: "summarize-or-drop-low-priority-context",
36
+ reason: "compaction applied repeatedly but did not reduce context enough",
37
+ };
38
+ }
39
+ return noHeadroomLoopRisk("headroom decisions show no repeated compaction risk");
40
+ }
41
+ function noHeadroomLoopRisk(reason) {
42
+ return { kind: "none", severity: 0, recommendedAction: "continue", reason };
43
+ }