@codeworksh/aikit 0.2.1 → 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,11 +1,12 @@
1
1
  import { Filesystem, NamedError, lazy } from "@codeworksh/utils";
2
- import { Type } from "@sinclair/typebox";
2
+ import { Type, TypeGuard } from "@sinclair/typebox";
3
+ import { capitalize, keys, mapValues, pick, pipe } from "remeda";
4
+ import Ajv from "ajv";
5
+ import addFormats from "ajv-formats";
3
6
  import { uuidv7 } from "uuidv7";
4
- import { keys, mapValues, pick, pipe } from "remeda";
5
7
  import Anthropic from "@anthropic-ai/sdk";
6
8
  import { parse } from "partial-json";
7
- import Ajv from "ajv";
8
- import addFormats from "ajv-formats";
9
+ import { transform } from "esbuild";
9
10
  //#region src/provider/provider.ts
10
11
  let Provider;
11
12
  (function(_Provider) {
@@ -240,237 +241,171 @@ let Model;
240
241
  _Model.modelsAreEqual = modelsAreEqual;
241
242
  })(Model || (Model = {}));
242
243
  //#endregion
243
- //#region src/message/message.ts
244
- let Message;
245
- (function(_Message) {
246
- const TextContentSchema = _Message.TextContentSchema = Type.Object({
247
- type: Type.Literal("text"),
248
- text: Type.String(),
249
- textSignature: Type.Optional(Type.String())
250
- });
251
- const ImageContentSchema = _Message.ImageContentSchema = Type.Object({
252
- type: Type.Literal("image"),
253
- data: Type.String(),
254
- mimeType: Type.String()
255
- });
256
- const ThinkingContentSchema = _Message.ThinkingContentSchema = Type.Object({
257
- type: Type.Literal("thinking"),
258
- thinking: Type.String(),
259
- thinkingSignature: Type.Optional(Type.String()),
260
- redacted: Type.Optional(Type.Boolean())
261
- });
262
- const ToolCallBaseSchema = _Message.ToolCallBaseSchema = Type.Object({
263
- type: Type.Literal("toolCall"),
264
- callID: Type.String(),
265
- name: Type.String(),
266
- arguments: Type.Record(Type.String(), Type.Any()),
267
- thoughtSignature: Type.Optional(Type.String()),
268
- time: Type.Object({
269
- start: Type.Number(),
270
- end: Type.Number()
271
- })
272
- });
273
- const ToolSuccessResult = _Message.ToolSuccessResult = Type.Object({
274
- content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
275
- details: Type.Optional(Type.Any()),
276
- isError: Type.Literal(false)
277
- });
278
- const ToolErrorResult = _Message.ToolErrorResult = Type.Object({
279
- content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
280
- details: Type.Optional(Type.Any()),
281
- isError: Type.Literal(true)
282
- });
283
- const ToolRunningPartial = _Message.ToolRunningPartial = Type.Object({
284
- content: Type.Optional(Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))),
285
- details: Type.Optional(Type.Any())
286
- });
287
- const ToolStatusEnum = _Message.ToolStatusEnum = {
288
- pending: "pending",
289
- running: "running",
290
- completed: "completed",
291
- error: "error",
292
- skipped: "skipped",
293
- 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"
294
259
  };
295
- const ToolRunningSchema = _Message.ToolRunningSchema = Type.Object({
296
- status: Type.Literal(ToolStatusEnum.running),
297
- partial: Type.Optional(ToolRunningPartial)
298
- });
299
- const ToolCompletedSchema = _Message.ToolCompletedSchema = Type.Object({
300
- status: Type.Literal(ToolStatusEnum.completed),
301
- result: ToolSuccessResult
302
- });
303
- const ToolErrorSchema = _Message.ToolErrorSchema = Type.Object({
304
- status: Type.Literal(ToolStatusEnum.error),
305
- result: ToolErrorResult
306
- });
307
- const ToolSkippedSchema = _Message.ToolSkippedSchema = Type.Object({
308
- status: Type.Literal(ToolStatusEnum.skipped),
309
- result: ToolErrorResult
310
- });
311
- const ToolAbortedSchema = _Message.ToolAbortedSchema = Type.Object({
312
- status: Type.Literal(ToolStatusEnum.aborted),
313
- result: ToolErrorResult
314
- });
315
- const ToolCallPendingPart = Type.Intersect([ToolCallBaseSchema, Type.Object({ status: Type.Literal(ToolStatusEnum.pending) })]);
316
- const ToolCallRunningPart = Type.Composite([ToolCallBaseSchema, ToolRunningSchema]);
317
- const ToolCallCompletedPart = Type.Composite([ToolCallBaseSchema, ToolCompletedSchema]);
318
- const ToolCallErrorPart = Type.Composite([ToolCallBaseSchema, ToolErrorSchema]);
319
- const ToolCallSkippedPart = Type.Composite([ToolCallBaseSchema, ToolSkippedSchema]);
320
- const ToolCallAbortedPart = Type.Composite([ToolCallBaseSchema, ToolAbortedSchema]);
321
- const ToolCallSchema = _Message.ToolCallSchema = Type.Union([
322
- ToolCallPendingPart,
323
- ToolCallRunningPart,
324
- ToolCallCompletedPart,
325
- ToolCallErrorPart,
326
- ToolCallSkippedPart,
327
- 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)
328
267
  ]);
329
- const UsageSchema = _Message.UsageSchema = Type.Object({
330
- input: Type.Number(),
331
- output: Type.Number(),
332
- cacheRead: Type.Number(),
333
- cacheWrite: Type.Number(),
334
- totalTokens: Type.Number(),
335
- cost: Type.Object({
336
- input: Type.Number(),
337
- output: Type.Number(),
338
- cacheRead: Type.Number(),
339
- cacheWrite: Type.Number(),
340
- total: Type.Number()
341
- })
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())
342
274
  });
343
- const StopReasonSchema = _Message.StopReasonSchema = Type.Union([
344
- Type.Literal("stop"),
345
- Type.Literal("length"),
346
- Type.Literal("toolUse"),
347
- Type.Literal("error"),
348
- Type.Literal("aborted")
275
+ const CacheRetentionSchema = _Stream.CacheRetentionSchema = Type.Union([
276
+ Type.Literal("none"),
277
+ Type.Literal("short"),
278
+ Type.Literal("long")
349
279
  ]);
350
- const UserMessageSchema = _Message.UserMessageSchema = Type.Object({
351
- messageId: Type.String(),
352
- role: Type.Literal("user"),
353
- time: Type.Object({ created: Type.Number() }),
354
- parts: Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))
355
- });
356
- const AssistantMessageSchema = _Message.AssistantMessageSchema = Type.Object({
357
- role: Type.Literal("assistant"),
358
- protocol: Model.KnownProtocolSchema,
359
- provider: Provider.Info,
360
- model: Type.String(),
361
- usage: UsageSchema,
362
- stopReason: StopReasonSchema,
363
- errorMessage: Type.Optional(Type.String()),
364
- time: Type.Object({
365
- created: Type.Number(),
366
- completed: Type.Number()
367
- }),
368
- parts: Type.Array(Type.Union([
369
- TextContentSchema,
370
- ImageContentSchema,
371
- ThinkingContentSchema,
372
- ToolCallSchema
373
- ])),
374
- responseId: Type.Optional(Type.String()),
375
- messageId: Type.String()
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()))
376
297
  });
377
- const MessageSchema = _Message.MessageSchema = Type.Union([UserMessageSchema, AssistantMessageSchema]);
378
- function createMessageId() {
379
- return uuidv7();
380
- }
381
- _Message.createMessageId = createMessageId;
382
- function createUserMessage(message) {
383
- const { messageId = createMessageId(), ...rest } = message;
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) {
384
303
  return {
385
- messageId,
386
- ...rest
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
387
315
  };
388
316
  }
389
- _Message.createUserMessage = createUserMessage;
390
- function createAssistantMessage(message) {
391
- const { messageId = createMessageId(), ...rest } = message;
317
+ _Stream.buildBaseOptions = buildBaseOptions;
318
+ function clampReasoning(effort) {
319
+ return effort === ThinkingLevelEnum.xhigh ? ThinkingLevelEnum.high : effort;
320
+ }
321
+ _Stream.clampReasoning = clampReasoning;
322
+ function adjustMaxTokensForThinking(baseMaxTokens, modelMaxTokens, reasoningLevel, customBudgets) {
323
+ const budgets = {
324
+ minimal: 1024,
325
+ low: 2048,
326
+ medium: 8192,
327
+ high: 16384,
328
+ ...customBudgets
329
+ };
330
+ const minOutputTokens = 1024;
331
+ let thinkingBudget = budgets[clampReasoning(reasoningLevel)];
332
+ const maxTokens = Math.min(baseMaxTokens + thinkingBudget, modelMaxTokens);
333
+ if (maxTokens <= thinkingBudget) thinkingBudget = Math.max(0, maxTokens - minOutputTokens);
392
334
  return {
393
- messageId,
394
- ...rest
335
+ maxTokens,
336
+ thinkingBudget
395
337
  };
396
338
  }
397
- _Message.createAssistantMessage = createAssistantMessage;
398
- const ToolSchema = _Message.ToolSchema = Type.Object({
399
- name: Type.String(),
400
- description: Type.String(),
401
- parameters: Type.Unsafe()
402
- });
403
- function defineTool(tool) {
404
- return tool;
339
+ _Stream.adjustMaxTokensForThinking = adjustMaxTokensForThinking;
340
+ const protocolProviderRegistry = /* @__PURE__ */ new Map();
341
+ function wrapStream(protocol, stream) {
342
+ return (model, context, options) => {
343
+ if (model.protocol !== protocol) throw new ProtocolMismatchError({
344
+ actual: model.protocol,
345
+ expected: protocol
346
+ });
347
+ return stream(model, context, options);
348
+ };
405
349
  }
406
- _Message.defineTool = defineTool;
407
- _Message.ContextSchema = Type.Object({
408
- systemPrompt: Type.Optional(Type.String()),
409
- messages: Type.Array(MessageSchema),
410
- tools: Type.Optional(Type.Array(ToolSchema))
411
- });
412
- function transformMessages(messages, model, normalizeToolCallId) {
413
- const toolCallIDMap = /* @__PURE__ */ new Map();
414
- const transformed = [];
415
- for (const msg of messages) {
416
- if (msg.role === "user") {
417
- transformed.push(msg);
418
- continue;
419
- }
420
- const assistantMsg = msg;
421
- const isSameModel = assistantMsg.provider.id === model.provider.id && assistantMsg.protocol === model.protocol && assistantMsg.model === model.id;
422
- const parts = assistantMsg.parts.flatMap((block) => {
423
- if (block.type === "thinking") {
424
- if (isSameModel && block.thinkingSignature) return block;
425
- if (!block.thinking || block.thinking.trim() === "") return [];
426
- if (isSameModel) return block;
427
- return {
428
- type: "text",
429
- text: block.thinking
430
- };
431
- }
432
- if (block.type === "text") {
433
- if (isSameModel) return block;
434
- return {
435
- type: "text",
436
- text: block.text
437
- };
438
- }
439
- if (block.type === "toolCall") {
440
- const toolCall = block;
441
- let normalizedToolCall = toolCall;
442
- if (!isSameModel && toolCall.thoughtSignature) {
443
- normalizedToolCall = { ...toolCall };
444
- delete normalizedToolCall.thoughtSignature;
445
- }
446
- const normalizedID = toolCallIDMap.get(block.callID);
447
- if (normalizedID && normalizedID !== block.callID) normalizedToolCall = {
448
- ...normalizedToolCall,
449
- callID: normalizedID
450
- };
451
- else if (!isSameModel && normalizeToolCallId) {
452
- const nextID = normalizeToolCallId(toolCall.callID, model, assistantMsg);
453
- if (nextID !== toolCall.callID) {
454
- toolCallIDMap.set(toolCall.callID, nextID);
455
- normalizedToolCall = {
456
- ...normalizedToolCall,
457
- callID: nextID
458
- };
459
- }
460
- }
461
- return normalizedToolCall;
462
- }
463
- return block;
464
- });
465
- transformed.push({
466
- ...assistantMsg,
467
- parts
350
+ function wrapStreamSimple(protocol, streamSimple) {
351
+ return (model, context, options) => {
352
+ if (model.protocol !== protocol) throw new ProtocolMismatchError({
353
+ actual: model.protocol,
354
+ expected: protocol
468
355
  });
469
- }
470
- return transformed;
356
+ return streamSimple(model, context, options);
357
+ };
471
358
  }
472
- _Message.transformMessages = transformMessages;
473
- })(Message || (Message = {}));
359
+ function registerProtocolProvider(provider, sourceId) {
360
+ protocolProviderRegistry.set(provider.protocol, {
361
+ provider: {
362
+ protocol: provider.protocol,
363
+ stream: wrapStream(provider.protocol, provider.stream),
364
+ streamSimple: wrapStreamSimple(provider.protocol, provider.streamSimple)
365
+ },
366
+ sourceId
367
+ });
368
+ }
369
+ _Stream.registerProtocolProvider = registerProtocolProvider;
370
+ function getProtocolProvider(protocol) {
371
+ return protocolProviderRegistry.get(protocol)?.provider;
372
+ }
373
+ _Stream.getProtocolProvider = getProtocolProvider;
374
+ function resolveProtocolProvider(model) {
375
+ const provider = getProtocolProvider(model.protocol);
376
+ if (!provider) throw new ProtocolProviderNotFoundError({ protocol: model.protocol });
377
+ return provider;
378
+ }
379
+ _Stream.resolveProtocolProvider = resolveProtocolProvider;
380
+ function stream(model, context, options) {
381
+ return resolveProtocolProvider(model).stream(model, context, options);
382
+ }
383
+ _Stream.stream = stream;
384
+ async function complete(model, context, options) {
385
+ return stream(model, context, options).result();
386
+ }
387
+ _Stream.complete = complete;
388
+ function streamSimple(model, context, options) {
389
+ return resolveProtocolProvider(model).streamSimple(model, context, options);
390
+ }
391
+ _Stream.streamSimple = streamSimple;
392
+ async function completeSimple(model, context, options) {
393
+ return streamSimple(model, context, options).result();
394
+ }
395
+ _Stream.completeSimple = completeSimple;
396
+ function getApiProviders() {
397
+ return Array.from(protocolProviderRegistry.values(), (entry) => entry.provider);
398
+ }
399
+ _Stream.getApiProviders = getApiProviders;
400
+ function unregisterProtocolProviders(sourceId) {
401
+ for (const [api, entry] of protocolProviderRegistry.entries()) if (entry.sourceId === sourceId) protocolProviderRegistry.delete(api);
402
+ }
403
+ _Stream.unregisterProtocolProviders = unregisterProtocolProviders;
404
+ function clearProtocolProviders() {
405
+ protocolProviderRegistry.clear();
406
+ }
407
+ _Stream.clearProtocolProviders = clearProtocolProviders;
408
+ })(Stream || (Stream = {}));
474
409
  //#endregion
475
410
  //#region src/utils/eventstream.ts
476
411
  var EventStream = class {
@@ -530,1306 +465,1381 @@ var AssistantMessageEventStream = class extends EventStream {
530
465
  }
531
466
  };
532
467
  //#endregion
533
- //#region src/utils/jsonparse.ts
534
- /**
535
- * @description Helper function to parse partial jsons during LLM streaming.
536
- */
468
+ //#region src/utils/validation.ts
469
+ const ajv = new Ajv({
470
+ allErrors: true,
471
+ strict: false,
472
+ coerceTypes: true
473
+ });
474
+ addFormats(ajv);
537
475
  /**
538
- * Attempts to parse potentially incomplete JSON during streaming.
539
- * Always returns a valid object, even if the JSON is incomplete.
540
- *
541
- * @param partialJson The partial JSON string from streaming
542
- * @returns Parsed object or empty object if parsing fails
476
+ * Validates an arbitrary value against a TypeBox schema and returns the coerced value.
543
477
  */
544
- function parseStreamingJson(partialJson) {
545
- if (!partialJson || partialJson.trim() === "") return {};
546
- try {
547
- return JSON.parse(partialJson);
548
- } catch {
549
- try {
550
- return parse(partialJson) ?? {};
551
- } catch {
552
- return {};
553
- }
554
- }
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"));
555
491
  }
556
- //#endregion
557
- //#region src/utils/sanitize.ts
558
492
  /**
559
- * Removes unpaired Unicode surrogate characters from a string.
560
- *
561
- * Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
562
- * or vice versa) cause JSON serialization errors in many API providers.
563
- *
564
- * Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
565
- * surrogates and will NOT be affected by this function.
566
- *
567
- * @param text - The text to sanitize
568
- * @returns The sanitized text with unpaired surrogates removed
569
- *
570
- * @example
571
- * // Valid emoji (properly paired surrogates) are preserved
572
- * sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
573
- *
574
- * // Unpaired high surrogate is removed
575
- * const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
576
- * sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
493
+ * Validates tool call arguments against the tool's TypeBox schema
577
494
  */
578
- function sanitizeSurrogates(text) {
579
- return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
495
+ function validateToolArguments(tool, toolExecution) {
496
+ return validateSchema(tool.parameters, toolExecution.rawArgs, `Tool "${toolExecution.name}"`);
580
497
  }
581
498
  //#endregion
582
- //#region src/provider/stream.ts
583
- let Stream;
584
- (function(_Stream) {
585
- const ProtocolProviderNotFoundError = _Stream.ProtocolProviderNotFoundError = NamedError.create("ProtocolProviderNotFoundError", Type.Object({ protocol: Model.KnownProtocolSchema }));
586
- const ProtocolMismatchError = _Stream.ProtocolMismatchError = NamedError.create("ProtocolMismatchError", Type.Object({
587
- actual: Model.KnownProtocolSchema,
588
- expected: Model.KnownProtocolSchema
589
- }));
590
- const ThinkingLevelEnum = _Stream.ThinkingLevelEnum = {
591
- off: "off",
592
- minimal: "minimal",
593
- low: "low",
594
- medium: "medium",
595
- high: "high",
596
- xhigh: "xhigh"
597
- };
598
- const ThinkingLevelSchema = _Stream.ThinkingLevelSchema = Type.Union([
599
- Type.Literal(ThinkingLevelEnum.off),
600
- Type.Literal(ThinkingLevelEnum.minimal),
601
- Type.Literal(ThinkingLevelEnum.low),
602
- Type.Literal(ThinkingLevelEnum.medium),
603
- Type.Literal(ThinkingLevelEnum.high),
604
- Type.Literal(ThinkingLevelEnum.xhigh)
605
- ]);
606
- const ThinkingBudgetsSchema = _Stream.ThinkingBudgetsSchema = Type.Object({
607
- off: Type.Optional(Type.Number()),
608
- minimal: Type.Optional(Type.Number()),
609
- low: Type.Optional(Type.Number()),
610
- medium: Type.Optional(Type.Number()),
611
- high: Type.Optional(Type.Number())
612
- });
613
- const CacheRetentionSchema = _Stream.CacheRetentionSchema = Type.Union([
614
- Type.Literal("none"),
615
- Type.Literal("short"),
616
- Type.Literal("long")
617
- ]);
618
- const TransportSchema = _Stream.TransportSchema = Type.Union([
619
- Type.Literal("sse"),
620
- Type.Literal("websocket"),
621
- Type.Literal("auto")
622
- ]);
623
- const OptionsSchema = _Stream.OptionsSchema = Type.Object({
624
- temperature: Type.Optional(Type.Number()),
625
- maxTokens: Type.Optional(Type.Number()),
626
- signal: Type.Optional(Type.Unsafe()),
627
- apiKey: Type.Optional(Type.String()),
628
- transport: Type.Optional(TransportSchema),
629
- cacheRetention: Type.Optional(CacheRetentionSchema),
630
- sessionId: Type.Optional(Type.String()),
631
- onPayload: Type.Optional(Type.Unsafe()),
632
- headers: Type.Optional(Type.Record(Type.String(), Type.String())),
633
- maxRetryDelayMs: Type.Optional(Type.Number()),
634
- metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
635
- });
636
- _Stream.SimpleOptionsSchema = Type.Composite([OptionsSchema, Type.Object({
637
- reasoning: Type.Optional(ThinkingLevelSchema),
638
- thinkingBudgets: Type.Optional(ThinkingBudgetsSchema)
639
- })]);
640
- function buildBaseOptions(model, options, apiKey) {
641
- return {
642
- temperature: options?.temperature,
643
- maxTokens: options?.maxTokens || Math.min(model.maxTokens, 32e3),
644
- signal: options?.signal,
645
- apiKey: apiKey || options?.apiKey,
646
- transport: options?.transport,
647
- cacheRetention: options?.cacheRetention,
648
- sessionId: options?.sessionId,
649
- headers: options?.headers,
650
- onPayload: options?.onPayload,
651
- maxRetryDelayMs: options?.maxRetryDelayMs,
652
- metadata: options?.metadata
653
- };
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 : []);
654
504
  }
655
- _Stream.buildBaseOptions = buildBaseOptions;
656
- function clampReasoning(effort) {
657
- return effort === ThinkingLevelEnum.xhigh ? ThinkingLevelEnum.high : effort;
505
+ const AgentContextError = _Loop.AgentContextError = NamedError.create("AgentContextError", Type.Object({ message: Type.String() }));
506
+ function snapshotAssistantMessage(message) {
507
+ return structuredClone(message);
658
508
  }
659
- _Stream.clampReasoning = clampReasoning;
660
- function adjustMaxTokensForThinking(baseMaxTokens, modelMaxTokens, reasoningLevel, customBudgets) {
661
- const budgets = {
662
- minimal: 1024,
663
- low: 2048,
664
- medium: 8192,
665
- high: 16384,
666
- ...customBudgets
667
- };
668
- const minOutputTokens = 1024;
669
- let thinkingBudget = budgets[clampReasoning(reasoningLevel)];
670
- const maxTokens = Math.min(baseMaxTokens + thinkingBudget, modelMaxTokens);
671
- if (maxTokens <= thinkingBudget) thinkingBudget = Math.max(0, maxTokens - minOutputTokens);
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`);
672
513
  return {
673
- maxTokens,
674
- thinkingBudget
514
+ message: snapshot,
515
+ part
675
516
  };
676
517
  }
677
- _Stream.adjustMaxTokensForThinking = adjustMaxTokensForThinking;
678
- const protocolProviderRegistry = /* @__PURE__ */ new Map();
679
- function wrapStream(protocol, stream) {
680
- return (model, context, options) => {
681
- if (model.protocol !== protocol) throw new ProtocolMismatchError({
682
- actual: model.protocol,
683
- expected: protocol
518
+ async function emitEvent(emit, event) {
519
+ await emit(event);
520
+ }
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)
684
545
  });
685
- return stream(model, context, options);
686
546
  };
687
- }
688
- function wrapStreamSimple(protocol, streamSimple) {
689
- return (model, context, options) => {
690
- if (model.protocol !== protocol) throw new ProtocolMismatchError({
691
- actual: model.protocol,
692
- expected: protocol
547
+ const emitMessageUpdate = async (message) => {
548
+ await emitEvent(emit, {
549
+ type: "message.update",
550
+ message: snapshotAssistantMessage(message)
693
551
  });
694
- return streamSimple(model, context, options);
695
552
  };
696
- }
697
- function registerProtocolProvider(provider, sourceId) {
698
- protocolProviderRegistry.set(provider.protocol, {
699
- provider: {
700
- protocol: provider.protocol,
701
- stream: wrapStream(provider.protocol, provider.stream),
702
- streamSimple: wrapStreamSimple(provider.protocol, provider.streamSimple)
703
- },
704
- sourceId
553
+ const emitPartStart = async (message, partIndex) => {
554
+ await emitEvent(emit, {
555
+ type: "message.part.start",
556
+ partIndex,
557
+ ...snapshotAssistantPartEvent(message, partIndex)
558
+ });
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"
566
+ });
567
+ };
568
+ const emitPartEnd = async (message, partIndex) => {
569
+ await emitEvent(emit, {
570
+ type: "message.part.end",
571
+ partIndex,
572
+ ...snapshotAssistantPartEvent(message, partIndex)
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;
616
+ }
617
+ const finalMessage = await events.result();
618
+ await ensureMessageStarted(finalMessage);
619
+ await emitEvent(emit, {
620
+ type: "message.end",
621
+ message: snapshotAssistantMessage(finalMessage)
705
622
  });
623
+ return finalMessage;
706
624
  }
707
- _Stream.registerProtocolProvider = registerProtocolProvider;
708
- function getProtocolProvider(protocol) {
709
- return protocolProviderRegistry.get(protocol)?.provider;
710
- }
711
- _Stream.getProtocolProvider = getProtocolProvider;
712
- function resolveProtocolProvider(model) {
713
- const provider = getProtocolProvider(model.protocol);
714
- if (!provider) throw new ProtocolProviderNotFoundError({ protocol: model.protocol });
715
- return provider;
716
- }
717
- _Stream.resolveProtocolProvider = resolveProtocolProvider;
718
- function stream(model, context, options) {
719
- return resolveProtocolProvider(model).stream(model, context, options);
720
- }
721
- _Stream.stream = stream;
722
- async function complete(model, context, options) {
723
- return stream(model, context, options).result();
724
- }
725
- _Stream.complete = complete;
726
- function streamSimple(model, context, options) {
727
- return resolveProtocolProvider(model).streamSimple(model, context, options);
728
- }
729
- _Stream.streamSimple = streamSimple;
730
- async function completeSimple(model, context, options) {
731
- return streamSimple(model, context, options).result();
732
- }
733
- _Stream.completeSimple = completeSimple;
734
- function getApiProviders() {
735
- return Array.from(protocolProviderRegistry.values(), (entry) => entry.provider);
736
- }
737
- _Stream.getApiProviders = getApiProviders;
738
- function unregisterProtocolProviders(sourceId) {
739
- for (const [api, entry] of protocolProviderRegistry.entries()) if (entry.sourceId === sourceId) protocolProviderRegistry.delete(api);
740
- }
741
- _Stream.unregisterProtocolProviders = unregisterProtocolProviders;
742
- function clearProtocolProviders() {
743
- protocolProviderRegistry.clear();
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);
744
628
  }
745
- _Stream.clearProtocolProviders = clearProtocolProviders;
746
- })(Stream || (Stream = {}));
747
- //#endregion
748
- //#region src/provider/providers/anthropic/index.ts
749
- function getEnvApiKey(_provider) {}
750
- function resolveCacheRetention(cacheRetention) {
751
- if (cacheRetention) return cacheRetention;
752
- if (typeof process !== "undefined" && process.env.CODEWORK_CACHE_RETENTION === "long") return "long";
753
- return "short";
754
- }
755
- function getCacheControl(baseUrl, cacheRetention) {
756
- const retention = resolveCacheRetention(cacheRetention);
757
- if (retention === "none") return { retention };
758
- const ttl = retention === "long" && baseUrl.includes("api.anthropic.com") ? "1h" : void 0;
759
- return {
760
- retention,
761
- cacheControl: {
762
- type: "ephemeral",
763
- ...ttl && { ttl }
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);
654
+ } else {
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);
675
+ }
764
676
  }
765
- };
766
- }
767
- const claudeCodeVersion = "2.1.2";
768
- const ccToolLookup = new Map([
769
- "Read",
770
- "Write",
771
- "Edit",
772
- "Bash",
773
- "Grep",
774
- "Glob",
775
- "AskUserQuestion",
776
- "EnterPlanMode",
777
- "ExitPlanMode",
778
- "KillShell",
779
- "NotebookEdit",
780
- "Skill",
781
- "Task",
782
- "TaskOutput",
783
- "TodoWrite",
784
- "WebFetch",
785
- "WebSearch"
786
- ].map((t) => [t.toLowerCase(), t]));
787
- const toClaudeCodeName = (name) => ccToolLookup.get(name.toLowerCase()) ?? name;
788
- const fromClaudeCodeName = (name, tools) => {
789
- if (tools && tools.length > 0) {
790
- const lowerName = name.toLowerCase();
791
- const matchedTool = tools.find((tool) => tool.name.toLowerCase() === lowerName);
792
- if (matchedTool) return matchedTool.name;
793
- }
794
- return name;
795
- };
796
- function convertContentBlocks(content) {
797
- if (!content.some((c) => c.type === "image")) return sanitizeSurrogates(content.map((c) => c.text).join("\n"));
798
- const blocks = content.map((block) => {
799
- if (block.type === "text") return {
800
- type: "text",
801
- text: sanitizeSurrogates(block.text)
802
- };
803
- return {
804
- type: "image",
805
- source: {
806
- type: "base64",
807
- media_type: block.mimeType,
808
- data: block.data
809
- }
810
- };
811
- });
812
- if (!blocks.some((b) => b.type === "text")) blocks.unshift({
813
- type: "text",
814
- text: "(see attached image)"
815
- });
816
- return blocks;
817
- }
818
- function mergeHeaders(...headerSources) {
819
- const merged = {};
820
- for (const headers of headerSources) if (headers) Object.assign(merged, headers);
821
- return merged;
822
- }
823
- function isOAuthToken(apiKey) {
824
- return apiKey.includes("sk-ant-oat");
825
- }
826
- function createClient(model, apiKey, interleavedThinking, optionsHeaders) {
827
- const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
828
- if (interleavedThinking) betaFeatures.push("interleaved-thinking-2025-05-14");
829
- if (isOAuthToken(apiKey)) {
830
- const defaultHeaders = mergeHeaders({
831
- accept: "application/json",
832
- "anthropic-dangerous-direct-browser-access": "true",
833
- "anthropic-beta": `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}`,
834
- "user-agent": `claude-cli/${claudeCodeVersion} (external, cli)`,
835
- "x-app": "cli"
836
- }, model.headers, optionsHeaders);
837
- return {
838
- client: new Anthropic({
839
- apiKey: null,
840
- authToken: apiKey,
841
- baseURL: model.baseUrl,
842
- defaultHeaders,
843
- dangerouslyAllowBrowser: true
844
- }),
845
- isOAuthToken: true
846
- };
847
- }
848
- const defaultHeaders = mergeHeaders({
849
- accept: "application/json",
850
- "anthropic-dangerous-direct-browser-access": "true",
851
- "anthropic-beta": betaFeatures.join(",")
852
- }, model.headers, optionsHeaders);
853
- return {
854
- client: new Anthropic({
855
- apiKey,
856
- baseURL: model.baseUrl,
857
- dangerouslyAllowBrowser: true,
858
- defaultHeaders
859
- }),
860
- isOAuthToken: false
861
- };
862
- }
863
- function normalizeToolCallId(id) {
864
- return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
865
- }
866
- function assertReplayableAssistant(message) {
867
- let sawToolCall = false;
868
- let sawNonTerminalToolCall = false;
869
- let sawTerminalToolCall = false;
870
- for (const part of message.parts) {
871
- if (part.type === "image") throw new Error("Anthropic replay does not support assistant image parts");
872
- if (part.type === "toolCall") {
873
- sawToolCall = true;
874
- if (part.status === "pending" || part.status === "running") sawNonTerminalToolCall = true;
875
- else sawTerminalToolCall = true;
876
- continue;
877
- }
878
- if (sawToolCall) throw new Error("Assistant parts after a toolCall are not replayable for Anthropic");
879
677
  }
880
- if (sawToolCall && message.stopReason !== "toolUse") throw new Error("Assistant message with tool calls must have stopReason 'toolUse'");
881
- if (!sawToolCall && message.stopReason === "toolUse") throw new Error("Assistant message with stopReason 'toolUse' must contain at least one toolCall");
882
- if (sawTerminalToolCall && sawNonTerminalToolCall) throw new Error("Assistant message cannot mix terminal and non-terminal toolCall states");
883
- }
884
- function buildSyntheticToolResult(block) {
885
- return {
886
- type: "tool_result",
887
- tool_use_id: block.callID,
888
- content: convertContentBlocks([{
889
- type: "text",
890
- text: [
891
- "<result>",
892
- "No Result Provided",
893
- "</result>"
894
- ].join("\n")
895
- }]),
896
- is_error: true
897
- };
898
- }
899
- function convertMessages(messages, model, isOAuthToken, cacheControl) {
900
- const params = [];
901
- const transformedMessages = Message.transformMessages(messages, model, normalizeToolCallId);
902
- for (const msg of transformedMessages) {
903
- if (msg.role === "user") {
904
- const blocks = msg.parts.map((item) => {
905
- if (item.type === "text") return {
906
- type: "text",
907
- text: sanitizeSurrogates(item.text)
908
- };
909
- return {
910
- type: "image",
911
- source: {
912
- type: "base64",
913
- media_type: item.mimeType,
914
- data: item.data
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
689
+ });
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
915
701
  }
916
702
  };
703
+ await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
704
+ } else runnables.push({
705
+ toolCall,
706
+ runnable: prepared.runnable,
707
+ tool: prepared.tool
917
708
  });
918
- let filteredBlocks = !model.input.includes("image") ? blocks.filter((b) => b.type !== "image") : blocks;
919
- filteredBlocks = filteredBlocks.filter((b) => b.type === "text" ? b.text.trim().length > 0 : true);
920
- if (filteredBlocks.length === 0) continue;
921
- params.push({
922
- role: "user",
923
- content: filteredBlocks
924
- });
925
- continue;
926
709
  }
927
- if (msg.stopReason === "error" || msg.stopReason === "aborted") continue;
928
- assertReplayableAssistant(msg);
929
- const assistantBlocks = [];
930
- const toolResults = [];
931
- for (const block of msg.parts) if (block.type === "text") {
932
- if (block.text.trim().length === 0) continue;
933
- assistantBlocks.push({
934
- type: "text",
935
- text: sanitizeSurrogates(block.text)
936
- });
937
- } else if (block.type === "thinking") {
938
- if (block.thinking.trim().length === 0) continue;
939
- if (!block.thinkingSignature || block.thinkingSignature.trim().length === 0) assistantBlocks.push({
940
- type: "text",
941
- text: sanitizeSurrogates(block.thinking)
942
- });
943
- else assistantBlocks.push({
944
- type: "thinking",
945
- thinking: sanitizeSurrogates(block.thinking),
946
- signature: block.thinkingSignature
947
- });
948
- } else if (block.type === "toolCall") {
949
- assistantBlocks.push({
950
- type: "tool_use",
951
- id: block.callID,
952
- name: isOAuthToken ? toClaudeCodeName(block.name) : block.name,
953
- input: block.arguments ?? {}
954
- });
955
- if (block.status === "pending" || block.status === "running") toolResults.push(buildSyntheticToolResult(block));
956
- else toolResults.push({
957
- type: "tool_result",
958
- tool_use_id: block.callID,
959
- content: convertContentBlocks(block.result.content),
960
- is_error: block.result.isError
961
- });
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);
962
731
  }
963
- if (assistantBlocks.length > 0) params.push({
964
- role: "assistant",
965
- content: assistantBlocks
966
- });
967
- if (toolResults.length > 0) params.push({
968
- role: "user",
969
- content: toolResults
970
- });
971
732
  }
972
- if (cacheControl && params.length > 0) {
973
- const lastMessage = params[params.length - 1];
974
- if (lastMessage.role === "user") {
975
- if (Array.isArray(lastMessage.content)) {
976
- const lastBlock = lastMessage.content[lastMessage.content.length - 1];
977
- if (lastBlock && (lastBlock.type === "text" || lastBlock.type === "image" || lastBlock.type === "tool_result")) lastBlock.cache_control = cacheControl;
978
- } else if (typeof lastMessage.content === "string") lastMessage.content = [{
979
- type: "text",
980
- text: lastMessage.content,
981
- cache_control: cacheControl
982
- }];
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
+ };
983
771
  }
984
772
  }
985
- return params;
986
- }
987
- function convertTools(tools, isOAuthToken) {
988
- if (!tools) return [];
989
- return tools.map((tool) => {
990
- const jsonSchema = tool.parameters;
991
- return {
992
- name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,
993
- description: tool.description,
994
- input_schema: {
995
- type: "object",
996
- properties: jsonSchema.properties || {},
997
- required: jsonSchema.required || []
998
- }
999
- };
1000
- });
1001
- }
1002
- function mapStopReason(reason) {
1003
- switch (reason) {
1004
- case "end_turn": return "stop";
1005
- case "max_tokens": return "length";
1006
- case "tool_use": return "toolUse";
1007
- case "pause_turn":
1008
- case "stop_sequence": return "stop";
1009
- case "refusal":
1010
- case "sensitive": return "error";
1011
- default: throw new Error("Unhandled Anthropic stop reason");
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
+ }
1012
803
  }
1013
- }
1014
- function buildParams(model, context, isOAuthToken, options) {
1015
- const { cacheControl } = getCacheControl(model.baseUrl, options?.cacheRetention);
1016
- const params = {
1017
- model: model.id,
1018
- messages: convertMessages(context.messages, model, isOAuthToken, cacheControl),
1019
- max_tokens: options?.maxTokens || model.maxTokens / 3 | 0,
1020
- stream: true
1021
- };
1022
- if (isOAuthToken) {
1023
- params.system = [{
1024
- type: "text",
1025
- text: "You are Claude Code, Anthropic's official CLI for Claude.",
1026
- ...cacheControl ? { cache_control: cacheControl } : {}
1027
- }];
1028
- if (context.systemPrompt) params.system.push({
1029
- type: "text",
1030
- text: sanitizeSurrogates(context.systemPrompt),
1031
- ...cacheControl ? { cache_control: cacheControl } : {}
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
1032
811
  });
1033
- } else if (context.systemPrompt) params.system = [{
1034
- type: "text",
1035
- text: sanitizeSurrogates(context.systemPrompt),
1036
- ...cacheControl ? { cache_control: cacheControl } : {}
1037
- }];
1038
- if (options?.temperature !== void 0 && !options?.thinkingEnabled) params.temperature = options.temperature;
1039
- if (context.tools) params.tools = convertTools(context.tools, isOAuthToken);
1040
- if (options?.thinkingEnabled && model.reasoning) params.thinking = {
1041
- type: "enabled",
1042
- budget_tokens: options.thinkingBudgetTokens || 1024
1043
- };
1044
- if (options?.metadata) {
1045
- const userId = options.metadata.user_id;
1046
- if (typeof userId === "string") params.metadata = { user_id: userId };
1047
- }
1048
- if (options?.toolChoice) params.tool_choice = typeof options.toolChoice === "string" ? { type: options.toolChoice } : options.toolChoice;
1049
- return params;
1050
- }
1051
- const streamAnthropic = (model, context, options) => {
1052
- const stream = new AssistantMessageEventStream();
1053
- (async () => {
1054
- const output = Message.createAssistantMessage({
1055
- role: "assistant",
1056
- parts: [],
1057
- protocol: model.protocol,
1058
- provider: model.provider,
1059
- model: model.id,
1060
- usage: {
1061
- input: 0,
1062
- output: 0,
1063
- cacheRead: 0,
1064
- cacheWrite: 0,
1065
- totalTokens: 0,
1066
- cost: {
1067
- input: 0,
1068
- output: 0,
1069
- cacheRead: 0,
1070
- cacheWrite: 0,
1071
- total: 0
1072
- }
1073
- },
1074
- stopReason: "stop",
812
+ const part = {
813
+ ...pendingPart,
814
+ ...result,
1075
815
  time: {
1076
- created: Date.now(),
1077
- completed: Date.now()
816
+ start: time.start,
817
+ end: Date.now()
1078
818
  }
819
+ };
820
+ message.parts[partIndex] = part;
821
+ await emitEvent(emit, {
822
+ type: "message.part.start",
823
+ message,
824
+ partIndex,
825
+ part
1079
826
  });
1080
- try {
1081
- let client;
1082
- let isOAuthToken;
1083
- if (options?.client) {
1084
- client = options.client;
1085
- isOAuthToken = false;
1086
- } else {
1087
- const apiKey = options?.apiKey ?? getEnvApiKey(model.provider);
1088
- if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
1089
- const created = createClient(model, apiKey, options?.interleavedThinking ?? true, options?.headers);
1090
- client = created.client;
1091
- isOAuthToken = created.isOAuthToken;
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;
1092
849
  }
1093
- let params = buildParams(model, context, isOAuthToken, options);
1094
- const nextParams = await options?.onPayload?.(params, model);
1095
- if (nextParams !== void 0) params = nextParams;
1096
- const anthropicStream = client.messages.stream({
1097
- ...params,
1098
- stream: true
1099
- }, { signal: options?.signal });
1100
- stream.push({
1101
- type: "start",
1102
- partial: output
1103
- });
1104
- const blocks = output.parts;
1105
- for await (const event of anthropicStream) if (event.type === "message_start") {
1106
- output.responseId = event.message.id;
1107
- output.usage.input = event.message.usage.input_tokens || 0;
1108
- output.usage.output = event.message.usage.output_tokens || 0;
1109
- output.usage.cacheRead = event.message.usage.cache_read_input_tokens || 0;
1110
- output.usage.cacheWrite = event.message.usage.cache_creation_input_tokens || 0;
1111
- output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
1112
- Model.calculateCost(model, output.usage);
1113
- } else if (event.type === "content_block_start") {
1114
- if (event.content_block.type === "text") {
1115
- const block = {
1116
- type: "text",
1117
- text: "",
1118
- index: event.index
1119
- };
1120
- output.parts.push(block);
1121
- stream.push({
1122
- type: "text.start",
1123
- partIndex: output.parts.length - 1,
1124
- partial: output
1125
- });
1126
- } else if (event.content_block.type === "thinking") {
1127
- const block = {
1128
- type: "thinking",
1129
- thinking: "",
1130
- thinkingSignature: "",
1131
- index: event.index
1132
- };
1133
- output.parts.push(block);
1134
- stream.push({
1135
- type: "thinking.start",
1136
- partIndex: output.parts.length - 1,
1137
- partial: output
1138
- });
1139
- } else if (event.content_block.type === "redacted_thinking") {
1140
- const block = {
1141
- type: "thinking",
1142
- thinking: "[Reasoning redacted]",
1143
- thinkingSignature: event.content_block.data,
1144
- redacted: true,
1145
- index: event.index
1146
- };
1147
- output.parts.push(block);
1148
- stream.push({
1149
- type: "thinking.start",
1150
- partIndex: output.parts.length - 1,
1151
- partial: output
1152
- });
1153
- } else if (event.content_block.type === "tool_use") {
1154
- const block = {
1155
- type: "toolCall",
1156
- callID: event.content_block.id,
1157
- name: isOAuthToken ? fromClaudeCodeName(event.content_block.name, context.tools) : event.content_block.name,
1158
- arguments: event.content_block.input ?? {},
1159
- partialJson: "",
1160
- status: "pending",
1161
- time: {
1162
- start: Date.now(),
1163
- end: Date.now()
1164
- },
1165
- index: event.index
1166
- };
1167
- output.parts.push(block);
1168
- stream.push({
1169
- type: "toolcall.start",
1170
- partIndex: output.parts.length - 1,
1171
- partial: output
1172
- });
1173
- }
1174
- } else if (event.type === "content_block_delta") {
1175
- const partIndex = blocks.findIndex((b) => b.index === event.index);
1176
- const block = blocks[partIndex];
1177
- if (!block) continue;
1178
- if (event.delta.type === "text_delta" && block.type === "text") {
1179
- block.text += event.delta.text;
1180
- stream.push({
1181
- type: "text.delta",
1182
- partIndex,
1183
- delta: event.delta.text,
1184
- partial: output
1185
- });
1186
- } else if (event.delta.type === "thinking_delta" && block.type === "thinking") {
1187
- block.thinking += event.delta.thinking;
1188
- stream.push({
1189
- type: "thinking.delta",
1190
- partIndex,
1191
- delta: event.delta.thinking,
1192
- partial: output
1193
- });
1194
- } else if (event.delta.type === "input_json_delta" && block.type === "toolCall") {
1195
- block.partialJson += event.delta.partial_json;
1196
- block.arguments = parseStreamingJson(block.partialJson);
1197
- stream.push({
1198
- type: "toolcall.delta",
1199
- partIndex,
1200
- delta: event.delta.partial_json,
1201
- partial: output
1202
- });
1203
- } else if (event.delta.type === "signature_delta" && block.type === "thinking") {
1204
- block.thinkingSignature = block.thinkingSignature || "";
1205
- block.thinkingSignature += event.delta.signature;
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 = [];
1206
896
  }
1207
- } else if (event.type === "content_block_stop") {
1208
- const partIndex = blocks.findIndex((b) => b.index === event.index);
1209
- const block = blocks[partIndex];
1210
- if (block) {
1211
- delete block.index;
1212
- if (block.type === "text") stream.push({
1213
- type: "text.end",
1214
- partIndex,
1215
- content: block.text,
1216
- partial: output
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
1217
904
  });
1218
- else if (block.type === "thinking") stream.push({
1219
- type: "thinking.end",
1220
- partIndex,
1221
- content: block.thinking,
1222
- partial: output
905
+ await emitEvent(emit, {
906
+ type: "agent.end",
907
+ messages: newMessages
1223
908
  });
1224
- else if (block.type === "toolCall") {
1225
- block.arguments = parseStreamingJson(block.partialJson);
1226
- delete block.partialJson;
1227
- stream.push({
1228
- type: "toolcall.end",
1229
- partIndex,
1230
- toolCall: block,
1231
- partial: output
1232
- });
1233
- }
909
+ return;
1234
910
  }
1235
- } else if (event.type === "message_delta") {
1236
- if (event.delta.stop_reason) output.stopReason = mapStopReason(event.delta.stop_reason);
1237
- if (event.usage.input_tokens != null) output.usage.input = event.usage.input_tokens;
1238
- if (event.usage.output_tokens != null) output.usage.output = event.usage.output_tokens;
1239
- if (event.usage.cache_read_input_tokens != null) output.usage.cacheRead = event.usage.cache_read_input_tokens;
1240
- if (event.usage.cache_creation_input_tokens != null) output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
1241
- output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
1242
- Model.calculateCost(model, output.usage);
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?.() || [];
1243
924
  }
1244
- if (options?.signal?.aborted) throw new Error("Request was aborted");
1245
- if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
1246
- output.time.completed = Date.now();
1247
- stream.push({
1248
- type: "done",
1249
- reason: output.stopReason,
1250
- message: output
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
1251
955
  });
1252
- stream.end();
1253
- } catch (error) {
1254
- for (const block of output.parts) delete block.index;
1255
- output.time.completed = Date.now();
1256
- output.stopReason = options?.signal?.aborted ? "aborted" : "error";
1257
- output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
1258
- stream.push({
1259
- type: "error",
1260
- reason: output.stopReason,
1261
- error: output
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
1262
973
  });
1263
- stream.end();
1264
974
  }
1265
- })();
1266
- return stream;
1267
- };
1268
- const streamSimpleAnthropic = (model, context, options) => {
1269
- const apiKey = options?.apiKey || getEnvApiKey(model.provider);
1270
- if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
1271
- const base = Stream.buildBaseOptions(model, options, apiKey);
1272
- if (!options?.reasoning) return streamAnthropic(model, context, {
1273
- ...base,
1274
- thinkingEnabled: false
1275
- });
1276
- const adjusted = Stream.adjustMaxTokensForThinking(base.maxTokens || 0, model.maxTokens, options.reasoning, options.thinkingBudgets);
1277
- return streamAnthropic(model, context, {
1278
- ...base,
1279
- maxTokens: adjusted.maxTokens,
1280
- thinkingEnabled: true,
1281
- thinkingBudgetTokens: adjusted.thinkingBudget
1282
- });
1283
- };
1284
- //#endregion
1285
- //#region src/provider/register.ts
1286
- const providerModules = [["providers/anthropic/index.ts", {
1287
- protocol: Model.KnownProtocolEnum.anthropicMessages,
1288
- stream: streamAnthropic,
1289
- streamSimple: streamSimpleAnthropic
1290
- }]];
1291
- for (const [path, provider] of providerModules) Stream.registerProtocolProvider(provider, path);
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 = {}));
1292
1011
  //#endregion
1293
- //#region src/utils/validation.ts
1294
- /**
1295
- * @description Validation powered by ajv, TypeBox natively doesn't provide data validation.
1296
- */
1297
- const ajv = new Ajv({
1298
- allErrors: true,
1299
- strict: false,
1300
- coerceTypes: true
1301
- });
1302
- addFormats(ajv);
1303
- /**
1304
- * Validates tool call arguments against the tool's TypeBox schema
1305
- */
1306
- function validateToolArguments(tool, toolExecution) {
1307
- const validate = ajv.compile(tool.parameters);
1308
- const args = structuredClone(toolExecution.rawArgs);
1309
- if (validate(args)) return args;
1310
- const errors = validate.errors?.map((err) => {
1311
- return ` - ${err.instancePath ? err.instancePath.substring(1) : err.params?.missingProperty || "root"}: ${err.message}`;
1312
- }).join("\n") || "Unknown Validation Error";
1313
- throw new Error([
1314
- `Validation Failed For Tool "${toolExecution.name}"`,
1315
- `${errors}\n`,
1316
- "Received Arguments:",
1317
- `${JSON.stringify(toolExecution.rawArgs, null, 2)}\n`
1318
- ].join("\n"));
1319
- }
1012
+ //#region src/agent.ts
1013
+ const agent = {
1014
+ loop: Loop.run,
1015
+ loopContinue: Loop.runContinue
1016
+ };
1320
1017
  //#endregion
1321
- //#region src/agent/loop.ts
1322
- let Loop;
1323
- (function(_Loop) {
1324
- function createAgentStream() {
1325
- return new EventStream((event) => event.type === "agent.end", (event) => event.type === "agent.end" ? event.messages : []);
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();
1326
1155
  }
1327
- const AgentContextError = _Loop.AgentContextError = NamedError.create("AgentContextError", Type.Object({ message: Type.String() }));
1328
- function snapshotAssistantMessage(message) {
1329
- return structuredClone(message);
1156
+ _Message.createMessageId = createMessageId;
1157
+ function createUserMessage(message) {
1158
+ const { messageId = createMessageId(), ...rest } = message;
1159
+ return {
1160
+ messageId,
1161
+ ...rest
1162
+ };
1330
1163
  }
1331
- function snapshotAssistantPartEvent(message, partIndex) {
1332
- const snapshot = snapshotAssistantMessage(message);
1333
- const part = snapshot.parts[partIndex];
1334
- if (!part) throw new Error(`Assistant message part at index ${partIndex} was not found`);
1164
+ _Message.createUserMessage = createUserMessage;
1165
+ function createAssistantMessage(message) {
1166
+ const { messageId = createMessageId(), ...rest } = message;
1335
1167
  return {
1336
- message: snapshot,
1337
- part
1168
+ messageId,
1169
+ ...rest
1338
1170
  };
1339
1171
  }
1340
- async function emitEvent(emit, event) {
1341
- await emit(event);
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;
1342
1180
  }
1343
- async function streamAssistantResponse(config, context, emit, streamFn, signal) {
1344
- let messages = context.messages;
1345
- if (config.transformContext) messages = await config.transformContext(messages, signal);
1346
- const llmMessages = await config.convertToLlm(messages);
1347
- const llmContext = {
1348
- systemPrompt: context.systemPrompt,
1349
- messages: llmMessages,
1350
- tools: context.tools
1351
- };
1352
- const streamFunction = streamFn || Stream.streamSimple;
1353
- const resolvedApiKey = (config.getApiKey ? await config.getApiKey(config.model.provider) : void 0) || config.apiKey;
1354
- const events = await streamFunction(config.model, llmContext, {
1355
- ...config,
1356
- apiKey: resolvedApiKey,
1357
- signal
1358
- });
1359
- let partialMessage = null;
1360
- let started = false;
1361
- const ensureMessageStarted = async (message) => {
1362
- if (started) return;
1363
- started = true;
1364
- await emitEvent(emit, {
1365
- type: "message.start",
1366
- message: snapshotAssistantMessage(message)
1367
- });
1368
- };
1369
- const emitMessageUpdate = async (message) => {
1370
- await emitEvent(emit, {
1371
- type: "message.update",
1372
- message: snapshotAssistantMessage(message)
1373
- });
1374
- };
1375
- const emitPartStart = async (message, partIndex) => {
1376
- await emitEvent(emit, {
1377
- type: "message.part.start",
1378
- partIndex,
1379
- ...snapshotAssistantPartEvent(message, partIndex)
1380
- });
1381
- };
1382
- const emitPartUpdate = async (message, partIndex) => {
1383
- await emitEvent(emit, {
1384
- type: "message.part.update",
1385
- partIndex,
1386
- ...snapshotAssistantPartEvent(message, partIndex),
1387
- source: "llm"
1388
- });
1389
- };
1390
- const emitPartEnd = async (message, partIndex) => {
1391
- await emitEvent(emit, {
1392
- type: "message.part.end",
1393
- partIndex,
1394
- ...snapshotAssistantPartEvent(message, partIndex)
1395
- });
1396
- };
1397
- for await (const event of events) switch (event.type) {
1398
- case "start":
1399
- partialMessage = event.partial;
1400
- await ensureMessageStarted(partialMessage);
1401
- break;
1402
- case "text.start":
1403
- case "thinking.start":
1404
- case "toolcall.start":
1405
- partialMessage = event.partial;
1406
- await ensureMessageStarted(partialMessage);
1407
- await emitPartStart(partialMessage, event.partIndex);
1408
- break;
1409
- case "text.delta":
1410
- case "thinking.delta":
1411
- case "toolcall.delta":
1412
- partialMessage = event.partial;
1413
- await ensureMessageStarted(partialMessage);
1414
- await emitPartUpdate(partialMessage, event.partIndex);
1415
- break;
1416
- case "text.end":
1417
- case "thinking.end":
1418
- case "toolcall.end":
1419
- partialMessage = event.partial;
1420
- await ensureMessageStarted(partialMessage);
1421
- await emitPartEnd(partialMessage, event.partIndex);
1422
- await emitMessageUpdate(partialMessage);
1423
- break;
1424
- case "done":
1425
- await ensureMessageStarted(event.message);
1426
- await emitEvent(emit, {
1427
- type: "message.end",
1428
- message: snapshotAssistantMessage(event.message)
1429
- });
1430
- return event.message;
1431
- case "error":
1432
- await ensureMessageStarted(event.error);
1433
- await emitEvent(emit, {
1434
- type: "message.end",
1435
- message: snapshotAssistantMessage(event.error)
1436
- });
1437
- return event.error;
1438
- }
1439
- const finalMessage = await events.result();
1440
- await ensureMessageStarted(finalMessage);
1441
- await emitEvent(emit, {
1442
- type: "message.end",
1443
- message: snapshotAssistantMessage(finalMessage)
1444
- });
1445
- return finalMessage;
1446
- }
1447
- async function executeToolCalls(config, currentContext, message, toolCalls, emit, signal) {
1448
- if (config.toolExecution === "sequential") return executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal);
1449
- return executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal);
1450
- }
1451
- async function executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal) {
1452
- for (const toolCall of toolCalls) {
1453
- const toolCallInFlight = {
1454
- callID: toolCall.callID,
1455
- name: toolCall.name,
1456
- rawArgs: toolCall.arguments
1457
- };
1458
- await emitEvent(emit, {
1459
- type: "tool.execution.start",
1460
- ...toolCallInFlight
1461
- });
1462
- const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
1463
- if (prepared.error) {
1464
- const error = {
1465
- status: "error",
1466
- result: {
1467
- content: [{
1468
- type: "text",
1469
- text: prepared.error.message
1470
- }],
1471
- details: prepared.error.details,
1472
- isError: true
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
+ };
1206
+ }
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;
1473
1220
  }
1474
- };
1475
- await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
1476
- } else {
1477
- const runnable = {
1478
- toolCall,
1479
- runnable: prepared.runnable,
1480
- tool: prepared.tool
1481
- };
1482
- const executed = await executeRunnableToolCall(runnable, message, emit, signal);
1483
- if (executed.error) {
1484
- const error = {
1485
- status: "error",
1486
- result: {
1487
- content: [{
1488
- type: "text",
1489
- text: executed.error.message
1490
- }],
1491
- details: executed.error.details,
1492
- isError: true
1493
- }
1221
+ const normalizedID = toolCallIDMap.get(block.callID);
1222
+ if (normalizedID && normalizedID !== block.callID) normalizedToolCall = {
1223
+ ...normalizedToolCall,
1224
+ callID: normalizedID
1494
1225
  };
1495
- await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
1496
- } else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
1497
- }
1498
- }
1499
- }
1500
- async function executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal) {
1501
- const runnables = [];
1502
- for (const toolCall of toolCalls) {
1503
- const toolCallInFlight = {
1504
- callID: toolCall.callID,
1505
- name: toolCall.name,
1506
- rawArgs: toolCall.arguments
1507
- };
1508
- await emitEvent(emit, {
1509
- type: "tool.execution.start",
1510
- ...toolCallInFlight
1511
- });
1512
- const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
1513
- if (prepared.error) {
1514
- const error = {
1515
- status: "error",
1516
- result: {
1517
- content: [{
1518
- type: "text",
1519
- text: prepared.error.message
1520
- }],
1521
- details: prepared.error.details,
1522
- isError: true
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
+ }
1523
1235
  }
1524
- };
1525
- await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
1526
- } else runnables.push({
1527
- toolCall,
1528
- runnable: prepared.runnable,
1529
- tool: prepared.tool
1236
+ return normalizedToolCall;
1237
+ }
1238
+ return block;
1239
+ });
1240
+ transformed.push({
1241
+ ...assistantMsg,
1242
+ parts
1530
1243
  });
1531
1244
  }
1532
- const runningExecutions = runnables.map((runnable) => ({
1533
- runnable,
1534
- execution: executeRunnableToolCall(runnable, message, emit, signal)
1535
- }));
1536
- for (const running of runningExecutions) {
1537
- const executed = await running.execution;
1538
- const { runnable } = running;
1539
- if (executed.error) {
1540
- const error = {
1541
- status: "error",
1542
- result: {
1543
- content: [{
1544
- type: "text",
1545
- text: executed.error.message
1546
- }],
1547
- details: executed.error.details,
1548
- isError: true
1549
- }
1550
- };
1551
- await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
1552
- } else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
1553
- }
1245
+ return transformed;
1554
1246
  }
1555
- async function prepareToolCall(config, currentContext, assistantMessage, toolCallInFlight, signal) {
1556
- const tool = currentContext.tools?.find((t) => t.name === toolCallInFlight.name);
1557
- if (!tool) return {
1558
- error: {
1559
- kind: "error",
1560
- message: `Tool ${toolCallInFlight.name} Not Found`
1561
- },
1562
- runnable: toolCallInFlight
1563
- };
1247
+ _Message.transformMessages = transformMessages;
1248
+ })(Message || (Message = {}));
1249
+ //#endregion
1250
+ //#region src/utils/jsonparse.ts
1251
+ /**
1252
+ * @description Helper function to parse partial jsons during LLM streaming.
1253
+ */
1254
+ /**
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
1260
+ */
1261
+ function parseStreamingJson(partialJson) {
1262
+ if (!partialJson || partialJson.trim() === "") return {};
1263
+ try {
1264
+ return JSON.parse(partialJson);
1265
+ } catch {
1564
1266
  try {
1565
- toolCallInFlight.args = validateToolArguments(tool, toolCallInFlight);
1566
- if (config.beforeToolExecution) {
1567
- const beforeResult = await config.beforeToolExecution({
1568
- context: currentContext,
1569
- assistantMessage,
1570
- toolCall: toolCallInFlight
1571
- }, signal);
1572
- if (beforeResult?.block) return {
1573
- error: {
1574
- kind: "blocked",
1575
- message: beforeResult?.reason || "Tool Execution was Blocked",
1576
- details: beforeResult?.details
1577
- },
1578
- runnable: toolCallInFlight
1579
- };
1580
- }
1581
- return {
1582
- runnable: toolCallInFlight,
1583
- tool
1584
- };
1585
- } catch (err) {
1586
- return {
1587
- error: {
1588
- kind: "error",
1589
- message: err instanceof Error ? err.message : String(err)
1590
- },
1591
- runnable: toolCallInFlight
1592
- };
1267
+ return parse(partialJson) ?? {};
1268
+ } catch {
1269
+ return {};
1593
1270
  }
1594
1271
  }
1595
- async function executeRunnableToolCall(toolCallRunnable, message, emit, signal) {
1596
- const { toolCall, runnable, tool } = toolCallRunnable;
1597
- const { partIndex, ...pendingPart } = toolCall;
1598
- try {
1599
- return { result: await tool.execute(runnable.callID, runnable.args, signal, async (result) => {
1600
- const runningResult = {
1601
- status: "running",
1602
- partial: result.partial
1603
- };
1604
- await emitEvent(emit, {
1605
- type: "tool.execution.update",
1606
- ...runnable,
1607
- ...runningResult
1608
- });
1609
- const runningPart = {
1610
- ...pendingPart,
1611
- ...runningResult
1612
- };
1613
- message.parts[partIndex] = runningPart;
1614
- await emitEvent(emit, {
1615
- type: "message.part.update",
1616
- message,
1617
- partIndex,
1618
- part: runningPart,
1619
- source: "tool"
1620
- });
1621
- }) };
1622
- } catch (err) {
1623
- return { error: { message: err instanceof Error ? err.message : String(err) } };
1272
+ }
1273
+ //#endregion
1274
+ //#region src/utils/sanitize.ts
1275
+ /**
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"
1294
+ */
1295
+ function sanitizeSurrogates(text) {
1296
+ return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
1297
+ }
1298
+ //#endregion
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 }
1624
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;
1625
1344
  }
1626
- async function emitToolCallTerminalResult(toolCall, runnable, message, emit, result) {
1627
- const { partIndex, ...pendingPart } = toolCall;
1628
- const { time } = pendingPart;
1629
- await emitEvent(emit, {
1630
- type: "tool.execution.end",
1631
- ...runnable,
1632
- ...result
1633
- });
1634
- const part = {
1635
- ...pendingPart,
1636
- ...result,
1637
- time: {
1638
- start: time.start,
1639
- end: Date.now()
1640
- }
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)
1641
1353
  };
1642
- message.parts[partIndex] = part;
1643
- await emitEvent(emit, {
1644
- type: "message.part.start",
1645
- message,
1646
- partIndex,
1647
- part
1648
- });
1649
- await emitEvent(emit, {
1650
- type: "message.part.end",
1651
- message,
1652
- partIndex,
1653
- part
1654
- });
1655
- await emitEvent(emit, {
1656
- type: "message.update",
1657
- message
1658
- });
1659
- }
1660
- async function finalizeExecutedToolCall(config, currentContext, toolCall, runnable, message, emit, result, signal) {
1661
- if (config.afterToolExecution) {
1662
- const afterResult = await config.afterToolExecution({
1663
- context: currentContext,
1664
- assistantMessage: message,
1665
- toolCall: runnable,
1666
- result
1667
- }, signal);
1668
- if (afterResult) {
1669
- await emitToolCallTerminalResult(toolCall, runnable, message, emit, afterResult);
1670
- return;
1354
+ return {
1355
+ type: "image",
1356
+ source: {
1357
+ type: "base64",
1358
+ media_type: block.mimeType,
1359
+ data: block.data
1671
1360
  }
1672
- }
1673
- await emitToolCallTerminalResult(toolCall, runnable, message, emit, result);
1361
+ };
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
1397
+ };
1674
1398
  }
1675
- /**
1676
- * Main core loop shared by run and runContinue.
1677
- */
1678
- async function runLoop(config, currentContext, newMessages, emit, streamFn, signal) {
1679
- let firstTurn = true;
1680
- let pendingMessages = await config.getSteeringMessages?.() || [];
1681
- while (true) {
1682
- let hasMoreToolCalls = true;
1683
- while (hasMoreToolCalls || pendingMessages.length > 0) {
1684
- if (!firstTurn) await emitEvent(emit, { type: "turn.start" });
1685
- else firstTurn = false;
1686
- if (pendingMessages.length > 0) {
1687
- for (const message of pendingMessages) {
1688
- await emitEvent(emit, {
1689
- type: "message.start",
1690
- message
1691
- });
1692
- for (const [partIndex, part] of message.parts.entries()) {
1693
- await emitEvent(emit, {
1694
- type: "message.part.start",
1695
- message,
1696
- partIndex,
1697
- part
1698
- });
1699
- await emitEvent(emit, {
1700
- type: "message.part.end",
1701
- message,
1702
- partIndex,
1703
- part
1704
- });
1705
- await emitEvent(emit, {
1706
- type: "message.update",
1707
- message
1708
- });
1709
- }
1710
- await emitEvent(emit, {
1711
- type: "message.end",
1712
- message
1713
- });
1714
- currentContext.messages.push(message);
1715
- newMessages.push(message);
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;
1428
+ }
1429
+ if (sawToolCall) throw new Error("Assistant parts after a toolCall are not replayable for Anthropic");
1430
+ }
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
1716
1466
  }
1717
- pendingMessages = [];
1467
+ };
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;
1477
+ }
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
+ });
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
+ });
1522
+ }
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
+ }];
1534
+ }
1535
+ }
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");
1563
+ }
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 } : {}
1583
+ });
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",
1626
+ time: {
1627
+ created: Date.now(),
1628
+ completed: Date.now()
1629
+ }
1630
+ });
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;
1643
+ }
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
+ });
1724
+ }
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
1736
+ });
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;
1718
1757
  }
1719
- const message = await streamAssistantResponse(config, currentContext, emit, streamFn, signal);
1720
- if (message.stopReason === "error" || message.stopReason === "aborted") {
1721
- currentContext.messages.push(message);
1722
- newMessages.push(message);
1723
- await emitEvent(emit, {
1724
- type: "turn.end",
1725
- message
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
1726
1768
  });
1727
- await emitEvent(emit, {
1728
- type: "agent.end",
1729
- messages: newMessages
1769
+ else if (block.type === "thinking") stream.push({
1770
+ type: "thinking.end",
1771
+ partIndex,
1772
+ content: block.thinking,
1773
+ partial: output
1730
1774
  });
1731
- 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
+ }
1732
1785
  }
1733
- const toolCalls = message.parts.map((part, index) => ({
1734
- ...part,
1735
- partIndex: index
1736
- })).filter((part) => part.type === "toolCall" && part.status === "pending");
1737
- hasMoreToolCalls = toolCalls.length > 0;
1738
- if (hasMoreToolCalls) await executeToolCalls(config, currentContext, message, toolCalls, emit, signal);
1739
- currentContext.messages.push(message);
1740
- newMessages.push(message);
1741
- await emitEvent(emit, {
1742
- type: "turn.end",
1743
- message
1744
- });
1745
- pendingMessages = await config.getSteeringMessages?.() || [];
1746
- }
1747
- const followUpMessages = await config.getFollowUpMessages?.() || [];
1748
- if (followUpMessages.length > 0) {
1749
- pendingMessages = followUpMessages;
1750
- 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);
1751
1794
  }
1752
- break;
1753
- }
1754
- await emitEvent(emit, {
1755
- type: "agent.end",
1756
- messages: newMessages
1757
- });
1758
- }
1759
- async function runAgentLoop(config, context, prompts, emit, streamFn, signal) {
1760
- /**
1761
- * maintain 2 arrays to represent state, `context` the actual full state sent to the LLM,
1762
- * `newMessages` for intermediate current loop state.
1763
- * think of newMessages as subset of context.messages for the running current loop.
1764
- * they do move at the same speed staring with initial user prompts.
1765
- * */
1766
- const newMessages = [...prompts];
1767
- const currentContext = {
1768
- ...context,
1769
- messages: [...context.messages, ...prompts]
1770
- };
1771
- await emitEvent(emit, { type: "agent.start" });
1772
- await emitEvent(emit, { type: "turn.start" });
1773
- for (const prompt of prompts) {
1774
- await emitEvent(emit, {
1775
- type: "message.start",
1776
- 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
1777
1802
  });
1778
- for (const [partIndex, part] of prompt.parts.entries()) {
1779
- await emitEvent(emit, {
1780
- type: "message.part.start",
1781
- message: prompt,
1782
- partIndex,
1783
- part
1784
- });
1785
- await emitEvent(emit, {
1786
- type: "message.part.end",
1787
- message: prompt,
1788
- partIndex,
1789
- part
1790
- });
1791
- }
1792
- await emitEvent(emit, {
1793
- type: "message.end",
1794
- 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
1795
1813
  });
1814
+ stream.end();
1796
1815
  }
1797
- await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
1798
- return newMessages;
1799
- }
1800
- _Loop.runAgentLoop = runAgentLoop;
1801
- async function runAgentLoopContinue(config, context, emit, streamFn, signal) {
1802
- if (context.messages.length === 0) throw new AgentContextError({ message: "connot continue: no messages in context" });
1803
- if (context.messages[context.messages.length - 1]?.role === "assistant") throw new AgentContextError({ message: "cannot continue from message role: assistant" });
1804
- const newMessages = [];
1805
- const currentContext = { ...context };
1806
- await emitEvent(emit, { type: "agent.start" });
1807
- await emitEvent(emit, { type: "turn.start" });
1808
- await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
1809
- return newMessages;
1810
- }
1811
- _Loop.runAgentLoopContinue = runAgentLoopContinue;
1812
- function run(config, context, prompts, streamFn, signal) {
1813
- const stream = createAgentStream();
1814
- runAgentLoop(config, context, prompts, async (event) => {
1815
- stream.push(event);
1816
- }, streamFn, signal).then((messages) => {
1817
- stream.end(messages);
1818
- });
1819
- return stream;
1820
- }
1821
- _Loop.run = run;
1822
- function runContinue(config, context, streamFn, signal) {
1823
- const stream = createAgentStream();
1824
- runAgentLoopContinue(config, context, async (event) => {
1825
- stream.push(event);
1826
- }, streamFn, signal).then((messages) => {
1827
- stream.end(messages);
1828
- });
1829
- return stream;
1830
- }
1831
- _Loop.runContinue = runContinue;
1832
- })(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);
1833
1843
  //#endregion
1834
1844
  //#region src/agent/agent.ts
1835
1845
  let Agent;
@@ -2297,11 +2307,353 @@ let Agent;
2297
2307
  _Agent.create = create;
2298
2308
  })(Agent || (Agent = {}));
2299
2309
  //#endregion
2300
- //#region src/agent.ts
2301
- const agent = {
2302
- loop: Loop.run,
2303
- loopContinue: Loop.runContinue
2304
- };
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()))
2319
+ });
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()))
2326
+ });
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 = {}));
2305
2657
  //#endregion
2306
2658
  //#region src/llm.ts
2307
2659
  const llmImpl = async (provider, model) => Model.getModel(provider, model);
@@ -2337,6 +2689,6 @@ Stream.complete;
2337
2689
  Stream.streamSimple;
2338
2690
  Stream.completeSimple;
2339
2691
  //#endregion
2340
- export { Agent, Message, agent, llm, stream };
2692
+ export { Agent, CodeMode, Message, agent, llm, stream };
2341
2693
 
2342
2694
  //# sourceMappingURL=index.mjs.map