@loomwork/effect-runtime 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +65 -2
  4. package/dist/EffectRuntime.d.ts +121 -0
  5. package/dist/EffectRuntime.d.ts.map +1 -0
  6. package/dist/EffectRuntime.js +422 -0
  7. package/dist/EffectRuntime.js.map +1 -0
  8. package/dist/authz.d.ts +228 -0
  9. package/dist/authz.d.ts.map +1 -0
  10. package/dist/authz.js +281 -0
  11. package/dist/authz.js.map +1 -0
  12. package/dist/effects/A2AEffect.d.ts +14 -0
  13. package/dist/effects/A2AEffect.d.ts.map +1 -0
  14. package/dist/effects/A2AEffect.js +21 -0
  15. package/dist/effects/A2AEffect.js.map +1 -0
  16. package/dist/effects/HumanEffect.d.ts +13 -0
  17. package/dist/effects/HumanEffect.d.ts.map +1 -0
  18. package/dist/effects/HumanEffect.js +21 -0
  19. package/dist/effects/HumanEffect.js.map +1 -0
  20. package/dist/effects/LazyEffect.d.ts +10 -0
  21. package/dist/effects/LazyEffect.d.ts.map +1 -0
  22. package/dist/effects/LazyEffect.js +28 -0
  23. package/dist/effects/LazyEffect.js.map +1 -0
  24. package/dist/effects/LlmEffect.d.ts +27 -0
  25. package/dist/effects/LlmEffect.d.ts.map +1 -0
  26. package/dist/effects/LlmEffect.js +27 -0
  27. package/dist/effects/LlmEffect.js.map +1 -0
  28. package/dist/effects/ToolEffect.d.ts +38 -0
  29. package/dist/effects/ToolEffect.d.ts.map +1 -0
  30. package/dist/effects/ToolEffect.js +31 -0
  31. package/dist/effects/ToolEffect.js.map +1 -0
  32. package/dist/index.d.ts +12 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +30 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/policy.d.ts +76 -0
  37. package/dist/policy.d.ts.map +1 -0
  38. package/dist/policy.js +212 -0
  39. package/dist/policy.js.map +1 -0
  40. package/dist/types.d.ts +416 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +28 -0
  43. package/dist/types.js.map +1 -0
  44. package/package.json +56 -3
  45. package/src/EffectRuntime.ts +484 -0
  46. package/src/authz.ts +424 -0
  47. package/src/effects/HumanEffect.ts +25 -0
  48. package/src/effects/LazyEffect.ts +25 -0
  49. package/src/effects/LlmEffect.ts +39 -0
  50. package/src/effects/ToolEffect.ts +59 -0
  51. package/src/index.ts +48 -0
  52. package/src/policy.ts +296 -0
  53. package/src/types.ts +428 -0
@@ -0,0 +1,228 @@
1
+ /**
2
+ * K12.1 — an authenticated principal chain, populated at the trust boundary
3
+ * (A2A server / resume API) and propagated immutably through dispatch. The
4
+ * KERNEL CARRIES IT OPAQUELY and never interprets it — authorizers, budget
5
+ * scopes, and stores do. Keeping the kernel free of a tenant model is what keeps
6
+ * Loom a generic execution kernel (P10). Extra fields are allowed; a
7
+ * `delegation` (K12.4) rides here.
8
+ */
9
+ export interface PrincipalChain {
10
+ /** The tenant boundary — an opaque, pseudonymous id (never human PII; see K10). */
11
+ tenant?: string;
12
+ /** The acting identity. */
13
+ actor?: string;
14
+ /** The identity this action is performed on behalf of (delegation origin). */
15
+ onBehalfOf?: string;
16
+ /** K12.4 — a verifiable attenuated-delegation token, checked at the callee. */
17
+ delegation?: DelegatedGrant;
18
+ /**
19
+ * R3k (ADR 0111 D2/D4) — set ONLY by the kernel's principal resolver, never by a caller: the
20
+ * execution's recorded principal carried a delegation (this is its digest id) that no codec or
21
+ * resolver could restore. The effect seam refuses every effect of such a principal BEFORE any
22
+ * authorizer is consulted ({@link principalBlocked}); it never falls back to a broader identity.
23
+ */
24
+ delegationUnresolved?: string;
25
+ /** R3k — the recorded principal was crypto-shredded: every effect is denied (never the resumer's). */
26
+ principalShredded?: true;
27
+ /** R3k — the recorded principal could not be rehydrated (a `ref` with no codec, an unknown record version). */
28
+ principalUnresolved?: string;
29
+ [key: string]: unknown;
30
+ }
31
+ /**
32
+ * R3k (ADR 0111 D4) — why the KERNEL refuses every effect of this principal before any
33
+ * authorizer is asked, or `undefined` when it does not. A `PolicyAuthorizer`, a `GrantTable` or a
34
+ * 3P authorizer would otherwise judge a chain whose delegation could not be restored on its own
35
+ * terms (an unattenuated child after a crash is the privilege escalation this closes).
36
+ */
37
+ export declare function principalBlocked(principal: PrincipalChain | undefined): string | undefined;
38
+ export interface CapabilityRequest {
39
+ toolId: string;
40
+ /**
41
+ * R12 — the sub-resource this call targets, when the effect declares one
42
+ * (`Effect.resourceOf`): e.g. `"<server>/<tool>"` for `mcp.invoke`. A grant naming
43
+ * {@link capabilityIds | `<toolId>:<resource>`} authorizes exactly that resource; a grant on
44
+ * the bare `toolId` still authorizes every resource of the effect (unchanged behaviour).
45
+ */
46
+ resource?: string;
47
+ executionId: string;
48
+ workflowId: string;
49
+ stepId: string;
50
+ /** K12.1 — the authenticated principal chain, if one was established. */
51
+ principal?: PrincipalChain;
52
+ }
53
+ /**
54
+ * R12 — the capability ids a request is authorized under: the effect id (covers every resource
55
+ * of the effect — the legacy, coarse grant) and, when the call names a resource, the qualified
56
+ * `<toolId>:<resource>` id (covers just that resource).
57
+ */
58
+ export declare function capabilityId(req: CapabilityRequest): string;
59
+ export declare function capabilityIds(req: CapabilityRequest): readonly string[];
60
+ export interface CapabilityDecision {
61
+ allowed: boolean;
62
+ reason?: string;
63
+ }
64
+ export interface CapabilityAuthorizer {
65
+ authorize(req: CapabilityRequest): CapabilityDecision;
66
+ }
67
+ export declare class CapabilityDeniedError extends Error {
68
+ readonly toolId: string;
69
+ readonly reason: string;
70
+ constructor(toolId: string, reason: string);
71
+ }
72
+ export interface GrantTableOptions {
73
+ /**
74
+ * Which field identifies the "principal" whose grants apply. Default: the
75
+ * workflowId (a flow is the unit of authority). Return `executionId` for
76
+ * per-execution grants, or read a grant-set id from the request.
77
+ */
78
+ principal?: (req: CapabilityRequest) => string;
79
+ }
80
+ /**
81
+ * The least-privilege authorizer: a static table mapping a principal to the set of
82
+ * effect ids it may invoke (`"*"` grants all). Anything not explicitly granted is
83
+ * denied. Grants are declared at boot and cannot be mutated at runtime, so authority
84
+ * is fixed and auditable.
85
+ */
86
+ export declare class GrantTable implements CapabilityAuthorizer {
87
+ private readonly grants;
88
+ private readonly principalOf;
89
+ constructor(grants: Record<string, readonly string[]>, options?: GrantTableOptions);
90
+ authorize(req: CapabilityRequest): CapabilityDecision;
91
+ /**
92
+ * A read-only projection of the static grants — principal → granted effect ids,
93
+ * or `"*"` when all effects are granted. For pre-flight tooling (e.g. `@loomwork/verify`)
94
+ * that needs to inspect authority without a live {@link CapabilityRequest}.
95
+ */
96
+ view(): Record<string, readonly string[] | "*">;
97
+ }
98
+ export interface DelegationAuthorizerOptions {
99
+ /**
100
+ * Shared secret used to verify delegation signatures at this trust boundary.
101
+ *
102
+ * NOTE this is SYMMETRIC: a party that can verify a token can also mint one.
103
+ * That is why {@link DelegationAuthorizerOptions.base} is required — see below.
104
+ */
105
+ secret: string;
106
+ /**
107
+ * The callee's OWN authority — an effect is allowed only if it is in BOTH the
108
+ * verified delegated set AND the callee's own grants (never ambient escalation).
109
+ *
110
+ * REQUIRED, and it is load-bearing for security rather than a convenience.
111
+ * Because `secret` is symmetric, the verifier is also a valid minter: a callee
112
+ * can always sign itself a token granting `"*"`, and that token is
113
+ * cryptographically indistinguishable from a genuine one. The delegation check
114
+ * therefore cannot bound a callee's authority on its own — only the
115
+ * intersection with `base` can. With `base` set, minting a token gains a callee
116
+ * nothing it did not already hold, which is what makes the symmetric secret
117
+ * safe here.
118
+ *
119
+ * This option used to be optional, documented as "omit to allow the full
120
+ * verified delegated set (the callee delegates all of its authority decisions
121
+ * upstream)". That mode could not deliver what it claimed: upstream cannot
122
+ * retain authority over a callee that holds the minting key. Delegating
123
+ * authority ACROSS a trust boundary needs asymmetric signatures (the verifier
124
+ * must not be able to mint), which this primitive does not implement.
125
+ */
126
+ base: CapabilityAuthorizer;
127
+ /** Clock for token-expiry checks (epoch ms). Injectable for tests; defaults to `Date.now`. */
128
+ now?: () => number;
129
+ }
130
+ /**
131
+ * K12.4 — authorizes an effect against a VERIFIED attenuated delegation carried
132
+ * in `req.principal.delegation`. The token's signature is checked first (a
133
+ * forged or tampered delegation is denied outright), then the effect must be in
134
+ * the **intersection** of the delegated grants and the callee's own authority
135
+ * (`base`) — a delegate can never invoke an effect the delegator lacked, nor one
136
+ * the callee itself lacks. No delegation present ⇒ falls through to `base`.
137
+ *
138
+ * TRUST MODEL — delegator and callee share one secret, so they are one trust
139
+ * domain. The signature stops a THIRD party (a caller, a middlebox) forging or
140
+ * tampering with a grant set in transit; it does not, and cannot, stop the
141
+ * verifier minting its own. Authority over the callee comes from `base`, never
142
+ * from the token. Cross-organisation delegation, where the verifier must not be
143
+ * able to mint, is out of scope for this primitive and needs asymmetric
144
+ * signatures. See ADR 0024.
145
+ */
146
+ export declare class DelegationAuthorizer implements CapabilityAuthorizer {
147
+ private readonly options;
148
+ constructor(options: DelegationAuthorizerOptions);
149
+ authorize(req: CapabilityRequest): CapabilityDecision;
150
+ }
151
+ /** A signed delegation: the grant subset a delegator conferred, HMAC-bound. */
152
+ export interface DelegatedGrant {
153
+ /** The delegated capability ids (effect ids the callee may invoke on the caller's behalf). */
154
+ grants: string[];
155
+ /** The principal this token was issued to (bound into the signature). */
156
+ issuedTo: string;
157
+ /** Optional absolute expiry (epoch ms), bound into the signature; rejected once past. */
158
+ exp?: number;
159
+ /** HMAC-SHA256 over `${issuedTo}\n${sorted grants}[\nexp=…]` with the shared delegation secret. */
160
+ sig: string;
161
+ }
162
+ /** Mint a delegation token: the delegator signs the subset it confers on `issuedTo`. */
163
+ export declare function signDelegation(secret: string, issuedTo: string, grants: string[], exp?: number): DelegatedGrant;
164
+ /**
165
+ * R3k (ADR 0111 D1) — a stable, NON-bearer identifier of a token, for the durable principal
166
+ * record: SHA-256 over the signature, so the log can say which delegation an execution held
167
+ * without holding anything that can be presented. The token itself is never written.
168
+ */
169
+ export declare function delegationDigest(token: DelegatedGrant): string;
170
+ /** Verify a delegation's signature (constant-time). A forged/tampered token fails. */
171
+ export declare function verifyDelegation(secret: string, token: DelegatedGrant): boolean;
172
+ /**
173
+ * Attenuate a delegation to a narrower subset, re-signed for a new holder — only
174
+ * capabilities already in the token survive, and the parent's expiry is inherited.
175
+ * Requires the shared secret (the delegator/mint authority performs it); a
176
+ * pure-holder chained-HMAC variant is a future option.
177
+ *
178
+ * "Never broadening" is enforced on BOTH axes:
179
+ *
180
+ * - **Grants.** A capability absent from the parent cannot appear in the child. A parent
181
+ * holding `"*"` may attenuate to any concrete subset (that is narrowing) — previously
182
+ * such a subset was filtered against the literal `["*"]` array and came back EMPTY, so a
183
+ * wildcard delegator could not attenuate at all.
184
+ * - **Expiry.** The parent's `exp` is inherited, and `exp` may only be brought EARLIER.
185
+ * This was the important one: `exp` used not to be propagated, so attenuating a token
186
+ * that expired in an hour produced a child that **never expired** — narrowing the grant
187
+ * set while silently making the token immortal.
188
+ *
189
+ * Throws on an unverifiable parent. The signature is what makes the grant set trustworthy,
190
+ * and this function holds the secret — so attenuating an unchecked token would launder a
191
+ * forged one into a validly signed child. The old contract merely *documented* that the
192
+ * caller must pass a verified token; a security primitive should not depend on that.
193
+ */
194
+ export declare function attenuateDelegation(secret: string, token: DelegatedGrant, issuedTo: string, subset: string[], exp?: number): DelegatedGrant;
195
+ import type { Scheduler } from "@loomwork/scheduler";
196
+ import type { Warmer } from "./types";
197
+ export interface EffectRuntimeOptions {
198
+ /** When set, every effect is authorized against it; ungranted effects are denied (K2). */
199
+ authorizer?: CapabilityAuthorizer;
200
+ /**
201
+ * When set, effects run through this scheduler (M6) — bounded concurrency + priority
202
+ * ordering across all of a runtime's effects (admission control). Omit for the
203
+ * zero-overhead direct path.
204
+ */
205
+ scheduler?: Scheduler;
206
+ /** Advisory pre-warm hook, invoked off-log before each effect (M6). */
207
+ warmer?: Warmer;
208
+ /** ADR 0119 — where a swallowed delta-sink throw is reported. Defaults to `console.warn`. */
209
+ onWarn?: (message: string) => void;
210
+ }
211
+ /**
212
+ * R16 / R17 — THE identity key: `[tenant, actor, onBehalfOf]`, one string, shared by everything that
213
+ * scopes state to "who" (a conversation's owner, a goal's owner). Two principals are the same identity
214
+ * iff all three fields are equal (an absent field equals only an absent field), so a delegate acting
215
+ * `onBehalfOf` someone is a DIFFERENT identity from that someone. A credential (a delegation token)
216
+ * is not part of identity. Keep one definition: two keys for one idea is how a scope check drifts.
217
+ */
218
+ export declare function identityKeyOf(i: {
219
+ tenant?: string;
220
+ actor: string;
221
+ onBehalfOf?: string;
222
+ }): string;
223
+ /**
224
+ * The identity key of a principal, or `undefined` when it names no actor (it can own nothing) or the
225
+ * kernel marked it unusable ({@link principalBlocked}: shredded, delegation or record unresolved).
226
+ */
227
+ export declare function principalIdentityKey(principal: PrincipalChain | undefined): string | undefined;
228
+ //# sourceMappingURL=authz.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz.d.ts","sourceRoot":"","sources":["../src/authz.ts"],"names":[],"mappings":"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"}
package/dist/authz.js ADDED
@@ -0,0 +1,281 @@
1
+ "use strict";
2
+ // ── Capability protection (Loom 2.0 K2) — the kernel's authority ring ─────────
3
+ //
4
+ // Every side effect flows through EffectRuntime.execute, so that is the one boundary
5
+ // a library CAN enforce (unlike hardware memory protection). A CapabilityAuthorizer
6
+ // decides, per invocation, whether the calling execution holds the authority to run
7
+ // the effect. Grants are unforgeable: an execution cannot grant itself a capability —
8
+ // grants live only in the authorizer, wired statically at boot (P5). A denied effect
9
+ // never runs; the denial is emitted as a durable EFFECT_DENIED fact (P9) and fails
10
+ // the step. Enforcement is opt-in: no authorizer ⇒ today's permissive behaviour.
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.DelegationAuthorizer = exports.GrantTable = exports.CapabilityDeniedError = void 0;
13
+ exports.principalBlocked = principalBlocked;
14
+ exports.capabilityId = capabilityId;
15
+ exports.capabilityIds = capabilityIds;
16
+ exports.signDelegation = signDelegation;
17
+ exports.delegationDigest = delegationDigest;
18
+ exports.verifyDelegation = verifyDelegation;
19
+ exports.attenuateDelegation = attenuateDelegation;
20
+ exports.identityKeyOf = identityKeyOf;
21
+ exports.principalIdentityKey = principalIdentityKey;
22
+ /**
23
+ * R3k (ADR 0111 D4) — why the KERNEL refuses every effect of this principal before any
24
+ * authorizer is asked, or `undefined` when it does not. A `PolicyAuthorizer`, a `GrantTable` or a
25
+ * 3P authorizer would otherwise judge a chain whose delegation could not be restored on its own
26
+ * terms (an unattenuated child after a crash is the privilege escalation this closes).
27
+ */
28
+ function principalBlocked(principal) {
29
+ if (principal === undefined)
30
+ return undefined;
31
+ if (principal.principalShredded === true) {
32
+ return "the execution's recorded principal was crypto-shredded";
33
+ }
34
+ if (principal.delegationUnresolved !== undefined) {
35
+ return `the execution's recorded delegation "${String(principal.delegationUnresolved)}" could not be restored (no principalCodec/resolver)`;
36
+ }
37
+ if (principal.principalUnresolved !== undefined) {
38
+ return `the execution's recorded principal could not be restored: ${String(principal.principalUnresolved)}`;
39
+ }
40
+ return undefined;
41
+ }
42
+ /**
43
+ * R12 — the capability ids a request is authorized under: the effect id (covers every resource
44
+ * of the effect — the legacy, coarse grant) and, when the call names a resource, the qualified
45
+ * `<toolId>:<resource>` id (covers just that resource).
46
+ */
47
+ function capabilityId(req) {
48
+ return req.resource === undefined ? req.toolId : `${req.toolId}:${req.resource}`;
49
+ }
50
+ function capabilityIds(req) {
51
+ return req.resource === undefined ? [req.toolId] : [req.toolId, `${req.toolId}:${req.resource}`];
52
+ }
53
+ class CapabilityDeniedError extends Error {
54
+ toolId;
55
+ reason;
56
+ constructor(toolId, reason) {
57
+ super(`Capability denied: effect "${toolId}" — ${reason}`);
58
+ this.name = "CapabilityDeniedError";
59
+ this.toolId = toolId;
60
+ this.reason = reason;
61
+ }
62
+ }
63
+ exports.CapabilityDeniedError = CapabilityDeniedError;
64
+ /**
65
+ * The least-privilege authorizer: a static table mapping a principal to the set of
66
+ * effect ids it may invoke (`"*"` grants all). Anything not explicitly granted is
67
+ * denied. Grants are declared at boot and cannot be mutated at runtime, so authority
68
+ * is fixed and auditable.
69
+ */
70
+ class GrantTable {
71
+ grants;
72
+ principalOf;
73
+ constructor(grants, options = {}) {
74
+ this.grants = new Map(Object.entries(grants).map(([k, v]) => [k, new Set(v)]));
75
+ this.principalOf = options.principal ?? ((req) => req.workflowId);
76
+ }
77
+ authorize(req) {
78
+ const principal = this.principalOf(req);
79
+ const granted = this.grants.get(principal);
80
+ if (granted === undefined) {
81
+ return { allowed: false, reason: `principal "${principal}" has no grants` };
82
+ }
83
+ if (granted.has("*") || capabilityIds(req).some((id) => granted.has(id))) {
84
+ return { allowed: true };
85
+ }
86
+ return {
87
+ allowed: false,
88
+ reason: `effect "${capabilityId(req)}" not granted to "${principal}"`,
89
+ };
90
+ }
91
+ /**
92
+ * A read-only projection of the static grants — principal → granted effect ids,
93
+ * or `"*"` when all effects are granted. For pre-flight tooling (e.g. `@loomwork/verify`)
94
+ * that needs to inspect authority without a live {@link CapabilityRequest}.
95
+ */
96
+ view() {
97
+ const out = {};
98
+ for (const [principal, set] of this.grants) {
99
+ out[principal] = set.has("*") ? "*" : [...set];
100
+ }
101
+ return out;
102
+ }
103
+ }
104
+ exports.GrantTable = GrantTable;
105
+ /**
106
+ * K12.4 — authorizes an effect against a VERIFIED attenuated delegation carried
107
+ * in `req.principal.delegation`. The token's signature is checked first (a
108
+ * forged or tampered delegation is denied outright), then the effect must be in
109
+ * the **intersection** of the delegated grants and the callee's own authority
110
+ * (`base`) — a delegate can never invoke an effect the delegator lacked, nor one
111
+ * the callee itself lacks. No delegation present ⇒ falls through to `base`.
112
+ *
113
+ * TRUST MODEL — delegator and callee share one secret, so they are one trust
114
+ * domain. The signature stops a THIRD party (a caller, a middlebox) forging or
115
+ * tampering with a grant set in transit; it does not, and cannot, stop the
116
+ * verifier minting its own. Authority over the callee comes from `base`, never
117
+ * from the token. Cross-organisation delegation, where the verifier must not be
118
+ * able to mint, is out of scope for this primitive and needs asymmetric
119
+ * signatures. See ADR 0024.
120
+ */
121
+ class DelegationAuthorizer {
122
+ options;
123
+ constructor(options) {
124
+ this.options = options;
125
+ // The type alone is not the enforcement — JS callers and `as any` bypass it,
126
+ // and an absent `base` silently turns the intersection into a no-op, which
127
+ // reads as a working gate while bounding nothing.
128
+ if (options.base === undefined || options.base === null) {
129
+ throw new Error("DelegationAuthorizer: `base` is required — the delegation secret is symmetric, so a " +
130
+ "verified token cannot bound a callee's authority on its own (see ADR 0024).");
131
+ }
132
+ }
133
+ authorize(req) {
134
+ // Defence in depth: the kernel already refuses these before any authorizer (principalBlocked).
135
+ const blocked = principalBlocked(req.principal);
136
+ if (blocked !== undefined)
137
+ return { allowed: false, reason: blocked };
138
+ const token = req.principal?.delegation;
139
+ if (token === undefined) {
140
+ return this.options.base.authorize(req);
141
+ }
142
+ if (!verifyDelegation(this.options.secret, token)) {
143
+ return { allowed: false, reason: "delegation signature invalid (forged or tampered)" };
144
+ }
145
+ // Reject expired tokens (exp is signature-bound, so it can't be extended). A token
146
+ // without exp never expires — the same as before this field existed.
147
+ if (token.exp !== undefined && (this.options.now?.() ?? Date.now()) >= token.exp) {
148
+ return { allowed: false, reason: `delegation expired at ${token.exp}` };
149
+ }
150
+ // K12.4 — bind the token to its intended holder. A delegation minted for
151
+ // `issuedTo` may only be presented BY that principal (`req.principal.actor`).
152
+ // The signature proves authenticity, not that the bearer is the intended
153
+ // holder — without this check a captured/misrouted token is a confused deputy,
154
+ // usable by whoever holds it.
155
+ if (req.principal?.actor !== token.issuedTo) {
156
+ return {
157
+ allowed: false,
158
+ reason: `delegation issued to "${token.issuedTo}" but presented by "${req.principal?.actor ?? "(none)"}"`,
159
+ };
160
+ }
161
+ const delegated = new Set(token.grants);
162
+ const inDelegated = delegated.has("*") || capabilityIds(req).some((id) => delegated.has(id));
163
+ if (!inDelegated) {
164
+ return {
165
+ allowed: false,
166
+ reason: `effect "${capabilityId(req)}" not in the delegated grant set`,
167
+ };
168
+ }
169
+ // Intersection: the callee must ALSO hold the authority (no ambient escalation).
170
+ // This is what bounds a callee that could mint its own token — never the signature.
171
+ const baseDecision = this.options.base.authorize(req);
172
+ if (!baseDecision.allowed) {
173
+ return {
174
+ allowed: false,
175
+ reason: `delegated, but the callee itself lacks "${req.toolId}": ${baseDecision.reason ?? ""}`,
176
+ };
177
+ }
178
+ return { allowed: true };
179
+ }
180
+ }
181
+ exports.DelegationAuthorizer = DelegationAuthorizer;
182
+ // ── K12.4 — verifiable attenuated delegation (macaroon-lite) ─────────────────
183
+ //
184
+ // The evaluation's amendment: "an attenuated grant set carried as a plain
185
+ // request field is forgeable by any caller or middlebox." So a delegation is
186
+ // SIGNED (HMAC) by the delegator and VERIFIED at the callee's trust boundary
187
+ // before its grants are honored. Attenuation can only NARROW: a holder derives
188
+ // a more-restricted token, never a broader one, because the delegated grant set
189
+ // is bound into the signature. The intersection is computed from the VERIFIED
190
+ // set, never a forgeable field.
191
+ const node_crypto_1 = require("node:crypto");
192
+ // `exp` is appended only when present, so tokens without it sign the exact prior payload
193
+ // (backward-compatible), while a tampered `exp` breaks the signature.
194
+ function delegationPayload(issuedTo, grants, exp) {
195
+ const base = `${issuedTo}\n${[...grants].sort().join(",")}`;
196
+ return exp !== undefined ? `${base}\nexp=${exp}` : base;
197
+ }
198
+ /** Mint a delegation token: the delegator signs the subset it confers on `issuedTo`. */
199
+ function signDelegation(secret, issuedTo, grants, exp) {
200
+ const sig = (0, node_crypto_1.createHmac)("sha256", secret)
201
+ .update(delegationPayload(issuedTo, grants, exp))
202
+ .digest("hex");
203
+ return { grants: [...grants], issuedTo, ...(exp !== undefined && { exp }), sig };
204
+ }
205
+ /**
206
+ * R3k (ADR 0111 D1) — a stable, NON-bearer identifier of a token, for the durable principal
207
+ * record: SHA-256 over the signature, so the log can say which delegation an execution held
208
+ * without holding anything that can be presented. The token itself is never written.
209
+ */
210
+ function delegationDigest(token) {
211
+ return (0, node_crypto_1.createHash)("sha256").update(String(token.sig)).digest("hex");
212
+ }
213
+ /** Verify a delegation's signature (constant-time). A forged/tampered token fails. */
214
+ function verifyDelegation(secret, token) {
215
+ const expected = (0, node_crypto_1.createHmac)("sha256", secret)
216
+ .update(delegationPayload(token.issuedTo, token.grants, token.exp))
217
+ .digest("hex");
218
+ const a = Buffer.from(expected, "hex");
219
+ const b = Buffer.from(token.sig, "hex");
220
+ return a.length === b.length && (0, node_crypto_1.timingSafeEqual)(a, b);
221
+ }
222
+ /**
223
+ * Attenuate a delegation to a narrower subset, re-signed for a new holder — only
224
+ * capabilities already in the token survive, and the parent's expiry is inherited.
225
+ * Requires the shared secret (the delegator/mint authority performs it); a
226
+ * pure-holder chained-HMAC variant is a future option.
227
+ *
228
+ * "Never broadening" is enforced on BOTH axes:
229
+ *
230
+ * - **Grants.** A capability absent from the parent cannot appear in the child. A parent
231
+ * holding `"*"` may attenuate to any concrete subset (that is narrowing) — previously
232
+ * such a subset was filtered against the literal `["*"]` array and came back EMPTY, so a
233
+ * wildcard delegator could not attenuate at all.
234
+ * - **Expiry.** The parent's `exp` is inherited, and `exp` may only be brought EARLIER.
235
+ * This was the important one: `exp` used not to be propagated, so attenuating a token
236
+ * that expired in an hour produced a child that **never expired** — narrowing the grant
237
+ * set while silently making the token immortal.
238
+ *
239
+ * Throws on an unverifiable parent. The signature is what makes the grant set trustworthy,
240
+ * and this function holds the secret — so attenuating an unchecked token would launder a
241
+ * forged one into a validly signed child. The old contract merely *documented* that the
242
+ * caller must pass a verified token; a security primitive should not depend on that.
243
+ */
244
+ function attenuateDelegation(secret, token, issuedTo, subset, exp) {
245
+ if (!verifyDelegation(secret, token)) {
246
+ throw new Error("attenuateDelegation: parent delegation failed verification — refusing to sign a child from an unverified token");
247
+ }
248
+ const parentIsWildcard = token.grants.includes("*");
249
+ const narrowed = parentIsWildcard ? [...subset] : subset.filter((g) => token.grants.includes(g));
250
+ // Inherit the parent's expiry; an explicit `exp` may only tighten it.
251
+ const inherited = token.exp;
252
+ const nextExp = inherited === undefined ? exp : exp === undefined ? inherited : Math.min(inherited, exp);
253
+ return signDelegation(secret, issuedTo, narrowed, nextExp);
254
+ }
255
+ /**
256
+ * R16 / R17 — THE identity key: `[tenant, actor, onBehalfOf]`, one string, shared by everything that
257
+ * scopes state to "who" (a conversation's owner, a goal's owner). Two principals are the same identity
258
+ * iff all three fields are equal (an absent field equals only an absent field), so a delegate acting
259
+ * `onBehalfOf` someone is a DIFFERENT identity from that someone. A credential (a delegation token)
260
+ * is not part of identity. Keep one definition: two keys for one idea is how a scope check drifts.
261
+ */
262
+ function identityKeyOf(i) {
263
+ return JSON.stringify([i.tenant ?? null, i.actor, i.onBehalfOf ?? null]);
264
+ }
265
+ /**
266
+ * The identity key of a principal, or `undefined` when it names no actor (it can own nothing) or the
267
+ * kernel marked it unusable ({@link principalBlocked}: shredded, delegation or record unresolved).
268
+ */
269
+ function principalIdentityKey(principal) {
270
+ if (principal === undefined || principalBlocked(principal) !== undefined)
271
+ return undefined;
272
+ const actor = principal.actor;
273
+ if (typeof actor !== "string" || actor.trim() === "")
274
+ return undefined;
275
+ return identityKeyOf({
276
+ actor,
277
+ ...(typeof principal.tenant === "string" && { tenant: principal.tenant }),
278
+ ...(typeof principal.onBehalfOf === "string" && { onBehalfOf: principal.onBehalfOf }),
279
+ });
280
+ }
281
+ //# sourceMappingURL=authz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz.js","sourceRoot":"","sources":["../src/authz.ts"],"names":[],"mappings":";AAAA,iFAAiF;AACjF,EAAE;AACF,qFAAqF;AACrF,oFAAoF;AACpF,oFAAoF;AACpF,sFAAsF;AACtF,qFAAqF;AACrF,mFAAmF;AACnF,iFAAiF;;;AAuCjF,4CAYC;AAuBD,oCAEC;AAED,sCAEC;AA2ND,wCAUC;AAOD,4CAEC;AAGD,4CAOC;AAwBD,kDAmBC;AA2BD,sCAEC;AAMD,oDASC;AA9XD;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,SAAqC;IACpE,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,SAAS,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;QACzC,OAAO,wDAAwD,CAAC;IAClE,CAAC;IACD,IAAI,SAAS,CAAC,oBAAoB,KAAK,SAAS,EAAE,CAAC;QACjD,OAAO,wCAAwC,MAAM,CAAC,SAAS,CAAC,oBAAoB,CAAC,sDAAsD,CAAC;IAC9I,CAAC;IACD,IAAI,SAAS,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;QAChD,OAAO,6DAA6D,MAAM,CAAC,SAAS,CAAC,mBAAmB,CAAC,EAAE,CAAC;IAC9G,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAkBD;;;;GAIG;AACH,SAAgB,YAAY,CAAC,GAAsB;IACjD,OAAO,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;AACnF,CAAC;AAED,SAAgB,aAAa,CAAC,GAAsB;IAClD,OAAO,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;AACnG,CAAC;AAaD,MAAa,qBAAsB,SAAQ,KAAK;IACrC,MAAM,CAAS;IACf,MAAM,CAAS;IACxB,YAAY,MAAc,EAAE,MAAc;QACxC,KAAK,CAAC,8BAA8B,MAAM,OAAO,MAAM,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AATD,sDASC;AAWD;;;;;GAKG;AACH,MAAa,UAAU;IACJ,MAAM,CAA2B;IACjC,WAAW,CAAqC;IAEjE,YAAY,MAAyC,EAAE,UAA6B,EAAE;QACpF,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACpE,CAAC;IAED,SAAS,CAAC,GAAsB;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,SAAS,iBAAiB,EAAE,CAAC;QAC9E,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACzE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,WAAW,YAAY,CAAC,GAAG,CAAC,qBAAqB,SAAS,GAAG;SACtE,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,MAAM,GAAG,GAA4C,EAAE,CAAC;QACxD,KAAK,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC3C,GAAG,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF;AApCD,gCAoCC;AAmCD;;;;;;;;;;;;;;;GAeG;AACH,MAAa,oBAAoB;IACF;IAA7B,YAA6B,OAAoC;QAApC,YAAO,GAAP,OAAO,CAA6B;QAC/D,6EAA6E;QAC7E,2EAA2E;QAC3E,kDAAkD;QAClD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CACb,sFAAsF;gBACpF,6EAA6E,CAChF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,SAAS,CAAC,GAAsB;QAC9B,+FAA+F;QAC/F,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAChD,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QACtE,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC;QACxC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC1C,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;YAClD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,mDAAmD,EAAE,CAAC;QACzF,CAAC;QACD,mFAAmF;QACnF,qEAAqE;QACrE,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;YACjF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,yBAAyB,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;QAC1E,CAAC;QACD,yEAAyE;QACzE,8EAA8E;QAC9E,yEAAyE;QACzE,+EAA+E;QAC/E,8BAA8B;QAC9B,IAAI,GAAG,CAAC,SAAS,EAAE,KAAK,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC5C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,yBAAyB,KAAK,CAAC,QAAQ,uBAAuB,GAAG,CAAC,SAAS,EAAE,KAAK,IAAI,QAAQ,GAAG;aAC1G,CAAC;QACJ,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7F,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,WAAW,YAAY,CAAC,GAAG,CAAC,kCAAkC;aACvE,CAAC;QACJ,CAAC;QACD,iFAAiF;QACjF,oFAAoF;QACpF,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtD,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;YAC1B,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,2CAA2C,GAAG,CAAC,MAAM,MAAM,YAAY,CAAC,MAAM,IAAI,EAAE,EAAE;aAC/F,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF;AA3DD,oDA2DC;AAED,gFAAgF;AAChF,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,gCAAgC;AAEhC,6CAAsE;AActE,yFAAyF;AACzF,sEAAsE;AACtE,SAAS,iBAAiB,CAAC,QAAgB,EAAE,MAAyB,EAAE,GAAY;IAClF,MAAM,IAAI,GAAG,GAAG,QAAQ,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5D,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED,wFAAwF;AACxF,SAAgB,cAAc,CAC5B,MAAc,EACd,QAAgB,EAChB,MAAgB,EAChB,GAAY;IAEZ,MAAM,GAAG,GAAG,IAAA,wBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC;SACrC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;SAChD,MAAM,CAAC,KAAK,CAAC,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;AACnF,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,KAAqB;IACpD,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC;AAED,sFAAsF;AACtF,SAAgB,gBAAgB,CAAC,MAAc,EAAE,KAAqB;IACpE,MAAM,QAAQ,GAAG,IAAA,wBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC;SAC1C,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;SAClE,MAAM,CAAC,KAAK,CAAC,CAAC;IACjB,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACvC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,IAAA,6BAAe,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,mBAAmB,CACjC,MAAc,EACd,KAAqB,EACrB,QAAgB,EAChB,MAAgB,EAChB,GAAY;IAEZ,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,gHAAgH,CACjH,CAAC;IACJ,CAAC;IACD,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACjG,sEAAsE;IACtE,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC;IAC5B,MAAM,OAAO,GACX,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC3F,OAAO,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAoBD;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,CAA0D;IACtF,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED;;;GAGG;AACH,SAAgB,oBAAoB,CAAC,SAAqC;IACxE,IAAI,SAAS,KAAK,SAAS,IAAI,gBAAgB,CAAC,SAAS,CAAC,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3F,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACvE,OAAO,aAAa,CAAC;QACnB,KAAK;QACL,GAAG,CAAC,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;QACzE,GAAG,CAAC,OAAO,SAAS,CAAC,UAAU,KAAK,QAAQ,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,UAAU,EAAE,CAAC;KACtF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Effect, EffectContext } from "../types";
2
+ /**
3
+ * @deprecated ADR 0115 — the `a2a` effect belongs to `@loom/plugin-a2a`, which now defines and
4
+ * exports its own `A2AEffect`. This copy stays for one minor so existing imports keep working
5
+ * (a re-export would make this kernel-adjacent package depend on a plugin). `"a2a"` is just a
6
+ * custom kind to the kernel.
7
+ */
8
+ export declare class A2AEffect<TIn = unknown, TOut = unknown> implements Effect<TIn, TOut> {
9
+ private readonly fn;
10
+ readonly kind: "a2a";
11
+ constructor(fn: (input: TIn, ctx: EffectContext) => Promise<TOut>);
12
+ execute(input: TIn, ctx: EffectContext): Promise<TOut>;
13
+ }
14
+ //# sourceMappingURL=A2AEffect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"A2AEffect.d.ts","sourceRoot":"","sources":["../../src/effects/A2AEffect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEtD;;;;;GAKG;AACH,qBAAa,SAAS,CAAC,GAAG,GAAG,OAAO,EAAE,IAAI,GAAG,OAAO,CAAE,YAAW,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC;IAGpE,OAAO,CAAC,QAAQ,CAAC,EAAE;IAF/B,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;gBAEF,EAAE,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC;IAElF,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.A2AEffect = void 0;
4
+ /**
5
+ * @deprecated ADR 0115 — the `a2a` effect belongs to `@loom/plugin-a2a`, which now defines and
6
+ * exports its own `A2AEffect`. This copy stays for one minor so existing imports keep working
7
+ * (a re-export would make this kernel-adjacent package depend on a plugin). `"a2a"` is just a
8
+ * custom kind to the kernel.
9
+ */
10
+ class A2AEffect {
11
+ fn;
12
+ kind = "a2a";
13
+ constructor(fn) {
14
+ this.fn = fn;
15
+ }
16
+ execute(input, ctx) {
17
+ return this.fn(input, ctx);
18
+ }
19
+ }
20
+ exports.A2AEffect = A2AEffect;
21
+ //# sourceMappingURL=A2AEffect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"A2AEffect.js","sourceRoot":"","sources":["../../src/effects/A2AEffect.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACH,MAAa,SAAS;IAGS;IAFpB,IAAI,GAAG,KAAc,CAAC;IAE/B,YAA6B,EAAqD;QAArD,OAAE,GAAF,EAAE,CAAmD;IAAG,CAAC;IAEtF,OAAO,CAAC,KAAU,EAAE,GAAkB;QACpC,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF;AARD,8BAQC"}
@@ -0,0 +1,13 @@
1
+ import type { Effect, EffectContext } from "../types";
2
+ export interface HumanEffectConfig {
3
+ on: string;
4
+ timeout?: number;
5
+ correlationKey?: (input: unknown) => string;
6
+ }
7
+ export declare class HumanEffect<TIn = unknown, TOut = unknown> implements Effect<TIn, TOut> {
8
+ private readonly config;
9
+ readonly kind: "human";
10
+ constructor(config: HumanEffectConfig);
11
+ execute(input: TIn, _ctx: EffectContext): Promise<TOut>;
12
+ }
13
+ //# sourceMappingURL=HumanEffect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HumanEffect.d.ts","sourceRoot":"","sources":["../../src/effects/HumanEffect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAC7C;AAED,qBAAa,WAAW,CAAC,GAAG,GAAG,OAAO,EAAE,IAAI,GAAG,OAAO,CAAE,YAAW,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC;IAGtE,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;gBAEJ,MAAM,EAAE,iBAAiB;IAEtD,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAUxD"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HumanEffect = void 0;
4
+ const core_1 = require("@loomwork/core");
5
+ class HumanEffect {
6
+ config;
7
+ kind = "human";
8
+ constructor(config) {
9
+ this.config = config;
10
+ }
11
+ execute(input, _ctx) {
12
+ const correlationKey = this.config.correlationKey !== undefined ? this.config.correlationKey(input) : undefined;
13
+ throw new core_1.SuspendError({
14
+ on: this.config.on,
15
+ ...(this.config.timeout !== undefined && { timeout: this.config.timeout }),
16
+ ...(correlationKey !== undefined && { correlationKey }),
17
+ });
18
+ }
19
+ }
20
+ exports.HumanEffect = HumanEffect;
21
+ //# sourceMappingURL=HumanEffect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HumanEffect.js","sourceRoot":"","sources":["../../src/effects/HumanEffect.ts"],"names":[],"mappings":";;;AAAA,yCAA8C;AAS9C,MAAa,WAAW;IAGO;IAFpB,IAAI,GAAG,OAAgB,CAAC;IAEjC,YAA6B,MAAyB;QAAzB,WAAM,GAAN,MAAM,CAAmB;IAAG,CAAC;IAE1D,OAAO,CAAC,KAAU,EAAE,IAAmB;QACrC,MAAM,cAAc,GAClB,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAE3F,MAAM,IAAI,mBAAY,CAAC;YACrB,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE;YAClB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAC1E,GAAG,CAAC,cAAc,KAAK,SAAS,IAAI,EAAE,cAAc,EAAE,CAAC;SACxD,CAAC,CAAC;IACL,CAAC;CACF;AAfD,kCAeC"}
@@ -0,0 +1,10 @@
1
+ import type { HandlerSpec } from "@loomwork/core";
2
+ import type { Effect, EffectContext } from "../types";
3
+ export declare class LazyEffect<TIn = unknown, TOut = unknown> implements Effect<TIn, TOut> {
4
+ private readonly spec;
5
+ readonly kind: "tool";
6
+ private resolved;
7
+ constructor(spec: HandlerSpec<TIn, TOut>);
8
+ execute(input: TIn, ctx: EffectContext): Promise<TOut>;
9
+ }
10
+ //# sourceMappingURL=LazyEffect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LazyEffect.d.ts","sourceRoot":"","sources":["../../src/effects/LazyEffect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwC,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAMtD,qBAAa,UAAU,CAAC,GAAG,GAAG,OAAO,EAAE,IAAI,GAAG,OAAO,CAAE,YAAW,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC;IAIrE,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAmC;gBAEtB,IAAI,EAAE,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC;IAEnD,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAW7D"}