@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.
Files changed (230) hide show
  1. package/dist/adapters/agent-runtime.js +4 -0
  2. package/dist/adapters/codex-function-tool-bridge.js +144 -0
  3. package/dist/adapters/codex-runtime.js +23 -4
  4. package/dist/adapters/git.js +12 -1
  5. package/dist/adapters/hermes-runtime.js +210 -0
  6. package/dist/agents/issue-task-controller.js +69 -2
  7. package/dist/agents/pull-request-agent.js +115 -0
  8. package/dist/agents/scheduler-agent.js +1 -1
  9. package/dist/app.js +6 -2
  10. package/dist/middleware/auth.js +1 -1
  11. package/dist/package.json +2 -1
  12. package/dist/routes/agent-sse.js +1 -0
  13. package/dist/routes/issue.js +1 -1
  14. package/dist/routes/notification.js +10 -0
  15. package/dist/routes/version.js +94 -0
  16. package/dist/routes/worktree.js +93 -0
  17. package/dist/services/agent.js +1 -1
  18. package/dist/services/notification-center.js +9 -0
  19. package/dist/services/version.js +59 -0
  20. package/dist/services/workflow-command-runner.js +23 -0
  21. package/dist/services/workflow.js +25 -8
  22. package/dist/services/workspace.js +23 -1
  23. package/dist/services/worktree.js +207 -0
  24. package/dist/storage/worktree-store.js +33 -0
  25. package/dist/web/404.html +1 -1
  26. package/dist/web/__next.__PAGE__.txt +4 -4
  27. package/dist/web/__next._full.txt +21 -21
  28. package/dist/web/__next._head.txt +4 -4
  29. package/dist/web/__next._index.txt +13 -13
  30. package/dist/web/__next._tree.txt +2 -2
  31. package/dist/web/_next/static/chunks/{08x85faoz58i7.js → 0-92u-tiuy40a.js} +4 -4
  32. package/dist/web/_next/static/chunks/{0db14450oqzhg.js → 0-yizd8osmas2.js} +1 -1
  33. package/dist/web/_next/static/chunks/0.ln~3mnv8.69.js +1 -0
  34. package/dist/web/_next/static/chunks/{11cchuwoupmbf.js → 01y3e9pemva7b.js} +10 -10
  35. package/dist/web/_next/static/chunks/02l~tmzrgny.~.js +1 -0
  36. package/dist/web/_next/static/chunks/03f1bwuynjdmk.js +1 -0
  37. package/dist/web/_next/static/chunks/04cb4kc9r2pd1.js +1 -0
  38. package/dist/web/_next/static/chunks/04de6ha5_e3pp.js +1 -0
  39. package/dist/web/_next/static/chunks/09he0p79ov1rn.js +1 -0
  40. package/dist/web/_next/static/chunks/09vpocs4zuc8h.js +1 -0
  41. package/dist/web/_next/static/chunks/0_o2exgr0e_nh.js +1 -0
  42. package/dist/web/_next/static/chunks/{0xbaakj0dfc0..js → 0_y9nskc-xt2k.js} +1 -1
  43. package/dist/web/_next/static/chunks/0clnj-1fi6u8j.js +3 -0
  44. package/dist/web/_next/static/chunks/0gv40by0zzxs_.js +1 -0
  45. package/dist/web/_next/static/chunks/0iusr8y88.kjg.js +1 -0
  46. package/dist/web/_next/static/chunks/{0-a.-hl3wv99k.js → 0j-8418fxvkd1.js} +1 -1
  47. package/dist/web/_next/static/chunks/0j.wxxyoclma5.js +1 -0
  48. package/dist/web/_next/static/chunks/{0.6b64sy0g5.3.js → 0jq.16sozqik..js} +1 -1
  49. package/dist/web/_next/static/chunks/0jw8xdk_fpnp0.css +1 -0
  50. package/dist/web/_next/static/chunks/0l1lkd-wcotma.js +1 -0
  51. package/dist/web/_next/static/chunks/{0u6h-hqc_i52e.js → 0ncxf_zr4n839.js} +1 -1
  52. package/dist/web/_next/static/chunks/0ndo7um~v7vco.js +1 -0
  53. package/dist/web/_next/static/chunks/0ngk.~kan8wq_.js +1 -0
  54. package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +1 -0
  55. package/dist/web/_next/static/chunks/0rt9bofnn6qvn.js +1 -0
  56. package/dist/web/_next/static/chunks/0t64.9jd00hio.js +3 -0
  57. package/dist/web/_next/static/chunks/0taui9--5n6-5.js +1 -0
  58. package/dist/web/_next/static/chunks/0ty39169lh~z3.js +1 -0
  59. package/dist/web/_next/static/chunks/0v8nof~waedlu.js +1 -0
  60. package/dist/web/_next/static/chunks/{0-pi.67w.ymsr.js → 0vcfoagnpf.aw.js} +1 -1
  61. package/dist/web/_next/static/chunks/{0vis~lx0nxgn-.js → 0wwwq.gyyz0vg.js} +3 -3
  62. package/dist/web/_next/static/chunks/0x5lak8yz7pm7.js +1 -0
  63. package/dist/web/_next/static/chunks/0xon_.4-o0_zq.js +7 -0
  64. package/dist/web/_next/static/chunks/0zjnrbt7inime.js +1 -0
  65. package/dist/web/_next/static/chunks/0zwebpitvpuv1.js +1 -0
  66. package/dist/web/_next/static/chunks/0~02o4g-o6n~m.js +1 -0
  67. package/dist/web/_next/static/chunks/{0w7.tb29958zj.js → 0~fjezp.k4qk3.js} +1 -1
  68. package/dist/web/_next/static/chunks/0~sh93re1-_rp.js +1 -0
  69. package/dist/web/_next/static/chunks/10_niiq0k6o-p.js +5 -0
  70. package/dist/web/_next/static/chunks/12tjr0_2ccrtq.js +2 -0
  71. package/dist/web/_next/static/chunks/12x5.go_x9mfk.js +1 -0
  72. package/dist/web/_next/static/chunks/{0l2w0qz1ea7ql.js → 12~5dk9i9d16t.js} +2 -2
  73. package/dist/web/_next/static/chunks/13iye4rv5p31b.js +1 -0
  74. package/dist/web/_next/static/chunks/{01~quun6sefgl.js → 13~y-b.b9rrw4.js} +1 -1
  75. package/dist/web/_next/static/chunks/151wzhb5q0lo5.js +1 -0
  76. package/dist/web/_not-found/__next._full.txt +19 -19
  77. package/dist/web/_not-found/__next._head.txt +4 -4
  78. package/dist/web/_not-found/__next._index.txt +13 -13
  79. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  80. package/dist/web/_not-found/__next._not-found.txt +3 -3
  81. package/dist/web/_not-found/__next._tree.txt +2 -2
  82. package/dist/web/_not-found.html +1 -1
  83. package/dist/web/_not-found.txt +19 -19
  84. package/dist/web/index.html +1 -1
  85. package/dist/web/index.txt +21 -21
  86. package/dist/web/login/__next._full.txt +21 -21
  87. package/dist/web/login/__next._head.txt +4 -4
  88. package/dist/web/login/__next._index.txt +13 -13
  89. package/dist/web/login/__next._tree.txt +2 -2
  90. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  91. package/dist/web/login/__next.login.txt +3 -3
  92. package/dist/web/login.html +1 -1
  93. package/dist/web/login.txt +21 -21
  94. package/dist/web/settings/__next._full.txt +23 -23
  95. package/dist/web/settings/__next._head.txt +4 -4
  96. package/dist/web/settings/__next._index.txt +13 -13
  97. package/dist/web/settings/__next._tree.txt +2 -2
  98. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  99. package/dist/web/settings/__next.settings.txt +5 -5
  100. package/dist/web/settings/agents/__next._full.txt +23 -23
  101. package/dist/web/settings/agents/__next._head.txt +4 -4
  102. package/dist/web/settings/agents/__next._index.txt +13 -13
  103. package/dist/web/settings/agents/__next._tree.txt +2 -2
  104. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  105. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  106. package/dist/web/settings/agents/__next.settings.txt +5 -5
  107. package/dist/web/settings/agents.html +1 -1
  108. package/dist/web/settings/agents.txt +23 -23
  109. package/dist/web/settings/mcps/__next._full.txt +23 -23
  110. package/dist/web/settings/mcps/__next._head.txt +4 -4
  111. package/dist/web/settings/mcps/__next._index.txt +13 -13
  112. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  113. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  114. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  115. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  116. package/dist/web/settings/mcps.html +1 -1
  117. package/dist/web/settings/mcps.txt +23 -23
  118. package/dist/web/settings/models/__next._full.txt +23 -23
  119. package/dist/web/settings/models/__next._head.txt +4 -4
  120. package/dist/web/settings/models/__next._index.txt +13 -13
  121. package/dist/web/settings/models/__next._tree.txt +2 -2
  122. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  123. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  124. package/dist/web/settings/models/__next.settings.txt +5 -5
  125. package/dist/web/settings/models.html +1 -1
  126. package/dist/web/settings/models.txt +23 -23
  127. package/dist/web/settings/output-styles/__next._full.txt +23 -23
  128. package/dist/web/settings/output-styles/__next._head.txt +4 -4
  129. package/dist/web/settings/output-styles/__next._index.txt +13 -13
  130. package/dist/web/settings/output-styles/__next._tree.txt +2 -2
  131. package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
  132. package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
  133. package/dist/web/settings/output-styles/__next.settings.txt +5 -5
  134. package/dist/web/settings/output-styles.html +1 -1
  135. package/dist/web/settings/output-styles.txt +23 -23
  136. package/dist/web/settings/prompts/__next._full.txt +23 -23
  137. package/dist/web/settings/prompts/__next._head.txt +4 -4
  138. package/dist/web/settings/prompts/__next._index.txt +13 -13
  139. package/dist/web/settings/prompts/__next._tree.txt +2 -2
  140. package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
  141. package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
  142. package/dist/web/settings/prompts/__next.settings.txt +5 -5
  143. package/dist/web/settings/prompts.html +1 -1
  144. package/dist/web/settings/prompts.txt +23 -23
  145. package/dist/web/settings/providers/__next._full.txt +23 -23
  146. package/dist/web/settings/providers/__next._head.txt +4 -4
  147. package/dist/web/settings/providers/__next._index.txt +13 -13
  148. package/dist/web/settings/providers/__next._tree.txt +2 -2
  149. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  150. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  151. package/dist/web/settings/providers/__next.settings.txt +5 -5
  152. package/dist/web/settings/providers.html +1 -1
  153. package/dist/web/settings/providers.txt +23 -23
  154. package/dist/web/settings/skills/__next._full.txt +23 -23
  155. package/dist/web/settings/skills/__next._head.txt +4 -4
  156. package/dist/web/settings/skills/__next._index.txt +13 -13
  157. package/dist/web/settings/skills/__next._tree.txt +2 -2
  158. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  159. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  160. package/dist/web/settings/skills/__next.settings.txt +5 -5
  161. package/dist/web/settings/skills.html +1 -1
  162. package/dist/web/settings/skills.txt +23 -23
  163. package/dist/web/settings/tools/__next._full.txt +23 -23
  164. package/dist/web/settings/tools/__next._head.txt +4 -4
  165. package/dist/web/settings/tools/__next._index.txt +13 -13
  166. package/dist/web/settings/tools/__next._tree.txt +2 -2
  167. package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
  168. package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
  169. package/dist/web/settings/tools/__next.settings.txt +5 -5
  170. package/dist/web/settings/tools.html +1 -1
  171. package/dist/web/settings/tools.txt +23 -23
  172. package/dist/web/settings.html +1 -1
  173. package/dist/web/settings.txt +23 -23
  174. package/dist/web/workflows/__next._full.txt +22 -22
  175. package/dist/web/workflows/__next._head.txt +4 -4
  176. package/dist/web/workflows/__next._index.txt +13 -13
  177. package/dist/web/workflows/__next._tree.txt +2 -2
  178. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  179. package/dist/web/workflows/__next.workflows.txt +3 -3
  180. package/dist/web/workflows.html +1 -1
  181. package/dist/web/workflows.txt +22 -22
  182. package/dist/web/workspace/_/__next._full.txt +20 -20
  183. package/dist/web/workspace/_/__next._head.txt +4 -4
  184. package/dist/web/workspace/_/__next._index.txt +13 -13
  185. package/dist/web/workspace/_/__next._tree.txt +2 -2
  186. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  187. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  188. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  189. package/dist/web/workspace/_.html +1 -1
  190. package/dist/web/workspace/_.txt +20 -20
  191. package/dist/web/workspaces/__next._full.txt +21 -21
  192. package/dist/web/workspaces/__next._head.txt +4 -4
  193. package/dist/web/workspaces/__next._index.txt +13 -13
  194. package/dist/web/workspaces/__next._tree.txt +2 -2
  195. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  196. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  197. package/dist/web/workspaces.html +1 -1
  198. package/dist/web/workspaces.txt +21 -21
  199. package/dist/ws/agent-prompt.js +32 -3
  200. package/dist/ws/agent-runner.js +1 -0
  201. package/package.json +2 -1
  202. package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +0 -1
  203. package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +0 -1
  204. package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +0 -1
  205. package/dist/web/_next/static/chunks/0.n~yfwf4~6w-.js +0 -1
  206. package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +0 -1
  207. package/dist/web/_next/static/chunks/02j4sr9psucf-.js +0 -1
  208. package/dist/web/_next/static/chunks/06si8l0doqe8p.js +0 -1
  209. package/dist/web/_next/static/chunks/08kfx8on9oty~.js +0 -1
  210. package/dist/web/_next/static/chunks/08ugbvzr20drp.js +0 -2
  211. package/dist/web/_next/static/chunks/0_cqojbge48kq.js +0 -5
  212. package/dist/web/_next/static/chunks/0d6~~lv0-6kex.js +0 -3
  213. package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +0 -1
  214. package/dist/web/_next/static/chunks/0ghx3j_.-gdyd.js +0 -1
  215. package/dist/web/_next/static/chunks/0kbvoxla7f751.js +0 -1
  216. package/dist/web/_next/static/chunks/0paaat8y96ayc.js +0 -7
  217. package/dist/web/_next/static/chunks/0pq318f8xgafg.js +0 -1
  218. package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +0 -1
  219. package/dist/web/_next/static/chunks/0xt-8wk21gs7g.js +0 -1
  220. package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +0 -3
  221. package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +0 -1
  222. package/dist/web/_next/static/chunks/0z~vqzbm_q371.js +0 -1
  223. package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +0 -1
  224. package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +0 -1
  225. package/dist/web/_next/static/chunks/1452eibz62gs6.css +0 -1
  226. package/dist/web/_next/static/chunks/16zlreiems5cn.js +0 -1
  227. package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +0 -1
  228. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_buildManifest.js +0 -0
  229. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_clientMiddlewareManifest.js +0 -0
  230. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_ssgManifest.js +0 -0
@@ -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 === false) {
19
+ if (workspace && workspace.autoProcessIssues !== true) {
20
20
  console.log(`[scheduler:${workspaceId}] auto-processing disabled for this workspace, skip`);
21
21
  return;
22
22
  }
package/dist/app.js CHANGED
@@ -33,16 +33,18 @@ import searchRouter from './routes/search.js';
33
33
  import notificationRouter from './routes/notification.js';
34
34
  import databaseRouter from './routes/database.js';
35
35
  import kanbanRouter from './routes/kanban.js';
36
+ import { worktreeRouter } from './routes/worktree.js';
36
37
  import speechRecognitionRouter, { handleSpeechStream } from './routes/speech-recognition.js';
37
38
  import agentCommandsRouter from './routes/agent-commands.js';
38
39
  import robotAccountRouter from './routes/robot-account.js';
40
+ import versionRouter from './routes/version.js';
39
41
  import { getUserSettings, setUserAvatarUrl, removeUserAvatarUrl } from './storage/user-settings-store.js';
40
42
  import { authMiddleware, verifyToken } from './middleware/auth.js';
41
43
  import { handleConnection } from './ws/handler.js';
42
44
  import { handleTypeScriptLspConnection } from './ws/typescript-lsp.js';
43
45
  import { broadcastToAll } from './ws/connection-manager.js';
44
46
  import { startScheduler, stopScheduler } from './agents/scheduler-agent.js';
45
- import { recoverRunningWorkOnStartup } from './services/issue-retry.js';
47
+ // import { recoverRunningWorkOnStartup } from './services/issue-retry.js';
46
48
  import { startPersistedNotificationServices } from './services/notification-hub/index.js';
47
49
  const __dirname = dirname(fileURLToPath(import.meta.url));
48
50
  const PORT = parseInt(process.env.PORT || '3100', 10);
@@ -191,6 +193,7 @@ app.post('/api/git-config', async (req, res) => {
191
193
  app.use('/api/workspaces/:id/search', searchRouter);
192
194
  app.use('/api/workspaces/:id/database', databaseRouter);
193
195
  app.use('/api/workspaces/:id/kanban', kanbanRouter);
196
+ app.use('/api/workspaces/:id/worktrees', worktreeRouter);
194
197
  app.use('/api/workspaces/:id/notifications', notificationRouter);
195
198
  app.use('/api/agents', agentRouter);
196
199
  app.use('/api', llmRouter);
@@ -203,6 +206,7 @@ app.use('/api/subscriptions', subscriptionRouter);
203
206
  app.use('/api/speech-recognition', speechRecognitionRouter);
204
207
  app.use('/api/agent-commands', agentCommandsRouter);
205
208
  app.use('/api/robot-accounts', robotAccountRouter);
209
+ app.use('/api', versionRouter);
206
210
  // Serve static web frontend in production (after API routes, before catch-all)
207
211
  const webDir = resolveRuntimeDir('web');
208
212
  if (existsSync(webDir)) {
@@ -296,7 +300,7 @@ const HOST = process.env.HOST || '0.0.0.0';
296
300
  server.listen(PORT, HOST, () => {
297
301
  console.log(`[server] listening on http://${HOST}:${PORT}`);
298
302
  console.log(`[server] websocket on ws://${HOST}:${PORT}/ws?workspaceId=...`);
299
- recoverRunningWorkOnStartup();
303
+ // recoverRunningWorkOnStartup();
300
304
  startPersistedNotificationServices().catch((err) => {
301
305
  console.error('[notification] failed to restore persisted services:', err);
302
306
  });
@@ -1,7 +1,7 @@
1
1
  import { getSecret } from '../services/auth-store.js';
2
2
  export function authMiddleware(req, res, next) {
3
3
  const secret = getSecret();
4
- const openPaths = ['/api/health', '/api/auth/login', '/api/auth/check'];
4
+ const openPaths = ['/api/health', '/api/auth/login', '/api/auth/check', '/api/version', '/api/version/check'];
5
5
  if (openPaths.includes(req.path))
6
6
  return next();
7
7
  const auth = req.headers.authorization;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-spaces/server",
3
- "version": "0.3.64",
3
+ "version": "0.3.66",
4
4
  "type": "module",
5
5
  "main": "app.js",
6
6
  "packageManager": "pnpm@10.17.1",
@@ -16,6 +16,7 @@
16
16
  "@langchain/google-genai": "^2.1.30",
17
17
  "@langchain/openai": "^1.4.5",
18
18
  "@larksuiteoapi/node-sdk": "^1.62.1",
19
+ "@modelcontextprotocol/sdk": "1.29.0",
19
20
  "@openai/codex-sdk": "^0.128.0",
20
21
  "cors": "^2.8.5",
21
22
  "dotenv": "^17.4.2",
@@ -67,6 +67,7 @@ router.post('/run', async (req, res) => {
67
67
  agentService.updateStatus(workspaceId, session.id, 'active');
68
68
  writeSse(res, 'status', { agentId: session.id, status: 'active' });
69
69
  const result = await runtime.execute(buildAgentPrompt(workspaceId, body.systemPrompt ?? preset.systemPrompt, userPrompt, normalizeMessages(body.messages), {
70
+ runtimeKind: preset.runtimeKind,
70
71
  mcpServers: Object.keys(mcpServers ?? {}),
71
72
  skills,
72
73
  boundDirs: workspace.boundDirs,
@@ -313,7 +313,7 @@ function mergeWorkflowMembers(members, workflowId) {
313
313
  return base;
314
314
  return Array.from(new Set([
315
315
  ...base,
316
- ...workflow.nodes.map((node) => node.data.agentConfigId).filter(Boolean),
316
+ ...workflow.nodes.filter((n) => n.type === 'agent').map((node) => node.data.agentConfigId).filter(Boolean),
317
317
  ]));
318
318
  }
319
319
  //# sourceMappingURL=issue.js.map
@@ -27,5 +27,15 @@ router.delete('/', (req, res) => {
27
27
  nc.clearAll(workspaceId);
28
28
  res.json({ ok: true });
29
29
  });
30
+ router.delete('/:notificationId', (req, res) => {
31
+ const workspaceId = req.params.id;
32
+ const notificationId = req.params.notificationId;
33
+ const removed = nc.removeNotification(workspaceId, notificationId);
34
+ if (!removed) {
35
+ res.status(404).json({ error: 'Not found' });
36
+ return;
37
+ }
38
+ res.json({ ok: true });
39
+ });
30
40
  export default router;
31
41
  //# sourceMappingURL=notification.js.map
@@ -0,0 +1,94 @@
1
+ import { Router } from 'express';
2
+ import { spawn } from 'node:child_process';
3
+ import { writeFileSync, unlinkSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { getLocalVersion, getCachedLatest, fetchLatestVersion, isNewerVersion } from '../services/version.js';
7
+ const router = Router();
8
+ const isDev = !process.env.NODE_ENV || process.env.NODE_ENV === 'development';
9
+ let isUpdating = false;
10
+ // GET /version — cached version info (openPaths in auth.ts)
11
+ router.get('/version', async (_req, res) => {
12
+ const local = getLocalVersion();
13
+ if (isDev) {
14
+ res.json({ local, latest: null, updateAvailable: false, dev: true });
15
+ return;
16
+ }
17
+ let latest = getCachedLatest();
18
+ if (!latest) {
19
+ latest = await fetchLatestVersion(false);
20
+ }
21
+ res.json({ local, latest, updateAvailable: latest ? isNewerVersion(latest, local) : false });
22
+ });
23
+ // GET /version/check — force npm lookup (openPaths in auth.ts)
24
+ router.get('/version/check', async (_req, res) => {
25
+ const local = getLocalVersion();
26
+ if (isDev) {
27
+ res.json({ local, latest: null, updateAvailable: false, dev: true });
28
+ return;
29
+ }
30
+ const latest = await fetchLatestVersion(true);
31
+ res.json({
32
+ local,
33
+ latest,
34
+ updateAvailable: latest ? isNewerVersion(latest, local) : false,
35
+ });
36
+ });
37
+ // POST /version/update — trigger self-update (requires auth)
38
+ router.post('/version/update', (_req, res) => {
39
+ if (isDev) {
40
+ res.status(403).json({ error: 'Updates are disabled in development mode' });
41
+ return;
42
+ }
43
+ if (isUpdating) {
44
+ res.status(409).json({ error: 'Update already in progress' });
45
+ return;
46
+ }
47
+ isUpdating = true;
48
+ const pid = process.pid;
49
+ const ppid = process.ppid;
50
+ const projectRoot = join(import.meta.dirname, '..', '..');
51
+ const isWin = process.platform === 'win32';
52
+ let scriptPath;
53
+ if (isWin) {
54
+ scriptPath = join(tmpdir(), `agent-spaces-update-${pid}.cmd`);
55
+ const cmd = [
56
+ '@echo off',
57
+ 'timeout /t 2 /nobreak >nul',
58
+ `taskkill /pid ${ppid} /f /t >nul 2>&1`,
59
+ `taskkill /pid ${pid} /f /t >nul 2>&1`,
60
+ `cd /d "${projectRoot}"`,
61
+ 'call pnpm install',
62
+ 'call pnpm start',
63
+ ].join('\r\n');
64
+ writeFileSync(scriptPath, cmd, 'utf-8');
65
+ }
66
+ else {
67
+ scriptPath = join(tmpdir(), `agent-spaces-update-${pid}.sh`);
68
+ const sh = [
69
+ '#!/bin/sh',
70
+ 'sleep 2',
71
+ `kill -9 ${ppid} 2>/dev/null || true`,
72
+ `kill -9 ${pid} 2>/dev/null || true`,
73
+ `cd "${projectRoot}"`,
74
+ 'pnpm install',
75
+ 'pnpm start',
76
+ ].join('\n');
77
+ writeFileSync(scriptPath, sh, 'utf-8');
78
+ }
79
+ const child = spawn(isWin ? 'cmd' : '/bin/sh', isWin ? ['/c', scriptPath] : [scriptPath], {
80
+ detached: true,
81
+ stdio: 'ignore',
82
+ windowsHide: true,
83
+ });
84
+ child.unref();
85
+ setTimeout(() => {
86
+ try {
87
+ unlinkSync(scriptPath);
88
+ }
89
+ catch { }
90
+ }, 10_000);
91
+ res.json({ ok: true });
92
+ });
93
+ export default router;
94
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1,93 @@
1
+ import { Router } from 'express';
2
+ import { z } from 'zod';
3
+ import { listWorkspaceWorktrees, getWorkspaceWorktree, createWorkspaceWorktree, deleteWorkspaceWorktree, getWorktreeDiff, createWorktreePR, mergeWorktreePR, getWorktreePRDraft, } from '../services/worktree.js';
4
+ export const worktreeRouter = Router({ mergeParams: true });
5
+ const createSchema = z.object({
6
+ name: z.string().min(1),
7
+ branch: z.string().optional(),
8
+ agentId: z.string().optional(),
9
+ issueId: z.string().optional(),
10
+ taskId: z.string().optional(),
11
+ });
12
+ const prSchema = z.object({
13
+ title: z.string().optional(),
14
+ body: z.string().optional(),
15
+ });
16
+ const prDraftSchema = z.object({
17
+ title: z.string().optional(),
18
+ });
19
+ worktreeRouter.get('/', (req, res) => {
20
+ const { id } = req.params;
21
+ res.json(listWorkspaceWorktrees(id));
22
+ });
23
+ worktreeRouter.post('/', async (req, res) => {
24
+ try {
25
+ const { id } = req.params;
26
+ const input = createSchema.parse(req.body);
27
+ const info = await createWorkspaceWorktree(id, input);
28
+ res.status(201).json(info);
29
+ }
30
+ catch (err) {
31
+ res.status(400).json({ error: err.message });
32
+ }
33
+ });
34
+ worktreeRouter.get('/:wtId', (req, res) => {
35
+ const { id, wtId } = req.params;
36
+ const info = getWorkspaceWorktree(id, wtId);
37
+ if (!info)
38
+ return res.status(404).json({ error: 'Worktree not found' });
39
+ res.json(info);
40
+ });
41
+ worktreeRouter.delete('/:wtId', async (req, res) => {
42
+ try {
43
+ const { id, wtId } = req.params;
44
+ await deleteWorkspaceWorktree(id, wtId);
45
+ res.json({ ok: true });
46
+ }
47
+ catch (err) {
48
+ res.status(400).json({ error: err.message });
49
+ }
50
+ });
51
+ worktreeRouter.get('/:wtId/diff', async (req, res) => {
52
+ try {
53
+ const { id, wtId } = req.params;
54
+ const diffs = await getWorktreeDiff(id, wtId);
55
+ res.json(diffs);
56
+ }
57
+ catch (err) {
58
+ res.status(400).json({ error: err.message });
59
+ }
60
+ });
61
+ worktreeRouter.post('/:wtId/pr', async (req, res) => {
62
+ try {
63
+ const { id, wtId } = req.params;
64
+ const { title, body } = prSchema.parse(req.body);
65
+ const prUrl = await createWorktreePR(id, wtId, title, body);
66
+ res.json({ prUrl });
67
+ }
68
+ catch (err) {
69
+ res.status(400).json({ error: err.message });
70
+ }
71
+ });
72
+ worktreeRouter.post('/:wtId/pr/draft', async (req, res) => {
73
+ try {
74
+ const { id, wtId } = req.params;
75
+ const { title } = prDraftSchema.parse(req.body);
76
+ const draft = await getWorktreePRDraft(id, wtId, title);
77
+ res.json(draft);
78
+ }
79
+ catch (err) {
80
+ res.status(400).json({ error: err.message });
81
+ }
82
+ });
83
+ worktreeRouter.post('/:wtId/merge', async (req, res) => {
84
+ try {
85
+ const { id, wtId } = req.params;
86
+ await mergeWorktreePR(id, wtId);
87
+ res.json({ ok: true });
88
+ }
89
+ catch (err) {
90
+ res.status(400).json({ error: err.message });
91
+ }
92
+ });
93
+ //# sourceMappingURL=worktree.js.map
@@ -11,7 +11,7 @@ import { extractUsageFromOutput } from '../storage/usage.js';
11
11
  const DEFAULT_AGENT_ROLE = 'agent';
12
12
  export const AGENT_GENERATOR_PRESET_ID = 'agent-generator';
13
13
  export const AGENT_COMMIT_PRESET_ID = 'commit-agent';
14
- const VALID_RUNTIME_KINDS = ['open-agent-sdk', 'claude-code', 'codex', 'langchain'];
14
+ const VALID_RUNTIME_KINDS = ['open-agent-sdk', 'claude-code', 'codex', 'langchain', 'hermes'];
15
15
  const VALID_TOOL_NAMES = new Set(BUILT_IN_AGENT_TOOLS.map((tool) => tool.name));
16
16
  const CLAUDE_BUILT_IN_DIRS = ['agents', 'commands'];
17
17
  const ANTHROPIC_BRIDGE_PROVIDERS = [
@@ -46,6 +46,15 @@ export function clearAll(workspaceId) {
46
46
  writeAll(workspaceId, []);
47
47
  broadcastToWorkspace(workspaceId, 'notification.cleared', null);
48
48
  }
49
+ export function removeNotification(workspaceId, notificationId) {
50
+ const all = readAll(workspaceId);
51
+ const idx = all.findIndex((n) => n.id === notificationId);
52
+ if (idx === -1)
53
+ return null;
54
+ const [removed] = all.splice(idx, 1);
55
+ writeAll(workspaceId, all);
56
+ return removed;
57
+ }
49
58
  export function unreadCount(workspaceId) {
50
59
  return readAll(workspaceId).filter((n) => !n.read).length;
51
60
  }
@@ -0,0 +1,59 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ let localVersion = '';
6
+ let cachedLatest = null;
7
+ const CACHE_TTL = 3600_000; // 1 hour
8
+ export function getLocalVersion() {
9
+ if (localVersion !== '')
10
+ return localVersion;
11
+ try {
12
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
13
+ localVersion = pkg.version || '0.0.0';
14
+ }
15
+ catch {
16
+ localVersion = '0.0.0';
17
+ }
18
+ return localVersion;
19
+ }
20
+ export function getCachedLatest() {
21
+ if (cachedLatest && Date.now() - cachedLatest.ts < CACHE_TTL) {
22
+ return cachedLatest.version;
23
+ }
24
+ return null;
25
+ }
26
+ export async function fetchLatestVersion(forceRefresh = false) {
27
+ if (!forceRefresh && cachedLatest && Date.now() - cachedLatest.ts < CACHE_TTL) {
28
+ return cachedLatest.version;
29
+ }
30
+ try {
31
+ const res = await fetch('https://registry.npmjs.org/@agent-spaces/server', {
32
+ signal: AbortSignal.timeout(8000),
33
+ });
34
+ if (!res.ok)
35
+ return cachedLatest?.version ?? null;
36
+ const data = await res.json();
37
+ const latest = data?.['dist-tags']?.latest;
38
+ if (latest) {
39
+ cachedLatest = { version: latest, ts: Date.now() };
40
+ }
41
+ return latest ?? cachedLatest?.version ?? null;
42
+ }
43
+ catch {
44
+ return cachedLatest?.version ?? null;
45
+ }
46
+ }
47
+ export function isNewerVersion(latest, local) {
48
+ const parse = (v) => v.replace(/^v/, '').split('.').map(Number);
49
+ const a = parse(latest);
50
+ const b = parse(local);
51
+ for (let i = 0; i < 3; i++) {
52
+ if ((a[i] || 0) > (b[i] || 0))
53
+ return true;
54
+ if ((a[i] || 0) < (b[i] || 0))
55
+ return false;
56
+ }
57
+ return false;
58
+ }
59
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1,23 @@
1
+ import { exec } from 'child_process';
2
+ import { getWorkspace } from '../storage/workspace-store.js';
3
+ export async function executeCommandNode(workspaceId, node) {
4
+ const workspace = getWorkspace(workspaceId);
5
+ const cwd = node.data.cwd || workspace?.boundDirs?.[0] || process.cwd();
6
+ return new Promise((resolve) => {
7
+ exec(node.data.script, {
8
+ cwd,
9
+ env: { ...process.env, ...node.data.env },
10
+ shell: node.data.shell || undefined,
11
+ timeout: 300_000,
12
+ maxBuffer: 10 * 1024 * 1024,
13
+ }, (error, stdout, stderr) => {
14
+ resolve({
15
+ success: !error,
16
+ exitCode: error ? (typeof error.code === 'number' ? error.code : 1) : 0,
17
+ stdout: stdout || '',
18
+ stderr: stderr || '',
19
+ });
20
+ });
21
+ });
22
+ }
23
+ //# sourceMappingURL=workflow-command-runner.js.map
@@ -72,6 +72,8 @@ function resolveStaleRoles(nodes) {
72
72
  const agentMap = new Map(listTemplates().map(a => [a.id, a]));
73
73
  const invalidIds = [];
74
74
  const resolved = nodes.map(node => {
75
+ if (node.type === 'command')
76
+ return node;
75
77
  const agent = agentMap.get(node.data.agentConfigId);
76
78
  if (!agent) {
77
79
  invalidIds.push(node.id);
@@ -177,19 +179,34 @@ export function mapWorkflowToTaskDrafts(template) {
177
179
  for (const edge of template.edges) {
178
180
  dependsOn.get(edge.target)?.push(edge.source);
179
181
  }
180
- return template.nodes.map(node => ({
181
- key: node.id,
182
- title: node.data.taskTitleTemplate || node.data.label,
183
- description: node.data.taskDescriptionTemplate || `Task assigned to ${node.data.label} (${node.data.role})`,
184
- agentConfigId: node.data.agentConfigId,
185
- dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
186
- sandboxDirs: undefined,
187
- }));
182
+ return template.nodes.map(node => {
183
+ if (node.type === 'command') {
184
+ return {
185
+ key: node.id,
186
+ title: node.data.label,
187
+ description: `Run command: ${node.data.label}`,
188
+ agentConfigId: undefined,
189
+ dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
190
+ sandboxDirs: undefined,
191
+ commandNode: node,
192
+ };
193
+ }
194
+ return {
195
+ key: node.id,
196
+ title: node.data.taskTitleTemplate || node.data.label,
197
+ description: node.data.taskDescriptionTemplate || `Task assigned to ${node.data.label} (${node.data.role})`,
198
+ agentConfigId: node.data.agentConfigId,
199
+ dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
200
+ sandboxDirs: undefined,
201
+ };
202
+ });
188
203
  }
189
204
  // --- Run-time Validation ---
190
205
  export function validateWorkflowForRun(_workspaceId, template, memberAgentIds) {
191
206
  const agentMap = new Map(listTemplates().map((a) => [a.id, a]));
192
207
  for (const node of template.nodes) {
208
+ if (node.type === 'command')
209
+ continue;
193
210
  const agent = agentMap.get(node.data.agentConfigId);
194
211
  if (!agent)
195
212
  return `Agent "${node.data.label}" (${node.data.agentConfigId}) no longer exists`;
@@ -1,5 +1,6 @@
1
1
  import { v4 as uuid } from 'uuid';
2
2
  import { listWorkspaces, getWorkspace, createWorkspace, updateWorkspace, deleteWorkspace } from '../storage/workspace-store.js';
3
+ import { listWorktrees } from '../storage/worktree-store.js';
3
4
  import { ensureDir } from '../storage/json-store.js';
4
5
  import { join } from 'node:path';
5
6
  import { existsSync, writeFileSync } from 'node:fs';
@@ -21,7 +22,14 @@ function initAgentspace(agentspaceDir) {
21
22
  }
22
23
  }
23
24
  export function getAll() {
24
- return listWorkspaces();
25
+ const workspaces = listWorkspaces();
26
+ const workspaceIds = new Set(workspaces.map((workspace) => workspace.id));
27
+ const worktreeWorkspaces = workspaces
28
+ .filter((workspace) => !workspace.isWorktree)
29
+ .flatMap((workspace) => listWorktrees(workspace.id)
30
+ .filter((worktree) => worktree.status === 'active' && !workspaceIds.has(worktree.id))
31
+ .map(worktreeToWorkspace));
32
+ return [...workspaces, ...worktreeWorkspaces];
25
33
  }
26
34
  export function getById(id) {
27
35
  return getWorkspace(id);
@@ -72,4 +80,18 @@ export function remove(id) {
72
80
  deleteWorkspace(id);
73
81
  return true;
74
82
  }
83
+ function worktreeToWorkspace(worktree) {
84
+ return {
85
+ id: worktree.id,
86
+ name: `${worktree.name} (Worktree)`,
87
+ boundDirs: [worktree.path],
88
+ agentspaceDir: join(worktree.path, '.agentspace'),
89
+ isWorktree: true,
90
+ parentWorkspaceId: worktree.workspaceId,
91
+ createdAt: worktree.createdAt,
92
+ updatedAt: worktree.updatedAt,
93
+ activeChannels: [],
94
+ activeIssues: [],
95
+ };
96
+ }
75
97
  //# sourceMappingURL=workspace.js.map