@xopcai/xopc 0.0.6 → 0.0.11

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 (412) hide show
  1. package/dist/extensions/weixin/src/api/api.js +1 -1
  2. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  3. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  4. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  5. package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js → agents-BdC4Y-HX.js} +2 -2
  6. package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
  7. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js → apps-page-C-oaSHkm.js} +2 -2
  8. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js.map → apps-page-C-oaSHkm.js.map} +1 -1
  9. package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
  10. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js → channels-settings-BqEUppPO.js} +2 -2
  11. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js.map → channels-settings-BqEUppPO.js.map} +1 -1
  12. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js → chat-agents-api-BhqjQ7iL.js} +2 -2
  13. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
  14. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js → cron-page-Cli49RKR.js} +2 -2
  15. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js.map → cron-page-Cli49RKR.js.map} +1 -1
  16. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js → cron-utils-Dkj-Ldpf.js} +2 -2
  17. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
  18. package/dist/gateway/static/root/assets/electron-env-BDtJw9AY.js +2 -0
  19. package/dist/gateway/static/root/assets/electron-env-BDtJw9AY.js.map +1 -0
  20. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js → extension-debug-page-BMcZlaxF.js} +2 -2
  21. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js → extension-iframe-host-D5HEF0KR.js} +2 -2
  23. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-CXdCSSPl.js} +2 -2
  25. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js.map → extension-page-CXdCSSPl.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js → extension-provider-DZCZgQE2.js} +2 -2
  27. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js → extension-settings-page-CX6STpx3.js} +2 -2
  29. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js → gateway-config-swr-Cph02QZn.js} +2 -2
  31. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
  33. package/dist/gateway/static/root/assets/index-iTUyfzNr.js +16 -0
  34. package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
  35. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js → logs-page-B9O5l3I8.js} +2 -2
  36. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js.map → logs-page-B9O5l3I8.js.map} +1 -1
  37. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js → model-selector-BLiY_O25.js} +2 -2
  38. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js.map → model-selector-BLiY_O25.js.map} +1 -1
  39. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js +2 -0
  40. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js.map +1 -0
  41. package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
  42. package/dist/gateway/static/root/assets/{page-header-store-HcRZK5CZ.js.map → page-header-store-BFpnFTed.js.map} +1 -1
  43. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js → session-api-DEhQXWJg.js} +2 -2
  44. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js.map → session-api-DEhQXWJg.js.map} +1 -1
  45. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js → session-working-directory-control-DKOtWs3-.js} +3 -3
  46. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
  47. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js → sessions-page-BYlWP1ep.js} +2 -2
  48. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
  49. package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js +2 -0
  50. package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
  51. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js → skill-api-DWrn8Az0.js} +2 -2
  52. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js.map → skill-api-DWrn8Az0.js.map} +1 -1
  53. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js → skills-page-C59WQpM1.js} +2 -2
  54. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js.map → skills-page-C59WQpM1.js.map} +1 -1
  55. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js → theme-store-CywXkKml.js} +2 -2
  56. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js.map → theme-store-CywXkKml.js.map} +1 -1
  57. package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
  58. package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
  59. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js → useTranslation-CACj0DBJ.js} +2 -2
  60. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
  61. package/dist/gateway/static/root/index.html +16 -16
  62. package/dist/package.js +1 -1
  63. package/dist/src/agent/agent-manager.d.ts +1 -0
  64. package/dist/src/agent/agent-manager.js +17 -9
  65. package/dist/src/agent/agent-manager.js.map +1 -1
  66. package/dist/src/agent/background-review/run-background-review.js +2 -0
  67. package/dist/src/agent/background-review/run-background-review.js.map +1 -1
  68. package/dist/src/agent/child-agent-factory.js +2 -0
  69. package/dist/src/agent/child-agent-factory.js.map +1 -1
  70. package/dist/src/agent/context/expand-at-file-mentions.d.ts +4 -0
  71. package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
  72. package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
  73. package/dist/src/agent/context/workspace-seed.js +1 -1
  74. package/dist/src/agent/image/index.d.ts +0 -1
  75. package/dist/src/agent/image/index.js +1 -2
  76. package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
  77. package/dist/src/agent/ipc/inbox.js +1 -1
  78. package/dist/src/agent/ipc/socket.js +1 -1
  79. package/dist/src/agent/memory/compaction.d.ts +1 -1
  80. package/dist/src/agent/memory/compaction.js +38 -11
  81. package/dist/src/agent/memory/compaction.js.map +1 -1
  82. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  83. package/dist/src/agent/messaging/command-handler.d.ts +13 -0
  84. package/dist/src/agent/messaging/command-handler.js +14 -2
  85. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  86. package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
  87. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  88. package/dist/src/agent/service.d.ts +16 -1
  89. package/dist/src/agent/service.js +175 -17
  90. package/dist/src/agent/service.js.map +1 -1
  91. package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
  92. package/dist/src/agent/skills/hub-hash.js +1 -1
  93. package/dist/src/agent/skills/hub-pull.js +1 -1
  94. package/dist/src/agent/skills/scanner.js +1 -1
  95. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  96. package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
  97. package/dist/src/agent/tools/browser/tools.js +2 -2
  98. package/dist/src/agent/tools/browser/tools.js.map +1 -1
  99. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  100. package/dist/src/agent/tools/image-tool.js +2 -2
  101. package/dist/src/agent/tools/image-tool.js.map +1 -1
  102. package/dist/src/agent/tools/index.d.ts +1 -1
  103. package/dist/src/agent/tools/index.js +2 -2
  104. package/dist/src/agent/tools/read.d.ts +0 -2
  105. package/dist/src/agent/tools/read.js +1 -3
  106. package/dist/src/agent/tools/read.js.map +1 -1
  107. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  108. package/dist/src/agent/tools/write.js +1 -1
  109. package/dist/src/auth/credentials.js +2 -2
  110. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  111. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  112. package/dist/src/channels/index.d.ts +1 -1
  113. package/dist/src/channels/index.js +2 -2
  114. package/dist/src/channels/pipeline.d.ts +8 -1
  115. package/dist/src/channels/pipeline.js +49 -4
  116. package/dist/src/channels/pipeline.js.map +1 -1
  117. package/dist/src/channels/plugin-types.d.ts +14 -0
  118. package/dist/src/chat-commands/builtins/config.d.ts +4 -0
  119. package/dist/src/chat-commands/builtins/config.js +197 -0
  120. package/dist/src/chat-commands/builtins/config.js.map +1 -0
  121. package/dist/src/chat-commands/builtins/context.d.ts +4 -0
  122. package/dist/src/chat-commands/builtins/context.js +44 -0
  123. package/dist/src/chat-commands/builtins/context.js.map +1 -0
  124. package/dist/src/chat-commands/builtins/session.js +111 -0
  125. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  126. package/dist/src/chat-commands/builtins/thinking.js +49 -21
  127. package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
  128. package/dist/src/chat-commands/config-paths.d.ts +10 -0
  129. package/dist/src/chat-commands/config-paths.js +45 -0
  130. package/dist/src/chat-commands/config-paths.js.map +1 -0
  131. package/dist/src/chat-commands/config-value.d.ts +12 -0
  132. package/dist/src/chat-commands/config-value.js +53 -0
  133. package/dist/src/chat-commands/config-value.js.map +1 -0
  134. package/dist/src/chat-commands/context.d.ts +24 -1
  135. package/dist/src/chat-commands/context.js +41 -0
  136. package/dist/src/chat-commands/context.js.map +1 -1
  137. package/dist/src/chat-commands/index.d.ts +2 -0
  138. package/dist/src/chat-commands/index.js +5 -1
  139. package/dist/src/chat-commands/index.js.map +1 -1
  140. package/dist/src/chat-commands/types.d.ts +33 -1
  141. package/dist/src/cli/commands/agent/interactive.js +1 -1
  142. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  143. package/dist/src/cli/commands/agent.js +21 -9
  144. package/dist/src/cli/commands/agent.js.map +1 -1
  145. package/dist/src/cli/commands/auth.js.map +1 -1
  146. package/dist/src/cli/commands/doctor/checks/channel-config.d.ts +2 -0
  147. package/dist/src/cli/commands/doctor/checks/channel-config.js +113 -0
  148. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -0
  149. package/dist/src/cli/commands/doctor/checks/channel-plugins.d.ts +2 -0
  150. package/dist/src/cli/commands/doctor/checks/channel-plugins.js +47 -0
  151. package/dist/src/cli/commands/doctor/checks/channel-plugins.js.map +1 -0
  152. package/dist/src/cli/commands/doctor/checks/config-health.d.ts +2 -0
  153. package/dist/src/cli/commands/doctor/checks/config-health.js +82 -0
  154. package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -0
  155. package/dist/src/cli/commands/doctor/checks/cron-health.d.ts +2 -0
  156. package/dist/src/cli/commands/doctor/checks/cron-health.js +116 -0
  157. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -0
  158. package/dist/src/cli/commands/doctor/checks/gateway-health.d.ts +2 -0
  159. package/dist/src/cli/commands/doctor/checks/gateway-health.js +64 -0
  160. package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -0
  161. package/dist/src/cli/commands/doctor/checks/gateway-service.d.ts +2 -0
  162. package/dist/src/cli/commands/doctor/checks/gateway-service.js +64 -0
  163. package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -0
  164. package/dist/src/cli/commands/doctor/checks/node-version.d.ts +2 -0
  165. package/dist/src/cli/commands/doctor/checks/node-version.js +33 -0
  166. package/dist/src/cli/commands/doctor/checks/node-version.js.map +1 -0
  167. package/dist/src/cli/commands/doctor/checks/provider-auth.d.ts +2 -0
  168. package/dist/src/cli/commands/doctor/checks/provider-auth.js +91 -0
  169. package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -0
  170. package/dist/src/cli/commands/doctor/checks/security-audit.d.ts +2 -0
  171. package/dist/src/cli/commands/doctor/checks/security-audit.js +85 -0
  172. package/dist/src/cli/commands/doctor/checks/security-audit.js.map +1 -0
  173. package/dist/src/cli/commands/doctor/checks/session-integrity.d.ts +2 -0
  174. package/dist/src/cli/commands/doctor/checks/session-integrity.js +118 -0
  175. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -0
  176. package/dist/src/cli/commands/doctor/checks/state-integrity.d.ts +2 -0
  177. package/dist/src/cli/commands/doctor/checks/state-integrity.js +99 -0
  178. package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -0
  179. package/dist/src/cli/commands/doctor/checks/version-check.d.ts +2 -0
  180. package/dist/src/cli/commands/doctor/checks/version-check.js +71 -0
  181. package/dist/src/cli/commands/doctor/checks/version-check.js.map +1 -0
  182. package/dist/src/cli/commands/doctor/checks/workspace-status.d.ts +2 -0
  183. package/dist/src/cli/commands/doctor/checks/workspace-status.js +73 -0
  184. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -0
  185. package/dist/src/cli/commands/doctor/flow.d.ts +9 -0
  186. package/dist/src/cli/commands/doctor/flow.js +51 -0
  187. package/dist/src/cli/commands/doctor/flow.js.map +1 -0
  188. package/dist/src/cli/commands/doctor/format.d.ts +6 -0
  189. package/dist/src/cli/commands/doctor/format.js +61 -0
  190. package/dist/src/cli/commands/doctor/format.js.map +1 -0
  191. package/dist/src/cli/commands/doctor/index.js +44 -0
  192. package/dist/src/cli/commands/doctor/index.js.map +1 -0
  193. package/dist/src/cli/commands/doctor/types.d.ts +20 -0
  194. package/dist/src/cli/commands/doctor/types.js +1 -0
  195. package/dist/src/cli/commands/extension.js +10 -0
  196. package/dist/src/cli/commands/extension.js.map +1 -1
  197. package/dist/src/cli/commands/init.js +1 -2
  198. package/dist/src/cli/commands/init.js.map +1 -1
  199. package/dist/src/cli/commands/session/utils.js.map +1 -1
  200. package/dist/src/cli/commands/update.d.ts +1 -0
  201. package/dist/src/cli/commands/update.js +171 -0
  202. package/dist/src/cli/commands/update.js.map +1 -0
  203. package/dist/src/cli/index.d.ts +2 -2
  204. package/dist/src/cli/index.js +4 -2
  205. package/dist/src/cli/index.js.map +1 -1
  206. package/dist/src/cli/utils/init-workspace.js +1 -1
  207. package/dist/src/config/index.d.ts +1 -0
  208. package/dist/src/config/index.js +4 -3
  209. package/dist/src/config/index.js.map +1 -1
  210. package/dist/src/config/loader.js +1 -1
  211. package/dist/src/config/models-json.d.ts +15 -15
  212. package/dist/src/config/paths.js.map +1 -1
  213. package/dist/src/config/profile.js +1 -1
  214. package/dist/src/config/runtime-overrides.d.ts +8 -0
  215. package/dist/src/config/runtime-overrides.js +40 -0
  216. package/dist/src/config/runtime-overrides.js.map +1 -0
  217. package/dist/src/config/schema.d.ts +34 -104
  218. package/dist/src/config/schema.js +18 -39
  219. package/dist/src/config/schema.js.map +1 -1
  220. package/dist/src/cron/persistence.js +1 -1
  221. package/dist/src/cron/run-log-store.js +1 -1
  222. package/dist/src/daemon/launchd.js +2 -2
  223. package/dist/src/daemon/launchd.js.map +1 -1
  224. package/dist/src/daemon/systemd.js +2 -2
  225. package/dist/src/daemon/systemd.js.map +1 -1
  226. package/dist/src/extensions/health.js +1 -1
  227. package/dist/src/extensions/loader.d.ts +1 -1
  228. package/dist/src/extensions/loader.js +5 -8
  229. package/dist/src/extensions/loader.js.map +1 -1
  230. package/dist/src/extensions/lockfile.js +1 -1
  231. package/dist/src/extensions/sdk/index.js +6 -1
  232. package/dist/src/extensions/sdk/index.js.map +1 -0
  233. package/dist/src/gateway/agents-admin.js +1 -1
  234. package/dist/src/gateway/agents-admin.js.map +1 -1
  235. package/dist/src/gateway/hono/lib/static-ui.js +1 -1
  236. package/dist/src/gateway/hono/oauth.js +1 -1
  237. package/dist/src/gateway/hono/routes/config.js +1 -1
  238. package/dist/src/gateway/hono/routes/doctor.d.ts +3 -0
  239. package/dist/src/gateway/hono/routes/doctor.js +35 -0
  240. package/dist/src/gateway/hono/routes/doctor.js.map +1 -0
  241. package/dist/src/gateway/hono/routes/index.js +4 -0
  242. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  243. package/dist/src/gateway/hono/routes/models.js +64 -11
  244. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  245. package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
  246. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  247. package/dist/src/gateway/hono/routes/update.d.ts +3 -0
  248. package/dist/src/gateway/hono/routes/update.js +141 -0
  249. package/dist/src/gateway/hono/routes/update.js.map +1 -0
  250. package/dist/src/gateway/hono/routes/workspace.js +82 -2
  251. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  252. package/dist/src/gateway/lock.js +1 -1
  253. package/dist/src/gateway/ports.js +98 -3
  254. package/dist/src/gateway/ports.js.map +1 -1
  255. package/dist/src/gateway/service.d.ts +1 -4
  256. package/dist/src/gateway/service.js +13 -20
  257. package/dist/src/gateway/service.js.map +1 -1
  258. package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
  259. package/dist/src/gateway/workspace-fs-file-list.js +56 -0
  260. package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
  261. package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
  262. package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
  263. package/dist/src/gateway/workspace-ripgrep.js +88 -4
  264. package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
  265. package/dist/src/infra/update-channels.d.ts +14 -0
  266. package/dist/src/infra/update-channels.js +30 -0
  267. package/dist/src/infra/update-channels.js.map +1 -0
  268. package/dist/src/infra/update-check.d.ts +53 -0
  269. package/dist/src/infra/update-check.js +155 -0
  270. package/dist/src/infra/update-check.js.map +1 -0
  271. package/dist/src/infra/update-runner.d.ts +18 -0
  272. package/dist/src/infra/update-runner.js +112 -0
  273. package/dist/src/infra/update-runner.js.map +1 -0
  274. package/dist/src/infra/update-startup.d.ts +20 -0
  275. package/dist/src/infra/update-startup.js +246 -0
  276. package/dist/src/infra/update-startup.js.map +1 -0
  277. package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
  278. package/dist/src/providers/extension-stream-bridge.js +239 -0
  279. package/dist/src/providers/extension-stream-bridge.js.map +1 -0
  280. package/dist/src/providers/index.d.ts +7 -2
  281. package/dist/src/providers/index.js +77 -14
  282. package/dist/src/providers/index.js.map +1 -1
  283. package/dist/src/providers/model-registry.js +1 -1
  284. package/dist/src/providers/plugin-registry.js +92 -87
  285. package/dist/src/providers/plugin-registry.js.map +1 -1
  286. package/dist/src/routing/bindings.js +1 -1
  287. package/dist/src/routing/index.d.ts +1 -1
  288. package/dist/src/routing/index.js +2 -2
  289. package/dist/src/routing/index.js.map +1 -1
  290. package/dist/src/routing/resolve-route.js +1 -1
  291. package/dist/src/routing/session-key.d.ts +0 -5
  292. package/dist/src/routing/session-key.js +1 -27
  293. package/dist/src/routing/session-key.js.map +1 -1
  294. package/dist/src/session/chat-export.d.ts +5 -0
  295. package/dist/src/session/chat-export.js +35 -0
  296. package/dist/src/session/chat-export.js.map +1 -0
  297. package/dist/src/session/config-store.js +1 -1
  298. package/dist/src/session/manager.d.ts +1 -1
  299. package/dist/src/session/manager.js +2 -2
  300. package/dist/src/session/manager.js.map +1 -1
  301. package/dist/src/session/store.d.ts +1 -1
  302. package/dist/src/session/store.js +3 -7
  303. package/dist/src/session/store.js.map +1 -1
  304. package/dist/src/session/types.d.ts +0 -10
  305. package/dist/src/session/types.js.map +1 -1
  306. package/dist/src/utils/logger/audit.js +1 -1
  307. package/dist/src/utils/logger/log-store.js +1 -1
  308. package/dist/src/utils/logger/rotation.js +1 -1
  309. package/dist/src/voice/tts/audio.js +1 -1
  310. package/package.json +2 -2
  311. package/dist/gateway/static/root/assets/agents-B6s2BvpH.js.map +0 -1
  312. package/dist/gateway/static/root/assets/attachment-load-6pRlDPZ8.js +0 -1
  313. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js +0 -16
  314. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js.map +0 -1
  315. package/dist/gateway/static/root/assets/index-KsVMH-Jo.css +0 -2
  316. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js +0 -2
  317. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js.map +0 -1
  318. package/dist/gateway/static/root/assets/page-header-store-HcRZK5CZ.js +0 -2
  319. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js +0 -2
  320. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js.map +0 -1
  321. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js +0 -2
  322. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js.map +0 -1
  323. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js +0 -2
  324. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js.map +0 -1
  325. package/dist/src/acp/commands.d.ts +0 -11
  326. package/dist/src/acp/commands.js +0 -17
  327. package/dist/src/acp/commands.js.map +0 -1
  328. package/dist/src/acp/control-plane/identity-reconcile.d.ts +0 -36
  329. package/dist/src/acp/control-plane/identity-reconcile.js +0 -124
  330. package/dist/src/acp/control-plane/identity-reconcile.js.map +0 -1
  331. package/dist/src/acp/control-plane/index.d.ts +0 -10
  332. package/dist/src/acp/control-plane/index.js +0 -6
  333. package/dist/src/acp/control-plane/manager.d.ts +0 -86
  334. package/dist/src/acp/control-plane/manager.js +0 -502
  335. package/dist/src/acp/control-plane/manager.js.map +0 -1
  336. package/dist/src/acp/control-plane/manager.types.d.ts +0 -125
  337. package/dist/src/acp/control-plane/manager.types.js +0 -14
  338. package/dist/src/acp/control-plane/manager.types.js.map +0 -1
  339. package/dist/src/acp/control-plane/manager.utils.d.ts +0 -29
  340. package/dist/src/acp/control-plane/manager.utils.js +0 -46
  341. package/dist/src/acp/control-plane/manager.utils.js.map +0 -1
  342. package/dist/src/acp/control-plane/runtime-cache-manager.d.ts +0 -49
  343. package/dist/src/acp/control-plane/runtime-cache-manager.js +0 -155
  344. package/dist/src/acp/control-plane/runtime-cache-manager.js.map +0 -1
  345. package/dist/src/acp/control-plane/runtime-cache.d.ts +0 -45
  346. package/dist/src/acp/control-plane/runtime-cache.js +0 -58
  347. package/dist/src/acp/control-plane/runtime-cache.js.map +0 -1
  348. package/dist/src/acp/control-plane/runtime-options.d.ts +0 -30
  349. package/dist/src/acp/control-plane/runtime-options.js +0 -92
  350. package/dist/src/acp/control-plane/runtime-options.js.map +0 -1
  351. package/dist/src/acp/control-plane/session-actor-queue.d.ts +0 -22
  352. package/dist/src/acp/control-plane/session-actor-queue.js +0 -70
  353. package/dist/src/acp/control-plane/session-actor-queue.js.map +0 -1
  354. package/dist/src/acp/control-plane/session-lifecycle-manager.d.ts +0 -59
  355. package/dist/src/acp/control-plane/session-lifecycle-manager.js +0 -209
  356. package/dist/src/acp/control-plane/session-lifecycle-manager.js.map +0 -1
  357. package/dist/src/acp/control-plane/session-store.d.ts +0 -39
  358. package/dist/src/acp/control-plane/session-store.js +0 -149
  359. package/dist/src/acp/control-plane/session-store.js.map +0 -1
  360. package/dist/src/acp/control-plane/turn-manager.d.ts +0 -40
  361. package/dist/src/acp/control-plane/turn-manager.js +0 -134
  362. package/dist/src/acp/control-plane/turn-manager.js.map +0 -1
  363. package/dist/src/acp/event-mapper.d.ts +0 -48
  364. package/dist/src/acp/event-mapper.js +0 -94
  365. package/dist/src/acp/event-mapper.js.map +0 -1
  366. package/dist/src/acp/index.d.ts +0 -10
  367. package/dist/src/acp/index.js +0 -5
  368. package/dist/src/acp/meta.d.ts +0 -15
  369. package/dist/src/acp/meta.js +0 -36
  370. package/dist/src/acp/meta.js.map +0 -1
  371. package/dist/src/acp/routing-integration.d.ts +0 -37
  372. package/dist/src/acp/routing-integration.js +0 -58
  373. package/dist/src/acp/routing-integration.js.map +0 -1
  374. package/dist/src/acp/runtime/backends/index.d.ts +0 -4
  375. package/dist/src/acp/runtime/backends/index.js +0 -2
  376. package/dist/src/acp/runtime/backends/local.d.ts +0 -136
  377. package/dist/src/acp/runtime/backends/local.js +0 -603
  378. package/dist/src/acp/runtime/backends/local.js.map +0 -1
  379. package/dist/src/acp/runtime/error-text.d.ts +0 -16
  380. package/dist/src/acp/runtime/error-text.js +0 -40
  381. package/dist/src/acp/runtime/error-text.js.map +0 -1
  382. package/dist/src/acp/runtime/errors.d.ts +0 -31
  383. package/dist/src/acp/runtime/errors.js +0 -47
  384. package/dist/src/acp/runtime/errors.js.map +0 -1
  385. package/dist/src/acp/runtime/index.d.ts +0 -7
  386. package/dist/src/acp/runtime/index.js +0 -4
  387. package/dist/src/acp/runtime/registry.d.ts +0 -35
  388. package/dist/src/acp/runtime/registry.js +0 -85
  389. package/dist/src/acp/runtime/registry.js.map +0 -1
  390. package/dist/src/acp/runtime/session-identity.d.ts +0 -35
  391. package/dist/src/acp/runtime/session-identity.js +0 -134
  392. package/dist/src/acp/runtime/session-identity.js.map +0 -1
  393. package/dist/src/acp/runtime/types.d.ts +0 -214
  394. package/dist/src/acp/secret-file.d.ts +0 -7
  395. package/dist/src/acp/secret-file.js +0 -19
  396. package/dist/src/acp/secret-file.js.map +0 -1
  397. package/dist/src/acp/server.d.ts +0 -48
  398. package/dist/src/acp/server.js +0 -300
  399. package/dist/src/acp/server.js.map +0 -1
  400. package/dist/src/acp/session.d.ts +0 -29
  401. package/dist/src/acp/session.js +0 -30
  402. package/dist/src/acp/session.js.map +0 -1
  403. package/dist/src/acp/types.d.ts +0 -39
  404. package/dist/src/acp/types.js +0 -13
  405. package/dist/src/acp/types.js.map +0 -1
  406. package/dist/src/agent/image/describe-images.d.ts +0 -18
  407. package/dist/src/agent/image/describe-images.js +0 -19
  408. package/dist/src/agent/image/describe-images.js.map +0 -1
  409. package/dist/src/cli/commands/acp.d.ts +0 -4
  410. package/dist/src/cli/commands/acp.js +0 -200
  411. package/dist/src/cli/commands/acp.js.map +0 -1
  412. /package/dist/src/{acp/runtime/types.js → cli/commands/doctor/index.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"child-agent-factory.js","names":[],"sources":["../../../src/agent/child-agent-factory.ts"],"sourcesContent":["import { Agent, type ThinkingLevel } from '@mariozechner/pi-agent-core';\nimport type { Api, Model } from '@mariozechner/pi-ai';\n\nimport type { Config } from '../config/schema.js';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport { resolveProviderApiKeySync } from '../auth/sync-provider-auth.js';\nimport { getApiKeySync } from '../providers/index.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { extractTextContent } from './context/workspace.js';\nimport {\n resolveAgentTurnTimeoutMs,\n runAgentTurnWithTimeout,\n} from './orchestration/run-agent-turn-with-timeout.js';\nimport type { ToolExecutorConfig } from './tools/executor.js';\nimport { AgentToolsFactory } from './tools/factory.js';\n\nconst log = createLogger('delegate-child');\n\nexport function buildChildSystemPrompt(goal: string, context?: string, workspace?: string): string {\n const parts = [\n 'You are a focused sub-agent working on a specific delegated task.',\n '',\n `YOUR TASK:\\n${goal}`,\n ];\n\n if (context?.trim()) {\n parts.push(`\\nCONTEXT:\\n${context.trim()}`);\n }\n\n if (workspace?.trim()) {\n parts.push(`\\nWORKSPACE: ${workspace.trim()}`);\n }\n\n parts.push(\n '\\nComplete this task using only the tools available to you. ' +\n 'When finished, reply with a clear, concise summary covering:\\n' +\n '- What you did\\n' +\n '- What you found or accomplished\\n' +\n '- Files created or modified\\n' +\n '- Issues encountered\\n\\n' +\n 'Your final reply is returned to the parent agent — be thorough but compact.',\n );\n\n return parts.join('\\n');\n}\n\nexport interface DelegateChildHandleOptions {\n workspace: string;\n goal: string;\n context?: string;\n allowedToolNames: string[];\n maxIterations: number;\n model: Model<Api>;\n bus: MessageBus;\n getConfig: () => Config | undefined;\n toolExecutorConfig?: Partial<ToolExecutorConfig>;\n}\n\nexport interface DelegateChildRunResult {\n summary: string;\n toolIterations: number;\n}\n\nexport interface DelegateChildHandle {\n run(): Promise<DelegateChildRunResult>;\n abort(): void;\n}\n\n/**\n * Build an isolated tool factory (no extensions, no session memory hooks) and a child {@link Agent}.\n */\nexport function createDelegateChildHandle(options: DelegateChildHandleOptions): DelegateChildHandle {\n const childFactory = new AgentToolsFactory({\n workspace: options.workspace,\n bus: options.bus,\n getCurrentContext: () => null,\n getConfig: options.getConfig,\n getPrimaryModel: () => options.model,\n toolExecutorConfig: options.toolExecutorConfig,\n });\n\n const allTools = childFactory.createAllTools({\n workspace: options.workspace,\n getPrimaryModel: () => options.model,\n disabledTools: new Set(['extensions']),\n });\n\n const allow = new Set(options.allowedToolNames);\n const filteredTools = allTools.filter((t) => allow.has(t.name));\n\n if (filteredTools.length === 0) {\n return {\n async run() {\n return {\n summary: 'No tools matched the allowlist after factory registration.',\n toolIterations: 0,\n };\n },\n abort() {},\n };\n }\n\n let toolIterations = 0;\n let aborted = false;\n\n const agent = new Agent({\n initialState: {\n systemPrompt: buildChildSystemPrompt(options.goal, options.context, options.workspace),\n model: options.model,\n thinkingLevel: 'low' as ThinkingLevel,\n tools: filteredTools,\n messages: [],\n },\n getApiKey: (provider: string) =>\n resolveProviderApiKeySync(provider) ?? getApiKeySync(provider) ?? '',\n beforeToolCall: async () => {\n if (aborted) {\n return { block: true, reason: 'Sub-agent aborted.' };\n }\n if (toolIterations >= options.maxIterations) {\n return {\n block: true,\n reason: `Sub-agent reached max tool iterations (${options.maxIterations}).`,\n };\n }\n return undefined;\n },\n afterToolCall: async () => {\n toolIterations += 1;\n return undefined;\n },\n });\n\n const userText = options.context?.trim()\n ? `${options.goal}\\n\\nAdditional context:\\n${options.context.trim()}`\n : options.goal;\n\n return {\n async run(): Promise<DelegateChildRunResult> {\n toolIterations = 0;\n aborted = false;\n try {\n await runAgentTurnWithTimeout(\n agent,\n async () => {\n await agent.prompt(userText);\n await agent.waitForIdle();\n },\n resolveAgentTurnTimeoutMs(options.getConfig()),\n );\n\n const messages = agent.state.messages;\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'assistant') {\n const content: unknown = msg.content;\n if (typeof content === 'string') {\n return { summary: content.trim() || '(empty assistant message)', toolIterations };\n }\n if (Array.isArray(content)) {\n const text = extractTextContent(content as Array<{ type: string; text?: string }>);\n return {\n summary: text.trim() || '(empty assistant message)',\n toolIterations,\n };\n }\n }\n }\n\n return {\n summary: aborted\n ? 'Sub-agent was aborted before producing a result.'\n : 'Sub-agent completed but produced no assistant text.',\n toolIterations,\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n const m = options.model as { model?: string; id?: string };\n const modelId = m?.model ?? m?.id;\n log.warn(\n {\n err: e,\n errorMessage: msg,\n goalPreview: options.goal.slice(0, 120),\n maxIterations: options.maxIterations,\n allowedToolCount: options.allowedToolNames.length,\n modelId,\n },\n `Delegate child run failed: ${msg}`,\n );\n return {\n summary: `Sub-agent error: ${msg}`,\n toolIterations,\n };\n }\n },\n\n abort(): void {\n aborted = true;\n agent.abort();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"child-agent-factory.js","names":[],"sources":["../../../src/agent/child-agent-factory.ts"],"sourcesContent":["import { Agent, type ThinkingLevel } from '@mariozechner/pi-agent-core';\nimport type { Api, Model } from '@mariozechner/pi-ai';\n\nimport type { Config } from '../config/schema.js';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport { resolveProviderApiKeySync } from '../auth/sync-provider-auth.js';\nimport { getApiKeySync } from '../providers/index.js';\nimport { createExtensionAwareStreamFn } from '../providers/extension-stream-bridge.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { extractTextContent } from './context/workspace.js';\nimport {\n resolveAgentTurnTimeoutMs,\n runAgentTurnWithTimeout,\n} from './orchestration/run-agent-turn-with-timeout.js';\nimport type { ToolExecutorConfig } from './tools/executor.js';\nimport { AgentToolsFactory } from './tools/factory.js';\n\nconst log = createLogger('delegate-child');\n\nexport function buildChildSystemPrompt(goal: string, context?: string, workspace?: string): string {\n const parts = [\n 'You are a focused sub-agent working on a specific delegated task.',\n '',\n `YOUR TASK:\\n${goal}`,\n ];\n\n if (context?.trim()) {\n parts.push(`\\nCONTEXT:\\n${context.trim()}`);\n }\n\n if (workspace?.trim()) {\n parts.push(`\\nWORKSPACE: ${workspace.trim()}`);\n }\n\n parts.push(\n '\\nComplete this task using only the tools available to you. ' +\n 'When finished, reply with a clear, concise summary covering:\\n' +\n '- What you did\\n' +\n '- What you found or accomplished\\n' +\n '- Files created or modified\\n' +\n '- Issues encountered\\n\\n' +\n 'Your final reply is returned to the parent agent — be thorough but compact.',\n );\n\n return parts.join('\\n');\n}\n\nexport interface DelegateChildHandleOptions {\n workspace: string;\n goal: string;\n context?: string;\n allowedToolNames: string[];\n maxIterations: number;\n model: Model<Api>;\n bus: MessageBus;\n getConfig: () => Config | undefined;\n toolExecutorConfig?: Partial<ToolExecutorConfig>;\n}\n\nexport interface DelegateChildRunResult {\n summary: string;\n toolIterations: number;\n}\n\nexport interface DelegateChildHandle {\n run(): Promise<DelegateChildRunResult>;\n abort(): void;\n}\n\n/**\n * Build an isolated tool factory (no extensions, no session memory hooks) and a child {@link Agent}.\n */\nexport function createDelegateChildHandle(options: DelegateChildHandleOptions): DelegateChildHandle {\n const childFactory = new AgentToolsFactory({\n workspace: options.workspace,\n bus: options.bus,\n getCurrentContext: () => null,\n getConfig: options.getConfig,\n getPrimaryModel: () => options.model,\n toolExecutorConfig: options.toolExecutorConfig,\n });\n\n const allTools = childFactory.createAllTools({\n workspace: options.workspace,\n getPrimaryModel: () => options.model,\n disabledTools: new Set(['extensions']),\n });\n\n const allow = new Set(options.allowedToolNames);\n const filteredTools = allTools.filter((t) => allow.has(t.name));\n\n if (filteredTools.length === 0) {\n return {\n async run() {\n return {\n summary: 'No tools matched the allowlist after factory registration.',\n toolIterations: 0,\n };\n },\n abort() {},\n };\n }\n\n let toolIterations = 0;\n let aborted = false;\n\n const agent = new Agent({\n initialState: {\n systemPrompt: buildChildSystemPrompt(options.goal, options.context, options.workspace),\n model: options.model,\n thinkingLevel: 'low' as ThinkingLevel,\n tools: filteredTools,\n messages: [],\n },\n streamFn: createExtensionAwareStreamFn(),\n getApiKey: (provider: string) =>\n resolveProviderApiKeySync(provider) ?? getApiKeySync(provider) ?? '',\n beforeToolCall: async () => {\n if (aborted) {\n return { block: true, reason: 'Sub-agent aborted.' };\n }\n if (toolIterations >= options.maxIterations) {\n return {\n block: true,\n reason: `Sub-agent reached max tool iterations (${options.maxIterations}).`,\n };\n }\n return undefined;\n },\n afterToolCall: async () => {\n toolIterations += 1;\n return undefined;\n },\n });\n\n const userText = options.context?.trim()\n ? `${options.goal}\\n\\nAdditional context:\\n${options.context.trim()}`\n : options.goal;\n\n return {\n async run(): Promise<DelegateChildRunResult> {\n toolIterations = 0;\n aborted = false;\n try {\n await runAgentTurnWithTimeout(\n agent,\n async () => {\n await agent.prompt(userText);\n await agent.waitForIdle();\n },\n resolveAgentTurnTimeoutMs(options.getConfig()),\n );\n\n const messages = agent.state.messages;\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'assistant') {\n const content: unknown = msg.content;\n if (typeof content === 'string') {\n return { summary: content.trim() || '(empty assistant message)', toolIterations };\n }\n if (Array.isArray(content)) {\n const text = extractTextContent(content as Array<{ type: string; text?: string }>);\n return {\n summary: text.trim() || '(empty assistant message)',\n toolIterations,\n };\n }\n }\n }\n\n return {\n summary: aborted\n ? 'Sub-agent was aborted before producing a result.'\n : 'Sub-agent completed but produced no assistant text.',\n toolIterations,\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n const m = options.model as { model?: string; id?: string };\n const modelId = m?.model ?? m?.id;\n log.warn(\n {\n err: e,\n errorMessage: msg,\n goalPreview: options.goal.slice(0, 120),\n maxIterations: options.maxIterations,\n allowedToolCount: options.allowedToolNames.length,\n modelId,\n },\n `Delegate child run failed: ${msg}`,\n );\n return {\n summary: `Sub-agent error: ${msg}`,\n toolIterations,\n };\n }\n },\n\n abort(): void {\n aborted = true;\n agent.abort();\n },\n };\n}\n"],"mappings":";;;;;;;;;;yBAK0E;gBACpB;aAEJ;AAUlD,MAAM,MAAM,aAAa,iBAAiB;AAE1C,SAAgB,uBAAuB,MAAc,SAAkB,WAA4B;CACjG,MAAM,QAAQ;EACZ;EACA;EACA,eAAe;EAChB;AAED,KAAI,SAAS,MAAM,CACjB,OAAM,KAAK,eAAe,QAAQ,MAAM,GAAG;AAG7C,KAAI,WAAW,MAAM,CACnB,OAAM,KAAK,gBAAgB,UAAU,MAAM,GAAG;AAGhD,OAAM,KACJ,+SAOD;AAED,QAAO,MAAM,KAAK,KAAK;;;;;AA4BzB,SAAgB,0BAA0B,SAA0D;CAUlG,MAAM,WATe,IAAI,kBAAkB;EACzC,WAAW,QAAQ;EACnB,KAAK,QAAQ;EACb,yBAAyB;EACzB,WAAW,QAAQ;EACnB,uBAAuB,QAAQ;EAC/B,oBAAoB,QAAQ;EAC7B,CAAC,CAE4B,eAAe;EAC3C,WAAW,QAAQ;EACnB,uBAAuB,QAAQ;EAC/B,eAAe,IAAI,IAAI,CAAC,aAAa,CAAC;EACvC,CAAC;CAEF,MAAM,QAAQ,IAAI,IAAI,QAAQ,iBAAiB;CAC/C,MAAM,gBAAgB,SAAS,QAAQ,MAAM,MAAM,IAAI,EAAE,KAAK,CAAC;AAE/D,KAAI,cAAc,WAAW,EAC3B,QAAO;EACL,MAAM,MAAM;AACV,UAAO;IACL,SAAS;IACT,gBAAgB;IACjB;;EAEH,QAAQ;EACT;CAGH,IAAI,iBAAiB;CACrB,IAAI,UAAU;CAEd,MAAM,QAAQ,IAAI,MAAM;EACtB,cAAc;GACZ,cAAc,uBAAuB,QAAQ,MAAM,QAAQ,SAAS,QAAQ,UAAU;GACtF,OAAO,QAAQ;GACf,eAAe;GACf,OAAO;GACP,UAAU,EAAE;GACb;EACD,UAAU,8BAA8B;EACxC,YAAY,aACV,0BAA0B,SAAS,IAAI,cAAc,SAAS,IAAI;EACpE,gBAAgB,YAAY;AAC1B,OAAI,QACF,QAAO;IAAE,OAAO;IAAM,QAAQ;IAAsB;AAEtD,OAAI,kBAAkB,QAAQ,cAC5B,QAAO;IACL,OAAO;IACP,QAAQ,0CAA0C,QAAQ,cAAc;IACzE;;EAIL,eAAe,YAAY;AACzB,qBAAkB;;EAGrB,CAAC;CAEF,MAAM,WAAW,QAAQ,SAAS,MAAM,GACpC,GAAG,QAAQ,KAAK,2BAA2B,QAAQ,QAAQ,MAAM,KACjE,QAAQ;AAEZ,QAAO;EACL,MAAM,MAAuC;AAC3C,oBAAiB;AACjB,aAAU;AACV,OAAI;AACF,UAAM,wBACJ,OACA,YAAY;AACV,WAAM,MAAM,OAAO,SAAS;AAC5B,WAAM,MAAM,aAAa;OAE3B,0BAA0B,QAAQ,WAAW,CAAC,CAC/C;IAED,MAAM,WAAW,MAAM,MAAM;AAC7B,SAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;KAC7C,MAAM,MAAM,SAAS;AACrB,SAAI,IAAI,SAAS,aAAa;MAC5B,MAAM,UAAmB,IAAI;AAC7B,UAAI,OAAO,YAAY,SACrB,QAAO;OAAE,SAAS,QAAQ,MAAM,IAAI;OAA6B;OAAgB;AAEnF,UAAI,MAAM,QAAQ,QAAQ,CAExB,QAAO;OACL,SAFW,mBAAmB,QAAkD,CAElE,MAAM,IAAI;OACxB;OACD;;;AAKP,WAAO;KACL,SAAS,UACL,qDACA;KACJ;KACD;YACM,GAAG;IACV,MAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IACtD,MAAM,IAAI,QAAQ;IAClB,MAAM,UAAU,GAAG,SAAS,GAAG;AAC/B,QAAI,KACF;KACE,KAAK;KACL,cAAc;KACd,aAAa,QAAQ,KAAK,MAAM,GAAG,IAAI;KACvC,eAAe,QAAQ;KACvB,kBAAkB,QAAQ,iBAAiB;KAC3C;KACD,EACD,8BAA8B,MAC/B;AACD,WAAO;KACL,SAAS,oBAAoB;KAC7B;KACD;;;EAIL,QAAc;AACZ,aAAU;AACV,SAAM,OAAO;;EAEhB"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Prepend `<file path="…">` blocks for `@file:` references before the raw user text.
3
+ */
4
+ export declare function expandAtFileMentionsInPlainText(text: string, workspaceRoot: string): Promise<string>;
@@ -0,0 +1,69 @@
1
+ import { resolveWorkspaceSafePath } from "../../gateway/workspace-editor-path.js";
2
+ import { readFile, readdir, stat } from "node:fs/promises";
3
+ //#region src/agent/context/expand-at-file-mentions.ts
4
+ /** Aligned with web `file-wire-pattern.ts`: unquoted path or quoted for spaces. */
5
+ const QUOTED_INNER = String.raw`((?:[^"\\]|\\.)*)`;
6
+ const UNQUOTED = String.raw`[a-zA-Z0-9_./\-\p{L}\p{N}]+`;
7
+ const FILE_RE = new RegExp(`@file:(?:"${QUOTED_INNER}"|(${UNQUOTED}))`, "gu");
8
+ const MAX_PATH_BYTES = 5e4;
9
+ const MAX_DIR_ENTRIES = 120;
10
+ function pathFromFileWireExec(m) {
11
+ const unquoted = m[2];
12
+ if (unquoted != null && unquoted !== "") return unquoted;
13
+ const q = m[1];
14
+ return q != null ? q.replace(/\\(.)/g, (_, ch) => ch) : "";
15
+ }
16
+ function escapeXmlAttr(s) {
17
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
18
+ }
19
+ function collectFileTokens(text) {
20
+ const out = [];
21
+ const r = new RegExp(FILE_RE.source, "gu");
22
+ let m;
23
+ while ((m = r.exec(text)) !== null) {
24
+ const v = pathFromFileWireExec(m).trim();
25
+ if (v) out.push({
26
+ start: m.index,
27
+ end: m.index + m[0].length,
28
+ value: v
29
+ });
30
+ }
31
+ return out;
32
+ }
33
+ async function workspacePathSnippet(rel, workspaceRoot) {
34
+ const abs = resolveWorkspaceSafePath(workspaceRoot, rel);
35
+ if (!abs) return "[invalid path]";
36
+ try {
37
+ const st = await stat(abs);
38
+ if (st.isDirectory()) return `[directory, first ${MAX_DIR_ENTRIES} non-hidden entries]\n${(await readdir(abs, { withFileTypes: true })).filter((e) => !e.name.startsWith(".")).slice(0, MAX_DIR_ENTRIES).map((e) => `${e.isDirectory() ? "(dir)" : "(file)"} ${e.name}`).join("\n")}`;
39
+ if (st.isFile()) {
40
+ const s = (await readFile(abs)).toString("utf8");
41
+ return s.length > MAX_PATH_BYTES ? `${s.slice(0, MAX_PATH_BYTES)}\n\n[... truncated ...]` : s;
42
+ }
43
+ return "[not a file or directory]";
44
+ } catch {
45
+ return "[unreadable or missing]";
46
+ }
47
+ }
48
+ /**
49
+ * Prepend `<file path="…">` blocks for `@file:` references before the raw user text.
50
+ */
51
+ async function expandAtFileMentionsInPlainText(text, workspaceRoot) {
52
+ if (!text.includes("@file:")) return text;
53
+ const tokens = collectFileTokens(text);
54
+ if (tokens.length === 0) return text;
55
+ const seen = /* @__PURE__ */ new Set();
56
+ const blocks = [];
57
+ for (const t of tokens) {
58
+ if (seen.has(t.value)) continue;
59
+ seen.add(t.value);
60
+ const body = await workspacePathSnippet(t.value, workspaceRoot);
61
+ blocks.push(`<file path="${escapeXmlAttr(t.value)}">\n${body}\n</file>`);
62
+ }
63
+ if (blocks.length === 0) return text;
64
+ return `${blocks.join("\n\n")}\n\n${text}`;
65
+ }
66
+ //#endregion
67
+ export { expandAtFileMentionsInPlainText };
68
+
69
+ //# sourceMappingURL=expand-at-file-mentions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expand-at-file-mentions.js","names":[],"sources":["../../../../src/agent/context/expand-at-file-mentions.ts"],"sourcesContent":["import { readFile, readdir, stat } from 'node:fs/promises';\n\nimport { resolveWorkspaceSafePath } from '../../gateway/workspace-editor-path.js';\n\n/** Aligned with web `file-wire-pattern.ts`: unquoted path or quoted for spaces. */\nconst QUOTED_INNER = String.raw`((?:[^\"\\\\]|\\\\.)*)`;\nconst UNQUOTED = String.raw`[a-zA-Z0-9_./\\-\\p{L}\\p{N}]+`;\nconst FILE_RE = new RegExp(`@file:(?:\"${QUOTED_INNER}\"|(${UNQUOTED}))`, 'gu');\n\nconst MAX_PATH_BYTES = 50_000;\nconst MAX_DIR_ENTRIES = 120;\n\ninterface TokenHit {\n start: number;\n end: number;\n value: string;\n}\n\nfunction pathFromFileWireExec(m: RegExpExecArray): string {\n const unquoted = m[2];\n if (unquoted != null && unquoted !== '') return unquoted;\n const q = m[1];\n return q != null ? q.replace(/\\\\(.)/g, (_: string, ch: string) => ch) : '';\n}\n\nfunction escapeXmlAttr(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;');\n}\n\nfunction collectFileTokens(text: string): TokenHit[] {\n const out: TokenHit[] = [];\n const r = new RegExp(FILE_RE.source, 'gu');\n let m: RegExpExecArray | null;\n while ((m = r.exec(text)) !== null) {\n const v = pathFromFileWireExec(m).trim();\n if (v) out.push({ start: m.index, end: m.index + m[0].length, value: v });\n }\n return out;\n}\n\nasync function workspacePathSnippet(rel: string, workspaceRoot: string): Promise<string> {\n const abs = resolveWorkspaceSafePath(workspaceRoot, rel);\n if (!abs) return '[invalid path]';\n try {\n const st = await stat(abs);\n if (st.isDirectory()) {\n const entries = await readdir(abs, { withFileTypes: true });\n const lines = entries\n .filter((e) => !e.name.startsWith('.'))\n .slice(0, MAX_DIR_ENTRIES)\n .map((e) => `${e.isDirectory() ? '(dir)' : '(file)'} ${e.name}`)\n .join('\\n');\n return `[directory, first ${MAX_DIR_ENTRIES} non-hidden entries]\\n${lines}`;\n }\n if (st.isFile()) {\n const buf = await readFile(abs);\n const s = buf.toString('utf8');\n return s.length > MAX_PATH_BYTES ? `${s.slice(0, MAX_PATH_BYTES)}\\n\\n[... truncated ...]` : s;\n }\n return '[not a file or directory]';\n } catch {\n return '[unreadable or missing]';\n }\n}\n\n/**\n * Prepend `<file path=\"…\">` blocks for `@file:` references before the raw user text.\n */\nexport async function expandAtFileMentionsInPlainText(text: string, workspaceRoot: string): Promise<string> {\n if (!text.includes('@file:')) return text;\n const tokens = collectFileTokens(text);\n if (tokens.length === 0) return text;\n\n const seen = new Set<string>();\n const blocks: string[] = [];\n\n for (const t of tokens) {\n if (seen.has(t.value)) continue;\n seen.add(t.value);\n const body = await workspacePathSnippet(t.value, workspaceRoot);\n blocks.push(`<file path=\"${escapeXmlAttr(t.value)}\">\\n${body}\\n</file>`);\n }\n\n if (blocks.length === 0) return text;\n return `${blocks.join('\\n\\n')}\\n\\n${text}`;\n}\n"],"mappings":";;;;AAKA,MAAM,eAAe,OAAO,GAAG;AAC/B,MAAM,WAAW,OAAO,GAAG;AAC3B,MAAM,UAAU,IAAI,OAAO,aAAa,aAAa,KAAK,SAAS,KAAK,KAAK;AAE7E,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAQxB,SAAS,qBAAqB,GAA4B;CACxD,MAAM,WAAW,EAAE;AACnB,KAAI,YAAY,QAAQ,aAAa,GAAI,QAAO;CAChD,MAAM,IAAI,EAAE;AACZ,QAAO,KAAK,OAAO,EAAE,QAAQ,WAAW,GAAW,OAAe,GAAG,GAAG;;AAG1E,SAAS,cAAc,GAAmB;AACxC,QAAO,EAAE,QAAQ,MAAM,QAAQ,CAAC,QAAQ,MAAM,SAAS,CAAC,QAAQ,MAAM,OAAO;;AAG/E,SAAS,kBAAkB,MAA0B;CACnD,MAAM,MAAkB,EAAE;CAC1B,MAAM,IAAI,IAAI,OAAO,QAAQ,QAAQ,KAAK;CAC1C,IAAI;AACJ,SAAQ,IAAI,EAAE,KAAK,KAAK,MAAM,MAAM;EAClC,MAAM,IAAI,qBAAqB,EAAE,CAAC,MAAM;AACxC,MAAI,EAAG,KAAI,KAAK;GAAE,OAAO,EAAE;GAAO,KAAK,EAAE,QAAQ,EAAE,GAAG;GAAQ,OAAO;GAAG,CAAC;;AAE3E,QAAO;;AAGT,eAAe,qBAAqB,KAAa,eAAwC;CACvF,MAAM,MAAM,yBAAyB,eAAe,IAAI;AACxD,KAAI,CAAC,IAAK,QAAO;AACjB,KAAI;EACF,MAAM,KAAK,MAAM,KAAK,IAAI;AAC1B,MAAI,GAAG,aAAa,CAOlB,QAAO,qBAAqB,gBAAgB,yBAN5B,MAAM,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC,EAExD,QAAQ,MAAM,CAAC,EAAE,KAAK,WAAW,IAAI,CAAC,CACtC,MAAM,GAAG,gBAAgB,CACzB,KAAK,MAAM,GAAG,EAAE,aAAa,GAAG,UAAU,SAAS,GAAG,EAAE,OAAO,CAC/D,KAAK,KAAK;AAGf,MAAI,GAAG,QAAQ,EAAE;GAEf,MAAM,KADM,MAAM,SAAS,IAAI,EACjB,SAAS,OAAO;AAC9B,UAAO,EAAE,SAAS,iBAAiB,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC,2BAA2B;;AAE9F,SAAO;SACD;AACN,SAAO;;;;;;AAOX,eAAsB,gCAAgC,MAAc,eAAwC;AAC1G,KAAI,CAAC,KAAK,SAAS,SAAS,CAAE,QAAO;CACrC,MAAM,SAAS,kBAAkB,KAAK;AACtC,KAAI,OAAO,WAAW,EAAG,QAAO;CAEhC,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,SAAmB,EAAE;AAE3B,MAAK,MAAM,KAAK,QAAQ;AACtB,MAAI,KAAK,IAAI,EAAE,MAAM,CAAE;AACvB,OAAK,IAAI,EAAE,MAAM;EACjB,MAAM,OAAO,MAAM,qBAAqB,EAAE,OAAO,cAAc;AAC/D,SAAO,KAAK,eAAe,cAAc,EAAE,MAAM,CAAC,MAAM,KAAK,WAAW;;AAG1E,KAAI,OAAO,WAAW,EAAG,QAAO;AAChC,QAAO,GAAG,OAAO,KAAK,OAAO,CAAC,MAAM"}
@@ -4,8 +4,8 @@ import { DEFAULT_AGENT_ID, init_agent_scope, resolveAgentBootstrapDir } from "..
4
4
  import { WORKSPACE_FILES, init_paths } from "../../config/paths.js";
5
5
  import { BOOTSTRAP_FILES } from "./workspace.js";
6
6
  import { dirname, join } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
7
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8
+ import { fileURLToPath } from "node:url";
9
9
  //#region src/agent/context/workspace-seed.ts
10
10
  /**
11
11
  * Seed bootstrap persona Markdown files under `…/agents/<id>/bootstrap/` (ensure workspace + write-if-missing).
@@ -2,7 +2,6 @@
2
2
  * Unified image stack: understanding (pi-ai multimodal) under this folder;
3
3
  * generation (OpenAI Images API; DashScope Beijing wan2.6-t2i via `dashscope/`) under `./generation/`.
4
4
  */
5
- export { describeImagesWithPiAi } from './describe-images.js';
6
5
  export { describeImages, describeImagesWithFallback, type DescribeImagesParams, type DescribeImagesWithFallbackParams, type DescribeImagesWithFallbackResult, type DescribeImagesAttempt, } from './understanding/runtime.js';
7
6
  export { registerImageUnderstandingProvider, getImageUnderstandingProvider, listImageUnderstandingProviders, } from './understanding/provider-registry.js';
8
7
  export type { ImageUnderstandingProvider, ImageUnderstandingRequest, ImageUnderstandingResult, } from './understanding/types.js';
@@ -1,11 +1,10 @@
1
1
  import { getImageUnderstandingProvider, listImageUnderstandingProviders, registerImageUnderstandingProvider } from "./understanding/provider-registry.js";
2
2
  import { resolveImageModelConfigForTool } from "./tool-model-config.js";
3
3
  import { describeImages, describeImagesWithFallback } from "./understanding/runtime.js";
4
- import { describeImagesWithPiAi } from "./describe-images.js";
5
4
  import { runWithImageModelFallback } from "./image-model-fallback.js";
6
5
  import { loadImageForToolInput } from "./load-image-media.js";
7
6
  import { DASHSCOPE_DEFAULT_IMAGE_MODEL, OPENAI_DEFAULT_IMAGE_MODEL } from "./generation/constants.js";
8
7
  import { getImageGenerationProvider, listImageGenerationProviders, registerImageGenerationProvider } from "./generation/provider-registry.js";
9
8
  import { generateImage, listImageGenerationProvidersSummary } from "./generation/runtime.js";
10
9
  import { modelSupportsVision, resolveImageHandlingStrategy } from "./vision-detection.js";
11
- export { DASHSCOPE_DEFAULT_IMAGE_MODEL, OPENAI_DEFAULT_IMAGE_MODEL, describeImages, describeImagesWithFallback, describeImagesWithPiAi, generateImage, getImageGenerationProvider, getImageUnderstandingProvider, listImageGenerationProviders, listImageGenerationProvidersSummary, listImageUnderstandingProviders, loadImageForToolInput, modelSupportsVision, registerImageGenerationProvider, registerImageUnderstandingProvider, resolveImageHandlingStrategy, resolveImageModelConfigForTool, runWithImageModelFallback };
10
+ export { DASHSCOPE_DEFAULT_IMAGE_MODEL, OPENAI_DEFAULT_IMAGE_MODEL, describeImages, describeImagesWithFallback, generateImage, getImageGenerationProvider, getImageUnderstandingProvider, listImageGenerationProviders, listImageGenerationProvidersSummary, listImageUnderstandingProviders, loadImageForToolInput, modelSupportsVision, registerImageGenerationProvider, registerImageUnderstandingProvider, resolveImageHandlingStrategy, resolveImageModelConfigForTool, runWithImageModelFallback };
@@ -1 +1 @@
1
- {"version":3,"file":"pi-ai-provider.js","names":[],"sources":["../../../../../src/agent/image/understanding/pi-ai-provider.ts"],"sourcesContent":["import { complete, type Api, type Context, type Model } from '@mariozechner/pi-ai';\nimport { resolveModel, getApiKey } from '../../../providers/index.js';\nimport { coerceImageAssistantText } from '../image-helpers.js';\nimport { registerImageUnderstandingProvider } from './provider-registry.js';\nimport type {\n ImageUnderstandingProvider,\n ImageUnderstandingRequest,\n ImageUnderstandingResult,\n} from './types.js';\n\nfunction resolveMaxTokens(modelMaxTokens: number | undefined, requestedMaxTokens = 4096): number {\n if (\n typeof modelMaxTokens !== 'number' ||\n !Number.isFinite(modelMaxTokens) ||\n modelMaxTokens <= 0\n ) {\n return requestedMaxTokens;\n }\n return Math.min(requestedMaxTokens, modelMaxTokens);\n}\n\nexport function buildPiAiImageUnderstandingProvider(providerId: string): ImageUnderstandingProvider {\n return {\n id: providerId,\n label: `pi-ai (${providerId})`,\n async isConfigured() {\n const apiKey = await getApiKey(providerId);\n return Boolean(apiKey);\n },\n async describeImages(modelId, request) {\n const modelRef = `${providerId}/${modelId}`;\n const model = resolveModel(modelRef) as Model<Api>;\n if (!model.input?.includes('image')) {\n throw new Error(`Model does not support images: ${modelRef}`);\n }\n const apiKey = await getApiKey(providerId);\n if (!apiKey) {\n throw new Error(`No API key configured for provider: ${providerId}`);\n }\n\n const context: Context = {\n messages: [\n {\n role: 'user',\n content: [\n { type: 'text', text: request.prompt },\n ...request.images.map((img) => ({\n type: 'image' as const,\n data: img.buffer.toString('base64'),\n mimeType: img.mimeType || 'image/jpeg',\n })),\n ],\n timestamp: Date.now(),\n },\n ],\n };\n\n const maxTokens = resolveMaxTokens(model.maxTokens, request.maxTokens ?? 512);\n const timeoutMs = request.timeoutMs ?? 60_000;\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(timeoutMs)\n : undefined;\n const signal = (() => {\n if (timeoutSignal && request.signal && typeof AbortSignal.any === 'function') {\n return AbortSignal.any([request.signal, timeoutSignal]);\n }\n if (request.signal) {\n return request.signal;\n }\n if (timeoutSignal) {\n return timeoutSignal;\n }\n const controller = new AbortController();\n setTimeout(() => controller.abort(), timeoutMs);\n return controller.signal;\n })();\n\n const message = await complete(model, context, {\n apiKey,\n maxTokens,\n signal,\n });\n\n const text = coerceImageAssistantText({\n message,\n provider: providerId,\n model: modelId,\n });\n return { text, provider: providerId, model: modelId };\n },\n };\n}\n\nfor (const providerId of ['openai', 'anthropic', 'google']) {\n registerImageUnderstandingProvider(buildPiAiImageUnderstandingProvider(providerId));\n}\n"],"mappings":";;;;;gBACsE;AAStE,SAAS,iBAAiB,gBAAoC,qBAAqB,MAAc;AAC/F,KACE,OAAO,mBAAmB,YAC1B,CAAC,OAAO,SAAS,eAAe,IAChC,kBAAkB,EAElB,QAAO;AAET,QAAO,KAAK,IAAI,oBAAoB,eAAe;;AAGrD,SAAgB,oCAAoC,YAAgD;AAClG,QAAO;EACL,IAAI;EACJ,OAAO,UAAU,WAAW;EAC5B,MAAM,eAAe;GACnB,MAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,UAAO,QAAQ,OAAO;;EAExB,MAAM,eAAe,SAAS,SAAS;GACrC,MAAM,WAAW,GAAG,WAAW,GAAG;GAClC,MAAM,QAAQ,aAAa,SAAS;AACpC,OAAI,CAAC,MAAM,OAAO,SAAS,QAAQ,CACjC,OAAM,IAAI,MAAM,kCAAkC,WAAW;GAE/D,MAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,OAAI,CAAC,OACH,OAAM,IAAI,MAAM,uCAAuC,aAAa;GAGtE,MAAM,UAAmB,EACvB,UAAU,CACR;IACE,MAAM;IACN,SAAS,CACP;KAAE,MAAM;KAAQ,MAAM,QAAQ;KAAQ,EACtC,GAAG,QAAQ,OAAO,KAAK,SAAS;KAC9B,MAAM;KACN,MAAM,IAAI,OAAO,SAAS,SAAS;KACnC,UAAU,IAAI,YAAY;KAC3B,EAAE,CACJ;IACD,WAAW,KAAK,KAAK;IACtB,CACF,EACF;GAED,MAAM,YAAY,iBAAiB,MAAM,WAAW,QAAQ,aAAa,IAAI;GAC7E,MAAM,YAAY,QAAQ,aAAa;GACvC,MAAM,gBACJ,OAAO,gBAAgB,eAAe,OAAO,YAAY,YAAY,aACjE,YAAY,QAAQ,UAAU,GAC9B,KAAA;AA2BN,UAAO;IAAE,MALI,yBAAyB;KACpC,SAPc,MAAM,SAAS,OAAO,SAAS;MAC7C;MACA;MACA,eAlBoB;AACpB,WAAI,iBAAiB,QAAQ,UAAU,OAAO,YAAY,QAAQ,WAChE,QAAO,YAAY,IAAI,CAAC,QAAQ,QAAQ,cAAc,CAAC;AAEzD,WAAI,QAAQ,OACV,QAAO,QAAQ;AAEjB,WAAI,cACF,QAAO;OAET,MAAM,aAAa,IAAI,iBAAiB;AACxC,wBAAiB,WAAW,OAAO,EAAE,UAAU;AAC/C,cAAO,WAAW;UAChB;MAMH,CAAC;KAIA,UAAU;KACV,OAAO;KACR,CAAC;IACa,UAAU;IAAY,OAAO;IAAS;;EAExD;;AAGH,KAAK,MAAM,cAAc;CAAC;CAAU;CAAa;CAAS,CACxD,oCAAmC,oCAAoC,WAAW,CAAC"}
1
+ {"version":3,"file":"pi-ai-provider.js","names":[],"sources":["../../../../../src/agent/image/understanding/pi-ai-provider.ts"],"sourcesContent":["import { complete, type Api, type Context, type Model } from '@mariozechner/pi-ai';\nimport { resolveModel, getApiKey } from '../../../providers/index.js';\nimport { coerceImageAssistantText } from '../image-helpers.js';\nimport { registerImageUnderstandingProvider } from './provider-registry.js';\nimport type { ImageUnderstandingProvider } from './types.js';\n\nfunction resolveMaxTokens(modelMaxTokens: number | undefined, requestedMaxTokens = 4096): number {\n if (\n typeof modelMaxTokens !== 'number' ||\n !Number.isFinite(modelMaxTokens) ||\n modelMaxTokens <= 0\n ) {\n return requestedMaxTokens;\n }\n return Math.min(requestedMaxTokens, modelMaxTokens);\n}\n\nexport function buildPiAiImageUnderstandingProvider(providerId: string): ImageUnderstandingProvider {\n return {\n id: providerId,\n label: `pi-ai (${providerId})`,\n async isConfigured() {\n const apiKey = await getApiKey(providerId);\n return Boolean(apiKey);\n },\n async describeImages(modelId, request) {\n const modelRef = `${providerId}/${modelId}`;\n const model = resolveModel(modelRef) as Model<Api>;\n if (!model.input?.includes('image')) {\n throw new Error(`Model does not support images: ${modelRef}`);\n }\n const apiKey = await getApiKey(providerId);\n if (!apiKey) {\n throw new Error(`No API key configured for provider: ${providerId}`);\n }\n\n const context: Context = {\n messages: [\n {\n role: 'user',\n content: [\n { type: 'text', text: request.prompt },\n ...request.images.map((img) => ({\n type: 'image' as const,\n data: img.buffer.toString('base64'),\n mimeType: img.mimeType || 'image/jpeg',\n })),\n ],\n timestamp: Date.now(),\n },\n ],\n };\n\n const maxTokens = resolveMaxTokens(model.maxTokens, request.maxTokens ?? 512);\n const timeoutMs = request.timeoutMs ?? 60_000;\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(timeoutMs)\n : undefined;\n const signal = (() => {\n if (timeoutSignal && request.signal && typeof AbortSignal.any === 'function') {\n return AbortSignal.any([request.signal, timeoutSignal]);\n }\n if (request.signal) {\n return request.signal;\n }\n if (timeoutSignal) {\n return timeoutSignal;\n }\n const controller = new AbortController();\n setTimeout(() => controller.abort(), timeoutMs);\n return controller.signal;\n })();\n\n const message = await complete(model, context, {\n apiKey,\n maxTokens,\n signal,\n });\n\n const text = coerceImageAssistantText({\n message,\n provider: providerId,\n model: modelId,\n });\n return { text, provider: providerId, model: modelId };\n },\n };\n}\n\nfor (const providerId of ['openai', 'anthropic', 'google']) {\n registerImageUnderstandingProvider(buildPiAiImageUnderstandingProvider(providerId));\n}\n"],"mappings":";;;;;gBACsE;AAKtE,SAAS,iBAAiB,gBAAoC,qBAAqB,MAAc;AAC/F,KACE,OAAO,mBAAmB,YAC1B,CAAC,OAAO,SAAS,eAAe,IAChC,kBAAkB,EAElB,QAAO;AAET,QAAO,KAAK,IAAI,oBAAoB,eAAe;;AAGrD,SAAgB,oCAAoC,YAAgD;AAClG,QAAO;EACL,IAAI;EACJ,OAAO,UAAU,WAAW;EAC5B,MAAM,eAAe;GACnB,MAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,UAAO,QAAQ,OAAO;;EAExB,MAAM,eAAe,SAAS,SAAS;GACrC,MAAM,WAAW,GAAG,WAAW,GAAG;GAClC,MAAM,QAAQ,aAAa,SAAS;AACpC,OAAI,CAAC,MAAM,OAAO,SAAS,QAAQ,CACjC,OAAM,IAAI,MAAM,kCAAkC,WAAW;GAE/D,MAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,OAAI,CAAC,OACH,OAAM,IAAI,MAAM,uCAAuC,aAAa;GAGtE,MAAM,UAAmB,EACvB,UAAU,CACR;IACE,MAAM;IACN,SAAS,CACP;KAAE,MAAM;KAAQ,MAAM,QAAQ;KAAQ,EACtC,GAAG,QAAQ,OAAO,KAAK,SAAS;KAC9B,MAAM;KACN,MAAM,IAAI,OAAO,SAAS,SAAS;KACnC,UAAU,IAAI,YAAY;KAC3B,EAAE,CACJ;IACD,WAAW,KAAK,KAAK;IACtB,CACF,EACF;GAED,MAAM,YAAY,iBAAiB,MAAM,WAAW,QAAQ,aAAa,IAAI;GAC7E,MAAM,YAAY,QAAQ,aAAa;GACvC,MAAM,gBACJ,OAAO,gBAAgB,eAAe,OAAO,YAAY,YAAY,aACjE,YAAY,QAAQ,UAAU,GAC9B,KAAA;AA2BN,UAAO;IAAE,MALI,yBAAyB;KACpC,SAPc,MAAM,SAAS,OAAO,SAAS;MAC7C;MACA;MACA,eAlBoB;AACpB,WAAI,iBAAiB,QAAQ,UAAU,OAAO,YAAY,QAAQ,WAChE,QAAO,YAAY,IAAI,CAAC,QAAQ,QAAQ,cAAc,CAAC;AAEzD,WAAI,QAAQ,OACV,QAAO,QAAQ;AAEjB,WAAI,cACF,QAAO;OAET,MAAM,aAAa,IAAI,iBAAiB;AACxC,wBAAiB,WAAW,OAAO,EAAE,UAAU;AAC/C,cAAO,WAAW;UAChB;MAMH,CAAC;KAIA,UAAU;KACV,OAAO;KACR,CAAC;IACa,UAAU;IAAY,OAAO;IAAS;;EAExD;;AAGH,KAAK,MAAM,cAAc;CAAC;CAAU;CAAa;CAAS,CACxD,oCAAmC,oCAAoC,WAAW,CAAC"}
@@ -1,9 +1,9 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { init_paths, resolveAgentDir } from "../../config/paths.js";
4
+ import { mkdir, readFile, readdir, rename, writeFile } from "fs/promises";
4
5
  import { join } from "path";
5
6
  import { existsSync, watch } from "fs";
6
- import { mkdir, readFile, readdir, rename, writeFile } from "fs/promises";
7
7
  //#region src/agent/ipc/inbox.ts
8
8
  init_logger();
9
9
  init_paths();
@@ -2,8 +2,8 @@ import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { init_paths, resolveSocketPath } from "../../config/paths.js";
4
4
  import { isValidIPCMessage } from "./types.js";
5
- import { dirname } from "path";
6
5
  import { mkdir } from "fs/promises";
6
+ import { dirname } from "path";
7
7
  import { Socket, createServer } from "net";
8
8
  //#region src/agent/ipc/socket.ts
9
9
  init_logger();
@@ -44,7 +44,7 @@ export declare class SessionCompactor {
44
44
  reason: string;
45
45
  usagePercent?: number;
46
46
  };
47
- compact(messages: AgentMessage[], _instructions?: string): Promise<CompactionResult>;
47
+ compact(messages: AgentMessage[], instructions?: string, force?: boolean): Promise<CompactionResult>;
48
48
  private generateSummary;
49
49
  private llmAbstractiveSummary;
50
50
  private formatMessages;
@@ -121,9 +121,17 @@ var SessionCompactor = class {
121
121
  reason: "within_threshold"
122
122
  };
123
123
  }
124
- async compact(messages, _instructions) {
124
+ async compact(messages, instructions, force) {
125
125
  const effectiveMessages = filterDroppableMessages(messages);
126
- if (effectiveMessages.length < this.config.minMessagesBeforeCompact) return {
126
+ const minRequired = this.config.minMessagesBeforeCompact;
127
+ if (!force && effectiveMessages.length < minRequired) return {
128
+ summary: "",
129
+ firstKeptIndex: 0,
130
+ tokensBefore: this.estimateTotalTokens(effectiveMessages),
131
+ tokensAfter: this.estimateTotalTokens(effectiveMessages),
132
+ compacted: false
133
+ };
134
+ if (force && effectiveMessages.length < 2) return {
127
135
  summary: "",
128
136
  firstKeptIndex: 0,
129
137
  tokensBefore: this.estimateTotalTokens(effectiveMessages),
@@ -131,15 +139,33 @@ var SessionCompactor = class {
131
139
  compacted: false
132
140
  };
133
141
  const range = calculateCompactionRange(effectiveMessages, this.config);
134
- const keepRecent = range ? effectiveMessages.length - range.end : this.config.keepRecentMessages;
135
- const messagesToSummarize = range ? effectiveMessages.slice(0, range.end) : effectiveMessages.slice(0, -keepRecent);
136
- const keptMessages = range ? effectiveMessages.slice(range.end) : effectiveMessages.slice(-keepRecent);
142
+ let messagesToSummarize;
143
+ let keptMessages;
144
+ if (range) {
145
+ messagesToSummarize = effectiveMessages.slice(0, range.end);
146
+ keptMessages = effectiveMessages.slice(range.end);
147
+ } else if (force) {
148
+ const keep = Math.min(this.config.keepRecentMessages, Math.max(1, effectiveMessages.length - 1));
149
+ messagesToSummarize = effectiveMessages.slice(0, effectiveMessages.length - keep);
150
+ keptMessages = effectiveMessages.slice(-keep);
151
+ } else {
152
+ const keepRecent = this.config.keepRecentMessages;
153
+ messagesToSummarize = effectiveMessages.slice(0, -keepRecent);
154
+ keptMessages = effectiveMessages.slice(-keepRecent);
155
+ }
156
+ if (messagesToSummarize.length === 0) return {
157
+ summary: "",
158
+ firstKeptIndex: 0,
159
+ tokensBefore: this.estimateTotalTokens(effectiveMessages),
160
+ tokensAfter: this.estimateTotalTokens(effectiveMessages),
161
+ compacted: false
162
+ };
137
163
  let preservedReasoning = null;
138
164
  if (this.config.preserveReasoning) {
139
165
  preservedReasoning = extractLastReasoning(messagesToSummarize);
140
166
  if (preservedReasoning) injectReasoningIntoFirstAssistant(keptMessages, preservedReasoning);
141
167
  }
142
- const summary = await this.generateSummary(messagesToSummarize);
168
+ const summary = await this.generateSummary(messagesToSummarize, instructions);
143
169
  const structuredSummary = this.config.mode === "structured" ? generateStructuredSummary(messagesToSummarize) : void 0;
144
170
  const tokensBefore = this.estimateTotalTokens(effectiveMessages);
145
171
  const tokensAfter = estimateTokens(summary) + 20 + this.estimateTotalTokens(keptMessages);
@@ -155,24 +181,25 @@ var SessionCompactor = class {
155
181
  compactedUsage
156
182
  };
157
183
  }
158
- async generateSummary(messages) {
184
+ async generateSummary(messages, instructions) {
159
185
  if (this.model && (this.config.mode === "abstractive" || this.config.mode === "structured")) try {
160
186
  if (this.config.mode === "structured") return formatSummaryAsText(generateStructuredSummary(messages), true);
161
- else return await this.llmAbstractiveSummary(messages);
187
+ else return await this.llmAbstractiveSummary(messages, instructions);
162
188
  } catch (err) {
163
189
  console.warn("[Compactor] LLM summarization failed, falling back to extractive", err);
164
190
  }
165
191
  return this.extractiveSummary(messages);
166
192
  }
167
- async llmAbstractiveSummary(messages) {
193
+ async llmAbstractiveSummary(messages, instructions) {
168
194
  if (!this.model) throw new Error("Model not available");
195
+ const conversation = this.formatMessages(messages);
169
196
  const prompt = `Summarize the following conversation in 2-3 concise sentences. Focus on:
170
197
  1. What the user was trying to accomplish
171
198
  2. Key decisions, outcomes, or solutions
172
199
  3. Any important context that should be preserved
173
-
200
+ ${instructions?.trim() ? `\nAdditional focus from the user:\n${instructions.trim()}\n` : ""}
174
201
  Conversation:
175
- ${this.formatMessages(messages)}
202
+ ${conversation}
176
203
 
177
204
  Summary:`;
178
205
  const controller = new AbortController();
@@ -1 +1 @@
1
- {"version":3,"file":"compaction.js","names":[],"sources":["../../../../src/agent/memory/compaction.ts"],"sourcesContent":["import type { AgentMessage } from '@mariozechner/pi-agent-core';\nimport { complete, type Model, type Api, type UserMessage } from '@mariozechner/pi-ai';\nimport { generateStructuredSummary, formatSummaryAsText, type ConversationSummary } from './summary-generator.js';\n\nexport interface CompactionResult {\n summary: string;\n firstKeptIndex: number;\n tokensBefore: number;\n tokensAfter: number;\n compacted: boolean;\n structuredSummary?: ConversationSummary;\n compactedUsage?: {\n input: number;\n output: number;\n total: number;\n cost?: number;\n };\n}\n\nexport interface CompactionConfig {\n enabled: boolean;\n mode: 'extractive' | 'abstractive' | 'structured';\n reserveTokens: number;\n triggerThreshold: number;\n minMessagesBeforeCompact: number;\n keepRecentMessages: number;\n summaryMaxTokens: number;\n evictionWindow: number;\n retentionWindow: number;\n preserveReasoning: boolean;\n accumulateUsage: boolean;\n}\n\nconst DEFAULT_CONFIG: CompactionConfig = {\n enabled: true,\n mode: 'abstractive',\n reserveTokens: 8000,\n triggerThreshold: 0.8,\n minMessagesBeforeCompact: 10,\n keepRecentMessages: 10,\n summaryMaxTokens: 500,\n evictionWindow: 0.2,\n retentionWindow: 6,\n preserveReasoning: true,\n accumulateUsage: true,\n};\n\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\nfunction estimateMessageTokens(msg: AgentMessage): number {\n let text = '';\n \n if (typeof msg.content === 'string') {\n text = msg.content;\n } else if (Array.isArray(msg.content)) {\n text = msg.content\n .filter(c => c.type === 'text')\n .map(c => (c as { text?: string }).text || '')\n .join('\\n');\n }\n \n return estimateTokens(text) + 10;\n}\n\n// Internal: Reasoning extraction utilities\ninterface ReasoningDetails {\n thinking?: string;\n signature?: string;\n}\n\nfunction extractLastReasoning(messages: AgentMessage[]): ReasoningDetails | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'assistant') {\n const rd = (msg as unknown as { reasoning_details?: ReasoningDetails }).reasoning_details;\n if (rd && (rd.thinking || rd.signature)) {\n return rd;\n }\n }\n }\n return null;\n}\n\n// Internal: Inject reasoning into first assistant message\nfunction injectReasoningIntoFirstAssistant(\n messages: AgentMessage[],\n reasoning: ReasoningDetails\n): void {\n for (const msg of messages) {\n if (msg.role === 'assistant') {\n const existing = (msg as unknown as { reasoning_details?: ReasoningDetails }).reasoning_details;\n if (!existing || (!existing.thinking && !existing.signature)) {\n (msg as unknown as { reasoning_details?: ReasoningDetails }).reasoning_details = reasoning;\n }\n break;\n }\n }\n}\n\n// Internal: Message usage tracking\ninterface MessageUsage {\n input: number;\n output: number;\n total: number;\n cost?: number;\n}\n\nexport function accumulateUsage(messages: AgentMessage[]): MessageUsage | undefined {\n let totalInput = 0;\n let totalOutput = 0;\n let totalCost = 0;\n let hasUsage = false;\n\n for (const msg of messages) {\n const usage = (msg as unknown as { usage?: MessageUsage }).usage;\n if (usage) {\n hasUsage = true;\n totalInput += usage.input || 0;\n totalOutput += usage.output || 0;\n totalCost += usage.cost || 0;\n }\n }\n\n if (!hasUsage) return undefined;\n\n return {\n input: totalInput,\n output: totalOutput,\n total: totalInput + totalOutput,\n cost: totalCost > 0 ? totalCost : undefined,\n };\n}\n\n// Internal: Droppable message filtering\ntype DroppableMessage = AgentMessage & {\n droppable?: boolean;\n};\n\nfunction filterDroppableMessages(messages: AgentMessage[]): AgentMessage[] {\n return messages.filter(msg => !(msg as DroppableMessage).droppable);\n}\n\nfunction findNthTurnFromEnd(messages: AgentMessage[], n: number): number {\n if (n <= 0) return messages.length;\n \n let turnsFound = 0;\n let lastRole: string | null = null;\n \n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n \n if (msg.role === 'user' && lastRole !== 'user') {\n turnsFound++;\n if (turnsFound === n) {\n return i;\n }\n }\n lastRole = msg.role;\n }\n \n return 0;\n}\n\n// Internal: Calculate compaction range\nfunction calculateCompactionRange(\n messages: AgentMessage[],\n config: CompactionConfig\n): { start: number; end: number } | null {\n const totalMessages = messages.length;\n \n if (totalMessages < config.minMessagesBeforeCompact) {\n return null;\n }\n \n const evictionEnd = Math.floor(totalMessages * config.evictionWindow);\n const retentionStart = findNthTurnFromEnd(messages, config.retentionWindow);\n const retentionEnd = retentionStart > 0 ? retentionStart - 1 : 0;\n const compactionEnd = Math.min(evictionEnd, retentionEnd);\n \n if (compactionEnd <= 1) {\n return null;\n }\n \n return { start: 0, end: compactionEnd };\n}\n\nexport class SessionCompactor {\n private config: CompactionConfig;\n \n constructor(\n config?: Partial<CompactionConfig>,\n private model?: Model<Api>\n ) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n needsCompaction(\n messages: AgentMessage[],\n contextWindow: number\n ): { needed: boolean; reason: string; usagePercent?: number } {\n if (!this.config.enabled) {\n return { needed: false, reason: 'disabled' };\n }\n\n if (messages.length < this.config.minMessagesBeforeCompact) {\n return { needed: false, reason: 'not_enough_messages' };\n }\n\n const totalTokens = this.estimateTotalTokens(messages);\n const usagePercent = totalTokens / contextWindow;\n\n if (usagePercent > this.config.triggerThreshold) {\n return { \n needed: true, \n reason: 'threshold_exceeded',\n usagePercent \n };\n }\n\n return { needed: false, reason: 'within_threshold' };\n }\n\n async compact(\n messages: AgentMessage[],\n _instructions?: string\n ): Promise<CompactionResult> {\n const effectiveMessages = filterDroppableMessages(messages);\n \n if (effectiveMessages.length < this.config.minMessagesBeforeCompact) {\n return {\n summary: '',\n firstKeptIndex: 0,\n tokensBefore: this.estimateTotalTokens(effectiveMessages),\n tokensAfter: this.estimateTotalTokens(effectiveMessages),\n compacted: false,\n };\n }\n\n const range = calculateCompactionRange(effectiveMessages, this.config);\n const keepRecent = range ? effectiveMessages.length - range.end : this.config.keepRecentMessages;\n const messagesToSummarize = range \n ? effectiveMessages.slice(0, range.end)\n : effectiveMessages.slice(0, -keepRecent);\n const keptMessages = range \n ? effectiveMessages.slice(range.end)\n : effectiveMessages.slice(-keepRecent);\n\n let preservedReasoning: ReasoningDetails | null = null;\n if (this.config.preserveReasoning) {\n preservedReasoning = extractLastReasoning(messagesToSummarize);\n if (preservedReasoning) {\n injectReasoningIntoFirstAssistant(keptMessages, preservedReasoning);\n }\n }\n\n const summary = await this.generateSummary(messagesToSummarize);\n const structuredSummary = this.config.mode === 'structured' \n ? generateStructuredSummary(messagesToSummarize)\n : undefined;\n\n const tokensBefore = this.estimateTotalTokens(effectiveMessages);\n const summaryTokens = estimateTokens(summary) + 20;\n const keptTokens = this.estimateTotalTokens(keptMessages);\n const tokensAfter = summaryTokens + keptTokens;\n\n let compactedUsage: MessageUsage | undefined;\n if (this.config.accumulateUsage) {\n compactedUsage = accumulateUsage(messagesToSummarize);\n }\n\n return {\n summary,\n firstKeptIndex: keptMessages.length > 0 ? effectiveMessages.length - keptMessages.length : 0,\n tokensBefore,\n tokensAfter,\n compacted: true,\n structuredSummary,\n compactedUsage,\n };\n }\n\n private async generateSummary(messages: AgentMessage[]): Promise<string> {\n if (this.model && (this.config.mode === 'abstractive' || this.config.mode === 'structured')) {\n try {\n if (this.config.mode === 'structured') {\n const structured = generateStructuredSummary(messages);\n return formatSummaryAsText(structured, true);\n } else {\n return await this.llmAbstractiveSummary(messages);\n }\n } catch (err) {\n console.warn('[Compactor] LLM summarization failed, falling back to extractive', err);\n }\n }\n\n return this.extractiveSummary(messages);\n }\n\n private async llmAbstractiveSummary(messages: AgentMessage[]): Promise<string> {\n if (!this.model) {\n throw new Error('Model not available');\n }\n\n const conversation = this.formatMessages(messages);\n const prompt = `Summarize the following conversation in 2-3 concise sentences. Focus on:\n1. What the user was trying to accomplish\n2. Key decisions, outcomes, or solutions\n3. Any important context that should be preserved\n\nConversation:\n${conversation}\n\nSummary:`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30000);\n\n try {\n const summaryMessage: UserMessage = { role: 'user', content: prompt, timestamp: Date.now() };\n const result = await complete(this.model, { \n messages: [summaryMessage]\n }, {\n maxTokens: this.config.summaryMaxTokens,\n temperature: 0.3,\n signal: controller.signal as any,\n });\n\n const text = Array.isArray(result.content)\n ? result.content.filter((c: any) => c.type === 'text').map((c: any) => c.text).join('')\n : '';\n\n return text.trim();\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error('LLM summarization timed out after 30 seconds');\n }\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private formatMessages(messages: AgentMessage[]): string {\n return messages\n .map(m => {\n const role = m.role;\n const content = typeof m.content === 'string' \n ? m.content \n : (m.content as Array<{ type: string; text?: string }>).filter(c => c.type === 'text').map(c => c.text || '').join('\\n');\n return `[${role}]: ${content}`;\n })\n .join('\\n\\n');\n }\n\n private extractiveSummary(messages: AgentMessage[]): string {\n const userMessages = messages\n .filter(m => m.role === 'user')\n .slice(-5)\n .map(m => {\n if (typeof m.content === 'string') return m.content;\n if (Array.isArray(m.content)) {\n return m.content\n .filter(c => c.type === 'text')\n .map(c => (c as { text?: string }).text || '')\n .join('\\n');\n }\n return '';\n })\n .filter(t => t.length > 0)\n .join('; ');\n\n return `Previous conversation covered: ${userMessages.slice(0, 300)}...`;\n }\n\n estimateTotalTokens(messages: AgentMessage[]): number {\n return messages.reduce((sum, msg) => sum + estimateMessageTokens(msg), 0);\n }\n\n applyCompaction(\n messages: AgentMessage[],\n result: CompactionResult\n ): AgentMessage[] {\n if (!result.compacted || !result.summary) {\n return messages;\n }\n\n const summaryMessage: AgentMessage & { usage?: MessageUsage } = {\n role: 'user',\n content: [{ type: 'text', text: `[Previous conversation summary]: ${result.summary}` }],\n timestamp: Date.now(),\n };\n\n if (result.compactedUsage) {\n summaryMessage.usage = result.compactedUsage;\n }\n\n const keptMessages = messages.slice(result.firstKeptIndex);\n return [summaryMessage, ...keptMessages];\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"compaction.js","names":[],"sources":["../../../../src/agent/memory/compaction.ts"],"sourcesContent":["import type { AgentMessage } from '@mariozechner/pi-agent-core';\nimport { complete, type Model, type Api, type UserMessage } from '@mariozechner/pi-ai';\nimport { generateStructuredSummary, formatSummaryAsText, type ConversationSummary } from './summary-generator.js';\n\nexport interface CompactionResult {\n summary: string;\n firstKeptIndex: number;\n tokensBefore: number;\n tokensAfter: number;\n compacted: boolean;\n structuredSummary?: ConversationSummary;\n compactedUsage?: {\n input: number;\n output: number;\n total: number;\n cost?: number;\n };\n}\n\nexport interface CompactionConfig {\n enabled: boolean;\n mode: 'extractive' | 'abstractive' | 'structured';\n reserveTokens: number;\n triggerThreshold: number;\n minMessagesBeforeCompact: number;\n keepRecentMessages: number;\n summaryMaxTokens: number;\n evictionWindow: number;\n retentionWindow: number;\n preserveReasoning: boolean;\n accumulateUsage: boolean;\n}\n\nconst DEFAULT_CONFIG: CompactionConfig = {\n enabled: true,\n mode: 'abstractive',\n reserveTokens: 8000,\n triggerThreshold: 0.8,\n minMessagesBeforeCompact: 10,\n keepRecentMessages: 10,\n summaryMaxTokens: 500,\n evictionWindow: 0.2,\n retentionWindow: 6,\n preserveReasoning: true,\n accumulateUsage: true,\n};\n\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\nfunction estimateMessageTokens(msg: AgentMessage): number {\n let text = '';\n \n if (typeof msg.content === 'string') {\n text = msg.content;\n } else if (Array.isArray(msg.content)) {\n text = msg.content\n .filter(c => c.type === 'text')\n .map(c => (c as { text?: string }).text || '')\n .join('\\n');\n }\n \n return estimateTokens(text) + 10;\n}\n\n// Internal: Reasoning extraction utilities\ninterface ReasoningDetails {\n thinking?: string;\n signature?: string;\n}\n\nfunction extractLastReasoning(messages: AgentMessage[]): ReasoningDetails | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'assistant') {\n const rd = (msg as unknown as { reasoning_details?: ReasoningDetails }).reasoning_details;\n if (rd && (rd.thinking || rd.signature)) {\n return rd;\n }\n }\n }\n return null;\n}\n\n// Internal: Inject reasoning into first assistant message\nfunction injectReasoningIntoFirstAssistant(\n messages: AgentMessage[],\n reasoning: ReasoningDetails\n): void {\n for (const msg of messages) {\n if (msg.role === 'assistant') {\n const existing = (msg as unknown as { reasoning_details?: ReasoningDetails }).reasoning_details;\n if (!existing || (!existing.thinking && !existing.signature)) {\n (msg as unknown as { reasoning_details?: ReasoningDetails }).reasoning_details = reasoning;\n }\n break;\n }\n }\n}\n\n// Internal: Message usage tracking\ninterface MessageUsage {\n input: number;\n output: number;\n total: number;\n cost?: number;\n}\n\nexport function accumulateUsage(messages: AgentMessage[]): MessageUsage | undefined {\n let totalInput = 0;\n let totalOutput = 0;\n let totalCost = 0;\n let hasUsage = false;\n\n for (const msg of messages) {\n const usage = (msg as unknown as { usage?: MessageUsage }).usage;\n if (usage) {\n hasUsage = true;\n totalInput += usage.input || 0;\n totalOutput += usage.output || 0;\n totalCost += usage.cost || 0;\n }\n }\n\n if (!hasUsage) return undefined;\n\n return {\n input: totalInput,\n output: totalOutput,\n total: totalInput + totalOutput,\n cost: totalCost > 0 ? totalCost : undefined,\n };\n}\n\n// Internal: Droppable message filtering\ntype DroppableMessage = AgentMessage & {\n droppable?: boolean;\n};\n\nfunction filterDroppableMessages(messages: AgentMessage[]): AgentMessage[] {\n return messages.filter(msg => !(msg as DroppableMessage).droppable);\n}\n\nfunction findNthTurnFromEnd(messages: AgentMessage[], n: number): number {\n if (n <= 0) return messages.length;\n \n let turnsFound = 0;\n let lastRole: string | null = null;\n \n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n \n if (msg.role === 'user' && lastRole !== 'user') {\n turnsFound++;\n if (turnsFound === n) {\n return i;\n }\n }\n lastRole = msg.role;\n }\n \n return 0;\n}\n\n// Internal: Calculate compaction range\nfunction calculateCompactionRange(\n messages: AgentMessage[],\n config: CompactionConfig\n): { start: number; end: number } | null {\n const totalMessages = messages.length;\n \n if (totalMessages < config.minMessagesBeforeCompact) {\n return null;\n }\n \n const evictionEnd = Math.floor(totalMessages * config.evictionWindow);\n const retentionStart = findNthTurnFromEnd(messages, config.retentionWindow);\n const retentionEnd = retentionStart > 0 ? retentionStart - 1 : 0;\n const compactionEnd = Math.min(evictionEnd, retentionEnd);\n \n if (compactionEnd <= 1) {\n return null;\n }\n \n return { start: 0, end: compactionEnd };\n}\n\nexport class SessionCompactor {\n private config: CompactionConfig;\n \n constructor(\n config?: Partial<CompactionConfig>,\n private model?: Model<Api>\n ) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n needsCompaction(\n messages: AgentMessage[],\n contextWindow: number\n ): { needed: boolean; reason: string; usagePercent?: number } {\n if (!this.config.enabled) {\n return { needed: false, reason: 'disabled' };\n }\n\n if (messages.length < this.config.minMessagesBeforeCompact) {\n return { needed: false, reason: 'not_enough_messages' };\n }\n\n const totalTokens = this.estimateTotalTokens(messages);\n const usagePercent = totalTokens / contextWindow;\n\n if (usagePercent > this.config.triggerThreshold) {\n return { \n needed: true, \n reason: 'threshold_exceeded',\n usagePercent \n };\n }\n\n return { needed: false, reason: 'within_threshold' };\n }\n\n async compact(\n messages: AgentMessage[],\n instructions?: string,\n force?: boolean,\n ): Promise<CompactionResult> {\n const effectiveMessages = filterDroppableMessages(messages);\n\n const minRequired = this.config.minMessagesBeforeCompact;\n if (!force && effectiveMessages.length < minRequired) {\n return {\n summary: '',\n firstKeptIndex: 0,\n tokensBefore: this.estimateTotalTokens(effectiveMessages),\n tokensAfter: this.estimateTotalTokens(effectiveMessages),\n compacted: false,\n };\n }\n\n if (force && effectiveMessages.length < 2) {\n return {\n summary: '',\n firstKeptIndex: 0,\n tokensBefore: this.estimateTotalTokens(effectiveMessages),\n tokensAfter: this.estimateTotalTokens(effectiveMessages),\n compacted: false,\n };\n }\n\n const range = calculateCompactionRange(effectiveMessages, this.config);\n let messagesToSummarize: AgentMessage[];\n let keptMessages: AgentMessage[];\n\n if (range) {\n messagesToSummarize = effectiveMessages.slice(0, range.end);\n keptMessages = effectiveMessages.slice(range.end);\n } else if (force) {\n const keep = Math.min(\n this.config.keepRecentMessages,\n Math.max(1, effectiveMessages.length - 1),\n );\n messagesToSummarize = effectiveMessages.slice(0, effectiveMessages.length - keep);\n keptMessages = effectiveMessages.slice(-keep);\n } else {\n const keepRecent = this.config.keepRecentMessages;\n messagesToSummarize = effectiveMessages.slice(0, -keepRecent);\n keptMessages = effectiveMessages.slice(-keepRecent);\n }\n\n if (messagesToSummarize.length === 0) {\n return {\n summary: '',\n firstKeptIndex: 0,\n tokensBefore: this.estimateTotalTokens(effectiveMessages),\n tokensAfter: this.estimateTotalTokens(effectiveMessages),\n compacted: false,\n };\n }\n\n let preservedReasoning: ReasoningDetails | null = null;\n if (this.config.preserveReasoning) {\n preservedReasoning = extractLastReasoning(messagesToSummarize);\n if (preservedReasoning) {\n injectReasoningIntoFirstAssistant(keptMessages, preservedReasoning);\n }\n }\n\n const summary = await this.generateSummary(messagesToSummarize, instructions);\n const structuredSummary = this.config.mode === 'structured' \n ? generateStructuredSummary(messagesToSummarize)\n : undefined;\n\n const tokensBefore = this.estimateTotalTokens(effectiveMessages);\n const summaryTokens = estimateTokens(summary) + 20;\n const keptTokens = this.estimateTotalTokens(keptMessages);\n const tokensAfter = summaryTokens + keptTokens;\n\n let compactedUsage: MessageUsage | undefined;\n if (this.config.accumulateUsage) {\n compactedUsage = accumulateUsage(messagesToSummarize);\n }\n\n return {\n summary,\n firstKeptIndex: keptMessages.length > 0 ? effectiveMessages.length - keptMessages.length : 0,\n tokensBefore,\n tokensAfter,\n compacted: true,\n structuredSummary,\n compactedUsage,\n };\n }\n\n private async generateSummary(messages: AgentMessage[], instructions?: string): Promise<string> {\n if (this.model && (this.config.mode === 'abstractive' || this.config.mode === 'structured')) {\n try {\n if (this.config.mode === 'structured') {\n const structured = generateStructuredSummary(messages);\n return formatSummaryAsText(structured, true);\n } else {\n return await this.llmAbstractiveSummary(messages, instructions);\n }\n } catch (err) {\n console.warn('[Compactor] LLM summarization failed, falling back to extractive', err);\n }\n }\n\n return this.extractiveSummary(messages);\n }\n\n private async llmAbstractiveSummary(messages: AgentMessage[], instructions?: string): Promise<string> {\n if (!this.model) {\n throw new Error('Model not available');\n }\n\n const conversation = this.formatMessages(messages);\n const extra = instructions?.trim()\n ? `\\nAdditional focus from the user:\\n${instructions.trim()}\\n`\n : '';\n const prompt = `Summarize the following conversation in 2-3 concise sentences. Focus on:\n1. What the user was trying to accomplish\n2. Key decisions, outcomes, or solutions\n3. Any important context that should be preserved\n${extra}\nConversation:\n${conversation}\n\nSummary:`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30000);\n\n try {\n const summaryMessage: UserMessage = { role: 'user', content: prompt, timestamp: Date.now() };\n const result = await complete(this.model, { \n messages: [summaryMessage]\n }, {\n maxTokens: this.config.summaryMaxTokens,\n temperature: 0.3,\n signal: controller.signal as any,\n });\n\n const text = Array.isArray(result.content)\n ? result.content.filter((c: any) => c.type === 'text').map((c: any) => c.text).join('')\n : '';\n\n return text.trim();\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error('LLM summarization timed out after 30 seconds');\n }\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private formatMessages(messages: AgentMessage[]): string {\n return messages\n .map(m => {\n const role = m.role;\n const content = typeof m.content === 'string' \n ? m.content \n : (m.content as Array<{ type: string; text?: string }>).filter(c => c.type === 'text').map(c => c.text || '').join('\\n');\n return `[${role}]: ${content}`;\n })\n .join('\\n\\n');\n }\n\n private extractiveSummary(messages: AgentMessage[]): string {\n const userMessages = messages\n .filter(m => m.role === 'user')\n .slice(-5)\n .map(m => {\n if (typeof m.content === 'string') return m.content;\n if (Array.isArray(m.content)) {\n return m.content\n .filter(c => c.type === 'text')\n .map(c => (c as { text?: string }).text || '')\n .join('\\n');\n }\n return '';\n })\n .filter(t => t.length > 0)\n .join('; ');\n\n return `Previous conversation covered: ${userMessages.slice(0, 300)}...`;\n }\n\n estimateTotalTokens(messages: AgentMessage[]): number {\n return messages.reduce((sum, msg) => sum + estimateMessageTokens(msg), 0);\n }\n\n applyCompaction(\n messages: AgentMessage[],\n result: CompactionResult\n ): AgentMessage[] {\n if (!result.compacted || !result.summary) {\n return messages;\n }\n\n const summaryMessage: AgentMessage & { usage?: MessageUsage } = {\n role: 'user',\n content: [{ type: 'text', text: `[Previous conversation summary]: ${result.summary}` }],\n timestamp: Date.now(),\n };\n\n if (result.compactedUsage) {\n summaryMessage.usage = result.compactedUsage;\n }\n\n const keptMessages = messages.slice(result.firstKeptIndex);\n return [summaryMessage, ...keptMessages];\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { dirname, join } from "node:path";
2
- import { fileURLToPath } from "node:url";
3
2
  import { readdir, stat } from "node:fs/promises";
3
+ import { fileURLToPath } from "node:url";
4
4
  //#region src/agent/memory/plugin-discovery.ts
5
5
  async function discoverMemoryPlugins() {
6
6
  const pluginsDir = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "plugins", "memory");
@@ -7,6 +7,7 @@ import type { MessageBus } from '../../infra/bus/index.js';
7
7
  import type { Config } from '../../config/schema.js';
8
8
  import type { SessionConfigStore, SessionStore } from '../../session/index.js';
9
9
  import type { ThinkLevel } from '../transcript/thinking-types.js';
10
+ import type { CompactionResult } from '../memory/compaction.js';
10
11
  export interface CommandContext {
11
12
  sessionKey: string;
12
13
  channel: string;
@@ -27,6 +28,15 @@ export interface CommandHandlerConfig {
27
28
  invalidateAgentSession?: (sessionKey: string) => void;
28
29
  /** Cancel streaming preview + in-flight LLM work for this session (e.g. /abort) */
29
30
  abortSessionTurn?: (sessionKey: string) => Promise<void>;
31
+ compactSession?: (sessionKey: string, options?: {
32
+ instructions?: string;
33
+ force?: boolean;
34
+ }) => Promise<CompactionResult>;
35
+ btwQuery?: (sessionKey: string, question: string) => Promise<{
36
+ text: string;
37
+ error?: string;
38
+ }>;
39
+ getSessionContextReport?: (sessionKey: string, mode: 'list' | 'detail' | 'json') => Promise<string>;
30
40
  }
31
41
  export declare class CommandHandler {
32
42
  private config;
@@ -38,6 +48,9 @@ export declare class CommandHandler {
38
48
  private switchModelForSession;
39
49
  private invalidateAgentSession?;
40
50
  private abortSessionTurn?;
51
+ private compactSession?;
52
+ private btwQuery?;
53
+ private getSessionContextReport?;
41
54
  constructor(handlerConfig: CommandHandlerConfig);
42
55
  /** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */
43
56
  updateAgentConfig(config: Config): void;
@@ -18,6 +18,9 @@ var CommandHandler = class {
18
18
  switchModelForSession;
19
19
  invalidateAgentSession;
20
20
  abortSessionTurn;
21
+ compactSession;
22
+ btwQuery;
23
+ getSessionContextReport;
21
24
  constructor(handlerConfig) {
22
25
  this.config = handlerConfig.config;
23
26
  this.bus = handlerConfig.bus;
@@ -28,6 +31,9 @@ var CommandHandler = class {
28
31
  this.switchModelForSession = handlerConfig.switchModelForSession;
29
32
  this.invalidateAgentSession = handlerConfig.invalidateAgentSession;
30
33
  this.abortSessionTurn = handlerConfig.abortSessionTurn;
34
+ this.compactSession = handlerConfig.compactSession;
35
+ this.btwQuery = handlerConfig.btwQuery;
36
+ this.getSessionContextReport = handlerConfig.getSessionContextReport;
31
37
  }
32
38
  /** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */
33
39
  updateAgentConfig(config) {
@@ -106,7 +112,10 @@ var CommandHandler = class {
106
112
  invalidateAgentSession: this.invalidateAgentSession,
107
113
  abortCurrentTurn: this.abortSessionTurn ? async () => {
108
114
  await this.abortSessionTurn(context.sessionKey);
109
- } : void 0
115
+ } : void 0,
116
+ compactSession: this.compactSession,
117
+ btwQuery: this.btwQuery,
118
+ getSessionContextReport: this.getSessionContextReport
110
119
  });
111
120
  const result = await commandRegistry.execute(commandName, cmdCtx, args);
112
121
  if (result.content) await this.bus.publishOutbound({
@@ -203,7 +212,10 @@ var CommandHandler = class {
203
212
  invalidateAgentSession: this.invalidateAgentSession,
204
213
  abortCurrentTurn: this.abortSessionTurn ? async () => {
205
214
  await this.abortSessionTurn(context.sessionKey);
206
- } : void 0
215
+ } : void 0,
216
+ compactSession: this.compactSession,
217
+ btwQuery: this.btwQuery,
218
+ getSessionContextReport: this.getSessionContextReport
207
219
  });
208
220
  const result = await commandRegistry.execute(commandName, wrapped, args);
209
221
  if (result.content) {
@@ -1 +1 @@
1
- {"version":3,"file":"command-handler.js","names":[],"sources":["../../../../src/agent/messaging/command-handler.ts"],"sourcesContent":["/**\n * Command Handler - Parses and executes commands\n *\n * Handles command execution using the unified command system.\n */\n\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { isProviderConfiguredSync } from '../../providers/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { commandRegistry, createCommandContext } from '../../chat-commands/index.js';\nimport { getAllProviders, getModelsByProvider, getProviderDisplayName } from '../../providers/index.js';\n\nconst log = createLogger('CommandHandler');\n\nexport interface CommandContext {\n sessionKey: string;\n channel: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n}\n\nexport interface CommandHandlerConfig {\n config: Config;\n bus: MessageBus;\n sessionStore: SessionStore;\n sessionConfigStore?: SessionConfigStore;\n /** After /think persists, sync pi-agent */\n applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n getCurrentModel: () => string;\n switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n /** Drop in-memory agent after session file is cleared (e.g. /new) */\n invalidateAgentSession?: (sessionKey: string) => void;\n /** Cancel streaming preview + in-flight LLM work for this session (e.g. /abort) */\n abortSessionTurn?: (sessionKey: string) => Promise<void>;\n}\n\nexport class CommandHandler {\n private config: Config;\n private bus: MessageBus;\n private sessionStore: SessionStore;\n private sessionConfigStore?: SessionConfigStore;\n private applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n private getCurrentModel: () => string;\n private switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n private invalidateAgentSession?: (sessionKey: string) => void;\n private abortSessionTurn?: (sessionKey: string) => Promise<void>;\n\n constructor(handlerConfig: CommandHandlerConfig) {\n this.config = handlerConfig.config;\n this.bus = handlerConfig.bus;\n this.sessionStore = handlerConfig.sessionStore;\n this.sessionConfigStore = handlerConfig.sessionConfigStore;\n this.applySessionThinkingLevel = handlerConfig.applySessionThinkingLevel;\n this.getCurrentModel = handlerConfig.getCurrentModel;\n this.switchModelForSession = handlerConfig.switchModelForSession;\n this.invalidateAgentSession = handlerConfig.invalidateAgentSession;\n this.abortSessionTurn = handlerConfig.abortSessionTurn;\n }\n\n /** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */\n updateAgentConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Execute a command using the unified command system\n */\n async executeCommand(\n commandName: string,\n args: string,\n context: CommandContext\n ): Promise<boolean> {\n // Check if command exists\n if (!commandRegistry.has(commandName)) {\n return false;\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command via new system');\n\n // Create command context\n const cmdCtx = createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as any;\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n });\n\n const result = await commandRegistry.execute(commandName, cmdCtx, args);\n\n if (result.content) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n return true;\n }\n\n /**\n * Run command and return all user-visible text (ctx.reply + result.content) for SSE/CLI.\n * Same bus side effects as {@link executeCommand}.\n */\n async executeCommandAndAggregateReply(\n commandName: string,\n args: string,\n context: CommandContext,\n ): Promise<{ handled: boolean; aggregatedText: string }> {\n if (!commandRegistry.has(commandName)) {\n return { handled: false, aggregatedText: '' };\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command (aggregate reply)');\n\n const { aggregatedText } = await this.runRegistryExecuteWithCapture(args, commandName, context);\n\n return { handled: true, aggregatedText };\n }\n\n private async runRegistryExecuteWithCapture(\n args: string,\n commandName: string,\n context: CommandContext,\n ): Promise<{ aggregatedText: string }> {\n const segments: string[] = [];\n\n const wrapped = createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n segments.push(text);\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as { input?: number; output?: number };\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n });\n\n const result = await commandRegistry.execute(commandName, wrapped, args);\n\n if (result.content) {\n segments.push(result.content);\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n const aggregatedText = segments.filter((s) => s && s.trim()).join('\\n\\n');\n return { aggregatedText };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"command-handler.js","names":[],"sources":["../../../../src/agent/messaging/command-handler.ts"],"sourcesContent":["/**\n * Command Handler - Parses and executes commands\n *\n * Handles command execution using the unified command system.\n */\n\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { isProviderConfiguredSync } from '../../providers/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { commandRegistry, createCommandContext } from '../../chat-commands/index.js';\nimport { getAllProviders, getModelsByProvider, getProviderDisplayName } from '../../providers/index.js';\n\nconst log = createLogger('CommandHandler');\n\nexport interface CommandContext {\n sessionKey: string;\n channel: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n}\n\nexport interface CommandHandlerConfig {\n config: Config;\n bus: MessageBus;\n sessionStore: SessionStore;\n sessionConfigStore?: SessionConfigStore;\n /** After /think persists, sync pi-agent */\n applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n getCurrentModel: () => string;\n switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n /** Drop in-memory agent after session file is cleared (e.g. /new) */\n invalidateAgentSession?: (sessionKey: string) => void;\n /** Cancel streaming preview + in-flight LLM work for this session (e.g. /abort) */\n abortSessionTurn?: (sessionKey: string) => Promise<void>;\n\n compactSession?: (\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ) => Promise<CompactionResult>;\n\n btwQuery?: (sessionKey: string, question: string) => Promise<{ text: string; error?: string }>;\n\n getSessionContextReport?: (\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ) => Promise<string>;\n}\n\nexport class CommandHandler {\n private config: Config;\n private bus: MessageBus;\n private sessionStore: SessionStore;\n private sessionConfigStore?: SessionConfigStore;\n private applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n private getCurrentModel: () => string;\n private switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n private invalidateAgentSession?: (sessionKey: string) => void;\n private abortSessionTurn?: (sessionKey: string) => Promise<void>;\n private compactSession?: CommandHandlerConfig['compactSession'];\n private btwQuery?: CommandHandlerConfig['btwQuery'];\n private getSessionContextReport?: CommandHandlerConfig['getSessionContextReport'];\n\n constructor(handlerConfig: CommandHandlerConfig) {\n this.config = handlerConfig.config;\n this.bus = handlerConfig.bus;\n this.sessionStore = handlerConfig.sessionStore;\n this.sessionConfigStore = handlerConfig.sessionConfigStore;\n this.applySessionThinkingLevel = handlerConfig.applySessionThinkingLevel;\n this.getCurrentModel = handlerConfig.getCurrentModel;\n this.switchModelForSession = handlerConfig.switchModelForSession;\n this.invalidateAgentSession = handlerConfig.invalidateAgentSession;\n this.abortSessionTurn = handlerConfig.abortSessionTurn;\n this.compactSession = handlerConfig.compactSession;\n this.btwQuery = handlerConfig.btwQuery;\n this.getSessionContextReport = handlerConfig.getSessionContextReport;\n }\n\n /** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */\n updateAgentConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Execute a command using the unified command system\n */\n async executeCommand(\n commandName: string,\n args: string,\n context: CommandContext\n ): Promise<boolean> {\n // Check if command exists\n if (!commandRegistry.has(commandName)) {\n return false;\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command via new system');\n\n // Create command context\n const cmdCtx = createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as any;\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n\n compactSession: this.compactSession,\n btwQuery: this.btwQuery,\n getSessionContextReport: this.getSessionContextReport,\n });\n\n const result = await commandRegistry.execute(commandName, cmdCtx, args);\n\n if (result.content) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n return true;\n }\n\n /**\n * Run command and return all user-visible text (ctx.reply + result.content) for SSE/CLI.\n * Same bus side effects as {@link executeCommand}.\n */\n async executeCommandAndAggregateReply(\n commandName: string,\n args: string,\n context: CommandContext,\n ): Promise<{ handled: boolean; aggregatedText: string }> {\n if (!commandRegistry.has(commandName)) {\n return { handled: false, aggregatedText: '' };\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command (aggregate reply)');\n\n const { aggregatedText } = await this.runRegistryExecuteWithCapture(args, commandName, context);\n\n return { handled: true, aggregatedText };\n }\n\n private async runRegistryExecuteWithCapture(\n args: string,\n commandName: string,\n context: CommandContext,\n ): Promise<{ aggregatedText: string }> {\n const segments: string[] = [];\n\n const wrapped = createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n segments.push(text);\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as { input?: number; output?: number };\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n\n compactSession: this.compactSession,\n btwQuery: this.btwQuery,\n getSessionContextReport: this.getSessionContextReport,\n });\n\n const result = await commandRegistry.execute(commandName, wrapped, args);\n\n if (result.content) {\n segments.push(result.content);\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n const aggregatedText = segments.filter((s) => s && s.trim()).join('\\n\\n');\n return { aggregatedText };\n }\n}\n"],"mappings":"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"}
@@ -6,6 +6,7 @@ import { cleanTrailingErrors, sanitizeMessages } from "../memory/message-sanitiz
6
6
  import { resolveEffectiveThinkingLevel } from "../../session/thinking-resolve.js";
7
7
  import { tryApplySessionTranscriptHygiene, tryApplySessionTranscriptHygieneForPersistence } from "../transcript/transcript-hygiene.js";
8
8
  import { runAgentTurnWithModelFallbacks } from "./run-agent-turn-with-fallbacks.js";
9
+ import { expandAtFileMentionsInPlainText } from "../context/expand-at-file-mentions.js";
9
10
  import { resolveInboundImageContentParts } from "../image/inbound-image-handling.js";
10
11
  //#region src/agent/orchestration/agent-orchestrator.ts
11
12
  init_logger();
@@ -148,7 +149,11 @@ var AgentOrchestrator = class {
148
149
  */
149
150
  async buildUserMessage(msg, sessionKey) {
150
151
  const storageRootAbs = this.getAgentInternalStorageRootForSession(sessionKey);
151
- const textBody = msg.content.trimStart().startsWith("/skill:") ? this.agentManager.expandSkillUserText(msg.content) : msg.content;
152
+ let textBody = msg.content.trimStart().startsWith("/skill:") ? this.agentManager.expandSkillUserText(msg.content) : msg.content;
153
+ if (/@file:/.test(textBody)) {
154
+ const root = this.agentManager.getResolvedWorkspaceForSession(sessionKey);
155
+ textBody = await expandAtFileMentionsInPlainText(textBody, root);
156
+ }
152
157
  if (!msg.attachments || msg.attachments.length === 0) return {
153
158
  role: "user",
154
159
  content: textBody,