@distri/core 0.2.4 → 0.2.7

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
@@ -26,22 +26,25 @@ __export(index_exports, {
26
26
  Agent: () => Agent,
27
27
  ApiError: () => ApiError,
28
28
  ConnectionError: () => ConnectionError,
29
+ DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
29
30
  DistriClient: () => DistriClient,
30
31
  DistriError: () => DistriError,
31
- convertA2AArtifactToDistri: () => convertA2AArtifactToDistri,
32
+ ExternalToolValidationError: () => ExternalToolValidationError,
32
33
  convertA2AMessageToDistri: () => convertA2AMessageToDistri,
33
34
  convertA2APartToDistri: () => convertA2APartToDistri,
34
35
  convertA2AStatusUpdateToDistri: () => convertA2AStatusUpdateToDistri,
35
36
  convertDistriMessageToA2A: () => convertDistriMessageToA2A,
36
37
  convertDistriPartToA2A: () => convertDistriPartToA2A,
38
+ createFailedToolResult: () => createFailedToolResult,
39
+ createSuccessfulToolResult: () => createSuccessfulToolResult,
37
40
  decodeA2AStreamEvent: () => decodeA2AStreamEvent,
38
41
  extractTextFromDistriMessage: () => extractTextFromDistriMessage,
39
42
  extractToolCallsFromDistriMessage: () => extractToolCallsFromDistriMessage,
43
+ extractToolResultData: () => extractToolResultData,
40
44
  extractToolResultsFromDistriMessage: () => extractToolResultsFromDistriMessage,
41
- isDistriArtifact: () => isDistriArtifact,
45
+ isArrayParts: () => isArrayParts,
42
46
  isDistriEvent: () => isDistriEvent,
43
47
  isDistriMessage: () => isDistriMessage,
44
- isDistriPlan: () => isDistriPlan,
45
48
  processA2AMessagesData: () => processA2AMessagesData,
46
49
  processA2AStreamData: () => processA2AStreamData,
47
50
  uuidv4: () => uuidv4
@@ -49,6 +52,90 @@ __export(index_exports, {
49
52
  module.exports = __toCommonJS(index_exports);
50
53
 
51
54
  // src/types.ts
55
+ function isArrayParts(result) {
56
+ return Array.isArray(result) && result[0].part_type;
57
+ }
58
+ function createSuccessfulToolResult(toolCallId, toolName, result) {
59
+ const parts = isArrayParts(result) ? result : [{
60
+ part_type: "data",
61
+ data: {
62
+ result,
63
+ success: true,
64
+ error: void 0
65
+ }
66
+ }];
67
+ return {
68
+ tool_call_id: toolCallId,
69
+ tool_name: toolName,
70
+ parts
71
+ };
72
+ }
73
+ function createFailedToolResult(toolCallId, toolName, error, result) {
74
+ return {
75
+ tool_call_id: toolCallId,
76
+ tool_name: toolName,
77
+ parts: [{
78
+ part_type: "data",
79
+ data: {
80
+ result: result ?? `Tool execution failed: ${error}`,
81
+ success: false,
82
+ error
83
+ }
84
+ }]
85
+ };
86
+ }
87
+ function isDataPart(part) {
88
+ return typeof part === "object" && part !== null && "part_type" in part && part.part_type === "data" && "data" in part;
89
+ }
90
+ function isToolResultData(data) {
91
+ return typeof data === "object" && data !== null && "success" in data && typeof data.success === "boolean";
92
+ }
93
+ function extractToolResultData(toolResult) {
94
+ if (!toolResult.parts || !Array.isArray(toolResult.parts) || toolResult.parts.length === 0) {
95
+ return null;
96
+ }
97
+ const firstPart = toolResult.parts[0];
98
+ if (isDataPart(firstPart)) {
99
+ const data = firstPart.data;
100
+ if (isToolResultData(data)) {
101
+ return {
102
+ result: data.result,
103
+ success: data.success,
104
+ error: data.error
105
+ };
106
+ }
107
+ if (typeof data === "string") {
108
+ try {
109
+ const parsed = JSON.parse(data);
110
+ if (isToolResultData(parsed)) {
111
+ return {
112
+ result: parsed.result,
113
+ success: parsed.success,
114
+ error: parsed.error
115
+ };
116
+ }
117
+ return {
118
+ result: parsed,
119
+ success: true,
120
+ error: void 0
121
+ };
122
+ } catch {
123
+ return {
124
+ result: data,
125
+ success: true,
126
+ error: void 0
127
+ };
128
+ }
129
+ }
130
+ return {
131
+ result: data,
132
+ success: true,
133
+ error: void 0
134
+ };
135
+ }
136
+ return null;
137
+ }
138
+ var DEFAULT_BASE_URL = "https://api.distri.dev";
52
139
  var DistriError = class extends Error {
53
140
  constructor(message, code, details) {
54
141
  super(message);
@@ -82,14 +169,8 @@ function isDistriMessage(event) {
82
169
  function isDistriEvent(event) {
83
170
  return "type" in event && "data" in event;
84
171
  }
85
- function isDistriPlan(event) {
86
- return "steps" in event && Array.isArray(event.steps) && "reasoning" in event;
87
- }
88
- function isDistriArtifact(event) {
89
- return "type" in event && "timestamp" in event && "id" in event;
90
- }
91
172
 
92
- // ../../node_modules/.pnpm/@a2a-js+sdk@https+++codeload.github.com+v3g42+a2a-js+tar.gz+51444c9/node_modules/@a2a-js/sdk/dist/chunk-CUGIRVQB.js
173
+ // ../../../node_modules/.pnpm/@a2a-js+sdk@https+++codeload.github.com+v3g42+a2a-js+tar.gz+51444c9/node_modules/@a2a-js/sdk/dist/chunk-CUGIRVQB.js
93
174
  var A2AClient = class {
94
175
  /**
95
176
  * Constructs an A2AClient instance.
@@ -475,32 +556,56 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
475
556
  }
476
557
  const metadata = statusUpdate.metadata;
477
558
  switch (metadata.type) {
478
- case "run_started":
479
- return {
559
+ case "run_started": {
560
+ const runStartedResult = {
480
561
  type: "run_started",
481
- data: {}
562
+ data: {
563
+ runId: statusUpdate.runId,
564
+ taskId: statusUpdate.taskId
565
+ }
482
566
  };
483
- case "run_finished":
484
- return {
567
+ return runStartedResult;
568
+ }
569
+ case "run_error": {
570
+ const runErrorResult = {
571
+ type: "run_error",
572
+ data: {
573
+ message: statusUpdate.error,
574
+ code: statusUpdate.code
575
+ }
576
+ };
577
+ return runErrorResult;
578
+ }
579
+ case "run_finished": {
580
+ const runFinishedResult = {
485
581
  type: "run_finished",
486
- data: {}
582
+ data: {
583
+ runId: statusUpdate.runId,
584
+ taskId: statusUpdate.taskId
585
+ }
487
586
  };
488
- case "plan_started":
489
- return {
587
+ return runFinishedResult;
588
+ }
589
+ case "plan_started": {
590
+ const planStartedResult = {
490
591
  type: "plan_started",
491
592
  data: {
492
593
  initial_plan: metadata.initial_plan
493
594
  }
494
595
  };
495
- case "plan_finished":
496
- return {
596
+ return planStartedResult;
597
+ }
598
+ case "plan_finished": {
599
+ const planFinishedResult = {
497
600
  type: "plan_finished",
498
601
  data: {
499
602
  total_steps: metadata.total_steps
500
603
  }
501
604
  };
502
- case "step_started":
503
- return {
605
+ return planFinishedResult;
606
+ }
607
+ case "step_started": {
608
+ const stepStartedResult = {
504
609
  type: "step_started",
505
610
  data: {
506
611
  step_id: metadata.step_id,
@@ -508,8 +613,10 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
508
613
  step_index: metadata.step_index || 0
509
614
  }
510
615
  };
511
- case "step_completed":
512
- return {
616
+ return stepStartedResult;
617
+ }
618
+ case "step_completed": {
619
+ const stepCompletedResult = {
513
620
  type: "step_completed",
514
621
  data: {
515
622
  step_id: metadata.step_id,
@@ -517,142 +624,132 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
517
624
  step_index: metadata.step_index || 0
518
625
  }
519
626
  };
520
- case "tool_execution_start":
521
- return {
522
- type: "tool_call_start",
627
+ return stepCompletedResult;
628
+ }
629
+ case "tool_execution_start": {
630
+ const toolStartResult = {
631
+ type: "tool_execution_start",
523
632
  data: {
524
633
  tool_call_id: metadata.tool_call_id,
525
634
  tool_call_name: metadata.tool_call_name || "Tool",
526
- parent_message_id: statusUpdate.taskId,
527
- is_external: true
635
+ parent_message_id: statusUpdate.taskId
528
636
  }
529
637
  };
530
- case "tool_execution_end":
531
- return {
532
- type: "tool_call_end",
638
+ return toolStartResult;
639
+ }
640
+ case "tool_execution_end": {
641
+ const toolEndResult = {
642
+ type: "tool_execution_end",
533
643
  data: {
534
644
  tool_call_id: metadata.tool_call_id
535
645
  }
536
646
  };
537
- case "text_message_start":
538
- return {
647
+ return toolEndResult;
648
+ }
649
+ case "text_message_start": {
650
+ const textStartResult = {
539
651
  type: "text_message_start",
540
652
  data: {
541
653
  message_id: metadata.message_id,
654
+ step_id: metadata.step_id || "",
542
655
  role: metadata.role === "assistant" ? "assistant" : "user"
543
656
  }
544
657
  };
545
- case "text_message_content":
546
- return {
658
+ return textStartResult;
659
+ }
660
+ case "text_message_content": {
661
+ const textContentResult = {
547
662
  type: "text_message_content",
548
663
  data: {
549
664
  message_id: metadata.message_id,
665
+ step_id: metadata.step_id || "",
550
666
  delta: metadata.delta || ""
551
667
  }
552
668
  };
553
- case "text_message_end":
554
- return {
669
+ return textContentResult;
670
+ }
671
+ case "text_message_end": {
672
+ const textEndResult = {
555
673
  type: "text_message_end",
556
674
  data: {
557
- message_id: metadata.message_id
675
+ message_id: metadata.message_id,
676
+ step_id: metadata.step_id || ""
558
677
  }
559
678
  };
560
- default:
679
+ return textEndResult;
680
+ }
681
+ case "tool_calls": {
682
+ const toolCallsResult = {
683
+ type: "tool_calls",
684
+ data: {
685
+ tool_calls: metadata.tool_calls || []
686
+ }
687
+ };
688
+ return toolCallsResult;
689
+ }
690
+ case "tool_results": {
691
+ const toolResultsResult = {
692
+ type: "tool_results",
693
+ data: {
694
+ results: metadata.results || []
695
+ }
696
+ };
697
+ return toolResultsResult;
698
+ }
699
+ case "browser_screenshot": {
700
+ const browserScreenshotResult = {
701
+ type: "browser_screenshot",
702
+ data: {
703
+ image: metadata.image || "",
704
+ format: metadata.format,
705
+ filename: metadata.filename,
706
+ size: metadata.size,
707
+ timestamp_ms: metadata.timestamp_ms
708
+ }
709
+ };
710
+ return browserScreenshotResult;
711
+ }
712
+ case "inline_hook_requested": {
713
+ const hookRequested = {
714
+ type: "inline_hook_requested",
715
+ data: {
716
+ hook_id: metadata.request?.hook_id || metadata.hook_id || "",
717
+ hook: metadata.request?.hook || metadata.hook || "",
718
+ context: metadata.request?.context || metadata.context || {
719
+ agent_id: statusUpdate.agentId,
720
+ thread_id: statusUpdate.contextId,
721
+ task_id: statusUpdate.taskId,
722
+ run_id: statusUpdate.agentId
723
+ },
724
+ timeout_ms: metadata.request?.timeout_ms || metadata.timeout_ms,
725
+ fire_and_forget: metadata.request?.fire_and_forget ?? metadata.fire_and_forget,
726
+ message: metadata.request?.message || metadata.message,
727
+ plan: metadata.request?.plan || metadata.plan,
728
+ result: metadata.request?.result || metadata.result
729
+ }
730
+ };
731
+ return hookRequested;
732
+ }
733
+ default: {
561
734
  console.warn(`Unhandled status update metadata type: ${metadata.type}`, metadata);
562
- return {
735
+ const defaultResult = {
563
736
  type: "run_started",
564
- data: { metadata }
737
+ data: {
738
+ runId: statusUpdate.runId,
739
+ taskId: statusUpdate.taskId
740
+ }
565
741
  };
742
+ return defaultResult;
743
+ }
566
744
  }
567
745
  }
568
- function convertA2AArtifactToDistri(artifact) {
569
- if (!artifact || !artifact.parts || !Array.isArray(artifact.parts)) {
570
- return null;
571
- }
572
- const part = artifact.parts[0];
573
- if (!part || part.kind !== "data" || !part.data) {
574
- return null;
575
- }
576
- const data = part.data;
577
- if (data.type === "llm_response") {
578
- const hasContent = data.content && data.content.trim() !== "";
579
- const hasToolCalls = data.tool_calls && Array.isArray(data.tool_calls) && data.tool_calls.length > 0;
580
- const isExternal = data.is_external;
581
- if (hasToolCalls) {
582
- const executionResult2 = {
583
- id: data.id || artifact.artifactId,
584
- type: "llm_response",
585
- timestamp: data.timestamp || data.created_at || Date.now(),
586
- content: data.content?.trim() || "",
587
- tool_calls: data.tool_calls,
588
- step_id: data.step_id,
589
- success: data.success,
590
- rejected: data.rejected,
591
- reason: data.reason,
592
- is_external: isExternal
593
- };
594
- return executionResult2;
595
- } else {
596
- const parts = [];
597
- if (hasContent) {
598
- parts.push({ type: "text", text: data.content });
599
- }
600
- const distriMessage = {
601
- id: artifact.artifactId,
602
- role: "assistant",
603
- parts,
604
- created_at: data.timestamp || data.created_at || (/* @__PURE__ */ new Date()).toISOString()
605
- };
606
- return distriMessage;
607
- }
608
- }
609
- if (data.type === "tool_results") {
610
- const executionResult2 = {
611
- id: data.id || artifact.artifactId,
612
- type: "tool_results",
613
- timestamp: data.timestamp || data.created_at || Date.now(),
614
- results: data.results || [],
615
- step_id: data.step_id,
616
- success: data.success,
617
- rejected: data.rejected,
618
- reason: data.reason
619
- };
620
- return executionResult2;
621
- }
622
- if (data.type === "plan") {
623
- const planResult = {
624
- id: data.id || artifact.artifactId,
625
- type: "plan",
626
- timestamp: data.timestamp || data.created_at || Date.now(),
627
- reasoning: data.reasoning || "",
628
- steps: data.steps || []
629
- };
630
- return planResult;
631
- }
632
- const executionResult = {
633
- id: artifact.artifactId,
634
- type: "artifact",
635
- timestamp: Date.now(),
636
- data,
637
- artifactId: artifact.artifactId,
638
- name: artifact.name || "",
639
- description: artifact.description || null
640
- };
641
- return executionResult;
642
- }
643
746
  function decodeA2AStreamEvent(event) {
644
- if (event.artifactId && event.parts) {
645
- return convertA2AArtifactToDistri(event);
646
- }
647
747
  if (event.kind === "message") {
648
748
  return convertA2AMessageToDistri(event);
649
749
  }
650
750
  if (event.kind === "status-update") {
651
751
  return convertA2AStatusUpdateToDistri(event);
652
752
  }
653
- if (event.kind === "artifact-update") {
654
- return convertA2AArtifactToDistri(event);
655
- }
656
753
  return null;
657
754
  }
658
755
  function processA2AStreamData(streamData) {
@@ -678,28 +775,26 @@ function processA2AMessagesData(data) {
678
775
  function convertA2APartToDistri(a2aPart) {
679
776
  switch (a2aPart.kind) {
680
777
  case "text":
681
- return { type: "text", text: a2aPart.text };
778
+ return { part_type: "text", data: a2aPart.text };
682
779
  case "file":
683
780
  if ("uri" in a2aPart.file) {
684
- return { type: "image_url", image: { mime_type: a2aPart.file.mimeType, url: a2aPart.file.uri } };
781
+ const fileUrl = { type: "url", mime_type: a2aPart.file.mimeType || "application/octet-stream", url: a2aPart.file.uri || "" };
782
+ return { part_type: "image", data: fileUrl };
685
783
  } else {
686
- return { type: "image_bytes", image: { mime_type: a2aPart.file.mimeType, data: a2aPart.file.bytes } };
784
+ const fileBytes = { type: "bytes", mime_type: a2aPart.file.mimeType || "application/octet-stream", data: a2aPart.file.bytes || "" };
785
+ return { part_type: "image", data: fileBytes };
687
786
  }
688
787
  case "data":
689
788
  switch (a2aPart.data.part_type) {
690
789
  case "tool_call":
691
- return { type: "tool_call", tool_call: a2aPart.data };
790
+ return { part_type: "tool_call", data: a2aPart.data };
692
791
  case "tool_result":
693
- return { type: "tool_result", tool_result: a2aPart.data };
694
- case "code_observation":
695
- return { type: "code_observation", thought: a2aPart.data.thought, code: a2aPart.data.code };
696
- case "plan":
697
- return { type: "plan", plan: a2aPart.data.plan };
792
+ return { part_type: "tool_result", data: a2aPart.data };
698
793
  default:
699
- return { type: "data", data: a2aPart.data };
794
+ return { part_type: "data", data: a2aPart.data };
700
795
  }
701
796
  default:
702
- return { type: "text", text: JSON.stringify(a2aPart) };
797
+ return { part_type: "text", data: JSON.stringify(a2aPart) };
703
798
  }
704
799
  }
705
800
  function convertDistriMessageToA2A(distriMessage, context) {
@@ -724,52 +819,95 @@ function convertDistriMessageToA2A(distriMessage, context) {
724
819
  parts: distriMessage.parts.map(convertDistriPartToA2A),
725
820
  kind: "message",
726
821
  contextId: context.thread_id,
727
- taskId: context.run_id
822
+ taskId: context.task_id || context.run_id || void 0
728
823
  };
729
824
  }
730
825
  function convertDistriPartToA2A(distriPart) {
731
- switch (distriPart.type) {
826
+ let result;
827
+ switch (distriPart.part_type) {
732
828
  case "text":
733
- return { kind: "text", text: distriPart.text };
734
- case "image_url":
735
- return { kind: "file", file: { mimeType: distriPart.image.mime_type, uri: distriPart.image.url } };
736
- case "image_bytes":
737
- return { kind: "file", file: { mimeType: distriPart.image.mime_type, bytes: distriPart.image.data } };
829
+ result = { kind: "text", text: distriPart.data };
830
+ break;
831
+ case "image":
832
+ if ("url" in distriPart.data) {
833
+ const fileUri = { mimeType: distriPart.data.mime_type, uri: distriPart.data.url };
834
+ result = { kind: "file", file: fileUri };
835
+ } else {
836
+ const fileBytes = { mimeType: distriPart.data.mime_type, bytes: distriPart.data.data };
837
+ result = { kind: "file", file: fileBytes };
838
+ }
839
+ break;
738
840
  case "tool_call":
739
- return { kind: "data", data: { part_type: "tool_call", tool_call: distriPart.tool_call } };
740
- case "tool_result":
741
- let val = {
841
+ result = {
742
842
  kind: "data",
743
843
  data: {
744
- tool_call_id: distriPart.tool_result.tool_call_id,
745
- tool_name: distriPart.tool_result.tool_name,
746
- result: distriPart.tool_result.result,
747
- part_type: "tool_result"
844
+ part_type: "tool_call",
845
+ data: distriPart.data
748
846
  }
749
847
  };
750
- return val;
751
- case "code_observation":
752
- return { kind: "data", data: { ...distriPart, part_type: "code_observation" } };
753
- case "plan":
754
- return { kind: "data", data: { ...distriPart, part_type: "plan" } };
755
- case "data":
756
- return { kind: "data", ...distriPart.data };
848
+ break;
849
+ case "tool_result": {
850
+ const toolResult = distriPart.data;
851
+ const parts = toolResult.parts.map((part) => {
852
+ if ("type" in part && part.type === "data") {
853
+ return {
854
+ part_type: "data",
855
+ data: part.data
856
+ };
857
+ } else if ("part_type" in part) {
858
+ return part;
859
+ } else {
860
+ return {
861
+ part_type: "data",
862
+ data: part
863
+ };
864
+ }
865
+ });
866
+ result = {
867
+ kind: "data",
868
+ data: {
869
+ part_type: "tool_result",
870
+ data: {
871
+ tool_call_id: toolResult.tool_call_id,
872
+ tool_name: toolResult.tool_name,
873
+ parts
874
+ }
875
+ }
876
+ };
877
+ break;
878
+ }
879
+ case "data": {
880
+ const dataValue = distriPart.data;
881
+ if (dataValue === null || typeof dataValue !== "object" || Array.isArray(dataValue)) {
882
+ result = { kind: "data", data: { value: dataValue } };
883
+ } else {
884
+ const dataObj = dataValue;
885
+ result = { kind: "data", data: dataObj };
886
+ }
887
+ break;
888
+ }
757
889
  }
890
+ return result;
758
891
  }
759
892
  function extractTextFromDistriMessage(message) {
760
- return message.parts.filter((part) => part.type === "text").map((part) => part.text).join("\n");
893
+ return message.parts.filter((part) => part.part_type === "text").map((part) => part.data).join("\n");
761
894
  }
762
895
  function extractToolCallsFromDistriMessage(message) {
763
- return message.parts.filter((part) => part.type === "tool_call").map((part) => part.tool_call);
896
+ return message.parts.filter((part) => part.part_type === "tool_call").map((part) => part.data);
764
897
  }
765
898
  function extractToolResultsFromDistriMessage(message) {
766
- return message.parts.filter((part) => part.type === "tool_result").map((part) => part.tool_result);
899
+ return message.parts.filter((part) => part.part_type === "tool_result").map((part) => part.data);
767
900
  }
768
901
 
769
902
  // src/distri-client.ts
770
- var DistriClient = class {
903
+ var _DistriClient = class _DistriClient {
771
904
  constructor(config) {
772
905
  this.agentClients = /* @__PURE__ */ new Map();
906
+ const headers = { ...config.headers };
907
+ this.accessToken = config.accessToken;
908
+ this.refreshToken = config.refreshToken;
909
+ this.tokenRefreshSkewMs = config.tokenRefreshSkewMs ?? 6e4;
910
+ this.onTokenRefresh = config.onTokenRefresh;
773
911
  this.config = {
774
912
  baseUrl: config.baseUrl.replace(/\/$/, ""),
775
913
  apiVersion: config.apiVersion || "v1",
@@ -777,10 +915,362 @@ var DistriClient = class {
777
915
  retryAttempts: config.retryAttempts || 3,
778
916
  retryDelay: config.retryDelay || 1e3,
779
917
  debug: config.debug || false,
780
- headers: config.headers || {},
918
+ headers,
781
919
  interceptor: config.interceptor || ((init) => Promise.resolve(init))
782
920
  };
783
- this.debug("DistriClient initialized with config:", this.config);
921
+ this.debug("DistriClient initialized with config:", {
922
+ baseUrl: this.config.baseUrl,
923
+ hasAccessToken: !!this.accessToken,
924
+ hasRefreshToken: !!this.refreshToken,
925
+ timeout: this.config.timeout
926
+ });
927
+ }
928
+ /**
929
+ * Create a client with default cloud configuration.
930
+ *
931
+ * @param overrides - Optional overrides for the default config
932
+ */
933
+ static create(overrides = {}) {
934
+ return new _DistriClient({
935
+ baseUrl: DEFAULT_BASE_URL,
936
+ ...overrides
937
+ });
938
+ }
939
+ /**
940
+ * Check if this client has authentication configured.
941
+ */
942
+ hasAuth() {
943
+ return !!this.accessToken || !!this.refreshToken;
944
+ }
945
+ /**
946
+ * Check if this client is configured for local development.
947
+ */
948
+ isLocal() {
949
+ return this.config.baseUrl.includes("localhost") || this.config.baseUrl.includes("127.0.0.1");
950
+ }
951
+ /**
952
+ * Session store: set a value (optionally with expiry)
953
+ */
954
+ async setSessionValue(sessionId, key, value, expiry) {
955
+ const body = { key, value };
956
+ if (expiry) {
957
+ body.expiry = typeof expiry === "string" ? expiry : expiry.toISOString();
958
+ }
959
+ const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values`, {
960
+ method: "POST",
961
+ headers: {
962
+ "Content-Type": "application/json",
963
+ ...this.config.headers
964
+ },
965
+ body: JSON.stringify(body)
966
+ });
967
+ if (!resp.ok && resp.status !== 204) {
968
+ const errorData = await resp.json().catch(() => ({}));
969
+ throw new ApiError(errorData.error || "Failed to set session value", resp.status);
970
+ }
971
+ }
972
+ /**
973
+ * Session store: get a single value
974
+ */
975
+ async getSessionValue(sessionId, key) {
976
+ const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
977
+ method: "GET",
978
+ headers: {
979
+ ...this.config.headers
980
+ }
981
+ });
982
+ if (!resp.ok) {
983
+ const errorData = await resp.json().catch(() => ({}));
984
+ throw new ApiError(errorData.error || "Failed to get session value", resp.status);
985
+ }
986
+ const data = await resp.json().catch(() => ({ value: null }));
987
+ return data?.value ?? null;
988
+ }
989
+ /**
990
+ * Session store: get all values in a session
991
+ */
992
+ async getSessionValues(sessionId) {
993
+ const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values`, {
994
+ method: "GET",
995
+ headers: {
996
+ ...this.config.headers
997
+ }
998
+ });
999
+ if (!resp.ok) {
1000
+ const errorData = await resp.json().catch(() => ({}));
1001
+ throw new ApiError(errorData.error || "Failed to get session values", resp.status);
1002
+ }
1003
+ const data = await resp.json().catch(() => ({ values: {} }));
1004
+ return data?.values ?? {};
1005
+ }
1006
+ /**
1007
+ * Session store: delete a single key
1008
+ */
1009
+ async deleteSessionValue(sessionId, key) {
1010
+ const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
1011
+ method: "DELETE",
1012
+ headers: {
1013
+ ...this.config.headers
1014
+ }
1015
+ });
1016
+ if (!resp.ok && resp.status !== 204) {
1017
+ const errorData = await resp.json().catch(() => ({}));
1018
+ throw new ApiError(errorData.error || "Failed to delete session value", resp.status);
1019
+ }
1020
+ }
1021
+ /**
1022
+ * Session store: clear all keys in a session
1023
+ */
1024
+ async clearSession(sessionId) {
1025
+ const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}`, {
1026
+ method: "DELETE",
1027
+ headers: {
1028
+ ...this.config.headers
1029
+ }
1030
+ });
1031
+ if (!resp.ok && resp.status !== 204) {
1032
+ const errorData = await resp.json().catch(() => ({}));
1033
+ throw new ApiError(errorData.error || "Failed to clear session", resp.status);
1034
+ }
1035
+ }
1036
+ /**
1037
+ * Set additional user message parts for the next agent iteration.
1038
+ * These parts will be appended to the user message in the prompt.
1039
+ * @param sessionId - The thread/session ID
1040
+ * @param parts - Array of DistriPart objects to append to user message
1041
+ */
1042
+ async setAdditionalUserParts(sessionId, parts) {
1043
+ await this.setSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY, parts);
1044
+ }
1045
+ /**
1046
+ * Get the current additional user message parts.
1047
+ * @param sessionId - The thread/session ID
1048
+ * @returns Array of DistriPart objects or null if not set
1049
+ */
1050
+ async getAdditionalUserParts(sessionId) {
1051
+ return this.getSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY);
1052
+ }
1053
+ /**
1054
+ * Clear/delete the additional user message parts.
1055
+ * @param sessionId - The thread/session ID
1056
+ */
1057
+ async clearAdditionalUserParts(sessionId) {
1058
+ await this.deleteSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY);
1059
+ }
1060
+ /**
1061
+ * Issue an access token + refresh token for temporary authentication.
1062
+ * Requires an existing authenticated session (bearer token).
1063
+ *
1064
+ * @returns Token response with access/refresh token strings
1065
+ * @throws ApiError if not authenticated or token issuance fails
1066
+ *
1067
+ * @example
1068
+ * ```typescript
1069
+ * const { access_token, refresh_token } = await client.issueToken();
1070
+ * // Persist the refresh token and use access_token for requests
1071
+ * ```
1072
+ */
1073
+ async issueToken() {
1074
+ const response = await this.fetch("/token", {
1075
+ method: "POST",
1076
+ headers: {
1077
+ "Content-Type": "application/json",
1078
+ ...this.config.headers
1079
+ }
1080
+ });
1081
+ if (!response.ok) {
1082
+ const errorData = await response.json().catch(() => ({}));
1083
+ throw new ApiError(errorData.error || "Failed to issue token", response.status);
1084
+ }
1085
+ const tokens = await response.json();
1086
+ if (!tokens?.access_token || !tokens?.refresh_token || typeof tokens?.expires_at !== "number") {
1087
+ throw new ApiError("Invalid token response", response.status);
1088
+ }
1089
+ this.applyTokens(tokens.access_token, tokens.refresh_token, false);
1090
+ return tokens;
1091
+ }
1092
+ /**
1093
+ * Get the current access/refresh tokens.
1094
+ */
1095
+ getTokens() {
1096
+ return { accessToken: this.accessToken, refreshToken: this.refreshToken };
1097
+ }
1098
+ /**
1099
+ * Update the access/refresh tokens in memory.
1100
+ */
1101
+ setTokens(tokens) {
1102
+ if (tokens.accessToken !== void 0) {
1103
+ this.accessToken = tokens.accessToken;
1104
+ }
1105
+ if (tokens.refreshToken !== void 0) {
1106
+ this.refreshToken = tokens.refreshToken;
1107
+ }
1108
+ }
1109
+ /**
1110
+ * Start streaming speech-to-text transcription via WebSocket
1111
+ */
1112
+ async streamingTranscription(options = {}) {
1113
+ const baseUrl = this.config.baseUrl;
1114
+ const wsUrl = baseUrl.replace("http://", "ws://").replace("https://", "wss://") + "/voice/stream";
1115
+ return new Promise((resolve, reject) => {
1116
+ const ws = new WebSocket(wsUrl);
1117
+ let isResolved = false;
1118
+ ws.onopen = () => {
1119
+ ws.send(JSON.stringify({ type: "start_session" }));
1120
+ options.onStart?.();
1121
+ if (!isResolved) {
1122
+ isResolved = true;
1123
+ resolve({
1124
+ sendAudio: (audioData) => {
1125
+ if (ws.readyState === WebSocket.OPEN) {
1126
+ ws.send(audioData);
1127
+ }
1128
+ },
1129
+ sendText: (text) => {
1130
+ if (ws.readyState === WebSocket.OPEN) {
1131
+ ws.send(JSON.stringify({ type: "text_chunk", text }));
1132
+ }
1133
+ },
1134
+ stop: () => {
1135
+ if (ws.readyState === WebSocket.OPEN) {
1136
+ ws.send(JSON.stringify({ type: "end_session" }));
1137
+ }
1138
+ },
1139
+ close: () => {
1140
+ ws.close();
1141
+ }
1142
+ });
1143
+ }
1144
+ };
1145
+ ws.onmessage = (event) => {
1146
+ try {
1147
+ const data = JSON.parse(event.data);
1148
+ switch (data.type) {
1149
+ case "text_chunk":
1150
+ options.onTranscript?.(data.text || "", data.is_final || false);
1151
+ break;
1152
+ case "session_started":
1153
+ this.debug("Speech-to-text session started");
1154
+ break;
1155
+ case "session_ended":
1156
+ this.debug("Speech-to-text session ended");
1157
+ options.onEnd?.();
1158
+ break;
1159
+ case "error": {
1160
+ const error = new Error(data.message || "WebSocket error");
1161
+ this.debug("Speech-to-text error:", error);
1162
+ options.onError?.(error);
1163
+ break;
1164
+ }
1165
+ default:
1166
+ this.debug("Unknown message type:", data.type);
1167
+ }
1168
+ } catch (error) {
1169
+ const parseError = new Error("Failed to parse WebSocket message");
1170
+ this.debug("Parse error:", parseError);
1171
+ options.onError?.(parseError);
1172
+ }
1173
+ };
1174
+ ws.onerror = (event) => {
1175
+ const error = new Error("WebSocket connection error");
1176
+ this.debug("WebSocket error:", event);
1177
+ options.onError?.(error);
1178
+ if (!isResolved) {
1179
+ isResolved = true;
1180
+ reject(error);
1181
+ }
1182
+ };
1183
+ ws.onclose = (event) => {
1184
+ this.debug("WebSocket closed:", event.code, event.reason);
1185
+ options.onEnd?.();
1186
+ };
1187
+ });
1188
+ }
1189
+ /**
1190
+ * Transcribe audio blob to text using speech-to-text API
1191
+ */
1192
+ async transcribe(audioBlob, config = {}) {
1193
+ try {
1194
+ const arrayBuffer = await audioBlob.arrayBuffer();
1195
+ const uint8Array = new Uint8Array(arrayBuffer);
1196
+ const base64String = btoa(String.fromCharCode(...uint8Array));
1197
+ const requestBody = {
1198
+ audio: base64String,
1199
+ model: config.model || "whisper-1",
1200
+ ...config.language && { language: config.language },
1201
+ ...config.temperature !== void 0 && { temperature: config.temperature }
1202
+ };
1203
+ this.debug("Transcribing audio:", {
1204
+ model: requestBody.model,
1205
+ language: config.language,
1206
+ audioSize: audioBlob.size
1207
+ });
1208
+ const response = await this.fetch(`/tts/transcribe`, {
1209
+ method: "POST",
1210
+ headers: {
1211
+ "Content-Type": "application/json",
1212
+ ...this.config.headers
1213
+ },
1214
+ body: JSON.stringify(requestBody)
1215
+ });
1216
+ if (!response.ok) {
1217
+ const errorData = await response.json().catch(() => ({}));
1218
+ const errorMessage = errorData.error || `Transcription failed: ${response.status}`;
1219
+ throw new ApiError(errorMessage, response.status);
1220
+ }
1221
+ const result = await response.json();
1222
+ const transcription = result.text || "";
1223
+ this.debug("Transcription result:", { text: transcription });
1224
+ return transcription;
1225
+ } catch (error) {
1226
+ if (error instanceof ApiError) throw error;
1227
+ throw new DistriError("Failed to transcribe audio", "TRANSCRIPTION_ERROR", error);
1228
+ }
1229
+ }
1230
+ async getConfiguration() {
1231
+ const response = await this.fetch(`/configuration`, {
1232
+ method: "GET",
1233
+ headers: {
1234
+ ...this.config.headers
1235
+ }
1236
+ });
1237
+ if (!response.ok) {
1238
+ const errorData = await response.json().catch(() => ({}));
1239
+ throw new ApiError(errorData.error || "Failed to load configuration", response.status);
1240
+ }
1241
+ return response.json();
1242
+ }
1243
+ async updateConfiguration(configuration) {
1244
+ const response = await this.fetch(`/configuration`, {
1245
+ method: "PUT",
1246
+ headers: {
1247
+ "Content-Type": "application/json",
1248
+ ...this.config.headers
1249
+ },
1250
+ body: JSON.stringify(configuration)
1251
+ });
1252
+ if (!response.ok) {
1253
+ const errorData = await response.json().catch(() => ({}));
1254
+ throw new ApiError(errorData.error || "Failed to update configuration", response.status);
1255
+ }
1256
+ return response.json();
1257
+ }
1258
+ /**
1259
+ * Minimal LLM helper that proxies to the Distri server using Distri messages.
1260
+ */
1261
+ async llm(messages, tools = [], options) {
1262
+ const headers = { "Content-Type": "application/json" };
1263
+ const response = await this.fetch(`/llm/execute`, {
1264
+ method: "POST",
1265
+ headers,
1266
+ body: JSON.stringify({ messages, tools, ...options })
1267
+ });
1268
+ if (!response.ok) {
1269
+ const errorData = await response.json().catch(() => ({}));
1270
+ const message = errorData?.error || response.statusText || "LLM request failed";
1271
+ throw new ApiError(`LLM request failed: ${message}`, response.status);
1272
+ }
1273
+ return response.json();
784
1274
  }
785
1275
  /**
786
1276
  * Get all available agents from the Distri server
@@ -837,14 +1327,18 @@ var DistriClient = class {
837
1327
  * Get or create A2AClient for an agent
838
1328
  */
839
1329
  getA2AClient(agentId) {
840
- if (!this.agentClients.has(agentId)) {
1330
+ const agentUrl = `${this.config.baseUrl}/agents/${agentId}`;
1331
+ const existing = this.agentClients.get(agentId);
1332
+ if (!existing || existing.url !== agentUrl) {
841
1333
  const fetchFn = this.fetchAbsolute.bind(this);
842
- const agentUrl = `${this.config.baseUrl}/agents/${agentId}`;
843
1334
  const client = new A2AClient(agentUrl, fetchFn);
844
- this.agentClients.set(agentId, client);
845
- this.debug(`Created A2AClient for agent ${agentId} at ${agentUrl}`);
1335
+ this.agentClients.set(agentId, { url: agentUrl, client });
1336
+ this.debug(
1337
+ existing ? `Recreated A2AClient for agent ${agentId} with new URL ${agentUrl}` : `Created A2AClient for agent ${agentId} at ${agentUrl}`
1338
+ );
1339
+ return client;
846
1340
  }
847
- return this.agentClients.get(agentId);
1341
+ return existing.client;
848
1342
  }
849
1343
  /**
850
1344
  * Send a message to an agent
@@ -871,6 +1365,7 @@ var DistriClient = class {
871
1365
  * Send a streaming message to an agent
872
1366
  */
873
1367
  async *sendMessageStream(agentId, params) {
1368
+ console.log("sendMessageStream", agentId, params);
874
1369
  try {
875
1370
  const client = this.getA2AClient(agentId);
876
1371
  yield* await client.sendMessageStream(params);
@@ -976,18 +1471,195 @@ var DistriClient = class {
976
1471
  };
977
1472
  await this.sendMessage(threadId, params);
978
1473
  }
1474
+ /**
1475
+ * Complete an external tool call
1476
+ */
1477
+ async completeTool(agentId, result) {
1478
+ try {
1479
+ const response = await this.fetch(`/agents/${agentId}/complete-tool`, {
1480
+ method: "POST",
1481
+ headers: {
1482
+ "Content-Type": "application/json",
1483
+ ...this.config.headers
1484
+ },
1485
+ body: JSON.stringify({
1486
+ tool_call_id: result.tool_call_id,
1487
+ tool_response: result
1488
+ })
1489
+ });
1490
+ if (!response.ok) {
1491
+ throw new ApiError(`Failed to complete tool: ${response.statusText}`, response.status);
1492
+ }
1493
+ this.debug(`Tool completed: ${result.tool_name} (${result.tool_call_id}) for agent ${agentId}`);
1494
+ } catch (error) {
1495
+ if (error instanceof ApiError) throw error;
1496
+ throw new DistriError(`Failed to complete tool ${result.tool_name} (${result.tool_call_id}) for agent ${agentId}`, "COMPLETE_TOOL_ERROR", error);
1497
+ }
1498
+ }
1499
+ /**
1500
+ * Complete an inline hook with a mutation payload.
1501
+ */
1502
+ async completeInlineHook(hookId, mutation) {
1503
+ const response = await this.fetch(`/event/hooks`, {
1504
+ method: "POST",
1505
+ headers: {
1506
+ "Content-Type": "application/json",
1507
+ ...this.config.headers
1508
+ },
1509
+ body: JSON.stringify({
1510
+ hook_id: hookId,
1511
+ mutation
1512
+ })
1513
+ });
1514
+ if (!response.ok) {
1515
+ throw new ApiError(`Failed to complete inline hook: ${response.statusText}`, response.status);
1516
+ }
1517
+ }
979
1518
  /**
980
1519
  * Get the base URL for making direct requests
981
1520
  */
982
1521
  get baseUrl() {
983
1522
  return this.config.baseUrl;
984
1523
  }
1524
+ applyTokens(accessToken, refreshToken, notify) {
1525
+ this.accessToken = accessToken;
1526
+ this.refreshToken = refreshToken;
1527
+ if (notify && this.onTokenRefresh) {
1528
+ this.onTokenRefresh({ accessToken, refreshToken });
1529
+ }
1530
+ }
1531
+ async ensureAccessToken() {
1532
+ if (!this.refreshToken) {
1533
+ return;
1534
+ }
1535
+ if (!this.accessToken || this.isTokenExpiring(this.accessToken)) {
1536
+ try {
1537
+ await this.refreshTokens();
1538
+ } catch (error) {
1539
+ this.debug("Token refresh failed:", error);
1540
+ }
1541
+ }
1542
+ }
1543
+ async refreshTokens() {
1544
+ if (!this.refreshToken) {
1545
+ return;
1546
+ }
1547
+ if (!this.refreshPromise) {
1548
+ this.refreshPromise = this.performTokenRefresh().finally(() => {
1549
+ this.refreshPromise = void 0;
1550
+ });
1551
+ }
1552
+ return this.refreshPromise;
1553
+ }
1554
+ async performTokenRefresh() {
1555
+ const response = await this.fetchAbsolute(
1556
+ `${this.config.baseUrl}/token`,
1557
+ {
1558
+ method: "POST",
1559
+ headers: {
1560
+ "Content-Type": "application/json",
1561
+ ...this.config.headers
1562
+ },
1563
+ body: JSON.stringify({
1564
+ grant_type: "refresh_token",
1565
+ refresh_token: this.refreshToken
1566
+ })
1567
+ },
1568
+ { skipAuth: true, retryOnAuth: false }
1569
+ );
1570
+ if (!response.ok) {
1571
+ const errorData = await response.json().catch(() => ({}));
1572
+ throw new ApiError(errorData.error || "Failed to refresh token", response.status);
1573
+ }
1574
+ const tokens = await response.json();
1575
+ if (!tokens?.access_token || !tokens?.refresh_token) {
1576
+ throw new ApiError("Invalid token response", response.status);
1577
+ }
1578
+ this.applyTokens(tokens.access_token, tokens.refresh_token, true);
1579
+ }
1580
+ isTokenExpiring(token) {
1581
+ const expiresAt = this.getTokenExpiry(token);
1582
+ if (!expiresAt) {
1583
+ return false;
1584
+ }
1585
+ return expiresAt <= Date.now() + this.tokenRefreshSkewMs;
1586
+ }
1587
+ getTokenExpiry(token) {
1588
+ const payload = this.decodeJwtPayload(token);
1589
+ const exp = payload?.exp;
1590
+ if (typeof exp !== "number") {
1591
+ return null;
1592
+ }
1593
+ return exp * 1e3;
1594
+ }
1595
+ decodeJwtPayload(token) {
1596
+ const parts = token.split(".");
1597
+ if (parts.length < 2) {
1598
+ return null;
1599
+ }
1600
+ const decoded = this.decodeBase64Url(parts[1]);
1601
+ if (!decoded) {
1602
+ return null;
1603
+ }
1604
+ try {
1605
+ return JSON.parse(decoded);
1606
+ } catch {
1607
+ return null;
1608
+ }
1609
+ }
1610
+ decodeBase64Url(value) {
1611
+ const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
1612
+ const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
1613
+ try {
1614
+ if (typeof atob === "function") {
1615
+ return atob(padded);
1616
+ }
1617
+ const buffer = globalThis.Buffer;
1618
+ if (typeof buffer !== "undefined") {
1619
+ return buffer.from(padded, "base64").toString("utf8");
1620
+ }
1621
+ } catch {
1622
+ return null;
1623
+ }
1624
+ return null;
1625
+ }
1626
+ applyAuthHeader(headers) {
1627
+ if (this.accessToken && !headers.has("authorization")) {
1628
+ headers.set("Authorization", `Bearer ${this.accessToken}`);
1629
+ }
1630
+ }
985
1631
  /**
986
1632
  * Enhanced fetch with retry logic
987
1633
  */
988
- async fetchAbsolute(url, initialInit) {
1634
+ async fetchAbsolute(url, initialInit, options) {
1635
+ const { skipAuth = false, retryOnAuth = true } = options ?? {};
989
1636
  const init = await this.config.interceptor(initialInit);
990
1637
  let lastError;
1638
+ const headers = new Headers();
1639
+ const applyHeaders = (src) => {
1640
+ if (!src) return;
1641
+ if (src instanceof Headers) {
1642
+ src.forEach((value, key) => headers.set(key, value));
1643
+ } else if (Array.isArray(src)) {
1644
+ src.forEach(([key, value]) => headers.set(key, value));
1645
+ } else if (typeof src === "object") {
1646
+ Object.entries(src).forEach(([key, value]) => {
1647
+ if (typeof value === "string") {
1648
+ headers.set(key, value);
1649
+ }
1650
+ });
1651
+ }
1652
+ };
1653
+ applyHeaders(this.config.headers);
1654
+ applyHeaders(init?.headers);
1655
+ const hasBody = init?.body !== void 0 && !(init.body instanceof FormData) && !(init.body instanceof Blob);
1656
+ if (!headers.has("content-type") && hasBody) {
1657
+ headers.set("Content-Type", "application/json");
1658
+ }
1659
+ if (!skipAuth) {
1660
+ await this.ensureAccessToken();
1661
+ this.applyAuthHeader(headers);
1662
+ }
991
1663
  for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {
992
1664
  try {
993
1665
  const controller = new AbortController();
@@ -995,12 +1667,15 @@ var DistriClient = class {
995
1667
  const response = await fetch(url, {
996
1668
  ...init,
997
1669
  signal: controller.signal,
998
- headers: {
999
- ...this.config.headers,
1000
- ...init?.headers
1001
- }
1670
+ headers
1002
1671
  });
1003
1672
  clearTimeout(timeoutId);
1673
+ if (!skipAuth && retryOnAuth && response.status === 401 && this.refreshToken) {
1674
+ const refreshed = await this.refreshTokens().then(() => true).catch(() => false);
1675
+ if (refreshed) {
1676
+ return this.fetchAbsolute(url, initialInit, { skipAuth, retryOnAuth: false });
1677
+ }
1678
+ }
1004
1679
  return response;
1005
1680
  } catch (error) {
1006
1681
  lastError = error instanceof Error ? error : new Error(String(error));
@@ -1013,7 +1688,8 @@ var DistriClient = class {
1013
1688
  throw lastError;
1014
1689
  }
1015
1690
  /**
1016
- * Enhanced fetch with retry logic
1691
+ * Enhanced fetch with retry logic and auth headers.
1692
+ * Exposed publicly for extensions like DistriHomeClient.
1017
1693
  */
1018
1694
  async fetch(input, initialInit) {
1019
1695
  const url = `${this.config.baseUrl}${input}`;
@@ -1055,7 +1731,7 @@ var DistriClient = class {
1055
1731
  id: id || uuidv4(),
1056
1732
  role,
1057
1733
  parts,
1058
- created_at
1734
+ created_at: created_at || (/* @__PURE__ */ new Date()).getTime()
1059
1735
  };
1060
1736
  }
1061
1737
  /**
@@ -1085,6 +1761,25 @@ var DistriClient = class {
1085
1761
  };
1086
1762
  }
1087
1763
  };
1764
+ // ============================================================
1765
+ // Additional User Message Parts API
1766
+ // ============================================================
1767
+ // These methods allow external tools to append parts (text, images)
1768
+ // to the user message in the next agent iteration.
1769
+ // The parts are stored under the key "__additional_user_parts".
1770
+ _DistriClient.ADDITIONAL_PARTS_KEY = "__additional_user_parts";
1771
+ // ============================================================
1772
+ // Token API
1773
+ // ============================================================
1774
+ // Issue access + refresh tokens for temporary authentication (e.g., frontend use)
1775
+ /**
1776
+ * Response from the token endpoint
1777
+ */
1778
+ _DistriClient.TokenType = {
1779
+ Main: "main",
1780
+ Short: "short"
1781
+ };
1782
+ var DistriClient = _DistriClient;
1088
1783
  function uuidv4() {
1089
1784
  if (typeof crypto?.randomUUID === "function") {
1090
1785
  return crypto.randomUUID();
@@ -1099,42 +1794,32 @@ function uuidv4() {
1099
1794
  }
1100
1795
 
1101
1796
  // src/agent.ts
1797
+ var ExternalToolValidationError = class extends DistriError {
1798
+ constructor(agentName, result) {
1799
+ super(
1800
+ result.message || "Missing required external tools for agent invocation.",
1801
+ "EXTERNAL_TOOL_VALIDATION_ERROR",
1802
+ {
1803
+ agentName,
1804
+ missingTools: result.missingTools,
1805
+ requiredTools: result.requiredTools,
1806
+ providedTools: result.providedTools
1807
+ }
1808
+ );
1809
+ this.name = "ExternalToolValidationError";
1810
+ this.agentName = agentName;
1811
+ this.missingTools = result.missingTools;
1812
+ this.requiredTools = result.requiredTools;
1813
+ this.providedTools = result.providedTools;
1814
+ }
1815
+ };
1102
1816
  var Agent = class _Agent {
1103
1817
  constructor(agentDefinition, client) {
1104
- this.tools = /* @__PURE__ */ new Map();
1818
+ this.hookHandlers = /* @__PURE__ */ new Map();
1819
+ this.defaultHookHandler = null;
1105
1820
  this.agentDefinition = agentDefinition;
1106
1821
  this.client = client;
1107
1822
  }
1108
- /**
1109
- * Add a tool to the agent (AG-UI style)
1110
- */
1111
- registerTool(tool) {
1112
- this.tools.set(tool.name, tool);
1113
- }
1114
- /**
1115
- * Add multiple tools at once
1116
- */
1117
- registerTools(tools) {
1118
- tools.forEach((tool) => this.registerTool(tool));
1119
- }
1120
- /**
1121
- * Remove a tool
1122
- */
1123
- unregisterTool(toolName) {
1124
- this.tools.delete(toolName);
1125
- }
1126
- /**
1127
- * Get all registered tools
1128
- */
1129
- getTools() {
1130
- return Array.from(this.tools.values());
1131
- }
1132
- /**
1133
- * Check if a tool is registered
1134
- */
1135
- hasTool(toolName) {
1136
- return this.tools.has(toolName);
1137
- }
1138
1823
  /**
1139
1824
  * Get agent information
1140
1825
  */
@@ -1147,9 +1832,18 @@ var Agent = class _Agent {
1147
1832
  get description() {
1148
1833
  return this.agentDefinition.description;
1149
1834
  }
1835
+ get agentType() {
1836
+ return this.agentDefinition.agent_type;
1837
+ }
1150
1838
  get iconUrl() {
1151
1839
  return this.agentDefinition.icon_url;
1152
1840
  }
1841
+ /**
1842
+ * Get the full agent definition (including backend tools)
1843
+ */
1844
+ getDefinition() {
1845
+ return this.agentDefinition;
1846
+ }
1153
1847
  /**
1154
1848
  * Fetch messages for a thread (public method for useChat)
1155
1849
  */
@@ -1159,51 +1853,154 @@ var Agent = class _Agent {
1159
1853
  /**
1160
1854
  * Direct (non-streaming) invoke
1161
1855
  */
1162
- async invoke(params) {
1163
- const enhancedParams = this.enhanceParamsWithTools(params);
1164
- console.log("enhancedParams", enhancedParams);
1856
+ async invoke(params, tools, hooks) {
1857
+ if (hooks) {
1858
+ this.registerHooks(hooks);
1859
+ }
1860
+ const enhancedParams = this.enhanceParamsWithTools(params, tools);
1165
1861
  return await this.client.sendMessage(this.agentDefinition.id, enhancedParams);
1166
1862
  }
1167
1863
  /**
1168
1864
  * Streaming invoke
1169
1865
  */
1170
- async invokeStream(params) {
1171
- const enhancedParams = this.enhanceParamsWithTools(params);
1172
- console.log("enhancedParams", enhancedParams);
1866
+ async invokeStream(params, tools, hooks) {
1867
+ if (hooks) {
1868
+ this.registerHooks(hooks);
1869
+ }
1870
+ const enhancedParams = this.enhanceParamsWithTools(params, tools);
1173
1871
  const a2aStream = this.client.sendMessageStream(this.agentDefinition.id, enhancedParams);
1174
- return async function* () {
1872
+ const self = this;
1873
+ return (async function* () {
1175
1874
  for await (const event of a2aStream) {
1176
1875
  const converted = decodeA2AStreamEvent(event);
1177
- if (converted) {
1876
+ if (converted && converted.type === "inline_hook_requested") {
1877
+ const hookReq = converted.data;
1878
+ const handler = self.hookHandlers.get(hookReq.hook) || self.defaultHookHandler;
1879
+ if (handler) {
1880
+ try {
1881
+ const mutation = await handler(hookReq);
1882
+ await self.client.completeInlineHook(hookReq.hook_id, mutation);
1883
+ } catch (err) {
1884
+ await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
1885
+ }
1886
+ } else {
1887
+ await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
1888
+ }
1889
+ yield converted;
1890
+ } else if (converted) {
1178
1891
  yield converted;
1179
1892
  }
1180
1893
  }
1181
- }();
1894
+ })();
1895
+ }
1896
+ /**
1897
+ * Validate that required external tools are registered before invoking.
1898
+ */
1899
+ validateExternalTools(tools = []) {
1900
+ const requiredTools = this.getRequiredExternalTools();
1901
+ const providedTools = tools.map((tool) => tool.name);
1902
+ if (requiredTools.length === 0) {
1903
+ return {
1904
+ isValid: true,
1905
+ requiredTools: [],
1906
+ providedTools,
1907
+ missingTools: []
1908
+ };
1909
+ }
1910
+ const providedSet = new Set(providedTools);
1911
+ const missingTools = requiredTools.filter((tool) => !providedSet.has(tool));
1912
+ const isValid = missingTools.length === 0;
1913
+ return {
1914
+ isValid,
1915
+ requiredTools,
1916
+ providedTools,
1917
+ missingTools,
1918
+ message: isValid ? void 0 : this.formatExternalToolValidationMessage(requiredTools, missingTools)
1919
+ };
1182
1920
  }
1183
1921
  /**
1184
1922
  * Enhance message params with tool definitions
1185
1923
  */
1186
- enhanceParamsWithTools(params) {
1187
- const tools = this.getTools();
1924
+ enhanceParamsWithTools(params, tools) {
1925
+ this.assertExternalTools(tools);
1926
+ const metadata = {
1927
+ ...params.metadata,
1928
+ external_tools: tools?.map((tool) => ({
1929
+ name: tool.name,
1930
+ description: tool.description,
1931
+ parameters: tool.parameters,
1932
+ is_final: tool.is_final
1933
+ })) || []
1934
+ };
1188
1935
  return {
1189
1936
  ...params,
1190
- metadata: {
1191
- ...params.metadata,
1192
- tools: tools.map((tool) => ({
1193
- name: tool.name,
1194
- description: tool.description,
1195
- input_schema: tool.input_schema
1196
- }))
1197
- }
1937
+ metadata
1198
1938
  };
1199
1939
  }
1940
+ assertExternalTools(tools) {
1941
+ const result = this.validateExternalTools(tools ?? []);
1942
+ if (!result.isValid) {
1943
+ throw new ExternalToolValidationError(this.agentDefinition.name || this.agentDefinition.id, result);
1944
+ }
1945
+ }
1946
+ getRequiredExternalTools() {
1947
+ const toolConfig = this.resolveToolConfig();
1948
+ if (!toolConfig?.external || !Array.isArray(toolConfig.external)) {
1949
+ return [];
1950
+ }
1951
+ return toolConfig.external.filter((tool) => typeof tool === "string" && tool.trim().length > 0);
1952
+ }
1953
+ resolveToolConfig() {
1954
+ const root = this.agentDefinition;
1955
+ return this.extractToolConfig(root) || this.extractToolConfig(root?.agent) || this.extractToolConfig(root?.definition);
1956
+ }
1957
+ extractToolConfig(candidate) {
1958
+ if (!candidate) return null;
1959
+ const tools = candidate.tools;
1960
+ if (!tools || Array.isArray(tools) || typeof tools !== "object") {
1961
+ return null;
1962
+ }
1963
+ return tools;
1964
+ }
1965
+ formatExternalToolValidationMessage(requiredTools, missingTools) {
1966
+ const requiredList = requiredTools.join(", ");
1967
+ const missingList = missingTools.join(", ");
1968
+ return `Agent has external tools that are not registered: ${missingList}. This is an embedded agent that can run within the parent application. Register DistriWidget for embedding the parent component. Required tools: ${requiredList}.`;
1969
+ }
1970
+ /**
1971
+ * Register multiple hooks at once.
1972
+ */
1973
+ registerHooks(hooks, defaultHandler) {
1974
+ Object.entries(hooks).forEach(([hook, handler]) => {
1975
+ this.hookHandlers.set(hook, handler);
1976
+ });
1977
+ if (defaultHandler) {
1978
+ this.defaultHookHandler = defaultHandler;
1979
+ }
1980
+ }
1200
1981
  /**
1201
1982
  * Create an agent instance from an agent ID
1202
1983
  */
1203
1984
  static async create(agentIdOrDef, client) {
1204
1985
  const agentDefinition = typeof agentIdOrDef === "string" ? await client.getAgent(agentIdOrDef) : agentIdOrDef;
1986
+ const tools = agentDefinition?.resolved_tools || [];
1987
+ console.log("\u{1F916} Agent definition loaded:", {
1988
+ id: agentDefinition.id,
1989
+ name: agentDefinition.name,
1990
+ tools: tools.map((t) => ({
1991
+ name: t.name,
1992
+ type: "function"
1993
+ })) || [],
1994
+ toolCount: agentDefinition.tools?.length || 0
1995
+ });
1205
1996
  return new _Agent(agentDefinition, client);
1206
1997
  }
1998
+ /**
1999
+ * Complete an external tool call by sending the result back to the server
2000
+ */
2001
+ async completeTool(result) {
2002
+ await this.client.completeTool(this.agentDefinition.id, result);
2003
+ }
1207
2004
  /**
1208
2005
  * List all available agents
1209
2006
  */
@@ -1218,22 +2015,25 @@ var Agent = class _Agent {
1218
2015
  Agent,
1219
2016
  ApiError,
1220
2017
  ConnectionError,
2018
+ DEFAULT_BASE_URL,
1221
2019
  DistriClient,
1222
2020
  DistriError,
1223
- convertA2AArtifactToDistri,
2021
+ ExternalToolValidationError,
1224
2022
  convertA2AMessageToDistri,
1225
2023
  convertA2APartToDistri,
1226
2024
  convertA2AStatusUpdateToDistri,
1227
2025
  convertDistriMessageToA2A,
1228
2026
  convertDistriPartToA2A,
2027
+ createFailedToolResult,
2028
+ createSuccessfulToolResult,
1229
2029
  decodeA2AStreamEvent,
1230
2030
  extractTextFromDistriMessage,
1231
2031
  extractToolCallsFromDistriMessage,
2032
+ extractToolResultData,
1232
2033
  extractToolResultsFromDistriMessage,
1233
- isDistriArtifact,
2034
+ isArrayParts,
1234
2035
  isDistriEvent,
1235
2036
  isDistriMessage,
1236
- isDistriPlan,
1237
2037
  processA2AMessagesData,
1238
2038
  processA2AStreamData,
1239
2039
  uuidv4