@coderifts/agent-guard 9.5.0 → 9.6.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/README.md +35 -0
- package/dist/cjs/execution-grant.d.ts +41 -0
- package/dist/cjs/execution-grant.d.ts.map +1 -0
- package/dist/cjs/execution-grant.js +66 -0
- package/dist/cjs/execution-grant.js.map +1 -0
- package/dist/cjs/guard.d.ts.map +1 -1
- package/dist/cjs/guard.js +91 -37
- package/dist/cjs/guard.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/tool-registry.d.ts +1 -1
- package/dist/cjs/tool-registry.d.ts.map +1 -1
- package/dist/cjs/tool-registry.js +2 -2
- package/dist/cjs/tool-registry.js.map +1 -1
- package/dist/cjs/types.d.ts +22 -2
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/with-coderifts.d.ts +6 -0
- package/dist/cjs/with-coderifts.d.ts.map +1 -1
- package/dist/cjs/with-coderifts.js +3 -0
- package/dist/cjs/with-coderifts.js.map +1 -1
- package/dist/esm/execution-grant.d.ts +41 -0
- package/dist/esm/execution-grant.d.ts.map +1 -0
- package/dist/esm/execution-grant.js +59 -0
- package/dist/esm/execution-grant.js.map +1 -0
- package/dist/esm/guard.d.ts.map +1 -1
- package/dist/esm/guard.js +91 -37
- package/dist/esm/guard.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tool-registry.d.ts +1 -1
- package/dist/esm/tool-registry.d.ts.map +1 -1
- package/dist/esm/tool-registry.js +2 -2
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/types.d.ts +22 -2
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/with-coderifts.d.ts +6 -0
- package/dist/esm/with-coderifts.d.ts.map +1 -1
- package/dist/esm/with-coderifts.js +3 -0
- package/dist/esm/with-coderifts.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -250,6 +250,41 @@ const { tools, registry_report, composition_assurance, receipt_thread } = withCo
|
|
|
250
250
|
// register ONLY `tools`. Receipt carry-forward is default-on (`receipt_thread`).
|
|
251
251
|
```
|
|
252
252
|
|
|
253
|
+
### Native execution grant (`executionGrant`, 9.6.0)
|
|
254
|
+
|
|
255
|
+
`withCodeRifts` 9.5.0 could not request a grant: its authorize sent four
|
|
256
|
+
fields and the factory received `decision_result`, so a top-level
|
|
257
|
+
`execution_grant` was dropped. The interim app-side wrap
|
|
258
|
+
(`withExecutionGrantClient` / `takeGrant()`) worked but was **last-authorize**
|
|
259
|
+
— overlapping tool calls could hand a tool the wrong grant.
|
|
260
|
+
|
|
261
|
+
9.6.0 closes that natively. Default **OFF** (absent config is byte-identical
|
|
262
|
+
to 9.5.0). A host on 9.6.0 does **not** need `withExecutionGrantClient`.
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
const { tools } = withCodeRifts({
|
|
266
|
+
tools: rawTools, // execute(args, { execution_grant }) — 2nd arg is THIS call's grant
|
|
267
|
+
client,
|
|
268
|
+
operation: 'merge',
|
|
269
|
+
executionGrant: {
|
|
270
|
+
enabled: true,
|
|
271
|
+
// Optional. Return the ATOMIC nonce for THIS call (customer executor
|
|
272
|
+
// state-challenge). A throw fails the call closed
|
|
273
|
+
// (EXECUTION_GRANT_NONCE_UNRESOLVABLE) — never a grant-less proceed.
|
|
274
|
+
resolveStateNonce: async ({ artifactId, toolName, args }) =>
|
|
275
|
+
challengeNonceFor(artifactId),
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`guardToolCall` factory 3rd argument is the same `{ execution_grant }` object,
|
|
281
|
+
scoped to that invocation. Allow-class authorize that requested a grant and
|
|
282
|
+
does not receive one fails closed (`EXECUTION_GRANT_MISSING` /
|
|
283
|
+
`SIGNER_UNAVAILABLE`). After a grant was requested, `failPolicy: 'open'`
|
|
284
|
+
does not OPEN_PASSTHROUGH grant-less — the call fails closed with the
|
|
285
|
+
server's reason. The outcome records `{ requested, arrived }` only —
|
|
286
|
+
not the token. Not a verdict input; not a preimage field.
|
|
287
|
+
|
|
253
288
|
Honesty (do not over-claim):
|
|
254
289
|
|
|
255
290
|
- **Freshness** is ACTIVE only because STRICT sets `requireFreshness` **and** you supplied
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native execution-grant helpers (9.6.0).
|
|
3
|
+
*
|
|
4
|
+
* Grant request is per guardToolCall invocation (local vars). The token is not
|
|
5
|
+
* a verdict input and is not folded into any fingerprint preimage.
|
|
6
|
+
*/
|
|
7
|
+
export type ExecutionGrantConfig = {
|
|
8
|
+
enabled: true;
|
|
9
|
+
resolveStateNonce?: (ctx: {
|
|
10
|
+
artifactId: string | null;
|
|
11
|
+
toolName: string;
|
|
12
|
+
args: unknown;
|
|
13
|
+
}) => string | Promise<string>;
|
|
14
|
+
};
|
|
15
|
+
export type ExecutionGrantObservation = {
|
|
16
|
+
requested: boolean;
|
|
17
|
+
arrived: boolean;
|
|
18
|
+
};
|
|
19
|
+
export type ExecutionGrantCallContext = {
|
|
20
|
+
execution_grant: string | null;
|
|
21
|
+
};
|
|
22
|
+
export declare function isExecutionGrantEnabled(config: {
|
|
23
|
+
executionGrant?: ExecutionGrantConfig;
|
|
24
|
+
} | null | undefined): boolean;
|
|
25
|
+
export declare function readExecutionGrantToken(response: unknown): string | null;
|
|
26
|
+
export declare function firstArtifactId(artifacts: unknown): string | null;
|
|
27
|
+
/** True when the thrown authorize error carries the named SIGNER_UNAVAILABLE code. */
|
|
28
|
+
export declare function isSignerUnavailableError(err: unknown): boolean;
|
|
29
|
+
export declare function resolveStateNonceForCall(config: {
|
|
30
|
+
executionGrant?: ExecutionGrantConfig;
|
|
31
|
+
}, call: {
|
|
32
|
+
toolName: string;
|
|
33
|
+
arguments?: unknown;
|
|
34
|
+
artifacts?: unknown;
|
|
35
|
+
}, artifacts: unknown): Promise<{
|
|
36
|
+
ok: true;
|
|
37
|
+
nonce?: string;
|
|
38
|
+
} | {
|
|
39
|
+
ok: false;
|
|
40
|
+
}>;
|
|
41
|
+
//# sourceMappingURL=execution-grant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-grant.d.ts","sourceRoot":"","sources":["../../src/execution-grant.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,IAAI,CAAC;IACd,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE;QACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,OAAO,CAAC;KACf,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAAE,cAAc,CAAC,EAAE,oBAAoB,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAErH;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIxE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CASjE;AAED,sFAAsF;AACtF,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAM9D;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE;IAAE,cAAc,CAAC,EAAE,oBAAoB,CAAA;CAAE,EACjD,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,EACpE,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAevD"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Native execution-grant helpers (9.6.0).
|
|
4
|
+
*
|
|
5
|
+
* Grant request is per guardToolCall invocation (local vars). The token is not
|
|
6
|
+
* a verdict input and is not folded into any fingerprint preimage.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.isExecutionGrantEnabled = isExecutionGrantEnabled;
|
|
10
|
+
exports.readExecutionGrantToken = readExecutionGrantToken;
|
|
11
|
+
exports.firstArtifactId = firstArtifactId;
|
|
12
|
+
exports.isSignerUnavailableError = isSignerUnavailableError;
|
|
13
|
+
exports.resolveStateNonceForCall = resolveStateNonceForCall;
|
|
14
|
+
function isExecutionGrantEnabled(config) {
|
|
15
|
+
return !!(config && config.executionGrant && config.executionGrant.enabled === true);
|
|
16
|
+
}
|
|
17
|
+
function readExecutionGrantToken(response) {
|
|
18
|
+
if (!response || typeof response !== 'object')
|
|
19
|
+
return null;
|
|
20
|
+
const g = response.execution_grant;
|
|
21
|
+
return typeof g === 'string' && g.length > 0 ? g : null;
|
|
22
|
+
}
|
|
23
|
+
function firstArtifactId(artifacts) {
|
|
24
|
+
if (!Array.isArray(artifacts))
|
|
25
|
+
return null;
|
|
26
|
+
for (const a of artifacts) {
|
|
27
|
+
if (a && typeof a === 'object' && typeof a.id === 'string') {
|
|
28
|
+
const id = String(a.id).trim();
|
|
29
|
+
if (id)
|
|
30
|
+
return id;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
/** True when the thrown authorize error carries the named SIGNER_UNAVAILABLE code. */
|
|
36
|
+
function isSignerUnavailableError(err) {
|
|
37
|
+
if (!err || typeof err !== 'object')
|
|
38
|
+
return false;
|
|
39
|
+
const e = err;
|
|
40
|
+
if (e.code === 'SIGNER_UNAVAILABLE')
|
|
41
|
+
return true;
|
|
42
|
+
if (e.body && e.body.code === 'SIGNER_UNAVAILABLE')
|
|
43
|
+
return true;
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
async function resolveStateNonceForCall(config, call, artifacts) {
|
|
47
|
+
const resolver = config.executionGrant && config.executionGrant.resolveStateNonce;
|
|
48
|
+
if (typeof resolver !== 'function')
|
|
49
|
+
return { ok: true };
|
|
50
|
+
try {
|
|
51
|
+
const raw = await resolver({
|
|
52
|
+
artifactId: firstArtifactId(artifacts) || firstArtifactId(call.artifacts),
|
|
53
|
+
toolName: call.toolName,
|
|
54
|
+
args: call.arguments,
|
|
55
|
+
});
|
|
56
|
+
if (raw == null || raw === '')
|
|
57
|
+
return { ok: true };
|
|
58
|
+
if (typeof raw !== 'string')
|
|
59
|
+
return { ok: false };
|
|
60
|
+
return { ok: true, nonce: raw };
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return { ok: false };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=execution-grant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-grant.js","sourceRoot":"","sources":["../../src/execution-grant.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAoBH,0DAEC;AAED,0DAIC;AAED,0CASC;AAGD,4DAMC;AAED,4DAmBC;AAjDD,SAAgB,uBAAuB,CAAC,MAAoE;IAC1G,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,cAAc,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC;AACvF,CAAC;AAED,SAAgB,uBAAuB,CAAC,QAAiB;IACvD,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,CAAC,GAAI,QAA0C,CAAC,eAAe,CAAC;IACtE,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED,SAAgB,eAAe,CAAC,SAAkB;IAChD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAQ,CAAsB,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YACjF,MAAM,EAAE,GAAG,MAAM,CAAE,CAAoB,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACnD,IAAI,EAAE;gBAAE,OAAO,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,sFAAsF;AACtF,SAAgB,wBAAwB,CAAC,GAAY;IACnD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,CAAC,GAAG,GAAoD,CAAC;IAC/D,IAAI,CAAC,CAAC,IAAI,KAAK,oBAAoB;QAAE,OAAO,IAAI,CAAC;IACjD,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,oBAAoB;QAAE,OAAO,IAAI,CAAC;IAChE,OAAO,KAAK,CAAC;AACf,CAAC;AAEM,KAAK,UAAU,wBAAwB,CAC5C,MAAiD,EACjD,IAAoE,EACpE,SAAkB;IAElB,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,cAAc,CAAC,iBAAiB,CAAC;IAClF,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACxD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC;YACzB,UAAU,EAAE,eAAe,CAAC,SAAS,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;YACzE,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,IAAI,CAAC,SAAS;SACrB,CAAC,CAAC;QACH,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACnD,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QAClD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
|
package/dist/cjs/guard.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,WAAW,EAAE,YAAY,EACT,kBAAkB,EAAE,cAAc,EAEnD,MAAM,YAAY,CAAC;AAYpB,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAGL,KAAK,2BAA2B,EACjC,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,WAAW,EAAE,YAAY,EACT,kBAAkB,EAAE,cAAc,EAEnD,MAAM,YAAY,CAAC;AAYpB,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAGL,KAAK,2BAA2B,EACjC,MAAM,wBAAwB,CAAC;AAgchC;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,IAAI,EAAE,kBAAkB,EACxB,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC,EACjC,MAAM,EAAE,WAAW,EACnB,WAAW,CAAC,EAAE,oBAAoB,GAAG,2BAA2B,GAC/D,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAqc1B"}
|
package/dist/cjs/guard.js
CHANGED
|
@@ -25,6 +25,7 @@ const cas_attestation_js_1 = require("./cas-attestation.js");
|
|
|
25
25
|
const monitoring_delivery_js_1 = require("./monitoring-delivery.js");
|
|
26
26
|
const monitoring_attestation_js_1 = require("./monitoring-attestation.js");
|
|
27
27
|
const policy_js_1 = require("./policy.js");
|
|
28
|
+
const execution_grant_js_1 = require("./execution-grant.js");
|
|
28
29
|
// Per-config breaker state (time-window; not consecutive).
|
|
29
30
|
const breakers = new WeakMap();
|
|
30
31
|
const nowMs = () => Date.now();
|
|
@@ -83,7 +84,12 @@ function breakerTripped(config) {
|
|
|
83
84
|
s.fails = s.fails.filter((x) => t - x < win);
|
|
84
85
|
return s.fails.length >= (config.maxUnavailablePerWindow ?? 3);
|
|
85
86
|
}
|
|
86
|
-
function
|
|
87
|
+
function requestAsksForGrant(request) {
|
|
88
|
+
return !!(request
|
|
89
|
+
&& typeof request === 'object'
|
|
90
|
+
&& request.include_execution_grant === true);
|
|
91
|
+
}
|
|
92
|
+
function classifyError(err, config, grantRequested = false) {
|
|
87
93
|
const e = err;
|
|
88
94
|
const name = e?.name;
|
|
89
95
|
const status = e?.status ?? e?.body?.status;
|
|
@@ -97,6 +103,11 @@ function classifyError(err, config) {
|
|
|
97
103
|
return { cause: 'REQUEST_REJECTED', integrity: true };
|
|
98
104
|
if (status === 400 || status === 401 || status === 409)
|
|
99
105
|
return { cause: 'REQUEST_REJECTED', integrity: true };
|
|
106
|
+
// SIGNER_UNAVAILABLE is grant-path only. Gating keeps grant-OFF 5xx as SERVER_ERROR (9.5.0).
|
|
107
|
+
// Naked 503 while THIS request asked for a grant is the signer-off class, not availability.
|
|
108
|
+
if (grantRequested && ((0, execution_grant_js_1.isSignerUnavailableError)(err) || status === 503)) {
|
|
109
|
+
return { cause: 'SIGNER_UNAVAILABLE', integrity: true };
|
|
110
|
+
}
|
|
100
111
|
if (typeof status === 'number' && status >= 500)
|
|
101
112
|
return { cause: 'SERVER_ERROR', integrity: false };
|
|
102
113
|
if (name === 'TypeError' || /fetch failed|network|ENOTFOUND|ECONNREFUSED|EAI_AGAIN/i.test(String(e?.message)))
|
|
@@ -130,7 +141,7 @@ async function preflightWithRetry(config, request) {
|
|
|
130
141
|
return { ok: true, response };
|
|
131
142
|
}
|
|
132
143
|
catch (err) {
|
|
133
|
-
last = classifyError(err, config);
|
|
144
|
+
last = classifyError(err, config, requestAsksForGrant(request));
|
|
134
145
|
if (last.integrity)
|
|
135
146
|
return { ok: false, ...last }; // integrity: never retry
|
|
136
147
|
}
|
|
@@ -167,30 +178,34 @@ async function verifyEnvelope(config, envelope) {
|
|
|
167
178
|
}
|
|
168
179
|
// ── Outcome builders (keep the discriminated union satisfied) ────────────────────
|
|
169
180
|
// Every path attaches proof + freshness + conditional_write bases (guard-produced only).
|
|
170
|
-
async function runEnforced(config, factory, approved, redacted, freshness, conditional_write, monitoring_delivery, monitoring_attestation) {
|
|
181
|
+
async function runEnforced(config, factory, approved, redacted, freshness, conditional_write, monitoring_delivery, monitoring_attestation, grantCtx, grantObs) {
|
|
171
182
|
emit(config, { type: 'execution_started', at: iso(), action: approved.action, decisionId: approved.envelope.decision_id });
|
|
172
183
|
try {
|
|
173
|
-
const result =
|
|
184
|
+
const result = grantCtx
|
|
185
|
+
? await factory(approved.envelope, redacted, grantCtx)
|
|
186
|
+
: await factory(approved.envelope, redacted);
|
|
174
187
|
const base = { executionAttempted: true, executed: true, enforced: true, result, verdict: approved, preflighted: true };
|
|
175
|
-
return finishExecuted(config, base, freshness, conditional_write, redacted, result, monitoring_delivery, monitoring_attestation);
|
|
188
|
+
return finishExecuted(config, base, freshness, conditional_write, redacted, result, monitoring_delivery, monitoring_attestation, grantObs);
|
|
176
189
|
}
|
|
177
190
|
catch (error) {
|
|
178
191
|
emit(config, { type: 'factory_error', at: iso(), action: approved.action });
|
|
179
192
|
const base = { executionAttempted: true, executed: false, enforced: true, error, verdict: approved, preflighted: true };
|
|
180
|
-
return finishExecuted(config, base, freshness, conditional_write, redacted, undefined, monitoring_delivery, monitoring_attestation);
|
|
193
|
+
return finishExecuted(config, base, freshness, conditional_write, redacted, undefined, monitoring_delivery, monitoring_attestation, grantObs);
|
|
181
194
|
}
|
|
182
195
|
}
|
|
183
|
-
async function runUnenforced(config, factory, envelope, verdict, preflighted, redacted, freshness, conditional_write, monitoring_delivery, monitoring_attestation) {
|
|
196
|
+
async function runUnenforced(config, factory, envelope, verdict, preflighted, redacted, freshness, conditional_write, monitoring_delivery, monitoring_attestation, grantCtx, grantObs) {
|
|
184
197
|
emit(config, { type: 'execution_started', at: iso() });
|
|
185
198
|
try {
|
|
186
|
-
const result =
|
|
199
|
+
const result = grantCtx
|
|
200
|
+
? await factory(envelope, redacted, grantCtx)
|
|
201
|
+
: await factory(envelope, redacted);
|
|
187
202
|
const base = { executionAttempted: true, executed: true, enforced: false, result, verdict, preflighted };
|
|
188
|
-
return finishExecuted(config, base, freshness, conditional_write, redacted, result, monitoring_delivery, monitoring_attestation);
|
|
203
|
+
return finishExecuted(config, base, freshness, conditional_write, redacted, result, monitoring_delivery, monitoring_attestation, grantObs);
|
|
189
204
|
}
|
|
190
205
|
catch (error) {
|
|
191
206
|
emit(config, { type: 'factory_error', at: iso() });
|
|
192
207
|
const base = { executionAttempted: true, executed: false, enforced: false, error, verdict, preflighted };
|
|
193
|
-
return finishExecuted(config, base, freshness, conditional_write, redacted, undefined, monitoring_delivery, monitoring_attestation);
|
|
208
|
+
return finishExecuted(config, base, freshness, conditional_write, redacted, undefined, monitoring_delivery, monitoring_attestation, grantObs);
|
|
194
209
|
}
|
|
195
210
|
}
|
|
196
211
|
function attachPolicyPresence(outcome, config) {
|
|
@@ -207,7 +222,7 @@ function coverageSnap(config) {
|
|
|
207
222
|
function coverageOutcomeFieldsFrom(s) {
|
|
208
223
|
return s.coverageObserved ? { coverage_observed: s.coverageObserved } : {};
|
|
209
224
|
}
|
|
210
|
-
function blocked(config, verdict, preflighted, freshness, conditional_write, monitoring_delivery, monitoring_attestation) {
|
|
225
|
+
function blocked(config, verdict, preflighted, freshness, conditional_write, monitoring_delivery, monitoring_attestation, grantObs) {
|
|
211
226
|
const commit_observation = {
|
|
212
227
|
status: 'not_observed', observed_at: iso(), host_attestation: 'absent',
|
|
213
228
|
};
|
|
@@ -229,10 +244,11 @@ function blocked(config, verdict, preflighted, freshness, conditional_write, mon
|
|
|
229
244
|
...(monitoring_delivery ? { monitoring_delivery } : {}),
|
|
230
245
|
...(monitoring_attestation ? { monitoring_attestation } : {}),
|
|
231
246
|
...coverageOutcomeFieldsFrom(cov),
|
|
247
|
+
...(grantObs ? { execution_grant: grantObs } : {}),
|
|
232
248
|
};
|
|
233
249
|
return attachPolicyPresence(out, config);
|
|
234
250
|
}
|
|
235
|
-
async function finishExecuted(config, base, freshness, conditional_write, redacted, result, monitoring_delivery, monitoring_attestation) {
|
|
251
|
+
async function finishExecuted(config, base, freshness, conditional_write, redacted, result, monitoring_delivery, monitoring_attestation, grantObs) {
|
|
236
252
|
const enabled = config.requireCommitObservation !== false;
|
|
237
253
|
const commit_observation = await (0, commit_observation_js_1.observeCommit)({
|
|
238
254
|
enabled,
|
|
@@ -307,6 +323,7 @@ async function finishExecuted(config, base, freshness, conditional_write, redact
|
|
|
307
323
|
: {}),
|
|
308
324
|
} : {}),
|
|
309
325
|
...coverageOutcomeFieldsFrom(cov),
|
|
326
|
+
...(grantObs ? { execution_grant: grantObs } : {}),
|
|
310
327
|
};
|
|
311
328
|
return attachPolicyPresence(out, config);
|
|
312
329
|
}
|
|
@@ -459,17 +476,33 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
459
476
|
// previous_receipt: host-supplied only (GuardConfig.previousReceipt). Never hardcoded-undefined
|
|
460
477
|
// forever — when the host provides a prior token (string or getter), thread it so the issuer can
|
|
461
478
|
// hash it into the signed `prev` slot. The guard does not retain or advance the value.
|
|
479
|
+
//
|
|
480
|
+
// executionGrant (9.6.0): per-invocation locals — never stored on GuardConfig (shared across
|
|
481
|
+
// overlapping calls). Default OFF is this object without include_execution_grant (9.5.0 shape).
|
|
462
482
|
const request = {
|
|
463
483
|
artifacts: detection.artifacts,
|
|
464
484
|
context: { operation: config.operation ?? 'tool_call', environment: config.environment, audience: config.audience },
|
|
465
485
|
previous_receipt: resolvePreviousReceipt(config),
|
|
466
486
|
idempotency_key: undefined,
|
|
467
487
|
};
|
|
488
|
+
let grantObs;
|
|
489
|
+
let grantForCall = null;
|
|
490
|
+
if ((0, execution_grant_js_1.isExecutionGrantEnabled)(config)) {
|
|
491
|
+
grantObs = { requested: true, arrived: false };
|
|
492
|
+
const nonceRes = await (0, execution_grant_js_1.resolveStateNonceForCall)(config, redacted, detection.artifacts);
|
|
493
|
+
if (!nonceRes.ok) {
|
|
494
|
+
breakerRecord(config);
|
|
495
|
+
return closedIntegrity(config, 'EXECUTION_GRANT_NONCE_UNRESOLVABLE', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
496
|
+
}
|
|
497
|
+
request.include_execution_grant = true;
|
|
498
|
+
if (nonceRes.nonce)
|
|
499
|
+
request.state_nonce = nonceRes.nonce;
|
|
500
|
+
}
|
|
468
501
|
// request-attributable payload cap => PAYLOAD_TOO_LARGE (integrity) => closed.
|
|
469
502
|
const cap = config.maxPayloadBytes ?? 1_000_000;
|
|
470
503
|
if (Buffer.byteLength(JSON.stringify(request), 'utf8') > cap) {
|
|
471
504
|
breakerRecord(config);
|
|
472
|
-
return closedIntegrity(config, 'PAYLOAD_TOO_LARGE', failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
505
|
+
return closedIntegrity(config, 'PAYLOAD_TOO_LARGE', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
473
506
|
}
|
|
474
507
|
emit(config, { type: 'preflight_start', at: iso() });
|
|
475
508
|
const pf = await preflightWithRetry(config, request);
|
|
@@ -482,14 +515,22 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
482
515
|
if (pf.integrity) {
|
|
483
516
|
emit(config, { type: 'breaker_tripped', at: iso(), cause: pf.cause });
|
|
484
517
|
const v = unavailableVerdict({ cause: pf.cause, failPolicy, resolution: 'CLOSED', action: 'STOP' }, count);
|
|
485
|
-
return blocked(config, v, false, basis, cw);
|
|
518
|
+
return blocked(config, v, false, basis, cw, undefined, undefined, grantObs);
|
|
486
519
|
}
|
|
487
520
|
// availability
|
|
488
521
|
const availCause = pf.cause;
|
|
522
|
+
// A grant was requested for THIS call: never OPEN_PASSTHROUGH / LKG-execute without a token.
|
|
523
|
+
// failPolicy 'open' remains the 9.5.0 opt-in only when the grant path is off.
|
|
524
|
+
if (grantObs && grantObs.requested) {
|
|
525
|
+
if (breakerTripped(config))
|
|
526
|
+
emit(config, { type: 'breaker_tripped', at: iso(), cause: availCause });
|
|
527
|
+
const v = unavailableVerdict({ cause: availCause, failPolicy, resolution: 'CLOSED', action: 'STOP' }, count);
|
|
528
|
+
return blocked(config, v, false, basis, cw, undefined, undefined, grantObs);
|
|
529
|
+
}
|
|
489
530
|
if (failPolicy === 'open' && !breakerTripped(config)) {
|
|
490
531
|
emit(config, { type: 'preflight_unavailable', at: iso(), cause: availCause, action: 'CONTINUE' });
|
|
491
532
|
const v = unavailableVerdict({ cause: availCause, failPolicy: 'open', resolution: 'OPEN_PASSTHROUGH', action: 'CONTINUE' }, count);
|
|
492
|
-
return runUnenforced(config, executeFactory, null, v, false, redacted, basis, cw);
|
|
533
|
+
return runUnenforced(config, executeFactory, null, v, false, redacted, basis, cw, undefined, undefined, undefined, grantObs);
|
|
493
534
|
}
|
|
494
535
|
if (failPolicy === 'lkg') {
|
|
495
536
|
const lkg = await tryLkg(config, inputFp);
|
|
@@ -503,31 +544,31 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
503
544
|
if (breakerTripped(config))
|
|
504
545
|
emit(config, { type: 'breaker_tripped', at: iso(), cause: availCause });
|
|
505
546
|
const v = unavailableVerdict({ cause: availCause, failPolicy, resolution: 'CLOSED', action: 'STOP' }, count);
|
|
506
|
-
return blocked(config, v, false, basis, cw);
|
|
547
|
+
return blocked(config, v, false, basis, cw, undefined, undefined, grantObs);
|
|
507
548
|
}
|
|
508
549
|
// We have a response. Read the decision (envelope-first) and verify the receipt.
|
|
509
550
|
const rd = (0, read_decision_js_1.readDecision)(pf.response);
|
|
510
551
|
// PRESENT but unrecognised action — halt with its own code before any decision map or reconcile.
|
|
511
552
|
if (rd.reason === 'EXECUTION_ACTION_UNRECOGNISED') {
|
|
512
553
|
breakerRecord(config);
|
|
513
|
-
return closedIntegrity(config, 'EXECUTION_ACTION_UNRECOGNISED', failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
554
|
+
return closedIntegrity(config, 'EXECUTION_ACTION_UNRECOGNISED', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
514
555
|
}
|
|
515
556
|
// Missing EA on v2 / non-legacy-1.0 — reuse closedIntegrity halt arm, cause UNREADABLE_DECISION.
|
|
516
557
|
// executed:false, enforced:false (blocked()). Never remap to CONTINUE.
|
|
517
558
|
if (rd.reason === 'UNREADABLE_DECISION') {
|
|
518
559
|
breakerRecord(config);
|
|
519
|
-
return closedIntegrity(config, 'UNREADABLE_DECISION', failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
560
|
+
return closedIntegrity(config, 'UNREADABLE_DECISION', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
520
561
|
}
|
|
521
562
|
if (!rd.envelope) {
|
|
522
563
|
// Known action but no envelope => integrity => closed.
|
|
523
564
|
breakerRecord(config);
|
|
524
|
-
return closedIntegrity(config, 'SCHEMA_INVALID', failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
565
|
+
return closedIntegrity(config, 'SCHEMA_INVALID', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
525
566
|
}
|
|
526
567
|
const envelope = rd.envelope;
|
|
527
568
|
// Past unrecognised early-return: action is a closed ExecutionAction (or we treat non-closed as integrity).
|
|
528
569
|
if (!(0, read_decision_js_1.isClosedAction)(rd.executionAction)) {
|
|
529
570
|
breakerRecord(config);
|
|
530
|
-
return closedIntegrity(config, 'EXECUTION_ACTION_UNRECOGNISED', failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
571
|
+
return closedIntegrity(config, 'EXECUTION_ACTION_UNRECOGNISED', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
531
572
|
}
|
|
532
573
|
const closedAction = rd.executionAction;
|
|
533
574
|
// 5. expiry check before honoring any decision.
|
|
@@ -544,7 +585,7 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
544
585
|
// signature. Both trip the breaker and STOP — the guard NEVER executes off an unbound receipt.
|
|
545
586
|
if (config.verifyReceipts !== false && !receiptVerified && envelope.receipt?.token) {
|
|
546
587
|
breakerRecord(config);
|
|
547
|
-
return closedIntegrity(config, bindResult.cause ?? 'RECEIPT_UNVERIFIED', failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
588
|
+
return closedIntegrity(config, bindResult.cause ?? 'RECEIPT_UNVERIFIED', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
548
589
|
}
|
|
549
590
|
// ── Client-side authorization gate (P0-b/c): mirror §106/§111/§115 before honoring any action. ──
|
|
550
591
|
// Threat model (bounded): without a receipt, fields including execution_action are not
|
|
@@ -557,21 +598,34 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
557
598
|
const gate = (0, enforcement_gate_js_1.evaluateEnvelope)(pf.response, envelope, closedAction, detection.artifacts);
|
|
558
599
|
if (gate.verdict === 'fail-closed') {
|
|
559
600
|
breakerRecord(config);
|
|
560
|
-
return closedIntegrity(config, gate.cause, failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
601
|
+
return closedIntegrity(config, gate.cause, failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
561
602
|
}
|
|
562
603
|
if (gate.verdict === 'block-strict') {
|
|
563
604
|
// A real (or stricter-reconciled) BLOCK / REQUIRE_APPROVAL — clean block; the factory never runs.
|
|
564
605
|
const { basis } = freshnessFor(config, redacted, fctx, detection.artifacts);
|
|
565
606
|
const { basis: cw } = conditionalWriteFor(config, redacted, cwctx);
|
|
566
607
|
return gate.decision === 'BLOCK'
|
|
567
|
-
? blocked(config, { kind: 'BLOCK', action: 'STOP', envelope, receiptVerified }, true, basis, cw)
|
|
568
|
-
: blocked(config, { kind: 'APPROVAL', action: 'REQUEST_APPROVAL', envelope, receiptVerified }, true, basis, cw);
|
|
608
|
+
? blocked(config, { kind: 'BLOCK', action: 'STOP', envelope, receiptVerified }, true, basis, cw, undefined, undefined, grantObs)
|
|
609
|
+
: blocked(config, { kind: 'APPROVAL', action: 'REQUEST_APPROVAL', envelope, receiptVerified }, true, basis, cw, undefined, undefined, grantObs);
|
|
569
610
|
}
|
|
570
611
|
const kind = gate.kind; // 'ALLOW' | 'MONITOR' — allow-class, safe, non-degraded, artifact-bound.
|
|
612
|
+
// Native grant: allow-class authorize that requested a grant must RECEIVE one.
|
|
613
|
+
// BLOCK/RA never mint a grant — missing token there is not EXECUTION_GRANT_MISSING.
|
|
614
|
+
if (grantObs && grantObs.requested) {
|
|
615
|
+
grantForCall = (0, execution_grant_js_1.readExecutionGrantToken)(pf.response);
|
|
616
|
+
grantObs = { requested: true, arrived: !!grantForCall };
|
|
617
|
+
if (!grantForCall) {
|
|
618
|
+
breakerRecord(config);
|
|
619
|
+
return closedIntegrity(config, 'EXECUTION_GRANT_MISSING', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const grantCtx = grantObs
|
|
623
|
+
? { execution_grant: grantForCall }
|
|
624
|
+
: undefined;
|
|
571
625
|
// An expired decision cannot be honored fresh => closed (integrity: wrong-time state).
|
|
572
626
|
if (expired) {
|
|
573
627
|
breakerRecord(config);
|
|
574
|
-
return closedIntegrity(config, 'SCHEMA_INVALID', failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
628
|
+
return closedIntegrity(config, 'SCHEMA_INVALID', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
575
629
|
}
|
|
576
630
|
// MONITOR gate: host must ASSERT monitoring is wired (monitoringSinkWired === true) AND supply
|
|
577
631
|
// onEvent. Presence of a function alone is not enough — () => {} used to unlock this gate while
|
|
@@ -625,20 +679,20 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
625
679
|
});
|
|
626
680
|
if (sinkWired && monitoringDelivery.status === 'not_delivered' && (0, monitoring_delivery_js_1.monitoringDeliveryFailClosed)(config)) {
|
|
627
681
|
breakerRecord(config);
|
|
628
|
-
return closedIntegrity(config, 'MONITORING_UNWIRED', failPolicy, fctx, cwctx, redacted, detection.artifacts, monitoringDelivery, monitoringAttestation);
|
|
682
|
+
return closedIntegrity(config, 'MONITORING_UNWIRED', failPolicy, fctx, cwctx, redacted, detection.artifacts, monitoringDelivery, monitoringAttestation, grantObs);
|
|
629
683
|
}
|
|
630
684
|
}
|
|
631
685
|
// Freshness re-check immediately before any execution (ACTIVE assessment uses preflight befores).
|
|
632
686
|
const { basis: freshBasis, blockCause: freshBlock } = freshnessFor(config, redacted, fctx, detection.artifacts);
|
|
633
687
|
if (freshBlock === 'FRESHNESS_REQUIRED' || freshBlock === 'FRESHNESS_FAILED') {
|
|
634
688
|
breakerRecord(config);
|
|
635
|
-
return closedIntegrity(config, freshBlock, failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
689
|
+
return closedIntegrity(config, freshBlock, failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
636
690
|
}
|
|
637
691
|
// Conditional-write re-check immediately before enforced execution (same conjunct class as freshness).
|
|
638
692
|
const { basis: cwBasis, blockCause: cwBlock } = conditionalWriteFor(config, redacted, cwctx);
|
|
639
693
|
if (cwBlock === 'CONDITIONAL_WRITE_REQUIRED') {
|
|
640
694
|
breakerRecord(config);
|
|
641
|
-
return closedIntegrity(config, cwBlock, failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
695
|
+
return closedIntegrity(config, cwBlock, failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
642
696
|
}
|
|
643
697
|
// observeOnly is an EXPLICIT report-only opt-in: execute but never enforce (the one sanctioned
|
|
644
698
|
// unenforced-execution mode alongside failPolicy=open; both are documented opt-ins, not the default).
|
|
@@ -647,14 +701,14 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
647
701
|
const verdict = kind === 'ALLOW'
|
|
648
702
|
? { kind: 'ALLOW', action: 'CONTINUE', envelope, receiptVerified }
|
|
649
703
|
: { kind: 'MONITOR', action: 'CONTINUE_WITH_MONITORING', envelope, receiptVerified };
|
|
650
|
-
return runUnenforced(config, executeFactory, envelope, verdict, true, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation);
|
|
704
|
+
return runUnenforced(config, executeFactory, envelope, verdict, true, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation, grantCtx, grantObs);
|
|
651
705
|
}
|
|
652
706
|
// Advisory CWM (sink WAS wired): delivery failed but failPolicy/observeOnly said not to block.
|
|
653
707
|
// Proceed unenforced with the reason visible. Unwired MONITOR still falls through to
|
|
654
708
|
// MONITORING_UNWIRED (CE-CC-04). ENFORCING not_delivered already returned closedIntegrity.
|
|
655
709
|
if (kind === 'MONITOR' && sinkWired && monitoringDelivery && monitoringDelivery.status === 'not_delivered') {
|
|
656
710
|
const degraded = { kind: 'MONITOR', action: 'CONTINUE_WITH_MONITORING', envelope, receiptVerified };
|
|
657
|
-
return runUnenforced(config, executeFactory, envelope, degraded, true, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation);
|
|
711
|
+
return runUnenforced(config, executeFactory, envelope, degraded, true, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation, grantCtx, grantObs);
|
|
658
712
|
}
|
|
659
713
|
// ── enforced ⟺ executed INVARIANT (contract-triggering path): execute ONLY when we can ENFORCE. ──
|
|
660
714
|
// enforceable = a bound-verified receipt AND (MONITOR) a wired sink. Anything else FAILS CLOSED —
|
|
@@ -683,7 +737,7 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
683
737
|
const offVerdict = kind === 'ALLOW'
|
|
684
738
|
? { kind: 'ALLOW', action: 'CONTINUE', envelope, receiptVerified }
|
|
685
739
|
: { kind: 'MONITOR', action: 'CONTINUE_WITH_MONITORING', envelope, receiptVerified };
|
|
686
|
-
return runUnenforced(config, executeFactory, envelope, offVerdict, true, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation);
|
|
740
|
+
return runUnenforced(config, executeFactory, envelope, offVerdict, true, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation, grantCtx, grantObs);
|
|
687
741
|
}
|
|
688
742
|
const et = (0, execution_time_fingerprint_js_1.checkExecutionTimeFingerprint)({
|
|
689
743
|
artifacts: detection.artifacts,
|
|
@@ -697,7 +751,7 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
697
751
|
const unmeasurable = (0, execution_time_fingerprint_js_1.isUnmeasurableExecutionStateReason)(et.reason);
|
|
698
752
|
if (execStateMode === true) {
|
|
699
753
|
breakerRecord(config);
|
|
700
|
-
return closedIntegrity(config, unmeasurable ? 'EXECUTION_STATE_UNMEASURABLE' : 'EXECUTION_STATE_DRIFT', failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
754
|
+
return closedIntegrity(config, unmeasurable ? 'EXECUTION_STATE_UNMEASURABLE' : 'EXECUTION_STATE_DRIFT', failPolicy, fctx, cwctx, redacted, detection.artifacts, undefined, undefined, grantObs);
|
|
701
755
|
}
|
|
702
756
|
// warn opt-down: emit, then run unenforced — a measured mismatch is not an enforced run.
|
|
703
757
|
if (unmeasurable) {
|
|
@@ -724,23 +778,23 @@ async function guardToolCall(call, executeFactory, config, callContext) {
|
|
|
724
778
|
const warnVerdict = kind === 'ALLOW'
|
|
725
779
|
? { kind: 'ALLOW', action: 'CONTINUE', envelope, receiptVerified }
|
|
726
780
|
: { kind: 'MONITOR', action: 'CONTINUE_WITH_MONITORING', envelope, receiptVerified };
|
|
727
|
-
return runUnenforced(config, executeFactory, envelope, warnVerdict, true, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation);
|
|
781
|
+
return runUnenforced(config, executeFactory, envelope, warnVerdict, true, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation, grantCtx, grantObs);
|
|
728
782
|
}
|
|
729
783
|
const approved = kind === 'ALLOW'
|
|
730
784
|
? { kind: 'ALLOW', action: 'CONTINUE', envelope, receiptVerified: true }
|
|
731
785
|
: { kind: 'MONITOR', action: 'CONTINUE_WITH_MONITORING', envelope, receiptVerified: true };
|
|
732
|
-
return runEnforced(config, executeFactory, approved, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation);
|
|
786
|
+
return runEnforced(config, executeFactory, approved, redacted, freshBasis, cwBasis, monitoringDelivery, monitoringAttestation, grantCtx, grantObs);
|
|
733
787
|
}
|
|
734
788
|
breakerRecord(config);
|
|
735
|
-
return closedIntegrity(config, receiptVerified ? 'MONITORING_UNWIRED' : 'RECEIPT_MISSING', failPolicy, fctx, cwctx, redacted, detection.artifacts, monitoringDelivery, monitoringAttestation);
|
|
789
|
+
return closedIntegrity(config, receiptVerified ? 'MONITORING_UNWIRED' : 'RECEIPT_MISSING', failPolicy, fctx, cwctx, redacted, detection.artifacts, monitoringDelivery, monitoringAttestation, grantObs);
|
|
736
790
|
}
|
|
737
|
-
function closedIntegrity(config, cause, failPolicy, fctx, cwctx, redacted, arts, monitoring_delivery, monitoring_attestation) {
|
|
791
|
+
function closedIntegrity(config, cause, failPolicy, fctx, cwctx, redacted, arts, monitoring_delivery, monitoring_attestation, grantObs) {
|
|
738
792
|
const count = (breakers.get(config)?.fails.length) ?? 1;
|
|
739
793
|
emit(config, { type: 'breaker_tripped', at: iso(), cause });
|
|
740
794
|
const v = unavailableVerdict({ cause, failPolicy, resolution: 'CLOSED', action: 'STOP' }, count);
|
|
741
795
|
const { basis } = freshnessFor(config, redacted, fctx, arts);
|
|
742
796
|
const { basis: cw } = conditionalWriteFor(config, redacted, cwctx);
|
|
743
|
-
return blocked(config, v, false, basis, cw, monitoring_delivery, monitoring_attestation);
|
|
797
|
+
return blocked(config, v, false, basis, cw, monitoring_delivery, monitoring_attestation, grantObs);
|
|
744
798
|
}
|
|
745
799
|
function isExpired(envelope) {
|
|
746
800
|
const exp = envelope.expires_at;
|