@athenaintel/react 0.42.1 → 0.42.2
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/README.md +36 -7
- package/dist/agent-runtime/src/index.d.ts +2 -2
- package/dist/agent-runtime/src/messages.cjs +38 -7
- package/dist/agent-runtime/src/messages.cjs.map +1 -1
- package/dist/agent-runtime/src/messages.d.ts +26 -8
- package/dist/agent-runtime/src/messages.js +38 -7
- package/dist/agent-runtime/src/messages.js.map +1 -1
- package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs +10 -0
- package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
- package/dist/agent-runtime/src/quota-safe-local-storage-session.d.ts +7 -0
- package/dist/agent-runtime/src/quota-safe-local-storage-session.js +10 -0
- package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
- package/dist/agent-runtime/src/session-lane-scope.cjs +25 -0
- package/dist/agent-runtime/src/session-lane-scope.cjs.map +1 -0
- package/dist/agent-runtime/src/session-lane-scope.d.ts +59 -0
- package/dist/agent-runtime/src/session-lane-scope.js +25 -0
- package/dist/agent-runtime/src/session-lane-scope.js.map +1 -0
- package/dist/agent-runtime/src/subagent-transcripts.cjs +2 -2
- package/dist/agent-runtime/src/subagent-transcripts.cjs.map +1 -1
- package/dist/agent-runtime/src/subagent-transcripts.d.ts +2 -2
- package/dist/agent-runtime/src/subagent-transcripts.js +3 -3
- package/dist/agent-runtime/src/subagent-transcripts.js.map +1 -1
- package/dist/agent-runtime/src/tool-names.cjs +4 -0
- package/dist/agent-runtime/src/tool-names.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-names.d.ts +7 -0
- package/dist/agent-runtime/src/tool-names.js +4 -0
- package/dist/agent-runtime/src/tool-names.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +18 -1
- package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-runtime.d.ts +19 -10
- package/dist/agent-runtime/src/use-deep-agent-runtime.js +19 -2
- package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs +28 -10
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.d.ts +35 -3
- package/dist/agent-runtime/src/use-deep-agent-thread.js +28 -10
- package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-voice.cjs +20 -0
- package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-voice.js +20 -0
- package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -1
- package/dist/agent-runtime/src/voice-presentation.cjs +2 -0
- package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -1
- package/dist/agent-runtime/src/voice-presentation.js +2 -0
- package/dist/agent-runtime/src/voice-presentation.js.map +1 -1
- package/dist/catalog/runtime-catalog.js +1 -1
- package/dist/chat/AthenaChat.cjs +125 -103
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +9 -0
- package/dist/chat/AthenaChat.js +126 -104
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/chat-voice-controls.cjs +128 -0
- package/dist/chat/chat-voice-controls.cjs.map +1 -0
- package/dist/chat/chat-voice-controls.d.ts +19 -0
- package/dist/chat/chat-voice-controls.js +128 -0
- package/dist/chat/chat-voice-controls.js.map +1 -0
- package/dist/chat/use-statewire-approval-request.js +1 -1
- package/dist/chat-ui/composer/tiptap-composer.cjs +42 -22
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.d.ts +1 -0
- package/dist/chat-ui/composer/tiptap-composer.js +43 -23
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +2 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +3 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.js +2 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
- package/dist/chat-ui/mentions/use-account-workspace-id.cjs +29 -51
- package/dist/chat-ui/mentions/use-account-workspace-id.cjs.map +1 -1
- package/dist/chat-ui/mentions/use-account-workspace-id.d.ts +2 -2
- package/dist/chat-ui/mentions/use-account-workspace-id.js +30 -52
- package/dist/chat-ui/mentions/use-account-workspace-id.js.map +1 -1
- package/dist/chat-ui/thread/composer-send-or-stop.cjs +2 -1
- package/dist/chat-ui/thread/composer-send-or-stop.cjs.map +1 -1
- package/dist/chat-ui/thread/composer-send-or-stop.d.ts +2 -0
- package/dist/chat-ui/thread/composer-send-or-stop.js +2 -1
- package/dist/chat-ui/thread/composer-send-or-stop.js.map +1 -1
- package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
- package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
- package/dist/diagnostics/athena-diagnostics.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/runtime/statewire-session-key.cjs.map +1 -1
- package/dist/runtime/statewire-session-key.d.ts +8 -5
- package/dist/runtime/statewire-session-key.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +5 -2
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +5 -2
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/voice/use-chat-voice-enabled.cjs +43 -0
- package/dist/voice/use-chat-voice-enabled.cjs.map +1 -0
- package/dist/voice/use-chat-voice-enabled.d.ts +9 -0
- package/dist/voice/use-chat-voice-enabled.js +43 -0
- package/dist/voice/use-chat-voice-enabled.js.map +1 -0
- package/package.json +4 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent-transcripts.cjs","sources":["../../../../agent-runtime/src/subagent-transcripts.ts"],"sourcesContent":["/**\n * A subagent's transcript as assistant-ui's native nested thread.\n *\n * The backend nests a `task` subagent's transcript in its card's ToolMessage,\n * at `artifact.subgraph_state.messages`: the newest steps on the running\n * placeholder, the whole transcript on the real result. The conversion cache\n * built here converts that transcript through the thread's own pipeline and\n * sets it as the `task` tool-call part's `messages`, assistant-ui's channel for\n * a tool call's nested conversation. `ReadonlyThreadProvider` (the provider\n * under `MessagePartPrimitive.Messages`) renders it as a read-only thread under\n * the parent's tool UIs, and `thread.tasks` derives child work from it.\n *\n * Hosts opt in (`useDeepAgentThread({ createConversionCache })`). It builds on\n * the shared conversion memory from outside, so the shared converter, which\n * ships in every surface's startup bundle (the Office pane's startup and total\n * budgets have no room to spare), stays as it is, and a surface that draws no\n * subagent transcript pays nothing for it.\n */\nimport type {\n ThreadAssistantMessagePart,\n ThreadMessage,\n ToolCallMessagePart,\n} from '@assistant-ui/core';\nimport {\n convertExternalMessages,\n createExternalMessageConversionCache,\n} from '@assistant-ui/core/react';\nimport type { LangChainMessage } from '@assistant-ui/react-langgraph';\nimport { DEEP_AGENT_REPEATS_RESULT_KEY } from './generated/runconfig-contract';\nimport { getMessageText } from './message-text';\nimport {\n convertDeepAgentLangChainMessage,\n createDeepAgentMessageConversionCache,\n type DeepAgentConversionMemory,\n markPreliminaryToolResults,\n sanitizeLangChainMessages,\n} from './messages';\nimport { TASK_TOOL_NAME } from './tool-names';\n\n/** The transcript nested on a `task` card's artifact, or `undefined` when it carries none. */\nfunction laneTranscriptOf(artifact: unknown): readonly LangChainMessage[] | undefined {\n if (typeof artifact !== 'object' || artifact === null) return undefined;\n const state: unknown = (artifact as { subgraph_state?: unknown }).subgraph_state;\n if (typeof state !== 'object' || state === null) return undefined;\n const messages: unknown = (state as { messages?: unknown }).messages;\n return Array.isArray(messages) ? (messages as LangChainMessage[]) : undefined;\n}\n\nconst restoredTranscripts = new WeakMap<\n readonly LangChainMessage[],\n { readonly content: unknown; readonly transcript: readonly LangChainMessage[] }\n>();\n\n/**\n * A card's nested transcript with its report restored. On a cut finished card\n * the last step can ride with empty content and `DEEP_AGENT_REPEATS_RESULT_KEY`:\n * its text is the subagent's report, the card's own `content`, which the card\n * carries anyway. The step gets that content back. Any other transcript comes\n * back as it is. Memoized per transcript, so a lane's conversion sees one array.\n */\nexport function withRepeatedResult(\n transcript: readonly LangChainMessage[],\n content: unknown\n): readonly LangChainMessage[] {\n const last = transcript[transcript.length - 1];\n if (\n last === undefined ||\n (last as Record<string, unknown>)[DEEP_AGENT_REPEATS_RESULT_KEY] !== true ||\n (typeof content !== 'string' && !Array.isArray(content))\n ) {\n return transcript;\n }\n const cached = restoredTranscripts.get(transcript);\n if (cached !== undefined && cached.content === content) return cached.transcript;\n const restored = [...transcript.slice(0, -1), { ...last, content } as LangChainMessage];\n restoredTranscripts.set(transcript, { content, transcript: restored });\n return restored;\n}\n\ntype AssistantLangChainMessage = Extract<LangChainMessage, { type: 'ai' }>;\n\nconst convertibleChunkTurns = new WeakMap<LangChainMessage, LangChainMessage>();\n\n/**\n * A nested assistant turn stored under the chunk class name (the legacy\n * assistant-stream fold keeps `AIMessageChunk`) as the `ai` message it is: the\n * shipped converter maps only `ai`, and would drop the turn.\n */\nfunction asConvertibleTurn(message: LangChainMessage): LangChainMessage {\n const type: string = message.type;\n if (type !== 'AIMessageChunk') return message;\n const cached = convertibleChunkTurns.get(message);\n if (cached !== undefined) return cached;\n const turn: LangChainMessage = { ...(message as AssistantLangChainMessage), type: 'ai' };\n convertibleChunkTurns.set(message, turn);\n return turn;\n}\n\n/** A lane step as the assistant turn it is when it calls nothing; `null` for any other step. */\nfunction answeringTurn(step: LangChainMessage): AssistantLangChainMessage | null {\n const turn = asConvertibleTurn(step);\n return turn.type === 'ai' && (turn.tool_calls?.length ?? 0) === 0 ? turn : null;\n}\n\nconst isTextBlock = (block: unknown): block is { type: 'text'; text?: unknown } =>\n typeof block === 'object' && block !== null && 'type' in block && block.type === 'text';\n\n/** The text a step's content shows: the string, or its text blocks joined. */\nfunction shownText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n let text = '';\n for (const block of content) {\n if (isTextBlock(block) && typeof block.text === 'string') text += block.text;\n }\n return text;\n}\n\n/** A turn that calls nothing and shows nothing: no text, and no block of another kind. */\nfunction blankTurn(step: LangChainMessage): AssistantLangChainMessage | null {\n const turn = answeringTurn(step);\n if (turn === null || shownText(turn.content).trim() !== '') return null;\n return typeof turn.content === 'string' || turn.content.every(isTextBlock) ? turn : null;\n}\n\nconst reportedLanes = new WeakMap<\n readonly LangChainMessage[],\n { readonly report: string; readonly transcript: readonly LangChainMessage[] }\n>();\n\n/**\n * A finished lane's transcript, closed by its report. A `task` card's content\n * is its subagent's report, the text of the lane's last turn. A transcript\n * that ends on that turn (an assistant step that calls nothing and says\n * something, which {@link withRepeatedResult} restores when it rides marked)\n * comes back as it is, even when blank steps follow it. A lane stored without\n * it shows the report as its closing answer: a lane whose last step is a blank\n * assistant step (lanes that finished before the host marked a repeated\n * report) gets the report in that step, its id and stamps kept, and a lane\n * that ends on a tool result or a turn that calls tools gets a closing step\n * after its last one. An empty report or transcript changes nothing. For\n * display only: the wire and the stored lane are untouched. Memoized per\n * transcript, so a lane's conversion sees one array.\n */\nexport function withLaneReport(\n transcript: readonly LangChainMessage[],\n report: string | undefined\n): readonly LangChainMessage[] {\n if (report === undefined || report.trim() === '' || transcript.length === 0) return transcript;\n // The lane's closing turn: its last step past any blank ones.\n let at = transcript.length - 1;\n let closing = transcript[at];\n while (closing !== undefined && blankTurn(closing) !== null) {\n at -= 1;\n closing = transcript[at];\n }\n const answered =\n closing !== undefined &&\n answeringTurn(closing) !== null &&\n shownText(closing.content).trim() !== '';\n if (answered) return transcript;\n const cached = reportedLanes.get(transcript);\n if (cached !== undefined && cached.report === report) return cached.transcript;\n const last = transcript[transcript.length - 1];\n const blank = last === undefined ? null : blankTurn(last);\n const closed: readonly LangChainMessage[] =\n blank !== null\n ? [...transcript.slice(0, -1), { ...blank, content: report }]\n : [...transcript, { id: `${last?.id ?? 'lane'}:report`, type: 'ai', content: report }];\n reportedLanes.set(transcript, { report, transcript: closed });\n return closed;\n}\n\n/** A nested transcript as the thread's own pipeline reads it (see {@link sanitizeLangChainMessages}). */\nfunction transcriptSources(transcript: readonly LangChainMessage[]): LangChainMessage[] {\n return sanitizeLangChainMessages(transcript.map(asConvertibleTurn));\n}\n\nconst reportedTranscripts = new WeakSet<readonly LangChainMessage[]>();\n\n/**\n * A nested transcript the shipped converter refused (it throws on, e.g., a\n * result whose tool name differs from its call's): reported once, and its card\n * goes without a nested thread instead of taking the whole thread down.\n */\nfunction reportUnconvertibleTranscript(\n transcript: readonly LangChainMessage[],\n error: unknown\n): void {\n if (reportedTranscripts.has(transcript)) return;\n reportedTranscripts.add(transcript);\n console.warn('[agent-runtime] a subagent transcript could not be converted', error);\n}\n\n/** Whether two values the wire carries (JSON: no functions, no cycles) are equal. */\nfunction sameJson(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((item, index) => sameJson(item, b[index]))\n );\n }\n const left = a as Record<string, unknown>;\n const right = b as Record<string, unknown>;\n const keys = Object.keys(left);\n return (\n keys.length === Object.keys(right).length &&\n keys.every(\n (key) =>\n Object.prototype.hasOwnProperty.call(right, key) && sameJson(left[key], right[key])\n )\n );\n}\n\n/**\n * Keeps a lane's unchanged steps as the objects it already converted. The host\n * replaces a card's whole document whenever the card changes, so every step it\n * carries arrives as a new object, and the conversion memory, which tells the\n * steps a turn continues by object, would see a new turn each time the card's\n * cut drops its oldest step and the turn's first step changes. A step whose id\n * and content are unchanged is the object held for that id. O(the steps' size).\n */\nfunction steadyStepObjects(): (\n transcript: readonly LangChainMessage[]\n) => readonly LangChainMessage[] {\n let held = new Map<string, LangChainMessage>();\n return (transcript) => {\n const next = new Map<string, LangChainMessage>();\n let kept = false;\n const steadied = transcript.map((entry) => {\n const id = entry.id;\n if (typeof id !== 'string') return entry;\n const before = held.get(id);\n const step = before !== undefined && sameJson(before, entry) ? before : entry;\n next.set(id, step);\n if (step !== entry) kept = true;\n return step;\n });\n held = next;\n return kept ? steadied : transcript;\n };\n}\n\n/** One lane's transcript, converted: `undefined` when the converter refused it. */\nexport type SubagentLaneConversion = (\n transcript: readonly LangChainMessage[],\n running: boolean\n) => readonly ThreadMessage[] | undefined;\n\n/**\n * One `task` lane's own conversion memory, for whatever converts that lane:\n * its unchanged steps keep their converted objects while it streams (a step the\n * wire sends again unchanged included, see {@link steadyStepObjects}), and its\n * turn keeps its row key (`deepAgentRowKey`) and part keys when the turn's head\n * moves — the oldest steps of a cut card falling off as the lane runs, or older\n * steps a transcript drawer pages in. A surface that keeps its disclosures under\n * that key keeps them open through both. A transcript array converted last time\n * is not converted again.\n */\nexport function createSubagentLaneConversion(): SubagentLaneConversion {\n const memory = createSubagentTranscriptConversionCache();\n const steady = steadyStepObjects();\n let last: {\n readonly transcript: readonly LangChainMessage[];\n readonly running: boolean;\n readonly messages: readonly ThreadMessage[] | undefined;\n } | null = null;\n return (transcript, running) => {\n if (last !== null && last.transcript === transcript && last.running === running) {\n return last.messages;\n }\n let messages: readonly ThreadMessage[] | undefined;\n try {\n messages = memory.convert(transcriptSources(steady(transcript)), running);\n } catch (error) {\n reportUnconvertibleTranscript(transcript, error);\n }\n last = { transcript, running, messages };\n return messages;\n };\n}\n\n/**\n * Sets each `task` part of a pass's converted rows to carry its nested\n * transcript as `messages`, for one attach. Each lane has its own conversion\n * memory, so its unchanged steps keep their converted objects while it\n * streams, as the thread's own messages do, and a lane whose transcript array\n * did not change is not converted again. A row, and a part whose lane did not\n * change, keep their identity: the same row always comes back as the same\n * object. A lane's memory lives only while a row of the pass holds its `task`\n * call: a lane a rewind removes, a branch switch hides or the window leaves\n * behind is let go with its transcript, and converted afresh if it returns.\n */\nfunction subagentTranscriptAttacher(): (rows: readonly ThreadMessage[]) => ThreadMessage[] {\n const lanes = new Map<string, SubagentLaneConversion>();\n const attachedRows = new WeakMap<ThreadMessage, ThreadMessage>();\n const attachedParts = new WeakMap<\n ToolCallMessagePart,\n { readonly messages: readonly ThreadMessage[]; readonly part: ToolCallMessagePart }\n >();\n\n const laneMessages = (\n toolCallId: string,\n transcript: readonly LangChainMessage[],\n running: boolean\n ): readonly ThreadMessage[] | undefined => {\n let lane = lanes.get(toolCallId);\n if (lane === undefined) {\n lane = createSubagentLaneConversion();\n lanes.set(toolCallId, lane);\n }\n return lane(transcript, running);\n };\n\n const attach = (row: ThreadMessage): ThreadMessage => {\n if (row.role !== 'assistant') return row;\n const cached = attachedRows.get(row);\n if (cached !== undefined) return cached;\n let content: ThreadAssistantMessagePart[] | null = null;\n for (const [index, part] of row.content.entries()) {\n if (part.type !== 'tool-call' || part.toolName !== TASK_TOOL_NAME) continue;\n const nested = laneTranscriptOf(part.artifact);\n if (nested === undefined) continue;\n // A running lane's placeholder is preliminary: its newest nested turn streams.\n const running = part.isPreliminary === true;\n const restored = withRepeatedResult(nested, part.result);\n const transcript =\n running || part.isError === true\n ? restored\n : withLaneReport(restored, getMessageText({ content: part.result }));\n const messages = laneMessages(part.toolCallId, transcript, running);\n if (messages === undefined) continue;\n const kept = attachedParts.get(part);\n const attached =\n kept !== undefined && kept.messages === messages ? kept.part : { ...part, messages };\n attachedParts.set(part, { messages, part: attached });\n content ??= [...row.content];\n content[index] = attached;\n }\n const attachedRow: ThreadMessage = content === null ? row : { ...row, content };\n attachedRows.set(row, attachedRow);\n return attachedRow;\n };\n\n return (rows) => {\n const attached = rows.map(attach);\n if (lanes.size === 0) return attached;\n const held = new Set<string>();\n for (const row of rows) {\n if (row.role !== 'assistant') continue;\n for (const part of row.content) {\n if (part.type !== 'tool-call' || part.toolName !== TASK_TOOL_NAME) continue;\n held.add(part.toolCallId);\n }\n }\n for (const toolCallId of lanes.keys()) {\n if (!held.has(toolCallId)) lanes.delete(toolCallId);\n }\n return attached;\n };\n}\n\n/**\n * A conversion memory for one attach (see\n * `useDeepAgentThread({ createConversionCache })`): the shared one's rows, with\n * each `task` tool-call part carrying its subagent's converted transcript as\n * `messages`. A pass that changes no row hands back the previous list itself.\n */\nexport function createSubagentTranscriptConversionCache(): DeepAgentConversionMemory {\n const thread = createDeepAgentMessageConversionCache();\n const attach = subagentTranscriptAttacher();\n let previousRows: ThreadMessage[] | null = null;\n let previous: ThreadMessage[] = [];\n return {\n convert(messages, isRunning) {\n const rows = thread.convert(messages, isRunning);\n if (rows === previousRows) return previous;\n previousRows = rows;\n const next = attach(rows);\n const unchanged =\n next.length === previous.length && next.every((row, index) => row === previous[index]);\n if (!unchanged) previous = next;\n return previous;\n },\n };\n}\n\n/**\n * Shared by every in-eval transcript: entries are keyed by source message, so\n * none outlives the wire state holding it and threads cannot collide.\n */\nconst inEvalTranscriptCache = createExternalMessageConversionCache();\nconst IN_EVAL_TRANSCRIPT_METADATA = {};\nconst inEvalTranscripts = new WeakMap<\n readonly LangChainMessage[],\n { readonly running: boolean; readonly messages: readonly ThreadMessage[] }\n>();\n\nconst NO_MESSAGES: readonly ThreadMessage[] = [];\n\n/**\n * An in-eval dispatch's live transcript (a `ptc_subagents` entry's `messages`)\n * as a nested thread, converted like a `task` lane's. Memoized per transcript\n * array; a step the lane did not change keeps its converted object. A\n * transcript the converter refuses reads as empty.\n */\nexport function convertSubagentTranscript(\n transcript: readonly LangChainMessage[],\n running: boolean\n): readonly ThreadMessage[] {\n const cached = inEvalTranscripts.get(transcript);\n if (cached !== undefined && cached.running === running) return cached.messages;\n let messages: readonly ThreadMessage[] = NO_MESSAGES;\n try {\n messages = convertExternalMessages(\n transcriptSources(transcript),\n convertDeepAgentLangChainMessage,\n running,\n IN_EVAL_TRANSCRIPT_METADATA,\n inEvalTranscriptCache\n ).map(markPreliminaryToolResults);\n } catch (error) {\n reportUnconvertibleTranscript(transcript, error);\n }\n inEvalTranscripts.set(transcript, { running, messages });\n return messages;\n}\n"],"names":["messages","DEEP_AGENT_REPEATS_RESULT_KEY","sanitizeLangChainMessages","TASK_TOOL_NAME","getMessageText","createDeepAgentMessageConversionCache","createExternalMessageConversionCache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"subagent-transcripts.cjs","sources":["../../../../agent-runtime/src/subagent-transcripts.ts"],"sourcesContent":["/**\n * A subagent's transcript as assistant-ui's native nested thread.\n *\n * The backend nests a `task` subagent's transcript in its card's ToolMessage,\n * at `artifact.subgraph_state.messages`: the newest steps on the running\n * placeholder, the whole transcript on the real result. The conversion cache\n * built here converts that transcript through the thread's own pipeline and\n * sets it as the `task` tool-call part's `messages`, assistant-ui's channel for\n * a tool call's nested conversation. `ReadonlyThreadProvider` (the provider\n * under `MessagePartPrimitive.Messages`) renders it as a read-only thread under\n * the parent's tool UIs, and `thread.tasks` derives child work from it.\n *\n * Hosts opt in (`useDeepAgentThread({ createConversionCache })`). It builds on\n * the shared conversion memory from outside, so the shared converter, which\n * ships in every surface's startup bundle (the Office pane's startup and total\n * budgets have no room to spare), stays as it is, and a surface that draws no\n * subagent transcript pays nothing for it.\n */\nimport type {\n ThreadAssistantMessagePart,\n ThreadMessage,\n ToolCallMessagePart,\n} from '@assistant-ui/core';\nimport {\n convertExternalMessages,\n createExternalMessageConversionCache,\n} from '@assistant-ui/core/react';\nimport type { LangChainMessage } from '@assistant-ui/react-langgraph';\nimport { DEEP_AGENT_REPEATS_RESULT_KEY } from './generated/runconfig-contract';\nimport { getMessageText } from './message-text';\nimport {\n convertDeepAgentLangChainMessage,\n createDeepAgentMessageConversionCache,\n type DeepAgentConversionMemory,\n type DeepAgentMessageConversionOptions,\n markPreliminaryToolResults,\n sanitizeLangChainMessages,\n} from './messages';\nimport { TASK_TOOL_NAME } from './tool-names';\n\n/** The transcript nested on a `task` card's artifact, or `undefined` when it carries none. */\nfunction laneTranscriptOf(artifact: unknown): readonly LangChainMessage[] | undefined {\n if (typeof artifact !== 'object' || artifact === null) return undefined;\n const state: unknown = (artifact as { subgraph_state?: unknown }).subgraph_state;\n if (typeof state !== 'object' || state === null) return undefined;\n const messages: unknown = (state as { messages?: unknown }).messages;\n return Array.isArray(messages) ? (messages as LangChainMessage[]) : undefined;\n}\n\nconst restoredTranscripts = new WeakMap<\n readonly LangChainMessage[],\n { readonly content: unknown; readonly transcript: readonly LangChainMessage[] }\n>();\n\n/**\n * A card's nested transcript with its report restored. On a cut finished card\n * the last step can ride with empty content and `DEEP_AGENT_REPEATS_RESULT_KEY`:\n * its text is the subagent's report, the card's own `content`, which the card\n * carries anyway. The step gets that content back. Any other transcript comes\n * back as it is. Memoized per transcript, so a lane's conversion sees one array.\n */\nexport function withRepeatedResult(\n transcript: readonly LangChainMessage[],\n content: unknown\n): readonly LangChainMessage[] {\n const last = transcript[transcript.length - 1];\n if (\n last === undefined ||\n (last as Record<string, unknown>)[DEEP_AGENT_REPEATS_RESULT_KEY] !== true ||\n (typeof content !== 'string' && !Array.isArray(content))\n ) {\n return transcript;\n }\n const cached = restoredTranscripts.get(transcript);\n if (cached !== undefined && cached.content === content) return cached.transcript;\n const restored = [...transcript.slice(0, -1), { ...last, content } as LangChainMessage];\n restoredTranscripts.set(transcript, { content, transcript: restored });\n return restored;\n}\n\ntype AssistantLangChainMessage = Extract<LangChainMessage, { type: 'ai' }>;\n\nconst convertibleChunkTurns = new WeakMap<LangChainMessage, LangChainMessage>();\n\n/**\n * A nested assistant turn stored under the chunk class name (the legacy\n * assistant-stream fold keeps `AIMessageChunk`) as the `ai` message it is: the\n * shipped converter maps only `ai`, and would drop the turn.\n */\nfunction asConvertibleTurn(message: LangChainMessage): LangChainMessage {\n const type: string = message.type;\n if (type !== 'AIMessageChunk') return message;\n const cached = convertibleChunkTurns.get(message);\n if (cached !== undefined) return cached;\n const turn: LangChainMessage = { ...(message as AssistantLangChainMessage), type: 'ai' };\n convertibleChunkTurns.set(message, turn);\n return turn;\n}\n\n/** A lane step as the assistant turn it is when it calls nothing; `null` for any other step. */\nfunction answeringTurn(step: LangChainMessage): AssistantLangChainMessage | null {\n const turn = asConvertibleTurn(step);\n return turn.type === 'ai' && (turn.tool_calls?.length ?? 0) === 0 ? turn : null;\n}\n\nconst isTextBlock = (block: unknown): block is { type: 'text'; text?: unknown } =>\n typeof block === 'object' && block !== null && 'type' in block && block.type === 'text';\n\n/** The text a step's content shows: the string, or its text blocks joined. */\nfunction shownText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n let text = '';\n for (const block of content) {\n if (isTextBlock(block) && typeof block.text === 'string') text += block.text;\n }\n return text;\n}\n\n/** A turn that calls nothing and shows nothing: no text, and no block of another kind. */\nfunction blankTurn(step: LangChainMessage): AssistantLangChainMessage | null {\n const turn = answeringTurn(step);\n if (turn === null || shownText(turn.content).trim() !== '') return null;\n return typeof turn.content === 'string' || turn.content.every(isTextBlock) ? turn : null;\n}\n\nconst reportedLanes = new WeakMap<\n readonly LangChainMessage[],\n { readonly report: string; readonly transcript: readonly LangChainMessage[] }\n>();\n\n/**\n * A finished lane's transcript, closed by its report. A `task` card's content\n * is its subagent's report, the text of the lane's last turn. A transcript\n * that ends on that turn (an assistant step that calls nothing and says\n * something, which {@link withRepeatedResult} restores when it rides marked)\n * comes back as it is, even when blank steps follow it. A lane stored without\n * it shows the report as its closing answer: a lane whose last step is a blank\n * assistant step (lanes that finished before the host marked a repeated\n * report) gets the report in that step, its id and stamps kept, and a lane\n * that ends on a tool result or a turn that calls tools gets a closing step\n * after its last one. An empty report or transcript changes nothing. For\n * display only: the wire and the stored lane are untouched. Memoized per\n * transcript, so a lane's conversion sees one array.\n */\nexport function withLaneReport(\n transcript: readonly LangChainMessage[],\n report: string | undefined\n): readonly LangChainMessage[] {\n if (report === undefined || report.trim() === '' || transcript.length === 0) return transcript;\n // The lane's closing turn: its last step past any blank ones.\n let at = transcript.length - 1;\n let closing = transcript[at];\n while (closing !== undefined && blankTurn(closing) !== null) {\n at -= 1;\n closing = transcript[at];\n }\n const answered =\n closing !== undefined &&\n answeringTurn(closing) !== null &&\n shownText(closing.content).trim() !== '';\n if (answered) return transcript;\n const cached = reportedLanes.get(transcript);\n if (cached !== undefined && cached.report === report) return cached.transcript;\n const last = transcript[transcript.length - 1];\n const blank = last === undefined ? null : blankTurn(last);\n const closed: readonly LangChainMessage[] =\n blank !== null\n ? [...transcript.slice(0, -1), { ...blank, content: report }]\n : [...transcript, { id: `${last?.id ?? 'lane'}:report`, type: 'ai', content: report }];\n reportedLanes.set(transcript, { report, transcript: closed });\n return closed;\n}\n\n/** A nested transcript as the thread's own pipeline reads it (see {@link sanitizeLangChainMessages}). */\nfunction transcriptSources(transcript: readonly LangChainMessage[]): LangChainMessage[] {\n return sanitizeLangChainMessages(transcript.map(asConvertibleTurn));\n}\n\nconst reportedTranscripts = new WeakSet<readonly LangChainMessage[]>();\n\n/**\n * A nested transcript the shipped converter refused (it throws on, e.g., a\n * result whose tool name differs from its call's): reported once, and its card\n * goes without a nested thread instead of taking the whole thread down.\n */\nfunction reportUnconvertibleTranscript(\n transcript: readonly LangChainMessage[],\n error: unknown\n): void {\n if (reportedTranscripts.has(transcript)) return;\n reportedTranscripts.add(transcript);\n console.warn('[agent-runtime] a subagent transcript could not be converted', error);\n}\n\n/** Whether two values the wire carries (JSON: no functions, no cycles) are equal. */\nfunction sameJson(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((item, index) => sameJson(item, b[index]))\n );\n }\n const left = a as Record<string, unknown>;\n const right = b as Record<string, unknown>;\n const keys = Object.keys(left);\n return (\n keys.length === Object.keys(right).length &&\n keys.every(\n (key) =>\n Object.prototype.hasOwnProperty.call(right, key) && sameJson(left[key], right[key])\n )\n );\n}\n\n/**\n * Keeps a lane's unchanged steps as the objects it already converted. The host\n * replaces a card's whole document whenever the card changes, so every step it\n * carries arrives as a new object, and the conversion memory, which tells the\n * steps a turn continues by object, would see a new turn each time the card's\n * cut drops its oldest step and the turn's first step changes. A step whose id\n * and content are unchanged is the object held for that id. O(the steps' size).\n */\nfunction steadyStepObjects(): (\n transcript: readonly LangChainMessage[]\n) => readonly LangChainMessage[] {\n let held = new Map<string, LangChainMessage>();\n return (transcript) => {\n const next = new Map<string, LangChainMessage>();\n let kept = false;\n const steadied = transcript.map((entry) => {\n const id = entry.id;\n if (typeof id !== 'string') return entry;\n const before = held.get(id);\n const step = before !== undefined && sameJson(before, entry) ? before : entry;\n next.set(id, step);\n if (step !== entry) kept = true;\n return step;\n });\n held = next;\n return kept ? steadied : transcript;\n };\n}\n\n/** One lane's transcript, converted: `undefined` when the converter refused it. */\nexport type SubagentLaneConversion = (\n transcript: readonly LangChainMessage[],\n running: boolean\n) => readonly ThreadMessage[] | undefined;\n\n/**\n * One `task` lane's own conversion memory, for whatever converts that lane:\n * its unchanged steps keep their converted objects while it streams (a step the\n * wire sends again unchanged included, see {@link steadyStepObjects}), and its\n * turn keeps its row key (`deepAgentRowKey`) and part keys when the turn's head\n * moves — the oldest steps of a cut card falling off as the lane runs, or older\n * steps a transcript drawer pages in. A surface that keeps its disclosures under\n * that key keeps them open through both. A transcript array converted last time\n * is not converted again.\n */\nexport function createSubagentLaneConversion(): SubagentLaneConversion {\n const memory = createSubagentTranscriptConversionCache();\n const steady = steadyStepObjects();\n let last: {\n readonly transcript: readonly LangChainMessage[];\n readonly running: boolean;\n readonly messages: readonly ThreadMessage[] | undefined;\n } | null = null;\n return (transcript, running) => {\n if (last !== null && last.transcript === transcript && last.running === running) {\n return last.messages;\n }\n let messages: readonly ThreadMessage[] | undefined;\n try {\n messages = memory.convert(transcriptSources(steady(transcript)), running);\n } catch (error) {\n reportUnconvertibleTranscript(transcript, error);\n }\n last = { transcript, running, messages };\n return messages;\n };\n}\n\n/**\n * Sets each `task` part of a pass's converted rows to carry its nested\n * transcript as `messages`, for one attach. Each lane has its own conversion\n * memory, so its unchanged steps keep their converted objects while it\n * streams, as the thread's own messages do, and a lane whose transcript array\n * did not change is not converted again. A row, and a part whose lane did not\n * change, keep their identity: the same row always comes back as the same\n * object. A lane's memory lives only while a row of the pass holds its `task`\n * call: a lane a rewind removes, a branch switch hides or the window leaves\n * behind is let go with its transcript, and converted afresh if it returns.\n */\nfunction subagentTranscriptAttacher(): (rows: readonly ThreadMessage[]) => ThreadMessage[] {\n const lanes = new Map<string, SubagentLaneConversion>();\n const attachedRows = new WeakMap<ThreadMessage, ThreadMessage>();\n const attachedParts = new WeakMap<\n ToolCallMessagePart,\n { readonly messages: readonly ThreadMessage[]; readonly part: ToolCallMessagePart }\n >();\n\n const laneMessages = (\n toolCallId: string,\n transcript: readonly LangChainMessage[],\n running: boolean\n ): readonly ThreadMessage[] | undefined => {\n let lane = lanes.get(toolCallId);\n if (lane === undefined) {\n lane = createSubagentLaneConversion();\n lanes.set(toolCallId, lane);\n }\n return lane(transcript, running);\n };\n\n const attach = (row: ThreadMessage): ThreadMessage => {\n if (row.role !== 'assistant') return row;\n const cached = attachedRows.get(row);\n if (cached !== undefined) return cached;\n let content: ThreadAssistantMessagePart[] | null = null;\n for (const [index, part] of row.content.entries()) {\n if (part.type !== 'tool-call' || part.toolName !== TASK_TOOL_NAME) continue;\n const nested = laneTranscriptOf(part.artifact);\n if (nested === undefined) continue;\n // A running lane's placeholder is preliminary: its newest nested turn streams.\n const running = part.isPreliminary === true;\n const restored = withRepeatedResult(nested, part.result);\n const transcript =\n running || part.isError === true\n ? restored\n : withLaneReport(restored, getMessageText({ content: part.result }));\n const messages = laneMessages(part.toolCallId, transcript, running);\n if (messages === undefined) continue;\n const kept = attachedParts.get(part);\n const attached =\n kept !== undefined && kept.messages === messages ? kept.part : { ...part, messages };\n attachedParts.set(part, { messages, part: attached });\n content ??= [...row.content];\n content[index] = attached;\n }\n const attachedRow: ThreadMessage = content === null ? row : { ...row, content };\n attachedRows.set(row, attachedRow);\n return attachedRow;\n };\n\n return (rows) => {\n const attached = rows.map(attach);\n if (lanes.size === 0) return attached;\n const held = new Set<string>();\n for (const row of rows) {\n if (row.role !== 'assistant') continue;\n for (const part of row.content) {\n if (part.type !== 'tool-call' || part.toolName !== TASK_TOOL_NAME) continue;\n held.add(part.toolCallId);\n }\n }\n for (const toolCallId of lanes.keys()) {\n if (!held.has(toolCallId)) lanes.delete(toolCallId);\n }\n return attached;\n };\n}\n\n/**\n * A conversion memory for one attach (see\n * `useDeepAgentThread({ createConversionCache })`): the shared one's rows, with\n * each `task` tool-call part carrying its subagent's converted transcript as\n * `messages`. A pass that changes no row hands back the previous list itself.\n */\nexport function createSubagentTranscriptConversionCache(\n options: DeepAgentMessageConversionOptions = {}\n): DeepAgentConversionMemory {\n const thread = createDeepAgentMessageConversionCache(options);\n const attach = subagentTranscriptAttacher();\n let previousRows: ThreadMessage[] | null = null;\n let previous: ThreadMessage[] = [];\n return {\n convert(messages, isRunning) {\n const rows = thread.convert(messages, isRunning);\n if (rows === previousRows) return previous;\n previousRows = rows;\n const next = attach(rows);\n const unchanged =\n next.length === previous.length && next.every((row, index) => row === previous[index]);\n if (!unchanged) previous = next;\n return previous;\n },\n };\n}\n\n/**\n * Shared by every in-eval transcript: entries are keyed by source message, so\n * none outlives the wire state holding it and threads cannot collide.\n */\nconst inEvalTranscriptCache = createExternalMessageConversionCache();\nconst IN_EVAL_TRANSCRIPT_METADATA = {};\nconst inEvalTranscripts = new WeakMap<\n readonly LangChainMessage[],\n { readonly running: boolean; readonly messages: readonly ThreadMessage[] }\n>();\n\nconst NO_MESSAGES: readonly ThreadMessage[] = [];\n\n/**\n * An in-eval dispatch's live transcript (a `ptc_subagents` entry's `messages`)\n * as a nested thread, converted like a `task` lane's. Memoized per transcript\n * array; a step the lane did not change keeps its converted object. A\n * transcript the converter refuses reads as empty.\n */\nexport function convertSubagentTranscript(\n transcript: readonly LangChainMessage[],\n running: boolean\n): readonly ThreadMessage[] {\n const cached = inEvalTranscripts.get(transcript);\n if (cached !== undefined && cached.running === running) return cached.messages;\n let messages: readonly ThreadMessage[] = NO_MESSAGES;\n try {\n messages = convertExternalMessages(\n transcriptSources(transcript),\n convertDeepAgentLangChainMessage,\n running,\n IN_EVAL_TRANSCRIPT_METADATA,\n inEvalTranscriptCache\n ).map(markPreliminaryToolResults);\n } catch (error) {\n reportUnconvertibleTranscript(transcript, error);\n }\n inEvalTranscripts.set(transcript, { running, messages });\n return messages;\n}\n"],"names":["messages","DEEP_AGENT_REPEATS_RESULT_KEY","sanitizeLangChainMessages","TASK_TOOL_NAME","getMessageText","createDeepAgentMessageConversionCache","createExternalMessageConversionCache"],"mappings":";;;;;;;AAyCA,SAAS,iBAAiB,UAA4D;AACpF,MAAI,OAAO,aAAa,YAAY,aAAa,KAAM,QAAO;AAC9D,QAAM,QAAkB,SAA0C;AAClE,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAMA,YAAqB,MAAiC;AAC5D,SAAO,MAAM,QAAQA,SAAQ,IAAKA,YAAkC;AACtE;AAEA,MAAM,0CAA0B,QAAA;AAYzB,SAAS,mBACd,YACA,SAC6B;AAC7B,QAAM,OAAO,WAAW,WAAW,SAAS,CAAC;AAC7C,MACE,SAAS,UACR,KAAiCC,kBAAAA,6BAA6B,MAAM,QACpE,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,OAAO,GACtD;AACA,WAAO;AAAA,EACT;AACA,QAAM,SAAS,oBAAoB,IAAI,UAAU;AACjD,MAAI,WAAW,UAAa,OAAO,YAAY,gBAAgB,OAAO;AACtE,QAAM,WAAW,CAAC,GAAG,WAAW,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,SAA6B;AACtF,sBAAoB,IAAI,YAAY,EAAE,SAAS,YAAY,UAAU;AACrE,SAAO;AACT;AAIA,MAAM,4CAA4B,QAAA;AAOlC,SAAS,kBAAkB,SAA6C;AACtE,QAAM,OAAe,QAAQ;AAC7B,MAAI,SAAS,iBAAkB,QAAO;AACtC,QAAM,SAAS,sBAAsB,IAAI,OAAO;AAChD,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,OAAyB,EAAE,GAAI,SAAuC,MAAM,KAAA;AAClF,wBAAsB,IAAI,SAAS,IAAI;AACvC,SAAO;AACT;AAGA,SAAS,cAAc,MAA0D;;AAC/E,QAAM,OAAO,kBAAkB,IAAI;AACnC,SAAO,KAAK,SAAS,WAAS,UAAK,eAAL,mBAAiB,WAAU,OAAO,IAAI,OAAO;AAC7E;AAEA,MAAM,cAAc,CAAC,UACnB,OAAO,UAAU,YAAY,UAAU,QAAQ,UAAU,SAAS,MAAM,SAAS;AAGnF,SAAS,UAAU,SAA0B;AAC3C,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,MAAI,OAAO;AACX,aAAW,SAAS,SAAS;AAC3B,QAAI,YAAY,KAAK,KAAK,OAAO,MAAM,SAAS,kBAAkB,MAAM;AAAA,EAC1E;AACA,SAAO;AACT;AAGA,SAAS,UAAU,MAA0D;AAC3E,QAAM,OAAO,cAAc,IAAI;AAC/B,MAAI,SAAS,QAAQ,UAAU,KAAK,OAAO,EAAE,KAAA,MAAW,GAAI,QAAO;AACnE,SAAO,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,MAAM,WAAW,IAAI,OAAO;AACtF;AAEA,MAAM,oCAAoB,QAAA;AAmBnB,SAAS,eACd,YACA,QAC6B;AAC7B,MAAI,WAAW,UAAa,OAAO,KAAA,MAAW,MAAM,WAAW,WAAW,EAAG,QAAO;AAEpF,MAAI,KAAK,WAAW,SAAS;AAC7B,MAAI,UAAU,WAAW,EAAE;AAC3B,SAAO,YAAY,UAAa,UAAU,OAAO,MAAM,MAAM;AAC3D,UAAM;AACN,cAAU,WAAW,EAAE;AAAA,EACzB;AACA,QAAM,WACJ,YAAY,UACZ,cAAc,OAAO,MAAM,QAC3B,UAAU,QAAQ,OAAO,EAAE,KAAA,MAAW;AACxC,MAAI,SAAU,QAAO;AACrB,QAAM,SAAS,cAAc,IAAI,UAAU;AAC3C,MAAI,WAAW,UAAa,OAAO,WAAW,eAAe,OAAO;AACpE,QAAM,OAAO,WAAW,WAAW,SAAS,CAAC;AAC7C,QAAM,QAAQ,SAAS,SAAY,OAAO,UAAU,IAAI;AACxD,QAAM,SACJ,UAAU,OACN,CAAC,GAAG,WAAW,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,OAAO,SAAS,OAAA,CAAQ,IAC1D,CAAC,GAAG,YAAY,EAAE,IAAI,IAAG,6BAAM,OAAM,MAAM,WAAW,MAAM,MAAM,SAAS,QAAQ;AACzF,gBAAc,IAAI,YAAY,EAAE,QAAQ,YAAY,QAAQ;AAC5D,SAAO;AACT;AAGA,SAAS,kBAAkB,YAA6D;AACtF,SAAOC,mCAA0B,WAAW,IAAI,iBAAiB,CAAC;AACpE;AAEA,MAAM,0CAA0B,QAAA;AAOhC,SAAS,8BACP,YACA,OACM;AACN,MAAI,oBAAoB,IAAI,UAAU,EAAG;AACzC,sBAAoB,IAAI,UAAU;AAClC,UAAQ,KAAK,gEAAgE,KAAK;AACpF;AAGA,SAAS,SAAS,GAAY,GAAqB;AACjD,MAAI,OAAO,GAAG,GAAG,CAAC,EAAG,QAAO;AAC5B,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,KAAM,QAAO;AACvF,MAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;AACxC,WACE,MAAM,QAAQ,CAAC,KACf,MAAM,QAAQ,CAAC,KACf,EAAE,WAAW,EAAE,UACf,EAAE,MAAM,CAAC,MAAM,UAAU,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC;AAAA,EAErD;AACA,QAAM,OAAO;AACb,QAAM,QAAQ;AACd,QAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,SACE,KAAK,WAAW,OAAO,KAAK,KAAK,EAAE,UACnC,KAAK;AAAA,IACH,CAAC,QACC,OAAO,UAAU,eAAe,KAAK,OAAO,GAAG,KAAK,SAAS,KAAK,GAAG,GAAG,MAAM,GAAG,CAAC;AAAA,EAAA;AAG1F;AAUA,SAAS,oBAEwB;AAC/B,MAAI,2BAAW,IAAA;AACf,SAAO,CAAC,eAAe;AACrB,UAAM,2BAAW,IAAA;AACjB,QAAI,OAAO;AACX,UAAM,WAAW,WAAW,IAAI,CAAC,UAAU;AACzC,YAAM,KAAK,MAAM;AACjB,UAAI,OAAO,OAAO,SAAU,QAAO;AACnC,YAAM,SAAS,KAAK,IAAI,EAAE;AAC1B,YAAM,OAAO,WAAW,UAAa,SAAS,QAAQ,KAAK,IAAI,SAAS;AACxE,WAAK,IAAI,IAAI,IAAI;AACjB,UAAI,SAAS,MAAO,QAAO;AAC3B,aAAO;AAAA,IACT,CAAC;AACD,WAAO;AACP,WAAO,OAAO,WAAW;AAAA,EAC3B;AACF;AAkBO,SAAS,+BAAuD;AACrE,QAAM,SAAS,wCAAA;AACf,QAAM,SAAS,kBAAA;AACf,MAAI,OAIO;AACX,SAAO,CAAC,YAAY,YAAY;AAC9B,QAAI,SAAS,QAAQ,KAAK,eAAe,cAAc,KAAK,YAAY,SAAS;AAC/E,aAAO,KAAK;AAAA,IACd;AACA,QAAIF;AACJ,QAAI;AACF,MAAAA,YAAW,OAAO,QAAQ,kBAAkB,OAAO,UAAU,CAAC,GAAG,OAAO;AAAA,IAC1E,SAAS,OAAO;AACd,oCAA8B,YAAY,KAAK;AAAA,IACjD;AACA,WAAO,EAAE,YAAY,SAAS,UAAAA,UAAA;AAC9B,WAAOA;AAAA,EACT;AACF;AAaA,SAAS,6BAAkF;AACzF,QAAM,4BAAY,IAAA;AAClB,QAAM,mCAAmB,QAAA;AACzB,QAAM,oCAAoB,QAAA;AAK1B,QAAM,eAAe,CACnB,YACA,YACA,YACyC;AACzC,QAAI,OAAO,MAAM,IAAI,UAAU;AAC/B,QAAI,SAAS,QAAW;AACtB,aAAO,6BAAA;AACP,YAAM,IAAI,YAAY,IAAI;AAAA,IAC5B;AACA,WAAO,KAAK,YAAY,OAAO;AAAA,EACjC;AAEA,QAAM,SAAS,CAAC,QAAsC;AACpD,QAAI,IAAI,SAAS,YAAa,QAAO;AACrC,UAAM,SAAS,aAAa,IAAI,GAAG;AACnC,QAAI,WAAW,OAAW,QAAO;AACjC,QAAI,UAA+C;AACnD,eAAW,CAAC,OAAO,IAAI,KAAK,IAAI,QAAQ,WAAW;AACjD,UAAI,KAAK,SAAS,eAAe,KAAK,aAAaG,UAAAA,eAAgB;AACnE,YAAM,SAAS,iBAAiB,KAAK,QAAQ;AAC7C,UAAI,WAAW,OAAW;AAE1B,YAAM,UAAU,KAAK,kBAAkB;AACvC,YAAM,WAAW,mBAAmB,QAAQ,KAAK,MAAM;AACvD,YAAM,aACJ,WAAW,KAAK,YAAY,OACxB,WACA,eAAe,UAAUC,YAAAA,eAAe,EAAE,SAAS,KAAK,OAAA,CAAQ,CAAC;AACvE,YAAMJ,YAAW,aAAa,KAAK,YAAY,YAAY,OAAO;AAClE,UAAIA,cAAa,OAAW;AAC5B,YAAM,OAAO,cAAc,IAAI,IAAI;AACnC,YAAM,WACJ,SAAS,UAAa,KAAK,aAAaA,YAAW,KAAK,OAAO,EAAE,GAAG,MAAM,UAAAA,UAAA;AAC5E,oBAAc,IAAI,MAAM,EAAE,UAAAA,WAAU,MAAM,UAAU;AACpD,4BAAY,CAAC,GAAG,IAAI,OAAO;AAC3B,cAAQ,KAAK,IAAI;AAAA,IACnB;AACA,UAAM,cAA6B,YAAY,OAAO,MAAM,EAAE,GAAG,KAAK,QAAA;AACtE,iBAAa,IAAI,KAAK,WAAW;AACjC,WAAO;AAAA,EACT;AAEA,SAAO,CAAC,SAAS;AACf,UAAM,WAAW,KAAK,IAAI,MAAM;AAChC,QAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,UAAM,2BAAW,IAAA;AACjB,eAAW,OAAO,MAAM;AACtB,UAAI,IAAI,SAAS,YAAa;AAC9B,iBAAW,QAAQ,IAAI,SAAS;AAC9B,YAAI,KAAK,SAAS,eAAe,KAAK,aAAaG,UAAAA,eAAgB;AACnE,aAAK,IAAI,KAAK,UAAU;AAAA,MAC1B;AAAA,IACF;AACA,eAAW,cAAc,MAAM,QAAQ;AACrC,UAAI,CAAC,KAAK,IAAI,UAAU,EAAG,OAAM,OAAO,UAAU;AAAA,IACpD;AACA,WAAO;AAAA,EACT;AACF;AAQO,SAAS,wCACd,UAA6C,IAClB;AAC3B,QAAM,SAASE,SAAAA,sCAAsC,OAAO;AAC5D,QAAM,SAAS,2BAAA;AACf,MAAI,eAAuC;AAC3C,MAAI,WAA4B,CAAA;AAChC,SAAO;AAAA,IACL,QAAQL,WAAU,WAAW;AAC3B,YAAM,OAAO,OAAO,QAAQA,WAAU,SAAS;AAC/C,UAAI,SAAS,aAAc,QAAO;AAClC,qBAAe;AACf,YAAM,OAAO,OAAO,IAAI;AACxB,YAAM,YACJ,KAAK,WAAW,SAAS,UAAU,KAAK,MAAM,CAAC,KAAK,UAAU,QAAQ,SAAS,KAAK,CAAC;AACvF,UAAI,CAAC,UAAW,YAAW;AAC3B,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;AAM8BM,MAAAA,qCAAA;;;;;"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { ThreadMessage } from '@assistant-ui/core';
|
|
20
20
|
import type { LangChainMessage } from '@assistant-ui/react-langgraph';
|
|
21
|
-
import { type DeepAgentConversionMemory } from './messages';
|
|
21
|
+
import { type DeepAgentConversionMemory, type DeepAgentMessageConversionOptions } from './messages';
|
|
22
22
|
/**
|
|
23
23
|
* A card's nested transcript with its report restored. On a cut finished card
|
|
24
24
|
* the last step can ride with empty content and `DEEP_AGENT_REPEATS_RESULT_KEY`:
|
|
@@ -61,7 +61,7 @@ export declare function createSubagentLaneConversion(): SubagentLaneConversion;
|
|
|
61
61
|
* each `task` tool-call part carrying its subagent's converted transcript as
|
|
62
62
|
* `messages`. A pass that changes no row hands back the previous list itself.
|
|
63
63
|
*/
|
|
64
|
-
export declare function createSubagentTranscriptConversionCache(): DeepAgentConversionMemory;
|
|
64
|
+
export declare function createSubagentTranscriptConversionCache(options?: DeepAgentMessageConversionOptions): DeepAgentConversionMemory;
|
|
65
65
|
/**
|
|
66
66
|
* An in-eval dispatch's live transcript (a `ptc_subagents` entry's `messages`)
|
|
67
67
|
* as a nested thread, converted like a `task` lane's. Memoized per transcript
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createExternalMessageConversionCache } from "@assistant-ui/core/react";
|
|
2
2
|
import { DEEP_AGENT_REPEATS_RESULT_KEY } from "./generated/runconfig-contract.js";
|
|
3
3
|
import { getMessageText } from "./message-text.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createDeepAgentMessageConversionCache, sanitizeLangChainMessages } from "./messages.js";
|
|
5
5
|
import { TASK_TOOL_NAME } from "./tool-names.js";
|
|
6
6
|
function laneTranscriptOf(artifact) {
|
|
7
7
|
if (typeof artifact !== "object" || artifact === null) return void 0;
|
|
@@ -182,8 +182,8 @@ function subagentTranscriptAttacher() {
|
|
|
182
182
|
return attached;
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
|
-
function createSubagentTranscriptConversionCache() {
|
|
186
|
-
const thread = createDeepAgentMessageConversionCache();
|
|
185
|
+
function createSubagentTranscriptConversionCache(options = {}) {
|
|
186
|
+
const thread = createDeepAgentMessageConversionCache(options);
|
|
187
187
|
const attach = subagentTranscriptAttacher();
|
|
188
188
|
let previousRows = null;
|
|
189
189
|
let previous = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent-transcripts.js","sources":["../../../../agent-runtime/src/subagent-transcripts.ts"],"sourcesContent":["/**\n * A subagent's transcript as assistant-ui's native nested thread.\n *\n * The backend nests a `task` subagent's transcript in its card's ToolMessage,\n * at `artifact.subgraph_state.messages`: the newest steps on the running\n * placeholder, the whole transcript on the real result. The conversion cache\n * built here converts that transcript through the thread's own pipeline and\n * sets it as the `task` tool-call part's `messages`, assistant-ui's channel for\n * a tool call's nested conversation. `ReadonlyThreadProvider` (the provider\n * under `MessagePartPrimitive.Messages`) renders it as a read-only thread under\n * the parent's tool UIs, and `thread.tasks` derives child work from it.\n *\n * Hosts opt in (`useDeepAgentThread({ createConversionCache })`). It builds on\n * the shared conversion memory from outside, so the shared converter, which\n * ships in every surface's startup bundle (the Office pane's startup and total\n * budgets have no room to spare), stays as it is, and a surface that draws no\n * subagent transcript pays nothing for it.\n */\nimport type {\n ThreadAssistantMessagePart,\n ThreadMessage,\n ToolCallMessagePart,\n} from '@assistant-ui/core';\nimport {\n convertExternalMessages,\n createExternalMessageConversionCache,\n} from '@assistant-ui/core/react';\nimport type { LangChainMessage } from '@assistant-ui/react-langgraph';\nimport { DEEP_AGENT_REPEATS_RESULT_KEY } from './generated/runconfig-contract';\nimport { getMessageText } from './message-text';\nimport {\n convertDeepAgentLangChainMessage,\n createDeepAgentMessageConversionCache,\n type DeepAgentConversionMemory,\n markPreliminaryToolResults,\n sanitizeLangChainMessages,\n} from './messages';\nimport { TASK_TOOL_NAME } from './tool-names';\n\n/** The transcript nested on a `task` card's artifact, or `undefined` when it carries none. */\nfunction laneTranscriptOf(artifact: unknown): readonly LangChainMessage[] | undefined {\n if (typeof artifact !== 'object' || artifact === null) return undefined;\n const state: unknown = (artifact as { subgraph_state?: unknown }).subgraph_state;\n if (typeof state !== 'object' || state === null) return undefined;\n const messages: unknown = (state as { messages?: unknown }).messages;\n return Array.isArray(messages) ? (messages as LangChainMessage[]) : undefined;\n}\n\nconst restoredTranscripts = new WeakMap<\n readonly LangChainMessage[],\n { readonly content: unknown; readonly transcript: readonly LangChainMessage[] }\n>();\n\n/**\n * A card's nested transcript with its report restored. On a cut finished card\n * the last step can ride with empty content and `DEEP_AGENT_REPEATS_RESULT_KEY`:\n * its text is the subagent's report, the card's own `content`, which the card\n * carries anyway. The step gets that content back. Any other transcript comes\n * back as it is. Memoized per transcript, so a lane's conversion sees one array.\n */\nexport function withRepeatedResult(\n transcript: readonly LangChainMessage[],\n content: unknown\n): readonly LangChainMessage[] {\n const last = transcript[transcript.length - 1];\n if (\n last === undefined ||\n (last as Record<string, unknown>)[DEEP_AGENT_REPEATS_RESULT_KEY] !== true ||\n (typeof content !== 'string' && !Array.isArray(content))\n ) {\n return transcript;\n }\n const cached = restoredTranscripts.get(transcript);\n if (cached !== undefined && cached.content === content) return cached.transcript;\n const restored = [...transcript.slice(0, -1), { ...last, content } as LangChainMessage];\n restoredTranscripts.set(transcript, { content, transcript: restored });\n return restored;\n}\n\ntype AssistantLangChainMessage = Extract<LangChainMessage, { type: 'ai' }>;\n\nconst convertibleChunkTurns = new WeakMap<LangChainMessage, LangChainMessage>();\n\n/**\n * A nested assistant turn stored under the chunk class name (the legacy\n * assistant-stream fold keeps `AIMessageChunk`) as the `ai` message it is: the\n * shipped converter maps only `ai`, and would drop the turn.\n */\nfunction asConvertibleTurn(message: LangChainMessage): LangChainMessage {\n const type: string = message.type;\n if (type !== 'AIMessageChunk') return message;\n const cached = convertibleChunkTurns.get(message);\n if (cached !== undefined) return cached;\n const turn: LangChainMessage = { ...(message as AssistantLangChainMessage), type: 'ai' };\n convertibleChunkTurns.set(message, turn);\n return turn;\n}\n\n/** A lane step as the assistant turn it is when it calls nothing; `null` for any other step. */\nfunction answeringTurn(step: LangChainMessage): AssistantLangChainMessage | null {\n const turn = asConvertibleTurn(step);\n return turn.type === 'ai' && (turn.tool_calls?.length ?? 0) === 0 ? turn : null;\n}\n\nconst isTextBlock = (block: unknown): block is { type: 'text'; text?: unknown } =>\n typeof block === 'object' && block !== null && 'type' in block && block.type === 'text';\n\n/** The text a step's content shows: the string, or its text blocks joined. */\nfunction shownText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n let text = '';\n for (const block of content) {\n if (isTextBlock(block) && typeof block.text === 'string') text += block.text;\n }\n return text;\n}\n\n/** A turn that calls nothing and shows nothing: no text, and no block of another kind. */\nfunction blankTurn(step: LangChainMessage): AssistantLangChainMessage | null {\n const turn = answeringTurn(step);\n if (turn === null || shownText(turn.content).trim() !== '') return null;\n return typeof turn.content === 'string' || turn.content.every(isTextBlock) ? turn : null;\n}\n\nconst reportedLanes = new WeakMap<\n readonly LangChainMessage[],\n { readonly report: string; readonly transcript: readonly LangChainMessage[] }\n>();\n\n/**\n * A finished lane's transcript, closed by its report. A `task` card's content\n * is its subagent's report, the text of the lane's last turn. A transcript\n * that ends on that turn (an assistant step that calls nothing and says\n * something, which {@link withRepeatedResult} restores when it rides marked)\n * comes back as it is, even when blank steps follow it. A lane stored without\n * it shows the report as its closing answer: a lane whose last step is a blank\n * assistant step (lanes that finished before the host marked a repeated\n * report) gets the report in that step, its id and stamps kept, and a lane\n * that ends on a tool result or a turn that calls tools gets a closing step\n * after its last one. An empty report or transcript changes nothing. For\n * display only: the wire and the stored lane are untouched. Memoized per\n * transcript, so a lane's conversion sees one array.\n */\nexport function withLaneReport(\n transcript: readonly LangChainMessage[],\n report: string | undefined\n): readonly LangChainMessage[] {\n if (report === undefined || report.trim() === '' || transcript.length === 0) return transcript;\n // The lane's closing turn: its last step past any blank ones.\n let at = transcript.length - 1;\n let closing = transcript[at];\n while (closing !== undefined && blankTurn(closing) !== null) {\n at -= 1;\n closing = transcript[at];\n }\n const answered =\n closing !== undefined &&\n answeringTurn(closing) !== null &&\n shownText(closing.content).trim() !== '';\n if (answered) return transcript;\n const cached = reportedLanes.get(transcript);\n if (cached !== undefined && cached.report === report) return cached.transcript;\n const last = transcript[transcript.length - 1];\n const blank = last === undefined ? null : blankTurn(last);\n const closed: readonly LangChainMessage[] =\n blank !== null\n ? [...transcript.slice(0, -1), { ...blank, content: report }]\n : [...transcript, { id: `${last?.id ?? 'lane'}:report`, type: 'ai', content: report }];\n reportedLanes.set(transcript, { report, transcript: closed });\n return closed;\n}\n\n/** A nested transcript as the thread's own pipeline reads it (see {@link sanitizeLangChainMessages}). */\nfunction transcriptSources(transcript: readonly LangChainMessage[]): LangChainMessage[] {\n return sanitizeLangChainMessages(transcript.map(asConvertibleTurn));\n}\n\nconst reportedTranscripts = new WeakSet<readonly LangChainMessage[]>();\n\n/**\n * A nested transcript the shipped converter refused (it throws on, e.g., a\n * result whose tool name differs from its call's): reported once, and its card\n * goes without a nested thread instead of taking the whole thread down.\n */\nfunction reportUnconvertibleTranscript(\n transcript: readonly LangChainMessage[],\n error: unknown\n): void {\n if (reportedTranscripts.has(transcript)) return;\n reportedTranscripts.add(transcript);\n console.warn('[agent-runtime] a subagent transcript could not be converted', error);\n}\n\n/** Whether two values the wire carries (JSON: no functions, no cycles) are equal. */\nfunction sameJson(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((item, index) => sameJson(item, b[index]))\n );\n }\n const left = a as Record<string, unknown>;\n const right = b as Record<string, unknown>;\n const keys = Object.keys(left);\n return (\n keys.length === Object.keys(right).length &&\n keys.every(\n (key) =>\n Object.prototype.hasOwnProperty.call(right, key) && sameJson(left[key], right[key])\n )\n );\n}\n\n/**\n * Keeps a lane's unchanged steps as the objects it already converted. The host\n * replaces a card's whole document whenever the card changes, so every step it\n * carries arrives as a new object, and the conversion memory, which tells the\n * steps a turn continues by object, would see a new turn each time the card's\n * cut drops its oldest step and the turn's first step changes. A step whose id\n * and content are unchanged is the object held for that id. O(the steps' size).\n */\nfunction steadyStepObjects(): (\n transcript: readonly LangChainMessage[]\n) => readonly LangChainMessage[] {\n let held = new Map<string, LangChainMessage>();\n return (transcript) => {\n const next = new Map<string, LangChainMessage>();\n let kept = false;\n const steadied = transcript.map((entry) => {\n const id = entry.id;\n if (typeof id !== 'string') return entry;\n const before = held.get(id);\n const step = before !== undefined && sameJson(before, entry) ? before : entry;\n next.set(id, step);\n if (step !== entry) kept = true;\n return step;\n });\n held = next;\n return kept ? steadied : transcript;\n };\n}\n\n/** One lane's transcript, converted: `undefined` when the converter refused it. */\nexport type SubagentLaneConversion = (\n transcript: readonly LangChainMessage[],\n running: boolean\n) => readonly ThreadMessage[] | undefined;\n\n/**\n * One `task` lane's own conversion memory, for whatever converts that lane:\n * its unchanged steps keep their converted objects while it streams (a step the\n * wire sends again unchanged included, see {@link steadyStepObjects}), and its\n * turn keeps its row key (`deepAgentRowKey`) and part keys when the turn's head\n * moves — the oldest steps of a cut card falling off as the lane runs, or older\n * steps a transcript drawer pages in. A surface that keeps its disclosures under\n * that key keeps them open through both. A transcript array converted last time\n * is not converted again.\n */\nexport function createSubagentLaneConversion(): SubagentLaneConversion {\n const memory = createSubagentTranscriptConversionCache();\n const steady = steadyStepObjects();\n let last: {\n readonly transcript: readonly LangChainMessage[];\n readonly running: boolean;\n readonly messages: readonly ThreadMessage[] | undefined;\n } | null = null;\n return (transcript, running) => {\n if (last !== null && last.transcript === transcript && last.running === running) {\n return last.messages;\n }\n let messages: readonly ThreadMessage[] | undefined;\n try {\n messages = memory.convert(transcriptSources(steady(transcript)), running);\n } catch (error) {\n reportUnconvertibleTranscript(transcript, error);\n }\n last = { transcript, running, messages };\n return messages;\n };\n}\n\n/**\n * Sets each `task` part of a pass's converted rows to carry its nested\n * transcript as `messages`, for one attach. Each lane has its own conversion\n * memory, so its unchanged steps keep their converted objects while it\n * streams, as the thread's own messages do, and a lane whose transcript array\n * did not change is not converted again. A row, and a part whose lane did not\n * change, keep their identity: the same row always comes back as the same\n * object. A lane's memory lives only while a row of the pass holds its `task`\n * call: a lane a rewind removes, a branch switch hides or the window leaves\n * behind is let go with its transcript, and converted afresh if it returns.\n */\nfunction subagentTranscriptAttacher(): (rows: readonly ThreadMessage[]) => ThreadMessage[] {\n const lanes = new Map<string, SubagentLaneConversion>();\n const attachedRows = new WeakMap<ThreadMessage, ThreadMessage>();\n const attachedParts = new WeakMap<\n ToolCallMessagePart,\n { readonly messages: readonly ThreadMessage[]; readonly part: ToolCallMessagePart }\n >();\n\n const laneMessages = (\n toolCallId: string,\n transcript: readonly LangChainMessage[],\n running: boolean\n ): readonly ThreadMessage[] | undefined => {\n let lane = lanes.get(toolCallId);\n if (lane === undefined) {\n lane = createSubagentLaneConversion();\n lanes.set(toolCallId, lane);\n }\n return lane(transcript, running);\n };\n\n const attach = (row: ThreadMessage): ThreadMessage => {\n if (row.role !== 'assistant') return row;\n const cached = attachedRows.get(row);\n if (cached !== undefined) return cached;\n let content: ThreadAssistantMessagePart[] | null = null;\n for (const [index, part] of row.content.entries()) {\n if (part.type !== 'tool-call' || part.toolName !== TASK_TOOL_NAME) continue;\n const nested = laneTranscriptOf(part.artifact);\n if (nested === undefined) continue;\n // A running lane's placeholder is preliminary: its newest nested turn streams.\n const running = part.isPreliminary === true;\n const restored = withRepeatedResult(nested, part.result);\n const transcript =\n running || part.isError === true\n ? restored\n : withLaneReport(restored, getMessageText({ content: part.result }));\n const messages = laneMessages(part.toolCallId, transcript, running);\n if (messages === undefined) continue;\n const kept = attachedParts.get(part);\n const attached =\n kept !== undefined && kept.messages === messages ? kept.part : { ...part, messages };\n attachedParts.set(part, { messages, part: attached });\n content ??= [...row.content];\n content[index] = attached;\n }\n const attachedRow: ThreadMessage = content === null ? row : { ...row, content };\n attachedRows.set(row, attachedRow);\n return attachedRow;\n };\n\n return (rows) => {\n const attached = rows.map(attach);\n if (lanes.size === 0) return attached;\n const held = new Set<string>();\n for (const row of rows) {\n if (row.role !== 'assistant') continue;\n for (const part of row.content) {\n if (part.type !== 'tool-call' || part.toolName !== TASK_TOOL_NAME) continue;\n held.add(part.toolCallId);\n }\n }\n for (const toolCallId of lanes.keys()) {\n if (!held.has(toolCallId)) lanes.delete(toolCallId);\n }\n return attached;\n };\n}\n\n/**\n * A conversion memory for one attach (see\n * `useDeepAgentThread({ createConversionCache })`): the shared one's rows, with\n * each `task` tool-call part carrying its subagent's converted transcript as\n * `messages`. A pass that changes no row hands back the previous list itself.\n */\nexport function createSubagentTranscriptConversionCache(): DeepAgentConversionMemory {\n const thread = createDeepAgentMessageConversionCache();\n const attach = subagentTranscriptAttacher();\n let previousRows: ThreadMessage[] | null = null;\n let previous: ThreadMessage[] = [];\n return {\n convert(messages, isRunning) {\n const rows = thread.convert(messages, isRunning);\n if (rows === previousRows) return previous;\n previousRows = rows;\n const next = attach(rows);\n const unchanged =\n next.length === previous.length && next.every((row, index) => row === previous[index]);\n if (!unchanged) previous = next;\n return previous;\n },\n };\n}\n\n/**\n * Shared by every in-eval transcript: entries are keyed by source message, so\n * none outlives the wire state holding it and threads cannot collide.\n */\nconst inEvalTranscriptCache = createExternalMessageConversionCache();\nconst IN_EVAL_TRANSCRIPT_METADATA = {};\nconst inEvalTranscripts = new WeakMap<\n readonly LangChainMessage[],\n { readonly running: boolean; readonly messages: readonly ThreadMessage[] }\n>();\n\nconst NO_MESSAGES: readonly ThreadMessage[] = [];\n\n/**\n * An in-eval dispatch's live transcript (a `ptc_subagents` entry's `messages`)\n * as a nested thread, converted like a `task` lane's. Memoized per transcript\n * array; a step the lane did not change keeps its converted object. A\n * transcript the converter refuses reads as empty.\n */\nexport function convertSubagentTranscript(\n transcript: readonly LangChainMessage[],\n running: boolean\n): readonly ThreadMessage[] {\n const cached = inEvalTranscripts.get(transcript);\n if (cached !== undefined && cached.running === running) return cached.messages;\n let messages: readonly ThreadMessage[] = NO_MESSAGES;\n try {\n messages = convertExternalMessages(\n transcriptSources(transcript),\n convertDeepAgentLangChainMessage,\n running,\n IN_EVAL_TRANSCRIPT_METADATA,\n inEvalTranscriptCache\n ).map(markPreliminaryToolResults);\n } catch (error) {\n reportUnconvertibleTranscript(transcript, error);\n }\n inEvalTranscripts.set(transcript, { running, messages });\n return messages;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"subagent-transcripts.js","sources":["../../../../agent-runtime/src/subagent-transcripts.ts"],"sourcesContent":["/**\n * A subagent's transcript as assistant-ui's native nested thread.\n *\n * The backend nests a `task` subagent's transcript in its card's ToolMessage,\n * at `artifact.subgraph_state.messages`: the newest steps on the running\n * placeholder, the whole transcript on the real result. The conversion cache\n * built here converts that transcript through the thread's own pipeline and\n * sets it as the `task` tool-call part's `messages`, assistant-ui's channel for\n * a tool call's nested conversation. `ReadonlyThreadProvider` (the provider\n * under `MessagePartPrimitive.Messages`) renders it as a read-only thread under\n * the parent's tool UIs, and `thread.tasks` derives child work from it.\n *\n * Hosts opt in (`useDeepAgentThread({ createConversionCache })`). It builds on\n * the shared conversion memory from outside, so the shared converter, which\n * ships in every surface's startup bundle (the Office pane's startup and total\n * budgets have no room to spare), stays as it is, and a surface that draws no\n * subagent transcript pays nothing for it.\n */\nimport type {\n ThreadAssistantMessagePart,\n ThreadMessage,\n ToolCallMessagePart,\n} from '@assistant-ui/core';\nimport {\n convertExternalMessages,\n createExternalMessageConversionCache,\n} from '@assistant-ui/core/react';\nimport type { LangChainMessage } from '@assistant-ui/react-langgraph';\nimport { DEEP_AGENT_REPEATS_RESULT_KEY } from './generated/runconfig-contract';\nimport { getMessageText } from './message-text';\nimport {\n convertDeepAgentLangChainMessage,\n createDeepAgentMessageConversionCache,\n type DeepAgentConversionMemory,\n type DeepAgentMessageConversionOptions,\n markPreliminaryToolResults,\n sanitizeLangChainMessages,\n} from './messages';\nimport { TASK_TOOL_NAME } from './tool-names';\n\n/** The transcript nested on a `task` card's artifact, or `undefined` when it carries none. */\nfunction laneTranscriptOf(artifact: unknown): readonly LangChainMessage[] | undefined {\n if (typeof artifact !== 'object' || artifact === null) return undefined;\n const state: unknown = (artifact as { subgraph_state?: unknown }).subgraph_state;\n if (typeof state !== 'object' || state === null) return undefined;\n const messages: unknown = (state as { messages?: unknown }).messages;\n return Array.isArray(messages) ? (messages as LangChainMessage[]) : undefined;\n}\n\nconst restoredTranscripts = new WeakMap<\n readonly LangChainMessage[],\n { readonly content: unknown; readonly transcript: readonly LangChainMessage[] }\n>();\n\n/**\n * A card's nested transcript with its report restored. On a cut finished card\n * the last step can ride with empty content and `DEEP_AGENT_REPEATS_RESULT_KEY`:\n * its text is the subagent's report, the card's own `content`, which the card\n * carries anyway. The step gets that content back. Any other transcript comes\n * back as it is. Memoized per transcript, so a lane's conversion sees one array.\n */\nexport function withRepeatedResult(\n transcript: readonly LangChainMessage[],\n content: unknown\n): readonly LangChainMessage[] {\n const last = transcript[transcript.length - 1];\n if (\n last === undefined ||\n (last as Record<string, unknown>)[DEEP_AGENT_REPEATS_RESULT_KEY] !== true ||\n (typeof content !== 'string' && !Array.isArray(content))\n ) {\n return transcript;\n }\n const cached = restoredTranscripts.get(transcript);\n if (cached !== undefined && cached.content === content) return cached.transcript;\n const restored = [...transcript.slice(0, -1), { ...last, content } as LangChainMessage];\n restoredTranscripts.set(transcript, { content, transcript: restored });\n return restored;\n}\n\ntype AssistantLangChainMessage = Extract<LangChainMessage, { type: 'ai' }>;\n\nconst convertibleChunkTurns = new WeakMap<LangChainMessage, LangChainMessage>();\n\n/**\n * A nested assistant turn stored under the chunk class name (the legacy\n * assistant-stream fold keeps `AIMessageChunk`) as the `ai` message it is: the\n * shipped converter maps only `ai`, and would drop the turn.\n */\nfunction asConvertibleTurn(message: LangChainMessage): LangChainMessage {\n const type: string = message.type;\n if (type !== 'AIMessageChunk') return message;\n const cached = convertibleChunkTurns.get(message);\n if (cached !== undefined) return cached;\n const turn: LangChainMessage = { ...(message as AssistantLangChainMessage), type: 'ai' };\n convertibleChunkTurns.set(message, turn);\n return turn;\n}\n\n/** A lane step as the assistant turn it is when it calls nothing; `null` for any other step. */\nfunction answeringTurn(step: LangChainMessage): AssistantLangChainMessage | null {\n const turn = asConvertibleTurn(step);\n return turn.type === 'ai' && (turn.tool_calls?.length ?? 0) === 0 ? turn : null;\n}\n\nconst isTextBlock = (block: unknown): block is { type: 'text'; text?: unknown } =>\n typeof block === 'object' && block !== null && 'type' in block && block.type === 'text';\n\n/** The text a step's content shows: the string, or its text blocks joined. */\nfunction shownText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n let text = '';\n for (const block of content) {\n if (isTextBlock(block) && typeof block.text === 'string') text += block.text;\n }\n return text;\n}\n\n/** A turn that calls nothing and shows nothing: no text, and no block of another kind. */\nfunction blankTurn(step: LangChainMessage): AssistantLangChainMessage | null {\n const turn = answeringTurn(step);\n if (turn === null || shownText(turn.content).trim() !== '') return null;\n return typeof turn.content === 'string' || turn.content.every(isTextBlock) ? turn : null;\n}\n\nconst reportedLanes = new WeakMap<\n readonly LangChainMessage[],\n { readonly report: string; readonly transcript: readonly LangChainMessage[] }\n>();\n\n/**\n * A finished lane's transcript, closed by its report. A `task` card's content\n * is its subagent's report, the text of the lane's last turn. A transcript\n * that ends on that turn (an assistant step that calls nothing and says\n * something, which {@link withRepeatedResult} restores when it rides marked)\n * comes back as it is, even when blank steps follow it. A lane stored without\n * it shows the report as its closing answer: a lane whose last step is a blank\n * assistant step (lanes that finished before the host marked a repeated\n * report) gets the report in that step, its id and stamps kept, and a lane\n * that ends on a tool result or a turn that calls tools gets a closing step\n * after its last one. An empty report or transcript changes nothing. For\n * display only: the wire and the stored lane are untouched. Memoized per\n * transcript, so a lane's conversion sees one array.\n */\nexport function withLaneReport(\n transcript: readonly LangChainMessage[],\n report: string | undefined\n): readonly LangChainMessage[] {\n if (report === undefined || report.trim() === '' || transcript.length === 0) return transcript;\n // The lane's closing turn: its last step past any blank ones.\n let at = transcript.length - 1;\n let closing = transcript[at];\n while (closing !== undefined && blankTurn(closing) !== null) {\n at -= 1;\n closing = transcript[at];\n }\n const answered =\n closing !== undefined &&\n answeringTurn(closing) !== null &&\n shownText(closing.content).trim() !== '';\n if (answered) return transcript;\n const cached = reportedLanes.get(transcript);\n if (cached !== undefined && cached.report === report) return cached.transcript;\n const last = transcript[transcript.length - 1];\n const blank = last === undefined ? null : blankTurn(last);\n const closed: readonly LangChainMessage[] =\n blank !== null\n ? [...transcript.slice(0, -1), { ...blank, content: report }]\n : [...transcript, { id: `${last?.id ?? 'lane'}:report`, type: 'ai', content: report }];\n reportedLanes.set(transcript, { report, transcript: closed });\n return closed;\n}\n\n/** A nested transcript as the thread's own pipeline reads it (see {@link sanitizeLangChainMessages}). */\nfunction transcriptSources(transcript: readonly LangChainMessage[]): LangChainMessage[] {\n return sanitizeLangChainMessages(transcript.map(asConvertibleTurn));\n}\n\nconst reportedTranscripts = new WeakSet<readonly LangChainMessage[]>();\n\n/**\n * A nested transcript the shipped converter refused (it throws on, e.g., a\n * result whose tool name differs from its call's): reported once, and its card\n * goes without a nested thread instead of taking the whole thread down.\n */\nfunction reportUnconvertibleTranscript(\n transcript: readonly LangChainMessage[],\n error: unknown\n): void {\n if (reportedTranscripts.has(transcript)) return;\n reportedTranscripts.add(transcript);\n console.warn('[agent-runtime] a subagent transcript could not be converted', error);\n}\n\n/** Whether two values the wire carries (JSON: no functions, no cycles) are equal. */\nfunction sameJson(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((item, index) => sameJson(item, b[index]))\n );\n }\n const left = a as Record<string, unknown>;\n const right = b as Record<string, unknown>;\n const keys = Object.keys(left);\n return (\n keys.length === Object.keys(right).length &&\n keys.every(\n (key) =>\n Object.prototype.hasOwnProperty.call(right, key) && sameJson(left[key], right[key])\n )\n );\n}\n\n/**\n * Keeps a lane's unchanged steps as the objects it already converted. The host\n * replaces a card's whole document whenever the card changes, so every step it\n * carries arrives as a new object, and the conversion memory, which tells the\n * steps a turn continues by object, would see a new turn each time the card's\n * cut drops its oldest step and the turn's first step changes. A step whose id\n * and content are unchanged is the object held for that id. O(the steps' size).\n */\nfunction steadyStepObjects(): (\n transcript: readonly LangChainMessage[]\n) => readonly LangChainMessage[] {\n let held = new Map<string, LangChainMessage>();\n return (transcript) => {\n const next = new Map<string, LangChainMessage>();\n let kept = false;\n const steadied = transcript.map((entry) => {\n const id = entry.id;\n if (typeof id !== 'string') return entry;\n const before = held.get(id);\n const step = before !== undefined && sameJson(before, entry) ? before : entry;\n next.set(id, step);\n if (step !== entry) kept = true;\n return step;\n });\n held = next;\n return kept ? steadied : transcript;\n };\n}\n\n/** One lane's transcript, converted: `undefined` when the converter refused it. */\nexport type SubagentLaneConversion = (\n transcript: readonly LangChainMessage[],\n running: boolean\n) => readonly ThreadMessage[] | undefined;\n\n/**\n * One `task` lane's own conversion memory, for whatever converts that lane:\n * its unchanged steps keep their converted objects while it streams (a step the\n * wire sends again unchanged included, see {@link steadyStepObjects}), and its\n * turn keeps its row key (`deepAgentRowKey`) and part keys when the turn's head\n * moves — the oldest steps of a cut card falling off as the lane runs, or older\n * steps a transcript drawer pages in. A surface that keeps its disclosures under\n * that key keeps them open through both. A transcript array converted last time\n * is not converted again.\n */\nexport function createSubagentLaneConversion(): SubagentLaneConversion {\n const memory = createSubagentTranscriptConversionCache();\n const steady = steadyStepObjects();\n let last: {\n readonly transcript: readonly LangChainMessage[];\n readonly running: boolean;\n readonly messages: readonly ThreadMessage[] | undefined;\n } | null = null;\n return (transcript, running) => {\n if (last !== null && last.transcript === transcript && last.running === running) {\n return last.messages;\n }\n let messages: readonly ThreadMessage[] | undefined;\n try {\n messages = memory.convert(transcriptSources(steady(transcript)), running);\n } catch (error) {\n reportUnconvertibleTranscript(transcript, error);\n }\n last = { transcript, running, messages };\n return messages;\n };\n}\n\n/**\n * Sets each `task` part of a pass's converted rows to carry its nested\n * transcript as `messages`, for one attach. Each lane has its own conversion\n * memory, so its unchanged steps keep their converted objects while it\n * streams, as the thread's own messages do, and a lane whose transcript array\n * did not change is not converted again. A row, and a part whose lane did not\n * change, keep their identity: the same row always comes back as the same\n * object. A lane's memory lives only while a row of the pass holds its `task`\n * call: a lane a rewind removes, a branch switch hides or the window leaves\n * behind is let go with its transcript, and converted afresh if it returns.\n */\nfunction subagentTranscriptAttacher(): (rows: readonly ThreadMessage[]) => ThreadMessage[] {\n const lanes = new Map<string, SubagentLaneConversion>();\n const attachedRows = new WeakMap<ThreadMessage, ThreadMessage>();\n const attachedParts = new WeakMap<\n ToolCallMessagePart,\n { readonly messages: readonly ThreadMessage[]; readonly part: ToolCallMessagePart }\n >();\n\n const laneMessages = (\n toolCallId: string,\n transcript: readonly LangChainMessage[],\n running: boolean\n ): readonly ThreadMessage[] | undefined => {\n let lane = lanes.get(toolCallId);\n if (lane === undefined) {\n lane = createSubagentLaneConversion();\n lanes.set(toolCallId, lane);\n }\n return lane(transcript, running);\n };\n\n const attach = (row: ThreadMessage): ThreadMessage => {\n if (row.role !== 'assistant') return row;\n const cached = attachedRows.get(row);\n if (cached !== undefined) return cached;\n let content: ThreadAssistantMessagePart[] | null = null;\n for (const [index, part] of row.content.entries()) {\n if (part.type !== 'tool-call' || part.toolName !== TASK_TOOL_NAME) continue;\n const nested = laneTranscriptOf(part.artifact);\n if (nested === undefined) continue;\n // A running lane's placeholder is preliminary: its newest nested turn streams.\n const running = part.isPreliminary === true;\n const restored = withRepeatedResult(nested, part.result);\n const transcript =\n running || part.isError === true\n ? restored\n : withLaneReport(restored, getMessageText({ content: part.result }));\n const messages = laneMessages(part.toolCallId, transcript, running);\n if (messages === undefined) continue;\n const kept = attachedParts.get(part);\n const attached =\n kept !== undefined && kept.messages === messages ? kept.part : { ...part, messages };\n attachedParts.set(part, { messages, part: attached });\n content ??= [...row.content];\n content[index] = attached;\n }\n const attachedRow: ThreadMessage = content === null ? row : { ...row, content };\n attachedRows.set(row, attachedRow);\n return attachedRow;\n };\n\n return (rows) => {\n const attached = rows.map(attach);\n if (lanes.size === 0) return attached;\n const held = new Set<string>();\n for (const row of rows) {\n if (row.role !== 'assistant') continue;\n for (const part of row.content) {\n if (part.type !== 'tool-call' || part.toolName !== TASK_TOOL_NAME) continue;\n held.add(part.toolCallId);\n }\n }\n for (const toolCallId of lanes.keys()) {\n if (!held.has(toolCallId)) lanes.delete(toolCallId);\n }\n return attached;\n };\n}\n\n/**\n * A conversion memory for one attach (see\n * `useDeepAgentThread({ createConversionCache })`): the shared one's rows, with\n * each `task` tool-call part carrying its subagent's converted transcript as\n * `messages`. A pass that changes no row hands back the previous list itself.\n */\nexport function createSubagentTranscriptConversionCache(\n options: DeepAgentMessageConversionOptions = {}\n): DeepAgentConversionMemory {\n const thread = createDeepAgentMessageConversionCache(options);\n const attach = subagentTranscriptAttacher();\n let previousRows: ThreadMessage[] | null = null;\n let previous: ThreadMessage[] = [];\n return {\n convert(messages, isRunning) {\n const rows = thread.convert(messages, isRunning);\n if (rows === previousRows) return previous;\n previousRows = rows;\n const next = attach(rows);\n const unchanged =\n next.length === previous.length && next.every((row, index) => row === previous[index]);\n if (!unchanged) previous = next;\n return previous;\n },\n };\n}\n\n/**\n * Shared by every in-eval transcript: entries are keyed by source message, so\n * none outlives the wire state holding it and threads cannot collide.\n */\nconst inEvalTranscriptCache = createExternalMessageConversionCache();\nconst IN_EVAL_TRANSCRIPT_METADATA = {};\nconst inEvalTranscripts = new WeakMap<\n readonly LangChainMessage[],\n { readonly running: boolean; readonly messages: readonly ThreadMessage[] }\n>();\n\nconst NO_MESSAGES: readonly ThreadMessage[] = [];\n\n/**\n * An in-eval dispatch's live transcript (a `ptc_subagents` entry's `messages`)\n * as a nested thread, converted like a `task` lane's. Memoized per transcript\n * array; a step the lane did not change keeps its converted object. A\n * transcript the converter refuses reads as empty.\n */\nexport function convertSubagentTranscript(\n transcript: readonly LangChainMessage[],\n running: boolean\n): readonly ThreadMessage[] {\n const cached = inEvalTranscripts.get(transcript);\n if (cached !== undefined && cached.running === running) return cached.messages;\n let messages: readonly ThreadMessage[] = NO_MESSAGES;\n try {\n messages = convertExternalMessages(\n transcriptSources(transcript),\n convertDeepAgentLangChainMessage,\n running,\n IN_EVAL_TRANSCRIPT_METADATA,\n inEvalTranscriptCache\n ).map(markPreliminaryToolResults);\n } catch (error) {\n reportUnconvertibleTranscript(transcript, error);\n }\n inEvalTranscripts.set(transcript, { running, messages });\n return messages;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,8 @@ const TASK_TOOL_NAME = "task";
|
|
|
4
4
|
const EXECUTE_TOOL_NAME = "execute";
|
|
5
5
|
const WRITE_FILE_TOOL_NAME = "write_file";
|
|
6
6
|
const EDIT_FILE_TOOL_NAME = "edit_file";
|
|
7
|
+
const SUBMIT_FEEDBACK_TOOL_NAME = "submit_feedback";
|
|
8
|
+
const QUIET_TOOL_NAMES = /* @__PURE__ */ new Set([SUBMIT_FEEDBACK_TOOL_NAME]);
|
|
7
9
|
const START_ASYNC_TASK_TOOL_NAME = "start_async_task";
|
|
8
10
|
const CHECK_ASYNC_TASK_TOOL_NAME = "check_async_task";
|
|
9
11
|
const UPDATE_ASYNC_TASK_TOOL_NAME = "update_async_task";
|
|
@@ -64,7 +66,9 @@ exports.CANCEL_ASYNC_TASK_TOOL_NAME = CANCEL_ASYNC_TASK_TOOL_NAME;
|
|
|
64
66
|
exports.CHECK_ASYNC_TASK_TOOL_NAME = CHECK_ASYNC_TASK_TOOL_NAME;
|
|
65
67
|
exports.EDIT_FILE_TOOL_NAME = EDIT_FILE_TOOL_NAME;
|
|
66
68
|
exports.EXECUTE_TOOL_NAME = EXECUTE_TOOL_NAME;
|
|
69
|
+
exports.QUIET_TOOL_NAMES = QUIET_TOOL_NAMES;
|
|
67
70
|
exports.START_ASYNC_TASK_TOOL_NAME = START_ASYNC_TASK_TOOL_NAME;
|
|
71
|
+
exports.SUBMIT_FEEDBACK_TOOL_NAME = SUBMIT_FEEDBACK_TOOL_NAME;
|
|
68
72
|
exports.TASK_TOOL_NAME = TASK_TOOL_NAME;
|
|
69
73
|
exports.UPDATE_ASYNC_TASK_TOOL_NAME = UPDATE_ASYNC_TASK_TOOL_NAME;
|
|
70
74
|
exports.WRITE_FILE_TOOL_NAME = WRITE_FILE_TOOL_NAME;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-names.cjs","sources":["../../../../agent-runtime/src/tool-names.ts"],"sourcesContent":["/**\n * Names of the deepagents built-in tools as they appear on the wire, plus the\n * shared arg shapes / display derivations keyed on those names. One home so\n * tool cards, activity panels, and transcript renderers on every surface agree\n * on which call is which.\n */\nimport type { LangChainToolCall } from \"@assistant-ui/react-langgraph\";\n\n/** The built-in deepagents delegation tool; a `task` call spawns a subagent. */\nexport const TASK_TOOL_NAME = \"task\";\n/** The QuickJS code-interpreter tool (langchain_quickjs middleware). */\nexport const EVAL_TOOL_NAME = \"eval\";\n/** The built-in deepagents sandbox shell tool. */\nexport const EXECUTE_TOOL_NAME = \"execute\";\n/**\n * The Athena studio code executors: sandboxed Python whose SDK calls the\n * backend records as PTC sub-calls, exactly like `execute` / `eval`.\n */\nexport const RUN_PYTHON_CODE_TOOL_NAME = \"run_python_code\";\nexport const EXECUTE_PRESENTATION_CODE_TOOL_NAME = \"execute_presentation_code\";\nexport const EXECUTE_SPREADSHEET_CODE_TOOL_NAME = \"execute_spreadsheet_code\";\nexport const EXECUTE_WORD_DOCUMENT_CODE_TOOL_NAME = \"execute_word_document_code\";\n/** Every tool whose ToolMessage artifact may carry promoted PTC sub-calls. */\nexport const PTC_PARENT_TOOL_NAMES: ReadonlySet<string> = new Set<string>([\n EXECUTE_TOOL_NAME,\n EVAL_TOOL_NAME,\n RUN_PYTHON_CODE_TOOL_NAME,\n EXECUTE_PRESENTATION_CODE_TOOL_NAME,\n EXECUTE_SPREADSHEET_CODE_TOOL_NAME,\n EXECUTE_WORD_DOCUMENT_CODE_TOOL_NAME,\n]);\n/** The built-in deepagents filesystem write/edit tools. */\nexport const WRITE_FILE_TOOL_NAME = \"write_file\";\nexport const EDIT_FILE_TOOL_NAME = \"edit_file\";\n\n/**\n * The deepagents async-subagent tools (ADR 0006 coding workers ride these):\n * `start_async_task` launches a remote task, `check_async_task` polls it,\n * `update_async_task` sends a follow-up, `cancel_async_task` interrupts it.\n */\nexport const START_ASYNC_TASK_TOOL_NAME = \"start_async_task\";\nexport const CHECK_ASYNC_TASK_TOOL_NAME = \"check_async_task\";\nexport const UPDATE_ASYNC_TASK_TOOL_NAME = \"update_async_task\";\nexport const CANCEL_ASYNC_TASK_TOOL_NAME = \"cancel_async_task\";\nexport const LIST_ASYNC_TASKS_TOOL_NAME = \"list_async_tasks\";\nexport const ASYNC_TASK_TOOL_NAMES = [\n START_ASYNC_TASK_TOOL_NAME,\n CHECK_ASYNC_TASK_TOOL_NAME,\n UPDATE_ASYNC_TASK_TOOL_NAME,\n CANCEL_ASYNC_TASK_TOOL_NAME,\n LIST_ASYNC_TASKS_TOOL_NAME,\n] as const;\n\n/**\n * Args shape shared by `write_file` / `edit_file` (single source of truth for\n * both the tool card and the changed-files panel). `write_file` carries the\n * whole `content`; `edit_file` carries the `old_string` → `new_string`\n * replacement instead (deepagents `WriteFileSchema` / `EditFileSchema`).\n */\nexport type FileToolArgs = {\n file_path?: string;\n path?: string;\n content?: string;\n old_string?: string;\n new_string?: string;\n replace_all?: boolean;\n /** Cross-asset targeting: the sandbox-backed asset the tool acted on. */\n sandbox_asset_id?: string | null;\n};\n\n/**\n * Human-salient one-liner for a nested tool call — the argument a person would\n * glance at (`execute` → the command, file tools → the path, searches → the\n * query). Empty string when nothing salient exists; capped so the ticker line\n * can't blow out the card.\n */\nexport function salientToolCallDetail(toolCall: LangChainToolCall): string {\n const args = (toolCall.args ?? {}) as Record<string, unknown>;\n const firstString = (...keys: string[]): string => {\n for (const key of keys) {\n const value = args[key];\n if (typeof value === \"string\" && value) return value;\n }\n return \"\";\n };\n let detail: string;\n switch (toolCall.name) {\n case EXECUTE_TOOL_NAME:\n detail = firstString(\"command\");\n break;\n case WRITE_FILE_TOOL_NAME:\n case EDIT_FILE_TOOL_NAME:\n detail = firstString(\"file_path\", \"path\");\n break;\n case \"read_file\":\n case \"ls\":\n detail = firstString(\"file_path\", \"path\");\n break;\n case \"write_todos\": {\n const todos = args.todos;\n detail = Array.isArray(todos)\n ? `${todos.length} step${todos.length === 1 ? \"\" : \"s\"}`\n : \"\";\n break;\n }\n case TASK_TOOL_NAME:\n case START_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"description\", \"subagent_type\");\n break;\n case UPDATE_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"message\", \"task_id\");\n break;\n case CHECK_ASYNC_TASK_TOOL_NAME:\n case CANCEL_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"task_id\");\n break;\n default:\n detail = firstString(\n \"query\",\n \"pattern\",\n \"command\",\n \"path\",\n \"file_path\",\n \"url\",\n \"question\",\n );\n }\n const clean = detail.replace(/\\s+/g, \" \").trim();\n return clean.length > 80 ? `${clean.slice(0, 79)}…` : clean;\n}\n"],"names":[],"mappings":";;AASO,MAAM,iBAAiB;AAIvB,MAAM,oBAAoB;AAmB1B,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;AAO5B,MAAM,6BAA6B;AACnC,MAAM,6BAA6B;AACnC,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AAiCpC,SAAS,sBAAsB,UAAqC;AACzE,QAAM,OAAQ,SAAS,QAAQ,CAAA;AAC/B,QAAM,cAAc,IAAI,SAA2B;AACjD,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,KAAK,GAAG;AACtB,UAAI,OAAO,UAAU,YAAY,MAAO,QAAO;AAAA,IACjD;AACA,WAAO;AAAA,EACT;AACA,MAAI;AACJ,UAAQ,SAAS,MAAA;AAAA,IACf,KAAK;AACH,eAAS,YAAY,SAAS;AAC9B;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,aAAa,MAAM;AACxC;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,aAAa,MAAM;AACxC;AAAA,IACF,KAAK,eAAe;AAClB,YAAM,QAAQ,KAAK;AACnB,eAAS,MAAM,QAAQ,KAAK,IACxB,GAAG,MAAM,MAAM,QAAQ,MAAM,WAAW,IAAI,KAAK,GAAG,KACpD;AACJ;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,eAAe,eAAe;AACnD;AAAA,IACF,KAAK;AACH,eAAS,YAAY,WAAW,SAAS;AACzC;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,SAAS;AAC9B;AAAA,IACF;AACE,eAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,EACF;AAEJ,QAAM,QAAQ,OAAO,QAAQ,QAAQ,GAAG,EAAE,KAAA;AAC1C,SAAO,MAAM,SAAS,KAAK,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM;AACxD
|
|
1
|
+
{"version":3,"file":"tool-names.cjs","sources":["../../../../agent-runtime/src/tool-names.ts"],"sourcesContent":["/**\n * Names of the deepagents built-in tools as they appear on the wire, plus the\n * shared arg shapes / display derivations keyed on those names. One home so\n * tool cards, activity panels, and transcript renderers on every surface agree\n * on which call is which.\n */\nimport type { LangChainToolCall } from \"@assistant-ui/react-langgraph\";\n\n/** The built-in deepagents delegation tool; a `task` call spawns a subagent. */\nexport const TASK_TOOL_NAME = \"task\";\n/** The QuickJS code-interpreter tool (langchain_quickjs middleware). */\nexport const EVAL_TOOL_NAME = \"eval\";\n/** The built-in deepagents sandbox shell tool. */\nexport const EXECUTE_TOOL_NAME = \"execute\";\n/**\n * The Athena studio code executors: sandboxed Python whose SDK calls the\n * backend records as PTC sub-calls, exactly like `execute` / `eval`.\n */\nexport const RUN_PYTHON_CODE_TOOL_NAME = \"run_python_code\";\nexport const EXECUTE_PRESENTATION_CODE_TOOL_NAME = \"execute_presentation_code\";\nexport const EXECUTE_SPREADSHEET_CODE_TOOL_NAME = \"execute_spreadsheet_code\";\nexport const EXECUTE_WORD_DOCUMENT_CODE_TOOL_NAME = \"execute_word_document_code\";\n/** Every tool whose ToolMessage artifact may carry promoted PTC sub-calls. */\nexport const PTC_PARENT_TOOL_NAMES: ReadonlySet<string> = new Set<string>([\n EXECUTE_TOOL_NAME,\n EVAL_TOOL_NAME,\n RUN_PYTHON_CODE_TOOL_NAME,\n EXECUTE_PRESENTATION_CODE_TOOL_NAME,\n EXECUTE_SPREADSHEET_CODE_TOOL_NAME,\n EXECUTE_WORD_DOCUMENT_CODE_TOOL_NAME,\n]);\n/** The built-in deepagents filesystem write/edit tools. */\nexport const WRITE_FILE_TOOL_NAME = \"write_file\";\nexport const EDIT_FILE_TOOL_NAME = \"edit_file\";\n\n/**\n * Tools whose calls render nothing in chat: no card, no status line. The call\n * and its result stay in the stored transcript and in exports; only the\n * conversation view leaves them out (see `convertDeepAgentMessage`).\n */\nexport const SUBMIT_FEEDBACK_TOOL_NAME = \"submit_feedback\";\nexport const QUIET_TOOL_NAMES: ReadonlySet<string> = new Set([SUBMIT_FEEDBACK_TOOL_NAME]);\n\n/**\n * The deepagents async-subagent tools (ADR 0006 coding workers ride these):\n * `start_async_task` launches a remote task, `check_async_task` polls it,\n * `update_async_task` sends a follow-up, `cancel_async_task` interrupts it.\n */\nexport const START_ASYNC_TASK_TOOL_NAME = \"start_async_task\";\nexport const CHECK_ASYNC_TASK_TOOL_NAME = \"check_async_task\";\nexport const UPDATE_ASYNC_TASK_TOOL_NAME = \"update_async_task\";\nexport const CANCEL_ASYNC_TASK_TOOL_NAME = \"cancel_async_task\";\nexport const LIST_ASYNC_TASKS_TOOL_NAME = \"list_async_tasks\";\nexport const ASYNC_TASK_TOOL_NAMES = [\n START_ASYNC_TASK_TOOL_NAME,\n CHECK_ASYNC_TASK_TOOL_NAME,\n UPDATE_ASYNC_TASK_TOOL_NAME,\n CANCEL_ASYNC_TASK_TOOL_NAME,\n LIST_ASYNC_TASKS_TOOL_NAME,\n] as const;\n\n/**\n * Args shape shared by `write_file` / `edit_file` (single source of truth for\n * both the tool card and the changed-files panel). `write_file` carries the\n * whole `content`; `edit_file` carries the `old_string` → `new_string`\n * replacement instead (deepagents `WriteFileSchema` / `EditFileSchema`).\n */\nexport type FileToolArgs = {\n file_path?: string;\n path?: string;\n content?: string;\n old_string?: string;\n new_string?: string;\n replace_all?: boolean;\n /** Cross-asset targeting: the sandbox-backed asset the tool acted on. */\n sandbox_asset_id?: string | null;\n};\n\n/**\n * Human-salient one-liner for a nested tool call — the argument a person would\n * glance at (`execute` → the command, file tools → the path, searches → the\n * query). Empty string when nothing salient exists; capped so the ticker line\n * can't blow out the card.\n */\nexport function salientToolCallDetail(toolCall: LangChainToolCall): string {\n const args = (toolCall.args ?? {}) as Record<string, unknown>;\n const firstString = (...keys: string[]): string => {\n for (const key of keys) {\n const value = args[key];\n if (typeof value === \"string\" && value) return value;\n }\n return \"\";\n };\n let detail: string;\n switch (toolCall.name) {\n case EXECUTE_TOOL_NAME:\n detail = firstString(\"command\");\n break;\n case WRITE_FILE_TOOL_NAME:\n case EDIT_FILE_TOOL_NAME:\n detail = firstString(\"file_path\", \"path\");\n break;\n case \"read_file\":\n case \"ls\":\n detail = firstString(\"file_path\", \"path\");\n break;\n case \"write_todos\": {\n const todos = args.todos;\n detail = Array.isArray(todos)\n ? `${todos.length} step${todos.length === 1 ? \"\" : \"s\"}`\n : \"\";\n break;\n }\n case TASK_TOOL_NAME:\n case START_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"description\", \"subagent_type\");\n break;\n case UPDATE_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"message\", \"task_id\");\n break;\n case CHECK_ASYNC_TASK_TOOL_NAME:\n case CANCEL_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"task_id\");\n break;\n default:\n detail = firstString(\n \"query\",\n \"pattern\",\n \"command\",\n \"path\",\n \"file_path\",\n \"url\",\n \"question\",\n );\n }\n const clean = detail.replace(/\\s+/g, \" \").trim();\n return clean.length > 80 ? `${clean.slice(0, 79)}…` : clean;\n}\n"],"names":[],"mappings":";;AASO,MAAM,iBAAiB;AAIvB,MAAM,oBAAoB;AAmB1B,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;AAO5B,MAAM,4BAA4B;AAClC,MAAM,mBAAwC,oBAAI,IAAI,CAAC,yBAAyB,CAAC;AAOjF,MAAM,6BAA6B;AACnC,MAAM,6BAA6B;AACnC,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AAiCpC,SAAS,sBAAsB,UAAqC;AACzE,QAAM,OAAQ,SAAS,QAAQ,CAAA;AAC/B,QAAM,cAAc,IAAI,SAA2B;AACjD,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,KAAK,GAAG;AACtB,UAAI,OAAO,UAAU,YAAY,MAAO,QAAO;AAAA,IACjD;AACA,WAAO;AAAA,EACT;AACA,MAAI;AACJ,UAAQ,SAAS,MAAA;AAAA,IACf,KAAK;AACH,eAAS,YAAY,SAAS;AAC9B;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,aAAa,MAAM;AACxC;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,aAAa,MAAM;AACxC;AAAA,IACF,KAAK,eAAe;AAClB,YAAM,QAAQ,KAAK;AACnB,eAAS,MAAM,QAAQ,KAAK,IACxB,GAAG,MAAM,MAAM,QAAQ,MAAM,WAAW,IAAI,KAAK,GAAG,KACpD;AACJ;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,eAAe,eAAe;AACnD;AAAA,IACF,KAAK;AACH,eAAS,YAAY,WAAW,SAAS;AACzC;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,SAAS;AAC9B;AAAA,IACF;AACE,eAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,EACF;AAEJ,QAAM,QAAQ,OAAO,QAAQ,QAAQ,GAAG,EAAE,KAAA;AAC1C,SAAO,MAAM,SAAS,KAAK,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM;AACxD;;;;;;;;;;;;"}
|
|
@@ -24,6 +24,13 @@ export declare const PTC_PARENT_TOOL_NAMES: ReadonlySet<string>;
|
|
|
24
24
|
/** The built-in deepagents filesystem write/edit tools. */
|
|
25
25
|
export declare const WRITE_FILE_TOOL_NAME = "write_file";
|
|
26
26
|
export declare const EDIT_FILE_TOOL_NAME = "edit_file";
|
|
27
|
+
/**
|
|
28
|
+
* Tools whose calls render nothing in chat: no card, no status line. The call
|
|
29
|
+
* and its result stay in the stored transcript and in exports; only the
|
|
30
|
+
* conversation view leaves them out (see `convertDeepAgentMessage`).
|
|
31
|
+
*/
|
|
32
|
+
export declare const SUBMIT_FEEDBACK_TOOL_NAME = "submit_feedback";
|
|
33
|
+
export declare const QUIET_TOOL_NAMES: ReadonlySet<string>;
|
|
27
34
|
/**
|
|
28
35
|
* The deepagents async-subagent tools (ADR 0006 coding workers ride these):
|
|
29
36
|
* `start_async_task` launches a remote task, `check_async_task` polls it,
|
|
@@ -2,6 +2,8 @@ const TASK_TOOL_NAME = "task";
|
|
|
2
2
|
const EXECUTE_TOOL_NAME = "execute";
|
|
3
3
|
const WRITE_FILE_TOOL_NAME = "write_file";
|
|
4
4
|
const EDIT_FILE_TOOL_NAME = "edit_file";
|
|
5
|
+
const SUBMIT_FEEDBACK_TOOL_NAME = "submit_feedback";
|
|
6
|
+
const QUIET_TOOL_NAMES = /* @__PURE__ */ new Set([SUBMIT_FEEDBACK_TOOL_NAME]);
|
|
5
7
|
const START_ASYNC_TASK_TOOL_NAME = "start_async_task";
|
|
6
8
|
const CHECK_ASYNC_TASK_TOOL_NAME = "check_async_task";
|
|
7
9
|
const UPDATE_ASYNC_TASK_TOOL_NAME = "update_async_task";
|
|
@@ -63,7 +65,9 @@ export {
|
|
|
63
65
|
CHECK_ASYNC_TASK_TOOL_NAME,
|
|
64
66
|
EDIT_FILE_TOOL_NAME,
|
|
65
67
|
EXECUTE_TOOL_NAME,
|
|
68
|
+
QUIET_TOOL_NAMES,
|
|
66
69
|
START_ASYNC_TASK_TOOL_NAME,
|
|
70
|
+
SUBMIT_FEEDBACK_TOOL_NAME,
|
|
67
71
|
TASK_TOOL_NAME,
|
|
68
72
|
UPDATE_ASYNC_TASK_TOOL_NAME,
|
|
69
73
|
WRITE_FILE_TOOL_NAME,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-names.js","sources":["../../../../agent-runtime/src/tool-names.ts"],"sourcesContent":["/**\n * Names of the deepagents built-in tools as they appear on the wire, plus the\n * shared arg shapes / display derivations keyed on those names. One home so\n * tool cards, activity panels, and transcript renderers on every surface agree\n * on which call is which.\n */\nimport type { LangChainToolCall } from \"@assistant-ui/react-langgraph\";\n\n/** The built-in deepagents delegation tool; a `task` call spawns a subagent. */\nexport const TASK_TOOL_NAME = \"task\";\n/** The QuickJS code-interpreter tool (langchain_quickjs middleware). */\nexport const EVAL_TOOL_NAME = \"eval\";\n/** The built-in deepagents sandbox shell tool. */\nexport const EXECUTE_TOOL_NAME = \"execute\";\n/**\n * The Athena studio code executors: sandboxed Python whose SDK calls the\n * backend records as PTC sub-calls, exactly like `execute` / `eval`.\n */\nexport const RUN_PYTHON_CODE_TOOL_NAME = \"run_python_code\";\nexport const EXECUTE_PRESENTATION_CODE_TOOL_NAME = \"execute_presentation_code\";\nexport const EXECUTE_SPREADSHEET_CODE_TOOL_NAME = \"execute_spreadsheet_code\";\nexport const EXECUTE_WORD_DOCUMENT_CODE_TOOL_NAME = \"execute_word_document_code\";\n/** Every tool whose ToolMessage artifact may carry promoted PTC sub-calls. */\nexport const PTC_PARENT_TOOL_NAMES: ReadonlySet<string> = new Set<string>([\n EXECUTE_TOOL_NAME,\n EVAL_TOOL_NAME,\n RUN_PYTHON_CODE_TOOL_NAME,\n EXECUTE_PRESENTATION_CODE_TOOL_NAME,\n EXECUTE_SPREADSHEET_CODE_TOOL_NAME,\n EXECUTE_WORD_DOCUMENT_CODE_TOOL_NAME,\n]);\n/** The built-in deepagents filesystem write/edit tools. */\nexport const WRITE_FILE_TOOL_NAME = \"write_file\";\nexport const EDIT_FILE_TOOL_NAME = \"edit_file\";\n\n/**\n * The deepagents async-subagent tools (ADR 0006 coding workers ride these):\n * `start_async_task` launches a remote task, `check_async_task` polls it,\n * `update_async_task` sends a follow-up, `cancel_async_task` interrupts it.\n */\nexport const START_ASYNC_TASK_TOOL_NAME = \"start_async_task\";\nexport const CHECK_ASYNC_TASK_TOOL_NAME = \"check_async_task\";\nexport const UPDATE_ASYNC_TASK_TOOL_NAME = \"update_async_task\";\nexport const CANCEL_ASYNC_TASK_TOOL_NAME = \"cancel_async_task\";\nexport const LIST_ASYNC_TASKS_TOOL_NAME = \"list_async_tasks\";\nexport const ASYNC_TASK_TOOL_NAMES = [\n START_ASYNC_TASK_TOOL_NAME,\n CHECK_ASYNC_TASK_TOOL_NAME,\n UPDATE_ASYNC_TASK_TOOL_NAME,\n CANCEL_ASYNC_TASK_TOOL_NAME,\n LIST_ASYNC_TASKS_TOOL_NAME,\n] as const;\n\n/**\n * Args shape shared by `write_file` / `edit_file` (single source of truth for\n * both the tool card and the changed-files panel). `write_file` carries the\n * whole `content`; `edit_file` carries the `old_string` → `new_string`\n * replacement instead (deepagents `WriteFileSchema` / `EditFileSchema`).\n */\nexport type FileToolArgs = {\n file_path?: string;\n path?: string;\n content?: string;\n old_string?: string;\n new_string?: string;\n replace_all?: boolean;\n /** Cross-asset targeting: the sandbox-backed asset the tool acted on. */\n sandbox_asset_id?: string | null;\n};\n\n/**\n * Human-salient one-liner for a nested tool call — the argument a person would\n * glance at (`execute` → the command, file tools → the path, searches → the\n * query). Empty string when nothing salient exists; capped so the ticker line\n * can't blow out the card.\n */\nexport function salientToolCallDetail(toolCall: LangChainToolCall): string {\n const args = (toolCall.args ?? {}) as Record<string, unknown>;\n const firstString = (...keys: string[]): string => {\n for (const key of keys) {\n const value = args[key];\n if (typeof value === \"string\" && value) return value;\n }\n return \"\";\n };\n let detail: string;\n switch (toolCall.name) {\n case EXECUTE_TOOL_NAME:\n detail = firstString(\"command\");\n break;\n case WRITE_FILE_TOOL_NAME:\n case EDIT_FILE_TOOL_NAME:\n detail = firstString(\"file_path\", \"path\");\n break;\n case \"read_file\":\n case \"ls\":\n detail = firstString(\"file_path\", \"path\");\n break;\n case \"write_todos\": {\n const todos = args.todos;\n detail = Array.isArray(todos)\n ? `${todos.length} step${todos.length === 1 ? \"\" : \"s\"}`\n : \"\";\n break;\n }\n case TASK_TOOL_NAME:\n case START_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"description\", \"subagent_type\");\n break;\n case UPDATE_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"message\", \"task_id\");\n break;\n case CHECK_ASYNC_TASK_TOOL_NAME:\n case CANCEL_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"task_id\");\n break;\n default:\n detail = firstString(\n \"query\",\n \"pattern\",\n \"command\",\n \"path\",\n \"file_path\",\n \"url\",\n \"question\",\n );\n }\n const clean = detail.replace(/\\s+/g, \" \").trim();\n return clean.length > 80 ? `${clean.slice(0, 79)}…` : clean;\n}\n"],"names":[],"mappings":"AASO,MAAM,iBAAiB;AAIvB,MAAM,oBAAoB;AAmB1B,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;AAO5B,MAAM,6BAA6B;AACnC,MAAM,6BAA6B;AACnC,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AAiCpC,SAAS,sBAAsB,UAAqC;AACzE,QAAM,OAAQ,SAAS,QAAQ,CAAA;AAC/B,QAAM,cAAc,IAAI,SAA2B;AACjD,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,KAAK,GAAG;AACtB,UAAI,OAAO,UAAU,YAAY,MAAO,QAAO;AAAA,IACjD;AACA,WAAO;AAAA,EACT;AACA,MAAI;AACJ,UAAQ,SAAS,MAAA;AAAA,IACf,KAAK;AACH,eAAS,YAAY,SAAS;AAC9B;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,aAAa,MAAM;AACxC;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,aAAa,MAAM;AACxC;AAAA,IACF,KAAK,eAAe;AAClB,YAAM,QAAQ,KAAK;AACnB,eAAS,MAAM,QAAQ,KAAK,IACxB,GAAG,MAAM,MAAM,QAAQ,MAAM,WAAW,IAAI,KAAK,GAAG,KACpD;AACJ;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,eAAe,eAAe;AACnD;AAAA,IACF,KAAK;AACH,eAAS,YAAY,WAAW,SAAS;AACzC;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,SAAS;AAC9B;AAAA,IACF;AACE,eAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,EACF;AAEJ,QAAM,QAAQ,OAAO,QAAQ,QAAQ,GAAG,EAAE,KAAA;AAC1C,SAAO,MAAM,SAAS,KAAK,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM;AACxD;"}
|
|
1
|
+
{"version":3,"file":"tool-names.js","sources":["../../../../agent-runtime/src/tool-names.ts"],"sourcesContent":["/**\n * Names of the deepagents built-in tools as they appear on the wire, plus the\n * shared arg shapes / display derivations keyed on those names. One home so\n * tool cards, activity panels, and transcript renderers on every surface agree\n * on which call is which.\n */\nimport type { LangChainToolCall } from \"@assistant-ui/react-langgraph\";\n\n/** The built-in deepagents delegation tool; a `task` call spawns a subagent. */\nexport const TASK_TOOL_NAME = \"task\";\n/** The QuickJS code-interpreter tool (langchain_quickjs middleware). */\nexport const EVAL_TOOL_NAME = \"eval\";\n/** The built-in deepagents sandbox shell tool. */\nexport const EXECUTE_TOOL_NAME = \"execute\";\n/**\n * The Athena studio code executors: sandboxed Python whose SDK calls the\n * backend records as PTC sub-calls, exactly like `execute` / `eval`.\n */\nexport const RUN_PYTHON_CODE_TOOL_NAME = \"run_python_code\";\nexport const EXECUTE_PRESENTATION_CODE_TOOL_NAME = \"execute_presentation_code\";\nexport const EXECUTE_SPREADSHEET_CODE_TOOL_NAME = \"execute_spreadsheet_code\";\nexport const EXECUTE_WORD_DOCUMENT_CODE_TOOL_NAME = \"execute_word_document_code\";\n/** Every tool whose ToolMessage artifact may carry promoted PTC sub-calls. */\nexport const PTC_PARENT_TOOL_NAMES: ReadonlySet<string> = new Set<string>([\n EXECUTE_TOOL_NAME,\n EVAL_TOOL_NAME,\n RUN_PYTHON_CODE_TOOL_NAME,\n EXECUTE_PRESENTATION_CODE_TOOL_NAME,\n EXECUTE_SPREADSHEET_CODE_TOOL_NAME,\n EXECUTE_WORD_DOCUMENT_CODE_TOOL_NAME,\n]);\n/** The built-in deepagents filesystem write/edit tools. */\nexport const WRITE_FILE_TOOL_NAME = \"write_file\";\nexport const EDIT_FILE_TOOL_NAME = \"edit_file\";\n\n/**\n * Tools whose calls render nothing in chat: no card, no status line. The call\n * and its result stay in the stored transcript and in exports; only the\n * conversation view leaves them out (see `convertDeepAgentMessage`).\n */\nexport const SUBMIT_FEEDBACK_TOOL_NAME = \"submit_feedback\";\nexport const QUIET_TOOL_NAMES: ReadonlySet<string> = new Set([SUBMIT_FEEDBACK_TOOL_NAME]);\n\n/**\n * The deepagents async-subagent tools (ADR 0006 coding workers ride these):\n * `start_async_task` launches a remote task, `check_async_task` polls it,\n * `update_async_task` sends a follow-up, `cancel_async_task` interrupts it.\n */\nexport const START_ASYNC_TASK_TOOL_NAME = \"start_async_task\";\nexport const CHECK_ASYNC_TASK_TOOL_NAME = \"check_async_task\";\nexport const UPDATE_ASYNC_TASK_TOOL_NAME = \"update_async_task\";\nexport const CANCEL_ASYNC_TASK_TOOL_NAME = \"cancel_async_task\";\nexport const LIST_ASYNC_TASKS_TOOL_NAME = \"list_async_tasks\";\nexport const ASYNC_TASK_TOOL_NAMES = [\n START_ASYNC_TASK_TOOL_NAME,\n CHECK_ASYNC_TASK_TOOL_NAME,\n UPDATE_ASYNC_TASK_TOOL_NAME,\n CANCEL_ASYNC_TASK_TOOL_NAME,\n LIST_ASYNC_TASKS_TOOL_NAME,\n] as const;\n\n/**\n * Args shape shared by `write_file` / `edit_file` (single source of truth for\n * both the tool card and the changed-files panel). `write_file` carries the\n * whole `content`; `edit_file` carries the `old_string` → `new_string`\n * replacement instead (deepagents `WriteFileSchema` / `EditFileSchema`).\n */\nexport type FileToolArgs = {\n file_path?: string;\n path?: string;\n content?: string;\n old_string?: string;\n new_string?: string;\n replace_all?: boolean;\n /** Cross-asset targeting: the sandbox-backed asset the tool acted on. */\n sandbox_asset_id?: string | null;\n};\n\n/**\n * Human-salient one-liner for a nested tool call — the argument a person would\n * glance at (`execute` → the command, file tools → the path, searches → the\n * query). Empty string when nothing salient exists; capped so the ticker line\n * can't blow out the card.\n */\nexport function salientToolCallDetail(toolCall: LangChainToolCall): string {\n const args = (toolCall.args ?? {}) as Record<string, unknown>;\n const firstString = (...keys: string[]): string => {\n for (const key of keys) {\n const value = args[key];\n if (typeof value === \"string\" && value) return value;\n }\n return \"\";\n };\n let detail: string;\n switch (toolCall.name) {\n case EXECUTE_TOOL_NAME:\n detail = firstString(\"command\");\n break;\n case WRITE_FILE_TOOL_NAME:\n case EDIT_FILE_TOOL_NAME:\n detail = firstString(\"file_path\", \"path\");\n break;\n case \"read_file\":\n case \"ls\":\n detail = firstString(\"file_path\", \"path\");\n break;\n case \"write_todos\": {\n const todos = args.todos;\n detail = Array.isArray(todos)\n ? `${todos.length} step${todos.length === 1 ? \"\" : \"s\"}`\n : \"\";\n break;\n }\n case TASK_TOOL_NAME:\n case START_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"description\", \"subagent_type\");\n break;\n case UPDATE_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"message\", \"task_id\");\n break;\n case CHECK_ASYNC_TASK_TOOL_NAME:\n case CANCEL_ASYNC_TASK_TOOL_NAME:\n detail = firstString(\"task_id\");\n break;\n default:\n detail = firstString(\n \"query\",\n \"pattern\",\n \"command\",\n \"path\",\n \"file_path\",\n \"url\",\n \"question\",\n );\n }\n const clean = detail.replace(/\\s+/g, \" \").trim();\n return clean.length > 80 ? `${clean.slice(0, 79)}…` : clean;\n}\n"],"names":[],"mappings":"AASO,MAAM,iBAAiB;AAIvB,MAAM,oBAAoB;AAmB1B,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;AAO5B,MAAM,4BAA4B;AAClC,MAAM,mBAAwC,oBAAI,IAAI,CAAC,yBAAyB,CAAC;AAOjF,MAAM,6BAA6B;AACnC,MAAM,6BAA6B;AACnC,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AAiCpC,SAAS,sBAAsB,UAAqC;AACzE,QAAM,OAAQ,SAAS,QAAQ,CAAA;AAC/B,QAAM,cAAc,IAAI,SAA2B;AACjD,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,KAAK,GAAG;AACtB,UAAI,OAAO,UAAU,YAAY,MAAO,QAAO;AAAA,IACjD;AACA,WAAO;AAAA,EACT;AACA,MAAI;AACJ,UAAQ,SAAS,MAAA;AAAA,IACf,KAAK;AACH,eAAS,YAAY,SAAS;AAC9B;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,aAAa,MAAM;AACxC;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,aAAa,MAAM;AACxC;AAAA,IACF,KAAK,eAAe;AAClB,YAAM,QAAQ,KAAK;AACnB,eAAS,MAAM,QAAQ,KAAK,IACxB,GAAG,MAAM,MAAM,QAAQ,MAAM,WAAW,IAAI,KAAK,GAAG,KACpD;AACJ;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,eAAe,eAAe;AACnD;AAAA,IACF,KAAK;AACH,eAAS,YAAY,WAAW,SAAS;AACzC;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,eAAS,YAAY,SAAS;AAC9B;AAAA,IACF;AACE,eAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,EACF;AAEJ,QAAM,QAAQ,OAAO,QAAQ,QAAQ,GAAG,EAAE,KAAA;AAC1C,SAAO,MAAM,SAAS,KAAK,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM;AACxD;"}
|
|
@@ -5,6 +5,7 @@ const errors = require("./errors.cjs");
|
|
|
5
5
|
const modelContext = require("./model-context.cjs");
|
|
6
6
|
const quotaSafeLocalStorageSession = require("./quota-safe-local-storage-session.cjs");
|
|
7
7
|
const sendRecovery = require("./send-recovery.cjs");
|
|
8
|
+
const sessionLaneScope = require("./session-lane-scope.cjs");
|
|
8
9
|
const threadExtras = require("./thread-extras.cjs");
|
|
9
10
|
const useDeepAgentConfig = require("./use-deep-agent-config.cjs");
|
|
10
11
|
const useDeepAgentThread = require("./use-deep-agent-thread.cjs");
|
|
@@ -77,7 +78,22 @@ function useDeepAgentRuntime(options) {
|
|
|
77
78
|
failureStampsRef.current = session === false || session.kind === "pending" ? void 0 : session.kind === "local-storage" ? canPersistLocalSession ? () => quotaSafeLocalStorageSession.readPersistedFailedSendStamps(session.key(threadOptions.threadId)) : void 0 : () => quotaSafeLocalStorageSession.readFailedSendStampsFromRecord(loadSessionRecord(session.store));
|
|
78
79
|
const sessionPending = session !== false && session.kind === "pending";
|
|
79
80
|
const attachKey = sessionPending ? pendingSessionAttachKey(threadOptions.attachKey) : threadOptions.attachKey;
|
|
80
|
-
const
|
|
81
|
+
const laneKey = session !== false && session.kind === "local-storage" ? session.key : null;
|
|
82
|
+
const [laneEpochState, setLaneEpochState] = react.useState(
|
|
83
|
+
() => sessionLaneScope.nextSessionLaneEpoch({
|
|
84
|
+
previous: sessionLaneScope.INITIAL_SESSION_LANE_EPOCH,
|
|
85
|
+
threadId: threadOptions.threadId,
|
|
86
|
+
key: laneKey
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
const nextLaneEpochState = sessionLaneScope.nextSessionLaneEpoch({
|
|
90
|
+
previous: laneEpochState,
|
|
91
|
+
threadId: threadOptions.threadId,
|
|
92
|
+
key: laneKey
|
|
93
|
+
});
|
|
94
|
+
if (nextLaneEpochState !== laneEpochState) setLaneEpochState(nextLaneEpochState);
|
|
95
|
+
const laneEpoch = nextLaneEpochState.epoch;
|
|
96
|
+
const attachId = useDeepAgentThread.deepAgentAttachId({ threadId: threadOptions.threadId, attachKey, laneEpoch });
|
|
81
97
|
const sendRecovery$1 = react.useMemo(
|
|
82
98
|
() => sendRecovery.createDeepAgentSendRecovery({
|
|
83
99
|
lane: () => {
|
|
@@ -99,6 +115,7 @@ function useDeepAgentRuntime(options) {
|
|
|
99
115
|
const thread = useDeepAgentThread.useDeepAgentThread({
|
|
100
116
|
...threadOptions,
|
|
101
117
|
...attachKey !== void 0 && { attachKey },
|
|
118
|
+
laneEpoch,
|
|
102
119
|
// Parked without a message (a host suspension, when one applies, wins):
|
|
103
120
|
// nothing dials and nothing journals until the lane is known. What paints
|
|
104
121
|
// the first frame is the real attach's to report, not the park's.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-deep-agent-runtime.cjs","sources":["../../../../agent-runtime/src/use-deep-agent-runtime.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { isStatewireChannelTeardown } from './errors';\nimport { type DeepAgentModelContextHost, registerDeepAgentRunConfig } from './model-context';\nimport {\n isStorageQuotaError,\n QuotaSafeLocalStorageSession,\n readFailedSendStampsFromRecord,\n readPersistedFailedSendStamps,\n type SessionStorageDegradation,\n} from './quota-safe-local-storage-session';\nimport { createDeepAgentSendRecovery } from './send-recovery';\nimport type { StatewireTransportOptions } from './statewire-transport';\nimport { readStatewireFailedSendLane } from './thread-extras';\nimport { isConfigSyncUnavailableRejection } from './use-deep-agent-config';\nimport { useDeepAgentThread, type UseDeepAgentThreadOptions } from './use-deep-agent-thread';\n\n/** The thread element `useDeepAgentThread` produces — what the host's `useAui`\n * consumes (`useAui({ thread })`). */\nexport type DeepAgentThreadElement = ReturnType<typeof useDeepAgentThread>;\n\n/**\n * Durable command-lane persistence for the thread — where the lane identity and\n * the commands still awaiting a durable verdict live, so a reload resumes the\n * lane instead of minting a new client.\n *\n * - `local-storage`: browser hosts. Backed by {@link QuotaSafeLocalStorageSession}\n * (the upstream `LocalStorageSession` contract with Athena keys, legacy\n * metadata, and retention). A storage failure after mounting — the origin\n * out of quota after statewire's own sweep, a lockdown-mode write refusal —\n * degrades the session to memory-only persistence and is reported through\n * `onStorageDegraded` (default: one console warning); it never stops the\n * connection. Guarded by a shared availability probe: SSR, storage-blocked\n * iframes, and locked-down browsers keep the session process-local, exactly\n * as before the seam existed, while an origin that is merely full mounts the\n * session and lets it degrade. `key` scopes the stored lane — a thread id is\n * only meaningful against the backend (and, where relevant, principal) that\n * minted it, so include the sync-host URL.\n * - `session-store`: React Native hosts pass a hydrated library store backed\n * by AsyncStorage. Its synchronous `load()` is\n * also the send-recovery freshness gate's view of the persisted failed lane,\n * so a store that persists `failed` gets the same 24 h staleness rule as the\n * browser hosts.\n * - `pending`: the principal the lane is scoped by is not known yet (the\n * host's signed-in user is still loading). See {@link DeepAgentPendingSession}.\n * - `false`: process-local session (a reload mints a new client).\n *\n * A `local-storage` `key` must name the same lane for the whole life of the\n * thread's mount. The statewire session that mounts on a lane keeps the client\n * id, lane clocks and journal it loaded there; moved to another key, it carries\n * them along, and the record it left keeps the same client id with a stale\n * clock and whatever it had journaled. Every later mount that loads that record\n * resends its journal and stamps early commands from its clock, at or below the\n * watermark the server keeps for the id, so the server absorbs each one as a\n * replay and answers `expired`. A host whose principal arrives after mount\n * passes `pending` until it does, never a stand-in key.\n */\nexport type DeepAgentSessionPersistence =\n | {\n kind: 'local-storage';\n key: (threadId: string) => string;\n onStorageDegraded?: (degradation: SessionStorageDegradation) => void;\n }\n | { kind: 'session-store'; store: NonNullable<StatewireTransportOptions['sessionStore']> }\n | DeepAgentPendingSession\n | false;\n\n/**\n * The durable lane's principal is not known yet. A lane belongs to one\n * principal and a mounted session cannot move to another lane (see\n * {@link DeepAgentSessionPersistence}), so nothing is mounted, nothing is\n * journaled and the transport stays parked — no dial — until the host passes\n * the real persistence. The park is an attach identity of its own: the attach\n * that follows makes every per-attach decision afresh (the remembered-snapshot\n * seed, the pooled hello, which the park leaves in the pool) and dials the real\n * lane once, as the thread's first attach. The park carries no connection\n * message, so it reads like a never-started chat rather than a paused one; a\n * host suspension, when one applies, wins.\n */\nexport type DeepAgentPendingSession = { kind: 'pending' };\n\n/**\n * The attach key a pending-lane park runs under. It differs from every key the\n * host passes, so the real attach after it is a new attach identity.\n */\nexport function pendingSessionAttachKey(attachKey: string | number | undefined): string {\n return `session-pending:${attachKey ?? ''}`;\n}\n\n/** Structural view of the browser storage global — this package compiles\n * without the DOM lib (React Native honesty; see `globals.d.ts`). */\ntype StorageGlobals = {\n localStorage?: {\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n };\n navigator?: { locks?: unknown };\n};\n\n/**\n * Whether a localStorage-backed session can persist here. A write probe, not a\n * `typeof` check: storage-blocked iframes (SDK embeds under third-party-storage\n * blocking) throw from the `localStorage` accessor itself, and lockdown-mode\n * origins throw from `setItem` — each of those must mean \"stay process-local\",\n * never a crash at mount. An origin that is merely out of quota still reads\n * and takes locks, so it mounts the session: {@link QuotaSafeLocalStorageSession}\n * degrades that to memory-only persistence and recovers when room frees,\n * where staying process-local would give up cross-tab sharing for the whole\n * page load over a condition that clears.\n *\n * The Web Locks API is probed too: the session storage serializes the shared\n * record across tabs through `navigator.locks` and refuses to mount without it\n * (the same precondition as the vendor `LocalStorageSession`), so an origin\n * with localStorage but no Web Locks — older WebViews, Safari before 15.4 —\n * must also stay process-local instead of throwing at mount.\n */\nexport function canPersistDeepAgentSession(): boolean {\n try {\n const globals = globalThis as StorageGlobals;\n if (!globals.navigator?.locks) return false;\n const storage = globals.localStorage;\n if (!storage) return false;\n const probeKey = '__athena_statewire_session_probe__';\n try {\n storage.setItem(probeKey, probeKey);\n storage.removeItem(probeKey);\n } catch (error) {\n return isStorageQuotaError(error);\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** A host session store's current record, or `null` when the read throws —\n * the recovery gate must never take the runtime down over a storage fault. */\nfunction loadSessionRecord(\n store: NonNullable<StatewireTransportOptions['sessionStore']>\n): ReturnType<NonNullable<StatewireTransportOptions['sessionStore']>['load']> | null {\n try {\n return store.load();\n } catch {\n return null;\n }\n}\n\n/**\n * The shared tail of every surface's transport error policy: channel teardown\n * is lifecycle noise (logged at debug), as is `config_sync_unavailable` — only\n * `config/*` writes emit it, and the host's config sync already handles it\n * through its own command promise (regionally-pinned workspaces run on\n * local-only config); everything else is a real transport error. Hosts with their own error chain\n * (paywall, telemetry, send-failure stores) end it with this so the fallback\n * logging stays uniform per surface.\n */\nexport function logUnhandledStatewireError(surface: string, error: Error): void {\n if (isStatewireChannelTeardown(error)) {\n console.debug(`[${surface}] statewire channel teardown:`, error);\n return;\n }\n if (isConfigSyncUnavailableRejection(error)) {\n console.debug(`[${surface}] config sync unavailable (handled by config sync):`, error);\n return;\n }\n console.error(`[${surface}] statewire transport error:`, error);\n}\n\n/**\n * The imperative thread-state slice of an assistant-ui client — structural so\n * web and native `useAui` results both satisfy it. The send-recovery layer\n * reads the statewire thread extras through it (the sanctioned imperative\n * pattern: `aui.thread().getState()` inside callbacks/effects, never render).\n */\nexport interface DeepAgentThreadStateClientHost {\n thread: () => { getState: () => { extras?: unknown } };\n subscribe: (listener: () => void) => () => void;\n}\n\n/** Deep-agent runConfig registration on the ambient model context (see\n * {@link registerDeepAgentRunConfig}). */\nexport interface DeepAgentRunConfigRegistration {\n /**\n * Builds the `runConfig` that drives the run. The core pulls it when the\n * model context notifies (mount and each key change); the server reads that\n * synced value at command admission. The per-render ref makes each pull use\n * the latest closure but does not trigger a pull itself.\n */\n get: () => Record<string, unknown>;\n /**\n * Re-registers the provider and notifies model-context sync when this value\n * changes (reference equality). Include getter inputs that can change while\n * mounted; they reach the server only after a key change triggers a pull.\n */\n key?: unknown;\n}\n\nexport interface UseDeepAgentRuntimeOptions<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n> extends Omit<UseDeepAgentThreadOptions, 'storage' | 'sessionStore'> {\n /** Host surface tag for the shared fallback error logging (e.g.\n * `'AthenaDeepAgent'`, `'AthenaSDK'`, `'ChromeStatewire'`). */\n surface: string;\n /** Durable command-lane persistence; defaults to process-local. */\n session?: DeepAgentSessionPersistence;\n /**\n * Builds the host's assistant-ui client from the thread element — pass the\n * platform `useAui` (web hosts from `@assistant-ui/react`, React Native\n * hosts from `@assistant-ui/react-native`; this package imports neither).\n * Called unconditionally on every render, so it MUST be (or call) a hook\n * following the rules of hooks — define it at module scope with a `use*`\n * name: `const useHostClient = (thread: DeepAgentThreadElement) =>\n * useAui({ thread })`.\n */\n useClient: (thread: DeepAgentThreadElement) => TClient;\n /** Registers the deep-agent runConfig on the client's model context. Hosts\n * that register elsewhere (e.g. a bridge component mounted under the\n * provider) omit it. */\n runConfig?: DeepAgentRunConfigRegistration;\n}\n\n/**\n * The configurable deep-agent statewire runtime — the one hook every Athena\n * chat surface (Olympus, `@athenaintel/react` SDK, Chrome extension, Office\n * add-in, mobile) mounts its assistant-ui client through.\n *\n * Composes the pieces every surface previously wired by hand:\n * `useDeepAgentThread` (attach lifecycle, auth parking, lane projection,\n * connection/running observers), durable session persistence with the shared\n * availability guard, transport-level send recovery (failed sends the server\n * never judged auto-retry under their original message id — `send-recovery.ts`),\n * the platform client via `useClient`, and the\n * runConfig model-context registration on provider notifications. Everything\n * host-specific — auth headers, run-config building, error policy, adapters,\n * diagnostics recorders — stays an option, so per-surface behavior is\n * configuration, not a fork.\n */\nexport function useDeepAgentRuntime<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n>(options: UseDeepAgentRuntimeOptions<TClient>): TClient {\n const {\n surface,\n session = false,\n useClient,\n runConfig,\n onError,\n onStateChange,\n onCommandChange,\n ...threadOptions\n } = options;\n\n // Probed once per mount, like every browser host did individually: an origin\n // that cannot persist stays process-local for the mount's lifetime.\n const [canPersistLocalSession] = useState(canPersistDeepAgentSession);\n\n // Per-render refs so the transport never remounts on a new closure identity\n // and always reads the latest policy.\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const surfaceRef = useRef(surface);\n surfaceRef.current = surface;\n const onStateChangeRef = useRef(onStateChange);\n onStateChangeRef.current = onStateChange;\n const onCommandChangeRef = useRef(onCommandChange);\n onCommandChangeRef.current = onCommandChange;\n const diagnosticsRef = useRef(threadOptions.diagnostics);\n diagnosticsRef.current = threadOptions.diagnostics;\n const clientRef = useRef<TClient | null>(null);\n\n // Where the persisted session record lives, for the recovery freshness gate\n // (read per sweep — undefined means \"no persistence, everything is fresh\").\n const failureStampsRef = useRef<(() => ReadonlyMap<string, number> | null) | undefined>(\n undefined\n );\n failureStampsRef.current =\n session === false || session.kind === 'pending'\n ? undefined\n : session.kind === 'local-storage'\n ? canPersistLocalSession\n ? () => readPersistedFailedSendStamps(session.key(threadOptions.threadId))\n : undefined\n : () => readFailedSendStampsFromRecord(loadSessionRecord(session.store));\n\n // A pending lane parks the thread under an attach identity of its own (see\n // `DeepAgentPendingSession`), so the attach once the lane is known is new.\n const sessionPending = session !== false && session.kind === 'pending';\n const attachKey = sessionPending\n ? pendingSessionAttachKey(threadOptions.attachKey)\n : threadOptions.attachKey;\n\n // Send recovery, keyed by attach identity exactly like the thread's session\n // (a switch or re-attach gets fresh hydration gating and retry budgets). The\n // controller re-dispatches failed sends the server never judged; see\n // `send-recovery.ts` for the contract.\n const attachId =\n attachKey === undefined ? threadOptions.threadId : `${threadOptions.threadId}:${attachKey}`;\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by attach identity on purpose; every other input is read through a per-render ref.\n const sendRecovery = useMemo(\n () =>\n createDeepAgentSendRecovery({\n lane: () => {\n const client = clientRef.current;\n if (!client) return null;\n return readStatewireFailedSendLane(client.thread().getState().extras);\n },\n failureStamps: () => failureStampsRef.current?.() ?? null,\n diagnostics: () => diagnosticsRef.current,\n }),\n [attachId]\n );\n useEffect(() => () => sendRecovery.dispose(), [sendRecovery]);\n const sendRecoveryRef = useRef(sendRecovery);\n sendRecoveryRef.current = sendRecovery;\n\n const thread = useDeepAgentThread({\n ...threadOptions,\n ...(attachKey !== undefined && { attachKey }),\n // Parked without a message (a host suspension, when one applies, wins):\n // nothing dials and nothing journals until the lane is known. What paints\n // the first frame is the real attach's to report, not the park's.\n ...(sessionPending && {\n suspended: threadOptions.suspended || {},\n onAttachSource: undefined,\n }),\n // A pooled dial for the thread is the real attach's to take: a park that\n // only waits for the lane neither takes nor discards it. A host suspension\n // still discards it, as it does without a pending lane.\n ...(sessionPending && !threadOptions.suspended && { prewarmPool: undefined }),\n ...(session !== false &&\n session.kind === 'local-storage' &&\n canPersistLocalSession && {\n storage: {\n session: QuotaSafeLocalStorageSession({\n threadId: threadOptions.threadId,\n key: session.key,\n ...(session.onStorageDegraded === undefined\n ? {}\n : { onStorageDegraded: session.onStorageDegraded }),\n }),\n },\n }),\n ...(session !== false && session.kind === 'session-store' && { sessionStore: session.store }),\n onStateChange: (state, ctx) => {\n sendRecoveryRef.current.observeStateChange(state);\n onStateChangeRef.current?.(state, ctx);\n },\n onCommandChange: (update, ctx) => {\n sendRecoveryRef.current.observeCommand(update);\n onCommandChangeRef.current?.(update, ctx);\n },\n onError: (error) => {\n const handler = onErrorRef.current;\n if (handler) {\n handler(error);\n return;\n }\n logUnhandledStatewireError(surfaceRef.current, error);\n },\n });\n\n const client = useClient(thread);\n clientRef.current = client;\n\n // A locked shared journal can publish failed history after onCommandChange,\n // with no canonical state change to wake recovery. Observe the client that\n // owns the visible failed lane; the controller guards in-flight retries.\n useEffect(() => {\n const unsubscribe = client.subscribe(() => sendRecovery.observeFailedLaneChange());\n sendRecovery.observeFailedLaneChange();\n return unsubscribe;\n }, [client, sendRecovery]);\n\n // Commands travel bare, so the runConfig that drives the run rides the\n // ambient model context: `contextSync: 'command-send'` mirrors it to the\n // server, which reads `config.runConfig` at command admission. The core pulls\n // on model-context notifications; the per-render ref ensures each pull uses\n // the latest closure but does not trigger a pull itself.\n const getRunConfigRef = useRef(runConfig?.get);\n getRunConfigRef.current = runConfig?.get;\n const hasRunConfig = runConfig !== undefined;\n const runConfigKey = runConfig?.key;\n useEffect(() => {\n if (!hasRunConfig) return undefined;\n return registerDeepAgentRunConfig(client, () => getRunConfigRef.current?.() ?? {});\n }, [client, hasRunConfig, runConfigKey]);\n\n return client;\n}\n\n"],"names":["isStorageQuotaError","isStatewireChannelTeardown","isConfigSyncUnavailableRejection","useState","useRef","readPersistedFailedSendStamps","readFailedSendStampsFromRecord","sendRecovery","useMemo","createDeepAgentSendRecovery","client","readStatewireFailedSendLane","useEffect","useDeepAgentThread","QuotaSafeLocalStorageSession","registerDeepAgentRunConfig"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-deep-agent-runtime.cjs","sources":["../../../../agent-runtime/src/use-deep-agent-runtime.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { isStatewireChannelTeardown } from './errors';\nimport { type DeepAgentModelContextHost, registerDeepAgentRunConfig } from './model-context';\nimport {\n isStorageQuotaError,\n QuotaSafeLocalStorageSession,\n readFailedSendStampsFromRecord,\n readPersistedFailedSendStamps,\n type SessionStorageDegradation,\n} from './quota-safe-local-storage-session';\nimport { createDeepAgentSendRecovery } from './send-recovery';\nimport { INITIAL_SESSION_LANE_EPOCH, nextSessionLaneEpoch } from './session-lane-scope';\nimport type { StatewireTransportOptions } from './statewire-transport';\nimport { readStatewireFailedSendLane } from './thread-extras';\nimport { isConfigSyncUnavailableRejection } from './use-deep-agent-config';\nimport {\n deepAgentAttachId,\n useDeepAgentThread,\n type UseDeepAgentThreadOptions,\n} from './use-deep-agent-thread';\n\n/** The thread element `useDeepAgentThread` produces — what the host's `useAui`\n * consumes (`useAui({ thread })`). */\nexport type DeepAgentThreadElement = ReturnType<typeof useDeepAgentThread>;\n\n/**\n * Durable command-lane persistence for the thread — where the lane identity and\n * the commands still awaiting a durable verdict live, so a reload resumes the\n * lane instead of minting a new client.\n *\n * - `local-storage`: browser hosts. Backed by {@link QuotaSafeLocalStorageSession}\n * (the upstream `LocalStorageSession` contract with Athena keys, legacy\n * metadata, and retention). A storage failure after mounting — the origin\n * out of quota after statewire's own sweep, a lockdown-mode write refusal —\n * degrades the session to memory-only persistence and is reported through\n * `onStorageDegraded` (default: one console warning); it never stops the\n * connection. Guarded by a shared availability probe: SSR, storage-blocked\n * iframes, and locked-down browsers keep the session process-local, exactly\n * as before the seam existed, while an origin that is merely full mounts the\n * session and lets it degrade. `key` scopes the stored lane — a thread id is\n * only meaningful against the backend (and, where relevant, principal) that\n * minted it, so include the sync-host URL.\n * - `session-store`: React Native hosts pass a hydrated library store backed\n * by AsyncStorage. Its synchronous `load()` is\n * also the send-recovery freshness gate's view of the persisted failed lane,\n * so a store that persists `failed` gets the same 24 h staleness rule as the\n * browser hosts.\n * - `pending`: the principal the lane is scoped by is not known yet (the\n * host's signed-in user is still loading). See {@link DeepAgentPendingSession}.\n * - `false`: process-local session (a reload mints a new client).\n *\n * A `local-storage` `key` names one principal's lanes (and one backend's). The\n * statewire session that mounts on a lane keeps the client id, lane clocks and\n * journal it loaded there, and everything in that journal is sent under\n * whatever credential the transport holds at its next attach. So a mounted\n * session never moves to another key: when the key starts naming another\n * principal's lanes for the same thread (another account signed in under a\n * mounted chat), the runtime starts a new lane epoch\n * (`UseDeepAgentThreadOptions['laneEpoch']`). The whole statewire thread\n * remounts on the new principal's lane and attaches afresh, as that lane's own\n * client, with nothing remembered or pooled from before. The previous\n * principal's record stays where it is, with its client id and whatever it had\n * journaled and not yet delivered: that principal's next mount on this browser\n * resends it as them, and the retention sweep retires it otherwise. It is never\n * sent as anyone else. A key for the same principal (a token refresh, a fresh\n * `key` function) and a thread switch keep the lane as it is. A host whose\n * principal arrives after mount passes `pending` until it does, never a\n * stand-in key: moving off a stand-in would be a principal change, a second\n * attach.\n */\nexport type DeepAgentSessionPersistence =\n | {\n kind: 'local-storage';\n key: (threadId: string) => string;\n onStorageDegraded?: (degradation: SessionStorageDegradation) => void;\n }\n | { kind: 'session-store'; store: NonNullable<StatewireTransportOptions['sessionStore']> }\n | DeepAgentPendingSession\n | false;\n\n/**\n * The durable lane's principal is not known yet. A lane belongs to one\n * principal and a mounted session cannot move to another lane (see\n * {@link DeepAgentSessionPersistence}), so nothing is mounted, nothing is\n * journaled and the transport stays parked — no dial — until the host passes\n * the real persistence. The park is an attach identity of its own: the attach\n * that follows makes every per-attach decision afresh (the remembered-snapshot\n * seed, the pooled hello, which the park leaves in the pool) and dials the real\n * lane once, as the thread's first attach. The park carries no connection\n * message, so it reads like a never-started chat rather than a paused one; a\n * host suspension, when one applies, wins.\n */\nexport type DeepAgentPendingSession = { kind: 'pending' };\n\n/**\n * The attach key a pending-lane park runs under. It differs from every key the\n * host passes, so the real attach after it is a new attach identity.\n */\nexport function pendingSessionAttachKey(attachKey: string | number | undefined): string {\n return `session-pending:${attachKey ?? ''}`;\n}\n\n/** Structural view of the browser storage global — this package compiles\n * without the DOM lib (React Native honesty; see `globals.d.ts`). */\ntype StorageGlobals = {\n localStorage?: {\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n };\n navigator?: { locks?: unknown };\n};\n\n/**\n * Whether a localStorage-backed session can persist here. A write probe, not a\n * `typeof` check: storage-blocked iframes (SDK embeds under third-party-storage\n * blocking) throw from the `localStorage` accessor itself, and lockdown-mode\n * origins throw from `setItem` — each of those must mean \"stay process-local\",\n * never a crash at mount. An origin that is merely out of quota still reads\n * and takes locks, so it mounts the session: {@link QuotaSafeLocalStorageSession}\n * degrades that to memory-only persistence and recovers when room frees,\n * where staying process-local would give up cross-tab sharing for the whole\n * page load over a condition that clears.\n *\n * The Web Locks API is probed too: the session storage serializes the shared\n * record across tabs through `navigator.locks` and refuses to mount without it\n * (the same precondition as the vendor `LocalStorageSession`), so an origin\n * with localStorage but no Web Locks — older WebViews, Safari before 15.4 —\n * must also stay process-local instead of throwing at mount.\n */\nexport function canPersistDeepAgentSession(): boolean {\n try {\n const globals = globalThis as StorageGlobals;\n if (!globals.navigator?.locks) return false;\n const storage = globals.localStorage;\n if (!storage) return false;\n const probeKey = '__athena_statewire_session_probe__';\n try {\n storage.setItem(probeKey, probeKey);\n storage.removeItem(probeKey);\n } catch (error) {\n return isStorageQuotaError(error);\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** A host session store's current record, or `null` when the read throws —\n * the recovery gate must never take the runtime down over a storage fault. */\nfunction loadSessionRecord(\n store: NonNullable<StatewireTransportOptions['sessionStore']>\n): ReturnType<NonNullable<StatewireTransportOptions['sessionStore']>['load']> | null {\n try {\n return store.load();\n } catch {\n return null;\n }\n}\n\n/**\n * The shared tail of every surface's transport error policy: channel teardown\n * is lifecycle noise (logged at debug), as is `config_sync_unavailable` — only\n * `config/*` writes emit it, and the host's config sync already handles it\n * through its own command promise (regionally-pinned workspaces run on\n * local-only config); everything else is a real transport error. Hosts with their own error chain\n * (paywall, telemetry, send-failure stores) end it with this so the fallback\n * logging stays uniform per surface.\n */\nexport function logUnhandledStatewireError(surface: string, error: Error): void {\n if (isStatewireChannelTeardown(error)) {\n console.debug(`[${surface}] statewire channel teardown:`, error);\n return;\n }\n if (isConfigSyncUnavailableRejection(error)) {\n console.debug(`[${surface}] config sync unavailable (handled by config sync):`, error);\n return;\n }\n console.error(`[${surface}] statewire transport error:`, error);\n}\n\n/**\n * The imperative thread-state slice of an assistant-ui client — structural so\n * web and native `useAui` results both satisfy it. The send-recovery layer\n * reads the statewire thread extras through it (the sanctioned imperative\n * pattern: `aui.thread().getState()` inside callbacks/effects, never render).\n */\nexport interface DeepAgentThreadStateClientHost {\n thread: () => { getState: () => { extras?: unknown } };\n subscribe: (listener: () => void) => () => void;\n}\n\n/** Deep-agent runConfig registration on the ambient model context (see\n * {@link registerDeepAgentRunConfig}). */\nexport interface DeepAgentRunConfigRegistration {\n /**\n * Builds the `runConfig` that drives the run. The core pulls it when the\n * model context notifies (mount and each key change); the server reads that\n * synced value at command admission. The per-render ref makes each pull use\n * the latest closure but does not trigger a pull itself.\n */\n get: () => Record<string, unknown>;\n /**\n * Re-registers the provider and notifies model-context sync when this value\n * changes (reference equality). Include getter inputs that can change while\n * mounted; they reach the server only after a key change triggers a pull.\n */\n key?: unknown;\n}\n\nexport interface UseDeepAgentRuntimeOptions<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n> extends Omit<UseDeepAgentThreadOptions, 'storage' | 'sessionStore' | 'laneEpoch'> {\n /** Host surface tag for the shared fallback error logging (e.g.\n * `'AthenaDeepAgent'`, `'AthenaSDK'`, `'ChromeStatewire'`). */\n surface: string;\n /** Durable command-lane persistence; defaults to process-local. */\n session?: DeepAgentSessionPersistence;\n /**\n * Builds the host's assistant-ui client from the thread element — pass the\n * platform `useAui` (web hosts from `@assistant-ui/react`, React Native\n * hosts from `@assistant-ui/react-native`; this package imports neither).\n * Called unconditionally on every render, so it MUST be (or call) a hook\n * following the rules of hooks — define it at module scope with a `use*`\n * name: `const useHostClient = (thread: DeepAgentThreadElement) =>\n * useAui({ thread })`.\n */\n useClient: (thread: DeepAgentThreadElement) => TClient;\n /** Registers the deep-agent runConfig on the client's model context. Hosts\n * that register elsewhere (e.g. a bridge component mounted under the\n * provider) omit it. */\n runConfig?: DeepAgentRunConfigRegistration;\n}\n\n/**\n * The configurable deep-agent statewire runtime — the one hook every Athena\n * chat surface (Olympus, `@athenaintel/react` SDK, Chrome extension, Office\n * add-in, mobile) mounts its assistant-ui client through.\n *\n * Composes the pieces every surface previously wired by hand:\n * `useDeepAgentThread` (attach lifecycle, auth parking, lane projection,\n * connection/running observers), durable session persistence with the shared\n * availability guard, transport-level send recovery (failed sends the server\n * never judged auto-retry under their original message id — `send-recovery.ts`),\n * the platform client via `useClient`, and the\n * runConfig model-context registration on provider notifications. Everything\n * host-specific — auth headers, run-config building, error policy, adapters,\n * diagnostics recorders — stays an option, so per-surface behavior is\n * configuration, not a fork.\n */\nexport function useDeepAgentRuntime<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n>(options: UseDeepAgentRuntimeOptions<TClient>): TClient {\n const {\n surface,\n session = false,\n useClient,\n runConfig,\n onError,\n onStateChange,\n onCommandChange,\n ...threadOptions\n } = options;\n\n // Probed once per mount, like every browser host did individually: an origin\n // that cannot persist stays process-local for the mount's lifetime.\n const [canPersistLocalSession] = useState(canPersistDeepAgentSession);\n\n // Per-render refs so the transport never remounts on a new closure identity\n // and always reads the latest policy.\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const surfaceRef = useRef(surface);\n surfaceRef.current = surface;\n const onStateChangeRef = useRef(onStateChange);\n onStateChangeRef.current = onStateChange;\n const onCommandChangeRef = useRef(onCommandChange);\n onCommandChangeRef.current = onCommandChange;\n const diagnosticsRef = useRef(threadOptions.diagnostics);\n diagnosticsRef.current = threadOptions.diagnostics;\n const clientRef = useRef<TClient | null>(null);\n\n // Where the persisted session record lives, for the recovery freshness gate\n // (read per sweep — undefined means \"no persistence, everything is fresh\").\n const failureStampsRef = useRef<(() => ReadonlyMap<string, number> | null) | undefined>(\n undefined\n );\n failureStampsRef.current =\n session === false || session.kind === 'pending'\n ? undefined\n : session.kind === 'local-storage'\n ? canPersistLocalSession\n ? () => readPersistedFailedSendStamps(session.key(threadOptions.threadId))\n : undefined\n : () => readFailedSendStampsFromRecord(loadSessionRecord(session.store));\n\n // A pending lane parks the thread under an attach identity of its own (see\n // `DeepAgentPendingSession`), so the attach once the lane is known is new.\n const sessionPending = session !== false && session.kind === 'pending';\n const attachKey = sessionPending\n ? pendingSessionAttachKey(threadOptions.attachKey)\n : threadOptions.attachKey;\n\n // The lane's scope, as the host's `key` names it: whose lanes this mount\n // runs on. A key that starts naming another principal's lanes starts a new\n // lane epoch, which remounts the whole thread on the new lane instead of\n // moving the mounted session there (see `DeepAgentSessionPersistence`).\n // Tracked whether or not this origin can persist: a process-local session\n // holds the previous principal's journal in memory just the same. A pending\n // lane keeps the last known scope, so a sign-out and a sign-in as someone\n // else with a gap between them is still a change.\n const laneKey = session !== false && session.kind === 'local-storage' ? session.key : null;\n const [laneEpochState, setLaneEpochState] = useState(() =>\n nextSessionLaneEpoch({\n previous: INITIAL_SESSION_LANE_EPOCH,\n threadId: threadOptions.threadId,\n key: laneKey,\n })\n );\n const nextLaneEpochState = nextSessionLaneEpoch({\n previous: laneEpochState,\n threadId: threadOptions.threadId,\n key: laneKey,\n });\n // React's derived-state update: re-renders at once with the new epoch, and\n // this render already uses it, so no render pairs the new key with the\n // mounted thread.\n if (nextLaneEpochState !== laneEpochState) setLaneEpochState(nextLaneEpochState);\n const laneEpoch = nextLaneEpochState.epoch;\n\n // Send recovery, keyed by attach identity exactly like the thread's session\n // (a switch, re-attach or new lane epoch gets fresh hydration gating and\n // retry budgets, so it never re-dispatches a previous principal's failed\n // sends). The controller re-dispatches failed sends the server never judged;\n // see `send-recovery.ts` for the contract.\n const attachId = deepAgentAttachId({ threadId: threadOptions.threadId, attachKey, laneEpoch });\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by attach identity on purpose; every other input is read through a per-render ref.\n const sendRecovery = useMemo(\n () =>\n createDeepAgentSendRecovery({\n lane: () => {\n const client = clientRef.current;\n if (!client) return null;\n return readStatewireFailedSendLane(client.thread().getState().extras);\n },\n failureStamps: () => failureStampsRef.current?.() ?? null,\n diagnostics: () => diagnosticsRef.current,\n }),\n [attachId]\n );\n useEffect(() => () => sendRecovery.dispose(), [sendRecovery]);\n const sendRecoveryRef = useRef(sendRecovery);\n sendRecoveryRef.current = sendRecovery;\n\n const thread = useDeepAgentThread({\n ...threadOptions,\n ...(attachKey !== undefined && { attachKey }),\n laneEpoch,\n // Parked without a message (a host suspension, when one applies, wins):\n // nothing dials and nothing journals until the lane is known. What paints\n // the first frame is the real attach's to report, not the park's.\n ...(sessionPending && {\n suspended: threadOptions.suspended || {},\n onAttachSource: undefined,\n }),\n // A pooled dial for the thread is the real attach's to take: a park that\n // only waits for the lane neither takes nor discards it. A host suspension\n // still discards it, as it does without a pending lane.\n ...(sessionPending && !threadOptions.suspended && { prewarmPool: undefined }),\n ...(session !== false &&\n session.kind === 'local-storage' &&\n canPersistLocalSession && {\n storage: {\n session: QuotaSafeLocalStorageSession({\n threadId: threadOptions.threadId,\n key: session.key,\n ...(session.onStorageDegraded === undefined\n ? {}\n : { onStorageDegraded: session.onStorageDegraded }),\n }),\n },\n }),\n ...(session !== false && session.kind === 'session-store' && { sessionStore: session.store }),\n onStateChange: (state, ctx) => {\n sendRecoveryRef.current.observeStateChange(state);\n onStateChangeRef.current?.(state, ctx);\n },\n onCommandChange: (update, ctx) => {\n sendRecoveryRef.current.observeCommand(update);\n onCommandChangeRef.current?.(update, ctx);\n },\n onError: (error) => {\n const handler = onErrorRef.current;\n if (handler) {\n handler(error);\n return;\n }\n logUnhandledStatewireError(surfaceRef.current, error);\n },\n });\n\n const client = useClient(thread);\n clientRef.current = client;\n\n // A locked shared journal can publish failed history after onCommandChange,\n // with no canonical state change to wake recovery. Observe the client that\n // owns the visible failed lane; the controller guards in-flight retries.\n useEffect(() => {\n const unsubscribe = client.subscribe(() => sendRecovery.observeFailedLaneChange());\n sendRecovery.observeFailedLaneChange();\n return unsubscribe;\n }, [client, sendRecovery]);\n\n // Commands travel bare, so the runConfig that drives the run rides the\n // ambient model context: `contextSync: 'command-send'` mirrors it to the\n // server, which reads `config.runConfig` at command admission. The core pulls\n // on model-context notifications; the per-render ref ensures each pull uses\n // the latest closure but does not trigger a pull itself.\n const getRunConfigRef = useRef(runConfig?.get);\n getRunConfigRef.current = runConfig?.get;\n const hasRunConfig = runConfig !== undefined;\n const runConfigKey = runConfig?.key;\n useEffect(() => {\n if (!hasRunConfig) return undefined;\n return registerDeepAgentRunConfig(client, () => getRunConfigRef.current?.() ?? {});\n }, [client, hasRunConfig, runConfigKey]);\n\n return client;\n}\n\n"],"names":["isStorageQuotaError","isStatewireChannelTeardown","isConfigSyncUnavailableRejection","useState","useRef","readPersistedFailedSendStamps","readFailedSendStampsFromRecord","nextSessionLaneEpoch","INITIAL_SESSION_LANE_EPOCH","deepAgentAttachId","sendRecovery","useMemo","createDeepAgentSendRecovery","client","readStatewireFailedSendLane","useEffect","useDeepAgentThread","QuotaSafeLocalStorageSession","registerDeepAgentRunConfig"],"mappings":"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"}
|