@xopcai/xopc 0.0.94 → 0.0.96

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 (433) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  4. package/dist/extensions/telegram/src/plugin.js +1 -1
  5. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  6. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  7. package/dist/extensions/telegram/xopc.extension.json +1 -1
  8. package/dist/extensions/weixin/src/api/api.js +2 -2
  9. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  10. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  11. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  12. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  13. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  14. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  15. package/dist/extensions/weixin/src/plugin.js +1 -1
  16. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  17. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  18. package/dist/gateway/static/root/assets/{agents-OqhbJkMf.js → agents-DmIuSaOE.js} +3 -3
  19. package/dist/gateway/static/root/assets/{apps-page-OHXW9XP8.js → apps-page-DFcHBxLw.js} +1 -1
  20. package/dist/gateway/static/root/assets/{channels-settings-4N2R-jof.js → channels-settings-DDUf55C5.js} +1 -1
  21. package/dist/gateway/static/root/assets/{channels-status-swr-Bv6f9kDq.js → channels-status-swr-BxF-_nzD.js} +1 -1
  22. package/dist/gateway/static/root/assets/{cron-api-BtaQaHJq.js → cron-api-DylQtnb_.js} +1 -1
  23. package/dist/gateway/static/root/assets/{cron-page-Dah32HJK.js → cron-page-BO0d9Pf-.js} +1 -1
  24. package/dist/gateway/static/root/assets/{dist-BJfD9Qvs.js → dist-BskF0qDS.js} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-debug-page-DnYuMzmH.js → extension-debug-page-BcZdTdjJ.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-CJfc-6XV.js → extension-page-D2iuDa1D.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-BxdfYQMG.js → extension-settings-page-BKpQCgLc.js} +1 -1
  28. package/dist/gateway/static/root/assets/{fetch-B0aeeY0q.js → fetch-CtNDpjij.js} +1 -1
  29. package/dist/gateway/static/root/assets/{field-primitives-DOLHwowi.js → field-primitives-2PekrGZF.js} +1 -1
  30. package/dist/gateway/static/root/assets/{heartbeat-config-api-Bj2INAf5.js → heartbeat-config-api-D3D7SW8A.js} +1 -1
  31. package/dist/gateway/static/root/assets/index-BvEhL9RQ.css +1 -0
  32. package/dist/gateway/static/root/assets/{index-DuQ1XPoA.js → index-Db9fd_X4.js} +74 -74
  33. package/dist/gateway/static/root/assets/{logs-page-AsOgLNJE.js → logs-page-B3I1a26m.js} +1 -1
  34. package/dist/gateway/static/root/assets/{note-detail-page-24J4mVP-.js → note-detail-page-BOizhtJ8.js} +54 -53
  35. package/dist/gateway/static/root/assets/{note-detail-page-B91pLkEI.css → note-detail-page-D4ZIVQbk.css} +1 -1
  36. package/dist/gateway/static/root/assets/{note-time-JBszYV3s.js → note-time-CjUGtqKr.js} +1 -1
  37. package/dist/gateway/static/root/assets/notes-page-BB8-I0Of.js +1 -0
  38. package/dist/gateway/static/root/assets/{sessions-page-DX9huWsA.js → sessions-page-BcH-1K9i.js} +1 -1
  39. package/dist/gateway/static/root/assets/{settings-advanced-gate-DWvhsTuz.js → settings-advanced-gate-Czn8nnjN.js} +2 -2
  40. package/dist/gateway/static/root/assets/{settings-form-section-CxMjaMiy.js → settings-form-section-ZZWDwgVe.js} +1 -1
  41. package/dist/gateway/static/root/assets/settings-page-BX8c_zrN.js +3 -0
  42. package/dist/gateway/static/root/assets/share-preview-page-Ch3_6Qah.js +2 -0
  43. package/dist/gateway/static/root/assets/{skills-page-CGKGKfwe.js → skills-page-WO0bbJ54.js} +1 -1
  44. package/dist/gateway/static/root/assets/{theme-store-Cg_SuBw0.js → theme-store-XxRFRZDX.js} +1 -1
  45. package/dist/gateway/static/root/assets/url-6zpynn1R.js +3 -0
  46. package/dist/gateway/static/root/assets/{utils-BmlcxR2j.js → utils-uTYKh54l.js} +1 -1
  47. package/dist/gateway/static/root/assets/{voice-api-key-field-DaGm2N4J.js → voice-api-key-field-BIAYHRs-.js} +1 -1
  48. package/dist/gateway/static/root/assets/{workflow-page.utils-D0vsIGHD.js → workflow-page.utils-BbWhqD36.js} +1 -1
  49. package/dist/gateway/static/root/assets/{workflows-page-BFCrD3nw.js → workflows-page-D4RIF7E1.js} +2 -2
  50. package/dist/gateway/static/root/index.html +5 -5
  51. package/dist/package.js +1 -1
  52. package/dist/src/agent/agent-manager.js +15 -9
  53. package/dist/src/agent/agent-manager.js.map +1 -1
  54. package/dist/src/agent/agent-scope.d.ts +0 -1
  55. package/dist/src/agent/agent-scope.js +2 -5
  56. package/dist/src/agent/agent-scope.js.map +1 -1
  57. package/dist/src/agent/bootstrap/bootstrap-cache.d.ts +3 -0
  58. package/dist/src/agent/bootstrap/bootstrap-cache.js +13 -3
  59. package/dist/src/agent/bootstrap/bootstrap-cache.js.map +1 -1
  60. package/dist/src/agent/bootstrap/bootstrap-files.d.ts +6 -0
  61. package/dist/src/agent/bootstrap/bootstrap-files.js +35 -12
  62. package/dist/src/agent/bootstrap/bootstrap-files.js.map +1 -1
  63. package/dist/src/agent/bootstrap/load-bootstrap-files.d.ts +5 -2
  64. package/dist/src/agent/bootstrap/load-bootstrap-files.js +12 -3
  65. package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
  66. package/dist/src/agent/context/workspace-seed.js +8 -4
  67. package/dist/src/agent/context/workspace-seed.js.map +1 -1
  68. package/dist/src/agent/context/workspace-state.d.ts +52 -0
  69. package/dist/src/agent/context/workspace-state.js +101 -0
  70. package/dist/src/agent/context/workspace-state.js.map +1 -0
  71. package/dist/src/agent/embedded/index.d.ts +2 -2
  72. package/dist/src/agent/embedded/index.js +3 -3
  73. package/dist/src/agent/embedded/run-turn.js +0 -3
  74. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  75. package/dist/src/agent/embedded/session-manager-init.d.ts +0 -17
  76. package/dist/src/agent/embedded/session-manager-init.js +1 -36
  77. package/dist/src/agent/embedded/session-manager-init.js.map +1 -1
  78. package/dist/src/agent/embedded/session-runner.d.ts +3 -12
  79. package/dist/src/agent/embedded/session-runner.js +12 -26
  80. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  81. package/dist/src/agent/embedded/session-tool-result-guard.js +2 -4
  82. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  83. package/dist/src/agent/embedded/sqlite-hydrating-session-manager.d.ts +10 -0
  84. package/dist/src/agent/embedded/sqlite-hydrating-session-manager.js +34 -0
  85. package/dist/src/agent/embedded/sqlite-hydrating-session-manager.js.map +1 -0
  86. package/dist/src/agent/goals/goal-run-store.js +4 -4
  87. package/dist/src/agent/goals/persistent-goal-service.js +8 -15
  88. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  89. package/dist/src/agent/goals/post-turn.js +2 -2
  90. package/dist/src/agent/image/load-image-media.js +2 -2
  91. package/dist/src/agent/ipc/bus.js +1 -1
  92. package/dist/src/agent/ipc/inbox.js +2 -2
  93. package/dist/src/agent/ipc/socket.js +1 -1
  94. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  95. package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -2
  96. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  97. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  98. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  99. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  100. package/dist/src/agent/memory/dreaming/events.js +1 -1
  101. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  102. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  103. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  104. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  105. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  106. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  107. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  108. package/dist/src/agent/models/manager.js +1 -1
  109. package/dist/src/agent/prompt/memory/index.d.ts +1 -0
  110. package/dist/src/agent/prompt/memory/index.js +34 -80
  111. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  112. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  113. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  114. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  115. package/dist/src/agent/sandbox/path-policy.js +2 -2
  116. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  117. package/dist/src/agent/service/process-direct-one-shot.js +8 -17
  118. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  119. package/dist/src/agent/service/process-direct-streaming.js +14 -23
  120. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  121. package/dist/src/agent/service.js +7 -11
  122. package/dist/src/agent/service.js.map +1 -1
  123. package/dist/src/agent/session/session-inspector.js +1 -1
  124. package/dist/src/agent/skills/config.js +1 -1
  125. package/dist/src/agent/skills/hub-hash.js +2 -2
  126. package/dist/src/agent/skills/hub-lock.js +1 -1
  127. package/dist/src/agent/skills/hub-pull.js +3 -3
  128. package/dist/src/agent/skills/index.js +1 -1
  129. package/dist/src/agent/skills/managed-store.js +1 -1
  130. package/dist/src/agent/skills/scanner.js +1 -1
  131. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  132. package/dist/src/agent/skills/skill-manager.js +1 -1
  133. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  134. package/dist/src/agent/tools/factory.js +1 -1
  135. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  136. package/dist/src/agent/tools/index.d.ts +0 -1
  137. package/dist/src/agent/tools/index.js +1 -2
  138. package/dist/src/agent/tools/send-media.js +1 -1
  139. package/dist/src/agent/tools/session-search-tool.d.ts +0 -1
  140. package/dist/src/agent/tools/session-search-tool.js +11 -6
  141. package/dist/src/agent/tools/session-search-tool.js.map +1 -1
  142. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  143. package/dist/src/agent/tools/workflow-tool.js +1 -1
  144. package/dist/src/agent/tools/write.js +1 -1
  145. package/dist/src/agent/workflow/catalog.js +1 -1
  146. package/dist/src/auth/credentials.js +3 -3
  147. package/dist/src/auth/profiles/store.js +1 -1
  148. package/dist/src/auth/sync-provider-auth.js +1 -1
  149. package/dist/src/browser/cache-dir-policy.js +1 -1
  150. package/dist/src/browser/cdp-local-launcher.js +2 -2
  151. package/dist/src/browser/providers/browser-ext-install.js +4 -4
  152. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  153. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  154. package/dist/src/browser/stealth.js +1 -1
  155. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  156. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  157. package/dist/src/channels/outbound/persist-store.js +1 -1
  158. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  159. package/dist/src/channels/pairing/pairing-store.js +2 -2
  160. package/dist/src/chat-commands/agent-edit.js +2 -2
  161. package/dist/src/chat-commands/builtins/config.js +2 -2
  162. package/dist/src/chat-commands/context.js +1 -1
  163. package/dist/src/cli/commands/config.js +1 -1
  164. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  165. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  166. package/dist/src/cli/commands/doctor/checks/session-integrity.js +32 -95
  167. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
  168. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  169. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  170. package/dist/src/cli/commands/extension-dev.js +1 -1
  171. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  172. package/dist/src/cli/commands/extension-pack.js +1 -1
  173. package/dist/src/cli/commands/gateway/logs.js +1 -1
  174. package/dist/src/cli/commands/image.js +1 -1
  175. package/dist/src/cli/commands/init.js +5 -7
  176. package/dist/src/cli/commands/init.js.map +1 -1
  177. package/dist/src/cli/commands/onboard.js +1 -1
  178. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  179. package/dist/src/commands/agents.config.js +1 -1
  180. package/dist/src/config/agent-profile.js +1 -1
  181. package/dist/src/config/gateway-bind.js +1 -1
  182. package/dist/src/config/index.js +7 -8
  183. package/dist/src/config/index.js.map +1 -1
  184. package/dist/src/config/loader.js +2 -2
  185. package/dist/src/config/models-json.js +2 -2
  186. package/dist/src/config/paths-state.d.ts +3 -0
  187. package/dist/src/config/paths-state.js +7 -3
  188. package/dist/src/config/paths-state.js.map +1 -1
  189. package/dist/src/config/paths.d.ts +5 -35
  190. package/dist/src/config/paths.js +6 -50
  191. package/dist/src/config/paths.js.map +1 -1
  192. package/dist/src/config/profile.js +2 -2
  193. package/dist/src/config/schema.d.ts +15 -0
  194. package/dist/src/config/schema.js +11 -0
  195. package/dist/src/config/schema.js.map +1 -1
  196. package/dist/src/config/workspace-path.js +1 -1
  197. package/dist/src/cron/execution-types.d.ts +42 -0
  198. package/dist/src/cron/executor.js +2 -2
  199. package/dist/src/cron/persistence.js +1 -1
  200. package/dist/src/cron/run-log-store.d.ts +4 -8
  201. package/dist/src/cron/run-log-store.js +26 -78
  202. package/dist/src/cron/run-log-store.js.map +1 -1
  203. package/dist/src/cron/service.d.ts +3 -3
  204. package/dist/src/cron/service.js +2 -2
  205. package/dist/src/cron/service.js.map +1 -1
  206. package/dist/src/cron/types.d.ts +1 -42
  207. package/dist/src/daemon/constants.js +1 -1
  208. package/dist/src/daemon/install-plan.js +2 -2
  209. package/dist/src/daemon/launchd.js +2 -2
  210. package/dist/src/daemon/schtasks.js +2 -2
  211. package/dist/src/daemon/systemd.js +2 -2
  212. package/dist/src/extensions/bundle-mcp.js +1 -1
  213. package/dist/src/extensions/discover-extensions.js +1 -1
  214. package/dist/src/extensions/health.js +1 -1
  215. package/dist/src/extensions/loader.js +1 -1
  216. package/dist/src/extensions/lockfile.js +2 -2
  217. package/dist/src/extensions/update.js +1 -1
  218. package/dist/src/gateway/agents-admin.js +8 -3
  219. package/dist/src/gateway/agents-admin.js.map +1 -1
  220. package/dist/src/gateway/file-path-classifier.d.ts +0 -1
  221. package/dist/src/gateway/file-path-classifier.js +2 -8
  222. package/dist/src/gateway/file-path-classifier.js.map +1 -1
  223. package/dist/src/gateway/heartbeat/service.js +1 -1
  224. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  225. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  226. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  227. package/dist/src/gateway/hono/oauth.js +1 -1
  228. package/dist/src/gateway/hono/routes/agents.js +1 -1
  229. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  230. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  231. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  232. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  233. package/dist/src/gateway/hono/routes/models.js +1 -1
  234. package/dist/src/gateway/hono/routes/shares.js +1 -1
  235. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  236. package/dist/src/gateway/lock.js +3 -3
  237. package/dist/src/gateway/ports.js +1 -1
  238. package/dist/src/gateway/service/agent-runner.js +2 -2
  239. package/dist/src/gateway/service/marketplace-service.js +2 -2
  240. package/dist/src/gateway/service.js +5 -1
  241. package/dist/src/gateway/service.js.map +1 -1
  242. package/dist/src/gateway/session-reset-service.d.ts +1 -1
  243. package/dist/src/gateway/session-reset-service.js +1 -1
  244. package/dist/src/gateway/session-reset-service.js.map +1 -1
  245. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  246. package/dist/src/heartbeat/index.js +1 -1
  247. package/dist/src/infra/brew.js +1 -1
  248. package/dist/src/infra/node-sqlite.d.ts +1 -0
  249. package/dist/src/infra/node-sqlite.js +17 -0
  250. package/dist/src/infra/node-sqlite.js.map +1 -0
  251. package/dist/src/infra/package-json.js +1 -1
  252. package/dist/src/infra/package-update-steps.js +1 -1
  253. package/dist/src/infra/path-env.js +2 -2
  254. package/dist/src/infra/restart.js +2 -2
  255. package/dist/src/infra/sqlite-errors.d.ts +1 -0
  256. package/dist/src/infra/sqlite-errors.js +77 -0
  257. package/dist/src/infra/sqlite-errors.js.map +1 -0
  258. package/dist/src/infra/stable-node-path.js +1 -1
  259. package/dist/src/infra/unhandled-rejections.d.ts +1 -0
  260. package/dist/src/infra/unhandled-rejections.js +25 -0
  261. package/dist/src/infra/unhandled-rejections.js.map +1 -0
  262. package/dist/src/infra/update-check.js +1 -1
  263. package/dist/src/infra/update-global.js +1 -1
  264. package/dist/src/infra/update-lock.js +3 -3
  265. package/dist/src/infra/update-runner.js +1 -1
  266. package/dist/src/infra/update-startup.js +2 -2
  267. package/dist/src/infra/warning-filter.d.ts +7 -0
  268. package/dist/src/infra/warning-filter.js +59 -0
  269. package/dist/src/infra/warning-filter.js.map +1 -0
  270. package/dist/src/infra/write-file-atomic.js +2 -2
  271. package/dist/src/notes/store.d.ts +3 -9
  272. package/dist/src/notes/store.js +22 -196
  273. package/dist/src/notes/store.js.map +1 -1
  274. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  275. package/dist/src/providers/index.js +2 -2
  276. package/dist/src/providers/model-registry.js +1 -1
  277. package/dist/src/session/config-store.d.ts +6 -75
  278. package/dist/src/session/config-store.js +38 -144
  279. package/dist/src/session/config-store.js.map +1 -1
  280. package/dist/src/session/config-types.d.ts +15 -0
  281. package/dist/src/session/config-types.js +1 -0
  282. package/dist/src/session/index.d.ts +1 -3
  283. package/dist/src/session/index.js +3 -5
  284. package/dist/src/session/init-session-turn.d.ts +0 -6
  285. package/dist/src/session/init-session-turn.js +18 -18
  286. package/dist/src/session/init-session-turn.js.map +1 -1
  287. package/dist/src/session/lifecycle-timestamps.d.ts +5 -2
  288. package/dist/src/session/lifecycle-timestamps.js.map +1 -1
  289. package/dist/src/session/{parity/load-jsonl-entries.js → load-jsonl-entries.js} +1 -1
  290. package/dist/src/session/load-jsonl-entries.js.map +1 -0
  291. package/dist/src/session/manager.d.ts +5 -3
  292. package/dist/src/session/manager.js +1 -5
  293. package/dist/src/session/manager.js.map +1 -1
  294. package/dist/src/session/resolve-session.d.ts +3 -6
  295. package/dist/src/session/resolve-session.js +26 -31
  296. package/dist/src/session/resolve-session.js.map +1 -1
  297. package/dist/src/session/session-context-for-llm.js +5 -1
  298. package/dist/src/session/session-context-for-llm.js.map +1 -1
  299. package/dist/src/session/session-id.js +12 -0
  300. package/dist/src/session/session-id.js.map +1 -0
  301. package/dist/src/session/session-title.js +2 -2
  302. package/dist/src/session/session-workspace.d.ts +1 -1
  303. package/dist/src/session/session-workspace.js.map +1 -1
  304. package/dist/src/session/store.d.ts +14 -63
  305. package/dist/src/session/store.js +172 -847
  306. package/dist/src/session/store.js.map +1 -1
  307. package/dist/src/session/stored-rows-to-file-entries.d.ts +11 -0
  308. package/dist/src/session/stored-rows-to-file-entries.js +95 -0
  309. package/dist/src/session/stored-rows-to-file-entries.js.map +1 -0
  310. package/dist/src/session/transcript-events.d.ts +1 -2
  311. package/dist/src/session/transcript-events.js +5 -12
  312. package/dist/src/session/transcript-events.js.map +1 -1
  313. package/dist/src/session/transcript-format.d.ts +1 -1
  314. package/dist/src/session/transcript-format.js.map +1 -1
  315. package/dist/src/session/transcript-stats.d.ts +1 -0
  316. package/dist/src/session/transcript-stats.js +10 -0
  317. package/dist/src/session/transcript-stats.js.map +1 -0
  318. package/dist/src/share/share-auto.js +2 -2
  319. package/dist/src/share/share-store.js +3 -3
  320. package/dist/src/share/share-thumbnail.js +2 -2
  321. package/dist/src/share/share-zip.js +1 -1
  322. package/dist/src/share/site-share-store.js +3 -3
  323. package/dist/src/share/site-static-serve.js +1 -1
  324. package/dist/src/storage/sqlite/config-repository.d.ts +6 -0
  325. package/dist/src/storage/sqlite/config-repository.js +56 -0
  326. package/dist/src/storage/sqlite/config-repository.js.map +1 -0
  327. package/dist/src/storage/sqlite/connection.d.ts +38 -0
  328. package/dist/src/storage/sqlite/connection.js +258 -0
  329. package/dist/src/storage/sqlite/connection.js.map +1 -0
  330. package/dist/src/storage/sqlite/cron-run-repository.d.ts +5 -0
  331. package/dist/src/storage/sqlite/cron-run-repository.js +97 -0
  332. package/dist/src/storage/sqlite/cron-run-repository.js.map +1 -0
  333. package/dist/src/storage/sqlite/fts.d.ts +2 -0
  334. package/dist/src/storage/sqlite/fts.js +11 -0
  335. package/dist/src/storage/sqlite/fts.js.map +1 -0
  336. package/dist/src/storage/sqlite/index.d.ts +12 -0
  337. package/dist/src/storage/sqlite/index.js +13 -0
  338. package/dist/src/storage/sqlite/memory-index-repository.d.ts +18 -0
  339. package/dist/src/storage/sqlite/memory-index-repository.js +132 -0
  340. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -0
  341. package/dist/src/storage/sqlite/notes-repository.d.ts +11 -0
  342. package/dist/src/storage/sqlite/notes-repository.js +191 -0
  343. package/dist/src/storage/sqlite/notes-repository.js.map +1 -0
  344. package/dist/src/storage/sqlite/paths.d.ts +1 -0
  345. package/dist/src/storage/sqlite/paths.js +7 -0
  346. package/dist/src/storage/sqlite/paths.js.map +1 -0
  347. package/dist/src/storage/sqlite/row-mappers.d.ts +82 -0
  348. package/dist/src/storage/sqlite/row-mappers.js +164 -0
  349. package/dist/src/storage/sqlite/row-mappers.js.map +1 -0
  350. package/dist/src/storage/sqlite/schema.d.ts +5 -0
  351. package/dist/src/storage/sqlite/schema.js +43 -0
  352. package/dist/src/storage/sqlite/schema.js.map +1 -0
  353. package/dist/src/storage/sqlite/schema.sql +195 -0
  354. package/dist/src/storage/sqlite/session-metadata.d.ts +8 -0
  355. package/dist/src/storage/sqlite/session-metadata.js +83 -0
  356. package/dist/src/storage/sqlite/session-metadata.js.map +1 -0
  357. package/dist/src/storage/sqlite/session-repository.d.ts +29 -0
  358. package/dist/src/storage/sqlite/session-repository.js +268 -0
  359. package/dist/src/storage/sqlite/session-repository.js.map +1 -0
  360. package/dist/src/storage/sqlite/transaction.d.ts +11 -0
  361. package/dist/src/storage/sqlite/transaction.js +115 -0
  362. package/dist/src/storage/sqlite/transaction.js.map +1 -0
  363. package/dist/src/storage/sqlite/transcript-repository.d.ts +34 -0
  364. package/dist/src/storage/sqlite/transcript-repository.js +241 -0
  365. package/dist/src/storage/sqlite/transcript-repository.js.map +1 -0
  366. package/dist/src/tui/clipboard-image.js +3 -3
  367. package/dist/src/tui/theme-manager.js +1 -1
  368. package/dist/src/tui/tui-keybindings-file.js +1 -1
  369. package/dist/src/tui/tui-scoped-models.js +2 -2
  370. package/dist/src/tui/tui-settings.js +1 -1
  371. package/dist/src/tui/tui.js +3 -3
  372. package/dist/src/tunnel/frpc-binary.js +3 -3
  373. package/dist/src/tunnel/frpc-config.js +1 -1
  374. package/dist/src/tunnel/frpc-extract.js +1 -1
  375. package/dist/src/tunnel/tunnel-state.js +1 -1
  376. package/dist/src/utils/logger/audit.js +1 -1
  377. package/dist/src/utils/logger/log-store.js +1 -1
  378. package/dist/src/utils/logger/rotation.js +1 -1
  379. package/dist/src/voice/tts/audio.js +1 -1
  380. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  381. package/dist/src/workflows/service/workflow-session-bridge.js +41 -64
  382. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  383. package/dist/src/workflows/store/event-store.js +1 -1
  384. package/dist/src/workflows/store/run-store.js +1 -1
  385. package/package.json +3 -3
  386. package/dist/gateway/static/root/assets/index-Bj_l8QDp.css +0 -1
  387. package/dist/gateway/static/root/assets/notes-page-BApAirFB.js +0 -1
  388. package/dist/gateway/static/root/assets/settings-page-4VmUTzQs.js +0 -3
  389. package/dist/gateway/static/root/assets/share-preview-page-IX0TJvRd.js +0 -2
  390. package/dist/gateway/static/root/assets/url-BHHmdJYc.js +0 -3
  391. package/dist/src/agent/embedded/session-manager-cache.d.ts +0 -19
  392. package/dist/src/agent/embedded/session-manager-cache.js +0 -48
  393. package/dist/src/agent/embedded/session-manager-cache.js.map +0 -1
  394. package/dist/src/session/parity/artifacts.d.ts +0 -16
  395. package/dist/src/session/parity/artifacts.js +0 -80
  396. package/dist/src/session/parity/artifacts.js.map +0 -1
  397. package/dist/src/session/parity/jsonl-transcript-io.d.ts +0 -54
  398. package/dist/src/session/parity/jsonl-transcript-io.js +0 -236
  399. package/dist/src/session/parity/jsonl-transcript-io.js.map +0 -1
  400. package/dist/src/session/parity/load-jsonl-entries.js.map +0 -1
  401. package/dist/src/session/parity/session-id.js +0 -18
  402. package/dist/src/session/parity/session-id.js.map +0 -1
  403. package/dist/src/session/parity/sessions-json-cache.d.ts +0 -14
  404. package/dist/src/session/parity/sessions-json-cache.js +0 -98
  405. package/dist/src/session/parity/sessions-json-cache.js.map +0 -1
  406. package/dist/src/session/parity/sessions-json-file-read.d.ts +0 -6
  407. package/dist/src/session/parity/sessions-json-file-read.js +0 -19
  408. package/dist/src/session/parity/sessions-json-file-read.js.map +0 -1
  409. package/dist/src/session/parity/sessions-json-file.d.ts +0 -11
  410. package/dist/src/session/parity/sessions-json-file.js +0 -52
  411. package/dist/src/session/parity/sessions-json-file.js.map +0 -1
  412. package/dist/src/session/parity/sessions-json-patch.d.ts +0 -14
  413. package/dist/src/session/parity/sessions-json-patch.js +0 -40
  414. package/dist/src/session/parity/sessions-json-patch.js.map +0 -1
  415. package/dist/src/session/parity/transcript-file-lock.d.ts +0 -22
  416. package/dist/src/session/parity/transcript-file-lock.js +0 -142
  417. package/dist/src/session/parity/transcript-file-lock.js.map +0 -1
  418. package/dist/src/session/parity/transcript-pagination.d.ts +0 -29
  419. package/dist/src/session/parity/transcript-pagination.js +0 -132
  420. package/dist/src/session/parity/transcript-pagination.js.map +0 -1
  421. package/dist/src/session/parity/transcript-paths.d.ts +0 -13
  422. package/dist/src/session/parity/transcript-paths.js +0 -64
  423. package/dist/src/session/parity/transcript-paths.js.map +0 -1
  424. package/dist/src/session/parity/xopc-session-disk-entry.d.ts +0 -22
  425. package/dist/src/session/search-index-cache.d.ts +0 -6
  426. package/dist/src/session/search-index-cache.js +0 -44
  427. package/dist/src/session/search-index-cache.js.map +0 -1
  428. package/dist/src/session/search-index.d.ts +0 -20
  429. package/dist/src/session/search-index.js +0 -124
  430. package/dist/src/session/search-index.js.map +0 -1
  431. /package/dist/src/{session/parity/xopc-session-disk-entry.js → cron/execution-types.js} +0 -0
  432. /package/dist/src/session/{parity/load-jsonl-entries.d.ts → load-jsonl-entries.d.ts} +0 -0
  433. /package/dist/src/session/{parity/session-id.d.ts → session-id.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"session-runner.js","names":[],"sources":["../../../../src/agent/embedded/session-runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport type { AgentTool } from '@earendil-works/pi-agent-core';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport {\n createAgentSession,\n DefaultResourceLoader,\n getAgentDir,\n SessionManager,\n SettingsManager,\n type AgentSession,\n} from '@earendil-works/pi-coding-agent';\nimport type { Model, Api } from '@earendil-works/pi-ai';\n\nimport { createLogger } from '../../utils/logger.js';\nimport { guardSessionManager, type GuardedPiTranscriptManager } from './session-tool-result-guard.js';\nimport { prepareSessionManagerForRun } from './session-manager-init.js';\nimport { defaultSessionManagerCache, type SessionManagerCache } from './session-manager-cache.js';\nimport { applyXopcProviderApiKey, createEmbeddedAuthStorage } from './xopc-auth-storage.js';\nimport { wrapStreamFnForXopcExtensions } from './xopc-stream-bridge.js';\nimport { xopcToolsToDefinitions } from './xopc-tools-bridge.js';\nimport { applySystemPromptOverrideToSession } from './system-prompt-override.js';\n\nconst log = createLogger('EmbeddedSessionRunner');\n\nconst DEFAULT_IDLE_TTL_MS = 5 * 60_000;\n\nexport type EmbeddedRunnerFingerprintInput = {\n sessionFile: string;\n workspaceDir: string;\n modelRef: string;\n toolNames: readonly string[];\n systemPrompt: string;\n thinkingLevel: string;\n};\n\nexport function buildEmbeddedRunnerFingerprint(input: EmbeddedRunnerFingerprintInput): string {\n const tools = [...input.toolNames].sort().join('\\0');\n const promptMarker = `${input.systemPrompt.length}:${input.systemPrompt.slice(0, 128)}`;\n return [\n input.sessionFile,\n input.workspaceDir,\n input.modelRef,\n tools,\n promptMarker,\n input.thinkingLevel,\n ].join('\u001f');\n}\n\ntype PooledRunner = {\n sessionKey: string;\n fingerprint: string;\n session: AgentSession;\n piSm: GuardedPiTranscriptManager;\n settingsManager: SettingsManager;\n baseStreamFn: AgentSession['agent']['streamFn'];\n lastUsedAt: number;\n idleTimer: ReturnType<typeof setTimeout> | null;\n};\n\nexport type AcquireEmbeddedSessionRunnerParams = {\n sessionKey: string;\n sessionId: string;\n sessionFile: string;\n sessionsDir: string;\n hadSessionFile: boolean;\n workspaceDir: string;\n model: Model<Api>;\n modelRef: string;\n tools: AgentTool[];\n systemPrompt: string;\n thinkingLevel: ThinkingLevel;\n};\n\nexport type AcquiredEmbeddedSessionRunner = {\n session: AgentSession;\n piSm: GuardedPiTranscriptManager;\n reused: boolean;\n release: () => void;\n};\n\nexport interface EmbeddedSessionRunnerPoolStats {\n acquires: number;\n reuses: number;\n creates: number;\n evictions: number;\n pooled: number;\n}\n\nexport function isEmbeddedSessionRunnerEnabled(): boolean {\n const raw = process.env.XOPC_SESSION_RUNNER?.trim().toLowerCase();\n if (raw === '0' || raw === 'false' || raw === 'off') {\n return false;\n }\n return true;\n}\n\nexport function getEmbeddedSessionRunnerIdleTtlMs(): number {\n const raw = process.env.XOPC_SESSION_RUNNER_TTL_MS?.trim();\n if (!raw) {\n return DEFAULT_IDLE_TTL_MS;\n }\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_IDLE_TTL_MS;\n}\n\nfunction createEmbeddedSettingsManager(cwd: string): SettingsManager {\n const sm = SettingsManager.inMemory({ compaction: { enabled: false } });\n sm.setCompactionEnabled(false);\n void cwd;\n return sm;\n}\n\nexport interface EmbeddedSessionRunnerPoolOptions {\n /** File-exists cache shared with prewarmSessionFile callers. */\n sessionManagerCache?: SessionManagerCache;\n /** Override for the env-driven enable flag (testing). */\n isEnabled?: () => boolean;\n /** Override for the env-driven idle TTL (testing). */\n getIdleTtlMs?: () => number;\n}\n\n/**\n * Owns the per-session pool of pi `AgentSession` runners. The class is the supported,\n * injectable owner; {@link defaultEmbeddedSessionRunnerPool} keeps the historic\n * module-level free functions working until every caller is migrated to DI.\n */\nexport class EmbeddedSessionRunnerPool {\n private readonly pool = new Map<string, PooledRunner>();\n private readonly cache: SessionManagerCache;\n private readonly isEnabledFn: () => boolean;\n private readonly getIdleTtlMsFn: () => number;\n\n private stats: Omit<EmbeddedSessionRunnerPoolStats, 'pooled'> = {\n acquires: 0,\n reuses: 0,\n creates: 0,\n evictions: 0,\n };\n\n constructor(opts: EmbeddedSessionRunnerPoolOptions = {}) {\n this.cache = opts.sessionManagerCache ?? defaultSessionManagerCache;\n this.isEnabledFn = opts.isEnabled ?? isEmbeddedSessionRunnerEnabled;\n this.getIdleTtlMsFn = opts.getIdleTtlMs ?? getEmbeddedSessionRunnerIdleTtlMs;\n }\n\n getStats(): Readonly<EmbeddedSessionRunnerPoolStats> {\n return { ...this.stats, pooled: this.pool.size };\n }\n\n resetForTest(): void {\n for (const entry of this.pool.values()) {\n this.clearIdleTimer(entry);\n }\n this.pool.clear();\n this.stats = { acquires: 0, reuses: 0, creates: 0, evictions: 0 };\n }\n\n evict(sessionKey: string, reason = 'explicit'): void {\n const entry = this.pool.get(sessionKey);\n if (!entry) {\n return;\n }\n this.disposePooledRunner(sessionKey, entry, reason);\n }\n\n evictAll(reason = 'dispose_all'): void {\n for (const sessionKey of [...this.pool.keys()]) {\n this.evict(sessionKey, reason);\n }\n }\n\n async acquire(params: AcquireEmbeddedSessionRunnerParams): Promise<AcquiredEmbeddedSessionRunner> {\n this.stats.acquires += 1;\n\n const fingerprint = buildEmbeddedRunnerFingerprint({\n sessionFile: params.sessionFile,\n workspaceDir: params.workspaceDir,\n modelRef: params.modelRef,\n toolNames: params.tools.map((t) => t.name),\n systemPrompt: params.systemPrompt,\n thinkingLevel: params.thinkingLevel ?? 'medium',\n });\n\n const reuseEnabled = this.isEnabledFn();\n const existing = this.pool.get(params.sessionKey);\n\n let entry: PooledRunner;\n let reused = false;\n\n if (reuseEnabled && existing && existing.fingerprint === fingerprint) {\n this.clearIdleTimer(existing);\n entry = existing;\n entry.lastUsedAt = Date.now();\n reused = true;\n this.stats.reuses += 1;\n applySystemPromptOverrideToSession(entry.session, params.systemPrompt);\n entry.session.agent.streamFn = entry.baseStreamFn;\n log.debug({ sessionKey: params.sessionKey }, 'Reusing pooled embedded session runner');\n } else {\n if (existing) {\n this.disposePooledRunner(params.sessionKey, existing, 'fingerprint_mismatch');\n }\n entry = await this.createPooledRunner(params);\n this.pool.set(params.sessionKey, entry);\n this.stats.creates += 1;\n log.debug({ sessionKey: params.sessionKey }, 'Created embedded session runner');\n }\n\n return {\n session: entry.session,\n piSm: entry.piSm,\n reused,\n release: () => {\n if (!this.isEnabledFn()) {\n this.disposePooledRunner(params.sessionKey, entry, 'runner_disabled');\n return;\n }\n entry.lastUsedAt = Date.now();\n this.scheduleIdleEviction(params.sessionKey, entry);\n },\n };\n }\n\n private clearIdleTimer(entry: PooledRunner): void {\n if (entry.idleTimer) {\n clearTimeout(entry.idleTimer);\n entry.idleTimer = null;\n }\n }\n\n private scheduleIdleEviction(sessionKey: string, entry: PooledRunner): void {\n this.clearIdleTimer(entry);\n const ttlMs = this.getIdleTtlMsFn();\n entry.idleTimer = setTimeout(() => {\n const current = this.pool.get(sessionKey);\n if (current === entry) {\n this.disposePooledRunner(sessionKey, entry, 'idle_ttl');\n }\n }, ttlMs);\n entry.idleTimer.unref?.();\n }\n\n private disposePooledRunner(sessionKey: string, entry: PooledRunner, reason: string): void {\n this.clearIdleTimer(entry);\n this.pool.delete(sessionKey);\n this.stats.evictions += 1;\n try {\n entry.piSm.flushPendingToolResults?.();\n } catch {\n /* ignore */\n }\n log.debug({ sessionKey, reason }, 'Embedded session runner evicted');\n }\n\n private async createPooledRunner(params: AcquireEmbeddedSessionRunnerParams): Promise<PooledRunner> {\n const {\n sessionKey,\n sessionId,\n sessionFile,\n sessionsDir,\n hadSessionFile,\n workspaceDir,\n model,\n thinkingLevel,\n tools,\n systemPrompt,\n } = params;\n\n await this.cache.prewarm(sessionFile);\n const settingsManager = createEmbeddedSettingsManager(workspaceDir);\n\n const piSm = guardSessionManager(SessionManager.open(sessionFile, sessionsDir, workspaceDir), {\n sessionKey,\n contextWindowTokens: model.contextWindow ?? 128_000,\n });\n\n await prepareSessionManagerForRun({\n sessionManager: piSm,\n sessionFile,\n hadSessionFile,\n sessionId,\n cwd: workspaceDir,\n });\n\n const toolDefs = xopcToolsToDefinitions(tools);\n const toolNames = tools.map((t) => t.name);\n\n const authStorage = createEmbeddedAuthStorage();\n applyXopcProviderApiKey(authStorage, model.provider);\n\n const resourceLoader = new DefaultResourceLoader({\n cwd: workspaceDir,\n agentDir: getAgentDir(),\n settingsManager,\n noContextFiles: true,\n });\n await resourceLoader.reload();\n\n const { session } = await createAgentSession({\n cwd: workspaceDir,\n model,\n thinkingLevel: thinkingLevel ?? 'medium',\n sessionManager: piSm,\n settingsManager,\n authStorage,\n resourceLoader,\n noTools: 'builtin',\n customTools: toolDefs,\n tools: toolNames,\n });\n\n applySystemPromptOverrideToSession(session, systemPrompt);\n const baseStreamFn = wrapStreamFnForXopcExtensions(session.agent.streamFn);\n session.agent.streamFn = baseStreamFn;\n\n const fingerprint = buildEmbeddedRunnerFingerprint({\n sessionFile,\n workspaceDir,\n modelRef: params.modelRef,\n toolNames,\n systemPrompt,\n thinkingLevel: thinkingLevel ?? 'medium',\n });\n\n return {\n sessionKey,\n fingerprint,\n session,\n piSm,\n settingsManager,\n baseStreamFn,\n lastUsedAt: Date.now(),\n idleTimer: null,\n };\n }\n}\n\nexport const defaultEmbeddedSessionRunnerPool = new EmbeddedSessionRunnerPool();\n\nexport function getEmbeddedSessionRunnerStats(): Readonly<EmbeddedSessionRunnerPoolStats> {\n return defaultEmbeddedSessionRunnerPool.getStats();\n}\n\nexport function resetEmbeddedSessionRunnerForTest(): void {\n defaultEmbeddedSessionRunnerPool.resetForTest();\n}\n\nexport function evictEmbeddedSessionRunner(sessionKey: string, reason = 'explicit'): void {\n defaultEmbeddedSessionRunnerPool.evict(sessionKey, reason);\n}\n\nexport function evictAllEmbeddedSessionRunners(reason = 'dispose_all'): void {\n defaultEmbeddedSessionRunnerPool.evictAll(reason);\n}\n\nexport function acquireEmbeddedSessionRunner(\n params: AcquireEmbeddedSessionRunnerParams,\n): Promise<AcquiredEmbeddedSessionRunner> {\n return defaultEmbeddedSessionRunnerPool.acquire(params);\n}\n\n/** Resolve transcript path inputs used by both runner acquire and turn execution. */\nexport async function resolveEmbeddedTranscriptInputs(\n sessionStore: import('../../session/store.js').SessionStore,\n sessionKey: string,\n): Promise<{\n sessionId: string;\n sessionFile: string;\n sessionsDir: string;\n hadSessionFile: boolean;\n}> {\n const { sessionId, absPath: sessionFile, sessionsDir } = await sessionStore.resolveTranscriptPath(sessionKey);\n return {\n sessionId,\n sessionFile,\n sessionsDir,\n hadSessionFile: existsSync(sessionFile),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-runner.js","names":[],"sources":["../../../../src/agent/embedded/session-runner.ts"],"sourcesContent":["import type { AgentTool } from '@earendil-works/pi-agent-core';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport {\n createAgentSession,\n DefaultResourceLoader,\n getAgentDir,\n SettingsManager,\n type AgentSession,\n} from '@earendil-works/pi-coding-agent';\nimport type { Model, Api } from '@earendil-works/pi-ai';\n\nimport { createLogger } from '../../utils/logger.js';\nimport { guardSessionManager, type GuardedPiTranscriptManager } from './session-tool-result-guard.js';\nimport { openSqliteHydratingSessionManager } from './sqlite-hydrating-session-manager.js';\nimport { applyXopcProviderApiKey, createEmbeddedAuthStorage } from './xopc-auth-storage.js';\nimport { wrapStreamFnForXopcExtensions } from './xopc-stream-bridge.js';\nimport { xopcToolsToDefinitions } from './xopc-tools-bridge.js';\nimport { applySystemPromptOverrideToSession } from './system-prompt-override.js';\n\nconst log = createLogger('EmbeddedSessionRunner');\n\nconst DEFAULT_IDLE_TTL_MS = 5 * 60_000;\n\nexport type EmbeddedRunnerFingerprintInput = {\n sessionId: string;\n workspaceDir: string;\n modelRef: string;\n toolNames: readonly string[];\n systemPrompt: string;\n thinkingLevel: string;\n};\n\nexport function buildEmbeddedRunnerFingerprint(input: EmbeddedRunnerFingerprintInput): string {\n const tools = [...input.toolNames].sort().join('\\0');\n const promptMarker = `${input.systemPrompt.length}:${input.systemPrompt.slice(0, 128)}`;\n return [\n input.sessionId,\n input.workspaceDir,\n input.modelRef,\n tools,\n promptMarker,\n input.thinkingLevel,\n ].join('\u001f');\n}\n\ntype PooledRunner = {\n sessionKey: string;\n fingerprint: string;\n session: AgentSession;\n piSm: GuardedPiTranscriptManager;\n settingsManager: SettingsManager;\n baseStreamFn: AgentSession['agent']['streamFn'];\n lastUsedAt: number;\n idleTimer: ReturnType<typeof setTimeout> | null;\n};\n\nexport type AcquireEmbeddedSessionRunnerParams = {\n sessionKey: string;\n sessionId: string;\n workspaceDir: string;\n model: Model<Api>;\n modelRef: string;\n tools: AgentTool[];\n systemPrompt: string;\n thinkingLevel: ThinkingLevel;\n};\n\nexport type AcquiredEmbeddedSessionRunner = {\n session: AgentSession;\n piSm: GuardedPiTranscriptManager;\n reused: boolean;\n release: () => void;\n};\n\nexport interface EmbeddedSessionRunnerPoolStats {\n acquires: number;\n reuses: number;\n creates: number;\n evictions: number;\n pooled: number;\n}\n\nexport function isEmbeddedSessionRunnerEnabled(): boolean {\n const raw = process.env.XOPC_SESSION_RUNNER?.trim().toLowerCase();\n if (raw === '0' || raw === 'false' || raw === 'off') {\n return false;\n }\n return true;\n}\n\nexport function getEmbeddedSessionRunnerIdleTtlMs(): number {\n const raw = process.env.XOPC_SESSION_RUNNER_TTL_MS?.trim();\n if (!raw) {\n return DEFAULT_IDLE_TTL_MS;\n }\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_IDLE_TTL_MS;\n}\n\nfunction createEmbeddedSettingsManager(cwd: string): SettingsManager {\n const sm = SettingsManager.inMemory({ compaction: { enabled: false } });\n sm.setCompactionEnabled(false);\n void cwd;\n return sm;\n}\n\nexport interface EmbeddedSessionRunnerPoolOptions {\n /** Override for the env-driven enable flag (testing). */\n isEnabled?: () => boolean;\n /** Override for the env-driven idle TTL (testing). */\n getIdleTtlMs?: () => number;\n}\n\n/**\n * Owns the per-session pool of pi `AgentSession` runners. The class is the supported,\n * injectable owner; {@link defaultEmbeddedSessionRunnerPool} keeps the historic\n * module-level free functions working until every caller is migrated to DI.\n */\nexport class EmbeddedSessionRunnerPool {\n private readonly pool = new Map<string, PooledRunner>();\n private readonly isEnabledFn: () => boolean;\n private readonly getIdleTtlMsFn: () => number;\n\n private stats: Omit<EmbeddedSessionRunnerPoolStats, 'pooled'> = {\n acquires: 0,\n reuses: 0,\n creates: 0,\n evictions: 0,\n };\n\n constructor(opts: EmbeddedSessionRunnerPoolOptions = {}) {\n this.isEnabledFn = opts.isEnabled ?? isEmbeddedSessionRunnerEnabled;\n this.getIdleTtlMsFn = opts.getIdleTtlMs ?? getEmbeddedSessionRunnerIdleTtlMs;\n }\n\n getStats(): Readonly<EmbeddedSessionRunnerPoolStats> {\n return { ...this.stats, pooled: this.pool.size };\n }\n\n resetForTest(): void {\n for (const entry of this.pool.values()) {\n this.clearIdleTimer(entry);\n }\n this.pool.clear();\n this.stats = { acquires: 0, reuses: 0, creates: 0, evictions: 0 };\n }\n\n evict(sessionKey: string, reason = 'explicit'): void {\n const entry = this.pool.get(sessionKey);\n if (!entry) {\n return;\n }\n this.disposePooledRunner(sessionKey, entry, reason);\n }\n\n evictAll(reason = 'dispose_all'): void {\n for (const sessionKey of [...this.pool.keys()]) {\n this.evict(sessionKey, reason);\n }\n }\n\n async acquire(params: AcquireEmbeddedSessionRunnerParams): Promise<AcquiredEmbeddedSessionRunner> {\n this.stats.acquires += 1;\n\n const fingerprint = buildEmbeddedRunnerFingerprint({\n sessionId: params.sessionId,\n workspaceDir: params.workspaceDir,\n modelRef: params.modelRef,\n toolNames: params.tools.map((t) => t.name),\n systemPrompt: params.systemPrompt,\n thinkingLevel: params.thinkingLevel ?? 'medium',\n });\n\n const reuseEnabled = this.isEnabledFn();\n const existing = this.pool.get(params.sessionKey);\n\n let entry: PooledRunner;\n let reused = false;\n\n if (reuseEnabled && existing && existing.fingerprint === fingerprint) {\n this.clearIdleTimer(existing);\n entry = existing;\n entry.lastUsedAt = Date.now();\n reused = true;\n this.stats.reuses += 1;\n applySystemPromptOverrideToSession(entry.session, params.systemPrompt);\n entry.session.agent.streamFn = entry.baseStreamFn;\n log.debug({ sessionKey: params.sessionKey }, 'Reusing pooled embedded session runner');\n } else {\n if (existing) {\n this.disposePooledRunner(params.sessionKey, existing, 'fingerprint_mismatch');\n }\n entry = await this.createPooledRunner(params);\n this.pool.set(params.sessionKey, entry);\n this.stats.creates += 1;\n log.debug({ sessionKey: params.sessionKey }, 'Created embedded session runner');\n }\n\n return {\n session: entry.session,\n piSm: entry.piSm,\n reused,\n release: () => {\n if (!this.isEnabledFn()) {\n this.disposePooledRunner(params.sessionKey, entry, 'runner_disabled');\n return;\n }\n entry.lastUsedAt = Date.now();\n this.scheduleIdleEviction(params.sessionKey, entry);\n },\n };\n }\n\n private clearIdleTimer(entry: PooledRunner): void {\n if (entry.idleTimer) {\n clearTimeout(entry.idleTimer);\n entry.idleTimer = null;\n }\n }\n\n private scheduleIdleEviction(sessionKey: string, entry: PooledRunner): void {\n this.clearIdleTimer(entry);\n const ttlMs = this.getIdleTtlMsFn();\n entry.idleTimer = setTimeout(() => {\n const current = this.pool.get(sessionKey);\n if (current === entry) {\n this.disposePooledRunner(sessionKey, entry, 'idle_ttl');\n }\n }, ttlMs);\n entry.idleTimer.unref?.();\n }\n\n private disposePooledRunner(sessionKey: string, entry: PooledRunner, reason: string): void {\n this.clearIdleTimer(entry);\n this.pool.delete(sessionKey);\n this.stats.evictions += 1;\n try {\n entry.piSm.flushPendingToolResults?.();\n } catch {\n /* ignore */\n }\n log.debug({ sessionKey, reason }, 'Embedded session runner evicted');\n }\n\n private async createPooledRunner(params: AcquireEmbeddedSessionRunnerParams): Promise<PooledRunner> {\n const { sessionKey, sessionId, workspaceDir, model, thinkingLevel, tools, systemPrompt } = params;\n\n const settingsManager = createEmbeddedSettingsManager(workspaceDir);\n\n const piSm = guardSessionManager(\n openSqliteHydratingSessionManager({\n sessionKey,\n sessionId,\n cwd: workspaceDir,\n }),\n {\n sessionKey,\n contextWindowTokens: model.contextWindow ?? 128_000,\n },\n );\n\n const toolDefs = xopcToolsToDefinitions(tools);\n const toolNames = tools.map((t) => t.name);\n\n const authStorage = createEmbeddedAuthStorage();\n applyXopcProviderApiKey(authStorage, model.provider);\n\n const resourceLoader = new DefaultResourceLoader({\n cwd: workspaceDir,\n agentDir: getAgentDir(),\n settingsManager,\n noContextFiles: true,\n });\n await resourceLoader.reload();\n\n const { session } = await createAgentSession({\n cwd: workspaceDir,\n model,\n thinkingLevel: thinkingLevel ?? 'medium',\n sessionManager: piSm,\n settingsManager,\n authStorage,\n resourceLoader,\n noTools: 'builtin',\n customTools: toolDefs,\n tools: toolNames,\n });\n\n applySystemPromptOverrideToSession(session, systemPrompt);\n const baseStreamFn = wrapStreamFnForXopcExtensions(session.agent.streamFn);\n session.agent.streamFn = baseStreamFn;\n\n const fingerprint = buildEmbeddedRunnerFingerprint({\n sessionId,\n workspaceDir,\n modelRef: params.modelRef,\n toolNames,\n systemPrompt,\n thinkingLevel: thinkingLevel ?? 'medium',\n });\n\n return {\n sessionKey,\n fingerprint,\n session,\n piSm,\n settingsManager,\n baseStreamFn,\n lastUsedAt: Date.now(),\n idleTimer: null,\n };\n }\n}\n\nexport const defaultEmbeddedSessionRunnerPool = new EmbeddedSessionRunnerPool();\n\nexport function getEmbeddedSessionRunnerStats(): Readonly<EmbeddedSessionRunnerPoolStats> {\n return defaultEmbeddedSessionRunnerPool.getStats();\n}\n\nexport function resetEmbeddedSessionRunnerForTest(): void {\n defaultEmbeddedSessionRunnerPool.resetForTest();\n}\n\nexport function evictEmbeddedSessionRunner(sessionKey: string, reason = 'explicit'): void {\n defaultEmbeddedSessionRunnerPool.evict(sessionKey, reason);\n}\n\nexport function evictAllEmbeddedSessionRunners(reason = 'dispose_all'): void {\n defaultEmbeddedSessionRunnerPool.evictAll(reason);\n}\n\nexport function acquireEmbeddedSessionRunner(\n params: AcquireEmbeddedSessionRunnerParams,\n): Promise<AcquiredEmbeddedSessionRunner> {\n return defaultEmbeddedSessionRunnerPool.acquire(params);\n}\n\n/** Resolve session identity used by embedded runner acquire and turn execution. */\nexport async function resolveEmbeddedTranscriptInputs(\n sessionStore: import('../../session/store.js').SessionStore,\n sessionKey: string,\n): Promise<{\n sessionId: string;\n sessionKey: string;\n}> {\n return sessionStore.resolveTranscriptPath(sessionKey);\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { init_string_coerce, normalizeOptionalString } from "../../utils/string-coerce.js";
2
- import { emitSessionTranscriptUpdate } from "../../session/transcript-events.js";
3
2
  import { boundedJsonUtf8Bytes, firstEnumerableOwnKeys, jsonUtf8BytesOrInfinity } from "../../infra/json-utf8-bytes.js";
3
+ import { emitSessionTranscriptUpdate } from "../../session/transcript-events.js";
4
4
  import { formatContextLimitTruncationNotice } from "./tool-result-context-guard.js";
5
5
  import { resolveLiveToolResultMaxChars, truncateToolResultMessage } from "./tool-result-truncation.js";
6
6
  import { extractToolCallsFromAssistant, extractToolResultId } from "../transcript/tool-call-id.js";
@@ -345,9 +345,7 @@ var ToolResultGuard = class {
345
345
  const finalMessage = this.applyBeforeWriteHook(this.persistMessage(nextMessage));
346
346
  if (!finalMessage) return;
347
347
  const result = this.originalAppend(finalMessage);
348
- const sessionFile = this.sessionManager.getSessionFile?.();
349
- if (sessionFile) emitSessionTranscriptUpdate({
350
- sessionFile,
348
+ if (this.opts.sessionKey) emitSessionTranscriptUpdate({
351
349
  sessionKey: this.opts.sessionKey,
352
350
  message: finalMessage,
353
351
  messageId: typeof result === "string" ? result : void 0
@@ -1 +1 @@
1
- {"version":3,"file":"session-tool-result-guard.js","names":[],"sources":["../../../../src/agent/embedded/session-tool-result-guard.ts"],"sourcesContent":["/**\n * Session tool-result guard — wraps a pi `SessionManager.appendMessage` to:\n * 1. cap oversized tool-result text + details so they cannot blow up the next\n * LLM request (size + persistence limits).\n * 2. track pending tool-call IDs so missing tool results can be synthesised\n * (some providers refuse a turn that has an orphan tool_use block).\n * 3. drop assistant `toolCall` blocks whose tool name is not in the allowlist\n * (these would also trigger provider 400s).\n * 4. broadcast `xopc:transcript-row` updates so the gateway UI can stream them.\n *\n * Previously this module shipped with three sibling files\n * (`session-tool-result-state.ts`, `session-raw-append-message.ts`,\n * `session-tool-result-guard-wrapper.ts`). They are now consolidated here as\n * private constructs around the `ToolResultGuard` class. Pi-coding-agent owns\n * the `SessionManager` instance and calls `appendMessage` from inside the\n * runtime, so we still need to monkey-patch that method — but the patched\n * implementation is just `guard.guardedAppend.bind(guard)` and all state lives\n * on the class.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { SessionManager } from '@earendil-works/pi-coding-agent';\n\nimport type { Config } from '../../config/schema.js';\nimport {\n boundedJsonUtf8Bytes,\n firstEnumerableOwnKeys,\n jsonUtf8BytesOrInfinity,\n type BoundedJsonUtf8Bytes,\n} from '../../infra/json-utf8-bytes.js';\nimport { emitSessionTranscriptUpdate } from '../../session/transcript-events.js';\nimport { normalizeOptionalString } from '../../utils/string-coerce.js';\nimport { formatContextLimitTruncationNotice } from './tool-result-context-guard.js';\nimport {\n DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS,\n resolveLiveToolResultMaxChars,\n truncateToolResultMessage,\n} from './tool-result-truncation.js';\nimport {\n makeMissingToolResult,\n sanitizeToolCallInputs,\n} from '../transcript/session-transcript-repair.js';\nimport {\n extractToolCallsFromAssistant,\n extractToolResultId,\n} from '../transcript/tool-call-id.js';\n\n// ── Public surface ──────────────────────────────────────────────────────────\n\nexport type BeforeMessageWriteHookEvent = { message: AgentMessage };\nexport type BeforeMessageWriteHookResult =\n | { block?: boolean; message?: AgentMessage }\n | undefined;\n\nexport interface ToolResultGuardOptions {\n /** Optional session key for transcript update broadcasts. */\n sessionKey?: string;\n /** Optional transform applied to any message before persistence. */\n transformMessageForPersistence?: (message: AgentMessage) => AgentMessage;\n /**\n * Optional, synchronous transform applied to toolResult messages *before* they are\n * persisted to the session transcript.\n */\n transformToolResultForPersistence?: (\n message: AgentMessage,\n meta: { toolCallId?: string; toolName?: string; isSynthetic?: boolean },\n ) => AgentMessage;\n /**\n * Whether to synthesize missing tool results to satisfy strict providers.\n * Defaults to true.\n */\n allowSyntheticToolResults?: boolean;\n missingToolResultText?: string;\n /**\n * Optional set/list of tool names accepted for assistant toolCall/toolUse blocks.\n * When set, tool calls with unknown names are dropped before persistence.\n */\n allowedToolNames?: Iterable<string>;\n /**\n * Synchronous hook invoked before any message is written to the session JSONL.\n * If the hook returns { block: true }, the message is silently dropped.\n * If it returns { message }, the modified message is written instead.\n */\n beforeMessageWriteHook?: (event: BeforeMessageWriteHookEvent) => BeforeMessageWriteHookResult;\n maxToolResultChars?: number;\n}\n\nexport interface InstallSessionToolResultGuardResult {\n flushPendingToolResults: () => void;\n clearPendingToolResults: () => void;\n getPendingIds: () => string[];\n}\n\n/** Idempotent wrapper that also adds the helper methods consumers expect. */\nexport type GuardedPiTranscriptManager = SessionManager & {\n flushPendingToolResults?: () => void;\n clearPendingToolResults?: () => void;\n};\n\n/**\n * Install the guard on a SessionManager and return its control API.\n * Subsequent assistant/toolResult writes by pi-coding-agent flow through the\n * guard transparently.\n */\nexport function installSessionToolResultGuard(\n sessionManager: SessionManager,\n opts: ToolResultGuardOptions = {},\n): InstallSessionToolResultGuardResult {\n const guard = new ToolResultGuard(sessionManager, opts);\n guard.attach();\n return {\n flushPendingToolResults: () => guard.flushPending(),\n clearPendingToolResults: () => guard.clearPending(),\n getPendingIds: () => guard.getPendingIds(),\n };\n}\n\n/**\n * Convenience wrapper used by the embedded runner pool: install the guard\n * (idempotent), pin the size cap from the model context window, and expose\n * `flushPendingToolResults` / `clearPendingToolResults` directly on the\n * SessionManager instance so callers do not need to keep the install result.\n */\nexport function guardSessionManager(\n sessionManager: SessionManager,\n opts?: {\n agentId?: string;\n sessionKey?: string;\n config?: Config;\n contextWindowTokens?: number;\n allowSyntheticToolResults?: boolean;\n missingToolResultText?: string;\n allowedToolNames?: Iterable<string>;\n },\n): GuardedPiTranscriptManager {\n if (typeof (sessionManager as GuardedPiTranscriptManager).flushPendingToolResults === 'function') {\n return sessionManager as GuardedPiTranscriptManager;\n }\n\n const result = installSessionToolResultGuard(sessionManager, {\n sessionKey: opts?.sessionKey,\n allowSyntheticToolResults: opts?.allowSyntheticToolResults,\n missingToolResultText: opts?.missingToolResultText,\n allowedToolNames: opts?.allowedToolNames,\n maxToolResultChars:\n typeof opts?.contextWindowTokens === 'number'\n ? resolveLiveToolResultMaxChars({\n contextWindowTokens: opts.contextWindowTokens,\n cfg: opts?.config,\n agentId: opts?.agentId,\n })\n : undefined,\n });\n const tagged = sessionManager as GuardedPiTranscriptManager;\n tagged.flushPendingToolResults = result.flushPendingToolResults;\n tagged.clearPendingToolResults = result.clearPendingToolResults;\n return tagged;\n}\n\n/**\n * Recover the original (un-guarded) appendMessage for a session manager.\n * Useful for callers that need a low-level \"bypass the guard\" write path.\n */\nexport function getRawSessionAppendMessage(\n sessionManager: SessionManager,\n): SessionManager['appendMessage'] {\n const stored = (sessionManager as SessionManagerWithRawAppend)[RAW_APPEND_MESSAGE];\n return stored ?? sessionManager.appendMessage.bind(sessionManager);\n}\n\n// ── Internal: persistence + truncation helpers ──────────────────────────────\n\nfunction resolveMaxToolResultChars(opts: { maxToolResultChars?: number }): number {\n return Math.max(1, opts.maxToolResultChars ?? DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS);\n}\n\nfunction capToolResultSize(msg: AgentMessage, maxChars: number): AgentMessage {\n if ((msg as { role?: string }).role !== 'toolResult') {\n return msg;\n }\n return truncateToolResultMessage(msg, maxChars, {\n suffix: (truncatedChars) => formatContextLimitTruncationNotice(truncatedChars),\n minKeepChars: 2_000,\n });\n}\n\n// `details` is runtime/UI metadata, not model-visible tool output. Keep the\n// session JSONL useful for debugging without letting metadata blobs dominate\n// disk, replay repair, transcript broadcasts, or future tooling that reads raw\n// sessions. Model-visible text belongs in tool result `content`.\nconst MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES = 8_192;\nconst MAX_PERSISTED_DETAIL_STRING_CHARS = 2_000;\nconst MAX_PERSISTED_DETAIL_SESSION_COUNT = 10;\nconst MAX_PERSISTED_DETAIL_FALLBACK_STRING_CHARS = 200;\n\nfunction originalDetailsSizeFields(size: BoundedJsonUtf8Bytes): Record<string, number> {\n return size.complete\n ? { originalDetailsBytes: size.bytes }\n : { originalDetailsBytesAtLeast: size.bytes };\n}\n\nfunction truncatePersistedDetailString(\n value: string,\n maxChars = MAX_PERSISTED_DETAIL_STRING_CHARS,\n): string {\n if (value.length <= maxChars) {\n return value;\n }\n return `${value.slice(0, maxChars)}\\n\\n[xopc persisted detail truncated: ${\n value.length - maxChars\n } chars omitted]`;\n}\n\nfunction sanitizePersistedSessionDetail(value: unknown): unknown {\n if (!value || typeof value !== 'object') {\n return value;\n }\n const src = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of [\n 'sessionId',\n 'status',\n 'pid',\n 'startedAt',\n 'endedAt',\n 'runtimeMs',\n 'cwd',\n 'name',\n 'truncated',\n 'exitCode',\n 'exitSignal',\n ]) {\n const field = src[key];\n if (field !== undefined) {\n out[key] = typeof field === 'string' ? truncatePersistedDetailString(field, 500) : field;\n }\n }\n if (typeof src.command === 'string') {\n out.command = truncatePersistedDetailString(src.command, 500);\n }\n return out;\n}\n\nfunction buildPersistedDetailsFallback(\n src: Record<string, unknown> | undefined,\n originalSize: BoundedJsonUtf8Bytes,\n sanitizedBytes?: number,\n): Record<string, unknown> {\n const fallback: Record<string, unknown> = {\n persistedDetailsTruncated: true,\n finalDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n };\n if (sanitizedBytes !== undefined) {\n fallback.sanitizedDetailsBytes = sanitizedBytes;\n }\n if (src) {\n fallback.originalDetailKeys = firstEnumerableOwnKeys(src, 40);\n for (const key of ['status', 'sessionId', 'pid', 'exitCode', 'exitSignal', 'truncated']) {\n const field = src[key];\n if (field !== undefined) {\n fallback[key] =\n typeof field === 'string'\n ? truncatePersistedDetailString(field, MAX_PERSISTED_DETAIL_FALLBACK_STRING_CHARS)\n : field;\n }\n }\n }\n return fallback;\n}\n\nfunction enforcePersistedDetailsByteCap(\n value: Record<string, unknown>,\n src: Record<string, unknown> | undefined,\n originalSize: BoundedJsonUtf8Bytes,\n): Record<string, unknown> {\n const sanitizedBytes = jsonUtf8BytesOrInfinity(value);\n if (sanitizedBytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return value;\n }\n const fallback = buildPersistedDetailsFallback(src, originalSize, sanitizedBytes);\n if (jsonUtf8BytesOrInfinity(fallback) <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return fallback;\n }\n return {\n persistedDetailsTruncated: true,\n finalDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n sanitizedDetailsBytes: sanitizedBytes,\n };\n}\n\nfunction sanitizeToolResultDetailsForPersistence(details: unknown): unknown {\n if (details === undefined || details === null) {\n return details;\n }\n const originalSize = boundedJsonUtf8Bytes(details, MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES);\n if (originalSize.complete && originalSize.bytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return details;\n }\n if (typeof details !== 'object') {\n return enforcePersistedDetailsByteCap(\n {\n persistedDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n valueType: typeof details,\n },\n undefined,\n originalSize,\n );\n }\n const src = details as Record<string, unknown>;\n const out: Record<string, unknown> = {\n persistedDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n originalDetailKeys: firstEnumerableOwnKeys(src, 40),\n };\n for (const key of [\n 'status',\n 'sessionId',\n 'pid',\n 'startedAt',\n 'endedAt',\n 'cwd',\n 'name',\n 'exitCode',\n 'exitSignal',\n 'retryInMs',\n 'total',\n 'totalLines',\n 'totalChars',\n 'truncated',\n 'fullOutputPath',\n 'truncation',\n ]) {\n const field = src[key];\n if (field !== undefined) {\n out[key] = typeof field === 'string' ? truncatePersistedDetailString(field) : field;\n }\n }\n if (typeof src.tail === 'string') {\n out.tail = truncatePersistedDetailString(src.tail);\n }\n if (Array.isArray(src.sessions)) {\n out.sessions = src.sessions\n .slice(0, MAX_PERSISTED_DETAIL_SESSION_COUNT)\n .map(sanitizePersistedSessionDetail);\n if (src.sessions.length > MAX_PERSISTED_DETAIL_SESSION_COUNT) {\n out.sessionsTruncated = src.sessions.length - MAX_PERSISTED_DETAIL_SESSION_COUNT;\n }\n }\n return enforcePersistedDetailsByteCap(out, src, originalSize);\n}\n\nfunction capToolResultDetails(msg: AgentMessage): AgentMessage {\n if ((msg as { role?: string }).role !== 'toolResult') {\n return msg;\n }\n const details = (msg as { details?: unknown }).details;\n const sanitizedDetails = sanitizeToolResultDetailsForPersistence(details);\n if (sanitizedDetails === details) {\n return msg;\n }\n const next = { ...msg } as AgentMessage & { details?: unknown };\n next.details = sanitizedDetails;\n return next;\n}\n\nfunction capToolResultForPersistence(msg: AgentMessage, maxChars: number): AgentMessage {\n return capToolResultDetails(capToolResultSize(msg, maxChars));\n}\n\nfunction normalizePersistedToolResultName(\n message: AgentMessage,\n fallbackName?: string,\n): AgentMessage {\n if ((message as { role?: unknown }).role !== 'toolResult') {\n return message;\n }\n const toolResult = message as Extract<AgentMessage, { role: 'toolResult' }>;\n const rawToolName = (toolResult as { toolName?: unknown }).toolName;\n const normalizedToolName = normalizeOptionalString(rawToolName);\n if (normalizedToolName) {\n if (rawToolName === normalizedToolName) {\n return toolResult;\n }\n return { ...toolResult, toolName: normalizedToolName };\n }\n\n const normalizedFallback = normalizeOptionalString(fallbackName);\n if (normalizedFallback) {\n return { ...toolResult, toolName: normalizedFallback };\n }\n\n if (typeof rawToolName === 'string') {\n return { ...toolResult, toolName: 'unknown' };\n }\n return toolResult;\n}\n\n// ── Internal: raw-append symbol storage ─────────────────────────────────────\n\nconst RAW_APPEND_MESSAGE = Symbol('xopc.session.rawAppendMessage');\n\ntype SessionManagerWithRawAppend = SessionManager & {\n [RAW_APPEND_MESSAGE]?: SessionManager['appendMessage'];\n};\n\nfunction rememberOriginalAppend(\n sessionManager: SessionManager,\n): SessionManager['appendMessage'] {\n const tagged = sessionManager as SessionManagerWithRawAppend;\n const stored = tagged[RAW_APPEND_MESSAGE];\n if (stored) {\n return stored;\n }\n const original = sessionManager.appendMessage.bind(sessionManager);\n tagged[RAW_APPEND_MESSAGE] = original;\n return original;\n}\n\n// ── Internal: pending tool-call state ──────────────────────────────────────\n\ntype PendingToolCall = { id: string; name?: string };\n\nclass PendingToolCallTracker {\n private readonly pending = new Map<string, string | undefined>();\n\n size(): number {\n return this.pending.size;\n }\n\n getToolName(id: string): string | undefined {\n return this.pending.get(id);\n }\n\n delete(id: string): void {\n this.pending.delete(id);\n }\n\n clear(): void {\n this.pending.clear();\n }\n\n trackToolCalls(calls: readonly PendingToolCall[]): void {\n for (const call of calls) {\n this.pending.set(call.id, call.name);\n }\n }\n\n entries(): IterableIterator<[string, string | undefined]> {\n return this.pending.entries();\n }\n\n getPendingIds(): string[] {\n return Array.from(this.pending.keys());\n }\n\n shouldFlushForSanitizedDrop(): boolean {\n return this.pending.size > 0;\n }\n\n shouldFlushBeforeNonToolResult(nextRole: unknown, toolCallCount: number): boolean {\n return this.pending.size > 0 && (toolCallCount === 0 || nextRole !== 'assistant');\n }\n\n shouldFlushBeforeNewToolCalls(toolCallCount: number): boolean {\n return this.pending.size > 0 && toolCallCount > 0;\n }\n}\n\n// ── ToolResultGuard class ──────────────────────────────────────────────────\n\nclass ToolResultGuard {\n private readonly sessionManager: SessionManager;\n private readonly opts: ToolResultGuardOptions;\n private readonly pending = new PendingToolCallTracker();\n private readonly originalAppend: SessionManager['appendMessage'];\n private readonly allowSyntheticToolResults: boolean;\n private readonly maxToolResultChars: number;\n\n constructor(sessionManager: SessionManager, opts: ToolResultGuardOptions) {\n this.sessionManager = sessionManager;\n this.opts = opts;\n this.originalAppend = rememberOriginalAppend(sessionManager);\n this.allowSyntheticToolResults = opts.allowSyntheticToolResults ?? true;\n this.maxToolResultChars = resolveMaxToolResultChars(opts);\n }\n\n /** Monkey-patch the session manager so pi-coding-agent's internal appendMessage flows through us. */\n attach(): void {\n const bound = this.guardedAppend.bind(this);\n this.sessionManager.appendMessage = bound as SessionManager['appendMessage'];\n }\n\n flushPending(): void {\n if (this.pending.size() === 0) {\n return;\n }\n if (this.allowSyntheticToolResults) {\n for (const [id, name] of this.pending.entries()) {\n const synthetic = makeMissingToolResult({\n toolCallId: id,\n toolName: name,\n text: this.opts.missingToolResultText,\n });\n const flushed = this.applyBeforeWriteHook(\n this.persistToolResult(this.persistMessage(synthetic), {\n toolCallId: id,\n toolName: name,\n isSynthetic: true,\n }),\n );\n if (flushed) {\n this.originalAppend(capToolResultForPersistence(flushed, this.maxToolResultChars) as never);\n }\n }\n }\n this.pending.clear();\n }\n\n clearPending(): void {\n this.pending.clear();\n }\n\n getPendingIds(): string[] {\n return this.pending.getPendingIds();\n }\n\n private persistMessage(message: AgentMessage): AgentMessage {\n const transformer = this.opts.transformMessageForPersistence;\n return transformer ? transformer(message) : message;\n }\n\n private persistToolResult(\n message: AgentMessage,\n meta: { toolCallId?: string; toolName?: string; isSynthetic?: boolean },\n ): AgentMessage {\n const transformer = this.opts.transformToolResultForPersistence;\n return transformer ? transformer(message, meta) : message;\n }\n\n /**\n * Run the before_message_write hook. Returns the (possibly modified) message,\n * or null if the message should be blocked.\n */\n private applyBeforeWriteHook(msg: AgentMessage): AgentMessage | null {\n const beforeWrite = this.opts.beforeMessageWriteHook;\n if (!beforeWrite) {\n return msg;\n }\n const result = beforeWrite({ message: msg });\n if (result?.block) {\n return null;\n }\n if (result?.message) {\n return result.message;\n }\n return msg;\n }\n\n private guardedAppend(message: AgentMessage): unknown {\n let nextMessage = message;\n const role = (message as { role?: unknown }).role;\n if (role === 'assistant') {\n const sanitized = sanitizeToolCallInputs([message], {\n allowedToolNames: this.opts.allowedToolNames,\n });\n if (sanitized.length === 0) {\n if (this.pending.shouldFlushForSanitizedDrop()) {\n this.flushPending();\n }\n return undefined;\n }\n nextMessage = sanitized[0];\n }\n const nextRole = (nextMessage as { role?: unknown }).role;\n\n if (nextRole === 'toolResult') {\n const id = extractToolResultId(nextMessage as Extract<AgentMessage, { role: 'toolResult' }>);\n const toolName = id ? this.pending.getToolName(id) : undefined;\n if (id) {\n this.pending.delete(id);\n }\n const normalizedToolResult = normalizePersistedToolResultName(nextMessage, toolName);\n // Apply hard size cap before persistence to prevent oversized tool results\n // from consuming the entire context window on subsequent LLM calls.\n const capped = capToolResultForPersistence(\n this.persistMessage(normalizedToolResult),\n this.maxToolResultChars,\n );\n const persisted = this.applyBeforeWriteHook(\n this.persistToolResult(capped, {\n toolCallId: id ?? undefined,\n toolName,\n isSynthetic: false,\n }),\n );\n if (!persisted) {\n return undefined;\n }\n return this.originalAppend(capToolResultForPersistence(persisted, this.maxToolResultChars) as never);\n }\n\n // Skip tool call extraction for aborted/errored assistant messages.\n // When stopReason is \"error\" or \"aborted\", the tool_use blocks may be incomplete\n // and should not have synthetic tool_results created. Creating synthetic results\n // for incomplete tool calls causes API 400 errors:\n // \"unexpected tool_use_id found in tool_result blocks\"\n // This matches the behavior in repairToolUseResultPairing (session-transcript-repair.ts)\n const stopReason = (nextMessage as { stopReason?: string }).stopReason;\n const toolCalls =\n nextRole === 'assistant' && stopReason !== 'aborted' && stopReason !== 'error'\n ? extractToolCallsFromAssistant(nextMessage as Extract<AgentMessage, { role: 'assistant' }>)\n : [];\n\n // Always clear pending tool call state before appending non-tool-result messages.\n // flushPendingToolResults() only inserts synthetic results when allowSyntheticToolResults\n // is true; it always clears the pending map. Without this, providers that disable\n // synthetic results (e.g. OpenAI) accumulate stale pending state when a user message\n // interrupts in-flight tool calls, leaving orphaned tool_use blocks in the transcript\n // that cause API 400 errors on subsequent requests.\n if (this.pending.shouldFlushBeforeNonToolResult(nextRole, toolCalls.length)) {\n this.flushPending();\n }\n // If new tool calls arrive while older ones are pending, flush the old ones first.\n if (this.pending.shouldFlushBeforeNewToolCalls(toolCalls.length)) {\n this.flushPending();\n }\n\n const finalMessage = this.applyBeforeWriteHook(this.persistMessage(nextMessage));\n if (!finalMessage) {\n return undefined;\n }\n const result = this.originalAppend(finalMessage as never);\n\n const sessionFile = (\n this.sessionManager as { getSessionFile?: () => string | null }\n ).getSessionFile?.();\n if (sessionFile) {\n emitSessionTranscriptUpdate({\n sessionFile,\n sessionKey: this.opts.sessionKey,\n message: finalMessage,\n messageId: typeof result === 'string' ? result : undefined,\n });\n }\n\n if (toolCalls.length > 0) {\n this.pending.trackToolCalls(toolCalls);\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-tool-result-guard.js","names":[],"sources":["../../../../src/agent/embedded/session-tool-result-guard.ts"],"sourcesContent":["/**\n * Session tool-result guard — wraps a pi `SessionManager.appendMessage` to:\n * 1. cap oversized tool-result text + details so they cannot blow up the next\n * LLM request (size + persistence limits).\n * 2. track pending tool-call IDs so missing tool results can be synthesised\n * (some providers refuse a turn that has an orphan tool_use block).\n * 3. drop assistant `toolCall` blocks whose tool name is not in the allowlist\n * (these would also trigger provider 400s).\n * 4. broadcast `xopc:transcript-row` updates so the gateway UI can stream them.\n *\n * Previously this module shipped with three sibling files\n * (`session-tool-result-state.ts`, `session-raw-append-message.ts`,\n * `session-tool-result-guard-wrapper.ts`). They are now consolidated here as\n * private constructs around the `ToolResultGuard` class. Pi-coding-agent owns\n * the `SessionManager` instance and calls `appendMessage` from inside the\n * runtime, so we still need to monkey-patch that method — but the patched\n * implementation is just `guard.guardedAppend.bind(guard)` and all state lives\n * on the class.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { SessionManager } from '@earendil-works/pi-coding-agent';\n\nimport type { Config } from '../../config/schema.js';\nimport {\n boundedJsonUtf8Bytes,\n firstEnumerableOwnKeys,\n jsonUtf8BytesOrInfinity,\n type BoundedJsonUtf8Bytes,\n} from '../../infra/json-utf8-bytes.js';\nimport { emitSessionTranscriptUpdate } from '../../session/transcript-events.js';\nimport { normalizeOptionalString } from '../../utils/string-coerce.js';\nimport { formatContextLimitTruncationNotice } from './tool-result-context-guard.js';\nimport {\n DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS,\n resolveLiveToolResultMaxChars,\n truncateToolResultMessage,\n} from './tool-result-truncation.js';\nimport {\n makeMissingToolResult,\n sanitizeToolCallInputs,\n} from '../transcript/session-transcript-repair.js';\nimport {\n extractToolCallsFromAssistant,\n extractToolResultId,\n} from '../transcript/tool-call-id.js';\n\n// ── Public surface ──────────────────────────────────────────────────────────\n\nexport type BeforeMessageWriteHookEvent = { message: AgentMessage };\nexport type BeforeMessageWriteHookResult =\n | { block?: boolean; message?: AgentMessage }\n | undefined;\n\nexport interface ToolResultGuardOptions {\n /** Optional session key for transcript update broadcasts. */\n sessionKey?: string;\n /** Optional transform applied to any message before persistence. */\n transformMessageForPersistence?: (message: AgentMessage) => AgentMessage;\n /**\n * Optional, synchronous transform applied to toolResult messages *before* they are\n * persisted to the session transcript.\n */\n transformToolResultForPersistence?: (\n message: AgentMessage,\n meta: { toolCallId?: string; toolName?: string; isSynthetic?: boolean },\n ) => AgentMessage;\n /**\n * Whether to synthesize missing tool results to satisfy strict providers.\n * Defaults to true.\n */\n allowSyntheticToolResults?: boolean;\n missingToolResultText?: string;\n /**\n * Optional set/list of tool names accepted for assistant toolCall/toolUse blocks.\n * When set, tool calls with unknown names are dropped before persistence.\n */\n allowedToolNames?: Iterable<string>;\n /**\n * Synchronous hook invoked before any message is written to the session JSONL.\n * If the hook returns { block: true }, the message is silently dropped.\n * If it returns { message }, the modified message is written instead.\n */\n beforeMessageWriteHook?: (event: BeforeMessageWriteHookEvent) => BeforeMessageWriteHookResult;\n maxToolResultChars?: number;\n}\n\nexport interface InstallSessionToolResultGuardResult {\n flushPendingToolResults: () => void;\n clearPendingToolResults: () => void;\n getPendingIds: () => string[];\n}\n\n/** Idempotent wrapper that also adds the helper methods consumers expect. */\nexport type GuardedPiTranscriptManager = SessionManager & {\n flushPendingToolResults?: () => void;\n clearPendingToolResults?: () => void;\n};\n\n/**\n * Install the guard on a SessionManager and return its control API.\n * Subsequent assistant/toolResult writes by pi-coding-agent flow through the\n * guard transparently.\n */\nexport function installSessionToolResultGuard(\n sessionManager: SessionManager,\n opts: ToolResultGuardOptions = {},\n): InstallSessionToolResultGuardResult {\n const guard = new ToolResultGuard(sessionManager, opts);\n guard.attach();\n return {\n flushPendingToolResults: () => guard.flushPending(),\n clearPendingToolResults: () => guard.clearPending(),\n getPendingIds: () => guard.getPendingIds(),\n };\n}\n\n/**\n * Convenience wrapper used by the embedded runner pool: install the guard\n * (idempotent), pin the size cap from the model context window, and expose\n * `flushPendingToolResults` / `clearPendingToolResults` directly on the\n * SessionManager instance so callers do not need to keep the install result.\n */\nexport function guardSessionManager(\n sessionManager: SessionManager,\n opts?: {\n agentId?: string;\n sessionKey?: string;\n config?: Config;\n contextWindowTokens?: number;\n allowSyntheticToolResults?: boolean;\n missingToolResultText?: string;\n allowedToolNames?: Iterable<string>;\n },\n): GuardedPiTranscriptManager {\n if (typeof (sessionManager as GuardedPiTranscriptManager).flushPendingToolResults === 'function') {\n return sessionManager as GuardedPiTranscriptManager;\n }\n\n const result = installSessionToolResultGuard(sessionManager, {\n sessionKey: opts?.sessionKey,\n allowSyntheticToolResults: opts?.allowSyntheticToolResults,\n missingToolResultText: opts?.missingToolResultText,\n allowedToolNames: opts?.allowedToolNames,\n maxToolResultChars:\n typeof opts?.contextWindowTokens === 'number'\n ? resolveLiveToolResultMaxChars({\n contextWindowTokens: opts.contextWindowTokens,\n cfg: opts?.config,\n agentId: opts?.agentId,\n })\n : undefined,\n });\n const tagged = sessionManager as GuardedPiTranscriptManager;\n tagged.flushPendingToolResults = result.flushPendingToolResults;\n tagged.clearPendingToolResults = result.clearPendingToolResults;\n return tagged;\n}\n\n/**\n * Recover the original (un-guarded) appendMessage for a session manager.\n * Useful for callers that need a low-level \"bypass the guard\" write path.\n */\nexport function getRawSessionAppendMessage(\n sessionManager: SessionManager,\n): SessionManager['appendMessage'] {\n const stored = (sessionManager as SessionManagerWithRawAppend)[RAW_APPEND_MESSAGE];\n return stored ?? sessionManager.appendMessage.bind(sessionManager);\n}\n\n// ── Internal: persistence + truncation helpers ──────────────────────────────\n\nfunction resolveMaxToolResultChars(opts: { maxToolResultChars?: number }): number {\n return Math.max(1, opts.maxToolResultChars ?? DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS);\n}\n\nfunction capToolResultSize(msg: AgentMessage, maxChars: number): AgentMessage {\n if ((msg as { role?: string }).role !== 'toolResult') {\n return msg;\n }\n return truncateToolResultMessage(msg, maxChars, {\n suffix: (truncatedChars) => formatContextLimitTruncationNotice(truncatedChars),\n minKeepChars: 2_000,\n });\n}\n\n// `details` is runtime/UI metadata, not model-visible tool output. Keep the\n// session JSONL useful for debugging without letting metadata blobs dominate\n// disk, replay repair, transcript broadcasts, or future tooling that reads raw\n// sessions. Model-visible text belongs in tool result `content`.\nconst MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES = 8_192;\nconst MAX_PERSISTED_DETAIL_STRING_CHARS = 2_000;\nconst MAX_PERSISTED_DETAIL_SESSION_COUNT = 10;\nconst MAX_PERSISTED_DETAIL_FALLBACK_STRING_CHARS = 200;\n\nfunction originalDetailsSizeFields(size: BoundedJsonUtf8Bytes): Record<string, number> {\n return size.complete\n ? { originalDetailsBytes: size.bytes }\n : { originalDetailsBytesAtLeast: size.bytes };\n}\n\nfunction truncatePersistedDetailString(\n value: string,\n maxChars = MAX_PERSISTED_DETAIL_STRING_CHARS,\n): string {\n if (value.length <= maxChars) {\n return value;\n }\n return `${value.slice(0, maxChars)}\\n\\n[xopc persisted detail truncated: ${\n value.length - maxChars\n } chars omitted]`;\n}\n\nfunction sanitizePersistedSessionDetail(value: unknown): unknown {\n if (!value || typeof value !== 'object') {\n return value;\n }\n const src = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of [\n 'sessionId',\n 'status',\n 'pid',\n 'startedAt',\n 'endedAt',\n 'runtimeMs',\n 'cwd',\n 'name',\n 'truncated',\n 'exitCode',\n 'exitSignal',\n ]) {\n const field = src[key];\n if (field !== undefined) {\n out[key] = typeof field === 'string' ? truncatePersistedDetailString(field, 500) : field;\n }\n }\n if (typeof src.command === 'string') {\n out.command = truncatePersistedDetailString(src.command, 500);\n }\n return out;\n}\n\nfunction buildPersistedDetailsFallback(\n src: Record<string, unknown> | undefined,\n originalSize: BoundedJsonUtf8Bytes,\n sanitizedBytes?: number,\n): Record<string, unknown> {\n const fallback: Record<string, unknown> = {\n persistedDetailsTruncated: true,\n finalDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n };\n if (sanitizedBytes !== undefined) {\n fallback.sanitizedDetailsBytes = sanitizedBytes;\n }\n if (src) {\n fallback.originalDetailKeys = firstEnumerableOwnKeys(src, 40);\n for (const key of ['status', 'sessionId', 'pid', 'exitCode', 'exitSignal', 'truncated']) {\n const field = src[key];\n if (field !== undefined) {\n fallback[key] =\n typeof field === 'string'\n ? truncatePersistedDetailString(field, MAX_PERSISTED_DETAIL_FALLBACK_STRING_CHARS)\n : field;\n }\n }\n }\n return fallback;\n}\n\nfunction enforcePersistedDetailsByteCap(\n value: Record<string, unknown>,\n src: Record<string, unknown> | undefined,\n originalSize: BoundedJsonUtf8Bytes,\n): Record<string, unknown> {\n const sanitizedBytes = jsonUtf8BytesOrInfinity(value);\n if (sanitizedBytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return value;\n }\n const fallback = buildPersistedDetailsFallback(src, originalSize, sanitizedBytes);\n if (jsonUtf8BytesOrInfinity(fallback) <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return fallback;\n }\n return {\n persistedDetailsTruncated: true,\n finalDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n sanitizedDetailsBytes: sanitizedBytes,\n };\n}\n\nfunction sanitizeToolResultDetailsForPersistence(details: unknown): unknown {\n if (details === undefined || details === null) {\n return details;\n }\n const originalSize = boundedJsonUtf8Bytes(details, MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES);\n if (originalSize.complete && originalSize.bytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return details;\n }\n if (typeof details !== 'object') {\n return enforcePersistedDetailsByteCap(\n {\n persistedDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n valueType: typeof details,\n },\n undefined,\n originalSize,\n );\n }\n const src = details as Record<string, unknown>;\n const out: Record<string, unknown> = {\n persistedDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n originalDetailKeys: firstEnumerableOwnKeys(src, 40),\n };\n for (const key of [\n 'status',\n 'sessionId',\n 'pid',\n 'startedAt',\n 'endedAt',\n 'cwd',\n 'name',\n 'exitCode',\n 'exitSignal',\n 'retryInMs',\n 'total',\n 'totalLines',\n 'totalChars',\n 'truncated',\n 'fullOutputPath',\n 'truncation',\n ]) {\n const field = src[key];\n if (field !== undefined) {\n out[key] = typeof field === 'string' ? truncatePersistedDetailString(field) : field;\n }\n }\n if (typeof src.tail === 'string') {\n out.tail = truncatePersistedDetailString(src.tail);\n }\n if (Array.isArray(src.sessions)) {\n out.sessions = src.sessions\n .slice(0, MAX_PERSISTED_DETAIL_SESSION_COUNT)\n .map(sanitizePersistedSessionDetail);\n if (src.sessions.length > MAX_PERSISTED_DETAIL_SESSION_COUNT) {\n out.sessionsTruncated = src.sessions.length - MAX_PERSISTED_DETAIL_SESSION_COUNT;\n }\n }\n return enforcePersistedDetailsByteCap(out, src, originalSize);\n}\n\nfunction capToolResultDetails(msg: AgentMessage): AgentMessage {\n if ((msg as { role?: string }).role !== 'toolResult') {\n return msg;\n }\n const details = (msg as { details?: unknown }).details;\n const sanitizedDetails = sanitizeToolResultDetailsForPersistence(details);\n if (sanitizedDetails === details) {\n return msg;\n }\n const next = { ...msg } as AgentMessage & { details?: unknown };\n next.details = sanitizedDetails;\n return next;\n}\n\nfunction capToolResultForPersistence(msg: AgentMessage, maxChars: number): AgentMessage {\n return capToolResultDetails(capToolResultSize(msg, maxChars));\n}\n\nfunction normalizePersistedToolResultName(\n message: AgentMessage,\n fallbackName?: string,\n): AgentMessage {\n if ((message as { role?: unknown }).role !== 'toolResult') {\n return message;\n }\n const toolResult = message as Extract<AgentMessage, { role: 'toolResult' }>;\n const rawToolName = (toolResult as { toolName?: unknown }).toolName;\n const normalizedToolName = normalizeOptionalString(rawToolName);\n if (normalizedToolName) {\n if (rawToolName === normalizedToolName) {\n return toolResult;\n }\n return { ...toolResult, toolName: normalizedToolName };\n }\n\n const normalizedFallback = normalizeOptionalString(fallbackName);\n if (normalizedFallback) {\n return { ...toolResult, toolName: normalizedFallback };\n }\n\n if (typeof rawToolName === 'string') {\n return { ...toolResult, toolName: 'unknown' };\n }\n return toolResult;\n}\n\n// ── Internal: raw-append symbol storage ─────────────────────────────────────\n\nconst RAW_APPEND_MESSAGE = Symbol('xopc.session.rawAppendMessage');\n\ntype SessionManagerWithRawAppend = SessionManager & {\n [RAW_APPEND_MESSAGE]?: SessionManager['appendMessage'];\n};\n\nfunction rememberOriginalAppend(\n sessionManager: SessionManager,\n): SessionManager['appendMessage'] {\n const tagged = sessionManager as SessionManagerWithRawAppend;\n const stored = tagged[RAW_APPEND_MESSAGE];\n if (stored) {\n return stored;\n }\n const original = sessionManager.appendMessage.bind(sessionManager);\n tagged[RAW_APPEND_MESSAGE] = original;\n return original;\n}\n\n// ── Internal: pending tool-call state ──────────────────────────────────────\n\ntype PendingToolCall = { id: string; name?: string };\n\nclass PendingToolCallTracker {\n private readonly pending = new Map<string, string | undefined>();\n\n size(): number {\n return this.pending.size;\n }\n\n getToolName(id: string): string | undefined {\n return this.pending.get(id);\n }\n\n delete(id: string): void {\n this.pending.delete(id);\n }\n\n clear(): void {\n this.pending.clear();\n }\n\n trackToolCalls(calls: readonly PendingToolCall[]): void {\n for (const call of calls) {\n this.pending.set(call.id, call.name);\n }\n }\n\n entries(): IterableIterator<[string, string | undefined]> {\n return this.pending.entries();\n }\n\n getPendingIds(): string[] {\n return Array.from(this.pending.keys());\n }\n\n shouldFlushForSanitizedDrop(): boolean {\n return this.pending.size > 0;\n }\n\n shouldFlushBeforeNonToolResult(nextRole: unknown, toolCallCount: number): boolean {\n return this.pending.size > 0 && (toolCallCount === 0 || nextRole !== 'assistant');\n }\n\n shouldFlushBeforeNewToolCalls(toolCallCount: number): boolean {\n return this.pending.size > 0 && toolCallCount > 0;\n }\n}\n\n// ── ToolResultGuard class ──────────────────────────────────────────────────\n\nclass ToolResultGuard {\n private readonly sessionManager: SessionManager;\n private readonly opts: ToolResultGuardOptions;\n private readonly pending = new PendingToolCallTracker();\n private readonly originalAppend: SessionManager['appendMessage'];\n private readonly allowSyntheticToolResults: boolean;\n private readonly maxToolResultChars: number;\n\n constructor(sessionManager: SessionManager, opts: ToolResultGuardOptions) {\n this.sessionManager = sessionManager;\n this.opts = opts;\n this.originalAppend = rememberOriginalAppend(sessionManager);\n this.allowSyntheticToolResults = opts.allowSyntheticToolResults ?? true;\n this.maxToolResultChars = resolveMaxToolResultChars(opts);\n }\n\n /** Monkey-patch the session manager so pi-coding-agent's internal appendMessage flows through us. */\n attach(): void {\n const bound = this.guardedAppend.bind(this);\n this.sessionManager.appendMessage = bound as SessionManager['appendMessage'];\n }\n\n flushPending(): void {\n if (this.pending.size() === 0) {\n return;\n }\n if (this.allowSyntheticToolResults) {\n for (const [id, name] of this.pending.entries()) {\n const synthetic = makeMissingToolResult({\n toolCallId: id,\n toolName: name,\n text: this.opts.missingToolResultText,\n });\n const flushed = this.applyBeforeWriteHook(\n this.persistToolResult(this.persistMessage(synthetic), {\n toolCallId: id,\n toolName: name,\n isSynthetic: true,\n }),\n );\n if (flushed) {\n this.originalAppend(capToolResultForPersistence(flushed, this.maxToolResultChars) as never);\n }\n }\n }\n this.pending.clear();\n }\n\n clearPending(): void {\n this.pending.clear();\n }\n\n getPendingIds(): string[] {\n return this.pending.getPendingIds();\n }\n\n private persistMessage(message: AgentMessage): AgentMessage {\n const transformer = this.opts.transformMessageForPersistence;\n return transformer ? transformer(message) : message;\n }\n\n private persistToolResult(\n message: AgentMessage,\n meta: { toolCallId?: string; toolName?: string; isSynthetic?: boolean },\n ): AgentMessage {\n const transformer = this.opts.transformToolResultForPersistence;\n return transformer ? transformer(message, meta) : message;\n }\n\n /**\n * Run the before_message_write hook. Returns the (possibly modified) message,\n * or null if the message should be blocked.\n */\n private applyBeforeWriteHook(msg: AgentMessage): AgentMessage | null {\n const beforeWrite = this.opts.beforeMessageWriteHook;\n if (!beforeWrite) {\n return msg;\n }\n const result = beforeWrite({ message: msg });\n if (result?.block) {\n return null;\n }\n if (result?.message) {\n return result.message;\n }\n return msg;\n }\n\n private guardedAppend(message: AgentMessage): unknown {\n let nextMessage = message;\n const role = (message as { role?: unknown }).role;\n if (role === 'assistant') {\n const sanitized = sanitizeToolCallInputs([message], {\n allowedToolNames: this.opts.allowedToolNames,\n });\n if (sanitized.length === 0) {\n if (this.pending.shouldFlushForSanitizedDrop()) {\n this.flushPending();\n }\n return undefined;\n }\n nextMessage = sanitized[0];\n }\n const nextRole = (nextMessage as { role?: unknown }).role;\n\n if (nextRole === 'toolResult') {\n const id = extractToolResultId(nextMessage as Extract<AgentMessage, { role: 'toolResult' }>);\n const toolName = id ? this.pending.getToolName(id) : undefined;\n if (id) {\n this.pending.delete(id);\n }\n const normalizedToolResult = normalizePersistedToolResultName(nextMessage, toolName);\n // Apply hard size cap before persistence to prevent oversized tool results\n // from consuming the entire context window on subsequent LLM calls.\n const capped = capToolResultForPersistence(\n this.persistMessage(normalizedToolResult),\n this.maxToolResultChars,\n );\n const persisted = this.applyBeforeWriteHook(\n this.persistToolResult(capped, {\n toolCallId: id ?? undefined,\n toolName,\n isSynthetic: false,\n }),\n );\n if (!persisted) {\n return undefined;\n }\n return this.originalAppend(capToolResultForPersistence(persisted, this.maxToolResultChars) as never);\n }\n\n // Skip tool call extraction for aborted/errored assistant messages.\n // When stopReason is \"error\" or \"aborted\", the tool_use blocks may be incomplete\n // and should not have synthetic tool_results created. Creating synthetic results\n // for incomplete tool calls causes API 400 errors:\n // \"unexpected tool_use_id found in tool_result blocks\"\n // This matches the behavior in repairToolUseResultPairing (session-transcript-repair.ts)\n const stopReason = (nextMessage as { stopReason?: string }).stopReason;\n const toolCalls =\n nextRole === 'assistant' && stopReason !== 'aborted' && stopReason !== 'error'\n ? extractToolCallsFromAssistant(nextMessage as Extract<AgentMessage, { role: 'assistant' }>)\n : [];\n\n // Always clear pending tool call state before appending non-tool-result messages.\n // flushPendingToolResults() only inserts synthetic results when allowSyntheticToolResults\n // is true; it always clears the pending map. Without this, providers that disable\n // synthetic results (e.g. OpenAI) accumulate stale pending state when a user message\n // interrupts in-flight tool calls, leaving orphaned tool_use blocks in the transcript\n // that cause API 400 errors on subsequent requests.\n if (this.pending.shouldFlushBeforeNonToolResult(nextRole, toolCalls.length)) {\n this.flushPending();\n }\n // If new tool calls arrive while older ones are pending, flush the old ones first.\n if (this.pending.shouldFlushBeforeNewToolCalls(toolCalls.length)) {\n this.flushPending();\n }\n\n const finalMessage = this.applyBeforeWriteHook(this.persistMessage(nextMessage));\n if (!finalMessage) {\n return undefined;\n }\n const result = this.originalAppend(finalMessage as never);\n\n if (this.opts.sessionKey) {\n emitSessionTranscriptUpdate({\n sessionKey: this.opts.sessionKey,\n message: finalMessage,\n messageId: typeof result === 'string' ? result : undefined,\n });\n }\n\n if (toolCalls.length > 0) {\n this.pending.trackToolCalls(toolCalls);\n }\n\n return result;\n }\n}\n"],"mappings":";;;;;;;;oBA+BuE;;;;;;AAyEvE,SAAgB,8BACd,gBACA,OAA+B,EAAE,EACI;CACrC,MAAM,QAAQ,IAAI,gBAAgB,gBAAgB,KAAK;AACvD,OAAM,QAAQ;AACd,QAAO;EACL,+BAA+B,MAAM,cAAc;EACnD,+BAA+B,MAAM,cAAc;EACnD,qBAAqB,MAAM,eAAe;EAC3C;;;;;;;;AASH,SAAgB,oBACd,gBACA,MAS4B;AAC5B,KAAI,OAAQ,eAA8C,4BAA4B,WACpF,QAAO;CAGT,MAAM,SAAS,8BAA8B,gBAAgB;EAC3D,YAAY,MAAM;EAClB,2BAA2B,MAAM;EACjC,uBAAuB,MAAM;EAC7B,kBAAkB,MAAM;EACxB,oBACE,OAAO,MAAM,wBAAwB,WACjC,8BAA8B;GAC5B,qBAAqB,KAAK;GAC1B,KAAK,MAAM;GACX,SAAS,MAAM;GAChB,CAAC,GACF,KAAA;EACP,CAAC;CACF,MAAM,SAAS;AACf,QAAO,0BAA0B,OAAO;AACxC,QAAO,0BAA0B,OAAO;AACxC,QAAO;;;;;;AAOT,SAAgB,2BACd,gBACiC;AAEjC,QADgB,eAA+C,uBAC9C,eAAe,cAAc,KAAK,eAAe;;AAKpE,SAAS,0BAA0B,MAA+C;AAChF,QAAO,KAAK,IAAI,GAAG,KAAK,sBAAA,KAAyD;;AAGnF,SAAS,kBAAkB,KAAmB,UAAgC;AAC5E,KAAK,IAA0B,SAAS,aACtC,QAAO;AAET,QAAO,0BAA0B,KAAK,UAAU;EAC9C,SAAS,mBAAmB,mCAAmC,eAAe;EAC9E,cAAc;EACf,CAAC;;AAOJ,MAAM,0CAA0C;AAChD,MAAM,oCAAoC;AAC1C,MAAM,qCAAqC;AAC3C,MAAM,6CAA6C;AAEnD,SAAS,0BAA0B,MAAoD;AACrF,QAAO,KAAK,WACR,EAAE,sBAAsB,KAAK,OAAO,GACpC,EAAE,6BAA6B,KAAK,OAAO;;AAGjD,SAAS,8BACP,OACA,WAAW,mCACH;AACR,KAAI,MAAM,UAAU,SAClB,QAAO;AAET,QAAO,GAAG,MAAM,MAAM,GAAG,SAAS,CAAC,wCACjC,MAAM,SAAS,SAChB;;AAGH,SAAS,+BAA+B,OAAyB;AAC/D,KAAI,CAAC,SAAS,OAAO,UAAU,SAC7B,QAAO;CAET,MAAM,MAAM;CACZ,MAAM,MAA+B,EAAE;AACvC,MAAK,MAAM,OAAO;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,EAAE;EACD,MAAM,QAAQ,IAAI;AAClB,MAAI,UAAU,KAAA,EACZ,KAAI,OAAO,OAAO,UAAU,WAAW,8BAA8B,OAAO,IAAI,GAAG;;AAGvF,KAAI,OAAO,IAAI,YAAY,SACzB,KAAI,UAAU,8BAA8B,IAAI,SAAS,IAAI;AAE/D,QAAO;;AAGT,SAAS,8BACP,KACA,cACA,gBACyB;CACzB,MAAM,WAAoC;EACxC,2BAA2B;EAC3B,uBAAuB;EACvB,GAAG,0BAA0B,aAAa;EAC3C;AACD,KAAI,mBAAmB,KAAA,EACrB,UAAS,wBAAwB;AAEnC,KAAI,KAAK;AACP,WAAS,qBAAqB,uBAAuB,KAAK,GAAG;AAC7D,OAAK,MAAM,OAAO;GAAC;GAAU;GAAa;GAAO;GAAY;GAAc;GAAY,EAAE;GACvF,MAAM,QAAQ,IAAI;AAClB,OAAI,UAAU,KAAA,EACZ,UAAS,OACP,OAAO,UAAU,WACb,8BAA8B,OAAO,2CAA2C,GAChF;;;AAIZ,QAAO;;AAGT,SAAS,+BACP,OACA,KACA,cACyB;CACzB,MAAM,iBAAiB,wBAAwB,MAAM;AACrD,KAAI,kBAAkB,wCACpB,QAAO;CAET,MAAM,WAAW,8BAA8B,KAAK,cAAc,eAAe;AACjF,KAAI,wBAAwB,SAAS,IAAI,wCACvC,QAAO;AAET,QAAO;EACL,2BAA2B;EAC3B,uBAAuB;EACvB,GAAG,0BAA0B,aAAa;EAC1C,uBAAuB;EACxB;;AAGH,SAAS,wCAAwC,SAA2B;AAC1E,KAAI,YAAY,KAAA,KAAa,YAAY,KACvC,QAAO;CAET,MAAM,eAAe,qBAAqB,SAAS,wCAAwC;AAC3F,KAAI,aAAa,YAAY,aAAa,SAAS,wCACjD,QAAO;AAET,KAAI,OAAO,YAAY,SACrB,QAAO,+BACL;EACE,2BAA2B;EAC3B,GAAG,0BAA0B,aAAa;EAC1C,WAAW,OAAO;EACnB,EACD,KAAA,GACA,aACD;CAEH,MAAM,MAAM;CACZ,MAAM,MAA+B;EACnC,2BAA2B;EAC3B,GAAG,0BAA0B,aAAa;EAC1C,oBAAoB,uBAAuB,KAAK,GAAG;EACpD;AACD,MAAK,MAAM,OAAO;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,EAAE;EACD,MAAM,QAAQ,IAAI;AAClB,MAAI,UAAU,KAAA,EACZ,KAAI,OAAO,OAAO,UAAU,WAAW,8BAA8B,MAAM,GAAG;;AAGlF,KAAI,OAAO,IAAI,SAAS,SACtB,KAAI,OAAO,8BAA8B,IAAI,KAAK;AAEpD,KAAI,MAAM,QAAQ,IAAI,SAAS,EAAE;AAC/B,MAAI,WAAW,IAAI,SAChB,MAAM,GAAG,mCAAmC,CAC5C,IAAI,+BAA+B;AACtC,MAAI,IAAI,SAAS,SAAS,mCACxB,KAAI,oBAAoB,IAAI,SAAS,SAAS;;AAGlD,QAAO,+BAA+B,KAAK,KAAK,aAAa;;AAG/D,SAAS,qBAAqB,KAAiC;AAC7D,KAAK,IAA0B,SAAS,aACtC,QAAO;CAET,MAAM,UAAW,IAA8B;CAC/C,MAAM,mBAAmB,wCAAwC,QAAQ;AACzE,KAAI,qBAAqB,QACvB,QAAO;CAET,MAAM,OAAO,EAAE,GAAG,KAAK;AACvB,MAAK,UAAU;AACf,QAAO;;AAGT,SAAS,4BAA4B,KAAmB,UAAgC;AACtF,QAAO,qBAAqB,kBAAkB,KAAK,SAAS,CAAC;;AAG/D,SAAS,iCACP,SACA,cACc;AACd,KAAK,QAA+B,SAAS,aAC3C,QAAO;CAET,MAAM,aAAa;CACnB,MAAM,cAAe,WAAsC;CAC3D,MAAM,qBAAqB,wBAAwB,YAAY;AAC/D,KAAI,oBAAoB;AACtB,MAAI,gBAAgB,mBAClB,QAAO;AAET,SAAO;GAAE,GAAG;GAAY,UAAU;GAAoB;;CAGxD,MAAM,qBAAqB,wBAAwB,aAAa;AAChE,KAAI,mBACF,QAAO;EAAE,GAAG;EAAY,UAAU;EAAoB;AAGxD,KAAI,OAAO,gBAAgB,SACzB,QAAO;EAAE,GAAG;EAAY,UAAU;EAAW;AAE/C,QAAO;;AAKT,MAAM,qBAAqB,OAAO,gCAAgC;AAMlE,SAAS,uBACP,gBACiC;CACjC,MAAM,SAAS;CACf,MAAM,SAAS,OAAO;AACtB,KAAI,OACF,QAAO;CAET,MAAM,WAAW,eAAe,cAAc,KAAK,eAAe;AAClE,QAAO,sBAAsB;AAC7B,QAAO;;AAOT,IAAM,yBAAN,MAA6B;CAC3B,0BAA2B,IAAI,KAAiC;CAEhE,OAAe;AACb,SAAO,KAAK,QAAQ;;CAGtB,YAAY,IAAgC;AAC1C,SAAO,KAAK,QAAQ,IAAI,GAAG;;CAG7B,OAAO,IAAkB;AACvB,OAAK,QAAQ,OAAO,GAAG;;CAGzB,QAAc;AACZ,OAAK,QAAQ,OAAO;;CAGtB,eAAe,OAAyC;AACtD,OAAK,MAAM,QAAQ,MACjB,MAAK,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAK;;CAIxC,UAA0D;AACxD,SAAO,KAAK,QAAQ,SAAS;;CAG/B,gBAA0B;AACxB,SAAO,MAAM,KAAK,KAAK,QAAQ,MAAM,CAAC;;CAGxC,8BAAuC;AACrC,SAAO,KAAK,QAAQ,OAAO;;CAG7B,+BAA+B,UAAmB,eAAgC;AAChF,SAAO,KAAK,QAAQ,OAAO,MAAM,kBAAkB,KAAK,aAAa;;CAGvE,8BAA8B,eAAgC;AAC5D,SAAO,KAAK,QAAQ,OAAO,KAAK,gBAAgB;;;AAMpD,IAAM,kBAAN,MAAsB;CACpB;CACA;CACA,UAA2B,IAAI,wBAAwB;CACvD;CACA;CACA;CAEA,YAAY,gBAAgC,MAA8B;AACxE,OAAK,iBAAiB;AACtB,OAAK,OAAO;AACZ,OAAK,iBAAiB,uBAAuB,eAAe;AAC5D,OAAK,4BAA4B,KAAK,6BAA6B;AACnE,OAAK,qBAAqB,0BAA0B,KAAK;;;CAI3D,SAAe;EACb,MAAM,QAAQ,KAAK,cAAc,KAAK,KAAK;AAC3C,OAAK,eAAe,gBAAgB;;CAGtC,eAAqB;AACnB,MAAI,KAAK,QAAQ,MAAM,KAAK,EAC1B;AAEF,MAAI,KAAK,0BACP,MAAK,MAAM,CAAC,IAAI,SAAS,KAAK,QAAQ,SAAS,EAAE;GAC/C,MAAM,YAAY,sBAAsB;IACtC,YAAY;IACZ,UAAU;IACV,MAAM,KAAK,KAAK;IACjB,CAAC;GACF,MAAM,UAAU,KAAK,qBACnB,KAAK,kBAAkB,KAAK,eAAe,UAAU,EAAE;IACrD,YAAY;IACZ,UAAU;IACV,aAAa;IACd,CAAC,CACH;AACD,OAAI,QACF,MAAK,eAAe,4BAA4B,SAAS,KAAK,mBAAmB,CAAU;;AAIjG,OAAK,QAAQ,OAAO;;CAGtB,eAAqB;AACnB,OAAK,QAAQ,OAAO;;CAGtB,gBAA0B;AACxB,SAAO,KAAK,QAAQ,eAAe;;CAGrC,eAAuB,SAAqC;EAC1D,MAAM,cAAc,KAAK,KAAK;AAC9B,SAAO,cAAc,YAAY,QAAQ,GAAG;;CAG9C,kBACE,SACA,MACc;EACd,MAAM,cAAc,KAAK,KAAK;AAC9B,SAAO,cAAc,YAAY,SAAS,KAAK,GAAG;;;;;;CAOpD,qBAA6B,KAAwC;EACnE,MAAM,cAAc,KAAK,KAAK;AAC9B,MAAI,CAAC,YACH,QAAO;EAET,MAAM,SAAS,YAAY,EAAE,SAAS,KAAK,CAAC;AAC5C,MAAI,QAAQ,MACV,QAAO;AAET,MAAI,QAAQ,QACV,QAAO,OAAO;AAEhB,SAAO;;CAGT,cAAsB,SAAgC;EACpD,IAAI,cAAc;AAElB,MADc,QAA+B,SAChC,aAAa;GACxB,MAAM,YAAY,uBAAuB,CAAC,QAAQ,EAAE,EAClD,kBAAkB,KAAK,KAAK,kBAC7B,CAAC;AACF,OAAI,UAAU,WAAW,GAAG;AAC1B,QAAI,KAAK,QAAQ,6BAA6B,CAC5C,MAAK,cAAc;AAErB;;AAEF,iBAAc,UAAU;;EAE1B,MAAM,WAAY,YAAmC;AAErD,MAAI,aAAa,cAAc;GAC7B,MAAM,KAAK,oBAAoB,YAA6D;GAC5F,MAAM,WAAW,KAAK,KAAK,QAAQ,YAAY,GAAG,GAAG,KAAA;AACrD,OAAI,GACF,MAAK,QAAQ,OAAO,GAAG;GAEzB,MAAM,uBAAuB,iCAAiC,aAAa,SAAS;GAGpF,MAAM,SAAS,4BACb,KAAK,eAAe,qBAAqB,EACzC,KAAK,mBACN;GACD,MAAM,YAAY,KAAK,qBACrB,KAAK,kBAAkB,QAAQ;IAC7B,YAAY,MAAM,KAAA;IAClB;IACA,aAAa;IACd,CAAC,CACH;AACD,OAAI,CAAC,UACH;AAEF,UAAO,KAAK,eAAe,4BAA4B,WAAW,KAAK,mBAAmB,CAAU;;EAStG,MAAM,aAAc,YAAwC;EAC5D,MAAM,YACJ,aAAa,eAAe,eAAe,aAAa,eAAe,UACnE,8BAA8B,YAA4D,GAC1F,EAAE;AAQR,MAAI,KAAK,QAAQ,+BAA+B,UAAU,UAAU,OAAO,CACzE,MAAK,cAAc;AAGrB,MAAI,KAAK,QAAQ,8BAA8B,UAAU,OAAO,CAC9D,MAAK,cAAc;EAGrB,MAAM,eAAe,KAAK,qBAAqB,KAAK,eAAe,YAAY,CAAC;AAChF,MAAI,CAAC,aACH;EAEF,MAAM,SAAS,KAAK,eAAe,aAAsB;AAEzD,MAAI,KAAK,KAAK,WACZ,6BAA4B;GAC1B,YAAY,KAAK,KAAK;GACtB,SAAS;GACT,WAAW,OAAO,WAAW,WAAW,SAAS,KAAA;GAClD,CAAC;AAGJ,MAAI,UAAU,SAAS,EACrB,MAAK,QAAQ,eAAe,UAAU;AAGxC,SAAO"}
@@ -0,0 +1,10 @@
1
+ import { SessionManager } from '@earendil-works/pi-coding-agent';
2
+ /**
3
+ * Open an in-memory pi SessionManager hydrated from the SQLite transcript.
4
+ * File persistence is disabled; writes flow through the tool-result guard into SQLite.
5
+ */
6
+ export declare function openSqliteHydratingSessionManager(params: {
7
+ sessionKey: string;
8
+ sessionId: string;
9
+ cwd: string;
10
+ }): SessionManager;
@@ -0,0 +1,34 @@
1
+ import { requireXopcDatabase } from "../../storage/sqlite/connection.js";
2
+ import { ensureSessionRecord } from "../../storage/sqlite/session-repository.js";
3
+ import { loadTranscriptRowsForSession } from "../../storage/sqlite/transcript-repository.js";
4
+ import "../../storage/sqlite/index.js";
5
+ import { storedRowsToFileEntries } from "../../session/stored-rows-to-file-entries.js";
6
+ import { repairAssistantUsageInSessionManager } from "./session-manager-init.js";
7
+ import { SessionManager } from "@earendil-works/pi-coding-agent";
8
+ //#region src/agent/embedded/sqlite-hydrating-session-manager.ts
9
+ /**
10
+ * Open an in-memory pi SessionManager hydrated from the SQLite transcript.
11
+ * File persistence is disabled; writes flow through the tool-result guard into SQLite.
12
+ */
13
+ function openSqliteHydratingSessionManager(params) {
14
+ requireXopcDatabase();
15
+ ensureSessionRecord(params.sessionKey, params.cwd);
16
+ const rows = loadTranscriptRowsForSession(params.sessionKey);
17
+ const sm = SessionManager.inMemory(params.cwd);
18
+ const entries = storedRowsToFileEntries({
19
+ sessionId: params.sessionId,
20
+ cwd: params.cwd,
21
+ rows
22
+ });
23
+ const internal = sm;
24
+ internal.fileEntries = entries;
25
+ internal.sessionId = params.sessionId;
26
+ internal.flushed = true;
27
+ internal._buildIndex();
28
+ repairAssistantUsageInSessionManager(sm);
29
+ return sm;
30
+ }
31
+ //#endregion
32
+ export { openSqliteHydratingSessionManager };
33
+
34
+ //# sourceMappingURL=sqlite-hydrating-session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-hydrating-session-manager.js","names":[],"sources":["../../../../src/agent/embedded/sqlite-hydrating-session-manager.ts"],"sourcesContent":["import { SessionManager, type FileEntry } from '@earendil-works/pi-coding-agent';\n\nimport {\n ensureSessionRecord,\n loadTranscriptRowsForSession,\n requireXopcDatabase,\n} from '../../storage/sqlite/index.js';\nimport { storedRowsToFileEntries } from '../../session/stored-rows-to-file-entries.js';\nimport { repairAssistantUsageInSessionManager } from './session-manager-init.js';\n\ntype SessionManagerHydrationTarget = {\n fileEntries: FileEntry[];\n sessionId: string;\n flushed: boolean;\n byId: Map<string, unknown>;\n labelsById: Map<string, unknown>;\n leafId: string | null;\n _buildIndex: () => void;\n};\n\n/**\n * Open an in-memory pi SessionManager hydrated from the SQLite transcript.\n * File persistence is disabled; writes flow through the tool-result guard into SQLite.\n */\nexport function openSqliteHydratingSessionManager(params: {\n sessionKey: string;\n sessionId: string;\n cwd: string;\n}): SessionManager {\n requireXopcDatabase();\n\n ensureSessionRecord(params.sessionKey, params.cwd);\n const rows = loadTranscriptRowsForSession(params.sessionKey);\n const sm = SessionManager.inMemory(params.cwd);\n const entries = storedRowsToFileEntries({\n sessionId: params.sessionId,\n cwd: params.cwd,\n rows,\n });\n\n const internal = sm as unknown as SessionManagerHydrationTarget;\n internal.fileEntries = entries;\n internal.sessionId = params.sessionId;\n internal.flushed = true;\n internal._buildIndex();\n repairAssistantUsageInSessionManager(sm);\n return sm;\n}\n"],"mappings":";;;;;;;;;;;;AAwBA,SAAgB,kCAAkC,QAI/B;AACjB,sBAAqB;AAErB,qBAAoB,OAAO,YAAY,OAAO,IAAI;CAClD,MAAM,OAAO,6BAA6B,OAAO,WAAW;CAC5D,MAAM,KAAK,eAAe,SAAS,OAAO,IAAI;CAC9C,MAAM,UAAU,wBAAwB;EACtC,WAAW,OAAO;EAClB,KAAK,OAAO;EACZ;EACD,CAAC;CAEF,MAAM,WAAW;AACjB,UAAS,cAAc;AACvB,UAAS,YAAY,OAAO;AAC5B,UAAS,UAAU;AACnB,UAAS,aAAa;AACtB,sCAAqC,GAAG;AACxC,QAAO"}
@@ -1,13 +1,13 @@
1
- import { init_agent_scope, resolveAgentHomeDir } from "../agent-scope.js";
2
- import { extractProfileAgentId } from "../../config/agent-profile.js";
1
+ import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
3
2
  import { createLogger } from "../../utils/logger/index.js";
4
3
  import { init_logger } from "../../utils/logger.js";
5
- import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
4
+ import { init_agent_scope, resolveAgentHomeDir } from "../agent-scope.js";
5
+ import { extractProfileAgentId } from "../../config/agent-profile.js";
6
6
  import { checklistCounts } from "./checklist-types.js";
7
7
  import "./goal-run-types.js";
8
- import { join } from "node:path";
9
8
  import { createHash, randomUUID } from "node:crypto";
10
9
  import { readFile } from "node:fs/promises";
10
+ import { join } from "node:path";
11
11
  //#region src/agent/goals/goal-run-store.ts
12
12
  init_agent_scope();
13
13
  init_write_file_atomic();
@@ -1,7 +1,6 @@
1
- import { init_session_key, parseSessionKey } from "../../routing/session-key.js";
2
1
  import { createLogger } from "../../utils/logger/index.js";
3
2
  import { init_logger } from "../../utils/logger.js";
4
- import { appendPiTranscriptMessage } from "../../session/parity/jsonl-transcript-io.js";
3
+ import { init_session_key, parseSessionKey } from "../../routing/session-key.js";
5
4
  import { handlePersistentGoalPostTurn } from "./post-turn.js";
6
5
  //#region src/agent/goals/persistent-goal-service.ts
7
6
  init_session_key();
@@ -58,19 +57,13 @@ var PersistentGoalService = class {
58
57
  appendAssistantReceipt: async (k, text) => {
59
58
  const trimmed = text.trim();
60
59
  if (!trimmed) return;
61
- const { absPath } = await this.opts.sessionStore.resolveTranscriptPath(k);
62
- await appendPiTranscriptMessage({
63
- absPath,
64
- cwd: this.opts.getResolvedWorkspaceForSession(k),
65
- message: {
66
- role: "assistant",
67
- content: [{
68
- type: "text",
69
- text: trimmed
70
- }],
71
- timestamp: Date.now()
72
- },
73
- sessionKey: k
60
+ await this.opts.sessionStore.appendTranscriptMessage(k, {
61
+ role: "assistant",
62
+ content: [{
63
+ type: "text",
64
+ text: trimmed
65
+ }],
66
+ timestamp: Date.now()
74
67
  });
75
68
  this.opts.notifyWebchatTranscriptAppend(k, trimmed);
76
69
  },
@@ -1 +1 @@
1
- {"version":3,"file":"persistent-goal-service.js","names":["parseRoutingSessionKey"],"sources":["../../../../src/agent/goals/persistent-goal-service.ts"],"sourcesContent":["/**\n * PersistentGoalService — owns the \"/goal\" runtime: continuation scheduling,\n * the `PersistentGoalApis` bag that command handlers receive, and the post-turn\n * verdict hook called from `OutboundCoordinator`.\n *\n * Previously this logic was scattered across `AgentService`:\n * - `setPersistentGoalWebchatContinuationScheduler` + a private callback field\n * - `schedulePersistentGoalContinuation` (bus vs webchat fork)\n * - `getPersistentGoalApisForCommand` (~40-line API factory)\n * - `recordPersistentGoalStreamOutcome` / `takePersistentGoalStreamOutcome`\n * - the `/goal` half of `emitSessionTurnComplete` (delegated to\n * `handlePersistentGoalPostTurn`)\n *\n * Concentrating it here gives the rest of `AgentService` a cleaner surface\n * (one collaborator instead of five methods) and makes the goal runtime\n * unit-testable in isolation.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { SessionStateBag } from '../session/index.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.js';\nimport { appendPiTranscriptMessage } from '../../session/parity/jsonl-transcript-io.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { PersistentGoalApis } from './persistent-goal-apis.js';\nimport { handlePersistentGoalPostTurn } from './post-turn.js';\n\nconst log = createLogger('PersistentGoalService');\n\nexport interface PersistentGoalRouting {\n sessionKey: string;\n channel: string;\n chatId: string;\n inboundMetadata?: Record<string, unknown>;\n}\n\nexport interface SessionTurnCompletionForGoal {\n sessionKey: string;\n channel: string;\n chatId: string;\n assistantPlainText: string;\n aborted: boolean;\n streamError?: string;\n skipPersistentGoalPostTurn?: boolean;\n outboundMetadata?: Record<string, unknown>;\n}\n\nexport interface PersistentGoalServiceOptions {\n bus: MessageBus;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n sessionState: SessionStateBag;\n /** Effective config snapshot accessor. */\n getConfig: () => Config | undefined;\n /** Resolve the workspace directory for `appendAssistantReceipt` writes. */\n getResolvedWorkspaceForSession: (sessionKey: string) => string;\n /** Notify the gateway UI after a metadata change (replaces the in-bag emit). */\n onSessionMetadataUpdated?: (sessionKey: string) => void;\n /** Push an assistant token + transcript refresh into a live webchat stream. */\n notifyWebchatTranscriptAppend: (sessionKey: string, assistantText: string) => void;\n}\n\nexport class PersistentGoalService {\n private readonly opts: PersistentGoalServiceOptions;\n /** Gateway only: webchat continuations bypass the bus and reuse `runGatewayAgent`. */\n private webchatContinuationScheduler?: (sessionKey: string, message: string) => void;\n\n constructor(opts: PersistentGoalServiceOptions) {\n this.opts = opts;\n }\n\n /** Register the gateway-side webchat continuation hook (set from `web-agent` wiring). */\n setWebchatContinuationScheduler(\n fn: ((sessionKey: string, message: string) => void) | undefined,\n ): void {\n this.webchatContinuationScheduler = fn;\n }\n\n /**\n * Continue a session after `/goal` decides the previous turn needs follow-up.\n * Webchat sessions go through the scheduler; bus-driven channels re-publish the\n * follow-up message as an inbound bus event so the existing inbound loop picks it up.\n */\n scheduleContinuation(\n sessionKey: string,\n message: string,\n routing: { channel: string; chatId: string; inboundMetadata?: Record<string, unknown> },\n ): void {\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed?.source === 'webchat' && this.webchatContinuationScheduler) {\n this.webchatContinuationScheduler(sessionKey, message);\n return;\n }\n queueMicrotask(() => {\n void this.opts.bus\n .publishInbound({\n channel: routing.channel,\n chat_id: routing.chatId,\n sender_id: 'persistent-goal',\n content: message,\n metadata: { sessionKey, ...routing.inboundMetadata },\n })\n .catch((err) => {\n log.warn({ err, sessionKey }, 'Persistent goal: publishInbound failed');\n });\n });\n }\n\n /** Build the per-command `PersistentGoalApis` bag (transcript writers + scheduler closures). */\n buildApisForRouting(routing: PersistentGoalRouting): PersistentGoalApis {\n return {\n getSessionMetadata: (k) => this.opts.sessionStore.getMetadata(k),\n updateSessionMetadata: async (k, u) => {\n await this.opts.sessionStore.updateMetadata(k, u);\n this.opts.onSessionMetadataUpdated?.(k);\n },\n loadMessages: (k) => this.opts.sessionStore.loadMessages(k),\n appendAssistantReceipt: async (k, text) => {\n const trimmed = text.trim();\n if (!trimmed) return;\n const { absPath } = await this.opts.sessionStore.resolveTranscriptPath(k);\n const workspaceDir = this.opts.getResolvedWorkspaceForSession(k);\n await appendPiTranscriptMessage({\n absPath,\n cwd: workspaceDir,\n message: {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n } as AgentMessage,\n sessionKey: k,\n });\n this.opts.notifyWebchatTranscriptAppend(k, trimmed);\n },\n scheduleContinuation: (sk, msg) => {\n this.scheduleContinuation(sk, msg, {\n channel: routing.channel,\n chatId: routing.chatId,\n inboundMetadata: routing.inboundMetadata,\n });\n },\n inboundConcurrentDepth: (sk) => this.opts.sessionState.getInboundTurnDepth(sk),\n };\n }\n\n recordStreamOutcome(sessionKey: string, outcome: { skipPersistentGoalPostTurn: boolean }): void {\n this.opts.sessionState.recordPersistentGoalStreamOutcome(sessionKey, outcome);\n }\n\n takeStreamOutcome(sessionKey: string): { skipPersistentGoalPostTurn: boolean } | undefined {\n return this.opts.sessionState.takePersistentGoalStreamOutcome(sessionKey);\n }\n\n /**\n * Run the `/goal` post-turn verdict for a completed user turn (called from\n * `OutboundCoordinator.emitSessionTurnComplete`).\n */\n async runPostTurn(payload: SessionTurnCompletionForGoal): Promise<void> {\n const apis = this.buildApisForRouting({\n sessionKey: payload.sessionKey,\n channel: payload.channel,\n chatId: payload.chatId,\n inboundMetadata: payload.outboundMetadata,\n });\n\n const src = parseRoutingSessionKey(payload.sessionKey)?.source;\n const isWebchat = src === 'webchat';\n const publishVerdict =\n !isWebchat && payload.channel !== 'cli'\n ? async (text: string) => {\n await this.opts.bus.publishOutbound({\n channel: payload.channel,\n chat_id: payload.chatId,\n content: text,\n type: 'message',\n metadata: {\n accountId: payload.outboundMetadata?.accountId,\n threadId: payload.outboundMetadata?.threadId,\n },\n });\n }\n : undefined;\n\n let runtimeSessionModelRef: string | undefined;\n try {\n runtimeSessionModelRef = this.opts.modelManager.getModelForSession(payload.sessionKey);\n } catch {\n runtimeSessionModelRef = undefined;\n }\n\n await handlePersistentGoalPostTurn({\n apis,\n sessionKey: payload.sessionKey,\n assistantPlainText: payload.assistantPlainText,\n aborted: payload.aborted,\n ...(payload.streamError !== undefined ? { streamError: payload.streamError } : {}),\n skipPersistentGoalPostTurn: payload.skipPersistentGoalPostTurn ?? false,\n config: this.opts.getConfig(),\n runtimeSessionModelRef,\n publishVerdictToChannel: publishVerdict,\n });\n }\n}\n"],"mappings":";;;;;;kBAyByF;aAEpC;AAIrD,MAAM,MAAM,aAAa,wBAAwB;AAmCjD,IAAa,wBAAb,MAAmC;CACjC;;CAEA;CAEA,YAAY,MAAoC;AAC9C,OAAK,OAAO;;;CAId,gCACE,IACM;AACN,OAAK,+BAA+B;;;;;;;CAQtC,qBACE,YACA,SACA,SACM;AAEN,MADeA,gBAAuB,WAC5B,EAAE,WAAW,aAAa,KAAK,8BAA8B;AACrE,QAAK,6BAA6B,YAAY,QAAQ;AACtD;;AAEF,uBAAqB;AACd,QAAK,KAAK,IACZ,eAAe;IACd,SAAS,QAAQ;IACjB,SAAS,QAAQ;IACjB,WAAW;IACX,SAAS;IACT,UAAU;KAAE;KAAY,GAAG,QAAQ;KAAiB;IACrD,CAAC,CACD,OAAO,QAAQ;AACd,QAAI,KAAK;KAAE;KAAK;KAAY,EAAE,yCAAyC;KACvE;IACJ;;;CAIJ,oBAAoB,SAAoD;AACtE,SAAO;GACL,qBAAqB,MAAM,KAAK,KAAK,aAAa,YAAY,EAAE;GAChE,uBAAuB,OAAO,GAAG,MAAM;AACrC,UAAM,KAAK,KAAK,aAAa,eAAe,GAAG,EAAE;AACjD,SAAK,KAAK,2BAA2B,EAAE;;GAEzC,eAAe,MAAM,KAAK,KAAK,aAAa,aAAa,EAAE;GAC3D,wBAAwB,OAAO,GAAG,SAAS;IACzC,MAAM,UAAU,KAAK,MAAM;AAC3B,QAAI,CAAC,QAAS;IACd,MAAM,EAAE,YAAY,MAAM,KAAK,KAAK,aAAa,sBAAsB,EAAE;AAEzE,UAAM,0BAA0B;KAC9B;KACA,KAHmB,KAAK,KAAK,+BAA+B,EAG3C;KACjB,SAAS;MACP,MAAM;MACN,SAAS,CAAC;OAAE,MAAM;OAAQ,MAAM;OAAS,CAAC;MAC1C,WAAW,KAAK,KAAK;MACtB;KACD,YAAY;KACb,CAAC;AACF,SAAK,KAAK,8BAA8B,GAAG,QAAQ;;GAErD,uBAAuB,IAAI,QAAQ;AACjC,SAAK,qBAAqB,IAAI,KAAK;KACjC,SAAS,QAAQ;KACjB,QAAQ,QAAQ;KAChB,iBAAiB,QAAQ;KAC1B,CAAC;;GAEJ,yBAAyB,OAAO,KAAK,KAAK,aAAa,oBAAoB,GAAG;GAC/E;;CAGH,oBAAoB,YAAoB,SAAwD;AAC9F,OAAK,KAAK,aAAa,kCAAkC,YAAY,QAAQ;;CAG/E,kBAAkB,YAAyE;AACzF,SAAO,KAAK,KAAK,aAAa,gCAAgC,WAAW;;;;;;CAO3E,MAAM,YAAY,SAAsD;EACtE,MAAM,OAAO,KAAK,oBAAoB;GACpC,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,iBAAiB,QAAQ;GAC1B,CAAC;EAIF,MAAM,iBACJ,EAHUA,gBAAuB,QAAQ,WAAW,EAAE,WAC9B,cAEV,QAAQ,YAAY,QAC9B,OAAO,SAAiB;AACtB,SAAM,KAAK,KAAK,IAAI,gBAAgB;IAClC,SAAS,QAAQ;IACjB,SAAS,QAAQ;IACjB,SAAS;IACT,MAAM;IACN,UAAU;KACR,WAAW,QAAQ,kBAAkB;KACrC,UAAU,QAAQ,kBAAkB;KACrC;IACF,CAAC;MAEJ,KAAA;EAEN,IAAI;AACJ,MAAI;AACF,4BAAyB,KAAK,KAAK,aAAa,mBAAmB,QAAQ,WAAW;UAChF;AACN,4BAAyB,KAAA;;AAG3B,QAAM,6BAA6B;GACjC;GACA,YAAY,QAAQ;GACpB,oBAAoB,QAAQ;GAC5B,SAAS,QAAQ;GACjB,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GACjF,4BAA4B,QAAQ,8BAA8B;GAClE,QAAQ,KAAK,KAAK,WAAW;GAC7B;GACA,yBAAyB;GAC1B,CAAC"}
1
+ {"version":3,"file":"persistent-goal-service.js","names":["parseRoutingSessionKey"],"sources":["../../../../src/agent/goals/persistent-goal-service.ts"],"sourcesContent":["/**\n * PersistentGoalService — owns the \"/goal\" runtime: continuation scheduling,\n * the `PersistentGoalApis` bag that command handlers receive, and the post-turn\n * verdict hook called from `OutboundCoordinator`.\n *\n * Previously this logic was scattered across `AgentService`:\n * - `setPersistentGoalWebchatContinuationScheduler` + a private callback field\n * - `schedulePersistentGoalContinuation` (bus vs webchat fork)\n * - `getPersistentGoalApisForCommand` (~40-line API factory)\n * - `recordPersistentGoalStreamOutcome` / `takePersistentGoalStreamOutcome`\n * - the `/goal` half of `emitSessionTurnComplete` (delegated to\n * `handlePersistentGoalPostTurn`)\n *\n * Concentrating it here gives the rest of `AgentService` a cleaner surface\n * (one collaborator instead of five methods) and makes the goal runtime\n * unit-testable in isolation.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { SessionStateBag } from '../session/index.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { PersistentGoalApis } from './persistent-goal-apis.js';\nimport { handlePersistentGoalPostTurn } from './post-turn.js';\n\nconst log = createLogger('PersistentGoalService');\n\nexport interface PersistentGoalRouting {\n sessionKey: string;\n channel: string;\n chatId: string;\n inboundMetadata?: Record<string, unknown>;\n}\n\nexport interface SessionTurnCompletionForGoal {\n sessionKey: string;\n channel: string;\n chatId: string;\n assistantPlainText: string;\n aborted: boolean;\n streamError?: string;\n skipPersistentGoalPostTurn?: boolean;\n outboundMetadata?: Record<string, unknown>;\n}\n\nexport interface PersistentGoalServiceOptions {\n bus: MessageBus;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n sessionState: SessionStateBag;\n /** Effective config snapshot accessor. */\n getConfig: () => Config | undefined;\n /** Resolve the workspace directory for `appendAssistantReceipt` writes. */\n getResolvedWorkspaceForSession: (sessionKey: string) => string;\n /** Notify the gateway UI after a metadata change (replaces the in-bag emit). */\n onSessionMetadataUpdated?: (sessionKey: string) => void;\n /** Push an assistant token + transcript refresh into a live webchat stream. */\n notifyWebchatTranscriptAppend: (sessionKey: string, assistantText: string) => void;\n}\n\nexport class PersistentGoalService {\n private readonly opts: PersistentGoalServiceOptions;\n /** Gateway only: webchat continuations bypass the bus and reuse `runGatewayAgent`. */\n private webchatContinuationScheduler?: (sessionKey: string, message: string) => void;\n\n constructor(opts: PersistentGoalServiceOptions) {\n this.opts = opts;\n }\n\n /** Register the gateway-side webchat continuation hook (set from `web-agent` wiring). */\n setWebchatContinuationScheduler(\n fn: ((sessionKey: string, message: string) => void) | undefined,\n ): void {\n this.webchatContinuationScheduler = fn;\n }\n\n /**\n * Continue a session after `/goal` decides the previous turn needs follow-up.\n * Webchat sessions go through the scheduler; bus-driven channels re-publish the\n * follow-up message as an inbound bus event so the existing inbound loop picks it up.\n */\n scheduleContinuation(\n sessionKey: string,\n message: string,\n routing: { channel: string; chatId: string; inboundMetadata?: Record<string, unknown> },\n ): void {\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed?.source === 'webchat' && this.webchatContinuationScheduler) {\n this.webchatContinuationScheduler(sessionKey, message);\n return;\n }\n queueMicrotask(() => {\n void this.opts.bus\n .publishInbound({\n channel: routing.channel,\n chat_id: routing.chatId,\n sender_id: 'persistent-goal',\n content: message,\n metadata: { sessionKey, ...routing.inboundMetadata },\n })\n .catch((err) => {\n log.warn({ err, sessionKey }, 'Persistent goal: publishInbound failed');\n });\n });\n }\n\n /** Build the per-command `PersistentGoalApis` bag (transcript writers + scheduler closures). */\n buildApisForRouting(routing: PersistentGoalRouting): PersistentGoalApis {\n return {\n getSessionMetadata: (k) => this.opts.sessionStore.getMetadata(k),\n updateSessionMetadata: async (k, u) => {\n await this.opts.sessionStore.updateMetadata(k, u);\n this.opts.onSessionMetadataUpdated?.(k);\n },\n loadMessages: (k) => this.opts.sessionStore.loadMessages(k),\n appendAssistantReceipt: async (k, text) => {\n const trimmed = text.trim();\n if (!trimmed) return;\n await this.opts.sessionStore.appendTranscriptMessage(k, {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n } as AgentMessage);\n this.opts.notifyWebchatTranscriptAppend(k, trimmed);\n },\n scheduleContinuation: (sk, msg) => {\n this.scheduleContinuation(sk, msg, {\n channel: routing.channel,\n chatId: routing.chatId,\n inboundMetadata: routing.inboundMetadata,\n });\n },\n inboundConcurrentDepth: (sk) => this.opts.sessionState.getInboundTurnDepth(sk),\n };\n }\n\n recordStreamOutcome(sessionKey: string, outcome: { skipPersistentGoalPostTurn: boolean }): void {\n this.opts.sessionState.recordPersistentGoalStreamOutcome(sessionKey, outcome);\n }\n\n takeStreamOutcome(sessionKey: string): { skipPersistentGoalPostTurn: boolean } | undefined {\n return this.opts.sessionState.takePersistentGoalStreamOutcome(sessionKey);\n }\n\n /**\n * Run the `/goal` post-turn verdict for a completed user turn (called from\n * `OutboundCoordinator.emitSessionTurnComplete`).\n */\n async runPostTurn(payload: SessionTurnCompletionForGoal): Promise<void> {\n const apis = this.buildApisForRouting({\n sessionKey: payload.sessionKey,\n channel: payload.channel,\n chatId: payload.chatId,\n inboundMetadata: payload.outboundMetadata,\n });\n\n const src = parseRoutingSessionKey(payload.sessionKey)?.source;\n const isWebchat = src === 'webchat';\n const publishVerdict =\n !isWebchat && payload.channel !== 'cli'\n ? async (text: string) => {\n await this.opts.bus.publishOutbound({\n channel: payload.channel,\n chat_id: payload.chatId,\n content: text,\n type: 'message',\n metadata: {\n accountId: payload.outboundMetadata?.accountId,\n threadId: payload.outboundMetadata?.threadId,\n },\n });\n }\n : undefined;\n\n let runtimeSessionModelRef: string | undefined;\n try {\n runtimeSessionModelRef = this.opts.modelManager.getModelForSession(payload.sessionKey);\n } catch {\n runtimeSessionModelRef = undefined;\n }\n\n await handlePersistentGoalPostTurn({\n apis,\n sessionKey: payload.sessionKey,\n assistantPlainText: payload.assistantPlainText,\n aborted: payload.aborted,\n ...(payload.streamError !== undefined ? { streamError: payload.streamError } : {}),\n skipPersistentGoalPostTurn: payload.skipPersistentGoalPostTurn ?? false,\n config: this.opts.getConfig(),\n runtimeSessionModelRef,\n publishVerdictToChannel: publishVerdict,\n });\n }\n}\n"],"mappings":";;;;;kBAyByF;aACpC;AAIrD,MAAM,MAAM,aAAa,wBAAwB;AAmCjD,IAAa,wBAAb,MAAmC;CACjC;;CAEA;CAEA,YAAY,MAAoC;AAC9C,OAAK,OAAO;;;CAId,gCACE,IACM;AACN,OAAK,+BAA+B;;;;;;;CAQtC,qBACE,YACA,SACA,SACM;AAEN,MADeA,gBAAuB,WAC5B,EAAE,WAAW,aAAa,KAAK,8BAA8B;AACrE,QAAK,6BAA6B,YAAY,QAAQ;AACtD;;AAEF,uBAAqB;AACd,QAAK,KAAK,IACZ,eAAe;IACd,SAAS,QAAQ;IACjB,SAAS,QAAQ;IACjB,WAAW;IACX,SAAS;IACT,UAAU;KAAE;KAAY,GAAG,QAAQ;KAAiB;IACrD,CAAC,CACD,OAAO,QAAQ;AACd,QAAI,KAAK;KAAE;KAAK;KAAY,EAAE,yCAAyC;KACvE;IACJ;;;CAIJ,oBAAoB,SAAoD;AACtE,SAAO;GACL,qBAAqB,MAAM,KAAK,KAAK,aAAa,YAAY,EAAE;GAChE,uBAAuB,OAAO,GAAG,MAAM;AACrC,UAAM,KAAK,KAAK,aAAa,eAAe,GAAG,EAAE;AACjD,SAAK,KAAK,2BAA2B,EAAE;;GAEzC,eAAe,MAAM,KAAK,KAAK,aAAa,aAAa,EAAE;GAC3D,wBAAwB,OAAO,GAAG,SAAS;IACzC,MAAM,UAAU,KAAK,MAAM;AAC3B,QAAI,CAAC,QAAS;AACd,UAAM,KAAK,KAAK,aAAa,wBAAwB,GAAG;KACtD,MAAM;KACN,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM;MAAS,CAAC;KAC1C,WAAW,KAAK,KAAK;KACtB,CAAiB;AAClB,SAAK,KAAK,8BAA8B,GAAG,QAAQ;;GAErD,uBAAuB,IAAI,QAAQ;AACjC,SAAK,qBAAqB,IAAI,KAAK;KACjC,SAAS,QAAQ;KACjB,QAAQ,QAAQ;KAChB,iBAAiB,QAAQ;KAC1B,CAAC;;GAEJ,yBAAyB,OAAO,KAAK,KAAK,aAAa,oBAAoB,GAAG;GAC/E;;CAGH,oBAAoB,YAAoB,SAAwD;AAC9F,OAAK,KAAK,aAAa,kCAAkC,YAAY,QAAQ;;CAG/E,kBAAkB,YAAyE;AACzF,SAAO,KAAK,KAAK,aAAa,gCAAgC,WAAW;;;;;;CAO3E,MAAM,YAAY,SAAsD;EACtE,MAAM,OAAO,KAAK,oBAAoB;GACpC,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,iBAAiB,QAAQ;GAC1B,CAAC;EAIF,MAAM,iBACJ,EAHUA,gBAAuB,QAAQ,WAAW,EAAE,WAC9B,cAEV,QAAQ,YAAY,QAC9B,OAAO,SAAiB;AACtB,SAAM,KAAK,KAAK,IAAI,gBAAgB;IAClC,SAAS,QAAQ;IACjB,SAAS,QAAQ;IACjB,SAAS;IACT,MAAM;IACN,UAAU;KACR,WAAW,QAAQ,kBAAkB;KACrC,UAAU,QAAQ,kBAAkB;KACrC;IACF,CAAC;MAEJ,KAAA;EAEN,IAAI;AACJ,MAAI;AACF,4BAAyB,KAAK,KAAK,aAAa,mBAAmB,QAAQ,WAAW;UAChF;AACN,4BAAyB,KAAA;;AAG3B,QAAM,6BAA6B;GACjC;GACA,YAAY,QAAQ;GACpB,oBAAoB,QAAQ;GAC5B,SAAS,QAAQ;GACjB,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GACjF,4BAA4B,QAAQ,8BAA8B;GAClE,QAAQ,KAAK,KAAK,WAAW;GAC7B;GACA,yBAAyB;GAC1B,CAAC"}
@@ -1,7 +1,7 @@
1
- import { getAgentDefaultModelRef, init_schema } from "../../config/schema.js";
2
- import { resolveEffectiveAgentProfileForSession } from "../../config/agent-profile.js";
3
1
  import { createLogger } from "../../utils/logger/index.js";
4
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { getAgentDefaultModelRef, init_schema } from "../../config/schema.js";
4
+ import { resolveEffectiveAgentProfileForSession } from "../../config/agent-profile.js";
5
5
  import { PERSISTENT_GOAL_CUSTOM_KEY, mergeCustomDataPatch, readPersistentGoal, serializePersistentGoal } from "./state.js";
6
6
  import { resolveGoalUiLocale } from "./goal-locale.js";
7
7
  import { evaluateAfterTurnHermesLike } from "./evaluate-turn.js";
@@ -1,8 +1,8 @@
1
1
  import { checkFileSafety } from "../prompt/safety.js";
2
2
  import { decodeDataUrl } from "./image-helpers.js";
3
- import { homedir } from "node:os";
4
- import path from "node:path";
5
3
  import { promises } from "node:fs";
4
+ import path from "node:path";
5
+ import { homedir } from "node:os";
6
6
  //#region src/agent/image/load-image-media.ts
7
7
  function expandUser(p) {
8
8
  if (p.startsWith("~/") || p === "~") return path.join(homedir(), p.slice(1));
@@ -1,6 +1,6 @@
1
- import { init_agent_scope, resolveDefaultAgentId } from "../agent-scope.js";
2
1
  import { createLogger } from "../../utils/logger/index.js";
3
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { init_agent_scope, resolveDefaultAgentId } from "../agent-scope.js";
4
4
  import { createResponseMessage, createSignalMessage, createTaskMessage } from "./types.js";
5
5
  import { AgentInbox } from "./inbox.js";
6
6
  //#region src/agent/ipc/bus.ts
@@ -1,10 +1,10 @@
1
+ import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
1
2
  import { createLogger } from "../../utils/logger/index.js";
2
3
  import { init_logger } from "../../utils/logger.js";
3
4
  import { init_paths, resolveAgentDir } from "../../config/paths.js";
4
- import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
5
+ import { mkdir, readFile, readdir, rename } from "fs/promises";
5
6
  import { join } from "path";
6
7
  import { existsSync, watch } from "fs";
7
- import { mkdir, readFile, readdir, rename } from "fs/promises";
8
8
  //#region src/agent/ipc/inbox.ts
9
9
  init_write_file_atomic();
10
10
  init_logger();
@@ -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();
@@ -1,6 +1,6 @@
1
- import { init_string_coerce, normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "../../utils/string-coerce.js";
2
1
  import { createLogger } from "../../utils/logger/index.js";
3
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { init_string_coerce, normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "../../utils/string-coerce.js";
4
4
  import { buildSafeToolName, normalizeReservedToolNames } from "./bundle-mcp-names.js";
5
5
  import { setPluginToolMeta } from "./mcp-tool-meta.js";
6
6
  import crypto from "node:crypto";
@@ -1,9 +1,9 @@
1
- import { init_string_coerce, normalizeOptionalString } from "../../utils/string-coerce.js";
2
1
  import { createLogger } from "../../utils/logger/index.js";
3
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { init_string_coerce, normalizeOptionalString } from "../../utils/string-coerce.js";
4
+ import { resolveGlobalSingleton } from "../../utils/global-singleton.js";
4
5
  import { sanitizeServerName } from "./bundle-mcp-names.js";
5
6
  import { resolveConnectorSecretReferences } from "../../connectors/secret-store.js";
6
- import { resolveGlobalSingleton } from "../../utils/global-singleton.js";
7
7
  import { redactSensitiveUrlLikeString } from "../../utils/redact-sensitive-url.js";
8
8
  import { loadEmbeddedMcpConfig } from "./embedded-mcp.js";
9
9
  import { isMcpConfigRecord } from "./mcp-config-shared.js";
@@ -1,6 +1,6 @@
1
- import { init_string_coerce, normalizeLowercaseStringOrEmpty } from "../../utils/string-coerce.js";
2
1
  import { createLogger } from "../../utils/logger/index.js";
3
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { init_string_coerce, normalizeLowercaseStringOrEmpty } from "../../utils/string-coerce.js";
4
4
  import { resolveXopcMcpTransportAlias } from "../../config/mcp-config-normalize.js";
5
5
  import { sanitizeForLog } from "../../utils/sanitize-log.js";
6
6
  import { describeHttpMcpServerLaunchConfig, resolveHttpMcpServerLaunchConfig } from "./mcp-http.js";
@@ -1,6 +1,6 @@
1
- import { init_string_coerce, normalizeOptionalString } from "../../utils/string-coerce.js";
2
1
  import { createLogger } from "../../utils/logger/index.js";
3
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { init_string_coerce, normalizeOptionalString } from "../../utils/string-coerce.js";
4
4
  import { XopcStdioClientTransport } from "./mcp-stdio-transport.js";
5
5
  import { resolveMcpTransportConfig } from "./mcp-transport-config.js";
6
6
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
@@ -1,6 +1,6 @@
1
- import { join } from "node:path";
2
1
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
3
2
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
4
  import lockfile from "proper-lockfile";
5
5
  //#region src/agent/memory/builtin-memory-store.ts
6
6
  /**
@@ -4,8 +4,8 @@ import { MEMORY_MD_FILENAME } from "./constants.js";
4
4
  import { clamp01, compareCandidatesByScore, computeCandidateScore, extractPromotionMarkers, isContaminatedSnippet, isExpiredEntry, isoDay, readFileLines, resolveDeepDefaults, sliceRange, snippetHash } from "./utils.js";
5
5
  import { loadDreamingStore, saveDreamingStore, withDreamingPromotionLock } from "./short-term-store.js";
6
6
  import { emptyDeepPhaseSkipped, writeDreamingDeepLastRun } from "./last-run.js";
7
- import path from "node:path";
8
7
  import fs from "node:fs/promises";
8
+ import path from "node:path";
9
9
  //#region src/agent/memory/dreaming/deep-promotion.ts
10
10
  init_logger();
11
11
  const log = createLogger("Dreaming:Deep");
@@ -1,6 +1,6 @@
1
1
  import { DREAMING_DIR_RELATIVE, DREAMING_EVENTS_LOG_RELATIVE } from "./constants.js";
2
- import path from "node:path";
3
2
  import fs from "node:fs/promises";
3
+ import path from "node:path";
4
4
  //#region src/agent/memory/dreaming/events.ts
5
5
  /**
6
6
  * Append a single event line to `memory/.dreams/events.jsonl`.
@@ -1,6 +1,6 @@
1
1
  import { DREAMING_LAST_RUN_RELATIVE } from "./constants.js";
2
- import path from "node:path";
3
2
  import fs from "node:fs/promises";
3
+ import path from "node:path";
4
4
  //#region src/agent/memory/dreaming/last-run.ts
5
5
  const DREAMING_LAST_RUN_FORMAT_VERSION = 2;
6
6
  function isRecord(v) {
@@ -4,8 +4,8 @@ import { DREAMING_DIR_RELATIVE } from "./constants.js";
4
4
  import { buildEntryKey, isoDay, normalizeMemoryPath, normalizeSnippetForHash, snippetHash } from "./utils.js";
5
5
  import { bumpEntryPhaseSignal, loadDreamingStore, saveDreamingStore } from "./short-term-store.js";
6
6
  import "./last-run.js";
7
- import path from "node:path";
8
7
  import fs from "node:fs/promises";
8
+ import path from "node:path";
9
9
  //#region src/agent/memory/dreaming/light-sweep.ts
10
10
  init_logger();
11
11
  const log = createLogger("Dreaming:Light");
@@ -1,8 +1,8 @@
1
1
  import { MEMORY_MD_FILENAME } from "./constants.js";
2
2
  import { clamp01, compareCandidatesByScore, computeCandidateScore, extractPromotionMarkers, isContaminatedSnippet, isExpiredEntry, readFileLines, resolveDeepDefaults, sliceRange, snippetHash } from "./utils.js";
3
3
  import { loadDreamingStore } from "./short-term-store.js";
4
- import path from "node:path";
5
4
  import fs from "node:fs/promises";
5
+ import path from "node:path";
6
6
  //#region src/agent/memory/dreaming/preview.ts
7
7
  async function previewDreamingDeepPromotion(params) {
8
8
  const cfg = resolveDeepDefaults(params.config);
@@ -4,8 +4,8 @@ import { DREAMING_DIR_RELATIVE, DREAMS_MD_FILENAME, MS_PER_DAY } from "./constan
4
4
  import { isoDay } from "./utils.js";
5
5
  import { bumpEntryPhaseSignal, loadDreamingStore, saveDreamingStore } from "./short-term-store.js";
6
6
  import "./last-run.js";
7
- import path from "node:path";
8
7
  import fs from "node:fs/promises";
8
+ import path from "node:path";
9
9
  //#region src/agent/memory/dreaming/rem-patterns.ts
10
10
  init_logger();
11
11
  const log = createLogger("Dreaming:REM");
@@ -2,9 +2,9 @@ import { createLogger } from "../../../utils/logger/index.js";
2
2
  import { init_logger } from "../../../utils/logger.js";
3
3
  import { SHORT_TERM_PROMOTION_LOCK_RELATIVE, SHORT_TERM_RECALL_STORE_RELATIVE } from "./constants.js";
4
4
  import { buildEntryKey, clamp01, isoDay, normalizeMemoryPath } from "./utils.js";
5
- import path from "node:path";
6
5
  import { createHash, randomUUID } from "node:crypto";
7
6
  import fs from "node:fs/promises";
7
+ import path from "node:path";
8
8
  //#region src/agent/memory/dreaming/short-term-store.ts
9
9
  init_logger();
10
10
  const log = createLogger("Dreaming:Store");
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_DEEP_CRON, DIVERSITY_WEIGHT, MS_PER_DAY, REINFORCEMENT_WEIGHT } from "./constants.js";
2
- import path from "node:path";
3
2
  import { createHash } from "node:crypto";
4
3
  import fs from "node:fs/promises";
4
+ import path from "node:path";
5
5
  //#region src/agent/memory/dreaming/utils.ts
6
6
  /** Normalize a workspace-relative memory path: forward slashes, no odd ../ escapes at start. */
7
7
  function normalizeMemoryPath(rel) {