@distri/core 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +275 -4
- package/dist/index.d.ts +275 -4
- package/dist/index.js +353 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +346 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/dist/index.mjs
CHANGED
|
@@ -1366,6 +1366,27 @@ var _DistriClient = class _DistriClient {
|
|
|
1366
1366
|
throw new DistriError(`Failed to fetch agent ${agentId}`, "FETCH_ERROR", error);
|
|
1367
1367
|
}
|
|
1368
1368
|
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Fetch all available models grouped by provider, with configuration status.
|
|
1371
|
+
* Each provider includes `configured: boolean` indicating whether the
|
|
1372
|
+
* provider's required API key(s) are set on the server.
|
|
1373
|
+
*/
|
|
1374
|
+
async fetchAvailableModels() {
|
|
1375
|
+
try {
|
|
1376
|
+
const response = await this.fetch(`/models`, {
|
|
1377
|
+
headers: {
|
|
1378
|
+
...this.config.headers
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
if (!response.ok) {
|
|
1382
|
+
throw new ApiError(`Failed to fetch models: ${response.statusText}`, response.status);
|
|
1383
|
+
}
|
|
1384
|
+
return await response.json();
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
if (error instanceof ApiError) throw error;
|
|
1387
|
+
throw new DistriError("Failed to fetch available models", "FETCH_ERROR", error);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1369
1390
|
/**
|
|
1370
1391
|
* Update an agent's definition (markdown only)
|
|
1371
1392
|
*/
|
|
@@ -2004,6 +2025,19 @@ var _DistriClient = class _DistriClient {
|
|
|
2004
2025
|
* Enhanced fetch with retry logic and auth headers.
|
|
2005
2026
|
* Exposed publicly for extensions like DistriHomeClient.
|
|
2006
2027
|
*/
|
|
2028
|
+
/** Call a registered tool via the server's /tools/call endpoint. */
|
|
2029
|
+
async callTool(toolName, input) {
|
|
2030
|
+
const resp = await this.fetch("/tools/call", {
|
|
2031
|
+
method: "POST",
|
|
2032
|
+
headers: { "Content-Type": "application/json" },
|
|
2033
|
+
body: JSON.stringify({ tool_name: toolName, input })
|
|
2034
|
+
});
|
|
2035
|
+
if (!resp.ok) {
|
|
2036
|
+
const text = await resp.text().catch(() => "");
|
|
2037
|
+
throw new Error(`Tool '${toolName}' failed (${resp.status}): ${text}`);
|
|
2038
|
+
}
|
|
2039
|
+
return resp.json();
|
|
2040
|
+
}
|
|
2007
2041
|
async fetch(input, initialInit) {
|
|
2008
2042
|
const url = `${this.config.baseUrl}${input}`;
|
|
2009
2043
|
return this.fetchAbsolute(url, initialInit);
|
|
@@ -2192,7 +2226,7 @@ var Agent = class _Agent {
|
|
|
2192
2226
|
const enhancedParams = this.enhanceParamsWithTools(params, tools);
|
|
2193
2227
|
const a2aStream = this.client.sendMessageStream(this.agentDefinition.id, enhancedParams);
|
|
2194
2228
|
const self = this;
|
|
2195
|
-
return
|
|
2229
|
+
return async function* () {
|
|
2196
2230
|
try {
|
|
2197
2231
|
for await (const event of a2aStream) {
|
|
2198
2232
|
const converted = decodeA2AStreamEvent(event);
|
|
@@ -2225,7 +2259,7 @@ var Agent = class _Agent {
|
|
|
2225
2259
|
};
|
|
2226
2260
|
yield runError;
|
|
2227
2261
|
}
|
|
2228
|
-
}
|
|
2262
|
+
}();
|
|
2229
2263
|
}
|
|
2230
2264
|
/**
|
|
2231
2265
|
* Validate that required external tools are registered before invoking.
|
|
@@ -2303,10 +2337,9 @@ var Agent = class _Agent {
|
|
|
2303
2337
|
}
|
|
2304
2338
|
return tools;
|
|
2305
2339
|
}
|
|
2306
|
-
formatExternalToolValidationMessage(
|
|
2307
|
-
const requiredList = requiredTools.join(", ");
|
|
2340
|
+
formatExternalToolValidationMessage(_requiredTools, missingTools) {
|
|
2308
2341
|
const missingList = missingTools.join(", ");
|
|
2309
|
-
return `Agent has external tools that are not registered: ${missingList}
|
|
2342
|
+
return `Agent has external tools that are not registered: ${missingList}.`;
|
|
2310
2343
|
}
|
|
2311
2344
|
/**
|
|
2312
2345
|
* Register multiple hooks at once.
|
|
@@ -2350,6 +2383,307 @@ var Agent = class _Agent {
|
|
|
2350
2383
|
return agentDefinitions.map((def) => new _Agent(def, client));
|
|
2351
2384
|
}
|
|
2352
2385
|
};
|
|
2386
|
+
|
|
2387
|
+
// src/workflow.ts
|
|
2388
|
+
function countSteps(workflow) {
|
|
2389
|
+
const counts = { pending: 0, blocked: 0, running: 0, done: 0, failed: 0, skipped: 0 };
|
|
2390
|
+
for (const step of workflow.steps) {
|
|
2391
|
+
const status = step.status || "pending";
|
|
2392
|
+
if (status in counts) counts[status]++;
|
|
2393
|
+
}
|
|
2394
|
+
return counts;
|
|
2395
|
+
}
|
|
2396
|
+
function workflowProgress(workflow) {
|
|
2397
|
+
if (workflow.steps.length === 0) return 100;
|
|
2398
|
+
const done = workflow.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
|
|
2399
|
+
return Math.round(done / workflow.steps.length * 100);
|
|
2400
|
+
}
|
|
2401
|
+
function stepIcon(status) {
|
|
2402
|
+
switch (status) {
|
|
2403
|
+
case "done":
|
|
2404
|
+
return "\u2705";
|
|
2405
|
+
case "failed":
|
|
2406
|
+
return "\u274C";
|
|
2407
|
+
case "running":
|
|
2408
|
+
return "\u23F3";
|
|
2409
|
+
case "skipped":
|
|
2410
|
+
return "\u23ED";
|
|
2411
|
+
case "blocked":
|
|
2412
|
+
return "\u{1F6AB}";
|
|
2413
|
+
case "pending":
|
|
2414
|
+
return "\u2B1C";
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// src/workflow-runner.ts
|
|
2419
|
+
function resolvePath(obj, path) {
|
|
2420
|
+
let current = obj;
|
|
2421
|
+
for (const segment of path.split(".")) {
|
|
2422
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
2423
|
+
current = current[segment];
|
|
2424
|
+
}
|
|
2425
|
+
return current;
|
|
2426
|
+
}
|
|
2427
|
+
function resolveReference(ref, ctx) {
|
|
2428
|
+
const dotIdx = ref.indexOf(".");
|
|
2429
|
+
if (dotIdx === -1) return void 0;
|
|
2430
|
+
const namespace = ref.substring(0, dotIdx);
|
|
2431
|
+
const path = ref.substring(dotIdx + 1);
|
|
2432
|
+
switch (namespace) {
|
|
2433
|
+
case "input":
|
|
2434
|
+
return resolvePath(ctx.input, path);
|
|
2435
|
+
case "steps":
|
|
2436
|
+
return resolvePath(ctx.steps, path);
|
|
2437
|
+
case "env":
|
|
2438
|
+
return resolvePath(ctx.env, path);
|
|
2439
|
+
case "context":
|
|
2440
|
+
return resolvePath(ctx.input, path) ?? resolvePath(ctx.steps, path) ?? resolvePath(ctx.env, path);
|
|
2441
|
+
default:
|
|
2442
|
+
return void 0;
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
function resolveTemplate(template, ctx) {
|
|
2446
|
+
return template.replace(/\{([^}]+)\}/g, (match, ref) => {
|
|
2447
|
+
const resolved = resolveReference(ref, ctx);
|
|
2448
|
+
if (resolved === void 0) return match;
|
|
2449
|
+
return typeof resolved === "string" ? resolved : JSON.stringify(resolved);
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
function resolveValue(value, ctx) {
|
|
2453
|
+
if (typeof value === "string") {
|
|
2454
|
+
const trimmed = value.trim();
|
|
2455
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") && !trimmed.slice(1).includes("{")) {
|
|
2456
|
+
const ref = trimmed.slice(1, -1);
|
|
2457
|
+
const resolved = resolveReference(ref, ctx);
|
|
2458
|
+
if (resolved !== void 0) return resolved;
|
|
2459
|
+
}
|
|
2460
|
+
return resolveTemplate(value, ctx);
|
|
2461
|
+
}
|
|
2462
|
+
if (Array.isArray(value)) {
|
|
2463
|
+
return value.map((v) => resolveValue(v, ctx));
|
|
2464
|
+
}
|
|
2465
|
+
if (value != null && typeof value === "object") {
|
|
2466
|
+
const result = {};
|
|
2467
|
+
for (const [k, v] of Object.entries(value)) {
|
|
2468
|
+
result[k] = resolveValue(v, ctx);
|
|
2469
|
+
}
|
|
2470
|
+
return result;
|
|
2471
|
+
}
|
|
2472
|
+
return value;
|
|
2473
|
+
}
|
|
2474
|
+
function resolveStepInput(step, ctx) {
|
|
2475
|
+
const mapping = step.input;
|
|
2476
|
+
if (mapping != null) {
|
|
2477
|
+
return resolveValue(mapping, ctx);
|
|
2478
|
+
}
|
|
2479
|
+
return ctx;
|
|
2480
|
+
}
|
|
2481
|
+
function detectCycles(steps) {
|
|
2482
|
+
const adj = /* @__PURE__ */ new Map();
|
|
2483
|
+
const ids = new Set(steps.map((s) => s.id));
|
|
2484
|
+
for (const step of steps) {
|
|
2485
|
+
const deps = step.depends_on || [];
|
|
2486
|
+
adj.set(step.id, deps);
|
|
2487
|
+
for (const dep of deps) {
|
|
2488
|
+
if (!ids.has(dep)) return `Step '${step.id}' depends on '${dep}' which does not exist`;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2492
|
+
const inStack = /* @__PURE__ */ new Set();
|
|
2493
|
+
function dfs(node, path) {
|
|
2494
|
+
visited.add(node);
|
|
2495
|
+
inStack.add(node);
|
|
2496
|
+
path.push(node);
|
|
2497
|
+
for (const dep of adj.get(node) || []) {
|
|
2498
|
+
if (!visited.has(dep)) {
|
|
2499
|
+
const cycle = dfs(dep, path);
|
|
2500
|
+
if (cycle) return cycle;
|
|
2501
|
+
} else if (inStack.has(dep)) {
|
|
2502
|
+
const cycleStart = path.indexOf(dep);
|
|
2503
|
+
return `Circular dependency: ${path.slice(cycleStart).join(" \u2192 ")} \u2192 ${dep}`;
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
inStack.delete(node);
|
|
2507
|
+
path.pop();
|
|
2508
|
+
return null;
|
|
2509
|
+
}
|
|
2510
|
+
for (const step of steps) {
|
|
2511
|
+
if (!visited.has(step.id)) {
|
|
2512
|
+
const cycle = dfs(step.id, []);
|
|
2513
|
+
if (cycle) return cycle;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
return null;
|
|
2517
|
+
}
|
|
2518
|
+
var WorkflowRunner = class {
|
|
2519
|
+
constructor(client, options = {}) {
|
|
2520
|
+
this.client = client;
|
|
2521
|
+
this.options = options;
|
|
2522
|
+
}
|
|
2523
|
+
/** Run a workflow to completion. Returns an async generator of WorkflowEvents. */
|
|
2524
|
+
async *run(definition, input = {}) {
|
|
2525
|
+
const cycle = detectCycles(definition.steps);
|
|
2526
|
+
if (cycle) throw new Error(cycle);
|
|
2527
|
+
const ctx = {
|
|
2528
|
+
input,
|
|
2529
|
+
steps: {},
|
|
2530
|
+
env: this.options.env || {}
|
|
2531
|
+
};
|
|
2532
|
+
const workflow = JSON.parse(JSON.stringify(definition));
|
|
2533
|
+
workflow.status = "running";
|
|
2534
|
+
workflow.context = ctx;
|
|
2535
|
+
for (const step of workflow.steps) {
|
|
2536
|
+
step.status = step.status || "pending";
|
|
2537
|
+
step.depends_on = step.depends_on || [];
|
|
2538
|
+
step.execution = step.execution || "sequential";
|
|
2539
|
+
}
|
|
2540
|
+
yield {
|
|
2541
|
+
event: "workflow_started",
|
|
2542
|
+
workflow_id: workflow.id,
|
|
2543
|
+
workflow_type: workflow.workflow_type,
|
|
2544
|
+
total_steps: workflow.steps.length
|
|
2545
|
+
};
|
|
2546
|
+
while (true) {
|
|
2547
|
+
const runnableIndices = this.findRunnable(workflow);
|
|
2548
|
+
if (runnableIndices.length === 0) break;
|
|
2549
|
+
for (const idx of runnableIndices) {
|
|
2550
|
+
const step = workflow.steps[idx];
|
|
2551
|
+
yield {
|
|
2552
|
+
event: "step_started",
|
|
2553
|
+
workflow_id: workflow.id,
|
|
2554
|
+
step_id: step.id,
|
|
2555
|
+
step_label: step.label
|
|
2556
|
+
};
|
|
2557
|
+
step.status = "running";
|
|
2558
|
+
step.started_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2559
|
+
try {
|
|
2560
|
+
const resolvedInput = resolveStepInput(step, ctx);
|
|
2561
|
+
const result = this.options.executeStep ? await this.options.executeStep(step, resolvedInput, ctx) : await this.executeStep(step, resolvedInput, ctx);
|
|
2562
|
+
step.status = result.status;
|
|
2563
|
+
step.result = result.result;
|
|
2564
|
+
step.error = result.error ?? null;
|
|
2565
|
+
step.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2566
|
+
if (result.result != null) {
|
|
2567
|
+
ctx.steps[step.id] = result.result;
|
|
2568
|
+
}
|
|
2569
|
+
if (result.status === "failed") {
|
|
2570
|
+
yield {
|
|
2571
|
+
event: "step_failed",
|
|
2572
|
+
workflow_id: workflow.id,
|
|
2573
|
+
step_id: step.id,
|
|
2574
|
+
step_label: step.label,
|
|
2575
|
+
error: result.error || "Unknown error"
|
|
2576
|
+
};
|
|
2577
|
+
workflow.status = "failed";
|
|
2578
|
+
break;
|
|
2579
|
+
} else {
|
|
2580
|
+
yield {
|
|
2581
|
+
event: "step_completed",
|
|
2582
|
+
workflow_id: workflow.id,
|
|
2583
|
+
step_id: step.id,
|
|
2584
|
+
step_label: step.label,
|
|
2585
|
+
result: result.result
|
|
2586
|
+
};
|
|
2587
|
+
}
|
|
2588
|
+
} catch (err) {
|
|
2589
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2590
|
+
step.status = "failed";
|
|
2591
|
+
step.error = errMsg;
|
|
2592
|
+
step.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2593
|
+
yield {
|
|
2594
|
+
event: "step_failed",
|
|
2595
|
+
workflow_id: workflow.id,
|
|
2596
|
+
step_id: step.id,
|
|
2597
|
+
step_label: step.label,
|
|
2598
|
+
error: errMsg
|
|
2599
|
+
};
|
|
2600
|
+
workflow.status = "failed";
|
|
2601
|
+
break;
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
if (workflow.status === "failed") break;
|
|
2605
|
+
}
|
|
2606
|
+
if (workflow.status !== "failed") {
|
|
2607
|
+
const allDone = workflow.steps.every((s) => s.status === "done" || s.status === "skipped");
|
|
2608
|
+
workflow.status = allDone ? "completed" : "failed";
|
|
2609
|
+
}
|
|
2610
|
+
const stepsDone = workflow.steps.filter((s) => s.status === "done").length;
|
|
2611
|
+
const stepsFailed = workflow.steps.filter((s) => s.status === "failed").length;
|
|
2612
|
+
yield {
|
|
2613
|
+
event: "workflow_completed",
|
|
2614
|
+
workflow_id: workflow.id,
|
|
2615
|
+
status: workflow.status,
|
|
2616
|
+
steps_done: stepsDone,
|
|
2617
|
+
steps_failed: stepsFailed
|
|
2618
|
+
};
|
|
2619
|
+
}
|
|
2620
|
+
/** Find indices of steps that are pending with all dependencies met. */
|
|
2621
|
+
findRunnable(workflow) {
|
|
2622
|
+
const runnable = [];
|
|
2623
|
+
for (let i = 0; i < workflow.steps.length; i++) {
|
|
2624
|
+
const step = workflow.steps[i];
|
|
2625
|
+
if (step.status !== "pending") continue;
|
|
2626
|
+
const depsMet = (step.depends_on || []).every(
|
|
2627
|
+
(depId) => workflow.steps.some((s) => s.id === depId && s.status === "done")
|
|
2628
|
+
);
|
|
2629
|
+
if (depsMet) runnable.push(i);
|
|
2630
|
+
}
|
|
2631
|
+
return runnable;
|
|
2632
|
+
}
|
|
2633
|
+
/** Default step executor — handles tool_call, api_call, checkpoint, etc. */
|
|
2634
|
+
async executeStep(step, resolvedInput, ctx) {
|
|
2635
|
+
switch (step.kind.type) {
|
|
2636
|
+
case "tool_call":
|
|
2637
|
+
return this.executeToolCall(step.kind, resolvedInput);
|
|
2638
|
+
case "api_call":
|
|
2639
|
+
return this.executeApiCall(step, step.kind, ctx);
|
|
2640
|
+
case "checkpoint":
|
|
2641
|
+
return { status: "done", result: { message: step.kind.message } };
|
|
2642
|
+
case "agent_run":
|
|
2643
|
+
return { status: "done", result: { deferred: true, agent_id: step.kind.agent_id, prompt: step.kind.prompt } };
|
|
2644
|
+
case "script":
|
|
2645
|
+
return { status: "done", result: { deferred: true, command: step.kind.command } };
|
|
2646
|
+
case "condition":
|
|
2647
|
+
return { status: "done", result: { expression: step.kind.expression, evaluated: true } };
|
|
2648
|
+
default:
|
|
2649
|
+
return { status: "failed", error: `Unknown step kind: ${step.kind.type}` };
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
async executeToolCall(kind, resolvedInput) {
|
|
2653
|
+
try {
|
|
2654
|
+
const result = await this.client.callTool(kind.tool_name, resolvedInput);
|
|
2655
|
+
return { status: "done", result };
|
|
2656
|
+
} catch (err) {
|
|
2657
|
+
return { status: "failed", error: `Tool '${kind.tool_name}' failed: ${err instanceof Error ? err.message : err}` };
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
async executeApiCall(step, kind, ctx) {
|
|
2661
|
+
let url = resolveTemplate(kind.url, ctx);
|
|
2662
|
+
let init = {
|
|
2663
|
+
method: kind.method,
|
|
2664
|
+
headers: { "Content-Type": "application/json", ...kind.headers || {} }
|
|
2665
|
+
};
|
|
2666
|
+
if (kind.body) {
|
|
2667
|
+
init.body = JSON.stringify(resolveValue(kind.body, ctx));
|
|
2668
|
+
}
|
|
2669
|
+
if (this.options.buildRequest) {
|
|
2670
|
+
const customized = this.options.buildRequest(step, init, url);
|
|
2671
|
+
url = customized.url;
|
|
2672
|
+
init = customized.init;
|
|
2673
|
+
}
|
|
2674
|
+
try {
|
|
2675
|
+
const resp = await fetch(url, init);
|
|
2676
|
+
const body = await resp.json().catch(() => null);
|
|
2677
|
+
if (resp.ok) {
|
|
2678
|
+
return { status: "done", result: { status: resp.status, body } };
|
|
2679
|
+
} else {
|
|
2680
|
+
return { status: "failed", error: `HTTP ${resp.status} \u2014 ${JSON.stringify(body)}` };
|
|
2681
|
+
}
|
|
2682
|
+
} catch (err) {
|
|
2683
|
+
return { status: "failed", error: `Request failed: ${err instanceof Error ? err.message : err}` };
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
};
|
|
2353
2687
|
export {
|
|
2354
2688
|
A2AProtocolError,
|
|
2355
2689
|
Agent,
|
|
@@ -2359,11 +2693,13 @@ export {
|
|
|
2359
2693
|
DistriClient,
|
|
2360
2694
|
DistriError,
|
|
2361
2695
|
ExternalToolValidationError,
|
|
2696
|
+
WorkflowRunner,
|
|
2362
2697
|
convertA2AMessageToDistri,
|
|
2363
2698
|
convertA2APartToDistri,
|
|
2364
2699
|
convertA2AStatusUpdateToDistri,
|
|
2365
2700
|
convertDistriMessageToA2A,
|
|
2366
2701
|
convertDistriPartToA2A,
|
|
2702
|
+
countSteps,
|
|
2367
2703
|
createFailedToolResult,
|
|
2368
2704
|
createSuccessfulToolResult,
|
|
2369
2705
|
decodeA2AStreamEvent,
|
|
@@ -2376,6 +2712,10 @@ export {
|
|
|
2376
2712
|
isDistriMessage,
|
|
2377
2713
|
processA2AMessagesData,
|
|
2378
2714
|
processA2AStreamData,
|
|
2379
|
-
|
|
2715
|
+
resolveTemplate,
|
|
2716
|
+
resolveValue,
|
|
2717
|
+
stepIcon,
|
|
2718
|
+
uuidv4,
|
|
2719
|
+
workflowProgress
|
|
2380
2720
|
};
|
|
2381
2721
|
//# sourceMappingURL=index.mjs.map
|