chainlesschain 0.165.2 → 0.165.4
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 +151 -29
- package/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-lS1R0Cza.js → AIOps-C__nnraW.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BmlgBPsj.js → ActionButton-CsKWHEZ-.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CNKczv1S.js → Analytics-BhIjfjWO.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BuZGJARQ.js → AppLayout-BjuXFzWZ.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-Dm73Sl-O.js → Artifacts-k6whDkZJ.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BGMW7A5e.js → Audit-C4FBMYRh.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BQ6M54JR.js → BackgroundAgents-C_vRrSXk.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DBzGP0bR.js → Backup-CngFZeVp.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-D1mLCoOV.js → BaseInput-B_H9EBRm.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BUfcN7p8.js → Chat-DLEC5kt7.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Du0Yg5Pi.js → Checkbox-m3ClR4xh.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B2RXziHW.js → Codegen-CmLSFrgp.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DWhekOrR.js → Col-DFGQteHN.js} +1 -1
- package/src/assets/web-panel/assets/{Community-HQhrkon4.js → Community-EUUVORHE.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-izHNb4uz.js → Compact-2PSg0Yr7.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BDwLy41-.js → Compliance-CcKur7gB.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DFJhLa7K.js → Cowork-DK_Fezfa.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DYY9RG7-.js → Cron-Dbw89xl7.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-rr7tJTtv.js → Crosschain-CH66h_Ec.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D2sfHRLi.js → DID-D1igRb17.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-q_eGdhha.js → Dashboard-DVAsrobk.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Cufrh2Bz.js → Dropdown-Dl7W8AQs.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-B-kdv10f.js → EmailListRenderer-D5tpaEQC.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D21XjgfG.js → FamilyGuardDashboard-O_VQf8gq.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CAwRTLZH.js → Federation-BTVcgS9n.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-vx3daNDF.js → FormItemContext-BRnkJAIz.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CRWY2wU4.js → GenericCardRenderer-8OWSCkDj.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Q6SwauyD.js → Git-WXLYTQj2.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BJ1q4IQZ.js → Governance-BezaUhtc.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CSWjROgD.js → Inference-CuRay3Gu.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-fzuQsWuY.js → KnowledgeGraph-DfWLDW1m.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DVF8Mnns.js → Logs-C55y2pPY.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BQP7LOA4.js → Marketplace-Dk08Uycg.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BNlJCmNc.js → McpTools-C11fSB5V.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-DLLg5WQ4.js → Memory-mr0eZc97.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CR2skNAF.js → MobileBridge-C0iCSRbb.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BxVavObl.js → MobileProjects-CY-xMG8Z.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-B17pgqMI.js → Mtc-Bplqc_Wc.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-4UmV5cIL.js → MtcAudit-BlzwsoSM.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DoMEXEcU.js → Multisig-BBLmTty6.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-h9DMXFX4.js → NLProgramming-DtT1uf1W.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cihn-xX8.js → Notes-DcqQAxTK.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BE6ZGa3R.js → NotificationSettings-cb0kWoZe.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CnwojoSI.js → OrderTableRenderer-kw9HZQNN.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B2vpwmjb.js → Organization-DlTlbkUN.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-C4z28R0-.js → Overflow-pp1qs-oY.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BVlFkwhW.js → P2P-BESLnyYz.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BXbEdL5B.js → PdhVaultBrowser-B-J1mGuf.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-C28xs4lp.js → Permissions-B36PeT1_.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dgl_rBjM.js → PersonalDataHub-CTK2ad-3.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-3QUIhrKI.js → Pipeline-zJNGXsac.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-8Gh33OZ5.js → Privacy-b9fz0Qf0.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BNmg4tZi.js → ProjectInit-DjZbZgiB.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-vgMfeCf9.js → ProjectSettings-C-hHJXlD.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CXoOa8eX.js → Providers-Dz-CAYYU.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BhfTF0oS.js → QrScannerModal-A0yoQ3-3.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-C746HEAj.js → QuickAsk-DrFgFJNC.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BSej8Eo0.js → Recommend-KC8hF1g4.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-f3gmnlG3.js → RemoteSession-DGTPIk6u.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-CyvybpkN.js → Reputation-QmbwM7Yk.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DwV2PeYn.js → Row-BxjJylia.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-11GrzjZy.js → RssFeed-B7Wda1J6.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BjKXkrAY.js → Search-_plQ_YSA.js} +1 -1
- package/src/assets/web-panel/assets/{Security-eWiFb9Yk.js → Security-CD2YJZfx.js} +2 -2
- package/src/assets/web-panel/assets/{Services-CdCsMAtt.js → Services-qQE7NmwE.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-4Q2UTKQ5.js → Skeleton-DgQqkFf7.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-v4rtYrRj.js → Skills-C4C0oFFt.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CRhsFvd_.js → Sla-Px2N2B5c.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DN6C3lQd.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-D16zonl7.js → SyncSettings-jm5aH_m7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Xxozz0i_.js → Tasks-DlkJcv67.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CXJpPtCT.js → Templates-DyrTHUBs.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-XRV0L1ZF.js → Tenant-mC434PtL.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CYuO1i_w.js → Terminal-o0_s3NgK.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BXqitWsu.js → TimelineRenderer-DAGlAXOw.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BvPrDHIy.js → Tokens-BnulS_bs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DhCrSTf5.js → Trigger-suQbVXYM.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DmsuZD6K.js → Trust-DbIJAPo5.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-D_yrts5C.js → UkeySign-CQNpbIHi.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B01ihPrm.js → VideoEditing-D8aWCERk.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DpUjKa-N.js → Wallet-CuLpYfZc.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CL07yON6.js → WebAuthn-CsfG8ZMc.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-CLMflnRr.js → WorkflowEditor-BYsEAZbd.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DtUqe8h_.js → chat-FT4Um3-I.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DPKrgVfy.js → colors-CKbS_Wlt.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BTt2GtOm.js → compact-item-CFJtzuJM.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D_VeUc1J.js → createContext-B9kSRW6K.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-1kndiylj.js → hasIn-DR-x0Xpi.js} +1 -1
- package/src/assets/web-panel/assets/{index-dVc-CXY4.js → index-7jHr_A_k.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFIZ_1Sf.js → index-B3ZX81mK.js} +1 -1
- package/src/assets/web-panel/assets/{index-fk2Rjacw.js → index-B7osrXqy.js} +3 -3
- package/src/assets/web-panel/assets/{index-CHG-KcS4.js → index-BGbPnC5x.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dbbymxpj.js → index-BI5hP_qy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaZAbw9A.js → index-BK3TiKVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzjkKUmR.js → index-BVPU43b1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAXfRKfG.js → index-BW-t9n-q.js} +1 -1
- package/src/assets/web-panel/assets/{index-8nz_fmlj.js → index-BfFCQHL3.js} +1 -1
- package/src/assets/web-panel/assets/index-Bizb4hkw.js +1 -0
- package/src/assets/web-panel/assets/{index-DdkplL9R.js → index-Bt4fS2eL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DG2MZQ3f.js → index-BwR7yc2h.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfKBRdSS.js → index-Bzz0C1BZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-GsoUQ2M7.js → index-C2JvmYRc.js} +1 -1
- package/src/assets/web-panel/assets/{index-l1BETrDb.js → index-CGaXUsIs.js} +1 -1
- package/src/assets/web-panel/assets/{index-4bna2_SR.js → index-CJmIQ9XR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfGl8CqG.js → index-C_enk9I_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D95OFht7.js → index-Cr2FLUsD.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsJCDUJG.js → index-CvrCCqJt.js} +1 -1
- package/src/assets/web-panel/assets/{index-sDoMHTNA.js → index-D4LfPvWA.js} +1 -1
- package/src/assets/web-panel/assets/{index-By5n8up9.js → index-DMGmg497.js} +1 -1
- package/src/assets/web-panel/assets/{index-ocf06EVQ.js → index-DShSkww-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bhudc_Ez.js → index-DTxNy0ZU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CX81qSM4.js → index-DW4BWjTk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlDNVgBt.js → index-DWdqx7zI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcO04eDn.js → index-Dap9c3eD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dwqmtk65.js → index-DcfxhKXR.js} +1 -1
- package/src/assets/web-panel/assets/{index-2eyAXE3d.js → index-DdkIhfi-.js} +1 -1
- package/src/assets/web-panel/assets/{index-brCInrzq.js → index-DeSgw-WV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJ8vXGOa.js → index-DiNUEpSQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHpBMWCh.js → index-DjEP7WkH.js} +1 -1
- package/src/assets/web-panel/assets/{index-m31Ka8hZ.js → index-DtkFqPVA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtF7EN49.js → index-KqDaCApw.js} +1 -1
- package/src/assets/web-panel/assets/index-OHSl-xCh.js +1 -0
- package/src/assets/web-panel/assets/{index-B9KvcS7R.js → index-OSzjTzGM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CauoBQkH.js → index-QBSeNVO3.js} +1 -1
- package/src/assets/web-panel/assets/{index-FzdwcSy2.js → index-WE08gMJP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxdTdBix.js → index-r2VI1Ikk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BepLZwgB.js → index-tqSEHPRT.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CvyYJd7z.js → initDefaultProps-Dd7n7VgI.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DYZkhUdz.js → motion-BwxouRw1.js} +1 -1
- package/src/assets/web-panel/assets/{move-slnJRVT1.js → move-DObN0PG9.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DVc0CKmy.js → mtc-parser-B3nOUQc7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BCXAtHCq.js → omit-o9WU2Nbl.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DBsemz_K.js → pickAttrs-CL_vvfQ_.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BRtkJ-nv.js → placementArrow-CYrM2tyK.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Cebq7EZn.js → responsiveObserve-k_06cQ96.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DT7qvId2.js → slide-CPKG0pxS.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-pQiITLkG.js → statusUtils-BDDEeLTw.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-c-19BVKQ.js → styleChecker-Cmb7RTh7.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DsS5-0bE.js → useFlexGapSupport-e2IedC4N.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CxGiMe5n.js → useFs-CE_gxVnm.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-D9btVID5.js → usePersonalDataHub-BkMIIKMz.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CUYFsFEN.js → vnode-DW886XV1.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CXmn_35j.js → zoom-Dwo_adaf.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 +105 -35
- package/src/commands/artifacts.js +363 -33
- package/src/commands/cowork.js +202 -36
- package/src/commands/session-budget.js +155 -0
- package/src/commands/session-location.js +948 -0
- package/src/commands/session.js +72 -46
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/artifact-protocol.js +77 -13
- package/src/gateways/ws/session-protocol.js +391 -24
- package/src/gateways/ws/ws-agent-handler.js +191 -14
- package/src/gateways/ws/ws-server.js +14 -3
- package/src/gateways/ws/ws-session-gateway.js +101 -5
- package/src/harness/jsonl-session-store.js +1210 -0
- package/src/lib/agent-team/team-runner.js +43 -13
- package/src/lib/artifact-access-ledger.js +461 -0
- package/src/lib/artifact-cleanup-ledger.js +861 -0
- package/src/lib/artifact-deletion-ledger.js +677 -0
- package/src/lib/artifact-store.js +256 -51
- package/src/lib/background-agent-keeper-protocol.js +75 -4
- package/src/lib/background-agent-launch-barrier.js +74 -0
- package/src/lib/background-agent-supervisor.js +149 -5
- package/src/lib/background-turn-bootstrap-protocol.js +4 -0
- package/src/lib/command-lifecycle-report.js +8 -4
- package/src/lib/cowork-adapter.js +9 -5
- package/src/lib/cowork-task-runner.js +20 -0
- package/src/lib/cowork-workflow.js +178 -20
- package/src/lib/direct-model-usage.js +62 -22
- package/src/lib/dynamic-workflow-runtime.js +3184 -111
- package/src/lib/execution-location-result-apply.js +339 -0
- package/src/lib/execution-location-result-artifact.js +271 -0
- package/src/lib/execution-location-result-review.js +253 -0
- package/src/lib/execution-location-result-store.js +331 -0
- package/src/lib/execution-location-result.js +617 -0
- package/src/lib/execution-location-target.js +236 -1
- package/src/lib/managed-tool-checkpoint.js +47 -4
- package/src/lib/process-execution-broker/workspace-transaction.js +3 -0
- package/src/lib/session-budget-production-root.js +376 -0
- package/src/lib/session-budget-runtime.js +109 -18
- package/src/lib/session-budget-usage.js +63 -0
- package/src/lib/session-host-snapshot.js +19 -2
- package/src/lib/session-resource-budget.js +766 -105
- package/src/lib/sub-agent-context.js +69 -22
- package/src/lib/web-ui-server.js +12 -1
- package/src/lib/with-file-lock.js +175 -26
- package/src/repl/agent-repl.js +237 -16
- package/src/runtime/agent-core.js +123 -0
- package/src/runtime/headless-runner.js +135 -46
- package/src/runtime/headless-stream.js +133 -25
- package/src/runtime/policies/agent-policy.js +10 -0
- package/src/workers/background-agent-keeper.js +112 -19
- package/src/workers/background-agent-worker.js +383 -134
- package/src/workers/background-turn-bootstrap.js +13 -8
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CY5m8ZHg.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-BLif4QSg.js +0 -1
- package/src/assets/web-panel/assets/Projects-BhtAVilc.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-DLjKsDVu.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Bj5nUW2v.js +0 -1
- package/src/assets/web-panel/assets/index-CPcM-eqo.js +0 -1
- package/src/assets/web-panel/assets/index-xeSyVoRe.js +0 -1
|
@@ -109,6 +109,16 @@ import {
|
|
|
109
109
|
readSessionHostResumeState,
|
|
110
110
|
} from "../lib/session-host-snapshot.js";
|
|
111
111
|
import { acquireSessionHostLease } from "../lib/session-host-lease.js";
|
|
112
|
+
import {
|
|
113
|
+
openProductionSessionBudgetRoot,
|
|
114
|
+
sessionBudgetAdmissionError,
|
|
115
|
+
} from "../lib/session-budget-production-root.js";
|
|
116
|
+
import {
|
|
117
|
+
beginSessionBudgetUsage,
|
|
118
|
+
markSessionBudgetUsageUnknown,
|
|
119
|
+
recordSessionBudgetUsage,
|
|
120
|
+
rejectSessionBudgetUsageUnknown,
|
|
121
|
+
} from "../lib/session-budget-usage.js";
|
|
112
122
|
import { classifyStreamRetryReason } from "../lib/stream-retry.js";
|
|
113
123
|
import {
|
|
114
124
|
SideEffectLedger,
|
|
@@ -809,6 +819,7 @@ async function runTurn(
|
|
|
809
819
|
waitForOutput,
|
|
810
820
|
persistUsageEvent,
|
|
811
821
|
persistToolEvent,
|
|
822
|
+
sessionBudget,
|
|
812
823
|
now = Date.now,
|
|
813
824
|
},
|
|
814
825
|
) {
|
|
@@ -1008,6 +1019,11 @@ async function runTurn(
|
|
|
1008
1019
|
}
|
|
1009
1020
|
case "token-usage":
|
|
1010
1021
|
if (event.ledgerPersisted !== true) persistUsageEvent?.(event);
|
|
1022
|
+
recordSessionBudgetUsage(
|
|
1023
|
+
sessionBudget,
|
|
1024
|
+
event,
|
|
1025
|
+
"headless usage settlement",
|
|
1026
|
+
);
|
|
1011
1027
|
// 用量归因: attributed child-loop usage (sub-agent / isolated skill)
|
|
1012
1028
|
// is forwarded on the stream (same wire shape) and counted toward the
|
|
1013
1029
|
// cost budget, but stays out of the turn's `usage` envelope, which
|
|
@@ -1044,6 +1060,15 @@ async function runTurn(
|
|
|
1044
1060
|
case "model-usage-started":
|
|
1045
1061
|
case "model-usage-unknown":
|
|
1046
1062
|
if (event.ledgerPersisted !== true) persistUsageEvent?.(event);
|
|
1063
|
+
if (event.type === "model-usage-started") {
|
|
1064
|
+
beginSessionBudgetUsage(
|
|
1065
|
+
sessionBudget,
|
|
1066
|
+
event,
|
|
1067
|
+
"headless provider call",
|
|
1068
|
+
);
|
|
1069
|
+
} else if (markSessionBudgetUsageUnknown(sessionBudget, event)) {
|
|
1070
|
+
rejectSessionBudgetUsageUnknown(event, "headless provider call");
|
|
1071
|
+
}
|
|
1047
1072
|
break;
|
|
1048
1073
|
case "compaction-usage-unknown":
|
|
1049
1074
|
if (event.ledgerPersisted !== true) {
|
|
@@ -1053,6 +1078,12 @@ async function runTurn(
|
|
|
1053
1078
|
code: event.code || "provider_transport_outcome_unknown",
|
|
1054
1079
|
});
|
|
1055
1080
|
}
|
|
1081
|
+
if (markSessionBudgetUsageUnknown(sessionBudget, event)) {
|
|
1082
|
+
rejectSessionBudgetUsageUnknown(
|
|
1083
|
+
event,
|
|
1084
|
+
"headless semantic compaction",
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1056
1087
|
emit({
|
|
1057
1088
|
type: "compaction_usage_unknown",
|
|
1058
1089
|
provider: event.provider,
|
|
@@ -1249,6 +1280,7 @@ async function withStreamSessionHostLease(options, deps, streamCleanup, task) {
|
|
|
1249
1280
|
deps.acquireSessionHostLease ||
|
|
1250
1281
|
(!hasInjectedSessionStore ? acquireSessionHostLease : null);
|
|
1251
1282
|
let lease = null;
|
|
1283
|
+
let budgetRoot = null;
|
|
1252
1284
|
let onLeaseAbort = null;
|
|
1253
1285
|
try {
|
|
1254
1286
|
if (persist && typeof acquireHostLease === "function") {
|
|
@@ -1262,7 +1294,7 @@ async function withStreamSessionHostLease(options, deps, streamCleanup, task) {
|
|
|
1262
1294
|
if (lease.signal.aborted) onLeaseAbort();
|
|
1263
1295
|
}
|
|
1264
1296
|
}
|
|
1265
|
-
|
|
1297
|
+
let scopedOptions = lease?.signal
|
|
1266
1298
|
? {
|
|
1267
1299
|
...options,
|
|
1268
1300
|
signal: options.signal
|
|
@@ -1270,12 +1302,28 @@ async function withStreamSessionHostLease(options, deps, streamCleanup, task) {
|
|
|
1270
1302
|
: lease.signal,
|
|
1271
1303
|
}
|
|
1272
1304
|
: options;
|
|
1305
|
+
budgetRoot = openProductionSessionBudgetRoot(
|
|
1306
|
+
persist ? sessionId : null,
|
|
1307
|
+
options.sessionBudgetRoot,
|
|
1308
|
+
{
|
|
1309
|
+
persist,
|
|
1310
|
+
signal: scopedOptions.signal || null,
|
|
1311
|
+
table: options.sessionBudgetRoot?.table,
|
|
1312
|
+
open: deps.openSessionBudget,
|
|
1313
|
+
store: deps.sessionBudgetStore,
|
|
1314
|
+
registry: deps.sessionBudgetRegistry,
|
|
1315
|
+
},
|
|
1316
|
+
);
|
|
1317
|
+
if (budgetRoot.enabled) {
|
|
1318
|
+
scopedOptions = { ...scopedOptions, ...budgetRoot.options };
|
|
1319
|
+
}
|
|
1273
1320
|
return await task(scopedOptions, {
|
|
1274
1321
|
...deps,
|
|
1275
1322
|
[STREAM_SESSION_ID]: sessionId,
|
|
1276
1323
|
[STREAM_SESSION_HOST_LEASE]: lease,
|
|
1277
1324
|
});
|
|
1278
1325
|
} finally {
|
|
1326
|
+
budgetRoot?.close?.();
|
|
1279
1327
|
if (lease?.signal && onLeaseAbort) {
|
|
1280
1328
|
lease.signal.removeEventListener("abort", onLeaseAbort);
|
|
1281
1329
|
}
|
|
@@ -1646,8 +1694,9 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
1646
1694
|
const { loadHooks, projectHookTrustNotice, attachAuthorityErrors } =
|
|
1647
1695
|
await import("../lib/settings-hooks.cjs");
|
|
1648
1696
|
const loaded = loadHooks({ cwd, settingsFile: options.settingsFile });
|
|
1649
|
-
const { mergePluginHooks } =
|
|
1650
|
-
|
|
1697
|
+
const { mergePluginHooks } = await import(
|
|
1698
|
+
"../lib/plugin-runtime/hooks.js"
|
|
1699
|
+
);
|
|
1651
1700
|
const effectiveHooks = mergePluginHooks(
|
|
1652
1701
|
attachAuthorityErrors(loaded.hooks, loaded.authorityErrors),
|
|
1653
1702
|
{ cwd },
|
|
@@ -1728,8 +1777,9 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
1728
1777
|
});
|
|
1729
1778
|
|
|
1730
1779
|
if (managedSettings) {
|
|
1731
|
-
const { assertManagedPermissionMode } =
|
|
1732
|
-
|
|
1780
|
+
const { assertManagedPermissionMode } = await import(
|
|
1781
|
+
"../lib/settings-loader.cjs"
|
|
1782
|
+
);
|
|
1733
1783
|
assertManagedPermissionMode(options.permissionMode, managedSettings);
|
|
1734
1784
|
}
|
|
1735
1785
|
const perm = resolvePermissionMode(options.permissionMode);
|
|
@@ -2217,8 +2267,9 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2217
2267
|
: null;
|
|
2218
2268
|
if (!instructionExcludes) {
|
|
2219
2269
|
try {
|
|
2220
|
-
const { readStringArraySetting } =
|
|
2221
|
-
|
|
2270
|
+
const { readStringArraySetting } = await import(
|
|
2271
|
+
"../lib/settings-loader.cjs"
|
|
2272
|
+
);
|
|
2222
2273
|
const fromSettings = readStringArraySetting("instructionExcludes", {
|
|
2223
2274
|
cwd,
|
|
2224
2275
|
settingsFile: options.settingsFile,
|
|
@@ -2283,8 +2334,9 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2283
2334
|
// Best-effort; no-op when project memory is off or no hook is registered.
|
|
2284
2335
|
if (settingsHooks && _loadedInstructions) {
|
|
2285
2336
|
try {
|
|
2286
|
-
const { runInstructionsLoadedHooks } =
|
|
2287
|
-
|
|
2337
|
+
const { runInstructionsLoadedHooks } = await import(
|
|
2338
|
+
"../lib/settings-hook-events.js"
|
|
2339
|
+
);
|
|
2288
2340
|
const ctx = runInstructionsLoadedHooks(settingsHooks, {
|
|
2289
2341
|
files: _loadedInstructions.files,
|
|
2290
2342
|
cwd,
|
|
@@ -2324,8 +2376,9 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2324
2376
|
// settings.json SessionStart hooks → inject session context once (observe-only).
|
|
2325
2377
|
if (settingsHooks) {
|
|
2326
2378
|
try {
|
|
2327
|
-
const { runSessionStartHooks } =
|
|
2328
|
-
|
|
2379
|
+
const { runSessionStartHooks } = await import(
|
|
2380
|
+
"../lib/settings-hook-events.js"
|
|
2381
|
+
);
|
|
2329
2382
|
const ctx = runSessionStartHooks(settingsHooks, {
|
|
2330
2383
|
source: "startup",
|
|
2331
2384
|
cwd,
|
|
@@ -2696,8 +2749,9 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2696
2749
|
// the single-prompt runner + the REPL /add-dir path. Best-effort.
|
|
2697
2750
|
if (hostMcp?.mcpClient && additionalDirectories.length > 0) {
|
|
2698
2751
|
try {
|
|
2699
|
-
const { notifyMcpRootsChanged, workspaceRootDirs } =
|
|
2700
|
-
|
|
2752
|
+
const { notifyMcpRootsChanged, workspaceRootDirs } = await import(
|
|
2753
|
+
"../repl/add-dir.js"
|
|
2754
|
+
);
|
|
2701
2755
|
notifyMcpRootsChanged(
|
|
2702
2756
|
[hostMcp.mcpClient],
|
|
2703
2757
|
workspaceRootDirs(options.cwd || process.cwd(), additionalDirectories),
|
|
@@ -2798,6 +2852,7 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
2798
2852
|
cwd,
|
|
2799
2853
|
additionalDirectories,
|
|
2800
2854
|
sessionId,
|
|
2855
|
+
sessionBudget: options.sessionBudget || null,
|
|
2801
2856
|
sessionHostSnapshot: canonicalResume?.snapshot || null,
|
|
2802
2857
|
// Auto-checkpoint (Claude-Code parity): snapshot the work tree before each
|
|
2803
2858
|
// mutating tool so a stream consumer (e.g. the IDE chat panel) can rewind.
|
|
@@ -3281,13 +3336,19 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
3281
3336
|
maxOutputTokens: options.compactionMaxOutputTokens,
|
|
3282
3337
|
summaryInputMaxChars: options.compactionInputMaxChars,
|
|
3283
3338
|
onProviderCallStart: () => {
|
|
3284
|
-
|
|
3339
|
+
const event = {
|
|
3285
3340
|
type: "model-usage-started",
|
|
3286
3341
|
callId: compactionCallId,
|
|
3287
3342
|
provider,
|
|
3288
3343
|
model,
|
|
3289
3344
|
source: "semantic-compaction",
|
|
3290
|
-
}
|
|
3345
|
+
};
|
|
3346
|
+
persistUsageEvent?.(event);
|
|
3347
|
+
beginSessionBudgetUsage(
|
|
3348
|
+
options.sessionBudget,
|
|
3349
|
+
event,
|
|
3350
|
+
"headless semantic compaction",
|
|
3351
|
+
);
|
|
3291
3352
|
compactionProviderStarted = true;
|
|
3292
3353
|
return compactionCallId;
|
|
3293
3354
|
},
|
|
@@ -3296,14 +3357,21 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
3296
3357
|
if (isAbortError(error) || options.signal?.aborted) throw error;
|
|
3297
3358
|
if (error?.runtimeLedgerPersistence === true) throw error;
|
|
3298
3359
|
if (compactionProviderStarted) {
|
|
3299
|
-
|
|
3360
|
+
const event = {
|
|
3300
3361
|
type: "model-usage-unknown",
|
|
3301
3362
|
callId: compactionCallId,
|
|
3302
3363
|
provider,
|
|
3303
3364
|
model,
|
|
3304
3365
|
source: "semantic-compaction",
|
|
3305
3366
|
code: "provider_call_failed",
|
|
3306
|
-
}
|
|
3367
|
+
};
|
|
3368
|
+
persistUsageEvent?.(event);
|
|
3369
|
+
if (markSessionBudgetUsageUnknown(options.sessionBudget, event)) {
|
|
3370
|
+
rejectSessionBudgetUsageUnknown(
|
|
3371
|
+
event,
|
|
3372
|
+
"headless semantic compaction",
|
|
3373
|
+
);
|
|
3374
|
+
}
|
|
3307
3375
|
}
|
|
3308
3376
|
emit({
|
|
3309
3377
|
type: "compaction-degraded",
|
|
@@ -3316,14 +3384,21 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
3316
3384
|
emit({ type: "compaction-degraded", ...degradedEvent });
|
|
3317
3385
|
}
|
|
3318
3386
|
if (usageUnknownEvent) {
|
|
3319
|
-
|
|
3387
|
+
const event = {
|
|
3320
3388
|
type: "model-usage-unknown",
|
|
3321
3389
|
callId: compactionCallId,
|
|
3322
3390
|
provider: usageUnknownEvent.provider || provider,
|
|
3323
3391
|
model: usageUnknownEvent.model || model,
|
|
3324
3392
|
source: "semantic-compaction",
|
|
3325
3393
|
code: "provider_transport_outcome_unknown",
|
|
3326
|
-
}
|
|
3394
|
+
};
|
|
3395
|
+
persistUsageEvent?.(event);
|
|
3396
|
+
if (markSessionBudgetUsageUnknown(options.sessionBudget, event)) {
|
|
3397
|
+
rejectSessionBudgetUsageUnknown(
|
|
3398
|
+
event,
|
|
3399
|
+
"headless semantic compaction",
|
|
3400
|
+
);
|
|
3401
|
+
}
|
|
3327
3402
|
emit({
|
|
3328
3403
|
type: "compaction_usage_unknown",
|
|
3329
3404
|
...usageUnknownEvent,
|
|
@@ -3344,12 +3419,18 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
3344
3419
|
}
|
|
3345
3420
|
let costExceeded = false;
|
|
3346
3421
|
if (usageEvent) {
|
|
3347
|
-
|
|
3422
|
+
const event = {
|
|
3348
3423
|
type: "token-usage",
|
|
3349
3424
|
callId: compactionCallId,
|
|
3350
3425
|
...usageEvent,
|
|
3351
3426
|
source: "semantic-compaction",
|
|
3352
|
-
}
|
|
3427
|
+
};
|
|
3428
|
+
persistUsageEvent?.(event);
|
|
3429
|
+
recordSessionBudgetUsage(
|
|
3430
|
+
options.sessionBudget,
|
|
3431
|
+
event,
|
|
3432
|
+
"headless semantic compaction usage settlement",
|
|
3433
|
+
);
|
|
3353
3434
|
emit({ type: "token_usage", ...usageEvent });
|
|
3354
3435
|
if (costBudget) {
|
|
3355
3436
|
costBudget.add(usageEvent);
|
|
@@ -3609,8 +3690,9 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
3609
3690
|
// session can honour the user's standing preferences ("越用越聪明"
|
|
3610
3691
|
// flywheel). Best-effort — a learning ledger must never break the chat.
|
|
3611
3692
|
try {
|
|
3612
|
-
const { appendFeedback } =
|
|
3613
|
-
|
|
3693
|
+
const { appendFeedback } = await import(
|
|
3694
|
+
"../lib/pdh-feedback-ledger.js"
|
|
3695
|
+
);
|
|
3614
3696
|
appendFeedback({ sessionId, turnId, kind, comment });
|
|
3615
3697
|
} catch {
|
|
3616
3698
|
/* persistence is best-effort */
|
|
@@ -3741,8 +3823,9 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
3741
3823
|
// settings.json UserPromptSubmit hooks. block → skip this turn; context → inject.
|
|
3742
3824
|
if (settingsHooks) {
|
|
3743
3825
|
try {
|
|
3744
|
-
const { runUserPromptSubmitHooks } =
|
|
3745
|
-
|
|
3826
|
+
const { runUserPromptSubmitHooks } = await import(
|
|
3827
|
+
"../lib/settings-hook-events.js"
|
|
3828
|
+
);
|
|
3746
3829
|
const ups = runUserPromptSubmitHooks(settingsHooks, {
|
|
3747
3830
|
prompt: userContent,
|
|
3748
3831
|
cwd,
|
|
@@ -3958,6 +4041,7 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
3958
4041
|
now: deps.now || Date.now,
|
|
3959
4042
|
persistUsageEvent,
|
|
3960
4043
|
persistToolEvent,
|
|
4044
|
+
sessionBudget: options.sessionBudget || null,
|
|
3961
4045
|
},
|
|
3962
4046
|
);
|
|
3963
4047
|
} catch (err) {
|
|
@@ -4001,6 +4085,30 @@ async function runAgentHeadlessStreamInWorkspace(
|
|
|
4001
4085
|
break;
|
|
4002
4086
|
}
|
|
4003
4087
|
}
|
|
4088
|
+
if (
|
|
4089
|
+
err?.code === "CC_SESSION_BUDGET_EXHAUSTED" ||
|
|
4090
|
+
err?.code === "CC_SESSION_BUDGET_USAGE_UNKNOWN" ||
|
|
4091
|
+
options.sessionBudget?.signal?.aborted === true
|
|
4092
|
+
) {
|
|
4093
|
+
const budgetReason =
|
|
4094
|
+
options.sessionBudget?.reason?.() ||
|
|
4095
|
+
err.budgetReason ||
|
|
4096
|
+
"session-aborted";
|
|
4097
|
+
emit({
|
|
4098
|
+
type: "result",
|
|
4099
|
+
subtype: "error_session_budget",
|
|
4100
|
+
is_error: true,
|
|
4101
|
+
code: err.code,
|
|
4102
|
+
error:
|
|
4103
|
+
err?.code === "CC_SESSION_BUDGET_USAGE_UNKNOWN"
|
|
4104
|
+
? err.message
|
|
4105
|
+
: sessionBudgetAdmissionError(budgetReason, "run").message,
|
|
4106
|
+
budget_reason: budgetReason,
|
|
4107
|
+
turn: turns,
|
|
4108
|
+
});
|
|
4109
|
+
sawError = true;
|
|
4110
|
+
break;
|
|
4111
|
+
}
|
|
4004
4112
|
const isAbort =
|
|
4005
4113
|
err?.name === "AbortError" || /abort/i.test(err?.message || "");
|
|
4006
4114
|
if (isAbort && !options.signal?.aborted) {
|
|
@@ -49,6 +49,16 @@ export function resolveAgentPolicy({
|
|
|
49
49
|
: [],
|
|
50
50
|
worktreeId:
|
|
51
51
|
overrides.worktreeId == null ? null : String(overrides.worktreeId),
|
|
52
|
+
sessionBudgetRoot:
|
|
53
|
+
overrides.sessionBudgetRoot &&
|
|
54
|
+
typeof overrides.sessionBudgetRoot === "object"
|
|
55
|
+
? Object.freeze({
|
|
56
|
+
...overrides.sessionBudgetRoot,
|
|
57
|
+
limits: Object.freeze({
|
|
58
|
+
...(overrides.sessionBudgetRoot.limits || {}),
|
|
59
|
+
}),
|
|
60
|
+
})
|
|
61
|
+
: null,
|
|
52
62
|
...(overrides.observabilityScope &&
|
|
53
63
|
typeof overrides.observabilityScope === "object"
|
|
54
64
|
? { observabilityScope: { ...overrides.observabilityScope } }
|
|
@@ -9,8 +9,15 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import net from "node:net";
|
|
12
|
-
import {
|
|
13
|
-
|
|
12
|
+
import {
|
|
13
|
+
readFileSync,
|
|
14
|
+
renameSync,
|
|
15
|
+
rmSync,
|
|
16
|
+
unlinkSync,
|
|
17
|
+
writeFileSync,
|
|
18
|
+
writeSync,
|
|
19
|
+
} from "node:fs";
|
|
20
|
+
import { dirname, resolve } from "node:path";
|
|
14
21
|
import { fileURLToPath } from "node:url";
|
|
15
22
|
import { createNdjsonReader } from "../lib/background-session-transport.js";
|
|
16
23
|
import {
|
|
@@ -28,6 +35,7 @@ import {
|
|
|
28
35
|
BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS,
|
|
29
36
|
BACKGROUND_AGENT_KEEPER_HELLO,
|
|
30
37
|
BACKGROUND_AGENT_KEEPER_HEARTBEAT,
|
|
38
|
+
BACKGROUND_AGENT_KEEPER_IDENTITY_PROBE_DELAY_MS,
|
|
31
39
|
BACKGROUND_AGENT_KEEPER_HEARTBEAT_TIMEOUT_MS,
|
|
32
40
|
BACKGROUND_AGENT_KEEPER_PERSIST_RETRY_TIMEOUT_MS,
|
|
33
41
|
BACKGROUND_AGENT_KEEPER_PROTOCOL_VERSION,
|
|
@@ -35,12 +43,15 @@ import {
|
|
|
35
43
|
BACKGROUND_AGENT_KEEPER_RETIRE,
|
|
36
44
|
BACKGROUND_AGENT_KEEPER_RETIRED,
|
|
37
45
|
BACKGROUND_AGENT_KEEPER_STATE_LOCK_TIMEOUT_MS,
|
|
46
|
+
backgroundAgentKeeperLaunchClaimPath,
|
|
38
47
|
cleanupBackgroundAgentKeeperPipeDirectory,
|
|
48
|
+
createBackgroundAgentKeeperLaunchClaim,
|
|
39
49
|
createBackgroundAgentKeeperMessage,
|
|
40
50
|
normalizeBackgroundAgentKeeperHello,
|
|
41
51
|
normalizeBackgroundAgentKeeperTurn,
|
|
42
52
|
sameBackgroundAgentKeeperTurn,
|
|
43
53
|
} from "../lib/background-agent-keeper-protocol.js";
|
|
54
|
+
import { waitForBackgroundAgentLaunchBarrier } from "../lib/background-agent-launch-barrier.js";
|
|
44
55
|
|
|
45
56
|
const STARTUP_TIMEOUT_MS = 30_000;
|
|
46
57
|
const HEARTBEAT_INTERVAL_MS = 1_000;
|
|
@@ -132,18 +143,29 @@ export function runBackgroundAgentKeeperHeartbeat({
|
|
|
132
143
|
reportPersistenceFailure = reportKeeperPersistenceFailure,
|
|
133
144
|
}) {
|
|
134
145
|
if (finishing) return false;
|
|
146
|
+
// Startup is deliberately serialized: the keeper publishes its listening
|
|
147
|
+
// claim, then remains read-only until the worker has claimed and
|
|
148
|
+
// authenticated. Persisting a pre-auth heartbeat would reintroduce the
|
|
149
|
+
// first-write lock race the launch barrier prevents.
|
|
150
|
+
if (!workerSocket || !authenticatedHello) return false;
|
|
135
151
|
const currentTime = now();
|
|
152
|
+
const workerHeartbeatAge = Number.isFinite(workerHeartbeatAt)
|
|
153
|
+
? Math.max(0, currentTime - workerHeartbeatAt)
|
|
154
|
+
: 0;
|
|
136
155
|
const workerHeartbeatExpired =
|
|
137
156
|
workerSocket &&
|
|
138
157
|
authenticatedHello &&
|
|
139
158
|
Number.isFinite(workerHeartbeatAt) &&
|
|
140
|
-
|
|
141
|
-
|
|
159
|
+
workerHeartbeatAge > BACKGROUND_AGENT_KEEPER_HEARTBEAT_TIMEOUT_MS;
|
|
160
|
+
const workerIdentityProbeDue =
|
|
161
|
+
armedTurn &&
|
|
162
|
+
Number.isFinite(workerHeartbeatAt) &&
|
|
163
|
+
workerHeartbeatAge > BACKGROUND_AGENT_KEEPER_IDENTITY_PROBE_DELAY_MS;
|
|
142
164
|
if (
|
|
143
165
|
workerSocket &&
|
|
144
166
|
authenticatedHello &&
|
|
145
167
|
(workerHeartbeatExpired ||
|
|
146
|
-
(
|
|
168
|
+
(workerIdentityProbeDue &&
|
|
147
169
|
!workerIdentityAlive(
|
|
148
170
|
authenticatedHello.workerPid,
|
|
149
171
|
authenticatedWorkerStartedAt,
|
|
@@ -153,8 +175,9 @@ export function runBackgroundAgentKeeperHeartbeat({
|
|
|
153
175
|
// after its worker dies if a platform helper retained a duplicate.
|
|
154
176
|
// Socket EOF and PID visibility are therefore not sufficient lifetime
|
|
155
177
|
// signals. The authenticated application heartbeat provides an
|
|
156
|
-
// independent bounded fence
|
|
157
|
-
//
|
|
178
|
+
// independent bounded fence. Delay the PID/start probe until application
|
|
179
|
+
// heartbeats are already stale so healthy Windows keepers do not create a
|
|
180
|
+
// synchronized WMIC/PowerShell probe storm under normal operation.
|
|
158
181
|
finishForWorkerDisconnect();
|
|
159
182
|
workerSocket.destroy();
|
|
160
183
|
return false;
|
|
@@ -239,7 +262,9 @@ export function stopBackgroundAgentKeeperTurnTrees(targets, options = {}) {
|
|
|
239
262
|
leaderAlive ||
|
|
240
263
|
(platform !== "win32" &&
|
|
241
264
|
target.processGroup === true &&
|
|
242
|
-
processTreeExecutionAlive(target.pid, target.startedAt
|
|
265
|
+
processTreeExecutionAlive(target.pid, target.startedAt, {
|
|
266
|
+
processGroup: true,
|
|
267
|
+
}));
|
|
243
268
|
if (residualAlive) {
|
|
244
269
|
failures.push(error?.message || String(error));
|
|
245
270
|
}
|
|
@@ -248,6 +273,46 @@ export function stopBackgroundAgentKeeperTurnTrees(targets, options = {}) {
|
|
|
248
273
|
return failures;
|
|
249
274
|
}
|
|
250
275
|
|
|
276
|
+
export async function waitForBackgroundAgentKeeperTurnTreesToStop(
|
|
277
|
+
targets,
|
|
278
|
+
options = {},
|
|
279
|
+
) {
|
|
280
|
+
const now = options.now || Date.now;
|
|
281
|
+
const sleep =
|
|
282
|
+
options.sleep ||
|
|
283
|
+
((milliseconds) =>
|
|
284
|
+
new Promise((resolvePromise) =>
|
|
285
|
+
setTimeout(resolvePromise, milliseconds),
|
|
286
|
+
));
|
|
287
|
+
const processTreeExecutionAlive =
|
|
288
|
+
options.isProcessTreeExecutionAlive ||
|
|
289
|
+
isBackgroundProcessTreeExecutionAlive;
|
|
290
|
+
const timeoutMs = Math.max(
|
|
291
|
+
1,
|
|
292
|
+
Number(options.timeoutMs) ||
|
|
293
|
+
BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS,
|
|
294
|
+
);
|
|
295
|
+
// Each strict macOS liveness probe invokes ps. Polling every 10 ms across
|
|
296
|
+
// 20 keepers created a process storm precisely while launchd was trying to
|
|
297
|
+
// reap the killed groups. A 100 ms cadence remains responsive and bounded
|
|
298
|
+
// while leaving the host enough scheduling capacity to establish absence.
|
|
299
|
+
const pollMs = Math.max(1, Number(options.pollMs) || 100);
|
|
300
|
+
const deadline = now() + timeoutMs;
|
|
301
|
+
const observeAlive = () =>
|
|
302
|
+
targets.filter((target) =>
|
|
303
|
+
processTreeExecutionAlive(target.pid, target.startedAt, {
|
|
304
|
+
processGroup: target.processGroup !== false,
|
|
305
|
+
}),
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
let alive = observeAlive();
|
|
309
|
+
while (alive.length > 0 && now() < deadline) {
|
|
310
|
+
await sleep(Math.max(1, Math.min(pollMs, deadline - now())));
|
|
311
|
+
alive = observeAlive();
|
|
312
|
+
}
|
|
313
|
+
return alive;
|
|
314
|
+
}
|
|
315
|
+
|
|
251
316
|
async function cleanupTurn(job, turn, reason) {
|
|
252
317
|
const requestedAt = Date.now();
|
|
253
318
|
const requestedPersistence = await retryKeeperPersistence(() =>
|
|
@@ -275,17 +340,7 @@ async function cleanupTurn(job, turn, reason) {
|
|
|
275
340
|
);
|
|
276
341
|
const failures = stopBackgroundAgentKeeperTurnTrees(targets);
|
|
277
342
|
|
|
278
|
-
const
|
|
279
|
-
Date.now() + BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS;
|
|
280
|
-
let alive = targets.filter((target) =>
|
|
281
|
-
isBackgroundProcessTreeExecutionAlive(target.pid, target.startedAt),
|
|
282
|
-
);
|
|
283
|
-
while (alive.length > 0 && Date.now() < deadline) {
|
|
284
|
-
await new Promise((resolvePromise) => setTimeout(resolvePromise, 10));
|
|
285
|
-
alive = targets.filter((target) =>
|
|
286
|
-
isBackgroundProcessTreeExecutionAlive(target.pid, target.startedAt),
|
|
287
|
-
);
|
|
288
|
-
}
|
|
343
|
+
const alive = await waitForBackgroundAgentKeeperTurnTreesToStop(targets);
|
|
289
344
|
const confirmed = alive.length === 0 && failures.length === 0;
|
|
290
345
|
let finalPersistenceAttempts = 0;
|
|
291
346
|
const finalPersistence = await retryKeeperPersistence(() => {
|
|
@@ -322,6 +377,24 @@ async function cleanupTurn(job, turn, reason) {
|
|
|
322
377
|
|
|
323
378
|
export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
324
379
|
const job = JSON.parse(readFileSync(jobFile, "utf8"));
|
|
380
|
+
const launchClaimPath = backgroundAgentKeeperLaunchClaimPath(
|
|
381
|
+
job.id,
|
|
382
|
+
job.keeperGeneration,
|
|
383
|
+
options.backgroundAgentsDirectory || dirname(resolve(jobFile)),
|
|
384
|
+
);
|
|
385
|
+
const launchBarrier = await waitForBackgroundAgentLaunchBarrier({
|
|
386
|
+
id: job.id,
|
|
387
|
+
workerGeneration: job.workerGeneration,
|
|
388
|
+
expectedPid: process.pid,
|
|
389
|
+
pidField: "keeperPid",
|
|
390
|
+
readState: options.readBackgroundAgentState || readBackgroundAgentState,
|
|
391
|
+
timeoutMs: Number(options.launchBarrierTimeoutMs) || undefined,
|
|
392
|
+
sleep: options.launchBarrierSleep,
|
|
393
|
+
});
|
|
394
|
+
if (launchBarrier.status !== "ready") {
|
|
395
|
+
removeJobFile(jobFile);
|
|
396
|
+
return { status: `launch-${launchBarrier.status}` };
|
|
397
|
+
}
|
|
325
398
|
removeJobFile(jobFile);
|
|
326
399
|
const startedAt = Date.now();
|
|
327
400
|
const claim = mutateBackgroundAgentState(job.id, (current) => {
|
|
@@ -342,6 +415,25 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
|
342
415
|
};
|
|
343
416
|
});
|
|
344
417
|
if (!claim.applied) return { status: "unclaimed" };
|
|
418
|
+
const launchClaim = createBackgroundAgentKeeperLaunchClaim({
|
|
419
|
+
id: job.id,
|
|
420
|
+
workerGeneration: job.workerGeneration,
|
|
421
|
+
keeperGeneration: job.keeperGeneration,
|
|
422
|
+
keeperPid: process.pid,
|
|
423
|
+
token: job.token,
|
|
424
|
+
});
|
|
425
|
+
const launchClaimCandidate = `${launchClaimPath}.${process.pid}.tmp`;
|
|
426
|
+
try {
|
|
427
|
+
writeFileSync(launchClaimCandidate, JSON.stringify(launchClaim), {
|
|
428
|
+
encoding: "utf8",
|
|
429
|
+
mode: 0o600,
|
|
430
|
+
flag: "wx",
|
|
431
|
+
});
|
|
432
|
+
renameSync(launchClaimCandidate, launchClaimPath);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
rmSync(launchClaimCandidate, { force: true });
|
|
435
|
+
throw error;
|
|
436
|
+
}
|
|
345
437
|
|
|
346
438
|
if (process.platform !== "win32") {
|
|
347
439
|
try {
|
|
@@ -686,6 +778,7 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
|
|
|
686
778
|
// Another keeper can still own a sibling socket in the shared namespace.
|
|
687
779
|
}
|
|
688
780
|
}
|
|
781
|
+
rmSync(launchClaimPath, { force: true });
|
|
689
782
|
return {
|
|
690
783
|
...result,
|
|
691
784
|
authenticatedWorkerPid: authenticatedHello?.workerPid || null,
|