@tangle-network/agent-app 0.43.40 → 0.43.42

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 (53) hide show
  1. package/dist/assistant/index.d.ts +3 -2
  2. package/dist/assistant/index.js +4 -3
  3. package/dist/assistant/index.js.map +1 -1
  4. package/dist/chat-routes/index.d.ts +19 -5
  5. package/dist/chat-routes/index.js +100 -7
  6. package/dist/chat-routes/index.js.map +1 -1
  7. package/dist/chat-store/index.d.ts +4 -3
  8. package/dist/chat-store/index.js +5 -1
  9. package/dist/chat-store/index.js.map +1 -1
  10. package/dist/chunk-2EO7CPL3.js +191 -0
  11. package/dist/chunk-2EO7CPL3.js.map +1 -0
  12. package/dist/{chunk-Y4QHNQ75.js → chunk-6KVH5SP5.js} +110 -6
  13. package/dist/chunk-6KVH5SP5.js.map +1 -0
  14. package/dist/{chunk-XYWCGFII.js → chunk-B5JD3DXD.js} +48 -2
  15. package/dist/chunk-B5JD3DXD.js.map +1 -0
  16. package/dist/{chunk-I2R2XT4M.js → chunk-I2ATYB7R.js} +18 -2
  17. package/dist/chunk-I2ATYB7R.js.map +1 -0
  18. package/dist/chunk-SIXYZ2FB.js +101 -0
  19. package/dist/chunk-SIXYZ2FB.js.map +1 -0
  20. package/dist/{chunk-4TXDD6P2.js → chunk-XAWFPMAR.js} +38 -3
  21. package/dist/chunk-XAWFPMAR.js.map +1 -0
  22. package/dist/{chunk-D4HB72W2.js → chunk-ZLHK25C3.js} +1890 -1213
  23. package/dist/chunk-ZLHK25C3.js.map +1 -0
  24. package/dist/chunk-ZU5GNSOJ.js +920 -0
  25. package/dist/chunk-ZU5GNSOJ.js.map +1 -0
  26. package/dist/{contract-DYbTzEDf.d.ts → contract-KfqJh_au.d.ts} +23 -2
  27. package/dist/design-canvas-react/index.js +4 -4
  28. package/dist/durable-chat/index.d.ts +419 -0
  29. package/dist/durable-chat/index.js +59 -0
  30. package/dist/durable-chat/index.js.map +1 -0
  31. package/dist/file-index-Bw_IQE_G.d.ts +194 -0
  32. package/dist/index.d.ts +5 -3
  33. package/dist/index.js +123 -45
  34. package/dist/interactions/index.d.ts +58 -3
  35. package/dist/interactions/index.js +7 -3
  36. package/dist/{parts-BcbitSNp.d.ts → parts-DjX0RRTS.d.ts} +9 -3
  37. package/dist/plans/index.d.ts +69 -0
  38. package/dist/plans/index.js +21 -0
  39. package/dist/plans/index.js.map +1 -0
  40. package/dist/stream/index.js +3 -1
  41. package/dist/teams/index.js +9 -9
  42. package/dist/teams/invitations-api.js +3 -3
  43. package/dist/web-react/index.d.ts +345 -94
  44. package/dist/web-react/index.js +50 -5
  45. package/package.json +11 -1
  46. package/dist/chunk-4TXDD6P2.js.map +0 -1
  47. package/dist/chunk-5SV5PSU7.js +0 -80
  48. package/dist/chunk-5SV5PSU7.js.map +0 -1
  49. package/dist/chunk-D4HB72W2.js.map +0 -1
  50. package/dist/chunk-I2R2XT4M.js.map +0 -1
  51. package/dist/chunk-XYWCGFII.js.map +0 -1
  52. package/dist/chunk-Y4QHNQ75.js.map +0 -1
  53. package/dist/wire-BaUF66AS.d.ts +0 -61
@@ -5,8 +5,14 @@ import {
5
5
  ChatEmptyState,
6
6
  ChatMessages,
7
7
  DEFAULT_EFFORT_LEVELS,
8
+ DEFAULT_MENTION_EMPTY_TEXT,
9
+ DEFAULT_MENTION_LIMIT,
10
+ DurableChatCards,
11
+ DurablePlanCard,
12
+ DurablePlanClientError,
8
13
  EffortPicker,
9
14
  FlowWaterfall,
15
+ INDEX_REFRESH_AFTER_MS,
10
16
  INTERACTION_SUBMIT_TIMEOUT_MESSAGE,
11
17
  INTERACTION_SUBMIT_TIMEOUT_MS,
12
18
  InteractionActionButton,
@@ -23,21 +29,30 @@ import {
23
29
  buildAnswerData,
24
30
  cancelChatInteraction,
25
31
  consumeChatStream,
32
+ createDurableInteractionAnswerSubmitter,
33
+ createDurablePlanDecisionClient,
26
34
  createInteractionAnswerSubmitter,
35
+ createMemoryInteractionAttemptStore,
36
+ createSessionInteractionAttemptStore,
27
37
  dispatchChatStreamLine,
38
+ durableChatCardsFromParts,
28
39
  fieldAnswer,
40
+ fieldValuesFromAnswers,
29
41
  formatActivityCost,
30
42
  formatActivityDuration,
31
43
  formatModelCost,
32
44
  formatTokensPerSecond,
33
45
  hasSecretField,
46
+ hydrateChatInteractions,
34
47
  interactionStatusLabels,
48
+ interactionSubmissionSignature,
35
49
  interactionTerminalNotes,
36
50
  isLateAnswerableStatus,
37
51
  lateAnswerMessage,
38
52
  mergeActivityPages,
39
53
  nextRevealCount,
40
54
  pendingApprovalOf,
55
+ rankFileMentions,
41
56
  resolveChatInteraction,
42
57
  responseErrorMessage,
43
58
  restoreChatInteractions,
@@ -45,19 +60,24 @@ import {
45
60
  terminalizePendingChatInteractions,
46
61
  upsertChatInteraction,
47
62
  useChatInteractions,
63
+ useDurablePlanFlow,
64
+ useFileMentions,
48
65
  usePending,
49
66
  usePopover,
50
67
  useSmoothText,
51
68
  useThinkingSeconds,
52
69
  waterfallLayout
53
- } from "../chunk-D4HB72W2.js";
70
+ } from "../chunk-ZLHK25C3.js";
54
71
  import {
55
72
  tabTerminalConnectionId,
56
73
  useSandboxTerminalConnection
57
74
  } from "../chunk-65P3HJY3.js";
58
75
  import {
59
- chatTurnRequestInit
60
- } from "../chunk-5SV5PSU7.js";
76
+ buildMentionPromptBlock,
77
+ chatTurnRequestInit,
78
+ fileMentionsToParts,
79
+ mediaTypeForMentionPath
80
+ } from "../chunk-2EO7CPL3.js";
61
81
  import "../chunk-2QI7XV2T.js";
62
82
  import {
63
83
  INTERACTION_CANCEL_EVENT,
@@ -77,11 +97,14 @@ import {
77
97
  isTerminalInteractionStatus,
78
98
  noticePart,
79
99
  noticePartKey,
100
+ parseInteractionAnswers,
80
101
  parseInteractionCancel,
81
102
  parseInteractionRequest,
82
103
  persistedPartToInteraction,
83
- questionInteractionContentSignature
84
- } from "../chunk-4TXDD6P2.js";
104
+ questionInteractionContentSignature,
105
+ stampInteractionAnswers
106
+ } from "../chunk-XAWFPMAR.js";
107
+ import "../chunk-SIXYZ2FB.js";
85
108
  import "../chunk-E7QYOOON.js";
86
109
  export {
87
110
  AgentActivityPanel,
@@ -90,8 +113,14 @@ export {
90
113
  ChatEmptyState,
91
114
  ChatMessages,
92
115
  DEFAULT_EFFORT_LEVELS,
116
+ DEFAULT_MENTION_EMPTY_TEXT,
117
+ DEFAULT_MENTION_LIMIT,
118
+ DurableChatCards,
119
+ DurablePlanCard,
120
+ DurablePlanClientError,
93
121
  EffortPicker,
94
122
  FlowWaterfall,
123
+ INDEX_REFRESH_AFTER_MS,
95
124
  INTERACTION_CANCEL_EVENT,
96
125
  INTERACTION_EVENT,
97
126
  INTERACTION_RESOLVED_EVENT,
@@ -109,6 +138,7 @@ export {
109
138
  SeatPaywall,
110
139
  activityTone,
111
140
  buildAnswerData,
141
+ buildMentionPromptBlock,
112
142
  canTransitionInteractionStatus,
113
143
  cancelChatInteraction,
114
144
  cancelStatusFor,
@@ -116,19 +146,28 @@ export {
116
146
  composerAnswerData,
117
147
  composerAnswerDeliveries,
118
148
  consumeChatStream,
149
+ createDurableInteractionAnswerSubmitter,
150
+ createDurablePlanDecisionClient,
119
151
  createInteractionAnswerSubmitter,
152
+ createMemoryInteractionAttemptStore,
153
+ createSessionInteractionAttemptStore,
120
154
  dedupeQuestionInteractionsByContent,
121
155
  dispatchChatStreamLine,
156
+ durableChatCardsFromParts,
122
157
  fieldAcceptsFreeText,
123
158
  fieldAnswer,
159
+ fieldValuesFromAnswers,
160
+ fileMentionsToParts,
124
161
  formatActivityCost,
125
162
  formatActivityDuration,
126
163
  formatModelCost,
127
164
  formatTokensPerSecond,
128
165
  hasSecretField,
166
+ hydrateChatInteractions,
129
167
  interactionFromWireRequest,
130
168
  interactionPartKey,
131
169
  interactionStatusLabels,
170
+ interactionSubmissionSignature,
132
171
  interactionTerminalNotes,
133
172
  interactionToPersistedPart,
134
173
  isLateAnswerableStatus,
@@ -136,23 +175,29 @@ export {
136
175
  isSafeInteractionFieldKey,
137
176
  isTerminalInteractionStatus,
138
177
  lateAnswerMessage,
178
+ mediaTypeForMentionPath,
139
179
  mergeActivityPages,
140
180
  nextRevealCount,
141
181
  noticePart,
142
182
  noticePartKey,
183
+ parseInteractionAnswers,
143
184
  parseInteractionCancel,
144
185
  parseInteractionRequest,
145
186
  pendingApprovalOf,
146
187
  persistedPartToInteraction,
147
188
  questionInteractionContentSignature,
189
+ rankFileMentions,
148
190
  resolveChatInteraction,
149
191
  responseErrorMessage,
150
192
  restoreChatInteractions,
193
+ stampInteractionAnswers,
151
194
  streamChatTurn,
152
195
  tabTerminalConnectionId,
153
196
  terminalizePendingChatInteractions,
154
197
  upsertChatInteraction,
155
198
  useChatInteractions,
199
+ useDurablePlanFlow,
200
+ useFileMentions,
156
201
  usePending,
157
202
  usePopover,
158
203
  useSandboxTerminalConnection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.43.40",
3
+ "version": "0.43.42",
4
4
  "packageManager": "pnpm@10.33.4",
5
5
  "description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent→app tool side channel, integration-hub client, per-workspace billing, and crypto — composed over the Tangle agent substrate through typed seams.",
6
6
  "keywords": [
@@ -182,6 +182,16 @@
182
182
  "import": "./dist/interactions/index.js",
183
183
  "default": "./dist/interactions/index.js"
184
184
  },
185
+ "./plans": {
186
+ "types": "./dist/plans/index.d.ts",
187
+ "import": "./dist/plans/index.js",
188
+ "default": "./dist/plans/index.js"
189
+ },
190
+ "./durable-chat": {
191
+ "types": "./dist/durable-chat/index.d.ts",
192
+ "import": "./dist/durable-chat/index.js",
193
+ "default": "./dist/durable-chat/index.js"
194
+ },
185
195
  "./missions": {
186
196
  "types": "./dist/missions/index.d.ts",
187
197
  "import": "./dist/missions/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/interactions/contract.ts"],"sourcesContent":["// Shared contract for agent interaction events (kind: \"question\" et al) — the\n// generalized human-in-the-loop primitive on the chat stream. Framework-agnostic\n// (no server- or React-only imports) so a producer (chat route) and a consumer\n// (stream parser, transcript, question card) agree on one wire shape and one\n// persisted-part shape.\n//\n// An `interaction` event means the run is BLOCKED inside the sidecar's\n// InteractionBroker until the user answers, the agent withdraws the ask\n// (`interaction.cancel`), or the broker times out. A pending interaction is\n// \"waiting on the user\", not \"model working\".\n\nimport {\n InteractionRequestSchema,\n type InteractionData,\n type InteractionField,\n type InteractionOutcome,\n type InteractionRequest,\n} from '@tangle-network/agent-interface'\n\nexport type { InteractionData, InteractionOutcome, InteractionRequest }\n\n// ---------------------------------------------------------------------------\n// Event names\n\n/** Sidecar → client: the agent raised an ask; data = `{ request }`. */\nexport const INTERACTION_EVENT = 'interaction' as const\n/** Sidecar → client: the ask was withdrawn; data = `{ id, reason? }`. */\nexport const INTERACTION_CANCEL_EVENT = 'interaction.cancel' as const\n/** An ask was answered; data = `{ id, status }`. In the wire contract so a\n * server broadcast and a client-local mark share one event name. */\nexport const INTERACTION_RESOLVED_EVENT = 'interaction.resolved' as const\n\n/** Interaction kinds a product typically renders a card for. Anything else is\n * auto-declined by the chat producer's safety net and never reaches a client.\n * A pure default; a product may substitute its own renderable set. */\nconst RENDERABLE_INTERACTION_KINDS: ReadonlySet<string> = new Set(['question', 'plan'])\n\nexport function isRenderableInteractionKind(kind: string): boolean {\n return RENDERABLE_INTERACTION_KINDS.has(kind)\n}\n\n/** Answer/field keys the sidecar will accept: identifier-safe and never a\n * prototype-pollution vector. */\nexport function isSafeInteractionFieldKey(key: string): boolean {\n return /^[A-Za-z0-9_-]+$/.test(key) && key !== '__proto__' && key !== 'constructor' && key !== 'prototype'\n}\n\n// ---------------------------------------------------------------------------\n// Field types\n//\n// `allowCustom` (a select that also accepts a write-in value) is defined by\n// newer agent-interface schemas; older pinned schemas strip unknown keys on\n// parse. The wire/persisted field types below carry the flag so a card can gate\n// its write-in input, and `parseInteractionRequest` returns the RAW payload\n// (schema-validated, not schema-parsed) so the flag survives.\n\nexport type ChatSelectField = Extract<InteractionField, { type: 'select' }> & {\n allowCustom?: boolean\n}\nexport type ChatInteractionField = Exclude<InteractionField, { type: 'select' }> | ChatSelectField\n\n/** `InteractionRequest` whose select fields may carry `allowCustom`. */\nexport type InteractionRequestWire = Omit<InteractionRequest, 'answerSpec'> & {\n answerSpec: { fields: ChatInteractionField[] }\n}\n\n// ---------------------------------------------------------------------------\n// Interaction lifecycle\n\nexport type ChatInteractionStatus = 'pending' | 'answered' | 'declined' | 'cancelled' | 'expired'\n\n/** The client/persisted view of one ask. `fields` come verbatim off the wire. */\nexport interface ChatInteraction {\n id: string\n kind: string\n title: string\n body?: string\n fields: ChatInteractionField[]\n status: ChatInteractionStatus\n /** Set when status came from an `interaction.cancel` (e.g. \"timeout\"). */\n cancelReason?: string\n}\n\nexport function isTerminalInteractionStatus(status: ChatInteractionStatus): boolean {\n return status !== 'pending'\n}\n\n/** Statuses only move forward (pending → terminal); a replayed/stale `pending`\n * must never resurrect a resolved card. */\nexport function canTransitionInteractionStatus(\n from: ChatInteractionStatus,\n to: ChatInteractionStatus,\n): boolean {\n return from === 'pending' && to !== from\n}\n\n/** Maps an `interaction.cancel` reason to the card's terminal status. */\nexport function cancelStatusFor(reason: string | undefined): ChatInteractionStatus {\n return reason === 'timeout' ? 'expired' : 'cancelled'\n}\n\nfunction stableValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(stableValue)\n if (!value || typeof value !== 'object') return value\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, stableValue(nested)]),\n )\n}\n\nfunction normalizedInteractionText(value: string | undefined): string {\n return (value ?? '').replace(/\\s+/g, ' ').trim()\n}\n\n/** Content identity for duplicate safety nets. Excludes volatile ids/statuses. */\nexport function questionInteractionContentSignature(interaction: ChatInteraction): string | null {\n if (interaction.kind !== 'question') return null\n return JSON.stringify(stableValue({\n kind: interaction.kind,\n title: normalizedInteractionText(interaction.title),\n body: normalizedInteractionText(interaction.body),\n fields: interaction.fields,\n }))\n}\n\nexport function dedupeQuestionInteractionsByContent(interactions: ChatInteraction[]): ChatInteraction[] {\n const seen = new Set<string>()\n return interactions.filter((interaction) => {\n const signature = questionInteractionContentSignature(interaction)\n if (!signature) return true\n if (seen.has(signature)) return false\n seen.add(signature)\n return true\n })\n}\n\n// ---------------------------------------------------------------------------\n// Wire parsing — typed outcomes, fail loud at the caller (log + skip; never a\n// half-rendered card).\n\nexport type ParseInteractionResult =\n | { succeeded: true; value: InteractionRequestWire }\n | { succeeded: false; error: string }\n\n/** Parses an `interaction` event's data (`{ request }`). Validates the shape\n * with the agent-interface schema but returns the raw request so a field a\n * pinned schema predates (`allowCustom`) survives. */\nexport function parseInteractionRequest(data: Record<string, unknown> | undefined): ParseInteractionResult {\n const request = data?.request\n if (!request || typeof request !== 'object') {\n return { succeeded: false, error: 'interaction event carried no request object' }\n }\n const validation = InteractionRequestSchema.safeParse(request)\n if (!validation.success) {\n return { succeeded: false, error: `malformed interaction request: ${validation.error.message}` }\n }\n return { succeeded: true, value: request as InteractionRequestWire }\n}\n\nexport interface InteractionCancelData {\n id: string\n reason?: string\n}\n\nexport function parseInteractionCancel(\n data: Record<string, unknown> | undefined,\n): { succeeded: true; value: InteractionCancelData } | { succeeded: false; error: string } {\n const id = typeof data?.id === 'string' && data.id ? data.id : null\n if (!id) return { succeeded: false, error: 'interaction.cancel event carried no id' }\n const reason = typeof data?.reason === 'string' && data.reason ? data.reason : undefined\n return { succeeded: true, value: { id, ...(reason ? { reason } : {}) } }\n}\n\n// ---------------------------------------------------------------------------\n// Composer-as-answer delivery: while asks are pending the composer never\n// blocks — typed text is delivered verbatim to every open ask and the agent\n// decides what it means. No interpretation here; the sidecar validates answers\n// fail-closed (invalid free text on an option-only ask → 400).\n\nexport function fieldAcceptsFreeText(field: ChatInteractionField): boolean {\n if (field.type === 'text') return true\n if (field.type === 'select') return (field as ChatSelectField).allowCustom === true\n return false\n}\n\nexport interface ComposerAnswerDelivery {\n interactionId: string\n field: ChatInteractionField\n}\n\n/** One delivery per pending ask: the first free-text-capable field, else the\n * first field. Zero-field asks are skipped (nothing to carry the text). */\nexport function composerAnswerDeliveries(pending: ChatInteraction[]): ComposerAnswerDelivery[] {\n const deliveries: ComposerAnswerDelivery[] = []\n for (const interaction of pending) {\n // Only questions take a composer-routed answer. A non-question ask (a plan)\n // is POSTed as outcome:\"accepted\" when answered — routing composer text to\n // it would silently APPROVE it. Approval is an explicit card click, so the\n // composer skips it and the plan card stays the only path.\n if (interaction.kind !== 'question') continue\n const field = interaction.fields.find(fieldAcceptsFreeText) ?? interaction.fields[0]\n if (!field) continue\n deliveries.push({ interactionId: interaction.id, field })\n }\n return deliveries\n}\n\n/** Shapes composer text into the respond payload for the routed field\n * (select answers are string arrays on the wire; text answers are strings). */\nexport function composerAnswerData(field: ChatInteractionField, text: string): InteractionData {\n return { [field.name]: field.type === 'select' ? [text] : text }\n}\n\n// ---------------------------------------------------------------------------\n// Part keys + codecs (persisted `messages.parts` entries and live stream parts\n// share these shapes).\n\nexport function interactionPartKey(id: string): string {\n return `interaction:${id}`\n}\n\nexport function noticePartKey(id: string): string {\n return `notice:${id}`\n}\n\nexport type NoticeKind = 'warning' | 'auto-declined'\n\n/**\n * Persisted-part shapes the codecs below produce — the SAME rows\n * `/chat-store`'s `ChatInteractionPart`/`ChatNoticePart` store, typed at the\n * source so a product pushing them into a `ChatMessagePart[]` transcript needs\n * no cast. Type aliases (not interfaces) on purpose: the implicit index\n * signature keeps them assignable to the `Record<string, unknown>` these\n * codecs previously returned, so existing consumers stay source-compatible.\n */\nexport type InteractionPersistedPart = {\n type: 'interaction'\n id: string\n kind: string\n title: string\n body?: string\n answerSpec: { fields: ChatInteractionField[] }\n status: ChatInteractionStatus\n cancelReason?: string\n}\n\nexport type NoticePersistedPart = {\n type: 'notice'\n id: string\n noticeKind: NoticeKind\n text: string\n}\n\n/** Builds the persisted/streamed `notice` part — a one-line transcript notice\n * explaining an out-of-band event (warning, auto-declined interaction). */\nexport function noticePart(noticeKind: NoticeKind, id: string, text: string): NoticePersistedPart {\n return { type: 'notice', id, noticeKind, text }\n}\n\n/** Reads a wire request into the client's pending `ChatInteraction`. */\nexport function interactionFromWireRequest(request: InteractionRequestWire): ChatInteraction {\n return {\n id: request.id,\n kind: request.kind,\n title: request.title,\n ...(request.body ? { body: request.body } : {}),\n fields: request.answerSpec.fields,\n status: 'pending',\n }\n}\n\n/** Builds the persisted/streamed `interaction` part from a wire request. */\nexport function interactionToPersistedPart(\n request: InteractionRequestWire,\n status: ChatInteractionStatus,\n cancelReason?: string,\n): InteractionPersistedPart {\n return {\n type: 'interaction',\n id: request.id,\n kind: request.kind,\n title: request.title,\n ...(request.body ? { body: request.body } : {}),\n answerSpec: { fields: request.answerSpec.fields },\n status,\n ...(cancelReason ? { cancelReason } : {}),\n }\n}\n\n/** Reads a persisted/streamed `interaction` part back into a `ChatInteraction`.\n * Returns null (caller logs) when the part is not one of ours. */\nexport function persistedPartToInteraction(part: Record<string, unknown>): ChatInteraction | null {\n if (String(part.type ?? '') !== 'interaction') return null\n const id = typeof part.id === 'string' && part.id ? part.id : null\n const kind = typeof part.kind === 'string' && part.kind ? part.kind : null\n const title = typeof part.title === 'string' ? part.title : ''\n const answerSpec = part.answerSpec as { fields?: unknown } | undefined\n const fields = Array.isArray(answerSpec?.fields) ? (answerSpec.fields as ChatInteractionField[]) : null\n const status = part.status as ChatInteractionStatus | undefined\n const validStatus = status && ['pending', 'answered', 'declined', 'cancelled', 'expired'].includes(status)\n if (!id || !kind || !fields || !validStatus) return null\n return {\n id,\n kind,\n title,\n ...(typeof part.body === 'string' && part.body ? { body: part.body } : {}),\n fields,\n status,\n ...(typeof part.cancelReason === 'string' && part.cancelReason ? { cancelReason: part.cancelReason } : {}),\n }\n}\n"],"mappings":"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","names":[]}
@@ -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":[]}