@agent-spaces/server 0.3.65 → 0.3.67
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/adapters/agent-runtime.js +4 -0
- package/dist/adapters/codex-function-tool-bridge.js +144 -0
- package/dist/adapters/codex-runtime.js +23 -4
- package/dist/adapters/git.js +12 -1
- package/dist/adapters/hermes-runtime.js +210 -0
- package/dist/agents/issue-task-controller.js +69 -2
- package/dist/agents/pull-request-agent.js +115 -0
- package/dist/agents/scheduler-agent.js +1 -1
- package/dist/app.js +6 -2
- package/dist/middleware/auth.js +1 -1
- package/dist/package.json +2 -1
- package/dist/routes/agent-sse.js +1 -0
- package/dist/routes/issue.js +1 -1
- package/dist/routes/notification.js +10 -0
- package/dist/routes/version.js +94 -0
- package/dist/routes/worktree.js +93 -0
- package/dist/services/agent.js +1 -1
- package/dist/services/notification-center.js +9 -0
- package/dist/services/version.js +59 -0
- package/dist/services/workflow-command-runner.js +23 -0
- package/dist/services/workflow.js +25 -8
- package/dist/services/workspace.js +23 -1
- package/dist/services/worktree.js +207 -0
- package/dist/storage/worktree-store.js +33 -0
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +4 -4
- package/dist/web/__next._full.txt +21 -21
- package/dist/web/__next._head.txt +4 -4
- package/dist/web/__next._index.txt +13 -13
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/chunks/{08x85faoz58i7.js → 0-92u-tiuy40a.js} +4 -4
- package/dist/web/_next/static/chunks/{0db14450oqzhg.js → 0-yizd8osmas2.js} +1 -1
- package/dist/web/_next/static/chunks/0.ln~3mnv8.69.js +1 -0
- package/dist/web/_next/static/chunks/{11cchuwoupmbf.js → 01y3e9pemva7b.js} +10 -10
- package/dist/web/_next/static/chunks/02l~tmzrgny.~.js +1 -0
- package/dist/web/_next/static/chunks/03f1bwuynjdmk.js +1 -0
- package/dist/web/_next/static/chunks/04cb4kc9r2pd1.js +1 -0
- package/dist/web/_next/static/chunks/04de6ha5_e3pp.js +1 -0
- package/dist/web/_next/static/chunks/09he0p79ov1rn.js +1 -0
- package/dist/web/_next/static/chunks/09vpocs4zuc8h.js +1 -0
- package/dist/web/_next/static/chunks/0_o2exgr0e_nh.js +1 -0
- package/dist/web/_next/static/chunks/{0xbaakj0dfc0..js → 0_y9nskc-xt2k.js} +1 -1
- package/dist/web/_next/static/chunks/0clnj-1fi6u8j.js +3 -0
- package/dist/web/_next/static/chunks/0gv40by0zzxs_.js +1 -0
- package/dist/web/_next/static/chunks/0iusr8y88.kjg.js +1 -0
- package/dist/web/_next/static/chunks/{0-a.-hl3wv99k.js → 0j-8418fxvkd1.js} +1 -1
- package/dist/web/_next/static/chunks/0j.wxxyoclma5.js +1 -0
- package/dist/web/_next/static/chunks/{0.6b64sy0g5.3.js → 0jq.16sozqik..js} +1 -1
- package/dist/web/_next/static/chunks/0jw8xdk_fpnp0.css +1 -0
- package/dist/web/_next/static/chunks/0l1lkd-wcotma.js +1 -0
- package/dist/web/_next/static/chunks/{0u6h-hqc_i52e.js → 0ncxf_zr4n839.js} +1 -1
- package/dist/web/_next/static/chunks/0ndo7um~v7vco.js +1 -0
- package/dist/web/_next/static/chunks/0ngk.~kan8wq_.js +1 -0
- package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +1 -0
- package/dist/web/_next/static/chunks/0rt9bofnn6qvn.js +1 -0
- package/dist/web/_next/static/chunks/0t64.9jd00hio.js +3 -0
- package/dist/web/_next/static/chunks/0taui9--5n6-5.js +1 -0
- package/dist/web/_next/static/chunks/0ty39169lh~z3.js +1 -0
- package/dist/web/_next/static/chunks/0v8nof~waedlu.js +1 -0
- package/dist/web/_next/static/chunks/{0-pi.67w.ymsr.js → 0vcfoagnpf.aw.js} +1 -1
- package/dist/web/_next/static/chunks/{0vis~lx0nxgn-.js → 0wwwq.gyyz0vg.js} +3 -3
- package/dist/web/_next/static/chunks/0x5lak8yz7pm7.js +1 -0
- package/dist/web/_next/static/chunks/0xon_.4-o0_zq.js +7 -0
- package/dist/web/_next/static/chunks/0zjnrbt7inime.js +1 -0
- package/dist/web/_next/static/chunks/0zwebpitvpuv1.js +1 -0
- package/dist/web/_next/static/chunks/0~02o4g-o6n~m.js +1 -0
- package/dist/web/_next/static/chunks/{0w7.tb29958zj.js → 0~fjezp.k4qk3.js} +1 -1
- package/dist/web/_next/static/chunks/0~sh93re1-_rp.js +1 -0
- package/dist/web/_next/static/chunks/10_niiq0k6o-p.js +5 -0
- package/dist/web/_next/static/chunks/12tjr0_2ccrtq.js +2 -0
- package/dist/web/_next/static/chunks/12x5.go_x9mfk.js +1 -0
- package/dist/web/_next/static/chunks/{0l2w0qz1ea7ql.js → 12~5dk9i9d16t.js} +2 -2
- package/dist/web/_next/static/chunks/13iye4rv5p31b.js +1 -0
- package/dist/web/_next/static/chunks/{01~quun6sefgl.js → 13~y-b.b9rrw4.js} +1 -1
- package/dist/web/_next/static/chunks/151wzhb5q0lo5.js +1 -0
- package/dist/web/_not-found/__next._full.txt +19 -19
- package/dist/web/_not-found/__next._head.txt +4 -4
- package/dist/web/_not-found/__next._index.txt +13 -13
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dist/web/_not-found/__next._not-found.txt +3 -3
- package/dist/web/_not-found/__next._tree.txt +2 -2
- package/dist/web/_not-found.html +1 -1
- package/dist/web/_not-found.txt +19 -19
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +21 -21
- package/dist/web/login/__next._full.txt +21 -21
- package/dist/web/login/__next._head.txt +4 -4
- package/dist/web/login/__next._index.txt +13 -13
- package/dist/web/login/__next._tree.txt +2 -2
- package/dist/web/login/__next.login.__PAGE__.txt +4 -4
- package/dist/web/login/__next.login.txt +3 -3
- package/dist/web/login.html +1 -1
- package/dist/web/login.txt +21 -21
- package/dist/web/settings/__next._full.txt +23 -23
- package/dist/web/settings/__next._head.txt +4 -4
- package/dist/web/settings/__next._index.txt +13 -13
- package/dist/web/settings/__next._tree.txt +2 -2
- package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
- package/dist/web/settings/__next.settings.txt +5 -5
- package/dist/web/settings/agents/__next._full.txt +23 -23
- package/dist/web/settings/agents/__next._head.txt +4 -4
- package/dist/web/settings/agents/__next._index.txt +13 -13
- package/dist/web/settings/agents/__next._tree.txt +2 -2
- package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
- package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
- package/dist/web/settings/agents/__next.settings.txt +5 -5
- package/dist/web/settings/agents.html +1 -1
- package/dist/web/settings/agents.txt +23 -23
- package/dist/web/settings/mcps/__next._full.txt +23 -23
- package/dist/web/settings/mcps/__next._head.txt +4 -4
- package/dist/web/settings/mcps/__next._index.txt +13 -13
- package/dist/web/settings/mcps/__next._tree.txt +2 -2
- package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
- package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
- package/dist/web/settings/mcps/__next.settings.txt +5 -5
- package/dist/web/settings/mcps.html +1 -1
- package/dist/web/settings/mcps.txt +23 -23
- package/dist/web/settings/models/__next._full.txt +23 -23
- package/dist/web/settings/models/__next._head.txt +4 -4
- package/dist/web/settings/models/__next._index.txt +13 -13
- package/dist/web/settings/models/__next._tree.txt +2 -2
- package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
- package/dist/web/settings/models/__next.settings.models.txt +3 -3
- package/dist/web/settings/models/__next.settings.txt +5 -5
- package/dist/web/settings/models.html +1 -1
- package/dist/web/settings/models.txt +23 -23
- package/dist/web/settings/output-styles/__next._full.txt +23 -23
- package/dist/web/settings/output-styles/__next._head.txt +4 -4
- package/dist/web/settings/output-styles/__next._index.txt +13 -13
- package/dist/web/settings/output-styles/__next._tree.txt +2 -2
- package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
- package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
- package/dist/web/settings/output-styles/__next.settings.txt +5 -5
- package/dist/web/settings/output-styles.html +1 -1
- package/dist/web/settings/output-styles.txt +23 -23
- package/dist/web/settings/prompts/__next._full.txt +23 -23
- package/dist/web/settings/prompts/__next._head.txt +4 -4
- package/dist/web/settings/prompts/__next._index.txt +13 -13
- package/dist/web/settings/prompts/__next._tree.txt +2 -2
- package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
- package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
- package/dist/web/settings/prompts/__next.settings.txt +5 -5
- package/dist/web/settings/prompts.html +1 -1
- package/dist/web/settings/prompts.txt +23 -23
- package/dist/web/settings/providers/__next._full.txt +23 -23
- package/dist/web/settings/providers/__next._head.txt +4 -4
- package/dist/web/settings/providers/__next._index.txt +13 -13
- package/dist/web/settings/providers/__next._tree.txt +2 -2
- package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
- package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
- package/dist/web/settings/providers/__next.settings.txt +5 -5
- package/dist/web/settings/providers.html +1 -1
- package/dist/web/settings/providers.txt +23 -23
- package/dist/web/settings/skills/__next._full.txt +23 -23
- package/dist/web/settings/skills/__next._head.txt +4 -4
- package/dist/web/settings/skills/__next._index.txt +13 -13
- package/dist/web/settings/skills/__next._tree.txt +2 -2
- package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
- package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
- package/dist/web/settings/skills/__next.settings.txt +5 -5
- package/dist/web/settings/skills.html +1 -1
- package/dist/web/settings/skills.txt +23 -23
- package/dist/web/settings/tools/__next._full.txt +23 -23
- package/dist/web/settings/tools/__next._head.txt +4 -4
- package/dist/web/settings/tools/__next._index.txt +13 -13
- package/dist/web/settings/tools/__next._tree.txt +2 -2
- package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
- package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
- package/dist/web/settings/tools/__next.settings.txt +5 -5
- package/dist/web/settings/tools.html +1 -1
- package/dist/web/settings/tools.txt +23 -23
- package/dist/web/settings.html +1 -1
- package/dist/web/settings.txt +23 -23
- package/dist/web/workflows/__next._full.txt +22 -22
- package/dist/web/workflows/__next._head.txt +4 -4
- package/dist/web/workflows/__next._index.txt +13 -13
- package/dist/web/workflows/__next._tree.txt +2 -2
- package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
- package/dist/web/workflows/__next.workflows.txt +3 -3
- package/dist/web/workflows.html +1 -1
- package/dist/web/workflows.txt +22 -22
- package/dist/web/workspace/_/__next._full.txt +20 -20
- package/dist/web/workspace/_/__next._head.txt +4 -4
- package/dist/web/workspace/_/__next._index.txt +13 -13
- package/dist/web/workspace/_/__next._tree.txt +2 -2
- package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.txt +3 -3
- package/dist/web/workspace/_.html +1 -1
- package/dist/web/workspace/_.txt +20 -20
- package/dist/web/workspaces/__next._full.txt +21 -21
- package/dist/web/workspaces/__next._head.txt +4 -4
- package/dist/web/workspaces/__next._index.txt +13 -13
- package/dist/web/workspaces/__next._tree.txt +2 -2
- package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
- package/dist/web/workspaces/__next.workspaces.txt +3 -3
- package/dist/web/workspaces.html +1 -1
- package/dist/web/workspaces.txt +21 -21
- package/dist/ws/agent-prompt.js +32 -3
- package/dist/ws/agent-runner.js +1 -0
- package/package.json +2 -1
- package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +0 -1
- package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +0 -1
- package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +0 -1
- package/dist/web/_next/static/chunks/0.n~yfwf4~6w-.js +0 -1
- package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +0 -1
- package/dist/web/_next/static/chunks/02j4sr9psucf-.js +0 -1
- package/dist/web/_next/static/chunks/06si8l0doqe8p.js +0 -1
- package/dist/web/_next/static/chunks/08kfx8on9oty~.js +0 -1
- package/dist/web/_next/static/chunks/08ugbvzr20drp.js +0 -2
- package/dist/web/_next/static/chunks/0_cqojbge48kq.js +0 -5
- package/dist/web/_next/static/chunks/0d6~~lv0-6kex.js +0 -3
- package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +0 -1
- package/dist/web/_next/static/chunks/0ghx3j_.-gdyd.js +0 -1
- package/dist/web/_next/static/chunks/0kbvoxla7f751.js +0 -1
- package/dist/web/_next/static/chunks/0paaat8y96ayc.js +0 -7
- package/dist/web/_next/static/chunks/0pq318f8xgafg.js +0 -1
- package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +0 -1
- package/dist/web/_next/static/chunks/0xt-8wk21gs7g.js +0 -1
- package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +0 -3
- package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +0 -1
- package/dist/web/_next/static/chunks/0z~vqzbm_q371.js +0 -1
- package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +0 -1
- package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +0 -1
- package/dist/web/_next/static/chunks/1452eibz62gs6.css +0 -1
- package/dist/web/_next/static/chunks/16zlreiems5cn.js +0 -1
- package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +0 -1
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_ssgManifest.js +0 -0
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { ClaudeCodeRuntime } from './claude-code-runtime/index.js';
|
|
5
5
|
import { CodexRuntime } from './codex-runtime.js';
|
|
6
|
+
import { HermesRuntime } from './hermes-runtime.js';
|
|
6
7
|
import { LangChainRuntime } from './langchain-runtime.js';
|
|
7
8
|
import { OpenAgentSdkRuntime } from './open-agent-sdk-runtime.js';
|
|
8
9
|
export { ClaudeCodeRuntime } from './claude-code-runtime/index.js';
|
|
9
10
|
export { CodexRuntime } from './codex-runtime.js';
|
|
11
|
+
export { HermesRuntime } from './hermes-runtime.js';
|
|
10
12
|
export { LangChainRuntime } from './langchain-runtime.js';
|
|
11
13
|
export { OpenAgentSdkRuntime } from './open-agent-sdk-runtime.js';
|
|
12
14
|
export function createAgentRuntime(configOrProvider = {}, model) {
|
|
@@ -22,6 +24,8 @@ export function createAgentRuntime(configOrProvider = {}, model) {
|
|
|
22
24
|
return new CodexRuntime(config);
|
|
23
25
|
case 'langchain':
|
|
24
26
|
return new LangChainRuntime(config);
|
|
27
|
+
case 'hermes':
|
|
28
|
+
return new HermesRuntime(config);
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
//# sourceMappingURL=agent-runtime.js.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
4
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
5
|
+
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
const AGENT_SPACES_MCP_SERVER_NAME = 'agent-spaces';
|
|
7
|
+
export async function startCodexFunctionToolBridge(functionTools, log) {
|
|
8
|
+
if (!functionTools?.length)
|
|
9
|
+
return undefined;
|
|
10
|
+
const toolsByName = new Map(functionTools.map((functionTool) => [functionTool.name, functionTool]));
|
|
11
|
+
const mcpServer = new Server({ name: AGENT_SPACES_MCP_SERVER_NAME, version: '0.1.0' }, {
|
|
12
|
+
capabilities: {
|
|
13
|
+
tools: { listChanged: false },
|
|
14
|
+
},
|
|
15
|
+
instructions: 'Agent Spaces built-in runtime tools for workspace data.',
|
|
16
|
+
});
|
|
17
|
+
const transport = new StreamableHTTPServerTransport({
|
|
18
|
+
sessionIdGenerator: randomUUID,
|
|
19
|
+
enableJsonResponse: true,
|
|
20
|
+
});
|
|
21
|
+
mcpServer.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
22
|
+
tools: functionTools.map(toMcpTool),
|
|
23
|
+
}));
|
|
24
|
+
mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
25
|
+
const functionTool = toolsByName.get(request.params.name);
|
|
26
|
+
if (!functionTool) {
|
|
27
|
+
throw new McpError(ErrorCode.InvalidParams, `Tool ${request.params.name} not found`);
|
|
28
|
+
}
|
|
29
|
+
const result = await functionTool.execute(request.params.arguments ?? {});
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: 'text',
|
|
34
|
+
text: stringifyToolResult(result),
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
...(isRecord(result) ? { structuredContent: result } : {}),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
await mcpServer.connect(transport);
|
|
41
|
+
const httpServer = createServer(async (req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
44
|
+
if (url.pathname !== '/mcp') {
|
|
45
|
+
res.writeHead(404).end('Not found');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
await transport.handleRequest(req, res);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
52
|
+
log?.(`function tool bridge request failed | ${message}`);
|
|
53
|
+
if (!res.headersSent)
|
|
54
|
+
res.writeHead(500, { 'content-type': 'text/plain; charset=utf-8' });
|
|
55
|
+
if (!res.writableEnded)
|
|
56
|
+
res.end(message);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
await listen(httpServer);
|
|
60
|
+
const address = httpServer.address();
|
|
61
|
+
if (!address || typeof address === 'string') {
|
|
62
|
+
await closeHttpServer(httpServer);
|
|
63
|
+
await mcpServer.close();
|
|
64
|
+
throw new Error('Failed to start Codex function tool bridge');
|
|
65
|
+
}
|
|
66
|
+
const url = `http://127.0.0.1:${address.port}/mcp`;
|
|
67
|
+
log?.(`function tool bridge started | url=${url} tools=${functionTools.map((tool) => tool.name).join(',')}`);
|
|
68
|
+
return {
|
|
69
|
+
name: AGENT_SPACES_MCP_SERVER_NAME,
|
|
70
|
+
url,
|
|
71
|
+
close: async () => {
|
|
72
|
+
await mcpServer.close();
|
|
73
|
+
await closeHttpServer(httpServer);
|
|
74
|
+
log?.('function tool bridge stopped');
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function toMcpTool(functionTool) {
|
|
79
|
+
return {
|
|
80
|
+
name: functionTool.name,
|
|
81
|
+
description: functionTool.description,
|
|
82
|
+
inputSchema: normalizeToolInputSchema(functionTool.inputSchema),
|
|
83
|
+
annotations: toMcpToolAnnotations(functionTool),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function normalizeToolInputSchema(schema) {
|
|
87
|
+
if (schema.type === 'object')
|
|
88
|
+
return schema;
|
|
89
|
+
return {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: normalizeSchemaProperties(schema.properties),
|
|
92
|
+
required: Array.isArray(schema.required) ? schema.required.map(String) : [],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function toMcpToolAnnotations(functionTool) {
|
|
96
|
+
if (!functionTool.annotations)
|
|
97
|
+
return undefined;
|
|
98
|
+
return {
|
|
99
|
+
readOnlyHint: functionTool.annotations.readOnly,
|
|
100
|
+
destructiveHint: functionTool.annotations.destructive,
|
|
101
|
+
openWorldHint: functionTool.annotations.openWorld,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function stringifyToolResult(result) {
|
|
105
|
+
if (result === undefined)
|
|
106
|
+
return 'null';
|
|
107
|
+
return JSON.stringify(result, null, 2) ?? String(result);
|
|
108
|
+
}
|
|
109
|
+
function normalizeSchemaProperties(properties) {
|
|
110
|
+
if (!isRecord(properties))
|
|
111
|
+
return {};
|
|
112
|
+
return Object.fromEntries(Object.entries(properties).filter((entry) => isRecord(entry[1])));
|
|
113
|
+
}
|
|
114
|
+
function listen(server) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
const onError = (err) => {
|
|
117
|
+
server.off('listening', onListening);
|
|
118
|
+
reject(err);
|
|
119
|
+
};
|
|
120
|
+
const onListening = () => {
|
|
121
|
+
server.off('error', onError);
|
|
122
|
+
resolve();
|
|
123
|
+
};
|
|
124
|
+
server.once('error', onError);
|
|
125
|
+
server.once('listening', onListening);
|
|
126
|
+
server.listen(0, '127.0.0.1');
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function closeHttpServer(server) {
|
|
130
|
+
if (!server.listening)
|
|
131
|
+
return Promise.resolve();
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
server.close((err) => {
|
|
134
|
+
if (err)
|
|
135
|
+
reject(err);
|
|
136
|
+
else
|
|
137
|
+
resolve();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function isRecord(value) {
|
|
142
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=codex-function-tool-bridge.js.map
|
|
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, wri
|
|
|
2
2
|
import { basename, extname, join } from 'node:path';
|
|
3
3
|
import { Codex } from '@openai/codex-sdk';
|
|
4
4
|
import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
|
|
5
|
+
import { startCodexFunctionToolBridge } from './codex-function-tool-bridge.js';
|
|
5
6
|
/**
|
|
6
7
|
* Runtime backed by OpenAI's Codex SDK.
|
|
7
8
|
* Spawns the bundled Codex CLI and consumes its structured JSONL event stream.
|
|
@@ -23,12 +24,15 @@ export class CodexRuntime {
|
|
|
23
24
|
if (codexHome)
|
|
24
25
|
prepareCodexHome(codexHome, agentDir);
|
|
25
26
|
const skillNames = normalizeSkillNames(options?.skills, codexHome);
|
|
26
|
-
const configOverrides = buildCodexConfig(this.config, options, skillNames);
|
|
27
27
|
const sandboxMode = normalizeSandboxMode(this.config.permissionMode);
|
|
28
28
|
const approvalPolicy = normalizeApprovalPolicy(this.config.permissionMode);
|
|
29
|
-
|
|
29
|
+
let functionToolBridge;
|
|
30
|
+
d(`starting | cwd=${cwd} model=${this.config.model ?? 'default'} sandboxMode=${sandboxMode} approvalPolicy=${approvalPolicy} maxTurns=${options?.maxTurns ?? '∞'} mcpServers=${Object.keys(options?.mcpServers ?? {}).join(',') || '-'} functionTools=${options?.functionTools?.map((tool) => tool.name).join(',') || '-'} skills=${skillNames.join(',') || '-'} codexHome=${codexHome ?? 'default'} sandboxDirs=${options?.sandboxDirs?.join(',') ?? '-'}`);
|
|
30
31
|
d(`prompt: ${prompt.slice(0, 300)}${prompt.length > 300 ? '...' : ''}`);
|
|
31
32
|
try {
|
|
33
|
+
functionToolBridge = await startCodexFunctionToolBridge(options?.functionTools, d);
|
|
34
|
+
const mcpServers = withFunctionToolBridge(options?.mcpServers, functionToolBridge);
|
|
35
|
+
const configOverrides = buildCodexConfig(this.config, mcpServers, skillNames);
|
|
32
36
|
const codex = new Codex({
|
|
33
37
|
apiKey: this.config.apiKey,
|
|
34
38
|
baseUrl: shouldUseCodexOpenAIBaseUrl(this.config) ? this.config.baseURL : undefined,
|
|
@@ -127,6 +131,13 @@ export class CodexRuntime {
|
|
|
127
131
|
return { success: false, summary: 'Codex execution failed', artifacts: [], error: message, output, sessionId: options?.resumeSessionId };
|
|
128
132
|
}
|
|
129
133
|
finally {
|
|
134
|
+
try {
|
|
135
|
+
await functionToolBridge?.close();
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
139
|
+
d(`function tool bridge close failed | ${message}`);
|
|
140
|
+
}
|
|
130
141
|
this.abortController = null;
|
|
131
142
|
}
|
|
132
143
|
}
|
|
@@ -143,13 +154,13 @@ function buildEnv(config, codexHome) {
|
|
|
143
154
|
CODEX_INTERNAL_ORIGINATOR_OVERRIDE: process.env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE || 'agent-spaces/server',
|
|
144
155
|
});
|
|
145
156
|
}
|
|
146
|
-
function buildCodexConfig(runtimeConfig,
|
|
157
|
+
function buildCodexConfig(runtimeConfig, rawMcpServers, skillNames) {
|
|
147
158
|
const config = {};
|
|
148
159
|
Object.assign(config, buildCodexProviderConfig(runtimeConfig));
|
|
149
160
|
if (runtimeConfig.thinkingEnabled !== false) {
|
|
150
161
|
config.model_reasoning_effort = runtimeConfig.thinkingEffort ?? 'medium';
|
|
151
162
|
}
|
|
152
|
-
const mcpServers = normalizeMcpServers(
|
|
163
|
+
const mcpServers = normalizeMcpServers(rawMcpServers);
|
|
153
164
|
if (mcpServers)
|
|
154
165
|
config.mcp_servers = mcpServers;
|
|
155
166
|
if (skillNames.length > 0) {
|
|
@@ -157,6 +168,14 @@ function buildCodexConfig(runtimeConfig, options, skillNames) {
|
|
|
157
168
|
}
|
|
158
169
|
return removeUndefined(config);
|
|
159
170
|
}
|
|
171
|
+
function withFunctionToolBridge(mcpServers, bridge) {
|
|
172
|
+
if (!bridge)
|
|
173
|
+
return mcpServers;
|
|
174
|
+
return {
|
|
175
|
+
...(mcpServers ?? {}),
|
|
176
|
+
[bridge.name]: { url: bridge.url },
|
|
177
|
+
};
|
|
178
|
+
}
|
|
160
179
|
function buildCodexProviderConfig(runtimeConfig) {
|
|
161
180
|
if (!runtimeConfig.baseURL || shouldUseCodexOpenAIBaseUrl(runtimeConfig))
|
|
162
181
|
return {};
|
package/dist/adapters/git.js
CHANGED
|
@@ -170,6 +170,12 @@ export async function gitStatus(workspaceId) {
|
|
|
170
170
|
try {
|
|
171
171
|
const upstream = await getUpstreamRef(git, result.branch);
|
|
172
172
|
if (upstream) {
|
|
173
|
+
const counts = await git.raw(['rev-list', '--left-right', '--count', `HEAD...${upstream}`]);
|
|
174
|
+
const [ahead, behind] = counts.trim().split(/\s+/).map((value) => Number(value));
|
|
175
|
+
if (Number.isFinite(ahead))
|
|
176
|
+
result.ahead = ahead;
|
|
177
|
+
if (Number.isFinite(behind))
|
|
178
|
+
result.behind = behind;
|
|
173
179
|
const remoteHeadHash = await git.revparse(['--short', upstream]);
|
|
174
180
|
result.remoteHeadHash = remoteHeadHash.trim();
|
|
175
181
|
}
|
|
@@ -376,7 +382,7 @@ export async function gitPush(workspaceId) {
|
|
|
376
382
|
const remotes = await git.getRemotes(true);
|
|
377
383
|
if (!remotes.length)
|
|
378
384
|
throw new Error('No remote repository configured. Please add a remote first.');
|
|
379
|
-
await git.push('origin', branch);
|
|
385
|
+
await git.push('origin', branch, ['--set-upstream']);
|
|
380
386
|
}
|
|
381
387
|
export async function gitFetch(workspaceId) {
|
|
382
388
|
const ws = getWorkspace(workspaceId);
|
|
@@ -392,6 +398,11 @@ export async function gitPull(workspaceId) {
|
|
|
392
398
|
const git = getGit(ws);
|
|
393
399
|
const status = await git.status();
|
|
394
400
|
const branch = status.current || 'HEAD';
|
|
401
|
+
const upstream = await getUpstreamRef(git, branch);
|
|
402
|
+
if (upstream?.startsWith('origin/')) {
|
|
403
|
+
await git.pull('origin', upstream.slice('origin/'.length));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
395
406
|
await git.pull('origin', branch);
|
|
396
407
|
}
|
|
397
408
|
export async function gitGetRemotes(workspaceId) {
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, rmSync, statSync } from 'node:fs';
|
|
3
|
+
import { basename, extname, join } from 'node:path';
|
|
4
|
+
import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Runtime backed by the external Hermes CLI.
|
|
7
|
+
*
|
|
8
|
+
* Hermes does not currently expose a structured JS SDK here, so this adapter
|
|
9
|
+
* treats verbose CLI output as the source of truth and streams it as text.
|
|
10
|
+
*/
|
|
11
|
+
export class HermesRuntime {
|
|
12
|
+
config;
|
|
13
|
+
child = null;
|
|
14
|
+
constructor(config = {}) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
}
|
|
17
|
+
async execute(prompt, workingDir, options) {
|
|
18
|
+
const output = [];
|
|
19
|
+
const cwd = workingDir || process.cwd();
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
const d = (message) => console.log(`[hermes] ${message}`);
|
|
22
|
+
const agentDir = options?.configDir;
|
|
23
|
+
const hermesHome = agentDir ? join(agentDir, '.hermes') : undefined;
|
|
24
|
+
if (hermesHome)
|
|
25
|
+
prepareHermesHome(hermesHome, agentDir);
|
|
26
|
+
const args = buildHermesArgs(appendOutputStyleToPrompt(prompt, options?.outputStyle), this.config, options);
|
|
27
|
+
d(`starting | cwd=${cwd} model=${this.config.model ?? 'profile-default'} provider=${this.config.provider ?? 'profile-default'} hermesHome=${hermesHome ?? 'default'} skills=${options?.skills?.join(',') || '-'}`);
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
let settled = false;
|
|
30
|
+
let stdoutBuffer = '';
|
|
31
|
+
let stderrBuffer = '';
|
|
32
|
+
let sessionId = options?.resumeSessionId;
|
|
33
|
+
let emittedSessionId = sessionId;
|
|
34
|
+
const finish = (result) => {
|
|
35
|
+
if (settled)
|
|
36
|
+
return;
|
|
37
|
+
settled = true;
|
|
38
|
+
this.child = null;
|
|
39
|
+
resolve(result);
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
this.child = spawn('hermes', args, {
|
|
43
|
+
cwd,
|
|
44
|
+
env: buildEnv(this.config, hermesHome),
|
|
45
|
+
windowsHide: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
50
|
+
d(`failed to start | ${message}`);
|
|
51
|
+
finish({
|
|
52
|
+
success: false,
|
|
53
|
+
summary: 'Hermes execution failed',
|
|
54
|
+
artifacts: [],
|
|
55
|
+
error: message,
|
|
56
|
+
output,
|
|
57
|
+
sessionId,
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.child.stdout.setEncoding('utf-8');
|
|
62
|
+
this.child.stderr.setEncoding('utf-8');
|
|
63
|
+
this.child.stdout.on('data', (chunk) => {
|
|
64
|
+
stdoutBuffer = consumeLines(stdoutBuffer + chunk, (line) => {
|
|
65
|
+
if (!line)
|
|
66
|
+
return;
|
|
67
|
+
output.push(line);
|
|
68
|
+
const parsedSessionId = extractSessionId(line);
|
|
69
|
+
if (parsedSessionId && parsedSessionId !== emittedSessionId) {
|
|
70
|
+
sessionId = parsedSessionId;
|
|
71
|
+
emittedSessionId = parsedSessionId;
|
|
72
|
+
options?.onEvent?.({ type: 'session', sessionId });
|
|
73
|
+
}
|
|
74
|
+
options?.onEvent?.({ type: 'output', line });
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
this.child.stderr.on('data', (chunk) => {
|
|
78
|
+
stderrBuffer = consumeLines(stderrBuffer + chunk, (line) => {
|
|
79
|
+
if (!line)
|
|
80
|
+
return;
|
|
81
|
+
const formatted = `[stderr] ${line}`;
|
|
82
|
+
output.push(formatted);
|
|
83
|
+
options?.onEvent?.({ type: 'output', line: formatted });
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
this.child.on('error', (err) => {
|
|
87
|
+
const message = err.message.includes('ENOENT')
|
|
88
|
+
? 'Hermes CLI was not found. Install Hermes and ensure the `hermes` command is available on PATH.'
|
|
89
|
+
: err.message;
|
|
90
|
+
d(`failed ${Date.now() - startTime}ms | ${message}`);
|
|
91
|
+
finish({
|
|
92
|
+
success: false,
|
|
93
|
+
summary: 'Hermes execution failed',
|
|
94
|
+
artifacts: [],
|
|
95
|
+
error: message,
|
|
96
|
+
output,
|
|
97
|
+
sessionId,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
this.child.on('close', (code, signal) => {
|
|
101
|
+
stdoutBuffer = flushLine(stdoutBuffer, output, options);
|
|
102
|
+
stderrBuffer = flushLine(stderrBuffer, output, options, '[stderr] ');
|
|
103
|
+
const elapsed = Date.now() - startTime;
|
|
104
|
+
if (code === 0) {
|
|
105
|
+
const text = lastMeaningfulLine(output);
|
|
106
|
+
d(`done ${elapsed}ms`);
|
|
107
|
+
finish({
|
|
108
|
+
success: true,
|
|
109
|
+
summary: summarizeResult(text),
|
|
110
|
+
artifacts: [],
|
|
111
|
+
output,
|
|
112
|
+
sessionId,
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const error = signal
|
|
117
|
+
? `Hermes execution stopped by signal ${signal}`
|
|
118
|
+
: `Hermes execution failed with exit code ${code ?? 'unknown'}`;
|
|
119
|
+
d(`failed ${elapsed}ms | ${error}`);
|
|
120
|
+
finish({
|
|
121
|
+
success: false,
|
|
122
|
+
summary: 'Hermes execution failed',
|
|
123
|
+
artifacts: [],
|
|
124
|
+
error,
|
|
125
|
+
output,
|
|
126
|
+
sessionId,
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
stop() {
|
|
132
|
+
this.child?.kill();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function buildHermesArgs(prompt, config, options) {
|
|
136
|
+
const args = ['chat', '-q', prompt, '--verbose'];
|
|
137
|
+
for (const skill of normalizeSkillNames(options?.skills))
|
|
138
|
+
args.push('-s', skill);
|
|
139
|
+
if (config.model)
|
|
140
|
+
args.push('--model', config.model);
|
|
141
|
+
if (config.provider)
|
|
142
|
+
args.push('--provider', String(config.provider));
|
|
143
|
+
return args;
|
|
144
|
+
}
|
|
145
|
+
function buildEnv(config, hermesHome) {
|
|
146
|
+
const apiKey = config.apiKey || process.env.HERMES_API_KEY || process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
|
|
147
|
+
return {
|
|
148
|
+
...process.env,
|
|
149
|
+
HERMES_HOME: hermesHome || process.env.HERMES_HOME,
|
|
150
|
+
HERMES_API_KEY: apiKey,
|
|
151
|
+
HERMES_BASE_URL: config.baseURL || process.env.HERMES_BASE_URL,
|
|
152
|
+
OPENAI_API_KEY: config.apiKey || process.env.OPENAI_API_KEY,
|
|
153
|
+
OPENAI_BASE_URL: config.baseURL || process.env.OPENAI_BASE_URL,
|
|
154
|
+
ANTHROPIC_API_KEY: config.apiKey || process.env.ANTHROPIC_API_KEY,
|
|
155
|
+
NO_COLOR: process.env.NO_COLOR || '1',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function prepareHermesHome(hermesHome, agentDir) {
|
|
159
|
+
mkdirSync(hermesHome, { recursive: true });
|
|
160
|
+
const skillsDir = join(hermesHome, 'skills');
|
|
161
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
162
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
163
|
+
if (!agentDir)
|
|
164
|
+
return;
|
|
165
|
+
const sourceSkillsDir = join(agentDir, 'skills');
|
|
166
|
+
if (!existsSync(sourceSkillsDir))
|
|
167
|
+
return;
|
|
168
|
+
for (const file of readdirSync(sourceSkillsDir)) {
|
|
169
|
+
if (extname(file).toLowerCase() !== '.md')
|
|
170
|
+
continue;
|
|
171
|
+
const sourceFile = join(sourceSkillsDir, file);
|
|
172
|
+
if (!statSync(sourceFile).isFile())
|
|
173
|
+
continue;
|
|
174
|
+
copyFileSync(sourceFile, join(skillsDir, basename(file)));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function consumeLines(buffer, onLine) {
|
|
178
|
+
const lines = buffer.split(/\r?\n/);
|
|
179
|
+
const remainder = lines.pop() ?? '';
|
|
180
|
+
for (const line of lines)
|
|
181
|
+
onLine(stripAnsi(line).trimEnd());
|
|
182
|
+
return remainder;
|
|
183
|
+
}
|
|
184
|
+
function flushLine(buffer, output, options, prefix = '') {
|
|
185
|
+
const line = stripAnsi(buffer).trimEnd();
|
|
186
|
+
if (line) {
|
|
187
|
+
const formatted = `${prefix}${line}`;
|
|
188
|
+
output.push(formatted);
|
|
189
|
+
options?.onEvent?.({ type: 'output', line: formatted });
|
|
190
|
+
}
|
|
191
|
+
return '';
|
|
192
|
+
}
|
|
193
|
+
function extractSessionId(line) {
|
|
194
|
+
const match = line.match(/\bsession(?:\s+id)?\s*[:=]\s*([a-zA-Z0-9._:-]+)/i);
|
|
195
|
+
return match?.[1];
|
|
196
|
+
}
|
|
197
|
+
function normalizeSkillNames(skills) {
|
|
198
|
+
if (!Array.isArray(skills))
|
|
199
|
+
return [];
|
|
200
|
+
return skills
|
|
201
|
+
.map((skill) => basename(skill).replace(/\.md$/i, '').trim())
|
|
202
|
+
.filter(Boolean);
|
|
203
|
+
}
|
|
204
|
+
function lastMeaningfulLine(output) {
|
|
205
|
+
return [...output].reverse().find((line) => line.trim() && !line.startsWith('[stderr]')) ?? '';
|
|
206
|
+
}
|
|
207
|
+
function stripAnsi(value) {
|
|
208
|
+
return value.replace(/\u001b\[[0-9;]*m/g, '');
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=hermes-runtime.js.map
|
|
@@ -4,12 +4,13 @@ import * as channelService from '../services/channel.js';
|
|
|
4
4
|
import * as issueService from '../services/issue.js';
|
|
5
5
|
import * as taskService from '../services/task.js';
|
|
6
6
|
import * as workspaceService from '../services/workspace.js';
|
|
7
|
-
import { mapWorkflowToTaskDrafts, validateWorkflowForRun } from '../services/workflow.js';
|
|
7
|
+
import { getWorkflow, mapWorkflowToTaskDrafts, validateWorkflowForRun } from '../services/workflow.js';
|
|
8
8
|
import { createIssueFunctionTools } from '../services/builtin-tools/index.js';
|
|
9
9
|
import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
|
|
10
10
|
import { prependPersistentAgentContext } from '../services/persistent-agent-context.js';
|
|
11
11
|
import { completeIssueAgentProgress, createIssueAgentProgress, createIssueAgentProgressTracker } from './issue-agent-progress.js';
|
|
12
12
|
import { wrapOnEventWithHooks } from '../services/hook-engine.js';
|
|
13
|
+
import { executeCommandNode } from '../services/workflow-command-runner.js';
|
|
13
14
|
const ACTIVE_TASK_STATUSES = ['running', 'reviewing', 'retrying', 'waiting_review'];
|
|
14
15
|
const activeIssueRuntimes = new Map();
|
|
15
16
|
export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, ctx) {
|
|
@@ -137,6 +138,13 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
|
|
|
137
138
|
if (task.status !== 'pending') {
|
|
138
139
|
return;
|
|
139
140
|
}
|
|
141
|
+
// Check if this task came from a command workflow node
|
|
142
|
+
const workflow = findWorkflowForIssue(workspaceId, issue);
|
|
143
|
+
const commandNode = findCommandNodeForTask(workflow, task);
|
|
144
|
+
if (commandNode) {
|
|
145
|
+
await runCommandTask(workspaceId, issueId, taskId, commandNode, ctx);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
140
148
|
const taskAgentPreset = findAgentForTask(workspaceId, issue, task);
|
|
141
149
|
if (!taskAgentPreset) {
|
|
142
150
|
if (issueService.getById(workspaceId, issueId)?.status === 'error')
|
|
@@ -274,6 +282,48 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
|
|
|
274
282
|
await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
|
|
275
283
|
}
|
|
276
284
|
}
|
|
285
|
+
async function runCommandTask(workspaceId, issueId, taskId, commandNode, ctx) {
|
|
286
|
+
const task = taskService.getById(workspaceId, taskId);
|
|
287
|
+
if (!task || task.status !== 'pending')
|
|
288
|
+
return;
|
|
289
|
+
const runningTask = taskService.updateStatus(workspaceId, taskId, 'running');
|
|
290
|
+
if (!runningTask)
|
|
291
|
+
return;
|
|
292
|
+
broadcastTaskUpdate(ctx, runningTask, 'pending');
|
|
293
|
+
ctx.broadcast('task.output', { taskId, data: `$ ${commandNode.data.script.split('\n')[0]}${commandNode.data.script.includes('\n') ? ' ...' : ''}` });
|
|
294
|
+
const result = await executeCommandNode(workspaceId, commandNode);
|
|
295
|
+
ctx.broadcast('task.output', { taskId, data: result.stdout });
|
|
296
|
+
if (result.stderr) {
|
|
297
|
+
ctx.broadcast('task.output', { taskId, data: result.stderr });
|
|
298
|
+
}
|
|
299
|
+
if (!result.success) {
|
|
300
|
+
const failedTask = taskService.updateStatus(workspaceId, taskId, 'failed', {
|
|
301
|
+
result: {
|
|
302
|
+
success: false,
|
|
303
|
+
summary: `Command failed with exit code ${result.exitCode}`,
|
|
304
|
+
artifacts: [],
|
|
305
|
+
error: result.stderr || `Exit code: ${result.exitCode}`,
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
broadcastTaskUpdate(ctx, failedTask, 'running');
|
|
309
|
+
await handleTaskFailure(workspaceId, issueId, taskId, {
|
|
310
|
+
success: false,
|
|
311
|
+
summary: `Command failed with exit code ${result.exitCode}`,
|
|
312
|
+
artifacts: [],
|
|
313
|
+
error: result.stderr || `Exit code: ${result.exitCode}`,
|
|
314
|
+
}, ctx);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const completedTask = taskService.complete(workspaceId, taskId, {
|
|
318
|
+
success: true,
|
|
319
|
+
summary: result.stdout.slice(-500) || 'Command completed',
|
|
320
|
+
artifacts: [],
|
|
321
|
+
});
|
|
322
|
+
broadcastTaskUpdate(ctx, completedTask, 'running');
|
|
323
|
+
if (issueService.getById(workspaceId, issueId)?.continuousRun !== false) {
|
|
324
|
+
await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
277
327
|
export async function continueIssueTaskScheduling(workspaceId, issueId, ctx) {
|
|
278
328
|
await scheduleRunnableIssueTasks(workspaceId, issueId, ctx, { force: true });
|
|
279
329
|
}
|
|
@@ -402,7 +452,7 @@ export function createTasksFromWorkflow(workspaceId, issueId, template, ctx) {
|
|
|
402
452
|
});
|
|
403
453
|
}
|
|
404
454
|
function ensureWorkflowAgentsForRun(workspaceId, issue, template, ctx) {
|
|
405
|
-
const workflowAgentIds = [...new Set(template.nodes.map((node) => node.data.agentConfigId).filter(Boolean))];
|
|
455
|
+
const workflowAgentIds = [...new Set(template.nodes.filter((n) => n.type === 'agent').map((node) => node.data.agentConfigId).filter(Boolean))];
|
|
406
456
|
const mergedMembers = [...new Set([...(issue.members ?? []), ...workflowAgentIds])];
|
|
407
457
|
const membersChanged = mergedMembers.length !== (issue.members ?? []).length
|
|
408
458
|
|| mergedMembers.some((member, index) => member !== issue.members?.[index]);
|
|
@@ -502,6 +552,23 @@ function unregisterActiveIssueRuntime(workspaceId, issueId, agentId) {
|
|
|
502
552
|
function issueRunKey(workspaceId, issueId) {
|
|
503
553
|
return `${workspaceId}:${issueId}`;
|
|
504
554
|
}
|
|
555
|
+
function findWorkflowForIssue(workspaceId, issue) {
|
|
556
|
+
if (!issue.workflowId)
|
|
557
|
+
return null;
|
|
558
|
+
return getWorkflow(issue.workflowId);
|
|
559
|
+
}
|
|
560
|
+
function findCommandNodeForTask(workflow, task) {
|
|
561
|
+
if (!workflow)
|
|
562
|
+
return null;
|
|
563
|
+
if (task.agentConfigId)
|
|
564
|
+
return null;
|
|
565
|
+
for (const node of workflow.nodes) {
|
|
566
|
+
if (node.type === 'command' && (task.title === node.data.label || task.description?.startsWith('Command:'))) {
|
|
567
|
+
return node;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
505
572
|
function broadcastTaskUpdate(ctx, task, from) {
|
|
506
573
|
if (!task)
|
|
507
574
|
return;
|