@rkat/sdk 0.5.2 → 0.6.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/dist/events.js CHANGED
@@ -3,8 +3,10 @@
3
3
  *
4
4
  * Events form a discriminated union on the `type` field (snake_case to match
5
5
  * the wire protocol). All other fields use idiomatic camelCase.
6
- * Missing fields are defaulted during parsing so partial streaming payloads
7
- * can still be represented as typed events.
6
+ * Missing semantic fields remain absent during parsing so partial streaming
7
+ * payloads do not become authoritative SDK state.
8
+ * Malformed known event payloads are preserved as `malformed_event` frames
9
+ * instead of being coerced into typed semantic events with fabricated defaults.
8
10
  *
9
11
  * @example
10
12
  * ```ts
@@ -41,26 +43,377 @@ export function isToolCallRequested(event) {
41
43
  export function isRunCompleted(event) {
42
44
  return event.type === "run_completed";
43
45
  }
46
+ export function isExtractionSucceeded(event) {
47
+ return event.type === "extraction_succeeded";
48
+ }
49
+ export function isExtractionFailed(event) {
50
+ return event.type === "extraction_failed";
51
+ }
44
52
  export function isRunFailed(event) {
45
53
  return event.type === "run_failed";
46
54
  }
47
55
  // ---------------------------------------------------------------------------
48
56
  // Wire → typed parser
49
57
  // ---------------------------------------------------------------------------
58
+ function isPlainRecord(raw) {
59
+ return typeof raw === "object" && raw !== null && !Array.isArray(raw);
60
+ }
61
+ function malformedEvent(raw, rawType, error) {
62
+ return { type: "malformed_event", rawType, raw, error };
63
+ }
64
+ function requireStringField(raw, field) {
65
+ const value = raw[field];
66
+ if (typeof value !== "string") {
67
+ throw new Error(`${field} must be string`);
68
+ }
69
+ return value;
70
+ }
71
+ function requireNumberField(raw, field) {
72
+ const value = raw[field];
73
+ if (typeof value !== "number" || !Number.isFinite(value)) {
74
+ throw new Error(`${field} must be number`);
75
+ }
76
+ return value;
77
+ }
78
+ function requireBooleanField(raw, field) {
79
+ const value = raw[field];
80
+ if (typeof value !== "boolean") {
81
+ throw new Error(`${field} must be boolean`);
82
+ }
83
+ return value;
84
+ }
85
+ function requireRecordField(raw, field) {
86
+ const value = raw[field];
87
+ if (!isPlainRecord(value)) {
88
+ throw new Error(`${field} must be object`);
89
+ }
90
+ return value;
91
+ }
92
+ function requireOneOf(value, field, allowed) {
93
+ if (!allowed.includes(value)) {
94
+ throw new Error(`${field} must be one of ${allowed.join(", ")}`);
95
+ }
96
+ return value;
97
+ }
50
98
  function parseUsage(raw) {
51
- if (!raw)
52
- return { inputTokens: 0, outputTokens: 0 };
99
+ if (!isPlainRecord(raw)) {
100
+ throw new Error("missing usage");
101
+ }
53
102
  return {
54
- inputTokens: Number(raw.input_tokens ?? 0),
55
- outputTokens: Number(raw.output_tokens ?? 0),
103
+ inputTokens: requireNumberField(raw, "input_tokens"),
104
+ outputTokens: requireNumberField(raw, "output_tokens"),
56
105
  cacheCreationTokens: raw.cache_creation_tokens != null
57
- ? Number(raw.cache_creation_tokens)
106
+ ? requireNumberField(raw, "cache_creation_tokens")
58
107
  : undefined,
59
108
  cacheReadTokens: raw.cache_read_tokens != null
60
- ? Number(raw.cache_read_tokens)
109
+ ? requireNumberField(raw, "cache_read_tokens")
61
110
  : undefined,
62
111
  };
63
112
  }
113
+ function hasOwn(raw, field) {
114
+ return Object.prototype.hasOwnProperty.call(raw, field);
115
+ }
116
+ function parseTerminalCauseKind(raw) {
117
+ const causeKind = parseWireString(raw.terminal_cause_kind ?? raw.terminalCauseKind);
118
+ if (causeKind !== undefined &&
119
+ TURN_TERMINAL_CAUSE_KINDS.has(causeKind)) {
120
+ return causeKind;
121
+ }
122
+ return undefined;
123
+ }
124
+ function terminalCauseKindField(raw) {
125
+ const terminalCauseKind = parseTerminalCauseKind(raw);
126
+ return terminalCauseKind === undefined ? {} : { terminalCauseKind };
127
+ }
128
+ function parseAgentErrorReason(raw) {
129
+ if (raw === undefined) {
130
+ return undefined;
131
+ }
132
+ if (raw === null) {
133
+ return null;
134
+ }
135
+ if (!isPlainRecord(raw)) {
136
+ throw new Error("error_report.reason must be object");
137
+ }
138
+ const reasonType = parseWireString(raw.reason_type ?? raw.reasonType);
139
+ if (reasonType === undefined) {
140
+ throw new Error("error_report.reason.reason_type must be string");
141
+ }
142
+ if (reasonType === "turn_terminal_cause") {
143
+ const outcome = parseWireString(raw.outcome);
144
+ const causeKind = parseWireString(raw.cause_kind ?? raw.causeKind);
145
+ if (outcome !== undefined &&
146
+ causeKind !== undefined &&
147
+ TURN_TERMINAL_OUTCOMES.has(outcome) &&
148
+ TURN_TERMINAL_CAUSE_KINDS.has(causeKind)) {
149
+ return {
150
+ reasonType,
151
+ outcome: outcome,
152
+ causeKind: causeKind,
153
+ };
154
+ }
155
+ return undefined;
156
+ }
157
+ if (!["hook_denied", "hook_timeout", "hook_execution_failed"].includes(reasonType)) {
158
+ return { reasonType: "unknown", rawReasonType: reasonType, raw };
159
+ }
160
+ const reason = {};
161
+ for (const [key, value] of Object.entries(raw)) {
162
+ if (key !== "hook_id" && key !== "hook_id_string") {
163
+ reason[key] = value;
164
+ }
165
+ }
166
+ reason.reason_type = reasonType;
167
+ if (reasonType === "hook_denied") {
168
+ reason.point = requireStringField(raw, "point");
169
+ reason.reason_code = requireStringField(raw, "reason_code");
170
+ }
171
+ else {
172
+ reason.hook_id = requireStringField(raw, "hook_id");
173
+ }
174
+ if (reasonType === "hook_timeout") {
175
+ reason.timeout_ms = requireNumberField(raw, "timeout_ms");
176
+ }
177
+ if (reasonType === "hook_execution_failed") {
178
+ reason.reason = requireStringField(raw, "reason");
179
+ }
180
+ if (reasonType === "hook_denied" && hasOwn(raw, "hook_id")) {
181
+ const hookId = raw.hook_id;
182
+ if (hookId !== null && typeof hookId !== "string") {
183
+ throw new Error("error_report.reason.hook_id must be string or null");
184
+ }
185
+ reason.hook_id = hookId;
186
+ }
187
+ return reason;
188
+ }
189
+ function parseAgentErrorReport(raw) {
190
+ if (raw === undefined) {
191
+ return undefined;
192
+ }
193
+ if (raw === null) {
194
+ return null;
195
+ }
196
+ if (!isPlainRecord(raw)) {
197
+ throw new Error("error_report must be object");
198
+ }
199
+ const reason = hasOwn(raw, "reason")
200
+ ? parseAgentErrorReason(raw.reason)
201
+ : undefined;
202
+ return {
203
+ class: requireStringField(raw, "class"),
204
+ message: requireStringField(raw, "message"),
205
+ ...(reason !== undefined ? { reason } : {}),
206
+ };
207
+ }
208
+ function parseContentInput(raw) {
209
+ if (Array.isArray(raw))
210
+ return raw;
211
+ return String(raw ?? "");
212
+ }
213
+ function parseContentBlocks(raw, legacyText) {
214
+ if (Array.isArray(raw))
215
+ return raw;
216
+ if (raw != null)
217
+ throw new Error("content must be a content block array");
218
+ if (typeof legacyText === "string" && legacyText.length > 0) {
219
+ return [{ type: "text", text: legacyText }];
220
+ }
221
+ return [];
222
+ }
223
+ function parseToolConfigChangeStatus(raw) {
224
+ if (raw == null || typeof raw !== "object") {
225
+ return undefined;
226
+ }
227
+ const value = raw;
228
+ const kind = String(value.kind ?? "");
229
+ switch (kind) {
230
+ case "boundary_applied":
231
+ return {
232
+ kind,
233
+ base_changed: typeof value.base_changed === "boolean" ? value.base_changed : false,
234
+ visible_changed: typeof value.visible_changed === "boolean" ? value.visible_changed : false,
235
+ revision: Number(value.revision ?? 0),
236
+ };
237
+ case "deferred_catalog_delta":
238
+ return {
239
+ kind,
240
+ added_hidden_count: Number(value.added_hidden_count ?? 0),
241
+ removed_hidden_count: Number(value.removed_hidden_count ?? 0),
242
+ pending_source_count: Number(value.pending_source_count ?? 0),
243
+ };
244
+ case "warning_failed_closed":
245
+ return {
246
+ kind,
247
+ error: String(value.error ?? ""),
248
+ };
249
+ case "external_tool_delta":
250
+ return {
251
+ kind,
252
+ phase: String(value.phase ?? "pending"),
253
+ ...(value.detail != null ? { detail: String(value.detail) } : {}),
254
+ };
255
+ default:
256
+ return undefined;
257
+ }
258
+ }
259
+ function parseSkillKey(raw) {
260
+ if (!isPlainRecord(raw)) {
261
+ return undefined;
262
+ }
263
+ const value = raw;
264
+ const sourceUuid = value.source_uuid ?? value.sourceUuid;
265
+ const skillName = value.skill_name ?? value.skillName;
266
+ if (typeof sourceUuid !== "string" || sourceUuid.length === 0) {
267
+ return undefined;
268
+ }
269
+ if (typeof skillName !== "string" || skillName.length === 0) {
270
+ return undefined;
271
+ }
272
+ return {
273
+ sourceUuid,
274
+ skillName,
275
+ };
276
+ }
277
+ function requireSkillKey(raw, field) {
278
+ const skillKey = parseSkillKey(raw);
279
+ if (!skillKey) {
280
+ throw new Error(`${field} must be SkillKey`);
281
+ }
282
+ return skillKey;
283
+ }
284
+ function requireSkillKeyArray(raw) {
285
+ if (!Array.isArray(raw)) {
286
+ throw new Error("skills must be SkillKey array");
287
+ }
288
+ return raw.map((skill, index) => requireSkillKey(skill, `skills[${index}]`));
289
+ }
290
+ const STOP_REASONS = new Set([
291
+ "end_turn",
292
+ "tool_use",
293
+ "max_tokens",
294
+ "stop_sequence",
295
+ "content_filter",
296
+ "cancelled",
297
+ ]);
298
+ function parseStopReason(raw) {
299
+ return typeof raw === "string" && STOP_REASONS.has(raw)
300
+ ? raw
301
+ : undefined;
302
+ }
303
+ const TOOL_CONFIG_CHANGE_OPERATIONS = new Set([
304
+ "add",
305
+ "remove",
306
+ "reload",
307
+ ]);
308
+ function parseToolConfigChangeOperation(raw) {
309
+ return typeof raw === "string" && TOOL_CONFIG_CHANGE_OPERATIONS.has(raw)
310
+ ? raw
311
+ : undefined;
312
+ }
313
+ function parseWireString(raw) {
314
+ return typeof raw === "string" ? raw : undefined;
315
+ }
316
+ function parseWireBoolean(raw) {
317
+ return typeof raw === "boolean" ? raw : undefined;
318
+ }
319
+ const TURN_TERMINAL_OUTCOMES = new Set([
320
+ "none",
321
+ "completed",
322
+ "failed",
323
+ "cancelled",
324
+ "budget_exhausted",
325
+ "time_budget_exceeded",
326
+ "structured_output_validation_failed",
327
+ ]);
328
+ const TURN_TERMINAL_CAUSE_KINDS = new Set([
329
+ "unknown",
330
+ "hook_denied",
331
+ "hook_failure",
332
+ "llm_failure",
333
+ "tool_failure",
334
+ "structured_output_validation_failed",
335
+ "budget_exhausted",
336
+ "time_budget_exceeded",
337
+ "retry_exhausted",
338
+ "turn_limit_reached",
339
+ "runtime_apply_failure",
340
+ "fatal_failure",
341
+ ]);
342
+ function parseSkillResolutionFailureReason(raw, fallbackMessage) {
343
+ if (raw == null || typeof raw !== "object") {
344
+ return undefined;
345
+ }
346
+ const value = raw;
347
+ const reasonTypeRaw = value.reason_type ?? value.reasonType;
348
+ if (typeof reasonTypeRaw !== "string") {
349
+ return undefined;
350
+ }
351
+ const reasonType = reasonTypeRaw;
352
+ switch (reasonType) {
353
+ case "not_found": {
354
+ const key = parseSkillKey(value.key);
355
+ return key ? { reasonType, key } : undefined;
356
+ }
357
+ case "capability_unavailable": {
358
+ const key = parseSkillKey(value.key);
359
+ if (!key || typeof value.capability !== "string" || value.capability.length === 0) {
360
+ return undefined;
361
+ }
362
+ return {
363
+ reasonType,
364
+ key,
365
+ capability: value.capability,
366
+ };
367
+ }
368
+ case "load":
369
+ case "parse":
370
+ return { reasonType, message: String(value.message ?? "") };
371
+ case "source_uuid_collision":
372
+ return {
373
+ reasonType,
374
+ sourceUuid: String(value.source_uuid ?? value.sourceUuid ?? ""),
375
+ existingFingerprint: String(value.existing_fingerprint ?? value.existingFingerprint ?? ""),
376
+ newFingerprint: String(value.new_fingerprint ?? value.newFingerprint ?? ""),
377
+ };
378
+ case "source_uuid_mutation_without_lineage":
379
+ return {
380
+ reasonType,
381
+ fingerprint: String(value.fingerprint ?? ""),
382
+ existingSourceUuid: String(value.existing_source_uuid ?? value.existingSourceUuid ?? ""),
383
+ mutatedSourceUuid: String(value.mutated_source_uuid ?? value.mutatedSourceUuid ?? ""),
384
+ };
385
+ case "missing_skill_remaps":
386
+ return {
387
+ reasonType,
388
+ eventId: String(value.event_id ?? value.eventId ?? ""),
389
+ eventKind: String(value.event_kind ?? value.eventKind ?? ""),
390
+ };
391
+ case "remap_without_lineage":
392
+ return {
393
+ reasonType,
394
+ fromSourceUuid: String(value.from_source_uuid ?? value.fromSourceUuid ?? ""),
395
+ fromSkillName: String(value.from_skill_name ?? value.fromSkillName ?? ""),
396
+ toSourceUuid: String(value.to_source_uuid ?? value.toSourceUuid ?? ""),
397
+ toSkillName: String(value.to_skill_name ?? value.toSkillName ?? ""),
398
+ };
399
+ case "unknown_skill_alias":
400
+ return { reasonType, alias: String(value.alias ?? "") };
401
+ case "remap_cycle":
402
+ return {
403
+ reasonType,
404
+ sourceUuid: String(value.source_uuid ?? value.sourceUuid ?? ""),
405
+ skillName: String(value.skill_name ?? value.skillName ?? ""),
406
+ };
407
+ case "unknown":
408
+ return { reasonType, message: String(value.message ?? fallbackMessage) };
409
+ default:
410
+ return {
411
+ reasonType: "unknown",
412
+ message: String(value.message ?? fallbackMessage),
413
+ rawReasonType: reasonType,
414
+ };
415
+ }
416
+ }
64
417
  /**
65
418
  * Parse a raw wire event dict into a typed {@link StreamEvent}.
66
419
  *
@@ -68,6 +421,28 @@ function parseUsage(raw) {
68
421
  * forward-compatibility.
69
422
  */
70
423
  export function parseEvent(raw) {
424
+ const normalizeScopePath = (value) => {
425
+ if (!Array.isArray(value)) {
426
+ return [];
427
+ }
428
+ return value.map((entry) => {
429
+ if (!entry || typeof entry !== "object") {
430
+ return { scope: "primary", session_id: "" };
431
+ }
432
+ const frame = entry;
433
+ if (frame.scope === "mob_member") {
434
+ return {
435
+ scope: "mob_member",
436
+ flow_run_id: String(frame.flow_run_id ?? ""),
437
+ agent_identity: String(frame.agent_identity ?? ""),
438
+ };
439
+ }
440
+ return {
441
+ scope: "primary",
442
+ session_id: String(frame.session_id ?? ""),
443
+ };
444
+ });
445
+ };
71
446
  if (raw.event &&
72
447
  (typeof raw.scope_id === "string" || Array.isArray(raw.scope_path))) {
73
448
  const innerRaw = typeof raw.event === "object" && raw.event !== null
@@ -76,7 +451,7 @@ export function parseEvent(raw) {
76
451
  return {
77
452
  type: "scoped_agent_event",
78
453
  scopeId: String(raw.scope_id ?? ""),
79
- scopePath: (raw.scope_path ?? []),
454
+ scopePath: normalizeScopePath(raw.scope_path),
80
455
  event: parseCoreEvent(innerRaw),
81
456
  };
82
457
  }
@@ -85,95 +460,194 @@ export function parseEvent(raw) {
85
460
  /** Parse a raw wire event as a core (non-wrapper) agent event. */
86
461
  export function parseCoreEvent(raw) {
87
462
  const type = String(raw.type ?? "");
88
- switch (type) {
89
- // Session lifecycle
90
- case "run_started":
91
- return { type, sessionId: String(raw.session_id ?? ""), prompt: String(raw.prompt ?? "") };
92
- case "run_completed":
93
- return { type, sessionId: String(raw.session_id ?? ""), result: String(raw.result ?? ""), usage: parseUsage(raw.usage) };
94
- case "run_failed":
95
- return { type, sessionId: String(raw.session_id ?? ""), error: String(raw.error ?? "") };
96
- // Turn / LLM
97
- case "turn_started":
98
- return { type, turnNumber: Number(raw.turn_number ?? 0) };
99
- case "text_delta":
100
- return { type, delta: String(raw.delta ?? "") };
101
- case "text_complete":
102
- return { type, content: String(raw.content ?? "") };
103
- case "tool_call_requested":
104
- return { type, id: String(raw.id ?? ""), name: String(raw.name ?? ""), args: raw.args };
105
- case "tool_result_received":
106
- return { type, id: String(raw.id ?? ""), name: String(raw.name ?? ""), isError: Boolean(raw.is_error) };
107
- case "turn_completed":
108
- return { type, stopReason: String(raw.stop_reason ?? "end_turn"), usage: parseUsage(raw.usage) };
109
- // Tool execution
110
- case "tool_execution_started":
111
- return { type, id: String(raw.id ?? ""), name: String(raw.name ?? "") };
112
- case "tool_execution_completed":
113
- return { type, id: String(raw.id ?? ""), name: String(raw.name ?? ""), result: String(raw.result ?? ""), isError: Boolean(raw.is_error), durationMs: Number(raw.duration_ms ?? 0) };
114
- case "tool_execution_timed_out":
115
- return { type, id: String(raw.id ?? ""), name: String(raw.name ?? ""), timeoutMs: Number(raw.timeout_ms ?? 0) };
116
- // Compaction
117
- case "compaction_started":
118
- return { type, inputTokens: Number(raw.input_tokens ?? 0), estimatedHistoryTokens: Number(raw.estimated_history_tokens ?? 0), messageCount: Number(raw.message_count ?? 0) };
119
- case "compaction_completed":
120
- return { type, summaryTokens: Number(raw.summary_tokens ?? 0), messagesBefore: Number(raw.messages_before ?? 0), messagesAfter: Number(raw.messages_after ?? 0) };
121
- case "compaction_failed":
122
- return { type, error: String(raw.error ?? "") };
123
- // Budget
124
- case "budget_warning":
125
- return { type, budgetType: String(raw.budget_type ?? ""), used: Number(raw.used ?? 0), limit: Number(raw.limit ?? 0), percent: Number(raw.percent ?? 0) };
126
- // Retry
127
- case "retrying":
128
- return { type, attempt: Number(raw.attempt ?? 0), maxAttempts: Number(raw.max_attempts ?? 0), error: String(raw.error ?? ""), delayMs: Number(raw.delay_ms ?? 0) };
129
- // Hooks
130
- case "hook_started":
131
- return { type, hookId: String(raw.hook_id ?? ""), point: String(raw.point ?? "") };
132
- case "hook_completed":
133
- return { type, hookId: String(raw.hook_id ?? ""), point: String(raw.point ?? ""), durationMs: Number(raw.duration_ms ?? 0) };
134
- case "hook_failed":
135
- return { type, hookId: String(raw.hook_id ?? ""), point: String(raw.point ?? ""), error: String(raw.error ?? "") };
136
- case "hook_denied":
137
- return { type, hookId: String(raw.hook_id ?? ""), point: String(raw.point ?? ""), reasonCode: String(raw.reason_code ?? ""), message: String(raw.message ?? ""), ...(raw.payload != null ? { payload: raw.payload } : {}) };
138
- case "hook_rewrite_applied":
139
- return { type, hookId: String(raw.hook_id ?? ""), point: String(raw.point ?? ""), patch: (raw.patch ?? {}) };
140
- case "hook_patch_published":
141
- return { type, hookId: String(raw.hook_id ?? ""), point: String(raw.point ?? ""), envelope: (raw.envelope ?? {}) };
142
- // Skills
143
- case "skills_resolved":
144
- return { type, skills: (raw.skills ?? []), injectionBytes: Number(raw.injection_bytes ?? 0) };
145
- case "skill_resolution_failed":
146
- return { type, reference: String(raw.reference ?? ""), error: String(raw.error ?? "") };
147
- // Interaction (comms)
148
- case "interaction_complete":
149
- return { type, interactionId: String(raw.interaction_id ?? ""), result: String(raw.result ?? "") };
150
- case "interaction_failed":
151
- return { type, interactionId: String(raw.interaction_id ?? ""), error: String(raw.error ?? "") };
152
- // Stream management
153
- case "stream_truncated":
154
- return { type, reason: String(raw.reason ?? "") };
155
- case "tool_config_changed": {
156
- const payloadRaw = typeof raw.payload === "object" && raw.payload !== null
157
- ? raw.payload
158
- : {};
159
- const appliedAtTurnRaw = payloadRaw.applied_at_turn;
160
- const appliedAtTurn = typeof appliedAtTurnRaw === "number" && Number.isFinite(appliedAtTurnRaw)
161
- ? appliedAtTurnRaw
162
- : undefined;
163
- return {
164
- type,
165
- payload: {
166
- operation: String(payloadRaw.operation ?? "reload"),
167
- target: String(payloadRaw.target ?? ""),
168
- status: String(payloadRaw.status ?? ""),
169
- persisted: typeof payloadRaw.persisted === "boolean" ? payloadRaw.persisted : false,
170
- ...(appliedAtTurn != null ? { applied_at_turn: appliedAtTurn } : {}),
171
- },
172
- };
463
+ try {
464
+ switch (type) {
465
+ // Session lifecycle
466
+ case "run_started":
467
+ return { type, sessionId: requireStringField(raw, "session_id"), prompt: parseContentInput(raw.prompt) };
468
+ case "run_completed":
469
+ return {
470
+ type,
471
+ sessionId: requireStringField(raw, "session_id"),
472
+ result: requireStringField(raw, "result"),
473
+ ...(raw.structured_output !== undefined ? { structuredOutput: raw.structured_output } : {}),
474
+ ...(raw.extraction_required !== undefined ? { extractionRequired: Boolean(raw.extraction_required) } : {}),
475
+ usage: parseUsage(raw.usage),
476
+ ...terminalCauseKindField(raw),
477
+ };
478
+ case "extraction_succeeded": {
479
+ const rawWarnings = Array.isArray(raw.schema_warnings) ? raw.schema_warnings : undefined;
480
+ const schemaWarnings = rawWarnings?.map((warning) => {
481
+ const record = isPlainRecord(warning) ? warning : {};
482
+ return {
483
+ provider: String(record.provider ?? ""),
484
+ path: String(record.path ?? ""),
485
+ message: String(record.message ?? ""),
486
+ };
487
+ });
488
+ return {
489
+ type,
490
+ sessionId: requireStringField(raw, "session_id"),
491
+ structuredOutput: raw.structured_output,
492
+ ...(schemaWarnings ? { schemaWarnings } : {}),
493
+ };
494
+ }
495
+ case "extraction_failed":
496
+ return {
497
+ type,
498
+ sessionId: requireStringField(raw, "session_id"),
499
+ lastOutput: requireStringField(raw, "last_output"),
500
+ attempts: requireNumberField(raw, "attempts"),
501
+ reason: requireStringField(raw, "reason"),
502
+ };
503
+ case "run_failed":
504
+ return {
505
+ type,
506
+ sessionId: requireStringField(raw, "session_id"),
507
+ errorClass: requireStringField(raw, "error_class"),
508
+ error: requireStringField(raw, "error"),
509
+ ...terminalCauseKindField(raw),
510
+ ...(hasOwn(raw, "error_report") ? { errorReport: parseAgentErrorReport(raw.error_report) ?? null } : {}),
511
+ };
512
+ // Turn / LLM
513
+ case "turn_started":
514
+ return { type, turnNumber: requireNumberField(raw, "turn_number") };
515
+ case "text_delta":
516
+ return { type, delta: requireStringField(raw, "delta") };
517
+ case "text_complete":
518
+ return { type, content: requireStringField(raw, "content") };
519
+ case "tool_call_requested":
520
+ return {
521
+ type,
522
+ id: requireStringField(raw, "id"),
523
+ name: requireStringField(raw, "name"),
524
+ args: requireRecordField(raw, "args"),
525
+ };
526
+ case "tool_result_received":
527
+ return {
528
+ type,
529
+ id: requireStringField(raw, "id"),
530
+ name: requireStringField(raw, "name"),
531
+ content: parseContentBlocks(raw.content),
532
+ isError: requireBooleanField(raw, "is_error"),
533
+ };
534
+ case "turn_completed":
535
+ return {
536
+ type,
537
+ stopReason: requireOneOf(requireStringField(raw, "stop_reason"), "stop_reason", ["end_turn", "tool_use", "max_tokens", "stop_sequence", "content_filter", "cancelled"]),
538
+ usage: parseUsage(raw.usage),
539
+ };
540
+ // Tool execution
541
+ case "tool_execution_started":
542
+ return { type, id: requireStringField(raw, "id"), name: requireStringField(raw, "name") };
543
+ case "tool_execution_completed":
544
+ return {
545
+ type,
546
+ id: requireStringField(raw, "id"),
547
+ name: requireStringField(raw, "name"),
548
+ result: requireStringField(raw, "result"),
549
+ content: parseContentBlocks(raw.content, raw.result),
550
+ ...(parseWireBoolean(raw.is_error) != null ? { isError: parseWireBoolean(raw.is_error) } : {}),
551
+ ...(typeof raw.duration_ms === "number" && Number.isFinite(raw.duration_ms)
552
+ ? { durationMs: raw.duration_ms }
553
+ : {}),
554
+ };
555
+ case "tool_execution_timed_out":
556
+ return { type, id: requireStringField(raw, "id"), name: requireStringField(raw, "name"), timeoutMs: requireNumberField(raw, "timeout_ms") };
557
+ // Compaction
558
+ case "compaction_started":
559
+ return { type, inputTokens: requireNumberField(raw, "input_tokens"), estimatedHistoryTokens: requireNumberField(raw, "estimated_history_tokens"), messageCount: requireNumberField(raw, "message_count") };
560
+ case "compaction_completed":
561
+ return { type, summaryTokens: requireNumberField(raw, "summary_tokens"), messagesBefore: requireNumberField(raw, "messages_before"), messagesAfter: requireNumberField(raw, "messages_after") };
562
+ case "compaction_failed":
563
+ return { type, error: requireStringField(raw, "error") };
564
+ // Budget
565
+ case "budget_warning":
566
+ return { type, budgetType: requireOneOf(requireStringField(raw, "budget_type"), "budget_type", ["tokens", "time", "tool_calls"]), used: requireNumberField(raw, "used"), limit: requireNumberField(raw, "limit"), percent: requireNumberField(raw, "percent") };
567
+ // Retry
568
+ case "retrying":
569
+ return { type, attempt: requireNumberField(raw, "attempt"), maxAttempts: requireNumberField(raw, "max_attempts"), error: requireStringField(raw, "error"), delayMs: requireNumberField(raw, "delay_ms") };
570
+ // Hooks
571
+ case "hook_started":
572
+ return { type, hookId: requireStringField(raw, "hook_id"), point: requireStringField(raw, "point") };
573
+ case "hook_completed":
574
+ return { type, hookId: requireStringField(raw, "hook_id"), point: requireStringField(raw, "point"), durationMs: requireNumberField(raw, "duration_ms") };
575
+ case "hook_failed":
576
+ return { type, hookId: requireStringField(raw, "hook_id"), point: requireStringField(raw, "point"), error: requireStringField(raw, "error") };
577
+ case "hook_denied":
578
+ return { type, hookId: requireStringField(raw, "hook_id"), point: requireStringField(raw, "point"), reasonCode: requireStringField(raw, "reason_code"), message: requireStringField(raw, "message"), ...(raw.payload != null ? { payload: raw.payload } : {}) };
579
+ // Skills
580
+ case "skills_resolved":
581
+ return {
582
+ type,
583
+ skills: requireSkillKeyArray(raw.skills),
584
+ injectionBytes: requireNumberField(raw, "injection_bytes"),
585
+ };
586
+ case "skill_resolution_failed": {
587
+ const reference = requireStringField(raw, "reference");
588
+ const error = requireStringField(raw, "error");
589
+ const skillKey = parseSkillKey(raw.skill_key);
590
+ const reason = parseSkillResolutionFailureReason(raw.reason, error);
591
+ return {
592
+ type,
593
+ ...(skillKey ? { skillKey } : {}),
594
+ ...(reason ? { reason } : {}),
595
+ reference,
596
+ error,
597
+ };
598
+ }
599
+ // Interaction (comms)
600
+ case "interaction_complete":
601
+ return {
602
+ type,
603
+ interactionId: requireStringField(raw, "interaction_id"),
604
+ result: requireStringField(raw, "result"),
605
+ ...(raw.structured_output !== undefined ? { structuredOutput: raw.structured_output } : {}),
606
+ };
607
+ case "interaction_failed":
608
+ return { type, interactionId: requireStringField(raw, "interaction_id"), error: requireStringField(raw, "error") };
609
+ // Stream management
610
+ case "stream_truncated":
611
+ return { type, reason: requireStringField(raw, "reason") };
612
+ case "tool_config_changed": {
613
+ if (!isPlainRecord(raw.payload))
614
+ throw new Error("payload must be object");
615
+ const payloadRaw = raw.payload;
616
+ const appliedAtTurnRaw = payloadRaw.applied_at_turn;
617
+ const appliedAtTurn = typeof appliedAtTurnRaw === "number" && Number.isFinite(appliedAtTurnRaw)
618
+ ? appliedAtTurnRaw
619
+ : undefined;
620
+ const statusInfo = parseToolConfigChangeStatus(payloadRaw?.status_info);
621
+ return {
622
+ type,
623
+ payload: {
624
+ ...(statusInfo != null ? { status_info: statusInfo } : {}),
625
+ operation: requireOneOf(requireStringField(payloadRaw, "operation"), "operation", ["add", "remove", "reload"]),
626
+ target: requireStringField(payloadRaw, "target"),
627
+ status: requireStringField(payloadRaw, "status"),
628
+ persisted: requireBooleanField(payloadRaw, "persisted"),
629
+ ...(appliedAtTurn != null ? { applied_at_turn: appliedAtTurn } : {}),
630
+ },
631
+ };
632
+ }
633
+ case "background_job_completed": {
634
+ const legacyStatus = typeof raw.status === "string" ? raw.status : undefined;
635
+ return {
636
+ type,
637
+ jobId: requireStringField(raw, "job_id"),
638
+ displayName: requireStringField(raw, "display_name"),
639
+ ...(legacyStatus != null ? { legacyStatus } : {}),
640
+ terminalStatus: requireOneOf(requireStringField(raw, "terminal_status"), "terminal_status", ["completed", "failed", "aborted", "cancelled", "retired", "terminated"]),
641
+ detail: requireStringField(raw, "detail"),
642
+ };
643
+ }
644
+ // Unknown — forward-compat
645
+ default:
646
+ return { type, ...raw };
173
647
  }
174
- // Unknown — forward-compat
175
- default:
176
- return { type, ...raw };
648
+ }
649
+ catch (error) {
650
+ return malformedEvent(raw, type, error instanceof Error ? error.message : String(error));
177
651
  }
178
652
  }
179
653
  //# sourceMappingURL=events.js.map