@agent-spaces/server 0.2.5 → 0.2.7

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 (266) hide show
  1. package/dist/adapters/agent-runtime.js +4 -0
  2. package/dist/adapters/langchain-runtime.js +207 -0
  3. package/dist/app.js +6 -0
  4. package/dist/package.json +5 -1
  5. package/dist/routes/agent-sse.js +201 -0
  6. package/dist/routes/notification.js +31 -0
  7. package/dist/routes/search.js +40 -0
  8. package/dist/services/agent.js +1 -1
  9. package/dist/services/notification-center.js +52 -0
  10. package/dist/services/notification-hub/events.js +32 -0
  11. package/dist/services/search.js +211 -0
  12. package/dist/web/404.html +1 -1
  13. package/dist/web/__next.__PAGE__.txt +4 -4
  14. package/dist/web/__next._full.txt +26 -22
  15. package/dist/web/__next._head.txt +4 -4
  16. package/dist/web/__next._index.txt +13 -9
  17. package/dist/web/__next._tree.txt +2 -2
  18. package/dist/web/_next/static/chunks/0-a17~4piefz_.js +1 -0
  19. package/dist/web/_next/static/chunks/0-utii~i2fncd.js +678 -0
  20. package/dist/web/_next/static/chunks/0-y43ovhjaeo_.js +1 -0
  21. package/dist/web/_next/static/chunks/0.-4-1p34akka.js +1 -0
  22. package/dist/web/_next/static/chunks/0.335m_d-cd_g.js +1 -0
  23. package/dist/web/_next/static/chunks/0.hcqmse3gu~-.js +1 -0
  24. package/dist/web/_next/static/chunks/0.~n0s82y4zh_.css +1 -0
  25. package/dist/web/_next/static/chunks/002-dxhags4dj.js +2 -0
  26. package/dist/web/_next/static/chunks/00n.pmqcipjyd.js +1 -0
  27. package/dist/web/_next/static/chunks/01pn1cm5v501e.js +1 -0
  28. package/dist/web/_next/static/chunks/02duknlg3rgjp.js +1 -0
  29. package/dist/web/_next/static/chunks/02ucy02hzl4vo.js +1 -0
  30. package/dist/web/_next/static/chunks/02wkg1tjv~_t2.js +1 -0
  31. package/dist/web/_next/static/chunks/02~6z3ja20ogz.js +1 -0
  32. package/dist/web/_next/static/chunks/0358_-ep6szf..js +1 -0
  33. package/dist/web/_next/static/chunks/035l5i6hvxwm4.js +1 -0
  34. package/dist/web/_next/static/chunks/03jy~o.gx._e-.js +3 -0
  35. package/dist/web/_next/static/chunks/03sd~wmqw.9gc.js +3 -0
  36. package/dist/web/_next/static/chunks/04zxh2e-4~-xs.js +1 -0
  37. package/dist/web/_next/static/chunks/05gu5s~8nh2wc.js +1 -0
  38. package/dist/web/_next/static/chunks/05irsi.vzfofq.js +1 -0
  39. package/dist/web/_next/static/chunks/05tbg-zp1~2c_.js +1 -0
  40. package/dist/web/_next/static/chunks/05tqezrd9jb7y.js +1 -0
  41. package/dist/web/_next/static/chunks/060_mn7wcaphv.js +1 -0
  42. package/dist/web/_next/static/chunks/06vb1.5j-tuzp.js +1 -0
  43. package/dist/web/_next/static/chunks/06xd5s9wkyu0_.js +1 -0
  44. package/dist/web/_next/static/chunks/07bs5ds68tg38.js +2 -0
  45. package/dist/web/_next/static/chunks/082lp-xqv7r.-.js +183 -0
  46. package/dist/web/_next/static/chunks/09j3~tko9ep2k.js +1 -0
  47. package/dist/web/_next/static/chunks/09qqxs3eg7e46.js +1 -0
  48. package/dist/web/_next/static/chunks/0_-20qzivafad.js +1 -0
  49. package/dist/web/_next/static/chunks/0_gm.-h0brxaf.js +1 -0
  50. package/dist/web/_next/static/chunks/0_yb4aw3n-lks.js +1 -0
  51. package/dist/web/_next/static/chunks/0ag4p6m9rze~n.js +1 -0
  52. package/dist/web/_next/static/chunks/0ay4fv-nniykh.css +10 -0
  53. package/dist/web/_next/static/chunks/0b9oy9dat2g6l.js +1 -0
  54. package/dist/web/_next/static/chunks/0bm-i9rs31uos.js +1 -0
  55. package/dist/web/_next/static/chunks/0brr7g0p8esun.js +1 -0
  56. package/dist/web/_next/static/chunks/0ck3is68a9586.js +1 -0
  57. package/dist/web/_next/static/chunks/0csqa76ud3h4d.js +1 -0
  58. package/dist/web/_next/static/chunks/0d4o_4v0-e97a.js +1 -0
  59. package/dist/web/_next/static/chunks/0d5sgidwe_pcg.js +1 -0
  60. package/dist/web/_next/static/chunks/0d8lm7uud1uuc.js +1 -0
  61. package/dist/web/_next/static/chunks/0extjypm.j7dp.js +1 -0
  62. package/dist/web/_next/static/chunks/0f0-0to._hvah.js +1 -0
  63. package/dist/web/_next/static/chunks/0f6vrjsdauiwl.js +2 -0
  64. package/dist/web/_next/static/chunks/0g3ypwmflp2mx.js +1 -0
  65. package/dist/web/_next/static/chunks/0ggkvx5v53lvc.js +1 -0
  66. package/dist/web/_next/static/chunks/0ghe5b2-kyi7-.js +1 -0
  67. package/dist/web/_next/static/chunks/0gkx0n_plricw.js +1 -0
  68. package/dist/web/_next/static/chunks/0gqhau02~fg83.js +1 -0
  69. package/dist/web/_next/static/chunks/0gtbd3yt7y1gx.js +1 -0
  70. package/dist/web/_next/static/chunks/0h5q90v_js2e0.js +1 -0
  71. package/dist/web/_next/static/chunks/0hu5nx-z4fj7v.js +1 -0
  72. package/dist/web/_next/static/chunks/0i0psxcvhdp9w.js +1 -0
  73. package/dist/web/_next/static/chunks/0jm496tvvtjqx.js +1 -0
  74. package/dist/web/_next/static/chunks/{03jbh7ud0jw~g.js → 0jmsjlawtg6oa.js} +1 -1
  75. package/dist/web/_next/static/chunks/0ju52ko_nh7gi.js +1 -0
  76. package/dist/web/_next/static/chunks/0jxj3xh399qcn.js +1 -0
  77. package/dist/web/_next/static/chunks/0kp1m06cg897x.js +1 -0
  78. package/dist/web/_next/static/chunks/0ku310xxoin~a.js +1 -0
  79. package/dist/web/_next/static/chunks/0kyv5yika-41a.js +1 -0
  80. package/dist/web/_next/static/chunks/0l9n6jio6olx1.js +1 -0
  81. package/dist/web/_next/static/chunks/0lkfsj~tpr.u8.js +1 -0
  82. package/dist/web/_next/static/chunks/0m9t_esdrum7w.js +1 -0
  83. package/dist/web/_next/static/chunks/0n.gs01bd1s_w.js +1 -0
  84. package/dist/web/_next/static/chunks/0n0j7tw_y1qk2.js +1 -0
  85. package/dist/web/_next/static/chunks/0n1jhw8ksl5a0.js +1 -0
  86. package/dist/web/_next/static/chunks/0nq24vo2_9kp4.js +1 -0
  87. package/dist/web/_next/static/chunks/0p~x_p9pgpnmh.js +1 -0
  88. package/dist/web/_next/static/chunks/0q01i3ypmm4rg.js +1 -0
  89. package/dist/web/_next/static/chunks/0q38twk.8uk88.js +1 -0
  90. package/dist/web/_next/static/chunks/0q7h6v.~-hcln.js +1 -0
  91. package/dist/web/_next/static/chunks/0qe5wvs8fe9ip.js +1 -0
  92. package/dist/web/_next/static/chunks/0qtd.b3zhzf38.js +1 -0
  93. package/dist/web/_next/static/chunks/0qxk21zejhdbm.js +1 -0
  94. package/dist/web/_next/static/chunks/0rfeqjw8k-viv.js +1 -0
  95. package/dist/web/_next/static/chunks/0rs7_x_.71x1k.js +1 -0
  96. package/dist/web/_next/static/chunks/0rx7dmtxo4d6p.js +1 -0
  97. package/dist/web/_next/static/chunks/0sjpil-vio33w.js +1 -0
  98. package/dist/web/_next/static/chunks/0ss8hcog1_n1i.js +1 -0
  99. package/dist/web/_next/static/chunks/0sz5yhrzrrx-_.js +1 -0
  100. package/dist/web/_next/static/chunks/0t1pc5h0n0y3..js +1 -0
  101. package/dist/web/_next/static/chunks/0tirraeyj7nqp.js +3 -0
  102. package/dist/web/_next/static/chunks/0tkii3.lkbu3u.js +1 -0
  103. package/dist/web/_next/static/chunks/0tl6o7zu1j1r-.css +88 -0
  104. package/dist/web/_next/static/chunks/0u-29g8eo8dqk.js +7 -0
  105. package/dist/web/_next/static/chunks/0uiiaq~dh4jdx.js +1 -0
  106. package/dist/web/_next/static/chunks/0vemm6g3yufzf.js +1 -0
  107. package/dist/web/_next/static/chunks/0w-ftk2~z_w0f.js +1 -0
  108. package/dist/web/_next/static/chunks/0w52~a1.76e6y.js +298 -0
  109. package/dist/web/_next/static/chunks/0wm70l0hod.sw.js +1 -0
  110. package/dist/web/_next/static/chunks/0wz8~mvztv-vq.js +1 -0
  111. package/dist/web/_next/static/chunks/0xgw6nacc67av.js +1 -0
  112. package/dist/web/_next/static/chunks/0y.6d96hu3k6k.js +1 -0
  113. package/dist/web/_next/static/chunks/0y5ipuqgqfrob.js +8 -0
  114. package/dist/web/_next/static/chunks/0yuyx75r122y-.js +1 -0
  115. package/dist/web/_next/static/chunks/0zb1vymmeoc9o.js +1 -0
  116. package/dist/web/_next/static/chunks/0zj_8cn9~m2~g.js +1 -0
  117. package/dist/web/_next/static/chunks/0ztjb8i4tqvql.js +1 -0
  118. package/dist/web/_next/static/chunks/0zxtxecpqyoeh.js +1 -0
  119. package/dist/web/_next/static/chunks/100t886jj0x0x.js +1 -0
  120. package/dist/web/_next/static/chunks/11n6upyldh80p.js +1 -0
  121. package/dist/web/_next/static/chunks/11x5nw_8yy5d0.js +1 -0
  122. package/dist/web/_next/static/chunks/11~8.35p7_vky.js +1 -0
  123. package/dist/web/_next/static/chunks/12ae7z-332nqe.js +1 -0
  124. package/dist/web/_next/static/chunks/12sucjx_z8ftc.js +1 -0
  125. package/dist/web/_next/static/chunks/13vl889h_tb-~.js +1 -0
  126. package/dist/web/_next/static/chunks/13w2r5w~ikt_6.js +1 -0
  127. package/dist/web/_next/static/chunks/14iaqoj9-09g..js +2 -0
  128. package/dist/web/_next/static/chunks/14j.uts4xnh.u.js +1 -0
  129. package/dist/web/_next/static/chunks/14qoup0y_1j0t.js +1 -0
  130. package/dist/web/_next/static/chunks/15_n_khh8cuax.js +5 -0
  131. package/dist/web/_next/static/chunks/15abz60ucjy3t.js +1 -0
  132. package/dist/web/_next/static/chunks/15gjlpek9nxo4.js +1 -0
  133. package/dist/web/_next/static/chunks/15s4k5wdbcagm.js +1 -0
  134. package/dist/web/_next/static/chunks/178fuwl3hg67c.js +1372 -0
  135. package/dist/web/_next/static/chunks/17a_qp~nx3_r1.js +1 -0
  136. package/dist/web/_next/static/chunks/184ukbf139de1.js +1 -0
  137. package/dist/web/_next/static/chunks/186mu8y9np1sy.js +1 -0
  138. package/dist/web/_next/static/chunks/turbopack-0-j2hpomozegf.js +1 -0
  139. package/dist/web/_next/static/chunks/turbopack-0_zn00zg5kmqz.js +1 -0
  140. package/dist/web/_next/static/chunks/turbopack-0l~pq1hytx49z.js +1 -0
  141. package/dist/web/_next/static/chunks/turbopack-0rrhp9pbrv0p3.js +1 -0
  142. package/dist/web/_next/static/chunks/turbopack-worker-0sjn--fhq~1cg.js +1 -0
  143. package/dist/web/_next/static/media/codicon.0~p3po1x7ylnt.ttf +0 -0
  144. package/dist/web/_next/static/media/css.worker.0wg41onjsu4l8.js +8 -0
  145. package/dist/web/_next/static/media/html.worker.13ihv~a-85iob.js +8 -0
  146. package/dist/web/_next/static/media/json.worker.15xmpow_4mdxu.js +8 -0
  147. package/dist/web/_next/static/media/ts.worker.05mkk_j-~tju1.js +14 -0
  148. package/dist/web/_not-found/__next._full.txt +30 -26
  149. package/dist/web/_not-found/__next._head.txt +4 -4
  150. package/dist/web/_not-found/__next._index.txt +13 -9
  151. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  152. package/dist/web/_not-found/__next._not-found.txt +3 -3
  153. package/dist/web/_not-found/__next._tree.txt +2 -2
  154. package/dist/web/_not-found.html +1 -1
  155. package/dist/web/_not-found.txt +30 -26
  156. package/dist/web/index.html +1 -1
  157. package/dist/web/index.txt +26 -22
  158. package/dist/web/login/__next._full.txt +29 -25
  159. package/dist/web/login/__next._head.txt +4 -4
  160. package/dist/web/login/__next._index.txt +13 -9
  161. package/dist/web/login/__next._tree.txt +2 -2
  162. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  163. package/dist/web/login/__next.login.txt +3 -3
  164. package/dist/web/login.html +1 -1
  165. package/dist/web/login.txt +29 -25
  166. package/dist/web/settings/__next._full.txt +34 -28
  167. package/dist/web/settings/__next._head.txt +4 -4
  168. package/dist/web/settings/__next._index.txt +13 -9
  169. package/dist/web/settings/__next._tree.txt +2 -2
  170. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  171. package/dist/web/settings/__next.settings.txt +5 -5
  172. package/dist/web/settings/agents/__next._full.txt +35 -31
  173. package/dist/web/settings/agents/__next._head.txt +4 -4
  174. package/dist/web/settings/agents/__next._index.txt +13 -9
  175. package/dist/web/settings/agents/__next._tree.txt +2 -2
  176. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  177. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  178. package/dist/web/settings/agents/__next.settings.txt +5 -5
  179. package/dist/web/settings/agents.html +1 -1
  180. package/dist/web/settings/agents.txt +35 -31
  181. package/dist/web/settings/mcps/__next._full.txt +35 -31
  182. package/dist/web/settings/mcps/__next._head.txt +4 -4
  183. package/dist/web/settings/mcps/__next._index.txt +13 -9
  184. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  185. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  186. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  187. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  188. package/dist/web/settings/mcps.html +1 -1
  189. package/dist/web/settings/mcps.txt +35 -31
  190. package/dist/web/settings/models/__next._full.txt +35 -31
  191. package/dist/web/settings/models/__next._head.txt +4 -4
  192. package/dist/web/settings/models/__next._index.txt +13 -9
  193. package/dist/web/settings/models/__next._tree.txt +2 -2
  194. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  195. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  196. package/dist/web/settings/models/__next.settings.txt +5 -5
  197. package/dist/web/settings/models.html +1 -1
  198. package/dist/web/settings/models.txt +35 -31
  199. package/dist/web/settings/providers/__next._full.txt +35 -31
  200. package/dist/web/settings/providers/__next._head.txt +4 -4
  201. package/dist/web/settings/providers/__next._index.txt +13 -9
  202. package/dist/web/settings/providers/__next._tree.txt +2 -2
  203. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  204. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  205. package/dist/web/settings/providers/__next.settings.txt +5 -5
  206. package/dist/web/settings/providers.html +1 -1
  207. package/dist/web/settings/providers.txt +35 -31
  208. package/dist/web/settings/skills/__next._full.txt +35 -31
  209. package/dist/web/settings/skills/__next._head.txt +4 -4
  210. package/dist/web/settings/skills/__next._index.txt +13 -9
  211. package/dist/web/settings/skills/__next._tree.txt +2 -2
  212. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  213. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  214. package/dist/web/settings/skills/__next.settings.txt +5 -5
  215. package/dist/web/settings/skills.html +1 -1
  216. package/dist/web/settings/skills.txt +35 -31
  217. package/dist/web/settings.html +1 -1
  218. package/dist/web/settings.txt +34 -28
  219. package/dist/web/workflows/__next._full.txt +31 -27
  220. package/dist/web/workflows/__next._head.txt +4 -4
  221. package/dist/web/workflows/__next._index.txt +13 -9
  222. package/dist/web/workflows/__next._tree.txt +2 -2
  223. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  224. package/dist/web/workflows/__next.workflows.txt +3 -3
  225. package/dist/web/workflows.html +1 -1
  226. package/dist/web/workflows.txt +31 -27
  227. package/dist/web/workspace/_/__next._full.txt +36 -25
  228. package/dist/web/workspace/_/__next._head.txt +4 -4
  229. package/dist/web/workspace/_/__next._index.txt +13 -9
  230. package/dist/web/workspace/_/__next._tree.txt +4 -2
  231. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +5 -3
  232. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  233. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  234. package/dist/web/workspace/_.html +1 -1
  235. package/dist/web/workspace/_.txt +36 -25
  236. package/dist/web/workspaces/__next._full.txt +30 -25
  237. package/dist/web/workspaces/__next._head.txt +4 -4
  238. package/dist/web/workspaces/__next._index.txt +13 -9
  239. package/dist/web/workspaces/__next._tree.txt +2 -2
  240. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  241. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  242. package/dist/web/workspaces.html +1 -1
  243. package/dist/web/workspaces.txt +30 -25
  244. package/package.json +15 -10
  245. package/dist/web/_next/static/chunks/0.4.g.8yf4rs0.js +0 -3
  246. package/dist/web/_next/static/chunks/02m_-ngl9w8co.js +0 -1
  247. package/dist/web/_next/static/chunks/08fqgb~~a~4ck.js +0 -1
  248. package/dist/web/_next/static/chunks/0an2wvcgfh4yg.js +0 -2
  249. package/dist/web/_next/static/chunks/0dpsi68hqjuon.js +0 -1
  250. package/dist/web/_next/static/chunks/0fwvdy-ml8wpk.js +0 -1
  251. package/dist/web/_next/static/chunks/0ib18ul605e~a.js +0 -1
  252. package/dist/web/_next/static/chunks/0j282o9qw3fnz.css +0 -1
  253. package/dist/web/_next/static/chunks/0lgk.w8y-3ppq.js +0 -1
  254. package/dist/web/_next/static/chunks/0o4m39hw4fb_j.js +0 -1
  255. package/dist/web/_next/static/chunks/0qyjxx0y7rzuu.js +0 -1
  256. package/dist/web/_next/static/chunks/0rrdur.v1a5r7.js +0 -1
  257. package/dist/web/_next/static/chunks/0spo.tmfeas-o.js +0 -1
  258. package/dist/web/_next/static/chunks/0u88ij9dqqh~-.js +0 -1
  259. package/dist/web/_next/static/chunks/0v4atb3hml5fn.js +0 -8
  260. package/dist/web/_next/static/chunks/0vwzzneidqvbo.js +0 -2
  261. package/dist/web/_next/static/chunks/11n16hogah-5..js +0 -1
  262. package/dist/web/_next/static/chunks/160ji-.dfvm20.js +0 -1
  263. package/dist/web/_next/static/chunks/18bgtswajrg9e.js +0 -179
  264. /package/dist/web/_next/static/{CW6M37hm3VxkelXPKGgw3 → hGfVPALj5nbwettayL9DY}/_buildManifest.js +0 -0
  265. /package/dist/web/_next/static/{CW6M37hm3VxkelXPKGgw3 → hGfVPALj5nbwettayL9DY}/_clientMiddlewareManifest.js +0 -0
  266. /package/dist/web/_next/static/{CW6M37hm3VxkelXPKGgw3 → hGfVPALj5nbwettayL9DY}/_ssgManifest.js +0 -0
@@ -3,9 +3,11 @@
3
3
  */
4
4
  import { ClaudeCodeRuntime } from './claude-code-runtime/index.js';
5
5
  import { CodexRuntime } from './codex-runtime.js';
6
+ import { LangChainRuntime } from './langchain-runtime.js';
6
7
  import { OpenAgentSdkRuntime } from './open-agent-sdk-runtime.js';
7
8
  export { ClaudeCodeRuntime } from './claude-code-runtime/index.js';
8
9
  export { CodexRuntime } from './codex-runtime.js';
10
+ export { LangChainRuntime } from './langchain-runtime.js';
9
11
  export { OpenAgentSdkRuntime } from './open-agent-sdk-runtime.js';
10
12
  export function createAgentRuntime(configOrProvider = {}, model) {
11
13
  const config = typeof configOrProvider === 'string'
@@ -18,6 +20,8 @@ export function createAgentRuntime(configOrProvider = {}, model) {
18
20
  return new ClaudeCodeRuntime(config);
19
21
  case 'codex':
20
22
  return new CodexRuntime(config);
23
+ case 'langchain':
24
+ return new LangChainRuntime(config);
21
25
  }
22
26
  }
23
27
  //# sourceMappingURL=agent-runtime.js.map
@@ -0,0 +1,207 @@
1
+ import { createAgent, initChatModel, tool } from 'langchain';
2
+ import { z } from 'zod';
3
+ import { summarizeResult } from './agent-runtime-types.js';
4
+ /**
5
+ * Runtime backed by LangChain.js.
6
+ * Uses LangChain's provider-neutral createAgent API and adapts Agent Spaces tools.
7
+ */
8
+ export class LangChainRuntime {
9
+ config;
10
+ abortController = null;
11
+ constructor(config = {}) {
12
+ this.config = config;
13
+ }
14
+ async execute(prompt, workingDir, options) {
15
+ this.abortController = new AbortController();
16
+ const output = [];
17
+ const cwd = workingDir || process.cwd();
18
+ const startTime = Date.now();
19
+ const d = (msg) => console.log(`[langchain] ${msg}`);
20
+ const model = buildModelIdentifier(this.config);
21
+ d(`starting | cwd=${cwd} provider=${this.config.provider ?? 'auto'} model=${model} baseURL=${this.config.baseURL ?? 'default'} maxTurns=${options?.maxTurns ?? '∞'} tools=${options?.functionTools?.map((runtimeTool) => runtimeTool.name).join(',') || '-'} sandboxDirs=${options?.sandboxDirs?.join(',') ?? '-'}`);
22
+ d(`prompt: ${prompt.slice(0, 300)}${prompt.length > 300 ? '...' : ''}`);
23
+ try {
24
+ const chatModel = await withTemporaryEnv(buildProviderEnv(this.config), () => initChatModel(model, buildModelConfig(this.config)));
25
+ const agent = createAgent({
26
+ model: chatModel,
27
+ tools: buildLangChainTools(options?.functionTools, output, options),
28
+ systemPrompt: options?.systemPrompt,
29
+ });
30
+ const result = await agent.invoke({ messages: [{ role: 'user', content: prompt }] }, {
31
+ signal: this.abortController?.signal,
32
+ recursionLimit: options?.maxTurns ? Math.max(2, options.maxTurns * 2 + 1) : undefined,
33
+ });
34
+ const text = extractFinalText(result);
35
+ const usage = extractUsage(result);
36
+ if (text) {
37
+ output.push(text);
38
+ options?.onEvent?.({ type: 'output', line: text });
39
+ }
40
+ if (usage?.totalTokens || usage?.inputTokens || usage?.outputTokens) {
41
+ const usageLine = `[Usage] tokens=${usage.totalTokens ?? '-'} input=${usage.inputTokens ?? '-'} output=${usage.outputTokens ?? '-'}`;
42
+ output.push(usageLine);
43
+ options?.onEvent?.({ type: 'output', line: usageLine });
44
+ }
45
+ const elapsed = Date.now() - startTime;
46
+ d(`done ${elapsed}ms | tokens=${usage?.totalTokens ?? 'unknown'}`);
47
+ return {
48
+ success: true,
49
+ summary: summarizeResult(text),
50
+ artifacts: [],
51
+ output,
52
+ usage,
53
+ };
54
+ }
55
+ catch (err) {
56
+ const elapsed = Date.now() - startTime;
57
+ const message = err instanceof Error ? err.message : String(err);
58
+ d(`failed ${elapsed}ms | ${message}`);
59
+ if (err instanceof Error && err.stack)
60
+ console.error(err.stack);
61
+ return { success: false, summary: 'LangChain execution failed', artifacts: [], error: message, output };
62
+ }
63
+ finally {
64
+ this.abortController = null;
65
+ }
66
+ }
67
+ stop() {
68
+ this.abortController?.abort();
69
+ }
70
+ }
71
+ function buildLangChainTools(functionTools, output, options) {
72
+ if (!functionTools?.length)
73
+ return [];
74
+ return functionTools.map((runtimeTool) => tool(async (input) => {
75
+ const line = `Tool: ${runtimeTool.name} input=${JSON.stringify(input)}`;
76
+ output.push(line);
77
+ options?.onEvent?.({ type: 'tool_use', id: runtimeTool.name, name: runtimeTool.name, input, line });
78
+ const result = await runtimeTool.execute(input);
79
+ options?.onEvent?.({ type: 'tool_result', toolUseId: runtimeTool.name, result });
80
+ return result;
81
+ }, {
82
+ name: runtimeTool.name,
83
+ description: runtimeTool.description,
84
+ schema: z.object({}).passthrough(),
85
+ }));
86
+ }
87
+ function buildModelIdentifier(config) {
88
+ const model = config.model || 'gpt-4o-mini';
89
+ const provider = normalizeLangChainProvider(config.provider);
90
+ if (!provider || model.includes(':'))
91
+ return model;
92
+ return `${provider}:${model}`;
93
+ }
94
+ function normalizeLangChainProvider(provider) {
95
+ switch (provider) {
96
+ case 'anthropic-messages':
97
+ return 'anthropic';
98
+ case 'openai-chat-completions':
99
+ case 'openai-responses':
100
+ return 'openai';
101
+ case 'gemini-generate-content':
102
+ return 'google-genai';
103
+ default:
104
+ return undefined;
105
+ }
106
+ }
107
+ function buildModelConfig(config) {
108
+ return removeUndefined({
109
+ apiKey: config.apiKey,
110
+ api_key: config.apiKey,
111
+ baseURL: config.baseURL,
112
+ baseUrl: config.baseURL,
113
+ configuration: config.baseURL ? { baseURL: config.baseURL } : undefined,
114
+ });
115
+ }
116
+ function buildProviderEnv(config) {
117
+ const provider = normalizeLangChainProvider(config.provider);
118
+ return {
119
+ OPENAI_API_KEY: provider === 'openai' ? config.apiKey : undefined,
120
+ OPENAI_BASE_URL: provider === 'openai' ? config.baseURL : undefined,
121
+ ANTHROPIC_API_KEY: provider === 'anthropic' ? config.apiKey : undefined,
122
+ ANTHROPIC_BASE_URL: provider === 'anthropic' ? config.baseURL : undefined,
123
+ GOOGLE_API_KEY: provider === 'google-genai' ? config.apiKey : undefined,
124
+ GEMINI_API_KEY: provider === 'google-genai' ? config.apiKey : undefined,
125
+ };
126
+ }
127
+ async function withTemporaryEnv(env, fn) {
128
+ const previous = new Map();
129
+ for (const [key, value] of Object.entries(env)) {
130
+ if (!value)
131
+ continue;
132
+ previous.set(key, process.env[key]);
133
+ process.env[key] = value;
134
+ }
135
+ try {
136
+ return await fn();
137
+ }
138
+ finally {
139
+ for (const [key, value] of previous) {
140
+ if (value === undefined) {
141
+ delete process.env[key];
142
+ }
143
+ else {
144
+ process.env[key] = value;
145
+ }
146
+ }
147
+ }
148
+ }
149
+ function extractFinalText(result) {
150
+ const messages = isRecord(result) && Array.isArray(result.messages) ? result.messages : [];
151
+ const last = [...messages].reverse().find((message) => isRecord(message) && getMessageType(message) === 'ai')
152
+ ?? messages.at(-1);
153
+ return stringifyMessageContent(isRecord(last) ? last.content : undefined);
154
+ }
155
+ function getMessageType(message) {
156
+ const getType = message._getType;
157
+ return typeof getType === 'function' ? getType.call(message) : undefined;
158
+ }
159
+ function stringifyMessageContent(content) {
160
+ if (typeof content === 'string')
161
+ return content;
162
+ if (!Array.isArray(content))
163
+ return content == null ? '' : JSON.stringify(content);
164
+ return content
165
+ .map((part) => {
166
+ if (typeof part === 'string')
167
+ return part;
168
+ if (!isRecord(part))
169
+ return '';
170
+ if (typeof part.text === 'string')
171
+ return part.text;
172
+ if (typeof part.content === 'string')
173
+ return part.content;
174
+ return '';
175
+ })
176
+ .filter(Boolean)
177
+ .join('\n');
178
+ }
179
+ function extractUsage(result) {
180
+ const messages = isRecord(result) && Array.isArray(result.messages) ? result.messages : [];
181
+ for (const message of [...messages].reverse()) {
182
+ if (!isRecord(message))
183
+ continue;
184
+ const usage = isRecord(message.usage_metadata)
185
+ ? message.usage_metadata
186
+ : isRecord(message.response_metadata) && isRecord(message.response_metadata.tokenUsage)
187
+ ? message.response_metadata.tokenUsage
188
+ : undefined;
189
+ if (!usage)
190
+ continue;
191
+ const inputTokens = numberFrom(usage.input_tokens ?? usage.promptTokens ?? usage.prompt_tokens);
192
+ const outputTokens = numberFrom(usage.output_tokens ?? usage.completionTokens ?? usage.completion_tokens);
193
+ const totalTokens = numberFrom(usage.total_tokens ?? usage.totalTokens) ?? (inputTokens !== undefined || outputTokens !== undefined ? (inputTokens ?? 0) + (outputTokens ?? 0) : undefined);
194
+ return removeUndefined({ inputTokens, outputTokens, totalTokens });
195
+ }
196
+ return undefined;
197
+ }
198
+ function numberFrom(value) {
199
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
200
+ }
201
+ function isRecord(value) {
202
+ return Boolean(value && typeof value === 'object');
203
+ }
204
+ function removeUndefined(value) {
205
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
206
+ }
207
+ //# sourceMappingURL=langchain-runtime.js.map
package/dist/app.js CHANGED
@@ -24,6 +24,9 @@ import commandRouter from './routes/command.js';
24
24
  import skillRouter from './routes/skill.js';
25
25
  import mcpRouter from './routes/mcp.js';
26
26
  import subscriptionRouter from './routes/subscription.js';
27
+ import agentSseRouter from './routes/agent-sse.js';
28
+ import searchRouter from './routes/search.js';
29
+ import notificationRouter from './routes/notification.js';
27
30
  import { authMiddleware, verifyToken } from './middleware/auth.js';
28
31
  import { handleConnection } from './ws/handler.js';
29
32
  import { startScheduler, stopScheduler } from './agents/scheduler-agent.js';
@@ -41,6 +44,7 @@ const app = express();
41
44
  const CORS_ORIGIN = process.env.CORS_ORIGIN || '*';
42
45
  app.use(cors({ origin: CORS_ORIGIN }));
43
46
  app.use(express.json({ limit: '50mb' }));
47
+ app.use('/api/agent-sse', agentSseRouter);
44
48
  app.use('/api', authMiddleware);
45
49
  // Serve static files from public/
46
50
  const publicDir = resolveRuntimeDir('public');
@@ -80,6 +84,8 @@ app.use('/api/workspaces/:id/commands', commandRouter);
80
84
  app.use('/api/workspaces/:id/agents', agentRouter);
81
85
  app.use('/api/workspaces/:id/tasks', taskRouter);
82
86
  app.use('/api/workspaces/:id/git', gitRouter);
87
+ app.use('/api/workspaces/:id/search', searchRouter);
88
+ app.use('/api/workspaces/:id/notifications', notificationRouter);
83
89
  app.use('/api/agents', agentRouter);
84
90
  app.use('/api', llmRouter);
85
91
  app.use('/api/folder', folderRouter);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-spaces/server",
3
- "version": "0.2.4",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "main": "app.js",
6
6
  "packageManager": "pnpm@10.17.1",
@@ -12,11 +12,15 @@
12
12
  "@agent-spaces/shared": "file:./shared",
13
13
  "@anthropic-ai/claude-agent-sdk": "^0.2.126",
14
14
  "@codeany/open-agent-sdk": "^0.2.1",
15
+ "@langchain/anthropic": "^1.3.29",
16
+ "@langchain/google-genai": "^2.1.30",
17
+ "@langchain/openai": "^1.4.5",
15
18
  "@larksuiteoapi/node-sdk": "^1.62.1",
16
19
  "@openai/codex-sdk": "^0.128.0",
17
20
  "cors": "^2.8.5",
18
21
  "dotenv": "^17.4.2",
19
22
  "express": "^5.1.0",
23
+ "langchain": "^1.4.0",
20
24
  "node-pty": "^1.1.0",
21
25
  "simple-git": "^3.36.0",
22
26
  "uuid": "^11.1.0",
@@ -0,0 +1,201 @@
1
+ import { Router } from 'express';
2
+ import { createAgentRuntime } from '../adapters/agent-runtime.js';
3
+ import { verifyToken } from '../middleware/auth.js';
4
+ import * as agentService from '../services/agent.js';
5
+ import * as workspaceService from '../services/workspace.js';
6
+ import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
7
+ import { buildAgentPrompt } from '../ws/agent-prompt.js';
8
+ const router = Router();
9
+ router.post('/run', async (req, res) => {
10
+ const body = req.body;
11
+ if (!verifyRequestKey(req, body)) {
12
+ res.status(401).json({ error: 'Unauthorized' });
13
+ return;
14
+ }
15
+ const workspaceId = resolveWorkspaceId(body.workspaceId);
16
+ if (!workspaceId) {
17
+ res.status(400).json({ error: 'workspaceId is required when no workspace exists' });
18
+ return;
19
+ }
20
+ const workspace = workspaceService.getById(workspaceId);
21
+ if (!workspace) {
22
+ res.status(404).json({ error: 'workspace not found' });
23
+ return;
24
+ }
25
+ const agentConfigId = (body.agentId ?? body.agentid)?.trim();
26
+ if (!agentConfigId) {
27
+ res.status(400).json({ error: 'agentid is required' });
28
+ return;
29
+ }
30
+ const preset = agentService.listPresets(workspaceId).find((agent) => agent.id === agentConfigId);
31
+ if (!preset || preset.enabled === false) {
32
+ res.status(404).json({ error: 'agent preset not found' });
33
+ return;
34
+ }
35
+ const userPrompt = resolveUserPrompt(body);
36
+ if (!userPrompt) {
37
+ res.status(400).json({ error: 'message, prompt, or messages is required' });
38
+ return;
39
+ }
40
+ const session = agentService.create(workspaceId, preset.role, preset.id);
41
+ const startTime = Date.now();
42
+ const mcpConfig = body.mcps ?? body.mcp ?? preset.mcps;
43
+ const mcpServers = agentService.getMcpServers(mcpConfig);
44
+ const requestedSkills = normalizeSkills(body.skills ?? body.skill) ?? preset.skills;
45
+ const configDir = agentService.getAgentConfigDir(workspaceId, { ...preset, skills: requestedSkills });
46
+ const skills = agentService.getAvailableSkillNames(configDir, requestedSkills);
47
+ const output = [];
48
+ let completed = false;
49
+ prepareSse(res);
50
+ writeSse(res, 'session', { session, workspaceId });
51
+ const runtime = createAgentRuntime({
52
+ kind: preset.runtimeKind,
53
+ provider: preset.modelProvider,
54
+ model: preset.modelId,
55
+ apiKey: preset.apiKey,
56
+ baseURL: getRuntimeBaseURL(preset.modelProvider, preset.apiBase),
57
+ adapterBaseURL: preset.apiBase,
58
+ ...getThinkingRuntimeConfig(preset),
59
+ });
60
+ res.on('close', () => {
61
+ if (!completed && !res.writableEnded)
62
+ runtime.stop();
63
+ });
64
+ try {
65
+ agentService.updateStatus(workspaceId, session.id, 'active');
66
+ writeSse(res, 'status', { agentId: session.id, status: 'active' });
67
+ const result = await runtime.execute(buildAgentPrompt(workspaceId, body.systemPrompt ?? preset.systemPrompt, userPrompt, normalizeMessages(body.messages), {
68
+ mcpServers: Object.keys(mcpServers ?? {}),
69
+ skills,
70
+ boundDirs: workspace.boundDirs,
71
+ builtInTools: [],
72
+ }), agentService.resolveWorkingDir(workspaceId, preset), {
73
+ maxTurns: normalizeMaxTurns(body.maxTurns),
74
+ mcpServers,
75
+ skills,
76
+ configDir,
77
+ sandboxDirs: preset.sandboxDirs,
78
+ onEvent: (event) => {
79
+ if (event.type === 'output')
80
+ output.push(event.line);
81
+ writeSse(res, event.type, serializeRuntimeEvent(event));
82
+ },
83
+ });
84
+ completed = true;
85
+ const displayOutput = output.length ? output : result.output;
86
+ agentService.complete(workspaceId, session.id, result.success ? undefined : result.error, {
87
+ runtime: preset.runtimeKind,
88
+ model: preset.modelId,
89
+ summary: result.summary,
90
+ output: displayOutput,
91
+ durationMs: Date.now() - startTime,
92
+ usage: result.usage,
93
+ costUsd: result.costUsd,
94
+ });
95
+ writeSse(res, 'done', {
96
+ agentId: session.id,
97
+ success: result.success,
98
+ summary: result.summary,
99
+ artifacts: result.artifacts,
100
+ error: result.error,
101
+ output: displayOutput,
102
+ usage: result.usage,
103
+ costUsd: result.costUsd,
104
+ durationMs: Date.now() - startTime,
105
+ });
106
+ res.end();
107
+ }
108
+ catch (err) {
109
+ completed = true;
110
+ const error = err instanceof Error ? err.message : String(err);
111
+ agentService.complete(workspaceId, session.id, error, {
112
+ runtime: preset.runtimeKind,
113
+ model: preset.modelId,
114
+ summary: error,
115
+ output: output.length ? output : [error],
116
+ durationMs: Date.now() - startTime,
117
+ });
118
+ writeSse(res, 'error', { agentId: session.id, error });
119
+ res.end();
120
+ }
121
+ });
122
+ function verifyRequestKey(req, body) {
123
+ const auth = req.headers.authorization;
124
+ const bearer = auth?.startsWith('Bearer ') ? auth.slice(7) : undefined;
125
+ const headerKey = typeof req.headers['x-agent-spaces-key'] === 'string'
126
+ ? req.headers['x-agent-spaces-key']
127
+ : undefined;
128
+ return verifyToken(body.key ?? bearer ?? headerKey ?? null);
129
+ }
130
+ function resolveWorkspaceId(workspaceId) {
131
+ const explicit = workspaceId?.trim();
132
+ if (explicit)
133
+ return explicit;
134
+ return workspaceService.getAll()[0]?.id;
135
+ }
136
+ function prepareSse(res) {
137
+ res.status(200);
138
+ res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
139
+ res.setHeader('Cache-Control', 'no-cache, no-transform');
140
+ res.setHeader('Connection', 'keep-alive');
141
+ res.flushHeaders?.();
142
+ }
143
+ function writeSse(res, event, data) {
144
+ res.write(`event: ${event}\n`);
145
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
146
+ }
147
+ function resolveUserPrompt(body) {
148
+ const direct = body.prompt ?? body.message;
149
+ if (typeof direct === 'string' && direct.trim())
150
+ return direct.trim();
151
+ const messages = normalizeMessages(body.messages);
152
+ const lastUserMessage = [...messages].reverse().find((message) => message.senderId === 'user');
153
+ return lastUserMessage?.content?.trim() ?? '';
154
+ }
155
+ function normalizeMessages(messages) {
156
+ if (!Array.isArray(messages))
157
+ return [];
158
+ return messages
159
+ .filter((message) => message && typeof message.content === 'string')
160
+ .map((message, index) => ({
161
+ id: `sse-message-${index}`,
162
+ channelId: 'sse',
163
+ senderId: message.senderId ?? 'user',
164
+ senderRole: message.senderRole,
165
+ content: message.content,
166
+ type: 'text',
167
+ status: message.status ?? 'completed',
168
+ createdAt: new Date(0).toISOString(),
169
+ updatedAt: new Date(0).toISOString(),
170
+ parts: message.parts,
171
+ }));
172
+ }
173
+ function normalizeSkills(input) {
174
+ if (!input)
175
+ return undefined;
176
+ const values = Array.isArray(input) ? input : [input];
177
+ return values.map((item) => String(item).trim()).filter(Boolean);
178
+ }
179
+ function normalizeMaxTurns(value) {
180
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : 100;
181
+ }
182
+ function serializeRuntimeEvent(event) {
183
+ if (event.type === 'tool_use') {
184
+ return {
185
+ type: event.type,
186
+ id: event.id,
187
+ name: event.name,
188
+ input: event.input,
189
+ line: event.line,
190
+ };
191
+ }
192
+ return event;
193
+ }
194
+ function getRuntimeBaseURL(provider, apiBase) {
195
+ if (provider === 'openai-responses-to-anthropic-messages'
196
+ || provider === 'openai-chat-completions-to-anthropic-messages')
197
+ return undefined;
198
+ return apiBase;
199
+ }
200
+ export default router;
201
+ //# sourceMappingURL=agent-sse.js.map
@@ -0,0 +1,31 @@
1
+ import { Router } from 'express';
2
+ import * as nc from '../services/notification-center.js';
3
+ const router = Router({ mergeParams: true });
4
+ router.get('/', (req, res) => {
5
+ const workspaceId = req.params.id;
6
+ res.json(nc.listNotifications(workspaceId));
7
+ });
8
+ router.put('/:notificationId/read', (req, res) => {
9
+ const workspaceId = req.params.id;
10
+ const notificationId = req.params.notificationId;
11
+ const n = nc.markRead(workspaceId, notificationId);
12
+ if (!n) {
13
+ res.status(404).json({ error: 'Not found' });
14
+ return;
15
+ }
16
+ res.json(n);
17
+ });
18
+ router.put('/read-all', (req, res) => {
19
+ const workspaceId = req.params.id;
20
+ const notifications = nc.listNotifications(workspaceId);
21
+ for (const n of notifications)
22
+ nc.markRead(workspaceId, n.id);
23
+ res.json({ ok: true });
24
+ });
25
+ router.delete('/', (req, res) => {
26
+ const workspaceId = req.params.id;
27
+ nc.clearAll(workspaceId);
28
+ res.json({ ok: true });
29
+ });
30
+ export default router;
31
+ //# sourceMappingURL=notification.js.map
@@ -0,0 +1,40 @@
1
+ import { Router } from 'express';
2
+ import * as searchService from '../services/search.js';
3
+ import * as fileService from '../services/file.js';
4
+ const router = Router({ mergeParams: true });
5
+ router.get('/code', async (req, res) => {
6
+ const ws = fileService.getWorkspace(req.params.id);
7
+ if (!ws) {
8
+ res.status(404).json({ error: 'Workspace not found' });
9
+ return;
10
+ }
11
+ const q = req.query.q;
12
+ if (!q) {
13
+ res.status(400).json({ error: 'q is required' });
14
+ return;
15
+ }
16
+ const results = await searchService.searchCode(ws, {
17
+ query: q,
18
+ regex: req.query.regex === 'true',
19
+ caseSensitive: req.query.caseSensitive === 'true',
20
+ filePattern: req.query.filePattern || undefined,
21
+ maxResults: parseInt(req.query.maxResults) || 200,
22
+ });
23
+ res.json({ results, total: results.length });
24
+ });
25
+ router.get('/files', async (req, res) => {
26
+ const ws = fileService.getWorkspace(req.params.id);
27
+ if (!ws) {
28
+ res.status(404).json({ error: 'Workspace not found' });
29
+ return;
30
+ }
31
+ const q = req.query.q;
32
+ if (!q) {
33
+ res.status(400).json({ error: 'q is required' });
34
+ return;
35
+ }
36
+ const results = await searchService.searchFiles(ws, q);
37
+ res.json({ results, total: results.length });
38
+ });
39
+ export default router;
40
+ //# sourceMappingURL=search.js.map
@@ -9,7 +9,7 @@ import { listChannels, updateChannel } from './channel.js';
9
9
  import { ensureDir, getDataDir } from '../storage/json-store.js';
10
10
  import { extractUsageFromOutput } from '../storage/usage.js';
11
11
  const DEFAULT_AGENT_ROLE = 'agent';
12
- const VALID_RUNTIME_KINDS = ['open-agent-sdk', 'claude-code', 'codex'];
12
+ const VALID_RUNTIME_KINDS = ['open-agent-sdk', 'claude-code', 'codex', 'langchain'];
13
13
  const VALID_TOOL_NAMES = new Set(BUILT_IN_AGENT_TOOLS.map((tool) => tool.name));
14
14
  const ANTHROPIC_BRIDGE_PROVIDERS = [
15
15
  'openai-responses-to-anthropic-messages',
@@ -0,0 +1,52 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { readJsonFile, writeJsonFile, ensureDir } from '../storage/json-store.js';
3
+ import { join } from 'node:path';
4
+ import { broadcastToWorkspace } from '../ws/connection-manager.js';
5
+ function notificationsPath(workspaceId) {
6
+ const dir = join(process.env.AGENT_SPACES_DATA_DIR || join(process.env.HOME || '~', '.agent-spaces-data'), 'workspaces', workspaceId);
7
+ ensureDir(dir);
8
+ return join(dir, 'notifications.json');
9
+ }
10
+ function readAll(workspaceId) {
11
+ return readJsonFile(notificationsPath(workspaceId)) ?? [];
12
+ }
13
+ function writeAll(workspaceId, notifications) {
14
+ writeJsonFile(notificationsPath(workspaceId), notifications);
15
+ }
16
+ export function listNotifications(workspaceId) {
17
+ return readAll(workspaceId).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
18
+ }
19
+ export function createNotification(workspaceId, type, title, description, data) {
20
+ const notifications = readAll(workspaceId);
21
+ const notification = {
22
+ id: randomUUID(),
23
+ workspaceId,
24
+ type,
25
+ title,
26
+ description,
27
+ data,
28
+ read: false,
29
+ createdAt: new Date().toISOString(),
30
+ };
31
+ notifications.push(notification);
32
+ writeAll(workspaceId, notifications);
33
+ broadcastToWorkspace(workspaceId, 'notification.created', notification);
34
+ return notification;
35
+ }
36
+ export function markRead(workspaceId, notificationId) {
37
+ const notifications = readAll(workspaceId);
38
+ const n = notifications.find((n) => n.id === notificationId);
39
+ if (!n)
40
+ return null;
41
+ n.read = true;
42
+ writeAll(workspaceId, notifications);
43
+ return n;
44
+ }
45
+ export function clearAll(workspaceId) {
46
+ writeAll(workspaceId, []);
47
+ broadcastToWorkspace(workspaceId, 'notification.cleared', null);
48
+ }
49
+ export function unreadCount(workspaceId) {
50
+ return readAll(workspaceId).filter((n) => !n.read).length;
51
+ }
52
+ //# sourceMappingURL=notification-center.js.map
@@ -1,8 +1,10 @@
1
1
  import * as issueService from '../issue.js';
2
2
  import * as taskService from '../task.js';
3
+ import * as notificationCenter from '../notification-center.js';
3
4
  import { adapters } from './types.js';
4
5
  import { shouldNotify, isIssueStartStatus, isTaskDoneStatus } from './helpers.js';
5
6
  export function publishWorkspaceEvent(workspaceId, wsEvent, data) {
7
+ persistInAppNotification(workspaceId, wsEvent, data);
6
8
  const envelope = buildNotificationEnvelope(workspaceId, wsEvent, data);
7
9
  if (!envelope)
8
10
  return;
@@ -93,4 +95,34 @@ function buildNotificationEnvelope(workspaceId, wsEvent, data) {
93
95
  }
94
96
  return null;
95
97
  }
98
+ function persistInAppNotification(workspaceId, wsEvent, data) {
99
+ if (wsEvent === 'issue.status_changed') {
100
+ const payload = data;
101
+ if (!payload.issueId)
102
+ return;
103
+ const issue = issueService.getById(workspaceId, payload.issueId);
104
+ if (!issue)
105
+ return;
106
+ if (payload.to === 'completed') {
107
+ notificationCenter.createNotification(workspaceId, 'issue_completed', `议题完成: ${issue.title}`, issue.description || undefined, { issueId: issue.id, status: 'completed' });
108
+ }
109
+ else if (payload.to === 'error') {
110
+ notificationCenter.createNotification(workspaceId, 'issue_failed', `议题失败: ${issue.title}`, issue.description || undefined, { issueId: issue.id, status: 'error' });
111
+ }
112
+ }
113
+ if (wsEvent === 'task.status_changed') {
114
+ const payload = data;
115
+ if (!payload.taskId)
116
+ return;
117
+ const task = taskService.getById(workspaceId, payload.taskId);
118
+ if (!task)
119
+ return;
120
+ if (payload.to === 'done') {
121
+ notificationCenter.createNotification(workspaceId, 'task_completed', `任务完成: ${task.title}`, task.description || undefined, { taskId: task.id, issueId: task.issueId, status: 'done' });
122
+ }
123
+ else if (payload.to === 'failed') {
124
+ notificationCenter.createNotification(workspaceId, 'task_failed', `任务失败: ${task.title}`, task.description || undefined, { taskId: task.id, issueId: task.issueId, status: 'failed' });
125
+ }
126
+ }
127
+ }
96
128
  //# sourceMappingURL=events.js.map