open-multi-agent-kit 0.78.2 → 0.78.3
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 +27 -2
- package/dist/benchmark/contracts.d.ts +116 -0
- package/dist/benchmark/contracts.js +6 -0
- package/dist/benchmark/fixtures.d.ts +11 -0
- package/dist/benchmark/fixtures.js +121 -0
- package/dist/benchmark/harness.d.ts +13 -0
- package/dist/benchmark/harness.js +191 -0
- package/dist/benchmark/shadow-mode.d.ts +17 -0
- package/dist/benchmark/shadow-mode.js +96 -0
- package/dist/commands/merge.js +102 -56
- package/dist/contracts/provider-health.d.ts +37 -0
- package/dist/contracts/provider-health.js +49 -1
- package/dist/evidence/evidence-trust-score.d.ts +101 -0
- package/dist/evidence/evidence-trust-score.js +408 -0
- package/dist/evidence/index.d.ts +2 -0
- package/dist/evidence/index.js +1 -0
- package/dist/orchestration/merge-arbiter.d.ts +91 -0
- package/dist/orchestration/merge-arbiter.js +376 -0
- package/dist/providers/health.d.ts +3 -0
- package/dist/providers/health.js +46 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/provider-health.d.ts +8 -1
- package/dist/providers/provider-health.js +39 -0
- package/dist/providers/provider-task-runner.js +31 -0
- package/dist/providers/provider.d.ts +2 -0
- package/dist/providers/router.js +87 -3
- package/dist/providers/types.d.ts +4 -0
- package/dist/runtime/provider-maturity-gate.d.ts +2 -0
- package/dist/runtime/provider-maturity-gate.js +28 -0
- package/dist/runtime/tool-dispatch-contracts.d.ts +24 -3
- package/dist/runtime/tool-dispatch-contracts.js +42 -2
- package/dist/runtime/weakness-remediation-index.d.ts +1 -1
- package/dist/runtime/weakness-remediation-index.js +1 -1
- package/dist/safety/enforcement-engine.d.ts +89 -0
- package/dist/safety/enforcement-engine.js +279 -0
- package/dist/safety/tool-authority-gate.d.ts +40 -0
- package/dist/safety/tool-authority-gate.js +92 -0
- package/dist/schema/evidence.schema.d.ts +2 -2
- package/dist/schema/proof-bundle.schema.d.ts +2 -2
- package/docs/benchmark-design.md +122 -0
- package/package.json +5 -2
package/dist/providers/router.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PROVIDER_CAPABILITY_ORDINAL } from "../contracts/provider-health.js";
|
|
1
2
|
import { DEFAULT_AUTHORITY_PROVIDER, resolveFallbackProvider } from "./types.js";
|
|
2
3
|
const DEEPSEEK_READ_ONLY_ROLES = new Set([
|
|
3
4
|
"explorer",
|
|
@@ -41,7 +42,17 @@ export function routeProvider(input) {
|
|
|
41
42
|
const role = input.role.toLowerCase();
|
|
42
43
|
const seed = `${input.nodeId ?? ""}:${role}:${input.taskType}`;
|
|
43
44
|
const authorityProvider = resolveFallbackProvider(input.authorityProvider);
|
|
44
|
-
const
|
|
45
|
+
const authorityVector = input.providerHealthVectors?.[authorityProvider];
|
|
46
|
+
const authorityProviderAllowed = providerVectorAllowsAuthority(authorityVector);
|
|
47
|
+
const authorityProviderFallbackOnly = authorityVector
|
|
48
|
+
? !providerVectorQuotaOk(authorityVector) && providerVectorAllowsDirect(authorityVector)
|
|
49
|
+
: false;
|
|
50
|
+
const authorityDecision = (reason, confidence, deepseek, extra = {}) => {
|
|
51
|
+
if (!authorityProviderAllowed || authorityProviderFallbackOnly) {
|
|
52
|
+
return authorityProviderDecision(authorityProvider, `${providerLabel(authorityProvider)} ${authorityProviderFallbackOnly ? "quota exhausted" : "auth insufficient"}; using fallback-only route`, Math.max(0.5, confidence - 0.15), deepseek, extra);
|
|
53
|
+
}
|
|
54
|
+
return authorityProviderDecision(authorityProvider, reason, confidence, deepseek, extra);
|
|
55
|
+
};
|
|
45
56
|
const directDeepSeekAllowed = canUseDirectDeepSeek(role, input);
|
|
46
57
|
const dedicatedDeepSeekAgent = isDedicatedDeepSeekAgent(input);
|
|
47
58
|
const withRouteEnsemble = (decision, winner) => ({
|
|
@@ -60,6 +71,11 @@ export function routeProvider(input) {
|
|
|
60
71
|
if (role === "orchestrator" || role === "merger" || role === "integrator") {
|
|
61
72
|
return withRouteEnsemble(authorityDecision("Core orchestration and merge authority stay with the configured authority provider", 1), "safety-gate");
|
|
62
73
|
}
|
|
74
|
+
// Hard constraint: if authority provider is blocked by vector, skip to safety gate
|
|
75
|
+
// unless an explicit, fully-available external provider is requested.
|
|
76
|
+
if (!authorityProviderAllowed && !requestedExternalProvider(input)) {
|
|
77
|
+
return withRouteEnsemble(authorityProviderDecision(authorityProvider, `${providerLabel(authorityProvider)} auth/quota insufficient for authority lanes; safety gate active`, 0.5), "safety-gate");
|
|
78
|
+
}
|
|
63
79
|
const externalProvider = requestedExternalProvider(input);
|
|
64
80
|
if (externalProvider) {
|
|
65
81
|
if (!isProviderAvailable(input, externalProvider)) {
|
|
@@ -69,6 +85,12 @@ export function routeProvider(input) {
|
|
|
69
85
|
}
|
|
70
86
|
// If the external provider is the authority provider, route to it as authority
|
|
71
87
|
if (externalProvider === authorityProvider) {
|
|
88
|
+
// Hard constraint: auth != ok → exclude authority lanes
|
|
89
|
+
if (!authorityProviderAllowed) {
|
|
90
|
+
return withRouteEnsemble(authorityDecision(`${providerLabel(externalProvider)} auth/quota insufficient for authority; using configured fallback`, 0.86, undefined, {
|
|
91
|
+
providerModel: genericProviderModelRef(input, externalProvider, "veto"),
|
|
92
|
+
}), "safety-gate");
|
|
93
|
+
}
|
|
72
94
|
return withRouteEnsemble(authorityDecision(`${providerLabel(externalProvider)} is the configured authority provider`, 0.9, undefined, {
|
|
73
95
|
providerModel: genericProviderModelRef(input, externalProvider, "authority"),
|
|
74
96
|
}), "authority-provider");
|
|
@@ -178,8 +200,48 @@ function requestedExternalProvider(input) {
|
|
|
178
200
|
function isGenericExternalProvider(value) {
|
|
179
201
|
return typeof value === "string" && value !== "auto" && value !== "kimi" && value !== "deepseek";
|
|
180
202
|
}
|
|
203
|
+
function providerVectorMeets(vector, minState) {
|
|
204
|
+
if (!vector)
|
|
205
|
+
return true;
|
|
206
|
+
const current = PROVIDER_CAPABILITY_ORDINAL[vector.auth];
|
|
207
|
+
const required = PROVIDER_CAPABILITY_ORDINAL[minState];
|
|
208
|
+
return current >= required;
|
|
209
|
+
}
|
|
210
|
+
function providerVectorQuotaOk(vector) {
|
|
211
|
+
if (!vector)
|
|
212
|
+
return true;
|
|
213
|
+
return PROVIDER_CAPABILITY_ORDINAL[vector.quota] >= PROVIDER_CAPABILITY_ORDINAL["quota_available"];
|
|
214
|
+
}
|
|
215
|
+
function providerVectorAllowsAuthority(vector) {
|
|
216
|
+
if (!vector)
|
|
217
|
+
return true;
|
|
218
|
+
return (PROVIDER_CAPABILITY_ORDINAL[vector.auth] >= PROVIDER_CAPABILITY_ORDINAL["auth_valid"] &&
|
|
219
|
+
PROVIDER_CAPABILITY_ORDINAL[vector.model] >= PROVIDER_CAPABILITY_ORDINAL["model_available"] &&
|
|
220
|
+
PROVIDER_CAPABILITY_ORDINAL[vector.quota] >= PROVIDER_CAPABILITY_ORDINAL["quota_available"]);
|
|
221
|
+
}
|
|
222
|
+
function providerVectorAllowsDirect(vector) {
|
|
223
|
+
if (!vector)
|
|
224
|
+
return true;
|
|
225
|
+
return (PROVIDER_CAPABILITY_ORDINAL[vector.auth] >= PROVIDER_CAPABILITY_ORDINAL["auth_valid"] &&
|
|
226
|
+
PROVIDER_CAPABILITY_ORDINAL[vector.model] >= PROVIDER_CAPABILITY_ORDINAL["model_available"] &&
|
|
227
|
+
PROVIDER_CAPABILITY_ORDINAL[vector.quota] >= PROVIDER_CAPABILITY_ORDINAL["quota_available"]);
|
|
228
|
+
}
|
|
229
|
+
function providerVectorAllowsAdvisory(vector) {
|
|
230
|
+
if (!vector)
|
|
231
|
+
return true;
|
|
232
|
+
return PROVIDER_CAPABILITY_ORDINAL[vector.auth] >= PROVIDER_CAPABILITY_ORDINAL["auth_present"];
|
|
233
|
+
}
|
|
181
234
|
function isProviderAvailable(input, provider) {
|
|
182
235
|
const explicit = input.providerAvailability?.[provider];
|
|
236
|
+
const vector = input.providerHealthVectors?.[provider];
|
|
237
|
+
if (vector) {
|
|
238
|
+
// Hard constraint: quota exhausted → fallback only (not fully available)
|
|
239
|
+
if (!providerVectorQuotaOk(vector))
|
|
240
|
+
return false;
|
|
241
|
+
// Hard constraint: auth not valid → not available for any lane
|
|
242
|
+
if (PROVIDER_CAPABILITY_ORDINAL[vector.auth] < PROVIDER_CAPABILITY_ORDINAL["auth_valid"])
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
183
245
|
return explicit === undefined ? true : explicit;
|
|
184
246
|
}
|
|
185
247
|
function canUseGenericDirectProvider(role, input) {
|
|
@@ -187,7 +249,15 @@ function canUseGenericDirectProvider(role, input) {
|
|
|
187
249
|
return false;
|
|
188
250
|
if (input.needsMcp || input.needsToolCalling)
|
|
189
251
|
return false;
|
|
190
|
-
|
|
252
|
+
if (!input.readOnly && !GENERIC_EXTERNAL_READ_ONLY_ROLES.has(role))
|
|
253
|
+
return false;
|
|
254
|
+
const externalProvider = requestedExternalProvider(input);
|
|
255
|
+
if (externalProvider) {
|
|
256
|
+
const vector = input.providerHealthVectors?.[externalProvider];
|
|
257
|
+
if (vector && !providerVectorAllowsDirect(vector))
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
191
261
|
}
|
|
192
262
|
function canUseGenericAdvisoryProvider(role, input) {
|
|
193
263
|
if (!GENERIC_EXTERNAL_ADVISORY_FILE_ROLES.has(role))
|
|
@@ -196,6 +266,12 @@ function canUseGenericAdvisoryProvider(role, input) {
|
|
|
196
266
|
return false;
|
|
197
267
|
if (input.needsMcp || input.needsToolCalling)
|
|
198
268
|
return false;
|
|
269
|
+
const externalProvider = requestedExternalProvider(input);
|
|
270
|
+
if (externalProvider) {
|
|
271
|
+
const vector = input.providerHealthVectors?.[externalProvider];
|
|
272
|
+
if (vector && !providerVectorAllowsAdvisory(vector))
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
199
275
|
return true;
|
|
200
276
|
}
|
|
201
277
|
function canUseDeepSeekProAdvisory(role, input) {
|
|
@@ -205,12 +281,20 @@ function canUseDeepSeekProAdvisory(role, input) {
|
|
|
205
281
|
return false;
|
|
206
282
|
if (input.needsMcp || input.needsToolCalling)
|
|
207
283
|
return false;
|
|
284
|
+
const vector = input.providerHealthVectors?.["deepseek"];
|
|
285
|
+
if (vector && !providerVectorAllowsAdvisory(vector))
|
|
286
|
+
return false;
|
|
208
287
|
return true;
|
|
209
288
|
}
|
|
210
289
|
function canUseDirectDeepSeek(role, input) {
|
|
211
290
|
if (input.risk !== "read")
|
|
212
291
|
return false;
|
|
213
|
-
|
|
292
|
+
if (!(input.readOnly === true || DEEPSEEK_READ_ONLY_ROLES.has(role)))
|
|
293
|
+
return false;
|
|
294
|
+
const vector = input.providerHealthVectors?.["deepseek"];
|
|
295
|
+
if (vector && !providerVectorAllowsDirect(vector))
|
|
296
|
+
return false;
|
|
297
|
+
return true;
|
|
214
298
|
}
|
|
215
299
|
function buildProviderRouteEnsemble(options) {
|
|
216
300
|
const { input, role, decision, winner, directDeepSeekAllowed } = options;
|
|
@@ -71,6 +71,8 @@ export interface ProviderRouteInput {
|
|
|
71
71
|
authorityProvider?: ProviderId;
|
|
72
72
|
preferredModel?: string;
|
|
73
73
|
preferredDeepSeekTier?: DeepSeekModelTier;
|
|
74
|
+
/** v2 capability vectors for hard-constraint filtering. */
|
|
75
|
+
providerHealthVectors?: Partial<Record<ProviderId, import("../contracts/provider-health.js").ProviderHealthVector>>;
|
|
74
76
|
}
|
|
75
77
|
export interface ProviderRouteDecision {
|
|
76
78
|
provider: ProviderId;
|
|
@@ -90,6 +92,8 @@ export interface ProviderAvailability {
|
|
|
90
92
|
checkedAt: number;
|
|
91
93
|
reason?: string;
|
|
92
94
|
disableForRun: boolean;
|
|
95
|
+
/** v2 capability vector (optional; present when profiler v2 is active). */
|
|
96
|
+
healthVector?: import("../contracts/provider-health.js").ProviderHealthVector;
|
|
93
97
|
}
|
|
94
98
|
export interface AgentProvider {
|
|
95
99
|
id: ProviderId;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* verdict.
|
|
7
7
|
*/
|
|
8
8
|
import type { AdapterTestKind, AdapterTestResult, ProviderAuthorityClass } from "./contracts/evidence.js";
|
|
9
|
+
import type { ProviderHealthVector } from "../contracts/provider-health.js";
|
|
9
10
|
/** Maturity evaluation result for a single provider. */
|
|
10
11
|
export interface MaturityResult {
|
|
11
12
|
/** Computed maturity score M_p ∈ [0, 1]. */
|
|
@@ -39,3 +40,4 @@ export interface ProviderMaturityTable {
|
|
|
39
40
|
}
|
|
40
41
|
export declare function createProviderMaturityTable(): ProviderMaturityTable;
|
|
41
42
|
export declare function createProviderMaturityGate(): ProviderMaturityGate;
|
|
43
|
+
export declare function evaluateProviderFromVector(gate: ProviderMaturityGate, vector: ProviderHealthVector): MaturityResult;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* produces a maturity score M_p, an authority class, and a pass/fail
|
|
6
6
|
* verdict.
|
|
7
7
|
*/
|
|
8
|
+
import { PROVIDER_CAPABILITY_ORDINAL } from "../contracts/provider-health.js";
|
|
8
9
|
// ── Constants ───────────────────────────────────────────────────
|
|
9
10
|
const WEIGHT_AUTH = 0.10;
|
|
10
11
|
const WEIGHT_READ = 0.10;
|
|
@@ -72,6 +73,30 @@ export function createProviderMaturityTable() {
|
|
|
72
73
|
},
|
|
73
74
|
};
|
|
74
75
|
}
|
|
76
|
+
function vectorToAdapterResults(vector) {
|
|
77
|
+
const authOrdinal = PROVIDER_CAPABILITY_ORDINAL[vector.auth];
|
|
78
|
+
const binaryOrdinal = PROVIDER_CAPABILITY_ORDINAL[vector.binary];
|
|
79
|
+
const modelOrdinal = PROVIDER_CAPABILITY_ORDINAL[vector.model];
|
|
80
|
+
const quotaOrdinal = PROVIDER_CAPABILITY_ORDINAL[vector.quota];
|
|
81
|
+
const authScore = authOrdinal >= PROVIDER_CAPABILITY_ORDINAL["auth_valid"] ? 1.0 : authOrdinal / PROVIDER_CAPABILITY_ORDINAL["auth_valid"];
|
|
82
|
+
const readScore = vector.supportsRead ? 1.0 : 0.0;
|
|
83
|
+
const writeScore = vector.supportsWrite ? 1.0 : 0.0;
|
|
84
|
+
const shellScore = vector.supportsShell ? 1.0 : 0.0;
|
|
85
|
+
const mcpScore = binaryOrdinal >= PROVIDER_CAPABILITY_ORDINAL["tool_contract_verified"] ? 1.0 : binaryOrdinal / PROVIDER_CAPABILITY_ORDINAL["tool_contract_verified"];
|
|
86
|
+
const mergeScore = vector.evidencePassRate7d;
|
|
87
|
+
const evidenceScore = vector.evidencePassRate7d;
|
|
88
|
+
const fallbackScore = 1.0 - vector.failureEwma;
|
|
89
|
+
return [
|
|
90
|
+
{ kind: "auth", passed: authScore >= 0.5, score: authScore },
|
|
91
|
+
{ kind: "read", passed: readScore >= 0.5, score: readScore },
|
|
92
|
+
{ kind: "write", passed: writeScore >= 0.5, score: writeScore },
|
|
93
|
+
{ kind: "shell", passed: shellScore >= 0.5, score: shellScore },
|
|
94
|
+
{ kind: "mcp", passed: mcpScore >= 0.5, score: mcpScore },
|
|
95
|
+
{ kind: "merge", passed: mergeScore >= 0.5, score: mergeScore },
|
|
96
|
+
{ kind: "evidence", passed: evidenceScore >= 0.5, score: evidenceScore },
|
|
97
|
+
{ kind: "fallback", passed: fallbackScore >= 0.5, score: fallbackScore },
|
|
98
|
+
];
|
|
99
|
+
}
|
|
75
100
|
export function createProviderMaturityGate() {
|
|
76
101
|
return {
|
|
77
102
|
evaluate(results) {
|
|
@@ -99,3 +124,6 @@ export function createProviderMaturityGate() {
|
|
|
99
124
|
},
|
|
100
125
|
};
|
|
101
126
|
}
|
|
127
|
+
export function evaluateProviderFromVector(gate, vector) {
|
|
128
|
+
return gate.evaluate(vectorToAdapterResults(vector));
|
|
129
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OmkToolCall, OmkToolDefinition } from "./tool-registry-contract.js";
|
|
2
|
-
import { type ToolAuthorityDecision, type ToolOp } from "../safety/tool-authority-gate.js";
|
|
2
|
+
import { type ToolAuthorityDecision, type ToolOp, type ToolOpV2 } from "../safety/tool-authority-gate.js";
|
|
3
3
|
import type { ProviderAuthorityLevel } from "../contracts/provider-health.js";
|
|
4
|
+
import type { EnforcementProof } from "../safety/enforcement-engine.js";
|
|
4
5
|
export interface ToolDispatchResult<R = unknown> {
|
|
5
6
|
readonly call: OmkToolCall;
|
|
6
7
|
readonly status: "fulfilled" | "rejected";
|
|
@@ -15,13 +16,15 @@ export type ToolAuthorityMode = "shadow" | "enforce";
|
|
|
15
16
|
*/
|
|
16
17
|
export interface ToolAuthorityDecisionRecord {
|
|
17
18
|
readonly toolName: string;
|
|
18
|
-
readonly op: ToolOp;
|
|
19
|
+
readonly op: ToolOp | ToolOpV2;
|
|
19
20
|
readonly decision: ToolAuthorityDecision;
|
|
20
21
|
readonly mode: ToolAuthorityMode;
|
|
21
22
|
/** True only when the verdict actually rejected the call (enforce + block). */
|
|
22
23
|
readonly enforced: boolean;
|
|
23
24
|
/** Redacted, human-readable reason. Never includes args or secret values. */
|
|
24
25
|
readonly reason: string;
|
|
26
|
+
/** v2 enforcement proof hash when available. */
|
|
27
|
+
readonly policyHash?: string;
|
|
25
28
|
}
|
|
26
29
|
/**
|
|
27
30
|
* Authority wiring for one dispatch turn. All inputs are non-secret enum/bool
|
|
@@ -42,6 +45,12 @@ export interface ToolAuthorityWiring {
|
|
|
42
45
|
readonly enforce?: boolean;
|
|
43
46
|
/** Optional sink for computed verdicts (invoked in both shadow and enforce). */
|
|
44
47
|
readonly onDecision?: (record: ToolAuthorityDecisionRecord) => void;
|
|
48
|
+
/**
|
|
49
|
+
* v2 enforcement proof from the adapter / runtime.
|
|
50
|
+
* When present, the gate uses policy-dependent capability resolution.
|
|
51
|
+
* Runtimes without a valid proof cannot enter authority lanes.
|
|
52
|
+
*/
|
|
53
|
+
readonly enforcementProof?: EnforcementProof;
|
|
45
54
|
}
|
|
46
55
|
/**
|
|
47
56
|
* Resolve the global enforcement opt-in from the environment. Default OFF means
|
|
@@ -52,8 +61,9 @@ export declare function resolveToolAuthorityEnforcement(env?: Record<string, str
|
|
|
52
61
|
/** Error used to reject a tool call rejected by the authority gate (enforce mode). */
|
|
53
62
|
export declare class ToolAuthorityBlockedError extends Error {
|
|
54
63
|
readonly toolName: string;
|
|
55
|
-
readonly op: ToolOp;
|
|
64
|
+
readonly op: ToolOp | ToolOpV2;
|
|
56
65
|
readonly decision: ToolAuthorityDecision;
|
|
66
|
+
readonly policyHash?: string;
|
|
57
67
|
constructor(record: ToolAuthorityDecisionRecord);
|
|
58
68
|
}
|
|
59
69
|
/** Compute the gate verdict for a single call. Pure (no IO, no env reads). */
|
|
@@ -61,4 +71,15 @@ export declare function evaluateToolAuthority(toolName: string, wiring: ToolAuth
|
|
|
61
71
|
readonly record: ToolAuthorityDecisionRecord;
|
|
62
72
|
readonly blocked: boolean;
|
|
63
73
|
};
|
|
74
|
+
/**
|
|
75
|
+
* Compute the gate verdict for a single call using v2 enforcement proof.
|
|
76
|
+
* Pure (no IO, no env reads).
|
|
77
|
+
*
|
|
78
|
+
* If `enforcementProof` is present, the gate uses policy-dependent capability
|
|
79
|
+
* resolution. Runtimes without a valid proof cannot enter authority lanes.
|
|
80
|
+
*/
|
|
81
|
+
export declare function evaluateToolAuthorityV2(toolName: string, wiring: ToolAuthorityWiring): {
|
|
82
|
+
readonly record: ToolAuthorityDecisionRecord;
|
|
83
|
+
readonly blocked: boolean;
|
|
84
|
+
};
|
|
64
85
|
export declare function dispatchToolCallsByContract<A, R>(calls: readonly OmkToolCall<A>[], registry: ReadonlyMap<string, OmkToolDefinition<A, R>>, dispatchOne: (call: OmkToolCall<A>) => Promise<R>, authority?: ToolAuthorityWiring): Promise<ToolDispatchResult<R>[]>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createToolExecutionBatches } from "./tool-registry-contract.js";
|
|
2
|
-
import { decideToolAuthority, mapToolNameToOp, } from "../safety/tool-authority-gate.js";
|
|
2
|
+
import { decideToolAuthority, decideToolAuthorityV2, mapToolNameToOp, } from "../safety/tool-authority-gate.js";
|
|
3
3
|
const ENFORCE_PATTERN = /^(1|true|yes|on)$/i;
|
|
4
4
|
/**
|
|
5
5
|
* Resolve the global enforcement opt-in from the environment. Default OFF means
|
|
@@ -20,12 +20,14 @@ export class ToolAuthorityBlockedError extends Error {
|
|
|
20
20
|
toolName;
|
|
21
21
|
op;
|
|
22
22
|
decision;
|
|
23
|
+
policyHash;
|
|
23
24
|
constructor(record) {
|
|
24
25
|
super(record.reason);
|
|
25
26
|
this.name = "ToolAuthorityBlockedError";
|
|
26
27
|
this.toolName = record.toolName;
|
|
27
28
|
this.op = record.op;
|
|
28
29
|
this.decision = record.decision;
|
|
30
|
+
this.policyHash = record.policyHash;
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
/** Compute the gate verdict for a single call. Pure (no IO, no env reads). */
|
|
@@ -57,6 +59,44 @@ export function evaluateToolAuthority(toolName, wiring) {
|
|
|
57
59
|
blocked,
|
|
58
60
|
};
|
|
59
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Compute the gate verdict for a single call using v2 enforcement proof.
|
|
64
|
+
* Pure (no IO, no env reads).
|
|
65
|
+
*
|
|
66
|
+
* If `enforcementProof` is present, the gate uses policy-dependent capability
|
|
67
|
+
* resolution. Runtimes without a valid proof cannot enter authority lanes.
|
|
68
|
+
*/
|
|
69
|
+
export function evaluateToolAuthorityV2(toolName, wiring) {
|
|
70
|
+
const op = mapToolNameToOp(toolName);
|
|
71
|
+
if (wiring.enforcementProof) {
|
|
72
|
+
const decision = decideToolAuthorityV2({
|
|
73
|
+
op,
|
|
74
|
+
writeAuthority: wiring.writeAuthority,
|
|
75
|
+
shellAuthority: wiring.shellAuthority,
|
|
76
|
+
approvalPolicy: wiring.approvalPolicy,
|
|
77
|
+
sandboxMode: wiring.sandboxMode,
|
|
78
|
+
tty: wiring.tty,
|
|
79
|
+
enforcementProof: wiring.enforcementProof,
|
|
80
|
+
});
|
|
81
|
+
const enforce = wiring.enforce === true;
|
|
82
|
+
const wouldBlock = decision === "block" || (decision === "ask" && !wiring.tty);
|
|
83
|
+
const blocked = enforce && wouldBlock;
|
|
84
|
+
return {
|
|
85
|
+
record: {
|
|
86
|
+
toolName,
|
|
87
|
+
op,
|
|
88
|
+
decision,
|
|
89
|
+
mode: enforce ? "enforce" : "shadow",
|
|
90
|
+
enforced: blocked,
|
|
91
|
+
reason: redactedAuthorityReason(op, decision, wiring),
|
|
92
|
+
policyHash: wiring.enforcementProof.policyHash,
|
|
93
|
+
},
|
|
94
|
+
blocked,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Fall back to legacy evaluation when no proof is present.
|
|
98
|
+
return evaluateToolAuthority(toolName, wiring);
|
|
99
|
+
}
|
|
60
100
|
/**
|
|
61
101
|
* Wrap a dispatch function with the authority checkpoint. In shadow mode the
|
|
62
102
|
* wrapper records the verdict and always delegates to `dispatchOne`. In enforce
|
|
@@ -64,7 +104,7 @@ export function evaluateToolAuthority(toolName, wiring) {
|
|
|
64
104
|
*/
|
|
65
105
|
function buildGatedDispatch(wiring, dispatchOne) {
|
|
66
106
|
return async (call) => {
|
|
67
|
-
const { record, blocked } =
|
|
107
|
+
const { record, blocked } = evaluateToolAuthorityV2(call.toolName, wiring);
|
|
68
108
|
wiring.onDecision?.(record);
|
|
69
109
|
if (blocked) {
|
|
70
110
|
throw new ToolAuthorityBlockedError(record);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
export { type IntegrationResultKind, type WeaknessRemediationState, type AdvancedControlLoopInput, type AdvancedControlLoopResult, type AdvancedControlLoop, type AdvancedControlLoopOptions, createAdvancedControlLoop, } from "./advanced-control-loop.js";
|
|
6
6
|
export { type SurfaceItem, type ScoredSurfaceItem, type MandatoryAnchor, type CompressionResult, type PublicSurfaceCompressorOptions, computeSurfaceScore, enforceFlowInvariant, PublicSurfaceCompressor, } from "./public-surface.js";
|
|
7
7
|
export { type ProofBundleScores, type TrustScoreResult, type ProofBundleTrustEngine, type DeriveScoresOptions, createProofBundleTrustEngine, } from "./proof-bundle-trust.js";
|
|
8
|
-
export { type MaturityResult, type ProviderMaturityGate, createProviderMaturityGate, } from "./provider-maturity-gate.js";
|
|
8
|
+
export { type MaturityResult, type ProviderMaturityGate, createProviderMaturityGate, evaluateProviderFromVector, } from "./provider-maturity-gate.js";
|
|
9
9
|
export { type RuntimeScoreV2, type RuntimeRouterDecisionV2, type RouterV2Options, type RouterV2ScoringEngine, type BlastRadiusParams, type EvidenceHistoryEntry, type NodeIntent, } from "./contracts/router-v2.js";
|
|
10
10
|
export { createRouterV2ScoringEngine, } from "./router-v2-scoring.js";
|
|
11
11
|
export { type ReleasePromotionInputs, type ReleasePromotionResult, type ReleaseVerdict, RELEASE_GATE_WEIGHTS, TAU_EVIDENCE, TAU_EVIDENCE_HIGH, TAU_PROOF, TAU_STABLE, BETA_PRIOR_ALPHA0, BETA_PRIOR_BETA0, SURFACE_BUDGET_K, } from "./contracts/weakness-remediation.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
export { createAdvancedControlLoop, } from "./advanced-control-loop.js";
|
|
6
6
|
export { computeSurfaceScore, enforceFlowInvariant, PublicSurfaceCompressor, } from "./public-surface.js";
|
|
7
7
|
export { createProofBundleTrustEngine, } from "./proof-bundle-trust.js";
|
|
8
|
-
export { createProviderMaturityGate, } from "./provider-maturity-gate.js";
|
|
8
|
+
export { createProviderMaturityGate, evaluateProviderFromVector, } from "./provider-maturity-gate.js";
|
|
9
9
|
export { createRouterV2ScoringEngine, } from "./router-v2-scoring.js";
|
|
10
10
|
export { RELEASE_GATE_WEIGHTS, TAU_EVIDENCE, TAU_EVIDENCE_HIGH, TAU_PROOF, TAU_STABLE, BETA_PRIOR_ALPHA0, BETA_PRIOR_BETA0, SURFACE_BUDGET_K, } from "./contracts/weakness-remediation.js";
|
|
11
11
|
export { createReleasePromotionGate, } from "../cli/release-promotion-gate.js";
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy / Sandbox Enforcement Engine v2
|
|
3
|
+
*
|
|
4
|
+
* Capability lattice with conservative policy combination.
|
|
5
|
+
* effectivePolicy = minByAuthority(userPolicy, repoPolicy, providerPolicy, adapterPolicy, riskPolicy)
|
|
6
|
+
*
|
|
7
|
+
* Conservative by default. Any ambiguity → block.
|
|
8
|
+
*/
|
|
9
|
+
export type SandboxCapability = "read" | "write" | "shell" | "network" | "secret_read" | "secret_write" | "merge" | "publish";
|
|
10
|
+
export declare const ALL_CAPABILITIES: readonly SandboxCapability[];
|
|
11
|
+
export type CapabilityLevel = "none" | "advisory" | "direct" | "full";
|
|
12
|
+
export interface CapabilityLattice {
|
|
13
|
+
read: CapabilityLevel;
|
|
14
|
+
write: CapabilityLevel;
|
|
15
|
+
shell: CapabilityLevel;
|
|
16
|
+
network: CapabilityLevel;
|
|
17
|
+
secret_read: CapabilityLevel;
|
|
18
|
+
secret_write: CapabilityLevel;
|
|
19
|
+
merge: CapabilityLevel;
|
|
20
|
+
publish: CapabilityLevel;
|
|
21
|
+
}
|
|
22
|
+
export type SandboxMode = "read-only" | "workspace-write" | "network-isolated" | "unrestricted";
|
|
23
|
+
export type ApprovalPolicy = "interactive" | "auto" | "yolo" | "block";
|
|
24
|
+
export interface PolicyLayer {
|
|
25
|
+
readonly source: "user" | "repo" | "provider" | "adapter" | "risk";
|
|
26
|
+
/** Partial lattice — omitted capabilities mean "no opinion" (inherit from other layers). */
|
|
27
|
+
readonly lattice: Partial<CapabilityLattice>;
|
|
28
|
+
readonly sandboxMode?: SandboxMode;
|
|
29
|
+
readonly approvalPolicy?: ApprovalPolicy;
|
|
30
|
+
}
|
|
31
|
+
export interface CombinedPolicy {
|
|
32
|
+
readonly lattice: Readonly<CapabilityLattice>;
|
|
33
|
+
readonly sandboxMode: SandboxMode;
|
|
34
|
+
readonly approvalPolicy: ApprovalPolicy;
|
|
35
|
+
/** Ordered list of sources that contributed to the combination. */
|
|
36
|
+
readonly sources: readonly PolicyLayer["source"][];
|
|
37
|
+
}
|
|
38
|
+
export interface EnforcementProof {
|
|
39
|
+
readonly sandboxMode: SandboxMode;
|
|
40
|
+
/** Which policy layers were active in the final combination. */
|
|
41
|
+
readonly enforcedBy: readonly string[];
|
|
42
|
+
/** Capabilities fully blocked (level === "none" or sandbox hard floor). */
|
|
43
|
+
readonly blockedCapabilities: readonly SandboxCapability[];
|
|
44
|
+
/** Capabilities that require explicit approval (level === "advisory" or interactive policy). */
|
|
45
|
+
readonly approvalRequired: readonly SandboxCapability[];
|
|
46
|
+
/** Deterministic hash of the combined policy for audit / replay. */
|
|
47
|
+
readonly policyHash: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function rankOf(level: CapabilityLevel): number;
|
|
50
|
+
export declare function defaultLattice(): CapabilityLattice;
|
|
51
|
+
/**
|
|
52
|
+
* Combine multiple policy layers by taking the **most restrictive**
|
|
53
|
+
* (minimum) authority level for each capability.
|
|
54
|
+
*
|
|
55
|
+
* If no layer expresses an opinion on a capability, it defaults to "full".
|
|
56
|
+
* If any layer expresses a sandbox mode, the most restrictive mode wins.
|
|
57
|
+
* If any layer expresses an approval policy, the most restrictive wins.
|
|
58
|
+
*/
|
|
59
|
+
export declare function combinePoliciesByMinAuthority(layers: readonly PolicyLayer[]): CombinedPolicy;
|
|
60
|
+
/**
|
|
61
|
+
* Compute the enforcement proof from a combined policy.
|
|
62
|
+
*
|
|
63
|
+
* Rules:
|
|
64
|
+
* 1. read-only sandbox blocks write, shell, network, merge, publish.
|
|
65
|
+
* 2. network-isolated sandbox blocks network.
|
|
66
|
+
* 3. Any capability with level "none" is blocked.
|
|
67
|
+
* 4. Any capability with level "advisory" requires approval.
|
|
68
|
+
* 5. interactive policy requires approval for non-read capabilities.
|
|
69
|
+
* 6. block policy blocks everything except read.
|
|
70
|
+
*/
|
|
71
|
+
export declare function computeEnforcementProof(combined: CombinedPolicy): EnforcementProof;
|
|
72
|
+
/**
|
|
73
|
+
* Returns true when the runtime/adapter has provided a valid enforcement proof.
|
|
74
|
+
* Runtimes without enforcement proof cannot enter authority lanes.
|
|
75
|
+
*/
|
|
76
|
+
export declare function hasValidEnforcementProof(proof: unknown): proof is EnforcementProof;
|
|
77
|
+
export declare function policyLayerFromLegacyAuthorities(source: PolicyLayer["source"], options: {
|
|
78
|
+
writeAuthority?: "none" | "advisory" | "direct" | "full";
|
|
79
|
+
shellAuthority?: "none" | "advisory" | "direct" | "full";
|
|
80
|
+
sandboxMode?: SandboxMode;
|
|
81
|
+
approvalPolicy?: ApprovalPolicy;
|
|
82
|
+
}): PolicyLayer;
|
|
83
|
+
export type ToolOpV2 = "read" | "write" | "shell" | "merge" | "network" | "secret";
|
|
84
|
+
/**
|
|
85
|
+
* Map a capability-lattice capability to the coarse ToolOp used by the gate.
|
|
86
|
+
* This preserves backward compatibility with the existing 4-class gate while
|
|
87
|
+
* allowing the new lattice to express finer-grained restrictions.
|
|
88
|
+
*/
|
|
89
|
+
export declare function capabilityToToolOp(cap: SandboxCapability): ToolOpV2;
|