conversationalist 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +148 -30
  2. package/dist/adapters/anthropic/index.d.ts.map +1 -1
  3. package/dist/adapters/anthropic/index.js +255 -2
  4. package/dist/adapters/anthropic/index.js.map +8 -4
  5. package/dist/adapters/gemini/index.d.ts.map +1 -1
  6. package/dist/adapters/gemini/index.js +255 -3
  7. package/dist/adapters/gemini/index.js.map +8 -4
  8. package/dist/adapters/openai/index.d.ts.map +1 -1
  9. package/dist/adapters/openai/index.js +247 -3
  10. package/dist/adapters/openai/index.js.map +8 -4
  11. package/dist/context.d.ts +6 -0
  12. package/dist/context.d.ts.map +1 -1
  13. package/dist/conversation/append.d.ts +5 -0
  14. package/dist/conversation/append.d.ts.map +1 -1
  15. package/dist/conversation/create.d.ts +9 -0
  16. package/dist/conversation/create.d.ts.map +1 -1
  17. package/dist/conversation/index.d.ts +8 -3
  18. package/dist/conversation/index.d.ts.map +1 -1
  19. package/dist/conversation/integrity.d.ts +16 -0
  20. package/dist/conversation/integrity.d.ts.map +1 -0
  21. package/dist/conversation/modify.d.ts +8 -2
  22. package/dist/conversation/modify.d.ts.map +1 -1
  23. package/dist/conversation/serialization.d.ts +0 -17
  24. package/dist/conversation/serialization.d.ts.map +1 -1
  25. package/dist/conversation/system-messages.d.ts.map +1 -1
  26. package/dist/conversation/tool-interactions.d.ts +45 -0
  27. package/dist/conversation/tool-interactions.d.ts.map +1 -0
  28. package/dist/conversation/transform.d.ts.map +1 -1
  29. package/dist/conversation/validation.d.ts +8 -0
  30. package/dist/conversation/validation.d.ts.map +1 -0
  31. package/dist/errors.d.ts +6 -1
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/export/index.js +249 -12
  34. package/dist/export/index.js.map +10 -6
  35. package/dist/guards.d.ts +13 -0
  36. package/dist/guards.d.ts.map +1 -0
  37. package/dist/history.d.ts +21 -8
  38. package/dist/history.d.ts.map +1 -1
  39. package/dist/index.d.ts +4 -3
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +2212 -259
  42. package/dist/index.js.map +26 -13
  43. package/dist/markdown/index.js +2169 -253
  44. package/dist/markdown/index.js.map +23 -12
  45. package/dist/schemas/index.d.ts +1 -1
  46. package/dist/schemas/index.d.ts.map +1 -1
  47. package/dist/schemas/index.js +38 -22
  48. package/dist/schemas/index.js.map +3 -3
  49. package/dist/schemas.d.ts +17 -37
  50. package/dist/schemas.d.ts.map +1 -1
  51. package/dist/streaming.d.ts.map +1 -1
  52. package/dist/types.d.ts +0 -4
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/utilities/index.d.ts +0 -1
  55. package/dist/utilities/index.d.ts.map +1 -1
  56. package/dist/utilities/markdown.d.ts.map +1 -1
  57. package/dist/utilities/tool-calls.d.ts +2 -6
  58. package/dist/utilities/tool-calls.d.ts.map +1 -1
  59. package/dist/utilities/tool-results.d.ts.map +1 -1
  60. package/dist/utilities/transient.d.ts.map +1 -1
  61. package/dist/utilities.d.ts +0 -1
  62. package/dist/utilities.d.ts.map +1 -1
  63. package/dist/versioning/index.d.ts +0 -1
  64. package/dist/versioning/index.d.ts.map +1 -1
  65. package/dist/versioning/index.js +1 -52
  66. package/dist/versioning/index.js.map +4 -5
  67. package/dist/with-conversation.d.ts +4 -1
  68. package/dist/with-conversation.d.ts.map +1 -1
  69. package/package.json +7 -4
  70. package/dist/conversation.d.ts +0 -109
  71. package/dist/conversation.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -23,6 +23,12 @@ function copyContent(content) {
23
23
  }
24
24
  // src/schemas.ts
25
25
  import { z } from "zod";
26
+ var isPlainObject = (value) => {
27
+ if (!value || typeof value !== "object")
28
+ return false;
29
+ const prototype = Reflect.getPrototypeOf(value);
30
+ return prototype === Object.prototype || prototype === null;
31
+ };
26
32
  function toMultiModalContent(value) {
27
33
  const result = { type: value.type };
28
34
  if (value.text !== undefined)
@@ -33,14 +39,28 @@ function toMultiModalContent(value) {
33
39
  result.mimeType = value.mimeType;
34
40
  return result;
35
41
  }
36
- var jsonValueSchema = z.lazy(() => z.union([
37
- z.string(),
38
- z.number(),
39
- z.boolean(),
40
- z.null(),
41
- z.array(jsonValueSchema),
42
- z.record(z.string(), jsonValueSchema)
43
- ]));
42
+ var jsonValueSchema = z.lazy(() => {
43
+ const jsonObjectSchema = z.preprocess((value, ctx) => {
44
+ if (!isPlainObject(value)) {
45
+ ctx.addIssue({
46
+ code: z.ZodIssueCode.custom,
47
+ message: "expected a plain object"
48
+ });
49
+ return z.NEVER;
50
+ }
51
+ return value;
52
+ }, z.record(z.string(), jsonValueSchema));
53
+ return z.union([
54
+ z.string(),
55
+ z.number().refine((value) => Number.isFinite(value), {
56
+ message: "expected a finite number"
57
+ }),
58
+ z.boolean(),
59
+ z.null(),
60
+ z.array(jsonValueSchema),
61
+ jsonObjectSchema
62
+ ]);
63
+ });
44
64
  var multiModalContentSchema = z.discriminatedUnion("type", [
45
65
  z.object({
46
66
  type: z.literal("text"),
@@ -66,16 +86,12 @@ var toolCallSchema = z.object({
66
86
  id: z.string(),
67
87
  name: z.string(),
68
88
  arguments: jsonValueSchema
69
- });
89
+ }).strict();
70
90
  var toolResultSchema = z.object({
71
91
  callId: z.string(),
72
92
  outcome: z.enum(["success", "error"]),
73
- content: jsonValueSchema,
74
- toolCallId: z.string().optional(),
75
- toolName: z.string().optional(),
76
- result: jsonValueSchema.optional(),
77
- error: z.string().optional()
78
- });
93
+ content: jsonValueSchema
94
+ }).strict();
79
95
  var tokenUsageSchema = z.object({
80
96
  prompt: z.number().int().min(0),
81
97
  completion: z.number().int().min(0),
@@ -90,8 +106,8 @@ var messageInputSchema = z.object({
90
106
  toolResult: toolResultSchema.optional(),
91
107
  tokenUsage: tokenUsageSchema.optional(),
92
108
  goalCompleted: z.boolean().optional()
93
- });
94
- var messageJSONSchema = z.object({
109
+ }).strict();
110
+ var messageSchema = z.object({
95
111
  id: z.string(),
96
112
  role: messageRoleSchema,
97
113
  content: z.union([z.string(), z.array(multiModalContentSchema)]),
@@ -103,7 +119,7 @@ var messageJSONSchema = z.object({
103
119
  toolResult: toolResultSchema.optional(),
104
120
  tokenUsage: tokenUsageSchema.optional(),
105
121
  goalCompleted: z.boolean().optional()
106
- }).loose();
122
+ }).strict();
107
123
  var conversationStatusSchema = z.enum([
108
124
  "active",
109
125
  "archived",
@@ -116,11 +132,11 @@ var conversationShape = {
116
132
  status: conversationStatusSchema,
117
133
  metadata: z.record(z.string(), jsonValueSchema),
118
134
  ids: z.array(z.string()),
119
- messages: z.record(z.string(), messageJSONSchema),
135
+ messages: z.record(z.string(), messageSchema),
120
136
  createdAt: z.string(),
121
137
  updatedAt: z.string()
122
138
  };
123
- var conversationSchema = z.object(conversationShape);
139
+ var conversationSchema = z.object(conversationShape).strict();
124
140
  // src/utilities/content.ts
125
141
  function toMultiModalArray(input) {
126
142
  if (typeof input === "string")
@@ -203,6 +219,160 @@ function pairToolCallsWithResults(messages) {
203
219
  }
204
220
  return pairs;
205
221
  }
222
+ // src/errors.ts
223
+ class ConversationalistError extends Error {
224
+ code;
225
+ context;
226
+ cause;
227
+ constructor(code, message, options) {
228
+ super(message);
229
+ this.name = "ConversationalistError";
230
+ this.code = code;
231
+ this.context = options?.context;
232
+ this.cause = options?.cause;
233
+ if (Error.captureStackTrace) {
234
+ Error.captureStackTrace(this, ConversationalistError);
235
+ }
236
+ }
237
+ toDetailedString() {
238
+ const parts = [`[${this.code}] ${this.message}`];
239
+ if (this.context && Object.keys(this.context).length > 0) {
240
+ parts.push(`Context: ${JSON.stringify(this.context, null, 2)}`);
241
+ }
242
+ if (this.cause) {
243
+ parts.push(`Caused by: ${this.cause.message}`);
244
+ }
245
+ return parts.join(`
246
+ `);
247
+ }
248
+ }
249
+ function createLockedError(conversationId) {
250
+ return new ConversationalistError("error:locked", `conversation ${conversationId} is locked (concurrent modification detected)`, { context: { conversationId } });
251
+ }
252
+ function createInvalidInputError(message, context) {
253
+ return new ConversationalistError("error:invalid-input", message, { context });
254
+ }
255
+ function createInvalidPositionError(expected, actual) {
256
+ return new ConversationalistError("error:invalid-position", `invalid position: expected ${expected}, got ${actual}`, { context: { expected, actual } });
257
+ }
258
+ function createInvalidToolReferenceError(callId) {
259
+ return new ConversationalistError("error:invalid-tool-reference", `tool result references non-existent tool-use: ${callId}`, { context: { callId } });
260
+ }
261
+ function createDuplicateIdError(id) {
262
+ return new ConversationalistError("error:duplicate-id", `conversation with id ${id} already exists`, { context: { id } });
263
+ }
264
+ function createNotFoundError(id) {
265
+ return new ConversationalistError("error:not-found", `conversation with id ${id} not found`, {
266
+ context: { id }
267
+ });
268
+ }
269
+ function createSerializationError(message, cause) {
270
+ return new ConversationalistError("error:serialization", message, { cause });
271
+ }
272
+ function createValidationError(message, context, cause) {
273
+ return new ConversationalistError("error:validation", message, { context, cause });
274
+ }
275
+ function createIntegrityError(message, context) {
276
+ return new ConversationalistError("error:integrity", message, { context });
277
+ }
278
+
279
+ // src/conversation/integrity.ts
280
+ function validateConversationIntegrity(conversation) {
281
+ const issues = [];
282
+ const seenIds = new Set;
283
+ conversation.ids.forEach((id, index) => {
284
+ if (seenIds.has(id)) {
285
+ issues.push({
286
+ code: "integrity:duplicate-message-id",
287
+ message: `duplicate message id in ids: ${id}`,
288
+ data: { id, position: index }
289
+ });
290
+ } else {
291
+ seenIds.add(id);
292
+ }
293
+ if (!conversation.messages[id]) {
294
+ issues.push({
295
+ code: "integrity:missing-message",
296
+ message: `missing message for id ${id}`,
297
+ data: { id, position: index }
298
+ });
299
+ }
300
+ });
301
+ for (const id of Object.keys(conversation.messages)) {
302
+ if (!seenIds.has(id)) {
303
+ issues.push({
304
+ code: "integrity:unlisted-message",
305
+ message: `message ${id} is not listed in ids`,
306
+ data: { id }
307
+ });
308
+ }
309
+ }
310
+ const toolUses = new Map;
311
+ conversation.ids.forEach((id, index) => {
312
+ const message = conversation.messages[id];
313
+ if (!message)
314
+ return;
315
+ if (message.role === "tool-use" && message.toolCall) {
316
+ if (toolUses.has(message.toolCall.id)) {
317
+ issues.push({
318
+ code: "integrity:duplicate-tool-call",
319
+ message: `duplicate toolCall.id ${message.toolCall.id}`,
320
+ data: { toolCallId: message.toolCall.id, messageId: message.id }
321
+ });
322
+ } else {
323
+ toolUses.set(message.toolCall.id, { position: index, messageId: message.id });
324
+ }
325
+ }
326
+ });
327
+ conversation.ids.forEach((id, index) => {
328
+ const message = conversation.messages[id];
329
+ if (!message)
330
+ return;
331
+ if (message.role === "tool-result" && message.toolResult) {
332
+ const toolUse = toolUses.get(message.toolResult.callId);
333
+ if (!toolUse) {
334
+ issues.push({
335
+ code: "integrity:orphan-tool-result",
336
+ message: `tool-result references missing tool-use ${message.toolResult.callId}`,
337
+ data: { callId: message.toolResult.callId, messageId: message.id }
338
+ });
339
+ } else if (toolUse.position >= index) {
340
+ issues.push({
341
+ code: "integrity:tool-result-before-call",
342
+ message: `tool-result ${message.toolResult.callId} occurs before tool-use`,
343
+ data: {
344
+ callId: message.toolResult.callId,
345
+ messageId: message.id,
346
+ toolUseMessageId: toolUse.messageId
347
+ }
348
+ });
349
+ }
350
+ }
351
+ });
352
+ return issues;
353
+ }
354
+ function assertConversationIntegrity(conversation) {
355
+ const issues = validateConversationIntegrity(conversation);
356
+ if (issues.length === 0)
357
+ return;
358
+ throw createIntegrityError("conversation integrity check failed", { issues });
359
+ }
360
+
361
+ // src/conversation/validation.ts
362
+ function assertConversationSafe(conversation) {
363
+ const parsed = conversationSchema.safeParse(conversation);
364
+ if (!parsed.success) {
365
+ throw createValidationError("conversation failed schema validation", {
366
+ issues: parsed.error.issues
367
+ });
368
+ }
369
+ assertConversationIntegrity(conversation);
370
+ }
371
+ function ensureConversationSafe(conversation) {
372
+ assertConversationSafe(conversation);
373
+ return conversation;
374
+ }
375
+
206
376
  // src/utilities/message-store.ts
207
377
  function getOrderedMessages(conversation) {
208
378
  const ordered = [];
@@ -259,7 +429,7 @@ function stripTransientMetadata(conversation) {
259
429
  }
260
430
  return toReadonly(baseMessage);
261
431
  });
262
- return toReadonly({
432
+ return ensureConversationSafe(toReadonly({
263
433
  schemaVersion: conversation.schemaVersion,
264
434
  id: conversation.id,
265
435
  title: conversation.title,
@@ -269,7 +439,7 @@ function stripTransientMetadata(conversation) {
269
439
  messages: toIdRecord(strippedMessages),
270
440
  createdAt: conversation.createdAt,
271
441
  updatedAt: conversation.updatedAt
272
- });
442
+ }));
273
443
  }
274
444
  // src/environment.ts
275
445
  function simpleTokenEstimator(message) {
@@ -302,64 +472,43 @@ function withEnvironment(environment, fn) {
302
472
  return (...args) => fn(...args, environment);
303
473
  }
304
474
 
305
- // src/errors.ts
306
- class ConversationalistError extends Error {
307
- code;
308
- context;
309
- cause;
310
- constructor(code, message, options) {
311
- super(message);
312
- this.name = "ConversationalistError";
313
- this.code = code;
314
- this.context = options?.context;
315
- this.cause = options?.cause;
316
- if (Error.captureStackTrace) {
317
- Error.captureStackTrace(this, ConversationalistError);
318
- }
319
- }
320
- toDetailedString() {
321
- const parts = [`[${this.code}] ${this.message}`];
322
- if (this.context && Object.keys(this.context).length > 0) {
323
- parts.push(`Context: ${JSON.stringify(this.context, null, 2)}`);
324
- }
325
- if (this.cause) {
326
- parts.push(`Caused by: ${this.cause.message}`);
327
- }
328
- return parts.join(`
329
- `);
330
- }
331
- }
332
- function createLockedError(conversationId) {
333
- return new ConversationalistError("error:locked", `conversation ${conversationId} is locked (concurrent modification detected)`, { context: { conversationId } });
334
- }
335
- function createInvalidInputError(message, context) {
336
- return new ConversationalistError("error:invalid-input", message, { context });
337
- }
338
- function createInvalidPositionError(expected, actual) {
339
- return new ConversationalistError("error:invalid-position", `invalid position: expected ${expected}, got ${actual}`, { context: { expected, actual } });
340
- }
341
- function createInvalidToolReferenceError(callId) {
342
- return new ConversationalistError("error:invalid-tool-reference", `tool result references non-existent tool-use: ${callId}`, { context: { callId } });
343
- }
344
- function createDuplicateIdError(id) {
345
- return new ConversationalistError("error:duplicate-id", `conversation with id ${id} already exists`, { context: { id } });
346
- }
347
- function createNotFoundError(id) {
348
- return new ConversationalistError("error:not-found", `conversation with id ${id} not found`, {
349
- context: { id }
350
- });
351
- }
352
- function createSerializationError(message, cause) {
353
- return new ConversationalistError("error:serialization", message, { cause });
354
- }
355
- function createValidationError(message, context, cause) {
356
- return new ConversationalistError("error:validation", message, { context, cause });
357
- }
358
-
359
475
  // src/types.ts
360
476
  var CURRENT_SCHEMA_VERSION = 3;
361
477
 
362
- // src/conversation.ts
478
+ // src/conversation/create.ts
479
+ function createConversation(options, environment) {
480
+ const resolvedEnvironment = resolveConversationEnvironment(environment);
481
+ const now = resolvedEnvironment.now();
482
+ const conv = {
483
+ schemaVersion: CURRENT_SCHEMA_VERSION,
484
+ id: options?.id ?? resolvedEnvironment.randomId(),
485
+ title: options?.title,
486
+ status: options?.status ?? "active",
487
+ metadata: { ...options?.metadata ?? {} },
488
+ ids: [],
489
+ messages: {},
490
+ createdAt: now,
491
+ updatedAt: now
492
+ };
493
+ return ensureConversationSafe(toReadonly(conv));
494
+ }
495
+ function createConversationUnsafe(options, environment) {
496
+ const resolvedEnvironment = resolveConversationEnvironment(environment);
497
+ const now = resolvedEnvironment.now();
498
+ const conv = {
499
+ schemaVersion: CURRENT_SCHEMA_VERSION,
500
+ id: options?.id ?? resolvedEnvironment.randomId(),
501
+ title: options?.title,
502
+ status: options?.status ?? "active",
503
+ metadata: { ...options?.metadata ?? {} },
504
+ ids: [],
505
+ messages: {},
506
+ createdAt: now,
507
+ updatedAt: now
508
+ };
509
+ return toReadonly(conv);
510
+ }
511
+ // src/conversation/tool-tracking.ts
363
512
  var buildToolUseIndex = (messages) => messages.reduce((index, message) => {
364
513
  if (message.role === "tool-use" && message.toolCall) {
365
514
  index.set(message.toolCall.id, { name: message.toolCall.name });
@@ -376,44 +525,37 @@ var assertToolReference = (index, callId) => {
376
525
  throw createInvalidToolReferenceError(callId);
377
526
  }
378
527
  };
528
+
529
+ // src/conversation/append.ts
379
530
  function partitionAppendArgs(args) {
380
- if (args.length === 0) {
531
+ const filtered = args.filter((arg) => arg !== undefined);
532
+ if (filtered.length === 0) {
381
533
  return { inputs: [] };
382
534
  }
383
- const last = args[args.length - 1];
535
+ const last = filtered[filtered.length - 1];
384
536
  if (isConversationEnvironmentParameter(last)) {
385
537
  return {
386
- inputs: args.slice(0, -1),
538
+ inputs: filtered.slice(0, -1),
387
539
  environment: last
388
540
  };
389
541
  }
390
- return { inputs: args };
391
- }
392
- function createConversation(options, environment) {
393
- const resolvedEnvironment = resolveConversationEnvironment(environment);
394
- const now = resolvedEnvironment.now();
395
- const conv = {
396
- schemaVersion: CURRENT_SCHEMA_VERSION,
397
- id: options?.id ?? resolvedEnvironment.randomId(),
398
- title: options?.title,
399
- status: options?.status ?? "active",
400
- metadata: { ...options?.metadata ?? {} },
401
- ids: [],
402
- messages: {},
403
- createdAt: now,
404
- updatedAt: now
405
- };
406
- return toReadonly(conv);
542
+ return { inputs: filtered };
407
543
  }
408
544
  function appendMessages(conversation, ...args) {
545
+ return appendMessagesInternal(conversation, args, true);
546
+ }
547
+ function appendUnsafeMessage(conversation, input, environment) {
548
+ return appendMessagesInternal(conversation, [input, environment], false);
549
+ }
550
+ var appendMessagesInternal = (conversation, args, validate) => {
409
551
  const { inputs, environment } = partitionAppendArgs(args);
410
552
  const resolvedEnvironment = resolveConversationEnvironment(environment);
411
553
  const now = resolvedEnvironment.now();
412
554
  const startPosition = conversation.ids.length;
413
- const initialToolUses = buildToolUseIndex(getOrderedMessages(conversation));
555
+ const initialToolUses = validate ? buildToolUseIndex(getOrderedMessages(conversation)) : new Map;
414
556
  const { messages } = inputs.reduce((state, input, index) => {
415
557
  const processedInput = resolvedEnvironment.plugins.reduce((acc, plugin) => plugin(acc), input);
416
- if (processedInput.role === "tool-result" && processedInput.toolResult) {
558
+ if (validate && processedInput.role === "tool-result" && processedInput.toolResult) {
417
559
  assertToolReference(state.toolUses, processedInput.toolResult.callId);
418
560
  }
419
561
  const normalizedContent = normalizeContent(processedInput.content);
@@ -440,7 +582,16 @@ function appendMessages(conversation, ...args) {
440
582
  } else {
441
583
  message = createMessage(baseMessage);
442
584
  }
443
- const toolUses = processedInput.role === "tool-use" && processedInput.toolCall ? registerToolUse(state.toolUses, processedInput.toolCall) : state.toolUses;
585
+ let toolUses = state.toolUses;
586
+ if (processedInput.role === "tool-use" && processedInput.toolCall) {
587
+ if (validate && state.toolUses.has(processedInput.toolCall.id)) {
588
+ throw createIntegrityError("duplicate toolCall.id in conversation", {
589
+ toolCallId: processedInput.toolCall.id,
590
+ messageId: baseMessage.id
591
+ });
592
+ }
593
+ toolUses = validate ? registerToolUse(state.toolUses, processedInput.toolCall) : state.toolUses;
594
+ }
444
595
  return {
445
596
  toolUses,
446
597
  messages: [...state.messages, message]
@@ -453,17 +604,25 @@ function appendMessages(conversation, ...args) {
453
604
  messages: { ...conversation.messages, ...toIdRecord(messages) },
454
605
  updatedAt: now
455
606
  };
456
- return toReadonly(next);
457
- }
607
+ const readonly = toReadonly(next);
608
+ return validate ? ensureConversationSafe(readonly) : readonly;
609
+ };
458
610
  function appendUserMessage(conversation, content, metadata, environment) {
459
- return environment ? appendMessages(conversation, { role: "user", content, metadata }, environment) : appendMessages(conversation, { role: "user", content, metadata });
611
+ const resolvedEnvironment = isConversationEnvironmentParameter(metadata) ? metadata : environment;
612
+ const resolvedMetadata = isConversationEnvironmentParameter(metadata) ? undefined : metadata;
613
+ return appendMessages(conversation, { role: "user", content, metadata: resolvedMetadata }, resolvedEnvironment);
460
614
  }
461
615
  function appendAssistantMessage(conversation, content, metadata, environment) {
462
- return environment ? appendMessages(conversation, { role: "assistant", content, metadata }, environment) : appendMessages(conversation, { role: "assistant", content, metadata });
616
+ const resolvedEnvironment = isConversationEnvironmentParameter(metadata) ? metadata : environment;
617
+ const resolvedMetadata = isConversationEnvironmentParameter(metadata) ? undefined : metadata;
618
+ return appendMessages(conversation, { role: "assistant", content, metadata: resolvedMetadata }, resolvedEnvironment);
463
619
  }
464
620
  function appendSystemMessage(conversation, content, metadata, environment) {
465
- return environment ? appendMessages(conversation, { role: "system", content, metadata }, environment) : appendMessages(conversation, { role: "system", content, metadata });
621
+ const resolvedEnvironment = isConversationEnvironmentParameter(metadata) ? metadata : environment;
622
+ const resolvedMetadata = isConversationEnvironmentParameter(metadata) ? undefined : metadata;
623
+ return appendMessages(conversation, { role: "system", content, metadata: resolvedMetadata }, resolvedEnvironment);
466
624
  }
625
+ // src/conversation/query.ts
467
626
  function getMessages(conversation, options) {
468
627
  const includeHidden = options?.includeHidden ?? false;
469
628
  const ordered = getOrderedMessages(conversation);
@@ -497,6 +656,7 @@ function getStatistics(conversation) {
497
656
  }, { byRole: {}, hidden: 0, withImages: 0 });
498
657
  return { total: ordered.length, ...stats };
499
658
  }
659
+ // src/conversation/system-messages.ts
500
660
  function hasSystemMessage(conversation) {
501
661
  return getOrderedMessages(conversation).some((m) => m.role === "system");
502
662
  }
@@ -507,7 +667,8 @@ function getSystemMessages(conversation) {
507
667
  return getOrderedMessages(conversation).filter((m) => m.role === "system");
508
668
  }
509
669
  function prependSystemMessage(conversation, content, metadata, environment) {
510
- const resolvedEnvironment = resolveConversationEnvironment(environment);
670
+ const resolvedEnvironment = resolveConversationEnvironment(isConversationEnvironmentParameter(metadata) ? metadata : environment);
671
+ const resolvedMetadata = isConversationEnvironmentParameter(metadata) ? undefined : metadata;
511
672
  const now = resolvedEnvironment.now();
512
673
  const newMessage = createMessage({
513
674
  id: resolvedEnvironment.randomId(),
@@ -515,7 +676,7 @@ function prependSystemMessage(conversation, content, metadata, environment) {
515
676
  content,
516
677
  position: 0,
517
678
  createdAt: now,
518
- metadata: { ...metadata ?? {} },
679
+ metadata: { ...resolvedMetadata ?? {} },
519
680
  hidden: false,
520
681
  toolCall: undefined,
521
682
  toolResult: undefined,
@@ -534,20 +695,21 @@ function prependSystemMessage(conversation, content, metadata, environment) {
534
695
  toolResult: message.toolResult,
535
696
  tokenUsage: message.tokenUsage
536
697
  }));
537
- return toReadonly({
698
+ return ensureConversationSafe(toReadonly({
538
699
  ...conversation,
539
700
  ids: [newMessage.id, ...ordered.map((message) => message.id)],
540
701
  messages: toIdRecord([newMessage, ...renumberedMessages]),
541
702
  updatedAt: now
542
- });
703
+ }));
543
704
  }
544
705
  function replaceSystemMessage(conversation, content, metadata, environment) {
545
- const resolvedEnvironment = resolveConversationEnvironment(environment);
706
+ const resolvedEnvironment = resolveConversationEnvironment(isConversationEnvironmentParameter(metadata) ? metadata : environment);
707
+ const resolvedMetadata = isConversationEnvironmentParameter(metadata) ? undefined : metadata;
546
708
  const now = resolvedEnvironment.now();
547
709
  const ordered = getOrderedMessages(conversation);
548
710
  const firstSystemIndex = ordered.findIndex((m) => m.role === "system");
549
711
  if (firstSystemIndex === -1) {
550
- return prependSystemMessage(conversation, content, metadata, resolvedEnvironment);
712
+ return prependSystemMessage(conversation, content, resolvedMetadata, resolvedEnvironment);
551
713
  }
552
714
  const original = ordered[firstSystemIndex];
553
715
  const replaced = createMessage({
@@ -556,7 +718,7 @@ function replaceSystemMessage(conversation, content, metadata, environment) {
556
718
  content,
557
719
  position: original.position,
558
720
  createdAt: original.createdAt,
559
- metadata: { ...metadata ?? original.metadata },
721
+ metadata: { ...resolvedMetadata ?? original.metadata },
560
722
  hidden: original.hidden,
561
723
  toolCall: undefined,
562
724
  toolResult: undefined,
@@ -568,13 +730,13 @@ function replaceSystemMessage(conversation, content, metadata, environment) {
568
730
  messages: { ...conversation.messages, [replaced.id]: replaced },
569
731
  updatedAt: now
570
732
  };
571
- return toReadonly(next);
733
+ return ensureConversationSafe(toReadonly(next));
572
734
  }
573
735
  function collapseSystemMessages(conversation, environment) {
574
736
  const ordered = getOrderedMessages(conversation);
575
737
  const systemMessages = ordered.filter((m) => m.role === "system");
576
738
  if (systemMessages.length <= 1) {
577
- return conversation;
739
+ return ensureConversationSafe(conversation);
578
740
  }
579
741
  const resolvedEnvironment = resolveConversationEnvironment(environment);
580
742
  const now = resolvedEnvironment.now();
@@ -630,9 +792,39 @@ function collapseSystemMessages(conversation, environment) {
630
792
  messages: toIdRecord(renumbered),
631
793
  updatedAt: now
632
794
  };
633
- return toReadonly(next);
795
+ return ensureConversationSafe(toReadonly(next));
634
796
  }
635
- function redactMessageAtPosition(conversation, position, placeholder = "[REDACTED]", environment) {
797
+ // src/utilities/tool-results.ts
798
+ function redactToolResult(toolResult, placeholder) {
799
+ return { ...toolResult, content: placeholder };
800
+ }
801
+
802
+ // src/conversation/modify.ts
803
+ var isRedactMessageOptions = (value) => {
804
+ if (!value || typeof value !== "object")
805
+ return false;
806
+ const candidate = value;
807
+ return "placeholder" in candidate || "redactToolArguments" in candidate || "redactToolResults" in candidate || "clearToolMetadata" in candidate;
808
+ };
809
+ function redactMessageAtPosition(conversation, position, placeholderOrOptions, environment) {
810
+ let placeholder = "[REDACTED]";
811
+ let options = {};
812
+ let env = environment;
813
+ if (typeof placeholderOrOptions === "string") {
814
+ placeholder = placeholderOrOptions;
815
+ } else if (placeholderOrOptions) {
816
+ if (!environment && isConversationEnvironmentParameter(placeholderOrOptions)) {
817
+ env = placeholderOrOptions;
818
+ } else if (isRedactMessageOptions(placeholderOrOptions)) {
819
+ options = placeholderOrOptions;
820
+ if (options.placeholder) {
821
+ placeholder = options.placeholder;
822
+ }
823
+ }
824
+ }
825
+ const redactToolArguments = options.redactToolArguments ?? true;
826
+ const redactToolResults = options.redactToolResults ?? true;
827
+ const clearToolMetadata = options.clearToolMetadata ?? false;
636
828
  if (position < 0 || position >= conversation.ids.length) {
637
829
  throw createInvalidPositionError(conversation.ids.length - 1, position);
638
830
  }
@@ -641,6 +833,22 @@ function redactMessageAtPosition(conversation, position, placeholder = "[REDACTE
641
833
  if (!original) {
642
834
  throw createInvalidPositionError(conversation.ids.length - 1, position);
643
835
  }
836
+ let toolCall = original.toolCall ? { ...original.toolCall } : undefined;
837
+ let toolResult = original.toolResult ? { ...original.toolResult } : undefined;
838
+ if (clearToolMetadata) {
839
+ toolCall = undefined;
840
+ toolResult = undefined;
841
+ } else {
842
+ if (original.role === "tool-use" && toolCall) {
843
+ toolCall = {
844
+ ...toolCall,
845
+ arguments: redactToolArguments ? placeholder : toolCall.arguments
846
+ };
847
+ }
848
+ if (original.role === "tool-result" && toolResult) {
849
+ toolResult = redactToolResults ? redactToolResult(toolResult, placeholder) : { ...toolResult };
850
+ }
851
+ }
644
852
  const redacted = createMessage({
645
853
  id: original.id,
646
854
  role: original.role,
@@ -649,11 +857,11 @@ function redactMessageAtPosition(conversation, position, placeholder = "[REDACTE
649
857
  createdAt: original.createdAt,
650
858
  metadata: { ...original.metadata },
651
859
  hidden: original.hidden,
652
- toolCall: undefined,
653
- toolResult: undefined,
654
- tokenUsage: undefined
860
+ toolCall,
861
+ toolResult,
862
+ tokenUsage: original.tokenUsage ? { ...original.tokenUsage } : undefined
655
863
  });
656
- const resolvedEnvironment = resolveConversationEnvironment(environment);
864
+ const resolvedEnvironment = resolveConversationEnvironment(env);
657
865
  const now = resolvedEnvironment.now();
658
866
  const next = {
659
867
  ...conversation,
@@ -661,70 +869,62 @@ function redactMessageAtPosition(conversation, position, placeholder = "[REDACTE
661
869
  messages: { ...conversation.messages, [redacted.id]: redacted },
662
870
  updatedAt: now
663
871
  };
664
- return toReadonly(next);
872
+ return ensureConversationSafe(toReadonly(next));
665
873
  }
666
- function migrateConversation(json) {
667
- if (typeof json !== "object" || json === null || Array.isArray(json)) {
668
- return {
669
- schemaVersion: CURRENT_SCHEMA_VERSION,
670
- id: "",
671
- status: "active",
672
- metadata: {},
673
- ids: [],
674
- messages: {},
675
- createdAt: new Date().toISOString(),
676
- updatedAt: new Date().toISOString()
677
- };
678
- }
679
- const data = json;
680
- const rawMessages = data.messages;
681
- let messages = {};
682
- let ids = [];
683
- const rawIds = data.ids;
684
- const isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
685
- if (Array.isArray(rawMessages)) {
686
- const rawMessageArray = rawMessages;
687
- ids = rawMessageArray.map((message) => message.id);
688
- messages = Object.fromEntries(rawMessageArray.map((message) => [message.id, message]));
689
- } else if (rawMessages && typeof rawMessages === "object") {
690
- messages = { ...rawMessages };
691
- if (isStringArray(rawIds) && rawIds.length > 0) {
692
- ids = [...rawIds];
693
- } else {
694
- ids = Object.values(messages).sort((a, b) => a.position - b.position).map((message) => message.id);
695
- }
696
- }
697
- if (ids.length > 0) {
698
- ids = ids.filter((id) => (id in messages));
699
- const missing = Object.keys(messages).filter((id) => !ids.includes(id));
700
- if (missing.length > 0) {
701
- const sortedMissing = missing.sort((a, b) => (messages[a]?.position ?? 0) - (messages[b]?.position ?? 0));
702
- ids = [...ids, ...sortedMissing];
703
- }
704
- }
705
- if (!("schemaVersion" in json)) {
874
+ // src/conversation/serialization.ts
875
+ function normalizeToolResult(toolResult) {
876
+ if (!toolResult)
877
+ return;
878
+ return {
879
+ callId: toolResult.callId,
880
+ outcome: toolResult.outcome,
881
+ content: toolResult.content
882
+ };
883
+ }
884
+ function normalizeMessage(message) {
885
+ const base = {
886
+ id: message.id,
887
+ role: message.role,
888
+ content: message.content,
889
+ position: message.position,
890
+ createdAt: message.createdAt,
891
+ metadata: message.metadata,
892
+ hidden: message.hidden,
893
+ toolCall: message.toolCall ? { ...message.toolCall } : undefined,
894
+ toolResult: normalizeToolResult(message.toolResult),
895
+ tokenUsage: message.tokenUsage ? { ...message.tokenUsage } : undefined
896
+ };
897
+ if (isAssistantMessage(message)) {
706
898
  return {
707
- ...data,
708
- schemaVersion: CURRENT_SCHEMA_VERSION,
709
- ids,
710
- messages
899
+ ...base,
900
+ role: "assistant",
901
+ goalCompleted: message.goalCompleted
711
902
  };
712
903
  }
713
- return { ...data, ids, messages };
904
+ return base;
714
905
  }
715
906
  function deserializeConversation(json) {
716
- const migrated = migrateConversation(json);
907
+ const parsed = conversationSchema.safeParse(json);
908
+ if (!parsed.success) {
909
+ throw createSerializationError("failed to deserialize conversation: invalid data");
910
+ }
911
+ const data = parsed.data;
717
912
  try {
718
- const orderedMessages = migrated.ids.map((id, index) => {
719
- const message = migrated.messages[id];
913
+ const messageIds = new Set(Object.keys(data.messages));
914
+ const orderedMessages = data.ids.map((id, index) => {
915
+ const message = data.messages[id];
720
916
  if (!message) {
721
917
  throw createSerializationError(`missing message for id ${id}`);
722
918
  }
723
919
  if (message.position !== index) {
724
920
  throw createInvalidPositionError(index, message.position);
725
921
  }
726
- return message;
922
+ messageIds.delete(id);
923
+ return normalizeMessage(message);
727
924
  });
925
+ if (messageIds.size > 0) {
926
+ throw createSerializationError(`messages not listed in ids: ${[...messageIds].join(", ")}`);
927
+ }
728
928
  orderedMessages.reduce((state, message) => {
729
929
  if (message.role === "tool-use" && message.toolCall) {
730
930
  return {
@@ -736,24 +936,28 @@ function deserializeConversation(json) {
736
936
  }
737
937
  return state;
738
938
  }, { toolUses: new Map });
739
- const messageInstances = orderedMessages.map((m) => createMessage(m));
939
+ const messageInstances = orderedMessages.map((message) => createMessage(message));
740
940
  const conv = {
741
- schemaVersion: migrated.schemaVersion,
742
- id: migrated.id,
743
- title: migrated.title,
744
- status: migrated.status,
745
- metadata: { ...migrated.metadata },
941
+ schemaVersion: data.schemaVersion,
942
+ id: data.id,
943
+ title: data.title,
944
+ status: data.status,
945
+ metadata: { ...data.metadata },
746
946
  ids: orderedMessages.map((message) => message.id),
747
947
  messages: toIdRecord(messageInstances),
748
- createdAt: migrated.createdAt,
749
- updatedAt: migrated.updatedAt
948
+ createdAt: data.createdAt,
949
+ updatedAt: data.updatedAt
750
950
  };
751
- return toReadonly(conv);
951
+ const readonly = toReadonly(conv);
952
+ assertConversationIntegrity(readonly);
953
+ return readonly;
752
954
  } catch (error) {
753
955
  throw createSerializationError(`failed to deserialize conversation: ${error instanceof Error ? error.message : String(error)}`, error);
754
956
  }
755
957
  }
958
+ // src/conversation/transform.ts
756
959
  function toChatMessages(conversation) {
960
+ assertConversationSafe(conversation);
757
961
  const roleMap = {
758
962
  user: "user",
759
963
  assistant: "assistant",
@@ -775,12 +979,102 @@ function toChatMessages(conversation) {
775
979
  }
776
980
  return result;
777
981
  }
778
- // src/context.ts
779
- var cloneMessageWithPosition = (message, position, content) => {
780
- const baseMessage = {
781
- id: message.id,
782
- role: message.role,
783
- content,
982
+ // src/conversation/tool-interactions.ts
983
+ function appendToolUse(conversation, toolCall, options, environment) {
984
+ const resolved = resolveConversationEnvironment(isConversationEnvironmentParameter(options) ? options : environment);
985
+ const resolvedOptions = isConversationEnvironmentParameter(options) ? undefined : options;
986
+ const callId = toolCall.callId ?? resolved.randomId();
987
+ const toolCallMeta = {
988
+ id: callId,
989
+ name: toolCall.toolId,
990
+ arguments: toolCall.args
991
+ };
992
+ return appendMessages(conversation, {
993
+ role: "tool-use",
994
+ content: resolvedOptions?.content ?? "",
995
+ metadata: resolvedOptions?.metadata,
996
+ hidden: resolvedOptions?.hidden,
997
+ toolCall: toolCallMeta,
998
+ tokenUsage: resolvedOptions?.tokenUsage
999
+ }, resolved);
1000
+ }
1001
+ function appendToolResult(conversation, toolResult, options, environment) {
1002
+ const resolvedOptions = isConversationEnvironmentParameter(options) ? undefined : options;
1003
+ const toolResultMeta = {
1004
+ callId: toolResult.callId,
1005
+ outcome: toolResult.outcome,
1006
+ content: toolResult.result
1007
+ };
1008
+ return appendMessages(conversation, {
1009
+ role: "tool-result",
1010
+ content: resolvedOptions?.content ?? "",
1011
+ metadata: resolvedOptions?.metadata,
1012
+ hidden: resolvedOptions?.hidden,
1013
+ toolResult: toolResultMeta,
1014
+ tokenUsage: resolvedOptions?.tokenUsage
1015
+ }, isConversationEnvironmentParameter(options) ? options : environment);
1016
+ }
1017
+ function getPendingToolCalls(conversation) {
1018
+ const ordered = getOrderedMessages(conversation);
1019
+ const completed = new Set;
1020
+ for (const message of ordered) {
1021
+ if (message.role === "tool-result" && message.toolResult) {
1022
+ completed.add(message.toolResult.callId);
1023
+ }
1024
+ }
1025
+ const pending = [];
1026
+ for (const message of ordered) {
1027
+ if (message.role === "tool-use" && message.toolCall) {
1028
+ if (!completed.has(message.toolCall.id)) {
1029
+ pending.push(message.toolCall);
1030
+ }
1031
+ }
1032
+ }
1033
+ return pending;
1034
+ }
1035
+ function getToolInteractions(conversation) {
1036
+ return pairToolCallsWithResults(getOrderedMessages(conversation));
1037
+ }
1038
+ // src/guards.ts
1039
+ function isSchema(schema, value) {
1040
+ return schema.safeParse(value).success;
1041
+ }
1042
+ function isConversation(value) {
1043
+ return isSchema(conversationSchema, value);
1044
+ }
1045
+ function isConversationStatus(value) {
1046
+ return isSchema(conversationStatusSchema, value);
1047
+ }
1048
+ function isJSONValue(value) {
1049
+ return isSchema(jsonValueSchema, value);
1050
+ }
1051
+ function isMessage(value) {
1052
+ return isSchema(messageSchema, value);
1053
+ }
1054
+ function isMessageInput(value) {
1055
+ return isSchema(messageInputSchema, value);
1056
+ }
1057
+ function isMessageRole(value) {
1058
+ return isSchema(messageRoleSchema, value);
1059
+ }
1060
+ function isMultiModalContent(value) {
1061
+ return isSchema(multiModalContentSchema, value);
1062
+ }
1063
+ function isTokenUsage(value) {
1064
+ return isSchema(tokenUsageSchema, value);
1065
+ }
1066
+ function isToolCall(value) {
1067
+ return isSchema(toolCallSchema, value);
1068
+ }
1069
+ function isToolResult(value) {
1070
+ return isSchema(toolResultSchema, value);
1071
+ }
1072
+ // src/context.ts
1073
+ var cloneMessageWithPosition = (message, position, content) => {
1074
+ const baseMessage = {
1075
+ id: message.id,
1076
+ role: message.role,
1077
+ content,
784
1078
  position,
785
1079
  createdAt: message.createdAt,
786
1080
  metadata: { ...message.metadata },
@@ -799,6 +1093,92 @@ var cloneMessageWithPosition = (message, position, content) => {
799
1093
  }
800
1094
  return createMessage(baseMessage);
801
1095
  };
1096
+ var createMessageBlock = (message, estimator) => ({
1097
+ messages: [message],
1098
+ minPosition: message.position,
1099
+ maxPosition: message.position,
1100
+ tokenCount: estimator(message)
1101
+ });
1102
+ var buildMessageBlocks = (messages, estimator, preserveToolPairs) => {
1103
+ if (!preserveToolPairs) {
1104
+ const blocks2 = messages.map((message) => createMessageBlock(message, estimator));
1105
+ const messageToBlock2 = new Map;
1106
+ for (const block of blocks2) {
1107
+ const message = block.messages[0];
1108
+ if (message) {
1109
+ messageToBlock2.set(message.id, block);
1110
+ }
1111
+ }
1112
+ return { blocks: blocks2, messageToBlock: messageToBlock2 };
1113
+ }
1114
+ const blocks = [];
1115
+ const toolUses = new Map;
1116
+ for (const message of messages) {
1117
+ if (message.role === "tool-use" && message.toolCall) {
1118
+ const block = createMessageBlock(message, estimator);
1119
+ toolUses.set(message.toolCall.id, block);
1120
+ blocks.push(block);
1121
+ continue;
1122
+ }
1123
+ if (message.role === "tool-result" && message.toolResult) {
1124
+ const existing = toolUses.get(message.toolResult.callId);
1125
+ if (existing) {
1126
+ existing.messages.push(message);
1127
+ existing.maxPosition = Math.max(existing.maxPosition, message.position);
1128
+ existing.tokenCount += estimator(message);
1129
+ continue;
1130
+ }
1131
+ const orphanBlock = createMessageBlock(message, estimator);
1132
+ orphanBlock.orphanToolResult = true;
1133
+ blocks.push(orphanBlock);
1134
+ continue;
1135
+ }
1136
+ blocks.push(createMessageBlock(message, estimator));
1137
+ }
1138
+ const filteredBlocks = blocks.filter((block) => !block.orphanToolResult);
1139
+ const messageToBlock = new Map;
1140
+ for (const block of filteredBlocks) {
1141
+ for (const message of block.messages) {
1142
+ messageToBlock.set(message.id, block);
1143
+ }
1144
+ }
1145
+ return { blocks: filteredBlocks, messageToBlock };
1146
+ };
1147
+ var collectBlocksForMessages = (messages, messageToBlock) => {
1148
+ const blocks = [];
1149
+ const seen = new Set;
1150
+ for (const message of messages) {
1151
+ const block = messageToBlock.get(message.id);
1152
+ if (block && !seen.has(block)) {
1153
+ seen.add(block);
1154
+ blocks.push(block);
1155
+ }
1156
+ }
1157
+ return blocks;
1158
+ };
1159
+ var collectMessagesFromBlocks = (blocks) => {
1160
+ const messages = [];
1161
+ const seen = new Set;
1162
+ for (const block of blocks) {
1163
+ for (const message of block.messages) {
1164
+ if (!seen.has(message.id)) {
1165
+ seen.add(message.id);
1166
+ messages.push(message);
1167
+ }
1168
+ }
1169
+ }
1170
+ messages.sort((a, b) => a.position - b.position);
1171
+ return messages;
1172
+ };
1173
+ var ensureTruncationSafe = (conversation, preserveToolPairs, operation) => {
1174
+ try {
1175
+ return ensureConversationSafe(conversation);
1176
+ } catch (error) {
1177
+ if (!preserveToolPairs && error instanceof ConversationalistError && error.code === "error:integrity")
1178
+ throw createIntegrityError(`${operation} produced invalid tool linkage; use preserveToolPairs: true to keep tool interactions intact`, { preserveToolPairs, issues: error.context?.["issues"] });
1179
+ throw error;
1180
+ }
1181
+ };
802
1182
  function estimateConversationTokens(conversation, estimateTokens, environment) {
803
1183
  let estimator = estimateTokens;
804
1184
  let env = environment;
@@ -811,6 +1191,7 @@ function estimateConversationTokens(conversation, estimateTokens, environment) {
811
1191
  return getOrderedMessages(conversation).reduce((total, message) => total + finalEstimator(message), 0);
812
1192
  }
813
1193
  function truncateToTokenLimit(conversation, maxTokens, optionsOrEstimator, environment) {
1194
+ assertConversationSafe(conversation);
814
1195
  let options = {};
815
1196
  let env = environment;
816
1197
  if (typeof optionsOrEstimator === "function") {
@@ -832,54 +1213,56 @@ function truncateToTokenLimit(conversation, maxTokens, optionsOrEstimator, envir
832
1213
  const estimator = options.estimateTokens ?? resolvedEnvironment.estimateTokens;
833
1214
  const preserveSystem = options.preserveSystemMessages ?? true;
834
1215
  const preserveLastN = options.preserveLastN ?? 0;
1216
+ const preserveToolPairs = options.preserveToolPairs ?? true;
835
1217
  const currentTokens = estimateConversationTokens(conversation, estimator, resolvedEnvironment);
836
1218
  if (currentTokens <= maxTokens) {
837
1219
  return conversation;
838
1220
  }
839
1221
  const now = resolvedEnvironment.now();
840
1222
  const orderedMessages = getOrderedMessages(conversation);
1223
+ const { blocks, messageToBlock } = buildMessageBlocks(orderedMessages, estimator, preserveToolPairs);
841
1224
  const systemMessages = preserveSystem ? orderedMessages.filter((m) => m.role === "system") : [];
842
1225
  const nonSystemMessages = orderedMessages.filter((m) => m.role !== "system");
843
1226
  const protectedMessages = preserveLastN > 0 ? nonSystemMessages.slice(-preserveLastN) : [];
844
- const removableMessages = preserveLastN > 0 ? nonSystemMessages.slice(0, -preserveLastN) : nonSystemMessages;
845
- const systemTokens = systemMessages.reduce((sum, m) => sum + estimator(m), 0);
846
- const protectedTokens = protectedMessages.reduce((sum, m) => sum + estimator(m), 0);
1227
+ const systemBlocks = collectBlocksForMessages(systemMessages, messageToBlock);
1228
+ const protectedBlocks = collectBlocksForMessages(protectedMessages, messageToBlock);
1229
+ const lockedBlocks = new Set([...systemBlocks, ...protectedBlocks]);
1230
+ const removableBlocks = blocks.filter((block) => !lockedBlocks.has(block));
1231
+ const systemTokens = systemBlocks.reduce((sum, block) => sum + block.tokenCount, 0);
1232
+ const protectedTokens = protectedBlocks.reduce((sum, block) => sum + block.tokenCount, 0);
847
1233
  const availableTokens = maxTokens - systemTokens - protectedTokens;
1234
+ let selectedBlocks = [];
848
1235
  if (availableTokens <= 0) {
849
- const allMessages2 = [...systemMessages, ...protectedMessages];
850
- const renumbered2 = allMessages2.map((message, index) => cloneMessageWithPosition(message, index, copyContent(message.content)));
851
- return toReadonly({
852
- ...conversation,
853
- ids: renumbered2.map((message) => message.id),
854
- messages: toIdRecord(renumbered2),
855
- updatedAt: now
856
- });
857
- }
858
- const keptRemovable = [];
859
- let usedTokens = 0;
860
- for (let i = removableMessages.length - 1;i >= 0; i--) {
861
- const message = removableMessages[i];
862
- const messageTokens = estimator(message);
863
- if (usedTokens + messageTokens <= availableTokens) {
864
- keptRemovable.unshift(message);
865
- usedTokens += messageTokens;
866
- } else {
867
- break;
1236
+ selectedBlocks = [...systemBlocks, ...protectedBlocks];
1237
+ } else {
1238
+ const sortedRemovable = [...removableBlocks].sort((a, b) => a.maxPosition - b.maxPosition);
1239
+ const keptRemovable = [];
1240
+ let usedTokens = 0;
1241
+ for (let i = sortedRemovable.length - 1;i >= 0; i--) {
1242
+ const block = sortedRemovable[i];
1243
+ if (usedTokens + block.tokenCount <= availableTokens) {
1244
+ keptRemovable.unshift(block);
1245
+ usedTokens += block.tokenCount;
1246
+ } else {
1247
+ break;
1248
+ }
868
1249
  }
1250
+ selectedBlocks = [...systemBlocks, ...keptRemovable, ...protectedBlocks];
869
1251
  }
870
- const allMessages = [...systemMessages, ...keptRemovable, ...protectedMessages];
871
- allMessages.sort((a, b) => a.position - b.position);
1252
+ const allMessages = collectMessagesFromBlocks(selectedBlocks);
872
1253
  const renumbered = allMessages.map((message, index) => cloneMessageWithPosition(message, index, copyContent(message.content)));
873
- return toReadonly({
1254
+ const next = toReadonly({
874
1255
  ...conversation,
875
1256
  ids: renumbered.map((message) => message.id),
876
1257
  messages: toIdRecord(renumbered),
877
1258
  updatedAt: now
878
1259
  });
1260
+ return ensureTruncationSafe(next, preserveToolPairs, "truncateToTokenLimit");
879
1261
  }
880
1262
  function getRecentMessages(conversation, count, options) {
881
1263
  const includeHidden = options?.includeHidden ?? false;
882
1264
  const includeSystem = options?.includeSystem ?? false;
1265
+ const preserveToolPairs = options?.preserveToolPairs ?? true;
883
1266
  const filtered = getOrderedMessages(conversation).filter((m) => {
884
1267
  if (!includeHidden && m.hidden)
885
1268
  return false;
@@ -887,23 +1270,35 @@ function getRecentMessages(conversation, count, options) {
887
1270
  return false;
888
1271
  return true;
889
1272
  });
890
- return filtered.slice(-count);
1273
+ if (!preserveToolPairs) {
1274
+ return filtered.slice(-count);
1275
+ }
1276
+ const { messageToBlock } = buildMessageBlocks(filtered, () => 0, preserveToolPairs);
1277
+ const tail = filtered.slice(-count);
1278
+ const blocks = collectBlocksForMessages(tail, messageToBlock);
1279
+ return collectMessagesFromBlocks(blocks);
891
1280
  }
892
1281
  function truncateFromPosition(conversation, position, options, environment) {
1282
+ assertConversationSafe(conversation);
893
1283
  const preserveSystem = options?.preserveSystemMessages ?? true;
1284
+ const preserveToolPairs = options?.preserveToolPairs ?? true;
894
1285
  const resolvedEnvironment = resolveConversationEnvironment(environment);
895
1286
  const now = resolvedEnvironment.now();
896
1287
  const ordered = getOrderedMessages(conversation);
1288
+ const { messageToBlock } = buildMessageBlocks(ordered, () => 0, preserveToolPairs);
897
1289
  const systemMessages = preserveSystem ? ordered.filter((m) => m.role === "system" && m.position < position) : [];
898
1290
  const keptMessages = ordered.filter((m) => m.position >= position);
899
- const allMessages = [...systemMessages, ...keptMessages];
1291
+ const systemBlocks = collectBlocksForMessages(systemMessages, messageToBlock);
1292
+ const keptBlocks = collectBlocksForMessages(keptMessages, messageToBlock);
1293
+ const allMessages = collectMessagesFromBlocks([...systemBlocks, ...keptBlocks]);
900
1294
  const renumbered = allMessages.map((message, index) => cloneMessageWithPosition(message, index, copyContent(message.content)));
901
- return toReadonly({
1295
+ const next = toReadonly({
902
1296
  ...conversation,
903
1297
  ids: renumbered.map((message) => message.id),
904
1298
  messages: toIdRecord(renumbered),
905
1299
  updatedAt: now
906
1300
  });
1301
+ return ensureTruncationSafe(next, preserveToolPairs, "truncateFromPosition");
907
1302
  }
908
1303
 
909
1304
  // src/streaming.ts
@@ -938,7 +1333,8 @@ function getStreamingMessage(conversation) {
938
1333
  return getOrderedMessages(conversation).find(isStreamingMessage);
939
1334
  }
940
1335
  function appendStreamingMessage(conversation, role, metadata, environment) {
941
- const resolvedEnvironment = resolveConversationEnvironment(environment);
1336
+ const resolvedEnvironment = resolveConversationEnvironment(isConversationEnvironmentParameter(metadata) ? metadata : environment);
1337
+ const resolvedMetadata = isConversationEnvironmentParameter(metadata) ? undefined : metadata;
942
1338
  const now = resolvedEnvironment.now();
943
1339
  const messageId = resolvedEnvironment.randomId();
944
1340
  const newMessage = createMessage({
@@ -947,7 +1343,7 @@ function appendStreamingMessage(conversation, role, metadata, environment) {
947
1343
  content: "",
948
1344
  position: conversation.ids.length,
949
1345
  createdAt: now,
950
- metadata: { ...metadata ?? {}, [STREAMING_KEY]: true },
1346
+ metadata: { ...resolvedMetadata ?? {}, [STREAMING_KEY]: true },
951
1347
  hidden: false,
952
1348
  toolCall: undefined,
953
1349
  toolResult: undefined,
@@ -959,14 +1355,14 @@ function appendStreamingMessage(conversation, role, metadata, environment) {
959
1355
  messages: { ...conversation.messages, [messageId]: newMessage },
960
1356
  updatedAt: now
961
1357
  });
962
- return { conversation: updatedConversation, messageId };
1358
+ return { conversation: ensureConversationSafe(updatedConversation), messageId };
963
1359
  }
964
1360
  function updateStreamingMessage(conversation, messageId, content, environment) {
965
1361
  const resolvedEnvironment = resolveConversationEnvironment(environment);
966
1362
  const now = resolvedEnvironment.now();
967
1363
  const original = conversation.messages[messageId];
968
1364
  if (!original) {
969
- return conversation;
1365
+ return ensureConversationSafe(conversation);
970
1366
  }
971
1367
  const overrides = {
972
1368
  content: typeof content === "string" ? content : [...content]
@@ -975,44 +1371,45 @@ function updateStreamingMessage(conversation, messageId, content, environment) {
975
1371
  overrides.tokenUsage = { ...original.tokenUsage };
976
1372
  }
977
1373
  const updated = cloneMessage(original, overrides);
978
- return toReadonly({
1374
+ return ensureConversationSafe(toReadonly({
979
1375
  ...conversation,
980
1376
  ids: [...conversation.ids],
981
1377
  messages: { ...conversation.messages, [updated.id]: updated },
982
1378
  updatedAt: now
983
- });
1379
+ }));
984
1380
  }
985
1381
  function finalizeStreamingMessage(conversation, messageId, options, environment) {
986
- const resolvedEnvironment = resolveConversationEnvironment(environment);
1382
+ const resolvedEnvironment = resolveConversationEnvironment(isConversationEnvironmentParameter(options) ? options : environment);
1383
+ const resolvedOptions = isConversationEnvironmentParameter(options) ? undefined : options;
987
1384
  const now = resolvedEnvironment.now();
988
1385
  const original = conversation.messages[messageId];
989
1386
  if (!original) {
990
- return conversation;
1387
+ return ensureConversationSafe(conversation);
991
1388
  }
992
1389
  const { [STREAMING_KEY]: _, ...restMetadata } = original.metadata;
993
1390
  const finalMetadata = {
994
1391
  ...restMetadata,
995
- ...options?.metadata ?? {}
1392
+ ...resolvedOptions?.metadata ?? {}
996
1393
  };
997
1394
  const finalizeOverrides = {
998
1395
  metadata: finalMetadata
999
1396
  };
1000
- if (options?.tokenUsage) {
1001
- finalizeOverrides.tokenUsage = { ...options.tokenUsage };
1397
+ if (resolvedOptions?.tokenUsage) {
1398
+ finalizeOverrides.tokenUsage = { ...resolvedOptions.tokenUsage };
1002
1399
  }
1003
1400
  const updated = cloneMessage(original, finalizeOverrides);
1004
- return toReadonly({
1401
+ return ensureConversationSafe(toReadonly({
1005
1402
  ...conversation,
1006
1403
  ids: [...conversation.ids],
1007
1404
  messages: { ...conversation.messages, [updated.id]: updated },
1008
1405
  updatedAt: now
1009
- });
1406
+ }));
1010
1407
  }
1011
1408
  function cancelStreamingMessage(conversation, messageId, environment) {
1012
1409
  const resolvedEnvironment = resolveConversationEnvironment(environment);
1013
1410
  const now = resolvedEnvironment.now();
1014
1411
  if (!conversation.messages[messageId]) {
1015
- return conversation;
1412
+ return ensureConversationSafe(conversation);
1016
1413
  }
1017
1414
  const messages = getOrderedMessages(conversation).filter((m) => m.id !== messageId).map((message, index) => message.position === index ? message : (() => {
1018
1415
  const overrides = {
@@ -1023,12 +1420,12 @@ function cancelStreamingMessage(conversation, messageId, environment) {
1023
1420
  }
1024
1421
  return cloneMessage(message, overrides);
1025
1422
  })());
1026
- return toReadonly({
1423
+ return ensureConversationSafe(toReadonly({
1027
1424
  ...conversation,
1028
1425
  ids: messages.map((message) => message.id),
1029
1426
  messages: toIdRecord(messages),
1030
1427
  updatedAt: now
1031
- });
1428
+ }));
1032
1429
  }
1033
1430
 
1034
1431
  // src/with-conversation.ts
@@ -1066,8 +1463,8 @@ function createDraft(initial) {
1066
1463
  current = collapseSystemMessages(current);
1067
1464
  return draft;
1068
1465
  },
1069
- redactMessageAtPosition: (position, placeholder) => {
1070
- current = redactMessageAtPosition(current, position, placeholder);
1466
+ redactMessageAtPosition: (position, placeholderOrOptions) => {
1467
+ current = redactMessageAtPosition(current, position, placeholderOrOptions);
1071
1468
  return draft;
1072
1469
  },
1073
1470
  appendStreamingMessage: (role, metadata) => {
@@ -1099,31 +1496,1539 @@ function createDraft(initial) {
1099
1496
  return draft;
1100
1497
  }
1101
1498
  function withConversation(conversation, fn) {
1102
- const draft = createDraft(conversation);
1499
+ const draft = createDraft(ensureConversationSafe(conversation));
1103
1500
  const maybePromise = fn(draft);
1104
1501
  if (maybePromise && typeof maybePromise === "object" && typeof maybePromise.then === "function") {
1105
- return maybePromise.then(() => draft.value);
1502
+ return maybePromise.then(() => ensureConversationSafe(draft.value));
1106
1503
  }
1107
- return draft.value;
1504
+ return ensureConversationSafe(draft.value);
1108
1505
  }
1109
1506
  function pipeConversation(conversation, ...fns) {
1110
- return fns.reduce((current, fn) => fn(current), conversation);
1507
+ const result = fns.reduce((current, fn) => fn(current), conversation);
1508
+ return ensureConversationSafe(result);
1111
1509
  }
1112
- // src/history.ts
1113
- class ConversationHistoryEvent extends CustomEvent {
1114
- constructor(type, detail) {
1115
- super(type, { detail });
1510
+ // node_modules/event-emission/dist/index.js
1511
+ var SymbolObservable = typeof Symbol === "function" && Symbol.observable || Symbol.for("@@observable");
1512
+ if (typeof Symbol === "function") {
1513
+ Symbol.observable = SymbolObservable;
1514
+ }
1515
+ function getMethod(obj, key) {
1516
+ if (obj === null || obj === undefined)
1517
+ return;
1518
+ const value = obj[key];
1519
+ if (value == null)
1520
+ return;
1521
+ if (typeof value !== "function") {
1522
+ throw new TypeError(value + " is not a function");
1523
+ }
1524
+ return value;
1525
+ }
1526
+ function hostReportError(e) {
1527
+ if (typeof queueMicrotask === "function") {
1528
+ queueMicrotask(() => {
1529
+ throw e;
1530
+ });
1531
+ } else {
1532
+ setTimeout(() => {
1533
+ throw e;
1534
+ });
1535
+ }
1536
+ }
1537
+ function SubscriptionObserverImpl(subscription) {
1538
+ this._subscription = subscription;
1539
+ }
1540
+ SubscriptionObserverImpl.prototype = Object.create(Object.prototype);
1541
+ Object.defineProperties(SubscriptionObserverImpl.prototype, {
1542
+ constructor: { value: Object, configurable: true, writable: true },
1543
+ closed: {
1544
+ get() {
1545
+ return this._subscription._closed;
1546
+ },
1547
+ configurable: true
1548
+ },
1549
+ next: {
1550
+ value: function next(value) {
1551
+ const subscription = this._subscription;
1552
+ if (subscription._closed)
1553
+ return;
1554
+ const observer = subscription._observer;
1555
+ try {
1556
+ const m = getMethod(observer, "next");
1557
+ if (!m)
1558
+ return;
1559
+ return m.call(observer, value);
1560
+ } catch (e) {
1561
+ try {
1562
+ this.error(e);
1563
+ } catch (err) {
1564
+ hostReportError(err);
1565
+ }
1566
+ }
1567
+ },
1568
+ configurable: true,
1569
+ writable: true
1570
+ },
1571
+ error: {
1572
+ value: function error(errorValue) {
1573
+ const subscription = this._subscription;
1574
+ if (subscription._closed)
1575
+ throw errorValue;
1576
+ subscription._closed = true;
1577
+ const observer = subscription._observer;
1578
+ try {
1579
+ const m = getMethod(observer, "error");
1580
+ if (m) {
1581
+ return m.call(observer, errorValue);
1582
+ }
1583
+ throw errorValue;
1584
+ } finally {
1585
+ subscription._cleanup();
1586
+ }
1587
+ },
1588
+ configurable: true,
1589
+ writable: true
1590
+ },
1591
+ complete: {
1592
+ value: function complete(value) {
1593
+ const subscription = this._subscription;
1594
+ if (subscription._closed)
1595
+ return;
1596
+ subscription._closed = true;
1597
+ const observer = subscription._observer;
1598
+ try {
1599
+ const m = getMethod(observer, "complete");
1600
+ if (m) {
1601
+ return m.call(observer, value);
1602
+ }
1603
+ return;
1604
+ } finally {
1605
+ subscription._cleanup();
1606
+ }
1607
+ },
1608
+ configurable: true,
1609
+ writable: true
1610
+ }
1611
+ });
1612
+ Object.defineProperty(SubscriptionObserverImpl.prototype.next, "length", { value: 1 });
1613
+ Object.defineProperty(SubscriptionObserverImpl.prototype.error, "length", { value: 1 });
1614
+ Object.defineProperty(SubscriptionObserverImpl.prototype.complete, "length", {
1615
+ value: 1
1616
+ });
1617
+ function Subscription(observer, subscriber) {
1618
+ this._observer = observer;
1619
+ this._cleanupFn = undefined;
1620
+ this._closed = false;
1621
+ const subscriptionObserver = new SubscriptionObserverImpl(this);
1622
+ try {
1623
+ const start = getMethod(observer, "start");
1624
+ if (start) {
1625
+ start.call(observer, this);
1626
+ }
1627
+ } catch (e) {
1628
+ hostReportError(e);
1629
+ }
1630
+ if (this._closed)
1631
+ return;
1632
+ try {
1633
+ const cleanup = subscriber(subscriptionObserver);
1634
+ if (cleanup != null) {
1635
+ if (typeof cleanup !== "function" && typeof cleanup.unsubscribe !== "function") {
1636
+ throw new TypeError(cleanup + " is not a function or a subscription");
1637
+ }
1638
+ this._cleanupFn = cleanup;
1639
+ if (this._closed) {
1640
+ this._cleanup();
1641
+ }
1642
+ }
1643
+ } catch (e) {
1644
+ subscriptionObserver.error(e);
1116
1645
  }
1117
1646
  }
1647
+ Subscription.prototype = Object.create(Object.prototype);
1648
+ Object.defineProperties(Subscription.prototype, {
1649
+ constructor: { value: Object, configurable: true, writable: true },
1650
+ closed: {
1651
+ get() {
1652
+ return this._closed;
1653
+ },
1654
+ configurable: true
1655
+ },
1656
+ unsubscribe: {
1657
+ value: function unsubscribe() {
1658
+ if (this._closed)
1659
+ return;
1660
+ this._closed = true;
1661
+ this._cleanup();
1662
+ },
1663
+ configurable: true,
1664
+ writable: true
1665
+ },
1666
+ _cleanup: {
1667
+ value: function _cleanup() {
1668
+ const cleanup = this._cleanupFn;
1669
+ if (!cleanup)
1670
+ return;
1671
+ this._cleanupFn = undefined;
1672
+ try {
1673
+ if (typeof cleanup === "function") {
1674
+ cleanup();
1675
+ } else if (cleanup && typeof cleanup.unsubscribe === "function") {
1676
+ cleanup.unsubscribe();
1677
+ }
1678
+ } catch (e) {
1679
+ hostReportError(e);
1680
+ }
1681
+ },
1682
+ configurable: true,
1683
+ writable: true
1684
+ }
1685
+ });
1118
1686
 
1687
+ class Observable {
1688
+ _subscriber;
1689
+ constructor(subscriber) {
1690
+ if (typeof subscriber !== "function") {
1691
+ throw new TypeError("Observable initializer must be a function");
1692
+ }
1693
+ this._subscriber = subscriber;
1694
+ }
1695
+ subscribe(observerOrNext, error2, complete2) {
1696
+ let observer;
1697
+ if (typeof observerOrNext === "function") {
1698
+ observer = {
1699
+ next: observerOrNext,
1700
+ error: error2,
1701
+ complete: complete2
1702
+ };
1703
+ } else if (typeof observerOrNext !== "object" || observerOrNext === null) {
1704
+ throw new TypeError(observerOrNext + " is not an object");
1705
+ } else {
1706
+ observer = observerOrNext;
1707
+ }
1708
+ return new Subscription(observer, this._subscriber);
1709
+ }
1710
+ [SymbolObservable]() {
1711
+ return this;
1712
+ }
1713
+ static of(...items) {
1714
+ const C = typeof this === "function" ? this : Observable;
1715
+ return new C((observer) => {
1716
+ for (let i = 0;i < items.length; ++i) {
1717
+ observer.next(items[i]);
1718
+ if (observer.closed)
1719
+ return;
1720
+ }
1721
+ observer.complete();
1722
+ });
1723
+ }
1724
+ static from(x) {
1725
+ const C = typeof this === "function" ? this : Observable;
1726
+ if (x == null)
1727
+ throw new TypeError(x + " is not an object");
1728
+ const method = x[SymbolObservable];
1729
+ if (method != null) {
1730
+ if (typeof method !== "function") {
1731
+ throw new TypeError(method + " is not a function");
1732
+ }
1733
+ const observable = method.call(x);
1734
+ if (Object(observable) !== observable) {
1735
+ throw new TypeError(observable + " is not an object");
1736
+ }
1737
+ if (observable.constructor === C) {
1738
+ return observable;
1739
+ }
1740
+ return new C((observer) => observable.subscribe(observer));
1741
+ }
1742
+ if (Symbol.iterator in x) {
1743
+ return new C((observer) => {
1744
+ for (const item of x) {
1745
+ observer.next(item);
1746
+ if (observer.closed)
1747
+ return;
1748
+ }
1749
+ observer.complete();
1750
+ });
1751
+ }
1752
+ throw new TypeError(x + " is not observable");
1753
+ }
1754
+ }
1755
+ var PROXY_MARKER = Symbol.for("@lasercat/event-emission/proxy");
1756
+ var ORIGINAL_TARGET = Symbol.for("@lasercat/event-emission/original");
1757
+ var ARRAY_MUTATORS = new Set([
1758
+ "push",
1759
+ "pop",
1760
+ "shift",
1761
+ "unshift",
1762
+ "splice",
1763
+ "sort",
1764
+ "reverse",
1765
+ "fill",
1766
+ "copyWithin"
1767
+ ]);
1768
+ function isProxyable(value) {
1769
+ return value !== null && typeof value === "object" && !isProxied(value) && !(value instanceof Date) && !(value instanceof RegExp) && !(value instanceof Map) && !(value instanceof Set) && !(value instanceof WeakMap) && !(value instanceof WeakSet) && !(value instanceof Promise) && !(value instanceof Error) && !(value instanceof ArrayBuffer) && !ArrayBuffer.isView(value);
1770
+ }
1771
+ function isProxied(value) {
1772
+ return typeof value === "object" && value !== null && value[PROXY_MARKER] === true;
1773
+ }
1774
+ function isArrayMutator(prop) {
1775
+ return typeof prop === "string" && ARRAY_MUTATORS.has(prop);
1776
+ }
1777
+ function cloneAlongPath(obj, path) {
1778
+ const isArray = Array.isArray(obj);
1779
+ const rootClone = isArray ? [...obj] : { ...obj };
1780
+ if (!path || isArray) {
1781
+ return rootClone;
1782
+ }
1783
+ const parts = path.split(".");
1784
+ const result = rootClone;
1785
+ let current = result;
1786
+ for (let i = 0;i < parts.length; i++) {
1787
+ const key = parts[i];
1788
+ const value = current[key];
1789
+ if (value !== null && typeof value === "object") {
1790
+ current[key] = Array.isArray(value) ? [...value] : { ...value };
1791
+ if (i < parts.length - 1) {
1792
+ current = current[key];
1793
+ }
1794
+ } else {
1795
+ break;
1796
+ }
1797
+ }
1798
+ return result;
1799
+ }
1800
+ function cloneForComparison(obj, strategy, changedPath, deepClone) {
1801
+ if (obj === null || typeof obj !== "object")
1802
+ return obj;
1803
+ switch (strategy) {
1804
+ case "shallow":
1805
+ return Array.isArray(obj) ? [...obj] : { ...obj };
1806
+ case "deep":
1807
+ if (deepClone) {
1808
+ return deepClone(obj);
1809
+ }
1810
+ if (typeof structuredClone !== "function") {
1811
+ throw new Error("structuredClone is not available in this runtime; provide observe.deepClone, or use cloneStrategy 'path' or 'shallow'.");
1812
+ }
1813
+ return structuredClone(obj);
1814
+ case "path":
1815
+ return cloneAlongPath(obj, changedPath);
1816
+ default:
1817
+ return obj;
1818
+ }
1819
+ }
1820
+ function computeArrayDiff(method, before, _after, args) {
1821
+ switch (method) {
1822
+ case "push":
1823
+ return { added: args };
1824
+ case "pop":
1825
+ return { removed: before.length > 0 ? [before[before.length - 1]] : [] };
1826
+ case "shift":
1827
+ return { removed: before.length > 0 ? [before[0]] : [] };
1828
+ case "unshift":
1829
+ return { added: args };
1830
+ case "splice": {
1831
+ const [start, deleteCount, ...items] = args;
1832
+ const actualStart = start < 0 ? Math.max(before.length + start, 0) : Math.min(start, before.length);
1833
+ const actualDeleteCount = Math.min(deleteCount ?? before.length - actualStart, before.length - actualStart);
1834
+ return {
1835
+ removed: before.slice(actualStart, actualStart + actualDeleteCount),
1836
+ added: items
1837
+ };
1838
+ }
1839
+ case "sort":
1840
+ case "reverse":
1841
+ case "fill":
1842
+ case "copyWithin":
1843
+ return {};
1844
+ default:
1845
+ return {};
1846
+ }
1847
+ }
1848
+ var proxyRegistry = new WeakMap;
1849
+ function getContextRegistry(target) {
1850
+ let contextMap = proxyRegistry.get(target);
1851
+ if (!contextMap) {
1852
+ contextMap = new WeakMap;
1853
+ proxyRegistry.set(target, contextMap);
1854
+ }
1855
+ return contextMap;
1856
+ }
1857
+ function createArrayMethodInterceptor(array, method, path, context) {
1858
+ const original = array[method];
1859
+ return function(...args) {
1860
+ const previousState = cloneForComparison(context.originalRoot, context.options.cloneStrategy, path, context.options.deepClone);
1861
+ const previousItems = [...array];
1862
+ const result = original.apply(this, args);
1863
+ const { added, removed } = computeArrayDiff(method, previousItems, array, args);
1864
+ const methodEventPath = path ? `update:${path}.${method}` : `update:${method}`;
1865
+ const arrayEventPath = path ? `update:${path}` : "update:";
1866
+ context.eventTarget.dispatchEvent({
1867
+ type: methodEventPath,
1868
+ detail: {
1869
+ method,
1870
+ args,
1871
+ result,
1872
+ added,
1873
+ removed,
1874
+ current: context.originalRoot,
1875
+ previous: previousState
1876
+ }
1877
+ });
1878
+ if (path) {
1879
+ context.eventTarget.dispatchEvent({
1880
+ type: arrayEventPath,
1881
+ detail: {
1882
+ value: array,
1883
+ current: context.originalRoot,
1884
+ previous: previousState
1885
+ }
1886
+ });
1887
+ }
1888
+ context.eventTarget.dispatchEvent({
1889
+ type: "update",
1890
+ detail: {
1891
+ current: context.originalRoot,
1892
+ previous: previousState
1893
+ }
1894
+ });
1895
+ return result;
1896
+ };
1897
+ }
1898
+ function createObservableProxyInternal(target, path, context) {
1899
+ const contextRegistry = getContextRegistry(target);
1900
+ const existing = contextRegistry.get(context);
1901
+ if (existing) {
1902
+ return existing.proxy;
1903
+ }
1904
+ const proxy = new Proxy(target, {
1905
+ get(obj, prop, receiver) {
1906
+ if (prop === PROXY_MARKER)
1907
+ return true;
1908
+ if (prop === ORIGINAL_TARGET)
1909
+ return obj;
1910
+ if (typeof prop === "symbol") {
1911
+ return Reflect.get(obj, prop, receiver);
1912
+ }
1913
+ const value = Reflect.get(obj, prop, receiver);
1914
+ if (Array.isArray(obj) && isArrayMutator(prop)) {
1915
+ return createArrayMethodInterceptor(obj, prop, path, context);
1916
+ }
1917
+ if (context.options.deep && isProxyable(value)) {
1918
+ const nestedPath = path ? `${path}.${prop}` : prop;
1919
+ return createObservableProxyInternal(value, nestedPath, context);
1920
+ }
1921
+ return value;
1922
+ },
1923
+ set(obj, prop, value, receiver) {
1924
+ if (typeof prop === "symbol") {
1925
+ return Reflect.set(obj, prop, value, receiver);
1926
+ }
1927
+ const oldValue = Reflect.get(obj, prop, receiver);
1928
+ if (Object.is(oldValue, value)) {
1929
+ return true;
1930
+ }
1931
+ const propPath = path ? `${path}.${prop}` : prop;
1932
+ const previousState = cloneForComparison(context.originalRoot, context.options.cloneStrategy, propPath, context.options.deepClone);
1933
+ const success = Reflect.set(obj, prop, value, receiver);
1934
+ if (success) {
1935
+ context.eventTarget.dispatchEvent({
1936
+ type: `update:${propPath}`,
1937
+ detail: {
1938
+ value,
1939
+ current: context.originalRoot,
1940
+ previous: previousState
1941
+ }
1942
+ });
1943
+ context.eventTarget.dispatchEvent({
1944
+ type: "update",
1945
+ detail: {
1946
+ current: context.originalRoot,
1947
+ previous: previousState
1948
+ }
1949
+ });
1950
+ }
1951
+ return success;
1952
+ },
1953
+ deleteProperty(obj, prop) {
1954
+ if (typeof prop === "symbol") {
1955
+ return Reflect.deleteProperty(obj, prop);
1956
+ }
1957
+ const propPath = path ? `${path}.${String(prop)}` : String(prop);
1958
+ const previousState = cloneForComparison(context.originalRoot, context.options.cloneStrategy, propPath, context.options.deepClone);
1959
+ const success = Reflect.deleteProperty(obj, prop);
1960
+ if (success) {
1961
+ context.eventTarget.dispatchEvent({
1962
+ type: `update:${propPath}`,
1963
+ detail: {
1964
+ value: undefined,
1965
+ current: context.originalRoot,
1966
+ previous: previousState
1967
+ }
1968
+ });
1969
+ context.eventTarget.dispatchEvent({
1970
+ type: "update",
1971
+ detail: {
1972
+ current: context.originalRoot,
1973
+ previous: previousState
1974
+ }
1975
+ });
1976
+ }
1977
+ return success;
1978
+ }
1979
+ });
1980
+ contextRegistry.set(context, {
1981
+ proxy,
1982
+ path
1983
+ });
1984
+ return proxy;
1985
+ }
1986
+ function isEventTarget(obj) {
1987
+ return typeof obj === "object" && obj !== null && typeof obj.addEventListener === "function" && typeof obj.removeEventListener === "function" && typeof obj.dispatchEvent === "function";
1988
+ }
1989
+ function setupEventForwarding(source, target) {
1990
+ const handlers = new Map;
1991
+ const sourceAddEventListener = source.addEventListener.bind(source);
1992
+ const sourceRemoveEventListener = source.removeEventListener.bind(source);
1993
+ const forwardHandler = (type) => (event) => {
1994
+ const detail = event.detail ?? event;
1995
+ target.dispatchEvent({
1996
+ type,
1997
+ detail
1998
+ });
1999
+ };
2000
+ const originalAddEventListener = target.addEventListener.bind(target);
2001
+ const wrappedAddEventListener = (type, listener, options) => {
2002
+ if (!handlers.has(type) && type !== "update" && !type.startsWith("update:")) {
2003
+ const handler = forwardHandler(type);
2004
+ handlers.set(type, handler);
2005
+ sourceAddEventListener(type, handler);
2006
+ }
2007
+ return originalAddEventListener(type, listener, options);
2008
+ };
2009
+ target.addEventListener = wrappedAddEventListener;
2010
+ return () => {
2011
+ target.addEventListener = originalAddEventListener;
2012
+ for (const [type, handler] of handlers) {
2013
+ sourceRemoveEventListener(type, handler);
2014
+ }
2015
+ handlers.clear();
2016
+ };
2017
+ }
2018
+ function createObservableProxy(target, eventTarget, options) {
2019
+ const resolvedOptions = {
2020
+ deep: options?.deep ?? true,
2021
+ cloneStrategy: options?.cloneStrategy ?? "path",
2022
+ deepClone: options?.deepClone
2023
+ };
2024
+ const context = {
2025
+ eventTarget,
2026
+ originalRoot: target,
2027
+ options: resolvedOptions
2028
+ };
2029
+ const proxy = createObservableProxyInternal(target, "", context);
2030
+ if (isEventTarget(target)) {
2031
+ const cleanupForwarding = setupEventForwarding(target, eventTarget);
2032
+ const maybeComplete = eventTarget.complete;
2033
+ if (typeof maybeComplete === "function") {
2034
+ const originalComplete = maybeComplete.bind(eventTarget);
2035
+ let cleaned = false;
2036
+ eventTarget.complete = () => {
2037
+ if (!cleaned) {
2038
+ cleaned = true;
2039
+ cleanupForwarding();
2040
+ }
2041
+ return originalComplete();
2042
+ };
2043
+ }
2044
+ }
2045
+ return proxy;
2046
+ }
2047
+
2048
+ class BufferOverflowError extends Error {
2049
+ constructor(eventType, bufferSize) {
2050
+ super(`Buffer overflow for event type "${eventType}" (max: ${bufferSize})`);
2051
+ this.name = "BufferOverflowError";
2052
+ }
2053
+ }
2054
+ function matchesWildcard(eventType, pattern) {
2055
+ if (pattern === "*")
2056
+ return true;
2057
+ return pattern.endsWith(":*") && eventType.startsWith(pattern.slice(0, -2) + ":");
2058
+ }
2059
+ var EVENT_STATE = Symbol("event-emission:event-state");
2060
+ function createEventTarget(targetOrOpts, opts) {
2061
+ if (opts?.observe === true && targetOrOpts && typeof targetOrOpts === "object") {
2062
+ const target = targetOrOpts;
2063
+ const eventTarget = createEventTargetInternal({
2064
+ onListenerError: opts.onListenerError
2065
+ });
2066
+ const proxy = createObservableProxy(target, eventTarget, {
2067
+ deep: opts.deep,
2068
+ cloneStrategy: opts.cloneStrategy,
2069
+ deepClone: opts.deepClone
2070
+ });
2071
+ const methodNames = [
2072
+ "addEventListener",
2073
+ "removeEventListener",
2074
+ "dispatchEvent",
2075
+ "clear",
2076
+ "on",
2077
+ "once",
2078
+ "removeAllListeners",
2079
+ "pipe",
2080
+ "addWildcardListener",
2081
+ "removeWildcardListener",
2082
+ "complete",
2083
+ "subscribe",
2084
+ "toObservable",
2085
+ "events"
2086
+ ];
2087
+ for (const name of methodNames) {
2088
+ Object.defineProperty(proxy, name, {
2089
+ value: eventTarget[name],
2090
+ writable: false,
2091
+ enumerable: false,
2092
+ configurable: true
2093
+ });
2094
+ }
2095
+ Object.defineProperty(proxy, "completed", {
2096
+ get: () => eventTarget.completed,
2097
+ enumerable: false,
2098
+ configurable: true
2099
+ });
2100
+ return proxy;
2101
+ }
2102
+ return createEventTargetInternal(targetOrOpts);
2103
+ }
2104
+ function createEventTargetInternal(opts) {
2105
+ const listeners = new Map;
2106
+ const wildcardListeners = new Set;
2107
+ let isCompleted = false;
2108
+ const completionCallbacks = new Set;
2109
+ const now = () => typeof globalThis.performance?.now === "function" ? globalThis.performance.now() : Date.now();
2110
+ const initializeEventState = (state, type, bubbles, cancelable) => {
2111
+ state.initializedFlag = true;
2112
+ state.stopPropagationFlag = false;
2113
+ state.stopImmediatePropagationFlag = false;
2114
+ state.canceledFlag = false;
2115
+ state.isTrusted = false;
2116
+ state.target = null;
2117
+ state.currentTarget = null;
2118
+ state.eventPhase = 0;
2119
+ state.type = type;
2120
+ state.bubbles = bubbles;
2121
+ state.cancelable = cancelable;
2122
+ };
2123
+ const setCanceledFlag = (state) => {
2124
+ if (state.cancelable && !state.inPassiveListenerFlag) {
2125
+ state.canceledFlag = true;
2126
+ }
2127
+ };
2128
+ const createEvent = (type, detail, init) => {
2129
+ const state = {
2130
+ dispatchFlag: false,
2131
+ initializedFlag: true,
2132
+ stopPropagationFlag: false,
2133
+ stopImmediatePropagationFlag: false,
2134
+ canceledFlag: false,
2135
+ inPassiveListenerFlag: false,
2136
+ composedFlag: Boolean(init?.composed),
2137
+ eventPhase: init?.eventPhase ?? 0,
2138
+ currentTarget: init?.currentTarget ?? init?.target ?? null,
2139
+ target: init?.target ?? null,
2140
+ timeStamp: init?.timeStamp ?? now(),
2141
+ path: [],
2142
+ type,
2143
+ bubbles: Boolean(init?.bubbles),
2144
+ cancelable: Boolean(init?.cancelable),
2145
+ isTrusted: false
2146
+ };
2147
+ const event = { detail };
2148
+ Object.defineProperties(event, {
2149
+ type: {
2150
+ get: () => state.type,
2151
+ enumerable: true,
2152
+ configurable: true
2153
+ },
2154
+ bubbles: {
2155
+ get: () => state.bubbles,
2156
+ enumerable: true,
2157
+ configurable: true
2158
+ },
2159
+ cancelable: {
2160
+ get: () => state.cancelable,
2161
+ enumerable: true,
2162
+ configurable: true
2163
+ },
2164
+ cancelBubble: {
2165
+ get: () => state.stopPropagationFlag,
2166
+ set: (value) => {
2167
+ if (value)
2168
+ state.stopPropagationFlag = true;
2169
+ },
2170
+ enumerable: true,
2171
+ configurable: true
2172
+ },
2173
+ composed: {
2174
+ get: () => state.composedFlag,
2175
+ enumerable: true,
2176
+ configurable: true
2177
+ },
2178
+ currentTarget: {
2179
+ get: () => state.currentTarget,
2180
+ enumerable: true,
2181
+ configurable: true
2182
+ },
2183
+ defaultPrevented: {
2184
+ get: () => state.canceledFlag,
2185
+ enumerable: true,
2186
+ configurable: true
2187
+ },
2188
+ eventPhase: {
2189
+ get: () => state.eventPhase,
2190
+ enumerable: true,
2191
+ configurable: true
2192
+ },
2193
+ isTrusted: {
2194
+ get: () => state.isTrusted,
2195
+ enumerable: true,
2196
+ configurable: true
2197
+ },
2198
+ returnValue: {
2199
+ get: () => !state.canceledFlag,
2200
+ set: (value) => {
2201
+ if (value === false)
2202
+ setCanceledFlag(state);
2203
+ },
2204
+ enumerable: true,
2205
+ configurable: true
2206
+ },
2207
+ srcElement: {
2208
+ get: () => state.target,
2209
+ enumerable: true,
2210
+ configurable: true
2211
+ },
2212
+ target: {
2213
+ get: () => state.target,
2214
+ enumerable: true,
2215
+ configurable: true
2216
+ },
2217
+ timeStamp: {
2218
+ get: () => state.timeStamp,
2219
+ enumerable: true,
2220
+ configurable: true
2221
+ },
2222
+ composedPath: {
2223
+ value: () => state.path.map((entry) => entry.invocationTarget),
2224
+ enumerable: true,
2225
+ configurable: true
2226
+ },
2227
+ initEvent: {
2228
+ value: (newType, bubbles = false, cancelable = false) => {
2229
+ if (state.dispatchFlag)
2230
+ return;
2231
+ initializeEventState(state, newType, Boolean(bubbles), Boolean(cancelable));
2232
+ },
2233
+ enumerable: true,
2234
+ configurable: true
2235
+ },
2236
+ preventDefault: {
2237
+ value: () => setCanceledFlag(state),
2238
+ enumerable: true,
2239
+ configurable: true
2240
+ },
2241
+ stopImmediatePropagation: {
2242
+ value: () => {
2243
+ state.stopPropagationFlag = true;
2244
+ state.stopImmediatePropagationFlag = true;
2245
+ },
2246
+ enumerable: true,
2247
+ configurable: true
2248
+ },
2249
+ stopPropagation: {
2250
+ value: () => {
2251
+ state.stopPropagationFlag = true;
2252
+ },
2253
+ enumerable: true,
2254
+ configurable: true
2255
+ },
2256
+ NONE: { value: 0, enumerable: true, configurable: true },
2257
+ CAPTURING_PHASE: { value: 1, enumerable: true, configurable: true },
2258
+ AT_TARGET: { value: 2, enumerable: true, configurable: true },
2259
+ BUBBLING_PHASE: { value: 3, enumerable: true, configurable: true },
2260
+ [EVENT_STATE]: {
2261
+ value: state,
2262
+ enumerable: false,
2263
+ configurable: false
2264
+ }
2265
+ });
2266
+ return event;
2267
+ };
2268
+ const getEventState = (event) => event[EVENT_STATE];
2269
+ const normalizeAddListenerOptions = (options) => {
2270
+ if (typeof options === "boolean") {
2271
+ return {
2272
+ capture: options,
2273
+ passive: false,
2274
+ once: false,
2275
+ signal: null
2276
+ };
2277
+ }
2278
+ return {
2279
+ capture: Boolean(options?.capture),
2280
+ passive: Boolean(options?.passive),
2281
+ once: Boolean(options?.once),
2282
+ signal: options?.signal ?? null
2283
+ };
2284
+ };
2285
+ const normalizeCaptureOption = (options) => {
2286
+ if (typeof options === "boolean")
2287
+ return options;
2288
+ return Boolean(options?.capture);
2289
+ };
2290
+ const removeListenerRecord = (type, record) => {
2291
+ if (record.removed)
2292
+ return;
2293
+ record.removed = true;
2294
+ const list = listeners.get(type);
2295
+ if (list) {
2296
+ const idx = list.indexOf(record);
2297
+ if (idx >= 0)
2298
+ list.splice(idx, 1);
2299
+ if (list.length === 0)
2300
+ listeners.delete(type);
2301
+ }
2302
+ if (record.signal && record.abortHandler) {
2303
+ record.signal.removeEventListener("abort", record.abortHandler);
2304
+ }
2305
+ };
2306
+ const handleListenerError = (eventType, error2) => {
2307
+ if (eventType === "error")
2308
+ return;
2309
+ if (opts?.onListenerError) {
2310
+ opts.onListenerError(eventType, error2);
2311
+ return;
2312
+ }
2313
+ const errorListeners = listeners.get("error");
2314
+ if (errorListeners && errorListeners.length > 0) {
2315
+ dispatchEvent({ type: "error", detail: error2 });
2316
+ } else {
2317
+ throw error2;
2318
+ }
2319
+ };
2320
+ const notifyWildcardListeners = (eventType, event) => {
2321
+ if (wildcardListeners.size === 0)
2322
+ return;
2323
+ for (const rec of Array.from(wildcardListeners)) {
2324
+ if (!matchesWildcard(eventType, rec.pattern))
2325
+ continue;
2326
+ const baseEvent = createEvent(rec.pattern, event.detail, {
2327
+ target,
2328
+ currentTarget: target,
2329
+ eventPhase: 2,
2330
+ bubbles: event.bubbles,
2331
+ cancelable: event.cancelable,
2332
+ composed: event.composed
2333
+ });
2334
+ const wildcardEvent = Object.defineProperties(baseEvent, {
2335
+ originalType: { value: eventType, enumerable: true, configurable: true }
2336
+ });
2337
+ try {
2338
+ const fn = rec.fn;
2339
+ const res = fn(wildcardEvent);
2340
+ if (res && typeof res.then === "function") {
2341
+ res.catch((error2) => {
2342
+ try {
2343
+ handleListenerError(eventType, error2);
2344
+ } catch (rethrown) {
2345
+ queueMicrotask(() => {
2346
+ throw rethrown;
2347
+ });
2348
+ }
2349
+ });
2350
+ }
2351
+ } catch (error2) {
2352
+ handleListenerError(eventType, error2);
2353
+ } finally {
2354
+ if (rec.once)
2355
+ wildcardListeners.delete(rec);
2356
+ }
2357
+ const state = getEventState(wildcardEvent);
2358
+ if (state?.stopImmediatePropagationFlag || state?.stopPropagationFlag) {
2359
+ break;
2360
+ }
2361
+ }
2362
+ };
2363
+ const addEventListener = (type, listener, options) => {
2364
+ if (isCompleted || !listener) {
2365
+ return () => {};
2366
+ }
2367
+ const { capture, passive, once, signal } = normalizeAddListenerOptions(options);
2368
+ let list = listeners.get(type);
2369
+ if (!list) {
2370
+ list = [];
2371
+ listeners.set(type, list);
2372
+ }
2373
+ for (const existing of list) {
2374
+ if (existing.original === listener && existing.capture === capture) {
2375
+ return () => removeEventListener(type, listener, options);
2376
+ }
2377
+ }
2378
+ const original = listener;
2379
+ const callback = typeof listener === "function" ? listener : (event) => listener.handleEvent(event);
2380
+ const record = {
2381
+ type,
2382
+ original,
2383
+ callback,
2384
+ capture,
2385
+ passive,
2386
+ once,
2387
+ signal,
2388
+ removed: false
2389
+ };
2390
+ list.push(record);
2391
+ const unsubscribe2 = () => removeListenerRecord(type, record);
2392
+ if (signal) {
2393
+ const onAbort = () => removeListenerRecord(type, record);
2394
+ record.abortHandler = onAbort;
2395
+ signal.addEventListener("abort", onAbort, { once: true });
2396
+ if (signal.aborted)
2397
+ onAbort();
2398
+ }
2399
+ return unsubscribe2;
2400
+ };
2401
+ const addWildcardListener = (pattern, listener, options) => {
2402
+ if (isCompleted)
2403
+ return () => {};
2404
+ const opts2 = options ?? {};
2405
+ for (const existing of wildcardListeners) {
2406
+ if (existing.pattern === pattern && existing.fn === listener) {
2407
+ return () => removeWildcardListener(pattern, listener);
2408
+ }
2409
+ }
2410
+ const record = {
2411
+ fn: listener,
2412
+ pattern,
2413
+ once: opts2.once,
2414
+ signal: opts2.signal
2415
+ };
2416
+ wildcardListeners.add(record);
2417
+ const unsubscribe2 = () => {
2418
+ wildcardListeners.delete(record);
2419
+ if (record.signal && record.abortHandler) {
2420
+ record.signal.removeEventListener("abort", record.abortHandler);
2421
+ }
2422
+ };
2423
+ if (opts2.signal) {
2424
+ const onAbort = () => unsubscribe2();
2425
+ record.abortHandler = onAbort;
2426
+ opts2.signal.addEventListener("abort", onAbort, { once: true });
2427
+ if (opts2.signal.aborted)
2428
+ onAbort();
2429
+ }
2430
+ return unsubscribe2;
2431
+ };
2432
+ const removeWildcardListener = (pattern, listener) => {
2433
+ for (const record of Array.from(wildcardListeners)) {
2434
+ if (record.pattern === pattern && record.fn === listener) {
2435
+ wildcardListeners.delete(record);
2436
+ if (record.signal && record.abortHandler) {
2437
+ record.signal.removeEventListener("abort", record.abortHandler);
2438
+ }
2439
+ }
2440
+ }
2441
+ };
2442
+ const invokeListeners = (eventType, event, phase, listenersSnapshot) => {
2443
+ const state = getEventState(event);
2444
+ if (!state || state.stopPropagationFlag)
2445
+ return;
2446
+ state.currentTarget = target;
2447
+ state.target = target;
2448
+ state.eventPhase = event.AT_TARGET;
2449
+ for (const rec of listenersSnapshot) {
2450
+ if (rec.removed)
2451
+ continue;
2452
+ if (phase === "capturing" && !rec.capture)
2453
+ continue;
2454
+ if (phase === "bubbling" && rec.capture)
2455
+ continue;
2456
+ if (rec.once)
2457
+ removeListenerRecord(rec.type, rec);
2458
+ if (rec.passive)
2459
+ state.inPassiveListenerFlag = true;
2460
+ try {
2461
+ const res = rec.callback.call(state.currentTarget, event);
2462
+ if (res && typeof res.then === "function") {
2463
+ res.catch((error2) => {
2464
+ try {
2465
+ handleListenerError(eventType, error2);
2466
+ } catch (rethrown) {
2467
+ queueMicrotask(() => {
2468
+ throw rethrown;
2469
+ });
2470
+ }
2471
+ });
2472
+ }
2473
+ } catch (error2) {
2474
+ handleListenerError(eventType, error2);
2475
+ } finally {
2476
+ if (rec.passive)
2477
+ state.inPassiveListenerFlag = false;
2478
+ }
2479
+ if (state.stopImmediatePropagationFlag)
2480
+ break;
2481
+ }
2482
+ };
2483
+ const dispatchEvent = (eventInput) => {
2484
+ if (isCompleted)
2485
+ return false;
2486
+ let event;
2487
+ let state;
2488
+ if (eventInput && typeof eventInput === "object") {
2489
+ state = getEventState(eventInput);
2490
+ if (state) {
2491
+ event = eventInput;
2492
+ } else {
2493
+ const input = eventInput;
2494
+ if (typeof input.type !== "string") {
2495
+ throw new TypeError("Event type must be a string");
2496
+ }
2497
+ event = createEvent(input.type, input.detail, {
2498
+ bubbles: input.bubbles,
2499
+ cancelable: input.cancelable,
2500
+ composed: input.composed,
2501
+ timeStamp: input.timeStamp
2502
+ });
2503
+ state = getEventState(event);
2504
+ }
2505
+ } else {
2506
+ throw new TypeError("dispatchEvent expects an event object");
2507
+ }
2508
+ const dispatchState = state ?? getEventState(event);
2509
+ if (dispatchState.dispatchFlag || !dispatchState.initializedFlag) {
2510
+ const message = "Failed to execute dispatchEvent: event is already being dispatched";
2511
+ if (typeof globalThis.DOMException === "function") {
2512
+ throw new globalThis.DOMException(message, "InvalidStateError");
2513
+ }
2514
+ const err = new Error(message);
2515
+ err.name = "InvalidStateError";
2516
+ throw err;
2517
+ }
2518
+ dispatchState.isTrusted = false;
2519
+ dispatchState.dispatchFlag = true;
2520
+ dispatchState.path = [
2521
+ {
2522
+ invocationTarget: target,
2523
+ invocationTargetInShadowTree: false,
2524
+ shadowAdjustedTarget: target,
2525
+ relatedTarget: null,
2526
+ touchTargets: [],
2527
+ rootOfClosedTree: false,
2528
+ slotInClosedTree: false
2529
+ }
2530
+ ];
2531
+ notifyWildcardListeners(dispatchState.type, event);
2532
+ const list = listeners.get(dispatchState.type);
2533
+ const snapshot = list ? list.slice() : [];
2534
+ invokeListeners(dispatchState.type, event, "capturing", snapshot);
2535
+ invokeListeners(dispatchState.type, event, "bubbling", snapshot);
2536
+ dispatchState.eventPhase = event.NONE;
2537
+ dispatchState.currentTarget = null;
2538
+ dispatchState.path = [];
2539
+ dispatchState.dispatchFlag = false;
2540
+ dispatchState.stopPropagationFlag = false;
2541
+ dispatchState.stopImmediatePropagationFlag = false;
2542
+ return !dispatchState.canceledFlag;
2543
+ };
2544
+ const removeEventListener = (type, listener, options) => {
2545
+ if (!listener)
2546
+ return;
2547
+ const capture = normalizeCaptureOption(options);
2548
+ const list = listeners.get(type);
2549
+ if (!list)
2550
+ return;
2551
+ for (const record of [...list]) {
2552
+ if (record.original === listener && record.capture === capture) {
2553
+ removeListenerRecord(type, record);
2554
+ }
2555
+ }
2556
+ };
2557
+ const clear = () => {
2558
+ for (const [type, list] of Array.from(listeners.entries())) {
2559
+ for (const record of [...list]) {
2560
+ removeListenerRecord(type, record);
2561
+ }
2562
+ }
2563
+ listeners.clear();
2564
+ for (const record of wildcardListeners) {
2565
+ if (record.signal && record.abortHandler) {
2566
+ record.signal.removeEventListener("abort", record.abortHandler);
2567
+ }
2568
+ }
2569
+ wildcardListeners.clear();
2570
+ };
2571
+ const on = (type, options) => {
2572
+ return new Observable((observer) => {
2573
+ let opts2;
2574
+ if (typeof options === "boolean") {
2575
+ opts2 = { capture: options };
2576
+ } else {
2577
+ opts2 = options ?? {};
2578
+ }
2579
+ const handler = opts2.handler;
2580
+ const once = opts2.once;
2581
+ const eventHandler = (e) => {
2582
+ let success = false;
2583
+ try {
2584
+ if (handler) {
2585
+ handler(e);
2586
+ }
2587
+ observer.next(e);
2588
+ success = true;
2589
+ } finally {
2590
+ if (once && success) {
2591
+ observer.complete();
2592
+ }
2593
+ }
2594
+ };
2595
+ const errorHandler = (e) => {
2596
+ observer.error(e.detail);
2597
+ };
2598
+ const unsubscribeEvent = addEventListener(type, eventHandler, opts2);
2599
+ let unsubscribeError;
2600
+ if (opts2.receiveError) {
2601
+ unsubscribeError = addEventListener("error", errorHandler, opts2);
2602
+ }
2603
+ return () => {
2604
+ unsubscribeEvent();
2605
+ if (unsubscribeError) {
2606
+ unsubscribeError();
2607
+ }
2608
+ };
2609
+ });
2610
+ };
2611
+ const onceMethod = (type, listener, options) => {
2612
+ if (typeof options === "boolean") {
2613
+ return addEventListener(type, listener, { capture: options, once: true });
2614
+ }
2615
+ return addEventListener(type, listener, { ...options ?? {}, once: true });
2616
+ };
2617
+ const removeAllListeners = (type) => {
2618
+ if (type !== undefined) {
2619
+ const list = listeners.get(type);
2620
+ if (list) {
2621
+ for (const record of [...list]) {
2622
+ removeListenerRecord(type, record);
2623
+ }
2624
+ listeners.delete(type);
2625
+ }
2626
+ } else {
2627
+ for (const [eventType, list] of Array.from(listeners.entries())) {
2628
+ for (const record of [...list]) {
2629
+ removeListenerRecord(eventType, record);
2630
+ }
2631
+ }
2632
+ listeners.clear();
2633
+ for (const record of wildcardListeners) {
2634
+ if (record.signal && record.abortHandler) {
2635
+ record.signal.removeEventListener("abort", record.abortHandler);
2636
+ }
2637
+ }
2638
+ wildcardListeners.clear();
2639
+ }
2640
+ };
2641
+ const pipe = (target2, mapFn) => {
2642
+ if (isCompleted) {
2643
+ return () => {};
2644
+ }
2645
+ const unsubscribe2 = addWildcardListener("*", (event) => {
2646
+ if (mapFn) {
2647
+ const mapped = mapFn(createEvent(event.originalType, event.detail, {
2648
+ target: target2,
2649
+ currentTarget: target2,
2650
+ eventPhase: 2,
2651
+ bubbles: event.bubbles,
2652
+ cancelable: event.cancelable,
2653
+ composed: event.composed
2654
+ }));
2655
+ if (mapped !== null) {
2656
+ target2.dispatchEvent(mapped);
2657
+ }
2658
+ } else {
2659
+ target2.dispatchEvent({
2660
+ type: event.originalType,
2661
+ detail: event.detail
2662
+ });
2663
+ }
2664
+ });
2665
+ const completionUnsub = () => {
2666
+ unsubscribe2();
2667
+ };
2668
+ completionCallbacks.add(completionUnsub);
2669
+ return () => {
2670
+ completionCallbacks.delete(completionUnsub);
2671
+ unsubscribe2();
2672
+ };
2673
+ };
2674
+ const complete2 = () => {
2675
+ if (isCompleted)
2676
+ return;
2677
+ isCompleted = true;
2678
+ for (const cb of completionCallbacks) {
2679
+ try {
2680
+ cb();
2681
+ } catch (err) {
2682
+ try {
2683
+ handleListenerError("complete", err);
2684
+ } catch {}
2685
+ }
2686
+ }
2687
+ completionCallbacks.clear();
2688
+ for (const [eventType, list] of Array.from(listeners.entries())) {
2689
+ for (const record of [...list]) {
2690
+ removeListenerRecord(eventType, record);
2691
+ }
2692
+ }
2693
+ listeners.clear();
2694
+ for (const record of wildcardListeners) {
2695
+ if (record.signal && record.abortHandler) {
2696
+ record.signal.removeEventListener("abort", record.abortHandler);
2697
+ }
2698
+ }
2699
+ wildcardListeners.clear();
2700
+ };
2701
+ const subscribe = (type, observerOrNext, error2, completeHandler) => {
2702
+ let observer;
2703
+ if (typeof observerOrNext === "function") {
2704
+ observer = {
2705
+ next: observerOrNext,
2706
+ error: error2,
2707
+ complete: completeHandler
2708
+ };
2709
+ } else {
2710
+ observer = observerOrNext ?? {};
2711
+ }
2712
+ let closed = false;
2713
+ if (isCompleted) {
2714
+ if (observer.complete) {
2715
+ try {
2716
+ observer.complete();
2717
+ } catch {}
2718
+ }
2719
+ return {
2720
+ unsubscribe: () => {
2721
+ closed = true;
2722
+ },
2723
+ get closed() {
2724
+ return closed || isCompleted;
2725
+ }
2726
+ };
2727
+ }
2728
+ const unsub = addEventListener(type, (event) => {
2729
+ if (closed)
2730
+ return;
2731
+ if (observer.next) {
2732
+ try {
2733
+ observer.next(event);
2734
+ } catch (err) {
2735
+ if (observer.error) {
2736
+ try {
2737
+ observer.error(err);
2738
+ } catch {}
2739
+ }
2740
+ }
2741
+ }
2742
+ });
2743
+ const onComplete = () => {
2744
+ if (closed)
2745
+ return;
2746
+ closed = true;
2747
+ if (observer.complete) {
2748
+ try {
2749
+ observer.complete();
2750
+ } catch {}
2751
+ }
2752
+ };
2753
+ completionCallbacks.add(onComplete);
2754
+ return {
2755
+ unsubscribe: () => {
2756
+ if (closed)
2757
+ return;
2758
+ closed = true;
2759
+ completionCallbacks.delete(onComplete);
2760
+ unsub();
2761
+ },
2762
+ get closed() {
2763
+ return closed || isCompleted;
2764
+ }
2765
+ };
2766
+ };
2767
+ const toObservable = () => {
2768
+ return new Observable((observer) => {
2769
+ if (isCompleted) {
2770
+ observer.complete();
2771
+ return;
2772
+ }
2773
+ const wildcardListener = (event) => {
2774
+ observer.next(createEvent(event.originalType, event.detail, {
2775
+ target,
2776
+ currentTarget: target,
2777
+ eventPhase: 2,
2778
+ bubbles: event.bubbles,
2779
+ cancelable: event.cancelable,
2780
+ composed: event.composed
2781
+ }));
2782
+ };
2783
+ const unsubscribe2 = addWildcardListener("*", wildcardListener);
2784
+ const onComplete = () => {
2785
+ observer.complete();
2786
+ };
2787
+ completionCallbacks.add(onComplete);
2788
+ return () => {
2789
+ unsubscribe2();
2790
+ completionCallbacks.delete(onComplete);
2791
+ };
2792
+ });
2793
+ };
2794
+ function events(type, options) {
2795
+ if (isCompleted) {
2796
+ return {
2797
+ [Symbol.asyncIterator]() {
2798
+ return this;
2799
+ },
2800
+ next() {
2801
+ return Promise.resolve({
2802
+ value: undefined,
2803
+ done: true
2804
+ });
2805
+ },
2806
+ return() {
2807
+ return Promise.resolve({
2808
+ value: undefined,
2809
+ done: true
2810
+ });
2811
+ }
2812
+ };
2813
+ }
2814
+ const signal = options?.signal;
2815
+ const bufferSize = options?.bufferSize ?? Infinity;
2816
+ const overflowStrategy = options?.overflowStrategy ?? "drop-oldest";
2817
+ const buffer = [];
2818
+ let resolve = null;
2819
+ let done = false;
2820
+ let hasOverflow = false;
2821
+ let onAbort = null;
2822
+ const cleanupAbortListener = () => {
2823
+ if (signal && onAbort) {
2824
+ signal.removeEventListener("abort", onAbort);
2825
+ }
2826
+ };
2827
+ const unsub = addEventListener(type, (event) => {
2828
+ if (done)
2829
+ return;
2830
+ const typedEvent = event;
2831
+ if (resolve) {
2832
+ const r = resolve;
2833
+ resolve = null;
2834
+ r({ value: typedEvent, done: false });
2835
+ } else {
2836
+ if (buffer.length >= bufferSize && bufferSize !== Infinity) {
2837
+ switch (overflowStrategy) {
2838
+ case "drop-oldest":
2839
+ buffer.shift();
2840
+ buffer.push(typedEvent);
2841
+ break;
2842
+ case "drop-latest":
2843
+ break;
2844
+ case "throw":
2845
+ unsub();
2846
+ completionCallbacks.delete(onComplete);
2847
+ done = true;
2848
+ hasOverflow = true;
2849
+ cleanupAbortListener();
2850
+ return;
2851
+ }
2852
+ } else {
2853
+ buffer.push(typedEvent);
2854
+ }
2855
+ }
2856
+ });
2857
+ const onComplete = () => {
2858
+ done = true;
2859
+ cleanupAbortListener();
2860
+ if (resolve) {
2861
+ const r = resolve;
2862
+ resolve = null;
2863
+ r({ value: undefined, done: true });
2864
+ }
2865
+ };
2866
+ completionCallbacks.add(onComplete);
2867
+ if (signal) {
2868
+ onAbort = () => {
2869
+ done = true;
2870
+ completionCallbacks.delete(onComplete);
2871
+ unsub();
2872
+ if (resolve) {
2873
+ const r = resolve;
2874
+ resolve = null;
2875
+ r({ value: undefined, done: true });
2876
+ }
2877
+ };
2878
+ signal.addEventListener("abort", onAbort, { once: true });
2879
+ if (signal.aborted)
2880
+ onAbort();
2881
+ }
2882
+ const iterator = {
2883
+ [Symbol.asyncIterator]() {
2884
+ return this;
2885
+ },
2886
+ async next() {
2887
+ if (buffer.length > 0) {
2888
+ return { value: buffer.shift(), done: false };
2889
+ }
2890
+ if (resolve !== null) {
2891
+ return Promise.reject(new Error("Concurrent calls to next() are not supported on this async iterator"));
2892
+ }
2893
+ return new Promise((_resolve, _reject) => {
2894
+ if (hasOverflow) {
2895
+ hasOverflow = false;
2896
+ _reject(new BufferOverflowError(type, bufferSize));
2897
+ return;
2898
+ }
2899
+ if (done) {
2900
+ _resolve({
2901
+ value: undefined,
2902
+ done: true
2903
+ });
2904
+ return;
2905
+ }
2906
+ resolve = _resolve;
2907
+ });
2908
+ },
2909
+ return() {
2910
+ if (resolve) {
2911
+ const r = resolve;
2912
+ resolve = null;
2913
+ r({ value: undefined, done: true });
2914
+ }
2915
+ done = true;
2916
+ completionCallbacks.delete(onComplete);
2917
+ unsub();
2918
+ cleanupAbortListener();
2919
+ return Promise.resolve({
2920
+ value: undefined,
2921
+ done: true
2922
+ });
2923
+ }
2924
+ };
2925
+ return iterator;
2926
+ }
2927
+ const target = {
2928
+ addEventListener,
2929
+ removeEventListener,
2930
+ dispatchEvent,
2931
+ clear,
2932
+ on,
2933
+ once: onceMethod,
2934
+ removeAllListeners,
2935
+ pipe,
2936
+ addWildcardListener,
2937
+ removeWildcardListener,
2938
+ complete: complete2,
2939
+ get completed() {
2940
+ return isCompleted;
2941
+ },
2942
+ subscribe,
2943
+ toObservable,
2944
+ events
2945
+ };
2946
+ target[SymbolObservable] = () => {
2947
+ return toObservable();
2948
+ };
2949
+ return target;
2950
+ }
2951
+
2952
+ class EventEmission {
2953
+ #target;
2954
+ constructor() {
2955
+ this.#target = createEventTarget();
2956
+ }
2957
+ addEventListener(type, listener, options) {
2958
+ return this.#target.addEventListener(type, listener, options);
2959
+ }
2960
+ removeEventListener(type, listener, options) {
2961
+ this.#target.removeEventListener(type, listener, options);
2962
+ }
2963
+ dispatchEvent(event) {
2964
+ return this.#target.dispatchEvent(event);
2965
+ }
2966
+ on(type, options) {
2967
+ return this.#target.on(type, options);
2968
+ }
2969
+ once(type, listener, options) {
2970
+ return this.#target.once(type, listener, options);
2971
+ }
2972
+ removeAllListeners(type) {
2973
+ this.#target.removeAllListeners(type);
2974
+ }
2975
+ clear() {
2976
+ this.#target.clear();
2977
+ }
2978
+ pipe(target, mapFn) {
2979
+ return this.#target.pipe(target, mapFn);
2980
+ }
2981
+ addWildcardListener(pattern, listener, options) {
2982
+ return this.#target.addWildcardListener(pattern, listener, options);
2983
+ }
2984
+ removeWildcardListener(pattern, listener) {
2985
+ this.#target.removeWildcardListener(pattern, listener);
2986
+ }
2987
+ subscribe(typeOrObserver, observerOrNext, error2, completeHandler) {
2988
+ if (typeof typeOrObserver === "string") {
2989
+ return this.#target.subscribe(typeOrObserver, observerOrNext, error2, completeHandler);
2990
+ }
2991
+ if (typeof typeOrObserver === "function") {
2992
+ return this.#target.toObservable().subscribe(typeOrObserver);
2993
+ }
2994
+ if (typeof typeOrObserver === "object" && typeOrObserver !== null) {
2995
+ const maybeObserver = typeOrObserver;
2996
+ if (typeof maybeObserver.next === "function" || typeof maybeObserver.error === "function" || typeof maybeObserver.complete === "function") {
2997
+ return this.#target.toObservable().subscribe(typeOrObserver);
2998
+ }
2999
+ return this.#target.toObservable().subscribe({});
3000
+ }
3001
+ throw new Error("subscribe() requires a string event type, callback function, or observer object");
3002
+ }
3003
+ toObservable() {
3004
+ return this.#target.toObservable();
3005
+ }
3006
+ [SymbolObservable]() {
3007
+ return this.toObservable();
3008
+ }
3009
+ complete() {
3010
+ this.#target.complete();
3011
+ }
3012
+ get completed() {
3013
+ return this.#target.completed;
3014
+ }
3015
+ events(type, options) {
3016
+ return this.#target.events(type, options);
3017
+ }
3018
+ }
3019
+
3020
+ // src/history.ts
1119
3021
  class ConversationHistory extends EventTarget {
1120
3022
  currentNode;
1121
3023
  environment;
3024
+ events;
1122
3025
  constructor(initial = createConversation(), environment) {
1123
3026
  super();
1124
3027
  this.environment = resolveConversationEnvironment(environment);
3028
+ this.events = createEventTarget();
3029
+ const safeInitial = ensureConversationSafe(initial);
1125
3030
  this.currentNode = {
1126
- conversation: initial,
3031
+ conversation: safeInitial,
1127
3032
  parent: null,
1128
3033
  children: []
1129
3034
  };
@@ -1133,25 +3038,54 @@ class ConversationHistory extends EventTarget {
1133
3038
  type,
1134
3039
  conversation: this.current
1135
3040
  };
1136
- this.dispatchEvent(new ConversationHistoryEvent("change", detail));
1137
- this.dispatchEvent(new ConversationHistoryEvent(type, detail));
3041
+ this.events.dispatchEvent({ type: "change", detail });
3042
+ this.events.dispatchEvent({ type, detail });
3043
+ }
3044
+ toAddListenerOptions(options) {
3045
+ if (typeof options === "boolean" || options === undefined)
3046
+ return options;
3047
+ const mapped = {};
3048
+ if (options.capture !== undefined)
3049
+ mapped.capture = options.capture;
3050
+ if (options.once !== undefined)
3051
+ mapped.once = options.once;
3052
+ if (options.passive !== undefined)
3053
+ mapped.passive = options.passive;
3054
+ if (options.signal !== undefined) {
3055
+ mapped.signal = options.signal;
3056
+ }
3057
+ return mapped;
3058
+ }
3059
+ toRemoveListenerOptions(options) {
3060
+ if (typeof options === "boolean" || options === undefined)
3061
+ return options;
3062
+ const mapped = {};
3063
+ if (options.capture !== undefined)
3064
+ mapped.capture = options.capture;
3065
+ return mapped;
1138
3066
  }
1139
3067
  addEventListener(type, callback, options) {
1140
3068
  if (!callback)
1141
3069
  return;
1142
- super.addEventListener(type, callback, options);
1143
- const unsubscribe = () => this.removeEventListener(type, callback, options);
1144
- return unsubscribe;
3070
+ return this.events.addEventListener(type, callback, this.toAddListenerOptions(options));
3071
+ }
3072
+ removeEventListener(type, callback, options) {
3073
+ if (!callback)
3074
+ return;
3075
+ this.events.removeEventListener(type, callback, this.toRemoveListenerOptions(options));
3076
+ }
3077
+ dispatchEvent(event) {
3078
+ return this.events.dispatchEvent(event);
1145
3079
  }
1146
3080
  subscribe(run) {
1147
3081
  run(this.current);
1148
3082
  const handler = (event) => {
1149
- if (event instanceof ConversationHistoryEvent) {
3083
+ if (event?.detail?.conversation) {
1150
3084
  run(event.detail.conversation);
1151
3085
  }
1152
3086
  };
1153
- const unsubscribe = this.addEventListener("change", handler);
1154
- return unsubscribe || (() => {});
3087
+ const unsubscribe2 = this.addEventListener("change", handler);
3088
+ return unsubscribe2 || (() => {});
1155
3089
  }
1156
3090
  getSnapshot() {
1157
3091
  return this.current;
@@ -1182,9 +3116,9 @@ class ConversationHistory extends EventTarget {
1182
3116
  get redoCount() {
1183
3117
  return this.currentNode.children.length;
1184
3118
  }
1185
- push(next) {
3119
+ push(next2) {
1186
3120
  const newNode = {
1187
- conversation: next,
3121
+ conversation: next2,
1188
3122
  parent: this.currentNode,
1189
3123
  children: []
1190
3124
  };
@@ -1201,9 +3135,9 @@ class ConversationHistory extends EventTarget {
1201
3135
  return;
1202
3136
  }
1203
3137
  redo(childIndex = 0) {
1204
- const next = this.currentNode.children[childIndex];
1205
- if (next) {
1206
- this.currentNode = next;
3138
+ const next2 = this.currentNode.children[childIndex];
3139
+ if (next2) {
3140
+ this.currentNode = next2;
1207
3141
  this.notifyChange("redo");
1208
3142
  return this.current;
1209
3143
  }
@@ -1292,8 +3226,8 @@ class ConversationHistory extends EventTarget {
1292
3226
  collapseSystemMessages() {
1293
3227
  this.push(collapseSystemMessages(this.current, this.env));
1294
3228
  }
1295
- redactMessageAtPosition(position, placeholder) {
1296
- this.push(redactMessageAtPosition(this.current, position, placeholder, this.env));
3229
+ redactMessageAtPosition(position, placeholderOrOptions) {
3230
+ this.push(redactMessageAtPosition(this.current, position, placeholderOrOptions, this.env));
1297
3231
  }
1298
3232
  truncateFromPosition(position, options) {
1299
3233
  this.push(truncateFromPosition(this.current, position, options, this.env));
@@ -1368,7 +3302,7 @@ class ConversationHistory extends EventTarget {
1368
3302
  return (...args) => {
1369
3303
  const boundFn = fn;
1370
3304
  const result = boundFn(this.current, ...args, this.env);
1371
- if (isConversation(result)) {
3305
+ if (isConversation2(result)) {
1372
3306
  this.push(result);
1373
3307
  }
1374
3308
  return result;
@@ -1390,14 +3324,16 @@ class ConversationHistory extends EventTarget {
1390
3324
  };
1391
3325
  if (root)
1392
3326
  clearNode(root);
3327
+ this.events.clear();
1393
3328
  }
1394
3329
  }
1395
- function isConversation(value) {
3330
+ function isConversation2(value) {
1396
3331
  return value !== null && typeof value === "object" && typeof value.schemaVersion === "number" && typeof value.id === "string" && typeof value.status === "string" && value.metadata !== null && typeof value.metadata === "object" && Array.isArray(value.ids) && typeof value.messages === "object" && value.messages !== null && !Array.isArray(value.messages) && typeof value.createdAt === "string" && typeof value.updatedAt === "string";
1397
3332
  }
1398
3333
  export {
1399
3334
  withEnvironment,
1400
3335
  withConversation,
3336
+ validateConversationIntegrity,
1401
3337
  updateStreamingMessage,
1402
3338
  truncateToTokenLimit,
1403
3339
  truncateFromPosition,
@@ -1417,18 +3353,30 @@ export {
1417
3353
  pairToolCallsWithResults,
1418
3354
  normalizeContent,
1419
3355
  multiModalContentSchema,
3356
+ messageSchema,
1420
3357
  messageRoleSchema,
1421
- messageJSONSchema,
1422
3358
  messageInputSchema,
1423
3359
  jsonValueSchema,
1424
3360
  isTransientKey,
3361
+ isToolResult,
3362
+ isToolCall,
3363
+ isTokenUsage,
1425
3364
  isStreamingMessage,
3365
+ isMultiModalContent,
3366
+ isMessageRole,
3367
+ isMessageInput,
3368
+ isMessage,
3369
+ isJSONValue,
3370
+ isConversationStatus,
3371
+ isConversation,
1426
3372
  isAssistantMessage,
1427
3373
  hasSystemMessage,
3374
+ getToolInteractions,
1428
3375
  getSystemMessages,
1429
3376
  getStreamingMessage,
1430
3377
  getStatistics,
1431
3378
  getRecentMessages,
3379
+ getPendingToolCalls,
1432
3380
  getMessages,
1433
3381
  getMessageIds,
1434
3382
  getMessageById,
@@ -1446,6 +3394,7 @@ export {
1446
3394
  createInvalidPositionError,
1447
3395
  createInvalidInputError,
1448
3396
  createDuplicateIdError,
3397
+ createConversationUnsafe,
1449
3398
  createConversation,
1450
3399
  copyMultiModalContent,
1451
3400
  copyContent,
@@ -1453,7 +3402,11 @@ export {
1453
3402
  conversationSchema,
1454
3403
  collapseSystemMessages,
1455
3404
  cancelStreamingMessage,
3405
+ assertConversationIntegrity,
1456
3406
  appendUserMessage,
3407
+ appendUnsafeMessage,
3408
+ appendToolUse,
3409
+ appendToolResult,
1457
3410
  appendSystemMessage,
1458
3411
  appendStreamingMessage,
1459
3412
  appendMessages,
@@ -1462,4 +3415,4 @@ export {
1462
3415
  ConversationHistory
1463
3416
  };
1464
3417
 
1465
- //# debugId=77EDEC984ED3373C64756E2164756E21
3418
+ //# debugId=F37C8EF457F39BC364756E2164756E21