chainlesschain 0.166.77 → 0.166.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -7
- package/completions/_cc +1 -1
- package/completions/cc.bash +1 -1
- package/completions/cc.fish +1 -1
- package/completions/cc.ps1 +1 -1
- package/package.json +1 -1
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +10 -0
- package/src/assets/linux-subreaper/linux-arm64/supervisor +0 -0
- package/src/assets/linux-subreaper/linux-x64/manifest.json +10 -0
- package/src/assets/linux-subreaper/linux-x64/supervisor +0 -0
- package/src/assets/web-panel/assets/{AIOps-CpiCBhRh.js → AIOps-DSyZEY2G.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CnU-Xvx-.js → ActionButton-DhNyZ577.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BJW-CNtf.js → Analytics-HMMpxmyi.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-UFQJL3G9.js → AppLayout-CwKMM5Ie.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-Dr_IUDoY.js → Artifacts-CNzDmph7.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-aglWo6rr.js → Audit-DxWpUW3V.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Dm_TCiXC.js → BackgroundAgents-C1R2ST-P.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DUdX4-gA.js → Backup-B6biTaEV.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BTcLjy1o.js → BaseInput-CVnkD2Z5.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Bhy9qZrl.js → Chat-Dbewl4Je.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-D0WrJQaQ.js → ChatBubbleRenderer-DVZ6SwC5.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CblrPa21.js → Checkbox-BqqiF1JE.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-SEAHzOlr.js → Codegen-BVMkGumc.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DJz8iFOR.js → Col-DmiRHy96.js} +1 -1
- package/src/assets/web-panel/assets/{Community-RufKhcQt.js → Community-BDlpygqA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Dlifx08U.js → Compact-B2NME_m-.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DdhQWcHh.js → Compliance-BlzDLE-R.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BLNn6oOm.js → Cowork-C8raPybB.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dv-JWY2d.js → Cron-DA8Py0zP.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-k7Pz7O-b.js → Crosschain-DU2XyHqB.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C_10syUW.js → DID-Df098fT_.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CMyH6Szx.js → Dashboard-D1rT702x.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DjmD2Saa.js → Dropdown-BweXGo1O.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-36_IRUSo.js → EmailListRenderer-B9ShbgJH.js} +1 -1
- package/src/assets/web-panel/assets/EvolutionSettings-D75_iBoc.js +1 -0
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DEgf9kyP.js → FamilyGuardDashboard-DrznZFVs.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DGO1wQls.js → Federation-DQZ2adIA.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Doj1FmKW.js → FormItemContext-DN30Dowh.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-GzOU27iT.js → GenericCardRenderer-DRdt0j1e.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Dd7D1EDK.js → Git-wwRgStzk.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-6IY8LcjI.js → Governance-CI4Uw0Nh.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BB9gl8q1.js → Inference-5TuE2AJI.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CeCed5wd.js → KnowledgeGraph-oXyvmFSR.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CYOeHuaI.js → Logs-ewudSWuv.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-Bk8Zqc2R.js → MarkdownRenderer-Cz5nyufm.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-D-_xgHgM.js → Marketplace-DcZeWFrs.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-NnaUTF5t.js → McpTools-BcV9p1RS.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CndN9pMt.js → Memory-CMhYvugt.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BXblFmhY.js → MobileBridge-5RP2uwvT.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CC26Dm2k.js → MobileProjects-BIuM9Ze4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BhaqWCiW.js → Mtc-DmBszTDe.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-D6fktM2w.js → MtcAudit-Czdp5ZGt.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CFvahz7s.js → Multisig-DRWUuNr-.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Co73Ivya.js → NLProgramming-IRQQN0-c.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-B4g33L1o.js → Notes-CGhD1lW9.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-lXd17EbN.js → NotificationSettings-BtyPgQAh.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BiOB0GIi.js → OrderTableRenderer-yUaqwCty.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-JQ7_wQIA.js → Organization-C3kTVn5i.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-gwgJgozQ.js → Overflow-C2-0iZhq.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-D9L_0gie.js → P2P-CISoEUxA.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CbOPsFE9.js → PdhVaultBrowser-NfTVtvKQ.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-Cz42HNQf.js → Permissions-CfK0jJhm.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DjorYtrF.js → PersonalDataHub-OsVxxh_j.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-DyWPhmm5.js → Pipeline-DEmK_Ch2.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DyfLMl2j.js → Privacy-D61s73b0.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CyqDx1OE.js → ProjectInit-DkLF5gHb.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DsBw0Csy.js → ProjectSettings-BA7odTrI.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CRaSI25Z.js → Projects-CdI1GcAG.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CCI_klN-.js → Providers-D9BYyP1r.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DNsnlDdS.js → QrScannerModal-CXMItUDD.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-B9r2iaee.js → QuickAsk-8-tF9ffZ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BJDe03GR.js → Recommend-C2QGWOSi.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-J7JR3rRo.js → RemoteSession-B1M_v1lx.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-DR39AJeV.js → Reputation-D0WZHgv1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dh77m3NZ.js → Row-D6nf9-c-.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BUdg28nJ.js → RssFeed-ClZ6M-mN.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BNC7sGhj.js → Search-ZWkt8vuO.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DISENGI7.js → Security-BUlQut2m.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BBBGpJ1-.js → Services-Cd_3xJOc.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DMcIoIv2.js → Skeleton-C8e2-cHW.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-QAyFbI3r.js → Skills-DucVRJp1.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CSg5wLH7.js → Sla-Csr5pUwI.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DMPxTc5x.js → SpeechSettings-CXvyaRAT.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CPG7l30y.js → SyncSettings-KdsIcV5h.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Y1kOVvVL.js → Tasks-B-eHmJm9.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-jt9NagL0.js → Templates-BglVeaLK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DauuJICx.js → Tenant-BBbQ8qan.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-E9rnKeGq.js → Terminal-Ca_pCVBi.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Cc7z2APG.js → TimelineRenderer-YD6XG_vZ.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CV0UthRA.js → Tokens-2lRoeVli.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BFcJJGfu.js → Trigger-C-3XNAU9.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BAydbNJA.js → Trust-k-1PxdLG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DcOS6sqL.js → UkeySign-FG-qW646.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BLHJZvif.js → VideoEditing-BNOe1cmd.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-4QFLKHwW.js → Wallet-D5C3i2Kw.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BocEOOSj.js → WebAuthn-BOpztqd3.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-DCfF7D0B.js → WorkflowEditor-BpVrpUEv.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CxvfsYGr.js → chat-DIuMHb38.js} +1 -1
- package/src/assets/web-panel/assets/{colors-KtQB9DmQ.js → colors-CTFJPyzQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Dx4J6arN.js → compact-item-CONfmJcW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-Cq0olgzo.js → createContext-RkW5CRrz.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DaEBln_q.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Bf9-h3D-.js → hasIn-CW4XOjDC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CK2U_AHi.js → index-7wMSBCU7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMR-keCL.js → index-Akmg1WdH.js} +1 -1
- package/src/assets/web-panel/assets/{index-6dDDSCHD.js → index-B54qoVff.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmQuG0fr.js → index-B8r2t0Ch.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClUfSChI.js → index-B9XoBoty.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3xfiItB.js → index-B9aS2VLu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DQDR-_ai.js → index-BFs1Tznc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZWjYfWt.js → index-BKgaRT0b.js} +1 -1
- package/src/assets/web-panel/assets/{index-nmyDOh_b.js → index-BM7bOUvu.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_8UcDan.js → index-BMg2JFmW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9yaUHm9.js → index-BRZsPxNn.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz7f4Jaq.js → index-Bf19fKef.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpO6DKuu.js → index-BkAbfBF-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFdEDvFn.js → index-CBjb2A5R.js} +1 -1
- package/src/assets/web-panel/assets/{index-gt5N6VM_.js → index-CEZWQthY.js} +1 -1
- package/src/assets/web-panel/assets/{index-0fi8DIVQ.js → index-CFGLf88d.js} +1 -1
- package/src/assets/web-panel/assets/{index-hSzyoXri.js → index-CLUa68JH.js} +3 -3
- package/src/assets/web-panel/assets/{index-Cy865Hhq.js → index-CM2X25Er.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDAe1UmZ.js → index-CX4T2uKN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMcC4cbj.js → index-CixqfdRk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsCdl2Od.js → index-CkRojEip.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnU-dUWd.js → index-Cvmc3f25.js} +1 -1
- package/src/assets/web-panel/assets/{index-Br1mDMfO.js → index-D-lpTPS8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaIclw-E.js → index-D59GbWCN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBRlZdZj.js → index-DAESljiJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8WuPI-h.js → index-DDHUx4Yz.js} +1 -1
- package/src/assets/web-panel/assets/index-DG1mcYTa.js +1 -0
- package/src/assets/web-panel/assets/{index-D2BfSIrn.js → index-DP5pPedy.js} +1 -1
- package/src/assets/web-panel/assets/index-DRs5Pe8u.js +1 -0
- package/src/assets/web-panel/assets/{index-CqnggDos.js → index-DSlXL6Ju.js} +1 -1
- package/src/assets/web-panel/assets/{index-_FIz-0tp.js → index-DWCJnn2O.js} +1 -1
- package/src/assets/web-panel/assets/{index-BKtQyb2o.js → index-DWY7YwDu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfrFNMvz.js → index-DZTwP364.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHmdMKYl.js → index-DidrMvOx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BO6XvO-C.js → index-Dj3K5kHs.js} +1 -1
- package/src/assets/web-panel/assets/{index-iE9tqEN5.js → index-YaeGscSw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DI5bRqNG.js → index-YlMnFNOi.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cfsn7N18.js → index-asWsB34i.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeYP_kT9.js → index-gkjiFjQd.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DvzKJ6o5.js → initDefaultProps-BOr4igh3.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D1yTvmxW.js → motion-CK469n2j.js} +1 -1
- package/src/assets/web-panel/assets/{move-DxtSIF8K.js → move-DzjudlYQ.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BDsQgIHY.js → mtc-parser-BQaKUxC6.js} +1 -1
- package/src/assets/web-panel/assets/{omit-UVBJW8kh.js → omit-MciovCFD.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D4QqtTyX.js → pickAttrs-DDPJ4zAz.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-QXmu6vBf.js → placementArrow-Cznyb-jI.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-D-PmXO9M.js → responsiveObserve-AJK3SLzJ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CKbGxEZJ.js → slide-CPQ28wu7.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BbgXPkXm.js → statusUtils-CFjzvqf_.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DOr9rk68.js → styleChecker-BPyFfTuR.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-B2wO2r36.js → useFlexGapSupport-Dq7N6R05.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-SeVGBK34.js → useFs-C3GS0uTc.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DUJBCxjD.js → usePersonalDataHub-BhgIVzP_.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CF4EYsig.js → vnode-4ZD9-MDr.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-pRUs5pqI.js → zoom-Bx4MRVNW.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/command-manifest.json +1 -1
- package/src/commands/checkpoint.js +42 -7
- package/src/commands/orchestrate.js +85 -25
- package/src/commands/session-location.js +5 -0
- package/src/commands/session-show.js +109 -1
- package/src/commands/session.js +26 -2
- package/src/commands/tokens.js +9 -4
- package/src/data/changelog.json +13 -0
- package/src/generated/cc-agent-protocol.schema.json +71 -0
- package/src/harness/jsonl-session-store.js +357 -114
- package/src/harness/mcp-client.js +44 -1
- package/src/lib/agent-router.js +35 -7
- package/src/lib/app-server/server.js +14 -0
- package/src/lib/background-agent-supervisor.js +14 -4
- package/src/lib/causal-observability.js +64 -1
- package/src/lib/checkpoint-summary-projection.js +98 -0
- package/src/lib/checkpoint-timeline-authority.js +15 -51
- package/src/lib/claude-code-bridge.js +302 -60
- package/src/lib/codex-app-server-adapter.js +161 -35
- package/src/lib/cost-budget.js +4 -0
- package/src/lib/doctor-checkup.js +31 -2
- package/src/lib/durable-security-store.js +6 -1
- package/src/lib/evolution/evolution-artifact-identity.js +21 -0
- package/src/lib/evolution/evolution-artifact-ports.js +5 -11
- package/src/lib/evolution/evolution-eval-cohort-receipt-reconciliation.js +389 -0
- package/src/lib/execution-location-failure-site.js +63 -0
- package/src/lib/execution-location-target.js +102 -8
- package/src/lib/external-agent-adapters.js +2 -0
- package/src/lib/image-file-boundary.js +154 -0
- package/src/lib/image-input.js +16 -3
- package/src/lib/llm-pricing.js +114 -8
- package/src/lib/mcp-call-ledger-store.js +7 -2
- package/src/lib/model-capabilities.js +21 -10
- package/src/lib/model-context-catalog.js +60 -1
- package/src/lib/openai-responses.js +7 -1
- package/src/lib/orchestrator.js +8 -2
- package/src/lib/process-execution-broker/index.js +93 -1
- package/src/lib/process-execution-broker/linux-subreaper-artifact.js +120 -0
- package/src/lib/process-execution-broker/linux-subreaper-helper.js +344 -0
- package/src/lib/process-execution-broker/linux-subreaper-process.js +326 -0
- package/src/lib/process-execution-broker/linux-subreaper-supervisor.c +360 -0
- package/src/lib/process-execution-broker/owned-posix-process-group-worker.mjs +131 -0
- package/src/lib/process-execution-broker/owned-posix-process-group.js +267 -0
- package/src/lib/process-execution-broker/process-ownership-journal.js +297 -0
- package/src/lib/process-execution-broker/process-ownership-quarantine.js +91 -0
- package/src/lib/runtime-usage-ledger.js +3 -0
- package/src/lib/secure-fs.js +158 -42
- package/src/lib/session-branch-history.js +240 -0
- package/src/lib/session-history-origins.js +285 -0
- package/src/lib/session-input-receipt.js +110 -0
- package/src/lib/session-resource-budget.js +8 -8
- package/src/lib/session-transcript-history.js +456 -0
- package/src/lib/session-transcript-page.js +164 -0
- package/src/lib/session-transcript-reference.js +38 -0
- package/src/lib/session-usage.js +8 -0
- package/src/lib/token-tracker.js +32 -54
- package/src/lib/usage-pricing-context.js +93 -0
- package/src/runtime/headless-stream.js +202 -4
- package/src/assets/web-panel/assets/EvolutionSettings-CmtU0XmM.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CmgDNkoO.js +0 -1
- package/src/assets/web-panel/assets/index-BpIZ2S01.js +0 -1
- package/src/assets/web-panel/assets/index-C-VmKuyv.js +0 -1
|
@@ -91,6 +91,13 @@ import {
|
|
|
91
91
|
} from "../lib/execution-location-result.js";
|
|
92
92
|
import { normalizeExecutionLocationResultStoreReceipt } from "../lib/execution-location-result-store.js";
|
|
93
93
|
import skillInvocationReceipt from "@chainlesschain/session-core/skill-invocation-receipt";
|
|
94
|
+
import {
|
|
95
|
+
BRANCH_HISTORY_MESSAGE,
|
|
96
|
+
BRANCH_HISTORY_ORIGIN,
|
|
97
|
+
resolveSessionBranchHistory,
|
|
98
|
+
branchMessageDigest,
|
|
99
|
+
createBranchHistoryImport,
|
|
100
|
+
} from "../lib/session-branch-history.js";
|
|
94
101
|
|
|
95
102
|
const { verifySkillInvocationReceipt } = skillInvocationReceipt;
|
|
96
103
|
|
|
@@ -723,7 +730,13 @@ function getSessionsDir() {
|
|
|
723
730
|
if (claudeProjectDir) {
|
|
724
731
|
ensureClaudeProjectStorageTree(homeDir, claudeProjectDir);
|
|
725
732
|
} else {
|
|
726
|
-
|
|
733
|
+
// Another process may have created the directory but still be securing
|
|
734
|
+
// its Windows ACL. Join that check/repair before writing an anchored
|
|
735
|
+
// child; a later directory DACL change also changes child ctime.
|
|
736
|
+
ensurePrivateDirectory(dir, {
|
|
737
|
+
applyWindowsAcl: true,
|
|
738
|
+
failIfUnavailable: true,
|
|
739
|
+
});
|
|
727
740
|
}
|
|
728
741
|
securedSessionsDir = dir;
|
|
729
742
|
const secured = lstatSync(dir);
|
|
@@ -2187,10 +2200,36 @@ export function withSessionAuthorityTransaction(
|
|
|
2187
2200
|
let appendAttempts = 0;
|
|
2188
2201
|
let writerActive = true;
|
|
2189
2202
|
let writerPoisoned = false;
|
|
2203
|
+
let readingProjection = false;
|
|
2190
2204
|
let transactionRecoveryEvidence = null;
|
|
2191
2205
|
const writer = Object.freeze({
|
|
2192
2206
|
initialHeadHash: currentHeadHash,
|
|
2193
2207
|
currentHeadHash: () => currentHeadHash,
|
|
2208
|
+
readProjection(createProjection) {
|
|
2209
|
+
if (!writerActive || writerPoisoned || readingProjection) {
|
|
2210
|
+
throw new Error(
|
|
2211
|
+
"Session authority reader lease is closed or poisoned",
|
|
2212
|
+
);
|
|
2213
|
+
}
|
|
2214
|
+
readingProjection = true;
|
|
2215
|
+
try {
|
|
2216
|
+
const result = readVerifiedProjectionLocked(
|
|
2217
|
+
sessionId,
|
|
2218
|
+
filePath,
|
|
2219
|
+
createProjection,
|
|
2220
|
+
{
|
|
2221
|
+
expectedHeadHash: currentHeadHash,
|
|
2222
|
+
},
|
|
2223
|
+
);
|
|
2224
|
+
if (result && typeof result.then === "function")
|
|
2225
|
+
throw new TypeError(
|
|
2226
|
+
"Session authority projection must be synchronous",
|
|
2227
|
+
);
|
|
2228
|
+
return result;
|
|
2229
|
+
} finally {
|
|
2230
|
+
readingProjection = false;
|
|
2231
|
+
}
|
|
2232
|
+
},
|
|
2194
2233
|
retainRecoveryEvidence(evidence) {
|
|
2195
2234
|
if (!writerActive) {
|
|
2196
2235
|
const error = new Error(
|
|
@@ -2209,6 +2248,10 @@ export function withSessionAuthorityTransaction(
|
|
|
2209
2248
|
return transactionRecoveryEvidence;
|
|
2210
2249
|
},
|
|
2211
2250
|
appendAuthorityEvent(type, data) {
|
|
2251
|
+
if (readingProjection)
|
|
2252
|
+
throw new Error(
|
|
2253
|
+
"Cannot append during a session authority projection",
|
|
2254
|
+
);
|
|
2212
2255
|
if (!writerActive) {
|
|
2213
2256
|
const error = new Error(
|
|
2214
2257
|
`Session authority transaction is already closed: ${sessionId}`,
|
|
@@ -3491,7 +3534,9 @@ function assertVerifiedTranscriptAnchor(
|
|
|
3491
3534
|
* event reduction, and the independently persisted head/count check share one
|
|
3492
3535
|
* forward transcript pass. `finish()` may call `authority.readMessages()` to
|
|
3493
3536
|
* recover only the active context with bounded reverse IO (latest compact
|
|
3494
|
-
* checkpoint plus its suffix).
|
|
3537
|
+
* checkpoint plus its suffix). Its optional synchronous `replayEvents(visitor)`
|
|
3538
|
+
* revalidates the same head in another forward pass under this lock. That
|
|
3539
|
+
* visitor lease expires when finish returns; it cannot escape to later tasks.
|
|
3495
3540
|
*
|
|
3496
3541
|
* This removes the mandatory all-event array from resume projections. It does
|
|
3497
3542
|
* not make verification IO sublinear: authenticating a plain chained JSONL
|
|
@@ -3519,54 +3564,11 @@ export function readVerifiedProjection(
|
|
|
3519
3564
|
return withFileLock(
|
|
3520
3565
|
filePath,
|
|
3521
3566
|
() => {
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
) {
|
|
3528
|
-
throw new TypeError(
|
|
3529
|
-
"Verified transcript projection must provide accept() and finish()",
|
|
3530
|
-
);
|
|
3531
|
-
}
|
|
3532
|
-
if (!existsSync(filePath)) {
|
|
3533
|
-
const presence = getSessionPresence(sessionId);
|
|
3534
|
-
if (presence === SESSION_PRESENCE.TOMBSTONED) {
|
|
3535
|
-
throw sessionDeletedError(sessionId);
|
|
3536
|
-
}
|
|
3537
|
-
if (presence === SESSION_PRESENCE.MISSING_TRANSCRIPT) {
|
|
3538
|
-
throw unverifiedTranscriptError(
|
|
3539
|
-
sessionId,
|
|
3540
|
-
missingTranscriptVerification(),
|
|
3541
|
-
);
|
|
3542
|
-
}
|
|
3543
|
-
return projection.finish(
|
|
3544
|
-
Object.freeze({
|
|
3545
|
-
headHash: null,
|
|
3546
|
-
eventCount: 0,
|
|
3547
|
-
readMessages: () => [],
|
|
3548
|
-
}),
|
|
3549
|
-
);
|
|
3550
|
-
}
|
|
3551
|
-
const structure = createSessionTranscriptStructureProjection(sessionId);
|
|
3552
|
-
const verification = verifyTranscriptFile(filePath, {
|
|
3553
|
-
ioMetrics: options.ioMetrics,
|
|
3554
|
-
onVerifiedEvent: (event) => {
|
|
3555
|
-
structure.accept(event);
|
|
3556
|
-
projection.accept(event);
|
|
3557
|
-
},
|
|
3558
|
-
});
|
|
3559
|
-
assertVerifiedTranscriptAnchor(sessionId, verification);
|
|
3560
|
-
structure.finish({ assertValid: true });
|
|
3561
|
-
return projection.finish(
|
|
3562
|
-
Object.freeze({
|
|
3563
|
-
headHash: verification.lastHash,
|
|
3564
|
-
eventCount: verification.chainedEvents,
|
|
3565
|
-
readMessages: () =>
|
|
3566
|
-
rebuildVerifiedMessagesFromFile(filePath, {
|
|
3567
|
-
ioMetrics: options.messageIoMetrics,
|
|
3568
|
-
}),
|
|
3569
|
-
}),
|
|
3567
|
+
return readVerifiedProjectionLocked(
|
|
3568
|
+
sessionId,
|
|
3569
|
+
filePath,
|
|
3570
|
+
createProjection,
|
|
3571
|
+
options,
|
|
3570
3572
|
);
|
|
3571
3573
|
},
|
|
3572
3574
|
{
|
|
@@ -3580,6 +3582,119 @@ export function readVerifiedProjection(
|
|
|
3580
3582
|
);
|
|
3581
3583
|
}
|
|
3582
3584
|
|
|
3585
|
+
function readVerifiedProjectionLocked(
|
|
3586
|
+
sessionId,
|
|
3587
|
+
filePath,
|
|
3588
|
+
createProjection,
|
|
3589
|
+
options = {},
|
|
3590
|
+
) {
|
|
3591
|
+
const projection = createProjection();
|
|
3592
|
+
if (
|
|
3593
|
+
!projection ||
|
|
3594
|
+
typeof projection.accept !== "function" ||
|
|
3595
|
+
typeof projection.finish !== "function"
|
|
3596
|
+
) {
|
|
3597
|
+
throw new TypeError(
|
|
3598
|
+
"Verified transcript projection must provide accept() and finish()",
|
|
3599
|
+
);
|
|
3600
|
+
}
|
|
3601
|
+
if (!existsSync(filePath)) {
|
|
3602
|
+
const presence = getSessionPresence(sessionId);
|
|
3603
|
+
if (presence === SESSION_PRESENCE.TOMBSTONED) {
|
|
3604
|
+
throw sessionDeletedError(sessionId);
|
|
3605
|
+
}
|
|
3606
|
+
if (presence === SESSION_PRESENCE.MISSING_TRANSCRIPT) {
|
|
3607
|
+
throw unverifiedTranscriptError(
|
|
3608
|
+
sessionId,
|
|
3609
|
+
missingTranscriptVerification(),
|
|
3610
|
+
);
|
|
3611
|
+
}
|
|
3612
|
+
return projection.finish(
|
|
3613
|
+
Object.freeze({
|
|
3614
|
+
headHash: null,
|
|
3615
|
+
eventCount: 0,
|
|
3616
|
+
readMessages: () => [],
|
|
3617
|
+
}),
|
|
3618
|
+
);
|
|
3619
|
+
}
|
|
3620
|
+
const structure = createSessionTranscriptStructureProjection(sessionId);
|
|
3621
|
+
const verification = verifyTranscriptFile(filePath, {
|
|
3622
|
+
ioMetrics: options.ioMetrics,
|
|
3623
|
+
onVerifiedEvent: (event) => {
|
|
3624
|
+
structure.accept(event);
|
|
3625
|
+
projection.accept(event);
|
|
3626
|
+
},
|
|
3627
|
+
});
|
|
3628
|
+
assertVerifiedTranscriptAnchor(sessionId, verification);
|
|
3629
|
+
structure.finish({ assertValid: true });
|
|
3630
|
+
if (
|
|
3631
|
+
options.expectedHeadHash !== undefined &&
|
|
3632
|
+
verification.lastHash !== options.expectedHeadHash
|
|
3633
|
+
) {
|
|
3634
|
+
throw unverifiedTranscriptError(sessionId, verification);
|
|
3635
|
+
}
|
|
3636
|
+
let replayLeaseActive = true;
|
|
3637
|
+
let replaying = false;
|
|
3638
|
+
try {
|
|
3639
|
+
return projection.finish(
|
|
3640
|
+
Object.freeze({
|
|
3641
|
+
headHash: verification.lastHash,
|
|
3642
|
+
eventCount: verification.chainedEvents,
|
|
3643
|
+
readMessages: () =>
|
|
3644
|
+
rebuildVerifiedMessagesFromFile(filePath, {
|
|
3645
|
+
ioMetrics: options.messageIoMetrics,
|
|
3646
|
+
}),
|
|
3647
|
+
// Rewinds can discard the entire bounded page buffer. A second scan
|
|
3648
|
+
// collects the selected prefix without retaining the archive in RAM.
|
|
3649
|
+
// This lease is synchronous and expires before releasing the lock.
|
|
3650
|
+
replayEvents(accept) {
|
|
3651
|
+
if (!replayLeaseActive || replaying) {
|
|
3652
|
+
throw new Error(
|
|
3653
|
+
"Verified replay lease is closed or already in use",
|
|
3654
|
+
);
|
|
3655
|
+
}
|
|
3656
|
+
if (
|
|
3657
|
+
typeof accept !== "function" ||
|
|
3658
|
+
accept.constructor?.name === "AsyncFunction"
|
|
3659
|
+
) {
|
|
3660
|
+
throw new TypeError(
|
|
3661
|
+
"Verified replay requires a synchronous visitor",
|
|
3662
|
+
);
|
|
3663
|
+
}
|
|
3664
|
+
replaying = true;
|
|
3665
|
+
try {
|
|
3666
|
+
const replayStructure =
|
|
3667
|
+
createSessionTranscriptStructureProjection(sessionId);
|
|
3668
|
+
const replayVerification = verifyTranscriptFile(filePath, {
|
|
3669
|
+
ioMetrics: options.replayIoMetrics,
|
|
3670
|
+
onVerifiedEvent(event) {
|
|
3671
|
+
replayStructure.accept(event);
|
|
3672
|
+
const result = accept(event);
|
|
3673
|
+
if (result && typeof result.then === "function")
|
|
3674
|
+
throw new TypeError(
|
|
3675
|
+
"Verified replay visitor must be synchronous",
|
|
3676
|
+
);
|
|
3677
|
+
},
|
|
3678
|
+
});
|
|
3679
|
+
assertVerifiedTranscriptAnchor(sessionId, replayVerification);
|
|
3680
|
+
replayStructure.finish({ assertValid: true });
|
|
3681
|
+
if (
|
|
3682
|
+
replayVerification.lastHash !== verification.lastHash ||
|
|
3683
|
+
replayVerification.chainedEvents !== verification.chainedEvents
|
|
3684
|
+
) {
|
|
3685
|
+
throw unverifiedTranscriptError(sessionId, replayVerification);
|
|
3686
|
+
}
|
|
3687
|
+
} finally {
|
|
3688
|
+
replaying = false;
|
|
3689
|
+
}
|
|
3690
|
+
},
|
|
3691
|
+
}),
|
|
3692
|
+
);
|
|
3693
|
+
} finally {
|
|
3694
|
+
replayLeaseActive = false;
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3583
3698
|
/**
|
|
3584
3699
|
* Compatibility reader for consumers that still require the complete event
|
|
3585
3700
|
* array. New resume projections should use `readVerifiedProjection()` so their
|
|
@@ -6672,6 +6787,8 @@ export function forkSession(sourceId, options = {}) {
|
|
|
6672
6787
|
* @param {string|null} [params.parentTurnId]
|
|
6673
6788
|
* @param {Array<{role:string,content:any}>} [params.messages] pre-branch turns
|
|
6674
6789
|
* @param {{title?:string,provider?:string,model?:string}} [params.meta]
|
|
6790
|
+
* @param {object|null} [params.history] Scoped display-history capability from
|
|
6791
|
+
* createSessionTranscriptBranchProjection(), consumed under the source lock.
|
|
6675
6792
|
* @returns {{branchSessionId:string, created:boolean, messages:number}}
|
|
6676
6793
|
*/
|
|
6677
6794
|
export function createBranchSession({
|
|
@@ -6680,6 +6797,7 @@ export function createBranchSession({
|
|
|
6680
6797
|
parentTurnId = null,
|
|
6681
6798
|
messages = [],
|
|
6682
6799
|
meta = {},
|
|
6800
|
+
history = null,
|
|
6683
6801
|
} = {}) {
|
|
6684
6802
|
if (isUnsafeSessionId(branchSessionId)) {
|
|
6685
6803
|
throw new Error(
|
|
@@ -6692,6 +6810,18 @@ export function createBranchSession({
|
|
|
6692
6810
|
const canonicalMessages = projectCanonicalResumeMessages(messages, {
|
|
6693
6811
|
strict: true,
|
|
6694
6812
|
});
|
|
6813
|
+
const historyPlan = resolveSessionBranchHistory(
|
|
6814
|
+
history,
|
|
6815
|
+
parentSessionId,
|
|
6816
|
+
messages,
|
|
6817
|
+
);
|
|
6818
|
+
if (
|
|
6819
|
+
historyPlan &&
|
|
6820
|
+
(process.platform === "win32"
|
|
6821
|
+
? branchSessionId.toLowerCase() === parentSessionId.toLowerCase()
|
|
6822
|
+
: branchSessionId === parentSessionId)
|
|
6823
|
+
)
|
|
6824
|
+
throw new Error("A history branch must be independent of its parent");
|
|
6695
6825
|
if (
|
|
6696
6826
|
(parentSessionId !== null && typeof parentSessionId !== "string") ||
|
|
6697
6827
|
(parentTurnId !== null && typeof parentTurnId !== "string")
|
|
@@ -6746,36 +6876,10 @@ export function createBranchSession({
|
|
|
6746
6876
|
// Host prompts and tool scaffolding are re-established on resume. The
|
|
6747
6877
|
// canonical projection above retains only explicitly durable systems.
|
|
6748
6878
|
}
|
|
6749
|
-
const inputDigest = `sha256:${createHash("sha256")
|
|
6750
|
-
.update(JSON.stringify(digestEvents), "utf8")
|
|
6751
|
-
.digest("hex")}`;
|
|
6752
6879
|
const branchGenerationId = `generation-${createHash("sha256")
|
|
6753
6880
|
.update(`branch-generation\0${branchSessionId}`, "utf8")
|
|
6754
6881
|
.digest("hex")
|
|
6755
6882
|
.slice(0, 32)}`;
|
|
6756
|
-
const plannedEvents = digestEvents.map((event, index) =>
|
|
6757
|
-
index === 0
|
|
6758
|
-
? {
|
|
6759
|
-
...event,
|
|
6760
|
-
data: encodeSessionGenerationData(
|
|
6761
|
-
event.data,
|
|
6762
|
-
createSessionGenerationAuthority(
|
|
6763
|
-
branchSessionId,
|
|
6764
|
-
null,
|
|
6765
|
-
branchGenerationId,
|
|
6766
|
-
),
|
|
6767
|
-
),
|
|
6768
|
-
}
|
|
6769
|
-
: event,
|
|
6770
|
-
);
|
|
6771
|
-
plannedEvents.push({
|
|
6772
|
-
type: "session_branch_complete",
|
|
6773
|
-
data: {
|
|
6774
|
-
schemaVersion: 1,
|
|
6775
|
-
inputDigest,
|
|
6776
|
-
messageCount: count,
|
|
6777
|
-
},
|
|
6778
|
-
});
|
|
6779
6883
|
|
|
6780
6884
|
const filePath = sessionPath(branchSessionId);
|
|
6781
6885
|
const sessionsDir = getSessionsDir();
|
|
@@ -6800,8 +6904,22 @@ export function createBranchSession({
|
|
|
6800
6904
|
);
|
|
6801
6905
|
}
|
|
6802
6906
|
if (existsSync(filePath)) inspectPhysicalTail(filePath);
|
|
6907
|
+
const anchoredMeta = readSessionMeta(sessionsDir, branchSessionId);
|
|
6908
|
+
const anchoredCount = Number(anchoredMeta?.event_count);
|
|
6909
|
+
let anchoredEventHash = null;
|
|
6910
|
+
let existingBranchData = null;
|
|
6911
|
+
let seenEvents = 0;
|
|
6912
|
+
const existingHistoryImport = createBranchHistoryImport();
|
|
6803
6913
|
let verification = existsSync(filePath)
|
|
6804
|
-
? verifyTranscriptFile(filePath
|
|
6914
|
+
? verifyTranscriptFile(filePath, {
|
|
6915
|
+
onVerifiedEvent(event) {
|
|
6916
|
+
existingHistoryImport.accept(event);
|
|
6917
|
+
seenEvents += 1;
|
|
6918
|
+
if (seenEvents === anchoredCount) anchoredEventHash = event.hash;
|
|
6919
|
+
if (seenEvents === 2 && event.type === "session_branch")
|
|
6920
|
+
existingBranchData = event.data;
|
|
6921
|
+
},
|
|
6922
|
+
})
|
|
6805
6923
|
: {
|
|
6806
6924
|
status: TRANSCRIPT_CHAIN_STATUS.EMPTY,
|
|
6807
6925
|
chainedEvents: 0,
|
|
@@ -6815,27 +6933,146 @@ export function createBranchSession({
|
|
|
6815
6933
|
) {
|
|
6816
6934
|
throw unverifiedTranscriptError(branchSessionId, verification);
|
|
6817
6935
|
}
|
|
6818
|
-
const
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
const
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6936
|
+
const existingCount = verification.chainedEvents;
|
|
6937
|
+
// Keep old deterministic snapshot branches idempotent. Never retrofit
|
|
6938
|
+
// archive data into an already published branch's execution history.
|
|
6939
|
+
const inherited =
|
|
6940
|
+
existingBranchData && existingBranchData.history == null
|
|
6941
|
+
? null
|
|
6942
|
+
: historyPlan;
|
|
6943
|
+
if (inherited && inherited.contextOrigins.length !== count)
|
|
6944
|
+
throw new Error("Branch history context does not match its snapshot");
|
|
6945
|
+
const visitDigestEvents = (visit) => {
|
|
6946
|
+
visit(digestEvents[0]);
|
|
6947
|
+
visit(
|
|
6948
|
+
inherited
|
|
6949
|
+
? {
|
|
6950
|
+
...digestEvents[1],
|
|
6951
|
+
data: {
|
|
6952
|
+
...digestEvents[1].data,
|
|
6953
|
+
history: inherited.descriptor,
|
|
6954
|
+
},
|
|
6955
|
+
}
|
|
6956
|
+
: digestEvents[1],
|
|
6957
|
+
);
|
|
6958
|
+
if (inherited) {
|
|
6959
|
+
let inheritedRows = 0;
|
|
6960
|
+
inherited.visit((row) => {
|
|
6961
|
+
// Preserve all display text, independently of the page's 200K
|
|
6962
|
+
// rendering cap. Chunks also keep escaped text below record limits.
|
|
6963
|
+
const chunkChars = 128 * 1024;
|
|
6964
|
+
let offset = 0;
|
|
6965
|
+
let part = 0;
|
|
6966
|
+
do {
|
|
6967
|
+
let end = Math.min(row.text.length, offset + chunkChars);
|
|
6968
|
+
const before = row.text.charCodeAt(end - 1);
|
|
6969
|
+
const after = row.text.charCodeAt(end);
|
|
6970
|
+
if (
|
|
6971
|
+
before >= 0xd800 &&
|
|
6972
|
+
before <= 0xdbff &&
|
|
6973
|
+
after >= 0xdc00 &&
|
|
6974
|
+
after <= 0xdfff
|
|
6975
|
+
)
|
|
6976
|
+
end -= 1;
|
|
6977
|
+
visit({
|
|
6978
|
+
type: BRANCH_HISTORY_MESSAGE,
|
|
6979
|
+
data: {
|
|
6980
|
+
...row,
|
|
6981
|
+
text: row.text.slice(offset, end),
|
|
6982
|
+
part: part++,
|
|
6983
|
+
last: end === row.text.length,
|
|
6984
|
+
},
|
|
6985
|
+
});
|
|
6986
|
+
offset = end;
|
|
6987
|
+
} while (offset < row.text.length);
|
|
6988
|
+
inheritedRows += 1;
|
|
6989
|
+
});
|
|
6990
|
+
if (inheritedRows !== inherited.descriptor.totalMessages)
|
|
6991
|
+
throw new Error("Branch history source changed while streaming");
|
|
6992
|
+
}
|
|
6993
|
+
digestEvents.slice(2).forEach((event, index) => {
|
|
6994
|
+
visit(event);
|
|
6995
|
+
if (inherited)
|
|
6996
|
+
visit({
|
|
6997
|
+
type: BRANCH_HISTORY_ORIGIN,
|
|
6998
|
+
data: {
|
|
6999
|
+
messageDigest: branchMessageDigest(event.data),
|
|
7000
|
+
origin: inherited.contextOrigins[index],
|
|
7001
|
+
},
|
|
7002
|
+
});
|
|
7003
|
+
});
|
|
7004
|
+
};
|
|
7005
|
+
const inputHash = createHash("sha256").update("[");
|
|
7006
|
+
let plannedCount = 0;
|
|
7007
|
+
visitDigestEvents((event) => {
|
|
7008
|
+
if (plannedCount > 0) inputHash.update(",");
|
|
7009
|
+
inputHash.update(JSON.stringify(event));
|
|
7010
|
+
plannedCount += 1;
|
|
7011
|
+
});
|
|
7012
|
+
const inputDigest = `sha256:${inputHash.update("]").digest("hex")}`;
|
|
7013
|
+
plannedCount += 1;
|
|
7014
|
+
const visitPlannedEvents = (visit) => {
|
|
7015
|
+
let index = 0;
|
|
7016
|
+
visitDigestEvents((event) => {
|
|
7017
|
+
visit(
|
|
7018
|
+
index === 0
|
|
7019
|
+
? {
|
|
7020
|
+
...event,
|
|
7021
|
+
data: encodeSessionGenerationData(
|
|
7022
|
+
event.data,
|
|
7023
|
+
createSessionGenerationAuthority(
|
|
7024
|
+
branchSessionId,
|
|
7025
|
+
null,
|
|
7026
|
+
branchGenerationId,
|
|
7027
|
+
),
|
|
7028
|
+
),
|
|
7029
|
+
}
|
|
7030
|
+
: event,
|
|
7031
|
+
index++,
|
|
6837
7032
|
);
|
|
6838
7033
|
});
|
|
7034
|
+
visit(
|
|
7035
|
+
{
|
|
7036
|
+
type: "session_branch_complete",
|
|
7037
|
+
data: {
|
|
7038
|
+
schemaVersion: inherited ? 2 : 1,
|
|
7039
|
+
inputDigest,
|
|
7040
|
+
messageCount: count,
|
|
7041
|
+
},
|
|
7042
|
+
},
|
|
7043
|
+
index,
|
|
7044
|
+
);
|
|
7045
|
+
};
|
|
7046
|
+
const existingIterator =
|
|
7047
|
+
existingCount > 0 ? iterateCanonicalJsonlLinesSync(filePath) : null;
|
|
7048
|
+
let prefixMatches = true;
|
|
7049
|
+
try {
|
|
7050
|
+
if (existingCount > 0)
|
|
7051
|
+
visitPlannedEvents((planned, index) => {
|
|
7052
|
+
if (index >= existingCount) return;
|
|
7053
|
+
const record = existingIterator.next();
|
|
7054
|
+
if (record.done) {
|
|
7055
|
+
prefixMatches = false;
|
|
7056
|
+
return;
|
|
7057
|
+
}
|
|
7058
|
+
const event = parseCanonicalJsonlRecord(record.value.line);
|
|
7059
|
+
let plannedData = planned.data;
|
|
7060
|
+
// Existing v1 branches predate generation authority. They remain
|
|
7061
|
+
// idempotently readable; only newly created branches publish it.
|
|
7062
|
+
if (
|
|
7063
|
+
index === 0 &&
|
|
7064
|
+
event?.data?.[SESSION_GENERATION_AUTHORITY_FIELD] === undefined
|
|
7065
|
+
) {
|
|
7066
|
+
plannedData = { ...plannedData };
|
|
7067
|
+
delete plannedData[SESSION_GENERATION_AUTHORITY_FIELD];
|
|
7068
|
+
}
|
|
7069
|
+
prefixMatches &&=
|
|
7070
|
+
event.type === planned.type &&
|
|
7071
|
+
JSON.stringify(event.data) === JSON.stringify(plannedData);
|
|
7072
|
+
});
|
|
7073
|
+
} finally {
|
|
7074
|
+
existingIterator?.return?.();
|
|
7075
|
+
}
|
|
6839
7076
|
if (!prefixMatches) {
|
|
6840
7077
|
const error = new Error(
|
|
6841
7078
|
`Branch session conflicts with its deterministic input: ${branchSessionId}`,
|
|
@@ -6843,11 +7080,12 @@ export function createBranchSession({
|
|
|
6843
7080
|
error.code = "SESSION_BRANCH_CONFLICT";
|
|
6844
7081
|
throw error;
|
|
6845
7082
|
}
|
|
7083
|
+
if (existingCount >= plannedCount) existingHistoryImport.finish();
|
|
6846
7084
|
|
|
6847
7085
|
// Once the exact completion marker is anchored, later branch turns are
|
|
6848
7086
|
// outside the idempotent creation transaction. A replay resolves to that
|
|
6849
7087
|
// progressed branch, but an unanchored suffix is never re-blessed.
|
|
6850
|
-
if (
|
|
7088
|
+
if (existingCount > plannedCount) {
|
|
6851
7089
|
assertVerifiedTranscriptAnchor(branchSessionId, verification);
|
|
6852
7090
|
return { branchSessionId, created: false, messages: 0 };
|
|
6853
7091
|
}
|
|
@@ -6856,8 +7094,6 @@ export function createBranchSession({
|
|
|
6856
7094
|
// sidecar. Only advance an existing anchor along that exact prefix. Never
|
|
6857
7095
|
// lower or replace a higher/different anchor: doing so would legitimize a
|
|
6858
7096
|
// truncated deterministic branch and permanently discard later turns.
|
|
6859
|
-
const anchoredMeta = readSessionMeta(sessionsDir, branchSessionId);
|
|
6860
|
-
const anchoredCount = Number(anchoredMeta?.event_count);
|
|
6861
7097
|
const anchorMatchesCurrent =
|
|
6862
7098
|
anchoredMeta?.deleted !== true &&
|
|
6863
7099
|
anchoredMeta?.last_hash === verification.lastHash &&
|
|
@@ -6867,11 +7103,9 @@ export function createBranchSession({
|
|
|
6867
7103
|
Number.isSafeInteger(anchoredCount) &&
|
|
6868
7104
|
anchoredCount >= 0 &&
|
|
6869
7105
|
anchoredCount < verification.chainedEvents &&
|
|
6870
|
-
verification.chainedEvents === existingEvents.length &&
|
|
6871
7106
|
(anchoredCount === 0
|
|
6872
7107
|
? anchoredMeta?.last_hash === null
|
|
6873
|
-
:
|
|
6874
|
-
anchoredMeta?.last_hash);
|
|
7108
|
+
: anchoredEventHash === anchoredMeta?.last_hash);
|
|
6875
7109
|
if (
|
|
6876
7110
|
anchoredMeta !== null &&
|
|
6877
7111
|
!anchorMatchesCurrent &&
|
|
@@ -6879,13 +7113,22 @@ export function createBranchSession({
|
|
|
6879
7113
|
) {
|
|
6880
7114
|
throw unverifiedTranscriptError(branchSessionId, verification);
|
|
6881
7115
|
}
|
|
6882
|
-
|
|
7116
|
+
const unpublishedHistoryPrefix =
|
|
7117
|
+
inherited && existingCount < plannedCount;
|
|
7118
|
+
// A second failed attempt must not make an incomplete history branch
|
|
7119
|
+
// resumable. Its first usable sidecar/witness is published only after the
|
|
7120
|
+
// complete archive, execution snapshot and completion digest validate.
|
|
7121
|
+
if (
|
|
7122
|
+
existsSync(filePath) &&
|
|
7123
|
+
!anchorMatchesCurrent &&
|
|
7124
|
+
!unpublishedHistoryPrefix
|
|
7125
|
+
) {
|
|
6883
7126
|
rebuildSessionMetaUnlocked(sessionsDir, branchSessionId, filePath);
|
|
6884
7127
|
}
|
|
6885
|
-
if (
|
|
7128
|
+
if (existingCount > 0 && !unpublishedHistoryPrefix) {
|
|
6886
7129
|
assertVerifiedTranscriptAnchor(branchSessionId, verification);
|
|
6887
7130
|
}
|
|
6888
|
-
if (
|
|
7131
|
+
if (existingCount === plannedCount) {
|
|
6889
7132
|
return { branchSessionId, created: false, messages: 0 };
|
|
6890
7133
|
}
|
|
6891
7134
|
|
|
@@ -6893,12 +7136,8 @@ export function createBranchSession({
|
|
|
6893
7136
|
let expectedTranscriptState = existsSync(filePath)
|
|
6894
7137
|
? readPhysicalTranscriptState(filePath)
|
|
6895
7138
|
: null;
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
index < plannedEvents.length;
|
|
6899
|
-
index += 1
|
|
6900
|
-
) {
|
|
6901
|
-
const planned = plannedEvents[index];
|
|
7139
|
+
visitPlannedEvents((planned, index) => {
|
|
7140
|
+
if (index < existingCount) return;
|
|
6902
7141
|
const core = {
|
|
6903
7142
|
type: planned.type,
|
|
6904
7143
|
timestamp: Date.now(),
|
|
@@ -6914,12 +7153,16 @@ export function createBranchSession({
|
|
|
6914
7153
|
expectedTranscriptState,
|
|
6915
7154
|
);
|
|
6916
7155
|
prevHash = hash;
|
|
6917
|
-
}
|
|
7156
|
+
});
|
|
6918
7157
|
|
|
6919
|
-
|
|
7158
|
+
const completedHistoryImport = createBranchHistoryImport();
|
|
7159
|
+
verification = verifyTranscriptFile(filePath, {
|
|
7160
|
+
onVerifiedEvent: (event) => completedHistoryImport.accept(event),
|
|
7161
|
+
});
|
|
7162
|
+
completedHistoryImport.finish();
|
|
6920
7163
|
if (
|
|
6921
7164
|
verification.status !== TRANSCRIPT_CHAIN_STATUS.VERIFIED ||
|
|
6922
|
-
verification.chainedEvents !==
|
|
7165
|
+
verification.chainedEvents !== plannedCount ||
|
|
6923
7166
|
verification.lastHash !== prevHash ||
|
|
6924
7167
|
verification.malformedLines > 0 ||
|
|
6925
7168
|
verification.truncatedTail
|
|
@@ -3525,13 +3525,16 @@ export class MCPClient extends EventEmitter {
|
|
|
3525
3525
|
* Call a tool on a specific server. If the server has a registered
|
|
3526
3526
|
* reconnector and the call fails with a connection-shaped error (server
|
|
3527
3527
|
* restarted / token rotated / entry dropped), re-resolve the config,
|
|
3528
|
-
* reconnect, and retry the call exactly once.
|
|
3528
|
+
* reconnect, and retry the call exactly once. HTTP 404 only reinitializes
|
|
3529
|
+
* the connection: an already-dispatched tool has an unknown outcome and
|
|
3530
|
+
* must never be retried automatically just because its session expired.
|
|
3529
3531
|
* @param {string} serverName - Server name
|
|
3530
3532
|
* @param {string} toolName - Tool name
|
|
3531
3533
|
* @param {object} args - Tool arguments
|
|
3532
3534
|
* @param {{signal?: AbortSignal, dispatchAdmission?: Function}} options - Host-owned cancellation and dispatch authority
|
|
3533
3535
|
*/
|
|
3534
3536
|
async callTool(serverName, toolName, args = {}, options = {}) {
|
|
3537
|
+
const attemptedEntry = this.servers.get(serverName);
|
|
3535
3538
|
if (options?.signal?.aborted) {
|
|
3536
3539
|
throw mcpRequestAbortedError(
|
|
3537
3540
|
serverName,
|
|
@@ -3542,6 +3545,43 @@ export class MCPClient extends EventEmitter {
|
|
|
3542
3545
|
try {
|
|
3543
3546
|
return await this._callToolOnce(serverName, toolName, args, options);
|
|
3544
3547
|
} catch (err) {
|
|
3548
|
+
if (
|
|
3549
|
+
!isMcpRpcError(err) &&
|
|
3550
|
+
safeRpcProperty(err, "code") === "CC_MCP_HTTP_STATUS" &&
|
|
3551
|
+
safeRpcProperty(err, "status") === 404 &&
|
|
3552
|
+
attemptedEntry?.httpUrl &&
|
|
3553
|
+
!options?.signal?.aborted
|
|
3554
|
+
) {
|
|
3555
|
+
// Both a stateless endpoint's transient 404 and an expired MCP
|
|
3556
|
+
// session may recover through initialize. The HTTP status alone is
|
|
3557
|
+
// insufficient evidence to replay a possibly effectful tool call.
|
|
3558
|
+
const current = this.servers.get(serverName);
|
|
3559
|
+
const recovered =
|
|
3560
|
+
current && current !== attemptedEntry
|
|
3561
|
+
? current.state === ServerState.CONNECTED
|
|
3562
|
+
: this._reconnecting.has(serverName) ||
|
|
3563
|
+
(current && !current._httpDiscardStopping)
|
|
3564
|
+
? await this._tryReconnect(serverName, {
|
|
3565
|
+
connectAuthRetryUsed: true,
|
|
3566
|
+
expectedEntry: attemptedEntry,
|
|
3567
|
+
})
|
|
3568
|
+
: false;
|
|
3569
|
+
const failure = mcpTransportError(
|
|
3570
|
+
"CC_MCP_HTTP_TOOL_OUTCOME_UNKNOWN",
|
|
3571
|
+
recovered
|
|
3572
|
+
? "MCP HTTP connection was reinitialized after HTTP 404; the tool outcome is unknown and was not replayed"
|
|
3573
|
+
: "MCP HTTP 404 recovery failed; the tool outcome is unknown and was not replayed",
|
|
3574
|
+
{
|
|
3575
|
+
transport: attemptedEntry.transportKind,
|
|
3576
|
+
url: attemptedEntry.config?.url,
|
|
3577
|
+
status: 404,
|
|
3578
|
+
dispatched: true,
|
|
3579
|
+
outcomeUnknown: true,
|
|
3580
|
+
},
|
|
3581
|
+
);
|
|
3582
|
+
failure.connectionRecovered = Boolean(recovered);
|
|
3583
|
+
throw failure;
|
|
3584
|
+
}
|
|
3545
3585
|
if (
|
|
3546
3586
|
mcpRpcControlData.has(err) &&
|
|
3547
3587
|
(await this._resolveRequiredUrlElicitations(serverName, err))
|
|
@@ -3734,6 +3774,9 @@ export class MCPClient extends EventEmitter {
|
|
|
3734
3774
|
const p = (async () => {
|
|
3735
3775
|
try {
|
|
3736
3776
|
const currentEntry = this.servers.get(name) || null;
|
|
3777
|
+
if (options.expectedEntry && currentEntry !== options.expectedEntry) {
|
|
3778
|
+
return currentEntry?.state === ServerState.CONNECTED;
|
|
3779
|
+
}
|
|
3737
3780
|
const currentConfig = currentEntry?.config || null;
|
|
3738
3781
|
// Resource subscriptions belong to the logical client/server
|
|
3739
3782
|
// relationship, not to one transport connection. Snapshot them before
|