@nuvin/nuvin-core 1.5.2 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,7 +33,8 @@ var AgentEventTypes = {
33
33
  SubAgentStarted: "sub_agent_started",
34
34
  SubAgentToolCall: "sub_agent_tool_call",
35
35
  SubAgentToolResult: "sub_agent_tool_result",
36
- SubAgentCompleted: "sub_agent_completed"
36
+ SubAgentCompleted: "sub_agent_completed",
37
+ SubAgentMetrics: "sub_agent_metrics"
37
38
  };
38
39
 
39
40
  // src/metrics.ts
@@ -375,6 +376,391 @@ var PersistingConsoleEventPort = class {
375
376
  }
376
377
  };
377
378
 
379
+ // src/tools/tool-call-parser.ts
380
+ function parseJSON(jsonString) {
381
+ try {
382
+ const parsed = JSON.parse(jsonString || "{}");
383
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
384
+ return {
385
+ success: false,
386
+ error: "Parsed JSON must be an object, not an array or primitive"
387
+ };
388
+ }
389
+ return { success: true, data: parsed };
390
+ } catch (error) {
391
+ return {
392
+ success: false,
393
+ error: error instanceof Error ? error.message : "Invalid JSON syntax"
394
+ };
395
+ }
396
+ }
397
+
398
+ // src/tools/tool-validators.ts
399
+ var validateBashToolParams = (params) => {
400
+ const errors = [];
401
+ if (!params.cmd || typeof params.cmd !== "string") {
402
+ errors.push('Required parameter "cmd" must be a non-empty string');
403
+ }
404
+ if (params.cwd !== void 0 && typeof params.cwd !== "string") {
405
+ errors.push('Parameter "cwd" must be a string');
406
+ }
407
+ if (params.timeoutMs !== void 0) {
408
+ if (typeof params.timeoutMs !== "number" || params.timeoutMs < 1) {
409
+ errors.push('Parameter "timeoutMs" must be a positive number');
410
+ }
411
+ }
412
+ if (params.description !== void 0 && typeof params.description !== "string") {
413
+ errors.push('Parameter "description" must be a string');
414
+ }
415
+ if (errors.length > 0) {
416
+ return { valid: false, errors };
417
+ }
418
+ return {
419
+ valid: true,
420
+ data: {
421
+ cmd: params.cmd,
422
+ cwd: params.cwd,
423
+ timeoutMs: params.timeoutMs,
424
+ description: params.description
425
+ }
426
+ };
427
+ };
428
+ var validateFileReadParams = (params) => {
429
+ const errors = [];
430
+ if (!params.path || typeof params.path !== "string") {
431
+ errors.push('Required parameter "path" must be a non-empty string');
432
+ }
433
+ if (params.lineStart !== void 0) {
434
+ if (typeof params.lineStart !== "number" || params.lineStart < 1) {
435
+ errors.push('Parameter "lineStart" must be a positive integer');
436
+ }
437
+ }
438
+ if (params.lineEnd !== void 0) {
439
+ if (typeof params.lineEnd !== "number" || params.lineEnd < 1) {
440
+ errors.push('Parameter "lineEnd" must be a positive integer');
441
+ }
442
+ }
443
+ if (params.description !== void 0 && typeof params.description !== "string") {
444
+ errors.push('Parameter "description" must be a string');
445
+ }
446
+ if (errors.length > 0) {
447
+ return { valid: false, errors };
448
+ }
449
+ return {
450
+ valid: true,
451
+ data: {
452
+ path: params.path,
453
+ lineStart: params.lineStart,
454
+ lineEnd: params.lineEnd,
455
+ description: params.description
456
+ }
457
+ };
458
+ };
459
+ var validateFileEditParams = (params) => {
460
+ const errors = [];
461
+ if (!params.file_path || typeof params.file_path !== "string") {
462
+ errors.push('Required parameter "file_path" must be a non-empty string');
463
+ }
464
+ if (!params.old_text || typeof params.old_text !== "string") {
465
+ errors.push('Required parameter "old_text" must be a string');
466
+ }
467
+ if (params.new_text === void 0 || typeof params.new_text !== "string") {
468
+ errors.push('Required parameter "new_text" must be a string');
469
+ }
470
+ if (params.dry_run !== void 0 && typeof params.dry_run !== "boolean") {
471
+ errors.push('Parameter "dry_run" must be a boolean');
472
+ }
473
+ if (params.description !== void 0 && typeof params.description !== "string") {
474
+ errors.push('Parameter "description" must be a string');
475
+ }
476
+ if (errors.length > 0) {
477
+ return { valid: false, errors };
478
+ }
479
+ return {
480
+ valid: true,
481
+ data: {
482
+ file_path: params.file_path,
483
+ old_text: params.old_text,
484
+ new_text: params.new_text,
485
+ dry_run: params.dry_run,
486
+ description: params.description
487
+ }
488
+ };
489
+ };
490
+ var validateFileNewParams = (params) => {
491
+ const errors = [];
492
+ if (!params.file_path || typeof params.file_path !== "string") {
493
+ errors.push('Required parameter "file_path" must be a non-empty string');
494
+ }
495
+ if (params.content === void 0 || typeof params.content !== "string") {
496
+ errors.push('Required parameter "content" must be a string');
497
+ }
498
+ if (params.description !== void 0 && typeof params.description !== "string") {
499
+ errors.push('Parameter "description" must be a string');
500
+ }
501
+ if (errors.length > 0) {
502
+ return { valid: false, errors };
503
+ }
504
+ return {
505
+ valid: true,
506
+ data: {
507
+ file_path: params.file_path,
508
+ content: params.content,
509
+ description: params.description
510
+ }
511
+ };
512
+ };
513
+ var validateDirLsParams = (params) => {
514
+ const errors = [];
515
+ if (params.path !== void 0 && typeof params.path !== "string") {
516
+ errors.push('Parameter "path" must be a string');
517
+ }
518
+ if (params.limit !== void 0) {
519
+ if (typeof params.limit !== "number" || params.limit < 1) {
520
+ errors.push('Parameter "limit" must be a positive integer');
521
+ }
522
+ }
523
+ if (params.description !== void 0 && typeof params.description !== "string") {
524
+ errors.push('Parameter "description" must be a string');
525
+ }
526
+ if (errors.length > 0) {
527
+ return { valid: false, errors };
528
+ }
529
+ return {
530
+ valid: true,
531
+ data: {
532
+ path: params.path,
533
+ limit: params.limit,
534
+ description: params.description
535
+ }
536
+ };
537
+ };
538
+ var validateWebSearchParams = (params) => {
539
+ const errors = [];
540
+ if (!params.query || typeof params.query !== "string") {
541
+ errors.push('Required parameter "query" must be a non-empty string');
542
+ }
543
+ if (params.count !== void 0) {
544
+ if (typeof params.count !== "number" || params.count < 1 || params.count > 50) {
545
+ errors.push('Parameter "count" must be a number between 1 and 50');
546
+ }
547
+ }
548
+ if (params.offset !== void 0) {
549
+ if (typeof params.offset !== "number" || params.offset < 0) {
550
+ errors.push('Parameter "offset" must be a non-negative number');
551
+ }
552
+ }
553
+ if (params.domains !== void 0) {
554
+ if (!Array.isArray(params.domains) || !params.domains.every((d) => typeof d === "string")) {
555
+ errors.push('Parameter "domains" must be an array of strings');
556
+ }
557
+ }
558
+ if (params.recencyDays !== void 0) {
559
+ if (typeof params.recencyDays !== "number" || params.recencyDays < 1) {
560
+ errors.push('Parameter "recencyDays" must be a positive number');
561
+ }
562
+ }
563
+ if (params.lang !== void 0 && typeof params.lang !== "string") {
564
+ errors.push('Parameter "lang" must be a string');
565
+ }
566
+ if (params.region !== void 0 && typeof params.region !== "string") {
567
+ errors.push('Parameter "region" must be a string');
568
+ }
569
+ if (params.safe !== void 0 && typeof params.safe !== "boolean") {
570
+ errors.push('Parameter "safe" must be a boolean');
571
+ }
572
+ if (params.type !== void 0) {
573
+ if (params.type !== "web" && params.type !== "images") {
574
+ errors.push('Parameter "type" must be either "web" or "images"');
575
+ }
576
+ }
577
+ if (params.hydrateMeta !== void 0 && typeof params.hydrateMeta !== "boolean") {
578
+ errors.push('Parameter "hydrateMeta" must be a boolean');
579
+ }
580
+ if (params.description !== void 0 && typeof params.description !== "string") {
581
+ errors.push('Parameter "description" must be a string');
582
+ }
583
+ if (errors.length > 0) {
584
+ return { valid: false, errors };
585
+ }
586
+ return {
587
+ valid: true,
588
+ data: {
589
+ query: params.query,
590
+ count: params.count,
591
+ offset: params.offset,
592
+ domains: params.domains,
593
+ recencyDays: params.recencyDays,
594
+ lang: params.lang,
595
+ region: params.region,
596
+ safe: params.safe,
597
+ type: params.type,
598
+ hydrateMeta: params.hydrateMeta,
599
+ description: params.description
600
+ }
601
+ };
602
+ };
603
+ var validateWebFetchParams = (params) => {
604
+ const errors = [];
605
+ if (!params.url || typeof params.url !== "string") {
606
+ errors.push('Required parameter "url" must be a non-empty string');
607
+ }
608
+ if (params.description !== void 0 && typeof params.description !== "string") {
609
+ errors.push('Parameter "description" must be a string');
610
+ }
611
+ if (errors.length > 0) {
612
+ return { valid: false, errors };
613
+ }
614
+ return {
615
+ valid: true,
616
+ data: {
617
+ url: params.url,
618
+ description: params.description
619
+ }
620
+ };
621
+ };
622
+ var validateTodoWriteParams = (params) => {
623
+ const errors = [];
624
+ if (!params.todos || !Array.isArray(params.todos)) {
625
+ errors.push('Required parameter "todos" must be an array');
626
+ } else {
627
+ for (let i = 0; i < params.todos.length; i++) {
628
+ const todo = params.todos[i];
629
+ if (typeof todo !== "object" || todo === null) {
630
+ errors.push(`todos[${i}] must be an object`);
631
+ continue;
632
+ }
633
+ if (!todo.id || typeof todo.id !== "string") {
634
+ errors.push(`todos[${i}].id must be a non-empty string`);
635
+ }
636
+ if (!todo.content || typeof todo.content !== "string") {
637
+ errors.push(`todos[${i}].content must be a non-empty string`);
638
+ }
639
+ if (!todo.status || !["pending", "in_progress", "completed"].includes(todo.status)) {
640
+ errors.push(`todos[${i}].status must be one of: pending, in_progress, completed`);
641
+ }
642
+ if (!todo.priority || !["high", "medium", "low"].includes(todo.priority)) {
643
+ errors.push(`todos[${i}].priority must be one of: high, medium, low`);
644
+ }
645
+ if (todo.createdAt !== void 0 && typeof todo.createdAt !== "string") {
646
+ errors.push(`todos[${i}].createdAt must be a string`);
647
+ }
648
+ }
649
+ }
650
+ if (params.description !== void 0 && typeof params.description !== "string") {
651
+ errors.push('Parameter "description" must be a string');
652
+ }
653
+ if (errors.length > 0) {
654
+ return { valid: false, errors };
655
+ }
656
+ return {
657
+ valid: true,
658
+ data: {
659
+ todos: params.todos,
660
+ description: params.description
661
+ }
662
+ };
663
+ };
664
+ var validateAssignTaskParams = (params) => {
665
+ const errors = [];
666
+ if (!params.agent || typeof params.agent !== "string") {
667
+ errors.push('Required parameter "agent" must be a non-empty string');
668
+ }
669
+ if (!params.task || typeof params.task !== "string") {
670
+ errors.push('Required parameter "task" must be a non-empty string');
671
+ }
672
+ if (!params.description || typeof params.description !== "string") {
673
+ errors.push('Required parameter "description" must be a non-empty string');
674
+ }
675
+ if (errors.length > 0) {
676
+ return { valid: false, errors };
677
+ }
678
+ return {
679
+ valid: true,
680
+ data: {
681
+ agent: params.agent,
682
+ task: params.task,
683
+ description: params.description
684
+ }
685
+ };
686
+ };
687
+ var toolValidators = {
688
+ bash_tool: validateBashToolParams,
689
+ file_read: validateFileReadParams,
690
+ file_edit: validateFileEditParams,
691
+ file_new: validateFileNewParams,
692
+ dir_ls: validateDirLsParams,
693
+ web_search: validateWebSearchParams,
694
+ web_fetch: validateWebFetchParams,
695
+ todo_write: validateTodoWriteParams,
696
+ assign_task: validateAssignTaskParams
697
+ };
698
+
699
+ // src/tools/tool-call-converter.ts
700
+ function convertToolCall(toolCall, options = {}) {
701
+ const { strict = false } = options;
702
+ const parseResult = parseJSON(toolCall.function.arguments);
703
+ if (!parseResult.success) {
704
+ return {
705
+ valid: false,
706
+ error: parseResult.error,
707
+ errorType: "parse",
708
+ callId: toolCall.id,
709
+ toolName: toolCall.function.name,
710
+ rawArguments: toolCall.function.arguments
711
+ };
712
+ }
713
+ const toolName = toolCall.function.name;
714
+ const parameters = parseResult.data;
715
+ const validator = toolValidators[toolName];
716
+ if (validator) {
717
+ const validationResult = validator(parameters);
718
+ if (!validationResult.valid) {
719
+ if (strict) {
720
+ return {
721
+ valid: false,
722
+ error: `Validation failed: ${validationResult.errors.join("; ")}`,
723
+ errorType: "validation",
724
+ callId: toolCall.id,
725
+ toolName: toolCall.function.name,
726
+ rawArguments: toolCall.function.arguments
727
+ };
728
+ }
729
+ console.warn(`[ToolCallConverter] Validation warnings for ${toolName}:`, validationResult.errors);
730
+ }
731
+ }
732
+ return {
733
+ valid: true,
734
+ invocation: {
735
+ id: toolCall.id,
736
+ name: toolCall.function.name,
737
+ parameters
738
+ }
739
+ };
740
+ }
741
+ function convertToolCalls(toolCalls, options = {}) {
742
+ const { strict = false, throwOnError = true } = options;
743
+ const invocations = [];
744
+ for (const tc of toolCalls) {
745
+ const result = convertToolCall(tc, { strict });
746
+ if (!result.valid) {
747
+ const errorMsg = `Tool call ${result.callId} (${result.toolName}): ${result.error}`;
748
+ if (throwOnError) {
749
+ throw new Error(errorMsg);
750
+ }
751
+ console.error(`[ToolCallConverter] ${errorMsg}`);
752
+ invocations.push({
753
+ id: result.callId,
754
+ name: result.toolName,
755
+ parameters: {}
756
+ });
757
+ } else {
758
+ invocations.push(result.invocation);
759
+ }
760
+ }
761
+ return invocations;
762
+ }
763
+
378
764
  // src/orchestrator.ts
379
765
  var removeAttachmentTokens = (value, attachments) => {
380
766
  return attachments.reduce((acc, attachment) => {
@@ -629,37 +1015,30 @@ var AgentOrchestrator = class {
629
1015
  if (!_llm) {
630
1016
  throw new Error("LLM provider not set");
631
1017
  }
632
- if (opts.retry) {
633
- providerMsgs = this.context.toProviderMessages(history, this.cfg.systemPrompt, []);
634
- userMessages = [];
635
- userDisplay = "[Retry]";
636
- enhanced = [];
1018
+ const normalized = typeof content === "string" ? { text: content, displayText: content, attachments: [] } : {
1019
+ text: content.text ?? "",
1020
+ displayText: content.displayText,
1021
+ attachments: Array.isArray(content.attachments) ? content.attachments : []
1022
+ };
1023
+ const attachments = normalized.attachments;
1024
+ enhanced = this.reminders.enhance(normalized.text, { conversationId: convo });
1025
+ const enhancedCombined = enhanced.join("\n");
1026
+ const messageParts = buildMessageParts(enhancedCombined, attachments);
1027
+ let userContent;
1028
+ if (attachments.length > 0 || messageParts.some((part) => part.type === "image")) {
1029
+ userContent = { type: "parts", parts: messageParts };
1030
+ } else if (messageParts.length === 1 && messageParts[0]?.type === "text") {
1031
+ userContent = messageParts[0].text;
1032
+ } else if (messageParts.length > 0) {
1033
+ userContent = { type: "parts", parts: messageParts };
637
1034
  } else {
638
- const normalized = typeof content === "string" ? { text: content, displayText: content, attachments: [] } : {
639
- text: content.text ?? "",
640
- displayText: content.displayText,
641
- attachments: Array.isArray(content.attachments) ? content.attachments : []
642
- };
643
- const attachments = normalized.attachments;
644
- enhanced = this.reminders.enhance(normalized.text, { conversationId: convo });
645
- const enhancedCombined = enhanced.join("\n");
646
- const messageParts = buildMessageParts(enhancedCombined, attachments);
647
- let userContent;
648
- if (attachments.length > 0 || messageParts.some((part) => part.type === "image")) {
649
- userContent = { type: "parts", parts: messageParts };
650
- } else if (messageParts.length === 1 && messageParts[0]?.type === "text") {
651
- userContent = messageParts[0].text;
652
- } else if (messageParts.length > 0) {
653
- userContent = { type: "parts", parts: messageParts };
654
- } else {
655
- userContent = enhancedCombined;
656
- }
657
- providerMsgs = this.context.toProviderMessages(history, this.cfg.systemPrompt, [userContent]);
658
- userDisplay = resolveDisplayText(normalized.text, attachments, normalized.displayText);
659
- const userTimestamp = this.clock.iso();
660
- userMessages = [{ id: this.ids.uuid(), role: "user", content: userContent, timestamp: userTimestamp }];
661
- await this.memory.append(convo, userMessages);
1035
+ userContent = enhancedCombined;
662
1036
  }
1037
+ providerMsgs = this.context.toProviderMessages(history, this.cfg.systemPrompt, [userContent]);
1038
+ userDisplay = resolveDisplayText(normalized.text, attachments, normalized.displayText);
1039
+ const userTimestamp = this.clock.iso();
1040
+ userMessages = [{ id: this.ids.uuid(), role: "user", content: userContent, timestamp: userTimestamp }];
1041
+ await this.memory.append(convo, userMessages);
663
1042
  if (opts.signal?.aborted) throw new Error("Aborted");
664
1043
  const toolDefs = this.tools.getToolDefinitions(this.cfg.enabledTools ?? []);
665
1044
  const toolNames = toolDefs.map((t) => t.function.name);
@@ -812,7 +1191,14 @@ var AgentOrchestrator = class {
812
1191
  };
813
1192
  const toolResultMsgs = [];
814
1193
  for (const tr of toolResults) {
815
- const contentStr = tr.status === "error" ? String(tr.result) : typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
1194
+ let contentStr;
1195
+ if (tr.status === "error") {
1196
+ contentStr = tr.result;
1197
+ } else if (tr.type === "text") {
1198
+ contentStr = tr.result;
1199
+ } else {
1200
+ contentStr = JSON.stringify(tr.result, null, 2);
1201
+ }
816
1202
  toolResultMsgs.push({
817
1203
  id: tr.id,
818
1204
  role: "tool",
@@ -838,7 +1224,14 @@ var AgentOrchestrator = class {
838
1224
  tool_calls: approvedCalls
839
1225
  });
840
1226
  for (const tr of toolResults) {
841
- const contentStr = tr.status === "error" ? String(tr.result) : typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
1227
+ let contentStr;
1228
+ if (tr.status === "error") {
1229
+ contentStr = tr.result;
1230
+ } else if (tr.type === "text") {
1231
+ contentStr = tr.result;
1232
+ } else {
1233
+ contentStr = JSON.stringify(tr.result, null, 2);
1234
+ }
842
1235
  accumulatedMessages.push({ role: "tool", content: contentStr, tool_call_id: tr.id, name: tr.name });
843
1236
  }
844
1237
  if (opts.signal?.aborted) throw new Error("Aborted");
@@ -978,14 +1371,9 @@ var AgentOrchestrator = class {
978
1371
  }
979
1372
  }
980
1373
  toInvocations(toolCalls) {
981
- return toolCalls.map((tc) => {
982
- let parameters = {};
983
- try {
984
- parameters = JSON.parse(tc.function.arguments || "{}");
985
- } catch {
986
- parameters = {};
987
- }
988
- return { id: tc.id, name: tc.function.name, parameters };
1374
+ return convertToolCalls(toolCalls, {
1375
+ strict: this.cfg.strictToolValidation ?? false,
1376
+ throwOnError: true
989
1377
  });
990
1378
  }
991
1379
  };
@@ -1543,6 +1931,18 @@ var TodoStore = class {
1543
1931
  }
1544
1932
  };
1545
1933
 
1934
+ // src/tools/result-helpers.ts
1935
+ function okText(result, metadata) {
1936
+ return { status: "success", type: "text", result, metadata };
1937
+ }
1938
+ function okJson(result, metadata) {
1939
+ return { status: "success", type: "json", result, metadata };
1940
+ }
1941
+ function err(result, metadata, errorReason) {
1942
+ const finalMetadata = errorReason ? { ...metadata, errorReason } : metadata;
1943
+ return { status: "error", type: "text", result, metadata: finalMetadata };
1944
+ }
1945
+
1546
1946
  // src/tools/TodoWriteTool.ts
1547
1947
  var TodoWriteTool = class {
1548
1948
  constructor(store) {
@@ -1634,28 +2034,28 @@ When in doubt, use this tool. Being proactive with task management demonstrates
1634
2034
  const todos = params?.todos;
1635
2035
  const ctx = context;
1636
2036
  if (!Array.isArray(todos) || todos.length === 0) {
1637
- return { status: "error", type: "text", result: 'Parameter "todos" must be a non-empty array' };
2037
+ return err('Parameter "todos" must be a non-empty array', void 0, "invalid_input" /* InvalidInput */);
1638
2038
  }
1639
2039
  for (let i = 0; i < todos.length; i++) {
1640
2040
  const t = todos[i];
1641
2041
  if (!t) continue;
1642
2042
  if (!t.content || typeof t.content !== "string" || !t.content.trim()) {
1643
- return { status: "error", type: "text", result: `Todo ${i + 1}: content required` };
2043
+ return err(`Todo ${i + 1}: content required`, void 0, "invalid_input" /* InvalidInput */);
1644
2044
  }
1645
2045
  if (!["pending", "in_progress", "completed"].includes(String(t.status))) {
1646
- return { status: "error", type: "text", result: `Todo ${i + 1}: invalid status` };
2046
+ return err(`Todo ${i + 1}: invalid status`, void 0, "invalid_input" /* InvalidInput */);
1647
2047
  }
1648
2048
  if (!["high", "medium", "low"].includes(String(t.priority))) {
1649
- return { status: "error", type: "text", result: `Todo ${i + 1}: invalid priority` };
2049
+ return err(`Todo ${i + 1}: invalid priority`, void 0, "invalid_input" /* InvalidInput */);
1650
2050
  }
1651
2051
  if (!t.id || typeof t.id !== "string" || !t.id.trim()) {
1652
- return { status: "error", type: "text", result: `Todo ${i + 1}: id required` };
2052
+ return err(`Todo ${i + 1}: id required`, void 0, "invalid_input" /* InvalidInput */);
1653
2053
  }
1654
2054
  }
1655
2055
  const ids = todos.map((t) => t.id);
1656
2056
  const unique = new Set(ids);
1657
2057
  if (unique.size !== ids.length) {
1658
- return { status: "error", type: "text", result: "Todo items must have unique IDs" };
2058
+ return err("Todo items must have unique IDs", void 0, "invalid_input" /* InvalidInput */);
1659
2059
  }
1660
2060
  const conversationId = ctx?.sessionId || ctx?.conversationId || "default";
1661
2061
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
@@ -1694,17 +2094,13 @@ All todo items are now completed! Ask about next steps. Completed list:
1694
2094
  Your todo list has changed. DO NOT mention this explicitly to the user. Latest contents:
1695
2095
  [${serialized}].
1696
2096
  </system-reminder>`;
1697
- return {
1698
- status: "success",
1699
- type: "text",
1700
- result: systemReminder,
1701
- metadata: {
1702
- recentChanges: true,
1703
- stats,
1704
- progress: `${progress}%`,
1705
- allCompleted
1706
- }
1707
- };
2097
+ return okText(systemReminder, {
2098
+ recentChanges: true,
2099
+ stats,
2100
+ progress: `${progress}%`,
2101
+ allCompleted,
2102
+ conversationId
2103
+ });
1708
2104
  }
1709
2105
  };
1710
2106
 
@@ -1919,21 +2315,35 @@ var WebSearchTool = class {
1919
2315
  };
1920
2316
  }
1921
2317
  provider = new GoogleCseProvider();
2318
+ /**
2319
+ * Execute web search using Google Programmable Search Engine
2320
+ *
2321
+ * @param params - Search query and optional filters (domains, recency, language, region)
2322
+ * @param ctx - Execution context with optional abort signal
2323
+ * @returns Structured JSON with search results including title, URL, snippet, and metadata
2324
+ *
2325
+ * @example
2326
+ * ```typescript
2327
+ * const result = await webSearchTool.execute({
2328
+ * query: 'TypeScript discriminated unions',
2329
+ * count: 10,
2330
+ * lang: 'en'
2331
+ * });
2332
+ * if (result.status === 'success' && result.type === 'json') {
2333
+ * console.log(result.result.count); // number of results
2334
+ * result.result.items.forEach(item => {
2335
+ * console.log(`${item.title}: ${item.url}`);
2336
+ * });
2337
+ * }
2338
+ * ```
2339
+ */
1922
2340
  async execute(params, ctx) {
1923
2341
  if (ctx?.signal?.aborted) {
1924
- return {
1925
- type: "text",
1926
- status: "error",
1927
- result: "Search aborted by user"
1928
- };
2342
+ return err("Search aborted by user", void 0, "aborted" /* Aborted */);
1929
2343
  }
1930
2344
  const query = typeof params.query === "string" ? params.query : "";
1931
2345
  if (!query) {
1932
- return {
1933
- type: "text",
1934
- status: "error",
1935
- result: "Missing required parameter: query"
1936
- };
2346
+ return err("Missing required parameter: query", void 0, "invalid_input" /* InvalidInput */);
1937
2347
  }
1938
2348
  const p = {
1939
2349
  query: String(params.query ?? "").trim(),
@@ -1948,14 +2358,10 @@ var WebSearchTool = class {
1948
2358
  hydrateMeta: params.hydrateMeta ?? false
1949
2359
  };
1950
2360
  if (!p.query) {
1951
- return { status: "error", type: "text", result: "Missing 'query'" };
2361
+ return err("Missing 'query'", void 0, "invalid_input" /* InvalidInput */);
1952
2362
  }
1953
2363
  if (!process.env.GOOGLE_CSE_KEY || !process.env.GOOGLE_CSE_CX) {
1954
- return {
1955
- status: "error",
1956
- type: "text",
1957
- result: "GOOGLE_CSE_KEY and GOOGLE_CSE_CX are required for this tool (Google CSE only)."
1958
- };
2364
+ return err("GOOGLE_CSE_KEY and GOOGLE_CSE_CX are required for this tool (Google CSE only).", void 0, "invalid_input" /* InvalidInput */);
1959
2365
  }
1960
2366
  try {
1961
2367
  let results = await this.provider.search(p, ctx?.signal);
@@ -1969,23 +2375,30 @@ var WebSearchTool = class {
1969
2375
  seen.add(k);
1970
2376
  return true;
1971
2377
  }).slice(0, p.count);
1972
- return {
1973
- status: "success",
1974
- type: "json",
1975
- result: {
1976
- engine: this.provider.name,
1977
- count: results.length,
1978
- items: results
1979
- }
1980
- };
2378
+ const appliedFilters = {};
2379
+ if (p.domains.length > 0) appliedFilters.domains = p.domains;
2380
+ if (p.recencyDays > 0) appliedFilters.recencyDays = p.recencyDays;
2381
+ if (p.lang && p.lang !== "en") appliedFilters.lang = p.lang;
2382
+ if (p.region) appliedFilters.region = p.region;
2383
+ return okJson({
2384
+ engine: this.provider.name,
2385
+ count: results.length,
2386
+ items: results,
2387
+ query: p.query,
2388
+ appliedFilters: Object.keys(appliedFilters).length > 0 ? appliedFilters : void 0
2389
+ }, {
2390
+ offset: p.offset,
2391
+ totalRequested: p.count,
2392
+ hydrated: p.hydrateMeta
2393
+ });
1981
2394
  } catch (error) {
1982
2395
  const message = error instanceof Error ? error.message : String(error);
1983
2396
  const isAborted = message.includes("aborted by user");
1984
- return {
1985
- status: "error",
1986
- type: "text",
1987
- result: isAborted ? "Search aborted by user" : message.slice(0, 500)
1988
- };
2397
+ return err(
2398
+ isAborted ? "Search aborted by user" : message.slice(0, 500),
2399
+ void 0,
2400
+ isAborted ? "aborted" /* Aborted */ : "unknown" /* Unknown */
2401
+ );
1989
2402
  }
1990
2403
  }
1991
2404
  };
@@ -2024,10 +2437,10 @@ var WebFetchTool = class {
2024
2437
  async execute(params, ctx) {
2025
2438
  const target = String(params.url ?? "");
2026
2439
  if (!target) {
2027
- return { status: "error", type: "text", result: "URL parameter is required" };
2440
+ return err("URL parameter is required", void 0, "invalid_input" /* InvalidInput */);
2028
2441
  }
2029
2442
  if (ctx?.signal?.aborted) {
2030
- return { status: "error", type: "text", result: "Fetch aborted by user" };
2443
+ return err("Fetch aborted by user", void 0, "aborted" /* Aborted */);
2031
2444
  }
2032
2445
  try {
2033
2446
  const res = await fetch(target, {
@@ -2037,33 +2450,41 @@ var WebFetchTool = class {
2037
2450
  signal: ctx?.signal
2038
2451
  });
2039
2452
  if (!res.ok) {
2040
- return {
2041
- status: "error",
2042
- type: "text",
2043
- result: `HTTP ${res.status} ${res.statusText}`
2044
- };
2453
+ return err(`HTTP ${res.status} ${res.statusText}`, { url: target }, "network_error" /* NetworkError */);
2045
2454
  }
2046
2455
  const contentType = res.headers.get("content-type") || "";
2047
2456
  const text = await res.text();
2457
+ const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
2458
+ let finalText;
2459
+ let format;
2048
2460
  if (contentType.includes("application/json")) {
2049
- const markdown = `\`\`\`json
2461
+ finalText = `\`\`\`json
2050
2462
  ${text}
2051
2463
  \`\`\``;
2052
- return { status: "success", type: "text", result: markdown };
2464
+ format = "json";
2053
2465
  } else if (contentType.includes("text/html")) {
2054
- const markdown = this.turndownService.turndown(text);
2055
- return { status: "success", type: "text", result: markdown };
2466
+ finalText = this.turndownService.turndown(text);
2467
+ format = "markdown";
2056
2468
  } else {
2057
- return { status: "success", type: "text", result: text };
2469
+ finalText = text;
2470
+ format = "text";
2058
2471
  }
2472
+ return okText(finalText, {
2473
+ url: target,
2474
+ contentType,
2475
+ statusCode: res.status,
2476
+ format,
2477
+ size: text.length,
2478
+ fetchedAt
2479
+ });
2059
2480
  } catch (error) {
2060
2481
  const errorMessage = error instanceof Error ? error.message : String(error);
2061
2482
  const isAborted = errorMessage.includes("aborted") || error.name === "AbortError";
2062
- return {
2063
- status: "error",
2064
- type: "text",
2065
- result: isAborted ? "Fetch aborted by user" : errorMessage
2066
- };
2483
+ return err(
2484
+ isAborted ? "Fetch aborted by user" : errorMessage,
2485
+ { url: target },
2486
+ isAborted ? "aborted" /* Aborted */ : "network_error" /* NetworkError */
2487
+ );
2067
2488
  }
2068
2489
  }
2069
2490
  };
@@ -2071,17 +2492,6 @@ ${text}
2071
2492
  // src/tools/FileReadTool.ts
2072
2493
  import { promises as fs3 } from "fs";
2073
2494
  import * as path3 from "path";
2074
-
2075
- // src/tools/result-helpers.ts
2076
- function ok(result, metadata) {
2077
- return { status: "success", type: "text", result, metadata };
2078
- }
2079
- function err(result, metadata, errorReason) {
2080
- const finalMetadata = errorReason ? { ...metadata, errorReason } : metadata;
2081
- return { status: "error", type: "text", result, metadata: finalMetadata };
2082
- }
2083
-
2084
- // src/tools/FileReadTool.ts
2085
2495
  var FileReadTool = class {
2086
2496
  name = "file_read";
2087
2497
  rootDir;
@@ -2114,24 +2524,45 @@ var FileReadTool = class {
2114
2524
  parameters: this.parameters
2115
2525
  };
2116
2526
  }
2527
+ /**
2528
+ * Read file contents with optional line range selection
2529
+ *
2530
+ * @param params - File path and optional line range (lineStart, lineEnd)
2531
+ * @param context - Execution context with optional workspace directory
2532
+ * @returns File content as text with metadata including path, size, and line range
2533
+ *
2534
+ * @example
2535
+ * ```typescript
2536
+ * const result = await fileReadTool.execute({ path: 'package.json' });
2537
+ * if (result.status === 'success' && result.type === 'text') {
2538
+ * console.log(result.result); // file contents
2539
+ * console.log(result.metadata?.path); // resolved file path
2540
+ * console.log(result.metadata?.size); // file size in bytes
2541
+ * }
2542
+ * ```
2543
+ */
2117
2544
  async execute(params, context) {
2118
2545
  try {
2119
2546
  if (!params.path || typeof params.path !== "string") {
2120
- return err('Parameter "path" must be a non-empty string');
2547
+ return err('Parameter "path" must be a non-empty string', void 0, "invalid_input" /* InvalidInput */);
2121
2548
  }
2122
2549
  const abs = this.resolveSafePath(params.path, context);
2123
2550
  const st = await fs3.stat(abs).catch(() => null);
2124
- if (!st || !st.isFile()) return err(`File not found: ${params.path}`);
2551
+ if (!st || !st.isFile()) return err(`File not found: ${params.path}`, { path: params.path }, "not_found" /* NotFound */);
2125
2552
  if (st.size > this.maxBytesHard) {
2126
- return err(`File too large (${st.size} bytes). Hard cap is ${this.maxBytesHard} bytes.`);
2553
+ return err(`File too large (${st.size} bytes). Hard cap is ${this.maxBytesHard} bytes.`, { path: params.path }, "invalid_input" /* InvalidInput */);
2127
2554
  }
2128
2555
  const payload = await fs3.readFile(abs);
2129
2556
  let text = payload.toString("utf8");
2557
+ const bomStripped = text.charCodeAt(0) === 65279;
2130
2558
  text = stripUtfBom(text);
2131
2559
  const metadata = {
2132
2560
  path: params.path,
2133
2561
  created: st.birthtime.toISOString(),
2134
- modified: st.mtime.toISOString()
2562
+ modified: st.mtime.toISOString(),
2563
+ size: st.size,
2564
+ encoding: "utf8",
2565
+ bomStripped
2135
2566
  };
2136
2567
  if (params.lineStart || params.lineEnd) {
2137
2568
  const lines = text.split(/\r?\n/);
@@ -2144,17 +2575,19 @@ var FileReadTool = class {
2144
2575
  return `${lineNum}\u2502${line}`;
2145
2576
  });
2146
2577
  const slice = numberedLines.join("\n");
2147
- return ok(slice, {
2578
+ return okText(slice, {
2148
2579
  ...metadata,
2149
- linesTotal: totalLines,
2150
- lineStart: lo,
2151
- lineEnd: hi
2580
+ lineRange: {
2581
+ lineStart: lo,
2582
+ lineEnd: hi,
2583
+ linesTotal: totalLines
2584
+ }
2152
2585
  });
2153
2586
  }
2154
- return ok(text, metadata);
2587
+ return okText(text, metadata);
2155
2588
  } catch (e) {
2156
2589
  const message = e instanceof Error ? e.message : String(e);
2157
- return err(message);
2590
+ return err(message, { path: params.path }, "unknown" /* Unknown */);
2158
2591
  }
2159
2592
  }
2160
2593
  // ---------- helpers ----------
@@ -2209,16 +2642,22 @@ var FileNewTool = class {
2209
2642
  }
2210
2643
  async execute(p, ctx) {
2211
2644
  try {
2212
- if (!p.file_path?.trim()) return err('Parameter "file_path" is required.');
2213
- if (typeof p.content !== "string") return err('"content" must be a string.');
2645
+ if (!p.file_path?.trim()) return err('Parameter "file_path" is required.', void 0, "invalid_input" /* InvalidInput */);
2646
+ if (typeof p.content !== "string") return err('"content" must be a string.', void 0, "invalid_input" /* InvalidInput */);
2214
2647
  const abs = this.resolveSafePath(p.file_path, ctx);
2648
+ const existsBefore = await fs4.stat(abs).then(() => true).catch(() => false);
2215
2649
  await fs4.mkdir(path4.dirname(abs), { recursive: true });
2216
2650
  const bytes = Buffer.from(p.content, "utf8");
2217
2651
  await this.writeAtomic(abs, bytes);
2218
- return ok(`File written at ${p.file_path}.`, { file_path: p.file_path, bytes: bytes.length });
2652
+ return okText(`File written at ${p.file_path}.`, {
2653
+ file_path: p.file_path,
2654
+ bytes: bytes.length,
2655
+ created: (/* @__PURE__ */ new Date()).toISOString(),
2656
+ overwritten: existsBefore
2657
+ });
2219
2658
  } catch (e) {
2220
2659
  const message = e instanceof Error ? e.message : String(e);
2221
- return err(message);
2660
+ return err(message, void 0, "unknown" /* Unknown */);
2222
2661
  }
2223
2662
  }
2224
2663
  // helpers
@@ -2295,21 +2734,21 @@ var FileEditTool = class {
2295
2734
  }
2296
2735
  async execute(p, ctx) {
2297
2736
  try {
2298
- if (!p.file_path?.trim()) return err('Parameter "file_path" is required.');
2737
+ if (!p.file_path?.trim()) return err('Parameter "file_path" is required.', void 0, "invalid_input" /* InvalidInput */);
2299
2738
  const abs = resolveAbsoluteWithinRoot(p.file_path, ctx, this.rootDir);
2300
2739
  if (typeof p.old_text !== "string") {
2301
- return err("old_text must be a string.");
2740
+ return err("old_text must be a string.", void 0, "invalid_input" /* InvalidInput */);
2302
2741
  }
2303
2742
  if (typeof p.new_text !== "string") {
2304
- return err("new_text must be a string.");
2743
+ return err("new_text must be a string.", void 0, "invalid_input" /* InvalidInput */);
2305
2744
  }
2306
2745
  const st = await fs5.stat(abs).catch(() => null);
2307
2746
  const exists = !!st && st.isFile();
2308
2747
  if (!exists || !st) {
2309
- return err("File does not exist. Use a file creation tool to create new files.");
2748
+ return err("File does not exist. Use a file creation tool to create new files.", void 0, "not_found" /* NotFound */);
2310
2749
  }
2311
2750
  if (st.size > this.maxBytes) {
2312
- return err(`File too large (${st.size} bytes). Cap is ${this.maxBytes}.`);
2751
+ return err(`File too large (${st.size} bytes). Cap is ${this.maxBytes}.`, void 0, "invalid_input" /* InvalidInput */);
2313
2752
  }
2314
2753
  const originalBytes = await fs5.readFile(abs);
2315
2754
  const originalEol = detectEol(originalBytes) ?? "lf";
@@ -2322,14 +2761,16 @@ var FileEditTool = class {
2322
2761
  const preview = oldTextLF.slice(0, 100).replace(/\n/g, "\\n");
2323
2762
  return err(
2324
2763
  `old_text not found in file. Make sure it matches exactly including whitespace.
2325
- Searching for: "${preview}${oldTextLF.length > 100 ? "..." : ""}"`
2764
+ Searching for: "${preview}${oldTextLF.length > 100 ? "..." : ""}"`,
2765
+ void 0,
2766
+ "not_found" /* NotFound */
2326
2767
  );
2327
2768
  }
2328
2769
  const resultLF = originalLF.slice(0, idx) + newTextLF + originalLF.slice(idx + oldTextLF.length);
2329
2770
  const finalText = convertEol(resultLF, originalEol);
2330
2771
  const finalBytes = Buffer.from(finalText, "utf8");
2331
2772
  if (finalBytes.length > this.maxBytes) {
2332
- return err(`Resulting file too large (${finalBytes.length} bytes). Cap is ${this.maxBytes}.`);
2773
+ return err(`Resulting file too large (${finalBytes.length} bytes). Cap is ${this.maxBytes}.`, void 0, "invalid_input" /* InvalidInput */);
2333
2774
  }
2334
2775
  const beforeSha = sha256(originalBytes);
2335
2776
  const afterSha = sha256(finalBytes);
@@ -2338,10 +2779,13 @@ Searching for: "${preview}${oldTextLF.length > 100 ? "..." : ""}"`
2338
2779
  await atomicWrite(abs, finalBytes);
2339
2780
  }
2340
2781
  const lineNumbers = this.calculateLineNumbers(originalLF, oldTextLF, newTextLF, idx);
2341
- return ok(
2782
+ return okText(
2342
2783
  noChange ? "No changes (content identical)." : p.dry_run ? "Validated (dry run: no write)." : "Edit applied successfully.",
2343
2784
  {
2344
2785
  path: showPath(abs, this.rootDir),
2786
+ created: st.birthtime.toISOString(),
2787
+ modified: st.mtime.toISOString(),
2788
+ size: st.size,
2345
2789
  eol: originalEol,
2346
2790
  oldTextLength: oldTextLF.length,
2347
2791
  newTextLength: newTextLF.length,
@@ -2349,11 +2793,12 @@ Searching for: "${preview}${oldTextLF.length > 100 ? "..." : ""}"`
2349
2793
  beforeSha,
2350
2794
  afterSha,
2351
2795
  dryRun: !!p.dry_run,
2352
- lineNumbers
2796
+ lineNumbers,
2797
+ noChange
2353
2798
  }
2354
2799
  );
2355
2800
  } catch (e) {
2356
- return err(e instanceof Error ? e.message : String(e));
2801
+ return err(e instanceof Error ? e.message : String(e), void 0, "unknown" /* Unknown */);
2357
2802
  }
2358
2803
  }
2359
2804
  calculateLineNumbers(originalText, oldText, newText, matchIndex) {
@@ -2500,6 +2945,23 @@ var BashTool = class {
2500
2945
  parameters: this.parameters
2501
2946
  };
2502
2947
  }
2948
+ /**
2949
+ * Execute bash command with timeout protection
2950
+ *
2951
+ * @param p - Command parameters including cmd, cwd, and optional timeout
2952
+ * @param ctx - Execution context with optional abort signal
2953
+ * @returns Structured result with command output and execution metadata
2954
+ *
2955
+ * @example
2956
+ * ```typescript
2957
+ * const result = await bashTool.execute({ cmd: 'ls -la' });
2958
+ * if (result.status === 'success' && result.type === 'text') {
2959
+ * console.log(result.result); // command output
2960
+ * console.log(result.metadata?.code); // exit code
2961
+ * console.log(result.metadata?.cwd); // working directory
2962
+ * }
2963
+ * ```
2964
+ */
2503
2965
  async execute(p, ctx) {
2504
2966
  try {
2505
2967
  return await this.execOnce(p, ctx?.signal);
@@ -2618,7 +3080,7 @@ ${output}` : "";
2618
3080
  }
2619
3081
  return err(output, metadata);
2620
3082
  }
2621
- return ok(output, { code, signal: exitSignal, cwd });
3083
+ return okText(output, { code, signal: exitSignal, cwd, stripped: stripAnsi });
2622
3084
  } catch (e) {
2623
3085
  if (timer) clearTimeout(timer);
2624
3086
  const message = e instanceof Error ? e.message : String(e);
@@ -2701,7 +3163,7 @@ var DirLsTool = class {
2701
3163
  name: this.name,
2702
3164
  description: [
2703
3165
  "List files and directories in a specified directory.",
2704
- "Returns entries sorted alphabetically by name in ls -la format.",
3166
+ "Returns entries sorted alphabetically by name as structured JSON.",
2705
3167
  "",
2706
3168
  "Typical uses:",
2707
3169
  "1) List all files in current directory",
@@ -2733,6 +3195,25 @@ var DirLsTool = class {
2733
3195
  return 0;
2734
3196
  }
2735
3197
  }
3198
+ /**
3199
+ * List directory contents as structured JSON
3200
+ *
3201
+ * @param params - Directory path and optional limit
3202
+ * @param context - Execution context with optional workspace directory
3203
+ * @returns Structured JSON with directory entries including name, type, size, and metadata
3204
+ *
3205
+ * @example
3206
+ * ```typescript
3207
+ * const result = await dirLsTool.execute({ path: 'src/tools', limit: 50 });
3208
+ * if (result.status === 'success' && result.type === 'json') {
3209
+ * console.log(result.result.path); // 'src/tools'
3210
+ * console.log(result.result.total); // number of entries
3211
+ * result.result.entries.forEach(entry => {
3212
+ * console.log(`${entry.name} (${entry.type}): ${entry.size} bytes`);
3213
+ * });
3214
+ * }
3215
+ * ```
3216
+ */
2736
3217
  async execute(params, context) {
2737
3218
  try {
2738
3219
  const targetPath = params.path ?? ".";
@@ -2741,10 +3222,10 @@ var DirLsTool = class {
2741
3222
  const abs = this.resolveSafePath(targetPath, context);
2742
3223
  const st = await fs7.stat(abs).catch(() => null);
2743
3224
  if (!st) {
2744
- return err(`Directory not found: ${targetPath}`);
3225
+ return err(`Directory not found: ${targetPath}`, void 0, "not_found" /* NotFound */);
2745
3226
  }
2746
3227
  if (!st.isDirectory()) {
2747
- return err(`Path is not a directory: ${targetPath}`);
3228
+ return err(`Path is not a directory: ${targetPath}`, void 0, "invalid_input" /* InvalidInput */);
2748
3229
  }
2749
3230
  const entries = await fs7.readdir(abs);
2750
3231
  const results = [];
@@ -2781,40 +3262,23 @@ var DirLsTool = class {
2781
3262
  results.push(dirEntry);
2782
3263
  }
2783
3264
  results.sort((a, b) => a.name.localeCompare(b.name));
2784
- const lines = results.map((entry) => this.formatAsLsLine(entry));
2785
- const output = lines.join("\n");
2786
- return ok(output);
3265
+ const result = okJson(
3266
+ {
3267
+ path: targetPath,
3268
+ entries: results,
3269
+ truncated: entries.length > limit,
3270
+ total: results.length
3271
+ },
3272
+ {
3273
+ limit,
3274
+ includeHidden
3275
+ }
3276
+ );
3277
+ return result;
2787
3278
  } catch (e) {
2788
3279
  const message = e instanceof Error ? e.message : String(e);
2789
- return err(message);
2790
- }
2791
- }
2792
- formatAsLsLine(entry) {
2793
- const mode = entry.mode ? this.formatMode(entry.mode) : "----------";
2794
- const size = entry.size !== void 0 ? entry.size.toString().padStart(10) : " 0";
2795
- const date = entry.mtime ? new Date(entry.mtime) : /* @__PURE__ */ new Date();
2796
- const month = date.toLocaleString("en-US", { month: "short" });
2797
- const day = date.getDate().toString().padStart(2);
2798
- const time = `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
2799
- const dateStr = `${month} ${day} ${time}`;
2800
- const typeIndicator = entry.type === "directory" ? "/" : entry.type === "symlink" ? "@" : "";
2801
- const name = `${entry.name}${typeIndicator}`;
2802
- return `${mode} ${size} ${dateStr} ${name}`;
2803
- }
2804
- formatMode(mode) {
2805
- const fileType = (mode & 61440) === 16384 ? "d" : (mode & 61440) === 40960 ? "l" : "-";
2806
- const perms = [
2807
- mode & 256 ? "r" : "-",
2808
- mode & 128 ? "w" : "-",
2809
- mode & 64 ? "x" : "-",
2810
- mode & 32 ? "r" : "-",
2811
- mode & 16 ? "w" : "-",
2812
- mode & 8 ? "x" : "-",
2813
- mode & 4 ? "r" : "-",
2814
- mode & 2 ? "w" : "-",
2815
- mode & 1 ? "x" : "-"
2816
- ];
2817
- return fileType + perms.join("");
3280
+ return err(message, void 0, "unknown" /* Unknown */);
3281
+ }
2818
3282
  }
2819
3283
  resolveSafePath(target, context) {
2820
3284
  const baseFromCtx = context?.workspaceRoot || context?.cwd || this.rootDir;
@@ -2892,7 +3356,7 @@ var AgentRegistry = class {
2892
3356
  systemPrompt: partial.systemPrompt,
2893
3357
  tools,
2894
3358
  temperature: partial.temperature ?? 0.7,
2895
- maxTokens: partial.maxTokens ?? 4e3,
3359
+ maxTokens: partial.maxTokens ?? 64e3,
2896
3360
  provider: partial.provider,
2897
3361
  model: partial.model,
2898
3362
  topP: partial.topP,
@@ -3084,20 +3548,55 @@ ${agentList}`,
3084
3548
  };
3085
3549
  }
3086
3550
  /**
3087
- * Execute task delegation
3551
+ * Delegate a task to a specialist agent for focused execution
3552
+ *
3553
+ * @param params - Agent ID and task description to delegate
3554
+ * @param context - Execution context including delegation depth tracking
3555
+ * @returns Delegation result with comprehensive metrics including cost breakdown
3556
+ *
3557
+ * @example
3558
+ * ```typescript
3559
+ * const result = await assignTool.execute({
3560
+ * agent: 'code-reviewer',
3561
+ * task: 'Review the changes in src/tools/*.ts',
3562
+ * description: 'Code review of tool implementations'
3563
+ * });
3564
+ * if (result.status === 'success' && result.type === 'text') {
3565
+ * console.log(result.result); // Agent's response
3566
+ * console.log(result.metadata.metrics?.totalCost); // Cost in USD
3567
+ * console.log(result.metadata.executionTimeMs); // Duration
3568
+ * console.log(result.metadata.toolCallsExecuted); // Number of tool calls
3569
+ * }
3570
+ * ```
3088
3571
  */
3089
3572
  async execute(params, context) {
3090
3573
  if (!params.agent || typeof params.agent !== "string") {
3091
- return err('Parameter "agent" is required and must be a string');
3574
+ return err('Parameter "agent" is required and must be a string', void 0, "invalid_input" /* InvalidInput */);
3092
3575
  }
3093
3576
  if (!params.task || typeof params.task !== "string") {
3094
- return err('Parameter "task" is required and must be a string');
3577
+ return err('Parameter "task" is required and must be a string', void 0, "invalid_input" /* InvalidInput */);
3095
3578
  }
3096
3579
  const outcome = await this.delegationService.delegate(params, context);
3097
3580
  if (!outcome.success || !outcome.summary) {
3098
- return err(outcome.error ?? "Failed to delegate task.");
3581
+ const isNotFound = outcome.error?.includes("not found");
3582
+ const isPolicyDenied = outcome.error?.includes("policy") || outcome.error?.includes("denied");
3583
+ return err(
3584
+ outcome.error ?? "Failed to delegate task.",
3585
+ {
3586
+ agentId: params.agent,
3587
+ agentNotFound: isNotFound,
3588
+ policyDenied: isPolicyDenied,
3589
+ delegationDepth: context?.delegationDepth
3590
+ },
3591
+ "unknown" /* Unknown */
3592
+ );
3099
3593
  }
3100
- return ok(outcome.summary, outcome.metadata);
3594
+ const metadata = outcome.metadata;
3595
+ return okText(outcome.summary, {
3596
+ ...metadata,
3597
+ taskDescription: params.task,
3598
+ delegationDepth: (context?.delegationDepth ?? 0) + 1
3599
+ });
3101
3600
  }
3102
3601
  };
3103
3602
 
@@ -3224,6 +3723,7 @@ var AgentManager = class {
3224
3723
  result: "Sub-agent execution aborted by user",
3225
3724
  metadata: {
3226
3725
  agentId,
3726
+ agentName: config.agentName,
3227
3727
  toolCallsExecuted: 0,
3228
3728
  executionTimeMs: 0,
3229
3729
  errorMessage: "Aborted before execution"
@@ -3237,13 +3737,14 @@ var AgentManager = class {
3237
3737
  result: `Maximum delegation depth (${MAX_DELEGATION_DEPTH}) exceeded`,
3238
3738
  metadata: {
3239
3739
  agentId,
3740
+ agentName: config.agentName,
3240
3741
  toolCallsExecuted: 0,
3241
3742
  executionTimeMs: Date.now() - startTime,
3242
3743
  errorMessage: "Max delegation depth exceeded"
3243
3744
  }
3244
3745
  };
3245
3746
  }
3246
- this.eventCallback?.({
3747
+ await this.eventCallback?.({
3247
3748
  type: AgentEventTypes.SubAgentStarted,
3248
3749
  conversationId: config.conversationId ?? "default",
3249
3750
  messageId: config.messageId ?? "",
@@ -3262,7 +3763,7 @@ var AgentManager = class {
3262
3763
  events.push(event);
3263
3764
  if (event.type === AgentEventTypes.ToolCalls) {
3264
3765
  for (const toolCall of event.toolCalls) {
3265
- this.eventCallback?.({
3766
+ await this.eventCallback?.({
3266
3767
  type: AgentEventTypes.SubAgentToolCall,
3267
3768
  conversationId: config.conversationId ?? "default",
3268
3769
  messageId: config.messageId ?? "",
@@ -3273,7 +3774,7 @@ var AgentManager = class {
3273
3774
  });
3274
3775
  }
3275
3776
  } else if (event.type === AgentEventTypes.ToolResult) {
3276
- this.eventCallback?.({
3777
+ await this.eventCallback?.({
3277
3778
  type: AgentEventTypes.SubAgentToolResult,
3278
3779
  conversationId: config.conversationId ?? "default",
3279
3780
  messageId: config.messageId ?? "",
@@ -3300,6 +3801,16 @@ var AgentManager = class {
3300
3801
  // Specialists run autonomously
3301
3802
  reasoningEffort: freshConfig.reasoningEffort ?? this.delegatingConfig.reasoningEffort
3302
3803
  };
3804
+ const metricsPort = new InMemoryMetricsPort((snapshot) => {
3805
+ this.eventCallback?.({
3806
+ type: AgentEventTypes.SubAgentMetrics,
3807
+ conversationId: config.conversationId ?? "default",
3808
+ messageId: config.messageId ?? "",
3809
+ agentId: config.agentId,
3810
+ toolCallId: config.toolCallId ?? "",
3811
+ metrics: snapshot
3812
+ });
3813
+ });
3303
3814
  const llm = this.resolveLLM(config);
3304
3815
  const specialistOrchestrator = new AgentOrchestrator(specialistConfig, {
3305
3816
  memory,
@@ -3310,7 +3821,8 @@ var AgentManager = class {
3310
3821
  clock: new SystemClock(),
3311
3822
  cost: new SimpleCost(),
3312
3823
  reminders: new NoopReminders(),
3313
- events: eventPort
3824
+ events: eventPort,
3825
+ metrics: metricsPort
3314
3826
  });
3315
3827
  this.activeAgents.set(agentId, specialistOrchestrator);
3316
3828
  try {
@@ -3325,7 +3837,8 @@ var AgentManager = class {
3325
3837
  totalTokens += event.usage.total_tokens;
3326
3838
  }
3327
3839
  }
3328
- this.eventCallback?.({
3840
+ const snapshot = metricsPort.getSnapshot();
3841
+ await this.eventCallback?.({
3329
3842
  type: AgentEventTypes.SubAgentCompleted,
3330
3843
  conversationId: config.conversationId ?? "default",
3331
3844
  messageId: config.messageId ?? "",
@@ -3340,11 +3853,13 @@ var AgentManager = class {
3340
3853
  result: response.content || "",
3341
3854
  metadata: {
3342
3855
  agentId,
3856
+ agentName: config.agentName,
3343
3857
  tokensUsed: totalTokens || response.metadata?.totalTokens,
3344
3858
  toolCallsExecuted,
3345
3859
  executionTimeMs,
3346
3860
  conversationHistory,
3347
- events
3861
+ events,
3862
+ metrics: snapshot
3348
3863
  }
3349
3864
  };
3350
3865
  } catch (error) {
@@ -3353,7 +3868,7 @@ var AgentManager = class {
3353
3868
  const isTimeout = error instanceof Error && error.message.includes("timeout");
3354
3869
  const isAborted = error instanceof Error && (error.message.includes("aborted") || error.name === "AbortError");
3355
3870
  const status = isTimeout ? "timeout" : isAborted ? "error" : "error";
3356
- this.eventCallback?.({
3871
+ await this.eventCallback?.({
3357
3872
  type: AgentEventTypes.SubAgentCompleted,
3358
3873
  conversationId: config.conversationId ?? "default",
3359
3874
  messageId: config.messageId ?? "",
@@ -3368,6 +3883,7 @@ var AgentManager = class {
3368
3883
  result: errorMessage,
3369
3884
  metadata: {
3370
3885
  agentId,
3886
+ agentName: config.agentName,
3371
3887
  toolCallsExecuted: events.filter((e) => e.type === "tool_calls").length,
3372
3888
  executionTimeMs,
3373
3889
  errorMessage,
@@ -3541,10 +4057,12 @@ var DefaultDelegationResultFormatter = class {
3541
4057
  summary: result.result,
3542
4058
  metadata: {
3543
4059
  agentId,
4060
+ agentName: result.metadata.agentName,
3544
4061
  status: result.status,
3545
4062
  executionTimeMs: result.metadata.executionTimeMs,
3546
4063
  toolCallsExecuted: result.metadata.toolCallsExecuted,
3547
- tokensUsed: result.metadata.tokensUsed
4064
+ tokensUsed: result.metadata.tokensUsed,
4065
+ metrics: result.metadata.metrics
3548
4066
  }
3549
4067
  };
3550
4068
  }
@@ -3803,6 +4321,121 @@ var CompositeToolPort = class {
3803
4321
  }
3804
4322
  };
3805
4323
 
4324
+ // src/tools/tool-params.ts
4325
+ function parseToolArguments(args) {
4326
+ if (typeof args === "string") {
4327
+ try {
4328
+ return JSON.parse(args);
4329
+ } catch {
4330
+ return {};
4331
+ }
4332
+ }
4333
+ return args;
4334
+ }
4335
+ function isBashToolArgs(args) {
4336
+ return "cmd" in args && typeof args.cmd === "string";
4337
+ }
4338
+ function isFileReadArgs(args) {
4339
+ return "path" in args && typeof args.path === "string";
4340
+ }
4341
+ function isFileEditArgs(args) {
4342
+ return "file_path" in args && "old_text" in args && "new_text" in args;
4343
+ }
4344
+ function isFileNewArgs(args) {
4345
+ return "file_path" in args && "content" in args;
4346
+ }
4347
+ function isTodoWriteArgs(args) {
4348
+ return "todos" in args && Array.isArray(args.todos);
4349
+ }
4350
+ function isAssignTaskArgs(args) {
4351
+ return "agent" in args && "task" in args && "description" in args;
4352
+ }
4353
+ function isWebSearchArgs(args) {
4354
+ return "query" in args && typeof args.query === "string";
4355
+ }
4356
+ function isWebFetchArgs(args) {
4357
+ return "url" in args && typeof args.url === "string";
4358
+ }
4359
+ function isDirLsArgs(args) {
4360
+ return !("cmd" in args) && !("url" in args) && !("query" in args) && !("todos" in args) && !("agent" in args) && !("file_path" in args) && !("content" in args);
4361
+ }
4362
+
4363
+ // src/tools/type-guards.ts
4364
+ function isSuccess(result) {
4365
+ return result.status === "success";
4366
+ }
4367
+ function isError(result) {
4368
+ return result.status === "error";
4369
+ }
4370
+ function isTextResult(result) {
4371
+ return result.type === "text";
4372
+ }
4373
+ function isJsonResult(result) {
4374
+ return result.type === "json";
4375
+ }
4376
+ function isSuccessText(result) {
4377
+ return result.status === "success" && result.type === "text";
4378
+ }
4379
+ function isSuccessJson(result) {
4380
+ return result.status === "success" && result.type === "json";
4381
+ }
4382
+
4383
+ // src/tools/tool-type-guards.ts
4384
+ function isBashResult(result) {
4385
+ return result.name === "bash_tool";
4386
+ }
4387
+ function isBashSuccess(result) {
4388
+ return result.name === "bash_tool" && result.status === "success" && result.type === "text";
4389
+ }
4390
+ function isFileReadResult(result) {
4391
+ return result.name === "file_read";
4392
+ }
4393
+ function isFileReadSuccess(result) {
4394
+ return result.name === "file_read" && result.status === "success" && result.type === "text";
4395
+ }
4396
+ function isFileEditResult(result) {
4397
+ return result.name === "file_edit";
4398
+ }
4399
+ function isFileEditSuccess(result) {
4400
+ return result.name === "file_edit" && result.status === "success" && result.type === "text";
4401
+ }
4402
+ function isFileNewResult(result) {
4403
+ return result.name === "file_new";
4404
+ }
4405
+ function isFileNewSuccess(result) {
4406
+ return result.name === "file_new" && result.status === "success" && result.type === "text";
4407
+ }
4408
+ function isDirLsResult(result) {
4409
+ return result.name === "dir_ls";
4410
+ }
4411
+ function isDirLsSuccess(result) {
4412
+ return result.name === "dir_ls" && result.status === "success" && result.type === "json";
4413
+ }
4414
+ function isWebSearchResult(result) {
4415
+ return result.name === "web_search";
4416
+ }
4417
+ function isWebSearchSuccess(result) {
4418
+ return result.name === "web_search" && result.status === "success" && result.type === "json";
4419
+ }
4420
+ function isWebFetchResult(result) {
4421
+ return result.name === "web_fetch";
4422
+ }
4423
+ function isWebFetchSuccess(result) {
4424
+ return result.name === "web_fetch" && result.status === "success" && result.type === "text";
4425
+ }
4426
+ function isTodoWriteResult(result) {
4427
+ return result.name === "todo_write";
4428
+ }
4429
+ function isTodoWriteSuccess(result) {
4430
+ return result.name === "todo_write" && result.status === "success" && result.type === "text";
4431
+ }
4432
+ function isAssignResult(result) {
4433
+ return result.name === "assign_task";
4434
+ }
4435
+ function isAssignSuccess(result) {
4436
+ return result.name === "assign_task" && result.status === "success" && result.type === "text";
4437
+ }
4438
+
3806
4439
  // src/agent-file-persistence.ts
3807
4440
  import * as fs8 from "fs";
3808
4441
  import * as path7 from "path";
@@ -3927,6 +4560,16 @@ var AgentFilePersistence = class {
3927
4560
  }
3928
4561
  };
3929
4562
 
4563
+ // src/sub-agent-types.ts
4564
+ function parseSubAgentToolCallArguments(argsString) {
4565
+ if (!argsString) return {};
4566
+ try {
4567
+ return JSON.parse(argsString);
4568
+ } catch {
4569
+ return {};
4570
+ }
4571
+ }
4572
+
3930
4573
  // src/llm-providers/llm-utils.ts
3931
4574
  function mergeChoices(choices) {
3932
4575
  const contentParts = [];
@@ -3984,9 +4627,11 @@ var BaseLLM = class {
3984
4627
  transport = null;
3985
4628
  apiUrl;
3986
4629
  enablePromptCaching = false;
4630
+ retryConfig;
3987
4631
  constructor(apiUrl, options) {
3988
4632
  this.apiUrl = apiUrl;
3989
4633
  this.enablePromptCaching = options?.enablePromptCaching ?? false;
4634
+ this.retryConfig = options?.retry;
3990
4635
  }
3991
4636
  transformUsage(rawUsage) {
3992
4637
  if (!rawUsage) return void 0;
@@ -4649,6 +5294,206 @@ function createTransport(inner, defaultBaseUrl, apiKey, baseUrl, version, custom
4649
5294
  return new SimpleBearerAuthTransport(inner, defaultBaseUrl, apiKey, baseUrl, version, customHeaders);
4650
5295
  }
4651
5296
 
5297
+ // src/transports/backoff.ts
5298
+ function calculateBackoff(attempt, baseDelayMs, maxDelayMs, multiplier, jitterFactor) {
5299
+ const exponentialDelay = baseDelayMs * multiplier ** attempt;
5300
+ const cappedDelay = Math.min(exponentialDelay, maxDelayMs);
5301
+ const jitterRange = cappedDelay * jitterFactor;
5302
+ const jitter = jitterRange * (Math.random() * 2 - 1);
5303
+ return Math.max(0, Math.round(cappedDelay + jitter));
5304
+ }
5305
+ function parseRetryAfterHeader(header) {
5306
+ if (!header) return null;
5307
+ const seconds = parseInt(header, 10);
5308
+ if (!Number.isNaN(seconds) && seconds >= 0) {
5309
+ return seconds * 1e3;
5310
+ }
5311
+ const date = Date.parse(header);
5312
+ if (!Number.isNaN(date)) {
5313
+ const delayMs = date - Date.now();
5314
+ return delayMs > 0 ? delayMs : null;
5315
+ }
5316
+ return null;
5317
+ }
5318
+
5319
+ // src/transports/error-classification.ts
5320
+ var DEFAULT_RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504];
5321
+ var RETRYABLE_NETWORK_ERRORS = [
5322
+ "econnrefused",
5323
+ "enotfound",
5324
+ "etimedout",
5325
+ "econnreset",
5326
+ "epipe",
5327
+ "network",
5328
+ "fetch failed",
5329
+ "socket hang up"
5330
+ ];
5331
+ var NON_RETRYABLE_PATTERNS = [
5332
+ "invalid",
5333
+ "unauthorized",
5334
+ "forbidden",
5335
+ "not found",
5336
+ "bad request",
5337
+ "api key",
5338
+ "authentication",
5339
+ "permission",
5340
+ "invalid_api_key",
5341
+ "insufficient_quota"
5342
+ ];
5343
+ function isRetryableStatusCode(status, retryableStatusCodes = DEFAULT_RETRYABLE_STATUS_CODES) {
5344
+ return retryableStatusCodes.includes(status);
5345
+ }
5346
+ function isRetryableError(error, retryableStatusCodes = DEFAULT_RETRYABLE_STATUS_CODES) {
5347
+ if (!error) return false;
5348
+ if (error instanceof Error && error.name === "AbortError") {
5349
+ return false;
5350
+ }
5351
+ if (error instanceof LLMError) {
5352
+ if (error.statusCode) {
5353
+ return isRetryableStatusCode(error.statusCode, retryableStatusCodes);
5354
+ }
5355
+ return error.isRetryable;
5356
+ }
5357
+ const err2 = error;
5358
+ const statusCode = err2.statusCode ?? err2.status;
5359
+ if (statusCode) {
5360
+ return isRetryableStatusCode(statusCode, retryableStatusCodes);
5361
+ }
5362
+ const message = err2.message?.toLowerCase() || "";
5363
+ const code = err2.code?.toLowerCase() || "";
5364
+ for (const pattern of NON_RETRYABLE_PATTERNS) {
5365
+ if (message.includes(pattern) || code.includes(pattern)) {
5366
+ return false;
5367
+ }
5368
+ }
5369
+ for (const networkError of RETRYABLE_NETWORK_ERRORS) {
5370
+ if (message.includes(networkError) || code.includes(networkError)) {
5371
+ return true;
5372
+ }
5373
+ }
5374
+ return false;
5375
+ }
5376
+
5377
+ // src/transports/retry-transport.ts
5378
+ var DEFAULT_RETRY_CONFIG = {
5379
+ maxRetries: 3,
5380
+ baseDelayMs: 1e3,
5381
+ maxDelayMs: 6e4,
5382
+ backoffMultiplier: 2,
5383
+ jitterFactor: 0.2,
5384
+ retryableStatusCodes: [429, 500, 502, 503, 504]
5385
+ };
5386
+ var AbortError = class _AbortError extends Error {
5387
+ constructor(message = "Operation aborted") {
5388
+ super(message);
5389
+ this.name = "AbortError";
5390
+ if (Error.captureStackTrace) {
5391
+ Error.captureStackTrace(this, _AbortError);
5392
+ }
5393
+ }
5394
+ };
5395
+ var sleep = (ms, signal) => {
5396
+ return new Promise((resolve6, reject) => {
5397
+ if (signal?.aborted) {
5398
+ reject(new AbortError("Operation aborted"));
5399
+ return;
5400
+ }
5401
+ const timeout = setTimeout(resolve6, ms);
5402
+ signal?.addEventListener(
5403
+ "abort",
5404
+ () => {
5405
+ clearTimeout(timeout);
5406
+ reject(new AbortError("Operation aborted during retry delay"));
5407
+ },
5408
+ { once: true }
5409
+ );
5410
+ });
5411
+ };
5412
+ var RetryTransport = class {
5413
+ constructor(inner, config = {}) {
5414
+ this.inner = inner;
5415
+ this.config = { ...DEFAULT_RETRY_CONFIG, ...config };
5416
+ }
5417
+ config;
5418
+ async get(url, headers, signal) {
5419
+ return this.executeWithRetry(() => this.inner.get(url, headers, signal), signal);
5420
+ }
5421
+ async post(url, body, headers, signal) {
5422
+ return this.executeWithRetry(() => this.inner.post(url, body, headers, signal), signal);
5423
+ }
5424
+ async executeWithRetry(fn, signal) {
5425
+ let lastError;
5426
+ let attempt = 0;
5427
+ while (attempt <= this.config.maxRetries) {
5428
+ if (signal?.aborted) {
5429
+ throw new AbortError("Operation aborted before execution");
5430
+ }
5431
+ try {
5432
+ const response = await fn();
5433
+ if (this.isRetryableResponse(response)) {
5434
+ const error = new Error(`HTTP ${response.status}`);
5435
+ error.status = response.status;
5436
+ if (attempt < this.config.maxRetries) {
5437
+ const delayMs = this.getDelayForResponse(response, attempt);
5438
+ this.config.onRetry?.(attempt + 1, error, delayMs);
5439
+ await sleep(delayMs, signal);
5440
+ attempt++;
5441
+ continue;
5442
+ }
5443
+ lastError = error;
5444
+ break;
5445
+ }
5446
+ return response;
5447
+ } catch (error) {
5448
+ if (error instanceof AbortError || error instanceof Error && error.name === "AbortError") {
5449
+ throw error;
5450
+ }
5451
+ lastError = error instanceof Error ? error : new Error(String(error));
5452
+ if (!isRetryableError(lastError, this.config.retryableStatusCodes)) {
5453
+ throw lastError;
5454
+ }
5455
+ if (attempt < this.config.maxRetries) {
5456
+ const delayMs = calculateBackoff(
5457
+ attempt,
5458
+ this.config.baseDelayMs,
5459
+ this.config.maxDelayMs,
5460
+ this.config.backoffMultiplier,
5461
+ this.config.jitterFactor
5462
+ );
5463
+ this.config.onRetry?.(attempt + 1, lastError, delayMs);
5464
+ await sleep(delayMs, signal);
5465
+ attempt++;
5466
+ } else {
5467
+ break;
5468
+ }
5469
+ }
5470
+ }
5471
+ if (lastError) {
5472
+ this.config.onExhausted?.(lastError, attempt + 1);
5473
+ throw lastError;
5474
+ }
5475
+ throw new Error("Retry exhausted without error");
5476
+ }
5477
+ isRetryableResponse(response) {
5478
+ return isRetryableStatusCode(response.status, this.config.retryableStatusCodes);
5479
+ }
5480
+ getDelayForResponse(response, attempt) {
5481
+ const headers = "headers" in response && response.headers instanceof Headers ? response.headers : null;
5482
+ const retryAfter = headers?.get("retry-after") ?? null;
5483
+ const retryAfterMs = parseRetryAfterHeader(retryAfter);
5484
+ if (retryAfterMs !== null) {
5485
+ return Math.min(retryAfterMs, this.config.maxDelayMs);
5486
+ }
5487
+ return calculateBackoff(
5488
+ attempt,
5489
+ this.config.baseDelayMs,
5490
+ this.config.maxDelayMs,
5491
+ this.config.backoffMultiplier,
5492
+ this.config.jitterFactor
5493
+ );
5494
+ }
5495
+ };
5496
+
4652
5497
  // src/llm-providers/model-limits.ts
4653
5498
  function normalizeModelLimits(provider, model) {
4654
5499
  switch (provider.toLowerCase()) {
@@ -4766,7 +5611,7 @@ function normalizeModelInfo(provider, model) {
4766
5611
  var GithubLLM = class extends BaseLLM {
4767
5612
  opts;
4768
5613
  constructor(opts = {}) {
4769
- super(opts.apiUrl ?? "https://api.individual.githubcopilot.com");
5614
+ super(opts.apiUrl ?? "https://api.individual.githubcopilot.com", { retry: opts.retry });
4770
5615
  this.opts = opts;
4771
5616
  }
4772
5617
  createTransport() {
@@ -4777,11 +5622,15 @@ var GithubLLM = class extends BaseLLM {
4777
5622
  maxFileSize: 5 * 1024 * 1024,
4778
5623
  captureResponseBody: true
4779
5624
  });
4780
- return new GithubAuthTransport(base, {
5625
+ const authTransport = new GithubAuthTransport(base, {
4781
5626
  baseUrl: this.opts.apiUrl,
4782
5627
  apiKey: this.opts.apiKey,
4783
5628
  accessToken: this.opts.accessToken
4784
5629
  });
5630
+ if (this.retryConfig) {
5631
+ return new RetryTransport(authTransport, this.retryConfig);
5632
+ }
5633
+ return authTransport;
4785
5634
  }
4786
5635
  transformUsage(rawUsage) {
4787
5636
  if (!rawUsage) return void 0;
@@ -5400,8 +6249,8 @@ var GenericLLM = class extends BaseLLM {
5400
6249
  providerName;
5401
6250
  customHeaders;
5402
6251
  constructor(baseUrl, modelConfig, opts = {}, customHeaders) {
5403
- const { enablePromptCaching = false, includeUsage = false, providerName = "unknown", ...restOpts } = opts;
5404
- super(opts.apiUrl || baseUrl, { enablePromptCaching });
6252
+ const { enablePromptCaching = false, includeUsage = false, providerName = "unknown", retry, ...restOpts } = opts;
6253
+ super(opts.apiUrl || baseUrl, { enablePromptCaching, retry });
5405
6254
  this.includeUsage = includeUsage;
5406
6255
  this.modelConfig = modelConfig;
5407
6256
  this.providerName = providerName;
@@ -5416,7 +6265,7 @@ var GenericLLM = class extends BaseLLM {
5416
6265
  maxFileSize: 5 * 1024 * 1024,
5417
6266
  captureResponseBody: true
5418
6267
  });
5419
- return createTransport(
6268
+ const authTransport = createTransport(
5420
6269
  base,
5421
6270
  this.apiUrl,
5422
6271
  this.opts.apiKey,
@@ -5424,6 +6273,10 @@ var GenericLLM = class extends BaseLLM {
5424
6273
  this.opts.version,
5425
6274
  this.customHeaders
5426
6275
  );
6276
+ if (this.retryConfig) {
6277
+ return new RetryTransport(authTransport, this.retryConfig);
6278
+ }
6279
+ return authTransport;
5427
6280
  }
5428
6281
  async getModels(signal) {
5429
6282
  if (this.modelConfig === false) {
@@ -5731,7 +6584,11 @@ var MCPToolPort = class {
5731
6584
  try {
5732
6585
  const res = await this.client.callTool({ name: original, arguments: c.parameters || {} });
5733
6586
  const flat = flattenMcpContent(res.content);
5734
- return { id: c.id, name: c.name, status: "success", type: flat.type, result: flat.value };
6587
+ if (flat.type === "text") {
6588
+ return { id: c.id, name: c.name, status: "success", type: "text", result: flat.value };
6589
+ } else {
6590
+ return { id: c.id, name: c.name, status: "success", type: "json", result: flat.value };
6591
+ }
5735
6592
  } catch (err2) {
5736
6593
  const message = err2 instanceof Error ? err2.message : String(err2);
5737
6594
  return { id: c.id, name: c.name, status: "error", type: "text", result: message };
@@ -5810,6 +6667,7 @@ function isValidConfig(value) {
5810
6667
  }
5811
6668
  export {
5812
6669
  AGENT_CREATOR_SYSTEM_PROMPT,
6670
+ AbortError,
5813
6671
  AgentEventTypes,
5814
6672
  AgentFilePersistence,
5815
6673
  AgentManager,
@@ -5822,6 +6680,7 @@ export {
5822
6680
  ConversationContext,
5823
6681
  ConversationStore,
5824
6682
  CoreMCPClient,
6683
+ DEFAULT_RETRY_CONFIG,
5825
6684
  DefaultDelegationPolicy,
5826
6685
  DefaultDelegationResultFormatter,
5827
6686
  DefaultDelegationService,
@@ -5841,6 +6700,7 @@ export {
5841
6700
  NoopReminders,
5842
6701
  PersistedMemory,
5843
6702
  PersistingConsoleEventPort,
6703
+ RetryTransport,
5844
6704
  RuntimeEnv,
5845
6705
  SimpleContextBuilder,
5846
6706
  SimpleCost,
@@ -5850,19 +6710,63 @@ export {
5850
6710
  buildAgentCreationPrompt,
5851
6711
  buildInjectedSystem,
5852
6712
  canonicalizeTerminalPaste,
6713
+ convertToolCall,
6714
+ convertToolCalls,
5853
6715
  createEmptySnapshot,
5854
6716
  createLLM,
6717
+ err,
5855
6718
  generateFolderTree,
5856
6719
  getAvailableProviders,
5857
6720
  getFallbackLimits,
5858
6721
  getProviderLabel,
6722
+ isAssignResult,
6723
+ isAssignSuccess,
6724
+ isAssignTaskArgs,
6725
+ isBashResult,
6726
+ isBashSuccess,
6727
+ isBashToolArgs,
6728
+ isDirLsArgs,
6729
+ isDirLsResult,
6730
+ isDirLsSuccess,
6731
+ isError,
6732
+ isFileEditArgs,
6733
+ isFileEditResult,
6734
+ isFileEditSuccess,
6735
+ isFileNewArgs,
6736
+ isFileNewResult,
6737
+ isFileNewSuccess,
6738
+ isFileReadArgs,
6739
+ isFileReadResult,
6740
+ isFileReadSuccess,
6741
+ isJsonResult,
6742
+ isRetryableError,
6743
+ isRetryableStatusCode,
6744
+ isSuccess,
6745
+ isSuccessJson,
6746
+ isSuccessText,
6747
+ isTextResult,
6748
+ isTodoWriteArgs,
6749
+ isTodoWriteResult,
6750
+ isTodoWriteSuccess,
6751
+ isWebFetchArgs,
6752
+ isWebFetchResult,
6753
+ isWebFetchSuccess,
6754
+ isWebSearchArgs,
6755
+ isWebSearchResult,
6756
+ isWebSearchSuccess,
5859
6757
  loadMCPConfig,
5860
6758
  normalizeModelInfo,
5861
6759
  normalizeModelLimits,
5862
6760
  normalizeNewlines,
6761
+ okJson,
6762
+ okText,
6763
+ parseJSON,
6764
+ parseSubAgentToolCallArguments,
6765
+ parseToolArguments,
5863
6766
  renderTemplate,
5864
6767
  resolveBackspaces,
5865
6768
  resolveCarriageReturns,
5866
6769
  stripAnsiAndControls,
5867
- supportsGetModels
6770
+ supportsGetModels,
6771
+ toolValidators
5868
6772
  };