@tangle-network/agent-core 0.3.8 → 0.4.1
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/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +72 -35
- package/dist/auth/index.js.map +1 -1
- package/dist/{index-CBGkvDtv.d.ts → index-Cm-DkFuR.d.ts} +20 -3
- package/dist/index-Cm-DkFuR.d.ts.map +1 -0
- package/dist/{index-B3GE-P6Q.d.ts → index-Dz1ZLn3K.d.ts} +2 -2
- package/dist/{index-B3GE-P6Q.d.ts.map → index-Dz1ZLn3K.d.ts.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.js +2 -2
- package/dist/{telemetry-D2XGH5UL.js → telemetry-dTKTPI8R.js} +231 -10
- package/dist/telemetry-dTKTPI8R.js.map +1 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/index-CBGkvDtv.d.ts.map +0 -1
- package/dist/telemetry-D2XGH5UL.js.map +0 -1
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/auth/channel-access.ts","../../src/auth/tokens.ts","../../src/auth/index.ts"],"mappings":";;;AAqDA;;;AAAA,UAzBiB,mBAAA;EAyBwC;EAvBvD,OAAA;EAuC0C;EArC1C,MAAA;AAAA;;;;;iBAOc,yBAAA,CAA0B,OAAA;;AAwF1C;;;iBA1EgB,yBAAA,CAA0B,OAAA;;;;;;;AAmI1C;;;iBAnHgB,4BAAA,CACd,OAAA,EAAS,yBAAA,EACT,OAAA,WACC,mBAAA;;;;;;;AAiKH;;;iBA1GgB,4BAAA,CACd,OAAA,EAAS,yBAAA,EACT,OAAA,WACC,mBAAA;;;;;;;AAsIH;;;;iBAhFgB,0BAAA,CACd,OAAA,EAAS,uBAAA,EACT,OAAA,WACC,mBAAA;AAqHH;;;;;;;;AAAA,iBAvEgB,qBAAA,CACd,OAAA,EAAS,gBAAA,EACT,OAAA,WACC,mBAAA;;;;;;;;iBA4Ba,yBAAA,CAA0B,OAAA,EAAS,gBAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/auth/channel-access.ts","../../src/auth/tokens.ts","../../src/auth/index.ts"],"mappings":"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"}
|
package/dist/auth/index.js
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
import { a as ReadTokenPayloadSchema, c as CallbackAuth, d as RefreshableTokenAuth, f as StaticTokenAuth, i as ProjectScopedTokenPayloadSchema, l as NoAuth, n as CreateSessionRequestSchema, o as SendMessageRequestSchema, r as ProductSchema, s as SessionScopedTokenPayloadSchema, t as BatchScopedTokenPayloadSchema, u as ReadTokenAuth } from "../types-DC6OJ1um.js";
|
|
2
|
-
import { createHmac, generateKeyPairSync, randomBytes, sign, timingSafeEqual, verify } from "node:crypto";
|
|
3
2
|
//#region src/auth/tokens.ts
|
|
3
|
+
const textEncoder = new TextEncoder();
|
|
4
|
+
const textDecoder = new TextDecoder();
|
|
5
|
+
let nodeCryptoCache;
|
|
4
6
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Resolve Node's `crypto` lazily and synchronously. Signing, verification, and
|
|
8
|
+
* key generation are server-only, but this module must stay importable in a
|
|
9
|
+
* browser bundle — a static `import "node:crypto"` forces bundlers to resolve
|
|
10
|
+
* the builtin at load and blanks the page. `process.getBuiltinModule` exists
|
|
11
|
+
* only on a Node runtime (Node >= 22.3); in a browser `globalThis.process` is
|
|
12
|
+
* undefined, so callers get a clear server-only error instead of a crash.
|
|
10
13
|
*/
|
|
14
|
+
function nodeCrypto() {
|
|
15
|
+
if (nodeCryptoCache) return nodeCryptoCache;
|
|
16
|
+
const resolved = globalThis.process?.getBuiltinModule?.("node:crypto");
|
|
17
|
+
if (!resolved) throw new Error("@tangle-network/agent-core/auth: token signing, verification, and key generation are server-only (Node.js crypto) and cannot run in a browser.");
|
|
18
|
+
nodeCryptoCache = resolved;
|
|
19
|
+
return resolved;
|
|
20
|
+
}
|
|
11
21
|
/**
|
|
12
22
|
* Generate a cryptographically secure random string.
|
|
13
23
|
* @param prefix - Prefix for the generated string (e.g., "orch_prod_")
|
|
14
24
|
* @param bytes - Number of random bytes (default: 32 = 256 bits)
|
|
15
25
|
*/
|
|
16
26
|
function generateSecureToken(prefix, bytes = 32) {
|
|
17
|
-
return `${prefix}${
|
|
27
|
+
return `${prefix}${base64UrlEncode(nodeCrypto().randomBytes(bytes))}`;
|
|
18
28
|
}
|
|
19
29
|
/**
|
|
20
30
|
* Generate a product API key.
|
|
@@ -29,23 +39,37 @@ function generateSigningSecret() {
|
|
|
29
39
|
return generateSecureToken("orch_sign_");
|
|
30
40
|
}
|
|
31
41
|
/**
|
|
32
|
-
* Base64URL encode (RFC 7515).
|
|
42
|
+
* Base64URL encode (RFC 7515). Isomorphic: `btoa` + `TextEncoder` exist in Node
|
|
43
|
+
* and browsers, so — unlike `Buffer` — this never blanks a browser page that
|
|
44
|
+
* transitively imports the module.
|
|
33
45
|
*/
|
|
34
46
|
function base64UrlEncode(data) {
|
|
35
|
-
|
|
47
|
+
const bytes = typeof data === "string" ? textEncoder.encode(data) : data;
|
|
48
|
+
let binary = "";
|
|
49
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
50
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
36
51
|
}
|
|
37
52
|
/**
|
|
38
|
-
* Base64URL decode.
|
|
53
|
+
* Base64URL decode (RFC 7515) to raw bytes.
|
|
39
54
|
*/
|
|
40
|
-
function
|
|
55
|
+
function base64UrlToBytes(data) {
|
|
41
56
|
const padded = data + "=".repeat((4 - data.length % 4) % 4);
|
|
42
|
-
|
|
57
|
+
const binary = atob(padded.replace(/-/g, "+").replace(/_/g, "/"));
|
|
58
|
+
const bytes = new Uint8Array(binary.length);
|
|
59
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
60
|
+
return bytes;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Base64URL decode to a UTF-8 string.
|
|
64
|
+
*/
|
|
65
|
+
function base64UrlDecode(data) {
|
|
66
|
+
return textDecoder.decode(base64UrlToBytes(data));
|
|
43
67
|
}
|
|
44
68
|
/**
|
|
45
69
|
* Create HMAC-SHA256 signature.
|
|
46
70
|
*/
|
|
47
71
|
function createSignature(data, secret) {
|
|
48
|
-
return base64UrlEncode(createHmac("sha256", secret).update(data).digest());
|
|
72
|
+
return base64UrlEncode(nodeCrypto().createHmac("sha256", secret).update(data).digest());
|
|
49
73
|
}
|
|
50
74
|
/**
|
|
51
75
|
* Verify signature using timing-safe comparison on raw HMAC bytes.
|
|
@@ -58,10 +82,11 @@ function createSignature(data, secret) {
|
|
|
58
82
|
* `products/sandbox/sdk/src/auth/tokens.ts`.
|
|
59
83
|
*/
|
|
60
84
|
function verifySignature(data, signature, secret) {
|
|
85
|
+
const { createHmac, timingSafeEqual } = nodeCrypto();
|
|
61
86
|
const expectedBuf = createHmac("sha256", secret).update(data).digest();
|
|
62
87
|
let providedBuf;
|
|
63
88
|
try {
|
|
64
|
-
providedBuf =
|
|
89
|
+
providedBuf = base64UrlToBytes(signature);
|
|
65
90
|
} catch {
|
|
66
91
|
return false;
|
|
67
92
|
}
|
|
@@ -73,12 +98,17 @@ function verifySignature(data, signature, secret) {
|
|
|
73
98
|
}
|
|
74
99
|
}
|
|
75
100
|
/**
|
|
76
|
-
* JWT header
|
|
101
|
+
* Base64URL-encoded JWT header `{"alg":"HS256","typ":"JWT"}`. Computed on first
|
|
102
|
+
* use (not at module load) and memoized.
|
|
77
103
|
*/
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
104
|
+
let jwtHeaderCache;
|
|
105
|
+
function jwtHeader() {
|
|
106
|
+
if (jwtHeaderCache === void 0) jwtHeaderCache = base64UrlEncode(JSON.stringify({
|
|
107
|
+
alg: "HS256",
|
|
108
|
+
typ: "JWT"
|
|
109
|
+
}));
|
|
110
|
+
return jwtHeaderCache;
|
|
111
|
+
}
|
|
82
112
|
/**
|
|
83
113
|
* Issue a read token (JWT) for WebSocket authentication.
|
|
84
114
|
*
|
|
@@ -94,7 +124,8 @@ function issueReadToken(signingSecret, payload, ttlMinutes) {
|
|
|
94
124
|
iat: now,
|
|
95
125
|
exp: now + ttlMinutes * 60
|
|
96
126
|
};
|
|
97
|
-
const
|
|
127
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(fullPayload));
|
|
128
|
+
const data = `${jwtHeader()}.${encodedPayload}`;
|
|
98
129
|
return `${data}.${createSignature(data, signingSecret)}`;
|
|
99
130
|
}
|
|
100
131
|
/**
|
|
@@ -130,10 +161,14 @@ function issueProjectScopedToken(signingSecret, payload, ttlMinutes) {
|
|
|
130
161
|
function issueBatchScopedToken(signingSecret, payload, ttlMinutes) {
|
|
131
162
|
return issueReadToken(signingSecret, payload, ttlMinutes);
|
|
132
163
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
164
|
+
let jwtHeaderEddsaCache;
|
|
165
|
+
function jwtHeaderEddsa() {
|
|
166
|
+
if (jwtHeaderEddsaCache === void 0) jwtHeaderEddsaCache = base64UrlEncode(JSON.stringify({
|
|
167
|
+
alg: "EdDSA",
|
|
168
|
+
typ: "JWT"
|
|
169
|
+
}));
|
|
170
|
+
return jwtHeaderEddsaCache;
|
|
171
|
+
}
|
|
137
172
|
/**
|
|
138
173
|
* Generate an Ed25519 key pair for per-session JWT signing.
|
|
139
174
|
* Private key stays in orchestrator memory. Public key is injected into
|
|
@@ -143,7 +178,7 @@ const JWT_HEADER_EDDSA = base64UrlEncode(JSON.stringify({
|
|
|
143
178
|
* @returns { privateKey, publicKey } as PEM strings
|
|
144
179
|
*/
|
|
145
180
|
function generateSidecarKeyPair() {
|
|
146
|
-
const { privateKey, publicKey } = generateKeyPairSync("ed25519", {
|
|
181
|
+
const { privateKey, publicKey } = nodeCrypto().generateKeyPairSync("ed25519", {
|
|
147
182
|
publicKeyEncoding: {
|
|
148
183
|
type: "spki",
|
|
149
184
|
format: "pem"
|
|
@@ -170,6 +205,7 @@ function generateSidecarKeyPair() {
|
|
|
170
205
|
function issueSidecarAccessToken(privateKey, payload, ttlMinutes) {
|
|
171
206
|
if (!privateKey.includes("PRIVATE KEY")) throw new Error("issueSidecarAccessToken requires an Ed25519 private key (PEM). HMAC fallback has been removed — generate a key pair with generateSidecarKeyPair().");
|
|
172
207
|
const now = Math.floor(Date.now() / 1e3);
|
|
208
|
+
const { randomBytes, sign } = nodeCrypto();
|
|
173
209
|
const jti = `${payload.cid}:${now}:${randomBytes(8).toString("hex")}`;
|
|
174
210
|
const fullPayload = {
|
|
175
211
|
...payload,
|
|
@@ -178,8 +214,9 @@ function issueSidecarAccessToken(privateKey, payload, ttlMinutes) {
|
|
|
178
214
|
iat: now,
|
|
179
215
|
exp: now + ttlMinutes * 60
|
|
180
216
|
};
|
|
181
|
-
const
|
|
182
|
-
|
|
217
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(fullPayload));
|
|
218
|
+
const data = `${jwtHeaderEddsa()}.${encodedPayload}`;
|
|
219
|
+
return `${data}.${base64UrlEncode(sign(null, textEncoder.encode(data), privateKey))}`;
|
|
183
220
|
}
|
|
184
221
|
/**
|
|
185
222
|
* Verify a sidecar JWT using Ed25519 public key. Rejects non-EdDSA tokens.
|
|
@@ -189,12 +226,12 @@ function verifySidecarToken(token, publicKey, containerId) {
|
|
|
189
226
|
try {
|
|
190
227
|
const parts = token.split(".");
|
|
191
228
|
if (parts.length !== 3) return null;
|
|
192
|
-
const headerJson =
|
|
229
|
+
const headerJson = base64UrlDecode(parts[0]);
|
|
193
230
|
if (JSON.parse(headerJson).alg !== "EdDSA") return null;
|
|
194
231
|
const data = `${parts[0]}.${parts[1]}`;
|
|
195
|
-
const signatureBuffer =
|
|
196
|
-
if (!verify(null,
|
|
197
|
-
const payload = JSON.parse(
|
|
232
|
+
const signatureBuffer = base64UrlToBytes(parts[2]);
|
|
233
|
+
if (!nodeCrypto().verify(null, textEncoder.encode(data), publicKey, signatureBuffer)) return null;
|
|
234
|
+
const payload = JSON.parse(base64UrlDecode(parts[1]));
|
|
198
235
|
if (payload.typ !== "sidecar") return null;
|
|
199
236
|
if (typeof payload.jti !== "string" || payload.jti.length === 0) return null;
|
|
200
237
|
const exactContainerMatch = payload.cid === containerId;
|
|
@@ -292,7 +329,7 @@ function verifyReadToken(token, product) {
|
|
|
292
329
|
};
|
|
293
330
|
const [header, encodedPayload, signature] = parts;
|
|
294
331
|
const data = `${header}.${encodedPayload}`;
|
|
295
|
-
if (header !==
|
|
332
|
+
if (header !== jwtHeader()) {
|
|
296
333
|
let parsedAlg = null;
|
|
297
334
|
try {
|
|
298
335
|
const decoded = JSON.parse(base64UrlDecode(header));
|
|
@@ -392,14 +429,14 @@ function getApiKeyHashSalt() {
|
|
|
392
429
|
* WARNING: Changing the salt will invalidate all existing API key hashes.
|
|
393
430
|
*/
|
|
394
431
|
function hashApiKey(apiKey) {
|
|
395
|
-
return createHmac("sha256", getApiKeyHashSalt()).update(apiKey).digest("hex");
|
|
432
|
+
return nodeCrypto().createHmac("sha256", getApiKeyHashSalt()).update(apiKey).digest("hex");
|
|
396
433
|
}
|
|
397
434
|
/**
|
|
398
435
|
* Hash an API key with an explicit salt.
|
|
399
436
|
* Use this when you need to verify against a specific salt.
|
|
400
437
|
*/
|
|
401
438
|
function hashApiKeyWithSalt(apiKey, salt) {
|
|
402
|
-
return createHmac("sha256", salt).update(apiKey).digest("hex");
|
|
439
|
+
return nodeCrypto().createHmac("sha256", salt).update(apiKey).digest("hex");
|
|
403
440
|
}
|
|
404
441
|
/**
|
|
405
442
|
* Verify an API key using timing-safe comparison.
|
|
@@ -407,7 +444,7 @@ function hashApiKeyWithSalt(apiKey, salt) {
|
|
|
407
444
|
function verifyApiKey(provided, stored) {
|
|
408
445
|
if (provided.length !== stored.length) return false;
|
|
409
446
|
try {
|
|
410
|
-
return timingSafeEqual(
|
|
447
|
+
return nodeCrypto().timingSafeEqual(textEncoder.encode(provided), textEncoder.encode(stored));
|
|
411
448
|
} catch {
|
|
412
449
|
return false;
|
|
413
450
|
}
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/auth/tokens.ts","../../src/auth/channel-access.ts","../../src/auth/index.ts"],"sourcesContent":["/**\n * JWT Token Utilities\n *\n * Token generation and verification for multi-tenant auth.\n * - Read/product tokens: HMAC-SHA256 (symmetric, fast)\n * - Sidecar tokens: Ed25519 (asymmetric — sidecar cannot forge tokens)\n */\n\nimport {\n createHmac,\n generateKeyPairSync,\n randomBytes,\n sign,\n timingSafeEqual,\n verify,\n} from \"node:crypto\";\nimport type {\n BatchScopedTokenPayload,\n ProductAuthInfo,\n ProjectScopedTokenPayload,\n ReadTokenPayload,\n SessionScopedTokenPayload,\n TokenScope,\n TokenValidationResult,\n} from \"./types.js\";\n\n/**\n * Generate a cryptographically secure random string.\n * @param prefix - Prefix for the generated string (e.g., \"orch_prod_\")\n * @param bytes - Number of random bytes (default: 32 = 256 bits)\n */\nexport function generateSecureToken(prefix: string, bytes = 32): string {\n return `${prefix}${randomBytes(bytes).toString(\"base64url\")}`;\n}\n\n/**\n * Generate a product API key.\n */\nexport function generateApiKey(): string {\n return generateSecureToken(\"orch_prod_\");\n}\n\n/**\n * Generate a signing secret.\n */\nexport function generateSigningSecret(): string {\n return generateSecureToken(\"orch_sign_\");\n}\n\n/**\n * Base64URL encode (RFC 7515).\n */\nfunction base64UrlEncode(data: string | Buffer): string {\n const buffer = typeof data === \"string\" ? Buffer.from(data) : data;\n return buffer\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\n/**\n * Base64URL decode.\n */\nfunction base64UrlDecode(data: string): string {\n const padded = data + \"=\".repeat((4 - (data.length % 4)) % 4);\n return Buffer.from(\n padded.replace(/-/g, \"+\").replace(/_/g, \"/\"),\n \"base64\",\n ).toString();\n}\n\n/**\n * Create HMAC-SHA256 signature.\n */\nfunction createSignature(data: string, secret: string): string {\n return base64UrlEncode(createHmac(\"sha256\", secret).update(data).digest());\n}\n\n/**\n * Verify signature using timing-safe comparison on raw HMAC bytes.\n *\n * Decoding both sides to raw bytes before the compare strips the\n * length-of-base64url-string oracle: every legitimate signature\n * decodes to exactly 32 bytes (HMAC-SHA256 digest size), so a\n * length mismatch on the decoded buffer means malformed input,\n * not a partial match. Equivalent to the pattern used in\n * `products/sandbox/sdk/src/auth/tokens.ts`.\n */\nfunction verifySignature(\n data: string,\n signature: string,\n secret: string,\n): boolean {\n const expectedBuf = createHmac(\"sha256\", secret).update(data).digest();\n let providedBuf: Buffer;\n try {\n providedBuf = Buffer.from(signature, \"base64url\");\n } catch {\n return false;\n }\n if (providedBuf.length !== expectedBuf.length) return false;\n try {\n return timingSafeEqual(providedBuf, expectedBuf);\n } catch {\n return false;\n }\n}\n\n/**\n * JWT header (always the same for our use case).\n */\nconst JWT_HEADER = base64UrlEncode(\n JSON.stringify({ alg: \"HS256\", typ: \"JWT\" }),\n);\n\n/**\n * Issue a read token (JWT) for WebSocket authentication.\n *\n * @param signingSecret - The product's signing secret\n * @param payload - Token payload (without iat/exp, those are added)\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueReadToken(\n signingSecret: string,\n payload: Omit<ReadTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes: number,\n): string {\n const now = Math.floor(Date.now() / 1000);\n\n // Type assertion is safe here: payload already satisfies one branch of the\n // ReadTokenPayload union (minus iat/exp/typ), and we're adding those fields.\n // TypeScript can't infer which union branch we're creating from a spread.\n const fullPayload = {\n ...payload,\n typ: \"read\" as const,\n iat: now,\n exp: now + ttlMinutes * 60,\n } as ReadTokenPayload;\n\n const encodedPayload = base64UrlEncode(JSON.stringify(fullPayload));\n const data = `${JWT_HEADER}.${encodedPayload}`;\n const signature = createSignature(data, signingSecret);\n\n return `${data}.${signature}`;\n}\n\n/**\n * Issue a session-scoped token (JWT) for WebSocket authentication.\n * Grants access to a single session's events.\n *\n * @param signingSecret - The product's signing secret\n * @param payload - Token payload with session ID\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueSessionScopedToken(\n signingSecret: string,\n payload: Omit<SessionScopedTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes: number,\n): string {\n return issueReadToken(signingSecret, payload, ttlMinutes);\n}\n\n/**\n * Issue a project-scoped token (JWT) for WebSocket authentication.\n * Grants access to all sessions within a single project.\n *\n * @param signingSecret - The product's signing secret\n * @param payload - Token payload with project ID\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueProjectScopedToken(\n signingSecret: string,\n payload: Omit<ProjectScopedTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes: number,\n): string {\n return issueReadToken(\n signingSecret,\n payload as Omit<ReadTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes,\n );\n}\n\n/**\n * Issue a batch-scoped token (JWT) for WebSocket authentication.\n * Grants access to multiple projects (organization-level access).\n *\n * @param signingSecret - The product's signing secret\n * @param payload - Token payload with project IDs array\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueBatchScopedToken(\n signingSecret: string,\n payload: Omit<BatchScopedTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes: number,\n): string {\n return issueReadToken(\n signingSecret,\n payload as Omit<ReadTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes,\n );\n}\n\n// Ed25519 JWT header (asymmetric — sidecar cannot forge tokens)\nconst JWT_HEADER_EDDSA = base64UrlEncode(\n JSON.stringify({ alg: \"EdDSA\", typ: \"JWT\" }),\n);\n\n/**\n * Capability strings carried in a sidecar token's `cap` claim. This is the\n * shared vocabulary the crypto layer stamps into the JWT (issueSidecarAccessToken)\n * and reads back out (verifySidecarToken). The route→capability authorization\n * policy that enforces these claims lives in the sidecar layer, not here.\n * Tokens with a `cap` claim are strictly limited to those capabilities; tokens\n * without a `cap` claim are full-scope (legacy orchestrator-internal use).\n *\n * Adding a new capability: extend this union, then update the enforcement\n * policy (route maps + authorization check) in the sidecar-auth package.\n */\nexport type SidecarCapability =\n | \"computer_use\"\n | \"read\"\n | \"debug\"\n | \"terminal\"\n | \"control\"\n | \"raw_input\";\n\n/**\n * Generate an Ed25519 key pair for per-session JWT signing.\n * Private key stays in orchestrator memory. Public key is injected into\n * the sidecar container. Even if the sidecar is fully compromised,\n * the attacker cannot forge JWTs for any container.\n *\n * @returns { privateKey, publicKey } as PEM strings\n */\nexport function generateSidecarKeyPair(): {\n privateKey: string;\n publicKey: string;\n} {\n const { privateKey, publicKey } = generateKeyPairSync(\"ed25519\", {\n publicKeyEncoding: { type: \"spki\", format: \"pem\" },\n privateKeyEncoding: { type: \"pkcs8\", format: \"pem\" },\n });\n return { privateKey, publicKey };\n}\n\n/**\n * Issue a sidecar access token (JWT) using Ed25519 signing.\n * Scoped to a specific container via the required `cid` claim.\n *\n * @param privateKey - Ed25519 private key (PEM). If not provided, falls back to HMAC.\n * @param hmacFallbackSecret - HMAC secret for backward compat (used when no Ed25519 key)\n * @param payload - Token payload with container ID\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueSidecarAccessToken(\n privateKey: string,\n payload: {\n sub: string;\n pid: string;\n cid: string;\n sid?: string;\n /**\n * Capability allowlist. When present, the token is restricted to\n * routes whose required capability is in this list, as enforced by the\n * sidecar-auth policy layer. Absent = full scope (legacy\n * orchestrator-internal tokens).\n */\n cap?: SidecarCapability[];\n },\n ttlMinutes: number,\n): string {\n if (!privateKey.includes(\"PRIVATE KEY\")) {\n throw new Error(\n \"issueSidecarAccessToken requires an Ed25519 private key (PEM). \" +\n \"HMAC fallback has been removed — generate a key pair with generateSidecarKeyPair().\",\n );\n }\n\n const now = Math.floor(Date.now() / 1000);\n // jti (JWT ID) enables token revocation — orchestrator adds jti to blocklist\n // on sandbox delete, sidecar checks blocklist on every request.\n const jti = `${payload.cid}:${now}:${randomBytes(8).toString(\"hex\")}`;\n const fullPayload = {\n ...payload,\n typ: \"sidecar\" as const,\n jti,\n iat: now,\n exp: now + ttlMinutes * 60,\n };\n const encodedPayload = base64UrlEncode(JSON.stringify(fullPayload));\n const data = `${JWT_HEADER_EDDSA}.${encodedPayload}`;\n const signature = base64UrlEncode(sign(null, Buffer.from(data), privateKey));\n return `${data}.${signature}`;\n}\n\n/**\n * Verify a sidecar JWT using Ed25519 public key. Rejects non-EdDSA tokens.\n * Returns decoded payload on success, null on failure.\n */\nexport function verifySidecarToken(\n token: string,\n publicKey: string,\n containerId: string,\n): {\n sub: string;\n pid: string;\n cid: string;\n sid?: string;\n typ: string;\n jti?: string;\n exp: number;\n cap?: SidecarCapability[];\n} | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) return null;\n\n const headerJson = Buffer.from(parts[0], \"base64url\").toString(\"utf-8\");\n const header = JSON.parse(headerJson);\n if (header.alg !== \"EdDSA\") return null;\n\n // SECURITY: verify signature BEFORE inspecting claims.\n // Parsing claims before verification creates a timing oracle that\n // leaks valid container IDs via early-return timing differences.\n const data = `${parts[0]}.${parts[1]}`;\n const signatureBuffer = Buffer.from(parts[2], \"base64url\");\n const valid = verify(null, Buffer.from(data), publicKey, signatureBuffer);\n if (!valid) return null;\n\n // Signature verified — now safe to parse and inspect claims\n const payload = JSON.parse(\n Buffer.from(parts[1], \"base64url\").toString(\"utf-8\"),\n );\n\n if (payload.typ !== \"sidecar\") return null;\n if (typeof payload.jti !== \"string\" || payload.jti.length === 0) {\n return null;\n }\n const exactContainerMatch = payload.cid === containerId;\n const shortDockerIdMatch = isDockerShortIdMatch(payload.cid, containerId);\n if (!exactContainerMatch && !shortDockerIdMatch) return null;\n\n // Require exp — tokens without expiration are rejected (no immortal tokens)\n const now = Math.floor(Date.now() / 1000);\n if (typeof payload.exp !== \"number\" || payload.exp < now) return null;\n\n // Require non-empty sub and cid\n if (typeof payload.sub !== \"string\" || payload.sub.length === 0)\n return null;\n\n return payload;\n } catch {\n return null;\n }\n}\n\nfunction isDockerShortIdMatch(\n payloadCid: unknown,\n containerId: unknown,\n): boolean {\n if (typeof payloadCid !== \"string\" || typeof containerId !== \"string\") {\n return false;\n }\n if (!/^[a-f0-9]{64}$/i.test(payloadCid)) {\n return false;\n }\n if (!/^[a-f0-9]{12}$/i.test(containerId)) {\n return false;\n }\n return payloadCid.startsWith(containerId);\n}\n\n/**\n * Check if a token payload is session-scoped.\n * Session-scoped tokens have a sid claim and no projectId/projectIds.\n */\nexport function isSessionScopedToken(\n payload: ReadTokenPayload,\n): payload is SessionScopedTokenPayload {\n return (\n \"sid\" in payload &&\n typeof payload.sid === \"string\" &&\n payload.sid.length > 0 &&\n !(\"projectId\" in payload && payload.projectId) &&\n !(\"projectIds\" in payload && payload.projectIds)\n );\n}\n\n/**\n * Check if a token payload is project-scoped.\n * Project-scoped tokens have a projectId claim and no sid/projectIds.\n */\nexport function isProjectScopedToken(\n payload: ReadTokenPayload,\n): payload is ProjectScopedTokenPayload {\n return (\n \"projectId\" in payload &&\n typeof payload.projectId === \"string\" &&\n payload.projectId.length > 0 &&\n !(\"sid\" in payload && payload.sid) &&\n !(\"projectIds\" in payload && payload.projectIds)\n );\n}\n\n/**\n * Check if a token payload is batch-scoped.\n * Batch-scoped tokens have a projectIds claim and no sid/projectId.\n */\nexport function isBatchScopedToken(\n payload: ReadTokenPayload,\n): payload is BatchScopedTokenPayload {\n return (\n \"projectIds\" in payload &&\n Array.isArray(payload.projectIds) &&\n payload.projectIds.length > 0 &&\n !(\"sid\" in payload && payload.sid) &&\n !(\"projectId\" in payload && payload.projectId)\n );\n}\n\n/**\n * Get the scope of a token payload.\n * Returns null if the token has an invalid/ambiguous scope.\n */\nexport function getTokenScope(payload: ReadTokenPayload): TokenScope | null {\n if (isSessionScopedToken(payload)) return \"session\";\n if (isProjectScopedToken(payload)) return \"project\";\n if (isBatchScopedToken(payload)) return \"batch\";\n return null;\n}\n\n/**\n * Validate that a token payload has exactly one valid scope.\n * Returns an error message if invalid, null if valid.\n */\nexport function validateTokenScope(payload: ReadTokenPayload): string | null {\n const hasSid = \"sid\" in payload && payload.sid;\n const hasProjectId = \"projectId\" in payload && payload.projectId;\n const hasProjectIds =\n \"projectIds\" in payload &&\n Array.isArray(payload.projectIds) &&\n payload.projectIds.length > 0;\n\n const scopeCount = [hasSid, hasProjectId, hasProjectIds].filter(\n Boolean,\n ).length;\n\n if (scopeCount === 0) {\n return \"Token must have one of: sid (session), projectId (project), or projectIds (batch)\";\n }\n\n if (scopeCount > 1) {\n return \"Token cannot have multiple scope claims (sid, projectId, projectIds are mutually exclusive)\";\n }\n\n return null;\n}\n\n/**\n * Decode a JWT without verification (to extract claims for lookup).\n * Returns null if the token is malformed.\n */\nexport function decodeToken(token: string): ReadTokenPayload | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n return null;\n }\n\n const payload = JSON.parse(base64UrlDecode(parts[1]));\n return payload as ReadTokenPayload;\n } catch {\n return null;\n }\n}\n\n/**\n * Verify a read token against a product's signing secrets.\n *\n * @param token - The JWT token to verify\n * @param product - Product auth info containing secrets\n */\nexport function verifyReadToken(\n token: string,\n product: ProductAuthInfo,\n): TokenValidationResult {\n // Check product status\n if (product.status !== \"active\") {\n return {\n valid: false,\n error: \"Product is suspended\",\n errorCode: \"PRODUCT_SUSPENDED\",\n };\n }\n\n // Parse token\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n return {\n valid: false,\n error: \"Invalid token format\",\n errorCode: \"TOKEN_INVALID\",\n };\n }\n\n const [header, encodedPayload, signature] = parts;\n const data = `${header}.${encodedPayload}`;\n\n // Pin algorithm to HS256 — defense-in-depth against alg-confusion\n // attacks that swap a forged header (e.g. `alg: \"none\"`) onto an\n // otherwise valid token. Fast path: when the header bytes match the\n // canonical `JWT_HEADER` verbatim, we skip the parse. Slow path:\n // decode the header and reject anything whose `alg` claim is not\n // `\"HS256\"`. Non-canonical-but-valid headers (e.g. `alg: \"HS256\"`\n // with an extra `kid` claim) fall through to signature verification,\n // which covers the actual header bytes — so any tampering with the\n // header still fails the signature check.\n if (header !== JWT_HEADER) {\n let parsedAlg: string | null = null;\n try {\n const decoded = JSON.parse(base64UrlDecode(header)) as Record<\n string,\n unknown\n >;\n if (typeof decoded.alg === \"string\") parsedAlg = decoded.alg;\n } catch {\n // Header didn't decode cleanly; treat as TOKEN_INVALID below.\n }\n if (parsedAlg !== \"HS256\") {\n return {\n valid: false,\n error: \"Unsupported token algorithm\",\n errorCode: \"TOKEN_INVALID\",\n };\n }\n }\n\n // Verify signature with current secret\n let signatureValid = verifySignature(\n data,\n signature,\n product.secrets.current.secret,\n );\n\n // If failed and we have a previous secret in grace period, try it\n if (!signatureValid && product.secrets.previous) {\n if (Date.now() < product.secrets.previous.expires_at) {\n signatureValid = verifySignature(\n data,\n signature,\n product.secrets.previous.secret,\n );\n }\n }\n\n if (!signatureValid) {\n return {\n valid: false,\n error: \"Invalid signature\",\n errorCode: \"SIGNATURE_INVALID\",\n };\n }\n\n // Parse payload\n let payload: ReadTokenPayload;\n try {\n payload = JSON.parse(base64UrlDecode(encodedPayload));\n } catch {\n return {\n valid: false,\n error: \"Invalid token payload\",\n errorCode: \"TOKEN_INVALID\",\n };\n }\n\n // Check token type\n if (payload.typ !== \"read\") {\n return {\n valid: false,\n error: \"Not a read token\",\n errorCode: \"TOKEN_WRONG_TYPE\",\n };\n }\n\n // Check expiration\n const now = Math.floor(Date.now() / 1000);\n if (payload.exp < now) {\n return {\n valid: false,\n error: \"Token expired\",\n errorCode: \"TOKEN_EXPIRED\",\n };\n }\n\n // Check product ID matches\n if (payload.pid !== product.product_id) {\n return {\n valid: false,\n error: \"Product ID mismatch\",\n errorCode: \"TOKEN_INVALID\",\n };\n }\n\n // Validate token scope (must have exactly one of sid, projectId, projectIds)\n const scopeError = validateTokenScope(payload);\n if (scopeError) {\n return {\n valid: false,\n error: scopeError,\n errorCode: \"TOKEN_INVALID\",\n };\n }\n\n return {\n valid: true,\n payload,\n scope: getTokenScope(payload) ?? undefined,\n };\n}\n\n/**\n * Get time until token expires (in seconds).\n * Returns negative if expired.\n */\nexport function getTokenTTL(payload: ReadTokenPayload): number {\n const now = Math.floor(Date.now() / 1000);\n return payload.exp - now;\n}\n\n/**\n * Check if token is expiring soon (within buffer seconds).\n */\nexport function isTokenExpiringSoon(\n payload: ReadTokenPayload,\n bufferSeconds = 60,\n): boolean {\n return getTokenTTL(payload) <= bufferSeconds;\n}\n\n/**\n * Default salt for API key hashing (development only).\n * Production MUST set API_KEY_HASH_SALT environment variable.\n * WARNING: Changing the salt will invalidate all existing API key hashes.\n */\nconst DEFAULT_API_KEY_SALT = \"api_key_hash_salt\";\n\nlet _saltWarned = false;\n\n/**\n * Get the API key hash salt from environment.\n * Logs a warning if falling back to the weak default (development only).\n */\nfunction getApiKeyHashSalt(): string {\n if (typeof process !== \"undefined\" && process.env?.API_KEY_HASH_SALT) {\n return process.env.API_KEY_HASH_SALT;\n }\n if (\n !_saltWarned &&\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV === \"production\"\n ) {\n _saltWarned = true;\n console.error(\n \"[SECURITY] API_KEY_HASH_SALT not set in production. \" +\n \"Using weak default salt. Set API_KEY_HASH_SALT to a cryptographically random value.\",\n );\n }\n return DEFAULT_API_KEY_SALT;\n}\n\n/**\n * Hash an API key for storage/lookup.\n * Uses HMAC-SHA256 for consistent, fast hashing.\n *\n * The salt can be configured via API_KEY_HASH_SALT environment variable.\n * WARNING: Changing the salt will invalidate all existing API key hashes.\n */\nexport function hashApiKey(apiKey: string): string {\n return createHmac(\"sha256\", getApiKeyHashSalt()).update(apiKey).digest(\"hex\");\n}\n\n/**\n * Hash an API key with an explicit salt.\n * Use this when you need to verify against a specific salt.\n */\nexport function hashApiKeyWithSalt(apiKey: string, salt: string): string {\n return createHmac(\"sha256\", salt).update(apiKey).digest(\"hex\");\n}\n\n/**\n * Verify an API key using timing-safe comparison.\n */\nexport function verifyApiKey(provided: string, stored: string): boolean {\n if (provided.length !== stored.length) {\n return false;\n }\n try {\n return timingSafeEqual(Buffer.from(provided), Buffer.from(stored));\n } catch {\n return false;\n }\n}\n","/**\n * Channel Access Control\n *\n * Determines what channels a token can access based on its scope.\n * Used by SessionGateway to validate subscriptions and event delivery.\n *\n * Channel patterns:\n * - session:{sessionId} - Events for a specific session\n * - agent:{sessionId} - Agent events for a specific session\n * - project:{projectId} - All events for a project\n * - system - System-wide events (heartbeat, etc.)\n */\n\nimport {\n isBatchScopedToken,\n isProjectScopedToken,\n isSessionScopedToken,\n} from \"./tokens.js\";\nimport type {\n BatchScopedTokenPayload,\n ProjectScopedTokenPayload,\n ReadTokenPayload,\n SessionScopedTokenPayload,\n} from \"./types.js\";\n\n/**\n * Result of channel access check.\n */\nexport interface ChannelAccessResult {\n /** Whether access is allowed */\n allowed: boolean;\n /** Reason for denial (if not allowed) */\n reason?: string;\n}\n\n/**\n * Extract session ID from a session channel pattern.\n * Returns null if not a session channel.\n */\nexport function extractSessionFromChannel(channel: string): string | null {\n if (channel.startsWith(\"session:\")) {\n return channel.slice(8);\n }\n if (channel.startsWith(\"agent:\")) {\n return channel.slice(6);\n }\n return null;\n}\n\n/**\n * Extract project ID from a project channel pattern.\n * Returns null if not a project channel.\n */\nexport function extractProjectFromChannel(channel: string): string | null {\n if (channel.startsWith(\"project:\")) {\n return channel.slice(8);\n }\n return null;\n}\n\n/**\n * Check if a session-scoped token can access a channel.\n *\n * Session tokens can access:\n * - session:{their-sid}\n * - agent:{their-sid}\n * - system (always allowed)\n * - Wildcard patterns matching their session\n */\nexport function canSessionTokenAccessChannel(\n payload: SessionScopedTokenPayload,\n channel: string,\n): ChannelAccessResult {\n // System channels are always allowed\n if (channel === \"system\" || channel.startsWith(\"system.\")) {\n return { allowed: true };\n }\n\n // Global wildcard requires higher scope\n if (channel === \"*\") {\n return {\n allowed: false,\n reason: \"Session-scoped tokens cannot subscribe to all channels\",\n };\n }\n\n // Check session channel match\n const sessionId = extractSessionFromChannel(channel);\n if (sessionId !== null) {\n if (sessionId === payload.sid) {\n return { allowed: true };\n }\n // Wildcard session channels not allowed for session tokens\n if (sessionId === \"*\") {\n return {\n allowed: false,\n reason: \"Session-scoped tokens cannot subscribe to all sessions\",\n };\n }\n return {\n allowed: false,\n reason: `Token only has access to session ${payload.sid}`,\n };\n }\n\n // Project channels require project scope\n if (channel.startsWith(\"project:\")) {\n return {\n allowed: false,\n reason: \"Session-scoped tokens cannot access project channels\",\n };\n }\n\n // Default: check if channel matches session pattern\n // Allow channels that don't have explicit scope (backward compatibility)\n return { allowed: true };\n}\n\n/**\n * Check if a project-scoped token can access a channel.\n *\n * Project tokens can access:\n * - project:{their-projectId}\n * - session:* (all sessions within project - requires validation by caller)\n * - agent:* (all agent events within project)\n * - system (always allowed)\n */\nexport function canProjectTokenAccessChannel(\n payload: ProjectScopedTokenPayload,\n channel: string,\n): ChannelAccessResult {\n // System channels are always allowed\n if (channel === \"system\" || channel.startsWith(\"system.\")) {\n return { allowed: true };\n }\n\n // Global wildcard requires batch scope or higher\n if (channel === \"*\") {\n return {\n allowed: false,\n reason:\n \"Project-scoped tokens cannot subscribe to all channels across projects\",\n };\n }\n\n // Project channel access\n const projectId = extractProjectFromChannel(channel);\n if (projectId !== null) {\n if (projectId === payload.projectId) {\n return { allowed: true };\n }\n return {\n allowed: false,\n reason: `Token only has access to project ${payload.projectId}`,\n };\n }\n\n // Session channels - project tokens can access any session in their project\n // The actual validation that session belongs to project must be done by the caller\n // who has access to the session-to-project mapping\n if (channel.startsWith(\"session:\") || channel.startsWith(\"agent:\")) {\n // Wildcard session channels allowed for project tokens\n const sessionId = extractSessionFromChannel(channel);\n if (sessionId === \"*\") {\n return { allowed: true };\n }\n // Specific session - allowed but caller must verify session is in project\n return { allowed: true };\n }\n\n // Default: allow general channels (backward compatibility)\n return { allowed: true };\n}\n\n/**\n * Check if a batch-scoped token can access a channel.\n *\n * Batch tokens can access:\n * - project:{any of their projectIds}\n * - session:* (all sessions within their projects)\n * - agent:* (all agent events within their projects)\n * - system (always allowed)\n * - * (global wildcard - for organization-wide subscriptions)\n */\nexport function canBatchTokenAccessChannel(\n payload: BatchScopedTokenPayload,\n channel: string,\n): ChannelAccessResult {\n // System channels are always allowed\n if (channel === \"system\" || channel.startsWith(\"system.\")) {\n return { allowed: true };\n }\n\n // Global wildcard allowed for batch tokens\n if (channel === \"*\") {\n return { allowed: true };\n }\n\n // Project channel access\n const projectId = extractProjectFromChannel(channel);\n if (projectId !== null) {\n // Wildcard project access allowed\n if (projectId === \"*\") {\n return { allowed: true };\n }\n // Specific project must be in the list\n if (payload.projectIds.includes(projectId)) {\n return { allowed: true };\n }\n return {\n allowed: false,\n reason: `Token does not have access to project ${projectId}`,\n };\n }\n\n // Session/agent channels - batch tokens can access any session in their projects\n // Caller must verify session belongs to one of the token's projects\n if (channel.startsWith(\"session:\") || channel.startsWith(\"agent:\")) {\n return { allowed: true };\n }\n\n // Default: allow general channels (backward compatibility)\n return { allowed: true };\n}\n\n/**\n * Check if a token can access a channel.\n * Dispatches to scope-specific functions based on token type.\n *\n * @param payload - The token payload\n * @param channel - The channel to check access for\n * @returns Access result with allowed boolean and optional reason\n */\nexport function canTokenAccessChannel(\n payload: ReadTokenPayload,\n channel: string,\n): ChannelAccessResult {\n // Use type guards for clean scope detection and type narrowing\n if (isSessionScopedToken(payload)) {\n return canSessionTokenAccessChannel(payload, channel);\n }\n\n if (isProjectScopedToken(payload)) {\n return canProjectTokenAccessChannel(payload, channel);\n }\n\n if (isBatchScopedToken(payload)) {\n return canBatchTokenAccessChannel(payload, channel);\n }\n\n // Invalid token scope\n return {\n allowed: false,\n reason: \"Token has no valid scope (missing sid, projectId, or projectIds)\",\n };\n}\n\n/**\n * Get the list of allowed channel patterns for a token.\n * Useful for informing clients what they can subscribe to.\n *\n * @param payload - The token payload\n * @returns Array of allowed channel patterns\n */\nexport function getAllowedChannelPatterns(payload: ReadTokenPayload): string[] {\n const patterns: string[] = [\"system\", \"system.*\"];\n\n // Use type guards for clean scope detection\n if (isSessionScopedToken(payload)) {\n patterns.push(`session:${payload.sid}`, `agent:${payload.sid}`);\n return patterns;\n }\n\n if (isProjectScopedToken(payload)) {\n patterns.push(\n `project:${payload.projectId}`,\n \"session:*\", // Within project\n \"agent:*\", // Within project\n );\n return patterns;\n }\n\n if (isBatchScopedToken(payload)) {\n patterns.push(\n \"*\", // Global wildcard\n \"project:*\", // All projects in batch\n ...payload.projectIds.map((id) => `project:${id}`),\n \"session:*\",\n \"agent:*\",\n );\n return patterns;\n }\n\n return patterns;\n}\n\n/**\n * Validate channel subscription request.\n * Checks multiple channels and returns aggregated result.\n *\n * @param payload - The token payload\n * @param channels - Channels to validate\n * @returns Object with allowed channels and denied channels with reasons\n */\nexport function validateChannelSubscription(\n payload: ReadTokenPayload,\n channels: string[],\n): {\n allowed: string[];\n denied: Array<{ channel: string; reason: string }>;\n} {\n const allowed: string[] = [];\n const denied: Array<{ channel: string; reason: string }> = [];\n\n for (const channel of channels) {\n const result = canTokenAccessChannel(payload, channel);\n if (result.allowed) {\n allowed.push(channel);\n } else {\n denied.push({ channel, reason: result.reason ?? \"Access denied\" });\n }\n }\n\n return { allowed, denied };\n}\n","/**\n * Authentication Configuration\n *\n * Token management with refresh hooks and expiration handling.\n */\n\nimport { issueReadToken } from \"./tokens.js\";\n\n// Re-export channel access utilities\nexport * from \"./channel-access.js\";\n// Re-export shared auth providers (eliminates 185 lines of duplication with browser.ts)\nexport type { AuthProvider } from \"./shared-providers.js\";\nexport {\n CallbackAuth,\n NoAuth,\n ReadTokenAuth,\n RefreshableTokenAuth,\n StaticTokenAuth,\n} from \"./shared-providers.js\";\nexport * from \"./tokens.js\";\n// Re-export types and token utilities\nexport * from \"./types.js\";\n\n/**\n * Environment-based auth that reads from env vars.\n * Matches the server-side patterns used in sidecar/orchestrator.\n */\nexport class EnvTokenAuth {\n private envVar: string;\n private fallback?: string;\n\n constructor(options: { envVar: string; fallback?: string }) {\n this.envVar = options.envVar;\n this.fallback = options.fallback;\n }\n\n async getToken(): Promise<string | null> {\n // Works in Node.js; in browser, fallback would be used\n if (typeof process !== \"undefined\" && process.env) {\n return process.env[this.envVar] ?? this.fallback ?? null;\n }\n return this.fallback ?? null;\n }\n}\n\n/**\n * Pre-configured auth for sidecar connections.\n * Uses SIDECAR_AUTH_TOKEN env var (matches server expectation).\n */\nexport class SidecarAuth extends EnvTokenAuth {\n constructor(token?: string) {\n super({ envVar: \"SIDECAR_AUTH_TOKEN\", fallback: token });\n }\n}\n\n/**\n * Product-based auth for multi-tenant orchestrator connections.\n * Uses ORCHESTRATOR_API_SECRET_KEY env var.\n */\nexport class ProductAuth extends EnvTokenAuth {\n constructor(apiKey?: string) {\n super({ envVar: \"ORCHESTRATOR_API_SECRET_KEY\", fallback: apiKey });\n }\n}\n\n/**\n * Product token issuer for backend services.\n *\n * Use this in product backends to issue read tokens for WebSocket connections.\n *\n * @example\n * ```typescript\n * import { ProductTokenIssuer } from \"@tangle-network/agent-core\";\n *\n * const issuer = new ProductTokenIssuer({\n * productId: \"vibecode\",\n * signingSecret: process.env.ORCHESTRATOR_SIGNING_SECRET!,\n * ttlMinutes: { free: 15, pro: 240 },\n * });\n *\n * // Issue a token for a user session\n * const token = issuer.issue({\n * userId: \"user_123\",\n * sessionId: \"sess_abc\",\n * tier: \"pro\",\n * });\n *\n * // Return token to frontend for WebSocket connection\n * res.json({\n * readToken: token.token,\n * expiresAt: token.expiresAt,\n * websocketUrl: `wss://orchestrator.example.com/session?token=${token.token}`,\n * });\n * ```\n */\nexport class ProductTokenIssuer {\n private readonly productId: string;\n private readonly signingSecret: string;\n private readonly ttlMinutes: {\n free: number;\n pro: number;\n enterprise?: number;\n };\n\n constructor(config: {\n productId: string;\n signingSecret: string;\n /** TTL in minutes for each tier (default: { free: 15, pro: 240 }) */\n ttlMinutes?: { free?: number; pro?: number; enterprise?: number };\n }) {\n this.productId = config.productId;\n this.signingSecret = config.signingSecret;\n this.ttlMinutes = {\n free: config.ttlMinutes?.free ?? 15,\n pro: config.ttlMinutes?.pro ?? 240,\n enterprise: config.ttlMinutes?.enterprise ?? 480,\n };\n }\n\n /**\n * Issue a read token for a user session.\n */\n issue(params: {\n userId: string;\n sessionId: string;\n tier?: \"free\" | \"pro\" | \"enterprise\";\n sidecarId?: string;\n }): { token: string; expiresAt: number } {\n const tier = params.tier ?? \"free\";\n const ttl = this.ttlMinutes[tier] ?? this.ttlMinutes.free;\n\n const token = issueReadToken(\n this.signingSecret,\n {\n sub: params.userId,\n sid: params.sessionId,\n pid: this.productId,\n cid: params.sidecarId,\n },\n ttl,\n );\n\n const expiresAt = Math.floor(Date.now() / 1000) + ttl * 60;\n\n return { token, expiresAt };\n }\n\n /**\n * Get the TTL in minutes for a tier.\n */\n getTtlMinutes(tier: \"free\" | \"pro\" | \"enterprise\" = \"free\"): number {\n return this.ttlMinutes[tier] ?? this.ttlMinutes.free;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/auth/tokens.ts","../../src/auth/channel-access.ts","../../src/auth/index.ts"],"sourcesContent":["/**\n * JWT Token Utilities\n *\n * Token generation and verification for multi-tenant auth.\n * - Read/product tokens: HMAC-SHA256 (symmetric, fast)\n * - Sidecar tokens: Ed25519 (asymmetric — sidecar cannot forge tokens)\n */\n\nimport type {\n BatchScopedTokenPayload,\n ProductAuthInfo,\n ProjectScopedTokenPayload,\n ReadTokenPayload,\n SessionScopedTokenPayload,\n TokenScope,\n TokenValidationResult,\n} from \"./types.js\";\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\ntype NodeCrypto = typeof import(\"node:crypto\");\nlet nodeCryptoCache: NodeCrypto | undefined;\n\n/**\n * Resolve Node's `crypto` lazily and synchronously. Signing, verification, and\n * key generation are server-only, but this module must stay importable in a\n * browser bundle — a static `import \"node:crypto\"` forces bundlers to resolve\n * the builtin at load and blanks the page. `process.getBuiltinModule` exists\n * only on a Node runtime (Node >= 22.3); in a browser `globalThis.process` is\n * undefined, so callers get a clear server-only error instead of a crash.\n */\nfunction nodeCrypto(): NodeCrypto {\n if (nodeCryptoCache) return nodeCryptoCache;\n const resolved = (\n globalThis as {\n process?: { getBuiltinModule?: (id: string) => unknown };\n }\n ).process?.getBuiltinModule?.(\"node:crypto\") as NodeCrypto | undefined;\n if (!resolved) {\n throw new Error(\n \"@tangle-network/agent-core/auth: token signing, verification, and key generation are server-only (Node.js crypto) and cannot run in a browser.\",\n );\n }\n nodeCryptoCache = resolved;\n return resolved;\n}\n\n/**\n * Generate a cryptographically secure random string.\n * @param prefix - Prefix for the generated string (e.g., \"orch_prod_\")\n * @param bytes - Number of random bytes (default: 32 = 256 bits)\n */\nexport function generateSecureToken(prefix: string, bytes = 32): string {\n return `${prefix}${base64UrlEncode(nodeCrypto().randomBytes(bytes))}`;\n}\n\n/**\n * Generate a product API key.\n */\nexport function generateApiKey(): string {\n return generateSecureToken(\"orch_prod_\");\n}\n\n/**\n * Generate a signing secret.\n */\nexport function generateSigningSecret(): string {\n return generateSecureToken(\"orch_sign_\");\n}\n\n/**\n * Base64URL encode (RFC 7515). Isomorphic: `btoa` + `TextEncoder` exist in Node\n * and browsers, so — unlike `Buffer` — this never blanks a browser page that\n * transitively imports the module.\n */\nfunction base64UrlEncode(data: string | Uint8Array): string {\n const bytes = typeof data === \"string\" ? textEncoder.encode(data) : data;\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\n/**\n * Base64URL decode (RFC 7515) to raw bytes.\n */\nfunction base64UrlToBytes(data: string): Uint8Array {\n const padded = data + \"=\".repeat((4 - (data.length % 4)) % 4);\n const binary = atob(padded.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n/**\n * Base64URL decode to a UTF-8 string.\n */\nfunction base64UrlDecode(data: string): string {\n return textDecoder.decode(base64UrlToBytes(data));\n}\n\n/**\n * Create HMAC-SHA256 signature.\n */\nfunction createSignature(data: string, secret: string): string {\n return base64UrlEncode(\n nodeCrypto().createHmac(\"sha256\", secret).update(data).digest(),\n );\n}\n\n/**\n * Verify signature using timing-safe comparison on raw HMAC bytes.\n *\n * Decoding both sides to raw bytes before the compare strips the\n * length-of-base64url-string oracle: every legitimate signature\n * decodes to exactly 32 bytes (HMAC-SHA256 digest size), so a\n * length mismatch on the decoded buffer means malformed input,\n * not a partial match. Equivalent to the pattern used in\n * `products/sandbox/sdk/src/auth/tokens.ts`.\n */\nfunction verifySignature(\n data: string,\n signature: string,\n secret: string,\n): boolean {\n const { createHmac, timingSafeEqual } = nodeCrypto();\n const expectedBuf = createHmac(\"sha256\", secret).update(data).digest();\n let providedBuf: Uint8Array;\n try {\n providedBuf = base64UrlToBytes(signature);\n } catch {\n return false;\n }\n if (providedBuf.length !== expectedBuf.length) return false;\n try {\n return timingSafeEqual(providedBuf, expectedBuf);\n } catch {\n return false;\n }\n}\n\n/**\n * Base64URL-encoded JWT header `{\"alg\":\"HS256\",\"typ\":\"JWT\"}`. Computed on first\n * use (not at module load) and memoized.\n */\nlet jwtHeaderCache: string | undefined;\nfunction jwtHeader(): string {\n if (jwtHeaderCache === undefined) {\n jwtHeaderCache = base64UrlEncode(\n JSON.stringify({ alg: \"HS256\", typ: \"JWT\" }),\n );\n }\n return jwtHeaderCache;\n}\n\n/**\n * Issue a read token (JWT) for WebSocket authentication.\n *\n * @param signingSecret - The product's signing secret\n * @param payload - Token payload (without iat/exp, those are added)\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueReadToken(\n signingSecret: string,\n payload: Omit<ReadTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes: number,\n): string {\n const now = Math.floor(Date.now() / 1000);\n\n // Type assertion is safe here: payload already satisfies one branch of the\n // ReadTokenPayload union (minus iat/exp/typ), and we're adding those fields.\n // TypeScript can't infer which union branch we're creating from a spread.\n const fullPayload = {\n ...payload,\n typ: \"read\" as const,\n iat: now,\n exp: now + ttlMinutes * 60,\n } as ReadTokenPayload;\n\n const encodedPayload = base64UrlEncode(JSON.stringify(fullPayload));\n const data = `${jwtHeader()}.${encodedPayload}`;\n const signature = createSignature(data, signingSecret);\n\n return `${data}.${signature}`;\n}\n\n/**\n * Issue a session-scoped token (JWT) for WebSocket authentication.\n * Grants access to a single session's events.\n *\n * @param signingSecret - The product's signing secret\n * @param payload - Token payload with session ID\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueSessionScopedToken(\n signingSecret: string,\n payload: Omit<SessionScopedTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes: number,\n): string {\n return issueReadToken(signingSecret, payload, ttlMinutes);\n}\n\n/**\n * Issue a project-scoped token (JWT) for WebSocket authentication.\n * Grants access to all sessions within a single project.\n *\n * @param signingSecret - The product's signing secret\n * @param payload - Token payload with project ID\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueProjectScopedToken(\n signingSecret: string,\n payload: Omit<ProjectScopedTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes: number,\n): string {\n return issueReadToken(\n signingSecret,\n payload as Omit<ReadTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes,\n );\n}\n\n/**\n * Issue a batch-scoped token (JWT) for WebSocket authentication.\n * Grants access to multiple projects (organization-level access).\n *\n * @param signingSecret - The product's signing secret\n * @param payload - Token payload with project IDs array\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueBatchScopedToken(\n signingSecret: string,\n payload: Omit<BatchScopedTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes: number,\n): string {\n return issueReadToken(\n signingSecret,\n payload as Omit<ReadTokenPayload, \"iat\" | \"exp\" | \"typ\">,\n ttlMinutes,\n );\n}\n\n// Ed25519 JWT header (asymmetric — sidecar cannot forge tokens); computed on\n// first use, not at module load, so the module stays browser-importable.\nlet jwtHeaderEddsaCache: string | undefined;\nfunction jwtHeaderEddsa(): string {\n if (jwtHeaderEddsaCache === undefined) {\n jwtHeaderEddsaCache = base64UrlEncode(\n JSON.stringify({ alg: \"EdDSA\", typ: \"JWT\" }),\n );\n }\n return jwtHeaderEddsaCache;\n}\n\n/**\n * Capability strings carried in a sidecar token's `cap` claim. This is the\n * shared vocabulary the crypto layer stamps into the JWT (issueSidecarAccessToken)\n * and reads back out (verifySidecarToken). The route→capability authorization\n * policy that enforces these claims lives in the sidecar layer, not here.\n * Tokens with a `cap` claim are strictly limited to those capabilities; tokens\n * without a `cap` claim are full-scope (legacy orchestrator-internal use).\n *\n * Adding a new capability: extend this union, then update the enforcement\n * policy (route maps + authorization check) in the sidecar-auth package.\n */\nexport type SidecarCapability =\n | \"computer_use\"\n | \"read\"\n | \"debug\"\n | \"terminal\"\n | \"control\"\n | \"raw_input\";\n\n/**\n * Generate an Ed25519 key pair for per-session JWT signing.\n * Private key stays in orchestrator memory. Public key is injected into\n * the sidecar container. Even if the sidecar is fully compromised,\n * the attacker cannot forge JWTs for any container.\n *\n * @returns { privateKey, publicKey } as PEM strings\n */\nexport function generateSidecarKeyPair(): {\n privateKey: string;\n publicKey: string;\n} {\n const { privateKey, publicKey } = nodeCrypto().generateKeyPairSync(\n \"ed25519\",\n {\n publicKeyEncoding: { type: \"spki\", format: \"pem\" },\n privateKeyEncoding: { type: \"pkcs8\", format: \"pem\" },\n },\n );\n return { privateKey, publicKey };\n}\n\n/**\n * Issue a sidecar access token (JWT) using Ed25519 signing.\n * Scoped to a specific container via the required `cid` claim.\n *\n * @param privateKey - Ed25519 private key (PEM). If not provided, falls back to HMAC.\n * @param hmacFallbackSecret - HMAC secret for backward compat (used when no Ed25519 key)\n * @param payload - Token payload with container ID\n * @param ttlMinutes - Token TTL in minutes\n */\nexport function issueSidecarAccessToken(\n privateKey: string,\n payload: {\n sub: string;\n pid: string;\n cid: string;\n sid?: string;\n /**\n * Capability allowlist. When present, the token is restricted to\n * routes whose required capability is in this list, as enforced by the\n * sidecar-auth policy layer. Absent = full scope (legacy\n * orchestrator-internal tokens).\n */\n cap?: SidecarCapability[];\n },\n ttlMinutes: number,\n): string {\n if (!privateKey.includes(\"PRIVATE KEY\")) {\n throw new Error(\n \"issueSidecarAccessToken requires an Ed25519 private key (PEM). \" +\n \"HMAC fallback has been removed — generate a key pair with generateSidecarKeyPair().\",\n );\n }\n\n const now = Math.floor(Date.now() / 1000);\n // jti (JWT ID) enables token revocation — orchestrator adds jti to blocklist\n // on sandbox delete, sidecar checks blocklist on every request.\n const { randomBytes, sign } = nodeCrypto();\n const jti = `${payload.cid}:${now}:${randomBytes(8).toString(\"hex\")}`;\n const fullPayload = {\n ...payload,\n typ: \"sidecar\" as const,\n jti,\n iat: now,\n exp: now + ttlMinutes * 60,\n };\n const encodedPayload = base64UrlEncode(JSON.stringify(fullPayload));\n const data = `${jwtHeaderEddsa()}.${encodedPayload}`;\n const signature = base64UrlEncode(\n sign(null, textEncoder.encode(data), privateKey),\n );\n return `${data}.${signature}`;\n}\n\n/**\n * Verify a sidecar JWT using Ed25519 public key. Rejects non-EdDSA tokens.\n * Returns decoded payload on success, null on failure.\n */\nexport function verifySidecarToken(\n token: string,\n publicKey: string,\n containerId: string,\n): {\n sub: string;\n pid: string;\n cid: string;\n sid?: string;\n typ: string;\n jti?: string;\n exp: number;\n cap?: SidecarCapability[];\n} | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) return null;\n\n const headerJson = base64UrlDecode(parts[0]);\n const header = JSON.parse(headerJson);\n if (header.alg !== \"EdDSA\") return null;\n\n // SECURITY: verify signature BEFORE inspecting claims.\n // Parsing claims before verification creates a timing oracle that\n // leaks valid container IDs via early-return timing differences.\n const data = `${parts[0]}.${parts[1]}`;\n const signatureBuffer = base64UrlToBytes(parts[2]);\n const valid = nodeCrypto().verify(\n null,\n textEncoder.encode(data),\n publicKey,\n signatureBuffer,\n );\n if (!valid) return null;\n\n // Signature verified — now safe to parse and inspect claims\n const payload = JSON.parse(base64UrlDecode(parts[1]));\n\n if (payload.typ !== \"sidecar\") return null;\n if (typeof payload.jti !== \"string\" || payload.jti.length === 0) {\n return null;\n }\n const exactContainerMatch = payload.cid === containerId;\n const shortDockerIdMatch = isDockerShortIdMatch(payload.cid, containerId);\n if (!exactContainerMatch && !shortDockerIdMatch) return null;\n\n // Require exp — tokens without expiration are rejected (no immortal tokens)\n const now = Math.floor(Date.now() / 1000);\n if (typeof payload.exp !== \"number\" || payload.exp < now) return null;\n\n // Require non-empty sub and cid\n if (typeof payload.sub !== \"string\" || payload.sub.length === 0)\n return null;\n\n return payload;\n } catch {\n return null;\n }\n}\n\nfunction isDockerShortIdMatch(\n payloadCid: unknown,\n containerId: unknown,\n): boolean {\n if (typeof payloadCid !== \"string\" || typeof containerId !== \"string\") {\n return false;\n }\n if (!/^[a-f0-9]{64}$/i.test(payloadCid)) {\n return false;\n }\n if (!/^[a-f0-9]{12}$/i.test(containerId)) {\n return false;\n }\n return payloadCid.startsWith(containerId);\n}\n\n/**\n * Check if a token payload is session-scoped.\n * Session-scoped tokens have a sid claim and no projectId/projectIds.\n */\nexport function isSessionScopedToken(\n payload: ReadTokenPayload,\n): payload is SessionScopedTokenPayload {\n return (\n \"sid\" in payload &&\n typeof payload.sid === \"string\" &&\n payload.sid.length > 0 &&\n !(\"projectId\" in payload && payload.projectId) &&\n !(\"projectIds\" in payload && payload.projectIds)\n );\n}\n\n/**\n * Check if a token payload is project-scoped.\n * Project-scoped tokens have a projectId claim and no sid/projectIds.\n */\nexport function isProjectScopedToken(\n payload: ReadTokenPayload,\n): payload is ProjectScopedTokenPayload {\n return (\n \"projectId\" in payload &&\n typeof payload.projectId === \"string\" &&\n payload.projectId.length > 0 &&\n !(\"sid\" in payload && payload.sid) &&\n !(\"projectIds\" in payload && payload.projectIds)\n );\n}\n\n/**\n * Check if a token payload is batch-scoped.\n * Batch-scoped tokens have a projectIds claim and no sid/projectId.\n */\nexport function isBatchScopedToken(\n payload: ReadTokenPayload,\n): payload is BatchScopedTokenPayload {\n return (\n \"projectIds\" in payload &&\n Array.isArray(payload.projectIds) &&\n payload.projectIds.length > 0 &&\n !(\"sid\" in payload && payload.sid) &&\n !(\"projectId\" in payload && payload.projectId)\n );\n}\n\n/**\n * Get the scope of a token payload.\n * Returns null if the token has an invalid/ambiguous scope.\n */\nexport function getTokenScope(payload: ReadTokenPayload): TokenScope | null {\n if (isSessionScopedToken(payload)) return \"session\";\n if (isProjectScopedToken(payload)) return \"project\";\n if (isBatchScopedToken(payload)) return \"batch\";\n return null;\n}\n\n/**\n * Validate that a token payload has exactly one valid scope.\n * Returns an error message if invalid, null if valid.\n */\nexport function validateTokenScope(payload: ReadTokenPayload): string | null {\n const hasSid = \"sid\" in payload && payload.sid;\n const hasProjectId = \"projectId\" in payload && payload.projectId;\n const hasProjectIds =\n \"projectIds\" in payload &&\n Array.isArray(payload.projectIds) &&\n payload.projectIds.length > 0;\n\n const scopeCount = [hasSid, hasProjectId, hasProjectIds].filter(\n Boolean,\n ).length;\n\n if (scopeCount === 0) {\n return \"Token must have one of: sid (session), projectId (project), or projectIds (batch)\";\n }\n\n if (scopeCount > 1) {\n return \"Token cannot have multiple scope claims (sid, projectId, projectIds are mutually exclusive)\";\n }\n\n return null;\n}\n\n/**\n * Decode a JWT without verification (to extract claims for lookup).\n * Returns null if the token is malformed.\n */\nexport function decodeToken(token: string): ReadTokenPayload | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n return null;\n }\n\n const payload = JSON.parse(base64UrlDecode(parts[1]));\n return payload as ReadTokenPayload;\n } catch {\n return null;\n }\n}\n\n/**\n * Verify a read token against a product's signing secrets.\n *\n * @param token - The JWT token to verify\n * @param product - Product auth info containing secrets\n */\nexport function verifyReadToken(\n token: string,\n product: ProductAuthInfo,\n): TokenValidationResult {\n // Check product status\n if (product.status !== \"active\") {\n return {\n valid: false,\n error: \"Product is suspended\",\n errorCode: \"PRODUCT_SUSPENDED\",\n };\n }\n\n // Parse token\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n return {\n valid: false,\n error: \"Invalid token format\",\n errorCode: \"TOKEN_INVALID\",\n };\n }\n\n const [header, encodedPayload, signature] = parts;\n const data = `${header}.${encodedPayload}`;\n\n // Pin algorithm to HS256 — defense-in-depth against alg-confusion\n // attacks that swap a forged header (e.g. `alg: \"none\"`) onto an\n // otherwise valid token. Fast path: when the header bytes match the\n // canonical `JWT_HEADER` verbatim, we skip the parse. Slow path:\n // decode the header and reject anything whose `alg` claim is not\n // `\"HS256\"`. Non-canonical-but-valid headers (e.g. `alg: \"HS256\"`\n // with an extra `kid` claim) fall through to signature verification,\n // which covers the actual header bytes — so any tampering with the\n // header still fails the signature check.\n if (header !== jwtHeader()) {\n let parsedAlg: string | null = null;\n try {\n const decoded = JSON.parse(base64UrlDecode(header)) as Record<\n string,\n unknown\n >;\n if (typeof decoded.alg === \"string\") parsedAlg = decoded.alg;\n } catch {\n // Header didn't decode cleanly; treat as TOKEN_INVALID below.\n }\n if (parsedAlg !== \"HS256\") {\n return {\n valid: false,\n error: \"Unsupported token algorithm\",\n errorCode: \"TOKEN_INVALID\",\n };\n }\n }\n\n // Verify signature with current secret\n let signatureValid = verifySignature(\n data,\n signature,\n product.secrets.current.secret,\n );\n\n // If failed and we have a previous secret in grace period, try it\n if (!signatureValid && product.secrets.previous) {\n if (Date.now() < product.secrets.previous.expires_at) {\n signatureValid = verifySignature(\n data,\n signature,\n product.secrets.previous.secret,\n );\n }\n }\n\n if (!signatureValid) {\n return {\n valid: false,\n error: \"Invalid signature\",\n errorCode: \"SIGNATURE_INVALID\",\n };\n }\n\n // Parse payload\n let payload: ReadTokenPayload;\n try {\n payload = JSON.parse(base64UrlDecode(encodedPayload));\n } catch {\n return {\n valid: false,\n error: \"Invalid token payload\",\n errorCode: \"TOKEN_INVALID\",\n };\n }\n\n // Check token type\n if (payload.typ !== \"read\") {\n return {\n valid: false,\n error: \"Not a read token\",\n errorCode: \"TOKEN_WRONG_TYPE\",\n };\n }\n\n // Check expiration\n const now = Math.floor(Date.now() / 1000);\n if (payload.exp < now) {\n return {\n valid: false,\n error: \"Token expired\",\n errorCode: \"TOKEN_EXPIRED\",\n };\n }\n\n // Check product ID matches\n if (payload.pid !== product.product_id) {\n return {\n valid: false,\n error: \"Product ID mismatch\",\n errorCode: \"TOKEN_INVALID\",\n };\n }\n\n // Validate token scope (must have exactly one of sid, projectId, projectIds)\n const scopeError = validateTokenScope(payload);\n if (scopeError) {\n return {\n valid: false,\n error: scopeError,\n errorCode: \"TOKEN_INVALID\",\n };\n }\n\n return {\n valid: true,\n payload,\n scope: getTokenScope(payload) ?? undefined,\n };\n}\n\n/**\n * Get time until token expires (in seconds).\n * Returns negative if expired.\n */\nexport function getTokenTTL(payload: ReadTokenPayload): number {\n const now = Math.floor(Date.now() / 1000);\n return payload.exp - now;\n}\n\n/**\n * Check if token is expiring soon (within buffer seconds).\n */\nexport function isTokenExpiringSoon(\n payload: ReadTokenPayload,\n bufferSeconds = 60,\n): boolean {\n return getTokenTTL(payload) <= bufferSeconds;\n}\n\n/**\n * Default salt for API key hashing (development only).\n * Production MUST set API_KEY_HASH_SALT environment variable.\n * WARNING: Changing the salt will invalidate all existing API key hashes.\n */\nconst DEFAULT_API_KEY_SALT = \"api_key_hash_salt\";\n\nlet _saltWarned = false;\n\n/**\n * Get the API key hash salt from environment.\n * Logs a warning if falling back to the weak default (development only).\n */\nfunction getApiKeyHashSalt(): string {\n if (typeof process !== \"undefined\" && process.env?.API_KEY_HASH_SALT) {\n return process.env.API_KEY_HASH_SALT;\n }\n if (\n !_saltWarned &&\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV === \"production\"\n ) {\n _saltWarned = true;\n console.error(\n \"[SECURITY] API_KEY_HASH_SALT not set in production. \" +\n \"Using weak default salt. Set API_KEY_HASH_SALT to a cryptographically random value.\",\n );\n }\n return DEFAULT_API_KEY_SALT;\n}\n\n/**\n * Hash an API key for storage/lookup.\n * Uses HMAC-SHA256 for consistent, fast hashing.\n *\n * The salt can be configured via API_KEY_HASH_SALT environment variable.\n * WARNING: Changing the salt will invalidate all existing API key hashes.\n */\nexport function hashApiKey(apiKey: string): string {\n return nodeCrypto()\n .createHmac(\"sha256\", getApiKeyHashSalt())\n .update(apiKey)\n .digest(\"hex\");\n}\n\n/**\n * Hash an API key with an explicit salt.\n * Use this when you need to verify against a specific salt.\n */\nexport function hashApiKeyWithSalt(apiKey: string, salt: string): string {\n return nodeCrypto().createHmac(\"sha256\", salt).update(apiKey).digest(\"hex\");\n}\n\n/**\n * Verify an API key using timing-safe comparison.\n */\nexport function verifyApiKey(provided: string, stored: string): boolean {\n if (provided.length !== stored.length) {\n return false;\n }\n try {\n return nodeCrypto().timingSafeEqual(\n textEncoder.encode(provided),\n textEncoder.encode(stored),\n );\n } catch {\n return false;\n }\n}\n","/**\n * Channel Access Control\n *\n * Determines what channels a token can access based on its scope.\n * Used by SessionGateway to validate subscriptions and event delivery.\n *\n * Channel patterns:\n * - session:{sessionId} - Events for a specific session\n * - agent:{sessionId} - Agent events for a specific session\n * - project:{projectId} - All events for a project\n * - system - System-wide events (heartbeat, etc.)\n */\n\nimport {\n isBatchScopedToken,\n isProjectScopedToken,\n isSessionScopedToken,\n} from \"./tokens.js\";\nimport type {\n BatchScopedTokenPayload,\n ProjectScopedTokenPayload,\n ReadTokenPayload,\n SessionScopedTokenPayload,\n} from \"./types.js\";\n\n/**\n * Result of channel access check.\n */\nexport interface ChannelAccessResult {\n /** Whether access is allowed */\n allowed: boolean;\n /** Reason for denial (if not allowed) */\n reason?: string;\n}\n\n/**\n * Extract session ID from a session channel pattern.\n * Returns null if not a session channel.\n */\nexport function extractSessionFromChannel(channel: string): string | null {\n if (channel.startsWith(\"session:\")) {\n return channel.slice(8);\n }\n if (channel.startsWith(\"agent:\")) {\n return channel.slice(6);\n }\n return null;\n}\n\n/**\n * Extract project ID from a project channel pattern.\n * Returns null if not a project channel.\n */\nexport function extractProjectFromChannel(channel: string): string | null {\n if (channel.startsWith(\"project:\")) {\n return channel.slice(8);\n }\n return null;\n}\n\n/**\n * Check if a session-scoped token can access a channel.\n *\n * Session tokens can access:\n * - session:{their-sid}\n * - agent:{their-sid}\n * - system (always allowed)\n * - Wildcard patterns matching their session\n */\nexport function canSessionTokenAccessChannel(\n payload: SessionScopedTokenPayload,\n channel: string,\n): ChannelAccessResult {\n // System channels are always allowed\n if (channel === \"system\" || channel.startsWith(\"system.\")) {\n return { allowed: true };\n }\n\n // Global wildcard requires higher scope\n if (channel === \"*\") {\n return {\n allowed: false,\n reason: \"Session-scoped tokens cannot subscribe to all channels\",\n };\n }\n\n // Check session channel match\n const sessionId = extractSessionFromChannel(channel);\n if (sessionId !== null) {\n if (sessionId === payload.sid) {\n return { allowed: true };\n }\n // Wildcard session channels not allowed for session tokens\n if (sessionId === \"*\") {\n return {\n allowed: false,\n reason: \"Session-scoped tokens cannot subscribe to all sessions\",\n };\n }\n return {\n allowed: false,\n reason: `Token only has access to session ${payload.sid}`,\n };\n }\n\n // Project channels require project scope\n if (channel.startsWith(\"project:\")) {\n return {\n allowed: false,\n reason: \"Session-scoped tokens cannot access project channels\",\n };\n }\n\n // Default: check if channel matches session pattern\n // Allow channels that don't have explicit scope (backward compatibility)\n return { allowed: true };\n}\n\n/**\n * Check if a project-scoped token can access a channel.\n *\n * Project tokens can access:\n * - project:{their-projectId}\n * - session:* (all sessions within project - requires validation by caller)\n * - agent:* (all agent events within project)\n * - system (always allowed)\n */\nexport function canProjectTokenAccessChannel(\n payload: ProjectScopedTokenPayload,\n channel: string,\n): ChannelAccessResult {\n // System channels are always allowed\n if (channel === \"system\" || channel.startsWith(\"system.\")) {\n return { allowed: true };\n }\n\n // Global wildcard requires batch scope or higher\n if (channel === \"*\") {\n return {\n allowed: false,\n reason:\n \"Project-scoped tokens cannot subscribe to all channels across projects\",\n };\n }\n\n // Project channel access\n const projectId = extractProjectFromChannel(channel);\n if (projectId !== null) {\n if (projectId === payload.projectId) {\n return { allowed: true };\n }\n return {\n allowed: false,\n reason: `Token only has access to project ${payload.projectId}`,\n };\n }\n\n // Session channels - project tokens can access any session in their project\n // The actual validation that session belongs to project must be done by the caller\n // who has access to the session-to-project mapping\n if (channel.startsWith(\"session:\") || channel.startsWith(\"agent:\")) {\n // Wildcard session channels allowed for project tokens\n const sessionId = extractSessionFromChannel(channel);\n if (sessionId === \"*\") {\n return { allowed: true };\n }\n // Specific session - allowed but caller must verify session is in project\n return { allowed: true };\n }\n\n // Default: allow general channels (backward compatibility)\n return { allowed: true };\n}\n\n/**\n * Check if a batch-scoped token can access a channel.\n *\n * Batch tokens can access:\n * - project:{any of their projectIds}\n * - session:* (all sessions within their projects)\n * - agent:* (all agent events within their projects)\n * - system (always allowed)\n * - * (global wildcard - for organization-wide subscriptions)\n */\nexport function canBatchTokenAccessChannel(\n payload: BatchScopedTokenPayload,\n channel: string,\n): ChannelAccessResult {\n // System channels are always allowed\n if (channel === \"system\" || channel.startsWith(\"system.\")) {\n return { allowed: true };\n }\n\n // Global wildcard allowed for batch tokens\n if (channel === \"*\") {\n return { allowed: true };\n }\n\n // Project channel access\n const projectId = extractProjectFromChannel(channel);\n if (projectId !== null) {\n // Wildcard project access allowed\n if (projectId === \"*\") {\n return { allowed: true };\n }\n // Specific project must be in the list\n if (payload.projectIds.includes(projectId)) {\n return { allowed: true };\n }\n return {\n allowed: false,\n reason: `Token does not have access to project ${projectId}`,\n };\n }\n\n // Session/agent channels - batch tokens can access any session in their projects\n // Caller must verify session belongs to one of the token's projects\n if (channel.startsWith(\"session:\") || channel.startsWith(\"agent:\")) {\n return { allowed: true };\n }\n\n // Default: allow general channels (backward compatibility)\n return { allowed: true };\n}\n\n/**\n * Check if a token can access a channel.\n * Dispatches to scope-specific functions based on token type.\n *\n * @param payload - The token payload\n * @param channel - The channel to check access for\n * @returns Access result with allowed boolean and optional reason\n */\nexport function canTokenAccessChannel(\n payload: ReadTokenPayload,\n channel: string,\n): ChannelAccessResult {\n // Use type guards for clean scope detection and type narrowing\n if (isSessionScopedToken(payload)) {\n return canSessionTokenAccessChannel(payload, channel);\n }\n\n if (isProjectScopedToken(payload)) {\n return canProjectTokenAccessChannel(payload, channel);\n }\n\n if (isBatchScopedToken(payload)) {\n return canBatchTokenAccessChannel(payload, channel);\n }\n\n // Invalid token scope\n return {\n allowed: false,\n reason: \"Token has no valid scope (missing sid, projectId, or projectIds)\",\n };\n}\n\n/**\n * Get the list of allowed channel patterns for a token.\n * Useful for informing clients what they can subscribe to.\n *\n * @param payload - The token payload\n * @returns Array of allowed channel patterns\n */\nexport function getAllowedChannelPatterns(payload: ReadTokenPayload): string[] {\n const patterns: string[] = [\"system\", \"system.*\"];\n\n // Use type guards for clean scope detection\n if (isSessionScopedToken(payload)) {\n patterns.push(`session:${payload.sid}`, `agent:${payload.sid}`);\n return patterns;\n }\n\n if (isProjectScopedToken(payload)) {\n patterns.push(\n `project:${payload.projectId}`,\n \"session:*\", // Within project\n \"agent:*\", // Within project\n );\n return patterns;\n }\n\n if (isBatchScopedToken(payload)) {\n patterns.push(\n \"*\", // Global wildcard\n \"project:*\", // All projects in batch\n ...payload.projectIds.map((id) => `project:${id}`),\n \"session:*\",\n \"agent:*\",\n );\n return patterns;\n }\n\n return patterns;\n}\n\n/**\n * Validate channel subscription request.\n * Checks multiple channels and returns aggregated result.\n *\n * @param payload - The token payload\n * @param channels - Channels to validate\n * @returns Object with allowed channels and denied channels with reasons\n */\nexport function validateChannelSubscription(\n payload: ReadTokenPayload,\n channels: string[],\n): {\n allowed: string[];\n denied: Array<{ channel: string; reason: string }>;\n} {\n const allowed: string[] = [];\n const denied: Array<{ channel: string; reason: string }> = [];\n\n for (const channel of channels) {\n const result = canTokenAccessChannel(payload, channel);\n if (result.allowed) {\n allowed.push(channel);\n } else {\n denied.push({ channel, reason: result.reason ?? \"Access denied\" });\n }\n }\n\n return { allowed, denied };\n}\n","/**\n * Authentication Configuration\n *\n * Token management with refresh hooks and expiration handling.\n */\n\nimport { issueReadToken } from \"./tokens.js\";\n\n// Re-export channel access utilities\nexport * from \"./channel-access.js\";\n// Re-export shared auth providers (eliminates 185 lines of duplication with browser.ts)\nexport type { AuthProvider } from \"./shared-providers.js\";\nexport {\n CallbackAuth,\n NoAuth,\n ReadTokenAuth,\n RefreshableTokenAuth,\n StaticTokenAuth,\n} from \"./shared-providers.js\";\nexport * from \"./tokens.js\";\n// Re-export types and token utilities\nexport * from \"./types.js\";\n\n/**\n * Environment-based auth that reads from env vars.\n * Matches the server-side patterns used in sidecar/orchestrator.\n */\nexport class EnvTokenAuth {\n private envVar: string;\n private fallback?: string;\n\n constructor(options: { envVar: string; fallback?: string }) {\n this.envVar = options.envVar;\n this.fallback = options.fallback;\n }\n\n async getToken(): Promise<string | null> {\n // Works in Node.js; in browser, fallback would be used\n if (typeof process !== \"undefined\" && process.env) {\n return process.env[this.envVar] ?? this.fallback ?? null;\n }\n return this.fallback ?? null;\n }\n}\n\n/**\n * Pre-configured auth for sidecar connections.\n * Uses SIDECAR_AUTH_TOKEN env var (matches server expectation).\n */\nexport class SidecarAuth extends EnvTokenAuth {\n constructor(token?: string) {\n super({ envVar: \"SIDECAR_AUTH_TOKEN\", fallback: token });\n }\n}\n\n/**\n * Product-based auth for multi-tenant orchestrator connections.\n * Uses ORCHESTRATOR_API_SECRET_KEY env var.\n */\nexport class ProductAuth extends EnvTokenAuth {\n constructor(apiKey?: string) {\n super({ envVar: \"ORCHESTRATOR_API_SECRET_KEY\", fallback: apiKey });\n }\n}\n\n/**\n * Product token issuer for backend services.\n *\n * Use this in product backends to issue read tokens for WebSocket connections.\n *\n * @example\n * ```typescript\n * import { ProductTokenIssuer } from \"@tangle-network/agent-core\";\n *\n * const issuer = new ProductTokenIssuer({\n * productId: \"vibecode\",\n * signingSecret: process.env.ORCHESTRATOR_SIGNING_SECRET!,\n * ttlMinutes: { free: 15, pro: 240 },\n * });\n *\n * // Issue a token for a user session\n * const token = issuer.issue({\n * userId: \"user_123\",\n * sessionId: \"sess_abc\",\n * tier: \"pro\",\n * });\n *\n * // Return token to frontend for WebSocket connection\n * res.json({\n * readToken: token.token,\n * expiresAt: token.expiresAt,\n * websocketUrl: `wss://orchestrator.example.com/session?token=${token.token}`,\n * });\n * ```\n */\nexport class ProductTokenIssuer {\n private readonly productId: string;\n private readonly signingSecret: string;\n private readonly ttlMinutes: {\n free: number;\n pro: number;\n enterprise?: number;\n };\n\n constructor(config: {\n productId: string;\n signingSecret: string;\n /** TTL in minutes for each tier (default: { free: 15, pro: 240 }) */\n ttlMinutes?: { free?: number; pro?: number; enterprise?: number };\n }) {\n this.productId = config.productId;\n this.signingSecret = config.signingSecret;\n this.ttlMinutes = {\n free: config.ttlMinutes?.free ?? 15,\n pro: config.ttlMinutes?.pro ?? 240,\n enterprise: config.ttlMinutes?.enterprise ?? 480,\n };\n }\n\n /**\n * Issue a read token for a user session.\n */\n issue(params: {\n userId: string;\n sessionId: string;\n tier?: \"free\" | \"pro\" | \"enterprise\";\n sidecarId?: string;\n }): { token: string; expiresAt: number } {\n const tier = params.tier ?? \"free\";\n const ttl = this.ttlMinutes[tier] ?? this.ttlMinutes.free;\n\n const token = issueReadToken(\n this.signingSecret,\n {\n sub: params.userId,\n sid: params.sessionId,\n pid: this.productId,\n cid: params.sidecarId,\n },\n ttl,\n );\n\n const expiresAt = Math.floor(Date.now() / 1000) + ttl * 60;\n\n return { token, expiresAt };\n }\n\n /**\n * Get the TTL in minutes for a tier.\n */\n getTtlMinutes(tier: \"free\" | \"pro\" | \"enterprise\" = \"free\"): number {\n return this.ttlMinutes[tier] ?? this.ttlMinutes.free;\n }\n}\n"],"mappings":"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"}
|
|
@@ -73,6 +73,23 @@ declare function asContentField(value: unknown): string | null;
|
|
|
73
73
|
* objects/arrays are JSON-stringified so a structured tool payload is still
|
|
74
74
|
* readable content. Null/empty → null (never "" or "null"). */
|
|
75
75
|
declare function asContentString(value: unknown): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* SQL `LIKE` patterns (over `jsonb_object_keys`) that detect indexed / array /
|
|
78
|
+
* tool-call content on a flattened bag — the DB-side complement to
|
|
79
|
+
* `normalizeContentAttributes` for a reader (e.g. the onboarding "content seen"
|
|
80
|
+
* check) that must decide "does any content-bearing key exist" in SQL without
|
|
81
|
+
* materializing rows. Kept beside the reconstruction so the in-process reader and
|
|
82
|
+
* the SQL predicate can never drift on which shapes count as content.
|
|
83
|
+
*/
|
|
84
|
+
declare const INDEXED_CONTENT_KEY_LIKE_PATTERNS: readonly string[];
|
|
85
|
+
/**
|
|
86
|
+
* Reconstruct a flattened attribute bag's indexed / array / tool-call content
|
|
87
|
+
* keys into the canonical coarse aliases the reader understands. Pure and
|
|
88
|
+
* NON-DESTRUCTIVE: an already-present coarse key wins, and the SAME reference is
|
|
89
|
+
* returned when nothing needs reconstructing. Every content read path runs this
|
|
90
|
+
* first, so a new provider's flattening is learned in ONE place.
|
|
91
|
+
*/
|
|
92
|
+
declare function normalizeContentAttributes(bag: Record<string, unknown> | null | undefined): Record<string, unknown>;
|
|
76
93
|
/**
|
|
77
94
|
* Extract normalized content from a flattened attribute bag (span attributes
|
|
78
95
|
* OR a log record's merged attribute bag). Pure: same input → same output, no
|
|
@@ -133,7 +150,7 @@ interface DeclaredIntentMatch {
|
|
|
133
150
|
* priority list), then the first user message of a messages body. Returns null
|
|
134
151
|
* when nothing usable is present — never a fabricated intent.
|
|
135
152
|
*/
|
|
136
|
-
declare function resolveDeclaredIntent(
|
|
153
|
+
declare function resolveDeclaredIntent(bag0: Record<string, unknown> | null | undefined): DeclaredIntentMatch | null;
|
|
137
154
|
/**
|
|
138
155
|
* Best-effort declared-task text from normalized content, in the order a
|
|
139
156
|
* declared intent is most likely to live: an explicit task, then the prompt,
|
|
@@ -299,5 +316,5 @@ declare function readTokenUsage(data: Record<string, unknown>): TokenUsageCounts
|
|
|
299
316
|
declare function readTokenCostUsd(data: Record<string, unknown>): number | undefined;
|
|
300
317
|
declare function addTokenUsage(current: TokenUsageCounts | undefined, next: TokenUsageCounts): TokenUsageCounts;
|
|
301
318
|
//#endregion
|
|
302
|
-
export {
|
|
303
|
-
//# sourceMappingURL=index-
|
|
319
|
+
export { declaredTaskText as $, GEN_AI_MODEL_KEYS as A, CONTENT_KEYS as B, TANGLE_TRIGGER_SOURCE_KEY as C, WORKFLOW_MARKER_ATTRS as D, TRIGGER_SOURCE_ATTRS as E, GEN_AI_USAGE_INPUT_TOKENS as F, CONTENT_TOOL_RESULT_KEY as G, CONTENT_PROMPT_KEY as H, GEN_AI_USAGE_OUTPUT_TOKENS as I, INDEXED_CONTENT_KEY_LIKE_PATTERNS as J, ContentField as K, GenAiUsage as L, GEN_AI_OUTPUT_TOKEN_KEYS as M, GEN_AI_REQUEST_MODEL as N, GEN_AI_CONVERSATION_ID as O, GEN_AI_RESPONSE_MODEL as P, classifyAgent as Q, genAiUsageAttributes as R, TANGLE_TRIGGER_KIND_KEY as S, TRIGGER_ID_ATTRS as T, CONTENT_TOOL_ARGS_KEY as U, CONTENT_KEY_FIELD as V, CONTENT_TOOL_NAME_KEY as W, asContentField as X, NormalizedContent as Y, asContentString as Z, TANGLE_RUN_ID_KEY as _, addTokenUsage as a, TANGLE_SUBJECT_KEY as b, readTokenCostUsd as c, tokenUsageSource as d, extractContent as et, PARENT_RUN_KEY_ATTRS as f, TANGLE_RUN_ID_CAMEL_KEY as g, SUBJECT_KEY_ATTRS as h, TokenUsageCounts as i, resolveDeclaredIntent as it, GEN_AI_OPERATION_NAME as j, GEN_AI_INPUT_TOKEN_KEYS as k, readTokenUsage as l, RUN_KIND_ATTRS as m, TOKEN_USAGE_INPUT_KEYS as n, logRecordContentBag as nt, firstTokenCount as o, RUN_KEY_ATTRS as p, DeclaredIntentMatch as q, TOKEN_USAGE_OUTPUT_KEYS as r, normalizeContentAttributes as rt, firstUsageCostUsd as s, TOKEN_USAGE_COST_KEYS as t, hasContent as tt, tokenCount as u, TANGLE_RUN_KIND_KEY as v, TANGLE_WORKFLOW_ID_KEY as w, TANGLE_TRIGGER_ID_KEY as x, TANGLE_RUN_PARENT_ID_KEY as y, CONTENT_COMPLETION_KEY as z };
|
|
320
|
+
//# sourceMappingURL=index-Cm-DkFuR.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Cm-DkFuR.d.ts","names":[],"sources":["../src/telemetry/content-attributes.ts","../src/telemetry/genai-attributes.ts","../src/telemetry/spine-attributes.ts","../src/telemetry/token-usage.ts"],"mappings":";;AAiCA;;;;;;;;;;;;;AAaA;;;;;AAYA;;;;;AAEA;;;;;AAEA;;;UA7BiB,iBAAA;EACf,IAAA;EACA,MAAA;EACA,UAAA;EACA,QAAA;EACA,QAAA;EACA,UAAA;EACA,QAAA;AAAA;;;;KAMU,YAAA,SAAqB,iBAAA;;cAYpB,kBAAA;;cAEA,sBAAA;AA4Kb;AAAA,cA1Ka,qBAAA;;cAEA,qBAAA;;cAEA,uBAAA;;;;;;;;cA+JA,YAAA;;;;cAOA,iBAAA,EAAmB,QAAA,CAAS,MAAA,SAAe,YAAA;AAgBxD;;AAAA,iBAXgB,cAAA,CAAe,KAAA;;;AAoL/B;iBAzKgB,eAAA,CAAgB,KAAA;;;;AA6XhC;;;;;cApNa,iCAAA;;;;AA+Tb;;;;iBA3GgB,0BAAA,CACd,GAAA,EAAK,MAAA,uCACJ,MAAA;;;;;AAqIH;;iBA5BgB,cAAA,CACd,UAAA,EAAY,MAAA,uCACX,iBAAA;;;AA6DH;;iBAnCgB,UAAA,CACd,UAAA,EAAY,MAAA;;;;;;;;AAgFd;;;;;AA8BA;iBA5EgB,mBAAA,CACd,UAAA,EAAY,MAAA,sCACZ,IAAA,8BACC,MAAA;;;;AAyFH;;;;;;;;;AA+BA;iBA7EgB,aAAA,CACd,GAAA,EAAK,MAAA;;;;;;UA6BU,mBAAA;EACf,IAAA;EC51B+B;ED81B/B,MAAA;AAAA;;AC51BF;;;;;AAEA;;;;iBDu2BgB,qBAAA,CACd,IAAA,EAAM,MAAA,uCACL,mBAAA;ACv2BH;;;;;AAEA;;;;AAFA,iBDo4BgB,gBAAA,CAAiB,OAAA,EAAS,iBAAA;;;;AAz3B1C;;;;;;;;;;;;;AAaA;AAAA,cC9Ba,oBAAA;;cAEA,qBAAA;;cAEA,yBAAA;;cAEA,0BAAA;;cAEA,qBAAA;ADoCb;AAAA,cClCa,sBAAA;;;;ADoCb;cC9Ba,iBAAA;;;;ADgCb;cCrBa,uBAAA;;cAOA,wBAAA;;UAOI,UAAA;EDSmB;ECPlC,KAAA;EDOkC;ECLlC,WAAA;EDoKW;EClKX,YAAA;AAAA;;;ADyKF;;;;;iBC3JgB,oBAAA,CACd,KAAA,EAAO,UAAA,GACN,MAAA;;;;AD9CH;;;;;;;;;;;;;AAaA;;;;;AAYA;;cEpCa,iBAAA;;cAEA,uBAAA;AFoCb;AAAA,cElCa,wBAAA;;cAEA,mBAAA;;cAEA,kBAAA;;cAEA,yBAAA;;cAEA,uBAAA;AF8Bb;AAAA,cE5Ba,qBAAA;;;cAGA,sBAAA;AF2Bb;AAAA,cExBa,aAAA;;;;AFuLb;;cE7Ka,oBAAA;;cASA,oBAAA;AF2Kb;AAAA,cEpKa,gBAAA;;;;;;cAUA,iBAAA;;;cAUA,cAAA;;cAKA,qBAAA;;;;AF5Db;;;;;;;;;;;;;AAaA;;;cG7Ba,sBAAA;AAAA,cAQA,uBAAA;AAAA,cAQA,qBAAA;AAAA,UAQI,gBAAA;EACf,WAAA;EACA,YAAA;AAAA;AAAA,iBAGc,UAAA,CAAW,KAAA;AAAA,iBAeX,eAAA,CACd,MAAA,EAAQ,MAAA,+BACR,IAAA;AAAA,iBAwBc,iBAAA,CACd,MAAA,EAAQ,MAAA,+BACR,IAAA;AAAA,iBAUc,gBAAA,CACd,IAAA,EAAM,MAAA,oBACL,MAAA;AAAA,iBAUa,cAAA,CACd,IAAA,EAAM,MAAA,oBACL,gBAAA;AAAA,iBAaa,gBAAA,CACd,IAAA,EAAM,MAAA;AAAA,iBAOQ,aAAA,CACd,OAAA,EAAS,gBAAA,cACT,IAAA,EAAM,gBAAA,GACL,gBAAA"}
|
|
@@ -5,8 +5,8 @@ import { FilePart, Part, Part as Part$1, PartBase, ReasoningPart, SubtaskPart, T
|
|
|
5
5
|
declare const AgentEventTypeSchema: z.ZodEnum<{
|
|
6
6
|
error: "error";
|
|
7
7
|
heartbeat: "heartbeat";
|
|
8
|
-
"message.part.updated": "message.part.updated";
|
|
9
8
|
status: "status";
|
|
9
|
+
"message.part.updated": "message.part.updated";
|
|
10
10
|
"connection.init": "connection.init";
|
|
11
11
|
"history.replay.start": "history.replay.start";
|
|
12
12
|
"history.replay.end": "history.replay.end";
|
|
@@ -130,4 +130,4 @@ interface TerminalExitEvent {
|
|
|
130
130
|
type TerminalEvent = TerminalDataEvent | TerminalResizeEvent | TerminalExitEvent;
|
|
131
131
|
//#endregion
|
|
132
132
|
export { ToolStateCompleted as A, TerminalDataEvent as C, TextPart as D, TerminalResizeEvent as E, isReasoningPart as F, isSubtaskPart as I, isTextPart as L, ToolStatePending as M, ToolStateRunning as N, ToolPart as O, isFilePart as P, isToolPart as R, SubtaskPart as S, TerminalExitEvent as T, ReplayEndEvent as _, DoneEvent as a, SessionUpdatedEvent as b, ExecutionCompletedEvent as c, FilePart as d, HeartbeatEvent as f, ReasoningPart as g, PartBase as h, ConnectionInitEvent as i, ToolStateError as j, ToolState as k, ExecutionFailedEvent as l, Part$1 as m, AgentEventType as n, ErrorEvent as o, MessagePartUpdatedEvent as p, AgentEventTypeSchema as r, EventMetadata as s, AgentEvent as t, ExecutionStartedEvent as u, ReplayStartEvent as v, TerminalEvent as w, StatusEvent as x, ResultEvent as y };
|
|
133
|
-
//# sourceMappingURL=index-
|
|
133
|
+
//# sourceMappingURL=index-Dz1ZLn3K.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-Dz1ZLn3K.d.ts","names":[],"sources":["../src/types/events.ts"],"mappings":";;;;cAsCa,oBAAA,EAAoB,CAAA,CAAA,OAAA;;;;;;;;;;;;;;;KAgBrB,cAAA,GAAiB,CAAA,CAAE,KAAA,QAAa,oBAAA;AAAA,UAG3B,aAAA;EACf,gBAAA;EACA,GAAA;EACA,OAAA;EACA,MAAA;AAAA;AAAA,UAOe,uBAAA;EACf,IAAA;EACA,IAAA,EAAM,IAAA;EACN,KAAA;EACA,KAAA,GAAQ,aAAA;AAAA;AAAA,UAIO,WAAA;EACf,IAAA;EACA,MAAA;EACA,MAAA;EACA,KAAA,GAAQ,aAAA;AAAA;AAAA,UAIO,UAAA;EACf,IAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA;EACA,KAAA,GAAQ,aAAA;AAAA;AAAA,UAIO,cAAA;EACf,IAAA;EACA,SAAA;EACA,KAAA,GAAQ,aAAA;AAAA;AAAA,UAIO,mBAAA;EACf,IAAA;EACA,gBAAA;EACA,QAAA;EACA,UAAA;EACA,SAAA;EACA,KAAA,GAAQ,aAAA;AAAA;AAAA,UAIO,gBAAA;EACf,IAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,MAAA;AAAA;AAAA,UAGe,cAAA;EACf,IAAA;EACA,WAAA;EACA,mBAAA;EACA,MAAA;EACA,UAAA;AAAA;AAAA,UAIe,qBAAA;EACf,IAAA;EACA,WAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,uBAAA;EACf,IAAA;EACA,WAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,oBAAA;EACf,IAAA;EACA,WAAA;EACA,SAAA;EACA,KAAA;EACA,SAAA;AAAA;AAAA,UAIe,mBAAA;EACf,IAAA;EACA,SAAA;EACA,KAAA;EACA,IAAA;IAAS,OAAA;IAAkB,OAAA;EAAA;AAAA;AAAA,UAIZ,WAAA;EACf,IAAA;EACA,SAAA;EACA,eAAA;EACA,UAAA,GAAa,MAAA;EACb,MAAA,GAAS,MAAA;EACT,QAAA,GAAW,MAAA;AAAA;AAAA,UAII,SAAA;EACf,IAAA;EACA,SAAA;AAAA;AAAA,KAIU,UAAA,GACR,uBAAA,GACA,WAAA,GACA,UAAA,GACA,cAAA,GACA,mBAAA,GACA,gBAAA,GACA,cAAA,GACA,qBAAA,GACA,uBAAA,GACA,oBAAA,GACA,mBAAA,GACA,WAAA,GACA,SAAA;AAAA,UAGa,iBAAA;EACf,IAAA;EACA,IAAA;EACA,UAAA;AAAA;AAAA,UAGe,mBAAA;EACf,IAAA;EACA,IAAA;EACA,IAAA;EACA,UAAA;AAAA;AAAA,UAGe,iBAAA;EACf,IAAA;EACA,QAAA;EACA,UAAA;AAAA;AAAA,KAGU,aAAA,GACR,iBAAA,GACA,mBAAA,GACA,iBAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { a as EventChannel, c as EventBufferConfig, i as ChannelHandler, n as Ev
|
|
|
5
5
|
import { S as TransportMode, _ as StreamOptions, a as RecordedRequest, b as TransportEvents, c as TransportAdapter, d as ConnectionQuality, f as ConnectionState, g as SSEEvent, h as RequestOptions, i as MockTransportConfig, l as ConnectionManager, m as ReconnectionConfig, n as MockStreamEvent, o as BaseTransportAdapter, p as DEFAULT_RECONNECTION_CONFIG, r as MockTransportAdapter, s as Subscription, t as MockResponse, u as ConnectionManagerConfig, v as SubscriptionState, x as TransportMetrics, y as TransportConfig } from "./index-VcOZI9jN.js";
|
|
6
6
|
import { a as ProtectionOptions, c as CircuitBreaker, d as CircuitOpenError, f as CircuitState, i as withTimeout, l as CircuitBreakerConfig, n as createTimeoutController, o as createProtectedFn, r as sleep, s as withProtection, t as TimeoutError, u as CircuitBreakerStats } from "./index-BOD4IhTr.js";
|
|
7
7
|
import { ParsedSSEEvent, SSEChunkParser, SSEEventData, SSEParserOptions, StreamTokenUsage, StreamUsageAccumulator, StreamUsageExtractor, consumeSSEStream, createStreamUsageExtractor, createUsageCallback, parseSSEData, parseSSEStream } from "./sse/index.js";
|
|
8
|
-
import { $ as
|
|
9
|
-
import { A as ToolStateCompleted, C as TerminalDataEvent, D as TextPart, E as TerminalResizeEvent, F as isReasoningPart, I as isSubtaskPart, L as isTextPart, M as ToolStatePending, N as ToolStateRunning, O as ToolPart, P as isFilePart, R as isToolPart, S as SubtaskPart, T as TerminalExitEvent, _ as ReplayEndEvent, a as DoneEvent, b as SessionUpdatedEvent, c as ExecutionCompletedEvent, d as FilePart, f as HeartbeatEvent, g as ReasoningPart, h as PartBase, i as ConnectionInitEvent, j as ToolStateError, k as ToolState, l as ExecutionFailedEvent, m as Part, n as AgentEventType, o as ErrorEvent, p as MessagePartUpdatedEvent, r as AgentEventTypeSchema, s as EventMetadata, t as AgentEvent, u as ExecutionStartedEvent, v as ReplayStartEvent, w as TerminalEvent, x as StatusEvent, y as ResultEvent } from "./index-
|
|
8
|
+
import { $ as declaredTaskText, A as GEN_AI_MODEL_KEYS, B as CONTENT_KEYS, C as TANGLE_TRIGGER_SOURCE_KEY, D as WORKFLOW_MARKER_ATTRS, E as TRIGGER_SOURCE_ATTRS, F as GEN_AI_USAGE_INPUT_TOKENS, G as CONTENT_TOOL_RESULT_KEY, H as CONTENT_PROMPT_KEY, I as GEN_AI_USAGE_OUTPUT_TOKENS, J as INDEXED_CONTENT_KEY_LIKE_PATTERNS, K as ContentField, L as GenAiUsage, M as GEN_AI_OUTPUT_TOKEN_KEYS, N as GEN_AI_REQUEST_MODEL, O as GEN_AI_CONVERSATION_ID, P as GEN_AI_RESPONSE_MODEL, Q as classifyAgent, R as genAiUsageAttributes, S as TANGLE_TRIGGER_KIND_KEY, T as TRIGGER_ID_ATTRS, U as CONTENT_TOOL_ARGS_KEY, V as CONTENT_KEY_FIELD, W as CONTENT_TOOL_NAME_KEY, X as asContentField, Y as NormalizedContent, Z as asContentString, _ as TANGLE_RUN_ID_KEY, a as addTokenUsage, b as TANGLE_SUBJECT_KEY, c as readTokenCostUsd, d as tokenUsageSource, et as extractContent, f as PARENT_RUN_KEY_ATTRS, g as TANGLE_RUN_ID_CAMEL_KEY, h as SUBJECT_KEY_ATTRS, i as TokenUsageCounts, it as resolveDeclaredIntent, j as GEN_AI_OPERATION_NAME, k as GEN_AI_INPUT_TOKEN_KEYS, l as readTokenUsage, m as RUN_KIND_ATTRS, n as TOKEN_USAGE_INPUT_KEYS, nt as logRecordContentBag, o as firstTokenCount, p as RUN_KEY_ATTRS, q as DeclaredIntentMatch, r as TOKEN_USAGE_OUTPUT_KEYS, rt as normalizeContentAttributes, s as firstUsageCostUsd, t as TOKEN_USAGE_COST_KEYS, tt as hasContent, u as tokenCount, v as TANGLE_RUN_KIND_KEY, w as TANGLE_WORKFLOW_ID_KEY, x as TANGLE_TRIGGER_ID_KEY, y as TANGLE_RUN_PARENT_ID_KEY, z as CONTENT_COMPLETION_KEY } from "./index-Cm-DkFuR.js";
|
|
9
|
+
import { A as ToolStateCompleted, C as TerminalDataEvent, D as TextPart, E as TerminalResizeEvent, F as isReasoningPart, I as isSubtaskPart, L as isTextPart, M as ToolStatePending, N as ToolStateRunning, O as ToolPart, P as isFilePart, R as isToolPart, S as SubtaskPart, T as TerminalExitEvent, _ as ReplayEndEvent, a as DoneEvent, b as SessionUpdatedEvent, c as ExecutionCompletedEvent, d as FilePart, f as HeartbeatEvent, g as ReasoningPart, h as PartBase, i as ConnectionInitEvent, j as ToolStateError, k as ToolState, l as ExecutionFailedEvent, m as Part, n as AgentEventType, o as ErrorEvent, p as MessagePartUpdatedEvent, r as AgentEventTypeSchema, s as EventMetadata, t as AgentEvent, u as ExecutionStartedEvent, v as ReplayStartEvent, w as TerminalEvent, x as StatusEvent, y as ResultEvent } from "./index-Dz1ZLn3K.js";
|
|
10
10
|
|
|
11
11
|
//#region src/cache/index.d.ts
|
|
12
12
|
/**
|
|
@@ -484,5 +484,5 @@ declare function createSessionTranslator(translation: SessionTranslation): <T>(e
|
|
|
484
484
|
*/
|
|
485
485
|
declare function extractSessionId(event: unknown): string | null;
|
|
486
486
|
//#endregion
|
|
487
|
-
export { type AgentEvent, type AgentEventType, AgentEventTypeSchema, type AuthProvider, BaseTransportAdapter, type BatchScopedTokenPayload, BatchScopedTokenPayloadSchema, BrowserNetworkInfo, BrowserPersistence, type BufferedEvent, CONTENT_COMPLETION_KEY, CONTENT_KEYS, CONTENT_KEY_FIELD, CONTENT_PROMPT_KEY, CONTENT_TOOL_ARGS_KEY, CONTENT_TOOL_NAME_KEY, CONTENT_TOOL_RESULT_KEY, type CacheConfig, type CacheStorage, CallbackAuth, type ChannelAccessResult, type ChannelConfig, type ChannelHandler, CircuitBreaker, type CircuitBreakerConfig, type CircuitBreakerStats, CircuitOpenError, type CircuitState, type ConnectionInitEvent, ConnectionManager, type ConnectionManagerConfig, type ConnectionQuality, type ConnectionState, type ContentField, type CreateSessionRequest, CreateSessionRequestSchema, type CreateSessionResponse, DEFAULT_RECONNECTION_CONFIG, type DeclaredIntentMatch, type DeduplicatorConfig, type DoneEvent, EnvTokenAuth, type ErrorCode, type ErrorContext, type ErrorEvent, EventBuffer, type EventBufferConfig, EventChannel, EventDeduplicator, type EventMetadata, type ExecutionCompletedEvent, type ExecutionFailedEvent, type ExecutionStartedEvent, type FilePart, GEN_AI_CONVERSATION_ID, GEN_AI_INPUT_TOKEN_KEYS, GEN_AI_MODEL_KEYS, GEN_AI_OPERATION_NAME, GEN_AI_OUTPUT_TOKEN_KEYS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, type GenAiUsage, type HeartbeatEvent, type Interceptor, InterceptorChain, LocalStorage, MemoryCache, MemorySecureStorage, MemoryStorage, type MessagePartUpdatedEvent, type MockResponse, type MockStreamEvent, MockTransportAdapter, type MockTransportConfig, type NetworkInfo, NoAuth, NodeNetworkInfo, type NormalizedContent, OfflineQueue, PARENT_RUN_KEY_ATTRS, type ParsedSSEEvent, type Part, type PartBase, type PersistenceAdapter, type PlatformAdapter, type PlatformCapabilities, type Product, ProductAuth, type ProductAuthInfo, ProductSchema, type ProductSecrets, ProductTokenIssuer, type ProjectScopedTokenPayload, ProjectScopedTokenPayloadSchema, type ProtectionOptions, type QueuedRequest, RUN_KEY_ATTRS, RUN_KIND_ATTRS, ReadTokenAuth, type ReadTokenPayload, ReadTokenPayloadSchema, type ReasoningPart, type ReconnectionConfig, type RecordedRequest, RefreshableTokenAuth, type ReplayEndEvent, type ReplayStartEvent, type RequestContext, type RequestOptions, ResponseCache, type ResponseContext, type ResultEvent, type RetryConfig, Retryable, SDKError, SSEChunkParser, type SSEEvent, type SSEEventData, type SSEParserOptions, SUBJECT_KEY_ATTRS, type SecureStorage, type SendMessageRequest, SendMessageRequestSchema, type SessionScopedTokenPayload, SessionScopedTokenPayloadSchema, type SessionTranslation, type SessionUpdatedEvent, SidecarAuth, StaticTokenAuth, type StatusEvent, type Storage, StoragePersistence, type StreamOptions, type StreamTokenUsage, type StreamUsageAccumulator, StreamUsageExtractor, type Subscription, type SubscriptionState, type SubtaskPart, TANGLE_RUN_ID_CAMEL_KEY, TANGLE_RUN_ID_KEY, TANGLE_RUN_KIND_KEY, TANGLE_RUN_PARENT_ID_KEY, TANGLE_SUBJECT_KEY, TANGLE_TRIGGER_ID_KEY, TANGLE_TRIGGER_KIND_KEY, TANGLE_TRIGGER_SOURCE_KEY, TANGLE_WORKFLOW_ID_KEY, TOKEN_USAGE_COST_KEYS, TOKEN_USAGE_INPUT_KEYS, TOKEN_USAGE_OUTPUT_KEYS, TRIGGER_ID_ATTRS, TRIGGER_SOURCE_ATTRS, type TerminalDataEvent, type TerminalEvent, type TerminalExitEvent, type TerminalResizeEvent, type TextPart, TimeoutError, type TokenScope, type TokenUsageCounts, type TokenValidationResult, type ToolPart, type ToolState, type ToolStateCompleted, type ToolStateError, type ToolStatePending, type ToolStateRunning, type TransportAdapter, type TransportConfig, type TransportEvents, type TransportMetrics, type TransportMode, WORKFLOW_MARKER_ATTRS, addTokenUsage, asContentField, asContentString, calculateDelay, canBatchTokenAccessChannel, canProjectTokenAccessChannel, canSessionTokenAccessChannel, canTokenAccessChannel, classifyAgent, consumeSSEStream, createDefaultPlatformAdapter, createHeaderInterceptor, createLoggingInterceptor, createMetricsInterceptor, createPlatformAdapter, createProtectedFn, createSessionTranslator, createStreamUsageExtractor, createTimeoutController, createUsageCallback, declaredTaskText, decodeToken, detectPlatform, detectStorage, extractContent, extractProjectFromChannel, extractSessionFromChannel, extractSessionId, firstTokenCount, firstUsageCostUsd, genAiUsageAttributes, generateApiKey, generateIdempotencyKey, generateRequestId, generateSecureToken, generateSigningSecret, getAllowedChannelPatterns, getTokenScope, getTokenTTL, hasContent, hashApiKey, isBatchScopedToken, isFilePart, isProjectScopedToken, isReasoningPart, isRetryable, isSDKError, isSessionScopedToken, isSubtaskPart, isTextPart, isTokenExpiringSoon, isToolPart, issueBatchScopedToken, issueProjectScopedToken, issueReadToken, issueSessionScopedToken, logRecordContentBag, matchesAnyChannel, matchesChannel, parseSSEData, parseSSEStream, readTokenCostUsd, readTokenUsage, resolveDeclaredIntent, sleep, tokenCount, tokenUsageSource, translateSessionId, validateChannelSubscription, validateTokenScope, verifyApiKey, verifyReadToken, withProtection, withRetry, withTimeout };
|
|
487
|
+
export { type AgentEvent, type AgentEventType, AgentEventTypeSchema, type AuthProvider, BaseTransportAdapter, type BatchScopedTokenPayload, BatchScopedTokenPayloadSchema, BrowserNetworkInfo, BrowserPersistence, type BufferedEvent, CONTENT_COMPLETION_KEY, CONTENT_KEYS, CONTENT_KEY_FIELD, CONTENT_PROMPT_KEY, CONTENT_TOOL_ARGS_KEY, CONTENT_TOOL_NAME_KEY, CONTENT_TOOL_RESULT_KEY, type CacheConfig, type CacheStorage, CallbackAuth, type ChannelAccessResult, type ChannelConfig, type ChannelHandler, CircuitBreaker, type CircuitBreakerConfig, type CircuitBreakerStats, CircuitOpenError, type CircuitState, type ConnectionInitEvent, ConnectionManager, type ConnectionManagerConfig, type ConnectionQuality, type ConnectionState, type ContentField, type CreateSessionRequest, CreateSessionRequestSchema, type CreateSessionResponse, DEFAULT_RECONNECTION_CONFIG, type DeclaredIntentMatch, type DeduplicatorConfig, type DoneEvent, EnvTokenAuth, type ErrorCode, type ErrorContext, type ErrorEvent, EventBuffer, type EventBufferConfig, EventChannel, EventDeduplicator, type EventMetadata, type ExecutionCompletedEvent, type ExecutionFailedEvent, type ExecutionStartedEvent, type FilePart, GEN_AI_CONVERSATION_ID, GEN_AI_INPUT_TOKEN_KEYS, GEN_AI_MODEL_KEYS, GEN_AI_OPERATION_NAME, GEN_AI_OUTPUT_TOKEN_KEYS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, type GenAiUsage, type HeartbeatEvent, INDEXED_CONTENT_KEY_LIKE_PATTERNS, type Interceptor, InterceptorChain, LocalStorage, MemoryCache, MemorySecureStorage, MemoryStorage, type MessagePartUpdatedEvent, type MockResponse, type MockStreamEvent, MockTransportAdapter, type MockTransportConfig, type NetworkInfo, NoAuth, NodeNetworkInfo, type NormalizedContent, OfflineQueue, PARENT_RUN_KEY_ATTRS, type ParsedSSEEvent, type Part, type PartBase, type PersistenceAdapter, type PlatformAdapter, type PlatformCapabilities, type Product, ProductAuth, type ProductAuthInfo, ProductSchema, type ProductSecrets, ProductTokenIssuer, type ProjectScopedTokenPayload, ProjectScopedTokenPayloadSchema, type ProtectionOptions, type QueuedRequest, RUN_KEY_ATTRS, RUN_KIND_ATTRS, ReadTokenAuth, type ReadTokenPayload, ReadTokenPayloadSchema, type ReasoningPart, type ReconnectionConfig, type RecordedRequest, RefreshableTokenAuth, type ReplayEndEvent, type ReplayStartEvent, type RequestContext, type RequestOptions, ResponseCache, type ResponseContext, type ResultEvent, type RetryConfig, Retryable, SDKError, SSEChunkParser, type SSEEvent, type SSEEventData, type SSEParserOptions, SUBJECT_KEY_ATTRS, type SecureStorage, type SendMessageRequest, SendMessageRequestSchema, type SessionScopedTokenPayload, SessionScopedTokenPayloadSchema, type SessionTranslation, type SessionUpdatedEvent, SidecarAuth, StaticTokenAuth, type StatusEvent, type Storage, StoragePersistence, type StreamOptions, type StreamTokenUsage, type StreamUsageAccumulator, StreamUsageExtractor, type Subscription, type SubscriptionState, type SubtaskPart, TANGLE_RUN_ID_CAMEL_KEY, TANGLE_RUN_ID_KEY, TANGLE_RUN_KIND_KEY, TANGLE_RUN_PARENT_ID_KEY, TANGLE_SUBJECT_KEY, TANGLE_TRIGGER_ID_KEY, TANGLE_TRIGGER_KIND_KEY, TANGLE_TRIGGER_SOURCE_KEY, TANGLE_WORKFLOW_ID_KEY, TOKEN_USAGE_COST_KEYS, TOKEN_USAGE_INPUT_KEYS, TOKEN_USAGE_OUTPUT_KEYS, TRIGGER_ID_ATTRS, TRIGGER_SOURCE_ATTRS, type TerminalDataEvent, type TerminalEvent, type TerminalExitEvent, type TerminalResizeEvent, type TextPart, TimeoutError, type TokenScope, type TokenUsageCounts, type TokenValidationResult, type ToolPart, type ToolState, type ToolStateCompleted, type ToolStateError, type ToolStatePending, type ToolStateRunning, type TransportAdapter, type TransportConfig, type TransportEvents, type TransportMetrics, type TransportMode, WORKFLOW_MARKER_ATTRS, addTokenUsage, asContentField, asContentString, calculateDelay, canBatchTokenAccessChannel, canProjectTokenAccessChannel, canSessionTokenAccessChannel, canTokenAccessChannel, classifyAgent, consumeSSEStream, createDefaultPlatformAdapter, createHeaderInterceptor, createLoggingInterceptor, createMetricsInterceptor, createPlatformAdapter, createProtectedFn, createSessionTranslator, createStreamUsageExtractor, createTimeoutController, createUsageCallback, declaredTaskText, decodeToken, detectPlatform, detectStorage, extractContent, extractProjectFromChannel, extractSessionFromChannel, extractSessionId, firstTokenCount, firstUsageCostUsd, genAiUsageAttributes, generateApiKey, generateIdempotencyKey, generateRequestId, generateSecureToken, generateSigningSecret, getAllowedChannelPatterns, getTokenScope, getTokenTTL, hasContent, hashApiKey, isBatchScopedToken, isFilePart, isProjectScopedToken, isReasoningPart, isRetryable, isSDKError, isSessionScopedToken, isSubtaskPart, isTextPart, isTokenExpiringSoon, isToolPart, issueBatchScopedToken, issueProjectScopedToken, issueReadToken, issueSessionScopedToken, logRecordContentBag, matchesAnyChannel, matchesChannel, normalizeContentAttributes, parseSSEData, parseSSEStream, readTokenCostUsd, readTokenUsage, resolveDeclaredIntent, sleep, tokenCount, tokenUsageSource, translateSessionId, validateChannelSubscription, validateTokenScope, verifyApiKey, verifyReadToken, withProtection, withRetry, withTimeout };
|
|
488
488
|
//# sourceMappingURL=index.d.ts.map
|