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
@@ -2,6 +2,9 @@ import { execSync } from "child_process";
2
2
  export function createChatAdvisoryRuntime() {
3
3
  return {
4
4
  id: "omk-advisory",
5
+ providerId: "omk",
6
+ runtimeMode: "local",
7
+ kind: "local",
5
8
  priority: 0,
6
9
  supports: () => true,
7
10
  async runNode(_capsule, _signal) {
@@ -6,6 +6,8 @@ export function createCodexCliRuntime(options) {
6
6
  const runner = createCodexCliAdvisoryTaskRunner(options);
7
7
  return {
8
8
  id: "codex-cli",
9
+ providerId: "codex",
10
+ runtimeMode: "cli",
9
11
  displayName: "Codex CLI",
10
12
  kind: "cli",
11
13
  priority: 60,
@@ -4,6 +4,7 @@
4
4
  * Spawns the `codex` CLI subprocess and bridges AgentTask / AgentResult.
5
5
  */
6
6
  import type { AgentRuntime, AgentRunResult, AgentResult, AgentTask, RuntimeCapabilities, RuntimeHealth } from "./agent-runtime.js";
7
+ import type { RuntimeHealthProbeRequest } from "./contracts/shared.js";
7
8
  import type { ContextCapsule } from "./context-capsule.js";
8
9
  export interface CodexRuntimeOptions {
9
10
  bin?: string;
@@ -16,6 +17,8 @@ export interface CodexRuntimeOptions {
16
17
  }
17
18
  export declare class CodexRuntime implements AgentRuntime {
18
19
  readonly id = "codex-cli";
20
+ readonly providerId = "codex";
21
+ readonly runtimeMode = "cli";
19
22
  readonly kind = "cli";
20
23
  readonly priority = 60;
21
24
  readonly capabilities: RuntimeCapabilities;
@@ -28,7 +31,7 @@ export declare class CodexRuntime implements AgentRuntime {
28
31
  private readonly safetyMarginTokens;
29
32
  constructor(options?: CodexRuntimeOptions);
30
33
  supports(capsule: ContextCapsule): boolean;
31
- health(): Promise<RuntimeHealth>;
34
+ health(input?: RuntimeHealthProbeRequest): Promise<RuntimeHealth>;
32
35
  runNode(capsule: ContextCapsule, signal: AbortSignal): Promise<AgentRunResult>;
33
36
  execute(task: AgentTask): Promise<AgentResult>;
34
37
  private buildPrompt;
@@ -9,8 +9,11 @@ import { sanitizeUserVisibleOutput } from "../util/user-visible-output.js";
9
9
  import { buildChildEnv } from "./child-env.js";
10
10
  import { contextPreflightErrorMessage, preflightProviderInput, } from "../providers/context-preflight.js";
11
11
  import { createRuntimeSandboxProfile } from "./sandbox-profile.js";
12
+ import { staticRuntimeHealth } from "./runtime-health-probes.js";
12
13
  export class CodexRuntime {
13
14
  id = "codex-cli";
15
+ providerId = "codex";
16
+ runtimeMode = "cli";
14
17
  kind = "cli";
15
18
  priority = 60;
16
19
  capabilities = {
@@ -68,14 +71,25 @@ export class CodexRuntime {
68
71
  }
69
72
  return true;
70
73
  }
71
- async health() {
72
- const available = await checkCommand(this.bin).catch(() => false);
73
- return {
74
+ async health(input = { probeKind: "static", highRisk: false }) {
75
+ const started = Date.now();
76
+ const runtimeOk = input.probeKind === "static"
77
+ ? await checkCommand(this.bin).catch(() => false)
78
+ : (await runShell(this.bin, ["--version"], { cwd: this.cwd, timeout: 5000 }).catch(() => ({ exitCode: 1 }))).exitCode === 0;
79
+ const available = runtimeOk;
80
+ return staticRuntimeHealth({
74
81
  runtimeId: this.id,
75
82
  available,
76
- reason: available ? undefined : "codex CLI is not available on PATH",
77
- checkedAt: new Date().toISOString(),
78
- };
83
+ reason: available ? undefined : "codex CLI is not available or failed --version probe",
84
+ runtimeOk,
85
+ authOk: runtimeOk,
86
+ modelOk: true,
87
+ quotaOk: true,
88
+ rateLimitOk: true,
89
+ latencyMs: input.probeKind === "static" ? undefined : Date.now() - started,
90
+ probeKind: input.probeKind,
91
+ ttlMs: input.probeKind === "static" ? 60_000 : 30_000,
92
+ });
79
93
  }
80
94
  async runNode(capsule, signal) {
81
95
  try {
@@ -146,12 +160,7 @@ export class CodexRuntime {
146
160
  ...(model ? { OMK_PROVIDER_MODEL: model } : {}),
147
161
  },
148
162
  });
149
- const sandboxMode = task.context.sandboxMode === "read-only" || task.context.sandboxMode === "workspace-write"
150
- ? task.context.sandboxMode
151
- :
152
- task.capabilities.write || task.capabilities.patch || task.capabilities.shell
153
- ? "workspace-write"
154
- : "read-only";
163
+ const sandboxMode = resolveCodexSandboxMode(task);
155
164
  const approvalPolicy = codexApprovalPolicy(task.context.approvalPolicy ?? task.context.env?.OMK_APPROVAL_POLICY, sandboxMode);
156
165
  const sandboxProfile = createRuntimeSandboxProfile({
157
166
  cwd: this.cwd,
@@ -254,10 +263,23 @@ export class CodexRuntime {
254
263
  return parts.join("\n");
255
264
  }
256
265
  }
266
+ function resolveCodexSandboxMode(task) {
267
+ if (task.context.sandboxMode === "read-only" || task.context.sandboxMode === "workspace-write") {
268
+ return task.context.sandboxMode;
269
+ }
270
+ // Advisory API runtimes must stay read-only even if capabilities request write.
271
+ if (task.context.env?.OMK_PROVIDER_AUTHORITY === "advisory")
272
+ return "read-only";
273
+ if (task.capabilities.write || task.capabilities.patch || task.capabilities.shell) {
274
+ return "workspace-write";
275
+ }
276
+ return "read-only";
277
+ }
257
278
  function codexApprovalPolicy(value, sandboxMode) {
258
279
  if (sandboxMode !== "read-only")
259
280
  return "on-request";
260
281
  const normalized = value?.trim().toLowerCase();
282
+ // OMK "ask" must never map to provider "never"; only explicit "never"/"yolo" does.
261
283
  if (normalized === "never" || normalized === "yolo")
262
284
  return "never";
263
285
  return "on-request";
@@ -57,18 +57,46 @@ export async function capsuleToTask(capsule, options = {}) {
57
57
  maxLatencyMs: undefined,
58
58
  };
59
59
  const capabilities = capabilitiesFromNode(capsule);
60
+ const safety = safetyFromCapsule(capsule, capabilities);
61
+ const sovereignty = routing?.freedomd
62
+ ? {
63
+ mode: (routing.freedomd.degradedMode ?? routing.freedomd.dataBoundary) ? "freedomd" : "standard",
64
+ dataBoundary: routing.freedomd.dataBoundary ?? "internal",
65
+ retentionDecision: "allow",
66
+ jurisdictionDecision: "allow",
67
+ providerCutoffRisk: 0,
68
+ localFallbackAvailable: false,
69
+ reason: routing.freedomd.sovereigntyReason ?? "from dag routing",
70
+ }
71
+ : undefined;
60
72
  const task = {
61
73
  prompt: capsule.task,
62
74
  context,
63
75
  tools,
64
76
  providerPolicy,
65
77
  capabilities,
78
+ safety,
79
+ sovereignty,
66
80
  };
67
81
  return task;
68
82
  }
69
83
  function isAbortSignal(value) {
70
84
  return "aborted" in value && typeof value.addEventListener === "function";
71
85
  }
86
+ function safetyFromCapsule(capsule, capabilities) {
87
+ const routing = capsule.node.routing;
88
+ const risk = routing?.risk ?? (capabilities.shell || capabilities.merge ? "shell" : capabilities.write || capabilities.patch ? "write" : "read");
89
+ const authorityMode = routing?.assignedProviderAuthority
90
+ ?? (routing?.readOnly === true ? "advisory" : "authority");
91
+ return {
92
+ risk,
93
+ riskTrace: routing?.riskTrace,
94
+ approvalPolicy: routing?.approvalPolicy ?? routing?.executionPrompt ?? "interactive",
95
+ sandboxMode: routing?.sandboxMode ?? (routing?.readOnly === true ? "read-only" : "workspace-write"),
96
+ evidenceRequired: routing?.evidenceRequired === true || capabilities.write || capabilities.patch || capabilities.shell || capabilities.merge,
97
+ authorityMode,
98
+ };
99
+ }
72
100
  function capabilitiesFromNode(capsule) {
73
101
  const node = capsule.node;
74
102
  const routing = node.routing;
@@ -77,7 +105,9 @@ function capabilitiesFromNode(capsule) {
77
105
  const assigned = new Set(routing?.assignedProviderCapabilities ?? []);
78
106
  const merge = assigned.has("merge") || role === "merger" || role === "integrator" || role === "orchestrator";
79
107
  const write = assigned.has("write") || merge || role === "coder" || role === "executor" || role === "refactorer";
80
- const shell = assigned.has("shell") || routing?.requiresToolCalling === true || gates.includes("command-pass") || gates.includes("test-pass");
108
+ const shell = routing?.readOnly === true
109
+ ? false
110
+ : assigned.has("shell") || routing?.requiresToolCalling === true || gates.includes("command-pass") || gates.includes("test-pass");
81
111
  const review = assigned.has("review") || role === "reviewer" || role === "qa" || role === "tester" || gates.includes("review-pass");
82
112
  const mcp = assigned.has("mcp") || routing?.requiresMcp === true;
83
113
  const vision = assigned.has("vision");
@@ -88,7 +118,7 @@ function capabilitiesFromNode(capsule) {
88
118
  mcp,
89
119
  patch: routing?.readOnly === true ? false : write,
90
120
  review,
91
- merge,
121
+ merge: routing?.readOnly === true ? false : merge,
92
122
  vision,
93
123
  toolCalling: routing?.requiresToolCalling === true || assigned.has("toolCalling"),
94
124
  maxTokens: capsule.budget.maxInputTokens,
@@ -5,11 +5,27 @@ import { mkdir, readFile, stat, writeFile } from "fs/promises";
5
5
  import { createContextBudgetOptimizer } from "./context-budget-optimizer.js";
6
6
  import { createDecisionTraceStore } from "../evidence/decision-trace.js";
7
7
  import { evaluateHeadroom } from "./headroom-policy.js";
8
+ import { getRunArtifactPath } from "../util/run-store.js";
8
9
  const DEFAULT_CONTEXT_WINDOW = 200_000;
9
10
  function resolveBudget(node) {
10
11
  const preset = node.routing?.contextBudget ?? "small";
11
12
  return CONTEXT_BUDGET_PRESETS[preset] ?? DEFAULT_CONTEXT_BUDGET;
12
13
  }
14
+ async function loadPrivatePromptPayload(node, runId, projectRoot) {
15
+ const ref = node.routing?.promptPayloadRef;
16
+ if (!ref || !runId)
17
+ return undefined;
18
+ try {
19
+ const raw = await readFile(getRunArtifactPath(runId, ref, projectRoot), "utf-8");
20
+ const parsed = JSON.parse(raw);
21
+ if (typeof parsed.compiledPrompt !== "string" || parsed.compiledPrompt.trim().length === 0)
22
+ return undefined;
23
+ return parsed.compiledPrompt;
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ }
13
29
  function collectDependencySummaries(node, state) {
14
30
  if (!state || !node.dependsOn.length)
15
31
  return [];
@@ -238,9 +254,11 @@ export function createContextBroker(options = {}) {
238
254
  nodeId: node.id,
239
255
  graphMemory,
240
256
  }).catch(() => { });
257
+ const privatePrompt = await loadPrivatePromptPayload(node, state?.runId, projectRoot);
241
258
  const task = [
242
259
  `Execute DAG node: ${node.id}`,
243
260
  `Name: ${node.name}`,
261
+ privatePrompt ? `PrivatePrompt: ${privatePrompt}` : undefined,
244
262
  `Role: ${node.role}`,
245
263
  node.routing?.actionAtom ? `ActionAtom: ${node.routing.actionAtom.id} | ${node.routing.actionAtom.label} | ${node.routing.actionAtom.verb} ${node.routing.actionAtom.object ?? "assigned scope"} | evidence=${node.routing.actionAtom.evidenceTarget} | done=${node.routing.actionAtom.doneCondition}` : undefined,
246
264
  node.routing?.skills?.length ? `Skills: ${node.routing.skills.join(", ")}` : undefined,
@@ -68,6 +68,58 @@ export interface AdapterTestResult {
68
68
  readonly score: number;
69
69
  readonly details?: string;
70
70
  }
71
+ /** Kinds of output gates that can satisfy evidence-required turns. */
72
+ export type EvidenceGateKind = "file-exists" | "test-pass" | "review-pass" | "command-pass" | "summary" | "artifact" | "diff";
73
+ /** A declared gate requirement. Declarations are not evidence. */
74
+ export interface EvidenceRequirement {
75
+ readonly gate: EvidenceGateKind;
76
+ readonly ref?: string;
77
+ readonly required: boolean;
78
+ }
79
+ /** A produced, replayable evidence observation. */
80
+ export interface EvidenceObservation {
81
+ readonly kind: EvidenceGateKind;
82
+ readonly source: "stdout" | "metadata" | "artifact" | "file";
83
+ readonly ref?: string;
84
+ readonly artifactPath?: string;
85
+ readonly timestamp: string;
86
+ readonly replayable: boolean;
87
+ readonly redacted: boolean;
88
+ /** Confidence that this observation is replayable, command-backed evidence. */
89
+ readonly confidence: number;
90
+ }
91
+ /** Result of checking whether a node/task produced required evidence. */
92
+ export interface EvidenceGateCheck {
93
+ readonly required: boolean;
94
+ readonly satisfied: boolean;
95
+ readonly gates: readonly EvidenceGateKind[];
96
+ readonly missing: readonly EvidenceGateKind[];
97
+ readonly reason: string;
98
+ readonly requirements?: readonly EvidenceRequirement[];
99
+ readonly observations?: readonly EvidenceObservation[];
100
+ }
101
+ export declare function isEvidenceGateKind(value: string | undefined): value is EvidenceGateKind;
102
+ export declare function evidenceRequirementsFromOutputs(outputs: readonly {
103
+ gate?: string;
104
+ ref?: string;
105
+ required?: boolean;
106
+ }[] | undefined): EvidenceRequirement[];
107
+ export declare function hasDeclaredEvidenceRequirement(outputs: readonly {
108
+ gate?: string;
109
+ ref?: string;
110
+ required?: boolean;
111
+ }[] | undefined): boolean;
112
+ export declare function evidenceObservationsFromResult(input: {
113
+ readonly metadata?: Record<string, unknown> | null;
114
+ readonly stdout?: string;
115
+ readonly artifactPaths?: readonly string[];
116
+ readonly timestamp?: string;
117
+ }): EvidenceObservation[];
118
+ export declare function checkEvidenceGate(required: boolean | undefined, outputs: readonly {
119
+ gate?: string;
120
+ ref?: string;
121
+ required?: boolean;
122
+ }[] | undefined, metadata?: Record<string, unknown> | null, stdout?: string, artifactPaths?: readonly string[]): EvidenceGateCheck;
71
123
  /** Per-gate check result. */
72
124
  export interface GateCheck {
73
125
  readonly gate: string;
@@ -4,4 +4,131 @@
4
4
  * Core interfaces that bridge reasoning traces, runtime decisions,
5
5
  * provider maturity, and release gates into a verifiable evidence model.
6
6
  */
7
- export {};
7
+ const EVIDENCE_GATE_KINDS = [
8
+ "file-exists",
9
+ "test-pass",
10
+ "review-pass",
11
+ "command-pass",
12
+ "summary",
13
+ "artifact",
14
+ "diff",
15
+ ];
16
+ export function isEvidenceGateKind(value) {
17
+ return Boolean(value && EVIDENCE_GATE_KINDS.includes(value.toLowerCase()));
18
+ }
19
+ export function evidenceRequirementsFromOutputs(outputs) {
20
+ const requirements = [];
21
+ for (const output of outputs ?? []) {
22
+ const kind = output.gate?.toLowerCase();
23
+ if (!isEvidenceGateKind(kind))
24
+ continue;
25
+ if (output.required === false)
26
+ continue;
27
+ requirements.push({ gate: kind, ref: output.ref, required: true });
28
+ }
29
+ return requirements;
30
+ }
31
+ export function hasDeclaredEvidenceRequirement(outputs) {
32
+ return evidenceRequirementsFromOutputs(outputs).length > 0;
33
+ }
34
+ export function evidenceObservationsFromResult(input) {
35
+ const timestamp = input.timestamp ?? new Date().toISOString();
36
+ const observations = [];
37
+ const metadata = input.metadata ?? undefined;
38
+ const metaGates = metadata?.evidenceGates;
39
+ if (Array.isArray(metaGates)) {
40
+ for (const raw of metaGates) {
41
+ const gate = typeof raw === "string" ? raw.toLowerCase() : undefined;
42
+ if (isEvidenceGateKind(gate)) {
43
+ observations.push({ kind: gate, source: "metadata", timestamp, replayable: true, redacted: true, confidence: 0.9 });
44
+ }
45
+ }
46
+ }
47
+ if (metadata?.commandPass === true || metadata?.testPass === true || metadata?.buildPass === true) {
48
+ observations.push({ kind: "command-pass", source: "metadata", timestamp, replayable: true, redacted: true, confidence: 0.9 });
49
+ }
50
+ if (metadata?.diff || metadata?.patch || metadata?.changedFiles) {
51
+ observations.push({ kind: "diff", source: "metadata", timestamp, replayable: true, redacted: true, confidence: 0.9 });
52
+ }
53
+ const artifactRef = metadata?.artifact ?? metadata?.artifactPath ?? metadata?.evidenceRef;
54
+ if (typeof artifactRef === "string" && artifactRef.trim().length > 0) {
55
+ observations.push({ kind: "artifact", source: "metadata", ref: artifactRef, artifactPath: artifactRef, timestamp, replayable: true, redacted: true, confidence: 0.95 });
56
+ }
57
+ for (const artifactPath of input.artifactPaths ?? []) {
58
+ observations.push({ kind: "artifact", source: "artifact", artifactPath, ref: artifactPath, timestamp, replayable: true, redacted: true, confidence: 0.95 });
59
+ }
60
+ const stdout = input.stdout ?? "";
61
+ if (stdout.trim().length > 0) {
62
+ observations.push({ kind: "summary", source: "stdout", timestamp, replayable: true, redacted: true, confidence: 0.4 });
63
+ }
64
+ if (/\b(pass(ed)?|success|ok)\b/i.test(stdout) && /\b(test|check|build|lint|command)\b/i.test(stdout)) {
65
+ observations.push({ kind: "command-pass", source: "stdout", timestamp, replayable: true, redacted: true, confidence: 0.4 });
66
+ }
67
+ return observations;
68
+ }
69
+ function minimumConfidenceFor(requirement) {
70
+ if (requirement.gate === "command-pass" || requirement.gate === "test-pass")
71
+ return 0.8;
72
+ return 0;
73
+ }
74
+ function observationSatisfies(requirement, observation) {
75
+ if (!observation.replayable || !observation.redacted)
76
+ return false;
77
+ if (observation.confidence < minimumConfidenceFor(requirement))
78
+ return false;
79
+ if (observation.kind === requirement.gate)
80
+ return true;
81
+ if (requirement.gate === "test-pass" && observation.kind === "command-pass")
82
+ return true;
83
+ if (requirement.gate === "file-exists" && observation.kind === "artifact")
84
+ return true;
85
+ return false;
86
+ }
87
+ export function checkEvidenceGate(required, outputs, metadata, stdout, artifactPaths) {
88
+ const requirements = evidenceRequirementsFromOutputs(outputs);
89
+ const observations = evidenceObservationsFromResult({ metadata, stdout, artifactPaths });
90
+ const observedKinds = new Set(observations.map((o) => o.kind));
91
+ if (!required) {
92
+ return { required: false, satisfied: true, gates: [...observedKinds], missing: [], reason: "evidence not required", requirements, observations };
93
+ }
94
+ if (requirements.length === 0 && observations.length > 0) {
95
+ const strongObservations = observations.filter((observation) => observation.confidence >= 0.8 && observation.replayable && observation.redacted);
96
+ if (strongObservations.length > 0) {
97
+ return {
98
+ required: true,
99
+ satisfied: true,
100
+ gates: [...new Set(strongObservations.map((observation) => observation.kind))],
101
+ missing: [],
102
+ reason: `evidence satisfied by high-confidence observations without explicit gate: ${[...new Set(strongObservations.map((observation) => observation.kind))].join(", ")}`,
103
+ requirements,
104
+ observations,
105
+ };
106
+ }
107
+ }
108
+ const effectiveRequirements = requirements.length > 0
109
+ ? requirements
110
+ : EVIDENCE_GATE_KINDS.map((gate) => ({ gate, required: true }));
111
+ const missing = effectiveRequirements
112
+ .filter((requirement) => !observations.some((observation) => observationSatisfies(requirement, observation)))
113
+ .map((requirement) => requirement.gate);
114
+ if (missing.length === 0) {
115
+ return {
116
+ required: true,
117
+ satisfied: true,
118
+ gates: [...observedKinds],
119
+ missing: [],
120
+ reason: `evidence satisfied by observations: ${[...observedKinds].join(", ") || "none"}`,
121
+ requirements: effectiveRequirements,
122
+ observations,
123
+ };
124
+ }
125
+ return {
126
+ required: true,
127
+ satisfied: false,
128
+ gates: [...observedKinds],
129
+ missing,
130
+ reason: `required evidence observations missing: ${[...new Set(missing)].join(", ")}`,
131
+ requirements: effectiveRequirements,
132
+ observations,
133
+ };
134
+ }
@@ -26,9 +26,36 @@ export interface RuntimeCapabilities {
26
26
  supportsStructuredOutput?: boolean;
27
27
  supportsToolCalling?: boolean;
28
28
  }
29
+ export type HealthState = "pass" | "fail" | "unknown";
30
+ export type RuntimeHealthProbeKind = "none" | "static" | "cheap-call" | "live-call";
31
+ export interface RuntimeHealthVector {
32
+ /** Legacy boolean dimensions retained for backward compatibility. */
33
+ runtimeOk?: boolean;
34
+ authOk?: boolean;
35
+ modelOk?: boolean;
36
+ quotaOk?: boolean;
37
+ rateLimitOk?: boolean;
38
+ /** Tri-state dimensions used by health-aware routing v2. */
39
+ runtime?: HealthState;
40
+ auth?: HealthState;
41
+ model?: HealthState;
42
+ quota?: HealthState;
43
+ rateLimit?: HealthState;
44
+ latencyMs?: number;
45
+ lastProbeKind?: RuntimeHealthProbeKind;
46
+ checkedAt?: string;
47
+ expiresAt?: string;
48
+ }
49
+ export interface RuntimeHealthProbeRequest {
50
+ readonly probeKind: RuntimeHealthProbeKind;
51
+ readonly taskRisk?: string;
52
+ readonly highRisk: boolean;
53
+ }
29
54
  export interface RuntimeHealth {
30
55
  runtimeId: RuntimeId;
31
56
  available: boolean;
32
57
  reason?: string;
33
58
  checkedAt: string;
59
+ /** Structured health signals beyond a binary available flag. */
60
+ vector?: RuntimeHealthVector;
34
61
  }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Freedomd data-retention gate.
3
+ *
4
+ * Decides whether a task's prompt, attachments, and relevant files may be sent
5
+ * to a provider given its retention profile and the org/user policy. The gate
6
+ * returns one of {allow, redact, downgrade, block} and never leaks secret-like
7
+ * spans to a retained provider.
8
+ */
9
+ import type { AgentRuntime, AgentTask } from "./agent-runtime.js";
10
+ import type { ProviderSovereigntyProfile } from "./provider-sovereignty.js";
11
+ export type DataBoundary = "public" | "internal" | "customer" | "secret";
12
+ export type RetentionDecision = "allow" | "redact" | "downgrade" | "block";
13
+ export interface DataSensitivity {
14
+ readonly boundary: DataBoundary;
15
+ readonly containsSecret: boolean;
16
+ readonly containsCustomerData: boolean;
17
+ readonly sensitiveSpans: readonly string[];
18
+ }
19
+ export interface ProviderRetentionProfile {
20
+ readonly retentionDays: number;
21
+ readonly zeroDataRetention: boolean;
22
+ readonly trainingUse: boolean;
23
+ readonly auditAccessUnknown: boolean;
24
+ }
25
+ export interface DataRetentionGateInput {
26
+ readonly task: AgentTask;
27
+ readonly runtime: AgentRuntime;
28
+ readonly providerProfile: ProviderSovereigntyProfile;
29
+ readonly orgMaxRetentionDays: number;
30
+ readonly allowRedaction: boolean;
31
+ readonly relevantFiles?: readonly string[];
32
+ }
33
+ export interface DataRetentionGateResult {
34
+ readonly decision: RetentionDecision;
35
+ readonly reason: string;
36
+ readonly sensitivity: DataSensitivity;
37
+ readonly redactedSpans?: readonly string[];
38
+ readonly downgradeMode?: "local-only-or-zdr-provider" | "read-only-local-review";
39
+ }
40
+ export declare function classifyDataSensitivity(task: AgentTask, relevantFiles?: readonly string[]): DataSensitivity;
41
+ export declare function providerRetentionProfileFromSovereignty(profile: ProviderSovereigntyProfile): ProviderRetentionProfile;
42
+ export declare function evaluateDataRetentionGate(input: DataRetentionGateInput): DataRetentionGateResult;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Freedomd data-retention gate.
3
+ *
4
+ * Decides whether a task's prompt, attachments, and relevant files may be sent
5
+ * to a provider given its retention profile and the org/user policy. The gate
6
+ * returns one of {allow, redact, downgrade, block} and never leaks secret-like
7
+ * spans to a retained provider.
8
+ */
9
+ import { maskSensitiveText } from "../util/secret-mask.js";
10
+ import { runtimeProviderId, runtimeModeOf } from "./authority-matrix.js";
11
+ const SECRET_KEYWORDS = [
12
+ "password", "secret", "token", "api_key", "apikey", "private_key",
13
+ "credential", "bearer", "authorization", "session", "cookie",
14
+ ];
15
+ const CUSTOMER_DATA_KEYWORDS = [
16
+ "customer", "user_id", "email", "phone", "ssn", "personal",
17
+ "gdpr", "hipaa", "pci", "pii",
18
+ ];
19
+ function boundaryForRisk(risk) {
20
+ if (risk === "shell" || risk === "merge")
21
+ return "secret";
22
+ if (risk === "write" || risk === "patch")
23
+ return "internal";
24
+ return "public";
25
+ }
26
+ function classifyTextSensitivity(text) {
27
+ const lower = text.toLowerCase();
28
+ const sensitiveSpans = [];
29
+ let containsSecret = false;
30
+ let containsCustomerData = false;
31
+ for (const keyword of SECRET_KEYWORDS) {
32
+ if (lower.includes(keyword)) {
33
+ containsSecret = true;
34
+ sensitiveSpans.push(`keyword:${keyword}`);
35
+ }
36
+ }
37
+ for (const keyword of CUSTOMER_DATA_KEYWORDS) {
38
+ if (lower.includes(keyword)) {
39
+ containsCustomerData = true;
40
+ sensitiveSpans.push(`keyword:${keyword}`);
41
+ }
42
+ }
43
+ const masked = maskSensitiveText(text);
44
+ if (masked !== text) {
45
+ containsSecret = true;
46
+ sensitiveSpans.push("pattern:secret-like");
47
+ }
48
+ return { containsSecret, containsCustomerData, sensitiveSpans: [...new Set(sensitiveSpans)] };
49
+ }
50
+ export function classifyDataSensitivity(task, relevantFiles) {
51
+ const text = [
52
+ task.prompt,
53
+ task.context.goal ?? "",
54
+ task.context.system ?? "",
55
+ ...(task.attachments ?? []).map((a) => a.name),
56
+ ...(relevantFiles ?? []),
57
+ ].join("\n");
58
+ const classified = classifyTextSensitivity(text);
59
+ const boundary = boundaryForRisk(task.safety?.risk);
60
+ return {
61
+ boundary,
62
+ containsSecret: classified.containsSecret,
63
+ containsCustomerData: classified.containsCustomerData,
64
+ sensitiveSpans: classified.sensitiveSpans,
65
+ };
66
+ }
67
+ export function providerRetentionProfileFromSovereignty(profile) {
68
+ return {
69
+ retentionDays: profile.retentionDays,
70
+ zeroDataRetention: profile.zeroDataRetention,
71
+ trainingUse: profile.trainingUse,
72
+ auditAccessUnknown: !profile.zeroDataRetention && profile.retentionDays > 0,
73
+ };
74
+ }
75
+ export function evaluateDataRetentionGate(input) {
76
+ const { task, runtime, providerProfile, orgMaxRetentionDays, allowRedaction } = input;
77
+ const sensitivity = classifyDataSensitivity(task, input.relevantFiles);
78
+ if (sensitivity.containsSecret) {
79
+ return {
80
+ decision: "block",
81
+ reason: "secret-like data cannot be sent to retained provider",
82
+ sensitivity,
83
+ };
84
+ }
85
+ const retention = providerRetentionProfileFromSovereignty(providerProfile);
86
+ if (sensitivity.containsCustomerData && !retention.zeroDataRetention) {
87
+ if (allowRedaction) {
88
+ return {
89
+ decision: "redact",
90
+ reason: `customer data redacted before sending to ${runtimeProviderId(runtime)}:${runtimeModeOf(runtime)}`,
91
+ sensitivity,
92
+ redactedSpans: sensitivity.sensitiveSpans,
93
+ };
94
+ }
95
+ return {
96
+ decision: "downgrade",
97
+ reason: "customer data with non-zero retention provider; downgrade required",
98
+ sensitivity,
99
+ downgradeMode: "local-only-or-zdr-provider",
100
+ };
101
+ }
102
+ if (retention.retentionDays > orgMaxRetentionDays) {
103
+ return {
104
+ decision: "block",
105
+ reason: `provider retention ${retention.retentionDays}d exceeds policy ${orgMaxRetentionDays}d`,
106
+ sensitivity,
107
+ };
108
+ }
109
+ const risk = task.safety?.risk;
110
+ if ((risk === "write" || risk === "shell" || risk === "merge") && retention.auditAccessUnknown) {
111
+ return {
112
+ decision: "downgrade",
113
+ reason: "high-risk task with unclear retention/audit boundary",
114
+ sensitivity,
115
+ downgradeMode: "read-only-local-review",
116
+ };
117
+ }
118
+ return {
119
+ decision: "allow",
120
+ reason: "retention policy acceptable for data boundary",
121
+ sensitivity,
122
+ };
123
+ }
@@ -5,6 +5,7 @@
5
5
  * Supports SSE streaming, tool calling, and reasoning_content.
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 DeepSeekRuntimeOptions {
10
11
  apiKey?: string;
@@ -13,6 +14,9 @@ export interface DeepSeekRuntimeOptions {
13
14
  }
14
15
  export declare class DeepSeekRuntime implements AgentRuntime {
15
16
  readonly id = "deepseek-api";
17
+ readonly providerId = "deepseek";
18
+ readonly advisory = true;
19
+ readonly runtimeMode = "api";
16
20
  readonly kind = "api";
17
21
  readonly priority = 40;
18
22
  readonly capabilities: RuntimeCapabilities;
@@ -21,7 +25,7 @@ export declare class DeepSeekRuntime implements AgentRuntime {
21
25
  private readonly baseUrl;
22
26
  constructor(options?: DeepSeekRuntimeOptions);
23
27
  supports(capsule: ContextCapsule): boolean;
24
- health(): Promise<RuntimeHealth>;
28
+ health(input?: RuntimeHealthProbeRequest): Promise<RuntimeHealth>;
25
29
  runNode(capsule: ContextCapsule, signal: AbortSignal): Promise<AgentRunResult>;
26
30
  execute(task: AgentTask): Promise<AgentResult>;
27
31
  private parseStreamResponse;