chainlesschain 0.166.76 → 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 +19 -8
- 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-CNaejClV.js → AIOps-DSyZEY2G.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CF_CVKT_.js → ActionButton-DhNyZ577.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-a3Z6J7gv.js → Analytics-HMMpxmyi.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BFtWEnct.js → AppLayout-CwKMM5Ie.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BQ8oeMvX.js → Artifacts-CNzDmph7.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-NOo9bmgk.js → Audit-DxWpUW3V.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-QRc7F3Tp.js → BackgroundAgents-C1R2ST-P.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-UlVgLn2P.js → Backup-B6biTaEV.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BER9LJAr.js → BaseInput-CVnkD2Z5.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Z8z2Fdfz.js → Chat-Dbewl4Je.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DdmuBrdQ.js → ChatBubbleRenderer-DVZ6SwC5.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CXPgyB8L.js → Checkbox-BqqiF1JE.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-D1vOsLJ7.js → Codegen-BVMkGumc.js} +1 -1
- package/src/assets/web-panel/assets/{Col-ucLnUJC7.js → Col-DmiRHy96.js} +1 -1
- package/src/assets/web-panel/assets/{Community-COsV9n9t.js → Community-BDlpygqA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-D0EkBEYh.js → Compact-B2NME_m-.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-C8w9SUjt.js → Compliance-BlzDLE-R.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CmVaKEJS.js → Cowork-C8raPybB.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-B_FiMMV2.js → Cron-DA8Py0zP.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-ByO3YL8N.js → Crosschain-DU2XyHqB.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DwpsLM9r.js → DID-Df098fT_.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B_8IUvM_.js → Dashboard-D1rT702x.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Bo6WQkuX.js → Dropdown-BweXGo1O.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C9LiQesx.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-Dst6VUnO.js → FamilyGuardDashboard-DrznZFVs.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Ci7CoLkJ.js → Federation-DQZ2adIA.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-bdDyUhUM.js → FormItemContext-DN30Dowh.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CrRwOUVi.js → GenericCardRenderer-DRdt0j1e.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Dis8h-Z-.js → Git-wwRgStzk.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DX7OH5w2.js → Governance-CI4Uw0Nh.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Cavv_Q_Z.js → Inference-5TuE2AJI.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cck8tnZn.js → KnowledgeGraph-oXyvmFSR.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BQt3aGMu.js → Logs-ewudSWuv.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Cz5nyufm.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CdmAKNSN.js → Marketplace-DcZeWFrs.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BUlJdyPY.js → McpTools-BcV9p1RS.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DQRTQDr-.js → Memory-CMhYvugt.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C0dnxgsl.js → MobileBridge-5RP2uwvT.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DQ5Gy7fa.js → MobileProjects-BIuM9Ze4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Xl7u2isS.js → Mtc-DmBszTDe.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-C3KX0574.js → MtcAudit-Czdp5ZGt.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-CHEhfkn8.js → Multisig-DRWUuNr-.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Ce38mH-8.js → NLProgramming-IRQQN0-c.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-C7nuTd7c.js → Notes-CGhD1lW9.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-BcoEcyq8.js → NotificationSettings-BtyPgQAh.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-D-t5hKkx.js → OrderTableRenderer-yUaqwCty.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-C_BGi4xi.js → Organization-C3kTVn5i.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BodhraDq.js → Overflow-C2-0iZhq.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BTa3sAIy.js → P2P-CISoEUxA.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BjZslP9T.js → PdhVaultBrowser-NfTVtvKQ.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-u3UDzGef.js → Permissions-CfK0jJhm.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-Cb7ybv65.js → PersonalDataHub-OsVxxh_j.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-CS24k-P7.js → Pipeline-DEmK_Ch2.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Ddj6Kqs7.js → Privacy-D61s73b0.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Bg5uQIgg.js → ProjectInit-DkLF5gHb.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-AmPR8T12.js → ProjectSettings-BA7odTrI.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DGSbAyTK.js → Projects-CdI1GcAG.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Dc1C3lbB.js → Providers-D9BYyP1r.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-Cv3HXpce.js → QrScannerModal-CXMItUDD.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-3dpbXARb.js → QuickAsk-8-tF9ffZ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-5aGRVnJK.js → Recommend-C2QGWOSi.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-CTS2jx0p.js → RemoteSession-B1M_v1lx.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BxaKI0hY.js → Reputation-D0WZHgv1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-C23uQdwd.js → Row-D6nf9-c-.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-ssX9YOhm.js → RssFeed-ClZ6M-mN.js} +3 -3
- package/src/assets/web-panel/assets/{Search-C24GogII.js → Search-ZWkt8vuO.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DdAcGKkk.js → Security-BUlQut2m.js} +3 -3
- package/src/assets/web-panel/assets/{Services-D0dCiEuq.js → Services-Cd_3xJOc.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DEY44o0v.js → Skeleton-C8e2-cHW.js} +1 -1
- package/src/assets/web-panel/assets/Skills-DucVRJp1.js +1 -0
- package/src/assets/web-panel/assets/{Sla-B_bbo2vB.js → Sla-Csr5pUwI.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-CXvyaRAT.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-CpIXql0Z.js → SyncSettings-KdsIcV5h.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-B-eHmJm9.js +1 -0
- package/src/assets/web-panel/assets/{Templates-C_44nVPq.js → Templates-BglVeaLK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CyZ2ey2P.js → Tenant-BBbQ8qan.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DwGNHWnp.js → Terminal-Ca_pCVBi.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BTAHNkqU.js → TimelineRenderer-YD6XG_vZ.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DCJWkUwc.js → Tokens-2lRoeVli.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BCjDO9-E.js → Trigger-C-3XNAU9.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-_YS8WbRb.js → Trust-k-1PxdLG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-m6R8xGq7.js → UkeySign-FG-qW646.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-xOfk29c4.js → VideoEditing-BNOe1cmd.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B1Gw0awc.js → Wallet-D5C3i2Kw.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CHK5ft18.js → WebAuthn-BOpztqd3.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-DEp1ukQQ.js → WorkflowEditor-BpVrpUEv.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B2sFizun.js → chat-DIuMHb38.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DJ17YV1E.js → colors-CTFJPyzQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CBhJJgEC.js → compact-item-CONfmJcW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BZkpvNJy.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-C0ZKo6Ei.js → hasIn-CW4XOjDC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bc3vVG2X.js → index-7wMSBCU7.js} +1 -1
- package/src/assets/web-panel/assets/{index-vFMwiAN4.js → index-Akmg1WdH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOBXNMdh.js → index-B54qoVff.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLo8Ls9u.js → index-B8r2t0Ch.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7gv9dvb.js → index-B9XoBoty.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHtpMr1G.js → index-B9aS2VLu.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dxe0YlZe.js → index-BFs1Tznc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DumLgj1t.js → index-BKgaRT0b.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNp6HKFh.js → index-BM7bOUvu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DM0rWF6z.js → index-BMg2JFmW.js} +1 -1
- package/src/assets/web-panel/assets/{index-4oULtRqE.js → index-BRZsPxNn.js} +1 -1
- package/src/assets/web-panel/assets/{index-LPPe0val.js → index-Bf19fKef.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ctguam0K.js → index-BkAbfBF-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DrU5xVpF.js → index-CBjb2A5R.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCOr6Q27.js → index-CEZWQthY.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9gYiACH.js → index-CFGLf88d.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZW84cEf.js → index-CLUa68JH.js} +3 -3
- package/src/assets/web-panel/assets/{index-BoDUYyuo.js → index-CM2X25Er.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyiCbHeL.js → index-CX4T2uKN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW5o1Gar.js → index-CixqfdRk.js} +1 -1
- package/src/assets/web-panel/assets/{index-5K2z_tgO.js → index-CkRojEip.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOfPp5OV.js → index-Cvmc3f25.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdxJm-HB.js → index-D-lpTPS8.js} +1 -1
- package/src/assets/web-panel/assets/{index-8ueG48gl.js → index-D59GbWCN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMxOoITz.js → index-DAESljiJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLMr5KMd.js → index-DDHUx4Yz.js} +1 -1
- package/src/assets/web-panel/assets/index-DG1mcYTa.js +1 -0
- package/src/assets/web-panel/assets/{index-BGIvhCPl.js → index-DP5pPedy.js} +1 -1
- package/src/assets/web-panel/assets/index-DRs5Pe8u.js +1 -0
- package/src/assets/web-panel/assets/{index-DjdSFO0d.js → index-DSlXL6Ju.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cn5UzvgR.js → index-DWCJnn2O.js} +1 -1
- package/src/assets/web-panel/assets/{index-Chct9tL8.js → index-DWY7YwDu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEnd5fiz.js → index-DZTwP364.js} +1 -1
- package/src/assets/web-panel/assets/{index-KzNhWykP.js → index-DidrMvOx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpyrhOeh.js → index-Dj3K5kHs.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZlkPrxb.js → index-YaeGscSw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dfgxe-fw.js → index-YlMnFNOi.js} +1 -1
- package/src/assets/web-panel/assets/{index-IHgzfc1u.js → index-asWsB34i.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJkoJzTU.js → index-gkjiFjQd.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Cqfoq0TJ.js → initDefaultProps-BOr4igh3.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CIiVJHNu.js → motion-CK469n2j.js} +1 -1
- package/src/assets/web-panel/assets/{move-CFl1o_A8.js → move-DzjudlYQ.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DYoCxcZ1.js → mtc-parser-BQaKUxC6.js} +1 -1
- package/src/assets/web-panel/assets/{omit-C1P2q5TY.js → omit-MciovCFD.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BtTRpJtC.js → pickAttrs-DDPJ4zAz.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Cst2PcNM.js → placementArrow-Cznyb-jI.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CF5kalV2.js → responsiveObserve-AJK3SLzJ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CtHGqpXN.js → slide-CPQ28wu7.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-N__biHXf.js → statusUtils-CFjzvqf_.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DR8V_H-i.js → styleChecker-BPyFfTuR.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D274dqun.js → useFlexGapSupport-Dq7N6R05.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BwHLYU5b.js → useFs-C3GS0uTc.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BiA-hVqt.js → usePersonalDataHub-BhgIVzP_.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Bugl94dy.js → vnode-4ZD9-MDr.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DEFl8GCu.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 +26 -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/decision-layer/providers.js +84 -9
- package/src/lib/decision-layer/runtime.js +86 -49
- package/src/lib/direct-model-usage.js +14 -3
- 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-runner.js +8 -0
- package/src/runtime/headless-stream.js +202 -4
- package/src/assets/web-panel/assets/EvolutionSettings-Pc1IpRfk.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-BIUeeiVA.js +0 -1
- package/src/assets/web-panel/assets/Skills-CYt0Mu2W.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-a2BBjtTL.js +0 -1
- package/src/assets/web-panel/assets/Tasks-BYQKU-GL.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DZhu13vj.js +0 -1
- package/src/assets/web-panel/assets/index-C6iFyQYz.js +0 -1
- package/src/assets/web-panel/assets/index-CztThH9f.js +0 -1
package/src/lib/secure-fs.js
CHANGED
|
@@ -235,9 +235,62 @@ function assertNoLinkTraversal(
|
|
|
235
235
|
return target;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
// Reapplying an unchanged directory DACL propagates to existing children on
|
|
239
|
+
// Windows and changes their ctime, invalidating otherwise intact session
|
|
240
|
+
// witnesses. Serialize check-and-repair across processes and skip a verified
|
|
241
|
+
// owner-only ACL. Keep the post-repair inspection and all traversal guards.
|
|
242
|
+
const WINDOWS_ACL_IDEMPOTENT_REPAIR = String.raw`
|
|
243
|
+
function Test-CcOwnerOnlySecurity($item, $security) {
|
|
244
|
+
$owner = $security.GetOwner([System.Security.Principal.SecurityIdentifier]).Value
|
|
245
|
+
$rules = @($security.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]))
|
|
246
|
+
if ($owner -ne $sid.Value -or $rules.Count -ne 1 -or ($item.PSIsContainer -and -not $security.AreAccessRulesProtected)) { return $false }
|
|
247
|
+
$rule = $rules[0]
|
|
248
|
+
$inheritance = [System.Security.AccessControl.InheritanceFlags]::None
|
|
249
|
+
if ($item.PSIsContainer) {
|
|
250
|
+
$inheritance = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
|
|
251
|
+
}
|
|
252
|
+
return ($rule.IdentityReference.Value -eq $sid.Value -and
|
|
253
|
+
$rule.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow -and
|
|
254
|
+
(-not $item.PSIsContainer -or -not $rule.IsInherited) -and
|
|
255
|
+
$rule.InheritanceFlags -eq $inheritance -and
|
|
256
|
+
$rule.PropagationFlags -eq [System.Security.AccessControl.PropagationFlags]::None -and
|
|
257
|
+
($rule.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -eq [System.Security.AccessControl.FileSystemRights]::FullControl)
|
|
258
|
+
}
|
|
259
|
+
function Write-CcOwnerOnlyAcl($item, [string]$path) {
|
|
260
|
+
$script:ccAclStage = 'repair-lock'
|
|
261
|
+
[Console]::Error.WriteLine('CC_WINDOWS_ACL_STAGE=repair-lock')
|
|
262
|
+
$digest = [System.Security.Cryptography.SHA256]::Create()
|
|
263
|
+
try {
|
|
264
|
+
$key = [System.BitConverter]::ToString($digest.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($sid.Value + ':' + [System.IO.Path]::GetFullPath($path).ToUpperInvariant()))).Replace('-', '')
|
|
265
|
+
} finally { $digest.Dispose() }
|
|
266
|
+
$mutex = [System.Threading.Mutex]::new($false, ('Global\ChainlessChain.PrivateAcl.' + $key))
|
|
267
|
+
$held = $false
|
|
268
|
+
try {
|
|
269
|
+
try { $held = $mutex.WaitOne(10000) }
|
|
270
|
+
catch [System.Threading.AbandonedMutexException] { $held = $true }
|
|
271
|
+
if (-not $held) { throw 'Timed out waiting for owner-only ACL repair' }
|
|
272
|
+
Assert-CcNoReparseTraversal $path
|
|
273
|
+
$script:ccAclStage = 'repair-inspect'
|
|
274
|
+
[Console]::Error.WriteLine('CC_WINDOWS_ACL_STAGE=repair-inspect')
|
|
275
|
+
$current = Get-Item -LiteralPath $path -Force
|
|
276
|
+
$security = Read-CcAcl $path
|
|
277
|
+
if (Test-CcOwnerOnlySecurity $current $security) { return }
|
|
278
|
+
$script:ccAclStage = 'repair-write'
|
|
279
|
+
[Console]::Error.WriteLine('CC_WINDOWS_ACL_STAGE=repair-write')
|
|
280
|
+
Write-CcOwnerOnlyAclImpl $current $path
|
|
281
|
+
} finally {
|
|
282
|
+
if ($held) { $mutex.ReleaseMutex() }
|
|
283
|
+
$mutex.Dispose()
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
`;
|
|
287
|
+
|
|
238
288
|
const WINDOWS_ACL_SCRIPT = String.raw`
|
|
239
289
|
param([string]$target, [string]$operation)
|
|
240
290
|
$ErrorActionPreference = 'Stop'
|
|
291
|
+
$script:ccAclStage = 'initialize'
|
|
292
|
+
try {
|
|
293
|
+
[Console]::Error.WriteLine('CC_WINDOWS_ACL_STAGE=initialize')
|
|
241
294
|
$utf8 = [System.Text.UTF8Encoding]::new($false)
|
|
242
295
|
[Console]::OutputEncoding = $utf8
|
|
243
296
|
$sections =
|
|
@@ -263,7 +316,11 @@ function Assert-CcNoReparseTraversal([string]$path) {
|
|
|
263
316
|
}
|
|
264
317
|
}
|
|
265
318
|
|
|
319
|
+
$script:ccAclStage = 'traversal'
|
|
320
|
+
[Console]::Error.WriteLine('CC_WINDOWS_ACL_STAGE=traversal')
|
|
266
321
|
Assert-CcNoReparseTraversal $target
|
|
322
|
+
$script:ccAclStage = 'lookup'
|
|
323
|
+
[Console]::Error.WriteLine('CC_WINDOWS_ACL_STAGE=lookup')
|
|
267
324
|
$item = Get-Item -LiteralPath $target -Force
|
|
268
325
|
|
|
269
326
|
function Read-CcAcl([string]$path) {
|
|
@@ -273,7 +330,8 @@ function Read-CcAcl([string]$path) {
|
|
|
273
330
|
return [System.Security.AccessControl.FileSecurity]::new($path, $sections)
|
|
274
331
|
}
|
|
275
332
|
|
|
276
|
-
|
|
333
|
+
${WINDOWS_ACL_IDEMPOTENT_REPAIR}
|
|
334
|
+
function Write-CcOwnerOnlyAclImpl($item, [string]$path) {
|
|
277
335
|
$security = Read-CcAcl $path
|
|
278
336
|
$owner = $security.GetOwner([System.Security.Principal.SecurityIdentifier])
|
|
279
337
|
if ($owner.Value -ne $sid.Value) {
|
|
@@ -322,6 +380,8 @@ if ($operation -eq 'repair') {
|
|
|
322
380
|
Write-CcOwnerOnlyAcl $item $target
|
|
323
381
|
}
|
|
324
382
|
|
|
383
|
+
$script:ccAclStage = 'verify'
|
|
384
|
+
[Console]::Error.WriteLine('CC_WINDOWS_ACL_STAGE=verify')
|
|
325
385
|
$acl = Read-CcAcl $target
|
|
326
386
|
$owner = $acl.GetOwner([System.Security.Principal.SecurityIdentifier]).Value
|
|
327
387
|
$rules = @($acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]))
|
|
@@ -366,6 +426,18 @@ if ($ownerOnly) {
|
|
|
366
426
|
aceCount = $rules.Count
|
|
367
427
|
} | ConvertTo-Json -Compress
|
|
368
428
|
if (-not $ownerOnly) { exit 4 }
|
|
429
|
+
} catch {
|
|
430
|
+
# Emit only a fixed stage and the deepest native HRESULT. Never emit the
|
|
431
|
+
# exception message, target path or arbitrary PowerShell error record.
|
|
432
|
+
$failure = $_.Exception
|
|
433
|
+
while ($null -ne $failure.InnerException) { $failure = $failure.InnerException }
|
|
434
|
+
[pscustomobject]@{
|
|
435
|
+
ownerOnly = $false
|
|
436
|
+
failureStage = $script:ccAclStage
|
|
437
|
+
hresult = ('0x{0:X8}' -f $failure.HResult)
|
|
438
|
+
} | ConvertTo-Json -Compress
|
|
439
|
+
exit 5
|
|
440
|
+
}
|
|
369
441
|
`;
|
|
370
442
|
|
|
371
443
|
const WINDOWS_ACL_BATCH_SCRIPT = String.raw`
|
|
@@ -481,7 +553,8 @@ function Read-CcAcl([string]$path) {
|
|
|
481
553
|
return [System.Security.AccessControl.FileSecurity]::new($path, $sections)
|
|
482
554
|
}
|
|
483
555
|
|
|
484
|
-
|
|
556
|
+
${WINDOWS_ACL_IDEMPOTENT_REPAIR}
|
|
557
|
+
function Write-CcOwnerOnlyAclImpl($item, [string]$path) {
|
|
485
558
|
$security = Read-CcAcl $path
|
|
486
559
|
$owner = $security.GetOwner([System.Security.Principal.SecurityIdentifier])
|
|
487
560
|
if ($owner.Value -ne $sid.Value) {
|
|
@@ -614,19 +687,48 @@ export const _deps = {
|
|
|
614
687
|
};
|
|
615
688
|
|
|
616
689
|
// ACL repair starts a short PowerShell process on Windows. Cache successful
|
|
617
|
-
// production repairs by path for this process;
|
|
690
|
+
// production repairs by path and filesystem identity for this process; injected-dependency
|
|
618
691
|
// tests and repairPrivatePath() always execute and verify the operation.
|
|
619
|
-
const securedWindowsPaths = new
|
|
692
|
+
const securedWindowsPaths = new Map();
|
|
693
|
+
|
|
694
|
+
function windowsPathIdentity(target, deps) {
|
|
695
|
+
try {
|
|
696
|
+
const entry = deps.fs.lstatSync(target, { bigint: true });
|
|
697
|
+
if (entry.isSymbolicLink() || !entry.ino) return null;
|
|
698
|
+
return `${entry.dev}:${entry.ino}:${entry.birthtimeNs}`;
|
|
699
|
+
} catch {
|
|
700
|
+
// An unreliable Node stat must take the native verification path again.
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function rememberSecuredWindowsPath(target, deps) {
|
|
706
|
+
const identity = windowsPathIdentity(target, deps);
|
|
707
|
+
if (identity) securedWindowsPaths.set(target, identity);
|
|
708
|
+
else securedWindowsPaths.delete(target);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function isSecuredWindowsPath(target, deps) {
|
|
712
|
+
const cached = securedWindowsPaths.get(target);
|
|
713
|
+
return Boolean(cached && cached === windowsPathIdentity(target, deps));
|
|
714
|
+
}
|
|
620
715
|
const WINDOWS_ACL_BATCH_SIZE = 500;
|
|
621
716
|
const WINDOWS_ACL_TIMEOUT_ENV = "CC_SECURE_FS_WINDOWS_ACL_TIMEOUT_MS";
|
|
622
717
|
const MAX_WINDOWS_ACL_TIMEOUT_MS = 5 * 60_000;
|
|
623
|
-
const WINDOWS_ACL_TIMEOUT_RETRY_LIMIT = 1;
|
|
624
718
|
|
|
625
719
|
export function _windowsAclWorkingDirectory(
|
|
626
720
|
environment = process.env,
|
|
627
721
|
filesystem = fs,
|
|
628
722
|
) {
|
|
629
|
-
|
|
723
|
+
// Isolated profiles can make Windows PowerShell first-use caches relative
|
|
724
|
+
// to cwd. Contain those writes in application state, never the source tree.
|
|
725
|
+
const isolatedLocalTarget =
|
|
726
|
+
typeof environment.CC_EXECUTION_LOCATION_RUNNER_ID === "string" &&
|
|
727
|
+
environment.CC_EXECUTION_LOCATION_RUNNER_ID.length > 0 &&
|
|
728
|
+
typeof environment.CHAINLESSCHAIN_HOME === "string" &&
|
|
729
|
+
isNativeAbsolute(environment.CHAINLESSCHAIN_HOME);
|
|
730
|
+
if (!isFormalQualityHermeticRuntime(environment) && !isolatedLocalTarget)
|
|
731
|
+
return null;
|
|
630
732
|
const configuredHome = resolve(String(environment.CHAINLESSCHAIN_HOME));
|
|
631
733
|
try {
|
|
632
734
|
const entry = filesystem.lstatSync(configuredHome);
|
|
@@ -654,44 +756,25 @@ export function _resolveWindowsAclTimeout(
|
|
|
654
756
|
);
|
|
655
757
|
}
|
|
656
758
|
|
|
657
|
-
//
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
//
|
|
661
|
-
//
|
|
662
|
-
// or permission failure as a transient condition.
|
|
759
|
+
// Give one cold PowerShell process the full bounded operation budget. Splitting
|
|
760
|
+
// it into two shorter spawns repeatedly kills a slow startup before it can
|
|
761
|
+
// finish (and can leave less time than the native mutex's 10-second wait).
|
|
762
|
+
// A process that exhausts the budget remains a failure; never start another
|
|
763
|
+
// process with a fresh budget or accept an unverified ACL after a timeout.
|
|
663
764
|
function runWindowsAclCommand(deps, args, options) {
|
|
664
|
-
|
|
665
|
-
const retryTimeoutMs = Math.max(
|
|
666
|
-
1,
|
|
667
|
-
Math.floor(totalTimeoutMs / (WINDOWS_ACL_TIMEOUT_RETRY_LIMIT + 1)),
|
|
668
|
-
);
|
|
669
|
-
const firstTimeoutMs = Math.max(1, totalTimeoutMs - retryTimeoutMs);
|
|
670
|
-
let result = deps.spawnSync("powershell.exe", args, {
|
|
765
|
+
return deps.spawnSync("powershell.exe", args, {
|
|
671
766
|
...options,
|
|
672
|
-
timeout:
|
|
767
|
+
timeout: Math.max(1, Math.floor(Number(options?.timeout) || 1)),
|
|
673
768
|
});
|
|
674
|
-
for (
|
|
675
|
-
let attempt = 0;
|
|
676
|
-
result?.error?.code === "ETIMEDOUT" &&
|
|
677
|
-
attempt < WINDOWS_ACL_TIMEOUT_RETRY_LIMIT;
|
|
678
|
-
attempt += 1
|
|
679
|
-
) {
|
|
680
|
-
result = deps.spawnSync("powershell.exe", args, {
|
|
681
|
-
...options,
|
|
682
|
-
timeout: retryTimeoutMs,
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
return result;
|
|
686
769
|
}
|
|
687
770
|
|
|
688
771
|
function repairWindowsAclOnce(target, deps, options) {
|
|
689
772
|
const cacheable = !options.deps;
|
|
690
|
-
if (cacheable &&
|
|
773
|
+
if (cacheable && isSecuredWindowsPath(target, deps)) {
|
|
691
774
|
return { ok: true, platform: "win32", cached: true };
|
|
692
775
|
}
|
|
693
776
|
const result = windowsAcl(target, "repair", deps);
|
|
694
|
-
if (result.ok && cacheable)
|
|
777
|
+
if (result.ok && cacheable) rememberSecuredWindowsPath(target, deps);
|
|
695
778
|
return result;
|
|
696
779
|
}
|
|
697
780
|
|
|
@@ -724,21 +807,54 @@ function windowsAcl(target, operation, deps) {
|
|
|
724
807
|
details = null;
|
|
725
808
|
}
|
|
726
809
|
if (result?.error) {
|
|
727
|
-
|
|
810
|
+
const progress =
|
|
811
|
+
[
|
|
812
|
+
...String(result.stderr || "")
|
|
813
|
+
.slice(0, 8192)
|
|
814
|
+
.matchAll(
|
|
815
|
+
/^CC_WINDOWS_ACL_STAGE=(initialize|traversal|lookup|repair-lock|repair-inspect|repair-write|verify)\r?$/gmu,
|
|
816
|
+
),
|
|
817
|
+
].at(-1)?.[1] || "startup";
|
|
818
|
+
const stage =
|
|
819
|
+
result.error.code === "ETIMEDOUT" ? `timeout-${progress}` : "spawn";
|
|
820
|
+
return {
|
|
821
|
+
ok: false,
|
|
822
|
+
platform: "win32",
|
|
823
|
+
error: `owner-only ACL process failed [windows-acl:${stage}]`,
|
|
824
|
+
};
|
|
728
825
|
}
|
|
729
826
|
if (result?.status !== 0 || details?.ownerOnly !== true) {
|
|
730
827
|
return {
|
|
731
828
|
ok: false,
|
|
732
829
|
platform: "win32",
|
|
733
|
-
error:
|
|
734
|
-
String(result?.stderr || "").trim() ||
|
|
735
|
-
`owner-only ACL verification exited ${result?.status}`,
|
|
830
|
+
error: `owner-only ACL verification failed ${windowsAclFailureMarker(details)}`,
|
|
736
831
|
details,
|
|
737
832
|
};
|
|
738
833
|
}
|
|
739
834
|
return { ok: true, platform: "win32", details };
|
|
740
835
|
}
|
|
741
836
|
|
|
837
|
+
function windowsAclFailureMarker(details) {
|
|
838
|
+
const allowedStages = new Set([
|
|
839
|
+
"initialize",
|
|
840
|
+
"traversal",
|
|
841
|
+
"lookup",
|
|
842
|
+
"repair-lock",
|
|
843
|
+
"repair-inspect",
|
|
844
|
+
"repair-write",
|
|
845
|
+
"verify",
|
|
846
|
+
]);
|
|
847
|
+
if (
|
|
848
|
+
allowedStages.has(details?.failureStage) &&
|
|
849
|
+
/^0x[0-9a-f]{8}$/iu.test(details?.hresult || "")
|
|
850
|
+
) {
|
|
851
|
+
return `[windows-acl:${details.failureStage}:${details.hresult.toLowerCase()}]`;
|
|
852
|
+
}
|
|
853
|
+
return details?.ownerOnly === false
|
|
854
|
+
? "[windows-acl:verify]"
|
|
855
|
+
: "[windows-acl:output]";
|
|
856
|
+
}
|
|
857
|
+
|
|
742
858
|
function windowsAclBatch(targets, operation, deps, expectedKind = null) {
|
|
743
859
|
if (targets.length === 0) return [];
|
|
744
860
|
const workingDirectory = _windowsAclWorkingDirectory(process.env, deps.fs);
|
|
@@ -999,7 +1115,7 @@ export function repairPrivatePaths(targets, options = {}) {
|
|
|
999
1115
|
if (!options.deps) {
|
|
1000
1116
|
for (const result of results) {
|
|
1001
1117
|
if (result.ok && result.exists !== false) {
|
|
1002
|
-
|
|
1118
|
+
rememberSecuredWindowsPath(result.target, deps);
|
|
1003
1119
|
}
|
|
1004
1120
|
}
|
|
1005
1121
|
}
|
|
@@ -1064,7 +1180,7 @@ function repairPrivatePathAfterPreflight(target, options, deps) {
|
|
|
1064
1180
|
const result = windowsAcl(target, "repair", deps);
|
|
1065
1181
|
if (!result.ok)
|
|
1066
1182
|
throw new Error(result.error || "owner-only ACL repair failed");
|
|
1067
|
-
if (!options.deps)
|
|
1183
|
+
if (!options.deps) rememberSecuredWindowsPath(target, deps);
|
|
1068
1184
|
return result;
|
|
1069
1185
|
}
|
|
1070
1186
|
const stat = deps.fs.lstatSync(target);
|
|
@@ -1183,7 +1299,7 @@ export function ensurePrivateFile(target, options = {}) {
|
|
|
1183
1299
|
}
|
|
1184
1300
|
} else if (
|
|
1185
1301
|
options.applyWindowsAcl === true &&
|
|
1186
|
-
(options.deps || !
|
|
1302
|
+
(options.deps || !isSecuredWindowsPath(target, deps))
|
|
1187
1303
|
) {
|
|
1188
1304
|
const [result] = windowsAclBatch([String(target)], "repair", deps, "file");
|
|
1189
1305
|
if (result.errorCode === "EXPECTED_KIND_MISMATCH") {
|
|
@@ -1192,7 +1308,7 @@ export function ensurePrivateFile(target, options = {}) {
|
|
|
1192
1308
|
if (!result.ok && result.exists !== false && options.failIfUnavailable) {
|
|
1193
1309
|
throw new Error(result.error || `Could not secure ${target}`);
|
|
1194
1310
|
}
|
|
1195
|
-
if (result.ok && !options.deps)
|
|
1311
|
+
if (result.ok && !options.deps) rememberSecuredWindowsPath(target, deps);
|
|
1196
1312
|
}
|
|
1197
1313
|
return target;
|
|
1198
1314
|
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { canonicalDigest } from "@chainlesschain/context-memory-kernel";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { SESSION_GENERATION_AUTHORITY_FIELD } from "../harness/session-list-index.js";
|
|
4
|
+
import {
|
|
5
|
+
encodePersistedMessage,
|
|
6
|
+
decodeVerifiedPersistedMessage,
|
|
7
|
+
getDurableSystemMessageProvenance,
|
|
8
|
+
projectCanonicalResumeMessages,
|
|
9
|
+
} from "./session-message-provenance.js";
|
|
10
|
+
|
|
11
|
+
export const BRANCH_HISTORY_SCHEMA = "chainlesschain.session-branch-history/v1";
|
|
12
|
+
export const BRANCH_HISTORY_SYSTEM_ORIGINS_SCHEMA =
|
|
13
|
+
"chainlesschain.session-branch-history/v2";
|
|
14
|
+
export const BRANCH_HISTORY_MESSAGE = "session_history_message";
|
|
15
|
+
export const BRANCH_HISTORY_ORIGIN = "session_history_origin";
|
|
16
|
+
const capabilities = new WeakMap();
|
|
17
|
+
|
|
18
|
+
export const branchContextDigest = (messages) =>
|
|
19
|
+
canonicalDigest(
|
|
20
|
+
projectCanonicalResumeMessages(messages, { strict: true }).map(
|
|
21
|
+
encodePersistedMessage,
|
|
22
|
+
),
|
|
23
|
+
"chainlesschain.branch-history-context/v1",
|
|
24
|
+
);
|
|
25
|
+
export const branchMessageDigest = (message) =>
|
|
26
|
+
canonicalDigest(message, "chainlesschain.branch-history-message/v1");
|
|
27
|
+
|
|
28
|
+
// A caller cannot smuggle JSON-shaped history into a new authority chain. The
|
|
29
|
+
// capability exists only inside a verified source projection's synchronous lock.
|
|
30
|
+
export function withSessionBranchHistory(plan, task) {
|
|
31
|
+
if (typeof task !== "function" || task.constructor?.name === "AsyncFunction")
|
|
32
|
+
throw new TypeError("Branch history consumer must be synchronous");
|
|
33
|
+
const capability = Object.freeze({});
|
|
34
|
+
capabilities.set(capability, plan);
|
|
35
|
+
try {
|
|
36
|
+
const result = task(capability);
|
|
37
|
+
if (result && typeof result.then === "function")
|
|
38
|
+
throw new TypeError("Branch history consumer must be synchronous");
|
|
39
|
+
return result;
|
|
40
|
+
} finally {
|
|
41
|
+
capabilities.delete(capability);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolveSessionBranchHistory(
|
|
46
|
+
capability,
|
|
47
|
+
parentSessionId,
|
|
48
|
+
messages,
|
|
49
|
+
) {
|
|
50
|
+
if (capability == null) return null;
|
|
51
|
+
const plan = capabilities.get(capability);
|
|
52
|
+
if (
|
|
53
|
+
!plan ||
|
|
54
|
+
plan.parentSessionId !== parentSessionId ||
|
|
55
|
+
plan.contextDigest !== branchContextDigest(messages)
|
|
56
|
+
) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
"Branch history capability is expired or does not match the branch",
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return plan;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function createBranchHistoryImport() {
|
|
65
|
+
let start = null;
|
|
66
|
+
let index = 0;
|
|
67
|
+
let active = null;
|
|
68
|
+
const hash = (value) =>
|
|
69
|
+
typeof value === "string" && /^[a-f0-9]{64}$/u.test(value);
|
|
70
|
+
const integer = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
71
|
+
const invalid = () => {
|
|
72
|
+
const error = new Error(
|
|
73
|
+
"Branch display history is incomplete or inconsistent",
|
|
74
|
+
);
|
|
75
|
+
error.code = "SESSION_BRANCH_HISTORY_INVALID";
|
|
76
|
+
throw error;
|
|
77
|
+
};
|
|
78
|
+
const require = (condition) => {
|
|
79
|
+
if (!condition) invalid();
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
accept(event) {
|
|
83
|
+
index += 1;
|
|
84
|
+
if (index === 1 && event.type === "session_start") {
|
|
85
|
+
const data = { ...event.data };
|
|
86
|
+
delete data[SESSION_GENERATION_AUTHORITY_FIELD];
|
|
87
|
+
start = { type: event.type, data };
|
|
88
|
+
}
|
|
89
|
+
if (event.type === "session_branch" && event.data?.history != null) {
|
|
90
|
+
const descriptor = event.data.history;
|
|
91
|
+
require(
|
|
92
|
+
index === 2 &&
|
|
93
|
+
start &&
|
|
94
|
+
!active &&
|
|
95
|
+
[
|
|
96
|
+
BRANCH_HISTORY_SCHEMA,
|
|
97
|
+
BRANCH_HISTORY_SYSTEM_ORIGINS_SCHEMA,
|
|
98
|
+
].includes(descriptor.schema) &&
|
|
99
|
+
hash(descriptor.sourceGeneration) &&
|
|
100
|
+
integer(descriptor.totalMessages) &&
|
|
101
|
+
integer(descriptor.contextMessageCount) &&
|
|
102
|
+
descriptor.contextMessageCount <= 32768,
|
|
103
|
+
);
|
|
104
|
+
const coverage = descriptor.coverage;
|
|
105
|
+
require(
|
|
106
|
+
coverage?.kind === "from-origin"
|
|
107
|
+
? coverage.boundaryEvent === null && coverage.reason === null
|
|
108
|
+
: coverage?.kind === "snapshot-boundary" &&
|
|
109
|
+
hash(coverage.boundaryEvent) &&
|
|
110
|
+
[
|
|
111
|
+
"timeline-replacement",
|
|
112
|
+
"context-snapshot",
|
|
113
|
+
"branch-snapshot",
|
|
114
|
+
].includes(coverage.reason),
|
|
115
|
+
);
|
|
116
|
+
const digest = createHash("sha256").update(
|
|
117
|
+
`[${JSON.stringify(start)},${JSON.stringify({ type: event.type, data: event.data })}`,
|
|
118
|
+
);
|
|
119
|
+
active = {
|
|
120
|
+
descriptor,
|
|
121
|
+
digest,
|
|
122
|
+
rows: 0,
|
|
123
|
+
contexts: 0,
|
|
124
|
+
pending: null,
|
|
125
|
+
textParts: [],
|
|
126
|
+
textBytes: 0,
|
|
127
|
+
textIdentity: null,
|
|
128
|
+
contextStarted: false,
|
|
129
|
+
};
|
|
130
|
+
return { kind: "start", coverage };
|
|
131
|
+
}
|
|
132
|
+
if (!active) {
|
|
133
|
+
if (
|
|
134
|
+
[BRANCH_HISTORY_MESSAGE, BRANCH_HISTORY_ORIGIN].includes(
|
|
135
|
+
event.type,
|
|
136
|
+
) ||
|
|
137
|
+
(event.type === "session_branch_complete" &&
|
|
138
|
+
event.data?.schemaVersion === 2)
|
|
139
|
+
)
|
|
140
|
+
invalid();
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const state = active;
|
|
144
|
+
if (event.type === "session_branch_complete") {
|
|
145
|
+
require(
|
|
146
|
+
!state.pending &&
|
|
147
|
+
state.textParts.length === 0 &&
|
|
148
|
+
state.rows === state.descriptor.totalMessages &&
|
|
149
|
+
state.contexts === state.descriptor.contextMessageCount &&
|
|
150
|
+
event.data?.schemaVersion === 2 &&
|
|
151
|
+
event.data.messageCount === state.contexts &&
|
|
152
|
+
event.data.inputDigest ===
|
|
153
|
+
`sha256:${state.digest.update("]").digest("hex")}`,
|
|
154
|
+
);
|
|
155
|
+
active = null;
|
|
156
|
+
return { kind: "complete" };
|
|
157
|
+
}
|
|
158
|
+
state.digest.update(
|
|
159
|
+
`,${JSON.stringify({ type: event.type, data: event.data })}`,
|
|
160
|
+
);
|
|
161
|
+
if (event.type === BRANCH_HISTORY_MESSAGE) {
|
|
162
|
+
const row = event.data;
|
|
163
|
+
require(
|
|
164
|
+
!state.contextStarted &&
|
|
165
|
+
state.rows < state.descriptor.totalMessages &&
|
|
166
|
+
["user", "assistant", "tool"].includes(row?.role) &&
|
|
167
|
+
typeof row.text === "string" &&
|
|
168
|
+
row.text.length <= 128 * 1024 &&
|
|
169
|
+
row.part === state.textParts.length &&
|
|
170
|
+
typeof row.last === "boolean" &&
|
|
171
|
+
hash(row.sourceEventId) &&
|
|
172
|
+
integer(row.sourceItemIndex),
|
|
173
|
+
);
|
|
174
|
+
const identity = `${row.role}:${row.sourceEventId}:${row.sourceItemIndex}`;
|
|
175
|
+
require(state.textIdentity === null || state.textIdentity === identity);
|
|
176
|
+
state.textIdentity = identity;
|
|
177
|
+
state.textBytes += Buffer.byteLength(row.text);
|
|
178
|
+
require(
|
|
179
|
+
state.textBytes <= 16 * 1024 * 1024 && state.textParts.length < 256,
|
|
180
|
+
);
|
|
181
|
+
state.textParts.push(row.text);
|
|
182
|
+
if (!row.last) return { kind: "archive-part" };
|
|
183
|
+
const message = { role: row.role, content: state.textParts.join("") };
|
|
184
|
+
state.textParts = [];
|
|
185
|
+
state.textBytes = 0;
|
|
186
|
+
state.textIdentity = null;
|
|
187
|
+
state.rows += 1;
|
|
188
|
+
return { kind: "archive", message };
|
|
189
|
+
}
|
|
190
|
+
if (
|
|
191
|
+
["user_message", "assistant_message", "system"].includes(event.type)
|
|
192
|
+
) {
|
|
193
|
+
require(
|
|
194
|
+
!state.pending &&
|
|
195
|
+
state.textParts.length === 0 &&
|
|
196
|
+
state.rows === state.descriptor.totalMessages &&
|
|
197
|
+
state.contexts < state.descriptor.contextMessageCount &&
|
|
198
|
+
event.data?.role ===
|
|
199
|
+
{
|
|
200
|
+
user_message: "user",
|
|
201
|
+
assistant_message: "assistant",
|
|
202
|
+
system: "system",
|
|
203
|
+
}[event.type],
|
|
204
|
+
);
|
|
205
|
+
state.contextStarted = true;
|
|
206
|
+
state.pending = event.data;
|
|
207
|
+
return { kind: "context" };
|
|
208
|
+
}
|
|
209
|
+
if (event.type === BRANCH_HISTORY_ORIGIN) {
|
|
210
|
+
const message = state.pending;
|
|
211
|
+
const origin = event.data?.origin;
|
|
212
|
+
require(
|
|
213
|
+
message && event.data.messageDigest === branchMessageDigest(message),
|
|
214
|
+
);
|
|
215
|
+
require(
|
|
216
|
+
origin === null
|
|
217
|
+
? message.role === "system"
|
|
218
|
+
: origin &&
|
|
219
|
+
integer(origin.first) &&
|
|
220
|
+
integer(origin.last) &&
|
|
221
|
+
origin.first <= origin.last &&
|
|
222
|
+
origin.last < state.rows &&
|
|
223
|
+
(message.role !== "system" ||
|
|
224
|
+
(state.descriptor.schema ===
|
|
225
|
+
BRANCH_HISTORY_SYSTEM_ORIGINS_SCHEMA &&
|
|
226
|
+
getDurableSystemMessageProvenance(
|
|
227
|
+
decodeVerifiedPersistedMessage(message),
|
|
228
|
+
))),
|
|
229
|
+
);
|
|
230
|
+
state.contexts += 1;
|
|
231
|
+
state.pending = null;
|
|
232
|
+
return { kind: "origin", message, origin };
|
|
233
|
+
}
|
|
234
|
+
invalid();
|
|
235
|
+
},
|
|
236
|
+
finish() {
|
|
237
|
+
if (active) invalid();
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|