@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.mjs
CHANGED
|
@@ -3,6 +3,90 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
|
|
5
5
|
// src/types.ts
|
|
6
|
+
function isArrayParts(result) {
|
|
7
|
+
return Array.isArray(result) && result[0].part_type;
|
|
8
|
+
}
|
|
9
|
+
function createSuccessfulToolResult(toolCallId, toolName, result) {
|
|
10
|
+
const parts = isArrayParts(result) ? result : [{
|
|
11
|
+
part_type: "data",
|
|
12
|
+
data: {
|
|
13
|
+
result,
|
|
14
|
+
success: true,
|
|
15
|
+
error: void 0
|
|
16
|
+
}
|
|
17
|
+
}];
|
|
18
|
+
return {
|
|
19
|
+
tool_call_id: toolCallId,
|
|
20
|
+
tool_name: toolName,
|
|
21
|
+
parts
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function createFailedToolResult(toolCallId, toolName, error, result) {
|
|
25
|
+
return {
|
|
26
|
+
tool_call_id: toolCallId,
|
|
27
|
+
tool_name: toolName,
|
|
28
|
+
parts: [{
|
|
29
|
+
part_type: "data",
|
|
30
|
+
data: {
|
|
31
|
+
result: result ?? `Tool execution failed: ${error}`,
|
|
32
|
+
success: false,
|
|
33
|
+
error
|
|
34
|
+
}
|
|
35
|
+
}]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function isDataPart(part) {
|
|
39
|
+
return typeof part === "object" && part !== null && "part_type" in part && part.part_type === "data" && "data" in part;
|
|
40
|
+
}
|
|
41
|
+
function isToolResultData(data) {
|
|
42
|
+
return typeof data === "object" && data !== null && "success" in data && typeof data.success === "boolean";
|
|
43
|
+
}
|
|
44
|
+
function extractToolResultData(toolResult) {
|
|
45
|
+
if (!toolResult.parts || !Array.isArray(toolResult.parts) || toolResult.parts.length === 0) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const firstPart = toolResult.parts[0];
|
|
49
|
+
if (isDataPart(firstPart)) {
|
|
50
|
+
const data = firstPart.data;
|
|
51
|
+
if (isToolResultData(data)) {
|
|
52
|
+
return {
|
|
53
|
+
result: data.result,
|
|
54
|
+
success: data.success,
|
|
55
|
+
error: data.error
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (typeof data === "string") {
|
|
59
|
+
try {
|
|
60
|
+
const parsed = JSON.parse(data);
|
|
61
|
+
if (isToolResultData(parsed)) {
|
|
62
|
+
return {
|
|
63
|
+
result: parsed.result,
|
|
64
|
+
success: parsed.success,
|
|
65
|
+
error: parsed.error
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
result: parsed,
|
|
70
|
+
success: true,
|
|
71
|
+
error: void 0
|
|
72
|
+
};
|
|
73
|
+
} catch {
|
|
74
|
+
return {
|
|
75
|
+
result: data,
|
|
76
|
+
success: true,
|
|
77
|
+
error: void 0
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
result: data,
|
|
83
|
+
success: true,
|
|
84
|
+
error: void 0
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
var DEFAULT_BASE_URL = "https://api.distri.dev";
|
|
6
90
|
var DistriError = class extends Error {
|
|
7
91
|
constructor(message, code, details) {
|
|
8
92
|
super(message);
|
|
@@ -36,14 +120,8 @@ function isDistriMessage(event) {
|
|
|
36
120
|
function isDistriEvent(event) {
|
|
37
121
|
return "type" in event && "data" in event;
|
|
38
122
|
}
|
|
39
|
-
function isDistriPlan(event) {
|
|
40
|
-
return "steps" in event && Array.isArray(event.steps) && "reasoning" in event;
|
|
41
|
-
}
|
|
42
|
-
function isDistriArtifact(event) {
|
|
43
|
-
return "type" in event && "timestamp" in event && "id" in event;
|
|
44
|
-
}
|
|
45
123
|
|
|
46
|
-
//
|
|
124
|
+
// ../../../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
|
|
47
125
|
var A2AClient = class {
|
|
48
126
|
/**
|
|
49
127
|
* Constructs an A2AClient instance.
|
|
@@ -429,32 +507,56 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
|
|
|
429
507
|
}
|
|
430
508
|
const metadata = statusUpdate.metadata;
|
|
431
509
|
switch (metadata.type) {
|
|
432
|
-
case "run_started":
|
|
433
|
-
|
|
510
|
+
case "run_started": {
|
|
511
|
+
const runStartedResult = {
|
|
434
512
|
type: "run_started",
|
|
435
|
-
data: {
|
|
513
|
+
data: {
|
|
514
|
+
runId: statusUpdate.runId,
|
|
515
|
+
taskId: statusUpdate.taskId
|
|
516
|
+
}
|
|
436
517
|
};
|
|
437
|
-
|
|
438
|
-
|
|
518
|
+
return runStartedResult;
|
|
519
|
+
}
|
|
520
|
+
case "run_error": {
|
|
521
|
+
const runErrorResult = {
|
|
522
|
+
type: "run_error",
|
|
523
|
+
data: {
|
|
524
|
+
message: statusUpdate.error,
|
|
525
|
+
code: statusUpdate.code
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
return runErrorResult;
|
|
529
|
+
}
|
|
530
|
+
case "run_finished": {
|
|
531
|
+
const runFinishedResult = {
|
|
439
532
|
type: "run_finished",
|
|
440
|
-
data: {
|
|
533
|
+
data: {
|
|
534
|
+
runId: statusUpdate.runId,
|
|
535
|
+
taskId: statusUpdate.taskId
|
|
536
|
+
}
|
|
441
537
|
};
|
|
442
|
-
|
|
443
|
-
|
|
538
|
+
return runFinishedResult;
|
|
539
|
+
}
|
|
540
|
+
case "plan_started": {
|
|
541
|
+
const planStartedResult = {
|
|
444
542
|
type: "plan_started",
|
|
445
543
|
data: {
|
|
446
544
|
initial_plan: metadata.initial_plan
|
|
447
545
|
}
|
|
448
546
|
};
|
|
449
|
-
|
|
450
|
-
|
|
547
|
+
return planStartedResult;
|
|
548
|
+
}
|
|
549
|
+
case "plan_finished": {
|
|
550
|
+
const planFinishedResult = {
|
|
451
551
|
type: "plan_finished",
|
|
452
552
|
data: {
|
|
453
553
|
total_steps: metadata.total_steps
|
|
454
554
|
}
|
|
455
555
|
};
|
|
456
|
-
|
|
457
|
-
|
|
556
|
+
return planFinishedResult;
|
|
557
|
+
}
|
|
558
|
+
case "step_started": {
|
|
559
|
+
const stepStartedResult = {
|
|
458
560
|
type: "step_started",
|
|
459
561
|
data: {
|
|
460
562
|
step_id: metadata.step_id,
|
|
@@ -462,8 +564,10 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
|
|
|
462
564
|
step_index: metadata.step_index || 0
|
|
463
565
|
}
|
|
464
566
|
};
|
|
465
|
-
|
|
466
|
-
|
|
567
|
+
return stepStartedResult;
|
|
568
|
+
}
|
|
569
|
+
case "step_completed": {
|
|
570
|
+
const stepCompletedResult = {
|
|
467
571
|
type: "step_completed",
|
|
468
572
|
data: {
|
|
469
573
|
step_id: metadata.step_id,
|
|
@@ -471,142 +575,132 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
|
|
|
471
575
|
step_index: metadata.step_index || 0
|
|
472
576
|
}
|
|
473
577
|
};
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
578
|
+
return stepCompletedResult;
|
|
579
|
+
}
|
|
580
|
+
case "tool_execution_start": {
|
|
581
|
+
const toolStartResult = {
|
|
582
|
+
type: "tool_execution_start",
|
|
477
583
|
data: {
|
|
478
584
|
tool_call_id: metadata.tool_call_id,
|
|
479
585
|
tool_call_name: metadata.tool_call_name || "Tool",
|
|
480
|
-
parent_message_id: statusUpdate.taskId
|
|
481
|
-
is_external: true
|
|
586
|
+
parent_message_id: statusUpdate.taskId
|
|
482
587
|
}
|
|
483
588
|
};
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
589
|
+
return toolStartResult;
|
|
590
|
+
}
|
|
591
|
+
case "tool_execution_end": {
|
|
592
|
+
const toolEndResult = {
|
|
593
|
+
type: "tool_execution_end",
|
|
487
594
|
data: {
|
|
488
595
|
tool_call_id: metadata.tool_call_id
|
|
489
596
|
}
|
|
490
597
|
};
|
|
491
|
-
|
|
492
|
-
|
|
598
|
+
return toolEndResult;
|
|
599
|
+
}
|
|
600
|
+
case "text_message_start": {
|
|
601
|
+
const textStartResult = {
|
|
493
602
|
type: "text_message_start",
|
|
494
603
|
data: {
|
|
495
604
|
message_id: metadata.message_id,
|
|
605
|
+
step_id: metadata.step_id || "",
|
|
496
606
|
role: metadata.role === "assistant" ? "assistant" : "user"
|
|
497
607
|
}
|
|
498
608
|
};
|
|
499
|
-
|
|
500
|
-
|
|
609
|
+
return textStartResult;
|
|
610
|
+
}
|
|
611
|
+
case "text_message_content": {
|
|
612
|
+
const textContentResult = {
|
|
501
613
|
type: "text_message_content",
|
|
502
614
|
data: {
|
|
503
615
|
message_id: metadata.message_id,
|
|
616
|
+
step_id: metadata.step_id || "",
|
|
504
617
|
delta: metadata.delta || ""
|
|
505
618
|
}
|
|
506
619
|
};
|
|
507
|
-
|
|
508
|
-
|
|
620
|
+
return textContentResult;
|
|
621
|
+
}
|
|
622
|
+
case "text_message_end": {
|
|
623
|
+
const textEndResult = {
|
|
509
624
|
type: "text_message_end",
|
|
510
625
|
data: {
|
|
511
|
-
message_id: metadata.message_id
|
|
626
|
+
message_id: metadata.message_id,
|
|
627
|
+
step_id: metadata.step_id || ""
|
|
512
628
|
}
|
|
513
629
|
};
|
|
514
|
-
|
|
630
|
+
return textEndResult;
|
|
631
|
+
}
|
|
632
|
+
case "tool_calls": {
|
|
633
|
+
const toolCallsResult = {
|
|
634
|
+
type: "tool_calls",
|
|
635
|
+
data: {
|
|
636
|
+
tool_calls: metadata.tool_calls || []
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
return toolCallsResult;
|
|
640
|
+
}
|
|
641
|
+
case "tool_results": {
|
|
642
|
+
const toolResultsResult = {
|
|
643
|
+
type: "tool_results",
|
|
644
|
+
data: {
|
|
645
|
+
results: metadata.results || []
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
return toolResultsResult;
|
|
649
|
+
}
|
|
650
|
+
case "browser_screenshot": {
|
|
651
|
+
const browserScreenshotResult = {
|
|
652
|
+
type: "browser_screenshot",
|
|
653
|
+
data: {
|
|
654
|
+
image: metadata.image || "",
|
|
655
|
+
format: metadata.format,
|
|
656
|
+
filename: metadata.filename,
|
|
657
|
+
size: metadata.size,
|
|
658
|
+
timestamp_ms: metadata.timestamp_ms
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
return browserScreenshotResult;
|
|
662
|
+
}
|
|
663
|
+
case "inline_hook_requested": {
|
|
664
|
+
const hookRequested = {
|
|
665
|
+
type: "inline_hook_requested",
|
|
666
|
+
data: {
|
|
667
|
+
hook_id: metadata.request?.hook_id || metadata.hook_id || "",
|
|
668
|
+
hook: metadata.request?.hook || metadata.hook || "",
|
|
669
|
+
context: metadata.request?.context || metadata.context || {
|
|
670
|
+
agent_id: statusUpdate.agentId,
|
|
671
|
+
thread_id: statusUpdate.contextId,
|
|
672
|
+
task_id: statusUpdate.taskId,
|
|
673
|
+
run_id: statusUpdate.agentId
|
|
674
|
+
},
|
|
675
|
+
timeout_ms: metadata.request?.timeout_ms || metadata.timeout_ms,
|
|
676
|
+
fire_and_forget: metadata.request?.fire_and_forget ?? metadata.fire_and_forget,
|
|
677
|
+
message: metadata.request?.message || metadata.message,
|
|
678
|
+
plan: metadata.request?.plan || metadata.plan,
|
|
679
|
+
result: metadata.request?.result || metadata.result
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
return hookRequested;
|
|
683
|
+
}
|
|
684
|
+
default: {
|
|
515
685
|
console.warn(`Unhandled status update metadata type: ${metadata.type}`, metadata);
|
|
516
|
-
|
|
686
|
+
const defaultResult = {
|
|
517
687
|
type: "run_started",
|
|
518
|
-
data: {
|
|
688
|
+
data: {
|
|
689
|
+
runId: statusUpdate.runId,
|
|
690
|
+
taskId: statusUpdate.taskId
|
|
691
|
+
}
|
|
519
692
|
};
|
|
693
|
+
return defaultResult;
|
|
694
|
+
}
|
|
520
695
|
}
|
|
521
696
|
}
|
|
522
|
-
function convertA2AArtifactToDistri(artifact) {
|
|
523
|
-
if (!artifact || !artifact.parts || !Array.isArray(artifact.parts)) {
|
|
524
|
-
return null;
|
|
525
|
-
}
|
|
526
|
-
const part = artifact.parts[0];
|
|
527
|
-
if (!part || part.kind !== "data" || !part.data) {
|
|
528
|
-
return null;
|
|
529
|
-
}
|
|
530
|
-
const data = part.data;
|
|
531
|
-
if (data.type === "llm_response") {
|
|
532
|
-
const hasContent = data.content && data.content.trim() !== "";
|
|
533
|
-
const hasToolCalls = data.tool_calls && Array.isArray(data.tool_calls) && data.tool_calls.length > 0;
|
|
534
|
-
const isExternal = data.is_external;
|
|
535
|
-
if (hasToolCalls) {
|
|
536
|
-
const executionResult2 = {
|
|
537
|
-
id: data.id || artifact.artifactId,
|
|
538
|
-
type: "llm_response",
|
|
539
|
-
timestamp: data.timestamp || data.created_at || Date.now(),
|
|
540
|
-
content: data.content?.trim() || "",
|
|
541
|
-
tool_calls: data.tool_calls,
|
|
542
|
-
step_id: data.step_id,
|
|
543
|
-
success: data.success,
|
|
544
|
-
rejected: data.rejected,
|
|
545
|
-
reason: data.reason,
|
|
546
|
-
is_external: isExternal
|
|
547
|
-
};
|
|
548
|
-
return executionResult2;
|
|
549
|
-
} else {
|
|
550
|
-
const parts = [];
|
|
551
|
-
if (hasContent) {
|
|
552
|
-
parts.push({ type: "text", text: data.content });
|
|
553
|
-
}
|
|
554
|
-
const distriMessage = {
|
|
555
|
-
id: artifact.artifactId,
|
|
556
|
-
role: "assistant",
|
|
557
|
-
parts,
|
|
558
|
-
created_at: data.timestamp || data.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
559
|
-
};
|
|
560
|
-
return distriMessage;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
if (data.type === "tool_results") {
|
|
564
|
-
const executionResult2 = {
|
|
565
|
-
id: data.id || artifact.artifactId,
|
|
566
|
-
type: "tool_results",
|
|
567
|
-
timestamp: data.timestamp || data.created_at || Date.now(),
|
|
568
|
-
results: data.results || [],
|
|
569
|
-
step_id: data.step_id,
|
|
570
|
-
success: data.success,
|
|
571
|
-
rejected: data.rejected,
|
|
572
|
-
reason: data.reason
|
|
573
|
-
};
|
|
574
|
-
return executionResult2;
|
|
575
|
-
}
|
|
576
|
-
if (data.type === "plan") {
|
|
577
|
-
const planResult = {
|
|
578
|
-
id: data.id || artifact.artifactId,
|
|
579
|
-
type: "plan",
|
|
580
|
-
timestamp: data.timestamp || data.created_at || Date.now(),
|
|
581
|
-
reasoning: data.reasoning || "",
|
|
582
|
-
steps: data.steps || []
|
|
583
|
-
};
|
|
584
|
-
return planResult;
|
|
585
|
-
}
|
|
586
|
-
const executionResult = {
|
|
587
|
-
id: artifact.artifactId,
|
|
588
|
-
type: "artifact",
|
|
589
|
-
timestamp: Date.now(),
|
|
590
|
-
data,
|
|
591
|
-
artifactId: artifact.artifactId,
|
|
592
|
-
name: artifact.name || "",
|
|
593
|
-
description: artifact.description || null
|
|
594
|
-
};
|
|
595
|
-
return executionResult;
|
|
596
|
-
}
|
|
597
697
|
function decodeA2AStreamEvent(event) {
|
|
598
|
-
if (event.artifactId && event.parts) {
|
|
599
|
-
return convertA2AArtifactToDistri(event);
|
|
600
|
-
}
|
|
601
698
|
if (event.kind === "message") {
|
|
602
699
|
return convertA2AMessageToDistri(event);
|
|
603
700
|
}
|
|
604
701
|
if (event.kind === "status-update") {
|
|
605
702
|
return convertA2AStatusUpdateToDistri(event);
|
|
606
703
|
}
|
|
607
|
-
if (event.kind === "artifact-update") {
|
|
608
|
-
return convertA2AArtifactToDistri(event);
|
|
609
|
-
}
|
|
610
704
|
return null;
|
|
611
705
|
}
|
|
612
706
|
function processA2AStreamData(streamData) {
|
|
@@ -632,28 +726,26 @@ function processA2AMessagesData(data) {
|
|
|
632
726
|
function convertA2APartToDistri(a2aPart) {
|
|
633
727
|
switch (a2aPart.kind) {
|
|
634
728
|
case "text":
|
|
635
|
-
return {
|
|
729
|
+
return { part_type: "text", data: a2aPart.text };
|
|
636
730
|
case "file":
|
|
637
731
|
if ("uri" in a2aPart.file) {
|
|
638
|
-
|
|
732
|
+
const fileUrl = { type: "url", mime_type: a2aPart.file.mimeType || "application/octet-stream", url: a2aPart.file.uri || "" };
|
|
733
|
+
return { part_type: "image", data: fileUrl };
|
|
639
734
|
} else {
|
|
640
|
-
|
|
735
|
+
const fileBytes = { type: "bytes", mime_type: a2aPart.file.mimeType || "application/octet-stream", data: a2aPart.file.bytes || "" };
|
|
736
|
+
return { part_type: "image", data: fileBytes };
|
|
641
737
|
}
|
|
642
738
|
case "data":
|
|
643
739
|
switch (a2aPart.data.part_type) {
|
|
644
740
|
case "tool_call":
|
|
645
|
-
return {
|
|
741
|
+
return { part_type: "tool_call", data: a2aPart.data };
|
|
646
742
|
case "tool_result":
|
|
647
|
-
return {
|
|
648
|
-
case "code_observation":
|
|
649
|
-
return { type: "code_observation", thought: a2aPart.data.thought, code: a2aPart.data.code };
|
|
650
|
-
case "plan":
|
|
651
|
-
return { type: "plan", plan: a2aPart.data.plan };
|
|
743
|
+
return { part_type: "tool_result", data: a2aPart.data };
|
|
652
744
|
default:
|
|
653
|
-
return {
|
|
745
|
+
return { part_type: "data", data: a2aPart.data };
|
|
654
746
|
}
|
|
655
747
|
default:
|
|
656
|
-
return {
|
|
748
|
+
return { part_type: "text", data: JSON.stringify(a2aPart) };
|
|
657
749
|
}
|
|
658
750
|
}
|
|
659
751
|
function convertDistriMessageToA2A(distriMessage, context) {
|
|
@@ -678,52 +770,95 @@ function convertDistriMessageToA2A(distriMessage, context) {
|
|
|
678
770
|
parts: distriMessage.parts.map(convertDistriPartToA2A),
|
|
679
771
|
kind: "message",
|
|
680
772
|
contextId: context.thread_id,
|
|
681
|
-
taskId: context.run_id
|
|
773
|
+
taskId: context.task_id || context.run_id || void 0
|
|
682
774
|
};
|
|
683
775
|
}
|
|
684
776
|
function convertDistriPartToA2A(distriPart) {
|
|
685
|
-
|
|
777
|
+
let result;
|
|
778
|
+
switch (distriPart.part_type) {
|
|
686
779
|
case "text":
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
780
|
+
result = { kind: "text", text: distriPart.data };
|
|
781
|
+
break;
|
|
782
|
+
case "image":
|
|
783
|
+
if ("url" in distriPart.data) {
|
|
784
|
+
const fileUri = { mimeType: distriPart.data.mime_type, uri: distriPart.data.url };
|
|
785
|
+
result = { kind: "file", file: fileUri };
|
|
786
|
+
} else {
|
|
787
|
+
const fileBytes = { mimeType: distriPart.data.mime_type, bytes: distriPart.data.data };
|
|
788
|
+
result = { kind: "file", file: fileBytes };
|
|
789
|
+
}
|
|
790
|
+
break;
|
|
692
791
|
case "tool_call":
|
|
693
|
-
|
|
694
|
-
case "tool_result":
|
|
695
|
-
let val = {
|
|
792
|
+
result = {
|
|
696
793
|
kind: "data",
|
|
697
794
|
data: {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
result: distriPart.tool_result.result,
|
|
701
|
-
part_type: "tool_result"
|
|
795
|
+
part_type: "tool_call",
|
|
796
|
+
data: distriPart.data
|
|
702
797
|
}
|
|
703
798
|
};
|
|
704
|
-
|
|
705
|
-
case "
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
799
|
+
break;
|
|
800
|
+
case "tool_result": {
|
|
801
|
+
const toolResult = distriPart.data;
|
|
802
|
+
const parts = toolResult.parts.map((part) => {
|
|
803
|
+
if ("type" in part && part.type === "data") {
|
|
804
|
+
return {
|
|
805
|
+
part_type: "data",
|
|
806
|
+
data: part.data
|
|
807
|
+
};
|
|
808
|
+
} else if ("part_type" in part) {
|
|
809
|
+
return part;
|
|
810
|
+
} else {
|
|
811
|
+
return {
|
|
812
|
+
part_type: "data",
|
|
813
|
+
data: part
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
result = {
|
|
818
|
+
kind: "data",
|
|
819
|
+
data: {
|
|
820
|
+
part_type: "tool_result",
|
|
821
|
+
data: {
|
|
822
|
+
tool_call_id: toolResult.tool_call_id,
|
|
823
|
+
tool_name: toolResult.tool_name,
|
|
824
|
+
parts
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
case "data": {
|
|
831
|
+
const dataValue = distriPart.data;
|
|
832
|
+
if (dataValue === null || typeof dataValue !== "object" || Array.isArray(dataValue)) {
|
|
833
|
+
result = { kind: "data", data: { value: dataValue } };
|
|
834
|
+
} else {
|
|
835
|
+
const dataObj = dataValue;
|
|
836
|
+
result = { kind: "data", data: dataObj };
|
|
837
|
+
}
|
|
838
|
+
break;
|
|
839
|
+
}
|
|
711
840
|
}
|
|
841
|
+
return result;
|
|
712
842
|
}
|
|
713
843
|
function extractTextFromDistriMessage(message) {
|
|
714
|
-
return message.parts.filter((part) => part.
|
|
844
|
+
return message.parts.filter((part) => part.part_type === "text").map((part) => part.data).join("\n");
|
|
715
845
|
}
|
|
716
846
|
function extractToolCallsFromDistriMessage(message) {
|
|
717
|
-
return message.parts.filter((part) => part.
|
|
847
|
+
return message.parts.filter((part) => part.part_type === "tool_call").map((part) => part.data);
|
|
718
848
|
}
|
|
719
849
|
function extractToolResultsFromDistriMessage(message) {
|
|
720
|
-
return message.parts.filter((part) => part.
|
|
850
|
+
return message.parts.filter((part) => part.part_type === "tool_result").map((part) => part.data);
|
|
721
851
|
}
|
|
722
852
|
|
|
723
853
|
// src/distri-client.ts
|
|
724
|
-
var
|
|
854
|
+
var _DistriClient = class _DistriClient {
|
|
725
855
|
constructor(config) {
|
|
726
856
|
this.agentClients = /* @__PURE__ */ new Map();
|
|
857
|
+
const headers = { ...config.headers };
|
|
858
|
+
this.accessToken = config.accessToken;
|
|
859
|
+
this.refreshToken = config.refreshToken;
|
|
860
|
+
this.tokenRefreshSkewMs = config.tokenRefreshSkewMs ?? 6e4;
|
|
861
|
+
this.onTokenRefresh = config.onTokenRefresh;
|
|
727
862
|
this.config = {
|
|
728
863
|
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
729
864
|
apiVersion: config.apiVersion || "v1",
|
|
@@ -731,10 +866,362 @@ var DistriClient = class {
|
|
|
731
866
|
retryAttempts: config.retryAttempts || 3,
|
|
732
867
|
retryDelay: config.retryDelay || 1e3,
|
|
733
868
|
debug: config.debug || false,
|
|
734
|
-
headers
|
|
869
|
+
headers,
|
|
735
870
|
interceptor: config.interceptor || ((init) => Promise.resolve(init))
|
|
736
871
|
};
|
|
737
|
-
this.debug("DistriClient initialized with config:",
|
|
872
|
+
this.debug("DistriClient initialized with config:", {
|
|
873
|
+
baseUrl: this.config.baseUrl,
|
|
874
|
+
hasAccessToken: !!this.accessToken,
|
|
875
|
+
hasRefreshToken: !!this.refreshToken,
|
|
876
|
+
timeout: this.config.timeout
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Create a client with default cloud configuration.
|
|
881
|
+
*
|
|
882
|
+
* @param overrides - Optional overrides for the default config
|
|
883
|
+
*/
|
|
884
|
+
static create(overrides = {}) {
|
|
885
|
+
return new _DistriClient({
|
|
886
|
+
baseUrl: DEFAULT_BASE_URL,
|
|
887
|
+
...overrides
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Check if this client has authentication configured.
|
|
892
|
+
*/
|
|
893
|
+
hasAuth() {
|
|
894
|
+
return !!this.accessToken || !!this.refreshToken;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Check if this client is configured for local development.
|
|
898
|
+
*/
|
|
899
|
+
isLocal() {
|
|
900
|
+
return this.config.baseUrl.includes("localhost") || this.config.baseUrl.includes("127.0.0.1");
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Session store: set a value (optionally with expiry)
|
|
904
|
+
*/
|
|
905
|
+
async setSessionValue(sessionId, key, value, expiry) {
|
|
906
|
+
const body = { key, value };
|
|
907
|
+
if (expiry) {
|
|
908
|
+
body.expiry = typeof expiry === "string" ? expiry : expiry.toISOString();
|
|
909
|
+
}
|
|
910
|
+
const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values`, {
|
|
911
|
+
method: "POST",
|
|
912
|
+
headers: {
|
|
913
|
+
"Content-Type": "application/json",
|
|
914
|
+
...this.config.headers
|
|
915
|
+
},
|
|
916
|
+
body: JSON.stringify(body)
|
|
917
|
+
});
|
|
918
|
+
if (!resp.ok && resp.status !== 204) {
|
|
919
|
+
const errorData = await resp.json().catch(() => ({}));
|
|
920
|
+
throw new ApiError(errorData.error || "Failed to set session value", resp.status);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Session store: get a single value
|
|
925
|
+
*/
|
|
926
|
+
async getSessionValue(sessionId, key) {
|
|
927
|
+
const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
|
|
928
|
+
method: "GET",
|
|
929
|
+
headers: {
|
|
930
|
+
...this.config.headers
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
if (!resp.ok) {
|
|
934
|
+
const errorData = await resp.json().catch(() => ({}));
|
|
935
|
+
throw new ApiError(errorData.error || "Failed to get session value", resp.status);
|
|
936
|
+
}
|
|
937
|
+
const data = await resp.json().catch(() => ({ value: null }));
|
|
938
|
+
return data?.value ?? null;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Session store: get all values in a session
|
|
942
|
+
*/
|
|
943
|
+
async getSessionValues(sessionId) {
|
|
944
|
+
const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values`, {
|
|
945
|
+
method: "GET",
|
|
946
|
+
headers: {
|
|
947
|
+
...this.config.headers
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
if (!resp.ok) {
|
|
951
|
+
const errorData = await resp.json().catch(() => ({}));
|
|
952
|
+
throw new ApiError(errorData.error || "Failed to get session values", resp.status);
|
|
953
|
+
}
|
|
954
|
+
const data = await resp.json().catch(() => ({ values: {} }));
|
|
955
|
+
return data?.values ?? {};
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Session store: delete a single key
|
|
959
|
+
*/
|
|
960
|
+
async deleteSessionValue(sessionId, key) {
|
|
961
|
+
const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
|
|
962
|
+
method: "DELETE",
|
|
963
|
+
headers: {
|
|
964
|
+
...this.config.headers
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
if (!resp.ok && resp.status !== 204) {
|
|
968
|
+
const errorData = await resp.json().catch(() => ({}));
|
|
969
|
+
throw new ApiError(errorData.error || "Failed to delete session value", resp.status);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Session store: clear all keys in a session
|
|
974
|
+
*/
|
|
975
|
+
async clearSession(sessionId) {
|
|
976
|
+
const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}`, {
|
|
977
|
+
method: "DELETE",
|
|
978
|
+
headers: {
|
|
979
|
+
...this.config.headers
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
if (!resp.ok && resp.status !== 204) {
|
|
983
|
+
const errorData = await resp.json().catch(() => ({}));
|
|
984
|
+
throw new ApiError(errorData.error || "Failed to clear session", resp.status);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Set additional user message parts for the next agent iteration.
|
|
989
|
+
* These parts will be appended to the user message in the prompt.
|
|
990
|
+
* @param sessionId - The thread/session ID
|
|
991
|
+
* @param parts - Array of DistriPart objects to append to user message
|
|
992
|
+
*/
|
|
993
|
+
async setAdditionalUserParts(sessionId, parts) {
|
|
994
|
+
await this.setSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY, parts);
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Get the current additional user message parts.
|
|
998
|
+
* @param sessionId - The thread/session ID
|
|
999
|
+
* @returns Array of DistriPart objects or null if not set
|
|
1000
|
+
*/
|
|
1001
|
+
async getAdditionalUserParts(sessionId) {
|
|
1002
|
+
return this.getSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY);
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Clear/delete the additional user message parts.
|
|
1006
|
+
* @param sessionId - The thread/session ID
|
|
1007
|
+
*/
|
|
1008
|
+
async clearAdditionalUserParts(sessionId) {
|
|
1009
|
+
await this.deleteSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY);
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Issue an access token + refresh token for temporary authentication.
|
|
1013
|
+
* Requires an existing authenticated session (bearer token).
|
|
1014
|
+
*
|
|
1015
|
+
* @returns Token response with access/refresh token strings
|
|
1016
|
+
* @throws ApiError if not authenticated or token issuance fails
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```typescript
|
|
1020
|
+
* const { access_token, refresh_token } = await client.issueToken();
|
|
1021
|
+
* // Persist the refresh token and use access_token for requests
|
|
1022
|
+
* ```
|
|
1023
|
+
*/
|
|
1024
|
+
async issueToken() {
|
|
1025
|
+
const response = await this.fetch("/token", {
|
|
1026
|
+
method: "POST",
|
|
1027
|
+
headers: {
|
|
1028
|
+
"Content-Type": "application/json",
|
|
1029
|
+
...this.config.headers
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
if (!response.ok) {
|
|
1033
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1034
|
+
throw new ApiError(errorData.error || "Failed to issue token", response.status);
|
|
1035
|
+
}
|
|
1036
|
+
const tokens = await response.json();
|
|
1037
|
+
if (!tokens?.access_token || !tokens?.refresh_token || typeof tokens?.expires_at !== "number") {
|
|
1038
|
+
throw new ApiError("Invalid token response", response.status);
|
|
1039
|
+
}
|
|
1040
|
+
this.applyTokens(tokens.access_token, tokens.refresh_token, false);
|
|
1041
|
+
return tokens;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Get the current access/refresh tokens.
|
|
1045
|
+
*/
|
|
1046
|
+
getTokens() {
|
|
1047
|
+
return { accessToken: this.accessToken, refreshToken: this.refreshToken };
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Update the access/refresh tokens in memory.
|
|
1051
|
+
*/
|
|
1052
|
+
setTokens(tokens) {
|
|
1053
|
+
if (tokens.accessToken !== void 0) {
|
|
1054
|
+
this.accessToken = tokens.accessToken;
|
|
1055
|
+
}
|
|
1056
|
+
if (tokens.refreshToken !== void 0) {
|
|
1057
|
+
this.refreshToken = tokens.refreshToken;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Start streaming speech-to-text transcription via WebSocket
|
|
1062
|
+
*/
|
|
1063
|
+
async streamingTranscription(options = {}) {
|
|
1064
|
+
const baseUrl = this.config.baseUrl;
|
|
1065
|
+
const wsUrl = baseUrl.replace("http://", "ws://").replace("https://", "wss://") + "/voice/stream";
|
|
1066
|
+
return new Promise((resolve, reject) => {
|
|
1067
|
+
const ws = new WebSocket(wsUrl);
|
|
1068
|
+
let isResolved = false;
|
|
1069
|
+
ws.onopen = () => {
|
|
1070
|
+
ws.send(JSON.stringify({ type: "start_session" }));
|
|
1071
|
+
options.onStart?.();
|
|
1072
|
+
if (!isResolved) {
|
|
1073
|
+
isResolved = true;
|
|
1074
|
+
resolve({
|
|
1075
|
+
sendAudio: (audioData) => {
|
|
1076
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
1077
|
+
ws.send(audioData);
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
sendText: (text) => {
|
|
1081
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
1082
|
+
ws.send(JSON.stringify({ type: "text_chunk", text }));
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
stop: () => {
|
|
1086
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
1087
|
+
ws.send(JSON.stringify({ type: "end_session" }));
|
|
1088
|
+
}
|
|
1089
|
+
},
|
|
1090
|
+
close: () => {
|
|
1091
|
+
ws.close();
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
ws.onmessage = (event) => {
|
|
1097
|
+
try {
|
|
1098
|
+
const data = JSON.parse(event.data);
|
|
1099
|
+
switch (data.type) {
|
|
1100
|
+
case "text_chunk":
|
|
1101
|
+
options.onTranscript?.(data.text || "", data.is_final || false);
|
|
1102
|
+
break;
|
|
1103
|
+
case "session_started":
|
|
1104
|
+
this.debug("Speech-to-text session started");
|
|
1105
|
+
break;
|
|
1106
|
+
case "session_ended":
|
|
1107
|
+
this.debug("Speech-to-text session ended");
|
|
1108
|
+
options.onEnd?.();
|
|
1109
|
+
break;
|
|
1110
|
+
case "error": {
|
|
1111
|
+
const error = new Error(data.message || "WebSocket error");
|
|
1112
|
+
this.debug("Speech-to-text error:", error);
|
|
1113
|
+
options.onError?.(error);
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
default:
|
|
1117
|
+
this.debug("Unknown message type:", data.type);
|
|
1118
|
+
}
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
const parseError = new Error("Failed to parse WebSocket message");
|
|
1121
|
+
this.debug("Parse error:", parseError);
|
|
1122
|
+
options.onError?.(parseError);
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
ws.onerror = (event) => {
|
|
1126
|
+
const error = new Error("WebSocket connection error");
|
|
1127
|
+
this.debug("WebSocket error:", event);
|
|
1128
|
+
options.onError?.(error);
|
|
1129
|
+
if (!isResolved) {
|
|
1130
|
+
isResolved = true;
|
|
1131
|
+
reject(error);
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
ws.onclose = (event) => {
|
|
1135
|
+
this.debug("WebSocket closed:", event.code, event.reason);
|
|
1136
|
+
options.onEnd?.();
|
|
1137
|
+
};
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Transcribe audio blob to text using speech-to-text API
|
|
1142
|
+
*/
|
|
1143
|
+
async transcribe(audioBlob, config = {}) {
|
|
1144
|
+
try {
|
|
1145
|
+
const arrayBuffer = await audioBlob.arrayBuffer();
|
|
1146
|
+
const uint8Array = new Uint8Array(arrayBuffer);
|
|
1147
|
+
const base64String = btoa(String.fromCharCode(...uint8Array));
|
|
1148
|
+
const requestBody = {
|
|
1149
|
+
audio: base64String,
|
|
1150
|
+
model: config.model || "whisper-1",
|
|
1151
|
+
...config.language && { language: config.language },
|
|
1152
|
+
...config.temperature !== void 0 && { temperature: config.temperature }
|
|
1153
|
+
};
|
|
1154
|
+
this.debug("Transcribing audio:", {
|
|
1155
|
+
model: requestBody.model,
|
|
1156
|
+
language: config.language,
|
|
1157
|
+
audioSize: audioBlob.size
|
|
1158
|
+
});
|
|
1159
|
+
const response = await this.fetch(`/tts/transcribe`, {
|
|
1160
|
+
method: "POST",
|
|
1161
|
+
headers: {
|
|
1162
|
+
"Content-Type": "application/json",
|
|
1163
|
+
...this.config.headers
|
|
1164
|
+
},
|
|
1165
|
+
body: JSON.stringify(requestBody)
|
|
1166
|
+
});
|
|
1167
|
+
if (!response.ok) {
|
|
1168
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1169
|
+
const errorMessage = errorData.error || `Transcription failed: ${response.status}`;
|
|
1170
|
+
throw new ApiError(errorMessage, response.status);
|
|
1171
|
+
}
|
|
1172
|
+
const result = await response.json();
|
|
1173
|
+
const transcription = result.text || "";
|
|
1174
|
+
this.debug("Transcription result:", { text: transcription });
|
|
1175
|
+
return transcription;
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
if (error instanceof ApiError) throw error;
|
|
1178
|
+
throw new DistriError("Failed to transcribe audio", "TRANSCRIPTION_ERROR", error);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
async getConfiguration() {
|
|
1182
|
+
const response = await this.fetch(`/configuration`, {
|
|
1183
|
+
method: "GET",
|
|
1184
|
+
headers: {
|
|
1185
|
+
...this.config.headers
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1188
|
+
if (!response.ok) {
|
|
1189
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1190
|
+
throw new ApiError(errorData.error || "Failed to load configuration", response.status);
|
|
1191
|
+
}
|
|
1192
|
+
return response.json();
|
|
1193
|
+
}
|
|
1194
|
+
async updateConfiguration(configuration) {
|
|
1195
|
+
const response = await this.fetch(`/configuration`, {
|
|
1196
|
+
method: "PUT",
|
|
1197
|
+
headers: {
|
|
1198
|
+
"Content-Type": "application/json",
|
|
1199
|
+
...this.config.headers
|
|
1200
|
+
},
|
|
1201
|
+
body: JSON.stringify(configuration)
|
|
1202
|
+
});
|
|
1203
|
+
if (!response.ok) {
|
|
1204
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1205
|
+
throw new ApiError(errorData.error || "Failed to update configuration", response.status);
|
|
1206
|
+
}
|
|
1207
|
+
return response.json();
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Minimal LLM helper that proxies to the Distri server using Distri messages.
|
|
1211
|
+
*/
|
|
1212
|
+
async llm(messages, tools = [], options) {
|
|
1213
|
+
const headers = { "Content-Type": "application/json" };
|
|
1214
|
+
const response = await this.fetch(`/llm/execute`, {
|
|
1215
|
+
method: "POST",
|
|
1216
|
+
headers,
|
|
1217
|
+
body: JSON.stringify({ messages, tools, ...options })
|
|
1218
|
+
});
|
|
1219
|
+
if (!response.ok) {
|
|
1220
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1221
|
+
const message = errorData?.error || response.statusText || "LLM request failed";
|
|
1222
|
+
throw new ApiError(`LLM request failed: ${message}`, response.status);
|
|
1223
|
+
}
|
|
1224
|
+
return response.json();
|
|
738
1225
|
}
|
|
739
1226
|
/**
|
|
740
1227
|
* Get all available agents from the Distri server
|
|
@@ -791,14 +1278,18 @@ var DistriClient = class {
|
|
|
791
1278
|
* Get or create A2AClient for an agent
|
|
792
1279
|
*/
|
|
793
1280
|
getA2AClient(agentId) {
|
|
794
|
-
|
|
1281
|
+
const agentUrl = `${this.config.baseUrl}/agents/${agentId}`;
|
|
1282
|
+
const existing = this.agentClients.get(agentId);
|
|
1283
|
+
if (!existing || existing.url !== agentUrl) {
|
|
795
1284
|
const fetchFn = this.fetchAbsolute.bind(this);
|
|
796
|
-
const agentUrl = `${this.config.baseUrl}/agents/${agentId}`;
|
|
797
1285
|
const client = new A2AClient(agentUrl, fetchFn);
|
|
798
|
-
this.agentClients.set(agentId, client);
|
|
799
|
-
this.debug(
|
|
1286
|
+
this.agentClients.set(agentId, { url: agentUrl, client });
|
|
1287
|
+
this.debug(
|
|
1288
|
+
existing ? `Recreated A2AClient for agent ${agentId} with new URL ${agentUrl}` : `Created A2AClient for agent ${agentId} at ${agentUrl}`
|
|
1289
|
+
);
|
|
1290
|
+
return client;
|
|
800
1291
|
}
|
|
801
|
-
return
|
|
1292
|
+
return existing.client;
|
|
802
1293
|
}
|
|
803
1294
|
/**
|
|
804
1295
|
* Send a message to an agent
|
|
@@ -825,6 +1316,7 @@ var DistriClient = class {
|
|
|
825
1316
|
* Send a streaming message to an agent
|
|
826
1317
|
*/
|
|
827
1318
|
async *sendMessageStream(agentId, params) {
|
|
1319
|
+
console.log("sendMessageStream", agentId, params);
|
|
828
1320
|
try {
|
|
829
1321
|
const client = this.getA2AClient(agentId);
|
|
830
1322
|
yield* await client.sendMessageStream(params);
|
|
@@ -930,18 +1422,195 @@ var DistriClient = class {
|
|
|
930
1422
|
};
|
|
931
1423
|
await this.sendMessage(threadId, params);
|
|
932
1424
|
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Complete an external tool call
|
|
1427
|
+
*/
|
|
1428
|
+
async completeTool(agentId, result) {
|
|
1429
|
+
try {
|
|
1430
|
+
const response = await this.fetch(`/agents/${agentId}/complete-tool`, {
|
|
1431
|
+
method: "POST",
|
|
1432
|
+
headers: {
|
|
1433
|
+
"Content-Type": "application/json",
|
|
1434
|
+
...this.config.headers
|
|
1435
|
+
},
|
|
1436
|
+
body: JSON.stringify({
|
|
1437
|
+
tool_call_id: result.tool_call_id,
|
|
1438
|
+
tool_response: result
|
|
1439
|
+
})
|
|
1440
|
+
});
|
|
1441
|
+
if (!response.ok) {
|
|
1442
|
+
throw new ApiError(`Failed to complete tool: ${response.statusText}`, response.status);
|
|
1443
|
+
}
|
|
1444
|
+
this.debug(`Tool completed: ${result.tool_name} (${result.tool_call_id}) for agent ${agentId}`);
|
|
1445
|
+
} catch (error) {
|
|
1446
|
+
if (error instanceof ApiError) throw error;
|
|
1447
|
+
throw new DistriError(`Failed to complete tool ${result.tool_name} (${result.tool_call_id}) for agent ${agentId}`, "COMPLETE_TOOL_ERROR", error);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Complete an inline hook with a mutation payload.
|
|
1452
|
+
*/
|
|
1453
|
+
async completeInlineHook(hookId, mutation) {
|
|
1454
|
+
const response = await this.fetch(`/event/hooks`, {
|
|
1455
|
+
method: "POST",
|
|
1456
|
+
headers: {
|
|
1457
|
+
"Content-Type": "application/json",
|
|
1458
|
+
...this.config.headers
|
|
1459
|
+
},
|
|
1460
|
+
body: JSON.stringify({
|
|
1461
|
+
hook_id: hookId,
|
|
1462
|
+
mutation
|
|
1463
|
+
})
|
|
1464
|
+
});
|
|
1465
|
+
if (!response.ok) {
|
|
1466
|
+
throw new ApiError(`Failed to complete inline hook: ${response.statusText}`, response.status);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
933
1469
|
/**
|
|
934
1470
|
* Get the base URL for making direct requests
|
|
935
1471
|
*/
|
|
936
1472
|
get baseUrl() {
|
|
937
1473
|
return this.config.baseUrl;
|
|
938
1474
|
}
|
|
1475
|
+
applyTokens(accessToken, refreshToken, notify) {
|
|
1476
|
+
this.accessToken = accessToken;
|
|
1477
|
+
this.refreshToken = refreshToken;
|
|
1478
|
+
if (notify && this.onTokenRefresh) {
|
|
1479
|
+
this.onTokenRefresh({ accessToken, refreshToken });
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
async ensureAccessToken() {
|
|
1483
|
+
if (!this.refreshToken) {
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
if (!this.accessToken || this.isTokenExpiring(this.accessToken)) {
|
|
1487
|
+
try {
|
|
1488
|
+
await this.refreshTokens();
|
|
1489
|
+
} catch (error) {
|
|
1490
|
+
this.debug("Token refresh failed:", error);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
async refreshTokens() {
|
|
1495
|
+
if (!this.refreshToken) {
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
if (!this.refreshPromise) {
|
|
1499
|
+
this.refreshPromise = this.performTokenRefresh().finally(() => {
|
|
1500
|
+
this.refreshPromise = void 0;
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
return this.refreshPromise;
|
|
1504
|
+
}
|
|
1505
|
+
async performTokenRefresh() {
|
|
1506
|
+
const response = await this.fetchAbsolute(
|
|
1507
|
+
`${this.config.baseUrl}/token`,
|
|
1508
|
+
{
|
|
1509
|
+
method: "POST",
|
|
1510
|
+
headers: {
|
|
1511
|
+
"Content-Type": "application/json",
|
|
1512
|
+
...this.config.headers
|
|
1513
|
+
},
|
|
1514
|
+
body: JSON.stringify({
|
|
1515
|
+
grant_type: "refresh_token",
|
|
1516
|
+
refresh_token: this.refreshToken
|
|
1517
|
+
})
|
|
1518
|
+
},
|
|
1519
|
+
{ skipAuth: true, retryOnAuth: false }
|
|
1520
|
+
);
|
|
1521
|
+
if (!response.ok) {
|
|
1522
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1523
|
+
throw new ApiError(errorData.error || "Failed to refresh token", response.status);
|
|
1524
|
+
}
|
|
1525
|
+
const tokens = await response.json();
|
|
1526
|
+
if (!tokens?.access_token || !tokens?.refresh_token) {
|
|
1527
|
+
throw new ApiError("Invalid token response", response.status);
|
|
1528
|
+
}
|
|
1529
|
+
this.applyTokens(tokens.access_token, tokens.refresh_token, true);
|
|
1530
|
+
}
|
|
1531
|
+
isTokenExpiring(token) {
|
|
1532
|
+
const expiresAt = this.getTokenExpiry(token);
|
|
1533
|
+
if (!expiresAt) {
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1536
|
+
return expiresAt <= Date.now() + this.tokenRefreshSkewMs;
|
|
1537
|
+
}
|
|
1538
|
+
getTokenExpiry(token) {
|
|
1539
|
+
const payload = this.decodeJwtPayload(token);
|
|
1540
|
+
const exp = payload?.exp;
|
|
1541
|
+
if (typeof exp !== "number") {
|
|
1542
|
+
return null;
|
|
1543
|
+
}
|
|
1544
|
+
return exp * 1e3;
|
|
1545
|
+
}
|
|
1546
|
+
decodeJwtPayload(token) {
|
|
1547
|
+
const parts = token.split(".");
|
|
1548
|
+
if (parts.length < 2) {
|
|
1549
|
+
return null;
|
|
1550
|
+
}
|
|
1551
|
+
const decoded = this.decodeBase64Url(parts[1]);
|
|
1552
|
+
if (!decoded) {
|
|
1553
|
+
return null;
|
|
1554
|
+
}
|
|
1555
|
+
try {
|
|
1556
|
+
return JSON.parse(decoded);
|
|
1557
|
+
} catch {
|
|
1558
|
+
return null;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
decodeBase64Url(value) {
|
|
1562
|
+
const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
1563
|
+
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
|
|
1564
|
+
try {
|
|
1565
|
+
if (typeof atob === "function") {
|
|
1566
|
+
return atob(padded);
|
|
1567
|
+
}
|
|
1568
|
+
const buffer = globalThis.Buffer;
|
|
1569
|
+
if (typeof buffer !== "undefined") {
|
|
1570
|
+
return buffer.from(padded, "base64").toString("utf8");
|
|
1571
|
+
}
|
|
1572
|
+
} catch {
|
|
1573
|
+
return null;
|
|
1574
|
+
}
|
|
1575
|
+
return null;
|
|
1576
|
+
}
|
|
1577
|
+
applyAuthHeader(headers) {
|
|
1578
|
+
if (this.accessToken && !headers.has("authorization")) {
|
|
1579
|
+
headers.set("Authorization", `Bearer ${this.accessToken}`);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
939
1582
|
/**
|
|
940
1583
|
* Enhanced fetch with retry logic
|
|
941
1584
|
*/
|
|
942
|
-
async fetchAbsolute(url, initialInit) {
|
|
1585
|
+
async fetchAbsolute(url, initialInit, options) {
|
|
1586
|
+
const { skipAuth = false, retryOnAuth = true } = options ?? {};
|
|
943
1587
|
const init = await this.config.interceptor(initialInit);
|
|
944
1588
|
let lastError;
|
|
1589
|
+
const headers = new Headers();
|
|
1590
|
+
const applyHeaders = (src) => {
|
|
1591
|
+
if (!src) return;
|
|
1592
|
+
if (src instanceof Headers) {
|
|
1593
|
+
src.forEach((value, key) => headers.set(key, value));
|
|
1594
|
+
} else if (Array.isArray(src)) {
|
|
1595
|
+
src.forEach(([key, value]) => headers.set(key, value));
|
|
1596
|
+
} else if (typeof src === "object") {
|
|
1597
|
+
Object.entries(src).forEach(([key, value]) => {
|
|
1598
|
+
if (typeof value === "string") {
|
|
1599
|
+
headers.set(key, value);
|
|
1600
|
+
}
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
applyHeaders(this.config.headers);
|
|
1605
|
+
applyHeaders(init?.headers);
|
|
1606
|
+
const hasBody = init?.body !== void 0 && !(init.body instanceof FormData) && !(init.body instanceof Blob);
|
|
1607
|
+
if (!headers.has("content-type") && hasBody) {
|
|
1608
|
+
headers.set("Content-Type", "application/json");
|
|
1609
|
+
}
|
|
1610
|
+
if (!skipAuth) {
|
|
1611
|
+
await this.ensureAccessToken();
|
|
1612
|
+
this.applyAuthHeader(headers);
|
|
1613
|
+
}
|
|
945
1614
|
for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {
|
|
946
1615
|
try {
|
|
947
1616
|
const controller = new AbortController();
|
|
@@ -949,12 +1618,15 @@ var DistriClient = class {
|
|
|
949
1618
|
const response = await fetch(url, {
|
|
950
1619
|
...init,
|
|
951
1620
|
signal: controller.signal,
|
|
952
|
-
headers
|
|
953
|
-
...this.config.headers,
|
|
954
|
-
...init?.headers
|
|
955
|
-
}
|
|
1621
|
+
headers
|
|
956
1622
|
});
|
|
957
1623
|
clearTimeout(timeoutId);
|
|
1624
|
+
if (!skipAuth && retryOnAuth && response.status === 401 && this.refreshToken) {
|
|
1625
|
+
const refreshed = await this.refreshTokens().then(() => true).catch(() => false);
|
|
1626
|
+
if (refreshed) {
|
|
1627
|
+
return this.fetchAbsolute(url, initialInit, { skipAuth, retryOnAuth: false });
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
958
1630
|
return response;
|
|
959
1631
|
} catch (error) {
|
|
960
1632
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -967,7 +1639,8 @@ var DistriClient = class {
|
|
|
967
1639
|
throw lastError;
|
|
968
1640
|
}
|
|
969
1641
|
/**
|
|
970
|
-
* Enhanced fetch with retry logic
|
|
1642
|
+
* Enhanced fetch with retry logic and auth headers.
|
|
1643
|
+
* Exposed publicly for extensions like DistriHomeClient.
|
|
971
1644
|
*/
|
|
972
1645
|
async fetch(input, initialInit) {
|
|
973
1646
|
const url = `${this.config.baseUrl}${input}`;
|
|
@@ -1009,7 +1682,7 @@ var DistriClient = class {
|
|
|
1009
1682
|
id: id || uuidv4(),
|
|
1010
1683
|
role,
|
|
1011
1684
|
parts,
|
|
1012
|
-
created_at
|
|
1685
|
+
created_at: created_at || (/* @__PURE__ */ new Date()).getTime()
|
|
1013
1686
|
};
|
|
1014
1687
|
}
|
|
1015
1688
|
/**
|
|
@@ -1039,6 +1712,25 @@ var DistriClient = class {
|
|
|
1039
1712
|
};
|
|
1040
1713
|
}
|
|
1041
1714
|
};
|
|
1715
|
+
// ============================================================
|
|
1716
|
+
// Additional User Message Parts API
|
|
1717
|
+
// ============================================================
|
|
1718
|
+
// These methods allow external tools to append parts (text, images)
|
|
1719
|
+
// to the user message in the next agent iteration.
|
|
1720
|
+
// The parts are stored under the key "__additional_user_parts".
|
|
1721
|
+
_DistriClient.ADDITIONAL_PARTS_KEY = "__additional_user_parts";
|
|
1722
|
+
// ============================================================
|
|
1723
|
+
// Token API
|
|
1724
|
+
// ============================================================
|
|
1725
|
+
// Issue access + refresh tokens for temporary authentication (e.g., frontend use)
|
|
1726
|
+
/**
|
|
1727
|
+
* Response from the token endpoint
|
|
1728
|
+
*/
|
|
1729
|
+
_DistriClient.TokenType = {
|
|
1730
|
+
Main: "main",
|
|
1731
|
+
Short: "short"
|
|
1732
|
+
};
|
|
1733
|
+
var DistriClient = _DistriClient;
|
|
1042
1734
|
function uuidv4() {
|
|
1043
1735
|
if (typeof crypto?.randomUUID === "function") {
|
|
1044
1736
|
return crypto.randomUUID();
|
|
@@ -1053,42 +1745,32 @@ function uuidv4() {
|
|
|
1053
1745
|
}
|
|
1054
1746
|
|
|
1055
1747
|
// src/agent.ts
|
|
1748
|
+
var ExternalToolValidationError = class extends DistriError {
|
|
1749
|
+
constructor(agentName, result) {
|
|
1750
|
+
super(
|
|
1751
|
+
result.message || "Missing required external tools for agent invocation.",
|
|
1752
|
+
"EXTERNAL_TOOL_VALIDATION_ERROR",
|
|
1753
|
+
{
|
|
1754
|
+
agentName,
|
|
1755
|
+
missingTools: result.missingTools,
|
|
1756
|
+
requiredTools: result.requiredTools,
|
|
1757
|
+
providedTools: result.providedTools
|
|
1758
|
+
}
|
|
1759
|
+
);
|
|
1760
|
+
this.name = "ExternalToolValidationError";
|
|
1761
|
+
this.agentName = agentName;
|
|
1762
|
+
this.missingTools = result.missingTools;
|
|
1763
|
+
this.requiredTools = result.requiredTools;
|
|
1764
|
+
this.providedTools = result.providedTools;
|
|
1765
|
+
}
|
|
1766
|
+
};
|
|
1056
1767
|
var Agent = class _Agent {
|
|
1057
1768
|
constructor(agentDefinition, client) {
|
|
1058
|
-
this.
|
|
1769
|
+
this.hookHandlers = /* @__PURE__ */ new Map();
|
|
1770
|
+
this.defaultHookHandler = null;
|
|
1059
1771
|
this.agentDefinition = agentDefinition;
|
|
1060
1772
|
this.client = client;
|
|
1061
1773
|
}
|
|
1062
|
-
/**
|
|
1063
|
-
* Add a tool to the agent (AG-UI style)
|
|
1064
|
-
*/
|
|
1065
|
-
registerTool(tool) {
|
|
1066
|
-
this.tools.set(tool.name, tool);
|
|
1067
|
-
}
|
|
1068
|
-
/**
|
|
1069
|
-
* Add multiple tools at once
|
|
1070
|
-
*/
|
|
1071
|
-
registerTools(tools) {
|
|
1072
|
-
tools.forEach((tool) => this.registerTool(tool));
|
|
1073
|
-
}
|
|
1074
|
-
/**
|
|
1075
|
-
* Remove a tool
|
|
1076
|
-
*/
|
|
1077
|
-
unregisterTool(toolName) {
|
|
1078
|
-
this.tools.delete(toolName);
|
|
1079
|
-
}
|
|
1080
|
-
/**
|
|
1081
|
-
* Get all registered tools
|
|
1082
|
-
*/
|
|
1083
|
-
getTools() {
|
|
1084
|
-
return Array.from(this.tools.values());
|
|
1085
|
-
}
|
|
1086
|
-
/**
|
|
1087
|
-
* Check if a tool is registered
|
|
1088
|
-
*/
|
|
1089
|
-
hasTool(toolName) {
|
|
1090
|
-
return this.tools.has(toolName);
|
|
1091
|
-
}
|
|
1092
1774
|
/**
|
|
1093
1775
|
* Get agent information
|
|
1094
1776
|
*/
|
|
@@ -1101,9 +1783,18 @@ var Agent = class _Agent {
|
|
|
1101
1783
|
get description() {
|
|
1102
1784
|
return this.agentDefinition.description;
|
|
1103
1785
|
}
|
|
1786
|
+
get agentType() {
|
|
1787
|
+
return this.agentDefinition.agent_type;
|
|
1788
|
+
}
|
|
1104
1789
|
get iconUrl() {
|
|
1105
1790
|
return this.agentDefinition.icon_url;
|
|
1106
1791
|
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Get the full agent definition (including backend tools)
|
|
1794
|
+
*/
|
|
1795
|
+
getDefinition() {
|
|
1796
|
+
return this.agentDefinition;
|
|
1797
|
+
}
|
|
1107
1798
|
/**
|
|
1108
1799
|
* Fetch messages for a thread (public method for useChat)
|
|
1109
1800
|
*/
|
|
@@ -1113,51 +1804,154 @@ var Agent = class _Agent {
|
|
|
1113
1804
|
/**
|
|
1114
1805
|
* Direct (non-streaming) invoke
|
|
1115
1806
|
*/
|
|
1116
|
-
async invoke(params) {
|
|
1117
|
-
|
|
1118
|
-
|
|
1807
|
+
async invoke(params, tools, hooks) {
|
|
1808
|
+
if (hooks) {
|
|
1809
|
+
this.registerHooks(hooks);
|
|
1810
|
+
}
|
|
1811
|
+
const enhancedParams = this.enhanceParamsWithTools(params, tools);
|
|
1119
1812
|
return await this.client.sendMessage(this.agentDefinition.id, enhancedParams);
|
|
1120
1813
|
}
|
|
1121
1814
|
/**
|
|
1122
1815
|
* Streaming invoke
|
|
1123
1816
|
*/
|
|
1124
|
-
async invokeStream(params) {
|
|
1125
|
-
|
|
1126
|
-
|
|
1817
|
+
async invokeStream(params, tools, hooks) {
|
|
1818
|
+
if (hooks) {
|
|
1819
|
+
this.registerHooks(hooks);
|
|
1820
|
+
}
|
|
1821
|
+
const enhancedParams = this.enhanceParamsWithTools(params, tools);
|
|
1127
1822
|
const a2aStream = this.client.sendMessageStream(this.agentDefinition.id, enhancedParams);
|
|
1128
|
-
|
|
1823
|
+
const self = this;
|
|
1824
|
+
return (async function* () {
|
|
1129
1825
|
for await (const event of a2aStream) {
|
|
1130
1826
|
const converted = decodeA2AStreamEvent(event);
|
|
1131
|
-
if (converted) {
|
|
1827
|
+
if (converted && converted.type === "inline_hook_requested") {
|
|
1828
|
+
const hookReq = converted.data;
|
|
1829
|
+
const handler = self.hookHandlers.get(hookReq.hook) || self.defaultHookHandler;
|
|
1830
|
+
if (handler) {
|
|
1831
|
+
try {
|
|
1832
|
+
const mutation = await handler(hookReq);
|
|
1833
|
+
await self.client.completeInlineHook(hookReq.hook_id, mutation);
|
|
1834
|
+
} catch (err) {
|
|
1835
|
+
await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
|
|
1836
|
+
}
|
|
1837
|
+
} else {
|
|
1838
|
+
await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
|
|
1839
|
+
}
|
|
1840
|
+
yield converted;
|
|
1841
|
+
} else if (converted) {
|
|
1132
1842
|
yield converted;
|
|
1133
1843
|
}
|
|
1134
1844
|
}
|
|
1135
|
-
}();
|
|
1845
|
+
})();
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* Validate that required external tools are registered before invoking.
|
|
1849
|
+
*/
|
|
1850
|
+
validateExternalTools(tools = []) {
|
|
1851
|
+
const requiredTools = this.getRequiredExternalTools();
|
|
1852
|
+
const providedTools = tools.map((tool) => tool.name);
|
|
1853
|
+
if (requiredTools.length === 0) {
|
|
1854
|
+
return {
|
|
1855
|
+
isValid: true,
|
|
1856
|
+
requiredTools: [],
|
|
1857
|
+
providedTools,
|
|
1858
|
+
missingTools: []
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
const providedSet = new Set(providedTools);
|
|
1862
|
+
const missingTools = requiredTools.filter((tool) => !providedSet.has(tool));
|
|
1863
|
+
const isValid = missingTools.length === 0;
|
|
1864
|
+
return {
|
|
1865
|
+
isValid,
|
|
1866
|
+
requiredTools,
|
|
1867
|
+
providedTools,
|
|
1868
|
+
missingTools,
|
|
1869
|
+
message: isValid ? void 0 : this.formatExternalToolValidationMessage(requiredTools, missingTools)
|
|
1870
|
+
};
|
|
1136
1871
|
}
|
|
1137
1872
|
/**
|
|
1138
1873
|
* Enhance message params with tool definitions
|
|
1139
1874
|
*/
|
|
1140
|
-
enhanceParamsWithTools(params) {
|
|
1141
|
-
|
|
1875
|
+
enhanceParamsWithTools(params, tools) {
|
|
1876
|
+
this.assertExternalTools(tools);
|
|
1877
|
+
const metadata = {
|
|
1878
|
+
...params.metadata,
|
|
1879
|
+
external_tools: tools?.map((tool) => ({
|
|
1880
|
+
name: tool.name,
|
|
1881
|
+
description: tool.description,
|
|
1882
|
+
parameters: tool.parameters,
|
|
1883
|
+
is_final: tool.is_final
|
|
1884
|
+
})) || []
|
|
1885
|
+
};
|
|
1142
1886
|
return {
|
|
1143
1887
|
...params,
|
|
1144
|
-
metadata
|
|
1145
|
-
...params.metadata,
|
|
1146
|
-
tools: tools.map((tool) => ({
|
|
1147
|
-
name: tool.name,
|
|
1148
|
-
description: tool.description,
|
|
1149
|
-
input_schema: tool.input_schema
|
|
1150
|
-
}))
|
|
1151
|
-
}
|
|
1888
|
+
metadata
|
|
1152
1889
|
};
|
|
1153
1890
|
}
|
|
1891
|
+
assertExternalTools(tools) {
|
|
1892
|
+
const result = this.validateExternalTools(tools ?? []);
|
|
1893
|
+
if (!result.isValid) {
|
|
1894
|
+
throw new ExternalToolValidationError(this.agentDefinition.name || this.agentDefinition.id, result);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
getRequiredExternalTools() {
|
|
1898
|
+
const toolConfig = this.resolveToolConfig();
|
|
1899
|
+
if (!toolConfig?.external || !Array.isArray(toolConfig.external)) {
|
|
1900
|
+
return [];
|
|
1901
|
+
}
|
|
1902
|
+
return toolConfig.external.filter((tool) => typeof tool === "string" && tool.trim().length > 0);
|
|
1903
|
+
}
|
|
1904
|
+
resolveToolConfig() {
|
|
1905
|
+
const root = this.agentDefinition;
|
|
1906
|
+
return this.extractToolConfig(root) || this.extractToolConfig(root?.agent) || this.extractToolConfig(root?.definition);
|
|
1907
|
+
}
|
|
1908
|
+
extractToolConfig(candidate) {
|
|
1909
|
+
if (!candidate) return null;
|
|
1910
|
+
const tools = candidate.tools;
|
|
1911
|
+
if (!tools || Array.isArray(tools) || typeof tools !== "object") {
|
|
1912
|
+
return null;
|
|
1913
|
+
}
|
|
1914
|
+
return tools;
|
|
1915
|
+
}
|
|
1916
|
+
formatExternalToolValidationMessage(requiredTools, missingTools) {
|
|
1917
|
+
const requiredList = requiredTools.join(", ");
|
|
1918
|
+
const missingList = missingTools.join(", ");
|
|
1919
|
+
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}.`;
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* Register multiple hooks at once.
|
|
1923
|
+
*/
|
|
1924
|
+
registerHooks(hooks, defaultHandler) {
|
|
1925
|
+
Object.entries(hooks).forEach(([hook, handler]) => {
|
|
1926
|
+
this.hookHandlers.set(hook, handler);
|
|
1927
|
+
});
|
|
1928
|
+
if (defaultHandler) {
|
|
1929
|
+
this.defaultHookHandler = defaultHandler;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1154
1932
|
/**
|
|
1155
1933
|
* Create an agent instance from an agent ID
|
|
1156
1934
|
*/
|
|
1157
1935
|
static async create(agentIdOrDef, client) {
|
|
1158
1936
|
const agentDefinition = typeof agentIdOrDef === "string" ? await client.getAgent(agentIdOrDef) : agentIdOrDef;
|
|
1937
|
+
const tools = agentDefinition?.resolved_tools || [];
|
|
1938
|
+
console.log("\u{1F916} Agent definition loaded:", {
|
|
1939
|
+
id: agentDefinition.id,
|
|
1940
|
+
name: agentDefinition.name,
|
|
1941
|
+
tools: tools.map((t) => ({
|
|
1942
|
+
name: t.name,
|
|
1943
|
+
type: "function"
|
|
1944
|
+
})) || [],
|
|
1945
|
+
toolCount: agentDefinition.tools?.length || 0
|
|
1946
|
+
});
|
|
1159
1947
|
return new _Agent(agentDefinition, client);
|
|
1160
1948
|
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Complete an external tool call by sending the result back to the server
|
|
1951
|
+
*/
|
|
1952
|
+
async completeTool(result) {
|
|
1953
|
+
await this.client.completeTool(this.agentDefinition.id, result);
|
|
1954
|
+
}
|
|
1161
1955
|
/**
|
|
1162
1956
|
* List all available agents
|
|
1163
1957
|
*/
|
|
@@ -1171,22 +1965,25 @@ export {
|
|
|
1171
1965
|
Agent,
|
|
1172
1966
|
ApiError,
|
|
1173
1967
|
ConnectionError,
|
|
1968
|
+
DEFAULT_BASE_URL,
|
|
1174
1969
|
DistriClient,
|
|
1175
1970
|
DistriError,
|
|
1176
|
-
|
|
1971
|
+
ExternalToolValidationError,
|
|
1177
1972
|
convertA2AMessageToDistri,
|
|
1178
1973
|
convertA2APartToDistri,
|
|
1179
1974
|
convertA2AStatusUpdateToDistri,
|
|
1180
1975
|
convertDistriMessageToA2A,
|
|
1181
1976
|
convertDistriPartToA2A,
|
|
1977
|
+
createFailedToolResult,
|
|
1978
|
+
createSuccessfulToolResult,
|
|
1182
1979
|
decodeA2AStreamEvent,
|
|
1183
1980
|
extractTextFromDistriMessage,
|
|
1184
1981
|
extractToolCallsFromDistriMessage,
|
|
1982
|
+
extractToolResultData,
|
|
1185
1983
|
extractToolResultsFromDistriMessage,
|
|
1186
|
-
|
|
1984
|
+
isArrayParts,
|
|
1187
1985
|
isDistriEvent,
|
|
1188
1986
|
isDistriMessage,
|
|
1189
|
-
isDistriPlan,
|
|
1190
1987
|
processA2AMessagesData,
|
|
1191
1988
|
processA2AStreamData,
|
|
1192
1989
|
uuidv4
|