chainlesschain 0.163.7 → 0.164.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/package.json +5 -1
- package/scripts/macos-mcp-launcher-build.mjs +317 -0
- package/scripts/macos-mcp-launcher-postinstall.sh +54 -0
- package/scripts/macos-mcp-launcher-preinstall.sh +61 -0
- package/src/assets/web-panel/assets/{AIOps-axqQfwv8.js → AIOps-D2MEn7fI.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DV-Ro0By.js → ActionButton-lCW_4Gf0.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Bt72WEVu.js → Analytics-Bs7i-nsS.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BtepG55E.js → AppLayout-CK7B6S_j.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-B7KaJ7S_.js → Artifacts-C8ILjpyo.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-FNFaUg7A.js → Audit-Bb9cNaWc.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DFb6J2wQ.js → BackgroundAgents-sC9ooXIf.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CC7YBhyj.js → Backup-D-DrsplT.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-RkgTNVtj.js → BaseInput-uCNZpwSK.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-hHEmGZy-.js → Chat-CsDK1uE9.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CAP9YhHv.js → ChatBubbleRenderer-OqSDt50w.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-z5wXOQP1.js → Checkbox-JhTXM1-K.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DVaDJnxy.js → Codegen-BbzZLnyD.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CJmaRDR5.js → Col-D4VW4m4j.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DF29_2bI.js → Community-BXrYawxV.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DtkQkBzn.js → Compact-DCihGy1u.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-xoHDxp7I.js → Compliance-DlyZ-lk8.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BUm-R7ht.js → Cowork-DvGZrhbO.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-Cgl14ZkB.js → Cron-DUncZi8h.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BNcbmoqh.js → Crosschain-C_hLuUcm.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Bwja0Zo9.js → DID-b6YSUqxr.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Bk5XjEdJ.js → Dashboard-uvM15Cfb.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Ci36Oitq.js → Dropdown-D1-Nd4qY.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer--U04ZN4q.js → EmailListRenderer-8ApvL6Zc.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-B2gNPR2s.js → FamilyGuardDashboard-LYJt8811.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-C4KghYn7.js → Federation-BbI3HGpv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-QL7x_Q_0.js → FormItemContext-DSoEKuFu.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-F3Ryltwm.js → GenericCardRenderer-B0S9OWd5.js} +1 -1
- package/src/assets/web-panel/assets/{Git-2HassK5k.js → Git-DIs9Zhf3.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DoBIp7oe.js → Governance-jzZXrxcl.js} +1 -1
- package/src/assets/web-panel/assets/{Inference--W01QhZ-.js → Inference-JUPjf2ZI.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DmvwqGpT.js → KnowledgeGraph-DxnfJvlP.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BBJ4yAel.js → Logs-iXiz8BEE.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BLCZMzhJ.js → MarkdownRenderer-GmB6DOr6.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-D5lNngSI.js → Marketplace-Y3Xch312.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CkEVOMKH.js → McpTools-D7kGGbO7.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-B1Ow1xVE.js → Memory-Do_hVxzh.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-SgYbUfb6.js → MobileBridge-CtrR0Tas.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-BL0Czcoj.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-DcTXSSma.js → Mtc-qo5QCTGd.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-Dt3WG4oj.js → MtcAudit-cq5mnjYj.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-B9B38UZx.js → Multisig-Bq0MAQHw.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-B0S4GGvG.js → NLProgramming-DdWE6Vi2.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DZ5U58Ng.js → Notes-BxMBFcIp.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-PjL5ZW34.js → NotificationSettings-RTrAcbUe.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DrAX2ESt.js → OrderTableRenderer-DJbpGqzT.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-Cv1GYs-c.js → Organization-SQzIPdqC.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Bbkp_PL3.js → Overflow-02yECh22.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BD8QUgoZ.js → P2P-BNg4pNCM.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BO-2PeaB.js → PdhVaultBrowser-BxPJZsD3.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-D_OPVa02.js → Permissions-CLnDnPAz.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-mh-pEmaB.js → PersonalDataHub-C5m0AjGf.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-6JC2EDdb.js → Pipeline-VwOf5Dhv.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CUshzUAG.js → Privacy-CC0W2hZd.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Cs2Aalri.js → ProjectInit-Dz893Bob.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-1ak49tcj.js → ProjectSettings-CxQkvl0k.js} +2 -2
- package/src/assets/web-panel/assets/Projects-DDAjkUkT.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CXXxvafH.js → Providers-DeB9IkxK.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-PTcDsF58.js → QrScannerModal-OCeoaswn.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CDwucrJw.js → QuickAsk-DenotaAj.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CJlLVRXR.js → Recommend-Cies_jjI.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DlN-ETsO.js → RemoteSession-Dxzl5n2T.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-DI23LR6m.js → Reputation-CW0mvxgZ.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Cl7UQMrM.js → Row-SEd32qZU.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CexHNfh8.js → RssFeed-BSwnJ3Lm.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BneBWvRN.js → Search-xe2m8bkC.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DX7Phgrp.js → Security-Mq2F9Nro.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BWXO8Mte.js → Services-BKGw2V3P.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DCyOjKPJ.js → Skeleton-DW95-Rxf.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CXqERsTB.js → Skills-CHgChCv_.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-bhbrbz8E.js → Sla-tKh6k3R7.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-3IPU0HBZ.js → SpeechSettings-D6UL3dA5.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DBNTQLbM.js → SyncSettings-D0Fw8fVL.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-quP0bkN-.js → Tasks-20x1GB3D.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BouWmgho.js → Templates-DJ-lts-a.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-qv0D9s7z.js → Tenant-BFdGpbs7.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BxvbE8BB.js → Terminal-Xpx61vHL.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D-IeLnJ2.js → TimelineRenderer-C6UIvyZQ.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CFMKKyVG.js → Tokens-Dellq08f.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-L8as3UuJ.js → Trigger-VKw8d0J-.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-MxmA4VZG.js → Trust-DNftdKT5.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-C0VvJYRi.js → UkeySign-DX9VAKJI.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Bf5QZj8N.js → VideoEditing-B2h3jO8q.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BIJ9riRz.js → Wallet-CjGkof7f.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-B8p9Mhkw.js → WebAuthn-2Zfcu55P.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BVsuaPen.js → WorkflowEditor-B9eZ8UxZ.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BfkjoKpV.js → chat-CQNfD-YO.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DeAlsWK8.js → colors-D4usyFT2.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cs97bz5j.js → compact-item-s4TpjvjX.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BqbG60LH.js → createContext-BRCSvYxj.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DKJjSWRn.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-i_RY0b1X.js → hasIn-AdkUbkV-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOVkf1K3.js → index--00U6vXZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-3tEpTc.js → index-B2V-eKfK.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2B4VPyP.js → index-B4JJCfQ2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkBi5TmC.js → index-BIoG5yYf.js} +1 -1
- package/src/assets/web-panel/assets/{index-79V_zv1s.js → index-BMcimU-2.js} +1 -1
- package/src/assets/web-panel/assets/{index-U4f5izHz.js → index-BPhkFZ1I.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiSkx4UB.js → index-BQ3DXBtD.js} +3 -3
- package/src/assets/web-panel/assets/{index-dIHr4B_u.js → index-Bad4VNvz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CIG8fQ2_.js → index-BgiXR_-_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqBgBpgp.js → index-BtobShro.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZxO_-E8a.js → index-Bw2MEnUk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUUrv141.js → index-C1mJtE6q.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bm7lLR4c.js → index-C484EJiS.js} +1 -1
- package/src/assets/web-panel/assets/{index-COdGA-8n.js → index-C5KAQFPQ.js} +1 -1
- package/src/assets/web-panel/assets/index-C61cnl3K.js +1 -0
- package/src/assets/web-panel/assets/{index-CED2PL_y.js → index-C7Y8Aluc.js} +1 -1
- package/src/assets/web-panel/assets/{index-YXgLng0K.js → index-CCsM_Ybx.js} +1 -1
- package/src/assets/web-panel/assets/{index-0-agYlWL.js → index-CJCFe2Qf.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5UkyA3c.js → index-CJVEOa4i.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1CbOH1i.js → index-CJdQrZth.js} +1 -1
- package/src/assets/web-panel/assets/{index-s1opAL-M.js → index-CKC1t3Ic.js} +1 -1
- package/src/assets/web-panel/assets/{index-yObj1O4n.js → index-CQhAVU5t.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgsqdnRV.js → index-CYdoQsa1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dk7-v4hP.js → index-Cduu2Hhr.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-JVTrpr.js → index-CkwiSLhU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeWoNHau.js → index-Cvyt-zhm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE2VtTlp.js → index-D2GjI2oe.js} +1 -1
- package/src/assets/web-panel/assets/index-D4XFHxBr.js +1 -0
- package/src/assets/web-panel/assets/{index-DmbfhJUP.js → index-DARsAyUp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BESLFt4q.js → index-DVKfQ0Ux.js} +1 -1
- package/src/assets/web-panel/assets/{index-PT_5sBMA.js → index-DZT36ZYq.js} +1 -1
- package/src/assets/web-panel/assets/{index-gyFpvL7l.js → index-D_XBHopw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYUVuwKt.js → index-DdCB70SS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcUXAB2s.js → index-DzRjHxEv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dk2qouAz.js → index-MMN9V6JF.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9evtvg2.js → index-RfMr79MW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuLQobzW.js → index-_IRVsV9X.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cf3WCLmy.js → index-nXyk8GZ9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CG3PRyAL.js → index-s_buFMEG.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-C9h9yKL3.js → initDefaultProps-Blmpk7HL.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BvmLsPdu.js → motion-DPBJMv1n.js} +1 -1
- package/src/assets/web-panel/assets/{move-CoL4ekU1.js → move-ButdzCM7.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-C4pMy6xV.js → mtc-parser-cyRC_QCe.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DPuJfNxx.js → omit-1epSV7hz.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D0KEeaK2.js → pickAttrs-DuPY8xJs.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BpkyINxt.js → placementArrow-6XZqarhe.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DQI_wOq8.js → responsiveObserve-B40pXBWM.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DYfdDiRg.js → slide-jbZ9Wu4n.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-B5dtvb0D.js → statusUtils-BKQQcVy4.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CF310mSj.js → styleChecker-NiKfWjUT.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D1Qp4BLp.js → useFlexGapSupport-bQUCvPeb.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-C7_iyvJK.js → useFs-CUGji7ph.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DZUt_fIm.js → usePersonalDataHub-4veyN2N2.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D3dsi0lZ.js → vnode-1-nLug-Z.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-WdbOlvdQ.js → zoom-dhCeRDMo.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +6 -6
- package/src/commands/automation.js +103 -1
- package/src/commands/cowork.js +174 -19
- package/src/commands/permissions.js +240 -16
- package/src/commands/plugin.js +1293 -56
- package/src/commands/session-location.js +276 -0
- package/src/commands/session.js +2 -0
- package/src/commands/team-merge-review.js +937 -0
- package/src/commands/team.js +2 -0
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/action-protocol.js +35 -10
- package/src/gateways/ws/remote-session-protocol.js +24 -1
- package/src/gateways/ws/ws-agent-handler.js +12 -0
- package/src/gateways/ws/ws-server.js +48 -12
- package/src/harness/jsonl-session-store.js +341 -24
- package/src/harness/mcp-client.js +8 -1
- package/src/harness/remote-session-audit.js +1 -0
- package/src/harness/remote-session-crypto.js +210 -2
- package/src/harness/remote-session-registry.js +159 -6
- package/src/harness/session-list-index.js +233 -14
- package/src/lib/agent-authority.js +6 -0
- package/src/lib/agent-schedule-store.js +139 -28
- package/src/lib/agent-team/team-merge-review-store.js +765 -0
- package/src/lib/agent-team/team-merge-review-transaction.js +1223 -0
- package/src/lib/agent-team/team-merge-review.js +1847 -0
- package/src/lib/automation-center-incidents.js +314 -0
- package/src/lib/automation-center-runtime.js +448 -0
- package/src/lib/automation-center.js +182 -38
- package/src/lib/automation-engine.js +41 -0
- package/src/lib/automation-execution-authority.js +241 -12
- package/src/lib/automation-execution-incident.js +737 -0
- package/src/lib/background-agent-keeper-client.js +248 -0
- package/src/lib/background-agent-keeper-protocol.js +147 -0
- package/src/lib/background-agent-supervisor.js +520 -40
- package/src/lib/background-interaction-resolver.js +12 -0
- package/src/lib/background-turn-bootstrap-protocol.js +126 -0
- package/src/lib/cowork-cron.js +85 -14
- package/src/lib/cowork-workflow.js +336 -16
- package/src/lib/dynamic-workflow-facade.js +648 -0
- package/src/lib/execution-location-contract.js +551 -0
- package/src/lib/execution-location-runtime.js +130 -0
- package/src/lib/ide-context.js +94 -6
- package/src/lib/mcp-stdio-package-materialization.js +166 -14
- package/src/lib/packer/pkg-config-generator.js +2 -0
- package/src/lib/permission-authority.js +77 -0
- package/src/lib/permission-side-effect-center.js +495 -0
- package/src/lib/plugin-runtime/install.js +514 -13
- package/src/lib/plugin-runtime/marketplace-artifact-readback.js +900 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +1181 -0
- package/src/lib/plugin-runtime/marketplace-impact.js +408 -0
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1567 -0
- package/src/lib/plugin-security.js +60 -3
- package/src/lib/process-execution-broker/index.js +1095 -57
- package/src/lib/process-execution-broker/linux-bwrap-descriptor-launch.js +375 -0
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +90 -16
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +159 -18
- package/src/lib/process-execution-broker/macos-mcp-launcher-contract.js +278 -0
- package/src/lib/process-execution-broker/macos-mcp-launcher-protocol.json +23 -0
- package/src/lib/process-execution-broker/macos-mcp-launcher.c +1497 -0
- package/src/lib/process-execution-broker/mcp-fd-entry-bootstrap.js +37 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +2330 -279
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +50 -4
- package/src/lib/public-ide-capability-manifest.js +3 -0
- package/src/lib/remote-approval-bridge.js +123 -17
- package/src/lib/remote-membership-authority.js +632 -0
- package/src/lib/remote-membership-coordinator.js +2409 -0
- package/src/lib/remote-membership-host-store.js +1750 -0
- package/src/lib/scheduler-kernel/agenda-adapter.js +17 -7
- package/src/lib/scheduler-kernel/automation-adapter.js +105 -11
- package/src/lib/scheduler-kernel/automation-event-adapter.js +87 -11
- package/src/lib/scheduler-kernel/contract.js +91 -0
- package/src/lib/scheduler-kernel/cowork-cron-adapter.js +12 -1
- package/src/lib/scheduler-kernel/loop-adapter.js +7 -1
- package/src/lib/scheduler-kernel/routine-adapter.js +7 -1
- package/src/lib/scheduler-kernel/runtime-control-capabilities.js +31 -0
- package/src/lib/scheduler-kernel/runtime.js +176 -3
- package/src/lib/scheduler-kernel/store.js +1253 -45
- package/src/lib/scoped-permission-store.js +369 -0
- package/src/lib/session-anti-rollback-anchor.js +262 -5
- package/src/lib/sub-agent-context.js +4 -0
- package/src/lib/workflow-definition-contract.js +149 -0
- package/src/repl/agent-repl.js +41 -7
- package/src/repl/chat-repl.js +2 -0
- package/src/repl/clipboard-image.js +735 -16
- package/src/repl/ide-status.js +1 -1
- package/src/repl/prompt-interactions.js +59 -6
- package/src/runtime/agent-core.js +93 -3
- package/src/runtime/headless-runner.js +30 -25
- package/src/runtime/headless-stream.js +32 -25
- package/src/runtime/mcp-config.js +81 -10
- package/src/workers/background-agent-keeper.js +498 -0
- package/src/workers/background-agent-worker.js +358 -13
- package/src/workers/background-turn-bootstrap.js +143 -0
- package/src/assets/web-panel/assets/MobileProjects-BWdOvQAj.js +0 -1
- package/src/assets/web-panel/assets/Projects-BV4oApsm.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DPty0mGN.js +0 -1
- package/src/assets/web-panel/assets/index-Bt7g0Vq_.js +0 -1
- package/src/assets/web-panel/assets/index-ChVT0p9u.js +0 -1
|
@@ -9,14 +9,23 @@
|
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
appendFileSync,
|
|
12
|
+
closeSync,
|
|
13
|
+
constants as fsConstants,
|
|
12
14
|
existsSync,
|
|
15
|
+
fstatSync,
|
|
16
|
+
fsyncSync,
|
|
17
|
+
lstatSync,
|
|
18
|
+
openSync,
|
|
13
19
|
readFileSync,
|
|
20
|
+
renameSync,
|
|
14
21
|
rmSync,
|
|
15
22
|
writeFileSync,
|
|
16
23
|
} from "node:fs";
|
|
17
|
-
import {
|
|
24
|
+
import { randomUUID } from "node:crypto";
|
|
25
|
+
import { basename, dirname, join } from "node:path";
|
|
18
26
|
import { iterateFileLinesReverseSync } from "../lib/file-lines.js";
|
|
19
27
|
import { ensurePrivateFile } from "../lib/secure-fs.js";
|
|
28
|
+
import { isAffectedWindowsZeroDeviceStatRuntime } from "../lib/secure-file-identity.js";
|
|
20
29
|
|
|
21
30
|
export const SESSION_INDEX_SCHEMA = 2;
|
|
22
31
|
export const SESSION_INDEX_FILE = ".sessions-index-v2.ndjson";
|
|
@@ -31,6 +40,14 @@ export const SESSION_GENERATION_AUTHORITY_SCHEMA =
|
|
|
31
40
|
// Keeping the injection immediately around the production sidecar/journal
|
|
32
41
|
// boundary makes that gate deterministic without replacing either write.
|
|
33
42
|
export const _sessionScaleFaultHooks = Object.seal({
|
|
43
|
+
afterMetaTempFsync: null,
|
|
44
|
+
afterMetaRename: null,
|
|
45
|
+
afterMetaDirectoryFsync: null,
|
|
46
|
+
afterTombstoneTempFsync: null,
|
|
47
|
+
afterTombstoneRename: null,
|
|
48
|
+
afterTombstoneDirectoryFsync: null,
|
|
49
|
+
afterTombstoneRemoval: null,
|
|
50
|
+
afterTombstoneRemovalDirectoryFsync: null,
|
|
34
51
|
afterMetaSnapshot: null,
|
|
35
52
|
});
|
|
36
53
|
|
|
@@ -40,6 +57,157 @@ function runSessionScaleFaultHook(name, payload) {
|
|
|
40
57
|
if (typeof hook === "function") hook(payload);
|
|
41
58
|
}
|
|
42
59
|
|
|
60
|
+
function annotateSnapshotFailure(
|
|
61
|
+
cause,
|
|
62
|
+
{ targetPath, renamed, directorySynced },
|
|
63
|
+
) {
|
|
64
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
65
|
+
if (!error.commitState) {
|
|
66
|
+
// Before rename the public snapshot is unchanged. POSIX needs the parent
|
|
67
|
+
// directory fsync to make the rename durable; Windows exposes no usable
|
|
68
|
+
// directory fsync through Node, so a flushed atomic rename is its complete
|
|
69
|
+
// host boundary. A hook after that boundary models response loss.
|
|
70
|
+
error.commitState = !renamed
|
|
71
|
+
? "not-committed"
|
|
72
|
+
: directorySynced
|
|
73
|
+
? "committed"
|
|
74
|
+
: "unknown";
|
|
75
|
+
}
|
|
76
|
+
if (!error.persistenceTarget) error.persistenceTarget = targetPath;
|
|
77
|
+
return error;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function syncParentDirectory(targetPath) {
|
|
81
|
+
if (process.platform === "win32") return false;
|
|
82
|
+
let descriptor = null;
|
|
83
|
+
try {
|
|
84
|
+
const flags =
|
|
85
|
+
fsConstants.O_RDONLY |
|
|
86
|
+
Number(fsConstants.O_DIRECTORY ?? 0) |
|
|
87
|
+
Number(fsConstants.O_NOFOLLOW ?? 0);
|
|
88
|
+
descriptor = openSync(dirname(targetPath), flags);
|
|
89
|
+
if (!fstatSync(descriptor).isDirectory()) {
|
|
90
|
+
throw new Error("Session snapshot parent is not a directory");
|
|
91
|
+
}
|
|
92
|
+
fsyncSync(descriptor);
|
|
93
|
+
return true;
|
|
94
|
+
} finally {
|
|
95
|
+
if (descriptor !== null) closeSync(descriptor);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function assertSnapshotPathMatchesDescriptor(
|
|
100
|
+
filePath,
|
|
101
|
+
descriptorStats,
|
|
102
|
+
serialized,
|
|
103
|
+
) {
|
|
104
|
+
const published = lstatSync(filePath, { bigint: true });
|
|
105
|
+
const usefulIdentity =
|
|
106
|
+
descriptorStats.dev !== 0n ||
|
|
107
|
+
descriptorStats.ino !== 0n ||
|
|
108
|
+
published.dev !== 0n ||
|
|
109
|
+
published.ino !== 0n;
|
|
110
|
+
const deviceMatches =
|
|
111
|
+
String(descriptorStats.dev) === String(published.dev) ||
|
|
112
|
+
(isAffectedWindowsZeroDeviceStatRuntime() &&
|
|
113
|
+
String(descriptorStats.dev) !== "0" &&
|
|
114
|
+
String(published.dev) === "0");
|
|
115
|
+
const identityMatches =
|
|
116
|
+
deviceMatches &&
|
|
117
|
+
String(descriptorStats.ino) === String(published.ino) &&
|
|
118
|
+
String(descriptorStats.mode) === String(published.mode);
|
|
119
|
+
const expectedBytes = BigInt(Buffer.byteLength(serialized, "utf8"));
|
|
120
|
+
if (
|
|
121
|
+
published.isSymbolicLink() ||
|
|
122
|
+
!published.isFile() ||
|
|
123
|
+
(usefulIdentity && !identityMatches) ||
|
|
124
|
+
descriptorStats.size !== expectedBytes ||
|
|
125
|
+
published.size !== expectedBytes ||
|
|
126
|
+
readFileSync(filePath, "utf8") !== serialized
|
|
127
|
+
) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
"Session snapshot path no longer identifies the flushed payload",
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Publish one authority snapshot without ever exposing a partial JSON file.
|
|
136
|
+
* The temporary file lives beside the destination so rename remains atomic.
|
|
137
|
+
* Activity ordering is intentionally outside this helper: that journal is a
|
|
138
|
+
* rebuildable cache and must not be confused with the durable snapshot.
|
|
139
|
+
*/
|
|
140
|
+
function writeSnapshotDurably(
|
|
141
|
+
filePath,
|
|
142
|
+
serialized,
|
|
143
|
+
{ kind, sessionId, status, afterTempFsync, afterRename, afterDirectoryFsync },
|
|
144
|
+
) {
|
|
145
|
+
const temporaryPath = join(
|
|
146
|
+
dirname(filePath),
|
|
147
|
+
`.${basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,
|
|
148
|
+
);
|
|
149
|
+
const payload = Object.freeze({
|
|
150
|
+
kind,
|
|
151
|
+
sessionId,
|
|
152
|
+
status,
|
|
153
|
+
targetPath: filePath,
|
|
154
|
+
temporaryPath,
|
|
155
|
+
});
|
|
156
|
+
let descriptor = null;
|
|
157
|
+
let descriptorStats = null;
|
|
158
|
+
let renamed = false;
|
|
159
|
+
let directorySynced = process.platform === "win32";
|
|
160
|
+
try {
|
|
161
|
+
descriptor = openSync(temporaryPath, "wx", 0o600);
|
|
162
|
+
writeFileSync(descriptor, serialized, "utf8");
|
|
163
|
+
ensurePrivateFile(temporaryPath);
|
|
164
|
+
fsyncSync(descriptor);
|
|
165
|
+
descriptorStats = fstatSync(descriptor, { bigint: true });
|
|
166
|
+
runSessionScaleFaultHook(afterTempFsync, payload);
|
|
167
|
+
assertSnapshotPathMatchesDescriptor(
|
|
168
|
+
temporaryPath,
|
|
169
|
+
descriptorStats,
|
|
170
|
+
serialized,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Both files are in the same directory; rename atomically replaces an old
|
|
174
|
+
// snapshot or publishes a first generation without a partial target.
|
|
175
|
+
// The temp already has the final owner-only permissions before publication.
|
|
176
|
+
renameSync(temporaryPath, filePath);
|
|
177
|
+
renamed = true;
|
|
178
|
+
assertSnapshotPathMatchesDescriptor(filePath, descriptorStats, serialized);
|
|
179
|
+
closeSync(descriptor);
|
|
180
|
+
descriptor = null;
|
|
181
|
+
runSessionScaleFaultHook(afterRename, payload);
|
|
182
|
+
if (process.platform !== "win32") {
|
|
183
|
+
directorySynced = syncParentDirectory(filePath);
|
|
184
|
+
runSessionScaleFaultHook(afterDirectoryFsync, payload);
|
|
185
|
+
}
|
|
186
|
+
} catch (cause) {
|
|
187
|
+
throw annotateSnapshotFailure(cause, {
|
|
188
|
+
targetPath: filePath,
|
|
189
|
+
renamed,
|
|
190
|
+
directorySynced,
|
|
191
|
+
});
|
|
192
|
+
} finally {
|
|
193
|
+
if (descriptor !== null) {
|
|
194
|
+
try {
|
|
195
|
+
closeSync(descriptor);
|
|
196
|
+
} catch {
|
|
197
|
+
// Preserve the persistence failure that stopped publication.
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!renamed) {
|
|
201
|
+
try {
|
|
202
|
+
rmSync(temporaryPath, { force: true });
|
|
203
|
+
} catch {
|
|
204
|
+
// A crash may leave an ignored same-directory temp file. Never replace
|
|
205
|
+
// the original persistence failure with best-effort cleanup failure.
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
43
211
|
export function sessionMetaPath(dir, sessionId) {
|
|
44
212
|
return join(dir, `${sessionId}.meta.json`);
|
|
45
213
|
}
|
|
@@ -189,6 +357,7 @@ function writeMetaSnapshot(dir, meta) {
|
|
|
189
357
|
// that publication fails. Remove it only after a live generation snapshot
|
|
190
358
|
// commits, so crashes can cause an extra candidate read but never hide a
|
|
191
359
|
// successfully committed tombstone.
|
|
360
|
+
let tombstonePublished = false;
|
|
192
361
|
if (meta.deleted === true) {
|
|
193
362
|
const generation = normalizeSessionGenerationAuthority(meta.generation);
|
|
194
363
|
const marker = {
|
|
@@ -206,21 +375,71 @@ function writeMetaSnapshot(dir, meta) {
|
|
|
206
375
|
Number(meta.deleted_at_ms || meta.updated_at_ms) || 0,
|
|
207
376
|
),
|
|
208
377
|
};
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
378
|
+
writeSnapshotDurably(tombstoneMarker, `${JSON.stringify(marker)}\n`, {
|
|
379
|
+
kind: "tombstone",
|
|
380
|
+
sessionId: meta.id,
|
|
381
|
+
status: "deleted",
|
|
382
|
+
afterTempFsync: "afterTombstoneTempFsync",
|
|
383
|
+
afterRename: "afterTombstoneRename",
|
|
384
|
+
afterDirectoryFsync: "afterTombstoneDirectoryFsync",
|
|
212
385
|
});
|
|
213
|
-
|
|
386
|
+
tombstonePublished = true;
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
writeSnapshotDurably(filePath, `${JSON.stringify(meta)}\n`, {
|
|
390
|
+
kind: "meta",
|
|
391
|
+
sessionId: meta.id,
|
|
392
|
+
status: meta.deleted === true ? "deleted" : "live",
|
|
393
|
+
afterTempFsync: "afterMetaTempFsync",
|
|
394
|
+
afterRename: "afterMetaRename",
|
|
395
|
+
afterDirectoryFsync: "afterMetaDirectoryFsync",
|
|
396
|
+
});
|
|
397
|
+
} catch (cause) {
|
|
398
|
+
// A durable marker is already a conservative deletion fence. If the meta
|
|
399
|
+
// publication then fails, the composite mutation is partial/unknown even
|
|
400
|
+
// when the old meta path itself was left byte-for-byte unchanged.
|
|
401
|
+
if (tombstonePublished && cause?.commitState === "not-committed") {
|
|
402
|
+
cause.commitState = "unknown";
|
|
403
|
+
}
|
|
404
|
+
throw cause;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (meta.deleted !== true && existsSync(tombstoneMarker)) {
|
|
408
|
+
const payload = Object.freeze({
|
|
409
|
+
kind: "tombstone-removal",
|
|
410
|
+
sessionId: meta.id,
|
|
411
|
+
status: "live",
|
|
412
|
+
targetPath: tombstoneMarker,
|
|
413
|
+
parentDirectory: dirname(tombstoneMarker),
|
|
414
|
+
});
|
|
415
|
+
let removed = false;
|
|
416
|
+
let directorySynced = process.platform === "win32";
|
|
417
|
+
try {
|
|
418
|
+
rmSync(tombstoneMarker, { force: true });
|
|
419
|
+
removed = true;
|
|
420
|
+
runSessionScaleFaultHook("afterTombstoneRemoval", payload);
|
|
421
|
+
if (process.platform !== "win32") {
|
|
422
|
+
directorySynced = syncParentDirectory(tombstoneMarker);
|
|
423
|
+
runSessionScaleFaultHook(
|
|
424
|
+
"afterTombstoneRemovalDirectoryFsync",
|
|
425
|
+
payload,
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
} catch (cause) {
|
|
429
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
430
|
+
if (!error.commitState) {
|
|
431
|
+
// The live meta was already durably published. Retaining the marker is
|
|
432
|
+
// fail-closed; removing it without a POSIX directory barrier is an
|
|
433
|
+
// unknown publication, while a post-barrier hook is response loss.
|
|
434
|
+
error.commitState =
|
|
435
|
+
removed && directorySynced ? "committed" : "unknown";
|
|
436
|
+
}
|
|
437
|
+
if (!error.persistenceTarget) {
|
|
438
|
+
error.persistenceTarget = tombstoneMarker;
|
|
439
|
+
}
|
|
440
|
+
throw error;
|
|
441
|
+
}
|
|
214
442
|
}
|
|
215
|
-
// The owning transcript lock serializes this derived snapshot. It need not
|
|
216
|
-
// use a second temp+rename protocol: interruption can only corrupt a cache,
|
|
217
|
-
// which readSessionMeta rejects and rebuildSessionMeta regenerates.
|
|
218
|
-
writeFileSync(filePath, `${JSON.stringify(meta)}\n`, {
|
|
219
|
-
encoding: "utf8",
|
|
220
|
-
mode: 0o600,
|
|
221
|
-
});
|
|
222
|
-
ensurePrivateFile(filePath);
|
|
223
|
-
if (meta.deleted !== true) rmSync(tombstoneMarker, { force: true });
|
|
224
443
|
}
|
|
225
444
|
|
|
226
445
|
/**
|
|
@@ -266,6 +266,12 @@ export function describeAuthorityChain(envelope = {}) {
|
|
|
266
266
|
const parts = [`origin=${envelope.origin || "unknown"}`];
|
|
267
267
|
if (envelope.principalId) parts.push(`principal=${envelope.principalId}`);
|
|
268
268
|
if (envelope.sessionId) parts.push(`session=${envelope.sessionId}`);
|
|
269
|
+
if (envelope.remoteSessionId)
|
|
270
|
+
parts.push(`remote-session=${envelope.remoteSessionId}`);
|
|
271
|
+
if (envelope.sessionEpoch)
|
|
272
|
+
parts.push(`session-epoch=${envelope.sessionEpoch}`);
|
|
273
|
+
if (envelope.membershipEpoch)
|
|
274
|
+
parts.push(`membership-epoch=${envelope.membershipEpoch}`);
|
|
269
275
|
if (envelope.parentAgentId) parts.push(`parent=${envelope.parentAgentId}`);
|
|
270
276
|
if (envelope.correlationId) parts.push(`corr=${envelope.correlationId}`);
|
|
271
277
|
parts.push(`authority=${authorityForOrigin(envelope.origin, envelope)}`);
|
|
@@ -258,6 +258,21 @@ function agendaStoreError(code, message, { retryable = false, retryAt } = {}) {
|
|
|
258
258
|
return error;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
function agendaPersistenceError(
|
|
262
|
+
code,
|
|
263
|
+
message,
|
|
264
|
+
cause,
|
|
265
|
+
{ filePath, commitState } = {},
|
|
266
|
+
) {
|
|
267
|
+
const error = new Error(message, { cause });
|
|
268
|
+
error.name = "AgendaScheduleStoreError";
|
|
269
|
+
error.code = code;
|
|
270
|
+
error.filePath = filePath;
|
|
271
|
+
if (typeof cause?.code === "string") error.fsCode = cause.code;
|
|
272
|
+
if (commitState) error.commitState = commitState;
|
|
273
|
+
return error;
|
|
274
|
+
}
|
|
275
|
+
|
|
261
276
|
function applyScheduledAgentSuccess(entry, atMs) {
|
|
262
277
|
if (entry.kind === "wakeup") {
|
|
263
278
|
entry.status = "fired";
|
|
@@ -666,9 +681,16 @@ export function previousCronTime(expr, fromMs, options = {}) {
|
|
|
666
681
|
// ─── store ───────────────────────────────────────────────────────────────
|
|
667
682
|
|
|
668
683
|
export class AgentScheduleStore {
|
|
669
|
-
constructor({
|
|
684
|
+
constructor({
|
|
685
|
+
dir = null,
|
|
686
|
+
now = () => Date.now(),
|
|
687
|
+
fileSystem = fs,
|
|
688
|
+
platform = process.platform,
|
|
689
|
+
} = {}) {
|
|
670
690
|
this.dir = dir || agentScheduleDir();
|
|
671
691
|
this._now = typeof now === "function" ? now : () => now;
|
|
692
|
+
this._fs = fileSystem;
|
|
693
|
+
this._platform = platform;
|
|
672
694
|
}
|
|
673
695
|
|
|
674
696
|
_file(kind) {
|
|
@@ -676,37 +698,124 @@ export class AgentScheduleStore {
|
|
|
676
698
|
}
|
|
677
699
|
|
|
678
700
|
_ensureDir() {
|
|
679
|
-
|
|
701
|
+
this._fs.mkdirSync(this.dir, { recursive: true, mode: 0o700 });
|
|
680
702
|
}
|
|
681
703
|
|
|
682
|
-
_readAll(kind) {
|
|
704
|
+
_readAll(kind, { failOnMalformed = false } = {}) {
|
|
705
|
+
const filePath = this._file(kind);
|
|
683
706
|
let raw;
|
|
684
707
|
try {
|
|
685
|
-
raw =
|
|
686
|
-
} catch {
|
|
687
|
-
return [];
|
|
708
|
+
raw = this._fs.readFileSync(filePath, "utf-8");
|
|
709
|
+
} catch (cause) {
|
|
710
|
+
if (cause?.code === "ENOENT") return [];
|
|
711
|
+
throw agendaPersistenceError(
|
|
712
|
+
"AGENDA_SCHEDULE_STORE_READ_FAILED",
|
|
713
|
+
`Agenda schedule store read failed (${filePath})`,
|
|
714
|
+
cause,
|
|
715
|
+
{ filePath },
|
|
716
|
+
);
|
|
688
717
|
}
|
|
689
718
|
const entries = [];
|
|
719
|
+
let lineNumber = 0;
|
|
690
720
|
for (const line of raw.split("\n")) {
|
|
721
|
+
lineNumber += 1;
|
|
691
722
|
const trimmed = line.trim();
|
|
692
723
|
if (!trimmed) continue;
|
|
693
724
|
try {
|
|
694
|
-
|
|
695
|
-
|
|
725
|
+
const entry = JSON.parse(trimmed);
|
|
726
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
727
|
+
throw new TypeError("schedule entry must be an object");
|
|
728
|
+
}
|
|
729
|
+
entries.push(entry);
|
|
730
|
+
} catch (cause) {
|
|
731
|
+
if (failOnMalformed) {
|
|
732
|
+
throw agendaPersistenceError(
|
|
733
|
+
"AGENDA_SCHEDULE_STORE_MALFORMED",
|
|
734
|
+
`Agenda schedule store contains malformed JSONL (${filePath}:${lineNumber})`,
|
|
735
|
+
cause,
|
|
736
|
+
{ filePath },
|
|
737
|
+
);
|
|
738
|
+
}
|
|
696
739
|
// Skip a corrupt line rather than throwing — one bad append must not
|
|
697
|
-
// poison
|
|
740
|
+
// poison advisory reads. Authoritative mutations opt into strict mode
|
|
741
|
+
// so they never replace a store after silently dropping durable rows.
|
|
698
742
|
}
|
|
699
743
|
}
|
|
700
744
|
return entries;
|
|
701
745
|
}
|
|
702
746
|
|
|
703
747
|
_writeAll(kind, entries) {
|
|
704
|
-
this.
|
|
748
|
+
const filePath = this._file(kind);
|
|
705
749
|
const body = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
750
|
+
const contents = Buffer.from(body ? `${body}\n` : "", "utf8");
|
|
751
|
+
const temporaryPath = path.join(
|
|
752
|
+
this.dir,
|
|
753
|
+
`.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,
|
|
754
|
+
);
|
|
755
|
+
let descriptor = null;
|
|
756
|
+
let renamed = false;
|
|
757
|
+
|
|
758
|
+
try {
|
|
759
|
+
this._ensureDir();
|
|
760
|
+
descriptor = this._fs.openSync(temporaryPath, "wx", 0o600);
|
|
761
|
+
let offset = 0;
|
|
762
|
+
while (offset < contents.length) {
|
|
763
|
+
const written = this._fs.writeSync(
|
|
764
|
+
descriptor,
|
|
765
|
+
contents,
|
|
766
|
+
offset,
|
|
767
|
+
contents.length - offset,
|
|
768
|
+
null,
|
|
769
|
+
);
|
|
770
|
+
if (!Number.isSafeInteger(written) || written <= 0) {
|
|
771
|
+
const cause = new Error("write returned no progress");
|
|
772
|
+
cause.code = "EIO";
|
|
773
|
+
throw cause;
|
|
774
|
+
}
|
|
775
|
+
offset += written;
|
|
776
|
+
}
|
|
777
|
+
this._fs.fsyncSync(descriptor);
|
|
778
|
+
this._fs.closeSync(descriptor);
|
|
779
|
+
descriptor = null;
|
|
780
|
+
this._fs.renameSync(temporaryPath, filePath);
|
|
781
|
+
renamed = true;
|
|
782
|
+
|
|
783
|
+
if (this._platform !== "win32") {
|
|
784
|
+
const directoryDescriptor = this._fs.openSync(this.dir, "r");
|
|
785
|
+
try {
|
|
786
|
+
this._fs.fsyncSync(directoryDescriptor);
|
|
787
|
+
} finally {
|
|
788
|
+
this._fs.closeSync(directoryDescriptor);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
} catch (cause) {
|
|
792
|
+
if (descriptor !== null) {
|
|
793
|
+
try {
|
|
794
|
+
this._fs.closeSync(descriptor);
|
|
795
|
+
} catch {
|
|
796
|
+
// Preserve the original persistence failure.
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (!renamed) {
|
|
800
|
+
try {
|
|
801
|
+
this._fs.unlinkSync(temporaryPath);
|
|
802
|
+
} catch {
|
|
803
|
+
// Best-effort orphan cleanup; the authoritative file is untouched.
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
throw agendaPersistenceError(
|
|
807
|
+
"AGENDA_SCHEDULE_STORE_WRITE_FAILED",
|
|
808
|
+
`Agenda schedule store write failed (${filePath})`,
|
|
809
|
+
cause,
|
|
810
|
+
{
|
|
811
|
+
filePath,
|
|
812
|
+
// A failure before rename leaves the old generation authoritative.
|
|
813
|
+
// A directory-fsync failure after rename makes crash durability
|
|
814
|
+
// unknowable even though an immediate read sees the new generation.
|
|
815
|
+
commitState: renamed ? "unknown" : "not-committed",
|
|
816
|
+
},
|
|
817
|
+
);
|
|
818
|
+
}
|
|
710
819
|
}
|
|
711
820
|
|
|
712
821
|
// ── create ──────────────────────────────────────────────────────────────
|
|
@@ -741,7 +850,7 @@ export class AgentScheduleStore {
|
|
|
741
850
|
createdAt: now,
|
|
742
851
|
status: "pending",
|
|
743
852
|
};
|
|
744
|
-
const entries = this._readAll("wakeup");
|
|
853
|
+
const entries = this._readAll("wakeup", { failOnMalformed: true });
|
|
745
854
|
entries.push(entry);
|
|
746
855
|
this._writeAll("wakeup", entries);
|
|
747
856
|
return entry;
|
|
@@ -786,7 +895,7 @@ export class AgentScheduleStore {
|
|
|
786
895
|
runs: 0,
|
|
787
896
|
status: "active",
|
|
788
897
|
};
|
|
789
|
-
const entries = this._readAll("cron");
|
|
898
|
+
const entries = this._readAll("cron", { failOnMalformed: true });
|
|
790
899
|
entries.push(entry);
|
|
791
900
|
this._writeAll("cron", entries);
|
|
792
901
|
return entry;
|
|
@@ -882,7 +991,7 @@ export class AgentScheduleStore {
|
|
|
882
991
|
checks: 0,
|
|
883
992
|
status: "active",
|
|
884
993
|
};
|
|
885
|
-
const entries = this._readAll("monitor");
|
|
994
|
+
const entries = this._readAll("monitor", { failOnMalformed: true });
|
|
886
995
|
entries.push(entry);
|
|
887
996
|
this._writeAll("monitor", entries);
|
|
888
997
|
return entry;
|
|
@@ -917,7 +1026,7 @@ export class AgentScheduleStore {
|
|
|
917
1026
|
path.join(this.dir, ".agenda-claims"),
|
|
918
1027
|
() => {
|
|
919
1028
|
for (const kind of SCHEDULE_KINDS) {
|
|
920
|
-
const entries = this._readAll(kind);
|
|
1029
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
921
1030
|
const entry = entries.find((candidate) => candidate.id === id);
|
|
922
1031
|
if (!entry) continue;
|
|
923
1032
|
if (!isSchedulableStatus(entry)) {
|
|
@@ -993,7 +1102,7 @@ export class AgentScheduleStore {
|
|
|
993
1102
|
path.join(this.dir, ".agenda-claims"),
|
|
994
1103
|
() => {
|
|
995
1104
|
for (const kind of SCHEDULE_KINDS) {
|
|
996
|
-
const entries = this._readAll(kind);
|
|
1105
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
997
1106
|
const entry = entries.find((candidate) => candidate.id === id);
|
|
998
1107
|
if (!entry) continue;
|
|
999
1108
|
const prior = entry.schedulerMigration;
|
|
@@ -1079,7 +1188,7 @@ export class AgentScheduleStore {
|
|
|
1079
1188
|
path.join(this.dir, ".agenda-claims"),
|
|
1080
1189
|
() => {
|
|
1081
1190
|
for (const kind of SCHEDULE_KINDS) {
|
|
1082
|
-
const entries = this._readAll(kind);
|
|
1191
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
1083
1192
|
const entry = entries.find((candidate) => candidate.id === id);
|
|
1084
1193
|
if (!entry) continue;
|
|
1085
1194
|
const migration = entry.schedulerMigration;
|
|
@@ -1171,7 +1280,9 @@ export class AgentScheduleStore {
|
|
|
1171
1280
|
() => {
|
|
1172
1281
|
const claimed = [];
|
|
1173
1282
|
for (const scheduleKind of kinds) {
|
|
1174
|
-
const entries = this._readAll(scheduleKind
|
|
1283
|
+
const entries = this._readAll(scheduleKind, {
|
|
1284
|
+
failOnMalformed: true,
|
|
1285
|
+
});
|
|
1175
1286
|
let changed = false;
|
|
1176
1287
|
for (const entry of entries) {
|
|
1177
1288
|
if (!isSchedulableStatus(entry) || isEntryExpired(entry, now))
|
|
@@ -1242,7 +1353,7 @@ export class AgentScheduleStore {
|
|
|
1242
1353
|
path.join(this.dir, ".agenda-claims"),
|
|
1243
1354
|
() => {
|
|
1244
1355
|
for (const kind of SCHEDULE_KINDS) {
|
|
1245
|
-
const entries = this._readAll(kind);
|
|
1356
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
1246
1357
|
const entry = entries.find((candidate) => candidate.id === id);
|
|
1247
1358
|
if (!entry) continue;
|
|
1248
1359
|
const prior = entry.schedulerExecution;
|
|
@@ -1330,7 +1441,7 @@ export class AgentScheduleStore {
|
|
|
1330
1441
|
path.join(this.dir, ".agenda-claims"),
|
|
1331
1442
|
() => {
|
|
1332
1443
|
for (const kind of SCHEDULE_KINDS) {
|
|
1333
|
-
const entries = this._readAll(kind);
|
|
1444
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
1334
1445
|
const entry = entries.find((candidate) => candidate.id === id);
|
|
1335
1446
|
if (!entry) continue;
|
|
1336
1447
|
const evidence = entry.schedulerExecution;
|
|
@@ -1408,7 +1519,7 @@ export class AgentScheduleStore {
|
|
|
1408
1519
|
path.join(this.dir, ".agenda-claims"),
|
|
1409
1520
|
() => {
|
|
1410
1521
|
for (const kind of SCHEDULE_KINDS) {
|
|
1411
|
-
const entries = this._readAll(kind);
|
|
1522
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
1412
1523
|
const entry = entries.find((candidate) => candidate.id === id);
|
|
1413
1524
|
if (!entry) continue;
|
|
1414
1525
|
const evidence = entry.schedulerExecution;
|
|
@@ -1484,7 +1595,7 @@ export class AgentScheduleStore {
|
|
|
1484
1595
|
const now = atMs != null ? atMs : this._now();
|
|
1485
1596
|
const retired = [];
|
|
1486
1597
|
for (const kind of SCHEDULE_KINDS) {
|
|
1487
|
-
const entries = this._readAll(kind);
|
|
1598
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
1488
1599
|
let changed = false;
|
|
1489
1600
|
for (const entry of entries) {
|
|
1490
1601
|
if (isSchedulableStatus(entry) && isEntryExpired(entry, now)) {
|
|
@@ -1501,7 +1612,7 @@ export class AgentScheduleStore {
|
|
|
1501
1612
|
|
|
1502
1613
|
cancel(id) {
|
|
1503
1614
|
for (const kind of SCHEDULE_KINDS) {
|
|
1504
|
-
const entries = this._readAll(kind);
|
|
1615
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
1505
1616
|
const idx = entries.findIndex((entry) => entry.id === id);
|
|
1506
1617
|
if (idx >= 0) {
|
|
1507
1618
|
const [removed] = entries.splice(idx, 1);
|
|
@@ -1522,7 +1633,7 @@ export class AgentScheduleStore {
|
|
|
1522
1633
|
pruneTerminal({ before = Infinity } = {}) {
|
|
1523
1634
|
const removed = [];
|
|
1524
1635
|
for (const kind of SCHEDULE_KINDS) {
|
|
1525
|
-
const entries = this._readAll(kind);
|
|
1636
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
1526
1637
|
const keep = entries.filter((entry) => {
|
|
1527
1638
|
const prunable =
|
|
1528
1639
|
TERMINAL_SET.has(entry.status) && terminalAt(entry) <= before;
|
|
@@ -1586,7 +1697,7 @@ export class AgentScheduleStore {
|
|
|
1586
1697
|
}
|
|
1587
1698
|
|
|
1588
1699
|
_mutate(kind, id, fn) {
|
|
1589
|
-
const entries = this._readAll(kind);
|
|
1700
|
+
const entries = this._readAll(kind, { failOnMalformed: true });
|
|
1590
1701
|
const entry = entries.find((e) => e.id === id);
|
|
1591
1702
|
if (!entry) return null;
|
|
1592
1703
|
fn(entry);
|