@ai-matrx/api 0.0.0-stage → 0.1.0

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.
Files changed (146) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +18 -2
  4. package/dist/account.cjs +670 -0
  5. package/dist/account.d.cts +3246 -0
  6. package/dist/account.d.ts +3246 -0
  7. package/dist/account.js +558 -0
  8. package/dist/agents.cjs +598 -0
  9. package/dist/agents.d.cts +3417 -0
  10. package/dist/agents.d.ts +3417 -0
  11. package/dist/agents.js +497 -0
  12. package/dist/ai.cjs +109 -0
  13. package/dist/ai.d.cts +493 -0
  14. package/dist/ai.d.ts +493 -0
  15. package/dist/ai.js +91 -0
  16. package/dist/apps.cjs +394 -0
  17. package/dist/apps.d.cts +1094 -0
  18. package/dist/apps.d.ts +1094 -0
  19. package/dist/apps.js +328 -0
  20. package/dist/architecture.cjs +1223 -0
  21. package/dist/architecture.d.cts +4701 -0
  22. package/dist/architecture.d.ts +4701 -0
  23. package/dist/architecture.js +1016 -0
  24. package/dist/audio.cjs +226 -0
  25. package/dist/audio.d.cts +723 -0
  26. package/dist/audio.d.ts +723 -0
  27. package/dist/audio.js +188 -0
  28. package/dist/chat.cjs +538 -0
  29. package/dist/chat.d.cts +2251 -0
  30. package/dist/chat.d.ts +2251 -0
  31. package/dist/chat.js +447 -0
  32. package/dist/chunk-2SXAAKGV.cjs +279 -0
  33. package/dist/chunk-AY5N6EA4.js +43 -0
  34. package/dist/chunk-BMQI7UET.cjs +48 -0
  35. package/dist/chunk-FNIYADWI.cjs +59 -0
  36. package/dist/chunk-KIEAFMKB.js +264 -0
  37. package/dist/chunk-UONBYRE4.js +1012 -0
  38. package/dist/chunk-YTEOFGFC.js +57 -0
  39. package/dist/chunk-YUDQWDCM.cjs +1047 -0
  40. package/dist/code.cjs +561 -0
  41. package/dist/code.d.cts +1976 -0
  42. package/dist/code.d.ts +1976 -0
  43. package/dist/code.js +467 -0
  44. package/dist/commerce.cjs +22 -0
  45. package/dist/commerce.d.cts +45 -0
  46. package/dist/commerce.d.ts +45 -0
  47. package/dist/commerce.js +18 -0
  48. package/dist/communications.cjs +215 -0
  49. package/dist/communications.d.cts +948 -0
  50. package/dist/communications.d.ts +948 -0
  51. package/dist/communications.js +179 -0
  52. package/dist/computer.cjs +330 -0
  53. package/dist/computer.d.cts +733 -0
  54. package/dist/computer.d.ts +733 -0
  55. package/dist/computer.js +275 -0
  56. package/dist/content.cjs +425 -0
  57. package/dist/content.d.cts +1796 -0
  58. package/dist/content.d.ts +1796 -0
  59. package/dist/content.js +354 -0
  60. package/dist/credentials.cjs +22 -0
  61. package/dist/credentials.d.cts +82 -0
  62. package/dist/credentials.d.ts +82 -0
  63. package/dist/credentials.js +1 -0
  64. package/dist/crm.cjs +271 -0
  65. package/dist/crm.d.cts +1731 -0
  66. package/dist/crm.d.ts +1731 -0
  67. package/dist/crm.js +226 -0
  68. package/dist/data.cjs +382 -0
  69. package/dist/data.d.cts +1860 -0
  70. package/dist/data.d.ts +1860 -0
  71. package/dist/data.js +318 -0
  72. package/dist/education.cjs +28 -0
  73. package/dist/education.d.cts +73 -0
  74. package/dist/education.d.ts +73 -0
  75. package/dist/education.js +23 -0
  76. package/dist/files.cjs +1330 -0
  77. package/dist/files.d.cts +4956 -0
  78. package/dist/files.d.ts +4956 -0
  79. package/dist/files.js +1104 -0
  80. package/dist/human-resources.cjs +178 -0
  81. package/dist/human-resources.d.cts +1208 -0
  82. package/dist/human-resources.d.ts +1208 -0
  83. package/dist/human-resources.js +148 -0
  84. package/dist/index-C2E-Cp19.d.ts +385 -0
  85. package/dist/index-wC2vtb70.d.cts +385 -0
  86. package/dist/index.cjs +1934 -0
  87. package/dist/index.d.cts +2006 -0
  88. package/dist/index.d.ts +2006 -0
  89. package/dist/index.js +1668 -0
  90. package/dist/integrations.cjs +5116 -0
  91. package/dist/integrations.d.cts +19213 -0
  92. package/dist/integrations.d.ts +19213 -0
  93. package/dist/integrations.js +4263 -0
  94. package/dist/intelligence.cjs +1305 -0
  95. package/dist/intelligence.d.cts +4658 -0
  96. package/dist/intelligence.d.ts +4658 -0
  97. package/dist/intelligence.js +1196 -0
  98. package/dist/knowledge.cjs +1113 -0
  99. package/dist/knowledge.d.cts +5427 -0
  100. package/dist/knowledge.d.ts +5427 -0
  101. package/dist/knowledge.js +921 -0
  102. package/dist/legal.cjs +225 -0
  103. package/dist/legal.d.cts +1038 -0
  104. package/dist/legal.d.ts +1038 -0
  105. package/dist/legal.js +187 -0
  106. package/dist/marketing.cjs +1155 -0
  107. package/dist/marketing.d.cts +7741 -0
  108. package/dist/marketing.d.ts +7741 -0
  109. package/dist/marketing.js +951 -0
  110. package/dist/masterwork.cjs +283 -0
  111. package/dist/masterwork.d.cts +1938 -0
  112. package/dist/masterwork.d.ts +1938 -0
  113. package/dist/masterwork.js +231 -0
  114. package/dist/media.cjs +610 -0
  115. package/dist/media.d.cts +2270 -0
  116. package/dist/media.d.ts +2270 -0
  117. package/dist/media.js +507 -0
  118. package/dist/net.cjs +67 -0
  119. package/dist/net.d.cts +204 -0
  120. package/dist/net.d.ts +204 -0
  121. package/dist/net.js +2 -0
  122. package/dist/platform.cjs +150 -0
  123. package/dist/platform.d.cts +572 -0
  124. package/dist/platform.d.ts +572 -0
  125. package/dist/platform.js +125 -0
  126. package/dist/publish.cjs +248 -0
  127. package/dist/publish.d.cts +1431 -0
  128. package/dist/publish.d.ts +1431 -0
  129. package/dist/publish.js +206 -0
  130. package/dist/schemas.cjs +2 -0
  131. package/dist/schemas.d.cts +1353 -0
  132. package/dist/schemas.d.ts +1353 -0
  133. package/dist/schemas.js +1 -0
  134. package/dist/stream.cjs +44 -0
  135. package/dist/stream.d.cts +2 -0
  136. package/dist/stream.d.ts +2 -0
  137. package/dist/stream.js +3 -0
  138. package/dist/web.cjs +1045 -0
  139. package/dist/web.d.cts +4186 -0
  140. package/dist/web.d.ts +4186 -0
  141. package/dist/web.js +867 -0
  142. package/dist/workflows.cjs +825 -0
  143. package/dist/workflows.d.cts +4699 -0
  144. package/dist/workflows.d.ts +4699 -0
  145. package/dist/workflows.js +686 -0
  146. package/package.json +418 -4
@@ -0,0 +1,2006 @@
1
+ import { M as MatrxStreamEnvelope, b as MatrxNdjsonIssue, c as MatrxStreamEnvelopeObservation, d as MatrxSseFrame } from './index-wC2vtb70.cjs';
2
+ export { A as Api, e as ApiRequestInfo, a as ApiStream, f as AuthTier, g as CONTRACT_SHA256, C as CallOptions, h as CreateApiOptions, D as DEPLOYMENTS, i as DOMAINS, j as DeploymentName, E as Environment, H as HttpMethod, J as JsonOrStream, N as NdjsonEvent, O as OperationArgs, k as OperationDescriptor, l as OrgRule, S as SseEvent, m as StreamKind, n as createApi, o as openJsonOrStream, p as openStream } from './index-wC2vtb70.cjs';
3
+ import { CredentialsPort } from './credentials.cjs';
4
+ export { API_KEY_PREFIX, CredentialsSetting, MATRX_HEADERS, MatrxCredential, MatrxCredentialKind, credentialToHeaders, toCredentialsPort } from './credentials.cjs';
5
+ import { ResilientFetchOptions } from './net.cjs';
6
+ export { AbortedError, ConnectTimeoutError, HeartbeatTimeoutError, HttpError, NetError, NetErrorCode, NetworkError, OfflineError, ResilientFetchResult, RetryOptions, StreamMonitorOptions, TotalTimeoutError, extractErrorMessage, isNetError, isTransportFailure, monitorStream, resilientFetch, toNetError, withRetry } from './net.cjs';
7
+
8
+ /**
9
+ * `@ai-matrx/agents/matrx` — the Matrx transport port.
10
+ *
11
+ * The ONE seam between this package's wire semantics and a host's connection
12
+ * policy. This package owns WHAT is said to the AI Matrx server — paths,
13
+ * methods, JSON bodies, streaming Accept headers, the `Last-Event-ID` cursor
14
+ * header — and the host owns HOW the connection is made:
15
+ *
16
+ * - base-URL / backend-channel resolution (global, sandbox override, local
17
+ * engine, EC2-dedicated — whatever ladder the host runs);
18
+ * - credentials (Supabase JWT `Authorization: Bearer`, guest
19
+ * `X-Fingerprint-ID`, or the API-key lane) — NEVER implemented here;
20
+ * - the `X-Organization-Id` context header;
21
+ * - retry policy, network-level timeouts, and diagnostics capture.
22
+ *
23
+ * A host implements the port in a few lines:
24
+ *
25
+ * ```ts
26
+ * const transport: MatrxTransport = {
27
+ * fetch: (path, init) =>
28
+ * fetch(`${baseUrl}${path}`, {
29
+ * ...init,
30
+ * headers: { ...init.headers, ...authHeaders() },
31
+ * }),
32
+ * };
33
+ * ```
34
+ *
35
+ * The port is deliberately structural and `fetch`-shaped so `@ai-matrx/data`'s
36
+ * `/api` transport can implement it without importing this package.
37
+ */
38
+ /**
39
+ * The request this package hands the port. A strict subset of `RequestInit`,
40
+ * so a host can spread it straight into `fetch`.
41
+ */
42
+ interface MatrxTransportRequest {
43
+ method: "GET" | "POST";
44
+ /**
45
+ * Wire-semantic headers the CALL requires (`Content-Type`, `Accept`,
46
+ * `Last-Event-ID`). The host merges its policy headers (auth, org) on top;
47
+ * it must not drop these.
48
+ */
49
+ headers: Record<string, string>;
50
+ /** Pre-serialized JSON body, present on POST calls that carry one. */
51
+ body?: string;
52
+ /** Caller cancellation. The host must wire it to the underlying fetch. */
53
+ signal?: AbortSignal;
54
+ }
55
+ /**
56
+ * The transport port. `path` is server-relative and always starts with `/`
57
+ * (`/ai/...`, `/runtime/...`); the host prepends its resolved base URL.
58
+ */
59
+ interface MatrxTransport {
60
+ fetch(path: string, init: MatrxTransportRequest): Promise<Response>;
61
+ }
62
+ /**
63
+ * A non-2xx response from the Matrx API, with the server's structured error
64
+ * body preserved and its richest human-readable message extracted.
65
+ */
66
+ declare class MatrxApiError extends Error {
67
+ readonly name = "MatrxApiError";
68
+ /** HTTP status of the failed response. */
69
+ readonly status: number;
70
+ /** Machine code from the server body (`code`, or `detail.code`), when present. */
71
+ readonly code: string | null;
72
+ /** The parsed server error body, verbatim (undefined when unparsable). */
73
+ readonly serverDetail: unknown;
74
+ /** The request path the failure came from (server-relative). */
75
+ readonly path: string;
76
+ constructor(args: {
77
+ status: number;
78
+ path: string;
79
+ serverDetail?: unknown;
80
+ message?: string;
81
+ });
82
+ }
83
+ /**
84
+ * Extract the richest human-readable message from a Matrx/FastAPI error body.
85
+ *
86
+ * aidream 4xx validation errors look like
87
+ * `{ error, user_message, details: [{ field, message, help }] }`; hand-raised
88
+ * HTTPExceptions carry `{ detail: { code, message } }`; FastAPI's defaults are
89
+ * `{ detail: string | [{ msg }] }`. Preference order: `user_message` →
90
+ * `message` → joined `details[].message` → `detail.message` →
91
+ * `detail` string → joined `detail[].msg`. Returns undefined for
92
+ * unrecognized bodies so callers fall back to the bare status line.
93
+ */
94
+ declare function extractMatrxErrorMessage(serverDetail: unknown): string | undefined;
95
+ /**
96
+ * Extract the machine error code from a Matrx error body: top-level `code`,
97
+ * else `detail.code` (the hand-raised HTTPException shape). Null when absent.
98
+ */
99
+ declare function extractMatrxErrorCode(serverDetail: unknown): string | null;
100
+
101
+ /**
102
+ * `@ai-matrx/agents/matrx` — the AI Matrx server's error model, as every
103
+ * client reads it: `BackendApiError` (the server's `APIError` body),
104
+ * `StreamTransportError` (the socket died mid-run; the run may still finish
105
+ * and is reattachable), parsing of HTTP / stream / persisted errors, and the
106
+ * ONE sentence a person sees (`getUserMessage`, `describeBackendFailure`).
107
+ *
108
+ * Moved from matrx-frontend `lib/api/errors.ts` (chat-package independence
109
+ * P9); the app re-exports it from here. Pure: no host, no window, no env.
110
+ */
111
+ /**
112
+ * Standardized error shape returned by all backend endpoints.
113
+ * Matches the Python `APIError` Pydantic model.
114
+ */
115
+ interface BackendApiErrorData {
116
+ /** Machine-readable error code (e.g. "auth_required", "validation_error") */
117
+ error: string;
118
+ /** Developer-facing detail for debugging */
119
+ message: string;
120
+ /** Safe to display directly in the UI */
121
+ user_message: string;
122
+ /** Extra info (validation errors, etc.) */
123
+ details: unknown | null;
124
+ /** Unique request ID for support/debugging */
125
+ request_id: string;
126
+ }
127
+ /** Common backend error codes */
128
+ type BackendErrorCode = "auth_required" | "token_required" | "admin_required" | "validation_error" | "not_found" | "internal_error" | "agent_error"
129
+ /** The stream socket died mid-run; the server run may still be completing
130
+ * and is reattachable. See `StreamTransportError`. */
131
+ | "stream_transport_lost" | (string & {});
132
+ /**
133
+ * Typed error thrown by all backend API operations.
134
+ * Contains structured fields matching the Python APIError model.
135
+ *
136
+ * Usage:
137
+ * ```typescript
138
+ * try {
139
+ * await client.post(ENDPOINTS.ai.agentStart(agentId), body);
140
+ * } catch (err) {
141
+ * if (err instanceof BackendApiError) {
142
+ * // Show err.userMessage to the user
143
+ * // Log err.requestId for debugging
144
+ * // Check err.code for programmatic handling
145
+ * }
146
+ * }
147
+ * ```
148
+ */
149
+ declare class BackendApiError extends Error {
150
+ /** Machine-readable error code */
151
+ readonly code: BackendErrorCode;
152
+ /** Developer-facing detail */
153
+ readonly detail: string;
154
+ /** Safe to display directly in the UI */
155
+ readonly userMessage: string;
156
+ /** Extra info (validation errors, etc.) */
157
+ readonly details: unknown | null;
158
+ /** Unique request ID for support/debugging */
159
+ readonly requestId: string;
160
+ /** HTTP status code (if from an HTTP response) */
161
+ readonly status: number | null;
162
+ constructor(data: {
163
+ code: BackendErrorCode;
164
+ detail: string;
165
+ userMessage: string;
166
+ details?: unknown | null;
167
+ requestId?: string | undefined;
168
+ status?: number | null | undefined;
169
+ });
170
+ /** Convert to the wire format for logging */
171
+ toJSON(): BackendApiErrorData;
172
+ }
173
+ /**
174
+ * The socket carrying a live NDJSON stream broke mid-run.
175
+ *
176
+ * THE DISTINCTION THIS EXISTS TO MAKE: a backend that blows up mid-stream does
177
+ * NOT break the socket — it emits a typed `error` event and closes the body
178
+ * cleanly. So an exception escaping the body reader means the *transport* died,
179
+ * not the run. And aidream streams run `detach_on_disconnect=True`: the server
180
+ * keeps executing and persisting the turn after our connection goes away.
181
+ *
182
+ * The client therefore cannot decide locally whether the answer is lost — it
183
+ * must ASK THE SERVER. That is what `resumable` means here: "reattach by
184
+ * requestId / conversationId / durable run id and let server truth settle it",
185
+ * never "this succeeded". A server that genuinely died reports `failed` on
186
+ * reattach and the honest record replaces the optimistic copy.
187
+ *
188
+ * Consumers: `run-ai-stream.ts` (chat → `reconnectServerOperation`) and
189
+ * `adopt-foreign-stream.ts` (pipeline runs → the surface's own rejoin).
190
+ */
191
+ declare class StreamTransportError extends BackendApiError {
192
+ /** Always true — reattach and let the server settle the outcome. */
193
+ readonly resumable: true;
194
+ constructor(data: {
195
+ detail: string;
196
+ details?: unknown | null;
197
+ requestId?: string;
198
+ });
199
+ }
200
+ /**
201
+ * True when a failure is a dropped transport rather than a failed run. Use this
202
+ * instead of `instanceof` at boundaries that re-wrap errors (thunk rejections,
203
+ * `callApi` result errors), where the class identity is lost but the code
204
+ * survives.
205
+ */
206
+ declare function isStreamTransportLost(error: unknown): boolean;
207
+ /**
208
+ * Parse a non-OK HTTP response into a BackendApiError.
209
+ *
210
+ * Handles the standardized backend shape and falls back gracefully
211
+ * when the response isn't JSON or uses a legacy format.
212
+ */
213
+ declare function parseHttpError(response: Response): Promise<BackendApiError>;
214
+ /**
215
+ * Parse an already-decoded JSON error body into a BackendApiError.
216
+ *
217
+ * Exported because XHR callers (upload/download progress paths) have the
218
+ * parsed body in hand and MUST NOT hand-roll a shallower read: a private
219
+ * copy in `python-client.ts` looked only at top-level `error`/`message`, so
220
+ * FastAPI's `{"detail": {...}}` envelope — what every matrx-files 500 uses —
221
+ * degraded to the useless `code: "internal", detail: "HTTP 500"`. That is
222
+ * exactly how an upload failure with a real server-side cause reached the
223
+ * user as `Upload failed (500)` and nothing else. One parser, every transport.
224
+ */
225
+ declare function parseHttpErrorBody(body: Record<string, unknown> | null, status: number): BackendApiError;
226
+ /**
227
+ * Adapt callApi's result-style error into the same canonical error used by
228
+ * direct fetch and streaming consumers.
229
+ *
230
+ * callApi intentionally returns errors instead of throwing them, but its
231
+ * `serverDetail` contains the complete FastAPI body. Sending only
232
+ * `error.message` to a feature discards that body and turns a precise
233
+ * configuration failure into "HTTP 422". This adapter keeps one parser and
234
+ * one human-facing explanation path across both client styles.
235
+ */
236
+ declare function parseCallApiError(error: {
237
+ message: string;
238
+ status?: number;
239
+ serverDetail?: unknown;
240
+ }): BackendApiError;
241
+ /**
242
+ * Parse streaming error event data into a BackendApiError.
243
+ *
244
+ * Handles both new format (`user_message`) and legacy (`user_visible_message`).
245
+ */
246
+ declare function parseStreamError(data: unknown): BackendApiError;
247
+ /**
248
+ * Restore the canonical error shape from a durable backend run row.
249
+ *
250
+ * Provider ledgers often keep an aggregate summary plus a more specific first
251
+ * child failure. Prefer that child so a page refresh does not turn a precise
252
+ * streamed failure back into "1 request failed".
253
+ */
254
+ declare function parsePersistedBackendError(data: unknown, requestId?: string): BackendApiError | null;
255
+ /** True when a message tells the reader nothing about what actually broke. */
256
+ declare function isGenericUserMessage(message: string | null | undefined): boolean;
257
+ interface UpstreamErrorPayload {
258
+ message: string;
259
+ code: string | null;
260
+ userMessage: string | null;
261
+ requestId: string | null;
262
+ status: number | null;
263
+ }
264
+ /**
265
+ * Recover an upstream service's structured error that a downstream service
266
+ * stringified into its own message.
267
+ *
268
+ * Real example (scraper wrapping aidream):
269
+ * `aidream could not resolve GSC credential 7223…: HTTP 409 {"error":"conflict",
270
+ * "message":"Google connection 7223… has no vault credential — it needs
271
+ * re-authentication","user_message":"Something went wrong…","request_id":"9002…"}`
272
+ *
273
+ * Without this, the only actionable sentence on the whole hop is invisible.
274
+ */
275
+ declare function unwrapUpstreamError(message: string): UpstreamErrorPayload | null;
276
+ interface BackendFailureExplanation {
277
+ /** Machine code from the deepest layer that classified the failure. */
278
+ code: string;
279
+ /** The most specific human-readable cause available — never a template. */
280
+ cause: string;
281
+ /** What to headline in the UI: the cause when the server was generic. */
282
+ headline: string;
283
+ /** True when every user-facing message the server sent was a template. */
284
+ headlineWasGeneric: boolean;
285
+ /** Message chain, outermost (closest service) first. */
286
+ chain: string[];
287
+ /** Deepest request id available, for cross-service log correlation. */
288
+ requestId: string;
289
+ status: number | null;
290
+ }
291
+ /**
292
+ * THE anti-secrecy primitive: turn any thrown backend/stream failure into the
293
+ * most specific explanation the payload can support — unwrapping every nested
294
+ * upstream error and refusing to let a templated `user_message` be the answer.
295
+ *
296
+ * Every surface that reports a backend failure to a human should headline
297
+ * `explanation.headline` and always keep `cause` + `requestId` reachable.
298
+ */
299
+ declare function describeBackendFailure(error: unknown): BackendFailureExplanation;
300
+ /**
301
+ * Extract a user-visible message from any error object.
302
+ * Utility for components that just need the display string.
303
+ */
304
+ declare function getUserMessage(error: unknown): string;
305
+
306
+ /**
307
+ * The AI API protocol-version policy — which version of the AI runtime a
308
+ * Matrx client talks to, and the v2 → v1 transport fallback (moved in from
309
+ * matrx-frontend `lib/api/ai-api-version.ts` + `call-api.ts::
310
+ * fetchWithV2Fallback` under C22; server truth
311
+ * `aidream/docs/runtime/V2_FRONTEND_MIGRATION.md`).
312
+ *
313
+ * Background: the Python backend exposes a `/v2` runtime-spine namespace that
314
+ * wraps the core AI request surfaces in a request-tracking envelope. Body,
315
+ * headers, and streaming response are BYTE-IDENTICAL to v1 — only the URL
316
+ * differs.
317
+ *
318
+ * ─── The one rule that keeps burning us ─────────────────────────────────────
319
+ * `/v2` is inserted at the FRONT of the in-app path, right before `/ai`:
320
+ *
321
+ * /ai/agents/{id} → /v2/ai/agents/{id} ✅ correct
322
+ * /ai/v2/agents/… ❌ WRONG (nested) — 404s
323
+ *
324
+ * `toV2Path` below is the ONLY place this transform is spelled out.
325
+ *
326
+ * ─── Scope: ONLY the covered surfaces have a v2 form ────────────────────────
327
+ * chat, manual, agents/{id}, conversations/{id} (+ singular aliases),
328
+ * prompts/{id}, mandates/{key}. EVERYTHING ELSE — cancel, warm, resume,
329
+ * fork-and-run, runtime operations, files … — has NO v2 route, and the server
330
+ * does NOT auto-downgrade (a `/v2` request to an uncovered surface is a plain
331
+ * 404). So the transform is a scoped allowlist, never a blanket prefix.
332
+ */
333
+
334
+ type MatrxAiApiVersion = "v1" | "v2";
335
+ /**
336
+ * The package-wide default AI API version — the production value every Matrx
337
+ * client runs unless its host deliberately overrides
338
+ * (`createMatrxTransport({ aiApiVersion })`).
339
+ */
340
+ declare const MATRX_AI_API_VERSION_DEFAULT: MatrxAiApiVersion;
341
+ /**
342
+ * The canonical v1 path TEMPLATES the v2 spine covers, `{param}` placeholders
343
+ * intact — for hosts that route through a template registry.
344
+ */
345
+ declare const V2_COVERED_AI_PATH_TEMPLATES: readonly ["/ai/manual", "/ai/chat", "/ai/agents/{agent_id}", "/ai/agent/{agent_id}", "/ai/conversations/{conversation_id}", "/ai/conversation/{conversation_id}", "/ai/prompts/{prompt_id}", "/ai/mandates/{mandate_key}"];
346
+ /**
347
+ * Insert `/v2` at the front of an in-app path. Idempotent, and prefix-aware:
348
+ * a legacy `/api/` compatibility prefix (stripped server-side) is preserved so
349
+ * `/api/ai/chat` → `/api/v2/ai/chat`.
350
+ */
351
+ declare function toV2Path(path: string): string;
352
+ /** Whether `path` (already interpolated) is one of the covered surfaces. */
353
+ declare function isCoveredAiPath(path: string): boolean;
354
+ /**
355
+ * Whether an in-app path (or a full URL whose path was built by `toV2Path`)
356
+ * targets the v2 namespace — the guard the downgrade fallback keys off.
357
+ */
358
+ declare function isV2Path(pathOrUrl: string): boolean;
359
+ /**
360
+ * The inverse of `toV2Path` — strip the `/v2` version segment so a transport
361
+ * failure of the v2 endpoint (network error, 404/405, 5xx BEFORE any stream
362
+ * content) can retry the identical request on the v1 route. Works on a bare
363
+ * path, an `/api`-prefixed path, or a full URL (the `/v2/ai/` shape is unique
364
+ * to the version namespace). Idempotent on non-v2 input.
365
+ */
366
+ declare function toV1FallbackUrl(url: string): string;
367
+ /**
368
+ * Apply the active AI API version to an ALREADY-INTERPOLATED in-app path.
369
+ *
370
+ * - Covered surface + v2 → `/v2` prefix inserted.
371
+ * - Anything else (or v1) → returned unchanged.
372
+ *
373
+ * Pass the in-app PATH only (no scheme/host); prepend the base URL afterward.
374
+ */
375
+ declare function applyAiApiVersion(path: string, version: MatrxAiApiVersion): string;
376
+ /** One v2 → v1 downgrade, surfaced to the host's diagnostics sink. */
377
+ interface MatrxProtocolDowngrade {
378
+ /** The failed v2 URL. */
379
+ url: string;
380
+ /** Why the downgrade fired (thrown error text, or `HTTP <status>`). */
381
+ reason: string;
382
+ /** HTTP status when the trigger was a response (404/405/5xx). */
383
+ status?: number;
384
+ }
385
+ interface MatrxProtocolFallbackOptions extends ResilientFetchOptions {
386
+ /**
387
+ * Fired on every downgrade — a sustained stream of these means a v2 surface
388
+ * is unhealthy. The fallback also `console.warn`s unconditionally (parity
389
+ * with the original host pipeline) so the signal never disappears silently.
390
+ */
391
+ onDowngrade?: (downgrade: MatrxProtocolDowngrade) => void;
392
+ }
393
+ /**
394
+ * `resilientFetch` with the v2 → v1 transport fallback. Fires ONLY when a
395
+ * `/v2/ai/...` ENDPOINT itself fails — a network-layer throw (non-abort), a
396
+ * 404/405 (surface not on v2), or a 5xx — always BEFORE any stream content is
397
+ * consumed. Never on an application error (those fail identically on v1), and
398
+ * never on a caller abort (a user cancel must not be logged as a downgrade —
399
+ * that poisons the exact telemetry the rollout reads to judge v2 health).
400
+ */
401
+ declare function fetchWithMatrxProtocolFallback(url: string, init: RequestInit, opts?: MatrxProtocolFallbackOptions): Promise<{
402
+ response: Response;
403
+ }>;
404
+ /**
405
+ * The exact-match `pathOverrides` map for `resolveEndpointPath` — keyed on the
406
+ * canonical templates, valued at their `/v2` siblings. Empty for v1. Every call
407
+ * that flows through the endpoint-override registry picks up v2 for the covered
408
+ * surfaces — and ONLY the covered surfaces. (Moved from matrx-frontend
409
+ * `lib/api/ai-api-version.ts`, chat-package independence P9.)
410
+ */
411
+ declare function aiVersionPathOverrides(version: MatrxAiApiVersion): Record<string, string>;
412
+
413
+ /**
414
+ * The PRODUCTION `MatrxTransport` — the full connection pipeline every Matrx
415
+ * host used to hand-roll, moved in under C22 (THE HARD PARTS LIVE IN THE
416
+ * PACKAGE). A host constructs it from identity alone:
417
+ *
418
+ * ```ts
419
+ * const transport = createMatrxTransport({
420
+ * baseUrl: "https://server.app.matrxserver.com",
421
+ * credentials, // CredentialsPort (@ai-matrx/data)
422
+ * organizationId: () => activeOrgId, // omit for conversation-lane calls
423
+ * });
424
+ * ```
425
+ *
426
+ * Everything else is defaulted to the proven production values:
427
+ *
428
+ * - **timeouts** — connect 15s (for a non-streaming FastAPI handler this is
429
+ * effectively time-to-response), total UNCAPPED (the port cannot tell a JSON
430
+ * call from a long-lived NDJSON/SSE stream; the connect timeout is the JSON
431
+ * guard), via `@ai-matrx/data/net`'s `resilientFetch`;
432
+ * - **protocol** — the AI API version transform (`applyAiApiVersion`, default
433
+ * v2) + the loud v2 → v1 transport fallback (`./protocol`);
434
+ * - **credentials** — read fresh from the injected `CredentialsPort` on EVERY
435
+ * call, so a token refresh mid-run is picked up; mapped to headers by the
436
+ * ONE `credentialToHeaders`;
437
+ * - **org context** — the fail-closed `X-Organization-Id` binding
438
+ * (`./org-context`): configured lanes REQUIRE an org and refuse before the
439
+ * wire; unconfigured lanes (conversation-scoped calls, which carry org in
440
+ * the body) send none;
441
+ * - **error classification** — every failure normalizes through
442
+ * `normalizeMatrxError` into the ONE `MatrxCallError` envelope;
443
+ * - **diagnostics** — a typed sink the host wires to its capture/telemetry
444
+ * (`onRequest`, `onError`, `onProtocolDowngrade`); wiring it is optional,
445
+ * the transport works silently without it.
446
+ *
447
+ * Header merge order is part of the port contract: wire headers first
448
+ * (`Content-Type` / `Accept` / `Last-Event-ID` — the package's), then
449
+ * credentials, then resolver policy headers, then the org header. Policy
450
+ * headers never carry `Content-Type` — the wire owns it, and a policy
451
+ * `Content-Type` merged over a GET SSE call would corrupt the wire, so the
452
+ * factory strips it defensively.
453
+ */
454
+
455
+ /**
456
+ * The ONE classified error shape for Matrx client calls — the envelope hosts
457
+ * branch on instead of string-matching exceptions. Structurally compatible
458
+ * with matrx-frontend's `ApiCallError` by design.
459
+ */
460
+ interface MatrxCallError {
461
+ type: "auth_error" | "network_error" | "http_error" | "validation_error" | "abort_error" | "unknown";
462
+ message: string;
463
+ /** HTTP status code, if applicable. */
464
+ status?: number;
465
+ /** Raw error detail from the server (the parsed error body). */
466
+ serverDetail?: unknown;
467
+ /** Machine code preserved through normalization (server `code`, org-context code, …). */
468
+ code?: string;
469
+ /** Original exception identity for diagnostics. */
470
+ name?: string;
471
+ /** Original exception stack. */
472
+ stack?: string;
473
+ /** JSON-safe dump of the original thrown value. */
474
+ raw?: unknown;
475
+ }
476
+ /**
477
+ * Classify any failure thrown by a Matrx client call — `MatrxApiError`, a
478
+ * typed `BackendApiError` / `StreamTransportError`, a `NetError` from the
479
+ * resilience layer, an org-context refusal, an abort — into the
480
+ * `MatrxCallError` envelope. Never throws.
481
+ *
482
+ * Every message is laundered through `honestTransportMessage`: a bare status
483
+ * line ("HTTP 400") never reaches a person — the status rides `status`.
484
+ * A `BackendApiError` keeps its machine `code` (a dropped socket stays
485
+ * `stream_transport_lost`, so a surface reattaches instead of dead-ending)
486
+ * and its structured `details` ride `serverDetail` (the organization-hold
487
+ * body carries the caller's membership choices there).
488
+ */
489
+ declare function normalizeMatrxError(err: unknown): MatrxCallError;
490
+ /**
491
+ * A fully-resolved connection target for one call: where to send it and which
492
+ * policy headers ride ON TOP of the wire + credential headers.
493
+ */
494
+ interface MatrxTransportTarget {
495
+ /** Fully-qualified base URL, no trailing slash. */
496
+ baseUrl: string;
497
+ /**
498
+ * Extra policy headers for this target (e.g. a resolver that carries its own
499
+ * credential headers). `Content-Type` is stripped — the wire owns it.
500
+ */
501
+ policyHeaders?: Record<string, string>;
502
+ /** Routing channel label surfaced to `onRequest` telemetry. */
503
+ channel?: string;
504
+ }
505
+ /** Context handed to every diagnostics callback. */
506
+ interface MatrxRequestInfo {
507
+ /** The final URL (base + version-transformed path). */
508
+ url: string;
509
+ method: "GET" | "POST";
510
+ /** The server-relative path as the package requested it. */
511
+ path: string;
512
+ /** The resolved routing channel (default "default"). */
513
+ channel: string;
514
+ /** The transport's call-site label (default "matrxTransport"). */
515
+ source: string;
516
+ }
517
+ /**
518
+ * The typed diagnostics sink — the host wires these to its telemetry/capture
519
+ * systems; all optional, and the transport is fully functional without them.
520
+ */
521
+ interface MatrxTransportDiagnostics {
522
+ /** Fired at the last pre-fetch moment of every call. */
523
+ onRequest?: (info: MatrxRequestInfo) => void;
524
+ /**
525
+ * Fired for every thrown network failure and every non-2xx response not
526
+ * listed in `expectedErrorStatuses`, with the classified envelope.
527
+ */
528
+ onError?: (error: MatrxCallError, info: MatrxRequestInfo) => void;
529
+ /** Fired on every v2 → v1 protocol downgrade. */
530
+ onProtocolDowngrade?: (downgrade: MatrxProtocolDowngrade) => void;
531
+ }
532
+ interface CreateMatrxTransportOptions {
533
+ /** Fixed base URL, no trailing slash. Exactly one of `baseUrl` / `resolveTarget`. */
534
+ baseUrl?: string;
535
+ /**
536
+ * Per-call target resolver — for hosts whose base URL / policy headers are
537
+ * dynamic (server toggles, sandbox overrides, conversation channels).
538
+ * Resolution runs fresh on EVERY call and receives that call's request, so a
539
+ * resolver reads the call's OWN `X-Organization-Id` (a run's organization,
540
+ * `withRunOrganization`) instead of substituting the session's. May return a
541
+ * Promise to HOLD the call (e.g. while the person picks an organization), or
542
+ * throw to refuse it loudly.
543
+ */
544
+ resolveTarget?: (request: MatrxTransportRequest) => MatrxTransportTarget | Promise<MatrxTransportTarget>;
545
+ /**
546
+ * The credential source (`@ai-matrx/data`), read fresh per call so token
547
+ * refreshes are picked up mid-run. Omit only when the resolver's
548
+ * `policyHeaders` already carry the credentials.
549
+ */
550
+ credentials?: CredentialsPort;
551
+ /**
552
+ * The org-context binding. When configured, every call REQUIRES a valid org
553
+ * (fail-closed `organization_context_required` before the wire) and carries
554
+ * `X-Organization-Id`. Omit for conversation-lane transports, which send the
555
+ * org in the body only.
556
+ */
557
+ organizationId?: string | (() => string | null | undefined);
558
+ /** AI API protocol version (value or per-call getter). Default `"v2"`. */
559
+ aiApiVersion?: MatrxAiApiVersion | (() => MatrxAiApiVersion);
560
+ /** Max ms request start → response headers. Default 15_000. */
561
+ connectTimeoutMs?: number;
562
+ /**
563
+ * Max ms for the whole handshake. Default `null` (uncapped) — the port
564
+ * cannot tell a JSON call from a long-lived stream, and capping would kill
565
+ * long streams; the connect timeout is the real JSON guard.
566
+ */
567
+ totalTimeoutMs?: number | null;
568
+ /**
569
+ * HTTP failures this transport's call class fully handles as expected
570
+ * domain outcomes (e.g. 404 on runtime-operation identify). They still
571
+ * reach the package client (which maps them to typed results) but are not
572
+ * reported through `onError`.
573
+ */
574
+ expectedErrorStatuses?: readonly number[];
575
+ /** Short call-site label surfaced on `MatrxRequestInfo` (default "matrxTransport"). */
576
+ source?: string;
577
+ diagnostics?: MatrxTransportDiagnostics;
578
+ }
579
+ /**
580
+ * Build the production `MatrxTransport`. See the module doc for the pipeline;
581
+ * every knob defaults to the proven production value.
582
+ */
583
+ declare function createMatrxTransport(options: CreateMatrxTransportOptions): MatrxTransport;
584
+
585
+ /**
586
+ * `@ai-matrx/agents/matrx` — THE request pipeline every typed Matrx server call
587
+ * rides (chat package independence P9b). One implementation for every client:
588
+ * matrx-frontend's `lib/api` `callApi` and the chat package's bare-host default
589
+ * both build on it, and supply only their host facts — where the server is,
590
+ * which credential and organization ride, and where diagnostics go.
591
+ *
592
+ * What lives here (pure, no host/window/env read):
593
+ *
594
+ * - `buildMatrxRequestUrl` — path params, the legacy `/api` strip, the query;
595
+ * - `buildMatrxRequestBody` — scope injection (`organization_id` /
596
+ * `project_id` / `task_id`), the UI-only field strip, and the fail-closed
597
+ * body-vs-context organization check;
598
+ * - `bareStatusSentence` / `isBareTransportCode` / `honestTransportMessage` —
599
+ * a bare status code is never a sentence at a person;
600
+ * - `parseMatrxNdjsonResponse` — a response body as typed envelopes, a broken
601
+ * body reader classified as a resumable `StreamTransportError`;
602
+ * - `executeMatrxCall` — the JSON or NDJSON execution over the v2 → v1
603
+ * protocol fallback, with the stream callbacks and `consumeStream`;
604
+ * - `sendMatrxRequest` / `readMatrxJsonResponse` — the raw-`Response` lane
605
+ * for a host's imperative client (multipart bodies, byte downloads,
606
+ * response-aware callers): one deadline, a caller abort kept as itself, and
607
+ * the ONE HTTP error parser;
608
+ * - `buildSafeRequestLog` / `redactUrlForRequestLog` /
609
+ * `shouldReportMatrxCallError` — the log and capture policy.
610
+ *
611
+ * The error envelope (`MatrxCallError`) and its ONE classifier
612
+ * (`normalizeMatrxError`) live in `./client`.
613
+ */
614
+
615
+ type MatrxHttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
616
+ /**
617
+ * Every context dimension a call may carry. `organization_id`, `project_id`
618
+ * and `task_id` are injected into the body; `user_id` rides the credential and
619
+ * `conversation_id` the path or an explicit body field — never injected.
620
+ */
621
+ interface MatrxCallScope {
622
+ user_id?: string;
623
+ organization_id?: string;
624
+ project_id?: string;
625
+ task_id?: string;
626
+ conversation_id?: string;
627
+ }
628
+ interface MatrxCallResult<T = unknown> {
629
+ /** Parsed JSON response body (non-streaming calls only). */
630
+ data?: T;
631
+ /** Server-assigned request id (response header). */
632
+ requestId?: string;
633
+ /** Server-assigned conversation id (response header). */
634
+ conversationId?: string;
635
+ /** Set when the call failed with an HTTP error response. */
636
+ error?: MatrxCallError;
637
+ }
638
+ type MatrxQueryScalar = string | number | boolean;
639
+ /**
640
+ * Query values: a scalar is sent as itself, an array repeats the key (the
641
+ * FastAPI repeatable-parameter convention), `null` / `undefined` are omitted.
642
+ */
643
+ type MatrxQueryParams = Record<string, MatrxQueryScalar | readonly MatrxQueryScalar[] | null | undefined>;
644
+ /**
645
+ * The full URL for one call: `{param}` segments substituted (encoded), the
646
+ * legacy `/api` prefix stripped (server routes no longer live under it), and
647
+ * the query appended.
648
+ */
649
+ declare function buildMatrxRequestUrl(baseUrl: string, pathTemplate: string, pathParams?: Record<string, string>, queryParams?: MatrxQueryParams): string;
650
+ /**
651
+ * Client capability flags that must never reach the server — the server's
652
+ * request schemas reject them.
653
+ */
654
+ declare const MATRX_UI_ONLY_BODY_FIELDS: ReadonlySet<string>;
655
+ /**
656
+ * The final request body: UI-only fields stripped, scope fields injected.
657
+ *
658
+ * A caller's `organization_id: null` (or blank) means "I have none of my own",
659
+ * never "send this for a different organization" — it is dropped and the
660
+ * scope's organization injected. A real value that disagrees with the scope
661
+ * is refused (`organization_context_mismatch`). With no scope organization
662
+ * (the org-less guest lane, an org-free read) nothing is injected for it.
663
+ * Other scope fields keep caller-wins behaviour.
664
+ */
665
+ declare function buildMatrxRequestBody(body: unknown, scope: MatrxCallScope): Record<string, unknown>;
666
+ /** True when a candidate sentence is really just the status line wearing words. */
667
+ declare function isBareTransportCode(text: string | null | undefined): boolean;
668
+ /**
669
+ * A BARE STATUS CODE IS NEVER A SENTENCE. What a person reads when the server
670
+ * answered an error with no readable message. The status itself rides
671
+ * `error.status`, which is what code branches on — this is only the words.
672
+ */
673
+ declare function bareStatusSentence(status: number): string;
674
+ /** `raw` unless it is empty or only a status line; then the status sentence. */
675
+ declare function honestTransportMessage(raw: string, status: number | undefined): string;
676
+ /** Request metadata safe to log: secret headers redacted, the body as its shape only. */
677
+ declare function buildSafeRequestLog(headers: Record<string, string>, body: unknown): {
678
+ headers: Record<string, string>;
679
+ body: Record<string, unknown>;
680
+ };
681
+ /** The URL with every query value redacted. */
682
+ declare function redactUrlForRequestLog(url: string): string;
683
+ /** False only for an HTTP status the call site declared an expected outcome. */
684
+ declare function shouldReportMatrxCallError(status: number | null | undefined, expectedErrorStatuses: readonly number[] | undefined): boolean;
685
+ interface MatrxStreamIds {
686
+ requestId: string | null;
687
+ conversationId: string | null;
688
+ }
689
+ interface MatrxStreamParse<E = MatrxStreamEnvelope> extends MatrxStreamIds {
690
+ events: AsyncGenerator<E, void, undefined>;
691
+ }
692
+ interface ParseMatrxNdjsonResponseHooks {
693
+ /** Every envelope, before the consumer sees it. */
694
+ onEvent?: (event: MatrxStreamEnvelope, ids: MatrxStreamIds) => void;
695
+ /** A broken body reader, already classified, before it is thrown. */
696
+ onTransportError?: (error: BackendApiError, ids: MatrxStreamIds) => void;
697
+ onMalformedLine?: (issue: MatrxNdjsonIssue) => void;
698
+ onUnknownEnvelope?: (value: unknown) => void;
699
+ }
700
+ /**
701
+ * A Matrx NDJSON response as typed envelopes. The ids come from headers, so
702
+ * they are available before any event. A body that breaks mid-run is a
703
+ * TRANSPORT loss (the run may still finish server-side and is reattachable) —
704
+ * thrown as `StreamTransportError`, never a failed run; an abort ends quietly.
705
+ */
706
+ declare function parseMatrxNdjsonResponse(response: Response, signal?: AbortSignal, hooks?: ParseMatrxNdjsonResponseHooks): MatrxStreamParse;
707
+ interface ExecuteMatrxCallRequest<E = MatrxStreamEnvelope> {
708
+ /** The final URL (`buildMatrxRequestUrl`). */
709
+ url: string;
710
+ method: string;
711
+ /** Every header the host binds (credential, organization, Content-Type). */
712
+ headers: Record<string, string>;
713
+ /** The assembled body (`buildMatrxRequestBody`); never sent on GET/HEAD. */
714
+ body: unknown;
715
+ /** NDJSON streaming call. */
716
+ stream?: boolean;
717
+ signal?: AbortSignal;
718
+ /** Time to response headers. Default 15_000. */
719
+ connectTimeoutMs?: number;
720
+ /** Whole-request cap for JSON calls. Default 30_000; `null` uncaps. Streams are uncapped. */
721
+ totalTimeoutMs?: number | null;
722
+ /** Fires when headers arrive, before any event. */
723
+ onStreamStart?: (requestId: string | null, conversationId: string | null) => void;
724
+ onStreamEvent?: (event: E) => void;
725
+ /**
726
+ * Take ownership of the body instead of the executor draining it (a body is
727
+ * consumed once). `onStreamEvent` is then not called; start / complete /
728
+ * error still fire.
729
+ */
730
+ consumeStream?: (response: Response, ids: MatrxStreamIds) => Promise<void>;
731
+ onStreamComplete?: (requestId: string | null, conversationId: string | null) => void;
732
+ /** Fires for an HTTP error response on a stream (thrown failures are the caller's). */
733
+ onStreamError?: (error: MatrxCallError) => void;
734
+ }
735
+ interface ExecuteMatrxCallHooks<E = MatrxStreamEnvelope> {
736
+ /** Every v2 → v1 protocol downgrade. */
737
+ onProtocolDowngrade?: (downgrade: MatrxProtocolDowngrade) => void;
738
+ /** The host's stream parser (default `parseMatrxNdjsonResponse`). */
739
+ parseStream?: (response: Response, signal?: AbortSignal) => MatrxStreamParse<E>;
740
+ }
741
+ /**
742
+ * Execute one call over the v2 → v1 protocol fallback. An HTTP error
743
+ * response resolves as `{ error }`; a thrown failure (network, timeout,
744
+ * abort, a broken stream) propagates — the caller normalizes it with
745
+ * `normalizeMatrxError` and decides what to capture.
746
+ */
747
+ declare function executeMatrxCall<T = unknown, E = MatrxStreamEnvelope>(request: ExecuteMatrxCallRequest<E>, hooks?: ExecuteMatrxCallHooks<E>): Promise<MatrxCallResult<T>>;
748
+ interface SendMatrxRequestOptions {
749
+ /** Caller cancellation; an abort propagates as the caller's own AbortError. */
750
+ signal?: AbortSignal;
751
+ /**
752
+ * Deadline (ms) from send to response headers. On expiry the call rejects
753
+ * with a `BackendApiError` coded `request_timeout` (status 504) — never a
754
+ * fake caller cancellation. Omit for no deadline.
755
+ */
756
+ timeoutMs?: number;
757
+ }
758
+ /**
759
+ * Send one request and resolve with the raw `Response`, whatever its status.
760
+ * The lane for an imperative host client whose bodies are not JSON (a
761
+ * `FormData` upload), whose answers are bytes, or which reads the response
762
+ * itself. A caller abort and a network failure propagate unchanged; only this
763
+ * call's own deadline is classified (`request_timeout`).
764
+ */
765
+ declare function sendMatrxRequest(url: string, init?: RequestInit, options?: SendMatrxRequestOptions): Promise<Response>;
766
+ /**
767
+ * A response's JSON body, or the ONE classified `BackendApiError`
768
+ * (`parseHttpError`) for a non-2xx answer. A 204 resolves `null`.
769
+ */
770
+ declare function readMatrxJsonResponse<T>(response: Response): Promise<T>;
771
+
772
+ /**
773
+ * The fail-closed organization-context kernel — ONE implementation for every
774
+ * Matrx client transport (moved in from matrx-frontend
775
+ * `lib/api/organization-context.ts` verbatim under C22; the host module is now
776
+ * a re-export of this one).
777
+ *
778
+ * Transports resolve their authoritative organization first, then use these
779
+ * functions to bind that exact value without guessing or defaulting. The
780
+ * server never manufactures an org (it 422s a blank one), so the client
781
+ * refuses before the wire: a missing or malformed org id throws
782
+ * `OrganizationContextError` instead of sending a request that cannot succeed.
783
+ */
784
+ type OrganizationContextErrorCode = "organization_context_required" | "organization_context_invalid" | "organization_context_mismatch";
785
+ declare class OrganizationContextError extends Error {
786
+ readonly code: OrganizationContextErrorCode;
787
+ constructor(code: OrganizationContextErrorCode, message: string);
788
+ }
789
+ declare const organizationOperationBrand: unique symbol;
790
+ /** A validated organization operation; construct only through the factory. */
791
+ type OrganizationOperation = Readonly<{
792
+ organization_id: string;
793
+ readonly [organizationOperationBrand]: true;
794
+ }>;
795
+ declare function createOrganizationOperation(organizationId: string): OrganizationOperation;
796
+ declare function assertOrganizationMatchesOperation(operation: OrganizationOperation, organizationId: string): void;
797
+ /**
798
+ * Normalize and validate the effective organization id for a request. The
799
+ * override (an explicit per-call value) beats the selected context value.
800
+ * Throws `organization_context_required` when neither is present and
801
+ * `organization_context_invalid` when the candidate is not a UUID.
802
+ */
803
+ declare function requireOrganizationContext(selectedOrganizationId: string | null | undefined, overrideOrganizationId?: string): string;
804
+ /**
805
+ * Bind `X-Organization-Id` onto a header bag. A pre-existing org header that
806
+ * disagrees with the context org is a `organization_context_mismatch` error —
807
+ * never silently overwritten in either direction.
808
+ */
809
+ declare function applyOrganizationContextHeader(headers: Record<string, string>, organizationId: string): Record<string, string>;
810
+ /**
811
+ * Assert that an `organization_id` query parameter (when present) matches the
812
+ * request context organization — the query string must never smuggle a
813
+ * different org past the header binding.
814
+ */
815
+ declare function assertQueryOrganizationMatchesContext(queryParams: Record<string, string | number | boolean> | undefined, organizationId: string): void;
816
+
817
+ /** `@ai-matrx/agents/matrx` — moved from matrx-frontend `lib/api/log-api-target.ts` (chat-package independence P9). */
818
+ /**
819
+ * lib/api/log-api-target.ts
820
+ *
821
+ * ONE place that logs the *final, resolved* destination of every outbound
822
+ * backend call — at the last moment, after every layer of server-address
823
+ * resolution has run and the URL can no longer change before `fetch()`.
824
+ *
825
+ * Why this exists:
826
+ * We have several independent resolution layers — `selectResolvedBaseUrl`
827
+ * (apiConfigSlice), the EC2 / sandbox override channel in
828
+ * `resolve-base-url.ts`, per-call `forceBaseUrl` / `baseUrlOverride`, and
829
+ * the env-var fallback in `python-client.ts`. A bug in any one of them can
830
+ * silently send traffic to the wrong host (or to the sandbox URL, which is
831
+ * reserved for a single rare case). This logger makes the actual target of
832
+ * EVERY call visible in the console so misrouting is impossible to miss.
833
+ *
834
+ * Call this immediately before the `fetch()` / `resilientFetch()` /
835
+ * `XMLHttpRequest.open()` that uses the URL — never earlier.
836
+ */
837
+ interface ApiTargetLogContext {
838
+ /** Short label of the call site, e.g. "callApi", "python-client.postJson". */
839
+ source: string;
840
+ /** HTTP method when known. */
841
+ method?: string;
842
+ /**
843
+ * Resolution channel when the call site knows it — "global" / "override" /
844
+ * "ec2-dedicated" / "force" / "env-fallback". Surfaces sandbox routing.
845
+ */
846
+ channel?: string;
847
+ /** The active server environment key from apiConfigSlice, when available. */
848
+ activeServer?: string;
849
+ /** Anything else worth seeing (conversationId, requestId, etc.). */
850
+ [extra: string]: unknown;
851
+ }
852
+ /**
853
+ * Log the final resolved target URL for an outbound backend call.
854
+ *
855
+ * Normal calls log at `console.info` with a green tag. Calls whose host looks
856
+ * like a sandbox/override target log at `console.error` with a red tag so they
857
+ * are impossible to miss while hunting a misrouting bug.
858
+ */
859
+ declare function logApiTarget(url: string, context: ApiTargetLogContext): void;
860
+
861
+ /**
862
+ * `@ai-matrx/agents/matrx` — the single primitive that transforms a server
863
+ * endpoint PATH (never the base URL) before it is sent: an exact per-path
864
+ * override, else a global version prefix. Moved from matrx-frontend
865
+ * `lib/api/resolve-endpoint-path.ts` (chat-package independence P9).
866
+ */
867
+ interface EndpointOverrideConfig {
868
+ /**
869
+ * Global leading version segment applied to every path that has no exact
870
+ * pathOverride. `null` / empty → no version transform. The value may be
871
+ * given with or without a leading slash ("v2" and "/v2" both work).
872
+ */
873
+ apiVersion?: string | null;
874
+ /**
875
+ * Exact-match canonical-path → replacement-path map. Keys are the path
876
+ * template exactly as written in `ENDPOINTS` / the generated schema
877
+ * (including any `{param}` segments). Wins over `apiVersion`.
878
+ */
879
+ pathOverrides?: Record<string, string>;
880
+ }
881
+ /**
882
+ * Resolve the final path for a canonical endpoint path, applying any active
883
+ * override layers. Pure — given the same inputs it always returns the same
884
+ * path. Callers prepend the resolved base URL themselves.
885
+ *
886
+ * @param canonicalPath The path exactly as declared in ENDPOINTS / the schema.
887
+ * @param config The active override layers (read from Redux).
888
+ */
889
+ declare function resolveEndpointPath(canonicalPath: string, config?: EndpointOverrideConfig | null): string;
890
+
891
+ /**
892
+ * Stamp the admin desktop-target preference onto a request body — but ONLY
893
+ * when the body already declares a live `desktop-native` capability.
894
+ *
895
+ * The preference (Settings → Admin Server, `adminPreferences.
896
+ * desktopTargetInstanceId`, persisted local-only) may DIRECT delegation to a
897
+ * specific desktop; it must never DECLARE one. Fabricating the capability
898
+ * here used to override the presence gate in `desktop-native.provider.ts`,
899
+ * so a stale preference silently rode every AI turn: the server stamped
900
+ * `target_instance_id` onto delegated tool calls while the smart-input
901
+ * indicator truthfully showed no desktop bound, and browser-executed tools
902
+ * (war_room_*, ui-first) 404'd their own /tool_results (submission-binding
903
+ * check) and wedged the turn.
904
+ */
905
+ declare function applyDesktopTargetToRequestBody(body: unknown, targetInstanceId: string | null | undefined): void;
906
+
907
+ /**
908
+ * `@ai-matrx/agents/matrx` — moved from matrx-frontend `lib/api/run-wait.ts` (chat-package
909
+ * independence P9). The knob read is injected (`resolveRunWaitWith`); the host wraps it.
910
+ */
911
+ /**
912
+ * How long a client waits for the server to START answering a run — per output
913
+ * kind, as an organization knob.
914
+ *
915
+ * 🚨 THE DEFECT (2026-09-22). Pressing Run in the agent builder on an image
916
+ * agent ("Product Photo Studio", Gemini 3.1 Flash Image) died on the client with
917
+ * "Connection timed out after 15000ms". The builder's manual path was the only
918
+ * agent stream path carrying a fixed 15-second `connectTimeoutMs`, and that
919
+ * timer does not measure a TCP connect: aidream's prepared-streaming routes
920
+ * (`create_prepared_streaming_response`) finish request PREPARATION — auth, the
921
+ * request lane, conversation create, resolving attached files — BEFORE the
922
+ * response headers go out. A busy server or a draining deploy pushed prep past
923
+ * 15 s, the client aborted, and aborting before the headers dropped the server
924
+ * run with it (conversation created, no messages).
925
+ *
926
+ * THE RULE. A generation is a job, not a request with a stopwatch:
927
+ * - Once the stream is open there is NO wall-clock cap — the server heartbeat
928
+ * (every 5 s) keeps the watchdog fed and the run ends on the stream's own
929
+ * `end` event.
930
+ * - The one cap that remains — "the server never started answering" — is an
931
+ * organization knob per output kind (`agents.run_wait.<kind>_seconds`),
932
+ * with generous agent-set defaults registered in the knob table, never a
933
+ * constant here.
934
+ * - When that cap is actually hit, the person is told the truth: which model,
935
+ * how long we waited, and that the server may still be working — with a
936
+ * door to the conversation, because the run can finish without us.
937
+ *
938
+ * If the knob snapshot cannot be read the wait falls back to the stream
939
+ * lifetime backstop (announced loudly), never to a number nobody chose.
940
+ */
941
+ /** What a run produces. Anything but text is a job (seconds to minutes). */
942
+ /** Every output kind a run can wait on — one `<kind>_seconds` knob each. */
943
+ declare const RUN_OUTPUT_KINDS: readonly ["text", "image", "video", "audio"];
944
+ type RunOutputKind = (typeof RUN_OUTPUT_KINDS)[number];
945
+ declare const RUN_WAIT_KNOB_FEATURE = "agents.run_wait";
946
+ /**
947
+ * Stream lifetime backstop — the same 24 hours the agent run path
948
+ * (`runAiStream`) has always used. It is the ceiling for a stream that keeps
949
+ * heartbeating forever, not a wait anyone tunes; the heartbeat watchdog is the
950
+ * real liveness check. Also the first-response wait when the knob cannot be
951
+ * read (setTimeout's own ceiling is ~24.8 days, so this stays safe).
952
+ */
953
+ declare const RUN_STREAM_LIFETIME_BACKSTOP_MS: number;
954
+ declare function isJobOutputKind(kind: RunOutputKind): boolean;
955
+ /**
956
+ * The output kind from a model's declared output modalities (the parsed
957
+ * `capabilities.output` list). Video outranks image outranks audio: a model
958
+ * that can emit several is waited on as its slowest.
959
+ */
960
+ declare function runOutputKindFromModalities(output: readonly string[] | null | undefined): RunOutputKind;
961
+ declare function runWaitKnobKey(kind: RunOutputKind): string;
962
+ interface RunWait {
963
+ /** Milliseconds the client waits for the stream to open. */
964
+ firstResponseMs: number;
965
+ /** Seconds as configured, or null when the knob could not be read. */
966
+ seconds: number | null;
967
+ kind: RunOutputKind;
968
+ }
969
+ /**
970
+ * Resolve the first-response wait for one run from the organization's knob.
971
+ * Never throws — a run is never refused because a setting could not load; the
972
+ * failure is announced and the wait becomes the lifetime backstop.
973
+ */
974
+ /**
975
+ * Reads one settings-register knob for an organization (and person). The HOST
976
+ * supplies it — matrx-frontend: `ensureEffectiveKnob` (lib/scoped-config).
977
+ */
978
+ type RunWaitKnobReader = (organizationId: string, userId: string | null, knob: {
979
+ feature: string;
980
+ key: string;
981
+ }) => Promise<unknown>;
982
+ declare function resolveRunWaitWith(readKnob: RunWaitKnobReader, organizationId: string | null | undefined, userId: string | null | undefined, kind: RunOutputKind): Promise<RunWait>;
983
+ /** "Generating an image with Gemini 3.1 Flash Image" — the job's working line. */
984
+ declare function runJobLabel(kind: RunOutputKind, modelLabel: string | null): string;
985
+ /**
986
+ * The honest sentence for a first-response timeout. The server may still
987
+ * finish the run, so the sentence says so and the caller attaches a door to
988
+ * the conversation.
989
+ */
990
+ declare function runWaitTimeoutMessage(kind: RunOutputKind, modelLabel: string | null, seconds: number | null): string;
991
+
992
+ /**
993
+ * `@ai-matrx/agents/matrx` — the client warm-up primitive.
994
+ *
995
+ * Contract: common-docs `systems/architecture/warm-cache/CONTRACT.md`.
996
+ *
997
+ * A page that knows what the person is about to need says so the moment they
998
+ * land: `warmup.warm([{ key: "agent", id }], "route")`. Calls made inside a
999
+ * ~50ms window coalesce into ONE authenticated `POST /ai/warm`; the same item
1000
+ * is not sent twice for the same scope within 60s; the request is
1001
+ * fire-and-forget and NEVER throws, rejects, or blocks. Signed
1002
+ * out → nothing is sent. Until the server endpoint exists the call answers 404
1003
+ * and the primitive stays silent (one `console.debug`, once).
1004
+ *
1005
+ * Auth, base URL and `X-Organization-Id` are the injected `MatrxTransport`'s
1006
+ * job, exactly as for every other call in this package.
1007
+ */
1008
+
1009
+ declare const WARM_PATH: "/ai/warm";
1010
+ /** Why the warm was requested (the server records it per accepted item). */
1011
+ type WarmReason = "session" | "route" | "org_change" | "agent_change" | "conversation_change";
1012
+ /** One thing to warm. `core` carries no id; the others name their record. */
1013
+ interface WarmItem {
1014
+ key: "core" | "org" | "agent" | "conversation" | (string & {});
1015
+ id?: string | undefined;
1016
+ }
1017
+ interface CreateWarmupOptions {
1018
+ /** The host's transport — base URL and credentials ride on it. */
1019
+ transport: MatrxTransport;
1020
+ /**
1021
+ * True only for a signed-in person. Read fresh on every use. While false (or
1022
+ * while `scope` is empty) requested warms are QUEUED, not dropped, and go out
1023
+ * when the host calls `notifyScopeChanged()` after the person and org are ready.
1024
+ */
1025
+ isSignedIn: () => boolean;
1026
+ /**
1027
+ * The dedupe scope, read fresh: the person plus the active org
1028
+ * (`${userId}:${orgId}`). Null/empty = not ready yet. Items go out only under
1029
+ * the scope they were requested in; a scope change drops the stale ones (the
1030
+ * page re-issues for the new scope) so nothing is ever sent with the wrong org.
1031
+ */
1032
+ scope?: () => string | null | undefined;
1033
+ /**
1034
+ * The active org id, read at send time and put on the request as
1035
+ * `X-Organization-Id` so the request names the org its scope names.
1036
+ */
1037
+ organizationId?: () => string | null | undefined;
1038
+ /** Coalescing window. Default 50ms. */
1039
+ coalesceMs?: number;
1040
+ /** Per-scope per-item dedupe window after a 2xx. Default 60_000ms. */
1041
+ dedupeMs?: number;
1042
+ /** Clock, for tests. Default `Date.now`. */
1043
+ now?: () => number;
1044
+ }
1045
+ interface Warmup {
1046
+ /** Queue items for the next coalesced `POST /ai/warm`. Never throws. */
1047
+ warm(items: readonly WarmItem[], reason: WarmReason): void;
1048
+ /** The current scope, or "" while the person/org are not ready. */
1049
+ currentScope(): string;
1050
+ /** Host calls this when the person or org may have changed; flushes the queue. */
1051
+ notifyScopeChanged(): void;
1052
+ /** Subscribe to scope changes (for `useSyncExternalStore`). Returns unsubscribe. */
1053
+ subscribe(listener: () => void): () => void;
1054
+ }
1055
+ declare function createWarmup(options: CreateWarmupOptions): Warmup;
1056
+ /**
1057
+ * Add `<link rel="preconnect">` for the server origin, once per origin, so the
1058
+ * TLS handshake is done before the first AI call. No-op outside a browser.
1059
+ */
1060
+ declare function preconnectServer(baseUrl: string | null | undefined): void;
1061
+ /** Test seam: forget which origins were preconnected. */
1062
+ declare function resetPreconnectedForTests(): void;
1063
+
1064
+ /**
1065
+ * Internal request plumbing for `@ai-matrx/agents/matrx`. Not part of the
1066
+ * public surface — `matrx/index.ts` deliberately does not re-export this
1067
+ * module. Everything here is pure: no globals, no work at import time.
1068
+ */
1069
+
1070
+ /** Encode one path segment (an id) safely into a server-relative path. */
1071
+ declare function encodePathSegment(value: string): string;
1072
+ type QueryValue = string | number | boolean | readonly string[] | undefined;
1073
+ /**
1074
+ * Build a query string. Array values repeat the key (`kind=a&kind=b` — the
1075
+ * FastAPI repeatable-parameter convention); undefined values are omitted.
1076
+ * Returns "" or a string starting with "?".
1077
+ */
1078
+ declare function buildQuery(params: Record<string, QueryValue>): string;
1079
+ interface JsonRequestOptions {
1080
+ method: "GET" | "POST";
1081
+ body?: unknown;
1082
+ signal?: AbortSignal;
1083
+ }
1084
+ /**
1085
+ * Execute a JSON request through the transport. Throws `MatrxApiError` on a
1086
+ * non-2xx response; resolves with the parsed JSON body otherwise.
1087
+ */
1088
+ declare function requestJson<T>(transport: MatrxTransport, path: string, options: JsonRequestOptions): Promise<T>;
1089
+ /**
1090
+ * Options for every streaming call, riding the NDJSON kernel's contract.
1091
+ * Public via `./run`'s re-export.
1092
+ */
1093
+ interface MatrxStreamCallOptions {
1094
+ /** Abort the fetch and end the events iterator. */
1095
+ signal?: AbortSignal;
1096
+ /** Bounded background read-ahead (see `stream/ndjson`). */
1097
+ maxReadAhead?: number;
1098
+ /** Malformed NDJSON is non-fatal but must never disappear silently. */
1099
+ onMalformedLine?: (issue: MatrxNdjsonIssue) => void;
1100
+ /** Valid JSON with no recognized Matrx envelope. */
1101
+ onUnknownEnvelope?: (value: unknown) => void;
1102
+ /** Observe every valid envelope in its exact wire form. */
1103
+ onValidEnvelope?: (observation: MatrxStreamEnvelopeObservation) => void;
1104
+ }
1105
+ /**
1106
+ * A live agent run: the server-assigned ids (from response headers, available
1107
+ * BEFORE any event) and the normalized event stream. Public via `./run`.
1108
+ */
1109
+ interface MatrxRunHandle {
1110
+ /** `X-Request-ID` — the ONLY id `POST /ai/cancel/{request_id}` accepts. */
1111
+ requestId: string | null;
1112
+ /** `X-Conversation-ID` — the server's conversation identity. */
1113
+ conversationId: string | null;
1114
+ /** Normalized `{event, data}` envelopes through the ONE wire kernel. */
1115
+ events: AsyncGenerator<MatrxStreamEnvelope, void, undefined>;
1116
+ /** The raw response, for hosts that need headers/status beyond the ids. */
1117
+ response: Response;
1118
+ }
1119
+ /**
1120
+ * Wrap a validated streaming Response into the run handle — the ONE place
1121
+ * the id headers are read and the NDJSON kernel is attached (`./run` and
1122
+ * `./operations`' rejoin share it).
1123
+ */
1124
+ declare function toRunHandle(response: Response, options: MatrxStreamCallOptions): MatrxRunHandle;
1125
+ interface StreamRequestOptions {
1126
+ method: "GET" | "POST";
1127
+ body?: unknown;
1128
+ /** Extra wire-semantic headers (`Accept`, `Last-Event-ID`). */
1129
+ headers?: Record<string, string>;
1130
+ signal?: AbortSignal;
1131
+ }
1132
+ /**
1133
+ * Execute a streaming request. Throws `MatrxApiError` on a non-2xx response
1134
+ * (reading the error body as JSON when possible) or when a 2xx response
1135
+ * carries no body; resolves with the validated `Response` otherwise.
1136
+ */
1137
+ declare function requestStream(transport: MatrxTransport, path: string, options: StreamRequestOptions): Promise<Response>;
1138
+
1139
+ /**
1140
+ * The conversation-start contract — client-minted `conversation_id`, `is_new`,
1141
+ * `store` — typed exactly per the cross-repo System of Record
1142
+ * (`common-docs/systems/agents/conversation-start-contract/FEATURE.md`;
1143
+ * server truth `aidream/services/conversation_context/scope.py::
1144
+ * ConversationStartRequest`).
1145
+ *
1146
+ * Every request that STARTS a conversation sends all three fields, no
1147
+ * defaults:
1148
+ *
1149
+ * | `is_new` | `store` | Result |
1150
+ * |----------|---------|----------------------------------------------------------|
1151
+ * | true | true | Create the row with the caller's id — 409 if it exists |
1152
+ * | true | false | No row. The id is correlation only (ephemeral run) |
1153
+ * | false | true | Continue it — 404 if the caller doesn't own it |
1154
+ * | false | false | Ephemeral run on a known id; nothing read, nothing written |
1155
+ *
1156
+ * `store` is the ONLY ephemeral signal; `is_new` is the caller's assertion
1157
+ * about the id, never a persistence switch. `prior_messages` (the client-owned
1158
+ * transcript of an ephemeral multi-turn run) is only valid with
1159
+ * `store: false` — the union below makes the invalid combination
1160
+ * unrepresentable, mirroring the server's 422.
1161
+ *
1162
+ * Continue routes (`POST /ai/conversations/{id}`) take the id from the path
1163
+ * and do not carry this triple.
1164
+ */
1165
+ /** Recursive JSON value — the package's honest type for free-form wire bags. */
1166
+ type MatrxJsonValue = string | number | boolean | null | MatrxJsonValue[] | {
1167
+ [key: string]: MatrxJsonValue;
1168
+ };
1169
+ /** A JSON object on the wire. */
1170
+ type MatrxJsonObject = {
1171
+ [key: string]: MatrxJsonValue;
1172
+ };
1173
+ /**
1174
+ * One LLM message on the request wire — `prior_messages` entries for
1175
+ * stateless multi-turn runs. Mirrors aidream's `ChatMessageInput`
1176
+ * (`aidream/schemas/messages.py`, `extra="allow"` — additional provider
1177
+ * fields round-trip untouched).
1178
+ */
1179
+ interface MatrxChatMessage {
1180
+ role: string;
1181
+ content?: string | MatrxJsonValue[] | null;
1182
+ name?: string | null;
1183
+ tool_call_id?: string | null;
1184
+ tool_calls?: MatrxJsonObject[] | null;
1185
+ [extra: string]: MatrxJsonValue | undefined;
1186
+ }
1187
+ /** `is_new: true, store: true` — create the row with the caller's id. */
1188
+ interface MatrxStoredConversationCreate {
1189
+ conversation_id: string;
1190
+ is_new: true;
1191
+ store: true;
1192
+ }
1193
+ /** `is_new: false, store: true` — continue an owned stored conversation via a start route. */
1194
+ interface MatrxStoredConversationContinue {
1195
+ conversation_id: string;
1196
+ is_new: false;
1197
+ store: true;
1198
+ }
1199
+ /**
1200
+ * `store: false` — ephemeral: nothing read, nothing written; the id is the
1201
+ * caller's correlation handle. This is the ONLY member that may carry
1202
+ * `prior_messages` (the server 422s a client transcript on a stored run).
1203
+ */
1204
+ interface MatrxEphemeralConversation {
1205
+ conversation_id: string;
1206
+ is_new: boolean;
1207
+ store: false;
1208
+ prior_messages?: MatrxChatMessage[];
1209
+ }
1210
+ /** The full conversation-start triple, one member per contract cell. */
1211
+ type MatrxConversationStart = MatrxStoredConversationCreate | MatrxStoredConversationContinue | MatrxEphemeralConversation;
1212
+ /** Mint a fresh client-side conversation id (the contract requires the CLIENT to mint it). */
1213
+ declare function mintMatrxConversationId(): string;
1214
+ /** Start a NEW stored conversation (`is_new: true, store: true`). */
1215
+ declare function newStoredConversationStart(conversationId?: string): MatrxStoredConversationCreate;
1216
+ /**
1217
+ * Continue an EXISTING stored conversation through a start route
1218
+ * (`is_new: false, store: true` — 404 when the caller doesn't own the id).
1219
+ * Prefer `continueAgentConversation` (the dedicated continue route) for
1220
+ * ordinary follow-up turns.
1221
+ */
1222
+ declare function continueStoredConversationStart(conversationId: string): MatrxStoredConversationContinue;
1223
+ /**
1224
+ * Start a NEW ephemeral run (`is_new: true, store: false`) — a freshly minted
1225
+ * correlation id, nothing persisted.
1226
+ */
1227
+ declare function newEphemeralConversationStart(conversationId?: string): MatrxEphemeralConversation;
1228
+ /**
1229
+ * Continue an ephemeral multi-turn run (`is_new: false, store: false`): the
1230
+ * CLIENT owns the transcript and replays it as `prior_messages` (ordered
1231
+ * oldest-first) because the server wrote no rows to rebuild from. The server
1232
+ * still owns the agent definition, model, tools, and system prompt.
1233
+ */
1234
+ declare function continueEphemeralConversationStart(conversationId: string, priorMessages: MatrxChatMessage[]): MatrxEphemeralConversation;
1235
+
1236
+ /**
1237
+ * Runtime operations — the canonical reconnect & resume read surface of the
1238
+ * execution spine, over the `MatrxTransport` port.
1239
+ *
1240
+ * Server truth (verified against aidream source,
1241
+ * `aidream/api/routers/runtime_operations.py` + `aidream/services/runtime/
1242
+ * reconnect.py`; mounted at bare `/runtime`):
1243
+ * - `GET /runtime/operations/{request_id}` — identify by `X-Request-ID`
1244
+ * - `GET /runtime/operations/by-link/{kind}/{id}` — identify by feature record
1245
+ * - `GET /runtime/executions/{id}/events` — durable seq-cursored page
1246
+ * - `GET /runtime/executions/{id}/events/stream` — SSE replay-then-follow,
1247
+ * `id:` = per-tree seq, reconnect with `Last-Event-ID`
1248
+ * - `POST /runtime/operations/{request_id}/rejoin` — replay + follow the
1249
+ * ORIGINAL NDJSON response while its detached task is alive (409 when live
1250
+ * delivery is unavailable — fall back to the durable lifecycle stream)
1251
+ *
1252
+ * The contract: identify → recover durable progress → follow live → re-query
1253
+ * the final result from the feature's own record. Token text is deliberately
1254
+ * never replayed on the lifecycle stream — that is what `/rejoin` is for.
1255
+ *
1256
+ * The SSE wire rides the package's own `stream/sse` kernel. This module owns
1257
+ * ONE connection's semantics (frames → typed events, cursor advancement,
1258
+ * terminal `end`); stall timers, retry budgets, and reconnect loops stay host
1259
+ * policy — every yielded item carries the cursor the next attempt resumes from.
1260
+ */
1261
+
1262
+ /** `matrx_runtime.models.ExecutionStatus` — the only progress column. */
1263
+ type MatrxRuntimeExecutionStatus = "pending" | "running" | "paused" | "waiting_input" | "completed" | "failed" | "cancelled";
1264
+ declare const TERMINAL_MATRX_RUNTIME_STATUSES: ReadonlySet<MatrxRuntimeExecutionStatus>;
1265
+ /** One durable spine event on the wire (`OperationEvent`) — `seq` is the reconnect cursor. */
1266
+ interface MatrxRuntimeOperationEvent {
1267
+ seq: number | null;
1268
+ /** Lifecycle vocabulary: created | started | paused | resumed | waiting_input | completed | failed | cancelled | checkpoint_saved | note. */
1269
+ kind: string;
1270
+ execution_id: string;
1271
+ root_execution_id: string | null;
1272
+ detail: MatrxJsonObject | null;
1273
+ created_at: string | null;
1274
+ }
1275
+ /** One root execution as a reconnecting client sees it (`OperationView`). */
1276
+ interface MatrxRuntimeOperationView {
1277
+ execution_id: string;
1278
+ /** Durable request identity — feeds `/rejoin` and no-prompt resume recovery. */
1279
+ request_id: string | null;
1280
+ /**
1281
+ * The root run's request id — the rejoin handle when by-link returns the
1282
+ * LINKED execution (with its own status) instead of the root. Absent on
1283
+ * older servers; `request_id` then is the handle.
1284
+ */
1285
+ root_request_id?: string | null;
1286
+ type: string;
1287
+ status: MatrxRuntimeExecutionStatus;
1288
+ is_terminal: boolean;
1289
+ waiting_input: boolean;
1290
+ /** Decimal on the wire — may arrive as number or string; display-only. */
1291
+ cost: number | string;
1292
+ meters: Record<string, number | string>;
1293
+ link_kind: string | null;
1294
+ link_id: string | null;
1295
+ error: MatrxJsonObject | null;
1296
+ created_at: string | null;
1297
+ started_at: string | null;
1298
+ ended_at: string | null;
1299
+ last_event_seq: number;
1300
+ events_path: string;
1301
+ stream_path: string;
1302
+ }
1303
+ interface MatrxOperationStatusResponse {
1304
+ request_id: string;
1305
+ operation_count: number;
1306
+ operations: MatrxRuntimeOperationView[];
1307
+ }
1308
+ interface MatrxOperationsByLinkResponse {
1309
+ link_kind: string;
1310
+ link_id: string;
1311
+ operation_count: number;
1312
+ operations: MatrxRuntimeOperationView[];
1313
+ }
1314
+ interface MatrxOperationEventsPage {
1315
+ execution_id: string;
1316
+ root_execution_id: string;
1317
+ events: MatrxRuntimeOperationEvent[];
1318
+ /** Feeds the next page or the SSE `Last-Event-ID` — polling and push share ONE cursor. */
1319
+ next_after_seq: number;
1320
+ has_more: boolean;
1321
+ root_status: MatrxRuntimeExecutionStatus;
1322
+ root_is_terminal: boolean;
1323
+ }
1324
+ /**
1325
+ * Where is my operation? Resolves an `X-Request-ID` to its root execution(s).
1326
+ * Returns null on 404 — missing and unowned share one shape by design
1327
+ * (existence is never leaked).
1328
+ */
1329
+ declare function getRuntimeOperationStatus(transport: MatrxTransport, requestId: string, options?: {
1330
+ signal?: AbortSignal;
1331
+ executionId?: string;
1332
+ }): Promise<MatrxOperationStatusResponse | null>;
1333
+ /**
1334
+ * Operations for a feature record — e.g. `("conversation", conversationId)`,
1335
+ * `("workflow", runId)`, `("agent_run", runId)`. Newest first; unowned trees
1336
+ * omitted. Returns null on 404 (surface absent, or the caller owns nothing —
1337
+ * one shape by design).
1338
+ */
1339
+ declare function getRuntimeOperationsByLink(transport: MatrxTransport, linkKind: string, linkId: string, options?: {
1340
+ limit?: number;
1341
+ signal?: AbortSignal;
1342
+ }): Promise<MatrxOperationsByLinkResponse | null>;
1343
+ /**
1344
+ * Durable progress page for the whole operation TREE:
1345
+ * `GET /runtime/executions/{id}/events?after_seq=…`. Pass any node id — it
1346
+ * resolves to the root.
1347
+ */
1348
+ declare function listRuntimeOperationEvents(transport: MatrxTransport, executionId: string, options?: {
1349
+ afterSeq?: number;
1350
+ limit?: number;
1351
+ /** Repeatable event-kind filter. */
1352
+ kinds?: readonly string[];
1353
+ signal?: AbortSignal;
1354
+ }): Promise<MatrxOperationEventsPage>;
1355
+ /**
1356
+ * One item from the follow stream. Every item carries `cursor` — the highest
1357
+ * event seq seen so far, which is exactly the `Last-Event-ID` a reconnect
1358
+ * resumes from (host retry policy owns the reconnect loop).
1359
+ */
1360
+ type MatrxOperationFollowEvent = {
1361
+ /** A parsed durable spine event. */
1362
+ type: "event";
1363
+ event: MatrxRuntimeOperationEvent;
1364
+ /** The frame's SSE `id:` as an integer, when it carried one. */
1365
+ seq: number | null;
1366
+ cursor: number;
1367
+ } | {
1368
+ /**
1369
+ * A frame that carried no deliverable event — a comment heartbeat, an
1370
+ * unknown event name, or a malformed payload (also surfaced through
1371
+ * `onMalformedFrame`). ANY parsed frame proves the wire is alive: hosts
1372
+ * reset stall timers and retry budgets on it.
1373
+ */
1374
+ type: "liveness";
1375
+ cursor: number;
1376
+ } | {
1377
+ /** The server's terminal frame — the root settled; the stream is over. */
1378
+ type: "end";
1379
+ status: MatrxRuntimeExecutionStatus | null;
1380
+ cursor: number;
1381
+ };
1382
+ interface FollowRuntimeOperationOptions {
1383
+ /** Resume cursor — the operation view's `last_event_seq` (0 = from start). */
1384
+ lastEventSeq?: number;
1385
+ /** Abort the follow — the generator simply ends. */
1386
+ signal?: AbortSignal;
1387
+ /** A frame whose payload failed to parse — never silently dropped. */
1388
+ onMalformedFrame?: (frame: MatrxSseFrame, error: unknown) => void;
1389
+ /** Unterminated trailing SSE text at stream end (diagnostic, never an event). */
1390
+ onIncomplete?: (text: string) => void;
1391
+ }
1392
+ /**
1393
+ * Follow ONE SSE connection of an operation's lifecycle stream:
1394
+ * `GET /runtime/executions/{id}/events/stream` with `Last-Event-ID` when
1395
+ * resuming past 0. Replays from the cursor, then follows live; a
1396
+ * WAITING_INPUT park keeps it open (a resume re-attaches to the same
1397
+ * execution and its events continue here). Ends after yielding
1398
+ * `{type: "end"}` when the root settles; a server close WITHOUT an end frame
1399
+ * simply ends the generator — reconnect from the last yielded `cursor` (host
1400
+ * retry policy).
1401
+ */
1402
+ declare function followRuntimeOperationEvents(transport: MatrxTransport, executionId: string, options?: FollowRuntimeOperationOptions): AsyncGenerator<MatrxOperationFollowEvent, void, undefined>;
1403
+ interface FollowRuntimeOperationToEndOptions {
1404
+ /** Resume cursor — the operation view's `last_event_seq` (0 = from start). */
1405
+ lastEventSeq?: number;
1406
+ /** Caller teardown — aborting resolves with `ended: false`. */
1407
+ signal?: AbortSignal;
1408
+ /**
1409
+ * The server pings every ~15s, so a wire that is open but silent past this
1410
+ * is dead (buffering proxy, idle-killed connection) — abort the attempt and
1411
+ * retry rather than hanging forever. Default 45_000.
1412
+ */
1413
+ stallTimeoutMs?: number;
1414
+ /**
1415
+ * Consecutive failed attempts before giving up. A single-server deployment
1416
+ * deliberately drains for 60s, then starts a new container — the default
1417
+ * budget (60 × 2s) keeps following for ~three minutes so the runtime ledger
1418
+ * can bridge that handoff. ANY parsed frame resets the budget. Default 60.
1419
+ */
1420
+ reconnectLimit?: number;
1421
+ /** Delay between attempts. Default 2_000. */
1422
+ reconnectDelayMs?: number;
1423
+ /** Fired per durable spine event (lifecycle transitions + notes). */
1424
+ onEvent: (event: MatrxRuntimeOperationEvent, seq: number | null) => void;
1425
+ /** A failed attempt (never silently swallowed when provided). */
1426
+ onAttemptError?: (error: unknown) => void;
1427
+ /** A frame whose payload failed to parse (the ledger heals gaps on reconnect). */
1428
+ onMalformedFrame?: (frame: MatrxSseFrame, error: unknown) => void;
1429
+ }
1430
+ interface FollowRuntimeOperationToEndResult {
1431
+ /** True when the server sent the terminal `end` frame. */
1432
+ ended: boolean;
1433
+ /** The root status carried on the `end` frame (when `ended`). */
1434
+ status: MatrxRuntimeExecutionStatus | null;
1435
+ }
1436
+ /**
1437
+ * Follow an operation's lifecycle stream TO ITS END — the full production
1438
+ * reconnect policy over `followRuntimeOperationEvents`: replay-then-follow
1439
+ * with bounded reconnects, a stall watchdog, and durable `Last-Event-ID`
1440
+ * cursor advancement across attempts.
1441
+ *
1442
+ * Resolves `{ended: true, status}` on the server's `end` frame (the operation
1443
+ * settled); `{ended: false}` when the caller aborted or every reconnect
1444
+ * attempt failed. A WAITING_INPUT park keeps the stream open by design — a
1445
+ * resume re-attaches to the same execution and its events continue arriving
1446
+ * on the same cursor. Any parsed frame — comment heartbeats included —
1447
+ * proves the wire is alive and resets both the stall timer and the retry
1448
+ * budget.
1449
+ */
1450
+ declare function followRuntimeOperationToEnd(transport: MatrxTransport, executionId: string, options: FollowRuntimeOperationToEndOptions): Promise<FollowRuntimeOperationToEndResult>;
1451
+ /**
1452
+ * Rejoin the ORIGINAL NDJSON response while its detached task is still alive:
1453
+ * `POST /runtime/operations/{request_id}/rejoin`. Replays the response from
1454
+ * frame one, then continues live — every frame is sequence-stamped
1455
+ * (`stream_seq`), so a same-page reconnect can drop frames it already
1456
+ * rendered. Throws `MatrxApiError` with status 409 when live delivery is
1457
+ * unavailable — fall back to `followRuntimeOperationEvents` + a final record
1458
+ * re-query. The `requestId` must be the server's `X-Request-ID`.
1459
+ */
1460
+ declare function rejoinRuntimeOperation(transport: MatrxTransport, requestId: string, options?: MatrxStreamCallOptions): Promise<MatrxRunHandle>;
1461
+
1462
+ /**
1463
+ * Resume-or-rejoin — the ONE client behavior for "pick a run back up".
1464
+ *
1465
+ * Server truth (aidream `services/runtime/FEATURE.md` § "A live run is
1466
+ * REJOINED, never resumed beside itself"; `reconnect.run_in_progress_error`):
1467
+ * - Every resume / retry / continue door asked to resume a run that is STILL
1468
+ * LIVE answers `409` with, at the ROOT of the error body,
1469
+ * `{error, code: "run_in_progress", live_request_id, rejoin_path, message,
1470
+ * …extras}` (extras: `run_id` for workflow / SEO / comparison, `arm_index` /
1471
+ * `arm`, `analysis_id`). The client follows `rejoin_path` — never a URL it
1472
+ * builds — because a door outside the spine (SEO collections) answers
1473
+ * `live_request_id: null` with its own `/seo/collections/{run_id}/rejoin`.
1474
+ * - Chat/conversation resume answers `code: "resume_conflict"` — even when the
1475
+ * body ALSO names `live_request_id` + `rejoin_path` — and it is ALWAYS host
1476
+ * retry, never a rejoin: the claim holder is usually the turn that is still
1477
+ * SUSPENDING, and rejoining it replays that turn from frame one (duplicate
1478
+ * text) while the person's continuation never runs. Only
1479
+ * `code: run_in_progress` rejoins.
1480
+ * - The envelope RESERVES `request_id` for the refusing call's own id — the
1481
+ * live run is read from `live_request_id`, never `request_id` (reading it
1482
+ * rejoined a request that did not exist: 404, 2026-10-01).
1483
+ * - A rejoin answers `409 live_stream_unavailable` when there is no journal to
1484
+ * replay (resumed workflow legs, comparison arms). Then: a body naming a
1485
+ * workflow `run_id` → follow `GET /runs/{run_id}/events/stream` (the workflow
1486
+ * SSE, which also carries the node_stream media frames); otherwise follow the
1487
+ * durable lifecycle (`followRuntimeOperationToEnd`).
1488
+ *
1489
+ * Resume and rejoin are work on an EXISTING run, so they carry THAT run's
1490
+ * organization (`organizationId`), never whatever the session has selected.
1491
+ */
1492
+
1493
+ declare const MATRX_RUN_IN_PROGRESS = "run_in_progress";
1494
+ declare const MATRX_RESUME_CONFLICT = "resume_conflict";
1495
+ declare const MATRX_LIVE_STREAM_UNAVAILABLE = "live_stream_unavailable";
1496
+ /**
1497
+ * Any error carrying an HTTP status and the parsed server body —
1498
+ * `MatrxApiError`, matrx-frontend's `ApiCallError`, or a host's own shape.
1499
+ */
1500
+ interface MatrxHttpErrorLike {
1501
+ status?: unknown;
1502
+ serverDetail?: unknown;
1503
+ }
1504
+ /**
1505
+ * The machine code of a Matrx HTTP error: `code` (or the envelope's hoisted
1506
+ * `error`) from `detail`, `details`, or the top level. Null when absent.
1507
+ */
1508
+ declare function readMatrxErrorCode(error: unknown): string | null;
1509
+ /**
1510
+ * The machine code a server message carries as a PREFIX
1511
+ * (`"resume_conflict: another run is live"` → `resume_conflict`), for envelope
1512
+ * variants that pass the message but map `error` to a generic status word.
1513
+ * Only the known resume/turn-lock codes match; null for anything else.
1514
+ */
1515
+ declare function readMatrxErrorCodeFromMessage(message: string | null | undefined): string | null;
1516
+ /**
1517
+ * The conversation's own organization id, for scoping a resume/rejoin to the
1518
+ * RUN's organization instead of the session's selection. Transport-agnostic:
1519
+ * the host injects `read` (a supabase select, a REST call, …) returning the raw
1520
+ * `organization_id` (or throwing). Null — with `onUnreadable(reason)` — when it
1521
+ * is missing or unreadable, so the caller keeps the active organization and
1522
+ * says so.
1523
+ */
1524
+ declare function readConversationOrganizationId(conversationId: string, read: (conversationId: string) => Promise<unknown>, onUnreadable?: (reason: string | null) => void): Promise<string | null>;
1525
+ /** Where to rejoin a live run, read from a resume door's 409. */
1526
+ interface MatrxLiveRunRejoin {
1527
+ /** The live request (`GET /runtime/operations/{id}`); null for doors outside the spine (SEO). */
1528
+ liveRequestId: string | null;
1529
+ /** Server-relative path to `POST` for the NDJSON replay-then-follow. Always from the body when present. */
1530
+ rejoinPath: string;
1531
+ /** `run_id` extra (workflow / SEO / comparison), when present. */
1532
+ runId: string | null;
1533
+ /** The refusal's code — always `run_in_progress` (chat's `resume_conflict` is retried, never rejoined). */
1534
+ code: string;
1535
+ /** The refusal body (root of the envelope), for door-specific extras (`arm_index`, `analysis_id`, …). */
1536
+ body: Record<string, unknown>;
1537
+ }
1538
+ /**
1539
+ * The rejoin target of a live-run refusal: `409` with `code: run_in_progress`.
1540
+ * Null for every other error — a genuine failure is never mistaken for a live
1541
+ * run, and chat's `resume_conflict` is never a rejoin even when its body names
1542
+ * one (the host retries the resume so the continuation runs). Reads
1543
+ * `rejoin_path` from the body first; derives the runtime rejoin from
1544
+ * `live_request_id` only when the body names no path. Never reads
1545
+ * `request_id`.
1546
+ */
1547
+ declare function readLiveRunRejoin(error: unknown): MatrxLiveRunRejoin | null;
1548
+ /**
1549
+ * The request id of a run that is STILL LIVE, read from a resume door's
1550
+ * refusal (`live_request_id` — never the envelope's own `request_id`). Null
1551
+ * for every other error, and for a live refusal outside the spine (SEO) —
1552
+ * follow `readLiveRunRejoin(error).rejoinPath` instead.
1553
+ */
1554
+ declare function readLiveRunRequestId(error: unknown): string | null;
1555
+ /**
1556
+ * `409 resume_conflict` — another run holds the conversation's claim (usually
1557
+ * the turn still suspending); retry the resume with the host's backoff so the
1558
+ * continuation runs. Never a rejoin, even when the body names a live run.
1559
+ */
1560
+ declare function isResumeConflict(error: unknown): boolean;
1561
+ /** `409 live_stream_unavailable` — the rejoin journal is gone; follow the durable lifecycle instead. */
1562
+ declare function isLiveStreamUnavailable(error: unknown): boolean;
1563
+ /**
1564
+ * The server-relative rejoin path — for hosts whose stream pipeline takes a
1565
+ * URL instead of a transport (prepend the resolved base URL).
1566
+ */
1567
+ declare function runtimeOperationRejoinPath(requestId: string): string;
1568
+ /**
1569
+ * The text to show for a stream `error` event: the server's `user_message`
1570
+ * first (it is written for the person — e.g. "OpenAI refused this request:
1571
+ * the platform's OpenAI account is out of credit."), then `message`. Accepts
1572
+ * the `{event, data}` envelope or its `data` payload. Null when neither is
1573
+ * present — the host supplies its own fallback; it must never REPLACE a
1574
+ * message the server sent.
1575
+ */
1576
+ declare function streamErrorText(eventOrPayload: unknown): string | null;
1577
+ /**
1578
+ * Open any Matrx NDJSON stream by path (`POST`, optional JSON body) — for
1579
+ * feature doors outside the agent lifecycle (`/podcast/resume/{run_id}`, …)
1580
+ * so their streams ride the same transport and wire kernel as every run.
1581
+ */
1582
+ declare function openMatrxStream(transport: MatrxTransport, path: string, options?: MatrxStreamCallOptions & {
1583
+ body?: unknown;
1584
+ }): Promise<MatrxRunHandle>;
1585
+ /**
1586
+ * Bind a run's organization onto every call as `X-Organization-Id`. The
1587
+ * call's own org is the request's organization: `createMatrxTransport` hands
1588
+ * it to the host's `resolveTarget(request)`, never lets a policy or session
1589
+ * org replace it, and never refuses it for want of a session org (2026-10-07:
1590
+ * Applet jobs were refused "Select an organization…" before any I/O, and with
1591
+ * an active org set the run silently moved into it).
1592
+ */
1593
+ declare function withRunOrganization(transport: MatrxTransport, organizationId: string): MatrxTransport;
1594
+ /**
1595
+ * Follow a workflow run's SSE (`GET /runs/{run_id}/events/stream`) — the
1596
+ * rejoin fallback for workflow legs with no stream journal. Delivers every
1597
+ * parsed event (durable run events with their seq; ephemeral `node_stream`
1598
+ * frames — including `kind: "media"` — with seq null). One connection, no
1599
+ * reconnect policy: resolves `{ended: true}` on the server's `end` frame,
1600
+ * `{ended: false}` when the wire closes or the caller aborts.
1601
+ */
1602
+ declare function followWorkflowRunEvents(transport: MatrxTransport, runId: string, options: {
1603
+ onEvent: (event: Record<string, unknown>, seq: number | null) => void;
1604
+ lastEventId?: number | null;
1605
+ signal?: AbortSignal;
1606
+ }): Promise<{
1607
+ ended: boolean;
1608
+ }>;
1609
+ type ResumeOrRejoinOutcome =
1610
+ /** The resume door ran the run; its stream was delivered to `onEnvelope`. */
1611
+ {
1612
+ kind: "resumed";
1613
+ requestId: string | null;
1614
+ }
1615
+ /** The run was live; its stream (from `rejoin_path`) was replayed + followed into `onEnvelope`. */
1616
+ | {
1617
+ kind: "rejoined";
1618
+ rejoin: MatrxLiveRunRejoin;
1619
+ }
1620
+ /**
1621
+ * The run was live but had no journal to replay; the durable lifecycle was
1622
+ * followed instead. Re-query the feature's record for the result.
1623
+ * `ended: false` = the follow gave up (or no operation was found).
1624
+ */
1625
+ | {
1626
+ kind: "followed";
1627
+ rejoin: MatrxLiveRunRejoin;
1628
+ executionId: string | null;
1629
+ ended: boolean;
1630
+ status: MatrxRuntimeExecutionStatus | null;
1631
+ }
1632
+ /** No journal, and the run is a workflow: its run SSE was followed into `onWorkflowEvent`. */
1633
+ | {
1634
+ kind: "followed_workflow";
1635
+ rejoin: MatrxLiveRunRejoin;
1636
+ runId: string;
1637
+ ended: boolean;
1638
+ }
1639
+ /** `409 resume_conflict` (named live run or not) — retry with the host's backoff policy. */
1640
+ | {
1641
+ kind: "resume_conflict";
1642
+ error: MatrxApiError;
1643
+ };
1644
+ interface ResumeOrRejoinOptions extends Omit<MatrxStreamCallOptions, "signal"> {
1645
+ /** Every NDJSON envelope — from the resume stream OR the rejoined stream. */
1646
+ onEnvelope: (envelope: MatrxStreamEnvelope) => void;
1647
+ /** THE RUN'S organization (from its durable record) — bound on every call. */
1648
+ organizationId?: string | null;
1649
+ signal?: AbortSignal;
1650
+ /** Fired the moment a live run is detected, before the rejoin opens (reset replay-sensitive state). */
1651
+ onRejoin?: (rejoin: MatrxLiveRunRejoin) => void;
1652
+ /** Durable lifecycle events while following (the no-journal fallback). */
1653
+ onOperationEvent?: (event: MatrxRuntimeOperationEvent) => void;
1654
+ /** Workflow run SSE events (the no-journal fallback for a workflow `run_id`). */
1655
+ onWorkflowEvent?: (event: Record<string, unknown>, seq: number | null) => void;
1656
+ /** Tuning for the durable follow (stall / reconnect policy). */
1657
+ follow?: Omit<FollowRuntimeOperationToEndOptions, "onEvent" | "signal" | "lastEventSeq">;
1658
+ }
1659
+ /**
1660
+ * Resume a run — or, when the server says it is still live, REJOIN it.
1661
+ *
1662
+ * Calls `resumeCall` (the feature's resume door) and pipes its stream into
1663
+ * `onEnvelope`. On a live-run refusal (`run_in_progress`) it follows the body's `rejoin_path`
1664
+ * into the SAME handler under the same organization; when the rejoin has no
1665
+ * journal (`live_stream_unavailable`) it follows the workflow run SSE (body
1666
+ * names a workflow `run_id`) or the durable lifecycle. Never runs a live run a
1667
+ * second time. Every `resume_conflict` comes back as an outcome; any other
1668
+ * error is thrown.
1669
+ */
1670
+ declare function resumeOrRejoin(transport: MatrxTransport, resumeCall: (transport: MatrxTransport, options: MatrxStreamCallOptions) => Promise<MatrxRunHandle>, options: ResumeOrRejoinOptions): Promise<ResumeOrRejoinOutcome>;
1671
+ /** What a `409 live_stream_unavailable` rejoin answer names: a workflow `run_id`, when it has one. */
1672
+ interface MatrxLiveStreamUnavailable {
1673
+ runId: string | null;
1674
+ }
1675
+ /**
1676
+ * Read a rejoin's `409 live_stream_unavailable` (the journal is gone). Null
1677
+ * for every other error. For hosts that open the rejoin stream themselves —
1678
+ * hand the result to `followUnavailableRejoin`.
1679
+ */
1680
+ declare function readLiveStreamUnavailable(error: unknown): MatrxLiveStreamUnavailable | null;
1681
+ type MatrxFollowedOutcome = Extract<ResumeOrRejoinOutcome, {
1682
+ kind: "followed" | "followed_workflow";
1683
+ }>;
1684
+ interface FollowUnavailableRejoinOptions {
1685
+ /** THE RUN'S organization; omit when `transport` is already bound to it. */
1686
+ organizationId?: string | null;
1687
+ signal?: AbortSignal;
1688
+ onOperationEvent?: (event: MatrxRuntimeOperationEvent) => void;
1689
+ onWorkflowEvent?: (event: Record<string, unknown>, seq: number | null) => void;
1690
+ follow?: Omit<FollowRuntimeOperationToEndOptions, "onEvent" | "signal" | "lastEventSeq">;
1691
+ }
1692
+ /**
1693
+ * The no-journal fallback of a rejoin — the SAME branch `resumeOrRejoin`
1694
+ * takes, exported for hosts whose rejoin stream rides their own wire (the
1695
+ * extension's offscreen fetch, the desktop's request loop). A workflow
1696
+ * `run_id` (from the unavailable body, else the refusal) on a runtime rejoin
1697
+ * → follow the workflow run SSE; otherwise read the live operation and
1698
+ * follow it to its end. Settle the result with `settleRunPickup`.
1699
+ */
1700
+ declare function followUnavailableRejoin(transport: MatrxTransport, rejoin: MatrxLiveRunRejoin, unavailable: MatrxLiveStreamUnavailable, options?: FollowUnavailableRejoinOptions): Promise<MatrxFollowedOutcome>;
1701
+ /**
1702
+ * The parts of a pick-up result `settleRunPickup` reads — every
1703
+ * `ResumeOrRejoinOutcome` is one, and so is a host's own follow result.
1704
+ */
1705
+ type MatrxRunPickup = {
1706
+ kind: "resumed" | "rejoined";
1707
+ } | {
1708
+ kind: "resume_conflict";
1709
+ } | {
1710
+ kind: "followed";
1711
+ executionId: string | null;
1712
+ ended: boolean;
1713
+ status: MatrxRuntimeExecutionStatus | null;
1714
+ } | {
1715
+ kind: "followed_workflow";
1716
+ ended: boolean;
1717
+ };
1718
+ type MatrxRunPickupSettlement =
1719
+ /** The turn arrived on a stream the host already rendered — nothing to reload. */
1720
+ {
1721
+ state: "streamed";
1722
+ }
1723
+ /** Bare `resume_conflict` — retry with the host's backoff. */
1724
+ | {
1725
+ state: "retry";
1726
+ }
1727
+ /**
1728
+ * The run is over (or no live operation remains): the SAVED turn was
1729
+ * reloaded — that is the answer to show, never an empty bubble and never a
1730
+ * failure the run did not have. `status` null = unknown (workflow / none
1731
+ * found); `reloaded: false` = the reload itself threw (`error`).
1732
+ */
1733
+ | {
1734
+ state: "settled";
1735
+ status: MatrxRuntimeExecutionStatus | null;
1736
+ reloaded: boolean;
1737
+ error?: unknown;
1738
+ }
1739
+ /** The follow gave up while the run may still be live; `onStillRunning` (default: reload what is saved) ran. */
1740
+ | {
1741
+ state: "still_running";
1742
+ reloaded: boolean;
1743
+ error?: unknown;
1744
+ };
1745
+ interface SettleRunPickupOptions {
1746
+ /** Re-read the feature's saved record (the conversation's messages) into the screen. */
1747
+ reloadSavedTurn: () => Promise<void> | void;
1748
+ /**
1749
+ * The follow gave up on a run that may still be live. Default: reload what
1750
+ * is saved now. A host with its own patient recovery (the web app's
1751
+ * dropped-stream poll) passes it here instead.
1752
+ */
1753
+ onStillRunning?: () => Promise<void> | void;
1754
+ }
1755
+ /**
1756
+ * THE one mapping from a pick-up result to what the screen does, shared by
1757
+ * every chat client so a live run that cannot replay ends the same way
1758
+ * everywhere: streamed → done; bare conflict → retry; followed to its end
1759
+ * (or nothing live left to follow) → reload the saved turn and report the
1760
+ * run's real status; follow gave up → still running (never "failed").
1761
+ */
1762
+ declare function settleRunPickup(pickup: MatrxRunPickup, options: SettleRunPickupOptions): Promise<MatrxRunPickupSettlement>;
1763
+
1764
+ /**
1765
+ * Follow or reconnect to a server job — THE client primitive for "a page
1766
+ * refreshed (or a tab came back) while the server was still working".
1767
+ *
1768
+ * Server truth (aidream `services/runtime/reconnect.py`): every stream opens a
1769
+ * durable run record keyed by its `X-Request-ID`, optionally linked to a
1770
+ * feature record (`("conversation", id)`, `("processed_document", id)`, …), and
1771
+ * its NDJSON response is journaled with `(stream_id, stream_seq)` stamps.
1772
+ *
1773
+ * One call does the whole recovery:
1774
+ *
1775
+ * 1. identify — `GET /runtime/operations/{request_id}` or
1776
+ * `GET /runtime/operations/by-link/{kind}/{id}`
1777
+ * 2. rejoin — `POST /runtime/operations/{request_id}/rejoin` replays the
1778
+ * journal then follows live; frames already delivered on this
1779
+ * page are dropped by `(stream_id, stream_seq)`
1780
+ * 3. fallback — 404/409 `live_stream_unavailable` (or any rejoin failure):
1781
+ * follow the durable lifecycle stream to its end
1782
+ * 4. settle — re-read the run's status and call `reloadSavedResult()`;
1783
+ * the feature's own record is the content truth
1784
+ *
1785
+ * The outcome ALWAYS carries a true status read from the server — never an
1786
+ * "unknown": a run that settled says so, a run still live after every follow
1787
+ * attempt says it is still running (`settled: false`), and no recorded run at
1788
+ * all is `"none"`.
1789
+ *
1790
+ * Framework-light: transport port in, callbacks out. React binding:
1791
+ * `useServerJob` in `@ai-matrx/agents/react`. Chat keeps its Redux pipeline and
1792
+ * shares `pickServerJobOperation` / `createStreamReplayCursor` from here.
1793
+ */
1794
+
1795
+ /** The transport stamp of one NDJSON frame, when the server sent one. */
1796
+ interface MatrxStreamCursorStamp {
1797
+ streamId: string | null;
1798
+ streamSeq: number;
1799
+ }
1800
+ /**
1801
+ * Read a frame's `(stream_id, stream_seq)` stamp. Null for unstamped frames
1802
+ * (older servers, compact envelopes) — those are always delivered.
1803
+ */
1804
+ declare function readStreamCursor(frame: unknown): MatrxStreamCursorStamp | null;
1805
+ /**
1806
+ * Remembers the highest delivered `stream_seq` PER `stream_id`, so a replayed
1807
+ * journal never renders a frame twice. A new `stream_id` is a new emitter
1808
+ * segment (e.g. a resumed leg) whose sequence restarts at 1 — its frames are
1809
+ * admitted. The mark is kept per segment, not just for the latest one: a page
1810
+ * that live-followed segment A then B and then rejoins (the journal replays A
1811
+ * then B) must drop BOTH replays. Keep one cursor per job for the life of the
1812
+ * page: the first live stream and every later rejoin share it.
1813
+ */
1814
+ interface StreamReplayCursor {
1815
+ /** True when the frame is new and should be delivered; false = already seen. */
1816
+ admit(frame: unknown): boolean;
1817
+ /** The segment of the last ADMITTED stamped frame. */
1818
+ readonly streamId: string | null;
1819
+ /** The high-water mark of that segment. */
1820
+ readonly lastSeq: number;
1821
+ /** High-water marks of every segment seen, by `stream_id`. */
1822
+ readonly marks: Readonly<Record<string, number>>;
1823
+ /** Forget everything (a cold page replays from frame one). */
1824
+ reset(): void;
1825
+ }
1826
+ interface StreamReplayCursorOptions {
1827
+ /** Fired when an ADMITTED frame opens a new emitter segment (sequence restarts). */
1828
+ onSegmentChange?: (streamId: string) => void;
1829
+ /**
1830
+ * Resume from marks a host persisted (e.g. Redux). `streamId`/`lastSeq` is the
1831
+ * current segment; `marks` adds earlier segments' high-water marks.
1832
+ */
1833
+ seed?: {
1834
+ streamId?: string | null;
1835
+ lastSeq?: number;
1836
+ marks?: Readonly<Record<string, number>>;
1837
+ };
1838
+ }
1839
+ declare function createStreamReplayCursor(options?: StreamReplayCursorOptions): StreamReplayCursor;
1840
+ /**
1841
+ * Where a followed job stands. The server's run statuses, plus:
1842
+ * `connecting` — looking the run up (only ever an intermediate state);
1843
+ * `none` — the server holds no run for this target (nothing to follow; the
1844
+ * saved record is the whole truth).
1845
+ */
1846
+ type ServerJobStatus = "connecting" | "none" | MatrxRuntimeExecutionStatus;
1847
+ /** A settled status the outcome can carry (never `connecting`). */
1848
+ type ServerJobFinalStatus = Exclude<ServerJobStatus, "connecting">;
1849
+ declare function isServerJobSettled(status: ServerJobStatus): boolean;
1850
+ /**
1851
+ * The one operation to follow from an identify answer: the first live one,
1852
+ * else the newest (the server sorts newest first). Null when there is none.
1853
+ * Works on both by-link shapes: the original (one run per operation) and the
1854
+ * newer one that returns the LINKED execution with its own status plus the
1855
+ * `root_request_id` to rejoin by.
1856
+ */
1857
+ declare function pickServerJobOperation<T extends {
1858
+ is_terminal: boolean;
1859
+ }>(operations: readonly T[] | null | undefined): T | null;
1860
+ /**
1861
+ * The request id to rejoin and re-read by: the root run's id when the server
1862
+ * names one (`root_request_id`, newer by-link shape), else the operation's own.
1863
+ */
1864
+ declare function serverJobRejoinHandle(operation: {
1865
+ request_id: string | null;
1866
+ root_request_id?: string | null;
1867
+ } | null | undefined): string | null;
1868
+ /** What to follow: the run's own `X-Request-ID`, or the feature record it is linked to. */
1869
+ type ServerJobTarget = {
1870
+ requestId: string;
1871
+ } | {
1872
+ linkKind: string;
1873
+ linkId: string;
1874
+ };
1875
+ type ServerJobPath = "none" | "already_settled" | "rejoin" | "lifecycle";
1876
+ interface ServerJobOutcome {
1877
+ /** The run's true status as last read from the server. */
1878
+ status: ServerJobFinalStatus;
1879
+ /** True when `status` is terminal or `none` — `reloadSavedResult` ran. */
1880
+ settled: boolean;
1881
+ /** The caller aborted; `status` is the last status read. */
1882
+ aborted: boolean;
1883
+ /** How the job was followed. */
1884
+ path: ServerJobPath;
1885
+ requestId: string | null;
1886
+ executionId: string | null;
1887
+ /** The run's error column when it failed. */
1888
+ error: Record<string, unknown> | null;
1889
+ /** NDJSON frames delivered to `onFrame` / dropped as already seen. */
1890
+ framesDelivered: number;
1891
+ framesSkipped: number;
1892
+ /** `reloadSavedResult` completed without throwing. */
1893
+ reloaded: boolean;
1894
+ reloadError?: unknown;
1895
+ }
1896
+ interface FollowServerJobOptions {
1897
+ target: ServerJobTarget;
1898
+ /** Every NEW NDJSON frame of the rejoined response (replay + live). */
1899
+ onFrame?: (envelope: MatrxStreamEnvelope) => void;
1900
+ /** Every status transition, with the operation view when one is known. */
1901
+ onStatus?: (status: ServerJobStatus, operation: MatrxRuntimeOperationView | null) => void;
1902
+ /** Durable lifecycle events while following without a journal. */
1903
+ onOperationEvent?: (event: MatrxRuntimeOperationEvent) => void;
1904
+ /** The rejoin could not replay; the lifecycle follow takes over (diagnostic). */
1905
+ onRejoinUnavailable?: (error: unknown) => void;
1906
+ /**
1907
+ * Re-read the feature's saved record into the screen. Called once the job
1908
+ * settles (terminal or no run) — the saved record is the content truth.
1909
+ */
1910
+ reloadSavedResult: (outcome: {
1911
+ status: ServerJobFinalStatus;
1912
+ operation: MatrxRuntimeOperationView | null;
1913
+ }) => Promise<void> | void;
1914
+ /** Share one cursor across every reconnect of this job on this page. */
1915
+ cursor?: StreamReplayCursor;
1916
+ /**
1917
+ * Host-owned rejoin: the host opens and renders the rejoin wire itself (chat
1918
+ * feeds it through its Redux stream pipeline, which dedupes on the same
1919
+ * cursor). Replaces the built-in frame loop. Throw when the wire is
1920
+ * unavailable (the lifecycle follow takes over); return `"stand-down"` when
1921
+ * the host has already taken over recovery and this attempt must stop.
1922
+ */
1923
+ rejoinConsumer?: (args: {
1924
+ operation: MatrxRuntimeOperationView;
1925
+ /** The id to rejoin by (`root_request_id` when the server names one). */
1926
+ requestId: string;
1927
+ }) => Promise<void | "stand-down">;
1928
+ /**
1929
+ * After a rejoin ended with the run still live: `"handoff"` returns now
1930
+ * (the host re-enters), `"follow"` (default) follows the lifecycle to its end.
1931
+ */
1932
+ afterRejoin?: (operation: MatrxRuntimeOperationView) => "follow" | "handoff";
1933
+ /** Wraps the identify read (e.g. an offline retry budget). */
1934
+ retry?: <T>(attempt: () => Promise<T>) => Promise<T>;
1935
+ /** THE RUN'S organization, when the transport is not already bound to it. */
1936
+ organizationId?: string | null;
1937
+ signal?: AbortSignal;
1938
+ /** Lifecycle follow tuning (stall watchdog, reconnect budget). */
1939
+ follow?: Omit<FollowRuntimeOperationToEndOptions, "onEvent" | "signal" | "lastEventSeq">;
1940
+ /** Max operations read by link. Default 5. */
1941
+ linkLimit?: number;
1942
+ /**
1943
+ * The host already holds (or shares) the FIRST by-link read for this target —
1944
+ * answer the identify step from it instead of issuing a second identical GET.
1945
+ * Used only for the initial identify of a `linkKind/linkId` target; later
1946
+ * refreshes always read the server fresh.
1947
+ */
1948
+ identifyLink?: (linkKind: string, linkId: string, signal?: AbortSignal) => Promise<readonly MatrxRuntimeOperationView[] | null | undefined>;
1949
+ }
1950
+ /**
1951
+ * Follow a server job to its end — or reconnect to it after a refresh. See the
1952
+ * module doc for the four steps. Throws only when the FIRST identify read
1953
+ * fails (the server is unreachable — nothing true can be said yet); every
1954
+ * later failure degrades to the next step and ends in a status the server
1955
+ * actually reported.
1956
+ */
1957
+ declare function followServerJob(transport: MatrxTransport, options: FollowServerJobOptions): Promise<ServerJobOutcome>;
1958
+
1959
+ /**
1960
+ * Browser-held provider sessions report their failures through the server.
1961
+ *
1962
+ * Realtime voice (OpenAI / xAI) and Cartesia TTS run browser → provider
1963
+ * directly on an ephemeral token from the token broker, so a provider refusal
1964
+ * (e.g. the platform's account is out of credit) never touches the server —
1965
+ * the operator would never hear of it, and the person would see raw provider
1966
+ * text. Every client calls `reportProviderSessionFailure` from the session's
1967
+ * error handler and shows the returned `user_message`.
1968
+ *
1969
+ * Server truth: `POST /broker/provider-failures`
1970
+ * (`aidream/api/routers/token_broker.py`; body `ProviderSessionFailureReport`
1971
+ * in `aidream/services/token_broker/models.py` — `extra="forbid"`, allow-listed
1972
+ * providers, bounded text; identity and organization come from the session).
1973
+ */
1974
+
1975
+ /** The providers whose sessions a browser holds directly (server allow-list). */
1976
+ type MatrxClientSessionProvider = "openai" | "xai" | "cartesia";
1977
+ interface ProviderSessionFailure {
1978
+ provider: MatrxClientSessionProvider;
1979
+ model?: string | null;
1980
+ /** HTTP status the provider answered, when the failure was an HTTP answer. */
1981
+ status_code?: number | null;
1982
+ /** The provider's own error type/code (`insufficient_quota`, …). */
1983
+ error_type?: string | null;
1984
+ /** The provider's text, verbatim — classified server-side, never shown. */
1985
+ message: string;
1986
+ }
1987
+ /** `ProviderSessionFailureVerdict` — what to show, and whether reconnecting can help. */
1988
+ interface ProviderSessionFailureVerdict {
1989
+ error_type: string;
1990
+ retryable: boolean;
1991
+ /** The sentence to show the person (never the provider's raw text). */
1992
+ user_message: string;
1993
+ }
1994
+ /** The body the server accepts — bounded exactly as its model bounds it. */
1995
+ declare function providerSessionFailureBody(failure: ProviderSessionFailure): Record<string, string | number>;
1996
+ /**
1997
+ * Report a browser-held provider session failure and get the sentence to
1998
+ * show. Resolves null (and warns) when the report itself could not be made —
1999
+ * the caller then shows its own fallback; it never throws from an error
2000
+ * handler.
2001
+ */
2002
+ declare function reportProviderSessionFailure(transport: MatrxTransport, failure: ProviderSessionFailure, options?: {
2003
+ signal?: AbortSignal;
2004
+ }): Promise<ProviderSessionFailureVerdict | null>;
2005
+
2006
+ export { type ApiTargetLogContext, BackendApiError, type BackendApiErrorData, type BackendErrorCode, type BackendFailureExplanation, type CreateMatrxTransportOptions, type CreateWarmupOptions, CredentialsPort, type EndpointOverrideConfig, type ExecuteMatrxCallHooks, type ExecuteMatrxCallRequest, type FollowRuntimeOperationOptions, type FollowRuntimeOperationToEndOptions, type FollowRuntimeOperationToEndResult, type FollowServerJobOptions, type FollowUnavailableRejoinOptions, type JsonRequestOptions, MATRX_AI_API_VERSION_DEFAULT, MATRX_LIVE_STREAM_UNAVAILABLE, MATRX_RESUME_CONFLICT, MATRX_RUN_IN_PROGRESS, MATRX_UI_ONLY_BODY_FIELDS, type MatrxAiApiVersion, MatrxApiError, type MatrxCallError, type MatrxCallResult, type MatrxCallScope, type MatrxChatMessage, type MatrxClientSessionProvider, type MatrxConversationStart, type MatrxEphemeralConversation, type MatrxFollowedOutcome, type MatrxHttpErrorLike, type MatrxHttpMethod, type MatrxJsonObject, type MatrxJsonValue, type MatrxLiveRunRejoin, type MatrxLiveStreamUnavailable, type MatrxOperationEventsPage, type MatrxOperationFollowEvent, type MatrxOperationStatusResponse, type MatrxOperationsByLinkResponse, type MatrxProtocolDowngrade, type MatrxProtocolFallbackOptions, type MatrxQueryParams, type MatrxRequestInfo, type MatrxRunHandle, type MatrxRunPickup, type MatrxRunPickupSettlement, type MatrxRuntimeExecutionStatus, type MatrxRuntimeOperationEvent, type MatrxRuntimeOperationView, type MatrxStoredConversationContinue, type MatrxStoredConversationCreate, type MatrxStreamCallOptions, type MatrxStreamCursorStamp, type MatrxStreamIds, type MatrxStreamParse, type MatrxTransport, type MatrxTransportDiagnostics, type MatrxTransportRequest, type MatrxTransportTarget, OrganizationContextError, type OrganizationContextErrorCode, type OrganizationOperation, type ParseMatrxNdjsonResponseHooks, type ProviderSessionFailure, type ProviderSessionFailureVerdict, type QueryValue, RUN_OUTPUT_KINDS, RUN_STREAM_LIFETIME_BACKSTOP_MS, RUN_WAIT_KNOB_FEATURE, ResilientFetchOptions, type ResumeOrRejoinOptions, type ResumeOrRejoinOutcome, type RunOutputKind, type RunWait, type RunWaitKnobReader, type SendMatrxRequestOptions, type ServerJobFinalStatus, type ServerJobOutcome, type ServerJobPath, type ServerJobStatus, type ServerJobTarget, type SettleRunPickupOptions, type StreamReplayCursor, type StreamReplayCursorOptions, type StreamRequestOptions, StreamTransportError, TERMINAL_MATRX_RUNTIME_STATUSES, type UpstreamErrorPayload, V2_COVERED_AI_PATH_TEMPLATES, WARM_PATH, type WarmItem, type WarmReason, type Warmup, aiVersionPathOverrides, applyAiApiVersion, applyDesktopTargetToRequestBody, applyOrganizationContextHeader, assertOrganizationMatchesOperation, assertQueryOrganizationMatchesContext, bareStatusSentence, buildMatrxRequestBody, buildMatrxRequestUrl, buildQuery, buildSafeRequestLog, continueEphemeralConversationStart, continueStoredConversationStart, createMatrxTransport, createOrganizationOperation, createStreamReplayCursor, createWarmup, describeBackendFailure, encodePathSegment, executeMatrxCall, extractMatrxErrorCode, extractMatrxErrorMessage, fetchWithMatrxProtocolFallback, followRuntimeOperationEvents, followRuntimeOperationToEnd, followServerJob, followUnavailableRejoin, followWorkflowRunEvents, getRuntimeOperationStatus, getRuntimeOperationsByLink, getUserMessage, honestTransportMessage, isBareTransportCode, isCoveredAiPath, isGenericUserMessage, isJobOutputKind, isLiveStreamUnavailable, isResumeConflict, isServerJobSettled, isStreamTransportLost, isV2Path, listRuntimeOperationEvents, logApiTarget, mintMatrxConversationId, newEphemeralConversationStart, newStoredConversationStart, normalizeMatrxError, openMatrxStream, parseCallApiError, parseHttpError, parseHttpErrorBody, parseMatrxNdjsonResponse, parsePersistedBackendError, parseStreamError, pickServerJobOperation, preconnectServer, providerSessionFailureBody, readConversationOrganizationId, readLiveRunRejoin, readLiveRunRequestId, readLiveStreamUnavailable, readMatrxErrorCode, readMatrxErrorCodeFromMessage, readMatrxJsonResponse, readStreamCursor, redactUrlForRequestLog, rejoinRuntimeOperation, reportProviderSessionFailure, requestJson, requestStream, requireOrganizationContext, resetPreconnectedForTests, resolveEndpointPath, resolveRunWaitWith, resumeOrRejoin, runJobLabel, runOutputKindFromModalities, runWaitKnobKey, runWaitTimeoutMessage, runtimeOperationRejoinPath, sendMatrxRequest, serverJobRejoinHandle, settleRunPickup, shouldReportMatrxCallError, streamErrorText, toRunHandle, toV1FallbackUrl, toV2Path, unwrapUpstreamError, withRunOrganization };