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.
- package/CHANGELOG.md +22 -0
- package/MATURITY.md +2 -2
- package/README.md +86 -39
- package/ROADMAP.md +20 -17
- package/dist/brand/night-city.theme.json +122 -24
- package/dist/brand/rust-forge.theme.json +1 -1
- package/dist/commands/chat/native-root-loop.d.ts +27 -3
- package/dist/commands/chat/native-root-loop.js +375 -49
- package/dist/commands/dag.js +45 -0
- package/dist/commands/init/content.d.ts +1 -1
- package/dist/commands/init/content.js +3 -3
- package/dist/commands/init.js +3 -3
- package/dist/commands/verify.js +59 -1
- package/dist/contracts/dag.d.ts +9 -1
- package/dist/evidence/graph-ontology-relation-gate.d.ts +10 -0
- package/dist/evidence/graph-ontology-relation-gate.js +46 -0
- package/dist/evidence/headroom-replay-verifier.d.ts +19 -0
- package/dist/evidence/headroom-replay-verifier.js +86 -0
- package/dist/memory/local-graph-memory-store.d.ts +75 -0
- package/dist/memory/local-graph-memory-store.js +570 -0
- package/dist/orchestration/dag.d.ts +22 -1
- package/dist/orchestration/evidence-gate.js +12 -0
- package/dist/orchestration/executor.js +108 -15
- package/dist/orchestration/loop-controller-headroom.d.ts +6 -0
- package/dist/orchestration/loop-controller-headroom.js +33 -0
- package/dist/orchestration/scheduler.d.ts +2 -0
- package/dist/orchestration/scheduler.js +3 -0
- package/dist/orchestration/task-graph.d.ts +17 -0
- package/dist/orchestration/task-graph.js +38 -0
- package/dist/providers/codex-cli-runner.js +17 -2
- package/dist/runtime/agent-runtime.d.ts +32 -3
- package/dist/runtime/agent-runtime.js +20 -7
- package/dist/runtime/authority-matrix.d.ts +23 -0
- package/dist/runtime/authority-matrix.js +159 -0
- package/dist/runtime/chat-advisory-runtime.js +3 -0
- package/dist/runtime/codex-cli-runtime.js +2 -0
- package/dist/runtime/codex-runtime.d.ts +4 -1
- package/dist/runtime/codex-runtime.js +34 -12
- package/dist/runtime/context-broker-converter.js +32 -2
- package/dist/runtime/context-broker.js +18 -0
- package/dist/runtime/contracts/evidence.d.ts +52 -0
- package/dist/runtime/contracts/evidence.js +128 -1
- package/dist/runtime/contracts/shared.d.ts +27 -0
- package/dist/runtime/data-retention-gate.d.ts +42 -0
- package/dist/runtime/data-retention-gate.js +123 -0
- package/dist/runtime/deepseek-runtime.d.ts +5 -1
- package/dist/runtime/deepseek-runtime.js +13 -7
- package/dist/runtime/external-cli-adapter.js +20 -7
- package/dist/runtime/freedomd-evidence-envelope.d.ts +57 -0
- package/dist/runtime/freedomd-evidence-envelope.js +110 -0
- package/dist/runtime/freedomd-exception.d.ts +32 -0
- package/dist/runtime/freedomd-exception.js +95 -0
- package/dist/runtime/freedomd-incidents.d.ts +23 -0
- package/dist/runtime/freedomd-incidents.js +103 -0
- package/dist/runtime/freedomd-policy.d.ts +35 -0
- package/dist/runtime/freedomd-policy.js +114 -0
- package/dist/runtime/freedomd-router.d.ts +66 -0
- package/dist/runtime/freedomd-router.js +433 -0
- package/dist/runtime/headroom-aware-loop-decision.d.ts +21 -0
- package/dist/runtime/headroom-aware-loop-decision.js +67 -0
- package/dist/runtime/headroom-loop-risk.d.ts +19 -0
- package/dist/runtime/headroom-loop-risk.js +43 -0
- package/dist/runtime/headroom-policy.d.ts +14 -1
- package/dist/runtime/headroom-policy.js +74 -12
- package/dist/runtime/kimi-api-runtime.d.ts +4 -1
- package/dist/runtime/kimi-api-runtime.js +35 -11
- package/dist/runtime/kimi-print-runtime.js +21 -0
- package/dist/runtime/kimi-wire-protocol-runtime.js +17 -1
- package/dist/runtime/local-llm-runtime.d.ts +5 -1
- package/dist/runtime/local-llm-runtime.js +14 -23
- package/dist/runtime/private-stderr.d.ts +13 -0
- package/dist/runtime/private-stderr.js +69 -0
- package/dist/runtime/prompt-envelope.d.ts +1 -1
- package/dist/runtime/provider-sovereignty.d.ts +81 -0
- package/dist/runtime/provider-sovereignty.js +285 -0
- package/dist/runtime/runtime-backed-task-runner.d.ts +1 -0
- package/dist/runtime/runtime-backed-task-runner.js +282 -10
- package/dist/runtime/runtime-bootstrap.d.ts +12 -0
- package/dist/runtime/runtime-bootstrap.js +56 -3
- package/dist/runtime/runtime-failure-classifier.d.ts +15 -0
- package/dist/runtime/runtime-failure-classifier.js +107 -0
- package/dist/runtime/runtime-health-probes.d.ts +30 -0
- package/dist/runtime/runtime-health-probes.js +102 -0
- package/dist/runtime/runtime-router.d.ts +6 -2
- package/dist/runtime/runtime-router.js +575 -53
- package/dist/runtime/structured-compaction.d.ts +107 -0
- package/dist/runtime/structured-compaction.js +414 -0
- package/dist/runtime/tool-dispatch-contracts.d.ts +7 -5
- package/dist/runtime/tool-dispatch-contracts.js +15 -4
- package/dist/runtime/tool-plane.d.ts +1 -1
- package/dist/runtime/tool-plane.js +49 -1
- package/dist/runtime/worker-manifest.js +1 -1
- package/dist/schema/evidence-bundle.schema.d.ts +8 -8
- package/dist/schema/proof-bundle.schema.d.ts +2 -2
- package/dist/util/hash.d.ts +2 -0
- package/dist/util/hash.js +13 -0
- package/dist/util/session.js +6 -5
- package/docs/2026-06-16/critical-issues.md +19 -0
- package/docs/2026-06-16/improvements.md +15 -0
- package/docs/2026-06-16/init-checklist.md +25 -0
- package/docs/2026-06-16/plan.md +20 -0
- package/docs/ARCHITECTURE_ANALYSIS_CODEGRAPH.md +2 -1
- package/docs/GSTACK_MIGRATION.md +1 -1
- package/docs/algorithm-hardening-playbook.md +219 -0
- package/docs/claims.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/native-root-runtime-hardening.md +42 -32
- package/docs/post-0793-hardening-roadmap.md +123 -0
- package/docs/provider-maturity.md +18 -16
- package/docs/versioning.md +3 -3
- package/docs/what-is-omk.md +1 -1
- package/package.json +8 -4
- package/readmeasset/.npmignore +2 -0
- package/readmeasset/ASSET_INDEX.md +2 -0
- package/readmeasset/ASSET_PROVENANCE.md +40 -20
- package/readmeasset/omk-adaptorch-ouroboros-supermemory.svg +1 -1
- package/readmeasset/omk-control-surfaces.svg +1 -1
- package/readmeasset/omk-core-loop.svg +1 -1
- package/readmeasset/omk-freedomd-control-plane.svg +59 -0
- package/readmeasset/omk-freedomd-control-plane.webp +0 -0
- package/readmeasset/omk-init-control-loop.svg +1 -1
- package/readmeasset/omk-logo-mark.svg +1 -1
- package/readmeasset/omk-parallel-subagents.svg +1 -1
- package/readmeasset/omk-release-assertions.svg +1 -1
|
@@ -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
|
|
73
|
-
|
|
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
|
|
77
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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;
|