@tangle-network/agent-app 0.45.45 → 0.45.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant/index.js +2 -2
- package/dist/chat-routes/index.js +1 -1
- package/dist/chat-store/index.js +1 -1
- package/dist/{chunk-XZ27ENOZ.js → chunk-XF7ZKISQ.js} +7 -2
- package/dist/chunk-XF7ZKISQ.js.map +1 -0
- package/dist/{chunk-G7BVJ3IH.js → chunk-ZX5UT4HF.js} +75 -18
- package/dist/chunk-ZX5UT4HF.js.map +1 -0
- package/dist/stream/index.js +1 -1
- package/dist/theme/tokens.css +173 -0
- package/dist/web-react/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-G7BVJ3IH.js.map +0 -1
- package/dist/chunk-XZ27ENOZ.js.map +0 -1
package/dist/assistant/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ChatComposer,
|
|
3
3
|
ChatMessages
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-ZX5UT4HF.js";
|
|
5
5
|
import "../chunk-FBVLEGEG.js";
|
|
6
6
|
import "../chunk-54FYYXAX.js";
|
|
7
7
|
import "../chunk-GEYACSFW.js";
|
|
@@ -20,7 +20,7 @@ import "../chunk-CCVG2TL6.js";
|
|
|
20
20
|
import "../chunk-5ZTFZBS6.js";
|
|
21
21
|
import "../chunk-ZVEEWGDK.js";
|
|
22
22
|
import "../chunk-KWXUBMXU.js";
|
|
23
|
-
import "../chunk-
|
|
23
|
+
import "../chunk-XF7ZKISQ.js";
|
|
24
24
|
import "../chunk-M3K2HVQD.js";
|
|
25
25
|
import "../chunk-YJMCRXQQ.js";
|
|
26
26
|
import "../chunk-TH7L265V.js";
|
package/dist/chat-store/index.js
CHANGED
|
@@ -97,10 +97,15 @@ function normalizePersistedPart(rawPart) {
|
|
|
97
97
|
type,
|
|
98
98
|
...id ? { id } : {},
|
|
99
99
|
...asString(rawPart.filename) ? { filename: asString(rawPart.filename) } : {},
|
|
100
|
+
// `name`: the durable attachment display name `promoteAgentFilePart`
|
|
101
|
+
// writes. Coexists with `filename` (legacy raw-harness shape) — kept
|
|
102
|
+
// separate rather than unified so neither producer's shape is lossy.
|
|
103
|
+
...asString(rawPart.name) ? { name: asString(rawPart.name) } : {},
|
|
100
104
|
...asString(rawPart.mediaType) ? { mediaType: asString(rawPart.mediaType) } : {},
|
|
101
105
|
...asString(rawPart.url) ? { url: asString(rawPart.url) } : {},
|
|
102
106
|
...asString(rawPart.path) ? { path: asString(rawPart.path) } : {},
|
|
103
|
-
...type === "file" && asString(rawPart.content) ? { content: asString(rawPart.content) } : {}
|
|
107
|
+
...type === "file" && asString(rawPart.content) ? { content: asString(rawPart.content) } : {},
|
|
108
|
+
...typeof rawPart.size === "number" && Number.isFinite(rawPart.size) ? { size: rawPart.size } : {}
|
|
104
109
|
};
|
|
105
110
|
}
|
|
106
111
|
if (type === "step-start") {
|
|
@@ -380,4 +385,4 @@ export {
|
|
|
380
385
|
terminalizeDanglingAssistantToolUpdates,
|
|
381
386
|
encodeEvent
|
|
382
387
|
};
|
|
383
|
-
//# sourceMappingURL=chunk-
|
|
388
|
+
//# sourceMappingURL=chunk-XF7ZKISQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stream/stream-normalizer.ts"],"sourcesContent":["import {\n canTransitionInteractionStatus,\n persistedPartToInteraction,\n type ChatInteractionStatus,\n} from '../interactions/contract'\nimport {\n canTransitionPlanStatus,\n persistedPartToPlan,\n planPartKey,\n planToPersistedPart,\n type ChatPlanStatus,\n} from '../plans/index'\n\n/** Represent a JSON-compatible object with string keys and values of any type */\nexport type JsonRecord = Record<string, unknown>\n\n/** Define an event object carrying a type and optional JSON data payload */\nexport interface StreamEvent {\n type: string\n data?: JsonRecord\n}\n\n/** Resolve an unknown value to a JsonRecord if it is a non-array object or return undefined */\nexport function asRecord(value: unknown): JsonRecord | undefined {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as JsonRecord\n : undefined\n}\n\n/** Resolve a non-empty string from a value or return undefined */\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\n/** Resolve a unique tool identifier from various possible properties or generate a fallback ID */\nexport function resolveToolId(part: JsonRecord): string {\n return String(\n part.id ??\n part.callID ??\n part.callId ??\n part.toolUseId ??\n part.toolCallId ??\n part.tool ??\n part.name ??\n `tool-${Date.now()}`,\n )\n}\n\n/** Resolve the tool name from a JSON record using tool, name, or a default value */\nexport function resolveToolName(part: JsonRecord): string {\n return String(part.tool ?? part.name ?? 'tool')\n}\n\n/** Resolve time properties from various keys into a normalized record with numeric start and end fields */\nexport function normalizeTime(value: unknown): JsonRecord | undefined {\n const record = asRecord(value)\n if (!record) return undefined\n\n const start = Number(record.start ?? record.startedAt ?? record.started_at)\n const end = Number(record.end ?? record.completedAt ?? record.completed_at)\n if (!Number.isFinite(start) && !Number.isFinite(end)) return undefined\n\n return {\n start: Number.isFinite(start) ? start : undefined,\n end: Number.isFinite(end) ? end : undefined,\n }\n}\n\n/** Normalize tool-related events into a standardized message.part.updated format */\nexport function normalizeToolEvent(event: StreamEvent): StreamEvent {\n if (event.type === 'tool_call' || event.type === 'tool.call') {\n const data = event.data ?? {}\n return {\n type: 'message.part.updated',\n data: {\n part: {\n type: 'tool',\n id: data.id ?? data.callId ?? data.callID ?? data.name,\n tool: data.name ?? data.tool ?? 'tool',\n input: data.arguments ?? data.input,\n status: 'running',\n },\n },\n }\n }\n\n if (event.type === 'tool_result' || event.type === 'tool.result') {\n const data = event.data ?? {}\n const error = asString(data.error)\n return {\n type: 'message.part.updated',\n data: {\n part: {\n type: 'tool',\n id: data.id ?? data.callId ?? data.callID ?? data.name,\n tool: data.name ?? data.tool ?? 'tool',\n output: data.output,\n error,\n status: error ? 'error' : 'completed',\n },\n },\n }\n }\n\n return event\n}\n\n/** Normalize a persisted part object by standardizing its structure and fields */\nexport function normalizePersistedPart(rawPart: JsonRecord): JsonRecord | null {\n const type = String(rawPart.type ?? '')\n\n if (type === 'text') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'text',\n text: asString(rawPart.text) ?? asString(rawPart.content) ?? '',\n // id: per-segment identity from the harness; absent on legacy parts,\n // which collapse to a single keyed segment. Never invented here.\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'reasoning') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'reasoning',\n text: asString(rawPart.text) ?? asString(rawPart.content) ?? '',\n time: normalizeTime(rawPart.time),\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'file' || type === 'image') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type,\n ...(id ? { id } : {}),\n ...(asString(rawPart.filename) ? { filename: asString(rawPart.filename) } : {}),\n // `name`: the durable attachment display name `promoteAgentFilePart`\n // writes. Coexists with `filename` (legacy raw-harness shape) — kept\n // separate rather than unified so neither producer's shape is lossy.\n ...(asString(rawPart.name) ? { name: asString(rawPart.name) } : {}),\n ...(asString(rawPart.mediaType) ? { mediaType: asString(rawPart.mediaType) } : {}),\n ...(asString(rawPart.url) ? { url: asString(rawPart.url) } : {}),\n ...(asString(rawPart.path) ? { path: asString(rawPart.path) } : {}),\n ...(type === 'file' && asString(rawPart.content) ? { content: asString(rawPart.content) } : {}),\n ...(typeof rawPart.size === 'number' && Number.isFinite(rawPart.size) ? { size: rawPart.size } : {}),\n }\n }\n\n if (type === 'step-start') {\n return { type: 'step-start' }\n }\n\n // The harness's per-step usage receipt. Dropping it here silently loses the\n // turn's token/cost accounting from the persisted transcript.\n if (type === 'step-finish') {\n const tokens = asRecord(rawPart.tokens)\n const cost = Number(rawPart.cost)\n return {\n type: 'step-finish',\n ...(asString(rawPart.reason) ? { reason: asString(rawPart.reason) } : {}),\n ...(tokens ? { tokens } : {}),\n ...(Number.isFinite(cost) ? { cost } : {}),\n }\n }\n\n if (type === 'subtask') {\n const id = asString(rawPart.id) ?? asString(rawPart.partId)\n return {\n type: 'subtask',\n prompt: asString(rawPart.prompt) ?? '',\n description: asString(rawPart.description) ?? '',\n agent: asString(rawPart.agent) ?? '',\n ...(id ? { id } : {}),\n }\n }\n\n if (type === 'interaction') {\n return persistedPartToInteraction(rawPart) ? rawPart : null\n }\n\n if (type === 'plan') {\n const plan = persistedPartToPlan(rawPart)\n return plan ? { ...rawPart, ...planToPersistedPart(plan) } : null\n }\n\n // System-authored notices pass through verbatim; `/chat-store` owns their\n // final typed validation before persistence.\n if (type === 'notice') {\n return rawPart\n }\n\n if (type === 'tool') {\n const state = asRecord(rawPart.state)\n const output = state?.output ?? rawPart.output\n const error = asString(state?.error ?? rawPart.error)\n const terminalError =\n state?.status === 'error' ||\n state?.status === 'failed' ||\n rawPart.status === 'error' ||\n rawPart.status === 'failed' ||\n Boolean(error)\n const status =\n state?.status === 'completed' || rawPart.status === 'completed'\n ? 'completed'\n : terminalError\n ? 'error'\n : output !== undefined\n ? 'completed'\n : 'running'\n\n return {\n type: 'tool',\n id: resolveToolId(rawPart),\n tool: resolveToolName(rawPart),\n callID:\n rawPart.callID != null || rawPart.callId != null\n ? String(rawPart.callID ?? rawPart.callId)\n : undefined,\n state: {\n status,\n input: state?.input ?? rawPart.input,\n output,\n error,\n metadata: asRecord(state?.metadata) ?? asRecord(rawPart.metadata),\n time: normalizeTime(state?.time ?? rawPart.time),\n },\n }\n }\n\n return null\n}\n\n/** Stream/transcript part key for a promoted (path-bearing) attachment,\n * keyed on its storage path — re-emitting the same path folds into the same\n * segment instead of duplicating it. */\nexport function attachmentPartKey(path: string): string {\n return `attachment:${path}`\n}\n\n/** Resolve a unique key string for a part based on its type and identifying properties */\nexport function getPartKey(part: JsonRecord): string {\n const type = String(part.type ?? 'unknown')\n if (type === 'tool') {\n return `tool:${resolveToolId(part)}`\n }\n if (type === 'plan') return planPartKey(String(part.planId ?? ''))\n if ((type === 'file' || type === 'image') && asString(part.path)) {\n return attachmentPartKey(String(part.path))\n }\n\n // Keyed by the part's OWN type so distinct kinds never merge into each\n // other. Untyped parts fall back to the text lane (legacy shape).\n const lane = type && type !== 'unknown' ? type : 'text'\n return `${lane}:${String(part.id ?? part.partId ?? part.index ?? 'current')}`\n}\n\n/** Shallow overlay that skips `undefined` incoming values, so a later partial\n * update never erases a field an earlier one captured. */\nfunction overlayDefined(base: JsonRecord, patch: JsonRecord): JsonRecord {\n const out: JsonRecord = { ...base }\n for (const [key, value] of Object.entries(patch)) {\n if (value !== undefined) out[key] = value\n }\n return out\n}\n\n/** Merge incoming JSON with existing persisted data, applying delta for text types when provided */\nexport function mergePersistedPart(existing: JsonRecord | undefined, incoming: JsonRecord, delta?: string): JsonRecord {\n const type = String(incoming.type ?? '')\n if (!existing) {\n if (type === 'text' && delta) {\n return { type: 'text', text: delta }\n }\n return incoming\n }\n\n if (type === 'text' && String(existing.type ?? '') === 'text') {\n const existingText = String(existing.text ?? '')\n const incomingText = String(incoming.text ?? '')\n return {\n ...existing,\n ...incoming,\n // An empty snapshot never erases accumulated text (matches reasoning).\n text: delta ? `${existingText}${delta}` : incomingText || existingText,\n }\n }\n\n if (type === 'reasoning' && String(existing.type ?? '') === 'reasoning') {\n const existingText = String(existing.text ?? '')\n const incomingText = String(incoming.text ?? '')\n return {\n ...existing,\n ...incoming,\n text: delta && incomingText === existingText ? `${existingText}${delta}` : incomingText || existingText,\n time: incoming.time ?? existing.time,\n }\n }\n\n if (type === 'tool' && String(existing.type ?? '') === 'tool') {\n const existingState = asRecord(existing.state) ?? {}\n const incomingState = asRecord(incoming.state) ?? {}\n // Overlay only DEFINED incoming fields: a normalized tool part always\n // carries `output`/`error` keys (undefined when not captured), so a plain\n // spread would clobber a completed tool's output with a later empty update.\n const mergedState = overlayDefined(existingState, incomingState)\n // A partial update with no captured status/output/error normalizes to\n // `running`; never let it downgrade a tool that already settled.\n const existingStatus = String(existingState.status ?? '')\n if (\n (existingStatus === 'completed' || existingStatus === 'error') &&\n String(incomingState.status ?? '') === 'running'\n ) {\n mergedState.status = existingStatus\n }\n return {\n ...overlayDefined(existing, incoming),\n state: mergedState,\n }\n }\n\n if (type === 'interaction' && String(existing.type ?? '') === 'interaction') {\n const merged = overlayDefined(existing, incoming)\n const existingStatus = existing.status as ChatInteractionStatus | undefined\n const incomingStatus = incoming.status as ChatInteractionStatus | undefined\n if (\n existingStatus &&\n incomingStatus &&\n existingStatus !== incomingStatus &&\n !canTransitionInteractionStatus(existingStatus, incomingStatus)\n ) {\n merged.status = existingStatus\n }\n if (incoming.answers === undefined && existing.answers !== undefined) {\n merged.answers = existing.answers\n }\n return merged\n }\n\n if (type === 'plan' && String(existing.type ?? '') === 'plan') {\n const existingRevision = Number(existing.revision)\n const incomingRevision = Number(incoming.revision)\n if (Number.isInteger(existingRevision) && Number.isInteger(incomingRevision)) {\n if (incomingRevision < existingRevision) return existing\n if (incomingRevision > existingRevision) return incoming\n }\n const merged = overlayDefined(existing, incoming)\n const existingStatus = existing.status as ChatPlanStatus | undefined\n const incomingStatus = incoming.status as ChatPlanStatus | undefined\n if (\n existingStatus &&\n incomingStatus &&\n existingStatus !== incomingStatus &&\n !canTransitionPlanStatus(existingStatus, incomingStatus)\n ) {\n merged.status = existingStatus\n }\n return merged\n }\n\n return incoming\n}\n\n/** Resolve errors when a tool fails to report a terminal result before the assistant turn ends */\nexport const MISSING_TOOL_TERMINAL_ERROR = 'Tool did not report a terminal result before the assistant turn completed.'\n/** Provide the reason identifier for a missing tool in the terminal environment */\nexport const MISSING_TOOL_TERMINAL_REASON = 'missing-tool-terminal'\n\n/** Closes a tool part left `running` when a stream ended abnormally: settles\n * it as a terminal `error` and stamps `state.metadata.terminalized` so the\n * synthetic settlement is distinguishable from a real tool failure. Parts\n * that already settled (and non-tool parts) pass through untouched. */\nexport function terminalizeDanglingToolPart(part: JsonRecord): JsonRecord {\n if (String(part.type ?? '') !== 'tool') return part\n\n const state = asRecord(part.state) ?? {}\n if (String(state.status ?? part.status ?? '') !== 'running') return part\n\n const metadata = asRecord(state.metadata) ?? {}\n return {\n ...part,\n state: {\n ...state,\n status: 'error',\n error: asString(state.error ?? part.error) ?? MISSING_TOOL_TERMINAL_ERROR,\n metadata: {\n ...metadata,\n terminalized: true,\n terminalReason: MISSING_TOOL_TERMINAL_REASON,\n },\n },\n }\n}\n\n/** Resolve dangling tool parts into terminal forms within the given JSON records array */\nexport function terminalizeDanglingToolParts(parts: JsonRecord[]): JsonRecord[] {\n return parts.map(terminalizeDanglingToolPart)\n}\n\n/** Settles still-pending interaction parts at persist time. The broker\n * guarantees a resolved question either answered (run unblocked, no cancel\n * event) or cancelled/timed out (cancel event already updated the part), so\n * the success path finalizes remaining pendings as `answered` and the\n * failure/terminalize paths as `expired`. */\nexport function finalizePendingInteractionParts(\n parts: JsonRecord[],\n outcome: Extract<ChatInteractionStatus, 'answered' | 'expired'>,\n): JsonRecord[] {\n return parts.map((part) => {\n if (String(part.type ?? '') !== 'interaction') return part\n if (String(part.status ?? '') !== 'pending') return part\n return { ...part, status: outcome }\n })\n}\n\n/** Collapses text-part artifacts of unstable upstream segment identity: the\n * same text arriving under two keys (id-less delta stream, then an\n * id-bearing snapshot) folds into two segments, and interleaved empty\n * segments survive as blank parts. Consecutive identical text parts merge\n * into one; empty text parts drop when any non-empty text part exists. */\nexport function collapseRedundantTextParts(parts: JsonRecord[]): JsonRecord[] {\n const hasNonEmptyText = parts.some(\n (part) => String(part.type ?? '') === 'text' && String(part.text ?? '').trim().length > 0,\n )\n const collapsed: JsonRecord[] = []\n for (const part of parts) {\n if (String(part.type ?? '') !== 'text') {\n collapsed.push(part)\n continue\n }\n const text = String(part.text ?? '')\n if (hasNonEmptyText && text.trim().length === 0) continue\n const previous = collapsed[collapsed.length - 1]\n if (previous && String(previous.type ?? '') === 'text' && String(previous.text ?? '') === text) continue\n collapsed.push(part)\n }\n return collapsed\n}\n\nfunction assembleAssistantParts(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n const parts = partOrder\n .map((key) => partMap.get(key))\n .filter((part): part is JsonRecord => Boolean(part))\n\n const textParts = parts.filter((part) => String(part.type ?? '') === 'text')\n\n if (textParts.length === 0) {\n if (finalText.trim()) {\n parts.push({ type: 'text', text: finalText })\n }\n return parts\n }\n\n // Id-less text parts form a single logical stream — the final text is\n // authoritative for it.\n if (!textParts.some((part) => asString(part.id))) {\n return parts.map((part) => {\n if (String(part.type ?? '') !== 'text') return part\n return {\n ...part,\n text: finalText || String(part.text ?? ''),\n }\n })\n }\n\n // Per-id text segments: invariant is concat(text parts) === persisted final\n // text, so segment boundaries survive without duplicating the answer into\n // every segment.\n const joined = textParts.map((part) => String(part.text ?? '')).join('')\n if (finalText === joined || finalText.trimEnd() === joined.trimEnd()) {\n return parts\n }\n\n if (finalText.startsWith(joined)) {\n // Final text extends the streamed segments (e.g. a failure diagnostic\n // appended after the stream) — persist the remainder as a trailing\n // id-less segment.\n return [...parts, { type: 'text', text: finalText.slice(joined.length) }]\n }\n\n // Final text replaced the streamed text outright. Keep non-text chronology;\n // collapse text to one authoritative segment at the last text position.\n const lastTextPart = textParts[textParts.length - 1]\n return parts\n .filter((part) => String(part.type ?? '') !== 'text' || part === lastTextPart)\n .map((part) => (part === lastTextPart ? { ...part, text: finalText } : part))\n}\n\n/** Resolve and clean up assistant parts by terminalizing and collapsing redundant segments */\nexport function finalizeAssistantParts(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n // A stream that ended abnormally can leave tool parts `running` — never\n // persist one; collapsing then removes the duplicate/blank text segments an\n // unstable upstream segment identity produced.\n return collapseRedundantTextParts(terminalizeDanglingToolParts(\n assembleAssistantParts(partOrder, partMap, finalText),\n ))\n}\n\n/** The MID-STREAM twin of {@link finalizeAssistantParts}: the same assembled,\n * collapsed projection MINUS the dangling-tool terminalizer.\n *\n * Incremental persistence snapshots the assistant body while the turn is\n * still running, and mid-stream a tool part sitting at `state.status:\n * 'running'` is the NORMAL in-flight state — not the abnormal end\n * {@link terminalizeDanglingToolPart} exists to settle. Running a live\n * snapshot through `finalizeAssistantParts` would persist every in-flight\n * tool call as a failure (`state.status:'error'`, `metadata.terminalized`),\n * so a reader of the durable row would see phantom tool errors that the final\n * write then silently un-does. Terminalization stays a completion-time\n * decision: the final write is the only writer allowed to settle a tool part.\n *\n * Pending `interaction` parts are likewise left `pending` here (the caller\n * skips {@link finalizePendingInteractionParts}) — an ask is genuinely\n * unanswered until the turn settles. */\nexport function draftAssistantParts(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n return collapseRedundantTextParts(assembleAssistantParts(partOrder, partMap, finalText))\n}\n\nfunction partStatus(part: JsonRecord | undefined): string {\n const state = asRecord(part?.state)\n return String(state?.status ?? part?.status ?? '')\n}\n\n/** Finalizes, then folds each synthetic tool settlement back into `partMap`\n * and returns just those updates — the shape a streaming loop needs to emit\n * closing `message.part.updated` frames for tools the stream never settled. */\nexport function terminalizeDanglingAssistantToolUpdates(\n partOrder: string[],\n partMap: Map<string, JsonRecord>,\n finalText: string,\n): JsonRecord[] {\n const finalizedParts = finalizeAssistantParts(partOrder, partMap, finalText)\n const updates: JsonRecord[] = []\n\n for (const part of finalizedParts) {\n if (String(part.type ?? '') !== 'tool') continue\n\n const key = getPartKey(part)\n const existing = partMap.get(key)\n if (partStatus(existing) !== 'running' || partStatus(part) === 'running') continue\n\n partMap.set(key, mergePersistedPart(existing, part))\n updates.push(part)\n }\n\n return updates\n}\n\n/** Encode a StreamEvent object into a Uint8Array using the provided TextEncoder */\nexport function encodeEvent(encoder: TextEncoder, event: StreamEvent): Uint8Array {\n return encoder.encode(`${JSON.stringify(event)}\\n`)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -938,6 +938,12 @@ function WarningGlyph({ className }) {
|
|
|
938
938
|
function iconForMediaType(mediaType) {
|
|
939
939
|
return mediaType?.startsWith("image/") ? ImageGlyph : FileGlyph;
|
|
940
940
|
}
|
|
941
|
+
function attachmentDisplayName(part) {
|
|
942
|
+
if (typeof part.name === "string" && part.name.trim().length > 0) return part.name;
|
|
943
|
+
const base = part.path.split("/").pop() ?? "";
|
|
944
|
+
const trimmed = base.trim();
|
|
945
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
946
|
+
}
|
|
941
947
|
var attachmentFileCache = /* @__PURE__ */ new Map();
|
|
942
948
|
function __resetAttachmentFileCacheForTests() {
|
|
943
949
|
attachmentFileCache.clear();
|
|
@@ -998,10 +1004,38 @@ function AttachmentThumbnailError({ name }) {
|
|
|
998
1004
|
/* @__PURE__ */ jsx6("span", { className: "line-clamp-2 text-[10px] leading-tight", children: name })
|
|
999
1005
|
] });
|
|
1000
1006
|
}
|
|
1007
|
+
function AttachmentUnavailable({ shape }) {
|
|
1008
|
+
if (shape === "thumbnail") {
|
|
1009
|
+
return /* @__PURE__ */ jsxs4(
|
|
1010
|
+
"span",
|
|
1011
|
+
{
|
|
1012
|
+
"aria-disabled": "true",
|
|
1013
|
+
className: "inline-flex h-16 w-16 shrink-0 flex-col items-center justify-center gap-1 rounded-md border border-border bg-muted px-1 text-center text-muted-foreground",
|
|
1014
|
+
children: [
|
|
1015
|
+
/* @__PURE__ */ jsx6(WarningGlyph, { className: "h-4 w-4 shrink-0" }),
|
|
1016
|
+
/* @__PURE__ */ jsx6("span", { className: "line-clamp-2 text-[10px] leading-tight", children: "Attachment unavailable" })
|
|
1017
|
+
]
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
return /* @__PURE__ */ jsxs4(
|
|
1022
|
+
"span",
|
|
1023
|
+
{
|
|
1024
|
+
"aria-disabled": "true",
|
|
1025
|
+
className: "inline-flex items-center gap-1 rounded-md border border-border bg-muted px-2 py-0.5 text-[11px] text-muted-foreground",
|
|
1026
|
+
children: [
|
|
1027
|
+
/* @__PURE__ */ jsx6(WarningGlyph, { className: "h-3 w-3 shrink-0" }),
|
|
1028
|
+
"Attachment unavailable"
|
|
1029
|
+
]
|
|
1030
|
+
}
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1001
1033
|
function AttachmentThumbnail({ part, resolveFileUrl, fetchFile }) {
|
|
1002
1034
|
const url = resolveFileUrl(part);
|
|
1035
|
+
const displayName = attachmentDisplayName(part);
|
|
1003
1036
|
const [result, setResult] = useState5(null);
|
|
1004
1037
|
useEffect4(() => {
|
|
1038
|
+
if (!displayName) return;
|
|
1005
1039
|
let cancelled = false;
|
|
1006
1040
|
setResult(null);
|
|
1007
1041
|
loadAttachmentFile(url, fetchFile).then((next) => {
|
|
@@ -1010,34 +1044,38 @@ function AttachmentThumbnail({ part, resolveFileUrl, fetchFile }) {
|
|
|
1010
1044
|
return () => {
|
|
1011
1045
|
cancelled = true;
|
|
1012
1046
|
};
|
|
1013
|
-
}, [url, fetchFile]);
|
|
1047
|
+
}, [url, fetchFile, displayName]);
|
|
1014
1048
|
const objectUrl = useAttachmentObjectUrl(result?.ok ? result.blob : void 0);
|
|
1015
1049
|
const handleClick = useCallback(() => {
|
|
1016
1050
|
if (!objectUrl) return;
|
|
1017
1051
|
window.open(objectUrl, "_blank", "noopener");
|
|
1018
1052
|
}, [objectUrl]);
|
|
1053
|
+
if (!displayName) {
|
|
1054
|
+
return /* @__PURE__ */ jsx6(AttachmentUnavailable, { shape: "thumbnail" });
|
|
1055
|
+
}
|
|
1019
1056
|
if (!result) {
|
|
1020
1057
|
return /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", className: "inline-block h-16 w-16 shrink-0 animate-pulse rounded-md bg-muted" });
|
|
1021
1058
|
}
|
|
1022
1059
|
if (!result.ok || !objectUrl) {
|
|
1023
|
-
return /* @__PURE__ */ jsx6(AttachmentThumbnailError, { name:
|
|
1060
|
+
return /* @__PURE__ */ jsx6(AttachmentThumbnailError, { name: displayName });
|
|
1024
1061
|
}
|
|
1025
1062
|
return /* @__PURE__ */ jsx6(
|
|
1026
1063
|
"button",
|
|
1027
1064
|
{
|
|
1028
1065
|
type: "button",
|
|
1029
1066
|
onClick: handleClick,
|
|
1030
|
-
"aria-label": `Open ${
|
|
1067
|
+
"aria-label": `Open ${displayName}`,
|
|
1031
1068
|
className: "h-16 w-16 shrink-0 overflow-hidden rounded-md border border-border",
|
|
1032
|
-
children: /* @__PURE__ */ jsx6("img", { src: objectUrl, alt:
|
|
1069
|
+
children: /* @__PURE__ */ jsx6("img", { src: objectUrl, alt: displayName, className: "h-16 w-16 object-cover" })
|
|
1033
1070
|
}
|
|
1034
1071
|
);
|
|
1035
1072
|
}
|
|
1036
1073
|
function AttachmentChip({ part, resolveFileUrl, fetchFile }) {
|
|
1074
|
+
const displayName = attachmentDisplayName(part);
|
|
1037
1075
|
const [status, setStatus] = useState5("idle");
|
|
1038
1076
|
const [errorMessage, setErrorMessage] = useState5(null);
|
|
1039
1077
|
const handleClick = useCallback(() => {
|
|
1040
|
-
if (status === "loading") return;
|
|
1078
|
+
if (!displayName || status === "loading") return;
|
|
1041
1079
|
setStatus("loading");
|
|
1042
1080
|
setErrorMessage(null);
|
|
1043
1081
|
const url = resolveFileUrl(part);
|
|
@@ -1047,7 +1085,7 @@ function AttachmentChip({ part, resolveFileUrl, fetchFile }) {
|
|
|
1047
1085
|
setErrorMessage(result.message);
|
|
1048
1086
|
return;
|
|
1049
1087
|
}
|
|
1050
|
-
const download = triggerAttachmentDownload(
|
|
1088
|
+
const download = triggerAttachmentDownload(displayName, result.blob);
|
|
1051
1089
|
if (!download.ok) {
|
|
1052
1090
|
setStatus("error");
|
|
1053
1091
|
setErrorMessage(download.message);
|
|
@@ -1055,7 +1093,10 @@ function AttachmentChip({ part, resolveFileUrl, fetchFile }) {
|
|
|
1055
1093
|
}
|
|
1056
1094
|
setStatus("idle");
|
|
1057
1095
|
});
|
|
1058
|
-
}, [status, resolveFileUrl, part, fetchFile]);
|
|
1096
|
+
}, [displayName, status, resolveFileUrl, part, fetchFile]);
|
|
1097
|
+
if (!displayName) {
|
|
1098
|
+
return /* @__PURE__ */ jsx6(AttachmentUnavailable, { shape: "chip" });
|
|
1099
|
+
}
|
|
1059
1100
|
const Icon = status === "error" ? WarningGlyph : iconForMediaType(part.mediaType);
|
|
1060
1101
|
const className = [
|
|
1061
1102
|
"inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-[11px]",
|
|
@@ -1070,7 +1111,7 @@ function AttachmentChip({ part, resolveFileUrl, fetchFile }) {
|
|
|
1070
1111
|
className,
|
|
1071
1112
|
children: [
|
|
1072
1113
|
/* @__PURE__ */ jsx6(Icon, { className: "h-3 w-3 shrink-0" }),
|
|
1073
|
-
|
|
1114
|
+
displayName,
|
|
1074
1115
|
typeof part.size === "number" && /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground/70", children: [
|
|
1075
1116
|
"\xB7 ",
|
|
1076
1117
|
formatBytes(part.size)
|
|
@@ -5499,7 +5540,8 @@ function StreamingCaret() {
|
|
|
5499
5540
|
return /* @__PURE__ */ jsx13(
|
|
5500
5541
|
"span",
|
|
5501
5542
|
{
|
|
5502
|
-
className: "ml-0.5 inline-block h-[1.1em] w-[3px] translate-y-[2px] animate-
|
|
5543
|
+
className: "ml-0.5 inline-block h-[1.1em] w-[3px] translate-y-[2px] animate-[agent-caret_1s_step-end_infinite] rounded-sm bg-foreground/70",
|
|
5544
|
+
"data-motion": "essential",
|
|
5503
5545
|
"aria-hidden": true
|
|
5504
5546
|
}
|
|
5505
5547
|
);
|
|
@@ -5514,10 +5556,17 @@ function SegmentText({
|
|
|
5514
5556
|
const text = useSmoothText(content, streaming);
|
|
5515
5557
|
const body = useMemo7(() => renderBody(text), [renderBody, text]);
|
|
5516
5558
|
if (!content.trim() && !showCaret) return null;
|
|
5517
|
-
return
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5559
|
+
return (
|
|
5560
|
+
// A settled run arrives from a short blur; the LIVE run does not, because
|
|
5561
|
+
// its text is already being revealed character by character and animating
|
|
5562
|
+
// the container on top of that makes the paragraph shimmer while it types.
|
|
5563
|
+
// The distinction is what separates "the answer materialised" from "the
|
|
5564
|
+
// log was appended to".
|
|
5565
|
+
/* @__PURE__ */ jsxs11("div", { className: `${messageClassName}${streaming ? "" : " agent-stream-in"}`, children: [
|
|
5566
|
+
body,
|
|
5567
|
+
showCaret && /* @__PURE__ */ jsx13(StreamingCaret, {})
|
|
5568
|
+
] })
|
|
5569
|
+
);
|
|
5521
5570
|
}
|
|
5522
5571
|
var COLLAPSE_TOOL_RUN_AT = 3;
|
|
5523
5572
|
function isImportantTool(call) {
|
|
@@ -5681,10 +5730,18 @@ function AssistantMessageImpl({
|
|
|
5681
5730
|
formatModelCost(msg, models) && /* @__PURE__ */ jsx13("span", { children: formatModelCost(msg, models) })
|
|
5682
5731
|
] }),
|
|
5683
5732
|
reasoning && /* @__PURE__ */ jsxs11("details", { className: "mb-2 rounded-lg border-l-2 border-border bg-secondary px-3 py-2", open: !hasAnswerText, children: [
|
|
5684
|
-
/* @__PURE__ */ jsx13("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: !hasAnswerText ?
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5733
|
+
/* @__PURE__ */ jsx13("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: !hasAnswerText ? (
|
|
5734
|
+
// A pulse dims the whole word on a loop, which is the same cue a
|
|
5735
|
+
// skeleton placeholder uses — it reads as "nothing here yet". A
|
|
5736
|
+
// sweep travels THROUGH the glyphs, which reads as work in
|
|
5737
|
+
// flight, and the elapsed seconds say how much. `essential`
|
|
5738
|
+
// because it is the only signal separating a working agent from
|
|
5739
|
+
// a stuck one, and reduced-motion still collapses its duration.
|
|
5740
|
+
/* @__PURE__ */ jsxs11("span", { className: "agent-shimmer", "data-motion": "essential", children: [
|
|
5741
|
+
"Thinking",
|
|
5742
|
+
thinkingSeconds >= 1 ? ` \xB7 ${thinkingSeconds}s` : "\u2026"
|
|
5743
|
+
] })
|
|
5744
|
+
) : thinkMsRef.current != null ? `Thought for ${Math.max(1, Math.round(thinkMsRef.current / 1e3))}s` : "Thought process" }),
|
|
5688
5745
|
/* @__PURE__ */ jsx13("div", { ref: reasoningScrollRef, className: "mt-2 max-h-48 overflow-y-auto whitespace-pre-wrap text-[13px] leading-relaxed text-muted-foreground", children: reasoning })
|
|
5689
5746
|
] }),
|
|
5690
5747
|
segments && segments.length > 0 ? /* @__PURE__ */ jsx13(
|
|
@@ -5990,4 +6047,4 @@ export {
|
|
|
5990
6047
|
useThinkingSeconds,
|
|
5991
6048
|
ChatMessages
|
|
5992
6049
|
};
|
|
5993
|
-
//# sourceMappingURL=chunk-
|
|
6050
|
+
//# sourceMappingURL=chunk-ZX5UT4HF.js.map
|