@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.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 (async function* () {
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(requiredTools, missingTools) {
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}. This is an embedded agent that can run within the parent application. Register DistriWidget for embedding the parent component. Required tools: ${requiredList}.`;
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
- uuidv4
2715
+ resolveTemplate,
2716
+ resolveValue,
2717
+ stepIcon,
2718
+ uuidv4,
2719
+ workflowProgress
2380
2720
  };
2381
2721
  //# sourceMappingURL=index.mjs.map