@tangle-network/agent-app 0.43.40 → 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.
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Wire contract between the chat client (composer + `streamChatTurn`) and the
3
+ * assembled server vertical (`createChatTurnRoutes`). Import-free on purpose:
4
+ * `/web-react` re-exports these types into browser bundles, so nothing here may
5
+ * reach a Node builtin or an engine package.
6
+ *
7
+ * The part shape mirrors the sandbox SDK's `PromptInputPart` structurally
8
+ * (text | image | file with filename/mediaType/url/path/content) — derived
9
+ * here, not imported, so the client bundle never touches the SDK.
10
+ */
11
+ interface ChatTurnTextPartInput {
12
+ type: 'text';
13
+ text: string;
14
+ }
15
+ /** A non-text prompt part the upload route hands back and the client echoes
16
+ * on send. `url` carries an inline `data:` URI for small files; `path` is a
17
+ * sandbox workspace reference for large ones (the >1 MiB gateway body cap
18
+ * makes the two-step upload mandatory). */
19
+ interface ChatTurnFilePartInput {
20
+ type: 'image' | 'file';
21
+ filename?: string;
22
+ mediaType?: string;
23
+ url?: string;
24
+ path?: string;
25
+ content?: string;
26
+ }
27
+ type ChatTurnPartInput = ChatTurnTextPartInput | ChatTurnFilePartInput;
28
+ /** POST body for the turn route. `content` may be empty when `parts` carry the
29
+ * message (an image-only send). Product routing fields (workspaceId etc.) ride
30
+ * alongside and are read by the product's `authorize` seam. */
31
+ interface ChatTurnRequestPayload {
32
+ threadId: string;
33
+ content?: string;
34
+ /** Non-text parts from the upload route, echoed back verbatim. */
35
+ parts?: ChatTurnFilePartInput[];
36
+ model?: string;
37
+ effort?: 'auto' | 'low' | 'medium' | 'high';
38
+ harness?: string;
39
+ /** Client-generated idempotency key for the logical turn (retry-safe). */
40
+ turnId?: string;
41
+ [key: string]: unknown;
42
+ }
43
+ /** `fetch` init for the turn route — the one place the client wire shape is
44
+ * serialized, so composer glue and products never drift from the server's
45
+ * parser. */
46
+ declare function chatTurnRequestInit(payload: ChatTurnRequestPayload): RequestInit;
47
+ declare const INLINE_PARTS_MAX_BYTES = 950000;
48
+ declare class ChatTurnInputError extends Error {
49
+ readonly status: number;
50
+ readonly code: string;
51
+ constructor(message: string, status?: number, code?: string);
52
+ }
53
+ declare function promptPartsByteSize(parts: ChatTurnPartInput[]): number;
54
+ /** Throws `ChatTurnInputError` (413) when the parts' inline payload would blow
55
+ * the gateway cap. Path-ref parts are tiny by construction and always pass. */
56
+ declare function assertPromptPartsWithinCap(parts: ChatTurnPartInput[], maxBytes?: number): void;
57
+ /** A file mention resolved from the composer's `@`-picker: the
58
+ * workspace-relative path plus enough metadata to build a prompt part and
59
+ * pointer text. `path` is the canonical identity — the mention pill's
60
+ * `MentionItem.id` for the file kind (`/web-react`'s `useFileMentions`). */
61
+ interface FileMention {
62
+ path: string;
63
+ name: string;
64
+ size?: number;
65
+ }
66
+ /** The `image/*` mime for a mention path by extension, or `undefined` for
67
+ * anything not in the known image set (dispatched as `type: 'file'`). */
68
+ declare function mediaTypeForMentionPath(path: string): string | undefined;
69
+ interface FileMentionsToPartsOptions {
70
+ /** Resolve a mention's workspace-relative path to the absolute path the
71
+ * dispatched part should carry (e.g. a host prefixing the in-box vault
72
+ * root). Default: identity — the path travels unchanged. */
73
+ resolvePath?: (path: string) => string;
74
+ }
75
+ /** Maps resolved file mentions to path-only `ChatTurnFilePartInput`s —
76
+ * `image` vs `file` by extension, and always a `path`, never a `url` (the
77
+ * url/path XOR invariant: a mention is a sandbox path reference, never
78
+ * inline bytes). */
79
+ declare function fileMentionsToParts(mentions: readonly FileMention[], opts?: FileMentionsToPartsOptions): ChatTurnFilePartInput[];
80
+ /** The agent-facing pointer block appended to the dispatched prompt — never
81
+ * persisted in message `content`. Empty array → `''` so callers can append
82
+ * unconditionally. This is the sole producer of that text: the current
83
+ * turn's dispatch and any history projection built from the same mention
84
+ * list both route through here, so the two can't drift apart. */
85
+ declare function buildMentionPromptBlock(mentions: readonly Pick<FileMention, 'name' | 'path'>[]): string;
86
+ /** Validates the untyped `parts` array off the wire. Returns the typed parts
87
+ * or throws `ChatTurnInputError` (400) naming the offending entry. */
88
+ declare function parseChatTurnParts(raw: unknown): ChatTurnFilePartInput[];
89
+
90
+ /**
91
+ * `createSandboxFileIndexRoute` — server side of `@`-file-mentions
92
+ * (companion to sandbox-ui#184's composer mention primitive). Serves a flat,
93
+ * ignore-filtered listing of the workspace sandbox so `useFileMentions`
94
+ * (`/web-react`) can filter it client-side without a round trip per
95
+ * keystroke.
96
+ *
97
+ * Same seam style as `createUploadRoute`: `authorize({ request })` resolves a
98
+ * structural `{ tree(path, opts) }` handle (the shape of the sandbox SDK's
99
+ * `box.fs.tree`) — no SDK import here. `authorize` also carries the
100
+ * cold-box signal: a sandbox that isn't running yet answers `{ status:
101
+ * 'warming' }` directly, never provisions-and-waits inside this route.
102
+ */
103
+
104
+ /** One entry from a structural `tree()` scan. Mirrors the sandbox SDK's
105
+ * `FileTreeFile` (`path`, `size`, `mtime`) — `mtime` is unused here so it's
106
+ * omitted from the structural match. */
107
+ interface SandboxTreeFile {
108
+ path: string;
109
+ size: number;
110
+ }
111
+ /** Structural match of the sandbox SDK's `box.fs.tree` result shape
112
+ * (`FileTreeResult`). `stats.truncated` is the only stat this route reads;
113
+ * the rest ride through unread on the real SDK type. */
114
+ interface SandboxTreeResult {
115
+ root: string;
116
+ files: SandboxTreeFile[];
117
+ stats: {
118
+ truncated: boolean;
119
+ };
120
+ }
121
+ /** Structural match of the sandbox SDK's `box.fs` tree surface. */
122
+ interface SandboxFileTreeSource {
123
+ tree(path: string, options?: {
124
+ maxDepth?: number;
125
+ }): Promise<SandboxTreeResult>;
126
+ }
127
+ interface FileIndexReadyResponse {
128
+ status: 'ready';
129
+ /** Workspace-relative entries. Same shape as `FileMention` (`./wire`) so a
130
+ * client can hand a response entry straight to `fileMentionsToParts` /
131
+ * `buildMentionPromptBlock` without remapping. */
132
+ files: FileMention[];
133
+ /** True when either the underlying scan truncated (SDK-side cap) or this
134
+ * route's own `maxEntries` cap trimmed the filtered list. The client
135
+ * should show "showing first N files" rather than imply completeness. */
136
+ truncated: boolean;
137
+ generatedAt: string;
138
+ }
139
+ /** Cold-box answer: no provisioning happened, no files were scanned. The
140
+ * client shows a warming state and retries — this route never blocks on a
141
+ * box coming up. */
142
+ interface FileIndexWarmingResponse {
143
+ status: 'warming';
144
+ }
145
+ type FileIndexResponse = FileIndexReadyResponse | FileIndexWarmingResponse;
146
+ /** Short-TTL cache seam so repeat popover opens in the same session don't
147
+ * re-scan the workspace. Host-provided (e.g. a KV binding); `key` is
148
+ * whatever `authorize` returns as `cacheKey` — this route treats it opaquely. */
149
+ interface FileIndexCache {
150
+ get(key: string): Promise<FileIndexReadyResponse | null> | FileIndexReadyResponse | null;
151
+ put(key: string, value: FileIndexReadyResponse, options?: {
152
+ ttlSeconds?: number;
153
+ }): Promise<void> | void;
154
+ }
155
+ type FileIndexAuthorization = {
156
+ status: 'ready';
157
+ /** Structural sandbox `fs` handle, usually `ensureWorkspaceSandbox(...)` → `box.fs`. */
158
+ fs: SandboxFileTreeSource;
159
+ /** Workspace root to index (e.g. `/home/agent`). */
160
+ root: string;
161
+ /** Extra ignore segments for this request, merged with the route's
162
+ * defaults + `CreateSandboxFileIndexRouteOptions.ignore`. */
163
+ ignore?: string[];
164
+ /** Opaque cache key for the optional cache seam. Omit to skip caching
165
+ * for this request (e.g. a workspace the host chooses not to cache). */
166
+ cacheKey?: string;
167
+ } | {
168
+ status: 'warming';
169
+ } | {
170
+ status: 'denied';
171
+ response: Response;
172
+ };
173
+ interface CreateSandboxFileIndexRouteOptions {
174
+ /** Authenticate the caller, resolve the sandbox `fs` handle, and signal a
175
+ * cold box — never provisions or waits. */
176
+ authorize(args: {
177
+ request: Request;
178
+ }): Promise<FileIndexAuthorization>;
179
+ /** Extra ignore segments beyond the route's defaults (node_modules, .git,
180
+ * dotfiles/dot-dirs, common build dirs). Matched as exact path-segment
181
+ * names, same rule as the defaults. */
182
+ ignore?: string[];
183
+ /** Passed to `fs.tree` as `options.maxDepth`. Default 12. */
184
+ maxDepth?: number;
185
+ /** Hard cap on entries returned after filtering. Default 5000. */
186
+ maxEntries?: number;
187
+ /** Optional host-provided cache seam. */
188
+ cache?: FileIndexCache;
189
+ /** Cache TTL in seconds when `cache` is set. Default 20. */
190
+ cacheTtlSeconds?: number;
191
+ }
192
+ declare function createSandboxFileIndexRoute(options: CreateSandboxFileIndexRouteOptions): (request: Request) => Promise<Response>;
193
+
194
+ export { type ChatTurnRequestPayload as C, type FileIndexAuthorization as F, INLINE_PARTS_MAX_BYTES as I, type SandboxFileTreeSource as S, type ChatTurnPartInput as a, type ChatTurnFilePartInput as b, ChatTurnInputError as c, type ChatTurnTextPartInput as d, type CreateSandboxFileIndexRouteOptions as e, type FileIndexCache as f, type FileIndexReadyResponse as g, type FileIndexResponse as h, type FileIndexWarmingResponse as i, type FileMention as j, type FileMentionsToPartsOptions as k, type SandboxTreeFile as l, type SandboxTreeResult as m, assertPromptPartsWithinCap as n, buildMentionPromptBlock as o, chatTurnRequestInit as p, createSandboxFileIndexRoute as q, fileMentionsToParts as r, mediaTypeForMentionPath as s, parseChatTurnParts as t, promptPartsByteSize as u };
@@ -4,12 +4,13 @@ import { C as ChatInteraction, a as ChatInteractionField, b as ChatInteractionSt
4
4
  export { d as ComposerAnswerDelivery, I as INTERACTION_CANCEL_EVENT, e as INTERACTION_EVENT, f as INTERACTION_RESOLVED_EVENT, h as InteractionPersistedPart, 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';
5
5
  import { InteractionData } from '@tangle-network/agent-interface';
6
6
  export { InteractionData, InteractionOutcome, InteractionRequest } from '@tangle-network/agent-interface';
7
+ import { j as FileMention } from '../file-index-Bw_IQE_G.js';
8
+ export { b as ChatTurnFilePartInput, a as ChatTurnPartInput, C as ChatTurnRequestPayload, g as FileIndexReadyResponse, h as FileIndexResponse, i as FileIndexWarmingResponse, o as buildMentionPromptBlock, p as chatTurnRequestInit, r as fileMentionsToParts, s as mediaTypeForMentionPath } from '../file-index-Bw_IQE_G.js';
7
9
  import { S as StepAgentActivity } from '../agent-activity-C8ZG0F0M.js';
8
10
  import { a as FlowTrace } from '../flow-types-Cb_AblZs.js';
9
11
  export { S as SandboxTerminalConnection, a as SandboxTerminalConnectionResponse, U as UseSandboxTerminalConnectionOptions, b as UseSandboxTerminalConnectionResult, t as tabTerminalConnectionId, u as useSandboxTerminalConnection } from '../sandbox-terminal-BIIC__CP.js';
10
12
  import { CatalogModel } from '../catalog/index.js';
11
13
  import { Harness } from '../harness/index.js';
12
- export { b as ChatTurnFilePartInput, a as ChatTurnPartInput, C as ChatTurnRequestPayload, f as chatTurnRequestInit } from '../wire-BaUF66AS.js';
13
14
 
14
15
  /**
15
16
  * Client-side chat-stream consumption — the NDJSON parse loop every agent
@@ -363,6 +364,97 @@ interface UseChatInteractionsResult {
363
364
  }
364
365
  declare function useChatInteractions(): UseChatInteractionsResult;
365
366
 
367
+ /**
368
+ * `useFileMentions` — the glue a host passes straight into `AgentComposer`'s
369
+ * `mention` prop (`@tangle-network/sandbox-ui#184`) to wire up `@`-file
370
+ * mentions against `createSandboxFileIndexRoute` (`/chat-routes`).
371
+ *
372
+ * Fetches the index once per session from `indexUrl`, refreshes it in the
373
+ * background whenever the popover opens (a `fetchItems` call) if the cached
374
+ * copy has aged past `refreshAfterMs`, and answers every keystroke from an
375
+ * in-memory fuzzy filter — no per-keystroke network round trip. The returned
376
+ * `refresh()` lets a caller force a re-fetch immediately instead of waiting
377
+ * on `refreshAfterMs` — e.g. right after the agent creates a file mid-session.
378
+ *
379
+ * `MentionItem`/the `mention` prop shape mirror the FROZEN contract from
380
+ * sandbox-ui#184 structurally (no import: `/web-react` stays dependency-free
381
+ * beyond React, and `@tangle-network/sandbox-ui` is an optional peer).
382
+ */
383
+
384
+ /** Mirrors sandbox-ui#184's `MentionItem` — the atomic pill's payload. For a
385
+ * file mention, `id` is the workspace-relative path (the pill's stable
386
+ * identity and the `@<id>` serialization sandbox-ui uses to round-trip
387
+ * `value`), `label` is the display name, and `detail` carries the full path
388
+ * for the popover row's secondary line. */
389
+ interface MentionItem {
390
+ id: string;
391
+ label: string;
392
+ detail?: string;
393
+ kind?: string;
394
+ }
395
+ /** Mirrors sandbox-ui#184's `AgentComposerProps['mention']` shape — plug the
396
+ * hook's `mention` return value straight into that prop. */
397
+ interface ComposerMentionProp {
398
+ trigger?: string;
399
+ fetchItems(query: string): Promise<MentionItem[]>;
400
+ onMentionsChange?(mentions: MentionItem[]): void;
401
+ renderItem?(item: MentionItem): ReactNode;
402
+ emptyText?: string;
403
+ }
404
+ /**
405
+ * Ranks `files` against `query` (case-insensitive), capped to `limit`:
406
+ * name-prefix matches first, then name-substring, then path-substring.
407
+ * Within a tier, shorter names sort first (the more specific match), then
408
+ * alphabetically by path for a stable order. An empty query returns the
409
+ * first `limit` entries unranked — the popover's default list before typing.
410
+ * Pure and dependency-free (no fuzzy-match library) so it's cheap enough to
411
+ * re-run on every keystroke against a 10k-entry index.
412
+ */
413
+ declare function rankFileMentions(files: readonly FileMention[], query: string, limit: number): FileMention[];
414
+ /** Max popover results per query — enough to show a useful spread of matches
415
+ * without pushing the fuzzy-filtered list past what a popover can usefully
416
+ * render in one screen. */
417
+ declare const DEFAULT_MENTION_LIMIT = 20;
418
+ /** How long a `ready` index is served before a background refetch — long
419
+ * enough that a full session's worth of popover opens don't repeatedly hit
420
+ * the index endpoint, short enough that a stale listing doesn't linger too
421
+ * far past workspace file changes. Callers who need the index current right
422
+ * now (e.g. just after the agent creates a file) call `refresh()` instead of
423
+ * waiting on this window. */
424
+ declare const INDEX_REFRESH_AFTER_MS: number;
425
+ /** Popover empty-state copy for a `ready` index whose query matched nothing.
426
+ * Loading/warming/error states have their own copy — see `emptyTextFor`. */
427
+ declare const DEFAULT_MENTION_EMPTY_TEXT = "No matching files";
428
+ interface UseFileMentionsOptions {
429
+ /** GET endpoint returning `FileIndexResponse` (a `createSandboxFileIndexRoute`). */
430
+ indexUrl: string;
431
+ /** Max popover results per query. Default {@link DEFAULT_MENTION_LIMIT}. */
432
+ limit?: number;
433
+ /** How long a `ready` index is served without a background refetch.
434
+ * Default {@link INDEX_REFRESH_AFTER_MS}. */
435
+ refreshAfterMs?: number;
436
+ /** `fetch` override for tests / non-global-fetch hosts. Default `fetch`. */
437
+ fetchImpl?: typeof fetch;
438
+ /** Text shown in the popover's empty state once the index is loaded and
439
+ * the query matched nothing. Default {@link DEFAULT_MENTION_EMPTY_TEXT}. */
440
+ emptyText?: string;
441
+ }
442
+ interface UseFileMentionsResult {
443
+ /** Spread straight into `AgentComposer`'s `mention` prop. */
444
+ mention: ComposerMentionProp;
445
+ /** The files currently referenced by mentions in the composer's value —
446
+ * the send-body list (map through `fileMentionsToParts`). */
447
+ mentions: FileMention[];
448
+ /** Drop all currently-referenced mentions (e.g. after a successful send). */
449
+ clearMentions: () => void;
450
+ /** Force a re-fetch of the index right now, ignoring `refreshAfterMs` — for
451
+ * example right after the agent creates a file mid-session, so the next
452
+ * popover open sees it. Dedupes against an already-in-flight load rather
453
+ * than firing a second request. */
454
+ refresh: () => Promise<void>;
455
+ }
456
+ declare function useFileMentions(options: UseFileMentionsOptions): UseFileMentionsResult;
457
+
366
458
  /**
367
459
  * Provider brand marks — real logo path data (simple-icons / SVG Logos, both
368
460
  * CC0) inlined so the picker shows actual provider identity instead of
@@ -792,4 +884,4 @@ declare function useThinkingSeconds(active: boolean): number;
792
884
  */
793
885
  declare function ChatMessages({ messages, models, renderMarkdown, renderExtras, userLabel, agentLabel, loading, approval, onToolCallClick, toolRenderers, error, onRetry, renderEmpty, emptyState, header, }: ChatMessagesProps): react.JSX.Element;
794
886
 
795
- export { type ActivityTone, type AgentActivityPage, AgentActivityPanel, type AgentActivityPanelProps, type AgentActivityRecord, AgentSessionControls, type AgentSessionControlsProps, CatalogModel, ChatComposer, type ChatComposerProps, type ChatEmptyDoor, ChatEmptyState, type ChatEmptyStateProps, ChatInteraction, ChatInteractionField, ChatInteractionStatus, type ChatMessageMetrics, type ChatMessageSegment, ChatMessages, type ChatMessagesProps, ChatSelectField, type ChatStreamCallbacks, type ChatStreamToolCall, type ChatStreamToolResult, type ChatToolCallInfo, type ChatUiMessage, type ComposerFile, type ComposerFilePart, type ConsumeChatStreamResult, DEFAULT_EFFORT_LEVELS, type EffortLevel, EffortPicker, type EffortPickerProps, type FieldValues, FlowWaterfall, type FlowWaterfallProps, INTERACTION_SUBMIT_TIMEOUT_MESSAGE, INTERACTION_SUBMIT_TIMEOUT_MS, InteractionActionButton, type InteractionAnswerSubmission, type InteractionAnswerSubmitterOptions, InteractionBadge, type InteractionBadgeVariant, InteractionCancelData, InteractionPlanCard, type InteractionPlanCardProps, InteractionQuestionCard, type InteractionQuestionCardProps, InteractionRequestWire, type InteractionSubmitResult, MissionActivityLane, type MissionActivityLaneProps, ModelPicker, type ModelPickerProps, type ProposalApprovalHandlers, ProviderLogo, type ProviderLogoProps, QuestionOptionList, type QuestionOptionListProps, RunDrillIn, type RunDrillInProps, SeatPaywall, type SeatPaywallProps, type SmoothRevealOptions, type StreamChatOptions, type SubmitInteractionAnswer, type ToolDetailRenderers, type ToolRunRecord, type ToolRunStep, type UseChatInteractionsResult, type WaterfallRow, activityTone, buildAnswerData, cancelChatInteraction, consumeChatStream, createInteractionAnswerSubmitter, dispatchChatStreamLine, fieldAnswer, formatActivityCost, formatActivityDuration, formatModelCost, formatTokensPerSecond, hasSecretField, interactionStatusLabels, interactionTerminalNotes, isLateAnswerableStatus, lateAnswerMessage, mergeActivityPages, nextRevealCount, pendingApprovalOf, resolveChatInteraction, responseErrorMessage, restoreChatInteractions, streamChatTurn, terminalizePendingChatInteractions, upsertChatInteraction, useChatInteractions, usePending, usePopover, useSmoothText, useThinkingSeconds, waterfallLayout };
887
+ export { type ActivityTone, type AgentActivityPage, AgentActivityPanel, type AgentActivityPanelProps, type AgentActivityRecord, AgentSessionControls, type AgentSessionControlsProps, CatalogModel, ChatComposer, type ChatComposerProps, type ChatEmptyDoor, ChatEmptyState, type ChatEmptyStateProps, ChatInteraction, ChatInteractionField, ChatInteractionStatus, type ChatMessageMetrics, type ChatMessageSegment, ChatMessages, type ChatMessagesProps, ChatSelectField, type ChatStreamCallbacks, type ChatStreamToolCall, type ChatStreamToolResult, type ChatToolCallInfo, type ChatUiMessage, type ComposerFile, type ComposerFilePart, type ComposerMentionProp, type ConsumeChatStreamResult, DEFAULT_EFFORT_LEVELS, DEFAULT_MENTION_EMPTY_TEXT, DEFAULT_MENTION_LIMIT, type EffortLevel, EffortPicker, type EffortPickerProps, type FieldValues, FileMention, FlowWaterfall, type FlowWaterfallProps, INDEX_REFRESH_AFTER_MS, INTERACTION_SUBMIT_TIMEOUT_MESSAGE, INTERACTION_SUBMIT_TIMEOUT_MS, InteractionActionButton, type InteractionAnswerSubmission, type InteractionAnswerSubmitterOptions, InteractionBadge, type InteractionBadgeVariant, InteractionCancelData, InteractionPlanCard, type InteractionPlanCardProps, InteractionQuestionCard, type InteractionQuestionCardProps, InteractionRequestWire, type InteractionSubmitResult, type MentionItem, MissionActivityLane, type MissionActivityLaneProps, ModelPicker, type ModelPickerProps, type ProposalApprovalHandlers, ProviderLogo, type ProviderLogoProps, QuestionOptionList, type QuestionOptionListProps, RunDrillIn, type RunDrillInProps, SeatPaywall, type SeatPaywallProps, type SmoothRevealOptions, type StreamChatOptions, type SubmitInteractionAnswer, type ToolDetailRenderers, type ToolRunRecord, type ToolRunStep, type UseChatInteractionsResult, type UseFileMentionsOptions, type UseFileMentionsResult, type WaterfallRow, activityTone, buildAnswerData, cancelChatInteraction, consumeChatStream, createInteractionAnswerSubmitter, dispatchChatStreamLine, fieldAnswer, formatActivityCost, formatActivityDuration, formatModelCost, formatTokensPerSecond, hasSecretField, interactionStatusLabels, interactionTerminalNotes, isLateAnswerableStatus, lateAnswerMessage, mergeActivityPages, nextRevealCount, pendingApprovalOf, rankFileMentions, resolveChatInteraction, responseErrorMessage, restoreChatInteractions, streamChatTurn, terminalizePendingChatInteractions, upsertChatInteraction, useChatInteractions, useFileMentions, usePending, usePopover, useSmoothText, useThinkingSeconds, waterfallLayout };
@@ -5,8 +5,11 @@ import {
5
5
  ChatEmptyState,
6
6
  ChatMessages,
7
7
  DEFAULT_EFFORT_LEVELS,
8
+ DEFAULT_MENTION_EMPTY_TEXT,
9
+ DEFAULT_MENTION_LIMIT,
8
10
  EffortPicker,
9
11
  FlowWaterfall,
12
+ INDEX_REFRESH_AFTER_MS,
10
13
  INTERACTION_SUBMIT_TIMEOUT_MESSAGE,
11
14
  INTERACTION_SUBMIT_TIMEOUT_MS,
12
15
  InteractionActionButton,
@@ -38,6 +41,7 @@ import {
38
41
  mergeActivityPages,
39
42
  nextRevealCount,
40
43
  pendingApprovalOf,
44
+ rankFileMentions,
41
45
  resolveChatInteraction,
42
46
  responseErrorMessage,
43
47
  restoreChatInteractions,
@@ -45,19 +49,23 @@ import {
45
49
  terminalizePendingChatInteractions,
46
50
  upsertChatInteraction,
47
51
  useChatInteractions,
52
+ useFileMentions,
48
53
  usePending,
49
54
  usePopover,
50
55
  useSmoothText,
51
56
  useThinkingSeconds,
52
57
  waterfallLayout
53
- } from "../chunk-D4HB72W2.js";
58
+ } from "../chunk-KM766NN3.js";
54
59
  import {
55
60
  tabTerminalConnectionId,
56
61
  useSandboxTerminalConnection
57
62
  } from "../chunk-65P3HJY3.js";
58
63
  import {
59
- chatTurnRequestInit
60
- } from "../chunk-5SV5PSU7.js";
64
+ buildMentionPromptBlock,
65
+ chatTurnRequestInit,
66
+ fileMentionsToParts,
67
+ mediaTypeForMentionPath
68
+ } from "../chunk-2EO7CPL3.js";
61
69
  import "../chunk-2QI7XV2T.js";
62
70
  import {
63
71
  INTERACTION_CANCEL_EVENT,
@@ -90,8 +98,11 @@ export {
90
98
  ChatEmptyState,
91
99
  ChatMessages,
92
100
  DEFAULT_EFFORT_LEVELS,
101
+ DEFAULT_MENTION_EMPTY_TEXT,
102
+ DEFAULT_MENTION_LIMIT,
93
103
  EffortPicker,
94
104
  FlowWaterfall,
105
+ INDEX_REFRESH_AFTER_MS,
95
106
  INTERACTION_CANCEL_EVENT,
96
107
  INTERACTION_EVENT,
97
108
  INTERACTION_RESOLVED_EVENT,
@@ -109,6 +120,7 @@ export {
109
120
  SeatPaywall,
110
121
  activityTone,
111
122
  buildAnswerData,
123
+ buildMentionPromptBlock,
112
124
  canTransitionInteractionStatus,
113
125
  cancelChatInteraction,
114
126
  cancelStatusFor,
@@ -121,6 +133,7 @@ export {
121
133
  dispatchChatStreamLine,
122
134
  fieldAcceptsFreeText,
123
135
  fieldAnswer,
136
+ fileMentionsToParts,
124
137
  formatActivityCost,
125
138
  formatActivityDuration,
126
139
  formatModelCost,
@@ -136,6 +149,7 @@ export {
136
149
  isSafeInteractionFieldKey,
137
150
  isTerminalInteractionStatus,
138
151
  lateAnswerMessage,
152
+ mediaTypeForMentionPath,
139
153
  mergeActivityPages,
140
154
  nextRevealCount,
141
155
  noticePart,
@@ -145,6 +159,7 @@ export {
145
159
  pendingApprovalOf,
146
160
  persistedPartToInteraction,
147
161
  questionInteractionContentSignature,
162
+ rankFileMentions,
148
163
  resolveChatInteraction,
149
164
  responseErrorMessage,
150
165
  restoreChatInteractions,
@@ -153,6 +168,7 @@ export {
153
168
  terminalizePendingChatInteractions,
154
169
  upsertChatInteraction,
155
170
  useChatInteractions,
171
+ useFileMentions,
156
172
  usePending,
157
173
  usePopover,
158
174
  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.41",
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": [
@@ -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":[]}