hammoc 1.8.4 → 1.9.0

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 (304) hide show
  1. package/README.md +9 -6
  2. package/package.json +1 -1
  3. package/packages/client/dist/assets/MermaidViewer-B-LwclIl.js +7 -0
  4. package/packages/client/dist/assets/PdfViewer-C3Nymwn3.js +12 -0
  5. package/packages/client/dist/assets/PdfViewer-QCQEGvBO.css +1 -0
  6. package/packages/client/dist/assets/abnfDiagram-VRR7QNED-GfhY_hnV.js +1 -0
  7. package/packages/client/dist/assets/{agentExampleHighlight-Dws80em9.js → agentExampleHighlight-CyhVLy0_.js} +1 -1
  8. package/packages/client/dist/assets/arc-8nyjXGC3.js +1 -0
  9. package/packages/client/dist/assets/architectureDiagram-ZJ3FMSHR-B4onXLm4.js +36 -0
  10. package/packages/client/dist/assets/blockDiagram-677ZJIJ3-D0D6ncaN.js +132 -0
  11. package/packages/client/dist/assets/c4Diagram-LMCZKHZV-CE7Tip-_.js +10 -0
  12. package/packages/client/dist/assets/channel-CPjlbdOo.js +1 -0
  13. package/packages/client/dist/assets/chunk-2Q5K7J3B-4rW1da1Z.js +1 -0
  14. package/packages/client/dist/assets/chunk-32BRIVSS-JuzxS4QA.js +1 -0
  15. package/packages/client/dist/assets/chunk-5VM5RSS4-WRNf-xsi.js +15 -0
  16. package/packages/client/dist/assets/chunk-EX3LRPZG-BDWPlBKO.js +231 -0
  17. package/packages/client/dist/assets/chunk-JWPE2WC7-B8nCyQAc.js +1 -0
  18. package/packages/client/dist/assets/chunk-MOJQB5TN-7c-yriHe.js +88 -0
  19. package/packages/client/dist/assets/chunk-RYQCIY6F-LTDywRs7.js +1 -0
  20. package/packages/client/dist/assets/chunk-V7JOEXUC-DjVgQpjl.js +206 -0
  21. package/packages/client/dist/assets/chunk-VR4S4FIN-7jDdkUpx.js +1 -0
  22. package/packages/client/dist/assets/chunk-XXDRQBXY-ENKV2O8a.js +1 -0
  23. package/packages/client/dist/assets/classDiagram-OUVF2IWQ-C0PbC7Pi.js +1 -0
  24. package/packages/client/dist/assets/classDiagram-v2-EOCWNBFH-C0PbC7Pi.js +1 -0
  25. package/packages/client/dist/assets/{commandTokenHighlight-aR071CNy.js → commandTokenHighlight-DGP0RKx0.js} +1 -1
  26. package/packages/client/dist/assets/cose-bilkent-JH36ORCC-CQTNdQ-g.js +1 -0
  27. package/packages/client/dist/assets/cynefin-VYW2F7L2-BYlAJRcO.js +178 -0
  28. package/packages/client/dist/assets/cynefinDiagram-TSTJHNR4-7zyUTflT.js +62 -0
  29. package/packages/client/dist/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
  30. package/packages/client/dist/assets/dagre-VKFMJZFB-DyZir42W.js +4 -0
  31. package/packages/client/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  32. package/packages/client/dist/assets/diagram-FQU43EPY-CmNAU8Nw.js +3 -0
  33. package/packages/client/dist/assets/diagram-G47NLZAW-B0EIk03o.js +24 -0
  34. package/packages/client/dist/assets/diagram-NH7WQ7WH-Dulv0f1N.js +24 -0
  35. package/packages/client/dist/assets/diagram-OA4YK3LP-DEEcMLgi.js +30 -0
  36. package/packages/client/dist/assets/diagram-WEI45ONY-ZYebAZ93.js +41 -0
  37. package/packages/client/dist/assets/ebnfDiagram-CCIWWBDH-DCHpt3qn.js +1 -0
  38. package/packages/client/dist/assets/erDiagram-Q63AITRT-B6T87-48.js +85 -0
  39. package/packages/client/dist/assets/flowDiagram-23GEKE2U-DAcW6mKJ.js +156 -0
  40. package/packages/client/dist/assets/ganttDiagram-NO4QXBWP-Ct_eFFcM.js +292 -0
  41. package/packages/client/dist/assets/gitGraphDiagram-IHSO6WYX-BGi8Fdm2.js +106 -0
  42. package/packages/client/dist/assets/graph-C9eacEi8.js +1 -0
  43. package/packages/client/dist/assets/index-CtMiUbMJ.js +1671 -0
  44. package/packages/client/dist/assets/index-hTu6ZG2N.js +2 -0
  45. package/packages/client/dist/assets/index-oBa2uNlv.css +32 -0
  46. package/packages/client/dist/assets/infoDiagram-FWYZ7A6U-CqvwbXJn.js +2 -0
  47. package/packages/client/dist/assets/init-Gi6I4Gst.js +1 -0
  48. package/packages/client/dist/assets/ishikawaDiagram-FXEZZL3T-CHhr5ioQ.js +70 -0
  49. package/packages/client/dist/assets/journeyDiagram-5HDEW3XC-d20NzZAd.js +139 -0
  50. package/packages/client/dist/assets/kanban-definition-HUTT4EX6-CHx3wOer.js +89 -0
  51. package/packages/client/dist/assets/katex-C5jXJg4s.js +257 -0
  52. package/packages/client/dist/assets/layout-DEXfKzaS.js +1 -0
  53. package/packages/client/dist/assets/linear-Cnp1E9M4.js +1 -0
  54. package/packages/client/dist/assets/map-Czzmt4hB.js +1 -0
  55. package/packages/client/dist/assets/mermaid.core-jAhLhPYr.js +314 -0
  56. package/packages/client/dist/assets/mindmap-definition-LN4V7U3C-zi30RPoJ.js +96 -0
  57. package/packages/client/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  58. package/packages/client/dist/assets/pdf.worker.min-qwK7q_zL.mjs +28 -0
  59. package/packages/client/dist/assets/pegDiagram-2B236MQR-4gKLzTqu.js +1 -0
  60. package/packages/client/dist/assets/pieDiagram-ENE6RG2P-DRDhpjxP.js +39 -0
  61. package/packages/client/dist/assets/quadrantDiagram-ABIIQ3AL-FmTxRIlW.js +7 -0
  62. package/packages/client/dist/assets/railroadDiagram-RFXS5EU6-Bc-Q7iPs.js +1 -0
  63. package/packages/client/dist/assets/requirementDiagram-TGXJPOKE-D209tlqv.js +84 -0
  64. package/packages/client/dist/assets/sankeyDiagram-HTMAVEWB--I_lSbCV.js +40 -0
  65. package/packages/client/dist/assets/sequenceDiagram-DBY2YBRQ-Da5YJcDJ.js +162 -0
  66. package/packages/client/dist/assets/sizeCapture-X5ZJPWSS-DYuc-rzB.js +1 -0
  67. package/packages/client/dist/assets/{snippetTokenHighlight-ot6u3hW4.js → snippetTokenHighlight-qXHxvlCE.js} +1 -1
  68. package/packages/client/dist/assets/stateDiagram-2N3HPSRC-DoOeZoRP.js +1 -0
  69. package/packages/client/dist/assets/stateDiagram-v2-6OUMAXLB-DLkUyWs6.js +1 -0
  70. package/packages/client/dist/assets/swimlanes-5IMT3BWC-BcR7uCTf.js +2 -0
  71. package/packages/client/dist/assets/swimlanesDiagram-G3AALYLV-C0FDo-hf.js +8 -0
  72. package/packages/client/dist/assets/timeline-definition-FHXFAJF6-CLzKR6_i.js +120 -0
  73. package/packages/client/dist/assets/vennDiagram-L72KCM5P-TajEacPR.js +34 -0
  74. package/packages/client/dist/assets/wardleyDiagram-EHGQE667-DKvVEb3V.js +78 -0
  75. package/packages/client/dist/assets/xychartDiagram-FW5EYKEG-CaJG8r0j.js +7 -0
  76. package/packages/client/dist/index.html +2 -2
  77. package/packages/client/dist/sw.js +2 -2
  78. package/packages/server/dist/controllers/accountController.d.ts.map +1 -1
  79. package/packages/server/dist/controllers/accountController.js +31 -11
  80. package/packages/server/dist/controllers/accountController.js.map +1 -1
  81. package/packages/server/dist/controllers/fileSystemController.d.ts +6 -0
  82. package/packages/server/dist/controllers/fileSystemController.d.ts.map +1 -1
  83. package/packages/server/dist/controllers/fileSystemController.js +34 -0
  84. package/packages/server/dist/controllers/fileSystemController.js.map +1 -1
  85. package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
  86. package/packages/server/dist/controllers/projectController.js +4 -2
  87. package/packages/server/dist/controllers/projectController.js.map +1 -1
  88. package/packages/server/dist/controllers/sessionController.d.ts +5 -0
  89. package/packages/server/dist/controllers/sessionController.d.ts.map +1 -1
  90. package/packages/server/dist/controllers/sessionController.js +95 -3
  91. package/packages/server/dist/controllers/sessionController.js.map +1 -1
  92. package/packages/server/dist/handlers/hookBridge.d.ts +131 -0
  93. package/packages/server/dist/handlers/hookBridge.d.ts.map +1 -0
  94. package/packages/server/dist/handlers/hookBridge.js +178 -0
  95. package/packages/server/dist/handlers/hookBridge.js.map +1 -0
  96. package/packages/server/dist/handlers/streamCallbacks.d.ts.map +1 -1
  97. package/packages/server/dist/handlers/streamCallbacks.js +30 -4
  98. package/packages/server/dist/handlers/streamCallbacks.js.map +1 -1
  99. package/packages/server/dist/handlers/websocket.d.ts +44 -2
  100. package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
  101. package/packages/server/dist/handlers/websocket.js +666 -64
  102. package/packages/server/dist/handlers/websocket.js.map +1 -1
  103. package/packages/server/dist/index.js +4 -1
  104. package/packages/server/dist/index.js.map +1 -1
  105. package/packages/server/dist/locales/en/server.json +9 -3
  106. package/packages/server/dist/locales/es/server.json +9 -3
  107. package/packages/server/dist/locales/ja/server.json +9 -3
  108. package/packages/server/dist/locales/ko/server.json +9 -3
  109. package/packages/server/dist/locales/pt/server.json +9 -3
  110. package/packages/server/dist/locales/zh-CN/server.json +9 -3
  111. package/packages/server/dist/routes/account.d.ts.map +1 -1
  112. package/packages/server/dist/routes/account.js +4 -2
  113. package/packages/server/dist/routes/account.js.map +1 -1
  114. package/packages/server/dist/routes/fileSystem.d.ts.map +1 -1
  115. package/packages/server/dist/routes/fileSystem.js +2 -0
  116. package/packages/server/dist/routes/fileSystem.js.map +1 -1
  117. package/packages/server/dist/routes/sessions.d.ts.map +1 -1
  118. package/packages/server/dist/routes/sessions.js +2 -0
  119. package/packages/server/dist/routes/sessions.js.map +1 -1
  120. package/packages/server/dist/services/accountInfoService.d.ts +36 -1
  121. package/packages/server/dist/services/accountInfoService.d.ts.map +1 -1
  122. package/packages/server/dist/services/accountInfoService.js +90 -4
  123. package/packages/server/dist/services/accountInfoService.js.map +1 -1
  124. package/packages/server/dist/services/accountStorageService.d.ts +55 -9
  125. package/packages/server/dist/services/accountStorageService.d.ts.map +1 -1
  126. package/packages/server/dist/services/accountStorageService.js +244 -12
  127. package/packages/server/dist/services/accountStorageService.js.map +1 -1
  128. package/packages/server/dist/services/chatEngine.d.ts +59 -3
  129. package/packages/server/dist/services/chatEngine.d.ts.map +1 -1
  130. package/packages/server/dist/services/chatService.d.ts +112 -3
  131. package/packages/server/dist/services/chatService.d.ts.map +1 -1
  132. package/packages/server/dist/services/chatService.js +353 -98
  133. package/packages/server/dist/services/chatService.js.map +1 -1
  134. package/packages/server/dist/services/claudeLoginService.d.ts +14 -3
  135. package/packages/server/dist/services/claudeLoginService.d.ts.map +1 -1
  136. package/packages/server/dist/services/claudeLoginService.js +18 -4
  137. package/packages/server/dist/services/claudeLoginService.js.map +1 -1
  138. package/packages/server/dist/services/cliChatEngine.d.ts.map +1 -1
  139. package/packages/server/dist/services/cliChatEngine.js +124 -22
  140. package/packages/server/dist/services/cliChatEngine.js.map +1 -1
  141. package/packages/server/dist/services/cliModalDetect.d.ts +14 -0
  142. package/packages/server/dist/services/cliModalDetect.d.ts.map +1 -1
  143. package/packages/server/dist/services/cliModalDetect.js +12 -3
  144. package/packages/server/dist/services/cliModalDetect.js.map +1 -1
  145. package/packages/server/dist/services/cliSessionPool.js +1 -1
  146. package/packages/server/dist/services/cliSessionPool.js.map +1 -1
  147. package/packages/server/dist/services/commandService.d.ts.map +1 -1
  148. package/packages/server/dist/services/commandService.js +19 -9
  149. package/packages/server/dist/services/commandService.js.map +1 -1
  150. package/packages/server/dist/services/engineResidencyRegistry.d.ts +112 -0
  151. package/packages/server/dist/services/engineResidencyRegistry.d.ts.map +1 -0
  152. package/packages/server/dist/services/engineResidencyRegistry.js +195 -0
  153. package/packages/server/dist/services/engineResidencyRegistry.js.map +1 -0
  154. package/packages/server/dist/services/harnessAgentService.d.ts.map +1 -1
  155. package/packages/server/dist/services/harnessAgentService.js +12 -2
  156. package/packages/server/dist/services/harnessAgentService.js.map +1 -1
  157. package/packages/server/dist/services/harnessLintService.d.ts.map +1 -1
  158. package/packages/server/dist/services/harnessLintService.js +25 -114
  159. package/packages/server/dist/services/harnessLintService.js.map +1 -1
  160. package/packages/server/dist/services/historyParser.d.ts.map +1 -1
  161. package/packages/server/dist/services/historyParser.js +23 -6
  162. package/packages/server/dist/services/historyParser.js.map +1 -1
  163. package/packages/server/dist/services/imageStorageService.d.ts.map +1 -1
  164. package/packages/server/dist/services/imageStorageService.js +7 -0
  165. package/packages/server/dist/services/imageStorageService.js.map +1 -1
  166. package/packages/server/dist/services/projectService.d.ts +4 -1
  167. package/packages/server/dist/services/projectService.d.ts.map +1 -1
  168. package/packages/server/dist/services/projectService.js +14 -1
  169. package/packages/server/dist/services/projectService.js.map +1 -1
  170. package/packages/server/dist/services/promptQueue.d.ts +34 -0
  171. package/packages/server/dist/services/promptQueue.d.ts.map +1 -0
  172. package/packages/server/dist/services/promptQueue.js +80 -0
  173. package/packages/server/dist/services/promptQueue.js.map +1 -0
  174. package/packages/server/dist/services/ptyService.d.ts.map +1 -1
  175. package/packages/server/dist/services/ptyService.js +4 -1
  176. package/packages/server/dist/services/ptyService.js.map +1 -1
  177. package/packages/server/dist/services/queueService.d.ts.map +1 -1
  178. package/packages/server/dist/services/queueService.js +62 -4
  179. package/packages/server/dist/services/queueService.js.map +1 -1
  180. package/packages/server/dist/services/rateLimitProbeService.d.ts +73 -3
  181. package/packages/server/dist/services/rateLimitProbeService.d.ts.map +1 -1
  182. package/packages/server/dist/services/rateLimitProbeService.js +397 -30
  183. package/packages/server/dist/services/rateLimitProbeService.js.map +1 -1
  184. package/packages/server/dist/services/sessionBufferManager.d.ts.map +1 -1
  185. package/packages/server/dist/services/sessionBufferManager.js +22 -4
  186. package/packages/server/dist/services/sessionBufferManager.js.map +1 -1
  187. package/packages/server/dist/services/sessionExportService.d.ts +34 -0
  188. package/packages/server/dist/services/sessionExportService.d.ts.map +1 -0
  189. package/packages/server/dist/services/sessionExportService.js +269 -0
  190. package/packages/server/dist/services/sessionExportService.js.map +1 -0
  191. package/packages/server/dist/services/sessionService.d.ts +6 -0
  192. package/packages/server/dist/services/sessionService.d.ts.map +1 -1
  193. package/packages/server/dist/services/sessionService.js +30 -9
  194. package/packages/server/dist/services/sessionService.js.map +1 -1
  195. package/packages/server/dist/services/streamHandler.d.ts +177 -1
  196. package/packages/server/dist/services/streamHandler.d.ts.map +1 -1
  197. package/packages/server/dist/services/streamHandler.js +425 -39
  198. package/packages/server/dist/services/streamHandler.js.map +1 -1
  199. package/packages/server/dist/services/workspaceContext.d.ts.map +1 -1
  200. package/packages/server/dist/services/workspaceContext.js +6 -0
  201. package/packages/server/dist/services/workspaceContext.js.map +1 -1
  202. package/packages/server/dist/snippets/fix-lint-command-not-on-path +13 -0
  203. package/packages/server/dist/snippets/fix-lint-duplicate-name +12 -0
  204. package/packages/server/dist/snippets/fix-lint-env-var-undefined +13 -0
  205. package/packages/server/dist/snippets/fix-lint-regex-invalid +14 -0
  206. package/packages/server/dist/snippets/fix-lint-tools-non-standard +14 -0
  207. package/packages/server/dist/snippets/fix-lint-url-invalid +13 -0
  208. package/packages/server/dist/snippets/fix-lint-yaml-parse-error +14 -0
  209. package/packages/server/dist/snippets/fix-malformed-skill +17 -0
  210. package/packages/server/dist/utils/backgroundHookSettings.d.ts +31 -0
  211. package/packages/server/dist/utils/backgroundHookSettings.d.ts.map +1 -0
  212. package/packages/server/dist/utils/backgroundHookSettings.js +41 -0
  213. package/packages/server/dist/utils/backgroundHookSettings.js.map +1 -0
  214. package/packages/server/dist/utils/backgroundTaskPoller.d.ts +69 -0
  215. package/packages/server/dist/utils/backgroundTaskPoller.d.ts.map +1 -0
  216. package/packages/server/dist/utils/backgroundTaskPoller.js +356 -0
  217. package/packages/server/dist/utils/backgroundTaskPoller.js.map +1 -0
  218. package/packages/server/dist/utils/backgroundTaskTracker.d.ts +51 -3
  219. package/packages/server/dist/utils/backgroundTaskTracker.d.ts.map +1 -1
  220. package/packages/server/dist/utils/backgroundTaskTracker.js +117 -9
  221. package/packages/server/dist/utils/backgroundTaskTracker.js.map +1 -1
  222. package/packages/server/dist/utils/backgroundToolGuard.d.ts +26 -0
  223. package/packages/server/dist/utils/backgroundToolGuard.d.ts.map +1 -0
  224. package/packages/server/dist/utils/backgroundToolGuard.js +40 -0
  225. package/packages/server/dist/utils/backgroundToolGuard.js.map +1 -0
  226. package/packages/server/dist/utils/boundedRing.d.ts +44 -0
  227. package/packages/server/dist/utils/boundedRing.d.ts.map +1 -0
  228. package/packages/server/dist/utils/boundedRing.js +77 -0
  229. package/packages/server/dist/utils/boundedRing.js.map +1 -0
  230. package/packages/server/dist/utils/effortUtils.d.ts +7 -6
  231. package/packages/server/dist/utils/effortUtils.d.ts.map +1 -1
  232. package/packages/server/dist/utils/effortUtils.js +10 -9
  233. package/packages/server/dist/utils/effortUtils.js.map +1 -1
  234. package/packages/server/dist/utils/messageTree.d.ts +25 -0
  235. package/packages/server/dist/utils/messageTree.d.ts.map +1 -1
  236. package/packages/server/dist/utils/messageTree.js +167 -3
  237. package/packages/server/dist/utils/messageTree.js.map +1 -1
  238. package/packages/server/dist/utils/pathUtils.d.ts.map +1 -1
  239. package/packages/server/dist/utils/pathUtils.js +1 -0
  240. package/packages/server/dist/utils/pathUtils.js.map +1 -1
  241. package/packages/server/dist/utils/residencyActivation.d.ts +61 -0
  242. package/packages/server/dist/utils/residencyActivation.d.ts.map +1 -0
  243. package/packages/server/dist/utils/residencyActivation.js +60 -0
  244. package/packages/server/dist/utils/residencyActivation.js.map +1 -0
  245. package/packages/server/dist/utils/residencyPolicy.d.ts +62 -0
  246. package/packages/server/dist/utils/residencyPolicy.d.ts.map +1 -0
  247. package/packages/server/dist/utils/residencyPolicy.js +82 -0
  248. package/packages/server/dist/utils/residencyPolicy.js.map +1 -0
  249. package/packages/server/dist/utils/runtimeParamStrategy.d.ts +51 -0
  250. package/packages/server/dist/utils/runtimeParamStrategy.d.ts.map +1 -0
  251. package/packages/server/dist/utils/runtimeParamStrategy.js +60 -0
  252. package/packages/server/dist/utils/runtimeParamStrategy.js.map +1 -0
  253. package/packages/server/dist/utils/stopStrategy.d.ts +62 -0
  254. package/packages/server/dist/utils/stopStrategy.d.ts.map +1 -0
  255. package/packages/server/dist/utils/stopStrategy.js +65 -0
  256. package/packages/server/dist/utils/stopStrategy.js.map +1 -0
  257. package/packages/server/resources/internals/INDEX.md +1 -1
  258. package/packages/server/resources/internals/image-storage.md +14 -2
  259. package/packages/server/resources/manual/02-chat.md +30 -13
  260. package/packages/server/resources/manual/03-sessions.md +22 -1
  261. package/packages/server/resources/manual/04-slash-commands-favorites.md +2 -1
  262. package/packages/server/resources/manual/05-projects.md +1 -0
  263. package/packages/server/resources/manual/06-file-explorer-editor.md +21 -2
  264. package/packages/server/resources/manual/07-git.md +1 -1
  265. package/packages/server/resources/manual/08-terminal.md +1 -1
  266. package/packages/server/resources/manual/12-harness-workbench.md +21 -16
  267. package/packages/server/resources/manual/13-settings.md +22 -6
  268. package/packages/server/resources/manual/14-keyboard-shortcuts.md +8 -1
  269. package/packages/shared/dist/constants/messageTree.d.ts +2 -0
  270. package/packages/shared/dist/constants/messageTree.d.ts.map +1 -1
  271. package/packages/shared/dist/constants/messageTree.js +6 -0
  272. package/packages/shared/dist/constants/messageTree.js.map +1 -1
  273. package/packages/shared/dist/index.d.ts +1 -1
  274. package/packages/shared/dist/index.d.ts.map +1 -1
  275. package/packages/shared/dist/index.js.map +1 -1
  276. package/packages/shared/dist/types/accounts.d.ts +13 -0
  277. package/packages/shared/dist/types/accounts.d.ts.map +1 -1
  278. package/packages/shared/dist/types/history.d.ts +14 -1
  279. package/packages/shared/dist/types/history.d.ts.map +1 -1
  280. package/packages/shared/dist/types/message.d.ts +26 -3
  281. package/packages/shared/dist/types/message.d.ts.map +1 -1
  282. package/packages/shared/dist/types/message.js +21 -1
  283. package/packages/shared/dist/types/message.js.map +1 -1
  284. package/packages/shared/dist/types/preferences.d.ts +5 -1
  285. package/packages/shared/dist/types/preferences.d.ts.map +1 -1
  286. package/packages/shared/dist/types/preferences.js +3 -0
  287. package/packages/shared/dist/types/preferences.js.map +1 -1
  288. package/packages/shared/dist/types/sdk.d.ts +68 -10
  289. package/packages/shared/dist/types/sdk.d.ts.map +1 -1
  290. package/packages/shared/dist/types/sdk.js +56 -16
  291. package/packages/shared/dist/types/sdk.js.map +1 -1
  292. package/packages/shared/dist/types/session.d.ts +6 -0
  293. package/packages/shared/dist/types/session.d.ts.map +1 -1
  294. package/packages/shared/dist/types/session.js +6 -0
  295. package/packages/shared/dist/types/session.js.map +1 -1
  296. package/packages/shared/dist/types/streaming.d.ts +32 -2
  297. package/packages/shared/dist/types/streaming.d.ts.map +1 -1
  298. package/packages/shared/dist/types/streaming.js.map +1 -1
  299. package/packages/shared/dist/types/websocket.d.ts +4 -0
  300. package/packages/shared/dist/types/websocket.d.ts.map +1 -1
  301. package/scripts/probe-persistent-hooks.mjs +795 -0
  302. package/packages/client/dist/assets/index-BHbx57gc.js +0 -2
  303. package/packages/client/dist/assets/index-BX7y_Qt3.css +0 -32
  304. package/packages/client/dist/assets/index-DWIOf7rA.js +0 -1657
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Story 38.7 — pure decision helpers for turn-flow residency ACTIVATION.
3
+ *
4
+ * Story 38.5 delivered the resident-engine registry + resource policy as a "ready
5
+ * seam" but never wired it into the turn flow (`chat:send` still re-spawned an
6
+ * engine every turn). This story turns it on. The two branch decisions that gate
7
+ * activation — "reuse the live resident engine vs create a fresh one" and "register
8
+ * a freshly-created engine as resident now vs at re-key" — are kept PURE here so
9
+ * they can be unit-tested WITHOUT the (pre-red) websocket socket harness, exactly
10
+ * as Story 38.3 (stopStrategy.ts) and 38.4 (runtimeParamStrategy.ts) did for their
11
+ * own branch logic. The stateful wiring that consumes these verdicts lives in
12
+ * handlers/websocket.ts; the engine lifecycle lives in
13
+ * services/engineResidencyRegistry.ts.
14
+ */
15
+ /**
16
+ * Whether `handleChatSend` should REUSE the live resident engine for this session
17
+ * instead of creating (and ~3s re-spawning) a fresh one.
18
+ *
19
+ * Reuse only when ALL hold:
20
+ * - `hasLiveResident` — the registry returned a live engine for this session
21
+ * (a crashed/dead one was already dropped by `registry.get`, so a hit means live).
22
+ * - not CLI — residency is SDK-engine-only (the CLI PTY lifecycle is untouched;
23
+ * a CLI engine never registers, so this is a belt-and-suspenders guard).
24
+ * - not a fork — `forkSession` is a query() CREATION option (no live-query setter,
25
+ * 38.5 probe), so a fork is inherently a NEW query for a NEW session; it must not
26
+ * ride the original session's resident engine.
27
+ * - not a branch/rewind — `resumeSessionAt` (conversation branching) and
28
+ * `rewindToMessageUuid` (inline rewind-before-send) are likewise query() CREATION
29
+ * options applied only when the persistent query is first built. Reusing a live
30
+ * query would silently ignore them, so force a fresh (registry-replacing) query.
31
+ *
32
+ * Note there is no separate `isResuming` input: a resume whose target is already
33
+ * resident simply continues on that live engine (the `resume` option is only needed
34
+ * to rebuild a NON-resident session from JSONL), so `hasLiveResident` already
35
+ * captures it. A resume that also branches/rewinds is caught by `isBranchOrRewind`.
36
+ */
37
+ export declare function shouldReuseResidentEngine(params: {
38
+ hasLiveResident: boolean;
39
+ engineMode: string | undefined;
40
+ isFork: boolean;
41
+ isBranchOrRewind: boolean;
42
+ }): boolean;
43
+ /**
44
+ * Whether a FRESHLY-CREATED engine should be registered as resident immediately at
45
+ * creation (before the send) rather than deferred.
46
+ *
47
+ * Registering before the send is what makes the engine `resident` so its callback
48
+ * path SKIPS the turn-end `close()` and the process survives into the next turn.
49
+ *
50
+ * - SDK only — the CLI engine has no residency hooks (`register` is a feature-detect
51
+ * no-op anyway; this keeps the caller from even trying).
52
+ * - not a fork — a fork's final sessionId is unknown until the SDK assigns it, and
53
+ * registering under the ORIGINAL session key at creation would clobber the
54
+ * original's resident entry. A fork is therefore registered under its assigned id
55
+ * at re-key time instead (see the websocket re-key mirror), not here.
56
+ */
57
+ export declare function shouldRegisterOnCreate(params: {
58
+ engineMode: string | undefined;
59
+ isFork: boolean;
60
+ }): boolean;
61
+ //# sourceMappingURL=residencyActivation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"residencyActivation.d.ts","sourceRoot":"","sources":["../../src/utils/residencyActivation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAChD,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,GAAG,OAAO,CAOV;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAEV"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Story 38.7 — pure decision helpers for turn-flow residency ACTIVATION.
3
+ *
4
+ * Story 38.5 delivered the resident-engine registry + resource policy as a "ready
5
+ * seam" but never wired it into the turn flow (`chat:send` still re-spawned an
6
+ * engine every turn). This story turns it on. The two branch decisions that gate
7
+ * activation — "reuse the live resident engine vs create a fresh one" and "register
8
+ * a freshly-created engine as resident now vs at re-key" — are kept PURE here so
9
+ * they can be unit-tested WITHOUT the (pre-red) websocket socket harness, exactly
10
+ * as Story 38.3 (stopStrategy.ts) and 38.4 (runtimeParamStrategy.ts) did for their
11
+ * own branch logic. The stateful wiring that consumes these verdicts lives in
12
+ * handlers/websocket.ts; the engine lifecycle lives in
13
+ * services/engineResidencyRegistry.ts.
14
+ */
15
+ /**
16
+ * Whether `handleChatSend` should REUSE the live resident engine for this session
17
+ * instead of creating (and ~3s re-spawning) a fresh one.
18
+ *
19
+ * Reuse only when ALL hold:
20
+ * - `hasLiveResident` — the registry returned a live engine for this session
21
+ * (a crashed/dead one was already dropped by `registry.get`, so a hit means live).
22
+ * - not CLI — residency is SDK-engine-only (the CLI PTY lifecycle is untouched;
23
+ * a CLI engine never registers, so this is a belt-and-suspenders guard).
24
+ * - not a fork — `forkSession` is a query() CREATION option (no live-query setter,
25
+ * 38.5 probe), so a fork is inherently a NEW query for a NEW session; it must not
26
+ * ride the original session's resident engine.
27
+ * - not a branch/rewind — `resumeSessionAt` (conversation branching) and
28
+ * `rewindToMessageUuid` (inline rewind-before-send) are likewise query() CREATION
29
+ * options applied only when the persistent query is first built. Reusing a live
30
+ * query would silently ignore them, so force a fresh (registry-replacing) query.
31
+ *
32
+ * Note there is no separate `isResuming` input: a resume whose target is already
33
+ * resident simply continues on that live engine (the `resume` option is only needed
34
+ * to rebuild a NON-resident session from JSONL), so `hasLiveResident` already
35
+ * captures it. A resume that also branches/rewinds is caught by `isBranchOrRewind`.
36
+ */
37
+ export function shouldReuseResidentEngine(params) {
38
+ return (params.hasLiveResident &&
39
+ params.engineMode !== 'cli' &&
40
+ !params.isFork &&
41
+ !params.isBranchOrRewind);
42
+ }
43
+ /**
44
+ * Whether a FRESHLY-CREATED engine should be registered as resident immediately at
45
+ * creation (before the send) rather than deferred.
46
+ *
47
+ * Registering before the send is what makes the engine `resident` so its callback
48
+ * path SKIPS the turn-end `close()` and the process survives into the next turn.
49
+ *
50
+ * - SDK only — the CLI engine has no residency hooks (`register` is a feature-detect
51
+ * no-op anyway; this keeps the caller from even trying).
52
+ * - not a fork — a fork's final sessionId is unknown until the SDK assigns it, and
53
+ * registering under the ORIGINAL session key at creation would clobber the
54
+ * original's resident entry. A fork is therefore registered under its assigned id
55
+ * at re-key time instead (see the websocket re-key mirror), not here.
56
+ */
57
+ export function shouldRegisterOnCreate(params) {
58
+ return params.engineMode === 'sdk' && !params.isFork;
59
+ }
60
+ //# sourceMappingURL=residencyActivation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"residencyActivation.js","sourceRoot":"","sources":["../../src/utils/residencyActivation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAKzC;IACC,OAAO,CACL,MAAM,CAAC,eAAe;QACtB,MAAM,CAAC,UAAU,KAAK,KAAK;QAC3B,CAAC,MAAM,CAAC,MAAM;QACd,CAAC,MAAM,CAAC,gBAAgB,CACzB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAGtC;IACC,OAAO,MAAM,CAAC,UAAU,KAAK,KAAK,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;AACvD,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Story 38.5 (Task 6a/6b) — resident-engine resource policy, as a PURE classifier.
3
+ *
4
+ * Under turn-boundary process residency the server keeps one SDK engine process
5
+ * alive per active session (no ~3s re-spawn between turns — probe-confirmed GO).
6
+ * That residency MUST be bounded, or idle sessions accumulate engine processes
7
+ * without limit ("무정책 상주 금지"). Two independent bounds:
8
+ *
9
+ * (a) idle timeout — a session with no turn activity for `idleTimeoutMs` is
10
+ * closed and dropped; the next send naturally resumes it from JSONL.
11
+ * (b) count cap — at most `maxEngines` resident engines; when a new one would
12
+ * exceed the cap, evict the LEAST-recently-active (LRU) sessions first.
13
+ *
14
+ * This is server-side *process lifecycle* management (how long a spawned engine
15
+ * lives), NOT traffic shaping (rate-limiting inbound requests) — the latter is
16
+ * infra/reverse-proxy territory. Kept pure (no timers, no engine handles, no
17
+ * socket deps) so the eviction/idle decisions are unit-testable without the
18
+ * (pre-red) websocket harness — same discipline as stopStrategy.ts /
19
+ * runtimeParamStrategy.ts. The stateful registry that HOLDS the engines and acts
20
+ * on these verdicts lives in services/engineResidencyRegistry.ts.
21
+ */
22
+ /** Minimal shape the policy needs about a resident engine — just its liveness clock. */
23
+ export interface ResidencyEntryInfo {
24
+ sessionId: string;
25
+ /** Wall-clock ms of the last turn activity on this session (Date.now() at touch). */
26
+ lastActivityAt: number;
27
+ }
28
+ /** Tunable bounds. `idleTimeoutMs <= 0` disables the idle sweep; `maxEngines <= 0` disables the cap. */
29
+ export interface ResidencyPolicyConfig {
30
+ idleTimeoutMs: number;
31
+ maxEngines: number;
32
+ }
33
+ /** Hard floor/ceiling so a misconfigured preference can't disable bounding in a dangerous way. */
34
+ export declare const RESIDENCY_LIMITS: {
35
+ /** Below this an idle timeout is treated as "disabled" (0) rather than an absurd churn. */
36
+ readonly MIN_IDLE_TIMEOUT_MS: 10000;
37
+ /** Absolute ceiling on resident engines regardless of preference (memory guard). */
38
+ readonly MAX_ENGINES_CEILING: 64;
39
+ };
40
+ /**
41
+ * Normalize a raw config (e.g. from preferences) into a safe effective policy:
42
+ * an idle timeout under the floor (but > 0) is lifted to the floor; the count cap
43
+ * is clamped to the ceiling. 0/negative means "disabled" for that bound and is
44
+ * preserved as-is.
45
+ */
46
+ export declare function normalizeResidencyConfig(raw: Partial<ResidencyPolicyConfig>): ResidencyPolicyConfig;
47
+ /**
48
+ * Session ids whose last activity is older than the idle timeout at `now`.
49
+ * Returns [] when the idle sweep is disabled (`idleTimeoutMs <= 0`). Order is
50
+ * oldest-first so callers can log/act deterministically.
51
+ */
52
+ export declare function selectIdleExpired(entries: readonly ResidencyEntryInfo[], idleTimeoutMs: number, now: number): string[];
53
+ /**
54
+ * LRU sessions to evict so that, after ADDING `incoming` new engines, the resident
55
+ * count does not exceed `maxEngines`. `incoming` defaults to 0 (pure over-cap
56
+ * trim of the current set). Sessions in `protectedIds` are never selected (e.g.
57
+ * the session whose turn is starting right now) — they still COUNT toward the cap,
58
+ * so more of the remaining LRU entries are evicted to make room. Returns
59
+ * oldest-first (the eviction order); [] when the cap is disabled or not exceeded.
60
+ */
61
+ export declare function selectLruEvictions(entries: readonly ResidencyEntryInfo[], maxEngines: number, incoming?: number, protectedIds?: readonly string[]): string[];
62
+ //# sourceMappingURL=residencyPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"residencyPolicy.d.ts","sourceRoot":"","sources":["../../src/utils/residencyPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,wFAAwF;AACxF,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wGAAwG;AACxG,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,kGAAkG;AAClG,eAAO,MAAM,gBAAgB;IAC3B,2FAA2F;;IAE3F,oFAAoF;;CAE5E,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,qBAAqB,CAUnG;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,kBAAkB,EAAE,EACtC,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAMV;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,kBAAkB,EAAE,EACtC,UAAU,EAAE,MAAM,EAClB,QAAQ,SAAI,EACZ,YAAY,GAAE,SAAS,MAAM,EAAO,GACnC,MAAM,EAAE,CAcV"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Story 38.5 (Task 6a/6b) — resident-engine resource policy, as a PURE classifier.
3
+ *
4
+ * Under turn-boundary process residency the server keeps one SDK engine process
5
+ * alive per active session (no ~3s re-spawn between turns — probe-confirmed GO).
6
+ * That residency MUST be bounded, or idle sessions accumulate engine processes
7
+ * without limit ("무정책 상주 금지"). Two independent bounds:
8
+ *
9
+ * (a) idle timeout — a session with no turn activity for `idleTimeoutMs` is
10
+ * closed and dropped; the next send naturally resumes it from JSONL.
11
+ * (b) count cap — at most `maxEngines` resident engines; when a new one would
12
+ * exceed the cap, evict the LEAST-recently-active (LRU) sessions first.
13
+ *
14
+ * This is server-side *process lifecycle* management (how long a spawned engine
15
+ * lives), NOT traffic shaping (rate-limiting inbound requests) — the latter is
16
+ * infra/reverse-proxy territory. Kept pure (no timers, no engine handles, no
17
+ * socket deps) so the eviction/idle decisions are unit-testable without the
18
+ * (pre-red) websocket harness — same discipline as stopStrategy.ts /
19
+ * runtimeParamStrategy.ts. The stateful registry that HOLDS the engines and acts
20
+ * on these verdicts lives in services/engineResidencyRegistry.ts.
21
+ */
22
+ /** Hard floor/ceiling so a misconfigured preference can't disable bounding in a dangerous way. */
23
+ export const RESIDENCY_LIMITS = {
24
+ /** Below this an idle timeout is treated as "disabled" (0) rather than an absurd churn. */
25
+ MIN_IDLE_TIMEOUT_MS: 10_000,
26
+ /** Absolute ceiling on resident engines regardless of preference (memory guard). */
27
+ MAX_ENGINES_CEILING: 64,
28
+ };
29
+ /**
30
+ * Normalize a raw config (e.g. from preferences) into a safe effective policy:
31
+ * an idle timeout under the floor (but > 0) is lifted to the floor; the count cap
32
+ * is clamped to the ceiling. 0/negative means "disabled" for that bound and is
33
+ * preserved as-is.
34
+ */
35
+ export function normalizeResidencyConfig(raw) {
36
+ const rawIdle = raw.idleTimeoutMs ?? 0;
37
+ const idleTimeoutMs = rawIdle <= 0
38
+ ? 0
39
+ : Math.max(rawIdle, RESIDENCY_LIMITS.MIN_IDLE_TIMEOUT_MS);
40
+ const rawMax = raw.maxEngines ?? 0;
41
+ const maxEngines = rawMax <= 0
42
+ ? 0
43
+ : Math.min(rawMax, RESIDENCY_LIMITS.MAX_ENGINES_CEILING);
44
+ return { idleTimeoutMs, maxEngines };
45
+ }
46
+ /**
47
+ * Session ids whose last activity is older than the idle timeout at `now`.
48
+ * Returns [] when the idle sweep is disabled (`idleTimeoutMs <= 0`). Order is
49
+ * oldest-first so callers can log/act deterministically.
50
+ */
51
+ export function selectIdleExpired(entries, idleTimeoutMs, now) {
52
+ if (idleTimeoutMs <= 0)
53
+ return [];
54
+ return [...entries]
55
+ .filter((e) => now - e.lastActivityAt >= idleTimeoutMs)
56
+ .sort((a, b) => a.lastActivityAt - b.lastActivityAt)
57
+ .map((e) => e.sessionId);
58
+ }
59
+ /**
60
+ * LRU sessions to evict so that, after ADDING `incoming` new engines, the resident
61
+ * count does not exceed `maxEngines`. `incoming` defaults to 0 (pure over-cap
62
+ * trim of the current set). Sessions in `protectedIds` are never selected (e.g.
63
+ * the session whose turn is starting right now) — they still COUNT toward the cap,
64
+ * so more of the remaining LRU entries are evicted to make room. Returns
65
+ * oldest-first (the eviction order); [] when the cap is disabled or not exceeded.
66
+ */
67
+ export function selectLruEvictions(entries, maxEngines, incoming = 0, protectedIds = []) {
68
+ if (maxEngines <= 0)
69
+ return [];
70
+ const total = entries.length + incoming;
71
+ const overflow = total - maxEngines;
72
+ if (overflow <= 0)
73
+ return [];
74
+ const protectedSet = new Set(protectedIds);
75
+ const evictable = [...entries]
76
+ .filter((e) => !protectedSet.has(e.sessionId))
77
+ .sort((a, b) => a.lastActivityAt - b.lastActivityAt);
78
+ // Cannot evict more than the evictable pool (protected entries stay even if that
79
+ // means briefly exceeding the cap — the alternative would kill a live turn).
80
+ return evictable.slice(0, Math.min(overflow, evictable.length)).map((e) => e.sessionId);
81
+ }
82
+ //# sourceMappingURL=residencyPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"residencyPolicy.js","sourceRoot":"","sources":["../../src/utils/residencyPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAeH,kGAAkG;AAClG,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,2FAA2F;IAC3F,mBAAmB,EAAE,MAAM;IAC3B,oFAAoF;IACpF,mBAAmB,EAAE,EAAE;CACf,CAAC;AAEX;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAmC;IAC1E,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,IAAI,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,OAAO,IAAI,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;IAC3D,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAsC,EACtC,aAAqB,EACrB,GAAW;IAEX,IAAI,aAAa,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO,CAAC,GAAG,OAAO,CAAC;SAChB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,cAAc,IAAI,aAAa,CAAC;SACtD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC;SACnD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAsC,EACtC,UAAkB,EAClB,QAAQ,GAAG,CAAC,EACZ,eAAkC,EAAE;IAEpC,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC;IACxC,MAAM,QAAQ,GAAG,KAAK,GAAG,UAAU,CAAC;IACpC,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAE7B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,CAAC,GAAG,OAAO,CAAC;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;SAC7C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;IAEvD,iFAAiF;IACjF,6EAA6E;IAC7E,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Story 38.4 — runtime parameter transition strategy.
3
+ *
4
+ * Classifies each mid-session parameter change as either injectable into the
5
+ * LIVE persistent query via a runtime setter (no process restart, no ~3s
6
+ * relaunch) or requiring a generator restart (a fresh query — the session /
7
+ * conversation is preserved, only the underlying generation process restarts).
8
+ *
9
+ * The classification is the probe-confirmed GO/NO-GO verdict from
10
+ * `scripts/probe-persistent-hooks.mjs runtime` (feedback_sdk_verify — measured,
11
+ * not assumed):
12
+ *
13
+ * · model → runtime-inject (Query.setModel — GO: the next turn's
14
+ * response reports the new model id)
15
+ * · permissionMode → runtime-inject (Query.setPermissionMode — GO, in place
16
+ * since Story 37.5; survives the
17
+ * persistent stream)
18
+ * · thinking → runtime-inject (Query.setMaxThinkingTokens — GO, but no
19
+ * caller changes it mid-turn today; the
20
+ * budget is derived at query creation from
21
+ * the model + showThinkingBlocks pref, so
22
+ * it stays a creation option — capability
23
+ * confirmed, wiring deferred)
24
+ * · effort → restart (the Query interface has NO setEffort —
25
+ * effort is a query() creation option only,
26
+ * so a change is inherently NO-GO for
27
+ * runtime injection and falls back to a
28
+ * generator restart)
29
+ *
30
+ * This is a PURE classifier (no SDK / socket dependency) so the "runtime inject
31
+ * vs fallback" branch (AC3) is unit-testable without the pre-red websocket
32
+ * harness. It is the SEAM the Story 38.5 residency path consumes: when a
33
+ * retained live query exists across turns, a 'runtime-inject' param is applied
34
+ * via its setter while a 'restart' param forces `createPersistentQuery`. Under
35
+ * the current instance-per-turn production (Story 38.1), every send already
36
+ * recreates the query, so 'restart' is the status quo and the turn-between
37
+ * benefit of 'runtime-inject' only becomes observable once 38.5 keeps the
38
+ * process resident — 38.4 leaves the seam, it does not implement residency.
39
+ */
40
+ export type RuntimeParam = 'model' | 'permissionMode' | 'thinking' | 'effort';
41
+ export type RuntimeTransition = 'runtime-inject' | 'restart';
42
+ /**
43
+ * How a mid-session change to `param` reaches the engine: injected into the live
44
+ * query via its runtime setter, or applied by restarting the generator (new
45
+ * query, session preserved). See the module doc for the probe verdict backing
46
+ * each mapping.
47
+ */
48
+ export declare function runtimeTransitionFor(param: RuntimeParam): RuntimeTransition;
49
+ /** True when `param` can be applied to a live query without a generator restart. */
50
+ export declare function canRuntimeInject(param: RuntimeParam): boolean;
51
+ //# sourceMappingURL=runtimeParamStrategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeParamStrategy.d.ts","sourceRoot":"","sources":["../../src/utils/runtimeParamStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,gBAAgB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE9E,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAE7D;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,iBAAiB,CAS3E;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Story 38.4 — runtime parameter transition strategy.
3
+ *
4
+ * Classifies each mid-session parameter change as either injectable into the
5
+ * LIVE persistent query via a runtime setter (no process restart, no ~3s
6
+ * relaunch) or requiring a generator restart (a fresh query — the session /
7
+ * conversation is preserved, only the underlying generation process restarts).
8
+ *
9
+ * The classification is the probe-confirmed GO/NO-GO verdict from
10
+ * `scripts/probe-persistent-hooks.mjs runtime` (feedback_sdk_verify — measured,
11
+ * not assumed):
12
+ *
13
+ * · model → runtime-inject (Query.setModel — GO: the next turn's
14
+ * response reports the new model id)
15
+ * · permissionMode → runtime-inject (Query.setPermissionMode — GO, in place
16
+ * since Story 37.5; survives the
17
+ * persistent stream)
18
+ * · thinking → runtime-inject (Query.setMaxThinkingTokens — GO, but no
19
+ * caller changes it mid-turn today; the
20
+ * budget is derived at query creation from
21
+ * the model + showThinkingBlocks pref, so
22
+ * it stays a creation option — capability
23
+ * confirmed, wiring deferred)
24
+ * · effort → restart (the Query interface has NO setEffort —
25
+ * effort is a query() creation option only,
26
+ * so a change is inherently NO-GO for
27
+ * runtime injection and falls back to a
28
+ * generator restart)
29
+ *
30
+ * This is a PURE classifier (no SDK / socket dependency) so the "runtime inject
31
+ * vs fallback" branch (AC3) is unit-testable without the pre-red websocket
32
+ * harness. It is the SEAM the Story 38.5 residency path consumes: when a
33
+ * retained live query exists across turns, a 'runtime-inject' param is applied
34
+ * via its setter while a 'restart' param forces `createPersistentQuery`. Under
35
+ * the current instance-per-turn production (Story 38.1), every send already
36
+ * recreates the query, so 'restart' is the status quo and the turn-between
37
+ * benefit of 'runtime-inject' only becomes observable once 38.5 keeps the
38
+ * process resident — 38.4 leaves the seam, it does not implement residency.
39
+ */
40
+ /**
41
+ * How a mid-session change to `param` reaches the engine: injected into the live
42
+ * query via its runtime setter, or applied by restarting the generator (new
43
+ * query, session preserved). See the module doc for the probe verdict backing
44
+ * each mapping.
45
+ */
46
+ export function runtimeTransitionFor(param) {
47
+ switch (param) {
48
+ case 'model':
49
+ case 'permissionMode':
50
+ case 'thinking':
51
+ return 'runtime-inject';
52
+ case 'effort':
53
+ return 'restart';
54
+ }
55
+ }
56
+ /** True when `param` can be applied to a live query without a generator restart. */
57
+ export function canRuntimeInject(param) {
58
+ return runtimeTransitionFor(param) === 'runtime-inject';
59
+ }
60
+ //# sourceMappingURL=runtimeParamStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeParamStrategy.js","sourceRoot":"","sources":["../../src/utils/runtimeParamStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAMH;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,gBAAgB,CAAC;QACtB,KAAK,UAAU;YACb,OAAO,gBAAgB,CAAC;QAC1B,KAAK,QAAQ;YACX,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,KAAmB;IAClD,OAAO,oBAAoB,CAAC,KAAK,CAAC,KAAK,gBAAgB,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Story 38.3: pure decision helpers for the Stop button (`chat:abort`) split and
3
+ * the user-interrupt suppression predicates.
4
+ *
5
+ * These were extracted from the websocket handler so the orchestration decisions
6
+ * — how a running stream is stopped, and how a turn-only interrupt's error result
7
+ * is treated — can be unit-tested WITHOUT a live socket harness. The socket-level
8
+ * suite (websocket.test.ts chat:send path) is blocked by a pre-existing environmental
9
+ * red (QA gate 38.3 TEST-COV-1), so adding socket tests would introduce new red.
10
+ * Keeping the branch logic pure gives AC1 (abort/interrupt split) and the interrupt
11
+ * suppression branches a deterministic regression guard that does not touch sockets.
12
+ */
13
+ /** How the Stop button stops a running stream. */
14
+ export type StopStrategy = 'interrupt' | 'abort';
15
+ /**
16
+ * Decide how the Stop button stops a running stream (Story 38.3 AC1).
17
+ *
18
+ * - SDK engine that exposes `interrupt()` → `'interrupt'`: turn-only, the persistent
19
+ * query's generator/session stays alive so the conversation can continue.
20
+ * - CLI engine (PTY Ctrl+C runs off the abortController), OR an SDK engine not yet
21
+ * created (boot window, no interrupt capability) → `'abort'`: hard abort.
22
+ *
23
+ * Faithful to the inline guard `engineMode !== 'cli' && svc?.interrupt`.
24
+ */
25
+ export declare function resolveStopStrategy(params: {
26
+ engineMode: string | undefined;
27
+ canInterrupt: boolean;
28
+ }): StopStrategy;
29
+ /**
30
+ * A turn-only interrupt ends the turn with an `error_during_execution` result. That
31
+ * is the intended Stop, not a failure — its error emission is suppressed (viewers
32
+ * already received `stream:detached`), so no spurious error card appears.
33
+ */
34
+ export declare function shouldSuppressResultError(userInterrupted: boolean): boolean;
35
+ /**
36
+ * An interrupted turn still fires `onComplete` to finalize partial output, but it is
37
+ * a Stop rather than a natural finish — skip the "완료" completion notification.
38
+ * `hasNotifiableViewers` folds in `notificationService.shouldNotify(sockets.size)`.
39
+ */
40
+ export declare function shouldNotifyComplete(userInterrupted: boolean, hasNotifiableViewers: boolean): boolean;
41
+ /**
42
+ * Whether an error result while resuming should be converted to a thrown error and
43
+ * retried. A user interrupt ALSO ends the turn with an error result, so it must not
44
+ * be mistaken for a resume failure and retried — the user asked to stop.
45
+ *
46
+ * Faithful to `isError && isResumeAttempt && !aborted && !userInterrupted && !hasEmittedOutput`.
47
+ */
48
+ export declare function shouldRetryResumeAfterError(params: {
49
+ isError: boolean;
50
+ isResumeAttempt: boolean;
51
+ aborted: boolean;
52
+ userInterrupted: boolean;
53
+ hasEmittedOutput: boolean;
54
+ }): boolean;
55
+ /**
56
+ * Whether the turn should hold, waiting on in-flight background tasks, at the end of
57
+ * the main generation. A user interrupt means "stop now" — do NOT hold. (The hard-abort
58
+ * path short-circuits via its aborted signal; the interrupt path leaves the signal unset,
59
+ * so this gates on the flag explicitly.)
60
+ */
61
+ export declare function shouldWaitForBackgroundTasks(userInterrupted: boolean, backgroundIsWaiting: boolean): boolean;
62
+ //# sourceMappingURL=stopStrategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stopStrategy.d.ts","sourceRoot":"","sources":["../../src/utils/stopStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,kDAAkD;AAClD,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;AAEjD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,OAAO,CAAC;CACvB,GAAG,YAAY,CAEf;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,eAAe,EAAE,OAAO,GAAG,OAAO,CAE3E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,GAAG,OAAO,CAErG;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,GAAG,OAAO,CAQV;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,eAAe,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO,GAAG,OAAO,CAE5G"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Story 38.3: pure decision helpers for the Stop button (`chat:abort`) split and
3
+ * the user-interrupt suppression predicates.
4
+ *
5
+ * These were extracted from the websocket handler so the orchestration decisions
6
+ * — how a running stream is stopped, and how a turn-only interrupt's error result
7
+ * is treated — can be unit-tested WITHOUT a live socket harness. The socket-level
8
+ * suite (websocket.test.ts chat:send path) is blocked by a pre-existing environmental
9
+ * red (QA gate 38.3 TEST-COV-1), so adding socket tests would introduce new red.
10
+ * Keeping the branch logic pure gives AC1 (abort/interrupt split) and the interrupt
11
+ * suppression branches a deterministic regression guard that does not touch sockets.
12
+ */
13
+ /**
14
+ * Decide how the Stop button stops a running stream (Story 38.3 AC1).
15
+ *
16
+ * - SDK engine that exposes `interrupt()` → `'interrupt'`: turn-only, the persistent
17
+ * query's generator/session stays alive so the conversation can continue.
18
+ * - CLI engine (PTY Ctrl+C runs off the abortController), OR an SDK engine not yet
19
+ * created (boot window, no interrupt capability) → `'abort'`: hard abort.
20
+ *
21
+ * Faithful to the inline guard `engineMode !== 'cli' && svc?.interrupt`.
22
+ */
23
+ export function resolveStopStrategy(params) {
24
+ return params.engineMode !== 'cli' && params.canInterrupt ? 'interrupt' : 'abort';
25
+ }
26
+ /**
27
+ * A turn-only interrupt ends the turn with an `error_during_execution` result. That
28
+ * is the intended Stop, not a failure — its error emission is suppressed (viewers
29
+ * already received `stream:detached`), so no spurious error card appears.
30
+ */
31
+ export function shouldSuppressResultError(userInterrupted) {
32
+ return userInterrupted;
33
+ }
34
+ /**
35
+ * An interrupted turn still fires `onComplete` to finalize partial output, but it is
36
+ * a Stop rather than a natural finish — skip the "완료" completion notification.
37
+ * `hasNotifiableViewers` folds in `notificationService.shouldNotify(sockets.size)`.
38
+ */
39
+ export function shouldNotifyComplete(userInterrupted, hasNotifiableViewers) {
40
+ return !userInterrupted && hasNotifiableViewers;
41
+ }
42
+ /**
43
+ * Whether an error result while resuming should be converted to a thrown error and
44
+ * retried. A user interrupt ALSO ends the turn with an error result, so it must not
45
+ * be mistaken for a resume failure and retried — the user asked to stop.
46
+ *
47
+ * Faithful to `isError && isResumeAttempt && !aborted && !userInterrupted && !hasEmittedOutput`.
48
+ */
49
+ export function shouldRetryResumeAfterError(params) {
50
+ return (params.isError &&
51
+ params.isResumeAttempt &&
52
+ !params.aborted &&
53
+ !params.userInterrupted &&
54
+ !params.hasEmittedOutput);
55
+ }
56
+ /**
57
+ * Whether the turn should hold, waiting on in-flight background tasks, at the end of
58
+ * the main generation. A user interrupt means "stop now" — do NOT hold. (The hard-abort
59
+ * path short-circuits via its aborted signal; the interrupt path leaves the signal unset,
60
+ * so this gates on the flag explicitly.)
61
+ */
62
+ export function shouldWaitForBackgroundTasks(userInterrupted, backgroundIsWaiting) {
63
+ return !userInterrupted && backgroundIsWaiting;
64
+ }
65
+ //# sourceMappingURL=stopStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stopStrategy.js","sourceRoot":"","sources":["../../src/utils/stopStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAGnC;IACC,OAAO,MAAM,CAAC,UAAU,KAAK,KAAK,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,eAAwB;IAChE,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,eAAwB,EAAE,oBAA6B;IAC1F,OAAO,CAAC,eAAe,IAAI,oBAAoB,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAM3C;IACC,OAAO,CACL,MAAM,CAAC,OAAO;QACd,MAAM,CAAC,eAAe;QACtB,CAAC,MAAM,CAAC,OAAO;QACf,CAAC,MAAM,CAAC,eAAe;QACvB,CAAC,MAAM,CAAC,gBAAgB,CACzB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAAC,eAAwB,EAAE,mBAA4B;IACjG,OAAO,CAAC,eAAe,IAAI,mBAAmB,CAAC;AACjD,CAAC"}
@@ -8,7 +8,7 @@ Read individual entries **on demand** when the user's request involves the under
8
8
 
9
9
  ## Entries
10
10
 
11
- - [Image Storage](./image-storage.md) — On-disk path and filename scheme for chat-attached images
11
+ - [Image Storage](./image-storage.md) — On-disk path and filename scheme for chat-attached images, and why chat-attached PDFs have no on-disk copy at all
12
12
  - [Harness File Layout](./harness-files.md) — Where `.claude/` items (skills, commands, agents, hooks, MCP servers, `CLAUDE.md`) live on disk, when changes take effect, the Context Builder's Hammoc-managed SessionStart files, plugin install-state paths, and how the Secret-on-Shared guard relates to direct file writes
13
13
  - [Observability Storage](./observability-storage.md) — Where the MCP call log and exact-token-count cache live, their JSONL/JSON format, body-stripping, and 30-day retention
14
14
  - [BMad QA-Fix Marker](./bmad-qa-fix-marker.md) — The `<!-- hammoc:qa-fix -->` story-file convention that drives QA re-review recommendations and the board's "QA Fixed" badge
@@ -2,6 +2,8 @@
2
2
 
3
3
  When the user attaches an image to a chat message, Hammoc saves it under **Claude Code's per-project session data**, not inside the project being worked on. The user does not see this — they just attach and send. This document is for an agent that needs to read or correlate the file directly.
4
4
 
5
+ > **PDF attachments do not use this mechanism** — chat-attached PDFs are never written to disk. See the "PDF attachments" section at the bottom.
6
+
5
7
  ## On-disk location
6
8
 
7
9
  ```
@@ -47,6 +49,16 @@ For an agent: in CLI mode an attached image arrives as a literal "use your Read
47
49
 
48
50
  ## Limits
49
51
 
50
- - 5 images per message
51
- - 10 MB per image
52
+ - 5 attachments per message — a **shared cap across images and PDFs combined**
53
+ - 10 MB per image; 20 MB combined raw size per message (all attachments together)
52
54
  - Total per-session storage is bounded only by disk; there is no automatic eviction other than session deletion.
55
+
56
+ ## PDF attachments (not stored on disk)
57
+
58
+ Chat-attached PDFs bypass this storage entirely:
59
+
60
+ - **No file is written** under `images/<sessionId>/` or anywhere else. The PDF travels to the model as an inline base64 `document` content block, and the stored chat message keeps only the original filename with an empty URL — there is no on-disk path an agent can Read.
61
+ - **SDK engine only.** The CLI engine rejects any message carrying a PDF before it is sent (server-side validation error), so no CLI `--add-dir` path-reference variant exists for PDFs.
62
+ - Per-PDF size cap is 20 MB, inside the shared 5-attachment / 20 MB-combined message limits above.
63
+
64
+ If a task needs the PDF bytes themselves, the agent cannot recover them from session storage — ask the user to place the file into the project tree instead.