@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/README.md +60 -1
- package/dist/index.d.mts +680 -273
- package/dist/index.d.ts +680 -273
- package/dist/index.js +1026 -226
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1020 -223
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
484
|
-
|
|
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
|
-
|
|
489
|
-
|
|
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
|
-
|
|
496
|
-
|
|
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
|
-
|
|
503
|
-
|
|
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
|
-
|
|
512
|
-
|
|
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
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
|
|
538
|
-
|
|
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
|
-
|
|
546
|
-
|
|
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
|
-
|
|
554
|
-
|
|
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
|
-
|
|
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
|
-
|
|
735
|
+
const defaultResult = {
|
|
563
736
|
type: "run_started",
|
|
564
|
-
data: {
|
|
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 {
|
|
778
|
+
return { part_type: "text", data: a2aPart.text };
|
|
682
779
|
case "file":
|
|
683
780
|
if ("uri" in a2aPart.file) {
|
|
684
|
-
|
|
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
|
-
|
|
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 {
|
|
790
|
+
return { part_type: "tool_call", data: a2aPart.data };
|
|
692
791
|
case "tool_result":
|
|
693
|
-
return {
|
|
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 {
|
|
794
|
+
return { part_type: "data", data: a2aPart.data };
|
|
700
795
|
}
|
|
701
796
|
default:
|
|
702
|
-
return {
|
|
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
|
-
|
|
826
|
+
let result;
|
|
827
|
+
switch (distriPart.part_type) {
|
|
732
828
|
case "text":
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
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
|
-
|
|
740
|
-
case "tool_result":
|
|
741
|
-
let val = {
|
|
841
|
+
result = {
|
|
742
842
|
kind: "data",
|
|
743
843
|
data: {
|
|
744
|
-
|
|
745
|
-
|
|
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
|
-
|
|
751
|
-
case "
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
918
|
+
headers,
|
|
781
919
|
interceptor: config.interceptor || ((init) => Promise.resolve(init))
|
|
782
920
|
};
|
|
783
|
-
this.debug("DistriClient initialized with 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
|
-
|
|
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(
|
|
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
|
|
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.
|
|
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
|
-
|
|
1164
|
-
|
|
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
|
-
|
|
1172
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2034
|
+
isArrayParts,
|
|
1234
2035
|
isDistriEvent,
|
|
1235
2036
|
isDistriMessage,
|
|
1236
|
-
isDistriPlan,
|
|
1237
2037
|
processA2AMessagesData,
|
|
1238
2038
|
processA2AStreamData,
|
|
1239
2039
|
uuidv4
|