@svenroth-ai/shipwright 0.24.1-next.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/LICENSE +21 -0
- package/README.md +55 -0
- package/bin/shipwright.mjs +187 -0
- package/client/dist/assets/EmbeddedTerminal-6GBZ9nXN.css +32 -0
- package/client/dist/assets/EmbeddedTerminal-CpeDvtzx.js +123 -0
- package/client/dist/assets/MarkdownEditorModal-Di9HN39z.js +212 -0
- package/client/dist/assets/abnfDiagram-VRR7QNED-BbSdq0X7.js +1 -0
- package/client/dist/assets/arc-BZT7vKjm.js +1 -0
- package/client/dist/assets/architectureDiagram-ZJ3FMSHR-BuRwoNgV.js +36 -0
- package/client/dist/assets/blockDiagram-677ZJIJ3-B2RNYWcr.js +132 -0
- package/client/dist/assets/c4Diagram-LMCZKHZV-fbXlt08K.js +10 -0
- package/client/dist/assets/channel-2-wkFEKK.js +1 -0
- package/client/dist/assets/chunk-2Q5K7J3B-NqMRUe5-.js +1 -0
- package/client/dist/assets/chunk-32BRIVSS-CIr9bFCd.js +1 -0
- package/client/dist/assets/chunk-5VM5RSS4-l0I6rbZN.js +15 -0
- package/client/dist/assets/chunk-EX3LRPZG-DXinoCxp.js +231 -0
- package/client/dist/assets/chunk-JWPE2WC7-BAcCano9.js +1 -0
- package/client/dist/assets/chunk-MOJQB5TN-DMVOqL7C.js +88 -0
- package/client/dist/assets/chunk-RYQCIY6F-CtNSAItH.js +1 -0
- package/client/dist/assets/chunk-V7JOEXUC-CufSmC93.js +206 -0
- package/client/dist/assets/chunk-VR4S4FIN-_BbUF9fa.js +1 -0
- package/client/dist/assets/chunk-XXDRQBXY-uQ35hUPB.js +1 -0
- package/client/dist/assets/classDiagram-OUVF2IWQ-A9SpDakb.js +1 -0
- package/client/dist/assets/classDiagram-v2-EOCWNBFH-A9SpDakb.js +1 -0
- package/client/dist/assets/cose-bilkent-JH36ORCC-B9q9uYCX.js +1 -0
- package/client/dist/assets/cynefin-VYW2F7L2-C2_bJyZA.js +178 -0
- package/client/dist/assets/cynefinDiagram-TSTJHNR4-0mzVmPv4.js +62 -0
- package/client/dist/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
- package/client/dist/assets/dagre-VKFMJZFB-CIE9HMtr.js +4 -0
- package/client/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/client/dist/assets/diagram-FQU43EPY-DdkBaZrX.js +3 -0
- package/client/dist/assets/diagram-G47NLZAW-Cql4k2sg.js +24 -0
- package/client/dist/assets/diagram-NH7WQ7WH-FRAUyqEu.js +24 -0
- package/client/dist/assets/diagram-OA4YK3LP-DXo8GTB8.js +30 -0
- package/client/dist/assets/diagram-WEI45ONY-DqVtWGdt.js +41 -0
- package/client/dist/assets/ebnfDiagram-CCIWWBDH-B0La0RsO.js +1 -0
- package/client/dist/assets/erDiagram-Q63AITRT-DHbbqiY9.js +85 -0
- package/client/dist/assets/flowDiagram-23GEKE2U-I6vhJlCk.js +156 -0
- package/client/dist/assets/ganttDiagram-NO4QXBWP-CDYZHcsU.js +292 -0
- package/client/dist/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
- package/client/dist/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
- package/client/dist/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
- package/client/dist/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
- package/client/dist/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
- package/client/dist/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
- package/client/dist/assets/gitGraphDiagram-IHSO6WYX-dyf7caP8.js +106 -0
- package/client/dist/assets/graph-C9eacEi8.js +1 -0
- package/client/dist/assets/index-BYrVLOXw.js +609 -0
- package/client/dist/assets/index-BZvZ9izZ.css +1 -0
- package/client/dist/assets/infoDiagram-FWYZ7A6U-BlWH6vM3.js +2 -0
- package/client/dist/assets/init-Gi6I4Gst.js +1 -0
- package/client/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/client/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/client/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/client/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/client/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/client/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/client/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/client/dist/assets/ishikawaDiagram-FXEZZL3T-BEKH-_6E.js +70 -0
- package/client/dist/assets/journeyDiagram-5HDEW3XC-cn_mQ2U9.js +139 -0
- package/client/dist/assets/kanban-definition-HUTT4EX6-DGZiYhYV.js +89 -0
- package/client/dist/assets/katex-DkKDou_j.js +257 -0
- package/client/dist/assets/layout-DEXfKzaS.js +1 -0
- package/client/dist/assets/linear-CdtYl6qR.js +1 -0
- package/client/dist/assets/map-Czzmt4hB.js +1 -0
- package/client/dist/assets/mermaid.core-CrKi1642.js +314 -0
- package/client/dist/assets/mindmap-definition-LN4V7U3C-G_CA7V9L.js +96 -0
- package/client/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/client/dist/assets/pegDiagram-2B236MQR-6ZeO9kDM.js +1 -0
- package/client/dist/assets/pieDiagram-ENE6RG2P-Bd0InZSQ.js +39 -0
- package/client/dist/assets/quadrantDiagram-ABIIQ3AL-Bg1m9Csn.js +7 -0
- package/client/dist/assets/railroadDiagram-RFXS5EU6-CCzPP1JU.js +1 -0
- package/client/dist/assets/requirementDiagram-TGXJPOKE-iN7kyPgM.js +84 -0
- package/client/dist/assets/sankeyDiagram-HTMAVEWB-DN1v_DUJ.js +40 -0
- package/client/dist/assets/sequenceDiagram-DBY2YBRQ-C5LMp5Sa.js +162 -0
- package/client/dist/assets/sizeCapture-X5ZJPWSS-Bp9jVLhL.js +1 -0
- package/client/dist/assets/stateDiagram-2N3HPSRC-VbWBetTf.js +1 -0
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-BuaUtNAG.js +1 -0
- package/client/dist/assets/swimlanes-5IMT3BWC-CrXokvQ-.js +2 -0
- package/client/dist/assets/swimlanesDiagram-G3AALYLV-C6hwc993.js +8 -0
- package/client/dist/assets/timeline-definition-FHXFAJF6-Cxx1NT9A.js +120 -0
- package/client/dist/assets/vennDiagram-L72KCM5P-C6DBgaEy.js +34 -0
- package/client/dist/assets/wardleyDiagram-EHGQE667-RV7xMg8g.js +78 -0
- package/client/dist/assets/xychartDiagram-FW5EYKEG-Llhe9pB1.js +7 -0
- package/client/dist/backdrops/deck-golden.jpg +0 -0
- package/client/dist/backdrops/lighthouse.jpg +0 -0
- package/client/dist/index.html +27 -0
- package/client/dist/shipwright-logo-white.png +0 -0
- package/lib/cache-runtime.mjs +72 -0
- package/lib/cache-sync.mjs +243 -0
- package/lib/claude-cli.mjs +173 -0
- package/lib/plugins.mjs +181 -0
- package/lib/preflight.mjs +208 -0
- package/lib/probes.mjs +53 -0
- package/lib/server.mjs +315 -0
- package/lib/util.mjs +83 -0
- package/lib/version-check.mjs +76 -0
- package/lib/win32-spawn.mjs +253 -0
- package/package.json +67 -0
- package/scripts/deploy-procs.mjs +208 -0
- package/scripts/deploy-swap.mjs +276 -0
- package/scripts/kill-targets.js +140 -0
- package/scripts/repair-claude-json.mjs +295 -0
- package/server/dist/config/default-actions.json +90 -0
- package/server/dist/config.d.ts +96 -0
- package/server/dist/config.d.ts.map +1 -0
- package/server/dist/config.js +68 -0
- package/server/dist/config.js.map +1 -0
- package/server/dist/core/actions-schema-validator.d.ts +8 -0
- package/server/dist/core/actions-schema-validator.d.ts.map +1 -0
- package/server/dist/core/actions-schema-validator.js +394 -0
- package/server/dist/core/actions-schema-validator.js.map +1 -0
- package/server/dist/core/actions-substitute.d.ts +115 -0
- package/server/dist/core/actions-substitute.d.ts.map +1 -0
- package/server/dist/core/actions-substitute.js +549 -0
- package/server/dist/core/actions-substitute.js.map +1 -0
- package/server/dist/core/board-column.d.ts +3 -0
- package/server/dist/core/board-column.d.ts.map +1 -0
- package/server/dist/core/board-column.js +4 -0
- package/server/dist/core/board-column.js.map +1 -0
- package/server/dist/core/campaign-events.d.ts +39 -0
- package/server/dist/core/campaign-events.d.ts.map +1 -0
- package/server/dist/core/campaign-events.js +192 -0
- package/server/dist/core/campaign-events.js.map +1 -0
- package/server/dist/core/campaign-loop-state.d.ts +31 -0
- package/server/dist/core/campaign-loop-state.d.ts.map +1 -0
- package/server/dist/core/campaign-loop-state.js +124 -0
- package/server/dist/core/campaign-loop-state.js.map +1 -0
- package/server/dist/core/campaign-parse.d.ts +28 -0
- package/server/dist/core/campaign-parse.d.ts.map +1 -0
- package/server/dist/core/campaign-parse.js +174 -0
- package/server/dist/core/campaign-parse.js.map +1 -0
- package/server/dist/core/campaign-paths.d.ts +24 -0
- package/server/dist/core/campaign-paths.d.ts.map +1 -0
- package/server/dist/core/campaign-paths.js +111 -0
- package/server/dist/core/campaign-paths.js.map +1 -0
- package/server/dist/core/campaign-route-helpers.d.ts +19 -0
- package/server/dist/core/campaign-route-helpers.d.ts.map +1 -0
- package/server/dist/core/campaign-route-helpers.js +51 -0
- package/server/dist/core/campaign-route-helpers.js.map +1 -0
- package/server/dist/core/campaign-status-json.d.ts +55 -0
- package/server/dist/core/campaign-status-json.d.ts.map +1 -0
- package/server/dist/core/campaign-status-json.js +57 -0
- package/server/dist/core/campaign-status-json.js.map +1 -0
- package/server/dist/core/campaign-store.d.ts +10 -0
- package/server/dist/core/campaign-store.d.ts.map +1 -0
- package/server/dist/core/campaign-store.js +245 -0
- package/server/dist/core/campaign-store.js.map +1 -0
- package/server/dist/core/campaign-types.d.ts +101 -0
- package/server/dist/core/campaign-types.d.ts.map +1 -0
- package/server/dist/core/campaign-types.js +10 -0
- package/server/dist/core/campaign-types.js.map +1 -0
- package/server/dist/core/campaign-write.d.ts +13 -0
- package/server/dist/core/campaign-write.d.ts.map +1 -0
- package/server/dist/core/campaign-write.js +128 -0
- package/server/dist/core/campaign-write.js.map +1 -0
- package/server/dist/core/cascade-delete-project-tasks.d.ts +14 -0
- package/server/dist/core/cascade-delete-project-tasks.d.ts.map +1 -0
- package/server/dist/core/cascade-delete-project-tasks.js +68 -0
- package/server/dist/core/cascade-delete-project-tasks.js.map +1 -0
- package/server/dist/core/claude-bin-resolver.d.ts +62 -0
- package/server/dist/core/claude-bin-resolver.d.ts.map +1 -0
- package/server/dist/core/claude-bin-resolver.js +208 -0
- package/server/dist/core/claude-bin-resolver.js.map +1 -0
- package/server/dist/core/claude-theme-reader.d.ts +15 -0
- package/server/dist/core/claude-theme-reader.d.ts.map +1 -0
- package/server/dist/core/claude-theme-reader.js +52 -0
- package/server/dist/core/claude-theme-reader.js.map +1 -0
- package/server/dist/core/cli-compat.d.ts +43 -0
- package/server/dist/core/cli-compat.d.ts.map +1 -0
- package/server/dist/core/cli-compat.js +88 -0
- package/server/dist/core/cli-compat.js.map +1 -0
- package/server/dist/core/compliance-dimensions.d.ts +20 -0
- package/server/dist/core/compliance-dimensions.d.ts.map +1 -0
- package/server/dist/core/compliance-dimensions.js +114 -0
- package/server/dist/core/compliance-dimensions.js.map +1 -0
- package/server/dist/core/compliance-reader.d.ts +49 -0
- package/server/dist/core/compliance-reader.d.ts.map +1 -0
- package/server/dist/core/compliance-reader.js +147 -0
- package/server/dist/core/compliance-reader.js.map +1 -0
- package/server/dist/core/contract-version.d.ts +72 -0
- package/server/dist/core/contract-version.d.ts.map +1 -0
- package/server/dist/core/contract-version.js +114 -0
- package/server/dist/core/contract-version.js.map +1 -0
- package/server/dist/core/design-feedback.d.ts +29 -0
- package/server/dist/core/design-feedback.d.ts.map +1 -0
- package/server/dist/core/design-feedback.js +80 -0
- package/server/dist/core/design-feedback.js.map +1 -0
- package/server/dist/core/dismissed-campaigns-store.d.ts +45 -0
- package/server/dist/core/dismissed-campaigns-store.d.ts.map +1 -0
- package/server/dist/core/dismissed-campaigns-store.js +147 -0
- package/server/dist/core/dismissed-campaigns-store.js.map +1 -0
- package/server/dist/core/event-log-reader.d.ts +19 -0
- package/server/dist/core/event-log-reader.d.ts.map +1 -0
- package/server/dist/core/event-log-reader.js +209 -0
- package/server/dist/core/event-log-reader.js.map +1 -0
- package/server/dist/core/event-log-types.d.ts +99 -0
- package/server/dist/core/event-log-types.d.ts.map +1 -0
- package/server/dist/core/event-log-types.js +14 -0
- package/server/dist/core/event-log-types.js.map +1 -0
- package/server/dist/core/gitignore-cache.d.ts +16 -0
- package/server/dist/core/gitignore-cache.d.ts.map +1 -0
- package/server/dist/core/gitignore-cache.js +103 -0
- package/server/dist/core/gitignore-cache.js.map +1 -0
- package/server/dist/core/grade-runner.d.ts +63 -0
- package/server/dist/core/grade-runner.d.ts.map +1 -0
- package/server/dist/core/grade-runner.js +176 -0
- package/server/dist/core/grade-runner.js.map +1 -0
- package/server/dist/core/grade-target.d.ts +43 -0
- package/server/dist/core/grade-target.d.ts.map +1 -0
- package/server/dist/core/grade-target.js +162 -0
- package/server/dist/core/grade-target.js.map +1 -0
- package/server/dist/core/inbox-derive.d.ts +56 -0
- package/server/dist/core/inbox-derive.d.ts.map +1 -0
- package/server/dist/core/inbox-derive.js +211 -0
- package/server/dist/core/inbox-derive.js.map +1 -0
- package/server/dist/core/jsonl-decode.d.ts +28 -0
- package/server/dist/core/jsonl-decode.d.ts.map +1 -0
- package/server/dist/core/jsonl-decode.js +96 -0
- package/server/dist/core/jsonl-decode.js.map +1 -0
- package/server/dist/core/jsonl-records.d.ts +104 -0
- package/server/dist/core/jsonl-records.d.ts.map +1 -0
- package/server/dist/core/jsonl-records.js +222 -0
- package/server/dist/core/jsonl-records.js.map +1 -0
- package/server/dist/core/launcher.d.ts +74 -0
- package/server/dist/core/launcher.d.ts.map +1 -0
- package/server/dist/core/launcher.js +192 -0
- package/server/dist/core/launcher.js.map +1 -0
- package/server/dist/core/mission-context/artifacts-commit.d.ts +28 -0
- package/server/dist/core/mission-context/artifacts-commit.d.ts.map +1 -0
- package/server/dist/core/mission-context/artifacts-commit.js +98 -0
- package/server/dist/core/mission-context/artifacts-commit.js.map +1 -0
- package/server/dist/core/mission-context/artifacts-decisions.d.ts +5 -0
- package/server/dist/core/mission-context/artifacts-decisions.d.ts.map +1 -0
- package/server/dist/core/mission-context/artifacts-decisions.js +114 -0
- package/server/dist/core/mission-context/artifacts-decisions.js.map +1 -0
- package/server/dist/core/mission-context/artifacts-slice2.d.ts +12 -0
- package/server/dist/core/mission-context/artifacts-slice2.d.ts.map +1 -0
- package/server/dist/core/mission-context/artifacts-slice2.js +125 -0
- package/server/dist/core/mission-context/artifacts-slice2.js.map +1 -0
- package/server/dist/core/mission-context/artifacts-tests.d.ts +34 -0
- package/server/dist/core/mission-context/artifacts-tests.d.ts.map +1 -0
- package/server/dist/core/mission-context/artifacts-tests.js +236 -0
- package/server/dist/core/mission-context/artifacts-tests.js.map +1 -0
- package/server/dist/core/mission-context/artifacts.d.ts +33 -0
- package/server/dist/core/mission-context/artifacts.d.ts.map +1 -0
- package/server/dist/core/mission-context/artifacts.js +186 -0
- package/server/dist/core/mission-context/artifacts.js.map +1 -0
- package/server/dist/core/mission-context/association.d.ts +27 -0
- package/server/dist/core/mission-context/association.d.ts.map +1 -0
- package/server/dist/core/mission-context/association.js +69 -0
- package/server/dist/core/mission-context/association.js.map +1 -0
- package/server/dist/core/mission-context/campaign-artifacts.d.ts +9 -0
- package/server/dist/core/mission-context/campaign-artifacts.d.ts.map +1 -0
- package/server/dist/core/mission-context/campaign-artifacts.js +195 -0
- package/server/dist/core/mission-context/campaign-artifacts.js.map +1 -0
- package/server/dist/core/mission-context/campaign-facts.d.ts +99 -0
- package/server/dist/core/mission-context/campaign-facts.d.ts.map +1 -0
- package/server/dist/core/mission-context/campaign-facts.js +111 -0
- package/server/dist/core/mission-context/campaign-facts.js.map +1 -0
- package/server/dist/core/mission-context/decision-drops.d.ts +46 -0
- package/server/dist/core/mission-context/decision-drops.d.ts.map +1 -0
- package/server/dist/core/mission-context/decision-drops.js +269 -0
- package/server/dist/core/mission-context/decision-drops.js.map +1 -0
- package/server/dist/core/mission-context/decisions.d.ts +91 -0
- package/server/dist/core/mission-context/decisions.d.ts.map +1 -0
- package/server/dist/core/mission-context/decisions.js +222 -0
- package/server/dist/core/mission-context/decisions.js.map +1 -0
- package/server/dist/core/mission-context/doc-ids.d.ts +35 -0
- package/server/dist/core/mission-context/doc-ids.d.ts.map +1 -0
- package/server/dist/core/mission-context/doc-ids.js +78 -0
- package/server/dist/core/mission-context/doc-ids.js.map +1 -0
- package/server/dist/core/mission-context/document-read.d.ts +14 -0
- package/server/dist/core/mission-context/document-read.d.ts.map +1 -0
- package/server/dist/core/mission-context/document-read.js +36 -0
- package/server/dist/core/mission-context/document-read.js.map +1 -0
- package/server/dist/core/mission-context/fold-map.d.ts +45 -0
- package/server/dist/core/mission-context/fold-map.d.ts.map +1 -0
- package/server/dist/core/mission-context/fold-map.js +168 -0
- package/server/dist/core/mission-context/fold-map.js.map +1 -0
- package/server/dist/core/mission-context/fs-read.d.ts +47 -0
- package/server/dist/core/mission-context/fs-read.d.ts.map +1 -0
- package/server/dist/core/mission-context/fs-read.js +104 -0
- package/server/dist/core/mission-context/fs-read.js.map +1 -0
- package/server/dist/core/mission-context/iterate-record.d.ts +52 -0
- package/server/dist/core/mission-context/iterate-record.d.ts.map +1 -0
- package/server/dist/core/mission-context/iterate-record.js +156 -0
- package/server/dist/core/mission-context/iterate-record.js.map +1 -0
- package/server/dist/core/mission-context/merge-check.d.ts +60 -0
- package/server/dist/core/mission-context/merge-check.d.ts.map +1 -0
- package/server/dist/core/mission-context/merge-check.js +217 -0
- package/server/dist/core/mission-context/merge-check.js.map +1 -0
- package/server/dist/core/mission-context/merge-refresh.d.ts +14 -0
- package/server/dist/core/mission-context/merge-refresh.d.ts.map +1 -0
- package/server/dist/core/mission-context/merge-refresh.js +53 -0
- package/server/dist/core/mission-context/merge-refresh.js.map +1 -0
- package/server/dist/core/mission-context/merged-events.d.ts +34 -0
- package/server/dist/core/mission-context/merged-events.d.ts.map +1 -0
- package/server/dist/core/mission-context/merged-events.js +120 -0
- package/server/dist/core/mission-context/merged-events.js.map +1 -0
- package/server/dist/core/mission-context/pipeline-artifacts.d.ts +53 -0
- package/server/dist/core/mission-context/pipeline-artifacts.d.ts.map +1 -0
- package/server/dist/core/mission-context/pipeline-artifacts.js +161 -0
- package/server/dist/core/mission-context/pipeline-artifacts.js.map +1 -0
- package/server/dist/core/mission-context/planned-impact.d.ts +28 -0
- package/server/dist/core/mission-context/planned-impact.d.ts.map +1 -0
- package/server/dist/core/mission-context/planned-impact.js +129 -0
- package/server/dist/core/mission-context/planned-impact.js.map +1 -0
- package/server/dist/core/mission-context/pointer.d.ts +44 -0
- package/server/dist/core/mission-context/pointer.d.ts.map +1 -0
- package/server/dist/core/mission-context/pointer.js +165 -0
- package/server/dist/core/mission-context/pointer.js.map +1 -0
- package/server/dist/core/mission-context/resolver-io.d.ts +47 -0
- package/server/dist/core/mission-context/resolver-io.d.ts.map +1 -0
- package/server/dist/core/mission-context/resolver-io.js +11 -0
- package/server/dist/core/mission-context/resolver-io.js.map +1 -0
- package/server/dist/core/mission-context/resolver-parts.d.ts +57 -0
- package/server/dist/core/mission-context/resolver-parts.d.ts.map +1 -0
- package/server/dist/core/mission-context/resolver-parts.js +136 -0
- package/server/dist/core/mission-context/resolver-parts.js.map +1 -0
- package/server/dist/core/mission-context/resolver.d.ts +11 -0
- package/server/dist/core/mission-context/resolver.d.ts.map +1 -0
- package/server/dist/core/mission-context/resolver.js +261 -0
- package/server/dist/core/mission-context/resolver.js.map +1 -0
- package/server/dist/core/mission-context/review-record-entry.d.ts +45 -0
- package/server/dist/core/mission-context/review-record-entry.d.ts.map +1 -0
- package/server/dist/core/mission-context/review-record-entry.js +214 -0
- package/server/dist/core/mission-context/review-record-entry.js.map +1 -0
- package/server/dist/core/mission-context/review-record.d.ts +35 -0
- package/server/dist/core/mission-context/review-record.d.ts.map +1 -0
- package/server/dist/core/mission-context/review-record.js +259 -0
- package/server/dist/core/mission-context/review-record.js.map +1 -0
- package/server/dist/core/mission-context/review-state.d.ts +33 -0
- package/server/dist/core/mission-context/review-state.d.ts.map +1 -0
- package/server/dist/core/mission-context/review-state.js +249 -0
- package/server/dist/core/mission-context/review-state.js.map +1 -0
- package/server/dist/core/mission-context/run-id-recovery.d.ts +44 -0
- package/server/dist/core/mission-context/run-id-recovery.d.ts.map +1 -0
- package/server/dist/core/mission-context/run-id-recovery.js +175 -0
- package/server/dist/core/mission-context/run-id-recovery.js.map +1 -0
- package/server/dist/core/mission-context/scenario.d.ts +121 -0
- package/server/dist/core/mission-context/scenario.d.ts.map +1 -0
- package/server/dist/core/mission-context/scenario.js +151 -0
- package/server/dist/core/mission-context/scenario.js.map +1 -0
- package/server/dist/core/mission-context/slice2-sources.d.ts +40 -0
- package/server/dist/core/mission-context/slice2-sources.d.ts.map +1 -0
- package/server/dist/core/mission-context/slice2-sources.js +80 -0
- package/server/dist/core/mission-context/slice2-sources.js.map +1 -0
- package/server/dist/core/mission-context/slice2-test-fixtures.d.ts +17 -0
- package/server/dist/core/mission-context/slice2-test-fixtures.d.ts.map +1 -0
- package/server/dist/core/mission-context/slice2-test-fixtures.js +61 -0
- package/server/dist/core/mission-context/slice2-test-fixtures.js.map +1 -0
- package/server/dist/core/mission-context/slice3-sources.d.ts +43 -0
- package/server/dist/core/mission-context/slice3-sources.d.ts.map +1 -0
- package/server/dist/core/mission-context/slice3-sources.js +192 -0
- package/server/dist/core/mission-context/slice3-sources.js.map +1 -0
- package/server/dist/core/mission-context/spec-candidates.d.ts +66 -0
- package/server/dist/core/mission-context/spec-candidates.d.ts.map +1 -0
- package/server/dist/core/mission-context/spec-candidates.js +164 -0
- package/server/dist/core/mission-context/spec-candidates.js.map +1 -0
- package/server/dist/core/mission-context/test-evidence.d.ts +14 -0
- package/server/dist/core/mission-context/test-evidence.d.ts.map +1 -0
- package/server/dist/core/mission-context/test-evidence.js +53 -0
- package/server/dist/core/mission-context/test-evidence.js.map +1 -0
- package/server/dist/core/mission-context/tests-diff.d.ts +68 -0
- package/server/dist/core/mission-context/tests-diff.d.ts.map +1 -0
- package/server/dist/core/mission-context/tests-diff.js +151 -0
- package/server/dist/core/mission-context/tests-diff.js.map +1 -0
- package/server/dist/core/mission-context/traceability.d.ts +58 -0
- package/server/dist/core/mission-context/traceability.d.ts.map +1 -0
- package/server/dist/core/mission-context/traceability.js +168 -0
- package/server/dist/core/mission-context/traceability.js.map +1 -0
- package/server/dist/core/mission-context/types-slice2.d.ts +192 -0
- package/server/dist/core/mission-context/types-slice2.d.ts.map +1 -0
- package/server/dist/core/mission-context/types-slice2.js +15 -0
- package/server/dist/core/mission-context/types-slice2.js.map +1 -0
- package/server/dist/core/mission-context/types-slice3.d.ts +84 -0
- package/server/dist/core/mission-context/types-slice3.d.ts.map +1 -0
- package/server/dist/core/mission-context/types-slice3.js +24 -0
- package/server/dist/core/mission-context/types-slice3.js.map +1 -0
- package/server/dist/core/mission-context/types.d.ts +198 -0
- package/server/dist/core/mission-context/types.d.ts.map +1 -0
- package/server/dist/core/mission-context/types.js +44 -0
- package/server/dist/core/mission-context/types.js.map +1 -0
- package/server/dist/core/mission-context/worktree-roots.d.ts +103 -0
- package/server/dist/core/mission-context/worktree-roots.d.ts.map +1 -0
- package/server/dist/core/mission-context/worktree-roots.js +201 -0
- package/server/dist/core/mission-context/worktree-roots.js.map +1 -0
- package/server/dist/core/model-tier-config-reader.d.ts +16 -0
- package/server/dist/core/model-tier-config-reader.d.ts.map +1 -0
- package/server/dist/core/model-tier-config-reader.js +87 -0
- package/server/dist/core/model-tier-config-reader.js.map +1 -0
- package/server/dist/core/normalize-fs-path.d.ts +2 -0
- package/server/dist/core/normalize-fs-path.d.ts.map +1 -0
- package/server/dist/core/normalize-fs-path.js +31 -0
- package/server/dist/core/normalize-fs-path.js.map +1 -0
- package/server/dist/core/parameter-resolver.d.ts +32 -0
- package/server/dist/core/parameter-resolver.d.ts.map +1 -0
- package/server/dist/core/parameter-resolver.js +300 -0
- package/server/dist/core/parameter-resolver.js.map +1 -0
- package/server/dist/core/path-guard.d.ts +40 -0
- package/server/dist/core/path-guard.d.ts.map +1 -0
- package/server/dist/core/path-guard.js +147 -0
- package/server/dist/core/path-guard.js.map +1 -0
- package/server/dist/core/pr-status.d.ts +47 -0
- package/server/dist/core/pr-status.d.ts.map +1 -0
- package/server/dist/core/pr-status.js +157 -0
- package/server/dist/core/pr-status.js.map +1 -0
- package/server/dist/core/preview-child-lifecycle.d.ts +77 -0
- package/server/dist/core/preview-child-lifecycle.d.ts.map +1 -0
- package/server/dist/core/preview-child-lifecycle.js +223 -0
- package/server/dist/core/preview-child-lifecycle.js.map +1 -0
- package/server/dist/core/preview-coherence.d.ts +59 -0
- package/server/dist/core/preview-coherence.d.ts.map +1 -0
- package/server/dist/core/preview-coherence.js +66 -0
- package/server/dist/core/preview-coherence.js.map +1 -0
- package/server/dist/core/preview-session-manager.d.ts +86 -0
- package/server/dist/core/preview-session-manager.d.ts.map +1 -0
- package/server/dist/core/preview-session-manager.js +303 -0
- package/server/dist/core/preview-session-manager.js.map +1 -0
- package/server/dist/core/preview-win32-spawn.d.ts +11 -0
- package/server/dist/core/preview-win32-spawn.d.ts.map +1 -0
- package/server/dist/core/preview-win32-spawn.js +45 -0
- package/server/dist/core/preview-win32-spawn.js.map +1 -0
- package/server/dist/core/profile-loader.d.ts +65 -0
- package/server/dist/core/profile-loader.d.ts.map +1 -0
- package/server/dist/core/profile-loader.js +125 -0
- package/server/dist/core/profile-loader.js.map +1 -0
- package/server/dist/core/project-actions-loader.d.ts +103 -0
- package/server/dist/core/project-actions-loader.d.ts.map +1 -0
- package/server/dist/core/project-actions-loader.js +161 -0
- package/server/dist/core/project-actions-loader.js.map +1 -0
- package/server/dist/core/project-manager.d.ts +110 -0
- package/server/dist/core/project-manager.d.ts.map +1 -0
- package/server/dist/core/project-manager.js +339 -0
- package/server/dist/core/project-manager.js.map +1 -0
- package/server/dist/core/project-registry-io.d.ts +64 -0
- package/server/dist/core/project-registry-io.d.ts.map +1 -0
- package/server/dist/core/project-registry-io.js +194 -0
- package/server/dist/core/project-registry-io.js.map +1 -0
- package/server/dist/core/readiness-probe.d.ts +89 -0
- package/server/dist/core/readiness-probe.d.ts.map +1 -0
- package/server/dist/core/readiness-probe.js +216 -0
- package/server/dist/core/readiness-probe.js.map +1 -0
- package/server/dist/core/run-config-reader.d.ts +35 -0
- package/server/dist/core/run-config-reader.d.ts.map +1 -0
- package/server/dist/core/run-config-reader.js +362 -0
- package/server/dist/core/run-config-reader.js.map +1 -0
- package/server/dist/core/run-data-join.d.ts +60 -0
- package/server/dist/core/run-data-join.d.ts.map +1 -0
- package/server/dist/core/run-data-join.js +264 -0
- package/server/dist/core/run-data-join.js.map +1 -0
- package/server/dist/core/run-data-types.d.ts +126 -0
- package/server/dist/core/run-data-types.d.ts.map +1 -0
- package/server/dist/core/run-data-types.js +25 -0
- package/server/dist/core/run-data-types.js.map +1 -0
- package/server/dist/core/run-loop-state-reader.d.ts +27 -0
- package/server/dist/core/run-loop-state-reader.d.ts.map +1 -0
- package/server/dist/core/run-loop-state-reader.js +79 -0
- package/server/dist/core/run-loop-state-reader.js.map +1 -0
- package/server/dist/core/sdk-sessions-merge.d.ts +60 -0
- package/server/dist/core/sdk-sessions-merge.d.ts.map +1 -0
- package/server/dist/core/sdk-sessions-merge.js +220 -0
- package/server/dist/core/sdk-sessions-merge.js.map +1 -0
- package/server/dist/core/sdk-sessions-store.d.ts +261 -0
- package/server/dist/core/sdk-sessions-store.d.ts.map +1 -0
- package/server/dist/core/sdk-sessions-store.js +315 -0
- package/server/dist/core/sdk-sessions-store.js.map +1 -0
- package/server/dist/core/sdk-sessions-validate.d.ts +9 -0
- package/server/dist/core/sdk-sessions-validate.d.ts.map +1 -0
- package/server/dist/core/sdk-sessions-validate.js +237 -0
- package/server/dist/core/sdk-sessions-validate.js.map +1 -0
- package/server/dist/core/session-jsonl-io.d.ts +35 -0
- package/server/dist/core/session-jsonl-io.d.ts.map +1 -0
- package/server/dist/core/session-jsonl-io.js +128 -0
- package/server/dist/core/session-jsonl-io.js.map +1 -0
- package/server/dist/core/session-parser.d.ts +81 -0
- package/server/dist/core/session-parser.d.ts.map +1 -0
- package/server/dist/core/session-parser.js +168 -0
- package/server/dist/core/session-parser.js.map +1 -0
- package/server/dist/core/session-watcher-debug.d.ts +7 -0
- package/server/dist/core/session-watcher-debug.d.ts.map +1 -0
- package/server/dist/core/session-watcher-debug.js +31 -0
- package/server/dist/core/session-watcher-debug.js.map +1 -0
- package/server/dist/core/session-watcher.d.ts +101 -0
- package/server/dist/core/session-watcher.d.ts.map +1 -0
- package/server/dist/core/session-watcher.js +226 -0
- package/server/dist/core/session-watcher.js.map +1 -0
- package/server/dist/core/shell-quote.d.ts +5 -0
- package/server/dist/core/shell-quote.d.ts.map +1 -0
- package/server/dist/core/shell-quote.js +49 -0
- package/server/dist/core/shell-quote.js.map +1 -0
- package/server/dist/core/task-editability.d.ts +22 -0
- package/server/dist/core/task-editability.d.ts.map +1 -0
- package/server/dist/core/task-editability.js +34 -0
- package/server/dist/core/task-editability.js.map +1 -0
- package/server/dist/core/terminal-prompt-detect.d.ts +7 -0
- package/server/dist/core/terminal-prompt-detect.d.ts.map +1 -0
- package/server/dist/core/terminal-prompt-detect.js +128 -0
- package/server/dist/core/terminal-prompt-detect.js.map +1 -0
- package/server/dist/core/tests-gate.d.ts +23 -0
- package/server/dist/core/tests-gate.d.ts.map +1 -0
- package/server/dist/core/tests-gate.js +92 -0
- package/server/dist/core/tests-gate.js.map +1 -0
- package/server/dist/core/triage-amend.d.ts +33 -0
- package/server/dist/core/triage-amend.d.ts.map +1 -0
- package/server/dist/core/triage-amend.js +109 -0
- package/server/dist/core/triage-amend.js.map +1 -0
- package/server/dist/core/triage-board-read.d.ts +37 -0
- package/server/dist/core/triage-board-read.d.ts.map +1 -0
- package/server/dist/core/triage-board-read.js +148 -0
- package/server/dist/core/triage-board-read.js.map +1 -0
- package/server/dist/core/triage-cli-runner.d.ts +61 -0
- package/server/dist/core/triage-cli-runner.d.ts.map +1 -0
- package/server/dist/core/triage-cli-runner.js +92 -0
- package/server/dist/core/triage-cli-runner.js.map +1 -0
- package/server/dist/core/triage-compose.d.ts +33 -0
- package/server/dist/core/triage-compose.d.ts.map +1 -0
- package/server/dist/core/triage-compose.js +51 -0
- package/server/dist/core/triage-compose.js.map +1 -0
- package/server/dist/core/triage-contract.d.ts +18 -0
- package/server/dist/core/triage-contract.d.ts.map +1 -0
- package/server/dist/core/triage-contract.js +45 -0
- package/server/dist/core/triage-contract.js.map +1 -0
- package/server/dist/core/triage-defer.d.ts +36 -0
- package/server/dist/core/triage-defer.d.ts.map +1 -0
- package/server/dist/core/triage-defer.js +117 -0
- package/server/dist/core/triage-defer.js.map +1 -0
- package/server/dist/core/triage-enrich.d.ts +31 -0
- package/server/dist/core/triage-enrich.d.ts.map +1 -0
- package/server/dist/core/triage-enrich.js +127 -0
- package/server/dist/core/triage-enrich.js.map +1 -0
- package/server/dist/core/triage-lock.d.ts +12 -0
- package/server/dist/core/triage-lock.d.ts.map +1 -0
- package/server/dist/core/triage-lock.js +41 -0
- package/server/dist/core/triage-lock.js.map +1 -0
- package/server/dist/core/triage-origin.d.ts +52 -0
- package/server/dist/core/triage-origin.d.ts.map +1 -0
- package/server/dist/core/triage-origin.js +0 -0
- package/server/dist/core/triage-origin.js.map +1 -0
- package/server/dist/core/triage-paths.d.ts +25 -0
- package/server/dist/core/triage-paths.d.ts.map +1 -0
- package/server/dist/core/triage-paths.js +125 -0
- package/server/dist/core/triage-paths.js.map +1 -0
- package/server/dist/core/triage-raw.d.ts +33 -0
- package/server/dist/core/triage-raw.d.ts.map +1 -0
- package/server/dist/core/triage-raw.js +66 -0
- package/server/dist/core/triage-raw.js.map +1 -0
- package/server/dist/core/triage-store.d.ts +42 -0
- package/server/dist/core/triage-store.d.ts.map +1 -0
- package/server/dist/core/triage-store.js +268 -0
- package/server/dist/core/triage-store.js.map +1 -0
- package/server/dist/core/triage-validation.d.ts +58 -0
- package/server/dist/core/triage-validation.d.ts.map +1 -0
- package/server/dist/core/triage-validation.js +198 -0
- package/server/dist/core/triage-validation.js.map +1 -0
- package/server/dist/core/win32-spawn.d.ts +29 -0
- package/server/dist/core/win32-spawn.d.ts.map +1 -0
- package/server/dist/core/win32-spawn.js +271 -0
- package/server/dist/core/win32-spawn.js.map +1 -0
- package/server/dist/external/__tests__/api-contract-probes.d.ts +49 -0
- package/server/dist/external/__tests__/api-contract-probes.d.ts.map +1 -0
- package/server/dist/external/__tests__/api-contract-probes.js +295 -0
- package/server/dist/external/__tests__/api-contract-probes.js.map +1 -0
- package/server/dist/external/_lead-fields-tag-filter-harness.d.ts +7 -0
- package/server/dist/external/_lead-fields-tag-filter-harness.d.ts.map +1 -0
- package/server/dist/external/_lead-fields-tag-filter-harness.js +41 -0
- package/server/dist/external/_lead-fields-tag-filter-harness.js.map +1 -0
- package/server/dist/external/_routes-edit-fields-harness.d.ts +8 -0
- package/server/dist/external/_routes-edit-fields-harness.d.ts.map +1 -0
- package/server/dist/external/_routes-edit-fields-harness.js +49 -0
- package/server/dist/external/_routes-edit-fields-harness.js.map +1 -0
- package/server/dist/external/_shared/helpers.d.ts +143 -0
- package/server/dist/external/_shared/helpers.d.ts.map +1 -0
- package/server/dist/external/_shared/helpers.js +231 -0
- package/server/dist/external/_shared/helpers.js.map +1 -0
- package/server/dist/external/actions/_helpers.d.ts +15 -0
- package/server/dist/external/actions/_helpers.d.ts.map +1 -0
- package/server/dist/external/actions/_helpers.js +48 -0
- package/server/dist/external/actions/_helpers.js.map +1 -0
- package/server/dist/external/actions/get.d.ts +8 -0
- package/server/dist/external/actions/get.d.ts.map +1 -0
- package/server/dist/external/actions/get.js +96 -0
- package/server/dist/external/actions/get.js.map +1 -0
- package/server/dist/external/actions/routes.d.ts +9 -0
- package/server/dist/external/actions/routes.d.ts.map +1 -0
- package/server/dist/external/actions/routes.js +33 -0
- package/server/dist/external/actions/routes.js.map +1 -0
- package/server/dist/external/actions/stub.d.ts +6 -0
- package/server/dist/external/actions/stub.d.ts.map +1 -0
- package/server/dist/external/actions/stub.js +51 -0
- package/server/dist/external/actions/stub.js.map +1 -0
- package/server/dist/external/actions/upload-delete.d.ts +6 -0
- package/server/dist/external/actions/upload-delete.d.ts.map +1 -0
- package/server/dist/external/actions/upload-delete.js +46 -0
- package/server/dist/external/actions/upload-delete.js.map +1 -0
- package/server/dist/external/actions/upload.d.ts +6 -0
- package/server/dist/external/actions/upload.d.ts.map +1 -0
- package/server/dist/external/actions/upload.js +141 -0
- package/server/dist/external/actions/upload.js.map +1 -0
- package/server/dist/external/compliance/routes.d.ts +9 -0
- package/server/dist/external/compliance/routes.d.ts.map +1 -0
- package/server/dist/external/compliance/routes.js +46 -0
- package/server/dist/external/compliance/routes.js.map +1 -0
- package/server/dist/external/design-review/__tests__/_helpers.d.ts +16 -0
- package/server/dist/external/design-review/__tests__/_helpers.d.ts.map +1 -0
- package/server/dist/external/design-review/__tests__/_helpers.js +92 -0
- package/server/dist/external/design-review/__tests__/_helpers.js.map +1 -0
- package/server/dist/external/design-review/feedback-write.d.ts +7 -0
- package/server/dist/external/design-review/feedback-write.d.ts.map +1 -0
- package/server/dist/external/design-review/feedback-write.js +126 -0
- package/server/dist/external/design-review/feedback-write.js.map +1 -0
- package/server/dist/external/design-review/gate.d.ts +11 -0
- package/server/dist/external/design-review/gate.d.ts.map +1 -0
- package/server/dist/external/design-review/gate.js +39 -0
- package/server/dist/external/design-review/gate.js.map +1 -0
- package/server/dist/external/design-review/routes.d.ts +9 -0
- package/server/dist/external/design-review/routes.d.ts.map +1 -0
- package/server/dist/external/design-review/routes.js +25 -0
- package/server/dist/external/design-review/routes.js.map +1 -0
- package/server/dist/external/design-review/serve.d.ts +10 -0
- package/server/dist/external/design-review/serve.d.ts.map +1 -0
- package/server/dist/external/design-review/serve.js +184 -0
- package/server/dist/external/design-review/serve.js.map +1 -0
- package/server/dist/external/events/routes.d.ts +16 -0
- package/server/dist/external/events/routes.d.ts.map +1 -0
- package/server/dist/external/events/routes.js +46 -0
- package/server/dist/external/events/routes.js.map +1 -0
- package/server/dist/external/file/_helpers.d.ts +62 -0
- package/server/dist/external/file/_helpers.d.ts.map +1 -0
- package/server/dist/external/file/_helpers.js +146 -0
- package/server/dist/external/file/_helpers.js.map +1 -0
- package/server/dist/external/file/routes.d.ts +7 -0
- package/server/dist/external/file/routes.d.ts.map +1 -0
- package/server/dist/external/file/routes.js +122 -0
- package/server/dist/external/file/routes.js.map +1 -0
- package/server/dist/external/file/write.d.ts +20 -0
- package/server/dist/external/file/write.d.ts.map +1 -0
- package/server/dist/external/file/write.js +172 -0
- package/server/dist/external/file/write.js.map +1 -0
- package/server/dist/external/inbox/_cache.d.ts +48 -0
- package/server/dist/external/inbox/_cache.d.ts.map +1 -0
- package/server/dist/external/inbox/_cache.js +46 -0
- package/server/dist/external/inbox/_cache.js.map +1 -0
- package/server/dist/external/inbox/_derive.d.ts +56 -0
- package/server/dist/external/inbox/_derive.d.ts.map +1 -0
- package/server/dist/external/inbox/_derive.js +231 -0
- package/server/dist/external/inbox/_derive.js.map +1 -0
- package/server/dist/external/inbox/routes.d.ts +13 -0
- package/server/dist/external/inbox/routes.d.ts.map +1 -0
- package/server/dist/external/inbox/routes.js +59 -0
- package/server/dist/external/inbox/routes.js.map +1 -0
- package/server/dist/external/launch/_helpers.d.ts +17 -0
- package/server/dist/external/launch/_helpers.d.ts.map +1 -0
- package/server/dist/external/launch/_helpers.js +25 -0
- package/server/dist/external/launch/_helpers.js.map +1 -0
- package/server/dist/external/launch/action-substitution-branch.d.ts +11 -0
- package/server/dist/external/launch/action-substitution-branch.d.ts.map +1 -0
- package/server/dist/external/launch/action-substitution-branch.js +112 -0
- package/server/dist/external/launch/action-substitution-branch.js.map +1 -0
- package/server/dist/external/launch/campaign-branch.d.ts +12 -0
- package/server/dist/external/launch/campaign-branch.d.ts.map +1 -0
- package/server/dist/external/launch/campaign-branch.js +95 -0
- package/server/dist/external/launch/campaign-branch.js.map +1 -0
- package/server/dist/external/launch/campaign-step-branch.d.ts +12 -0
- package/server/dist/external/launch/campaign-step-branch.d.ts.map +1 -0
- package/server/dist/external/launch/campaign-step-branch.js +108 -0
- package/server/dist/external/launch/campaign-step-branch.js.map +1 -0
- package/server/dist/external/launch/legacy-fallback-branch.d.ts +12 -0
- package/server/dist/external/launch/legacy-fallback-branch.d.ts.map +1 -0
- package/server/dist/external/launch/legacy-fallback-branch.js +29 -0
- package/server/dist/external/launch/legacy-fallback-branch.js.map +1 -0
- package/server/dist/external/launch/master-run-branch.d.ts +24 -0
- package/server/dist/external/launch/master-run-branch.d.ts.map +1 -0
- package/server/dist/external/launch/master-run-branch.js +168 -0
- package/server/dist/external/launch/master-run-branch.js.map +1 -0
- package/server/dist/external/launch/parse-body.d.ts +46 -0
- package/server/dist/external/launch/parse-body.d.ts.map +1 -0
- package/server/dist/external/launch/parse-body.js +115 -0
- package/server/dist/external/launch/parse-body.js.map +1 -0
- package/server/dist/external/launch/phase-task-branch.d.ts +18 -0
- package/server/dist/external/launch/phase-task-branch.d.ts.map +1 -0
- package/server/dist/external/launch/phase-task-branch.js +170 -0
- package/server/dist/external/launch/phase-task-branch.js.map +1 -0
- package/server/dist/external/launch/routes.d.ts +23 -0
- package/server/dist/external/launch/routes.d.ts.map +1 -0
- package/server/dist/external/launch/routes.js +198 -0
- package/server/dist/external/launch/routes.js.map +1 -0
- package/server/dist/external/media/_helpers.d.ts +32 -0
- package/server/dist/external/media/_helpers.d.ts.map +1 -0
- package/server/dist/external/media/_helpers.js +77 -0
- package/server/dist/external/media/_helpers.js.map +1 -0
- package/server/dist/external/media/routes.d.ts +7 -0
- package/server/dist/external/media/routes.d.ts.map +1 -0
- package/server/dist/external/media/routes.js +120 -0
- package/server/dist/external/media/routes.js.map +1 -0
- package/server/dist/external/mission-context/facts-slice3.d.ts +22 -0
- package/server/dist/external/mission-context/facts-slice3.d.ts.map +1 -0
- package/server/dist/external/mission-context/facts-slice3.js +143 -0
- package/server/dist/external/mission-context/facts-slice3.js.map +1 -0
- package/server/dist/external/mission-context/facts.d.ts +44 -0
- package/server/dist/external/mission-context/facts.d.ts.map +1 -0
- package/server/dist/external/mission-context/facts.js +109 -0
- package/server/dist/external/mission-context/facts.js.map +1 -0
- package/server/dist/external/mission-context/routes.d.ts +83 -0
- package/server/dist/external/mission-context/routes.d.ts.map +1 -0
- package/server/dist/external/mission-context/routes.js +226 -0
- package/server/dist/external/mission-context/routes.js.map +1 -0
- package/server/dist/external/mission-context/test-harness.d.ts +62 -0
- package/server/dist/external/mission-context/test-harness.d.ts.map +1 -0
- package/server/dist/external/mission-context/test-harness.js +121 -0
- package/server/dist/external/mission-context/test-harness.js.map +1 -0
- package/server/dist/external/mission-context/wire.d.ts +12 -0
- package/server/dist/external/mission-context/wire.d.ts.map +1 -0
- package/server/dist/external/mission-context/wire.js +62 -0
- package/server/dist/external/mission-context/wire.js.map +1 -0
- package/server/dist/external/model-config/routes.d.ts +9 -0
- package/server/dist/external/model-config/routes.d.ts.map +1 -0
- package/server/dist/external/model-config/routes.js +18 -0
- package/server/dist/external/model-config/routes.js.map +1 -0
- package/server/dist/external/org/__tests__/fixtures/lock-race-worker.d.ts +2 -0
- package/server/dist/external/org/__tests__/fixtures/lock-race-worker.d.ts.map +1 -0
- package/server/dist/external/org/__tests__/fixtures/lock-race-worker.js +67 -0
- package/server/dist/external/org/__tests__/fixtures/lock-race-worker.js.map +1 -0
- package/server/dist/external/org/_helpers.d.ts +67 -0
- package/server/dist/external/org/_helpers.d.ts.map +1 -0
- package/server/dist/external/org/_helpers.js +146 -0
- package/server/dist/external/org/_helpers.js.map +1 -0
- package/server/dist/external/org/beat-register-release-audit.d.ts +17 -0
- package/server/dist/external/org/beat-register-release-audit.d.ts.map +1 -0
- package/server/dist/external/org/beat-register-release-audit.js +96 -0
- package/server/dist/external/org/beat-register-release-audit.js.map +1 -0
- package/server/dist/external/org/beat-register-release-core.d.ts +50 -0
- package/server/dist/external/org/beat-register-release-core.d.ts.map +1 -0
- package/server/dist/external/org/beat-register-release-core.js +183 -0
- package/server/dist/external/org/beat-register-release-core.js.map +1 -0
- package/server/dist/external/org/beat-register-release.d.ts +13 -0
- package/server/dist/external/org/beat-register-release.d.ts.map +1 -0
- package/server/dist/external/org/beat-register-release.js +81 -0
- package/server/dist/external/org/beat-register-release.js.map +1 -0
- package/server/dist/external/org/beat-register.d.ts +100 -0
- package/server/dist/external/org/beat-register.d.ts.map +1 -0
- package/server/dist/external/org/beat-register.js +198 -0
- package/server/dist/external/org/beat-register.js.map +1 -0
- package/server/dist/external/org/countersign.d.ts +8 -0
- package/server/dist/external/org/countersign.d.ts.map +1 -0
- package/server/dist/external/org/countersign.js +157 -0
- package/server/dist/external/org/countersign.js.map +1 -0
- package/server/dist/external/org/cron.d.ts +22 -0
- package/server/dist/external/org/cron.d.ts.map +1 -0
- package/server/dist/external/org/cron.js +59 -0
- package/server/dist/external/org/cron.js.map +1 -0
- package/server/dist/external/org/decisions-lock.d.ts +68 -0
- package/server/dist/external/org/decisions-lock.d.ts.map +1 -0
- package/server/dist/external/org/decisions-lock.js +209 -0
- package/server/dist/external/org/decisions-lock.js.map +1 -0
- package/server/dist/external/org/file-read.d.ts +14 -0
- package/server/dist/external/org/file-read.d.ts.map +1 -0
- package/server/dist/external/org/file-read.js +80 -0
- package/server/dist/external/org/file-read.js.map +1 -0
- package/server/dist/external/org/file-write.d.ts +14 -0
- package/server/dist/external/org/file-write.d.ts.map +1 -0
- package/server/dist/external/org/file-write.js +205 -0
- package/server/dist/external/org/file-write.js.map +1 -0
- package/server/dist/external/org/last-run.d.ts +28 -0
- package/server/dist/external/org/last-run.d.ts.map +1 -0
- package/server/dist/external/org/last-run.js +137 -0
- package/server/dist/external/org/last-run.js.map +1 -0
- package/server/dist/external/org/org-chart-lookup.d.ts +16 -0
- package/server/dist/external/org/org-chart-lookup.d.ts.map +1 -0
- package/server/dist/external/org/org-chart-lookup.js +100 -0
- package/server/dist/external/org/org-chart-lookup.js.map +1 -0
- package/server/dist/external/org/org-chart.d.ts +25 -0
- package/server/dist/external/org/org-chart.d.ts.map +1 -0
- package/server/dist/external/org/org-chart.js +110 -0
- package/server/dist/external/org/org-chart.js.map +1 -0
- package/server/dist/external/org/routes.d.ts +12 -0
- package/server/dist/external/org/routes.d.ts.map +1 -0
- package/server/dist/external/org/routes.js +90 -0
- package/server/dist/external/org/routes.js.map +1 -0
- package/server/dist/external/org/usage.d.ts +24 -0
- package/server/dist/external/org/usage.d.ts.map +1 -0
- package/server/dist/external/org/usage.js +110 -0
- package/server/dist/external/org/usage.js.map +1 -0
- package/server/dist/external/pr-status/routes.d.ts +7 -0
- package/server/dist/external/pr-status/routes.d.ts.map +1 -0
- package/server/dist/external/pr-status/routes.js +37 -0
- package/server/dist/external/pr-status/routes.js.map +1 -0
- package/server/dist/external/preview/routes.d.ts +10 -0
- package/server/dist/external/preview/routes.d.ts.map +1 -0
- package/server/dist/external/preview/routes.js +71 -0
- package/server/dist/external/preview/routes.js.map +1 -0
- package/server/dist/external/routes.d.ts +106 -0
- package/server/dist/external/routes.d.ts.map +1 -0
- package/server/dist/external/routes.js +162 -0
- package/server/dist/external/routes.js.map +1 -0
- package/server/dist/external/run-config/routes.d.ts +9 -0
- package/server/dist/external/run-config/routes.d.ts.map +1 -0
- package/server/dist/external/run-config/routes.js +55 -0
- package/server/dist/external/run-config/routes.js.map +1 -0
- package/server/dist/external/runs/routes.d.ts +16 -0
- package/server/dist/external/runs/routes.d.ts.map +1 -0
- package/server/dist/external/runs/routes.js +69 -0
- package/server/dist/external/runs/routes.js.map +1 -0
- package/server/dist/external/tasks/_phase-helpers.d.ts +37 -0
- package/server/dist/external/tasks/_phase-helpers.d.ts.map +1 -0
- package/server/dist/external/tasks/_phase-helpers.js +101 -0
- package/server/dist/external/tasks/_phase-helpers.js.map +1 -0
- package/server/dist/external/tasks/create.d.ts +12 -0
- package/server/dist/external/tasks/create.d.ts.map +1 -0
- package/server/dist/external/tasks/create.js +139 -0
- package/server/dist/external/tasks/create.js.map +1 -0
- package/server/dist/external/tasks/lifecycle.d.ts +12 -0
- package/server/dist/external/tasks/lifecycle.d.ts.map +1 -0
- package/server/dist/external/tasks/lifecycle.js +231 -0
- package/server/dist/external/tasks/lifecycle.js.map +1 -0
- package/server/dist/external/tasks/list-get.d.ts +11 -0
- package/server/dist/external/tasks/list-get.d.ts.map +1 -0
- package/server/dist/external/tasks/list-get.js +50 -0
- package/server/dist/external/tasks/list-get.js.map +1 -0
- package/server/dist/external/tasks/patch.d.ts +12 -0
- package/server/dist/external/tasks/patch.d.ts.map +1 -0
- package/server/dist/external/tasks/patch.js +192 -0
- package/server/dist/external/tasks/patch.js.map +1 -0
- package/server/dist/external/tasks/routes.d.ts +18 -0
- package/server/dist/external/tasks/routes.d.ts.map +1 -0
- package/server/dist/external/tasks/routes.js +49 -0
- package/server/dist/external/tasks/routes.js.map +1 -0
- package/server/dist/external/transcript/routes.d.ts +12 -0
- package/server/dist/external/transcript/routes.d.ts.map +1 -0
- package/server/dist/external/transcript/routes.js +160 -0
- package/server/dist/external/transcript/routes.js.map +1 -0
- package/server/dist/external/tree/routes.d.ts +7 -0
- package/server/dist/external/tree/routes.d.ts.map +1 -0
- package/server/dist/external/tree/routes.js +96 -0
- package/server/dist/external/tree/routes.js.map +1 -0
- package/server/dist/index.d.ts +3 -0
- package/server/dist/index.d.ts.map +1 -0
- package/server/dist/index.js +774 -0
- package/server/dist/index.js.map +1 -0
- package/server/dist/lib/bind-errors.d.ts +18 -0
- package/server/dist/lib/bind-errors.d.ts.map +1 -0
- package/server/dist/lib/bind-errors.js +43 -0
- package/server/dist/lib/bind-errors.js.map +1 -0
- package/server/dist/lib/resolveHonoHost.d.ts +3 -0
- package/server/dist/lib/resolveHonoHost.d.ts.map +1 -0
- package/server/dist/lib/resolveHonoHost.js +34 -0
- package/server/dist/lib/resolveHonoHost.js.map +1 -0
- package/server/dist/lib/resolveNetworkProfile.d.ts +28 -0
- package/server/dist/lib/resolveNetworkProfile.d.ts.map +1 -0
- package/server/dist/lib/resolveNetworkProfile.js +42 -0
- package/server/dist/lib/resolveNetworkProfile.js.map +1 -0
- package/server/dist/lib/resolveTailscaleIp.d.ts +26 -0
- package/server/dist/lib/resolveTailscaleIp.d.ts.map +1 -0
- package/server/dist/lib/resolveTailscaleIp.js +61 -0
- package/server/dist/lib/resolveTailscaleIp.js.map +1 -0
- package/server/dist/lib/resolveTrustedOrigins.d.ts +9 -0
- package/server/dist/lib/resolveTrustedOrigins.d.ts.map +1 -0
- package/server/dist/lib/resolveTrustedOrigins.js +154 -0
- package/server/dist/lib/resolveTrustedOrigins.js.map +1 -0
- package/server/dist/middleware/error-handler.d.ts +8 -0
- package/server/dist/middleware/error-handler.d.ts.map +1 -0
- package/server/dist/middleware/error-handler.js +75 -0
- package/server/dist/middleware/error-handler.js.map +1 -0
- package/server/dist/middleware/logger.d.ts +3 -0
- package/server/dist/middleware/logger.d.ts.map +1 -0
- package/server/dist/middleware/logger.js +13 -0
- package/server/dist/middleware/logger.js.map +1 -0
- package/server/dist/routes/_triage-api-harness.d.ts +16 -0
- package/server/dist/routes/_triage-api-harness.d.ts.map +1 -0
- package/server/dist/routes/_triage-api-harness.js +89 -0
- package/server/dist/routes/_triage-api-harness.js.map +1 -0
- package/server/dist/routes/campaigns.d.ts +26 -0
- package/server/dist/routes/campaigns.d.ts.map +1 -0
- package/server/dist/routes/campaigns.js +237 -0
- package/server/dist/routes/campaigns.js.map +1 -0
- package/server/dist/routes/diagnostics.d.ts +84 -0
- package/server/dist/routes/diagnostics.d.ts.map +1 -0
- package/server/dist/routes/diagnostics.js +143 -0
- package/server/dist/routes/diagnostics.js.map +1 -0
- package/server/dist/routes/grade.d.ts +13 -0
- package/server/dist/routes/grade.d.ts.map +1 -0
- package/server/dist/routes/grade.js +45 -0
- package/server/dist/routes/grade.js.map +1 -0
- package/server/dist/routes/profiles.d.ts +25 -0
- package/server/dist/routes/profiles.d.ts.map +1 -0
- package/server/dist/routes/profiles.js +58 -0
- package/server/dist/routes/profiles.js.map +1 -0
- package/server/dist/routes/projects.d.ts +20 -0
- package/server/dist/routes/projects.d.ts.map +1 -0
- package/server/dist/routes/projects.js +105 -0
- package/server/dist/routes/projects.js.map +1 -0
- package/server/dist/routes/readiness.d.ts +11 -0
- package/server/dist/routes/readiness.d.ts.map +1 -0
- package/server/dist/routes/readiness.js +52 -0
- package/server/dist/routes/readiness.js.map +1 -0
- package/server/dist/routes/settings.d.ts +13 -0
- package/server/dist/routes/settings.d.ts.map +1 -0
- package/server/dist/routes/settings.js +69 -0
- package/server/dist/routes/settings.js.map +1 -0
- package/server/dist/routes/terminal-appearance.d.ts +7 -0
- package/server/dist/routes/terminal-appearance.d.ts.map +1 -0
- package/server/dist/routes/terminal-appearance.js +28 -0
- package/server/dist/routes/terminal-appearance.js.map +1 -0
- package/server/dist/routes/triage.d.ts +35 -0
- package/server/dist/routes/triage.d.ts.map +1 -0
- package/server/dist/routes/triage.js +496 -0
- package/server/dist/routes/triage.js.map +1 -0
- package/server/dist/terminal/backpressure-telemetry.d.ts +86 -0
- package/server/dist/terminal/backpressure-telemetry.d.ts.map +1 -0
- package/server/dist/terminal/backpressure-telemetry.js +158 -0
- package/server/dist/terminal/backpressure-telemetry.js.map +1 -0
- package/server/dist/terminal/boot-wipe.d.ts +47 -0
- package/server/dist/terminal/boot-wipe.d.ts.map +1 -0
- package/server/dist/terminal/boot-wipe.js +122 -0
- package/server/dist/terminal/boot-wipe.js.map +1 -0
- package/server/dist/terminal/headless-mirror.d.ts +139 -0
- package/server/dist/terminal/headless-mirror.d.ts.map +1 -0
- package/server/dist/terminal/headless-mirror.js +389 -0
- package/server/dist/terminal/headless-mirror.js.map +1 -0
- package/server/dist/terminal/headless-probe.d.ts +26 -0
- package/server/dist/terminal/headless-probe.d.ts.map +1 -0
- package/server/dist/terminal/headless-probe.js +99 -0
- package/server/dist/terminal/headless-probe.js.map +1 -0
- package/server/dist/terminal/idle-reaper.d.ts +47 -0
- package/server/dist/terminal/idle-reaper.d.ts.map +1 -0
- package/server/dist/terminal/idle-reaper.js +95 -0
- package/server/dist/terminal/idle-reaper.js.map +1 -0
- package/server/dist/terminal/image-paste.d.ts +60 -0
- package/server/dist/terminal/image-paste.d.ts.map +1 -0
- package/server/dist/terminal/image-paste.js +260 -0
- package/server/dist/terminal/image-paste.js.map +1 -0
- package/server/dist/terminal/pty-manager.d.ts +452 -0
- package/server/dist/terminal/pty-manager.d.ts.map +1 -0
- package/server/dist/terminal/pty-manager.js +1014 -0
- package/server/dist/terminal/pty-manager.js.map +1 -0
- package/server/dist/terminal/replay-snapshot.d.ts +25 -0
- package/server/dist/terminal/replay-snapshot.d.ts.map +1 -0
- package/server/dist/terminal/replay-snapshot.js +124 -0
- package/server/dist/terminal/replay-snapshot.js.map +1 -0
- package/server/dist/terminal/resync-gate.d.ts +20 -0
- package/server/dist/terminal/resync-gate.d.ts.map +1 -0
- package/server/dist/terminal/resync-gate.js +58 -0
- package/server/dist/terminal/resync-gate.js.map +1 -0
- package/server/dist/terminal/routes.d.ts +67 -0
- package/server/dist/terminal/routes.d.ts.map +1 -0
- package/server/dist/terminal/routes.js +455 -0
- package/server/dist/terminal/routes.js.map +1 -0
- package/server/dist/terminal/scrollback-store.d.ts +120 -0
- package/server/dist/terminal/scrollback-store.d.ts.map +1 -0
- package/server/dist/terminal/scrollback-store.js +572 -0
- package/server/dist/terminal/scrollback-store.js.map +1 -0
- package/server/dist/terminal/snapshot-preserve.d.ts +63 -0
- package/server/dist/terminal/snapshot-preserve.d.ts.map +1 -0
- package/server/dist/terminal/snapshot-preserve.js +116 -0
- package/server/dist/terminal/snapshot-preserve.js.map +1 -0
- package/server/dist/terminal/snapshot-store.d.ts +147 -0
- package/server/dist/terminal/snapshot-store.d.ts.map +1 -0
- package/server/dist/terminal/snapshot-store.js +439 -0
- package/server/dist/terminal/snapshot-store.js.map +1 -0
- package/server/dist/terminal/snapshot-tmp-sweep.d.ts +48 -0
- package/server/dist/terminal/snapshot-tmp-sweep.d.ts.map +1 -0
- package/server/dist/terminal/snapshot-tmp-sweep.js +141 -0
- package/server/dist/terminal/snapshot-tmp-sweep.js.map +1 -0
- package/server/dist/terminal/spawn-env.d.ts +46 -0
- package/server/dist/terminal/spawn-env.d.ts.map +1 -0
- package/server/dist/terminal/spawn-env.js +232 -0
- package/server/dist/terminal/spawn-env.js.map +1 -0
- package/server/dist/terminal/terminal-reset.d.ts +31 -0
- package/server/dist/terminal/terminal-reset.d.ts.map +1 -0
- package/server/dist/terminal/terminal-reset.js +43 -0
- package/server/dist/terminal/terminal-reset.js.map +1 -0
- package/server/dist/terminal/ws-heartbeat.d.ts +78 -0
- package/server/dist/terminal/ws-heartbeat.d.ts.map +1 -0
- package/server/dist/terminal/ws-heartbeat.js +164 -0
- package/server/dist/terminal/ws-heartbeat.js.map +1 -0
- package/server/dist/terminal/ws-upgrade-handler.d.ts +96 -0
- package/server/dist/terminal/ws-upgrade-handler.d.ts.map +1 -0
- package/server/dist/terminal/ws-upgrade-handler.fixtures.d.ts +46 -0
- package/server/dist/terminal/ws-upgrade-handler.fixtures.d.ts.map +1 -0
- package/server/dist/terminal/ws-upgrade-handler.fixtures.js +126 -0
- package/server/dist/terminal/ws-upgrade-handler.fixtures.js.map +1 -0
- package/server/dist/terminal/ws-upgrade-handler.js +518 -0
- package/server/dist/terminal/ws-upgrade-handler.js.map +1 -0
- package/server/dist/types/action-schema.d.ts +75 -0
- package/server/dist/types/action-schema.d.ts.map +1 -0
- package/server/dist/types/action-schema.js +32 -0
- package/server/dist/types/action-schema.js.map +1 -0
- package/server/dist/types/project.d.ts +72 -0
- package/server/dist/types/project.d.ts.map +1 -0
- package/server/dist/types/project.js +16 -0
- package/server/dist/types/project.js.map +1 -0
- package/server/dist/types/run-config-v2.d.ts +172 -0
- package/server/dist/types/run-config-v2.d.ts.map +1 -0
- package/server/dist/types/run-config-v2.js +169 -0
- package/server/dist/types/run-config-v2.js.map +1 -0
- package/server/dist/types/settings.d.ts +25 -0
- package/server/dist/types/settings.d.ts.map +1 -0
- package/server/dist/types/settings.js +12 -0
- package/server/dist/types/settings.js.map +1 -0
- package/server/dist/types/task.d.ts +55 -0
- package/server/dist/types/task.d.ts.map +1 -0
- package/server/dist/types/task.js +13 -0
- package/server/dist/types/task.js.map +1 -0
- package/server/dist/types/triage.d.ts +135 -0
- package/server/dist/types/triage.d.ts.map +1 -0
- package/server/dist/types/triage.js +15 -0
- package/server/dist/types/triage.js.map +1 -0
- package/server/package.json +43 -0
- package/server/profiles/PROFILE_SCHEMA_VERSION +1 -0
- package/server/profiles/README.md +27 -0
- package/server/profiles/python-plugin-monorepo.json +35 -0
- package/server/profiles/supabase-nextjs.json +253 -0
- package/server/profiles/vite-hono.json +92 -0
|
@@ -0,0 +1,1014 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* pty-manager.ts — embedded-terminal pty lifecycle (iterate-2026-05-03).
|
|
3
|
+
*
|
|
4
|
+
* Plan-D'' compliance: webui hosts a NEUTRAL shell (pwsh / cmd / bash / …)
|
|
5
|
+
* — never `claude` directly. Whitelist enforced by basename normalisation
|
|
6
|
+
* so absolute paths like `/usr/local/bin/claude` cannot slip through.
|
|
7
|
+
*
|
|
8
|
+
* External-review (2026-05-03) drove these contracts:
|
|
9
|
+
* - WS upgrade is the authoritative ensure-or-create entrypoint;
|
|
10
|
+
* spawn() is idempotent for the same taskId (no dual-creation race).
|
|
11
|
+
* - Writer ownership is bound to the live WS connection identity;
|
|
12
|
+
* detach clears the writer slot synchronously.
|
|
13
|
+
* - Last-connection-close keeps the pty alive (ADR-068-A1 Replay-on-
|
|
14
|
+
* Attach contract). Navigation away from TaskDetail must NOT kill
|
|
15
|
+
* a running claude session; the user re-attaches by navigating back.
|
|
16
|
+
* - 30-min idle ceiling forces kill (the orphan GC mechanism). Plus
|
|
17
|
+
* explicit user "Stop terminal session" + DELETE task cascade.
|
|
18
|
+
* - Per-conn outbound buffer cap with drop-oldest backpressure.
|
|
19
|
+
* - Shell-aware path quoting via quotePathForShell() — used by the
|
|
20
|
+
* image-paste flow so cwd-with-spaces does not break the prompt.
|
|
21
|
+
*
|
|
22
|
+
* The actual node-pty backend is injected via PtySpawnFn so unit tests
|
|
23
|
+
* can stay native-binary-free.
|
|
24
|
+
*/
|
|
25
|
+
import { HeadlessMirror } from "./headless-mirror.js";
|
|
26
|
+
import { IdleReaper, DEFAULT_IDLE_TIMEOUT_MS } from "./idle-reaper.js";
|
|
27
|
+
import { writeSnapshotPreservingLarger } from "./snapshot-preserve.js";
|
|
28
|
+
import { BackpressureTelemetry } from "./backpressure-telemetry.js";
|
|
29
|
+
export class PtySpawnRejectedError extends Error {
|
|
30
|
+
constructor(target) {
|
|
31
|
+
super(`pty-manager: refusing to spawn '${target}' — only whitelisted shells are allowed (Plan-D''/ADR-067)`);
|
|
32
|
+
this.name = "PtySpawnRejectedError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* iterate-2026-07-15-e2e-pty-spawn-cwd-267 — the injected `spawnFn`
|
|
37
|
+
* (node-pty's `CreateProcess`) threw. Dominant cause on Windows: a spawn
|
|
38
|
+
* `cwd` that was removed / is delete-pending → `error code: 267`
|
|
39
|
+
* (ERROR_DIRECTORY). Distinct from `PtySpawnRejectedError` (whitelist);
|
|
40
|
+
* callers convert it to a clean rejection, not an uncaught throw. Full
|
|
41
|
+
* root-cause (267 ≠ resource exhaustion) is in the run's decision drop.
|
|
42
|
+
*/
|
|
43
|
+
export class PtySpawnFailedError extends Error {
|
|
44
|
+
/** Parsed native code when present (267 = ERROR_DIRECTORY on win32), else null. */
|
|
45
|
+
code;
|
|
46
|
+
constructor(cwd, cause) {
|
|
47
|
+
const causeMsg = cause instanceof Error ? cause.message : String(cause);
|
|
48
|
+
super(`pty-manager: failed to spawn a shell in cwd '${cwd}': ${causeMsg}`);
|
|
49
|
+
this.name = "PtySpawnFailedError";
|
|
50
|
+
const m = /error code:\s*(\d+)/i.exec(causeMsg);
|
|
51
|
+
this.code = m ? Number(m[1]) : null;
|
|
52
|
+
this.cause = cause;
|
|
53
|
+
}
|
|
54
|
+
/** True when the failure is an unusable cwd (win32 267 / POSIX ENOENT /
|
|
55
|
+
* ENOTDIR) — the user-actionable case; other failures (EMFILE/EPERM) are
|
|
56
|
+
* NOT labelled as a cwd problem, so callers stay accurate (code-review N1). */
|
|
57
|
+
get isLikelyCwdError() {
|
|
58
|
+
return this.code === 267 || /\b(ENOENT|ENOTDIR)\b/.test(this.message);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Whitelist + shell-kind inference
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
const WHITELIST = new Set([
|
|
65
|
+
"pwsh",
|
|
66
|
+
"pwsh.exe",
|
|
67
|
+
"powershell",
|
|
68
|
+
"powershell.exe",
|
|
69
|
+
"cmd",
|
|
70
|
+
"cmd.exe",
|
|
71
|
+
"bash",
|
|
72
|
+
"bash.exe",
|
|
73
|
+
"zsh",
|
|
74
|
+
"sh",
|
|
75
|
+
"fish",
|
|
76
|
+
]);
|
|
77
|
+
function basenameLower(target) {
|
|
78
|
+
// Handle both "/" and "\\" so a Windows path passed on a POSIX host
|
|
79
|
+
// still normalizes correctly during tests.
|
|
80
|
+
const lastSlash = Math.max(target.lastIndexOf("/"), target.lastIndexOf("\\"));
|
|
81
|
+
const base = lastSlash === -1 ? target : target.slice(lastSlash + 1);
|
|
82
|
+
return base.toLowerCase();
|
|
83
|
+
}
|
|
84
|
+
function inferShellKind(target) {
|
|
85
|
+
const b = basenameLower(target);
|
|
86
|
+
if (b === "pwsh" || b === "pwsh.exe" || b === "powershell" || b === "powershell.exe") {
|
|
87
|
+
return "pwsh";
|
|
88
|
+
}
|
|
89
|
+
if (b === "cmd" || b === "cmd.exe") {
|
|
90
|
+
return "cmd";
|
|
91
|
+
}
|
|
92
|
+
return "posix";
|
|
93
|
+
}
|
|
94
|
+
function isWhitelistedShell(target) {
|
|
95
|
+
return WHITELIST.has(basenameLower(target));
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// quotePathForShell — used by image-paste route so paths with spaces work.
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
export function quotePathForShell(absPath, kind) {
|
|
101
|
+
if (kind === "pwsh") {
|
|
102
|
+
// PowerShell single-quoted strings: only ' itself needs escaping (doubled).
|
|
103
|
+
return "'" + absPath.replace(/'/g, "''") + "'";
|
|
104
|
+
}
|
|
105
|
+
if (kind === "cmd") {
|
|
106
|
+
// cmd.exe: double-quoted, " escaped as "" (de-facto rule for argv parsing).
|
|
107
|
+
return '"' + absPath.replace(/"/g, '""') + '"';
|
|
108
|
+
}
|
|
109
|
+
// POSIX: single-quoted; close-quote, escaped quote, re-open-quote.
|
|
110
|
+
return "'" + absPath.replace(/'/g, "'\\''") + "'";
|
|
111
|
+
}
|
|
112
|
+
/** Sentinel for the legacy token-less pause()/resume() API. */
|
|
113
|
+
const ANON_PAUSE_TOKEN = Symbol("pty-manager.anonymous-pause");
|
|
114
|
+
export class PtyManager {
|
|
115
|
+
entries = new Map();
|
|
116
|
+
spawnFn;
|
|
117
|
+
wsBufferBytes;
|
|
118
|
+
idleTimeoutMs;
|
|
119
|
+
idleReaper;
|
|
120
|
+
scrollbackStore;
|
|
121
|
+
snapshotStore;
|
|
122
|
+
headlessMirrorEnabled;
|
|
123
|
+
expectedTerminalVersion;
|
|
124
|
+
watchdogStuckThresholdBytes;
|
|
125
|
+
watchdogStuckDurationMs;
|
|
126
|
+
watchdogIntervalMs;
|
|
127
|
+
watchdogEnabledOpt;
|
|
128
|
+
nowFn;
|
|
129
|
+
watchdogTimer = null;
|
|
130
|
+
/**
|
|
131
|
+
* Per-conn capability tracking (per external review code-pass —
|
|
132
|
+
* openai-5). `Map<conn, "ok" | "missing">`: "ok" = bufferedAmount
|
|
133
|
+
* returned a number; "missing" = bufferedAmount was undefined →
|
|
134
|
+
* skip THAT conn's eviction without disabling the watchdog globally.
|
|
135
|
+
* Conns absent from the map haven't been probed yet — first
|
|
136
|
+
* deliverWithBackpressure / watchdogTick that sees them stamps
|
|
137
|
+
* the result. Earlier global-disable design was rejected because
|
|
138
|
+
* a single legacy adapter would have permanently disabled stuck-
|
|
139
|
+
* writer eviction for healthy conns sharing the same PtyManager.
|
|
140
|
+
*/
|
|
141
|
+
connCapability = new Map();
|
|
142
|
+
/** Whether the capability-missing warning has already been logged (rate-limit). */
|
|
143
|
+
capabilityWarnLogged = false;
|
|
144
|
+
/** Owns the saturation-episode state formerly `PtyEntry.backpressureRaised`. */
|
|
145
|
+
backpressure = new BackpressureTelemetry();
|
|
146
|
+
constructor(opts) {
|
|
147
|
+
this.spawnFn = opts.spawn;
|
|
148
|
+
this.wsBufferBytes = opts.wsBufferBytes ?? 1_048_576;
|
|
149
|
+
this.idleTimeoutMs = opts.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
150
|
+
// Orphan-GC (attachment-gated, iterate-2026-06-02): the idle ceiling
|
|
151
|
+
// fires ONLY when no client is attached; reap = the existing kill() path
|
|
152
|
+
// (flags the intentional close → v0.8.7 marker). Policy in idle-reaper.ts.
|
|
153
|
+
this.idleReaper = new IdleReaper({
|
|
154
|
+
timeoutMs: this.idleTimeoutMs,
|
|
155
|
+
onReap: (taskId) => this.kill(taskId),
|
|
156
|
+
});
|
|
157
|
+
this.scrollbackStore = opts.scrollbackStore;
|
|
158
|
+
this.snapshotStore = opts.snapshotStore;
|
|
159
|
+
// Mirror requires BOTH flag-on AND a configured store. Either alone
|
|
160
|
+
// is a no-op so misconfiguration cannot leak in-memory Terminals
|
|
161
|
+
// without a persistence path.
|
|
162
|
+
this.headlessMirrorEnabled =
|
|
163
|
+
(opts.headlessMirrorEnabled ?? false) && !!opts.snapshotStore;
|
|
164
|
+
this.expectedTerminalVersion = opts.expectedTerminalVersion ?? "unknown";
|
|
165
|
+
this.watchdogStuckThresholdBytes =
|
|
166
|
+
opts.watchdogStuckThresholdBytes ?? 524_288; // 512 KiB
|
|
167
|
+
this.watchdogStuckDurationMs = opts.watchdogStuckDurationMs ?? 2_000;
|
|
168
|
+
this.watchdogIntervalMs = opts.watchdogIntervalMs ?? 2_000;
|
|
169
|
+
this.watchdogEnabledOpt = opts.watchdogEnabled ?? false;
|
|
170
|
+
this.nowFn = opts.now ?? Date.now;
|
|
171
|
+
if (this.watchdogEnabledOpt) {
|
|
172
|
+
this.watchdogTimer = setInterval(() => this.watchdogTick(), this.watchdogIntervalMs);
|
|
173
|
+
// Avoid keeping a Node event loop alive solely for the watchdog.
|
|
174
|
+
const t = this.watchdogTimer;
|
|
175
|
+
if (typeof t.unref === "function")
|
|
176
|
+
t.unref();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** Look up a handle by taskId. Returns undefined if not yet spawned. */
|
|
180
|
+
get(taskId) {
|
|
181
|
+
return this.entries.get(taskId)?.meta;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Return the set of taskIds with a live pty entry. Used by the daily
|
|
185
|
+
* scrollback sweep (ADR-068-A1, AC-11) to skip clearing files for
|
|
186
|
+
* tasks whose pty is still running but whose `sdk-sessions.json` state
|
|
187
|
+
* has drifted (e.g. session ended mid-run).
|
|
188
|
+
*/
|
|
189
|
+
getLiveTaskIds() {
|
|
190
|
+
return new Set(this.entries.keys());
|
|
191
|
+
}
|
|
192
|
+
/** Idempotent ensure-or-create. */
|
|
193
|
+
spawn(taskId, opts) {
|
|
194
|
+
const existing = this.entries.get(taskId);
|
|
195
|
+
if (existing)
|
|
196
|
+
return existing.meta;
|
|
197
|
+
if (!isWhitelistedShell(opts.shell)) {
|
|
198
|
+
throw new PtySpawnRejectedError(opts.shell);
|
|
199
|
+
}
|
|
200
|
+
const meta = {
|
|
201
|
+
taskId,
|
|
202
|
+
cwd: opts.cwd,
|
|
203
|
+
shell: opts.shell,
|
|
204
|
+
shellKind: inferShellKind(opts.shell),
|
|
205
|
+
};
|
|
206
|
+
// iterate-2026-07-15 — node-pty's CreateProcess throws synchronously on an
|
|
207
|
+
// unusable cwd (267). Type it so WS-upgrade + prewarm callers degrade
|
|
208
|
+
// cleanly; no entry is registered yet, so a failed spawn leaks nothing.
|
|
209
|
+
let pty;
|
|
210
|
+
try {
|
|
211
|
+
pty = this.spawnFn(opts.shell, [], {
|
|
212
|
+
cwd: opts.cwd,
|
|
213
|
+
cols: opts.cols ?? 120,
|
|
214
|
+
rows: opts.rows ?? 30,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
throw new PtySpawnFailedError(opts.cwd, err);
|
|
219
|
+
}
|
|
220
|
+
// ADR-088: optional headless mirror — lifetime co-extensive with the
|
|
221
|
+
// entry (disposed in cleanup()); dims match the pty-spawn defaults.
|
|
222
|
+
const mirror = this.headlessMirrorEnabled
|
|
223
|
+
? new HeadlessMirror({
|
|
224
|
+
taskId,
|
|
225
|
+
cols: opts.cols ?? 120,
|
|
226
|
+
rows: opts.rows ?? 30,
|
|
227
|
+
})
|
|
228
|
+
: null;
|
|
229
|
+
const entry = {
|
|
230
|
+
meta,
|
|
231
|
+
pty,
|
|
232
|
+
mirror,
|
|
233
|
+
dataSubs: new Set(),
|
|
234
|
+
connSubs: new Map(),
|
|
235
|
+
writer: null,
|
|
236
|
+
hadDataWritten: false,
|
|
237
|
+
lastResizeCols: null,
|
|
238
|
+
lastResizeRows: null,
|
|
239
|
+
pendingByConn: new Map(),
|
|
240
|
+
pauseRefCount: 0,
|
|
241
|
+
pausedConns: new Set(),
|
|
242
|
+
bufferedExceededSince: new Map(),
|
|
243
|
+
closing: false,
|
|
244
|
+
};
|
|
245
|
+
this.entries.set(taskId, entry);
|
|
246
|
+
// Forward pty output to all subscribers + reset idle timer.
|
|
247
|
+
pty.onData((data) => {
|
|
248
|
+
this.touchIdle(entry);
|
|
249
|
+
// ADR-068-A1: persist to disk before broadcast (fs.appendFileSync;
|
|
250
|
+
// try/catch so a disk error never breaks the broadcaster). D01 #2 —
|
|
251
|
+
// skip once torn down so a stuck shell can't re-create <taskId>.log.
|
|
252
|
+
if (this.scrollbackStore && !entry.tornDown) {
|
|
253
|
+
try {
|
|
254
|
+
this.scrollbackStore.append(taskId, Buffer.from(data, "utf8"));
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
// eslint-disable-next-line no-console
|
|
258
|
+
console.warn(`[pty-manager] scrollback append failed for ${taskId}: ${err.message}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ADR-088: shadow-write to the headless mirror. The mirror's
|
|
262
|
+
// write() returns a Promise that resolves after the parser
|
|
263
|
+
// callback fires; we kick it off but DO NOT await — the
|
|
264
|
+
// broadcast must stay synchronous so the WS write loop is not
|
|
265
|
+
// serialized behind xterm.js's parser. The promise resolves on
|
|
266
|
+
// the next microtask and any throw is swallowed by `.catch`.
|
|
267
|
+
// Architecture invariant #3 (await before serialize) is held by
|
|
268
|
+
// serializeStable() at snapshot time — not by every chunk write.
|
|
269
|
+
if (entry.mirror) {
|
|
270
|
+
entry.mirror.write(data).catch((err) => {
|
|
271
|
+
// eslint-disable-next-line no-console
|
|
272
|
+
console.warn(`[pty-manager] headless mirror write failed for ${taskId}: ${err.message}`);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
for (const cb of entry.dataSubs) {
|
|
276
|
+
try {
|
|
277
|
+
cb(data);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// ignore
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
for (const [conn, sub] of entry.connSubs) {
|
|
284
|
+
this.deliverWithBackpressure(entry, conn, sub, data);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
pty.onExit(() => {
|
|
288
|
+
// AC-2 — intentional kill (closing=true) appends a dim-grey
|
|
289
|
+
// "shell stopped" marker AFTER the dying-process flush drains
|
|
290
|
+
// (onExit fires once stdio handles closed). Natural exit → none.
|
|
291
|
+
if (entry.closing && this.scrollbackStore) {
|
|
292
|
+
this.appendShellStoppedMarker(taskId);
|
|
293
|
+
}
|
|
294
|
+
this.cleanup(taskId);
|
|
295
|
+
// D01/F01 — release a delete-cascade kill() awaiting full teardown.
|
|
296
|
+
entry.onExitDone?.();
|
|
297
|
+
});
|
|
298
|
+
this.touchIdle(entry);
|
|
299
|
+
return meta;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* iterate-2026-05-08 v0.8.7 AC-2 — append a single dim-grey ANSI marker
|
|
303
|
+
* frame to disk-scrollback on intentional kill. Best-effort; never throws
|
|
304
|
+
* (onExit cleanup must stay infallible). Format:
|
|
305
|
+
*
|
|
306
|
+
* \r\n\x1b[2m──── shell stopped at HH:MM:SS ────\x1b[m\r\n
|
|
307
|
+
*
|
|
308
|
+
* Timing safety (openai 2026-05-08 high; D01 #5): ScrollbackStore.append()
|
|
309
|
+
* is fs.appendFileSync — one open(O_APPEND)→write→close per call, no
|
|
310
|
+
* per-task WriteStream. closeStream() only resets the size cache, so the
|
|
311
|
+
* marker append in onExit CANNOT land on a closed handle even though kill()
|
|
312
|
+
* fires closeStream before awaiting onExit; a deleted entry doesn't affect
|
|
313
|
+
* append correctness (lookup by taskId + reopen each call).
|
|
314
|
+
*/
|
|
315
|
+
appendShellStoppedMarker(taskId) {
|
|
316
|
+
if (!this.scrollbackStore)
|
|
317
|
+
return;
|
|
318
|
+
try {
|
|
319
|
+
const ts = new Date().toISOString().slice(11, 19);
|
|
320
|
+
const marker = `\r\n\x1b[2m──── shell stopped at ${ts} ────\x1b[m\r\n`;
|
|
321
|
+
this.scrollbackStore.append(taskId, Buffer.from(marker, "utf8"));
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
// eslint-disable-next-line no-console
|
|
325
|
+
console.warn(`[pty-manager] shell-stopped marker append failed for ${taskId}: ${err.message}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
write(taskId, data) {
|
|
329
|
+
const entry = this.entries.get(taskId);
|
|
330
|
+
if (!entry)
|
|
331
|
+
return;
|
|
332
|
+
this.touchIdle(entry);
|
|
333
|
+
// The one signal for "no longer virgin" — set on real input, never
|
|
334
|
+
// at attach-time (iterate-2026-08-16-task-lifecycle-ux-fixes).
|
|
335
|
+
entry.hadDataWritten = true;
|
|
336
|
+
entry.pty.write(data);
|
|
337
|
+
}
|
|
338
|
+
resize(taskId, cols, rows) {
|
|
339
|
+
const entry = this.entries.get(taskId);
|
|
340
|
+
if (!entry)
|
|
341
|
+
return;
|
|
342
|
+
// Iterate v0.8.6 AC-2 — dedupe no-op resizes. ConPTY (and node-pty)
|
|
343
|
+
// emit a SIGWINCH-driven redraw on EVERY pty.resize call, even when
|
|
344
|
+
// (cols, rows) match the current state. PowerShell's READLINE
|
|
345
|
+
// responds to that by repainting "version banner + prompt + typed
|
|
346
|
+
// line" — ~1 KiB per redraw. With multiple WS attaches per visit
|
|
347
|
+
// (StrictMode double-mount) and multiple visits per session, this
|
|
348
|
+
// accumulates as the "100 Claude banner" symptom in xterm. Skip
|
|
349
|
+
// the call when nothing changed; the pty already has the right
|
|
350
|
+
// dimensions.
|
|
351
|
+
if (entry.lastResizeCols === cols && entry.lastResizeRows === rows) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
entry.lastResizeCols = cols;
|
|
355
|
+
entry.lastResizeRows = rows;
|
|
356
|
+
entry.pty.resize(cols, rows);
|
|
357
|
+
// ADR-088: keep the headless mirror's dimensions in lockstep so the
|
|
358
|
+
// M2 stabilization at snapshot time uses the right cols/rows.
|
|
359
|
+
if (entry.mirror) {
|
|
360
|
+
try {
|
|
361
|
+
entry.mirror.resize(cols, rows);
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
// eslint-disable-next-line no-console
|
|
365
|
+
console.warn(`[pty-manager] mirror resize failed for ${taskId}: ${err.message}`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Post-replay redraw nudge (iterate-2026-07-27, FR-01.28) — re-apply the pty's
|
|
371
|
+
* CURRENT dimensions, deliberately bypassing `resize()`'s no-op dedupe.
|
|
372
|
+
*
|
|
373
|
+
* node-pty raises a SIGWINCH-driven redraw on EVERY `pty.resize` call, changed
|
|
374
|
+
* or not. `resize()` dedupes that (v0.8.6 AC-2, PowerShell banner spam) and
|
|
375
|
+
* MUST keep doing so — but the dedupe also swallows the one signal a fullscreen
|
|
376
|
+
* TUI needs after we restore a cell-state snapshot it never drew (ADR-087):
|
|
377
|
+
* Claude Code then repaints differentially with CUF cell-skips, which do not
|
|
378
|
+
* erase, so every skipped cell keeps a stale character. Rationale + the
|
|
379
|
+
* deterministic reproduction: `cuf-stale-cell-repro.test.ts`.
|
|
380
|
+
*
|
|
381
|
+
* Dimension-LESS by design (a caller cannot reflow the grid it is repairing)
|
|
382
|
+
* and at-most-once per attach (the dedupe is bypassed here — a loop re-creates
|
|
383
|
+
* the v0.8.6 spam). No-op for an unknown / torn-down / never-sized pty.
|
|
384
|
+
*/
|
|
385
|
+
forceRedraw(taskId) {
|
|
386
|
+
const entry = this.entries.get(taskId);
|
|
387
|
+
if (!entry || entry.tornDown)
|
|
388
|
+
return;
|
|
389
|
+
const cols = entry.lastResizeCols;
|
|
390
|
+
const rows = entry.lastResizeRows;
|
|
391
|
+
if (typeof cols !== "number" || typeof rows !== "number")
|
|
392
|
+
return;
|
|
393
|
+
try {
|
|
394
|
+
entry.pty.resize(cols, rows);
|
|
395
|
+
}
|
|
396
|
+
catch (err) {
|
|
397
|
+
// `(err as Error).message` is NOT safe here: JS permits `throw null`, in
|
|
398
|
+
// which case the property read throws again and escapes this catch — out
|
|
399
|
+
// through the WS message handler. AC-3 promises swallowing, so format
|
|
400
|
+
// defensively (external code review MEDIUM).
|
|
401
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
402
|
+
// eslint-disable-next-line no-console
|
|
403
|
+
console.warn(`[pty-manager] forceRedraw failed for ${taskId}: ${message}`);
|
|
404
|
+
}
|
|
405
|
+
// Mirror dims are unchanged, so it is deliberately NOT resized (ADR-088).
|
|
406
|
+
}
|
|
407
|
+
async kill(taskId) {
|
|
408
|
+
const entry = this.entries.get(taskId);
|
|
409
|
+
if (!entry)
|
|
410
|
+
return;
|
|
411
|
+
// AC-2 — flag intentional kill so onExit appends the marker; a duplicate
|
|
412
|
+
// kill() is a no-op (entry already gone). D01 #2 — tombstone the entry first
|
|
413
|
+
// so a parked flush / stuck-shell onData can't resurrect the wiped side-files.
|
|
414
|
+
entry.closing = true;
|
|
415
|
+
entry.tornDown = true;
|
|
416
|
+
// D01/F01 — a live entry guarantees onExit fires; `exited` = the marker append.
|
|
417
|
+
const exited = new Promise((r) => { entry.onExitDone = r; });
|
|
418
|
+
try {
|
|
419
|
+
entry.pty.kill();
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
// best-effort
|
|
423
|
+
}
|
|
424
|
+
// D01 #3 — cleanup stores the SINGLE finalize on the entry; read it here so
|
|
425
|
+
// a synchronous onExit (which ran cleanup already) is still awaited.
|
|
426
|
+
this.cleanup(taskId);
|
|
427
|
+
const finalize = entry.finalizePromise ?? Promise.resolve();
|
|
428
|
+
if (this.scrollbackStore) {
|
|
429
|
+
void this.scrollbackStore
|
|
430
|
+
.closeStream(taskId)
|
|
431
|
+
.catch((err) => {
|
|
432
|
+
// eslint-disable-next-line no-console
|
|
433
|
+
console.warn(`[pty-manager] scrollback closeStream failed for ${taskId}: ${err.message}`);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
// D01 #1 — never hang the DELETE: finalize (privacy) is fully awaited; the
|
|
437
|
+
// proof-of-death `exited` degrades to a bounded 3 s ceiling (onExit is
|
|
438
|
+
// reliable for a live entry, but pty.kill() could throw / child could wedge).
|
|
439
|
+
await Promise.allSettled([
|
|
440
|
+
Promise.race([exited, new Promise((r) => { setTimeout(r, 3000).unref?.(); })]),
|
|
441
|
+
finalize,
|
|
442
|
+
]);
|
|
443
|
+
}
|
|
444
|
+
killAll() {
|
|
445
|
+
// D01 #4 — kill() is async now, but shutdown must not block on per-pty
|
|
446
|
+
// teardown; finalize writes are best-effort on process death (pre-existing
|
|
447
|
+
// F26, orphaned tmps swept by snapshot-tmp-sweep). Fire-and-forget.
|
|
448
|
+
const taskIds = [...this.entries.keys()];
|
|
449
|
+
for (const id of taskIds)
|
|
450
|
+
void this.kill(id);
|
|
451
|
+
if (this.watchdogTimer !== null) {
|
|
452
|
+
clearInterval(this.watchdogTimer);
|
|
453
|
+
this.watchdogTimer = null;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Pause the live pty (ADR-068-A1) — anonymous-token API kept for
|
|
458
|
+
* backwards compat. Per-conn pause ownership is preferred via
|
|
459
|
+
* `pauseForConn(taskId, connToken)` so the watchdog's force-evict
|
|
460
|
+
* path can clean up specific stakes (per Gemini #3 + OpenAI #12).
|
|
461
|
+
*/
|
|
462
|
+
pause(taskId) {
|
|
463
|
+
this.pauseForConn(taskId, ANON_PAUSE_TOKEN);
|
|
464
|
+
}
|
|
465
|
+
/** Counterpart to pause(). Idempotent — calling on a non-paused pty is a no-op. */
|
|
466
|
+
resume(taskId) {
|
|
467
|
+
this.resumeForConn(taskId, ANON_PAUSE_TOKEN);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* AC-3a (iterate-2026-05-05). Pause the live pty under per-conn refcount
|
|
471
|
+
* ownership. Multiple conns can each hold their own pause stake;
|
|
472
|
+
* `pty.pause()` fires only on the 0→1 transition, `pty.resume()` only
|
|
473
|
+
* on the 1→0 transition. Idempotent for the same conn.
|
|
474
|
+
*/
|
|
475
|
+
pauseForConn(taskId, connToken) {
|
|
476
|
+
const entry = this.entries.get(taskId);
|
|
477
|
+
if (!entry)
|
|
478
|
+
return;
|
|
479
|
+
if (entry.pausedConns.has(connToken))
|
|
480
|
+
return;
|
|
481
|
+
entry.pausedConns.add(connToken);
|
|
482
|
+
entry.pauseRefCount++;
|
|
483
|
+
if (entry.pauseRefCount === 1 && entry.pty.pause) {
|
|
484
|
+
try {
|
|
485
|
+
entry.pty.pause();
|
|
486
|
+
}
|
|
487
|
+
catch (err) {
|
|
488
|
+
// eslint-disable-next-line no-console
|
|
489
|
+
console.warn(`[pty-manager] pause failed for ${taskId}: ${err.message}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Counterpart to pauseForConn. Idempotent — silent no-op when the
|
|
495
|
+
* conn was not pausing (e.g. force-evicted twice, or never paused).
|
|
496
|
+
*/
|
|
497
|
+
resumeForConn(taskId, connToken) {
|
|
498
|
+
const entry = this.entries.get(taskId);
|
|
499
|
+
if (!entry)
|
|
500
|
+
return;
|
|
501
|
+
if (!entry.pausedConns.has(connToken))
|
|
502
|
+
return;
|
|
503
|
+
entry.pausedConns.delete(connToken);
|
|
504
|
+
entry.pauseRefCount = Math.max(0, entry.pauseRefCount - 1);
|
|
505
|
+
if (entry.pauseRefCount === 0 && entry.pty.resume) {
|
|
506
|
+
try {
|
|
507
|
+
entry.pty.resume();
|
|
508
|
+
}
|
|
509
|
+
catch (err) {
|
|
510
|
+
// eslint-disable-next-line no-console
|
|
511
|
+
console.warn(`[pty-manager] resume failed for ${taskId}: ${err.message}`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Generic data subscription used by tests + non-WS observers. Do NOT use
|
|
517
|
+
* for WS connections — those go through subscribeForConnection so the
|
|
518
|
+
* backpressure machinery sees them.
|
|
519
|
+
*/
|
|
520
|
+
subscribe(taskId, cb) {
|
|
521
|
+
const entry = this.entries.get(taskId);
|
|
522
|
+
if (!entry)
|
|
523
|
+
return () => undefined;
|
|
524
|
+
entry.dataSubs.add(cb);
|
|
525
|
+
return () => {
|
|
526
|
+
entry.dataSubs.delete(cb);
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Bind a WS connection. First attach is writer, the rest are readers.
|
|
531
|
+
* Idempotent: re-attaching the same `conn` returns its existing role
|
|
532
|
+
* (so onMessage can re-call attach to look up role without flipping
|
|
533
|
+
* the writer-slot — closes external code-review F6).
|
|
534
|
+
*/
|
|
535
|
+
attach(taskId, conn) {
|
|
536
|
+
const entry = this.entries.get(taskId);
|
|
537
|
+
if (!entry)
|
|
538
|
+
throw new Error(`pty-manager: attach to unknown task ${taskId}`);
|
|
539
|
+
// Atomic snapshot BEFORE mutation — closes the race a separate
|
|
540
|
+
// public read would expose against concurrent attach() calls
|
|
541
|
+
// (external review HIGH, iterate-2026-05-27-fix-pty-reused-prewarm-race).
|
|
542
|
+
// Sourced from hadDataWritten — see AttachResult.hadPriorWriter's doc comment.
|
|
543
|
+
const hadPriorWriter = entry.hadDataWritten;
|
|
544
|
+
let role;
|
|
545
|
+
if (entry.writer === null) {
|
|
546
|
+
entry.writer = conn;
|
|
547
|
+
role = "writer";
|
|
548
|
+
}
|
|
549
|
+
else if (entry.writer === conn) {
|
|
550
|
+
// Re-attach by the same conn — keep its writer role (idempotent).
|
|
551
|
+
role = "writer";
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
role = "reader";
|
|
555
|
+
}
|
|
556
|
+
if (!entry.connSubs.has(conn)) {
|
|
557
|
+
// Placeholder — the routes layer calls subscribeForConnection right after.
|
|
558
|
+
entry.connSubs.set(conn, { onData: () => undefined });
|
|
559
|
+
}
|
|
560
|
+
// Attachment-gating (2026-06-02): a watching client disarms the grace.
|
|
561
|
+
this.touchIdle(entry);
|
|
562
|
+
return { role, hadPriorWriter };
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Non-mutating role lookup. Use this in hot paths (e.g. onMessage)
|
|
566
|
+
* where calling `attach` would re-run the writer-slot decision logic.
|
|
567
|
+
* Returns `null` for unknown task or unbound connection.
|
|
568
|
+
*/
|
|
569
|
+
getRole(taskId, conn) {
|
|
570
|
+
const entry = this.entries.get(taskId);
|
|
571
|
+
if (!entry)
|
|
572
|
+
return null;
|
|
573
|
+
if (!entry.connSubs.has(conn))
|
|
574
|
+
return null;
|
|
575
|
+
return entry.writer === conn ? "writer" : "reader";
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Whether the entry currently has any writer bound. Used by
|
|
579
|
+
* /paste-image to refuse pty-injection when no live writer exists
|
|
580
|
+
* (external code-review F3 — writer-ownership tightening).
|
|
581
|
+
*/
|
|
582
|
+
hasActiveWriter(taskId) {
|
|
583
|
+
const entry = this.entries.get(taskId);
|
|
584
|
+
if (!entry)
|
|
585
|
+
return false;
|
|
586
|
+
return entry.writer !== null;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Iterate E (ADR-092) — number of currently-attached WS connections
|
|
590
|
+
* for the task. Includes writer + every reader. The routes WS-close
|
|
591
|
+
* handler uses this to decide whether to trigger
|
|
592
|
+
* `flushMirrorSnapshot` (only when the count drops to 0).
|
|
593
|
+
*
|
|
594
|
+
* Returns 0 for unknown tasks (no entry).
|
|
595
|
+
*/
|
|
596
|
+
attachCount(taskId) {
|
|
597
|
+
const entry = this.entries.get(taskId);
|
|
598
|
+
if (!entry)
|
|
599
|
+
return 0;
|
|
600
|
+
return entry.connSubs.size;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Iterate E (ADR-092) — serialize the live headless mirror on demand
|
|
604
|
+
* and return a SnapshotRecord (NOT written to disk). Used by the WS
|
|
605
|
+
* attach replay flow as a fallback when `tryReadSnapshot` returns
|
|
606
|
+
* null for a LIVE pty: without this path, re-attach to a live pty
|
|
607
|
+
* yields a blank terminal (the original ADR-091 bug).
|
|
608
|
+
*
|
|
609
|
+
* Returns null when:
|
|
610
|
+
* - no entry for `taskId`,
|
|
611
|
+
* - entry exists but `mirror === null` (flag-disabled OR
|
|
612
|
+
* initialization refused),
|
|
613
|
+
* - `mirror.serializeStable()` throws (e.g. mirror in cleanup).
|
|
614
|
+
*
|
|
615
|
+
* The returned record's `terminalVersion` is the value passed via
|
|
616
|
+
* `PtyManagerOpts.expectedTerminalVersion` at construction time —
|
|
617
|
+
* keeping it coupled to the same pinned `@xterm/headless` version
|
|
618
|
+
* the WS replay path's version-gate expects. When unset, defaults
|
|
619
|
+
* to `"unknown"` matching SnapshotStore's last-resort sentinel.
|
|
620
|
+
*
|
|
621
|
+
* Best-effort: never throws; logs warn on serialize failure.
|
|
622
|
+
*/
|
|
623
|
+
async serializeMirrorIfLive(taskId) {
|
|
624
|
+
const entry = this.entries.get(taskId);
|
|
625
|
+
if (!entry || !entry.mirror)
|
|
626
|
+
return null;
|
|
627
|
+
try {
|
|
628
|
+
const stable = await entry.mirror.serializeStable();
|
|
629
|
+
const { cols, rows } = entry.mirror.dimensions;
|
|
630
|
+
return {
|
|
631
|
+
version: "v2",
|
|
632
|
+
terminalVersion: this.expectedTerminalVersion,
|
|
633
|
+
cols,
|
|
634
|
+
rows,
|
|
635
|
+
data: stable,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
catch (err) {
|
|
639
|
+
// eslint-disable-next-line no-console
|
|
640
|
+
console.warn(`[pty-manager] live-mirror serialize failed for ${taskId}: ${err.message}`);
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* iterate-2026-05-18-inbox-terminal-prompts — decoded visible-viewport
|
|
646
|
+
* text of the live headless mirror, or `null` when there is no live
|
|
647
|
+
* mirror for `taskId` (no entry, or `mirror === null` — flag off /
|
|
648
|
+
* pty exited / external-terminal launch).
|
|
649
|
+
*
|
|
650
|
+
* Consumed by the `/api/external/inbox` route to detect a waiting
|
|
651
|
+
* `AskUserQuestion` picker — the picker is on-screen, never journaled
|
|
652
|
+
* to the JSONL. Synchronous + best-effort: never throws (it sits
|
|
653
|
+
* behind a 3 s inbox poll).
|
|
654
|
+
*/
|
|
655
|
+
peekTerminalText(taskId) {
|
|
656
|
+
const entry = this.entries.get(taskId);
|
|
657
|
+
if (!entry || !entry.mirror)
|
|
658
|
+
return null;
|
|
659
|
+
try {
|
|
660
|
+
return entry.mirror.getVisibleText();
|
|
661
|
+
}
|
|
662
|
+
catch {
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Iterate E (ADR-092) — persist the live headless mirror to disk via
|
|
668
|
+
* `SnapshotStore.write()`, but do NOT dispose the mirror. The pty
|
|
669
|
+
* stays alive so subsequent `pty.onData` chunks keep mirroring and
|
|
670
|
+
* subsequent `serializeMirrorIfLive()` calls still return fresh
|
|
671
|
+
* state.
|
|
672
|
+
*
|
|
673
|
+
* This is the "snapshot-on-detach" half of the ADR-092 fix: when the
|
|
674
|
+
* last WS subscriber detaches, the routes layer fires this so the
|
|
675
|
+
* next attach (potentially after server restart) finds an on-disk
|
|
676
|
+
* snapshot at `tryReadSnapshot`. Without it, a Hono restart loses
|
|
677
|
+
* every live-pty's state since the last `pty.kill`.
|
|
678
|
+
*
|
|
679
|
+
* The write goes through the shared ADR-096 preservation gate
|
|
680
|
+
* (`writeSnapshotPreservingLarger`, snapshot-preserve.ts) — SAME gate
|
|
681
|
+
* finalizeMirrorSnapshot uses. Before this was shared, flush wrote
|
|
682
|
+
* unconditionally and could clobber a richer on-disk snapshot with a
|
|
683
|
+
* thin mirror (Claude TUI alt-screen exit) on the 2nd detach→reopen
|
|
684
|
+
* cycle, blanking the scrollback. The gate skips the write when the
|
|
685
|
+
* new payload is substantially smaller than the existing one.
|
|
686
|
+
*
|
|
687
|
+
* CLAUDE.md rule 21 / ADR-092: flush is a KEEP-ALIVE path — it MUST
|
|
688
|
+
* NOT dispose the mirror (the pty stays alive so subsequent onData
|
|
689
|
+
* keeps mirroring). Only finalizeMirrorSnapshot disposes.
|
|
690
|
+
*
|
|
691
|
+
* No-op when:
|
|
692
|
+
* - no entry for `taskId`,
|
|
693
|
+
* - entry has no mirror (flag off),
|
|
694
|
+
* - no `snapshotStore` wired.
|
|
695
|
+
*
|
|
696
|
+
* Best-effort: never throws. Caller can fire-and-forget.
|
|
697
|
+
*/
|
|
698
|
+
async flushMirrorSnapshot(taskId) {
|
|
699
|
+
const entry = this.entries.get(taskId);
|
|
700
|
+
if (!entry || !entry.mirror || !this.snapshotStore)
|
|
701
|
+
return;
|
|
702
|
+
try {
|
|
703
|
+
const stable = await entry.mirror.serializeStable();
|
|
704
|
+
// D01 #2 — re-check the captured entry on THIS tick: a flush that parked
|
|
705
|
+
// at serializeStable across a delete's kill+clear must NOT re-land the file.
|
|
706
|
+
if (entry.tornDown)
|
|
707
|
+
return;
|
|
708
|
+
const { cols, rows } = entry.mirror.dimensions;
|
|
709
|
+
// ADR-096 shared preservation gate — do NOT clobber a richer on-disk
|
|
710
|
+
// snapshot with a thinner mirror (see snapshot-preserve.ts). Same gate
|
|
711
|
+
// as finalizeMirrorSnapshot. Mirror is NOT disposed here (rule 21).
|
|
712
|
+
//
|
|
713
|
+
// shouldProceed re-checks entry.tornDown SYNCHRONOUSLY immediately
|
|
714
|
+
// before the write enqueue — the helper's unqueued `await store.read`
|
|
715
|
+
// yields the loop after the guard above, so a delete cascade that lands
|
|
716
|
+
// in that gap (kill sets tornDown + clear() unlinks the snapshot) MUST
|
|
717
|
+
// abort this write, or it would resurrect the just-wiped secret-bearing
|
|
718
|
+
// snapshot (doubt-review HIGH, iterate-2026-07-12). finalize OMITS this
|
|
719
|
+
// predicate — its kill-path write is authoritative and must land.
|
|
720
|
+
await writeSnapshotPreservingLarger(this.snapshotStore, taskId, { cols, rows, data: stable }, { caller: "flushMirrorSnapshot", shouldProceed: () => !entry.tornDown });
|
|
721
|
+
}
|
|
722
|
+
catch (err) {
|
|
723
|
+
// eslint-disable-next-line no-console
|
|
724
|
+
console.warn(`[pty-manager] flushMirrorSnapshot failed for ${taskId}: ${err.message}`);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
/** Replace the placeholder subscription with the routes' real callbacks. */
|
|
728
|
+
subscribeForConnection(taskId, conn, sub) {
|
|
729
|
+
const entry = this.entries.get(taskId);
|
|
730
|
+
if (!entry)
|
|
731
|
+
return;
|
|
732
|
+
entry.connSubs.set(conn, sub);
|
|
733
|
+
if (!entry.pendingByConn.has(conn)) {
|
|
734
|
+
entry.pendingByConn.set(conn, { bytes: 0, queue: [] });
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Iterate E (ADR-092) — detach + count-after.
|
|
739
|
+
*
|
|
740
|
+
* Returns `{ remainingAttachCount }` so routes layer can decide on the
|
|
741
|
+
* snapshot-on-last-detach trigger atomically with the detach itself —
|
|
742
|
+
* closes the race the external code review (OpenAI HIGH #1) flagged
|
|
743
|
+
* in the original split-step design (check count → detach → check
|
|
744
|
+
* count again was vulnerable to concurrent attach landing in
|
|
745
|
+
* between).
|
|
746
|
+
*
|
|
747
|
+
* Detaches AND returns the post-detach count in one observation; the
|
|
748
|
+
* caller's "becameZero" branch is `remainingAttachCount === 0`. The
|
|
749
|
+
* underlying `connSubs.delete` is synchronous, so no inter-step
|
|
750
|
+
* window remains.
|
|
751
|
+
*/
|
|
752
|
+
detachAndCount(taskId, conn) {
|
|
753
|
+
this.detach(taskId, conn);
|
|
754
|
+
return { remainingAttachCount: this.attachCount(taskId) };
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Detach a WS conn. If conn was writer, the slot is freed and any
|
|
758
|
+
* remaining reader connection is promoted to writer (with onPromoteToWriter
|
|
759
|
+
* fired so the routes layer can notify that client).
|
|
760
|
+
*
|
|
761
|
+
* 2026-05-05 — last-detach NO LONGER kills the pty. The previous
|
|
762
|
+
* "no orphan tab leak" policy collided fundamentally with ADR-068-A1
|
|
763
|
+
* (Iterate 5) Replay-on-Attach: any TaskBoard ↔ TaskDetail navigation
|
|
764
|
+
* closes the WS, the server killed the pty, and re-attaching produced
|
|
765
|
+
* a brand-new shell with no claude session — the symptom users report
|
|
766
|
+
* as "Session bleibt nicht aktiv". Orphan management now relies on:
|
|
767
|
+
* - 30-min idle ceiling (touchIdle on every pty.onData)
|
|
768
|
+
* - explicit user "Stop terminal session" menu action
|
|
769
|
+
* - DELETE task cascade (Phase 4 Close-task-kills-pty)
|
|
770
|
+
* - server shutdown
|
|
771
|
+
* The pty's process tree is bounded, the scrollback file is bounded
|
|
772
|
+
* (1 MiB rotated), and the daily sweep TTL also catches abandoned
|
|
773
|
+
* scrollback for tasks whose pty has long since exited.
|
|
774
|
+
*/
|
|
775
|
+
detach(taskId, conn) {
|
|
776
|
+
// BEFORE the entry guard: accounts hang off the manager, not the entry, so a
|
|
777
|
+
// detach after the entry is gone (reaper / stop / delete) would leak one.
|
|
778
|
+
this.backpressure.release(conn);
|
|
779
|
+
const entry = this.entries.get(taskId);
|
|
780
|
+
if (!entry)
|
|
781
|
+
return;
|
|
782
|
+
// AC-3 cleanup (per Gemini #3 + OpenAI #12): if the detached conn
|
|
783
|
+
// held a pause stake (e.g. mid-replay watchdog eviction), release
|
|
784
|
+
// it so pty.resume fires and the refcount doesn't leak.
|
|
785
|
+
if (entry.pausedConns.has(conn)) {
|
|
786
|
+
this.resumeForConn(taskId, conn);
|
|
787
|
+
}
|
|
788
|
+
entry.connSubs.delete(conn);
|
|
789
|
+
entry.pendingByConn.delete(conn);
|
|
790
|
+
entry.bufferedExceededSince.delete(conn);
|
|
791
|
+
this.connCapability.delete(conn);
|
|
792
|
+
const wasWriter = entry.writer === conn;
|
|
793
|
+
if (wasWriter)
|
|
794
|
+
entry.writer = null;
|
|
795
|
+
if (wasWriter) {
|
|
796
|
+
// Promote the oldest remaining connection to writer (Map iteration
|
|
797
|
+
// order is insertion order). Closes the StrictMode double-mount race
|
|
798
|
+
// where the second WS opens BEFORE the first close arrives, takes
|
|
799
|
+
// reader role, and only learns about the freed writer slot via
|
|
800
|
+
// this promotion hook.
|
|
801
|
+
const next = entry.connSubs.keys().next();
|
|
802
|
+
if (!next.done) {
|
|
803
|
+
const promoted = next.value;
|
|
804
|
+
entry.writer = promoted;
|
|
805
|
+
const sub = entry.connSubs.get(promoted);
|
|
806
|
+
if (sub?.onPromoteToWriter) {
|
|
807
|
+
try {
|
|
808
|
+
sub.onPromoteToWriter();
|
|
809
|
+
}
|
|
810
|
+
catch {
|
|
811
|
+
/* ignore */
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
// Attachment-gating (2026-06-02): arm the grace iff that was the last client.
|
|
817
|
+
this.touchIdle(entry);
|
|
818
|
+
}
|
|
819
|
+
// --- internals -----------------------------------------------------------
|
|
820
|
+
cleanup(taskId) {
|
|
821
|
+
const entry = this.entries.get(taskId);
|
|
822
|
+
if (!entry)
|
|
823
|
+
return;
|
|
824
|
+
this.idleReaper.cancel(taskId);
|
|
825
|
+
this.entries.delete(taskId);
|
|
826
|
+
// ADR-088: finalize the headless mirror snapshot, then dispose. D01 #3 —
|
|
827
|
+
// store the promise on the entry so BOTH onExit and kill() await the SAME
|
|
828
|
+
// finalize (a synchronous onExit no longer drops kill()'s await). Failures
|
|
829
|
+
// never propagate — finalizeMirrorSnapshot is best-effort/infallible.
|
|
830
|
+
if (entry.mirror && this.snapshotStore) {
|
|
831
|
+
entry.finalizePromise = this.finalizeMirrorSnapshot(taskId, entry.mirror);
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
if (entry.mirror) {
|
|
835
|
+
try {
|
|
836
|
+
entry.mirror.dispose();
|
|
837
|
+
}
|
|
838
|
+
catch { /* ignore */ }
|
|
839
|
+
}
|
|
840
|
+
entry.finalizePromise = Promise.resolve();
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* ADR-088 — produce the M2 stable snapshot and persist it. Best-effort:
|
|
845
|
+
* a disk failure here MUST NOT crash kill / onExit. Logs a structured
|
|
846
|
+
* warn on failure so observability picks it up.
|
|
847
|
+
*
|
|
848
|
+
* Why this is detached + async: kill() / onExit are synchronous
|
|
849
|
+
* lifecycle hooks. The double-serialize cycle costs ~10 ms per the
|
|
850
|
+
* spike — small but non-zero. Detaching keeps the close path
|
|
851
|
+
* responsive and avoids back-pressuring the broadcaster.
|
|
852
|
+
*
|
|
853
|
+
* Iterate H (ADR-096) — the write goes through the shared preservation
|
|
854
|
+
* gate `writeSnapshotPreservingLarger` (snapshot-preserve.ts): if an
|
|
855
|
+
* existing on-disk snapshot is substantially larger than the about-to-
|
|
856
|
+
* write one (a Claude TUI alt-screen-exit clear yields a near-empty
|
|
857
|
+
* cell-state), the write is SKIPPED so the richer replay artifact
|
|
858
|
+
* survives. Same gate flushMirrorSnapshot now uses (iterate-2026-07-12
|
|
859
|
+
* de-dup). mirror.dispose() + releaseQueue still run in `finally` on
|
|
860
|
+
* every branch (skip or write).
|
|
861
|
+
*/
|
|
862
|
+
async finalizeMirrorSnapshot(taskId, mirror) {
|
|
863
|
+
try {
|
|
864
|
+
const stable = await mirror.serializeStable();
|
|
865
|
+
const { cols, rows } = mirror.dimensions;
|
|
866
|
+
if (this.snapshotStore) {
|
|
867
|
+
await writeSnapshotPreservingLarger(this.snapshotStore, taskId, { cols, rows, data: stable }, { caller: "finalizeMirrorSnapshot" });
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
catch (err) {
|
|
871
|
+
// eslint-disable-next-line no-console
|
|
872
|
+
console.warn(`[pty-manager] snapshot finalize failed for ${taskId}: ${err.message}`);
|
|
873
|
+
}
|
|
874
|
+
finally {
|
|
875
|
+
try {
|
|
876
|
+
mirror.dispose();
|
|
877
|
+
}
|
|
878
|
+
catch {
|
|
879
|
+
/* ignore */
|
|
880
|
+
}
|
|
881
|
+
// ADR-089 (external review gemini): release the snapshot-store's
|
|
882
|
+
// per-task write queue so the Map cannot grow unboundedly for
|
|
883
|
+
// long-lived processes that churn through many tasks. The
|
|
884
|
+
// queue's onIdle() is awaited internally so any concurrent
|
|
885
|
+
// write (rare — finalize is the only write surface) completes
|
|
886
|
+
// before the entry is dropped.
|
|
887
|
+
if (this.snapshotStore) {
|
|
888
|
+
try {
|
|
889
|
+
await this.snapshotStore.releaseQueue(taskId);
|
|
890
|
+
}
|
|
891
|
+
catch {
|
|
892
|
+
/* best-effort */
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
// Orphan grace is attachment-gated (idle-reaper.ts): armed only when no client is attached.
|
|
898
|
+
touchIdle(entry) {
|
|
899
|
+
this.idleReaper.touch(entry.meta.taskId, entry.connSubs.size);
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Deliver outgoing pty data to a single WS connection subscriber,
|
|
903
|
+
* applying drop-while-saturated backpressure: if the WS already has
|
|
904
|
+
* more than `wsBufferBytes` queued on the wire, drop the new chunk
|
|
905
|
+
* and fire `onBackpressure` TWICE per saturation episode — once when it
|
|
906
|
+
* opens and once when delivery resumes, the latter carrying the accurate
|
|
907
|
+
* episode total (policy: backpressure-telemetry.ts, iterate-2026-07-30).
|
|
908
|
+
*
|
|
909
|
+
* Note (vs external code-review F7): the spec said "drop-oldest", which
|
|
910
|
+
* needs a server-side drain hook (@hono/node-ws exposes none) plus a
|
|
911
|
+
* bufferedAmount-poll loop. Drop-while-saturated is simpler and cannot
|
|
912
|
+
* grow the server buffer unboundedly if drains stall. What that note
|
|
913
|
+
* called an acceptable loss window is no longer merely accepted: the
|
|
914
|
+
* client answers with a `resync` and the bytes come back as a full grid
|
|
915
|
+
* (ws-upgrade-handler.ts performResync).
|
|
916
|
+
*/
|
|
917
|
+
deliverWithBackpressure(entry, conn, sub, data) {
|
|
918
|
+
const live = conn.bufferedAmount;
|
|
919
|
+
const incoming = Buffer.byteLength(data, "utf8");
|
|
920
|
+
// AC-3b — per-conn capability stamp (post-code-review v3, openai-5).
|
|
921
|
+
if (!this.connCapability.has(conn)) {
|
|
922
|
+
this.connCapability.set(conn, typeof live === "number" ? "ok" : "missing");
|
|
923
|
+
}
|
|
924
|
+
// AC-3b — track when this conn's buffered backlog first exceeded
|
|
925
|
+
// the stuck threshold. The watchdog evicts based on this timestamp,
|
|
926
|
+
// NOT on pty.onData freshness (per external review v2 — gemini-2 +
|
|
927
|
+
// openai-9: a runaway pty would otherwise keep the writer pinned
|
|
928
|
+
// forever).
|
|
929
|
+
if (typeof live === "number") {
|
|
930
|
+
const exceeded = live > this.watchdogStuckThresholdBytes;
|
|
931
|
+
const prev = entry.bufferedExceededSince.get(conn);
|
|
932
|
+
if (exceeded && (prev === undefined || prev === null)) {
|
|
933
|
+
entry.bufferedExceededSince.set(conn, this.nowFn());
|
|
934
|
+
}
|
|
935
|
+
else if (!exceeded && prev !== null && prev !== undefined) {
|
|
936
|
+
entry.bufferedExceededSince.set(conn, null);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
// Saturated: chunk discarded, NEVER resent — client answers with `resync`.
|
|
940
|
+
// Drained: closes the episode with the accurate total. See backpressure-telemetry.ts.
|
|
941
|
+
const saturated = typeof live === "number" && live + incoming > this.wsBufferBytes;
|
|
942
|
+
const notice = saturated
|
|
943
|
+
? this.backpressure.onDrop(entry.meta.taskId, conn, incoming)
|
|
944
|
+
: this.backpressure.onDelivered(entry.meta.taskId, conn);
|
|
945
|
+
if (notice && sub.onBackpressure) {
|
|
946
|
+
try {
|
|
947
|
+
sub.onBackpressure(notice);
|
|
948
|
+
}
|
|
949
|
+
catch { /* ignore */ }
|
|
950
|
+
}
|
|
951
|
+
if (saturated)
|
|
952
|
+
return;
|
|
953
|
+
try {
|
|
954
|
+
sub.onData(data);
|
|
955
|
+
}
|
|
956
|
+
catch { /* ignore */ }
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* AC-3b watchdog tick. Walks every entry; for each one with a writer,
|
|
960
|
+
* checks how long the writer's WS bufferedAmount has been above the
|
|
961
|
+
* stuck threshold. Beyond `watchdogStuckDurationMs`, evict via
|
|
962
|
+
* `detach()` so the cleanup chain (pause refcount release + reader
|
|
963
|
+
* promotion + onPromoteToWriter) runs.
|
|
964
|
+
*
|
|
965
|
+
* Capability is tracked PER-CONN (post-code-review openai-5). A
|
|
966
|
+
* single legacy WS adapter without bufferedAmount no longer disables
|
|
967
|
+
* eviction for healthy conns sharing the same manager.
|
|
968
|
+
*/
|
|
969
|
+
watchdogTick() {
|
|
970
|
+
for (const [taskId, entry] of this.entries) {
|
|
971
|
+
const writer = entry.writer;
|
|
972
|
+
if (writer === null)
|
|
973
|
+
continue;
|
|
974
|
+
// Per-conn capability check.
|
|
975
|
+
if (this.connCapability.get(writer) === "missing")
|
|
976
|
+
continue;
|
|
977
|
+
const live = writer.bufferedAmount;
|
|
978
|
+
if (typeof live !== "number") {
|
|
979
|
+
// Stamp this writer as missing-capability + warn once globally.
|
|
980
|
+
this.connCapability.set(writer, "missing");
|
|
981
|
+
if (!this.capabilityWarnLogged) {
|
|
982
|
+
this.capabilityWarnLogged = true;
|
|
983
|
+
// eslint-disable-next-line no-console
|
|
984
|
+
console.warn("[pty-manager] watchdog disabled for one or more writers — WSContext does not expose bufferedAmount; falling back to ws.close-driven release for affected conns");
|
|
985
|
+
}
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
this.connCapability.set(writer, "ok");
|
|
989
|
+
// If the writer's backlog crossed threshold mid-tick (no prior
|
|
990
|
+
// delivery to record it via deliverWithBackpressure), record now.
|
|
991
|
+
const prev = entry.bufferedExceededSince.get(writer);
|
|
992
|
+
const exceeded = live > this.watchdogStuckThresholdBytes;
|
|
993
|
+
if (exceeded && (prev === undefined || prev === null)) {
|
|
994
|
+
entry.bufferedExceededSince.set(writer, this.nowFn());
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
if (!exceeded) {
|
|
998
|
+
if (prev !== null && prev !== undefined) {
|
|
999
|
+
entry.bufferedExceededSince.set(writer, null);
|
|
1000
|
+
}
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
// exceeded === true && prev !== null/undefined — check stuck duration.
|
|
1004
|
+
if (typeof prev === "number" && this.nowFn() - prev >= this.watchdogStuckDurationMs) {
|
|
1005
|
+
// Stuck — evict. detach() runs the pause-refcount cleanup +
|
|
1006
|
+
// promotes the next reader. Log so UAT can correlate.
|
|
1007
|
+
// eslint-disable-next-line no-console
|
|
1008
|
+
console.warn(`[pty-manager] watchdog evicting stuck writer for task ${taskId} (bufferedAmount=${live}B for ${this.nowFn() - prev}ms)`);
|
|
1009
|
+
this.detach(taskId, writer);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
//# sourceMappingURL=pty-manager.js.map
|