@ai-matrx/agents 0.37.1 → 0.38.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/CHANGELOG.md +24 -0
- package/dist/content-transfer/index.cjs.map +1 -1
- package/dist/content-transfer/index.js.map +1 -1
- package/dist/content-transfer/react/index.cjs.map +1 -1
- package/dist/content-transfer/react/index.js.map +1 -1
- package/dist/generated/api-types.cjs.map +1 -1
- package/dist/generated/api-types.d.cts +4 -4
- package/dist/generated/api-types.d.ts +4 -4
- package/dist/index.cjs +49 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +49 -7
- package/dist/index.js.map +1 -1
- package/dist/{keys.generated-DL-5LCNz.d.cts → keys.generated-DxqNwOgr.d.cts} +4 -4
- package/dist/{keys.generated-DL-5LCNz.d.ts → keys.generated-DxqNwOgr.d.ts} +4 -4
- package/dist/mandates/index.cjs +2 -2
- package/dist/mandates/index.cjs.map +1 -1
- package/dist/mandates/index.d.cts +2 -2
- package/dist/mandates/index.d.ts +2 -2
- package/dist/mandates/index.js +2 -2
- package/dist/mandates/index.js.map +1 -1
- package/dist/matrx/index.cjs +49 -7
- package/dist/matrx/index.cjs.map +1 -1
- package/dist/matrx/index.d.cts +35 -3
- package/dist/matrx/index.d.ts +35 -3
- package/dist/matrx/index.js +49 -7
- package/dist/matrx/index.js.map +1 -1
- package/dist/portable/mcp.cjs.map +1 -1
- package/dist/portable/mcp.js.map +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js.map +1 -1
- package/generated/api-types.ts +4 -4
- package/mandates/snapshots/keys.0.37.2.json +653 -0
- package/mandates/snapshots/keys.0.38.0.json +653 -0
- package/mandates/snapshots/keys.0.38.1.json +653 -0
- package/mandates/snapshots/keys.0.38.2.json +653 -0
- package/package.json +2 -2
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../react/use-agent-run.ts","../../projection/request.ts","../../matrx/transport.ts","../../matrx/client.ts","../../matrx/backend-errors.ts","../../stream/ndjson.ts","../../matrx/org-context.ts","../../matrx/call.ts","../../matrx/internal.ts","../../stream/sse.ts","../../matrx/operations.ts","../../matrx/run.ts","../../react/use-follow-runtime-operation.ts"],"sourcesContent":["/**\n * `useAgentRun` — the package's run/stream hook: start, continue, and cancel\n * an agent run over an injected `MatrxTransport`, with the streamed state\n * exposed as the parity-proven request projection\n * (`@ai-matrx/agents/projection/request` — the ONE event interpreter).\n *\n * The hook owns the whole loop the hosts used to hand-roll: the streaming\n * call, folding every NDJSON envelope through `projectAgentEvent`, terminal\n * status/error interpretation, unmount/stale-run teardown, and server-side\n * cancel by the run's `X-Request-ID` (the ONLY id the cancel route accepts).\n * The host injects only the transport — typically the production\n * `createMatrxTransport` from `@ai-matrx/agents/matrx`.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n createAgentRequestProjection,\n projectAgentEvent,\n type AgentRequestProjection,\n} from \"../projection/request\";\nimport {\n cancelAgentRun,\n continueAgentConversation,\n normalizeMatrxError,\n startAgentRun,\n type MatrxAgentStartRequest,\n type MatrxCallError,\n type MatrxCancelResponse,\n type MatrxConversationContinueRequest,\n type MatrxRunHandle,\n type MatrxStreamCallOptions,\n type MatrxTransport,\n} from \"../matrx/index\";\n\nexport type AgentRunPhase =\n | \"idle\"\n | \"starting\"\n | \"streaming\"\n | \"complete\"\n | \"error\"\n | \"cancelled\";\n\nexport interface UseAgentRunOptions {\n /** The transport (or a per-run factory — resolved fresh at each start). */\n transport: MatrxTransport | (() => MatrxTransport);\n /** Stream-kernel knobs forwarded to every run (malformed-line hooks, read-ahead). */\n streamOptions?: Omit<MatrxStreamCallOptions, \"signal\">;\n /** Fired after every folded event with the fresh projection. */\n onProjection?: (projection: AgentRequestProjection) => void;\n /** Fired when a run fails, with the classified envelope. */\n onError?: (error: MatrxCallError) => void;\n}\n\nexport interface UseAgentRun {\n /** The lifecycle phase of the CURRENT run. */\n phase: AgentRunPhase;\n /** The live projection of the current run (null before the first start). */\n projection: AgentRequestProjection | null;\n /** Server-assigned `X-Request-ID` — feeds cancel and reconnect. */\n requestId: string | null;\n /** Server-assigned `X-Conversation-ID`. */\n conversationId: string | null;\n /** The classified failure of the current run, when phase is \"error\". */\n error: MatrxCallError | null;\n /** Start an agent run (`POST /ai/agents/{agent_id}`); resolves the final projection. */\n start: (\n agentId: string,\n request: MatrxAgentStartRequest,\n ) => Promise<AgentRequestProjection>;\n /** Continue a stored conversation (`POST /ai/conversations/{id}`); resolves the final projection. */\n continueConversation: (\n conversationId: string,\n request: MatrxConversationContinueRequest,\n ) => Promise<AgentRequestProjection>;\n /**\n * Server-side cancel of the current run by its `X-Request-ID`.\n * `mode: \"interrupt\"` = stop-and-fork. Resolves null when no run id is\n * known yet. The stream stays open until the server ends it — everything\n * already streamed persists.\n */\n cancel: (\n mode?: \"cancel\" | \"interrupt\",\n ) => Promise<MatrxCancelResponse | null>;\n /** Abort the in-flight fetch/stream locally (server work continues by design). */\n abort: () => void;\n /** Clear state back to idle (aborts any in-flight run first). */\n reset: () => void;\n}\n\nexport function useAgentRun(options: UseAgentRunOptions): UseAgentRun {\n const [phase, setPhase] = useState<AgentRunPhase>(\"idle\");\n const [projection, setProjection] = useState<AgentRequestProjection | null>(\n null,\n );\n const [error, setError] = useState<MatrxCallError | null>(null);\n\n // The latest options, without re-running effects/callbacks on identity churn.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // Run token: only the newest run may write state.\n const runSeqRef = useRef(0);\n const controllerRef = useRef<AbortController | null>(null);\n const requestIdRef = useRef<string | null>(null);\n const transportRef = useRef<MatrxTransport | null>(null);\n\n const abort = useCallback(() => {\n controllerRef.current?.abort();\n }, []);\n\n // Unmount teardown — the fetch/stream must not outlive the component.\n useEffect(() => () => controllerRef.current?.abort(), []);\n\n const runStream = useCallback(\n async (\n open: (\n transport: MatrxTransport,\n callOptions: MatrxStreamCallOptions,\n ) => Promise<MatrxRunHandle>,\n seedConversationId: string | null,\n ): Promise<AgentRequestProjection> => {\n const seq = ++runSeqRef.current;\n const isCurrent = () => runSeqRef.current === seq;\n\n controllerRef.current?.abort();\n const controller = new AbortController();\n controllerRef.current = controller;\n requestIdRef.current = null;\n\n const opts = optionsRef.current;\n const transport =\n typeof opts.transport === \"function\"\n ? opts.transport()\n : opts.transport;\n transportRef.current = transport;\n\n setPhase(\"starting\");\n setError(null);\n setProjection(null);\n\n try {\n const handle = await open(transport, {\n ...opts.streamOptions,\n signal: controller.signal,\n });\n requestIdRef.current = handle.requestId;\n\n let current = createAgentRequestProjection({\n requestId: handle.requestId ?? crypto.randomUUID(),\n conversationId: handle.conversationId ?? seedConversationId,\n });\n if (isCurrent()) {\n setPhase(\"streaming\");\n setProjection(current);\n }\n\n for await (const envelope of handle.events) {\n current = projectAgentEvent(current, envelope);\n if (isCurrent()) {\n setProjection(current);\n optionsRef.current.onProjection?.(current);\n }\n }\n\n if (isCurrent()) {\n setPhase(\n current.status === \"error\"\n ? \"error\"\n : current.status === \"cancelled\"\n ? \"cancelled\"\n : \"complete\",\n );\n if (current.status === \"error\") {\n const failure: MatrxCallError = {\n type: \"unknown\",\n message:\n (typeof current.error?.user_message === \"string\" &&\n current.error.user_message) ||\n (typeof current.error?.message === \"string\" &&\n current.error.message) ||\n \"The agent run failed\",\n serverDetail: current.error,\n };\n setError(failure);\n optionsRef.current.onError?.(failure);\n }\n }\n return current;\n } catch (err) {\n const failure = normalizeMatrxError(err);\n if (isCurrent()) {\n setPhase(failure.type === \"abort_error\" ? \"cancelled\" : \"error\");\n if (failure.type !== \"abort_error\") {\n setError(failure);\n optionsRef.current.onError?.(failure);\n }\n }\n throw err;\n }\n },\n [],\n );\n\n const start = useCallback(\n (agentId: string, request: MatrxAgentStartRequest) =>\n runStream(\n (transport, callOptions) =>\n startAgentRun(transport, agentId, request, callOptions),\n request.conversation_id,\n ),\n [runStream],\n );\n\n const continueConversation = useCallback(\n (conversationId: string, request: MatrxConversationContinueRequest) =>\n runStream(\n (transport, callOptions) =>\n continueAgentConversation(\n transport,\n conversationId,\n request,\n callOptions,\n ),\n conversationId,\n ),\n [runStream],\n );\n\n const cancel = useCallback(\n async (\n mode: \"cancel\" | \"interrupt\" = \"cancel\",\n ): Promise<MatrxCancelResponse | null> => {\n const requestId = requestIdRef.current;\n const transport = transportRef.current;\n if (!requestId || !transport) return null;\n return cancelAgentRun(transport, requestId, { mode });\n },\n [],\n );\n\n const reset = useCallback(() => {\n runSeqRef.current += 1;\n controllerRef.current?.abort();\n controllerRef.current = null;\n requestIdRef.current = null;\n setPhase(\"idle\");\n setProjection(null);\n setError(null);\n }, []);\n\n return {\n phase,\n projection,\n requestId: projection?.requestId ?? null,\n conversationId: projection?.conversationId ?? null,\n error,\n start,\n continueConversation,\n cancel,\n abort,\n reset,\n };\n}\n","export type AgentProjectionStatus =\n | \"pending\"\n | \"streaming\"\n | \"awaiting-tools\"\n | \"complete\"\n | \"error\"\n | \"cancelled\";\n\nexport interface AgentProjectionOperation {\n operationId: string;\n operation: string;\n parentOperationId: string | null;\n status: \"active\" | \"success\" | \"failed\" | \"cancelled\";\n metadata: Record<string, unknown> | null;\n result: Record<string, unknown> | null;\n}\n\nexport interface AgentProjectionTool {\n callId: string;\n toolName: string;\n status:\n | \"started\"\n | \"progress\"\n | \"step\"\n | \"preview\"\n | \"completed\"\n | \"error\"\n | \"delegated\";\n message: string | null;\n data: Record<string, unknown> | null;\n}\n\nexport interface AgentProjectionRenderBlock {\n blockId: string;\n blockIndex: number;\n type: string;\n status: \"streaming\" | \"complete\" | \"error\";\n content: string | null;\n data: Record<string, unknown> | null;\n metadata: Record<string, unknown> | null;\n}\n\nexport interface AgentRequestProjection {\n requestId: string;\n conversationId: string | null;\n status: AgentProjectionStatus;\n answer: string;\n reasoning: string;\n reasoningActive: boolean;\n phase: string | null;\n phaseHistory: string[];\n operations: Record<string, AgentProjectionOperation>;\n tools: Record<string, AgentProjectionTool>;\n renderBlocks: Record<string, AgentProjectionRenderBlock>;\n renderBlockOrder: string[];\n completion: Record<string, unknown> | null;\n error: Record<string, unknown> | null;\n lastTransportSeq: number;\n transportStreamId: string | null;\n eventCount: number;\n}\n\nexport interface AgentProjectionEvent {\n event: string;\n data?: unknown;\n stream_seq?: number;\n stream_id?: string;\n}\n\nconst asRecord = (value: unknown): Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n\nconst asString = (value: unknown): string | null =>\n typeof value === \"string\" ? value : null;\n\nconst asNumber = (value: unknown): number | null =>\n typeof value === \"number\" && Number.isFinite(value) ? value : null;\n\nexport function createAgentRequestProjection(input: {\n requestId: string;\n conversationId?: string | null;\n}): AgentRequestProjection {\n return {\n requestId: input.requestId,\n conversationId: input.conversationId ?? null,\n status: \"pending\",\n answer: \"\",\n reasoning: \"\",\n reasoningActive: false,\n phase: null,\n phaseHistory: [],\n operations: {},\n tools: {},\n renderBlocks: {},\n renderBlockOrder: [],\n completion: null,\n error: null,\n lastTransportSeq: 0,\n transportStreamId: null,\n eventCount: 0,\n };\n}\n\nfunction toolStatus(event: string): AgentProjectionTool[\"status\"] {\n switch (event) {\n case \"tool_started\":\n return \"started\";\n case \"tool_step\":\n return \"step\";\n case \"tool_result_preview\":\n return \"preview\";\n case \"tool_completed\":\n return \"completed\";\n case \"tool_error\":\n return \"error\";\n case \"tool_delegated\":\n return \"delegated\";\n default:\n return \"progress\";\n }\n}\n\nexport function projectAgentEvent(\n current: AgentRequestProjection,\n event: AgentProjectionEvent,\n): AgentRequestProjection {\n const streamSeq = asNumber(event.stream_seq);\n const streamId = asString(event.stream_id);\n const sameSegment = streamId === null || streamId === current.transportStreamId;\n if (\n sameSegment &&\n streamSeq !== null &&\n streamSeq <= current.lastTransportSeq\n ) {\n return current;\n }\n\n const data = asRecord(event.data);\n const next: AgentRequestProjection = {\n ...current,\n status: current.status === \"pending\" ? \"streaming\" : current.status,\n lastTransportSeq:\n streamSeq === null\n ? current.lastTransportSeq\n : sameSegment\n ? Math.max(current.lastTransportSeq, streamSeq)\n : streamSeq,\n transportStreamId: streamId ?? current.transportStreamId,\n eventCount: current.eventCount + 1,\n };\n\n switch (event.event) {\n case \"chunk\": {\n const text = asString(data.text);\n return text === null ? next : { ...next, answer: current.answer + text };\n }\n case \"reasoning_chunk\": {\n const text = asString(data.text);\n return text === null\n ? next\n : {\n ...next,\n reasoning: current.reasoning + text,\n reasoningActive: true,\n };\n }\n case \"reasoning\":\n return {\n ...next,\n reasoningActive: data.state === \"started\",\n };\n case \"phase\": {\n const phase = asString(data.phase);\n if (phase === null) return next;\n return {\n ...next,\n phase,\n phaseHistory: [...current.phaseHistory, phase],\n };\n }\n case \"init\": {\n const operationId = asString(data.operation_id);\n const operation = asString(data.operation);\n if (operationId === null || operation === null) return next;\n return {\n ...next,\n operations: {\n ...current.operations,\n [operationId]: {\n operationId,\n operation,\n parentOperationId: asString(data.parent_operation_id),\n status: \"active\",\n metadata: Object.keys(asRecord(data.metadata)).length\n ? asRecord(data.metadata)\n : null,\n result: null,\n },\n },\n };\n }\n case \"completion\": {\n const operationId = asString(data.operation_id);\n const operation = asString(data.operation);\n const rawStatus = asString(data.status);\n const status: AgentProjectionOperation[\"status\"] =\n rawStatus === \"failed\" || rawStatus === \"cancelled\"\n ? rawStatus\n : \"success\";\n const result = asRecord(data.result);\n const operations = operationId\n ? {\n ...current.operations,\n [operationId]: {\n ...(current.operations[operationId] ?? {\n operationId,\n operation: operation ?? \"unknown\",\n parentOperationId: null,\n metadata: null,\n }),\n status,\n result,\n },\n }\n : current.operations;\n if (operation === \"user_request\") {\n return {\n ...next,\n operations,\n completion: data,\n status:\n status === \"success\"\n ? \"complete\"\n : status === \"cancelled\"\n ? \"cancelled\"\n : \"error\",\n };\n }\n return { ...next, operations };\n }\n case \"tool_event\": {\n const callId = asString(data.call_id);\n const toolName = asString(data.tool_name);\n const lifecycle = asString(data.event);\n if (callId === null || toolName === null || lifecycle === null) return next;\n const status = toolStatus(lifecycle);\n return {\n ...next,\n status:\n status === \"delegated\"\n ? \"awaiting-tools\"\n : status === \"completed\" || status === \"error\"\n ? \"streaming\"\n : next.status,\n tools: {\n ...current.tools,\n [callId]: {\n callId,\n toolName,\n status,\n message: asString(data.message),\n data: Object.keys(asRecord(data.data)).length ? asRecord(data.data) : null,\n },\n },\n };\n }\n case \"render_block\": {\n const blockId = asString(data.blockId);\n const blockIndex = asNumber(data.blockIndex);\n const type = asString(data.type);\n if (blockId === null || blockIndex === null || type === null) return next;\n const alreadyKnown = Object.hasOwn(current.renderBlocks, blockId);\n return {\n ...next,\n renderBlocks: {\n ...current.renderBlocks,\n [blockId]: {\n blockId,\n blockIndex,\n type,\n status:\n data.status === \"complete\" || data.status === \"error\"\n ? data.status\n : \"streaming\",\n content: asString(data.content),\n data: Object.keys(asRecord(data.data)).length ? asRecord(data.data) : null,\n metadata: Object.keys(asRecord(data.metadata)).length\n ? asRecord(data.metadata)\n : null,\n },\n },\n renderBlockOrder: alreadyKnown\n ? current.renderBlockOrder\n : [...current.renderBlockOrder, blockId],\n };\n }\n case \"error\":\n return { ...next, status: \"error\", error: data };\n case \"end\":\n return {\n ...next,\n status:\n current.status === \"error\" || current.status === \"cancelled\"\n ? current.status\n : \"complete\",\n reasoningActive: false,\n };\n case \"data\": {\n const conversationId =\n data.type === \"conversation_id\" ? asString(data.conversation_id) : null;\n return conversationId === null ? next : { ...next, conversationId };\n }\n default:\n return next;\n }\n}\n\nexport function projectAgentEvents(\n initial: AgentRequestProjection,\n events: readonly AgentProjectionEvent[],\n): AgentRequestProjection {\n return events.reduce(projectAgentEvent, initial);\n}\n","/**\n * `@ai-matrx/agents/matrx` — the Matrx transport port.\n *\n * The ONE seam between this package's wire semantics and a host's connection\n * policy. This package owns WHAT is said to the AI Matrx server — paths,\n * methods, JSON bodies, streaming Accept headers, the `Last-Event-ID` cursor\n * header — and the host owns HOW the connection is made:\n *\n * - base-URL / backend-channel resolution (global, sandbox override, local\n * engine, EC2-dedicated — whatever ladder the host runs);\n * - credentials (Supabase JWT `Authorization: Bearer`, guest\n * `X-Fingerprint-ID`, or the API-key lane) — NEVER implemented here;\n * - the `X-Organization-Id` context header;\n * - retry policy, network-level timeouts, and diagnostics capture.\n *\n * A host implements the port in a few lines:\n *\n * ```ts\n * const transport: MatrxTransport = {\n * fetch: (path, init) =>\n * fetch(`${baseUrl}${path}`, {\n * ...init,\n * headers: { ...init.headers, ...authHeaders() },\n * }),\n * };\n * ```\n *\n * The port is deliberately structural and `fetch`-shaped so `@ai-matrx/data`'s\n * `/api` transport can implement it without importing this package.\n */\n\n/**\n * The request this package hands the port. A strict subset of `RequestInit`,\n * so a host can spread it straight into `fetch`.\n */\nexport interface MatrxTransportRequest {\n method: \"GET\" | \"POST\";\n /**\n * Wire-semantic headers the CALL requires (`Content-Type`, `Accept`,\n * `Last-Event-ID`). The host merges its policy headers (auth, org) on top;\n * it must not drop these.\n */\n headers: Record<string, string>;\n /** Pre-serialized JSON body, present on POST calls that carry one. */\n body?: string;\n /** Caller cancellation. The host must wire it to the underlying fetch. */\n signal?: AbortSignal;\n}\n\n/**\n * The transport port. `path` is server-relative and always starts with `/`\n * (`/ai/...`, `/runtime/...`); the host prepends its resolved base URL.\n */\nexport interface MatrxTransport {\n fetch(path: string, init: MatrxTransportRequest): Promise<Response>;\n}\n\n/**\n * A non-2xx response from the Matrx API, with the server's structured error\n * body preserved and its richest human-readable message extracted.\n */\nexport class MatrxApiError extends Error {\n override readonly name = \"MatrxApiError\";\n /** HTTP status of the failed response. */\n readonly status: number;\n /** Machine code from the server body (`code`, or `detail.code`), when present. */\n readonly code: string | null;\n /** The parsed server error body, verbatim (undefined when unparsable). */\n readonly serverDetail: unknown;\n /** The request path the failure came from (server-relative). */\n readonly path: string;\n\n constructor(args: {\n status: number;\n path: string;\n serverDetail?: unknown;\n message?: string;\n }) {\n super(\n args.message ??\n extractMatrxErrorMessage(args.serverDetail) ??\n `HTTP ${args.status}`,\n );\n this.status = args.status;\n this.path = args.path;\n this.serverDetail = args.serverDetail;\n this.code = extractMatrxErrorCode(args.serverDetail);\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction nonBlankString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\n/**\n * Extract the richest human-readable message from a Matrx/FastAPI error body.\n *\n * aidream 4xx validation errors look like\n * `{ error, user_message, details: [{ field, message, help }] }`; hand-raised\n * HTTPExceptions carry `{ detail: { code, message } }`; FastAPI's defaults are\n * `{ detail: string | [{ msg }] }`. Preference order: `user_message` →\n * `message` → joined `details[].message` → `detail.message` →\n * `detail` string → joined `detail[].msg`. Returns undefined for\n * unrecognized bodies so callers fall back to the bare status line.\n */\nexport function extractMatrxErrorMessage(\n serverDetail: unknown,\n): string | undefined {\n if (!isRecord(serverDetail)) return undefined;\n\n const userMessage = nonBlankString(serverDetail.user_message);\n if (userMessage) return userMessage;\n const message = nonBlankString(serverDetail.message);\n if (message) return message;\n\n if (Array.isArray(serverDetail.details)) {\n const messages = serverDetail.details\n .map((entry: unknown) => {\n if (!isRecord(entry)) return undefined;\n const detailMessage = nonBlankString(entry.message);\n if (!detailMessage) return undefined;\n const field = nonBlankString(entry.field);\n return field ? `${field}: ${detailMessage}` : detailMessage;\n })\n .filter((m): m is string => typeof m === \"string\");\n if (messages.length > 0) return messages.join(\"; \");\n }\n\n const detail = serverDetail.detail;\n if (isRecord(detail)) {\n const detailMessage =\n nonBlankString(detail.message) ?? nonBlankString(detail.user_message);\n if (detailMessage) return detailMessage;\n }\n if (typeof detail === \"string\" && detail.trim()) return detail;\n if (Array.isArray(detail)) {\n const messages = detail\n .map((entry: unknown) =>\n isRecord(entry) ? nonBlankString(entry.msg) : undefined,\n )\n .filter((m): m is string => typeof m === \"string\");\n if (messages.length > 0) return messages.join(\"; \");\n }\n return undefined;\n}\n\n/**\n * Extract the machine error code from a Matrx error body: top-level `code`,\n * else `detail.code` (the hand-raised HTTPException shape). Null when absent.\n */\nexport function extractMatrxErrorCode(serverDetail: unknown): string | null {\n if (!isRecord(serverDetail)) return null;\n const topLevel = nonBlankString(serverDetail.code);\n if (topLevel) return topLevel;\n const detail = serverDetail.detail;\n if (isRecord(detail)) {\n const nested = nonBlankString(detail.code);\n if (nested) return nested;\n }\n return null;\n}\n","/**\n * The PRODUCTION `MatrxTransport` — the full connection pipeline every Matrx\n * host used to hand-roll, moved in under C22 (THE HARD PARTS LIVE IN THE\n * PACKAGE). A host constructs it from identity alone:\n *\n * ```ts\n * const transport = createMatrxTransport({\n * baseUrl: \"https://server.app.matrxserver.com\",\n * credentials, // CredentialsPort (@ai-matrx/data)\n * organizationId: () => activeOrgId, // omit for conversation-lane calls\n * });\n * ```\n *\n * Everything else is defaulted to the proven production values:\n *\n * - **timeouts** — connect 15s (for a non-streaming FastAPI handler this is\n * effectively time-to-response), total UNCAPPED (the port cannot tell a JSON\n * call from a long-lived NDJSON/SSE stream; the connect timeout is the JSON\n * guard), via `@ai-matrx/data/net`'s `resilientFetch`;\n * - **protocol** — the AI API version transform (`applyAiApiVersion`, default\n * v2) + the loud v2 → v1 transport fallback (`./protocol`);\n * - **credentials** — read fresh from the injected `CredentialsPort` on EVERY\n * call, so a token refresh mid-run is picked up; mapped to headers by the\n * ONE `credentialToHeaders`;\n * - **org context** — the fail-closed `X-Organization-Id` binding\n * (`./org-context`): configured lanes REQUIRE an org and refuse before the\n * wire; unconfigured lanes (conversation-scoped calls, which carry org in\n * the body) send none;\n * - **error classification** — every failure normalizes through\n * `normalizeMatrxError` into the ONE `MatrxCallError` envelope;\n * - **diagnostics** — a typed sink the host wires to its capture/telemetry\n * (`onRequest`, `onError`, `onProtocolDowngrade`); wiring it is optional,\n * the transport works silently without it.\n *\n * Header merge order is part of the port contract: wire headers first\n * (`Content-Type` / `Accept` / `Last-Event-ID` — the package's), then\n * credentials, then resolver policy headers, then the org header. Policy\n * headers never carry `Content-Type` — the wire owns it, and a policy\n * `Content-Type` merged over a GET SSE call would corrupt the wire, so the\n * factory strips it defensively.\n */\n\nimport { isNetError, type CredentialsPort } from \"@ai-matrx/data\";\nimport { credentialToHeaders } from \"@ai-matrx/data\";\nimport { extractErrorMessage } from \"@ai-matrx/data/net\";\nimport { BackendApiError } from \"./backend-errors\";\nimport {\n bareStatusSentence,\n honestTransportMessage,\n isBareTransportCode,\n} from \"./call\";\nimport {\n applyOrganizationContextHeader,\n OrganizationContextError,\n requireOrganizationContext,\n} from \"./org-context\";\nimport {\n applyAiApiVersion,\n fetchWithMatrxProtocolFallback,\n MATRX_AI_API_VERSION_DEFAULT,\n type MatrxAiApiVersion,\n type MatrxProtocolDowngrade,\n} from \"./protocol\";\nimport {\n extractMatrxErrorCode,\n extractMatrxErrorMessage,\n MatrxApiError,\n type MatrxTransport,\n type MatrxTransportRequest,\n} from \"./transport\";\n\n// ─── The normalized error envelope ──────────────────────────────────────────\n\n/**\n * The ONE classified error shape for Matrx client calls — the envelope hosts\n * branch on instead of string-matching exceptions. Structurally compatible\n * with matrx-frontend's `ApiCallError` by design.\n */\nexport interface MatrxCallError {\n type:\n | \"auth_error\"\n | \"network_error\"\n | \"http_error\"\n | \"validation_error\"\n | \"abort_error\"\n | \"unknown\";\n message: string;\n /** HTTP status code, if applicable. */\n status?: number;\n /** Raw error detail from the server (the parsed error body). */\n serverDetail?: unknown;\n /** Machine code preserved through normalization (server `code`, org-context code, …). */\n code?: string;\n /** Original exception identity for diagnostics. */\n name?: string;\n /** Original exception stack. */\n stack?: string;\n /** JSON-safe dump of the original thrown value. */\n raw?: unknown;\n}\n\nfunction classifyStatus(status: number): \"validation_error\" | \"http_error\" {\n return status >= 400 && status < 500 ? \"validation_error\" : \"http_error\";\n}\n\n/**\n * Classify any failure thrown by a Matrx client call — `MatrxApiError`, a\n * typed `BackendApiError` / `StreamTransportError`, a `NetError` from the\n * resilience layer, an org-context refusal, an abort — into the\n * `MatrxCallError` envelope. Never throws.\n *\n * Every message is laundered through `honestTransportMessage`: a bare status\n * line (\"HTTP 400\") never reaches a person — the status rides `status`.\n * A `BackendApiError` keeps its machine `code` (a dropped socket stays\n * `stream_transport_lost`, so a surface reattaches instead of dead-ending)\n * and its structured `details` ride `serverDetail` (the organization-hold\n * body carries the caller's membership choices there).\n */\nexport function normalizeMatrxError(err: unknown): MatrxCallError {\n if (err instanceof OrganizationContextError) {\n return {\n type: \"validation_error\",\n message: err.message,\n code: err.code,\n name: err.name,\n ...(err.stack !== undefined ? { stack: err.stack } : {}),\n };\n }\n\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return { type: \"abort_error\", message: \"Request was cancelled.\" };\n }\n\n if (err instanceof MatrxApiError) {\n return {\n type: classifyStatus(err.status),\n message: honestTransportMessage(err.message, err.status),\n status: err.status,\n serverDetail: err.serverDetail,\n ...(err.code ? { code: err.code } : {}),\n name: err.name,\n };\n }\n\n if (err instanceof BackendApiError) {\n return {\n type: \"network_error\",\n message: honestTransportMessage(\n err.detail || err.userMessage,\n err.status ?? undefined,\n ),\n code: err.code,\n name: err.name,\n ...(err.status !== null ? { status: err.status } : {}),\n ...(err.details !== null ? { serverDetail: err.details } : {}),\n };\n }\n\n if (isNetError(err)) {\n if (err.code === \"aborted\") {\n return { type: \"abort_error\", message: err.message };\n }\n if (err.code === \"http\") {\n const status = err.status ?? 0;\n return {\n type: classifyStatus(status),\n message: honestTransportMessage(err.message, status),\n status,\n };\n }\n // connect-timeout / total-timeout / heartbeat-timeout / network / offline\n return { type: \"network_error\", message: err.message };\n }\n\n if (err instanceof Error) {\n const httpMatch = err.message.match(/HTTP (\\d+):\\s*(.*)/);\n if (httpMatch) {\n const status = parseInt(httpMatch[1] as string, 10);\n return {\n type: classifyStatus(status),\n message: honestTransportMessage(httpMatch[2] ?? err.message, status),\n status,\n };\n }\n // \"HTTP 400\" with no colon: the status line itself, never the sentence.\n if (isBareTransportCode(err.message)) {\n const status = Number.parseInt(err.message.replace(/\\D+/g, \"\"), 10);\n return {\n type: classifyStatus(status),\n message: bareStatusSentence(status),\n status,\n };\n }\n return { type: \"network_error\", message: err.message };\n }\n\n return { type: \"unknown\", message: extractErrorMessage(err) };\n}\n\n// ─── The factory ────────────────────────────────────────────────────────────\n\n/**\n * A fully-resolved connection target for one call: where to send it and which\n * policy headers ride ON TOP of the wire + credential headers.\n */\nexport interface MatrxTransportTarget {\n /** Fully-qualified base URL, no trailing slash. */\n baseUrl: string;\n /**\n * Extra policy headers for this target (e.g. a resolver that carries its own\n * credential headers). `Content-Type` is stripped — the wire owns it.\n */\n policyHeaders?: Record<string, string>;\n /** Routing channel label surfaced to `onRequest` telemetry. */\n channel?: string;\n}\n\n/** Context handed to every diagnostics callback. */\nexport interface MatrxRequestInfo {\n /** The final URL (base + version-transformed path). */\n url: string;\n method: \"GET\" | \"POST\";\n /** The server-relative path as the package requested it. */\n path: string;\n /** The resolved routing channel (default \"default\"). */\n channel: string;\n /** The transport's call-site label (default \"matrxTransport\"). */\n source: string;\n}\n\n/**\n * The typed diagnostics sink — the host wires these to its telemetry/capture\n * systems; all optional, and the transport is fully functional without them.\n */\nexport interface MatrxTransportDiagnostics {\n /** Fired at the last pre-fetch moment of every call. */\n onRequest?: (info: MatrxRequestInfo) => void;\n /**\n * Fired for every thrown network failure and every non-2xx response not\n * listed in `expectedErrorStatuses`, with the classified envelope.\n */\n onError?: (error: MatrxCallError, info: MatrxRequestInfo) => void;\n /** Fired on every v2 → v1 protocol downgrade. */\n onProtocolDowngrade?: (downgrade: MatrxProtocolDowngrade) => void;\n}\n\nexport interface CreateMatrxTransportOptions {\n /** Fixed base URL, no trailing slash. Exactly one of `baseUrl` / `resolveTarget`. */\n baseUrl?: string;\n /**\n * Per-call target resolver — for hosts whose base URL / policy headers are\n * dynamic (server toggles, sandbox overrides, conversation channels).\n * Resolution runs fresh on EVERY call. May throw to refuse a call loudly.\n */\n resolveTarget?: () => MatrxTransportTarget;\n /**\n * The credential source (`@ai-matrx/data`), read fresh per call so token\n * refreshes are picked up mid-run. Omit only when the resolver's\n * `policyHeaders` already carry the credentials.\n */\n credentials?: CredentialsPort;\n /**\n * The org-context binding. When configured, every call REQUIRES a valid org\n * (fail-closed `organization_context_required` before the wire) and carries\n * `X-Organization-Id`. Omit for conversation-lane transports, which send the\n * org in the body only.\n */\n organizationId?: string | (() => string | null | undefined);\n /** AI API protocol version (value or per-call getter). Default `\"v2\"`. */\n aiApiVersion?: MatrxAiApiVersion | (() => MatrxAiApiVersion);\n /** Max ms request start → response headers. Default 15_000. */\n connectTimeoutMs?: number;\n /**\n * Max ms for the whole handshake. Default `null` (uncapped) — the port\n * cannot tell a JSON call from a long-lived stream, and capping would kill\n * long streams; the connect timeout is the real JSON guard.\n */\n totalTimeoutMs?: number | null;\n /**\n * HTTP failures this transport's call class fully handles as expected\n * domain outcomes (e.g. 404 on runtime-operation identify). They still\n * reach the package client (which maps them to typed results) but are not\n * reported through `onError`.\n */\n expectedErrorStatuses?: readonly number[];\n /** Short call-site label surfaced on `MatrxRequestInfo` (default \"matrxTransport\"). */\n source?: string;\n diagnostics?: MatrxTransportDiagnostics;\n}\n\nconst PRODUCTION_CONNECT_TIMEOUT_MS = 15_000;\n\nfunction stripContentType(\n headers: Record<string, string>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([name]) => name.toLowerCase() !== \"content-type\",\n ),\n );\n}\n\n/**\n * Build the production `MatrxTransport`. See the module doc for the pipeline;\n * every knob defaults to the proven production value.\n */\nexport function createMatrxTransport(\n options: CreateMatrxTransportOptions,\n): MatrxTransport {\n const {\n baseUrl,\n resolveTarget,\n credentials,\n organizationId,\n aiApiVersion,\n connectTimeoutMs = PRODUCTION_CONNECT_TIMEOUT_MS,\n totalTimeoutMs = null,\n expectedErrorStatuses,\n diagnostics,\n source = \"matrxTransport\",\n } = options;\n\n if ((baseUrl === undefined) === (resolveTarget === undefined)) {\n throw new Error(\n \"createMatrxTransport: provide exactly one of `baseUrl` or `resolveTarget`.\",\n );\n }\n\n const resolve: () => MatrxTransportTarget =\n resolveTarget ?? (() => ({ baseUrl: baseUrl as string }));\n\n const resolveVersion = (): MatrxAiApiVersion => {\n if (aiApiVersion === undefined) return MATRX_AI_API_VERSION_DEFAULT;\n return typeof aiApiVersion === \"function\" ? aiApiVersion() : aiApiVersion;\n };\n\n const resolveOrganizationId = (): string | null => {\n if (organizationId === undefined) return null;\n const candidate =\n typeof organizationId === \"function\" ? organizationId() : organizationId;\n // Fail closed: a configured org lane with no org refuses before the wire.\n return requireOrganizationContext(candidate);\n };\n\n return {\n async fetch(path: string, init: MatrxTransportRequest): Promise<Response> {\n const target = resolve();\n\n // `path` is already interpolated (`/ai/agents/<uuid>`), so the version\n // transform is the concrete-path bridge, not a template registry.\n const versionedPath = applyAiApiVersion(path, resolveVersion());\n const url = `${target.baseUrl}${versionedPath}`;\n\n // Wire headers FIRST; credentials, then resolver policy headers, then\n // the org header on top — none of them may carry Content-Type.\n const credentialHeaders = credentials\n ? credentialToHeaders(await credentials.get())\n : {};\n let headers: Record<string, string> = {\n ...init.headers,\n ...stripContentType(credentialHeaders),\n ...stripContentType(target.policyHeaders ?? {}),\n };\n const orgId = resolveOrganizationId();\n if (orgId !== null) {\n headers = applyOrganizationContextHeader(headers, orgId);\n }\n\n const info: MatrxRequestInfo = {\n url,\n method: init.method,\n path,\n channel: target.channel ?? \"default\",\n source,\n };\n diagnostics?.onRequest?.(info);\n\n let response: Response;\n try {\n ({ response } = await fetchWithMatrxProtocolFallback(\n url,\n {\n method: init.method,\n headers,\n ...(init.body !== undefined ? { body: init.body } : {}),\n },\n {\n ...(init.signal ? { signal: init.signal } : {}),\n connectTimeoutMs,\n totalTimeoutMs,\n throwOnHttpError: false,\n ...(diagnostics?.onProtocolDowngrade\n ? { onDowngrade: diagnostics.onProtocolDowngrade }\n : {}),\n },\n ));\n } catch (err) {\n diagnostics?.onError?.(normalizeMatrxError(err), info);\n throw err;\n }\n\n if (!response.ok && !expectedErrorStatuses?.includes(response.status)) {\n // The package client consumes the original body for its\n // MatrxApiError; read the diagnostics copy from a clone so the two\n // never fight.\n const serverDetail: unknown = await response\n .clone()\n .json()\n .catch(() => undefined);\n const code = extractMatrxErrorCode(serverDetail);\n diagnostics?.onError?.(\n {\n type: classifyStatus(response.status),\n message:\n extractMatrxErrorMessage(serverDetail) ??\n `HTTP ${response.status}`,\n status: response.status,\n serverDetail,\n ...(code ? { code } : {}),\n },\n info,\n );\n }\n\n return response;\n },\n };\n}\n","/**\n * `@ai-matrx/agents/matrx` — the AI Matrx server's error model, as every\n * client reads it: `BackendApiError` (the server's `APIError` body),\n * `StreamTransportError` (the socket died mid-run; the run may still finish\n * and is reattachable), parsing of HTTP / stream / persisted errors, and the\n * ONE sentence a person sees (`getUserMessage`, `describeBackendFailure`).\n *\n * Moved from matrx-frontend `lib/api/errors.ts` (chat-package independence\n * P9); the app re-exports it from here. Pure: no host, no window, no env.\n */\n\n/**\n * Standardized error shape returned by all backend endpoints.\n * Matches the Python `APIError` Pydantic model.\n */\nexport interface BackendApiErrorData {\n /** Machine-readable error code (e.g. \"auth_required\", \"validation_error\") */\n error: string;\n /** Developer-facing detail for debugging */\n message: string;\n /** Safe to display directly in the UI */\n user_message: string;\n /** Extra info (validation errors, etc.) */\n details: unknown | null;\n /** Unique request ID for support/debugging */\n request_id: string;\n}\n\n/** Common backend error codes */\nexport type BackendErrorCode =\n | \"auth_required\"\n | \"token_required\"\n | \"admin_required\"\n | \"validation_error\"\n | \"not_found\"\n | \"internal_error\"\n | \"agent_error\"\n /** The stream socket died mid-run; the server run may still be completing\n * and is reattachable. See `StreamTransportError`. */\n | \"stream_transport_lost\"\n | (string & {});\n\n// ============================================================================\n// ERROR CLASS\n// ============================================================================\n\n/**\n * Typed error thrown by all backend API operations.\n * Contains structured fields matching the Python APIError model.\n *\n * Usage:\n * ```typescript\n * try {\n * await client.post(ENDPOINTS.ai.agentStart(agentId), body);\n * } catch (err) {\n * if (err instanceof BackendApiError) {\n * // Show err.userMessage to the user\n * // Log err.requestId for debugging\n * // Check err.code for programmatic handling\n * }\n * }\n * ```\n */\nexport class BackendApiError extends Error {\n /** Machine-readable error code */\n readonly code: BackendErrorCode;\n /** Developer-facing detail */\n readonly detail: string;\n /** Safe to display directly in the UI */\n readonly userMessage: string;\n /** Extra info (validation errors, etc.) */\n readonly details: unknown | null;\n /** Unique request ID for support/debugging */\n readonly requestId: string;\n /** HTTP status code (if from an HTTP response) */\n readonly status: number | null;\n\n constructor(data: {\n code: BackendErrorCode;\n detail: string;\n userMessage: string;\n details?: unknown | null;\n requestId?: string | undefined;\n status?: number | null | undefined;\n }) {\n super(data.userMessage);\n this.name = \"BackendApiError\";\n this.code = data.code;\n this.detail = data.detail;\n this.userMessage = data.userMessage;\n this.details = data.details ?? null;\n // MATRX-EXCEPTION: requestId is genuinely optional (constructor param);\n // \"\" means \"no request id available\" — a display/log field, not persisted.\n this.requestId = data.requestId ?? \"\";\n this.status = data.status ?? null;\n }\n\n /** Convert to the wire format for logging */\n toJSON(): BackendApiErrorData {\n return {\n error: this.code,\n message: this.detail,\n user_message: this.userMessage,\n details: this.details,\n request_id: this.requestId,\n };\n }\n}\n\n/**\n * The socket carrying a live NDJSON stream broke mid-run.\n *\n * THE DISTINCTION THIS EXISTS TO MAKE: a backend that blows up mid-stream does\n * NOT break the socket — it emits a typed `error` event and closes the body\n * cleanly. So an exception escaping the body reader means the *transport* died,\n * not the run. And aidream streams run `detach_on_disconnect=True`: the server\n * keeps executing and persisting the turn after our connection goes away.\n *\n * The client therefore cannot decide locally whether the answer is lost — it\n * must ASK THE SERVER. That is what `resumable` means here: \"reattach by\n * requestId / conversationId / durable run id and let server truth settle it\",\n * never \"this succeeded\". A server that genuinely died reports `failed` on\n * reattach and the honest record replaces the optimistic copy.\n *\n * Consumers: `run-ai-stream.ts` (chat → `reconnectServerOperation`) and\n * `adopt-foreign-stream.ts` (pipeline runs → the surface's own rejoin).\n */\nexport class StreamTransportError extends BackendApiError {\n /** Always true — reattach and let the server settle the outcome. */\n readonly resumable = true as const;\n\n constructor(data: {\n detail: string;\n details?: unknown | null;\n requestId?: string;\n }) {\n super({\n code: \"stream_transport_lost\",\n detail: data.detail,\n userMessage:\n \"The connection dropped. Your run is still going on the server — reconnecting to it now.\",\n details: data.details ?? null,\n ...(data.requestId !== undefined ? { requestId: data.requestId } : {}),\n });\n this.name = \"StreamTransportError\";\n }\n}\n\n/**\n * True when a failure is a dropped transport rather than a failed run. Use this\n * instead of `instanceof` at boundaries that re-wrap errors (thunk rejections,\n * `callApi` result errors), where the class identity is lost but the code\n * survives.\n */\nexport function isStreamTransportLost(error: unknown): boolean {\n if (error instanceof StreamTransportError) return true;\n if (error instanceof BackendApiError) {\n return error.code === \"stream_transport_lost\";\n }\n if (error && typeof error === \"object\") {\n const code = (error as { code?: unknown }).code;\n if (code === \"stream_transport_lost\") return true;\n const errorType = (error as { error_type?: unknown }).error_type;\n if (errorType === \"transport_lost\") return true;\n }\n return false;\n}\n\n// ============================================================================\n// HTTP ERROR PARSER\n// ============================================================================\n\n/**\n * Parse a non-OK HTTP response into a BackendApiError.\n *\n * Handles the standardized backend shape and falls back gracefully\n * when the response isn't JSON or uses a legacy format.\n */\nexport async function parseHttpError(\n response: Response,\n): Promise<BackendApiError> {\n const status = response.status;\n let body: Record<string, unknown> | null = null;\n\n try {\n body = await response.json();\n } catch {\n // Not JSON — try plain text\n try {\n const text = await response.text();\n return new BackendApiError({\n code: statusToCode(status),\n detail: text || `HTTP ${status}`,\n userMessage: text || `Request failed (${status})`,\n status,\n });\n } catch {\n return new BackendApiError({\n code: statusToCode(status),\n detail: `HTTP ${status}`,\n userMessage: `Request failed (${status})`,\n status,\n });\n }\n }\n\n return parseHttpErrorBody(body, status);\n}\n\n/**\n * Parse an already-decoded JSON error body into a BackendApiError.\n *\n * Exported because XHR callers (upload/download progress paths) have the\n * parsed body in hand and MUST NOT hand-roll a shallower read: a private\n * copy in `python-client.ts` looked only at top-level `error`/`message`, so\n * FastAPI's `{\"detail\": {...}}` envelope — what every matrx-files 500 uses —\n * degraded to the useless `code: \"internal\", detail: \"HTTP 500\"`. That is\n * exactly how an upload failure with a real server-side cause reached the\n * user as `Upload failed (500)` and nothing else. One parser, every transport.\n */\nexport function parseHttpErrorBody(\n body: Record<string, unknown> | null,\n status: number,\n): BackendApiError {\n if (!body) {\n return new BackendApiError({\n code: statusToCode(status),\n detail: `HTTP ${status}`,\n userMessage: `Request failed (${status})`,\n status,\n });\n }\n // Standard backend shape: { error, message, user_message, details, request_id }\n if (typeof body.error === \"string\" && typeof body.user_message === \"string\") {\n return new BackendApiError({\n code: body.error as BackendErrorCode,\n detail: (body.message as string) || `HTTP ${status}`,\n userMessage: body.user_message as string,\n details: body.details ?? null,\n requestId:\n typeof body.request_id === \"string\" ? body.request_id : undefined,\n status,\n });\n }\n\n // Legacy: nested error object with user_visible_message\n if (typeof body.error === \"object\" && body.error !== null) {\n const errorObj = body.error as Record<string, unknown>;\n return new BackendApiError({\n code:\n (errorObj.type as string) ||\n (errorObj.error as string) ||\n statusToCode(status),\n detail: (errorObj.message as string) || `HTTP ${status}`,\n userMessage:\n (errorObj.user_message as string) ||\n (errorObj.user_visible_message as string) ||\n (errorObj.message as string) ||\n `Request failed (${status})`,\n details: errorObj.details ?? null,\n requestId:\n typeof errorObj.request_id === \"string\"\n ? errorObj.request_id\n : undefined,\n status,\n });\n }\n\n // FastAPI 422 validation shape: { detail: [{ loc, msg, type }, ...] }\n if (Array.isArray(body.detail)) {\n const first = body.detail[0] as Record<string, unknown> | undefined;\n const firstMsg = first && typeof first.msg === \"string\" ? first.msg : null;\n return new BackendApiError({\n code: statusToCode(status),\n detail: firstMsg\n ? `Validation error: ${firstMsg}`\n : JSON.stringify(body.detail),\n userMessage: firstMsg || `Request failed (${status})`,\n details: body.detail,\n status,\n });\n }\n\n // FastAPI HTTPException with structured detail: { detail: { code?, error?, message?, user_message?, ... } }\n if (\n typeof body.detail === \"object\" &&\n body.detail !== null &&\n !Array.isArray(body.detail)\n ) {\n const d = body.detail as Record<string, unknown>;\n const code =\n (typeof d.code === \"string\" && d.code) ||\n (typeof d.error === \"string\" && d.error) ||\n statusToCode(status);\n const message =\n (typeof d.message === \"string\" && d.message) ||\n (typeof d.detail === \"string\" && d.detail) ||\n `HTTP ${status}`;\n return new BackendApiError({\n code: code as BackendErrorCode,\n detail: message,\n userMessage:\n (typeof d.user_message === \"string\" && d.user_message) ||\n (typeof d.user_visible_message === \"string\" &&\n d.user_visible_message) ||\n message,\n details: d.details ?? d,\n requestId: typeof d.request_id === \"string\" ? d.request_id : undefined,\n status,\n });\n }\n\n // Legacy: flat { error: string, message: string } or { detail: string }\n return new BackendApiError({\n code: typeof body.error === \"string\" ? body.error : statusToCode(status),\n detail:\n (body.message as string) ||\n (body.detail as string) ||\n (body.error as string) ||\n `HTTP ${status}`,\n userMessage:\n (body.user_message as string) ||\n (body.user_visible_message as string) ||\n (body.message as string) ||\n (body.detail as string) ||\n `Request failed (${status})`,\n details: body.details ?? null,\n requestId:\n typeof body.request_id === \"string\" ? body.request_id : undefined,\n status,\n });\n}\n\n/**\n * Adapt callApi's result-style error into the same canonical error used by\n * direct fetch and streaming consumers.\n *\n * callApi intentionally returns errors instead of throwing them, but its\n * `serverDetail` contains the complete FastAPI body. Sending only\n * `error.message` to a feature discards that body and turns a precise\n * configuration failure into \"HTTP 422\". This adapter keeps one parser and\n * one human-facing explanation path across both client styles.\n */\nexport function parseCallApiError(error: {\n message: string;\n status?: number;\n serverDetail?: unknown;\n}): BackendApiError {\n const status = error.status ?? 500;\n const body =\n error.serverDetail &&\n typeof error.serverDetail === \"object\" &&\n !Array.isArray(error.serverDetail)\n ? (error.serverDetail as Record<string, unknown>)\n : { message: error.message };\n return parseHttpErrorBody(body, status);\n}\n\n// ============================================================================\n// STREAMING ERROR PARSER\n// ============================================================================\n\n/**\n * Parse streaming error event data into a BackendApiError.\n *\n * Handles both new format (`user_message`) and legacy (`user_visible_message`).\n */\nexport function parseStreamError(data: unknown): BackendApiError {\n if (!data || typeof data !== \"object\") {\n return new BackendApiError({\n code: \"internal_error\",\n detail: typeof data === \"string\" ? data : \"Unknown streaming error\",\n userMessage: typeof data === \"string\" ? data : \"Something went wrong\",\n });\n }\n\n const obj = data as Record<string, unknown>;\n const details =\n typeof obj.details === \"object\" && obj.details !== null\n ? (obj.details as Record<string, unknown>)\n : null;\n return new BackendApiError({\n code:\n (obj.code as string) ||\n (obj.error_type as string) ||\n (obj.error as string) ||\n \"internal_error\",\n detail: (obj.message as string) || \"Streaming error\",\n userMessage:\n (obj.user_message as string) ||\n (obj.message as string) ||\n \"Something went wrong\",\n details,\n requestId:\n typeof obj.request_id === \"string\"\n ? obj.request_id\n : typeof details?.request_id === \"string\"\n ? details.request_id\n : undefined,\n });\n}\n\n/**\n * Restore the canonical error shape from a durable backend run row.\n *\n * Provider ledgers often keep an aggregate summary plus a more specific first\n * child failure. Prefer that child so a page refresh does not turn a precise\n * streamed failure back into \"1 request failed\".\n */\nexport function parsePersistedBackendError(\n data: unknown,\n requestId = \"\",\n): BackendApiError | null {\n if (!data || typeof data !== \"object\") return null;\n const error = data as Record<string, unknown>;\n const failures = Array.isArray(error.failures) ? error.failures : [];\n const firstSpecific = failures.find(\n (failure): failure is Record<string, unknown> =>\n typeof failure === \"object\" &&\n failure !== null &&\n typeof (failure as Record<string, unknown>).message === \"string\",\n );\n const summary =\n typeof error.message === \"string\"\n ? error.message\n : \"Backend operation failed\";\n const specific =\n typeof firstSpecific?.message === \"string\"\n ? firstSpecific.message\n : summary;\n const detail = specific === summary ? summary : `${summary}: ${specific}`;\n const code = typeof error.type === \"string\" ? error.type : \"internal_error\";\n // A persisted failure may carry a human-facing line the technical detail\n // cannot express — the load-bearing case being a paid result that survived\n // its persistence failure (`WritePreservedError`, D183): \"it broke\" is true\n // and \"your work is preserved and will be recovered\" is what the user needs.\n // `describeBackendFailure` still overrides a TEMPLATED one with the cause.\n const userMessage =\n typeof error.user_message === \"string\" && error.user_message\n ? error.user_message\n : detail;\n return new BackendApiError({\n code,\n detail,\n userMessage,\n details: error,\n requestId,\n });\n}\n\n// ============================================================================\n// FAILURE EXPLANATION — never let a templated non-answer be the whole story\n// ============================================================================\n\n/**\n * Server messages that carry ZERO diagnostic value. The streaming layer\n * (`matrx-connect/streaming/response.py`) emits the first one for every\n * unclassified crash — \"CanonicalGscSync failed unexpectedly. Please try\n * again or adjust your settings.\" — while the REAL cause travels in the same\n * payload's `message`. Treating those as the answer is what makes failures\n * feel secretive.\n */\nconst GENERIC_MESSAGE_PATTERNS: readonly RegExp[] = [\n /failed unexpectedly/i,\n /^\\s*something went wrong/i,\n /please try again(\\s+later)?\\.?\\s*$/i,\n /^\\s*request failed\\b/i,\n /^\\s*unknown (streaming )?error/i,\n /^\\s*internal server error\\.?\\s*$/i,\n];\n\n/** True when a message tells the reader nothing about what actually broke. */\nexport function isGenericUserMessage(\n message: string | null | undefined,\n): boolean {\n const value = (message ?? \"\").trim();\n if (!value) return true;\n return GENERIC_MESSAGE_PATTERNS.some((pattern) => pattern.test(value));\n}\n\nexport interface UpstreamErrorPayload {\n message: string;\n code: string | null;\n userMessage: string | null;\n requestId: string | null;\n status: number | null;\n}\n\n/**\n * Recover an upstream service's structured error that a downstream service\n * stringified into its own message.\n *\n * Real example (scraper wrapping aidream):\n * `aidream could not resolve GSC credential 7223…: HTTP 409 {\"error\":\"conflict\",\n * \"message\":\"Google connection 7223… has no vault credential — it needs\n * re-authentication\",\"user_message\":\"Something went wrong…\",\"request_id\":\"9002…\"}`\n *\n * Without this, the only actionable sentence on the whole hop is invisible.\n */\nexport function unwrapUpstreamError(\n message: string,\n): UpstreamErrorPayload | null {\n const start = message.indexOf(\"{\");\n const end = message.lastIndexOf(\"}\");\n if (start < 0 || end <= start) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(message.slice(start, end + 1));\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const body = parsed as Record<string, unknown>;\n const inner =\n (typeof body.message === \"string\" && body.message) ||\n (typeof body.detail === \"string\" && body.detail) ||\n \"\";\n if (!inner) return null;\n const statusMatch = /\\bHTTP (\\d{3})\\b/.exec(message.slice(0, start));\n return {\n message: inner,\n code:\n (typeof body.error_type === \"string\" && body.error_type) ||\n (typeof body.error === \"string\" && body.error) ||\n null,\n userMessage:\n typeof body.user_message === \"string\" ? body.user_message : null,\n requestId: typeof body.request_id === \"string\" ? body.request_id : null,\n status: statusMatch ? Number(statusMatch[1]) : null,\n };\n}\n\nexport interface BackendFailureExplanation {\n /** Machine code from the deepest layer that classified the failure. */\n code: string;\n /** The most specific human-readable cause available — never a template. */\n cause: string;\n /** What to headline in the UI: the cause when the server was generic. */\n headline: string;\n /** True when every user-facing message the server sent was a template. */\n headlineWasGeneric: boolean;\n /** Message chain, outermost (closest service) first. */\n chain: string[];\n /** Deepest request id available, for cross-service log correlation. */\n requestId: string;\n status: number | null;\n}\n\n/**\n * THE anti-secrecy primitive: turn any thrown backend/stream failure into the\n * most specific explanation the payload can support — unwrapping every nested\n * upstream error and refusing to let a templated `user_message` be the answer.\n *\n * Every surface that reports a backend failure to a human should headline\n * `explanation.headline` and always keep `cause` + `requestId` reachable.\n */\nexport function describeBackendFailure(\n error: unknown,\n): BackendFailureExplanation {\n const chain: string[] = [];\n let code = \"internal_error\";\n let requestId = \"\";\n let status: number | null = null;\n let userFacing: string | null = null;\n\n if (error instanceof BackendApiError) {\n code = error.code;\n requestId = error.requestId;\n status = error.status;\n userFacing = error.userMessage;\n if (error.detail) chain.push(error.detail);\n if (error.userMessage && error.userMessage !== error.detail) {\n chain.push(error.userMessage);\n }\n } else if (error instanceof Error) {\n chain.push(error.message);\n userFacing = error.message;\n } else if (typeof error === \"string\") {\n chain.push(error);\n userFacing = error;\n } else {\n chain.push(\"Unknown error\");\n }\n\n // Walk the nesting: ANY message in the chain may have stringified the\n // service above it (the technical `detail` usually does, the templated\n // `user_message` never does), so every layer gets unwrapped.\n for (let cursor = 0; cursor < chain.length && cursor < 12; cursor += 1) {\n const upstream = unwrapUpstreamError(chain[cursor] ?? \"\");\n if (!upstream || chain.includes(upstream.message)) continue;\n chain.push(upstream.message);\n if (upstream.code) code = upstream.code;\n if (upstream.requestId) requestId = upstream.requestId;\n if (upstream.status !== null) status = upstream.status;\n }\n\n const specific = [...chain]\n .reverse()\n .find((message) => !isGenericUserMessage(message));\n const cause = specific ?? chain[0] ?? \"Unknown error\";\n const headlineWasGeneric = isGenericUserMessage(userFacing);\n return {\n code,\n cause,\n headline: headlineWasGeneric ? cause : (userFacing ?? cause),\n headlineWasGeneric,\n chain,\n requestId,\n status,\n };\n}\n\n/**\n * Extract a user-visible message from any error object.\n * Utility for components that just need the display string.\n */\nexport function getUserMessage(error: unknown): string {\n if (error instanceof BackendApiError) {\n return error.userMessage;\n }\n if (error instanceof Error) {\n return error.message;\n }\n if (typeof error === \"string\") {\n return error;\n }\n // A Redux thunk's `.unwrap()` rejects with a SerializedError — a plain\n // object, not an Error — whose `message` is the real reason.\n if (\n typeof error === \"object\" &&\n error !== null &&\n typeof (error as { message?: unknown }).message === \"string\" &&\n (error as { message: string }).message\n ) {\n return (error as { message: string }).message;\n }\n return \"Something went wrong\";\n}\n\n// ============================================================================\n// INTERNAL HELPERS\n// ============================================================================\n\nfunction statusToCode(status: number): BackendErrorCode {\n switch (status) {\n case 401:\n return \"auth_required\";\n case 403:\n return \"admin_required\";\n case 404:\n return \"not_found\";\n case 422:\n return \"validation_error\";\n default:\n return \"internal_error\";\n }\n}\n","/**\n * Canonical AI Matrx NDJSON wire kernel.\n *\n * This module is deliberately independent of React, Redux, Next.js, Supabase,\n * and generated application types. Every Matrx client uses it to turn the\n * backend's byte stream into the same normalized `{ event, data }` envelopes.\n * Host runtimes remain responsible for HTTP/auth errors and for deciding what\n * each event means in their state model.\n */\n\nexport interface MatrxStreamEnvelope<TData = unknown> {\n event: string;\n /** Immutable emitter segment; transport sequence is scoped to this id. */\n stream_id?: string;\n data: TData;\n /** Monotonic transport sequence from full envelopes, when supplied. */\n stream_seq?: number;\n}\n\nexport interface MatrxNdjsonIssue {\n line: string;\n error: unknown;\n /** One-based physical NDJSON line number. */\n lineNumber: number;\n /** True when an unterminated trailing fragment was parsed by `finish()`. */\n atCompletion: boolean;\n}\n\nexport interface MatrxStreamEnvelopeObservation {\n /** Exact parsed JSON value before compact/full normalization. */\n raw: unknown;\n envelope: MatrxStreamEnvelope;\n line: string;\n lineNumber: number;\n atCompletion: boolean;\n}\n\nexport interface MatrxNdjsonFramerOptions {\n /** Malformed JSON is non-fatal, but it must never disappear silently. */\n onMalformedLine?: (issue: MatrxNdjsonIssue) => void;\n /** Valid JSON with no recognized Matrx event envelope is also non-fatal. */\n onUnknownEnvelope?: (value: unknown) => void;\n /** Observe every valid envelope without changing or consuming it. */\n onValidEnvelope?: (observation: MatrxStreamEnvelopeObservation) => void;\n}\n\nexport interface ReadMatrxNdjsonOptions extends MatrxNdjsonFramerOptions {\n signal?: AbortSignal;\n /** Maximum normalized events read ahead of the iterator consumer. */\n maxReadAhead?: number;\n}\n\nexport interface MatrxNdjsonFramer {\n /** Push a text fragment. Fragments may split JSON tokens or line endings. */\n pushText(fragment: string): MatrxStreamEnvelope[];\n /** Push a byte fragment. UTF-8 code points may span calls. */\n pushBytes(fragment: Uint8Array): MatrxStreamEnvelope[];\n /** Parse the final unterminated line and flush any pending UTF-8 bytes. */\n finish(): MatrxStreamEnvelope[];\n}\n\ntype QueueItem =\n | { kind: \"event\"; value: MatrxStreamEnvelope }\n | { kind: \"error\"; error: unknown }\n | { kind: \"done\" };\n\nexport const DEFAULT_MATRX_NDJSON_READ_AHEAD = 64;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Normalize both supported Matrx wire shapes:\n *\n * - full: `{ \"event\": \"chunk\", \"data\": { \"text\": \"...\" } }`\n * - compact chunk: `{ \"e\": \"c\", \"t\": \"...\" }`\n * - compact reasoning: `{ \"e\": \"r\", \"t\": \"...\" }`\n */\nexport function normalizeMatrxStreamEnvelope(\n value: unknown,\n): MatrxStreamEnvelope | null {\n if (!isRecord(value)) return null;\n\n if (typeof value.event === \"string\") {\n const streamSeq =\n typeof value.stream_seq === \"number\" && Number.isFinite(value.stream_seq)\n ? value.stream_seq\n : undefined;\n const streamId = typeof value.stream_id === \"string\" ? value.stream_id : undefined;\n return {\n event: value.event,\n data: value.data,\n ...(streamId === undefined ? {} : { stream_id: streamId }),\n ...(streamSeq === undefined ? {} : { stream_seq: streamSeq }),\n };\n }\n if (value.e === \"c\" && typeof value.t === \"string\") {\n const streamId = typeof value.stream_id === \"string\" ? value.stream_id : undefined;\n const streamSeq =\n typeof value.stream_seq === \"number\" && Number.isFinite(value.stream_seq)\n ? value.stream_seq\n : undefined;\n return {\n event: \"chunk\",\n data: { text: value.t },\n ...(streamId === undefined ? {} : { stream_id: streamId }),\n ...(streamSeq === undefined ? {} : { stream_seq: streamSeq }),\n };\n }\n if (value.e === \"r\" && typeof value.t === \"string\") {\n const streamId = typeof value.stream_id === \"string\" ? value.stream_id : undefined;\n const streamSeq =\n typeof value.stream_seq === \"number\" && Number.isFinite(value.stream_seq)\n ? value.stream_seq\n : undefined;\n return {\n event: \"reasoning_chunk\",\n data: { text: value.t },\n ...(streamId === undefined ? {} : { stream_id: streamId }),\n ...(streamSeq === undefined ? {} : { stream_seq: streamSeq }),\n };\n }\n return null;\n}\n\n/**\n * Create the transport-independent NDJSON framer used by the stream reader.\n * Browser extensions, desktop bridges, WebSockets, and tests can feed it\n * fragmented strings or bytes without constructing a `ReadableStream`.\n */\nexport function createMatrxNdjsonFramer(\n options: MatrxNdjsonFramerOptions = {},\n): MatrxNdjsonFramer {\n const decoder = new TextDecoder();\n let buffer = \"\";\n let lineNumber = 0;\n let finished = false;\n\n const assertOpen = (): void => {\n if (finished) throw new Error(\"Matrx NDJSON framer is already finished\");\n };\n\n const parseLine = (\n line: string,\n atCompletion: boolean,\n ): MatrxStreamEnvelope | null => {\n const currentLineNumber = ++lineNumber;\n const trimmed = line.trim();\n if (!trimmed) return null;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed) as unknown;\n } catch (error) {\n options.onMalformedLine?.({\n line: trimmed,\n error,\n lineNumber: currentLineNumber,\n atCompletion,\n });\n return null;\n }\n\n const envelope = normalizeMatrxStreamEnvelope(parsed);\n if (!envelope) {\n options.onUnknownEnvelope?.(parsed);\n return null;\n }\n options.onValidEnvelope?.({\n raw: parsed,\n envelope,\n line: trimmed,\n lineNumber: currentLineNumber,\n atCompletion,\n });\n return envelope;\n };\n\n const pushDecodedText = (fragment: string): MatrxStreamEnvelope[] => {\n buffer += fragment;\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n const envelopes: MatrxStreamEnvelope[] = [];\n for (const line of lines) {\n const envelope = parseLine(line, false);\n if (envelope) envelopes.push(envelope);\n }\n return envelopes;\n };\n\n return {\n pushText(fragment) {\n assertOpen();\n // Flush any pending byte fragment before switching to explicit text.\n return pushDecodedText(decoder.decode() + fragment);\n },\n pushBytes(fragment) {\n assertOpen();\n return pushDecodedText(decoder.decode(fragment, { stream: true }));\n },\n finish() {\n assertOpen();\n finished = true;\n const envelopes = pushDecodedText(decoder.decode());\n if (buffer.length > 0) {\n const envelope = parseLine(buffer, true);\n buffer = \"\";\n if (envelope) envelopes.push(envelope);\n }\n return envelopes;\n },\n };\n}\n\nfunction readAheadLimit(value: number | undefined): number {\n const limit = value ?? DEFAULT_MATRX_NDJSON_READ_AHEAD;\n if (!Number.isSafeInteger(limit) || limit < 1) {\n throw new RangeError(\"maxReadAhead must be a positive safe integer\");\n }\n return limit;\n}\n\n/**\n * Read and normalize a Matrx NDJSON response body with bounded background\n * read-ahead. Consumer work does not stall the network until `maxReadAhead`\n * complete events are waiting; the bound prevents an abandoned or blocked\n * consumer from growing memory without limit.\n */\nexport async function* readMatrxNdjsonStream(\n body: ReadableStream<Uint8Array>,\n options: ReadMatrxNdjsonOptions = {},\n): AsyncGenerator<MatrxStreamEnvelope, void, undefined> {\n const maxReadAhead = readAheadLimit(options.maxReadAhead);\n const queue: QueueItem[] = [];\n let queuedEventCount = 0;\n let wakeConsumer: (() => void) | null = null;\n let wakeProducer: (() => void) | null = null;\n let readerFinished = false;\n let consumerClosed = false;\n\n const wakeWaitingConsumer = (): void => {\n const wake = wakeConsumer;\n wakeConsumer = null;\n wake?.();\n };\n const wakeWaitingProducer = (): void => {\n const wake = wakeProducer;\n wakeProducer = null;\n wake?.();\n };\n const enqueueTerminal = (item: QueueItem): void => {\n if (consumerClosed) return;\n queue.push(item);\n wakeWaitingConsumer();\n };\n const enqueueEvent = async (value: MatrxStreamEnvelope): Promise<boolean> => {\n while (\n queuedEventCount >= maxReadAhead &&\n !consumerClosed &&\n !options.signal?.aborted\n ) {\n await new Promise<void>((resolve) => {\n wakeProducer = resolve;\n });\n }\n if (consumerClosed || options.signal?.aborted) return false;\n queue.push({ kind: \"event\", value });\n queuedEventCount += 1;\n wakeWaitingConsumer();\n return true;\n };\n const waitForReadCapacity = async (): Promise<boolean> => {\n while (\n queuedEventCount >= maxReadAhead &&\n !consumerClosed &&\n !options.signal?.aborted\n ) {\n await new Promise<void>((resolve) => {\n wakeProducer = resolve;\n });\n }\n return !consumerClosed && !options.signal?.aborted;\n };\n\n const reader = body.getReader();\n const framer = createMatrxNdjsonFramer(options);\n\n const onAbort = (): void => {\n wakeWaitingProducer();\n wakeWaitingConsumer();\n void reader.cancel(options.signal?.reason).catch(() => undefined);\n };\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n if (options.signal?.aborted) onAbort();\n\n const readerPromise = (async (): Promise<void> => {\n try {\n while (!options.signal?.aborted && !consumerClosed) {\n if (!(await waitForReadCapacity())) return;\n const { value, done } = await reader.read();\n if (done) break;\n for (const envelope of framer.pushBytes(value)) {\n if (!(await enqueueEvent(envelope))) return;\n }\n }\n\n if (!options.signal?.aborted && !consumerClosed) {\n for (const envelope of framer.finish()) {\n if (!(await enqueueEvent(envelope))) return;\n }\n }\n } catch (error) {\n const aborted =\n options.signal?.aborted ||\n consumerClosed ||\n (error instanceof Error && error.name === \"AbortError\");\n if (!aborted) enqueueTerminal({ kind: \"error\", error });\n } finally {\n readerFinished = true;\n reader.releaseLock();\n enqueueTerminal({ kind: \"done\" });\n }\n })();\n\n try {\n while (true) {\n if (queue.length === 0) {\n if (options.signal?.aborted || readerFinished) return;\n await new Promise<void>((resolve) => {\n wakeConsumer = resolve;\n });\n }\n\n const item = queue.shift();\n if (item?.kind === \"event\") queuedEventCount -= 1;\n wakeWaitingProducer();\n if (!item || item.kind === \"done\") return;\n if (item.kind === \"error\") throw item.error;\n yield item.value;\n }\n } finally {\n consumerClosed = true;\n wakeWaitingProducer();\n wakeWaitingConsumer();\n options.signal?.removeEventListener(\"abort\", onAbort);\n if (!readerFinished) {\n await reader.cancel().catch(() => undefined);\n }\n await readerPromise;\n }\n}\n","import { isUuidShape } from \"@ai-matrx/kit/uuid\";\n\n/**\n * The fail-closed organization-context kernel — ONE implementation for every\n * Matrx client transport (moved in from matrx-frontend\n * `lib/api/organization-context.ts` verbatim under C22; the host module is now\n * a re-export of this one).\n *\n * Transports resolve their authoritative organization first, then use these\n * functions to bind that exact value without guessing or defaulting. The\n * server never manufactures an org (it 422s a blank one), so the client\n * refuses before the wire: a missing or malformed org id throws\n * `OrganizationContextError` instead of sending a request that cannot succeed.\n */\n\nexport type OrganizationContextErrorCode =\n | \"organization_context_required\"\n | \"organization_context_invalid\"\n | \"organization_context_mismatch\";\n\nexport class OrganizationContextError extends Error {\n readonly code: OrganizationContextErrorCode;\n\n constructor(code: OrganizationContextErrorCode, message: string) {\n super(message);\n this.name = \"OrganizationContextError\";\n this.code = code;\n }\n}\n\ndeclare const organizationOperationBrand: unique symbol;\n\n/** A validated organization operation; construct only through the factory. */\nexport type OrganizationOperation = Readonly<{\n organization_id: string;\n readonly [organizationOperationBrand]: true;\n}>;\n\nexport function createOrganizationOperation(\n organizationId: string,\n): OrganizationOperation {\n const organization_id = requireOrganizationContext(organizationId);\n return Object.freeze({ organization_id }) as OrganizationOperation;\n}\n\nexport function assertOrganizationMatchesOperation(\n operation: OrganizationOperation,\n organizationId: string,\n): void {\n if (operation.organization_id !== requireOrganizationContext(organizationId)) {\n throw new OrganizationContextError(\n \"organization_context_mismatch\",\n \"Organization ID must match the request operation.\",\n );\n }\n}\n\n/**\n * Normalize and validate the effective organization id for a request. The\n * override (an explicit per-call value) beats the selected context value.\n * Throws `organization_context_required` when neither is present and\n * `organization_context_invalid` when the candidate is not a UUID.\n */\nexport function requireOrganizationContext(\n selectedOrganizationId: string | null | undefined,\n overrideOrganizationId?: string,\n): string {\n const candidate = overrideOrganizationId ?? selectedOrganizationId;\n if (typeof candidate !== \"string\" || candidate.trim().length === 0) {\n throw new OrganizationContextError(\n \"organization_context_required\",\n \"Select an organization before sending this request.\",\n );\n }\n\n const normalized = candidate.trim();\n if (!isUuidShape(normalized)) {\n throw new OrganizationContextError(\n \"organization_context_invalid\",\n \"The selected organization ID is invalid.\",\n );\n }\n return normalized.toLowerCase();\n}\n\n/**\n * Bind `X-Organization-Id` onto a header bag. A pre-existing org header that\n * disagrees with the context org is a `organization_context_mismatch` error —\n * never silently overwritten in either direction.\n */\nexport function applyOrganizationContextHeader(\n headers: Record<string, string>,\n organizationId: string,\n): Record<string, string> {\n const normalizedOrganizationId = requireOrganizationContext(organizationId);\n for (const [name, value] of Object.entries(headers)) {\n if (\n name.toLowerCase() === \"x-organization-id\" &&\n value.trim().toLowerCase() !== normalizedOrganizationId\n ) {\n throw new OrganizationContextError(\n \"organization_context_mismatch\",\n \"X-Organization-Id must match the request context organization.\",\n );\n }\n }\n const withoutOrganizationHeader = Object.fromEntries(\n Object.entries(headers).filter(\n ([name]) => name.toLowerCase() !== \"x-organization-id\",\n ),\n );\n return {\n ...withoutOrganizationHeader,\n \"X-Organization-Id\": normalizedOrganizationId,\n };\n}\n\n/**\n * Assert that an `organization_id` query parameter (when present) matches the\n * request context organization — the query string must never smuggle a\n * different org past the header binding.\n */\nexport function assertQueryOrganizationMatchesContext(\n queryParams: Record<string, string | number | boolean> | undefined,\n organizationId: string,\n): void {\n if (!queryParams || queryParams.organization_id === undefined) return;\n const queryOrganizationId = requireOrganizationContext(\n String(queryParams.organization_id),\n );\n if (queryOrganizationId !== requireOrganizationContext(organizationId)) {\n throw new OrganizationContextError(\n \"organization_context_mismatch\",\n \"Query organization_id must match the request context organization.\",\n );\n }\n}\n","/**\n * `@ai-matrx/agents/matrx` — THE request pipeline every typed Matrx server call\n * rides (chat package independence P9b). One implementation for every client:\n * matrx-frontend's `lib/api` `callApi` and the chat package's bare-host default\n * both build on it, and supply only their host facts — where the server is,\n * which credential and organization ride, and where diagnostics go.\n *\n * What lives here (pure, no host/window/env read):\n *\n * - `buildMatrxRequestUrl` — path params, the legacy `/api` strip, the query;\n * - `buildMatrxRequestBody` — scope injection (`organization_id` /\n * `project_id` / `task_id`), the UI-only field strip, and the fail-closed\n * body-vs-context organization check;\n * - `bareStatusSentence` / `isBareTransportCode` / `honestTransportMessage` —\n * a bare status code is never a sentence at a person;\n * - `parseMatrxNdjsonResponse` — a response body as typed envelopes, a broken\n * body reader classified as a resumable `StreamTransportError`;\n * - `executeMatrxCall` — the JSON or NDJSON execution over the v2 → v1\n * protocol fallback, with the stream callbacks and `consumeStream`;\n * - `buildSafeRequestLog` / `redactUrlForRequestLog` /\n * `shouldReportMatrxCallError` — the log and capture policy.\n *\n * The error envelope (`MatrxCallError`) and its ONE classifier\n * (`normalizeMatrxError`) live in `./client`.\n */\n\nimport {\n readMatrxNdjsonStream,\n type MatrxNdjsonIssue,\n type MatrxStreamEnvelope,\n} from \"../stream/ndjson\";\nimport { BackendApiError, StreamTransportError } from \"./backend-errors\";\nimport type { MatrxCallError } from \"./client\";\nimport { OrganizationContextError } from \"./org-context\";\nimport {\n fetchWithMatrxProtocolFallback,\n type MatrxProtocolDowngrade,\n} from \"./protocol\";\nimport { extractMatrxErrorMessage } from \"./transport\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport type MatrxHttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/**\n * Every context dimension a call may carry. `organization_id`, `project_id`\n * and `task_id` are injected into the body; `user_id` rides the credential and\n * `conversation_id` the path or an explicit body field — never injected.\n */\nexport interface MatrxCallScope {\n user_id?: string;\n organization_id?: string;\n project_id?: string;\n task_id?: string;\n conversation_id?: string;\n}\n\nexport interface MatrxCallResult<T = unknown> {\n /** Parsed JSON response body (non-streaming calls only). */\n data?: T;\n /** Server-assigned request id (response header). */\n requestId?: string;\n /** Server-assigned conversation id (response header). */\n conversationId?: string;\n /** Set when the call failed with an HTTP error response. */\n error?: MatrxCallError;\n}\n\nexport type MatrxQueryParams = Record<string, string | number | boolean>;\n\n// ─── URL ────────────────────────────────────────────────────────────────────\n\n/**\n * The full URL for one call: `{param}` segments substituted (encoded), the\n * legacy `/api` prefix stripped (server routes no longer live under it), and\n * the query appended.\n */\nexport function buildMatrxRequestUrl(\n baseUrl: string,\n pathTemplate: string,\n pathParams?: Record<string, string>,\n queryParams?: MatrxQueryParams,\n): string {\n let resolvedPath = pathTemplate;\n if (pathParams) {\n for (const [key, value] of Object.entries(pathParams)) {\n resolvedPath = resolvedPath.replace(`{${key}}`, encodeURIComponent(value));\n }\n }\n const fullPath = resolvedPath.startsWith(\"/api/\")\n ? resolvedPath.slice(4)\n : resolvedPath;\n const url = `${baseUrl}${fullPath}`;\n if (queryParams && Object.keys(queryParams).length > 0) {\n const qs = new URLSearchParams(\n Object.fromEntries(\n Object.entries(queryParams).map(([k, v]) => [k, String(v)]),\n ),\n ).toString();\n return `${url}?${qs}`;\n }\n return url;\n}\n\n// ─── Body ───────────────────────────────────────────────────────────────────\n\n/**\n * Client capability flags that must never reach the server — the server's\n * request schemas reject them.\n */\nexport const MATRX_UI_ONLY_BODY_FIELDS: ReadonlySet<string> = new Set([\n \"youtube_videos\",\n \"file_urls\",\n \"image_urls\",\n]);\n\n/**\n * The final request body: UI-only fields stripped, scope fields injected.\n *\n * A caller's `organization_id: null` (or blank) means \"I have none of my own\",\n * never \"send this for a different organization\" — it is dropped and the\n * scope's organization injected. A real value that disagrees with the scope\n * is refused (`organization_context_mismatch`). With no scope organization\n * (the org-less guest lane, an org-free read) nothing is injected for it.\n * Other scope fields keep caller-wins behaviour.\n */\nexport function buildMatrxRequestBody(\n body: unknown,\n scope: MatrxCallScope,\n): Record<string, unknown> {\n // MATRX-EXCEPTION: `body` is optional by design — a caller with no body\n // still gets scope fields injected, so `{}` is the correct start.\n const raw = (body ?? {}) as Record<string, unknown>;\n const base: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(raw)) {\n if (!MATRX_UI_ONLY_BODY_FIELDS.has(key)) base[key] = value;\n }\n\n if (\n base.organization_id === null ||\n (typeof base.organization_id === \"string\" &&\n base.organization_id.trim() === \"\")\n ) {\n delete base.organization_id;\n }\n\n const bodyOrganizationId = base.organization_id;\n if (\n scope.organization_id !== undefined &&\n bodyOrganizationId !== undefined &&\n (typeof bodyOrganizationId !== \"string\" ||\n bodyOrganizationId.trim() !== scope.organization_id)\n ) {\n throw new OrganizationContextError(\n \"organization_context_mismatch\",\n \"Request body organization_id must match the request context organization.\",\n );\n }\n if (bodyOrganizationId !== undefined && scope.organization_id !== undefined) {\n base.organization_id = scope.organization_id;\n }\n\n const scopeFields: Record<string, unknown> = {\n ...(scope.organization_id !== undefined\n ? { organization_id: scope.organization_id }\n : {}),\n };\n if (scope.project_id !== undefined) scopeFields.project_id = scope.project_id;\n if (scope.task_id !== undefined) scopeFields.task_id = scope.task_id;\n return { ...scopeFields, ...base };\n}\n\n// ─── Honest status sentences ────────────────────────────────────────────────\n\nconst BARE_TRANSPORT_CODE =\n /^\\s*(?:HTTP|HTTP\\s*Error|Status(?:\\s*Code)?)?\\s*[:\\-]?\\s*\\d{3}\\s*[.:!]?\\s*$/i;\n\n/** True when a candidate sentence is really just the status line wearing words. */\nexport function isBareTransportCode(text: string | null | undefined): boolean {\n if (!text) return false;\n return BARE_TRANSPORT_CODE.test(text);\n}\n\n/**\n * A BARE STATUS CODE IS NEVER A SENTENCE. What a person reads when the server\n * answered an error with no readable message. The status itself rides\n * `error.status`, which is what code branches on — this is only the words.\n */\nexport function bareStatusSentence(status: number): string {\n if (status === 401 || status === 403) {\n return \"The server would not let this request through — your session may have expired, or this account may not have access here. Sign in again, and if it repeats, ask an administrator.\";\n }\n if (status === 404) {\n return \"The server has nothing at that address. Reload the page; if it repeats, report it — a client asking for something that no longer exists is a defect, not your mistake.\";\n }\n if (status === 429) {\n return \"The server is rate-limiting this request. Wait a few seconds and try again.\";\n }\n if (status >= 500) {\n return \"The server failed while answering this, and sent no explanation. Try again in a moment; if it repeats, report it with the request id above.\";\n }\n return \"The server refused this request and sent no reason with it — the missing reason is itself a defect worth reporting. Reload the page and try once more; if it repeats, report it with the request id above.\";\n}\n\n/** `raw` unless it is empty or only a status line; then the status sentence. */\nexport function honestTransportMessage(\n raw: string,\n status: number | undefined,\n): string {\n if (raw && !isBareTransportCode(raw)) return raw;\n return bareStatusSentence(status ?? 0);\n}\n\n// ─── Log + capture policy ───────────────────────────────────────────────────\n\nconst SENSITIVE_HEADER_NAME = /authorization|cookie|token|api[-_]?key/i;\n\n/** Request metadata safe to log: secret headers redacted, the body as its shape only. */\nexport function buildSafeRequestLog(\n headers: Record<string, string>,\n body: unknown,\n): { headers: Record<string, string>; body: Record<string, unknown> } {\n const safeHeaders = Object.fromEntries(\n Object.entries(headers).map(([name, value]) => [\n name,\n SENSITIVE_HEADER_NAME.test(name) ? \"[REDACTED]\" : value,\n ]),\n );\n const bodyMetadata: Record<string, unknown> = Array.isArray(body)\n ? { type: \"array\", itemCount: body.length }\n : body && typeof body === \"object\"\n ? { type: \"object\", keys: Object.keys(body as Record<string, unknown>) }\n : { type: body === null ? \"null\" : typeof body };\n return { headers: safeHeaders, body: bodyMetadata };\n}\n\n/** The URL with every query value redacted. */\nexport function redactUrlForRequestLog(url: string): string {\n try {\n const parsed = new URL(url);\n for (const key of parsed.searchParams.keys()) {\n parsed.searchParams.set(key, \"[REDACTED]\");\n }\n return parsed.toString();\n } catch {\n const queryIndex = url.indexOf(\"?\");\n return queryIndex === -1 ? url : `${url.slice(0, queryIndex)}?[REDACTED]`;\n }\n}\n\n/** False only for an HTTP status the call site declared an expected outcome. */\nexport function shouldReportMatrxCallError(\n status: number | null | undefined,\n expectedErrorStatuses: readonly number[] | undefined,\n): boolean {\n return status == null || !expectedErrorStatuses?.includes(status);\n}\n\n// ─── Stream parsing ─────────────────────────────────────────────────────────\n\nexport interface MatrxStreamIds {\n requestId: string | null;\n conversationId: string | null;\n}\n\nexport interface MatrxStreamParse<E = MatrxStreamEnvelope> extends MatrxStreamIds {\n events: AsyncGenerator<E, void, undefined>;\n}\n\nexport interface ParseMatrxNdjsonResponseHooks {\n /** Every envelope, before the consumer sees it. */\n onEvent?: (event: MatrxStreamEnvelope, ids: MatrxStreamIds) => void;\n /** A broken body reader, already classified, before it is thrown. */\n onTransportError?: (error: BackendApiError, ids: MatrxStreamIds) => void;\n onMalformedLine?: (issue: MatrxNdjsonIssue) => void;\n onUnknownEnvelope?: (value: unknown) => void;\n}\n\n/**\n * A Matrx NDJSON response as typed envelopes. The ids come from headers, so\n * they are available before any event. A body that breaks mid-run is a\n * TRANSPORT loss (the run may still finish server-side and is reattachable) —\n * thrown as `StreamTransportError`, never a failed run; an abort ends quietly.\n */\nexport function parseMatrxNdjsonResponse(\n response: Response,\n signal?: AbortSignal,\n hooks: ParseMatrxNdjsonResponseHooks = {},\n): MatrxStreamParse {\n const ids: MatrxStreamIds = {\n requestId: response.headers.get(\"X-Request-ID\"),\n conversationId: response.headers.get(\"X-Conversation-ID\"),\n };\n async function* events(): AsyncGenerator<MatrxStreamEnvelope, void, undefined> {\n if (!response.body) {\n throw new BackendApiError({\n code: \"internal_error\",\n detail: \"Response has no body\",\n userMessage: \"No response received from server\",\n });\n }\n try {\n for await (const envelope of readMatrxNdjsonStream(response.body, {\n ...(signal ? { signal } : {}),\n ...(hooks.onMalformedLine ? { onMalformedLine: hooks.onMalformedLine } : {}),\n ...(hooks.onUnknownEnvelope ? { onUnknownEnvelope: hooks.onUnknownEnvelope } : {}),\n })) {\n hooks.onEvent?.(envelope, ids);\n yield envelope;\n }\n } catch (error) {\n if (signal?.aborted || (error instanceof Error && error.name === \"AbortError\")) {\n return;\n }\n const transportError =\n error instanceof BackendApiError\n ? error\n : new StreamTransportError({\n detail:\n error instanceof Error\n ? error.message\n : \"The response stream ended unexpectedly.\",\n details: error,\n ...(ids.requestId ? { requestId: ids.requestId } : {}),\n });\n hooks.onTransportError?.(transportError, ids);\n throw transportError;\n }\n }\n return { events: events(), ...ids };\n}\n\n// ─── Execution ──────────────────────────────────────────────────────────────\n\nexport interface ExecuteMatrxCallRequest<E = MatrxStreamEnvelope> {\n /** The final URL (`buildMatrxRequestUrl`). */\n url: string;\n method: string;\n /** Every header the host binds (credential, organization, Content-Type). */\n headers: Record<string, string>;\n /** The assembled body (`buildMatrxRequestBody`); never sent on GET/HEAD. */\n body: unknown;\n /** NDJSON streaming call. */\n stream?: boolean;\n signal?: AbortSignal;\n /** Time to response headers. Default 15_000. */\n connectTimeoutMs?: number;\n /** Whole-request cap for JSON calls. Default 30_000; `null` uncaps. Streams are uncapped. */\n totalTimeoutMs?: number | null;\n /** Fires when headers arrive, before any event. */\n onStreamStart?: (requestId: string | null, conversationId: string | null) => void;\n onStreamEvent?: (event: E) => void;\n /**\n * Take ownership of the body instead of the executor draining it (a body is\n * consumed once). `onStreamEvent` is then not called; start / complete /\n * error still fire.\n */\n consumeStream?: (response: Response, ids: MatrxStreamIds) => Promise<void>;\n onStreamComplete?: (requestId: string | null, conversationId: string | null) => void;\n /** Fires for an HTTP error response on a stream (thrown failures are the caller's). */\n onStreamError?: (error: MatrxCallError) => void;\n}\n\nexport interface ExecuteMatrxCallHooks<E = MatrxStreamEnvelope> {\n /** Every v2 → v1 protocol downgrade. */\n onProtocolDowngrade?: (downgrade: MatrxProtocolDowngrade) => void;\n /** The host's stream parser (default `parseMatrxNdjsonResponse`). */\n parseStream?: (response: Response, signal?: AbortSignal) => MatrxStreamParse<E>;\n}\n\nconst DEFAULT_CONNECT_TIMEOUT_MS = 15_000;\nconst DEFAULT_JSON_TOTAL_TIMEOUT_MS = 30_000;\n\nasync function httpErrorFrom(response: Response): Promise<MatrxCallError> {\n const serverDetail: unknown = await response.json().catch(() => undefined);\n return {\n type:\n response.status >= 400 && response.status < 500\n ? \"validation_error\"\n : \"http_error\",\n message:\n extractMatrxErrorMessage(serverDetail) ?? bareStatusSentence(response.status),\n status: response.status,\n serverDetail,\n };\n}\n\n/**\n * Execute one call over the v2 → v1 protocol fallback. An HTTP error\n * response resolves as `{ error }`; a thrown failure (network, timeout,\n * abort, a broken stream) propagates — the caller normalizes it with\n * `normalizeMatrxError` and decides what to capture.\n */\nexport async function executeMatrxCall<T = unknown, E = MatrxStreamEnvelope>(\n request: ExecuteMatrxCallRequest<E>,\n hooks: ExecuteMatrxCallHooks<E> = {},\n): Promise<MatrxCallResult<T>> {\n const upper = request.method.toUpperCase();\n const sendsBody = request.stream || (upper !== \"GET\" && upper !== \"HEAD\");\n const { response } = await fetchWithMatrxProtocolFallback(\n request.url,\n {\n method: request.method,\n headers: request.headers,\n ...(sendsBody ? { body: JSON.stringify(request.body) } : {}),\n },\n {\n ...(request.signal ? { signal: request.signal } : {}),\n connectTimeoutMs: request.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,\n totalTimeoutMs: request.stream\n ? null\n : request.totalTimeoutMs === undefined\n ? DEFAULT_JSON_TOTAL_TIMEOUT_MS\n : request.totalTimeoutMs,\n throwOnHttpError: false,\n ...(hooks.onProtocolDowngrade ? { onDowngrade: hooks.onProtocolDowngrade } : {}),\n },\n );\n\n const requestId = response.headers.get(\"X-Request-ID\");\n const conversationId = response.headers.get(\"X-Conversation-ID\");\n const idFields = {\n ...(requestId !== null ? { requestId } : {}),\n ...(conversationId !== null ? { conversationId } : {}),\n };\n\n if (!response.ok) {\n const error = await httpErrorFrom(response);\n if (request.stream) request.onStreamError?.(error);\n return { ...idFields, error };\n }\n\n if (!request.stream) {\n const data = (response.status === 204 ? undefined : await response.json()) as T;\n return { data, ...idFields };\n }\n\n if (request.consumeStream) {\n request.onStreamStart?.(requestId, conversationId);\n await request.consumeStream(response, { requestId, conversationId });\n request.onStreamComplete?.(requestId, conversationId);\n return idFields;\n }\n\n const parse =\n hooks.parseStream ??\n ((res: Response, signal?: AbortSignal) =>\n parseMatrxNdjsonResponse(res, signal) as unknown as MatrxStreamParse<E>);\n const parsed = parse(response, request.signal);\n request.onStreamStart?.(parsed.requestId, parsed.conversationId);\n for await (const event of parsed.events) {\n request.onStreamEvent?.(event);\n }\n request.onStreamComplete?.(parsed.requestId, parsed.conversationId);\n return {\n ...(parsed.requestId !== null ? { requestId: parsed.requestId } : {}),\n ...(parsed.conversationId !== null ? { conversationId: parsed.conversationId } : {}),\n };\n}\n","/**\n * Internal request plumbing for `@ai-matrx/agents/matrx`. Not part of the\n * public surface — `matrx/index.ts` deliberately does not re-export this\n * module. Everything here is pure: no globals, no work at import time.\n */\n\nimport {\n readMatrxNdjsonStream,\n type MatrxNdjsonIssue,\n type MatrxStreamEnvelope,\n type MatrxStreamEnvelopeObservation,\n} from \"../stream/ndjson\";\nimport { MatrxApiError, type MatrxTransport } from \"./transport\";\n\n/** Encode one path segment (an id) safely into a server-relative path. */\nexport function encodePathSegment(value: string): string {\n return encodeURIComponent(value);\n}\n\nexport type QueryValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined;\n\n/**\n * Build a query string. Array values repeat the key (`kind=a&kind=b` — the\n * FastAPI repeatable-parameter convention); undefined values are omitted.\n * Returns \"\" or a string starting with \"?\".\n */\nexport function buildQuery(params: Record<string, QueryValue>): string {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const entry of value) search.append(key, entry);\n } else {\n search.append(key, String(value));\n }\n }\n const encoded = search.toString();\n return encoded ? `?${encoded}` : \"\";\n}\n\nasync function readServerDetail(response: Response): Promise<unknown> {\n try {\n return (await response.json()) as unknown;\n } catch {\n return undefined;\n }\n}\n\nasync function throwApiError(path: string, response: Response): Promise<never> {\n throw new MatrxApiError({\n status: response.status,\n path,\n serverDetail: await readServerDetail(response),\n });\n}\n\nexport interface JsonRequestOptions {\n method: \"GET\" | \"POST\";\n body?: unknown;\n signal?: AbortSignal;\n}\n\n/**\n * Execute a JSON request through the transport. Throws `MatrxApiError` on a\n * non-2xx response; resolves with the parsed JSON body otherwise.\n */\nexport async function requestJson<T>(\n transport: MatrxTransport,\n path: string,\n options: JsonRequestOptions,\n): Promise<T> {\n const hasBody = options.method !== \"GET\" && options.body !== undefined;\n const response = await transport.fetch(path, {\n method: options.method,\n headers: hasBody ? { \"Content-Type\": \"application/json\" } : {},\n ...(hasBody ? { body: JSON.stringify(options.body) } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n });\n if (!response.ok) return throwApiError(path, response);\n return (await response.json()) as T;\n}\n\n/**\n * Options for every streaming call, riding the NDJSON kernel's contract.\n * Public via `./run`'s re-export.\n */\nexport interface MatrxStreamCallOptions {\n /** Abort the fetch and end the events iterator. */\n signal?: AbortSignal;\n /** Bounded background read-ahead (see `stream/ndjson`). */\n maxReadAhead?: number;\n /** Malformed NDJSON is non-fatal but must never disappear silently. */\n onMalformedLine?: (issue: MatrxNdjsonIssue) => void;\n /** Valid JSON with no recognized Matrx envelope. */\n onUnknownEnvelope?: (value: unknown) => void;\n /** Observe every valid envelope in its exact wire form. */\n onValidEnvelope?: (observation: MatrxStreamEnvelopeObservation) => void;\n}\n\n/**\n * A live agent run: the server-assigned ids (from response headers, available\n * BEFORE any event) and the normalized event stream. Public via `./run`.\n */\nexport interface MatrxRunHandle {\n /** `X-Request-ID` — the ONLY id `POST /ai/cancel/{request_id}` accepts. */\n requestId: string | null;\n /** `X-Conversation-ID` — the server's conversation identity. */\n conversationId: string | null;\n /** Normalized `{event, data}` envelopes through the ONE wire kernel. */\n events: AsyncGenerator<MatrxStreamEnvelope, void, undefined>;\n /** The raw response, for hosts that need headers/status beyond the ids. */\n response: Response;\n}\n\n/**\n * Wrap a validated streaming Response into the run handle — the ONE place\n * the id headers are read and the NDJSON kernel is attached (`./run` and\n * `./operations`' rejoin share it).\n */\nexport function toRunHandle(\n response: Response,\n options: MatrxStreamCallOptions,\n): MatrxRunHandle {\n return {\n requestId: response.headers.get(\"X-Request-ID\"),\n conversationId: response.headers.get(\"X-Conversation-ID\"),\n events: readMatrxNdjsonStream(response.body as ReadableStream<Uint8Array>, {\n ...(options.signal ? { signal: options.signal } : {}),\n ...(options.maxReadAhead !== undefined\n ? { maxReadAhead: options.maxReadAhead }\n : {}),\n ...(options.onMalformedLine\n ? { onMalformedLine: options.onMalformedLine }\n : {}),\n ...(options.onUnknownEnvelope\n ? { onUnknownEnvelope: options.onUnknownEnvelope }\n : {}),\n ...(options.onValidEnvelope\n ? { onValidEnvelope: options.onValidEnvelope }\n : {}),\n }),\n response,\n };\n}\n\nexport interface StreamRequestOptions {\n method: \"GET\" | \"POST\";\n body?: unknown;\n /** Extra wire-semantic headers (`Accept`, `Last-Event-ID`). */\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\n/**\n * Execute a streaming request. Throws `MatrxApiError` on a non-2xx response\n * (reading the error body as JSON when possible) or when a 2xx response\n * carries no body; resolves with the validated `Response` otherwise.\n */\nexport async function requestStream(\n transport: MatrxTransport,\n path: string,\n options: StreamRequestOptions,\n): Promise<Response> {\n const hasBody = options.method !== \"GET\" && options.body !== undefined;\n const response = await transport.fetch(path, {\n method: options.method,\n headers: {\n ...(hasBody ? { \"Content-Type\": \"application/json\" } : {}),\n ...options.headers,\n },\n ...(hasBody ? { body: JSON.stringify(options.body) } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n });\n if (!response.ok) return throwApiError(path, response);\n if (!response.body) {\n throw new MatrxApiError({\n status: response.status,\n path,\n serverDetail: { code: \"missing_response_body\" },\n message: \"The streaming response carried no body.\",\n });\n }\n return response;\n}\n","/**\n * `@ai-matrx/agents/stream/sse` — the Matrx SSE frame kernel.\n *\n * Two Matrx clients hand-rolled the identical `text/event-stream` framing —\n * the same separator regex, the same field parsing, the same CRLF incident —\n * for the durable rejoin endpoints (`/runtime/.../events/stream`,\n * `/runs/.../events/stream`). This module is that framing extracted ONCE, as a\n * pure incremental parser, so a host keeps only what is genuinely host policy:\n * the fetch, the stall timer, the retry budget, and what each event MEANS.\n *\n * Contract, mirroring `stream/ndjson`:\n * - Pure and effect-free: no fetch, no timers, no globals. Importing this\n * module performs no work.\n * - Incremental across arbitrary chunk boundaries: a frame split anywhere —\n * mid-line, mid-separator, mid-UTF-8 when using the byte reader — parses\n * identically to one delivered whole.\n * - All three SSE line terminators (`\\r\\n`, `\\n`, `\\r`) and all three frame\n * separators are handled — the CRLF-vs-LF divergence that bit production is\n * covered by construction and by test.\n * - Comment-only frames (heartbeats, `:` lines) ARE emitted (with\n * `data === null`) because hosts use any parsed frame as liveness proof to\n * reset stall timers and retry budgets. Frames with data join multi-line\n * `data:` fields with `\\n` per the SSE spec.\n * - `id:` is surfaced raw AND, when it is a safe integer, as `seq` — the\n * `Last-Event-ID` cursor both Matrx rejoin endpoints use for durable replay.\n * Cursor ADVANCEMENT (`seq > cursor`) stays host-side, next to the retry.\n */\n\nexport interface MatrxSseFrame {\n /** `event:` field; the SSE default \"message\" when absent. */\n event: string;\n /** `id:` field, raw, when present. */\n id: string | null;\n /**\n * `id:` parsed as a non-negative safe integer, else null. Matrx rejoin\n * streams use integer ids as the `Last-Event-ID` replay cursor.\n */\n seq: number | null;\n /**\n * Joined `data:` lines (`\\n`-separated per spec), or null for a frame with\n * no data field at all (e.g. a comment-only heartbeat). An empty-string\n * data field is `\"\"`, not null.\n */\n data: string | null;\n}\n\nconst FRAME_SEPARATOR = /\\r\\n\\r\\n|\\n\\n|\\r\\r/;\nconst LINE_SEPARATOR = /\\r\\n|\\n|\\r/;\n\n/** Parse ONE complete frame's text (no trailing separator). */\nexport function parseMatrxSseFrame(frame: string): MatrxSseFrame {\n let event = \"message\";\n let id: string | null = null;\n const dataLines: string[] = [];\n let sawData = false;\n\n for (const line of frame.split(LINE_SEPARATOR)) {\n if (line.startsWith(\":\")) continue;\n if (line.startsWith(\"event:\")) event = line.slice(6).trim();\n else if (line.startsWith(\"data:\")) {\n sawData = true;\n dataLines.push(line.slice(5).replace(/^ /, \"\"));\n } else if (line.startsWith(\"id:\")) id = line.slice(3).trim();\n }\n\n const seqCandidate = id !== null && id !== \"\" ? Number(id) : NaN;\n const seq =\n Number.isSafeInteger(seqCandidate) && seqCandidate >= 0\n ? seqCandidate\n : null;\n\n return { event, id, seq, data: sawData ? dataLines.join(\"\\n\") : null };\n}\n\nexport interface MatrxSseFramer {\n /** Feed a decoded text chunk; returns every frame it completed. */\n push(chunk: string): MatrxSseFrame[];\n /**\n * Signal end of input. A non-empty trailing buffer is an UNTERMINATED frame:\n * per the SSE spec it was never dispatched, so it is returned separately for\n * the host to treat as diagnostic, never as a delivered event.\n */\n flush(): { incomplete: string | null };\n}\n\n/** Incremental SSE framer over already-decoded text. */\nexport function createMatrxSseFramer(): MatrxSseFramer {\n let buffer = \"\";\n return {\n push(chunk: string): MatrxSseFrame[] {\n buffer += chunk;\n const frames: MatrxSseFrame[] = [];\n for (;;) {\n const sep = FRAME_SEPARATOR.exec(buffer);\n if (sep === null) break;\n // A lone trailing `\\r` could be the first half of `\\r\\n\\r\\n`'s final\n // newline — but the separator regex only matched what is already\n // complete, so the slice below is always safe.\n const frame = buffer.slice(0, sep.index);\n buffer = buffer.slice(sep.index + sep[0].length);\n frames.push(parseMatrxSseFrame(frame));\n }\n return frames;\n },\n flush() {\n const rest = buffer;\n buffer = \"\";\n return { incomplete: rest.length > 0 ? rest : null };\n },\n };\n}\n\nexport interface ReadMatrxSseOptions {\n /**\n * Called with any unterminated trailing text at stream end (a frame the\n * server never finished — diagnostic, not a delivered event).\n */\n onIncomplete?: (text: string) => void;\n}\n\n/**\n * Async-iterate the frames of a byte stream (e.g. `response.body`), handling\n * split UTF-8 across chunk boundaries. Cancellation follows the reader: abort\n * the fetch and the iterator ends; a transport error after complete frames\n * were yielded surfaces AFTER those frames, with its original cause.\n */\nexport async function* readMatrxSseStream(\n stream: ReadableStream<Uint8Array>,\n options: ReadMatrxSseOptions = {},\n): AsyncGenerator<MatrxSseFrame, void, undefined> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n const framer = createMatrxSseFramer();\n try {\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n const frames = framer.push(decoder.decode(value, { stream: true }));\n for (const frame of frames) yield frame;\n }\n const tail = framer.push(decoder.decode());\n for (const frame of tail) yield frame;\n const { incomplete } = framer.flush();\n if (incomplete !== null) options.onIncomplete?.(incomplete);\n } finally {\n reader.releaseLock();\n }\n}\n","/**\n * Runtime operations — the canonical reconnect & resume read surface of the\n * execution spine, over the `MatrxTransport` port.\n *\n * Server truth (verified against aidream source,\n * `aidream/api/routers/runtime_operations.py` + `aidream/services/runtime/\n * reconnect.py`; mounted at bare `/runtime`):\n * - `GET /runtime/operations/{request_id}` — identify by `X-Request-ID`\n * - `GET /runtime/operations/by-link/{kind}/{id}` — identify by feature record\n * - `GET /runtime/executions/{id}/events` — durable seq-cursored page\n * - `GET /runtime/executions/{id}/events/stream` — SSE replay-then-follow,\n * `id:` = per-tree seq, reconnect with `Last-Event-ID`\n * - `POST /runtime/operations/{request_id}/rejoin` — replay + follow the\n * ORIGINAL NDJSON response while its detached task is alive (409 when live\n * delivery is unavailable — fall back to the durable lifecycle stream)\n *\n * The contract: identify → recover durable progress → follow live → re-query\n * the final result from the feature's own record. Token text is deliberately\n * never replayed on the lifecycle stream — that is what `/rejoin` is for.\n *\n * The SSE wire rides the package's own `stream/sse` kernel. This module owns\n * ONE connection's semantics (frames → typed events, cursor advancement,\n * terminal `end`); stall timers, retry budgets, and reconnect loops stay host\n * policy — every yielded item carries the cursor the next attempt resumes from.\n */\n\nimport { readMatrxSseStream, type MatrxSseFrame } from \"../stream/sse\";\nimport type { MatrxJsonObject } from \"./conversation\";\nimport {\n encodePathSegment,\n buildQuery,\n requestJson,\n requestStream,\n toRunHandle,\n type MatrxRunHandle,\n type MatrxStreamCallOptions,\n} from \"./internal\";\nimport { MatrxApiError, type MatrxTransport } from \"./transport\";\n\n// ─── Wire types (mirroring `aidream/services/runtime/reconnect.py`) ─────────\n\n/** `matrx_runtime.models.ExecutionStatus` — the only progress column. */\nexport type MatrxRuntimeExecutionStatus =\n | \"pending\"\n | \"running\"\n | \"paused\"\n | \"waiting_input\"\n | \"completed\"\n | \"failed\"\n | \"cancelled\";\n\nexport const TERMINAL_MATRX_RUNTIME_STATUSES: ReadonlySet<MatrxRuntimeExecutionStatus> =\n new Set([\"completed\", \"failed\", \"cancelled\"]);\n\nconst RUNTIME_STATUSES: ReadonlySet<string> = new Set([\n \"pending\",\n \"running\",\n \"paused\",\n \"waiting_input\",\n \"completed\",\n \"failed\",\n \"cancelled\",\n]);\n\n/** One durable spine event on the wire (`OperationEvent`) — `seq` is the reconnect cursor. */\nexport interface MatrxRuntimeOperationEvent {\n seq: number | null;\n /** Lifecycle vocabulary: created | started | paused | resumed | waiting_input | completed | failed | cancelled | checkpoint_saved | note. */\n kind: string;\n execution_id: string;\n root_execution_id: string | null;\n detail: MatrxJsonObject | null;\n created_at: string | null;\n}\n\n/** One root execution as a reconnecting client sees it (`OperationView`). */\nexport interface MatrxRuntimeOperationView {\n execution_id: string;\n /** Durable request identity — feeds `/rejoin` and no-prompt resume recovery. */\n request_id: string | null;\n type: string;\n status: MatrxRuntimeExecutionStatus;\n is_terminal: boolean;\n waiting_input: boolean;\n /** Decimal on the wire — may arrive as number or string; display-only. */\n cost: number | string;\n meters: Record<string, number | string>;\n link_kind: string | null;\n link_id: string | null;\n error: MatrxJsonObject | null;\n created_at: string | null;\n started_at: string | null;\n ended_at: string | null;\n last_event_seq: number;\n events_path: string;\n stream_path: string;\n}\n\nexport interface MatrxOperationStatusResponse {\n request_id: string;\n operation_count: number;\n operations: MatrxRuntimeOperationView[];\n}\n\nexport interface MatrxOperationsByLinkResponse {\n link_kind: string;\n link_id: string;\n operation_count: number;\n operations: MatrxRuntimeOperationView[];\n}\n\nexport interface MatrxOperationEventsPage {\n execution_id: string;\n root_execution_id: string;\n events: MatrxRuntimeOperationEvent[];\n /** Feeds the next page or the SSE `Last-Event-ID` — polling and push share ONE cursor. */\n next_after_seq: number;\n has_more: boolean;\n root_status: MatrxRuntimeExecutionStatus;\n root_is_terminal: boolean;\n}\n\n// ─── Identify + durable progress ────────────────────────────────────────────\n\n/**\n * Where is my operation? Resolves an `X-Request-ID` to its root execution(s).\n * Returns null on 404 — missing and unowned share one shape by design\n * (existence is never leaked).\n */\nexport async function getRuntimeOperationStatus(\n transport: MatrxTransport,\n requestId: string,\n options: { signal?: AbortSignal } = {},\n): Promise<MatrxOperationStatusResponse | null> {\n try {\n return await requestJson<MatrxOperationStatusResponse>(\n transport,\n `/runtime/operations/${encodePathSegment(requestId)}`,\n { method: \"GET\", ...(options.signal ? { signal: options.signal } : {}) },\n );\n } catch (error) {\n if (error instanceof MatrxApiError && error.status === 404) return null;\n throw error;\n }\n}\n\n/**\n * Operations for a feature record — e.g. `(\"conversation\", conversationId)`,\n * `(\"workflow\", runId)`, `(\"agent_run\", runId)`. Newest first; unowned trees\n * omitted. Returns null on 404 (surface absent, or the caller owns nothing —\n * one shape by design).\n */\nexport async function getRuntimeOperationsByLink(\n transport: MatrxTransport,\n linkKind: string,\n linkId: string,\n options: { limit?: number; signal?: AbortSignal } = {},\n): Promise<MatrxOperationsByLinkResponse | null> {\n const query = buildQuery(\n options.limit !== undefined ? { limit: options.limit } : {},\n );\n try {\n return await requestJson<MatrxOperationsByLinkResponse>(\n transport,\n `/runtime/operations/by-link/${encodePathSegment(linkKind)}/${encodePathSegment(linkId)}${query}`,\n { method: \"GET\", ...(options.signal ? { signal: options.signal } : {}) },\n );\n } catch (error) {\n if (error instanceof MatrxApiError && error.status === 404) return null;\n throw error;\n }\n}\n\n/**\n * Durable progress page for the whole operation TREE:\n * `GET /runtime/executions/{id}/events?after_seq=…`. Pass any node id — it\n * resolves to the root.\n */\nexport function listRuntimeOperationEvents(\n transport: MatrxTransport,\n executionId: string,\n options: {\n afterSeq?: number;\n limit?: number;\n /** Repeatable event-kind filter. */\n kinds?: readonly string[];\n signal?: AbortSignal;\n } = {},\n): Promise<MatrxOperationEventsPage> {\n const query = buildQuery({\n ...(options.afterSeq !== undefined ? { after_seq: options.afterSeq } : {}),\n ...(options.limit !== undefined ? { limit: options.limit } : {}),\n ...(options.kinds !== undefined ? { kind: options.kinds } : {}),\n });\n return requestJson<MatrxOperationEventsPage>(\n transport,\n `/runtime/executions/${encodePathSegment(executionId)}/events${query}`,\n { method: \"GET\", ...(options.signal ? { signal: options.signal } : {}) },\n );\n}\n\n// ─── SSE follow (replay-then-follow with Last-Event-ID) ─────────────────────\n\n/**\n * One item from the follow stream. Every item carries `cursor` — the highest\n * event seq seen so far, which is exactly the `Last-Event-ID` a reconnect\n * resumes from (host retry policy owns the reconnect loop).\n */\nexport type MatrxOperationFollowEvent =\n | {\n /** A parsed durable spine event. */\n type: \"event\";\n event: MatrxRuntimeOperationEvent;\n /** The frame's SSE `id:` as an integer, when it carried one. */\n seq: number | null;\n cursor: number;\n }\n | {\n /**\n * A frame that carried no deliverable event — a comment heartbeat, an\n * unknown event name, or a malformed payload (also surfaced through\n * `onMalformedFrame`). ANY parsed frame proves the wire is alive: hosts\n * reset stall timers and retry budgets on it.\n */\n type: \"liveness\";\n cursor: number;\n }\n | {\n /** The server's terminal frame — the root settled; the stream is over. */\n type: \"end\";\n status: MatrxRuntimeExecutionStatus | null;\n cursor: number;\n };\n\nexport interface FollowRuntimeOperationOptions {\n /** Resume cursor — the operation view's `last_event_seq` (0 = from start). */\n lastEventSeq?: number;\n /** Abort the follow — the generator simply ends. */\n signal?: AbortSignal;\n /** A frame whose payload failed to parse — never silently dropped. */\n onMalformedFrame?: (frame: MatrxSseFrame, error: unknown) => void;\n /** Unterminated trailing SSE text at stream end (diagnostic, never an event). */\n onIncomplete?: (text: string) => void;\n}\n\nfunction parseEndStatus(data: string | null): MatrxRuntimeExecutionStatus | null {\n if (data === null) return null;\n try {\n const parsed = JSON.parse(data) as unknown;\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n typeof (parsed as { status?: unknown }).status === \"string\"\n ) {\n const status = (parsed as { status: string }).status;\n return RUNTIME_STATUSES.has(status)\n ? (status as MatrxRuntimeExecutionStatus)\n : null;\n }\n } catch {\n // malformed end payload — still terminal\n }\n return null;\n}\n\n/**\n * Follow ONE SSE connection of an operation's lifecycle stream:\n * `GET /runtime/executions/{id}/events/stream` with `Last-Event-ID` when\n * resuming past 0. Replays from the cursor, then follows live; a\n * WAITING_INPUT park keeps it open (a resume re-attaches to the same\n * execution and its events continue here). Ends after yielding\n * `{type: \"end\"}` when the root settles; a server close WITHOUT an end frame\n * simply ends the generator — reconnect from the last yielded `cursor` (host\n * retry policy).\n */\nexport async function* followRuntimeOperationEvents(\n transport: MatrxTransport,\n executionId: string,\n options: FollowRuntimeOperationOptions = {},\n): AsyncGenerator<MatrxOperationFollowEvent, void, undefined> {\n let cursor = options.lastEventSeq ?? 0;\n\n const headers: Record<string, string> = { Accept: \"text/event-stream\" };\n if (cursor > 0) headers[\"Last-Event-ID\"] = String(cursor);\n\n const response = await requestStream(\n transport,\n `/runtime/executions/${encodePathSegment(executionId)}/events/stream`,\n {\n method: \"GET\",\n headers,\n ...(options.signal ? { signal: options.signal } : {}),\n },\n );\n\n const frames = readMatrxSseStream(\n response.body as ReadableStream<Uint8Array>,\n options.onIncomplete ? { onIncomplete: options.onIncomplete } : {},\n );\n\n for await (const frame of frames) {\n if (frame.event === \"end\") {\n yield { type: \"end\", status: parseEndStatus(frame.data), cursor };\n return;\n }\n if (frame.event === \"execution_event\" && frame.data !== null) {\n let event: MatrxRuntimeOperationEvent;\n try {\n event = JSON.parse(frame.data) as MatrxRuntimeOperationEvent;\n } catch (error) {\n options.onMalformedFrame?.(frame, error);\n yield { type: \"liveness\", cursor };\n continue;\n }\n if (frame.seq !== null && frame.seq > cursor) cursor = frame.seq;\n yield { type: \"event\", event, seq: frame.seq, cursor };\n continue;\n }\n // Comment heartbeats, unknown event names, data-less frames: liveness.\n yield { type: \"liveness\", cursor };\n }\n}\n\n// ─── Follow to end (the production reconnect policy) ────────────────────────\n\nexport interface FollowRuntimeOperationToEndOptions {\n /** Resume cursor — the operation view's `last_event_seq` (0 = from start). */\n lastEventSeq?: number;\n /** Caller teardown — aborting resolves with `ended: false`. */\n signal?: AbortSignal;\n /**\n * The server pings every ~15s, so a wire that is open but silent past this\n * is dead (buffering proxy, idle-killed connection) — abort the attempt and\n * retry rather than hanging forever. Default 45_000.\n */\n stallTimeoutMs?: number;\n /**\n * Consecutive failed attempts before giving up. A single-server deployment\n * deliberately drains for 60s, then starts a new container — the default\n * budget (60 × 2s) keeps following for ~three minutes so the runtime ledger\n * can bridge that handoff. ANY parsed frame resets the budget. Default 60.\n */\n reconnectLimit?: number;\n /** Delay between attempts. Default 2_000. */\n reconnectDelayMs?: number;\n /** Fired per durable spine event (lifecycle transitions + notes). */\n onEvent: (event: MatrxRuntimeOperationEvent, seq: number | null) => void;\n /** A failed attempt (never silently swallowed when provided). */\n onAttemptError?: (error: unknown) => void;\n /** A frame whose payload failed to parse (the ledger heals gaps on reconnect). */\n onMalformedFrame?: (frame: MatrxSseFrame, error: unknown) => void;\n}\n\nexport interface FollowRuntimeOperationToEndResult {\n /** True when the server sent the terminal `end` frame. */\n ended: boolean;\n /** The root status carried on the `end` frame (when `ended`). */\n status: MatrxRuntimeExecutionStatus | null;\n}\n\n/**\n * Follow an operation's lifecycle stream TO ITS END — the full production\n * reconnect policy over `followRuntimeOperationEvents`: replay-then-follow\n * with bounded reconnects, a stall watchdog, and durable `Last-Event-ID`\n * cursor advancement across attempts.\n *\n * Resolves `{ended: true, status}` on the server's `end` frame (the operation\n * settled); `{ended: false}` when the caller aborted or every reconnect\n * attempt failed. A WAITING_INPUT park keeps the stream open by design — a\n * resume re-attaches to the same execution and its events continue arriving\n * on the same cursor. Any parsed frame — comment heartbeats included —\n * proves the wire is alive and resets both the stall timer and the retry\n * budget.\n */\nexport async function followRuntimeOperationToEnd(\n transport: MatrxTransport,\n executionId: string,\n options: FollowRuntimeOperationToEndOptions,\n): Promise<FollowRuntimeOperationToEndResult> {\n const stallTimeoutMs = options.stallTimeoutMs ?? 45_000;\n const reconnectLimit = options.reconnectLimit ?? 60;\n const reconnectDelayMs = options.reconnectDelayMs ?? 2_000;\n const outer = options.signal;\n\n let cursor = options.lastEventSeq ?? 0;\n let failures = 0;\n\n while (!outer?.aborted && failures < reconnectLimit) {\n const attempt = new AbortController();\n const onOuterAbort = () => attempt.abort();\n outer?.addEventListener(\"abort\", onOuterAbort, { once: true });\n\n let stallTimer: ReturnType<typeof setTimeout> | null = null;\n const armStall = () => {\n if (stallTimer !== null) clearTimeout(stallTimer);\n stallTimer = setTimeout(() => attempt.abort(), stallTimeoutMs);\n };\n\n try {\n const items = followRuntimeOperationEvents(transport, executionId, {\n lastEventSeq: cursor,\n signal: attempt.signal,\n ...(options.onMalformedFrame\n ? { onMalformedFrame: options.onMalformedFrame }\n : {}),\n });\n\n armStall();\n for await (const item of items) {\n armStall();\n failures = 0;\n cursor = item.cursor;\n if (item.type === \"end\") {\n return { ended: true, status: item.status };\n }\n if (item.type === \"event\") {\n options.onEvent(item.event, item.seq);\n }\n }\n // Server closed without an `end` frame (e.g. process restart). Retry —\n // `Last-Event-ID` replays anything missed from the durable ledger.\n failures += 1;\n } catch (error) {\n if (outer?.aborted) break;\n failures += 1;\n options.onAttemptError?.(error);\n } finally {\n if (stallTimer !== null) clearTimeout(stallTimer);\n outer?.removeEventListener(\"abort\", onOuterAbort);\n }\n\n if (!outer?.aborted && failures < reconnectLimit) {\n await new Promise((r) => setTimeout(r, reconnectDelayMs));\n }\n }\n\n return { ended: false, status: null };\n}\n\n// ─── NDJSON rejoin (replay the original response) ───────────────────────────\n\n/**\n * Rejoin the ORIGINAL NDJSON response while its detached task is still alive:\n * `POST /runtime/operations/{request_id}/rejoin`. Replays the response from\n * frame one, then continues live — every frame is sequence-stamped\n * (`stream_seq`), so a same-page reconnect can drop frames it already\n * rendered. Throws `MatrxApiError` with status 409 when live delivery is\n * unavailable — fall back to `followRuntimeOperationEvents` + a final record\n * re-query. The `requestId` must be the server's `X-Request-ID`.\n */\nexport async function rejoinRuntimeOperation(\n transport: MatrxTransport,\n requestId: string,\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n const response = await requestStream(\n transport,\n `/runtime/operations/${encodePathSegment(requestId)}/rejoin`,\n {\n method: \"POST\",\n // The route takes no body model; the reference client posts an empty\n // JSON object. Match it so proxies see an ordinary JSON POST.\n body: {},\n ...(options.signal ? { signal: options.signal } : {}),\n },\n );\n return toRunHandle(response, options);\n}\n","/**\n * Agent run lifecycle against the AI Matrx API — start, continue, resume,\n * cancel — over the `MatrxTransport` port, with every streaming response\n * parsed through the package's ONE NDJSON wire kernel (`stream/ndjson`).\n *\n * Server truth (verified against aidream source):\n * - `POST /ai/agents/{agent_id}` — start (`aidream/api/routers/agents.py`)\n * - `POST /ai/conversations/{conversation_id}` — continue (`aidream/api/routers/conversations.py`)\n * - `POST /ai/conversations/{conversation_id}/resume` — resume after\n * client-delegated tool suspension (same router)\n * - `POST /ai/cancel/{request_id}?mode=interrupt` — cancel (`aidream/api/routers/cancel.py`)\n *\n * Response headers arrive before the body: `X-Conversation-ID` and\n * `X-Request-ID` are surfaced on the run handle immediately. `X-Request-ID`\n * is the ONLY id the server accepts for cancel — a client-local id means\n * nothing to it.\n *\n * Host policy stays out: no retry, no store, no timeouts, no persistence\n * (C10: no-persistence). Cancellation is the caller's `AbortSignal`; a client\n * disconnect never stops server work (`detach_on_disconnect`).\n */\n\nimport type { MatrxStreamEnvelope } from \"../stream/ndjson\";\nimport type {\n MatrxConversationStart,\n MatrxJsonObject,\n MatrxJsonValue,\n} from \"./conversation\";\nimport {\n encodePathSegment,\n buildQuery,\n requestJson,\n requestStream,\n toRunHandle,\n type MatrxRunHandle,\n type MatrxStreamCallOptions,\n} from \"./internal\";\nimport type { MatrxTransport } from \"./transport\";\nimport { streamErrorText } from \"./rejoin\";\n\nexport type { MatrxRunHandle, MatrxStreamCallOptions } from \"./internal\";\n\n// ─── Request shapes (mirroring the server's Pydantic models) ────────────────\n\n/**\n * Stable identity of the durable entity whose saved context owns a run —\n * the server reloads the row and uses ITS scope (`ContextAnchor`,\n * `aidream/services/conversation_context/scope.py`).\n */\nexport interface MatrxContextAnchor {\n resource_type: string;\n resource_id: string;\n}\n\n/**\n * Scope and source fields shared by every scoped request\n * (`ScopedRequest` / `AcceptsInjectedScope` server-side). All optional here;\n * the start request narrows `organization_id` to required.\n */\nexport interface MatrxRequestScope {\n organization_id?: string;\n project_id?: string | null;\n task_id?: string | null;\n /** Active context-scope ids from the client's global picker (membership-validated server-side). */\n scope_ids?: string[] | null;\n /** Active scope-TYPE ids — a type-level selection with no specific scope chosen. */\n active_scope_type_ids?: string[] | null;\n context_anchor?: MatrxContextAnchor | null;\n /** Stable application slug that initiated the request. */\n source_app?: string | null;\n /** Stable feature slug within the source application. */\n source_feature?: string | null;\n /** \"user\" = a person directly triggered this; \"auto\" = client automation; omit for API callers. */\n initiation?: \"user\" | \"auto\" | null;\n /** Specific connected desktop instance allowed to claim delegated local tools. */\n target_instance_id?: string | null;\n}\n\n/**\n * Fields shared by start/continue turn requests (tool injection, client\n * capability envelope, context object). The complex bags (`tools`, `client`,\n * `user`, `config_overrides`) are typed as JSON objects — their authoritative\n * schemas are the server's Pydantic models and the generated API types;\n * this package stays payload-agnostic about them by design.\n */\nexport interface MatrxTurnFields {\n /** What the human typed (string), or structured input parts. Never smuggle machine content here. */\n user_input?: string | MatrxJsonValue[] | null;\n /** Per-run model/config overrides (LLMParams shape). */\n config_overrides?: MatrxJsonObject | null;\n debug?: boolean;\n /** Additive tool specs merged into the agent's resolved tool set. */\n tools?: MatrxJsonObject[];\n /** When set, becomes the agent's ENTIRE tool set for the turn. */\n tools_replace?: MatrxJsonObject[] | null;\n /** Client capability envelope (`ClientContext`). */\n client?: MatrxJsonObject | null;\n /** Per-request user-level tool inclusion/exclusion overrides. */\n user?: MatrxJsonObject | null;\n /** Per-route context object, free-form by design. */\n context?: MatrxJsonObject;\n writable_variables?: string[];\n allow_context_create?: boolean;\n /** Request-snapshot capture override (tri-state; omit for the platform default). */\n snapshot?: boolean | null;\n}\n\n/**\n * `POST /ai/agents/{agent_id}` body (`AgentStartRequest` server-side).\n * The conversation-start triple is required by construction; `organization_id`\n * is required (the server 422s a blank one — it never manufactures an org).\n * `stream` is not accepted here: this client is the streaming path and always\n * sends `stream: true`.\n */\nexport type MatrxAgentStartRequest = MatrxConversationStart &\n Omit<MatrxRequestScope, \"organization_id\"> &\n MatrxTurnFields & {\n organization_id: string;\n /** Variable name → value map filling the agent's declared variables. */\n variables?: MatrxJsonObject | null;\n /** Run the versions table row instead of the live agent row. */\n is_version?: boolean;\n max_iterations?: number;\n max_retries_per_iteration?: number;\n };\n\n/**\n * `POST /ai/mandates/{mandate_key}` uses the exact saved-agent start body.\n * The server resolves the mandate's holder, binding, and configuration; a\n * client must offer only the declared variables and human input here.\n */\nexport type MatrxMandateStartRequest = MatrxAgentStartRequest;\n\n/** `POST /ai/conversations/{id}` body (`ConversationContinueRequest` server-side). */\nexport type MatrxConversationContinueRequest = MatrxRequestScope &\n MatrxTurnFields & {\n /** Re-run the conversation's current persisted state (recovery after a failed turn); omit `user_input`. */\n retry?: boolean;\n };\n\n/**\n * `POST /ai/conversations/{id}/resume` body (`ResumeRequest` server-side) —\n * the shipped durable continuation after client-delegated tool calls were\n * answered via `POST /tool_results` while the original stream was gone.\n * `user_request_id` is optional: when omitted the server resolves the turn\n * from the conversation's newest answered client-delegated tool call.\n * Re-send fresh `context` here — a resumed loop is otherwise context-blind.\n */\nexport type MatrxConversationResumeRequest = MatrxRequestScope & {\n user_request_id?: string | null;\n config_overrides?: MatrxJsonObject | null;\n debug?: boolean;\n tools?: MatrxJsonObject[];\n tools_replace?: MatrxJsonObject[] | null;\n client?: MatrxJsonObject | null;\n user?: MatrxJsonObject | null;\n context?: MatrxJsonObject;\n writable_variables?: string[];\n allow_context_create?: boolean;\n};\n\n/** `POST /ai/cancel/{request_id}` response (`CancelResponse` server-side). */\nexport interface MatrxCancelResponse {\n status: string;\n request_id: string;\n spine_executions_signalled: string[];\n}\n\n// ─── The run handle ─────────────────────────────────────────────────────────\n// `MatrxStreamCallOptions`, `MatrxRunHandle`, and the handle constructor live\n// in ./internal so `./operations`' rejoin shares the exact same construction.\n\nasync function streamCall(\n transport: MatrxTransport,\n path: string,\n body: object,\n options: MatrxStreamCallOptions,\n): Promise<MatrxRunHandle> {\n const response = await requestStream(transport, path, {\n method: \"POST\",\n // This client IS the streaming path — `stream: true` always, last so a\n // caller-supplied value can never flip the response off NDJSON.\n body: { ...body, stream: true },\n ...(options.signal ? { signal: options.signal } : {}),\n });\n return toRunHandle(response, options);\n}\n\n// ─── Lifecycle calls ────────────────────────────────────────────────────────\n\n/** Start an agent run: `POST /ai/agents/{agent_id}` (NDJSON stream). */\nexport function startAgentRun(\n transport: MatrxTransport,\n agentId: string,\n request: MatrxAgentStartRequest,\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n return streamCall(\n transport,\n `/ai/agents/${encodePathSegment(agentId)}`,\n request,\n options,\n );\n}\n\n/**\n * Start a declared mandate: `POST /ai/mandates/{mandate_key}` (NDJSON\n * stream). This is deliberately separate from `startAgentRun`: callers name\n * the mandate and never resolve or echo its holder/configuration themselves.\n */\nexport function startMandateRun(\n transport: MatrxTransport,\n mandateKey: string,\n request: MatrxMandateStartRequest,\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n return streamCall(\n transport,\n `/ai/mandates/${encodePathSegment(mandateKey)}`,\n request,\n options,\n );\n}\n\n/** Continue a stored conversation: `POST /ai/conversations/{id}` (NDJSON stream). */\nexport function continueAgentConversation(\n transport: MatrxTransport,\n conversationId: string,\n request: MatrxConversationContinueRequest,\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n return streamCall(\n transport,\n `/ai/conversations/${encodePathSegment(conversationId)}`,\n request,\n options,\n );\n}\n\n/**\n * Resume a suspended loop after delegated tool answers landed:\n * `POST /ai/conversations/{id}/resume` (NDJSON stream). A 409\n * (`resume_conflict`) means another resume holds the run claim — retrying is\n * host policy.\n */\nexport function resumeAgentConversation(\n transport: MatrxTransport,\n conversationId: string,\n request: MatrxConversationResumeRequest = {},\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n return streamCall(\n transport,\n `/ai/conversations/${encodePathSegment(conversationId)}/resume`,\n request,\n options,\n );\n}\n\n/**\n * Stop a running request at its next iteration boundary:\n * `POST /ai/cancel/{request_id}`. Cooperative and best-effort — the in-flight\n * provider call finishes by design, and everything already streamed persists.\n * `mode: \"interrupt\"` = stop-and-fork: the tail after the last clean boundary\n * persists hidden so the user's follow-up replies to what they actually saw.\n * The id must be the server's `X-Request-ID`.\n */\nexport function cancelAgentRun(\n transport: MatrxTransport,\n requestId: string,\n options: { mode?: \"cancel\" | \"interrupt\"; signal?: AbortSignal } = {},\n): Promise<MatrxCancelResponse> {\n const query = buildQuery(\n options.mode === \"interrupt\" ? { mode: \"interrupt\" } : {},\n );\n return requestJson<MatrxCancelResponse>(\n transport,\n `/ai/cancel/${encodePathSegment(requestId)}${query}`,\n {\n method: \"POST\",\n ...(options.signal ? { signal: options.signal } : {}),\n },\n );\n}\n\n// ─── End-to-end convenience: run to completion ──────────────────────────────\n\n/**\n * A run that terminated unsuccessfully: the server emitted a fatal `error`\n * event, or the `user_request` completion settled `failed`/`cancelled`.\n */\nexport class MatrxRunError extends Error {\n override readonly name = \"MatrxRunError\";\n /** The verbatim `error` event payload, when one fired. */\n readonly errorPayload: Record<string, unknown> | null;\n /** The `user_request` completion status (`\"failed\"` | `\"cancelled\"`), when that was the trigger. */\n readonly completionStatus: string | null;\n /** Text streamed before the failure — partial content never vanishes. */\n readonly partialText: string;\n\n constructor(args: {\n message: string;\n errorPayload?: Record<string, unknown> | null;\n completionStatus?: string | null;\n partialText?: string;\n }) {\n super(args.message);\n this.errorPayload = args.errorPayload ?? null;\n this.completionStatus = args.completionStatus ?? null;\n this.partialText = args.partialText ?? \"\";\n }\n}\n\nexport interface MatrxCompletedRun {\n /** Accumulated `chunk` text (falls back to the completion's `result.output`). */\n text: string;\n requestId: string | null;\n conversationId: string | null;\n /** The `user_request` completion payload, verbatim, when one arrived. */\n completion: Record<string, unknown> | null;\n}\n\nexport interface RunAgentToCompletionOptions extends MatrxStreamCallOptions {\n /** Live progress: the full accumulated text after each chunk. */\n onChunk?: (fullText: string) => void;\n /** Every normalized envelope, before this helper interprets it. */\n onEvent?: (envelope: MatrxStreamEnvelope) => void;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringField(value: unknown, key: string): string | null {\n if (!isRecord(value)) return null;\n const field = value[key];\n return typeof field === \"string\" && field ? field : null;\n}\n\n/**\n * Run an agent end-to-end and resolve with its full text output — the\n * package-level equivalent of the simplest existing host path\n * (`useRunAgent`): accumulate `chunk` text, treat a fatal `error` event or a\n * `failed`/`cancelled` `user_request` completion as a thrown `MatrxRunError`,\n * and fall back to the completion's `result.output` when no text streamed.\n *\n * The caller still owns the conversation-start triple on `request` — a\n * one-shot run typically uses `newEphemeralConversationStart()`.\n */\nexport async function runAgentToCompletion(\n transport: MatrxTransport,\n agentId: string,\n request: MatrxAgentStartRequest,\n options: RunAgentToCompletionOptions = {},\n): Promise<MatrxCompletedRun> {\n const handle = await startAgentRun(transport, agentId, request, options);\n\n let text = \"\";\n let completion: Record<string, unknown> | null = null;\n let failure: MatrxRunError | null = null;\n\n for await (const envelope of handle.events) {\n options.onEvent?.(envelope);\n\n if (envelope.event === \"chunk\") {\n const chunk = stringField(envelope.data, \"text\");\n if (chunk !== null) {\n text += chunk;\n options.onChunk?.(text);\n }\n continue;\n }\n\n if (envelope.event === \"error\" && failure === null) {\n const payload = isRecord(envelope.data) ? envelope.data : null;\n failure = new MatrxRunError({\n message: streamErrorText(payload) ?? \"The agent run failed\",\n errorPayload: payload,\n partialText: text,\n });\n continue;\n }\n\n if (envelope.event !== \"completion\" || !isRecord(envelope.data)) continue;\n // `init`/`completion` pairs exist for five operations; the terminal\n // outcome of the run is the `user_request` completion (STREAM-CONTRACT\n // §3.2 — always present, always last).\n if (envelope.data.operation !== \"user_request\") continue;\n\n completion = envelope.data;\n const status = envelope.data.status;\n if ((status === \"failed\" || status === \"cancelled\") && failure === null) {\n const result = isRecord(envelope.data.result) ? envelope.data.result : null;\n failure = new MatrxRunError({\n message:\n stringField(result, \"error\") ??\n stringField(result, \"user_message\") ??\n `The agent run ${status}`,\n completionStatus: status,\n partialText: text,\n });\n }\n }\n\n if (failure) throw failure;\n\n if (!text && completion) {\n const result = completion.result;\n const output = stringField(result, \"output\");\n if (output !== null) text = output;\n }\n\n return {\n text,\n requestId: handle.requestId,\n conversationId: handle.conversationId,\n completion,\n };\n}\n","/**\n * `useFollowRuntimeOperation` — the reconnect follower as a hook: follow one\n * runtime operation's durable lifecycle stream\n * (`GET /runtime/executions/{id}/events/stream`, `@ai-matrx/agents/stream/sse`\n * under the hood) with the FULL production reconnect policy built in —\n * stall watchdog, bounded retry budget, `Last-Event-ID` cursor — via\n * `followRuntimeOperationToEnd`. Tuning knobs are typed and default to the\n * proven production values (45s stall, 60 × 2s budget).\n *\n * The host injects the transport and what each event MEANS (`onEvent`);\n * everything else is package policy.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n followRuntimeOperationToEnd,\n type MatrxRuntimeExecutionStatus,\n type MatrxRuntimeOperationEvent,\n type MatrxTransport,\n} from \"../matrx/index\";\n\nexport interface UseFollowRuntimeOperationOptions {\n /** The transport (typically the production `createMatrxTransport`). */\n transport: MatrxTransport | (() => MatrxTransport);\n /** The execution to follow — null/undefined idles the hook. */\n executionId: string | null | undefined;\n /** Resume cursor — the operation view's `last_event_seq` (0 = from start). */\n lastEventSeq?: number;\n /** Gate — false tears the follow down (default true when an id is set). */\n enabled?: boolean;\n /** Stall watchdog, ms. Default 45_000. */\n stallTimeoutMs?: number;\n /** Consecutive-failure budget. Default 60. */\n reconnectLimit?: number;\n /** Delay between attempts, ms. Default 2_000. */\n reconnectDelayMs?: number;\n /** Fired per durable spine event (lifecycle transitions + notes). */\n onEvent?: (event: MatrxRuntimeOperationEvent, seq: number | null) => void;\n /** Fired once when the follow settles (server `end`, exhausted budget, or teardown). */\n onSettled?: (result: {\n ended: boolean;\n status: MatrxRuntimeExecutionStatus | null;\n }) => void;\n}\n\nexport interface UseFollowRuntimeOperation {\n /** True while a follow loop is live for the current execution. */\n following: boolean;\n /** True once the server sent the terminal `end` frame. */\n ended: boolean;\n /** The root status from the `end` frame, when ended. */\n status: MatrxRuntimeExecutionStatus | null;\n}\n\nexport function useFollowRuntimeOperation(\n options: UseFollowRuntimeOperationOptions,\n): UseFollowRuntimeOperation {\n const [following, setFollowing] = useState(false);\n const [ended, setEnded] = useState(false);\n const [status, setStatus] = useState<MatrxRuntimeExecutionStatus | null>(\n null,\n );\n\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const { executionId, enabled = true } = options;\n const active = enabled && !!executionId;\n\n const follow = useCallback(\n (id: string, signal: AbortSignal, isCurrent: () => boolean) => {\n const opts = optionsRef.current;\n const transport =\n typeof opts.transport === \"function\"\n ? opts.transport()\n : opts.transport;\n return followRuntimeOperationToEnd(transport, id, {\n signal,\n ...(opts.lastEventSeq !== undefined\n ? { lastEventSeq: opts.lastEventSeq }\n : {}),\n ...(opts.stallTimeoutMs !== undefined\n ? { stallTimeoutMs: opts.stallTimeoutMs }\n : {}),\n ...(opts.reconnectLimit !== undefined\n ? { reconnectLimit: opts.reconnectLimit }\n : {}),\n ...(opts.reconnectDelayMs !== undefined\n ? { reconnectDelayMs: opts.reconnectDelayMs }\n : {}),\n onEvent: (event, seq) => {\n if (isCurrent()) optionsRef.current.onEvent?.(event, seq);\n },\n });\n },\n [],\n );\n\n useEffect(() => {\n if (!active || !executionId) return;\n let current = true;\n const controller = new AbortController();\n setFollowing(true);\n setEnded(false);\n setStatus(null);\n\n void follow(executionId, controller.signal, () => current)\n .then((result) => {\n if (!current) return;\n setFollowing(false);\n setEnded(result.ended);\n setStatus(result.status);\n optionsRef.current.onSettled?.(result);\n })\n .catch(() => {\n if (!current) return;\n setFollowing(false);\n optionsRef.current.onSettled?.({ ended: false, status: null });\n });\n\n return () => {\n current = false;\n controller.abort();\n setFollowing(false);\n };\n }, [active, executionId, follow]);\n\n return { following, ended, status };\n}\n"],"mappings":"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","names":["isRecord","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../../react/use-agent-run.ts","../../projection/request.ts","../../matrx/transport.ts","../../matrx/client.ts","../../matrx/backend-errors.ts","../../stream/ndjson.ts","../../matrx/org-context.ts","../../matrx/call.ts","../../matrx/internal.ts","../../stream/sse.ts","../../matrx/operations.ts","../../matrx/run.ts","../../react/use-follow-runtime-operation.ts"],"sourcesContent":["/**\n * `useAgentRun` — the package's run/stream hook: start, continue, and cancel\n * an agent run over an injected `MatrxTransport`, with the streamed state\n * exposed as the parity-proven request projection\n * (`@ai-matrx/agents/projection/request` — the ONE event interpreter).\n *\n * The hook owns the whole loop the hosts used to hand-roll: the streaming\n * call, folding every NDJSON envelope through `projectAgentEvent`, terminal\n * status/error interpretation, unmount/stale-run teardown, and server-side\n * cancel by the run's `X-Request-ID` (the ONLY id the cancel route accepts).\n * The host injects only the transport — typically the production\n * `createMatrxTransport` from `@ai-matrx/agents/matrx`.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n createAgentRequestProjection,\n projectAgentEvent,\n type AgentRequestProjection,\n} from \"../projection/request\";\nimport {\n cancelAgentRun,\n continueAgentConversation,\n normalizeMatrxError,\n startAgentRun,\n type MatrxAgentStartRequest,\n type MatrxCallError,\n type MatrxCancelResponse,\n type MatrxConversationContinueRequest,\n type MatrxRunHandle,\n type MatrxStreamCallOptions,\n type MatrxTransport,\n} from \"../matrx/index\";\n\nexport type AgentRunPhase =\n | \"idle\"\n | \"starting\"\n | \"streaming\"\n | \"complete\"\n | \"error\"\n | \"cancelled\";\n\nexport interface UseAgentRunOptions {\n /** The transport (or a per-run factory — resolved fresh at each start). */\n transport: MatrxTransport | (() => MatrxTransport);\n /** Stream-kernel knobs forwarded to every run (malformed-line hooks, read-ahead). */\n streamOptions?: Omit<MatrxStreamCallOptions, \"signal\">;\n /** Fired after every folded event with the fresh projection. */\n onProjection?: (projection: AgentRequestProjection) => void;\n /** Fired when a run fails, with the classified envelope. */\n onError?: (error: MatrxCallError) => void;\n}\n\nexport interface UseAgentRun {\n /** The lifecycle phase of the CURRENT run. */\n phase: AgentRunPhase;\n /** The live projection of the current run (null before the first start). */\n projection: AgentRequestProjection | null;\n /** Server-assigned `X-Request-ID` — feeds cancel and reconnect. */\n requestId: string | null;\n /** Server-assigned `X-Conversation-ID`. */\n conversationId: string | null;\n /** The classified failure of the current run, when phase is \"error\". */\n error: MatrxCallError | null;\n /** Start an agent run (`POST /ai/agents/{agent_id}`); resolves the final projection. */\n start: (\n agentId: string,\n request: MatrxAgentStartRequest,\n ) => Promise<AgentRequestProjection>;\n /** Continue a stored conversation (`POST /ai/conversations/{id}`); resolves the final projection. */\n continueConversation: (\n conversationId: string,\n request: MatrxConversationContinueRequest,\n ) => Promise<AgentRequestProjection>;\n /**\n * Server-side cancel of the current run by its `X-Request-ID`.\n * `mode: \"interrupt\"` = stop-and-fork. Resolves null when no run id is\n * known yet. The stream stays open until the server ends it — everything\n * already streamed persists.\n */\n cancel: (\n mode?: \"cancel\" | \"interrupt\",\n ) => Promise<MatrxCancelResponse | null>;\n /** Abort the in-flight fetch/stream locally (server work continues by design). */\n abort: () => void;\n /** Clear state back to idle (aborts any in-flight run first). */\n reset: () => void;\n}\n\nexport function useAgentRun(options: UseAgentRunOptions): UseAgentRun {\n const [phase, setPhase] = useState<AgentRunPhase>(\"idle\");\n const [projection, setProjection] = useState<AgentRequestProjection | null>(\n null,\n );\n const [error, setError] = useState<MatrxCallError | null>(null);\n\n // The latest options, without re-running effects/callbacks on identity churn.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // Run token: only the newest run may write state.\n const runSeqRef = useRef(0);\n const controllerRef = useRef<AbortController | null>(null);\n const requestIdRef = useRef<string | null>(null);\n const transportRef = useRef<MatrxTransport | null>(null);\n\n const abort = useCallback(() => {\n controllerRef.current?.abort();\n }, []);\n\n // Unmount teardown — the fetch/stream must not outlive the component.\n useEffect(() => () => controllerRef.current?.abort(), []);\n\n const runStream = useCallback(\n async (\n open: (\n transport: MatrxTransport,\n callOptions: MatrxStreamCallOptions,\n ) => Promise<MatrxRunHandle>,\n seedConversationId: string | null,\n ): Promise<AgentRequestProjection> => {\n const seq = ++runSeqRef.current;\n const isCurrent = () => runSeqRef.current === seq;\n\n controllerRef.current?.abort();\n const controller = new AbortController();\n controllerRef.current = controller;\n requestIdRef.current = null;\n\n const opts = optionsRef.current;\n const transport =\n typeof opts.transport === \"function\"\n ? opts.transport()\n : opts.transport;\n transportRef.current = transport;\n\n setPhase(\"starting\");\n setError(null);\n setProjection(null);\n\n try {\n const handle = await open(transport, {\n ...opts.streamOptions,\n signal: controller.signal,\n });\n requestIdRef.current = handle.requestId;\n\n let current = createAgentRequestProjection({\n requestId: handle.requestId ?? crypto.randomUUID(),\n conversationId: handle.conversationId ?? seedConversationId,\n });\n if (isCurrent()) {\n setPhase(\"streaming\");\n setProjection(current);\n }\n\n for await (const envelope of handle.events) {\n current = projectAgentEvent(current, envelope);\n if (isCurrent()) {\n setProjection(current);\n optionsRef.current.onProjection?.(current);\n }\n }\n\n if (isCurrent()) {\n setPhase(\n current.status === \"error\"\n ? \"error\"\n : current.status === \"cancelled\"\n ? \"cancelled\"\n : \"complete\",\n );\n if (current.status === \"error\") {\n const failure: MatrxCallError = {\n type: \"unknown\",\n message:\n (typeof current.error?.user_message === \"string\" &&\n current.error.user_message) ||\n (typeof current.error?.message === \"string\" &&\n current.error.message) ||\n \"The agent run failed\",\n serverDetail: current.error,\n };\n setError(failure);\n optionsRef.current.onError?.(failure);\n }\n }\n return current;\n } catch (err) {\n const failure = normalizeMatrxError(err);\n if (isCurrent()) {\n setPhase(failure.type === \"abort_error\" ? \"cancelled\" : \"error\");\n if (failure.type !== \"abort_error\") {\n setError(failure);\n optionsRef.current.onError?.(failure);\n }\n }\n throw err;\n }\n },\n [],\n );\n\n const start = useCallback(\n (agentId: string, request: MatrxAgentStartRequest) =>\n runStream(\n (transport, callOptions) =>\n startAgentRun(transport, agentId, request, callOptions),\n request.conversation_id,\n ),\n [runStream],\n );\n\n const continueConversation = useCallback(\n (conversationId: string, request: MatrxConversationContinueRequest) =>\n runStream(\n (transport, callOptions) =>\n continueAgentConversation(\n transport,\n conversationId,\n request,\n callOptions,\n ),\n conversationId,\n ),\n [runStream],\n );\n\n const cancel = useCallback(\n async (\n mode: \"cancel\" | \"interrupt\" = \"cancel\",\n ): Promise<MatrxCancelResponse | null> => {\n const requestId = requestIdRef.current;\n const transport = transportRef.current;\n if (!requestId || !transport) return null;\n return cancelAgentRun(transport, requestId, { mode });\n },\n [],\n );\n\n const reset = useCallback(() => {\n runSeqRef.current += 1;\n controllerRef.current?.abort();\n controllerRef.current = null;\n requestIdRef.current = null;\n setPhase(\"idle\");\n setProjection(null);\n setError(null);\n }, []);\n\n return {\n phase,\n projection,\n requestId: projection?.requestId ?? null,\n conversationId: projection?.conversationId ?? null,\n error,\n start,\n continueConversation,\n cancel,\n abort,\n reset,\n };\n}\n","export type AgentProjectionStatus =\n | \"pending\"\n | \"streaming\"\n | \"awaiting-tools\"\n | \"complete\"\n | \"error\"\n | \"cancelled\";\n\nexport interface AgentProjectionOperation {\n operationId: string;\n operation: string;\n parentOperationId: string | null;\n status: \"active\" | \"success\" | \"failed\" | \"cancelled\";\n metadata: Record<string, unknown> | null;\n result: Record<string, unknown> | null;\n}\n\nexport interface AgentProjectionTool {\n callId: string;\n toolName: string;\n status:\n | \"started\"\n | \"progress\"\n | \"step\"\n | \"preview\"\n | \"completed\"\n | \"error\"\n | \"delegated\";\n message: string | null;\n data: Record<string, unknown> | null;\n}\n\nexport interface AgentProjectionRenderBlock {\n blockId: string;\n blockIndex: number;\n type: string;\n status: \"streaming\" | \"complete\" | \"error\";\n content: string | null;\n data: Record<string, unknown> | null;\n metadata: Record<string, unknown> | null;\n}\n\nexport interface AgentRequestProjection {\n requestId: string;\n conversationId: string | null;\n status: AgentProjectionStatus;\n answer: string;\n reasoning: string;\n reasoningActive: boolean;\n phase: string | null;\n phaseHistory: string[];\n operations: Record<string, AgentProjectionOperation>;\n tools: Record<string, AgentProjectionTool>;\n renderBlocks: Record<string, AgentProjectionRenderBlock>;\n renderBlockOrder: string[];\n completion: Record<string, unknown> | null;\n error: Record<string, unknown> | null;\n lastTransportSeq: number;\n transportStreamId: string | null;\n eventCount: number;\n}\n\nexport interface AgentProjectionEvent {\n event: string;\n data?: unknown;\n stream_seq?: number;\n stream_id?: string;\n}\n\nconst asRecord = (value: unknown): Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n\nconst asString = (value: unknown): string | null =>\n typeof value === \"string\" ? value : null;\n\nconst asNumber = (value: unknown): number | null =>\n typeof value === \"number\" && Number.isFinite(value) ? value : null;\n\nexport function createAgentRequestProjection(input: {\n requestId: string;\n conversationId?: string | null;\n}): AgentRequestProjection {\n return {\n requestId: input.requestId,\n conversationId: input.conversationId ?? null,\n status: \"pending\",\n answer: \"\",\n reasoning: \"\",\n reasoningActive: false,\n phase: null,\n phaseHistory: [],\n operations: {},\n tools: {},\n renderBlocks: {},\n renderBlockOrder: [],\n completion: null,\n error: null,\n lastTransportSeq: 0,\n transportStreamId: null,\n eventCount: 0,\n };\n}\n\nfunction toolStatus(event: string): AgentProjectionTool[\"status\"] {\n switch (event) {\n case \"tool_started\":\n return \"started\";\n case \"tool_step\":\n return \"step\";\n case \"tool_result_preview\":\n return \"preview\";\n case \"tool_completed\":\n return \"completed\";\n case \"tool_error\":\n return \"error\";\n case \"tool_delegated\":\n return \"delegated\";\n default:\n return \"progress\";\n }\n}\n\nexport function projectAgentEvent(\n current: AgentRequestProjection,\n event: AgentProjectionEvent,\n): AgentRequestProjection {\n const streamSeq = asNumber(event.stream_seq);\n const streamId = asString(event.stream_id);\n const sameSegment = streamId === null || streamId === current.transportStreamId;\n if (\n sameSegment &&\n streamSeq !== null &&\n streamSeq <= current.lastTransportSeq\n ) {\n return current;\n }\n\n const data = asRecord(event.data);\n const next: AgentRequestProjection = {\n ...current,\n status: current.status === \"pending\" ? \"streaming\" : current.status,\n lastTransportSeq:\n streamSeq === null\n ? current.lastTransportSeq\n : sameSegment\n ? Math.max(current.lastTransportSeq, streamSeq)\n : streamSeq,\n transportStreamId: streamId ?? current.transportStreamId,\n eventCount: current.eventCount + 1,\n };\n\n switch (event.event) {\n case \"chunk\": {\n const text = asString(data.text);\n return text === null ? next : { ...next, answer: current.answer + text };\n }\n case \"reasoning_chunk\": {\n const text = asString(data.text);\n return text === null\n ? next\n : {\n ...next,\n reasoning: current.reasoning + text,\n reasoningActive: true,\n };\n }\n case \"reasoning\":\n return {\n ...next,\n reasoningActive: data.state === \"started\",\n };\n case \"phase\": {\n const phase = asString(data.phase);\n if (phase === null) return next;\n return {\n ...next,\n phase,\n phaseHistory: [...current.phaseHistory, phase],\n };\n }\n case \"init\": {\n const operationId = asString(data.operation_id);\n const operation = asString(data.operation);\n if (operationId === null || operation === null) return next;\n return {\n ...next,\n operations: {\n ...current.operations,\n [operationId]: {\n operationId,\n operation,\n parentOperationId: asString(data.parent_operation_id),\n status: \"active\",\n metadata: Object.keys(asRecord(data.metadata)).length\n ? asRecord(data.metadata)\n : null,\n result: null,\n },\n },\n };\n }\n case \"completion\": {\n const operationId = asString(data.operation_id);\n const operation = asString(data.operation);\n const rawStatus = asString(data.status);\n const status: AgentProjectionOperation[\"status\"] =\n rawStatus === \"failed\" || rawStatus === \"cancelled\"\n ? rawStatus\n : \"success\";\n const result = asRecord(data.result);\n const operations = operationId\n ? {\n ...current.operations,\n [operationId]: {\n ...(current.operations[operationId] ?? {\n operationId,\n operation: operation ?? \"unknown\",\n parentOperationId: null,\n metadata: null,\n }),\n status,\n result,\n },\n }\n : current.operations;\n if (operation === \"user_request\") {\n return {\n ...next,\n operations,\n completion: data,\n status:\n status === \"success\"\n ? \"complete\"\n : status === \"cancelled\"\n ? \"cancelled\"\n : \"error\",\n };\n }\n return { ...next, operations };\n }\n case \"tool_event\": {\n const callId = asString(data.call_id);\n const toolName = asString(data.tool_name);\n const lifecycle = asString(data.event);\n if (callId === null || toolName === null || lifecycle === null) return next;\n const status = toolStatus(lifecycle);\n return {\n ...next,\n status:\n status === \"delegated\"\n ? \"awaiting-tools\"\n : status === \"completed\" || status === \"error\"\n ? \"streaming\"\n : next.status,\n tools: {\n ...current.tools,\n [callId]: {\n callId,\n toolName,\n status,\n message: asString(data.message),\n data: Object.keys(asRecord(data.data)).length ? asRecord(data.data) : null,\n },\n },\n };\n }\n case \"render_block\": {\n const blockId = asString(data.blockId);\n const blockIndex = asNumber(data.blockIndex);\n const type = asString(data.type);\n if (blockId === null || blockIndex === null || type === null) return next;\n const alreadyKnown = Object.hasOwn(current.renderBlocks, blockId);\n return {\n ...next,\n renderBlocks: {\n ...current.renderBlocks,\n [blockId]: {\n blockId,\n blockIndex,\n type,\n status:\n data.status === \"complete\" || data.status === \"error\"\n ? data.status\n : \"streaming\",\n content: asString(data.content),\n data: Object.keys(asRecord(data.data)).length ? asRecord(data.data) : null,\n metadata: Object.keys(asRecord(data.metadata)).length\n ? asRecord(data.metadata)\n : null,\n },\n },\n renderBlockOrder: alreadyKnown\n ? current.renderBlockOrder\n : [...current.renderBlockOrder, blockId],\n };\n }\n case \"error\":\n return { ...next, status: \"error\", error: data };\n case \"end\":\n return {\n ...next,\n status:\n current.status === \"error\" || current.status === \"cancelled\"\n ? current.status\n : \"complete\",\n reasoningActive: false,\n };\n case \"data\": {\n const conversationId =\n data.type === \"conversation_id\" ? asString(data.conversation_id) : null;\n return conversationId === null ? next : { ...next, conversationId };\n }\n default:\n return next;\n }\n}\n\nexport function projectAgentEvents(\n initial: AgentRequestProjection,\n events: readonly AgentProjectionEvent[],\n): AgentRequestProjection {\n return events.reduce(projectAgentEvent, initial);\n}\n","/**\n * `@ai-matrx/agents/matrx` — the Matrx transport port.\n *\n * The ONE seam between this package's wire semantics and a host's connection\n * policy. This package owns WHAT is said to the AI Matrx server — paths,\n * methods, JSON bodies, streaming Accept headers, the `Last-Event-ID` cursor\n * header — and the host owns HOW the connection is made:\n *\n * - base-URL / backend-channel resolution (global, sandbox override, local\n * engine, EC2-dedicated — whatever ladder the host runs);\n * - credentials (Supabase JWT `Authorization: Bearer`, guest\n * `X-Fingerprint-ID`, or the API-key lane) — NEVER implemented here;\n * - the `X-Organization-Id` context header;\n * - retry policy, network-level timeouts, and diagnostics capture.\n *\n * A host implements the port in a few lines:\n *\n * ```ts\n * const transport: MatrxTransport = {\n * fetch: (path, init) =>\n * fetch(`${baseUrl}${path}`, {\n * ...init,\n * headers: { ...init.headers, ...authHeaders() },\n * }),\n * };\n * ```\n *\n * The port is deliberately structural and `fetch`-shaped so `@ai-matrx/data`'s\n * `/api` transport can implement it without importing this package.\n */\n\n/**\n * The request this package hands the port. A strict subset of `RequestInit`,\n * so a host can spread it straight into `fetch`.\n */\nexport interface MatrxTransportRequest {\n method: \"GET\" | \"POST\";\n /**\n * Wire-semantic headers the CALL requires (`Content-Type`, `Accept`,\n * `Last-Event-ID`). The host merges its policy headers (auth, org) on top;\n * it must not drop these.\n */\n headers: Record<string, string>;\n /** Pre-serialized JSON body, present on POST calls that carry one. */\n body?: string;\n /** Caller cancellation. The host must wire it to the underlying fetch. */\n signal?: AbortSignal;\n}\n\n/**\n * The transport port. `path` is server-relative and always starts with `/`\n * (`/ai/...`, `/runtime/...`); the host prepends its resolved base URL.\n */\nexport interface MatrxTransport {\n fetch(path: string, init: MatrxTransportRequest): Promise<Response>;\n}\n\n/**\n * A non-2xx response from the Matrx API, with the server's structured error\n * body preserved and its richest human-readable message extracted.\n */\nexport class MatrxApiError extends Error {\n override readonly name = \"MatrxApiError\";\n /** HTTP status of the failed response. */\n readonly status: number;\n /** Machine code from the server body (`code`, or `detail.code`), when present. */\n readonly code: string | null;\n /** The parsed server error body, verbatim (undefined when unparsable). */\n readonly serverDetail: unknown;\n /** The request path the failure came from (server-relative). */\n readonly path: string;\n\n constructor(args: {\n status: number;\n path: string;\n serverDetail?: unknown;\n message?: string;\n }) {\n super(\n args.message ??\n extractMatrxErrorMessage(args.serverDetail) ??\n `HTTP ${args.status}`,\n );\n this.status = args.status;\n this.path = args.path;\n this.serverDetail = args.serverDetail;\n this.code = extractMatrxErrorCode(args.serverDetail);\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction nonBlankString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\n/**\n * Extract the richest human-readable message from a Matrx/FastAPI error body.\n *\n * aidream 4xx validation errors look like\n * `{ error, user_message, details: [{ field, message, help }] }`; hand-raised\n * HTTPExceptions carry `{ detail: { code, message } }`; FastAPI's defaults are\n * `{ detail: string | [{ msg }] }`. Preference order: `user_message` →\n * `message` → joined `details[].message` → `detail.message` →\n * `detail` string → joined `detail[].msg`. Returns undefined for\n * unrecognized bodies so callers fall back to the bare status line.\n */\nexport function extractMatrxErrorMessage(\n serverDetail: unknown,\n): string | undefined {\n if (!isRecord(serverDetail)) return undefined;\n\n const userMessage = nonBlankString(serverDetail.user_message);\n if (userMessage) return userMessage;\n const message = nonBlankString(serverDetail.message);\n if (message) return message;\n\n if (Array.isArray(serverDetail.details)) {\n const messages = serverDetail.details\n .map((entry: unknown) => {\n if (!isRecord(entry)) return undefined;\n const detailMessage = nonBlankString(entry.message);\n if (!detailMessage) return undefined;\n const field = nonBlankString(entry.field);\n return field ? `${field}: ${detailMessage}` : detailMessage;\n })\n .filter((m): m is string => typeof m === \"string\");\n if (messages.length > 0) return messages.join(\"; \");\n }\n\n const detail = serverDetail.detail;\n if (isRecord(detail)) {\n const detailMessage =\n nonBlankString(detail.message) ?? nonBlankString(detail.user_message);\n if (detailMessage) return detailMessage;\n }\n if (typeof detail === \"string\" && detail.trim()) return detail;\n if (Array.isArray(detail)) {\n const messages = detail\n .map((entry: unknown) =>\n isRecord(entry) ? nonBlankString(entry.msg) : undefined,\n )\n .filter((m): m is string => typeof m === \"string\");\n if (messages.length > 0) return messages.join(\"; \");\n }\n return undefined;\n}\n\n/**\n * Extract the machine error code from a Matrx error body: top-level `code`,\n * else `detail.code` (the hand-raised HTTPException shape). Null when absent.\n */\nexport function extractMatrxErrorCode(serverDetail: unknown): string | null {\n if (!isRecord(serverDetail)) return null;\n const topLevel = nonBlankString(serverDetail.code);\n if (topLevel) return topLevel;\n const detail = serverDetail.detail;\n if (isRecord(detail)) {\n const nested = nonBlankString(detail.code);\n if (nested) return nested;\n }\n return null;\n}\n","/**\n * The PRODUCTION `MatrxTransport` — the full connection pipeline every Matrx\n * host used to hand-roll, moved in under C22 (THE HARD PARTS LIVE IN THE\n * PACKAGE). A host constructs it from identity alone:\n *\n * ```ts\n * const transport = createMatrxTransport({\n * baseUrl: \"https://server.app.matrxserver.com\",\n * credentials, // CredentialsPort (@ai-matrx/data)\n * organizationId: () => activeOrgId, // omit for conversation-lane calls\n * });\n * ```\n *\n * Everything else is defaulted to the proven production values:\n *\n * - **timeouts** — connect 15s (for a non-streaming FastAPI handler this is\n * effectively time-to-response), total UNCAPPED (the port cannot tell a JSON\n * call from a long-lived NDJSON/SSE stream; the connect timeout is the JSON\n * guard), via `@ai-matrx/data/net`'s `resilientFetch`;\n * - **protocol** — the AI API version transform (`applyAiApiVersion`, default\n * v2) + the loud v2 → v1 transport fallback (`./protocol`);\n * - **credentials** — read fresh from the injected `CredentialsPort` on EVERY\n * call, so a token refresh mid-run is picked up; mapped to headers by the\n * ONE `credentialToHeaders`;\n * - **org context** — the fail-closed `X-Organization-Id` binding\n * (`./org-context`): configured lanes REQUIRE an org and refuse before the\n * wire; unconfigured lanes (conversation-scoped calls, which carry org in\n * the body) send none;\n * - **error classification** — every failure normalizes through\n * `normalizeMatrxError` into the ONE `MatrxCallError` envelope;\n * - **diagnostics** — a typed sink the host wires to its capture/telemetry\n * (`onRequest`, `onError`, `onProtocolDowngrade`); wiring it is optional,\n * the transport works silently without it.\n *\n * Header merge order is part of the port contract: wire headers first\n * (`Content-Type` / `Accept` / `Last-Event-ID` — the package's), then\n * credentials, then resolver policy headers, then the org header. Policy\n * headers never carry `Content-Type` — the wire owns it, and a policy\n * `Content-Type` merged over a GET SSE call would corrupt the wire, so the\n * factory strips it defensively.\n */\n\nimport { isNetError, type CredentialsPort } from \"@ai-matrx/data\";\nimport { credentialToHeaders } from \"@ai-matrx/data\";\nimport { extractErrorMessage } from \"@ai-matrx/data/net\";\nimport { BackendApiError } from \"./backend-errors\";\nimport {\n bareStatusSentence,\n honestTransportMessage,\n isBareTransportCode,\n} from \"./call\";\nimport {\n applyOrganizationContextHeader,\n OrganizationContextError,\n requireOrganizationContext,\n} from \"./org-context\";\nimport {\n applyAiApiVersion,\n fetchWithMatrxProtocolFallback,\n MATRX_AI_API_VERSION_DEFAULT,\n type MatrxAiApiVersion,\n type MatrxProtocolDowngrade,\n} from \"./protocol\";\nimport {\n extractMatrxErrorCode,\n extractMatrxErrorMessage,\n MatrxApiError,\n type MatrxTransport,\n type MatrxTransportRequest,\n} from \"./transport\";\n\n// ─── The normalized error envelope ──────────────────────────────────────────\n\n/**\n * The ONE classified error shape for Matrx client calls — the envelope hosts\n * branch on instead of string-matching exceptions. Structurally compatible\n * with matrx-frontend's `ApiCallError` by design.\n */\nexport interface MatrxCallError {\n type:\n | \"auth_error\"\n | \"network_error\"\n | \"http_error\"\n | \"validation_error\"\n | \"abort_error\"\n | \"unknown\";\n message: string;\n /** HTTP status code, if applicable. */\n status?: number;\n /** Raw error detail from the server (the parsed error body). */\n serverDetail?: unknown;\n /** Machine code preserved through normalization (server `code`, org-context code, …). */\n code?: string;\n /** Original exception identity for diagnostics. */\n name?: string;\n /** Original exception stack. */\n stack?: string;\n /** JSON-safe dump of the original thrown value. */\n raw?: unknown;\n}\n\nfunction classifyStatus(status: number): \"validation_error\" | \"http_error\" {\n return status >= 400 && status < 500 ? \"validation_error\" : \"http_error\";\n}\n\n/**\n * Classify any failure thrown by a Matrx client call — `MatrxApiError`, a\n * typed `BackendApiError` / `StreamTransportError`, a `NetError` from the\n * resilience layer, an org-context refusal, an abort — into the\n * `MatrxCallError` envelope. Never throws.\n *\n * Every message is laundered through `honestTransportMessage`: a bare status\n * line (\"HTTP 400\") never reaches a person — the status rides `status`.\n * A `BackendApiError` keeps its machine `code` (a dropped socket stays\n * `stream_transport_lost`, so a surface reattaches instead of dead-ending)\n * and its structured `details` ride `serverDetail` (the organization-hold\n * body carries the caller's membership choices there).\n */\nexport function normalizeMatrxError(err: unknown): MatrxCallError {\n if (err instanceof OrganizationContextError) {\n return {\n type: \"validation_error\",\n message: err.message,\n code: err.code,\n name: err.name,\n ...(err.stack !== undefined ? { stack: err.stack } : {}),\n };\n }\n\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return { type: \"abort_error\", message: \"Request was cancelled.\" };\n }\n\n if (err instanceof MatrxApiError) {\n return {\n type: classifyStatus(err.status),\n message: honestTransportMessage(err.message, err.status),\n status: err.status,\n serverDetail: err.serverDetail,\n ...(err.code ? { code: err.code } : {}),\n name: err.name,\n };\n }\n\n if (err instanceof BackendApiError) {\n return {\n type: \"network_error\",\n message: honestTransportMessage(\n err.detail || err.userMessage,\n err.status ?? undefined,\n ),\n code: err.code,\n name: err.name,\n ...(err.status !== null ? { status: err.status } : {}),\n ...(err.details !== null ? { serverDetail: err.details } : {}),\n };\n }\n\n if (isNetError(err)) {\n if (err.code === \"aborted\") {\n return { type: \"abort_error\", message: err.message };\n }\n if (err.code === \"http\") {\n const status = err.status ?? 0;\n return {\n type: classifyStatus(status),\n message: honestTransportMessage(err.message, status),\n status,\n };\n }\n // connect-timeout / total-timeout / heartbeat-timeout / network / offline\n return { type: \"network_error\", message: err.message };\n }\n\n if (err instanceof Error) {\n const httpMatch = err.message.match(/HTTP (\\d+):\\s*(.*)/);\n if (httpMatch) {\n const status = parseInt(httpMatch[1] as string, 10);\n return {\n type: classifyStatus(status),\n message: honestTransportMessage(httpMatch[2] ?? err.message, status),\n status,\n };\n }\n // \"HTTP 400\" with no colon: the status line itself, never the sentence.\n if (isBareTransportCode(err.message)) {\n const status = Number.parseInt(err.message.replace(/\\D+/g, \"\"), 10);\n return {\n type: classifyStatus(status),\n message: bareStatusSentence(status),\n status,\n };\n }\n return { type: \"network_error\", message: err.message };\n }\n\n return { type: \"unknown\", message: extractErrorMessage(err) };\n}\n\n// ─── The factory ────────────────────────────────────────────────────────────\n\n/**\n * A fully-resolved connection target for one call: where to send it and which\n * policy headers ride ON TOP of the wire + credential headers.\n */\nexport interface MatrxTransportTarget {\n /** Fully-qualified base URL, no trailing slash. */\n baseUrl: string;\n /**\n * Extra policy headers for this target (e.g. a resolver that carries its own\n * credential headers). `Content-Type` is stripped — the wire owns it.\n */\n policyHeaders?: Record<string, string>;\n /** Routing channel label surfaced to `onRequest` telemetry. */\n channel?: string;\n}\n\n/** Context handed to every diagnostics callback. */\nexport interface MatrxRequestInfo {\n /** The final URL (base + version-transformed path). */\n url: string;\n method: \"GET\" | \"POST\";\n /** The server-relative path as the package requested it. */\n path: string;\n /** The resolved routing channel (default \"default\"). */\n channel: string;\n /** The transport's call-site label (default \"matrxTransport\"). */\n source: string;\n}\n\n/**\n * The typed diagnostics sink — the host wires these to its telemetry/capture\n * systems; all optional, and the transport is fully functional without them.\n */\nexport interface MatrxTransportDiagnostics {\n /** Fired at the last pre-fetch moment of every call. */\n onRequest?: (info: MatrxRequestInfo) => void;\n /**\n * Fired for every thrown network failure and every non-2xx response not\n * listed in `expectedErrorStatuses`, with the classified envelope.\n */\n onError?: (error: MatrxCallError, info: MatrxRequestInfo) => void;\n /** Fired on every v2 → v1 protocol downgrade. */\n onProtocolDowngrade?: (downgrade: MatrxProtocolDowngrade) => void;\n}\n\nexport interface CreateMatrxTransportOptions {\n /** Fixed base URL, no trailing slash. Exactly one of `baseUrl` / `resolveTarget`. */\n baseUrl?: string;\n /**\n * Per-call target resolver — for hosts whose base URL / policy headers are\n * dynamic (server toggles, sandbox overrides, conversation channels).\n * Resolution runs fresh on EVERY call. May throw to refuse a call loudly.\n */\n resolveTarget?: () => MatrxTransportTarget;\n /**\n * The credential source (`@ai-matrx/data`), read fresh per call so token\n * refreshes are picked up mid-run. Omit only when the resolver's\n * `policyHeaders` already carry the credentials.\n */\n credentials?: CredentialsPort;\n /**\n * The org-context binding. When configured, every call REQUIRES a valid org\n * (fail-closed `organization_context_required` before the wire) and carries\n * `X-Organization-Id`. Omit for conversation-lane transports, which send the\n * org in the body only.\n */\n organizationId?: string | (() => string | null | undefined);\n /** AI API protocol version (value or per-call getter). Default `\"v2\"`. */\n aiApiVersion?: MatrxAiApiVersion | (() => MatrxAiApiVersion);\n /** Max ms request start → response headers. Default 15_000. */\n connectTimeoutMs?: number;\n /**\n * Max ms for the whole handshake. Default `null` (uncapped) — the port\n * cannot tell a JSON call from a long-lived stream, and capping would kill\n * long streams; the connect timeout is the real JSON guard.\n */\n totalTimeoutMs?: number | null;\n /**\n * HTTP failures this transport's call class fully handles as expected\n * domain outcomes (e.g. 404 on runtime-operation identify). They still\n * reach the package client (which maps them to typed results) but are not\n * reported through `onError`.\n */\n expectedErrorStatuses?: readonly number[];\n /** Short call-site label surfaced on `MatrxRequestInfo` (default \"matrxTransport\"). */\n source?: string;\n diagnostics?: MatrxTransportDiagnostics;\n}\n\nconst PRODUCTION_CONNECT_TIMEOUT_MS = 15_000;\n\nfunction stripContentType(\n headers: Record<string, string>,\n): Record<string, string> {\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([name]) => name.toLowerCase() !== \"content-type\",\n ),\n );\n}\n\n/**\n * Build the production `MatrxTransport`. See the module doc for the pipeline;\n * every knob defaults to the proven production value.\n */\nexport function createMatrxTransport(\n options: CreateMatrxTransportOptions,\n): MatrxTransport {\n const {\n baseUrl,\n resolveTarget,\n credentials,\n organizationId,\n aiApiVersion,\n connectTimeoutMs = PRODUCTION_CONNECT_TIMEOUT_MS,\n totalTimeoutMs = null,\n expectedErrorStatuses,\n diagnostics,\n source = \"matrxTransport\",\n } = options;\n\n if ((baseUrl === undefined) === (resolveTarget === undefined)) {\n throw new Error(\n \"createMatrxTransport: provide exactly one of `baseUrl` or `resolveTarget`.\",\n );\n }\n\n const resolve: () => MatrxTransportTarget =\n resolveTarget ?? (() => ({ baseUrl: baseUrl as string }));\n\n const resolveVersion = (): MatrxAiApiVersion => {\n if (aiApiVersion === undefined) return MATRX_AI_API_VERSION_DEFAULT;\n return typeof aiApiVersion === \"function\" ? aiApiVersion() : aiApiVersion;\n };\n\n const resolveOrganizationId = (): string | null => {\n if (organizationId === undefined) return null;\n const candidate =\n typeof organizationId === \"function\" ? organizationId() : organizationId;\n // Fail closed: a configured org lane with no org refuses before the wire.\n return requireOrganizationContext(candidate);\n };\n\n return {\n async fetch(path: string, init: MatrxTransportRequest): Promise<Response> {\n const target = resolve();\n\n // `path` is already interpolated (`/ai/agents/<uuid>`), so the version\n // transform is the concrete-path bridge, not a template registry.\n const versionedPath = applyAiApiVersion(path, resolveVersion());\n const url = `${target.baseUrl}${versionedPath}`;\n\n // Wire headers FIRST; credentials, then resolver policy headers, then\n // the org header on top — none of them may carry Content-Type.\n const credentialHeaders = credentials\n ? credentialToHeaders(await credentials.get())\n : {};\n let headers: Record<string, string> = {\n ...init.headers,\n ...stripContentType(credentialHeaders),\n ...stripContentType(target.policyHeaders ?? {}),\n };\n const orgId = resolveOrganizationId();\n if (orgId !== null) {\n headers = applyOrganizationContextHeader(headers, orgId);\n }\n\n const info: MatrxRequestInfo = {\n url,\n method: init.method,\n path,\n channel: target.channel ?? \"default\",\n source,\n };\n diagnostics?.onRequest?.(info);\n\n let response: Response;\n try {\n ({ response } = await fetchWithMatrxProtocolFallback(\n url,\n {\n method: init.method,\n headers,\n ...(init.body !== undefined ? { body: init.body } : {}),\n },\n {\n ...(init.signal ? { signal: init.signal } : {}),\n connectTimeoutMs,\n totalTimeoutMs,\n throwOnHttpError: false,\n ...(diagnostics?.onProtocolDowngrade\n ? { onDowngrade: diagnostics.onProtocolDowngrade }\n : {}),\n },\n ));\n } catch (err) {\n diagnostics?.onError?.(normalizeMatrxError(err), info);\n throw err;\n }\n\n if (!response.ok && !expectedErrorStatuses?.includes(response.status)) {\n // The package client consumes the original body for its\n // MatrxApiError; read the diagnostics copy from a clone so the two\n // never fight.\n const serverDetail: unknown = await response\n .clone()\n .json()\n .catch(() => undefined);\n const code = extractMatrxErrorCode(serverDetail);\n diagnostics?.onError?.(\n {\n type: classifyStatus(response.status),\n message:\n extractMatrxErrorMessage(serverDetail) ??\n `HTTP ${response.status}`,\n status: response.status,\n serverDetail,\n ...(code ? { code } : {}),\n },\n info,\n );\n }\n\n return response;\n },\n };\n}\n","/**\n * `@ai-matrx/agents/matrx` — the AI Matrx server's error model, as every\n * client reads it: `BackendApiError` (the server's `APIError` body),\n * `StreamTransportError` (the socket died mid-run; the run may still finish\n * and is reattachable), parsing of HTTP / stream / persisted errors, and the\n * ONE sentence a person sees (`getUserMessage`, `describeBackendFailure`).\n *\n * Moved from matrx-frontend `lib/api/errors.ts` (chat-package independence\n * P9); the app re-exports it from here. Pure: no host, no window, no env.\n */\n\n/**\n * Standardized error shape returned by all backend endpoints.\n * Matches the Python `APIError` Pydantic model.\n */\nexport interface BackendApiErrorData {\n /** Machine-readable error code (e.g. \"auth_required\", \"validation_error\") */\n error: string;\n /** Developer-facing detail for debugging */\n message: string;\n /** Safe to display directly in the UI */\n user_message: string;\n /** Extra info (validation errors, etc.) */\n details: unknown | null;\n /** Unique request ID for support/debugging */\n request_id: string;\n}\n\n/** Common backend error codes */\nexport type BackendErrorCode =\n | \"auth_required\"\n | \"token_required\"\n | \"admin_required\"\n | \"validation_error\"\n | \"not_found\"\n | \"internal_error\"\n | \"agent_error\"\n /** The stream socket died mid-run; the server run may still be completing\n * and is reattachable. See `StreamTransportError`. */\n | \"stream_transport_lost\"\n | (string & {});\n\n// ============================================================================\n// ERROR CLASS\n// ============================================================================\n\n/**\n * Typed error thrown by all backend API operations.\n * Contains structured fields matching the Python APIError model.\n *\n * Usage:\n * ```typescript\n * try {\n * await client.post(ENDPOINTS.ai.agentStart(agentId), body);\n * } catch (err) {\n * if (err instanceof BackendApiError) {\n * // Show err.userMessage to the user\n * // Log err.requestId for debugging\n * // Check err.code for programmatic handling\n * }\n * }\n * ```\n */\nexport class BackendApiError extends Error {\n /** Machine-readable error code */\n readonly code: BackendErrorCode;\n /** Developer-facing detail */\n readonly detail: string;\n /** Safe to display directly in the UI */\n readonly userMessage: string;\n /** Extra info (validation errors, etc.) */\n readonly details: unknown | null;\n /** Unique request ID for support/debugging */\n readonly requestId: string;\n /** HTTP status code (if from an HTTP response) */\n readonly status: number | null;\n\n constructor(data: {\n code: BackendErrorCode;\n detail: string;\n userMessage: string;\n details?: unknown | null;\n requestId?: string | undefined;\n status?: number | null | undefined;\n }) {\n super(data.userMessage);\n this.name = \"BackendApiError\";\n this.code = data.code;\n this.detail = data.detail;\n this.userMessage = data.userMessage;\n this.details = data.details ?? null;\n // MATRX-EXCEPTION: requestId is genuinely optional (constructor param);\n // \"\" means \"no request id available\" — a display/log field, not persisted.\n this.requestId = data.requestId ?? \"\";\n this.status = data.status ?? null;\n }\n\n /** Convert to the wire format for logging */\n toJSON(): BackendApiErrorData {\n return {\n error: this.code,\n message: this.detail,\n user_message: this.userMessage,\n details: this.details,\n request_id: this.requestId,\n };\n }\n}\n\n/**\n * The socket carrying a live NDJSON stream broke mid-run.\n *\n * THE DISTINCTION THIS EXISTS TO MAKE: a backend that blows up mid-stream does\n * NOT break the socket — it emits a typed `error` event and closes the body\n * cleanly. So an exception escaping the body reader means the *transport* died,\n * not the run. And aidream streams run `detach_on_disconnect=True`: the server\n * keeps executing and persisting the turn after our connection goes away.\n *\n * The client therefore cannot decide locally whether the answer is lost — it\n * must ASK THE SERVER. That is what `resumable` means here: \"reattach by\n * requestId / conversationId / durable run id and let server truth settle it\",\n * never \"this succeeded\". A server that genuinely died reports `failed` on\n * reattach and the honest record replaces the optimistic copy.\n *\n * Consumers: `run-ai-stream.ts` (chat → `reconnectServerOperation`) and\n * `adopt-foreign-stream.ts` (pipeline runs → the surface's own rejoin).\n */\nexport class StreamTransportError extends BackendApiError {\n /** Always true — reattach and let the server settle the outcome. */\n readonly resumable = true as const;\n\n constructor(data: {\n detail: string;\n details?: unknown | null;\n requestId?: string;\n }) {\n super({\n code: \"stream_transport_lost\",\n detail: data.detail,\n userMessage:\n \"The connection dropped. Your run is still going on the server — reconnecting to it now.\",\n details: data.details ?? null,\n ...(data.requestId !== undefined ? { requestId: data.requestId } : {}),\n });\n this.name = \"StreamTransportError\";\n }\n}\n\n/**\n * True when a failure is a dropped transport rather than a failed run. Use this\n * instead of `instanceof` at boundaries that re-wrap errors (thunk rejections,\n * `callApi` result errors), where the class identity is lost but the code\n * survives.\n */\nexport function isStreamTransportLost(error: unknown): boolean {\n if (error instanceof StreamTransportError) return true;\n if (error instanceof BackendApiError) {\n return error.code === \"stream_transport_lost\";\n }\n if (error && typeof error === \"object\") {\n const code = (error as { code?: unknown }).code;\n if (code === \"stream_transport_lost\") return true;\n const errorType = (error as { error_type?: unknown }).error_type;\n if (errorType === \"transport_lost\") return true;\n }\n return false;\n}\n\n// ============================================================================\n// HTTP ERROR PARSER\n// ============================================================================\n\n/**\n * Parse a non-OK HTTP response into a BackendApiError.\n *\n * Handles the standardized backend shape and falls back gracefully\n * when the response isn't JSON or uses a legacy format.\n */\nexport async function parseHttpError(\n response: Response,\n): Promise<BackendApiError> {\n const status = response.status;\n let body: Record<string, unknown> | null = null;\n\n try {\n body = await response.json();\n } catch {\n // Not JSON — try plain text\n try {\n const text = await response.text();\n return new BackendApiError({\n code: statusToCode(status),\n detail: text || `HTTP ${status}`,\n userMessage: text || `Request failed (${status})`,\n status,\n });\n } catch {\n return new BackendApiError({\n code: statusToCode(status),\n detail: `HTTP ${status}`,\n userMessage: `Request failed (${status})`,\n status,\n });\n }\n }\n\n return parseHttpErrorBody(body, status);\n}\n\n/**\n * Parse an already-decoded JSON error body into a BackendApiError.\n *\n * Exported because XHR callers (upload/download progress paths) have the\n * parsed body in hand and MUST NOT hand-roll a shallower read: a private\n * copy in `python-client.ts` looked only at top-level `error`/`message`, so\n * FastAPI's `{\"detail\": {...}}` envelope — what every matrx-files 500 uses —\n * degraded to the useless `code: \"internal\", detail: \"HTTP 500\"`. That is\n * exactly how an upload failure with a real server-side cause reached the\n * user as `Upload failed (500)` and nothing else. One parser, every transport.\n */\nexport function parseHttpErrorBody(\n body: Record<string, unknown> | null,\n status: number,\n): BackendApiError {\n if (!body) {\n return new BackendApiError({\n code: statusToCode(status),\n detail: `HTTP ${status}`,\n userMessage: `Request failed (${status})`,\n status,\n });\n }\n // Standard backend shape: { error, message, user_message, details, request_id }\n if (typeof body.error === \"string\" && typeof body.user_message === \"string\") {\n return new BackendApiError({\n code: body.error as BackendErrorCode,\n detail: (body.message as string) || `HTTP ${status}`,\n userMessage: body.user_message as string,\n details: body.details ?? null,\n requestId:\n typeof body.request_id === \"string\" ? body.request_id : undefined,\n status,\n });\n }\n\n // Legacy: nested error object with user_visible_message\n if (typeof body.error === \"object\" && body.error !== null) {\n const errorObj = body.error as Record<string, unknown>;\n return new BackendApiError({\n code:\n (errorObj.type as string) ||\n (errorObj.error as string) ||\n statusToCode(status),\n detail: (errorObj.message as string) || `HTTP ${status}`,\n userMessage:\n (errorObj.user_message as string) ||\n (errorObj.user_visible_message as string) ||\n (errorObj.message as string) ||\n `Request failed (${status})`,\n details: errorObj.details ?? null,\n requestId:\n typeof errorObj.request_id === \"string\"\n ? errorObj.request_id\n : undefined,\n status,\n });\n }\n\n // FastAPI 422 validation shape: { detail: [{ loc, msg, type }, ...] }\n if (Array.isArray(body.detail)) {\n const first = body.detail[0] as Record<string, unknown> | undefined;\n const firstMsg = first && typeof first.msg === \"string\" ? first.msg : null;\n return new BackendApiError({\n code: statusToCode(status),\n detail: firstMsg\n ? `Validation error: ${firstMsg}`\n : JSON.stringify(body.detail),\n userMessage: firstMsg || `Request failed (${status})`,\n details: body.detail,\n status,\n });\n }\n\n // FastAPI HTTPException with structured detail: { detail: { code?, error?, message?, user_message?, ... } }\n if (\n typeof body.detail === \"object\" &&\n body.detail !== null &&\n !Array.isArray(body.detail)\n ) {\n const d = body.detail as Record<string, unknown>;\n const code =\n (typeof d.code === \"string\" && d.code) ||\n (typeof d.error === \"string\" && d.error) ||\n statusToCode(status);\n const message =\n (typeof d.message === \"string\" && d.message) ||\n (typeof d.detail === \"string\" && d.detail) ||\n `HTTP ${status}`;\n return new BackendApiError({\n code: code as BackendErrorCode,\n detail: message,\n userMessage:\n (typeof d.user_message === \"string\" && d.user_message) ||\n (typeof d.user_visible_message === \"string\" &&\n d.user_visible_message) ||\n message,\n details: d.details ?? d,\n requestId: typeof d.request_id === \"string\" ? d.request_id : undefined,\n status,\n });\n }\n\n // Legacy: flat { error: string, message: string } or { detail: string }\n return new BackendApiError({\n code: typeof body.error === \"string\" ? body.error : statusToCode(status),\n detail:\n (body.message as string) ||\n (body.detail as string) ||\n (body.error as string) ||\n `HTTP ${status}`,\n userMessage:\n (body.user_message as string) ||\n (body.user_visible_message as string) ||\n (body.message as string) ||\n (body.detail as string) ||\n `Request failed (${status})`,\n details: body.details ?? null,\n requestId:\n typeof body.request_id === \"string\" ? body.request_id : undefined,\n status,\n });\n}\n\n/**\n * Adapt callApi's result-style error into the same canonical error used by\n * direct fetch and streaming consumers.\n *\n * callApi intentionally returns errors instead of throwing them, but its\n * `serverDetail` contains the complete FastAPI body. Sending only\n * `error.message` to a feature discards that body and turns a precise\n * configuration failure into \"HTTP 422\". This adapter keeps one parser and\n * one human-facing explanation path across both client styles.\n */\nexport function parseCallApiError(error: {\n message: string;\n status?: number;\n serverDetail?: unknown;\n}): BackendApiError {\n const status = error.status ?? 500;\n const body =\n error.serverDetail &&\n typeof error.serverDetail === \"object\" &&\n !Array.isArray(error.serverDetail)\n ? (error.serverDetail as Record<string, unknown>)\n : { message: error.message };\n return parseHttpErrorBody(body, status);\n}\n\n// ============================================================================\n// STREAMING ERROR PARSER\n// ============================================================================\n\n/**\n * Parse streaming error event data into a BackendApiError.\n *\n * Handles both new format (`user_message`) and legacy (`user_visible_message`).\n */\nexport function parseStreamError(data: unknown): BackendApiError {\n if (!data || typeof data !== \"object\") {\n return new BackendApiError({\n code: \"internal_error\",\n detail: typeof data === \"string\" ? data : \"Unknown streaming error\",\n userMessage: typeof data === \"string\" ? data : \"Something went wrong\",\n });\n }\n\n const obj = data as Record<string, unknown>;\n const details =\n typeof obj.details === \"object\" && obj.details !== null\n ? (obj.details as Record<string, unknown>)\n : null;\n return new BackendApiError({\n code:\n (obj.code as string) ||\n (obj.error_type as string) ||\n (obj.error as string) ||\n \"internal_error\",\n detail: (obj.message as string) || \"Streaming error\",\n userMessage:\n (obj.user_message as string) ||\n (obj.message as string) ||\n \"Something went wrong\",\n details,\n requestId:\n typeof obj.request_id === \"string\"\n ? obj.request_id\n : typeof details?.request_id === \"string\"\n ? details.request_id\n : undefined,\n });\n}\n\n/**\n * Restore the canonical error shape from a durable backend run row.\n *\n * Provider ledgers often keep an aggregate summary plus a more specific first\n * child failure. Prefer that child so a page refresh does not turn a precise\n * streamed failure back into \"1 request failed\".\n */\nexport function parsePersistedBackendError(\n data: unknown,\n requestId = \"\",\n): BackendApiError | null {\n if (!data || typeof data !== \"object\") return null;\n const error = data as Record<string, unknown>;\n const failures = Array.isArray(error.failures) ? error.failures : [];\n const firstSpecific = failures.find(\n (failure): failure is Record<string, unknown> =>\n typeof failure === \"object\" &&\n failure !== null &&\n typeof (failure as Record<string, unknown>).message === \"string\",\n );\n const summary =\n typeof error.message === \"string\"\n ? error.message\n : \"Backend operation failed\";\n const specific =\n typeof firstSpecific?.message === \"string\"\n ? firstSpecific.message\n : summary;\n const detail = specific === summary ? summary : `${summary}: ${specific}`;\n const code = typeof error.type === \"string\" ? error.type : \"internal_error\";\n // A persisted failure may carry a human-facing line the technical detail\n // cannot express — the load-bearing case being a paid result that survived\n // its persistence failure (`WritePreservedError`, D183): \"it broke\" is true\n // and \"your work is preserved and will be recovered\" is what the user needs.\n // `describeBackendFailure` still overrides a TEMPLATED one with the cause.\n const userMessage =\n typeof error.user_message === \"string\" && error.user_message\n ? error.user_message\n : detail;\n return new BackendApiError({\n code,\n detail,\n userMessage,\n details: error,\n requestId,\n });\n}\n\n// ============================================================================\n// FAILURE EXPLANATION — never let a templated non-answer be the whole story\n// ============================================================================\n\n/**\n * Server messages that carry ZERO diagnostic value. The streaming layer\n * (`matrx-connect/streaming/response.py`) emits the first one for every\n * unclassified crash — \"CanonicalGscSync failed unexpectedly. Please try\n * again or adjust your settings.\" — while the REAL cause travels in the same\n * payload's `message`. Treating those as the answer is what makes failures\n * feel secretive.\n */\nconst GENERIC_MESSAGE_PATTERNS: readonly RegExp[] = [\n /failed unexpectedly/i,\n /^\\s*something went wrong/i,\n /please try again(\\s+later)?\\.?\\s*$/i,\n /^\\s*request failed\\b/i,\n /^\\s*unknown (streaming )?error/i,\n /^\\s*internal server error\\.?\\s*$/i,\n];\n\n/** True when a message tells the reader nothing about what actually broke. */\nexport function isGenericUserMessage(\n message: string | null | undefined,\n): boolean {\n const value = (message ?? \"\").trim();\n if (!value) return true;\n return GENERIC_MESSAGE_PATTERNS.some((pattern) => pattern.test(value));\n}\n\nexport interface UpstreamErrorPayload {\n message: string;\n code: string | null;\n userMessage: string | null;\n requestId: string | null;\n status: number | null;\n}\n\n/**\n * Recover an upstream service's structured error that a downstream service\n * stringified into its own message.\n *\n * Real example (scraper wrapping aidream):\n * `aidream could not resolve GSC credential 7223…: HTTP 409 {\"error\":\"conflict\",\n * \"message\":\"Google connection 7223… has no vault credential — it needs\n * re-authentication\",\"user_message\":\"Something went wrong…\",\"request_id\":\"9002…\"}`\n *\n * Without this, the only actionable sentence on the whole hop is invisible.\n */\nexport function unwrapUpstreamError(\n message: string,\n): UpstreamErrorPayload | null {\n const start = message.indexOf(\"{\");\n const end = message.lastIndexOf(\"}\");\n if (start < 0 || end <= start) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(message.slice(start, end + 1));\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const body = parsed as Record<string, unknown>;\n const inner =\n (typeof body.message === \"string\" && body.message) ||\n (typeof body.detail === \"string\" && body.detail) ||\n \"\";\n if (!inner) return null;\n const statusMatch = /\\bHTTP (\\d{3})\\b/.exec(message.slice(0, start));\n return {\n message: inner,\n code:\n (typeof body.error_type === \"string\" && body.error_type) ||\n (typeof body.error === \"string\" && body.error) ||\n null,\n userMessage:\n typeof body.user_message === \"string\" ? body.user_message : null,\n requestId: typeof body.request_id === \"string\" ? body.request_id : null,\n status: statusMatch ? Number(statusMatch[1]) : null,\n };\n}\n\nexport interface BackendFailureExplanation {\n /** Machine code from the deepest layer that classified the failure. */\n code: string;\n /** The most specific human-readable cause available — never a template. */\n cause: string;\n /** What to headline in the UI: the cause when the server was generic. */\n headline: string;\n /** True when every user-facing message the server sent was a template. */\n headlineWasGeneric: boolean;\n /** Message chain, outermost (closest service) first. */\n chain: string[];\n /** Deepest request id available, for cross-service log correlation. */\n requestId: string;\n status: number | null;\n}\n\n/**\n * THE anti-secrecy primitive: turn any thrown backend/stream failure into the\n * most specific explanation the payload can support — unwrapping every nested\n * upstream error and refusing to let a templated `user_message` be the answer.\n *\n * Every surface that reports a backend failure to a human should headline\n * `explanation.headline` and always keep `cause` + `requestId` reachable.\n */\nexport function describeBackendFailure(\n error: unknown,\n): BackendFailureExplanation {\n const chain: string[] = [];\n let code = \"internal_error\";\n let requestId = \"\";\n let status: number | null = null;\n let userFacing: string | null = null;\n\n if (error instanceof BackendApiError) {\n code = error.code;\n requestId = error.requestId;\n status = error.status;\n userFacing = error.userMessage;\n if (error.detail) chain.push(error.detail);\n if (error.userMessage && error.userMessage !== error.detail) {\n chain.push(error.userMessage);\n }\n } else if (error instanceof Error) {\n chain.push(error.message);\n userFacing = error.message;\n } else if (typeof error === \"string\") {\n chain.push(error);\n userFacing = error;\n } else {\n chain.push(\"Unknown error\");\n }\n\n // Walk the nesting: ANY message in the chain may have stringified the\n // service above it (the technical `detail` usually does, the templated\n // `user_message` never does), so every layer gets unwrapped.\n for (let cursor = 0; cursor < chain.length && cursor < 12; cursor += 1) {\n const upstream = unwrapUpstreamError(chain[cursor] ?? \"\");\n if (!upstream || chain.includes(upstream.message)) continue;\n chain.push(upstream.message);\n if (upstream.code) code = upstream.code;\n if (upstream.requestId) requestId = upstream.requestId;\n if (upstream.status !== null) status = upstream.status;\n }\n\n const specific = [...chain]\n .reverse()\n .find((message) => !isGenericUserMessage(message));\n const cause = specific ?? chain[0] ?? \"Unknown error\";\n const headlineWasGeneric = isGenericUserMessage(userFacing);\n return {\n code,\n cause,\n headline: headlineWasGeneric ? cause : (userFacing ?? cause),\n headlineWasGeneric,\n chain,\n requestId,\n status,\n };\n}\n\n/**\n * Extract a user-visible message from any error object.\n * Utility for components that just need the display string.\n */\nexport function getUserMessage(error: unknown): string {\n if (error instanceof BackendApiError) {\n return error.userMessage;\n }\n if (error instanceof Error) {\n return error.message;\n }\n if (typeof error === \"string\") {\n return error;\n }\n // A Redux thunk's `.unwrap()` rejects with a SerializedError — a plain\n // object, not an Error — whose `message` is the real reason.\n if (\n typeof error === \"object\" &&\n error !== null &&\n typeof (error as { message?: unknown }).message === \"string\" &&\n (error as { message: string }).message\n ) {\n return (error as { message: string }).message;\n }\n return \"Something went wrong\";\n}\n\n// ============================================================================\n// INTERNAL HELPERS\n// ============================================================================\n\nfunction statusToCode(status: number): BackendErrorCode {\n switch (status) {\n case 401:\n return \"auth_required\";\n case 403:\n return \"admin_required\";\n case 404:\n return \"not_found\";\n case 422:\n return \"validation_error\";\n default:\n return \"internal_error\";\n }\n}\n","/**\n * Canonical AI Matrx NDJSON wire kernel.\n *\n * This module is deliberately independent of React, Redux, Next.js, Supabase,\n * and generated application types. Every Matrx client uses it to turn the\n * backend's byte stream into the same normalized `{ event, data }` envelopes.\n * Host runtimes remain responsible for HTTP/auth errors and for deciding what\n * each event means in their state model.\n */\n\nexport interface MatrxStreamEnvelope<TData = unknown> {\n event: string;\n /** Immutable emitter segment; transport sequence is scoped to this id. */\n stream_id?: string;\n data: TData;\n /** Monotonic transport sequence from full envelopes, when supplied. */\n stream_seq?: number;\n}\n\nexport interface MatrxNdjsonIssue {\n line: string;\n error: unknown;\n /** One-based physical NDJSON line number. */\n lineNumber: number;\n /** True when an unterminated trailing fragment was parsed by `finish()`. */\n atCompletion: boolean;\n}\n\nexport interface MatrxStreamEnvelopeObservation {\n /** Exact parsed JSON value before compact/full normalization. */\n raw: unknown;\n envelope: MatrxStreamEnvelope;\n line: string;\n lineNumber: number;\n atCompletion: boolean;\n}\n\nexport interface MatrxNdjsonFramerOptions {\n /** Malformed JSON is non-fatal, but it must never disappear silently. */\n onMalformedLine?: (issue: MatrxNdjsonIssue) => void;\n /** Valid JSON with no recognized Matrx event envelope is also non-fatal. */\n onUnknownEnvelope?: (value: unknown) => void;\n /** Observe every valid envelope without changing or consuming it. */\n onValidEnvelope?: (observation: MatrxStreamEnvelopeObservation) => void;\n}\n\nexport interface ReadMatrxNdjsonOptions extends MatrxNdjsonFramerOptions {\n signal?: AbortSignal;\n /** Maximum normalized events read ahead of the iterator consumer. */\n maxReadAhead?: number;\n}\n\nexport interface MatrxNdjsonFramer {\n /** Push a text fragment. Fragments may split JSON tokens or line endings. */\n pushText(fragment: string): MatrxStreamEnvelope[];\n /** Push a byte fragment. UTF-8 code points may span calls. */\n pushBytes(fragment: Uint8Array): MatrxStreamEnvelope[];\n /** Parse the final unterminated line and flush any pending UTF-8 bytes. */\n finish(): MatrxStreamEnvelope[];\n}\n\ntype QueueItem =\n | { kind: \"event\"; value: MatrxStreamEnvelope }\n | { kind: \"error\"; error: unknown }\n | { kind: \"done\" };\n\nexport const DEFAULT_MATRX_NDJSON_READ_AHEAD = 64;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Normalize both supported Matrx wire shapes:\n *\n * - full: `{ \"event\": \"chunk\", \"data\": { \"text\": \"...\" } }`\n * - compact chunk: `{ \"e\": \"c\", \"t\": \"...\" }`\n * - compact reasoning: `{ \"e\": \"r\", \"t\": \"...\" }`\n */\nexport function normalizeMatrxStreamEnvelope(\n value: unknown,\n): MatrxStreamEnvelope | null {\n if (!isRecord(value)) return null;\n\n if (typeof value.event === \"string\") {\n const streamSeq =\n typeof value.stream_seq === \"number\" && Number.isFinite(value.stream_seq)\n ? value.stream_seq\n : undefined;\n const streamId = typeof value.stream_id === \"string\" ? value.stream_id : undefined;\n return {\n event: value.event,\n data: value.data,\n ...(streamId === undefined ? {} : { stream_id: streamId }),\n ...(streamSeq === undefined ? {} : { stream_seq: streamSeq }),\n };\n }\n if (value.e === \"c\" && typeof value.t === \"string\") {\n const streamId = typeof value.stream_id === \"string\" ? value.stream_id : undefined;\n const streamSeq =\n typeof value.stream_seq === \"number\" && Number.isFinite(value.stream_seq)\n ? value.stream_seq\n : undefined;\n return {\n event: \"chunk\",\n data: { text: value.t },\n ...(streamId === undefined ? {} : { stream_id: streamId }),\n ...(streamSeq === undefined ? {} : { stream_seq: streamSeq }),\n };\n }\n if (value.e === \"r\" && typeof value.t === \"string\") {\n const streamId = typeof value.stream_id === \"string\" ? value.stream_id : undefined;\n const streamSeq =\n typeof value.stream_seq === \"number\" && Number.isFinite(value.stream_seq)\n ? value.stream_seq\n : undefined;\n return {\n event: \"reasoning_chunk\",\n data: { text: value.t },\n ...(streamId === undefined ? {} : { stream_id: streamId }),\n ...(streamSeq === undefined ? {} : { stream_seq: streamSeq }),\n };\n }\n return null;\n}\n\n/**\n * Create the transport-independent NDJSON framer used by the stream reader.\n * Browser extensions, desktop bridges, WebSockets, and tests can feed it\n * fragmented strings or bytes without constructing a `ReadableStream`.\n */\nexport function createMatrxNdjsonFramer(\n options: MatrxNdjsonFramerOptions = {},\n): MatrxNdjsonFramer {\n const decoder = new TextDecoder();\n let buffer = \"\";\n let lineNumber = 0;\n let finished = false;\n\n const assertOpen = (): void => {\n if (finished) throw new Error(\"Matrx NDJSON framer is already finished\");\n };\n\n const parseLine = (\n line: string,\n atCompletion: boolean,\n ): MatrxStreamEnvelope | null => {\n const currentLineNumber = ++lineNumber;\n const trimmed = line.trim();\n if (!trimmed) return null;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed) as unknown;\n } catch (error) {\n options.onMalformedLine?.({\n line: trimmed,\n error,\n lineNumber: currentLineNumber,\n atCompletion,\n });\n return null;\n }\n\n const envelope = normalizeMatrxStreamEnvelope(parsed);\n if (!envelope) {\n options.onUnknownEnvelope?.(parsed);\n return null;\n }\n options.onValidEnvelope?.({\n raw: parsed,\n envelope,\n line: trimmed,\n lineNumber: currentLineNumber,\n atCompletion,\n });\n return envelope;\n };\n\n const pushDecodedText = (fragment: string): MatrxStreamEnvelope[] => {\n buffer += fragment;\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n const envelopes: MatrxStreamEnvelope[] = [];\n for (const line of lines) {\n const envelope = parseLine(line, false);\n if (envelope) envelopes.push(envelope);\n }\n return envelopes;\n };\n\n return {\n pushText(fragment) {\n assertOpen();\n // Flush any pending byte fragment before switching to explicit text.\n return pushDecodedText(decoder.decode() + fragment);\n },\n pushBytes(fragment) {\n assertOpen();\n return pushDecodedText(decoder.decode(fragment, { stream: true }));\n },\n finish() {\n assertOpen();\n finished = true;\n const envelopes = pushDecodedText(decoder.decode());\n if (buffer.length > 0) {\n const envelope = parseLine(buffer, true);\n buffer = \"\";\n if (envelope) envelopes.push(envelope);\n }\n return envelopes;\n },\n };\n}\n\nfunction readAheadLimit(value: number | undefined): number {\n const limit = value ?? DEFAULT_MATRX_NDJSON_READ_AHEAD;\n if (!Number.isSafeInteger(limit) || limit < 1) {\n throw new RangeError(\"maxReadAhead must be a positive safe integer\");\n }\n return limit;\n}\n\n/**\n * Read and normalize a Matrx NDJSON response body with bounded background\n * read-ahead. Consumer work does not stall the network until `maxReadAhead`\n * complete events are waiting; the bound prevents an abandoned or blocked\n * consumer from growing memory without limit.\n */\nexport async function* readMatrxNdjsonStream(\n body: ReadableStream<Uint8Array>,\n options: ReadMatrxNdjsonOptions = {},\n): AsyncGenerator<MatrxStreamEnvelope, void, undefined> {\n const maxReadAhead = readAheadLimit(options.maxReadAhead);\n const queue: QueueItem[] = [];\n let queuedEventCount = 0;\n let wakeConsumer: (() => void) | null = null;\n let wakeProducer: (() => void) | null = null;\n let readerFinished = false;\n let consumerClosed = false;\n\n const wakeWaitingConsumer = (): void => {\n const wake = wakeConsumer;\n wakeConsumer = null;\n wake?.();\n };\n const wakeWaitingProducer = (): void => {\n const wake = wakeProducer;\n wakeProducer = null;\n wake?.();\n };\n const enqueueTerminal = (item: QueueItem): void => {\n if (consumerClosed) return;\n queue.push(item);\n wakeWaitingConsumer();\n };\n const enqueueEvent = async (value: MatrxStreamEnvelope): Promise<boolean> => {\n while (\n queuedEventCount >= maxReadAhead &&\n !consumerClosed &&\n !options.signal?.aborted\n ) {\n await new Promise<void>((resolve) => {\n wakeProducer = resolve;\n });\n }\n if (consumerClosed || options.signal?.aborted) return false;\n queue.push({ kind: \"event\", value });\n queuedEventCount += 1;\n wakeWaitingConsumer();\n return true;\n };\n const waitForReadCapacity = async (): Promise<boolean> => {\n while (\n queuedEventCount >= maxReadAhead &&\n !consumerClosed &&\n !options.signal?.aborted\n ) {\n await new Promise<void>((resolve) => {\n wakeProducer = resolve;\n });\n }\n return !consumerClosed && !options.signal?.aborted;\n };\n\n const reader = body.getReader();\n const framer = createMatrxNdjsonFramer(options);\n\n const onAbort = (): void => {\n wakeWaitingProducer();\n wakeWaitingConsumer();\n void reader.cancel(options.signal?.reason).catch(() => undefined);\n };\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n if (options.signal?.aborted) onAbort();\n\n const readerPromise = (async (): Promise<void> => {\n try {\n while (!options.signal?.aborted && !consumerClosed) {\n if (!(await waitForReadCapacity())) return;\n const { value, done } = await reader.read();\n if (done) break;\n for (const envelope of framer.pushBytes(value)) {\n if (!(await enqueueEvent(envelope))) return;\n }\n }\n\n if (!options.signal?.aborted && !consumerClosed) {\n for (const envelope of framer.finish()) {\n if (!(await enqueueEvent(envelope))) return;\n }\n }\n } catch (error) {\n const aborted =\n options.signal?.aborted ||\n consumerClosed ||\n (error instanceof Error && error.name === \"AbortError\");\n if (!aborted) enqueueTerminal({ kind: \"error\", error });\n } finally {\n readerFinished = true;\n reader.releaseLock();\n enqueueTerminal({ kind: \"done\" });\n }\n })();\n\n try {\n while (true) {\n if (queue.length === 0) {\n if (options.signal?.aborted || readerFinished) return;\n await new Promise<void>((resolve) => {\n wakeConsumer = resolve;\n });\n }\n\n const item = queue.shift();\n if (item?.kind === \"event\") queuedEventCount -= 1;\n wakeWaitingProducer();\n if (!item || item.kind === \"done\") return;\n if (item.kind === \"error\") throw item.error;\n yield item.value;\n }\n } finally {\n consumerClosed = true;\n wakeWaitingProducer();\n wakeWaitingConsumer();\n options.signal?.removeEventListener(\"abort\", onAbort);\n if (!readerFinished) {\n await reader.cancel().catch(() => undefined);\n }\n await readerPromise;\n }\n}\n","import { isUuidShape } from \"@ai-matrx/kit/uuid\";\n\n/**\n * The fail-closed organization-context kernel — ONE implementation for every\n * Matrx client transport (moved in from matrx-frontend\n * `lib/api/organization-context.ts` verbatim under C22; the host module is now\n * a re-export of this one).\n *\n * Transports resolve their authoritative organization first, then use these\n * functions to bind that exact value without guessing or defaulting. The\n * server never manufactures an org (it 422s a blank one), so the client\n * refuses before the wire: a missing or malformed org id throws\n * `OrganizationContextError` instead of sending a request that cannot succeed.\n */\n\nexport type OrganizationContextErrorCode =\n | \"organization_context_required\"\n | \"organization_context_invalid\"\n | \"organization_context_mismatch\";\n\nexport class OrganizationContextError extends Error {\n readonly code: OrganizationContextErrorCode;\n\n constructor(code: OrganizationContextErrorCode, message: string) {\n super(message);\n this.name = \"OrganizationContextError\";\n this.code = code;\n }\n}\n\ndeclare const organizationOperationBrand: unique symbol;\n\n/** A validated organization operation; construct only through the factory. */\nexport type OrganizationOperation = Readonly<{\n organization_id: string;\n readonly [organizationOperationBrand]: true;\n}>;\n\nexport function createOrganizationOperation(\n organizationId: string,\n): OrganizationOperation {\n const organization_id = requireOrganizationContext(organizationId);\n return Object.freeze({ organization_id }) as OrganizationOperation;\n}\n\nexport function assertOrganizationMatchesOperation(\n operation: OrganizationOperation,\n organizationId: string,\n): void {\n if (operation.organization_id !== requireOrganizationContext(organizationId)) {\n throw new OrganizationContextError(\n \"organization_context_mismatch\",\n \"Organization ID must match the request operation.\",\n );\n }\n}\n\n/**\n * Normalize and validate the effective organization id for a request. The\n * override (an explicit per-call value) beats the selected context value.\n * Throws `organization_context_required` when neither is present and\n * `organization_context_invalid` when the candidate is not a UUID.\n */\nexport function requireOrganizationContext(\n selectedOrganizationId: string | null | undefined,\n overrideOrganizationId?: string,\n): string {\n const candidate = overrideOrganizationId ?? selectedOrganizationId;\n if (typeof candidate !== \"string\" || candidate.trim().length === 0) {\n throw new OrganizationContextError(\n \"organization_context_required\",\n \"Select an organization before sending this request.\",\n );\n }\n\n const normalized = candidate.trim();\n if (!isUuidShape(normalized)) {\n throw new OrganizationContextError(\n \"organization_context_invalid\",\n \"The selected organization ID is invalid.\",\n );\n }\n return normalized.toLowerCase();\n}\n\n/**\n * Bind `X-Organization-Id` onto a header bag. A pre-existing org header that\n * disagrees with the context org is a `organization_context_mismatch` error —\n * never silently overwritten in either direction.\n */\nexport function applyOrganizationContextHeader(\n headers: Record<string, string>,\n organizationId: string,\n): Record<string, string> {\n const normalizedOrganizationId = requireOrganizationContext(organizationId);\n for (const [name, value] of Object.entries(headers)) {\n if (\n name.toLowerCase() === \"x-organization-id\" &&\n value.trim().toLowerCase() !== normalizedOrganizationId\n ) {\n throw new OrganizationContextError(\n \"organization_context_mismatch\",\n \"X-Organization-Id must match the request context organization.\",\n );\n }\n }\n const withoutOrganizationHeader = Object.fromEntries(\n Object.entries(headers).filter(\n ([name]) => name.toLowerCase() !== \"x-organization-id\",\n ),\n );\n return {\n ...withoutOrganizationHeader,\n \"X-Organization-Id\": normalizedOrganizationId,\n };\n}\n\n/**\n * Assert that an `organization_id` query parameter (when present) matches the\n * request context organization — the query string must never smuggle a\n * different org past the header binding.\n */\nexport function assertQueryOrganizationMatchesContext(\n queryParams: Record<string, string | number | boolean> | undefined,\n organizationId: string,\n): void {\n if (!queryParams || queryParams.organization_id === undefined) return;\n const queryOrganizationId = requireOrganizationContext(\n String(queryParams.organization_id),\n );\n if (queryOrganizationId !== requireOrganizationContext(organizationId)) {\n throw new OrganizationContextError(\n \"organization_context_mismatch\",\n \"Query organization_id must match the request context organization.\",\n );\n }\n}\n","/**\n * `@ai-matrx/agents/matrx` — THE request pipeline every typed Matrx server call\n * rides (chat package independence P9b). One implementation for every client:\n * matrx-frontend's `lib/api` `callApi` and the chat package's bare-host default\n * both build on it, and supply only their host facts — where the server is,\n * which credential and organization ride, and where diagnostics go.\n *\n * What lives here (pure, no host/window/env read):\n *\n * - `buildMatrxRequestUrl` — path params, the legacy `/api` strip, the query;\n * - `buildMatrxRequestBody` — scope injection (`organization_id` /\n * `project_id` / `task_id`), the UI-only field strip, and the fail-closed\n * body-vs-context organization check;\n * - `bareStatusSentence` / `isBareTransportCode` / `honestTransportMessage` —\n * a bare status code is never a sentence at a person;\n * - `parseMatrxNdjsonResponse` — a response body as typed envelopes, a broken\n * body reader classified as a resumable `StreamTransportError`;\n * - `executeMatrxCall` — the JSON or NDJSON execution over the v2 → v1\n * protocol fallback, with the stream callbacks and `consumeStream`;\n * - `sendMatrxRequest` / `readMatrxJsonResponse` — the raw-`Response` lane\n * for a host's imperative client (multipart bodies, byte downloads,\n * response-aware callers): one deadline, a caller abort kept as itself, and\n * the ONE HTTP error parser;\n * - `buildSafeRequestLog` / `redactUrlForRequestLog` /\n * `shouldReportMatrxCallError` — the log and capture policy.\n *\n * The error envelope (`MatrxCallError`) and its ONE classifier\n * (`normalizeMatrxError`) live in `./client`.\n */\n\nimport {\n readMatrxNdjsonStream,\n type MatrxNdjsonIssue,\n type MatrxStreamEnvelope,\n} from \"../stream/ndjson\";\nimport {\n BackendApiError,\n parseHttpError,\n StreamTransportError,\n} from \"./backend-errors\";\nimport type { MatrxCallError } from \"./client\";\nimport { OrganizationContextError } from \"./org-context\";\nimport {\n fetchWithMatrxProtocolFallback,\n type MatrxProtocolDowngrade,\n} from \"./protocol\";\nimport { extractMatrxErrorMessage } from \"./transport\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport type MatrxHttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/**\n * Every context dimension a call may carry. `organization_id`, `project_id`\n * and `task_id` are injected into the body; `user_id` rides the credential and\n * `conversation_id` the path or an explicit body field — never injected.\n */\nexport interface MatrxCallScope {\n user_id?: string;\n organization_id?: string;\n project_id?: string;\n task_id?: string;\n conversation_id?: string;\n}\n\nexport interface MatrxCallResult<T = unknown> {\n /** Parsed JSON response body (non-streaming calls only). */\n data?: T;\n /** Server-assigned request id (response header). */\n requestId?: string;\n /** Server-assigned conversation id (response header). */\n conversationId?: string;\n /** Set when the call failed with an HTTP error response. */\n error?: MatrxCallError;\n}\n\ntype MatrxQueryScalar = string | number | boolean;\n\n/**\n * Query values: a scalar is sent as itself, an array repeats the key (the\n * FastAPI repeatable-parameter convention), `null` / `undefined` are omitted.\n */\nexport type MatrxQueryParams = Record<\n string,\n MatrxQueryScalar | readonly MatrxQueryScalar[] | null | undefined\n>;\n\n// ─── URL ────────────────────────────────────────────────────────────────────\n\n/**\n * The full URL for one call: `{param}` segments substituted (encoded), the\n * legacy `/api` prefix stripped (server routes no longer live under it), and\n * the query appended.\n */\nexport function buildMatrxRequestUrl(\n baseUrl: string,\n pathTemplate: string,\n pathParams?: Record<string, string>,\n queryParams?: MatrxQueryParams,\n): string {\n let resolvedPath = pathTemplate;\n if (pathParams) {\n for (const [key, value] of Object.entries(pathParams)) {\n resolvedPath = resolvedPath.replace(`{${key}}`, encodeURIComponent(value));\n }\n }\n const fullPath = resolvedPath.startsWith(\"/api/\")\n ? resolvedPath.slice(4)\n : resolvedPath;\n const url = `${baseUrl}${fullPath}`;\n if (queryParams) {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(queryParams)) {\n if (value === null || value === undefined) continue;\n const values: readonly MatrxQueryScalar[] = Array.isArray(value)\n ? value\n : [value as MatrxQueryScalar];\n for (const entry of values) search.append(key, String(entry));\n }\n const qs = search.toString();\n if (qs) return `${url}${url.includes(\"?\") ? \"&\" : \"?\"}${qs}`;\n }\n return url;\n}\n\n// ─── Body ───────────────────────────────────────────────────────────────────\n\n/**\n * Client capability flags that must never reach the server — the server's\n * request schemas reject them.\n */\nexport const MATRX_UI_ONLY_BODY_FIELDS: ReadonlySet<string> = new Set([\n \"youtube_videos\",\n \"file_urls\",\n \"image_urls\",\n]);\n\n/**\n * The final request body: UI-only fields stripped, scope fields injected.\n *\n * A caller's `organization_id: null` (or blank) means \"I have none of my own\",\n * never \"send this for a different organization\" — it is dropped and the\n * scope's organization injected. A real value that disagrees with the scope\n * is refused (`organization_context_mismatch`). With no scope organization\n * (the org-less guest lane, an org-free read) nothing is injected for it.\n * Other scope fields keep caller-wins behaviour.\n */\nexport function buildMatrxRequestBody(\n body: unknown,\n scope: MatrxCallScope,\n): Record<string, unknown> {\n // MATRX-EXCEPTION: `body` is optional by design — a caller with no body\n // still gets scope fields injected, so `{}` is the correct start.\n const raw = (body ?? {}) as Record<string, unknown>;\n const base: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(raw)) {\n if (!MATRX_UI_ONLY_BODY_FIELDS.has(key)) base[key] = value;\n }\n\n if (\n base.organization_id === null ||\n (typeof base.organization_id === \"string\" &&\n base.organization_id.trim() === \"\")\n ) {\n delete base.organization_id;\n }\n\n const bodyOrganizationId = base.organization_id;\n if (\n scope.organization_id !== undefined &&\n bodyOrganizationId !== undefined &&\n (typeof bodyOrganizationId !== \"string\" ||\n bodyOrganizationId.trim() !== scope.organization_id)\n ) {\n throw new OrganizationContextError(\n \"organization_context_mismatch\",\n \"Request body organization_id must match the request context organization.\",\n );\n }\n if (bodyOrganizationId !== undefined && scope.organization_id !== undefined) {\n base.organization_id = scope.organization_id;\n }\n\n const scopeFields: Record<string, unknown> = {\n ...(scope.organization_id !== undefined\n ? { organization_id: scope.organization_id }\n : {}),\n };\n if (scope.project_id !== undefined) scopeFields.project_id = scope.project_id;\n if (scope.task_id !== undefined) scopeFields.task_id = scope.task_id;\n return { ...scopeFields, ...base };\n}\n\n// ─── Honest status sentences ────────────────────────────────────────────────\n\nconst BARE_TRANSPORT_CODE =\n /^\\s*(?:HTTP|HTTP\\s*Error|Status(?:\\s*Code)?)?\\s*[:\\-]?\\s*\\d{3}\\s*[.:!]?\\s*$/i;\n\n/** True when a candidate sentence is really just the status line wearing words. */\nexport function isBareTransportCode(text: string | null | undefined): boolean {\n if (!text) return false;\n return BARE_TRANSPORT_CODE.test(text);\n}\n\n/**\n * A BARE STATUS CODE IS NEVER A SENTENCE. What a person reads when the server\n * answered an error with no readable message. The status itself rides\n * `error.status`, which is what code branches on — this is only the words.\n */\nexport function bareStatusSentence(status: number): string {\n if (status === 401 || status === 403) {\n return \"The server would not let this request through — your session may have expired, or this account may not have access here. Sign in again, and if it repeats, ask an administrator.\";\n }\n if (status === 404) {\n return \"The server has nothing at that address. Reload the page; if it repeats, report it — a client asking for something that no longer exists is a defect, not your mistake.\";\n }\n if (status === 429) {\n return \"The server is rate-limiting this request. Wait a few seconds and try again.\";\n }\n if (status >= 500) {\n return \"The server failed while answering this, and sent no explanation. Try again in a moment; if it repeats, report it with the request id above.\";\n }\n return \"The server refused this request and sent no reason with it — the missing reason is itself a defect worth reporting. Reload the page and try once more; if it repeats, report it with the request id above.\";\n}\n\n/** `raw` unless it is empty or only a status line; then the status sentence. */\nexport function honestTransportMessage(\n raw: string,\n status: number | undefined,\n): string {\n if (raw && !isBareTransportCode(raw)) return raw;\n return bareStatusSentence(status ?? 0);\n}\n\n// ─── Log + capture policy ───────────────────────────────────────────────────\n\nconst SENSITIVE_HEADER_NAME = /authorization|cookie|token|api[-_]?key/i;\n\n/** Request metadata safe to log: secret headers redacted, the body as its shape only. */\nexport function buildSafeRequestLog(\n headers: Record<string, string>,\n body: unknown,\n): { headers: Record<string, string>; body: Record<string, unknown> } {\n const safeHeaders = Object.fromEntries(\n Object.entries(headers).map(([name, value]) => [\n name,\n SENSITIVE_HEADER_NAME.test(name) ? \"[REDACTED]\" : value,\n ]),\n );\n const bodyMetadata: Record<string, unknown> = Array.isArray(body)\n ? { type: \"array\", itemCount: body.length }\n : body && typeof body === \"object\"\n ? { type: \"object\", keys: Object.keys(body as Record<string, unknown>) }\n : { type: body === null ? \"null\" : typeof body };\n return { headers: safeHeaders, body: bodyMetadata };\n}\n\n/** The URL with every query value redacted. */\nexport function redactUrlForRequestLog(url: string): string {\n try {\n const parsed = new URL(url);\n for (const key of parsed.searchParams.keys()) {\n parsed.searchParams.set(key, \"[REDACTED]\");\n }\n return parsed.toString();\n } catch {\n const queryIndex = url.indexOf(\"?\");\n return queryIndex === -1 ? url : `${url.slice(0, queryIndex)}?[REDACTED]`;\n }\n}\n\n/** False only for an HTTP status the call site declared an expected outcome. */\nexport function shouldReportMatrxCallError(\n status: number | null | undefined,\n expectedErrorStatuses: readonly number[] | undefined,\n): boolean {\n return status == null || !expectedErrorStatuses?.includes(status);\n}\n\n// ─── Stream parsing ─────────────────────────────────────────────────────────\n\nexport interface MatrxStreamIds {\n requestId: string | null;\n conversationId: string | null;\n}\n\nexport interface MatrxStreamParse<E = MatrxStreamEnvelope> extends MatrxStreamIds {\n events: AsyncGenerator<E, void, undefined>;\n}\n\nexport interface ParseMatrxNdjsonResponseHooks {\n /** Every envelope, before the consumer sees it. */\n onEvent?: (event: MatrxStreamEnvelope, ids: MatrxStreamIds) => void;\n /** A broken body reader, already classified, before it is thrown. */\n onTransportError?: (error: BackendApiError, ids: MatrxStreamIds) => void;\n onMalformedLine?: (issue: MatrxNdjsonIssue) => void;\n onUnknownEnvelope?: (value: unknown) => void;\n}\n\n/**\n * A Matrx NDJSON response as typed envelopes. The ids come from headers, so\n * they are available before any event. A body that breaks mid-run is a\n * TRANSPORT loss (the run may still finish server-side and is reattachable) —\n * thrown as `StreamTransportError`, never a failed run; an abort ends quietly.\n */\nexport function parseMatrxNdjsonResponse(\n response: Response,\n signal?: AbortSignal,\n hooks: ParseMatrxNdjsonResponseHooks = {},\n): MatrxStreamParse {\n const ids: MatrxStreamIds = {\n requestId: response.headers.get(\"X-Request-ID\"),\n conversationId: response.headers.get(\"X-Conversation-ID\"),\n };\n async function* events(): AsyncGenerator<MatrxStreamEnvelope, void, undefined> {\n if (!response.body) {\n throw new BackendApiError({\n code: \"internal_error\",\n detail: \"Response has no body\",\n userMessage: \"No response received from server\",\n });\n }\n try {\n for await (const envelope of readMatrxNdjsonStream(response.body, {\n ...(signal ? { signal } : {}),\n ...(hooks.onMalformedLine ? { onMalformedLine: hooks.onMalformedLine } : {}),\n ...(hooks.onUnknownEnvelope ? { onUnknownEnvelope: hooks.onUnknownEnvelope } : {}),\n })) {\n hooks.onEvent?.(envelope, ids);\n yield envelope;\n }\n } catch (error) {\n if (signal?.aborted || (error instanceof Error && error.name === \"AbortError\")) {\n return;\n }\n const transportError =\n error instanceof BackendApiError\n ? error\n : new StreamTransportError({\n detail:\n error instanceof Error\n ? error.message\n : \"The response stream ended unexpectedly.\",\n details: error,\n ...(ids.requestId ? { requestId: ids.requestId } : {}),\n });\n hooks.onTransportError?.(transportError, ids);\n throw transportError;\n }\n }\n return { events: events(), ...ids };\n}\n\n// ─── Execution ──────────────────────────────────────────────────────────────\n\nexport interface ExecuteMatrxCallRequest<E = MatrxStreamEnvelope> {\n /** The final URL (`buildMatrxRequestUrl`). */\n url: string;\n method: string;\n /** Every header the host binds (credential, organization, Content-Type). */\n headers: Record<string, string>;\n /** The assembled body (`buildMatrxRequestBody`); never sent on GET/HEAD. */\n body: unknown;\n /** NDJSON streaming call. */\n stream?: boolean;\n signal?: AbortSignal;\n /** Time to response headers. Default 15_000. */\n connectTimeoutMs?: number;\n /** Whole-request cap for JSON calls. Default 30_000; `null` uncaps. Streams are uncapped. */\n totalTimeoutMs?: number | null;\n /** Fires when headers arrive, before any event. */\n onStreamStart?: (requestId: string | null, conversationId: string | null) => void;\n onStreamEvent?: (event: E) => void;\n /**\n * Take ownership of the body instead of the executor draining it (a body is\n * consumed once). `onStreamEvent` is then not called; start / complete /\n * error still fire.\n */\n consumeStream?: (response: Response, ids: MatrxStreamIds) => Promise<void>;\n onStreamComplete?: (requestId: string | null, conversationId: string | null) => void;\n /** Fires for an HTTP error response on a stream (thrown failures are the caller's). */\n onStreamError?: (error: MatrxCallError) => void;\n}\n\nexport interface ExecuteMatrxCallHooks<E = MatrxStreamEnvelope> {\n /** Every v2 → v1 protocol downgrade. */\n onProtocolDowngrade?: (downgrade: MatrxProtocolDowngrade) => void;\n /** The host's stream parser (default `parseMatrxNdjsonResponse`). */\n parseStream?: (response: Response, signal?: AbortSignal) => MatrxStreamParse<E>;\n}\n\nconst DEFAULT_CONNECT_TIMEOUT_MS = 15_000;\nconst DEFAULT_JSON_TOTAL_TIMEOUT_MS = 30_000;\n\nasync function httpErrorFrom(response: Response): Promise<MatrxCallError> {\n const serverDetail: unknown = await response.json().catch(() => undefined);\n return {\n type:\n response.status >= 400 && response.status < 500\n ? \"validation_error\"\n : \"http_error\",\n message:\n extractMatrxErrorMessage(serverDetail) ?? bareStatusSentence(response.status),\n status: response.status,\n serverDetail,\n };\n}\n\n/**\n * Execute one call over the v2 → v1 protocol fallback. An HTTP error\n * response resolves as `{ error }`; a thrown failure (network, timeout,\n * abort, a broken stream) propagates — the caller normalizes it with\n * `normalizeMatrxError` and decides what to capture.\n */\nexport async function executeMatrxCall<T = unknown, E = MatrxStreamEnvelope>(\n request: ExecuteMatrxCallRequest<E>,\n hooks: ExecuteMatrxCallHooks<E> = {},\n): Promise<MatrxCallResult<T>> {\n const upper = request.method.toUpperCase();\n const sendsBody = request.stream || (upper !== \"GET\" && upper !== \"HEAD\");\n const { response } = await fetchWithMatrxProtocolFallback(\n request.url,\n {\n method: request.method,\n headers: request.headers,\n ...(sendsBody ? { body: JSON.stringify(request.body) } : {}),\n },\n {\n ...(request.signal ? { signal: request.signal } : {}),\n connectTimeoutMs: request.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,\n totalTimeoutMs: request.stream\n ? null\n : request.totalTimeoutMs === undefined\n ? DEFAULT_JSON_TOTAL_TIMEOUT_MS\n : request.totalTimeoutMs,\n throwOnHttpError: false,\n ...(hooks.onProtocolDowngrade ? { onDowngrade: hooks.onProtocolDowngrade } : {}),\n },\n );\n\n const requestId = response.headers.get(\"X-Request-ID\");\n const conversationId = response.headers.get(\"X-Conversation-ID\");\n const idFields = {\n ...(requestId !== null ? { requestId } : {}),\n ...(conversationId !== null ? { conversationId } : {}),\n };\n\n if (!response.ok) {\n const error = await httpErrorFrom(response);\n if (request.stream) request.onStreamError?.(error);\n return { ...idFields, error };\n }\n\n if (!request.stream) {\n const data = (response.status === 204 ? undefined : await response.json()) as T;\n return { data, ...idFields };\n }\n\n if (request.consumeStream) {\n request.onStreamStart?.(requestId, conversationId);\n await request.consumeStream(response, { requestId, conversationId });\n request.onStreamComplete?.(requestId, conversationId);\n return idFields;\n }\n\n const parse =\n hooks.parseStream ??\n ((res: Response, signal?: AbortSignal) =>\n parseMatrxNdjsonResponse(res, signal) as unknown as MatrxStreamParse<E>);\n const parsed = parse(response, request.signal);\n request.onStreamStart?.(parsed.requestId, parsed.conversationId);\n for await (const event of parsed.events) {\n request.onStreamEvent?.(event);\n }\n request.onStreamComplete?.(parsed.requestId, parsed.conversationId);\n return {\n ...(parsed.requestId !== null ? { requestId: parsed.requestId } : {}),\n ...(parsed.conversationId !== null ? { conversationId: parsed.conversationId } : {}),\n };\n}\n\n// ─── Raw send ───────────────────────────────────────────────────────────────\n\nexport interface SendMatrxRequestOptions {\n /** Caller cancellation; an abort propagates as the caller's own AbortError. */\n signal?: AbortSignal;\n /**\n * Deadline (ms) from send to response headers. On expiry the call rejects\n * with a `BackendApiError` coded `request_timeout` (status 504) — never a\n * fake caller cancellation. Omit for no deadline.\n */\n timeoutMs?: number;\n}\n\n/**\n * Send one request and resolve with the raw `Response`, whatever its status.\n * The lane for an imperative host client whose bodies are not JSON (a\n * `FormData` upload), whose answers are bytes, or which reads the response\n * itself. A caller abort and a network failure propagate unchanged; only this\n * call's own deadline is classified (`request_timeout`).\n */\nexport async function sendMatrxRequest(\n url: string,\n init: RequestInit = {},\n options: SendMatrxRequestOptions = {},\n): Promise<Response> {\n const callerSignal = options.signal ?? init.signal ?? undefined;\n if (options.timeoutMs === undefined) {\n return fetch(url, { ...init, ...(callerSignal ? { signal: callerSignal } : {}) });\n }\n const controller = new AbortController();\n const onCallerAbort = (): void => controller.abort(callerSignal?.reason);\n if (callerSignal) {\n if (callerSignal.aborted) controller.abort(callerSignal.reason);\n else callerSignal.addEventListener(\"abort\", onCallerAbort, { once: true });\n }\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, options.timeoutMs);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } catch (error) {\n if (timedOut) {\n throw new BackendApiError({\n code: \"request_timeout\",\n detail: `${init.method ?? \"GET\"} ${url} exceeded ${options.timeoutMs}ms`,\n userMessage: \"The request timed out — please retry.\",\n status: 504,\n });\n }\n throw error;\n } finally {\n clearTimeout(timer);\n callerSignal?.removeEventListener(\"abort\", onCallerAbort);\n }\n}\n\n/**\n * A response's JSON body, or the ONE classified `BackendApiError`\n * (`parseHttpError`) for a non-2xx answer. A 204 resolves `null`.\n */\nexport async function readMatrxJsonResponse<T>(response: Response): Promise<T> {\n if (!response.ok) throw await parseHttpError(response);\n if (response.status === 204) return null as T;\n return (await response.json()) as T;\n}\n","/**\n * Internal request plumbing for `@ai-matrx/agents/matrx`. Not part of the\n * public surface — `matrx/index.ts` deliberately does not re-export this\n * module. Everything here is pure: no globals, no work at import time.\n */\n\nimport {\n readMatrxNdjsonStream,\n type MatrxNdjsonIssue,\n type MatrxStreamEnvelope,\n type MatrxStreamEnvelopeObservation,\n} from \"../stream/ndjson\";\nimport { MatrxApiError, type MatrxTransport } from \"./transport\";\n\n/** Encode one path segment (an id) safely into a server-relative path. */\nexport function encodePathSegment(value: string): string {\n return encodeURIComponent(value);\n}\n\nexport type QueryValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined;\n\n/**\n * Build a query string. Array values repeat the key (`kind=a&kind=b` — the\n * FastAPI repeatable-parameter convention); undefined values are omitted.\n * Returns \"\" or a string starting with \"?\".\n */\nexport function buildQuery(params: Record<string, QueryValue>): string {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const entry of value) search.append(key, entry);\n } else {\n search.append(key, String(value));\n }\n }\n const encoded = search.toString();\n return encoded ? `?${encoded}` : \"\";\n}\n\nasync function readServerDetail(response: Response): Promise<unknown> {\n try {\n return (await response.json()) as unknown;\n } catch {\n return undefined;\n }\n}\n\nasync function throwApiError(path: string, response: Response): Promise<never> {\n throw new MatrxApiError({\n status: response.status,\n path,\n serverDetail: await readServerDetail(response),\n });\n}\n\nexport interface JsonRequestOptions {\n method: \"GET\" | \"POST\";\n body?: unknown;\n signal?: AbortSignal;\n}\n\n/**\n * Execute a JSON request through the transport. Throws `MatrxApiError` on a\n * non-2xx response; resolves with the parsed JSON body otherwise.\n */\nexport async function requestJson<T>(\n transport: MatrxTransport,\n path: string,\n options: JsonRequestOptions,\n): Promise<T> {\n const hasBody = options.method !== \"GET\" && options.body !== undefined;\n const response = await transport.fetch(path, {\n method: options.method,\n headers: hasBody ? { \"Content-Type\": \"application/json\" } : {},\n ...(hasBody ? { body: JSON.stringify(options.body) } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n });\n if (!response.ok) return throwApiError(path, response);\n return (await response.json()) as T;\n}\n\n/**\n * Options for every streaming call, riding the NDJSON kernel's contract.\n * Public via `./run`'s re-export.\n */\nexport interface MatrxStreamCallOptions {\n /** Abort the fetch and end the events iterator. */\n signal?: AbortSignal;\n /** Bounded background read-ahead (see `stream/ndjson`). */\n maxReadAhead?: number;\n /** Malformed NDJSON is non-fatal but must never disappear silently. */\n onMalformedLine?: (issue: MatrxNdjsonIssue) => void;\n /** Valid JSON with no recognized Matrx envelope. */\n onUnknownEnvelope?: (value: unknown) => void;\n /** Observe every valid envelope in its exact wire form. */\n onValidEnvelope?: (observation: MatrxStreamEnvelopeObservation) => void;\n}\n\n/**\n * A live agent run: the server-assigned ids (from response headers, available\n * BEFORE any event) and the normalized event stream. Public via `./run`.\n */\nexport interface MatrxRunHandle {\n /** `X-Request-ID` — the ONLY id `POST /ai/cancel/{request_id}` accepts. */\n requestId: string | null;\n /** `X-Conversation-ID` — the server's conversation identity. */\n conversationId: string | null;\n /** Normalized `{event, data}` envelopes through the ONE wire kernel. */\n events: AsyncGenerator<MatrxStreamEnvelope, void, undefined>;\n /** The raw response, for hosts that need headers/status beyond the ids. */\n response: Response;\n}\n\n/**\n * Wrap a validated streaming Response into the run handle — the ONE place\n * the id headers are read and the NDJSON kernel is attached (`./run` and\n * `./operations`' rejoin share it).\n */\nexport function toRunHandle(\n response: Response,\n options: MatrxStreamCallOptions,\n): MatrxRunHandle {\n return {\n requestId: response.headers.get(\"X-Request-ID\"),\n conversationId: response.headers.get(\"X-Conversation-ID\"),\n events: readMatrxNdjsonStream(response.body as ReadableStream<Uint8Array>, {\n ...(options.signal ? { signal: options.signal } : {}),\n ...(options.maxReadAhead !== undefined\n ? { maxReadAhead: options.maxReadAhead }\n : {}),\n ...(options.onMalformedLine\n ? { onMalformedLine: options.onMalformedLine }\n : {}),\n ...(options.onUnknownEnvelope\n ? { onUnknownEnvelope: options.onUnknownEnvelope }\n : {}),\n ...(options.onValidEnvelope\n ? { onValidEnvelope: options.onValidEnvelope }\n : {}),\n }),\n response,\n };\n}\n\nexport interface StreamRequestOptions {\n method: \"GET\" | \"POST\";\n body?: unknown;\n /** Extra wire-semantic headers (`Accept`, `Last-Event-ID`). */\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\n/**\n * Execute a streaming request. Throws `MatrxApiError` on a non-2xx response\n * (reading the error body as JSON when possible) or when a 2xx response\n * carries no body; resolves with the validated `Response` otherwise.\n */\nexport async function requestStream(\n transport: MatrxTransport,\n path: string,\n options: StreamRequestOptions,\n): Promise<Response> {\n const hasBody = options.method !== \"GET\" && options.body !== undefined;\n const response = await transport.fetch(path, {\n method: options.method,\n headers: {\n ...(hasBody ? { \"Content-Type\": \"application/json\" } : {}),\n ...options.headers,\n },\n ...(hasBody ? { body: JSON.stringify(options.body) } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n });\n if (!response.ok) return throwApiError(path, response);\n if (!response.body) {\n throw new MatrxApiError({\n status: response.status,\n path,\n serverDetail: { code: \"missing_response_body\" },\n message: \"The streaming response carried no body.\",\n });\n }\n return response;\n}\n","/**\n * `@ai-matrx/agents/stream/sse` — the Matrx SSE frame kernel.\n *\n * Two Matrx clients hand-rolled the identical `text/event-stream` framing —\n * the same separator regex, the same field parsing, the same CRLF incident —\n * for the durable rejoin endpoints (`/runtime/.../events/stream`,\n * `/runs/.../events/stream`). This module is that framing extracted ONCE, as a\n * pure incremental parser, so a host keeps only what is genuinely host policy:\n * the fetch, the stall timer, the retry budget, and what each event MEANS.\n *\n * Contract, mirroring `stream/ndjson`:\n * - Pure and effect-free: no fetch, no timers, no globals. Importing this\n * module performs no work.\n * - Incremental across arbitrary chunk boundaries: a frame split anywhere —\n * mid-line, mid-separator, mid-UTF-8 when using the byte reader — parses\n * identically to one delivered whole.\n * - All three SSE line terminators (`\\r\\n`, `\\n`, `\\r`) and all three frame\n * separators are handled — the CRLF-vs-LF divergence that bit production is\n * covered by construction and by test.\n * - Comment-only frames (heartbeats, `:` lines) ARE emitted (with\n * `data === null`) because hosts use any parsed frame as liveness proof to\n * reset stall timers and retry budgets. Frames with data join multi-line\n * `data:` fields with `\\n` per the SSE spec.\n * - `id:` is surfaced raw AND, when it is a safe integer, as `seq` — the\n * `Last-Event-ID` cursor both Matrx rejoin endpoints use for durable replay.\n * Cursor ADVANCEMENT (`seq > cursor`) stays host-side, next to the retry.\n */\n\nexport interface MatrxSseFrame {\n /** `event:` field; the SSE default \"message\" when absent. */\n event: string;\n /** `id:` field, raw, when present. */\n id: string | null;\n /**\n * `id:` parsed as a non-negative safe integer, else null. Matrx rejoin\n * streams use integer ids as the `Last-Event-ID` replay cursor.\n */\n seq: number | null;\n /**\n * Joined `data:` lines (`\\n`-separated per spec), or null for a frame with\n * no data field at all (e.g. a comment-only heartbeat). An empty-string\n * data field is `\"\"`, not null.\n */\n data: string | null;\n}\n\nconst FRAME_SEPARATOR = /\\r\\n\\r\\n|\\n\\n|\\r\\r/;\nconst LINE_SEPARATOR = /\\r\\n|\\n|\\r/;\n\n/** Parse ONE complete frame's text (no trailing separator). */\nexport function parseMatrxSseFrame(frame: string): MatrxSseFrame {\n let event = \"message\";\n let id: string | null = null;\n const dataLines: string[] = [];\n let sawData = false;\n\n for (const line of frame.split(LINE_SEPARATOR)) {\n if (line.startsWith(\":\")) continue;\n if (line.startsWith(\"event:\")) event = line.slice(6).trim();\n else if (line.startsWith(\"data:\")) {\n sawData = true;\n dataLines.push(line.slice(5).replace(/^ /, \"\"));\n } else if (line.startsWith(\"id:\")) id = line.slice(3).trim();\n }\n\n const seqCandidate = id !== null && id !== \"\" ? Number(id) : NaN;\n const seq =\n Number.isSafeInteger(seqCandidate) && seqCandidate >= 0\n ? seqCandidate\n : null;\n\n return { event, id, seq, data: sawData ? dataLines.join(\"\\n\") : null };\n}\n\nexport interface MatrxSseFramer {\n /** Feed a decoded text chunk; returns every frame it completed. */\n push(chunk: string): MatrxSseFrame[];\n /**\n * Signal end of input. A non-empty trailing buffer is an UNTERMINATED frame:\n * per the SSE spec it was never dispatched, so it is returned separately for\n * the host to treat as diagnostic, never as a delivered event.\n */\n flush(): { incomplete: string | null };\n}\n\n/** Incremental SSE framer over already-decoded text. */\nexport function createMatrxSseFramer(): MatrxSseFramer {\n let buffer = \"\";\n return {\n push(chunk: string): MatrxSseFrame[] {\n buffer += chunk;\n const frames: MatrxSseFrame[] = [];\n for (;;) {\n const sep = FRAME_SEPARATOR.exec(buffer);\n if (sep === null) break;\n // A lone trailing `\\r` could be the first half of `\\r\\n\\r\\n`'s final\n // newline — but the separator regex only matched what is already\n // complete, so the slice below is always safe.\n const frame = buffer.slice(0, sep.index);\n buffer = buffer.slice(sep.index + sep[0].length);\n frames.push(parseMatrxSseFrame(frame));\n }\n return frames;\n },\n flush() {\n const rest = buffer;\n buffer = \"\";\n return { incomplete: rest.length > 0 ? rest : null };\n },\n };\n}\n\nexport interface ReadMatrxSseOptions {\n /**\n * Called with any unterminated trailing text at stream end (a frame the\n * server never finished — diagnostic, not a delivered event).\n */\n onIncomplete?: (text: string) => void;\n}\n\n/**\n * Async-iterate the frames of a byte stream (e.g. `response.body`), handling\n * split UTF-8 across chunk boundaries. Cancellation follows the reader: abort\n * the fetch and the iterator ends; a transport error after complete frames\n * were yielded surfaces AFTER those frames, with its original cause.\n */\nexport async function* readMatrxSseStream(\n stream: ReadableStream<Uint8Array>,\n options: ReadMatrxSseOptions = {},\n): AsyncGenerator<MatrxSseFrame, void, undefined> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n const framer = createMatrxSseFramer();\n try {\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n const frames = framer.push(decoder.decode(value, { stream: true }));\n for (const frame of frames) yield frame;\n }\n const tail = framer.push(decoder.decode());\n for (const frame of tail) yield frame;\n const { incomplete } = framer.flush();\n if (incomplete !== null) options.onIncomplete?.(incomplete);\n } finally {\n reader.releaseLock();\n }\n}\n","/**\n * Runtime operations — the canonical reconnect & resume read surface of the\n * execution spine, over the `MatrxTransport` port.\n *\n * Server truth (verified against aidream source,\n * `aidream/api/routers/runtime_operations.py` + `aidream/services/runtime/\n * reconnect.py`; mounted at bare `/runtime`):\n * - `GET /runtime/operations/{request_id}` — identify by `X-Request-ID`\n * - `GET /runtime/operations/by-link/{kind}/{id}` — identify by feature record\n * - `GET /runtime/executions/{id}/events` — durable seq-cursored page\n * - `GET /runtime/executions/{id}/events/stream` — SSE replay-then-follow,\n * `id:` = per-tree seq, reconnect with `Last-Event-ID`\n * - `POST /runtime/operations/{request_id}/rejoin` — replay + follow the\n * ORIGINAL NDJSON response while its detached task is alive (409 when live\n * delivery is unavailable — fall back to the durable lifecycle stream)\n *\n * The contract: identify → recover durable progress → follow live → re-query\n * the final result from the feature's own record. Token text is deliberately\n * never replayed on the lifecycle stream — that is what `/rejoin` is for.\n *\n * The SSE wire rides the package's own `stream/sse` kernel. This module owns\n * ONE connection's semantics (frames → typed events, cursor advancement,\n * terminal `end`); stall timers, retry budgets, and reconnect loops stay host\n * policy — every yielded item carries the cursor the next attempt resumes from.\n */\n\nimport { readMatrxSseStream, type MatrxSseFrame } from \"../stream/sse\";\nimport type { MatrxJsonObject } from \"./conversation\";\nimport {\n encodePathSegment,\n buildQuery,\n requestJson,\n requestStream,\n toRunHandle,\n type MatrxRunHandle,\n type MatrxStreamCallOptions,\n} from \"./internal\";\nimport { MatrxApiError, type MatrxTransport } from \"./transport\";\n\n// ─── Wire types (mirroring `aidream/services/runtime/reconnect.py`) ─────────\n\n/** `matrx_runtime.models.ExecutionStatus` — the only progress column. */\nexport type MatrxRuntimeExecutionStatus =\n | \"pending\"\n | \"running\"\n | \"paused\"\n | \"waiting_input\"\n | \"completed\"\n | \"failed\"\n | \"cancelled\";\n\nexport const TERMINAL_MATRX_RUNTIME_STATUSES: ReadonlySet<MatrxRuntimeExecutionStatus> =\n new Set([\"completed\", \"failed\", \"cancelled\"]);\n\nconst RUNTIME_STATUSES: ReadonlySet<string> = new Set([\n \"pending\",\n \"running\",\n \"paused\",\n \"waiting_input\",\n \"completed\",\n \"failed\",\n \"cancelled\",\n]);\n\n/** One durable spine event on the wire (`OperationEvent`) — `seq` is the reconnect cursor. */\nexport interface MatrxRuntimeOperationEvent {\n seq: number | null;\n /** Lifecycle vocabulary: created | started | paused | resumed | waiting_input | completed | failed | cancelled | checkpoint_saved | note. */\n kind: string;\n execution_id: string;\n root_execution_id: string | null;\n detail: MatrxJsonObject | null;\n created_at: string | null;\n}\n\n/** One root execution as a reconnecting client sees it (`OperationView`). */\nexport interface MatrxRuntimeOperationView {\n execution_id: string;\n /** Durable request identity — feeds `/rejoin` and no-prompt resume recovery. */\n request_id: string | null;\n type: string;\n status: MatrxRuntimeExecutionStatus;\n is_terminal: boolean;\n waiting_input: boolean;\n /** Decimal on the wire — may arrive as number or string; display-only. */\n cost: number | string;\n meters: Record<string, number | string>;\n link_kind: string | null;\n link_id: string | null;\n error: MatrxJsonObject | null;\n created_at: string | null;\n started_at: string | null;\n ended_at: string | null;\n last_event_seq: number;\n events_path: string;\n stream_path: string;\n}\n\nexport interface MatrxOperationStatusResponse {\n request_id: string;\n operation_count: number;\n operations: MatrxRuntimeOperationView[];\n}\n\nexport interface MatrxOperationsByLinkResponse {\n link_kind: string;\n link_id: string;\n operation_count: number;\n operations: MatrxRuntimeOperationView[];\n}\n\nexport interface MatrxOperationEventsPage {\n execution_id: string;\n root_execution_id: string;\n events: MatrxRuntimeOperationEvent[];\n /** Feeds the next page or the SSE `Last-Event-ID` — polling and push share ONE cursor. */\n next_after_seq: number;\n has_more: boolean;\n root_status: MatrxRuntimeExecutionStatus;\n root_is_terminal: boolean;\n}\n\n// ─── Identify + durable progress ────────────────────────────────────────────\n\n/**\n * Where is my operation? Resolves an `X-Request-ID` to its root execution(s).\n * Returns null on 404 — missing and unowned share one shape by design\n * (existence is never leaked).\n */\nexport async function getRuntimeOperationStatus(\n transport: MatrxTransport,\n requestId: string,\n options: { signal?: AbortSignal } = {},\n): Promise<MatrxOperationStatusResponse | null> {\n try {\n return await requestJson<MatrxOperationStatusResponse>(\n transport,\n `/runtime/operations/${encodePathSegment(requestId)}`,\n { method: \"GET\", ...(options.signal ? { signal: options.signal } : {}) },\n );\n } catch (error) {\n if (error instanceof MatrxApiError && error.status === 404) return null;\n throw error;\n }\n}\n\n/**\n * Operations for a feature record — e.g. `(\"conversation\", conversationId)`,\n * `(\"workflow\", runId)`, `(\"agent_run\", runId)`. Newest first; unowned trees\n * omitted. Returns null on 404 (surface absent, or the caller owns nothing —\n * one shape by design).\n */\nexport async function getRuntimeOperationsByLink(\n transport: MatrxTransport,\n linkKind: string,\n linkId: string,\n options: { limit?: number; signal?: AbortSignal } = {},\n): Promise<MatrxOperationsByLinkResponse | null> {\n const query = buildQuery(\n options.limit !== undefined ? { limit: options.limit } : {},\n );\n try {\n return await requestJson<MatrxOperationsByLinkResponse>(\n transport,\n `/runtime/operations/by-link/${encodePathSegment(linkKind)}/${encodePathSegment(linkId)}${query}`,\n { method: \"GET\", ...(options.signal ? { signal: options.signal } : {}) },\n );\n } catch (error) {\n if (error instanceof MatrxApiError && error.status === 404) return null;\n throw error;\n }\n}\n\n/**\n * Durable progress page for the whole operation TREE:\n * `GET /runtime/executions/{id}/events?after_seq=…`. Pass any node id — it\n * resolves to the root.\n */\nexport function listRuntimeOperationEvents(\n transport: MatrxTransport,\n executionId: string,\n options: {\n afterSeq?: number;\n limit?: number;\n /** Repeatable event-kind filter. */\n kinds?: readonly string[];\n signal?: AbortSignal;\n } = {},\n): Promise<MatrxOperationEventsPage> {\n const query = buildQuery({\n ...(options.afterSeq !== undefined ? { after_seq: options.afterSeq } : {}),\n ...(options.limit !== undefined ? { limit: options.limit } : {}),\n ...(options.kinds !== undefined ? { kind: options.kinds } : {}),\n });\n return requestJson<MatrxOperationEventsPage>(\n transport,\n `/runtime/executions/${encodePathSegment(executionId)}/events${query}`,\n { method: \"GET\", ...(options.signal ? { signal: options.signal } : {}) },\n );\n}\n\n// ─── SSE follow (replay-then-follow with Last-Event-ID) ─────────────────────\n\n/**\n * One item from the follow stream. Every item carries `cursor` — the highest\n * event seq seen so far, which is exactly the `Last-Event-ID` a reconnect\n * resumes from (host retry policy owns the reconnect loop).\n */\nexport type MatrxOperationFollowEvent =\n | {\n /** A parsed durable spine event. */\n type: \"event\";\n event: MatrxRuntimeOperationEvent;\n /** The frame's SSE `id:` as an integer, when it carried one. */\n seq: number | null;\n cursor: number;\n }\n | {\n /**\n * A frame that carried no deliverable event — a comment heartbeat, an\n * unknown event name, or a malformed payload (also surfaced through\n * `onMalformedFrame`). ANY parsed frame proves the wire is alive: hosts\n * reset stall timers and retry budgets on it.\n */\n type: \"liveness\";\n cursor: number;\n }\n | {\n /** The server's terminal frame — the root settled; the stream is over. */\n type: \"end\";\n status: MatrxRuntimeExecutionStatus | null;\n cursor: number;\n };\n\nexport interface FollowRuntimeOperationOptions {\n /** Resume cursor — the operation view's `last_event_seq` (0 = from start). */\n lastEventSeq?: number;\n /** Abort the follow — the generator simply ends. */\n signal?: AbortSignal;\n /** A frame whose payload failed to parse — never silently dropped. */\n onMalformedFrame?: (frame: MatrxSseFrame, error: unknown) => void;\n /** Unterminated trailing SSE text at stream end (diagnostic, never an event). */\n onIncomplete?: (text: string) => void;\n}\n\nfunction parseEndStatus(data: string | null): MatrxRuntimeExecutionStatus | null {\n if (data === null) return null;\n try {\n const parsed = JSON.parse(data) as unknown;\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n typeof (parsed as { status?: unknown }).status === \"string\"\n ) {\n const status = (parsed as { status: string }).status;\n return RUNTIME_STATUSES.has(status)\n ? (status as MatrxRuntimeExecutionStatus)\n : null;\n }\n } catch {\n // malformed end payload — still terminal\n }\n return null;\n}\n\n/**\n * Follow ONE SSE connection of an operation's lifecycle stream:\n * `GET /runtime/executions/{id}/events/stream` with `Last-Event-ID` when\n * resuming past 0. Replays from the cursor, then follows live; a\n * WAITING_INPUT park keeps it open (a resume re-attaches to the same\n * execution and its events continue here). Ends after yielding\n * `{type: \"end\"}` when the root settles; a server close WITHOUT an end frame\n * simply ends the generator — reconnect from the last yielded `cursor` (host\n * retry policy).\n */\nexport async function* followRuntimeOperationEvents(\n transport: MatrxTransport,\n executionId: string,\n options: FollowRuntimeOperationOptions = {},\n): AsyncGenerator<MatrxOperationFollowEvent, void, undefined> {\n let cursor = options.lastEventSeq ?? 0;\n\n const headers: Record<string, string> = { Accept: \"text/event-stream\" };\n if (cursor > 0) headers[\"Last-Event-ID\"] = String(cursor);\n\n const response = await requestStream(\n transport,\n `/runtime/executions/${encodePathSegment(executionId)}/events/stream`,\n {\n method: \"GET\",\n headers,\n ...(options.signal ? { signal: options.signal } : {}),\n },\n );\n\n const frames = readMatrxSseStream(\n response.body as ReadableStream<Uint8Array>,\n options.onIncomplete ? { onIncomplete: options.onIncomplete } : {},\n );\n\n for await (const frame of frames) {\n if (frame.event === \"end\") {\n yield { type: \"end\", status: parseEndStatus(frame.data), cursor };\n return;\n }\n if (frame.event === \"execution_event\" && frame.data !== null) {\n let event: MatrxRuntimeOperationEvent;\n try {\n event = JSON.parse(frame.data) as MatrxRuntimeOperationEvent;\n } catch (error) {\n options.onMalformedFrame?.(frame, error);\n yield { type: \"liveness\", cursor };\n continue;\n }\n if (frame.seq !== null && frame.seq > cursor) cursor = frame.seq;\n yield { type: \"event\", event, seq: frame.seq, cursor };\n continue;\n }\n // Comment heartbeats, unknown event names, data-less frames: liveness.\n yield { type: \"liveness\", cursor };\n }\n}\n\n// ─── Follow to end (the production reconnect policy) ────────────────────────\n\nexport interface FollowRuntimeOperationToEndOptions {\n /** Resume cursor — the operation view's `last_event_seq` (0 = from start). */\n lastEventSeq?: number;\n /** Caller teardown — aborting resolves with `ended: false`. */\n signal?: AbortSignal;\n /**\n * The server pings every ~15s, so a wire that is open but silent past this\n * is dead (buffering proxy, idle-killed connection) — abort the attempt and\n * retry rather than hanging forever. Default 45_000.\n */\n stallTimeoutMs?: number;\n /**\n * Consecutive failed attempts before giving up. A single-server deployment\n * deliberately drains for 60s, then starts a new container — the default\n * budget (60 × 2s) keeps following for ~three minutes so the runtime ledger\n * can bridge that handoff. ANY parsed frame resets the budget. Default 60.\n */\n reconnectLimit?: number;\n /** Delay between attempts. Default 2_000. */\n reconnectDelayMs?: number;\n /** Fired per durable spine event (lifecycle transitions + notes). */\n onEvent: (event: MatrxRuntimeOperationEvent, seq: number | null) => void;\n /** A failed attempt (never silently swallowed when provided). */\n onAttemptError?: (error: unknown) => void;\n /** A frame whose payload failed to parse (the ledger heals gaps on reconnect). */\n onMalformedFrame?: (frame: MatrxSseFrame, error: unknown) => void;\n}\n\nexport interface FollowRuntimeOperationToEndResult {\n /** True when the server sent the terminal `end` frame. */\n ended: boolean;\n /** The root status carried on the `end` frame (when `ended`). */\n status: MatrxRuntimeExecutionStatus | null;\n}\n\n/**\n * Follow an operation's lifecycle stream TO ITS END — the full production\n * reconnect policy over `followRuntimeOperationEvents`: replay-then-follow\n * with bounded reconnects, a stall watchdog, and durable `Last-Event-ID`\n * cursor advancement across attempts.\n *\n * Resolves `{ended: true, status}` on the server's `end` frame (the operation\n * settled); `{ended: false}` when the caller aborted or every reconnect\n * attempt failed. A WAITING_INPUT park keeps the stream open by design — a\n * resume re-attaches to the same execution and its events continue arriving\n * on the same cursor. Any parsed frame — comment heartbeats included —\n * proves the wire is alive and resets both the stall timer and the retry\n * budget.\n */\nexport async function followRuntimeOperationToEnd(\n transport: MatrxTransport,\n executionId: string,\n options: FollowRuntimeOperationToEndOptions,\n): Promise<FollowRuntimeOperationToEndResult> {\n const stallTimeoutMs = options.stallTimeoutMs ?? 45_000;\n const reconnectLimit = options.reconnectLimit ?? 60;\n const reconnectDelayMs = options.reconnectDelayMs ?? 2_000;\n const outer = options.signal;\n\n let cursor = options.lastEventSeq ?? 0;\n let failures = 0;\n\n while (!outer?.aborted && failures < reconnectLimit) {\n const attempt = new AbortController();\n const onOuterAbort = () => attempt.abort();\n outer?.addEventListener(\"abort\", onOuterAbort, { once: true });\n\n let stallTimer: ReturnType<typeof setTimeout> | null = null;\n const armStall = () => {\n if (stallTimer !== null) clearTimeout(stallTimer);\n stallTimer = setTimeout(() => attempt.abort(), stallTimeoutMs);\n };\n\n try {\n const items = followRuntimeOperationEvents(transport, executionId, {\n lastEventSeq: cursor,\n signal: attempt.signal,\n ...(options.onMalformedFrame\n ? { onMalformedFrame: options.onMalformedFrame }\n : {}),\n });\n\n armStall();\n for await (const item of items) {\n armStall();\n failures = 0;\n cursor = item.cursor;\n if (item.type === \"end\") {\n return { ended: true, status: item.status };\n }\n if (item.type === \"event\") {\n options.onEvent(item.event, item.seq);\n }\n }\n // Server closed without an `end` frame (e.g. process restart). Retry —\n // `Last-Event-ID` replays anything missed from the durable ledger.\n failures += 1;\n } catch (error) {\n if (outer?.aborted) break;\n failures += 1;\n options.onAttemptError?.(error);\n } finally {\n if (stallTimer !== null) clearTimeout(stallTimer);\n outer?.removeEventListener(\"abort\", onOuterAbort);\n }\n\n if (!outer?.aborted && failures < reconnectLimit) {\n await new Promise((r) => setTimeout(r, reconnectDelayMs));\n }\n }\n\n return { ended: false, status: null };\n}\n\n// ─── NDJSON rejoin (replay the original response) ───────────────────────────\n\n/**\n * Rejoin the ORIGINAL NDJSON response while its detached task is still alive:\n * `POST /runtime/operations/{request_id}/rejoin`. Replays the response from\n * frame one, then continues live — every frame is sequence-stamped\n * (`stream_seq`), so a same-page reconnect can drop frames it already\n * rendered. Throws `MatrxApiError` with status 409 when live delivery is\n * unavailable — fall back to `followRuntimeOperationEvents` + a final record\n * re-query. The `requestId` must be the server's `X-Request-ID`.\n */\nexport async function rejoinRuntimeOperation(\n transport: MatrxTransport,\n requestId: string,\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n const response = await requestStream(\n transport,\n `/runtime/operations/${encodePathSegment(requestId)}/rejoin`,\n {\n method: \"POST\",\n // The route takes no body model; the reference client posts an empty\n // JSON object. Match it so proxies see an ordinary JSON POST.\n body: {},\n ...(options.signal ? { signal: options.signal } : {}),\n },\n );\n return toRunHandle(response, options);\n}\n","/**\n * Agent run lifecycle against the AI Matrx API — start, continue, resume,\n * cancel — over the `MatrxTransport` port, with every streaming response\n * parsed through the package's ONE NDJSON wire kernel (`stream/ndjson`).\n *\n * Server truth (verified against aidream source):\n * - `POST /ai/agents/{agent_id}` — start (`aidream/api/routers/agents.py`)\n * - `POST /ai/conversations/{conversation_id}` — continue (`aidream/api/routers/conversations.py`)\n * - `POST /ai/conversations/{conversation_id}/resume` — resume after\n * client-delegated tool suspension (same router)\n * - `POST /ai/cancel/{request_id}?mode=interrupt` — cancel (`aidream/api/routers/cancel.py`)\n *\n * Response headers arrive before the body: `X-Conversation-ID` and\n * `X-Request-ID` are surfaced on the run handle immediately. `X-Request-ID`\n * is the ONLY id the server accepts for cancel — a client-local id means\n * nothing to it.\n *\n * Host policy stays out: no retry, no store, no timeouts, no persistence\n * (C10: no-persistence). Cancellation is the caller's `AbortSignal`; a client\n * disconnect never stops server work (`detach_on_disconnect`).\n */\n\nimport type { MatrxStreamEnvelope } from \"../stream/ndjson\";\nimport type {\n MatrxConversationStart,\n MatrxJsonObject,\n MatrxJsonValue,\n} from \"./conversation\";\nimport {\n encodePathSegment,\n buildQuery,\n requestJson,\n requestStream,\n toRunHandle,\n type MatrxRunHandle,\n type MatrxStreamCallOptions,\n} from \"./internal\";\nimport type { MatrxTransport } from \"./transport\";\nimport { streamErrorText } from \"./rejoin\";\n\nexport type { MatrxRunHandle, MatrxStreamCallOptions } from \"./internal\";\n\n// ─── Request shapes (mirroring the server's Pydantic models) ────────────────\n\n/**\n * Stable identity of the durable entity whose saved context owns a run —\n * the server reloads the row and uses ITS scope (`ContextAnchor`,\n * `aidream/services/conversation_context/scope.py`).\n */\nexport interface MatrxContextAnchor {\n resource_type: string;\n resource_id: string;\n}\n\n/**\n * Scope and source fields shared by every scoped request\n * (`ScopedRequest` / `AcceptsInjectedScope` server-side). All optional here;\n * the start request narrows `organization_id` to required.\n */\nexport interface MatrxRequestScope {\n organization_id?: string;\n project_id?: string | null;\n task_id?: string | null;\n /** Active context-scope ids from the client's global picker (membership-validated server-side). */\n scope_ids?: string[] | null;\n /** Active scope-TYPE ids — a type-level selection with no specific scope chosen. */\n active_scope_type_ids?: string[] | null;\n context_anchor?: MatrxContextAnchor | null;\n /** Stable application slug that initiated the request. */\n source_app?: string | null;\n /** Stable feature slug within the source application. */\n source_feature?: string | null;\n /** \"user\" = a person directly triggered this; \"auto\" = client automation; omit for API callers. */\n initiation?: \"user\" | \"auto\" | null;\n /** Specific connected desktop instance allowed to claim delegated local tools. */\n target_instance_id?: string | null;\n}\n\n/**\n * Fields shared by start/continue turn requests (tool injection, client\n * capability envelope, context object). The complex bags (`tools`, `client`,\n * `user`, `config_overrides`) are typed as JSON objects — their authoritative\n * schemas are the server's Pydantic models and the generated API types;\n * this package stays payload-agnostic about them by design.\n */\nexport interface MatrxTurnFields {\n /** What the human typed (string), or structured input parts. Never smuggle machine content here. */\n user_input?: string | MatrxJsonValue[] | null;\n /** Per-run model/config overrides (LLMParams shape). */\n config_overrides?: MatrxJsonObject | null;\n debug?: boolean;\n /** Additive tool specs merged into the agent's resolved tool set. */\n tools?: MatrxJsonObject[];\n /** When set, becomes the agent's ENTIRE tool set for the turn. */\n tools_replace?: MatrxJsonObject[] | null;\n /** Client capability envelope (`ClientContext`). */\n client?: MatrxJsonObject | null;\n /** Per-request user-level tool inclusion/exclusion overrides. */\n user?: MatrxJsonObject | null;\n /** Per-route context object, free-form by design. */\n context?: MatrxJsonObject;\n writable_variables?: string[];\n allow_context_create?: boolean;\n /** Request-snapshot capture override (tri-state; omit for the platform default). */\n snapshot?: boolean | null;\n}\n\n/**\n * `POST /ai/agents/{agent_id}` body (`AgentStartRequest` server-side).\n * The conversation-start triple is required by construction; `organization_id`\n * is required (the server 422s a blank one — it never manufactures an org).\n * `stream` is not accepted here: this client is the streaming path and always\n * sends `stream: true`.\n */\nexport type MatrxAgentStartRequest = MatrxConversationStart &\n Omit<MatrxRequestScope, \"organization_id\"> &\n MatrxTurnFields & {\n organization_id: string;\n /** Variable name → value map filling the agent's declared variables. */\n variables?: MatrxJsonObject | null;\n /** Run the versions table row instead of the live agent row. */\n is_version?: boolean;\n max_iterations?: number;\n max_retries_per_iteration?: number;\n };\n\n/**\n * `POST /ai/mandates/{mandate_key}` uses the exact saved-agent start body.\n * The server resolves the mandate's holder, binding, and configuration; a\n * client must offer only the declared variables and human input here.\n */\nexport type MatrxMandateStartRequest = MatrxAgentStartRequest;\n\n/** `POST /ai/conversations/{id}` body (`ConversationContinueRequest` server-side). */\nexport type MatrxConversationContinueRequest = MatrxRequestScope &\n MatrxTurnFields & {\n /** Re-run the conversation's current persisted state (recovery after a failed turn); omit `user_input`. */\n retry?: boolean;\n };\n\n/**\n * `POST /ai/conversations/{id}/resume` body (`ResumeRequest` server-side) —\n * the shipped durable continuation after client-delegated tool calls were\n * answered via `POST /tool_results` while the original stream was gone.\n * `user_request_id` is optional: when omitted the server resolves the turn\n * from the conversation's newest answered client-delegated tool call.\n * Re-send fresh `context` here — a resumed loop is otherwise context-blind.\n */\nexport type MatrxConversationResumeRequest = MatrxRequestScope & {\n user_request_id?: string | null;\n config_overrides?: MatrxJsonObject | null;\n debug?: boolean;\n tools?: MatrxJsonObject[];\n tools_replace?: MatrxJsonObject[] | null;\n client?: MatrxJsonObject | null;\n user?: MatrxJsonObject | null;\n context?: MatrxJsonObject;\n writable_variables?: string[];\n allow_context_create?: boolean;\n};\n\n/** `POST /ai/cancel/{request_id}` response (`CancelResponse` server-side). */\nexport interface MatrxCancelResponse {\n status: string;\n request_id: string;\n spine_executions_signalled: string[];\n}\n\n// ─── The run handle ─────────────────────────────────────────────────────────\n// `MatrxStreamCallOptions`, `MatrxRunHandle`, and the handle constructor live\n// in ./internal so `./operations`' rejoin shares the exact same construction.\n\nasync function streamCall(\n transport: MatrxTransport,\n path: string,\n body: object,\n options: MatrxStreamCallOptions,\n): Promise<MatrxRunHandle> {\n const response = await requestStream(transport, path, {\n method: \"POST\",\n // This client IS the streaming path — `stream: true` always, last so a\n // caller-supplied value can never flip the response off NDJSON.\n body: { ...body, stream: true },\n ...(options.signal ? { signal: options.signal } : {}),\n });\n return toRunHandle(response, options);\n}\n\n// ─── Lifecycle calls ────────────────────────────────────────────────────────\n\n/** Start an agent run: `POST /ai/agents/{agent_id}` (NDJSON stream). */\nexport function startAgentRun(\n transport: MatrxTransport,\n agentId: string,\n request: MatrxAgentStartRequest,\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n return streamCall(\n transport,\n `/ai/agents/${encodePathSegment(agentId)}`,\n request,\n options,\n );\n}\n\n/**\n * Start a declared mandate: `POST /ai/mandates/{mandate_key}` (NDJSON\n * stream). This is deliberately separate from `startAgentRun`: callers name\n * the mandate and never resolve or echo its holder/configuration themselves.\n */\nexport function startMandateRun(\n transport: MatrxTransport,\n mandateKey: string,\n request: MatrxMandateStartRequest,\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n return streamCall(\n transport,\n `/ai/mandates/${encodePathSegment(mandateKey)}`,\n request,\n options,\n );\n}\n\n/** Continue a stored conversation: `POST /ai/conversations/{id}` (NDJSON stream). */\nexport function continueAgentConversation(\n transport: MatrxTransport,\n conversationId: string,\n request: MatrxConversationContinueRequest,\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n return streamCall(\n transport,\n `/ai/conversations/${encodePathSegment(conversationId)}`,\n request,\n options,\n );\n}\n\n/**\n * Resume a suspended loop after delegated tool answers landed:\n * `POST /ai/conversations/{id}/resume` (NDJSON stream). A 409\n * (`resume_conflict`) means another resume holds the run claim — retrying is\n * host policy.\n */\nexport function resumeAgentConversation(\n transport: MatrxTransport,\n conversationId: string,\n request: MatrxConversationResumeRequest = {},\n options: MatrxStreamCallOptions = {},\n): Promise<MatrxRunHandle> {\n return streamCall(\n transport,\n `/ai/conversations/${encodePathSegment(conversationId)}/resume`,\n request,\n options,\n );\n}\n\n/**\n * Stop a running request at its next iteration boundary:\n * `POST /ai/cancel/{request_id}`. Cooperative and best-effort — the in-flight\n * provider call finishes by design, and everything already streamed persists.\n * `mode: \"interrupt\"` = stop-and-fork: the tail after the last clean boundary\n * persists hidden so the user's follow-up replies to what they actually saw.\n * The id must be the server's `X-Request-ID`.\n */\nexport function cancelAgentRun(\n transport: MatrxTransport,\n requestId: string,\n options: { mode?: \"cancel\" | \"interrupt\"; signal?: AbortSignal } = {},\n): Promise<MatrxCancelResponse> {\n const query = buildQuery(\n options.mode === \"interrupt\" ? { mode: \"interrupt\" } : {},\n );\n return requestJson<MatrxCancelResponse>(\n transport,\n `/ai/cancel/${encodePathSegment(requestId)}${query}`,\n {\n method: \"POST\",\n ...(options.signal ? { signal: options.signal } : {}),\n },\n );\n}\n\n// ─── End-to-end convenience: run to completion ──────────────────────────────\n\n/**\n * A run that terminated unsuccessfully: the server emitted a fatal `error`\n * event, or the `user_request` completion settled `failed`/`cancelled`.\n */\nexport class MatrxRunError extends Error {\n override readonly name = \"MatrxRunError\";\n /** The verbatim `error` event payload, when one fired. */\n readonly errorPayload: Record<string, unknown> | null;\n /** The `user_request` completion status (`\"failed\"` | `\"cancelled\"`), when that was the trigger. */\n readonly completionStatus: string | null;\n /** Text streamed before the failure — partial content never vanishes. */\n readonly partialText: string;\n\n constructor(args: {\n message: string;\n errorPayload?: Record<string, unknown> | null;\n completionStatus?: string | null;\n partialText?: string;\n }) {\n super(args.message);\n this.errorPayload = args.errorPayload ?? null;\n this.completionStatus = args.completionStatus ?? null;\n this.partialText = args.partialText ?? \"\";\n }\n}\n\nexport interface MatrxCompletedRun {\n /** Accumulated `chunk` text (falls back to the completion's `result.output`). */\n text: string;\n requestId: string | null;\n conversationId: string | null;\n /** The `user_request` completion payload, verbatim, when one arrived. */\n completion: Record<string, unknown> | null;\n}\n\nexport interface RunAgentToCompletionOptions extends MatrxStreamCallOptions {\n /** Live progress: the full accumulated text after each chunk. */\n onChunk?: (fullText: string) => void;\n /** Every normalized envelope, before this helper interprets it. */\n onEvent?: (envelope: MatrxStreamEnvelope) => void;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringField(value: unknown, key: string): string | null {\n if (!isRecord(value)) return null;\n const field = value[key];\n return typeof field === \"string\" && field ? field : null;\n}\n\n/**\n * Run an agent end-to-end and resolve with its full text output — the\n * package-level equivalent of the simplest existing host path\n * (`useRunAgent`): accumulate `chunk` text, treat a fatal `error` event or a\n * `failed`/`cancelled` `user_request` completion as a thrown `MatrxRunError`,\n * and fall back to the completion's `result.output` when no text streamed.\n *\n * The caller still owns the conversation-start triple on `request` — a\n * one-shot run typically uses `newEphemeralConversationStart()`.\n */\nexport async function runAgentToCompletion(\n transport: MatrxTransport,\n agentId: string,\n request: MatrxAgentStartRequest,\n options: RunAgentToCompletionOptions = {},\n): Promise<MatrxCompletedRun> {\n const handle = await startAgentRun(transport, agentId, request, options);\n\n let text = \"\";\n let completion: Record<string, unknown> | null = null;\n let failure: MatrxRunError | null = null;\n\n for await (const envelope of handle.events) {\n options.onEvent?.(envelope);\n\n if (envelope.event === \"chunk\") {\n const chunk = stringField(envelope.data, \"text\");\n if (chunk !== null) {\n text += chunk;\n options.onChunk?.(text);\n }\n continue;\n }\n\n if (envelope.event === \"error\" && failure === null) {\n const payload = isRecord(envelope.data) ? envelope.data : null;\n failure = new MatrxRunError({\n message: streamErrorText(payload) ?? \"The agent run failed\",\n errorPayload: payload,\n partialText: text,\n });\n continue;\n }\n\n if (envelope.event !== \"completion\" || !isRecord(envelope.data)) continue;\n // `init`/`completion` pairs exist for five operations; the terminal\n // outcome of the run is the `user_request` completion (STREAM-CONTRACT\n // §3.2 — always present, always last).\n if (envelope.data.operation !== \"user_request\") continue;\n\n completion = envelope.data;\n const status = envelope.data.status;\n if ((status === \"failed\" || status === \"cancelled\") && failure === null) {\n const result = isRecord(envelope.data.result) ? envelope.data.result : null;\n failure = new MatrxRunError({\n message:\n stringField(result, \"error\") ??\n stringField(result, \"user_message\") ??\n `The agent run ${status}`,\n completionStatus: status,\n partialText: text,\n });\n }\n }\n\n if (failure) throw failure;\n\n if (!text && completion) {\n const result = completion.result;\n const output = stringField(result, \"output\");\n if (output !== null) text = output;\n }\n\n return {\n text,\n requestId: handle.requestId,\n conversationId: handle.conversationId,\n completion,\n };\n}\n","/**\n * `useFollowRuntimeOperation` — the reconnect follower as a hook: follow one\n * runtime operation's durable lifecycle stream\n * (`GET /runtime/executions/{id}/events/stream`, `@ai-matrx/agents/stream/sse`\n * under the hood) with the FULL production reconnect policy built in —\n * stall watchdog, bounded retry budget, `Last-Event-ID` cursor — via\n * `followRuntimeOperationToEnd`. Tuning knobs are typed and default to the\n * proven production values (45s stall, 60 × 2s budget).\n *\n * The host injects the transport and what each event MEANS (`onEvent`);\n * everything else is package policy.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n followRuntimeOperationToEnd,\n type MatrxRuntimeExecutionStatus,\n type MatrxRuntimeOperationEvent,\n type MatrxTransport,\n} from \"../matrx/index\";\n\nexport interface UseFollowRuntimeOperationOptions {\n /** The transport (typically the production `createMatrxTransport`). */\n transport: MatrxTransport | (() => MatrxTransport);\n /** The execution to follow — null/undefined idles the hook. */\n executionId: string | null | undefined;\n /** Resume cursor — the operation view's `last_event_seq` (0 = from start). */\n lastEventSeq?: number;\n /** Gate — false tears the follow down (default true when an id is set). */\n enabled?: boolean;\n /** Stall watchdog, ms. Default 45_000. */\n stallTimeoutMs?: number;\n /** Consecutive-failure budget. Default 60. */\n reconnectLimit?: number;\n /** Delay between attempts, ms. Default 2_000. */\n reconnectDelayMs?: number;\n /** Fired per durable spine event (lifecycle transitions + notes). */\n onEvent?: (event: MatrxRuntimeOperationEvent, seq: number | null) => void;\n /** Fired once when the follow settles (server `end`, exhausted budget, or teardown). */\n onSettled?: (result: {\n ended: boolean;\n status: MatrxRuntimeExecutionStatus | null;\n }) => void;\n}\n\nexport interface UseFollowRuntimeOperation {\n /** True while a follow loop is live for the current execution. */\n following: boolean;\n /** True once the server sent the terminal `end` frame. */\n ended: boolean;\n /** The root status from the `end` frame, when ended. */\n status: MatrxRuntimeExecutionStatus | null;\n}\n\nexport function useFollowRuntimeOperation(\n options: UseFollowRuntimeOperationOptions,\n): UseFollowRuntimeOperation {\n const [following, setFollowing] = useState(false);\n const [ended, setEnded] = useState(false);\n const [status, setStatus] = useState<MatrxRuntimeExecutionStatus | null>(\n null,\n );\n\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const { executionId, enabled = true } = options;\n const active = enabled && !!executionId;\n\n const follow = useCallback(\n (id: string, signal: AbortSignal, isCurrent: () => boolean) => {\n const opts = optionsRef.current;\n const transport =\n typeof opts.transport === \"function\"\n ? opts.transport()\n : opts.transport;\n return followRuntimeOperationToEnd(transport, id, {\n signal,\n ...(opts.lastEventSeq !== undefined\n ? { lastEventSeq: opts.lastEventSeq }\n : {}),\n ...(opts.stallTimeoutMs !== undefined\n ? { stallTimeoutMs: opts.stallTimeoutMs }\n : {}),\n ...(opts.reconnectLimit !== undefined\n ? { reconnectLimit: opts.reconnectLimit }\n : {}),\n ...(opts.reconnectDelayMs !== undefined\n ? { reconnectDelayMs: opts.reconnectDelayMs }\n : {}),\n onEvent: (event, seq) => {\n if (isCurrent()) optionsRef.current.onEvent?.(event, seq);\n },\n });\n },\n [],\n );\n\n useEffect(() => {\n if (!active || !executionId) return;\n let current = true;\n const controller = new AbortController();\n setFollowing(true);\n setEnded(false);\n setStatus(null);\n\n void follow(executionId, controller.signal, () => current)\n .then((result) => {\n if (!current) return;\n setFollowing(false);\n setEnded(result.ended);\n setStatus(result.status);\n optionsRef.current.onSettled?.(result);\n })\n .catch(() => {\n if (!current) return;\n setFollowing(false);\n optionsRef.current.onSettled?.({ ended: false, status: null });\n });\n\n return () => {\n current = false;\n controller.abort();\n setFollowing(false);\n };\n }, [active, executionId, follow]);\n\n return { following, ended, status };\n}\n"],"mappings":";;;AAcA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;;;ACuDzD,IAAM,WAAW,CAAC,UAChB,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAC9D,QACD,CAAC;AAEP,IAAM,WAAW,CAAC,UAChB,OAAO,UAAU,WAAW,QAAQ;AAEtC,IAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AAEzD,SAAS,6BAA6B,OAGlB;AACzB,SAAO;AAAA,IACL,WAAW,MAAM;AAAA,IACjB,gBAAgB,MAAM,kBAAkB;AAAA,IACxC,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,cAAc,CAAC;AAAA,IACf,YAAY,CAAC;AAAA,IACb,OAAO,CAAC;AAAA,IACR,cAAc,CAAC;AAAA,IACf,kBAAkB,CAAC;AAAA,IACnB,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,YAAY;AAAA,EACd;AACF;AAEA,SAAS,WAAW,OAA8C;AAChE,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,kBACd,SACA,OACwB;AACxB,QAAM,YAAY,SAAS,MAAM,UAAU;AAC3C,QAAM,WAAW,SAAS,MAAM,SAAS;AACzC,QAAM,cAAc,aAAa,QAAQ,aAAa,QAAQ;AAC9D,MACE,eACA,cAAc,QACd,aAAa,QAAQ,kBACrB;AACA,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,SAAS,MAAM,IAAI;AAChC,QAAM,OAA+B;AAAA,IACnC,GAAG;AAAA,IACH,QAAQ,QAAQ,WAAW,YAAY,cAAc,QAAQ;AAAA,IAC7D,kBACE,cAAc,OACV,QAAQ,mBACR,cACE,KAAK,IAAI,QAAQ,kBAAkB,SAAS,IAC5C;AAAA,IACR,mBAAmB,YAAY,QAAQ;AAAA,IACvC,YAAY,QAAQ,aAAa;AAAA,EACnC;AAEA,UAAQ,MAAM,OAAO;AAAA,IACnB,KAAK,SAAS;AACZ,YAAM,OAAO,SAAS,KAAK,IAAI;AAC/B,aAAO,SAAS,OAAO,OAAO,EAAE,GAAG,MAAM,QAAQ,QAAQ,SAAS,KAAK;AAAA,IACzE;AAAA,IACA,KAAK,mBAAmB;AACtB,YAAM,OAAO,SAAS,KAAK,IAAI;AAC/B,aAAO,SAAS,OACZ,OACA;AAAA,QACE,GAAG;AAAA,QACH,WAAW,QAAQ,YAAY;AAAA,QAC/B,iBAAiB;AAAA,MACnB;AAAA,IACN;AAAA,IACA,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,iBAAiB,KAAK,UAAU;AAAA,MAClC;AAAA,IACF,KAAK,SAAS;AACZ,YAAM,QAAQ,SAAS,KAAK,KAAK;AACjC,UAAI,UAAU,KAAM,QAAO;AAC3B,aAAO;AAAA,QACL,GAAG;AAAA,QACH;AAAA,QACA,cAAc,CAAC,GAAG,QAAQ,cAAc,KAAK;AAAA,MAC/C;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,cAAc,SAAS,KAAK,YAAY;AAC9C,YAAM,YAAY,SAAS,KAAK,SAAS;AACzC,UAAI,gBAAgB,QAAQ,cAAc,KAAM,QAAO;AACvD,aAAO;AAAA,QACL,GAAG;AAAA,QACH,YAAY;AAAA,UACV,GAAG,QAAQ;AAAA,UACX,CAAC,WAAW,GAAG;AAAA,YACb;AAAA,YACA;AAAA,YACA,mBAAmB,SAAS,KAAK,mBAAmB;AAAA,YACpD,QAAQ;AAAA,YACR,UAAU,OAAO,KAAK,SAAS,KAAK,QAAQ,CAAC,EAAE,SAC3C,SAAS,KAAK,QAAQ,IACtB;AAAA,YACJ,QAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,KAAK,cAAc;AACjB,YAAM,cAAc,SAAS,KAAK,YAAY;AAC9C,YAAM,YAAY,SAAS,KAAK,SAAS;AACzC,YAAM,YAAY,SAAS,KAAK,MAAM;AACtC,YAAM,SACJ,cAAc,YAAY,cAAc,cACpC,YACA;AACN,YAAM,SAAS,SAAS,KAAK,MAAM;AACnC,YAAM,aAAa,cACf;AAAA,QACE,GAAG,QAAQ;AAAA,QACX,CAAC,WAAW,GAAG;AAAA,UACb,GAAI,QAAQ,WAAW,WAAW,KAAK;AAAA,YACrC;AAAA,YACA,WAAW,aAAa;AAAA,YACxB,mBAAmB;AAAA,YACnB,UAAU;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF,IACA,QAAQ;AACZ,UAAI,cAAc,gBAAgB;AAChC,eAAO;AAAA,UACL,GAAG;AAAA,UACH;AAAA,UACA,YAAY;AAAA,UACZ,QACE,WAAW,YACP,aACA,WAAW,cACT,cACA;AAAA,QACV;AAAA,MACF;AACA,aAAO,EAAE,GAAG,MAAM,WAAW;AAAA,IAC/B;AAAA,IACA,KAAK,cAAc;AACjB,YAAM,SAAS,SAAS,KAAK,OAAO;AACpC,YAAM,WAAW,SAAS,KAAK,SAAS;AACxC,YAAM,YAAY,SAAS,KAAK,KAAK;AACrC,UAAI,WAAW,QAAQ,aAAa,QAAQ,cAAc,KAAM,QAAO;AACvE,YAAM,SAAS,WAAW,SAAS;AACnC,aAAO;AAAA,QACL,GAAG;AAAA,QACH,QACE,WAAW,cACP,mBACA,WAAW,eAAe,WAAW,UACnC,cACA,KAAK;AAAA,QACb,OAAO;AAAA,UACL,GAAG,QAAQ;AAAA,UACX,CAAC,MAAM,GAAG;AAAA,YACR;AAAA,YACA;AAAA,YACA;AAAA,YACA,SAAS,SAAS,KAAK,OAAO;AAAA,YAC9B,MAAM,OAAO,KAAK,SAAS,KAAK,IAAI,CAAC,EAAE,SAAS,SAAS,KAAK,IAAI,IAAI;AAAA,UACxE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,KAAK,gBAAgB;AACnB,YAAM,UAAU,SAAS,KAAK,OAAO;AACrC,YAAM,aAAa,SAAS,KAAK,UAAU;AAC3C,YAAM,OAAO,SAAS,KAAK,IAAI;AAC/B,UAAI,YAAY,QAAQ,eAAe,QAAQ,SAAS,KAAM,QAAO;AACrE,YAAM,eAAe,OAAO,OAAO,QAAQ,cAAc,OAAO;AAChE,aAAO;AAAA,QACL,GAAG;AAAA,QACH,cAAc;AAAA,UACZ,GAAG,QAAQ;AAAA,UACX,CAAC,OAAO,GAAG;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA,QACE,KAAK,WAAW,cAAc,KAAK,WAAW,UAC1C,KAAK,SACL;AAAA,YACN,SAAS,SAAS,KAAK,OAAO;AAAA,YAC9B,MAAM,OAAO,KAAK,SAAS,KAAK,IAAI,CAAC,EAAE,SAAS,SAAS,KAAK,IAAI,IAAI;AAAA,YACtE,UAAU,OAAO,KAAK,SAAS,KAAK,QAAQ,CAAC,EAAE,SAC3C,SAAS,KAAK,QAAQ,IACtB;AAAA,UACN;AAAA,QACF;AAAA,QACA,kBAAkB,eACd,QAAQ,mBACR,CAAC,GAAG,QAAQ,kBAAkB,OAAO;AAAA,MAC3C;AAAA,IACF;AAAA,IACA,KAAK;AACH,aAAO,EAAE,GAAG,MAAM,QAAQ,SAAS,OAAO,KAAK;AAAA,IACjD,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,QACE,QAAQ,WAAW,WAAW,QAAQ,WAAW,cAC7C,QAAQ,SACR;AAAA,QACN,iBAAiB;AAAA,MACnB;AAAA,IACF,KAAK,QAAQ;AACX,YAAM,iBACJ,KAAK,SAAS,oBAAoB,SAAS,KAAK,eAAe,IAAI;AACrE,aAAO,mBAAmB,OAAO,OAAO,EAAE,GAAG,MAAM,eAAe;AAAA,IACpE;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;;;AChQO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACrB,OAAO;AAAA;AAAA,EAEhB;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,MAKT;AACD;AAAA,MACE,KAAK,WACH,yBAAyB,KAAK,YAAY,KAC1C,QAAQ,KAAK,MAAM;AAAA,IACvB;AACA,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,eAAe,KAAK;AACzB,SAAK,OAAO,sBAAsB,KAAK,YAAY;AAAA,EACrD;AACF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,eAAe,OAAoC;AAC1D,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAC7D;AAaO,SAAS,yBACd,cACoB;AACpB,MAAI,CAAC,SAAS,YAAY,EAAG,QAAO;AAEpC,QAAM,cAAc,eAAe,aAAa,YAAY;AAC5D,MAAI,YAAa,QAAO;AACxB,QAAM,UAAU,eAAe,aAAa,OAAO;AACnD,MAAI,QAAS,QAAO;AAEpB,MAAI,MAAM,QAAQ,aAAa,OAAO,GAAG;AACvC,UAAM,WAAW,aAAa,QAC3B,IAAI,CAAC,UAAmB;AACvB,UAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,YAAM,gBAAgB,eAAe,MAAM,OAAO;AAClD,UAAI,CAAC,cAAe,QAAO;AAC3B,YAAM,QAAQ,eAAe,MAAM,KAAK;AACxC,aAAO,QAAQ,GAAG,KAAK,KAAK,aAAa,KAAK;AAAA,IAChD,CAAC,EACA,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AACnD,QAAI,SAAS,SAAS,EAAG,QAAO,SAAS,KAAK,IAAI;AAAA,EACpD;AAEA,QAAM,SAAS,aAAa;AAC5B,MAAI,SAAS,MAAM,GAAG;AACpB,UAAM,gBACJ,eAAe,OAAO,OAAO,KAAK,eAAe,OAAO,YAAY;AACtE,QAAI,cAAe,QAAO;AAAA,EAC5B;AACA,MAAI,OAAO,WAAW,YAAY,OAAO,KAAK,EAAG,QAAO;AACxD,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,UAAM,WAAW,OACd;AAAA,MAAI,CAAC,UACJ,SAAS,KAAK,IAAI,eAAe,MAAM,GAAG,IAAI;AAAA,IAChD,EACC,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AACnD,QAAI,SAAS,SAAS,EAAG,QAAO,SAAS,KAAK,IAAI;AAAA,EACpD;AACA,SAAO;AACT;AAMO,SAAS,sBAAsB,cAAsC;AAC1E,MAAI,CAAC,SAAS,YAAY,EAAG,QAAO;AACpC,QAAM,WAAW,eAAe,aAAa,IAAI;AACjD,MAAI,SAAU,QAAO;AACrB,QAAM,SAAS,aAAa;AAC5B,MAAI,SAAS,MAAM,GAAG;AACpB,UAAM,SAAS,eAAe,OAAO,IAAI;AACzC,QAAI,OAAQ,QAAO;AAAA,EACrB;AACA,SAAO;AACT;;;AC1HA,SAAS,kBAAwC;AACjD,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;;;ACmB7B,IAAM,kBAAN,cAA8B,MAAM;AAAA;AAAA,EAEhC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,MAOT;AACD,UAAM,KAAK,WAAW;AACtB,SAAK,OAAO;AACZ,SAAK,OAAO,KAAK;AACjB,SAAK,SAAS,KAAK;AACnB,SAAK,cAAc,KAAK;AACxB,SAAK,UAAU,KAAK,WAAW;AAG/B,SAAK,YAAY,KAAK,aAAa;AACnC,SAAK,SAAS,KAAK,UAAU;AAAA,EAC/B;AAAA;AAAA,EAGA,SAA8B;AAC5B,WAAO;AAAA,MACL,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK;AAAA,MACd,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,YAAY,KAAK;AAAA,IACnB;AAAA,EACF;AACF;;;ACzCO,IAAM,kCAAkC;AAE/C,SAASA,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AASO,SAAS,6BACd,OAC4B;AAC5B,MAAI,CAACA,UAAS,KAAK,EAAG,QAAO;AAE7B,MAAI,OAAO,MAAM,UAAU,UAAU;AACnC,UAAM,YACJ,OAAO,MAAM,eAAe,YAAY,OAAO,SAAS,MAAM,UAAU,IACpE,MAAM,aACN;AACN,UAAM,WAAW,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AACzE,WAAO;AAAA,MACL,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,MACZ,GAAI,aAAa,SAAY,CAAC,IAAI,EAAE,WAAW,SAAS;AAAA,MACxD,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,YAAY,UAAU;AAAA,IAC7D;AAAA,EACF;AACA,MAAI,MAAM,MAAM,OAAO,OAAO,MAAM,MAAM,UAAU;AAClD,UAAM,WAAW,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AACzE,UAAM,YACJ,OAAO,MAAM,eAAe,YAAY,OAAO,SAAS,MAAM,UAAU,IACpE,MAAM,aACN;AACN,WAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM,EAAE,MAAM,MAAM,EAAE;AAAA,MACtB,GAAI,aAAa,SAAY,CAAC,IAAI,EAAE,WAAW,SAAS;AAAA,MACxD,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,YAAY,UAAU;AAAA,IAC7D;AAAA,EACF;AACA,MAAI,MAAM,MAAM,OAAO,OAAO,MAAM,MAAM,UAAU;AAClD,UAAM,WAAW,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AACzE,UAAM,YACJ,OAAO,MAAM,eAAe,YAAY,OAAO,SAAS,MAAM,UAAU,IACpE,MAAM,aACN;AACN,WAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM,EAAE,MAAM,MAAM,EAAE;AAAA,MACtB,GAAI,aAAa,SAAY,CAAC,IAAI,EAAE,WAAW,SAAS;AAAA,MACxD,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,YAAY,UAAU;AAAA,IAC7D;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,wBACd,UAAoC,CAAC,GAClB;AACnB,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AACb,MAAI,aAAa;AACjB,MAAI,WAAW;AAEf,QAAM,aAAa,MAAY;AAC7B,QAAI,SAAU,OAAM,IAAI,MAAM,yCAAyC;AAAA,EACzE;AAEA,QAAM,YAAY,CAChB,MACA,iBAC+B;AAC/B,UAAM,oBAAoB,EAAE;AAC5B,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS,QAAO;AAErB,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,OAAO;AAAA,IAC7B,SAAS,OAAO;AACd,cAAQ,kBAAkB;AAAA,QACxB,MAAM;AAAA,QACN;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,MACF,CAAC;AACD,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,6BAA6B,MAAM;AACpD,QAAI,CAAC,UAAU;AACb,cAAQ,oBAAoB,MAAM;AAClC,aAAO;AAAA,IACT;AACA,YAAQ,kBAAkB;AAAA,MACxB,KAAK;AAAA,MACL;AAAA,MACA,MAAM;AAAA,MACN,YAAY;AAAA,MACZ;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,CAAC,aAA4C;AACnE,cAAU;AACV,UAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,aAAS,MAAM,IAAI,KAAK;AACxB,UAAM,YAAmC,CAAC;AAC1C,eAAW,QAAQ,OAAO;AACxB,YAAM,WAAW,UAAU,MAAM,KAAK;AACtC,UAAI,SAAU,WAAU,KAAK,QAAQ;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,SAAS,UAAU;AACjB,iBAAW;AAEX,aAAO,gBAAgB,QAAQ,OAAO,IAAI,QAAQ;AAAA,IACpD;AAAA,IACA,UAAU,UAAU;AAClB,iBAAW;AACX,aAAO,gBAAgB,QAAQ,OAAO,UAAU,EAAE,QAAQ,KAAK,CAAC,CAAC;AAAA,IACnE;AAAA,IACA,SAAS;AACP,iBAAW;AACX,iBAAW;AACX,YAAM,YAAY,gBAAgB,QAAQ,OAAO,CAAC;AAClD,UAAI,OAAO,SAAS,GAAG;AACrB,cAAM,WAAW,UAAU,QAAQ,IAAI;AACvC,iBAAS;AACT,YAAI,SAAU,WAAU,KAAK,QAAQ;AAAA,MACvC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,eAAe,OAAmC;AACzD,QAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,GAAG;AAC7C,UAAM,IAAI,WAAW,8CAA8C;AAAA,EACrE;AACA,SAAO;AACT;AAQA,gBAAuB,sBACrB,MACA,UAAkC,CAAC,GACmB;AACtD,QAAM,eAAe,eAAe,QAAQ,YAAY;AACxD,QAAM,QAAqB,CAAC;AAC5B,MAAI,mBAAmB;AACvB,MAAI,eAAoC;AACxC,MAAI,eAAoC;AACxC,MAAI,iBAAiB;AACrB,MAAI,iBAAiB;AAErB,QAAM,sBAAsB,MAAY;AACtC,UAAM,OAAO;AACb,mBAAe;AACf,WAAO;AAAA,EACT;AACA,QAAM,sBAAsB,MAAY;AACtC,UAAM,OAAO;AACb,mBAAe;AACf,WAAO;AAAA,EACT;AACA,QAAM,kBAAkB,CAAC,SAA0B;AACjD,QAAI,eAAgB;AACpB,UAAM,KAAK,IAAI;AACf,wBAAoB;AAAA,EACtB;AACA,QAAM,eAAe,OAAO,UAAiD;AAC3E,WACE,oBAAoB,gBACpB,CAAC,kBACD,CAAC,QAAQ,QAAQ,SACjB;AACA,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,uBAAe;AAAA,MACjB,CAAC;AAAA,IACH;AACA,QAAI,kBAAkB,QAAQ,QAAQ,QAAS,QAAO;AACtD,UAAM,KAAK,EAAE,MAAM,SAAS,MAAM,CAAC;AACnC,wBAAoB;AACpB,wBAAoB;AACpB,WAAO;AAAA,EACT;AACA,QAAM,sBAAsB,YAA8B;AACxD,WACE,oBAAoB,gBACpB,CAAC,kBACD,CAAC,QAAQ,QAAQ,SACjB;AACA,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,uBAAe;AAAA,MACjB,CAAC;AAAA,IACH;AACA,WAAO,CAAC,kBAAkB,CAAC,QAAQ,QAAQ;AAAA,EAC7C;AAEA,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,SAAS,wBAAwB,OAAO;AAE9C,QAAM,UAAU,MAAY;AAC1B,wBAAoB;AACpB,wBAAoB;AACpB,SAAK,OAAO,OAAO,QAAQ,QAAQ,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,EAClE;AACA,UAAQ,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACjE,MAAI,QAAQ,QAAQ,QAAS,SAAQ;AAErC,QAAM,iBAAiB,YAA2B;AAChD,QAAI;AACF,aAAO,CAAC,QAAQ,QAAQ,WAAW,CAAC,gBAAgB;AAClD,YAAI,CAAE,MAAM,oBAAoB,EAAI;AACpC,cAAM,EAAE,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AACV,mBAAW,YAAY,OAAO,UAAU,KAAK,GAAG;AAC9C,cAAI,CAAE,MAAM,aAAa,QAAQ,EAAI;AAAA,QACvC;AAAA,MACF;AAEA,UAAI,CAAC,QAAQ,QAAQ,WAAW,CAAC,gBAAgB;AAC/C,mBAAW,YAAY,OAAO,OAAO,GAAG;AACtC,cAAI,CAAE,MAAM,aAAa,QAAQ,EAAI;AAAA,QACvC;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,YAAM,UACJ,QAAQ,QAAQ,WAChB,kBACC,iBAAiB,SAAS,MAAM,SAAS;AAC5C,UAAI,CAAC,QAAS,iBAAgB,EAAE,MAAM,SAAS,MAAM,CAAC;AAAA,IACxD,UAAE;AACA,uBAAiB;AACjB,aAAO,YAAY;AACnB,sBAAgB,EAAE,MAAM,OAAO,CAAC;AAAA,IAClC;AAAA,EACF,GAAG;AAEH,MAAI;AACF,WAAO,MAAM;AACX,UAAI,MAAM,WAAW,GAAG;AACtB,YAAI,QAAQ,QAAQ,WAAW,eAAgB;AAC/C,cAAM,IAAI,QAAc,CAAC,YAAY;AACnC,yBAAe;AAAA,QACjB,CAAC;AAAA,MACH;AAEA,YAAM,OAAO,MAAM,MAAM;AACzB,UAAI,MAAM,SAAS,QAAS,qBAAoB;AAChD,0BAAoB;AACpB,UAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ;AACnC,UAAI,KAAK,SAAS,QAAS,OAAM,KAAK;AACtC,YAAM,KAAK;AAAA,IACb;AAAA,EACF,UAAE;AACA,qBAAiB;AACjB,wBAAoB;AACpB,wBAAoB;AACpB,YAAQ,QAAQ,oBAAoB,SAAS,OAAO;AACpD,QAAI,CAAC,gBAAgB;AACnB,YAAM,OAAO,OAAO,EAAE,MAAM,MAAM,MAAS;AAAA,IAC7C;AACA,UAAM;AAAA,EACR;AACF;;;AC/VA,SAAS,mBAAmB;AAoBrB,IAAM,2BAAN,cAAuC,MAAM;AAAA,EACzC;AAAA,EAET,YAAY,MAAoC,SAAiB;AAC/D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;;;ACuKA,IAAM,sBACJ;AAGK,SAAS,oBAAoB,MAA0C;AAC5E,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,oBAAoB,KAAK,IAAI;AACtC;AAOO,SAAS,mBAAmB,QAAwB;AACzD,MAAI,WAAW,OAAO,WAAW,KAAK;AACpC,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,MAAI,UAAU,KAAK;AACjB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGO,SAAS,uBACd,KACA,QACQ;AACR,MAAI,OAAO,CAAC,oBAAoB,GAAG,EAAG,QAAO;AAC7C,SAAO,mBAAmB,UAAU,CAAC;AACvC;;;AJnIA,SAAS,eAAe,QAAmD;AACzE,SAAO,UAAU,OAAO,SAAS,MAAM,qBAAqB;AAC9D;AAeO,SAAS,oBAAoB,KAA8B;AAChE,MAAI,eAAe,0BAA0B;AAC3C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,IAAI;AAAA,MACb,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,GAAI,IAAI,UAAU,SAAY,EAAE,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,IACxD;AAAA,EACF;AAEA,MAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,WAAO,EAAE,MAAM,eAAe,SAAS,yBAAyB;AAAA,EAClE;AAEA,MAAI,eAAe,eAAe;AAChC,WAAO;AAAA,MACL,MAAM,eAAe,IAAI,MAAM;AAAA,MAC/B,SAAS,uBAAuB,IAAI,SAAS,IAAI,MAAM;AAAA,MACvD,QAAQ,IAAI;AAAA,MACZ,cAAc,IAAI;AAAA,MAClB,GAAI,IAAI,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAAA,MACrC,MAAM,IAAI;AAAA,IACZ;AAAA,EACF;AAEA,MAAI,eAAe,iBAAiB;AAClC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,QACP,IAAI,UAAU,IAAI;AAAA,QAClB,IAAI,UAAU;AAAA,MAChB;AAAA,MACA,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,GAAI,IAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,OAAO,IAAI,CAAC;AAAA,MACpD,GAAI,IAAI,YAAY,OAAO,EAAE,cAAc,IAAI,QAAQ,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AAEA,MAAI,WAAW,GAAG,GAAG;AACnB,QAAI,IAAI,SAAS,WAAW;AAC1B,aAAO,EAAE,MAAM,eAAe,SAAS,IAAI,QAAQ;AAAA,IACrD;AACA,QAAI,IAAI,SAAS,QAAQ;AACvB,YAAM,SAAS,IAAI,UAAU;AAC7B,aAAO;AAAA,QACL,MAAM,eAAe,MAAM;AAAA,QAC3B,SAAS,uBAAuB,IAAI,SAAS,MAAM;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,iBAAiB,SAAS,IAAI,QAAQ;AAAA,EACvD;AAEA,MAAI,eAAe,OAAO;AACxB,UAAM,YAAY,IAAI,QAAQ,MAAM,oBAAoB;AACxD,QAAI,WAAW;AACb,YAAM,SAAS,SAAS,UAAU,CAAC,GAAa,EAAE;AAClD,aAAO;AAAA,QACL,MAAM,eAAe,MAAM;AAAA,QAC3B,SAAS,uBAAuB,UAAU,CAAC,KAAK,IAAI,SAAS,MAAM;AAAA,QACnE;AAAA,MACF;AAAA,IACF;AAEA,QAAI,oBAAoB,IAAI,OAAO,GAAG;AACpC,YAAM,SAAS,OAAO,SAAS,IAAI,QAAQ,QAAQ,QAAQ,EAAE,GAAG,EAAE;AAClE,aAAO;AAAA,QACL,MAAM,eAAe,MAAM;AAAA,QAC3B,SAAS,mBAAmB,MAAM;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,MAAM,iBAAiB,SAAS,IAAI,QAAQ;AAAA,EACvD;AAEA,SAAO,EAAE,MAAM,WAAW,SAAS,oBAAoB,GAAG,EAAE;AAC9D;;;AKtLO,SAAS,kBAAkB,OAAuB;AACvD,SAAO,mBAAmB,KAAK;AACjC;AAcO,SAAS,WAAW,QAA4C;AACrE,QAAM,SAAS,IAAI,gBAAgB;AACnC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,UAAU,OAAW;AACzB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,SAAS,MAAO,QAAO,OAAO,KAAK,KAAK;AAAA,IACrD,OAAO;AACL,aAAO,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,IAClC;AAAA,EACF;AACA,QAAM,UAAU,OAAO,SAAS;AAChC,SAAO,UAAU,IAAI,OAAO,KAAK;AACnC;AAEA,eAAe,iBAAiB,UAAsC;AACpE,MAAI;AACF,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,cAAc,MAAc,UAAoC;AAC7E,QAAM,IAAI,cAAc;AAAA,IACtB,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA,cAAc,MAAM,iBAAiB,QAAQ;AAAA,EAC/C,CAAC;AACH;AAYA,eAAsB,YACpB,WACA,MACA,SACY;AACZ,QAAM,UAAU,QAAQ,WAAW,SAAS,QAAQ,SAAS;AAC7D,QAAM,WAAW,MAAM,UAAU,MAAM,MAAM;AAAA,IAC3C,QAAQ,QAAQ;AAAA,IAChB,SAAS,UAAU,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,IAC7D,GAAI,UAAU,EAAE,MAAM,KAAK,UAAU,QAAQ,IAAI,EAAE,IAAI,CAAC;AAAA,IACxD,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,EACrD,CAAC;AACD,MAAI,CAAC,SAAS,GAAI,QAAO,cAAc,MAAM,QAAQ;AACrD,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAuCO,SAAS,YACd,UACA,SACgB;AAChB,SAAO;AAAA,IACL,WAAW,SAAS,QAAQ,IAAI,cAAc;AAAA,IAC9C,gBAAgB,SAAS,QAAQ,IAAI,mBAAmB;AAAA,IACxD,QAAQ,sBAAsB,SAAS,MAAoC;AAAA,MACzE,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,MACnD,GAAI,QAAQ,iBAAiB,SACzB,EAAE,cAAc,QAAQ,aAAa,IACrC,CAAC;AAAA,MACL,GAAI,QAAQ,kBACR,EAAE,iBAAiB,QAAQ,gBAAgB,IAC3C,CAAC;AAAA,MACL,GAAI,QAAQ,oBACR,EAAE,mBAAmB,QAAQ,kBAAkB,IAC/C,CAAC;AAAA,MACL,GAAI,QAAQ,kBACR,EAAE,iBAAiB,QAAQ,gBAAgB,IAC3C,CAAC;AAAA,IACP,CAAC;AAAA,IACD;AAAA,EACF;AACF;AAeA,eAAsB,cACpB,WACA,MACA,SACmB;AACnB,QAAM,UAAU,QAAQ,WAAW,SAAS,QAAQ,SAAS;AAC7D,QAAM,WAAW,MAAM,UAAU,MAAM,MAAM;AAAA,IAC3C,QAAQ,QAAQ;AAAA,IAChB,SAAS;AAAA,MACP,GAAI,UAAU,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,MACxD,GAAG,QAAQ;AAAA,IACb;AAAA,IACA,GAAI,UAAU,EAAE,MAAM,KAAK,UAAU,QAAQ,IAAI,EAAE,IAAI,CAAC;AAAA,IACxD,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,EACrD,CAAC;AACD,MAAI,CAAC,SAAS,GAAI,QAAO,cAAc,MAAM,QAAQ;AACrD,MAAI,CAAC,SAAS,MAAM;AAClB,UAAM,IAAI,cAAc;AAAA,MACtB,QAAQ,SAAS;AAAA,MACjB;AAAA,MACA,cAAc,EAAE,MAAM,wBAAwB;AAAA,MAC9C,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;AC9IA,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AAGhB,SAAS,mBAAmB,OAA8B;AAC/D,MAAI,QAAQ;AACZ,MAAI,KAAoB;AACxB,QAAM,YAAsB,CAAC;AAC7B,MAAI,UAAU;AAEd,aAAW,QAAQ,MAAM,MAAM,cAAc,GAAG;AAC9C,QAAI,KAAK,WAAW,GAAG,EAAG;AAC1B,QAAI,KAAK,WAAW,QAAQ,EAAG,SAAQ,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,aACjD,KAAK,WAAW,OAAO,GAAG;AACjC,gBAAU;AACV,gBAAU,KAAK,KAAK,MAAM,CAAC,EAAE,QAAQ,MAAM,EAAE,CAAC;AAAA,IAChD,WAAW,KAAK,WAAW,KAAK,EAAG,MAAK,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,EAC7D;AAEA,QAAM,eAAe,OAAO,QAAQ,OAAO,KAAK,OAAO,EAAE,IAAI;AAC7D,QAAM,MACJ,OAAO,cAAc,YAAY,KAAK,gBAAgB,IAClD,eACA;AAEN,SAAO,EAAE,OAAO,IAAI,KAAK,MAAM,UAAU,UAAU,KAAK,IAAI,IAAI,KAAK;AACvE;AAcO,SAAS,uBAAuC;AACrD,MAAI,SAAS;AACb,SAAO;AAAA,IACL,KAAK,OAAgC;AACnC,gBAAU;AACV,YAAM,SAA0B,CAAC;AACjC,iBAAS;AACP,cAAM,MAAM,gBAAgB,KAAK,MAAM;AACvC,YAAI,QAAQ,KAAM;AAIlB,cAAM,QAAQ,OAAO,MAAM,GAAG,IAAI,KAAK;AACvC,iBAAS,OAAO,MAAM,IAAI,QAAQ,IAAI,CAAC,EAAE,MAAM;AAC/C,eAAO,KAAK,mBAAmB,KAAK,CAAC;AAAA,MACvC;AACA,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AACN,YAAM,OAAO;AACb,eAAS;AACT,aAAO,EAAE,YAAY,KAAK,SAAS,IAAI,OAAO,KAAK;AAAA,IACrD;AAAA,EACF;AACF;AAgBA,gBAAuB,mBACrB,QACA,UAA+B,CAAC,GACgB;AAChD,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,SAAS,qBAAqB;AACpC,MAAI;AACF,eAAS;AACP,YAAM,EAAE,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AACV,YAAM,SAAS,OAAO,KAAK,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC;AAClE,iBAAW,SAAS,OAAQ,OAAM;AAAA,IACpC;AACA,UAAM,OAAO,OAAO,KAAK,QAAQ,OAAO,CAAC;AACzC,eAAW,SAAS,KAAM,OAAM;AAChC,UAAM,EAAE,WAAW,IAAI,OAAO,MAAM;AACpC,QAAI,eAAe,KAAM,SAAQ,eAAe,UAAU;AAAA,EAC5D,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;;;AC7FA,IAAM,mBAAwC,oBAAI,IAAI;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAuLD,SAAS,eAAe,MAAyD;AAC/E,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QACE,OAAO,WAAW,YAClB,WAAW,QACX,OAAQ,OAAgC,WAAW,UACnD;AACA,YAAM,SAAU,OAA8B;AAC9C,aAAO,iBAAiB,IAAI,MAAM,IAC7B,SACD;AAAA,IACN;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAYA,gBAAuB,6BACrB,WACA,aACA,UAAyC,CAAC,GACkB;AAC5D,MAAI,SAAS,QAAQ,gBAAgB;AAErC,QAAM,UAAkC,EAAE,QAAQ,oBAAoB;AACtE,MAAI,SAAS,EAAG,SAAQ,eAAe,IAAI,OAAO,MAAM;AAExD,QAAM,WAAW,MAAM;AAAA,IACrB;AAAA,IACA,uBAAuB,kBAAkB,WAAW,CAAC;AAAA,IACrD;AAAA,MACE,QAAQ;AAAA,MACR;AAAA,MACA,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,SAAS;AAAA,IACb,SAAS;AAAA,IACT,QAAQ,eAAe,EAAE,cAAc,QAAQ,aAAa,IAAI,CAAC;AAAA,EACnE;AAEA,mBAAiB,SAAS,QAAQ;AAChC,QAAI,MAAM,UAAU,OAAO;AACzB,YAAM,EAAE,MAAM,OAAO,QAAQ,eAAe,MAAM,IAAI,GAAG,OAAO;AAChE;AAAA,IACF;AACA,QAAI,MAAM,UAAU,qBAAqB,MAAM,SAAS,MAAM;AAC5D,UAAI;AACJ,UAAI;AACF,gBAAQ,KAAK,MAAM,MAAM,IAAI;AAAA,MAC/B,SAAS,OAAO;AACd,gBAAQ,mBAAmB,OAAO,KAAK;AACvC,cAAM,EAAE,MAAM,YAAY,OAAO;AACjC;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,QAAQ,MAAM,MAAM,OAAQ,UAAS,MAAM;AAC7D,YAAM,EAAE,MAAM,SAAS,OAAO,KAAK,MAAM,KAAK,OAAO;AACrD;AAAA,IACF;AAEA,UAAM,EAAE,MAAM,YAAY,OAAO;AAAA,EACnC;AACF;AAqDA,eAAsB,4BACpB,WACA,aACA,SAC4C;AAC5C,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,mBAAmB,QAAQ,oBAAoB;AACrD,QAAM,QAAQ,QAAQ;AAEtB,MAAI,SAAS,QAAQ,gBAAgB;AACrC,MAAI,WAAW;AAEf,SAAO,CAAC,OAAO,WAAW,WAAW,gBAAgB;AACnD,UAAM,UAAU,IAAI,gBAAgB;AACpC,UAAM,eAAe,MAAM,QAAQ,MAAM;AACzC,WAAO,iBAAiB,SAAS,cAAc,EAAE,MAAM,KAAK,CAAC;AAE7D,QAAI,aAAmD;AACvD,UAAM,WAAW,MAAM;AACrB,UAAI,eAAe,KAAM,cAAa,UAAU;AAChD,mBAAa,WAAW,MAAM,QAAQ,MAAM,GAAG,cAAc;AAAA,IAC/D;AAEA,QAAI;AACF,YAAM,QAAQ,6BAA6B,WAAW,aAAa;AAAA,QACjE,cAAc;AAAA,QACd,QAAQ,QAAQ;AAAA,QAChB,GAAI,QAAQ,mBACR,EAAE,kBAAkB,QAAQ,iBAAiB,IAC7C,CAAC;AAAA,MACP,CAAC;AAED,eAAS;AACT,uBAAiB,QAAQ,OAAO;AAC9B,iBAAS;AACT,mBAAW;AACX,iBAAS,KAAK;AACd,YAAI,KAAK,SAAS,OAAO;AACvB,iBAAO,EAAE,OAAO,MAAM,QAAQ,KAAK,OAAO;AAAA,QAC5C;AACA,YAAI,KAAK,SAAS,SAAS;AACzB,kBAAQ,QAAQ,KAAK,OAAO,KAAK,GAAG;AAAA,QACtC;AAAA,MACF;AAGA,kBAAY;AAAA,IACd,SAAS,OAAO;AACd,UAAI,OAAO,QAAS;AACpB,kBAAY;AACZ,cAAQ,iBAAiB,KAAK;AAAA,IAChC,UAAE;AACA,UAAI,eAAe,KAAM,cAAa,UAAU;AAChD,aAAO,oBAAoB,SAAS,YAAY;AAAA,IAClD;AAEA,QAAI,CAAC,OAAO,WAAW,WAAW,gBAAgB;AAChD,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,OAAO,QAAQ,KAAK;AACtC;;;ACzQA,eAAe,WACb,WACA,MACA,MACA,SACyB;AACzB,QAAM,WAAW,MAAM,cAAc,WAAW,MAAM;AAAA,IACpD,QAAQ;AAAA;AAAA;AAAA,IAGR,MAAM,EAAE,GAAG,MAAM,QAAQ,KAAK;AAAA,IAC9B,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,EACrD,CAAC;AACD,SAAO,YAAY,UAAU,OAAO;AACtC;AAKO,SAAS,cACd,WACA,SACA,SACA,UAAkC,CAAC,GACV;AACzB,SAAO;AAAA,IACL;AAAA,IACA,cAAc,kBAAkB,OAAO,CAAC;AAAA,IACxC;AAAA,IACA;AAAA,EACF;AACF;AAsBO,SAAS,0BACd,WACA,gBACA,SACA,UAAkC,CAAC,GACV;AACzB,SAAO;AAAA,IACL;AAAA,IACA,qBAAqB,kBAAkB,cAAc,CAAC;AAAA,IACtD;AAAA,IACA;AAAA,EACF;AACF;AA8BO,SAAS,eACd,WACA,WACA,UAAmE,CAAC,GACtC;AAC9B,QAAM,QAAQ;AAAA,IACZ,QAAQ,SAAS,cAAc,EAAE,MAAM,YAAY,IAAI,CAAC;AAAA,EAC1D;AACA,SAAO;AAAA,IACL;AAAA,IACA,cAAc,kBAAkB,SAAS,CAAC,GAAG,KAAK;AAAA,IAClD;AAAA,MACE,QAAQ;AAAA,MACR,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IACrD;AAAA,EACF;AACF;;;AXlMO,SAAS,YAAY,SAA0C;AACpE,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,MAAM;AACxD,QAAM,CAAC,YAAY,aAAa,IAAI;AAAA,IAClC;AAAA,EACF;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAgC,IAAI;AAG9D,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAGrB,QAAM,YAAY,OAAO,CAAC;AAC1B,QAAM,gBAAgB,OAA+B,IAAI;AACzD,QAAM,eAAe,OAAsB,IAAI;AAC/C,QAAM,eAAe,OAA8B,IAAI;AAEvD,QAAM,QAAQ,YAAY,MAAM;AAC9B,kBAAc,SAAS,MAAM;AAAA,EAC/B,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM,MAAM,cAAc,SAAS,MAAM,GAAG,CAAC,CAAC;AAExD,QAAM,YAAY;AAAA,IAChB,OACE,MAIA,uBACoC;AACpC,YAAM,MAAM,EAAE,UAAU;AACxB,YAAM,YAAY,MAAM,UAAU,YAAY;AAE9C,oBAAc,SAAS,MAAM;AAC7B,YAAM,aAAa,IAAI,gBAAgB;AACvC,oBAAc,UAAU;AACxB,mBAAa,UAAU;AAEvB,YAAM,OAAO,WAAW;AACxB,YAAM,YACJ,OAAO,KAAK,cAAc,aACtB,KAAK,UAAU,IACf,KAAK;AACX,mBAAa,UAAU;AAEvB,eAAS,UAAU;AACnB,eAAS,IAAI;AACb,oBAAc,IAAI;AAElB,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,WAAW;AAAA,UACnC,GAAG,KAAK;AAAA,UACR,QAAQ,WAAW;AAAA,QACrB,CAAC;AACD,qBAAa,UAAU,OAAO;AAE9B,YAAI,UAAU,6BAA6B;AAAA,UACzC,WAAW,OAAO,aAAa,OAAO,WAAW;AAAA,UACjD,gBAAgB,OAAO,kBAAkB;AAAA,QAC3C,CAAC;AACD,YAAI,UAAU,GAAG;AACf,mBAAS,WAAW;AACpB,wBAAc,OAAO;AAAA,QACvB;AAEA,yBAAiB,YAAY,OAAO,QAAQ;AAC1C,oBAAU,kBAAkB,SAAS,QAAQ;AAC7C,cAAI,UAAU,GAAG;AACf,0BAAc,OAAO;AACrB,uBAAW,QAAQ,eAAe,OAAO;AAAA,UAC3C;AAAA,QACF;AAEA,YAAI,UAAU,GAAG;AACf;AAAA,YACE,QAAQ,WAAW,UACf,UACA,QAAQ,WAAW,cACjB,cACA;AAAA,UACR;AACA,cAAI,QAAQ,WAAW,SAAS;AAC9B,kBAAM,UAA0B;AAAA,cAC9B,MAAM;AAAA,cACN,SACG,OAAO,QAAQ,OAAO,iBAAiB,YACtC,QAAQ,MAAM,gBACf,OAAO,QAAQ,OAAO,YAAY,YACjC,QAAQ,MAAM,WAChB;AAAA,cACF,cAAc,QAAQ;AAAA,YACxB;AACA,qBAAS,OAAO;AAChB,uBAAW,QAAQ,UAAU,OAAO;AAAA,UACtC;AAAA,QACF;AACA,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,UAAU,oBAAoB,GAAG;AACvC,YAAI,UAAU,GAAG;AACf,mBAAS,QAAQ,SAAS,gBAAgB,cAAc,OAAO;AAC/D,cAAI,QAAQ,SAAS,eAAe;AAClC,qBAAS,OAAO;AAChB,uBAAW,QAAQ,UAAU,OAAO;AAAA,UACtC;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ;AAAA,IACZ,CAAC,SAAiB,YAChB;AAAA,MACE,CAAC,WAAW,gBACV,cAAc,WAAW,SAAS,SAAS,WAAW;AAAA,MACxD,QAAQ;AAAA,IACV;AAAA,IACF,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,uBAAuB;AAAA,IAC3B,CAAC,gBAAwB,YACvB;AAAA,MACE,CAAC,WAAW,gBACV;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACF;AAAA,IACF;AAAA,IACF,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,SAAS;AAAA,IACb,OACE,OAA+B,aACS;AACxC,YAAM,YAAY,aAAa;AAC/B,YAAM,YAAY,aAAa;AAC/B,UAAI,CAAC,aAAa,CAAC,UAAW,QAAO;AACrC,aAAO,eAAe,WAAW,WAAW,EAAE,KAAK,CAAC;AAAA,IACtD;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ,YAAY,MAAM;AAC9B,cAAU,WAAW;AACrB,kBAAc,SAAS,MAAM;AAC7B,kBAAc,UAAU;AACxB,iBAAa,UAAU;AACvB,aAAS,MAAM;AACf,kBAAc,IAAI;AAClB,aAAS,IAAI;AAAA,EACf,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,YAAY,aAAa;AAAA,IACpC,gBAAgB,YAAY,kBAAkB;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AYzPA,SAAS,eAAAC,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAyClD,SAAS,0BACd,SAC2B;AAC3B,QAAM,CAAC,WAAW,YAAY,IAAIC,UAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,KAAK;AACxC,QAAM,CAAC,QAAQ,SAAS,IAAIA;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,aAAaC,QAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,EAAE,aAAa,UAAU,KAAK,IAAI;AACxC,QAAM,SAAS,WAAW,CAAC,CAAC;AAE5B,QAAM,SAASC;AAAA,IACb,CAAC,IAAY,QAAqB,cAA6B;AAC7D,YAAM,OAAO,WAAW;AACxB,YAAM,YACJ,OAAO,KAAK,cAAc,aACtB,KAAK,UAAU,IACf,KAAK;AACX,aAAO,4BAA4B,WAAW,IAAI;AAAA,QAChD;AAAA,QACA,GAAI,KAAK,iBAAiB,SACtB,EAAE,cAAc,KAAK,aAAa,IAClC,CAAC;AAAA,QACL,GAAI,KAAK,mBAAmB,SACxB,EAAE,gBAAgB,KAAK,eAAe,IACtC,CAAC;AAAA,QACL,GAAI,KAAK,mBAAmB,SACxB,EAAE,gBAAgB,KAAK,eAAe,IACtC,CAAC;AAAA,QACL,GAAI,KAAK,qBAAqB,SAC1B,EAAE,kBAAkB,KAAK,iBAAiB,IAC1C,CAAC;AAAA,QACL,SAAS,CAAC,OAAO,QAAQ;AACvB,cAAI,UAAU,EAAG,YAAW,QAAQ,UAAU,OAAO,GAAG;AAAA,QAC1D;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AAEA,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,YAAa;AAC7B,QAAI,UAAU;AACd,UAAM,aAAa,IAAI,gBAAgB;AACvC,iBAAa,IAAI;AACjB,aAAS,KAAK;AACd,cAAU,IAAI;AAEd,SAAK,OAAO,aAAa,WAAW,QAAQ,MAAM,OAAO,EACtD,KAAK,CAAC,WAAW;AAChB,UAAI,CAAC,QAAS;AACd,mBAAa,KAAK;AAClB,eAAS,OAAO,KAAK;AACrB,gBAAU,OAAO,MAAM;AACvB,iBAAW,QAAQ,YAAY,MAAM;AAAA,IACvC,CAAC,EACA,MAAM,MAAM;AACX,UAAI,CAAC,QAAS;AACd,mBAAa,KAAK;AAClB,iBAAW,QAAQ,YAAY,EAAE,OAAO,OAAO,QAAQ,KAAK,CAAC;AAAA,IAC/D,CAAC;AAEH,WAAO,MAAM;AACX,gBAAU;AACV,iBAAW,MAAM;AACjB,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,aAAa,MAAM,CAAC;AAEhC,SAAO,EAAE,WAAW,OAAO,OAAO;AACpC;","names":["isRecord","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect"]}
|