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.
- package/README.md +148 -30
- package/dist/adapters/anthropic/index.d.ts.map +1 -1
- package/dist/adapters/anthropic/index.js +255 -2
- package/dist/adapters/anthropic/index.js.map +8 -4
- package/dist/adapters/gemini/index.d.ts.map +1 -1
- package/dist/adapters/gemini/index.js +255 -3
- package/dist/adapters/gemini/index.js.map +8 -4
- package/dist/adapters/openai/index.d.ts.map +1 -1
- package/dist/adapters/openai/index.js +247 -3
- package/dist/adapters/openai/index.js.map +8 -4
- package/dist/context.d.ts +6 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/conversation/append.d.ts +5 -0
- package/dist/conversation/append.d.ts.map +1 -1
- package/dist/conversation/create.d.ts +9 -0
- package/dist/conversation/create.d.ts.map +1 -1
- package/dist/conversation/index.d.ts +8 -3
- package/dist/conversation/index.d.ts.map +1 -1
- package/dist/conversation/integrity.d.ts +16 -0
- package/dist/conversation/integrity.d.ts.map +1 -0
- package/dist/conversation/modify.d.ts +8 -2
- package/dist/conversation/modify.d.ts.map +1 -1
- package/dist/conversation/serialization.d.ts +0 -17
- package/dist/conversation/serialization.d.ts.map +1 -1
- package/dist/conversation/system-messages.d.ts.map +1 -1
- package/dist/conversation/tool-interactions.d.ts +45 -0
- package/dist/conversation/tool-interactions.d.ts.map +1 -0
- package/dist/conversation/transform.d.ts.map +1 -1
- package/dist/conversation/validation.d.ts +8 -0
- package/dist/conversation/validation.d.ts.map +1 -0
- package/dist/errors.d.ts +6 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/export/index.js +249 -12
- package/dist/export/index.js.map +10 -6
- package/dist/guards.d.ts +13 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/history.d.ts +21 -8
- package/dist/history.d.ts.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2212 -259
- package/dist/index.js.map +26 -13
- package/dist/markdown/index.js +2169 -253
- package/dist/markdown/index.js.map +23 -12
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +38 -22
- package/dist/schemas/index.js.map +3 -3
- package/dist/schemas.d.ts +17 -37
- package/dist/schemas.d.ts.map +1 -1
- package/dist/streaming.d.ts.map +1 -1
- package/dist/types.d.ts +0 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utilities/index.d.ts +0 -1
- package/dist/utilities/index.d.ts.map +1 -1
- package/dist/utilities/markdown.d.ts.map +1 -1
- package/dist/utilities/tool-calls.d.ts +2 -6
- package/dist/utilities/tool-calls.d.ts.map +1 -1
- package/dist/utilities/tool-results.d.ts.map +1 -1
- package/dist/utilities/transient.d.ts.map +1 -1
- package/dist/utilities.d.ts +0 -1
- package/dist/utilities.d.ts.map +1 -1
- package/dist/versioning/index.d.ts +0 -1
- package/dist/versioning/index.d.ts.map +1 -1
- package/dist/versioning/index.js +1 -52
- package/dist/versioning/index.js.map +4 -5
- package/dist/with-conversation.d.ts +4 -1
- package/dist/with-conversation.d.ts.map +1 -1
- package/package.json +7 -4
- package/dist/conversation.d.ts +0 -109
- 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(() =>
|
|
37
|
-
z.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
}).
|
|
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(),
|
|
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
|
-
|
|
531
|
+
const filtered = args.filter((arg) => arg !== undefined);
|
|
532
|
+
if (filtered.length === 0) {
|
|
381
533
|
return { inputs: [] };
|
|
382
534
|
}
|
|
383
|
-
const last =
|
|
535
|
+
const last = filtered[filtered.length - 1];
|
|
384
536
|
if (isConversationEnvironmentParameter(last)) {
|
|
385
537
|
return {
|
|
386
|
-
inputs:
|
|
538
|
+
inputs: filtered.slice(0, -1),
|
|
387
539
|
environment: last
|
|
388
540
|
};
|
|
389
541
|
}
|
|
390
|
-
return { inputs:
|
|
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
|
-
|
|
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
|
-
|
|
457
|
-
|
|
607
|
+
const readonly = toReadonly(next);
|
|
608
|
+
return validate ? ensureConversationSafe(readonly) : readonly;
|
|
609
|
+
};
|
|
458
610
|
function appendUserMessage(conversation, content, metadata, environment) {
|
|
459
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: { ...
|
|
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,
|
|
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: { ...
|
|
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
|
-
|
|
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
|
|
653
|
-
toolResult
|
|
654
|
-
tokenUsage: undefined
|
|
860
|
+
toolCall,
|
|
861
|
+
toolResult,
|
|
862
|
+
tokenUsage: original.tokenUsage ? { ...original.tokenUsage } : undefined
|
|
655
863
|
});
|
|
656
|
-
const resolvedEnvironment = resolveConversationEnvironment(
|
|
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
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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
|
-
...
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
messages
|
|
899
|
+
...base,
|
|
900
|
+
role: "assistant",
|
|
901
|
+
goalCompleted: message.goalCompleted
|
|
711
902
|
};
|
|
712
903
|
}
|
|
713
|
-
return
|
|
904
|
+
return base;
|
|
714
905
|
}
|
|
715
906
|
function deserializeConversation(json) {
|
|
716
|
-
const
|
|
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
|
|
719
|
-
|
|
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
|
-
|
|
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((
|
|
939
|
+
const messageInstances = orderedMessages.map((message) => createMessage(message));
|
|
740
940
|
const conv = {
|
|
741
|
-
schemaVersion:
|
|
742
|
-
id:
|
|
743
|
-
title:
|
|
744
|
-
status:
|
|
745
|
-
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:
|
|
749
|
-
updatedAt:
|
|
948
|
+
createdAt: data.createdAt,
|
|
949
|
+
updatedAt: data.updatedAt
|
|
750
950
|
};
|
|
751
|
-
|
|
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/
|
|
779
|
-
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
|
845
|
-
const
|
|
846
|
-
const
|
|
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
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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: { ...
|
|
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
|
-
...
|
|
1392
|
+
...resolvedOptions?.metadata ?? {}
|
|
996
1393
|
};
|
|
997
1394
|
const finalizeOverrides = {
|
|
998
1395
|
metadata: finalMetadata
|
|
999
1396
|
};
|
|
1000
|
-
if (
|
|
1001
|
-
finalizeOverrides.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,
|
|
1070
|
-
current = redactMessageAtPosition(current, position,
|
|
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
|
-
|
|
1507
|
+
const result = fns.reduce((current, fn) => fn(current), conversation);
|
|
1508
|
+
return ensureConversationSafe(result);
|
|
1111
1509
|
}
|
|
1112
|
-
//
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
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:
|
|
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(
|
|
1137
|
-
this.dispatchEvent(
|
|
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
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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
|
|
3083
|
+
if (event?.detail?.conversation) {
|
|
1150
3084
|
run(event.detail.conversation);
|
|
1151
3085
|
}
|
|
1152
3086
|
};
|
|
1153
|
-
const
|
|
1154
|
-
return
|
|
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(
|
|
3119
|
+
push(next2) {
|
|
1186
3120
|
const newNode = {
|
|
1187
|
-
conversation:
|
|
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
|
|
1205
|
-
if (
|
|
1206
|
-
this.currentNode =
|
|
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,
|
|
1296
|
-
this.push(redactMessageAtPosition(this.current, position,
|
|
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 (
|
|
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
|
|
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=
|
|
3418
|
+
//# debugId=F37C8EF457F39BC364756E2164756E21
|