@xopcai/xopc 0.0.113 → 0.0.114

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 (327) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CBlklyNr.js → agent-avatar-dicebear-Fp-p7bP1.js} +1 -1
  4. package/dist/gateway/static/root/assets/agent-browser-settings-page-CWh7trjd.js +6 -0
  5. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-kC8OuLoj.js +1 -0
  6. package/dist/gateway/static/root/assets/agents-DJWG26Si.js +71 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-28E_WfMe.js → app-management-settings-panel-v24i25Oj.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-Do1z0XgJ.js → appearance-settings-ycbdJRuZ.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-0V3DXZ7J.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-BAFlsov_.js → archive-plugin-CJzSADJo.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-V3qGPC3x.js → attachment-load-BTAB4zGS.js} +1 -1
  12. package/dist/gateway/static/root/assets/{binary-plugins-DT8NvA6W.js → binary-plugins-DXR1-mct.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-CdLMEcHd.js → block-editor-CzQri9h2.js} +44 -44
  14. package/dist/gateway/static/root/assets/channels-settings-CIKOkYdO.js +1 -0
  15. package/dist/gateway/static/root/assets/{config-api-Bf5mjpu7.js → config-api-DRqjEvAx.js} +1 -1
  16. package/dist/gateway/static/root/assets/connectors-settings-ClrDS5jH.js +2 -0
  17. package/dist/gateway/static/root/assets/{cron-api--pAB_rps.js → cron-api-BXaZXv1o.js} +1 -1
  18. package/dist/gateway/static/root/assets/cron-page-Chi_4u4P.js +3 -0
  19. package/dist/gateway/static/root/assets/dreaming-settings-B0_rs2tj.js +1 -0
  20. package/dist/gateway/static/root/assets/{extension-debug-page-BGhE7msg.js → extension-debug-page-DSkqMYo7.js} +1 -1
  21. package/dist/gateway/static/root/assets/{extension-page-CvpI45WG.js → extension-page-h2dGDbMN.js} +1 -1
  22. package/dist/gateway/static/root/assets/{extension-settings-page-DAmR5FUe.js → extension-settings-page-hN99RhOi.js} +1 -1
  23. package/dist/gateway/static/root/assets/{fetch-NZmjSXLl.js → fetch-D9QqUtzZ.js} +1 -1
  24. package/dist/gateway/static/root/assets/{format-interval-label-qhth3BMV.js → format-interval-label-D6E687rG.js} +1 -1
  25. package/dist/gateway/static/root/assets/{gateway-config-api-CaXBt-9R.js → gateway-config-api-CLt_SKTC.js} +1 -1
  26. package/dist/gateway/static/root/assets/{gateway-settings-DphpbLBc.js → gateway-settings-1PDi4lI0.js} +1 -1
  27. package/dist/gateway/static/root/assets/{gateway-startup-retry-Be1omjh3.js → gateway-startup-retry-N7Z_sz7p.js} +1 -1
  28. package/dist/gateway/static/root/assets/goal-detail-page-D_wwXL-t.js +1 -0
  29. package/dist/gateway/static/root/assets/goals-page-DZ2toZ8y.js +2 -0
  30. package/dist/gateway/static/root/assets/{goals-settings-ceEBjnfc.js → goals-settings-LFlwtIKE.js} +1 -1
  31. package/dist/gateway/static/root/assets/{heartbeat-config-api-BzO6G4fD.js → heartbeat-config-api-DGPadTw-.js} +1 -1
  32. package/dist/gateway/static/root/assets/{heartbeat-settings-DCST9-du.js → heartbeat-settings-BHHAs8ZX.js} +1 -1
  33. package/dist/gateway/static/root/assets/index-DefIdW9q.js +122 -0
  34. package/dist/gateway/static/root/assets/index-DzmWBkq-.css +1 -0
  35. package/dist/gateway/static/root/assets/{interval-presets-DJmshV8n.js → interval-presets-CEklPQ_r.js} +1 -1
  36. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-9AUUlQ5Q.js → keyboard-shortcuts-settings-Ce_aQ8Iv.js} +1 -1
  37. package/dist/gateway/static/root/assets/logs-page-j_D2QTuQ.js +2 -0
  38. package/dist/gateway/static/root/assets/{markdown-split-CYYELqAr.js → markdown-split-BMz3xPXO.js} +1 -1
  39. package/dist/gateway/static/root/assets/{media-plugins-BEcMR2M8.js → media-plugins-BalOSoz9.js} +1 -1
  40. package/dist/gateway/static/root/assets/messages-BN_WiUg0.js +3 -0
  41. package/dist/gateway/static/root/assets/models-hub-panel-CMH_TmZz.js +2 -0
  42. package/dist/gateway/static/root/assets/note-detail-page-O18BrQPq.js +2 -0
  43. package/dist/gateway/static/root/assets/{note-image-lightbox-DNAoPcmm.js → note-image-lightbox-C5D1Q5mY.js} +1 -1
  44. package/dist/gateway/static/root/assets/notes-page-B_ZYCmY9.js +1 -0
  45. package/dist/gateway/static/root/assets/{preview-open-alternatives-BPLz0AIv.js → preview-open-alternatives-CYFR5U9l.js} +1 -1
  46. package/dist/gateway/static/root/assets/remote-access-hub-CwKgWixY.js +1 -0
  47. package/dist/gateway/static/root/assets/{save-bar-controls-CrWwWRVK.js → save-bar-controls-BQ79Z5IP.js} +1 -1
  48. package/dist/gateway/static/root/assets/{schedule-field-CF2sadvN.js → schedule-field-Bgk6Emue.js} +1 -1
  49. package/dist/gateway/static/root/assets/{schema-form-Di5cKjYE.js → schema-form-DzS36TMa.js} +1 -1
  50. package/dist/gateway/static/root/assets/sessions-page-C4fcD-7B.js +1 -0
  51. package/dist/gateway/static/root/assets/settings-advanced-gate-Ad2OJ86w.js +1 -0
  52. package/dist/gateway/static/root/assets/{settings-collapsible-section-Chh282jW.js → settings-collapsible-section-CdjFnSAy.js} +1 -1
  53. package/dist/gateway/static/root/assets/{settings-form-section-Dq-83cQd.js → settings-form-section-DZLy_exu.js} +1 -1
  54. package/dist/gateway/static/root/assets/settings-page-D2BMY5wo.js +1 -0
  55. package/dist/gateway/static/root/assets/setup-status-panel-aWwxl_E1.js +1 -0
  56. package/dist/gateway/static/root/assets/{share-preview-page-0FXYnSUA.js → share-preview-page-_qRDr4g_.js} +1 -1
  57. package/dist/gateway/static/root/assets/shares-settings-DUE83uIf.js +1 -0
  58. package/dist/gateway/static/root/assets/skill-reload-api-mWKJcrog.js +1 -0
  59. package/dist/gateway/static/root/assets/skills-page-0Sx1Q58n.js +2 -0
  60. package/dist/gateway/static/root/assets/{system-settings-panel-W1pTcojB.js → system-settings-panel-hqaBF5aG.js} +1 -1
  61. package/dist/gateway/static/root/assets/{use-agent-defaults-form-BVRvkK7m.js → use-agent-defaults-form-zbVIQLOA.js} +1 -1
  62. package/dist/gateway/static/root/assets/{use-channel-catalog-CgJibfMM.js → use-channel-catalog-CbXq97rT.js} +1 -1
  63. package/dist/gateway/static/root/assets/{use-settings-tab-guard-Bw_qkRQE.js → use-settings-tab-guard-DwS3drWK.js} +1 -1
  64. package/dist/gateway/static/root/assets/voice-api-key-field-D6HR0hR2.js +1 -0
  65. package/dist/gateway/static/root/assets/{voice-player-store-C7lziaAx.js → voice-player-store-DSuOd9zy.js} +1 -1
  66. package/dist/gateway/static/root/assets/workflow-page.utils-C0Uq3xyq.js +1 -0
  67. package/dist/gateway/static/root/assets/workflows-page-DW0fmWhQ.js +18 -0
  68. package/dist/gateway/static/root/index.html +5 -5
  69. package/dist/package.js +1 -1
  70. package/dist/src/agent/agent-manager.d.ts +8 -0
  71. package/dist/src/agent/agent-manager.js +57 -22
  72. package/dist/src/agent/agent-manager.js.map +1 -1
  73. package/dist/src/agent/bootstrap/project-agents-context.d.ts +2 -0
  74. package/dist/src/agent/bootstrap/project-agents-context.js +32 -0
  75. package/dist/src/agent/bootstrap/project-agents-context.js.map +1 -0
  76. package/dist/src/agent/goals/persistent-goal-service.js +2 -4
  77. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  78. package/dist/src/agent/inbound/inbound-loop.js +5 -1
  79. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  80. package/dist/src/agent/inbound/turn-dispatcher.d.ts +2 -2
  81. package/dist/src/agent/inbound/turn-dispatcher.js +2 -2
  82. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  83. package/dist/src/agent/mcp/bundle-mcp-materialize.d.ts +27 -0
  84. package/dist/src/agent/mcp/bundle-mcp-materialize.js +49 -1
  85. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  86. package/dist/src/agent/mcp/bundle-mcp-runtime.js +78 -3
  87. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  88. package/dist/src/agent/mcp/bundle-mcp-types.d.ts +19 -0
  89. package/dist/src/agent/service/btw-query.js +11 -3
  90. package/dist/src/agent/service/btw-query.js.map +1 -1
  91. package/dist/src/agent/service/parse-outbound-session-key.js +13 -8
  92. package/dist/src/agent/service/parse-outbound-session-key.js.map +1 -1
  93. package/dist/src/agent/service/process-direct-one-shot.d.ts +2 -2
  94. package/dist/src/agent/service/process-direct-one-shot.js +1 -1
  95. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  96. package/dist/src/agent/service/process-direct-streaming.d.ts +2 -2
  97. package/dist/src/agent/service/process-direct-streaming.js +1 -1
  98. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  99. package/dist/src/agent/service.d.ts +1 -1
  100. package/dist/src/agent/service.js +9 -5
  101. package/dist/src/agent/service.js.map +1 -1
  102. package/dist/src/agent/service.types.d.ts +5 -0
  103. package/dist/src/agent/skills/filesystem-watcher.js +4 -3
  104. package/dist/src/agent/skills/filesystem-watcher.js.map +1 -1
  105. package/dist/src/agent/skills/index.js +2 -1
  106. package/dist/src/agent/skills/index.js.map +1 -1
  107. package/dist/src/agent/skills/skill-manage-ops.js +2 -1
  108. package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
  109. package/dist/src/agent/skills/types.d.ts +1 -1
  110. package/dist/src/agent/skills/watcher.js +2 -5
  111. package/dist/src/agent/skills/watcher.js.map +1 -1
  112. package/dist/src/agent/skills/workspace-skills-dir.d.ts +1 -0
  113. package/dist/src/agent/skills/workspace-skills-dir.js +11 -0
  114. package/dist/src/agent/skills/workspace-skills-dir.js.map +1 -0
  115. package/dist/src/agent/tool-manuals/browser.d.ts +1 -1
  116. package/dist/src/agent/tool-manuals/browser.js +62 -3
  117. package/dist/src/agent/tool-manuals/browser.js.map +1 -1
  118. package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +4 -1
  119. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +13 -4
  120. package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
  121. package/dist/src/agent/tools/browser/tool/schemas.d.ts +0 -2
  122. package/dist/src/agent/tools/browser/tool/schemas.js +1 -2
  123. package/dist/src/agent/tools/browser/tool/schemas.js.map +1 -1
  124. package/dist/src/agent/tools/factory.js +1 -10
  125. package/dist/src/agent/tools/factory.js.map +1 -1
  126. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  127. package/dist/src/agent/tools/skill-manage-tool.js.map +1 -1
  128. package/dist/src/agent/workflow/subagent-runner.js +18 -1
  129. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  130. package/dist/src/browser/actions/extension-registry-bridge.js +91 -7
  131. package/dist/src/browser/actions/extension-registry-bridge.js.map +1 -1
  132. package/dist/src/browser/actions/registry.js +383 -7
  133. package/dist/src/browser/actions/registry.js.map +1 -1
  134. package/dist/src/browser/actions/types.d.ts +11 -3
  135. package/dist/src/browser/index.js +2 -2
  136. package/dist/src/browser/pipeline/output.d.ts +18 -16
  137. package/dist/src/browser/pipeline/output.js +28 -20
  138. package/dist/src/browser/pipeline/output.js.map +1 -1
  139. package/dist/src/browser/pipeline/runner.d.ts +7 -14
  140. package/dist/src/browser/pipeline/runner.js +349 -102
  141. package/dist/src/browser/pipeline/runner.js.map +1 -1
  142. package/dist/src/browser/pipeline/schema.d.ts +1 -0
  143. package/dist/src/browser/pipeline/schema.js +35 -58
  144. package/dist/src/browser/pipeline/schema.js.map +1 -1
  145. package/dist/src/browser/pipeline/source.d.ts +2 -0
  146. package/dist/src/browser/pipeline/source.js +10 -1
  147. package/dist/src/browser/pipeline/source.js.map +1 -1
  148. package/dist/src/browser/pipeline/template.d.ts +10 -2
  149. package/dist/src/browser/pipeline/template.js +50 -8
  150. package/dist/src/browser/pipeline/template.js.map +1 -1
  151. package/dist/src/chat-commands/builtins/config.js +3 -5
  152. package/dist/src/chat-commands/builtins/config.js.map +1 -1
  153. package/dist/src/chat-commands/context.d.ts +5 -0
  154. package/dist/src/chat-commands/context.js +18 -9
  155. package/dist/src/chat-commands/context.js.map +1 -1
  156. package/dist/src/chat-commands/index.d.ts +1 -1
  157. package/dist/src/chat-commands/index.js +2 -2
  158. package/dist/src/chat-commands/index.js.map +1 -1
  159. package/dist/src/chat-commands/processor.js +22 -21
  160. package/dist/src/chat-commands/processor.js.map +1 -1
  161. package/dist/src/chat-commands/session-key.d.ts +3 -15
  162. package/dist/src/chat-commands/session-key.js +3 -49
  163. package/dist/src/chat-commands/session-key.js.map +1 -1
  164. package/dist/src/chat-commands/types.d.ts +4 -0
  165. package/dist/src/cli/commands/browser-cli-helpers.js +11 -5
  166. package/dist/src/cli/commands/browser-cli-helpers.js.map +1 -1
  167. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  168. package/dist/src/cli/commands/doctor/checks/session-integrity.js +8 -8
  169. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
  170. package/dist/src/config/rules.js +5 -0
  171. package/dist/src/config/rules.js.map +1 -1
  172. package/dist/src/connectors/builtin-catalog.js +180 -1
  173. package/dist/src/connectors/builtin-catalog.js.map +1 -1
  174. package/dist/src/connectors/health.js +18 -9
  175. package/dist/src/connectors/health.js.map +1 -1
  176. package/dist/src/connectors/types.d.ts +19 -0
  177. package/dist/src/connectors/usage.js +12 -4
  178. package/dist/src/connectors/usage.js.map +1 -1
  179. package/dist/src/extensions/types/tui.d.ts +2 -0
  180. package/dist/src/gateway/heartbeat/service.js +13 -1
  181. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  182. package/dist/src/gateway/hono/app.js +13 -4
  183. package/dist/src/gateway/hono/app.js.map +1 -1
  184. package/dist/src/gateway/hono/oauth-async.js +1 -1
  185. package/dist/src/gateway/hono/routes/agent-stream.js +6 -6
  186. package/dist/src/gateway/hono/routes/agent-stream.js.map +1 -1
  187. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +1 -1
  188. package/dist/src/gateway/hono/routes/config-patch/gateway.js +6 -0
  189. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  190. package/dist/src/gateway/hono/routes/goals.js +16 -3
  191. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  192. package/dist/src/gateway/hono/routes/logs.js +1 -0
  193. package/dist/src/gateway/hono/routes/logs.js.map +1 -1
  194. package/dist/src/gateway/hono/routes/mcp.js +27 -8
  195. package/dist/src/gateway/hono/routes/mcp.js.map +1 -1
  196. package/dist/src/gateway/hono/routes/notes.js +14 -2
  197. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  198. package/dist/src/gateway/hono/routes/sessions.js +62 -4
  199. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  200. package/dist/src/gateway/hono/routes/workspace.js +3 -66
  201. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  202. package/dist/src/gateway/hono/sse.d.ts +2 -2
  203. package/dist/src/gateway/hono/sse.js +7 -15
  204. package/dist/src/gateway/hono/sse.js.map +1 -1
  205. package/dist/src/gateway/resolve-webchat-session-key.d.ts +2 -6
  206. package/dist/src/gateway/resolve-webchat-session-key.js +5 -31
  207. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -1
  208. package/dist/src/gateway/service/agent-runner.d.ts +2 -3
  209. package/dist/src/gateway/service/agent-runner.js +9 -22
  210. package/dist/src/gateway/service/agent-runner.js.map +1 -1
  211. package/dist/src/gateway/service/run-gateway-agent.js +9 -7
  212. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  213. package/dist/src/gateway/service/sessions-api.d.ts +9 -0
  214. package/dist/src/gateway/service/sessions-api.js +11 -0
  215. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  216. package/dist/src/gateway/service.d.ts +1 -1
  217. package/dist/src/gateway/service.js +19 -5
  218. package/dist/src/gateway/service.js.map +1 -1
  219. package/dist/src/gateway/workspace-file-search.d.ts +9 -0
  220. package/dist/src/gateway/workspace-file-search.js +76 -0
  221. package/dist/src/gateway/workspace-file-search.js.map +1 -0
  222. package/dist/src/goals/goal-notifications.d.ts +2 -0
  223. package/dist/src/goals/goal-notifications.js +16 -11
  224. package/dist/src/goals/goal-notifications.js.map +1 -1
  225. package/dist/src/goals/goal-service.js +1 -6
  226. package/dist/src/goals/goal-service.js.map +1 -1
  227. package/dist/src/project-trust/trust-store.js +2 -0
  228. package/dist/src/project-trust/trust-store.js.map +1 -1
  229. package/dist/src/session/index.d.ts +1 -1
  230. package/dist/src/session/index.js +2 -2
  231. package/dist/src/session/init-session-turn.js +6 -9
  232. package/dist/src/session/init-session-turn.js.map +1 -1
  233. package/dist/src/session/manager.d.ts +5 -1
  234. package/dist/src/session/manager.js +5 -2
  235. package/dist/src/session/manager.js.map +1 -1
  236. package/dist/src/session/reset-type.d.ts +0 -2
  237. package/dist/src/session/reset-type.js +3 -9
  238. package/dist/src/session/reset-type.js.map +1 -1
  239. package/dist/src/session/resolve-session.js +9 -20
  240. package/dist/src/session/resolve-session.js.map +1 -1
  241. package/dist/src/session/store.d.ts +11 -3
  242. package/dist/src/session/store.js +29 -13
  243. package/dist/src/session/store.js.map +1 -1
  244. package/dist/src/session/types.d.ts +2 -4
  245. package/dist/src/session/types.js.map +1 -1
  246. package/dist/src/storage/sqlite/index.d.ts +2 -2
  247. package/dist/src/storage/sqlite/index.js +3 -3
  248. package/dist/src/storage/sqlite/migrations/010_session_id_identity.sql +30 -0
  249. package/dist/src/storage/sqlite/migrations/runner.d.ts +2 -2
  250. package/dist/src/storage/sqlite/migrations/runner.js +4 -4
  251. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  252. package/dist/src/storage/sqlite/migrations/runner.ts +2 -2
  253. package/dist/src/storage/sqlite/row-mappers.d.ts +4 -4
  254. package/dist/src/storage/sqlite/row-mappers.js +11 -14
  255. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  256. package/dist/src/storage/sqlite/schema.js +3 -3
  257. package/dist/src/storage/sqlite/schema.js.map +1 -1
  258. package/dist/src/storage/sqlite/schema.sql +37 -10
  259. package/dist/src/storage/sqlite/session-metadata.d.ts +2 -7
  260. package/dist/src/storage/sqlite/session-metadata.js +18 -72
  261. package/dist/src/storage/sqlite/session-metadata.js.map +1 -1
  262. package/dist/src/storage/sqlite/session-repository.d.ts +6 -5
  263. package/dist/src/storage/sqlite/session-repository.js +34 -40
  264. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  265. package/dist/src/storage/sqlite/transcript-repository.d.ts +2 -2
  266. package/dist/src/storage/sqlite/transcript-repository.js +52 -52
  267. package/dist/src/storage/sqlite/transcript-repository.js.map +1 -1
  268. package/dist/src/tui/backends/embedded-backend.d.ts +11 -4
  269. package/dist/src/tui/backends/embedded-backend.js +27 -1
  270. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  271. package/dist/src/tui/backends/gateway-sse-backend.d.ts +4 -1
  272. package/dist/src/tui/backends/gateway-sse-backend.js +21 -2
  273. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  274. package/dist/src/tui/extension-host/autocomplete.js +13 -10
  275. package/dist/src/tui/extension-host/autocomplete.js.map +1 -1
  276. package/dist/src/tui/extension-host/runtime.d.ts +7 -0
  277. package/dist/src/tui/extension-host/runtime.js +6 -4
  278. package/dist/src/tui/extension-host/runtime.js.map +1 -1
  279. package/dist/src/tui/tui-backend.d.ts +9 -0
  280. package/dist/src/tui/tui-commands.js +85 -6
  281. package/dist/src/tui/tui-commands.js.map +1 -1
  282. package/dist/src/tui/tui-file-autocomplete.d.ts +5 -0
  283. package/dist/src/tui/tui-file-autocomplete.js +26 -0
  284. package/dist/src/tui/tui-file-autocomplete.js.map +1 -0
  285. package/dist/src/tui/tui-picker-overlay.js +1 -1
  286. package/dist/src/tui/tui.js +29 -11
  287. package/dist/src/tui/tui.js.map +1 -1
  288. package/dist/src/utils/logger/context.d.ts +2 -2
  289. package/dist/src/utils/logger/context.js +2 -1
  290. package/dist/src/utils/logger/context.js.map +1 -1
  291. package/dist/src/utils/logger/log-store.js +1 -0
  292. package/dist/src/utils/logger/log-store.js.map +1 -1
  293. package/dist/src/utils/logger/pino-record.js +3 -0
  294. package/dist/src/utils/logger/pino-record.js.map +1 -1
  295. package/dist/src/utils/logger/types.d.ts +5 -0
  296. package/dist/src/utils/logger/types.js.map +1 -1
  297. package/dist/src/workflows/service/workflow-session-bridge.js +13 -1
  298. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  299. package/package.json +1 -1
  300. package/dist/gateway/static/root/assets/agent-browser-settings-page-rGbKc2Hh.js +0 -6
  301. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-CtwVbjOA.js +0 -1
  302. package/dist/gateway/static/root/assets/agents-CCBmGcwc.js +0 -71
  303. package/dist/gateway/static/root/assets/apps-page-xwCsJS15.js +0 -1
  304. package/dist/gateway/static/root/assets/channels-settings-C044DcYk.js +0 -1
  305. package/dist/gateway/static/root/assets/connectors-settings-8VEWUOTj.js +0 -2
  306. package/dist/gateway/static/root/assets/cron-page-DqmNGNOy.js +0 -3
  307. package/dist/gateway/static/root/assets/dreaming-settings-6UZE1FSK.js +0 -1
  308. package/dist/gateway/static/root/assets/goal-detail-page-bv0VRDaf.js +0 -1
  309. package/dist/gateway/static/root/assets/goals-page-1Ywnt4Cb.js +0 -2
  310. package/dist/gateway/static/root/assets/index-Cxm6xsps.js +0 -122
  311. package/dist/gateway/static/root/assets/index-b_R4tKqt.css +0 -1
  312. package/dist/gateway/static/root/assets/logs-page-B_wrxhee.js +0 -2
  313. package/dist/gateway/static/root/assets/messages-CkT-lcgP.js +0 -3
  314. package/dist/gateway/static/root/assets/models-hub-panel-D0JDSbNu.js +0 -2
  315. package/dist/gateway/static/root/assets/note-detail-page-DpFV-EO6.js +0 -2
  316. package/dist/gateway/static/root/assets/notes-page-CdkjTch8.js +0 -1
  317. package/dist/gateway/static/root/assets/remote-access-hub-DSYVAi96.js +0 -1
  318. package/dist/gateway/static/root/assets/sessions-page-CenkcVjF.js +0 -1
  319. package/dist/gateway/static/root/assets/settings-advanced-gate-D9sva86V.js +0 -1
  320. package/dist/gateway/static/root/assets/settings-page-DL35U0x0.js +0 -1
  321. package/dist/gateway/static/root/assets/setup-status-panel-Ctwbp9Er.js +0 -1
  322. package/dist/gateway/static/root/assets/shares-settings-DIM3JNsr.js +0 -1
  323. package/dist/gateway/static/root/assets/skill-reload-api-DAlNaNzL.js +0 -1
  324. package/dist/gateway/static/root/assets/skills-page-CLrHVzAt.js +0 -2
  325. package/dist/gateway/static/root/assets/voice-api-key-field-vYAO71Gn.js +0 -1
  326. package/dist/gateway/static/root/assets/workflow-page.utils-sgHjOXOm.js +0 -1
  327. package/dist/gateway/static/root/assets/workflows-page-DYyOTmFx.js +0 -18
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.js","names":[],"sources":["../../../../../src/gateway/hono/routes/sessions.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { buildSessionKey, parseSessionKey } from '../../../routing/session-key.js';\nimport { agentExists, getDefaultAgentId } from '../../../routing/resolve-route.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport { createGatewayRouteLogger, logRouteError } from '../lib/route-logger.js';\nimport { messagesToClientHistory } from '../../../session/client-history.js';\nimport type { SessionType } from '../../../session/types.js';\nimport { computeUserRoundDeleteRange } from '../../../session/user-round-delete.js';\nimport { deleteMediaUrisNoLongerReferenced } from '../../../media/session-references.js';\nimport { respondStartupUnavailable } from '../lib/startup-unavailable.js';\nimport type { StartupUnavailableGatewayMethod } from '../../startup-readiness.js';\nimport { evictEmbeddedSessionRunner } from '../../../agent/embedded/session-runner.js';\n\nconst log = createGatewayRouteLogger('Sessions');\n\ntype SessionsStartupMethod = StartupUnavailableGatewayMethod;\n\nconst SESSION_TYPES = new Set<SessionType>(['chat', 'workflow-run', 'workflow-subagent', 'cron', 'heartbeat']);\n\nfunction isSessionType(value: string): value is SessionType {\n return SESSION_TYPES.has(value as SessionType);\n}\n\nfunction ensureGatewayReadyForSessions(\n c: Parameters<typeof respondStartupUnavailable>[0],\n service: AuthenticatedRouteDeps['service'],\n method: SessionsStartupMethod,\n): Response | null {\n if (service.isGatewayReady()) {\n return null;\n }\n return respondStartupUnavailable(c, method);\n}\n\nexport function registerSessionsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // ========== Session REST API (/api/sessions) ==========\n\n // POST /api/sessions - Create new session (reuses empty sessions)\n authenticated.post('/api/sessions', async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const channel = body.channel || 'webchat';\n const routingCfg = service.currentConfig;\n let agentId =\n typeof body.agentId === 'string' && body.agentId.trim()\n ? body.agentId.trim().toLowerCase()\n : getDefaultAgentId(routingCfg);\n if (!agentExists(agentId, routingCfg)) {\n agentId = getDefaultAgentId(routingCfg);\n }\n\n // If a specific chat_id is provided, use it (for advanced use cases)\n // Otherwise, try to find and reuse an existing empty session\n if (body.chat_id) {\n const sessionKey = buildSessionKey({\n agentId,\n source: channel,\n accountId: 'default',\n peerKind: 'direct',\n peerId: body.chat_id,\n });\n\n await service.sessionIndexInstance.saveMessages(sessionKey, []);\n const session = await service.sessions.getSession(sessionKey);\n return c.json({ session }, 201);\n }\n\n // Look for existing empty sessions to reuse\n const existingSessions = await service.sessions.listSessions({\n channel,\n limit: 50,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n \n // Reuse an empty session only when it matches the requested agent (session key embeds agent id).\n const emptySession = existingSessions.items.find((s) => {\n if (s.messageCount !== 0) return false;\n const parsed = parseSessionKey(s.key);\n return parsed?.agentId === agentId;\n });\n \n if (emptySession) {\n // Return existing empty session instead of creating a new one\n const session = await service.sessions.getSession(emptySession.key);\n return c.json({ session, reused: true }, 200);\n }\n \n // No empty session found, create a new one\n const chatId = `chat_${Date.now()}`;\n const sessionKey = buildSessionKey({\n agentId,\n source: channel,\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n\n await service.sessionIndexInstance.saveMessages(sessionKey, []);\n\n const session = await service.sessions.getSession(sessionKey);\n return c.json({ session }, 201);\n });\n\n // GET /api/sessions - List sessions\n authenticated.get('/api/sessions', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.list');\n if (blocked) {\n return blocked;\n }\n const query = c.req.query();\n const sessionTypes = query.types\n ?.split(',')\n .map((value) => value.trim())\n .filter(isSessionType);\n const result = await service.sessions.listSessions({\n status: query.status as any,\n search: query.search,\n channel: query.channel,\n sessionTypes: sessionTypes?.length ? sessionTypes : undefined,\n includeHidden: query.includeHidden === 'true',\n limit: query.limit ? parseInt(query.limit) : undefined,\n offset: query.offset ? parseInt(query.offset) : undefined,\n });\n return c.json(result);\n });\n\n // GET /api/sessions/stats - Get session stats (must be before /:key)\n authenticated.get('/api/sessions/stats', async (c) => {\n const result = await service.sessions.stats();\n return c.json(result);\n });\n\n // GET /api/sessions/chat-ids - Get unique chat IDs from sessions (must be before /:key)\n authenticated.get('/api/sessions/chat-ids', async (c) => {\n const channel = c.req.query('channel');\n const chatIds = await service.sessions.chatIds(channel || undefined);\n return c.json({ ok: true, payload: { chatIds } });\n });\n\n // GET /api/sessions/:key/run — read-only active webchat agent run (for UI resume)\n authenticated.get('/api/sessions/:key/run', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.run');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, payload: service.sessions.getActiveRun(key) });\n });\n\n // GET /api/sessions/:key/agent-config — resolved session agent settings (thinking, etc.)\n authenticated.get('/api/sessions/:key/agent-config', async (c) => {\n const key = c.req.param('key');\n const payload = await service.sessions.getAgentConfig(key);\n return c.json({ ok: true, payload });\n });\n\n authenticated.patch('/api/sessions/:key/agent-config', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const result = await service.sessions.patchAgentConfig(key, body);\n if (!result.ok) {\n return c.json({ ok: false, error: result.error }, 400);\n }\n return c.json({ ok: true });\n });\n\n // GET /api/sessions/:key/messages — flattened transcript for TUI / clients\n authenticated.get('/api/sessions/:key/messages', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.messages');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const limitRaw = c.req.query('limit');\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : undefined;\n const limit =\n parsedLimit !== undefined && Number.isFinite(parsedLimit)\n ? Math.min(500, Math.max(1, parsedLimit))\n : undefined;\n\n const before = c.req.query('before')?.trim();\n const offsetRaw = c.req.query('offset');\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n\n const result = await service.sessions.getMessagePage(key, {\n limit,\n offset,\n ...(before ? { before } : {}),\n });\n if (!result) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n\n const messages = messagesToClientHistory(result.session.messages, { limit });\n return c.json({\n ok: true,\n payload: { messages },\n pagination: result.pagination,\n });\n });\n\n // GET /api/sessions/:key/history — UI chat history page from the newest tail.\n authenticated.get('/api/sessions/:key/history', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.history');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const offsetRaw = c.req.query('offset');\n const limitRaw = c.req.query('limit');\n const before = c.req.query('before')?.trim();\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : 50;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n const limit = Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50;\n const result = await service.sessions.getMessagePage(key, {\n offset,\n limit,\n ...(before ? { before } : {}),\n });\n\n if (!result) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n return c.json(result);\n });\n\n // POST /api/sessions/:key/transcript/context — append persisted-only `kind: 'context'` row (not in LLM context)\n authenticated.post('/api/sessions/:key/transcript/context', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const id = typeof body.id === 'string' && body.id.trim() ? body.id.trim() : undefined;\n const text = typeof body.text === 'string' ? body.text : undefined;\n const data =\n body.data !== undefined && typeof body.data === 'object' && body.data !== null && !Array.isArray(body.data)\n ? (body.data as Record<string, unknown>)\n : undefined;\n await service.sessionIndexInstance.appendTranscriptContextEntry(key, { id, text, data });\n return c.json({ ok: true });\n });\n\n // POST /api/sessions/:key/transcript/label — append label change for a transcript entry.\n authenticated.post('/api/sessions/:key/transcript/label', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const targetId = typeof body.targetId === 'string' ? body.targetId.trim() : '';\n if (!targetId) {\n return c.json({ ok: false, error: 'targetId is required' }, 400);\n }\n const label = typeof body.label === 'string' && body.label.trim() ? body.label.trim() : undefined;\n await service.sessionIndexInstance.appendTranscriptLabelEntry(key, { targetId, label });\n return c.json({ ok: true });\n });\n\n // POST /api/sessions/:key/transcript/custom — append extension state for TUI replay.\n authenticated.post('/api/sessions/:key/transcript/custom', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const customType = typeof body.customType === 'string' ? body.customType.trim() : '';\n if (!customType) {\n return c.json({ ok: false, error: 'customType is required' }, 400);\n }\n await service.sessionIndexInstance.appendTranscriptCustomEntry(key, {\n customType,\n data: body.data,\n });\n return c.json({ ok: true });\n });\n\n // POST /api/sessions/:key/transcript/custom-message — append visible extension custom message.\n authenticated.post('/api/sessions/:key/transcript/custom-message', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const customType = typeof body.customType === 'string' ? body.customType.trim() : '';\n if (!customType) {\n return c.json({ ok: false, error: 'customType is required' }, 400);\n }\n const content =\n typeof body.content === 'string' || Array.isArray(body.content) ? body.content : '';\n await service.sessionIndexInstance.appendTranscriptCustomMessageEntry(key, {\n customType,\n content,\n display: typeof body.display === 'boolean' ? body.display : undefined,\n details: body.details,\n });\n evictEmbeddedSessionRunner(key, 'gateway_custom_message_appended');\n return c.json({ ok: true });\n });\n\n // POST /api/sessions/:key/transcript/bash — append local TUI shell execution.\n authenticated.post('/api/sessions/:key/transcript/bash', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const command = typeof body.command === 'string' ? body.command.trim() : '';\n if (!command) {\n return c.json({ ok: false, error: 'command is required' }, 400);\n }\n await service.sessionIndexInstance.appendTranscriptBashExecutionEntry(key, {\n command,\n output: typeof body.output === 'string' ? body.output : '',\n exitCode: typeof body.exitCode === 'number' ? body.exitCode : body.exitCode === null ? null : undefined,\n signal: typeof body.signal === 'string' ? body.signal : body.signal === null ? null : undefined,\n excludeFromContext: body.excludeFromContext === true,\n truncated: body.truncated === true,\n fullOutputPath: typeof body.fullOutputPath === 'string' ? body.fullOutputPath : undefined,\n });\n evictEmbeddedSessionRunner(key, 'gateway_bash_execution_appended');\n return c.json({ ok: true });\n });\n\n // GET /api/sessions/:key/compaction/checkpoints — list pre-compaction snapshots (OpenClaw-style)\n authenticated.get('/api/sessions/:key/compaction/checkpoints', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const checkpoints = await service.sessions.listCompactionCheckpoints(key);\n return c.json({ ok: true, payload: { checkpoints } });\n });\n\n authenticated.get('/api/sessions/:key/compaction/checkpoints/:checkpointId', async (c) => {\n const key = c.req.param('key');\n const checkpointId = c.req.param('checkpointId');\n const checkpoint = await service.sessions.getCompactionCheckpoint(key, checkpointId);\n if (!checkpoint) {\n return c.json({ ok: false, error: 'Checkpoint not found' }, 404);\n }\n return c.json({ ok: true, payload: { checkpoint } });\n });\n\n authenticated.post('/api/sessions/:key/compaction/restore', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const checkpointId = typeof body.checkpointId === 'string' ? body.checkpointId.trim() : '';\n if (!checkpointId) {\n return c.json({ ok: false, error: 'checkpointId required' }, 400);\n }\n try {\n await service.sessions.restoreCompactionCheckpoint(key, checkpointId);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.sessions', { operation: 'restoreCheckpoint', sessionKey: key });\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('not found') || msg.includes('Invalid')) {\n return c.json({ ok: false, error: msg }, 404);\n }\n return c.json({ ok: false, error: msg }, 500);\n }\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, session });\n });\n\n authenticated.post('/api/sessions/:key/compaction/run', async (c) => {\n const key = c.req.param('key');\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const instructions = typeof body.instructions === 'string' ? body.instructions : undefined;\n const force = typeof body.force === 'boolean' ? body.force : true;\n const result = await service.sessions.runCompaction(key, { instructions, force });\n return c.json({ ok: true, payload: { result } });\n });\n\n // GET /api/sessions/:key - Get single session (must be after /stats and /chat-ids)\n authenticated.get('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const includeRaw = c.req.query('include') ?? '';\n const includeSet = new Set(\n includeRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n );\n const includeTranscript = includeSet.has('transcript');\n const includeTranscriptRows = includeSet.has('transcriptRows');\n const offsetRaw = c.req.query('offset');\n const limitRaw = c.req.query('limit');\n const hasPagingQuery = offsetRaw !== undefined || limitRaw !== undefined;\n\n if (hasPagingQuery) {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.history');\n if (blocked) {\n return blocked;\n }\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : 50;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n const limit = Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50;\n const result = await service.sessions.getMessagePage(key, {\n offset,\n limit,\n includeTranscriptSummary: includeTranscript,\n includeTranscriptRows,\n });\n if (!result) {\n return c.json({ error: 'Session not found' }, 404);\n }\n return c.json(result);\n }\n\n const session = await service.sessions.getSession(key, {\n includeTranscriptSummary: includeTranscript,\n includeTranscriptRows,\n });\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n return c.json({ session });\n });\n\n // PATCH /api/sessions/:key - Partial metadata (name, tags, customData); OpenClaw-style patch subset\n authenticated.patch('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const patch: {\n name?: string;\n tags?: string[];\n replaceTags?: boolean;\n customData?: Record<string, unknown>;\n } = {};\n if (typeof body.name === 'string') {\n patch.name = body.name;\n }\n if (Array.isArray(body.tags)) {\n patch.tags = body.tags;\n }\n if (typeof body.replaceTags === 'boolean') {\n patch.replaceTags = body.replaceTags;\n }\n if (body.customData !== undefined && typeof body.customData === 'object' && body.customData !== null) {\n patch.customData = body.customData as Record<string, unknown>;\n }\n const result = await service.sessions.patch(key, patch);\n if (result.ok === false) {\n return c.json({ ok: false, error: result.error }, 404);\n }\n\n const session = await service.sessions.getSession(key);\n return c.json({ ok: true, session });\n });\n\n // GET /api/sessions/:key/export - Export session (must be before /:key)\n authenticated.get('/api/sessions/:key/export', async (c) => {\n const key = c.req.param('key');\n const format = c.req.query('format') as any || 'json';\n const result = await service.sessions.export(key, format);\n return c.json(result);\n });\n\n authenticated.post('/api/sessions/import', async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const targetKey = typeof body.targetKey === 'string' ? body.targetKey : '';\n const content = typeof body.content === 'string' ? body.content : '';\n if (!targetKey.trim()) {\n return c.json({ ok: false, error: 'targetKey is required' }, 400);\n }\n if (!content.trim()) {\n return c.json({ ok: false, error: 'content is required' }, 400);\n }\n try {\n const result = await service.sessions.importExport(targetKey, content);\n return c.json({ ok: true, ...result });\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: errorMessage }, 400);\n }\n });\n\n authenticated.post('/api/sessions/:key/btw', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const question = typeof body.question === 'string' ? body.question : '';\n const result = await service.sessions.btwQuery(key, question);\n return c.json(result);\n });\n\n authenticated.post('/api/sessions/:key/fork', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const targetKey = typeof body.targetKey === 'string' ? body.targetKey : '';\n if (!targetKey.trim()) {\n return c.json({ ok: false, error: 'targetKey is required' }, 400);\n }\n try {\n const result = await service.sessions.fork(key, targetKey);\n return c.json({ ok: true, ...result });\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: errorMessage }, 400);\n }\n });\n\n authenticated.post('/api/sessions/:key/fork-row', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const targetKey = typeof body.targetKey === 'string' ? body.targetKey : '';\n const throughRow = typeof body.throughRow === 'number' ? Math.trunc(body.throughRow) : NaN;\n if (!targetKey.trim()) {\n return c.json({ ok: false, error: 'targetKey is required' }, 400);\n }\n if (!Number.isFinite(throughRow) || throughRow < 1) {\n return c.json({ ok: false, error: 'throughRow must be a positive integer' }, 400);\n }\n try {\n const result = await service.sessions.forkRows(key, targetKey, { throughRow });\n return c.json({ ok: true, ...result });\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: errorMessage }, 400);\n }\n });\n\n // DELETE /api/sessions/:key/messages — delete LLM rows by range or by user turn.\n // `userRoundIndex` (0-based among user messages) removes the user row and every following\n // assistant / tool / toolResult row until the next user. Prefer this from the web console so\n // tool loops are not left orphaned after retry/delete.\n authenticated.delete('/api/sessions/:key/messages', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const loaded = await service.sessionIndexInstance.loadMessages(key);\n if (!loaded) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n let startIndex = typeof body.startIndex === 'number' ? body.startIndex : -1;\n let count = typeof body.count === 'number' ? body.count : 0;\n const userRoundIndex =\n typeof body.userRoundIndex === 'number' ? body.userRoundIndex : undefined;\n\n if (userRoundIndex !== undefined) {\n const range = computeUserRoundDeleteRange(loaded, userRoundIndex);\n if (!range) {\n return c.json({ error: 'User round index out of range' }, 400);\n }\n startIndex = range.startIndex;\n count = range.count;\n }\n\n if (startIndex < 0 || count <= 0) {\n return c.json({ error: 'Invalid startIndex or count' }, 400);\n }\n if (startIndex >= loaded.length) {\n return c.json({ error: 'Index out of range' }, 400);\n }\n const deleteCount = Math.min(count, loaded.length - startIndex);\n const removed = loaded.slice(startIndex, startIndex + deleteCount);\n const next = loaded.slice(0, startIndex).concat(loaded.slice(startIndex + deleteCount));\n await service.sessionIndexInstance.saveMessages(key, next);\n await deleteMediaUrisNoLongerReferenced({ removed, remaining: next });\n return c.json({ ok: true, deleted: deleteCount });\n });\n\n // POST /api/sessions/:key/reset - Reset session (archive transcript, new session id; keep key + overrides)\n authenticated.post('/api/sessions/:key/reset', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.reset');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const result = await service.sessions.reset(key);\n if (result.ok === false) {\n const status = result.error === 'Session not found' ? 404 : 400;\n return c.json({ ok: false, error: result.error }, status);\n }\n const session = await service.sessions.getSession(key);\n return c.json({\n ok: true,\n reset: true,\n sessionId: result.sessionId,\n previousSessionId: result.previousSessionId,\n session,\n });\n });\n\n // DELETE /api/sessions/:key - Delete session (removes key from index)\n authenticated.delete('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.delete(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/archive - Archive session\n authenticated.post('/api/sessions/:key/archive', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.archive(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/unarchive - Unarchive session\n authenticated.post('/api/sessions/:key/unarchive', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.unarchive(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/pin - Pin session\n authenticated.post('/api/sessions/:key/pin', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.pin(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/unpin - Unpin session\n authenticated.post('/api/sessions/:key/unpin', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.unpin(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/rename - Rename session\n authenticated.post('/api/sessions/:key/rename', async (c) => {\n const key = c.req.param('key');\n\n const body = await c.req.json();\n const { name } = body;\n const result = await service.sessions.rename(key, name);\n return c.json(result);\n });\n\n // ========== Subagent REST API (/api/subagents) ==========\n\n // GET /api/subagents - List subagent sessions\n authenticated.get('/api/subagents', async (c) => {\n const query = c.req.query();\n const result = await service.sessions.listSubagents({\n limit: query.limit ? parseInt(query.limit) : undefined,\n offset: query.offset ? parseInt(query.offset) : undefined,\n });\n return c.json(result);\n });\n\n // GET /api/subagents/:key - Get subagent session detail\n authenticated.get('/api/subagents/:key', async (c) => {\n const key = c.req.param('key');\n // Verify it's a subagent session\n if (!key.startsWith('subagent:')) {\n return c.json({ error: 'Not a subagent session' }, 400);\n }\n const includeRaw = c.req.query('include') ?? '';\n const includeSet = new Set(\n includeRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n );\n const session = await service.sessions.getSession(key, {\n includeTranscriptSummary: includeSet.has('transcript'),\n includeTranscriptRows: includeSet.has('transcriptRows'),\n });\n if (!session) {\n return c.json({ error: 'Subagent session not found' }, 404);\n }\n return c.json({ session });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sessions.js","names":[],"sources":["../../../../../src/gateway/hono/routes/sessions.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { buildSessionKey } from '../../../routing/session-key.js';\nimport { agentExists, getDefaultAgentId } from '../../../routing/resolve-route.js';\nimport type { SessionMetadataSeed } from '../../../storage/sqlite/index.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport { createGatewayRouteLogger, logRouteError } from '../lib/route-logger.js';\nimport { messagesToClientHistory } from '../../../session/client-history.js';\nimport type { SessionType } from '../../../session/types.js';\nimport { computeUserRoundDeleteRange } from '../../../session/user-round-delete.js';\nimport { deleteMediaUrisNoLongerReferenced } from '../../../media/session-references.js';\nimport { respondStartupUnavailable } from '../lib/startup-unavailable.js';\nimport type { StartupUnavailableGatewayMethod } from '../../startup-readiness.js';\nimport { evictEmbeddedSessionRunner } from '../../../agent/embedded/session-runner.js';\n\nconst log = createGatewayRouteLogger('Sessions');\n\ntype SessionsStartupMethod = StartupUnavailableGatewayMethod;\n\nconst SESSION_TYPES = new Set<SessionType>(['chat', 'workflow-run', 'workflow-subagent', 'cron', 'heartbeat']);\n\nfunction isSessionType(value: string): value is SessionType {\n return SESSION_TYPES.has(value as SessionType);\n}\n\nfunction ensureGatewayReadyForSessions(\n c: Parameters<typeof respondStartupUnavailable>[0],\n service: AuthenticatedRouteDeps['service'],\n method: SessionsStartupMethod,\n): Response | null {\n if (service.isGatewayReady()) {\n return null;\n }\n return respondStartupUnavailable(c, method);\n}\n\nfunction buildDirectSessionMetadata(params: {\n agentId: string;\n source: string;\n accountId: string;\n peerId: string;\n}): SessionMetadataSeed {\n return {\n sourceChannel: params.source,\n sourceChatId: [params.accountId, 'direct', params.peerId].join(':'),\n sessionType: 'chat',\n routing: {\n agentId: params.agentId,\n source: params.source,\n accountId: params.accountId,\n peerKind: 'direct',\n peerId: params.peerId,\n },\n };\n}\n\nexport function registerSessionsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // ========== Session REST API (/api/sessions) ==========\n\n // POST /api/sessions - Create new session (reuses empty sessions)\n authenticated.post('/api/sessions', async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const channel = body.channel || 'webchat';\n const routingCfg = service.currentConfig;\n let agentId =\n typeof body.agentId === 'string' && body.agentId.trim()\n ? body.agentId.trim().toLowerCase()\n : getDefaultAgentId(routingCfg);\n if (!agentExists(agentId, routingCfg)) {\n agentId = getDefaultAgentId(routingCfg);\n }\n\n // If a specific chat_id is provided, use it (for advanced use cases)\n // Otherwise, try to find and reuse an existing empty session\n if (body.chat_id) {\n const sessionKey = buildSessionKey({\n agentId,\n source: channel,\n accountId: 'default',\n peerKind: 'direct',\n peerId: body.chat_id,\n });\n\n await service.sessionIndexInstance.saveMessages(sessionKey, [], {\n metadata: buildDirectSessionMetadata({\n agentId,\n source: channel,\n accountId: 'default',\n peerId: body.chat_id,\n }),\n });\n const session = await service.sessions.getSession(sessionKey);\n return c.json({ session }, 201);\n }\n\n // Look for existing empty sessions to reuse\n const existingSessions = await service.sessions.listSessions({\n channel,\n limit: 50,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n \n // Reuse an empty session only when it matches the requested agent (session key embeds agent id).\n const emptySession = existingSessions.items.find((s) => {\n if (s.messageCount !== 0) return false;\n return s.routing?.agentId === agentId;\n });\n \n if (emptySession) {\n // Return existing empty session instead of creating a new one\n const session = await service.sessions.getSession(emptySession.key);\n return c.json({ session, reused: true }, 200);\n }\n \n // No empty session found, create a new one\n const chatId = `chat_${Date.now()}`;\n const sessionKey = buildSessionKey({\n agentId,\n source: channel,\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n\n await service.sessionIndexInstance.saveMessages(sessionKey, [], {\n metadata: buildDirectSessionMetadata({\n agentId,\n source: channel,\n accountId: 'default',\n peerId: chatId,\n }),\n });\n\n const session = await service.sessions.getSession(sessionKey);\n return c.json({ session }, 201);\n });\n\n // GET /api/sessions - List sessions\n authenticated.get('/api/sessions', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.list');\n if (blocked) {\n return blocked;\n }\n const query = c.req.query();\n const sessionTypes = query.types\n ?.split(',')\n .map((value) => value.trim())\n .filter(isSessionType);\n const result = await service.sessions.listSessions({\n status: query.status as any,\n search: query.search,\n channel: query.channel,\n sessionTypes: sessionTypes?.length ? sessionTypes : undefined,\n includeHidden: query.includeHidden === 'true',\n limit: query.limit ? parseInt(query.limit) : undefined,\n offset: query.offset ? parseInt(query.offset) : undefined,\n });\n return c.json(result);\n });\n\n // GET /api/sessions/stats - Get session stats (must be before /:key)\n authenticated.get('/api/sessions/stats', async (c) => {\n const result = await service.sessions.stats();\n return c.json(result);\n });\n\n // GET /api/sessions/chat-ids - Get unique chat IDs from sessions (must be before /:key)\n authenticated.get('/api/sessions/chat-ids', async (c) => {\n const channel = c.req.query('channel');\n const chatIds = await service.sessions.chatIds(channel || undefined);\n return c.json({ ok: true, payload: { chatIds } });\n });\n\n // GET /api/sessions/resolve?sessionId=... - Resolve OpenClaw-style session id to canonical key.\n authenticated.get('/api/sessions/resolve', async (c) => {\n const result = await service.sessions.resolveSession({\n sessionId: c.req.query('sessionId'),\n sessionKey: c.req.query('sessionKey') ?? c.req.query('key'),\n });\n if (!result) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, payload: result });\n });\n\n // POST /api/sessions/resolve - Resolve by JSON body (`sessionId`, `sessionKey`, or `key`).\n authenticated.post('/api/sessions/resolve', async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const result = await service.sessions.resolveSession({\n sessionId: typeof body.sessionId === 'string' ? body.sessionId : undefined,\n sessionKey: typeof body.sessionKey === 'string' ? body.sessionKey : undefined,\n key: typeof body.key === 'string' ? body.key : undefined,\n });\n if (!result) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, payload: result });\n });\n\n // GET /api/sessions/:key/run — read-only active webchat agent run (for UI resume)\n authenticated.get('/api/sessions/:key/run', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.run');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, payload: service.sessions.getActiveRun(key) });\n });\n\n // GET /api/sessions/:key/agent-config — resolved session agent settings (thinking, etc.)\n authenticated.get('/api/sessions/:key/agent-config', async (c) => {\n const key = c.req.param('key');\n const payload = await service.sessions.getAgentConfig(key);\n return c.json({ ok: true, payload });\n });\n\n authenticated.patch('/api/sessions/:key/agent-config', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const result = await service.sessions.patchAgentConfig(key, body);\n if (!result.ok) {\n return c.json({ ok: false, error: result.error }, 400);\n }\n return c.json({ ok: true });\n });\n\n // GET /api/sessions/:key/messages — flattened transcript for TUI / clients\n authenticated.get('/api/sessions/:key/messages', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.messages');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const limitRaw = c.req.query('limit');\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : undefined;\n const limit =\n parsedLimit !== undefined && Number.isFinite(parsedLimit)\n ? Math.min(500, Math.max(1, parsedLimit))\n : undefined;\n\n const before = c.req.query('before')?.trim();\n const offsetRaw = c.req.query('offset');\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n\n const result = await service.sessions.getMessagePage(key, {\n limit,\n offset,\n ...(before ? { before } : {}),\n });\n if (!result) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n\n const messages = messagesToClientHistory(result.session.messages, { limit });\n return c.json({\n ok: true,\n payload: { messages },\n pagination: result.pagination,\n });\n });\n\n // GET /api/sessions/:key/history — UI chat history page from the newest tail.\n authenticated.get('/api/sessions/:key/history', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.history');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const offsetRaw = c.req.query('offset');\n const limitRaw = c.req.query('limit');\n const before = c.req.query('before')?.trim();\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : 50;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n const limit = Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50;\n const result = await service.sessions.getMessagePage(key, {\n offset,\n limit,\n ...(before ? { before } : {}),\n });\n\n if (!result) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n return c.json(result);\n });\n\n // POST /api/sessions/:key/transcript/context — append persisted-only `kind: 'context'` row (not in LLM context)\n authenticated.post('/api/sessions/:key/transcript/context', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const id = typeof body.id === 'string' && body.id.trim() ? body.id.trim() : undefined;\n const text = typeof body.text === 'string' ? body.text : undefined;\n const data =\n body.data !== undefined && typeof body.data === 'object' && body.data !== null && !Array.isArray(body.data)\n ? (body.data as Record<string, unknown>)\n : undefined;\n await service.sessionIndexInstance.appendTranscriptContextEntry(key, { id, text, data });\n return c.json({ ok: true });\n });\n\n // POST /api/sessions/:key/transcript/label — append label change for a transcript entry.\n authenticated.post('/api/sessions/:key/transcript/label', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const targetId = typeof body.targetId === 'string' ? body.targetId.trim() : '';\n if (!targetId) {\n return c.json({ ok: false, error: 'targetId is required' }, 400);\n }\n const label = typeof body.label === 'string' && body.label.trim() ? body.label.trim() : undefined;\n await service.sessionIndexInstance.appendTranscriptLabelEntry(key, { targetId, label });\n return c.json({ ok: true });\n });\n\n // POST /api/sessions/:key/transcript/custom — append extension state for TUI replay.\n authenticated.post('/api/sessions/:key/transcript/custom', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const customType = typeof body.customType === 'string' ? body.customType.trim() : '';\n if (!customType) {\n return c.json({ ok: false, error: 'customType is required' }, 400);\n }\n await service.sessionIndexInstance.appendTranscriptCustomEntry(key, {\n customType,\n data: body.data,\n });\n return c.json({ ok: true });\n });\n\n // POST /api/sessions/:key/transcript/custom-message — append visible extension custom message.\n authenticated.post('/api/sessions/:key/transcript/custom-message', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const customType = typeof body.customType === 'string' ? body.customType.trim() : '';\n if (!customType) {\n return c.json({ ok: false, error: 'customType is required' }, 400);\n }\n const content =\n typeof body.content === 'string' || Array.isArray(body.content) ? body.content : '';\n await service.sessionIndexInstance.appendTranscriptCustomMessageEntry(key, {\n customType,\n content,\n display: typeof body.display === 'boolean' ? body.display : undefined,\n details: body.details,\n });\n evictEmbeddedSessionRunner(key, 'gateway_custom_message_appended');\n return c.json({ ok: true });\n });\n\n // POST /api/sessions/:key/transcript/bash — append local TUI shell execution.\n authenticated.post('/api/sessions/:key/transcript/bash', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const command = typeof body.command === 'string' ? body.command.trim() : '';\n if (!command) {\n return c.json({ ok: false, error: 'command is required' }, 400);\n }\n await service.sessionIndexInstance.appendTranscriptBashExecutionEntry(key, {\n command,\n output: typeof body.output === 'string' ? body.output : '',\n exitCode: typeof body.exitCode === 'number' ? body.exitCode : body.exitCode === null ? null : undefined,\n signal: typeof body.signal === 'string' ? body.signal : body.signal === null ? null : undefined,\n excludeFromContext: body.excludeFromContext === true,\n truncated: body.truncated === true,\n fullOutputPath: typeof body.fullOutputPath === 'string' ? body.fullOutputPath : undefined,\n });\n evictEmbeddedSessionRunner(key, 'gateway_bash_execution_appended');\n return c.json({ ok: true });\n });\n\n // GET /api/sessions/:key/compaction/checkpoints — list pre-compaction snapshots (OpenClaw-style)\n authenticated.get('/api/sessions/:key/compaction/checkpoints', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const checkpoints = await service.sessions.listCompactionCheckpoints(key);\n return c.json({ ok: true, payload: { checkpoints } });\n });\n\n authenticated.get('/api/sessions/:key/compaction/checkpoints/:checkpointId', async (c) => {\n const key = c.req.param('key');\n const checkpointId = c.req.param('checkpointId');\n const checkpoint = await service.sessions.getCompactionCheckpoint(key, checkpointId);\n if (!checkpoint) {\n return c.json({ ok: false, error: 'Checkpoint not found' }, 404);\n }\n return c.json({ ok: true, payload: { checkpoint } });\n });\n\n authenticated.post('/api/sessions/:key/compaction/restore', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const checkpointId = typeof body.checkpointId === 'string' ? body.checkpointId.trim() : '';\n if (!checkpointId) {\n return c.json({ ok: false, error: 'checkpointId required' }, 400);\n }\n try {\n await service.sessions.restoreCompactionCheckpoint(key, checkpointId);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.sessions', { operation: 'restoreCheckpoint', sessionKey: key });\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('not found') || msg.includes('Invalid')) {\n return c.json({ ok: false, error: msg }, 404);\n }\n return c.json({ ok: false, error: msg }, 500);\n }\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, session });\n });\n\n authenticated.post('/api/sessions/:key/compaction/run', async (c) => {\n const key = c.req.param('key');\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const instructions = typeof body.instructions === 'string' ? body.instructions : undefined;\n const force = typeof body.force === 'boolean' ? body.force : true;\n const result = await service.sessions.runCompaction(key, { instructions, force });\n return c.json({ ok: true, payload: { result } });\n });\n\n // GET /api/sessions/:key - Get single session (must be after /stats and /chat-ids)\n authenticated.get('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const includeRaw = c.req.query('include') ?? '';\n const includeSet = new Set(\n includeRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n );\n const includeTranscript = includeSet.has('transcript');\n const includeTranscriptRows = includeSet.has('transcriptRows');\n const offsetRaw = c.req.query('offset');\n const limitRaw = c.req.query('limit');\n const hasPagingQuery = offsetRaw !== undefined || limitRaw !== undefined;\n\n if (hasPagingQuery) {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.history');\n if (blocked) {\n return blocked;\n }\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : 50;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n const limit = Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50;\n const result = await service.sessions.getMessagePage(key, {\n offset,\n limit,\n includeTranscriptSummary: includeTranscript,\n includeTranscriptRows,\n });\n if (!result) {\n return c.json({ error: 'Session not found' }, 404);\n }\n return c.json(result);\n }\n\n const session = await service.sessions.getSession(key, {\n includeTranscriptSummary: includeTranscript,\n includeTranscriptRows,\n });\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n return c.json({ session });\n });\n\n // PATCH /api/sessions/:key - Partial metadata (name, tags, customData); OpenClaw-style patch subset\n authenticated.patch('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const patch: {\n name?: string;\n tags?: string[];\n replaceTags?: boolean;\n customData?: Record<string, unknown>;\n } = {};\n if (typeof body.name === 'string') {\n patch.name = body.name;\n }\n if (Array.isArray(body.tags)) {\n patch.tags = body.tags;\n }\n if (typeof body.replaceTags === 'boolean') {\n patch.replaceTags = body.replaceTags;\n }\n if (body.customData !== undefined && typeof body.customData === 'object' && body.customData !== null) {\n patch.customData = body.customData as Record<string, unknown>;\n }\n const result = await service.sessions.patch(key, patch);\n if (result.ok === false) {\n return c.json({ ok: false, error: result.error }, 404);\n }\n\n const session = await service.sessions.getSession(key);\n return c.json({ ok: true, session });\n });\n\n // GET /api/sessions/:key/export - Export session (must be before /:key)\n authenticated.get('/api/sessions/:key/export', async (c) => {\n const key = c.req.param('key');\n const format = c.req.query('format') as any || 'json';\n const result = await service.sessions.export(key, format);\n return c.json(result);\n });\n\n authenticated.post('/api/sessions/import', async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const targetKey = typeof body.targetKey === 'string' ? body.targetKey : '';\n const content = typeof body.content === 'string' ? body.content : '';\n if (!targetKey.trim()) {\n return c.json({ ok: false, error: 'targetKey is required' }, 400);\n }\n if (!content.trim()) {\n return c.json({ ok: false, error: 'content is required' }, 400);\n }\n try {\n const result = await service.sessions.importExport(targetKey, content);\n return c.json({ ok: true, ...result });\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: errorMessage }, 400);\n }\n });\n\n authenticated.post('/api/sessions/:key/btw', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const question = typeof body.question === 'string' ? body.question : '';\n const result = await service.sessions.btwQuery(key, question);\n return c.json(result);\n });\n\n authenticated.post('/api/sessions/:key/fork', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const targetKey = typeof body.targetKey === 'string' ? body.targetKey : '';\n if (!targetKey.trim()) {\n return c.json({ ok: false, error: 'targetKey is required' }, 400);\n }\n try {\n const result = await service.sessions.fork(key, targetKey);\n return c.json({ ok: true, ...result });\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: errorMessage }, 400);\n }\n });\n\n authenticated.post('/api/sessions/:key/fork-row', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const targetKey = typeof body.targetKey === 'string' ? body.targetKey : '';\n const throughRow = typeof body.throughRow === 'number' ? Math.trunc(body.throughRow) : NaN;\n if (!targetKey.trim()) {\n return c.json({ ok: false, error: 'targetKey is required' }, 400);\n }\n if (!Number.isFinite(throughRow) || throughRow < 1) {\n return c.json({ ok: false, error: 'throughRow must be a positive integer' }, 400);\n }\n try {\n const result = await service.sessions.forkRows(key, targetKey, { throughRow });\n return c.json({ ok: true, ...result });\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: errorMessage }, 400);\n }\n });\n\n // DELETE /api/sessions/:key/messages — delete LLM rows by range or by user turn.\n // `userRoundIndex` (0-based among user messages) removes the user row and every following\n // assistant / tool / toolResult row until the next user. Prefer this from the web console so\n // tool loops are not left orphaned after retry/delete.\n authenticated.delete('/api/sessions/:key/messages', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const loaded = await service.sessionIndexInstance.loadMessages(key);\n if (!loaded) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n let startIndex = typeof body.startIndex === 'number' ? body.startIndex : -1;\n let count = typeof body.count === 'number' ? body.count : 0;\n const userRoundIndex =\n typeof body.userRoundIndex === 'number' ? body.userRoundIndex : undefined;\n\n if (userRoundIndex !== undefined) {\n const range = computeUserRoundDeleteRange(loaded, userRoundIndex);\n if (!range) {\n return c.json({ error: 'User round index out of range' }, 400);\n }\n startIndex = range.startIndex;\n count = range.count;\n }\n\n if (startIndex < 0 || count <= 0) {\n return c.json({ error: 'Invalid startIndex or count' }, 400);\n }\n if (startIndex >= loaded.length) {\n return c.json({ error: 'Index out of range' }, 400);\n }\n const deleteCount = Math.min(count, loaded.length - startIndex);\n const removed = loaded.slice(startIndex, startIndex + deleteCount);\n const next = loaded.slice(0, startIndex).concat(loaded.slice(startIndex + deleteCount));\n await service.sessionIndexInstance.saveMessages(key, next);\n await deleteMediaUrisNoLongerReferenced({ removed, remaining: next });\n return c.json({ ok: true, deleted: deleteCount });\n });\n\n // POST /api/sessions/:key/reset - Reset session (archive transcript, new session id; keep key + overrides)\n authenticated.post('/api/sessions/:key/reset', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.reset');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const result = await service.sessions.reset(key);\n if (result.ok === false) {\n const status = result.error === 'Session not found' ? 404 : 400;\n return c.json({ ok: false, error: result.error }, status);\n }\n const session = await service.sessions.getSession(key);\n return c.json({\n ok: true,\n reset: true,\n sessionId: result.sessionId,\n previousSessionId: result.previousSessionId,\n session,\n });\n });\n\n // DELETE /api/sessions/:key - Delete session (removes key from index)\n authenticated.delete('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.delete(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/archive - Archive session\n authenticated.post('/api/sessions/:key/archive', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.archive(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/unarchive - Unarchive session\n authenticated.post('/api/sessions/:key/unarchive', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.unarchive(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/pin - Pin session\n authenticated.post('/api/sessions/:key/pin', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.pin(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/unpin - Unpin session\n authenticated.post('/api/sessions/:key/unpin', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.unpin(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/rename - Rename session\n authenticated.post('/api/sessions/:key/rename', async (c) => {\n const key = c.req.param('key');\n\n const body = await c.req.json();\n const { name } = body;\n const result = await service.sessions.rename(key, name);\n return c.json(result);\n });\n\n // ========== Subagent REST API (/api/subagents) ==========\n\n // GET /api/subagents - List subagent sessions\n authenticated.get('/api/subagents', async (c) => {\n const query = c.req.query();\n const result = await service.sessions.listSubagents({\n limit: query.limit ? parseInt(query.limit) : undefined,\n offset: query.offset ? parseInt(query.offset) : undefined,\n });\n return c.json(result);\n });\n\n // GET /api/subagents/:key - Get subagent session detail\n authenticated.get('/api/subagents/:key', async (c) => {\n const key = c.req.param('key');\n // Verify it's a subagent session\n if (!key.startsWith('subagent:')) {\n return c.json({ error: 'Not a subagent session' }, 400);\n }\n const includeRaw = c.req.query('include') ?? '';\n const includeSet = new Set(\n includeRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n );\n const session = await service.sessions.getSession(key, {\n includeTranscriptSummary: includeSet.has('transcript'),\n includeTranscriptRows: includeSet.has('transcriptRows'),\n });\n if (!session) {\n return c.json({ error: 'Subagent session not found' }, 404);\n }\n return c.json({ session });\n });\n}\n"],"mappings":"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"}
@@ -6,8 +6,8 @@ import { buildFilePathClassifierContext, classifyFileLocation, displayNameForPat
6
6
  import { fileReferenceRegistry } from "../../file-reference-registry.js";
7
7
  import { resolveHeartbeatMdPath } from "../../workspace-heartbeat-path.js";
8
8
  import { createGatewayRouteLogger } from "../lib/route-logger.js";
9
- import { listWorkspaceRelativeFilesFsFallback } from "../../workspace-fs-file-list.js";
10
- import { runRipgrepInDirectory, runRipgrepListFiles } from "../../workspace-ripgrep.js";
9
+ import { runRipgrepInDirectory } from "../../workspace-ripgrep.js";
10
+ import { fuzzySearchWorkspaceFiles } from "../../workspace-file-search.js";
11
11
  import { randomUUID } from "node:crypto";
12
12
  import { constants } from "node:fs";
13
13
  import { copyFile, link, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
@@ -15,69 +15,6 @@ import { basename, dirname, join, resolve } from "node:path";
15
15
  //#region src/gateway/hono/routes/workspace.ts
16
16
  init_agent_scope();
17
17
  const log = createGatewayRouteLogger("Workspace");
18
- const FILE_SEARCH_MAX_LIMIT = 50;
19
- /** Subsequence fuzzy match: all query chars appear in order in `candidate` (case-insensitive). */
20
- function fuzzySubsequenceScore(query, candidate) {
21
- const q = query.toLowerCase();
22
- const c = candidate.toLowerCase();
23
- if (q.length === 0) return 0;
24
- let qi = 0;
25
- for (let ci = 0; ci < c.length && qi < q.length; ci++) if (c[ci] === q[qi]) qi++;
26
- if (qi < q.length) return null;
27
- const base = c.split("/").pop() ?? c;
28
- let score = 10;
29
- if (c.startsWith(q)) score += 40;
30
- if (base.startsWith(q)) score += 35;
31
- else if (base.includes(q)) score += 20;
32
- else if (c.includes(q)) score += 10;
33
- score -= c.length * 1e-4;
34
- return score;
35
- }
36
- async function fuzzySearchWorkspaceFiles(workspaceRoot, query, limit) {
37
- let files = await runRipgrepListFiles(workspaceRoot);
38
- if (files.length === 0) {
39
- files = await listWorkspaceRelativeFilesFsFallback(workspaceRoot, 12e4);
40
- if (files.length > 0) log.debug({
41
- workspaceRoot,
42
- fileCount: files.length
43
- }, "workspace files/search: file list from fs walk (ripgrep unavailable or returned empty)");
44
- }
45
- const q = query.trim();
46
- const capped = Math.min(Math.max(limit, 1), FILE_SEARCH_MAX_LIMIT);
47
- const rows = [];
48
- if (!q) {
49
- const sorted = [...files].sort((a, b) => a.localeCompare(b));
50
- for (const rel of sorted.slice(0, capped)) {
51
- const name = rel.split("/").pop() ?? rel;
52
- rows.push({
53
- name,
54
- path: rel,
55
- isDirectory: false,
56
- score: 0
57
- });
58
- }
59
- return rows;
60
- }
61
- for (const rel of files) {
62
- const name = rel.split("/").pop() ?? rel;
63
- const scorePath = fuzzySubsequenceScore(q, rel);
64
- const scoreName = fuzzySubsequenceScore(q, name);
65
- const score = Math.max(scorePath ?? -Infinity, scoreName ?? -Infinity);
66
- if (score === -Infinity) continue;
67
- rows.push({
68
- name,
69
- path: rel,
70
- isDirectory: false,
71
- score
72
- });
73
- }
74
- rows.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
75
- return rows.slice(0, capped).map(({ name, path, isDirectory }) => ({
76
- name,
77
- path,
78
- isDirectory
79
- }));
80
- }
81
18
  function isKnownEditorAgentId(cfg, id) {
82
19
  const n = normalizeAgentId(id);
83
20
  if (n === resolveDefaultAgentId(cfg)) return true;
@@ -1043,7 +980,7 @@ function registerWorkspaceRoutes(authenticated, deps) {
1043
980
  authenticated.get("/api/workspace/editor/files/search", async (c) => {
1044
981
  const q = typeof c.req.query("q") === "string" ? c.req.query("q").trim() : "";
1045
982
  const limitRaw = c.req.query("limit");
1046
- const limit = Math.min(Math.max(parseInt(typeof limitRaw === "string" ? limitRaw : "15", 10) || 15, 1), FILE_SEARCH_MAX_LIMIT);
983
+ const limit = Math.min(Math.max(parseInt(typeof limitRaw === "string" ? limitRaw : "15", 10) || 15, 1), 50);
1047
984
  const ws = await resolveEditorWorkspaceRootAsync(service, service.currentConfig, c.req.query("sessionKey"), c.req.query("agentId"));
1048
985
  if (ws.ok === false) return c.json({
1049
986
  ok: false,
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.js","names":["fsConstants"],"sources":["../../../../../src/gateway/hono/routes/workspace.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport { copyFile, link, mkdir, readdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\n\nimport { type Config } from '../../../config/schema.js';\nimport { getWorkspacePath } from '../../../config/workspace-path-helpers.js';\nimport { validateWritePath } from '../../../agent/sandbox/path-policy.js';\nimport {\n listAgentEntries,\n normalizeAgentId,\n resolveAgentWorkspaceDir,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport { createGatewayRouteLogger } from '../lib/route-logger.js';\nimport { resolveHeartbeatMdPath } from '../../workspace-heartbeat-path.js';\nimport {\n isPathUnderWorkspace,\n resolveWorkspaceSafePath,\n toWorkspaceRelativePosix,\n} from '../../workspace-editor-path.js';\nimport { listWorkspaceRelativeFilesFsFallback } from '../../workspace-fs-file-list.js';\nimport { runRipgrepInDirectory, runRipgrepListFiles } from '../../workspace-ripgrep.js';\nimport {\n buildFilePathClassifierContext,\n classifyFileLocation,\n displayNameForPath,\n fileRefSessionKeysMatch,\n resolveFileReferenceCandidate,\n} from '../../file-path-classifier.js';\nimport {\n fileReferenceRegistry,\n type FileReferenceCapability,\n type FileReferenceLocationKind,\n type FileReferenceScope,\n} from '../../file-reference-registry.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { GatewayService } from '../../service.js';\n\nconst log = createGatewayRouteLogger('Workspace');\n\nconst FILE_SEARCH_MAX_LIMIT = 50;\n\n/** Subsequence fuzzy match: all query chars appear in order in `candidate` (case-insensitive). */\nfunction fuzzySubsequenceScore(query: string, candidate: string): number | null {\n const q = query.toLowerCase();\n const c = candidate.toLowerCase();\n if (q.length === 0) return 0;\n let qi = 0;\n for (let ci = 0; ci < c.length && qi < q.length; ci++) {\n if (c[ci] === q[qi]) qi++;\n }\n if (qi < q.length) return null;\n const base = c.split('/').pop() ?? c;\n let score = 10;\n if (c.startsWith(q)) score += 40;\n if (base.startsWith(q)) score += 35;\n else if (base.includes(q)) score += 20;\n else if (c.includes(q)) score += 10;\n score -= c.length * 0.0001;\n return score;\n}\n\nasync function fuzzySearchWorkspaceFiles(\n workspaceRoot: string,\n query: string,\n limit: number,\n): Promise<Array<{ name: string; path: string; isDirectory: boolean }>> {\n let files = await runRipgrepListFiles(workspaceRoot);\n if (files.length === 0) {\n files = await listWorkspaceRelativeFilesFsFallback(workspaceRoot, 120_000);\n if (files.length > 0) {\n log.debug(\n { workspaceRoot, fileCount: files.length },\n 'workspace files/search: file list from fs walk (ripgrep unavailable or returned empty)',\n );\n }\n }\n const q = query.trim();\n const capped = Math.min(Math.max(limit, 1), FILE_SEARCH_MAX_LIMIT);\n\n type Row = { name: string; path: string; isDirectory: boolean; score: number };\n const rows: Row[] = [];\n\n if (!q) {\n const sorted = [...files].sort((a, b) => a.localeCompare(b));\n for (const rel of sorted.slice(0, capped)) {\n const name = rel.split('/').pop() ?? rel;\n rows.push({ name, path: rel, isDirectory: false, score: 0 });\n }\n return rows;\n }\n\n for (const rel of files) {\n const name = rel.split('/').pop() ?? rel;\n const scorePath = fuzzySubsequenceScore(q, rel);\n const scoreName = fuzzySubsequenceScore(q, name);\n const score = Math.max(scorePath ?? -Infinity, scoreName ?? -Infinity);\n if (score === -Infinity) continue;\n rows.push({ name, path: rel, isDirectory: false, score });\n }\n\n rows.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));\n return rows.slice(0, capped).map(({ name, path, isDirectory }) => ({ name, path, isDirectory }));\n}\n\nfunction isKnownEditorAgentId(cfg: Config, id: string): boolean {\n const n = normalizeAgentId(id);\n if (n === resolveDefaultAgentId(cfg)) return true;\n return listAgentEntries(cfg).some((e) => normalizeAgentId(e.id) === n);\n}\n\nfunction resolveEditorWorkspaceRoot(\n cfg: Config,\n agentIdRaw: string | undefined,\n): { ok: true; root: string } | { ok: false; message: string } {\n const trimmed = typeof agentIdRaw === 'string' ? agentIdRaw.trim() : '';\n if (!trimmed) {\n const root = getWorkspacePath(cfg);\n if (!root) return { ok: false, message: 'Workspace not configured' };\n return { ok: true, root };\n }\n const id = normalizeAgentId(trimmed);\n if (!isKnownEditorAgentId(cfg, id)) {\n return { ok: false, message: 'Unknown agent' };\n }\n return { ok: true, root: resolveAgentWorkspaceDir(cfg, id) };\n}\n\n/** Prefer `sessionKey` (per-session workspace override) over `agentId`. */\nasync function resolveEditorWorkspaceRootAsync(\n service: GatewayService,\n cfg: Config,\n sessionKeyRaw: string | undefined,\n agentIdRaw: string | undefined,\n): Promise<{ ok: true; root: string } | { ok: false; message: string }> {\n const sk = typeof sessionKeyRaw === 'string' ? sessionKeyRaw.trim() : '';\n if (sk) {\n try {\n const root = await service.sessions.getEffectiveWorkspacePath(sk);\n return { ok: true, root };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey: sk }, 'Session workspace root resolution failed');\n return { ok: false, message: em || 'Session workspace resolution failed' };\n }\n }\n return resolveEditorWorkspaceRoot(cfg, agentIdRaw);\n}\n\ninterface ResolvedWorkspaceImportConfig {\n targetDir: string;\n maxBytes: number;\n allowOverwrite: boolean;\n}\n\nfunction resolveWorkspaceImportConfig(cfg: Config): ResolvedWorkspaceImportConfig {\n const raw = cfg.workspace?.import;\n return {\n targetDir: raw?.targetDir?.trim() || 'imports',\n maxBytes: raw?.maxBytes ?? 104_857_600,\n allowOverwrite: raw?.allowOverwrite ?? true,\n };\n}\n\n/** Strip path separators, NULs and control chars from a basename so it stays in the destination dir. */\nfunction sanitizeImportBasename(name: string): string {\n return name\n .replace(/[\\\\/]/g, '')\n .replace(/[\\x00-\\x1f\\x7f]/g, '')\n .trim();\n}\n\n/**\n * Race-safe target picker for the `rename`-on-conflict strategy. Uses `link(tmp, target)`\n * which atomically fails with EEXIST when the candidate is taken; on success the tmp\n * file is left in place for the caller to unlink. Returns the linked target path.\n */\nasync function pickAvailableTargetWithLink(\n tmpAbs: string,\n initialDestAbs: string,\n maxAttempts = 1000,\n): Promise<{ ok: true; path: string; attempts: number } | { ok: false; attempts: number }> {\n const dir = dirname(initialDestAbs);\n const original = basename(initialDestAbs);\n const dotIdx = original.lastIndexOf('.');\n const stem = dotIdx > 0 ? original.slice(0, dotIdx) : original;\n const ext = dotIdx > 0 ? original.slice(dotIdx) : '';\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const candidate = attempt === 1 ? initialDestAbs : join(dir, `${stem}-${attempt}${ext}`);\n try {\n await link(tmpAbs, candidate);\n return { ok: true, path: candidate, attempts: attempt };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n }\n }\n return { ok: false, attempts: maxAttempts };\n}\n\nfunction fileReferenceCapabilities(\n scope: FileReferenceScope,\n isDirectory: boolean,\n locationKind?: FileReferenceLocationKind,\n): FileReferenceCapability[] {\n if (scope === 'workspace') {\n return isDirectory\n ? ['openExternal', 'revealInFolder', 'copyPath']\n : ['preview', 'edit', 'openExternal', 'revealInFolder', 'copyPath'];\n }\n if (scope === 'external' || scope === 'agent-profile' || scope === 'session-artifact') {\n const base: FileReferenceCapability[] = ['openExternal', 'revealInFolder', 'copyPath'];\n // v1: importToWorkspace for files only; exclude xopc-config to prevent copying\n // app config into the workspace (semantically wrong).\n if (!isDirectory && locationKind !== 'xopc-config') {\n base.push('importToWorkspace');\n }\n return base;\n }\n if (scope === 'missing') return ['copyPath'];\n return [];\n}\n\nfunction isFileReferenceAction(action: unknown): action is 'openExternal' | 'revealInFolder' {\n return action === 'openExternal' || action === 'revealInFolder';\n}\n\nexport function registerWorkspaceRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n authenticated.get('/api/workspace/heartbeat-md', async (c) => {\n const abs = resolveHeartbeatMdPath(service.currentConfig);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return c.json({ ok: true, payload: { content: content, file: 'HEARTBEAT.md' } });\n } catch {\n return c.json({ ok: true, payload: { content: '', file: 'HEARTBEAT.md' } });\n }\n });\n\n authenticated.put('/api/workspace/heartbeat-md', async (c) => {\n const abs = resolveHeartbeatMdPath(service.currentConfig);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n const content =\n typeof body === 'object' &&\n body !== null &&\n 'content' in body &&\n typeof (body as { content: unknown }).content === 'string'\n ? (body as { content: string }).content\n : '';\n try {\n await writeFile(abs, content, 'utf-8');\n return c.json({ ok: true, payload: { file: 'HEARTBEAT.md' } });\n } catch (err) {\n log.error({ err, path: abs }, 'Failed to write HEARTBEAT.md');\n return c.json({ ok: false, error: { message: 'Write failed' } }, 500);\n }\n });\n\n authenticated.get('/api/workspace/editor/list', async (c) => {\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const dirRel = typeof c.req.query('dir') === 'string' ? c.req.query('dir')! : '';\n const absDir = resolveWorkspaceSafePath(workspaceRoot, dirRel);\n if (!absDir) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(absDir);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isDirectory()) {\n return c.json({ ok: false, error: { message: 'Not a directory' } }, 400);\n }\n const dirents = await readdir(absDir, { withFileTypes: true });\n const entries: { name: string; path: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (const entry of dirents) {\n if (entry.name.startsWith('.')) continue;\n const fullPath = join(absDir, entry.name);\n if (entry.isDirectory()) {\n entries.push({\n name: entry.name,\n path: toWorkspaceRelativePosix(workspaceRoot, fullPath),\n absolutePath: fullPath,\n isDirectory: true,\n });\n } else {\n entries.push({\n name: entry.name,\n path: toWorkspaceRelativePosix(workspaceRoot, fullPath),\n absolutePath: fullPath,\n isDirectory: false,\n });\n }\n }\n entries.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n return c.json({ ok: true, payload: { entries } });\n });\n\n authenticated.get('/api/workspace/editor/read', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return c.json({\n ok: true,\n payload: {\n content,\n path: toWorkspaceRelativePosix(workspaceRoot, abs),\n absolutePath: abs,\n mtimeMs: st.mtimeMs,\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n /** Read file as raw bytes and return base64 (for PDF/images in workspace preview — avoids UTF-8 corruption). */\n authenticated.get('/api/workspace/editor/read-base64', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n const buf = await readFile(abs);\n return c.json({\n ok: true,\n payload: {\n contentBase64: buf.toString('base64'),\n path: toWorkspaceRelativePosix(workspaceRoot, abs),\n /** Host absolute path — Electron can open with the default app (shell.openPath). */\n absolutePath: abs,\n mtimeMs: st.mtimeMs,\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n /** Map an absolute host path to a workspace-relative path (if under this session’s workspace). */\n authenticated.get('/api/workspace/editor/resolve-path', async (c) => {\n const raw = c.req.query('absolutePath');\n if (!raw || typeof raw !== 'string' || !raw.trim()) {\n return c.json({ ok: false, error: { message: 'Missing absolutePath' } }, 400);\n }\n const absolutePath = raw.trim();\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const normalized = resolve(absolutePath);\n if (!isPathUnderWorkspace(workspaceRoot, normalized)) {\n return c.json({ ok: false, error: { message: 'Path not under workspace' } }, 403);\n }\n const rel = toWorkspaceRelativePosix(workspaceRoot, normalized);\n return c.json({ ok: true, payload: { workspaceRelativePath: rel } });\n });\n\n authenticated.get('/api/workspace/editor/resolve-reference', async (c) => {\n const rawPath = typeof c.req.query('path') === 'string' ? c.req.query('path')!.trim() : '';\n if (!rawPath) {\n return c.json({ ok: false, error: { code: 'INVALID_PATH', message: 'Missing path' } }, 400);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n sessionKey,\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { code: 'WORKSPACE_RESOLUTION_FAILED', message: ws.message } }, 400);\n }\n\n const workspaceRoot = ws.root;\n const classifierCtx = { ...buildFilePathClassifierContext(service.currentConfig, sessionKey), workspaceRoot };\n const displayName = displayNameForPath(rawPath);\n const { candidate, invalid } = await resolveFileReferenceCandidate(rawPath, workspaceRoot, classifierCtx);\n\n if (!candidate || invalid) {\n return c.json({\n ok: true,\n payload: {\n inputPath: rawPath,\n displayName,\n scope: 'invalid' satisfies FileReferenceScope,\n exists: false,\n capabilities: [] as FileReferenceCapability[],\n errorCode: 'INVALID_PATH',\n },\n });\n }\n\n let st: Awaited<ReturnType<typeof stat>> | null = null;\n try {\n st = await stat(candidate);\n } catch {\n st = null;\n }\n\n if (!st) {\n // Always include the resolved candidate so the UI's \"Copy path\" yields\n // something actionable (\"I looked here, no file\"). Without this, bare\n // workspace-relative mentions fall back to the `rel:<path>` UI sentinel.\n return c.json({\n ok: true,\n payload: {\n inputPath: rawPath,\n displayName,\n scope: 'missing' satisfies FileReferenceScope,\n exists: false,\n absolutePath: candidate,\n capabilities: fileReferenceCapabilities('missing', false),\n errorCode: 'FILE_NOT_FOUND',\n },\n });\n }\n\n const classified = classifyFileLocation(candidate, classifierCtx);\n const { scope, locationKind, manageRoute } = classified;\n const inWorkspace = scope === 'workspace';\n const isDirectory = st.isDirectory();\n const capabilities = fileReferenceCapabilities(scope, isDirectory, locationKind);\n const ref = fileReferenceRegistry.register({\n absolutePath: candidate,\n sessionKey: sessionKey || undefined,\n scope,\n locationKind,\n capabilities,\n });\n\n return c.json({\n ok: true,\n payload: {\n fileRefId: ref.id,\n inputPath: rawPath,\n displayName,\n scope,\n locationKind,\n manageRoute,\n exists: true,\n isDirectory,\n absolutePath: candidate,\n workspaceRelativePath: inWorkspace ? toWorkspaceRelativePosix(workspaceRoot, candidate) : undefined,\n capabilities,\n mtimeMs: st.mtimeMs,\n },\n });\n });\n\n authenticated.post('/api/workspace/file-ref/:id/resolve-action', async (c) => {\n const id = c.req.param('id')?.trim() ?? '';\n if (!id) {\n return c.json({ ok: false, error: { code: 'INVALID_FILE_REF', message: 'Missing file reference' } }, 400);\n }\n\n const ref = fileReferenceRegistry.resolve(id);\n if (!ref) {\n return c.json({ ok: false, error: { code: 'FILE_REF_EXPIRED', message: 'File reference expired' } }, 404);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n if (!fileRefSessionKeysMatch(ref.sessionKey, sessionKey)) {\n return c.json({ ok: false, error: { code: 'FILE_REF_FORBIDDEN', message: 'File reference forbidden' } }, 403);\n }\n\n const body = (await c.req.json().catch(() => ({}))) as { action?: unknown };\n const action = body.action;\n if (!isFileReferenceAction(action)) {\n return c.json({ ok: false, error: { code: 'INVALID_ACTION', message: 'Invalid action' } }, 400);\n }\n if (!ref.capabilities.includes(action)) {\n return c.json({ ok: false, error: { code: 'ACTION_NOT_ALLOWED', message: 'Action not allowed' } }, 403);\n }\n\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(ref.absolutePath);\n } catch {\n return c.json({ ok: false, error: { code: 'FILE_NOT_FOUND', message: 'File not found' } }, 404);\n }\n\n return c.json({\n ok: true,\n payload: {\n absolutePath: ref.absolutePath,\n isDirectory: st.isDirectory(),\n },\n });\n });\n\n authenticated.post('/api/workspace/import-file-ref/:id', async (c) => {\n const id = c.req.param('id')?.trim() ?? '';\n if (!id) {\n return c.json({ ok: false, error: { code: 'INVALID_FILE_REF', message: 'Missing file reference' } }, 400);\n }\n\n const ref = fileReferenceRegistry.resolve(id);\n if (!ref) {\n return c.json({ ok: false, error: { code: 'FILE_REF_EXPIRED', message: 'File reference expired' } }, 404);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n if (!fileRefSessionKeysMatch(ref.sessionKey, sessionKey)) {\n return c.json({ ok: false, error: { code: 'FILE_REF_FORBIDDEN', message: 'File reference forbidden' } }, 403);\n }\n\n if (!ref.capabilities.includes('importToWorkspace')) {\n return c.json({ ok: false, error: { code: 'IMPORT_NOT_ALLOWED', message: 'Import not allowed for this file' } }, 403);\n }\n\n let sourceStat: Awaited<ReturnType<typeof stat>>;\n try {\n sourceStat = await stat(ref.absolutePath);\n } catch {\n fileReferenceRegistry.expireById(id);\n return c.json({ ok: false, error: { code: 'SOURCE_NOT_FOUND', message: 'Source file no longer exists' } }, 404);\n }\n if (!sourceStat.isFile()) {\n return c.json({ ok: false, error: { code: 'SOURCE_NOT_FILE', message: 'Source is not a regular file' } }, 400);\n }\n\n const importCfg = resolveWorkspaceImportConfig(service.currentConfig);\n if (sourceStat.size > importCfg.maxBytes) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'SOURCE_TOO_LARGE',\n message: `Source exceeds maximum import size (${importCfg.maxBytes} bytes)`,\n },\n },\n 413,\n );\n }\n\n const ws = await resolveEditorWorkspaceRootAsync(service, service.currentConfig, sessionKey, undefined);\n if (ws.ok === false) {\n return c.json({ ok: false, error: { code: 'WORKSPACE_RESOLUTION_FAILED', message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n\n let body: { destination?: unknown; onConflict?: unknown };\n try {\n body = (await c.req.json().catch(() => ({}))) as typeof body;\n } catch {\n body = {};\n }\n const requestedDestRaw = typeof body.destination === 'string' ? body.destination.trim() : '';\n const onConflictRaw = typeof body.onConflict === 'string' ? body.onConflict : 'rename';\n if (onConflictRaw !== 'rename' && onConflictRaw !== 'overwrite' && onConflictRaw !== 'error') {\n return c.json({ ok: false, error: { code: 'INVALID_CONFLICT_MODE', message: 'Invalid onConflict value' } }, 400);\n }\n const onConflict = onConflictRaw as 'rename' | 'overwrite' | 'error';\n if (onConflict === 'overwrite' && !importCfg.allowOverwrite) {\n return c.json({ ok: false, error: { code: 'OVERWRITE_DISABLED', message: 'Overwrite is disabled by config' } }, 403);\n }\n\n const sourceBasename = sanitizeImportBasename(basename(ref.absolutePath)) || 'imported-file';\n let requestedRel: string;\n if (!requestedDestRaw) {\n requestedRel = `${importCfg.targetDir}/${sourceBasename}`;\n } else {\n const trimmedDest = requestedDestRaw.replace(/\\\\/g, '/');\n // Path ending with `/` is treated as a directory; append source basename.\n requestedRel = trimmedDest.endsWith('/') ? `${trimmedDest}${sourceBasename}` : trimmedDest;\n }\n\n let initialDestAbs = resolveWorkspaceSafePath(workspaceRoot, requestedRel);\n if (!initialDestAbs) {\n return c.json({ ok: false, error: { code: 'INVALID_DESTINATION', message: 'Invalid destination path' } }, 400);\n }\n\n // Sandbox: blocks `.xopc/xopc.json`, `.env*`, etc.; canonical symlink resolution included.\n const writePolicy = validateWritePath(initialDestAbs, workspaceRoot);\n if (!writePolicy.allowed) {\n return c.json({ ok: false, error: { code: 'DESTINATION_BLOCKED', message: writePolicy.reason ?? 'Destination blocked' } }, 403);\n }\n\n if (resolve(ref.absolutePath) === resolve(initialDestAbs)) {\n return c.json({ ok: false, error: { code: 'SAME_LOCATION', message: 'Destination is the same as source' } }, 400);\n }\n\n const destDir = dirname(initialDestAbs);\n try {\n await mkdir(destDir, { recursive: true });\n } catch (err) {\n log.warn({ err, destDir }, 'Failed to create import destination directory');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to prepare destination' } }, 500);\n }\n\n // Stage source into a hidden tmp file inside the destination directory so we\n // can atomically `link` (rename strategy) or `rename` (overwrite) to land it.\n const tmpName = `.${basename(initialDestAbs)}.import-${randomUUID()}.tmp`;\n const tmpAbs = join(destDir, tmpName);\n\n const started = Date.now();\n let renamed = false;\n let overwrote = false;\n let finalDestAbs = initialDestAbs;\n\n try {\n try {\n await copyFile(ref.absolutePath, tmpAbs, fsConstants.COPYFILE_FICLONE);\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, source: ref.absolutePath, tmpAbs }, 'Failed to copy source into staging tmp');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to copy source file' } }, 500);\n }\n\n if (onConflict === 'overwrite') {\n // Snapshot pre-rename existence for telemetry; the actual overwrite is unconditional.\n overwrote = await stat(initialDestAbs).then(() => true).catch(() => false);\n try {\n await rename(tmpAbs, initialDestAbs);\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, target: initialDestAbs }, 'Atomic rename failed');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to finalize import' } }, 500);\n }\n } else if (onConflict === 'error') {\n const exists = await stat(initialDestAbs).then(() => true).catch(() => false);\n if (exists) {\n await unlink(tmpAbs).catch(() => {});\n return c.json({ ok: false, error: { code: 'DESTINATION_EXISTS', message: 'Destination already exists' } }, 409);\n }\n try {\n await link(tmpAbs, initialDestAbs);\n await unlink(tmpAbs).catch(() => {});\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, target: initialDestAbs }, 'Hard link to destination failed');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to finalize import' } }, 500);\n }\n } else {\n // rename: race-safe loop using O_EXCL semantics of `link`.\n const picked = await pickAvailableTargetWithLink(tmpAbs, initialDestAbs);\n if (!picked.ok) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ target: initialDestAbs, attempts: picked.attempts }, 'Failed to find free import target name');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'No free destination name available' } }, 500);\n }\n await unlink(tmpAbs).catch(() => {});\n finalDestAbs = picked.path;\n renamed = picked.path !== initialDestAbs;\n }\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.error({ err }, 'Import file unexpected failure');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Import failed' } }, 500);\n }\n\n let finalMtime: number;\n try {\n finalMtime = (await stat(finalDestAbs)).mtimeMs;\n } catch {\n finalMtime = Date.now();\n }\n\n const workspaceRel = toWorkspaceRelativePosix(workspaceRoot, finalDestAbs);\n\n fileReferenceRegistry.expireById(id);\n const newRef = fileReferenceRegistry.register({\n absolutePath: finalDestAbs,\n sessionKey: sessionKey || undefined,\n scope: 'workspace',\n capabilities: fileReferenceCapabilities('workspace', false),\n });\n\n const sourceScope = ref.scope;\n const sourceLocationKind = ref.locationKind;\n\n service.emit('workspace.file-imported', {\n sessionKey: sessionKey || undefined,\n workspaceRelativePath: workspaceRel,\n absolutePath: finalDestAbs,\n bytes: sourceStat.size,\n sourceScope,\n sourceLocationKind,\n });\n\n log.info(\n {\n sessionKey,\n fileRefId: id,\n sourceAbsolutePath: ref.absolutePath,\n sourceScope,\n sourceLocationKind,\n destWorkspaceRelativePath: workspaceRel,\n bytes: sourceStat.size,\n renamed,\n overwrote,\n durationMs: Date.now() - started,\n },\n 'Workspace file import succeeded',\n );\n\n return c.json({\n ok: true,\n payload: {\n workspaceRelativePath: workspaceRel,\n absolutePath: finalDestAbs,\n bytesCopied: sourceStat.size,\n sourceAbsolutePath: ref.absolutePath,\n sourceScope,\n sourceLocationKind,\n renamed,\n overwrote,\n mtimeMs: finalMtime,\n newFileRefId: newRef.id,\n },\n });\n });\n\n /**\n * Serve a workspace file as raw bytes (e.g. <img> after auth fetch + blob URL).\n * Path is workspace-relative; scope via sessionKey / agentId like other editor routes.\n */\n authenticated.get('/api/workspace/editor/raw', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n const ext = pathRel.split('.').pop()?.toLowerCase() ?? '';\n const mimeByExt: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n svg: 'image/svg+xml',\n pdf: 'application/pdf',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n txt: 'text/plain',\n md: 'text/markdown',\n json: 'application/json',\n html: 'text/html',\n css: 'text/css',\n js: 'text/javascript',\n ts: 'text/typescript',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n webm: 'video/webm',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n };\n const contentType = mimeByExt[ext] || 'application/octet-stream';\n try {\n const buf = await readFile(abs);\n return new Response(buf, {\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'private, max-age=3600',\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n authenticated.put('/api/workspace/editor/write', async (c) => {\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n const pathRel =\n typeof body === 'object' &&\n body !== null &&\n 'path' in body &&\n typeof (body as { path: unknown }).path === 'string'\n ? (body as { path: string }).path\n : '';\n const content =\n typeof body === 'object' &&\n body !== null &&\n 'content' in body &&\n typeof (body as { content: unknown }).content === 'string'\n ? (body as { content: string }).content\n : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>> | undefined;\n try {\n st = await stat(abs);\n } catch {\n st = undefined;\n }\n if (st && !st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n await writeFile(abs, content, 'utf-8');\n let mtimeMs: number;\n try {\n mtimeMs = (await stat(abs)).mtimeMs;\n } catch {\n mtimeMs = Date.now();\n }\n return c.json({\n ok: true,\n payload: { path: toWorkspaceRelativePosix(workspaceRoot, abs), mtimeMs },\n });\n } catch (err) {\n log.error({ err, path: abs }, 'workspace editor write failed');\n return c.json({ ok: false, error: { message: 'Write failed' } }, 500);\n }\n });\n\n authenticated.get('/api/workspace/editor/search', async (c) => {\n const q = typeof c.req.query('q') === 'string' ? c.req.query('q')!.trim() : '';\n const dirRel = typeof c.req.query('dir') === 'string' ? c.req.query('dir')! : '';\n if (!q) {\n return c.json({\n ok: true,\n payload: { results: [] as { filePath: string; lineNumber: number; lineContent: string; matchStart: number; matchEnd: number }[] },\n });\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const absDir = resolveWorkspaceSafePath(workspaceRoot, dirRel);\n if (!absDir) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(absDir);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isDirectory()) {\n return c.json({ ok: false, error: { message: 'Not a directory' } }, 400);\n }\n const raw = await runRipgrepInDirectory(q, absDir);\n const results = raw\n .filter((r) => isPathUnderWorkspace(workspaceRoot, r.filePath))\n .map((r) => ({\n ...r,\n filePath: toWorkspaceRelativePosix(workspaceRoot, resolve(r.filePath)),\n }));\n return c.json({ ok: true, payload: { results } });\n });\n\n /** Fuzzy filename / path search over the session workspace (ripgrep `--files` + subsequence scoring). */\n authenticated.get('/api/workspace/editor/files/search', async (c) => {\n const q = typeof c.req.query('q') === 'string' ? c.req.query('q')!.trim() : '';\n const limitRaw = c.req.query('limit');\n const limit = Math.min(\n Math.max(parseInt(typeof limitRaw === 'string' ? limitRaw : '15', 10) || 15, 1),\n FILE_SEARCH_MAX_LIMIT,\n );\n\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n\n const entries = await fuzzySearchWorkspaceFiles(ws.root, q, limit);\n return c.json({ ok: true, payload: { entries } });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workspace.js","names":["fsConstants"],"sources":["../../../../../src/gateway/hono/routes/workspace.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport { copyFile, link, mkdir, readdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\n\nimport { type Config } from '../../../config/schema.js';\nimport { getWorkspacePath } from '../../../config/workspace-path-helpers.js';\nimport { validateWritePath } from '../../../agent/sandbox/path-policy.js';\nimport {\n listAgentEntries,\n normalizeAgentId,\n resolveAgentWorkspaceDir,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport { createGatewayRouteLogger } from '../lib/route-logger.js';\nimport { resolveHeartbeatMdPath } from '../../workspace-heartbeat-path.js';\nimport {\n isPathUnderWorkspace,\n resolveWorkspaceSafePath,\n toWorkspaceRelativePosix,\n} from '../../workspace-editor-path.js';\nimport { runRipgrepInDirectory } from '../../workspace-ripgrep.js';\nimport { FILE_SEARCH_MAX_LIMIT, fuzzySearchWorkspaceFiles } from '../../workspace-file-search.js';\nimport {\n buildFilePathClassifierContext,\n classifyFileLocation,\n displayNameForPath,\n fileRefSessionKeysMatch,\n resolveFileReferenceCandidate,\n} from '../../file-path-classifier.js';\nimport {\n fileReferenceRegistry,\n type FileReferenceCapability,\n type FileReferenceLocationKind,\n type FileReferenceScope,\n} from '../../file-reference-registry.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { GatewayService } from '../../service.js';\n\nconst log = createGatewayRouteLogger('Workspace');\n\nfunction isKnownEditorAgentId(cfg: Config, id: string): boolean {\n const n = normalizeAgentId(id);\n if (n === resolveDefaultAgentId(cfg)) return true;\n return listAgentEntries(cfg).some((e) => normalizeAgentId(e.id) === n);\n}\n\nfunction resolveEditorWorkspaceRoot(\n cfg: Config,\n agentIdRaw: string | undefined,\n): { ok: true; root: string } | { ok: false; message: string } {\n const trimmed = typeof agentIdRaw === 'string' ? agentIdRaw.trim() : '';\n if (!trimmed) {\n const root = getWorkspacePath(cfg);\n if (!root) return { ok: false, message: 'Workspace not configured' };\n return { ok: true, root };\n }\n const id = normalizeAgentId(trimmed);\n if (!isKnownEditorAgentId(cfg, id)) {\n return { ok: false, message: 'Unknown agent' };\n }\n return { ok: true, root: resolveAgentWorkspaceDir(cfg, id) };\n}\n\n/** Prefer `sessionKey` (per-session workspace override) over `agentId`. */\nasync function resolveEditorWorkspaceRootAsync(\n service: GatewayService,\n cfg: Config,\n sessionKeyRaw: string | undefined,\n agentIdRaw: string | undefined,\n): Promise<{ ok: true; root: string } | { ok: false; message: string }> {\n const sk = typeof sessionKeyRaw === 'string' ? sessionKeyRaw.trim() : '';\n if (sk) {\n try {\n const root = await service.sessions.getEffectiveWorkspacePath(sk);\n return { ok: true, root };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey: sk }, 'Session workspace root resolution failed');\n return { ok: false, message: em || 'Session workspace resolution failed' };\n }\n }\n return resolveEditorWorkspaceRoot(cfg, agentIdRaw);\n}\n\ninterface ResolvedWorkspaceImportConfig {\n targetDir: string;\n maxBytes: number;\n allowOverwrite: boolean;\n}\n\nfunction resolveWorkspaceImportConfig(cfg: Config): ResolvedWorkspaceImportConfig {\n const raw = cfg.workspace?.import;\n return {\n targetDir: raw?.targetDir?.trim() || 'imports',\n maxBytes: raw?.maxBytes ?? 104_857_600,\n allowOverwrite: raw?.allowOverwrite ?? true,\n };\n}\n\n/** Strip path separators, NULs and control chars from a basename so it stays in the destination dir. */\nfunction sanitizeImportBasename(name: string): string {\n return name\n .replace(/[\\\\/]/g, '')\n .replace(/[\\x00-\\x1f\\x7f]/g, '')\n .trim();\n}\n\n/**\n * Race-safe target picker for the `rename`-on-conflict strategy. Uses `link(tmp, target)`\n * which atomically fails with EEXIST when the candidate is taken; on success the tmp\n * file is left in place for the caller to unlink. Returns the linked target path.\n */\nasync function pickAvailableTargetWithLink(\n tmpAbs: string,\n initialDestAbs: string,\n maxAttempts = 1000,\n): Promise<{ ok: true; path: string; attempts: number } | { ok: false; attempts: number }> {\n const dir = dirname(initialDestAbs);\n const original = basename(initialDestAbs);\n const dotIdx = original.lastIndexOf('.');\n const stem = dotIdx > 0 ? original.slice(0, dotIdx) : original;\n const ext = dotIdx > 0 ? original.slice(dotIdx) : '';\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const candidate = attempt === 1 ? initialDestAbs : join(dir, `${stem}-${attempt}${ext}`);\n try {\n await link(tmpAbs, candidate);\n return { ok: true, path: candidate, attempts: attempt };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n }\n }\n return { ok: false, attempts: maxAttempts };\n}\n\nfunction fileReferenceCapabilities(\n scope: FileReferenceScope,\n isDirectory: boolean,\n locationKind?: FileReferenceLocationKind,\n): FileReferenceCapability[] {\n if (scope === 'workspace') {\n return isDirectory\n ? ['openExternal', 'revealInFolder', 'copyPath']\n : ['preview', 'edit', 'openExternal', 'revealInFolder', 'copyPath'];\n }\n if (scope === 'external' || scope === 'agent-profile' || scope === 'session-artifact') {\n const base: FileReferenceCapability[] = ['openExternal', 'revealInFolder', 'copyPath'];\n // v1: importToWorkspace for files only; exclude xopc-config to prevent copying\n // app config into the workspace (semantically wrong).\n if (!isDirectory && locationKind !== 'xopc-config') {\n base.push('importToWorkspace');\n }\n return base;\n }\n if (scope === 'missing') return ['copyPath'];\n return [];\n}\n\nfunction isFileReferenceAction(action: unknown): action is 'openExternal' | 'revealInFolder' {\n return action === 'openExternal' || action === 'revealInFolder';\n}\n\nexport function registerWorkspaceRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n authenticated.get('/api/workspace/heartbeat-md', async (c) => {\n const abs = resolveHeartbeatMdPath(service.currentConfig);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return c.json({ ok: true, payload: { content: content, file: 'HEARTBEAT.md' } });\n } catch {\n return c.json({ ok: true, payload: { content: '', file: 'HEARTBEAT.md' } });\n }\n });\n\n authenticated.put('/api/workspace/heartbeat-md', async (c) => {\n const abs = resolveHeartbeatMdPath(service.currentConfig);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n const content =\n typeof body === 'object' &&\n body !== null &&\n 'content' in body &&\n typeof (body as { content: unknown }).content === 'string'\n ? (body as { content: string }).content\n : '';\n try {\n await writeFile(abs, content, 'utf-8');\n return c.json({ ok: true, payload: { file: 'HEARTBEAT.md' } });\n } catch (err) {\n log.error({ err, path: abs }, 'Failed to write HEARTBEAT.md');\n return c.json({ ok: false, error: { message: 'Write failed' } }, 500);\n }\n });\n\n authenticated.get('/api/workspace/editor/list', async (c) => {\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const dirRel = typeof c.req.query('dir') === 'string' ? c.req.query('dir')! : '';\n const absDir = resolveWorkspaceSafePath(workspaceRoot, dirRel);\n if (!absDir) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(absDir);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isDirectory()) {\n return c.json({ ok: false, error: { message: 'Not a directory' } }, 400);\n }\n const dirents = await readdir(absDir, { withFileTypes: true });\n const entries: { name: string; path: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (const entry of dirents) {\n if (entry.name.startsWith('.')) continue;\n const fullPath = join(absDir, entry.name);\n if (entry.isDirectory()) {\n entries.push({\n name: entry.name,\n path: toWorkspaceRelativePosix(workspaceRoot, fullPath),\n absolutePath: fullPath,\n isDirectory: true,\n });\n } else {\n entries.push({\n name: entry.name,\n path: toWorkspaceRelativePosix(workspaceRoot, fullPath),\n absolutePath: fullPath,\n isDirectory: false,\n });\n }\n }\n entries.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n return c.json({ ok: true, payload: { entries } });\n });\n\n authenticated.get('/api/workspace/editor/read', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return c.json({\n ok: true,\n payload: {\n content,\n path: toWorkspaceRelativePosix(workspaceRoot, abs),\n absolutePath: abs,\n mtimeMs: st.mtimeMs,\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n /** Read file as raw bytes and return base64 (for PDF/images in workspace preview — avoids UTF-8 corruption). */\n authenticated.get('/api/workspace/editor/read-base64', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n const buf = await readFile(abs);\n return c.json({\n ok: true,\n payload: {\n contentBase64: buf.toString('base64'),\n path: toWorkspaceRelativePosix(workspaceRoot, abs),\n /** Host absolute path — Electron can open with the default app (shell.openPath). */\n absolutePath: abs,\n mtimeMs: st.mtimeMs,\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n /** Map an absolute host path to a workspace-relative path (if under this session’s workspace). */\n authenticated.get('/api/workspace/editor/resolve-path', async (c) => {\n const raw = c.req.query('absolutePath');\n if (!raw || typeof raw !== 'string' || !raw.trim()) {\n return c.json({ ok: false, error: { message: 'Missing absolutePath' } }, 400);\n }\n const absolutePath = raw.trim();\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const normalized = resolve(absolutePath);\n if (!isPathUnderWorkspace(workspaceRoot, normalized)) {\n return c.json({ ok: false, error: { message: 'Path not under workspace' } }, 403);\n }\n const rel = toWorkspaceRelativePosix(workspaceRoot, normalized);\n return c.json({ ok: true, payload: { workspaceRelativePath: rel } });\n });\n\n authenticated.get('/api/workspace/editor/resolve-reference', async (c) => {\n const rawPath = typeof c.req.query('path') === 'string' ? c.req.query('path')!.trim() : '';\n if (!rawPath) {\n return c.json({ ok: false, error: { code: 'INVALID_PATH', message: 'Missing path' } }, 400);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n sessionKey,\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { code: 'WORKSPACE_RESOLUTION_FAILED', message: ws.message } }, 400);\n }\n\n const workspaceRoot = ws.root;\n const classifierCtx = { ...buildFilePathClassifierContext(service.currentConfig, sessionKey), workspaceRoot };\n const displayName = displayNameForPath(rawPath);\n const { candidate, invalid } = await resolveFileReferenceCandidate(rawPath, workspaceRoot, classifierCtx);\n\n if (!candidate || invalid) {\n return c.json({\n ok: true,\n payload: {\n inputPath: rawPath,\n displayName,\n scope: 'invalid' satisfies FileReferenceScope,\n exists: false,\n capabilities: [] as FileReferenceCapability[],\n errorCode: 'INVALID_PATH',\n },\n });\n }\n\n let st: Awaited<ReturnType<typeof stat>> | null = null;\n try {\n st = await stat(candidate);\n } catch {\n st = null;\n }\n\n if (!st) {\n // Always include the resolved candidate so the UI's \"Copy path\" yields\n // something actionable (\"I looked here, no file\"). Without this, bare\n // workspace-relative mentions fall back to the `rel:<path>` UI sentinel.\n return c.json({\n ok: true,\n payload: {\n inputPath: rawPath,\n displayName,\n scope: 'missing' satisfies FileReferenceScope,\n exists: false,\n absolutePath: candidate,\n capabilities: fileReferenceCapabilities('missing', false),\n errorCode: 'FILE_NOT_FOUND',\n },\n });\n }\n\n const classified = classifyFileLocation(candidate, classifierCtx);\n const { scope, locationKind, manageRoute } = classified;\n const inWorkspace = scope === 'workspace';\n const isDirectory = st.isDirectory();\n const capabilities = fileReferenceCapabilities(scope, isDirectory, locationKind);\n const ref = fileReferenceRegistry.register({\n absolutePath: candidate,\n sessionKey: sessionKey || undefined,\n scope,\n locationKind,\n capabilities,\n });\n\n return c.json({\n ok: true,\n payload: {\n fileRefId: ref.id,\n inputPath: rawPath,\n displayName,\n scope,\n locationKind,\n manageRoute,\n exists: true,\n isDirectory,\n absolutePath: candidate,\n workspaceRelativePath: inWorkspace ? toWorkspaceRelativePosix(workspaceRoot, candidate) : undefined,\n capabilities,\n mtimeMs: st.mtimeMs,\n },\n });\n });\n\n authenticated.post('/api/workspace/file-ref/:id/resolve-action', async (c) => {\n const id = c.req.param('id')?.trim() ?? '';\n if (!id) {\n return c.json({ ok: false, error: { code: 'INVALID_FILE_REF', message: 'Missing file reference' } }, 400);\n }\n\n const ref = fileReferenceRegistry.resolve(id);\n if (!ref) {\n return c.json({ ok: false, error: { code: 'FILE_REF_EXPIRED', message: 'File reference expired' } }, 404);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n if (!fileRefSessionKeysMatch(ref.sessionKey, sessionKey)) {\n return c.json({ ok: false, error: { code: 'FILE_REF_FORBIDDEN', message: 'File reference forbidden' } }, 403);\n }\n\n const body = (await c.req.json().catch(() => ({}))) as { action?: unknown };\n const action = body.action;\n if (!isFileReferenceAction(action)) {\n return c.json({ ok: false, error: { code: 'INVALID_ACTION', message: 'Invalid action' } }, 400);\n }\n if (!ref.capabilities.includes(action)) {\n return c.json({ ok: false, error: { code: 'ACTION_NOT_ALLOWED', message: 'Action not allowed' } }, 403);\n }\n\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(ref.absolutePath);\n } catch {\n return c.json({ ok: false, error: { code: 'FILE_NOT_FOUND', message: 'File not found' } }, 404);\n }\n\n return c.json({\n ok: true,\n payload: {\n absolutePath: ref.absolutePath,\n isDirectory: st.isDirectory(),\n },\n });\n });\n\n authenticated.post('/api/workspace/import-file-ref/:id', async (c) => {\n const id = c.req.param('id')?.trim() ?? '';\n if (!id) {\n return c.json({ ok: false, error: { code: 'INVALID_FILE_REF', message: 'Missing file reference' } }, 400);\n }\n\n const ref = fileReferenceRegistry.resolve(id);\n if (!ref) {\n return c.json({ ok: false, error: { code: 'FILE_REF_EXPIRED', message: 'File reference expired' } }, 404);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n if (!fileRefSessionKeysMatch(ref.sessionKey, sessionKey)) {\n return c.json({ ok: false, error: { code: 'FILE_REF_FORBIDDEN', message: 'File reference forbidden' } }, 403);\n }\n\n if (!ref.capabilities.includes('importToWorkspace')) {\n return c.json({ ok: false, error: { code: 'IMPORT_NOT_ALLOWED', message: 'Import not allowed for this file' } }, 403);\n }\n\n let sourceStat: Awaited<ReturnType<typeof stat>>;\n try {\n sourceStat = await stat(ref.absolutePath);\n } catch {\n fileReferenceRegistry.expireById(id);\n return c.json({ ok: false, error: { code: 'SOURCE_NOT_FOUND', message: 'Source file no longer exists' } }, 404);\n }\n if (!sourceStat.isFile()) {\n return c.json({ ok: false, error: { code: 'SOURCE_NOT_FILE', message: 'Source is not a regular file' } }, 400);\n }\n\n const importCfg = resolveWorkspaceImportConfig(service.currentConfig);\n if (sourceStat.size > importCfg.maxBytes) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'SOURCE_TOO_LARGE',\n message: `Source exceeds maximum import size (${importCfg.maxBytes} bytes)`,\n },\n },\n 413,\n );\n }\n\n const ws = await resolveEditorWorkspaceRootAsync(service, service.currentConfig, sessionKey, undefined);\n if (ws.ok === false) {\n return c.json({ ok: false, error: { code: 'WORKSPACE_RESOLUTION_FAILED', message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n\n let body: { destination?: unknown; onConflict?: unknown };\n try {\n body = (await c.req.json().catch(() => ({}))) as typeof body;\n } catch {\n body = {};\n }\n const requestedDestRaw = typeof body.destination === 'string' ? body.destination.trim() : '';\n const onConflictRaw = typeof body.onConflict === 'string' ? body.onConflict : 'rename';\n if (onConflictRaw !== 'rename' && onConflictRaw !== 'overwrite' && onConflictRaw !== 'error') {\n return c.json({ ok: false, error: { code: 'INVALID_CONFLICT_MODE', message: 'Invalid onConflict value' } }, 400);\n }\n const onConflict = onConflictRaw as 'rename' | 'overwrite' | 'error';\n if (onConflict === 'overwrite' && !importCfg.allowOverwrite) {\n return c.json({ ok: false, error: { code: 'OVERWRITE_DISABLED', message: 'Overwrite is disabled by config' } }, 403);\n }\n\n const sourceBasename = sanitizeImportBasename(basename(ref.absolutePath)) || 'imported-file';\n let requestedRel: string;\n if (!requestedDestRaw) {\n requestedRel = `${importCfg.targetDir}/${sourceBasename}`;\n } else {\n const trimmedDest = requestedDestRaw.replace(/\\\\/g, '/');\n // Path ending with `/` is treated as a directory; append source basename.\n requestedRel = trimmedDest.endsWith('/') ? `${trimmedDest}${sourceBasename}` : trimmedDest;\n }\n\n let initialDestAbs = resolveWorkspaceSafePath(workspaceRoot, requestedRel);\n if (!initialDestAbs) {\n return c.json({ ok: false, error: { code: 'INVALID_DESTINATION', message: 'Invalid destination path' } }, 400);\n }\n\n // Sandbox: blocks `.xopc/xopc.json`, `.env*`, etc.; canonical symlink resolution included.\n const writePolicy = validateWritePath(initialDestAbs, workspaceRoot);\n if (!writePolicy.allowed) {\n return c.json({ ok: false, error: { code: 'DESTINATION_BLOCKED', message: writePolicy.reason ?? 'Destination blocked' } }, 403);\n }\n\n if (resolve(ref.absolutePath) === resolve(initialDestAbs)) {\n return c.json({ ok: false, error: { code: 'SAME_LOCATION', message: 'Destination is the same as source' } }, 400);\n }\n\n const destDir = dirname(initialDestAbs);\n try {\n await mkdir(destDir, { recursive: true });\n } catch (err) {\n log.warn({ err, destDir }, 'Failed to create import destination directory');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to prepare destination' } }, 500);\n }\n\n // Stage source into a hidden tmp file inside the destination directory so we\n // can atomically `link` (rename strategy) or `rename` (overwrite) to land it.\n const tmpName = `.${basename(initialDestAbs)}.import-${randomUUID()}.tmp`;\n const tmpAbs = join(destDir, tmpName);\n\n const started = Date.now();\n let renamed = false;\n let overwrote = false;\n let finalDestAbs = initialDestAbs;\n\n try {\n try {\n await copyFile(ref.absolutePath, tmpAbs, fsConstants.COPYFILE_FICLONE);\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, source: ref.absolutePath, tmpAbs }, 'Failed to copy source into staging tmp');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to copy source file' } }, 500);\n }\n\n if (onConflict === 'overwrite') {\n // Snapshot pre-rename existence for telemetry; the actual overwrite is unconditional.\n overwrote = await stat(initialDestAbs).then(() => true).catch(() => false);\n try {\n await rename(tmpAbs, initialDestAbs);\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, target: initialDestAbs }, 'Atomic rename failed');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to finalize import' } }, 500);\n }\n } else if (onConflict === 'error') {\n const exists = await stat(initialDestAbs).then(() => true).catch(() => false);\n if (exists) {\n await unlink(tmpAbs).catch(() => {});\n return c.json({ ok: false, error: { code: 'DESTINATION_EXISTS', message: 'Destination already exists' } }, 409);\n }\n try {\n await link(tmpAbs, initialDestAbs);\n await unlink(tmpAbs).catch(() => {});\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, target: initialDestAbs }, 'Hard link to destination failed');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to finalize import' } }, 500);\n }\n } else {\n // rename: race-safe loop using O_EXCL semantics of `link`.\n const picked = await pickAvailableTargetWithLink(tmpAbs, initialDestAbs);\n if (!picked.ok) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ target: initialDestAbs, attempts: picked.attempts }, 'Failed to find free import target name');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'No free destination name available' } }, 500);\n }\n await unlink(tmpAbs).catch(() => {});\n finalDestAbs = picked.path;\n renamed = picked.path !== initialDestAbs;\n }\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.error({ err }, 'Import file unexpected failure');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Import failed' } }, 500);\n }\n\n let finalMtime: number;\n try {\n finalMtime = (await stat(finalDestAbs)).mtimeMs;\n } catch {\n finalMtime = Date.now();\n }\n\n const workspaceRel = toWorkspaceRelativePosix(workspaceRoot, finalDestAbs);\n\n fileReferenceRegistry.expireById(id);\n const newRef = fileReferenceRegistry.register({\n absolutePath: finalDestAbs,\n sessionKey: sessionKey || undefined,\n scope: 'workspace',\n capabilities: fileReferenceCapabilities('workspace', false),\n });\n\n const sourceScope = ref.scope;\n const sourceLocationKind = ref.locationKind;\n\n service.emit('workspace.file-imported', {\n sessionKey: sessionKey || undefined,\n workspaceRelativePath: workspaceRel,\n absolutePath: finalDestAbs,\n bytes: sourceStat.size,\n sourceScope,\n sourceLocationKind,\n });\n\n log.info(\n {\n sessionKey,\n fileRefId: id,\n sourceAbsolutePath: ref.absolutePath,\n sourceScope,\n sourceLocationKind,\n destWorkspaceRelativePath: workspaceRel,\n bytes: sourceStat.size,\n renamed,\n overwrote,\n durationMs: Date.now() - started,\n },\n 'Workspace file import succeeded',\n );\n\n return c.json({\n ok: true,\n payload: {\n workspaceRelativePath: workspaceRel,\n absolutePath: finalDestAbs,\n bytesCopied: sourceStat.size,\n sourceAbsolutePath: ref.absolutePath,\n sourceScope,\n sourceLocationKind,\n renamed,\n overwrote,\n mtimeMs: finalMtime,\n newFileRefId: newRef.id,\n },\n });\n });\n\n /**\n * Serve a workspace file as raw bytes (e.g. <img> after auth fetch + blob URL).\n * Path is workspace-relative; scope via sessionKey / agentId like other editor routes.\n */\n authenticated.get('/api/workspace/editor/raw', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n const ext = pathRel.split('.').pop()?.toLowerCase() ?? '';\n const mimeByExt: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n svg: 'image/svg+xml',\n pdf: 'application/pdf',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n txt: 'text/plain',\n md: 'text/markdown',\n json: 'application/json',\n html: 'text/html',\n css: 'text/css',\n js: 'text/javascript',\n ts: 'text/typescript',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n webm: 'video/webm',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n };\n const contentType = mimeByExt[ext] || 'application/octet-stream';\n try {\n const buf = await readFile(abs);\n return new Response(buf, {\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'private, max-age=3600',\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n authenticated.put('/api/workspace/editor/write', async (c) => {\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n const pathRel =\n typeof body === 'object' &&\n body !== null &&\n 'path' in body &&\n typeof (body as { path: unknown }).path === 'string'\n ? (body as { path: string }).path\n : '';\n const content =\n typeof body === 'object' &&\n body !== null &&\n 'content' in body &&\n typeof (body as { content: unknown }).content === 'string'\n ? (body as { content: string }).content\n : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>> | undefined;\n try {\n st = await stat(abs);\n } catch {\n st = undefined;\n }\n if (st && !st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n await writeFile(abs, content, 'utf-8');\n let mtimeMs: number;\n try {\n mtimeMs = (await stat(abs)).mtimeMs;\n } catch {\n mtimeMs = Date.now();\n }\n return c.json({\n ok: true,\n payload: { path: toWorkspaceRelativePosix(workspaceRoot, abs), mtimeMs },\n });\n } catch (err) {\n log.error({ err, path: abs }, 'workspace editor write failed');\n return c.json({ ok: false, error: { message: 'Write failed' } }, 500);\n }\n });\n\n authenticated.get('/api/workspace/editor/search', async (c) => {\n const q = typeof c.req.query('q') === 'string' ? c.req.query('q')!.trim() : '';\n const dirRel = typeof c.req.query('dir') === 'string' ? c.req.query('dir')! : '';\n if (!q) {\n return c.json({\n ok: true,\n payload: { results: [] as { filePath: string; lineNumber: number; lineContent: string; matchStart: number; matchEnd: number }[] },\n });\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const absDir = resolveWorkspaceSafePath(workspaceRoot, dirRel);\n if (!absDir) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(absDir);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isDirectory()) {\n return c.json({ ok: false, error: { message: 'Not a directory' } }, 400);\n }\n const raw = await runRipgrepInDirectory(q, absDir);\n const results = raw\n .filter((r) => isPathUnderWorkspace(workspaceRoot, r.filePath))\n .map((r) => ({\n ...r,\n filePath: toWorkspaceRelativePosix(workspaceRoot, resolve(r.filePath)),\n }));\n return c.json({ ok: true, payload: { results } });\n });\n\n /** Fuzzy filename / path search over the session workspace (ripgrep `--files` + subsequence scoring). */\n authenticated.get('/api/workspace/editor/files/search', async (c) => {\n const q = typeof c.req.query('q') === 'string' ? c.req.query('q')!.trim() : '';\n const limitRaw = c.req.query('limit');\n const limit = Math.min(\n Math.max(parseInt(typeof limitRaw === 'string' ? limitRaw : '15', 10) || 15, 1),\n FILE_SEARCH_MAX_LIMIT,\n );\n\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n\n const entries = await fuzzySearchWorkspaceFiles(ws.root, q, limit);\n return c.json({ ok: true, payload: { entries } });\n });\n}\n"],"mappings":"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"}