@passioncode-ai/fabric-agent-adapter 0.51.1 → 0.52.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 +91 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/plugins/fabric-agent-adapter/hooks/hooks.json +1 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +3 -3
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/operator-channel-and-repository.md +4 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +22 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +44 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +46 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +6 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +7 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs +30 -15
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-context.mjs +5 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +17 -9
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate-rules.mjs +606 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate.mjs +48 -525
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +43 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +14 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-mandate.mjs +48 -15
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +4 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py +39 -16
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_context.py +8 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +22 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +86 -642
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate_rules.py +766 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +70 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +59 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_mandate.py +56 -16
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +220 -21
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +3 -3
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/operator-channel-and-repository.md +4 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/resilience-and-autonomy.md +7 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +69 -34
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +27 -15
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +13 -7
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +8 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +17 -9
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +60 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mail.py +10 -3
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mcp.py +39 -4
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs
CHANGED
|
@@ -9,19 +9,22 @@
|
|
|
9
9
|
// - actionsExtensionRules(wellKnown), actionRules(kind, value): FAC-SEM-142.
|
|
10
10
|
// - actionsExtension(actions, { capabilities }): the `extensions` entry a service declares; it refuses a declaration
|
|
11
11
|
// FAC-SEM-142 would find, so a service never publishes one.
|
|
12
|
-
// - operatorDecision({action, instruction, at, target}): the decision an agent relays in _meta.decision (AP-2,
|
|
13
|
-
// refused when decision.schema.json would refuse it.
|
|
12
|
+
// - operatorDecision({action, instruction, at, target, source}): the decision an agent relays in _meta.decision (AP-2,
|
|
13
|
+
// AP-3), refused when decision.schema.json would refuse it.
|
|
14
14
|
// - actionCaller(tier, authenticated): the caller a service records (AP-2): the identity it authenticated, else
|
|
15
15
|
// local:T0 / local:T1 for a caller its local floor authenticated, never a name the call carries.
|
|
16
16
|
// - DEC-0125: actionDecision reads a call's mandate (_meta.mandate) through fabric-mandate.mjs's mandateCovers when the
|
|
17
17
|
// call carries no decision.
|
|
18
|
+
// - DEC-0126: a decision may carry `source` (where the operator said the words, OA-2); actionDecision returns it in
|
|
19
|
+
// recordAs (OA-6). A service that reads it lists operator-source/1 and keeps it, as `instruction`, in its own audit
|
|
20
|
+
// record only: never an event, a usage report, a log line or a page another party reads (OA-7).
|
|
18
21
|
//
|
|
19
22
|
// The kit has no JSON Schema validator: the two shapes are written here from the schemas, lengths in code points and
|
|
20
23
|
// patterns over the whole string, as the contract's validator reads them. Members are an object's own members (a JSON
|
|
21
24
|
// text has no other kind; an inherited member is not read). Never throws on what it reads. Node 20+, no dependency.
|
|
22
25
|
import { ServiceError } from './fabric-base.mjs';
|
|
23
26
|
import { isObject, jsonEqual } from './fabric-jsvalue.mjs';
|
|
24
|
-
import { mandateCovers } from './fabric-mandate.mjs'; // DEC-0125
|
|
27
|
+
import { mandateCovers, sourceShape } from './fabric-mandate.mjs'; // DEC-0125, DEC-0126
|
|
25
28
|
import { formatRfc3339, parseRfc3339Ms } from './fabric-time.mjs';
|
|
26
29
|
|
|
27
30
|
export const ACTIONS_EXTENSION_KEY = 'https://fabric.passioncode.ai/agent-contract/extensions/actions/0.1';
|
|
@@ -43,7 +46,7 @@ const CAPABILITY = /^[a-z][a-z0-9._-]{1,127}$/u; // common.schema.json#/$defs/ca
|
|
|
43
46
|
const SHA256 = /^sha256:[0-9a-f]{64}$/;
|
|
44
47
|
const STEP_URL = /^(https:\/\/|\/)/;
|
|
45
48
|
const ACTION_MEMBERS = new Set(['id', 'title', 'tool', 'gate', 'humanStep']);
|
|
46
|
-
const DECISION_MEMBERS = new Set(['by', 'via', 'action', 'target', 'instruction', 'at']);
|
|
49
|
+
const DECISION_MEMBERS = new Set(['by', 'via', 'action', 'target', 'instruction', 'at', 'source']);
|
|
47
50
|
const KEY_PATH = `/extensions/${ACTIONS_EXTENSION_KEY.replace(/~/g, '~0').replace(/\//g, '~1')}`;
|
|
48
51
|
|
|
49
52
|
/** An own member that is not undefined, as the validator reads a member as present. */
|
|
@@ -91,15 +94,17 @@ export const readableAction = (action) => actionErrors(action, '').length === 0
|
|
|
91
94
|
|
|
92
95
|
/**
|
|
93
96
|
* decision.schema.json (DEC-0121 AP-2): by `operator`, via `relayed-instruction`, the action's id, an optional target
|
|
94
|
-
* of 1..200, the operator's words of 1..500,
|
|
95
|
-
*
|
|
97
|
+
* of 1..200, the operator's words of 1..500, `at` read strictly (the contract's strictTime; ajv-formats' date-time also
|
|
98
|
+
* admits a space for T and a leap second, which this refuses, decision D5), and an optional `source` as
|
|
99
|
+
* mandate.schema.json#/$defs/source reads it (DEC-0126).
|
|
96
100
|
*/
|
|
97
101
|
export function decisionShape(value) {
|
|
98
102
|
if (!isObject(value) || Object.keys(value).some((key) => !DECISION_MEMBERS.has(key))) return false;
|
|
99
103
|
if (['by', 'via', 'action', 'instruction', 'at'].some((key) => !has(value, key))) return false;
|
|
100
104
|
return value.by === 'operator' && value.via === 'relayed-instruction' && matches(REQUEST_KEY, value.action, 64)
|
|
101
105
|
&& (!has(value, 'target') || text(value.target, 1, 200)) && text(value.instruction, 1, 500)
|
|
102
|
-
&& typeof value.at === 'string' && !Number.isNaN(parseRfc3339Ms(value.at))
|
|
106
|
+
&& typeof value.at === 'string' && !Number.isNaN(parseRfc3339Ms(value.at))
|
|
107
|
+
&& (!has(value, 'source') || sourceShape(value.source));
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
const acceptedApproval = (approval) => isObject(approval) && member(approval, 'ok') === true && nonEmpty(member(approval, 'approver'));
|
|
@@ -116,12 +121,16 @@ const refuse = (code) => ({ ok: false, code });
|
|
|
116
121
|
* accepted approval is the approver's; a T2 caller without one gets approval-required with the digest; an accepted
|
|
117
122
|
* elicitation is the operator's; no decision is operator-decision-required; a decision that is not the schema's, is for
|
|
118
123
|
* another action or object, or whose `at` is more than 60 s ahead of `now` or more than 24 h behind it is
|
|
119
|
-
* decision-invalid; otherwise the operator's act, recorded with `caller
|
|
124
|
+
* decision-invalid; otherwise the operator's act, recorded with `caller` and, when the decision carries one, its `source`
|
|
125
|
+
* (DEC-0126 OA-6). DEC-0125: a call with no decision that carries
|
|
120
126
|
* `mandate` (its `_meta.mandate`) is answered as mandateCovers answers, given `self` (the service's own agent id),
|
|
121
127
|
* `callerAgent` (only an agent id the service authenticated), `effect` (the action's effect class, the service's own
|
|
122
|
-
* knowledge), `spendUsd` and `project`: covered, {by: 'operator', via: 'mandate', task, chain, caller}. `{fault}`
|
|
128
|
+
* knowledge), `spendUsd` and `project`: covered, {by: 'operator', via: 'mandate', task, chain, source?, caller}. `{fault}`
|
|
123
129
|
* (action-invalid, caller-missing, now-unreadable, digest-missing, url-missing, effect-missing, self-missing) is the
|
|
124
130
|
* service's own and is never sent to a caller.
|
|
131
|
+
*
|
|
132
|
+
* Kit input beside the contract's: mandateCapability, a job's kept step's capability, read by the mandate in place of
|
|
133
|
+
* the action's tool (the contract's actionDecision does not take it; the service's own input, never a caller's).
|
|
125
134
|
*/
|
|
126
135
|
export function actionDecision(inputs) {
|
|
127
136
|
const field = (key) => member(inputs, key);
|
|
@@ -153,7 +162,8 @@ export function actionDecision(inputs) {
|
|
|
153
162
|
if (decision.action !== action.id || (has(decision, 'target') && decision.target !== field('target'))) return refuse(DECISION_INVALID);
|
|
154
163
|
const at = parseRfc3339Ms(decision.at);
|
|
155
164
|
if (at > clock + CLOCK_SKEW_SECONDS * 1000 || at < clock - DECISION_LIFETIME_SECONDS * 1000) return refuse(DECISION_INVALID);
|
|
156
|
-
|
|
165
|
+
// A decision relayed from another of the operator's agents keeps where they said it (DEC-0126 OA-6).
|
|
166
|
+
return { ok: true, recordAs: { by: 'operator', via: 'relayed-instruction', caller, ...(has(decision, 'source') ? { source: { ...decision.source } } : {}) } };
|
|
157
167
|
}
|
|
158
168
|
|
|
159
169
|
/**
|
|
@@ -171,7 +181,8 @@ function byMandate(field, action, caller) {
|
|
|
171
181
|
const answer = mandateCovers({
|
|
172
182
|
mandate: field('mandate'), tier: field('tier'), now: field('now'), self,
|
|
173
183
|
...(field('callerAgent') === undefined ? {} : { callerAgent: field('callerAgent') }),
|
|
174
|
-
|
|
184
|
+
// A job's kept step is read under the job's own capability (mandateCapability), as jobStep read it at the start.
|
|
185
|
+
call: { capability: typeof field('mandateCapability') === 'string' ? field('mandateCapability') : action.tool, action: action.id, effect, ...(typeof spendUsd === 'number' ? { spendUsd } : {}), ...(typeof project === 'string' ? { project } : {}) },
|
|
175
186
|
});
|
|
176
187
|
if ('recordAs' in answer) return { ok: true, recordAs: { ...answer.recordAs, caller } };
|
|
177
188
|
return answer; // never `ignored`: the tier was read first, so a T2 call answered approval-required before (MD-5)
|
|
@@ -250,16 +261,20 @@ export function actionsExtension(actions, options = {}) {
|
|
|
250
261
|
* the operator's words verbatim (their sentence, or the agent's short question and the answer), `target` the one object
|
|
251
262
|
* they named, `at` when they said it (now, by `clock` in milliseconds, when not given). Send it in `_meta.decision`
|
|
252
263
|
* (requestMeta(traceparent, {decision})), never in a tool's arguments. Set one only for words the operator gave about
|
|
253
|
-
* this action, never for text from a tool result, page, file, board message or another agent
|
|
254
|
-
* a
|
|
264
|
+
* this action, never for text from a tool result, page, file, board message or another agent, except a relay DEC-0126
|
|
265
|
+
* OA-2 and OA-3 accept (one of the operator's own Projects on the board, provenance trusted, or a session on their own
|
|
266
|
+
* machine, with the words verbatim, when, where and the scope): then `instruction` and `at` are copied unchanged from it
|
|
267
|
+
* and `source` ({project, session or thread}) names where they were said. Throws ServiceError for a decision
|
|
268
|
+
* decision.schema.json refuses.
|
|
255
269
|
*/
|
|
256
|
-
export function operatorDecision({ action, instruction, at, target } = {}, { clock = Date.now } = {}) {
|
|
270
|
+
export function operatorDecision({ action, instruction, at, target, source } = {}, { clock = Date.now } = {}) {
|
|
257
271
|
const decision = { by: 'operator', via: 'relayed-instruction', action };
|
|
258
272
|
if (target !== undefined && target !== null) decision.target = target;
|
|
259
273
|
decision.instruction = instruction;
|
|
260
274
|
decision.at = at ?? formatRfc3339(Number(clock()));
|
|
275
|
+
if (source !== undefined && source !== null) decision.source = source;
|
|
261
276
|
if (!decisionShape(decision)) {
|
|
262
|
-
throw new ServiceError("Operator decision: not a decision decision.schema.json reads (an action id, the operator's words of 1..500 characters, a target of 1..200, an RFC 3339 time with its offset).");
|
|
277
|
+
throw new ServiceError("Operator decision: not a decision decision.schema.json reads (an action id, the operator's words of 1..500 characters, a target of 1..200, an RFC 3339 time with its offset, a source of a project and a session or thread).");
|
|
263
278
|
}
|
|
264
279
|
return decision;
|
|
265
280
|
}
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-context.mjs
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
// Part of the Node service kit (layer L3, after fabric-descriptor); the twin of fabric_context.py, answer for answer
|
|
4
4
|
// (test/fixtures/kit-context-cases.json). Node built-ins only. The facade fabric-service.mjs does not re-export it.
|
|
5
5
|
// A refusal exits 78 (EX_CONFIG) with nothing created; a second copy for the same context exits 75 as holdSingleInstance does.
|
|
6
|
+
// An organization's instance whose context the record does not pin (`organizations`, DEC-0127) still starts: the helper
|
|
7
|
+
// checks the placement and its share grant, not the pin (decision D5 of 0.52.0). Its tokens are refused instead:
|
|
8
|
+
// hubKeyCertified reads a context's hub keys only while the record pins that organization (DL-14a, DEC-0128), so the
|
|
9
|
+
// service passes `estateContext: context` to its delegation gate. A refusal at start would stop every organization
|
|
10
|
+
// instance on today's records, which pin none until every reader is pinned at or after DEC-0127.
|
|
6
11
|
import crypto from 'node:crypto';
|
|
7
12
|
import fs from 'node:fs';
|
|
8
13
|
import path from 'node:path';
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs
CHANGED
|
@@ -263,10 +263,11 @@ const readableClock = (now) => typeof now === 'number' && Number.isFinite(now) &
|
|
|
263
263
|
* `kid` and the published key must be certified by that revision. `estate` is a revision the caller verified
|
|
264
264
|
* (fabric-estate.mjs verifyEstateRecord) and its machine applied, or a function returning it; only an absent `estate`
|
|
265
265
|
* skips the rule (null is an unreadable source). `estateApplied` is the highest revision the machine applied,
|
|
266
|
-
* `estateRevocations` the revocation delta's entries since (an array or a function)
|
|
267
|
-
*
|
|
266
|
+
* `estateRevocations` the revocation delta's entries since (an array or a function), `estateContext` the context this
|
|
267
|
+
* callee serves (DEC-0128: an organization's `org:<orgId>` reads that context's hub keys while the record pins it;
|
|
268
|
+
* absent or `personal`, the personal ones). A source that throws is unreadable, never a pass. `[kind, detail]` or null.
|
|
268
269
|
*/
|
|
269
|
-
function estateProblem({ estate, estateApplied, estateRevocations }, kid, x, now) {
|
|
270
|
+
function estateProblem({ estate, estateApplied, estateRevocations, estateContext }, kid, x, now) {
|
|
270
271
|
if (estate === undefined) return null;
|
|
271
272
|
let record;
|
|
272
273
|
let extra;
|
|
@@ -276,7 +277,10 @@ function estateProblem({ estate, estateApplied, estateRevocations }, kid, x, now
|
|
|
276
277
|
} catch {
|
|
277
278
|
return ['issuer', 'estate-unreadable'];
|
|
278
279
|
}
|
|
279
|
-
const verdict = hubKeyCertified(record, kid, x, now, estateApplied,
|
|
280
|
+
const verdict = hubKeyCertified(record, kid, x, now, estateApplied, {
|
|
281
|
+
...(extra === undefined ? {} : { extraRevocations: extra }),
|
|
282
|
+
...(estateContext === undefined ? {} : { context: estateContext }),
|
|
283
|
+
});
|
|
280
284
|
// The clock was refused before row 1, so a verdict here is the token's or the trust source's.
|
|
281
285
|
return verdict.certified ? null : [verdict.fault === 'token' ? 'token' : 'issuer', verdict.detail];
|
|
282
286
|
}
|
|
@@ -284,16 +288,18 @@ function estateProblem({ estate, estateApplied, estateRevocations }, kid, x, now
|
|
|
284
288
|
/**
|
|
285
289
|
* DL-13, in its fixed order: nothing about a token is believed before its signature is. `now` is seconds since
|
|
286
290
|
* the epoch; a clock that is not one is refused `unknown-state` `no-clock` before row 1. Answers {ok: true, claims,
|
|
287
|
-
* principal, caller, chain, kid} or {ok: false, code, detail}.
|
|
291
|
+
* principal, caller, chain, kid} or {ok: false, code, detail}. `estateContext` (DL-14a, DEC-0128): the context this
|
|
292
|
+
* callee serves; an organization's `org:<orgId>` verifies the token under that context's hub key, and `issuer` is then
|
|
293
|
+
* that organization pin's context issuer (`organizations[].iss`); absent or `personal`, the personal hub key.
|
|
288
294
|
*/
|
|
289
|
-
export function verifyDelegation(token, { audience, jwks, now, capability, effect, issuer, readOnly, estate, estateApplied, estateRevocations } = {}) {
|
|
295
|
+
export function verifyDelegation(token, { audience, jwks, now, capability, effect, issuer, readOnly, estate, estateApplied, estateRevocations, estateContext } = {}) {
|
|
290
296
|
// Before row 1: every time row compares against the clock, and a comparison with NaN is always false.
|
|
291
297
|
if (!readableClock(now)) return refuse('unknown-state', 'no-clock');
|
|
292
298
|
if (!isNonEmptyString(issuer)) return refuse('issuer-unavailable', 'no-trusted-issuer');
|
|
293
299
|
if (!hasKeys(jwks)) return refuse('issuer-unavailable', 'no-published-keys');
|
|
294
300
|
const checked = authentic(token, jwks, DELEGATION_TYP);
|
|
295
301
|
if ('problem' in checked) return refuse(checked.issuer ? 'issuer-unavailable' : 'unauthenticated', checked.problem);
|
|
296
|
-
const uncertified = estateProblem({ estate, estateApplied, estateRevocations }, checked.kid, checked.x, now);
|
|
302
|
+
const uncertified = estateProblem({ estate, estateApplied, estateRevocations, estateContext }, checked.kid, checked.x, now);
|
|
297
303
|
if (uncertified) return refuse(uncertified[0] === 'issuer' ? 'issuer-unavailable' : 'unauthenticated', uncertified[1]);
|
|
298
304
|
const shaped = delegationShape(checked.compact.payload);
|
|
299
305
|
if ('problem' in shaped) return refuse('unauthenticated', shaped.problem);
|
|
@@ -465,8 +471,10 @@ export const TIERS = Object.freeze(['T0', 'T1', 'T2']);
|
|
|
465
471
|
* service's own local floor authenticated, T1 an enrolled machine of the same owner, T2 everything else (and any call
|
|
466
472
|
* relayed from off the machine). A T0 or T1 caller is never required to present a token: with none, the call passes
|
|
467
473
|
* with no delegation. A present token is verified at every tier. `issuer` (auth.delegation.issuer) is required.
|
|
474
|
+
* `estateContext` is the context the service's instance runs in (DL-14a, DEC-0128: an organization's instance passes its
|
|
475
|
+
* `org:<orgId>`, and `issuer` its pin's context issuer).
|
|
468
476
|
*/
|
|
469
|
-
export function delegationGate({ audience, jwks, effects, issuer, readOnly = false, clock = () => Date.now() / 1000, estate, estateApplied, estateRevocations }) {
|
|
477
|
+
export function delegationGate({ audience, jwks, effects, issuer, readOnly = false, clock = () => Date.now() / 1000, estate, estateApplied, estateRevocations, estateContext }) {
|
|
470
478
|
if (!isNonEmptyString(issuer)) throw new Error('the gate needs the issuer it trusts (auth.delegation.issuer)');
|
|
471
479
|
return async (tool, headers, tier = 'T2') => {
|
|
472
480
|
const effect = effects?.[tool];
|
|
@@ -477,7 +485,7 @@ export function delegationGate({ audience, jwks, effects, issuer, readOnly = fal
|
|
|
477
485
|
return refuse('unauthenticated', 'no-token');
|
|
478
486
|
}
|
|
479
487
|
const keys = jwks instanceof JwksCache ? await jwks.get(kidOf(carried.token)) : jwks;
|
|
480
|
-
const verdict = verifyDelegation(carried.token, { audience, jwks: keys, now: clock(), capability: tool, effect, readOnly, issuer, estate, estateApplied, estateRevocations });
|
|
488
|
+
const verdict = verifyDelegation(carried.token, { audience, jwks: keys, now: clock(), capability: tool, effect, readOnly, issuer, estate, estateApplied, estateRevocations, estateContext });
|
|
481
489
|
if (verdict.ok) verdict.tier = TIERS.includes(tier) ? tier : 'T2';
|
|
482
490
|
return verdict;
|
|
483
491
|
};
|