hammoc 1.8.3 → 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.
- package/README.md +9 -6
- package/package.json +1 -1
- package/packages/client/dist/assets/MermaidViewer-B-LwclIl.js +7 -0
- package/packages/client/dist/assets/PdfViewer-C3Nymwn3.js +12 -0
- package/packages/client/dist/assets/PdfViewer-QCQEGvBO.css +1 -0
- package/packages/client/dist/assets/abnfDiagram-VRR7QNED-GfhY_hnV.js +1 -0
- package/packages/client/dist/assets/{agentExampleHighlight-B0EHAvjx.js → agentExampleHighlight-CyhVLy0_.js} +1 -1
- package/packages/client/dist/assets/arc-8nyjXGC3.js +1 -0
- package/packages/client/dist/assets/architectureDiagram-ZJ3FMSHR-B4onXLm4.js +36 -0
- package/packages/client/dist/assets/blockDiagram-677ZJIJ3-D0D6ncaN.js +132 -0
- package/packages/client/dist/assets/c4Diagram-LMCZKHZV-CE7Tip-_.js +10 -0
- package/packages/client/dist/assets/channel-CPjlbdOo.js +1 -0
- package/packages/client/dist/assets/chunk-2Q5K7J3B-4rW1da1Z.js +1 -0
- package/packages/client/dist/assets/chunk-32BRIVSS-JuzxS4QA.js +1 -0
- package/packages/client/dist/assets/chunk-5VM5RSS4-WRNf-xsi.js +15 -0
- package/packages/client/dist/assets/chunk-EX3LRPZG-BDWPlBKO.js +231 -0
- package/packages/client/dist/assets/chunk-JWPE2WC7-B8nCyQAc.js +1 -0
- package/packages/client/dist/assets/chunk-MOJQB5TN-7c-yriHe.js +88 -0
- package/packages/client/dist/assets/chunk-RYQCIY6F-LTDywRs7.js +1 -0
- package/packages/client/dist/assets/chunk-V7JOEXUC-DjVgQpjl.js +206 -0
- package/packages/client/dist/assets/chunk-VR4S4FIN-7jDdkUpx.js +1 -0
- package/packages/client/dist/assets/chunk-XXDRQBXY-ENKV2O8a.js +1 -0
- package/packages/client/dist/assets/classDiagram-OUVF2IWQ-C0PbC7Pi.js +1 -0
- package/packages/client/dist/assets/classDiagram-v2-EOCWNBFH-C0PbC7Pi.js +1 -0
- package/packages/client/dist/assets/{commandTokenHighlight-BFQeQmwm.js → commandTokenHighlight-DGP0RKx0.js} +1 -1
- package/packages/client/dist/assets/cose-bilkent-JH36ORCC-CQTNdQ-g.js +1 -0
- package/packages/client/dist/assets/cynefin-VYW2F7L2-BYlAJRcO.js +178 -0
- package/packages/client/dist/assets/cynefinDiagram-TSTJHNR4-7zyUTflT.js +62 -0
- package/packages/client/dist/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
- package/packages/client/dist/assets/dagre-VKFMJZFB-DyZir42W.js +4 -0
- package/packages/client/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/packages/client/dist/assets/diagram-FQU43EPY-CmNAU8Nw.js +3 -0
- package/packages/client/dist/assets/diagram-G47NLZAW-B0EIk03o.js +24 -0
- package/packages/client/dist/assets/diagram-NH7WQ7WH-Dulv0f1N.js +24 -0
- package/packages/client/dist/assets/diagram-OA4YK3LP-DEEcMLgi.js +30 -0
- package/packages/client/dist/assets/diagram-WEI45ONY-ZYebAZ93.js +41 -0
- package/packages/client/dist/assets/ebnfDiagram-CCIWWBDH-DCHpt3qn.js +1 -0
- package/packages/client/dist/assets/erDiagram-Q63AITRT-B6T87-48.js +85 -0
- package/packages/client/dist/assets/flowDiagram-23GEKE2U-DAcW6mKJ.js +156 -0
- package/packages/client/dist/assets/ganttDiagram-NO4QXBWP-Ct_eFFcM.js +292 -0
- package/packages/client/dist/assets/gitGraphDiagram-IHSO6WYX-BGi8Fdm2.js +106 -0
- package/packages/client/dist/assets/graph-C9eacEi8.js +1 -0
- package/packages/client/dist/assets/index-CtMiUbMJ.js +1671 -0
- package/packages/client/dist/assets/index-hTu6ZG2N.js +2 -0
- package/packages/client/dist/assets/index-oBa2uNlv.css +32 -0
- package/packages/client/dist/assets/infoDiagram-FWYZ7A6U-CqvwbXJn.js +2 -0
- package/packages/client/dist/assets/init-Gi6I4Gst.js +1 -0
- package/packages/client/dist/assets/ishikawaDiagram-FXEZZL3T-CHhr5ioQ.js +70 -0
- package/packages/client/dist/assets/journeyDiagram-5HDEW3XC-d20NzZAd.js +139 -0
- package/packages/client/dist/assets/kanban-definition-HUTT4EX6-CHx3wOer.js +89 -0
- package/packages/client/dist/assets/katex-C5jXJg4s.js +257 -0
- package/packages/client/dist/assets/layout-DEXfKzaS.js +1 -0
- package/packages/client/dist/assets/linear-Cnp1E9M4.js +1 -0
- package/packages/client/dist/assets/map-Czzmt4hB.js +1 -0
- package/packages/client/dist/assets/mermaid.core-jAhLhPYr.js +314 -0
- package/packages/client/dist/assets/mindmap-definition-LN4V7U3C-zi30RPoJ.js +96 -0
- package/packages/client/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/packages/client/dist/assets/pdf.worker.min-qwK7q_zL.mjs +28 -0
- package/packages/client/dist/assets/pegDiagram-2B236MQR-4gKLzTqu.js +1 -0
- package/packages/client/dist/assets/pieDiagram-ENE6RG2P-DRDhpjxP.js +39 -0
- package/packages/client/dist/assets/quadrantDiagram-ABIIQ3AL-FmTxRIlW.js +7 -0
- package/packages/client/dist/assets/railroadDiagram-RFXS5EU6-Bc-Q7iPs.js +1 -0
- package/packages/client/dist/assets/requirementDiagram-TGXJPOKE-D209tlqv.js +84 -0
- package/packages/client/dist/assets/sankeyDiagram-HTMAVEWB--I_lSbCV.js +40 -0
- package/packages/client/dist/assets/sequenceDiagram-DBY2YBRQ-Da5YJcDJ.js +162 -0
- package/packages/client/dist/assets/sizeCapture-X5ZJPWSS-DYuc-rzB.js +1 -0
- package/packages/client/dist/assets/{snippetTokenHighlight-DVju7SBc.js → snippetTokenHighlight-qXHxvlCE.js} +1 -1
- package/packages/client/dist/assets/stateDiagram-2N3HPSRC-DoOeZoRP.js +1 -0
- package/packages/client/dist/assets/stateDiagram-v2-6OUMAXLB-DLkUyWs6.js +1 -0
- package/packages/client/dist/assets/swimlanes-5IMT3BWC-BcR7uCTf.js +2 -0
- package/packages/client/dist/assets/swimlanesDiagram-G3AALYLV-C0FDo-hf.js +8 -0
- package/packages/client/dist/assets/timeline-definition-FHXFAJF6-CLzKR6_i.js +120 -0
- package/packages/client/dist/assets/vennDiagram-L72KCM5P-TajEacPR.js +34 -0
- package/packages/client/dist/assets/wardleyDiagram-EHGQE667-DKvVEb3V.js +78 -0
- package/packages/client/dist/assets/xychartDiagram-FW5EYKEG-CaJG8r0j.js +7 -0
- package/packages/client/dist/index.html +2 -2
- package/packages/client/dist/sw.js +2 -2
- package/packages/server/dist/controllers/accountController.d.ts.map +1 -1
- package/packages/server/dist/controllers/accountController.js +31 -11
- package/packages/server/dist/controllers/accountController.js.map +1 -1
- package/packages/server/dist/controllers/fileSystemController.d.ts +6 -0
- package/packages/server/dist/controllers/fileSystemController.d.ts.map +1 -1
- package/packages/server/dist/controllers/fileSystemController.js +34 -0
- package/packages/server/dist/controllers/fileSystemController.js.map +1 -1
- package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
- package/packages/server/dist/controllers/projectController.js +4 -2
- package/packages/server/dist/controllers/projectController.js.map +1 -1
- package/packages/server/dist/controllers/sessionController.d.ts +5 -0
- package/packages/server/dist/controllers/sessionController.d.ts.map +1 -1
- package/packages/server/dist/controllers/sessionController.js +95 -3
- package/packages/server/dist/controllers/sessionController.js.map +1 -1
- package/packages/server/dist/handlers/hookBridge.d.ts +131 -0
- package/packages/server/dist/handlers/hookBridge.d.ts.map +1 -0
- package/packages/server/dist/handlers/hookBridge.js +178 -0
- package/packages/server/dist/handlers/hookBridge.js.map +1 -0
- package/packages/server/dist/handlers/streamCallbacks.d.ts.map +1 -1
- package/packages/server/dist/handlers/streamCallbacks.js +30 -4
- package/packages/server/dist/handlers/streamCallbacks.js.map +1 -1
- package/packages/server/dist/handlers/websocket.d.ts +44 -2
- package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
- package/packages/server/dist/handlers/websocket.js +666 -64
- package/packages/server/dist/handlers/websocket.js.map +1 -1
- package/packages/server/dist/index.js +4 -1
- package/packages/server/dist/index.js.map +1 -1
- package/packages/server/dist/locales/en/server.json +9 -3
- package/packages/server/dist/locales/es/server.json +9 -3
- package/packages/server/dist/locales/ja/server.json +9 -3
- package/packages/server/dist/locales/ko/server.json +9 -3
- package/packages/server/dist/locales/pt/server.json +9 -3
- package/packages/server/dist/locales/zh-CN/server.json +9 -3
- package/packages/server/dist/routes/account.d.ts.map +1 -1
- package/packages/server/dist/routes/account.js +4 -2
- package/packages/server/dist/routes/account.js.map +1 -1
- package/packages/server/dist/routes/fileSystem.d.ts.map +1 -1
- package/packages/server/dist/routes/fileSystem.js +2 -0
- package/packages/server/dist/routes/fileSystem.js.map +1 -1
- package/packages/server/dist/routes/sessions.d.ts.map +1 -1
- package/packages/server/dist/routes/sessions.js +2 -0
- package/packages/server/dist/routes/sessions.js.map +1 -1
- package/packages/server/dist/services/accountInfoService.d.ts +36 -1
- package/packages/server/dist/services/accountInfoService.d.ts.map +1 -1
- package/packages/server/dist/services/accountInfoService.js +90 -4
- package/packages/server/dist/services/accountInfoService.js.map +1 -1
- package/packages/server/dist/services/accountStorageService.d.ts +55 -9
- package/packages/server/dist/services/accountStorageService.d.ts.map +1 -1
- package/packages/server/dist/services/accountStorageService.js +244 -12
- package/packages/server/dist/services/accountStorageService.js.map +1 -1
- package/packages/server/dist/services/chatEngine.d.ts +59 -3
- package/packages/server/dist/services/chatEngine.d.ts.map +1 -1
- package/packages/server/dist/services/chatService.d.ts +112 -3
- package/packages/server/dist/services/chatService.d.ts.map +1 -1
- package/packages/server/dist/services/chatService.js +353 -98
- package/packages/server/dist/services/chatService.js.map +1 -1
- package/packages/server/dist/services/claudeLoginService.d.ts +14 -3
- package/packages/server/dist/services/claudeLoginService.d.ts.map +1 -1
- package/packages/server/dist/services/claudeLoginService.js +18 -4
- package/packages/server/dist/services/claudeLoginService.js.map +1 -1
- package/packages/server/dist/services/cliChatEngine.d.ts.map +1 -1
- package/packages/server/dist/services/cliChatEngine.js +124 -22
- package/packages/server/dist/services/cliChatEngine.js.map +1 -1
- package/packages/server/dist/services/cliModalDetect.d.ts +14 -0
- package/packages/server/dist/services/cliModalDetect.d.ts.map +1 -1
- package/packages/server/dist/services/cliModalDetect.js +12 -3
- package/packages/server/dist/services/cliModalDetect.js.map +1 -1
- package/packages/server/dist/services/cliSessionPool.js +1 -1
- package/packages/server/dist/services/cliSessionPool.js.map +1 -1
- package/packages/server/dist/services/commandService.d.ts.map +1 -1
- package/packages/server/dist/services/commandService.js +19 -9
- package/packages/server/dist/services/commandService.js.map +1 -1
- package/packages/server/dist/services/engineResidencyRegistry.d.ts +112 -0
- package/packages/server/dist/services/engineResidencyRegistry.d.ts.map +1 -0
- package/packages/server/dist/services/engineResidencyRegistry.js +195 -0
- package/packages/server/dist/services/engineResidencyRegistry.js.map +1 -0
- package/packages/server/dist/services/harnessAgentService.d.ts.map +1 -1
- package/packages/server/dist/services/harnessAgentService.js +12 -2
- package/packages/server/dist/services/harnessAgentService.js.map +1 -1
- package/packages/server/dist/services/harnessLintService.d.ts.map +1 -1
- package/packages/server/dist/services/harnessLintService.js +25 -114
- package/packages/server/dist/services/harnessLintService.js.map +1 -1
- package/packages/server/dist/services/historyParser.d.ts.map +1 -1
- package/packages/server/dist/services/historyParser.js +23 -6
- package/packages/server/dist/services/historyParser.js.map +1 -1
- package/packages/server/dist/services/imageStorageService.d.ts.map +1 -1
- package/packages/server/dist/services/imageStorageService.js +7 -0
- package/packages/server/dist/services/imageStorageService.js.map +1 -1
- package/packages/server/dist/services/projectService.d.ts +4 -1
- package/packages/server/dist/services/projectService.d.ts.map +1 -1
- package/packages/server/dist/services/projectService.js +14 -1
- package/packages/server/dist/services/projectService.js.map +1 -1
- package/packages/server/dist/services/promptQueue.d.ts +34 -0
- package/packages/server/dist/services/promptQueue.d.ts.map +1 -0
- package/packages/server/dist/services/promptQueue.js +80 -0
- package/packages/server/dist/services/promptQueue.js.map +1 -0
- package/packages/server/dist/services/ptyService.d.ts.map +1 -1
- package/packages/server/dist/services/ptyService.js +4 -1
- package/packages/server/dist/services/ptyService.js.map +1 -1
- package/packages/server/dist/services/queueService.d.ts.map +1 -1
- package/packages/server/dist/services/queueService.js +62 -4
- package/packages/server/dist/services/queueService.js.map +1 -1
- package/packages/server/dist/services/rateLimitProbeService.d.ts +73 -3
- package/packages/server/dist/services/rateLimitProbeService.d.ts.map +1 -1
- package/packages/server/dist/services/rateLimitProbeService.js +397 -30
- package/packages/server/dist/services/rateLimitProbeService.js.map +1 -1
- package/packages/server/dist/services/sessionBufferManager.d.ts.map +1 -1
- package/packages/server/dist/services/sessionBufferManager.js +22 -4
- package/packages/server/dist/services/sessionBufferManager.js.map +1 -1
- package/packages/server/dist/services/sessionExportService.d.ts +34 -0
- package/packages/server/dist/services/sessionExportService.d.ts.map +1 -0
- package/packages/server/dist/services/sessionExportService.js +269 -0
- package/packages/server/dist/services/sessionExportService.js.map +1 -0
- package/packages/server/dist/services/sessionService.d.ts +6 -0
- package/packages/server/dist/services/sessionService.d.ts.map +1 -1
- package/packages/server/dist/services/sessionService.js +30 -9
- package/packages/server/dist/services/sessionService.js.map +1 -1
- package/packages/server/dist/services/streamHandler.d.ts +177 -1
- package/packages/server/dist/services/streamHandler.d.ts.map +1 -1
- package/packages/server/dist/services/streamHandler.js +425 -39
- package/packages/server/dist/services/streamHandler.js.map +1 -1
- package/packages/server/dist/services/workspaceContext.d.ts.map +1 -1
- package/packages/server/dist/services/workspaceContext.js +6 -0
- package/packages/server/dist/services/workspaceContext.js.map +1 -1
- package/packages/server/dist/snippets/fix-lint-command-not-on-path +13 -0
- package/packages/server/dist/snippets/fix-lint-duplicate-name +12 -0
- package/packages/server/dist/snippets/fix-lint-env-var-undefined +13 -0
- package/packages/server/dist/snippets/fix-lint-regex-invalid +14 -0
- package/packages/server/dist/snippets/fix-lint-tools-non-standard +14 -0
- package/packages/server/dist/snippets/fix-lint-url-invalid +13 -0
- package/packages/server/dist/snippets/fix-lint-yaml-parse-error +14 -0
- package/packages/server/dist/snippets/fix-malformed-skill +17 -0
- package/packages/server/dist/utils/backgroundHookSettings.d.ts +31 -0
- package/packages/server/dist/utils/backgroundHookSettings.d.ts.map +1 -0
- package/packages/server/dist/utils/backgroundHookSettings.js +41 -0
- package/packages/server/dist/utils/backgroundHookSettings.js.map +1 -0
- package/packages/server/dist/utils/backgroundTaskPoller.d.ts +69 -0
- package/packages/server/dist/utils/backgroundTaskPoller.d.ts.map +1 -0
- package/packages/server/dist/utils/backgroundTaskPoller.js +356 -0
- package/packages/server/dist/utils/backgroundTaskPoller.js.map +1 -0
- package/packages/server/dist/utils/backgroundTaskTracker.d.ts +51 -3
- package/packages/server/dist/utils/backgroundTaskTracker.d.ts.map +1 -1
- package/packages/server/dist/utils/backgroundTaskTracker.js +117 -9
- package/packages/server/dist/utils/backgroundTaskTracker.js.map +1 -1
- package/packages/server/dist/utils/backgroundToolGuard.d.ts +26 -0
- package/packages/server/dist/utils/backgroundToolGuard.d.ts.map +1 -0
- package/packages/server/dist/utils/backgroundToolGuard.js +40 -0
- package/packages/server/dist/utils/backgroundToolGuard.js.map +1 -0
- package/packages/server/dist/utils/boundedRing.d.ts +44 -0
- package/packages/server/dist/utils/boundedRing.d.ts.map +1 -0
- package/packages/server/dist/utils/boundedRing.js +77 -0
- package/packages/server/dist/utils/boundedRing.js.map +1 -0
- package/packages/server/dist/utils/effortUtils.d.ts +7 -6
- package/packages/server/dist/utils/effortUtils.d.ts.map +1 -1
- package/packages/server/dist/utils/effortUtils.js +10 -9
- package/packages/server/dist/utils/effortUtils.js.map +1 -1
- package/packages/server/dist/utils/messageTree.d.ts +25 -0
- package/packages/server/dist/utils/messageTree.d.ts.map +1 -1
- package/packages/server/dist/utils/messageTree.js +167 -3
- package/packages/server/dist/utils/messageTree.js.map +1 -1
- package/packages/server/dist/utils/pathUtils.d.ts.map +1 -1
- package/packages/server/dist/utils/pathUtils.js +1 -0
- package/packages/server/dist/utils/pathUtils.js.map +1 -1
- package/packages/server/dist/utils/residencyActivation.d.ts +61 -0
- package/packages/server/dist/utils/residencyActivation.d.ts.map +1 -0
- package/packages/server/dist/utils/residencyActivation.js +60 -0
- package/packages/server/dist/utils/residencyActivation.js.map +1 -0
- package/packages/server/dist/utils/residencyPolicy.d.ts +62 -0
- package/packages/server/dist/utils/residencyPolicy.d.ts.map +1 -0
- package/packages/server/dist/utils/residencyPolicy.js +82 -0
- package/packages/server/dist/utils/residencyPolicy.js.map +1 -0
- package/packages/server/dist/utils/runtimeParamStrategy.d.ts +51 -0
- package/packages/server/dist/utils/runtimeParamStrategy.d.ts.map +1 -0
- package/packages/server/dist/utils/runtimeParamStrategy.js +60 -0
- package/packages/server/dist/utils/runtimeParamStrategy.js.map +1 -0
- package/packages/server/dist/utils/stopStrategy.d.ts +62 -0
- package/packages/server/dist/utils/stopStrategy.d.ts.map +1 -0
- package/packages/server/dist/utils/stopStrategy.js +65 -0
- package/packages/server/dist/utils/stopStrategy.js.map +1 -0
- package/packages/server/resources/internals/INDEX.md +1 -1
- package/packages/server/resources/internals/image-storage.md +14 -2
- package/packages/server/resources/manual/02-chat.md +30 -13
- package/packages/server/resources/manual/03-sessions.md +22 -1
- package/packages/server/resources/manual/04-slash-commands-favorites.md +2 -1
- package/packages/server/resources/manual/05-projects.md +1 -0
- package/packages/server/resources/manual/06-file-explorer-editor.md +21 -2
- package/packages/server/resources/manual/07-git.md +1 -1
- package/packages/server/resources/manual/08-terminal.md +1 -1
- package/packages/server/resources/manual/12-harness-workbench.md +21 -16
- package/packages/server/resources/manual/13-settings.md +22 -6
- package/packages/server/resources/manual/14-keyboard-shortcuts.md +8 -1
- package/packages/shared/dist/constants/messageTree.d.ts +2 -0
- package/packages/shared/dist/constants/messageTree.d.ts.map +1 -1
- package/packages/shared/dist/constants/messageTree.js +6 -0
- package/packages/shared/dist/constants/messageTree.js.map +1 -1
- package/packages/shared/dist/index.d.ts +1 -1
- package/packages/shared/dist/index.d.ts.map +1 -1
- package/packages/shared/dist/index.js.map +1 -1
- package/packages/shared/dist/types/accounts.d.ts +13 -0
- package/packages/shared/dist/types/accounts.d.ts.map +1 -1
- package/packages/shared/dist/types/history.d.ts +14 -1
- package/packages/shared/dist/types/history.d.ts.map +1 -1
- package/packages/shared/dist/types/message.d.ts +26 -3
- package/packages/shared/dist/types/message.d.ts.map +1 -1
- package/packages/shared/dist/types/message.js +21 -1
- package/packages/shared/dist/types/message.js.map +1 -1
- package/packages/shared/dist/types/preferences.d.ts +5 -1
- package/packages/shared/dist/types/preferences.d.ts.map +1 -1
- package/packages/shared/dist/types/preferences.js +3 -0
- package/packages/shared/dist/types/preferences.js.map +1 -1
- package/packages/shared/dist/types/sdk.d.ts +68 -10
- package/packages/shared/dist/types/sdk.d.ts.map +1 -1
- package/packages/shared/dist/types/sdk.js +56 -16
- package/packages/shared/dist/types/sdk.js.map +1 -1
- package/packages/shared/dist/types/session.d.ts +6 -0
- package/packages/shared/dist/types/session.d.ts.map +1 -1
- package/packages/shared/dist/types/session.js +6 -0
- package/packages/shared/dist/types/session.js.map +1 -1
- package/packages/shared/dist/types/streaming.d.ts +32 -2
- package/packages/shared/dist/types/streaming.d.ts.map +1 -1
- package/packages/shared/dist/types/streaming.js.map +1 -1
- package/packages/shared/dist/types/websocket.d.ts +4 -0
- package/packages/shared/dist/types/websocket.d.ts.map +1 -1
- package/scripts/probe-persistent-hooks.mjs +795 -0
- package/packages/client/dist/assets/index-BX7y_Qt3.css +0 -32
- package/packages/client/dist/assets/index-Bav7AMMy.js +0 -2
- package/packages/client/dist/assets/index-DgRQoBIg.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
|
|
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.
|