@agent-spaces/server 0.3.63 → 0.3.64

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 (242) hide show
  1. package/dist/adapters/claude-code-runtime/message-format.js +1 -1
  2. package/dist/adapters/git.js +161 -57
  3. package/dist/agents/agent-designer.js +66 -12
  4. package/dist/agents/issue-task-controller.js +1 -1
  5. package/dist/app.js +50 -1
  6. package/dist/package.json +1 -1
  7. package/dist/routes/agent-commands.js +72 -0
  8. package/dist/routes/agent-sse.js +6 -0
  9. package/dist/routes/agent.js +15 -1
  10. package/dist/routes/database.js +345 -0
  11. package/dist/routes/git.js +52 -1
  12. package/dist/routes/kanban.js +39 -0
  13. package/dist/services/agent-commands.js +168 -0
  14. package/dist/services/builtin-tools/command-tools.js +117 -0
  15. package/dist/services/builtin-tools/database-tools.js +578 -0
  16. package/dist/services/builtin-tools/index.js +5 -0
  17. package/dist/services/builtin-tools/input-helpers.js +26 -0
  18. package/dist/services/builtin-tools/issue-tools.js +209 -0
  19. package/dist/services/builtin-tools/kanban-tools.js +277 -0
  20. package/dist/services/database-vector.js +244 -0
  21. package/dist/services/kanban.js +50 -0
  22. package/dist/storage/database-store.js +365 -0
  23. package/dist/storage/kanban-store.js +142 -0
  24. package/dist/web/404.html +1 -1
  25. package/dist/web/__next.__PAGE__.txt +4 -4
  26. package/dist/web/__next._full.txt +24 -29
  27. package/dist/web/__next._head.txt +4 -4
  28. package/dist/web/__next._index.txt +14 -14
  29. package/dist/web/__next._tree.txt +3 -8
  30. package/dist/web/_next/static/chunks/0-9194nbxy_6d.js +1 -0
  31. package/dist/web/_next/static/chunks/{13~y-b.b9rrw4.js → 01~quun6sefgl.js} +1 -1
  32. package/dist/web/_next/static/chunks/04a4vsq2wknm4.js +1 -0
  33. package/dist/web/_next/static/chunks/04dg4l4hv11q2.js +3 -0
  34. package/dist/web/_next/static/chunks/04fxddw-y20~-.js +7 -0
  35. package/dist/web/_next/static/chunks/04w7~5ooz_pwm.js +1 -0
  36. package/dist/web/_next/static/chunks/05oc21sj24wtq.js +1 -0
  37. package/dist/web/_next/static/chunks/{0-oog4zg4_m~m.css → 062f9zqctzg.b.css} +0 -3
  38. package/dist/web/_next/static/chunks/{014x2qbuiszni.js → 08ckpwmmwlors.js} +3 -3
  39. package/dist/web/_next/static/chunks/08k~meqbd8d6v.js +1 -0
  40. package/dist/web/_next/static/chunks/099h96wjsdb25.js +2 -0
  41. package/dist/web/_next/static/chunks/09h23logvq65m.js +1 -0
  42. package/dist/web/_next/static/chunks/0_mvduwjsm35w.js +1 -0
  43. package/dist/web/_next/static/chunks/0a5y-vhf4i5id.js +3 -0
  44. package/dist/web/_next/static/chunks/0ake2f3x2uan4.js +24 -0
  45. package/dist/web/_next/static/chunks/0ck8dw_9-1cdx.js +1 -0
  46. package/dist/web/_next/static/chunks/0f7cb9li~3jra.js +67 -0
  47. package/dist/web/_next/static/chunks/0fttswpzu69qx.js +1 -0
  48. package/dist/web/_next/static/chunks/0ghs9on7wodrm.js +1 -0
  49. package/dist/web/_next/static/chunks/0ghx3j_.-gdyd.js +1 -0
  50. package/dist/web/_next/static/chunks/0h9cu-k6z5o08.js +1 -0
  51. package/dist/web/_next/static/chunks/{02c22g~xd-y30.js → 0iuf30t0rjsjv.js} +2 -2
  52. package/dist/web/_next/static/chunks/{046me66nilkff.js → 0jb4~3qqkw2ps.js} +1 -1
  53. package/dist/web/_next/static/chunks/0k4zdm72k_bfy.js +1 -0
  54. package/dist/web/_next/static/chunks/{0kof5qdg8c2-k.js → 0n6_4so_afuwk.js} +37 -37
  55. package/dist/web/_next/static/chunks/0nb8.iteznuvx.js +1 -0
  56. package/dist/web/_next/static/chunks/0os58q93stt7j.js +3 -0
  57. package/dist/web/_next/static/chunks/0s9lmk8iauou3.js +1 -0
  58. package/dist/web/_next/static/chunks/0sp_zbx~w0am7.js +1 -0
  59. package/dist/web/_next/static/chunks/0tel~zsv4z8l_.js +1 -0
  60. package/dist/web/_next/static/chunks/0twamv4wr_dbv.js +1 -0
  61. package/dist/web/_next/static/chunks/{0_udc.6.tueey.js → 0uhol5wafdbl~.js} +1 -1
  62. package/dist/web/_next/static/chunks/0v3a_x4vt42kx.js +5 -0
  63. package/dist/web/_next/static/chunks/{002-dxhags4dj.js → 0v6hte2z-32ca.js} +2 -2
  64. package/dist/web/_next/static/chunks/0v7q4mys.mnm..css +1 -0
  65. package/dist/web/_next/static/chunks/{0ogpc3f3keopc.js → 0vbqaynjq8ga..js} +2 -2
  66. package/dist/web/_next/static/chunks/0xbaakj0dfc0..js +1 -0
  67. package/dist/web/_next/static/chunks/0z~vqzbm_q371.js +1 -0
  68. package/dist/web/_next/static/chunks/0~3xm80~j9ddg.js +1 -0
  69. package/dist/web/_next/static/chunks/{0y09i51u6ixq5.js → 10l-9myrdo3x9.js} +1 -1
  70. package/dist/web/_next/static/chunks/12jm4gwmvq-0q.js +90 -0
  71. package/dist/web/_next/static/chunks/{0aa6bcdmtpekn.js → 1442stwa0gbxe.js} +1 -1
  72. package/dist/web/_next/static/chunks/14s37n6h0_q3x.js +16 -0
  73. package/dist/web/_next/static/chunks/161ik5kfyuwtd.js +1 -0
  74. package/dist/web/_next/static/chunks/17k~zbv0_4rep.js +1 -0
  75. package/dist/web/_not-found/__next._full.txt +23 -23
  76. package/dist/web/_not-found/__next._head.txt +4 -4
  77. package/dist/web/_not-found/__next._index.txt +14 -14
  78. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  79. package/dist/web/_not-found/__next._not-found.txt +3 -3
  80. package/dist/web/_not-found/__next._tree.txt +3 -3
  81. package/dist/web/_not-found.html +1 -1
  82. package/dist/web/_not-found.txt +23 -23
  83. package/dist/web/index.html +1 -1
  84. package/dist/web/index.txt +24 -29
  85. package/dist/web/login/__next._full.txt +32 -36
  86. package/dist/web/login/__next._head.txt +4 -4
  87. package/dist/web/login/__next._index.txt +14 -14
  88. package/dist/web/login/__next._tree.txt +3 -8
  89. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  90. package/dist/web/login/__next.login.txt +3 -3
  91. package/dist/web/login.html +1 -1
  92. package/dist/web/login.txt +32 -36
  93. package/dist/web/settings/__next._full.txt +26 -31
  94. package/dist/web/settings/__next._head.txt +4 -4
  95. package/dist/web/settings/__next._index.txt +14 -14
  96. package/dist/web/settings/__next._tree.txt +3 -8
  97. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  98. package/dist/web/settings/__next.settings.txt +5 -5
  99. package/dist/web/settings/agents/__next._full.txt +26 -31
  100. package/dist/web/settings/agents/__next._head.txt +4 -4
  101. package/dist/web/settings/agents/__next._index.txt +14 -14
  102. package/dist/web/settings/agents/__next._tree.txt +3 -8
  103. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  104. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  105. package/dist/web/settings/agents/__next.settings.txt +5 -5
  106. package/dist/web/settings/agents.html +1 -1
  107. package/dist/web/settings/agents.txt +26 -31
  108. package/dist/web/settings/mcps/__next._full.txt +26 -31
  109. package/dist/web/settings/mcps/__next._head.txt +4 -4
  110. package/dist/web/settings/mcps/__next._index.txt +14 -14
  111. package/dist/web/settings/mcps/__next._tree.txt +3 -8
  112. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  113. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  114. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  115. package/dist/web/settings/mcps.html +1 -1
  116. package/dist/web/settings/mcps.txt +26 -31
  117. package/dist/web/settings/models/__next._full.txt +26 -31
  118. package/dist/web/settings/models/__next._head.txt +4 -4
  119. package/dist/web/settings/models/__next._index.txt +14 -14
  120. package/dist/web/settings/models/__next._tree.txt +3 -8
  121. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  122. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  123. package/dist/web/settings/models/__next.settings.txt +5 -5
  124. package/dist/web/settings/models.html +1 -1
  125. package/dist/web/settings/models.txt +26 -31
  126. package/dist/web/settings/output-styles/__next._full.txt +26 -31
  127. package/dist/web/settings/output-styles/__next._head.txt +4 -4
  128. package/dist/web/settings/output-styles/__next._index.txt +14 -14
  129. package/dist/web/settings/output-styles/__next._tree.txt +3 -8
  130. package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
  131. package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
  132. package/dist/web/settings/output-styles/__next.settings.txt +5 -5
  133. package/dist/web/settings/output-styles.html +1 -1
  134. package/dist/web/settings/output-styles.txt +26 -31
  135. package/dist/web/settings/prompts/__next._full.txt +26 -31
  136. package/dist/web/settings/prompts/__next._head.txt +4 -4
  137. package/dist/web/settings/prompts/__next._index.txt +14 -14
  138. package/dist/web/settings/prompts/__next._tree.txt +3 -8
  139. package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
  140. package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
  141. package/dist/web/settings/prompts/__next.settings.txt +5 -5
  142. package/dist/web/settings/prompts.html +1 -1
  143. package/dist/web/settings/prompts.txt +26 -31
  144. package/dist/web/settings/providers/__next._full.txt +26 -31
  145. package/dist/web/settings/providers/__next._head.txt +4 -4
  146. package/dist/web/settings/providers/__next._index.txt +14 -14
  147. package/dist/web/settings/providers/__next._tree.txt +3 -8
  148. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  149. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  150. package/dist/web/settings/providers/__next.settings.txt +5 -5
  151. package/dist/web/settings/providers.html +1 -1
  152. package/dist/web/settings/providers.txt +26 -31
  153. package/dist/web/settings/skills/__next._full.txt +26 -31
  154. package/dist/web/settings/skills/__next._head.txt +4 -4
  155. package/dist/web/settings/skills/__next._index.txt +14 -14
  156. package/dist/web/settings/skills/__next._tree.txt +3 -8
  157. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  158. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  159. package/dist/web/settings/skills/__next.settings.txt +5 -5
  160. package/dist/web/settings/skills.html +1 -1
  161. package/dist/web/settings/skills.txt +26 -31
  162. package/dist/web/settings/tools/__next._full.txt +38 -0
  163. package/dist/web/settings/tools/__next._head.txt +6 -0
  164. package/dist/web/settings/tools/__next._index.txt +15 -0
  165. package/dist/web/settings/tools/__next._tree.txt +3 -0
  166. package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +9 -0
  167. package/dist/web/settings/tools/__next.settings.tools.txt +5 -0
  168. package/dist/web/settings/tools/__next.settings.txt +7 -0
  169. package/dist/web/settings/tools.html +1 -0
  170. package/dist/web/settings/tools.txt +38 -0
  171. package/dist/web/settings.html +1 -1
  172. package/dist/web/settings.txt +26 -31
  173. package/dist/web/workflows/__next._full.txt +34 -38
  174. package/dist/web/workflows/__next._head.txt +4 -4
  175. package/dist/web/workflows/__next._index.txt +14 -14
  176. package/dist/web/workflows/__next._tree.txt +3 -8
  177. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  178. package/dist/web/workflows/__next.workflows.txt +3 -3
  179. package/dist/web/workflows.html +1 -1
  180. package/dist/web/workflows.txt +34 -38
  181. package/dist/web/workspace/_/__next._full.txt +25 -29
  182. package/dist/web/workspace/_/__next._head.txt +4 -4
  183. package/dist/web/workspace/_/__next._index.txt +14 -14
  184. package/dist/web/workspace/_/__next._tree.txt +4 -8
  185. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  186. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  187. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  188. package/dist/web/workspace/_.html +1 -1
  189. package/dist/web/workspace/_.txt +25 -29
  190. package/dist/web/workspaces/__next._full.txt +24 -29
  191. package/dist/web/workspaces/__next._head.txt +4 -4
  192. package/dist/web/workspaces/__next._index.txt +14 -14
  193. package/dist/web/workspaces/__next._tree.txt +3 -8
  194. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  195. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  196. package/dist/web/workspaces.html +1 -1
  197. package/dist/web/workspaces.txt +24 -29
  198. package/dist/ws/agent-prompt.js +45 -8
  199. package/dist/ws/agent-runner.js +6 -4
  200. package/package.json +11 -10
  201. package/dist/web/_next/static/chunks/0-1bfvfwifh-m.js +0 -1
  202. package/dist/web/_next/static/chunks/04v-pqqs-2lvp.css +0 -1
  203. package/dist/web/_next/static/chunks/06w~.izyfylap.js +0 -1
  204. package/dist/web/_next/static/chunks/09t62a89~jesc.js +0 -3
  205. package/dist/web/_next/static/chunks/09y_3krccdd~u.js +0 -1
  206. package/dist/web/_next/static/chunks/0_e~hmntdfop9.js +0 -1
  207. package/dist/web/_next/static/chunks/0cke33d38fnby.js +0 -90
  208. package/dist/web/_next/static/chunks/0cyq~8xb~r5fc.js +0 -1
  209. package/dist/web/_next/static/chunks/0fxa1edqr7dve.js +0 -1
  210. package/dist/web/_next/static/chunks/0hra80eyuu37u.js +0 -1
  211. package/dist/web/_next/static/chunks/0ie7y4k2ncvnn.js +0 -1
  212. package/dist/web/_next/static/chunks/0igc~uli58ncw.js +0 -2
  213. package/dist/web/_next/static/chunks/0msb2mi3hiu5_.js +0 -67
  214. package/dist/web/_next/static/chunks/0p1n8bvfnpl41.js +0 -1
  215. package/dist/web/_next/static/chunks/0s_dva~eh2yhi.js +0 -3
  216. package/dist/web/_next/static/chunks/0typj55ox8d5i.js +0 -1
  217. package/dist/web/_next/static/chunks/0w2p4ldi2aymp.js +0 -1
  218. package/dist/web/_next/static/chunks/0zc0xgp5ourcn.js +0 -1
  219. package/dist/web/_next/static/chunks/0zmhdridea1s5.js +0 -90
  220. package/dist/web/_next/static/chunks/10.alq.6stmld.js +0 -1
  221. package/dist/web/_next/static/chunks/11b3zgk1etlkw.js +0 -7
  222. package/dist/web/_next/static/chunks/11qp8r5dotf7d.js +0 -1
  223. package/dist/web/_next/static/chunks/12mxlicp247c8.js +0 -1
  224. package/dist/web/_next/static/chunks/135w27dd8c19s.js +0 -5
  225. package/dist/web/_next/static/chunks/176dc-dtokxxi.js +0 -16
  226. package/dist/web/_next/static/chunks/17g-0pqre.anw.js +0 -1
  227. package/dist/web/_next/static/media/0a7740363b4d4863-s.1110aazvokzp..woff2 +0 -0
  228. package/dist/web/_next/static/media/0da9c7f357bd9d4d-s.0ek5sshv8wk3m.woff2 +0 -0
  229. package/dist/web/_next/static/media/1b99372b3eaef0c8-s.p.0gx2haw2tmll8.woff2 +0 -0
  230. package/dist/web/_next/static/media/41e95f694c5c4549-s.0r1jrq7neqdsq.woff2 +0 -0
  231. package/dist/web/_next/static/media/5c285b27cdda1fe8-s.p.0yo6-5yoeeudq.woff2 +0 -0
  232. package/dist/web/_next/static/media/5f9d24ebef5d5292-s.0esuu2f5si~v8.woff2 +0 -0
  233. package/dist/web/_next/static/media/6c55a692938ebbbc-s.0g6rbszwfgn9y.woff2 +0 -0
  234. package/dist/web/_next/static/media/8e6fa89aa22d24ec-s.p.0uvzar8hswo3p.woff2 +0 -0
  235. package/dist/web/_next/static/media/a218039a3287bcfd-s.p.17-1enzs_j91b.woff2 +0 -0
  236. package/dist/web/_next/static/media/b2ea385cb5ae8625-s.0kjod.kaq1k69.woff2 +0 -0
  237. package/dist/web/_next/static/media/bdc7e24a509eb931-s.0apt5mko2.qn3.woff2 +0 -0
  238. package/dist/web/_next/static/media/c3cb240f9c892514-s.163v2i5cux7-c.woff2 +0 -0
  239. package/dist/web/_next/static/media/e2334d715941921e-s.p.12skym0rqknxy.woff2 +0 -0
  240. /package/dist/web/_next/static/{OoXAsPtrUWjC1l898FSvI → e8w3vd9Eov_LDYWj_0zsE}/_buildManifest.js +0 -0
  241. /package/dist/web/_next/static/{OoXAsPtrUWjC1l898FSvI → e8w3vd9Eov_LDYWj_0zsE}/_clientMiddlewareManifest.js +0 -0
  242. /package/dist/web/_next/static/{OoXAsPtrUWjC1l898FSvI → e8w3vd9Eov_LDYWj_0zsE}/_ssgManifest.js +0 -0
@@ -24,7 +24,7 @@ export function formatMessage(message) {
24
24
  case 'tool_progress':
25
25
  return `${message.tool_name} running (${message.elapsed_time_seconds}s)`;
26
26
  case 'tool_use_summary':
27
- return message.summary;
27
+ return null;
28
28
  default:
29
29
  return null;
30
30
  }
@@ -36,26 +36,36 @@ function getGit(workspace) {
36
36
  }
37
37
  function mapStatus(raw) {
38
38
  const files = [];
39
+ const addOrUpdate = (file) => {
40
+ const existing = files.find(x => x.path === file.path);
41
+ if (existing) {
42
+ Object.assign(existing, file, {
43
+ staged: existing.staged || file.staged,
44
+ conflicted: existing.conflicted || file.conflicted,
45
+ status: file.conflicted ? 'conflicted' : existing.status,
46
+ });
47
+ }
48
+ else {
49
+ files.push(file);
50
+ }
51
+ };
39
52
  for (const f of raw.staged) {
40
- files.push({ path: f, status: mapStatusCode(raw.files.find(r => r.path === f)?.index) });
53
+ addOrUpdate({ path: f, status: mapStatusCode(raw.files.find(r => r.path === f)?.index), staged: true });
41
54
  }
42
55
  for (const f of raw.modified) {
43
- if (!files.find(x => x.path === f)) {
44
- files.push({ path: f, status: 'modified' });
45
- }
56
+ addOrUpdate({ path: f, status: 'modified' });
46
57
  }
47
58
  for (const f of raw.not_added) {
48
- files.push({ path: f, status: 'untracked' });
59
+ addOrUpdate({ path: f, status: 'untracked' });
49
60
  }
50
61
  for (const f of raw.deleted) {
51
- if (!files.find(x => x.path === f)) {
52
- files.push({ path: f, status: 'deleted' });
53
- }
62
+ addOrUpdate({ path: f, status: 'deleted' });
54
63
  }
55
64
  for (const f of raw.created) {
56
- if (!files.find(x => x.path === f)) {
57
- files.push({ path: f, status: 'added' });
58
- }
65
+ addOrUpdate({ path: f, status: 'added', staged: raw.staged.includes(f) });
66
+ }
67
+ for (const f of raw.conflicted) {
68
+ addOrUpdate({ path: f, status: 'conflicted', conflicted: true });
59
69
  }
60
70
  return {
61
71
  branch: raw.current || 'HEAD',
@@ -67,6 +77,73 @@ function mapStatus(raw) {
67
77
  deletions: 0,
68
78
  };
69
79
  }
80
+ async function getUpstreamRef(git, branch) {
81
+ const upstream = await git.raw(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'])
82
+ .then(v => v.trim())
83
+ .catch(() => '');
84
+ if (upstream)
85
+ return upstream;
86
+ if (!branch || branch === 'HEAD')
87
+ return null;
88
+ const originBranch = `origin/${branch}`;
89
+ const hasOriginBranch = await git.raw(['rev-parse', '--verify', '--quiet', originBranch])
90
+ .then(() => true)
91
+ .catch(() => false);
92
+ return hasOriginBranch ? originBranch : null;
93
+ }
94
+ function splitConflictSides(content) {
95
+ const left = [];
96
+ const right = [];
97
+ let mode = 'both';
98
+ let hasConflict = false;
99
+ for (const line of content.split('\n')) {
100
+ if (line.startsWith('<<<<<<< ')) {
101
+ hasConflict = true;
102
+ mode = 'left';
103
+ continue;
104
+ }
105
+ if (line.startsWith('=======')) {
106
+ mode = 'right';
107
+ continue;
108
+ }
109
+ if (line.startsWith('>>>>>>> ')) {
110
+ mode = 'both';
111
+ continue;
112
+ }
113
+ if (mode === 'both' || mode === 'left')
114
+ left.push(line);
115
+ if (mode === 'both' || mode === 'right')
116
+ right.push(line);
117
+ }
118
+ return { left: left.join('\n'), right: right.join('\n'), hasConflict };
119
+ }
120
+ async function buildWorkingDiff(git, rootDir, filePath, conflicted = false) {
121
+ const binary = isBinaryPath(filePath);
122
+ const workingContent = binary ? '' : await readFile(join(rootDir, filePath), 'utf-8').catch(() => '');
123
+ if (!binary && conflicted) {
124
+ const conflict = splitConflictSides(workingContent);
125
+ if (conflict.hasConflict) {
126
+ return {
127
+ path: filePath,
128
+ oldContent: conflict.left,
129
+ newContent: conflict.right,
130
+ isBinary: false,
131
+ isNew: false,
132
+ isDeleted: false,
133
+ isConflict: true,
134
+ };
135
+ }
136
+ }
137
+ const oldContent = binary ? '' : await git.show([`HEAD:${filePath}`]).catch(() => '');
138
+ return {
139
+ path: filePath,
140
+ oldContent,
141
+ newContent: workingContent,
142
+ isBinary: binary,
143
+ isNew: !oldContent,
144
+ isDeleted: !workingContent,
145
+ };
146
+ }
70
147
  function mapStatusCode(code) {
71
148
  switch (code) {
72
149
  case 'A': return 'added';
@@ -83,6 +160,19 @@ export async function gitStatus(workspaceId) {
83
160
  const git = getGit(ws);
84
161
  const raw = await git.status();
85
162
  const result = mapStatus(raw);
163
+ try {
164
+ const headHash = await git.revparse(['--short', 'HEAD']);
165
+ result.headHash = headHash.trim();
166
+ }
167
+ catch { }
168
+ try {
169
+ const upstream = await getUpstreamRef(git, result.branch);
170
+ if (upstream) {
171
+ const remoteHeadHash = await git.revparse(['--short', upstream]);
172
+ result.remoteHeadHash = remoteHeadHash.trim();
173
+ }
174
+ }
175
+ catch { }
86
176
  if (!result.clean) {
87
177
  try {
88
178
  // Tracked changes: modified/deleted/renamed
@@ -122,66 +212,73 @@ export async function gitDiff(workspaceId, filePath) {
122
212
  throw new Error('Workspace not found');
123
213
  const git = getGit(ws);
124
214
  const diffs = [];
215
+ const status = await git.status();
216
+ const conflicted = new Set(status.conflicted);
125
217
  if (filePath) {
126
- const diff = await git.diff([filePath]);
127
- const oldContent = await git.show([`HEAD:${filePath}`]).catch(() => '');
128
- const newContent = await import('node:fs/promises').then(fs => fs.readFile(`${ws.boundDirs[0]}/${filePath}`, 'utf-8')).catch(() => '');
129
- diffs.push({
130
- path: filePath,
131
- oldContent: isBinaryPath(filePath) ? '' : oldContent,
132
- newContent: isBinaryPath(filePath) ? '' : newContent,
133
- isBinary: isBinaryPath(filePath),
134
- isNew: !oldContent,
135
- isDeleted: !newContent,
136
- });
218
+ diffs.push(await buildWorkingDiff(git, ws.boundDirs[0], filePath, conflicted.has(filePath)));
137
219
  }
138
220
  else {
139
221
  const raw = await git.diff(['--name-only']);
140
- const files = raw.split('\n').filter(Boolean);
222
+ const cachedRaw = await git.diff(['--cached', '--name-only']);
223
+ const files = Array.from(new Set([
224
+ ...raw.split('\n').filter(Boolean),
225
+ ...cachedRaw.split('\n').filter(Boolean),
226
+ ...status.not_added,
227
+ ...status.conflicted,
228
+ ]));
141
229
  for (const f of files) {
142
- const binary = isBinaryPath(f);
143
- const oldContent = binary ? '' : await git.show([`HEAD:${f}`]).catch(() => '');
144
- const newContent = binary ? '' : await import('node:fs/promises').then(fs => fs.readFile(`${ws.boundDirs[0]}/${f}`, 'utf-8')).catch(() => '');
145
- diffs.push({
146
- path: f,
147
- oldContent,
148
- newContent,
149
- isBinary: binary,
150
- isNew: !oldContent,
151
- isDeleted: !newContent,
152
- });
153
- }
154
- // Also check untracked (staged new files)
155
- const status = await git.status();
156
- for (const f of status.not_added) {
157
- const binary = isBinaryPath(f);
158
- const newContent = binary ? '' : await import('node:fs/promises').then(fs => fs.readFile(`${ws.boundDirs[0]}/${f}`, 'utf-8')).catch(() => '');
159
- if (newContent || binary) {
160
- diffs.push({
161
- path: f,
162
- oldContent: '',
163
- newContent: binary ? '' : newContent,
164
- isBinary: binary,
165
- isNew: true,
166
- isDeleted: false,
167
- });
168
- }
230
+ diffs.push(await buildWorkingDiff(git, ws.boundDirs[0], f, conflicted.has(f)));
169
231
  }
170
232
  }
171
233
  return diffs;
172
234
  }
235
+ export async function gitStage(workspaceId, filePath) {
236
+ const ws = getWorkspace(workspaceId);
237
+ if (!ws)
238
+ throw new Error('Workspace not found');
239
+ const git = getGit(ws);
240
+ await git.add(filePath);
241
+ }
242
+ export async function gitUnstage(workspaceId, filePath) {
243
+ const ws = getWorkspace(workspaceId);
244
+ if (!ws)
245
+ throw new Error('Workspace not found');
246
+ const git = getGit(ws);
247
+ await git.raw(['restore', '--staged', '--', filePath]).catch(async () => {
248
+ await git.raw(['reset', 'HEAD', '--', filePath]);
249
+ });
250
+ }
251
+ export async function gitResolveFile(workspaceId, filePath, content, stage = true) {
252
+ const ws = getWorkspace(workspaceId);
253
+ if (!ws)
254
+ throw new Error('Workspace not found');
255
+ const git = getGit(ws);
256
+ await writeFile(join(ws.boundDirs[0], filePath), content, 'utf-8');
257
+ if (stage)
258
+ await git.add(filePath);
259
+ }
173
260
  export async function gitLog(workspaceId, maxCount = 50) {
174
261
  const ws = getWorkspace(workspaceId);
175
262
  if (!ws)
176
263
  throw new Error('Workspace not found');
177
264
  const git = getGit(ws);
178
- const log = await git.log({ maxCount });
179
- return log.all.map(entry => ({
180
- hash: entry.hash.substring(0, 7),
181
- message: entry.message,
182
- author: entry.author_name,
183
- date: entry.date,
184
- }));
265
+ const status = await git.status();
266
+ const upstream = await getUpstreamRef(git, status.current || 'HEAD');
267
+ const refs = upstream ? ['HEAD', upstream] : ['HEAD'];
268
+ const pretty = '%H%x1f%s%x1f%an%x1f%aI%x1e';
269
+ const raw = await git.raw(['log', `--max-count=${maxCount}`, `--pretty=format:${pretty}`, ...refs]);
270
+ return raw.split('\x1e')
271
+ .map(record => record.trim())
272
+ .filter(Boolean)
273
+ .map(record => {
274
+ const [hash = '', message = '', author = '', date = ''] = record.split('\x1f');
275
+ return {
276
+ hash: hash.substring(0, 7),
277
+ message,
278
+ author,
279
+ date,
280
+ };
281
+ });
185
282
  }
186
283
  export async function gitCommit(workspaceId, message) {
187
284
  const ws = getWorkspace(workspaceId);
@@ -274,6 +371,13 @@ export async function gitPush(workspaceId) {
274
371
  throw new Error('No remote repository configured. Please add a remote first.');
275
372
  await git.push('origin', branch);
276
373
  }
374
+ export async function gitFetch(workspaceId) {
375
+ const ws = getWorkspace(workspaceId);
376
+ if (!ws)
377
+ throw new Error('Workspace not found');
378
+ const git = getGit(ws);
379
+ await git.fetch('origin');
380
+ }
277
381
  export async function gitPull(workspaceId) {
278
382
  const ws = getWorkspace(workspaceId);
279
383
  if (!ws)
@@ -28,13 +28,36 @@ export async function generateAgentDesign(userPrompt) {
28
28
  apiBase: maskUrl(config.apiBase),
29
29
  promptLength: prompt.length,
30
30
  });
31
- const content = await requestDesign(config, prompt);
31
+ const content = await requestText(config, buildDesignSystemPrompt(config), prompt);
32
32
  console.info('[agent-designer] model text extracted', {
33
33
  length: content.length,
34
34
  preview: content.slice(0, 500),
35
35
  });
36
36
  return normalizeDesign(parseJsonObject(content));
37
37
  }
38
+ export async function optimizeAgentPrompt(userPrompt, currentPrompt) {
39
+ const prompt = userPrompt.trim();
40
+ if (!prompt)
41
+ throw new Error('prompt is required');
42
+ const config = resolveModelConfig();
43
+ if (!config) {
44
+ throw new Error(`Configure model settings for ${AGENT_GENERATOR_PRESET_ID} before optimizing prompts.`);
45
+ }
46
+ console.info('[agent-designer] optimizing agent prompt', {
47
+ agentId: AGENT_GENERATOR_PRESET_ID,
48
+ provider: config.modelProvider ?? inferProvider(config.apiBase),
49
+ modelId: config.modelId,
50
+ apiBase: maskUrl(config.apiBase),
51
+ promptLength: prompt.length,
52
+ currentPromptLength: currentPrompt.trim().length,
53
+ });
54
+ const content = await requestText(config, buildOptimizationSystemPrompt(config), buildPromptOptimizationUserPrompt(prompt, currentPrompt));
55
+ console.info('[agent-designer] optimized prompt received', {
56
+ length: content.length,
57
+ preview: content.slice(0, 500),
58
+ });
59
+ return { systemPrompt: normalizePrompt(content) };
60
+ }
38
61
  function resolveModelConfig() {
39
62
  const preset = readAgentTemplate(AGENT_GENERATOR_PRESET_ID);
40
63
  if (preset?.apiBase && preset.apiKey && preset.modelId) {
@@ -51,14 +74,17 @@ function resolveModelConfig() {
51
74
  return null;
52
75
  }
53
76
  async function requestDesign(config, userPrompt) {
77
+ return requestText(config, buildDesignSystemPrompt(config), userPrompt);
78
+ }
79
+ async function requestText(config, systemPrompt, userPrompt) {
54
80
  const provider = config.modelProvider ?? inferProvider(config.apiBase);
55
81
  if (provider === 'anthropic-messages')
56
- return requestAnthropic(config, userPrompt);
82
+ return requestAnthropic(config, systemPrompt, userPrompt);
57
83
  if (provider === 'gemini-generate-content')
58
- return requestGemini(config, userPrompt);
59
- return requestOpenAICompatible(config, userPrompt, provider === 'openai-responses' || provider === 'openai-responses-to-anthropic-messages');
84
+ return requestGemini(config, systemPrompt, userPrompt);
85
+ return requestOpenAICompatible(config, systemPrompt, userPrompt, provider === 'openai-responses' || provider === 'openai-responses-to-anthropic-messages');
60
86
  }
61
- async function requestOpenAICompatible(config, userPrompt, useResponsesApi) {
87
+ async function requestOpenAICompatible(config, systemPrompt, userPrompt, useResponsesApi) {
62
88
  const url = joinUrl(config.apiBase, useResponsesApi ? '/responses' : '/chat/completions');
63
89
  const response = await fetch(url, {
64
90
  method: 'POST',
@@ -69,14 +95,14 @@ async function requestOpenAICompatible(config, userPrompt, useResponsesApi) {
69
95
  body: JSON.stringify(useResponsesApi
70
96
  ? {
71
97
  model: config.modelId,
72
- input: `${buildSystemPrompt(config)}\n\nUser request:\n${userPrompt}`,
98
+ input: `${systemPrompt}\n\nUser request:\n${userPrompt}`,
73
99
  temperature: config.temperature ?? 0.2,
74
100
  max_output_tokens: config.maxTokens,
75
101
  }
76
102
  : {
77
103
  model: config.modelId,
78
104
  messages: [
79
- { role: 'system', content: buildSystemPrompt(config) },
105
+ { role: 'system', content: systemPrompt },
80
106
  { role: 'user', content: userPrompt },
81
107
  ],
82
108
  temperature: config.temperature ?? 0.2,
@@ -88,7 +114,7 @@ async function requestOpenAICompatible(config, userPrompt, useResponsesApi) {
88
114
  throw new Error(body.error || `Agent design generation failed with status ${response.status}`);
89
115
  return body.text;
90
116
  }
91
- async function requestAnthropic(config, userPrompt) {
117
+ async function requestAnthropic(config, systemPrompt, userPrompt) {
92
118
  const response = await fetch(getAnthropicMessagesUrl(config.apiBase), {
93
119
  method: 'POST',
94
120
  headers: {
@@ -98,7 +124,7 @@ async function requestAnthropic(config, userPrompt) {
98
124
  },
99
125
  body: JSON.stringify({
100
126
  model: config.modelId,
101
- system: buildSystemPrompt(config),
127
+ system: systemPrompt,
102
128
  messages: [{ role: 'user', content: userPrompt }],
103
129
  max_tokens: config.maxTokens ?? 4096,
104
130
  temperature: config.temperature ?? 0.2,
@@ -109,7 +135,7 @@ async function requestAnthropic(config, userPrompt) {
109
135
  throw new Error(body.error || `Agent design generation failed with status ${response.status}`);
110
136
  return body.text;
111
137
  }
112
- async function requestGemini(config, userPrompt) {
138
+ async function requestGemini(config, systemPrompt, userPrompt) {
113
139
  const response = await fetch(joinUrl(config.apiBase, `/models/${encodeURIComponent(config.modelId)}:generateContent`), {
114
140
  method: 'POST',
115
141
  headers: {
@@ -117,7 +143,7 @@ async function requestGemini(config, userPrompt) {
117
143
  'Content-Type': 'application/json',
118
144
  },
119
145
  body: JSON.stringify({
120
- systemInstruction: { parts: [{ text: buildSystemPrompt(config) }] },
146
+ systemInstruction: { parts: [{ text: systemPrompt }] },
121
147
  contents: [{ role: 'user', parts: [{ text: userPrompt }] }],
122
148
  generationConfig: {
123
149
  temperature: config.temperature ?? 0.2,
@@ -130,12 +156,40 @@ async function requestGemini(config, userPrompt) {
130
156
  throw new Error(body.error || `Agent design generation failed with status ${response.status}`);
131
157
  return body.text;
132
158
  }
133
- function buildSystemPrompt(config) {
159
+ function buildDesignSystemPrompt(config) {
134
160
  const custom = config.systemPrompt?.trim();
135
161
  if (!custom)
136
162
  return SYSTEM_PROMPT;
137
163
  return `${custom}\n\n${SYSTEM_PROMPT}`;
138
164
  }
165
+ function buildOptimizationSystemPrompt(config) {
166
+ const custom = config.systemPrompt?.trim();
167
+ const base = [
168
+ 'You optimize agent system prompts.',
169
+ 'Return only the rewritten prompt text.',
170
+ 'Do not wrap the result in markdown fences.',
171
+ 'Do not add commentary, bullets about the process, or any explanation outside the prompt.',
172
+ 'Keep the prompt actionable, concise, and directly usable as a system prompt.',
173
+ 'Preserve important constraints from the current prompt unless the user request clearly asks to change them.',
174
+ ].join('\n');
175
+ if (!custom)
176
+ return base;
177
+ return `${custom}\n\n${base}`;
178
+ }
179
+ function buildPromptOptimizationUserPrompt(userRequest, currentPrompt) {
180
+ return [
181
+ 'User request:',
182
+ userRequest,
183
+ '',
184
+ 'Current system prompt:',
185
+ currentPrompt.trim() || '(empty)',
186
+ '',
187
+ 'Rewrite the current system prompt according to the user request. Return only the final prompt text.',
188
+ ].join('\n');
189
+ }
190
+ function normalizePrompt(text) {
191
+ return text.trim().replace(/^```(?:markdown|md)?\s*/i, '').replace(/\s*```$/i, '');
192
+ }
139
193
  async function readResponseBody(response) {
140
194
  const raw = await response.text();
141
195
  if (!raw)
@@ -5,7 +5,7 @@ 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
7
  import { mapWorkflowToTaskDrafts, validateWorkflowForRun } from '../services/workflow.js';
8
- import { createIssueFunctionTools } from '../services/builtin-tools.js';
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';
package/dist/app.js CHANGED
@@ -7,7 +7,7 @@ import { createServer } from 'node:http';
7
7
  import { WebSocketServer } from 'ws';
8
8
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
9
9
  import { writeFile } from 'node:fs/promises';
10
- import { join, dirname, basename } from 'node:path';
10
+ import { join, dirname, extname, basename } from 'node:path';
11
11
  import { fileURLToPath } from 'node:url';
12
12
  import workspaceRouter from './routes/workspace.js';
13
13
  import fileRouter from './routes/file.js';
@@ -31,7 +31,10 @@ import subscriptionRouter from './routes/subscription.js';
31
31
  import agentSseRouter from './routes/agent-sse.js';
32
32
  import searchRouter from './routes/search.js';
33
33
  import notificationRouter from './routes/notification.js';
34
+ import databaseRouter from './routes/database.js';
35
+ import kanbanRouter from './routes/kanban.js';
34
36
  import speechRecognitionRouter, { handleSpeechStream } from './routes/speech-recognition.js';
37
+ import agentCommandsRouter from './routes/agent-commands.js';
35
38
  import { getUserSettings, setUserAvatarUrl, removeUserAvatarUrl } from './storage/user-settings-store.js';
36
39
  import { authMiddleware, verifyToken } from './middleware/auth.js';
37
40
  import { handleConnection } from './ws/handler.js';
@@ -158,6 +161,49 @@ app.post('/api/upload/avatar', async (req, res) => {
158
161
  await writeFile(join(avatarsDir, name), Buffer.from(base64, 'base64'));
159
162
  res.json({ url: `/static/avatars/${name}` });
160
163
  });
164
+ // Font upload
165
+ const FONT_EXTS = new Set(['.ttf', '.otf', '.woff', '.woff2']);
166
+ const fontsDir = join(publicDir, 'fonts');
167
+ if (!existsSync(fontsDir))
168
+ mkdirSync(fontsDir, { recursive: true });
169
+ app.get('/api/fonts', (_req, res) => {
170
+ if (!existsSync(fontsDir)) {
171
+ res.json([]);
172
+ return;
173
+ }
174
+ const fonts = readdirSync(fontsDir).filter(f => FONT_EXTS.has(extname(f).toLowerCase()));
175
+ res.json(fonts.map(f => ({ name: f, url: `/static/fonts/${f}` })));
176
+ });
177
+ app.post('/api/fonts/upload', async (req, res) => {
178
+ const { name, content } = req.body;
179
+ if (!name || !content) {
180
+ res.status(400).json({ error: 'name and content are required' });
181
+ return;
182
+ }
183
+ const ext = extname(name).toLowerCase();
184
+ if (!FONT_EXTS.has(ext)) {
185
+ res.status(400).json({ error: 'Unsupported font format' });
186
+ return;
187
+ }
188
+ const safeName = basename(name).replace(/[^a-zA-Z0-9._-]/g, '_');
189
+ await writeFile(join(fontsDir, safeName), Buffer.from(content, 'base64'));
190
+ res.json({ name: safeName, url: `/static/fonts/${safeName}` });
191
+ });
192
+ app.delete('/api/fonts/:name', (req, res) => {
193
+ const name = basename(req.params.name);
194
+ const ext = extname(name).toLowerCase();
195
+ if (!FONT_EXTS.has(ext)) {
196
+ res.status(400).json({ error: 'Invalid font file' });
197
+ return;
198
+ }
199
+ const filePath = join(fontsDir, name);
200
+ if (!existsSync(filePath)) {
201
+ res.status(404).json({ error: 'Font not found' });
202
+ return;
203
+ }
204
+ import('node:fs').then(fs => fs.unlinkSync(filePath));
205
+ res.json({ ok: true });
206
+ });
161
207
  // User settings
162
208
  app.get('/api/user/settings', (_req, res) => {
163
209
  const settings = getUserSettings();
@@ -206,6 +252,8 @@ app.post('/api/git-config', async (req, res) => {
206
252
  }
207
253
  });
208
254
  app.use('/api/workspaces/:id/search', searchRouter);
255
+ app.use('/api/workspaces/:id/database', databaseRouter);
256
+ app.use('/api/workspaces/:id/kanban', kanbanRouter);
209
257
  app.use('/api/workspaces/:id/notifications', notificationRouter);
210
258
  app.use('/api/agents', agentRouter);
211
259
  app.use('/api', llmRouter);
@@ -216,6 +264,7 @@ app.use('/api/output-styles', outputStyleRouter);
216
264
  app.use('/api/mcps', mcpRouter);
217
265
  app.use('/api/subscriptions', subscriptionRouter);
218
266
  app.use('/api/speech-recognition', speechRecognitionRouter);
267
+ app.use('/api/agent-commands', agentCommandsRouter);
219
268
  // Serve static web frontend in production (after API routes, before catch-all)
220
269
  const webDir = resolveRuntimeDir('web');
221
270
  if (existsSync(webDir)) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-spaces/server",
3
- "version": "0.3.63",
3
+ "version": "0.3.64",
4
4
  "type": "module",
5
5
  "main": "app.js",
6
6
  "packageManager": "pnpm@10.17.1",
@@ -0,0 +1,72 @@
1
+ import { Router } from 'express';
2
+ import { listAgentsWithCommands, listAllCommands, listCommands, getCommand, createCommand, updateCommand, deleteCommand, applyCommandToAgents, } from '../services/agent-commands.js';
3
+ const router = Router();
4
+ router.get('/agents', (_req, res) => {
5
+ res.json(listAgentsWithCommands());
6
+ });
7
+ router.get('/all', (_req, res) => {
8
+ res.json(listAllCommands());
9
+ });
10
+ router.get('/:agentId', (req, res) => {
11
+ const agentId = typeof req.params.agentId === 'string' ? req.params.agentId : req.params.agentId[0];
12
+ res.json(listCommands(agentId));
13
+ });
14
+ router.get('/:agentId/:name', (req, res) => {
15
+ const agentId = typeof req.params.agentId === 'string' ? req.params.agentId : req.params.agentId[0];
16
+ const name = typeof req.params.name === 'string' ? req.params.name : req.params.name[0];
17
+ const group = req.query.group;
18
+ const cmd = getCommand(agentId, name, group);
19
+ if (!cmd) {
20
+ res.status(404).json({ error: 'Command not found' });
21
+ return;
22
+ }
23
+ res.json(cmd);
24
+ });
25
+ router.post('/:agentId', (req, res) => {
26
+ const agentId = typeof req.params.agentId === 'string' ? req.params.agentId : req.params.agentId[0];
27
+ const { name, content, group } = req.body;
28
+ if (!name || !content) {
29
+ res.status(400).json({ error: 'name and content required' });
30
+ return;
31
+ }
32
+ res.json(createCommand(agentId, name, content, group));
33
+ });
34
+ router.put('/:agentId/:name', (req, res) => {
35
+ const agentId = typeof req.params.agentId === 'string' ? req.params.agentId : req.params.agentId[0];
36
+ const name = typeof req.params.name === 'string' ? req.params.name : req.params.name[0];
37
+ const { content, group } = req.body;
38
+ if (!content) {
39
+ res.status(400).json({ error: 'content required' });
40
+ return;
41
+ }
42
+ const cmd = updateCommand(agentId, name, content, group);
43
+ if (!cmd) {
44
+ res.status(404).json({ error: 'Command not found' });
45
+ return;
46
+ }
47
+ res.json(cmd);
48
+ });
49
+ router.delete('/:agentId/:name', (req, res) => {
50
+ const agentId = typeof req.params.agentId === 'string' ? req.params.agentId : req.params.agentId[0];
51
+ const name = typeof req.params.name === 'string' ? req.params.name : req.params.name[0];
52
+ const group = req.query.group;
53
+ const ok = deleteCommand(agentId, name, group);
54
+ if (!ok) {
55
+ res.status(404).json({ error: 'Command not found' });
56
+ return;
57
+ }
58
+ res.json({ success: true });
59
+ });
60
+ router.post('/:agentId/:name/apply', (req, res) => {
61
+ const agentId = typeof req.params.agentId === 'string' ? req.params.agentId : req.params.agentId[0];
62
+ const name = typeof req.params.name === 'string' ? req.params.name : req.params.name[0];
63
+ const { group, agentIds } = req.body;
64
+ if (!Array.isArray(agentIds) || agentIds.length === 0) {
65
+ res.status(400).json({ error: 'agentIds required' });
66
+ return;
67
+ }
68
+ const applied = applyCommandToAgents(agentId, name, group || '', agentIds);
69
+ res.json({ applied });
70
+ });
71
+ export default router;
72
+ //# sourceMappingURL=agent-commands.js.map
@@ -143,11 +143,17 @@ function prepareSse(res) {
143
143
  res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
144
144
  res.setHeader('Cache-Control', 'no-cache, no-transform');
145
145
  res.setHeader('Connection', 'keep-alive');
146
+ res.setHeader('X-Accel-Buffering', 'no');
146
147
  res.flushHeaders?.();
148
+ res.socket?.setNoDelay?.(true);
147
149
  }
148
150
  function writeSse(res, event, data) {
149
151
  res.write(`event: ${event}\n`);
150
152
  res.write(`data: ${JSON.stringify(data)}\n\n`);
153
+ // Force flush — compression middleware and some proxies buffer small writes
154
+ const flushable = res;
155
+ if (typeof flushable.flush === 'function')
156
+ flushable.flush();
151
157
  }
152
158
  function resolveUserPrompt(body) {
153
159
  const direct = body.prompt ?? body.message;
@@ -1,6 +1,6 @@
1
1
  import { Router } from 'express';
2
2
  import * as agentService from '../services/agent.js';
3
- import { generateAgentDesign } from '../agents/agent-designer.js';
3
+ import { generateAgentDesign, optimizeAgentPrompt } from '../agents/agent-designer.js';
4
4
  const router = Router({ mergeParams: true });
5
5
  router.get('/usage/dashboard', (req, res) => {
6
6
  const days = Number(req.query.days ?? 30);
@@ -36,6 +36,20 @@ router.post('/presets/generate', async (req, res) => {
36
36
  res.status(400).json({ error: err instanceof Error ? err.message : 'agent generation failed' });
37
37
  }
38
38
  });
39
+ router.post('/presets/optimize-prompt', async (req, res) => {
40
+ const { prompt, currentPrompt } = req.body;
41
+ if (!prompt?.trim()) {
42
+ res.status(400).json({ error: 'prompt is required' });
43
+ return;
44
+ }
45
+ try {
46
+ res.json(await optimizeAgentPrompt(prompt, currentPrompt ?? ''));
47
+ }
48
+ catch (err) {
49
+ console.error('[agent-designer] optimize failed', err);
50
+ res.status(400).json({ error: err instanceof Error ? err.message : 'prompt optimization failed' });
51
+ }
52
+ });
39
53
  router.post('/presets/test-connection', async (req, res) => {
40
54
  const workspaceId = req.params.id;
41
55
  const data = req.body;