@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.js CHANGED
@@ -30,11 +30,13 @@ __export(index_exports, {
30
30
  DistriClient: () => DistriClient,
31
31
  DistriError: () => DistriError,
32
32
  ExternalToolValidationError: () => ExternalToolValidationError,
33
+ WorkflowRunner: () => WorkflowRunner,
33
34
  convertA2AMessageToDistri: () => convertA2AMessageToDistri,
34
35
  convertA2APartToDistri: () => convertA2APartToDistri,
35
36
  convertA2AStatusUpdateToDistri: () => convertA2AStatusUpdateToDistri,
36
37
  convertDistriMessageToA2A: () => convertDistriMessageToA2A,
37
38
  convertDistriPartToA2A: () => convertDistriPartToA2A,
39
+ countSteps: () => countSteps,
38
40
  createFailedToolResult: () => createFailedToolResult,
39
41
  createSuccessfulToolResult: () => createSuccessfulToolResult,
40
42
  decodeA2AStreamEvent: () => decodeA2AStreamEvent,
@@ -47,7 +49,11 @@ __export(index_exports, {
47
49
  isDistriMessage: () => isDistriMessage,
48
50
  processA2AMessagesData: () => processA2AMessagesData,
49
51
  processA2AStreamData: () => processA2AStreamData,
50
- uuidv4: () => uuidv4
52
+ resolveTemplate: () => resolveTemplate,
53
+ resolveValue: () => resolveValue,
54
+ stepIcon: () => stepIcon,
55
+ uuidv4: () => uuidv4,
56
+ workflowProgress: () => workflowProgress
51
57
  });
52
58
  module.exports = __toCommonJS(index_exports);
53
59
 
@@ -1415,6 +1421,27 @@ var _DistriClient = class _DistriClient {
1415
1421
  throw new DistriError(`Failed to fetch agent ${agentId}`, "FETCH_ERROR", error);
1416
1422
  }
1417
1423
  }
1424
+ /**
1425
+ * Fetch all available models grouped by provider, with configuration status.
1426
+ * Each provider includes `configured: boolean` indicating whether the
1427
+ * provider's required API key(s) are set on the server.
1428
+ */
1429
+ async fetchAvailableModels() {
1430
+ try {
1431
+ const response = await this.fetch(`/models`, {
1432
+ headers: {
1433
+ ...this.config.headers
1434
+ }
1435
+ });
1436
+ if (!response.ok) {
1437
+ throw new ApiError(`Failed to fetch models: ${response.statusText}`, response.status);
1438
+ }
1439
+ return await response.json();
1440
+ } catch (error) {
1441
+ if (error instanceof ApiError) throw error;
1442
+ throw new DistriError("Failed to fetch available models", "FETCH_ERROR", error);
1443
+ }
1444
+ }
1418
1445
  /**
1419
1446
  * Update an agent's definition (markdown only)
1420
1447
  */
@@ -2053,6 +2080,19 @@ var _DistriClient = class _DistriClient {
2053
2080
  * Enhanced fetch with retry logic and auth headers.
2054
2081
  * Exposed publicly for extensions like DistriHomeClient.
2055
2082
  */
2083
+ /** Call a registered tool via the server's /tools/call endpoint. */
2084
+ async callTool(toolName, input) {
2085
+ const resp = await this.fetch("/tools/call", {
2086
+ method: "POST",
2087
+ headers: { "Content-Type": "application/json" },
2088
+ body: JSON.stringify({ tool_name: toolName, input })
2089
+ });
2090
+ if (!resp.ok) {
2091
+ const text = await resp.text().catch(() => "");
2092
+ throw new Error(`Tool '${toolName}' failed (${resp.status}): ${text}`);
2093
+ }
2094
+ return resp.json();
2095
+ }
2056
2096
  async fetch(input, initialInit) {
2057
2097
  const url = `${this.config.baseUrl}${input}`;
2058
2098
  return this.fetchAbsolute(url, initialInit);
@@ -2241,7 +2281,7 @@ var Agent = class _Agent {
2241
2281
  const enhancedParams = this.enhanceParamsWithTools(params, tools);
2242
2282
  const a2aStream = this.client.sendMessageStream(this.agentDefinition.id, enhancedParams);
2243
2283
  const self = this;
2244
- return (async function* () {
2284
+ return async function* () {
2245
2285
  try {
2246
2286
  for await (const event of a2aStream) {
2247
2287
  const converted = decodeA2AStreamEvent(event);
@@ -2274,7 +2314,7 @@ var Agent = class _Agent {
2274
2314
  };
2275
2315
  yield runError;
2276
2316
  }
2277
- })();
2317
+ }();
2278
2318
  }
2279
2319
  /**
2280
2320
  * Validate that required external tools are registered before invoking.
@@ -2352,10 +2392,9 @@ var Agent = class _Agent {
2352
2392
  }
2353
2393
  return tools;
2354
2394
  }
2355
- formatExternalToolValidationMessage(requiredTools, missingTools) {
2356
- const requiredList = requiredTools.join(", ");
2395
+ formatExternalToolValidationMessage(_requiredTools, missingTools) {
2357
2396
  const missingList = missingTools.join(", ");
2358
- 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}.`;
2397
+ return `Agent has external tools that are not registered: ${missingList}.`;
2359
2398
  }
2360
2399
  /**
2361
2400
  * Register multiple hooks at once.
@@ -2399,6 +2438,307 @@ var Agent = class _Agent {
2399
2438
  return agentDefinitions.map((def) => new _Agent(def, client));
2400
2439
  }
2401
2440
  };
2441
+
2442
+ // src/workflow.ts
2443
+ function countSteps(workflow) {
2444
+ const counts = { pending: 0, blocked: 0, running: 0, done: 0, failed: 0, skipped: 0 };
2445
+ for (const step of workflow.steps) {
2446
+ const status = step.status || "pending";
2447
+ if (status in counts) counts[status]++;
2448
+ }
2449
+ return counts;
2450
+ }
2451
+ function workflowProgress(workflow) {
2452
+ if (workflow.steps.length === 0) return 100;
2453
+ const done = workflow.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
2454
+ return Math.round(done / workflow.steps.length * 100);
2455
+ }
2456
+ function stepIcon(status) {
2457
+ switch (status) {
2458
+ case "done":
2459
+ return "\u2705";
2460
+ case "failed":
2461
+ return "\u274C";
2462
+ case "running":
2463
+ return "\u23F3";
2464
+ case "skipped":
2465
+ return "\u23ED";
2466
+ case "blocked":
2467
+ return "\u{1F6AB}";
2468
+ case "pending":
2469
+ return "\u2B1C";
2470
+ }
2471
+ }
2472
+
2473
+ // src/workflow-runner.ts
2474
+ function resolvePath(obj, path) {
2475
+ let current = obj;
2476
+ for (const segment of path.split(".")) {
2477
+ if (current == null || typeof current !== "object") return void 0;
2478
+ current = current[segment];
2479
+ }
2480
+ return current;
2481
+ }
2482
+ function resolveReference(ref, ctx) {
2483
+ const dotIdx = ref.indexOf(".");
2484
+ if (dotIdx === -1) return void 0;
2485
+ const namespace = ref.substring(0, dotIdx);
2486
+ const path = ref.substring(dotIdx + 1);
2487
+ switch (namespace) {
2488
+ case "input":
2489
+ return resolvePath(ctx.input, path);
2490
+ case "steps":
2491
+ return resolvePath(ctx.steps, path);
2492
+ case "env":
2493
+ return resolvePath(ctx.env, path);
2494
+ case "context":
2495
+ return resolvePath(ctx.input, path) ?? resolvePath(ctx.steps, path) ?? resolvePath(ctx.env, path);
2496
+ default:
2497
+ return void 0;
2498
+ }
2499
+ }
2500
+ function resolveTemplate(template, ctx) {
2501
+ return template.replace(/\{([^}]+)\}/g, (match, ref) => {
2502
+ const resolved = resolveReference(ref, ctx);
2503
+ if (resolved === void 0) return match;
2504
+ return typeof resolved === "string" ? resolved : JSON.stringify(resolved);
2505
+ });
2506
+ }
2507
+ function resolveValue(value, ctx) {
2508
+ if (typeof value === "string") {
2509
+ const trimmed = value.trim();
2510
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") && !trimmed.slice(1).includes("{")) {
2511
+ const ref = trimmed.slice(1, -1);
2512
+ const resolved = resolveReference(ref, ctx);
2513
+ if (resolved !== void 0) return resolved;
2514
+ }
2515
+ return resolveTemplate(value, ctx);
2516
+ }
2517
+ if (Array.isArray(value)) {
2518
+ return value.map((v) => resolveValue(v, ctx));
2519
+ }
2520
+ if (value != null && typeof value === "object") {
2521
+ const result = {};
2522
+ for (const [k, v] of Object.entries(value)) {
2523
+ result[k] = resolveValue(v, ctx);
2524
+ }
2525
+ return result;
2526
+ }
2527
+ return value;
2528
+ }
2529
+ function resolveStepInput(step, ctx) {
2530
+ const mapping = step.input;
2531
+ if (mapping != null) {
2532
+ return resolveValue(mapping, ctx);
2533
+ }
2534
+ return ctx;
2535
+ }
2536
+ function detectCycles(steps) {
2537
+ const adj = /* @__PURE__ */ new Map();
2538
+ const ids = new Set(steps.map((s) => s.id));
2539
+ for (const step of steps) {
2540
+ const deps = step.depends_on || [];
2541
+ adj.set(step.id, deps);
2542
+ for (const dep of deps) {
2543
+ if (!ids.has(dep)) return `Step '${step.id}' depends on '${dep}' which does not exist`;
2544
+ }
2545
+ }
2546
+ const visited = /* @__PURE__ */ new Set();
2547
+ const inStack = /* @__PURE__ */ new Set();
2548
+ function dfs(node, path) {
2549
+ visited.add(node);
2550
+ inStack.add(node);
2551
+ path.push(node);
2552
+ for (const dep of adj.get(node) || []) {
2553
+ if (!visited.has(dep)) {
2554
+ const cycle = dfs(dep, path);
2555
+ if (cycle) return cycle;
2556
+ } else if (inStack.has(dep)) {
2557
+ const cycleStart = path.indexOf(dep);
2558
+ return `Circular dependency: ${path.slice(cycleStart).join(" \u2192 ")} \u2192 ${dep}`;
2559
+ }
2560
+ }
2561
+ inStack.delete(node);
2562
+ path.pop();
2563
+ return null;
2564
+ }
2565
+ for (const step of steps) {
2566
+ if (!visited.has(step.id)) {
2567
+ const cycle = dfs(step.id, []);
2568
+ if (cycle) return cycle;
2569
+ }
2570
+ }
2571
+ return null;
2572
+ }
2573
+ var WorkflowRunner = class {
2574
+ constructor(client, options = {}) {
2575
+ this.client = client;
2576
+ this.options = options;
2577
+ }
2578
+ /** Run a workflow to completion. Returns an async generator of WorkflowEvents. */
2579
+ async *run(definition, input = {}) {
2580
+ const cycle = detectCycles(definition.steps);
2581
+ if (cycle) throw new Error(cycle);
2582
+ const ctx = {
2583
+ input,
2584
+ steps: {},
2585
+ env: this.options.env || {}
2586
+ };
2587
+ const workflow = JSON.parse(JSON.stringify(definition));
2588
+ workflow.status = "running";
2589
+ workflow.context = ctx;
2590
+ for (const step of workflow.steps) {
2591
+ step.status = step.status || "pending";
2592
+ step.depends_on = step.depends_on || [];
2593
+ step.execution = step.execution || "sequential";
2594
+ }
2595
+ yield {
2596
+ event: "workflow_started",
2597
+ workflow_id: workflow.id,
2598
+ workflow_type: workflow.workflow_type,
2599
+ total_steps: workflow.steps.length
2600
+ };
2601
+ while (true) {
2602
+ const runnableIndices = this.findRunnable(workflow);
2603
+ if (runnableIndices.length === 0) break;
2604
+ for (const idx of runnableIndices) {
2605
+ const step = workflow.steps[idx];
2606
+ yield {
2607
+ event: "step_started",
2608
+ workflow_id: workflow.id,
2609
+ step_id: step.id,
2610
+ step_label: step.label
2611
+ };
2612
+ step.status = "running";
2613
+ step.started_at = (/* @__PURE__ */ new Date()).toISOString();
2614
+ try {
2615
+ const resolvedInput = resolveStepInput(step, ctx);
2616
+ const result = this.options.executeStep ? await this.options.executeStep(step, resolvedInput, ctx) : await this.executeStep(step, resolvedInput, ctx);
2617
+ step.status = result.status;
2618
+ step.result = result.result;
2619
+ step.error = result.error ?? null;
2620
+ step.completed_at = (/* @__PURE__ */ new Date()).toISOString();
2621
+ if (result.result != null) {
2622
+ ctx.steps[step.id] = result.result;
2623
+ }
2624
+ if (result.status === "failed") {
2625
+ yield {
2626
+ event: "step_failed",
2627
+ workflow_id: workflow.id,
2628
+ step_id: step.id,
2629
+ step_label: step.label,
2630
+ error: result.error || "Unknown error"
2631
+ };
2632
+ workflow.status = "failed";
2633
+ break;
2634
+ } else {
2635
+ yield {
2636
+ event: "step_completed",
2637
+ workflow_id: workflow.id,
2638
+ step_id: step.id,
2639
+ step_label: step.label,
2640
+ result: result.result
2641
+ };
2642
+ }
2643
+ } catch (err) {
2644
+ const errMsg = err instanceof Error ? err.message : String(err);
2645
+ step.status = "failed";
2646
+ step.error = errMsg;
2647
+ step.completed_at = (/* @__PURE__ */ new Date()).toISOString();
2648
+ yield {
2649
+ event: "step_failed",
2650
+ workflow_id: workflow.id,
2651
+ step_id: step.id,
2652
+ step_label: step.label,
2653
+ error: errMsg
2654
+ };
2655
+ workflow.status = "failed";
2656
+ break;
2657
+ }
2658
+ }
2659
+ if (workflow.status === "failed") break;
2660
+ }
2661
+ if (workflow.status !== "failed") {
2662
+ const allDone = workflow.steps.every((s) => s.status === "done" || s.status === "skipped");
2663
+ workflow.status = allDone ? "completed" : "failed";
2664
+ }
2665
+ const stepsDone = workflow.steps.filter((s) => s.status === "done").length;
2666
+ const stepsFailed = workflow.steps.filter((s) => s.status === "failed").length;
2667
+ yield {
2668
+ event: "workflow_completed",
2669
+ workflow_id: workflow.id,
2670
+ status: workflow.status,
2671
+ steps_done: stepsDone,
2672
+ steps_failed: stepsFailed
2673
+ };
2674
+ }
2675
+ /** Find indices of steps that are pending with all dependencies met. */
2676
+ findRunnable(workflow) {
2677
+ const runnable = [];
2678
+ for (let i = 0; i < workflow.steps.length; i++) {
2679
+ const step = workflow.steps[i];
2680
+ if (step.status !== "pending") continue;
2681
+ const depsMet = (step.depends_on || []).every(
2682
+ (depId) => workflow.steps.some((s) => s.id === depId && s.status === "done")
2683
+ );
2684
+ if (depsMet) runnable.push(i);
2685
+ }
2686
+ return runnable;
2687
+ }
2688
+ /** Default step executor — handles tool_call, api_call, checkpoint, etc. */
2689
+ async executeStep(step, resolvedInput, ctx) {
2690
+ switch (step.kind.type) {
2691
+ case "tool_call":
2692
+ return this.executeToolCall(step.kind, resolvedInput);
2693
+ case "api_call":
2694
+ return this.executeApiCall(step, step.kind, ctx);
2695
+ case "checkpoint":
2696
+ return { status: "done", result: { message: step.kind.message } };
2697
+ case "agent_run":
2698
+ return { status: "done", result: { deferred: true, agent_id: step.kind.agent_id, prompt: step.kind.prompt } };
2699
+ case "script":
2700
+ return { status: "done", result: { deferred: true, command: step.kind.command } };
2701
+ case "condition":
2702
+ return { status: "done", result: { expression: step.kind.expression, evaluated: true } };
2703
+ default:
2704
+ return { status: "failed", error: `Unknown step kind: ${step.kind.type}` };
2705
+ }
2706
+ }
2707
+ async executeToolCall(kind, resolvedInput) {
2708
+ try {
2709
+ const result = await this.client.callTool(kind.tool_name, resolvedInput);
2710
+ return { status: "done", result };
2711
+ } catch (err) {
2712
+ return { status: "failed", error: `Tool '${kind.tool_name}' failed: ${err instanceof Error ? err.message : err}` };
2713
+ }
2714
+ }
2715
+ async executeApiCall(step, kind, ctx) {
2716
+ let url = resolveTemplate(kind.url, ctx);
2717
+ let init = {
2718
+ method: kind.method,
2719
+ headers: { "Content-Type": "application/json", ...kind.headers || {} }
2720
+ };
2721
+ if (kind.body) {
2722
+ init.body = JSON.stringify(resolveValue(kind.body, ctx));
2723
+ }
2724
+ if (this.options.buildRequest) {
2725
+ const customized = this.options.buildRequest(step, init, url);
2726
+ url = customized.url;
2727
+ init = customized.init;
2728
+ }
2729
+ try {
2730
+ const resp = await fetch(url, init);
2731
+ const body = await resp.json().catch(() => null);
2732
+ if (resp.ok) {
2733
+ return { status: "done", result: { status: resp.status, body } };
2734
+ } else {
2735
+ return { status: "failed", error: `HTTP ${resp.status} \u2014 ${JSON.stringify(body)}` };
2736
+ }
2737
+ } catch (err) {
2738
+ return { status: "failed", error: `Request failed: ${err instanceof Error ? err.message : err}` };
2739
+ }
2740
+ }
2741
+ };
2402
2742
  // Annotate the CommonJS export names for ESM import in node:
2403
2743
  0 && (module.exports = {
2404
2744
  A2AProtocolError,
@@ -2409,11 +2749,13 @@ var Agent = class _Agent {
2409
2749
  DistriClient,
2410
2750
  DistriError,
2411
2751
  ExternalToolValidationError,
2752
+ WorkflowRunner,
2412
2753
  convertA2AMessageToDistri,
2413
2754
  convertA2APartToDistri,
2414
2755
  convertA2AStatusUpdateToDistri,
2415
2756
  convertDistriMessageToA2A,
2416
2757
  convertDistriPartToA2A,
2758
+ countSteps,
2417
2759
  createFailedToolResult,
2418
2760
  createSuccessfulToolResult,
2419
2761
  decodeA2AStreamEvent,
@@ -2426,6 +2768,10 @@ var Agent = class _Agent {
2426
2768
  isDistriMessage,
2427
2769
  processA2AMessagesData,
2428
2770
  processA2AStreamData,
2429
- uuidv4
2771
+ resolveTemplate,
2772
+ resolveValue,
2773
+ stepIcon,
2774
+ uuidv4,
2775
+ workflowProgress
2430
2776
  });
2431
2777
  //# sourceMappingURL=index.js.map