@intentgate-app/intentgate 0.3.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/LICENSE +201 -0
- package/README.md +160 -0
- package/dist/index.cjs +661 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +537 -0
- package/dist/index.d.ts +537 -0
- package/dist/index.js +612 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory provenance primitives for the IntentGate TypeScript SDK.
|
|
3
|
+
*
|
|
4
|
+
* This module implements the SDK side of the AAI03 (Memory Poisoning)
|
|
5
|
+
* defense. The agent wraps its memory backend (vector DB, Redis,
|
|
6
|
+
* in-memory dict, anything) with {@link MemoryStore}, which signs
|
|
7
|
+
* every write with an HMAC-SHA256 keyed by a per-session signing key
|
|
8
|
+
* derived (via HKDF) from the capability token. At tool-call time the
|
|
9
|
+
* agent declares which memory entries influenced the call via the
|
|
10
|
+
* `memoryProvenance` parameter on `Gateway.toolCall`; the gateway
|
|
11
|
+
* re-derives the session key and verifies each entry.
|
|
12
|
+
*
|
|
13
|
+
* # Cross-implementation contract
|
|
14
|
+
*
|
|
15
|
+
* The byte encoding of an {@link Envelope} (see {@link canonical})
|
|
16
|
+
* MUST match the Go gateway's `internal/provenance.Canonical`
|
|
17
|
+
* byte-for-byte AND match the Python SDK's `intentgate.memory.canonical`
|
|
18
|
+
* byte-for-byte. The KDF MUST be HKDF-SHA256 with info=
|
|
19
|
+
* `intentgate-memory-v1`. The HMAC MUST be HMAC-SHA256.
|
|
20
|
+
*
|
|
21
|
+
* Drift in any of these contracts means the SDK and the gateway
|
|
22
|
+
* silently disagree on signatures — a class of bug caught by the
|
|
23
|
+
* cross-implementation KAT test in `tests/memory.test.ts`. If you
|
|
24
|
+
* change anything in this file, run that test against the Go gateway's
|
|
25
|
+
* `TestDeriveSessionKey_KnownAnswer` and the Python SDK's
|
|
26
|
+
* `test_hkdf_kat_matches_go_gateway` to confirm the wire contract
|
|
27
|
+
* still holds.
|
|
28
|
+
*
|
|
29
|
+
* # Zero runtime dependencies
|
|
30
|
+
*
|
|
31
|
+
* Uses only `node:crypto` (createHmac, createHash, hkdfSync,
|
|
32
|
+
* timingSafeEqual, randomUUID). No third-party crypto package. The
|
|
33
|
+
* Node 18+ baseline is documented in package.json.
|
|
34
|
+
*/
|
|
35
|
+
/** Length of a derived session signing key in bytes. Matches the Go
|
|
36
|
+
* gateway's SessionKeySize and the Python SDK's SESSION_KEY_SIZE. */
|
|
37
|
+
declare const SESSION_KEY_SIZE = 32;
|
|
38
|
+
/** The conventional PrevHash value for the first entry in a session.
|
|
39
|
+
* Named so the special-case is obvious at the call site. */
|
|
40
|
+
declare const ZERO_HASH: Buffer;
|
|
41
|
+
/**
|
|
42
|
+
* Raised when the SDK cannot produce or verify a memory envelope.
|
|
43
|
+
* Distinct from the gateway-side `ProvenanceError` in `errors.ts`
|
|
44
|
+
* (which is raised by `Gateway.toolCall` when the gateway rejected
|
|
45
|
+
* the provenance check). This class is for SDK-internal failures —
|
|
46
|
+
* a tampered entry detected at read time, a malformed envelope, etc.
|
|
47
|
+
*/
|
|
48
|
+
declare class MemoryProvenanceError extends Error {
|
|
49
|
+
constructor(message: string);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Derive the per-session memory signing key.
|
|
53
|
+
*
|
|
54
|
+
* Matches the gateway's `provenance.DeriveSessionKey` and the Python
|
|
55
|
+
* SDK's `derive_session_key`: HKDF-SHA256 with salt = `sessionId` (as
|
|
56
|
+
* UTF-8 bytes), info = `intentgate-memory-v1`, length = 32.
|
|
57
|
+
*
|
|
58
|
+
* @throws if `masterKey` is empty or `sessionId` is empty.
|
|
59
|
+
*/
|
|
60
|
+
declare function deriveSessionKey(masterKey: Buffer | Uint8Array, sessionId: string): Buffer;
|
|
61
|
+
/** One signed memory entry. Stored opaquely by the customer's memory
|
|
62
|
+
* backend; produced by `MemoryStore.write`; verified by the gateway. */
|
|
63
|
+
interface Envelope {
|
|
64
|
+
/** Stable identifier for the entry. */
|
|
65
|
+
id: string;
|
|
66
|
+
/** The capability token's `jti` whose signing key signed this. */
|
|
67
|
+
sessionId: string;
|
|
68
|
+
/** Creation time as Unix milliseconds. */
|
|
69
|
+
timestamp: number;
|
|
70
|
+
/** Application-level payload bytes. */
|
|
71
|
+
data: Buffer;
|
|
72
|
+
/** SHA-256 of the canonical bytes of the previous entry in this
|
|
73
|
+
* session, or ZERO_HASH for the first entry. */
|
|
74
|
+
prevHash: Buffer;
|
|
75
|
+
/** HMAC-SHA256 of canonical(envelope) under the session key. */
|
|
76
|
+
hmac: Buffer;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Produce the byte sequence the envelope's HMAC covers.
|
|
80
|
+
*
|
|
81
|
+
* Encoding is a length-prefixed concatenation of the immutable fields
|
|
82
|
+
* in order: sessionId (utf-8), id (utf-8), timestamp (big-endian
|
|
83
|
+
* uint64), prevHash, data. Lengths are big-endian uint32.
|
|
84
|
+
*
|
|
85
|
+
* The encoding is deliberately NOT JSON — same byte sequence the Go
|
|
86
|
+
* gateway produces in `provenance.Canonical` and the Python SDK
|
|
87
|
+
* produces in `canonical`. Cross-verified by the KAT test.
|
|
88
|
+
*
|
|
89
|
+
* The `hmac` field is excluded (a signature cannot cover itself).
|
|
90
|
+
*/
|
|
91
|
+
declare function canonical(env: Pick<Envelope, "id" | "sessionId" | "timestamp" | "data" | "prevHash">): Buffer;
|
|
92
|
+
/**
|
|
93
|
+
* Return a copy of `env` with the `hmac` field populated.
|
|
94
|
+
*
|
|
95
|
+
* Used at memory-write time by `MemoryStore.write` and by tests.
|
|
96
|
+
* Computes `HMAC-SHA256(sessionKey, canonical(env))`.
|
|
97
|
+
*
|
|
98
|
+
* @throws if `sessionKey` is empty.
|
|
99
|
+
*/
|
|
100
|
+
declare function sign(sessionKey: Buffer, env: Omit<Envelope, "hmac">): Envelope;
|
|
101
|
+
/**
|
|
102
|
+
* Check `env.hmac` against `sessionKey`. Returns `undefined` on a
|
|
103
|
+
* valid signature; throws {@link MemoryProvenanceError} otherwise.
|
|
104
|
+
* Comparison uses `timingSafeEqual` — constant-time with respect to
|
|
105
|
+
* signature contents.
|
|
106
|
+
*/
|
|
107
|
+
declare function verify(sessionKey: Buffer, env: Envelope): void;
|
|
108
|
+
/**
|
|
109
|
+
* Check a list of envelopes as a per-session chain.
|
|
110
|
+
*
|
|
111
|
+
* Each entry's HMAC must verify and each entry's `prevHash` must
|
|
112
|
+
* equal SHA-256 of the canonical bytes of the previous entry. The
|
|
113
|
+
* first entry's `prevHash` must equal {@link ZERO_HASH}.
|
|
114
|
+
*
|
|
115
|
+
* Empty chain is valid (let policy decide whether absence of memory
|
|
116
|
+
* provenance is a deny condition for the tool).
|
|
117
|
+
*/
|
|
118
|
+
declare function verifyChain(sessionKey: Buffer, chain: readonly Envelope[]): void;
|
|
119
|
+
/** Customer-supplied write hook for a backing store. */
|
|
120
|
+
type MemoryWriteHook = (entryId: string, env: Envelope) => void;
|
|
121
|
+
/** Customer-supplied read hook. Must throw if entry not found. */
|
|
122
|
+
type MemoryReadHook = (entryId: string) => Envelope;
|
|
123
|
+
/**
|
|
124
|
+
* Sign-on-write, verify-on-read wrapper around a memory backend.
|
|
125
|
+
*
|
|
126
|
+
* The customer plugs their underlying memory store (Pinecone, Redis,
|
|
127
|
+
* pgvector, an in-memory Map, anything) into this wrapper by
|
|
128
|
+
* supplying two callables: a write-hook and a read-hook. The wrapper
|
|
129
|
+
* signs entries on write and verifies them on read, so a tampered
|
|
130
|
+
* entry surfaces immediately at the agent's read site rather than
|
|
131
|
+
* waiting until the gateway rejects the tool call.
|
|
132
|
+
*
|
|
133
|
+
* The default storage backend is an in-memory Map — suitable for SDK
|
|
134
|
+
* tests, demos, and small agents. Production agents pass their real
|
|
135
|
+
* backend's read/write callables.
|
|
136
|
+
*/
|
|
137
|
+
declare class MemoryStore {
|
|
138
|
+
private readonly sessionId;
|
|
139
|
+
private readonly key;
|
|
140
|
+
private readonly fallback;
|
|
141
|
+
private readonly writeHook?;
|
|
142
|
+
private readonly readHook?;
|
|
143
|
+
private chainHead;
|
|
144
|
+
/**
|
|
145
|
+
* @param sessionId The `jti` of the capability token this store is bound to.
|
|
146
|
+
* Used as the HKDF salt to derive the signing key.
|
|
147
|
+
* @param memorySigningKey The 32-byte signing key returned by
|
|
148
|
+
* `POST /v1/admin/mint` when `with_memory_signing_key: true`.
|
|
149
|
+
* @param options Optional `writeHook` / `readHook` callables; when
|
|
150
|
+
* absent the wrapper uses an in-memory Map fallback.
|
|
151
|
+
*/
|
|
152
|
+
constructor(sessionId: string, memorySigningKey: Buffer | Uint8Array, options?: {
|
|
153
|
+
writeHook?: MemoryWriteHook;
|
|
154
|
+
readHook?: MemoryReadHook;
|
|
155
|
+
});
|
|
156
|
+
/**
|
|
157
|
+
* Sign `data` into a new envelope and store it. Returns the entry
|
|
158
|
+
* ID, which the caller passes to `Gateway.toolCall` via the
|
|
159
|
+
* `memoryProvenance` list.
|
|
160
|
+
*
|
|
161
|
+
* `data` may be a Buffer, a string (utf-8 encoded), or any JSON-
|
|
162
|
+
* serializable value (encoded with sorted keys + no whitespace so
|
|
163
|
+
* equivalent inputs produce identical envelope bytes).
|
|
164
|
+
*/
|
|
165
|
+
write(data: Buffer | string | Record<string, unknown> | unknown[]): string;
|
|
166
|
+
/**
|
|
167
|
+
* Fetch and verify the envelope identified by `entryId`.
|
|
168
|
+
*
|
|
169
|
+
* @throws if the entry is missing (`Error`) or if the HMAC fails
|
|
170
|
+
* ({@link MemoryProvenanceError}, indicating the entry was
|
|
171
|
+
* tampered with after writing).
|
|
172
|
+
*/
|
|
173
|
+
read(entryId: string): Envelope;
|
|
174
|
+
/**
|
|
175
|
+
* Build the wire-format provenance entries for a tool call. Each
|
|
176
|
+
* entry is verified before inclusion — if any envelope was tampered
|
|
177
|
+
* with at the storage layer, {@link MemoryProvenanceError} is
|
|
178
|
+
* raised here rather than at the gateway.
|
|
179
|
+
*
|
|
180
|
+
* The returned objects use base64url (no padding) encoding for byte
|
|
181
|
+
* fields — same shape the Go gateway parses.
|
|
182
|
+
*/
|
|
183
|
+
provenanceFor(entryIds: readonly string[]): Array<{
|
|
184
|
+
id: string;
|
|
185
|
+
session_id: string;
|
|
186
|
+
ts: number;
|
|
187
|
+
data: string;
|
|
188
|
+
prev_hash: string;
|
|
189
|
+
hmac: string;
|
|
190
|
+
}>;
|
|
191
|
+
/** Number of entries in the fallback in-memory store. */
|
|
192
|
+
get size(): number;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Exception hierarchy for the IntentGate SDK.
|
|
197
|
+
*
|
|
198
|
+
* Every gateway response that isn't a clean allow becomes a typed
|
|
199
|
+
* Error. The hierarchy lets callers catch broadly (`catch (e) { if
|
|
200
|
+
* (e instanceof IntentGateError) ... }`) or narrowly (`if (e instanceof
|
|
201
|
+
* CapabilityError) ...`) depending on whether they want to distinguish
|
|
202
|
+
* which check fired.
|
|
203
|
+
*
|
|
204
|
+
* Stage codes are stable across gateway versions:
|
|
205
|
+
*
|
|
206
|
+
* CapabilityError -32010
|
|
207
|
+
* IntentError -32011
|
|
208
|
+
* PolicyError -32012
|
|
209
|
+
* BudgetError -32013
|
|
210
|
+
* ProvenanceError -32014 (opt-in, AAI03 memory-poisoning defense)
|
|
211
|
+
*
|
|
212
|
+
* Anything else (parse errors, method not found, internal errors) is
|
|
213
|
+
* a `ProtocolError`. Network and HTTP transport failures (timeouts,
|
|
214
|
+
* connection refused, non-JSON responses) are `GatewayError` —
|
|
215
|
+
* distinguish "the gateway is unreachable" from "the gateway said no".
|
|
216
|
+
*/
|
|
217
|
+
declare class IntentGateError extends Error {
|
|
218
|
+
/** JSON-RPC error code from the gateway, or 0 if client-side. */
|
|
219
|
+
readonly code: number;
|
|
220
|
+
/** Optional structured payload from the gateway's `error.data`. */
|
|
221
|
+
readonly data: unknown;
|
|
222
|
+
constructor(message: string, opts?: {
|
|
223
|
+
code?: number;
|
|
224
|
+
data?: unknown;
|
|
225
|
+
cause?: unknown;
|
|
226
|
+
});
|
|
227
|
+
/**
|
|
228
|
+
* Human-friendly string. When `data` is a string, it usually carries
|
|
229
|
+
* the operator-facing reason (e.g. the Rego rule's explanation), so
|
|
230
|
+
* we surface it on `toString`.
|
|
231
|
+
*/
|
|
232
|
+
toString(): string;
|
|
233
|
+
}
|
|
234
|
+
/** Network or transport failure reaching the gateway. */
|
|
235
|
+
declare class GatewayError extends IntentGateError {
|
|
236
|
+
constructor(message: string, opts?: {
|
|
237
|
+
code?: number;
|
|
238
|
+
data?: unknown;
|
|
239
|
+
cause?: unknown;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/** JSON-RPC error not in the four stage-specific codes. */
|
|
243
|
+
declare class ProtocolError extends IntentGateError {
|
|
244
|
+
constructor(message: string, opts?: {
|
|
245
|
+
code?: number;
|
|
246
|
+
data?: unknown;
|
|
247
|
+
cause?: unknown;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/** Capability stage denied: token signature, expiry, agent lock, etc. */
|
|
251
|
+
declare class CapabilityError extends IntentGateError {
|
|
252
|
+
constructor(message: string, opts?: {
|
|
253
|
+
code?: number;
|
|
254
|
+
data?: unknown;
|
|
255
|
+
cause?: unknown;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
/** Intent stage denied: requested tool isn't in the extracted intent. */
|
|
259
|
+
declare class IntentError extends IntentGateError {
|
|
260
|
+
constructor(message: string, opts?: {
|
|
261
|
+
code?: number;
|
|
262
|
+
data?: unknown;
|
|
263
|
+
cause?: unknown;
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
/** Policy stage denied: a Rego rule fired. */
|
|
267
|
+
declare class PolicyError extends IntentGateError {
|
|
268
|
+
constructor(message: string, opts?: {
|
|
269
|
+
code?: number;
|
|
270
|
+
data?: unknown;
|
|
271
|
+
cause?: unknown;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/** Budget stage denied: max-calls caveat exhausted. */
|
|
275
|
+
declare class BudgetError extends IntentGateError {
|
|
276
|
+
constructor(message: string, opts?: {
|
|
277
|
+
code?: number;
|
|
278
|
+
data?: unknown;
|
|
279
|
+
cause?: unknown;
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Provenance stage denied: the opt-in AAI03 memory-poisoning defense
|
|
284
|
+
* rejected the call. Raised when the X-Intent-Memory-Provenance header
|
|
285
|
+
* carries an entry whose HMAC does not verify, whose prev_hash chain
|
|
286
|
+
* is broken, or whose envelope is structurally malformed.
|
|
287
|
+
*
|
|
288
|
+
* JSON-RPC code -32014. Only emitted by gateways with provenance
|
|
289
|
+
* enabled (INTENTGATE_PROVENANCE_ENABLED=true); not raised against
|
|
290
|
+
* the default four-check pipeline.
|
|
291
|
+
*/
|
|
292
|
+
declare class ProvenanceError extends IntentGateError {
|
|
293
|
+
constructor(message: string, opts?: {
|
|
294
|
+
code?: number;
|
|
295
|
+
data?: unknown;
|
|
296
|
+
cause?: unknown;
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Pick the typed exception class for a JSON-RPC error code. Codes in
|
|
301
|
+
* the stage range produce the matching stage class; anything else
|
|
302
|
+
* (parse, invalid request, method not found, internal error,
|
|
303
|
+
* out-of-range custom codes) maps to ProtocolError.
|
|
304
|
+
*/
|
|
305
|
+
declare function forCode(code: number): typeof IntentGateError;
|
|
306
|
+
|
|
307
|
+
/** One piece of the tool's response, in MCP shape. */
|
|
308
|
+
interface ContentBlock {
|
|
309
|
+
type: string;
|
|
310
|
+
text?: string;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Per-call gateway decision metadata, lifted from the `_intentgate`
|
|
314
|
+
* vendor extension on the JSON-RPC result. Always present on a
|
|
315
|
+
* successful tool_call; the gateway populates it on every allow.
|
|
316
|
+
*/
|
|
317
|
+
interface IntentGateMetadata {
|
|
318
|
+
decision: string;
|
|
319
|
+
reason: string;
|
|
320
|
+
check: string;
|
|
321
|
+
latencyMs: number;
|
|
322
|
+
}
|
|
323
|
+
/** Successful tool-call response. */
|
|
324
|
+
interface ToolCallResult {
|
|
325
|
+
content: ContentBlock[];
|
|
326
|
+
/** Tool's own `isError` flag — distinct from gateway transport errors. */
|
|
327
|
+
isError: boolean;
|
|
328
|
+
intentgate: IntentGateMetadata | null;
|
|
329
|
+
}
|
|
330
|
+
interface ToolCallOptions {
|
|
331
|
+
/** Tool arguments. The gateway logs only the keys, never the values. */
|
|
332
|
+
arguments?: Record<string, unknown>;
|
|
333
|
+
/**
|
|
334
|
+
* The user's original prompt. Sent in `X-Intent-Prompt`; the gateway
|
|
335
|
+
* feeds it to the intent extractor and verifies the requested tool
|
|
336
|
+
* is consistent with the extracted intent. Optional, but strongly
|
|
337
|
+
* recommended in production — without it the intent check is
|
|
338
|
+
* skipped (or denies in strict mode).
|
|
339
|
+
*/
|
|
340
|
+
intentPrompt?: string;
|
|
341
|
+
/**
|
|
342
|
+
* JSON-RPC request id. When unset, the Gateway uses a sequential
|
|
343
|
+
* per-instance counter — fine for most agents.
|
|
344
|
+
*/
|
|
345
|
+
requestId?: number | string;
|
|
346
|
+
/**
|
|
347
|
+
* Optional list of memory entry IDs that influenced this tool call.
|
|
348
|
+
* When supplied together with `memoryStore`, the SDK looks up the
|
|
349
|
+
* corresponding signed envelopes and packs them into the
|
|
350
|
+
* `X-Intent-Memory-Provenance` header. The gateway re-derives the
|
|
351
|
+
* session signing key from the capability token's jti, verifies
|
|
352
|
+
* each HMAC, and walks the chain — closing the sophisticated AAI03
|
|
353
|
+
* (Memory Poisoning) case. Used only when the gateway has
|
|
354
|
+
* provenance enabled; otherwise the header is ignored. See
|
|
355
|
+
* `MemoryStore`.
|
|
356
|
+
*/
|
|
357
|
+
memoryProvenance?: readonly string[];
|
|
358
|
+
/**
|
|
359
|
+
* MemoryStore instance the SDK queries for the envelopes named in
|
|
360
|
+
* `memoryProvenance`. Required iff `memoryProvenance` is non-empty.
|
|
361
|
+
*/
|
|
362
|
+
memoryStore?: MemoryStore;
|
|
363
|
+
}
|
|
364
|
+
interface GatewayOptions {
|
|
365
|
+
/**
|
|
366
|
+
* Capability token from `igctl mint` or your tenant's mint service.
|
|
367
|
+
* When omitted, no Authorization header is sent and the gateway
|
|
368
|
+
* will reject with CapabilityError if it's in strict mode.
|
|
369
|
+
*/
|
|
370
|
+
token?: string;
|
|
371
|
+
/** Per-request timeout in milliseconds. Default 10s. */
|
|
372
|
+
timeoutMs?: number;
|
|
373
|
+
/**
|
|
374
|
+
* Pluggable fetch implementation. Defaults to the global
|
|
375
|
+
* `fetch` (Node 18+, browsers, and most modern runtimes). Useful
|
|
376
|
+
* for test injection, custom transports, or shared connection
|
|
377
|
+
* pooling.
|
|
378
|
+
*/
|
|
379
|
+
fetch?: typeof fetch;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Thin client for the IntentGate gateway.
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```ts
|
|
386
|
+
* import { Gateway } from "@intentgate-app/intentgate";
|
|
387
|
+
*
|
|
388
|
+
* const gw = new Gateway("http://localhost:8080", {
|
|
389
|
+
* token: process.env.INTENTGATE_TOKEN,
|
|
390
|
+
* });
|
|
391
|
+
* const result = await gw.toolCall("read_invoice", {
|
|
392
|
+
* arguments: { id: "123" },
|
|
393
|
+
* intentPrompt: "Process today's AP invoices",
|
|
394
|
+
* });
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
declare class Gateway {
|
|
398
|
+
private readonly url;
|
|
399
|
+
private readonly token;
|
|
400
|
+
private readonly timeoutMs;
|
|
401
|
+
private readonly fetchImpl;
|
|
402
|
+
private nextId;
|
|
403
|
+
constructor(url: string, opts?: GatewayOptions);
|
|
404
|
+
/**
|
|
405
|
+
* Invoke a tool through the gateway.
|
|
406
|
+
*
|
|
407
|
+
* Resolves with a {@link ToolCallResult} for an allowed call. Throws
|
|
408
|
+
* one of the typed errors (CapabilityError / IntentError /
|
|
409
|
+
* PolicyError / BudgetError / ProtocolError / GatewayError) when
|
|
410
|
+
* the gateway denies, the request fails to reach the gateway, or
|
|
411
|
+
* the response isn't well-formed JSON-RPC.
|
|
412
|
+
*/
|
|
413
|
+
toolCall(tool: string, opts?: ToolCallOptions): Promise<ToolCallResult>;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Pure-TypeScript capability-token helpers.
|
|
418
|
+
*
|
|
419
|
+
* The IntentGate gateway issues capability tokens with a Macaroons-
|
|
420
|
+
* style chained-HMAC signature: a token holder can derive a strictly
|
|
421
|
+
* more restrictive child token by appending a caveat and HMAC'ing it
|
|
422
|
+
* under the parent's signature **without ever touching the master
|
|
423
|
+
* key**. That is the defining property of capability tokens, and the
|
|
424
|
+
* reason this module ships in the SDK.
|
|
425
|
+
*
|
|
426
|
+
* Use case. A parent agent receives a token allowing tools `[a, b,
|
|
427
|
+
* c]`, spawns a sub-agent for one task, and wants the sub-agent's
|
|
428
|
+
* token to allow only `[a]`. The parent calls {@link attenuate} and
|
|
429
|
+
* hands the resulting token string to the sub-agent. The gateway
|
|
430
|
+
* (which has the master key) accepts the attenuated token and rejects
|
|
431
|
+
* any sub-agent call that would have needed `b` or `c`.
|
|
432
|
+
*
|
|
433
|
+
* What we don't do here:
|
|
434
|
+
*
|
|
435
|
+
* - **No master key access.** By design — that's what makes
|
|
436
|
+
* attenuation safe. To mint a brand-new root token, use the
|
|
437
|
+
* gateway's `POST /v1/admin/mint` endpoint, not this module.
|
|
438
|
+
* - **No semantic narrowing check.** Adding a "broader" caveat
|
|
439
|
+
* doesn't widen the chain because the parent's narrower caveat
|
|
440
|
+
* fires first on the gateway side. We don't second-guess the
|
|
441
|
+
* caller; policy belongs in the gateway, not the SDK.
|
|
442
|
+
*
|
|
443
|
+
* # Wire format
|
|
444
|
+
*
|
|
445
|
+
* We mirror the Go gateway's serialization byte-for-byte on the one
|
|
446
|
+
* place it matters: the new caveat's canonical JSON, which seeds the
|
|
447
|
+
* HMAC step. Anywhere else, JSON ordering doesn't affect correctness
|
|
448
|
+
* because the gateway re-marshals from its parsed Go struct during
|
|
449
|
+
* `Verify`. Every implementation in this package matches the Python
|
|
450
|
+
* SDK byte-for-byte against the same fixtures, so a token attenuated
|
|
451
|
+
* by either SDK verifies on the same gateway.
|
|
452
|
+
*/
|
|
453
|
+
/**
|
|
454
|
+
* Caveat-type identifiers, kept in sync with the Go consts in
|
|
455
|
+
* gateway/internal/capability/token.go.
|
|
456
|
+
*/
|
|
457
|
+
declare const CaveatType: {
|
|
458
|
+
readonly EXPIRY: "exp";
|
|
459
|
+
readonly TOOL_ALLOW: "tool_allow";
|
|
460
|
+
readonly TOOL_DENY: "tool_deny";
|
|
461
|
+
readonly AGENT_LOCK: "agent_lock";
|
|
462
|
+
readonly MAX_CALLS: "max_calls";
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* A structured restriction recorded in a token's chain. Only fields
|
|
466
|
+
* relevant to the {@link type} are emitted; the rest are omitted from
|
|
467
|
+
* the JSON output (matching Go's `omitempty` tags).
|
|
468
|
+
*
|
|
469
|
+
* Field order on the wire: `t, tools, agent, exp, max_calls`. This
|
|
470
|
+
* matches Go's encoding/json declaration-order behavior and seeds
|
|
471
|
+
* the HMAC step that derives the child's signature.
|
|
472
|
+
*/
|
|
473
|
+
interface Caveat {
|
|
474
|
+
type: string;
|
|
475
|
+
tools?: string[];
|
|
476
|
+
agent?: string;
|
|
477
|
+
/** Unix seconds. */
|
|
478
|
+
expiry?: number;
|
|
479
|
+
maxCalls?: number;
|
|
480
|
+
}
|
|
481
|
+
declare class AttenuationError extends Error {
|
|
482
|
+
constructor(message: string, opts?: {
|
|
483
|
+
cause?: unknown;
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Decode a base64url(JSON) token into its parsed object. No signature
|
|
488
|
+
* check (the gateway does that). Useful for inspecting the chain —
|
|
489
|
+
* `decodeToken(t).cav` lists the caveats bound to the token.
|
|
490
|
+
*/
|
|
491
|
+
declare function decodeToken(token: string): Record<string, unknown>;
|
|
492
|
+
interface AttenuateOptions {
|
|
493
|
+
/** Narrow to this tool whitelist (caveat type `tool_allow`). */
|
|
494
|
+
addTools?: string[];
|
|
495
|
+
/** Add a deny list (caveat type `tool_deny`). Additive on top of any parent deny. */
|
|
496
|
+
denyTools?: string[];
|
|
497
|
+
/** Cap remaining calls (caveat type `max_calls`). The chain enforces the minimum. */
|
|
498
|
+
maxCalls?: number;
|
|
499
|
+
/** Set absolute expiry (Unix seconds). Caveat type `exp`. */
|
|
500
|
+
expiresAt?: number;
|
|
501
|
+
/** Convenience: now + N seconds. Caveat type `exp`. */
|
|
502
|
+
expiresInSeconds?: number;
|
|
503
|
+
/** User-supplied caveats appended last (advanced). */
|
|
504
|
+
extra?: Caveat[];
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Append narrowing caveats to a parent token and return a new token
|
|
508
|
+
* string that the gateway accepts as a cryptographic descendant of
|
|
509
|
+
* the parent.
|
|
510
|
+
*
|
|
511
|
+
* Each option groups one common attenuation pattern. Multiple options
|
|
512
|
+
* combine; each generates one caveat in this order:
|
|
513
|
+
*
|
|
514
|
+
* 1. `addTools` → `tool_allow` caveat
|
|
515
|
+
* 2. `denyTools` → `tool_deny` caveat
|
|
516
|
+
* 3. `maxCalls` → `max_calls` caveat
|
|
517
|
+
* 4. `expiresAt` / → `exp` caveat (absolute Unix seconds)
|
|
518
|
+
* `expiresInSeconds`
|
|
519
|
+
* 5. `extra` → user-supplied caveats appended last
|
|
520
|
+
*
|
|
521
|
+
* @example
|
|
522
|
+
* ```ts
|
|
523
|
+
* import { attenuate } from "@intentgate-app/intentgate";
|
|
524
|
+
*
|
|
525
|
+
* // Parent token: agent allowed [search, read, email] for 1h.
|
|
526
|
+
* // Child: only [search, read], one call max.
|
|
527
|
+
* const child = attenuate(parentToken, {
|
|
528
|
+
* addTools: ["search", "read"],
|
|
529
|
+
* maxCalls: 1,
|
|
530
|
+
* });
|
|
531
|
+
*
|
|
532
|
+
* // Hand `child` to the sub-agent.
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
declare function attenuate(token: string, opts?: AttenuateOptions): string;
|
|
536
|
+
|
|
537
|
+
export { type AttenuateOptions, AttenuationError, BudgetError, CapabilityError, type Caveat, CaveatType, type ContentBlock, type Envelope, Gateway, GatewayError, type GatewayOptions, IntentError, IntentGateError, type IntentGateMetadata, MemoryProvenanceError, type MemoryReadHook, MemoryStore, type MemoryWriteHook, PolicyError, ProtocolError, ProvenanceError, SESSION_KEY_SIZE, type ToolCallOptions, type ToolCallResult, ZERO_HASH, attenuate, canonical, decodeToken, deriveSessionKey, forCode, sign, verify, verifyChain };
|