agentbnb 2.2.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/card-P5C36VBD.js +81 -0
- package/dist/chunk-BEI5MTNZ.js +91 -0
- package/dist/chunk-EZB4RUIG.js +372 -0
- package/dist/chunk-PJSYSVKN.js +491 -0
- package/dist/chunk-TQMI73LL.js +125 -0
- package/dist/chunk-V7M6GIJZ.js +666 -0
- package/dist/cli/index.js +495 -1353
- package/dist/conduct-JZJS2ZHA.js +115 -0
- package/dist/conductor-mode-DJ3RIJ5T.js +111 -0
- package/dist/index.d.ts +2174 -120
- package/dist/index.js +1721 -27
- package/dist/peers-G36URZYB.js +12 -0
- package/package.json +2 -2
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CapabilityCardV2Schema
|
|
3
|
+
} from "./chunk-TQMI73LL.js";
|
|
4
|
+
|
|
5
|
+
// src/conductor/card.ts
|
|
6
|
+
var CONDUCTOR_OWNER = "agentbnb-conductor";
|
|
7
|
+
var CONDUCTOR_CARD_ID = "00000000-0000-4000-8000-000000000001";
|
|
8
|
+
function buildConductorCard() {
|
|
9
|
+
const card = {
|
|
10
|
+
spec_version: "2.0",
|
|
11
|
+
id: CONDUCTOR_CARD_ID,
|
|
12
|
+
owner: CONDUCTOR_OWNER,
|
|
13
|
+
agent_name: "AgentBnB Conductor",
|
|
14
|
+
skills: [
|
|
15
|
+
{
|
|
16
|
+
id: "orchestrate",
|
|
17
|
+
name: "Task Orchestration",
|
|
18
|
+
description: "Decomposes complex tasks and coordinates multi-agent execution",
|
|
19
|
+
level: 3,
|
|
20
|
+
inputs: [
|
|
21
|
+
{
|
|
22
|
+
name: "task",
|
|
23
|
+
type: "text",
|
|
24
|
+
description: "Natural language task description"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
outputs: [
|
|
28
|
+
{
|
|
29
|
+
name: "result",
|
|
30
|
+
type: "json",
|
|
31
|
+
description: "Aggregated execution results"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
pricing: { credits_per_call: 5 }
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "plan",
|
|
38
|
+
name: "Execution Planning",
|
|
39
|
+
description: "Returns an execution plan with cost estimate without executing",
|
|
40
|
+
level: 1,
|
|
41
|
+
inputs: [
|
|
42
|
+
{
|
|
43
|
+
name: "task",
|
|
44
|
+
type: "text",
|
|
45
|
+
description: "Natural language task description"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
outputs: [
|
|
49
|
+
{
|
|
50
|
+
name: "plan",
|
|
51
|
+
type: "json",
|
|
52
|
+
description: "Execution plan with cost breakdown"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
pricing: { credits_per_call: 1 }
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
availability: { online: true }
|
|
59
|
+
};
|
|
60
|
+
return CapabilityCardV2Schema.parse(card);
|
|
61
|
+
}
|
|
62
|
+
function registerConductorCard(db) {
|
|
63
|
+
const card = buildConductorCard();
|
|
64
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
65
|
+
const existing = db.prepare("SELECT id FROM capability_cards WHERE id = ?").get(card.id);
|
|
66
|
+
if (existing) {
|
|
67
|
+
db.prepare(
|
|
68
|
+
"UPDATE capability_cards SET data = ?, updated_at = ? WHERE id = ?"
|
|
69
|
+
).run(JSON.stringify(card), now, card.id);
|
|
70
|
+
} else {
|
|
71
|
+
db.prepare(
|
|
72
|
+
"INSERT INTO capability_cards (id, owner, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
|
|
73
|
+
).run(card.id, card.owner, JSON.stringify(card), now, now);
|
|
74
|
+
}
|
|
75
|
+
return card;
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
CONDUCTOR_OWNER,
|
|
79
|
+
buildConductorCard,
|
|
80
|
+
registerConductorCard
|
|
81
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/cli/peers.ts
|
|
2
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
3
|
+
import { join as join2 } from "path";
|
|
4
|
+
|
|
5
|
+
// src/cli/config.ts
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
function getConfigDir() {
|
|
10
|
+
return process.env["AGENTBNB_DIR"] ?? join(homedir(), ".agentbnb");
|
|
11
|
+
}
|
|
12
|
+
function getConfigPath() {
|
|
13
|
+
return join(getConfigDir(), "config.json");
|
|
14
|
+
}
|
|
15
|
+
function loadConfig() {
|
|
16
|
+
const configPath = getConfigPath();
|
|
17
|
+
if (!existsSync(configPath)) return null;
|
|
18
|
+
try {
|
|
19
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
20
|
+
return JSON.parse(raw);
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function saveConfig(config) {
|
|
26
|
+
const dir = getConfigDir();
|
|
27
|
+
if (!existsSync(dir)) {
|
|
28
|
+
mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), "utf-8");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/cli/peers.ts
|
|
34
|
+
function getPeersPath() {
|
|
35
|
+
return join2(getConfigDir(), "peers.json");
|
|
36
|
+
}
|
|
37
|
+
function loadPeers() {
|
|
38
|
+
const peersPath = getPeersPath();
|
|
39
|
+
if (!existsSync2(peersPath)) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const raw = readFileSync2(peersPath, "utf-8");
|
|
44
|
+
return JSON.parse(raw);
|
|
45
|
+
} catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function writePeers(peers) {
|
|
50
|
+
const dir = getConfigDir();
|
|
51
|
+
if (!existsSync2(dir)) {
|
|
52
|
+
mkdirSync2(dir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
writeFileSync2(getPeersPath(), JSON.stringify(peers, null, 2), "utf-8");
|
|
55
|
+
}
|
|
56
|
+
function savePeer(peer) {
|
|
57
|
+
const peers = loadPeers();
|
|
58
|
+
const lowerName = peer.name.toLowerCase();
|
|
59
|
+
const existing = peers.findIndex((p) => p.name.toLowerCase() === lowerName);
|
|
60
|
+
if (existing >= 0) {
|
|
61
|
+
peers[existing] = peer;
|
|
62
|
+
} else {
|
|
63
|
+
peers.push(peer);
|
|
64
|
+
}
|
|
65
|
+
writePeers(peers);
|
|
66
|
+
}
|
|
67
|
+
function removePeer(name) {
|
|
68
|
+
const peers = loadPeers();
|
|
69
|
+
const lowerName = name.toLowerCase();
|
|
70
|
+
const filtered = peers.filter((p) => p.name.toLowerCase() !== lowerName);
|
|
71
|
+
if (filtered.length === peers.length) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
writePeers(filtered);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
function findPeer(name) {
|
|
78
|
+
const peers = loadPeers();
|
|
79
|
+
const lowerName = name.toLowerCase();
|
|
80
|
+
return peers.find((p) => p.name.toLowerCase() === lowerName) ?? null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
getConfigDir,
|
|
85
|
+
loadConfig,
|
|
86
|
+
saveConfig,
|
|
87
|
+
loadPeers,
|
|
88
|
+
savePeer,
|
|
89
|
+
removePeer,
|
|
90
|
+
findPeer
|
|
91
|
+
};
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import {
|
|
2
|
+
interpolateObject,
|
|
3
|
+
requestCapability,
|
|
4
|
+
scorePeers,
|
|
5
|
+
searchCards
|
|
6
|
+
} from "./chunk-V7M6GIJZ.js";
|
|
7
|
+
|
|
8
|
+
// src/conductor/task-decomposer.ts
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
var TEMPLATES = {
|
|
11
|
+
"video-production": {
|
|
12
|
+
keywords: ["video", "demo", "clip", "animation"],
|
|
13
|
+
steps: [
|
|
14
|
+
{
|
|
15
|
+
description: "Generate script from task description",
|
|
16
|
+
required_capability: "text_gen",
|
|
17
|
+
estimated_credits: 2,
|
|
18
|
+
depends_on_indices: []
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
description: "Generate voiceover from script",
|
|
22
|
+
required_capability: "tts",
|
|
23
|
+
estimated_credits: 3,
|
|
24
|
+
depends_on_indices: [0]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
description: "Generate video visuals from script",
|
|
28
|
+
required_capability: "video_gen",
|
|
29
|
+
estimated_credits: 5,
|
|
30
|
+
depends_on_indices: [0]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
description: "Composite voiceover and video into final output",
|
|
34
|
+
required_capability: "video_edit",
|
|
35
|
+
estimated_credits: 3,
|
|
36
|
+
depends_on_indices: [1, 2]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"deep-analysis": {
|
|
41
|
+
keywords: ["analyze", "analysis", "research", "report", "evaluate"],
|
|
42
|
+
steps: [
|
|
43
|
+
{
|
|
44
|
+
description: "Research and gather relevant data",
|
|
45
|
+
required_capability: "web_search",
|
|
46
|
+
estimated_credits: 2,
|
|
47
|
+
depends_on_indices: []
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
description: "Analyze gathered data",
|
|
51
|
+
required_capability: "text_gen",
|
|
52
|
+
estimated_credits: 3,
|
|
53
|
+
depends_on_indices: [0]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
description: "Summarize analysis findings",
|
|
57
|
+
required_capability: "text_gen",
|
|
58
|
+
estimated_credits: 2,
|
|
59
|
+
depends_on_indices: [1]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
description: "Format into final report",
|
|
63
|
+
required_capability: "text_gen",
|
|
64
|
+
estimated_credits: 1,
|
|
65
|
+
depends_on_indices: [2]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"content-generation": {
|
|
70
|
+
keywords: ["write", "blog", "article", "content", "post", "essay"],
|
|
71
|
+
steps: [
|
|
72
|
+
{
|
|
73
|
+
description: "Create content outline",
|
|
74
|
+
required_capability: "text_gen",
|
|
75
|
+
estimated_credits: 1,
|
|
76
|
+
depends_on_indices: []
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
description: "Draft content from outline",
|
|
80
|
+
required_capability: "text_gen",
|
|
81
|
+
estimated_credits: 3,
|
|
82
|
+
depends_on_indices: [0]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
description: "Review and refine draft",
|
|
86
|
+
required_capability: "text_gen",
|
|
87
|
+
estimated_credits: 2,
|
|
88
|
+
depends_on_indices: [1]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
description: "Finalize and polish content",
|
|
92
|
+
required_capability: "text_gen",
|
|
93
|
+
estimated_credits: 1,
|
|
94
|
+
depends_on_indices: [2]
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
function decompose(task, _availableCapabilities) {
|
|
100
|
+
const lower = task.toLowerCase();
|
|
101
|
+
for (const template of Object.values(TEMPLATES)) {
|
|
102
|
+
const matched = template.keywords.some((kw) => lower.includes(kw));
|
|
103
|
+
if (!matched) continue;
|
|
104
|
+
const ids = template.steps.map(() => randomUUID());
|
|
105
|
+
return template.steps.map((step, i) => ({
|
|
106
|
+
id: ids[i],
|
|
107
|
+
description: step.description,
|
|
108
|
+
required_capability: step.required_capability,
|
|
109
|
+
params: {},
|
|
110
|
+
depends_on: step.depends_on_indices.map((idx) => ids[idx]),
|
|
111
|
+
estimated_credits: step.estimated_credits
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/conductor/capability-matcher.ts
|
|
118
|
+
var MAX_ALTERNATIVES = 2;
|
|
119
|
+
function matchSubTasks(opts) {
|
|
120
|
+
const { db, subtasks, conductorOwner } = opts;
|
|
121
|
+
return subtasks.map((subtask) => {
|
|
122
|
+
const cards = searchCards(db, subtask.required_capability, { online: true });
|
|
123
|
+
const candidates = [];
|
|
124
|
+
for (const card of cards) {
|
|
125
|
+
const cardAsV2 = card;
|
|
126
|
+
if (Array.isArray(cardAsV2.skills)) {
|
|
127
|
+
for (const skill of cardAsV2.skills) {
|
|
128
|
+
candidates.push({
|
|
129
|
+
card,
|
|
130
|
+
cost: skill.pricing.credits_per_call,
|
|
131
|
+
skillId: skill.id
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
candidates.push({
|
|
136
|
+
card,
|
|
137
|
+
cost: card.pricing.credits_per_call,
|
|
138
|
+
skillId: void 0
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const scored = scorePeers(candidates, conductorOwner);
|
|
143
|
+
if (scored.length === 0) {
|
|
144
|
+
return {
|
|
145
|
+
subtask_id: subtask.id,
|
|
146
|
+
selected_agent: "",
|
|
147
|
+
selected_skill: "",
|
|
148
|
+
score: 0,
|
|
149
|
+
credits: 0,
|
|
150
|
+
alternatives: []
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const top = scored[0];
|
|
154
|
+
const alternatives = scored.slice(1, 1 + MAX_ALTERNATIVES).map((s) => ({
|
|
155
|
+
agent: s.card.owner,
|
|
156
|
+
skill: s.skillId ?? "",
|
|
157
|
+
score: s.rawScore,
|
|
158
|
+
credits: s.cost
|
|
159
|
+
}));
|
|
160
|
+
return {
|
|
161
|
+
subtask_id: subtask.id,
|
|
162
|
+
selected_agent: top.card.owner,
|
|
163
|
+
selected_skill: top.skillId ?? "",
|
|
164
|
+
score: top.rawScore,
|
|
165
|
+
credits: top.cost,
|
|
166
|
+
alternatives
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/conductor/budget-controller.ts
|
|
172
|
+
var ORCHESTRATION_FEE = 5;
|
|
173
|
+
var BudgetController = class {
|
|
174
|
+
/**
|
|
175
|
+
* Creates a new BudgetController.
|
|
176
|
+
*
|
|
177
|
+
* @param budgetManager - Underlying BudgetManager for reserve floor enforcement.
|
|
178
|
+
* @param maxBudget - Hard ceiling for the orchestration run.
|
|
179
|
+
*/
|
|
180
|
+
constructor(budgetManager, maxBudget) {
|
|
181
|
+
this.budgetManager = budgetManager;
|
|
182
|
+
this.maxBudget = maxBudget;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Pre-calculates the total budget for an orchestration run.
|
|
186
|
+
*
|
|
187
|
+
* Sums all matched sub-task credits, adds the orchestration fee,
|
|
188
|
+
* and determines whether approval is required (estimated > max).
|
|
189
|
+
*
|
|
190
|
+
* @param matches - MatchResult[] from the CapabilityMatcher.
|
|
191
|
+
* @returns An ExecutionBudget with cost breakdown and approval status.
|
|
192
|
+
*/
|
|
193
|
+
calculateBudget(matches) {
|
|
194
|
+
const perTaskSpending = /* @__PURE__ */ new Map();
|
|
195
|
+
let subTotal = 0;
|
|
196
|
+
for (const match of matches) {
|
|
197
|
+
perTaskSpending.set(match.subtask_id, match.credits);
|
|
198
|
+
subTotal += match.credits;
|
|
199
|
+
}
|
|
200
|
+
const estimatedTotal = subTotal + ORCHESTRATION_FEE;
|
|
201
|
+
return {
|
|
202
|
+
estimated_total: estimatedTotal,
|
|
203
|
+
max_budget: this.maxBudget,
|
|
204
|
+
orchestration_fee: ORCHESTRATION_FEE,
|
|
205
|
+
per_task_spending: perTaskSpending,
|
|
206
|
+
requires_approval: estimatedTotal > this.maxBudget
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Checks whether orchestration can proceed without explicit approval.
|
|
211
|
+
*
|
|
212
|
+
* Returns true only when:
|
|
213
|
+
* 1. The budget does NOT require approval (estimated_total <= max_budget)
|
|
214
|
+
* 2. The BudgetManager confirms sufficient credits (respecting reserve floor)
|
|
215
|
+
*
|
|
216
|
+
* @param budget - ExecutionBudget from calculateBudget().
|
|
217
|
+
* @returns true if execution can proceed autonomously.
|
|
218
|
+
*/
|
|
219
|
+
canExecute(budget) {
|
|
220
|
+
if (budget.requires_approval) return false;
|
|
221
|
+
return this.budgetManager.canSpend(budget.estimated_total);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Checks budget after explicit user/agent approval.
|
|
225
|
+
*
|
|
226
|
+
* Ignores the requires_approval flag — used when the caller has already
|
|
227
|
+
* obtained explicit approval for the over-budget orchestration.
|
|
228
|
+
* Still enforces the reserve floor via BudgetManager.canSpend().
|
|
229
|
+
*
|
|
230
|
+
* @param budget - ExecutionBudget from calculateBudget().
|
|
231
|
+
* @returns true if the agent has sufficient credits (reserve floor check only).
|
|
232
|
+
*/
|
|
233
|
+
approveAndCheck(budget) {
|
|
234
|
+
return this.budgetManager.canSpend(budget.estimated_total);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// src/conductor/pipeline-orchestrator.ts
|
|
239
|
+
function computeWaves(subtasks) {
|
|
240
|
+
const waves = [];
|
|
241
|
+
const completed = /* @__PURE__ */ new Set();
|
|
242
|
+
const remaining = new Map(subtasks.map((s) => [s.id, s]));
|
|
243
|
+
while (remaining.size > 0) {
|
|
244
|
+
const wave = [];
|
|
245
|
+
for (const [id, task] of remaining) {
|
|
246
|
+
const depsResolved = task.depends_on.every((dep) => completed.has(dep));
|
|
247
|
+
if (depsResolved) {
|
|
248
|
+
wave.push(id);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (wave.length === 0) {
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
for (const id of wave) {
|
|
255
|
+
remaining.delete(id);
|
|
256
|
+
completed.add(id);
|
|
257
|
+
}
|
|
258
|
+
waves.push(wave);
|
|
259
|
+
}
|
|
260
|
+
return waves;
|
|
261
|
+
}
|
|
262
|
+
async function orchestrate(opts) {
|
|
263
|
+
const { subtasks, matches, gatewayToken, resolveAgentUrl, timeoutMs = 3e4, maxBudget } = opts;
|
|
264
|
+
const startTime = Date.now();
|
|
265
|
+
if (subtasks.length === 0) {
|
|
266
|
+
return {
|
|
267
|
+
success: true,
|
|
268
|
+
results: /* @__PURE__ */ new Map(),
|
|
269
|
+
total_credits: 0,
|
|
270
|
+
latency_ms: Date.now() - startTime
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
const results = /* @__PURE__ */ new Map();
|
|
274
|
+
const errors = [];
|
|
275
|
+
let totalCredits = 0;
|
|
276
|
+
const waves = computeWaves(subtasks);
|
|
277
|
+
const subtaskMap = new Map(subtasks.map((s) => [s.id, s]));
|
|
278
|
+
for (const wave of waves) {
|
|
279
|
+
if (maxBudget !== void 0 && totalCredits >= maxBudget) {
|
|
280
|
+
errors.push(`Budget exceeded: spent ${totalCredits} cr, max ${maxBudget} cr`);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
const executableIds = [];
|
|
284
|
+
for (const taskId of wave) {
|
|
285
|
+
const m = matches.get(taskId);
|
|
286
|
+
if (maxBudget !== void 0 && m && totalCredits + m.credits > maxBudget) {
|
|
287
|
+
errors.push(`Skipping task ${taskId}: would exceed budget (${totalCredits} + ${m.credits} > ${maxBudget})`);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
executableIds.push(taskId);
|
|
291
|
+
}
|
|
292
|
+
const waveResults = await Promise.allSettled(
|
|
293
|
+
executableIds.map(async (taskId) => {
|
|
294
|
+
const subtask = subtaskMap.get(taskId);
|
|
295
|
+
const m = matches.get(taskId);
|
|
296
|
+
if (!m) {
|
|
297
|
+
throw new Error(`No match found for subtask ${taskId}`);
|
|
298
|
+
}
|
|
299
|
+
const stepsContext = {};
|
|
300
|
+
for (const [id, val] of results) {
|
|
301
|
+
stepsContext[id] = val;
|
|
302
|
+
}
|
|
303
|
+
const interpContext = { steps: stepsContext, prev: void 0 };
|
|
304
|
+
if (subtask.depends_on.length > 0) {
|
|
305
|
+
const lastDep = subtask.depends_on[subtask.depends_on.length - 1];
|
|
306
|
+
interpContext.prev = results.get(lastDep);
|
|
307
|
+
}
|
|
308
|
+
const interpolatedParams = interpolateObject(
|
|
309
|
+
subtask.params,
|
|
310
|
+
interpContext
|
|
311
|
+
);
|
|
312
|
+
const primary = resolveAgentUrl(m.selected_agent);
|
|
313
|
+
try {
|
|
314
|
+
const res = await requestCapability({
|
|
315
|
+
gatewayUrl: primary.url,
|
|
316
|
+
token: gatewayToken,
|
|
317
|
+
cardId: primary.cardId,
|
|
318
|
+
params: interpolatedParams,
|
|
319
|
+
timeoutMs
|
|
320
|
+
});
|
|
321
|
+
return { taskId, result: res, credits: m.credits };
|
|
322
|
+
} catch (primaryErr) {
|
|
323
|
+
if (m.alternatives.length > 0) {
|
|
324
|
+
const alt = m.alternatives[0];
|
|
325
|
+
const altAgent = resolveAgentUrl(alt.agent);
|
|
326
|
+
try {
|
|
327
|
+
const altRes = await requestCapability({
|
|
328
|
+
gatewayUrl: altAgent.url,
|
|
329
|
+
token: gatewayToken,
|
|
330
|
+
cardId: altAgent.cardId,
|
|
331
|
+
params: interpolatedParams,
|
|
332
|
+
timeoutMs
|
|
333
|
+
});
|
|
334
|
+
return { taskId, result: altRes, credits: alt.credits };
|
|
335
|
+
} catch (altErr) {
|
|
336
|
+
throw new Error(
|
|
337
|
+
`Task ${taskId}: primary (${m.selected_agent}) failed: ${primaryErr instanceof Error ? primaryErr.message : String(primaryErr)}; alternative (${alt.agent}) failed: ${altErr instanceof Error ? altErr.message : String(altErr)}`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
throw new Error(
|
|
342
|
+
`Task ${taskId}: ${primaryErr instanceof Error ? primaryErr.message : String(primaryErr)}`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
})
|
|
346
|
+
);
|
|
347
|
+
for (const settlement of waveResults) {
|
|
348
|
+
if (settlement.status === "fulfilled") {
|
|
349
|
+
const { taskId, result, credits } = settlement.value;
|
|
350
|
+
results.set(taskId, result);
|
|
351
|
+
totalCredits += credits;
|
|
352
|
+
} else {
|
|
353
|
+
errors.push(settlement.reason instanceof Error ? settlement.reason.message : String(settlement.reason));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
success: errors.length === 0,
|
|
359
|
+
results,
|
|
360
|
+
total_credits: totalCredits,
|
|
361
|
+
latency_ms: Date.now() - startTime,
|
|
362
|
+
errors: errors.length > 0 ? errors : void 0
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export {
|
|
367
|
+
decompose,
|
|
368
|
+
matchSubTasks,
|
|
369
|
+
ORCHESTRATION_FEE,
|
|
370
|
+
BudgetController,
|
|
371
|
+
orchestrate
|
|
372
|
+
};
|