@xopcai/xopc 0.0.122 → 0.0.124

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 (273) hide show
  1. package/README.md +28 -11
  2. package/README.zh-CN.md +28 -11
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CK3s5cJo.js → agent-avatar-dicebear-B5uSVAyJ.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-BjIxt67Z.js +2 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-CWsNHtTs.js → app-management-settings-panel-CYCONLIL.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-C4b8y6iZ.js → appearance-settings-Clw3ryKg.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-QICTeGjX.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-DAtNW3Ia.js → archive-plugin-CXUGRp2i.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-Dm-RY54y.js → attachment-load-Cmiub3Wg.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automation-suggestion-card-BKHewSVT.js → automation-suggestion-card-CU0-4nQ-.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-page-Car-jtiz.js +1 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-BG6Xnk71.js → binary-plugins-0Izz-N3p.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-DfndiO3h.js → block-editor-C1Kv0IkJ.js} +28 -28
  16. package/dist/gateway/static/root/assets/browser-settings-page-CMeBJzlS.js +8 -0
  17. package/dist/gateway/static/root/assets/{capability-presets-api-Cx93GLdw.js → capability-presets-api-BmQTM9Il.js} +1 -1
  18. package/dist/gateway/static/root/assets/capability-presets-settings-panel-DZEJEoCF.js +1 -0
  19. package/dist/gateway/static/root/assets/{channel-recipient-api-DdPTtEs8.js → channel-recipient-api-BeA2aVor.js} +1 -1
  20. package/dist/gateway/static/root/assets/channels-settings-kkW-xFau.js +1 -0
  21. package/dist/gateway/static/root/assets/connectors-page-BvaQvO5F.js +2 -0
  22. package/dist/gateway/static/root/assets/{directory-picker-path-field-D835EHJd.js → directory-picker-path-field-2rjzUu57.js} +1 -1
  23. package/dist/gateway/static/root/assets/{dreaming-settings-CCa1F7Fg.js → dreaming-settings-DnHO686d.js} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-debug-page-W2ITCPSA.js → extension-debug-page-BGLRuWVN.js} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-page-DrvUytvn.js → extension-page-D978ePp9.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-settings-page-qM8aLT5u.js → extension-settings-page-B6Ih4Swx.js} +1 -1
  27. package/dist/gateway/static/root/assets/{fetch-ovkvM8KQ.js → fetch-BU5S0Sn7.js} +1 -1
  28. package/dist/gateway/static/root/assets/{gateway-config-api-BB3biuD0.js → gateway-config-api-BIPGwrl1.js} +1 -1
  29. package/dist/gateway/static/root/assets/gateway-settings-CMEA3r5u.js +1 -0
  30. package/dist/gateway/static/root/assets/{gateway-startup-retry-CanQ8UcR.js → gateway-startup-retry-Cd0Zz30N.js} +1 -1
  31. package/dist/gateway/static/root/assets/{goal-create-dialog-CNqNdEkQ.js → goal-create-dialog-B-KHKeXN.js} +1 -1
  32. package/dist/gateway/static/root/assets/goal-detail-page-BxrSoWdj.js +1 -0
  33. package/dist/gateway/static/root/assets/{goals-config-api-BBOX6mUl.js → goals-config-api-B95KdGMj.js} +1 -1
  34. package/dist/gateway/static/root/assets/{goals-page-BP1kfvWv.js → goals-page-BTJzIl28.js} +2 -2
  35. package/dist/gateway/static/root/assets/{goals-settings-Bh3A7OZV.js → goals-settings-BJyEuLLm.js} +1 -1
  36. package/dist/gateway/static/root/assets/heartbeat-settings-wkOjYNQT.js +1 -0
  37. package/dist/gateway/static/root/assets/index-BLaXr6Ev.css +1 -0
  38. package/dist/gateway/static/root/assets/index-c_9TVr_C.js +191 -0
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B4TKJHla.js → keyboard-shortcuts-settings-BT8T4DyC.js} +1 -1
  40. package/dist/gateway/static/root/assets/logs-page-CrxBJUgI.js +2 -0
  41. package/dist/gateway/static/root/assets/{markdown-split-DcFtdLci.js → markdown-split-Dv66bO2b.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-B6SmeLVB.js → media-plugins-BhIQU5yS.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-O6TAtdFC.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-DOOpz6y1.js +2 -0
  45. package/dist/gateway/static/root/assets/notes-page-Br16pK9v.js +1 -0
  46. package/dist/gateway/static/root/assets/notes-workbench-PSJAi0ZC.js +2 -0
  47. package/dist/gateway/static/root/assets/{preview-open-alternatives-C29O19s7.js → preview-open-alternatives-DGzet1d7.js} +1 -1
  48. package/dist/gateway/static/root/assets/project-detail-page-CIGGgKuw.js +1 -0
  49. package/dist/gateway/static/root/assets/projects-page-B2grhsBt.js +1 -0
  50. package/dist/gateway/static/root/assets/remote-access-hub-CXMORxBD.js +1 -0
  51. package/dist/gateway/static/root/assets/{save-bar-controls-B4ad9GfB.js → save-bar-controls-Cqgb-jqa.js} +1 -1
  52. package/dist/gateway/static/root/assets/{schedule-field-DdYncfLR.js → schedule-field-DVZcI8Yt.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schema-form-BMTrDE-7.js → schema-form-Bj7sOxC8.js} +1 -1
  54. package/dist/gateway/static/root/assets/sessions-page-Bi2wkBzU.js +1 -0
  55. package/dist/gateway/static/root/assets/settings-advanced-gate-B1L3G4IA.js +1 -0
  56. package/dist/gateway/static/root/assets/{settings-collapsible-section-MwTW2eK4.js → settings-collapsible-section-BmXn-8N6.js} +1 -1
  57. package/dist/gateway/static/root/assets/{settings-form-section-Dg6LBbcr.js → settings-form-section-BnqYMWHR.js} +1 -1
  58. package/dist/gateway/static/root/assets/settings-page-BfRSHJmO.js +1 -0
  59. package/dist/gateway/static/root/assets/{settings-page-layout-BX4T-OLF.js → settings-page-layout-CdWZU1hd.js} +1 -1
  60. package/dist/gateway/static/root/assets/{setup-status-panel-Ce1pcFtR.js → setup-status-panel-CWtPM7RK.js} +1 -1
  61. package/dist/gateway/static/root/assets/{share-preview-page-DWE-dUFh.js → share-preview-page-IB1v8i86.js} +1 -1
  62. package/dist/gateway/static/root/assets/shares-settings-ngbsBgij.js +1 -0
  63. package/dist/gateway/static/root/assets/skill-reload-api-BIe442Gv.js +1 -0
  64. package/dist/gateway/static/root/assets/skills-page-BfchZRVa.js +2 -0
  65. package/dist/gateway/static/root/assets/{system-settings-panel-Dd6Y35Cm.js → system-settings-panel-BxQ384RC.js} +1 -1
  66. package/dist/gateway/static/root/assets/{typed-models-lib-Cx6XV5A2.js → typed-models-lib-BkcpvFYO.js} +1 -1
  67. package/dist/gateway/static/root/assets/{use-channel-catalog-D4sMbmy7.js → use-channel-catalog-Cd0FaEQ4.js} +1 -1
  68. package/dist/gateway/static/root/assets/{user-profile-settings-panel-Bx-hjMQc.js → user-profile-settings-panel-DQ7NnRSY.js} +1 -1
  69. package/dist/gateway/static/root/assets/{utils-D7b_vZnv.js → utils-YKhVf7Ka.js} +1 -1
  70. package/dist/gateway/static/root/assets/voice-api-key-field-C5Ov8Uk_.js +1 -0
  71. package/dist/gateway/static/root/assets/{workflow-run-setup-panel--TdohF5N.js → workflow-run-setup-panel-BYD9BtOq.js} +2 -2
  72. package/dist/gateway/static/root/assets/{workflow-start-dialog-B0lavx_o.js → workflow-start-dialog-BNJVCWoS.js} +1 -1
  73. package/dist/gateway/static/root/assets/workflows-page-BYF29ANm.js +18 -0
  74. package/dist/gateway/static/root/index.html +5 -5
  75. package/dist/package.js +1 -1
  76. package/dist/src/agent/agent-manager.d.ts +6 -1
  77. package/dist/src/agent/agent-manager.js +28 -12
  78. package/dist/src/agent/agent-manager.js.map +1 -1
  79. package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
  80. package/dist/src/agent/embedded/xopc-tools-bridge.js +11 -7
  81. package/dist/src/agent/embedded/xopc-tools-bridge.js.map +1 -1
  82. package/dist/src/agent/lifecycle/progress.js +3 -1
  83. package/dist/src/agent/lifecycle/progress.js.map +1 -1
  84. package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +2 -2
  85. package/dist/src/agent/lifecycle/timeout-wrapper.js +4 -4
  86. package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
  87. package/dist/src/agent/memory/summary-generator.d.ts +1 -1
  88. package/dist/src/agent/memory/summary-generator.js +1 -2
  89. package/dist/src/agent/memory/summary-generator.js.map +1 -1
  90. package/dist/src/agent/middleware/index.d.ts +1 -1
  91. package/dist/src/agent/middleware/self-verify.d.ts +44 -14
  92. package/dist/src/agent/middleware/self-verify.js +214 -44
  93. package/dist/src/agent/middleware/self-verify.js.map +1 -1
  94. package/dist/src/agent/orchestration/agent-event-handler.js +57 -15
  95. package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
  96. package/dist/src/agent/prompt/sections/tooling.js +18 -13
  97. package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
  98. package/dist/src/agent/prompt/system-reminder.js +2 -2
  99. package/dist/src/agent/prompt/system-reminder.js.map +1 -1
  100. package/dist/src/agent/sandbox/sanitize-env-vars.d.ts +2 -2
  101. package/dist/src/agent/sandbox/sanitize-env-vars.js +2 -2
  102. package/dist/src/agent/sandbox/sanitize-env-vars.js.map +1 -1
  103. package/dist/src/agent/sandbox/types.d.ts +2 -2
  104. package/dist/src/agent/sandbox/types.js.map +1 -1
  105. package/dist/src/agent/service/btw-query.js +1 -1
  106. package/dist/src/agent/service/btw-query.js.map +1 -1
  107. package/dist/src/agent/service/process-direct-one-shot.js +6 -2
  108. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  109. package/dist/src/agent/service/process-direct-streaming.js +12 -7
  110. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  111. package/dist/src/agent/service.js +2 -1
  112. package/dist/src/agent/service.js.map +1 -1
  113. package/dist/src/agent/skills/skill-tool-gating.js +1 -1
  114. package/dist/src/agent/skills/skill-tool-gating.js.map +1 -1
  115. package/dist/src/agent/starter-agents.js +9 -9
  116. package/dist/src/agent/starter-agents.js.map +1 -1
  117. package/dist/src/agent/tools/apply-patch.d.ts +25 -0
  118. package/dist/src/agent/tools/apply-patch.js +256 -0
  119. package/dist/src/agent/tools/apply-patch.js.map +1 -0
  120. package/dist/src/agent/tools/delegate-tool.d.ts +1 -1
  121. package/dist/src/agent/tools/delegate-tool.js +3 -3
  122. package/dist/src/agent/tools/delegate-tool.js.map +1 -1
  123. package/dist/src/agent/tools/exec-command.d.ts +30 -0
  124. package/dist/src/agent/tools/exec-command.js +224 -0
  125. package/dist/src/agent/tools/exec-command.js.map +1 -0
  126. package/dist/src/agent/tools/execute-code-tool.js +7 -4
  127. package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
  128. package/dist/src/agent/tools/factory.d.ts +3 -3
  129. package/dist/src/agent/tools/factory.js +8 -9
  130. package/dist/src/agent/tools/factory.js.map +1 -1
  131. package/dist/src/agent/tools/goal-tool.d.ts +5 -1
  132. package/dist/src/agent/tools/goal-tool.js +8 -2
  133. package/dist/src/agent/tools/goal-tool.js.map +1 -1
  134. package/dist/src/agent/tools/index.d.ts +4 -3
  135. package/dist/src/agent/tools/index.js +5 -4
  136. package/dist/src/agent/tools/skills-tools.d.ts +1 -1
  137. package/dist/src/agent/tools/skills-tools.js.map +1 -1
  138. package/dist/src/agent/tools/update-plan-tool.d.ts +11 -0
  139. package/dist/src/agent/tools/update-plan-tool.js +72 -0
  140. package/dist/src/agent/tools/update-plan-tool.js.map +1 -0
  141. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
  142. package/dist/src/agent/workflow/builtins/debug-incident.js +1 -1
  143. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
  144. package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
  145. package/dist/src/agent/workflow/builtins/pr-review.js +1 -1
  146. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
  147. package/dist/src/agent/workflow/builtins/release-check.d.ts +1 -1
  148. package/dist/src/agent/workflow/builtins/release-check.js +1 -1
  149. package/dist/src/agent/workflow/builtins/release-check.js.map +1 -1
  150. package/dist/src/agent/workflow/step-labels.js +7 -3
  151. package/dist/src/agent/workflow/step-labels.js.map +1 -1
  152. package/dist/src/chat-commands/agent-edit.js +1 -1
  153. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  154. package/dist/src/chat-commands/builtins/goal.js +4 -1
  155. package/dist/src/chat-commands/builtins/goal.js.map +1 -1
  156. package/dist/src/chat-commands/builtins/review.d.ts +1 -0
  157. package/dist/src/chat-commands/builtins/review.js +167 -0
  158. package/dist/src/chat-commands/builtins/review.js.map +1 -0
  159. package/dist/src/chat-commands/index.d.ts +1 -0
  160. package/dist/src/chat-commands/index.js +3 -1
  161. package/dist/src/chat-commands/index.js.map +1 -1
  162. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  163. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  164. package/dist/src/config/agent-profile.js +1 -1
  165. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  166. package/dist/src/gateway/agent-builtin-tools.js +3 -2
  167. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  168. package/dist/src/gateway/agents-admin.js +1 -1
  169. package/dist/src/gateway/chat-stream/mapper.d.ts +6 -0
  170. package/dist/src/gateway/chat-stream/mapper.js +149 -6
  171. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  172. package/dist/src/gateway/chat-stream/protocol.d.ts +51 -1
  173. package/dist/src/gateway/hono/routes/commands-skills.js +21 -0
  174. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  175. package/dist/src/gateway/hono/routes/index.js +2 -0
  176. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  177. package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
  178. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  179. package/dist/src/gateway/hono/routes/projects.js +136 -14
  180. package/dist/src/gateway/hono/routes/projects.js.map +1 -1
  181. package/dist/src/gateway/hono/routes/search.d.ts +3 -0
  182. package/dist/src/gateway/hono/routes/search.js +47 -0
  183. package/dist/src/gateway/hono/routes/search.js.map +1 -0
  184. package/dist/src/gateway/security/dangerous-tools.js +1 -1
  185. package/dist/src/gateway/security/dangerous-tools.js.map +1 -1
  186. package/dist/src/infra/update-restart.js +1 -1
  187. package/dist/src/projects/index.d.ts +3 -0
  188. package/dist/src/projects/index.js +4 -1
  189. package/dist/src/projects/project-agent-suggestion.d.ts +14 -0
  190. package/dist/src/projects/project-agent-suggestion.js +19 -0
  191. package/dist/src/projects/project-agent-suggestion.js.map +1 -0
  192. package/dist/src/projects/project-kind.d.ts +13 -0
  193. package/dist/src/projects/project-kind.js +147 -0
  194. package/dist/src/projects/project-kind.js.map +1 -0
  195. package/dist/src/projects/project-service.d.ts +15 -0
  196. package/dist/src/projects/project-service.js +96 -3
  197. package/dist/src/projects/project-service.js.map +1 -1
  198. package/dist/src/projects/project-store.js +32 -8
  199. package/dist/src/projects/project-store.js.map +1 -1
  200. package/dist/src/projects/types.d.ts +4 -1
  201. package/dist/src/projects/workspace-project.d.ts +22 -0
  202. package/dist/src/projects/workspace-project.js +143 -0
  203. package/dist/src/projects/workspace-project.js.map +1 -0
  204. package/dist/src/review/review-git.d.ts +48 -0
  205. package/dist/src/review/review-git.js +333 -0
  206. package/dist/src/review/review-git.js.map +1 -0
  207. package/dist/src/review/review-types.d.ts +22 -0
  208. package/dist/src/review/review-types.js +29 -0
  209. package/dist/src/review/review-types.js.map +1 -0
  210. package/dist/src/session/session-context-for-llm.js +151 -0
  211. package/dist/src/session/session-context-for-llm.js.map +1 -1
  212. package/dist/src/storage/sqlite/migrations/021_projects_fts.sql +21 -0
  213. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  214. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  215. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  216. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  217. package/dist/src/storage/sqlite/schema.js +1 -1
  218. package/dist/src/tui/backends/embedded-backend.d.ts +8 -1
  219. package/dist/src/tui/backends/embedded-backend.js +60 -1
  220. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  221. package/dist/src/tui/backends/gateway-sse-backend.d.ts +9 -1
  222. package/dist/src/tui/backends/gateway-sse-backend.js +29 -0
  223. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  224. package/dist/src/tui/components/assistant-message.js +35 -0
  225. package/dist/src/tui/components/assistant-message.js.map +1 -1
  226. package/dist/src/tui/tui-agent-events.js +25 -0
  227. package/dist/src/tui/tui-agent-events.js.map +1 -1
  228. package/dist/src/tui/tui-backend.d.ts +20 -0
  229. package/dist/src/tui/tui-commands.d.ts +1 -0
  230. package/dist/src/tui/tui-commands.js +110 -4
  231. package/dist/src/tui/tui-commands.js.map +1 -1
  232. package/dist/src/tui/tui-picker-overlay.d.ts +2 -0
  233. package/dist/src/tui/tui-picker-overlay.js +153 -1
  234. package/dist/src/tui/tui-picker-overlay.js.map +1 -1
  235. package/dist/src/tui/tui.js +36 -2
  236. package/dist/src/tui/tui.js.map +1 -1
  237. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  238. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  239. package/package.json +1 -1
  240. package/dist/gateway/static/root/assets/agents-SsQrj9B-.js +0 -2
  241. package/dist/gateway/static/root/assets/apps-page-CYb6fame.js +0 -1
  242. package/dist/gateway/static/root/assets/automations-page-DpOOq1nk.js +0 -1
  243. package/dist/gateway/static/root/assets/browser-settings-page-9nXxgh4u.js +0 -8
  244. package/dist/gateway/static/root/assets/capability-presets-settings-panel-D528_YFy.js +0 -1
  245. package/dist/gateway/static/root/assets/channels-settings-Ck-3h5lI.js +0 -1
  246. package/dist/gateway/static/root/assets/connectors-page-DL-fAqE_.js +0 -2
  247. package/dist/gateway/static/root/assets/gateway-settings-mmmnIH27.js +0 -1
  248. package/dist/gateway/static/root/assets/goal-detail-page-Ceu5zrN6.js +0 -1
  249. package/dist/gateway/static/root/assets/heartbeat-settings-YoYgKlfX.js +0 -1
  250. package/dist/gateway/static/root/assets/index-DdqSO9R2.js +0 -191
  251. package/dist/gateway/static/root/assets/index-jGB-2nI-.css +0 -1
  252. package/dist/gateway/static/root/assets/logs-page-BgVk5DvZ.js +0 -2
  253. package/dist/gateway/static/root/assets/messages-B5O4OIEo.js +0 -3
  254. package/dist/gateway/static/root/assets/models-hub-panel-BU0x8PGP.js +0 -2
  255. package/dist/gateway/static/root/assets/notes-page-D6JYVP4I.js +0 -1
  256. package/dist/gateway/static/root/assets/notes-workbench-mqRJYnJS.js +0 -2
  257. package/dist/gateway/static/root/assets/project-detail-page-DOTgzoeh.js +0 -1
  258. package/dist/gateway/static/root/assets/projects-page-DGUIb4y5.js +0 -1
  259. package/dist/gateway/static/root/assets/remote-access-hub-DVKKUj3u.js +0 -1
  260. package/dist/gateway/static/root/assets/sessions-page-BVbzUWxW.js +0 -1
  261. package/dist/gateway/static/root/assets/settings-advanced-gate-DrNNoSqE.js +0 -1
  262. package/dist/gateway/static/root/assets/settings-page-CMDqgeMR.js +0 -1
  263. package/dist/gateway/static/root/assets/shares-settings-aKSraR8l.js +0 -1
  264. package/dist/gateway/static/root/assets/skill-reload-api-CS-FU_B-.js +0 -1
  265. package/dist/gateway/static/root/assets/skills-page-BaF3u0LA.js +0 -2
  266. package/dist/gateway/static/root/assets/voice-api-key-field-CkhbRt1q.js +0 -1
  267. package/dist/gateway/static/root/assets/workflows-page-CZR05Bu_.js +0 -18
  268. package/dist/src/agent/tools/edit.d.ts +0 -14
  269. package/dist/src/agent/tools/edit.js +0 -132
  270. package/dist/src/agent/tools/edit.js.map +0 -1
  271. package/dist/src/agent/tools/shell.d.ts +0 -15
  272. package/dist/src/agent/tools/shell.js +0 -158
  273. package/dist/src/agent/tools/shell.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-bundles.js","names":[],"sources":["../../../../../src/gateway/hono/routes/lazy-bundles.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { GatewayService } from '../../service.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport type AuthenticatedLazyRouteBundle = {\n id: string;\n match: (path: string) => boolean;\n load: () => Promise<{ register: (authenticated: Hono, deps: AuthenticatedRouteDeps) => void }>;\n};\n\nexport type AppLazyRouteBundle = {\n id: string;\n prefixes: readonly string[];\n match: (path: string) => boolean;\n load: () => Promise<{\n registerOnApp: (app: Hono, service: GatewayService) => void;\n }>;\n};\n\nfunction startsWithAny(path: string, prefixes: readonly string[]): boolean {\n return prefixes.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));\n}\n\nexport const AUTHENTICATED_LAZY_ROUTE_BUNDLES: readonly AuthenticatedLazyRouteBundle[] = [\n {\n id: 'media',\n match: (path) => startsWithAny(path, ['/api/media']),\n load: async () => {\n const { registerMediaRoutes } = await import('./media.js');\n return { register: registerMediaRoutes };\n },\n },\n {\n id: 'workspace',\n match: (path) => startsWithAny(path, ['/api/workspace']),\n load: async () => {\n const { registerWorkspaceRoutes } = await import('./workspace.js');\n return { register: registerWorkspaceRoutes };\n },\n },\n {\n id: 'host-fs',\n match: (path) => startsWithAny(path, ['/api/host/fs']),\n load: async () => {\n const { registerHostFsRoutes } = await import('./host-fs.js');\n return { register: registerHostFsRoutes };\n },\n },\n {\n id: 'channels',\n match: (path) => startsWithAny(path, ['/api/channels']),\n load: async () => {\n const { registerChannelRoutes } = await import('./channels.js');\n return { register: registerChannelRoutes };\n },\n },\n {\n id: 'browser-install',\n match: (path) =>\n path === '/api/browser/playwright/install/stream' ||\n path === '/api/browser/cloakbrowser/install/stream',\n load: async () => {\n const { registerBrowserInstallRoutes } = await import('./browser-install.js');\n return { register: registerBrowserInstallRoutes };\n },\n },\n {\n id: 'browser',\n // `browser-install` above already matched the SSE install streams; this\n // catches the remaining /api/browser/* handlers (extension, cdp,\n // cloakbrowser doctor/launch/install, playwright doctor/install, cloud).\n match: (path) => startsWithAny(path, ['/api/browser']),\n load: async () => {\n const { registerBrowserRoutes } = await import('./browser.js');\n return { register: registerBrowserRoutes };\n },\n },\n {\n id: 'config',\n match: (path) =>\n startsWithAny(path, [\n '/api/config',\n '/api/heartbeat/trigger',\n // Secret reveal handlers live in config routes but use /api/gateway and\n // /api/tools paths; without these prefixes the extension-gateway bundle\n // matches first and returns 404 (no handler for multi-segment paths).\n '/api/gateway/reveal-auth-secret',\n '/api/tools/web/reveal-search-api-key',\n ]),\n load: async () => {\n const { registerConfigRoutes } = await import('./config.js');\n return { register: registerConfigRoutes };\n },\n },\n {\n id: 'doctor',\n match: (path) => startsWithAny(path, ['/api/doctor', '/api/migrations']),\n load: async () => {\n const { registerDoctorRoutes } = await import('./doctor.js');\n const { registerMigrationRoutes } = await import('./migrations.js');\n return {\n register: (authenticated, deps) => {\n registerDoctorRoutes(authenticated, deps);\n registerMigrationRoutes(authenticated, deps);\n },\n };\n },\n },\n {\n id: 'dreaming',\n match: (path) => startsWithAny(path, ['/api/dreaming']),\n load: async () => {\n const { registerDreamingRoutes } = await import('./dreaming.js');\n return { register: registerDreamingRoutes };\n },\n },\n {\n id: 'agents',\n match: (path) => startsWithAny(path, ['/api/agents', '/api/user-profile', '/api/voice/models']),\n load: async () => {\n const { registerAgentsRoutes } = await import('./agents.js');\n return { register: registerAgentsRoutes };\n },\n },\n {\n id: 'capability-presets',\n match: (path) => startsWithAny(path, ['/api/capability-presets', '/api/global-defaults']),\n load: async () => {\n const { registerCapabilityPresetsRoutes } = await import('./capability-presets.js');\n const { registerGlobalDefaultsRoutes } = await import('./global-defaults.js');\n return {\n register: (authenticated, deps) => {\n registerCapabilityPresetsRoutes(authenticated, deps);\n registerGlobalDefaultsRoutes(authenticated, deps);\n },\n };\n },\n },\n {\n id: 'auth-registry-extensions',\n match: (path) =>\n startsWithAny(path, [\n '/api/auth',\n '/api/registry',\n '/api/extensions',\n '/api/context',\n '/api/marketplace',\n ]),\n load: async () => {\n const { registerAuthRegistryExtensionsRoutes } = await import('./auth-registry-extensions.js');\n return { register: registerAuthRegistryExtensionsRoutes };\n },\n },\n {\n id: 'models',\n match: (path) =>\n startsWithAny(path, ['/api/models', '/api/models-json', '/api/providers', '/api/image']),\n load: async () => {\n const { registerModelsRoutes } = await import('./models.js');\n return { register: registerModelsRoutes };\n },\n },\n {\n id: 'ai-assist',\n match: (path) => startsWithAny(path, ['/api/ai']),\n load: async () => {\n const { registerAiAssistRoutes } = await import('./ai-assist.js');\n return { register: registerAiAssistRoutes };\n },\n },\n {\n id: 'commands-skills',\n match: (path) => startsWithAny(path, ['/api/commands', '/api/skills', '/api/chat/skills']),\n load: async () => {\n const { registerCommandsSkillsRoutes } = await import('./commands-skills.js');\n return { register: registerCommandsSkillsRoutes };\n },\n },\n {\n id: 'automations',\n match: (path) => startsWithAny(path, ['/api/automations', '/api/automation-runs']),\n load: async () => {\n const { registerAutomationRoutes } = await import('../../../automations/api/routes.js');\n return { register: registerAutomationRoutes };\n },\n },\n {\n id: 'goals',\n match: (path) => startsWithAny(path, ['/api/goals']),\n load: async () => {\n const { registerGoalsRoutes } = await import('./goals.js');\n return { register: registerGoalsRoutes };\n },\n },\n {\n id: 'notes',\n match: (path) => startsWithAny(path, ['/api/notes']),\n load: async () => {\n const { registerNotesRoutes } = await import('./notes.js');\n return { register: registerNotesRoutes };\n },\n },\n {\n id: 'home',\n match: (path) => startsWithAny(path, ['/api/home']),\n load: async () => {\n const { registerHomeRoutes } = await import('./home.js');\n return { register: registerHomeRoutes };\n },\n },\n {\n id: 'workflows',\n match: (path) => startsWithAny(path, ['/api/workflows']),\n load: async () => {\n const { registerWorkflowRoutes } = await import('./workflows.js');\n return { register: registerWorkflowRoutes };\n },\n },\n {\n id: 'logs',\n match: (path) => startsWithAny(path, ['/api/logs']),\n load: async () => {\n const { registerLogsRoutes } = await import('./logs.js');\n return { register: registerLogsRoutes };\n },\n },\n {\n id: 'shares',\n match: (path) => startsWithAny(path, ['/api/shares']),\n load: async () => {\n const { registerShareRoutes } = await import('./shares.js');\n return { register: registerShareRoutes };\n },\n },\n {\n id: 'site-shares',\n match: (path) => startsWithAny(path, ['/api/site-shares']),\n load: async () => {\n const { registerSiteShareRoutes } = await import('./site-shares.js');\n return { register: registerSiteShareRoutes };\n },\n },\n {\n id: 'tunnel',\n match: (path) => startsWithAny(path, ['/api/tunnel']),\n load: async () => {\n const { registerTunnelRoutes } = await import('./tunnel.js');\n return { register: registerTunnelRoutes };\n },\n },\n {\n id: 'exposure',\n match: (path) => startsWithAny(path, ['/api/exposure']),\n load: async () => {\n const { registerExposureRoutes } = await import('./exposure.js');\n return { register: registerExposureRoutes };\n },\n },\n {\n id: 'extension-gateway',\n match: (path) => startsWithAny(path, ['/api/gateway']),\n load: async () => {\n const { registerExtensionGatewayRoutes } = await import('./extension-gateway.js');\n return { register: registerExtensionGatewayRoutes };\n },\n },\n {\n id: 'update',\n match: (path) => startsWithAny(path, ['/api/update']),\n load: async () => {\n const { registerUpdateRoutes } = await import('./update.js');\n return { register: registerUpdateRoutes };\n },\n },\n {\n id: 'voice',\n match: (path) => startsWithAny(path, ['/api/voice']) && path !== '/api/voice/models',\n load: async () => {\n const { registerVoiceRoutes } = await import('./voice.js');\n return { register: registerVoiceRoutes };\n },\n },\n {\n id: 'connectors',\n match: (path) => startsWithAny(path, ['/api/connectors']),\n load: async () => {\n const { registerConnectorRoutes } = await import('./connectors.js');\n return { register: registerConnectorRoutes };\n },\n },\n {\n id: 'tui',\n match: (path) => startsWithAny(path, ['/api/tui']),\n load: async () => {\n const { registerTuiRoutes } = await import('./tui.js');\n return { register: registerTuiRoutes };\n },\n },\n {\n id: 'mcp',\n match: (path) => startsWithAny(path, ['/api/mcp']),\n load: async () => {\n const { registerMcpRoutes } = await import('./mcp.js');\n return { register: registerMcpRoutes };\n },\n },\n];\n\nexport const APP_LAZY_ROUTE_BUNDLES: readonly AppLazyRouteBundle[] = [\n {\n id: 'shares-public',\n prefixes: ['/s'],\n match: (path) => startsWithAny(path, ['/s']),\n load: async () => {\n const { registerSharePublicRoutes } = await import('./shares.js');\n return { registerOnApp: registerSharePublicRoutes };\n },\n },\n {\n id: 'tunnel-public',\n prefixes: [\n '/api/tunnel/pair/ping',\n '/api/tunnel/pair/validate-url',\n '/api/tunnel/exchange-token',\n ],\n match: (path) =>\n path === '/api/tunnel/exchange-token' ||\n path === '/api/tunnel/pair/ping' ||\n path === '/api/tunnel/pair/validate-url',\n load: async () => {\n const { registerTunnelPublicRoutes } = await import('./tunnel.js');\n return { registerOnApp: registerTunnelPublicRoutes };\n },\n },\n];\n\nexport function findAuthenticatedLazyRouteBundle(path: string): AuthenticatedLazyRouteBundle | undefined {\n return AUTHENTICATED_LAZY_ROUTE_BUNDLES.find((bundle) => bundle.match(path));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lazy-bundles.js","names":[],"sources":["../../../../../src/gateway/hono/routes/lazy-bundles.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { GatewayService } from '../../service.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport type AuthenticatedLazyRouteBundle = {\n id: string;\n match: (path: string) => boolean;\n load: () => Promise<{ register: (authenticated: Hono, deps: AuthenticatedRouteDeps) => void }>;\n};\n\nexport type AppLazyRouteBundle = {\n id: string;\n prefixes: readonly string[];\n match: (path: string) => boolean;\n load: () => Promise<{\n registerOnApp: (app: Hono, service: GatewayService) => void;\n }>;\n};\n\nfunction startsWithAny(path: string, prefixes: readonly string[]): boolean {\n return prefixes.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));\n}\n\nexport const AUTHENTICATED_LAZY_ROUTE_BUNDLES: readonly AuthenticatedLazyRouteBundle[] = [\n {\n id: 'media',\n match: (path) => startsWithAny(path, ['/api/media']),\n load: async () => {\n const { registerMediaRoutes } = await import('./media.js');\n return { register: registerMediaRoutes };\n },\n },\n {\n id: 'workspace',\n match: (path) => startsWithAny(path, ['/api/workspace']),\n load: async () => {\n const { registerWorkspaceRoutes } = await import('./workspace.js');\n return { register: registerWorkspaceRoutes };\n },\n },\n {\n id: 'host-fs',\n match: (path) => startsWithAny(path, ['/api/host/fs']),\n load: async () => {\n const { registerHostFsRoutes } = await import('./host-fs.js');\n return { register: registerHostFsRoutes };\n },\n },\n {\n id: 'channels',\n match: (path) => startsWithAny(path, ['/api/channels']),\n load: async () => {\n const { registerChannelRoutes } = await import('./channels.js');\n return { register: registerChannelRoutes };\n },\n },\n {\n id: 'browser-install',\n match: (path) =>\n path === '/api/browser/playwright/install/stream' ||\n path === '/api/browser/cloakbrowser/install/stream',\n load: async () => {\n const { registerBrowserInstallRoutes } = await import('./browser-install.js');\n return { register: registerBrowserInstallRoutes };\n },\n },\n {\n id: 'browser',\n // `browser-install` above already matched the SSE install streams; this\n // catches the remaining /api/browser/* handlers (extension, cdp,\n // cloakbrowser doctor/launch/install, playwright doctor/install, cloud).\n match: (path) => startsWithAny(path, ['/api/browser']),\n load: async () => {\n const { registerBrowserRoutes } = await import('./browser.js');\n return { register: registerBrowserRoutes };\n },\n },\n {\n id: 'config',\n match: (path) =>\n startsWithAny(path, [\n '/api/config',\n '/api/heartbeat/trigger',\n // Secret reveal handlers live in config routes but use /api/gateway and\n // /api/tools paths; without these prefixes the extension-gateway bundle\n // matches first and returns 404 (no handler for multi-segment paths).\n '/api/gateway/reveal-auth-secret',\n '/api/tools/web/reveal-search-api-key',\n ]),\n load: async () => {\n const { registerConfigRoutes } = await import('./config.js');\n return { register: registerConfigRoutes };\n },\n },\n {\n id: 'doctor',\n match: (path) => startsWithAny(path, ['/api/doctor', '/api/migrations']),\n load: async () => {\n const { registerDoctorRoutes } = await import('./doctor.js');\n const { registerMigrationRoutes } = await import('./migrations.js');\n return {\n register: (authenticated, deps) => {\n registerDoctorRoutes(authenticated, deps);\n registerMigrationRoutes(authenticated, deps);\n },\n };\n },\n },\n {\n id: 'dreaming',\n match: (path) => startsWithAny(path, ['/api/dreaming']),\n load: async () => {\n const { registerDreamingRoutes } = await import('./dreaming.js');\n return { register: registerDreamingRoutes };\n },\n },\n {\n id: 'agents',\n match: (path) => startsWithAny(path, ['/api/agents', '/api/user-profile', '/api/voice/models']),\n load: async () => {\n const { registerAgentsRoutes } = await import('./agents.js');\n return { register: registerAgentsRoutes };\n },\n },\n {\n id: 'capability-presets',\n match: (path) => startsWithAny(path, ['/api/capability-presets', '/api/global-defaults']),\n load: async () => {\n const { registerCapabilityPresetsRoutes } = await import('./capability-presets.js');\n const { registerGlobalDefaultsRoutes } = await import('./global-defaults.js');\n return {\n register: (authenticated, deps) => {\n registerCapabilityPresetsRoutes(authenticated, deps);\n registerGlobalDefaultsRoutes(authenticated, deps);\n },\n };\n },\n },\n {\n id: 'auth-registry-extensions',\n match: (path) =>\n startsWithAny(path, [\n '/api/auth',\n '/api/registry',\n '/api/extensions',\n '/api/context',\n '/api/marketplace',\n ]),\n load: async () => {\n const { registerAuthRegistryExtensionsRoutes } = await import('./auth-registry-extensions.js');\n return { register: registerAuthRegistryExtensionsRoutes };\n },\n },\n {\n id: 'models',\n match: (path) =>\n startsWithAny(path, ['/api/models', '/api/models-json', '/api/providers', '/api/image']),\n load: async () => {\n const { registerModelsRoutes } = await import('./models.js');\n return { register: registerModelsRoutes };\n },\n },\n {\n id: 'ai-assist',\n match: (path) => startsWithAny(path, ['/api/ai']),\n load: async () => {\n const { registerAiAssistRoutes } = await import('./ai-assist.js');\n return { register: registerAiAssistRoutes };\n },\n },\n {\n id: 'commands-skills',\n match: (path) => startsWithAny(path, ['/api/commands', '/api/review', '/api/skills', '/api/chat/skills']),\n load: async () => {\n const { registerCommandsSkillsRoutes } = await import('./commands-skills.js');\n return { register: registerCommandsSkillsRoutes };\n },\n },\n {\n id: 'automations',\n match: (path) => startsWithAny(path, ['/api/automations', '/api/automation-runs']),\n load: async () => {\n const { registerAutomationRoutes } = await import('../../../automations/api/routes.js');\n return { register: registerAutomationRoutes };\n },\n },\n {\n id: 'goals',\n match: (path) => startsWithAny(path, ['/api/goals']),\n load: async () => {\n const { registerGoalsRoutes } = await import('./goals.js');\n return { register: registerGoalsRoutes };\n },\n },\n {\n id: 'notes',\n match: (path) => startsWithAny(path, ['/api/notes']),\n load: async () => {\n const { registerNotesRoutes } = await import('./notes.js');\n return { register: registerNotesRoutes };\n },\n },\n {\n id: 'home',\n match: (path) => startsWithAny(path, ['/api/home']),\n load: async () => {\n const { registerHomeRoutes } = await import('./home.js');\n return { register: registerHomeRoutes };\n },\n },\n {\n id: 'workflows',\n match: (path) => startsWithAny(path, ['/api/workflows']),\n load: async () => {\n const { registerWorkflowRoutes } = await import('./workflows.js');\n return { register: registerWorkflowRoutes };\n },\n },\n {\n id: 'logs',\n match: (path) => startsWithAny(path, ['/api/logs']),\n load: async () => {\n const { registerLogsRoutes } = await import('./logs.js');\n return { register: registerLogsRoutes };\n },\n },\n {\n id: 'shares',\n match: (path) => startsWithAny(path, ['/api/shares']),\n load: async () => {\n const { registerShareRoutes } = await import('./shares.js');\n return { register: registerShareRoutes };\n },\n },\n {\n id: 'site-shares',\n match: (path) => startsWithAny(path, ['/api/site-shares']),\n load: async () => {\n const { registerSiteShareRoutes } = await import('./site-shares.js');\n return { register: registerSiteShareRoutes };\n },\n },\n {\n id: 'tunnel',\n match: (path) => startsWithAny(path, ['/api/tunnel']),\n load: async () => {\n const { registerTunnelRoutes } = await import('./tunnel.js');\n return { register: registerTunnelRoutes };\n },\n },\n {\n id: 'exposure',\n match: (path) => startsWithAny(path, ['/api/exposure']),\n load: async () => {\n const { registerExposureRoutes } = await import('./exposure.js');\n return { register: registerExposureRoutes };\n },\n },\n {\n id: 'extension-gateway',\n match: (path) => startsWithAny(path, ['/api/gateway']),\n load: async () => {\n const { registerExtensionGatewayRoutes } = await import('./extension-gateway.js');\n return { register: registerExtensionGatewayRoutes };\n },\n },\n {\n id: 'update',\n match: (path) => startsWithAny(path, ['/api/update']),\n load: async () => {\n const { registerUpdateRoutes } = await import('./update.js');\n return { register: registerUpdateRoutes };\n },\n },\n {\n id: 'voice',\n match: (path) => startsWithAny(path, ['/api/voice']) && path !== '/api/voice/models',\n load: async () => {\n const { registerVoiceRoutes } = await import('./voice.js');\n return { register: registerVoiceRoutes };\n },\n },\n {\n id: 'connectors',\n match: (path) => startsWithAny(path, ['/api/connectors']),\n load: async () => {\n const { registerConnectorRoutes } = await import('./connectors.js');\n return { register: registerConnectorRoutes };\n },\n },\n {\n id: 'tui',\n match: (path) => startsWithAny(path, ['/api/tui']),\n load: async () => {\n const { registerTuiRoutes } = await import('./tui.js');\n return { register: registerTuiRoutes };\n },\n },\n {\n id: 'mcp',\n match: (path) => startsWithAny(path, ['/api/mcp']),\n load: async () => {\n const { registerMcpRoutes } = await import('./mcp.js');\n return { register: registerMcpRoutes };\n },\n },\n];\n\nexport const APP_LAZY_ROUTE_BUNDLES: readonly AppLazyRouteBundle[] = [\n {\n id: 'shares-public',\n prefixes: ['/s'],\n match: (path) => startsWithAny(path, ['/s']),\n load: async () => {\n const { registerSharePublicRoutes } = await import('./shares.js');\n return { registerOnApp: registerSharePublicRoutes };\n },\n },\n {\n id: 'tunnel-public',\n prefixes: [\n '/api/tunnel/pair/ping',\n '/api/tunnel/pair/validate-url',\n '/api/tunnel/exchange-token',\n ],\n match: (path) =>\n path === '/api/tunnel/exchange-token' ||\n path === '/api/tunnel/pair/ping' ||\n path === '/api/tunnel/pair/validate-url',\n load: async () => {\n const { registerTunnelPublicRoutes } = await import('./tunnel.js');\n return { registerOnApp: registerTunnelPublicRoutes };\n },\n },\n];\n\nexport function findAuthenticatedLazyRouteBundle(path: string): AuthenticatedLazyRouteBundle | undefined {\n return AUTHENTICATED_LAZY_ROUTE_BUNDLES.find((bundle) => bundle.match(path));\n}\n"],"mappings":"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"}
@@ -5,7 +5,10 @@ import { getSessionMetadata } from "../../../storage/sqlite/session-repository.j
5
5
  import { loadTranscriptRowsForSession } from "../../../storage/sqlite/transcript-repository.js";
6
6
  import { listMemoryRecords, upsertMemoryRecord } from "../../../storage/sqlite/memory-records-repository.js";
7
7
  import "../../../storage/sqlite/index.js";
8
+ import { ProjectWorkspaceConflictError, ProjectWorkspaceMissingError } from "../../../projects/workspace-project.js";
8
9
  import { isValidProjectAgentId, normalizeProjectAgentId, resolveProjectAgentId } from "../../../projects/project-agent.js";
10
+ import { inferProjectKind } from "../../../projects/project-kind.js";
11
+ import { inferSuggestedProjectDefaultAgentId } from "../../../projects/project-agent-suggestion.js";
9
12
  import { buildProjectLoopOverview } from "../../../projects/project-loop.js";
10
13
  import "../../../projects/index.js";
11
14
  import { createGatewayRouteLogger } from "../lib/route-logger.js";
@@ -101,27 +104,57 @@ function registerProjectsRoutes(authenticated, deps) {
101
104
  authenticated.post("/api/projects", async (c) => {
102
105
  const body = await c.req.json().catch(() => ({}));
103
106
  const name = textField(body, "name")?.trim();
104
- if (!name) return c.json({
107
+ const workspaceRoot = textField(body, "workspaceRoot")?.trim();
108
+ if (!name && !workspaceRoot) return c.json({
105
109
  ok: false,
106
110
  error: "Missing name"
107
111
  }, 400);
108
- const defaultAgentId = normalizeProjectAgentId(textField(body, "defaultAgentId"));
112
+ const hasDefaultAgentPatch = Object.hasOwn(body, "defaultAgentId");
113
+ const explicitDefaultAgentId = normalizeProjectAgentId(textField(body, "defaultAgentId"));
114
+ const defaultAgentId = hasDefaultAgentPatch ? explicitDefaultAgentId : inferSuggestedProjectDefaultAgentId({
115
+ config: service.currentConfig,
116
+ name,
117
+ description: textField(body, "description"),
118
+ workspaceRoot,
119
+ projectKind: textField(body, "projectKind")
120
+ });
109
121
  if (!isValidProjectAgentId(service.currentConfig, defaultAgentId)) return c.json({
110
122
  ok: false,
111
123
  error: "Default agent not found"
112
124
  }, 400);
113
- const project = service.projects.create({
114
- name,
115
- description: textField(body, "description"),
116
- defaultAgentId,
117
- workspaceRoot: textField(body, "workspaceRoot"),
118
- brief: textField(body, "brief"),
119
- instructions: textField(body, "instructions")
120
- });
121
- return c.json({
122
- ok: true,
123
- project
124
- }, 201);
125
+ try {
126
+ const project = service.projects.create({
127
+ name,
128
+ description: textField(body, "description"),
129
+ defaultAgentId,
130
+ workspaceRoot,
131
+ createWorkspaceRoot: body.createWorkspaceRoot === true,
132
+ projectKind: textField(body, "projectKind"),
133
+ brief: textField(body, "brief"),
134
+ instructions: textField(body, "instructions")
135
+ });
136
+ return c.json({
137
+ ok: true,
138
+ project
139
+ }, 201);
140
+ } catch (error) {
141
+ if (error instanceof ProjectWorkspaceConflictError) return c.json({
142
+ ok: false,
143
+ code: "workspace_already_bound",
144
+ error: error.message,
145
+ project: error.project
146
+ }, 409);
147
+ if (error instanceof ProjectWorkspaceMissingError) return c.json({
148
+ ok: false,
149
+ code: "workspace_root_missing",
150
+ error: error.message,
151
+ workspaceRoot: error.workspaceRoot
152
+ }, 409);
153
+ return c.json({
154
+ ok: false,
155
+ error: error instanceof Error ? error.message : String(error)
156
+ }, 400);
157
+ }
125
158
  });
126
159
  authenticated.get("/api/projects", async (c) => {
127
160
  const status = parseProjectStatus(c.req.query("status"));
@@ -145,6 +178,82 @@ function registerProjectsRoutes(authenticated, deps) {
145
178
  suggestions: sessionKey ? service.projects.suggestProjectsForSession(sessionKey) : []
146
179
  });
147
180
  });
181
+ authenticated.post("/api/projects/infer-defaults", async (c) => {
182
+ const body = await c.req.json().catch(() => ({}));
183
+ const input = {
184
+ name: textField(body, "name"),
185
+ description: textField(body, "description"),
186
+ workspaceRoot: textField(body, "workspaceRoot"),
187
+ projectKind: textField(body, "projectKind")
188
+ };
189
+ const inference = inferProjectKind(input);
190
+ const defaultAgentId = inferSuggestedProjectDefaultAgentId({
191
+ config: service.currentConfig,
192
+ ...input
193
+ });
194
+ return c.json({
195
+ ok: true,
196
+ inference,
197
+ defaultAgentId
198
+ });
199
+ });
200
+ authenticated.post("/api/projects/resolve-workspace", async (c) => {
201
+ const body = await c.req.json().catch(() => ({}));
202
+ const workspacePath = textField(body, "workspacePath")?.trim();
203
+ if (!workspacePath) return c.json({
204
+ ok: false,
205
+ error: "Missing workspacePath"
206
+ }, 400);
207
+ const agentId = textField(body, "agentId")?.trim() || "main";
208
+ const suggestedDefaultAgentId = inferSuggestedProjectDefaultAgentId({
209
+ config: service.currentConfig,
210
+ workspaceRoot: workspacePath,
211
+ projectKind: textField(body, "projectKind")
212
+ });
213
+ let match;
214
+ try {
215
+ match = service.projects.resolveOrCreateForWorkspacePath({
216
+ workspacePath,
217
+ agentId,
218
+ defaultAgentId: suggestedDefaultAgentId,
219
+ autoCreate: body.autoCreate !== false
220
+ });
221
+ } catch (error) {
222
+ if (error instanceof ProjectWorkspaceConflictError) match = {
223
+ project: error.project,
224
+ reason: "exact",
225
+ created: false
226
+ };
227
+ else throw error;
228
+ }
229
+ if (!match) return c.json({
230
+ ok: true,
231
+ project: null
232
+ });
233
+ const sessionKey = textField(body, "sessionKey")?.trim();
234
+ const projectDefaultAgentId = normalizeProjectAgentId(match.project.defaultAgentId);
235
+ const shouldBindRequestedSession = !projectDefaultAgentId || projectDefaultAgentId === normalizeProjectAgentId(agentId);
236
+ if (sessionKey && shouldBindRequestedSession) {
237
+ if (!getSessionMetadata(sessionKey)) await service.sessionIndexInstance.saveMessages(sessionKey, [], { metadata: {
238
+ sourceChannel: "tui",
239
+ sourceChatId: `default:direct:${sessionKey}`,
240
+ sessionType: "chat",
241
+ projectId: match.project.id,
242
+ routing: {
243
+ agentId,
244
+ source: "tui",
245
+ accountId: "default",
246
+ peerKind: "direct",
247
+ peerId: sessionKey
248
+ }
249
+ } });
250
+ service.projects.attachSession(sessionKey, match.project.id);
251
+ }
252
+ return c.json({
253
+ ok: true,
254
+ ...match
255
+ });
256
+ });
148
257
  authenticated.get("/api/projects/:id/overview", async (c) => {
149
258
  const project = service.projects.getWithDetails(c.req.param("id"));
150
259
  if (!project) return c.json({
@@ -298,6 +407,7 @@ function registerProjectsRoutes(authenticated, deps) {
298
407
  ...status ? { status } : {},
299
408
  ...defaultAgentPatch !== void 0 ? { defaultAgentId: defaultAgentId ?? null } : {},
300
409
  ...optionalTextField(body, "workspaceRoot") !== void 0 ? { workspaceRoot: optionalTextField(body, "workspaceRoot") } : {},
410
+ createWorkspaceRoot: body.createWorkspaceRoot === true,
301
411
  ...optionalTextField(body, "brief") !== void 0 ? { brief: optionalTextField(body, "brief") } : {},
302
412
  ...optionalTextField(body, "instructions") !== void 0 ? { instructions: optionalTextField(body, "instructions") } : {}
303
413
  });
@@ -306,6 +416,18 @@ function registerProjectsRoutes(authenticated, deps) {
306
416
  project
307
417
  });
308
418
  } catch (error) {
419
+ if (error instanceof ProjectWorkspaceConflictError) return c.json({
420
+ ok: false,
421
+ code: "workspace_already_bound",
422
+ error: error.message,
423
+ project: error.project
424
+ }, 409);
425
+ if (error instanceof ProjectWorkspaceMissingError) return c.json({
426
+ ok: false,
427
+ code: "workspace_root_missing",
428
+ error: error.message,
429
+ workspaceRoot: error.workspaceRoot
430
+ }, 409);
309
431
  return c.json({
310
432
  ok: false,
311
433
  error: error instanceof Error ? error.message : String(error)
@@ -1 +1 @@
1
- {"version":3,"file":"projects.js","names":["path","record"],"sources":["../../../../../src/gateway/hono/routes/projects.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { readdir, realpath, stat } from 'node:fs/promises';\nimport * as path from 'node:path';\n\nimport type { GoalWithDetails } from '../../../goals/index.js';\nimport { GoalService } from '../../../goals/index.js';\nimport {\n buildProjectLoopOverview,\n isValidProjectAgentId,\n normalizeProjectAgentId,\n resolveProjectAgentId,\n type ProjectStatus,\n type ProjectWorkflowRunBrief,\n} from '../../../projects/index.js';\nimport {\n getSqliteDatabase,\n getSessionMetadata,\n listMemoryRecords,\n loadTranscriptRowsForSession,\n upsertMemoryRecord,\n} from '../../../storage/sqlite/index.js';\nimport { createGatewayRouteLogger } from '../lib/route-logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createGatewayRouteLogger('Projects');\n\nfunction parseProjectStatus(raw: unknown): ProjectStatus | undefined {\n return raw === 'active' || raw === 'paused' || raw === 'archived' ? raw : undefined;\n}\n\nfunction parseLimit(raw: string | undefined, fallback = 50): number | undefined {\n if (!raw) return undefined;\n const n = Number.parseInt(raw, 10);\n return Number.isFinite(n) ? Math.min(500, Math.max(1, n)) : fallback;\n}\n\nfunction textField(body: Record<string, unknown>, key: string): string | undefined {\n const value = body[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction optionalTextField(body: Record<string, unknown>, key: string): string | null | undefined {\n if (!(key in body)) return undefined;\n const value = body[key];\n return typeof value === 'string' ? value : null;\n}\n\nconst SKIP_FILE_NAMES = new Set(['.git', 'node_modules']);\n\nfunction normalizeRelativePath(raw: string | undefined): string {\n const normalized = (raw ?? '').trim().replaceAll('\\\\', '/');\n return normalized.replace(/^\\/+/, '');\n}\n\nfunction isWithinDirectory(root: string, target: string): boolean {\n const relativePath = path.relative(root, target);\n return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));\n}\n\nfunction textFromTranscriptContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((part) => {\n if (!part || typeof part !== 'object') return '';\n const record = part as Record<string, unknown>;\n return typeof record.text === 'string' ? record.text : '';\n })\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction buildSessionSummaryMemoryContent(sessionKey: string, explicitSummary?: string): string {\n const summary = explicitSummary?.trim();\n if (summary) return summary;\n const rows = loadTranscriptRowsForSession(sessionKey)\n .filter((row) => {\n if (!row || typeof row !== 'object') return false;\n const record = row as unknown as Record<string, unknown>;\n const role = record.role;\n return role === 'user' || role === 'assistant' || role === 'compactionSummary' || role === 'branchSummary';\n })\n .slice(-12);\n const lines = rows\n .map((row) => {\n const record = row as unknown as Record<string, unknown>;\n const rowRole = typeof record.role === 'string' ? record.role : 'message';\n const role = rowRole === 'compactionSummary' || rowRole === 'branchSummary' ? 'summary' : rowRole;\n const text = (typeof record.summary === 'string' ? record.summary : undefined) ?? textFromTranscriptContent(record.content);\n const compact = text.trim().replace(/\\s+/g, ' ').slice(0, 280);\n return compact ? `- ${role}: ${compact}` : '';\n })\n .filter(Boolean);\n return lines.length > 0\n ? [`Session summary for ${sessionKey}:`, ...lines].join('\\n')\n : `Session summary for ${sessionKey}: no transcript content available.`;\n}\n\nfunction listFailedProjectWorkflowRuns(projectId: string, limit = 5): ProjectWorkflowRunBrief[] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT run_id, definition_id, status, created_at_ms, error_message\n FROM workflow_runs\n WHERE project_id = ? AND status IN ('failed', 'timeout', 'cancelled')\n ORDER BY created_at_ms DESC\n LIMIT ?`,\n )\n .all(projectId, Math.max(1, Math.min(20, Math.floor(limit)))) as Array<{\n run_id: string;\n definition_id: string;\n status: string;\n created_at_ms: number;\n error_message: string | null;\n }>;\n return rows.map((row) => ({\n runId: row.run_id,\n definitionId: row.definition_id,\n status: row.status,\n createdAt: row.created_at_ms,\n errorMessage: row.error_message ?? undefined,\n }));\n}\n\nfunction buildProjectDigestMemoryContent(input: ReturnType<typeof buildProjectLoopOverview> & { projectName: string }): string {\n const lines = [\n `Project digest: ${input.projectName}`,\n `Status: ${input.digest.summary}`,\n input.digest.nextAction ? `Next: ${input.digest.nextAction}` : undefined,\n ].filter(Boolean) as string[];\n if (input.attentionItems.length > 0) {\n lines.push('', 'Attention:');\n for (const item of input.attentionItems.slice(0, 8)) {\n lines.push(`- ${item.kind}: ${item.title}${item.detail ? ` - ${item.detail}` : ''}`);\n }\n }\n if (input.timeline.length > 0) {\n lines.push('', 'Recent activity:');\n for (const item of input.timeline.slice(0, 8)) {\n lines.push(`- ${item.kind}: ${item.title}${item.detail ? ` - ${item.detail}` : ''}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction projectDigestMemoryRecordId(projectId: string): string {\n return `project-digest:${projectId}`;\n}\n\nexport function registerProjectsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n const goals = new GoalService();\n\n authenticated.post('/api/projects', async (c) => {\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const name = textField(body, 'name')?.trim();\n if (!name) return c.json({ ok: false, error: 'Missing name' }, 400);\n const defaultAgentId = normalizeProjectAgentId(textField(body, 'defaultAgentId'));\n if (!isValidProjectAgentId(service.currentConfig, defaultAgentId)) {\n return c.json({ ok: false, error: 'Default agent not found' }, 400);\n }\n const project = service.projects.create({\n name,\n description: textField(body, 'description'),\n defaultAgentId,\n workspaceRoot: textField(body, 'workspaceRoot'),\n brief: textField(body, 'brief'),\n instructions: textField(body, 'instructions'),\n });\n return c.json({ ok: true, project }, 201);\n });\n\n authenticated.get('/api/projects', async (c) => {\n const status = parseProjectStatus(c.req.query('status'));\n const result = service.projects.list({\n ...(status ? { status } : {}),\n search: c.req.query('search'),\n sortBy: c.req.query('sortBy') as 'updatedAt' | 'createdAt' | 'name' | undefined,\n sortOrder: c.req.query('sortOrder') as 'asc' | 'desc' | undefined,\n limit: parseLimit(c.req.query('limit')),\n offset: c.req.query('offset') ? Math.max(0, Number.parseInt(c.req.query('offset')!, 10) || 0) : undefined,\n });\n return c.json({ ok: true, ...result });\n });\n\n authenticated.get('/api/projects/suggestions', async (c) => {\n const sessionKey = c.req.query('sessionKey')?.trim();\n return c.json({ ok: true, suggestions: sessionKey ? service.projects.suggestProjectsForSession(sessionKey) : [] });\n });\n\n authenticated.get('/api/projects/:id/overview', async (c) => {\n const project = service.projects.getWithDetails(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n const goalIds = service.projects.listGoalIds(project.id, parseLimit(c.req.query('goalLimit'), 100));\n const projectGoals = goalIds\n .map((id) => goals.get(id))\n .filter((goal): goal is GoalWithDetails => Boolean(goal));\n const loop = buildProjectLoopOverview({\n project,\n goals: projectGoals,\n recentWorkflowRuns: project.recentWorkflowRuns,\n failedWorkflowRuns: listFailedProjectWorkflowRuns(project.id),\n memoryRecords: listMemoryRecords({ projectId: project.id, status: 'active', limit: 5 }),\n });\n return c.json({\n ok: true,\n overview: {\n project,\n stats: {\n sessionCount: project.sessionCount,\n goalCount: project.goalCount,\n activeGoalCount: project.activeGoalCount,\n recentWorkflowRunCount: project.recentWorkflowRuns.length,\n staleGoalCount: loop.staleGoals.length,\n attentionCount: loop.attentionItems.length,\n failedWorkflowRunCount: loop.failedWorkflowRuns.length,\n },\n activeGoals: loop.activeGoals,\n blockedGoals: loop.blockedGoals,\n staleGoals: loop.staleGoals,\n nextActions: loop.nextActions,\n attentionItems: loop.attentionItems,\n timeline: loop.timeline,\n digest: loop.digest,\n failedWorkflowRuns: loop.failedWorkflowRuns,\n recentSessions: project.recentSessions,\n recentWorkflowRuns: project.recentWorkflowRuns,\n recommendedAction: loop.recommendedAction,\n },\n });\n });\n\n authenticated.post('/api/projects/:id/digest-memory', async (c) => {\n const project = service.projects.getWithDetails(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n const goalIds = service.projects.listGoalIds(project.id, 100);\n const projectGoals = goalIds\n .map((id) => goals.get(id))\n .filter((goal): goal is GoalWithDetails => Boolean(goal));\n const loop = buildProjectLoopOverview({\n project,\n goals: projectGoals,\n recentWorkflowRuns: project.recentWorkflowRuns,\n failedWorkflowRuns: listFailedProjectWorkflowRuns(project.id),\n memoryRecords: listMemoryRecords({ projectId: project.id, status: 'active', limit: 5 }),\n });\n const record = upsertMemoryRecord({\n id: projectDigestMemoryRecordId(project.id),\n providerId: 'local',\n kind: 'daily_note',\n agentId: resolveProjectAgentId({\n config: service.currentConfig,\n projects: service.projects,\n projectId: project.id,\n }),\n workspaceId: project.workspaceRoot,\n projectId: project.id,\n content: buildProjectDigestMemoryContent({ ...loop, projectName: project.name }),\n source: {\n provider: 'project-digest',\n },\n confidence: 0.75,\n tags: ['project', 'project-digest', project.slug],\n status: 'active',\n sensitivity: 'normal',\n });\n return c.json({ ok: true, record }, 201);\n });\n\n authenticated.post('/api/projects/:id/blockers', async (c) => {\n const project = service.projects.getWithDetails(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const title = typeof body.title === 'string' ? body.title.trim() : '';\n if (!title) return c.json({ ok: false, error: 'Missing title' }, 400);\n const reason = typeof body.reason === 'string' ? body.reason.trim() : '';\n const goal = goals.create({\n title,\n description: reason || undefined,\n agentId: resolveProjectAgentId({\n config: service.currentConfig,\n projects: service.projects,\n projectId: project.id,\n }),\n priority: body.priority === 'low' || body.priority === 'normal' || body.priority === 'high' ? body.priority : 'high',\n source: 'api',\n projectId: project.id,\n });\n const blocked = goals.setStatus(goal.id, 'blocked', { reason: reason || title });\n if (reason) {\n goals.setContextMessage({ goalId: goal.id, text: reason });\n }\n return c.json({ ok: true, goal: blocked ?? goals.get(goal.id) }, 201);\n });\n\n authenticated.get('/api/projects/:id', async (c) => {\n const project = service.projects.getWithDetails(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n return c.json({ ok: true, project });\n });\n\n authenticated.patch('/api/projects/:id', async (c) => {\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const status = parseProjectStatus(body.status);\n const defaultAgentPatch = optionalTextField(body, 'defaultAgentId');\n const defaultAgentId = defaultAgentPatch === undefined ? undefined : normalizeProjectAgentId(defaultAgentPatch);\n if (defaultAgentId && !isValidProjectAgentId(service.currentConfig, defaultAgentId)) {\n return c.json({ ok: false, error: 'Default agent not found' }, 400);\n }\n try {\n const project = service.projects.update(c.req.param('id'), {\n ...(textField(body, 'name') !== undefined ? { name: textField(body, 'name') } : {}),\n ...(optionalTextField(body, 'description') !== undefined ? { description: optionalTextField(body, 'description') } : {}),\n ...(status ? { status } : {}),\n ...(defaultAgentPatch !== undefined ? { defaultAgentId: defaultAgentId ?? null } : {}),\n ...(optionalTextField(body, 'workspaceRoot') !== undefined ? { workspaceRoot: optionalTextField(body, 'workspaceRoot') } : {}),\n ...(optionalTextField(body, 'brief') !== undefined ? { brief: optionalTextField(body, 'brief') } : {}),\n ...(optionalTextField(body, 'instructions') !== undefined ? { instructions: optionalTextField(body, 'instructions') } : {}),\n });\n return c.json({ ok: true, project });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.delete('/api/projects/:id', async (c) => {\n service.projects.delete(c.req.param('id'));\n return c.json({ ok: true });\n });\n\n authenticated.get('/api/projects/:id/sessions', async (c) => {\n try {\n const keys = service.projects.listSessionKeys(c.req.param('id'), parseLimit(c.req.query('limit'), 100));\n const sessions = await Promise.all(keys.map((key) => service.sessions.getSession(key)));\n return c.json({ ok: true, sessions: sessions.filter(Boolean) });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.get('/api/projects/:id/goals', async (c) => {\n try {\n const ids = service.projects.listGoalIds(c.req.param('id'), parseLimit(c.req.query('limit'), 100));\n return c.json({ ok: true, goals: ids.map((id) => goals.get(id)).filter(Boolean) });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.get('/api/projects/:id/files', async (c) => {\n const project = service.projects.get(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n if (!project.workspaceRoot?.trim()) {\n return c.json({ ok: false, error: 'Project workspace root is not set' }, 400);\n }\n\n let root: string;\n try {\n root = await realpath(project.workspaceRoot);\n const rootStat = await stat(root);\n if (!rootStat.isDirectory()) {\n return c.json({ ok: false, error: 'Project workspace root is not a directory' }, 400);\n }\n } catch (err) {\n log.warn({ err, projectId: project.id, path: project.workspaceRoot }, 'project workspace root unavailable');\n return c.json({ ok: false, error: 'Project workspace root is unavailable' }, 404);\n }\n\n const requestedPath = normalizeRelativePath(c.req.query('path'));\n const candidate = path.resolve(root, requestedPath);\n if (!isWithinDirectory(root, candidate)) {\n return c.json({ ok: false, error: 'Path is outside project workspace' }, 400);\n }\n\n let currentPath: string;\n try {\n currentPath = await realpath(candidate);\n if (!isWithinDirectory(root, currentPath)) {\n return c.json({ ok: false, error: 'Path is outside project workspace' }, 400);\n }\n const currentStat = await stat(currentPath);\n if (!currentStat.isDirectory()) {\n return c.json({ ok: false, error: 'Path is not a directory' }, 400);\n }\n } catch (err) {\n log.warn({ err, projectId: project.id, path: candidate }, 'project file path unavailable');\n return c.json({ ok: false, error: 'Path not found' }, 404);\n }\n\n try {\n const dirents = await readdir(currentPath, { withFileTypes: true });\n const entries = await Promise.all(dirents\n .filter((entry) => !SKIP_FILE_NAMES.has(entry.name))\n .map(async (entry) => {\n const absolutePath = path.join(currentPath, entry.name);\n const entryStat = await stat(absolutePath).catch(() => null);\n const relativePath = path.relative(root, absolutePath).split(path.sep).join('/');\n return {\n name: entry.name,\n path: relativePath,\n type: entry.isDirectory() ? 'directory' : 'file',\n size: entryStat?.isFile() ? entryStat.size : undefined,\n updatedAt: entryStat?.mtime.toISOString(),\n };\n }));\n entries.sort((left, right) => {\n if (left.type !== right.type) return left.type === 'directory' ? -1 : 1;\n return left.name.localeCompare(right.name);\n });\n const relativeCurrentPath = path.relative(root, currentPath).split(path.sep).join('/');\n const parentRelativePath = relativeCurrentPath ? path.dirname(relativeCurrentPath).replaceAll('\\\\', '/') : null;\n return c.json({\n ok: true,\n root,\n path: relativeCurrentPath === '.' ? '' : relativeCurrentPath,\n parentPath: parentRelativePath === '.' ? '' : parentRelativePath,\n entries,\n });\n } catch (err) {\n log.warn({ err, projectId: project.id, path: currentPath }, 'project file list failed');\n if ((err as NodeJS.ErrnoException)?.code === 'EACCES') {\n return c.json({ ok: false, error: 'Permission denied' }, 403);\n }\n return c.json({ ok: false, error: 'Failed to read project files' }, 500);\n }\n });\n\n authenticated.post('/api/projects/:id/sessions/:sessionKey', async (c) => {\n try {\n service.projects.attachSession(c.req.param('sessionKey'), c.req.param('id'));\n const session = await service.sessions.getSession(c.req.param('sessionKey'));\n return c.json({ ok: true, session });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.delete('/api/projects/:id/sessions/:sessionKey', async (c) => {\n try {\n const projectId = c.req.param('id');\n const sessionKey = c.req.param('sessionKey');\n if (!service.projects.get(projectId)) {\n return c.json({ ok: false, error: 'Project not found' }, 404);\n }\n const session = await service.sessions.getSession(sessionKey);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n if (session.projectId !== projectId) {\n return c.json({ ok: false, error: 'Session is not attached to this project' }, 409);\n }\n service.projects.detachSession(sessionKey);\n return c.json({ ok: true });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.post('/api/projects/:id/sessions/:sessionKey/summary-memory', async (c) => {\n const projectId = c.req.param('id');\n const sessionKey = c.req.param('sessionKey');\n const project = service.projects.get(projectId);\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n const session = getSessionMetadata(sessionKey);\n if (!session) return c.json({ ok: false, error: 'Session not found' }, 404);\n if (session.projectId !== projectId) {\n return c.json({ ok: false, error: 'Session is not attached to this project' }, 409);\n }\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const content = buildSessionSummaryMemoryContent(\n sessionKey,\n typeof body.summary === 'string' ? body.summary : undefined,\n );\n const record = upsertMemoryRecord({\n providerId: 'local',\n kind: 'session_summary',\n agentId: session.routing?.agentId ?? 'main',\n workspaceId: session.cwd,\n sessionKey,\n projectId,\n content,\n source: {\n provider: 'project-summary',\n sessionEntryId: sessionKey,\n },\n confidence: typeof body.confidence === 'number' ? body.confidence : 0.7,\n tags: ['project', 'session-summary', project.slug],\n status: 'active',\n sensitivity: 'normal',\n evidence: [{ sessionKey }],\n });\n return c.json({ ok: true, record }, 201);\n });\n\n authenticated.post('/api/projects/:id/goals/:goalId', async (c) => {\n try {\n service.projects.attachGoal(c.req.param('goalId'), c.req.param('id'));\n return c.json({ ok: true, goal: goals.get(c.req.param('goalId')) });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.delete('/api/projects/:id/goals/:goalId', async (c) => {\n try {\n const projectId = c.req.param('id');\n const goalId = c.req.param('goalId');\n if (!service.projects.get(projectId)) {\n return c.json({ ok: false, error: 'Project not found' }, 404);\n }\n const goal = goals.get(goalId);\n if (!goal) {\n return c.json({ ok: false, error: 'Goal not found' }, 404);\n }\n if (goal.projectId !== projectId) {\n return c.json({ ok: false, error: 'Goal is not attached to this project' }, 409);\n }\n service.projects.detachGoal(goalId);\n return c.json({ ok: true });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"projects.js","names":["path","record"],"sources":["../../../../../src/gateway/hono/routes/projects.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { readdir, realpath, stat } from 'node:fs/promises';\nimport * as path from 'node:path';\n\nimport type { GoalWithDetails } from '../../../goals/index.js';\nimport { GoalService } from '../../../goals/index.js';\nimport {\n buildProjectLoopOverview,\n inferProjectKind,\n inferSuggestedProjectDefaultAgentId,\n isValidProjectAgentId,\n normalizeProjectAgentId,\n ProjectWorkspaceConflictError,\n ProjectWorkspaceMissingError,\n resolveProjectAgentId,\n type ProjectStatus,\n type ProjectWorkflowRunBrief,\n} from '../../../projects/index.js';\nimport {\n getSqliteDatabase,\n getSessionMetadata,\n listMemoryRecords,\n loadTranscriptRowsForSession,\n upsertMemoryRecord,\n} from '../../../storage/sqlite/index.js';\nimport { createGatewayRouteLogger } from '../lib/route-logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createGatewayRouteLogger('Projects');\n\nfunction parseProjectStatus(raw: unknown): ProjectStatus | undefined {\n return raw === 'active' || raw === 'paused' || raw === 'archived' ? raw : undefined;\n}\n\nfunction parseLimit(raw: string | undefined, fallback = 50): number | undefined {\n if (!raw) return undefined;\n const n = Number.parseInt(raw, 10);\n return Number.isFinite(n) ? Math.min(500, Math.max(1, n)) : fallback;\n}\n\nfunction textField(body: Record<string, unknown>, key: string): string | undefined {\n const value = body[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction optionalTextField(body: Record<string, unknown>, key: string): string | null | undefined {\n if (!(key in body)) return undefined;\n const value = body[key];\n return typeof value === 'string' ? value : null;\n}\n\nconst SKIP_FILE_NAMES = new Set(['.git', 'node_modules']);\n\nfunction normalizeRelativePath(raw: string | undefined): string {\n const normalized = (raw ?? '').trim().replaceAll('\\\\', '/');\n return normalized.replace(/^\\/+/, '');\n}\n\nfunction isWithinDirectory(root: string, target: string): boolean {\n const relativePath = path.relative(root, target);\n return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));\n}\n\nfunction textFromTranscriptContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((part) => {\n if (!part || typeof part !== 'object') return '';\n const record = part as Record<string, unknown>;\n return typeof record.text === 'string' ? record.text : '';\n })\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction buildSessionSummaryMemoryContent(sessionKey: string, explicitSummary?: string): string {\n const summary = explicitSummary?.trim();\n if (summary) return summary;\n const rows = loadTranscriptRowsForSession(sessionKey)\n .filter((row) => {\n if (!row || typeof row !== 'object') return false;\n const record = row as unknown as Record<string, unknown>;\n const role = record.role;\n return role === 'user' || role === 'assistant' || role === 'compactionSummary' || role === 'branchSummary';\n })\n .slice(-12);\n const lines = rows\n .map((row) => {\n const record = row as unknown as Record<string, unknown>;\n const rowRole = typeof record.role === 'string' ? record.role : 'message';\n const role = rowRole === 'compactionSummary' || rowRole === 'branchSummary' ? 'summary' : rowRole;\n const text = (typeof record.summary === 'string' ? record.summary : undefined) ?? textFromTranscriptContent(record.content);\n const compact = text.trim().replace(/\\s+/g, ' ').slice(0, 280);\n return compact ? `- ${role}: ${compact}` : '';\n })\n .filter(Boolean);\n return lines.length > 0\n ? [`Session summary for ${sessionKey}:`, ...lines].join('\\n')\n : `Session summary for ${sessionKey}: no transcript content available.`;\n}\n\nfunction listFailedProjectWorkflowRuns(projectId: string, limit = 5): ProjectWorkflowRunBrief[] {\n const rows = getSqliteDatabase()\n .prepare(\n `SELECT run_id, definition_id, status, created_at_ms, error_message\n FROM workflow_runs\n WHERE project_id = ? AND status IN ('failed', 'timeout', 'cancelled')\n ORDER BY created_at_ms DESC\n LIMIT ?`,\n )\n .all(projectId, Math.max(1, Math.min(20, Math.floor(limit)))) as Array<{\n run_id: string;\n definition_id: string;\n status: string;\n created_at_ms: number;\n error_message: string | null;\n }>;\n return rows.map((row) => ({\n runId: row.run_id,\n definitionId: row.definition_id,\n status: row.status,\n createdAt: row.created_at_ms,\n errorMessage: row.error_message ?? undefined,\n }));\n}\n\nfunction buildProjectDigestMemoryContent(input: ReturnType<typeof buildProjectLoopOverview> & { projectName: string }): string {\n const lines = [\n `Project digest: ${input.projectName}`,\n `Status: ${input.digest.summary}`,\n input.digest.nextAction ? `Next: ${input.digest.nextAction}` : undefined,\n ].filter(Boolean) as string[];\n if (input.attentionItems.length > 0) {\n lines.push('', 'Attention:');\n for (const item of input.attentionItems.slice(0, 8)) {\n lines.push(`- ${item.kind}: ${item.title}${item.detail ? ` - ${item.detail}` : ''}`);\n }\n }\n if (input.timeline.length > 0) {\n lines.push('', 'Recent activity:');\n for (const item of input.timeline.slice(0, 8)) {\n lines.push(`- ${item.kind}: ${item.title}${item.detail ? ` - ${item.detail}` : ''}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction projectDigestMemoryRecordId(projectId: string): string {\n return `project-digest:${projectId}`;\n}\n\nexport function registerProjectsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n const goals = new GoalService();\n\n authenticated.post('/api/projects', async (c) => {\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const name = textField(body, 'name')?.trim();\n const workspaceRoot = textField(body, 'workspaceRoot')?.trim();\n if (!name && !workspaceRoot) return c.json({ ok: false, error: 'Missing name' }, 400);\n const hasDefaultAgentPatch = Object.hasOwn(body, 'defaultAgentId');\n const explicitDefaultAgentId = normalizeProjectAgentId(textField(body, 'defaultAgentId'));\n const defaultAgentId = hasDefaultAgentPatch\n ? explicitDefaultAgentId\n : inferSuggestedProjectDefaultAgentId({\n config: service.currentConfig,\n name,\n description: textField(body, 'description'),\n workspaceRoot,\n projectKind: textField(body, 'projectKind'),\n });\n if (!isValidProjectAgentId(service.currentConfig, defaultAgentId)) {\n return c.json({ ok: false, error: 'Default agent not found' }, 400);\n }\n try {\n const project = service.projects.create({\n name,\n description: textField(body, 'description'),\n defaultAgentId,\n workspaceRoot,\n createWorkspaceRoot: body.createWorkspaceRoot === true,\n projectKind: textField(body, 'projectKind'),\n brief: textField(body, 'brief'),\n instructions: textField(body, 'instructions'),\n });\n return c.json({ ok: true, project }, 201);\n } catch (error) {\n if (error instanceof ProjectWorkspaceConflictError) {\n return c.json({ ok: false, code: 'workspace_already_bound', error: error.message, project: error.project }, 409);\n }\n if (error instanceof ProjectWorkspaceMissingError) {\n return c.json({ ok: false, code: 'workspace_root_missing', error: error.message, workspaceRoot: error.workspaceRoot }, 409);\n }\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 400);\n }\n });\n\n authenticated.get('/api/projects', async (c) => {\n const status = parseProjectStatus(c.req.query('status'));\n const result = service.projects.list({\n ...(status ? { status } : {}),\n search: c.req.query('search'),\n sortBy: c.req.query('sortBy') as 'updatedAt' | 'createdAt' | 'name' | undefined,\n sortOrder: c.req.query('sortOrder') as 'asc' | 'desc' | undefined,\n limit: parseLimit(c.req.query('limit')),\n offset: c.req.query('offset') ? Math.max(0, Number.parseInt(c.req.query('offset')!, 10) || 0) : undefined,\n });\n return c.json({ ok: true, ...result });\n });\n\n authenticated.get('/api/projects/suggestions', async (c) => {\n const sessionKey = c.req.query('sessionKey')?.trim();\n return c.json({ ok: true, suggestions: sessionKey ? service.projects.suggestProjectsForSession(sessionKey) : [] });\n });\n\n authenticated.post('/api/projects/infer-defaults', async (c) => {\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const input = {\n name: textField(body, 'name'),\n description: textField(body, 'description'),\n workspaceRoot: textField(body, 'workspaceRoot'),\n projectKind: textField(body, 'projectKind'),\n };\n const inference = inferProjectKind(input);\n const defaultAgentId = inferSuggestedProjectDefaultAgentId({\n config: service.currentConfig,\n ...input,\n });\n return c.json({ ok: true, inference, defaultAgentId });\n });\n\n authenticated.post('/api/projects/resolve-workspace', async (c) => {\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const workspacePath = textField(body, 'workspacePath')?.trim();\n if (!workspacePath) return c.json({ ok: false, error: 'Missing workspacePath' }, 400);\n const agentId = textField(body, 'agentId')?.trim() || 'main';\n const suggestedDefaultAgentId = inferSuggestedProjectDefaultAgentId({\n config: service.currentConfig,\n workspaceRoot: workspacePath,\n projectKind: textField(body, 'projectKind'),\n });\n let match;\n try {\n match = service.projects.resolveOrCreateForWorkspacePath({\n workspacePath,\n agentId,\n defaultAgentId: suggestedDefaultAgentId,\n autoCreate: body.autoCreate !== false,\n });\n } catch (error) {\n if (error instanceof ProjectWorkspaceConflictError) {\n match = { project: error.project, reason: 'exact' as const, created: false };\n } else {\n throw error;\n }\n }\n if (!match) return c.json({ ok: true, project: null });\n\n const sessionKey = textField(body, 'sessionKey')?.trim();\n const projectDefaultAgentId = normalizeProjectAgentId(match.project.defaultAgentId);\n const shouldBindRequestedSession = !projectDefaultAgentId || projectDefaultAgentId === normalizeProjectAgentId(agentId);\n if (sessionKey && shouldBindRequestedSession) {\n const existingSession = getSessionMetadata(sessionKey);\n if (!existingSession) {\n await service.sessionIndexInstance.saveMessages(sessionKey, [], {\n metadata: {\n sourceChannel: 'tui',\n sourceChatId: `default:direct:${sessionKey}`,\n sessionType: 'chat',\n projectId: match.project.id,\n routing: {\n agentId,\n source: 'tui',\n accountId: 'default',\n peerKind: 'direct',\n peerId: sessionKey,\n },\n },\n });\n }\n service.projects.attachSession(sessionKey, match.project.id);\n }\n\n return c.json({ ok: true, ...match });\n });\n\n authenticated.get('/api/projects/:id/overview', async (c) => {\n const project = service.projects.getWithDetails(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n const goalIds = service.projects.listGoalIds(project.id, parseLimit(c.req.query('goalLimit'), 100));\n const projectGoals = goalIds\n .map((id) => goals.get(id))\n .filter((goal): goal is GoalWithDetails => Boolean(goal));\n const loop = buildProjectLoopOverview({\n project,\n goals: projectGoals,\n recentWorkflowRuns: project.recentWorkflowRuns,\n failedWorkflowRuns: listFailedProjectWorkflowRuns(project.id),\n memoryRecords: listMemoryRecords({ projectId: project.id, status: 'active', limit: 5 }),\n });\n return c.json({\n ok: true,\n overview: {\n project,\n stats: {\n sessionCount: project.sessionCount,\n goalCount: project.goalCount,\n activeGoalCount: project.activeGoalCount,\n recentWorkflowRunCount: project.recentWorkflowRuns.length,\n staleGoalCount: loop.staleGoals.length,\n attentionCount: loop.attentionItems.length,\n failedWorkflowRunCount: loop.failedWorkflowRuns.length,\n },\n activeGoals: loop.activeGoals,\n blockedGoals: loop.blockedGoals,\n staleGoals: loop.staleGoals,\n nextActions: loop.nextActions,\n attentionItems: loop.attentionItems,\n timeline: loop.timeline,\n digest: loop.digest,\n failedWorkflowRuns: loop.failedWorkflowRuns,\n recentSessions: project.recentSessions,\n recentWorkflowRuns: project.recentWorkflowRuns,\n recommendedAction: loop.recommendedAction,\n },\n });\n });\n\n authenticated.post('/api/projects/:id/digest-memory', async (c) => {\n const project = service.projects.getWithDetails(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n const goalIds = service.projects.listGoalIds(project.id, 100);\n const projectGoals = goalIds\n .map((id) => goals.get(id))\n .filter((goal): goal is GoalWithDetails => Boolean(goal));\n const loop = buildProjectLoopOverview({\n project,\n goals: projectGoals,\n recentWorkflowRuns: project.recentWorkflowRuns,\n failedWorkflowRuns: listFailedProjectWorkflowRuns(project.id),\n memoryRecords: listMemoryRecords({ projectId: project.id, status: 'active', limit: 5 }),\n });\n const record = upsertMemoryRecord({\n id: projectDigestMemoryRecordId(project.id),\n providerId: 'local',\n kind: 'daily_note',\n agentId: resolveProjectAgentId({\n config: service.currentConfig,\n projects: service.projects,\n projectId: project.id,\n }),\n workspaceId: project.workspaceRoot,\n projectId: project.id,\n content: buildProjectDigestMemoryContent({ ...loop, projectName: project.name }),\n source: {\n provider: 'project-digest',\n },\n confidence: 0.75,\n tags: ['project', 'project-digest', project.slug],\n status: 'active',\n sensitivity: 'normal',\n });\n return c.json({ ok: true, record }, 201);\n });\n\n authenticated.post('/api/projects/:id/blockers', async (c) => {\n const project = service.projects.getWithDetails(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const title = typeof body.title === 'string' ? body.title.trim() : '';\n if (!title) return c.json({ ok: false, error: 'Missing title' }, 400);\n const reason = typeof body.reason === 'string' ? body.reason.trim() : '';\n const goal = goals.create({\n title,\n description: reason || undefined,\n agentId: resolveProjectAgentId({\n config: service.currentConfig,\n projects: service.projects,\n projectId: project.id,\n }),\n priority: body.priority === 'low' || body.priority === 'normal' || body.priority === 'high' ? body.priority : 'high',\n source: 'api',\n projectId: project.id,\n });\n const blocked = goals.setStatus(goal.id, 'blocked', { reason: reason || title });\n if (reason) {\n goals.setContextMessage({ goalId: goal.id, text: reason });\n }\n return c.json({ ok: true, goal: blocked ?? goals.get(goal.id) }, 201);\n });\n\n authenticated.get('/api/projects/:id', async (c) => {\n const project = service.projects.getWithDetails(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n return c.json({ ok: true, project });\n });\n\n authenticated.patch('/api/projects/:id', async (c) => {\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const status = parseProjectStatus(body.status);\n const defaultAgentPatch = optionalTextField(body, 'defaultAgentId');\n const defaultAgentId = defaultAgentPatch === undefined ? undefined : normalizeProjectAgentId(defaultAgentPatch);\n if (defaultAgentId && !isValidProjectAgentId(service.currentConfig, defaultAgentId)) {\n return c.json({ ok: false, error: 'Default agent not found' }, 400);\n }\n try {\n const project = service.projects.update(c.req.param('id'), {\n ...(textField(body, 'name') !== undefined ? { name: textField(body, 'name') } : {}),\n ...(optionalTextField(body, 'description') !== undefined ? { description: optionalTextField(body, 'description') } : {}),\n ...(status ? { status } : {}),\n ...(defaultAgentPatch !== undefined ? { defaultAgentId: defaultAgentId ?? null } : {}),\n ...(optionalTextField(body, 'workspaceRoot') !== undefined ? { workspaceRoot: optionalTextField(body, 'workspaceRoot') } : {}),\n createWorkspaceRoot: body.createWorkspaceRoot === true,\n ...(optionalTextField(body, 'brief') !== undefined ? { brief: optionalTextField(body, 'brief') } : {}),\n ...(optionalTextField(body, 'instructions') !== undefined ? { instructions: optionalTextField(body, 'instructions') } : {}),\n });\n return c.json({ ok: true, project });\n } catch (error) {\n if (error instanceof ProjectWorkspaceConflictError) {\n return c.json({ ok: false, code: 'workspace_already_bound', error: error.message, project: error.project }, 409);\n }\n if (error instanceof ProjectWorkspaceMissingError) {\n return c.json({ ok: false, code: 'workspace_root_missing', error: error.message, workspaceRoot: error.workspaceRoot }, 409);\n }\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.delete('/api/projects/:id', async (c) => {\n service.projects.delete(c.req.param('id'));\n return c.json({ ok: true });\n });\n\n authenticated.get('/api/projects/:id/sessions', async (c) => {\n try {\n const keys = service.projects.listSessionKeys(c.req.param('id'), parseLimit(c.req.query('limit'), 100));\n const sessions = await Promise.all(keys.map((key) => service.sessions.getSession(key)));\n return c.json({ ok: true, sessions: sessions.filter(Boolean) });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.get('/api/projects/:id/goals', async (c) => {\n try {\n const ids = service.projects.listGoalIds(c.req.param('id'), parseLimit(c.req.query('limit'), 100));\n return c.json({ ok: true, goals: ids.map((id) => goals.get(id)).filter(Boolean) });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.get('/api/projects/:id/files', async (c) => {\n const project = service.projects.get(c.req.param('id'));\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n if (!project.workspaceRoot?.trim()) {\n return c.json({ ok: false, error: 'Project workspace root is not set' }, 400);\n }\n\n let root: string;\n try {\n root = await realpath(project.workspaceRoot);\n const rootStat = await stat(root);\n if (!rootStat.isDirectory()) {\n return c.json({ ok: false, error: 'Project workspace root is not a directory' }, 400);\n }\n } catch (err) {\n log.warn({ err, projectId: project.id, path: project.workspaceRoot }, 'project workspace root unavailable');\n return c.json({ ok: false, error: 'Project workspace root is unavailable' }, 404);\n }\n\n const requestedPath = normalizeRelativePath(c.req.query('path'));\n const candidate = path.resolve(root, requestedPath);\n if (!isWithinDirectory(root, candidate)) {\n return c.json({ ok: false, error: 'Path is outside project workspace' }, 400);\n }\n\n let currentPath: string;\n try {\n currentPath = await realpath(candidate);\n if (!isWithinDirectory(root, currentPath)) {\n return c.json({ ok: false, error: 'Path is outside project workspace' }, 400);\n }\n const currentStat = await stat(currentPath);\n if (!currentStat.isDirectory()) {\n return c.json({ ok: false, error: 'Path is not a directory' }, 400);\n }\n } catch (err) {\n log.warn({ err, projectId: project.id, path: candidate }, 'project file path unavailable');\n return c.json({ ok: false, error: 'Path not found' }, 404);\n }\n\n try {\n const dirents = await readdir(currentPath, { withFileTypes: true });\n const entries = await Promise.all(dirents\n .filter((entry) => !SKIP_FILE_NAMES.has(entry.name))\n .map(async (entry) => {\n const absolutePath = path.join(currentPath, entry.name);\n const entryStat = await stat(absolutePath).catch(() => null);\n const relativePath = path.relative(root, absolutePath).split(path.sep).join('/');\n return {\n name: entry.name,\n path: relativePath,\n type: entry.isDirectory() ? 'directory' : 'file',\n size: entryStat?.isFile() ? entryStat.size : undefined,\n updatedAt: entryStat?.mtime.toISOString(),\n };\n }));\n entries.sort((left, right) => {\n if (left.type !== right.type) return left.type === 'directory' ? -1 : 1;\n return left.name.localeCompare(right.name);\n });\n const relativeCurrentPath = path.relative(root, currentPath).split(path.sep).join('/');\n const parentRelativePath = relativeCurrentPath ? path.dirname(relativeCurrentPath).replaceAll('\\\\', '/') : null;\n return c.json({\n ok: true,\n root,\n path: relativeCurrentPath === '.' ? '' : relativeCurrentPath,\n parentPath: parentRelativePath === '.' ? '' : parentRelativePath,\n entries,\n });\n } catch (err) {\n log.warn({ err, projectId: project.id, path: currentPath }, 'project file list failed');\n if ((err as NodeJS.ErrnoException)?.code === 'EACCES') {\n return c.json({ ok: false, error: 'Permission denied' }, 403);\n }\n return c.json({ ok: false, error: 'Failed to read project files' }, 500);\n }\n });\n\n authenticated.post('/api/projects/:id/sessions/:sessionKey', async (c) => {\n try {\n service.projects.attachSession(c.req.param('sessionKey'), c.req.param('id'));\n const session = await service.sessions.getSession(c.req.param('sessionKey'));\n return c.json({ ok: true, session });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.delete('/api/projects/:id/sessions/:sessionKey', async (c) => {\n try {\n const projectId = c.req.param('id');\n const sessionKey = c.req.param('sessionKey');\n if (!service.projects.get(projectId)) {\n return c.json({ ok: false, error: 'Project not found' }, 404);\n }\n const session = await service.sessions.getSession(sessionKey);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n if (session.projectId !== projectId) {\n return c.json({ ok: false, error: 'Session is not attached to this project' }, 409);\n }\n service.projects.detachSession(sessionKey);\n return c.json({ ok: true });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.post('/api/projects/:id/sessions/:sessionKey/summary-memory', async (c) => {\n const projectId = c.req.param('id');\n const sessionKey = c.req.param('sessionKey');\n const project = service.projects.get(projectId);\n if (!project) return c.json({ ok: false, error: 'Project not found' }, 404);\n const session = getSessionMetadata(sessionKey);\n if (!session) return c.json({ ok: false, error: 'Session not found' }, 404);\n if (session.projectId !== projectId) {\n return c.json({ ok: false, error: 'Session is not attached to this project' }, 409);\n }\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;\n const content = buildSessionSummaryMemoryContent(\n sessionKey,\n typeof body.summary === 'string' ? body.summary : undefined,\n );\n const record = upsertMemoryRecord({\n providerId: 'local',\n kind: 'session_summary',\n agentId: session.routing?.agentId ?? 'main',\n workspaceId: session.cwd,\n sessionKey,\n projectId,\n content,\n source: {\n provider: 'project-summary',\n sessionEntryId: sessionKey,\n },\n confidence: typeof body.confidence === 'number' ? body.confidence : 0.7,\n tags: ['project', 'session-summary', project.slug],\n status: 'active',\n sensitivity: 'normal',\n evidence: [{ sessionKey }],\n });\n return c.json({ ok: true, record }, 201);\n });\n\n authenticated.post('/api/projects/:id/goals/:goalId', async (c) => {\n try {\n service.projects.attachGoal(c.req.param('goalId'), c.req.param('id'));\n return c.json({ ok: true, goal: goals.get(c.req.param('goalId')) });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n\n authenticated.delete('/api/projects/:id/goals/:goalId', async (c) => {\n try {\n const projectId = c.req.param('id');\n const goalId = c.req.param('goalId');\n if (!service.projects.get(projectId)) {\n return c.json({ ok: false, error: 'Project not found' }, 404);\n }\n const goal = goals.get(goalId);\n if (!goal) {\n return c.json({ ok: false, error: 'Goal not found' }, 404);\n }\n if (goal.projectId !== projectId) {\n return c.json({ ok: false, error: 'Goal is not attached to this project' }, 409);\n }\n service.projects.detachGoal(goalId);\n return c.json({ ok: true });\n } catch (error) {\n return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);\n }\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AuthenticatedRouteDeps } from './deps.js';
3
+ export declare function registerSearchRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void;
@@ -0,0 +1,47 @@
1
+ //#region src/gateway/hono/routes/search.ts
2
+ function parseLimit(value) {
3
+ const parsed = value ? Number.parseInt(value, 10) : 20;
4
+ return Math.min(50, Math.max(1, Number.isFinite(parsed) ? parsed : 20));
5
+ }
6
+ function parseTypes(value) {
7
+ if (!value?.trim()) return new Set(["project"]);
8
+ return new Set(value.split(",").map((type) => type.trim()).filter((type) => type === "project"));
9
+ }
10
+ function projectSubtitle(project) {
11
+ return project.description ?? project.workspaceRoot ?? project.brief ?? project.slug;
12
+ }
13
+ function registerSearchRoutes(authenticated, deps) {
14
+ const { service } = deps;
15
+ authenticated.get("/api/search", (c) => {
16
+ const query = c.req.query("q")?.trim() ?? "";
17
+ const limit = parseLimit(c.req.query("limit"));
18
+ const types = parseTypes(c.req.query("types"));
19
+ const hits = [];
20
+ if (query && types.has("project")) {
21
+ const projects = service.projects.list({
22
+ search: query,
23
+ limit,
24
+ offset: 0
25
+ }).items;
26
+ hits.push(...projects.map((project, index) => ({
27
+ kind: "project",
28
+ id: `project:${project.id}`,
29
+ title: project.name,
30
+ subtitle: projectSubtitle(project),
31
+ href: `/projects/${encodeURIComponent(project.id)}`,
32
+ score: index,
33
+ payload: { project }
34
+ })));
35
+ }
36
+ return c.json({
37
+ ok: true,
38
+ query,
39
+ hits,
40
+ total: hits.length
41
+ });
42
+ });
43
+ }
44
+ //#endregion
45
+ export { registerSearchRoutes };
46
+
47
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","names":[],"sources":["../../../../../src/gateway/hono/routes/search.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { Project } from '../../../projects/types.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\ntype GlobalSearchHitKind = 'project';\n\ntype GlobalSearchHit = {\n kind: GlobalSearchHitKind;\n id: string;\n title: string;\n subtitle?: string;\n href: string;\n score: number;\n payload: {\n project: Project;\n };\n};\n\nfunction parseLimit(value: string | undefined): number {\n const parsed = value ? Number.parseInt(value, 10) : 20;\n return Math.min(50, Math.max(1, Number.isFinite(parsed) ? parsed : 20));\n}\n\nfunction parseTypes(value: string | undefined): Set<GlobalSearchHitKind> {\n if (!value?.trim()) return new Set<GlobalSearchHitKind>(['project']);\n return new Set(\n value\n .split(',')\n .map((type) => type.trim())\n .filter((type): type is GlobalSearchHitKind => type === 'project'),\n );\n}\n\nfunction projectSubtitle(project: Project): string | undefined {\n return project.description ?? project.workspaceRoot ?? project.brief ?? project.slug;\n}\n\nexport function registerSearchRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n authenticated.get('/api/search', (c) => {\n const query = c.req.query('q')?.trim() ?? '';\n const limit = parseLimit(c.req.query('limit'));\n const types = parseTypes(c.req.query('types'));\n const hits: GlobalSearchHit[] = [];\n\n if (query && types.has('project')) {\n const projects = service.projects.list({ search: query, limit, offset: 0 }).items;\n hits.push(\n ...projects.map((project, index) => ({\n kind: 'project' as const,\n id: `project:${project.id}`,\n title: project.name,\n subtitle: projectSubtitle(project),\n href: `/projects/${encodeURIComponent(project.id)}`,\n score: index,\n payload: { project },\n })),\n );\n }\n\n return c.json({ ok: true, query, hits, total: hits.length });\n });\n}\n"],"mappings":";AAmBA,SAAS,WAAW,OAAmC;CACrD,MAAM,SAAS,QAAQ,OAAO,SAAS,OAAO,GAAG,GAAG;AACpD,QAAO,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,OAAO,SAAS,OAAO,GAAG,SAAS,GAAG,CAAC;;AAGzE,SAAS,WAAW,OAAqD;AACvE,KAAI,CAAC,OAAO,MAAM,CAAE,QAAO,IAAI,IAAyB,CAAC,UAAU,CAAC;AACpE,QAAO,IAAI,IACT,MACG,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,QAAQ,SAAsC,SAAS,UAAU,CACrE;;AAGH,SAAS,gBAAgB,SAAsC;AAC7D,QAAO,QAAQ,eAAe,QAAQ,iBAAiB,QAAQ,SAAS,QAAQ;;AAGlF,SAAgB,qBAAqB,eAAqB,MAAoC;CAC5F,MAAM,EAAE,YAAY;AAEpB,eAAc,IAAI,gBAAgB,MAAM;EACtC,MAAM,QAAQ,EAAE,IAAI,MAAM,IAAI,EAAE,MAAM,IAAI;EAC1C,MAAM,QAAQ,WAAW,EAAE,IAAI,MAAM,QAAQ,CAAC;EAC9C,MAAM,QAAQ,WAAW,EAAE,IAAI,MAAM,QAAQ,CAAC;EAC9C,MAAM,OAA0B,EAAE;AAElC,MAAI,SAAS,MAAM,IAAI,UAAU,EAAE;GACjC,MAAM,WAAW,QAAQ,SAAS,KAAK;IAAE,QAAQ;IAAO;IAAO,QAAQ;IAAG,CAAC,CAAC;AAC5E,QAAK,KACH,GAAG,SAAS,KAAK,SAAS,WAAW;IACnC,MAAM;IACN,IAAI,WAAW,QAAQ;IACvB,OAAO,QAAQ;IACf,UAAU,gBAAgB,QAAQ;IAClC,MAAM,aAAa,mBAAmB,QAAQ,GAAG;IACjD,OAAO;IACP,SAAS,EAAE,SAAS;IACrB,EAAE,CACJ;;AAGH,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM;GAAO;GAAM,OAAO,KAAK;GAAQ,CAAC;GAC5D"}
@@ -9,7 +9,7 @@
9
9
  const DEFAULT_GATEWAY_HTTP_TOOL_DENY = [
10
10
  "exec",
11
11
  "spawn",
12
- "shell",
12
+ "exec_command",
13
13
  "fs_write",
14
14
  "fs_delete",
15
15
  "fs_move",
@@ -1 +1 @@
1
- {"version":3,"file":"dangerous-tools.js","names":[],"sources":["../../../../src/gateway/security/dangerous-tools.ts"],"sourcesContent":["/**\n * Tools denied via Gateway HTTP tool invoke endpoints by default.\n *\n * These are high-risk because they enable command execution, file mutation,\n * session orchestration, or control-plane actions that don't belong on a\n * non-interactive HTTP surface.\n */\nexport const DEFAULT_GATEWAY_HTTP_TOOL_DENY: readonly string[] = [\n // Direct command execution — immediate RCE surface\n 'exec',\n // Arbitrary child process creation\n 'spawn',\n // Shell command execution\n 'shell',\n // Arbitrary file mutation on the host\n 'fs_write',\n // Arbitrary file deletion on the host\n 'fs_delete',\n // Arbitrary file move/rename on the host\n 'fs_move',\n // Patch application can rewrite arbitrary files\n 'apply_patch',\n // Session orchestration — spawning agents remotely is RCE\n 'sessions_spawn',\n // Cross-session injection — message injection across sessions\n 'sessions_send',\n // Persistent automation control plane — can create/update/remove scheduled runs\n 'cron',\n // Gateway control plane — prevents gateway reconfiguration via HTTP\n 'gateway',\n // Node command relay can reach system.run on paired hosts\n 'nodes',\n] as const;\n\nconst DANGEROUS_TOOL_SET: ReadonlySet<string> = new Set(DEFAULT_GATEWAY_HTTP_TOOL_DENY);\n\n/**\n * Check whether a tool name is in the default HTTP deny list.\n */\nexport function isDangerousHttpTool(toolName: string): boolean {\n return DANGEROUS_TOOL_SET.has(toolName.toLowerCase());\n}\n\n/**\n * Filter a list of tool names, removing those on the HTTP deny list.\n * Returns only the safe tools.\n */\nexport function filterDangerousHttpTools(toolNames: string[]): {\n allowed: string[];\n denied: string[];\n} {\n const allowed: string[] = [];\n const denied: string[] = [];\n for (const name of toolNames) {\n if (isDangerousHttpTool(name)) {\n denied.push(name);\n } else {\n allowed.push(name);\n }\n }\n return { allowed, denied };\n}\n"],"mappings":";;;;;;;;AAOA,MAAa,iCAAoD;CAE/D;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CACD;AAED,MAAM,qBAA0C,IAAI,IAAI,+BAA+B;;;;AAKvF,SAAgB,oBAAoB,UAA2B;AAC7D,QAAO,mBAAmB,IAAI,SAAS,aAAa,CAAC;;;;;;AAOvD,SAAgB,yBAAyB,WAGvC;CACA,MAAM,UAAoB,EAAE;CAC5B,MAAM,SAAmB,EAAE;AAC3B,MAAK,MAAM,QAAQ,UACjB,KAAI,oBAAoB,KAAK,CAC3B,QAAO,KAAK,KAAK;KAEjB,SAAQ,KAAK,KAAK;AAGtB,QAAO;EAAE;EAAS;EAAQ"}
1
+ {"version":3,"file":"dangerous-tools.js","names":[],"sources":["../../../../src/gateway/security/dangerous-tools.ts"],"sourcesContent":["/**\n * Tools denied via Gateway HTTP tool invoke endpoints by default.\n *\n * These are high-risk because they enable command execution, file mutation,\n * session orchestration, or control-plane actions that don't belong on a\n * non-interactive HTTP surface.\n */\nexport const DEFAULT_GATEWAY_HTTP_TOOL_DENY: readonly string[] = [\n // Direct command execution — immediate RCE surface\n 'exec',\n // Arbitrary child process creation\n 'spawn',\n // Shell command execution\n 'exec_command',\n // Arbitrary file mutation on the host\n 'fs_write',\n // Arbitrary file deletion on the host\n 'fs_delete',\n // Arbitrary file move/rename on the host\n 'fs_move',\n // Patch application can rewrite arbitrary files\n 'apply_patch',\n // Session orchestration — spawning agents remotely is RCE\n 'sessions_spawn',\n // Cross-session injection — message injection across sessions\n 'sessions_send',\n // Persistent automation control plane — can create/update/remove scheduled runs\n 'cron',\n // Gateway control plane — prevents gateway reconfiguration via HTTP\n 'gateway',\n // Node command relay can reach system.run on paired hosts\n 'nodes',\n] as const;\n\nconst DANGEROUS_TOOL_SET: ReadonlySet<string> = new Set(DEFAULT_GATEWAY_HTTP_TOOL_DENY);\n\n/**\n * Check whether a tool name is in the default HTTP deny list.\n */\nexport function isDangerousHttpTool(toolName: string): boolean {\n return DANGEROUS_TOOL_SET.has(toolName.toLowerCase());\n}\n\n/**\n * Filter a list of tool names, removing those on the HTTP deny list.\n * Returns only the safe tools.\n */\nexport function filterDangerousHttpTools(toolNames: string[]): {\n allowed: string[];\n denied: string[];\n} {\n const allowed: string[] = [];\n const denied: string[] = [];\n for (const name of toolNames) {\n if (isDangerousHttpTool(name)) {\n denied.push(name);\n } else {\n allowed.push(name);\n }\n }\n return { allowed, denied };\n}\n"],"mappings":";;;;;;;;AAOA,MAAa,iCAAoD;CAE/D;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CACD;AAED,MAAM,qBAA0C,IAAI,IAAI,+BAA+B;;;;AAKvF,SAAgB,oBAAoB,UAA2B;AAC7D,QAAO,mBAAmB,IAAI,SAAS,aAAa,CAAC;;;;;;AAOvD,SAAgB,yBAAyB,WAGvC;CACA,MAAM,UAAoB,EAAE;CAC5B,MAAM,SAAmB,EAAE;AAC3B,MAAK,MAAM,QAAQ,UACjB,KAAI,oBAAoB,KAAK,CAC3B,QAAO,KAAK,KAAK;KAEjB,SAAQ,KAAK,KAAK;AAGtB,QAAO;EAAE;EAAS;EAAQ"}
@@ -2,8 +2,8 @@ import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { init_paths, resolveConfigPath } from "../config/paths.js";
4
4
  import { init_loader, loadConfig } from "../config/loader.js";
5
- import { isRestartEnabled } from "../config/commands.flags.js";
6
5
  import { resolveGatewayService } from "../daemon/service.js";
6
+ import { isRestartEnabled } from "../config/commands.flags.js";
7
7
  import { authorizeGatewaySigusr1Restart, writeGatewayRestartIntentSync } from "./restart.js";
8
8
  import { findVerifiedGatewayListenerPidsOnPortSync, signalVerifiedGatewayPidSync } from "./gateway-processes.js";
9
9
  //#region src/infra/update-restart.ts
@@ -2,6 +2,8 @@ export * from './types.js';
2
2
  export * from './project-store.js';
3
3
  export * from './project-service.js';
4
4
  export * from './project-agent.js';
5
+ export * from './project-agent-suggestion.js';
6
+ export * from './project-kind.js';
5
7
  export * from './project-loop.js';
6
8
  export * from './session-bind.js';
7
9
  export * from './goal-bind.js';
@@ -9,3 +11,4 @@ export * from './workflow-bind.js';
9
11
  export * from './automation-bind.js';
10
12
  export * from './memory-bind.js';
11
13
  export * from './workspace.js';
14
+ export * from './workspace-project.js';
@@ -2,10 +2,13 @@ import { ProjectStore, slugifyProjectName } from "./project-store.js";
2
2
  import { getProjectForSession, getProjectWorkspacePathForSession, resolveProjectWorkspacePath } from "./workspace.js";
3
3
  import { bindGoalToProject, bindGoalsToProject, listProjectGoalIds, unbindGoalFromProject } from "./goal-bind.js";
4
4
  import { bindSessionToProject, bindSessionsToProject, listProjectSessionKeys, moveSessionToProject, unbindSessionFromProject } from "./session-bind.js";
5
+ import { ProjectWorkspaceConflictError, ProjectWorkspaceMissingError, canonicalWorkspacePath, ensureWorkspaceDirectory, inferProjectNameFromWorkspaceRoot, isPathSameOrInsideWorkspace, isSafeAutoCreateWorkspaceRoot, resolveWorkspaceProjectRoot, workspaceDirectoryExists } from "./workspace-project.js";
5
6
  import { ProjectService } from "./project-service.js";
6
7
  import { isValidProjectAgentId, normalizeProjectAgentId, resolveProjectAgentId } from "./project-agent.js";
8
+ import { inferProjectKind } from "./project-kind.js";
9
+ import { CODING_PROJECT_AGENT_ID, inferSuggestedProjectDefaultAgentId, suggestProjectDefaultAgentId } from "./project-agent-suggestion.js";
7
10
  import { buildProjectLoopOverview } from "./project-loop.js";
8
11
  import { bindWorkflowRunToProject } from "./workflow-bind.js";
9
12
  import { bindAutomationToProject } from "./automation-bind.js";
10
13
  import { bindMemoryRecordToProject } from "./memory-bind.js";
11
- export { ProjectService, ProjectStore, bindAutomationToProject, bindGoalToProject, bindGoalsToProject, bindMemoryRecordToProject, bindSessionToProject, bindSessionsToProject, bindWorkflowRunToProject, buildProjectLoopOverview, getProjectForSession, getProjectWorkspacePathForSession, isValidProjectAgentId, listProjectGoalIds, listProjectSessionKeys, moveSessionToProject, normalizeProjectAgentId, resolveProjectAgentId, resolveProjectWorkspacePath, slugifyProjectName, unbindGoalFromProject, unbindSessionFromProject };
14
+ export { CODING_PROJECT_AGENT_ID, ProjectService, ProjectStore, ProjectWorkspaceConflictError, ProjectWorkspaceMissingError, bindAutomationToProject, bindGoalToProject, bindGoalsToProject, bindMemoryRecordToProject, bindSessionToProject, bindSessionsToProject, bindWorkflowRunToProject, buildProjectLoopOverview, canonicalWorkspacePath, ensureWorkspaceDirectory, getProjectForSession, getProjectWorkspacePathForSession, inferProjectKind, inferProjectNameFromWorkspaceRoot, inferSuggestedProjectDefaultAgentId, isPathSameOrInsideWorkspace, isSafeAutoCreateWorkspaceRoot, isValidProjectAgentId, listProjectGoalIds, listProjectSessionKeys, moveSessionToProject, normalizeProjectAgentId, resolveProjectAgentId, resolveProjectWorkspacePath, resolveWorkspaceProjectRoot, slugifyProjectName, suggestProjectDefaultAgentId, unbindGoalFromProject, unbindSessionFromProject, workspaceDirectoryExists };