chainlesschain 0.166.46 → 0.166.48
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 +6 -4
- package/package.json +4 -2
- package/src/assets/web-panel/assets/{AIOps-Cp8KiMmv.js → AIOps-BLGEffkh.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-B4d9BGsm.js → ActionButton-Cz1z2VGD.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Cf9yXTkv.js → Analytics-C3j9cf6q.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-40UkmxtP.js → AppLayout-BR9ERuCA.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-Ckkb4oOA.js → Artifacts-CozJCm4y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Dnf569k5.js → Audit-DC5g9Ra2.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CA6bLem4.js → BackgroundAgents-B1zrQ0dW.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-ChQU0I_Q.js → Backup-ChVnfjpm.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BHeZXnNN.js → BaseInput-8_vLQsLf.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DcFVYB2E.js → Chat-Dct9bm6L.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BvoSsCKh.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-2PDbejv2.js → Checkbox-DZQIsCIC.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DJBn1RAu.js → Codegen-CdzHxmnq.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CPRq7n6y.js → Col-DdAN8LeW.js} +1 -1
- package/src/assets/web-panel/assets/{Community-axvINFMi.js → Community-CNgjT9MA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-_OX4Qbe0.js → Compact-BtQiX6G-.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DWAkvzRL.js → Compliance-OGX5ma3j.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CUIJ25oj.js → Cowork-CgugQnGM.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-nR0m7H3_.js → Cron-Bpg7cPFD.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CgTLXzh_.js → Crosschain-D1hsuswf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BllzRB6Q.js → DID-miDRsMBw.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C9f0IIBN.js → Dashboard-CGcl8htP.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BkahetiO.js → Dropdown-BFaxTdVE.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-rartVC-k.js → EmailListRenderer-BQdGLZIO.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-BqpG3XHV.js → EvolutionSettings-bMvgBk6r.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgUdFvw1.js → FamilyGuardDashboard-c1WVC_--.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-oFpIQU6g.js → Federation-DyAcFrQY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BUH5xlyP.js → FormItemContext-CfwpfD2B.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-ilRa-ueg.js → GenericCardRenderer-DkJcbBpS.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DlHHSF8j.js → Git-DgYIrPJh.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-yQiiXIWe.js → Governance-BF1tYPJ6.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BUDRVGOP.js → Inference-BFkwRIrU.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BaGyIoN1.js → KnowledgeGraph-DpCoqhBa.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-C1cO6zE0.js → Logs-DU2qAJmD.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-_z_Iaoes.js → MarkdownRenderer-QWv7UJLq.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CM8cGL0K.js → Marketplace-Dz9NUrfo.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CDqAMU0U.js → McpTools-B67I3d5v.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CfB0-Ofv.js → Memory-crkElJSy.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BL18ytvB.js → MobileBridge-BO20VbdX.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-PIyyxbuJ.js → MobileProjects-Ba-3gZZd.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BTTrFCol.js → Mtc-BAHZM084.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-JHZn037c.js → MtcAudit-CL3DzajL.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-CMf9NEdB.js → Multisig-BSWWzvWH.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dhk3qaus.js → NLProgramming-fnxqXM_3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-6F8EarNN.js → Notes-qUZPIQXT.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-gxdaVEit.js → NotificationSettings-x9Xrixqu.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-gVgXQWdc.js → OrderTableRenderer-BtMtQUt7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CzvHcDNO.js → Organization-C946Hzna.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Dclx30Vv.js → Overflow-BYf8KIts.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CM0Wm9vZ.js → P2P-Cbqf5dnv.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-rmxp0cuF.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-B_oATenG.js → Permissions-CZBw45EW.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-CptiVBh5.js → PersonalDataHub-BKwUAt43.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DkHzgyyI.js → Pipeline-DmFD2QW4.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DzZvwcKh.js → Privacy-KpuwWvt5.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CzkCs-5G.js → ProjectInit-Bt8UheX6.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BB8dcxRD.js → ProjectSettings-2ZeAn9CP.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-B7PW9CFY.js → Projects-zBuet8Zs.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Tfx-ti1V.js → Providers-DcEAnw6L.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-EvWlNhfX.js → QrScannerModal-qF7liYG9.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CUspgbNC.js → QuickAsk-qiESlVBJ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-COY2g77J.js → Recommend-Bk-cQyi9.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BzMBU6E6.js → RemoteSession-CwrekErS.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-RtYRF4YP.js → Reputation-Bmw_UQtW.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Bva3l9Lc.js → Row-CPdqZIUX.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BhjzLvW3.js → RssFeed-D_W-xxvS.js} +2 -2
- package/src/assets/web-panel/assets/{Search-BYzT2PTR.js → Search-Hu0oDFj_.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bcl4-KTv.js → Security-DeRxUwmq.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BRRa4A0f.js → Services-CsNmHFkf.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DYQ016qy.js → Skeleton-gv_Vz0tN.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CAUKL9JA.js → Skills-Dgg-_rCB.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BTyUqfuD.js → Sla-Bpu-bsEJ.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CUjMihrs.js → SpeechSettings-CrhleHuF.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CvfOSupr.js → SyncSettings-JHjUN6I9.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-EvjNXrWH.js → Tasks-DKRWKkl9.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-wPAp0EoM.js → Templates-leiWntLq.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CP0D-q9Q.js → Tenant--1bYulvn.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CQ0f2iFg.js → Terminal-C0y6FvSS.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D3JrGSPb.js → TimelineRenderer-CQK4PVv4.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-yXf2PPp9.js → Tokens-DbIvF-DV.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DfmMOoZ2.js → Trigger-CkXJ2vuq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BTCyMgf1.js → Trust-B02WXJPw.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DKCwIBHi.js → UkeySign-VPMkfzmw.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CQpDSzLO.js → VideoEditing-C5ezUnah.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CyFrzC3K.js → Wallet-CYfvYRrY.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-D6Nr_1kd.js → WebAuthn-DtKl-kpr.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CgR9tNrB.js → WorkflowEditor-DACITF-u.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CRG_IeyP.js → chat-qnygfnLi.js} +1 -1
- package/src/assets/web-panel/assets/{colors-MdE3TStI.js → colors-D4mISLBD.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-B0LRuiNV.js → compact-item-BBPfxYGL.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-B_V5fjcX.js → createContext-Byx4ndHp.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DBy6gsuI.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DSL-c_hr.js → hasIn-CGjLkkn3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE7bSYmW.js → index-B8UpadHC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BD0w-888.js → index-B9jH-h4X.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbZZ7P_c.js → index-BN7_TSgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIDCNwy6.js → index-BNe2lCk6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkN53KiJ.js → index-B_fHPZ8E.js} +1 -1
- package/src/assets/web-panel/assets/index-BfEDXk_E.js +1 -0
- package/src/assets/web-panel/assets/{index-BsAEd-Do.js → index-BklGH3YI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CS3bOhV7.js → index-BmKYaaGP.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3iW-mhn.js → index-BxQF1wCU.js} +1 -1
- package/src/assets/web-panel/assets/{index-SmQfEHjp.js → index-ByA1NyFP.js} +1 -1
- package/src/assets/web-panel/assets/{index-0200QxC4.js → index-BzeBBAl5.js} +1 -1
- package/src/assets/web-panel/assets/{index-c8OqO3rq.js → index-C3abGsSW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-aKWR2v.js → index-C9tcHs46.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNhWoD6d.js → index-CD5Btq6p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVDi4rS9.js → index-CFMWjuav.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEOAZm5j.js → index-CMj8qmpX.js} +1 -1
- package/src/assets/web-panel/assets/{index-C67dPW0L.js → index-CVUJjQAT.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3pJBTvo.js → index-CWhI8G14.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtOTppk8.js → index-CZjNamSU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRRY44XP.js → index-CmHE-lY_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Tg3yl8wc.js → index-CoA8WsVr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXMeisJK.js → index-Cpg3AWRy.js} +1 -1
- package/src/assets/web-panel/assets/index-CrxUwNM8.js +1 -0
- package/src/assets/web-panel/assets/{index-Bq8-2Ibv.js → index-CsVuQsY1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHp1nSur.js → index-CydHMjwc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPYshcic.js → index-DH6iC5b2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBwBZwio.js → index-DZXhKAk9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Didyuq2Z.js → index-D_g9mA1N.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdHMc9LQ.js → index-DboTeuPF.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5dqieYX.js → index-DfgpKd64.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdplnAS9.js → index-Dmfe0L2Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-KaSejHjf.js → index-Dzni_5Qw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpwGsAA-.js → index-J5JkStvY.js} +1 -1
- package/src/assets/web-panel/assets/{index-OBhlHSMN.js → index-Ne8UGllV.js} +3 -3
- package/src/assets/web-panel/assets/{index-CZICtYJ0.js → index-O09jXbir.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVgTf7CZ.js → index-Y7el_3WS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQAsL19t.js → index-c_tBkIlU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMm8tO2X.js → index-lKJWAmd4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNytQuFq.js → index-xzRKTy0R.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-P8SBN6Di.js → initDefaultProps-XuhfEIhB.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BubbjUT_.js → motion-Cbxh6AxO.js} +1 -1
- package/src/assets/web-panel/assets/{move-Chcykv0I.js → move-C7IEnFly.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DyiYuGDu.js → mtc-parser-lLADx4LL.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Rdwb_Neb.js → omit-mIbic_k-.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CCr0MYHl.js → pickAttrs-_xIYEqu3.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B1ZXz3bb.js → placementArrow-ClB-E0ny.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BGEdwej6.js → responsiveObserve-D36Bg2ut.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CpnsUMaT.js → slide-CUuO08Bl.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C7x-TVp_.js → statusUtils-BKahwLbv.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CC5k_791.js → styleChecker-2pT-T8vy.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BpZFKsJW.js → useFlexGapSupport-DyH5g7Rg.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CVq5HySy.js → useFs-CTOauB3X.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BGkdLryu.js → usePersonalDataHub-B02JYIMe.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-uLtOUEK-.js → vnode-Do22wQrc.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BfVC9iUs.js → zoom-BfnL7rit.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +8 -0
- package/src/commands/background-session.js +31 -3
- package/src/commands/chat.js +5 -1
- package/src/commands/eval.js +86 -28
- package/src/commands/llm.js +104 -1
- package/src/commands/plugin.js +161 -1
- package/src/commands/sandbox.js +121 -2
- package/src/commands/serve.js +11 -2
- package/src/commands/skill.js +29 -0
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/session-protocol.js +13 -0
- package/src/gateways/ws/ws-agent-handler.js +17 -0
- package/src/gateways/ws/ws-server.js +3 -0
- package/src/gateways/ws/ws-session-state.js +131 -0
- package/src/generated/cc-agent-protocol.schema.json +48 -1
- package/src/lazy-dispatch.js +8 -1
- package/src/lib/agent-sandbox.js +337 -53
- package/src/lib/app-server/cli-agent-kernel-adapter.js +28 -0
- package/src/lib/app-server/server.js +93 -0
- package/src/lib/background-agent-keeper-client.js +2 -0
- package/src/lib/background-agent-keeper-protocol.js +7 -0
- package/src/lib/background-agent-list-index.js +222 -0
- package/src/lib/background-agent-supervisor.js +232 -47
- package/src/lib/codex-app-server-adapter.js +82 -27
- package/src/lib/context-memory-kernel/durable-memory-port.js +31 -8
- package/src/lib/context-memory-kernel/provider-context.js +118 -19
- package/src/lib/deferred-question-context.js +278 -0
- package/src/lib/eval/evidence.js +202 -64
- package/src/lib/eval/plugin-suite.js +1181 -0
- package/src/lib/eval/runner.js +10 -3
- package/src/lib/evolution/agent-evolution-runtime-composition.js +236 -1
- package/src/lib/evolution/agent-model-projection.js +97 -1
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +154 -1
- package/src/lib/evolution/evolution-artifact-ports.js +6 -0
- package/src/lib/evolution/evolution-deployment-loader.js +87 -3
- package/src/lib/evolution/evolution-eval-gate.js +8 -0
- package/src/lib/evolution/evolution-eval-runtime-composition.js +255 -0
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +58 -2
- package/src/lib/evolution/evolution-ledger-file-backend.js +23 -2
- package/src/lib/evolution/evolution-ledger-manifest-chain.js +223 -7
- package/src/lib/evolution/evolution-ledger-v2-journal.js +525 -0
- package/src/lib/evolution/evolution-ledger-v2-manifest-backend.js +46 -2
- package/src/lib/evolution/evolution-ledger.js +57 -1
- package/src/lib/evolution/evolution-run-ledger-adapter.js +93 -0
- package/src/lib/evolution/evolution-run-wiki-evidence-resolver.js +259 -0
- package/src/lib/evolution/governed-hub-llm.js +238 -5
- package/src/lib/evolution/governed-hub-resolver.js +82 -4
- package/src/lib/evolution/governed-hub-skill.js +11 -5
- package/src/lib/evolution/governed-skill-synthesis-provider-chat.js +10 -1
- package/src/lib/evolution/skill-runtime-revalidation-ledger-adapter.js +254 -0
- package/src/lib/evolution/skill-runtime-revalidation.js +530 -0
- package/src/lib/evolution/skill-target-matrix-eval.js +4 -4
- package/src/lib/evolution/wiki-evidence-commit-guard.js +366 -0
- package/src/lib/evolution/wiki-informed-skill-proposer.js +13 -1
- package/src/lib/goal-context.js +14 -5
- package/src/lib/interaction-adapter.js +166 -1
- package/src/lib/model-capabilities.js +271 -0
- package/src/lib/model-context-catalog.js +105 -0
- package/src/lib/model-context-window.js +4 -49
- package/src/lib/openai-responses.js +428 -0
- package/src/lib/plugin-runtime/scopes.js +7 -1
- package/src/lib/remote-read-loop-guard.js +2 -2
- package/src/lib/runtime-usage-ledger.js +16 -0
- package/src/lib/session-host-snapshot.js +16 -0
- package/src/lib/skill-loader.js +12 -0
- package/src/lib/with-file-lock.js +3 -1
- package/src/repl/agent-repl.js +5 -1
- package/src/runtime/agent-core.js +348 -23
- package/src/runtime/agent-runtime.js +7 -0
- package/src/runtime/coding-agent-contract-shared.cjs +13 -1
- package/src/runtime/headless-runner.js +4 -1
- package/src/runtime/headless-stream.js +283 -45
- package/src/runtime/runtime-factory.js +26 -2
- package/src/workers/background-agent-keeper.js +23 -13
- package/src/workers/background-agent-worker.js +27 -13
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Bi2SY9E_.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-fYmQzmTA.js +0 -7
- package/src/assets/web-panel/assets/devWarning-BtFLt3qI.js +0 -1
- package/src/assets/web-panel/assets/index-BhuWQHeG.js +0 -1
- package/src/assets/web-panel/assets/index-D-4tFek3.js +0 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chmodSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
openSync,
|
|
5
|
+
closeSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
renameSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
statSync,
|
|
11
|
+
writeFileSync,
|
|
12
|
+
} from "node:fs";
|
|
13
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
|
|
16
|
+
export const BACKGROUND_AGENT_LIST_INDEX_SCHEMA =
|
|
17
|
+
"chainlesschain.background-agent-list-index/v1";
|
|
18
|
+
export const BACKGROUND_AGENT_LIST_INDEX_FILE =
|
|
19
|
+
".background-agent-list-index-v1";
|
|
20
|
+
|
|
21
|
+
const STATE_FILE = /^(bg-[A-Za-z0-9-]+)\.json$/u;
|
|
22
|
+
const MAX_INDEX_BYTES = 64 * 1024 * 1024;
|
|
23
|
+
const MAX_INDEX_ENTRIES = 1_000_000;
|
|
24
|
+
|
|
25
|
+
function sha256(value, domain) {
|
|
26
|
+
return `sha256:${createHash("sha256")
|
|
27
|
+
.update(domain)
|
|
28
|
+
.update("\0")
|
|
29
|
+
.update(value)
|
|
30
|
+
.digest("hex")}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function stateFileSignature(directory, name, id) {
|
|
34
|
+
const target = join(directory, name);
|
|
35
|
+
const link = lstatSync(target);
|
|
36
|
+
if (!link.isFile() || link.isSymbolicLink()) {
|
|
37
|
+
throw new TypeError(`background agent state is not a regular file: ${id}`);
|
|
38
|
+
}
|
|
39
|
+
const stat = statSync(target, { bigint: true });
|
|
40
|
+
return {
|
|
41
|
+
id,
|
|
42
|
+
name,
|
|
43
|
+
size: String(stat.size),
|
|
44
|
+
mtimeNs: String(stat.mtimeNs),
|
|
45
|
+
ctimeNs: String(stat.ctimeNs),
|
|
46
|
+
dev: String(stat.dev),
|
|
47
|
+
ino: String(stat.ino),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function collectInventory(directory) {
|
|
52
|
+
const files = readdirSync(directory, { withFileTypes: true })
|
|
53
|
+
.map((entry) => {
|
|
54
|
+
const match = STATE_FILE.exec(entry.name);
|
|
55
|
+
if (!match || entry.name.includes(".job.")) return null;
|
|
56
|
+
if (!entry.isFile()) {
|
|
57
|
+
throw new TypeError(
|
|
58
|
+
`background agent state is not a regular file: ${match[1]}`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return stateFileSignature(directory, entry.name, match[1]);
|
|
62
|
+
})
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.sort((left, right) => left.name.localeCompare(right.name, "en"));
|
|
65
|
+
if (files.length > MAX_INDEX_ENTRIES) {
|
|
66
|
+
throw new RangeError(
|
|
67
|
+
`background agent list index exceeds ${MAX_INDEX_ENTRIES} entries`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const encoded = JSON.stringify(files);
|
|
71
|
+
return {
|
|
72
|
+
files,
|
|
73
|
+
digest: sha256(encoded, "cc.background-agent-list-inventory/v1"),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeEntry(value, inventoryIds) {
|
|
78
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
79
|
+
if (!inventoryIds.has(value.id)) return null;
|
|
80
|
+
const startedAt = Number(value.startedAt);
|
|
81
|
+
if (!Number.isFinite(startedAt) || startedAt < 0) return null;
|
|
82
|
+
if (
|
|
83
|
+
typeof value.status !== "string" ||
|
|
84
|
+
value.status.length === 0 ||
|
|
85
|
+
value.status.length > 64
|
|
86
|
+
) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return { id: value.id, startedAt, status: value.status };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function readCachedIndex(directory, inventory) {
|
|
93
|
+
const target = join(directory, BACKGROUND_AGENT_LIST_INDEX_FILE);
|
|
94
|
+
try {
|
|
95
|
+
const stat = lstatSync(target);
|
|
96
|
+
if (
|
|
97
|
+
!stat.isFile() ||
|
|
98
|
+
stat.isSymbolicLink() ||
|
|
99
|
+
stat.size <= 0 ||
|
|
100
|
+
stat.size > MAX_INDEX_BYTES
|
|
101
|
+
) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const parsed = JSON.parse(readFileSync(target, "utf8"));
|
|
105
|
+
if (
|
|
106
|
+
!parsed ||
|
|
107
|
+
typeof parsed !== "object" ||
|
|
108
|
+
Array.isArray(parsed) ||
|
|
109
|
+
parsed.schema !== BACKGROUND_AGENT_LIST_INDEX_SCHEMA ||
|
|
110
|
+
parsed.inventoryDigest !== inventory.digest ||
|
|
111
|
+
parsed.fileCount !== inventory.files.length ||
|
|
112
|
+
!Array.isArray(parsed.entries) ||
|
|
113
|
+
parsed.entries.length > inventory.files.length
|
|
114
|
+
) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const inventoryIds = new Set(inventory.files.map((file) => file.id));
|
|
118
|
+
const entries = parsed.entries.map((entry) =>
|
|
119
|
+
normalizeEntry(entry, inventoryIds),
|
|
120
|
+
);
|
|
121
|
+
if (entries.some((entry) => entry === null)) return null;
|
|
122
|
+
if (new Set(entries.map((entry) => entry.id)).size !== entries.length) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const contentDigest = sha256(
|
|
126
|
+
JSON.stringify({
|
|
127
|
+
schema: parsed.schema,
|
|
128
|
+
inventoryDigest: parsed.inventoryDigest,
|
|
129
|
+
fileCount: parsed.fileCount,
|
|
130
|
+
entries,
|
|
131
|
+
}),
|
|
132
|
+
"cc.background-agent-list-index/v1",
|
|
133
|
+
);
|
|
134
|
+
if (parsed.digest !== contentDigest) return null;
|
|
135
|
+
return entries;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function writeCachedIndex(directory, inventory, entries) {
|
|
142
|
+
const target = join(directory, BACKGROUND_AGENT_LIST_INDEX_FILE);
|
|
143
|
+
const value = {
|
|
144
|
+
schema: BACKGROUND_AGENT_LIST_INDEX_SCHEMA,
|
|
145
|
+
inventoryDigest: inventory.digest,
|
|
146
|
+
fileCount: inventory.files.length,
|
|
147
|
+
entries,
|
|
148
|
+
};
|
|
149
|
+
value.digest = sha256(
|
|
150
|
+
JSON.stringify(value),
|
|
151
|
+
"cc.background-agent-list-index/v1",
|
|
152
|
+
);
|
|
153
|
+
const temporary = `${target}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
|
|
154
|
+
try {
|
|
155
|
+
const descriptor = openSync(temporary, "wx", 0o600);
|
|
156
|
+
try {
|
|
157
|
+
writeFileSync(descriptor, `${JSON.stringify(value)}\n`, "utf8");
|
|
158
|
+
} finally {
|
|
159
|
+
closeSync(descriptor);
|
|
160
|
+
}
|
|
161
|
+
if (process.platform !== "win32") chmodSync(temporary, 0o600);
|
|
162
|
+
renameSync(temporary, target);
|
|
163
|
+
} catch {
|
|
164
|
+
rmSync(temporary, { force: true });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function projectState(state, id) {
|
|
169
|
+
if (!state || typeof state !== "object" || String(state.id) !== id) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
const startedAt = Number(state.startedAt || 0);
|
|
173
|
+
if (!Number.isFinite(startedAt) || startedAt < 0) return null;
|
|
174
|
+
if (
|
|
175
|
+
typeof state.status !== "string" ||
|
|
176
|
+
state.status.length === 0 ||
|
|
177
|
+
state.status.length > 64
|
|
178
|
+
) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return { id, startedAt, status: state.status };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Load a content-free projection when it exactly matches every authority-file
|
|
186
|
+
* identity. A miss rebuilds from authority and retries the inventory fence;
|
|
187
|
+
* callers must fall back to their authoritative scan if a concurrent writer
|
|
188
|
+
* prevents a stable rebuild.
|
|
189
|
+
*/
|
|
190
|
+
export function loadBackgroundAgentListIndex({ directory, readState }) {
|
|
191
|
+
if (typeof directory !== "string" || !directory) {
|
|
192
|
+
throw new TypeError("background agent list index directory is required");
|
|
193
|
+
}
|
|
194
|
+
if (typeof readState !== "function") {
|
|
195
|
+
throw new TypeError("background agent list index readState is required");
|
|
196
|
+
}
|
|
197
|
+
const inventory = collectInventory(directory);
|
|
198
|
+
const cached = readCachedIndex(directory, inventory);
|
|
199
|
+
if (cached) {
|
|
200
|
+
return Object.freeze({
|
|
201
|
+
source: "index",
|
|
202
|
+
entries: Object.freeze(cached.map((entry) => Object.freeze(entry))),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const entries = inventory.files
|
|
207
|
+
.map((file) => projectState(readState(file.id), file.id))
|
|
208
|
+
.filter(Boolean);
|
|
209
|
+
const verifiedInventory = collectInventory(directory);
|
|
210
|
+
if (verifiedInventory.digest !== inventory.digest) {
|
|
211
|
+
const error = new Error(
|
|
212
|
+
"background agent authority changed while rebuilding the list index",
|
|
213
|
+
);
|
|
214
|
+
error.code = "BACKGROUND_AGENT_LIST_INDEX_RACE";
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
writeCachedIndex(directory, inventory, entries);
|
|
218
|
+
return Object.freeze({
|
|
219
|
+
source: "rebuilt",
|
|
220
|
+
entries: Object.freeze(entries.map((entry) => Object.freeze(entry))),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
@@ -60,9 +60,12 @@ import {
|
|
|
60
60
|
cleanupBackgroundAgentKeeperPipeDirectory,
|
|
61
61
|
prepareBackgroundAgentKeeperPipePath,
|
|
62
62
|
} from "./background-agent-keeper-protocol.js";
|
|
63
|
+
import { loadBackgroundAgentListIndex } from "./background-agent-list-index.js";
|
|
63
64
|
|
|
64
65
|
export const DEFAULT_HEARTBEAT_INTERVAL_MS = 5000;
|
|
65
66
|
export const DEFAULT_HEARTBEAT_STALE_MS = 120000;
|
|
67
|
+
export const DEFAULT_BACKGROUND_AGENT_PAGE_LIMIT = 50;
|
|
68
|
+
export const MAX_BACKGROUND_AGENT_PAGE_LIMIT = 200;
|
|
66
69
|
|
|
67
70
|
/**
|
|
68
71
|
* Pid identity tolerance (Gap 1: OS pid reuse). A pid alone does not identify
|
|
@@ -1370,7 +1373,14 @@ export function mutateBackgroundAgentState(id, updater, options = {}) {
|
|
|
1370
1373
|
// A live numeric pid can belong to a later process after the lock owner
|
|
1371
1374
|
// was killed. Use the lock acquisition timestamp as a one-sided identity
|
|
1372
1375
|
// fence so only an owner which existed before acquisition still blocks.
|
|
1373
|
-
|
|
1376
|
+
// Periodic telemetry may request a strict try-lock. In that mode an
|
|
1377
|
+
// incumbent is deliberately retained without a Windows PID/start-time
|
|
1378
|
+
// probe: a heartbeat must never synchronously stall a control channel
|
|
1379
|
+
// merely to determine whether it could reclaim a lock.
|
|
1380
|
+
_isOwnerAlive:
|
|
1381
|
+
options.probeLockOwner === false
|
|
1382
|
+
? () => true
|
|
1383
|
+
: isBackgroundAgentStateLockOwnerAlive,
|
|
1374
1384
|
},
|
|
1375
1385
|
);
|
|
1376
1386
|
}
|
|
@@ -1458,57 +1468,65 @@ export async function deliverBackgroundNeedsInputNotification(
|
|
|
1458
1468
|
* the only authority to start or continue a turn: a terminal, deleted,
|
|
1459
1469
|
* corrupt, differently-generated or already-owned record is never revived.
|
|
1460
1470
|
*/
|
|
1461
|
-
export function claimBackgroundAgentHeartbeat(
|
|
1471
|
+
export function claimBackgroundAgentHeartbeat(
|
|
1472
|
+
id,
|
|
1473
|
+
heartbeat = {},
|
|
1474
|
+
options = {},
|
|
1475
|
+
) {
|
|
1462
1476
|
const workerPid = Number(heartbeat.workerPid ?? heartbeat.pid);
|
|
1463
1477
|
const workerGeneration = heartbeat.workerGeneration;
|
|
1464
1478
|
if (!Number.isInteger(workerPid) || workerPid <= 0) {
|
|
1465
1479
|
throw new Error(`Invalid background worker pid: ${heartbeat.workerPid}`);
|
|
1466
1480
|
}
|
|
1467
|
-
return mutateBackgroundAgentState(
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
current.
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
current.workerClaimedPid
|
|
1479
|
-
|
|
1480
|
-
claimedWorkerPid > 0 &&
|
|
1481
|
-
claimedWorkerPid !== workerPid
|
|
1482
|
-
) {
|
|
1483
|
-
return null;
|
|
1484
|
-
}
|
|
1485
|
-
const currentWorkerPid = Number(current.workerPid ?? current.pid);
|
|
1486
|
-
if (
|
|
1487
|
-
!current.workerGeneration &&
|
|
1488
|
-
Number.isInteger(currentWorkerPid) &&
|
|
1489
|
-
currentWorkerPid > 0 &&
|
|
1490
|
-
currentWorkerPid !== workerPid &&
|
|
1491
|
-
current.launchFinalizationUncertain !== true
|
|
1492
|
-
) {
|
|
1493
|
-
return null;
|
|
1494
|
-
}
|
|
1495
|
-
return {
|
|
1496
|
-
...current,
|
|
1497
|
-
...heartbeat,
|
|
1498
|
-
id,
|
|
1499
|
-
pid: workerPid,
|
|
1500
|
-
workerPid,
|
|
1501
|
-
workerClaimedPid:
|
|
1481
|
+
return mutateBackgroundAgentState(
|
|
1482
|
+
id,
|
|
1483
|
+
(current) => {
|
|
1484
|
+
if (!current || current.status !== "running") return null;
|
|
1485
|
+
if (current.stopRequestedAt) return null;
|
|
1486
|
+
if (
|
|
1487
|
+
current.workerGeneration &&
|
|
1488
|
+
current.workerGeneration !== workerGeneration
|
|
1489
|
+
) {
|
|
1490
|
+
return null;
|
|
1491
|
+
}
|
|
1492
|
+
const claimedWorkerPid = Number(current.workerClaimedPid);
|
|
1493
|
+
if (
|
|
1502
1494
|
current.workerClaimedPid != null &&
|
|
1503
1495
|
Number.isInteger(claimedWorkerPid) &&
|
|
1504
|
-
claimedWorkerPid > 0
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1496
|
+
claimedWorkerPid > 0 &&
|
|
1497
|
+
claimedWorkerPid !== workerPid
|
|
1498
|
+
) {
|
|
1499
|
+
return null;
|
|
1500
|
+
}
|
|
1501
|
+
const currentWorkerPid = Number(current.workerPid ?? current.pid);
|
|
1502
|
+
if (
|
|
1503
|
+
!current.workerGeneration &&
|
|
1504
|
+
Number.isInteger(currentWorkerPid) &&
|
|
1505
|
+
currentWorkerPid > 0 &&
|
|
1506
|
+
currentWorkerPid !== workerPid &&
|
|
1507
|
+
current.launchFinalizationUncertain !== true
|
|
1508
|
+
) {
|
|
1509
|
+
return null;
|
|
1510
|
+
}
|
|
1511
|
+
return {
|
|
1512
|
+
...current,
|
|
1513
|
+
...heartbeat,
|
|
1514
|
+
id,
|
|
1515
|
+
pid: workerPid,
|
|
1516
|
+
workerPid,
|
|
1517
|
+
workerClaimedPid:
|
|
1518
|
+
current.workerClaimedPid != null &&
|
|
1519
|
+
Number.isInteger(claimedWorkerPid) &&
|
|
1520
|
+
claimedWorkerPid > 0
|
|
1521
|
+
? claimedWorkerPid
|
|
1522
|
+
: workerPid,
|
|
1523
|
+
workerClaimedAt: current.workerClaimedAt || Date.now(),
|
|
1524
|
+
status: "running",
|
|
1525
|
+
heartbeatAt: heartbeat.heartbeatAt ?? Date.now(),
|
|
1526
|
+
};
|
|
1527
|
+
},
|
|
1528
|
+
options,
|
|
1529
|
+
);
|
|
1512
1530
|
}
|
|
1513
1531
|
|
|
1514
1532
|
function deleteBackgroundAgentState(id, predicate = () => true) {
|
|
@@ -2038,7 +2056,21 @@ export function effectiveBackgroundAgentState(state, options = {}) {
|
|
|
2038
2056
|
return next;
|
|
2039
2057
|
}
|
|
2040
2058
|
|
|
2041
|
-
|
|
2059
|
+
function backgroundAgentStartedAt(state) {
|
|
2060
|
+
const startedAt = Number(state?.startedAt);
|
|
2061
|
+
return Number.isFinite(startedAt) && startedAt >= 0 ? startedAt : 0;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
function compareBackgroundAgentListEntries(a, b) {
|
|
2065
|
+
const newestFirst = backgroundAgentStartedAt(b) - backgroundAgentStartedAt(a);
|
|
2066
|
+
if (newestFirst !== 0) return newestFirst;
|
|
2067
|
+
return String(a.id).localeCompare(String(b.id));
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
function listBackgroundAgentEntries(
|
|
2071
|
+
options = {},
|
|
2072
|
+
{ stableTieBreak = false } = {},
|
|
2073
|
+
) {
|
|
2042
2074
|
return (
|
|
2043
2075
|
readdirSync(backgroundAgentsDir())
|
|
2044
2076
|
.filter((name) => name.endsWith(".json") && !name.includes(".job."))
|
|
@@ -2046,7 +2078,11 @@ export function listBackgroundAgents(options = {}) {
|
|
|
2046
2078
|
.filter(Boolean)
|
|
2047
2079
|
.map((state) => effectiveBackgroundAgentState(state, options))
|
|
2048
2080
|
.filter((state) => options.all || state.status === "running")
|
|
2049
|
-
.sort(
|
|
2081
|
+
.sort(
|
|
2082
|
+
stableTieBreak
|
|
2083
|
+
? compareBackgroundAgentListEntries
|
|
2084
|
+
: (a, b) => (b.startedAt || 0) - (a.startedAt || 0),
|
|
2085
|
+
)
|
|
2050
2086
|
// Display-only enrichment: attach the canonical unified lifecycle state.
|
|
2051
2087
|
// This is a list feed (never written back to disk), so the derived field
|
|
2052
2088
|
// cannot leak into the on-disk schema the way it would in the mutate paths.
|
|
@@ -2054,6 +2090,155 @@ export function listBackgroundAgents(options = {}) {
|
|
|
2054
2090
|
);
|
|
2055
2091
|
}
|
|
2056
2092
|
|
|
2093
|
+
function normalizeBackgroundAgentPageLimit(value) {
|
|
2094
|
+
if (value === undefined || value === null) {
|
|
2095
|
+
return DEFAULT_BACKGROUND_AGENT_PAGE_LIMIT;
|
|
2096
|
+
}
|
|
2097
|
+
const limit =
|
|
2098
|
+
typeof value === "string" && /^\d+$/u.test(value) ? Number(value) : value;
|
|
2099
|
+
if (
|
|
2100
|
+
!Number.isSafeInteger(limit) ||
|
|
2101
|
+
limit < 1 ||
|
|
2102
|
+
limit > MAX_BACKGROUND_AGENT_PAGE_LIMIT
|
|
2103
|
+
) {
|
|
2104
|
+
throw new TypeError(
|
|
2105
|
+
`background agent page limit must be an integer from 1 to ${MAX_BACKGROUND_AGENT_PAGE_LIMIT}`,
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
return limit;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
function encodeBackgroundAgentPageCursor(state, all) {
|
|
2112
|
+
const payload = JSON.stringify({
|
|
2113
|
+
version: 1,
|
|
2114
|
+
all: all === true,
|
|
2115
|
+
startedAt: backgroundAgentStartedAt(state),
|
|
2116
|
+
id: safeId(state?.id),
|
|
2117
|
+
});
|
|
2118
|
+
return `bg1.${Buffer.from(payload).toString("base64url")}`;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
function decodeBackgroundAgentPageCursor(value) {
|
|
2122
|
+
if (value === undefined || value === null) return null;
|
|
2123
|
+
if (
|
|
2124
|
+
typeof value !== "string" ||
|
|
2125
|
+
value.length > 768 ||
|
|
2126
|
+
!/^bg1\.[A-Za-z0-9_-]+$/u.test(value)
|
|
2127
|
+
) {
|
|
2128
|
+
throw new TypeError("background agent page cursor is invalid");
|
|
2129
|
+
}
|
|
2130
|
+
let payload;
|
|
2131
|
+
try {
|
|
2132
|
+
const encoded = value.slice(4);
|
|
2133
|
+
const decoded = Buffer.from(encoded, "base64url");
|
|
2134
|
+
if (decoded.toString("base64url") !== encoded) {
|
|
2135
|
+
throw new Error("cursor encoding is not canonical");
|
|
2136
|
+
}
|
|
2137
|
+
payload = JSON.parse(decoded.toString("utf8"));
|
|
2138
|
+
} catch {
|
|
2139
|
+
throw new TypeError("background agent page cursor is invalid");
|
|
2140
|
+
}
|
|
2141
|
+
if (
|
|
2142
|
+
!payload ||
|
|
2143
|
+
typeof payload !== "object" ||
|
|
2144
|
+
Array.isArray(payload) ||
|
|
2145
|
+
Object.keys(payload).length !== 4 ||
|
|
2146
|
+
payload.version !== 1 ||
|
|
2147
|
+
typeof payload.all !== "boolean" ||
|
|
2148
|
+
!Number.isSafeInteger(payload.startedAt) ||
|
|
2149
|
+
payload.startedAt < 0
|
|
2150
|
+
) {
|
|
2151
|
+
throw new TypeError("background agent page cursor is invalid");
|
|
2152
|
+
}
|
|
2153
|
+
let id;
|
|
2154
|
+
try {
|
|
2155
|
+
id = safeId(payload.id);
|
|
2156
|
+
} catch {
|
|
2157
|
+
throw new TypeError("background agent page cursor is invalid");
|
|
2158
|
+
}
|
|
2159
|
+
return { all: payload.all, startedAt: payload.startedAt, id };
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
function isBackgroundAgentAfterCursor(state, cursor) {
|
|
2163
|
+
const startedAt = backgroundAgentStartedAt(state);
|
|
2164
|
+
if (startedAt !== cursor.startedAt) return startedAt < cursor.startedAt;
|
|
2165
|
+
return String(state.id).localeCompare(cursor.id) > 0;
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
/**
|
|
2169
|
+
* Return one stable, bounded page from the newest-first background session
|
|
2170
|
+
* feed. The cursor is an opaque ordering fence, not an authority token.
|
|
2171
|
+
*
|
|
2172
|
+
* State files are still read to preserve the current authoritative lifecycle
|
|
2173
|
+
* reconciliation. A future read-only index may reduce that I/O, but this API
|
|
2174
|
+
* immediately bounds the object graph and protocol response retained by a
|
|
2175
|
+
* caller.
|
|
2176
|
+
*/
|
|
2177
|
+
export function listBackgroundAgentsPage(options = {}) {
|
|
2178
|
+
const limit = normalizeBackgroundAgentPageLimit(options.limit);
|
|
2179
|
+
const cursor = decodeBackgroundAgentPageCursor(options.cursor);
|
|
2180
|
+
if (cursor && cursor.all !== (options.all === true)) {
|
|
2181
|
+
throw new TypeError(
|
|
2182
|
+
"background agent page cursor does not match the requested filter",
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
let indexed = null;
|
|
2186
|
+
try {
|
|
2187
|
+
indexed = loadBackgroundAgentListIndex({
|
|
2188
|
+
directory: backgroundAgentsDir(),
|
|
2189
|
+
readState: readBackgroundAgentState,
|
|
2190
|
+
});
|
|
2191
|
+
} catch {
|
|
2192
|
+
// The index is a disposable, content-free projection. Authority races,
|
|
2193
|
+
// corruption, or an unwritable cache must retain the full-scan behavior.
|
|
2194
|
+
}
|
|
2195
|
+
if (indexed) {
|
|
2196
|
+
const projected = [];
|
|
2197
|
+
const summaries = [...indexed.entries]
|
|
2198
|
+
.filter((state) => options.all || state.status === "running")
|
|
2199
|
+
.sort(compareBackgroundAgentListEntries);
|
|
2200
|
+
for (const summary of summaries) {
|
|
2201
|
+
if (cursor && !isBackgroundAgentAfterCursor(summary, cursor)) continue;
|
|
2202
|
+
const state = readBackgroundAgentState(summary.id);
|
|
2203
|
+
if (!state) continue;
|
|
2204
|
+
const effective = effectiveBackgroundAgentState(state, options);
|
|
2205
|
+
if (!effective || (!options.all && effective.status !== "running")) {
|
|
2206
|
+
continue;
|
|
2207
|
+
}
|
|
2208
|
+
if (cursor && !isBackgroundAgentAfterCursor(effective, cursor)) continue;
|
|
2209
|
+
projected.push(withLifecycleState(effective));
|
|
2210
|
+
if (projected.length > limit) break;
|
|
2211
|
+
}
|
|
2212
|
+
const sessions = projected.slice(0, limit);
|
|
2213
|
+
const nextCursor =
|
|
2214
|
+
projected.length > sessions.length
|
|
2215
|
+
? encodeBackgroundAgentPageCursor(sessions.at(-1), options.all)
|
|
2216
|
+
: null;
|
|
2217
|
+
return Object.freeze({
|
|
2218
|
+
sessions: Object.freeze(sessions),
|
|
2219
|
+
nextCursor,
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
const eligible = cursor
|
|
2223
|
+
? listBackgroundAgentEntries(options, { stableTieBreak: true }).filter(
|
|
2224
|
+
(state) => isBackgroundAgentAfterCursor(state, cursor),
|
|
2225
|
+
)
|
|
2226
|
+
: listBackgroundAgentEntries(options, { stableTieBreak: true });
|
|
2227
|
+
const sessions = eligible.slice(0, limit);
|
|
2228
|
+
const nextCursor =
|
|
2229
|
+
eligible.length > sessions.length
|
|
2230
|
+
? encodeBackgroundAgentPageCursor(sessions.at(-1), options.all)
|
|
2231
|
+
: null;
|
|
2232
|
+
return Object.freeze({
|
|
2233
|
+
sessions: Object.freeze(sessions),
|
|
2234
|
+
nextCursor,
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
export function listBackgroundAgents(options = {}) {
|
|
2239
|
+
return listBackgroundAgentEntries(options);
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2057
2242
|
function sleepSyncMs(ms) {
|
|
2058
2243
|
try {
|
|
2059
2244
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|