@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.
- package/CHANGELOG.md +100 -0
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/chunk-UFQQNUFE.js +1083 -0
- package/dist/chunk-UFQQNUFE.js.map +1 -0
- package/dist/fixtures.cjs +297 -0
- package/dist/fixtures.cjs.map +1 -0
- package/dist/fixtures.d.cts +81 -0
- package/dist/fixtures.d.ts +81 -0
- package/dist/fixtures.js +295 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.cjs +1783 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +937 -0
- package/dist/index.d.ts +937 -0
- package/dist/index.js +672 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/index.cjs +1109 -0
- package/dist/protocol/index.cjs.map +1 -0
- package/dist/protocol/index.d.cts +576 -0
- package/dist/protocol/index.d.ts +576 -0
- package/dist/protocol/index.js +3 -0
- package/dist/protocol/index.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @m8tes/react — normalized PUBLIC stream event model.
|
|
3
|
+
*
|
|
4
|
+
* The m8tes V2 backend emits Claude-native FLAT SSE frames (`content_block_delta`,
|
|
5
|
+
* `tool_use`, `permission_request`, `done`, ...). The normalizer (`normalizer.ts`)
|
|
6
|
+
* maps each wire frame to ZERO or MORE events of this stable, versioned
|
|
7
|
+
* discriminated union, keyed on `type`.
|
|
8
|
+
*
|
|
9
|
+
* Type discriminants follow the Vercel AI SDK v5 kebab-case idiom where semantics
|
|
10
|
+
* align (`text-*`, `reasoning-*`, `tool-input-*`, `tool-output-available`) so a
|
|
11
|
+
* future `@m8tes/ai-sdk` adapter maps cleanly. They DIVERGE where m8tes semantics
|
|
12
|
+
* are first-class and have no vanilla-`useChat` analog: human-in-the-loop
|
|
13
|
+
* `approval-request` / `question`, and run rejoin/replay (`run-start` carries
|
|
14
|
+
* runId/runUuid; every terminal path is explicit).
|
|
15
|
+
*
|
|
16
|
+
* Every event carries:
|
|
17
|
+
* - `seq`: a client-assigned monotonic counter. The wire exposes NO SSE `id:`
|
|
18
|
+
* and NO per-event sequence number, so this is assigned by the normalizer and
|
|
19
|
+
* is NOT a dedupe key (see `normalizer.ts` for the domain-id dedupe strategy).
|
|
20
|
+
* - `raw`: the unmodified wire frame, for forward-compatibility.
|
|
21
|
+
*
|
|
22
|
+
* Frames that normalize to nothing renderable (`: keep-alive`, `keep-alive`,
|
|
23
|
+
* `ping`, the duplicate `metadata` preamble frame, `RUNNER_DIAG`, etc.) are
|
|
24
|
+
* dropped by the normalizer and never enter this union.
|
|
25
|
+
*
|
|
26
|
+
* This is a PUBLIC PROTOCOL: once shipped, names are semver-stable. Bump
|
|
27
|
+
* `PROTOCOL_VERSION` on any breaking change to the shape.
|
|
28
|
+
*/
|
|
29
|
+
declare const PROTOCOL_VERSION: "m8tes.stream.v2";
|
|
30
|
+
type ProtocolVersion = typeof PROTOCOL_VERSION;
|
|
31
|
+
/** Tool/approval state machine (superset of AI SDK v5 tool-part states + HITL). */
|
|
32
|
+
type ToolState = "input-streaming" | "input-available" | "awaiting-approval" | "output-available" | "output-error" | "denied";
|
|
33
|
+
/** Coarse run status surfaced to the UI. */
|
|
34
|
+
type RunStatus = "connecting" | "running" | "awaiting_approval" | "awaiting_input" | "complete" | "incomplete" | "error" | "cancelled";
|
|
35
|
+
interface BaseEvent {
|
|
36
|
+
type: string;
|
|
37
|
+
/** Client-assigned, monotonic per stream. There is NO wire sequence id. */
|
|
38
|
+
seq: number;
|
|
39
|
+
/** The unmodified wire frame this event was normalized from. */
|
|
40
|
+
raw: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
/** From the first `metadata` frame. Re-applied (idempotently) on every rejoin. */
|
|
43
|
+
interface RunStartEvent extends BaseEvent {
|
|
44
|
+
type: "run-start";
|
|
45
|
+
protocolVersion: ProtocolVersion;
|
|
46
|
+
runId: number | null;
|
|
47
|
+
runUuid: string | null;
|
|
48
|
+
mode: "task" | "chat";
|
|
49
|
+
agentName?: string;
|
|
50
|
+
instanceId?: number;
|
|
51
|
+
taskId?: number;
|
|
52
|
+
status?: string;
|
|
53
|
+
sandbox?: {
|
|
54
|
+
id: string | null;
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Coarse status transitions (sandbox connect, metadata.status, pause entry). */
|
|
59
|
+
interface RunStatusEvent extends BaseEvent {
|
|
60
|
+
type: "run-status";
|
|
61
|
+
status: RunStatus;
|
|
62
|
+
message?: string;
|
|
63
|
+
sandboxId?: string | null;
|
|
64
|
+
durationMs?: number;
|
|
65
|
+
}
|
|
66
|
+
/** From `run_metrics`. */
|
|
67
|
+
interface RunMetricsEvent extends BaseEvent {
|
|
68
|
+
type: "run-metrics";
|
|
69
|
+
model: string;
|
|
70
|
+
executionTimeMs: number;
|
|
71
|
+
inputTokens: number;
|
|
72
|
+
outputTokens: number;
|
|
73
|
+
costUsd: number | null;
|
|
74
|
+
sdkCostUsd: number | null;
|
|
75
|
+
stopReason: string | null;
|
|
76
|
+
completionState: string;
|
|
77
|
+
unresolvedToolUseIds: string[];
|
|
78
|
+
messageCount: number;
|
|
79
|
+
/** present only when completionState !== "complete" */
|
|
80
|
+
diagnostics?: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
/** Terminal success path. From `done`. completionState !== "complete" => incomplete run. */
|
|
83
|
+
interface RunFinishEvent extends BaseEvent {
|
|
84
|
+
type: "run-finish";
|
|
85
|
+
status: "complete" | "incomplete";
|
|
86
|
+
completionState: string;
|
|
87
|
+
unresolvedToolUseIds: string[];
|
|
88
|
+
stopReason: string | null;
|
|
89
|
+
terminalCompletionSeen: boolean;
|
|
90
|
+
messageCount: number;
|
|
91
|
+
}
|
|
92
|
+
/** Structured failure codes carried on the wire as a dedicated `type`. */
|
|
93
|
+
type RunErrorCode = "SANDBOX_BOOT_TIMEOUT" | "SANDBOX_QUOTA_EXHAUSTED" | "SANDBOX_UNAVAILABLE" | "SNAPSHOT_VERSION_MISMATCH" | "AGENT_RUNNER_DIED" | "RUNNER_LIFECYCLE_ERROR";
|
|
94
|
+
/** Terminal failure path. From `error`, `sdk_error`, `AGENT_RUNNER_DIED`, SANDBOX_*, SNAPSHOT_VERSION_MISMATCH. */
|
|
95
|
+
interface RunErrorEvent extends BaseEvent {
|
|
96
|
+
type: "run-error";
|
|
97
|
+
error: string;
|
|
98
|
+
message: string;
|
|
99
|
+
/** structured failure code when the wire type was a coded failure, else null */
|
|
100
|
+
code: RunErrorCode | null;
|
|
101
|
+
fatal: true;
|
|
102
|
+
/** HTTP status when the failure was an API (non-stream) error, e.g. 402/429 */
|
|
103
|
+
apiStatus?: number;
|
|
104
|
+
/** v2 envelope `error.details` — carries machine codes like
|
|
105
|
+
* `error_code: "END_USER_RATE_LIMITED"` plus `retry_after`/`period_end` */
|
|
106
|
+
apiDetails?: Record<string, unknown>;
|
|
107
|
+
}
|
|
108
|
+
/** Terminal cancellation. From `cancelled`. */
|
|
109
|
+
interface RunCancelledEvent extends BaseEvent {
|
|
110
|
+
type: "run-cancelled";
|
|
111
|
+
runId?: number | null;
|
|
112
|
+
}
|
|
113
|
+
interface MessageStartEvent extends BaseEvent {
|
|
114
|
+
type: "message-start";
|
|
115
|
+
messageId: string;
|
|
116
|
+
role: "assistant";
|
|
117
|
+
usage?: {
|
|
118
|
+
inputTokens: number;
|
|
119
|
+
outputTokens: number;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** From `message_stop` or `message_complete`. */
|
|
123
|
+
interface MessageEndEvent extends BaseEvent {
|
|
124
|
+
type: "message-end";
|
|
125
|
+
messageId: string;
|
|
126
|
+
usage?: {
|
|
127
|
+
inputTokens: number;
|
|
128
|
+
outputTokens: number;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
interface TextStartEvent extends BaseEvent {
|
|
132
|
+
type: "text-start";
|
|
133
|
+
id: string;
|
|
134
|
+
messageId: string;
|
|
135
|
+
}
|
|
136
|
+
interface TextDeltaEvent extends BaseEvent {
|
|
137
|
+
type: "text-delta";
|
|
138
|
+
id: string;
|
|
139
|
+
delta: string;
|
|
140
|
+
messageId: string;
|
|
141
|
+
}
|
|
142
|
+
interface TextEndEvent extends BaseEvent {
|
|
143
|
+
type: "text-end";
|
|
144
|
+
id: string;
|
|
145
|
+
messageId: string;
|
|
146
|
+
}
|
|
147
|
+
interface ReasoningStartEvent extends BaseEvent {
|
|
148
|
+
type: "reasoning-start";
|
|
149
|
+
id: string;
|
|
150
|
+
messageId: string;
|
|
151
|
+
}
|
|
152
|
+
interface ReasoningDeltaEvent extends BaseEvent {
|
|
153
|
+
type: "reasoning-delta";
|
|
154
|
+
id: string;
|
|
155
|
+
delta: string;
|
|
156
|
+
messageId: string;
|
|
157
|
+
}
|
|
158
|
+
interface ReasoningEndEvent extends BaseEvent {
|
|
159
|
+
type: "reasoning-end";
|
|
160
|
+
id: string;
|
|
161
|
+
messageId: string;
|
|
162
|
+
}
|
|
163
|
+
interface PlanStartEvent extends BaseEvent {
|
|
164
|
+
type: "plan-start";
|
|
165
|
+
id: string;
|
|
166
|
+
messageId: string;
|
|
167
|
+
}
|
|
168
|
+
interface PlanDeltaEvent extends BaseEvent {
|
|
169
|
+
type: "plan-delta";
|
|
170
|
+
id: string;
|
|
171
|
+
delta: string;
|
|
172
|
+
messageId: string;
|
|
173
|
+
}
|
|
174
|
+
interface PlanEndEvent extends BaseEvent {
|
|
175
|
+
type: "plan-end";
|
|
176
|
+
id: string;
|
|
177
|
+
messageId: string;
|
|
178
|
+
}
|
|
179
|
+
interface ToolInputStartEvent extends BaseEvent {
|
|
180
|
+
type: "tool-input-start";
|
|
181
|
+
toolCallId: string;
|
|
182
|
+
toolName: string;
|
|
183
|
+
messageId: string;
|
|
184
|
+
state: "input-streaming";
|
|
185
|
+
}
|
|
186
|
+
interface ToolInputDeltaEvent extends BaseEvent {
|
|
187
|
+
type: "tool-input-delta";
|
|
188
|
+
toolCallId: string;
|
|
189
|
+
inputTextDelta: string;
|
|
190
|
+
state: "input-streaming";
|
|
191
|
+
}
|
|
192
|
+
interface ToolInputAvailableEvent extends BaseEvent {
|
|
193
|
+
type: "tool-input-available";
|
|
194
|
+
toolCallId: string;
|
|
195
|
+
toolName: string;
|
|
196
|
+
input: unknown;
|
|
197
|
+
state: "input-available";
|
|
198
|
+
}
|
|
199
|
+
interface ToolOutputAvailableEvent extends BaseEvent {
|
|
200
|
+
type: "tool-output-available";
|
|
201
|
+
toolCallId: string;
|
|
202
|
+
output: unknown;
|
|
203
|
+
isError: boolean;
|
|
204
|
+
state: "output-available" | "output-error";
|
|
205
|
+
}
|
|
206
|
+
/** Tool approval gate. From `awaiting_approval` (toolName !== "AskUserQuestion") or `permission_request`. */
|
|
207
|
+
interface ApprovalRequestEvent extends BaseEvent {
|
|
208
|
+
type: "approval-request";
|
|
209
|
+
requestId: string;
|
|
210
|
+
toolCallId?: string;
|
|
211
|
+
toolName: string;
|
|
212
|
+
toolInput: unknown;
|
|
213
|
+
timeoutSeconds: number;
|
|
214
|
+
}
|
|
215
|
+
/** Derived on resume / from the answer round-trip; surfaced for state reconciliation. */
|
|
216
|
+
interface ApprovalResolvedEvent extends BaseEvent {
|
|
217
|
+
type: "approval-resolved";
|
|
218
|
+
requestId: string;
|
|
219
|
+
resolved: "approved" | "denied";
|
|
220
|
+
}
|
|
221
|
+
/** AskUserQuestion gate. From `awaiting_approval` where toolName === "AskUserQuestion". */
|
|
222
|
+
interface QuestionOption {
|
|
223
|
+
label: string;
|
|
224
|
+
description?: string;
|
|
225
|
+
}
|
|
226
|
+
interface QuestionItem {
|
|
227
|
+
question: string;
|
|
228
|
+
/** Real wire options are `{label, description}` objects; the normalizer also
|
|
229
|
+
* coerces bare-string options into `{label}` defensively. */
|
|
230
|
+
options?: QuestionOption[];
|
|
231
|
+
[k: string]: unknown;
|
|
232
|
+
}
|
|
233
|
+
interface QuestionEvent extends BaseEvent {
|
|
234
|
+
type: "question";
|
|
235
|
+
requestId: string;
|
|
236
|
+
toolCallId: string;
|
|
237
|
+
questions: QuestionItem[];
|
|
238
|
+
timeoutSeconds: number;
|
|
239
|
+
}
|
|
240
|
+
/** From `sandbox-connecting` / `sandbox-connected`. (Fatal sandbox failures => RunErrorEvent with `code`.) */
|
|
241
|
+
interface SandboxStatusEvent extends BaseEvent {
|
|
242
|
+
type: "sandbox-status";
|
|
243
|
+
phase: "connecting" | "connected";
|
|
244
|
+
message: string;
|
|
245
|
+
runId: number | null;
|
|
246
|
+
sandboxId?: string | null;
|
|
247
|
+
durationMs?: number;
|
|
248
|
+
}
|
|
249
|
+
/** Non-fatal diagnostics: stderr, mcp_error/mcp_auth_failed (warning), rate_limit, system_message. */
|
|
250
|
+
interface NoticeEvent extends BaseEvent {
|
|
251
|
+
type: "notice";
|
|
252
|
+
level: "info" | "warning";
|
|
253
|
+
source: "stderr" | "mcp" | "rate_limit" | "system";
|
|
254
|
+
message: string;
|
|
255
|
+
detail?: string;
|
|
256
|
+
}
|
|
257
|
+
/** From `compact_boundary`: the SDK summarized the conversation history. */
|
|
258
|
+
interface CompactBoundaryEvent extends BaseEvent {
|
|
259
|
+
type: "compact-boundary";
|
|
260
|
+
trigger: "manual" | "auto";
|
|
261
|
+
preTokens: number;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Any wire type with no normalized mapping (`sdk_content_snapshot`, `user_message`,
|
|
265
|
+
* `compact_boundary`, and future/unknown types). `raw` is preserved so consumers
|
|
266
|
+
* and a newer normalizer can recover the original. Forward-compat: a new backend
|
|
267
|
+
* event surfaces here instead of crashing.
|
|
268
|
+
*/
|
|
269
|
+
interface UnknownEvent extends BaseEvent {
|
|
270
|
+
type: "unknown";
|
|
271
|
+
wireType: string;
|
|
272
|
+
}
|
|
273
|
+
type M8tesStreamEvent = RunStartEvent | RunStatusEvent | RunMetricsEvent | RunFinishEvent | RunErrorEvent | RunCancelledEvent | MessageStartEvent | MessageEndEvent | TextStartEvent | TextDeltaEvent | TextEndEvent | ReasoningStartEvent | ReasoningDeltaEvent | ReasoningEndEvent | PlanStartEvent | PlanDeltaEvent | PlanEndEvent | ToolInputStartEvent | ToolInputDeltaEvent | ToolInputAvailableEvent | ToolOutputAvailableEvent | ApprovalRequestEvent | ApprovalResolvedEvent | QuestionEvent | SandboxStatusEvent | NoticeEvent | CompactBoundaryEvent | UnknownEvent;
|
|
274
|
+
type M8tesStreamEventType = M8tesStreamEvent["type"];
|
|
275
|
+
/** Terminal events end a stream. */
|
|
276
|
+
declare const TERMINAL_EVENT_TYPES: readonly ["run-finish", "run-error", "run-cancelled"];
|
|
277
|
+
type TerminalEventType = (typeof TERMINAL_EVENT_TYPES)[number];
|
|
278
|
+
/** True for the three terminal event types. */
|
|
279
|
+
declare function isTerminalEvent(event: M8tesStreamEvent): event is RunFinishEvent | RunErrorEvent | RunCancelledEvent;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* SSE wire decoder: raw bytes/text -> wire frame objects.
|
|
283
|
+
*
|
|
284
|
+
* Layer 1 of the pipeline (Layer 2 is `normalizer.ts`). This module ONLY knows
|
|
285
|
+
* the SSE envelope + JSON, never the m8tes event semantics.
|
|
286
|
+
*
|
|
287
|
+
* chunk(s) ─push─► [ frame split on blank line ] ─► [ data: lines ]
|
|
288
|
+
* ─► [ concatenated-JSON split ] ─► JSON.parse ─► wire object
|
|
289
|
+
*
|
|
290
|
+
* Wire facts it handles (from the streaming contract):
|
|
291
|
+
* - Frames are separated by a blank line; `\r\n` is normalized to `\n`.
|
|
292
|
+
* - A line starting with `:` is an SSE comment (keepalive) — ignored.
|
|
293
|
+
* - A frame may carry one or more `data:` lines (joined with `\n` per spec).
|
|
294
|
+
* - A single `data:` payload may glue MULTIPLE complete JSON objects together
|
|
295
|
+
* (observed in the app stream); they are split with a string-aware brace
|
|
296
|
+
* scanner — NOT a naive `}{` regex, which breaks on braces inside strings.
|
|
297
|
+
* - Invalid JSON in a completed frame is DROPPED (reported via `onMalformed`),
|
|
298
|
+
* never thrown — a corrupt frame must not kill the stream (contract §7).
|
|
299
|
+
*/
|
|
300
|
+
interface SseParserOptions {
|
|
301
|
+
/**
|
|
302
|
+
* Called for each `data:` payload (or split piece) that fails to parse as JSON.
|
|
303
|
+
* Default: no-op. Wire this to telemetry; do NOT throw from it.
|
|
304
|
+
*/
|
|
305
|
+
onMalformed?: (rawPayload: string, error: unknown) => void;
|
|
306
|
+
}
|
|
307
|
+
type WireFrame$1 = Record<string, unknown>;
|
|
308
|
+
/** Split a payload into top-level JSON object/array substrings, string-aware. */
|
|
309
|
+
declare function splitConcatenatedJson(payload: string): string[];
|
|
310
|
+
/**
|
|
311
|
+
* Stateful streaming decoder. Feed it response-body chunks (any size, split
|
|
312
|
+
* mid-frame); it returns the wire frames completed by each chunk. Call `flush()`
|
|
313
|
+
* once the stream ends to drain a trailing frame that had no closing blank line.
|
|
314
|
+
*/
|
|
315
|
+
declare function createSseDecoder(opts?: SseParserOptions): {
|
|
316
|
+
/** Feed a chunk; returns the wire frames it completed. */
|
|
317
|
+
push(chunk: string): WireFrame$1[];
|
|
318
|
+
/** Flush a trailing unterminated frame at end-of-stream. */
|
|
319
|
+
flush(): WireFrame$1[];
|
|
320
|
+
};
|
|
321
|
+
/** Convenience: parse a whole SSE string into wire frames (used by fixtures/tests). */
|
|
322
|
+
declare function parseSse(text: string, opts?: SseParserOptions): WireFrame$1[];
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Normalizer: wire frames -> public M8tesStreamEvent[].
|
|
326
|
+
*
|
|
327
|
+
* Layer 2 of the pipeline (Layer 1 = `sse-parser.ts`). Each wire frame maps to
|
|
328
|
+
* ZERO or MORE public events. The normalizer owns:
|
|
329
|
+
* - `seq`: a monotonic counter stamped on every emitted event (no wire seq id).
|
|
330
|
+
* - dedupe: a domain-id `open`/`seen` ledger so a `/stream` rejoin that
|
|
331
|
+
* re-streams blocks + re-sends a message_snapshot + re-sends a pending
|
|
332
|
+
* permission_request renders EXACTLY ONCE (streaming contract §dedupeStrategy).
|
|
333
|
+
*
|
|
334
|
+
* One normalizer instance is kept ALIVE across a reconnect (the hook reuses it),
|
|
335
|
+
* so `seen` persists and replayed content is dropped. Idempotent-apply rules:
|
|
336
|
+
* - START events: skip if the key is already open or seen.
|
|
337
|
+
* - DELTA events: drop if the key is in `seen` (finalized) — a replayed delta
|
|
338
|
+
* for a closed block. A bare key not yet seen is lazily opened (tolerates a
|
|
339
|
+
* trimmed replay prefix).
|
|
340
|
+
* - END / result / terminal / approval events: finalize, move key to `seen`;
|
|
341
|
+
* a second occurrence is a no-op.
|
|
342
|
+
*
|
|
343
|
+
* Unknown/unmapped wire types pass through as `UnknownEvent` (never throw).
|
|
344
|
+
*/
|
|
345
|
+
|
|
346
|
+
type WireFrame = Record<string, unknown>;
|
|
347
|
+
interface Normalizer {
|
|
348
|
+
/** Map one wire frame to zero+ public events (seq-stamped, in order). */
|
|
349
|
+
push(frame: WireFrame): M8tesStreamEvent[];
|
|
350
|
+
/** Discard all dedupe/lifecycle state (e.g. starting a brand-new run). */
|
|
351
|
+
reset(): void;
|
|
352
|
+
}
|
|
353
|
+
declare function createNormalizer(): Normalizer;
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Accumulator: the normalized event stream -> renderable conversation state.
|
|
357
|
+
*
|
|
358
|
+
* Pure + immutable (new state object on every change) so React can re-render via
|
|
359
|
+
* useState/useSyncExternalStore. Framework-agnostic — `useMate` wraps it. It
|
|
360
|
+
* assumes the normalizer has already deduped (so each event is "new"); it only
|
|
361
|
+
* folds events into messages/parts + run status + pending HITL.
|
|
362
|
+
*
|
|
363
|
+
* Lazy creation: a `text-delta`/`tool-*` with no preceding start (a trimmed
|
|
364
|
+
* replay prefix) lazily creates its message/part, so a partial rejoin still
|
|
365
|
+
* renders rather than dropping content.
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
type TextLikeKind = "text" | "reasoning" | "plan";
|
|
369
|
+
interface TextPart {
|
|
370
|
+
kind: TextLikeKind;
|
|
371
|
+
id: string;
|
|
372
|
+
text: string;
|
|
373
|
+
}
|
|
374
|
+
interface ToolPart {
|
|
375
|
+
kind: "tool";
|
|
376
|
+
toolCallId: string;
|
|
377
|
+
toolName: string;
|
|
378
|
+
state: ToolState;
|
|
379
|
+
inputText: string;
|
|
380
|
+
input?: unknown;
|
|
381
|
+
output?: unknown;
|
|
382
|
+
isError?: boolean;
|
|
383
|
+
}
|
|
384
|
+
/** Non-fatal diagnostics rendered as a quiet inline row (mcp auth failures,
|
|
385
|
+
* rate limits) — the platform surfaces these; silence hides real failures. */
|
|
386
|
+
interface NoticePart {
|
|
387
|
+
kind: "notice";
|
|
388
|
+
id: string;
|
|
389
|
+
level: "info" | "warning";
|
|
390
|
+
source: string;
|
|
391
|
+
message: string;
|
|
392
|
+
}
|
|
393
|
+
/** Sandbox lifecycle pill ("connecting to environment" -> "environment ready"). */
|
|
394
|
+
interface SandboxPart {
|
|
395
|
+
kind: "sandbox";
|
|
396
|
+
id: string;
|
|
397
|
+
phase: "connecting" | "connected";
|
|
398
|
+
durationMs?: number;
|
|
399
|
+
}
|
|
400
|
+
/** Conversation-summarized divider (SDK auto/manual compaction). */
|
|
401
|
+
interface CompactPart {
|
|
402
|
+
kind: "compact";
|
|
403
|
+
id: string;
|
|
404
|
+
trigger: "manual" | "auto";
|
|
405
|
+
preTokens: number;
|
|
406
|
+
}
|
|
407
|
+
type MatePart = TextPart | ToolPart | NoticePart | SandboxPart | CompactPart;
|
|
408
|
+
interface MateMessage {
|
|
409
|
+
id: string;
|
|
410
|
+
role: "assistant" | "user";
|
|
411
|
+
parts: MatePart[];
|
|
412
|
+
}
|
|
413
|
+
type MateStatus = "idle" | RunStatus;
|
|
414
|
+
interface ConversationState {
|
|
415
|
+
runId: number | null;
|
|
416
|
+
runUuid: string | null;
|
|
417
|
+
agentName: string | null;
|
|
418
|
+
status: MateStatus;
|
|
419
|
+
messages: MateMessage[];
|
|
420
|
+
pendingApproval: ApprovalRequestEvent | null;
|
|
421
|
+
pendingQuestion: QuestionEvent | null;
|
|
422
|
+
/** From run-metrics: "refusal" | "max_tokens" render a quiet outcome row
|
|
423
|
+
* ("Request declined" / "Response truncated"); anything else is null. */
|
|
424
|
+
finishStopReason: "refusal" | "max_tokens" | null;
|
|
425
|
+
error: {
|
|
426
|
+
message: string;
|
|
427
|
+
code: string | null;
|
|
428
|
+
/** HTTP status for API errors (402 limit / 429 rate) */
|
|
429
|
+
apiStatus?: number;
|
|
430
|
+
/** v2 `error.details` (machine `error_code`, `retry_after`, `period_end`, ...) */
|
|
431
|
+
apiDetails?: Record<string, unknown>;
|
|
432
|
+
} | null;
|
|
433
|
+
}
|
|
434
|
+
declare const initialConversationState: ConversationState;
|
|
435
|
+
declare function accumulate(state: ConversationState, e: M8tesStreamEvent): ConversationState;
|
|
436
|
+
/** Stateful convenience wrapper for the hook (holds the running snapshot). */
|
|
437
|
+
interface Accumulator {
|
|
438
|
+
readonly state: ConversationState;
|
|
439
|
+
apply(event: M8tesStreamEvent): ConversationState;
|
|
440
|
+
/** Optimistically append the user's own message (shown immediately on send). */
|
|
441
|
+
addUserMessage(text: string): ConversationState;
|
|
442
|
+
reset(): void;
|
|
443
|
+
}
|
|
444
|
+
declare function createAccumulator(): Accumulator;
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Typed error hierarchy for the m8tes V2 API — ONE implementation shared by
|
|
448
|
+
* `@m8tes/sdk` (API-key client) and `@m8tes/react` (browser proxy client).
|
|
449
|
+
*
|
|
450
|
+
* Mirrors the Python SDK's `sdk/py/m8tes/_exceptions.py` so the two languages
|
|
451
|
+
* behave identically: same status→class map, same `docUrl`/`errorCode`/
|
|
452
|
+
* `retryAfter`/`details` fields, just camelCase.
|
|
453
|
+
*
|
|
454
|
+
* The wire envelope is standardized across every /api/v2 route
|
|
455
|
+
* (`fastapi/app/routers/v2/_errors.py`):
|
|
456
|
+
*
|
|
457
|
+
* { "error": { "message", "type", "code", "request_id", "doc_url",
|
|
458
|
+
* "details": { "error_code": "...", ...context } } }
|
|
459
|
+
*
|
|
460
|
+
* Note `error.code` is the int HTTP status by design; the machine-readable app
|
|
461
|
+
* code (e.g. "RUN_LIMIT_REACHED", "END_USER_RATE_LIMITED") lives at
|
|
462
|
+
* `error.details.error_code` and is surfaced here as `errorCode`.
|
|
463
|
+
*
|
|
464
|
+
* No auth and no `node:` imports live in this file — it ships in browser
|
|
465
|
+
* bundles via `@m8tes/sdk/protocol`.
|
|
466
|
+
*/
|
|
467
|
+
interface ApiErrorFields {
|
|
468
|
+
/** Envelope `error.type`, e.g. "authentication_error". */
|
|
469
|
+
type: string;
|
|
470
|
+
/** Envelope `error.code` — the int HTTP status (kept for wire fidelity). */
|
|
471
|
+
code: number;
|
|
472
|
+
/** HTTP status of the response. */
|
|
473
|
+
status: number;
|
|
474
|
+
/** `error.request_id`, else the `x-request-id` response header. Quote this in bug reports. */
|
|
475
|
+
requestId?: string;
|
|
476
|
+
/** Full `error.details` object: actionable context (runs_used, period_end, ...). */
|
|
477
|
+
details?: unknown;
|
|
478
|
+
/** `error.doc_url` — docs deep link for this error type. */
|
|
479
|
+
docUrl?: string;
|
|
480
|
+
/** `error.details.error_code` — machine-readable app code for branching. */
|
|
481
|
+
errorCode?: string;
|
|
482
|
+
/** Seconds from the `Retry-After` header. Set on 429; undefined when absent or an HTTP-date. */
|
|
483
|
+
retryAfter?: number;
|
|
484
|
+
}
|
|
485
|
+
/** Base class for every API error. Catch this to catch them all. */
|
|
486
|
+
declare class M8tesApiError extends Error {
|
|
487
|
+
readonly type: string;
|
|
488
|
+
readonly code: number;
|
|
489
|
+
readonly status: number;
|
|
490
|
+
readonly requestId?: string;
|
|
491
|
+
readonly details?: unknown;
|
|
492
|
+
readonly docUrl?: string;
|
|
493
|
+
readonly errorCode?: string;
|
|
494
|
+
readonly retryAfter?: number;
|
|
495
|
+
constructor(message: string, f: ApiErrorFields);
|
|
496
|
+
}
|
|
497
|
+
/** 400 or 422 — invalid request. Check `.status` to tell them apart. */
|
|
498
|
+
declare class ValidationError extends M8tesApiError {
|
|
499
|
+
constructor(message: string, f: ApiErrorFields);
|
|
500
|
+
}
|
|
501
|
+
/** 401 — missing, malformed, or revoked API key. */
|
|
502
|
+
declare class AuthenticationError extends M8tesApiError {
|
|
503
|
+
constructor(message: string, f: ApiErrorFields);
|
|
504
|
+
}
|
|
505
|
+
/** 402 — billing limit reached or subscription issue. `.details` carries the caps. */
|
|
506
|
+
declare class BillingError extends M8tesApiError {
|
|
507
|
+
constructor(message: string, f: ApiErrorFields);
|
|
508
|
+
}
|
|
509
|
+
/** 403 — authenticated but not allowed. */
|
|
510
|
+
declare class PermissionDeniedError extends M8tesApiError {
|
|
511
|
+
constructor(message: string, f: ApiErrorFields);
|
|
512
|
+
}
|
|
513
|
+
/** 404 — no such resource, or it belongs to another account/end-user. */
|
|
514
|
+
declare class NotFoundError extends M8tesApiError {
|
|
515
|
+
constructor(message: string, f: ApiErrorFields);
|
|
516
|
+
}
|
|
517
|
+
/** 409 — conflicting state. */
|
|
518
|
+
declare class ConflictError extends M8tesApiError {
|
|
519
|
+
constructor(message: string, f: ApiErrorFields);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* 409 from `GET /runs/{id}/stream` — the run is no longer executing, so there is
|
|
523
|
+
* nothing to join. The caller should fall back to `runs.get(id)` for the final
|
|
524
|
+
* result rather than treating this as a failure.
|
|
525
|
+
*/
|
|
526
|
+
declare class RunNotStreamingError extends ConflictError {
|
|
527
|
+
constructor(message: string, f: ApiErrorFields);
|
|
528
|
+
}
|
|
529
|
+
/** 429 — too many requests. Honour `.retryAfter` before retrying. */
|
|
530
|
+
declare class RateLimitError extends M8tesApiError {
|
|
531
|
+
constructor(message: string, f: ApiErrorFields);
|
|
532
|
+
}
|
|
533
|
+
/** 500+ — server-side error. Safe to retry idempotent requests. */
|
|
534
|
+
declare class APIError extends M8tesApiError {
|
|
535
|
+
constructor(message: string, f: ApiErrorFields);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* A stream completed but emitted error events (expired credential, provider rate
|
|
539
|
+
* limit, quota exhaustion). Raised so a failed run is never mistaken for a
|
|
540
|
+
* successful empty one. `.details.errors` holds the raw messages.
|
|
541
|
+
*/
|
|
542
|
+
declare class RunFailedError extends M8tesApiError {
|
|
543
|
+
constructor(message: string, f: ApiErrorFields);
|
|
544
|
+
}
|
|
545
|
+
/** `Retry-After` in seconds. An HTTP-date (or garbage) yields undefined, matching Python. */
|
|
546
|
+
declare function parseRetryAfter(raw: string | null | undefined): number | undefined;
|
|
547
|
+
/**
|
|
548
|
+
* Pull the standardized envelope out of a parsed error body.
|
|
549
|
+
*
|
|
550
|
+
* Tolerant by design: a proxy, a CDN, or a non-v2 route can return HTML, an
|
|
551
|
+
* empty body, or a bare `{detail: ...}`, and callers still get a usable error
|
|
552
|
+
* with the right status rather than a SyntaxError.
|
|
553
|
+
*/
|
|
554
|
+
declare function parseErrorEnvelope(status: number, body: unknown, headers?: {
|
|
555
|
+
get(name: string): string | null;
|
|
556
|
+
}): {
|
|
557
|
+
message: string;
|
|
558
|
+
fields: ApiErrorFields;
|
|
559
|
+
};
|
|
560
|
+
interface ErrorFromResponseOptions {
|
|
561
|
+
/**
|
|
562
|
+
* Map 409 to `RunNotStreamingError` (the "run already finished, fall back to
|
|
563
|
+
* runs.get()" signal) instead of the generic `ConflictError`. Set on stream
|
|
564
|
+
* requests, where 409 has exactly that meaning.
|
|
565
|
+
*/
|
|
566
|
+
conflictIsNotStreaming?: boolean;
|
|
567
|
+
}
|
|
568
|
+
/** The error class a status maps to. Exported so clients that build their own
|
|
569
|
+
* message (e.g. the SDK's base-URL diagnostics) still get the right type. */
|
|
570
|
+
declare function errorClassForStatus(status: number, opts?: ErrorFromResponseOptions): new (message: string, f: ApiErrorFields) => M8tesApiError;
|
|
571
|
+
/**
|
|
572
|
+
* Build the right typed error from a non-OK `Response`. Consumes the body.
|
|
573
|
+
*/
|
|
574
|
+
declare function errorFromResponse(res: Response, opts?: ErrorFromResponseOptions): Promise<M8tesApiError>;
|
|
575
|
+
|
|
576
|
+
export { APIError, type Accumulator, type ApiErrorFields, type ApprovalRequestEvent, type ApprovalResolvedEvent, AuthenticationError, type BaseEvent, BillingError, type CompactBoundaryEvent, type CompactPart, ConflictError, type ConversationState, type ErrorFromResponseOptions, M8tesApiError, type M8tesStreamEvent, type M8tesStreamEventType, type MateMessage, type MatePart, type MateStatus, type MessageEndEvent, type MessageStartEvent, type Normalizer, NotFoundError, type NoticeEvent, type NoticePart, PROTOCOL_VERSION, PermissionDeniedError, type PlanDeltaEvent, type PlanEndEvent, type PlanStartEvent, type ProtocolVersion, type QuestionEvent, type QuestionItem, type QuestionOption, RateLimitError, type ReasoningDeltaEvent, type ReasoningEndEvent, type ReasoningStartEvent, type RunCancelledEvent, type RunErrorCode, type RunErrorEvent, RunFailedError, type RunFinishEvent, type RunMetricsEvent, RunNotStreamingError, type RunStartEvent, type RunStatus, type RunStatusEvent, type SandboxPart, type SandboxStatusEvent, type SseParserOptions, TERMINAL_EVENT_TYPES, type TerminalEventType, type TextDeltaEvent, type TextEndEvent, type TextLikeKind, type TextPart, type TextStartEvent, type ToolInputAvailableEvent, type ToolInputDeltaEvent, type ToolInputStartEvent, type ToolOutputAvailableEvent, type ToolPart, type ToolState, type UnknownEvent, ValidationError, accumulate, createAccumulator, createNormalizer, createSseDecoder, errorClassForStatus, errorFromResponse, initialConversationState, isTerminalEvent, parseErrorEnvelope, parseRetryAfter, parseSse, splitConcatenatedJson };
|