agent-worker 0.15.0 → 0.17.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/{backends-C7pQwuAx.mjs → backends-D7DT0uox.mjs} +3 -3
- package/dist/{backends-BYWmuyF9.mjs → backends-DUvcm-ce.mjs} +1 -1
- package/dist/cli/index.mjs +658 -97
- package/dist/context-CoRTddGx.mjs +4 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -1
- package/dist/{memory-provider-ZLOKyCxA.mjs → memory-provider-Z9D8NdwS.mjs} +1 -1
- package/dist/{runner-DB-b57iZ.mjs → runner-BmT0Y8MD.mjs} +23 -3
- package/dist/{workflow-DQ6Eju4n.mjs → workflow-LOZUlaDo.mjs} +88 -8
- package/package.json +9 -41
- package/dist/context-CdcZpO-0.mjs +0 -4
|
@@ -84,7 +84,7 @@ async function createModelWithProvider(modelName, provider) {
|
|
|
84
84
|
const pkg = PROVIDER_PACKAGES[provider];
|
|
85
85
|
if (!pkg) throw new Error(`Unknown provider: ${provider}. Supported: ${Object.keys(PROVIDER_PACKAGES).join(", ")}`);
|
|
86
86
|
const providerFn = await loadProvider(provider, pkg.package, pkg.export);
|
|
87
|
-
if (!providerFn) throw new Error(`
|
|
87
|
+
if (!providerFn) throw new Error(`Failed to load ${pkg.package} for provider "${provider}". Try reinstalling agent-worker.`);
|
|
88
88
|
return providerFn(modelName);
|
|
89
89
|
}
|
|
90
90
|
const { name, base_url, api_key } = provider;
|
|
@@ -92,7 +92,7 @@ async function createModelWithProvider(modelName, provider) {
|
|
|
92
92
|
if (!pkg) throw new Error(`Unknown provider: ${name}. Supported: ${Object.keys(PROVIDER_PACKAGES).join(", ")}`);
|
|
93
93
|
if (!base_url && !api_key) {
|
|
94
94
|
const providerFn = await loadProvider(name, pkg.package, pkg.export);
|
|
95
|
-
if (!providerFn) throw new Error(`
|
|
95
|
+
if (!providerFn) throw new Error(`Failed to load ${pkg.package} for provider "${name}". Try reinstalling agent-worker.`);
|
|
96
96
|
return providerFn(modelName);
|
|
97
97
|
}
|
|
98
98
|
const opts = {};
|
|
@@ -176,7 +176,7 @@ async function loadProviderModel(provider, modelName) {
|
|
|
176
176
|
const config = PROVIDER_PACKAGES[provider];
|
|
177
177
|
if (!config) throw new Error(`Unknown provider: ${provider}. Supported: ${Object.keys(PROVIDER_PACKAGES).join(", ")}. Or use gateway format: provider/model (e.g., openai/gpt-5.2)`);
|
|
178
178
|
const providerFn = await loadProvider(provider, config.package, config.export);
|
|
179
|
-
if (!providerFn) throw new Error(`
|
|
179
|
+
if (!providerFn) throw new Error(`Failed to load ${config.package} for provider "${provider}". Try reinstalling agent-worker.`);
|
|
180
180
|
return providerFn(modelName);
|
|
181
181
|
}
|
|
182
182
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as CLAUDE_MODEL_MAP, D as SDK_MODEL_ALIASES, E as OPENCODE_MODEL_MAP, O as getModelForBackend, S as BACKEND_DEFAULT_MODELS, T as CURSOR_MODEL_MAP, _ as extractCodexResult, a as createMockBackend, b as execWithIdleTimeout, c as extractOpenCodeResult, d as CodexBackend, f as ClaudeCodeBackend, g as extractClaudeResult, h as createStreamParser, i as MockAIBackend, k as normalizeBackendType, l as opencodeAdapter, m as codexAdapter, n as createBackend, o as SdkBackend, p as claudeAdapter, r as listBackends, s as OpenCodeBackend, t as checkBackends, u as CursorBackend, v as formatEvent, w as CODEX_MODEL_MAP, x as DEFAULT_IDLE_TIMEOUT, y as IdleTimeoutError } from "./backends-
|
|
1
|
+
import { C as CLAUDE_MODEL_MAP, D as SDK_MODEL_ALIASES, E as OPENCODE_MODEL_MAP, O as getModelForBackend, S as BACKEND_DEFAULT_MODELS, T as CURSOR_MODEL_MAP, _ as extractCodexResult, a as createMockBackend, b as execWithIdleTimeout, c as extractOpenCodeResult, d as CodexBackend, f as ClaudeCodeBackend, g as extractClaudeResult, h as createStreamParser, i as MockAIBackend, k as normalizeBackendType, l as opencodeAdapter, m as codexAdapter, n as createBackend, o as SdkBackend, p as claudeAdapter, r as listBackends, s as OpenCodeBackend, t as checkBackends, u as CursorBackend, v as formatEvent, w as CODEX_MODEL_MAP, x as DEFAULT_IDLE_TIMEOUT, y as IdleTimeoutError } from "./backends-D7DT0uox.mjs";
|
|
2
2
|
|
|
3
3
|
export { listBackends };
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as parseModel, I as getDefaultModel, L as isAutoProvider, P as createModelAsync, R as resolveModelFallback, a as createMockBackend, j as FRONTIER_MODELS, k as normalizeBackendType, n as createBackend } from "../backends-
|
|
2
|
+
import { A as parseModel, I as getDefaultModel, L as isAutoProvider, P as createModelAsync, R as resolveModelFallback, a as createMockBackend, j as FRONTIER_MODELS, k as normalizeBackendType, n as createBackend } from "../backends-D7DT0uox.mjs";
|
|
3
3
|
import { t as createTool } from "../create-tool-gcUuI1FD.mjs";
|
|
4
4
|
import { generateText, stepCountIs, tool } from "ai";
|
|
5
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
7
7
|
import { appendFile, mkdir, open, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
8
|
-
import { stringify } from "yaml";
|
|
8
|
+
import { parse, stringify } from "yaml";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { execSync, spawn } from "node:child_process";
|
|
@@ -21,6 +21,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
21
21
|
import { MockLanguageModelV3, mockValues } from "ai/test";
|
|
22
22
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
23
23
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
24
|
+
import { z as z$1 } from "zod/v4";
|
|
24
25
|
|
|
25
26
|
//#region rolldown:runtime
|
|
26
27
|
var __defProp = Object.defineProperty;
|
|
@@ -1709,76 +1710,106 @@ function formatInbox(inbox) {
|
|
|
1709
1710
|
return `- [${time}] From @${m.entry.from}${priority}${dm}: ${m.entry.content}`;
|
|
1710
1711
|
}).join("\n");
|
|
1711
1712
|
}
|
|
1713
|
+
/** Project context (what codebase to work on) */
|
|
1714
|
+
const projectSection = (ctx) => `## Project\nWorking on: ${ctx.projectDir}`;
|
|
1715
|
+
/** Inbox (unread messages for this agent) */
|
|
1716
|
+
const inboxSection = (ctx) => {
|
|
1717
|
+
const count = ctx.inbox.length;
|
|
1718
|
+
return `## Inbox (${count} ${count === 1 ? "message" : "messages"} for you)\n${formatInbox(ctx.inbox)}`;
|
|
1719
|
+
};
|
|
1720
|
+
/** Recent activity hint (use tool instead of injecting messages) */
|
|
1721
|
+
const activitySection = () => "## Recent Activity\nUse channel_read tool to view recent channel messages and conversation context if needed.";
|
|
1722
|
+
/** Shared document section */
|
|
1723
|
+
const documentSection = (ctx) => ctx.documentContent ? `## Shared Document\n${ctx.documentContent}` : null;
|
|
1724
|
+
/** Retry notice */
|
|
1725
|
+
const retrySection = (ctx) => ctx.retryAttempt > 1 ? `## Note\nThis is retry attempt ${ctx.retryAttempt}. Previous attempt failed.` : null;
|
|
1726
|
+
/** MCP tool instructions */
|
|
1727
|
+
const instructionsSection = (ctx) => {
|
|
1728
|
+
const lines = [];
|
|
1729
|
+
lines.push("## Instructions");
|
|
1730
|
+
lines.push("You are an agent in a multi-agent workflow. Communicate ONLY through the MCP tools below.");
|
|
1731
|
+
lines.push("Your text output is NOT seen by other agents — you MUST use channel_send to communicate.");
|
|
1732
|
+
lines.push("");
|
|
1733
|
+
lines.push("### Channel Tools");
|
|
1734
|
+
lines.push("- **channel_send**: Send a message to the shared channel. Use @agentname to mention/notify.");
|
|
1735
|
+
lines.push(" Use the \"to\" parameter for private DMs: channel_send({ message: \"...\", to: \"bob\" })");
|
|
1736
|
+
lines.push("- **channel_read**: Read recent channel messages (DMs and logs are auto-filtered).");
|
|
1737
|
+
lines.push("");
|
|
1738
|
+
lines.push("### Team Tools");
|
|
1739
|
+
lines.push("- **team_members**: List all agents you can @mention. Pass includeStatus=true to see their current state and tasks.");
|
|
1740
|
+
lines.push("- **team_doc_read/write/append/list/create**: Shared team documents.");
|
|
1741
|
+
lines.push("");
|
|
1742
|
+
lines.push("### Personal Tools");
|
|
1743
|
+
lines.push("- **my_inbox**: Check your unread messages.");
|
|
1744
|
+
lines.push("- **my_inbox_ack**: Acknowledge messages after processing (pass the latest message ID).");
|
|
1745
|
+
lines.push("- **my_status_set**: Update your status. Call when starting work (state='running', task='...') or when done (state='idle').");
|
|
1746
|
+
lines.push("");
|
|
1747
|
+
lines.push("### Proposal & Voting Tools");
|
|
1748
|
+
lines.push("- **team_proposal_create**: Create a proposal for team voting (types: election, decision, approval, assignment).");
|
|
1749
|
+
lines.push("- **team_vote**: Cast your vote on an active proposal. You can change your vote by voting again.");
|
|
1750
|
+
lines.push("- **team_proposal_status**: Check status of a proposal, or list all active proposals.");
|
|
1751
|
+
lines.push("- **team_proposal_cancel**: Cancel a proposal you created.");
|
|
1752
|
+
lines.push("");
|
|
1753
|
+
lines.push("### Resource Tools");
|
|
1754
|
+
lines.push("- **resource_create**: Store large content, get a reference (resource:id) for use anywhere.");
|
|
1755
|
+
lines.push("- **resource_read**: Read resource content by ID.");
|
|
1756
|
+
if (ctx.feedback) {
|
|
1757
|
+
lines.push("");
|
|
1758
|
+
lines.push("### Feedback Tool");
|
|
1759
|
+
lines.push("- **feedback_submit**: Report workflow improvement needs — a missing tool, an awkward step, or a capability gap.");
|
|
1760
|
+
lines.push(" Only use when you genuinely hit a pain point during your work.");
|
|
1761
|
+
}
|
|
1762
|
+
return lines.join("\n");
|
|
1763
|
+
};
|
|
1764
|
+
/** Workflow instructions (read → work → ack → exit) */
|
|
1765
|
+
const workflowSection = () => {
|
|
1766
|
+
const lines = [];
|
|
1767
|
+
lines.push("### Workflow");
|
|
1768
|
+
lines.push("1. Read your inbox messages above");
|
|
1769
|
+
lines.push("2. Do your assigned work using channel_send with @mentions");
|
|
1770
|
+
lines.push("3. Acknowledge your inbox with my_inbox_ack");
|
|
1771
|
+
lines.push("4. Exit when your task is complete");
|
|
1772
|
+
return lines.join("\n");
|
|
1773
|
+
};
|
|
1774
|
+
/** Exit guidance (when to stop) */
|
|
1775
|
+
const exitSection = () => {
|
|
1776
|
+
const lines = [];
|
|
1777
|
+
lines.push("### IMPORTANT: When to stop");
|
|
1778
|
+
lines.push("- Once your assigned task is complete, acknowledge your inbox and exit. Do NOT keep chatting.");
|
|
1779
|
+
lines.push("- Do NOT send pleasantries (\"you're welcome\", \"glad to help\", \"thanks again\") — they trigger unnecessary cycles.");
|
|
1780
|
+
lines.push("- Do NOT @mention another agent in your final message unless you need them to do more work.");
|
|
1781
|
+
lines.push("- If you receive a thank-you or acknowledgment, just call my_inbox_ack and exit. Do not reply.");
|
|
1782
|
+
return lines.join("\n");
|
|
1783
|
+
};
|
|
1712
1784
|
/**
|
|
1713
|
-
*
|
|
1785
|
+
* Default prompt sections — produces the same output as the original
|
|
1786
|
+
* monolithic buildAgentPrompt. New sections (soul, memory, todo) can
|
|
1787
|
+
* be inserted at specific positions without touching these.
|
|
1788
|
+
*/
|
|
1789
|
+
const DEFAULT_SECTIONS = [
|
|
1790
|
+
projectSection,
|
|
1791
|
+
inboxSection,
|
|
1792
|
+
activitySection,
|
|
1793
|
+
documentSection,
|
|
1794
|
+
retrySection,
|
|
1795
|
+
instructionsSection,
|
|
1796
|
+
workflowSection,
|
|
1797
|
+
exitSection
|
|
1798
|
+
];
|
|
1799
|
+
/**
|
|
1800
|
+
* Assemble prompt from sections. Joins non-null sections with blank lines.
|
|
1801
|
+
*/
|
|
1802
|
+
function assemblePrompt(sections, ctx) {
|
|
1803
|
+
return sections.map((section) => section(ctx)).filter((content) => content !== null).join("\n\n");
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Build the complete agent prompt from run context.
|
|
1807
|
+
*
|
|
1808
|
+
* Uses the default section list. For custom section lists,
|
|
1809
|
+
* use assemblePrompt() directly.
|
|
1714
1810
|
*/
|
|
1715
1811
|
function buildAgentPrompt(ctx) {
|
|
1716
|
-
|
|
1717
|
-
sections.push("## Project");
|
|
1718
|
-
sections.push(`Working on: ${ctx.projectDir}`);
|
|
1719
|
-
sections.push("");
|
|
1720
|
-
sections.push(`## Inbox (${ctx.inbox.length} message${ctx.inbox.length === 1 ? "" : "s"} for you)`);
|
|
1721
|
-
sections.push(formatInbox(ctx.inbox));
|
|
1722
|
-
sections.push("");
|
|
1723
|
-
sections.push("## Recent Activity");
|
|
1724
|
-
sections.push("Use channel_read tool to view recent channel messages and conversation context if needed.");
|
|
1725
|
-
if (ctx.documentContent) {
|
|
1726
|
-
sections.push("");
|
|
1727
|
-
sections.push("## Shared Document");
|
|
1728
|
-
sections.push(ctx.documentContent);
|
|
1729
|
-
}
|
|
1730
|
-
if (ctx.retryAttempt > 1) {
|
|
1731
|
-
sections.push("");
|
|
1732
|
-
sections.push(`## Note`);
|
|
1733
|
-
sections.push(`This is retry attempt ${ctx.retryAttempt}. Previous attempt failed.`);
|
|
1734
|
-
}
|
|
1735
|
-
sections.push("");
|
|
1736
|
-
sections.push("## Instructions");
|
|
1737
|
-
sections.push("You are an agent in a multi-agent workflow. Communicate ONLY through the MCP tools below.");
|
|
1738
|
-
sections.push("Your text output is NOT seen by other agents — you MUST use channel_send to communicate.");
|
|
1739
|
-
sections.push("");
|
|
1740
|
-
sections.push("### Channel Tools");
|
|
1741
|
-
sections.push("- **channel_send**: Send a message to the shared channel. Use @agentname to mention/notify.");
|
|
1742
|
-
sections.push(" Use the \"to\" parameter for private DMs: channel_send({ message: \"...\", to: \"bob\" })");
|
|
1743
|
-
sections.push("- **channel_read**: Read recent channel messages (DMs and logs are auto-filtered).");
|
|
1744
|
-
sections.push("");
|
|
1745
|
-
sections.push("### Team Tools");
|
|
1746
|
-
sections.push("- **team_members**: List all agents you can @mention. Pass includeStatus=true to see their current state and tasks.");
|
|
1747
|
-
sections.push("- **team_doc_read/write/append/list/create**: Shared team documents.");
|
|
1748
|
-
sections.push("");
|
|
1749
|
-
sections.push("### Personal Tools");
|
|
1750
|
-
sections.push("- **my_inbox**: Check your unread messages.");
|
|
1751
|
-
sections.push("- **my_inbox_ack**: Acknowledge messages after processing (pass the latest message ID).");
|
|
1752
|
-
sections.push("- **my_status_set**: Update your status. Call when starting work (state='running', task='...') or when done (state='idle').");
|
|
1753
|
-
sections.push("");
|
|
1754
|
-
sections.push("### Proposal & Voting Tools");
|
|
1755
|
-
sections.push("- **team_proposal_create**: Create a proposal for team voting (types: election, decision, approval, assignment).");
|
|
1756
|
-
sections.push("- **team_vote**: Cast your vote on an active proposal. You can change your vote by voting again.");
|
|
1757
|
-
sections.push("- **team_proposal_status**: Check status of a proposal, or list all active proposals.");
|
|
1758
|
-
sections.push("- **team_proposal_cancel**: Cancel a proposal you created.");
|
|
1759
|
-
sections.push("");
|
|
1760
|
-
sections.push("### Resource Tools");
|
|
1761
|
-
sections.push("- **resource_create**: Store large content, get a reference (resource:id) for use anywhere.");
|
|
1762
|
-
sections.push("- **resource_read**: Read resource content by ID.");
|
|
1763
|
-
if (ctx.feedback) {
|
|
1764
|
-
sections.push("");
|
|
1765
|
-
sections.push("### Feedback Tool");
|
|
1766
|
-
sections.push("- **feedback_submit**: Report workflow improvement needs — a missing tool, an awkward step, or a capability gap.");
|
|
1767
|
-
sections.push(" Only use when you genuinely hit a pain point during your work.");
|
|
1768
|
-
}
|
|
1769
|
-
sections.push("");
|
|
1770
|
-
sections.push("### Workflow");
|
|
1771
|
-
sections.push("1. Read your inbox messages above");
|
|
1772
|
-
sections.push("2. Do your assigned work using channel_send with @mentions");
|
|
1773
|
-
sections.push("3. Acknowledge your inbox with my_inbox_ack");
|
|
1774
|
-
sections.push("4. Exit when your task is complete");
|
|
1775
|
-
sections.push("");
|
|
1776
|
-
sections.push("### IMPORTANT: When to stop");
|
|
1777
|
-
sections.push("- Once your assigned task is complete, acknowledge your inbox and exit. Do NOT keep chatting.");
|
|
1778
|
-
sections.push("- Do NOT send pleasantries (\"you're welcome\", \"glad to help\", \"thanks again\") — they trigger unnecessary cycles.");
|
|
1779
|
-
sections.push("- Do NOT @mention another agent in your final message unless you need them to do more work.");
|
|
1780
|
-
sections.push("- If you receive a thank-you or acknowledgment, just call my_inbox_ack and exit. Do not reply.");
|
|
1781
|
-
return sections.join("\n");
|
|
1812
|
+
return assemblePrompt(DEFAULT_SECTIONS, ctx);
|
|
1782
1813
|
}
|
|
1783
1814
|
|
|
1784
1815
|
//#endregion
|
|
@@ -2284,6 +2315,8 @@ function createAgentLoop(config) {
|
|
|
2284
2315
|
let wakeResolver = null;
|
|
2285
2316
|
let pollTimeout = null;
|
|
2286
2317
|
let directRunning = false;
|
|
2318
|
+
let _hasFailures = false;
|
|
2319
|
+
let _lastError;
|
|
2287
2320
|
const scheduleConfig = agent.schedule;
|
|
2288
2321
|
let resolvedSchedule;
|
|
2289
2322
|
if (scheduleConfig) try {
|
|
@@ -2384,6 +2417,8 @@ function createAgentLoop(config) {
|
|
|
2384
2417
|
}
|
|
2385
2418
|
}
|
|
2386
2419
|
if (lastResult && !lastResult.success) {
|
|
2420
|
+
_hasFailures = true;
|
|
2421
|
+
_lastError = lastResult.error;
|
|
2387
2422
|
errorLog(`ERROR max retries exhausted, acknowledging to prevent loop`);
|
|
2388
2423
|
await contextProvider.ackInbox(name, latestId);
|
|
2389
2424
|
}
|
|
@@ -2399,6 +2434,12 @@ function createAgentLoop(config) {
|
|
|
2399
2434
|
get state() {
|
|
2400
2435
|
return state;
|
|
2401
2436
|
},
|
|
2437
|
+
get hasFailures() {
|
|
2438
|
+
return _hasFailures;
|
|
2439
|
+
},
|
|
2440
|
+
get lastError() {
|
|
2441
|
+
return _lastError;
|
|
2442
|
+
},
|
|
2402
2443
|
async start() {
|
|
2403
2444
|
if (state !== "stopped") throw new Error(`Loop ${name} is already running`);
|
|
2404
2445
|
state = "idle";
|
|
@@ -2839,6 +2880,12 @@ var daemon_exports = /* @__PURE__ */ __exportAll({
|
|
|
2839
2880
|
createDaemonApp: () => createDaemonApp,
|
|
2840
2881
|
startDaemon: () => startDaemon
|
|
2841
2882
|
});
|
|
2883
|
+
/** Key prefix for standalone agent workflow handles */
|
|
2884
|
+
const STANDALONE_PREFIX = "standalone:";
|
|
2885
|
+
/** Build a workflow key for standalone agents */
|
|
2886
|
+
function standaloneKey(agentName) {
|
|
2887
|
+
return `${STANDALONE_PREFIX}${agentName}`;
|
|
2888
|
+
}
|
|
2842
2889
|
let state = null;
|
|
2843
2890
|
let shuttingDown = false;
|
|
2844
2891
|
const mcpSessions = /* @__PURE__ */ new Map();
|
|
@@ -2846,6 +2893,10 @@ async function gracefulShutdown() {
|
|
|
2846
2893
|
if (shuttingDown) return;
|
|
2847
2894
|
shuttingDown = true;
|
|
2848
2895
|
if (state) {
|
|
2896
|
+
for (const [, loop] of state.loops) try {
|
|
2897
|
+
await loop.stop();
|
|
2898
|
+
} catch {}
|
|
2899
|
+
state.loops.clear();
|
|
2849
2900
|
for (const [, wf] of state.workflows) try {
|
|
2850
2901
|
await wf.shutdown();
|
|
2851
2902
|
} catch {}
|
|
@@ -2867,8 +2918,8 @@ async function parseJsonBody(c) {
|
|
|
2867
2918
|
return null;
|
|
2868
2919
|
}
|
|
2869
2920
|
}
|
|
2870
|
-
/** Map AgentConfig to the
|
|
2871
|
-
function
|
|
2921
|
+
/** Map AgentConfig to the ResolvedWorkflowAgent type needed by the factory */
|
|
2922
|
+
function configToResolvedWorkflowAgent(cfg) {
|
|
2872
2923
|
return {
|
|
2873
2924
|
backend: cfg.backend,
|
|
2874
2925
|
model: cfg.model,
|
|
@@ -2878,10 +2929,16 @@ function configToResolvedAgent(cfg) {
|
|
|
2878
2929
|
};
|
|
2879
2930
|
}
|
|
2880
2931
|
/**
|
|
2881
|
-
* Find an agent's loop
|
|
2882
|
-
*
|
|
2932
|
+
* Find an agent's loop.
|
|
2933
|
+
* First checks daemon-level loops (standalone agents),
|
|
2934
|
+
* then falls back to workflow-scoped loops (workflow agents).
|
|
2883
2935
|
*/
|
|
2884
2936
|
function findLoop(s, agentName) {
|
|
2937
|
+
const daemonLoop = s.loops.get(agentName);
|
|
2938
|
+
if (daemonLoop) return {
|
|
2939
|
+
loop: daemonLoop,
|
|
2940
|
+
workflow: s.workflows.get(standaloneKey(agentName)) ?? null
|
|
2941
|
+
};
|
|
2885
2942
|
for (const wf of s.workflows.values()) {
|
|
2886
2943
|
const l = wf.loops.get(agentName);
|
|
2887
2944
|
if (l) return {
|
|
@@ -2892,9 +2949,12 @@ function findLoop(s, agentName) {
|
|
|
2892
2949
|
return null;
|
|
2893
2950
|
}
|
|
2894
2951
|
/**
|
|
2895
|
-
* Ensure a standalone agent has a
|
|
2952
|
+
* Ensure a standalone agent has a loop + runtime.
|
|
2896
2953
|
* Creates the infrastructure lazily on first call (starts MCP server, etc.).
|
|
2897
2954
|
*
|
|
2955
|
+
* The loop is stored in `s.loops` (daemon-owned).
|
|
2956
|
+
* A WorkflowHandle is still created for runtime resource management (MCP, context).
|
|
2957
|
+
*
|
|
2898
2958
|
* This is the bridge between POST /agents (stores config only) and
|
|
2899
2959
|
* POST /run or /serve (needs a loop to execute).
|
|
2900
2960
|
*/
|
|
@@ -2903,11 +2963,13 @@ async function ensureAgentLoop(s, agentName) {
|
|
|
2903
2963
|
if (existing) return existing;
|
|
2904
2964
|
const cfg = s.configs.get(agentName);
|
|
2905
2965
|
if (!cfg) throw new Error(`Agent not found: ${agentName}`);
|
|
2906
|
-
const agentDef =
|
|
2907
|
-
const wfKey =
|
|
2966
|
+
const agentDef = configToResolvedWorkflowAgent(cfg);
|
|
2967
|
+
const wfKey = standaloneKey(agentName);
|
|
2968
|
+
const workflowName = cfg.workflow ?? "global";
|
|
2969
|
+
const workflowTag = cfg.tag ?? "main";
|
|
2908
2970
|
const runtime = await createMinimalRuntime({
|
|
2909
|
-
workflowName
|
|
2910
|
-
tag:
|
|
2971
|
+
workflowName,
|
|
2972
|
+
tag: workflowTag,
|
|
2911
2973
|
agentNames: [agentName]
|
|
2912
2974
|
});
|
|
2913
2975
|
let loop;
|
|
@@ -2921,10 +2983,12 @@ async function ensureAgentLoop(s, agentName) {
|
|
|
2921
2983
|
await runtime.shutdown();
|
|
2922
2984
|
throw err;
|
|
2923
2985
|
}
|
|
2986
|
+
s.loops.set(agentName, loop);
|
|
2924
2987
|
const handle = {
|
|
2925
|
-
name:
|
|
2926
|
-
tag:
|
|
2988
|
+
name: workflowName,
|
|
2989
|
+
tag: workflowTag,
|
|
2927
2990
|
key: wfKey,
|
|
2991
|
+
standalone: true,
|
|
2928
2992
|
agents: [agentName],
|
|
2929
2993
|
loops: new Map([[agentName, loop]]),
|
|
2930
2994
|
contextProvider: runtime.contextProvider,
|
|
@@ -2968,7 +3032,7 @@ function createDaemonApp(options) {
|
|
|
2968
3032
|
const s = getState();
|
|
2969
3033
|
if (!s) return c.json({ status: "unavailable" }, 503);
|
|
2970
3034
|
const standaloneAgents = [...s.configs.keys()];
|
|
2971
|
-
const workflowList = [...s.workflows.values()].map((wf) => ({
|
|
3035
|
+
const workflowList = [...s.workflows.values()].filter((wf) => !wf.standalone).map((wf) => ({
|
|
2972
3036
|
name: wf.name,
|
|
2973
3037
|
tag: wf.tag,
|
|
2974
3038
|
agents: wf.agents
|
|
@@ -3022,7 +3086,7 @@ function createDaemonApp(options) {
|
|
|
3022
3086
|
if (!s) return c.json({ error: "Not ready" }, 503);
|
|
3023
3087
|
const body = await parseJsonBody(c);
|
|
3024
3088
|
if (!body || typeof body !== "object") return c.json({ error: "Invalid JSON body" }, 400);
|
|
3025
|
-
const { name, model, system, backend = "default", provider, workflow
|
|
3089
|
+
const { name, model, system, backend = "default", provider, workflow, tag, schedule } = body;
|
|
3026
3090
|
if (!name || !model || !system) return c.json({ error: "name, model, system required" }, 400);
|
|
3027
3091
|
if (s.configs.has(name)) return c.json({ error: `Agent already exists: ${name}` }, 409);
|
|
3028
3092
|
const agentConfig = {
|
|
@@ -3067,7 +3131,14 @@ function createDaemonApp(options) {
|
|
|
3067
3131
|
if (!s) return c.json({ error: "Not ready" }, 503);
|
|
3068
3132
|
const name = c.req.param("name");
|
|
3069
3133
|
if (!s.configs.delete(name)) return c.json({ error: "Agent not found" }, 404);
|
|
3070
|
-
const
|
|
3134
|
+
const daemonLoop = s.loops.get(name);
|
|
3135
|
+
if (daemonLoop) {
|
|
3136
|
+
try {
|
|
3137
|
+
await daemonLoop.stop();
|
|
3138
|
+
} catch {}
|
|
3139
|
+
s.loops.delete(name);
|
|
3140
|
+
}
|
|
3141
|
+
const wfKey = standaloneKey(name);
|
|
3071
3142
|
const wf = s.workflows.get(wfKey);
|
|
3072
3143
|
if (wf) {
|
|
3073
3144
|
try {
|
|
@@ -3221,7 +3292,7 @@ function createDaemonApp(options) {
|
|
|
3221
3292
|
const key = `${workflowName}:${tag}`;
|
|
3222
3293
|
if (s.workflows.has(key)) return c.json({ error: `Workflow already running: ${key}` }, 409);
|
|
3223
3294
|
try {
|
|
3224
|
-
const { runWorkflowWithLoops } = await import("../runner-
|
|
3295
|
+
const { runWorkflowWithLoops } = await import("../runner-BmT0Y8MD.mjs");
|
|
3225
3296
|
const result = await runWorkflowWithLoops({
|
|
3226
3297
|
workflow,
|
|
3227
3298
|
workflowName,
|
|
@@ -3260,7 +3331,7 @@ function createDaemonApp(options) {
|
|
|
3260
3331
|
app.get("/workflows", (c) => {
|
|
3261
3332
|
const s = getState();
|
|
3262
3333
|
if (!s) return c.json({ error: "Not ready" }, 503);
|
|
3263
|
-
const workflows = [...s.workflows.values()].map((wf) => {
|
|
3334
|
+
const workflows = [...s.workflows.values()].filter((wf) => !wf.standalone).map((wf) => {
|
|
3264
3335
|
const agentStates = {};
|
|
3265
3336
|
for (const [name, loop] of wf.loops) agentStates[name] = loop.state;
|
|
3266
3337
|
return {
|
|
@@ -3324,6 +3395,7 @@ async function startDaemon(config = {}) {
|
|
|
3324
3395
|
});
|
|
3325
3396
|
state = {
|
|
3326
3397
|
configs: /* @__PURE__ */ new Map(),
|
|
3398
|
+
loops: /* @__PURE__ */ new Map(),
|
|
3327
3399
|
workflows: /* @__PURE__ */ new Map(),
|
|
3328
3400
|
store,
|
|
3329
3401
|
server,
|
|
@@ -3531,6 +3603,380 @@ function outputJson(data) {
|
|
|
3531
3603
|
console.log(JSON.stringify(data, null, 2));
|
|
3532
3604
|
}
|
|
3533
3605
|
|
|
3606
|
+
//#endregion
|
|
3607
|
+
//#region src/agent/definition.ts
|
|
3608
|
+
/**
|
|
3609
|
+
* AgentDefinition — Top-level persistent agent identity.
|
|
3610
|
+
*
|
|
3611
|
+
* This is the NEW AgentDefinition from AGENT-TOP-LEVEL architecture.
|
|
3612
|
+
* It describes WHO an agent is (prompt, soul, context) — not how it runs in a workflow.
|
|
3613
|
+
*
|
|
3614
|
+
* Loaded from .agents/*.yaml files. Workflows reference agents by name.
|
|
3615
|
+
*
|
|
3616
|
+
* Distinct from:
|
|
3617
|
+
* - WorkflowAgentDef (workflow/types.ts) — inline agent config within a workflow
|
|
3618
|
+
* - AgentConfig (agent/config.ts) — runtime config for daemon-created agents
|
|
3619
|
+
*/
|
|
3620
|
+
/**
|
|
3621
|
+
* Standard subdirectories within an agent's context directory.
|
|
3622
|
+
* Created automatically when the agent is loaded.
|
|
3623
|
+
*/
|
|
3624
|
+
const CONTEXT_SUBDIRS = [
|
|
3625
|
+
"memory",
|
|
3626
|
+
"notes",
|
|
3627
|
+
"conversations",
|
|
3628
|
+
"todo"
|
|
3629
|
+
];
|
|
3630
|
+
const AgentSoulSchema = z$1.object({
|
|
3631
|
+
role: z$1.string().optional(),
|
|
3632
|
+
expertise: z$1.array(z$1.string()).optional(),
|
|
3633
|
+
style: z$1.string().optional(),
|
|
3634
|
+
principles: z$1.array(z$1.string()).optional()
|
|
3635
|
+
}).passthrough();
|
|
3636
|
+
const ProviderConfigSchema = z$1.object({
|
|
3637
|
+
name: z$1.string(),
|
|
3638
|
+
base_url: z$1.string().optional(),
|
|
3639
|
+
api_key: z$1.string().optional()
|
|
3640
|
+
}).passthrough();
|
|
3641
|
+
const AgentPromptConfigSchema = z$1.union([z$1.object({
|
|
3642
|
+
system: z$1.string(),
|
|
3643
|
+
system_file: z$1.undefined().optional()
|
|
3644
|
+
}), z$1.object({
|
|
3645
|
+
system_file: z$1.string(),
|
|
3646
|
+
system: z$1.undefined().optional()
|
|
3647
|
+
})]);
|
|
3648
|
+
const AgentContextConfigSchema = z$1.object({
|
|
3649
|
+
dir: z$1.string().optional(),
|
|
3650
|
+
thin_thread: z$1.number().int().min(1).optional()
|
|
3651
|
+
});
|
|
3652
|
+
const ScheduleConfigSchema = z$1.object({
|
|
3653
|
+
wakeup: z$1.union([z$1.string(), z$1.number()]),
|
|
3654
|
+
prompt: z$1.string().optional()
|
|
3655
|
+
});
|
|
3656
|
+
const AgentDefinitionSchema = z$1.object({
|
|
3657
|
+
name: z$1.string().min(1),
|
|
3658
|
+
model: z$1.string().min(1),
|
|
3659
|
+
backend: z$1.enum([
|
|
3660
|
+
"sdk",
|
|
3661
|
+
"claude",
|
|
3662
|
+
"cursor",
|
|
3663
|
+
"codex",
|
|
3664
|
+
"opencode",
|
|
3665
|
+
"mock"
|
|
3666
|
+
]).optional(),
|
|
3667
|
+
provider: z$1.union([z$1.string(), ProviderConfigSchema]).optional(),
|
|
3668
|
+
prompt: AgentPromptConfigSchema,
|
|
3669
|
+
soul: AgentSoulSchema.optional(),
|
|
3670
|
+
context: AgentContextConfigSchema.optional(),
|
|
3671
|
+
max_tokens: z$1.number().int().positive().optional(),
|
|
3672
|
+
max_steps: z$1.number().int().positive().optional(),
|
|
3673
|
+
schedule: ScheduleConfigSchema.optional()
|
|
3674
|
+
});
|
|
3675
|
+
|
|
3676
|
+
//#endregion
|
|
3677
|
+
//#region src/agent/agent-handle.ts
|
|
3678
|
+
/**
|
|
3679
|
+
* AgentHandle — Runtime wrapper for an agent definition + persistent context.
|
|
3680
|
+
*
|
|
3681
|
+
* Created by AgentRegistry when an agent is loaded. Provides:
|
|
3682
|
+
* - Context directory management (memory/, notes/, conversations/, todo/)
|
|
3683
|
+
* - Read/write operations for personal context
|
|
3684
|
+
* - State tracking (idle, running, stopped, error)
|
|
3685
|
+
*
|
|
3686
|
+
* Phase 1 scope: context directory + read/write ops.
|
|
3687
|
+
* Phase 3 adds: loop, workspaces, threads.
|
|
3688
|
+
*/
|
|
3689
|
+
var AgentHandle = class {
|
|
3690
|
+
/** Agent definition (from YAML) */
|
|
3691
|
+
definition;
|
|
3692
|
+
/** Absolute path to agent's persistent context directory */
|
|
3693
|
+
contextDir;
|
|
3694
|
+
/** Current agent state */
|
|
3695
|
+
state = "idle";
|
|
3696
|
+
constructor(definition, contextDir) {
|
|
3697
|
+
this.definition = definition;
|
|
3698
|
+
this.contextDir = contextDir;
|
|
3699
|
+
}
|
|
3700
|
+
/** Agent name (convenience accessor) */
|
|
3701
|
+
get name() {
|
|
3702
|
+
return this.definition.name;
|
|
3703
|
+
}
|
|
3704
|
+
/**
|
|
3705
|
+
* Ensure the context directory and all subdirectories exist.
|
|
3706
|
+
* Called on agent load/creation. Idempotent.
|
|
3707
|
+
*/
|
|
3708
|
+
ensureContextDir() {
|
|
3709
|
+
for (const sub of CONTEXT_SUBDIRS) mkdirSync(join(this.contextDir, sub), { recursive: true });
|
|
3710
|
+
}
|
|
3711
|
+
/**
|
|
3712
|
+
* Read all memory entries as key-value records.
|
|
3713
|
+
* Memory files are YAML in memory/<key>.yaml.
|
|
3714
|
+
*/
|
|
3715
|
+
async readMemory() {
|
|
3716
|
+
const memDir = join(this.contextDir, "memory");
|
|
3717
|
+
if (!existsSync(memDir)) return {};
|
|
3718
|
+
const result = {};
|
|
3719
|
+
const files = await readdir(memDir);
|
|
3720
|
+
for (const file of files) {
|
|
3721
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
3722
|
+
const key = basename(file).replace(/\.ya?ml$/i, "");
|
|
3723
|
+
try {
|
|
3724
|
+
result[key] = parse(await readFile(join(memDir, file), "utf-8"));
|
|
3725
|
+
} catch (err) {
|
|
3726
|
+
console.warn(`Skipping malformed memory file ${file}:`, err);
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
return result;
|
|
3730
|
+
}
|
|
3731
|
+
/**
|
|
3732
|
+
* Write a memory entry. Creates/overwrites memory/<key>.yaml.
|
|
3733
|
+
*/
|
|
3734
|
+
async writeMemory(key, value) {
|
|
3735
|
+
const memDir = join(this.contextDir, "memory");
|
|
3736
|
+
await mkdir(memDir, { recursive: true });
|
|
3737
|
+
await writeFile(join(memDir, `${key}.yaml`), stringify(value));
|
|
3738
|
+
}
|
|
3739
|
+
/**
|
|
3740
|
+
* Read agent's notes, most recent first.
|
|
3741
|
+
* Notes are markdown files in notes/.
|
|
3742
|
+
*/
|
|
3743
|
+
async readNotes(limit) {
|
|
3744
|
+
const notesDir = join(this.contextDir, "notes");
|
|
3745
|
+
if (!existsSync(notesDir)) return [];
|
|
3746
|
+
const files = (await readdir(notesDir)).filter((f) => f.endsWith(".md")).sort().reverse();
|
|
3747
|
+
const selected = limit ? files.slice(0, limit) : files;
|
|
3748
|
+
return Promise.all(selected.map((f) => readFile(join(notesDir, f), "utf-8")));
|
|
3749
|
+
}
|
|
3750
|
+
/**
|
|
3751
|
+
* Append a note. Creates notes/<date>-<slug>.md.
|
|
3752
|
+
*/
|
|
3753
|
+
async appendNote(content, slug) {
|
|
3754
|
+
const notesDir = join(this.contextDir, "notes");
|
|
3755
|
+
await mkdir(notesDir, { recursive: true });
|
|
3756
|
+
const filename = `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}-${slug ?? `note-${Date.now().toString(36)}`}.md`;
|
|
3757
|
+
await writeFile(join(notesDir, filename), content);
|
|
3758
|
+
return filename;
|
|
3759
|
+
}
|
|
3760
|
+
/**
|
|
3761
|
+
* Read active todos from todo/index.md.
|
|
3762
|
+
* Returns lines that look like incomplete tasks: "- [ ] ..."
|
|
3763
|
+
*/
|
|
3764
|
+
async readTodos() {
|
|
3765
|
+
const todoFile = join(this.contextDir, "todo", "index.md");
|
|
3766
|
+
if (!existsSync(todoFile)) return [];
|
|
3767
|
+
return (await readFile(todoFile, "utf-8")).split("\n").filter((line) => line.match(/^\s*-\s*\[\s*\]/)).map((line) => line.replace(/^\s*-\s*\[\s*\]\s*/, "").trim());
|
|
3768
|
+
}
|
|
3769
|
+
/**
|
|
3770
|
+
* Write the full todo list. Replaces todo/index.md.
|
|
3771
|
+
*/
|
|
3772
|
+
async writeTodos(todos) {
|
|
3773
|
+
const todoDir = join(this.contextDir, "todo");
|
|
3774
|
+
await mkdir(todoDir, { recursive: true });
|
|
3775
|
+
const content = todos.map((t) => `- [ ] ${t}`).join("\n") + "\n";
|
|
3776
|
+
await writeFile(join(todoDir, "index.md"), content);
|
|
3777
|
+
}
|
|
3778
|
+
};
|
|
3779
|
+
|
|
3780
|
+
//#endregion
|
|
3781
|
+
//#region src/agent/yaml-parser.ts
|
|
3782
|
+
/**
|
|
3783
|
+
* Agent YAML Parser — Load agent definitions from .agents/*.yaml files.
|
|
3784
|
+
*
|
|
3785
|
+
* Handles:
|
|
3786
|
+
* - Single file: parseAgentFile("path/to/alice.yaml")
|
|
3787
|
+
* - Directory: discoverAgents("path/to/project") → scans .agents/*.yaml
|
|
3788
|
+
* - Validation: Zod schema + semantic checks (system XOR system_file)
|
|
3789
|
+
* - Resolution: system_file → reads content into system (relative to YAML dir)
|
|
3790
|
+
*
|
|
3791
|
+
* The name field is optional in YAML — defaults to filename (without .yaml).
|
|
3792
|
+
*/
|
|
3793
|
+
/** Default directory for agent definitions (relative to project root) */
|
|
3794
|
+
const AGENTS_DIR = ".agents";
|
|
3795
|
+
/**
|
|
3796
|
+
* Parse an agent definition from a YAML file.
|
|
3797
|
+
*
|
|
3798
|
+
* Validates the schema, resolves system_file to inline content,
|
|
3799
|
+
* and infers name from filename if not specified.
|
|
3800
|
+
*
|
|
3801
|
+
* @throws Error if file doesn't exist, YAML is malformed, or validation fails.
|
|
3802
|
+
*/
|
|
3803
|
+
function parseAgentFile(filePath) {
|
|
3804
|
+
if (!existsSync(filePath)) throw new Error(`Agent file not found: ${filePath}`);
|
|
3805
|
+
const data = parse(readFileSync(filePath, "utf-8"));
|
|
3806
|
+
if (!data || typeof data !== "object") throw new Error(`Invalid YAML in ${filePath}: expected an object`);
|
|
3807
|
+
const obj = data;
|
|
3808
|
+
if (!obj.name) obj.name = basename(filePath).replace(/\.ya?ml$/i, "");
|
|
3809
|
+
const result = AgentDefinitionSchema.safeParse(obj);
|
|
3810
|
+
if (!result.success) {
|
|
3811
|
+
const issues = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
3812
|
+
throw new Error(`Invalid agent definition in ${filePath}:\n${issues}`);
|
|
3813
|
+
}
|
|
3814
|
+
const def = result.data;
|
|
3815
|
+
if (def.prompt.system_file) {
|
|
3816
|
+
const promptPath = join(dirname(filePath), def.prompt.system_file);
|
|
3817
|
+
if (!existsSync(promptPath)) throw new Error(`system_file not found: ${def.prompt.system_file} (resolved: ${promptPath})`);
|
|
3818
|
+
const content = readFileSync(promptPath, "utf-8");
|
|
3819
|
+
return {
|
|
3820
|
+
...def,
|
|
3821
|
+
prompt: { system: content }
|
|
3822
|
+
};
|
|
3823
|
+
}
|
|
3824
|
+
return def;
|
|
3825
|
+
}
|
|
3826
|
+
/**
|
|
3827
|
+
* Discover all agent YAML files in a project's .agents/ directory.
|
|
3828
|
+
* Returns parsed and validated definitions.
|
|
3829
|
+
*
|
|
3830
|
+
* Non-fatal: logs warnings for invalid files, skips them.
|
|
3831
|
+
*
|
|
3832
|
+
* @param projectDir - Project root directory
|
|
3833
|
+
* @param log - Optional warning logger (default: console.warn)
|
|
3834
|
+
* @returns Array of valid agent definitions
|
|
3835
|
+
*/
|
|
3836
|
+
function discoverAgents(projectDir, log) {
|
|
3837
|
+
const agentsDir = join(projectDir, AGENTS_DIR);
|
|
3838
|
+
if (!existsSync(agentsDir)) return [];
|
|
3839
|
+
const warn = log ?? console.warn;
|
|
3840
|
+
const agents = [];
|
|
3841
|
+
let entries;
|
|
3842
|
+
try {
|
|
3843
|
+
entries = readdirSync(agentsDir);
|
|
3844
|
+
} catch {
|
|
3845
|
+
return [];
|
|
3846
|
+
}
|
|
3847
|
+
for (const entry of entries) {
|
|
3848
|
+
if (!entry.endsWith(".yaml") && !entry.endsWith(".yml")) continue;
|
|
3849
|
+
const filePath = join(agentsDir, entry);
|
|
3850
|
+
try {
|
|
3851
|
+
agents.push(parseAgentFile(filePath));
|
|
3852
|
+
} catch (err) {
|
|
3853
|
+
warn(`Skipping ${entry}: ${err instanceof Error ? err.message : String(err)}`);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
return agents;
|
|
3857
|
+
}
|
|
3858
|
+
/**
|
|
3859
|
+
* Serialize an agent definition to YAML string.
|
|
3860
|
+
* Used by CLI `agent create` to write .agents/<name>.yaml.
|
|
3861
|
+
*/
|
|
3862
|
+
function serializeAgent(def) {
|
|
3863
|
+
const obj = {
|
|
3864
|
+
name: def.name,
|
|
3865
|
+
model: def.model
|
|
3866
|
+
};
|
|
3867
|
+
if (def.backend) obj.backend = def.backend;
|
|
3868
|
+
if (def.provider) obj.provider = def.provider;
|
|
3869
|
+
obj.prompt = def.prompt;
|
|
3870
|
+
if (def.soul) obj.soul = def.soul;
|
|
3871
|
+
if (def.context) obj.context = def.context;
|
|
3872
|
+
if (def.max_tokens) obj.max_tokens = def.max_tokens;
|
|
3873
|
+
if (def.max_steps) obj.max_steps = def.max_steps;
|
|
3874
|
+
if (def.schedule) obj.schedule = def.schedule;
|
|
3875
|
+
return stringify(obj, { lineWidth: 120 });
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
//#endregion
|
|
3879
|
+
//#region src/agent/agent-registry.ts
|
|
3880
|
+
/**
|
|
3881
|
+
* AgentRegistry — Loads and manages top-level agent definitions.
|
|
3882
|
+
*
|
|
3883
|
+
* Responsibilities:
|
|
3884
|
+
* - Discover agents from .agents/*.yaml
|
|
3885
|
+
* - Load definitions → create AgentHandles
|
|
3886
|
+
* - Register/unregister agents at runtime
|
|
3887
|
+
* - Ensure context directories exist
|
|
3888
|
+
* - Provide agent lookup by name
|
|
3889
|
+
*
|
|
3890
|
+
* Owned by the daemon. One registry per daemon process.
|
|
3891
|
+
*/
|
|
3892
|
+
var AgentRegistry = class {
|
|
3893
|
+
/** Loaded agent handles, keyed by name */
|
|
3894
|
+
agents = /* @__PURE__ */ new Map();
|
|
3895
|
+
/** Project root directory */
|
|
3896
|
+
projectDir;
|
|
3897
|
+
/** Agents directory (.agents/) */
|
|
3898
|
+
agentsDir;
|
|
3899
|
+
constructor(projectDir) {
|
|
3900
|
+
this.projectDir = projectDir;
|
|
3901
|
+
this.agentsDir = join(projectDir, AGENTS_DIR);
|
|
3902
|
+
}
|
|
3903
|
+
/**
|
|
3904
|
+
* Load all agents from .agents/*.yaml.
|
|
3905
|
+
* Skips invalid files (logs warnings).
|
|
3906
|
+
* Creates context directories for each loaded agent.
|
|
3907
|
+
*/
|
|
3908
|
+
loadFromDisk(log) {
|
|
3909
|
+
const defs = discoverAgents(this.projectDir, log);
|
|
3910
|
+
for (const def of defs) this.registerDefinition(def);
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3913
|
+
* Register an agent definition. Creates AgentHandle + ensures context dir.
|
|
3914
|
+
* Overwrites existing agent with same name (reload semantics).
|
|
3915
|
+
*/
|
|
3916
|
+
registerDefinition(def) {
|
|
3917
|
+
const handle = new AgentHandle(def, this.resolveContextDir(def));
|
|
3918
|
+
handle.ensureContextDir();
|
|
3919
|
+
this.agents.set(def.name, handle);
|
|
3920
|
+
return handle;
|
|
3921
|
+
}
|
|
3922
|
+
/**
|
|
3923
|
+
* Create a new agent: write YAML file + register.
|
|
3924
|
+
* @throws Error if agent already exists on disk.
|
|
3925
|
+
*/
|
|
3926
|
+
create(def) {
|
|
3927
|
+
const yamlPath = this.agentYamlPath(def.name);
|
|
3928
|
+
if (existsSync(yamlPath)) throw new Error(`Agent file already exists: ${yamlPath}`);
|
|
3929
|
+
mkdirSync(this.agentsDir, { recursive: true });
|
|
3930
|
+
writeFileSync(yamlPath, serializeAgent(def));
|
|
3931
|
+
return this.registerDefinition(def);
|
|
3932
|
+
}
|
|
3933
|
+
/**
|
|
3934
|
+
* Delete an agent: remove YAML file + context directory + unregister.
|
|
3935
|
+
* @returns true if agent existed and was deleted.
|
|
3936
|
+
*/
|
|
3937
|
+
delete(name) {
|
|
3938
|
+
const handle = this.agents.get(name);
|
|
3939
|
+
if (!handle) return false;
|
|
3940
|
+
this.agents.delete(name);
|
|
3941
|
+
const yamlPath = this.agentYamlPath(name);
|
|
3942
|
+
if (existsSync(yamlPath)) try {
|
|
3943
|
+
unlinkSync(yamlPath);
|
|
3944
|
+
} catch {}
|
|
3945
|
+
if (existsSync(handle.contextDir)) try {
|
|
3946
|
+
rmSync(handle.contextDir, {
|
|
3947
|
+
recursive: true,
|
|
3948
|
+
force: true
|
|
3949
|
+
});
|
|
3950
|
+
} catch {}
|
|
3951
|
+
return true;
|
|
3952
|
+
}
|
|
3953
|
+
/** Get agent handle by name */
|
|
3954
|
+
get(name) {
|
|
3955
|
+
return this.agents.get(name);
|
|
3956
|
+
}
|
|
3957
|
+
/** Check if agent exists */
|
|
3958
|
+
has(name) {
|
|
3959
|
+
return this.agents.has(name);
|
|
3960
|
+
}
|
|
3961
|
+
/** List all registered agent handles */
|
|
3962
|
+
list() {
|
|
3963
|
+
return [...this.agents.values()];
|
|
3964
|
+
}
|
|
3965
|
+
/** Number of registered agents */
|
|
3966
|
+
get size() {
|
|
3967
|
+
return this.agents.size;
|
|
3968
|
+
}
|
|
3969
|
+
/** Resolve agent's context directory (absolute path) */
|
|
3970
|
+
resolveContextDir(def) {
|
|
3971
|
+
if (def.context?.dir) return join(this.projectDir, def.context.dir);
|
|
3972
|
+
return join(this.agentsDir, def.name);
|
|
3973
|
+
}
|
|
3974
|
+
/** Path to agent's YAML file */
|
|
3975
|
+
agentYamlPath(name) {
|
|
3976
|
+
return join(this.agentsDir, `${name}.yaml`);
|
|
3977
|
+
}
|
|
3978
|
+
};
|
|
3979
|
+
|
|
3534
3980
|
//#endregion
|
|
3535
3981
|
//#region src/cli/commands/agent.ts
|
|
3536
3982
|
var agent_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -3645,7 +4091,7 @@ Examples:
|
|
|
3645
4091
|
return;
|
|
3646
4092
|
}
|
|
3647
4093
|
for (const a of agents) {
|
|
3648
|
-
const wf = a.tag === "main" ? `@${a.workflow}` : `@${a.workflow}:${a.tag}
|
|
4094
|
+
const wf = a.workflow ? a.tag === "main" ? `@${a.workflow}` : `@${a.workflow}:${a.tag}` : "";
|
|
3649
4095
|
const info = a.model || a.state || "";
|
|
3650
4096
|
console.log(`${a.name.padEnd(12)} ${info.padEnd(30)} ${wf}`);
|
|
3651
4097
|
}
|
|
@@ -3743,6 +4189,121 @@ Examples:
|
|
|
3743
4189
|
process.exit(1);
|
|
3744
4190
|
} else console.log(res.content ?? JSON.stringify(res));
|
|
3745
4191
|
});
|
|
4192
|
+
const agentCmd = program.command("agent").description("Manage persistent agent definitions (.agents/*.yaml)");
|
|
4193
|
+
agentCmd.command("create <name>").description("Create a persistent agent definition").option("-m, --model <model>", `Model (default: ${getDefaultModel()})`).addOption(new Option("-b, --backend <type>", "Backend type").choices([
|
|
4194
|
+
"sdk",
|
|
4195
|
+
"claude",
|
|
4196
|
+
"codex",
|
|
4197
|
+
"cursor",
|
|
4198
|
+
"opencode",
|
|
4199
|
+
"mock"
|
|
4200
|
+
]).default(void 0)).option("-s, --system <prompt>", "System prompt").option("-f, --system-file <file>", "Read system prompt from file").option("--role <role>", "Soul: agent role").option("--expertise <items>", "Soul: expertise (comma-separated)").option("--style <style>", "Soul: communication style").option("--dir <path>", "Project directory", ".").option("--json", "Output as JSON").addHelpText("after", `
|
|
4201
|
+
Creates .agents/<name>.yaml and context directory (.agents/<name>/).
|
|
4202
|
+
|
|
4203
|
+
Examples:
|
|
4204
|
+
$ agent-worker agent create alice -m anthropic/claude-sonnet-4-5 -s "You are a code reviewer."
|
|
4205
|
+
$ agent-worker agent create bob --role developer --expertise "typescript,testing"
|
|
4206
|
+
$ agent-worker agent create coder -f ./prompts/coder.md
|
|
4207
|
+
`).action(async (name, options) => {
|
|
4208
|
+
const registry = new AgentRegistry(resolve(options.dir));
|
|
4209
|
+
let system = options.system ?? "You are a helpful assistant.";
|
|
4210
|
+
if (options.systemFile) system = readFileSync(options.systemFile, "utf-8");
|
|
4211
|
+
const def = {
|
|
4212
|
+
name,
|
|
4213
|
+
model: options.model || getDefaultModel(),
|
|
4214
|
+
prompt: { system }
|
|
4215
|
+
};
|
|
4216
|
+
if (options.backend) def.backend = options.backend;
|
|
4217
|
+
if (options.role || options.expertise || options.style) {
|
|
4218
|
+
def.soul = {};
|
|
4219
|
+
if (options.role) def.soul.role = options.role;
|
|
4220
|
+
if (options.expertise) def.soul.expertise = options.expertise.split(",").map((s) => s.trim());
|
|
4221
|
+
if (options.style) def.soul.style = options.style;
|
|
4222
|
+
}
|
|
4223
|
+
try {
|
|
4224
|
+
const handle = registry.create(def);
|
|
4225
|
+
if (options.json) outputJson({
|
|
4226
|
+
name,
|
|
4227
|
+
model: def.model,
|
|
4228
|
+
contextDir: handle.contextDir
|
|
4229
|
+
});
|
|
4230
|
+
else {
|
|
4231
|
+
console.log(`Created: .agents/${name}.yaml`);
|
|
4232
|
+
console.log(`Context: ${handle.contextDir}`);
|
|
4233
|
+
}
|
|
4234
|
+
} catch (err) {
|
|
4235
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4236
|
+
console.error(`Error: ${msg}`);
|
|
4237
|
+
process.exit(1);
|
|
4238
|
+
}
|
|
4239
|
+
});
|
|
4240
|
+
agentCmd.command("list").description("List persistent agent definitions").option("--dir <path>", "Project directory", ".").option("--json", "Output as JSON").action(async (options) => {
|
|
4241
|
+
const registry = new AgentRegistry(resolve(options.dir));
|
|
4242
|
+
registry.loadFromDisk();
|
|
4243
|
+
const agents = registry.list();
|
|
4244
|
+
if (options.json) {
|
|
4245
|
+
outputJson({ agents: agents.map((h) => ({
|
|
4246
|
+
name: h.name,
|
|
4247
|
+
model: h.definition.model,
|
|
4248
|
+
backend: h.definition.backend,
|
|
4249
|
+
soul: h.definition.soul,
|
|
4250
|
+
contextDir: h.contextDir
|
|
4251
|
+
})) });
|
|
4252
|
+
return;
|
|
4253
|
+
}
|
|
4254
|
+
if (agents.length === 0) {
|
|
4255
|
+
console.log("No agent definitions found in .agents/");
|
|
4256
|
+
return;
|
|
4257
|
+
}
|
|
4258
|
+
for (const h of agents) {
|
|
4259
|
+
const soul = h.definition.soul?.role ? ` (${h.definition.soul.role})` : "";
|
|
4260
|
+
console.log(`${h.name.padEnd(16)} ${h.definition.model}${soul}`);
|
|
4261
|
+
}
|
|
4262
|
+
});
|
|
4263
|
+
agentCmd.command("info <name>").description("Show agent definition details").option("--dir <path>", "Project directory", ".").option("--json", "Output as JSON").action(async (name, options) => {
|
|
4264
|
+
const registry = new AgentRegistry(resolve(options.dir));
|
|
4265
|
+
registry.loadFromDisk();
|
|
4266
|
+
const handle = registry.get(name);
|
|
4267
|
+
if (!handle) {
|
|
4268
|
+
console.error(`Agent not found: ${name}`);
|
|
4269
|
+
process.exit(1);
|
|
4270
|
+
}
|
|
4271
|
+
const def = handle.definition;
|
|
4272
|
+
if (options.json) {
|
|
4273
|
+
outputJson({
|
|
4274
|
+
...def,
|
|
4275
|
+
contextDir: handle.contextDir
|
|
4276
|
+
});
|
|
4277
|
+
return;
|
|
4278
|
+
}
|
|
4279
|
+
console.log(`Name: ${def.name}`);
|
|
4280
|
+
console.log(`Model: ${def.model}`);
|
|
4281
|
+
if (def.backend) console.log(`Backend: ${def.backend}`);
|
|
4282
|
+
if (def.prompt.system) {
|
|
4283
|
+
const preview = def.prompt.system.length > 80 ? def.prompt.system.slice(0, 77) + "..." : def.prompt.system;
|
|
4284
|
+
console.log(`Prompt: ${preview}`);
|
|
4285
|
+
}
|
|
4286
|
+
if (def.soul) {
|
|
4287
|
+
if (def.soul.role) console.log(`Role: ${def.soul.role}`);
|
|
4288
|
+
if (def.soul.expertise) console.log(`Expert: ${def.soul.expertise.join(", ")}`);
|
|
4289
|
+
if (def.soul.style) console.log(`Style: ${def.soul.style}`);
|
|
4290
|
+
if (def.soul.principles) {
|
|
4291
|
+
console.log(`Principles:`);
|
|
4292
|
+
for (const p of def.soul.principles) console.log(` - ${p}`);
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
console.log(`Context: ${handle.contextDir}`);
|
|
4296
|
+
});
|
|
4297
|
+
agentCmd.command("delete <name>").description("Delete agent definition and context").option("--dir <path>", "Project directory", ".").action(async (name, options) => {
|
|
4298
|
+
const registry = new AgentRegistry(resolve(options.dir));
|
|
4299
|
+
registry.loadFromDisk();
|
|
4300
|
+
if (!registry.has(name)) {
|
|
4301
|
+
console.error(`Agent not found: ${name}`);
|
|
4302
|
+
process.exit(1);
|
|
4303
|
+
}
|
|
4304
|
+
registry.delete(name);
|
|
4305
|
+
console.log(`Deleted: ${name}`);
|
|
4306
|
+
});
|
|
3746
4307
|
}
|
|
3747
4308
|
|
|
3748
4309
|
//#endregion
|
|
@@ -3872,7 +4433,7 @@ Note: Workflow name is inferred from YAML 'name' field or filename.
|
|
|
3872
4433
|
Workflow-defined params (see 'params:' in YAML) are passed after '--'.
|
|
3873
4434
|
Set GITHUB_TOKEN env var to access private repositories.
|
|
3874
4435
|
`).action(async (file, options) => {
|
|
3875
|
-
const { parseWorkflowFile, parseWorkflowParams, formatParamHelp, runWorkflowWithLoops } = await import("../workflow-
|
|
4436
|
+
const { parseWorkflowFile, parseWorkflowParams, formatParamHelp, runWorkflowWithLoops } = await import("../workflow-LOZUlaDo.mjs");
|
|
3876
4437
|
const tag = options.tag || DEFAULT_TAG;
|
|
3877
4438
|
const parsedWorkflow = await parseWorkflowFile(file, { tag });
|
|
3878
4439
|
const workflowName = parsedWorkflow.name;
|
|
@@ -3895,7 +4456,7 @@ Note: Workflow name is inferred from YAML 'name' field or filename.
|
|
|
3895
4456
|
isCleaningUp = true;
|
|
3896
4457
|
console.log("\nInterrupted, cleaning up...");
|
|
3897
4458
|
if (loops) {
|
|
3898
|
-
const { shutdownLoops } = await import("../workflow-
|
|
4459
|
+
const { shutdownLoops } = await import("../workflow-LOZUlaDo.mjs");
|
|
3899
4460
|
const { createSilentLogger } = await Promise.resolve().then(() => logger_exports);
|
|
3900
4461
|
await shutdownLoops(loops, createSilentLogger());
|
|
3901
4462
|
}
|
|
@@ -3970,7 +4531,7 @@ Workflow runs inside the daemon. Use ls/stop to manage:
|
|
|
3970
4531
|
Note: Workflow name is inferred from YAML 'name' field or filename.
|
|
3971
4532
|
Workflow-defined params (see 'params:' in YAML) are passed after '--'.
|
|
3972
4533
|
`).action(async (file, options) => {
|
|
3973
|
-
const { parseWorkflowFile, parseWorkflowParams, formatParamHelp } = await import("../workflow-
|
|
4534
|
+
const { parseWorkflowFile, parseWorkflowParams, formatParamHelp } = await import("../workflow-LOZUlaDo.mjs");
|
|
3974
4535
|
const { ensureDaemon } = await Promise.resolve().then(() => agent_exports);
|
|
3975
4536
|
const tag = options.tag || DEFAULT_TAG;
|
|
3976
4537
|
const parsedWorkflow = await parseWorkflowFile(file, { tag });
|
|
@@ -4169,7 +4730,7 @@ function registerInfoCommands(program) {
|
|
|
4169
4730
|
console.log(`Auto: AGENT_DEFAULT_MODELS="deepseek-chat, anthropic/claude-sonnet-4-5"`);
|
|
4170
4731
|
});
|
|
4171
4732
|
program.command("backends").description("Check available backends (SDK, CLI tools)").action(async () => {
|
|
4172
|
-
const { listBackends } = await import("../backends-
|
|
4733
|
+
const { listBackends } = await import("../backends-DUvcm-ce.mjs");
|
|
4173
4734
|
const backends = await listBackends();
|
|
4174
4735
|
console.log("Backend Status:\n");
|
|
4175
4736
|
for (const backend of backends) {
|
|
@@ -4199,7 +4760,7 @@ Examples:
|
|
|
4199
4760
|
$ agent-worker doc read @review:pr-123 # Read specific workflow:tag document
|
|
4200
4761
|
`).action(async (targetInput) => {
|
|
4201
4762
|
const dir = await resolveDir(targetInput);
|
|
4202
|
-
const { createFileContextProvider } = await import("../context-
|
|
4763
|
+
const { createFileContextProvider } = await import("../context-CoRTddGx.mjs");
|
|
4203
4764
|
const content = await createFileContextProvider(dir, []).readDocument();
|
|
4204
4765
|
console.log(content || "(empty document)");
|
|
4205
4766
|
});
|
|
@@ -4217,7 +4778,7 @@ Examples:
|
|
|
4217
4778
|
process.exit(1);
|
|
4218
4779
|
}
|
|
4219
4780
|
const dir = await resolveDir(targetInput);
|
|
4220
|
-
const { createFileContextProvider } = await import("../context-
|
|
4781
|
+
const { createFileContextProvider } = await import("../context-CoRTddGx.mjs");
|
|
4221
4782
|
await createFileContextProvider(dir, []).writeDocument(content);
|
|
4222
4783
|
console.log("Document written");
|
|
4223
4784
|
});
|
|
@@ -4235,7 +4796,7 @@ Examples:
|
|
|
4235
4796
|
process.exit(1);
|
|
4236
4797
|
}
|
|
4237
4798
|
const dir = await resolveDir(targetInput);
|
|
4238
|
-
const { createFileContextProvider } = await import("../context-
|
|
4799
|
+
const { createFileContextProvider } = await import("../context-CoRTddGx.mjs");
|
|
4239
4800
|
await createFileContextProvider(dir, []).appendDocument(content);
|
|
4240
4801
|
console.log("Content appended");
|
|
4241
4802
|
});
|
|
@@ -4249,7 +4810,7 @@ async function resolveDir(targetInput) {
|
|
|
4249
4810
|
|
|
4250
4811
|
//#endregion
|
|
4251
4812
|
//#region package.json
|
|
4252
|
-
var version = "0.
|
|
4813
|
+
var version = "0.17.0";
|
|
4253
4814
|
|
|
4254
4815
|
//#endregion
|
|
4255
4816
|
//#region src/cli/index.ts
|
|
@@ -4279,4 +4840,4 @@ registerDocCommands(program);
|
|
|
4279
4840
|
program.parse();
|
|
4280
4841
|
|
|
4281
4842
|
//#endregion
|
|
4282
|
-
export {
|
|
4843
|
+
export { formatInbox$1 as $, resolveContextDir as A, MENTION_PATTERN as B, retrySection as C, FileContextProvider as D, runWithHttp as E, DefaultChannelStore as F, createResourceRef as G, RESOURCE_PREFIX$1 as H, FileStorage as I, shouldUseResource as J, extractMentions as K, MemoryStorage as L, DefaultResourceStore as M, DefaultDocumentStore as N, createFileContextProvider as O, DefaultInboxStore as P, createLogTool as Q, ContextProviderImpl as R, projectSection as S, LOOP_DEFAULTS as T, RESOURCE_SCHEME as U, MESSAGE_LENGTH_THRESHOLD as V, calculatePriority as W, formatProposal as X, createContextMCPServer as Y, formatProposalList as Z, documentSection as _, getBackendByType as a, inboxSection as b, createAgentLoop as c, generateWorkflowMCPConfig as d, formatToolParams as et, writeBackendMcpConfig as f, buildAgentPrompt as g, assemblePrompt as h, createSilentLogger as i, DefaultStatusStore as j, getDefaultContextDir as k, runSdkAgent as l, activitySection as m, createWiredLoop as n, EventLog as nt, getBackendForModel as o, DEFAULT_SECTIONS as p, generateResourceId as q, createChannelLogger as r, checkWorkflowIdle as s, createMinimalRuntime as t, getAgentId as tt, runMockAgent as u, exitSection as v, workflowSection as w, instructionsSection as x, formatInbox as y, CONTEXT_DEFAULTS as z };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { $ as formatInbox, A as resolveContextDir, B as MENTION_PATTERN, D as FileContextProvider, F as DefaultChannelStore, G as createResourceRef, H as RESOURCE_PREFIX, I as FileStorage, J as shouldUseResource, K as extractMentions, L as MemoryStorage, M as DefaultResourceStore, N as DefaultDocumentStore, O as createFileContextProvider, P as DefaultInboxStore, Q as createLogTool, R as ContextProviderImpl, U as RESOURCE_SCHEME, V as MESSAGE_LENGTH_THRESHOLD, W as calculatePriority, X as formatProposal, Y as createContextMCPServer, Z as formatProposalList, et as formatToolParams, j as DefaultStatusStore, k as getDefaultContextDir, nt as EventLog, q as generateResourceId, tt as getAgentId, z as CONTEXT_DEFAULTS } from "./cli/index.mjs";
|
|
2
|
+
import { n as createMemoryContextProvider, t as MemoryContextProvider } from "./memory-provider-Z9D8NdwS.mjs";
|
|
3
|
+
|
|
4
|
+
export { createFileContextProvider };
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as createModelWithProvider, M as SUPPORTED_PROVIDERS, N as createModel, P as createModelAsync, a as createMockBackend, d as CodexBackend, f as ClaudeCodeBackend, i as MockAIBackend, j as FRONTIER_MODELS, n as createBackend, o as SdkBackend, r as listBackends, t as checkBackends, u as CursorBackend } from "./backends-
|
|
1
|
+
import { F as createModelWithProvider, M as SUPPORTED_PROVIDERS, N as createModel, P as createModelAsync, a as createMockBackend, d as CodexBackend, f as ClaudeCodeBackend, i as MockAIBackend, j as FRONTIER_MODELS, n as createBackend, o as SdkBackend, r as listBackends, t as checkBackends, u as CursorBackend } from "./backends-D7DT0uox.mjs";
|
|
2
2
|
import { t as createTool } from "./create-tool-gcUuI1FD.mjs";
|
|
3
3
|
import { ToolLoopAgent, stepCountIs } from "ai";
|
|
4
4
|
import { createBashTool } from "bash-tool";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { F as DefaultChannelStore, L as MemoryStorage, M as DefaultResourceStore, N as DefaultDocumentStore, P as DefaultInboxStore, R as ContextProviderImpl, j as DefaultStatusStore } from "./cli/index.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/workflow/context/memory-provider.ts
|
|
4
4
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import "./backends-
|
|
1
|
+
import "./backends-D7DT0uox.mjs";
|
|
2
2
|
import "./create-tool-gcUuI1FD.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { n as createMemoryContextProvider } from "./memory-provider-
|
|
3
|
+
import { D as FileContextProvider, E as runWithHttp, O as createFileContextProvider, Y as createContextMCPServer, i as createSilentLogger, n as createWiredLoop, nt as EventLog, r as createChannelLogger, s as checkWorkflowIdle } from "./cli/index.mjs";
|
|
4
|
+
import { n as createMemoryContextProvider } from "./memory-provider-Z9D8NdwS.mjs";
|
|
5
5
|
import { existsSync, mkdirSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
@@ -602,6 +602,26 @@ async function runWorkflowWithLoops(config) {
|
|
|
602
602
|
}
|
|
603
603
|
}
|
|
604
604
|
if (isIdle) {
|
|
605
|
+
const failedAgents = [...loops.entries()].filter(([, loop]) => loop.hasFailures).map(([agentName, loop]) => ({
|
|
606
|
+
name: agentName,
|
|
607
|
+
error: loop.lastError
|
|
608
|
+
}));
|
|
609
|
+
if (failedAgents.length > 0) {
|
|
610
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
611
|
+
const details = failedAgents.map((a) => `${a.name}: ${a.error}`).join("; ");
|
|
612
|
+
logger.info(`Workflow failed (${elapsed}s): ${details}`);
|
|
613
|
+
channelWatcher?.stop();
|
|
614
|
+
await shutdownLoops(loops, logger);
|
|
615
|
+
await runtime.shutdown();
|
|
616
|
+
return {
|
|
617
|
+
success: false,
|
|
618
|
+
error: details,
|
|
619
|
+
setupResults: runtime.setupResults,
|
|
620
|
+
duration: Date.now() - startTime,
|
|
621
|
+
contextProvider: runtime.contextProvider,
|
|
622
|
+
feedback: runtime.getFeedback?.()
|
|
623
|
+
};
|
|
624
|
+
}
|
|
605
625
|
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
606
626
|
logger.info(`Workflow complete (${elapsed}s)`);
|
|
607
627
|
break;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./backends-
|
|
1
|
+
import "./backends-D7DT0uox.mjs";
|
|
2
2
|
import "./create-tool-gcUuI1FD.mjs";
|
|
3
|
-
import { a as getBackendByType, b as
|
|
4
|
-
import "./memory-provider-
|
|
5
|
-
import { createWorkflowProvider, initWorkflow, n as interpolate, runWorkflowWithLoops, shutdownLoops, t as createContext } from "./runner-
|
|
3
|
+
import { A as resolveContextDir, C as retrySection, S as projectSection, T as LOOP_DEFAULTS, _ as documentSection, a as getBackendByType, b as inboxSection, c as createAgentLoop, d as generateWorkflowMCPConfig, f as writeBackendMcpConfig, g as buildAgentPrompt, h as assemblePrompt, i as createSilentLogger, l as runSdkAgent, m as activitySection, n as createWiredLoop, o as getBackendForModel, p as DEFAULT_SECTIONS, r as createChannelLogger, s as checkWorkflowIdle, t as createMinimalRuntime, u as runMockAgent, v as exitSection, w as workflowSection, x as instructionsSection, y as formatInbox, z as CONTEXT_DEFAULTS } from "./cli/index.mjs";
|
|
4
|
+
import "./memory-provider-Z9D8NdwS.mjs";
|
|
5
|
+
import { createWorkflowProvider, initWorkflow, n as interpolate, runWorkflowWithLoops, shutdownLoops, t as createContext } from "./runner-BmT0Y8MD.mjs";
|
|
6
6
|
import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
7
7
|
import { basename, dirname, join, resolve } from "node:path";
|
|
8
8
|
import { parse } from "yaml";
|
|
@@ -10,6 +10,13 @@ import { homedir } from "node:os";
|
|
|
10
10
|
import { execFileSync } from "node:child_process";
|
|
11
11
|
import { parseArgs } from "node:util";
|
|
12
12
|
|
|
13
|
+
//#region src/workflow/types.ts
|
|
14
|
+
/** Type guard: is this agent entry a reference to a global agent? */
|
|
15
|
+
function isRefAgentEntry(entry) {
|
|
16
|
+
return "ref" in entry && typeof entry.ref === "string";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
13
20
|
//#region src/workflow/source.ts
|
|
14
21
|
/**
|
|
15
22
|
* Workflow source resolver — supports local files and remote GitHub references.
|
|
@@ -263,7 +270,8 @@ async function parseWorkflowFile(filePath, options) {
|
|
|
263
270
|
}
|
|
264
271
|
const name = raw.name || source.inferredName;
|
|
265
272
|
const agents = {};
|
|
266
|
-
for (const [agentName,
|
|
273
|
+
for (const [agentName, entry] of Object.entries(raw.agents)) if (isRefAgentEntry(entry)) agents[agentName] = resolveRefAgent(entry, options?.agentRegistry);
|
|
274
|
+
else agents[agentName] = await resolveInlineAgent(entry, source.readRelativeFile);
|
|
267
275
|
const context = resolveContext(raw.context, contextBaseDir, name, workflow, tag);
|
|
268
276
|
return {
|
|
269
277
|
name,
|
|
@@ -315,14 +323,14 @@ function resolveContext(config, workflowDir, workflowName, workflow, tag) {
|
|
|
315
323
|
};
|
|
316
324
|
}
|
|
317
325
|
/**
|
|
318
|
-
* Resolve agent definition (load system prompt from file if needed).
|
|
326
|
+
* Resolve an inline agent definition (load system prompt from file if needed).
|
|
319
327
|
*
|
|
320
328
|
* Uses a `readRelativeFile` function to abstract local vs remote file access.
|
|
321
329
|
* Also transforms `wakeup` and `wakeup_prompt` fields into a `ScheduleConfig`
|
|
322
330
|
* object, which is the format expected by the daemon and loop layers
|
|
323
331
|
* for setting up periodic wakeup timers.
|
|
324
332
|
*/
|
|
325
|
-
async function
|
|
333
|
+
async function resolveInlineAgent(agent, readRelativeFile) {
|
|
326
334
|
let resolvedSystemPrompt = agent.system_prompt;
|
|
327
335
|
if (resolvedSystemPrompt?.endsWith(".txt") || resolvedSystemPrompt?.endsWith(".md")) {
|
|
328
336
|
const content = await readRelativeFile(resolvedSystemPrompt);
|
|
@@ -336,7 +344,34 @@ async function resolveAgent(agent, readRelativeFile) {
|
|
|
336
344
|
return {
|
|
337
345
|
...agent,
|
|
338
346
|
resolvedSystemPrompt,
|
|
339
|
-
schedule
|
|
347
|
+
schedule,
|
|
348
|
+
isRef: false
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Resolve a ref agent entry — load from AgentRegistry, map to WorkflowAgentDef,
|
|
353
|
+
* apply workflow overrides (prompt.append, max_tokens, max_steps).
|
|
354
|
+
*/
|
|
355
|
+
function resolveRefAgent(entry, registry) {
|
|
356
|
+
if (!registry) throw new Error(`Agent ref "${entry.ref}" requires an AgentRegistry. Pass agentRegistry in ParseOptions.`);
|
|
357
|
+
const handle = registry.get(entry.ref);
|
|
358
|
+
if (!handle) throw new Error(`Agent ref "${entry.ref}" not found in registry. Available agents: ${registry.list().map((h) => h.definition.name).join(", ") || "(none)"}`);
|
|
359
|
+
const def = handle.definition;
|
|
360
|
+
const basePrompt = def.prompt.system ?? "";
|
|
361
|
+
let resolvedSystemPrompt = basePrompt;
|
|
362
|
+
if (entry.prompt?.append) resolvedSystemPrompt = basePrompt ? `${basePrompt}\n\n${entry.prompt.append}` : entry.prompt.append;
|
|
363
|
+
const backend = def.backend === "sdk" ? "default" : def.backend;
|
|
364
|
+
return {
|
|
365
|
+
model: def.model,
|
|
366
|
+
backend,
|
|
367
|
+
provider: def.provider,
|
|
368
|
+
system_prompt: basePrompt,
|
|
369
|
+
max_tokens: entry.max_tokens ?? def.max_tokens,
|
|
370
|
+
max_steps: entry.max_steps ?? def.max_steps,
|
|
371
|
+
schedule: def.schedule,
|
|
372
|
+
resolvedSystemPrompt,
|
|
373
|
+
handle,
|
|
374
|
+
isRef: true
|
|
340
375
|
};
|
|
341
376
|
}
|
|
342
377
|
/**
|
|
@@ -538,6 +573,51 @@ function validateAgent(name, agent, errors) {
|
|
|
538
573
|
return;
|
|
539
574
|
}
|
|
540
575
|
const a = agent;
|
|
576
|
+
if (typeof a.ref === "string") validateRefAgent(path, a, errors);
|
|
577
|
+
else validateInlineAgent(path, a, errors);
|
|
578
|
+
}
|
|
579
|
+
function validateRefAgent(path, a, errors) {
|
|
580
|
+
if (!a.ref.length) errors.push({
|
|
581
|
+
path: `${path}.ref`,
|
|
582
|
+
message: "Field \"ref\" must be a non-empty string"
|
|
583
|
+
});
|
|
584
|
+
if (a.prompt !== void 0) if (typeof a.prompt !== "object" || a.prompt === null) errors.push({
|
|
585
|
+
path: `${path}.prompt`,
|
|
586
|
+
message: "Field \"prompt\" for ref agents must be an object with optional \"append\""
|
|
587
|
+
});
|
|
588
|
+
else {
|
|
589
|
+
const p = a.prompt;
|
|
590
|
+
if (p.append !== void 0 && typeof p.append !== "string") errors.push({
|
|
591
|
+
path: `${path}.prompt.append`,
|
|
592
|
+
message: "Field \"prompt.append\" must be a string"
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
if (a.system_prompt !== void 0) errors.push({
|
|
596
|
+
path: `${path}.system_prompt`,
|
|
597
|
+
message: "Field \"system_prompt\" cannot be used with ref agents — use \"prompt.append\" instead"
|
|
598
|
+
});
|
|
599
|
+
for (const field of [
|
|
600
|
+
"model",
|
|
601
|
+
"backend",
|
|
602
|
+
"provider",
|
|
603
|
+
"tools",
|
|
604
|
+
"wakeup",
|
|
605
|
+
"wakeup_prompt",
|
|
606
|
+
"timeout"
|
|
607
|
+
]) if (a[field] !== void 0) errors.push({
|
|
608
|
+
path: `${path}.${field}`,
|
|
609
|
+
message: `Field "${field}" cannot be used with ref agents — it comes from the agent definition`
|
|
610
|
+
});
|
|
611
|
+
if (a.max_tokens !== void 0 && typeof a.max_tokens !== "number") errors.push({
|
|
612
|
+
path: `${path}.max_tokens`,
|
|
613
|
+
message: "Field \"max_tokens\" must be a number"
|
|
614
|
+
});
|
|
615
|
+
if (a.max_steps !== void 0 && typeof a.max_steps !== "number") errors.push({
|
|
616
|
+
path: `${path}.max_steps`,
|
|
617
|
+
message: "Field \"max_steps\" must be a number"
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
function validateInlineAgent(path, a, errors) {
|
|
541
621
|
const backend = typeof a.backend === "string" ? a.backend : "default";
|
|
542
622
|
if (a.model !== void 0 && typeof a.model !== "string") errors.push({
|
|
543
623
|
path: `${path}.model`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "SDK and CLI for creating and testing agent workers with Vercel AI SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
@@ -29,9 +29,17 @@
|
|
|
29
29
|
"format": "oxfmt src",
|
|
30
30
|
"format:check": "oxfmt src --check",
|
|
31
31
|
"typecheck": "tsgo",
|
|
32
|
+
"gen:schema": "bun scripts/gen-workflow-ref.ts > docs/workflow/SCHEMA.md",
|
|
32
33
|
"prepublishOnly": "bun run build"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
36
|
+
"@ai-sdk/anthropic": "^3.0.0",
|
|
37
|
+
"@ai-sdk/deepseek": "^1.0.0",
|
|
38
|
+
"@ai-sdk/google": "^1.0.0",
|
|
39
|
+
"@ai-sdk/groq": "^1.0.0",
|
|
40
|
+
"@ai-sdk/mistral": "^1.0.0",
|
|
41
|
+
"@ai-sdk/openai": "^3.0.0",
|
|
42
|
+
"@ai-sdk/xai": "^1.0.0",
|
|
35
43
|
"@clack/prompts": "^1.0.0",
|
|
36
44
|
"@hono/node-server": "^1.19.9",
|
|
37
45
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
@@ -50,13 +58,6 @@
|
|
|
50
58
|
"zod": "^4.3.6"
|
|
51
59
|
},
|
|
52
60
|
"devDependencies": {
|
|
53
|
-
"@ai-sdk/anthropic": "^3.0.0",
|
|
54
|
-
"@ai-sdk/deepseek": "^1.0.0",
|
|
55
|
-
"@ai-sdk/google": "^1.0.0",
|
|
56
|
-
"@ai-sdk/groq": "^1.0.0",
|
|
57
|
-
"@ai-sdk/mistral": "^1.0.0",
|
|
58
|
-
"@ai-sdk/openai": "^3.0.0",
|
|
59
|
-
"@ai-sdk/xai": "^1.0.0",
|
|
60
61
|
"@types/bun": "latest",
|
|
61
62
|
"@types/node": ">=22",
|
|
62
63
|
"@typescript/native-preview": "^7.0.0-dev.20260203.1",
|
|
@@ -64,39 +65,6 @@
|
|
|
64
65
|
"oxlint": "^1.43.0",
|
|
65
66
|
"tsdown": "^0.20.1"
|
|
66
67
|
},
|
|
67
|
-
"peerDependencies": {
|
|
68
|
-
"@ai-sdk/anthropic": "^3.0.0",
|
|
69
|
-
"@ai-sdk/deepseek": "^1.0.0",
|
|
70
|
-
"@ai-sdk/google": "^1.0.0",
|
|
71
|
-
"@ai-sdk/groq": "^1.0.0",
|
|
72
|
-
"@ai-sdk/mistral": "^1.0.0",
|
|
73
|
-
"@ai-sdk/openai": "^3.0.0",
|
|
74
|
-
"@ai-sdk/xai": "^1.0.0",
|
|
75
|
-
"typescript": "^5"
|
|
76
|
-
},
|
|
77
|
-
"peerDependenciesMeta": {
|
|
78
|
-
"@ai-sdk/anthropic": {
|
|
79
|
-
"optional": true
|
|
80
|
-
},
|
|
81
|
-
"@ai-sdk/openai": {
|
|
82
|
-
"optional": true
|
|
83
|
-
},
|
|
84
|
-
"@ai-sdk/deepseek": {
|
|
85
|
-
"optional": true
|
|
86
|
-
},
|
|
87
|
-
"@ai-sdk/google": {
|
|
88
|
-
"optional": true
|
|
89
|
-
},
|
|
90
|
-
"@ai-sdk/groq": {
|
|
91
|
-
"optional": true
|
|
92
|
-
},
|
|
93
|
-
"@ai-sdk/mistral": {
|
|
94
|
-
"optional": true
|
|
95
|
-
},
|
|
96
|
-
"@ai-sdk/xai": {
|
|
97
|
-
"optional": true
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
68
|
"repository": {
|
|
101
69
|
"type": "git",
|
|
102
70
|
"url": "https://github.com/lidessen/moniro.git",
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { A as MENTION_PATTERN, B as formatProposal, C as DefaultDocumentStore, D as MemoryStorage, E as FileStorage, F as createResourceRef, G as getAgentId, H as createLogTool, I as extractMentions, K as EventLog, L as generateResourceId, M as RESOURCE_PREFIX, N as RESOURCE_SCHEME, O as ContextProviderImpl, P as calculatePriority, R as shouldUseResource, S as DefaultResourceStore, T as DefaultChannelStore, U as formatInbox, V as formatProposalList, W as formatToolParams, _ as FileContextProvider, b as resolveContextDir, j as MESSAGE_LENGTH_THRESHOLD, k as CONTEXT_DEFAULTS, v as createFileContextProvider, w as DefaultInboxStore, x as DefaultStatusStore, y as getDefaultContextDir, z as createContextMCPServer } from "./cli/index.mjs";
|
|
2
|
-
import { n as createMemoryContextProvider, t as MemoryContextProvider } from "./memory-provider-ZLOKyCxA.mjs";
|
|
3
|
-
|
|
4
|
-
export { createFileContextProvider };
|