@tangle-network/agent-app 0.43.43 → 0.43.44

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.
@@ -1,4 +1,4 @@
1
- import { AgentProfileMcpServer, ProvisionEvent, AgentProfileFileMount, SandboxInstance, AgentProfile, StorageConfig, ScopedTokenScope, TurnDriveResult, Sandbox } from '@tangle-network/sandbox';
1
+ import { AgentProfileMcpServer, ProvisionEvent, SandboxInstance, AgentProfileFileMount, AgentProfile, StorageConfig, ScopedTokenScope, TurnDriveResult, Sandbox } from '@tangle-network/sandbox';
2
2
  export { StorageConfig } from '@tangle-network/sandbox';
3
3
  import { a as ToolHeaderNames } from '../auth-DuptSkWh.js';
4
4
  import { f as AppToolName, c as AppToolContext } from '../types-BEOvc_ue.js';
@@ -14,6 +14,67 @@ type Outcome<T> = {
14
14
  error: Error;
15
15
  };
16
16
 
17
+ /**
18
+ * Reading arbitrary bytes out of a sandbox over an exec channel that only
19
+ * speaks text.
20
+ *
21
+ * `box.exec` returns stdout as a string, so a binary file has to be encoded to
22
+ * survive the trip: `wc -c` gives the on-disk length, `base64` gives the
23
+ * payload, and the decoded byte count is checked against the stat. That last
24
+ * check is the point of the module — an exec channel that caps or clips its
25
+ * output still reports `exitCode: 0` with a short buffer, which decodes into a
26
+ * perfectly valid but TRUNCATED file. Verifying the length turns that silent
27
+ * corruption into a loud failure at the boundary.
28
+ *
29
+ * Both helpers return typed outcomes; callers must inspect `succeeded` before
30
+ * touching `value`.
31
+ */
32
+ /** The `box.exec` surface these helpers use — structural, so a caller can pass
33
+ * the sandbox SDK's `SandboxInstance` directly or a narrower test double. */
34
+ interface SandboxExecChannel {
35
+ exec(command: string, options?: {
36
+ sessionId?: string;
37
+ }): Promise<{
38
+ stdout: string;
39
+ stderr: string;
40
+ exitCode: number;
41
+ }>;
42
+ }
43
+ interface SandboxExecOptions {
44
+ /** Run inside a named session rather than the box's default one. */
45
+ sessionId?: string;
46
+ }
47
+ type SandboxFileSizeOutcome = {
48
+ succeeded: true;
49
+ value: number;
50
+ } | {
51
+ succeeded: false;
52
+ error: string;
53
+ };
54
+ type SandboxFileBytesOutcome = {
55
+ succeeded: true;
56
+ value: {
57
+ bytes: Uint8Array;
58
+ size: number;
59
+ };
60
+ } | {
61
+ succeeded: false;
62
+ error: string;
63
+ };
64
+ /** Wraps a value in single quotes for `sh`, closing and reopening the quote
65
+ * around each embedded quote (`'` → `'"'"'`). Every path these helpers
66
+ * interpolate into a command goes through this — in-box filenames are
67
+ * arbitrary, so spaces, quotes and `$` are ordinary content, not syntax. */
68
+ declare function shellQuote(value: string): string;
69
+ /** Stats a sandbox file's byte length via `wc -c`. A caller enforcing a size
70
+ * cap must check this BEFORE {@link readSandboxBinaryBytes}, so an oversize
71
+ * file is rejected without paying for a base64 round trip of it. */
72
+ declare function statSandboxFileSize(box: SandboxExecChannel, absolutePath: string, options?: SandboxExecOptions): Promise<SandboxFileSizeOutcome>;
73
+ /** Reads a sandbox file as base64 and decodes it, verifying the decoded byte
74
+ * length against `expectedSize` (from a prior {@link statSandboxFileSize}). A
75
+ * mismatch is reported, never returned as a short buffer. */
76
+ declare function readSandboxBinaryBytes(box: SandboxExecChannel, absolutePath: string, expectedSize: number, options?: SandboxExecOptions): Promise<SandboxFileBytesOutcome>;
77
+
17
78
  interface TerminalProxyIdentity {
18
79
  userId: string;
19
80
  workspaceId: string;
@@ -436,6 +497,54 @@ declare function assertProvisionPayloadWithinCap(payload: ProvisionPayloadSectio
436
497
  * every exec inside it dies on the oversized entry.
437
498
  */
438
499
  declare function assertEnvWithinLimits(env: Record<string, string>): void;
500
+ /** What a peek can find. `not-running` carries the platform's own state string
501
+ * (`stopped`, `starting`, `failed`, …) — narrowing it to a union here would
502
+ * drop states the platform adds later, and every caller wants it for a log. */
503
+ type PeekWorkspaceSandboxOutcome = {
504
+ status: 'running';
505
+ box: SandboxInstance;
506
+ } | {
507
+ status: 'not-running';
508
+ state: string;
509
+ box: SandboxInstance;
510
+ } | {
511
+ status: 'absent';
512
+ };
513
+ /**
514
+ * Read-only twin of {@link ensureWorkspaceSandbox}: report whether a
515
+ * workspace's box exists and is running, WITHOUT provisioning, resuming, or
516
+ * bootstrapping anything.
517
+ *
518
+ * This is what a read-mostly path needs — a file-index route's `authorize`
519
+ * seam, a stale-lock reconciliation, a status badge. Calling `ensure` from one
520
+ * of those spins a box up as a side effect of a read (legal-agent #509), and
521
+ * costs the caller a cold start it never asked for.
522
+ *
523
+ * Matching is on BOTH the box key and the display name, IN THAT ORDER.
524
+ * `client.get(id)` keys on the platform's opaque sandbox id, not the
525
+ * deterministic key a product derives from a workspace, and is itself a
526
+ * `list().find` underneath — so a lookup by identity has to list and match.
527
+ * Provisioning here always stamps `name` with the box key, so the key is the
528
+ * authoritative match; the display-name pass exists only to adopt boxes on a
529
+ * host that predates that convention. The order matters: a single unordered
530
+ * `find` returns whichever the platform happens to list first, so a stopped
531
+ * display-name box could shadow a running box-key one and report
532
+ * `not-running` for a live workspace.
533
+ *
534
+ * Unlike `ensure`, this lists ALL statuses in one call: distinguishing "no box"
535
+ * from "box is stopped" is the whole point, and a status-filtered list cannot.
536
+ *
537
+ * A `client.list()` rejection propagates RAW, unlike the `Outcome`-wrapping
538
+ * helpers `ensure` uses internally. That is deliberate: there is no honest
539
+ * outcome to map a listing failure onto — it is not `absent` and not
540
+ * `not-running`, and inventing one would have callers act on a status the
541
+ * platform never reported. Callers that must tolerate it say so explicitly
542
+ * (the stale-turn-lock policy documents "a throw is treated as unreachable").
543
+ */
544
+ declare function peekWorkspaceSandbox(shell: SandboxRuntimeConfig, options: {
545
+ workspaceId: string;
546
+ userId?: string;
547
+ }): Promise<PeekWorkspaceSandboxOutcome>;
439
548
  declare function ensureWorkspaceSandbox(shell: SandboxRuntimeConfig, options: EnsureWorkspaceSandboxOptions): Promise<SandboxInstance>;
440
549
  interface ResolvedModel {
441
550
  model: string;
@@ -549,4 +658,4 @@ declare function classifySeveredStream(event: unknown): SandboxStepTransition |
549
658
  declare function isTerminalPromptEvent(event: unknown): boolean;
550
659
  declare function detectInteractiveQuestion(event: unknown): string | null;
551
660
 
552
- export { type AppToolDescriptor, type AuthenticatedSandboxUser, type BuildAppToolMcpServersOptions, type BuildSandboxToolFileMountsOptions, DEFAULT_SANDBOX_RESOURCES, type DriveSandboxTurnOptions, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, type EnsureWorkspaceSandboxOptions, type LivenessProbeConfig, type MemberSyncSeam, type Outcome, PROVISION_PAYLOAD_MAX_BYTES, type ProfileComposeOptions, type PromptInputPart, type ProviderResolutionConfig, type ProvisionPayloadSections, type ProvisionProfileSection, type ResolveSandboxClientCredentialsOptions, type ResolvedModel, type SandboxApiCredentials, type SandboxBuildContext, type SandboxClientCredentials, type SandboxCredentialEnvironment, type SandboxPermissionLevel, type SandboxResourceConfig, type SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, type SandboxRuntimeConfig, type SandboxRuntimeConnection, type SandboxScope, type SandboxStepTransition, type SandboxTerminalTokenOptions, type SandboxTerminalTokenResult, type SandboxTerminalTokenSubject, type SandboxTerminalWsMatch, type SandboxToolPathOptions, type SandboxToolSpec, type ScopedTokenResult, type SecretStore, type StoppedSandboxResumeFailure, type StoppedSandboxResumeRecovery, type StreamSandboxPromptOptions, type TerminalProxyIdentity, type WorkspaceSandboxConnectionArgs, type WorkspaceSandboxConnectionHandlerOptions, type WorkspaceSandboxEnsureContext, type WorkspaceSandboxInstanceLike, type WorkspaceSandboxManager, type WorkspaceSandboxManagerOptions, type WorkspaceSandboxRuntimeProxyArgs, type WorkspaceSandboxRuntimeProxyHandlerOptions, type WorkspaceSandboxTerminalUpgradeHandlerOptions, type WriteProfileFilesOptions, assertEnvWithinLimits, assertProvisionPayloadWithinCap, attachReasoningEffort, bearerSubprotocolToken, bearerToken, buildAppToolMcpServers, buildSandboxRuntimeProxyHeaders, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, createSandboxTerminalToken, createWorkspaceSandboxConnectionHandler, createWorkspaceSandboxManager, createWorkspaceSandboxRuntimeProxyHandler, createWorkspaceSandboxTerminalUpgradeHandler, deferredCorpusHash, 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 };
661
+ export { type AppToolDescriptor, type AuthenticatedSandboxUser, type BuildAppToolMcpServersOptions, type BuildSandboxToolFileMountsOptions, DEFAULT_SANDBOX_RESOURCES, type DriveSandboxTurnOptions, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, type EnsureWorkspaceSandboxOptions, type LivenessProbeConfig, type MemberSyncSeam, type Outcome, PROVISION_PAYLOAD_MAX_BYTES, type PeekWorkspaceSandboxOutcome, type ProfileComposeOptions, type PromptInputPart, type ProviderResolutionConfig, type ProvisionPayloadSections, type ProvisionProfileSection, type ResolveSandboxClientCredentialsOptions, type ResolvedModel, type SandboxApiCredentials, type SandboxBuildContext, type SandboxClientCredentials, type SandboxCredentialEnvironment, type SandboxExecChannel, type SandboxExecOptions, type SandboxFileBytesOutcome, type SandboxFileSizeOutcome, type SandboxPermissionLevel, type SandboxResourceConfig, type SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, type SandboxRuntimeConfig, type SandboxRuntimeConnection, type SandboxScope, type SandboxStepTransition, type SandboxTerminalTokenOptions, type SandboxTerminalTokenResult, type SandboxTerminalTokenSubject, type SandboxTerminalWsMatch, type SandboxToolPathOptions, type SandboxToolSpec, type ScopedTokenResult, type SecretStore, type StoppedSandboxResumeFailure, type StoppedSandboxResumeRecovery, type StreamSandboxPromptOptions, type TerminalProxyIdentity, type WorkspaceSandboxConnectionArgs, type WorkspaceSandboxConnectionHandlerOptions, type WorkspaceSandboxEnsureContext, type WorkspaceSandboxInstanceLike, type WorkspaceSandboxManager, type WorkspaceSandboxManagerOptions, type WorkspaceSandboxRuntimeProxyArgs, type WorkspaceSandboxRuntimeProxyHandlerOptions, type WorkspaceSandboxTerminalUpgradeHandlerOptions, type WriteProfileFilesOptions, assertEnvWithinLimits, assertProvisionPayloadWithinCap, attachReasoningEffort, bearerSubprotocolToken, bearerToken, buildAppToolMcpServers, buildSandboxRuntimeProxyHeaders, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, createSandboxTerminalToken, createWorkspaceSandboxConnectionHandler, createWorkspaceSandboxManager, createWorkspaceSandboxRuntimeProxyHandler, createWorkspaceSandboxTerminalUpgradeHandler, deferredCorpusHash, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, encodeSandboxRuntimePath, ensureWorkspaceSandbox, flattenHistory, getClient, isSandboxTerminalWsUpgrade, isTerminalPromptEvent, matchSandboxTerminalWsPath, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, mintTerminalProxyToken, peekWorkspaceSandbox, readSandboxBinaryBytes, readSecret, resetClientCache, resolveModel, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, shellQuote, splitDeferredProfileFiles, statSandboxFileSize, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, terminalTokenFromRequest, verifySandboxTerminalToken, verifyTerminalProxyToken, writeProfileFilesToBox };
@@ -34,6 +34,8 @@ import {
34
34
  mergeHistoryIntoParts,
35
35
  mintSandboxScopedToken,
36
36
  mintTerminalProxyToken,
37
+ peekWorkspaceSandbox,
38
+ readSandboxBinaryBytes,
37
39
  readSecret,
38
40
  resetClientCache,
39
41
  resolveModel,
@@ -44,7 +46,9 @@ import {
44
46
  sandboxToolPath,
45
47
  sandboxToolRootDir,
46
48
  secretStoreFromClient,
49
+ shellQuote,
47
50
  splitDeferredProfileFiles,
51
+ statSandboxFileSize,
48
52
  storeSecret,
49
53
  streamSandboxPrompt,
50
54
  syncSandboxMemberAdd,
@@ -54,7 +58,7 @@ import {
54
58
  verifySandboxTerminalToken,
55
59
  verifyTerminalProxyToken,
56
60
  writeProfileFilesToBox
57
- } from "../chunk-5GWXCSLQ.js";
61
+ } from "../chunk-5RJNEEO2.js";
58
62
  import "../chunk-E7QYOOON.js";
59
63
  import "../chunk-PJC4NXPA.js";
60
64
  import "../chunk-7EVZUIHW.js";
@@ -97,6 +101,8 @@ export {
97
101
  mergeHistoryIntoParts,
98
102
  mintSandboxScopedToken,
99
103
  mintTerminalProxyToken,
104
+ peekWorkspaceSandbox,
105
+ readSandboxBinaryBytes,
100
106
  readSecret,
101
107
  resetClientCache,
102
108
  resolveModel,
@@ -107,7 +113,9 @@ export {
107
113
  sandboxToolPath,
108
114
  sandboxToolRootDir,
109
115
  secretStoreFromClient,
116
+ shellQuote,
110
117
  splitDeferredProfileFiles,
118
+ statSandboxFileSize,
111
119
  storeSecret,
112
120
  streamSandboxPrompt,
113
121
  syncSandboxMemberAdd,
@@ -5,13 +5,14 @@ export { d as ComposerAnswerDelivery, I as INTERACTION_CANCEL_EVENT, e as INTERA
5
5
  import { ChatPlan } from '../plans/index.js';
6
6
  import { InteractionData } from '@tangle-network/agent-interface';
7
7
  export { InteractionData, InteractionOutcome, InteractionRequest } from '@tangle-network/agent-interface';
8
- import { j as FileMention } from '../file-index-Bw_IQE_G.js';
9
- 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';
8
+ import { F as FileMention, d as ChatMentionPart } from '../parts-1_3y2JmR.js';
9
+ export { c as ChatMentionKind, D as ChatTurnFilePartInput, B as ChatTurnPartInput, A as ChatTurnRequestPayload, L as buildMentionPromptBlock, N as chatTurnRequestInit, O as fileMentionsToParts, P as mediaTypeForMentionPath, x as mentionInputToPart, Q as mentionKindForPath, y as mentionPartsFromMessageParts } from '../parts-1_3y2JmR.js';
10
10
  import { S as StepAgentActivity } from '../agent-activity-C8ZG0F0M.js';
11
11
  import { a as FlowTrace } from '../flow-types-Cb_AblZs.js';
12
12
  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';
13
13
  import { CatalogModel } from '../catalog/index.js';
14
14
  import { Harness } from '../harness/index.js';
15
+ export { b as FileIndexReadyResponse, c as FileIndexResponse, d as FileIndexWarmingResponse } from '../file-index-Bn6sitKb.js';
15
16
 
16
17
  type DurablePlanDecision = 'approved' | 'rejected';
17
18
  /** Stable authority receipt for the follow-up turn dispatched by a plan
@@ -608,6 +609,53 @@ interface UseFileMentionsResult {
608
609
  }
609
610
  declare function useFileMentions(options: UseFileMentionsOptions): UseFileMentionsResult;
610
611
 
612
+ /**
613
+ * Transcript-side counterpart to the composer's `@`-mention primitive
614
+ * (sandbox-ui#184). The composer serializes a picked file into the message
615
+ * text as `@<path>`; this module is the exact inverse — it finds those tokens
616
+ * again in a PERSISTED message and splits the text so a renderer can draw a
617
+ * pill where the user typed one and leave the rest as prose.
618
+ *
619
+ * Pure and product-agnostic: no React, no fetch, no DOM. The only input beyond
620
+ * the text is the message's OWN mention parts, so one message can never render
621
+ * a pill for a path another message mentioned.
622
+ *
623
+ * `ChatMentionPart` and the runtime helpers `mentionInputToPart` /
624
+ * `mentionPartsFromMessageParts` are re-exported here from `../chat-store/parts`
625
+ * directly (not the `/chat-store` barrel), so a browser bundle gets the mention
626
+ * vocabulary and its converters without importing `/chat-store`, whose barrel
627
+ * pulls the drizzle peer.
628
+ */
629
+
630
+ /** One run of a segmented message: literal prose, or a matched mention with
631
+ * the part that produced it. `text` for a mention segment is the token as it
632
+ * appears in the message (`@<path>`), so a renderer that ignores `part` still
633
+ * reproduces the original string exactly. */
634
+ interface MentionTextSegment {
635
+ type: 'text' | 'mention';
636
+ text: string;
637
+ part?: ChatMentionPart;
638
+ }
639
+ /**
640
+ * Split a message's text into plain-text and mention segments by matching
641
+ * `@<path>` runs against that message's own mention parts.
642
+ *
643
+ * Only a part whose exact `@<path>` token appears in `content`, at a token
644
+ * boundary on both sides, counts as a match; everything else — including
645
+ * unrelated `@` text — passes through as plain text untouched. When two parts'
646
+ * tokens both match at the same position (one path a prefix of another), the
647
+ * LONGEST token wins, so nested-looking paths split at the right boundary.
648
+ *
649
+ * Returns the matched parts alongside the segments: a caller that also renders
650
+ * a fallback chip row can drop the chip for anything now shown inline and keep
651
+ * it only for mentions the text does not actually contain (a restored draft, a
652
+ * queued message whose text was edited).
653
+ */
654
+ declare function segmentMentionContent(content: string, parts: ReadonlyArray<ChatMentionPart>): {
655
+ segments: MentionTextSegment[];
656
+ matched: Set<ChatMentionPart>;
657
+ };
658
+
611
659
  /**
612
660
  * Provider brand marks — real logo path data (simple-icons / SVG Logos, both
613
661
  * CC0) inlined so the picker shows actual provider identity instead of
@@ -1043,4 +1091,4 @@ declare function useThinkingSeconds(active: boolean): number;
1043
1091
  */
1044
1092
  declare function ChatMessages({ messages, models, renderMarkdown, renderExtras, durableCards, userLabel, agentLabel, loading, approval, onToolCallClick, toolRenderers, error, onRetry, renderEmpty, emptyState, header, }: ChatMessagesProps): react.JSX.Element;
1045
1093
 
1046
- export { type ActivityTone, type AgentActivityPage, AgentActivityPanel, type AgentActivityPanelProps, type AgentActivityRecord, AgentSessionControls, type AgentSessionControlsProps, CatalogModel, ChatComposer, type ChatComposerProps, type ChatEmptyDoor, ChatEmptyState, type ChatEmptyStateProps, ChatInteraction, ChatInteractionField, type ChatInteractionRestoreMode, 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 DurableChatCard, DurableChatCards, type DurableChatCardsProps, type DurableInteractionAnswerSubmitterOptions, DurablePlanCard, type DurablePlanCardProps, DurablePlanClientError, type DurablePlanCurrentInput, type DurablePlanDecision, type DurablePlanDecisionClient, type DurablePlanDecisionClientOptions, type DurablePlanDecisionInput, type DurablePlanDecisionResult, type DurablePlanFollowUpReceipt, 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, InteractionAnswers, type InteractionAttemptStore, 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, type RestoreChatInteractionsOptions, RunDrillIn, type RunDrillInProps, SeatPaywall, type SeatPaywallProps, type SmoothRevealOptions, type StreamChatOptions, type SubmitInteractionAnswer, type ToolDetailRenderers, type ToolRunRecord, type ToolRunStep, type UseChatInteractionsOptions, type UseChatInteractionsResult, type UseDurablePlanFlowOptions, type UseDurablePlanFlowResult, type UseFileMentionsOptions, type UseFileMentionsResult, type WaterfallRow, activityTone, buildAnswerData, cancelChatInteraction, consumeChatStream, createDurableInteractionAnswerSubmitter, createDurablePlanDecisionClient, createInteractionAnswerSubmitter, createMemoryInteractionAttemptStore, createSessionInteractionAttemptStore, dispatchChatStreamLine, durableChatCardsFromParts, fieldAnswer, fieldValuesFromAnswers, formatActivityCost, formatActivityDuration, formatModelCost, formatTokensPerSecond, hasSecretField, hydrateChatInteractions, interactionStatusLabels, interactionSubmissionSignature, interactionTerminalNotes, isLateAnswerableStatus, lateAnswerMessage, mergeActivityPages, nextRevealCount, pendingApprovalOf, rankFileMentions, resolveChatInteraction, responseErrorMessage, restoreChatInteractions, streamChatTurn, terminalizePendingChatInteractions, upsertChatInteraction, useChatInteractions, useDurablePlanFlow, useFileMentions, usePending, usePopover, useSmoothText, useThinkingSeconds, waterfallLayout };
1094
+ export { type ActivityTone, type AgentActivityPage, AgentActivityPanel, type AgentActivityPanelProps, type AgentActivityRecord, AgentSessionControls, type AgentSessionControlsProps, CatalogModel, ChatComposer, type ChatComposerProps, type ChatEmptyDoor, ChatEmptyState, type ChatEmptyStateProps, ChatInteraction, ChatInteractionField, type ChatInteractionRestoreMode, ChatInteractionStatus, ChatMentionPart, 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 DurableChatCard, DurableChatCards, type DurableChatCardsProps, type DurableInteractionAnswerSubmitterOptions, DurablePlanCard, type DurablePlanCardProps, DurablePlanClientError, type DurablePlanCurrentInput, type DurablePlanDecision, type DurablePlanDecisionClient, type DurablePlanDecisionClientOptions, type DurablePlanDecisionInput, type DurablePlanDecisionResult, type DurablePlanFollowUpReceipt, 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, InteractionAnswers, type InteractionAttemptStore, InteractionBadge, type InteractionBadgeVariant, InteractionCancelData, InteractionPlanCard, type InteractionPlanCardProps, InteractionQuestionCard, type InteractionQuestionCardProps, InteractionRequestWire, type InteractionSubmitResult, type MentionItem, type MentionTextSegment, MissionActivityLane, type MissionActivityLaneProps, ModelPicker, type ModelPickerProps, type ProposalApprovalHandlers, ProviderLogo, type ProviderLogoProps, QuestionOptionList, type QuestionOptionListProps, type RestoreChatInteractionsOptions, RunDrillIn, type RunDrillInProps, SeatPaywall, type SeatPaywallProps, type SmoothRevealOptions, type StreamChatOptions, type SubmitInteractionAnswer, type ToolDetailRenderers, type ToolRunRecord, type ToolRunStep, type UseChatInteractionsOptions, type UseChatInteractionsResult, type UseDurablePlanFlowOptions, type UseDurablePlanFlowResult, type UseFileMentionsOptions, type UseFileMentionsResult, type WaterfallRow, activityTone, buildAnswerData, cancelChatInteraction, consumeChatStream, createDurableInteractionAnswerSubmitter, createDurablePlanDecisionClient, createInteractionAnswerSubmitter, createMemoryInteractionAttemptStore, createSessionInteractionAttemptStore, dispatchChatStreamLine, durableChatCardsFromParts, fieldAnswer, fieldValuesFromAnswers, formatActivityCost, formatActivityDuration, formatModelCost, formatTokensPerSecond, hasSecretField, hydrateChatInteractions, interactionStatusLabels, interactionSubmissionSignature, interactionTerminalNotes, isLateAnswerableStatus, lateAnswerMessage, mergeActivityPages, nextRevealCount, pendingApprovalOf, rankFileMentions, resolveChatInteraction, responseErrorMessage, restoreChatInteractions, segmentMentionContent, streamChatTurn, terminalizePendingChatInteractions, upsertChatInteraction, useChatInteractions, useDurablePlanFlow, useFileMentions, usePending, usePopover, useSmoothText, useThinkingSeconds, waterfallLayout };
@@ -56,6 +56,7 @@ import {
56
56
  resolveChatInteraction,
57
57
  responseErrorMessage,
58
58
  restoreChatInteractions,
59
+ segmentMentionContent,
59
60
  streamChatTurn,
60
61
  terminalizePendingChatInteractions,
61
62
  upsertChatInteraction,
@@ -67,18 +68,22 @@ import {
67
68
  useSmoothText,
68
69
  useThinkingSeconds,
69
70
  waterfallLayout
70
- } from "../chunk-PEUBCJXF.js";
71
+ } from "../chunk-Q4EU6MGU.js";
71
72
  import {
72
73
  tabTerminalConnectionId,
73
74
  useSandboxTerminalConnection
74
75
  } from "../chunk-65P3HJY3.js";
76
+ import "../chunk-LCNY3DCM.js";
77
+ import "../chunk-2QI7XV2T.js";
75
78
  import {
76
79
  buildMentionPromptBlock,
77
80
  chatTurnRequestInit,
78
81
  fileMentionsToParts,
79
- mediaTypeForMentionPath
80
- } from "../chunk-2EO7CPL3.js";
81
- import "../chunk-2QI7XV2T.js";
82
+ mediaTypeForMentionPath,
83
+ mentionInputToPart,
84
+ mentionKindForPath,
85
+ mentionPartsFromMessageParts
86
+ } from "../chunk-6E2XJSCT.js";
82
87
  import {
83
88
  INTERACTION_CANCEL_EVENT,
84
89
  INTERACTION_EVENT,
@@ -176,6 +181,9 @@ export {
176
181
  isTerminalInteractionStatus,
177
182
  lateAnswerMessage,
178
183
  mediaTypeForMentionPath,
184
+ mentionInputToPart,
185
+ mentionKindForPath,
186
+ mentionPartsFromMessageParts,
179
187
  mergeActivityPages,
180
188
  nextRevealCount,
181
189
  noticePart,
@@ -190,6 +198,7 @@ export {
190
198
  resolveChatInteraction,
191
199
  responseErrorMessage,
192
200
  restoreChatInteractions,
201
+ segmentMentionContent,
193
202
  stampInteractionAnswers,
194
203
  streamChatTurn,
195
204
  tabTerminalConnectionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.43.43",
3
+ "version": "0.43.44",
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,191 +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
- var MENTION_IMAGE_MEDIA_TYPES = /* @__PURE__ */ new Map([
43
- [".png", "image/png"],
44
- [".jpg", "image/jpeg"],
45
- [".jpeg", "image/jpeg"],
46
- [".gif", "image/gif"],
47
- [".webp", "image/webp"],
48
- [".svg", "image/svg+xml"],
49
- [".bmp", "image/bmp"],
50
- [".heic", "image/heic"],
51
- [".heif", "image/heif"],
52
- [".avif", "image/avif"]
53
- ]);
54
- function extensionOf(path) {
55
- const base = path.split("/").filter(Boolean).pop() ?? path;
56
- const dot = base.lastIndexOf(".");
57
- return dot > 0 ? base.slice(dot).toLowerCase() : "";
58
- }
59
- function mediaTypeForMentionPath(path) {
60
- return MENTION_IMAGE_MEDIA_TYPES.get(extensionOf(path));
61
- }
62
- function fileMentionsToParts(mentions, opts = {}) {
63
- const resolvePath = opts.resolvePath ?? ((path) => path);
64
- return mentions.map((mention) => {
65
- const mediaType = mediaTypeForMentionPath(mention.path);
66
- const part = {
67
- type: mediaType ? "image" : "file",
68
- filename: mention.name,
69
- path: resolvePath(mention.path)
70
- };
71
- if (mediaType) part.mediaType = mediaType;
72
- return part;
73
- });
74
- }
75
- function buildMentionPromptBlock(mentions) {
76
- if (mentions.length === 0) return "";
77
- const lines = mentions.map((m) => `- ${m.name} (${m.path})`);
78
- return `
79
-
80
- Mentioned files \u2014 read them from these paths:
81
- ${lines.join("\n")}`;
82
- }
83
- function parseChatTurnParts(raw) {
84
- if (raw === void 0 || raw === null) return [];
85
- if (!Array.isArray(raw)) throw new ChatTurnInputError("parts must be an array");
86
- return raw.map((entry, index) => {
87
- const part = entry;
88
- if (!part || typeof part !== "object") {
89
- throw new ChatTurnInputError(`parts[${index}] must be an object`);
90
- }
91
- if (part.type !== "image" && part.type !== "file") {
92
- throw new ChatTurnInputError(`parts[${index}].type must be 'image' or 'file'`);
93
- }
94
- for (const key of ["filename", "mediaType", "url", "path", "content"]) {
95
- if (part[key] !== void 0 && typeof part[key] !== "string") {
96
- throw new ChatTurnInputError(`parts[${index}].${key} must be a string`);
97
- }
98
- }
99
- if (!part.url && !part.path && !part.content) {
100
- throw new ChatTurnInputError(`parts[${index}] needs a url, path, or content`);
101
- }
102
- return {
103
- type: part.type,
104
- ...part.filename !== void 0 ? { filename: part.filename } : {},
105
- ...part.mediaType !== void 0 ? { mediaType: part.mediaType } : {},
106
- ...part.url !== void 0 ? { url: part.url } : {},
107
- ...part.path !== void 0 ? { path: part.path } : {},
108
- ...part.content !== void 0 ? { content: part.content } : {}
109
- };
110
- });
111
- }
112
-
113
- // src/chat-routes/file-index.ts
114
- var DEFAULT_IGNORE_SEGMENTS = [
115
- "node_modules",
116
- "dist",
117
- "build",
118
- "out",
119
- "coverage",
120
- "target",
121
- "__pycache__",
122
- "venv"
123
- ];
124
- function isIgnored(relPath, ignoreSegments) {
125
- for (const segment of relPath.split("/")) {
126
- if (!segment) continue;
127
- if (segment.startsWith(".")) return true;
128
- if (ignoreSegments.has(segment)) return true;
129
- }
130
- return false;
131
- }
132
- function relativeTo(root, path) {
133
- const prefix = root.endsWith("/") ? root : `${root}/`;
134
- if (path.startsWith(prefix)) return path.slice(prefix.length);
135
- if (path === root) return "";
136
- return path;
137
- }
138
- function basename(path) {
139
- const segments = path.split("/").filter(Boolean);
140
- return segments[segments.length - 1] ?? path;
141
- }
142
- function createSandboxFileIndexRoute(options) {
143
- const maxDepth = options.maxDepth ?? 12;
144
- const maxEntries = options.maxEntries ?? 5e3;
145
- const cacheTtlSeconds = options.cacheTtlSeconds ?? 20;
146
- const staticIgnore = /* @__PURE__ */ new Set([...DEFAULT_IGNORE_SEGMENTS, ...options.ignore ?? []]);
147
- return async function fileIndex(request) {
148
- const auth = await options.authorize({ request });
149
- if (auth.status === "denied") return auth.response;
150
- if (auth.status === "warming") {
151
- return Response.json({ status: "warming" });
152
- }
153
- const cache = options.cache;
154
- if (cache && auth.cacheKey) {
155
- const cached = await cache.get(auth.cacheKey);
156
- if (cached) return Response.json(cached);
157
- }
158
- const ignoreSegments = auth.ignore?.length ? /* @__PURE__ */ new Set([...staticIgnore, ...auth.ignore]) : staticIgnore;
159
- const scan = await auth.fs.tree(auth.root, { maxDepth });
160
- const filtered = scan.files.filter((f) => !isIgnored(relativeTo(scan.root, f.path), ignoreSegments));
161
- const truncated = scan.stats.truncated || filtered.length > maxEntries;
162
- const files = filtered.slice(0, maxEntries).map((f) => {
163
- const path = relativeTo(scan.root, f.path);
164
- const entry = { path, name: basename(path) };
165
- if (typeof f.size === "number") entry.size = f.size;
166
- return entry;
167
- });
168
- const body = {
169
- status: "ready",
170
- files,
171
- truncated,
172
- generatedAt: (/* @__PURE__ */ new Date()).toISOString()
173
- };
174
- if (cache && auth.cacheKey) await cache.put(auth.cacheKey, body, { ttlSeconds: cacheTtlSeconds });
175
- return Response.json(body);
176
- };
177
- }
178
-
179
- export {
180
- chatTurnRequestInit,
181
- INLINE_PARTS_MAX_BYTES,
182
- ChatTurnInputError,
183
- promptPartsByteSize,
184
- assertPromptPartsWithinCap,
185
- mediaTypeForMentionPath,
186
- fileMentionsToParts,
187
- buildMentionPromptBlock,
188
- parseChatTurnParts,
189
- createSandboxFileIndexRoute
190
- };
191
- //# sourceMappingURL=chunk-2EO7CPL3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/chat-routes/wire.ts","../src/chat-routes/file-index.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// ── file mentions ────────────────────────────────────────────────────────\n//\n// A file mention (`@`-picked in the composer, sandbox-ui#184) is a path\n// reference into the workspace sandbox — no byte upload. These helpers turn\n// a resolved mention list into wire parts and the prompt pointer block that\n// tells the agent where to read them from.\n\n/** A file mention resolved from the composer's `@`-picker: the\n * workspace-relative path plus enough metadata to build a prompt part and\n * pointer text. `path` is the canonical identity — the mention pill's\n * `MentionItem.id` for the file kind (`/web-react`'s `useFileMentions`). */\nexport interface FileMention {\n path: string\n name: string\n size?: number\n}\n\nconst MENTION_IMAGE_MEDIA_TYPES: ReadonlyMap<string, string> = new Map([\n ['.png', 'image/png'],\n ['.jpg', 'image/jpeg'],\n ['.jpeg', 'image/jpeg'],\n ['.gif', 'image/gif'],\n ['.webp', 'image/webp'],\n ['.svg', 'image/svg+xml'],\n ['.bmp', 'image/bmp'],\n ['.heic', 'image/heic'],\n ['.heif', 'image/heif'],\n ['.avif', 'image/avif'],\n])\n\nfunction extensionOf(path: string): string {\n const base = path.split('/').filter(Boolean).pop() ?? path\n const dot = base.lastIndexOf('.')\n return dot > 0 ? base.slice(dot).toLowerCase() : ''\n}\n\n/** The `image/*` mime for a mention path by extension, or `undefined` for\n * anything not in the known image set (dispatched as `type: 'file'`). */\nexport function mediaTypeForMentionPath(path: string): string | undefined {\n return MENTION_IMAGE_MEDIA_TYPES.get(extensionOf(path))\n}\n\nexport interface FileMentionsToPartsOptions {\n /** Resolve a mention's workspace-relative path to the absolute path the\n * dispatched part should carry (e.g. a host prefixing the in-box vault\n * root). Default: identity — the path travels unchanged. */\n resolvePath?: (path: string) => string\n}\n\n/** Maps resolved file mentions to path-only `ChatTurnFilePartInput`s —\n * `image` vs `file` by extension, and always a `path`, never a `url` (the\n * url/path XOR invariant: a mention is a sandbox path reference, never\n * inline bytes). */\nexport function fileMentionsToParts(\n mentions: readonly FileMention[],\n opts: FileMentionsToPartsOptions = {},\n): ChatTurnFilePartInput[] {\n const resolvePath = opts.resolvePath ?? ((path: string) => path)\n return mentions.map((mention) => {\n const mediaType = mediaTypeForMentionPath(mention.path)\n const part: ChatTurnFilePartInput = {\n type: mediaType ? 'image' : 'file',\n filename: mention.name,\n path: resolvePath(mention.path),\n }\n if (mediaType) part.mediaType = mediaType\n return part\n })\n}\n\n/** The agent-facing pointer block appended to the dispatched prompt — never\n * persisted in message `content`. Empty array → `''` so callers can append\n * unconditionally. This is the sole producer of that text: the current\n * turn's dispatch and any history projection built from the same mention\n * list both route through here, so the two can't drift apart. */\nexport function buildMentionPromptBlock(\n mentions: readonly Pick<FileMention, 'name' | 'path'>[],\n): string {\n if (mentions.length === 0) return ''\n const lines = mentions.map((m) => `- ${m.name} (${m.path})`)\n return `\\n\\nMentioned files — read them from these paths:\\n${lines.join('\\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","/**\n * `createSandboxFileIndexRoute` — server side of `@`-file-mentions\n * (companion to sandbox-ui#184's composer mention primitive). Serves a flat,\n * ignore-filtered listing of the workspace sandbox so `useFileMentions`\n * (`/web-react`) can filter it client-side without a round trip per\n * keystroke.\n *\n * Same seam style as `createUploadRoute`: `authorize({ request })` resolves a\n * structural `{ tree(path, opts) }` handle (the shape of the sandbox SDK's\n * `box.fs.tree`) — no SDK import here. `authorize` also carries the\n * cold-box signal: a sandbox that isn't running yet answers `{ status:\n * 'warming' }` directly, never provisions-and-waits inside this route.\n */\n\nimport type { FileMention } from './wire'\n\n/** One entry from a structural `tree()` scan. Mirrors the sandbox SDK's\n * `FileTreeFile` (`path`, `size`, `mtime`) — `mtime` is unused here so it's\n * omitted from the structural match. */\nexport interface SandboxTreeFile {\n path: string\n size: number\n}\n\n/** Structural match of the sandbox SDK's `box.fs.tree` result shape\n * (`FileTreeResult`). `stats.truncated` is the only stat this route reads;\n * the rest ride through unread on the real SDK type. */\nexport interface SandboxTreeResult {\n root: string\n files: SandboxTreeFile[]\n stats: { truncated: boolean }\n}\n\n/** Structural match of the sandbox SDK's `box.fs` tree surface. */\nexport interface SandboxFileTreeSource {\n tree(path: string, options?: { maxDepth?: number }): Promise<SandboxTreeResult>\n}\n\nexport interface FileIndexReadyResponse {\n status: 'ready'\n /** Workspace-relative entries. Same shape as `FileMention` (`./wire`) so a\n * client can hand a response entry straight to `fileMentionsToParts` /\n * `buildMentionPromptBlock` without remapping. */\n files: FileMention[]\n /** True when either the underlying scan truncated (SDK-side cap) or this\n * route's own `maxEntries` cap trimmed the filtered list. The client\n * should show \"showing first N files\" rather than imply completeness. */\n truncated: boolean\n generatedAt: string\n}\n\n/** Cold-box answer: no provisioning happened, no files were scanned. The\n * client shows a warming state and retries — this route never blocks on a\n * box coming up. */\nexport interface FileIndexWarmingResponse {\n status: 'warming'\n}\n\nexport type FileIndexResponse = FileIndexReadyResponse | FileIndexWarmingResponse\n\n/** Short-TTL cache seam so repeat popover opens in the same session don't\n * re-scan the workspace. Host-provided (e.g. a KV binding); `key` is\n * whatever `authorize` returns as `cacheKey` — this route treats it opaquely. */\nexport interface FileIndexCache {\n get(key: string): Promise<FileIndexReadyResponse | null> | FileIndexReadyResponse | null\n put(key: string, value: FileIndexReadyResponse, options?: { ttlSeconds?: number }): Promise<void> | void\n}\n\nexport type FileIndexAuthorization =\n | {\n status: 'ready'\n /** Structural sandbox `fs` handle, usually `ensureWorkspaceSandbox(...)` → `box.fs`. */\n fs: SandboxFileTreeSource\n /** Workspace root to index (e.g. `/home/agent`). */\n root: string\n /** Extra ignore segments for this request, merged with the route's\n * defaults + `CreateSandboxFileIndexRouteOptions.ignore`. */\n ignore?: string[]\n /** Opaque cache key for the optional cache seam. Omit to skip caching\n * for this request (e.g. a workspace the host chooses not to cache). */\n cacheKey?: string\n }\n | { status: 'warming' }\n | { status: 'denied'; response: Response }\n\nexport interface CreateSandboxFileIndexRouteOptions {\n /** Authenticate the caller, resolve the sandbox `fs` handle, and signal a\n * cold box — never provisions or waits. */\n authorize(args: { request: Request }): Promise<FileIndexAuthorization>\n /** Extra ignore segments beyond the route's defaults (node_modules, .git,\n * dotfiles/dot-dirs, common build dirs). Matched as exact path-segment\n * names, same rule as the defaults. */\n ignore?: string[]\n /** Passed to `fs.tree` as `options.maxDepth`. Default 12. */\n maxDepth?: number\n /** Hard cap on entries returned after filtering. Default 5000. */\n maxEntries?: number\n /** Optional host-provided cache seam. */\n cache?: FileIndexCache\n /** Cache TTL in seconds when `cache` is set. Default 20. */\n cacheTtlSeconds?: number\n}\n\n/** Segment names ignored anywhere in a path, beyond the generic dotfile rule\n * below. Intentionally small and language/framework-agnostic — callers\n * extend it via `ignore` for anything domain-specific (e.g. a vault's\n * `uploads` dir). */\nconst DEFAULT_IGNORE_SEGMENTS = [\n 'node_modules',\n 'dist',\n 'build',\n 'out',\n 'coverage',\n 'target',\n '__pycache__',\n 'venv',\n]\n\n/** A path segment starting with `.` (`.git`, `.env`, `.next`, `.cache`, …) is\n * always ignored — this single rule covers most dot-prefixed VCS/tooling\n * dirs and dotfiles without enumerating them. */\nfunction isIgnored(relPath: string, ignoreSegments: ReadonlySet<string>): boolean {\n for (const segment of relPath.split('/')) {\n if (!segment) continue\n if (segment.startsWith('.')) return true\n if (ignoreSegments.has(segment)) return true\n }\n return false\n}\n\n/** Strips the tree result's echoed `root` prefix so entries are always\n * workspace-relative, whichever convention the structural `fs.tree` uses\n * (root-relative already, or root-prefixed). */\nfunction relativeTo(root: string, path: string): string {\n const prefix = root.endsWith('/') ? root : `${root}/`\n if (path.startsWith(prefix)) return path.slice(prefix.length)\n if (path === root) return ''\n return path\n}\n\nfunction basename(path: string): string {\n const segments = path.split('/').filter(Boolean)\n return segments[segments.length - 1] ?? path\n}\n\nexport function createSandboxFileIndexRoute(\n options: CreateSandboxFileIndexRouteOptions,\n): (request: Request) => Promise<Response> {\n const maxDepth = options.maxDepth ?? 12\n const maxEntries = options.maxEntries ?? 5000\n const cacheTtlSeconds = options.cacheTtlSeconds ?? 20\n const staticIgnore = new Set([...DEFAULT_IGNORE_SEGMENTS, ...(options.ignore ?? [])])\n\n return async function fileIndex(request: Request): Promise<Response> {\n const auth = await options.authorize({ request })\n if (auth.status === 'denied') return auth.response\n if (auth.status === 'warming') {\n return Response.json({ status: 'warming' } satisfies FileIndexWarmingResponse)\n }\n\n const cache = options.cache\n if (cache && auth.cacheKey) {\n const cached = await cache.get(auth.cacheKey)\n if (cached) return Response.json(cached)\n }\n\n const ignoreSegments = auth.ignore?.length\n ? new Set([...staticIgnore, ...auth.ignore])\n : staticIgnore\n\n const scan = await auth.fs.tree(auth.root, { maxDepth })\n const filtered = scan.files.filter((f) => !isIgnored(relativeTo(scan.root, f.path), ignoreSegments))\n const truncated = scan.stats.truncated || filtered.length > maxEntries\n const files: FileMention[] = filtered.slice(0, maxEntries).map((f) => {\n const path = relativeTo(scan.root, f.path)\n const entry: FileMention = { path, name: basename(path) }\n if (typeof f.size === 'number') entry.size = f.size\n return entry\n })\n\n const body: FileIndexReadyResponse = {\n status: 'ready',\n files,\n truncated,\n generatedAt: new Date().toISOString(),\n }\n\n if (cache && auth.cacheKey) await cache.put(auth.cacheKey, body, { ttlSeconds: cacheTtlSeconds })\n\n return Response.json(body)\n }\n}\n"],"mappings":"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","names":[]}