@agent-spaces/server 0.1.2 → 0.2.1

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 (216) hide show
  1. package/dist/adapters/claude-code-runtime/index.js +4 -1
  2. package/dist/adapters/git.js +2 -21
  3. package/dist/agents/issue-agent-runner.js +35 -21
  4. package/dist/agents/issue-task-controller.js +120 -73
  5. package/dist/agents/planner-agent.js +9 -12
  6. package/dist/app.js +10 -0
  7. package/dist/hooks/agent-hooks.js +5 -8
  8. package/dist/package.json +1 -1
  9. package/dist/routes/agent.js +40 -17
  10. package/dist/routes/channel.js +4 -1
  11. package/dist/routes/command.js +108 -0
  12. package/dist/routes/folder.js +44 -7
  13. package/dist/routes/issue.js +57 -23
  14. package/dist/routes/mcp.js +50 -0
  15. package/dist/routes/skill.js +57 -0
  16. package/dist/routes/subscription.js +49 -0
  17. package/dist/routes/task.js +16 -1
  18. package/dist/routes/workflow.js +63 -0
  19. package/dist/routes/workspace.js +2 -21
  20. package/dist/services/agent.js +140 -76
  21. package/dist/services/builtin-tools.js +72 -0
  22. package/dist/services/channel.js +5 -5
  23. package/dist/services/command-process-manager.js +136 -0
  24. package/dist/services/command.js +48 -0
  25. package/dist/services/issue.js +5 -6
  26. package/dist/services/mcp.js +120 -0
  27. package/dist/services/notification-hub/bot-commands.js +48 -16
  28. package/dist/services/notification-hub/helpers.js +12 -0
  29. package/dist/services/pty.js +10 -3
  30. package/dist/services/skill.js +171 -0
  31. package/dist/services/subscription/aicode.js +45 -0
  32. package/dist/services/subscription/base.js +3 -0
  33. package/dist/services/subscription/index.js +20 -0
  34. package/dist/services/subscription/minimax.js +60 -0
  35. package/dist/services/subscription/zhipu.js +39 -0
  36. package/dist/services/workflow.js +203 -0
  37. package/dist/services/workspace.js +0 -1
  38. package/dist/storage/command-store.js +17 -0
  39. package/dist/storage/subscription-store.js +44 -0
  40. package/dist/storage/workflow-store.js +40 -0
  41. package/dist/web/404.html +1 -1
  42. package/dist/web/__next.__PAGE__.txt +4 -4
  43. package/dist/web/__next._full.txt +22 -21
  44. package/dist/web/__next._head.txt +4 -4
  45. package/dist/web/__next._index.txt +9 -8
  46. package/dist/web/__next._tree.txt +2 -2
  47. package/dist/web/_next/static/MpJepsgfsHGGcc7rwFU8Y/_buildManifest.js +11 -0
  48. package/dist/web/_next/static/chunks/0-245kun-watp.js +2 -0
  49. package/dist/web/_next/static/chunks/0-ca_fo-yp3~z.js +1 -0
  50. package/dist/web/_next/static/chunks/0-vgd3j-zh3m8.js +2 -0
  51. package/dist/web/_next/static/chunks/0.4.g.8yf4rs0.js +3 -0
  52. package/dist/web/_next/static/chunks/02m_-ngl9w8co.js +1 -0
  53. package/dist/web/_next/static/chunks/03jbh7ud0jw~g.js +1 -0
  54. package/dist/web/_next/static/chunks/07kqxmubf5dua.js +1 -0
  55. package/dist/web/_next/static/chunks/088q-5_51dsrw.js +1 -0
  56. package/dist/web/_next/static/chunks/09_ki3dc5cfwv.css +1 -0
  57. package/dist/web/_next/static/chunks/09jryrjps0vl2.js +1 -0
  58. package/dist/web/_next/static/chunks/0b2tump5duj9j.js +1 -0
  59. package/dist/web/_next/static/chunks/0dctkv_2d2r0g.js +8 -0
  60. package/dist/web/_next/static/chunks/0efgv37kxyht8.js +1 -0
  61. package/dist/web/_next/static/chunks/0fwvdy-ml8wpk.js +1 -0
  62. package/dist/web/_next/static/chunks/0g4vm6.v0o_lt.js +1 -0
  63. package/dist/web/_next/static/chunks/0g_b4t~000.o~.js +179 -0
  64. package/dist/web/_next/static/chunks/0gyede80jpy3n.js +4 -0
  65. package/dist/web/_next/static/chunks/0h256h-tu4e0r.js +2 -0
  66. package/dist/web/_next/static/chunks/0ib18ul605e~a.js +1 -0
  67. package/dist/web/_next/static/chunks/0j1g_rd9t5ot1.js +1 -0
  68. package/dist/web/_next/static/chunks/0jn~llaqcxo4q.js +1 -0
  69. package/dist/web/_next/static/chunks/0jvmviuftg5e2.css +1 -0
  70. package/dist/web/_next/static/chunks/0lb8l2~6s_owo.js +1 -0
  71. package/dist/web/_next/static/chunks/0ni5ot2603_28.js +1 -0
  72. package/dist/web/_next/static/chunks/0pep4mkvt3.rh.js +31 -0
  73. package/dist/web/_next/static/chunks/0pq2670_ezbcj.js +1 -0
  74. package/dist/web/_next/static/chunks/0q_scqkk9.t53.js +2 -0
  75. package/dist/web/_next/static/chunks/0rrdur.v1a5r7.js +1 -0
  76. package/dist/web/_next/static/chunks/0spo.tmfeas-o.js +1 -0
  77. package/dist/web/_next/static/chunks/0tta_56qj1z8-.js +1 -0
  78. package/dist/web/_next/static/chunks/0u88ij9dqqh~-.js +1 -0
  79. package/dist/web/_next/static/chunks/0zl19l5tuoppw.js +1 -0
  80. package/dist/web/_next/static/chunks/11n16hogah-5..js +1 -0
  81. package/dist/web/_next/static/chunks/1250wo~-5dgyx.js +1 -0
  82. package/dist/web/_next/static/chunks/14n8i2xz4_y-e.js +1 -0
  83. package/dist/web/_next/static/chunks/160ji-.dfvm20.js +1 -0
  84. package/dist/web/_next/static/chunks/turbopack-0lxiiw.jhevml.js +1 -0
  85. package/dist/web/_next/static/media/favicon.0~ekuj.zhggpa.ico +0 -0
  86. package/dist/web/_not-found/__next._full.txt +24 -19
  87. package/dist/web/_not-found/__next._head.txt +4 -4
  88. package/dist/web/_not-found/__next._index.txt +9 -8
  89. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  90. package/dist/web/_not-found/__next._not-found.txt +3 -3
  91. package/dist/web/_not-found/__next._tree.txt +2 -2
  92. package/dist/web/_not-found.html +1 -1
  93. package/dist/web/_not-found.txt +24 -19
  94. package/dist/web/apple-touch-icon.png +0 -0
  95. package/dist/web/favicon.ico +0 -0
  96. package/dist/web/icon-192.png +0 -0
  97. package/dist/web/icon-512.png +0 -0
  98. package/dist/web/index.html +1 -1
  99. package/dist/web/index.txt +22 -21
  100. package/dist/web/login/__next._full.txt +24 -23
  101. package/dist/web/login/__next._head.txt +4 -4
  102. package/dist/web/login/__next._index.txt +9 -8
  103. package/dist/web/login/__next._tree.txt +2 -2
  104. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  105. package/dist/web/login/__next.login.txt +3 -3
  106. package/dist/web/login.html +1 -1
  107. package/dist/web/login.txt +24 -23
  108. package/dist/web/settings/__next._full.txt +34 -0
  109. package/dist/web/settings/__next._head.txt +6 -0
  110. package/dist/web/settings/__next._index.txt +11 -0
  111. package/dist/web/settings/__next._tree.txt +8 -0
  112. package/dist/web/settings/__next.settings.__PAGE__.txt +9 -0
  113. package/dist/web/settings/__next.settings.txt +7 -0
  114. package/dist/web/settings/agents/__next._full.txt +38 -0
  115. package/dist/web/settings/agents/__next._head.txt +6 -0
  116. package/dist/web/settings/agents/__next._index.txt +11 -0
  117. package/dist/web/settings/agents/__next._tree.txt +8 -0
  118. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +9 -0
  119. package/dist/web/settings/agents/__next.settings.agents.txt +5 -0
  120. package/dist/web/settings/agents/__next.settings.txt +7 -0
  121. package/dist/web/settings/agents.html +1 -0
  122. package/dist/web/settings/agents.txt +38 -0
  123. package/dist/web/settings/mcps/__next._full.txt +38 -0
  124. package/dist/web/settings/mcps/__next._head.txt +6 -0
  125. package/dist/web/settings/mcps/__next._index.txt +11 -0
  126. package/dist/web/settings/mcps/__next._tree.txt +8 -0
  127. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +9 -0
  128. package/dist/web/settings/mcps/__next.settings.mcps.txt +5 -0
  129. package/dist/web/settings/mcps/__next.settings.txt +7 -0
  130. package/dist/web/settings/mcps.html +1 -0
  131. package/dist/web/settings/mcps.txt +38 -0
  132. package/dist/web/settings/models/__next._full.txt +38 -0
  133. package/dist/web/settings/models/__next._head.txt +6 -0
  134. package/dist/web/settings/models/__next._index.txt +11 -0
  135. package/dist/web/settings/models/__next._tree.txt +8 -0
  136. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +9 -0
  137. package/dist/web/settings/models/__next.settings.models.txt +5 -0
  138. package/dist/web/settings/models/__next.settings.txt +7 -0
  139. package/dist/web/settings/models.html +1 -0
  140. package/dist/web/settings/models.txt +38 -0
  141. package/dist/web/settings/providers/__next._full.txt +38 -0
  142. package/dist/web/settings/providers/__next._head.txt +6 -0
  143. package/dist/web/settings/providers/__next._index.txt +11 -0
  144. package/dist/web/settings/providers/__next._tree.txt +8 -0
  145. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +9 -0
  146. package/dist/web/settings/providers/__next.settings.providers.txt +5 -0
  147. package/dist/web/settings/providers/__next.settings.txt +7 -0
  148. package/dist/web/settings/providers.html +1 -0
  149. package/dist/web/settings/providers.txt +38 -0
  150. package/dist/web/settings/skills/__next._full.txt +38 -0
  151. package/dist/web/settings/skills/__next._head.txt +6 -0
  152. package/dist/web/settings/skills/__next._index.txt +11 -0
  153. package/dist/web/settings/skills/__next._tree.txt +8 -0
  154. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +9 -0
  155. package/dist/web/settings/skills/__next.settings.skills.txt +5 -0
  156. package/dist/web/settings/skills/__next.settings.txt +7 -0
  157. package/dist/web/settings/skills.html +1 -0
  158. package/dist/web/settings/skills.txt +38 -0
  159. package/dist/web/settings.html +1 -0
  160. package/dist/web/settings.txt +34 -0
  161. package/dist/web/workflows/__next._full.txt +35 -0
  162. package/dist/web/workflows/__next._head.txt +6 -0
  163. package/dist/web/workflows/__next._index.txt +11 -0
  164. package/dist/web/workflows/__next._tree.txt +9 -0
  165. package/dist/web/workflows/__next.workflows.__PAGE__.txt +10 -0
  166. package/dist/web/workflows/__next.workflows.txt +5 -0
  167. package/dist/web/workflows.html +1 -0
  168. package/dist/web/workflows.txt +35 -0
  169. package/dist/web/workspace/_/__next._full.txt +25 -20
  170. package/dist/web/workspace/_/__next._head.txt +4 -4
  171. package/dist/web/workspace/_/__next._index.txt +9 -8
  172. package/dist/web/workspace/_/__next._tree.txt +2 -2
  173. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  174. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  175. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  176. package/dist/web/workspace/_.html +1 -1
  177. package/dist/web/workspace/_.txt +25 -20
  178. package/dist/web/workspaces/__next._full.txt +24 -23
  179. package/dist/web/workspaces/__next._head.txt +4 -4
  180. package/dist/web/workspaces/__next._index.txt +9 -8
  181. package/dist/web/workspaces/__next._tree.txt +2 -2
  182. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  183. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  184. package/dist/web/workspaces.html +1 -1
  185. package/dist/web/workspaces.txt +24 -23
  186. package/dist/ws/agent-prompt.js +84 -0
  187. package/dist/ws/agent-runner.js +592 -0
  188. package/dist/ws/handler.js +9 -1200
  189. package/dist/ws/html-utils.js +30 -0
  190. package/dist/ws/message-parts.js +498 -0
  191. package/package.json +3 -2
  192. package/dist/web/_next/static/chunks/038whpa0zpnas.js +0 -1
  193. package/dist/web/_next/static/chunks/06q5go~xoz5a3.js +0 -1
  194. package/dist/web/_next/static/chunks/095.wizobwtyg.js +0 -2
  195. package/dist/web/_next/static/chunks/0bfg.w~u-83h5.js +0 -1
  196. package/dist/web/_next/static/chunks/0ce7i6~sb20rv.js +0 -113
  197. package/dist/web/_next/static/chunks/0d4~pcva1uk-a.js +0 -1
  198. package/dist/web/_next/static/chunks/0iq70n_u75nyu.js +0 -1
  199. package/dist/web/_next/static/chunks/0memz-8zsbxpu.js +0 -1
  200. package/dist/web/_next/static/chunks/0nw~w.3~twebx.js +0 -2
  201. package/dist/web/_next/static/chunks/0pyytgz~5vt0f.js +0 -31
  202. package/dist/web/_next/static/chunks/0ujjcrz~1nq.q.css +0 -1
  203. package/dist/web/_next/static/chunks/0vdnx9n41dyjl.js +0 -1
  204. package/dist/web/_next/static/chunks/0yl4mqmxtll6g.js +0 -1
  205. package/dist/web/_next/static/chunks/0zcbfka5tcle3.js +0 -1
  206. package/dist/web/_next/static/chunks/0~eo58u99i.fr.js +0 -8
  207. package/dist/web/_next/static/chunks/13_2vxyccsv84.js +0 -4
  208. package/dist/web/_next/static/chunks/13wfs~urgxu0w.js +0 -1
  209. package/dist/web/_next/static/chunks/157~3c6wqkt83.js +0 -1
  210. package/dist/web/_next/static/chunks/15jvow_z4uq-..js +0 -1
  211. package/dist/web/_next/static/chunks/15v697nf~r-cy.js +0 -2
  212. package/dist/web/_next/static/chunks/turbopack-164~7ulq9o9yc.js +0 -1
  213. package/dist/web/_next/static/media/favicon.0x3dzn~oxb6tn.ico +0 -0
  214. package/dist/web/_next/static/owMnKmxATbtXK_J_k_uHh/_buildManifest.js +0 -21
  215. /package/dist/web/_next/static/{owMnKmxATbtXK_J_k_uHh → MpJepsgfsHGGcc7rwFU8Y}/_clientMiddlewareManifest.js +0 -0
  216. /package/dist/web/_next/static/{owMnKmxATbtXK_J_k_uHh → MpJepsgfsHGGcc7rwFU8Y}/_ssgManifest.js +0 -0
@@ -31,8 +31,11 @@ export class ClaudeCodeRuntime {
31
31
  const apiKey = this.adapterRun ? 'default' : this.config.apiKey;
32
32
  const model = getClaudeCodeModel(this.config);
33
33
  const additionalDirectories = normalizeAdditionalDirectories(cwd, options?.sandboxDirs);
34
+ const sdkMcpServers = normalizeMcpServers(options?.mcpServers, options?.functionTools);
35
+ const sdkMcpServerNames = Object.keys(sdkMcpServers ?? {});
34
36
  d(`starting | cwd=${cwd} model=${model ?? 'default'} targetModel=${this.config.model ?? 'default'} provider=${this.config.provider ?? 'default'} baseURL=${baseURL ?? 'default'} permissionMode=${permissionMode} maxTurns=${options?.maxTurns ?? '∞'} tools=claude_code mcpServers=${Object.keys(options?.mcpServers ?? {}).join(',') || '-'} skills=${skillNames.join(',') || '-'} configDir=${configDir ?? 'default'} sandboxDirs=${additionalDirectories.join(',') || '-'} claudeExecutable=${claudeExecutable ?? 'sdk-default'}`);
35
37
  d(`prompt: ${prompt.slice(0, 300)}${prompt.length > 300 ? '...' : ''}`);
38
+ d(`sdk mcp servers | ${sdkMcpServerNames.join(',') || '-'}`);
36
39
  try {
37
40
  const queryOptions = {
38
41
  cwd,
@@ -40,7 +43,7 @@ export class ClaudeCodeRuntime {
40
43
  maxTurns: options?.maxTurns,
41
44
  pathToClaudeCodeExecutable: claudeExecutable,
42
45
  tools: { type: 'preset', preset: 'claude_code' },
43
- mcpServers: normalizeMcpServers(options?.mcpServers, options?.functionTools),
46
+ mcpServers: sdkMcpServers,
44
47
  skills: skillNames,
45
48
  managedSettings: {
46
49
  strictPluginOnlyCustomization: ['mcp'],
@@ -18,18 +18,6 @@ function getGit(workspace) {
18
18
  if (existing)
19
19
  return existing;
20
20
  const git = simpleGit(workspace.boundDirs[0], getGitOptions());
21
- git.outputHandler((_binary, stdout, stderr) => {
22
- stderr.on('data', (chunk) => {
23
- const text = chunk.toString().trim();
24
- if (text)
25
- console.log(`[git:${workspace.id}] ${text}`);
26
- });
27
- stdout.on('data', (chunk) => {
28
- const text = chunk.toString().trim();
29
- if (text)
30
- console.log(`[git:${workspace.id}] ${text}`);
31
- });
32
- });
33
21
  gitInstances.set(workspace.id, git);
34
22
  return git;
35
23
  }
@@ -288,19 +276,12 @@ export async function gitClone(targetDir, url, onProgress) {
288
276
  throw new Error(`目录 ${cloneDir} 已存在`);
289
277
  }
290
278
  const git = simpleGit(getGitOptions());
291
- git.outputHandler((_binary, stdout, stderr) => {
279
+ git.outputHandler((_binary, _stdout, stderr) => {
292
280
  stderr.on('data', (chunk) => {
293
- const text = chunk.toString();
294
- console.log(`[git:clone] ${text.trim()}`);
295
- const parsed = parseCloneProgress(text);
281
+ const parsed = parseCloneProgress(chunk.toString());
296
282
  if (parsed)
297
283
  onProgress(parsed);
298
284
  });
299
- stdout.on('data', (chunk) => {
300
- const text = chunk.toString().trim();
301
- if (text)
302
- console.log(`[git:clone] ${text}`);
303
- });
304
285
  });
305
286
  await git.clone(url, cloneDir, ['--progress']);
306
287
  return cloneDir;
@@ -1,38 +1,52 @@
1
- import { runPlanner } from './planner-agent.js';
2
- import { syncIssueTasksAfterPlanning } from './issue-task-controller.js';
1
+ import { scheduleRunnableIssueTasks, createTasksFromWorkflow } from './issue-task-controller.js';
3
2
  import * as issueService from '../services/issue.js';
4
- import * as issueCommentService from '../services/issue-comment.js';
5
3
  import * as taskService from '../services/task.js';
6
4
  import * as agentService from '../services/agent.js';
7
- export async function runIssueAutomation(workspaceId, issueId, ctx, options = {}) {
5
+ import * as workflowService from '../services/workflow.js';
6
+ export async function runIssueAutomation(workspaceId, issueId, ctx) {
8
7
  const issue = issueService.getById(workspaceId, issueId);
9
8
  if (!issue) {
10
9
  console.warn(`[issue-runner] issue not found workspaceId=${workspaceId} issueId=${issueId}`);
11
10
  return;
12
11
  }
12
+ // Workflow template branch
13
+ if (issue.workflowId) {
14
+ const template = workflowService.getWorkflow(issue.workflowId);
15
+ if (template) {
16
+ try {
17
+ createTasksFromWorkflow(workspaceId, issueId, template, ctx);
18
+ return;
19
+ }
20
+ catch (err) {
21
+ console.warn(`Workflow execution failed for issue ${issueId}: ${err.message}.`);
22
+ markIssueError(workspaceId, issueId, `Workflow execution failed: ${err.message}`, ctx);
23
+ return;
24
+ }
25
+ }
26
+ else {
27
+ console.warn(`Workflow template ${issue.workflowId} not found`);
28
+ markIssueError(workspaceId, issueId, `Workflow template ${issue.workflowId} not found`, ctx);
29
+ return;
30
+ }
31
+ }
13
32
  const tasks = taskService.list(workspaceId, issueId);
14
- const shouldPlan = options.forcePlanner || tasks.length === 0;
15
- if (shouldPlan) {
16
- await runPlanner(workspaceId, issueId, ctx);
33
+ if (tasks.length > 0) {
34
+ await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
17
35
  return;
18
36
  }
19
- await syncIssueTasksAfterPlanning(workspaceId, issueId, {
20
- planSummary: latestUserCommentSummary(workspaceId, issueId),
21
- planOutput: [],
22
- }, ctx);
23
- }
24
- export function shouldForcePlannerFromMentions(workspaceId, mentions) {
25
- const mentionedIds = new Set(mentions);
26
- return (agentService.listPresets(workspaceId) ?? []).some((agent) => mentionedIds.has(agent.id) && agent.role === 'planner' && agent.enabled !== false);
37
+ console.warn(`[issue-runner] no workflow configured for issue workspaceId=${workspaceId} issueId=${issueId}`);
38
+ markIssueError(workspaceId, issueId, 'No workflow configured for issue', ctx);
27
39
  }
28
40
  export function hasActiveIssueAutomation(workspaceId) {
29
41
  return agentService.list(workspaceId).some((session) => session.status === 'active' &&
30
- ['planner', 'custom', 'executor', 'reviewer'].includes(session.role));
42
+ !['scheduler', 'bot'].includes(session.role));
31
43
  }
32
- function latestUserCommentSummary(workspaceId, issueId) {
33
- const latest = [...issueCommentService.listIssueComments(workspaceId, issueId)]
34
- .reverse()
35
- .find((comment) => comment.senderId === 'user');
36
- return latest?.content ?? '';
44
+ function markIssueError(workspaceId, issueId, message, ctx) {
45
+ const issue = issueService.getById(workspaceId, issueId);
46
+ const updated = issueService.markError(workspaceId, issueId, message);
47
+ if (!updated)
48
+ return;
49
+ ctx.broadcast('issue.status_changed', { issueId, from: issue?.status ?? 'draft', to: 'error' });
50
+ ctx.broadcast('issue.updated', updated);
37
51
  }
38
52
  //# sourceMappingURL=issue-agent-runner.js.map
@@ -3,7 +3,8 @@ import * as agentService from '../services/agent.js';
3
3
  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
- import { onExecutorComplete } from '../hooks/agent-hooks.js';
6
+ import * as workspaceService from '../services/workspace.js';
7
+ import { mapWorkflowToTaskDrafts, validateWorkflowForRun } from '../services/workflow.js';
7
8
  import { createIssueFunctionTools } from '../services/builtin-tools.js';
8
9
  import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
9
10
  import { completeIssueAgentProgress, createIssueAgentProgress, createIssueAgentProgressTracker } from './issue-agent-progress.js';
@@ -14,9 +15,9 @@ export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, c
14
15
  console.warn(`[issue-task-controller] issue not found workspaceId=${workspaceId} issueId=${issueId}`);
15
16
  return;
16
17
  }
17
- const taskSyncPreset = findTaskCreatorForIssue(workspaceId, issue, input.plannerPreset);
18
+ const taskSyncPreset = findTaskSyncAgentForIssue(workspaceId, issue, input.plannerPreset);
18
19
  if (!taskSyncPreset) {
19
- console.warn(`[issue-task-controller] no task creator or executor member found workspaceId=${workspaceId} issueId=${issueId}`);
20
+ console.warn(`[issue-task-controller] no task sync agent member found workspaceId=${workspaceId} issueId=${issueId}`);
20
21
  updateIssueStatus(workspaceId, issueId, 'error', ctx);
21
22
  return;
22
23
  }
@@ -28,7 +29,7 @@ export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, c
28
29
  ctx.broadcast('agent.status_changed', { agentId: taskSyncAgent.id, from: fromStatus, to: 'active' });
29
30
  ctx.broadcast('agent.output', { agentId: taskSyncAgent.id, data: `Syncing issue tasks: ${issueId}` });
30
31
  const startTime = Date.now();
31
- const taskSyncWorkingDir = agentService.resolveWorkingDir(workspaceId, taskSyncPreset);
32
+ const taskSyncWorkingDir = resolveIssueWorkspaceRoot(workspaceId);
32
33
  const progress = createIssueAgentProgress(workspaceId, issue, taskSyncPreset, taskSyncAgent.id, {
33
34
  runtime: taskSyncPreset.runtimeKind,
34
35
  model: taskSyncPreset.modelId,
@@ -122,13 +123,16 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
122
123
  console.warn(`[issue-task-controller] missing issue/task workspaceId=${workspaceId} issueId=${issueId} taskId=${taskId}`);
123
124
  return;
124
125
  }
125
- const executorPreset = findExecutorForTask(workspaceId, issue, task);
126
- if (!executorPreset) {
126
+ if (task.status !== 'pending') {
127
+ return;
128
+ }
129
+ const taskAgentPreset = findAgentForTask(workspaceId, issue, task);
130
+ if (!taskAgentPreset) {
127
131
  const missingExecutorResult = {
128
132
  success: false,
129
- summary: 'No executor agent configured in issue channel members',
133
+ summary: 'No runnable agent configured in issue channel members',
130
134
  artifacts: [],
131
- error: 'No executor member found for issue channel',
135
+ error: 'No runnable agent member found for issue channel',
132
136
  };
133
137
  const failed = taskService.updateStatus(workspaceId, taskId, 'failed', {
134
138
  result: {
@@ -142,69 +146,69 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
142
146
  await handleTaskFailure(workspaceId, issueId, taskId, missingExecutorResult, ctx);
143
147
  return;
144
148
  }
145
- const executor = agentService.getOrCreateSessionForConfig(workspaceId, executorPreset);
146
- ctx.broadcast('agent.started', executor);
147
- const executorFromStatus = executor.status;
148
- agentService.updateStatus(workspaceId, executor.id, 'active');
149
- issueService.addAgent(workspaceId, issueId, executorPreset.id);
150
- ctx.broadcast('agent.status_changed', { agentId: executor.id, from: executorFromStatus, to: 'active' });
151
- const runningTask = taskService.assignAgent(workspaceId, taskId, executor.id);
149
+ const taskAgent = agentService.getOrCreateSessionForConfig(workspaceId, taskAgentPreset);
150
+ ctx.broadcast('agent.started', taskAgent);
151
+ const agentFromStatus = taskAgent.status;
152
+ agentService.updateStatus(workspaceId, taskAgent.id, 'active');
153
+ issueService.addAgent(workspaceId, issueId, taskAgentPreset.id);
154
+ ctx.broadcast('agent.status_changed', { agentId: taskAgent.id, from: agentFromStatus, to: 'active' });
155
+ const runningTask = taskService.assignAgent(workspaceId, taskId, taskAgent.id);
152
156
  if (!runningTask) {
153
- agentService.complete(workspaceId, executor.id, 'Task not found');
157
+ agentService.complete(workspaceId, taskAgent.id, 'Task not found');
154
158
  return;
155
159
  }
156
160
  broadcastTaskUpdate(ctx, runningTask, task.status);
157
- agentService.assignTask(workspaceId, executor.id, taskId);
158
- const runtime = createRuntimeForPreset(executorPreset);
159
- const executorWorkingDir = agentService.resolveWorkingDir(workspaceId, executorPreset);
161
+ agentService.assignTask(workspaceId, taskAgent.id, taskId);
162
+ const runtime = createRuntimeForPreset(taskAgentPreset);
163
+ const agentWorkingDir = resolveIssueWorkspaceRoot(workspaceId);
160
164
  const startTime = Date.now();
161
- const progress = createIssueAgentProgress(workspaceId, issue, executorPreset, executor.id, {
162
- runtime: executorPreset.runtimeKind,
163
- model: executorPreset.modelId,
165
+ const progress = createIssueAgentProgress(workspaceId, issue, taskAgentPreset, taskAgent.id, {
166
+ runtime: taskAgentPreset.runtimeKind,
167
+ model: taskAgentPreset.modelId,
164
168
  taskId,
165
- phase: 'executor',
169
+ phase: taskAgentPreset.role,
166
170
  });
167
- const executorTracker = createIssueAgentProgressTracker({
171
+ const agentTracker = createIssueAgentProgressTracker({
168
172
  workspaceId,
169
173
  issue,
170
174
  progress,
171
- agentSessionId: executor.id,
172
- workspaceRoot: executorWorkingDir,
175
+ agentSessionId: taskAgent.id,
176
+ workspaceRoot: agentWorkingDir,
173
177
  onOutput: (line) => {
174
- ctx.broadcast('agent.output', { agentId: executor.id, data: line });
178
+ ctx.broadcast('agent.output', { agentId: taskAgent.id, data: line });
175
179
  ctx.broadcast('task.output', { taskId, data: line });
176
180
  },
177
181
  });
178
- ctx.broadcast('agent.output', { agentId: executor.id, data: `Executing task: ${runningTask.title}` });
182
+ ctx.broadcast('agent.output', { agentId: taskAgent.id, data: `Executing task: ${runningTask.title}` });
179
183
  ctx.broadcast('agent.output', {
180
- agentId: executor.id,
181
- data: `[debug] executor agentConfigId=${executorPreset.id} taskAgentConfigId=${runningTask.agentConfigId || '(fallback)'} runtime=${executorPreset.runtimeKind ?? 'open-agent-sdk'}`,
184
+ agentId: taskAgent.id,
185
+ data: `[debug] workflow agentConfigId=${taskAgentPreset.id} role=${taskAgentPreset.role} taskAgentConfigId=${runningTask.agentConfigId || '(fallback)'} runtime=${taskAgentPreset.runtimeKind ?? 'open-agent-sdk'}`,
182
186
  });
183
- const result = await runtime.execute(buildExecutorPrompt(issue, runningTask, executorWorkingDir), executorWorkingDir, {
187
+ const result = await runtime.execute(buildTaskAgentPrompt(issue, runningTask, taskAgentPreset, agentWorkingDir), agentWorkingDir, {
184
188
  maxTurns: 100,
185
- mcpServers: agentService.getMcpServers(executorPreset.mcps),
186
- functionTools: createCurrentIssueTools(workspaceId, issue, executorPreset),
187
- skills: agentService.getAvailableSkillNames(agentService.getAgentConfigDir(workspaceId, executorPreset), executorPreset.skills),
188
- configDir: agentService.getAgentConfigDir(workspaceId, executorPreset),
189
- sandboxDirs: runningTask.sandboxDirs ?? executorPreset.sandboxDirs,
190
- onEvent: executorTracker.handleEvent,
189
+ mcpServers: agentService.getMcpServers(taskAgentPreset.mcps),
190
+ functionTools: createCurrentIssueTools(workspaceId, issue, taskAgentPreset),
191
+ skills: agentService.getAvailableSkillNames(agentService.getAgentConfigDir(workspaceId, taskAgentPreset), taskAgentPreset.skills),
192
+ configDir: agentService.getAgentConfigDir(workspaceId, taskAgentPreset),
193
+ sandboxDirs: runningTask.sandboxDirs ?? taskAgentPreset.sandboxDirs,
194
+ onEvent: agentTracker.handleEvent,
191
195
  });
192
- if (executorTracker.output.length === 0) {
196
+ if (agentTracker.output.length === 0) {
193
197
  for (const line of result.output) {
194
- executorTracker.output.push(line);
195
- ctx.broadcast('agent.output', { agentId: executor.id, data: line });
198
+ agentTracker.output.push(line);
199
+ ctx.broadcast('agent.output', { agentId: taskAgent.id, data: line });
196
200
  ctx.broadcast('task.output', { taskId, data: line });
197
201
  }
198
202
  }
199
- completeIssueAgentProgress(workspaceId, issue, progress, result.summary, executorTracker.output, {
200
- runtime: executorPreset.runtimeKind,
201
- model: executorPreset.modelId,
203
+ completeIssueAgentProgress(workspaceId, issue, progress, result.summary, agentTracker.output, {
204
+ runtime: taskAgentPreset.runtimeKind,
205
+ model: taskAgentPreset.modelId,
202
206
  duration: Date.now() - startTime,
203
207
  messageStatus: result.success ? 'completed' : 'error',
204
- parts: executorTracker.buildParts({
205
- sessionId: executor.id,
206
- workspaceRoot: executorWorkingDir,
207
- model: executorPreset.modelId,
208
+ parts: agentTracker.buildParts({
209
+ sessionId: taskAgent.id,
210
+ workspaceRoot: agentWorkingDir,
211
+ model: taskAgentPreset.modelId,
208
212
  usage: result.usage,
209
213
  success: result.success,
210
214
  error: result.error,
@@ -214,21 +218,22 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
214
218
  const failedTask = taskService.updateStatus(workspaceId, taskId, 'failed', { result });
215
219
  broadcastTaskUpdate(ctx, failedTask, 'running');
216
220
  }
217
- agentService.complete(workspaceId, executor.id, result.success ? undefined : result.error || result.summary, {
218
- runtime: executorPreset.runtimeKind,
219
- model: executorPreset.modelId,
221
+ agentService.complete(workspaceId, taskAgent.id, result.success ? undefined : result.error || result.summary, {
222
+ runtime: taskAgentPreset.runtimeKind,
223
+ model: taskAgentPreset.modelId,
220
224
  summary: result.summary,
221
- output: executorTracker.output.length ? executorTracker.output : result.output,
225
+ output: agentTracker.output.length ? agentTracker.output : result.output,
222
226
  durationMs: Date.now() - startTime,
223
227
  usage: result.usage,
224
228
  costUsd: result.costUsd,
225
229
  });
226
- ctx.broadcast('agent.completed', { agentId: executor.id, result });
230
+ ctx.broadcast('agent.completed', { agentId: taskAgent.id, result });
227
231
  if (!result.success) {
228
232
  await handleTaskFailure(workspaceId, issueId, taskId, result, ctx);
229
233
  return;
230
234
  }
231
- await onExecutorComplete(workspaceId, taskId, issueId, result, ctx);
235
+ const completedTask = taskService.complete(workspaceId, taskId, result);
236
+ broadcastTaskUpdate(ctx, completedTask, 'running');
232
237
  await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
233
238
  }
234
239
  export async function continueIssueTaskScheduling(workspaceId, issueId, ctx) {
@@ -274,7 +279,7 @@ function createTaskSyncTools(workspaceId, issue, preset, ctx) {
274
279
  },
275
280
  ];
276
281
  }
277
- function replaceIssueTasksFromDrafts(workspaceId, issueId, ctx, drafts) {
282
+ export function replaceIssueTasksFromDrafts(workspaceId, issueId, ctx, drafts) {
278
283
  const issue = requireIssue(workspaceId, issueId);
279
284
  const validAgentIds = new Set(getIssueMemberPresets(workspaceId, issue).map((agent) => agent.id));
280
285
  const normalizedDrafts = drafts.map((task) => ({
@@ -307,25 +312,62 @@ function replaceIssueTasksFromDrafts(workspaceId, issueId, ctx, drafts) {
307
312
  ctx.broadcast('issue.updated', issueTasks);
308
313
  return updated;
309
314
  }
310
- function findExecutorForTask(workspaceId, issue, task) {
311
- const assignable = getIssueMemberPresets(workspaceId, issue);
312
- if (task.agentConfigId) {
313
- const assigned = assignable.find((agent) => agent.id === task.agentConfigId && agent.role === 'executor');
314
- if (assigned)
315
- return assigned;
315
+ export function createTasksFromWorkflow(workspaceId, issueId, template, ctx) {
316
+ const issue = requireIssue(workspaceId, issueId);
317
+ ensureWorkflowAgentsForRun(workspaceId, issue, template, ctx);
318
+ // Validate all agents exist, are enabled, and are in channel members
319
+ const channel = channelService.getChannel(workspaceId, issue.channelId);
320
+ const memberAgentIds = new Set(channel?.members ?? []);
321
+ const validationError = validateWorkflowForRun(workspaceId, template, memberAgentIds);
322
+ if (validationError) {
323
+ throw new Error(`Workflow validation failed: ${validationError}`);
316
324
  }
317
- return assignable.find((agent) => agent.role === 'executor') ?? null;
318
- }
319
- function findTaskCreatorForIssue(workspaceId, issue, plannerPreset) {
320
- return findIssueMemberAgent(workspaceId, issue, 'custom')
321
- ?? plannerPreset
322
- ?? findIssueMemberAgent(workspaceId, issue, 'executor');
325
+ const drafts = mapWorkflowToTaskDrafts(template);
326
+ issueService.updateStatus(workspaceId, issueId, 'planned');
327
+ ctx.broadcast('issue.status_changed', { issueId, oldStatus: issue.status, newStatus: 'planned' });
328
+ replaceIssueTasksFromDrafts(workspaceId, issueId, ctx, drafts);
329
+ issueService.updateStatus(workspaceId, issueId, 'in_progress');
330
+ ctx.broadcast('issue.status_changed', { issueId, oldStatus: 'planned', newStatus: 'in_progress' });
331
+ ctx.broadcast('issue.updated', issueService.getById(workspaceId, issueId));
332
+ scheduleRunnableIssueTasks(workspaceId, issueId, ctx).catch((err) => {
333
+ console.error(`[workflow] task scheduling failed for issue ${issueId}:`, err);
334
+ });
323
335
  }
324
- function findIssueMemberAgent(workspaceId, issue, role) {
336
+ function ensureWorkflowAgentsForRun(workspaceId, issue, template, ctx) {
337
+ const workflowAgentIds = [...new Set(template.nodes.map((node) => node.data.agentConfigId).filter(Boolean))];
338
+ const mergedMembers = [...new Set([...(issue.members ?? []), ...workflowAgentIds])];
339
+ const membersChanged = mergedMembers.length !== (issue.members ?? []).length
340
+ || mergedMembers.some((member, index) => member !== issue.members?.[index]);
341
+ if (membersChanged) {
342
+ issue.members = mergedMembers;
343
+ const savedIssue = issueService.save(workspaceId, issue);
344
+ ctx.broadcast('issue.updated', savedIssue);
345
+ }
325
346
  const channel = channelService.getChannel(workspaceId, issue.channelId);
326
- if (!channel)
347
+ const mergedChannelMembers = [...new Set([...(channel?.members ?? []), ...workflowAgentIds])];
348
+ const channelMembersChanged = mergedChannelMembers.length !== (channel?.members ?? []).length
349
+ || mergedChannelMembers.some((member, index) => member !== channel?.members?.[index]);
350
+ if (channelMembersChanged) {
351
+ const updatedChannel = channelService.updateChannel(workspaceId, issue.channelId, { members: mergedChannelMembers });
352
+ if (updatedChannel)
353
+ ctx.broadcast('channel.updated', updatedChannel);
354
+ }
355
+ }
356
+ function findAgentForTask(workspaceId, issue, task) {
357
+ const assignable = getIssueMemberPresets(workspaceId, issue);
358
+ if (!task.agentConfigId)
327
359
  return null;
328
- return agentService.findEnabledPresetByRoleInMembers(workspaceId, channel.members, role);
360
+ return assignable.find((agent) => agent.id === task.agentConfigId) ?? null;
361
+ }
362
+ function findTaskSyncAgentForIssue(workspaceId, issue, plannerPreset) {
363
+ const assignable = getIssueMemberPresets(workspaceId, issue);
364
+ const findByRole = (role) => assignable.find((agent) => agent.role === role) ?? null;
365
+ return plannerPreset
366
+ ?? findByRole('task_creator')
367
+ ?? findByRole('agent')
368
+ ?? findByRole('custom')
369
+ ?? findByRole('executor')
370
+ ?? assignable.find((agent) => !['scheduler', 'bot'].includes(agent.role)) ?? null;
329
371
  }
330
372
  function getIssueMemberPresets(workspaceId, issue) {
331
373
  const channel = channelService.getChannel(workspaceId, issue.channelId);
@@ -390,6 +432,9 @@ function createRuntimeForPreset(preset) {
390
432
  ...getThinkingRuntimeConfig(preset),
391
433
  });
392
434
  }
435
+ function resolveIssueWorkspaceRoot(workspaceId) {
436
+ return workspaceService.getById(workspaceId)?.boundDirs?.[0] || process.cwd();
437
+ }
393
438
  function buildTaskSyncPrompt(issue, input, workingDir) {
394
439
  return [
395
440
  'You are the issue task synchronization controller.',
@@ -400,9 +445,9 @@ function buildTaskSyncPrompt(issue, input, workingDir) {
400
445
  'Use ReplaceIssueTasks to write tasks. Do not rely on private planner-only context.',
401
446
  'Create coarse-grained, independently deliverable tasks. Default to a single implementation task for one cohesive issue.',
402
447
  'NEVER create review/audit/审查 tasks. The review phase is handled automatically by the system after all implementation tasks complete — do not include it as a task.',
403
- 'Split into multiple tasks only when the issue clearly requires major cross-area work, such as separate frontend and backend changes, database/API contract changes plus UI changes, or independent workstreams that different executors can complete without stepping on each other.',
448
+ 'Split into multiple tasks only when the issue clearly requires major cross-area work, such as separate frontend and backend changes, database/API contract changes plus UI changes, or independent workstreams that different agents can complete without stepping on each other.',
404
449
  'Do not split by tiny implementation steps such as "update types", "add route", "adjust UI text", "run tests", or "write docs" unless that item is itself a substantial deliverable.',
405
- 'Each task description should include the relevant implementation scope and expected verification, so executor agents do not need a separate task for every small step.',
450
+ 'Each task description should include the relevant implementation scope and expected verification, so agents do not need a separate task for every small step.',
406
451
  'Assign only valid agentConfigId values from ViewCurrentChannelIssue.validAgentConfigIds. Express task dependencies with dependsOnKeys only when one task truly cannot start before another completes.',
407
452
  '',
408
453
  'Current issue:',
@@ -417,8 +462,9 @@ function buildTaskSyncPrompt(issue, input, workingDir) {
417
462
  input.planOutput?.join('\n').trim(),
418
463
  ].filter(Boolean).join('\n');
419
464
  }
420
- function buildExecutorPrompt(issue, task, workingDir) {
465
+ function buildTaskAgentPrompt(issue, task, preset, workingDir) {
421
466
  return [
467
+ preset.systemPrompt?.trim(),
422
468
  'Before executing, call ViewCurrentChannelIssue with the current channel id to load the latest shared issue context and comments.',
423
469
  `The current workspace working directory is: ${workingDir}`,
424
470
  'Create and modify project files under this working directory. Do not place deliverables in /tmp unless the task explicitly asks for temporary scratch output.',
@@ -432,9 +478,10 @@ function buildExecutorPrompt(issue, task, workingDir) {
432
478
  '',
433
479
  'Current task:',
434
480
  `- Task id: ${task.id}`,
481
+ `- Assigned agent: ${preset.name} (${preset.role})`,
435
482
  `- Title: ${task.title}`,
436
483
  `- Description: ${task.description}`,
437
- ].join('\n');
484
+ ].filter(Boolean).join('\n');
438
485
  }
439
486
  function createCurrentIssueTools(workspaceId, issue, preset) {
440
487
  const channel = channelService.getChannel(workspaceId, issue.channelId);
@@ -6,7 +6,6 @@ import * as issueService from '../services/issue.js';
6
6
  import { createAgentRuntime } from '../adapters/agent-runtime.js';
7
7
  import * as wsService from '../services/workspace.js';
8
8
  import * as channelService from '../services/channel.js';
9
- import { syncIssueTasksAfterPlanning } from './issue-task-controller.js';
10
9
  import { completeIssueAgentProgress, createIssueAgentProgress, createIssueAgentProgressTracker } from './issue-agent-progress.js';
11
10
  import { createIssueFunctionTools } from '../services/builtin-tools.js';
12
11
  import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
@@ -18,11 +17,8 @@ export async function runPlanner(workspaceId, issueId, ctx) {
18
17
  }
19
18
  const plannerPreset = findIssueMemberAgent(workspaceId, issue, 'planner');
20
19
  if (!plannerPreset) {
21
- console.warn(`[planner] no planner member found; continuing with task creator workspaceId=${workspaceId} issueId=${issueId} channelId=${issue.channelId}`);
22
- await syncIssueTasksAfterPlanning(workspaceId, issueId, {
23
- planSummary: '',
24
- planOutput: [],
25
- }, ctx);
20
+ console.warn(`[planner] no planner member found workspaceId=${workspaceId} issueId=${issueId} channelId=${issue.channelId}`);
21
+ markIssueError(workspaceId, issueId, issue.status, 'Planner agent is not configured', ctx);
26
22
  return;
27
23
  }
28
24
  const planner = agentService.getOrCreateSessionForConfig(workspaceId, plannerPreset);
@@ -105,12 +101,13 @@ export async function runPlanner(workspaceId, issueId, ctx) {
105
101
  ctx.broadcast('issue.updated', erroredIssue);
106
102
  return;
107
103
  }
108
- await syncIssueTasksAfterPlanning(workspaceId, issueId, {
109
- plannerPreset,
110
- plannerSessionId: planner.id,
111
- planSummary: planResult.summary,
112
- planOutput: tracker.output.length ? tracker.output : planResult.output,
113
- }, ctx);
104
+ markIssueError(workspaceId, issueId, 'planned', 'Planner pipeline is disabled; issue automation is workflow-driven', ctx);
105
+ }
106
+ function markIssueError(workspaceId, issueId, from, message, ctx) {
107
+ const erroredIssue = issueService.markError(workspaceId, issueId, message);
108
+ ctx.broadcast('issue.status_changed', { issueId, from, to: 'error' });
109
+ if (erroredIssue)
110
+ ctx.broadcast('issue.updated', erroredIssue);
114
111
  }
115
112
  function findIssueMemberAgent(workspaceId, issue, role) {
116
113
  const channel = channelService.getChannel(workspaceId, issue.channelId);
package/dist/app.js CHANGED
@@ -13,12 +13,17 @@ import workspaceRouter from './routes/workspace.js';
13
13
  import fileRouter from './routes/file.js';
14
14
  import channelRouter from './routes/channel.js';
15
15
  import issueRouter from './routes/issue.js';
16
+ import workflowRouter from './routes/workflow.js';
16
17
  import agentRouter from './routes/agent.js';
17
18
  import taskRouter from './routes/task.js';
18
19
  import gitRouter from './routes/git.js';
19
20
  import llmRouter from './routes/llm.js';
20
21
  import authRouter from './routes/auth.js';
21
22
  import folderRouter from './routes/folder.js';
23
+ import commandRouter from './routes/command.js';
24
+ import skillRouter from './routes/skill.js';
25
+ import mcpRouter from './routes/mcp.js';
26
+ import subscriptionRouter from './routes/subscription.js';
22
27
  import { authMiddleware, verifyToken } from './middleware/auth.js';
23
28
  import { handleConnection } from './ws/handler.js';
24
29
  import { startScheduler, stopScheduler } from './agents/scheduler-agent.js';
@@ -69,12 +74,17 @@ app.use('/api/workspaces', workspaceRouter);
69
74
  app.use('/api/workspaces/:id/files', fileRouter);
70
75
  app.use('/api/workspaces/:id/channels', channelRouter);
71
76
  app.use('/api/workspaces/:id/issues', issueRouter);
77
+ app.use('/api/workflows', workflowRouter);
78
+ app.use('/api/workspaces/:id/commands', commandRouter);
72
79
  app.use('/api/workspaces/:id/agents', agentRouter);
73
80
  app.use('/api/workspaces/:id/tasks', taskRouter);
74
81
  app.use('/api/workspaces/:id/git', gitRouter);
75
82
  app.use('/api/agents', agentRouter);
76
83
  app.use('/api', llmRouter);
77
84
  app.use('/api/folder', folderRouter);
85
+ app.use('/api/skills', skillRouter);
86
+ app.use('/api/mcps', mcpRouter);
87
+ app.use('/api/subscriptions', subscriptionRouter);
78
88
  // Serve static web frontend in production (after API routes, before catch-all)
79
89
  const webDir = resolveRuntimeDir('web');
80
90
  if (existsSync(webDir)) {
@@ -1,19 +1,16 @@
1
1
  /**
2
- * Agent hooks: chain executor completion to reviewer processing.
2
+ * Agent hooks retained for legacy callers.
3
3
  */
4
- import { runReviewer } from '../agents/reviewer-agent.js';
5
4
  /**
6
- * Hook: executor complete triggers reviewer.
7
- * This is the core hook in the agent orchestration pipeline.
5
+ * Workflow task execution completes tasks directly. Review steps should be
6
+ * modeled as workflow nodes instead of this hardcoded hook.
8
7
  */
9
- export async function onExecutorComplete(workspaceId, taskId, issueId, result, ctx) {
8
+ export async function onExecutorComplete(workspaceId, taskId, issueId, result, _ctx) {
10
9
  console.log(`[hook:onExecutorComplete] entered workspaceId=${workspaceId} taskId=${taskId} issueId=${issueId} success=${result.success} summary=${JSON.stringify(result.summary)}`);
11
10
  if (!result.success) {
12
11
  console.warn(`[hook:onExecutorComplete] task ${taskId} failed: ${result.error}`);
13
12
  return;
14
13
  }
15
- console.log(`[hook:onExecutorComplete] triggering reviewer taskId=${taskId} issueId=${issueId}`);
16
- await runReviewer(workspaceId, taskId, issueId, result, ctx);
17
- console.log(`[hook:onExecutorComplete] reviewer completed taskId=${taskId} issueId=${issueId}`);
14
+ console.log(`[hook:onExecutorComplete] reviewer hook skipped; workflow controls task order taskId=${taskId} issueId=${issueId}`);
18
15
  }
19
16
  //# sourceMappingURL=agent-hooks.js.map
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-spaces/server",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "app.js",
6
6
  "packageManager": "pnpm@10.17.1",