attenu-guard 0.3.1 → 0.4.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 (74) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/cjs/adapters/langgraph.d.ts +27 -3
  3. package/dist/cjs/adapters/langgraph.d.ts.map +1 -1
  4. package/dist/cjs/adapters/langgraph.js +290 -11
  5. package/dist/cjs/adapters/langgraph.js.map +1 -1
  6. package/dist/cjs/audit.d.ts +38 -1
  7. package/dist/cjs/audit.d.ts.map +1 -1
  8. package/dist/cjs/audit.js +52 -8
  9. package/dist/cjs/audit.js.map +1 -1
  10. package/dist/cjs/chain.d.ts +32 -0
  11. package/dist/cjs/chain.d.ts.map +1 -1
  12. package/dist/cjs/chain.js +0 -0
  13. package/dist/cjs/chain.js.map +1 -1
  14. package/dist/cjs/evidence.d.ts +38 -3
  15. package/dist/cjs/evidence.d.ts.map +1 -1
  16. package/dist/cjs/evidence.js +523 -13
  17. package/dist/cjs/evidence.js.map +1 -1
  18. package/dist/cjs/guard.d.ts +163 -9
  19. package/dist/cjs/guard.d.ts.map +1 -1
  20. package/dist/cjs/guard.js +426 -26
  21. package/dist/cjs/guard.js.map +1 -1
  22. package/dist/cjs/index.d.ts +10 -7
  23. package/dist/cjs/index.d.ts.map +1 -1
  24. package/dist/cjs/index.js +21 -5
  25. package/dist/cjs/index.js.map +1 -1
  26. package/dist/cjs/params.d.ts +52 -0
  27. package/dist/cjs/params.d.ts.map +1 -0
  28. package/dist/cjs/params.js +97 -0
  29. package/dist/cjs/params.js.map +1 -0
  30. package/dist/cjs/reasons.d.ts +89 -1
  31. package/dist/cjs/reasons.d.ts.map +1 -1
  32. package/dist/cjs/reasons.js +99 -3
  33. package/dist/cjs/reasons.js.map +1 -1
  34. package/dist/cjs/version.d.ts +10 -0
  35. package/dist/cjs/version.d.ts.map +1 -0
  36. package/dist/cjs/version.js +13 -0
  37. package/dist/cjs/version.js.map +1 -0
  38. package/dist/esm/adapters/langgraph.d.ts +27 -3
  39. package/dist/esm/adapters/langgraph.d.ts.map +1 -1
  40. package/dist/esm/adapters/langgraph.js +290 -11
  41. package/dist/esm/adapters/langgraph.js.map +1 -1
  42. package/dist/esm/audit.d.ts +38 -1
  43. package/dist/esm/audit.d.ts.map +1 -1
  44. package/dist/esm/audit.js +51 -8
  45. package/dist/esm/audit.js.map +1 -1
  46. package/dist/esm/chain.d.ts +32 -0
  47. package/dist/esm/chain.d.ts.map +1 -1
  48. package/dist/esm/chain.js +0 -0
  49. package/dist/esm/chain.js.map +1 -1
  50. package/dist/esm/evidence.d.ts +38 -3
  51. package/dist/esm/evidence.d.ts.map +1 -1
  52. package/dist/esm/evidence.js +523 -13
  53. package/dist/esm/evidence.js.map +1 -1
  54. package/dist/esm/guard.d.ts +163 -9
  55. package/dist/esm/guard.d.ts.map +1 -1
  56. package/dist/esm/guard.js +393 -27
  57. package/dist/esm/guard.js.map +1 -1
  58. package/dist/esm/index.d.ts +10 -7
  59. package/dist/esm/index.d.ts.map +1 -1
  60. package/dist/esm/index.js +6 -4
  61. package/dist/esm/index.js.map +1 -1
  62. package/dist/esm/params.d.ts +52 -0
  63. package/dist/esm/params.d.ts.map +1 -0
  64. package/dist/esm/params.js +92 -0
  65. package/dist/esm/params.js.map +1 -0
  66. package/dist/esm/reasons.d.ts +89 -1
  67. package/dist/esm/reasons.d.ts.map +1 -1
  68. package/dist/esm/reasons.js +97 -2
  69. package/dist/esm/reasons.js.map +1 -1
  70. package/dist/esm/version.d.ts +10 -0
  71. package/dist/esm/version.d.ts.map +1 -0
  72. package/dist/esm/version.js +10 -0
  73. package/dist/esm/version.js.map +1 -0
  74. package/package.json +1 -1
@@ -11,7 +11,7 @@
11
11
  * the Python library's `attenu_guard.reasons` value for value.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.Decision = exports.Reason = exports.DISPOSITIONS = exports.Disposition = exports.ReasonCode = void 0;
14
+ exports.Decision = exports.Reason = exports.CompletionResult = exports.BODY_STATES = exports.BodyState = exports.CAPTURES = exports.Capture = exports.DISPOSITIONS = exports.Disposition = exports.ReasonCode = void 0;
15
15
  /**
16
16
  * Stable string constants for machine-readable denial reasons. Never rename an
17
17
  * existing value; add new ones instead.
@@ -37,6 +37,14 @@ exports.ReasonCode = {
37
37
  CHAIN_CEILING: "chain_ceiling",
38
38
  /** The principal holds no authority at all in this chain. */
39
39
  NO_AUTHORITY: "no_authority",
40
+ // 0.9.0 execution-binding transition (schema_version=2 chains only — see guard.ts):
41
+ /** `check` refused: the node already called `complete()`. */
42
+ NODE_FINALIZED: "node_finalized",
43
+ /**
44
+ * The CSPRNG failed while allocating `callId`; fail-closed — the call is denied and
45
+ * nothing is written.
46
+ */
47
+ CALL_ID_UNAVAILABLE: "call_id_unavailable",
40
48
  };
41
49
  /**
42
50
  * WHY a denied scope was not in the node's authority — the ledger's answer to
@@ -54,6 +62,74 @@ exports.Disposition = {
54
62
  OUT_OF_AUTHORITY: "out_of_authority",
55
63
  };
56
64
  exports.DISPOSITIONS = new Set(Object.values(exports.Disposition));
65
+ /**
66
+ * What the adapter's code path WILL observe for a given `check`ed call (0.9.0 execution binding)
67
+ * — recorded on the `allow` entry alongside `adapter` (module/version/hookPath). Describes
68
+ * observation CAPABILITY only, never a claim of quality: a verifier routes a call into
69
+ * observed/unobserved reporting from this label, never trusts it as evidence on its own.
70
+ */
71
+ exports.Capture = {
72
+ /** The adapter's wrapper calls the body itself, synchronously. */
73
+ WRAPPER_SYNC: "wrapper_sync",
74
+ /** ... and awaits it. */
75
+ WRAPPER_ASYNC: "wrapper_async",
76
+ /** The framework itself calls back after the body runs. */
77
+ FRAMEWORK_POST_HOOK: "framework_post_hook",
78
+ /** The adapter sees the call authorized but never observes it finish. */
79
+ PRE_HOOK_ONLY: "pre_hook_only",
80
+ };
81
+ exports.CAPTURES = new Set(Object.values(exports.Capture));
82
+ /**
83
+ * The `outcome` record's observation of how a body-owning wrapper's call ended (0.9.0 execution
84
+ * binding, docs/execution-binding spec section 3) — an OBSERVATION, not a judgment about the
85
+ * world. There is no `executed`/`blocked`/`timeout`/`cancelled` at this layer; each of those
86
+ * words claims knowledge a wrapper does not always have. Adapters emitting into a richer outcome
87
+ * vocabulary own that mapping.
88
+ */
89
+ exports.BodyState = {
90
+ /** The body returned to the wrapper. */
91
+ RETURNED: "returned",
92
+ /** It raised (`errorCode` required, from the exception's class/constructor name). */
93
+ RAISED: "raised",
94
+ /** The wrapper stopped observing while the body may still run. */
95
+ ABANDONED: "abandoned",
96
+ /** The wrapper returned a generator/stream/future it does not itself consume. */
97
+ DEFERRED: "deferred",
98
+ };
99
+ exports.BODY_STATES = new Set(Object.values(exports.BodyState));
100
+ /**
101
+ * `Guard.complete()`'s return value on a `schemaVersion: 2` chain ONLY (0.9.0, docs/
102
+ * execution-binding spec section 1): whether the node was actually finalized, and — when it
103
+ * refused because calls are still pending an outcome — the `callId`s it is waiting on. On a
104
+ * `schemaVersion: 1` chain (the default), `complete()` returns a plain `boolean`,
105
+ * byte-and-type IDENTICAL to every release before 0.9.0 — v1 never gained pending-call
106
+ * awareness, so there is nothing new for it to report and no reason to change its return type
107
+ * (mirrors attenu-guard Python's merge-gate fix restoring this exact split).
108
+ *
109
+ * Python's v2 return value is bool-coercible via `__bool__`, so `if guard.complete():` keeps
110
+ * reading naturally there. JavaScript has no such hook for `if` — `ToBoolean` on any object is
111
+ * unconditionally `true`, so on v2 an `if (guard.complete())` check cannot be bridged to `false`
112
+ * no matter what this class defines, and neither can `assert.equal`/`assert.strictEqual` from
113
+ * `node:assert/strict` (no coercion at all). What CAN be bridged: `valueOf()` returns
114
+ * `.completed`, so contexts that genuinely coerce via `==`/`!=` (loose equality), template
115
+ * literals, and arithmetic still read as a boolean. On v2, read `.completed` explicitly wherever
116
+ * you would have written `if (guard.complete())`.
117
+ */
118
+ class CompletionResult {
119
+ completed;
120
+ pendingCallIds;
121
+ constructor(completed, pendingCallIds = []) {
122
+ this.completed = completed;
123
+ this.pendingCallIds = pendingCallIds;
124
+ }
125
+ valueOf() {
126
+ return this.completed;
127
+ }
128
+ toString() {
129
+ return String(this.completed);
130
+ }
131
+ }
132
+ exports.CompletionResult = CompletionResult;
57
133
  /** One specific cause of a denial. */
58
134
  class Reason {
59
135
  code;
@@ -107,10 +183,18 @@ class Decision {
107
183
  allowed;
108
184
  reasons;
109
185
  determiningNode;
110
- constructor(allowed, reasons = [], determiningNode = null) {
186
+ callId;
187
+ constructor(allowed, reasons = [], determiningNode = null,
188
+ /**
189
+ * 0.9.0 execution binding: set only on a `schemaVersion: 2` chain (`Guard.check` /
190
+ * `Guard.recordDenial`); `null` on schema-version-1 chains and on any `Decision` built
191
+ * outside a `Guard` transition (`wouldAllow`, tests).
192
+ */
193
+ callId = null) {
111
194
  this.allowed = allowed;
112
195
  this.reasons = reasons;
113
196
  this.determiningNode = determiningNode;
197
+ this.callId = callId;
114
198
  }
115
199
  /** A single human-readable line — for logs, CLIs and error messages. */
116
200
  explain() {
@@ -120,12 +204,24 @@ class Decision {
120
204
  return "denied";
121
205
  return "denied: " + this.reasons.map((r) => r.toString()).join("; ");
122
206
  }
207
+ /**
208
+ * `callId` is included only when set (a `schemaVersion: 2` chain's `check`/`recordDenial`) — a
209
+ * v1 Decision's serialized shape stays byte-and-key identical to every release before 0.9.0,
210
+ * never gaining a `call_id: null` key it never had.
211
+ */
123
212
  toDict() {
124
- return {
213
+ const d = {
125
214
  allowed: this.allowed,
126
215
  reasons: this.reasons.map((r) => r.toDict()),
127
216
  determining_node: this.determiningNode,
128
217
  };
218
+ if (this.callId !== null)
219
+ d["call_id"] = this.callId;
220
+ return d;
221
+ }
222
+ /** A copy of this Decision with `callId` set — mirrors Python's `dataclasses.replace`. */
223
+ withCallId(callId) {
224
+ return new Decision(this.allowed, this.reasons, this.determiningNode, callId);
129
225
  }
130
226
  static allow(node = null) {
131
227
  return new Decision(true, [], node);
@@ -1 +1 @@
1
- {"version":3,"file":"reasons.js","sourceRoot":"","sources":["../../src/reasons.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reasons.js","sourceRoot":"","sources":["../../src/reasons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * version.ts — the package version, as its own leaf module.
3
+ *
4
+ * guard.ts needs this (to attribute a guard-supplied `adapter.version` on a bare `check()` with
5
+ * no wrapper — see the "Execution binding" section of guard.ts's module doc comment) and is
6
+ * itself imported BY index.ts, so index.ts cannot be the source: `guard.ts -> index.ts ->
7
+ * guard.ts` would be circular. A one-constant leaf module has nothing to be circular with.
8
+ */
9
+ export declare const VERSION = "0.3.0";
10
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,UAAU,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VERSION = void 0;
4
+ /**
5
+ * version.ts — the package version, as its own leaf module.
6
+ *
7
+ * guard.ts needs this (to attribute a guard-supplied `adapter.version` on a bare `check()` with
8
+ * no wrapper — see the "Execution binding" section of guard.ts's module doc comment) and is
9
+ * itself imported BY index.ts, so index.ts cannot be the source: `guard.ts -> index.ts ->
10
+ * guard.ts` would be circular. A one-constant leaf module has nothing to be circular with.
11
+ */
12
+ exports.VERSION = "0.3.0";
13
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACU,QAAA,OAAO,GAAG,OAAO,CAAC"}
@@ -35,6 +35,22 @@
35
35
  * this adapter does not prescribe which. It guarantees only that the tool body
36
36
  * never executes on a denial.
37
37
  *
38
+ * ## Execution binding (0.9.0, reference wiring for this adapter)
39
+ *
40
+ * When `guard`'s chain was issued with `schemaVersion: 2` (see `Guard.issue`), `guardNode` also
41
+ * passes `capture`/`adapter`/`authorizedParams` to `check` and calls `guard.recordOutcome` once
42
+ * the wrapped callable finishes — `Capture.WRAPPER_ASYNC` when `fn` is a genuine `async function`
43
+ * (`fn.constructor.name === "AsyncFunction"`, the same test Python's `inspect.iscoroutinefunction`
44
+ * makes), `Capture.WRAPPER_SYNC` otherwise. `authorizedParams`/`invokedParams` are
45
+ * `{args: [...]}` built from exactly what the wrapped callable is called with — JavaScript has no
46
+ * separate `kwargs`, so unlike the Python adapter's `{args, kwargs}` this carries only `args`; a
47
+ * LangGraph.js node in any case receives one state object, not Python's `(*args, **kwargs)`. They
48
+ * are unchanged between the two observations here, since this decorator itself never mutates
49
+ * them; a framework that DOES mutate arguments between authorization and invocation is where a
50
+ * real substitution would become visible. On a `schemaVersion: 1` chain (the default), this
51
+ * adapter behaves exactly as it did before 0.9.0: no `capture`/`authorizedParams`, no
52
+ * `recordOutcome` call. Every other framework adapter is unchanged in this release.
53
+ *
38
54
  * ## Delegation
39
55
  *
40
56
  * Handing work to a sub-agent is the delegation moment. `delegateTo` mints the
@@ -52,7 +68,7 @@
52
68
  import { type Guard } from "../guard.js";
53
69
  import type { Authority } from "../authority.js";
54
70
  import type { Context } from "../ceilings.js";
55
- import type { Decision } from "../reasons.js";
71
+ import { type Decision } from "../reasons.js";
56
72
  /** Options shared by every guarded wrapper. */
57
73
  export interface GuardOptions {
58
74
  /**
@@ -81,7 +97,9 @@ export type GuardedNode<F extends (...args: any[]) => any> = F & {
81
97
  * `guard.check(toolScope, {context, tool})`. On a denial this throws
82
98
  * `AuthorityDenied` and the wrapped callable is NEVER invoked. Otherwise it
83
99
  * calls through with the original arguments and returns the result unchanged —
84
- * including a promise, which is passed along untouched.
100
+ * including a promise, which is passed along untouched. On a `schemaVersion: 2`
101
+ * guard, also binds the call's outcome via `guard.recordOutcome` — see the
102
+ * module doc comment's "Execution binding" section.
85
103
  *
86
104
  * Use the Guard for the SPECIFIC agent this callable belongs to, not the
87
105
  * orchestrator's broader one, so a denial reflects that node's real, narrowed
@@ -125,7 +143,13 @@ export interface GuardToolOptions extends GuardOptions {
125
143
  * The context function receives the raw invoke arguments. `ToolNode` passes a
126
144
  * tool call object, so the arguments the model proposed are at `input.args`;
127
145
  * a direct `tool.invoke({...})` passes them at the top level. `toolArgs` below
128
- * reads either shape.
146
+ * reads either shape — and, on a `schemaVersion: 2` guard, feeds a single
147
+ * IMMUTABLE snapshot taken BEFORE `tool.invoke` runs, reused for both
148
+ * `authorizedParams` and `invokedParams`: this adapter's closest analogue of "the
149
+ * exact tool-call JSON object" the execution-binding spec names (see the module
150
+ * doc comment's "Execution binding" section; this is the one construct in this
151
+ * adapter that wraps a tool BODY, so it is the reference wiring for
152
+ * `recordOutcome`, mirroring `guardNode` above).
129
153
  */
130
154
  export declare function guardTool<T extends ToolLike>(guard: Guard, tool: T, options?: GuardToolOptions): T;
131
155
  /** Marker properties, so a caller can tell a guarded tool from a bare one. */
@@ -1 +1 @@
1
- {"version":3,"file":"langgraph.d.ts","sourceRoot":"","sources":["../../../src/adapters/langgraph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"langgraph.d.ts","sourceRoot":"","sources":["../../../src/adapters/langgraph.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,22 @@
35
35
  * this adapter does not prescribe which. It guarantees only that the tool body
36
36
  * never executes on a denial.
37
37
  *
38
+ * ## Execution binding (0.9.0, reference wiring for this adapter)
39
+ *
40
+ * When `guard`'s chain was issued with `schemaVersion: 2` (see `Guard.issue`), `guardNode` also
41
+ * passes `capture`/`adapter`/`authorizedParams` to `check` and calls `guard.recordOutcome` once
42
+ * the wrapped callable finishes — `Capture.WRAPPER_ASYNC` when `fn` is a genuine `async function`
43
+ * (`fn.constructor.name === "AsyncFunction"`, the same test Python's `inspect.iscoroutinefunction`
44
+ * makes), `Capture.WRAPPER_SYNC` otherwise. `authorizedParams`/`invokedParams` are
45
+ * `{args: [...]}` built from exactly what the wrapped callable is called with — JavaScript has no
46
+ * separate `kwargs`, so unlike the Python adapter's `{args, kwargs}` this carries only `args`; a
47
+ * LangGraph.js node in any case receives one state object, not Python's `(*args, **kwargs)`. They
48
+ * are unchanged between the two observations here, since this decorator itself never mutates
49
+ * them; a framework that DOES mutate arguments between authorization and invocation is where a
50
+ * real substitution would become visible. On a `schemaVersion: 1` chain (the default), this
51
+ * adapter behaves exactly as it did before 0.9.0: no `capture`/`authorizedParams`, no
52
+ * `recordOutcome` call. Every other framework adapter is unchanged in this release.
53
+ *
38
54
  * ## Delegation
39
55
  *
40
56
  * Handing work to a sub-agent is the delegation moment. `delegateTo` mints the
@@ -50,6 +66,71 @@
50
66
  * const tools = guardTools(researcher, [crmQuery], { scopes: { crm_query: "crm.read" } });
51
67
  */
52
68
  import { AuthorityDenied } from "../guard.js";
69
+ import { BodyState, Capture } from "../reasons.js";
70
+ import { VERSION } from "../version.js";
71
+ /**
72
+ * An IMMUTABLE snapshot of the call's arguments, taken BEFORE the wrapped callable runs and
73
+ * reused for BOTH `authorizedParams` (`check`) and `invokedParams` (`recordOutcome`) — so a
74
+ * callable that mutates its own inputs in place cannot make this adapter observe two different
75
+ * values for what was actually one call's arguments (see the module doc comment's "Execution
76
+ * binding" section; no `kwargs` — JavaScript has no separate keyword-argument bag).
77
+ */
78
+ function snapshotParams(args) {
79
+ const raw = { args: [...args] };
80
+ try {
81
+ return structuredClone(raw);
82
+ }
83
+ catch {
84
+ // Best-effort: something in here isn't structured-cloneable (a live socket, a function, ...).
85
+ // Fall back to the shallow copy — a residual risk only if THAT specific object is later
86
+ // mutated in place, a rare edge case documented here rather than silently claimed away.
87
+ return raw;
88
+ }
89
+ }
90
+ /**
91
+ * `true` if `result` is a generator/async-generator/promise-like value whose consumption this
92
+ * wrapper does not itself observe — spec's `deferred`: "the record covers the call, not the
93
+ * eventual exhaustion." A promise counts too (the closest JavaScript analogue of Python's
94
+ * `asyncio.Future`/`concurrent.futures.Future`): the async branch here already `await`s `fn`
95
+ * itself, so a bare promise surfacing as `result` there means `fn` returned ANOTHER promise
96
+ * without awaiting it, which is exactly the same "not actually consumed" situation.
97
+ */
98
+ function isDeferredResult(result) {
99
+ if (result === null || typeof result !== "object")
100
+ return false;
101
+ const r = result;
102
+ if (typeof r["next"] === "function" && typeof r[Symbol.iterator] === "function")
103
+ return true;
104
+ if (typeof r["next"] === "function" && typeof r[Symbol.asyncIterator] === "function")
105
+ return true;
106
+ if (typeof r["then"] === "function")
107
+ return true;
108
+ return false;
109
+ }
110
+ function bodyStateFor(result) {
111
+ return isDeferredResult(result) ? BodyState.DEFERRED : BodyState.RETURNED;
112
+ }
113
+ /**
114
+ * `true` for the JavaScript analogue of Python's `asyncio.CancelledError`: an `AbortController`/
115
+ * `AbortSignal`-driven abort, surfaced as a `DOMException`/`Error` named `"AbortError"` — the
116
+ * standard shape Node and the Fetch/Web platform both use. There is no single universal async
117
+ * cancellation exception type in JavaScript the way `asyncio.CancelledError` is in Python, so this
118
+ * is the best-grounded, most widely applicable translation rather than a exact 1:1 port.
119
+ */
120
+ function isAbortError(exc) {
121
+ return exc instanceof Error && exc.name === "AbortError";
122
+ }
123
+ /** The class/constructor name of a thrown value — JavaScript's nearest analogue of Python's `type(exc).__name__`. */
124
+ function errorCodeOf(exc) {
125
+ if (exc instanceof Error)
126
+ return exc.constructor.name || "Error";
127
+ if (exc === null)
128
+ return "null";
129
+ return typeof exc === "object" ? "object" : typeof exc;
130
+ }
131
+ function elapsedMs(start) {
132
+ return Math.round(performance.now() - start);
133
+ }
53
134
  /**
54
135
  * Wrap a callable so every call is authorized through `guard` first.
55
136
  *
@@ -57,7 +138,9 @@ import { AuthorityDenied } from "../guard.js";
57
138
  * `guard.check(toolScope, {context, tool})`. On a denial this throws
58
139
  * `AuthorityDenied` and the wrapped callable is NEVER invoked. Otherwise it
59
140
  * calls through with the original arguments and returns the result unchanged —
60
- * including a promise, which is passed along untouched.
141
+ * including a promise, which is passed along untouched. On a `schemaVersion: 2`
142
+ * guard, also binds the call's outcome via `guard.recordOutcome` — see the
143
+ * module doc comment's "Execution binding" section.
61
144
  *
62
145
  * Use the Guard for the SPECIFIC agent this callable belongs to, not the
63
146
  * orchestrator's broader one, so a denial reflects that node's real, narrowed
@@ -65,18 +148,102 @@ import { AuthorityDenied } from "../guard.js";
65
148
  */
66
149
  export function guardNode(guard, toolScope, fn, options = {}) {
67
150
  const resolvedTool = options.tool !== undefined ? options.tool : (fn.name || null);
68
- const wrapped = function (...args) {
151
+ const isAsyncFn = fn.constructor.name === "AsyncFunction";
152
+ // guard.schemaVersion never changes for a guard's lifetime, so it is safe (and correct: a
153
+ // wrapper's sync-vs-async SHAPE must be fixed at definition time, not per-call) to decide it
154
+ // once, here, at decoration time.
155
+ const v2 = guard.schemaVersion === 2;
156
+ const capture = isAsyncFn ? Capture.WRAPPER_ASYNC : Capture.WRAPPER_SYNC;
157
+ const adapterInfo = {
158
+ module: "attenu-guard/adapters/langgraph",
159
+ version: VERSION,
160
+ hookPath: `guardNode:${fn.name || toolScope}`,
161
+ };
162
+ function authorize(args, snapshot) {
69
163
  const context = options.contextFn ? options.contextFn(...args) : {};
164
+ const extra = v2 ? { capture, adapter: adapterInfo, authorizedParams: snapshot ?? undefined } : {};
70
165
  const decision = guard.check(toolScope, {
71
166
  context,
72
167
  tool: resolvedTool,
73
168
  disposition: options.disposition ?? null,
74
169
  metered: options.metered ?? false,
170
+ ...extra,
75
171
  });
76
172
  if (!decision.allowed)
77
173
  throw new AuthorityDenied(decision);
78
- return fn.apply(this, args);
79
- };
174
+ return decision;
175
+ }
176
+ let wrapped;
177
+ if (isAsyncFn && v2) {
178
+ wrapped = (async function (...args) {
179
+ const snapshot = snapshotParams(args);
180
+ const decision = authorize(args, snapshot);
181
+ const start = performance.now();
182
+ try {
183
+ const result = await fn.apply(this, args);
184
+ guard.recordOutcome(decision.callId, bodyStateFor(result), {
185
+ invokedParams: snapshot,
186
+ durationMs: elapsedMs(start),
187
+ });
188
+ return result;
189
+ }
190
+ catch (exc) {
191
+ // The wrapper stopped observing while the body may still run — exactly spec's
192
+ // `abandoned`, not `raised`. Still re-thrown: cancellation must propagate normally.
193
+ if (isAbortError(exc)) {
194
+ guard.recordOutcome(decision.callId, BodyState.ABANDONED, {
195
+ invokedParams: snapshot,
196
+ durationMs: elapsedMs(start),
197
+ });
198
+ }
199
+ else {
200
+ guard.recordOutcome(decision.callId, BodyState.RAISED, {
201
+ errorCode: errorCodeOf(exc),
202
+ invokedParams: snapshot,
203
+ durationMs: elapsedMs(start),
204
+ });
205
+ }
206
+ throw exc;
207
+ }
208
+ });
209
+ }
210
+ else if (isAsyncFn) {
211
+ // v1 (schemaVersion: 1, the default): EXACTLY the pre-0.9.0 shape — a plain SYNC wrapper
212
+ // that authorizes, then returns fn(...args) UNAWAITED. The caller awaits the returned
213
+ // promise itself, as it always has; `wrapped` is never itself an async function on v1, even
214
+ // when `fn` is.
215
+ wrapped = (function (...args) {
216
+ authorize(args, null);
217
+ return fn.apply(this, args);
218
+ });
219
+ }
220
+ else {
221
+ wrapped = (function (...args) {
222
+ if (!v2) {
223
+ authorize(args, null);
224
+ return fn.apply(this, args);
225
+ }
226
+ const snapshot = snapshotParams(args);
227
+ const decision = authorize(args, snapshot);
228
+ const start = performance.now();
229
+ try {
230
+ const result = fn.apply(this, args);
231
+ guard.recordOutcome(decision.callId, bodyStateFor(result), {
232
+ invokedParams: snapshot,
233
+ durationMs: elapsedMs(start),
234
+ });
235
+ return result;
236
+ }
237
+ catch (exc) {
238
+ guard.recordOutcome(decision.callId, BodyState.RAISED, {
239
+ errorCode: errorCodeOf(exc),
240
+ invokedParams: snapshot,
241
+ durationMs: elapsedMs(start),
242
+ });
243
+ throw exc;
244
+ }
245
+ });
246
+ }
80
247
  Object.defineProperties(wrapped, {
81
248
  name: { value: fn.name, configurable: true },
82
249
  guard: { value: guard, enumerable: true },
@@ -106,26 +273,138 @@ export function addGuardedNode(graph, name, guard, toolScope, fn, options = {})
106
273
  * The context function receives the raw invoke arguments. `ToolNode` passes a
107
274
  * tool call object, so the arguments the model proposed are at `input.args`;
108
275
  * a direct `tool.invoke({...})` passes them at the top level. `toolArgs` below
109
- * reads either shape.
276
+ * reads either shape — and, on a `schemaVersion: 2` guard, feeds a single
277
+ * IMMUTABLE snapshot taken BEFORE `tool.invoke` runs, reused for both
278
+ * `authorizedParams` and `invokedParams`: this adapter's closest analogue of "the
279
+ * exact tool-call JSON object" the execution-binding spec names (see the module
280
+ * doc comment's "Execution binding" section; this is the one construct in this
281
+ * adapter that wraps a tool BODY, so it is the reference wiring for
282
+ * `recordOutcome`, mirroring `guardNode` above).
110
283
  */
111
284
  export function guardTool(guard, tool, options = {}) {
112
285
  const scope = options.scope ?? tool.name;
113
286
  const label = options.tool !== undefined ? options.tool : tool.name;
114
- const guardedInvoke = (input, config) => {
287
+ const isAsyncInvoke = tool.invoke.constructor.name === "AsyncFunction";
288
+ // guard.schemaVersion never changes for a guard's lifetime — decided once, at wrap time, same
289
+ // as guardNode.
290
+ const v2 = guard.schemaVersion === 2;
291
+ const capture = isAsyncInvoke ? Capture.WRAPPER_ASYNC : Capture.WRAPPER_SYNC;
292
+ const adapterInfo = {
293
+ module: "attenu-guard/adapters/langgraph",
294
+ version: VERSION,
295
+ hookPath: `guardTool:${tool.name}`,
296
+ };
297
+ function snapshotToolParams(input) {
298
+ const raw = toolArgs(input);
299
+ try {
300
+ return structuredClone(raw);
301
+ }
302
+ catch {
303
+ return raw;
304
+ }
305
+ }
306
+ function authorize(input, config, snapshot) {
115
307
  const context = options.contextFn ? options.contextFn(input, config) : {};
308
+ const extra = v2 ? { capture, adapter: adapterInfo, authorizedParams: snapshot ?? undefined } : {};
116
309
  const decision = guard.check(scope, {
117
310
  context,
118
311
  tool: label,
119
312
  disposition: options.disposition ?? null,
120
313
  metered: options.metered ?? false,
314
+ ...extra,
121
315
  });
122
- if (!decision.allowed) {
123
- if (options.onDenied)
124
- return options.onDenied(decision, input);
316
+ if (!decision.allowed)
125
317
  throw new AuthorityDenied(decision);
318
+ return decision;
319
+ }
320
+ /** `authorize`, but returns `{denied: ...}` instead of throwing when `onDenied` is set. */
321
+ function authorizeOrDenied(input, config, snapshot) {
322
+ try {
323
+ return { decision: authorize(input, config, snapshot) };
126
324
  }
127
- return tool.invoke(input, config);
128
- };
325
+ catch (exc) {
326
+ if (exc instanceof AuthorityDenied && options.onDenied) {
327
+ return { denied: options.onDenied(exc.decision, input) };
328
+ }
329
+ throw exc;
330
+ }
331
+ }
332
+ let guardedInvoke;
333
+ if (isAsyncInvoke && v2) {
334
+ guardedInvoke = async (input, config) => {
335
+ const snapshot = snapshotToolParams(input);
336
+ const outcome = authorizeOrDenied(input, config, snapshot);
337
+ if ("denied" in outcome)
338
+ return outcome.denied;
339
+ const start = performance.now();
340
+ try {
341
+ const result = await tool.invoke(input, config);
342
+ guard.recordOutcome(outcome.decision.callId, bodyStateFor(result), {
343
+ invokedParams: snapshot,
344
+ durationMs: elapsedMs(start),
345
+ });
346
+ return result;
347
+ }
348
+ catch (exc) {
349
+ if (isAbortError(exc)) {
350
+ guard.recordOutcome(outcome.decision.callId, BodyState.ABANDONED, {
351
+ invokedParams: snapshot,
352
+ durationMs: elapsedMs(start),
353
+ });
354
+ }
355
+ else {
356
+ guard.recordOutcome(outcome.decision.callId, BodyState.RAISED, {
357
+ errorCode: errorCodeOf(exc),
358
+ invokedParams: snapshot,
359
+ durationMs: elapsedMs(start),
360
+ });
361
+ }
362
+ throw exc;
363
+ }
364
+ };
365
+ }
366
+ else if (isAsyncInvoke) {
367
+ // v1: EXACTLY the pre-0.9.0 shape — a plain SYNC function that authorizes, then returns
368
+ // tool.invoke(...) UNAWAITED (the caller — typically ToolNode's own `await` — consumes the
369
+ // returned promise itself, as it always has).
370
+ guardedInvoke = (input, config) => {
371
+ const outcome = authorizeOrDenied(input, config, null);
372
+ if ("denied" in outcome)
373
+ return outcome.denied;
374
+ return tool.invoke(input, config);
375
+ };
376
+ }
377
+ else {
378
+ guardedInvoke = (input, config) => {
379
+ if (!v2) {
380
+ const outcome = authorizeOrDenied(input, config, null);
381
+ if ("denied" in outcome)
382
+ return outcome.denied;
383
+ return tool.invoke(input, config);
384
+ }
385
+ const snapshot = snapshotToolParams(input);
386
+ const outcome = authorizeOrDenied(input, config, snapshot);
387
+ if ("denied" in outcome)
388
+ return outcome.denied;
389
+ const start = performance.now();
390
+ try {
391
+ const result = tool.invoke(input, config);
392
+ guard.recordOutcome(outcome.decision.callId, bodyStateFor(result), {
393
+ invokedParams: snapshot,
394
+ durationMs: elapsedMs(start),
395
+ });
396
+ return result;
397
+ }
398
+ catch (exc) {
399
+ guard.recordOutcome(outcome.decision.callId, BodyState.RAISED, {
400
+ errorCode: errorCodeOf(exc),
401
+ invokedParams: snapshot,
402
+ durationMs: elapsedMs(start),
403
+ });
404
+ throw exc;
405
+ }
406
+ };
407
+ }
129
408
  return new Proxy(tool, {
130
409
  get(target, prop, receiver) {
131
410
  if (prop === "invoke")