@ai-matrx/agents 0.3.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.1 — 2026-08-29
4
+
5
+ - Clarified the package boundary in the published README: `@ai-matrx/agents`
6
+ talks to the fully assembled AI Dream brain, while
7
+ `@ai-matrx/agent-engine` runs a brain the embedder assembles locally.
8
+
9
+ ## 0.5.0 — 2026-08-29
10
+
11
+ - Added `@ai-matrx/agents/matrx` — the Matrx API client: run an agent
12
+ end-to-end against the AI Matrx server over ONE injected, fetch-shaped
13
+ `MatrxTransport` port (base URL, credentials, org header, retry, and
14
+ diagnostics stay the host's job — this package never implements auth).
15
+ - The conversation-start contract as a discriminated union (client-minted
16
+ `conversation_id`, `is_new`, `store`; ephemeral `prior_messages` is only
17
+ representable with `store: false`) plus builders for all four cells.
18
+ - Run lifecycle: `startAgentRun` (`POST /ai/agents/{id}`),
19
+ `continueAgentConversation`, `resumeAgentConversation`, `cancelAgentRun`
20
+ (`X-Request-ID` only), and `runAgentToCompletion` — every stream parsed
21
+ through the package's own NDJSON kernel; `stream: true` is forced so a
22
+ caller flag can never flip the response off NDJSON. `X-Request-ID` /
23
+ `X-Conversation-ID` surface on the handle before any event.
24
+ - Reconnect surface: identify by request id or feature link, durable
25
+ seq-cursored event pages, `followRuntimeOperationEvents` (SSE
26
+ replay-then-follow with `Last-Event-ID`, riding `stream/sse`; comment
27
+ heartbeats and malformed frames surface as liveness, terminal `end`
28
+ carries the root status), and `rejoinRuntimeOperation` (replay the
29
+ original NDJSON response; 409 = fall back to the lifecycle stream).
30
+ - Delegated tools: `submitAgentToolResults` (idempotent; the
31
+ `continuation_needed` signal for `/resume`), per-conversation and
32
+ per-user pending-call discovery with optional instance claim.
33
+ - `MatrxApiError` preserves the server's structured error body and extracts
34
+ its richest message/code (`user_message` → `message` → joined details →
35
+ FastAPI `detail` shapes).
36
+ - Every route, header, and response shape verified against the aidream
37
+ server source (routers `agents`/`conversations`/`cancel`/
38
+ `runtime_operations`, matrx-connect's streaming response headers).
39
+
40
+ ## 0.4.0 — 2026-08-28
41
+
42
+ - Added `@ai-matrx/agents/stream/sse` — the pure incremental `text/event-stream`
43
+ frame kernel (`createMatrxSseFramer`, `parseMatrxSseFrame`,
44
+ `readMatrxSseStream`). Extracted from the two identical hand-rolled parsers in
45
+ the Matrix rejoin paths; covers all three SSE line/frame terminators by
46
+ construction (the production CRLF incident), byte-split UTF-8, comment
47
+ heartbeats as liveness-bearing frames, integer `id:` surfaced as the
48
+ `Last-Event-ID` cursor, and unterminated tails reported as diagnostics rather
49
+ than delivered as events. Host policy (fetch, stall timers, retry budgets,
50
+ cursor advancement) deliberately stays host-side.
51
+
3
52
  ## 0.3.0 — 2026-08-24
4
53
 
5
54
  - Added a transport-independent incremental NDJSON framer for fragmented text
package/README.md CHANGED
@@ -5,6 +5,10 @@ standardizes the stream wire, pure request/workflow projection, and safe
5
5
  Creator-facing result boundaries without importing React, Redux, Next.js, or
6
6
  application code.
7
7
 
8
+ **This package talks to the fully assembled AI Dream brain. It does not run the
9
+ provider/tool loop locally; use `@ai-matrx/agent-engine` when you need to run a
10
+ brain you assemble with your own providers, tools, persistence, and memory.**
11
+
8
12
  ## Install
9
13
 
10
14
  ```bash
@@ -81,6 +85,44 @@ a second copy of the same content. Configure `maxOpenFrameSets`,
81
85
  `maxFramesPerBlock`, and `maxBytesPerBlock` when creating the projection;
82
86
  rejected or malformed blocks are observable through `lastRenderBlockIssue`.
83
87
 
88
+ ## Run an agent end-to-end (`./matrx`)
89
+
90
+ The `@ai-matrx/agents/matrx` subpath is the Matrx API client. You inject ONE
91
+ fetch-shaped transport — base URL, credentials (user JWT, guest fingerprint,
92
+ or API key), the org header, and retry policy are yours; the package owns the
93
+ wire semantics (paths, bodies, streaming headers, the `Last-Event-ID` cursor):
94
+
95
+ ```ts
96
+ import {
97
+ newEphemeralConversationStart,
98
+ runAgentToCompletion,
99
+ type MatrxTransport,
100
+ } from "@ai-matrx/agents/matrx";
101
+
102
+ const transport: MatrxTransport = {
103
+ fetch: (path, init) =>
104
+ fetch(`https://server.app.matrxserver.com${path}`, {
105
+ ...init,
106
+ headers: { ...init.headers, Authorization: `Bearer ${token}` },
107
+ }),
108
+ };
109
+
110
+ const run = await runAgentToCompletion(transport, agentId, {
111
+ ...newEphemeralConversationStart(),
112
+ organization_id: orgId,
113
+ user_input: "Summarize this quarter's numbers.",
114
+ }, { onChunk: (text) => render(text) });
115
+ ```
116
+
117
+ Lower-level pieces: `startAgentRun` / `continueAgentConversation` /
118
+ `resumeAgentConversation` return a `MatrxRunHandle` (`X-Request-ID` and
119
+ `X-Conversation-ID` before any event, plus the normalized envelope stream);
120
+ `cancelAgentRun` stops a run by its server request id;
121
+ `getRuntimeOperationStatus` / `listRuntimeOperationEvents` /
122
+ `followRuntimeOperationEvents` / `rejoinRuntimeOperation` are the reconnect
123
+ ladder; `submitAgentToolResults` + pending-call discovery drive delegated
124
+ client tools (watch `continuation_needed` → `resumeAgentConversation`).
125
+
84
126
  ## Runtime support
85
127
 
86
128
  The package is framework-free ESM targeting modern browsers, browser-based
package/dist/index.cjs CHANGED
@@ -204,6 +204,68 @@ async function* readMatrxNdjsonStream(body, options = {}) {
204
204
  }
205
205
  }
206
206
 
207
+ // stream/sse.ts
208
+ var FRAME_SEPARATOR = /\r\n\r\n|\n\n|\r\r/;
209
+ var LINE_SEPARATOR = /\r\n|\n|\r/;
210
+ function parseMatrxSseFrame(frame) {
211
+ let event = "message";
212
+ let id = null;
213
+ const dataLines = [];
214
+ let sawData = false;
215
+ for (const line of frame.split(LINE_SEPARATOR)) {
216
+ if (line.startsWith(":")) continue;
217
+ if (line.startsWith("event:")) event = line.slice(6).trim();
218
+ else if (line.startsWith("data:")) {
219
+ sawData = true;
220
+ dataLines.push(line.slice(5).replace(/^ /, ""));
221
+ } else if (line.startsWith("id:")) id = line.slice(3).trim();
222
+ }
223
+ const seqCandidate = id !== null && id !== "" ? Number(id) : NaN;
224
+ const seq = Number.isSafeInteger(seqCandidate) && seqCandidate >= 0 ? seqCandidate : null;
225
+ return { event, id, seq, data: sawData ? dataLines.join("\n") : null };
226
+ }
227
+ function createMatrxSseFramer() {
228
+ let buffer = "";
229
+ return {
230
+ push(chunk) {
231
+ buffer += chunk;
232
+ const frames = [];
233
+ for (; ; ) {
234
+ const sep = FRAME_SEPARATOR.exec(buffer);
235
+ if (sep === null) break;
236
+ const frame = buffer.slice(0, sep.index);
237
+ buffer = buffer.slice(sep.index + sep[0].length);
238
+ frames.push(parseMatrxSseFrame(frame));
239
+ }
240
+ return frames;
241
+ },
242
+ flush() {
243
+ const rest = buffer;
244
+ buffer = "";
245
+ return { incomplete: rest.length > 0 ? rest : null };
246
+ }
247
+ };
248
+ }
249
+ async function* readMatrxSseStream(stream, options = {}) {
250
+ const reader = stream.getReader();
251
+ const decoder = new TextDecoder();
252
+ const framer = createMatrxSseFramer();
253
+ try {
254
+ for (; ; ) {
255
+ const { value, done } = await reader.read();
256
+ if (done) break;
257
+ const frames = framer.push(decoder.decode(value, { stream: true }));
258
+ for (const frame of frames) yield frame;
259
+ }
260
+ const tail = framer.push(decoder.decode());
261
+ for (const frame of tail) yield frame;
262
+ const { incomplete } = framer.flush();
263
+ if (incomplete !== null) options.onIncomplete?.(incomplete);
264
+ } finally {
265
+ reader.releaseLock();
266
+ }
267
+ }
268
+
207
269
  // presentation/result.ts
208
270
  var PRIVATE_REASONING_TYPES = /* @__PURE__ */ new Set([
209
271
  "thinking",
@@ -705,16 +767,496 @@ function projectWorkflowNodeEvent(current, event) {
705
767
  }
706
768
  }
707
769
 
770
+ // matrx/transport.ts
771
+ var MatrxApiError = class extends Error {
772
+ name = "MatrxApiError";
773
+ /** HTTP status of the failed response. */
774
+ status;
775
+ /** Machine code from the server body (`code`, or `detail.code`), when present. */
776
+ code;
777
+ /** The parsed server error body, verbatim (undefined when unparsable). */
778
+ serverDetail;
779
+ /** The request path the failure came from (server-relative). */
780
+ path;
781
+ constructor(args) {
782
+ super(
783
+ args.message ?? extractMatrxErrorMessage(args.serverDetail) ?? `HTTP ${args.status}`
784
+ );
785
+ this.status = args.status;
786
+ this.path = args.path;
787
+ this.serverDetail = args.serverDetail;
788
+ this.code = extractMatrxErrorCode(args.serverDetail);
789
+ }
790
+ };
791
+ function isRecord3(value) {
792
+ return typeof value === "object" && value !== null && !Array.isArray(value);
793
+ }
794
+ function nonBlankString(value) {
795
+ return typeof value === "string" && value.trim() ? value : void 0;
796
+ }
797
+ function extractMatrxErrorMessage(serverDetail) {
798
+ if (!isRecord3(serverDetail)) return void 0;
799
+ const userMessage = nonBlankString(serverDetail.user_message);
800
+ if (userMessage) return userMessage;
801
+ const message = nonBlankString(serverDetail.message);
802
+ if (message) return message;
803
+ if (Array.isArray(serverDetail.details)) {
804
+ const messages = serverDetail.details.map((entry) => {
805
+ if (!isRecord3(entry)) return void 0;
806
+ const detailMessage = nonBlankString(entry.message);
807
+ if (!detailMessage) return void 0;
808
+ const field = nonBlankString(entry.field);
809
+ return field ? `${field}: ${detailMessage}` : detailMessage;
810
+ }).filter((m) => typeof m === "string");
811
+ if (messages.length > 0) return messages.join("; ");
812
+ }
813
+ const detail = serverDetail.detail;
814
+ if (isRecord3(detail)) {
815
+ const detailMessage = nonBlankString(detail.message) ?? nonBlankString(detail.user_message);
816
+ if (detailMessage) return detailMessage;
817
+ }
818
+ if (typeof detail === "string" && detail.trim()) return detail;
819
+ if (Array.isArray(detail)) {
820
+ const messages = detail.map(
821
+ (entry) => isRecord3(entry) ? nonBlankString(entry.msg) : void 0
822
+ ).filter((m) => typeof m === "string");
823
+ if (messages.length > 0) return messages.join("; ");
824
+ }
825
+ return void 0;
826
+ }
827
+ function extractMatrxErrorCode(serverDetail) {
828
+ if (!isRecord3(serverDetail)) return null;
829
+ const topLevel = nonBlankString(serverDetail.code);
830
+ if (topLevel) return topLevel;
831
+ const detail = serverDetail.detail;
832
+ if (isRecord3(detail)) {
833
+ const nested = nonBlankString(detail.code);
834
+ if (nested) return nested;
835
+ }
836
+ return null;
837
+ }
838
+
839
+ // matrx/conversation.ts
840
+ function mintMatrxConversationId() {
841
+ return crypto.randomUUID();
842
+ }
843
+ function newStoredConversationStart(conversationId) {
844
+ return {
845
+ conversation_id: conversationId ?? mintMatrxConversationId(),
846
+ is_new: true,
847
+ store: true
848
+ };
849
+ }
850
+ function continueStoredConversationStart(conversationId) {
851
+ return { conversation_id: conversationId, is_new: false, store: true };
852
+ }
853
+ function newEphemeralConversationStart(conversationId) {
854
+ return {
855
+ conversation_id: conversationId ?? mintMatrxConversationId(),
856
+ is_new: true,
857
+ store: false
858
+ };
859
+ }
860
+ function continueEphemeralConversationStart(conversationId, priorMessages) {
861
+ return {
862
+ conversation_id: conversationId,
863
+ is_new: false,
864
+ store: false,
865
+ prior_messages: priorMessages
866
+ };
867
+ }
868
+
869
+ // matrx/internal.ts
870
+ function encodePathSegment(value) {
871
+ return encodeURIComponent(value);
872
+ }
873
+ function buildQuery(params) {
874
+ const search = new URLSearchParams();
875
+ for (const [key, value] of Object.entries(params)) {
876
+ if (value === void 0) continue;
877
+ if (Array.isArray(value)) {
878
+ for (const entry of value) search.append(key, entry);
879
+ } else {
880
+ search.append(key, String(value));
881
+ }
882
+ }
883
+ const encoded = search.toString();
884
+ return encoded ? `?${encoded}` : "";
885
+ }
886
+ async function readServerDetail(response) {
887
+ try {
888
+ return await response.json();
889
+ } catch {
890
+ return void 0;
891
+ }
892
+ }
893
+ async function throwApiError(path, response) {
894
+ throw new MatrxApiError({
895
+ status: response.status,
896
+ path,
897
+ serverDetail: await readServerDetail(response)
898
+ });
899
+ }
900
+ async function requestJson(transport, path, options) {
901
+ const hasBody = options.method !== "GET" && options.body !== void 0;
902
+ const response = await transport.fetch(path, {
903
+ method: options.method,
904
+ headers: hasBody ? { "Content-Type": "application/json" } : {},
905
+ ...hasBody ? { body: JSON.stringify(options.body) } : {},
906
+ ...options.signal ? { signal: options.signal } : {}
907
+ });
908
+ if (!response.ok) return throwApiError(path, response);
909
+ return await response.json();
910
+ }
911
+ function toRunHandle(response, options) {
912
+ return {
913
+ requestId: response.headers.get("X-Request-ID"),
914
+ conversationId: response.headers.get("X-Conversation-ID"),
915
+ events: readMatrxNdjsonStream(response.body, {
916
+ ...options.signal ? { signal: options.signal } : {},
917
+ ...options.maxReadAhead !== void 0 ? { maxReadAhead: options.maxReadAhead } : {},
918
+ ...options.onMalformedLine ? { onMalformedLine: options.onMalformedLine } : {},
919
+ ...options.onUnknownEnvelope ? { onUnknownEnvelope: options.onUnknownEnvelope } : {},
920
+ ...options.onValidEnvelope ? { onValidEnvelope: options.onValidEnvelope } : {}
921
+ }),
922
+ response
923
+ };
924
+ }
925
+ async function requestStream(transport, path, options) {
926
+ const hasBody = options.method !== "GET" && options.body !== void 0;
927
+ const response = await transport.fetch(path, {
928
+ method: options.method,
929
+ headers: {
930
+ ...hasBody ? { "Content-Type": "application/json" } : {},
931
+ ...options.headers
932
+ },
933
+ ...hasBody ? { body: JSON.stringify(options.body) } : {},
934
+ ...options.signal ? { signal: options.signal } : {}
935
+ });
936
+ if (!response.ok) return throwApiError(path, response);
937
+ if (!response.body) {
938
+ throw new MatrxApiError({
939
+ status: response.status,
940
+ path,
941
+ serverDetail: { code: "missing_response_body" },
942
+ message: "The streaming response carried no body."
943
+ });
944
+ }
945
+ return response;
946
+ }
947
+
948
+ // matrx/run.ts
949
+ async function streamCall(transport, path, body, options) {
950
+ const response = await requestStream(transport, path, {
951
+ method: "POST",
952
+ // This client IS the streaming path — `stream: true` always, last so a
953
+ // caller-supplied value can never flip the response off NDJSON.
954
+ body: { ...body, stream: true },
955
+ ...options.signal ? { signal: options.signal } : {}
956
+ });
957
+ return toRunHandle(response, options);
958
+ }
959
+ function startAgentRun(transport, agentId, request, options = {}) {
960
+ return streamCall(
961
+ transport,
962
+ `/ai/agents/${encodePathSegment(agentId)}`,
963
+ request,
964
+ options
965
+ );
966
+ }
967
+ function continueAgentConversation(transport, conversationId, request, options = {}) {
968
+ return streamCall(
969
+ transport,
970
+ `/ai/conversations/${encodePathSegment(conversationId)}`,
971
+ request,
972
+ options
973
+ );
974
+ }
975
+ function resumeAgentConversation(transport, conversationId, request = {}, options = {}) {
976
+ return streamCall(
977
+ transport,
978
+ `/ai/conversations/${encodePathSegment(conversationId)}/resume`,
979
+ request,
980
+ options
981
+ );
982
+ }
983
+ function cancelAgentRun(transport, requestId, options = {}) {
984
+ const query = buildQuery(
985
+ options.mode === "interrupt" ? { mode: "interrupt" } : {}
986
+ );
987
+ return requestJson(
988
+ transport,
989
+ `/ai/cancel/${encodePathSegment(requestId)}${query}`,
990
+ {
991
+ method: "POST",
992
+ ...options.signal ? { signal: options.signal } : {}
993
+ }
994
+ );
995
+ }
996
+ var MatrxRunError = class extends Error {
997
+ name = "MatrxRunError";
998
+ /** The verbatim `error` event payload, when one fired. */
999
+ errorPayload;
1000
+ /** The `user_request` completion status (`"failed"` | `"cancelled"`), when that was the trigger. */
1001
+ completionStatus;
1002
+ /** Text streamed before the failure — partial content never vanishes. */
1003
+ partialText;
1004
+ constructor(args) {
1005
+ super(args.message);
1006
+ this.errorPayload = args.errorPayload ?? null;
1007
+ this.completionStatus = args.completionStatus ?? null;
1008
+ this.partialText = args.partialText ?? "";
1009
+ }
1010
+ };
1011
+ function isRecord4(value) {
1012
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1013
+ }
1014
+ function stringField(value, key) {
1015
+ if (!isRecord4(value)) return null;
1016
+ const field = value[key];
1017
+ return typeof field === "string" && field ? field : null;
1018
+ }
1019
+ async function runAgentToCompletion(transport, agentId, request, options = {}) {
1020
+ const handle = await startAgentRun(transport, agentId, request, options);
1021
+ let text = "";
1022
+ let completion = null;
1023
+ let failure = null;
1024
+ for await (const envelope of handle.events) {
1025
+ options.onEvent?.(envelope);
1026
+ if (envelope.event === "chunk") {
1027
+ const chunk = stringField(envelope.data, "text");
1028
+ if (chunk !== null) {
1029
+ text += chunk;
1030
+ options.onChunk?.(text);
1031
+ }
1032
+ continue;
1033
+ }
1034
+ if (envelope.event === "error" && failure === null) {
1035
+ const payload = isRecord4(envelope.data) ? envelope.data : null;
1036
+ failure = new MatrxRunError({
1037
+ message: stringField(payload, "user_message") ?? stringField(payload, "message") ?? "The agent run failed",
1038
+ errorPayload: payload,
1039
+ partialText: text
1040
+ });
1041
+ continue;
1042
+ }
1043
+ if (envelope.event !== "completion" || !isRecord4(envelope.data)) continue;
1044
+ if (envelope.data.operation !== "user_request") continue;
1045
+ completion = envelope.data;
1046
+ const status = envelope.data.status;
1047
+ if ((status === "failed" || status === "cancelled") && failure === null) {
1048
+ const result = isRecord4(envelope.data.result) ? envelope.data.result : null;
1049
+ failure = new MatrxRunError({
1050
+ message: stringField(result, "error") ?? stringField(result, "user_message") ?? `The agent run ${status}`,
1051
+ completionStatus: status,
1052
+ partialText: text
1053
+ });
1054
+ }
1055
+ }
1056
+ if (failure) throw failure;
1057
+ if (!text && completion) {
1058
+ const result = completion.result;
1059
+ const output = stringField(result, "output");
1060
+ if (output !== null) text = output;
1061
+ }
1062
+ return {
1063
+ text,
1064
+ requestId: handle.requestId,
1065
+ conversationId: handle.conversationId,
1066
+ completion
1067
+ };
1068
+ }
1069
+
1070
+ // matrx/operations.ts
1071
+ var TERMINAL_MATRX_RUNTIME_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
1072
+ var RUNTIME_STATUSES = /* @__PURE__ */ new Set([
1073
+ "pending",
1074
+ "running",
1075
+ "paused",
1076
+ "waiting_input",
1077
+ "completed",
1078
+ "failed",
1079
+ "cancelled"
1080
+ ]);
1081
+ async function getRuntimeOperationStatus(transport, requestId, options = {}) {
1082
+ try {
1083
+ return await requestJson(
1084
+ transport,
1085
+ `/runtime/operations/${encodePathSegment(requestId)}`,
1086
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1087
+ );
1088
+ } catch (error) {
1089
+ if (error instanceof MatrxApiError && error.status === 404) return null;
1090
+ throw error;
1091
+ }
1092
+ }
1093
+ async function getRuntimeOperationsByLink(transport, linkKind, linkId, options = {}) {
1094
+ const query = buildQuery(
1095
+ options.limit !== void 0 ? { limit: options.limit } : {}
1096
+ );
1097
+ try {
1098
+ return await requestJson(
1099
+ transport,
1100
+ `/runtime/operations/by-link/${encodePathSegment(linkKind)}/${encodePathSegment(linkId)}${query}`,
1101
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1102
+ );
1103
+ } catch (error) {
1104
+ if (error instanceof MatrxApiError && error.status === 404) return null;
1105
+ throw error;
1106
+ }
1107
+ }
1108
+ function listRuntimeOperationEvents(transport, executionId, options = {}) {
1109
+ const query = buildQuery({
1110
+ ...options.afterSeq !== void 0 ? { after_seq: options.afterSeq } : {},
1111
+ ...options.limit !== void 0 ? { limit: options.limit } : {},
1112
+ ...options.kinds !== void 0 ? { kind: options.kinds } : {}
1113
+ });
1114
+ return requestJson(
1115
+ transport,
1116
+ `/runtime/executions/${encodePathSegment(executionId)}/events${query}`,
1117
+ { method: "GET", ...options.signal ? { signal: options.signal } : {} }
1118
+ );
1119
+ }
1120
+ function parseEndStatus(data) {
1121
+ if (data === null) return null;
1122
+ try {
1123
+ const parsed = JSON.parse(data);
1124
+ if (typeof parsed === "object" && parsed !== null && typeof parsed.status === "string") {
1125
+ const status = parsed.status;
1126
+ return RUNTIME_STATUSES.has(status) ? status : null;
1127
+ }
1128
+ } catch {
1129
+ }
1130
+ return null;
1131
+ }
1132
+ async function* followRuntimeOperationEvents(transport, executionId, options = {}) {
1133
+ let cursor = options.lastEventSeq ?? 0;
1134
+ const headers = { Accept: "text/event-stream" };
1135
+ if (cursor > 0) headers["Last-Event-ID"] = String(cursor);
1136
+ const response = await requestStream(
1137
+ transport,
1138
+ `/runtime/executions/${encodePathSegment(executionId)}/events/stream`,
1139
+ {
1140
+ method: "GET",
1141
+ headers,
1142
+ ...options.signal ? { signal: options.signal } : {}
1143
+ }
1144
+ );
1145
+ const frames = readMatrxSseStream(
1146
+ response.body,
1147
+ options.onIncomplete ? { onIncomplete: options.onIncomplete } : {}
1148
+ );
1149
+ for await (const frame of frames) {
1150
+ if (frame.event === "end") {
1151
+ yield { type: "end", status: parseEndStatus(frame.data), cursor };
1152
+ return;
1153
+ }
1154
+ if (frame.event === "execution_event" && frame.data !== null) {
1155
+ let event;
1156
+ try {
1157
+ event = JSON.parse(frame.data);
1158
+ } catch (error) {
1159
+ options.onMalformedFrame?.(frame, error);
1160
+ yield { type: "liveness", cursor };
1161
+ continue;
1162
+ }
1163
+ if (frame.seq !== null && frame.seq > cursor) cursor = frame.seq;
1164
+ yield { type: "event", event, seq: frame.seq, cursor };
1165
+ continue;
1166
+ }
1167
+ yield { type: "liveness", cursor };
1168
+ }
1169
+ }
1170
+ async function rejoinRuntimeOperation(transport, requestId, options = {}) {
1171
+ const response = await requestStream(
1172
+ transport,
1173
+ `/runtime/operations/${encodePathSegment(requestId)}/rejoin`,
1174
+ {
1175
+ method: "POST",
1176
+ // The route takes no body model; the reference client posts an empty
1177
+ // JSON object. Match it so proxies see an ordinary JSON POST.
1178
+ body: {},
1179
+ ...options.signal ? { signal: options.signal } : {}
1180
+ }
1181
+ );
1182
+ return toRunHandle(response, options);
1183
+ }
1184
+
1185
+ // matrx/tools.ts
1186
+ function submitAgentToolResults(transport, conversationId, results, options = {}) {
1187
+ return requestJson(
1188
+ transport,
1189
+ `/ai/conversations/${encodePathSegment(conversationId)}/tool_results`,
1190
+ {
1191
+ method: "POST",
1192
+ body: {
1193
+ results,
1194
+ ...options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
1195
+ },
1196
+ ...options.signal ? { signal: options.signal } : {}
1197
+ }
1198
+ );
1199
+ }
1200
+ function listConversationPendingToolCalls(transport, conversationId, options = {}) {
1201
+ return requestJson(
1202
+ transport,
1203
+ `/ai/conversations/${encodePathSegment(conversationId)}/pending_calls`,
1204
+ {
1205
+ method: "GET",
1206
+ ...options.signal ? { signal: options.signal } : {}
1207
+ }
1208
+ );
1209
+ }
1210
+ function listUserPendingToolCalls(transport, options = {}) {
1211
+ const query = buildQuery(
1212
+ options.instanceId !== void 0 ? { instance_id: options.instanceId } : {}
1213
+ );
1214
+ return requestJson(
1215
+ transport,
1216
+ `/ai/user/pending_calls${query}`,
1217
+ {
1218
+ method: "GET",
1219
+ ...options.signal ? { signal: options.signal } : {}
1220
+ }
1221
+ );
1222
+ }
1223
+
708
1224
  exports.DEFAULT_MATRX_NDJSON_READ_AHEAD = DEFAULT_MATRX_NDJSON_READ_AHEAD;
709
1225
  exports.DEFAULT_WORKFLOW_PROJECTION_LIMITS = DEFAULT_WORKFLOW_PROJECTION_LIMITS;
1226
+ exports.MatrxApiError = MatrxApiError;
1227
+ exports.MatrxRunError = MatrxRunError;
1228
+ exports.TERMINAL_MATRX_RUNTIME_STATUSES = TERMINAL_MATRX_RUNTIME_STATUSES;
1229
+ exports.cancelAgentRun = cancelAgentRun;
1230
+ exports.continueAgentConversation = continueAgentConversation;
1231
+ exports.continueEphemeralConversationStart = continueEphemeralConversationStart;
1232
+ exports.continueStoredConversationStart = continueStoredConversationStart;
710
1233
  exports.createAgentRequestProjection = createAgentRequestProjection;
711
1234
  exports.createMatrxNdjsonFramer = createMatrxNdjsonFramer;
1235
+ exports.createMatrxSseFramer = createMatrxSseFramer;
712
1236
  exports.createWorkflowNodeProjection = createWorkflowNodeProjection;
1237
+ exports.extractMatrxErrorCode = extractMatrxErrorCode;
1238
+ exports.extractMatrxErrorMessage = extractMatrxErrorMessage;
1239
+ exports.followRuntimeOperationEvents = followRuntimeOperationEvents;
1240
+ exports.getRuntimeOperationStatus = getRuntimeOperationStatus;
1241
+ exports.getRuntimeOperationsByLink = getRuntimeOperationsByLink;
1242
+ exports.listConversationPendingToolCalls = listConversationPendingToolCalls;
1243
+ exports.listRuntimeOperationEvents = listRuntimeOperationEvents;
1244
+ exports.listUserPendingToolCalls = listUserPendingToolCalls;
1245
+ exports.mintMatrxConversationId = mintMatrxConversationId;
1246
+ exports.newEphemeralConversationStart = newEphemeralConversationStart;
1247
+ exports.newStoredConversationStart = newStoredConversationStart;
713
1248
  exports.normalizeMatrxStreamEnvelope = normalizeMatrxStreamEnvelope;
1249
+ exports.parseMatrxSseFrame = parseMatrxSseFrame;
714
1250
  exports.projectAgentEvent = projectAgentEvent;
715
1251
  exports.projectAgentEvents = projectAgentEvents;
716
1252
  exports.projectAgentResultForDisplay = projectAgentResultForDisplay;
717
1253
  exports.projectWorkflowNodeEvent = projectWorkflowNodeEvent;
718
1254
  exports.readMatrxNdjsonStream = readMatrxNdjsonStream;
1255
+ exports.readMatrxSseStream = readMatrxSseStream;
1256
+ exports.rejoinRuntimeOperation = rejoinRuntimeOperation;
1257
+ exports.resumeAgentConversation = resumeAgentConversation;
1258
+ exports.runAgentToCompletion = runAgentToCompletion;
1259
+ exports.startAgentRun = startAgentRun;
1260
+ exports.submitAgentToolResults = submitAgentToolResults;
719
1261
  //# sourceMappingURL=index.cjs.map
720
1262
  //# sourceMappingURL=index.cjs.map