agent-worker 0.11.0 → 0.13.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.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { D as FRONTIER_MODELS, T as normalizeBackendType, j as getDefaultModel, n as createBackend } from "../backends-DLaP0rMW.mjs";
3
- import { t as AgentWorker } from "../worker-CJ5_b2_q.mjs";
2
+ import { I as getDefaultModel, j as FRONTIER_MODELS, k as normalizeBackendType, n as createBackend } from "../backends-CziIqKRg.mjs";
3
+ import { t as AgentWorker } from "../worker-DBJ8136Q.mjs";
4
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import { dirname, isAbsolute, join, relative } from "node:path";
6
6
  import { appendFile, mkdir, open, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
@@ -9,7 +9,6 @@ import { homedir } from "node:os";
9
9
  import { spawn } from "node:child_process";
10
10
  import { Command, Option } from "commander";
11
11
  import { Hono } from "hono";
12
- import { cors } from "hono/cors";
13
12
  import { streamSSE } from "hono/streaming";
14
13
  import { randomUUID } from "node:crypto";
15
14
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -98,7 +97,8 @@ var LocalWorker = class {
98
97
  model: config.model,
99
98
  system: config.system,
100
99
  tools: {},
101
- backend
100
+ backend,
101
+ provider: config.provider
102
102
  }, restore);
103
103
  }
104
104
  send(input, options) {
@@ -1430,17 +1430,22 @@ function createFileContextProvider(contextDir, validAgents) {
1430
1430
  * Registry (configs) — what agents exist and their configuration
1431
1431
  * StateStore (store) — conversation history and usage (pluggable)
1432
1432
  * WorkerHandle (workers) — execution, local or remote
1433
+ * Workflows (workflows) — running workflow instances with controllers
1433
1434
  *
1434
1435
  * The daemon is pure glue: receive request → lookup config →
1435
1436
  * dispatch to worker → persist state → return response.
1436
1437
  *
1437
- * 9 HTTP endpoints:
1438
+ * HTTP endpoints:
1438
1439
  * GET /health, POST /shutdown
1439
1440
  * GET/POST /agents, GET/DELETE /agents/:name
1440
1441
  * POST /run (SSE), POST /serve
1442
+ * GET/POST /workflows, DELETE /workflows/:name/:tag
1441
1443
  * ALL /mcp
1442
1444
  */
1443
- var daemon_exports = /* @__PURE__ */ __exportAll({ startDaemon: () => startDaemon });
1445
+ var daemon_exports = /* @__PURE__ */ __exportAll({
1446
+ createDaemonApp: () => createDaemonApp,
1447
+ startDaemon: () => startDaemon
1448
+ });
1444
1449
  let state = null;
1445
1450
  let shuttingDown = false;
1446
1451
  const mcpSessions = /* @__PURE__ */ new Map();
@@ -1448,10 +1453,14 @@ async function gracefulShutdown() {
1448
1453
  if (shuttingDown) return;
1449
1454
  shuttingDown = true;
1450
1455
  if (state) {
1456
+ for (const [, wf] of state.workflows) try {
1457
+ await wf.shutdown();
1458
+ } catch {}
1459
+ state.workflows.clear();
1451
1460
  for (const [name, handle] of state.workers) try {
1452
1461
  await state.store.save(name, handle.getState());
1453
1462
  } catch {}
1454
- await state.server.close();
1463
+ if (state.server) await state.server.close();
1455
1464
  }
1456
1465
  for (const [, session] of mcpSessions) try {
1457
1466
  await session.transport.close();
@@ -1460,28 +1469,45 @@ async function gracefulShutdown() {
1460
1469
  removeDaemonInfo();
1461
1470
  process.exit(0);
1462
1471
  }
1463
- function getWorkflowAgentNames$1(workflow, tag) {
1464
- if (!state) return [];
1465
- return [...state.configs.values()].filter((c) => c.workflow === workflow && c.tag === tag).map((c) => c.name);
1466
- }
1467
- async function startDaemon(config = {}) {
1468
- const existing = isDaemonRunning();
1469
- if (existing) {
1470
- console.error(`Daemon already running: pid=${existing.pid} port=${existing.port}`);
1471
- process.exit(1);
1472
+ /** Safe JSON body parsing — returns null on malformed input */
1473
+ async function parseJsonBody(c) {
1474
+ try {
1475
+ return await c.req.json();
1476
+ } catch {
1477
+ return null;
1472
1478
  }
1473
- const host = config.host ?? "127.0.0.1";
1474
- const store = config.store ?? new MemoryStateStore();
1479
+ }
1480
+ function createDaemonApp(optionsOrGetState) {
1481
+ const { getState, token } = typeof optionsOrGetState === "function" ? {
1482
+ getState: optionsOrGetState,
1483
+ token: void 0
1484
+ } : optionsOrGetState;
1475
1485
  const app = new Hono();
1476
- app.use("*", cors());
1486
+ if (token) app.use("*", async (c, next) => {
1487
+ if (c.req.header("authorization") !== `Bearer ${token}`) return c.json({ error: "Unauthorized" }, 401);
1488
+ await next();
1489
+ });
1490
+ function getWorkflowAgentNames(workflow, tag) {
1491
+ const s = getState();
1492
+ if (!s) return [];
1493
+ return [...s.configs.values()].filter((c) => c.workflow === workflow && c.tag === tag).map((c) => c.name);
1494
+ }
1477
1495
  app.get("/health", (c) => {
1478
- if (!state) return c.json({ status: "unavailable" }, 503);
1496
+ const s = getState();
1497
+ if (!s) return c.json({ status: "unavailable" }, 503);
1498
+ const standaloneAgents = [...s.configs.keys()];
1499
+ const workflowList = [...s.workflows.values()].map((wf) => ({
1500
+ name: wf.name,
1501
+ tag: wf.tag,
1502
+ agents: wf.agents
1503
+ }));
1479
1504
  return c.json({
1480
1505
  status: "ok",
1481
1506
  pid: process.pid,
1482
- port: state.port,
1483
- uptime: Date.now() - new Date(state.startedAt).getTime(),
1484
- agents: [...state.configs.keys()]
1507
+ port: s.port,
1508
+ uptime: Date.now() - new Date(s.startedAt).getTime(),
1509
+ agents: standaloneAgents,
1510
+ workflows: workflowList
1485
1511
  });
1486
1512
  });
1487
1513
  app.post("/shutdown", (c) => {
@@ -1489,34 +1515,53 @@ async function startDaemon(config = {}) {
1489
1515
  return c.json({ success: true });
1490
1516
  });
1491
1517
  app.get("/agents", (c) => {
1492
- if (!state) return c.json({ error: "Not ready" }, 503);
1493
- const agents = [...state.configs.values()].map((c) => ({
1494
- name: c.name,
1495
- model: c.model,
1496
- backend: c.backend,
1497
- workflow: c.workflow,
1498
- tag: c.tag,
1499
- createdAt: c.createdAt
1518
+ const s = getState();
1519
+ if (!s) return c.json({ error: "Not ready" }, 503);
1520
+ const standaloneAgents = [...s.configs.values()].map((cfg) => ({
1521
+ name: cfg.name,
1522
+ model: cfg.model,
1523
+ backend: cfg.backend,
1524
+ workflow: cfg.workflow,
1525
+ tag: cfg.tag,
1526
+ createdAt: cfg.createdAt,
1527
+ source: "standalone"
1528
+ }));
1529
+ const workflowAgents = [...s.workflows.values()].flatMap((wf) => wf.agents.map((agentName) => {
1530
+ const controller = wf.controllers.get(agentName);
1531
+ return {
1532
+ name: agentName,
1533
+ model: "",
1534
+ backend: "",
1535
+ workflow: wf.name,
1536
+ tag: wf.tag,
1537
+ createdAt: wf.startedAt,
1538
+ source: "workflow",
1539
+ state: controller?.state ?? "unknown"
1540
+ };
1500
1541
  }));
1501
- return c.json({ agents });
1542
+ return c.json({ agents: [...standaloneAgents, ...workflowAgents] });
1502
1543
  });
1503
1544
  app.post("/agents", async (c) => {
1504
- if (!state) return c.json({ error: "Not ready" }, 503);
1505
- const { name, model, system, backend = "default", workflow = "global", tag = "main" } = await c.req.json();
1545
+ const s = getState();
1546
+ if (!s) return c.json({ error: "Not ready" }, 503);
1547
+ const body = await parseJsonBody(c);
1548
+ if (!body || typeof body !== "object") return c.json({ error: "Invalid JSON body" }, 400);
1549
+ const { name, model, system, backend = "default", provider, workflow = "global", tag = "main" } = body;
1506
1550
  if (!name || !model || !system) return c.json({ error: "name, model, system required" }, 400);
1507
- if (state.configs.has(name)) return c.json({ error: `Agent already exists: ${name}` }, 409);
1551
+ if (s.configs.has(name)) return c.json({ error: `Agent already exists: ${name}` }, 409);
1508
1552
  const agentConfig = {
1509
1553
  name,
1510
1554
  model,
1511
1555
  system,
1512
1556
  backend,
1557
+ provider,
1513
1558
  workflow,
1514
1559
  tag,
1515
1560
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1516
1561
  };
1517
- const handle = new LocalWorker(agentConfig, await state.store.load(name) ?? void 0);
1518
- state.configs.set(name, agentConfig);
1519
- state.workers.set(name, handle);
1562
+ const handle = new LocalWorker(agentConfig, await s.store.load(name) ?? void 0);
1563
+ s.configs.set(name, agentConfig);
1564
+ s.workers.set(name, handle);
1520
1565
  return c.json({
1521
1566
  name,
1522
1567
  model,
@@ -1526,8 +1571,9 @@ async function startDaemon(config = {}) {
1526
1571
  }, 201);
1527
1572
  });
1528
1573
  app.get("/agents/:name", (c) => {
1529
- if (!state) return c.json({ error: "Not ready" }, 503);
1530
- const cfg = state.configs.get(c.req.param("name"));
1574
+ const s = getState();
1575
+ if (!s) return c.json({ error: "Not ready" }, 503);
1576
+ const cfg = s.configs.get(c.req.param("name"));
1531
1577
  if (!cfg) return c.json({ error: "Agent not found" }, 404);
1532
1578
  return c.json({
1533
1579
  name: cfg.name,
@@ -1540,21 +1586,25 @@ async function startDaemon(config = {}) {
1540
1586
  });
1541
1587
  });
1542
1588
  app.delete("/agents/:name", async (c) => {
1543
- if (!state) return c.json({ error: "Not ready" }, 503);
1589
+ const s = getState();
1590
+ if (!s) return c.json({ error: "Not ready" }, 503);
1544
1591
  const name = c.req.param("name");
1545
- if (!state.configs.delete(name)) return c.json({ error: "Agent not found" }, 404);
1546
- const handle = state.workers.get(name);
1592
+ if (!s.configs.delete(name)) return c.json({ error: "Agent not found" }, 404);
1593
+ const handle = s.workers.get(name);
1547
1594
  if (handle) try {
1548
- await state.store.save(name, handle.getState());
1595
+ await s.store.save(name, handle.getState());
1549
1596
  } catch {}
1550
- state.workers.delete(name);
1597
+ s.workers.delete(name);
1551
1598
  return c.json({ success: true });
1552
1599
  });
1553
1600
  app.post("/run", async (c) => {
1554
- if (!state) return c.json({ error: "Not ready" }, 503);
1555
- const { agent: agentName, message } = await c.req.json();
1601
+ const s = getState();
1602
+ if (!s) return c.json({ error: "Not ready" }, 503);
1603
+ const body = await parseJsonBody(c);
1604
+ if (!body || typeof body !== "object") return c.json({ error: "Invalid JSON body" }, 400);
1605
+ const { agent: agentName, message } = body;
1556
1606
  if (!agentName || !message) return c.json({ error: "agent and message required" }, 400);
1557
- const handle = state.workers.get(agentName);
1607
+ const handle = s.workers.get(agentName);
1558
1608
  if (!handle) return c.json({ error: `Agent not found: ${agentName}` }, 404);
1559
1609
  return streamSSE(c, async (stream) => {
1560
1610
  try {
@@ -1562,7 +1612,8 @@ async function startDaemon(config = {}) {
1562
1612
  while (true) {
1563
1613
  const { value, done } = await gen.next();
1564
1614
  if (done) {
1565
- if (state) await state.store.save(agentName, handle.getState());
1615
+ const currentState = getState();
1616
+ if (currentState) await currentState.store.save(agentName, handle.getState());
1566
1617
  await stream.writeSSE({
1567
1618
  event: "done",
1568
1619
  data: JSON.stringify(value)
@@ -1587,14 +1638,17 @@ async function startDaemon(config = {}) {
1587
1638
  });
1588
1639
  });
1589
1640
  app.post("/serve", async (c) => {
1590
- if (!state) return c.json({ error: "Not ready" }, 503);
1591
- const { agent: agentName, message } = await c.req.json();
1641
+ const s = getState();
1642
+ if (!s) return c.json({ error: "Not ready" }, 503);
1643
+ const body = await parseJsonBody(c);
1644
+ if (!body || typeof body !== "object") return c.json({ error: "Invalid JSON body" }, 400);
1645
+ const { agent: agentName, message } = body;
1592
1646
  if (!agentName || !message) return c.json({ error: "agent and message required" }, 400);
1593
- const handle = state.workers.get(agentName);
1647
+ const handle = s.workers.get(agentName);
1594
1648
  if (!handle) return c.json({ error: `Agent not found: ${agentName}` }, 404);
1595
1649
  try {
1596
1650
  const response = await handle.send(message);
1597
- await state.store.save(agentName, handle.getState());
1651
+ await s.store.save(agentName, handle.getState());
1598
1652
  return c.json(response);
1599
1653
  } catch (error) {
1600
1654
  const msg = error instanceof Error ? error.message : String(error);
@@ -1602,7 +1656,8 @@ async function startDaemon(config = {}) {
1602
1656
  }
1603
1657
  });
1604
1658
  app.all("/mcp", async (c) => {
1605
- if (!state) return c.json({ error: "Not ready" }, 503);
1659
+ const s = getState();
1660
+ if (!s) return c.json({ error: "Not ready" }, 503);
1606
1661
  const req = c.req.raw;
1607
1662
  const sessionId = req.headers.get("mcp-session-id");
1608
1663
  if (sessionId && mcpSessions.has(sessionId)) {
@@ -1618,10 +1673,10 @@ async function startDaemon(config = {}) {
1618
1673
  const body = await req.json();
1619
1674
  if (!(Array.isArray(body) ? body.some((m) => m?.method === "initialize") : body?.method === "initialize")) return c.json({ error: "Bad request: session required" }, 400);
1620
1675
  const agentName = new URL(req.url).searchParams.get("agent") || "user";
1621
- const agentCfg = state.configs.get(agentName);
1676
+ const agentCfg = s.configs.get(agentName);
1622
1677
  const workflow = agentCfg?.workflow ?? "global";
1623
1678
  const tag = agentCfg?.tag ?? "main";
1624
- const workflowAgents = getWorkflowAgentNames$1(workflow, tag);
1679
+ const workflowAgents = getWorkflowAgentNames(workflow, tag);
1625
1680
  const allNames = [...new Set([
1626
1681
  ...workflowAgents,
1627
1682
  agentName,
@@ -1654,7 +1709,105 @@ async function startDaemon(config = {}) {
1654
1709
  if (req.method === "GET") return c.json({ error: "Session ID required for GET requests" }, 400);
1655
1710
  return c.json({ error: "Method not allowed" }, 405);
1656
1711
  });
1657
- const server = await startHttpServer(app, {
1712
+ app.post("/workflows", async (c) => {
1713
+ const s = getState();
1714
+ if (!s) return c.json({ error: "Not ready" }, 503);
1715
+ const body = await parseJsonBody(c);
1716
+ if (!body || typeof body !== "object") return c.json({ error: "Invalid JSON body" }, 400);
1717
+ const { workflow, tag = "main", feedback, pollInterval } = body;
1718
+ if (!workflow || !workflow.agents) return c.json({ error: "workflow (parsed YAML) required" }, 400);
1719
+ const workflowName = workflow.name || "global";
1720
+ const key = `${workflowName}:${tag}`;
1721
+ if (s.workflows.has(key)) return c.json({ error: `Workflow already running: ${key}` }, 409);
1722
+ try {
1723
+ const { runWorkflowWithControllers } = await import("../runner-CnxROIev.mjs");
1724
+ const result = await runWorkflowWithControllers({
1725
+ workflow,
1726
+ workflowName,
1727
+ tag,
1728
+ mode: "start",
1729
+ headless: true,
1730
+ feedback,
1731
+ pollInterval,
1732
+ log: () => {}
1733
+ });
1734
+ if (!result.success) return c.json({ error: result.error || "Workflow failed to start" }, 500);
1735
+ const handle = {
1736
+ name: workflowName,
1737
+ tag,
1738
+ key,
1739
+ agents: Object.keys(workflow.agents),
1740
+ controllers: result.controllers,
1741
+ contextProvider: result.contextProvider,
1742
+ shutdown: result.shutdown,
1743
+ workflowPath: workflow.filePath,
1744
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
1745
+ };
1746
+ s.workflows.set(key, handle);
1747
+ return c.json({
1748
+ key,
1749
+ name: workflowName,
1750
+ tag,
1751
+ agents: handle.agents
1752
+ }, 201);
1753
+ } catch (error) {
1754
+ const msg = error instanceof Error ? error.message : String(error);
1755
+ return c.json({ error: `Failed to start workflow: ${msg}` }, 500);
1756
+ }
1757
+ });
1758
+ app.get("/workflows", (c) => {
1759
+ const s = getState();
1760
+ if (!s) return c.json({ error: "Not ready" }, 503);
1761
+ const workflows = [...s.workflows.values()].map((wf) => {
1762
+ const agentStates = {};
1763
+ for (const [name, controller] of wf.controllers) agentStates[name] = controller.state;
1764
+ return {
1765
+ name: wf.name,
1766
+ tag: wf.tag,
1767
+ key: wf.key,
1768
+ agents: wf.agents,
1769
+ agentStates,
1770
+ workflowPath: wf.workflowPath,
1771
+ startedAt: wf.startedAt
1772
+ };
1773
+ });
1774
+ return c.json({ workflows });
1775
+ });
1776
+ async function deleteWorkflow(c, name, tag) {
1777
+ const s = getState();
1778
+ if (!s) return c.json({ error: "Not ready" }, 503);
1779
+ const key = `${name}:${tag}`;
1780
+ const handle = s.workflows.get(key);
1781
+ if (!handle) return c.json({ error: `Workflow not found: ${key}` }, 404);
1782
+ try {
1783
+ await handle.shutdown();
1784
+ s.workflows.delete(key);
1785
+ return c.json({
1786
+ success: true,
1787
+ key
1788
+ });
1789
+ } catch (error) {
1790
+ const msg = error instanceof Error ? error.message : String(error);
1791
+ return c.json({ error: `Failed to stop workflow: ${msg}` }, 500);
1792
+ }
1793
+ }
1794
+ app.delete("/workflows/:name/:tag", (c) => deleteWorkflow(c, c.req.param("name"), c.req.param("tag")));
1795
+ app.delete("/workflows/:name", (c) => deleteWorkflow(c, c.req.param("name"), "main"));
1796
+ return app;
1797
+ }
1798
+ async function startDaemon(config = {}) {
1799
+ const existing = isDaemonRunning();
1800
+ if (existing) {
1801
+ console.error(`Daemon already running: pid=${existing.pid} port=${existing.port}`);
1802
+ process.exit(1);
1803
+ }
1804
+ const host = config.host ?? "127.0.0.1";
1805
+ const store = config.store ?? new MemoryStateStore();
1806
+ const token = randomUUID();
1807
+ const server = await startHttpServer(createDaemonApp({
1808
+ getState: () => state,
1809
+ token
1810
+ }), {
1658
1811
  port: config.port ?? DEFAULT_PORT,
1659
1812
  hostname: host
1660
1813
  });
@@ -1664,11 +1817,13 @@ async function startDaemon(config = {}) {
1664
1817
  pid: process.pid,
1665
1818
  host,
1666
1819
  port: actualPort,
1667
- startedAt
1820
+ startedAt,
1821
+ token
1668
1822
  });
1669
1823
  state = {
1670
1824
  configs: /* @__PURE__ */ new Map(),
1671
1825
  workers: /* @__PURE__ */ new Map(),
1826
+ workflows: /* @__PURE__ */ new Map(),
1672
1827
  store,
1673
1828
  server,
1674
1829
  port: actualPort,
@@ -1698,6 +1853,18 @@ async function startDaemon(config = {}) {
1698
1853
  * POST /run (SSE), POST /serve
1699
1854
  * ALL /mcp
1700
1855
  */
1856
+ var client_exports = /* @__PURE__ */ __exportAll({
1857
+ createAgent: () => createAgent,
1858
+ deleteAgent: () => deleteAgent,
1859
+ health: () => health,
1860
+ isDaemonActive: () => isDaemonActive,
1861
+ listAgents: () => listAgents,
1862
+ run: () => run,
1863
+ serve: () => serve,
1864
+ shutdown: () => shutdown,
1865
+ startWorkflow: () => startWorkflow,
1866
+ stopWorkflow: () => stopWorkflow
1867
+ });
1701
1868
  const MAX_RETRIES = 3;
1702
1869
  const BASE_DELAY_MS = 200;
1703
1870
  function isRetryableError(error) {
@@ -1708,23 +1875,32 @@ function isRetryableError(error) {
1708
1875
  }
1709
1876
  return false;
1710
1877
  }
1711
- function getDaemonUrl() {
1878
+ function getDaemonConnection() {
1712
1879
  const daemon = isDaemonRunning();
1713
1880
  if (!daemon) return null;
1714
- return `http://${daemon.host}:${daemon.port}`;
1881
+ return {
1882
+ url: `http://${daemon.host}:${daemon.port}`,
1883
+ token: daemon.token
1884
+ };
1715
1885
  }
1716
1886
  function requireDaemon() {
1717
- const url = getDaemonUrl();
1718
- if (!url) throw new Error("No daemon running. Start one with: agent-worker daemon");
1719
- return url;
1887
+ const conn = getDaemonConnection();
1888
+ if (!conn) throw new Error("No daemon running. Start one with: agent-worker daemon");
1889
+ return conn;
1890
+ }
1891
+ /** Build headers with auth token */
1892
+ function authHeaders(token, extra) {
1893
+ const headers = { ...extra };
1894
+ if (token) headers["Authorization"] = `Bearer ${token}`;
1895
+ return headers;
1720
1896
  }
1721
1897
  async function request(method, path, body) {
1722
- const baseUrl = requireDaemon();
1898
+ const { url: baseUrl, token } = requireDaemon();
1723
1899
  let lastError;
1724
1900
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) try {
1725
1901
  const init = {
1726
1902
  method,
1727
- headers: body !== void 0 ? { "Content-Type": "application/json" } : void 0,
1903
+ headers: authHeaders(token, body !== void 0 ? { "Content-Type": "application/json" } : void 0),
1728
1904
  body: body !== void 0 ? JSON.stringify(body) : void 0,
1729
1905
  signal: AbortSignal.timeout(6e4)
1730
1906
  };
@@ -1771,8 +1947,11 @@ function serve(body) {
1771
1947
  */
1772
1948
  async function run(body, onChunk) {
1773
1949
  let baseUrl;
1950
+ let token;
1774
1951
  try {
1775
- baseUrl = requireDaemon();
1952
+ const conn = requireDaemon();
1953
+ baseUrl = conn.url;
1954
+ token = conn.token;
1776
1955
  } catch (error) {
1777
1956
  return {
1778
1957
  success: false,
@@ -1782,7 +1961,7 @@ async function run(body, onChunk) {
1782
1961
  try {
1783
1962
  const res = await fetch(`${baseUrl}/run`, {
1784
1963
  method: "POST",
1785
- headers: { "Content-Type": "application/json" },
1964
+ headers: authHeaders(token, { "Content-Type": "application/json" }),
1786
1965
  body: JSON.stringify(body)
1787
1966
  });
1788
1967
  if (!res.ok || !res.body) return await res.json();
@@ -1819,13 +1998,22 @@ async function run(body, onChunk) {
1819
1998
  };
1820
1999
  }
1821
2000
  }
2001
+ /** POST /workflows — start a workflow via daemon */
2002
+ function startWorkflow(body) {
2003
+ return request("POST", "/workflows", body);
2004
+ }
2005
+ /** DELETE /workflows/:name/:tag — stop a workflow */
2006
+ function stopWorkflow(name, tag = "main") {
2007
+ return request("DELETE", `/workflows/${encodeURIComponent(name)}/${encodeURIComponent(tag)}`);
2008
+ }
1822
2009
  /** Check if daemon is running */
1823
2010
  function isDaemonActive() {
1824
- return getDaemonUrl() !== null;
2011
+ return getDaemonConnection() !== null;
1825
2012
  }
1826
2013
 
1827
2014
  //#endregion
1828
2015
  //#region src/cli/output.ts
2016
+ var output_exports = /* @__PURE__ */ __exportAll({ outputJson: () => outputJson });
1829
2017
  /**
1830
2018
  * CLI Output Utilities
1831
2019
  *
@@ -1844,6 +2032,10 @@ function outputJson(data) {
1844
2032
 
1845
2033
  //#endregion
1846
2034
  //#region src/cli/commands/agent.ts
2035
+ var agent_exports = /* @__PURE__ */ __exportAll({
2036
+ ensureDaemon: () => ensureDaemon,
2037
+ registerAgentCommands: () => registerAgentCommands
2038
+ });
1847
2039
  /**
1848
2040
  * Ensure daemon is running. If not, spawn it in background and wait.
1849
2041
  */
@@ -1879,23 +2071,33 @@ function registerAgentCommands(program) {
1879
2071
  "claude",
1880
2072
  "codex",
1881
2073
  "cursor",
2074
+ "opencode",
1882
2075
  "mock"
1883
- ]).default("default")).option("-s, --system <prompt>", "System prompt", "You are a helpful assistant.").option("-f, --system-file <file>", "Read system prompt from file").option("--workflow <name>", "Workflow name (default: global)").option("--tag <tag>", "Workflow instance tag (default: main)").option("--port <port>", `Daemon port if starting new daemon (default: ${DEFAULT_PORT})`).option("--host <host>", "Daemon host (default: 127.0.0.1)").option("--json", "Output as JSON").addHelpText("after", `
2076
+ ]).default("default")).option("--provider <name>", "Provider SDK name (e.g., anthropic, openai)").option("--base-url <url>", "Override provider base URL").option("--api-key <ref>", "API key env var (e.g., $MINIMAX_API_KEY)").option("-s, --system <prompt>", "System prompt", "You are a helpful assistant.").option("-f, --system-file <file>", "Read system prompt from file").option("--workflow <name>", "Workflow name (default: global)").option("--tag <tag>", "Workflow instance tag (default: main)").option("--port <port>", `Daemon port if starting new daemon (default: ${DEFAULT_PORT})`).option("--host <host>", "Daemon host (default: 127.0.0.1)").option("--json", "Output as JSON").addHelpText("after", `
1884
2077
  Examples:
1885
2078
  $ agent-worker new alice -m anthropic/claude-sonnet-4-5
1886
2079
  $ agent-worker new bot -b mock
1887
2080
  $ agent-worker new reviewer --workflow review --tag pr-123
2081
+ $ agent-worker new coder -m MiniMax-M2.5 --provider anthropic --base-url https://api.minimax.io/anthropic/v1 --api-key '$MINIMAX_API_KEY'
1888
2082
  `).action(async (name, options) => {
1889
2083
  let system = options.system;
1890
2084
  if (options.systemFile) system = readFileSync(options.systemFile, "utf-8");
1891
2085
  const backend = normalizeBackendType(options.backend ?? "default");
1892
2086
  const model = options.model || getDefaultModel();
2087
+ let provider;
2088
+ if (options.provider) if (options.baseUrl || options.apiKey) provider = {
2089
+ name: options.provider,
2090
+ base_url: options.baseUrl,
2091
+ api_key: options.apiKey
2092
+ };
2093
+ else provider = options.provider;
1893
2094
  await ensureDaemon(options.port ? parseInt(options.port, 10) : void 0, options.host);
1894
2095
  const res = await createAgent({
1895
2096
  name,
1896
2097
  model,
1897
2098
  system,
1898
2099
  backend,
2100
+ provider,
1899
2101
  workflow: options.workflow,
1900
2102
  tag: options.tag
1901
2103
  });
@@ -1932,13 +2134,16 @@ Examples:
1932
2134
  }
1933
2135
  for (const a of agents) {
1934
2136
  const wf = a.tag === "main" ? `@${a.workflow}` : `@${a.workflow}:${a.tag}`;
1935
- console.log(`${a.name.padEnd(12)} ${a.model.padEnd(30)} ${wf}`);
2137
+ const info = a.model || a.state || "";
2138
+ console.log(`${a.name.padEnd(12)} ${info.padEnd(30)} ${wf}`);
1936
2139
  }
1937
2140
  });
1938
- program.command("stop [name]").description("Stop agent or daemon").option("--all", "Stop daemon (all agents)").addHelpText("after", `
2141
+ program.command("stop [name]").description("Stop agent, workflow, or daemon").option("--all", "Stop daemon (all agents and workflows)").addHelpText("after", `
1939
2142
  Examples:
1940
- $ agent-worker stop alice # Stop specific agent
1941
- $ agent-worker stop --all # Stop daemon (all agents)
2143
+ $ agent-worker stop alice # Stop specific agent
2144
+ $ agent-worker stop @review:pr-123 # Stop workflow
2145
+ $ agent-worker stop @review # Stop workflow (tag defaults to main)
2146
+ $ agent-worker stop --all # Stop daemon (everything)
1942
2147
  `).action(async (name, options) => {
1943
2148
  if (!isDaemonActive()) {
1944
2149
  console.error("No daemon running");
@@ -1951,11 +2156,17 @@ Examples:
1951
2156
  return;
1952
2157
  }
1953
2158
  if (!name) {
1954
- console.error("Specify agent name or use --all");
2159
+ console.error("Specify agent name, @workflow[:tag], or use --all");
1955
2160
  process.exit(1);
1956
2161
  }
1957
- const res = await deleteAgent(name);
1958
- if (res.success) console.log(`Stopped: ${name}`);
2162
+ const { parseTarget } = await Promise.resolve().then(() => target_exports);
2163
+ const target = parseTarget(name);
2164
+ let res;
2165
+ if (target.agent === void 0) {
2166
+ const { stopWorkflow: stopWf } = await Promise.resolve().then(() => client_exports);
2167
+ res = await stopWf(target.workflow, target.tag);
2168
+ } else res = await deleteAgent(target.agent);
2169
+ if (res.success) console.log(`Stopped: ${target.display}`);
1959
2170
  else {
1960
2171
  console.error("Error:", res.error);
1961
2172
  process.exit(1);
@@ -1973,6 +2184,14 @@ Examples:
1973
2184
  console.log(`Daemon: pid=${res.pid} port=${res.port}`);
1974
2185
  const agents = res.agents ?? [];
1975
2186
  console.log(`Agents: ${agents.length > 0 ? agents.join(", ") : "(none)"}`);
2187
+ const workflows = res.workflows ?? [];
2188
+ if (workflows.length > 0) {
2189
+ console.log(`Workflows:`);
2190
+ for (const wf of workflows) {
2191
+ const display = wf.tag === "main" ? `@${wf.name}` : `@${wf.name}:${wf.tag}`;
2192
+ console.log(` ${display} → ${wf.agents.join(", ")}`);
2193
+ }
2194
+ }
1976
2195
  if (res.uptime) {
1977
2196
  const secs = Math.round(res.uptime / 1e3);
1978
2197
  console.log(`Uptime: ${secs}s`);
@@ -1981,8 +2200,8 @@ Examples:
1981
2200
  });
1982
2201
  program.command("ask <agent> <message>").description("Send message to agent (SSE streaming)").option("--json", "Output final response as JSON").addHelpText("after", `
1983
2202
  Examples:
1984
- $ agent-worker run alice "analyze this code"
1985
- $ agent-worker run alice "hello" --json
2203
+ $ agent-worker ask alice "analyze this code"
2204
+ $ agent-worker ask alice "hello" --json
1986
2205
  `).action(async (agent, message, options) => {
1987
2206
  if (!isDaemonActive()) {
1988
2207
  console.error("No daemon running");
@@ -2132,7 +2351,7 @@ Examples:
2132
2351
 
2133
2352
  Note: Workflow name is inferred from YAML 'name' field or filename
2134
2353
  `).action(async (file, options) => {
2135
- const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-CfITD7TN.mjs");
2354
+ const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-CIE3WPNx.mjs");
2136
2355
  const tag = options.tag || DEFAULT_TAG;
2137
2356
  const parsedWorkflow = await parseWorkflowFile(file, { tag });
2138
2357
  const workflowName = parsedWorkflow.name;
@@ -2143,8 +2362,8 @@ Note: Workflow name is inferred from YAML 'name' field or filename
2143
2362
  isCleaningUp = true;
2144
2363
  console.log("\nInterrupted, cleaning up...");
2145
2364
  if (controllers) {
2146
- const { shutdownControllers } = await import("../workflow-CfITD7TN.mjs");
2147
- const { createSilentLogger } = await import("../logger-CCFaMMV7.mjs");
2365
+ const { shutdownControllers } = await import("../workflow-CIE3WPNx.mjs");
2366
+ const { createSilentLogger } = await import("../logger-Bfdo83xL.mjs");
2148
2367
  await shutdownControllers(controllers, createSilentLogger());
2149
2368
  }
2150
2369
  process.exit(130);
@@ -2181,7 +2400,7 @@ Note: Workflow name is inferred from YAML 'name' field or filename
2181
2400
  feedback: result.feedback
2182
2401
  }, null, 2));
2183
2402
  else if (!options.debug) {
2184
- const { showWorkflowSummary } = await import("../display-pretty-BsCsnPqs.mjs");
2403
+ const { showWorkflowSummary } = await import("../display-pretty-BCJq5v9d.mjs");
2185
2404
  showWorkflowSummary({
2186
2405
  duration: result.duration,
2187
2406
  document: finalDoc,
@@ -2205,73 +2424,60 @@ Note: Workflow name is inferred from YAML 'name' field or filename
2205
2424
  process.exit(1);
2206
2425
  }
2207
2426
  });
2208
- program.command("start <file>").description("Start workflow and keep agents running").option("--tag <tag>", "Workflow instance tag (default: main)", DEFAULT_TAG).option("-d, --debug", "Show debug details (internal logs, MCP traces, idle checks)").option("--feedback", "Enable feedback tool (agents can report tool/workflow observations)").option("--background", "Run in background (daemonize)").addHelpText("after", `
2427
+ program.command("start <file>").description("Start workflow via daemon and keep agents running").option("--tag <tag>", "Workflow instance tag (default: main)", DEFAULT_TAG).option("--feedback", "Enable feedback tool (agents can report tool/workflow observations)").option("--json", "Output as JSON").addHelpText("after", `
2209
2428
  Examples:
2210
- $ agent-worker start review.yaml # Foreground (Ctrl+C to stop)
2211
- $ agent-worker start review.yaml --background # Background daemon
2429
+ $ agent-worker start review.yaml # Start review:main (Ctrl+C to stop)
2212
2430
  $ agent-worker start review.yaml --tag pr-123 # Start review:pr-123
2213
2431
 
2432
+ Workflow runs inside the daemon. Use ls/stop to manage:
2433
+ $ agent-worker ls # List all agents
2434
+ $ agent-worker stop @review:pr-123 # Stop workflow
2435
+
2214
2436
  Note: Workflow name is inferred from YAML 'name' field or filename
2215
2437
  `).action(async (file, options) => {
2216
- const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-CfITD7TN.mjs");
2438
+ const { parseWorkflowFile } = await import("../workflow-CIE3WPNx.mjs");
2439
+ const { ensureDaemon } = await Promise.resolve().then(() => agent_exports);
2217
2440
  const tag = options.tag || DEFAULT_TAG;
2218
2441
  const parsedWorkflow = await parseWorkflowFile(file, { tag });
2219
2442
  const workflowName = parsedWorkflow.name;
2220
- if (options.background) {
2221
- const { getDefaultContextDir } = await Promise.resolve().then(() => file_provider_exports);
2222
- const contextDir = getDefaultContextDir(workflowName, tag);
2223
- const args = [
2224
- process.argv[1] ?? "",
2225
- "start",
2226
- file
2227
- ];
2228
- if (tag !== DEFAULT_TAG) args.push("--tag", tag);
2229
- if (options.feedback) args.push("--feedback");
2230
- const child = spawn(process.execPath, args, {
2231
- detached: true,
2232
- stdio: "ignore"
2443
+ await ensureDaemon();
2444
+ const res = await startWorkflow({
2445
+ workflow: parsedWorkflow,
2446
+ tag,
2447
+ feedback: options.feedback
2448
+ });
2449
+ if (res.error) {
2450
+ console.error("Error:", res.error);
2451
+ process.exit(1);
2452
+ }
2453
+ const agents = res.agents ?? [];
2454
+ if (options.json) {
2455
+ const { outputJson } = await Promise.resolve().then(() => output_exports);
2456
+ outputJson({
2457
+ name: workflowName,
2458
+ tag,
2459
+ agents
2233
2460
  });
2234
- child.unref();
2235
- console.log(`Workflow: ${workflowName}:${tag}`);
2236
- console.log(`PID: ${child.pid}`);
2237
- console.log(`Context: ${contextDir}`);
2238
- console.log(`\nTo monitor:`);
2239
- console.log(` agent-worker ls @${workflowName}:${tag}`);
2240
- console.log(` agent-worker peek @${workflowName}:${tag}`);
2241
- console.log(`\nTo stop:`);
2242
- console.log(` agent-worker stop @${workflowName}:${tag}`);
2243
2461
  return;
2244
2462
  }
2245
- let shutdownFn;
2463
+ console.log(`Workflow: @${workflowName}${tag !== "main" ? ":" + tag : ""}`);
2464
+ console.log(`Agents: ${agents.join(", ")}`);
2465
+ console.log(`\nTo monitor:`);
2466
+ console.log(` agent-worker ls`);
2467
+ console.log(` agent-worker peek @${workflowName}${tag !== "main" ? ":" + tag : ""}`);
2468
+ console.log(`\nTo stop:`);
2469
+ console.log(` agent-worker stop @${workflowName}${tag !== "main" ? ":" + tag : ""}`);
2470
+ let isCleaningUp = false;
2246
2471
  const cleanup = async () => {
2247
- console.log("\nShutting down...");
2248
- if (shutdownFn) await shutdownFn();
2472
+ if (isCleaningUp) return;
2473
+ isCleaningUp = true;
2474
+ console.log("\nStopping workflow...");
2475
+ await stopWorkflow(workflowName, tag);
2249
2476
  process.exit(0);
2250
2477
  };
2251
2478
  process.on("SIGINT", cleanup);
2252
2479
  process.on("SIGTERM", cleanup);
2253
- try {
2254
- const result = await runWorkflowWithControllers({
2255
- workflow: parsedWorkflow,
2256
- workflowName,
2257
- tag,
2258
- instance: `${workflowName}:${tag}`,
2259
- debug: options.debug,
2260
- log: console.log,
2261
- mode: "start",
2262
- feedback: options.feedback
2263
- });
2264
- if (!result.success) {
2265
- console.error("Workflow failed:", result.error);
2266
- process.exit(1);
2267
- }
2268
- shutdownFn = result.shutdown;
2269
- await new Promise(() => {});
2270
- } catch (error) {
2271
- console.error("Error:", error instanceof Error ? error.message : String(error));
2272
- await cleanup();
2273
- process.exit(1);
2274
- }
2480
+ await new Promise(() => {});
2275
2481
  });
2276
2482
  }
2277
2483
 
@@ -2380,10 +2586,6 @@ const PROVIDER_API_KEYS = {
2380
2586
  xai: {
2381
2587
  envVar: "XAI_API_KEY",
2382
2588
  description: "xAI Grok"
2383
- },
2384
- minimax: {
2385
- envVar: "MINIMAX_API_KEY",
2386
- description: "MiniMax"
2387
2589
  }
2388
2590
  };
2389
2591
  function registerInfoCommands(program) {
@@ -2404,10 +2606,11 @@ function registerInfoCommands(program) {
2404
2606
  console.log(` Provider only: provider (e.g., openai → ${gatewayExample})`);
2405
2607
  console.log(` Gateway format: provider/model (e.g., ${gatewayExample})`);
2406
2608
  console.log(` Direct format: provider:model (e.g., ${directExample})`);
2609
+ console.log(` Custom endpoint: --provider anthropic --base-url <url> --api-key '$KEY'`);
2407
2610
  console.log(`\nDefault: ${defaultModel} (when no model specified)`);
2408
2611
  });
2409
2612
  program.command("backends").description("Check available backends (SDK, CLI tools)").action(async () => {
2410
- const { listBackends } = await import("../backends-DG5igQii.mjs");
2613
+ const { listBackends } = await import("../backends-BWzhErjT.mjs");
2411
2614
  const backends = await listBackends();
2412
2615
  console.log("Backend Status:\n");
2413
2616
  for (const backend of backends) {
@@ -2437,7 +2640,7 @@ Examples:
2437
2640
  $ agent-worker doc read @review:pr-123 # Read specific workflow:tag document
2438
2641
  `).action(async (targetInput) => {
2439
2642
  const dir = await resolveDir(targetInput);
2440
- const { createFileContextProvider } = await import("../context-CKL-RY7a.mjs");
2643
+ const { createFileContextProvider } = await import("../context-BqEyt2SF.mjs");
2441
2644
  const content = await createFileContextProvider(dir, []).readDocument();
2442
2645
  console.log(content || "(empty document)");
2443
2646
  });
@@ -2455,7 +2658,7 @@ Examples:
2455
2658
  process.exit(1);
2456
2659
  }
2457
2660
  const dir = await resolveDir(targetInput);
2458
- const { createFileContextProvider } = await import("../context-CKL-RY7a.mjs");
2661
+ const { createFileContextProvider } = await import("../context-BqEyt2SF.mjs");
2459
2662
  await createFileContextProvider(dir, []).writeDocument(content);
2460
2663
  console.log("Document written");
2461
2664
  });
@@ -2473,7 +2676,7 @@ Examples:
2473
2676
  process.exit(1);
2474
2677
  }
2475
2678
  const dir = await resolveDir(targetInput);
2476
- const { createFileContextProvider } = await import("../context-CKL-RY7a.mjs");
2679
+ const { createFileContextProvider } = await import("../context-BqEyt2SF.mjs");
2477
2680
  await createFileContextProvider(dir, []).appendDocument(content);
2478
2681
  console.log("Content appended");
2479
2682
  });
@@ -2487,7 +2690,7 @@ async function resolveDir(targetInput) {
2487
2690
 
2488
2691
  //#endregion
2489
2692
  //#region package.json
2490
- var version = "0.11.0";
2693
+ var version = "0.13.0";
2491
2694
 
2492
2695
  //#endregion
2493
2696
  //#region src/cli/index.ts