@tangle-network/agent-app 0.43.24 → 0.43.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/app-auth/index.d.ts +163 -0
  2. package/dist/app-auth/index.js +166 -0
  3. package/dist/app-auth/index.js.map +1 -0
  4. package/dist/assets/index.d.ts +2 -2
  5. package/dist/assistant/index.d.ts +2 -0
  6. package/dist/assistant/index.js +3 -1
  7. package/dist/assistant/index.js.map +1 -1
  8. package/dist/chat-routes/index.d.ts +276 -0
  9. package/dist/chat-routes/index.js +564 -0
  10. package/dist/chat-routes/index.js.map +1 -0
  11. package/dist/chat-store/index.d.ts +194 -0
  12. package/dist/chat-store/index.js +198 -0
  13. package/dist/chat-store/index.js.map +1 -0
  14. package/dist/chunk-4TXDD6P2.js +163 -0
  15. package/dist/chunk-4TXDD6P2.js.map +1 -0
  16. package/dist/chunk-5EQCITY3.js +20 -0
  17. package/dist/chunk-5EQCITY3.js.map +1 -0
  18. package/dist/chunk-5SV5PSU7.js +80 -0
  19. package/dist/chunk-5SV5PSU7.js.map +1 -0
  20. package/dist/chunk-7LNGJDNA.js +1 -0
  21. package/dist/chunk-7LNGJDNA.js.map +1 -0
  22. package/dist/{chunk-77RLNLFP.js → chunk-ATRJULKZ.js} +41 -5
  23. package/dist/chunk-ATRJULKZ.js.map +1 -0
  24. package/dist/{chunk-VMY4TKMN.js → chunk-FCQP75JT.js} +936 -436
  25. package/dist/chunk-FCQP75JT.js.map +1 -0
  26. package/dist/chunk-I2R2XT4M.js +62 -0
  27. package/dist/chunk-I2R2XT4M.js.map +1 -0
  28. package/dist/{chunk-3PK3T4KD.js → chunk-JJGZ54EB.js} +44 -1
  29. package/dist/chunk-JJGZ54EB.js.map +1 -0
  30. package/dist/chunk-TKVJE63N.js +304 -0
  31. package/dist/chunk-TKVJE63N.js.map +1 -0
  32. package/dist/{chunk-SAOAAA3S.js → chunk-UHXQ3KNX.js} +1 -22
  33. package/dist/chunk-UHXQ3KNX.js.map +1 -0
  34. package/dist/chunk-Y4QHNQ75.js +203 -0
  35. package/dist/chunk-Y4QHNQ75.js.map +1 -0
  36. package/dist/contract-DYbTzEDf.d.ts +122 -0
  37. package/dist/core-7qIM7svy.d.ts +21 -0
  38. package/dist/index.d.ts +6 -2
  39. package/dist/index.js +179 -92
  40. package/dist/interactions/index.d.ts +141 -0
  41. package/dist/interactions/index.js +60 -0
  42. package/dist/interactions/index.js.map +1 -0
  43. package/dist/parts-BcbitSNp.d.ts +176 -0
  44. package/dist/platform/index.d.ts +2 -270
  45. package/dist/platform/index.js +14 -278
  46. package/dist/platform/index.js.map +1 -1
  47. package/dist/preset-cloudflare/index.d.ts +0 -10
  48. package/dist/preset-cloudflare/index.js +1 -1
  49. package/dist/profile/index.d.ts +33 -2
  50. package/dist/profile/index.js +37 -1
  51. package/dist/profile/index.js.map +1 -1
  52. package/dist/sandbox/index.d.ts +47 -1
  53. package/dist/sandbox/index.js +11 -1
  54. package/dist/sso-CNOsARMJ.d.ts +285 -0
  55. package/dist/stream/index.js +1 -1
  56. package/dist/teams/index.js +9 -9
  57. package/dist/teams/invitations-api.js +3 -3
  58. package/dist/web-react/index.d.ts +229 -99
  59. package/dist/web-react/index.js +72 -22
  60. package/dist/wire-BaUF66AS.d.ts +61 -0
  61. package/package.json +25 -1
  62. package/dist/chunk-3PK3T4KD.js.map +0 -1
  63. package/dist/chunk-77RLNLFP.js.map +0 -1
  64. package/dist/chunk-SAOAAA3S.js.map +0 -1
  65. package/dist/chunk-VMY4TKMN.js.map +0 -1
@@ -0,0 +1,203 @@
1
+ import {
2
+ interactionFromWireRequest,
3
+ isSafeInteractionFieldKey,
4
+ questionInteractionContentSignature
5
+ } from "./chunk-4TXDD6P2.js";
6
+
7
+ // src/interactions/sidecar.ts
8
+ var DEFAULT_TIMEOUT_MS = 5e3;
9
+ function sanitizeUpstreamMessage(input) {
10
+ const message = input instanceof Error ? input.message : String(input);
11
+ return message.replace(/Bearer\s+[^\s]+/gi, "Bearer [redacted]").replace(/\b(?:sk|pk|tc)[_-][A-Za-z0-9_-]{8,}\b/g, "[redacted-key]");
12
+ }
13
+ async function interactionsFetch(connection, init) {
14
+ const doFetch = connection.fetchImpl ?? fetch;
15
+ const url = `${connection.runtimeUrl.replace(/\/$/, "")}/agents/sessions/${encodeURIComponent(connection.sessionId)}/interactions`;
16
+ let response;
17
+ try {
18
+ response = await doFetch(url, {
19
+ method: init.method,
20
+ headers: {
21
+ ...connection.authToken ? { Authorization: `Bearer ${connection.authToken}` } : {},
22
+ ...init.method === "POST" ? { "Content-Type": "application/json" } : {}
23
+ },
24
+ ...init.method === "POST" ? { body: JSON.stringify(init.body) } : {},
25
+ signal: AbortSignal.timeout(connection.timeoutMs ?? DEFAULT_TIMEOUT_MS)
26
+ });
27
+ } catch (err) {
28
+ return {
29
+ succeeded: false,
30
+ error: { code: "UPSTREAM_UNREACHABLE", message: sanitizeUpstreamMessage(err), status: 0 }
31
+ };
32
+ }
33
+ const raw = await response.text().catch(() => "");
34
+ let parsed = {};
35
+ try {
36
+ parsed = raw ? JSON.parse(raw) : {};
37
+ } catch {
38
+ }
39
+ if (!response.ok) {
40
+ const upstreamError = parsed.error ?? {};
41
+ return {
42
+ succeeded: false,
43
+ error: {
44
+ code: typeof upstreamError.code === "string" && upstreamError.code ? upstreamError.code : "UPSTREAM_ERROR",
45
+ message: sanitizeUpstreamMessage(
46
+ typeof upstreamError.message === "string" && upstreamError.message ? upstreamError.message : `sidecar interactions ${init.method} failed (${response.status})`
47
+ ),
48
+ status: response.status
49
+ }
50
+ };
51
+ }
52
+ return { succeeded: true, value: parsed };
53
+ }
54
+ async function listSessionInteractions(connection) {
55
+ const result = await interactionsFetch(connection, { method: "GET" });
56
+ if (!result.succeeded) return result;
57
+ const data = result.value.data;
58
+ if (!Array.isArray(data?.interactions)) {
59
+ return {
60
+ succeeded: false,
61
+ error: { code: "MALFORMED_RESPONSE", message: "sidecar list returned no interactions array", status: 200 }
62
+ };
63
+ }
64
+ return { succeeded: true, value: data.interactions };
65
+ }
66
+ async function respondToSessionInteraction(connection, response) {
67
+ const result = await interactionsFetch(connection, {
68
+ method: "POST",
69
+ body: {
70
+ id: response.id,
71
+ outcome: response.outcome,
72
+ ...response.data ? { data: response.data } : {}
73
+ }
74
+ });
75
+ if (!result.succeeded) return result;
76
+ return { succeeded: true, value: void 0 };
77
+ }
78
+
79
+ // src/interactions/route.ts
80
+ function validateInteractionAnswerBody(body) {
81
+ const id = typeof body.id === "string" && body.id ? body.id : null;
82
+ if (!id) return { ok: false, error: "Missing interaction id" };
83
+ const outcome = body.outcome;
84
+ if (outcome !== "accepted" && outcome !== "declined") {
85
+ return { ok: false, error: "Invalid outcome: expected accepted or declined" };
86
+ }
87
+ if (body.data === void 0) return { ok: true, id, outcome };
88
+ if (!body.data || typeof body.data !== "object" || Array.isArray(body.data)) {
89
+ return { ok: false, error: "Invalid data: expected an object of field values" };
90
+ }
91
+ const data = {};
92
+ for (const [key, value] of Object.entries(body.data)) {
93
+ if (!isSafeInteractionFieldKey(key)) {
94
+ return { ok: false, error: "Invalid data: field names must contain only letters, numbers, underscores, or hyphens" };
95
+ }
96
+ const validValue = typeof value === "string" || typeof value === "number" || typeof value === "boolean" || Array.isArray(value) && value.every((item) => typeof item === "string");
97
+ if (!validValue) {
98
+ return { ok: false, error: "Invalid data: field values must be strings, numbers, booleans, or string arrays" };
99
+ }
100
+ data[key] = value;
101
+ }
102
+ return { ok: true, id, outcome, data };
103
+ }
104
+ function mapInteractionRespondFailure(error, logger = console) {
105
+ if (error.code === "INVALID_INTERACTION_ANSWER") {
106
+ return Response.json(
107
+ { code: "INVALID_INTERACTION_ANSWER", error: "This question needs an answer from the card above \u2014 pick one of the listed options." },
108
+ { status: 400 }
109
+ );
110
+ }
111
+ if (error.status === 404) {
112
+ return Response.json(
113
+ { code: "INTERACTION_EXPIRED", error: "This question is no longer waiting for an answer." },
114
+ { status: 410 }
115
+ );
116
+ }
117
+ if (error.status === 501 || error.code === "NOT_IMPLEMENTED") {
118
+ return Response.json(
119
+ { code: "INTERACTIONS_UNSUPPORTED", error: "This agent backend cannot accept answers this way." },
120
+ { status: 501 }
121
+ );
122
+ }
123
+ logger.error("[interactions] respond failed:", error);
124
+ return Response.json(
125
+ { code: "INTERACTION_UPSTREAM_FAILED", error: "Could not reach the agent. Try again." },
126
+ { status: 503 }
127
+ );
128
+ }
129
+ function createInteractionAnswerRoute(options) {
130
+ const logger = options.logger ?? console;
131
+ async function list(request) {
132
+ const resolution = await options.resolveConnection({ request, intent: "list" });
133
+ if (!resolution.ok) {
134
+ if ("response" in resolution) return resolution.response;
135
+ return Response.json({ interactions: [], unavailable: resolution.unavailable });
136
+ }
137
+ const result = await listSessionInteractions(resolution.connection);
138
+ if (!result.succeeded) {
139
+ logger.warn("[interactions] list failed:", result.error);
140
+ return Response.json({ interactions: [], unavailable: result.error.code });
141
+ }
142
+ return Response.json({ interactions: result.value });
143
+ }
144
+ async function answer(request) {
145
+ const body = await request.json().catch(() => null);
146
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
147
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
148
+ }
149
+ const validation = validateInteractionAnswerBody(body);
150
+ if (!validation.ok) return Response.json({ error: validation.error }, { status: 400 });
151
+ const resolution = await options.resolveConnection({ request, intent: "answer", body });
152
+ if (!resolution.ok) {
153
+ if ("response" in resolution) return resolution.response;
154
+ return mapInteractionRespondFailure(
155
+ { code: resolution.unavailable, message: "sandbox runtime unavailable", status: 0 },
156
+ logger
157
+ );
158
+ }
159
+ const connection = resolution.connection;
160
+ const answerPayload = {
161
+ outcome: validation.outcome,
162
+ ...validation.data ? { data: validation.data } : {}
163
+ };
164
+ const before = await listSessionInteractions(connection);
165
+ const answeredRequest = before.succeeded ? before.value.find((item) => item.id === validation.id) : void 0;
166
+ const answeredSignature = answeredRequest ? questionInteractionContentSignature(interactionFromWireRequest(answeredRequest)) : null;
167
+ const result = await respondToSessionInteraction(connection, { id: validation.id, ...answerPayload });
168
+ if (!result.succeeded) return mapInteractionRespondFailure(result.error, logger);
169
+ let remaining = await listSessionInteractions(connection);
170
+ if (remaining.succeeded && answeredSignature) {
171
+ const duplicateRequests = remaining.value.filter((item) => {
172
+ if (item.id === validation.id) return false;
173
+ return questionInteractionContentSignature(interactionFromWireRequest(item)) === answeredSignature;
174
+ });
175
+ for (const duplicate of duplicateRequests) {
176
+ const duplicateResult = await respondToSessionInteraction(connection, { id: duplicate.id, ...answerPayload });
177
+ if (!duplicateResult.succeeded) break;
178
+ }
179
+ if (duplicateRequests.length > 0) remaining = await listSessionInteractions(connection);
180
+ }
181
+ if (remaining.succeeded && remaining.value.some((item) => item.id === validation.id || answeredSignature && questionInteractionContentSignature(interactionFromWireRequest(item)) === answeredSignature)) {
182
+ logger.error("[interactions] respond returned ok but interaction is still pending:", {
183
+ sessionId: connection.sessionId,
184
+ interactionId: validation.id
185
+ });
186
+ return Response.json(
187
+ { code: "INTERACTION_STILL_PENDING", error: "The agent did not accept the answer. Try answering again." },
188
+ { status: 503 }
189
+ );
190
+ }
191
+ return Response.json({ ok: true });
192
+ }
193
+ return { list, answer };
194
+ }
195
+
196
+ export {
197
+ listSessionInteractions,
198
+ respondToSessionInteraction,
199
+ validateInteractionAnswerBody,
200
+ mapInteractionRespondFailure,
201
+ createInteractionAnswerRoute
202
+ };
203
+ //# sourceMappingURL=chunk-Y4QHNQ75.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/interactions/sidecar.ts","../src/interactions/route.ts"],"sourcesContent":["/**\n * Server-side client for the sandbox sidecar's generic interaction routes\n * (`GET/POST {runtimeUrl}/agents/sessions/{sessionId}/interactions`). The\n * pinned sandbox SDK exposes only the question-specific `session().answer()`\n * convenience; these raw calls are backend-agnostic (question/permission/plan,\n * any harness) and carry explicit outcomes (accepted/declined).\n *\n * Server-only: the sidecar bearer must never reach browser code. The caller\n * supplies the connection as a structural value (runtime URL + bearer +\n * session id) — no sandbox-SDK import, so any box-resolution strategy works.\n */\n\nimport type { InteractionData, InteractionOutcome, InteractionRequestWire } from './contract'\n\nexport interface SidecarInteractionsError {\n code: string\n message: string\n /** Upstream HTTP status; 0 when the sidecar was unreachable. */\n status: number\n}\n\nexport type SidecarInteractionsResult<T> =\n | { succeeded: true; value: T }\n | { succeeded: false; error: SidecarInteractionsError }\n\n/** Where and how to reach one session's interaction registry. */\nexport interface SidecarInteractionsConnection {\n runtimeUrl: string\n authToken?: string\n /** The sidecar agent-session id (the chat thread's session). */\n sessionId: string\n /** Request deadline. A pending interaction means the box is up and the\n * sidecar responsive; a short default keeps a wedged runtime from stalling\n * the answering request. */\n timeoutMs?: number\n /** Injection seam for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch\n}\n\nconst DEFAULT_TIMEOUT_MS = 5_000\n\n/** Strips bearer tokens / key material before an upstream message is logged\n * or surfaced. */\nfunction sanitizeUpstreamMessage(input: unknown): string {\n const message = input instanceof Error ? input.message : String(input)\n return message\n .replace(/Bearer\\s+[^\\s]+/gi, 'Bearer [redacted]')\n .replace(/\\b(?:sk|pk|tc)[_-][A-Za-z0-9_-]{8,}\\b/g, '[redacted-key]')\n}\n\nasync function interactionsFetch(\n connection: SidecarInteractionsConnection,\n init: { method: 'GET' } | { method: 'POST'; body: Record<string, unknown> },\n): Promise<SidecarInteractionsResult<Record<string, unknown>>> {\n const doFetch = connection.fetchImpl ?? fetch\n const url = `${connection.runtimeUrl.replace(/\\/$/, '')}/agents/sessions/${encodeURIComponent(connection.sessionId)}/interactions`\n let response: Response\n try {\n response = await doFetch(url, {\n method: init.method,\n headers: {\n ...(connection.authToken ? { Authorization: `Bearer ${connection.authToken}` } : {}),\n ...(init.method === 'POST' ? { 'Content-Type': 'application/json' } : {}),\n },\n ...(init.method === 'POST' ? { body: JSON.stringify(init.body) } : {}),\n signal: AbortSignal.timeout(connection.timeoutMs ?? DEFAULT_TIMEOUT_MS),\n })\n } catch (err) {\n return {\n succeeded: false,\n error: { code: 'UPSTREAM_UNREACHABLE', message: sanitizeUpstreamMessage(err), status: 0 },\n }\n }\n const raw = await response.text().catch(() => '')\n let parsed: Record<string, unknown> = {}\n try {\n parsed = raw ? (JSON.parse(raw) as Record<string, unknown>) : {}\n } catch {\n // Non-JSON error bodies (proxy 502 pages) fall through to the status check.\n }\n if (!response.ok) {\n const upstreamError = (parsed.error ?? {}) as { code?: unknown; message?: unknown }\n return {\n succeeded: false,\n error: {\n code: typeof upstreamError.code === 'string' && upstreamError.code ? upstreamError.code : 'UPSTREAM_ERROR',\n message: sanitizeUpstreamMessage(\n typeof upstreamError.message === 'string' && upstreamError.message\n ? upstreamError.message\n : `sidecar interactions ${init.method} failed (${response.status})`,\n ),\n status: response.status,\n },\n }\n }\n return { succeeded: true, value: parsed }\n}\n\n/** Outstanding (unanswered) interactions for the session — the sidecar's\n * registry is authoritative, so this is the reconnect/reload source of truth. */\nexport async function listSessionInteractions(\n connection: SidecarInteractionsConnection,\n): Promise<SidecarInteractionsResult<InteractionRequestWire[]>> {\n const result = await interactionsFetch(connection, { method: 'GET' })\n if (!result.succeeded) return result\n const data = result.value.data as { interactions?: unknown } | undefined\n if (!Array.isArray(data?.interactions)) {\n return {\n succeeded: false,\n error: { code: 'MALFORMED_RESPONSE', message: 'sidecar list returned no interactions array', status: 200 },\n }\n }\n return { succeeded: true, value: data.interactions as InteractionRequestWire[] }\n}\n\n/** Resolves one interaction. `data` is required by the sidecar only for\n * `accepted` outcomes and is validated fail-closed against the answerSpec\n * (400 INVALID_INTERACTION_ANSWER on mismatch). */\nexport async function respondToSessionInteraction(\n connection: SidecarInteractionsConnection,\n response: { id: string; outcome: InteractionOutcome; data?: InteractionData },\n): Promise<SidecarInteractionsResult<void>> {\n const result = await interactionsFetch(connection, {\n method: 'POST',\n body: {\n id: response.id,\n outcome: response.outcome,\n ...(response.data ? { data: response.data } : {}),\n },\n })\n if (!result.succeeded) return result\n return { succeeded: true, value: undefined }\n}\n","/**\n * Framework-neutral interaction-answer endpoints, lifted out of the per-app\n * route files (gtm `api.chat.interactions`, legal `api.chat.interactions`,\n * tax `api.sessions.$id.interactions` — three byte-similar forks):\n *\n * list(request) — GET: outstanding asks for a live turn (reload restore)\n * answer(request) — POST `{ id, outcome, data? }`: resolve one ask\n *\n * The product supplies ONE seam, `resolveConnection`: authenticate the caller,\n * authorize the thread/session, and resolve the sidecar connection. Everything\n * behind the seam is mechanism the forks kept re-fixing:\n *\n * - body validation (safe field keys, typed values),\n * - sidecar error → client contract mapping (every \"the ask is gone\" shape\n * becomes 410 so the card flips to its expired state),\n * - duplicate resolution: after answering, every other outstanding ask with\n * the same content signature (a re-emitted duplicate) gets the same answer,\n * - unblock verification: re-list and fail loud (503 INTERACTION_STILL_PENDING)\n * when the sidecar accepted the POST but the ask is still open,\n * - best-effort list: sidecar failures return `{ interactions: [],\n * unavailable }` so a reload restore never breaks the live stream.\n *\n * Handlers return web-standard `Response`s (Workers, Node 18+, Deno) — no\n * router import anywhere.\n */\n\nimport {\n interactionFromWireRequest,\n isSafeInteractionFieldKey,\n questionInteractionContentSignature,\n type InteractionData,\n} from './contract'\nimport {\n listSessionInteractions,\n respondToSessionInteraction,\n type SidecarInteractionsConnection,\n type SidecarInteractionsError,\n} from './sidecar'\n\n// A client resolves an ask by answering (`accepted`) or refusing (`declined`).\n// Withdrawal (`cancelled`) is an agent/broker outcome delivered via the\n// `interaction.cancel` event, never a client POST, so it is not accepted here.\nexport type InteractionClientOutcome = 'accepted' | 'declined'\n\nexport type InteractionAnswerBodyValidation =\n | { ok: true; id: string; outcome: InteractionClientOutcome; data?: InteractionData }\n | { ok: false; error: string }\n\n/** Validates the client POST body: `{ id, outcome, data? }` with\n * identifier-safe field keys and primitive/string-array values only. */\nexport function validateInteractionAnswerBody(body: Record<string, unknown>): InteractionAnswerBodyValidation {\n const id = typeof body.id === 'string' && body.id ? body.id : null\n if (!id) return { ok: false, error: 'Missing interaction id' }\n const outcome = body.outcome\n if (outcome !== 'accepted' && outcome !== 'declined') {\n return { ok: false, error: 'Invalid outcome: expected accepted or declined' }\n }\n if (body.data === undefined) return { ok: true, id, outcome }\n if (!body.data || typeof body.data !== 'object' || Array.isArray(body.data)) {\n return { ok: false, error: 'Invalid data: expected an object of field values' }\n }\n const data: InteractionData = {}\n for (const [key, value] of Object.entries(body.data)) {\n if (!isSafeInteractionFieldKey(key)) {\n return { ok: false, error: 'Invalid data: field names must contain only letters, numbers, underscores, or hyphens' }\n }\n const validValue =\n typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ||\n (Array.isArray(value) && value.every((item) => typeof item === 'string'))\n if (!validValue) {\n return { ok: false, error: 'Invalid data: field values must be strings, numbers, booleans, or string arrays' }\n }\n data[key] = value as InteractionData[string]\n }\n return { ok: true, id, outcome, data }\n}\n\nexport type InteractionRouteLogger = Pick<Console, 'warn' | 'error'>\n\n/** Sidecar error → the client-actionable contract. Every \"the ask is gone\"\n * shape maps to 410 so the card flips to its expired state — a raw 404/409\n * must never surface. */\nexport function mapInteractionRespondFailure(\n error: SidecarInteractionsError,\n logger: InteractionRouteLogger = console,\n): Response {\n if (error.code === 'INVALID_INTERACTION_ANSWER') {\n return Response.json(\n { code: 'INVALID_INTERACTION_ANSWER', error: 'This question needs an answer from the card above — pick one of the listed options.' },\n { status: 400 },\n )\n }\n if (error.status === 404) {\n return Response.json(\n { code: 'INTERACTION_EXPIRED', error: 'This question is no longer waiting for an answer.' },\n { status: 410 },\n )\n }\n if (error.status === 501 || error.code === 'NOT_IMPLEMENTED') {\n return Response.json(\n { code: 'INTERACTIONS_UNSUPPORTED', error: 'This agent backend cannot accept answers this way.' },\n { status: 501 },\n )\n }\n logger.error('[interactions] respond failed:', error)\n return Response.json(\n { code: 'INTERACTION_UPSTREAM_FAILED', error: 'Could not reach the agent. Try again.' },\n { status: 503 },\n )\n}\n\n/** The product seam's verdict for one request. `response` short-circuits with\n * a product-authored Response (401/404/429…); `unavailable` means the caller\n * is fine but the sandbox runtime is not reachable — the factory shapes that\n * per intent (empty list for `list`, 503 for `answer`). */\nexport type InteractionConnectionResolution =\n | { ok: true; connection: SidecarInteractionsConnection }\n | { ok: false; response: Response }\n | { ok: false; unavailable: string }\n\nexport interface ResolveInteractionConnectionArgs {\n request: Request\n intent: 'list' | 'answer'\n /** The parsed, validated POST body (answer intent only) so the resolver can\n * read product routing fields (workspaceId/threadId) without re-parsing. */\n body?: Record<string, unknown>\n}\n\nexport interface InteractionAnswerRouteOptions {\n /** Authenticate + authorize the caller and resolve the sidecar connection.\n * This is the only product-supplied step: session auth, workspace/thread\n * access, rate limiting, and box resolution all live here. */\n resolveConnection: (args: ResolveInteractionConnectionArgs) => Promise<InteractionConnectionResolution>\n logger?: InteractionRouteLogger\n}\n\nexport interface InteractionAnswerRoute {\n /** GET — outstanding interactions for a live turn. Failures return an empty\n * list with an explicit `unavailable` code: the caller is a best-effort\n * reload restore, and the live/replayed stream must stay untouched when the\n * sidecar cannot answer. */\n list: (request: Request) => Promise<Response>\n /** POST `{ id, outcome, data?, ...productFields }` — resolve one ask, answer\n * content-identical duplicates the same way, then re-list to prove the run\n * actually unblocked. */\n answer: (request: Request) => Promise<Response>\n}\n\nexport function createInteractionAnswerRoute(options: InteractionAnswerRouteOptions): InteractionAnswerRoute {\n const logger = options.logger ?? console\n\n async function list(request: Request): Promise<Response> {\n const resolution = await options.resolveConnection({ request, intent: 'list' })\n if (!resolution.ok) {\n if ('response' in resolution) return resolution.response\n return Response.json({ interactions: [], unavailable: resolution.unavailable })\n }\n const result = await listSessionInteractions(resolution.connection)\n if (!result.succeeded) {\n logger.warn('[interactions] list failed:', result.error)\n return Response.json({ interactions: [], unavailable: result.error.code })\n }\n return Response.json({ interactions: result.value })\n }\n\n async function answer(request: Request): Promise<Response> {\n const body = await request.json().catch(() => null) as Record<string, unknown> | null\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n return Response.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n const validation = validateInteractionAnswerBody(body)\n if (!validation.ok) return Response.json({ error: validation.error }, { status: 400 })\n\n const resolution = await options.resolveConnection({ request, intent: 'answer', body })\n if (!resolution.ok) {\n if ('response' in resolution) return resolution.response\n return mapInteractionRespondFailure(\n { code: resolution.unavailable, message: 'sandbox runtime unavailable', status: 0 },\n logger,\n )\n }\n const connection = resolution.connection\n const answerPayload = {\n outcome: validation.outcome,\n ...(validation.data ? { data: validation.data } : {}),\n }\n\n // Snapshot the answered ask's content signature BEFORE resolving it, so any\n // content-identical duplicates still outstanding afterwards (the agent may\n // have re-emitted the same question N times) can be answered the same way.\n const before = await listSessionInteractions(connection)\n const answeredRequest = before.succeeded ? before.value.find((item) => item.id === validation.id) : undefined\n const answeredSignature = answeredRequest\n ? questionInteractionContentSignature(interactionFromWireRequest(answeredRequest))\n : null\n\n const result = await respondToSessionInteraction(connection, { id: validation.id, ...answerPayload })\n if (!result.succeeded) return mapInteractionRespondFailure(result.error, logger)\n\n let remaining = await listSessionInteractions(connection)\n if (remaining.succeeded && answeredSignature) {\n const duplicateRequests = remaining.value.filter((item) => {\n if (item.id === validation.id) return false\n return questionInteractionContentSignature(interactionFromWireRequest(item)) === answeredSignature\n })\n for (const duplicate of duplicateRequests) {\n const duplicateResult = await respondToSessionInteraction(connection, { id: duplicate.id, ...answerPayload })\n if (!duplicateResult.succeeded) break\n }\n if (duplicateRequests.length > 0) remaining = await listSessionInteractions(connection)\n }\n\n // Prove the run actually unblocked: neither the answered id nor any\n // content-duplicate may still be outstanding. If one is, the sidecar\n // accepted the POST but did not release the run — report it rather than\n // tell the user it was answered.\n if (remaining.succeeded && remaining.value.some((item) => item.id === validation.id || (\n answeredSignature && questionInteractionContentSignature(interactionFromWireRequest(item)) === answeredSignature\n ))) {\n logger.error('[interactions] respond returned ok but interaction is still pending:', {\n sessionId: connection.sessionId,\n interactionId: validation.id,\n })\n return Response.json(\n { code: 'INTERACTION_STILL_PENDING', error: 'The agent did not accept the answer. Try answering again.' },\n { status: 503 },\n )\n }\n\n return Response.json({ ok: true })\n }\n\n return { list, answer }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,122 @@
1
+ import { InteractionField, InteractionRequest, InteractionData } from '@tangle-network/agent-interface';
2
+
3
+ /** Sidecar → client: the agent raised an ask; data = `{ request }`. */
4
+ declare const INTERACTION_EVENT: "interaction";
5
+ /** Sidecar → client: the ask was withdrawn; data = `{ id, reason? }`. */
6
+ declare const INTERACTION_CANCEL_EVENT: "interaction.cancel";
7
+ /** An ask was answered; data = `{ id, status }`. In the wire contract so a
8
+ * server broadcast and a client-local mark share one event name. */
9
+ declare const INTERACTION_RESOLVED_EVENT: "interaction.resolved";
10
+ declare function isRenderableInteractionKind(kind: string): boolean;
11
+ /** Answer/field keys the sidecar will accept: identifier-safe and never a
12
+ * prototype-pollution vector. */
13
+ declare function isSafeInteractionFieldKey(key: string): boolean;
14
+ type ChatSelectField = Extract<InteractionField, {
15
+ type: 'select';
16
+ }> & {
17
+ allowCustom?: boolean;
18
+ };
19
+ type ChatInteractionField = Exclude<InteractionField, {
20
+ type: 'select';
21
+ }> | ChatSelectField;
22
+ /** `InteractionRequest` whose select fields may carry `allowCustom`. */
23
+ type InteractionRequestWire = Omit<InteractionRequest, 'answerSpec'> & {
24
+ answerSpec: {
25
+ fields: ChatInteractionField[];
26
+ };
27
+ };
28
+ type ChatInteractionStatus = 'pending' | 'answered' | 'declined' | 'cancelled' | 'expired';
29
+ /** The client/persisted view of one ask. `fields` come verbatim off the wire. */
30
+ interface ChatInteraction {
31
+ id: string;
32
+ kind: string;
33
+ title: string;
34
+ body?: string;
35
+ fields: ChatInteractionField[];
36
+ status: ChatInteractionStatus;
37
+ /** Set when status came from an `interaction.cancel` (e.g. "timeout"). */
38
+ cancelReason?: string;
39
+ }
40
+ declare function isTerminalInteractionStatus(status: ChatInteractionStatus): boolean;
41
+ /** Statuses only move forward (pending → terminal); a replayed/stale `pending`
42
+ * must never resurrect a resolved card. */
43
+ declare function canTransitionInteractionStatus(from: ChatInteractionStatus, to: ChatInteractionStatus): boolean;
44
+ /** Maps an `interaction.cancel` reason to the card's terminal status. */
45
+ declare function cancelStatusFor(reason: string | undefined): ChatInteractionStatus;
46
+ /** Content identity for duplicate safety nets. Excludes volatile ids/statuses. */
47
+ declare function questionInteractionContentSignature(interaction: ChatInteraction): string | null;
48
+ declare function dedupeQuestionInteractionsByContent(interactions: ChatInteraction[]): ChatInteraction[];
49
+ type ParseInteractionResult = {
50
+ succeeded: true;
51
+ value: InteractionRequestWire;
52
+ } | {
53
+ succeeded: false;
54
+ error: string;
55
+ };
56
+ /** Parses an `interaction` event's data (`{ request }`). Validates the shape
57
+ * with the agent-interface schema but returns the raw request so a field a
58
+ * pinned schema predates (`allowCustom`) survives. */
59
+ declare function parseInteractionRequest(data: Record<string, unknown> | undefined): ParseInteractionResult;
60
+ interface InteractionCancelData {
61
+ id: string;
62
+ reason?: string;
63
+ }
64
+ declare function parseInteractionCancel(data: Record<string, unknown> | undefined): {
65
+ succeeded: true;
66
+ value: InteractionCancelData;
67
+ } | {
68
+ succeeded: false;
69
+ error: string;
70
+ };
71
+ declare function fieldAcceptsFreeText(field: ChatInteractionField): boolean;
72
+ interface ComposerAnswerDelivery {
73
+ interactionId: string;
74
+ field: ChatInteractionField;
75
+ }
76
+ /** One delivery per pending ask: the first free-text-capable field, else the
77
+ * first field. Zero-field asks are skipped (nothing to carry the text). */
78
+ declare function composerAnswerDeliveries(pending: ChatInteraction[]): ComposerAnswerDelivery[];
79
+ /** Shapes composer text into the respond payload for the routed field
80
+ * (select answers are string arrays on the wire; text answers are strings). */
81
+ declare function composerAnswerData(field: ChatInteractionField, text: string): InteractionData;
82
+ declare function interactionPartKey(id: string): string;
83
+ declare function noticePartKey(id: string): string;
84
+ type NoticeKind = 'warning' | 'auto-declined';
85
+ /**
86
+ * Persisted-part shapes the codecs below produce — the SAME rows
87
+ * `/chat-store`'s `ChatInteractionPart`/`ChatNoticePart` store, typed at the
88
+ * source so a product pushing them into a `ChatMessagePart[]` transcript needs
89
+ * no cast. Type aliases (not interfaces) on purpose: the implicit index
90
+ * signature keeps them assignable to the `Record<string, unknown>` these
91
+ * codecs previously returned, so existing consumers stay source-compatible.
92
+ */
93
+ type InteractionPersistedPart = {
94
+ type: 'interaction';
95
+ id: string;
96
+ kind: string;
97
+ title: string;
98
+ body?: string;
99
+ answerSpec: {
100
+ fields: ChatInteractionField[];
101
+ };
102
+ status: ChatInteractionStatus;
103
+ cancelReason?: string;
104
+ };
105
+ type NoticePersistedPart = {
106
+ type: 'notice';
107
+ id: string;
108
+ noticeKind: NoticeKind;
109
+ text: string;
110
+ };
111
+ /** Builds the persisted/streamed `notice` part — a one-line transcript notice
112
+ * explaining an out-of-band event (warning, auto-declined interaction). */
113
+ declare function noticePart(noticeKind: NoticeKind, id: string, text: string): NoticePersistedPart;
114
+ /** Reads a wire request into the client's pending `ChatInteraction`. */
115
+ declare function interactionFromWireRequest(request: InteractionRequestWire): ChatInteraction;
116
+ /** Builds the persisted/streamed `interaction` part from a wire request. */
117
+ declare function interactionToPersistedPart(request: InteractionRequestWire, status: ChatInteractionStatus, cancelReason?: string): InteractionPersistedPart;
118
+ /** Reads a persisted/streamed `interaction` part back into a `ChatInteraction`.
119
+ * Returns null (caller logs) when the part is not one of ours. */
120
+ declare function persistedPartToInteraction(part: Record<string, unknown>): ChatInteraction | null;
121
+
122
+ export { persistedPartToInteraction as A, questionInteractionContentSignature as B, type ChatInteraction as C, INTERACTION_CANCEL_EVENT as I, type NoticeKind as N, type ParseInteractionResult as P, type ChatInteractionField as a, type ChatInteractionStatus as b, type ChatSelectField as c, type ComposerAnswerDelivery as d, INTERACTION_EVENT as e, INTERACTION_RESOLVED_EVENT as f, type InteractionCancelData as g, type InteractionPersistedPart as h, type InteractionRequestWire as i, type NoticePersistedPart as j, canTransitionInteractionStatus as k, cancelStatusFor as l, composerAnswerData as m, composerAnswerDeliveries as n, dedupeQuestionInteractionsByContent as o, fieldAcceptsFreeText as p, interactionFromWireRequest as q, interactionPartKey as r, interactionToPersistedPart as s, isRenderableInteractionKind as t, isSafeInteractionFieldKey as u, isTerminalInteractionStatus as v, noticePart as w, noticePartKey as x, parseInteractionCancel as y, parseInteractionRequest as z };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Pure (drizzle-free) pieces of the chat store: thread-title derivation, the
3
+ * bulk-delete bound, and the typed input error. Split from `./schema`/`./store`
4
+ * so the root barrel can re-export them without dragging the optional
5
+ * drizzle-orm peer into every root-entry consumer.
6
+ */
7
+ /** Bounds a single bulk-delete request's write set; product surfaces cap
8
+ * thread lists at far fewer, so a larger batch is a malformed or hostile
9
+ * request. (Lifted from legal's api.threads.bulk-delete route.) */
10
+ declare const BULK_DELETE_MAX_THREADS = 200;
11
+ /** Invalid caller input (missing/oversized ids, empty title). Products map it
12
+ * to a 400; anything else out of the store is a real failure. */
13
+ declare class ChatStoreInputError extends Error {
14
+ constructor(message: string);
15
+ }
16
+ /** Thread titles come from the first message — keep the list scannable by
17
+ * storing only its first non-empty line, capped at 80 chars, never the whole
18
+ * multi-page prompt. (Lifted verbatim from legal's chat.new route.) */
19
+ declare function threadTitleFromMessage(message: string): string;
20
+
21
+ export { BULK_DELETE_MAX_THREADS as B, ChatStoreInputError as C, threadTitleFromMessage as t };
package/dist/index.d.ts CHANGED
@@ -14,12 +14,16 @@ export { DEFAULT_HARNESS, Harness, KNOWN_HARNESSES, ResolveSessionHarnessInput,
14
14
  export { AgentAppConfig, AgentIdentityConfig, AgentIntegrationsConfig, AgentKnowledgeConfig, AgentTaxonomyConfig, AgentUiConfig, KnowledgeLoopConfig, KnowledgeSourceSpec, agentAppConfigJsonSchema, defineAgentApp } from './config/index.js';
15
15
  export { D1Like, D1PreparedLike, DrizzleColumnLike, DrizzleSqliteCoreLike, PRESET_MIGRATION_SQL, PRESET_TABLES, PresetBillingOptions, PresetKnowledgeAccessorOptions, PresetToolHandlerOptions, VaultKv, createD1KnowledgeStateAccessor, createPresetDrizzleSchema, createPresetFieldCrypto, createPresetToolHandlers, createPresetWorkspaceKeyManager, createPresetWorkspaceKeyStore } from './preset-cloudflare/index.js';
16
16
  export { KeyCrypto, KeyProvisioner, PlanLimit, PlatformBalanceInfo, PlatformBalanceManager, PlatformBalanceManagerOptions, PlatformBillingClient, PlatformIdentity, PlatformProductUsage, SharedBillingState, TcloudKeyClient, WorkspaceKeyManager, WorkspaceKeyManagerOptions, WorkspaceKeyRecord, WorkspaceKeyStore, WorkspaceModelKeyUsage, createPlatformBalanceManager, createTcloudKeyProvisioner, createWorkspaceKeyManager } from './billing/index.js';
17
+ export { B as BULK_DELETE_MAX_THREADS, C as ChatStoreInputError, t as threadTitleFromMessage } from './core-7qIM7svy.js';
18
+ export { C as ChatFilePart, a as ChatImagePart, b as ChatInteractionPart, c as ChatMessagePart, d as ChatNoticePart, e as ChatPartTime, f as ChatReasoningPart, g as ChatStepFinishPart, h as ChatStepStartPart, i as ChatSubtaskPart, j as ChatTextPart, k as ChatToolPart, l as ChatToolState, m as ChatToolStatus, n as ChatUsageTokens, S as StorableHarnessPartKind, o as isChatInteractionPart, p as isChatStepFinishPart, q as isChatTextPart, r as isChatToolPart, t as toChatMessageParts } from './parts-BcbitSNp.js';
17
19
  export { DeriveKeyOptions, createFieldCrypto, decodeHexKey, decryptAesGcm, decryptBytes, decryptWithKey, deriveKey, encryptAesGcm, encryptBytes, encryptWithKey } from './crypto/index.js';
18
20
  export { BufferedTurnEvent, BufferedTurnOptions, BufferedTurnTap, D1LikeForTurns, JsonRecord, PersistedChatMessageForTurn, PumpBufferedTurnOptions, ReplayTurnEventsOptions, ResolvedChatTurn, StreamEvent, TURN_EVENTS_MIGRATION_SQL, TURN_STATUS_SCOPE_MIGRATION_SQL, TurnEventStore, TurnStatus, asRecord, asString, buildUserTextParts, coalesceChatStreamEvents, coalesceDeltas, createBufferedTurnTap, createD1TurnEventStore, createMemoryTurnEventStore, encodeEvent, finalizeAssistantParts, getPartKey, mergePersistedPart, messageHasTurnId, normalizeClientTurnId, normalizePersistedPart, normalizeTime, normalizeToolEvent, pumpBufferedTurn, replayTurnEvents, resolveChatTurn, resolveToolId, resolveToolName } from './stream/index.js';
19
21
  export { HubExecClient, HubExecClientOptions, HubExecErrorCode, HubExecResult, HubInvokeDeps, HubInvokeInput, HubInvokeOutcome, ParsedIntegrationAction, invokeIntegrationHub, resolveIntegrationAction } from './integrations/index.js';
22
+ export { C as ChatInteraction, a as ChatInteractionField, b as ChatInteractionStatus, c as ChatSelectField, d as ComposerAnswerDelivery, I as INTERACTION_CANCEL_EVENT, e as INTERACTION_EVENT, f as INTERACTION_RESOLVED_EVENT, g as InteractionCancelData, h as InteractionPersistedPart, i as InteractionRequestWire, N as NoticeKind, j as NoticePersistedPart, P as ParseInteractionResult, k as canTransitionInteractionStatus, l as cancelStatusFor, m as composerAnswerData, n as composerAnswerDeliveries, o as dedupeQuestionInteractionsByContent, p as fieldAcceptsFreeText, q as interactionFromWireRequest, r as interactionPartKey, s as interactionToPersistedPart, t as isRenderableInteractionKind, u as isSafeInteractionFieldKey, v as isTerminalInteractionStatus, w as noticePart, x as noticePartKey, y as parseInteractionCancel, z as parseInteractionRequest, A as persistedPartToInteraction, B as questionInteractionContentSignature } from './contract-DYbTzEDf.js';
23
+ export { InteractionAnswerBodyValidation, InteractionAnswerRoute, InteractionAnswerRouteOptions, InteractionClientOutcome, InteractionConnectionResolution, InteractionRouteLogger, ResolveInteractionConnectionArgs, SidecarInteractionsConnection, SidecarInteractionsError, SidecarInteractionsResult, createInteractionAnswerRoute, listSessionInteractions, mapInteractionRespondFailure, respondToSessionInteraction, validateInteractionAnswerBody } from './interactions/index.js';
20
24
  export { CompleteMissionInput, CreateMissionInput, DEFAULT_MISSION_STEP_KINDS, InMemoryMissionStore, MISSION_CONTROL_CHANNEL_ID, MissionApprovalsPort, MissionAuditEvent, MissionConcurrencyError, MissionCostLedger, MissionEngine, MissionEngineOptions, MissionEventSink, MissionGateKind, MissionGateOptions, MissionGateProposal, MissionOutcome, MissionPlanRunOptions, MissionProposalResolution, MissionRecord, MissionService, MissionServiceOptions, MissionState, MissionStatus, MissionStep, MissionStepState, MissionStepStatus, MissionStorePort, MissionStreamEvent, MissionStreamStatus, MissionStreamStep, MissionStreamStepStatus, MissionUpdateGuard, MissionUpdatePatch, ParseMissionBlocksOptions, ParsedMission, ParsedMissionStep, PlanOutcome, RetryableStepError, SandboxDispatch, SandboxDispatchDoneResult, SandboxDispatchInProgressResult, SandboxDispatchInput, SandboxDispatchResult, SetStepStatusPatch, StepGateClassification, StepOutcome, applyMissionEvent, asMissionStreamEvent, budgetGateProposalId, buildAgentMissionPlan, createInMemoryMissionStore, createMissionEngine, createMissionService, isMissionStopRequested, isMissionTerminal, mergeMissionState, noopEventSink, parseMissionBlocks, parseSessionStreamEnvelope, reduceMissionEvents, stepGateProposalId, volumeGateProposalId } from './missions/index.js';
21
25
  export { S as StepAgentActivity, W as WithAgentActivity, s as stepAgentActivity } from './agent-activity-C8ZG0F0M.js';
22
- export { AppToolDescriptor, AuthenticatedSandboxUser, BuildAppToolMcpServersOptions, BuildSandboxToolFileMountsOptions, DEFAULT_SANDBOX_RESOURCES, EnsureWorkspaceSandboxOptions, LivenessProbeConfig, MemberSyncSeam, Outcome, ProfileComposeOptions, PromptInputPart, ProviderResolutionConfig, ResolveSandboxClientCredentialsOptions, ResolvedModel, SandboxApiCredentials, SandboxBuildContext, SandboxClientCredentials, SandboxCredentialEnvironment, SandboxPermissionLevel, SandboxResourceConfig, SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, SandboxRuntimeConfig, SandboxRuntimeConnection, SandboxScope, SandboxStepTransition, SandboxTerminalTokenOptions, SandboxTerminalTokenResult, SandboxTerminalTokenSubject, SandboxTerminalWsMatch, SandboxToolPathOptions, SandboxToolSpec, ScopedTokenResult, SecretStore, StoppedSandboxResumeFailure, StoppedSandboxResumeRecovery, StreamSandboxPromptOptions, TerminalProxyIdentity, WorkspaceSandboxConnectionArgs, WorkspaceSandboxConnectionHandlerOptions, WorkspaceSandboxEnsureContext, WorkspaceSandboxInstanceLike, WorkspaceSandboxManager, WorkspaceSandboxManagerOptions, WorkspaceSandboxRuntimeProxyArgs, WorkspaceSandboxRuntimeProxyHandlerOptions, WorkspaceSandboxTerminalUpgradeHandlerOptions, WriteProfileFilesOptions, attachReasoningEffort, bearerSubprotocolToken, bearerToken, buildAppToolMcpServers, buildSandboxRuntimeProxyHeaders, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, createSandboxTerminalToken, createWorkspaceSandboxConnectionHandler, createWorkspaceSandboxManager, createWorkspaceSandboxRuntimeProxyHandler, createWorkspaceSandboxTerminalUpgradeHandler, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, encodeSandboxRuntimePath, ensureWorkspaceSandbox, flattenHistory, getClient, isSandboxTerminalWsUpgrade, isTerminalPromptEvent, matchSandboxTerminalWsPath, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, mintTerminalProxyToken, readSecret, resetClientCache, resolveModel, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, splitDeferredProfileFiles, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, terminalTokenFromRequest, verifySandboxTerminalToken, verifyTerminalProxyToken, writeProfileFilesToBox } from './sandbox/index.js';
26
+ export { AppToolDescriptor, AuthenticatedSandboxUser, BuildAppToolMcpServersOptions, BuildSandboxToolFileMountsOptions, DEFAULT_SANDBOX_RESOURCES, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, EnsureWorkspaceSandboxOptions, LivenessProbeConfig, MemberSyncSeam, Outcome, PROVISION_PAYLOAD_MAX_BYTES, ProfileComposeOptions, PromptInputPart, ProviderResolutionConfig, ProvisionPayloadSections, ProvisionProfileSection, ResolveSandboxClientCredentialsOptions, ResolvedModel, SandboxApiCredentials, SandboxBuildContext, SandboxClientCredentials, SandboxCredentialEnvironment, SandboxPermissionLevel, SandboxResourceConfig, SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, SandboxRuntimeConfig, SandboxRuntimeConnection, SandboxScope, SandboxStepTransition, SandboxTerminalTokenOptions, SandboxTerminalTokenResult, SandboxTerminalTokenSubject, SandboxTerminalWsMatch, SandboxToolPathOptions, SandboxToolSpec, ScopedTokenResult, SecretStore, StoppedSandboxResumeFailure, StoppedSandboxResumeRecovery, StreamSandboxPromptOptions, TerminalProxyIdentity, WorkspaceSandboxConnectionArgs, WorkspaceSandboxConnectionHandlerOptions, WorkspaceSandboxEnsureContext, WorkspaceSandboxInstanceLike, WorkspaceSandboxManager, WorkspaceSandboxManagerOptions, WorkspaceSandboxRuntimeProxyArgs, WorkspaceSandboxRuntimeProxyHandlerOptions, WorkspaceSandboxTerminalUpgradeHandlerOptions, WriteProfileFilesOptions, assertEnvWithinLimits, assertProvisionPayloadWithinCap, attachReasoningEffort, bearerSubprotocolToken, bearerToken, buildAppToolMcpServers, buildSandboxRuntimeProxyHeaders, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, createSandboxTerminalToken, createWorkspaceSandboxConnectionHandler, createWorkspaceSandboxManager, createWorkspaceSandboxRuntimeProxyHandler, createWorkspaceSandboxTerminalUpgradeHandler, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, encodeSandboxRuntimePath, ensureWorkspaceSandbox, flattenHistory, getClient, isSandboxTerminalWsUpgrade, isTerminalPromptEvent, matchSandboxTerminalWsPath, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, mintTerminalProxyToken, readSecret, resetClientCache, resolveModel, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, splitDeferredProfileFiles, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, terminalTokenFromRequest, verifySandboxTerminalToken, verifyTerminalProxyToken, writeProfileFilesToBox } from './sandbox/index.js';
23
27
  export { CookieOptions, JsonObject, KvLike, RateLimitResult, RequestContext, SecurityHeaderOptions, addSecurityHeaders, assertMediaUrl, checkRateLimit, clearCookieHeader, extractRequestContext, parseJsonObjectBody, readCookieValue, requireString, serializeCookie } from './web/index.js';
24
28
  export { BuildRedactedDocumentOptions, DEFAULT_REDACTION_PATTERNS, RedactForIngestionOptions, RedactedDocSegment, RedactedDocument, RedactionPattern, RedactionSpan, RevealResult, RevealSpanOptions, buildRedactedDocument, detectSpans, maskSpans, redactForIngestion, revealSpan } from './redact/index.js';
25
29
  export { ApprovalEvent, ApprovalEventSchema, AssetContentMap, AssetFormat, AssetSpec, AssetStatus, AssetVariant, BrandTokens, BrandTokensSchema, ConversionMetrics, ConversionMetricsSchema, CopyContent, CopyContentSchema, CopyPlatform, EmailBodySection, EmailContent, EmailContentSchema, EmailCtaSection, EmailDividerSection, EmailFeatureSection, EmailHeroSection, EmailSection, EmailTestimonialSection, ImageBackground, ImageContent, ImageContentSchema, ImageImageLayer, ImageLayer, ImageLayerType, ImageLogoLayer, ImageShapeLayer, ImageSlide, ImageTextLayer, VideoCaption, VideoContent, VideoContentSchema, VideoCountdownScene, VideoImageRevealScene, VideoScene, VideoSlideScene, VideoTextAnimationScene, parseAssetSpec, safeParseAssetSpec } from './assets/index.js';
@@ -36,8 +40,8 @@ export { C as CHANNEL_PRESETS, a as ChannelPreset, b as ChannelPresetId, c as Ch
36
40
  export { RunToolLoopOptions as AppToolLoopOptions, ToolLoopAssistantToolCall as LoopAssistantToolCall, ToolLoopMessage as LoopMessage, ToolLoopCall as LoopToolCall, StreamToolLoopOptions as StreamAppToolLoopOptions, StreamToolLoopYield as StreamLoopYield, ToolLoopEvent, ToolLoopResult, ToolLoopStopReason, runToolLoop as runAppToolLoop, streamToolLoop as streamAppToolLoop } from '@tangle-network/agent-runtime';
37
41
  export { CompletionRequirement, CompletionVerdict, CorrectnessChecker, ProducedState, RuntimeEventLike, SatisfiedBy, TaskGold, createLlmCorrectnessChecker, extractProducedState, verifyCompletion, weightedComposite } from '@tangle-network/agent-eval';
38
42
  export { F as FlowSpan, a as FlowTrace } from './flow-types-Cb_AblZs.js';
43
+ export { InteractionData, InteractionOutcome, InteractionRequest, modelProvider } from '@tangle-network/agent-interface';
39
44
  export { StorageConfig } from '@tangle-network/sandbox';
40
- export { modelProvider } from '@tangle-network/agent-interface';
41
45
  import '@tangle-network/agent-runtime/intelligence';
42
46
  import '@tangle-network/agent-knowledge';
43
47
  import 'zod';