@tangle-network/agent-app 0.43.39 → 0.43.41
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/assistant/index.d.ts +1 -1
- package/dist/assistant/index.js +2 -2
- package/dist/chat-routes/index.d.ts +2 -2
- package/dist/chat-routes/index.js +22 -14
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chunk-2EO7CPL3.js +191 -0
- package/dist/chunk-2EO7CPL3.js.map +1 -0
- package/dist/{chunk-H4WCEZE3.js → chunk-5GWXCSLQ.js} +5 -5
- package/dist/{chunk-D4HB72W2.js → chunk-KM766NN3.js} +159 -25
- package/dist/chunk-KM766NN3.js.map +1 -0
- package/dist/{chunk-3II3AWHY.js → chunk-PJC4NXPA.js} +7 -34
- package/dist/chunk-PJC4NXPA.js.map +1 -0
- package/dist/chunk-S5SRJJQG.js +36 -0
- package/dist/chunk-S5SRJJQG.js.map +1 -0
- package/dist/chunk-UMSOSUEU.js +124 -0
- package/dist/chunk-UMSOSUEU.js.map +1 -0
- package/dist/design-canvas-react/index.js +4 -4
- package/dist/file-index-Bw_IQE_G.d.ts +194 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +43 -28
- package/dist/object-store/index.d.ts +202 -0
- package/dist/object-store/index.js +18 -0
- package/dist/object-store/index.js.map +1 -0
- package/dist/sandbox/index.js +3 -2
- package/dist/teams-react/index.js +3 -3
- package/dist/tools/index.js +2 -1
- package/dist/web-react/index.d.ts +94 -2
- package/dist/web-react/index.js +19 -3
- package/package.json +6 -1
- package/dist/chunk-3II3AWHY.js.map +0 -1
- package/dist/chunk-5SV5PSU7.js +0 -80
- package/dist/chunk-5SV5PSU7.js.map +0 -1
- package/dist/chunk-D4HB72W2.js.map +0 -1
- package/dist/wire-BaUF66AS.d.ts +0 -61
- /package/dist/{chunk-H4WCEZE3.js.map → chunk-5GWXCSLQ.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/crypto/web-token.ts","../src/tools/capability.ts","../src/tools/gating.ts","../src/tools/http.ts"],"sourcesContent":["/**\n * Dependency-free WebCrypto primitives for HMAC-signed, base64url-encoded\n * tokens — base64url encode/decode, HMAC-SHA256, and a constant-time compare.\n * Runs on Cloudflare Workers, Node, and the browser with no Node `crypto`\n * dependency. Shared by the sandbox terminal-proxy token, the WS-upgrade token\n * parser, and the app-tool capability token so the logic lives in one place\n * rather than three near-identical private copies.\n *\n * Internal leaf: not exported from the `/crypto` barrel (that subpath is the\n * AES-GCM field-crypto surface); imported directly by the modules that need it.\n */\n\n/** base64url-encode raw bytes (RFC 4648 §5, no padding). */\nexport function base64UrlEncode(bytes: Uint8Array): string {\n let bin = ''\n for (const b of bytes) bin += String.fromCharCode(b)\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\n/** base64url-encode a UTF-8 string. */\nexport function base64UrlEncodeText(text: string): string {\n return base64UrlEncode(new TextEncoder().encode(text))\n}\n\n/** Decode a base64url string back to its UTF-8 text. Re-pads before `atob` so\n * unpadded input decodes correctly regardless of the runtime's leniency. */\nexport function base64UrlDecodeText(value: string): string {\n const padded = value.replace(/-/g, '+').replace(/_/g, '/').padEnd(Math.ceil(value.length / 4) * 4, '=')\n const bin = atob(padded)\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i += 1) bytes[i] = bin.charCodeAt(i)\n return new TextDecoder().decode(bytes)\n}\n\n/** HMAC-SHA256 `message` under `secret`, returned base64url-encoded. */\nexport async function hmacSha256Base64Url(message: string, secret: string): Promise<string> {\n const enc = new TextEncoder()\n const key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])\n const sig = await crypto.subtle.sign('HMAC', key, enc.encode(message))\n return base64UrlEncode(new Uint8Array(sig))\n}\n\n/** Length-independent-leak-free compare of two same-charset strings. */\nexport function constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n let diff = 0\n for (let i = 0; i < a.length; i += 1) diff |= a.charCodeAt(i) ^ b.charCodeAt(i)\n return diff === 0\n}\n","/**\n * Per-user capability token — the sandbox→app auth primitive behind the\n * `verifyToken` seam in {@link authenticateToolRequest}.\n *\n * An app-agent runs inside the sandbox and reaches the host app back over HTTP\n * (the app tools, the integration-invoke bridge). The route must act AS the\n * connecting user without trusting any model-supplied identity, so the turn\n * mints a short HMAC token bound to the user id and bakes it into the per-turn\n * MCP server header; the route verifies it to recover the user.\n *\n * `HMAC-SHA256(secret, \"user:<userId>\")`, base64url, with an app-chosen prefix.\n * The token encodes no scopes — the hub's policy engine authorizes per action.\n * Fail-closed: with no secret, no token is minted (the caller MUST omit the MCP\n * server rather than fake an authorized call). WebCrypto only — runs on\n * Workers, Node, and the browser with no Node `crypto` dependency.\n */\n\nimport {\n base64UrlDecodeText,\n base64UrlEncodeText,\n constantTimeEqual,\n hmacSha256Base64Url,\n} from '../crypto/web-token'\n\nexport interface CapabilityTokenOptions {\n /** Shared HMAC secret. When absent, mint returns undefined / verify returns false. */\n secret?: string\n /** Token prefix (namespaces the credential; lets verify reject foreign tokens\n * cheaply). Default `cap_`. */\n prefix?: string\n}\n\n/** Mint a capability token for `userId`, or `undefined` when no secret is\n * configured (fail-closed — the caller omits the MCP server rather than fake it). */\nexport async function createCapabilityToken(userId: string, opts: CapabilityTokenOptions): Promise<string | undefined> {\n const secret = opts.secret?.trim()\n if (!secret) return undefined\n const prefix = opts.prefix ?? 'cap_'\n return `${prefix}${await sign(userId, secret)}`\n}\n\n/** Verify a capability token against `userId`. Returns false (never throws) for\n * an unconfigured secret, a wrong prefix, a malformed token, or a mismatch. */\nexport async function verifyCapabilityToken(userId: string, token: string, opts: CapabilityTokenOptions): Promise<boolean> {\n const secret = opts.secret?.trim()\n const prefix = opts.prefix ?? 'cap_'\n if (!secret || !token.startsWith(prefix)) return false\n const expected = `${prefix}${await sign(userId, secret)}`\n return constantTimeEqual(token, expected)\n}\n\nexport interface ExpiringCapabilityTokenOptions extends CapabilityTokenOptions {\n /** Token lifetime. Expired tokens verify false regardless of signature. */\n expiresInMs: number\n /** Clock injection for tests; defaults to Date.now. */\n now?: () => number\n}\n\n/**\n * Mint an EXPIRING capability token: `<prefix><base64url(payload)>.<sig>` where\n * the payload carries `{ sub, exp, n }` (subject, epoch-ms expiry, random\n * nonce) and the signature is HMAC-SHA256 over the encoded payload. Use this\n * for user-initiated scoped channels (e.g. a per-sequence MCP endpoint) where\n * a captured token must not stay valid past its window; the bare\n * {@link createCapabilityToken} remains for turn-scoped tool bridges whose\n * mint+verify happen inside one request cycle. Fail-closed like the bare\n * variant: no secret → no token.\n */\nexport async function createExpiringCapabilityToken(subject: string, opts: ExpiringCapabilityTokenOptions): Promise<string | undefined> {\n const secret = opts.secret?.trim()\n if (!secret) return undefined\n if (!Number.isFinite(opts.expiresInMs) || opts.expiresInMs <= 0) throw new Error('expiresInMs must be a positive number')\n const prefix = opts.prefix ?? 'cap_'\n const now = opts.now ?? Date.now\n const payload = base64UrlEncodeText(JSON.stringify({ sub: subject, exp: now() + opts.expiresInMs, n: crypto.randomUUID() }))\n return `${prefix}${payload}.${await hmacSha256Base64Url(payload, secret)}`\n}\n\n/** Verify an expiring token against `subject`: prefix, payload integrity,\n * subject match, and expiry all checked; returns false (never throws) on any\n * failure including a malformed payload. */\nexport async function verifyExpiringCapabilityToken(subject: string, token: string, opts: CapabilityTokenOptions & { now?: () => number }): Promise<boolean> {\n const secret = opts.secret?.trim()\n const prefix = opts.prefix ?? 'cap_'\n if (!secret || !token.startsWith(prefix)) return false\n const body = token.slice(prefix.length)\n const dot = body.lastIndexOf('.')\n if (dot <= 0 || dot === body.length - 1) return false\n const payload = body.slice(0, dot)\n const sig = body.slice(dot + 1)\n if (!constantTimeEqual(sig, await hmacSha256Base64Url(payload, secret))) return false\n let parsed: { sub?: unknown; exp?: unknown }\n try {\n parsed = JSON.parse(base64UrlDecodeText(payload)) as { sub?: unknown; exp?: unknown }\n } catch {\n return false\n }\n if (parsed.sub !== subject) return false\n if (typeof parsed.exp !== 'number') return false\n const now = opts.now ?? Date.now\n return parsed.exp > now()\n}\n\nasync function sign(userId: string, secret: string): Promise<string> {\n return hmacSha256Base64Url(`user:${userId}`, secret)\n}\n","/**\n * Capability gating — compose an agent session's tool surface from a\n * product-defined capability registry.\n *\n * Products that let users pick what an agent can do (a \"Studio\" agent with the\n * full build toolset vs a clean \"Assistant\" with none) all need the same\n * mechanics: a registry of named capabilities, each unlocking proposal types\n * and/or named tool groups, resolved against the product's\n * {@link AppToolTaxonomy} into the concrete tools to expose. The mechanics are\n * generic and live here; the capability VOCABULARY (ids, labels, which\n * proposal types, which tool groups) is the product's.\n */\n\nimport type { AppToolTaxonomy } from './types'\n\n/** One toggleable tool group in a product's capability registry. */\nexport interface ToolCapability {\n id: string\n label: string\n description: string\n /** Proposal types this capability unlocks (intersected with the taxonomy,\n * so a capability can never widen the product's proposal surface). */\n proposalTypes?: readonly string[]\n /** Unlocks every taxonomy proposal type beyond the base set — the domain's\n * specialized long tail (risk_assessment, vuln_report, …). */\n domainActions?: boolean\n /** Named product tool groups (e.g. 'sandbox', 'integrations') this\n * capability unlocks. The vocabulary is the product's; the resolver only\n * unions them. */\n toolGroups?: readonly string[]\n}\n\nexport interface ResolveToolCapabilitiesOptions {\n taxonomy: AppToolTaxonomy\n /** The product's full capability registry. */\n capabilities: readonly ToolCapability[]\n /** Enabled capability ids. `undefined` means full access (legacy callers\n * that don't send a capability set); an explicit `[]` means a pure chat\n * agent with no tools. Unknown ids are ignored. */\n enabled: readonly string[] | undefined\n /** The shared base proposal types `domainActions` excludes. Defaults to\n * every type some capability names explicitly via `proposalTypes` — i.e.\n * \"domain actions\" are the taxonomy types no capability claims. */\n baseProposalTypes?: readonly string[]\n}\n\nexport interface ResolvedToolCapabilities {\n /** Proposal types to keep — feed to {@link restrictTaxonomy}. */\n proposalTypes: string[]\n /** Product tool groups to expose (deduped union across enabled caps). */\n toolGroups: string[]\n}\n\n/**\n * Resolve an enabled capability-id set against a taxonomy into the concrete\n * tool surface. Fail-closed: only types present in the taxonomy survive, and\n * an empty `enabled` set yields no tools at all.\n */\nexport function resolveToolCapabilities(\n opts: ResolveToolCapabilitiesOptions,\n): ResolvedToolCapabilities {\n const { taxonomy, capabilities, enabled } = opts\n if (enabled === undefined) {\n return {\n proposalTypes: [...taxonomy.proposalTypes],\n toolGroups: [...new Set(capabilities.flatMap((c) => c.toolGroups ?? []))],\n }\n }\n const base = new Set(\n opts.baseProposalTypes ?? capabilities.flatMap((c) => c.proposalTypes ?? []),\n )\n const domainTypes = taxonomy.proposalTypes.filter((t) => !base.has(t))\n const byId = new Map(capabilities.map((c) => [c.id, c]))\n\n const proposalTypes = new Set<string>()\n const toolGroups = new Set<string>()\n for (const id of enabled) {\n const cap = byId.get(id)\n if (!cap) continue\n for (const t of cap.proposalTypes ?? []) {\n if (taxonomy.proposalTypes.includes(t)) proposalTypes.add(t)\n }\n if (cap.domainActions) for (const t of domainTypes) proposalTypes.add(t)\n for (const g of cap.toolGroups ?? []) toolGroups.add(g)\n }\n return { proposalTypes: [...proposalTypes], toolGroups: [...toolGroups] }\n}\n\n/**\n * Restrict a taxonomy to a subset of proposal types, intersecting the\n * regulated subset too — the regulated label survives restriction, so a\n * narrowed agent can never launder a regulated type into an unregulated one.\n */\nexport function restrictTaxonomy(\n taxonomy: AppToolTaxonomy,\n allowed: readonly string[],\n): AppToolTaxonomy {\n const allow = new Set(allowed)\n return {\n proposalTypes: taxonomy.proposalTypes.filter((t) => allow.has(t)),\n regulatedTypes: taxonomy.regulatedTypes.filter((t) => allow.has(t)),\n }\n}\n","import { authenticateToolRequest, type ToolHeaderNames } from './auth'\nimport { dispatchAppTool, outcomeStatus, type DispatchOptions } from './dispatch'\nimport type { AppToolName } from './openai'\nimport type { AppToolDefinition } from './registry'\n\nexport interface HandleToolRequestOptions extends DispatchOptions {\n /** Which app tool this route serves — a built-in name or a product-registered\n * {@link AppToolDefinition} (auto-added to `customTools` for dispatch). */\n tool: AppToolName | AppToolDefinition\n /** Verify the bearer capability token belongs to the header user. */\n verifyToken: (userId: string, bearer: string) => Promise<boolean>\n headerNames?: ToolHeaderNames\n /** Optional success-message builder for a friendlier tool result. */\n message?: (result: unknown) => string\n}\n\n/**\n * Handle one app-tool HTTP request end to end — the sandbox MCP path. The\n * agent's per-turn HTTP MCP server POSTs here; this authenticates (header user\n * + capability token), reads the args (MCP-alias tolerant), dispatches to the\n * product handler, and returns a JSON Response. A product's route file becomes\n * a one-liner: `export const action = ({ request }) => handleAppToolRequest(request, cfg)`.\n */\nexport async function handleAppToolRequest(request: Request, opts: HandleToolRequestOptions): Promise<Response> {\n if (request.method !== 'POST') return Response.json({ error: 'Method not allowed' }, { status: 405 })\n\n const auth = await authenticateToolRequest(request, { verifyToken: opts.verifyToken, headerNames: opts.headerNames })\n if (!auth.ok) return auth.response\n\n let body: { args?: Record<string, unknown>; arguments?: Record<string, unknown> } & Record<string, unknown>\n try {\n body = (await request.json()) as typeof body\n } catch {\n return Response.json({ error: 'Invalid JSON' }, { status: 400 })\n }\n const args = (body.args ?? body.arguments ?? body) as Record<string, unknown>\n\n // A custom tool passed as `tool` is registered for this dispatch so the route\n // file stays a one-liner (no separate customTools wiring needed).\n const toolName = typeof opts.tool === 'string' ? opts.tool : opts.tool.name\n const customTools =\n typeof opts.tool === 'string' ? opts.customTools : [...(opts.customTools ?? []), opts.tool]\n const outcome = await dispatchAppTool(toolName, args, auth.ctx, { ...opts, customTools })\n if (!outcome.ok) {\n return Response.json({ error: outcome.code, message: outcome.message }, { status: outcomeStatus(outcome) })\n }\n const payload = outcome.result as Record<string, unknown>\n return Response.json({ ok: true, ...payload, ...(opts.message ? { message: opts.message(outcome.result) } : {}) })\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-5SV5PSU7.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
// src/chat-routes/wire.ts
|
|
2
|
-
function chatTurnRequestInit(payload) {
|
|
3
|
-
return {
|
|
4
|
-
method: "POST",
|
|
5
|
-
headers: { "Content-Type": "application/json" },
|
|
6
|
-
body: JSON.stringify(payload)
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
var INLINE_PARTS_MAX_BYTES = 95e4;
|
|
10
|
-
var ChatTurnInputError = class extends Error {
|
|
11
|
-
constructor(message, status = 400, code = "INVALID_CHAT_TURN") {
|
|
12
|
-
super(message);
|
|
13
|
-
this.status = status;
|
|
14
|
-
this.code = code;
|
|
15
|
-
this.name = "ChatTurnInputError";
|
|
16
|
-
}
|
|
17
|
-
status;
|
|
18
|
-
code;
|
|
19
|
-
};
|
|
20
|
-
function partByteSize(part) {
|
|
21
|
-
let bytes = 0;
|
|
22
|
-
if (part.type === "text") return part.text.length;
|
|
23
|
-
if (part.url) bytes += part.url.length;
|
|
24
|
-
if (part.content) bytes += part.content.length;
|
|
25
|
-
if (part.path) bytes += part.path.length;
|
|
26
|
-
return bytes;
|
|
27
|
-
}
|
|
28
|
-
function promptPartsByteSize(parts) {
|
|
29
|
-
return parts.reduce((total, part) => total + partByteSize(part), 0);
|
|
30
|
-
}
|
|
31
|
-
function assertPromptPartsWithinCap(parts, maxBytes = INLINE_PARTS_MAX_BYTES) {
|
|
32
|
-
const total = promptPartsByteSize(parts);
|
|
33
|
-
if (total <= maxBytes) return;
|
|
34
|
-
const largest = [...parts].sort((a, b) => partByteSize(b) - partByteSize(a))[0];
|
|
35
|
-
const largestName = largest && largest.type !== "text" ? largest.filename ?? largest.path ?? largest.type : "text";
|
|
36
|
-
throw new ChatTurnInputError(
|
|
37
|
-
`Inline prompt parts total ${total}B, over the ${maxBytes}B budget (largest: ${largestName}, ${largest ? partByteSize(largest) : 0}B). Upload large files through the upload route so they travel as sandbox path references.`,
|
|
38
|
-
413,
|
|
39
|
-
"PROMPT_PARTS_TOO_LARGE"
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
function parseChatTurnParts(raw) {
|
|
43
|
-
if (raw === void 0 || raw === null) return [];
|
|
44
|
-
if (!Array.isArray(raw)) throw new ChatTurnInputError("parts must be an array");
|
|
45
|
-
return raw.map((entry, index) => {
|
|
46
|
-
const part = entry;
|
|
47
|
-
if (!part || typeof part !== "object") {
|
|
48
|
-
throw new ChatTurnInputError(`parts[${index}] must be an object`);
|
|
49
|
-
}
|
|
50
|
-
if (part.type !== "image" && part.type !== "file") {
|
|
51
|
-
throw new ChatTurnInputError(`parts[${index}].type must be 'image' or 'file'`);
|
|
52
|
-
}
|
|
53
|
-
for (const key of ["filename", "mediaType", "url", "path", "content"]) {
|
|
54
|
-
if (part[key] !== void 0 && typeof part[key] !== "string") {
|
|
55
|
-
throw new ChatTurnInputError(`parts[${index}].${key} must be a string`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (!part.url && !part.path && !part.content) {
|
|
59
|
-
throw new ChatTurnInputError(`parts[${index}] needs a url, path, or content`);
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
type: part.type,
|
|
63
|
-
...part.filename !== void 0 ? { filename: part.filename } : {},
|
|
64
|
-
...part.mediaType !== void 0 ? { mediaType: part.mediaType } : {},
|
|
65
|
-
...part.url !== void 0 ? { url: part.url } : {},
|
|
66
|
-
...part.path !== void 0 ? { path: part.path } : {},
|
|
67
|
-
...part.content !== void 0 ? { content: part.content } : {}
|
|
68
|
-
};
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export {
|
|
73
|
-
chatTurnRequestInit,
|
|
74
|
-
INLINE_PARTS_MAX_BYTES,
|
|
75
|
-
ChatTurnInputError,
|
|
76
|
-
promptPartsByteSize,
|
|
77
|
-
assertPromptPartsWithinCap,
|
|
78
|
-
parseChatTurnParts
|
|
79
|
-
};
|
|
80
|
-
//# sourceMappingURL=chunk-5SV5PSU7.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/chat-routes/wire.ts"],"sourcesContent":["/**\n * Wire contract between the chat client (composer + `streamChatTurn`) and the\n * assembled server vertical (`createChatTurnRoutes`). Import-free on purpose:\n * `/web-react` re-exports these types into browser bundles, so nothing here may\n * reach a Node builtin or an engine package.\n *\n * The part shape mirrors the sandbox SDK's `PromptInputPart` structurally\n * (text | image | file with filename/mediaType/url/path/content) — derived\n * here, not imported, so the client bundle never touches the SDK.\n */\n\nexport interface ChatTurnTextPartInput {\n type: 'text'\n text: string\n}\n\n/** A non-text prompt part the upload route hands back and the client echoes\n * on send. `url` carries an inline `data:` URI for small files; `path` is a\n * sandbox workspace reference for large ones (the >1 MiB gateway body cap\n * makes the two-step upload mandatory). */\nexport interface ChatTurnFilePartInput {\n type: 'image' | 'file'\n filename?: string\n mediaType?: string\n url?: string\n path?: string\n content?: string\n}\n\nexport type ChatTurnPartInput = ChatTurnTextPartInput | ChatTurnFilePartInput\n\n/** POST body for the turn route. `content` may be empty when `parts` carry the\n * message (an image-only send). Product routing fields (workspaceId etc.) ride\n * alongside and are read by the product's `authorize` seam. */\nexport interface ChatTurnRequestPayload {\n threadId: string\n content?: string\n /** Non-text parts from the upload route, echoed back verbatim. */\n parts?: ChatTurnFilePartInput[]\n model?: string\n effort?: 'auto' | 'low' | 'medium' | 'high'\n harness?: string\n /** Client-generated idempotency key for the logical turn (retry-safe). */\n turnId?: string\n [key: string]: unknown\n}\n\n/** `fetch` init for the turn route — the one place the client wire shape is\n * serialized, so composer glue and products never drift from the server's\n * parser. */\nexport function chatTurnRequestInit(payload: ChatTurnRequestPayload): RequestInit {\n return {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n }\n}\n\n// ── inline-part byte budget ─────────────────────────────────────────────────\n//\n// The sandbox gateway caps request bodies at 1 MiB; a turn body whose inline\n// `data:` parts exceed it dies at the gateway with an opaque 413. Enforce the\n// budget at the route boundary instead, with headroom for the JSON envelope\n// (same fail-loud-at-the-choke-point style as /sandbox's provision-payload and\n// env-size gates).\n\nexport const INLINE_PARTS_MAX_BYTES = 950_000\n\nexport class ChatTurnInputError extends Error {\n constructor(message: string, readonly status = 400, readonly code = 'INVALID_CHAT_TURN') {\n super(message)\n this.name = 'ChatTurnInputError'\n }\n}\n\nfunction partByteSize(part: ChatTurnPartInput): number {\n let bytes = 0\n if (part.type === 'text') return part.text.length\n if (part.url) bytes += part.url.length\n if (part.content) bytes += part.content.length\n if (part.path) bytes += part.path.length\n return bytes\n}\n\nexport function promptPartsByteSize(parts: ChatTurnPartInput[]): number {\n return parts.reduce((total, part) => total + partByteSize(part), 0)\n}\n\n/** Throws `ChatTurnInputError` (413) when the parts' inline payload would blow\n * the gateway cap. Path-ref parts are tiny by construction and always pass. */\nexport function assertPromptPartsWithinCap(\n parts: ChatTurnPartInput[],\n maxBytes = INLINE_PARTS_MAX_BYTES,\n): void {\n const total = promptPartsByteSize(parts)\n if (total <= maxBytes) return\n const largest = [...parts].sort((a, b) => partByteSize(b) - partByteSize(a))[0]\n const largestName = largest && largest.type !== 'text' ? largest.filename ?? largest.path ?? largest.type : 'text'\n throw new ChatTurnInputError(\n `Inline prompt parts total ${total}B, over the ${maxBytes}B budget (largest: ${largestName}, ${largest ? partByteSize(largest) : 0}B). ` +\n 'Upload large files through the upload route so they travel as sandbox path references.',\n 413,\n 'PROMPT_PARTS_TOO_LARGE',\n )\n}\n\n/** Validates the untyped `parts` array off the wire. Returns the typed parts\n * or throws `ChatTurnInputError` (400) naming the offending entry. */\nexport function parseChatTurnParts(raw: unknown): ChatTurnFilePartInput[] {\n if (raw === undefined || raw === null) return []\n if (!Array.isArray(raw)) throw new ChatTurnInputError('parts must be an array')\n return raw.map((entry, index) => {\n const part = entry as Record<string, unknown> | null\n if (!part || typeof part !== 'object') {\n throw new ChatTurnInputError(`parts[${index}] must be an object`)\n }\n if (part.type !== 'image' && part.type !== 'file') {\n throw new ChatTurnInputError(`parts[${index}].type must be 'image' or 'file'`)\n }\n for (const key of ['filename', 'mediaType', 'url', 'path', 'content'] as const) {\n if (part[key] !== undefined && typeof part[key] !== 'string') {\n throw new ChatTurnInputError(`parts[${index}].${key} must be a string`)\n }\n }\n if (!part.url && !part.path && !part.content) {\n throw new ChatTurnInputError(`parts[${index}] needs a url, path, or content`)\n }\n return {\n type: part.type,\n ...(part.filename !== undefined ? { filename: part.filename as string } : {}),\n ...(part.mediaType !== undefined ? { mediaType: part.mediaType as string } : {}),\n ...(part.url !== undefined ? { url: part.url as string } : {}),\n ...(part.path !== undefined ? { path: part.path as string } : {}),\n ...(part.content !== undefined ? { content: part.content as string } : {}),\n }\n })\n}\n"],"mappings":";AAkDO,SAAS,oBAAoB,SAA8C;AAChF,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B;AACF;AAUO,IAAM,yBAAyB;AAE/B,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAY,SAA0B,SAAS,KAAc,OAAO,qBAAqB;AACvF,UAAM,OAAO;AADuB;AAAuB;AAE3D,SAAK,OAAO;AAAA,EACd;AAAA,EAHsC;AAAA,EAAuB;AAI/D;AAEA,SAAS,aAAa,MAAiC;AACrD,MAAI,QAAQ;AACZ,MAAI,KAAK,SAAS,OAAQ,QAAO,KAAK,KAAK;AAC3C,MAAI,KAAK,IAAK,UAAS,KAAK,IAAI;AAChC,MAAI,KAAK,QAAS,UAAS,KAAK,QAAQ;AACxC,MAAI,KAAK,KAAM,UAAS,KAAK,KAAK;AAClC,SAAO;AACT;AAEO,SAAS,oBAAoB,OAAoC;AACtE,SAAO,MAAM,OAAO,CAAC,OAAO,SAAS,QAAQ,aAAa,IAAI,GAAG,CAAC;AACpE;AAIO,SAAS,2BACd,OACA,WAAW,wBACL;AACN,QAAM,QAAQ,oBAAoB,KAAK;AACvC,MAAI,SAAS,SAAU;AACvB,QAAM,UAAU,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,EAAE,CAAC;AAC9E,QAAM,cAAc,WAAW,QAAQ,SAAS,SAAS,QAAQ,YAAY,QAAQ,QAAQ,QAAQ,OAAO;AAC5G,QAAM,IAAI;AAAA,IACR,6BAA6B,KAAK,eAAe,QAAQ,sBAAsB,WAAW,KAAK,UAAU,aAAa,OAAO,IAAI,CAAC;AAAA,IAElI;AAAA,IACA;AAAA,EACF;AACF;AAIO,SAAS,mBAAmB,KAAuC;AACxE,MAAI,QAAQ,UAAa,QAAQ,KAAM,QAAO,CAAC;AAC/C,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,OAAM,IAAI,mBAAmB,wBAAwB;AAC9E,SAAO,IAAI,IAAI,CAAC,OAAO,UAAU;AAC/B,UAAM,OAAO;AACb,QAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,YAAM,IAAI,mBAAmB,SAAS,KAAK,qBAAqB;AAAA,IAClE;AACA,QAAI,KAAK,SAAS,WAAW,KAAK,SAAS,QAAQ;AACjD,YAAM,IAAI,mBAAmB,SAAS,KAAK,kCAAkC;AAAA,IAC/E;AACA,eAAW,OAAO,CAAC,YAAY,aAAa,OAAO,QAAQ,SAAS,GAAY;AAC9E,UAAI,KAAK,GAAG,MAAM,UAAa,OAAO,KAAK,GAAG,MAAM,UAAU;AAC5D,cAAM,IAAI,mBAAmB,SAAS,KAAK,KAAK,GAAG,mBAAmB;AAAA,MACxE;AAAA,IACF;AACA,QAAI,CAAC,KAAK,OAAO,CAAC,KAAK,QAAQ,CAAC,KAAK,SAAS;AAC5C,YAAM,IAAI,mBAAmB,SAAS,KAAK,iCAAiC;AAAA,IAC9E;AACA,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,GAAI,KAAK,aAAa,SAAY,EAAE,UAAU,KAAK,SAAmB,IAAI,CAAC;AAAA,MAC3E,GAAI,KAAK,cAAc,SAAY,EAAE,WAAW,KAAK,UAAoB,IAAI,CAAC;AAAA,MAC9E,GAAI,KAAK,QAAQ,SAAY,EAAE,KAAK,KAAK,IAAc,IAAI,CAAC;AAAA,MAC5D,GAAI,KAAK,SAAS,SAAY,EAAE,MAAM,KAAK,KAAe,IAAI,CAAC;AAAA,MAC/D,GAAI,KAAK,YAAY,SAAY,EAAE,SAAS,KAAK,QAAkB,IAAI,CAAC;AAAA,IAC1E;AAAA,EACF,CAAC;AACH;","names":[]}
|