@m8tes/sdk 0.1.0-alpha.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.
@@ -0,0 +1,1083 @@
1
+ // src/protocol/events.ts
2
+ var PROTOCOL_VERSION = "m8tes.stream.v2";
3
+ var TERMINAL_EVENT_TYPES = [
4
+ "run-finish",
5
+ "run-error",
6
+ "run-cancelled"
7
+ ];
8
+ function isTerminalEvent(event) {
9
+ return TERMINAL_EVENT_TYPES.includes(event.type);
10
+ }
11
+
12
+ // src/protocol/sse-parser.ts
13
+ function splitConcatenatedJson(payload) {
14
+ const out = [];
15
+ let depth = 0;
16
+ let inString = false;
17
+ let escaped = false;
18
+ let start = -1;
19
+ let lastEnd = 0;
20
+ for (let i = 0; i < payload.length; i++) {
21
+ const ch = payload.charAt(i);
22
+ if (start === -1) {
23
+ if (ch === "{" || ch === "[") {
24
+ start = i;
25
+ depth = 1;
26
+ inString = false;
27
+ escaped = false;
28
+ }
29
+ continue;
30
+ }
31
+ if (inString) {
32
+ if (escaped) escaped = false;
33
+ else if (ch === "\\") escaped = true;
34
+ else if (ch === '"') inString = false;
35
+ continue;
36
+ }
37
+ if (ch === '"') {
38
+ inString = true;
39
+ } else if (ch === "{" || ch === "[") {
40
+ depth++;
41
+ } else if (ch === "}" || ch === "]") {
42
+ depth--;
43
+ if (depth === 0) {
44
+ out.push(payload.slice(start, i + 1));
45
+ start = -1;
46
+ lastEnd = i + 1;
47
+ }
48
+ }
49
+ }
50
+ if (start !== -1) {
51
+ out.push(payload.slice(start));
52
+ } else {
53
+ const tail = payload.slice(lastEnd).trim();
54
+ if (tail !== "") out.push(tail);
55
+ }
56
+ return out;
57
+ }
58
+ function frameDataPayload(frameText) {
59
+ const dataLines = [];
60
+ for (const rawLine of frameText.split("\n")) {
61
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
62
+ if (line === "" || line.charAt(0) === ":") continue;
63
+ if (line.startsWith("data:")) {
64
+ let value = line.slice(5);
65
+ if (value.charAt(0) === " ") value = value.slice(1);
66
+ dataLines.push(value);
67
+ }
68
+ }
69
+ if (dataLines.length === 0) return null;
70
+ return dataLines.join("\n");
71
+ }
72
+ function parsePayload(payload, onMalformed) {
73
+ const out = [];
74
+ for (const candidate of splitConcatenatedJson(payload)) {
75
+ try {
76
+ const parsed = JSON.parse(candidate);
77
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
78
+ out.push(parsed);
79
+ } else {
80
+ onMalformed?.(candidate, new Error("frame is not a JSON object"));
81
+ }
82
+ } catch (err) {
83
+ onMalformed?.(candidate, err);
84
+ }
85
+ }
86
+ return out;
87
+ }
88
+ function createSseDecoder(opts = {}) {
89
+ let buf = "";
90
+ function ingest(chunk) {
91
+ buf += chunk;
92
+ let tail = "";
93
+ if (buf.endsWith("\r")) {
94
+ tail = "\r";
95
+ buf = buf.slice(0, -1);
96
+ }
97
+ buf = buf.replace(/\r\n?/g, "\n") + tail;
98
+ }
99
+ function drain(final) {
100
+ const frames = [];
101
+ let sep = buf.indexOf("\n\n");
102
+ while (sep !== -1) {
103
+ const frameText = buf.slice(0, sep);
104
+ buf = buf.slice(sep + 2);
105
+ const payload = frameDataPayload(frameText);
106
+ if (payload !== null) frames.push(...parsePayload(payload, opts.onMalformed));
107
+ sep = buf.indexOf("\n\n");
108
+ }
109
+ if (final && buf.trim() !== "") {
110
+ const payload = frameDataPayload(buf);
111
+ if (payload !== null) frames.push(...parsePayload(payload, opts.onMalformed));
112
+ buf = "";
113
+ }
114
+ return frames;
115
+ }
116
+ return {
117
+ /** Feed a chunk; returns the wire frames it completed. */
118
+ push(chunk) {
119
+ ingest(chunk);
120
+ return drain(false);
121
+ },
122
+ /** Flush a trailing unterminated frame at end-of-stream. */
123
+ flush() {
124
+ if (buf.endsWith("\r")) buf = `${buf.slice(0, -1)}
125
+ `;
126
+ return drain(true);
127
+ }
128
+ };
129
+ }
130
+ function parseSse(text, opts = {}) {
131
+ const dec = createSseDecoder(opts);
132
+ return [...dec.push(text), ...dec.flush()];
133
+ }
134
+
135
+ // src/protocol/normalizer.ts
136
+ var ASK_USER_QUESTION = "AskUserQuestion";
137
+ var SANDBOX_ERROR_CODES = /* @__PURE__ */ new Set([
138
+ "SANDBOX_BOOT_TIMEOUT",
139
+ "SANDBOX_QUOTA_EXHAUSTED",
140
+ "SANDBOX_UNAVAILABLE",
141
+ "SNAPSHOT_VERSION_MISMATCH",
142
+ "AGENT_RUNNER_DIED",
143
+ "RUNNER_LIFECYCLE_ERROR"
144
+ ]);
145
+ var str = (o, k) => typeof o[k] === "string" ? o[k] : void 0;
146
+ var num = (o, k) => typeof o[k] === "number" ? o[k] : void 0;
147
+ var bool = (o, k) => typeof o[k] === "boolean" ? o[k] : void 0;
148
+ var obj = (o, k) => {
149
+ const v = o[k];
150
+ return v !== null && typeof v === "object" && !Array.isArray(v) ? v : void 0;
151
+ };
152
+ var arr = (o, k) => Array.isArray(o[k]) ? o[k] : void 0;
153
+ function blockKindFromType(t) {
154
+ switch (t) {
155
+ case "text":
156
+ return "text";
157
+ case "thinking":
158
+ case "reasoning":
159
+ return "reasoning";
160
+ case "plan":
161
+ return "plan";
162
+ case "tool_use":
163
+ case "server_tool_use":
164
+ return "tool_use";
165
+ default:
166
+ return "other";
167
+ }
168
+ }
169
+ var blockEndType = {
170
+ text: "text-end",
171
+ reasoning: "reasoning-end",
172
+ plan: "plan-end"
173
+ };
174
+ var blockStartType = {
175
+ text: "text-start",
176
+ reasoning: "reasoning-start",
177
+ plan: "plan-start"
178
+ };
179
+ var blockDeltaType = {
180
+ text: "text-delta",
181
+ reasoning: "reasoning-delta",
182
+ plan: "plan-delta"
183
+ };
184
+ function createNormalizer() {
185
+ let seq = 0;
186
+ let open = /* @__PURE__ */ new Set();
187
+ let seen = /* @__PURE__ */ new Set();
188
+ let blockKind = /* @__PURE__ */ new Map();
189
+ let currentMessageId = null;
190
+ let activeTextBlockId = null;
191
+ let lastToolId = null;
192
+ let lastQuestionToolId = null;
193
+ let anyContentStreamed = false;
194
+ let runStartEmitted = false;
195
+ let terminalEmitted = false;
196
+ function reset() {
197
+ seq = 0;
198
+ open = /* @__PURE__ */ new Set();
199
+ seen = /* @__PURE__ */ new Set();
200
+ blockKind = /* @__PURE__ */ new Map();
201
+ currentMessageId = null;
202
+ activeTextBlockId = null;
203
+ lastToolId = null;
204
+ lastQuestionToolId = null;
205
+ anyContentStreamed = false;
206
+ runStartEmitted = false;
207
+ terminalEmitted = false;
208
+ }
209
+ function emitContentBlockStart(f, out) {
210
+ const id = str(f, "id");
211
+ if (!id) return;
212
+ const kind = blockKindFromType(str(f, "block_type") ?? str(f, "type"));
213
+ const key = kind === "tool_use" ? `tool:${id}` : `block:${id}`;
214
+ if (open.has(key) || seen.has(key)) return;
215
+ blockKind.set(id, kind);
216
+ if (kind === "tool_use") {
217
+ const name = str(f, "name") ?? "";
218
+ if (name === ASK_USER_QUESTION) lastQuestionToolId = id;
219
+ else lastToolId = id;
220
+ open.add(key);
221
+ out.push({
222
+ type: "tool-input-start",
223
+ toolCallId: id,
224
+ toolName: name,
225
+ messageId: currentMessageId ?? "",
226
+ state: "input-streaming"
227
+ });
228
+ } else if (kind === "text" || kind === "reasoning" || kind === "plan") {
229
+ open.add(key);
230
+ if (kind === "text") activeTextBlockId = id;
231
+ anyContentStreamed = true;
232
+ out.push({
233
+ type: blockStartType[kind],
234
+ id,
235
+ messageId: currentMessageId ?? ""
236
+ });
237
+ }
238
+ }
239
+ function emitContentBlockDelta(f, out) {
240
+ const id = str(f, "id");
241
+ if (!id) return;
242
+ const delta = obj(f, "delta");
243
+ if (!delta) return;
244
+ const dType = str(delta, "type");
245
+ if (dType === "input_json_delta") {
246
+ const key2 = `tool:${id}`;
247
+ if (seen.has(key2)) return;
248
+ const partial = str(delta, "partial_json") ?? "";
249
+ if (partial === "") return;
250
+ if (!open.has(key2)) {
251
+ open.add(key2);
252
+ blockKind.set(id, "tool_use");
253
+ }
254
+ out.push({
255
+ type: "tool-input-delta",
256
+ toolCallId: id,
257
+ inputTextDelta: partial,
258
+ state: "input-streaming"
259
+ });
260
+ return;
261
+ }
262
+ let kind = null;
263
+ if (dType === "text_delta") kind = "text";
264
+ else if (dType === "thinking_delta" || dType === "reasoning_delta")
265
+ kind = "reasoning";
266
+ else if (dType === "plan_delta") kind = "plan";
267
+ if (!kind) return;
268
+ const key = `block:${id}`;
269
+ if (seen.has(key)) return;
270
+ const text = kind === "reasoning" ? str(delta, "thinking") ?? str(delta, "text") ?? "" : kind === "plan" ? str(delta, "text") ?? str(delta, "plan") ?? "" : str(delta, "text") ?? "";
271
+ if (text === "") return;
272
+ if (!open.has(key)) {
273
+ open.add(key);
274
+ blockKind.set(id, kind);
275
+ if (kind === "text") activeTextBlockId = id;
276
+ anyContentStreamed = true;
277
+ }
278
+ out.push({ type: blockDeltaType[kind], id, delta: text, messageId: currentMessageId ?? "" });
279
+ }
280
+ function emitContentBlockStop(f, out) {
281
+ const id = str(f, "id");
282
+ if (!id) return;
283
+ const kind = blockKind.get(id) ?? "other";
284
+ if (kind === "tool_use") {
285
+ seen.add(`tool:${id}`);
286
+ open.delete(`tool:${id}`);
287
+ return;
288
+ }
289
+ if (kind === "text" || kind === "reasoning" || kind === "plan") {
290
+ const key = `block:${id}`;
291
+ open.delete(key);
292
+ if (seen.has(key)) return;
293
+ seen.add(key);
294
+ if (activeTextBlockId === id) activeTextBlockId = null;
295
+ out.push({ type: blockEndType[kind], id, messageId: currentMessageId ?? "" });
296
+ }
297
+ }
298
+ function emitMessageDelta(f, out) {
299
+ const delta = obj(f, "delta");
300
+ if (!delta) return;
301
+ const text = str(delta, "text") ?? "";
302
+ if (text === "" || !activeTextBlockId) return;
303
+ const key = `block:${activeTextBlockId}`;
304
+ if (seen.has(key)) return;
305
+ out.push({
306
+ type: "text-delta",
307
+ id: activeTextBlockId,
308
+ delta: text,
309
+ messageId: currentMessageId ?? ""
310
+ });
311
+ }
312
+ function emitStandaloneToolUse(f, out) {
313
+ const id = str(f, "id");
314
+ if (!id) return;
315
+ const name = str(f, "name") ?? "";
316
+ if (name === ASK_USER_QUESTION) lastQuestionToolId = id;
317
+ else if (name) lastToolId = id;
318
+ if ("input" in f) {
319
+ const key = `toolinput:${id}`;
320
+ if (seen.has(key)) return;
321
+ seen.add(key);
322
+ out.push({
323
+ type: "tool-input-available",
324
+ toolCallId: id,
325
+ toolName: name,
326
+ input: f["input"],
327
+ state: "input-available"
328
+ });
329
+ } else {
330
+ const partial = str(f, "input_text");
331
+ if (partial && !seen.has(`tool:${id}`)) {
332
+ out.push({
333
+ type: "tool-input-delta",
334
+ toolCallId: id,
335
+ inputTextDelta: partial,
336
+ state: "input-streaming"
337
+ });
338
+ }
339
+ }
340
+ }
341
+ function emitToolResult(f, out) {
342
+ const id = str(f, "tool_use_id");
343
+ if (!id) return;
344
+ const key = `tool_result:${id}`;
345
+ if (seen.has(key)) return;
346
+ seen.add(key);
347
+ const isError = bool(f, "is_error") ?? false;
348
+ const output = "content" in f ? f["content"] : f["result"];
349
+ out.push({
350
+ type: "tool-output-available",
351
+ toolCallId: id,
352
+ output,
353
+ isError,
354
+ state: isError ? "output-error" : "output-available"
355
+ });
356
+ }
357
+ function emitApprovalGate(f, out) {
358
+ const requestId = str(f, "request_id");
359
+ if (!requestId) return;
360
+ const toolName = str(f, "tool_name") ?? "";
361
+ const timeoutSeconds = num(f, "timeout_seconds") ?? 0;
362
+ const toolInput = f["tool_input"];
363
+ if (toolName === ASK_USER_QUESTION) {
364
+ const key = `question:${requestId}`;
365
+ if (seen.has(key)) return;
366
+ seen.add(key);
367
+ const ti = obj(f, "tool_input");
368
+ const questions = ((ti && arr(ti, "questions")) ?? []).flatMap((q) => {
369
+ if (q === null || typeof q !== "object") return [];
370
+ const qq = q;
371
+ const options = (arr(qq, "options") ?? []).flatMap((o) => {
372
+ if (typeof o === "string") return [{ label: o }];
373
+ if (o === null || typeof o !== "object") return [];
374
+ const label = str(o, "label");
375
+ if (!label) return [];
376
+ const description = str(o, "description");
377
+ return [description === void 0 ? { label } : { label, description }];
378
+ });
379
+ return [{ ...qq, question: str(qq, "question") ?? "", options }];
380
+ });
381
+ out.push({
382
+ type: "question",
383
+ requestId,
384
+ toolCallId: str(f, "tool_use_id") ?? lastQuestionToolId ?? "",
385
+ questions,
386
+ timeoutSeconds
387
+ });
388
+ } else {
389
+ const key = `approval:${requestId}`;
390
+ if (seen.has(key)) return;
391
+ seen.add(key);
392
+ out.push({
393
+ type: "approval-request",
394
+ requestId,
395
+ // The frame's own tool_use_id is authoritative (live awaiting_approval
396
+ // carries it; rejoin permission_request does too as of the run_join
397
+ // fix). The last-tool inference is only a fallback — with PARALLEL tool
398
+ // calls it can name the wrong tool.
399
+ toolCallId: str(f, "tool_use_id") ?? lastToolId ?? void 0,
400
+ toolName,
401
+ toolInput,
402
+ timeoutSeconds
403
+ });
404
+ }
405
+ }
406
+ function emitSnapshot(f, out) {
407
+ const messageId = str(f, "message_id") ?? currentMessageId ?? "";
408
+ const blocks = arr(f, "content_blocks") ?? [];
409
+ for (let i = 0; i < blocks.length; i++) {
410
+ const raw = blocks[i];
411
+ if (raw === null || typeof raw !== "object") continue;
412
+ const b = raw;
413
+ const bType = str(b, "type");
414
+ const kind = blockKindFromType(bType);
415
+ if (kind === "text" || kind === "reasoning" || kind === "plan") {
416
+ let id = str(b, "id");
417
+ if (!id) {
418
+ if (anyContentStreamed) continue;
419
+ id = `${messageId}:snap:${i}`;
420
+ }
421
+ const key = `block:${id}`;
422
+ if (seen.has(key)) continue;
423
+ if (open.has(key)) {
424
+ open.delete(key);
425
+ seen.add(key);
426
+ if (activeTextBlockId === id) activeTextBlockId = null;
427
+ out.push({ type: blockEndType[kind], id, messageId });
428
+ continue;
429
+ }
430
+ seen.add(key);
431
+ out.push({ type: blockStartType[kind], id, messageId });
432
+ const text = kind === "reasoning" ? str(b, "thinking") ?? str(b, "text") ?? "" : kind === "plan" ? str(b, "plan") ?? str(b, "text") ?? "" : str(b, "text") ?? "";
433
+ if (text !== "") out.push({ type: blockDeltaType[kind], id, delta: text, messageId });
434
+ out.push({ type: blockEndType[kind], id, messageId });
435
+ } else if (bType === "tool_use") {
436
+ const id = str(b, "id");
437
+ if (!id || seen.has(`toolinput:${id}`)) continue;
438
+ seen.add(`toolinput:${id}`);
439
+ out.push({
440
+ type: "tool-input-available",
441
+ toolCallId: id,
442
+ toolName: str(b, "name") ?? "",
443
+ input: b["input"],
444
+ state: "input-available"
445
+ });
446
+ } else if (bType === "tool_result") {
447
+ const id = str(b, "tool_use_id");
448
+ if (!id || seen.has(`tool_result:${id}`)) continue;
449
+ seen.add(`tool_result:${id}`);
450
+ const isError = bool(b, "is_error") ?? false;
451
+ out.push({
452
+ type: "tool-output-available",
453
+ toolCallId: id,
454
+ output: "content" in b ? b["content"] : b["result"],
455
+ isError,
456
+ state: isError ? "output-error" : "output-available"
457
+ });
458
+ }
459
+ }
460
+ }
461
+ function emitNotice(out, level, source, message, detail) {
462
+ out.push(detail === void 0 ? { type: "notice", level, source, message } : { type: "notice", level, source, message, detail });
463
+ }
464
+ function handle(frame, out) {
465
+ const type = str(frame, "type") ?? "";
466
+ switch (type) {
467
+ /* ---- dropped no-ops ---- */
468
+ case "keep-alive":
469
+ case "ping":
470
+ case "RUNNER_DIAG":
471
+ case "sdk_init":
472
+ case "sandbox_metrics":
473
+ return;
474
+ /* ---- run lifecycle ---- */
475
+ case "metadata": {
476
+ if (runStartEmitted) return;
477
+ runStartEmitted = true;
478
+ const sandboxId = str(frame, "sandbox_id") ?? null;
479
+ const sandboxEnabled = bool(frame, "sandbox_enabled") ?? false;
480
+ out.push({
481
+ type: "run-start",
482
+ protocolVersion: PROTOCOL_VERSION,
483
+ runId: num(frame, "run_id") ?? null,
484
+ runUuid: str(frame, "run_uuid") ?? null,
485
+ mode: str(frame, "mode") === "task" ? "task" : "chat",
486
+ ...str(frame, "agent_name") ? { agentName: str(frame, "agent_name") } : {},
487
+ ...num(frame, "instance_id") !== void 0 ? { instanceId: num(frame, "instance_id") } : {},
488
+ ...num(frame, "task_id") !== void 0 ? { taskId: num(frame, "task_id") } : {},
489
+ ...str(frame, "status") ? { status: str(frame, "status") } : {},
490
+ sandbox: { id: sandboxId, enabled: sandboxEnabled }
491
+ });
492
+ return;
493
+ }
494
+ case "run_metrics": {
495
+ out.push({
496
+ type: "run-metrics",
497
+ model: str(frame, "model") ?? "",
498
+ executionTimeMs: num(frame, "execution_time_ms") ?? 0,
499
+ inputTokens: num(frame, "input_tokens_used") ?? 0,
500
+ outputTokens: num(frame, "output_tokens_used") ?? 0,
501
+ costUsd: num(frame, "claude_token_cost_usd") ?? null,
502
+ sdkCostUsd: num(frame, "sdk_total_cost_usd") ?? null,
503
+ stopReason: str(frame, "stop_reason") ?? null,
504
+ completionState: str(frame, "completion_state") ?? "",
505
+ unresolvedToolUseIds: (arr(frame, "unresolved_tool_use_ids") ?? []).filter(
506
+ (x) => typeof x === "string"
507
+ ),
508
+ messageCount: num(frame, "message_count") ?? 0,
509
+ ...obj(frame, "diagnostics") ? { diagnostics: obj(frame, "diagnostics") } : {}
510
+ });
511
+ return;
512
+ }
513
+ case "done": {
514
+ if (terminalEmitted) return;
515
+ terminalEmitted = true;
516
+ const completionState = str(frame, "completion_state") ?? "complete";
517
+ out.push({
518
+ type: "run-finish",
519
+ status: completionState === "complete" ? "complete" : "incomplete",
520
+ completionState,
521
+ unresolvedToolUseIds: (arr(frame, "unresolved_tool_use_ids") ?? []).filter(
522
+ (x) => typeof x === "string"
523
+ ),
524
+ stopReason: str(frame, "stop_reason") ?? null,
525
+ terminalCompletionSeen: bool(frame, "terminal_completion_seen") ?? false,
526
+ messageCount: num(frame, "message_count") ?? 0
527
+ });
528
+ return;
529
+ }
530
+ case "cancelled": {
531
+ if (terminalEmitted) return;
532
+ terminalEmitted = true;
533
+ out.push({ type: "run-cancelled", runId: num(frame, "run_id") ?? null });
534
+ return;
535
+ }
536
+ case "error":
537
+ case "AGENT_RUNNER_DIED":
538
+ case "RUNNER_LIFECYCLE_ERROR":
539
+ case "SANDBOX_BOOT_TIMEOUT":
540
+ case "SANDBOX_QUOTA_EXHAUSTED":
541
+ case "SANDBOX_UNAVAILABLE":
542
+ case "SNAPSHOT_VERSION_MISMATCH": {
543
+ if (terminalEmitted) return;
544
+ terminalEmitted = true;
545
+ const code = SANDBOX_ERROR_CODES.has(type) ? type : null;
546
+ out.push({
547
+ type: "run-error",
548
+ error: str(frame, "error") ?? str(frame, "message") ?? type,
549
+ message: str(frame, "message") ?? "Agent execution failed",
550
+ code,
551
+ fatal: true
552
+ });
553
+ return;
554
+ }
555
+ case "sdk_error": {
556
+ emitNotice(out, "warning", "system", str(frame, "error") ?? "SDK error");
557
+ return;
558
+ }
559
+ case "sdk_success": {
560
+ if (bool(frame, "is_error") !== true) {
561
+ out.push({ type: "unknown", wireType: type });
562
+ return;
563
+ }
564
+ if (terminalEmitted) return;
565
+ terminalEmitted = true;
566
+ out.push({
567
+ type: "run-error",
568
+ error: str(frame, "subtype") ?? "sdk_error",
569
+ message: str(frame, "result") ?? "The model provider returned an error.",
570
+ code: null,
571
+ fatal: true
572
+ });
573
+ return;
574
+ }
575
+ /* ---- message lifecycle ---- */
576
+ case "message_start": {
577
+ const msg = obj(frame, "message");
578
+ const id = (msg && str(msg, "id")) ?? str(frame, "message_id");
579
+ if (!id) return;
580
+ currentMessageId = id;
581
+ const key = `message:${id}`;
582
+ if (seen.has(key)) return;
583
+ seen.add(key);
584
+ const usage = msg && obj(msg, "usage");
585
+ out.push({
586
+ type: "message-start",
587
+ messageId: id,
588
+ role: "assistant",
589
+ ...usage ? { usage: { inputTokens: num(usage, "input_tokens") ?? 0, outputTokens: num(usage, "output_tokens") ?? 0 } } : {}
590
+ });
591
+ return;
592
+ }
593
+ case "message_stop":
594
+ case "message_complete": {
595
+ const id = str(frame, "message_id") ?? currentMessageId;
596
+ if (!id) return;
597
+ const key = `message:${id}:end`;
598
+ if (seen.has(key)) return;
599
+ seen.add(key);
600
+ const usage = obj(frame, "usage");
601
+ out.push({
602
+ type: "message-end",
603
+ messageId: id,
604
+ ...usage ? { usage: { inputTokens: num(usage, "input_tokens") ?? 0, outputTokens: num(usage, "output_tokens") ?? 0 } } : {}
605
+ });
606
+ return;
607
+ }
608
+ case "message_snapshot":
609
+ emitSnapshot(frame, out);
610
+ return;
611
+ /* ---- content blocks ---- */
612
+ case "content_block_start":
613
+ emitContentBlockStart(frame, out);
614
+ return;
615
+ case "content_block_delta":
616
+ emitContentBlockDelta(frame, out);
617
+ return;
618
+ case "content_block_stop":
619
+ emitContentBlockStop(frame, out);
620
+ return;
621
+ case "message_delta":
622
+ emitMessageDelta(frame, out);
623
+ return;
624
+ /* ---- tools ---- */
625
+ case "tool_use":
626
+ emitStandaloneToolUse(frame, out);
627
+ return;
628
+ case "tool_result":
629
+ emitToolResult(frame, out);
630
+ return;
631
+ /* ---- standalone reasoning / plan: passthrough as `unknown` for v1 ----
632
+ * These top-level (non-content_block) frames carry full content with no
633
+ * wire id, so they cannot be deduped across a rejoin and two bursts would
634
+ * collide on a synthetic id. Block-based reasoning (content_block
635
+ * reasoning_delta) is the dedupe-safe path and IS handled. Surface these
636
+ * raw until their real wire behavior is confirmed (CONTRACT.md open Q). */
637
+ /* ---- HITL ---- */
638
+ case "awaiting_approval":
639
+ case "permission_request":
640
+ emitApprovalGate(frame, out);
641
+ return;
642
+ /* ---- sandbox + notices ---- */
643
+ case "sandbox-connecting":
644
+ out.push({
645
+ type: "sandbox-status",
646
+ phase: "connecting",
647
+ message: str(frame, "message") ?? "Connecting to sandbox",
648
+ runId: num(frame, "run_id") ?? null
649
+ });
650
+ return;
651
+ case "sandbox-connected":
652
+ out.push({
653
+ type: "sandbox-status",
654
+ phase: "connected",
655
+ message: str(frame, "message") ?? "Connected to sandbox",
656
+ runId: num(frame, "run_id") ?? null,
657
+ ...str(frame, "sandbox_id") !== void 0 ? { sandboxId: str(frame, "sandbox_id") } : {},
658
+ ...num(frame, "duration_ms") !== void 0 ? { durationMs: num(frame, "duration_ms") } : {}
659
+ });
660
+ return;
661
+ case "stderr":
662
+ emitNotice(out, "info", "stderr", str(frame, "message") ?? "");
663
+ return;
664
+ case "mcp_error":
665
+ case "mcp_auth_failed":
666
+ emitNotice(out, "warning", "mcp", str(frame, "message") ?? str(frame, "error") ?? "MCP error", str(frame, "detail"));
667
+ return;
668
+ case "rate_limit": {
669
+ const info = obj(frame, "rate_limit_info");
670
+ const status = (info && str(info, "status")) ?? "allowed";
671
+ emitNotice(out, status === "rejected" ? "warning" : "info", "rate_limit", `Rate limit: ${status}`);
672
+ return;
673
+ }
674
+ case "compact_boundary":
675
+ out.push({
676
+ type: "compact-boundary",
677
+ trigger: str(frame, "trigger") === "manual" ? "manual" : "auto",
678
+ preTokens: num(frame, "pre_tokens") ?? 0
679
+ });
680
+ return;
681
+ /* ---- passthrough ---- */
682
+ default:
683
+ out.push({ type: "unknown", wireType: type });
684
+ return;
685
+ }
686
+ }
687
+ return {
688
+ push(frame) {
689
+ const partials = [];
690
+ handle(frame, partials);
691
+ return partials.map(
692
+ (e) => ({ ...e, seq: seq++, raw: frame })
693
+ );
694
+ },
695
+ reset
696
+ };
697
+ }
698
+
699
+ // src/protocol/accumulator.ts
700
+ var initialConversationState = {
701
+ runId: null,
702
+ runUuid: null,
703
+ agentName: null,
704
+ status: "idle",
705
+ messages: [],
706
+ pendingApproval: null,
707
+ pendingQuestion: null,
708
+ finishStopReason: null,
709
+ error: null
710
+ };
711
+ function lastAssistantIndex(messages) {
712
+ for (let i = messages.length - 1; i >= 0; i--) {
713
+ if (messages[i].role === "assistant") return i;
714
+ }
715
+ return -1;
716
+ }
717
+ function withMessageParts(state, messageId, role, fn) {
718
+ const messages = state.messages.slice();
719
+ let idx = messageId ? messages.findIndex((m) => m.id === messageId) : lastAssistantIndex(messages);
720
+ if (idx === -1) {
721
+ messages.push({ id: messageId || `m:${messages.length}`, role, parts: [] });
722
+ idx = messages.length - 1;
723
+ }
724
+ const msg = messages[idx];
725
+ messages[idx] = { ...msg, parts: fn(msg.parts) };
726
+ return { ...state, messages };
727
+ }
728
+ var isTextLike = (p) => p.kind === "text" || p.kind === "reasoning" || p.kind === "plan";
729
+ function ensureTextPart(parts, kind, id) {
730
+ if (parts.some((p) => isTextLike(p) && p.id === id)) return parts;
731
+ return [...parts, { kind, id, text: "" }];
732
+ }
733
+ function applyTextDelta(parts, kind, id, delta) {
734
+ const i = parts.findIndex((p2) => isTextLike(p2) && p2.id === id);
735
+ if (i === -1) return [...parts, { kind, id, text: delta }];
736
+ const p = parts[i];
737
+ const next = parts.slice();
738
+ next[i] = { ...p, text: p.text + delta };
739
+ return next;
740
+ }
741
+ function updateToolPart(state, toolCallId, toolName, patch) {
742
+ const messages = state.messages.slice();
743
+ for (let mi = 0; mi < messages.length; mi++) {
744
+ const parts = messages[mi].parts;
745
+ const pi = parts.findIndex((p) => p.kind === "tool" && p.toolCallId === toolCallId);
746
+ if (pi !== -1) {
747
+ const np = parts.slice();
748
+ np[pi] = patch(parts[pi]);
749
+ messages[mi] = { ...messages[mi], parts: np };
750
+ return { ...state, messages };
751
+ }
752
+ }
753
+ let idx = lastAssistantIndex(messages);
754
+ if (idx === -1) {
755
+ messages.push({ id: `m:${messages.length}`, role: "assistant", parts: [] });
756
+ idx = messages.length - 1;
757
+ }
758
+ const base = {
759
+ kind: "tool",
760
+ toolCallId,
761
+ toolName: toolName ?? "",
762
+ state: "input-streaming",
763
+ inputText: ""
764
+ };
765
+ messages[idx] = { ...messages[idx], parts: [...messages[idx].parts, patch(base)] };
766
+ return { ...state, messages };
767
+ }
768
+ function resume(status) {
769
+ return status === "awaiting_approval" || status === "awaiting_input" ? "running" : status;
770
+ }
771
+ function accumulate(state, e) {
772
+ switch (e.type) {
773
+ case "run-start":
774
+ return {
775
+ ...state,
776
+ runId: e.runId,
777
+ runUuid: e.runUuid,
778
+ agentName: e.agentName ?? state.agentName ?? null,
779
+ status: "running",
780
+ error: null,
781
+ // A retry re-drives the turn: the previous attempt's outcome row
782
+ // ("Response truncated", "Request declined") must not survive it.
783
+ finishStopReason: null
784
+ };
785
+ case "run-status":
786
+ return { ...state, status: e.status };
787
+ case "message-start":
788
+ return withMessageParts(state, e.messageId, e.role, (p) => p);
789
+ case "text-start":
790
+ return {
791
+ ...withMessageParts(state, e.messageId, "assistant", (p) => ensureTextPart(p, "text", e.id)),
792
+ status: resume(state.status)
793
+ };
794
+ case "text-delta":
795
+ return {
796
+ ...withMessageParts(state, e.messageId, "assistant", (p) => applyTextDelta(p, "text", e.id, e.delta)),
797
+ status: resume(state.status)
798
+ };
799
+ case "reasoning-start":
800
+ return withMessageParts(state, e.messageId, "assistant", (p) => ensureTextPart(p, "reasoning", e.id));
801
+ case "reasoning-delta":
802
+ return withMessageParts(state, e.messageId, "assistant", (p) => applyTextDelta(p, "reasoning", e.id, e.delta));
803
+ case "plan-start":
804
+ return withMessageParts(state, e.messageId, "assistant", (p) => ensureTextPart(p, "plan", e.id));
805
+ case "plan-delta":
806
+ return withMessageParts(state, e.messageId, "assistant", (p) => applyTextDelta(p, "plan", e.id, e.delta));
807
+ case "text-end":
808
+ case "reasoning-end":
809
+ case "plan-end":
810
+ case "message-end":
811
+ return state;
812
+ case "tool-input-start":
813
+ return {
814
+ ...withMessageParts(
815
+ state,
816
+ e.messageId,
817
+ "assistant",
818
+ (p) => p.some((x) => x.kind === "tool" && x.toolCallId === e.toolCallId) ? p : [
819
+ ...p,
820
+ {
821
+ kind: "tool",
822
+ toolCallId: e.toolCallId,
823
+ toolName: e.toolName,
824
+ state: "input-streaming",
825
+ inputText: ""
826
+ }
827
+ ]
828
+ ),
829
+ status: resume(state.status)
830
+ };
831
+ case "tool-input-delta":
832
+ return updateToolPart(state, e.toolCallId, void 0, (t) => ({
833
+ ...t,
834
+ inputText: t.inputText + e.inputTextDelta,
835
+ state: "input-streaming"
836
+ }));
837
+ case "tool-input-available":
838
+ return updateToolPart(state, e.toolCallId, e.toolName, (t) => ({
839
+ ...t,
840
+ input: e.input,
841
+ state: "input-available"
842
+ }));
843
+ case "tool-output-available": {
844
+ const s = updateToolPart(state, e.toolCallId, void 0, (t) => ({
845
+ ...t,
846
+ output: e.output,
847
+ isError: e.isError,
848
+ state: e.state
849
+ }));
850
+ const pendingApproval = s.pendingApproval && s.pendingApproval.toolCallId === e.toolCallId ? null : s.pendingApproval;
851
+ const pendingQuestion = s.pendingQuestion && s.pendingQuestion.toolCallId === e.toolCallId ? null : s.pendingQuestion;
852
+ return { ...s, pendingApproval, pendingQuestion, status: resume(s.status) };
853
+ }
854
+ case "approval-request":
855
+ return { ...state, pendingApproval: e, status: "awaiting_approval" };
856
+ case "approval-resolved":
857
+ return state.pendingApproval && state.pendingApproval.requestId === e.requestId ? { ...state, pendingApproval: null } : state;
858
+ case "question":
859
+ return { ...state, pendingQuestion: e, status: "awaiting_input" };
860
+ case "run-finish":
861
+ return { ...state, status: e.status, pendingApproval: null, pendingQuestion: null };
862
+ case "run-error":
863
+ return {
864
+ ...state,
865
+ status: "error",
866
+ error: {
867
+ message: e.message,
868
+ code: e.code,
869
+ ...e.apiStatus !== void 0 ? { apiStatus: e.apiStatus } : {},
870
+ ...e.apiDetails !== void 0 ? { apiDetails: e.apiDetails } : {}
871
+ },
872
+ pendingApproval: null,
873
+ pendingQuestion: null
874
+ };
875
+ case "run-cancelled":
876
+ return { ...state, status: "cancelled", pendingApproval: null, pendingQuestion: null };
877
+ case "sandbox-status": {
878
+ const withStatus = e.phase === "connecting" && state.status === "idle" ? { ...state, status: "connecting" } : state;
879
+ if (e.phase === "connecting") {
880
+ return withMessageParts(withStatus, "", "assistant", (p) => [
881
+ ...p,
882
+ { kind: "sandbox", id: `sb:${p.length}`, phase: "connecting" }
883
+ ]);
884
+ }
885
+ const messages = withStatus.messages.slice();
886
+ for (let mi = messages.length - 1; mi >= 0; mi--) {
887
+ const parts = messages[mi].parts;
888
+ for (let pi = parts.length - 1; pi >= 0; pi--) {
889
+ const part = parts[pi];
890
+ if (part.kind === "sandbox" && part.phase === "connecting") {
891
+ const np = parts.slice();
892
+ np[pi] = { ...part, phase: "connected", ...e.durationMs !== void 0 ? { durationMs: e.durationMs } : {} };
893
+ messages[mi] = { ...messages[mi], parts: np };
894
+ return { ...withStatus, messages };
895
+ }
896
+ }
897
+ }
898
+ return withMessageParts(withStatus, "", "assistant", (p) => [
899
+ ...p,
900
+ { kind: "sandbox", id: `sb:${p.length}`, phase: "connected", ...e.durationMs !== void 0 ? { durationMs: e.durationMs } : {} }
901
+ ]);
902
+ }
903
+ case "notice":
904
+ if (e.level !== "warning") return state;
905
+ return withMessageParts(state, "", "assistant", (p) => [
906
+ ...p,
907
+ { kind: "notice", id: `n:${p.length}`, level: e.level, source: e.source, message: e.message }
908
+ ]);
909
+ case "compact-boundary":
910
+ return withMessageParts(state, "", "assistant", (p) => [
911
+ ...p,
912
+ { kind: "compact", id: `c:${p.length}`, trigger: e.trigger, preTokens: e.preTokens }
913
+ ]);
914
+ case "run-metrics":
915
+ return e.stopReason === "refusal" || e.stopReason === "max_tokens" ? { ...state, finishStopReason: e.stopReason } : state;
916
+ case "unknown":
917
+ return state;
918
+ default:
919
+ return state;
920
+ }
921
+ }
922
+ function createAccumulator() {
923
+ let state = initialConversationState;
924
+ return {
925
+ get state() {
926
+ return state;
927
+ },
928
+ apply(event) {
929
+ state = accumulate(state, event);
930
+ return state;
931
+ },
932
+ addUserMessage(text) {
933
+ const id = `u:${state.messages.length}`;
934
+ state = {
935
+ ...state,
936
+ messages: [...state.messages, { id, role: "user", parts: [{ kind: "text", id, text }] }]
937
+ };
938
+ return state;
939
+ },
940
+ reset() {
941
+ state = initialConversationState;
942
+ }
943
+ };
944
+ }
945
+
946
+ // src/protocol/errors.ts
947
+ var M8tesApiError = class extends Error {
948
+ type;
949
+ code;
950
+ status;
951
+ requestId;
952
+ details;
953
+ docUrl;
954
+ errorCode;
955
+ retryAfter;
956
+ constructor(message, f) {
957
+ super(message);
958
+ this.name = "M8tesApiError";
959
+ this.type = f.type;
960
+ this.code = f.code;
961
+ this.status = f.status;
962
+ this.requestId = f.requestId;
963
+ this.details = f.details;
964
+ this.docUrl = f.docUrl;
965
+ this.errorCode = f.errorCode;
966
+ this.retryAfter = f.retryAfter;
967
+ }
968
+ };
969
+ var ValidationError = class extends M8tesApiError {
970
+ constructor(message, f) {
971
+ super(message, f);
972
+ this.name = "ValidationError";
973
+ }
974
+ };
975
+ var AuthenticationError = class extends M8tesApiError {
976
+ constructor(message, f) {
977
+ super(message, f);
978
+ this.name = "AuthenticationError";
979
+ }
980
+ };
981
+ var BillingError = class extends M8tesApiError {
982
+ constructor(message, f) {
983
+ super(message, f);
984
+ this.name = "BillingError";
985
+ }
986
+ };
987
+ var PermissionDeniedError = class extends M8tesApiError {
988
+ constructor(message, f) {
989
+ super(message, f);
990
+ this.name = "PermissionDeniedError";
991
+ }
992
+ };
993
+ var NotFoundError = class extends M8tesApiError {
994
+ constructor(message, f) {
995
+ super(message, f);
996
+ this.name = "NotFoundError";
997
+ }
998
+ };
999
+ var ConflictError = class extends M8tesApiError {
1000
+ constructor(message, f) {
1001
+ super(message, f);
1002
+ this.name = "ConflictError";
1003
+ }
1004
+ };
1005
+ var RunNotStreamingError = class extends ConflictError {
1006
+ constructor(message, f) {
1007
+ super(message, f);
1008
+ this.name = "RunNotStreamingError";
1009
+ }
1010
+ };
1011
+ var RateLimitError = class extends M8tesApiError {
1012
+ constructor(message, f) {
1013
+ super(message, f);
1014
+ this.name = "RateLimitError";
1015
+ }
1016
+ };
1017
+ var APIError = class extends M8tesApiError {
1018
+ constructor(message, f) {
1019
+ super(message, f);
1020
+ this.name = "APIError";
1021
+ }
1022
+ };
1023
+ var RunFailedError = class extends M8tesApiError {
1024
+ constructor(message, f) {
1025
+ super(message, f);
1026
+ this.name = "RunFailedError";
1027
+ }
1028
+ };
1029
+ var STATUS_MAP = {
1030
+ 400: ValidationError,
1031
+ 401: AuthenticationError,
1032
+ 402: BillingError,
1033
+ 403: PermissionDeniedError,
1034
+ 404: NotFoundError,
1035
+ 409: ConflictError,
1036
+ 422: ValidationError,
1037
+ 429: RateLimitError
1038
+ };
1039
+ function parseRetryAfter(raw) {
1040
+ if (!raw) return void 0;
1041
+ const seconds = Number(raw);
1042
+ return Number.isFinite(seconds) ? seconds : void 0;
1043
+ }
1044
+ function str2(o, key) {
1045
+ return typeof o[key] === "string" ? o[key] : void 0;
1046
+ }
1047
+ function parseErrorEnvelope(status, body, headers) {
1048
+ const e = body !== null && typeof body === "object" && "error" in body && typeof body.error === "object" ? body.error ?? {} : {};
1049
+ const details = e["details"];
1050
+ const errorCode = details !== null && typeof details === "object" ? str2(details, "error_code") : void 0;
1051
+ return {
1052
+ message: str2(e, "message") ?? `Request failed (${status})`,
1053
+ fields: {
1054
+ type: str2(e, "type") ?? "api_error",
1055
+ code: typeof e["code"] === "number" ? e["code"] : status,
1056
+ status,
1057
+ requestId: str2(e, "request_id") ?? headers?.get("x-request-id") ?? void 0,
1058
+ details,
1059
+ docUrl: str2(e, "doc_url"),
1060
+ errorCode,
1061
+ retryAfter: parseRetryAfter(headers?.get("retry-after"))
1062
+ }
1063
+ };
1064
+ }
1065
+ function errorClassForStatus(status, opts = {}) {
1066
+ if (status === 409 && opts.conflictIsNotStreaming) return RunNotStreamingError;
1067
+ return STATUS_MAP[status] ?? APIError;
1068
+ }
1069
+ async function errorFromResponse(res, opts = {}) {
1070
+ let body;
1071
+ try {
1072
+ body = await res.json();
1073
+ } catch {
1074
+ body = void 0;
1075
+ }
1076
+ const { message, fields } = parseErrorEnvelope(res.status, body, res.headers);
1077
+ const Cls = errorClassForStatus(res.status, opts);
1078
+ return new Cls(message, fields);
1079
+ }
1080
+
1081
+ export { APIError, AuthenticationError, BillingError, ConflictError, M8tesApiError, NotFoundError, PROTOCOL_VERSION, PermissionDeniedError, RateLimitError, RunFailedError, RunNotStreamingError, TERMINAL_EVENT_TYPES, ValidationError, accumulate, createAccumulator, createNormalizer, createSseDecoder, errorClassForStatus, errorFromResponse, initialConversationState, isTerminalEvent, parseErrorEnvelope, parseRetryAfter, parseSse, splitConcatenatedJson };
1082
+ //# sourceMappingURL=chunk-UFQQNUFE.js.map
1083
+ //# sourceMappingURL=chunk-UFQQNUFE.js.map