@distri/core 0.2.3 → 0.2.5

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
@@ -22,22 +22,155 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
22
22
  // src/index.ts
23
23
  var index_exports = {};
24
24
  __export(index_exports, {
25
+ A2AProtocolError: () => A2AProtocolError,
25
26
  Agent: () => Agent,
27
+ ApiError: () => ApiError,
28
+ ConnectionError: () => ConnectionError,
29
+ DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
26
30
  DistriClient: () => DistriClient,
31
+ DistriError: () => DistriError,
32
+ ExternalToolValidationError: () => ExternalToolValidationError,
27
33
  convertA2AMessageToDistri: () => convertA2AMessageToDistri,
28
34
  convertA2APartToDistri: () => convertA2APartToDistri,
35
+ convertA2AStatusUpdateToDistri: () => convertA2AStatusUpdateToDistri,
29
36
  convertDistriMessageToA2A: () => convertDistriMessageToA2A,
30
37
  convertDistriPartToA2A: () => convertDistriPartToA2A,
38
+ createFailedToolResult: () => createFailedToolResult,
39
+ createSuccessfulToolResult: () => createSuccessfulToolResult,
40
+ decodeA2AStreamEvent: () => decodeA2AStreamEvent,
31
41
  extractTextFromDistriMessage: () => extractTextFromDistriMessage,
32
42
  extractToolCallsFromDistriMessage: () => extractToolCallsFromDistriMessage,
43
+ extractToolResultData: () => extractToolResultData,
33
44
  extractToolResultsFromDistriMessage: () => extractToolResultsFromDistriMessage,
45
+ isArrayParts: () => isArrayParts,
34
46
  isDistriEvent: () => isDistriEvent,
35
47
  isDistriMessage: () => isDistriMessage,
48
+ processA2AMessagesData: () => processA2AMessagesData,
49
+ processA2AStreamData: () => processA2AStreamData,
36
50
  uuidv4: () => uuidv4
37
51
  });
38
52
  module.exports = __toCommonJS(index_exports);
39
53
 
40
- // ../../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
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";
139
+ var DistriError = class extends Error {
140
+ constructor(message, code, details) {
141
+ super(message);
142
+ this.code = code;
143
+ this.details = details;
144
+ this.name = "DistriError";
145
+ }
146
+ };
147
+ var A2AProtocolError = class extends DistriError {
148
+ constructor(message, details) {
149
+ super(message, "A2A_PROTOCOL_ERROR", details);
150
+ this.name = "A2AProtocolError";
151
+ }
152
+ };
153
+ var ApiError = class extends DistriError {
154
+ constructor(message, statusCode, details) {
155
+ super(message, "API_ERROR", details);
156
+ this.statusCode = statusCode;
157
+ this.name = "ApiError";
158
+ }
159
+ };
160
+ var ConnectionError = class extends DistriError {
161
+ constructor(message, details) {
162
+ super(message, "CONNECTION_ERROR", details);
163
+ this.name = "ConnectionError";
164
+ }
165
+ };
166
+ function isDistriMessage(event) {
167
+ return "id" in event && "role" in event && "parts" in event;
168
+ }
169
+ function isDistriEvent(event) {
170
+ return "type" in event && "data" in event;
171
+ }
172
+
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
41
174
  var A2AClient = class {
42
175
  /**
43
176
  * Constructs an A2AClient instance.
@@ -407,35 +540,6 @@ var A2AClient = class {
407
540
  }
408
541
  };
409
542
 
410
- // src/types.ts
411
- var DistriError = class extends Error {
412
- constructor(message, code, details) {
413
- super(message);
414
- this.code = code;
415
- this.details = details;
416
- this.name = "DistriError";
417
- }
418
- };
419
- var A2AProtocolError = class extends DistriError {
420
- constructor(message, details) {
421
- super(message, "A2A_PROTOCOL_ERROR", details);
422
- this.name = "A2AProtocolError";
423
- }
424
- };
425
- var ApiError = class extends DistriError {
426
- constructor(message, statusCode, details) {
427
- super(message, "API_ERROR", details);
428
- this.statusCode = statusCode;
429
- this.name = "ApiError";
430
- }
431
- };
432
- function isDistriMessage(event) {
433
- return "id" in event && "role" in event && "parts" in event;
434
- }
435
- function isDistriEvent(event) {
436
- return "type" in event && "metadata" in event;
437
- }
438
-
439
543
  // src/encoder.ts
440
544
  function convertA2AMessageToDistri(a2aMessage) {
441
545
  const role = a2aMessage.role === "agent" ? "assistant" : "user";
@@ -446,31 +550,251 @@ function convertA2AMessageToDistri(a2aMessage) {
446
550
  created_at: a2aMessage.createdAt
447
551
  };
448
552
  }
553
+ function convertA2AStatusUpdateToDistri(statusUpdate) {
554
+ if (!statusUpdate.metadata || !statusUpdate.metadata.type) {
555
+ return null;
556
+ }
557
+ const metadata = statusUpdate.metadata;
558
+ switch (metadata.type) {
559
+ case "run_started": {
560
+ const runStartedResult = {
561
+ type: "run_started",
562
+ data: {
563
+ runId: statusUpdate.runId,
564
+ taskId: statusUpdate.taskId
565
+ }
566
+ };
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 = {
581
+ type: "run_finished",
582
+ data: {
583
+ runId: statusUpdate.runId,
584
+ taskId: statusUpdate.taskId
585
+ }
586
+ };
587
+ return runFinishedResult;
588
+ }
589
+ case "plan_started": {
590
+ const planStartedResult = {
591
+ type: "plan_started",
592
+ data: {
593
+ initial_plan: metadata.initial_plan
594
+ }
595
+ };
596
+ return planStartedResult;
597
+ }
598
+ case "plan_finished": {
599
+ const planFinishedResult = {
600
+ type: "plan_finished",
601
+ data: {
602
+ total_steps: metadata.total_steps
603
+ }
604
+ };
605
+ return planFinishedResult;
606
+ }
607
+ case "step_started": {
608
+ const stepStartedResult = {
609
+ type: "step_started",
610
+ data: {
611
+ step_id: metadata.step_id,
612
+ step_title: metadata.step_title || "Processing",
613
+ step_index: metadata.step_index || 0
614
+ }
615
+ };
616
+ return stepStartedResult;
617
+ }
618
+ case "step_completed": {
619
+ const stepCompletedResult = {
620
+ type: "step_completed",
621
+ data: {
622
+ step_id: metadata.step_id,
623
+ step_title: metadata.step_title || "Processing",
624
+ step_index: metadata.step_index || 0
625
+ }
626
+ };
627
+ return stepCompletedResult;
628
+ }
629
+ case "tool_execution_start": {
630
+ const toolStartResult = {
631
+ type: "tool_execution_start",
632
+ data: {
633
+ tool_call_id: metadata.tool_call_id,
634
+ tool_call_name: metadata.tool_call_name || "Tool",
635
+ parent_message_id: statusUpdate.taskId
636
+ }
637
+ };
638
+ return toolStartResult;
639
+ }
640
+ case "tool_execution_end": {
641
+ const toolEndResult = {
642
+ type: "tool_execution_end",
643
+ data: {
644
+ tool_call_id: metadata.tool_call_id
645
+ }
646
+ };
647
+ return toolEndResult;
648
+ }
649
+ case "text_message_start": {
650
+ const textStartResult = {
651
+ type: "text_message_start",
652
+ data: {
653
+ message_id: metadata.message_id,
654
+ step_id: metadata.step_id || "",
655
+ role: metadata.role === "assistant" ? "assistant" : "user"
656
+ }
657
+ };
658
+ return textStartResult;
659
+ }
660
+ case "text_message_content": {
661
+ const textContentResult = {
662
+ type: "text_message_content",
663
+ data: {
664
+ message_id: metadata.message_id,
665
+ step_id: metadata.step_id || "",
666
+ delta: metadata.delta || ""
667
+ }
668
+ };
669
+ return textContentResult;
670
+ }
671
+ case "text_message_end": {
672
+ const textEndResult = {
673
+ type: "text_message_end",
674
+ data: {
675
+ message_id: metadata.message_id,
676
+ step_id: metadata.step_id || ""
677
+ }
678
+ };
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: {
734
+ console.warn(`Unhandled status update metadata type: ${metadata.type}`, metadata);
735
+ const defaultResult = {
736
+ type: "run_started",
737
+ data: {
738
+ runId: statusUpdate.runId,
739
+ taskId: statusUpdate.taskId
740
+ }
741
+ };
742
+ return defaultResult;
743
+ }
744
+ }
745
+ }
746
+ function decodeA2AStreamEvent(event) {
747
+ if (event.kind === "message") {
748
+ return convertA2AMessageToDistri(event);
749
+ }
750
+ if (event.kind === "status-update") {
751
+ return convertA2AStatusUpdateToDistri(event);
752
+ }
753
+ return null;
754
+ }
755
+ function processA2AStreamData(streamData) {
756
+ const results = [];
757
+ for (const item of streamData) {
758
+ const converted = decodeA2AStreamEvent(item);
759
+ if (converted) {
760
+ results.push(converted);
761
+ }
762
+ }
763
+ return results;
764
+ }
765
+ function processA2AMessagesData(data) {
766
+ const results = [];
767
+ for (const item of data) {
768
+ if (item.kind === "message") {
769
+ const distriMessage = convertA2AMessageToDistri(item);
770
+ results.push(distriMessage);
771
+ }
772
+ }
773
+ return results;
774
+ }
449
775
  function convertA2APartToDistri(a2aPart) {
450
776
  switch (a2aPart.kind) {
451
777
  case "text":
452
- return { type: "text", text: a2aPart.text };
778
+ return { part_type: "text", data: a2aPart.text };
453
779
  case "file":
454
780
  if ("uri" in a2aPart.file) {
455
- 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 };
456
783
  } else {
457
- 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 };
458
786
  }
459
787
  case "data":
460
788
  switch (a2aPart.data.part_type) {
461
789
  case "tool_call":
462
- return { type: "tool_call", tool_call: a2aPart.data };
790
+ return { part_type: "tool_call", data: a2aPart.data };
463
791
  case "tool_result":
464
- return { type: "tool_result", tool_result: a2aPart.data };
465
- case "code_observation":
466
- return { type: "code_observation", thought: a2aPart.data.thought, code: a2aPart.data.code };
467
- case "plan":
468
- return { type: "plan", plan: a2aPart.data.plan };
792
+ return { part_type: "tool_result", data: a2aPart.data };
469
793
  default:
470
- return { type: "data", data: a2aPart.data };
794
+ return { part_type: "data", data: a2aPart.data };
471
795
  }
472
796
  default:
473
- return { type: "text", text: JSON.stringify(a2aPart) };
797
+ return { part_type: "text", data: JSON.stringify(a2aPart) };
474
798
  }
475
799
  }
476
800
  function convertDistriMessageToA2A(distriMessage, context) {
@@ -495,52 +819,95 @@ function convertDistriMessageToA2A(distriMessage, context) {
495
819
  parts: distriMessage.parts.map(convertDistriPartToA2A),
496
820
  kind: "message",
497
821
  contextId: context.thread_id,
498
- taskId: context.run_id
822
+ taskId: context.task_id || context.run_id || void 0
499
823
  };
500
824
  }
501
825
  function convertDistriPartToA2A(distriPart) {
502
- switch (distriPart.type) {
826
+ let result;
827
+ switch (distriPart.part_type) {
503
828
  case "text":
504
- return { kind: "text", text: distriPart.text };
505
- case "image_url":
506
- return { kind: "file", file: { mimeType: distriPart.image.mime_type, uri: distriPart.image.url } };
507
- case "image_bytes":
508
- 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;
509
840
  case "tool_call":
510
- return { kind: "data", data: { part_type: "tool_call", tool_call: distriPart.tool_call } };
511
- case "tool_result":
512
- let val = {
841
+ result = {
513
842
  kind: "data",
514
843
  data: {
515
- tool_call_id: distriPart.tool_result.tool_call_id,
516
- result: distriPart.tool_result.result,
517
- part_type: "tool_result"
844
+ part_type: "tool_call",
845
+ data: distriPart.data
518
846
  }
519
847
  };
520
- console.log("<> val", val);
521
- return val;
522
- case "code_observation":
523
- return { kind: "data", data: { ...distriPart, part_type: "code_observation" } };
524
- case "plan":
525
- return { kind: "data", data: { ...distriPart, part_type: "plan" } };
526
- case "data":
527
- 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
+ }
528
889
  }
890
+ return result;
529
891
  }
530
892
  function extractTextFromDistriMessage(message) {
531
- 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");
532
894
  }
533
895
  function extractToolCallsFromDistriMessage(message) {
534
- 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);
535
897
  }
536
898
  function extractToolResultsFromDistriMessage(message) {
537
- 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);
538
900
  }
539
901
 
540
902
  // src/distri-client.ts
541
- var DistriClient = class {
903
+ var _DistriClient = class _DistriClient {
542
904
  constructor(config) {
543
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;
544
911
  this.config = {
545
912
  baseUrl: config.baseUrl.replace(/\/$/, ""),
546
913
  apiVersion: config.apiVersion || "v1",
@@ -548,10 +915,362 @@ var DistriClient = class {
548
915
  retryAttempts: config.retryAttempts || 3,
549
916
  retryDelay: config.retryDelay || 1e3,
550
917
  debug: config.debug || false,
551
- headers: config.headers || {},
918
+ headers,
552
919
  interceptor: config.interceptor || ((init) => Promise.resolve(init))
553
920
  };
554
- 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();
555
1274
  }
556
1275
  /**
557
1276
  * Get all available agents from the Distri server
@@ -608,14 +1327,18 @@ var DistriClient = class {
608
1327
  * Get or create A2AClient for an agent
609
1328
  */
610
1329
  getA2AClient(agentId) {
611
- 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) {
612
1333
  const fetchFn = this.fetchAbsolute.bind(this);
613
- const agentUrl = `${this.config.baseUrl}/agents/${agentId}`;
614
1334
  const client = new A2AClient(agentUrl, fetchFn);
615
- this.agentClients.set(agentId, client);
616
- 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;
617
1340
  }
618
- return this.agentClients.get(agentId);
1341
+ return existing.client;
619
1342
  }
620
1343
  /**
621
1344
  * Send a message to an agent
@@ -642,6 +1365,7 @@ var DistriClient = class {
642
1365
  * Send a streaming message to an agent
643
1366
  */
644
1367
  async *sendMessageStream(agentId, params) {
1368
+ console.log("sendMessageStream", agentId, params);
645
1369
  try {
646
1370
  const client = this.getA2AClient(agentId);
647
1371
  yield* await client.sendMessageStream(params);
@@ -747,18 +1471,195 @@ var DistriClient = class {
747
1471
  };
748
1472
  await this.sendMessage(threadId, params);
749
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
+ }
750
1518
  /**
751
1519
  * Get the base URL for making direct requests
752
1520
  */
753
1521
  get baseUrl() {
754
1522
  return this.config.baseUrl;
755
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
+ }
756
1631
  /**
757
1632
  * Enhanced fetch with retry logic
758
1633
  */
759
- async fetchAbsolute(url, initialInit) {
1634
+ async fetchAbsolute(url, initialInit, options) {
1635
+ const { skipAuth = false, retryOnAuth = true } = options ?? {};
760
1636
  const init = await this.config.interceptor(initialInit);
761
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
+ }
762
1663
  for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {
763
1664
  try {
764
1665
  const controller = new AbortController();
@@ -766,12 +1667,15 @@ var DistriClient = class {
766
1667
  const response = await fetch(url, {
767
1668
  ...init,
768
1669
  signal: controller.signal,
769
- headers: {
770
- ...this.config.headers,
771
- ...init?.headers
772
- }
1670
+ headers
773
1671
  });
774
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
+ }
775
1679
  return response;
776
1680
  } catch (error) {
777
1681
  lastError = error instanceof Error ? error : new Error(String(error));
@@ -826,7 +1730,7 @@ var DistriClient = class {
826
1730
  id: id || uuidv4(),
827
1731
  role,
828
1732
  parts,
829
- created_at
1733
+ created_at: created_at || (/* @__PURE__ */ new Date()).getTime()
830
1734
  };
831
1735
  }
832
1736
  /**
@@ -856,6 +1760,25 @@ var DistriClient = class {
856
1760
  };
857
1761
  }
858
1762
  };
1763
+ // ============================================================
1764
+ // Additional User Message Parts API
1765
+ // ============================================================
1766
+ // These methods allow external tools to append parts (text, images)
1767
+ // to the user message in the next agent iteration.
1768
+ // The parts are stored under the key "__additional_user_parts".
1769
+ _DistriClient.ADDITIONAL_PARTS_KEY = "__additional_user_parts";
1770
+ // ============================================================
1771
+ // Token API
1772
+ // ============================================================
1773
+ // Issue access + refresh tokens for temporary authentication (e.g., frontend use)
1774
+ /**
1775
+ * Response from the token endpoint
1776
+ */
1777
+ _DistriClient.TokenType = {
1778
+ Main: "main",
1779
+ Short: "short"
1780
+ };
1781
+ var DistriClient = _DistriClient;
859
1782
  function uuidv4() {
860
1783
  if (typeof crypto?.randomUUID === "function") {
861
1784
  return crypto.randomUUID();
@@ -870,42 +1793,32 @@ function uuidv4() {
870
1793
  }
871
1794
 
872
1795
  // src/agent.ts
1796
+ var ExternalToolValidationError = class extends DistriError {
1797
+ constructor(agentName, result) {
1798
+ super(
1799
+ result.message || "Missing required external tools for agent invocation.",
1800
+ "EXTERNAL_TOOL_VALIDATION_ERROR",
1801
+ {
1802
+ agentName,
1803
+ missingTools: result.missingTools,
1804
+ requiredTools: result.requiredTools,
1805
+ providedTools: result.providedTools
1806
+ }
1807
+ );
1808
+ this.name = "ExternalToolValidationError";
1809
+ this.agentName = agentName;
1810
+ this.missingTools = result.missingTools;
1811
+ this.requiredTools = result.requiredTools;
1812
+ this.providedTools = result.providedTools;
1813
+ }
1814
+ };
873
1815
  var Agent = class _Agent {
874
1816
  constructor(agentDefinition, client) {
875
- this.tools = /* @__PURE__ */ new Map();
1817
+ this.hookHandlers = /* @__PURE__ */ new Map();
1818
+ this.defaultHookHandler = null;
876
1819
  this.agentDefinition = agentDefinition;
877
1820
  this.client = client;
878
1821
  }
879
- /**
880
- * Add a tool to the agent (AG-UI style)
881
- */
882
- registerTool(tool) {
883
- this.tools.set(tool.name, tool);
884
- }
885
- /**
886
- * Add multiple tools at once
887
- */
888
- registerTools(tools) {
889
- tools.forEach((tool) => this.registerTool(tool));
890
- }
891
- /**
892
- * Remove a tool
893
- */
894
- unregisterTool(toolName) {
895
- this.tools.delete(toolName);
896
- }
897
- /**
898
- * Get all registered tools
899
- */
900
- getTools() {
901
- return Array.from(this.tools.values());
902
- }
903
- /**
904
- * Check if a tool is registered
905
- */
906
- hasTool(toolName) {
907
- return this.tools.has(toolName);
908
- }
909
1822
  /**
910
1823
  * Get agent information
911
1824
  */
@@ -918,9 +1831,18 @@ var Agent = class _Agent {
918
1831
  get description() {
919
1832
  return this.agentDefinition.description;
920
1833
  }
1834
+ get agentType() {
1835
+ return this.agentDefinition.agent_type ?? this.agentDefinition.agentType;
1836
+ }
921
1837
  get iconUrl() {
922
1838
  return this.agentDefinition.icon_url;
923
1839
  }
1840
+ /**
1841
+ * Get the full agent definition (including backend tools)
1842
+ */
1843
+ getDefinition() {
1844
+ return this.agentDefinition;
1845
+ }
924
1846
  /**
925
1847
  * Fetch messages for a thread (public method for useChat)
926
1848
  */
@@ -930,56 +1852,153 @@ var Agent = class _Agent {
930
1852
  /**
931
1853
  * Direct (non-streaming) invoke
932
1854
  */
933
- async invoke(params) {
934
- const enhancedParams = this.enhanceParamsWithTools(params);
935
- console.log("enhancedParams", enhancedParams);
1855
+ async invoke(params, tools, hooks) {
1856
+ if (hooks) {
1857
+ this.registerHooks(hooks);
1858
+ }
1859
+ const enhancedParams = this.enhanceParamsWithTools(params, tools);
936
1860
  return await this.client.sendMessage(this.agentDefinition.id, enhancedParams);
937
1861
  }
938
1862
  /**
939
1863
  * Streaming invoke
940
1864
  */
941
- async invokeStream(params) {
942
- const enhancedParams = this.enhanceParamsWithTools(params);
943
- console.log("enhancedParams", enhancedParams);
1865
+ async invokeStream(params, tools, hooks) {
1866
+ if (hooks) {
1867
+ this.registerHooks(hooks);
1868
+ }
1869
+ const enhancedParams = this.enhanceParamsWithTools(params, tools);
944
1870
  const a2aStream = this.client.sendMessageStream(this.agentDefinition.id, enhancedParams);
945
- return async function* () {
1871
+ const self = this;
1872
+ return (async function* () {
946
1873
  for await (const event of a2aStream) {
947
- if (event.kind === "message") {
948
- yield convertA2AMessageToDistri(event);
949
- } else if (event.kind === "status-update") {
950
- yield event;
951
- } else if (event.kind === "artifact-update") {
952
- yield event;
953
- } else {
954
- yield event;
1874
+ const converted = decodeA2AStreamEvent(event);
1875
+ if (converted && converted.type === "inline_hook_requested") {
1876
+ const hookReq = converted.data;
1877
+ const handler = self.hookHandlers.get(hookReq.hook) || self.defaultHookHandler;
1878
+ if (handler) {
1879
+ try {
1880
+ const mutation = await handler(hookReq);
1881
+ await self.client.completeInlineHook(hookReq.hook_id, mutation);
1882
+ } catch (err) {
1883
+ await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
1884
+ }
1885
+ } else {
1886
+ await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
1887
+ }
1888
+ yield converted;
1889
+ } else if (converted) {
1890
+ yield converted;
955
1891
  }
956
1892
  }
957
- }();
1893
+ })();
1894
+ }
1895
+ /**
1896
+ * Validate that required external tools are registered before invoking.
1897
+ */
1898
+ validateExternalTools(tools = []) {
1899
+ const requiredTools = this.getRequiredExternalTools();
1900
+ const providedTools = tools.map((tool) => tool.name);
1901
+ if (requiredTools.length === 0) {
1902
+ return {
1903
+ isValid: true,
1904
+ requiredTools: [],
1905
+ providedTools,
1906
+ missingTools: []
1907
+ };
1908
+ }
1909
+ const providedSet = new Set(providedTools);
1910
+ const missingTools = requiredTools.filter((tool) => !providedSet.has(tool));
1911
+ const isValid = missingTools.length === 0;
1912
+ return {
1913
+ isValid,
1914
+ requiredTools,
1915
+ providedTools,
1916
+ missingTools,
1917
+ message: isValid ? void 0 : this.formatExternalToolValidationMessage(requiredTools, missingTools)
1918
+ };
958
1919
  }
959
1920
  /**
960
1921
  * Enhance message params with tool definitions
961
1922
  */
962
- enhanceParamsWithTools(params) {
963
- const tools = this.getTools();
1923
+ enhanceParamsWithTools(params, tools) {
1924
+ this.assertExternalTools(tools);
1925
+ const metadata = {
1926
+ ...params.metadata,
1927
+ external_tools: tools?.map((tool) => ({
1928
+ name: tool.name,
1929
+ description: tool.description,
1930
+ parameters: tool.parameters,
1931
+ is_final: tool.is_final
1932
+ })) || []
1933
+ };
964
1934
  return {
965
1935
  ...params,
966
- metadata: {
967
- ...params.metadata,
968
- tools: tools.map((tool) => ({
969
- name: tool.name,
970
- description: tool.description,
971
- input_schema: tool.input_schema
972
- }))
973
- }
1936
+ metadata
974
1937
  };
975
1938
  }
1939
+ assertExternalTools(tools) {
1940
+ const result = this.validateExternalTools(tools ?? []);
1941
+ if (!result.isValid) {
1942
+ throw new ExternalToolValidationError(this.agentDefinition.name || this.agentDefinition.id, result);
1943
+ }
1944
+ }
1945
+ getRequiredExternalTools() {
1946
+ const toolConfig = this.resolveToolConfig();
1947
+ if (!toolConfig?.external || !Array.isArray(toolConfig.external)) {
1948
+ return [];
1949
+ }
1950
+ return toolConfig.external.filter((tool) => typeof tool === "string" && tool.trim().length > 0);
1951
+ }
1952
+ resolveToolConfig() {
1953
+ const root = this.agentDefinition;
1954
+ return this.extractToolConfig(root) || this.extractToolConfig(root?.agent) || this.extractToolConfig(root?.definition);
1955
+ }
1956
+ extractToolConfig(candidate) {
1957
+ if (!candidate) return null;
1958
+ const tools = candidate.tools;
1959
+ if (!tools || Array.isArray(tools) || typeof tools !== "object") {
1960
+ return null;
1961
+ }
1962
+ return tools;
1963
+ }
1964
+ formatExternalToolValidationMessage(requiredTools, missingTools) {
1965
+ const requiredList = requiredTools.join(", ");
1966
+ const missingList = missingTools.join(", ");
1967
+ 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}.`;
1968
+ }
1969
+ /**
1970
+ * Register multiple hooks at once.
1971
+ */
1972
+ registerHooks(hooks, defaultHandler) {
1973
+ Object.entries(hooks).forEach(([hook, handler]) => {
1974
+ this.hookHandlers.set(hook, handler);
1975
+ });
1976
+ if (defaultHandler) {
1977
+ this.defaultHookHandler = defaultHandler;
1978
+ }
1979
+ }
976
1980
  /**
977
1981
  * Create an agent instance from an agent ID
978
1982
  */
979
- static async create(agentId, client) {
980
- const agentDefinition = await client.getAgent(agentId);
1983
+ static async create(agentIdOrDef, client) {
1984
+ const agentDefinition = typeof agentIdOrDef === "string" ? await client.getAgent(agentIdOrDef) : agentIdOrDef;
1985
+ console.log("\u{1F916} Agent definition loaded:", {
1986
+ id: agentDefinition.id,
1987
+ name: agentDefinition.name,
1988
+ tools: agentDefinition.tools?.map((t) => ({
1989
+ name: t.name,
1990
+ type: t.type || "function"
1991
+ })) || [],
1992
+ toolCount: agentDefinition.tools?.length || 0
1993
+ });
981
1994
  return new _Agent(agentDefinition, client);
982
1995
  }
1996
+ /**
1997
+ * Complete an external tool call by sending the result back to the server
1998
+ */
1999
+ async completeTool(result) {
2000
+ await this.client.completeTool(this.agentDefinition.id, result);
2001
+ }
983
2002
  /**
984
2003
  * List all available agents
985
2004
  */
@@ -990,17 +2009,31 @@ var Agent = class _Agent {
990
2009
  };
991
2010
  // Annotate the CommonJS export names for ESM import in node:
992
2011
  0 && (module.exports = {
2012
+ A2AProtocolError,
993
2013
  Agent,
2014
+ ApiError,
2015
+ ConnectionError,
2016
+ DEFAULT_BASE_URL,
994
2017
  DistriClient,
2018
+ DistriError,
2019
+ ExternalToolValidationError,
995
2020
  convertA2AMessageToDistri,
996
2021
  convertA2APartToDistri,
2022
+ convertA2AStatusUpdateToDistri,
997
2023
  convertDistriMessageToA2A,
998
2024
  convertDistriPartToA2A,
2025
+ createFailedToolResult,
2026
+ createSuccessfulToolResult,
2027
+ decodeA2AStreamEvent,
999
2028
  extractTextFromDistriMessage,
1000
2029
  extractToolCallsFromDistriMessage,
2030
+ extractToolResultData,
1001
2031
  extractToolResultsFromDistriMessage,
2032
+ isArrayParts,
1002
2033
  isDistriEvent,
1003
2034
  isDistriMessage,
2035
+ processA2AMessagesData,
2036
+ processA2AStreamData,
1004
2037
  uuidv4
1005
2038
  });
1006
2039
  //# sourceMappingURL=index.js.map