@codeworksh/aikit 0.2.0 → 0.3.0

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/index.mjs CHANGED
@@ -1,10 +1,12 @@
1
1
  import { Filesystem, NamedError, lazy } from "@codeworksh/utils";
2
- import { Type } from "@sinclair/typebox";
3
- import { keys, mapValues, pick, pipe } from "remeda";
4
- import Anthropic from "@anthropic-ai/sdk";
5
- import { parse } from "partial-json";
2
+ import { Type, TypeGuard } from "@sinclair/typebox";
3
+ import { capitalize, keys, mapValues, pick, pipe } from "remeda";
6
4
  import Ajv from "ajv";
7
5
  import addFormats from "ajv-formats";
6
+ import { uuidv7 } from "uuidv7";
7
+ import Anthropic from "@anthropic-ai/sdk";
8
+ import { parse } from "partial-json";
9
+ import { transform } from "esbuild";
8
10
  //#region src/provider/provider.ts
9
11
  let Provider;
10
12
  (function(_Provider) {
@@ -239,395 +241,78 @@ let Model;
239
241
  _Model.modelsAreEqual = modelsAreEqual;
240
242
  })(Model || (Model = {}));
241
243
  //#endregion
242
- //#region src/message/message.ts
243
- let Message;
244
- (function(_Message) {
245
- const TextContentSchema = _Message.TextContentSchema = Type.Object({
246
- type: Type.Literal("text"),
247
- text: Type.String(),
248
- textSignature: Type.Optional(Type.String())
249
- });
250
- const ImageContentSchema = _Message.ImageContentSchema = Type.Object({
251
- type: Type.Literal("image"),
252
- data: Type.String(),
253
- mimeType: Type.String()
254
- });
255
- const ThinkingContentSchema = _Message.ThinkingContentSchema = Type.Object({
256
- type: Type.Literal("thinking"),
257
- thinking: Type.String(),
258
- thinkingSignature: Type.Optional(Type.String()),
259
- redacted: Type.Optional(Type.Boolean())
260
- });
261
- const ToolCallBaseSchema = _Message.ToolCallBaseSchema = Type.Object({
262
- type: Type.Literal("toolCall"),
263
- callID: Type.String(),
264
- name: Type.String(),
265
- arguments: Type.Record(Type.String(), Type.Any()),
266
- thoughtSignature: Type.Optional(Type.String()),
267
- time: Type.Object({
268
- start: Type.Number(),
269
- end: Type.Number()
270
- })
271
- });
272
- const ToolSuccessResult = _Message.ToolSuccessResult = Type.Object({
273
- content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
274
- details: Type.Optional(Type.Any()),
275
- isError: Type.Literal(false)
276
- });
277
- const ToolErrorResult = _Message.ToolErrorResult = Type.Object({
278
- content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
279
- details: Type.Optional(Type.Any()),
280
- isError: Type.Literal(true)
281
- });
282
- const ToolRunningPartial = _Message.ToolRunningPartial = Type.Object({
283
- content: Type.Optional(Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))),
284
- details: Type.Optional(Type.Any())
285
- });
286
- const ToolStatusEnum = _Message.ToolStatusEnum = {
287
- pending: "pending",
288
- running: "running",
289
- completed: "completed",
290
- error: "error",
291
- skipped: "skipped",
292
- aborted: "aborted"
244
+ //#region src/provider/stream.ts
245
+ let Stream;
246
+ (function(_Stream) {
247
+ const ProtocolProviderNotFoundError = _Stream.ProtocolProviderNotFoundError = NamedError.create("ProtocolProviderNotFoundError", Type.Object({ protocol: Model.KnownProtocolSchema }));
248
+ const ProtocolMismatchError = _Stream.ProtocolMismatchError = NamedError.create("ProtocolMismatchError", Type.Object({
249
+ actual: Model.KnownProtocolSchema,
250
+ expected: Model.KnownProtocolSchema
251
+ }));
252
+ const ThinkingLevelEnum = _Stream.ThinkingLevelEnum = {
253
+ off: "off",
254
+ minimal: "minimal",
255
+ low: "low",
256
+ medium: "medium",
257
+ high: "high",
258
+ xhigh: "xhigh"
293
259
  };
294
- const ToolRunningSchema = _Message.ToolRunningSchema = Type.Object({
295
- status: Type.Literal(ToolStatusEnum.running),
296
- partial: Type.Optional(ToolRunningPartial)
297
- });
298
- const ToolCompletedSchema = _Message.ToolCompletedSchema = Type.Object({
299
- status: Type.Literal(ToolStatusEnum.completed),
300
- result: ToolSuccessResult
301
- });
302
- const ToolErrorSchema = _Message.ToolErrorSchema = Type.Object({
303
- status: Type.Literal(ToolStatusEnum.error),
304
- result: ToolErrorResult
305
- });
306
- const ToolSkippedSchema = _Message.ToolSkippedSchema = Type.Object({
307
- status: Type.Literal(ToolStatusEnum.skipped),
308
- result: ToolErrorResult
309
- });
310
- const ToolAbortedSchema = _Message.ToolAbortedSchema = Type.Object({
311
- status: Type.Literal(ToolStatusEnum.aborted),
312
- result: ToolErrorResult
313
- });
314
- const ToolCallPendingPart = Type.Intersect([ToolCallBaseSchema, Type.Object({ status: Type.Literal(ToolStatusEnum.pending) })]);
315
- const ToolCallRunningPart = Type.Composite([ToolCallBaseSchema, ToolRunningSchema]);
316
- const ToolCallCompletedPart = Type.Composite([ToolCallBaseSchema, ToolCompletedSchema]);
317
- const ToolCallErrorPart = Type.Composite([ToolCallBaseSchema, ToolErrorSchema]);
318
- const ToolCallSkippedPart = Type.Composite([ToolCallBaseSchema, ToolSkippedSchema]);
319
- const ToolCallAbortedPart = Type.Composite([ToolCallBaseSchema, ToolAbortedSchema]);
320
- const ToolCallSchema = _Message.ToolCallSchema = Type.Union([
321
- ToolCallPendingPart,
322
- ToolCallRunningPart,
323
- ToolCallCompletedPart,
324
- ToolCallErrorPart,
325
- ToolCallSkippedPart,
326
- ToolCallAbortedPart
260
+ const ThinkingLevelSchema = _Stream.ThinkingLevelSchema = Type.Union([
261
+ Type.Literal(ThinkingLevelEnum.off),
262
+ Type.Literal(ThinkingLevelEnum.minimal),
263
+ Type.Literal(ThinkingLevelEnum.low),
264
+ Type.Literal(ThinkingLevelEnum.medium),
265
+ Type.Literal(ThinkingLevelEnum.high),
266
+ Type.Literal(ThinkingLevelEnum.xhigh)
327
267
  ]);
328
- const UsageSchema = _Message.UsageSchema = Type.Object({
329
- input: Type.Number(),
330
- output: Type.Number(),
331
- cacheRead: Type.Number(),
332
- cacheWrite: Type.Number(),
333
- totalTokens: Type.Number(),
334
- cost: Type.Object({
335
- input: Type.Number(),
336
- output: Type.Number(),
337
- cacheRead: Type.Number(),
338
- cacheWrite: Type.Number(),
339
- total: Type.Number()
340
- })
268
+ const ThinkingBudgetsSchema = _Stream.ThinkingBudgetsSchema = Type.Object({
269
+ off: Type.Optional(Type.Number()),
270
+ minimal: Type.Optional(Type.Number()),
271
+ low: Type.Optional(Type.Number()),
272
+ medium: Type.Optional(Type.Number()),
273
+ high: Type.Optional(Type.Number())
341
274
  });
342
- const StopReasonSchema = _Message.StopReasonSchema = Type.Union([
343
- Type.Literal("stop"),
344
- Type.Literal("length"),
345
- Type.Literal("toolUse"),
346
- Type.Literal("error"),
347
- Type.Literal("aborted")
275
+ const CacheRetentionSchema = _Stream.CacheRetentionSchema = Type.Union([
276
+ Type.Literal("none"),
277
+ Type.Literal("short"),
278
+ Type.Literal("long")
348
279
  ]);
349
- const UserMessageSchema = _Message.UserMessageSchema = Type.Object({
350
- role: Type.Literal("user"),
351
- time: Type.Object({ created: Type.Number() }),
352
- parts: Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))
353
- });
354
- const AssistantMessageSchema = _Message.AssistantMessageSchema = Type.Object({
355
- role: Type.Literal("assistant"),
356
- protocol: Model.KnownProtocolSchema,
357
- provider: Provider.Info,
358
- model: Type.String(),
359
- usage: UsageSchema,
360
- stopReason: StopReasonSchema,
361
- errorMessage: Type.Optional(Type.String()),
362
- time: Type.Object({
363
- created: Type.Number(),
364
- completed: Type.Number()
365
- }),
366
- parts: Type.Array(Type.Union([
367
- TextContentSchema,
368
- ImageContentSchema,
369
- ThinkingContentSchema,
370
- ToolCallSchema
371
- ])),
372
- responseID: Type.Optional(Type.String())
373
- });
374
- const MessageSchema = _Message.MessageSchema = Type.Union([UserMessageSchema, AssistantMessageSchema]);
375
- const ToolSchema = _Message.ToolSchema = Type.Object({
376
- name: Type.String(),
377
- description: Type.String(),
378
- parameters: Type.Unsafe()
280
+ const TransportSchema = _Stream.TransportSchema = Type.Union([
281
+ Type.Literal("sse"),
282
+ Type.Literal("websocket"),
283
+ Type.Literal("auto")
284
+ ]);
285
+ const OptionsSchema = _Stream.OptionsSchema = Type.Object({
286
+ temperature: Type.Optional(Type.Number()),
287
+ maxTokens: Type.Optional(Type.Number()),
288
+ signal: Type.Optional(Type.Unsafe()),
289
+ apiKey: Type.Optional(Type.String()),
290
+ transport: Type.Optional(TransportSchema),
291
+ cacheRetention: Type.Optional(CacheRetentionSchema),
292
+ sessionId: Type.Optional(Type.String()),
293
+ onPayload: Type.Optional(Type.Unsafe()),
294
+ headers: Type.Optional(Type.Record(Type.String(), Type.String())),
295
+ maxRetryDelayMs: Type.Optional(Type.Number()),
296
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
379
297
  });
380
- function defineTool(tool) {
381
- return tool;
382
- }
383
- _Message.defineTool = defineTool;
384
- _Message.ContextSchema = Type.Object({
385
- systemPrompt: Type.Optional(Type.String()),
386
- messages: Type.Array(MessageSchema),
387
- tools: Type.Optional(Type.Array(ToolSchema))
388
- });
389
- function transformMessages(messages, model, normalizeToolCallId) {
390
- const toolCallIDMap = /* @__PURE__ */ new Map();
391
- const transformed = [];
392
- for (const msg of messages) {
393
- if (msg.role === "user") {
394
- transformed.push(msg);
395
- continue;
396
- }
397
- const assistantMsg = msg;
398
- const isSameModel = assistantMsg.provider.id === model.provider.id && assistantMsg.protocol === model.protocol && assistantMsg.model === model.id;
399
- const parts = assistantMsg.parts.flatMap((block) => {
400
- if (block.type === "thinking") {
401
- if (isSameModel && block.thinkingSignature) return block;
402
- if (!block.thinking || block.thinking.trim() === "") return [];
403
- if (isSameModel) return block;
404
- return {
405
- type: "text",
406
- text: block.thinking
407
- };
408
- }
409
- if (block.type === "text") {
410
- if (isSameModel) return block;
411
- return {
412
- type: "text",
413
- text: block.text
414
- };
415
- }
416
- if (block.type === "toolCall") {
417
- const toolCall = block;
418
- let normalizedToolCall = toolCall;
419
- if (!isSameModel && toolCall.thoughtSignature) {
420
- normalizedToolCall = { ...toolCall };
421
- delete normalizedToolCall.thoughtSignature;
422
- }
423
- const normalizedID = toolCallIDMap.get(block.callID);
424
- if (normalizedID && normalizedID !== block.callID) normalizedToolCall = {
425
- ...normalizedToolCall,
426
- callID: normalizedID
427
- };
428
- else if (!isSameModel && normalizeToolCallId) {
429
- const nextID = normalizeToolCallId(toolCall.callID, model, assistantMsg);
430
- if (nextID !== toolCall.callID) {
431
- toolCallIDMap.set(toolCall.callID, nextID);
432
- normalizedToolCall = {
433
- ...normalizedToolCall,
434
- callID: nextID
435
- };
436
- }
437
- }
438
- return normalizedToolCall;
439
- }
440
- return block;
441
- });
442
- transformed.push({
443
- ...assistantMsg,
444
- parts
445
- });
446
- }
447
- return transformed;
448
- }
449
- _Message.transformMessages = transformMessages;
450
- })(Message || (Message = {}));
451
- //#endregion
452
- //#region src/utils/eventstream.ts
453
- var EventStream = class {
454
- queue = [];
455
- waiting = [];
456
- done = false;
457
- finalResultPromise;
458
- resolveFinalResult;
459
- constructor(isComplete, extractResult) {
460
- this.isComplete = isComplete;
461
- this.extractResult = extractResult;
462
- this.finalResultPromise = new Promise((resolve) => {
463
- this.resolveFinalResult = resolve;
464
- });
465
- }
466
- push(event) {
467
- if (this.done) return;
468
- if (this.isComplete(event)) {
469
- this.done = true;
470
- this.resolveFinalResult(this.extractResult(event));
471
- }
472
- const waiter = this.waiting.shift();
473
- if (waiter) waiter({
474
- value: event,
475
- done: false
476
- });
477
- else this.queue.push(event);
478
- }
479
- end(result) {
480
- this.done = true;
481
- if (result !== void 0) this.resolveFinalResult(result);
482
- while (this.waiting.length > 0) this.waiting.shift()({
483
- value: void 0,
484
- done: true
485
- });
486
- }
487
- async *[Symbol.asyncIterator]() {
488
- while (true) if (this.queue.length > 0) yield this.queue.shift();
489
- else if (this.done) return;
490
- else {
491
- const result = await new Promise((resolve) => this.waiting.push(resolve));
492
- if (result.done) return;
493
- yield result.value;
494
- }
495
- }
496
- result() {
497
- return this.finalResultPromise;
498
- }
499
- };
500
- var AssistantMessageEventStream = class extends EventStream {
501
- constructor() {
502
- super((event) => event.type === "done" || event.type === "error", (event) => {
503
- if (event.type === "done") return event.message;
504
- else if (event.type === "error") return event.error;
505
- throw new Error("Unexpected event type for final result");
506
- });
507
- }
508
- };
509
- //#endregion
510
- //#region src/utils/jsonparse.ts
511
- /**
512
- * @description Helper function to parse partial jsons during LLM streaming.
513
- */
514
- /**
515
- * Attempts to parse potentially incomplete JSON during streaming.
516
- * Always returns a valid object, even if the JSON is incomplete.
517
- *
518
- * @param partialJson The partial JSON string from streaming
519
- * @returns Parsed object or empty object if parsing fails
520
- */
521
- function parseStreamingJson(partialJson) {
522
- if (!partialJson || partialJson.trim() === "") return {};
523
- try {
524
- return JSON.parse(partialJson);
525
- } catch {
526
- try {
527
- return parse(partialJson) ?? {};
528
- } catch {
529
- return {};
530
- }
531
- }
532
- }
533
- //#endregion
534
- //#region src/utils/sanitize.ts
535
- /**
536
- * Removes unpaired Unicode surrogate characters from a string.
537
- *
538
- * Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
539
- * or vice versa) cause JSON serialization errors in many API providers.
540
- *
541
- * Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
542
- * surrogates and will NOT be affected by this function.
543
- *
544
- * @param text - The text to sanitize
545
- * @returns The sanitized text with unpaired surrogates removed
546
- *
547
- * @example
548
- * // Valid emoji (properly paired surrogates) are preserved
549
- * sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
550
- *
551
- * // Unpaired high surrogate is removed
552
- * const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
553
- * sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
554
- */
555
- function sanitizeSurrogates(text) {
556
- return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
557
- }
558
- //#endregion
559
- //#region src/provider/stream.ts
560
- let Stream;
561
- (function(_Stream) {
562
- const ProtocolProviderNotFoundError = _Stream.ProtocolProviderNotFoundError = NamedError.create("ProtocolProviderNotFoundError", Type.Object({ protocol: Model.KnownProtocolSchema }));
563
- const ProtocolMismatchError = _Stream.ProtocolMismatchError = NamedError.create("ProtocolMismatchError", Type.Object({
564
- actual: Model.KnownProtocolSchema,
565
- expected: Model.KnownProtocolSchema
566
- }));
567
- const ThinkingLevelEnum = _Stream.ThinkingLevelEnum = {
568
- off: "off",
569
- minimal: "minimal",
570
- low: "low",
571
- medium: "medium",
572
- high: "high",
573
- xhigh: "xhigh"
574
- };
575
- const ThinkingLevelSchema = _Stream.ThinkingLevelSchema = Type.Union([
576
- Type.Literal(ThinkingLevelEnum.off),
577
- Type.Literal(ThinkingLevelEnum.minimal),
578
- Type.Literal(ThinkingLevelEnum.low),
579
- Type.Literal(ThinkingLevelEnum.medium),
580
- Type.Literal(ThinkingLevelEnum.high),
581
- Type.Literal(ThinkingLevelEnum.xhigh)
582
- ]);
583
- const ThinkingBudgetsSchema = _Stream.ThinkingBudgetsSchema = Type.Object({
584
- off: Type.Optional(Type.Number()),
585
- minimal: Type.Optional(Type.Number()),
586
- low: Type.Optional(Type.Number()),
587
- medium: Type.Optional(Type.Number()),
588
- high: Type.Optional(Type.Number())
589
- });
590
- const CacheRetentionSchema = _Stream.CacheRetentionSchema = Type.Union([
591
- Type.Literal("none"),
592
- Type.Literal("short"),
593
- Type.Literal("long")
594
- ]);
595
- const TransportSchema = _Stream.TransportSchema = Type.Union([
596
- Type.Literal("sse"),
597
- Type.Literal("websocket"),
598
- Type.Literal("auto")
599
- ]);
600
- const OptionsSchema = _Stream.OptionsSchema = Type.Object({
601
- temperature: Type.Optional(Type.Number()),
602
- maxTokens: Type.Optional(Type.Number()),
603
- signal: Type.Optional(Type.Unsafe()),
604
- apiKey: Type.Optional(Type.String()),
605
- transport: Type.Optional(TransportSchema),
606
- cacheRetention: Type.Optional(CacheRetentionSchema),
607
- sessionId: Type.Optional(Type.String()),
608
- onPayload: Type.Optional(Type.Unsafe()),
609
- headers: Type.Optional(Type.Record(Type.String(), Type.String())),
610
- maxRetryDelayMs: Type.Optional(Type.Number()),
611
- metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
612
- });
613
- _Stream.SimpleOptionsSchema = Type.Composite([OptionsSchema, Type.Object({
614
- reasoning: Type.Optional(ThinkingLevelSchema),
615
- thinkingBudgets: Type.Optional(ThinkingBudgetsSchema)
616
- })]);
617
- function buildBaseOptions(model, options, apiKey) {
618
- return {
619
- temperature: options?.temperature,
620
- maxTokens: options?.maxTokens || Math.min(model.maxTokens, 32e3),
621
- signal: options?.signal,
622
- apiKey: apiKey || options?.apiKey,
623
- transport: options?.transport,
624
- cacheRetention: options?.cacheRetention,
625
- sessionId: options?.sessionId,
626
- headers: options?.headers,
627
- onPayload: options?.onPayload,
628
- maxRetryDelayMs: options?.maxRetryDelayMs,
629
- metadata: options?.metadata
630
- };
298
+ _Stream.SimpleOptionsSchema = Type.Composite([OptionsSchema, Type.Object({
299
+ reasoning: Type.Optional(ThinkingLevelSchema),
300
+ thinkingBudgets: Type.Optional(ThinkingBudgetsSchema)
301
+ })]);
302
+ function buildBaseOptions(model, options, apiKey) {
303
+ return {
304
+ temperature: options?.temperature,
305
+ maxTokens: options?.maxTokens || Math.min(model.maxTokens, 32e3),
306
+ signal: options?.signal,
307
+ apiKey: apiKey || options?.apiKey,
308
+ transport: options?.transport,
309
+ cacheRetention: options?.cacheRetention,
310
+ sessionId: options?.sessionId,
311
+ headers: options?.headers,
312
+ onPayload: options?.onPayload,
313
+ maxRetryDelayMs: options?.maxRetryDelayMs,
314
+ metadata: options?.metadata
315
+ };
631
316
  }
632
317
  _Stream.buildBaseOptions = buildBaseOptions;
633
318
  function clampReasoning(effort) {
@@ -722,1099 +407,1439 @@ let Stream;
722
407
  _Stream.clearProtocolProviders = clearProtocolProviders;
723
408
  })(Stream || (Stream = {}));
724
409
  //#endregion
725
- //#region src/provider/providers/anthropic/index.ts
726
- function getEnvApiKey(_provider) {}
727
- function resolveCacheRetention(cacheRetention) {
728
- if (cacheRetention) return cacheRetention;
729
- if (typeof process !== "undefined" && process.env.CODEWORK_CACHE_RETENTION === "long") return "long";
730
- return "short";
731
- }
732
- function getCacheControl(baseUrl, cacheRetention) {
733
- const retention = resolveCacheRetention(cacheRetention);
734
- if (retention === "none") return { retention };
735
- const ttl = retention === "long" && baseUrl.includes("api.anthropic.com") ? "1h" : void 0;
736
- return {
737
- retention,
738
- cacheControl: {
739
- type: "ephemeral",
740
- ...ttl && { ttl }
741
- }
742
- };
743
- }
744
- const claudeCodeVersion = "2.1.2";
745
- const ccToolLookup = new Map([
746
- "Read",
747
- "Write",
748
- "Edit",
749
- "Bash",
750
- "Grep",
751
- "Glob",
752
- "AskUserQuestion",
753
- "EnterPlanMode",
754
- "ExitPlanMode",
755
- "KillShell",
756
- "NotebookEdit",
757
- "Skill",
758
- "Task",
759
- "TaskOutput",
760
- "TodoWrite",
761
- "WebFetch",
762
- "WebSearch"
763
- ].map((t) => [t.toLowerCase(), t]));
764
- const toClaudeCodeName = (name) => ccToolLookup.get(name.toLowerCase()) ?? name;
765
- const fromClaudeCodeName = (name, tools) => {
766
- if (tools && tools.length > 0) {
767
- const lowerName = name.toLowerCase();
768
- const matchedTool = tools.find((tool) => tool.name.toLowerCase() === lowerName);
769
- if (matchedTool) return matchedTool.name;
410
+ //#region src/utils/eventstream.ts
411
+ var EventStream = class {
412
+ queue = [];
413
+ waiting = [];
414
+ done = false;
415
+ finalResultPromise;
416
+ resolveFinalResult;
417
+ constructor(isComplete, extractResult) {
418
+ this.isComplete = isComplete;
419
+ this.extractResult = extractResult;
420
+ this.finalResultPromise = new Promise((resolve) => {
421
+ this.resolveFinalResult = resolve;
422
+ });
423
+ }
424
+ push(event) {
425
+ if (this.done) return;
426
+ if (this.isComplete(event)) {
427
+ this.done = true;
428
+ this.resolveFinalResult(this.extractResult(event));
429
+ }
430
+ const waiter = this.waiting.shift();
431
+ if (waiter) waiter({
432
+ value: event,
433
+ done: false
434
+ });
435
+ else this.queue.push(event);
436
+ }
437
+ end(result) {
438
+ this.done = true;
439
+ if (result !== void 0) this.resolveFinalResult(result);
440
+ while (this.waiting.length > 0) this.waiting.shift()({
441
+ value: void 0,
442
+ done: true
443
+ });
444
+ }
445
+ async *[Symbol.asyncIterator]() {
446
+ while (true) if (this.queue.length > 0) yield this.queue.shift();
447
+ else if (this.done) return;
448
+ else {
449
+ const result = await new Promise((resolve) => this.waiting.push(resolve));
450
+ if (result.done) return;
451
+ yield result.value;
452
+ }
453
+ }
454
+ result() {
455
+ return this.finalResultPromise;
770
456
  }
771
- return name;
772
457
  };
773
- function convertContentBlocks(content) {
774
- if (!content.some((c) => c.type === "image")) return sanitizeSurrogates(content.map((c) => c.text).join("\n"));
775
- const blocks = content.map((block) => {
776
- if (block.type === "text") return {
777
- type: "text",
778
- text: sanitizeSurrogates(block.text)
779
- };
780
- return {
781
- type: "image",
782
- source: {
783
- type: "base64",
784
- media_type: block.mimeType,
785
- data: block.data
786
- }
787
- };
788
- });
789
- if (!blocks.some((b) => b.type === "text")) blocks.unshift({
790
- type: "text",
791
- text: "(see attached image)"
792
- });
793
- return blocks;
794
- }
795
- function mergeHeaders(...headerSources) {
796
- const merged = {};
797
- for (const headers of headerSources) if (headers) Object.assign(merged, headers);
798
- return merged;
458
+ var AssistantMessageEventStream = class extends EventStream {
459
+ constructor() {
460
+ super((event) => event.type === "done" || event.type === "error", (event) => {
461
+ if (event.type === "done") return event.message;
462
+ else if (event.type === "error") return event.error;
463
+ throw new Error("Unexpected event type for final result");
464
+ });
465
+ }
466
+ };
467
+ //#endregion
468
+ //#region src/utils/validation.ts
469
+ const ajv = new Ajv({
470
+ allErrors: true,
471
+ strict: false,
472
+ coerceTypes: true
473
+ });
474
+ addFormats(ajv);
475
+ /**
476
+ * Validates an arbitrary value against a TypeBox schema and returns the coerced value.
477
+ */
478
+ function validateSchema(schema, value, label) {
479
+ const validate = ajv.compile(schema);
480
+ const input = structuredClone(value);
481
+ if (validate(input)) return input;
482
+ const errors = validate.errors?.map((err) => {
483
+ return ` - ${err.instancePath ? err.instancePath.substring(1) : err.params?.missingProperty || "root"}: ${err.message}`;
484
+ }).join("\n") || "Unknown Validation Error";
485
+ throw new Error([
486
+ `Validation Failed For ${label}`,
487
+ `${errors}\n`,
488
+ "Received Value:",
489
+ `${JSON.stringify(value, null, 2)}\n`
490
+ ].join("\n"));
799
491
  }
800
- function isOAuthToken(apiKey) {
801
- return apiKey.includes("sk-ant-oat");
492
+ /**
493
+ * Validates tool call arguments against the tool's TypeBox schema
494
+ */
495
+ function validateToolArguments(tool, toolExecution) {
496
+ return validateSchema(tool.parameters, toolExecution.rawArgs, `Tool "${toolExecution.name}"`);
802
497
  }
803
- function createClient(model, apiKey, interleavedThinking, optionsHeaders) {
804
- const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
805
- if (interleavedThinking) betaFeatures.push("interleaved-thinking-2025-05-14");
806
- if (isOAuthToken(apiKey)) {
807
- const defaultHeaders = mergeHeaders({
808
- accept: "application/json",
809
- "anthropic-dangerous-direct-browser-access": "true",
810
- "anthropic-beta": `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}`,
811
- "user-agent": `claude-cli/${claudeCodeVersion} (external, cli)`,
812
- "x-app": "cli"
813
- }, model.headers, optionsHeaders);
498
+ //#endregion
499
+ //#region src/agent/loop.ts
500
+ let Loop;
501
+ (function(_Loop) {
502
+ function createAgentStream() {
503
+ return new EventStream((event) => event.type === "agent.end", (event) => event.type === "agent.end" ? event.messages : []);
504
+ }
505
+ const AgentContextError = _Loop.AgentContextError = NamedError.create("AgentContextError", Type.Object({ message: Type.String() }));
506
+ function snapshotAssistantMessage(message) {
507
+ return structuredClone(message);
508
+ }
509
+ function snapshotAssistantPartEvent(message, partIndex) {
510
+ const snapshot = snapshotAssistantMessage(message);
511
+ const part = snapshot.parts[partIndex];
512
+ if (!part) throw new Error(`Assistant message part at index ${partIndex} was not found`);
814
513
  return {
815
- client: new Anthropic({
816
- apiKey: null,
817
- authToken: apiKey,
818
- baseURL: model.baseUrl,
819
- defaultHeaders,
820
- dangerouslyAllowBrowser: true
821
- }),
822
- isOAuthToken: true
514
+ message: snapshot,
515
+ part
823
516
  };
824
517
  }
825
- const defaultHeaders = mergeHeaders({
826
- accept: "application/json",
827
- "anthropic-dangerous-direct-browser-access": "true",
828
- "anthropic-beta": betaFeatures.join(",")
829
- }, model.headers, optionsHeaders);
830
- return {
831
- client: new Anthropic({
832
- apiKey,
833
- baseURL: model.baseUrl,
834
- dangerouslyAllowBrowser: true,
835
- defaultHeaders
836
- }),
837
- isOAuthToken: false
838
- };
839
- }
840
- function normalizeToolCallId(id) {
841
- return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
842
- }
843
- function assertReplayableAssistant(message) {
844
- let sawToolCall = false;
845
- let sawNonTerminalToolCall = false;
846
- let sawTerminalToolCall = false;
847
- for (const part of message.parts) {
848
- if (part.type === "image") throw new Error("Anthropic replay does not support assistant image parts");
849
- if (part.type === "toolCall") {
850
- sawToolCall = true;
851
- if (part.status === "pending" || part.status === "running") sawNonTerminalToolCall = true;
852
- else sawTerminalToolCall = true;
853
- continue;
854
- }
855
- if (sawToolCall) throw new Error("Assistant parts after a toolCall are not replayable for Anthropic");
518
+ async function emitEvent(emit, event) {
519
+ await emit(event);
856
520
  }
857
- if (sawToolCall && message.stopReason !== "toolUse") throw new Error("Assistant message with tool calls must have stopReason 'toolUse'");
858
- if (!sawToolCall && message.stopReason === "toolUse") throw new Error("Assistant message with stopReason 'toolUse' must contain at least one toolCall");
859
- if (sawTerminalToolCall && sawNonTerminalToolCall) throw new Error("Assistant message cannot mix terminal and non-terminal toolCall states");
860
- }
861
- function buildSyntheticToolResult(block) {
862
- return {
863
- type: "tool_result",
864
- tool_use_id: block.callID,
865
- content: convertContentBlocks([{
866
- type: "text",
867
- text: [
868
- "<result>",
869
- "No Result Provided",
870
- "</result>"
871
- ].join("\n")
872
- }]),
873
- is_error: true
874
- };
875
- }
876
- function convertMessages(messages, model, isOAuthToken, cacheControl) {
877
- const params = [];
878
- const transformedMessages = Message.transformMessages(messages, model, normalizeToolCallId);
879
- for (const msg of transformedMessages) {
880
- if (msg.role === "user") {
881
- const blocks = msg.parts.map((item) => {
882
- if (item.type === "text") return {
883
- type: "text",
884
- text: sanitizeSurrogates(item.text)
885
- };
886
- return {
887
- type: "image",
888
- source: {
889
- type: "base64",
890
- media_type: item.mimeType,
891
- data: item.data
892
- }
893
- };
894
- });
895
- let filteredBlocks = !model.input.includes("image") ? blocks.filter((b) => b.type !== "image") : blocks;
896
- filteredBlocks = filteredBlocks.filter((b) => b.type === "text" ? b.text.trim().length > 0 : true);
897
- if (filteredBlocks.length === 0) continue;
898
- params.push({
899
- role: "user",
900
- content: filteredBlocks
521
+ async function streamAssistantResponse(config, context, emit, streamFn, signal) {
522
+ let messages = context.messages;
523
+ if (config.transformContext) messages = await config.transformContext(messages, signal);
524
+ const llmMessages = await config.convertToLlm(messages);
525
+ const llmContext = {
526
+ systemPrompt: context.systemPrompt,
527
+ messages: llmMessages,
528
+ tools: context.tools
529
+ };
530
+ const streamFunction = streamFn || Stream.streamSimple;
531
+ const resolvedApiKey = (config.getApiKey ? await config.getApiKey(config.model.provider) : void 0) || config.apiKey;
532
+ const events = await streamFunction(config.model, llmContext, {
533
+ ...config,
534
+ apiKey: resolvedApiKey,
535
+ signal
536
+ });
537
+ let partialMessage = null;
538
+ let started = false;
539
+ const ensureMessageStarted = async (message) => {
540
+ if (started) return;
541
+ started = true;
542
+ await emitEvent(emit, {
543
+ type: "message.start",
544
+ message: snapshotAssistantMessage(message)
901
545
  });
902
- continue;
903
- }
904
- if (msg.stopReason === "error" || msg.stopReason === "aborted") continue;
905
- assertReplayableAssistant(msg);
906
- const assistantBlocks = [];
907
- const toolResults = [];
908
- for (const block of msg.parts) if (block.type === "text") {
909
- if (block.text.trim().length === 0) continue;
910
- assistantBlocks.push({
911
- type: "text",
912
- text: sanitizeSurrogates(block.text)
546
+ };
547
+ const emitMessageUpdate = async (message) => {
548
+ await emitEvent(emit, {
549
+ type: "message.update",
550
+ message: snapshotAssistantMessage(message)
913
551
  });
914
- } else if (block.type === "thinking") {
915
- if (block.thinking.trim().length === 0) continue;
916
- if (!block.thinkingSignature || block.thinkingSignature.trim().length === 0) assistantBlocks.push({
917
- type: "text",
918
- text: sanitizeSurrogates(block.thinking)
552
+ };
553
+ const emitPartStart = async (message, partIndex) => {
554
+ await emitEvent(emit, {
555
+ type: "message.part.start",
556
+ partIndex,
557
+ ...snapshotAssistantPartEvent(message, partIndex)
919
558
  });
920
- else assistantBlocks.push({
921
- type: "thinking",
922
- thinking: sanitizeSurrogates(block.thinking),
923
- signature: block.thinkingSignature
924
- });
925
- } else if (block.type === "toolCall") {
926
- assistantBlocks.push({
927
- type: "tool_use",
928
- id: block.callID,
929
- name: isOAuthToken ? toClaudeCodeName(block.name) : block.name,
930
- input: block.arguments ?? {}
559
+ };
560
+ const emitPartUpdate = async (message, partIndex) => {
561
+ await emitEvent(emit, {
562
+ type: "message.part.update",
563
+ partIndex,
564
+ ...snapshotAssistantPartEvent(message, partIndex),
565
+ source: "llm"
931
566
  });
932
- if (block.status === "pending" || block.status === "running") toolResults.push(buildSyntheticToolResult(block));
933
- else toolResults.push({
934
- type: "tool_result",
935
- tool_use_id: block.callID,
936
- content: convertContentBlocks(block.result.content),
937
- is_error: block.result.isError
567
+ };
568
+ const emitPartEnd = async (message, partIndex) => {
569
+ await emitEvent(emit, {
570
+ type: "message.part.end",
571
+ partIndex,
572
+ ...snapshotAssistantPartEvent(message, partIndex)
938
573
  });
574
+ };
575
+ for await (const event of events) switch (event.type) {
576
+ case "start":
577
+ partialMessage = event.partial;
578
+ await ensureMessageStarted(partialMessage);
579
+ break;
580
+ case "text.start":
581
+ case "thinking.start":
582
+ case "toolcall.start":
583
+ partialMessage = event.partial;
584
+ await ensureMessageStarted(partialMessage);
585
+ await emitPartStart(partialMessage, event.partIndex);
586
+ break;
587
+ case "text.delta":
588
+ case "thinking.delta":
589
+ case "toolcall.delta":
590
+ partialMessage = event.partial;
591
+ await ensureMessageStarted(partialMessage);
592
+ await emitPartUpdate(partialMessage, event.partIndex);
593
+ break;
594
+ case "text.end":
595
+ case "thinking.end":
596
+ case "toolcall.end":
597
+ partialMessage = event.partial;
598
+ await ensureMessageStarted(partialMessage);
599
+ await emitPartEnd(partialMessage, event.partIndex);
600
+ await emitMessageUpdate(partialMessage);
601
+ break;
602
+ case "done":
603
+ await ensureMessageStarted(event.message);
604
+ await emitEvent(emit, {
605
+ type: "message.end",
606
+ message: snapshotAssistantMessage(event.message)
607
+ });
608
+ return event.message;
609
+ case "error":
610
+ await ensureMessageStarted(event.error);
611
+ await emitEvent(emit, {
612
+ type: "message.end",
613
+ message: snapshotAssistantMessage(event.error)
614
+ });
615
+ return event.error;
939
616
  }
940
- if (assistantBlocks.length > 0) params.push({
941
- role: "assistant",
942
- content: assistantBlocks
943
- });
944
- if (toolResults.length > 0) params.push({
945
- role: "user",
946
- content: toolResults
617
+ const finalMessage = await events.result();
618
+ await ensureMessageStarted(finalMessage);
619
+ await emitEvent(emit, {
620
+ type: "message.end",
621
+ message: snapshotAssistantMessage(finalMessage)
947
622
  });
623
+ return finalMessage;
948
624
  }
949
- if (cacheControl && params.length > 0) {
950
- const lastMessage = params[params.length - 1];
951
- if (lastMessage.role === "user") {
952
- if (Array.isArray(lastMessage.content)) {
953
- const lastBlock = lastMessage.content[lastMessage.content.length - 1];
954
- if (lastBlock && (lastBlock.type === "text" || lastBlock.type === "image" || lastBlock.type === "tool_result")) lastBlock.cache_control = cacheControl;
955
- } else if (typeof lastMessage.content === "string") lastMessage.content = [{
956
- type: "text",
957
- text: lastMessage.content,
958
- cache_control: cacheControl
959
- }];
960
- }
961
- }
962
- return params;
963
- }
964
- function convertTools(tools, isOAuthToken) {
965
- if (!tools) return [];
966
- return tools.map((tool) => {
967
- const jsonSchema = tool.parameters;
968
- return {
969
- name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,
970
- description: tool.description,
971
- input_schema: {
972
- type: "object",
973
- properties: jsonSchema.properties || {},
974
- required: jsonSchema.required || []
975
- }
976
- };
977
- });
978
- }
979
- function mapStopReason(reason) {
980
- switch (reason) {
981
- case "end_turn": return "stop";
982
- case "max_tokens": return "length";
983
- case "tool_use": return "toolUse";
984
- case "pause_turn":
985
- case "stop_sequence": return "stop";
986
- case "refusal":
987
- case "sensitive": return "error";
988
- default: throw new Error("Unhandled Anthropic stop reason");
989
- }
990
- }
991
- function buildParams(model, context, isOAuthToken, options) {
992
- const { cacheControl } = getCacheControl(model.baseUrl, options?.cacheRetention);
993
- const params = {
994
- model: model.id,
995
- messages: convertMessages(context.messages, model, isOAuthToken, cacheControl),
996
- max_tokens: options?.maxTokens || model.maxTokens / 3 | 0,
997
- stream: true
998
- };
999
- if (isOAuthToken) {
1000
- params.system = [{
1001
- type: "text",
1002
- text: "You are Claude Code, Anthropic's official CLI for Claude.",
1003
- ...cacheControl ? { cache_control: cacheControl } : {}
1004
- }];
1005
- if (context.systemPrompt) params.system.push({
1006
- type: "text",
1007
- text: sanitizeSurrogates(context.systemPrompt),
1008
- ...cacheControl ? { cache_control: cacheControl } : {}
1009
- });
1010
- } else if (context.systemPrompt) params.system = [{
1011
- type: "text",
1012
- text: sanitizeSurrogates(context.systemPrompt),
1013
- ...cacheControl ? { cache_control: cacheControl } : {}
1014
- }];
1015
- if (options?.temperature !== void 0 && !options?.thinkingEnabled) params.temperature = options.temperature;
1016
- if (context.tools) params.tools = convertTools(context.tools, isOAuthToken);
1017
- if (options?.thinkingEnabled && model.reasoning) params.thinking = {
1018
- type: "enabled",
1019
- budget_tokens: options.thinkingBudgetTokens || 1024
1020
- };
1021
- if (options?.metadata) {
1022
- const userId = options.metadata.user_id;
1023
- if (typeof userId === "string") params.metadata = { user_id: userId };
625
+ async function executeToolCalls(config, currentContext, message, toolCalls, emit, signal) {
626
+ if (config.toolExecution === "sequential") return executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal);
627
+ return executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal);
1024
628
  }
1025
- if (options?.toolChoice) params.tool_choice = typeof options.toolChoice === "string" ? { type: options.toolChoice } : options.toolChoice;
1026
- return params;
1027
- }
1028
- const streamAnthropic = (model, context, options) => {
1029
- const stream = new AssistantMessageEventStream();
1030
- (async () => {
1031
- const output = {
1032
- role: "assistant",
1033
- parts: [],
1034
- protocol: model.protocol,
1035
- provider: model.provider,
1036
- model: model.id,
1037
- usage: {
1038
- input: 0,
1039
- output: 0,
1040
- cacheRead: 0,
1041
- cacheWrite: 0,
1042
- totalTokens: 0,
1043
- cost: {
1044
- input: 0,
1045
- output: 0,
1046
- cacheRead: 0,
1047
- cacheWrite: 0,
1048
- total: 0
1049
- }
1050
- },
1051
- stopReason: "stop",
1052
- time: {
1053
- created: Date.now(),
1054
- completed: Date.now()
1055
- }
1056
- };
1057
- try {
1058
- let client;
1059
- let isOAuthToken;
1060
- if (options?.client) {
1061
- client = options.client;
1062
- isOAuthToken = false;
629
+ async function executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal) {
630
+ for (const toolCall of toolCalls) {
631
+ const toolCallInFlight = {
632
+ callID: toolCall.callID,
633
+ name: toolCall.name,
634
+ rawArgs: toolCall.arguments
635
+ };
636
+ await emitEvent(emit, {
637
+ type: "tool.execution.start",
638
+ ...toolCallInFlight
639
+ });
640
+ const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
641
+ if (prepared.error) {
642
+ const error = {
643
+ status: "error",
644
+ result: {
645
+ content: [{
646
+ type: "text",
647
+ text: prepared.error.message
648
+ }],
649
+ details: prepared.error.details,
650
+ isError: true
651
+ }
652
+ };
653
+ await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
1063
654
  } else {
1064
- const apiKey = options?.apiKey ?? getEnvApiKey(model.provider);
1065
- if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
1066
- const created = createClient(model, apiKey, options?.interleavedThinking ?? true, options?.headers);
1067
- client = created.client;
1068
- isOAuthToken = created.isOAuthToken;
655
+ const runnable = {
656
+ toolCall,
657
+ runnable: prepared.runnable,
658
+ tool: prepared.tool
659
+ };
660
+ const executed = await executeRunnableToolCall(runnable, message, emit, signal);
661
+ if (executed.error) {
662
+ const error = {
663
+ status: "error",
664
+ result: {
665
+ content: [{
666
+ type: "text",
667
+ text: executed.error.message
668
+ }],
669
+ details: executed.error.details,
670
+ isError: true
671
+ }
672
+ };
673
+ await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
674
+ } else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
1069
675
  }
1070
- let params = buildParams(model, context, isOAuthToken, options);
1071
- const nextParams = await options?.onPayload?.(params, model);
1072
- if (nextParams !== void 0) params = nextParams;
1073
- const anthropicStream = client.messages.stream({
1074
- ...params,
1075
- stream: true
1076
- }, { signal: options?.signal });
1077
- stream.push({
1078
- type: "start",
1079
- partial: output
676
+ }
677
+ }
678
+ async function executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal) {
679
+ const runnables = [];
680
+ for (const toolCall of toolCalls) {
681
+ const toolCallInFlight = {
682
+ callID: toolCall.callID,
683
+ name: toolCall.name,
684
+ rawArgs: toolCall.arguments
685
+ };
686
+ await emitEvent(emit, {
687
+ type: "tool.execution.start",
688
+ ...toolCallInFlight
1080
689
  });
1081
- const blocks = output.parts;
1082
- for await (const event of anthropicStream) if (event.type === "message_start") {
1083
- output.responseID = event.message.id;
1084
- output.usage.input = event.message.usage.input_tokens || 0;
1085
- output.usage.output = event.message.usage.output_tokens || 0;
1086
- output.usage.cacheRead = event.message.usage.cache_read_input_tokens || 0;
1087
- output.usage.cacheWrite = event.message.usage.cache_creation_input_tokens || 0;
1088
- output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
1089
- Model.calculateCost(model, output.usage);
1090
- } else if (event.type === "content_block_start") {
1091
- if (event.content_block.type === "text") {
1092
- const block = {
1093
- type: "text",
1094
- text: "",
1095
- index: event.index
1096
- };
1097
- output.parts.push(block);
1098
- stream.push({
1099
- type: "text.start",
1100
- partIndex: output.parts.length - 1,
1101
- partial: output
690
+ const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
691
+ if (prepared.error) {
692
+ const error = {
693
+ status: "error",
694
+ result: {
695
+ content: [{
696
+ type: "text",
697
+ text: prepared.error.message
698
+ }],
699
+ details: prepared.error.details,
700
+ isError: true
701
+ }
702
+ };
703
+ await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
704
+ } else runnables.push({
705
+ toolCall,
706
+ runnable: prepared.runnable,
707
+ tool: prepared.tool
708
+ });
709
+ }
710
+ const runningExecutions = runnables.map((runnable) => ({
711
+ runnable,
712
+ execution: executeRunnableToolCall(runnable, message, emit, signal)
713
+ }));
714
+ for (const running of runningExecutions) {
715
+ const executed = await running.execution;
716
+ const { runnable } = running;
717
+ if (executed.error) {
718
+ const error = {
719
+ status: "error",
720
+ result: {
721
+ content: [{
722
+ type: "text",
723
+ text: executed.error.message
724
+ }],
725
+ details: executed.error.details,
726
+ isError: true
727
+ }
728
+ };
729
+ await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
730
+ } else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
731
+ }
732
+ }
733
+ async function prepareToolCall(config, currentContext, assistantMessage, toolCallInFlight, signal) {
734
+ const tool = currentContext.tools?.find((t) => t.name === toolCallInFlight.name);
735
+ if (!tool) return {
736
+ error: {
737
+ kind: "error",
738
+ message: `Tool ${toolCallInFlight.name} Not Found`
739
+ },
740
+ runnable: toolCallInFlight
741
+ };
742
+ try {
743
+ toolCallInFlight.args = validateToolArguments(tool, toolCallInFlight);
744
+ if (config.beforeToolExecution) {
745
+ const beforeResult = await config.beforeToolExecution({
746
+ context: currentContext,
747
+ assistantMessage,
748
+ toolCall: toolCallInFlight
749
+ }, signal);
750
+ if (beforeResult?.block) return {
751
+ error: {
752
+ kind: "blocked",
753
+ message: beforeResult?.reason || "Tool Execution was Blocked",
754
+ details: beforeResult?.details
755
+ },
756
+ runnable: toolCallInFlight
757
+ };
758
+ }
759
+ return {
760
+ runnable: toolCallInFlight,
761
+ tool
762
+ };
763
+ } catch (err) {
764
+ return {
765
+ error: {
766
+ kind: "error",
767
+ message: err instanceof Error ? err.message : String(err)
768
+ },
769
+ runnable: toolCallInFlight
770
+ };
771
+ }
772
+ }
773
+ async function executeRunnableToolCall(toolCallRunnable, message, emit, signal) {
774
+ const { toolCall, runnable, tool } = toolCallRunnable;
775
+ const { partIndex, ...pendingPart } = toolCall;
776
+ try {
777
+ return { result: await tool.execute(runnable.callID, runnable.args, signal, async (result) => {
778
+ const runningResult = {
779
+ status: "running",
780
+ partial: result.partial
781
+ };
782
+ await emitEvent(emit, {
783
+ type: "tool.execution.update",
784
+ ...runnable,
785
+ ...runningResult
786
+ });
787
+ const runningPart = {
788
+ ...pendingPart,
789
+ ...runningResult
790
+ };
791
+ message.parts[partIndex] = runningPart;
792
+ await emitEvent(emit, {
793
+ type: "message.part.update",
794
+ message,
795
+ partIndex,
796
+ part: runningPart,
797
+ source: "tool"
798
+ });
799
+ }) };
800
+ } catch (err) {
801
+ return { error: { message: err instanceof Error ? err.message : String(err) } };
802
+ }
803
+ }
804
+ async function emitToolCallTerminalResult(toolCall, runnable, message, emit, result) {
805
+ const { partIndex, ...pendingPart } = toolCall;
806
+ const { time } = pendingPart;
807
+ await emitEvent(emit, {
808
+ type: "tool.execution.end",
809
+ ...runnable,
810
+ ...result
811
+ });
812
+ const part = {
813
+ ...pendingPart,
814
+ ...result,
815
+ time: {
816
+ start: time.start,
817
+ end: Date.now()
818
+ }
819
+ };
820
+ message.parts[partIndex] = part;
821
+ await emitEvent(emit, {
822
+ type: "message.part.start",
823
+ message,
824
+ partIndex,
825
+ part
826
+ });
827
+ await emitEvent(emit, {
828
+ type: "message.part.end",
829
+ message,
830
+ partIndex,
831
+ part
832
+ });
833
+ await emitEvent(emit, {
834
+ type: "message.update",
835
+ message
836
+ });
837
+ }
838
+ async function finalizeExecutedToolCall(config, currentContext, toolCall, runnable, message, emit, result, signal) {
839
+ if (config.afterToolExecution) {
840
+ const afterResult = await config.afterToolExecution({
841
+ context: currentContext,
842
+ assistantMessage: message,
843
+ toolCall: runnable,
844
+ result
845
+ }, signal);
846
+ if (afterResult) {
847
+ await emitToolCallTerminalResult(toolCall, runnable, message, emit, afterResult);
848
+ return;
849
+ }
850
+ }
851
+ await emitToolCallTerminalResult(toolCall, runnable, message, emit, result);
852
+ }
853
+ /**
854
+ * Main core loop shared by run and runContinue.
855
+ */
856
+ async function runLoop(config, currentContext, newMessages, emit, streamFn, signal) {
857
+ let firstTurn = true;
858
+ let pendingMessages = await config.getSteeringMessages?.() || [];
859
+ while (true) {
860
+ let hasMoreToolCalls = true;
861
+ while (hasMoreToolCalls || pendingMessages.length > 0) {
862
+ if (!firstTurn) await emitEvent(emit, { type: "turn.start" });
863
+ else firstTurn = false;
864
+ if (pendingMessages.length > 0) {
865
+ for (const message of pendingMessages) {
866
+ await emitEvent(emit, {
867
+ type: "message.start",
868
+ message
869
+ });
870
+ for (const [partIndex, part] of message.parts.entries()) {
871
+ await emitEvent(emit, {
872
+ type: "message.part.start",
873
+ message,
874
+ partIndex,
875
+ part
876
+ });
877
+ await emitEvent(emit, {
878
+ type: "message.part.end",
879
+ message,
880
+ partIndex,
881
+ part
882
+ });
883
+ await emitEvent(emit, {
884
+ type: "message.update",
885
+ message
886
+ });
887
+ }
888
+ await emitEvent(emit, {
889
+ type: "message.end",
890
+ message
891
+ });
892
+ currentContext.messages.push(message);
893
+ newMessages.push(message);
894
+ }
895
+ pendingMessages = [];
896
+ }
897
+ const message = await streamAssistantResponse(config, currentContext, emit, streamFn, signal);
898
+ if (message.stopReason === "error" || message.stopReason === "aborted") {
899
+ currentContext.messages.push(message);
900
+ newMessages.push(message);
901
+ await emitEvent(emit, {
902
+ type: "turn.end",
903
+ message
1102
904
  });
1103
- } else if (event.content_block.type === "thinking") {
1104
- const block = {
1105
- type: "thinking",
1106
- thinking: "",
1107
- thinkingSignature: "",
1108
- index: event.index
1109
- };
1110
- output.parts.push(block);
1111
- stream.push({
1112
- type: "thinking.start",
1113
- partIndex: output.parts.length - 1,
1114
- partial: output
1115
- });
1116
- } else if (event.content_block.type === "redacted_thinking") {
1117
- const block = {
1118
- type: "thinking",
1119
- thinking: "[Reasoning redacted]",
1120
- thinkingSignature: event.content_block.data,
1121
- redacted: true,
1122
- index: event.index
1123
- };
1124
- output.parts.push(block);
1125
- stream.push({
1126
- type: "thinking.start",
1127
- partIndex: output.parts.length - 1,
1128
- partial: output
1129
- });
1130
- } else if (event.content_block.type === "tool_use") {
1131
- const block = {
1132
- type: "toolCall",
1133
- callID: event.content_block.id,
1134
- name: isOAuthToken ? fromClaudeCodeName(event.content_block.name, context.tools) : event.content_block.name,
1135
- arguments: event.content_block.input ?? {},
1136
- partialJson: "",
1137
- status: "pending",
1138
- time: {
1139
- start: Date.now(),
1140
- end: Date.now()
1141
- },
1142
- index: event.index
1143
- };
1144
- output.parts.push(block);
1145
- stream.push({
1146
- type: "toolcall.start",
1147
- partIndex: output.parts.length - 1,
1148
- partial: output
905
+ await emitEvent(emit, {
906
+ type: "agent.end",
907
+ messages: newMessages
1149
908
  });
909
+ return;
1150
910
  }
1151
- } else if (event.type === "content_block_delta") {
1152
- const partIndex = blocks.findIndex((b) => b.index === event.index);
1153
- const block = blocks[partIndex];
1154
- if (!block) continue;
1155
- if (event.delta.type === "text_delta" && block.type === "text") {
1156
- block.text += event.delta.text;
1157
- stream.push({
1158
- type: "text.delta",
1159
- partIndex,
1160
- delta: event.delta.text,
1161
- partial: output
1162
- });
1163
- } else if (event.delta.type === "thinking_delta" && block.type === "thinking") {
1164
- block.thinking += event.delta.thinking;
1165
- stream.push({
1166
- type: "thinking.delta",
1167
- partIndex,
1168
- delta: event.delta.thinking,
1169
- partial: output
1170
- });
1171
- } else if (event.delta.type === "input_json_delta" && block.type === "toolCall") {
1172
- block.partialJson += event.delta.partial_json;
1173
- block.arguments = parseStreamingJson(block.partialJson);
1174
- stream.push({
1175
- type: "toolcall.delta",
1176
- partIndex,
1177
- delta: event.delta.partial_json,
1178
- partial: output
1179
- });
1180
- } else if (event.delta.type === "signature_delta" && block.type === "thinking") {
1181
- block.thinkingSignature = block.thinkingSignature || "";
1182
- block.thinkingSignature += event.delta.signature;
911
+ const toolCalls = message.parts.map((part, index) => ({
912
+ ...part,
913
+ partIndex: index
914
+ })).filter((part) => part.type === "toolCall" && part.status === "pending");
915
+ hasMoreToolCalls = toolCalls.length > 0;
916
+ if (hasMoreToolCalls) await executeToolCalls(config, currentContext, message, toolCalls, emit, signal);
917
+ currentContext.messages.push(message);
918
+ newMessages.push(message);
919
+ await emitEvent(emit, {
920
+ type: "turn.end",
921
+ message
922
+ });
923
+ pendingMessages = await config.getSteeringMessages?.() || [];
924
+ }
925
+ const followUpMessages = await config.getFollowUpMessages?.() || [];
926
+ if (followUpMessages.length > 0) {
927
+ pendingMessages = followUpMessages;
928
+ continue;
929
+ }
930
+ break;
931
+ }
932
+ await emitEvent(emit, {
933
+ type: "agent.end",
934
+ messages: newMessages
935
+ });
936
+ }
937
+ async function runAgentLoop(config, context, prompts, emit, streamFn, signal) {
938
+ /**
939
+ * maintain 2 arrays to represent state, `context` the actual full state sent to the LLM,
940
+ * `newMessages` for intermediate current loop state.
941
+ * think of newMessages as subset of context.messages for the running current loop.
942
+ * they do move at the same speed staring with initial user prompts.
943
+ * */
944
+ const newMessages = [...prompts];
945
+ const currentContext = {
946
+ ...context,
947
+ messages: [...context.messages, ...prompts]
948
+ };
949
+ await emitEvent(emit, { type: "agent.start" });
950
+ await emitEvent(emit, { type: "turn.start" });
951
+ for (const prompt of prompts) {
952
+ await emitEvent(emit, {
953
+ type: "message.start",
954
+ message: prompt
955
+ });
956
+ for (const [partIndex, part] of prompt.parts.entries()) {
957
+ await emitEvent(emit, {
958
+ type: "message.part.start",
959
+ message: prompt,
960
+ partIndex,
961
+ part
962
+ });
963
+ await emitEvent(emit, {
964
+ type: "message.part.end",
965
+ message: prompt,
966
+ partIndex,
967
+ part
968
+ });
969
+ }
970
+ await emitEvent(emit, {
971
+ type: "message.end",
972
+ message: prompt
973
+ });
974
+ }
975
+ await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
976
+ return newMessages;
977
+ }
978
+ _Loop.runAgentLoop = runAgentLoop;
979
+ async function runAgentLoopContinue(config, context, emit, streamFn, signal) {
980
+ if (context.messages.length === 0) throw new AgentContextError({ message: "connot continue: no messages in context" });
981
+ if (context.messages[context.messages.length - 1]?.role === "assistant") throw new AgentContextError({ message: "cannot continue from message role: assistant" });
982
+ const newMessages = [];
983
+ const currentContext = { ...context };
984
+ await emitEvent(emit, { type: "agent.start" });
985
+ await emitEvent(emit, { type: "turn.start" });
986
+ await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
987
+ return newMessages;
988
+ }
989
+ _Loop.runAgentLoopContinue = runAgentLoopContinue;
990
+ function run(config, context, prompts, streamFn, signal) {
991
+ const stream = createAgentStream();
992
+ runAgentLoop(config, context, prompts, async (event) => {
993
+ stream.push(event);
994
+ }, streamFn, signal).then((messages) => {
995
+ stream.end(messages);
996
+ });
997
+ return stream;
998
+ }
999
+ _Loop.run = run;
1000
+ function runContinue(config, context, streamFn, signal) {
1001
+ const stream = createAgentStream();
1002
+ runAgentLoopContinue(config, context, async (event) => {
1003
+ stream.push(event);
1004
+ }, streamFn, signal).then((messages) => {
1005
+ stream.end(messages);
1006
+ });
1007
+ return stream;
1008
+ }
1009
+ _Loop.runContinue = runContinue;
1010
+ })(Loop || (Loop = {}));
1011
+ //#endregion
1012
+ //#region src/agent.ts
1013
+ const agent = {
1014
+ loop: Loop.run,
1015
+ loopContinue: Loop.runContinue
1016
+ };
1017
+ //#endregion
1018
+ //#region src/message/message.ts
1019
+ let Message;
1020
+ (function(_Message) {
1021
+ const TextContentSchema = _Message.TextContentSchema = Type.Object({
1022
+ type: Type.Literal("text"),
1023
+ text: Type.String(),
1024
+ textSignature: Type.Optional(Type.String())
1025
+ });
1026
+ const ImageContentSchema = _Message.ImageContentSchema = Type.Object({
1027
+ type: Type.Literal("image"),
1028
+ data: Type.String(),
1029
+ mimeType: Type.String()
1030
+ });
1031
+ const ThinkingContentSchema = _Message.ThinkingContentSchema = Type.Object({
1032
+ type: Type.Literal("thinking"),
1033
+ thinking: Type.String(),
1034
+ thinkingSignature: Type.Optional(Type.String()),
1035
+ redacted: Type.Optional(Type.Boolean())
1036
+ });
1037
+ const ToolCallBaseSchema = _Message.ToolCallBaseSchema = Type.Object({
1038
+ type: Type.Literal("toolCall"),
1039
+ callID: Type.String(),
1040
+ name: Type.String(),
1041
+ arguments: Type.Record(Type.String(), Type.Any()),
1042
+ thoughtSignature: Type.Optional(Type.String()),
1043
+ time: Type.Object({
1044
+ start: Type.Number(),
1045
+ end: Type.Number()
1046
+ })
1047
+ });
1048
+ const ToolSuccessResult = _Message.ToolSuccessResult = Type.Object({
1049
+ content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
1050
+ details: Type.Optional(Type.Any()),
1051
+ isError: Type.Literal(false)
1052
+ });
1053
+ const ToolErrorResult = _Message.ToolErrorResult = Type.Object({
1054
+ content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
1055
+ details: Type.Optional(Type.Any()),
1056
+ isError: Type.Literal(true)
1057
+ });
1058
+ const ToolRunningPartial = _Message.ToolRunningPartial = Type.Object({
1059
+ content: Type.Optional(Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))),
1060
+ details: Type.Optional(Type.Any())
1061
+ });
1062
+ const ToolStatusEnum = _Message.ToolStatusEnum = {
1063
+ pending: "pending",
1064
+ running: "running",
1065
+ completed: "completed",
1066
+ error: "error",
1067
+ skipped: "skipped",
1068
+ aborted: "aborted"
1069
+ };
1070
+ const ToolRunningSchema = _Message.ToolRunningSchema = Type.Object({
1071
+ status: Type.Literal(ToolStatusEnum.running),
1072
+ partial: Type.Optional(ToolRunningPartial)
1073
+ });
1074
+ const ToolCompletedSchema = _Message.ToolCompletedSchema = Type.Object({
1075
+ status: Type.Literal(ToolStatusEnum.completed),
1076
+ result: ToolSuccessResult
1077
+ });
1078
+ const ToolErrorSchema = _Message.ToolErrorSchema = Type.Object({
1079
+ status: Type.Literal(ToolStatusEnum.error),
1080
+ result: ToolErrorResult
1081
+ });
1082
+ const ToolSkippedSchema = _Message.ToolSkippedSchema = Type.Object({
1083
+ status: Type.Literal(ToolStatusEnum.skipped),
1084
+ result: ToolErrorResult
1085
+ });
1086
+ const ToolAbortedSchema = _Message.ToolAbortedSchema = Type.Object({
1087
+ status: Type.Literal(ToolStatusEnum.aborted),
1088
+ result: ToolErrorResult
1089
+ });
1090
+ const ToolCallPendingPart = Type.Intersect([ToolCallBaseSchema, Type.Object({ status: Type.Literal(ToolStatusEnum.pending) })]);
1091
+ const ToolCallRunningPart = Type.Composite([ToolCallBaseSchema, ToolRunningSchema]);
1092
+ const ToolCallCompletedPart = Type.Composite([ToolCallBaseSchema, ToolCompletedSchema]);
1093
+ const ToolCallErrorPart = Type.Composite([ToolCallBaseSchema, ToolErrorSchema]);
1094
+ const ToolCallSkippedPart = Type.Composite([ToolCallBaseSchema, ToolSkippedSchema]);
1095
+ const ToolCallAbortedPart = Type.Composite([ToolCallBaseSchema, ToolAbortedSchema]);
1096
+ const ToolCallSchema = _Message.ToolCallSchema = Type.Union([
1097
+ ToolCallPendingPart,
1098
+ ToolCallRunningPart,
1099
+ ToolCallCompletedPart,
1100
+ ToolCallErrorPart,
1101
+ ToolCallSkippedPart,
1102
+ ToolCallAbortedPart
1103
+ ]);
1104
+ const UsageSchema = _Message.UsageSchema = Type.Object({
1105
+ input: Type.Number(),
1106
+ output: Type.Number(),
1107
+ cacheRead: Type.Number(),
1108
+ cacheWrite: Type.Number(),
1109
+ totalTokens: Type.Number(),
1110
+ cost: Type.Object({
1111
+ input: Type.Number(),
1112
+ output: Type.Number(),
1113
+ cacheRead: Type.Number(),
1114
+ cacheWrite: Type.Number(),
1115
+ total: Type.Number()
1116
+ })
1117
+ });
1118
+ const StopReasonSchema = _Message.StopReasonSchema = Type.Union([
1119
+ Type.Literal("stop"),
1120
+ Type.Literal("length"),
1121
+ Type.Literal("toolUse"),
1122
+ Type.Literal("error"),
1123
+ Type.Literal("aborted")
1124
+ ]);
1125
+ const UserMessageSchema = _Message.UserMessageSchema = Type.Object({
1126
+ messageId: Type.String(),
1127
+ role: Type.Literal("user"),
1128
+ time: Type.Object({ created: Type.Number() }),
1129
+ parts: Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))
1130
+ });
1131
+ const AssistantMessageSchema = _Message.AssistantMessageSchema = Type.Object({
1132
+ role: Type.Literal("assistant"),
1133
+ protocol: Model.KnownProtocolSchema,
1134
+ provider: Provider.Info,
1135
+ model: Type.String(),
1136
+ usage: UsageSchema,
1137
+ stopReason: StopReasonSchema,
1138
+ errorMessage: Type.Optional(Type.String()),
1139
+ time: Type.Object({
1140
+ created: Type.Number(),
1141
+ completed: Type.Number()
1142
+ }),
1143
+ parts: Type.Array(Type.Union([
1144
+ TextContentSchema,
1145
+ ImageContentSchema,
1146
+ ThinkingContentSchema,
1147
+ ToolCallSchema
1148
+ ])),
1149
+ responseId: Type.Optional(Type.String()),
1150
+ messageId: Type.String()
1151
+ });
1152
+ const MessageSchema = _Message.MessageSchema = Type.Union([UserMessageSchema, AssistantMessageSchema]);
1153
+ function createMessageId() {
1154
+ return uuidv7();
1155
+ }
1156
+ _Message.createMessageId = createMessageId;
1157
+ function createUserMessage(message) {
1158
+ const { messageId = createMessageId(), ...rest } = message;
1159
+ return {
1160
+ messageId,
1161
+ ...rest
1162
+ };
1163
+ }
1164
+ _Message.createUserMessage = createUserMessage;
1165
+ function createAssistantMessage(message) {
1166
+ const { messageId = createMessageId(), ...rest } = message;
1167
+ return {
1168
+ messageId,
1169
+ ...rest
1170
+ };
1171
+ }
1172
+ _Message.createAssistantMessage = createAssistantMessage;
1173
+ const ToolSchema = _Message.ToolSchema = Type.Object({
1174
+ name: Type.String(),
1175
+ description: Type.String(),
1176
+ parameters: Type.Unsafe()
1177
+ });
1178
+ function defineTool(tool) {
1179
+ return tool;
1180
+ }
1181
+ _Message.defineTool = defineTool;
1182
+ _Message.ContextSchema = Type.Object({
1183
+ systemPrompt: Type.Optional(Type.String()),
1184
+ messages: Type.Array(MessageSchema),
1185
+ tools: Type.Optional(Type.Array(ToolSchema))
1186
+ });
1187
+ function transformMessages(messages, model, normalizeToolCallId) {
1188
+ const toolCallIDMap = /* @__PURE__ */ new Map();
1189
+ const transformed = [];
1190
+ for (const msg of messages) {
1191
+ if (msg.role === "user") {
1192
+ transformed.push(msg);
1193
+ continue;
1194
+ }
1195
+ const assistantMsg = msg;
1196
+ const isSameModel = assistantMsg.provider.id === model.provider.id && assistantMsg.protocol === model.protocol && assistantMsg.model === model.id;
1197
+ const parts = assistantMsg.parts.flatMap((block) => {
1198
+ if (block.type === "thinking") {
1199
+ if (isSameModel && block.thinkingSignature) return block;
1200
+ if (!block.thinking || block.thinking.trim() === "") return [];
1201
+ if (isSameModel) return block;
1202
+ return {
1203
+ type: "text",
1204
+ text: block.thinking
1205
+ };
1183
1206
  }
1184
- } else if (event.type === "content_block_stop") {
1185
- const partIndex = blocks.findIndex((b) => b.index === event.index);
1186
- const block = blocks[partIndex];
1187
- if (block) {
1188
- delete block.index;
1189
- if (block.type === "text") stream.push({
1190
- type: "text.end",
1191
- partIndex,
1192
- content: block.text,
1193
- partial: output
1194
- });
1195
- else if (block.type === "thinking") stream.push({
1196
- type: "thinking.end",
1197
- partIndex,
1198
- content: block.thinking,
1199
- partial: output
1200
- });
1201
- else if (block.type === "toolCall") {
1202
- block.arguments = parseStreamingJson(block.partialJson);
1203
- delete block.partialJson;
1204
- stream.push({
1205
- type: "toolcall.end",
1206
- partIndex,
1207
- toolCall: block,
1208
- partial: output
1209
- });
1207
+ if (block.type === "text") {
1208
+ if (isSameModel) return block;
1209
+ return {
1210
+ type: "text",
1211
+ text: block.text
1212
+ };
1213
+ }
1214
+ if (block.type === "toolCall") {
1215
+ const toolCall = block;
1216
+ let normalizedToolCall = toolCall;
1217
+ if (!isSameModel && toolCall.thoughtSignature) {
1218
+ normalizedToolCall = { ...toolCall };
1219
+ delete normalizedToolCall.thoughtSignature;
1220
+ }
1221
+ const normalizedID = toolCallIDMap.get(block.callID);
1222
+ if (normalizedID && normalizedID !== block.callID) normalizedToolCall = {
1223
+ ...normalizedToolCall,
1224
+ callID: normalizedID
1225
+ };
1226
+ else if (!isSameModel && normalizeToolCallId) {
1227
+ const nextID = normalizeToolCallId(toolCall.callID, model, assistantMsg);
1228
+ if (nextID !== toolCall.callID) {
1229
+ toolCallIDMap.set(toolCall.callID, nextID);
1230
+ normalizedToolCall = {
1231
+ ...normalizedToolCall,
1232
+ callID: nextID
1233
+ };
1234
+ }
1210
1235
  }
1236
+ return normalizedToolCall;
1211
1237
  }
1212
- } else if (event.type === "message_delta") {
1213
- if (event.delta.stop_reason) output.stopReason = mapStopReason(event.delta.stop_reason);
1214
- if (event.usage.input_tokens != null) output.usage.input = event.usage.input_tokens;
1215
- if (event.usage.output_tokens != null) output.usage.output = event.usage.output_tokens;
1216
- if (event.usage.cache_read_input_tokens != null) output.usage.cacheRead = event.usage.cache_read_input_tokens;
1217
- if (event.usage.cache_creation_input_tokens != null) output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
1218
- output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
1219
- Model.calculateCost(model, output.usage);
1220
- }
1221
- if (options?.signal?.aborted) throw new Error("Request was aborted");
1222
- if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
1223
- output.time.completed = Date.now();
1224
- stream.push({
1225
- type: "done",
1226
- reason: output.stopReason,
1227
- message: output
1238
+ return block;
1228
1239
  });
1229
- stream.end();
1230
- } catch (error) {
1231
- for (const block of output.parts) delete block.index;
1232
- output.time.completed = Date.now();
1233
- output.stopReason = options?.signal?.aborted ? "aborted" : "error";
1234
- output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
1235
- stream.push({
1236
- type: "error",
1237
- reason: output.stopReason,
1238
- error: output
1240
+ transformed.push({
1241
+ ...assistantMsg,
1242
+ parts
1239
1243
  });
1240
- stream.end();
1241
1244
  }
1242
- })();
1243
- return stream;
1244
- };
1245
- const streamSimpleAnthropic = (model, context, options) => {
1246
- const apiKey = options?.apiKey || getEnvApiKey(model.provider);
1247
- if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
1248
- const base = Stream.buildBaseOptions(model, options, apiKey);
1249
- if (!options?.reasoning) return streamAnthropic(model, context, {
1250
- ...base,
1251
- thinkingEnabled: false
1252
- });
1253
- const adjusted = Stream.adjustMaxTokensForThinking(base.maxTokens || 0, model.maxTokens, options.reasoning, options.thinkingBudgets);
1254
- return streamAnthropic(model, context, {
1255
- ...base,
1256
- maxTokens: adjusted.maxTokens,
1257
- thinkingEnabled: true,
1258
- thinkingBudgetTokens: adjusted.thinkingBudget
1259
- });
1260
- };
1261
- //#endregion
1262
- //#region src/provider/register.ts
1263
- const providerModules = [["providers/anthropic/index.ts", {
1264
- protocol: Model.KnownProtocolEnum.anthropicMessages,
1265
- stream: streamAnthropic,
1266
- streamSimple: streamSimpleAnthropic
1267
- }]];
1268
- for (const [path, provider] of providerModules) Stream.registerProtocolProvider(provider, path);
1245
+ return transformed;
1246
+ }
1247
+ _Message.transformMessages = transformMessages;
1248
+ })(Message || (Message = {}));
1269
1249
  //#endregion
1270
- //#region src/utils/validation.ts
1250
+ //#region src/utils/jsonparse.ts
1271
1251
  /**
1272
- * @description Validation powered by ajv, TypeBox natively doesn't provide data validation.
1252
+ * @description Helper function to parse partial jsons during LLM streaming.
1273
1253
  */
1274
- const ajv = new Ajv({
1275
- allErrors: true,
1276
- strict: false,
1277
- coerceTypes: true
1278
- });
1279
- addFormats(ajv);
1280
1254
  /**
1281
- * Finds a tool by name and validates the tool call arguments against its TypeBox schema
1255
+ * Attempts to parse potentially incomplete JSON during streaming.
1256
+ * Always returns a valid object, even if the JSON is incomplete.
1257
+ *
1258
+ * @param partialJson The partial JSON string from streaming
1259
+ * @returns Parsed object or empty object if parsing fails
1282
1260
  */
1283
- function validateToolCall(tools, toolExecution) {
1284
- const tool = tools.find((t) => t.name === toolExecution.name);
1285
- if (!tool) throw new Error(`Tool "${toolExecution.name}" not found`);
1286
- return validateToolArguments(tool, toolExecution);
1261
+ function parseStreamingJson(partialJson) {
1262
+ if (!partialJson || partialJson.trim() === "") return {};
1263
+ try {
1264
+ return JSON.parse(partialJson);
1265
+ } catch {
1266
+ try {
1267
+ return parse(partialJson) ?? {};
1268
+ } catch {
1269
+ return {};
1270
+ }
1271
+ }
1287
1272
  }
1273
+ //#endregion
1274
+ //#region src/utils/sanitize.ts
1288
1275
  /**
1289
- * Validates tool call arguments against the tool's TypeBox schema
1276
+ * Removes unpaired Unicode surrogate characters from a string.
1277
+ *
1278
+ * Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
1279
+ * or vice versa) cause JSON serialization errors in many API providers.
1280
+ *
1281
+ * Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
1282
+ * surrogates and will NOT be affected by this function.
1283
+ *
1284
+ * @param text - The text to sanitize
1285
+ * @returns The sanitized text with unpaired surrogates removed
1286
+ *
1287
+ * @example
1288
+ * // Valid emoji (properly paired surrogates) are preserved
1289
+ * sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
1290
+ *
1291
+ * // Unpaired high surrogate is removed
1292
+ * const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
1293
+ * sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
1290
1294
  */
1291
- function validateToolArguments(tool, toolExecution) {
1292
- const validate = ajv.compile(tool.parameters);
1293
- const args = structuredClone(toolExecution.rawArgs);
1294
- if (validate(args)) return args;
1295
- const errors = validate.errors?.map((err) => {
1296
- return ` - ${err.instancePath ? err.instancePath.substring(1) : err.params?.missingProperty || "root"}: ${err.message}`;
1297
- }).join("\n") || "Unknown Validation Error";
1298
- throw new Error([
1299
- `Validation Failed For Tool "${toolExecution.name}"`,
1300
- `${errors}\n`,
1301
- "Received Arguments:",
1302
- `${JSON.stringify(toolExecution.rawArgs, null, 2)}\n`
1303
- ].join("\n"));
1295
+ function sanitizeSurrogates(text) {
1296
+ return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
1304
1297
  }
1305
1298
  //#endregion
1306
- //#region src/agent/loop.ts
1307
- let Loop;
1308
- (function(_Loop) {
1309
- function createAgentStream() {
1310
- return new EventStream((event) => event.type === "agent.end", (event) => event.type === "agent.end" ? event.messages : []);
1311
- }
1312
- const AgentContextError = _Loop.AgentContextError = NamedError.create("AgentContextError", Type.Object({ message: Type.String() }));
1313
- function snapshotAssistantMessage(message) {
1314
- return structuredClone(message);
1315
- }
1316
- function snapshotAssistantPartEvent(message, partIndex) {
1317
- const snapshot = snapshotAssistantMessage(message);
1318
- const part = snapshot.parts[partIndex];
1319
- if (!part) throw new Error(`Assistant message part at index ${partIndex} was not found`);
1320
- return {
1321
- message: snapshot,
1322
- part
1323
- };
1324
- }
1325
- async function emitEvent(emit, event) {
1326
- await emit(event);
1299
+ //#region src/provider/providers/anthropic/index.ts
1300
+ function getEnvApiKey(_provider) {}
1301
+ function resolveCacheRetention(cacheRetention) {
1302
+ if (cacheRetention) return cacheRetention;
1303
+ if (typeof process !== "undefined" && process.env.CODEWORK_CACHE_RETENTION === "long") return "long";
1304
+ return "short";
1305
+ }
1306
+ function getCacheControl(baseUrl, cacheRetention) {
1307
+ const retention = resolveCacheRetention(cacheRetention);
1308
+ if (retention === "none") return { retention };
1309
+ const ttl = retention === "long" && baseUrl.includes("api.anthropic.com") ? "1h" : void 0;
1310
+ return {
1311
+ retention,
1312
+ cacheControl: {
1313
+ type: "ephemeral",
1314
+ ...ttl && { ttl }
1315
+ }
1316
+ };
1317
+ }
1318
+ const claudeCodeVersion = "2.1.2";
1319
+ const ccToolLookup = new Map([
1320
+ "Read",
1321
+ "Write",
1322
+ "Edit",
1323
+ "Bash",
1324
+ "Grep",
1325
+ "Glob",
1326
+ "AskUserQuestion",
1327
+ "EnterPlanMode",
1328
+ "ExitPlanMode",
1329
+ "KillShell",
1330
+ "NotebookEdit",
1331
+ "Skill",
1332
+ "Task",
1333
+ "TaskOutput",
1334
+ "TodoWrite",
1335
+ "WebFetch",
1336
+ "WebSearch"
1337
+ ].map((t) => [t.toLowerCase(), t]));
1338
+ const toClaudeCodeName = (name) => ccToolLookup.get(name.toLowerCase()) ?? name;
1339
+ const fromClaudeCodeName = (name, tools) => {
1340
+ if (tools && tools.length > 0) {
1341
+ const lowerName = name.toLowerCase();
1342
+ const matchedTool = tools.find((tool) => tool.name.toLowerCase() === lowerName);
1343
+ if (matchedTool) return matchedTool.name;
1327
1344
  }
1328
- async function streamAssistantResponse(config, context, emit, streamFn, signal) {
1329
- let messages = context.messages;
1330
- if (config.transformContext) messages = await config.transformContext(messages, signal);
1331
- const llmMessages = await config.convertToLlm(messages);
1332
- const llmContext = {
1333
- systemPrompt: context.systemPrompt,
1334
- messages: llmMessages,
1335
- tools: context.tools
1336
- };
1337
- const streamFunction = streamFn || Stream.streamSimple;
1338
- const resolvedApiKey = (config.getApiKey ? await config.getApiKey(config.model.provider) : void 0) || config.apiKey;
1339
- const events = await streamFunction(config.model, llmContext, {
1340
- ...config,
1341
- apiKey: resolvedApiKey,
1342
- signal
1343
- });
1344
- let partialMessage = null;
1345
- let started = false;
1346
- const ensureMessageStarted = async (message) => {
1347
- if (started) return;
1348
- started = true;
1349
- await emitEvent(emit, {
1350
- type: "message.start",
1351
- message: snapshotAssistantMessage(message)
1352
- });
1353
- };
1354
- const emitMessageUpdate = async (message) => {
1355
- await emitEvent(emit, {
1356
- type: "message.update",
1357
- message: snapshotAssistantMessage(message)
1358
- });
1359
- };
1360
- const emitPartStart = async (message, partIndex) => {
1361
- await emitEvent(emit, {
1362
- type: "message.part.start",
1363
- partIndex,
1364
- ...snapshotAssistantPartEvent(message, partIndex)
1365
- });
1345
+ return name;
1346
+ };
1347
+ function convertContentBlocks(content) {
1348
+ if (!content.some((c) => c.type === "image")) return sanitizeSurrogates(content.map((c) => c.text).join("\n"));
1349
+ const blocks = content.map((block) => {
1350
+ if (block.type === "text") return {
1351
+ type: "text",
1352
+ text: sanitizeSurrogates(block.text)
1366
1353
  };
1367
- const emitPartUpdate = async (message, partIndex) => {
1368
- await emitEvent(emit, {
1369
- type: "message.part.update",
1370
- partIndex,
1371
- ...snapshotAssistantPartEvent(message, partIndex),
1372
- source: "llm"
1373
- });
1354
+ return {
1355
+ type: "image",
1356
+ source: {
1357
+ type: "base64",
1358
+ media_type: block.mimeType,
1359
+ data: block.data
1360
+ }
1374
1361
  };
1375
- const emitPartEnd = async (message, partIndex) => {
1376
- await emitEvent(emit, {
1377
- type: "message.part.end",
1378
- partIndex,
1379
- ...snapshotAssistantPartEvent(message, partIndex)
1380
- });
1362
+ });
1363
+ if (!blocks.some((b) => b.type === "text")) blocks.unshift({
1364
+ type: "text",
1365
+ text: "(see attached image)"
1366
+ });
1367
+ return blocks;
1368
+ }
1369
+ function mergeHeaders(...headerSources) {
1370
+ const merged = {};
1371
+ for (const headers of headerSources) if (headers) Object.assign(merged, headers);
1372
+ return merged;
1373
+ }
1374
+ function isOAuthToken(apiKey) {
1375
+ return apiKey.includes("sk-ant-oat");
1376
+ }
1377
+ function createClient(model, apiKey, interleavedThinking, optionsHeaders) {
1378
+ const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
1379
+ if (interleavedThinking) betaFeatures.push("interleaved-thinking-2025-05-14");
1380
+ if (isOAuthToken(apiKey)) {
1381
+ const defaultHeaders = mergeHeaders({
1382
+ accept: "application/json",
1383
+ "anthropic-dangerous-direct-browser-access": "true",
1384
+ "anthropic-beta": `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}`,
1385
+ "user-agent": `claude-cli/${claudeCodeVersion} (external, cli)`,
1386
+ "x-app": "cli"
1387
+ }, model.headers, optionsHeaders);
1388
+ return {
1389
+ client: new Anthropic({
1390
+ apiKey: null,
1391
+ authToken: apiKey,
1392
+ baseURL: model.baseUrl,
1393
+ defaultHeaders,
1394
+ dangerouslyAllowBrowser: true
1395
+ }),
1396
+ isOAuthToken: true
1381
1397
  };
1382
- for await (const event of events) switch (event.type) {
1383
- case "start":
1384
- partialMessage = event.partial;
1385
- await ensureMessageStarted(partialMessage);
1386
- break;
1387
- case "text.start":
1388
- case "thinking.start":
1389
- case "toolcall.start":
1390
- partialMessage = event.partial;
1391
- await ensureMessageStarted(partialMessage);
1392
- await emitPartStart(partialMessage, event.partIndex);
1393
- break;
1394
- case "text.delta":
1395
- case "thinking.delta":
1396
- case "toolcall.delta":
1397
- partialMessage = event.partial;
1398
- await ensureMessageStarted(partialMessage);
1399
- await emitPartUpdate(partialMessage, event.partIndex);
1400
- break;
1401
- case "text.end":
1402
- case "thinking.end":
1403
- case "toolcall.end":
1404
- partialMessage = event.partial;
1405
- await ensureMessageStarted(partialMessage);
1406
- await emitPartEnd(partialMessage, event.partIndex);
1407
- await emitMessageUpdate(partialMessage);
1408
- break;
1409
- case "done":
1410
- await ensureMessageStarted(event.message);
1411
- await emitEvent(emit, {
1412
- type: "message.end",
1413
- message: snapshotAssistantMessage(event.message)
1414
- });
1415
- return event.message;
1416
- case "error":
1417
- await ensureMessageStarted(event.error);
1418
- await emitEvent(emit, {
1419
- type: "message.end",
1420
- message: snapshotAssistantMessage(event.error)
1421
- });
1422
- return event.error;
1423
- }
1424
- const finalMessage = await events.result();
1425
- await ensureMessageStarted(finalMessage);
1426
- await emitEvent(emit, {
1427
- type: "message.end",
1428
- message: snapshotAssistantMessage(finalMessage)
1429
- });
1430
- return finalMessage;
1431
1398
  }
1432
- async function executeToolCalls(config, currentContext, message, toolCalls, emit, signal) {
1433
- if (config.toolExecution === "sequential") return executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal);
1434
- return executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal);
1435
- }
1436
- async function executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal) {
1437
- for (const toolCall of toolCalls) {
1438
- const toolCallInFlight = {
1439
- callID: toolCall.callID,
1440
- name: toolCall.name,
1441
- rawArgs: toolCall.arguments
1442
- };
1443
- await emitEvent(emit, {
1444
- type: "tool.execution.start",
1445
- ...toolCallInFlight
1446
- });
1447
- const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
1448
- if (prepared.error) {
1449
- const error = {
1450
- status: "error",
1451
- result: {
1452
- content: [{
1453
- type: "text",
1454
- text: prepared.error.message
1455
- }],
1456
- details: prepared.error.details,
1457
- isError: true
1458
- }
1459
- };
1460
- await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
1461
- } else {
1462
- const runnable = {
1463
- toolCall,
1464
- runnable: prepared.runnable,
1465
- tool: prepared.tool
1466
- };
1467
- const executed = await executeRunnableToolCall(runnable, message, emit, signal);
1468
- if (executed.error) {
1469
- const error = {
1470
- status: "error",
1471
- result: {
1472
- content: [{
1473
- type: "text",
1474
- text: executed.error.message
1475
- }],
1476
- details: executed.error.details,
1477
- isError: true
1478
- }
1479
- };
1480
- await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
1481
- } else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
1482
- }
1399
+ const defaultHeaders = mergeHeaders({
1400
+ accept: "application/json",
1401
+ "anthropic-dangerous-direct-browser-access": "true",
1402
+ "anthropic-beta": betaFeatures.join(",")
1403
+ }, model.headers, optionsHeaders);
1404
+ return {
1405
+ client: new Anthropic({
1406
+ apiKey,
1407
+ baseURL: model.baseUrl,
1408
+ dangerouslyAllowBrowser: true,
1409
+ defaultHeaders
1410
+ }),
1411
+ isOAuthToken: false
1412
+ };
1413
+ }
1414
+ function normalizeToolCallId(id) {
1415
+ return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
1416
+ }
1417
+ function assertReplayableAssistant(message) {
1418
+ let sawToolCall = false;
1419
+ let sawNonTerminalToolCall = false;
1420
+ let sawTerminalToolCall = false;
1421
+ for (const part of message.parts) {
1422
+ if (part.type === "image") throw new Error("Anthropic replay does not support assistant image parts");
1423
+ if (part.type === "toolCall") {
1424
+ sawToolCall = true;
1425
+ if (part.status === "pending" || part.status === "running") sawNonTerminalToolCall = true;
1426
+ else sawTerminalToolCall = true;
1427
+ continue;
1483
1428
  }
1429
+ if (sawToolCall) throw new Error("Assistant parts after a toolCall are not replayable for Anthropic");
1484
1430
  }
1485
- async function executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal) {
1486
- const runnables = [];
1487
- for (const toolCall of toolCalls) {
1488
- const toolCallInFlight = {
1489
- callID: toolCall.callID,
1490
- name: toolCall.name,
1491
- rawArgs: toolCall.arguments
1492
- };
1493
- await emitEvent(emit, {
1494
- type: "tool.execution.start",
1495
- ...toolCallInFlight
1496
- });
1497
- const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
1498
- if (prepared.error) {
1499
- const error = {
1500
- status: "error",
1501
- result: {
1502
- content: [{
1503
- type: "text",
1504
- text: prepared.error.message
1505
- }],
1506
- details: prepared.error.details,
1507
- isError: true
1431
+ if (sawToolCall && message.stopReason !== "toolUse") throw new Error("Assistant message with tool calls must have stopReason 'toolUse'");
1432
+ if (!sawToolCall && message.stopReason === "toolUse") throw new Error("Assistant message with stopReason 'toolUse' must contain at least one toolCall");
1433
+ if (sawTerminalToolCall && sawNonTerminalToolCall) throw new Error("Assistant message cannot mix terminal and non-terminal toolCall states");
1434
+ }
1435
+ function buildSyntheticToolResult(block) {
1436
+ return {
1437
+ type: "tool_result",
1438
+ tool_use_id: block.callID,
1439
+ content: convertContentBlocks([{
1440
+ type: "text",
1441
+ text: [
1442
+ "<result>",
1443
+ "No Result Provided",
1444
+ "</result>"
1445
+ ].join("\n")
1446
+ }]),
1447
+ is_error: true
1448
+ };
1449
+ }
1450
+ function convertMessages(messages, model, isOAuthToken, cacheControl) {
1451
+ const params = [];
1452
+ const transformedMessages = Message.transformMessages(messages, model, normalizeToolCallId);
1453
+ for (const msg of transformedMessages) {
1454
+ if (msg.role === "user") {
1455
+ const blocks = msg.parts.map((item) => {
1456
+ if (item.type === "text") return {
1457
+ type: "text",
1458
+ text: sanitizeSurrogates(item.text)
1459
+ };
1460
+ return {
1461
+ type: "image",
1462
+ source: {
1463
+ type: "base64",
1464
+ media_type: item.mimeType,
1465
+ data: item.data
1508
1466
  }
1509
1467
  };
1510
- await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
1511
- } else runnables.push({
1512
- toolCall,
1513
- runnable: prepared.runnable,
1514
- tool: prepared.tool
1515
1468
  });
1469
+ let filteredBlocks = !model.input.includes("image") ? blocks.filter((b) => b.type !== "image") : blocks;
1470
+ filteredBlocks = filteredBlocks.filter((b) => b.type === "text" ? b.text.trim().length > 0 : true);
1471
+ if (filteredBlocks.length === 0) continue;
1472
+ params.push({
1473
+ role: "user",
1474
+ content: filteredBlocks
1475
+ });
1476
+ continue;
1516
1477
  }
1517
- const runningExecutions = runnables.map((runnable) => ({
1518
- runnable,
1519
- execution: executeRunnableToolCall(runnable, message, emit, signal)
1520
- }));
1521
- for (const running of runningExecutions) {
1522
- const executed = await running.execution;
1523
- const { runnable } = running;
1524
- if (executed.error) {
1525
- const error = {
1526
- status: "error",
1527
- result: {
1528
- content: [{
1529
- type: "text",
1530
- text: executed.error.message
1531
- }],
1532
- details: executed.error.details,
1533
- isError: true
1534
- }
1535
- };
1536
- await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
1537
- } else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
1478
+ if (msg.stopReason === "error" || msg.stopReason === "aborted") continue;
1479
+ assertReplayableAssistant(msg);
1480
+ const assistantBlocks = [];
1481
+ const toolResults = [];
1482
+ for (const block of msg.parts) if (block.type === "text") {
1483
+ if (block.text.trim().length === 0) continue;
1484
+ assistantBlocks.push({
1485
+ type: "text",
1486
+ text: sanitizeSurrogates(block.text)
1487
+ });
1488
+ } else if (block.type === "thinking") {
1489
+ if (block.thinking.trim().length === 0) continue;
1490
+ if (!block.thinkingSignature || block.thinkingSignature.trim().length === 0) assistantBlocks.push({
1491
+ type: "text",
1492
+ text: sanitizeSurrogates(block.thinking)
1493
+ });
1494
+ else assistantBlocks.push({
1495
+ type: "thinking",
1496
+ thinking: sanitizeSurrogates(block.thinking),
1497
+ signature: block.thinkingSignature
1498
+ });
1499
+ } else if (block.type === "toolCall") {
1500
+ assistantBlocks.push({
1501
+ type: "tool_use",
1502
+ id: block.callID,
1503
+ name: isOAuthToken ? toClaudeCodeName(block.name) : block.name,
1504
+ input: block.arguments ?? {}
1505
+ });
1506
+ if (block.status === "pending" || block.status === "running") toolResults.push(buildSyntheticToolResult(block));
1507
+ else toolResults.push({
1508
+ type: "tool_result",
1509
+ tool_use_id: block.callID,
1510
+ content: convertContentBlocks(block.result.content),
1511
+ is_error: block.result.isError
1512
+ });
1538
1513
  }
1514
+ if (assistantBlocks.length > 0) params.push({
1515
+ role: "assistant",
1516
+ content: assistantBlocks
1517
+ });
1518
+ if (toolResults.length > 0) params.push({
1519
+ role: "user",
1520
+ content: toolResults
1521
+ });
1539
1522
  }
1540
- async function prepareToolCall(config, currentContext, assistantMessage, toolCallInFlight, signal) {
1541
- const tool = currentContext.tools?.find((t) => t.name === toolCallInFlight.name);
1542
- if (!tool) return {
1543
- error: {
1544
- kind: "error",
1545
- message: `Tool ${toolCallInFlight.name} Not Found`
1546
- },
1547
- runnable: toolCallInFlight
1548
- };
1549
- try {
1550
- toolCallInFlight.args = validateToolArguments(tool, toolCallInFlight);
1551
- if (config.beforeToolExecution) {
1552
- const beforeResult = await config.beforeToolExecution({
1553
- context: currentContext,
1554
- assistantMessage,
1555
- toolCall: toolCallInFlight
1556
- }, signal);
1557
- if (beforeResult?.block) return {
1558
- error: {
1559
- kind: "blocked",
1560
- message: beforeResult?.reason || "Tool Execution was Blocked",
1561
- details: beforeResult?.details
1562
- },
1563
- runnable: toolCallInFlight
1564
- };
1565
- }
1566
- return {
1567
- runnable: toolCallInFlight,
1568
- tool
1569
- };
1570
- } catch (err) {
1571
- return {
1572
- error: {
1573
- kind: "error",
1574
- message: err instanceof Error ? err.message : String(err)
1575
- },
1576
- runnable: toolCallInFlight
1577
- };
1523
+ if (cacheControl && params.length > 0) {
1524
+ const lastMessage = params[params.length - 1];
1525
+ if (lastMessage.role === "user") {
1526
+ if (Array.isArray(lastMessage.content)) {
1527
+ const lastBlock = lastMessage.content[lastMessage.content.length - 1];
1528
+ if (lastBlock && (lastBlock.type === "text" || lastBlock.type === "image" || lastBlock.type === "tool_result")) lastBlock.cache_control = cacheControl;
1529
+ } else if (typeof lastMessage.content === "string") lastMessage.content = [{
1530
+ type: "text",
1531
+ text: lastMessage.content,
1532
+ cache_control: cacheControl
1533
+ }];
1578
1534
  }
1579
1535
  }
1580
- async function executeRunnableToolCall(toolCallRunnable, message, emit, signal) {
1581
- const { toolCall, runnable, tool } = toolCallRunnable;
1582
- const { partIndex, ...pendingPart } = toolCall;
1583
- try {
1584
- return { result: await tool.execute(runnable.callID, runnable.args, signal, async (result) => {
1585
- const runningResult = {
1586
- status: "running",
1587
- partial: result.partial
1588
- };
1589
- await emitEvent(emit, {
1590
- type: "tool.execution.update",
1591
- ...runnable,
1592
- ...runningResult
1593
- });
1594
- const runningPart = {
1595
- ...pendingPart,
1596
- ...runningResult
1597
- };
1598
- message.parts[partIndex] = runningPart;
1599
- await emitEvent(emit, {
1600
- type: "message.part.update",
1601
- message,
1602
- partIndex,
1603
- part: runningPart,
1604
- source: "tool"
1605
- });
1606
- }) };
1607
- } catch (err) {
1608
- return { error: { message: err instanceof Error ? err.message : String(err) } };
1609
- }
1536
+ return params;
1537
+ }
1538
+ function convertTools(tools, isOAuthToken) {
1539
+ if (!tools) return [];
1540
+ return tools.map((tool) => {
1541
+ const jsonSchema = tool.parameters;
1542
+ return {
1543
+ name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,
1544
+ description: tool.description,
1545
+ input_schema: {
1546
+ type: "object",
1547
+ properties: jsonSchema.properties || {},
1548
+ required: jsonSchema.required || []
1549
+ }
1550
+ };
1551
+ });
1552
+ }
1553
+ function mapStopReason(reason) {
1554
+ switch (reason) {
1555
+ case "end_turn": return "stop";
1556
+ case "max_tokens": return "length";
1557
+ case "tool_use": return "toolUse";
1558
+ case "pause_turn":
1559
+ case "stop_sequence": return "stop";
1560
+ case "refusal":
1561
+ case "sensitive": return "error";
1562
+ default: throw new Error("Unhandled Anthropic stop reason");
1610
1563
  }
1611
- async function emitToolCallTerminalResult(toolCall, runnable, message, emit, result) {
1612
- const { partIndex, ...pendingPart } = toolCall;
1613
- const { time } = pendingPart;
1614
- await emitEvent(emit, {
1615
- type: "tool.execution.end",
1616
- ...runnable,
1617
- ...result
1564
+ }
1565
+ function buildParams(model, context, isOAuthToken, options) {
1566
+ const { cacheControl } = getCacheControl(model.baseUrl, options?.cacheRetention);
1567
+ const params = {
1568
+ model: model.id,
1569
+ messages: convertMessages(context.messages, model, isOAuthToken, cacheControl),
1570
+ max_tokens: options?.maxTokens || model.maxTokens / 3 | 0,
1571
+ stream: true
1572
+ };
1573
+ if (isOAuthToken) {
1574
+ params.system = [{
1575
+ type: "text",
1576
+ text: "You are Claude Code, Anthropic's official CLI for Claude.",
1577
+ ...cacheControl ? { cache_control: cacheControl } : {}
1578
+ }];
1579
+ if (context.systemPrompt) params.system.push({
1580
+ type: "text",
1581
+ text: sanitizeSurrogates(context.systemPrompt),
1582
+ ...cacheControl ? { cache_control: cacheControl } : {}
1618
1583
  });
1619
- const part = {
1620
- ...pendingPart,
1621
- ...result,
1584
+ } else if (context.systemPrompt) params.system = [{
1585
+ type: "text",
1586
+ text: sanitizeSurrogates(context.systemPrompt),
1587
+ ...cacheControl ? { cache_control: cacheControl } : {}
1588
+ }];
1589
+ if (options?.temperature !== void 0 && !options?.thinkingEnabled) params.temperature = options.temperature;
1590
+ if (context.tools) params.tools = convertTools(context.tools, isOAuthToken);
1591
+ if (options?.thinkingEnabled && model.reasoning) params.thinking = {
1592
+ type: "enabled",
1593
+ budget_tokens: options.thinkingBudgetTokens || 1024
1594
+ };
1595
+ if (options?.metadata) {
1596
+ const userId = options.metadata.user_id;
1597
+ if (typeof userId === "string") params.metadata = { user_id: userId };
1598
+ }
1599
+ if (options?.toolChoice) params.tool_choice = typeof options.toolChoice === "string" ? { type: options.toolChoice } : options.toolChoice;
1600
+ return params;
1601
+ }
1602
+ const streamAnthropic = (model, context, options) => {
1603
+ const stream = new AssistantMessageEventStream();
1604
+ (async () => {
1605
+ const output = Message.createAssistantMessage({
1606
+ role: "assistant",
1607
+ parts: [],
1608
+ protocol: model.protocol,
1609
+ provider: model.provider,
1610
+ model: model.id,
1611
+ usage: {
1612
+ input: 0,
1613
+ output: 0,
1614
+ cacheRead: 0,
1615
+ cacheWrite: 0,
1616
+ totalTokens: 0,
1617
+ cost: {
1618
+ input: 0,
1619
+ output: 0,
1620
+ cacheRead: 0,
1621
+ cacheWrite: 0,
1622
+ total: 0
1623
+ }
1624
+ },
1625
+ stopReason: "stop",
1622
1626
  time: {
1623
- start: time.start,
1624
- end: Date.now()
1627
+ created: Date.now(),
1628
+ completed: Date.now()
1625
1629
  }
1626
- };
1627
- message.parts[partIndex] = part;
1628
- await emitEvent(emit, {
1629
- type: "message.part.start",
1630
- message,
1631
- partIndex,
1632
- part
1633
- });
1634
- await emitEvent(emit, {
1635
- type: "message.part.end",
1636
- message,
1637
- partIndex,
1638
- part
1639
- });
1640
- await emitEvent(emit, {
1641
- type: "message.update",
1642
- message
1643
1630
  });
1644
- }
1645
- async function finalizeExecutedToolCall(config, currentContext, toolCall, runnable, message, emit, result, signal) {
1646
- if (config.afterToolExecution) {
1647
- const afterResult = await config.afterToolExecution({
1648
- context: currentContext,
1649
- assistantMessage: message,
1650
- toolCall: runnable,
1651
- result
1652
- }, signal);
1653
- if (afterResult) {
1654
- await emitToolCallTerminalResult(toolCall, runnable, message, emit, afterResult);
1655
- return;
1631
+ try {
1632
+ let client;
1633
+ let isOAuthToken;
1634
+ if (options?.client) {
1635
+ client = options.client;
1636
+ isOAuthToken = false;
1637
+ } else {
1638
+ const apiKey = options?.apiKey ?? getEnvApiKey(model.provider);
1639
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
1640
+ const created = createClient(model, apiKey, options?.interleavedThinking ?? true, options?.headers);
1641
+ client = created.client;
1642
+ isOAuthToken = created.isOAuthToken;
1656
1643
  }
1657
- }
1658
- await emitToolCallTerminalResult(toolCall, runnable, message, emit, result);
1659
- }
1660
- /**
1661
- * Main core loop shared by run and runContinue.
1662
- */
1663
- async function runLoop(config, currentContext, newMessages, emit, streamFn, signal) {
1664
- let firstTurn = true;
1665
- let pendingMessages = await config.getSteeringMessages?.() || [];
1666
- while (true) {
1667
- let hasMoreToolCalls = true;
1668
- while (hasMoreToolCalls || pendingMessages.length > 0) {
1669
- if (!firstTurn) await emitEvent(emit, { type: "turn.start" });
1670
- else firstTurn = false;
1671
- if (pendingMessages.length > 0) {
1672
- for (const message of pendingMessages) {
1673
- await emitEvent(emit, {
1674
- type: "message.start",
1675
- message
1676
- });
1677
- for (const [partIndex, part] of message.parts.entries()) {
1678
- await emitEvent(emit, {
1679
- type: "message.part.start",
1680
- message,
1681
- partIndex,
1682
- part
1683
- });
1684
- await emitEvent(emit, {
1685
- type: "message.part.end",
1686
- message,
1687
- partIndex,
1688
- part
1689
- });
1690
- await emitEvent(emit, {
1691
- type: "message.update",
1692
- message
1693
- });
1694
- }
1695
- await emitEvent(emit, {
1696
- type: "message.end",
1697
- message
1698
- });
1699
- currentContext.messages.push(message);
1700
- newMessages.push(message);
1701
- }
1702
- pendingMessages = [];
1644
+ let params = buildParams(model, context, isOAuthToken, options);
1645
+ const nextParams = await options?.onPayload?.(params, model);
1646
+ if (nextParams !== void 0) params = nextParams;
1647
+ const anthropicStream = client.messages.stream({
1648
+ ...params,
1649
+ stream: true
1650
+ }, { signal: options?.signal });
1651
+ stream.push({
1652
+ type: "start",
1653
+ partial: output
1654
+ });
1655
+ const blocks = output.parts;
1656
+ for await (const event of anthropicStream) if (event.type === "message_start") {
1657
+ output.responseId = event.message.id;
1658
+ output.usage.input = event.message.usage.input_tokens || 0;
1659
+ output.usage.output = event.message.usage.output_tokens || 0;
1660
+ output.usage.cacheRead = event.message.usage.cache_read_input_tokens || 0;
1661
+ output.usage.cacheWrite = event.message.usage.cache_creation_input_tokens || 0;
1662
+ output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
1663
+ Model.calculateCost(model, output.usage);
1664
+ } else if (event.type === "content_block_start") {
1665
+ if (event.content_block.type === "text") {
1666
+ const block = {
1667
+ type: "text",
1668
+ text: "",
1669
+ index: event.index
1670
+ };
1671
+ output.parts.push(block);
1672
+ stream.push({
1673
+ type: "text.start",
1674
+ partIndex: output.parts.length - 1,
1675
+ partial: output
1676
+ });
1677
+ } else if (event.content_block.type === "thinking") {
1678
+ const block = {
1679
+ type: "thinking",
1680
+ thinking: "",
1681
+ thinkingSignature: "",
1682
+ index: event.index
1683
+ };
1684
+ output.parts.push(block);
1685
+ stream.push({
1686
+ type: "thinking.start",
1687
+ partIndex: output.parts.length - 1,
1688
+ partial: output
1689
+ });
1690
+ } else if (event.content_block.type === "redacted_thinking") {
1691
+ const block = {
1692
+ type: "thinking",
1693
+ thinking: "[Reasoning redacted]",
1694
+ thinkingSignature: event.content_block.data,
1695
+ redacted: true,
1696
+ index: event.index
1697
+ };
1698
+ output.parts.push(block);
1699
+ stream.push({
1700
+ type: "thinking.start",
1701
+ partIndex: output.parts.length - 1,
1702
+ partial: output
1703
+ });
1704
+ } else if (event.content_block.type === "tool_use") {
1705
+ const block = {
1706
+ type: "toolCall",
1707
+ callID: event.content_block.id,
1708
+ name: isOAuthToken ? fromClaudeCodeName(event.content_block.name, context.tools) : event.content_block.name,
1709
+ arguments: event.content_block.input ?? {},
1710
+ partialJson: "",
1711
+ status: "pending",
1712
+ time: {
1713
+ start: Date.now(),
1714
+ end: Date.now()
1715
+ },
1716
+ index: event.index
1717
+ };
1718
+ output.parts.push(block);
1719
+ stream.push({
1720
+ type: "toolcall.start",
1721
+ partIndex: output.parts.length - 1,
1722
+ partial: output
1723
+ });
1703
1724
  }
1704
- const message = await streamAssistantResponse(config, currentContext, emit, streamFn, signal);
1705
- if (message.stopReason === "error" || message.stopReason === "aborted") {
1706
- currentContext.messages.push(message);
1707
- newMessages.push(message);
1708
- await emitEvent(emit, {
1709
- type: "turn.end",
1710
- message
1725
+ } else if (event.type === "content_block_delta") {
1726
+ const partIndex = blocks.findIndex((b) => b.index === event.index);
1727
+ const block = blocks[partIndex];
1728
+ if (!block) continue;
1729
+ if (event.delta.type === "text_delta" && block.type === "text") {
1730
+ block.text += event.delta.text;
1731
+ stream.push({
1732
+ type: "text.delta",
1733
+ partIndex,
1734
+ delta: event.delta.text,
1735
+ partial: output
1711
1736
  });
1712
- await emitEvent(emit, {
1713
- type: "agent.end",
1714
- messages: newMessages
1737
+ } else if (event.delta.type === "thinking_delta" && block.type === "thinking") {
1738
+ block.thinking += event.delta.thinking;
1739
+ stream.push({
1740
+ type: "thinking.delta",
1741
+ partIndex,
1742
+ delta: event.delta.thinking,
1743
+ partial: output
1744
+ });
1745
+ } else if (event.delta.type === "input_json_delta" && block.type === "toolCall") {
1746
+ block.partialJson += event.delta.partial_json;
1747
+ block.arguments = parseStreamingJson(block.partialJson);
1748
+ stream.push({
1749
+ type: "toolcall.delta",
1750
+ partIndex,
1751
+ delta: event.delta.partial_json,
1752
+ partial: output
1753
+ });
1754
+ } else if (event.delta.type === "signature_delta" && block.type === "thinking") {
1755
+ block.thinkingSignature = block.thinkingSignature || "";
1756
+ block.thinkingSignature += event.delta.signature;
1757
+ }
1758
+ } else if (event.type === "content_block_stop") {
1759
+ const partIndex = blocks.findIndex((b) => b.index === event.index);
1760
+ const block = blocks[partIndex];
1761
+ if (block) {
1762
+ delete block.index;
1763
+ if (block.type === "text") stream.push({
1764
+ type: "text.end",
1765
+ partIndex,
1766
+ content: block.text,
1767
+ partial: output
1768
+ });
1769
+ else if (block.type === "thinking") stream.push({
1770
+ type: "thinking.end",
1771
+ partIndex,
1772
+ content: block.thinking,
1773
+ partial: output
1715
1774
  });
1716
- return;
1775
+ else if (block.type === "toolCall") {
1776
+ block.arguments = parseStreamingJson(block.partialJson);
1777
+ delete block.partialJson;
1778
+ stream.push({
1779
+ type: "toolcall.end",
1780
+ partIndex,
1781
+ toolCall: block,
1782
+ partial: output
1783
+ });
1784
+ }
1717
1785
  }
1718
- const toolCalls = message.parts.map((part, index) => ({
1719
- ...part,
1720
- partIndex: index
1721
- })).filter((part) => part.type === "toolCall" && part.status === "pending");
1722
- hasMoreToolCalls = toolCalls.length > 0;
1723
- if (hasMoreToolCalls) await executeToolCalls(config, currentContext, message, toolCalls, emit, signal);
1724
- currentContext.messages.push(message);
1725
- newMessages.push(message);
1726
- await emitEvent(emit, {
1727
- type: "turn.end",
1728
- message
1729
- });
1730
- pendingMessages = await config.getSteeringMessages?.() || [];
1731
- }
1732
- const followUpMessages = await config.getFollowUpMessages?.() || [];
1733
- if (followUpMessages.length > 0) {
1734
- pendingMessages = followUpMessages;
1735
- continue;
1786
+ } else if (event.type === "message_delta") {
1787
+ if (event.delta.stop_reason) output.stopReason = mapStopReason(event.delta.stop_reason);
1788
+ if (event.usage.input_tokens != null) output.usage.input = event.usage.input_tokens;
1789
+ if (event.usage.output_tokens != null) output.usage.output = event.usage.output_tokens;
1790
+ if (event.usage.cache_read_input_tokens != null) output.usage.cacheRead = event.usage.cache_read_input_tokens;
1791
+ if (event.usage.cache_creation_input_tokens != null) output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
1792
+ output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
1793
+ Model.calculateCost(model, output.usage);
1736
1794
  }
1737
- break;
1738
- }
1739
- await emitEvent(emit, {
1740
- type: "agent.end",
1741
- messages: newMessages
1742
- });
1743
- }
1744
- async function runAgentLoop(config, context, prompts, emit, streamFn, signal) {
1745
- /**
1746
- * maintain 2 arrays to represent state, `context` the actual full state sent to the LLM,
1747
- * `newMessages` for intermediate current loop state.
1748
- * think of newMessages as subset of context.messages for the running current loop.
1749
- * they do move at the same speed staring with initial user prompts.
1750
- * */
1751
- const newMessages = [...prompts];
1752
- const currentContext = {
1753
- ...context,
1754
- messages: [...context.messages, ...prompts]
1755
- };
1756
- await emitEvent(emit, { type: "agent.start" });
1757
- await emitEvent(emit, { type: "turn.start" });
1758
- for (const prompt of prompts) {
1759
- await emitEvent(emit, {
1760
- type: "message.start",
1761
- message: prompt
1795
+ if (options?.signal?.aborted) throw new Error("Request was aborted");
1796
+ if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
1797
+ output.time.completed = Date.now();
1798
+ stream.push({
1799
+ type: "done",
1800
+ reason: output.stopReason,
1801
+ message: output
1762
1802
  });
1763
- for (const [partIndex, part] of prompt.parts.entries()) {
1764
- await emitEvent(emit, {
1765
- type: "message.part.start",
1766
- message: prompt,
1767
- partIndex,
1768
- part
1769
- });
1770
- await emitEvent(emit, {
1771
- type: "message.part.end",
1772
- message: prompt,
1773
- partIndex,
1774
- part
1775
- });
1776
- }
1777
- await emitEvent(emit, {
1778
- type: "message.end",
1779
- message: prompt
1803
+ stream.end();
1804
+ } catch (error) {
1805
+ for (const block of output.parts) delete block.index;
1806
+ output.time.completed = Date.now();
1807
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
1808
+ output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
1809
+ stream.push({
1810
+ type: "error",
1811
+ reason: output.stopReason,
1812
+ error: output
1780
1813
  });
1814
+ stream.end();
1781
1815
  }
1782
- await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
1783
- return newMessages;
1784
- }
1785
- _Loop.runAgentLoop = runAgentLoop;
1786
- async function runAgentLoopContinue(config, context, emit, streamFn, signal) {
1787
- if (context.messages.length === 0) throw new AgentContextError({ message: "connot continue: no messages in context" });
1788
- if (context.messages[context.messages.length - 1]?.role === "assistant") throw new AgentContextError({ message: "cannot continue from message role: assistant" });
1789
- const newMessages = [];
1790
- const currentContext = { ...context };
1791
- await emitEvent(emit, { type: "agent.start" });
1792
- await emitEvent(emit, { type: "turn.start" });
1793
- await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
1794
- return newMessages;
1795
- }
1796
- _Loop.runAgentLoopContinue = runAgentLoopContinue;
1797
- function run(config, context, prompts, streamFn, signal) {
1798
- const stream = createAgentStream();
1799
- runAgentLoop(config, context, prompts, async (event) => {
1800
- stream.push(event);
1801
- }, streamFn, signal).then((messages) => {
1802
- stream.end(messages);
1803
- });
1804
- return stream;
1805
- }
1806
- _Loop.run = run;
1807
- function runContinue(config, context, streamFn, signal) {
1808
- const stream = createAgentStream();
1809
- runAgentLoopContinue(config, context, async (event) => {
1810
- stream.push(event);
1811
- }, streamFn, signal).then((messages) => {
1812
- stream.end(messages);
1813
- });
1814
- return stream;
1815
- }
1816
- _Loop.runContinue = runContinue;
1817
- })(Loop || (Loop = {}));
1816
+ })();
1817
+ return stream;
1818
+ };
1819
+ const streamSimpleAnthropic = (model, context, options) => {
1820
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider);
1821
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
1822
+ const base = Stream.buildBaseOptions(model, options, apiKey);
1823
+ if (!options?.reasoning) return streamAnthropic(model, context, {
1824
+ ...base,
1825
+ thinkingEnabled: false
1826
+ });
1827
+ const adjusted = Stream.adjustMaxTokensForThinking(base.maxTokens || 0, model.maxTokens, options.reasoning, options.thinkingBudgets);
1828
+ return streamAnthropic(model, context, {
1829
+ ...base,
1830
+ maxTokens: adjusted.maxTokens,
1831
+ thinkingEnabled: true,
1832
+ thinkingBudgetTokens: adjusted.thinkingBudget
1833
+ });
1834
+ };
1835
+ //#endregion
1836
+ //#region src/provider/register.ts
1837
+ const providerModules = [["providers/anthropic/index.ts", {
1838
+ protocol: Model.KnownProtocolEnum.anthropicMessages,
1839
+ stream: streamAnthropic,
1840
+ streamSimple: streamSimpleAnthropic
1841
+ }]];
1842
+ for (const [path, provider] of providerModules) Stream.registerProtocolProvider(provider, path);
1818
1843
  //#endregion
1819
1844
  //#region src/agent/agent.ts
1820
1845
  let Agent;
@@ -1849,7 +1874,6 @@ let Agent;
1849
1874
  _state;
1850
1875
  listeners = /* @__PURE__ */ new Set();
1851
1876
  abortController;
1852
- activeAssistantIndex;
1853
1877
  convertToLlm;
1854
1878
  transformContext;
1855
1879
  steeringQueue = [];
@@ -2001,19 +2025,34 @@ let Agent;
2001
2025
  }
2002
2026
  replaceMessages(ms) {
2003
2027
  this._state.messages = ms.slice();
2004
- this.activeAssistantIndex = void 0;
2005
2028
  }
2006
2029
  appendMessage(m) {
2007
2030
  this._state.messages = [...this._state.messages, m];
2008
2031
  }
2009
- patchStoredAssistantPart(partIndex, part) {
2010
- if (this.activeAssistantIndex === void 0) return false;
2032
+ findMessageIndexById(messageId) {
2033
+ return this._state.messages.findIndex((message) => message.messageId === messageId);
2034
+ }
2035
+ replaceMessageById(message) {
2036
+ const messageIndex = this.findMessageIndexById(message.messageId);
2037
+ if (messageIndex === -1) return false;
2038
+ const messages = this._state.messages.slice();
2039
+ messages[messageIndex] = message;
2040
+ this._state.messages = messages;
2041
+ return true;
2042
+ }
2043
+ upsertMessage(message) {
2044
+ if (this.replaceMessageById(message)) return;
2045
+ this.appendMessage(message);
2046
+ }
2047
+ patchStoredAssistantPart(messageId, partIndex, part) {
2048
+ const messageIndex = this.findMessageIndexById(messageId);
2049
+ if (messageIndex === -1) return false;
2011
2050
  const messages = this._state.messages.slice();
2012
- const existing = messages[this.activeAssistantIndex];
2051
+ const existing = messages[messageIndex];
2013
2052
  if (!existing || existing.role !== "assistant") return false;
2014
2053
  const nextParts = existing.parts.slice();
2015
2054
  nextParts[partIndex] = part;
2016
- messages[this.activeAssistantIndex] = {
2055
+ messages[messageIndex] = {
2017
2056
  ...existing,
2018
2057
  parts: nextParts
2019
2058
  };
@@ -2076,7 +2115,6 @@ let Agent;
2076
2115
  }
2077
2116
  clearMessages() {
2078
2117
  this._state.messages = [];
2079
- this.activeAssistantIndex = void 0;
2080
2118
  }
2081
2119
  abort() {
2082
2120
  this.abortController?.abort();
@@ -2090,7 +2128,6 @@ let Agent;
2090
2128
  this._state.streamMessage = null;
2091
2129
  this._state.pendingToolCalls = /* @__PURE__ */ new Set();
2092
2130
  this._state.error = void 0;
2093
- this.activeAssistantIndex = void 0;
2094
2131
  this.steeringQueue = [];
2095
2132
  this.followUpQueue = [];
2096
2133
  }
@@ -2147,7 +2184,7 @@ let Agent;
2147
2184
  if (messages) await Loop.runAgentLoop(config, context, messages, async (e) => this._processLoopEvent(e), this.streamFn, this.abortController.signal);
2148
2185
  else await Loop.runAgentLoopContinue(config, context, async (e) => this._processLoopEvent(e), this.streamFn, this.abortController.signal);
2149
2186
  } catch (err) {
2150
- const message = {
2187
+ const message = Message.createAssistantMessage({
2151
2188
  role: "assistant",
2152
2189
  protocol: model.protocol,
2153
2190
  provider: model.provider,
@@ -2173,7 +2210,7 @@ let Agent;
2173
2210
  completed: Date.now()
2174
2211
  },
2175
2212
  parts: []
2176
- };
2213
+ });
2177
2214
  this.appendMessage(message);
2178
2215
  this._state.error = err?.message || String(err);
2179
2216
  this.emit({
@@ -2201,32 +2238,31 @@ let Agent;
2201
2238
  async prompt(messages, images) {
2202
2239
  this.ensureIdle();
2203
2240
  if (!this._state.model) throw new ModelNotConfiguredErr({ message: "no model configured yet." });
2204
- const message = {
2241
+ const message = Message.createUserMessage({
2205
2242
  role: "user",
2206
2243
  time: { created: Date.now() },
2207
2244
  parts: [...messages, ...images ?? []]
2208
- };
2245
+ });
2209
2246
  await this.loop([message]);
2210
2247
  }
2211
2248
  _processLoopEvent(event) {
2212
2249
  switch (event.type) {
2213
2250
  case "message.start":
2214
2251
  this._state.streamMessage = event.message;
2215
- if (event.message.role === "assistant") this.activeAssistantIndex = void 0;
2216
2252
  break;
2217
2253
  case "message.part.start":
2218
2254
  case "message.part.update":
2219
2255
  case "message.part.end":
2220
2256
  this._state.streamMessage = event.message;
2221
- if (event.message.role === "assistant") this.patchStoredAssistantPart(event.partIndex, event.part);
2257
+ if (event.message.role === "assistant") this.patchStoredAssistantPart(event.message.messageId, event.partIndex, event.part);
2222
2258
  break;
2223
2259
  case "message.update":
2224
2260
  this._state.streamMessage = event.message;
2261
+ this.replaceMessageById(event.message);
2225
2262
  break;
2226
2263
  case "message.end":
2227
2264
  this._state.streamMessage = null;
2228
- this.appendMessage(event.message);
2229
- if (event.message.role === "assistant") this.activeAssistantIndex = this._state.messages.length - 1;
2265
+ this.upsertMessage(event.message);
2230
2266
  break;
2231
2267
  case "tool.execution.start": {
2232
2268
  const pendingToolCalls = new Set(this._state.pendingToolCalls);
@@ -2271,207 +2307,353 @@ let Agent;
2271
2307
  _Agent.create = create;
2272
2308
  })(Agent || (Agent = {}));
2273
2309
  //#endregion
2274
- //#region src/agent.ts
2275
- const agent = {
2276
- create: Agent.create,
2277
- loop: Loop.run,
2278
- loopContinue: Loop.runContinue
2279
- };
2280
- //#endregion
2281
- //#region src/event/event.ts
2282
- let Event;
2283
- (function(_Event) {
2284
- const LLMMessageEventType = _Event.LLMMessageEventType = {
2285
- start: "start",
2286
- textStart: "text.start",
2287
- textDelta: "text.delta",
2288
- textEnd: "text.end",
2289
- thinkingStart: "thinking.start",
2290
- thinkingDelta: "thinking.delta",
2291
- thinkingEnd: "thinking.end",
2292
- toolcallStart: "toolcall.start",
2293
- toolcallDelta: "toolcall.delta",
2294
- toolcallEnd: "toolcall.end",
2295
- done: "done",
2296
- error: "error"
2297
- };
2298
- _Event.LLMMessageEventSchema = Type.Union([
2299
- Type.Object({
2300
- type: Type.Literal(LLMMessageEventType.start),
2301
- partial: Message.AssistantMessageSchema
2302
- }),
2303
- Type.Object({
2304
- type: Type.Literal(LLMMessageEventType.textStart),
2305
- partIndex: Type.Number(),
2306
- partial: Message.AssistantMessageSchema
2307
- }),
2308
- Type.Object({
2309
- type: Type.Literal(LLMMessageEventType.textDelta),
2310
- partIndex: Type.Number(),
2311
- delta: Type.String(),
2312
- partial: Message.AssistantMessageSchema
2313
- }),
2314
- Type.Object({
2315
- type: Type.Literal(LLMMessageEventType.textEnd),
2316
- partIndex: Type.Number(),
2317
- content: Type.String(),
2318
- partial: Message.AssistantMessageSchema
2319
- }),
2320
- Type.Object({
2321
- type: Type.Literal(LLMMessageEventType.thinkingStart),
2322
- partIndex: Type.Number(),
2323
- partial: Message.AssistantMessageSchema
2324
- }),
2325
- Type.Object({
2326
- type: Type.Literal(LLMMessageEventType.thinkingDelta),
2327
- partIndex: Type.Number(),
2328
- delta: Type.String(),
2329
- partial: Message.AssistantMessageSchema
2330
- }),
2331
- Type.Object({
2332
- type: Type.Literal(LLMMessageEventType.thinkingEnd),
2333
- partIndex: Type.Number(),
2334
- content: Type.String(),
2335
- partial: Message.AssistantMessageSchema
2336
- }),
2337
- Type.Object({
2338
- type: Type.Literal(LLMMessageEventType.toolcallStart),
2339
- partIndex: Type.Number(),
2340
- partial: Message.AssistantMessageSchema
2341
- }),
2342
- Type.Object({
2343
- type: Type.Literal(LLMMessageEventType.toolcallDelta),
2344
- partIndex: Type.Number(),
2345
- delta: Type.String(),
2346
- partial: Message.AssistantMessageSchema
2347
- }),
2348
- Type.Object({
2349
- type: Type.Literal(LLMMessageEventType.toolcallEnd),
2350
- partIndex: Type.Number(),
2351
- toolCall: Message.ToolCallSchema,
2352
- partial: Message.AssistantMessageSchema
2353
- }),
2354
- Type.Object({
2355
- type: Type.Literal(LLMMessageEventType.done),
2356
- reason: Type.Union([
2357
- Type.Literal("stop"),
2358
- Type.Literal("length"),
2359
- Type.Literal("toolUse")
2360
- ]),
2361
- message: Message.AssistantMessageSchema
2362
- }),
2363
- Type.Object({
2364
- type: Type.Literal(LLMMessageEventType.error),
2365
- reason: Type.Union([Type.Literal("aborted"), Type.Literal("error")]),
2366
- error: Message.AssistantMessageSchema
2367
- })
2368
- ]);
2369
- const AgentEventType = _Event.AgentEventType = {
2370
- agentStart: "agent.start",
2371
- agentEnd: "agent.end",
2372
- turnStart: "turn.start",
2373
- turnEnd: "turn.end",
2374
- messageStart: "message.start",
2375
- messagePartStart: "message.part.start",
2376
- messagePartUpdate: "message.part.update",
2377
- messagePartEnd: "message.part.end",
2378
- messageUpdate: "message.update",
2379
- messageEnd: "message.end",
2380
- toolExecutionStart: "tool.execution.start",
2381
- toolExecutionUpdate: "tool.execution.update",
2382
- toolExecutionEnd: "tool.execution.end"
2383
- };
2384
- const UserMessagePart = Type.Union([Message.TextContentSchema, Message.ImageContentSchema]);
2385
- const AssistantMessagePart = Type.Union([
2386
- Message.TextContentSchema,
2387
- Message.ImageContentSchema,
2388
- Message.ThinkingContentSchema,
2389
- Message.ToolCallSchema
2390
- ]);
2391
- const UserMessagePartStart = Type.Object({
2392
- type: Type.Literal(AgentEventType.messagePartStart),
2393
- message: Message.UserMessageSchema,
2394
- partIndex: Type.Number(),
2395
- part: UserMessagePart
2396
- });
2397
- const UserMessagePartEnd = Type.Object({
2398
- type: Type.Literal(AgentEventType.messagePartEnd),
2399
- message: Message.UserMessageSchema,
2400
- partIndex: Type.Number(),
2401
- part: UserMessagePart
2402
- });
2403
- const AssistantMessagePartStart = Type.Object({
2404
- type: Type.Literal(AgentEventType.messagePartStart),
2405
- message: Message.AssistantMessageSchema,
2406
- partIndex: Type.Number(),
2407
- part: AssistantMessagePart
2408
- });
2409
- const AssistantMessagePartLLMUpdate = Type.Object({
2410
- type: Type.Literal(AgentEventType.messagePartUpdate),
2411
- message: Message.AssistantMessageSchema,
2412
- partIndex: Type.Number(),
2413
- part: AssistantMessagePart,
2414
- source: Type.Literal("llm")
2415
- });
2416
- const AssistantMessagePartToolUpdate = Type.Object({
2417
- type: Type.Literal(AgentEventType.messagePartUpdate),
2418
- message: Message.AssistantMessageSchema,
2419
- partIndex: Type.Number(),
2420
- part: AssistantMessagePart,
2421
- source: Type.Literal("tool")
2310
+ //#region src/agent/codemode/codemode.ts
2311
+ let CodeMode;
2312
+ (function(_CodeMode) {
2313
+ const ToolDefinitionErr = _CodeMode.ToolDefinitionErr = NamedError.create("ToolDefinitionErr", Type.Object({ message: Type.String() }));
2314
+ const toolFnPrefix = process.env.CODEWORK_CODEMODE_FN_PREFIX ?? "external_";
2315
+ const SandboxExecutionInputSchema = _CodeMode.SandboxExecutionInputSchema = Type.Object({ typescriptCode: Type.String({ description: "TypeScript code to execute in the sandbox." }) });
2316
+ const SandboxExecutionOutputSchema = _CodeMode.SandboxExecutionOutputSchema = Type.Object({
2317
+ result: Type.Optional(Type.Unknown()),
2318
+ logs: Type.Optional(Type.Array(Type.String()))
2422
2319
  });
2423
- const AssistantMessagePartEnd = Type.Object({
2424
- type: Type.Literal(AgentEventType.messagePartEnd),
2425
- message: Message.AssistantMessageSchema,
2426
- partIndex: Type.Number(),
2427
- part: AssistantMessagePart
2320
+ const SandboxExecutionErrorSchema = _CodeMode.SandboxExecutionErrorSchema = Type.Object({
2321
+ message: Type.String(),
2322
+ name: Type.Optional(Type.String()),
2323
+ stack: Type.Optional(Type.String()),
2324
+ line: Type.Optional(Type.Number()),
2325
+ logs: Type.Optional(Type.Array(Type.String()))
2428
2326
  });
2429
- const ToolExecutionStartSchema = Type.Composite([Agent.ToolCallInFlightSchema, Type.Object({ type: Type.Literal(AgentEventType.toolExecutionStart) })]);
2430
- const ToolExecutionUpdateSchema = Type.Composite([
2431
- Agent.ToolCallInFlightSchema,
2432
- Type.Object({ type: Type.Literal(AgentEventType.toolExecutionUpdate) }),
2433
- Message.ToolRunningSchema
2434
- ]);
2435
- const ToolExecutionEndSchema = Type.Composite([
2436
- Agent.ToolCallInFlightSchema,
2437
- Type.Object({ type: Type.Literal(AgentEventType.toolExecutionEnd) }),
2438
- Type.Union([Message.ToolCompletedSchema, Message.ToolErrorSchema])
2439
- ]);
2440
- _Event.AgentEventSchema = Type.Union([
2441
- Type.Object({ type: Type.Literal(AgentEventType.agentStart) }),
2442
- Type.Object({
2443
- type: Type.Literal(AgentEventType.agentEnd),
2444
- messages: Type.Array(Message.MessageSchema)
2445
- }),
2446
- Type.Object({ type: Type.Literal(AgentEventType.turnStart) }),
2447
- Type.Object({
2448
- type: Type.Literal(AgentEventType.turnEnd),
2449
- message: Message.AssistantMessageSchema
2450
- }),
2451
- Type.Object({
2452
- type: Type.Literal(AgentEventType.messageStart),
2453
- message: Message.MessageSchema
2454
- }),
2455
- Type.Union([UserMessagePartStart, UserMessagePartEnd]),
2456
- Type.Union([
2457
- AssistantMessagePartStart,
2458
- AssistantMessagePartLLMUpdate,
2459
- AssistantMessagePartToolUpdate,
2460
- AssistantMessagePartEnd
2461
- ]),
2462
- Type.Object({
2463
- type: Type.Literal(AgentEventType.messageUpdate),
2464
- message: Message.MessageSchema
2465
- }),
2466
- Type.Object({
2467
- type: Type.Literal(AgentEventType.messageEnd),
2468
- message: Message.MessageSchema
2469
- }),
2470
- ToolExecutionStartSchema,
2471
- ToolExecutionUpdateSchema,
2472
- ToolExecutionEndSchema
2473
- ]);
2474
- })(Event || (Event = {}));
2327
+ function withMessageInStack(message, stack) {
2328
+ if (!stack) return void 0;
2329
+ return stack.includes(message) ? stack : `${message}\n${stack}`;
2330
+ }
2331
+ function normalizeExecutionError(error) {
2332
+ if (error instanceof Error) return {
2333
+ name: error.name,
2334
+ message: error.message,
2335
+ stack: withMessageInStack(error.message, error.stack),
2336
+ line: parseErrorLine(error.stack)
2337
+ };
2338
+ if (typeof error === "object" && error !== null) {
2339
+ const candidate = error;
2340
+ return {
2341
+ name: typeof candidate.name === "string" ? candidate.name : void 0,
2342
+ message: typeof candidate.message === "string" ? candidate.message : JSON.stringify(candidate) || "Unknown execution error",
2343
+ stack: typeof candidate.stack === "string" ? withMessageInStack(typeof candidate.message === "string" ? candidate.message : JSON.stringify(candidate) || "Unknown execution error", candidate.stack) : void 0,
2344
+ line: typeof candidate.line === "number" ? candidate.line : parseErrorLine(candidate.stack)
2345
+ };
2346
+ }
2347
+ return {
2348
+ name: "Error",
2349
+ message: String(error)
2350
+ };
2351
+ }
2352
+ function parseErrorLine(stack) {
2353
+ if (typeof stack !== "string") return void 0;
2354
+ const lineMatch = stack.match(/:(\d+)(?::\d+)?\)?$/m);
2355
+ return lineMatch ? Number(lineMatch[1]) : void 0;
2356
+ }
2357
+ function serializeForContent(value) {
2358
+ return JSON.stringify(value, (_key, currentValue) => typeof currentValue === "bigint" ? currentValue.toString() : currentValue, 2);
2359
+ }
2360
+ function toolContentToText(result) {
2361
+ return result.content.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n").trim();
2362
+ }
2363
+ function unwrapToolResult(tool, result) {
2364
+ if (result.status === "completed") {
2365
+ if (tool.outputSchema) return validateSchema(tool.outputSchema, result.result.details, `Tool "${tool.name}" output`);
2366
+ return result.result.details;
2367
+ }
2368
+ if (tool.errorSchema && result.result.details !== void 0) validateSchema(tool.errorSchema, result.result.details, `Tool "${tool.name}" error`);
2369
+ const message = toolContentToText(result.result) || `Tool "${tool.name}" failed`;
2370
+ const error = new Error(message);
2371
+ if (result.result.details !== void 0) error.cause = result.result.details;
2372
+ throw error;
2373
+ }
2374
+ function toolToBinding(tool, prefix = "") {
2375
+ const inputSchema = TypeGuard.IsSchema(tool.parameters) ? tool.parameters : Type.Object({});
2376
+ const outputSchema = TypeGuard.IsSchema(tool.outputSchema) ? tool.outputSchema : void 0;
2377
+ const errorSchema = TypeGuard.IsSchema(tool.errorSchema) ? tool.errorSchema : void 0;
2378
+ if (!("execute" in tool) || typeof tool.execute !== "function") throw new ToolDefinitionErr({ message: `tool "${tool.name}" does not have an execute function. code mode requires tools with implementation` });
2379
+ return {
2380
+ name: `${prefix}${tool.name}`,
2381
+ description: tool.description,
2382
+ inputSchema,
2383
+ outputSchema,
2384
+ errorSchema,
2385
+ execute: async (callID, params, signal) => {
2386
+ const validatedParams = validateSchema(inputSchema, params, `Tool "${tool.name}" input`);
2387
+ return unwrapToolResult(tool, await tool.execute(callID, validatedParams, signal));
2388
+ }
2389
+ };
2390
+ }
2391
+ function toolsForBinding(tools, prefix) {
2392
+ const bindings = {};
2393
+ for (const tool of tools) {
2394
+ const name = `${prefix}${tool.name}`;
2395
+ bindings[name] = toolToBinding(tool, prefix);
2396
+ }
2397
+ return bindings;
2398
+ }
2399
+ function indent(value, depth = 1) {
2400
+ const prefix = " ".repeat(depth);
2401
+ return value.split("\n").map((line) => `${prefix}${line}`).join("\n");
2402
+ }
2403
+ function toPropertyKey(name) {
2404
+ return /^[$A-Z_a-z][$\w]*$/i.test(name) ? name : JSON.stringify(name);
2405
+ }
2406
+ function schemaToTypeScript(schema) {
2407
+ if (TypeGuard.IsAny(schema)) return "any";
2408
+ if (TypeGuard.IsUnknown(schema)) return "unknown";
2409
+ if (TypeGuard.IsString(schema)) return "string";
2410
+ if (TypeGuard.IsNumber(schema) || TypeGuard.IsInteger(schema)) return "number";
2411
+ if (TypeGuard.IsBoolean(schema)) return "boolean";
2412
+ if (TypeGuard.IsNull(schema)) return "null";
2413
+ if (TypeGuard.IsUndefined(schema) || TypeGuard.IsVoid(schema)) return "undefined";
2414
+ if (TypeGuard.IsLiteral(schema)) return JSON.stringify(schema.const);
2415
+ if (TypeGuard.IsArray(schema)) return `Array<${schemaToTypeScript(schema.items)}>`;
2416
+ if (TypeGuard.IsTuple(schema)) return `[${(schema.items ?? []).map((item) => schemaToTypeScript(item)).join(", ")}]`;
2417
+ if (TypeGuard.IsUnion(schema)) return schema.anyOf.map((item) => schemaToTypeScript(item)).join(" | ");
2418
+ if (TypeGuard.IsIntersect(schema)) return schema.allOf.map((item) => schemaToTypeScript(item)).join(" & ");
2419
+ if (TypeGuard.IsRecord(schema)) return `Record<string, ${schemaToTypeScript(Object.values(schema.patternProperties ?? {}).find(TypeGuard.IsSchema) ?? (schema.additionalProperties && TypeGuard.IsSchema(schema.additionalProperties) ? schema.additionalProperties : Type.Unknown()))}>`;
2420
+ if (TypeGuard.IsObject(schema)) {
2421
+ const required = new Set(schema.required ?? []);
2422
+ const properties = Object.entries(schema.properties ?? {}).map(([key, value]) => {
2423
+ const optional = required.has(key) ? "" : "?";
2424
+ return `${toPropertyKey(key)}${optional}: ${schemaToTypeScript(value)};`;
2425
+ });
2426
+ if (schema.additionalProperties === true) properties.push("[key: string]: unknown;");
2427
+ else if (schema.additionalProperties && TypeGuard.IsSchema(schema.additionalProperties)) properties.push(`[key: string]: ${schemaToTypeScript(schema.additionalProperties)};`);
2428
+ if (properties.length === 0) return "{}";
2429
+ return `{\n${indent(properties.join("\n"))}\n}`;
2430
+ }
2431
+ return "unknown";
2432
+ }
2433
+ function generateTypeStubs(bindings, options = {}) {
2434
+ const { includeDescriptions = true } = options;
2435
+ const declarations = [];
2436
+ for (const [name, binding] of Object.entries(bindings)) {
2437
+ const inputTypeName = `${capitalize(name)}Input`;
2438
+ const outputTypeName = `${capitalize(name)}Output`;
2439
+ declarations.push(`type ${inputTypeName} = ${schemaToTypeScript(binding.inputSchema)};`);
2440
+ let outputTypeRef = "unknown";
2441
+ if (binding.outputSchema !== void 0) {
2442
+ declarations.push(`type ${outputTypeName} = ${schemaToTypeScript(binding.outputSchema)};`);
2443
+ outputTypeRef = outputTypeName;
2444
+ }
2445
+ const description = includeDescriptions && binding.description ? `/** ${binding.description} */\n` : "";
2446
+ declarations.push(`${description}declare function ${name}(input: ${inputTypeName}): Promise<${outputTypeRef}>;`);
2447
+ }
2448
+ return declarations.join("\n\n");
2449
+ }
2450
+ _CodeMode.generateTypeStubs = generateTypeStubs;
2451
+ function buildExampleSnippet() {
2452
+ return [
2453
+ "```typescript",
2454
+ "const numbers = [12, 24, 36];",
2455
+ "const total = numbers.reduce((sum, value) => sum + value, 0);",
2456
+ "",
2457
+ "return {",
2458
+ " count: numbers.length,",
2459
+ " average: total / numbers.length,",
2460
+ "};",
2461
+ "```"
2462
+ ];
2463
+ }
2464
+ function generateSystemPrompt(config) {
2465
+ const fnPrefix = config.fnPrefix ?? toolFnPrefix;
2466
+ const bindings = toolsForBinding(config.tools, fnPrefix);
2467
+ const typeStubs = generateTypeStubs(bindings);
2468
+ return [
2469
+ "## Code Execution Tool",
2470
+ "",
2471
+ "You have access to `sandbox_execute_typescript` which runs TypeScript code in a sandboxed environment.",
2472
+ "",
2473
+ "### When to Use",
2474
+ "",
2475
+ "Use `sandbox_execute_typescript` when you need to:",
2476
+ "- Process data with loops, conditionals, or complex logic",
2477
+ "- Make multiple tool calls in parallel with `Promise.all(...)`",
2478
+ "- Transform, filter, or aggregate data",
2479
+ "- Perform calculations or data analysis",
2480
+ "",
2481
+ "For simple operations, prefer calling tools directly.",
2482
+ "",
2483
+ "### Available External APIs",
2484
+ "",
2485
+ "Inside your TypeScript code, you can call these async functions:",
2486
+ "",
2487
+ Object.entries(bindings).map(([name, binding]) => `- \`${name}(input)\`: ${binding.description}`).join("\n") || "- No external APIs available for this run.",
2488
+ "",
2489
+ "### Type Definitions",
2490
+ "",
2491
+ "```typescript",
2492
+ typeStubs,
2493
+ "```",
2494
+ "",
2495
+ "### Example",
2496
+ "",
2497
+ ...buildExampleSnippet(),
2498
+ "",
2499
+ "### Important Notes",
2500
+ "",
2501
+ `- All\`${fnPrefix}\`*calls are async, so always use \`await\``,
2502
+ "- Return a value to pass results back to you",
2503
+ "- Use `console.log()` for debugging (logs are captured)",
2504
+ "- The sandbox is isolated - no network access or file system",
2505
+ "- Each execution is isolated and does not share state with previous runs",
2506
+ ""
2507
+ ].join("\n");
2508
+ }
2509
+ _CodeMode.generateSystemPrompt = generateSystemPrompt;
2510
+ async function transpileTypeScript(typescriptCode) {
2511
+ return (await transform([
2512
+ "(async () => {",
2513
+ " const __codemodeNormalizeError = (error: unknown) => {",
2514
+ " if (error instanceof Error) return { name: error.name, message: error.message, stack: error.stack };",
2515
+ " if (typeof error === 'object' && error !== null) {",
2516
+ " const candidate = error as { name?: unknown; message?: unknown; stack?: unknown };",
2517
+ " return {",
2518
+ " name: typeof candidate.name === 'string' ? candidate.name : 'Error',",
2519
+ " message:",
2520
+ " typeof candidate.message === 'string'",
2521
+ " ? candidate.message",
2522
+ " : JSON.stringify(candidate) || 'Unknown execution error',",
2523
+ " stack: typeof candidate.stack === 'string' ? candidate.stack : undefined,",
2524
+ " };",
2525
+ " }",
2526
+ " return { name: 'Error', message: String(error) };",
2527
+ " };",
2528
+ " try {",
2529
+ typescriptCode,
2530
+ " } catch (error) {",
2531
+ " throw __codemodeNormalizeError(error);",
2532
+ " }",
2533
+ "})()"
2534
+ ].join("\n"), {
2535
+ loader: "ts",
2536
+ target: "es2022",
2537
+ sourcefile: "script.ts",
2538
+ minify: false,
2539
+ keepNames: false
2540
+ })).code;
2541
+ }
2542
+ function buildToolDescription(tools, fnPrefix = toolFnPrefix) {
2543
+ return [
2544
+ "Execute TypeScript code in a secure sandbox environment. ",
2545
+ "The code can use these external API functions:",
2546
+ "<functions>",
2547
+ ` ${tools.map((tool) => `${fnPrefix}${tool.name}`).join("\n")}`,
2548
+ "</functions>",
2549
+ `All ${fnPrefix}* calls are async and must be awaited. `,
2550
+ "Return a value to pass results back. Use console.log() for debugging."
2551
+ ].join("\n");
2552
+ }
2553
+ function createCodeModeTool(config) {
2554
+ if (config.tools.length === 0) throw new ToolDefinitionErr({ message: "at least one tool must be provided to `createCodeModeTool`" });
2555
+ return Agent.defineTool({
2556
+ name: "sandbox_execute_typescript",
2557
+ label: "Sandbox TypeScript",
2558
+ description: buildToolDescription(config.tools, config.fnPrefix),
2559
+ parameters: SandboxExecutionInputSchema,
2560
+ outputSchema: SandboxExecutionOutputSchema,
2561
+ errorSchema: SandboxExecutionErrorSchema,
2562
+ async execute(_callID, params, signal, onUpdate) {
2563
+ const bindings = toolsForBinding(config.tools, config.fnPrefix ?? toolFnPrefix);
2564
+ let context;
2565
+ try {
2566
+ await onUpdate?.({
2567
+ status: "running",
2568
+ partial: { content: [{
2569
+ type: "text",
2570
+ text: "Transpiling TypeScript for sandbox execution"
2571
+ }] }
2572
+ });
2573
+ const javascriptCode = await transpileTypeScript(params.typescriptCode);
2574
+ await onUpdate?.({
2575
+ status: "running",
2576
+ partial: { content: [{
2577
+ type: "text",
2578
+ text: "Executing code in sandbox runtime"
2579
+ }] }
2580
+ });
2581
+ context = await config.driver.createContext({
2582
+ bindings,
2583
+ timeout: config.timeout,
2584
+ memoryLimit: config.memoryLimit,
2585
+ signal
2586
+ });
2587
+ const executionResult = await context.execute(javascriptCode);
2588
+ if (executionResult.success) {
2589
+ const details = {
2590
+ result: executionResult.value,
2591
+ ...executionResult.logs && executionResult.logs.length > 0 ? { logs: executionResult.logs } : {}
2592
+ };
2593
+ return {
2594
+ status: "completed",
2595
+ result: {
2596
+ content: [{
2597
+ type: "text",
2598
+ text: serializeForContent(details)
2599
+ }],
2600
+ details,
2601
+ isError: false
2602
+ }
2603
+ };
2604
+ }
2605
+ const details = {
2606
+ message: executionResult.error?.message || "sandbox execution failed",
2607
+ ...executionResult.error?.name ? { name: executionResult.error.name } : {},
2608
+ ...executionResult.error?.stack ? { stack: executionResult.error.stack } : {},
2609
+ ...executionResult.error?.line != null ? { line: executionResult.error.line } : {},
2610
+ ...executionResult.logs && executionResult.logs.length > 0 ? { logs: executionResult.logs } : {}
2611
+ };
2612
+ return {
2613
+ status: "error",
2614
+ result: {
2615
+ content: [{
2616
+ type: "text",
2617
+ text: serializeForContent(details)
2618
+ }],
2619
+ details,
2620
+ isError: true
2621
+ }
2622
+ };
2623
+ } catch (error) {
2624
+ const normalizedError = normalizeExecutionError(error);
2625
+ const details = {
2626
+ message: normalizedError.message,
2627
+ ...normalizedError.name ? { name: normalizedError.name } : {},
2628
+ ...normalizedError.stack ? { stack: normalizedError.stack } : {},
2629
+ ...normalizedError.line != null ? { line: normalizedError.line } : {}
2630
+ };
2631
+ return {
2632
+ status: "error",
2633
+ result: {
2634
+ content: [{
2635
+ type: "text",
2636
+ text: serializeForContent(details)
2637
+ }],
2638
+ details,
2639
+ isError: true
2640
+ }
2641
+ };
2642
+ } finally {
2643
+ await context?.dispose();
2644
+ }
2645
+ }
2646
+ });
2647
+ }
2648
+ _CodeMode.createCodeModeTool = createCodeModeTool;
2649
+ async function create(config) {
2650
+ return {
2651
+ tool: createCodeModeTool(config),
2652
+ systemPrompt: generateSystemPrompt(config)
2653
+ };
2654
+ }
2655
+ _CodeMode.create = create;
2656
+ })(CodeMode || (CodeMode = {}));
2475
2657
  //#endregion
2476
2658
  //#region src/llm.ts
2477
2659
  const llmImpl = async (provider, model) => Model.getModel(provider, model);
@@ -2503,13 +2685,10 @@ const stream = Object.assign(streamImpl, {
2503
2685
  completeSimple: Stream.completeSimple,
2504
2686
  resolveProtocolProvider: Stream.resolveProtocolProvider
2505
2687
  });
2506
- /** Resolve a streamed response directly to the final assistant message. */
2507
- const complete = Stream.complete;
2508
- /** Start a stream using the simplified options surface. */
2509
- const streamSimple = Stream.streamSimple;
2510
- /** Resolve a simplified stream request directly to the final assistant message. */
2511
- const completeSimple = Stream.completeSimple;
2688
+ Stream.complete;
2689
+ Stream.streamSimple;
2690
+ Stream.completeSimple;
2512
2691
  //#endregion
2513
- export { Agent, Event, Message, Model, ModelCatalog, Stream, agent, complete, completeSimple, llm, stream, streamSimple, validateToolArguments, validateToolCall };
2692
+ export { Agent, CodeMode, Message, agent, llm, stream };
2514
2693
 
2515
2694
  //# sourceMappingURL=index.mjs.map