@agent-spaces/server 0.3.65 → 0.4.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/adapters/agent-runtime.js +8 -0
- package/dist/adapters/codex-function-tool-bridge.js +152 -0
- package/dist/adapters/codex-runtime.js +23 -4
- package/dist/adapters/git.js +25 -3
- package/dist/adapters/hermes-runtime.js +813 -0
- package/dist/adapters/langchain-runtime.js +384 -21
- package/dist/adapters/oh-my-pi-runtime.js +858 -0
- package/dist/adapters/open-agent-sdk-runtime.js +202 -5
- package/dist/adapters/open-agent-sdk-runtime.test.js +35 -0
- package/dist/agents/agent-message-parts.js +52 -2
- package/dist/agents/issue-task-controller.js +71 -2
- package/dist/agents/pull-request-agent.js +115 -0
- package/dist/agents/scheduler-agent.js +1 -1
- package/dist/agents/title-generator-agent.js +120 -0
- package/dist/app.js +61 -4
- package/dist/middleware/auth.js +1 -1
- package/dist/package.json +2 -1
- package/dist/routes/agent-sse.js +70 -6
- package/dist/routes/channel.js +23 -2
- package/dist/routes/chat-run.js +191 -0
- package/dist/routes/chat.js +142 -0
- package/dist/routes/command.js +16 -0
- package/dist/routes/data.js +189 -0
- package/dist/routes/git.js +10 -1
- package/dist/routes/import.js +199 -0
- package/dist/routes/issue.js +17 -5
- package/dist/routes/notification.js +10 -0
- package/dist/routes/plugin.js +69 -0
- package/dist/routes/version.js +95 -0
- package/dist/routes/workflow-hook.js +71 -0
- package/dist/routes/workflow.js +282 -7
- package/dist/routes/workspace.js +13 -4
- package/dist/routes/worktree.js +93 -0
- package/dist/services/agent.js +123 -36
- package/dist/services/ai-text.js +185 -0
- package/dist/services/builtin-tools/index.js +1 -0
- package/dist/services/builtin-tools/workflow-editor-tools.js +509 -0
- package/dist/services/builtin-tools/workflow-exec-tools.js +320 -0
- package/dist/services/chat.js +134 -0
- package/dist/services/command-process-manager.js +16 -0
- package/dist/services/execution-manager.js +1346 -0
- package/dist/services/generated-title.js +59 -0
- package/dist/services/gitignore.js +22 -18
- package/dist/services/interaction-manager.js +114 -0
- package/dist/services/issue-retry.js +25 -0
- package/dist/services/notification-center.js +9 -0
- package/dist/services/output-style.js +8 -1
- package/dist/services/plugin.js +257 -0
- package/dist/services/prompt-template.js +8 -1
- package/dist/services/pty.js +20 -0
- package/dist/services/search.js +16 -6
- package/dist/services/skill.js +2 -0
- package/dist/services/version.js +69 -0
- package/dist/services/workflow-command-runner.js +24 -0
- package/dist/services/workflow-trigger-service.js +137 -0
- package/dist/services/workflow.js +219 -39
- package/dist/services/workspace.js +23 -1
- package/dist/services/worktree.js +207 -0
- package/dist/storage/agent-store.js +8 -0
- package/dist/storage/chat-store.js +151 -0
- package/dist/storage/database-store.js +6 -0
- package/dist/storage/json-store.js +2 -1
- package/dist/storage/kanban-store.js +6 -0
- package/dist/storage/workflow-store.js +386 -22
- 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 +36 -3
- package/dist/ws/agent-runner.js +30 -18
- package/dist/ws/chat-handler.js +89 -0
- package/dist/ws/connection-manager.js +49 -2
- package/dist/ws/execution-channels.js +88 -0
- package/dist/ws/handler.js +32 -5
- package/dist/ws/message-parts.js +130 -12
- package/dist/ws/terminal-handler.js +26 -0
- package/package.json +13 -1
- package/public/avatars/user.jpg +0 -0
- 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
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
import { createAgent, registerSkill, unregisterSkill } from '@codeany/open-agent-sdk';
|
|
2
4
|
import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
|
|
5
|
+
import { startCodexFunctionToolBridge } from './codex-function-tool-bridge.js';
|
|
6
|
+
const registeredConfiguredSkills = new Set();
|
|
7
|
+
const DEFAULT_DATA_DIR = join(process.env.HOME || '~', '.agent-spaces-data');
|
|
3
8
|
/**
|
|
4
9
|
* Runtime backed by @codeany/open-agent-sdk.
|
|
5
10
|
* Runs the agent loop in-process with the SDK's built-in tools.
|
|
@@ -17,9 +22,13 @@ export class OpenAgentSdkRuntime {
|
|
|
17
22
|
const cwd = workingDir || process.cwd();
|
|
18
23
|
const startTime = Date.now();
|
|
19
24
|
const d = (msg) => console.log(`[agent] ${msg}`);
|
|
20
|
-
|
|
25
|
+
let functionToolBridge;
|
|
26
|
+
d(`starting | cwd=${cwd} provider=${this.config.provider ?? 'default'} model=${this.config.model ?? 'default'} baseURL=${this.config.baseURL ?? 'default'} permissionMode=${this.config.permissionMode ?? 'bypassPermissions'} maxTurns=${options?.maxTurns ?? '∞'} allowedTools=${options?.tools?.join(',') ?? 'all'} mcpServers=${Object.keys(options?.mcpServers ?? {}).join(',') || '-'} functionTools=${options?.functionTools?.map((tool) => tool.name).join(',') || '-'} sandboxDirs=${options?.sandboxDirs?.join(',') ?? '-'}`);
|
|
21
27
|
d(`prompt: ${prompt.slice(0, 300)}${prompt.length > 300 ? '...' : ''}`);
|
|
22
28
|
try {
|
|
29
|
+
functionToolBridge = await startCodexFunctionToolBridge(options?.functionTools, d);
|
|
30
|
+
const mcpServers = withFunctionToolBridge(normalizeOpenAgentMcpServers(options?.mcpServers), functionToolBridge);
|
|
31
|
+
d(`resolved tools | allowedTools=${options?.tools?.join(',') ?? 'all'} mcpServers=${Object.keys(mcpServers ?? {}).join(',') || '-'} functionTools=${options?.functionTools?.map((tool) => tool.name).join(',') || '-'}`);
|
|
23
32
|
this.agent = createAgent({
|
|
24
33
|
apiType: normalizeApiType(this.config.provider),
|
|
25
34
|
model: this.config.model,
|
|
@@ -29,13 +38,17 @@ export class OpenAgentSdkRuntime {
|
|
|
29
38
|
systemPrompt: options?.systemPrompt,
|
|
30
39
|
maxTurns: options?.maxTurns,
|
|
31
40
|
allowedTools: options?.tools,
|
|
41
|
+
mcpServers,
|
|
32
42
|
additionalDirectories: options?.sandboxDirs,
|
|
33
43
|
permissionMode: this.config.permissionMode ?? 'bypassPermissions',
|
|
34
44
|
abortController: this.abortController,
|
|
35
45
|
});
|
|
46
|
+
const registeredSkills = registerConfiguredSkills(options?.configDir, options?.skills);
|
|
47
|
+
if (options?.skills?.length) {
|
|
48
|
+
d(`skills registered | requested=${options.skills.join(',') || '-'} registered=${registeredSkills.join(',') || '-'}`);
|
|
49
|
+
}
|
|
36
50
|
d('agent created, sending prompt...');
|
|
37
|
-
|
|
38
|
-
const result = await this.agent.prompt(appendOutputStyleToPrompt(prompt, options?.outputStyle));
|
|
51
|
+
const result = await collectQueryResult(this.agent.query(appendOutputStyleToPrompt(prompt, options?.outputStyle)), output, options, d);
|
|
39
52
|
const elapsed = Date.now() - startTime;
|
|
40
53
|
const inputTokens = result.usage.input_tokens;
|
|
41
54
|
const outputTokens = result.usage.output_tokens;
|
|
@@ -68,7 +81,18 @@ export class OpenAgentSdkRuntime {
|
|
|
68
81
|
return { success: false, summary: 'Agent execution failed', artifacts: [], error: message, output };
|
|
69
82
|
}
|
|
70
83
|
finally {
|
|
71
|
-
|
|
84
|
+
try {
|
|
85
|
+
await this.agent?.close();
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
try {
|
|
89
|
+
await functionToolBridge?.close();
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
93
|
+
d(`function tool bridge close failed | ${message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
72
96
|
this.agent = null;
|
|
73
97
|
this.abortController = null;
|
|
74
98
|
}
|
|
@@ -78,10 +102,183 @@ export class OpenAgentSdkRuntime {
|
|
|
78
102
|
this.agent?.interrupt();
|
|
79
103
|
}
|
|
80
104
|
}
|
|
105
|
+
function withFunctionToolBridge(mcpServers, bridge) {
|
|
106
|
+
if (!bridge)
|
|
107
|
+
return mcpServers;
|
|
108
|
+
return {
|
|
109
|
+
...(mcpServers ?? {}),
|
|
110
|
+
[bridge.name]: { type: 'http', url: bridge.url },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export function normalizeOpenAgentMcpServers(mcpServers) {
|
|
114
|
+
if (!mcpServers)
|
|
115
|
+
return undefined;
|
|
116
|
+
return Object.fromEntries(Object.entries(mcpServers).map(([name, server]) => [name, normalizeOpenAgentMcpServer(server)]));
|
|
117
|
+
}
|
|
118
|
+
function normalizeOpenAgentMcpServer(server) {
|
|
119
|
+
if (!isRecord(server))
|
|
120
|
+
return server;
|
|
121
|
+
const args = Array.isArray(server.args) ? server.args.map(String) : undefined;
|
|
122
|
+
const badFetchPackageIndex = args?.indexOf('@modelcontextprotocol/server-fetch') ?? -1;
|
|
123
|
+
if (String(server.command) !== 'npx' || !args || badFetchPackageIndex < 0)
|
|
124
|
+
return server;
|
|
125
|
+
return {
|
|
126
|
+
...server,
|
|
127
|
+
command: 'uvx',
|
|
128
|
+
args: ['mcp-server-fetch', ...args.slice(badFetchPackageIndex + 1)],
|
|
129
|
+
env: {
|
|
130
|
+
PYTHONIOENCODING: 'utf-8',
|
|
131
|
+
...(isRecord(server.env) ? server.env : {}),
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function isRecord(value) {
|
|
136
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
137
|
+
}
|
|
138
|
+
async function collectQueryResult(events, output, options, log) {
|
|
139
|
+
const collected = {
|
|
140
|
+
text: '',
|
|
141
|
+
usage: { input_tokens: 0, output_tokens: 0 },
|
|
142
|
+
num_turns: 0,
|
|
143
|
+
};
|
|
144
|
+
for await (const event of events) {
|
|
145
|
+
if (event.type === 'system' && event.subtype === 'init') {
|
|
146
|
+
log(`sdk init | session=${event.session_id} model=${event.model} cwd=${event.cwd} permissionMode=${event.permission_mode} tools=${event.tools.join(',') || '-'} mcpServers=${event.mcp_servers.map((server) => `${server.name}:${server.status}`).join(',') || '-'}`);
|
|
147
|
+
options?.onEvent?.({ type: 'session', sessionId: event.session_id });
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (event.type === 'assistant') {
|
|
151
|
+
const text = event.message.content
|
|
152
|
+
.filter((block) => block.type === 'text')
|
|
153
|
+
.map((block) => block.text)
|
|
154
|
+
.join('');
|
|
155
|
+
if (text)
|
|
156
|
+
collected.text = text;
|
|
157
|
+
for (const block of event.message.content) {
|
|
158
|
+
if (block.type !== 'tool_use')
|
|
159
|
+
continue;
|
|
160
|
+
const line = `Tool: ${block.name} input=${JSON.stringify(block.input)}`;
|
|
161
|
+
log(`tool use | id=${block.id} name=${block.name} input=${JSON.stringify(block.input)}`);
|
|
162
|
+
output.push(line);
|
|
163
|
+
options?.onEvent?.({ type: 'tool_use', id: block.id, name: block.name, input: block.input, line });
|
|
164
|
+
}
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (event.type === 'tool_result') {
|
|
168
|
+
const result = event.result;
|
|
169
|
+
log(`tool result | id=${result.tool_use_id} name=${result.tool_name} output=${truncateForLog(result.output)}`);
|
|
170
|
+
options?.onEvent?.({ type: 'tool_result', toolUseId: result.tool_use_id, result: result.output });
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (event.type === 'result') {
|
|
174
|
+
collected.num_turns = event.num_turns ?? 0;
|
|
175
|
+
collected.usage = {
|
|
176
|
+
input_tokens: event.usage?.input_tokens ?? 0,
|
|
177
|
+
output_tokens: event.usage?.output_tokens ?? 0,
|
|
178
|
+
cache_read_input_tokens: event.usage?.cache_read_input_tokens,
|
|
179
|
+
cache_creation_input_tokens: event.usage?.cache_creation_input_tokens,
|
|
180
|
+
};
|
|
181
|
+
if (event.is_error) {
|
|
182
|
+
log(`sdk result error | subtype=${event.subtype} errors=${event.errors?.join('; ') || '-'}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return collected;
|
|
187
|
+
}
|
|
188
|
+
function truncateForLog(value, max = 1000) {
|
|
189
|
+
return value.length > max ? `${value.slice(0, max)}...` : value;
|
|
190
|
+
}
|
|
81
191
|
function normalizeApiType(provider) {
|
|
82
192
|
if (provider === 'anthropic-messages' || provider === 'openai-completions') {
|
|
83
193
|
return provider;
|
|
84
194
|
}
|
|
85
195
|
return undefined;
|
|
86
196
|
}
|
|
197
|
+
export function registerConfiguredSkills(agentDir, skills) {
|
|
198
|
+
for (const skill of registeredConfiguredSkills)
|
|
199
|
+
unregisterSkill(skill);
|
|
200
|
+
registeredConfiguredSkills.clear();
|
|
201
|
+
if (!agentDir || !Array.isArray(skills))
|
|
202
|
+
return [];
|
|
203
|
+
const registered = [];
|
|
204
|
+
for (const rawSkill of skills) {
|
|
205
|
+
const skillName = sanitizeSkillName(rawSkill);
|
|
206
|
+
if (!skillName)
|
|
207
|
+
continue;
|
|
208
|
+
const skillFile = resolveSkillFile(agentDir, skillName);
|
|
209
|
+
if (!skillFile)
|
|
210
|
+
continue;
|
|
211
|
+
const source = readFileSync(skillFile, 'utf-8');
|
|
212
|
+
const parsed = parseSkillMarkdown(source);
|
|
213
|
+
const name = sanitizeSkillName(parsed.meta.name) || skillName;
|
|
214
|
+
const aliases = new Set(parseListMeta(parsed.meta.aliases).map(sanitizeSkillName).filter(Boolean));
|
|
215
|
+
if (name !== skillName)
|
|
216
|
+
aliases.add(skillName);
|
|
217
|
+
registerSkill({
|
|
218
|
+
name,
|
|
219
|
+
aliases: aliases.size ? [...aliases] : undefined,
|
|
220
|
+
description: parsed.meta.description || summarizeSkillDescription(parsed.body, skillName),
|
|
221
|
+
whenToUse: parsed.meta['when-to-use'] || parsed.meta.whenToUse,
|
|
222
|
+
userInvocable: true,
|
|
223
|
+
async getPrompt() {
|
|
224
|
+
return [{ type: 'text', text: parsed.body || source }];
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
registeredConfiguredSkills.add(name);
|
|
228
|
+
registered.push(name);
|
|
229
|
+
}
|
|
230
|
+
return registered;
|
|
231
|
+
}
|
|
232
|
+
function resolveSkillFile(agentDir, skillName) {
|
|
233
|
+
const skillsBase = join(agentDir, 'skills');
|
|
234
|
+
const folderSkillFile = join(skillsBase, skillName, 'SKILL.md');
|
|
235
|
+
if (existsSync(folderSkillFile) && statSync(folderSkillFile).size > 0)
|
|
236
|
+
return folderSkillFile;
|
|
237
|
+
const legacySkillFile = join(skillsBase, `${skillName}.md`);
|
|
238
|
+
if (existsSync(legacySkillFile) && statSync(legacySkillFile).size > 0)
|
|
239
|
+
return legacySkillFile;
|
|
240
|
+
const globalSkillFile = join(getDataDir(), 'skills', skillName, 'SKILL.md');
|
|
241
|
+
if (existsSync(globalSkillFile) && statSync(globalSkillFile).size > 0)
|
|
242
|
+
return globalSkillFile;
|
|
243
|
+
const globalLegacySkillFile = join(getDataDir(), 'skills', `${skillName}.md`);
|
|
244
|
+
if (existsSync(globalLegacySkillFile) && statSync(globalLegacySkillFile).size > 0)
|
|
245
|
+
return globalLegacySkillFile;
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
function getDataDir() {
|
|
249
|
+
return process.env.AGENT_SPACES_DATA_DIR || DEFAULT_DATA_DIR;
|
|
250
|
+
}
|
|
251
|
+
function parseSkillMarkdown(source) {
|
|
252
|
+
const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
253
|
+
if (!match)
|
|
254
|
+
return { meta: {}, body: source.trim() };
|
|
255
|
+
const meta = {};
|
|
256
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
257
|
+
const parsed = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
258
|
+
if (!parsed)
|
|
259
|
+
continue;
|
|
260
|
+
meta[parsed[1]] = parsed[2].trim().replace(/^['"]|['"]$/g, '');
|
|
261
|
+
}
|
|
262
|
+
return { meta, body: source.slice(match[0].length).trim() };
|
|
263
|
+
}
|
|
264
|
+
function summarizeSkillDescription(body, skillName) {
|
|
265
|
+
const firstLine = body
|
|
266
|
+
.split(/\r?\n/)
|
|
267
|
+
.map((line) => line.replace(/^#+\s*/, '').trim())
|
|
268
|
+
.find(Boolean);
|
|
269
|
+
return firstLine || `Configured skill ${skillName}`;
|
|
270
|
+
}
|
|
271
|
+
function parseListMeta(value) {
|
|
272
|
+
if (!value)
|
|
273
|
+
return [];
|
|
274
|
+
const trimmed = value.trim();
|
|
275
|
+
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
276
|
+
return trimmed.slice(1, -1).split(',').map((item) => item.trim().replace(/^['"]|['"]$/g, ''));
|
|
277
|
+
}
|
|
278
|
+
return trimmed.split(',').map((item) => item.trim());
|
|
279
|
+
}
|
|
280
|
+
function sanitizeSkillName(name) {
|
|
281
|
+
const raw = basename(name ?? '').replace(/\.md$/i, '').trim();
|
|
282
|
+
return raw.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
283
|
+
}
|
|
87
284
|
//# sourceMappingURL=open-agent-sdk-runtime.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { clearSkills, getSkill } from '@codeany/open-agent-sdk';
|
|
7
|
+
import { registerConfiguredSkills } from './open-agent-sdk-runtime.js';
|
|
8
|
+
test('registerConfiguredSkills loads folder skills from agent config', async () => {
|
|
9
|
+
clearSkills();
|
|
10
|
+
const agentDir = mkdtempSync(join(tmpdir(), 'agent-skills-'));
|
|
11
|
+
try {
|
|
12
|
+
mkdirSync(join(agentDir, 'skills', 'brainstorming'), { recursive: true });
|
|
13
|
+
writeFileSync(join(agentDir, 'skills', 'brainstorming', 'SKILL.md'), [
|
|
14
|
+
'---',
|
|
15
|
+
'name: brainstorming',
|
|
16
|
+
'description: Explore requirements before implementation.',
|
|
17
|
+
'---',
|
|
18
|
+
'',
|
|
19
|
+
'Ask clarifying questions and produce a design.',
|
|
20
|
+
].join('\n'), 'utf-8');
|
|
21
|
+
const registered = registerConfiguredSkills(agentDir, ['brainstorming']);
|
|
22
|
+
const skill = getSkill('brainstorming');
|
|
23
|
+
assert.deepEqual(registered, ['brainstorming']);
|
|
24
|
+
assert.ok(skill);
|
|
25
|
+
assert.equal(skill.description, 'Explore requirements before implementation.');
|
|
26
|
+
assert.deepEqual(await skill.getPrompt('', {}), [
|
|
27
|
+
{ type: 'text', text: 'Ask clarifying questions and produce a design.' },
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
rmSync(agentDir, { recursive: true, force: true });
|
|
32
|
+
clearSkills();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=open-agent-sdk-runtime.test.js.map
|
|
@@ -63,7 +63,8 @@ export function createAgentMessagePartsTracker(input) {
|
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
function buildAgentMessageParts(input) {
|
|
66
|
-
const
|
|
66
|
+
const extractedReasoning = extractInlineReasoning(normalizeOutputLines(input.output));
|
|
67
|
+
const lines = extractedReasoning.lines;
|
|
67
68
|
const finalTextRange = findFinalTextRange(lines);
|
|
68
69
|
const finalText = finalTextRange
|
|
69
70
|
? collapseRepeatedTextBlock(lines.slice(finalTextRange.start, finalTextRange.end + 1)).join('\n').trim()
|
|
@@ -71,7 +72,10 @@ function buildAgentMessageParts(input) {
|
|
|
71
72
|
const usage = input.usage ?? extractUsage(lines);
|
|
72
73
|
const parts = [];
|
|
73
74
|
const chainItems = buildChainItems(lines, finalTextRange, finalText, input.workspaceRoot, input.toolDetails, input.persistentContext);
|
|
74
|
-
const reasoningText = normalizeReasoningText(
|
|
75
|
+
const reasoningText = normalizeReasoningText([
|
|
76
|
+
...(input.reasoning ?? []),
|
|
77
|
+
...extractedReasoning.reasoning.map((text) => ({ text })),
|
|
78
|
+
]);
|
|
75
79
|
if (reasoningText) {
|
|
76
80
|
parts.push({
|
|
77
81
|
id: `reasoning-${input.sessionId}`,
|
|
@@ -124,6 +128,52 @@ function normalizeReasoningText(reasoning) {
|
|
|
124
128
|
.map((item) => item.text.trim())
|
|
125
129
|
.filter(Boolean)).join('\n\n').trim();
|
|
126
130
|
}
|
|
131
|
+
function extractInlineReasoning(lines) {
|
|
132
|
+
const reasoning = [];
|
|
133
|
+
const outputLines = [];
|
|
134
|
+
let buffer = [];
|
|
135
|
+
let inThinkBlock = false;
|
|
136
|
+
const flushReasoning = () => {
|
|
137
|
+
const text = buffer.join('\n').trim();
|
|
138
|
+
if (text)
|
|
139
|
+
reasoning.push(text);
|
|
140
|
+
buffer = [];
|
|
141
|
+
};
|
|
142
|
+
for (const line of lines) {
|
|
143
|
+
let rest = line;
|
|
144
|
+
let visible = '';
|
|
145
|
+
while (rest) {
|
|
146
|
+
if (inThinkBlock) {
|
|
147
|
+
const closeIndex = rest.search(/<\/think>/i);
|
|
148
|
+
if (closeIndex < 0) {
|
|
149
|
+
buffer.push(rest);
|
|
150
|
+
rest = '';
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
buffer.push(rest.slice(0, closeIndex));
|
|
154
|
+
flushReasoning();
|
|
155
|
+
rest = rest.slice(closeIndex).replace(/^<\/think>/i, '');
|
|
156
|
+
inThinkBlock = false;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const openIndex = rest.search(/<think>/i);
|
|
160
|
+
if (openIndex < 0) {
|
|
161
|
+
visible += rest;
|
|
162
|
+
rest = '';
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
visible += rest.slice(0, openIndex);
|
|
166
|
+
rest = rest.slice(openIndex).replace(/^<think>/i, '');
|
|
167
|
+
inThinkBlock = true;
|
|
168
|
+
}
|
|
169
|
+
const cleaned = visible.trimEnd();
|
|
170
|
+
if (cleaned.trim())
|
|
171
|
+
outputLines.push(cleaned);
|
|
172
|
+
}
|
|
173
|
+
if (inThinkBlock)
|
|
174
|
+
flushReasoning();
|
|
175
|
+
return { lines: outputLines, reasoning };
|
|
176
|
+
}
|
|
127
177
|
function hasTokenUsage(usage) {
|
|
128
178
|
return Boolean(usage.totalTokens || usage.inputTokens || usage.outputTokens || usage.cachedInputTokens || usage.reasoningTokens);
|
|
129
179
|
}
|
|
@@ -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,50 @@ 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
|
+
const cmdData = commandNode.data;
|
|
294
|
+
const cmdScript = cmdData.script;
|
|
295
|
+
ctx.broadcast('task.output', { taskId, data: `$ ${cmdScript.split('\n')[0]}${cmdScript.includes('\n') ? ' ...' : ''}` });
|
|
296
|
+
const result = await executeCommandNode(workspaceId, commandNode);
|
|
297
|
+
ctx.broadcast('task.output', { taskId, data: result.stdout });
|
|
298
|
+
if (result.stderr) {
|
|
299
|
+
ctx.broadcast('task.output', { taskId, data: result.stderr });
|
|
300
|
+
}
|
|
301
|
+
if (!result.success) {
|
|
302
|
+
const failedTask = taskService.updateStatus(workspaceId, taskId, 'failed', {
|
|
303
|
+
result: {
|
|
304
|
+
success: false,
|
|
305
|
+
summary: `Command failed with exit code ${result.exitCode}`,
|
|
306
|
+
artifacts: [],
|
|
307
|
+
error: result.stderr || `Exit code: ${result.exitCode}`,
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
broadcastTaskUpdate(ctx, failedTask, 'running');
|
|
311
|
+
await handleTaskFailure(workspaceId, issueId, taskId, {
|
|
312
|
+
success: false,
|
|
313
|
+
summary: `Command failed with exit code ${result.exitCode}`,
|
|
314
|
+
artifacts: [],
|
|
315
|
+
error: result.stderr || `Exit code: ${result.exitCode}`,
|
|
316
|
+
}, ctx);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const completedTask = taskService.complete(workspaceId, taskId, {
|
|
320
|
+
success: true,
|
|
321
|
+
summary: result.stdout.slice(-500) || 'Command completed',
|
|
322
|
+
artifacts: [],
|
|
323
|
+
});
|
|
324
|
+
broadcastTaskUpdate(ctx, completedTask, 'running');
|
|
325
|
+
if (issueService.getById(workspaceId, issueId)?.continuousRun !== false) {
|
|
326
|
+
await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
277
329
|
export async function continueIssueTaskScheduling(workspaceId, issueId, ctx) {
|
|
278
330
|
await scheduleRunnableIssueTasks(workspaceId, issueId, ctx, { force: true });
|
|
279
331
|
}
|
|
@@ -402,7 +454,7 @@ export function createTasksFromWorkflow(workspaceId, issueId, template, ctx) {
|
|
|
402
454
|
});
|
|
403
455
|
}
|
|
404
456
|
function ensureWorkflowAgentsForRun(workspaceId, issue, template, ctx) {
|
|
405
|
-
const workflowAgentIds = [...new Set(template.nodes.map((node) => node.data.agentConfigId).filter(Boolean))];
|
|
457
|
+
const workflowAgentIds = [...new Set(template.nodes.filter((n) => n.type === 'agent').map((node) => node.data.agentConfigId).filter(Boolean))];
|
|
406
458
|
const mergedMembers = [...new Set([...(issue.members ?? []), ...workflowAgentIds])];
|
|
407
459
|
const membersChanged = mergedMembers.length !== (issue.members ?? []).length
|
|
408
460
|
|| mergedMembers.some((member, index) => member !== issue.members?.[index]);
|
|
@@ -502,6 +554,23 @@ function unregisterActiveIssueRuntime(workspaceId, issueId, agentId) {
|
|
|
502
554
|
function issueRunKey(workspaceId, issueId) {
|
|
503
555
|
return `${workspaceId}:${issueId}`;
|
|
504
556
|
}
|
|
557
|
+
function findWorkflowForIssue(workspaceId, issue) {
|
|
558
|
+
if (!issue.workflowId)
|
|
559
|
+
return null;
|
|
560
|
+
return getWorkflow(issue.workflowId);
|
|
561
|
+
}
|
|
562
|
+
function findCommandNodeForTask(workflow, task) {
|
|
563
|
+
if (!workflow)
|
|
564
|
+
return null;
|
|
565
|
+
if (task.agentConfigId)
|
|
566
|
+
return null;
|
|
567
|
+
for (const node of workflow.nodes) {
|
|
568
|
+
if (node.type === 'command' && (task.title === node.label || task.description?.startsWith('Command:'))) {
|
|
569
|
+
return node;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
505
574
|
function broadcastTaskUpdate(ctx, task, from) {
|
|
506
575
|
if (!task)
|
|
507
576
|
return;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as agentService from '../services/agent.js';
|
|
2
|
+
import { AGENT_GENERATOR_PRESET_ID } from '../services/agent.js';
|
|
3
|
+
import { createAgentRuntime } from '../adapters/agent-runtime.js';
|
|
4
|
+
import { getWorkspace } from '../storage/workspace-store.js';
|
|
5
|
+
import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
|
|
6
|
+
import { prependPersistentAgentContext } from '../services/persistent-agent-context.js';
|
|
7
|
+
const DEFAULT_SYSTEM_PROMPT = [
|
|
8
|
+
'You are a pull request description generator.',
|
|
9
|
+
'Return a concise, useful PR body based only on the provided commits and diff.',
|
|
10
|
+
'Use Markdown with these sections: Summary and Changes.',
|
|
11
|
+
'Keep it short. Do not greet, explain your process, ask questions, provide options, or wrap the response in code fences.',
|
|
12
|
+
'Output only the final PR body text.',
|
|
13
|
+
].join(' ');
|
|
14
|
+
export async function runPullRequestAgent(workspaceId, input) {
|
|
15
|
+
const ws = getWorkspace(workspaceId);
|
|
16
|
+
if (!ws)
|
|
17
|
+
throw new Error('Workspace not found');
|
|
18
|
+
if (!input.diff.trim() && input.commits.length === 0)
|
|
19
|
+
throw new Error('No changes found');
|
|
20
|
+
const agent = findPullRequestAgent(workspaceId);
|
|
21
|
+
const session = agentService.getOrCreateSessionForConfig(workspaceId, agent);
|
|
22
|
+
agentService.updateStatus(workspaceId, session.id, 'active');
|
|
23
|
+
const runtime = createAgentRuntime({
|
|
24
|
+
kind: agent.runtimeKind,
|
|
25
|
+
provider: agent.modelProvider,
|
|
26
|
+
model: agent.modelId,
|
|
27
|
+
apiKey: agent.apiKey,
|
|
28
|
+
baseURL: agent.apiBase,
|
|
29
|
+
...getThinkingRuntimeConfig(agent),
|
|
30
|
+
});
|
|
31
|
+
const workingDir = agentService.resolveWorkingDir(workspaceId, agent);
|
|
32
|
+
const systemPrompt = DEFAULT_SYSTEM_PROMPT;
|
|
33
|
+
const truncatedDiff = input.diff.length > 12000
|
|
34
|
+
? `${input.diff.substring(0, 12000)}\n... (truncated)`
|
|
35
|
+
: input.diff;
|
|
36
|
+
const userPrompt = [
|
|
37
|
+
`Generate a pull request body for branch ${input.worktree.branch} targeting ${input.baseBranch}.`,
|
|
38
|
+
'',
|
|
39
|
+
'Commits:',
|
|
40
|
+
input.commits.length > 0 ? input.commits.map((commit) => `- ${commit}`).join('\n') : '- No commit messages available',
|
|
41
|
+
'',
|
|
42
|
+
'Diff:',
|
|
43
|
+
truncatedDiff,
|
|
44
|
+
].join('\n');
|
|
45
|
+
const result = await runtime.execute(prependPersistentAgentContext(userPrompt, {
|
|
46
|
+
workspaceId,
|
|
47
|
+
workingDir,
|
|
48
|
+
boundDirs: ws.boundDirs,
|
|
49
|
+
includeWorkspacePrompt: false,
|
|
50
|
+
excludeNativeClaudeMd: agent.runtimeKind === 'claude-code',
|
|
51
|
+
}), workingDir, {
|
|
52
|
+
maxTurns: 1,
|
|
53
|
+
userPrompt,
|
|
54
|
+
systemPrompt,
|
|
55
|
+
outputStyle: agent.outputStyle,
|
|
56
|
+
});
|
|
57
|
+
agentService.complete(workspaceId, session.id, result.success ? undefined : result.error, {
|
|
58
|
+
runtime: agent.runtimeKind,
|
|
59
|
+
model: agent.modelId,
|
|
60
|
+
summary: result.summary,
|
|
61
|
+
output: result.output,
|
|
62
|
+
usage: result.usage,
|
|
63
|
+
costUsd: result.costUsd,
|
|
64
|
+
});
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
throw new Error(result.error || 'Pull request agent failed');
|
|
67
|
+
}
|
|
68
|
+
const body = sanitizePullRequestBody(result.output);
|
|
69
|
+
if (!body)
|
|
70
|
+
throw new Error('Empty response from pull request agent');
|
|
71
|
+
return body;
|
|
72
|
+
}
|
|
73
|
+
function findPullRequestAgent(workspaceId) {
|
|
74
|
+
const presets = agentService.listPresets(workspaceId);
|
|
75
|
+
const preset = presets?.find((a) => a.id === AGENT_GENERATOR_PRESET_ID);
|
|
76
|
+
if (preset)
|
|
77
|
+
return preset;
|
|
78
|
+
const template = agentService.readAgentTemplate(AGENT_GENERATOR_PRESET_ID);
|
|
79
|
+
if (template)
|
|
80
|
+
return template;
|
|
81
|
+
return agentService.listTemplates().find((a) => a.id === AGENT_GENERATOR_PRESET_ID) ?? agentService.listTemplates()[0];
|
|
82
|
+
}
|
|
83
|
+
function sanitizePullRequestBody(output) {
|
|
84
|
+
const chunks = (Array.isArray(output) ? output : [output])
|
|
85
|
+
.map((chunk) => cleanOutputChunk(chunk))
|
|
86
|
+
.filter(Boolean);
|
|
87
|
+
const uniqueChunks = dedupeAdjacentChunks(chunks);
|
|
88
|
+
return (uniqueChunks.at(-1) || uniqueChunks.join('\n\n')).trim();
|
|
89
|
+
}
|
|
90
|
+
function cleanOutputChunk(chunk) {
|
|
91
|
+
return chunk
|
|
92
|
+
.split('\n')
|
|
93
|
+
.filter((line) => !isRuntimeNoise(line))
|
|
94
|
+
.join('\n')
|
|
95
|
+
.replace(/```(?:\w+)?\n([\s\S]*?)```/g, '$1')
|
|
96
|
+
.trim();
|
|
97
|
+
}
|
|
98
|
+
function dedupeAdjacentChunks(chunks) {
|
|
99
|
+
const result = [];
|
|
100
|
+
for (const chunk of chunks) {
|
|
101
|
+
if (result.at(-1)?.trim() === chunk.trim())
|
|
102
|
+
continue;
|
|
103
|
+
result.push(chunk);
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
function isRuntimeNoise(line) {
|
|
108
|
+
const trimmed = line.trim();
|
|
109
|
+
return /^\[Usage\]/.test(trimmed)
|
|
110
|
+
|| /^Claude Code initialized\b/i.test(trimmed)
|
|
111
|
+
|| /^Codex initialized\b/i.test(trimmed)
|
|
112
|
+
|| /^Tool:/i.test(trimmed)
|
|
113
|
+
|| /^Todo:/i.test(trimmed);
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=pull-request-agent.js.map
|
|
@@ -16,7 +16,7 @@ export function startScheduler(workspaceId, ctx) {
|
|
|
16
16
|
const tick = async () => {
|
|
17
17
|
// 检查 workspace 是否关闭了自动处理
|
|
18
18
|
const workspace = workspaceService.getById(workspaceId);
|
|
19
|
-
if (workspace && workspace.autoProcessIssues
|
|
19
|
+
if (workspace && workspace.autoProcessIssues !== true) {
|
|
20
20
|
console.log(`[scheduler:${workspaceId}] auto-processing disabled for this workspace, skip`);
|
|
21
21
|
return;
|
|
22
22
|
}
|