chainlesschain 0.163.5 → 0.163.7
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/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/web-panel/assets/{AIOps-Cv2EE1Zy.js → AIOps-axqQfwv8.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CcpxMLuW.js → ActionButton-DV-Ro0By.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DE8mJoMI.js → Analytics-Bt72WEVu.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DKn9dk9L.js → AppLayout-BtepG55E.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-Cp3mG3Py.js → Artifacts-B7KaJ7S_.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-zvsQfF2C.js → Audit-FNFaUg7A.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Bi85SsjN.js → BackgroundAgents-DFb6J2wQ.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Cw47Dj6s.js → Backup-CC7YBhyj.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C5Ip2afH.js → BaseInput-RkgTNVtj.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-C0iAFkJv.js → Chat-hHEmGZy-.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BV9Wv7wO.js → ChatBubbleRenderer-CAP9YhHv.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-3KZZCiAF.js → Checkbox-z5wXOQP1.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-C8OMbBAz.js → Codegen-DVaDJnxy.js} +1 -1
- package/src/assets/web-panel/assets/{Col-B-MB84LJ.js → Col-CJmaRDR5.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DpR7Dxao.js → Community-DF29_2bI.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CyDtz0OS.js → Compact-DtkQkBzn.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-5tiRa2An.js → Compliance-xoHDxp7I.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DnQaKZ5x.js → Cowork-BUm-R7ht.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-BaWfoEDf.js → Cron-Cgl14ZkB.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Bu0HoHhb.js → Crosschain-BNcbmoqh.js} +1 -1
- package/src/assets/web-panel/assets/{DID-31zk8upX.js → DID-Bwja0Zo9.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BmVne-e2.js → Dashboard-Bk5XjEdJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-B4PkoOWd.js → Dropdown-Ci36Oitq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BZ6tyFtI.js → EmailListRenderer--U04ZN4q.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Osozn4Ds.js → FamilyGuardDashboard-B2gNPR2s.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DaH8TOAP.js → Federation-C4KghYn7.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-tbU0uwis.js → FormItemContext-QL7x_Q_0.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Df8LQBrL.js → GenericCardRenderer-F3Ryltwm.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DsMB6-14.js → Git-2HassK5k.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BnH0dZzd.js → Governance-DoBIp7oe.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DhnN1N_P.js → Inference--W01QhZ-.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DnNshAgn.js → KnowledgeGraph-DmvwqGpT.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BmBFX3_N.js → Logs-BBJ4yAel.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DCslfQU3.js → MarkdownRenderer-BLCZMzhJ.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CPEgWDc3.js → Marketplace-D5lNngSI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C36qjgF9.js → McpTools-CkEVOMKH.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-tNHvrgqN.js → Memory-B1Ow1xVE.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Xaf6cKjc.js → MobileBridge-SgYbUfb6.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-D2E5TTeO.js → MobileProjects-BWdOvQAj.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-D4AJ2dwV.js → Mtc-DcTXSSma.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-Df-gtn2_.js → MtcAudit-Dt3WG4oj.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-DTCHLWld.js → Multisig-B9B38UZx.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DruZ_1Jb.js → NLProgramming-B0S4GGvG.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Dx3pdGDs.js → Notes-DZ5U58Ng.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Bi3L69oL.js → NotificationSettings-PjL5ZW34.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BHY33m7C.js → OrderTableRenderer-DrAX2ESt.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BG2ZVEV-.js → Organization-Cv1GYs-c.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Viwtg4Mn.js → Overflow-Bbkp_PL3.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Clm_yc1Z.js → P2P-BD8QUgoZ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-NKmmDiOw.js → PdhVaultBrowser-BO-2PeaB.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DIL8u9Nk.js → Permissions-D_OPVa02.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B-jM4wt-.js → PersonalDataHub-mh-pEmaB.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-627I__H5.js → Pipeline-6JC2EDdb.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-2DN2qrkW.js → Privacy-CUshzUAG.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CN4wC0sF.js → ProjectInit-Cs2Aalri.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-K35vsh5H.js → ProjectSettings-1ak49tcj.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-B_wQadhX.js → Projects-BV4oApsm.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-MckaQUhQ.js → Providers-CXXxvafH.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BS3mtA_O.js → QrScannerModal-PTcDsF58.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BHp_nofL.js → QuickAsk-CDwucrJw.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DlmnPuZj.js → Recommend-CJlLVRXR.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-tsyFbvpy.js → RemoteSession-DlN-ETsO.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Ba1iWv2D.js → Reputation-DI23LR6m.js} +1 -1
- package/src/assets/web-panel/assets/{Row-NnlGC--f.js → Row-Cl7UQMrM.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CjThSIRv.js → RssFeed-CexHNfh8.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CojUyC5x.js → Search-BneBWvRN.js} +1 -1
- package/src/assets/web-panel/assets/{Security-c-sxeJEc.js → Security-DX7Phgrp.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CtcKEEmF.js → Services-BWXO8Mte.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CIBBXflN.js → Skeleton-DCyOjKPJ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-D1XOOK9n.js → Skills-CXqERsTB.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-hRfEp5Sn.js → Sla-bhbrbz8E.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Dexb1f_6.js → SpeechSettings-3IPU0HBZ.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BJsSFGYy.js → SyncSettings-DBNTQLbM.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CfSZ8A1L.js → Tasks-quP0bkN-.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-idDf47W-.js → Templates-BouWmgho.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-C-UsX6w6.js → Tenant-qv0D9s7z.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BgJtX5n1.js → Terminal-BxvbE8BB.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DP0BLKTl.js → TimelineRenderer-D-IeLnJ2.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-LwCzqETp.js → Tokens-CFMKKyVG.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D_9tVch0.js → Trigger-L8as3UuJ.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BH2UlEWh.js → Trust-MxmA4VZG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CIpD0nYb.js → UkeySign-C0VvJYRi.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-9ShhYxK_.js → VideoEditing-Bf5QZj8N.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C7afGNIG.js → Wallet-BIJ9riRz.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-bUKn6i5R.js → WebAuthn-B8p9Mhkw.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CxgQaGyp.js → WorkflowEditor-BVsuaPen.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DMVkmJwx.js → chat-BfkjoKpV.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CDzQh5nS.js → colors-DeAlsWK8.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C51kEF4D.js → compact-item-Cs97bz5j.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BTk9bXBn.js → createContext-BqbG60LH.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DPty0mGN.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CMje33rb.js → hasIn-i_RY0b1X.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpcrFxMI.js → index-0-agYlWL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BW4F5YDF.js → index-79V_zv1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRG-aqOs.js → index-B-3tEpTc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCRvG9lq.js → index-B-JVTrpr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgivOKHA.js → index-B1CbOH1i.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzCkNylB.js → index-B5UkyA3c.js} +1 -1
- package/src/assets/web-panel/assets/{index-x53Vf7la.js → index-BESLFt4q.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCLj9hcD.js → index-BOVkf1K3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CypsfcEq.js → index-BYUVuwKt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtlEqHLP.js → index-BcUXAB2s.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ld_4mFmC.js → index-BkBi5TmC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqU9njjT.js → index-Bm7lLR4c.js} +1 -1
- package/src/assets/web-panel/assets/index-Bt7g0Vq_.js +1 -0
- package/src/assets/web-panel/assets/{index-CwGPwjUu.js → index-BuLQobzW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5IWTMVl.js → index-CE2VtTlp.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJfnCuMq.js → index-CED2PL_y.js} +1 -1
- package/src/assets/web-panel/assets/{index-hg_emBsi.js → index-CG3PRyAL.js} +1 -1
- package/src/assets/web-panel/assets/{index-togc_uYn.js → index-CIG8fQ2_.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4rQCS8T.js → index-COdGA-8n.js} +1 -1
- package/src/assets/web-panel/assets/{index-0Auljb10.js → index-CeWoNHau.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd0hmPxD.js → index-Cf3WCLmy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXQBPLyE.js → index-CgsqdnRV.js} +1 -1
- package/src/assets/web-panel/assets/index-ChVT0p9u.js +1 -0
- package/src/assets/web-panel/assets/{index-h31MwrME.js → index-D2B4VPyP.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1oZReaY.js → index-D9evtvg2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfC-piWZ.js → index-DUUrv141.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9rC-fGW.js → index-DiSkx4UB.js} +3 -3
- package/src/assets/web-panel/assets/{index-CvSeVzqe.js → index-Dk2qouAz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtM8OT_7.js → index-Dk7-v4hP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBz9LIMS.js → index-DmbfhJUP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAfY7m7i.js → index-DqBgBpgp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVePe_Rf.js → index-PT_5sBMA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDLkNkLj.js → index-U4f5izHz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTlsE8Df.js → index-YXgLng0K.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeEaAEIp.js → index-ZxO_-E8a.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0-zMtKA.js → index-dIHr4B_u.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1oZQmAA.js → index-gyFpvL7l.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRLQyp2A.js → index-s1opAL-M.js} +1 -1
- package/src/assets/web-panel/assets/{index-OS-vfbTA.js → index-yObj1O4n.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-vrA8K-ac.js → initDefaultProps-C9h9yKL3.js} +1 -1
- package/src/assets/web-panel/assets/{motion-ByEzwIAl.js → motion-BvmLsPdu.js} +1 -1
- package/src/assets/web-panel/assets/{move-Ck4AWjw-.js → move-CoL4ekU1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-6p0l9SrB.js → mtc-parser-C4pMy6xV.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B1CUPQ6R.js → omit-DPuJfNxx.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-B1STg1iW.js → pickAttrs-D0KEeaK2.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DTTllkBV.js → placementArrow-BpkyINxt.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-AIrrCfBb.js → responsiveObserve-DQI_wOq8.js} +1 -1
- package/src/assets/web-panel/assets/{slide-C_fPMjdR.js → slide-DYfdDiRg.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-D5ow2yIP.js → statusUtils-B5dtvb0D.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Dy3hEA--.js → styleChecker-CF310mSj.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-WfzcfSh4.js → useFlexGapSupport-D1Qp4BLp.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CKSejfas.js → useFs-C7_iyvJK.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-od3zpPBN.js → usePersonalDataHub-DZUt_fIm.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DRD_2DFA.js → vnode-D3dsi0lZ.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DmNrEtgf.js → zoom-WdbOlvdQ.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +5 -5
- package/src/command-manifest.json +1 -1
- package/src/commands/agenda.js +76 -4
- package/src/commands/agent.js +90 -1
- package/src/commands/automation.js +462 -3
- package/src/commands/background-session.js +102 -1
- package/src/commands/chat.js +11 -2
- package/src/commands/compact.js +106 -23
- package/src/commands/cowork.js +6 -1
- package/src/commands/loop.js +187 -38
- package/src/commands/routine.js +12 -5
- package/src/commands/scheduler-daemon.js +807 -0
- package/src/commands/session-observability.js +305 -0
- package/src/commands/session.js +38 -0
- package/src/data/changelog.json +28 -2
- package/src/gateways/ws/ws-agent-handler.js +439 -5
- package/src/harness/jsonl-session-store.js +463 -125
- package/src/harness/prompt-compressor.js +86 -13
- package/src/harness/provider-backed-compaction.js +100 -27
- package/src/lib/advisor-runtime.js +3 -1
- package/src/lib/agent-schedule-store.js +737 -47
- package/src/lib/agent-team/team-adjudication.js +40 -1
- package/src/lib/automation-center-routines.js +363 -0
- package/src/lib/automation-center.js +435 -0
- package/src/lib/automation-engine.js +568 -19
- package/src/lib/automation-execution-authority.js +736 -0
- package/src/lib/background-agent-supervisor.js +871 -215
- package/src/lib/background-command-argv.js +24 -0
- package/src/lib/background-interaction-journal.js +361 -52
- package/src/lib/background-interaction-resolver.js +32 -0
- package/src/lib/background-launch-profile.js +2 -0
- package/src/lib/background-needs-input-incident.js +221 -0
- package/src/lib/causal-observability.js +2661 -0
- package/src/lib/chat-session-admission.js +76 -0
- package/src/lib/cost-budget.js +5 -7
- package/src/lib/cowork/ab-comparator-cli.js +11 -0
- package/src/lib/cowork/debate-review-cli.js +12 -0
- package/src/lib/cowork-adapter.js +159 -38
- package/src/lib/cowork-cron.js +673 -19
- package/src/lib/cowork-observe.js +5 -1
- package/src/lib/cowork-task-runner.js +112 -17
- package/src/lib/delivery-coordinator.js +142 -0
- package/src/lib/direct-model-usage.js +121 -0
- package/src/lib/file-lines.js +183 -47
- package/src/lib/llm-pricing.js +12 -17
- package/src/lib/loop.js +62 -3
- package/src/lib/permission-engine.js +14 -7
- package/src/lib/public-ide-capability-manifest.js +2 -0
- package/src/lib/routine-store.js +546 -86
- package/src/lib/runtime-usage-ledger.js +182 -0
- package/src/lib/scheduler-kernel/agenda-adapter.js +604 -29
- package/src/lib/scheduler-kernel/authority-resolver.js +163 -0
- package/src/lib/scheduler-kernel/automation-adapter.js +994 -0
- package/src/lib/scheduler-kernel/automation-event-adapter.js +757 -0
- package/src/lib/scheduler-kernel/contract.js +11 -3
- package/src/lib/scheduler-kernel/cowork-cron-adapter.js +343 -15
- package/src/lib/scheduler-kernel/loop-adapter.js +913 -0
- package/src/lib/scheduler-kernel/migration-admin.js +346 -0
- package/src/lib/scheduler-kernel/migration-rollback-admin.js +932 -0
- package/src/lib/scheduler-kernel/routine-adapter.js +691 -22
- package/src/lib/scheduler-kernel/runtime.js +63 -1
- package/src/lib/scheduler-kernel/service.js +328 -0
- package/src/lib/scheduler-kernel/source-locator-path.js +142 -0
- package/src/lib/scheduler-kernel/store.js +3439 -206
- package/src/lib/session-host-lease.js +171 -2
- package/src/lib/session-host-snapshot.js +14 -3
- package/src/lib/session-persistence-failure.js +1 -0
- package/src/lib/session-tail.js +179 -34
- package/src/lib/session-transcript-structure.js +96 -0
- package/src/lib/sub-agent-context.js +328 -22
- package/src/lib/ws-agent-handler.js +4 -1
- package/src/repl/agent-repl.js +732 -91
- package/src/repl/chat-repl.js +5 -1
- package/src/runtime/agent-core.js +1274 -208
- package/src/runtime/coding-agent-contract-shared.cjs +5 -0
- package/src/runtime/fallback-model.js +5 -0
- package/src/runtime/headless-runner.js +324 -82
- package/src/runtime/headless-stream.js +232 -26
- package/src/runtime/policies/agent-policy.js +4 -0
- package/src/workers/background-agent-worker.js +468 -98
- package/src/assets/web-panel/assets/devWarning-CdwPwYj8.js +0 -1
- package/src/assets/web-panel/assets/index-BEeOdfEe.js +0 -1
- package/src/assets/web-panel/assets/index-BTe0TOu6.js +0 -1
package/src/lib/file-lines.js
CHANGED
|
@@ -1,7 +1,64 @@
|
|
|
1
1
|
import { closeSync, fstatSync, openSync, readSync } from "node:fs";
|
|
2
|
-
import { StringDecoder } from "node:string_decoder";
|
|
3
2
|
|
|
4
3
|
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
|
4
|
+
export const DEFAULT_MAX_FILE_LINE_BYTES = 16 * 1024 * 1024;
|
|
5
|
+
export const FILE_LINE_TOO_LARGE_CODE = "CC_FILE_LINE_TOO_LARGE";
|
|
6
|
+
|
|
7
|
+
function normalizeMaxLineBytes(value) {
|
|
8
|
+
const parsed = Number(value);
|
|
9
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
10
|
+
throw new TypeError("maxLineBytes must be a positive safe integer");
|
|
11
|
+
}
|
|
12
|
+
return parsed;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeChunkSize(value) {
|
|
16
|
+
const parsed = Number(value);
|
|
17
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
18
|
+
throw new TypeError("chunkSize must be a positive safe integer");
|
|
19
|
+
}
|
|
20
|
+
return parsed;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function assertFileLineByteLength(
|
|
24
|
+
byteLength,
|
|
25
|
+
{
|
|
26
|
+
maxLineBytes = DEFAULT_MAX_FILE_LINE_BYTES,
|
|
27
|
+
code = FILE_LINE_TOO_LARGE_CODE,
|
|
28
|
+
lineNo = null,
|
|
29
|
+
direction = null,
|
|
30
|
+
label = "File line",
|
|
31
|
+
} = {},
|
|
32
|
+
) {
|
|
33
|
+
const actualBytes = Number(byteLength);
|
|
34
|
+
const maxBytes = normalizeMaxLineBytes(maxLineBytes);
|
|
35
|
+
if (!Number.isSafeInteger(actualBytes) || actualBytes < 0) {
|
|
36
|
+
throw new TypeError("byteLength must be a non-negative safe integer");
|
|
37
|
+
}
|
|
38
|
+
if (actualBytes <= maxBytes) return actualBytes;
|
|
39
|
+
const error = new Error(`${label} exceeded the ${maxBytes}-byte limit`);
|
|
40
|
+
error.code = code;
|
|
41
|
+
error.actualBytes = actualBytes;
|
|
42
|
+
error.maxBytes = maxBytes;
|
|
43
|
+
if (Number.isSafeInteger(lineNo) && lineNo > 0) error.lineNo = lineNo;
|
|
44
|
+
if (direction) error.direction = direction;
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function assertLineByteLength(byteLength, options, details) {
|
|
49
|
+
return assertFileLineByteLength(byteLength, {
|
|
50
|
+
maxLineBytes: options.maxLineBytes,
|
|
51
|
+
code: options.lineTooLargeCode,
|
|
52
|
+
label: options.lineLabel,
|
|
53
|
+
...details,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function joinedLineBuffer(parts, byteLength) {
|
|
58
|
+
if (parts.length === 0) return Buffer.alloc(0);
|
|
59
|
+
if (parts.length === 1) return parts[0];
|
|
60
|
+
return Buffer.concat(parts, byteLength);
|
|
61
|
+
}
|
|
5
62
|
|
|
6
63
|
function recordRead(ioMetrics, bytes) {
|
|
7
64
|
if (!ioMetrics || typeof ioMetrics !== "object") return;
|
|
@@ -13,7 +70,8 @@ function recordRead(ioMetrics, bytes) {
|
|
|
13
70
|
/**
|
|
14
71
|
* Stream UTF-8 lines from a file without loading the whole file. The final
|
|
15
72
|
* unterminated fragment is yielded with `terminated:false`, which lets callers
|
|
16
|
-
* distinguish a crash tail from a normal record.
|
|
73
|
+
* distinguish a crash tail from a normal record. Records are assembled as raw
|
|
74
|
+
* byte slices and checked before any Buffer.concat or UTF-8 decode.
|
|
17
75
|
*/
|
|
18
76
|
export function* iterateFileLinesSync(
|
|
19
77
|
filePath,
|
|
@@ -21,35 +79,81 @@ export function* iterateFileLinesSync(
|
|
|
21
79
|
chunkSize = DEFAULT_CHUNK_SIZE,
|
|
22
80
|
includeEmpty = false,
|
|
23
81
|
ioMetrics = null,
|
|
82
|
+
maxLineBytes = DEFAULT_MAX_FILE_LINE_BYTES,
|
|
83
|
+
lineTooLargeCode = FILE_LINE_TOO_LARGE_CODE,
|
|
84
|
+
lineLabel = "File line",
|
|
24
85
|
} = {},
|
|
25
86
|
) {
|
|
87
|
+
const lineOptions = {
|
|
88
|
+
maxLineBytes: normalizeMaxLineBytes(maxLineBytes),
|
|
89
|
+
lineTooLargeCode,
|
|
90
|
+
lineLabel,
|
|
91
|
+
};
|
|
92
|
+
const buffer = Buffer.allocUnsafe(
|
|
93
|
+
Math.min(normalizeChunkSize(chunkSize), lineOptions.maxLineBytes),
|
|
94
|
+
);
|
|
26
95
|
const fd = openSync(filePath, "r");
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let pending = "";
|
|
96
|
+
let pendingParts = [];
|
|
97
|
+
let pendingBytes = 0;
|
|
30
98
|
let lineNo = 0;
|
|
31
99
|
try {
|
|
32
100
|
for (;;) {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
recordRead(ioMetrics,
|
|
36
|
-
|
|
37
|
-
let newline;
|
|
38
|
-
while (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
pending = pending.slice(newline + 1);
|
|
101
|
+
const bytesRead = readSync(fd, buffer, 0, buffer.length, null);
|
|
102
|
+
if (bytesRead === 0) break;
|
|
103
|
+
recordRead(ioMetrics, bytesRead);
|
|
104
|
+
let start = 0;
|
|
105
|
+
let newline = buffer.indexOf(0x0a, start);
|
|
106
|
+
while (newline >= 0 && newline < bytesRead) {
|
|
107
|
+
const segment = buffer.subarray(start, newline);
|
|
108
|
+
const lineByteLength = pendingBytes + segment.length;
|
|
42
109
|
lineNo += 1;
|
|
43
|
-
|
|
44
|
-
|
|
110
|
+
assertLineByteLength(lineByteLength, lineOptions, {
|
|
111
|
+
lineNo,
|
|
112
|
+
direction: "forward",
|
|
113
|
+
});
|
|
114
|
+
let lineBytes = joinedLineBuffer(
|
|
115
|
+
segment.length > 0 ? [...pendingParts, segment] : pendingParts,
|
|
116
|
+
lineByteLength,
|
|
117
|
+
);
|
|
118
|
+
if (lineBytes.length > 0 && lineBytes[lineBytes.length - 1] === 0x0d) {
|
|
119
|
+
lineBytes = lineBytes.subarray(0, lineBytes.length - 1);
|
|
120
|
+
}
|
|
121
|
+
if (includeEmpty || lineBytes.length > 0) {
|
|
122
|
+
yield {
|
|
123
|
+
line: lineBytes.toString("utf8"),
|
|
124
|
+
lineNo,
|
|
125
|
+
terminated: true,
|
|
126
|
+
};
|
|
45
127
|
}
|
|
128
|
+
pendingParts = [];
|
|
129
|
+
pendingBytes = 0;
|
|
130
|
+
start = newline + 1;
|
|
131
|
+
newline = buffer.indexOf(0x0a, start);
|
|
46
132
|
}
|
|
133
|
+
|
|
134
|
+
const trailing = buffer.subarray(start, bytesRead);
|
|
135
|
+
const nextPendingBytes = pendingBytes + trailing.length;
|
|
136
|
+
assertLineByteLength(nextPendingBytes, lineOptions, {
|
|
137
|
+
lineNo: lineNo + 1,
|
|
138
|
+
direction: "forward",
|
|
139
|
+
});
|
|
140
|
+
if (trailing.length > 0) pendingParts.push(Buffer.from(trailing));
|
|
141
|
+
pendingBytes = nextPendingBytes;
|
|
47
142
|
}
|
|
48
|
-
|
|
49
|
-
if (
|
|
143
|
+
|
|
144
|
+
if (pendingBytes > 0 || includeEmpty) {
|
|
50
145
|
lineNo += 1;
|
|
51
|
-
|
|
52
|
-
|
|
146
|
+
assertLineByteLength(pendingBytes, lineOptions, {
|
|
147
|
+
lineNo,
|
|
148
|
+
direction: "forward",
|
|
149
|
+
});
|
|
150
|
+
if (includeEmpty || pendingBytes > 0) {
|
|
151
|
+
const pending = joinedLineBuffer(pendingParts, pendingBytes);
|
|
152
|
+
yield {
|
|
153
|
+
line: pending.toString("utf8"),
|
|
154
|
+
lineNo,
|
|
155
|
+
terminated: false,
|
|
156
|
+
};
|
|
53
157
|
}
|
|
54
158
|
}
|
|
55
159
|
} finally {
|
|
@@ -68,36 +172,57 @@ export function* iterateFileLinesReverseSync(
|
|
|
68
172
|
chunkSize = DEFAULT_CHUNK_SIZE,
|
|
69
173
|
includeEmpty = false,
|
|
70
174
|
ioMetrics = null,
|
|
175
|
+
maxLineBytes = DEFAULT_MAX_FILE_LINE_BYTES,
|
|
176
|
+
lineTooLargeCode = FILE_LINE_TOO_LARGE_CODE,
|
|
177
|
+
lineLabel = "File line",
|
|
71
178
|
} = {},
|
|
72
179
|
) {
|
|
180
|
+
const lineOptions = {
|
|
181
|
+
maxLineBytes: normalizeMaxLineBytes(maxLineBytes),
|
|
182
|
+
lineTooLargeCode,
|
|
183
|
+
lineLabel,
|
|
184
|
+
};
|
|
185
|
+
const readChunkSize = Math.min(
|
|
186
|
+
normalizeChunkSize(chunkSize),
|
|
187
|
+
lineOptions.maxLineBytes,
|
|
188
|
+
);
|
|
73
189
|
const fd = openSync(filePath, "r");
|
|
74
|
-
|
|
75
|
-
let
|
|
76
|
-
if (size > 0) {
|
|
77
|
-
const last = Buffer.allocUnsafe(1);
|
|
78
|
-
const bytes = readSync(fd, last, 0, 1, size - 1);
|
|
79
|
-
recordRead(ioMetrics, bytes);
|
|
80
|
-
endsWithNewline = last[0] === 0x0a;
|
|
81
|
-
}
|
|
82
|
-
let position = size;
|
|
83
|
-
let carry = Buffer.alloc(0);
|
|
190
|
+
let carryParts = [];
|
|
191
|
+
let carryBytes = 0;
|
|
84
192
|
let isPhysicalTail = true;
|
|
85
193
|
try {
|
|
194
|
+
const size = fstatSync(fd).size;
|
|
195
|
+
let endsWithNewline = false;
|
|
196
|
+
if (size > 0) {
|
|
197
|
+
const last = Buffer.allocUnsafe(1);
|
|
198
|
+
const bytesRead = readSync(fd, last, 0, 1, size - 1);
|
|
199
|
+
recordRead(ioMetrics, bytesRead);
|
|
200
|
+
endsWithNewline = last[0] === 0x0a;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let position = size;
|
|
86
204
|
while (position > 0) {
|
|
87
|
-
const length = Math.min(
|
|
205
|
+
const length = Math.min(readChunkSize, position);
|
|
88
206
|
position -= length;
|
|
89
207
|
const chunk = Buffer.allocUnsafe(length);
|
|
90
|
-
const
|
|
91
|
-
recordRead(ioMetrics,
|
|
92
|
-
|
|
93
|
-
let
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
208
|
+
const bytesRead = readSync(fd, chunk, 0, length, position);
|
|
209
|
+
recordRead(ioMetrics, bytesRead);
|
|
210
|
+
let end = bytesRead;
|
|
211
|
+
for (let index = bytesRead - 1; index >= 0; index -= 1) {
|
|
212
|
+
if (chunk[index] !== 0x0a) continue;
|
|
213
|
+
const segment = chunk.subarray(index + 1, end);
|
|
214
|
+
const lineByteLength = segment.length + carryBytes;
|
|
215
|
+
assertLineByteLength(lineByteLength, lineOptions, {
|
|
216
|
+
direction: "reverse",
|
|
217
|
+
});
|
|
218
|
+
let lineBytes = joinedLineBuffer(
|
|
219
|
+
segment.length > 0 ? [segment, ...carryParts] : carryParts,
|
|
220
|
+
lineByteLength,
|
|
221
|
+
);
|
|
222
|
+
if (lineBytes.length > 0 && lineBytes[lineBytes.length - 1] === 0x0d) {
|
|
223
|
+
lineBytes = lineBytes.subarray(0, lineBytes.length - 1);
|
|
99
224
|
}
|
|
100
|
-
const line =
|
|
225
|
+
const line = lineBytes.toString("utf8");
|
|
101
226
|
const terminated = isPhysicalTail ? endsWithNewline : true;
|
|
102
227
|
// A file ending in "\n" produces one empty physical-tail slice. Skip
|
|
103
228
|
// it without changing the termination status of the real last line.
|
|
@@ -105,16 +230,27 @@ export function* iterateFileLinesReverseSync(
|
|
|
105
230
|
yield { line, terminated };
|
|
106
231
|
isPhysicalTail = false;
|
|
107
232
|
}
|
|
233
|
+
carryParts = [];
|
|
234
|
+
carryBytes = 0;
|
|
108
235
|
end = index;
|
|
109
236
|
}
|
|
110
|
-
|
|
237
|
+
|
|
238
|
+
const prefix = chunk.subarray(0, end);
|
|
239
|
+
const nextCarryBytes = prefix.length + carryBytes;
|
|
240
|
+
assertLineByteLength(nextCarryBytes, lineOptions, {
|
|
241
|
+
direction: "reverse",
|
|
242
|
+
});
|
|
243
|
+
if (prefix.length > 0) carryParts.unshift(Buffer.from(prefix));
|
|
244
|
+
carryBytes = nextCarryBytes;
|
|
111
245
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
246
|
+
|
|
247
|
+
if (carryBytes > 0 || includeEmpty) {
|
|
248
|
+
assertLineByteLength(carryBytes, lineOptions, { direction: "reverse" });
|
|
249
|
+
let lineBytes = joinedLineBuffer(carryParts, carryBytes);
|
|
250
|
+
if (lineBytes.length > 0 && lineBytes[lineBytes.length - 1] === 0x0d) {
|
|
251
|
+
lineBytes = lineBytes.subarray(0, lineBytes.length - 1);
|
|
116
252
|
}
|
|
117
|
-
const line =
|
|
253
|
+
const line = lineBytes.toString("utf8");
|
|
118
254
|
if (includeEmpty || line.length > 0) {
|
|
119
255
|
yield { line, terminated: !isPhysicalTail };
|
|
120
256
|
}
|
package/src/lib/llm-pricing.js
CHANGED
|
@@ -84,11 +84,6 @@ export const PRICE_TABLE = Object.freeze({
|
|
|
84
84
|
],
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
const round = (n, dp = 6) => {
|
|
88
|
-
const f = Math.pow(10, dp);
|
|
89
|
-
return Math.round((Number(n) + Number.EPSILON) * f) / f;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
87
|
/**
|
|
93
88
|
* Merge user-supplied price overrides (typically `config.llm.pricing`) onto the
|
|
94
89
|
* built-in table. Override shape mirrors PRICE_TABLE:
|
|
@@ -221,22 +216,22 @@ export function estimateCost({
|
|
|
221
216
|
rate: null,
|
|
222
217
|
};
|
|
223
218
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
);
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
219
|
+
// Keep full IEEE-754 precision throughout authority and budget arithmetic.
|
|
220
|
+
// Rounding each component to six decimals lets a real sub-microdollar call
|
|
221
|
+
// become zero and can make a hard `maxUsd` gate pass incorrectly. Display
|
|
222
|
+
// surfaces may format these values, but safety decisions consume the raw sum.
|
|
223
|
+
const inputCost = (Number(inputTokens) / 1e6) * rate.in;
|
|
224
|
+
const outputCost = (Number(outputTokens) / 1e6) * rate.out;
|
|
225
|
+
const cacheReadCost =
|
|
226
|
+
(Number(cacheReadTokens) / 1e6) * rate.in * cacheReadMultiplier(provider);
|
|
227
|
+
const cacheCreationCost =
|
|
228
|
+
(Number(cacheCreationTokens) / 1e6) * rate.in * CACHE_WRITE_MULTIPLIER;
|
|
232
229
|
return {
|
|
233
230
|
inputCost,
|
|
234
231
|
outputCost,
|
|
235
232
|
cacheReadCost,
|
|
236
233
|
cacheCreationCost,
|
|
237
|
-
totalCost:
|
|
238
|
-
inputCost + outputCost + cacheReadCost + cacheCreationCost,
|
|
239
|
-
),
|
|
234
|
+
totalCost: inputCost + outputCost + cacheReadCost + cacheCreationCost,
|
|
240
235
|
currency: "USD",
|
|
241
236
|
matched: true,
|
|
242
237
|
free: rate.pattern === "free",
|
|
@@ -280,7 +275,7 @@ export function priceRollup(aggregate, { table } = {}) {
|
|
|
280
275
|
const unpriced = [];
|
|
281
276
|
for (const row of byModel) {
|
|
282
277
|
if (row.matched) {
|
|
283
|
-
totalCost
|
|
278
|
+
totalCost += row.cost;
|
|
284
279
|
} else if (row.totalTokens > 0) {
|
|
285
280
|
unpriced.push({
|
|
286
281
|
provider: row.provider,
|
package/src/lib/loop.js
CHANGED
|
@@ -86,17 +86,73 @@ export function summarizeLoopEvents(events) {
|
|
|
86
86
|
let config = null;
|
|
87
87
|
let completedIterations = 0;
|
|
88
88
|
let lastExitCode = null;
|
|
89
|
+
const scheduled = new Map();
|
|
90
|
+
const completed = new Set();
|
|
91
|
+
let schedulerMigration = null;
|
|
89
92
|
for (const e of events || []) {
|
|
90
93
|
if (e?.type === "loop_config") {
|
|
91
|
-
|
|
94
|
+
const migrationFence = e.data?.schedulerMigrationFence;
|
|
95
|
+
if (
|
|
96
|
+
migrationFence?.schemaVersion === 1 &&
|
|
97
|
+
migrationFence.state === "retired" &&
|
|
98
|
+
migrationFence.originalConfig &&
|
|
99
|
+
typeof migrationFence.originalConfig === "object" &&
|
|
100
|
+
!Array.isArray(migrationFence.originalConfig)
|
|
101
|
+
) {
|
|
102
|
+
// New clients recover the original definition from the fence while an
|
|
103
|
+
// older client sees the intentionally empty operands and refuses to
|
|
104
|
+
// start. The source is restored by appending the original loop_config.
|
|
105
|
+
config = migrationFence.originalConfig;
|
|
106
|
+
schedulerMigration = migrationFence;
|
|
107
|
+
} else {
|
|
108
|
+
config = e.data || null;
|
|
109
|
+
}
|
|
110
|
+
} else if (
|
|
111
|
+
e?.type === "loop_iteration_scheduled" &&
|
|
112
|
+
Number.isSafeInteger(e.data?.n) &&
|
|
113
|
+
e.data.n > 0 &&
|
|
114
|
+
Number.isSafeInteger(e.data?.scheduledFor) &&
|
|
115
|
+
e.data.scheduledFor >= 0
|
|
116
|
+
) {
|
|
117
|
+
scheduled.set(e.data.n, {
|
|
118
|
+
n: e.data.n,
|
|
119
|
+
scheduledFor: e.data.scheduledFor,
|
|
120
|
+
});
|
|
92
121
|
} else if (e?.type === "loop_iteration") {
|
|
93
122
|
completedIterations += 1;
|
|
123
|
+
if (Number.isSafeInteger(e.data?.n) && e.data.n > 0) {
|
|
124
|
+
completed.add(e.data.n);
|
|
125
|
+
}
|
|
94
126
|
if (e.data && typeof e.data.exitCode !== "undefined") {
|
|
95
127
|
lastExitCode = e.data.exitCode;
|
|
96
128
|
}
|
|
129
|
+
} else if (e?.type === "loop_scheduler_migration") {
|
|
130
|
+
schedulerMigration = e.data || null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const pendingIteration =
|
|
134
|
+
[...scheduled.values()]
|
|
135
|
+
.filter((entry) => !completed.has(entry.n))
|
|
136
|
+
.sort((a, b) => a.n - b.n)[0] || null;
|
|
137
|
+
return {
|
|
138
|
+
config,
|
|
139
|
+
completedIterations,
|
|
140
|
+
lastExitCode,
|
|
141
|
+
pendingIteration,
|
|
142
|
+
schedulerMigration,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Return durable terminal data for one loop iteration, if already recorded. */
|
|
147
|
+
export function findLoopIterationResult(events, iteration) {
|
|
148
|
+
if (!Number.isSafeInteger(iteration) || iteration < 1) return null;
|
|
149
|
+
for (let index = (events || []).length - 1; index >= 0; index -= 1) {
|
|
150
|
+
const event = events[index];
|
|
151
|
+
if (event?.type === "loop_iteration" && event.data?.n === iteration) {
|
|
152
|
+
return event.data || null;
|
|
97
153
|
}
|
|
98
154
|
}
|
|
99
|
-
return
|
|
155
|
+
return null;
|
|
100
156
|
}
|
|
101
157
|
|
|
102
158
|
/** Default abortable sleep — resolves early if the signal aborts. */
|
|
@@ -187,7 +243,10 @@ export async function runLoop({
|
|
|
187
243
|
if (untilExitZero && res.exitCode === 0) {
|
|
188
244
|
return { iterations: i, stoppedBy: "exit-zero", results };
|
|
189
245
|
}
|
|
190
|
-
if (
|
|
246
|
+
if (
|
|
247
|
+
untilRegex &&
|
|
248
|
+
(res.matchedUntil === true || untilRegex.test(res.output || ""))
|
|
249
|
+
) {
|
|
191
250
|
return { iterations: i, stoppedBy: "match", results };
|
|
192
251
|
}
|
|
193
252
|
if (maxIterations && i >= maxIterations) {
|
|
@@ -91,6 +91,8 @@ export const PERMISSION_SCOPES = [
|
|
|
91
91
|
"audit:write",
|
|
92
92
|
"config:read",
|
|
93
93
|
"config:write",
|
|
94
|
+
"automation:execute",
|
|
95
|
+
"automation:connector:*",
|
|
94
96
|
"system:admin",
|
|
95
97
|
];
|
|
96
98
|
|
|
@@ -296,7 +298,7 @@ function _grantActive(expiresAt, nowMs) {
|
|
|
296
298
|
/**
|
|
297
299
|
* Get all roles and direct permissions for a user.
|
|
298
300
|
*/
|
|
299
|
-
export function getUserPermissions(db, userDid) {
|
|
301
|
+
export function getUserPermissions(db, userDid, { nowMs = Date.now() } = {}) {
|
|
300
302
|
ensurePermissionTables(db);
|
|
301
303
|
|
|
302
304
|
// Get active role grants
|
|
@@ -305,7 +307,6 @@ export function getUserPermissions(db, userDid) {
|
|
|
305
307
|
.all(userDid);
|
|
306
308
|
|
|
307
309
|
// Filter out expired grants
|
|
308
|
-
const nowMs = Date.now();
|
|
309
310
|
const grants = allGrants.filter((g) => _grantActive(g.expires_at, nowMs));
|
|
310
311
|
|
|
311
312
|
// Get direct permissions (not expired)
|
|
@@ -347,8 +348,8 @@ export function getUserPermissions(db, userDid) {
|
|
|
347
348
|
/**
|
|
348
349
|
* Check if a user has a specific permission.
|
|
349
350
|
*/
|
|
350
|
-
export function checkPermission(db, userDid, permission) {
|
|
351
|
-
const userPerms = getUserPermissions(db, userDid);
|
|
351
|
+
export function checkPermission(db, userDid, permission, options) {
|
|
352
|
+
const userPerms = getUserPermissions(db, userDid, options);
|
|
352
353
|
|
|
353
354
|
// Admin wildcard
|
|
354
355
|
if (userPerms.isAdmin) return true;
|
|
@@ -356,9 +357,15 @@ export function checkPermission(db, userDid, permission) {
|
|
|
356
357
|
// Exact match
|
|
357
358
|
if (userPerms.effectivePermissions.includes(permission)) return true;
|
|
358
359
|
|
|
359
|
-
//
|
|
360
|
-
|
|
361
|
-
|
|
360
|
+
// Hierarchical wildcard match. In addition to `note:*`, this allows an
|
|
361
|
+
// explicitly narrow grant such as `automation:connector:*` to authorize
|
|
362
|
+
// `automation:connector:slack` without granting unrelated automation
|
|
363
|
+
// capabilities.
|
|
364
|
+
const segments = permission.split(":");
|
|
365
|
+
for (let length = segments.length - 1; length >= 1; length -= 1) {
|
|
366
|
+
const wildcard = `${segments.slice(0, length).join(":")}:*`;
|
|
367
|
+
if (userPerms.effectivePermissions.includes(wildcard)) return true;
|
|
368
|
+
}
|
|
362
369
|
|
|
363
370
|
return false;
|
|
364
371
|
}
|
|
@@ -50,6 +50,7 @@ const VSCODE_COMMANDS = [
|
|
|
50
50
|
"chainlesschain.session.prStatus",
|
|
51
51
|
"chainlesschain.background.agents",
|
|
52
52
|
"chainlesschain.sessions.workbench",
|
|
53
|
+
"chainlesschain.automation.center",
|
|
53
54
|
"chainlesschain.remote.control",
|
|
54
55
|
"chainlesschain.usage.show",
|
|
55
56
|
"chainlesschain.plugins.manage",
|
|
@@ -101,6 +102,7 @@ const JETBRAINS_ACTIONS = [
|
|
|
101
102
|
"chainlesschain.bg.Agents",
|
|
102
103
|
"chainlesschain.remote.Control",
|
|
103
104
|
"chainlesschain.sessions.Workbench",
|
|
105
|
+
"chainlesschain.automation.Center",
|
|
104
106
|
"chainlesschain.usage.Show",
|
|
105
107
|
"chainlesschain.artifacts.Browse",
|
|
106
108
|
"chainlesschain.policy.Viewer",
|