attenu-guard 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.
- package/CHANGELOG.md +46 -0
- package/LICENSE +202 -0
- package/README.md +200 -0
- package/SECURITY.md +34 -0
- package/bin/attenu-guard.js +4 -0
- package/dist/cjs/adapters/langgraph.d.ts +174 -0
- package/dist/cjs/adapters/langgraph.d.ts.map +1 -0
- package/dist/cjs/adapters/langgraph.js +242 -0
- package/dist/cjs/adapters/langgraph.js.map +1 -0
- package/dist/cjs/audit.d.ts +82 -0
- package/dist/cjs/audit.d.ts.map +1 -0
- package/dist/cjs/audit.js +195 -0
- package/dist/cjs/audit.js.map +1 -0
- package/dist/cjs/authority.d.ts +114 -0
- package/dist/cjs/authority.d.ts.map +1 -0
- package/dist/cjs/authority.js +238 -0
- package/dist/cjs/authority.js.map +1 -0
- package/dist/cjs/canonical.d.ts +91 -0
- package/dist/cjs/canonical.d.ts.map +1 -0
- package/dist/cjs/canonical.js +372 -0
- package/dist/cjs/canonical.js.map +1 -0
- package/dist/cjs/ceilings.d.ts +211 -0
- package/dist/cjs/ceilings.d.ts.map +1 -0
- package/dist/cjs/ceilings.js +476 -0
- package/dist/cjs/ceilings.js.map +1 -0
- package/dist/cjs/chain.d.ts +106 -0
- package/dist/cjs/chain.d.ts.map +1 -0
- package/dist/cjs/chain.js +0 -0
- package/dist/cjs/chain.js.map +1 -0
- package/dist/cjs/cli.d.ts +16 -0
- package/dist/cjs/cli.d.ts.map +1 -0
- package/dist/cjs/cli.js +100 -0
- package/dist/cjs/cli.js.map +1 -0
- package/dist/cjs/evidence.d.ts +163 -0
- package/dist/cjs/evidence.d.ts.map +1 -0
- package/dist/cjs/evidence.js +409 -0
- package/dist/cjs/evidence.js.map +1 -0
- package/dist/cjs/guard.d.ts +186 -0
- package/dist/cjs/guard.d.ts.map +1 -0
- package/dist/cjs/guard.js +432 -0
- package/dist/cjs/guard.js.map +1 -0
- package/dist/cjs/index.d.ts +52 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +99 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/reasons.d.ts +93 -0
- package/dist/cjs/reasons.d.ts.map +1 -0
- package/dist/cjs/reasons.js +139 -0
- package/dist/cjs/reasons.js.map +1 -0
- package/dist/cjs/strikes.d.ts +24 -0
- package/dist/cjs/strikes.d.ts.map +1 -0
- package/dist/cjs/strikes.js +30 -0
- package/dist/cjs/strikes.js.map +1 -0
- package/dist/cjs/wire.d.ts +83 -0
- package/dist/cjs/wire.d.ts.map +1 -0
- package/dist/cjs/wire.js +165 -0
- package/dist/cjs/wire.js.map +1 -0
- package/dist/esm/adapters/langgraph.d.ts +174 -0
- package/dist/esm/adapters/langgraph.d.ts.map +1 -0
- package/dist/esm/adapters/langgraph.js +199 -0
- package/dist/esm/adapters/langgraph.js.map +1 -0
- package/dist/esm/audit.d.ts +82 -0
- package/dist/esm/audit.d.ts.map +1 -0
- package/dist/esm/audit.js +189 -0
- package/dist/esm/audit.js.map +1 -0
- package/dist/esm/authority.d.ts +114 -0
- package/dist/esm/authority.d.ts.map +1 -0
- package/dist/esm/authority.js +233 -0
- package/dist/esm/authority.js.map +1 -0
- package/dist/esm/canonical.d.ts +91 -0
- package/dist/esm/canonical.d.ts.map +1 -0
- package/dist/esm/canonical.js +359 -0
- package/dist/esm/canonical.js.map +1 -0
- package/dist/esm/ceilings.d.ts +211 -0
- package/dist/esm/ceilings.d.ts.map +1 -0
- package/dist/esm/ceilings.js +460 -0
- package/dist/esm/ceilings.js.map +1 -0
- package/dist/esm/chain.d.ts +106 -0
- package/dist/esm/chain.d.ts.map +1 -0
- package/dist/esm/chain.js +0 -0
- package/dist/esm/chain.js.map +1 -0
- package/dist/esm/cli.d.ts +16 -0
- package/dist/esm/cli.d.ts.map +1 -0
- package/dist/esm/cli.js +97 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/evidence.d.ts +163 -0
- package/dist/esm/evidence.d.ts.map +1 -0
- package/dist/esm/evidence.js +398 -0
- package/dist/esm/evidence.js.map +1 -0
- package/dist/esm/guard.d.ts +186 -0
- package/dist/esm/guard.d.ts.map +1 -0
- package/dist/esm/guard.js +427 -0
- package/dist/esm/guard.js.map +1 -0
- package/dist/esm/index.d.ts +52 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +42 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/reasons.d.ts +93 -0
- package/dist/esm/reasons.d.ts.map +1 -0
- package/dist/esm/reasons.js +134 -0
- package/dist/esm/reasons.js.map +1 -0
- package/dist/esm/strikes.d.ts +24 -0
- package/dist/esm/strikes.d.ts.map +1 -0
- package/dist/esm/strikes.js +26 -0
- package/dist/esm/strikes.js.map +1 -0
- package/dist/esm/wire.d.ts +83 -0
- package/dist/esm/wire.d.ts.map +1 -0
- package/dist/esm/wire.js +159 -0
- package/dist/esm/wire.js.map +1 -0
- package/package.json +89 -0
package/dist/cjs/wire.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* wire.ts — the signing and verification backends.
|
|
4
|
+
*
|
|
5
|
+
* Anchors and delegation tokens are signed over canonical JSON bytes. Two
|
|
6
|
+
* backends ship, matching the Python library's `attenu_guard.wire`:
|
|
7
|
+
*
|
|
8
|
+
* `HS256TestSigner` stdlib HMAC-SHA256, for tests, examples and local dev;
|
|
9
|
+
* `Ed25519Signer` the production signer the Internet-Draft requires, with
|
|
10
|
+
* `Ed25519Verifier` for anyone holding only the public half.
|
|
11
|
+
*
|
|
12
|
+
* Everything here is built on Node's own `node:crypto`, so the package keeps
|
|
13
|
+
* zero runtime dependencies. Ed25519 keys are wrapped in the fixed DER prefixes
|
|
14
|
+
* below rather than pulled from a library.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Ed25519Verifier = exports.Ed25519Signer = exports.HS256TestSigner = void 0;
|
|
18
|
+
const node_crypto_1 = require("node:crypto");
|
|
19
|
+
/**
|
|
20
|
+
* stdlib HMAC-SHA256 signer — the default for tests, examples and local dev,
|
|
21
|
+
* because it needs no key management at all.
|
|
22
|
+
*
|
|
23
|
+
* NOT FOR PRODUCTION. HMAC is symmetric: `sign` and `verify` use the identical
|
|
24
|
+
* secret, so anyone able to verify a bundle is also able to forge one. That
|
|
25
|
+
* precludes the property the draft is built around — public offline
|
|
26
|
+
* verification at an untrusted enforcement point — because every verifier would
|
|
27
|
+
* have to hold the minting secret. Use `Ed25519Signer` in production; this
|
|
28
|
+
* class exists so the wire format and the interop fixtures can be exercised
|
|
29
|
+
* with no key distribution.
|
|
30
|
+
*/
|
|
31
|
+
class HS256TestSigner {
|
|
32
|
+
secret;
|
|
33
|
+
alg = "HS256";
|
|
34
|
+
kid;
|
|
35
|
+
constructor(secret, kid = "test") {
|
|
36
|
+
this.secret = secret;
|
|
37
|
+
this.kid = kid;
|
|
38
|
+
}
|
|
39
|
+
sign(signingInput) {
|
|
40
|
+
return (0, node_crypto_1.createHmac)("sha256", this.secret).update(signingInput).digest();
|
|
41
|
+
}
|
|
42
|
+
verify(signingInput, sig, _keyId) {
|
|
43
|
+
// `keyId` is accepted for parity with multi-key signers but not used for
|
|
44
|
+
// key selection: this signer holds exactly one secret. The comparison is
|
|
45
|
+
// constant-time to avoid a signature-forgery timing oracle.
|
|
46
|
+
const expected = this.sign(signingInput);
|
|
47
|
+
return expected.length === sig.length && (0, node_crypto_1.timingSafeEqual)(expected, sig);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.HS256TestSigner = HS256TestSigner;
|
|
51
|
+
// Fixed DER wrappers for raw Ed25519 keys (RFC 8410). A raw 32-byte public key
|
|
52
|
+
// becomes a SubjectPublicKeyInfo by prefixing the first; a raw 32-byte private
|
|
53
|
+
// key becomes a PKCS#8 PrivateKeyInfo by prefixing the second.
|
|
54
|
+
const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
55
|
+
const ED25519_PKCS8_PREFIX = Buffer.from("302e020100300506032b657004220420", "hex");
|
|
56
|
+
function publicKeyFromRaw(raw) {
|
|
57
|
+
if (raw.length !== 32) {
|
|
58
|
+
throw new Error(`an Ed25519 public key is 32 bytes, got ${raw.length}`);
|
|
59
|
+
}
|
|
60
|
+
return (0, node_crypto_1.createPublicKey)({
|
|
61
|
+
key: Buffer.concat([ED25519_SPKI_PREFIX, raw]),
|
|
62
|
+
format: "der",
|
|
63
|
+
type: "spki",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function privateKeyFromRaw(raw) {
|
|
67
|
+
if (raw.length !== 32) {
|
|
68
|
+
throw new Error(`an Ed25519 private key is 32 bytes, got ${raw.length}`);
|
|
69
|
+
}
|
|
70
|
+
return (0, node_crypto_1.createPrivateKey)({
|
|
71
|
+
key: Buffer.concat([ED25519_PKCS8_PREFIX, raw]),
|
|
72
|
+
format: "der",
|
|
73
|
+
type: "pkcs8",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function rawPublicBytes(key) {
|
|
77
|
+
const der = key.export({ format: "der", type: "spki" });
|
|
78
|
+
return Buffer.from(der.subarray(der.length - 32));
|
|
79
|
+
}
|
|
80
|
+
function rawPrivateBytes(key) {
|
|
81
|
+
const der = key.export({ format: "der", type: "pkcs8" });
|
|
82
|
+
return Buffer.from(der.subarray(der.length - 32));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The production signer the draft requires ("Implementations MUST support
|
|
86
|
+
* Ed25519"); JOSE alg "EdDSA". Public-key, so — unlike `HS256TestSigner` — a
|
|
87
|
+
* verifier never needs the signing secret, which is exactly what offline
|
|
88
|
+
* verification at an untrusted edge depends on.
|
|
89
|
+
*/
|
|
90
|
+
class Ed25519Signer {
|
|
91
|
+
alg = "EdDSA";
|
|
92
|
+
kid;
|
|
93
|
+
privateKey;
|
|
94
|
+
publicKey;
|
|
95
|
+
constructor(privateKey, kid = "ed25519-1") {
|
|
96
|
+
if (privateKey === undefined) {
|
|
97
|
+
const pair = (0, node_crypto_1.generateKeyPairSync)("ed25519");
|
|
98
|
+
this.privateKey = pair.privateKey;
|
|
99
|
+
this.publicKey = pair.publicKey;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
this.privateKey = privateKey;
|
|
103
|
+
this.publicKey = (0, node_crypto_1.createPublicKey)(privateKey);
|
|
104
|
+
}
|
|
105
|
+
this.kid = kid;
|
|
106
|
+
}
|
|
107
|
+
/** A signer wrapping a freshly generated keypair. */
|
|
108
|
+
static generate(kid = "ed25519-1") {
|
|
109
|
+
return new Ed25519Signer(undefined, kid);
|
|
110
|
+
}
|
|
111
|
+
/** Rebuild a signer from the 32 raw bytes `privateBytesRaw` produced. */
|
|
112
|
+
static fromPrivateBytes(raw, kid = "ed25519-1") {
|
|
113
|
+
return new Ed25519Signer(privateKeyFromRaw(raw), kid);
|
|
114
|
+
}
|
|
115
|
+
sign(signingInput) {
|
|
116
|
+
return (0, node_crypto_1.sign)(null, signingInput, this.privateKey);
|
|
117
|
+
}
|
|
118
|
+
verify(signingInput, sig, _keyId) {
|
|
119
|
+
try {
|
|
120
|
+
return (0, node_crypto_1.verify)(null, signingInput, this.publicKey, sig);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** The 32-byte raw public key — a trust anchor to distribute out of band. */
|
|
127
|
+
publicBytesRaw() {
|
|
128
|
+
return rawPublicBytes(this.publicKey);
|
|
129
|
+
}
|
|
130
|
+
/** The 32-byte raw private key — what a key FILE stores, never a repo. */
|
|
131
|
+
privateBytesRaw() {
|
|
132
|
+
return rawPrivateBytes(this.privateKey);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.Ed25519Signer = Ed25519Signer;
|
|
136
|
+
/**
|
|
137
|
+
* The public-key-only counterpart of `Ed25519Signer` — what a console, an
|
|
138
|
+
* auditor or an ingest server holds. It verifies anchors signed by the matching
|
|
139
|
+
* private key and can never sign: `sign` throws.
|
|
140
|
+
*/
|
|
141
|
+
class Ed25519Verifier {
|
|
142
|
+
alg = "EdDSA";
|
|
143
|
+
kid;
|
|
144
|
+
publicKey;
|
|
145
|
+
constructor(publicBytesRaw, kid = "ed25519-1") {
|
|
146
|
+
this.publicKey = publicKeyFromRaw(publicBytesRaw);
|
|
147
|
+
this.kid = kid;
|
|
148
|
+
}
|
|
149
|
+
sign(_signingInput) {
|
|
150
|
+
throw new Error("Ed25519Verifier holds no private key and cannot sign");
|
|
151
|
+
}
|
|
152
|
+
verify(signingInput, sig, _keyId) {
|
|
153
|
+
try {
|
|
154
|
+
return (0, node_crypto_1.verify)(null, signingInput, this.publicKey, sig);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
publicBytesRaw() {
|
|
161
|
+
return rawPublicBytes(this.publicKey);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.Ed25519Verifier = Ed25519Verifier;
|
|
165
|
+
//# sourceMappingURL=wire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.js","sourceRoot":"","sources":["../../src/wire.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/langgraph.ts — a thin LangGraph.js integration.
|
|
3
|
+
*
|
|
4
|
+
* The adapter is built in two layers, the same shape as the Python one:
|
|
5
|
+
*
|
|
6
|
+
* 1. The AUTHORIZATION-WRAPPING LOGIC is plain TypeScript. It wraps an
|
|
7
|
+
* arbitrary callable so that every call is authorized through a `Guard`
|
|
8
|
+
* first, and it does not import, require or reference `@langchain/langgraph`
|
|
9
|
+
* at all. That is deliberate: a LangGraph node is, by LangGraph's own
|
|
10
|
+
* convention, just a callable — `(state) => partialState` — so wrapping
|
|
11
|
+
* "any callable" is enough to wrap a node or a plain tool function alike,
|
|
12
|
+
* and it keeps the logic testable with no framework installed.
|
|
13
|
+
*
|
|
14
|
+
* 2. The one spot that touches the package itself (`isLangGraphAvailable`)
|
|
15
|
+
* imports it lazily. `addGuardedNode` takes an already-constructed graph
|
|
16
|
+
* and only calls `.addNode(name, callable)` on it, so it needs no import
|
|
17
|
+
* either.
|
|
18
|
+
*
|
|
19
|
+
* ## Which hook this uses, and why
|
|
20
|
+
*
|
|
21
|
+
* The Python adapter wraps node callables. LangGraph.js has no public
|
|
22
|
+
* before-tool-call hook: `ToolNode` resolves the tool itself and invokes it at
|
|
23
|
+
* `dist/prebuilt/tool_node.js:207` (`const output = await tool.invoke(toolCall,
|
|
24
|
+
* runtime)`), inside a `protected runTool`. So this adapter takes the
|
|
25
|
+
* TOOL-WRAPPING form: `guardTool` returns a stand-in for a tool object whose
|
|
26
|
+
* `invoke` authorizes first and calls through only on an allow. The stand-in
|
|
27
|
+
* keeps the original `name`, `description` and `schema`, which is all `ToolNode`
|
|
28
|
+
* looks at when it matches a tool call to a tool, so a wrapped tool drops
|
|
29
|
+
* straight into `new ToolNode([...])` or `createReactAgent({tools})`.
|
|
30
|
+
*
|
|
31
|
+
* A denial throws `AuthorityDenied` — the same error `Guard.enforce` throws —
|
|
32
|
+
* BEFORE the tool body runs, so a poisoned instruction or a runaway plan never
|
|
33
|
+
* reaches the call it is not authorized to make. A graph can catch it around
|
|
34
|
+
* `graph.invoke(...)`, or route around it with LangGraph's own error handling;
|
|
35
|
+
* this adapter does not prescribe which. It guarantees only that the tool body
|
|
36
|
+
* never executes on a denial.
|
|
37
|
+
*
|
|
38
|
+
* ## Delegation
|
|
39
|
+
*
|
|
40
|
+
* Handing work to a sub-agent is the delegation moment. `delegateTo` mints the
|
|
41
|
+
* child `Guard` at the handoff — before the sub-agent's graph is invoked — so
|
|
42
|
+
* the sub-agent's tools are guarded by an authority that is provably a subset of
|
|
43
|
+
* the parent's.
|
|
44
|
+
*
|
|
45
|
+
* const researcher = delegateTo(supervisor, {
|
|
46
|
+
* agentId: "researcher",
|
|
47
|
+
* request: new Authority({ scopes: ["crm.read"], ceilings: [new RowLimit(500)], ttl: 900 }),
|
|
48
|
+
* task: "summarise the Q3 pipeline",
|
|
49
|
+
* });
|
|
50
|
+
* const tools = guardTools(researcher, [crmQuery], { scopes: { crm_query: "crm.read" } });
|
|
51
|
+
*/
|
|
52
|
+
import { type Guard } from "../guard.js";
|
|
53
|
+
import type { Authority } from "../authority.js";
|
|
54
|
+
import type { Context } from "../ceilings.js";
|
|
55
|
+
import type { Decision } from "../reasons.js";
|
|
56
|
+
/** Options shared by every guarded wrapper. */
|
|
57
|
+
export interface GuardOptions {
|
|
58
|
+
/**
|
|
59
|
+
* Called with the exact arguments the wrapped callable received; returns the
|
|
60
|
+
* context for `guard.check`, e.g. `{rows: 5000, egress: "none"}`. Omitted
|
|
61
|
+
* means an empty context — fine for a scope-only check.
|
|
62
|
+
*/
|
|
63
|
+
contextFn?: (...args: any[]) => Context;
|
|
64
|
+
/** The `tool` label recorded on the ledger. Defaults to the callable's name. */
|
|
65
|
+
tool?: string | null;
|
|
66
|
+
/** What the caller knows about why this scope would be absent. */
|
|
67
|
+
disposition?: string | null;
|
|
68
|
+
/** Marks the call as consuming a metered resource. */
|
|
69
|
+
metered?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** A callable node, with the guard and scope it was wrapped with attached. */
|
|
72
|
+
export type GuardedNode<F extends (...args: any[]) => any> = F & {
|
|
73
|
+
readonly guard: Guard;
|
|
74
|
+
readonly toolScope: string;
|
|
75
|
+
readonly unwrapped: F;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Wrap a callable so every call is authorized through `guard` first.
|
|
79
|
+
*
|
|
80
|
+
* On call: the context is `contextFn(...args)` if given, else `{}`; then
|
|
81
|
+
* `guard.check(toolScope, {context, tool})`. On a denial this throws
|
|
82
|
+
* `AuthorityDenied` and the wrapped callable is NEVER invoked. Otherwise it
|
|
83
|
+
* calls through with the original arguments and returns the result unchanged —
|
|
84
|
+
* including a promise, which is passed along untouched.
|
|
85
|
+
*
|
|
86
|
+
* Use the Guard for the SPECIFIC agent this callable belongs to, not the
|
|
87
|
+
* orchestrator's broader one, so a denial reflects that node's real, narrowed
|
|
88
|
+
* authority.
|
|
89
|
+
*/
|
|
90
|
+
export declare function guardNode<F extends (...args: any[]) => any>(guard: Guard, toolScope: string, fn: F, options?: GuardOptions): GuardedNode<F>;
|
|
91
|
+
/** The minimum a LangGraph.js graph needs to expose for `addGuardedNode`. */
|
|
92
|
+
export interface GraphLike {
|
|
93
|
+
addNode(name: string, action: (...args: any[]) => any, ...rest: any[]): unknown;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Build the guarded wrapper and register it on a graph in one call.
|
|
97
|
+
*
|
|
98
|
+
* `graph` is structurally typed: it only needs `.addNode(name, callable)`,
|
|
99
|
+
* which is `StateGraph`'s real signature — so this helper needs no import of
|
|
100
|
+
* its own and stays testable against a plain stand-in graph.
|
|
101
|
+
*/
|
|
102
|
+
export declare function addGuardedNode<F extends (...args: any[]) => any>(graph: GraphLike, name: string, guard: Guard, toolScope: string, fn: F, options?: GuardOptions): GuardedNode<F>;
|
|
103
|
+
/** The shape `ToolNode` requires of a tool: a name and an `invoke`. */
|
|
104
|
+
export interface ToolLike {
|
|
105
|
+
name: string;
|
|
106
|
+
invoke(input: any, config?: any): any;
|
|
107
|
+
[key: string]: any;
|
|
108
|
+
}
|
|
109
|
+
export interface GuardToolOptions extends GuardOptions {
|
|
110
|
+
/** The scope this tool needs. Defaults to the tool's own name. */
|
|
111
|
+
scope?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Called when the guard denies the call, instead of throwing. Return the
|
|
114
|
+
* value the tool should yield — e.g. a `ToolMessage` explaining the refusal,
|
|
115
|
+
* so the model can see the denial and re-plan rather than the run aborting.
|
|
116
|
+
*/
|
|
117
|
+
onDenied?: (decision: Decision, input: any) => any;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Return a stand-in for `tool` whose `invoke` authorizes through `guard` before
|
|
121
|
+
* the tool body runs. Everything else — `name`, `description`, `schema`, any
|
|
122
|
+
* LangChain internals — is forwarded to the original, so `ToolNode` matches and
|
|
123
|
+
* runs it exactly as it would the unwrapped tool.
|
|
124
|
+
*
|
|
125
|
+
* The context function receives the raw invoke arguments. `ToolNode` passes a
|
|
126
|
+
* tool call object, so the arguments the model proposed are at `input.args`;
|
|
127
|
+
* a direct `tool.invoke({...})` passes them at the top level. `toolArgs` below
|
|
128
|
+
* reads either shape.
|
|
129
|
+
*/
|
|
130
|
+
export declare function guardTool<T extends ToolLike>(guard: Guard, tool: T, options?: GuardToolOptions): T;
|
|
131
|
+
/** Marker properties, so a caller can tell a guarded tool from a bare one. */
|
|
132
|
+
export declare const ATTENU_GUARD = "__attenuGuard";
|
|
133
|
+
export declare const ATTENU_SCOPE = "__attenuScope";
|
|
134
|
+
export interface GuardToolsOptions extends GuardOptions {
|
|
135
|
+
/** Tool name -> scope. A tool absent from the map uses its own name. */
|
|
136
|
+
scopes?: Record<string, string>;
|
|
137
|
+
/** Tool name -> context function, overriding the shared `contextFn`. */
|
|
138
|
+
contexts?: Record<string, (input: any, config?: any) => Context>;
|
|
139
|
+
onDenied?: (decision: Decision, input: any) => any;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Guard a whole tool list in one call — what you hand to `new ToolNode([...])`
|
|
143
|
+
* or `createReactAgent({tools})`.
|
|
144
|
+
*/
|
|
145
|
+
export declare function guardTools<T extends ToolLike>(guard: Guard, tools: readonly T[], options?: GuardToolsOptions): T[];
|
|
146
|
+
/**
|
|
147
|
+
* The arguments a tool call carries, whichever shape `invoke` was handed.
|
|
148
|
+
* `ToolNode` passes `{name, args, id, type}`; a direct call passes the args
|
|
149
|
+
* themselves. Useful inside a `contextFn`.
|
|
150
|
+
*/
|
|
151
|
+
export declare function toolArgs(input: any): Record<string, any>;
|
|
152
|
+
export interface DelegateOptions {
|
|
153
|
+
agentId: string;
|
|
154
|
+
request: Authority;
|
|
155
|
+
task: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Mint the child Guard for a sub-agent handoff. Handing work to a sub-agent IS
|
|
159
|
+
* the delegation moment: call this before the sub-agent's graph is invoked, so
|
|
160
|
+
* its tools are guarded by an authority that is provably a subset of the
|
|
161
|
+
* parent's. A request wider than the parent holds comes back narrowed.
|
|
162
|
+
*
|
|
163
|
+
* Throws `AuthorityError` if the handoff is structurally refused — the parent
|
|
164
|
+
* is revoked or expired, the agent is banned, or the chain's depth or fanout
|
|
165
|
+
* ceiling would be exceeded.
|
|
166
|
+
*/
|
|
167
|
+
export declare function delegateTo(parent: Guard, options: DelegateOptions): Guard;
|
|
168
|
+
/**
|
|
169
|
+
* True iff `@langchain/langgraph` is importable here. The import is lazy and
|
|
170
|
+
* happens only when this is called, so merely importing this adapter — or using
|
|
171
|
+
* `guardNode` / `guardTool` — never requires LangGraph to be installed.
|
|
172
|
+
*/
|
|
173
|
+
export declare function isLangGraphAvailable(): Promise<boolean>;
|
|
174
|
+
//# sourceMappingURL=langgraph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"langgraph.d.ts","sourceRoot":"","sources":["../../../src/adapters/langgraph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/langgraph.ts — a thin LangGraph.js integration.
|
|
3
|
+
*
|
|
4
|
+
* The adapter is built in two layers, the same shape as the Python one:
|
|
5
|
+
*
|
|
6
|
+
* 1. The AUTHORIZATION-WRAPPING LOGIC is plain TypeScript. It wraps an
|
|
7
|
+
* arbitrary callable so that every call is authorized through a `Guard`
|
|
8
|
+
* first, and it does not import, require or reference `@langchain/langgraph`
|
|
9
|
+
* at all. That is deliberate: a LangGraph node is, by LangGraph's own
|
|
10
|
+
* convention, just a callable — `(state) => partialState` — so wrapping
|
|
11
|
+
* "any callable" is enough to wrap a node or a plain tool function alike,
|
|
12
|
+
* and it keeps the logic testable with no framework installed.
|
|
13
|
+
*
|
|
14
|
+
* 2. The one spot that touches the package itself (`isLangGraphAvailable`)
|
|
15
|
+
* imports it lazily. `addGuardedNode` takes an already-constructed graph
|
|
16
|
+
* and only calls `.addNode(name, callable)` on it, so it needs no import
|
|
17
|
+
* either.
|
|
18
|
+
*
|
|
19
|
+
* ## Which hook this uses, and why
|
|
20
|
+
*
|
|
21
|
+
* The Python adapter wraps node callables. LangGraph.js has no public
|
|
22
|
+
* before-tool-call hook: `ToolNode` resolves the tool itself and invokes it at
|
|
23
|
+
* `dist/prebuilt/tool_node.js:207` (`const output = await tool.invoke(toolCall,
|
|
24
|
+
* runtime)`), inside a `protected runTool`. So this adapter takes the
|
|
25
|
+
* TOOL-WRAPPING form: `guardTool` returns a stand-in for a tool object whose
|
|
26
|
+
* `invoke` authorizes first and calls through only on an allow. The stand-in
|
|
27
|
+
* keeps the original `name`, `description` and `schema`, which is all `ToolNode`
|
|
28
|
+
* looks at when it matches a tool call to a tool, so a wrapped tool drops
|
|
29
|
+
* straight into `new ToolNode([...])` or `createReactAgent({tools})`.
|
|
30
|
+
*
|
|
31
|
+
* A denial throws `AuthorityDenied` — the same error `Guard.enforce` throws —
|
|
32
|
+
* BEFORE the tool body runs, so a poisoned instruction or a runaway plan never
|
|
33
|
+
* reaches the call it is not authorized to make. A graph can catch it around
|
|
34
|
+
* `graph.invoke(...)`, or route around it with LangGraph's own error handling;
|
|
35
|
+
* this adapter does not prescribe which. It guarantees only that the tool body
|
|
36
|
+
* never executes on a denial.
|
|
37
|
+
*
|
|
38
|
+
* ## Delegation
|
|
39
|
+
*
|
|
40
|
+
* Handing work to a sub-agent is the delegation moment. `delegateTo` mints the
|
|
41
|
+
* child `Guard` at the handoff — before the sub-agent's graph is invoked — so
|
|
42
|
+
* the sub-agent's tools are guarded by an authority that is provably a subset of
|
|
43
|
+
* the parent's.
|
|
44
|
+
*
|
|
45
|
+
* const researcher = delegateTo(supervisor, {
|
|
46
|
+
* agentId: "researcher",
|
|
47
|
+
* request: new Authority({ scopes: ["crm.read"], ceilings: [new RowLimit(500)], ttl: 900 }),
|
|
48
|
+
* task: "summarise the Q3 pipeline",
|
|
49
|
+
* });
|
|
50
|
+
* const tools = guardTools(researcher, [crmQuery], { scopes: { crm_query: "crm.read" } });
|
|
51
|
+
*/
|
|
52
|
+
import { AuthorityDenied } from "../guard.js";
|
|
53
|
+
/**
|
|
54
|
+
* Wrap a callable so every call is authorized through `guard` first.
|
|
55
|
+
*
|
|
56
|
+
* On call: the context is `contextFn(...args)` if given, else `{}`; then
|
|
57
|
+
* `guard.check(toolScope, {context, tool})`. On a denial this throws
|
|
58
|
+
* `AuthorityDenied` and the wrapped callable is NEVER invoked. Otherwise it
|
|
59
|
+
* calls through with the original arguments and returns the result unchanged —
|
|
60
|
+
* including a promise, which is passed along untouched.
|
|
61
|
+
*
|
|
62
|
+
* Use the Guard for the SPECIFIC agent this callable belongs to, not the
|
|
63
|
+
* orchestrator's broader one, so a denial reflects that node's real, narrowed
|
|
64
|
+
* authority.
|
|
65
|
+
*/
|
|
66
|
+
export function guardNode(guard, toolScope, fn, options = {}) {
|
|
67
|
+
const resolvedTool = options.tool !== undefined ? options.tool : (fn.name || null);
|
|
68
|
+
const wrapped = function (...args) {
|
|
69
|
+
const context = options.contextFn ? options.contextFn(...args) : {};
|
|
70
|
+
const decision = guard.check(toolScope, {
|
|
71
|
+
context,
|
|
72
|
+
tool: resolvedTool,
|
|
73
|
+
disposition: options.disposition ?? null,
|
|
74
|
+
metered: options.metered ?? false,
|
|
75
|
+
});
|
|
76
|
+
if (!decision.allowed)
|
|
77
|
+
throw new AuthorityDenied(decision);
|
|
78
|
+
return fn.apply(this, args);
|
|
79
|
+
};
|
|
80
|
+
Object.defineProperties(wrapped, {
|
|
81
|
+
name: { value: fn.name, configurable: true },
|
|
82
|
+
guard: { value: guard, enumerable: true },
|
|
83
|
+
toolScope: { value: toolScope, enumerable: true },
|
|
84
|
+
unwrapped: { value: fn, enumerable: true },
|
|
85
|
+
});
|
|
86
|
+
return wrapped;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Build the guarded wrapper and register it on a graph in one call.
|
|
90
|
+
*
|
|
91
|
+
* `graph` is structurally typed: it only needs `.addNode(name, callable)`,
|
|
92
|
+
* which is `StateGraph`'s real signature — so this helper needs no import of
|
|
93
|
+
* its own and stays testable against a plain stand-in graph.
|
|
94
|
+
*/
|
|
95
|
+
export function addGuardedNode(graph, name, guard, toolScope, fn, options = {}) {
|
|
96
|
+
const node = guardNode(guard, toolScope, fn, options);
|
|
97
|
+
graph.addNode(name, node);
|
|
98
|
+
return node;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Return a stand-in for `tool` whose `invoke` authorizes through `guard` before
|
|
102
|
+
* the tool body runs. Everything else — `name`, `description`, `schema`, any
|
|
103
|
+
* LangChain internals — is forwarded to the original, so `ToolNode` matches and
|
|
104
|
+
* runs it exactly as it would the unwrapped tool.
|
|
105
|
+
*
|
|
106
|
+
* The context function receives the raw invoke arguments. `ToolNode` passes a
|
|
107
|
+
* tool call object, so the arguments the model proposed are at `input.args`;
|
|
108
|
+
* a direct `tool.invoke({...})` passes them at the top level. `toolArgs` below
|
|
109
|
+
* reads either shape.
|
|
110
|
+
*/
|
|
111
|
+
export function guardTool(guard, tool, options = {}) {
|
|
112
|
+
const scope = options.scope ?? tool.name;
|
|
113
|
+
const label = options.tool !== undefined ? options.tool : tool.name;
|
|
114
|
+
const guardedInvoke = (input, config) => {
|
|
115
|
+
const context = options.contextFn ? options.contextFn(input, config) : {};
|
|
116
|
+
const decision = guard.check(scope, {
|
|
117
|
+
context,
|
|
118
|
+
tool: label,
|
|
119
|
+
disposition: options.disposition ?? null,
|
|
120
|
+
metered: options.metered ?? false,
|
|
121
|
+
});
|
|
122
|
+
if (!decision.allowed) {
|
|
123
|
+
if (options.onDenied)
|
|
124
|
+
return options.onDenied(decision, input);
|
|
125
|
+
throw new AuthorityDenied(decision);
|
|
126
|
+
}
|
|
127
|
+
return tool.invoke(input, config);
|
|
128
|
+
};
|
|
129
|
+
return new Proxy(tool, {
|
|
130
|
+
get(target, prop, receiver) {
|
|
131
|
+
if (prop === "invoke")
|
|
132
|
+
return guardedInvoke;
|
|
133
|
+
if (prop === ATTENU_GUARD)
|
|
134
|
+
return guard;
|
|
135
|
+
if (prop === ATTENU_SCOPE)
|
|
136
|
+
return scope;
|
|
137
|
+
const value = Reflect.get(target, prop, receiver);
|
|
138
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/** Marker properties, so a caller can tell a guarded tool from a bare one. */
|
|
143
|
+
export const ATTENU_GUARD = "__attenuGuard";
|
|
144
|
+
export const ATTENU_SCOPE = "__attenuScope";
|
|
145
|
+
/**
|
|
146
|
+
* Guard a whole tool list in one call — what you hand to `new ToolNode([...])`
|
|
147
|
+
* or `createReactAgent({tools})`.
|
|
148
|
+
*/
|
|
149
|
+
export function guardTools(guard, tools, options = {}) {
|
|
150
|
+
return tools.map((t) => guardTool(guard, t, {
|
|
151
|
+
scope: options.scopes?.[t.name] ?? t.name,
|
|
152
|
+
contextFn: options.contexts?.[t.name] ?? options.contextFn,
|
|
153
|
+
disposition: options.disposition ?? null,
|
|
154
|
+
metered: options.metered ?? false,
|
|
155
|
+
onDenied: options.onDenied,
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The arguments a tool call carries, whichever shape `invoke` was handed.
|
|
160
|
+
* `ToolNode` passes `{name, args, id, type}`; a direct call passes the args
|
|
161
|
+
* themselves. Useful inside a `contextFn`.
|
|
162
|
+
*/
|
|
163
|
+
export function toolArgs(input) {
|
|
164
|
+
if (input !== null && typeof input === "object" && "args" in input && !Array.isArray(input)) {
|
|
165
|
+
const args = input.args;
|
|
166
|
+
if (args !== null && typeof args === "object")
|
|
167
|
+
return args;
|
|
168
|
+
}
|
|
169
|
+
return (input ?? {});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Mint the child Guard for a sub-agent handoff. Handing work to a sub-agent IS
|
|
173
|
+
* the delegation moment: call this before the sub-agent's graph is invoked, so
|
|
174
|
+
* its tools are guarded by an authority that is provably a subset of the
|
|
175
|
+
* parent's. A request wider than the parent holds comes back narrowed.
|
|
176
|
+
*
|
|
177
|
+
* Throws `AuthorityError` if the handoff is structurally refused — the parent
|
|
178
|
+
* is revoked or expired, the agent is banned, or the chain's depth or fanout
|
|
179
|
+
* ceiling would be exceeded.
|
|
180
|
+
*/
|
|
181
|
+
export function delegateTo(parent, options) {
|
|
182
|
+
return parent.delegate(options.agentId, options.request, options.task);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* True iff `@langchain/langgraph` is importable here. The import is lazy and
|
|
186
|
+
* happens only when this is called, so merely importing this adapter — or using
|
|
187
|
+
* `guardNode` / `guardTool` — never requires LangGraph to be installed.
|
|
188
|
+
*/
|
|
189
|
+
export async function isLangGraphAvailable() {
|
|
190
|
+
const specifier = "@langchain/langgraph";
|
|
191
|
+
try {
|
|
192
|
+
await import(specifier);
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=langgraph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"langgraph.js","sourceRoot":"","sources":["../../../src/adapters/langgraph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash-chained audit log — the open, verifiable record of every authority decision.
|
|
3
|
+
*
|
|
4
|
+
* Each event is appended as one JSON line whose `hash` covers the event plus the
|
|
5
|
+
* previous line's hash. Any insertion, deletion or reordering breaks the chain
|
|
6
|
+
* and is detectable offline by anyone, with no vendor in the loop.
|
|
7
|
+
*
|
|
8
|
+
* The schema is versioned and published as `schema/agent-audit.schema.json` in
|
|
9
|
+
* the Python distribution, so other tools (SIEMs, observability) can ingest it.
|
|
10
|
+
* The hashes computed here are byte-identical to the Python library's for
|
|
11
|
+
* identical content — see `canonical.ts` for how that is guaranteed.
|
|
12
|
+
*/
|
|
13
|
+
import { type CJson } from "./canonical.js";
|
|
14
|
+
import type { Signer } from "./wire.js";
|
|
15
|
+
export declare const SCHEMA_VERSION = 1;
|
|
16
|
+
export declare const GENESIS: string;
|
|
17
|
+
/** One line of the ledger. */
|
|
18
|
+
export type LedgerEntry = Record<string, CJson>;
|
|
19
|
+
/** A local destination that receives every entry after the file write. */
|
|
20
|
+
export interface Sink {
|
|
21
|
+
write(entry: LedgerEntry): void;
|
|
22
|
+
}
|
|
23
|
+
/** A signed external commitment to a chain head. */
|
|
24
|
+
export interface Anchor {
|
|
25
|
+
v: number;
|
|
26
|
+
chain_id: string;
|
|
27
|
+
seq: number;
|
|
28
|
+
head: string;
|
|
29
|
+
ts: number | string;
|
|
30
|
+
kid: string | null;
|
|
31
|
+
sig: string;
|
|
32
|
+
/** Set by `exportBundle`; not part of the signed body. */
|
|
33
|
+
verified?: boolean;
|
|
34
|
+
[key: string]: CJson | undefined;
|
|
35
|
+
}
|
|
36
|
+
/** `sha256(prev_hash || canonical(event-without-hash))`, as hex. */
|
|
37
|
+
export declare function hashEntry(prevHash: string, payload: LedgerEntry): string;
|
|
38
|
+
export interface AuditLogInit {
|
|
39
|
+
/** Where to append the `.jsonl` ledger. Omit for an in-memory log. */
|
|
40
|
+
path?: string | null;
|
|
41
|
+
/** Local sinks; each gets every entry after the file write. */
|
|
42
|
+
sinks?: readonly Sink[];
|
|
43
|
+
}
|
|
44
|
+
/** Append-only, hash-chained decision log. */
|
|
45
|
+
export declare class AuditLog {
|
|
46
|
+
readonly path: string | null;
|
|
47
|
+
private readonly sinks;
|
|
48
|
+
private prev;
|
|
49
|
+
private seq;
|
|
50
|
+
private readonly _entries;
|
|
51
|
+
/**
|
|
52
|
+
* Timestamps are injected by the caller (the `Guard` uses a monotonic
|
|
53
|
+
* counter) so the log stays deterministic in tests and reproducible in
|
|
54
|
+
* replay. In production the runtime supplies a trusted timestamp.
|
|
55
|
+
*/
|
|
56
|
+
constructor(init?: AuditLogInit | string | null);
|
|
57
|
+
append(event: string, ts: number | string, fields?: LedgerEntry): LedgerEntry;
|
|
58
|
+
get entries(): LedgerEntry[];
|
|
59
|
+
get length(): number;
|
|
60
|
+
[Symbol.iterator](): Iterator<LedgerEntry>;
|
|
61
|
+
/** `[seq, hash]` of the last entry — the chain head; `[-1, GENESIS]` if empty. */
|
|
62
|
+
head(): [number, string];
|
|
63
|
+
private chainIdHint;
|
|
64
|
+
/**
|
|
65
|
+
* A signed external COMMITMENT to the chain head. Publish it out of band; a
|
|
66
|
+
* later `verifyAnchor` then catches a log that was fully rewritten and
|
|
67
|
+
* re-hashed, which plain `verify` cannot — a consistent rewrite reproduces
|
|
68
|
+
* its own hashes. The signed head hash is the fixed point.
|
|
69
|
+
*/
|
|
70
|
+
anchor(signer: Signer, ts?: number | string): Anchor;
|
|
71
|
+
/** The chain reproduces AND its head matches a SIGNED anchor. */
|
|
72
|
+
static verifyAnchor(entries: readonly LedgerEntry[], anchor: Record<string, CJson> | null | undefined, signer: Signer): [boolean, string | null];
|
|
73
|
+
/** Recompute the chain. Returns `[ok, firstBadReason]`. */
|
|
74
|
+
static verify(entries: readonly LedgerEntry[]): [boolean, string | null];
|
|
75
|
+
/** Read a `.jsonl` ledger, keeping every number's original literal. */
|
|
76
|
+
static load(path: string): LedgerEntry[];
|
|
77
|
+
/** Parse `.jsonl` text into entries. Blank lines are skipped. */
|
|
78
|
+
static parseLines(text: string): LedgerEntry[];
|
|
79
|
+
}
|
|
80
|
+
/** The first `chain_id` any entry carries, or `"chain"`. */
|
|
81
|
+
export declare function chainIdOf(entries: readonly LedgerEntry[]): string;
|
|
82
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/audit.ts"],"names":[],"mappings":"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"}
|