chainlesschain 0.166.6 → 0.166.9
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 +21 -0
- package/package.json +5 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-CVnFjTfG.js → AIOps-DJvpifGx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BxkwuuQC.js → ActionButton-D2UhuF_A.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C2670q6z.js → Analytics-C9ho9obu.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CU_rylW6.js → AppLayout-BvRCpFK3.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-DGJmYqnj.js → Artifacts-jxrySvpk.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BZOQbINJ.js → Audit-ovEw0ZYl.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DghB4QwF.js → BackgroundAgents-D7H5z6zS.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-l0mwPbta.js → Backup-BuXob8za.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Bv3WSZOh.js → BaseInput-CtnszuQ2.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-s88nBdvt.js → Chat-B_mYE8fJ.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-Z8CBuB1a.js → ChatBubbleRenderer-DaiA6CsX.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-B5icasEs.js → Checkbox-ierdtcJD.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CsUFqNud.js → Codegen-3VKPJD_B.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DlBtClaW.js → Col-1EdMyf5P.js} +1 -1
- package/src/assets/web-panel/assets/{Community-xRn1IdLB.js → Community-D6H7UA8K.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-naIr8a_o.js → Compact-BBAZvYIm.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Dp9esfFE.js → Compliance-CgsTrAoj.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DQlMYJB-.js → Cowork-tnZgLoTE.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-Cd0oNLzJ.js → Cron-CPpZw2Nh.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BtDIx3XX.js → Crosschain-BymBcp5z.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BOlpzEwg.js → DID-DKPxS_K1.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DV15Axk1.js → Dashboard-DvJhEmH9.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-COXrewN1.js → Dropdown-b8iQyQIZ.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-EjIt0Cm7.js → EmailListRenderer--0-A-zKb.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DJlhwb-R.js → FamilyGuardDashboard-C12iDN06.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BMiFl82k.js → Federation-BRhaE2Z8.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BUcDQyxn.js → FormItemContext-C4dnq7yf.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-C25uFxvK.js → GenericCardRenderer-CGAdbR5n.js} +1 -1
- package/src/assets/web-panel/assets/{Git-D20uaEUU.js → Git-4eA2fYzL.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CQxYJjCX.js → Governance-BJuY9Y-9.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-m-sczpS5.js → Inference-DZ5Dy4up.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CBsIlU0S.js → KnowledgeGraph-BMTjRs4A.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-D0-5bqeu.js → Logs-BKEeQEfJ.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-B05DgASC.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CwDGFu2Q.js → Marketplace-Dhho4fEg.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-7FxJ3omT.js → McpTools-BW6xxSlU.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-DOVyb59U.js → Memory-mPqlpSkq.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DZXl-Hje.js → MobileBridge-Cb9N_DQI.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DlZ8tRXt.js → MobileProjects-BNs8tah_.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-D7Pjx1s_.js → Mtc-CaxBt8tn.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BND7lX8p.js → MtcAudit-DRPQbp3t.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BknS7B-p.js → Multisig-CRm-d8bK.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-7C2-KPHt.js → NLProgramming-BCCk3LPs.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DNWdTndN.js → Notes-Cv8SN8gN.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-DCm4KcJZ.js → NotificationSettings-CggIvTh5.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DyVwwjJx.js → OrderTableRenderer-BW3_bV12.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-Dl2O_qJ9.js → Organization-BuRE-VCO.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BOFCvk9Q.js → Overflow-DBrm1gju.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DgdVowY1.js → P2P-CWVnDYgS.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-8RKUIpte.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-BHNEUUwa.js → Permissions-tDUAxogW.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CaZbrOdz.js → PersonalDataHub-Dv2-CFis.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DJDnoXmE.js → Pipeline-DM6kvFni.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CBQr2eX8.js → Privacy-DzK-yL0d.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Yghqa06z.js → ProjectInit-B5UrNpkj.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-HBAtJi8U.js → ProjectSettings-CxJD43oW.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Z2Orm8Ma.js +1 -0
- package/src/assets/web-panel/assets/{Providers-uJTz77GI.js → Providers-CYl4jRYK.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-k3M5ouTp.js → QrScannerModal-DeH2T0fH.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CMji45PH.js → QuickAsk-Bw9k_kTk.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DyoZYPls.js → Recommend-Bl_jGwoi.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-d6u01Kq0.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-D4hIs4vV.js → Reputation-Ds_Dfr-k.js} +1 -1
- package/src/assets/web-panel/assets/{Row-mzE-ElTg.js → Row-BpQUQTNj.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-cg7J3hI6.js → RssFeed-BatpZBGt.js} +3 -3
- package/src/assets/web-panel/assets/{Search-6M0TkGSZ.js → Search-D7hOObzi.js} +1 -1
- package/src/assets/web-panel/assets/{Security-B1GQ67qh.js → Security-dXQ4FKNt.js} +4 -4
- package/src/assets/web-panel/assets/{Services-D_RwU_5S.js → Services-D8wInGPa.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-TZjk-dsW.js → Skeleton-DXQCoYpM.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CG6FcaCV.js → Skills-D3oLRIVn.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BI190QDv.js → Sla-BmP4-LnJ.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BWmxF2Wu.js → SpeechSettings-DLj-xzro.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CwPiGw6L.js → SyncSettings-8KKaaRMr.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BIXgCeSk.js → Tasks-GyK42I7C.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CQFgGCoK.js → Templates-n1y_8UBG.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DAelEdkL.js → Tenant-B39nBph8.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BbqWK6qh.js → Terminal-DCQoOKXe.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-cmG3zvY5.js → TimelineRenderer-BkhbDfXk.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-C8444NKU.js → Tokens-BeHIIU8D.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-C1Rpnz0I.js → Trigger-BHvWXVOb.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-u4koPiFD.js → Trust-y2WaOSto.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-oPWKn80Z.js → UkeySign-DBFeJBzt.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D0CUUY_5.js → VideoEditing-BEOWitC-.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-fu9pIAbt.js → Wallet-BITedTe6.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Bc5ciuOx.js → WebAuthn-DMwlJVin.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-BCin4_4N.js → WorkflowEditor-CKfGFEu1.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B2jbM-XN.js → chat-DR6gnBGF.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CUVoUQ_2.js → colors-C_Hw5NCn.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DgOGpkRH.js → compact-item-B8IPwsEu.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DIxU6eKI.js → createContext-D-u4JVnJ.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Cq6lSd_q.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DoKRKMav.js → hasIn-CZubdS4D.js} +1 -1
- package/src/assets/web-panel/assets/{index-ByjrulIZ.js → index-11ASqx-A.js} +1 -1
- package/src/assets/web-panel/assets/index-BQ3213fz.js +1 -0
- package/src/assets/web-panel/assets/{index-BwYofQJg.js → index-BXS6wYJZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLdpHe6w.js → index-Bi21Etg4.js} +1 -1
- package/src/assets/web-panel/assets/{index-2flsPFIX.js → index-Bizm87cX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXb-Smmm.js → index-Bv2132PO.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5ySiGYy.js → index-BzoFanEQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWbL1-N0.js → index-C25rOz7O.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cj-us-D_.js → index-C2VmtNaQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9TsO9T9.js → index-Cbm8F0M2.js} +1 -1
- package/src/assets/web-panel/assets/{index-eTWPaeBI.js → index-CfniPP5p.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2yo6ast.js → index-Cg2bCeol.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPXdBnGi.js → index-CiXkhOkY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCLeNqIL.js → index-Cl5RpreD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtSPhEyC.js → index-CqmLw8cs.js} +1 -1
- package/src/assets/web-panel/assets/{index-eYxUASSG.js → index-Ctje8Vl3.js} +1 -1
- package/src/assets/web-panel/assets/{index-i5MB455u.js → index-D1w96gg9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpvyOt5Z.js → index-D47jt8ET.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeWSw-z5.js → index-DJ6pG79m.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3F_p8NA.js → index-DTosx2Wu.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9mSYovv.js → index-DY6MHo9D.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJMHnbmu.js → index-DcFjRT-K.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-ufbAJB.js → index-DfnzrUxU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFUprhNd.js → index-DjRrqooR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Co4lDEk-.js → index-DmRcgiYT.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGTZdZOE.js → index-DpjHpRoq.js} +3 -3
- package/src/assets/web-panel/assets/{index-DHugFl3v.js → index-Dt-w3xQ7.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-f6l4Vm.js → index-DuOU5apg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtyAuGOm.js → index-LvjAKDSl.js} +1 -1
- package/src/assets/web-panel/assets/{index-be8SonCh.js → index-_nL79NT8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRMEO2Tl.js → index-auyNYjLo.js} +1 -1
- package/src/assets/web-panel/assets/{index-C88m9lsi.js → index-cVqbas5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqlwIjJx.js → index-ffe7bvqP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpTFx8Xx.js → index-idZKyADQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cskz--fd.js → index-k1Xa5iw1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CF80xxPG.js → index-mYZJxGH6.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ds8r3Fzb.js → index-rYBHSDcJ.js} +1 -1
- package/src/assets/web-panel/assets/index-vJagfET2.js +1 -0
- package/src/assets/web-panel/assets/{index-C846lPGD.js → index-xoNssgz_.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-B4chl7iR.js → initDefaultProps-lcU_dT2r.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DruN9h96.js → motion-Dc7UNXZ1.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cl0UtCgU.js → move-Cll8ZXaL.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-FX2ttw70.js → mtc-parser-CAb_yLIM.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BDCneY46.js → omit-DurYMyTe.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DycsEg8b.js → pickAttrs-BITQyOlw.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bc-dBuMv.js → placementArrow-Dg1rH91i.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve--Wwaf8lu.js → responsiveObserve-D_EWIeac.js} +1 -1
- package/src/assets/web-panel/assets/{slide-vDmHQqHH.js → slide-lK4d3zsh.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-9c52yv3G.js → statusUtils-Bflq-Git.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BT4BiO6_.js → styleChecker-Ccp0I-wt.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-C7XxWPcP.js → useFlexGapSupport-CYZbByYA.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BHoMqh4i.js → useFs-B_x0nxFD.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BOLxvsAG.js → usePersonalDataHub-DW55rL85.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CA7poeeP.js → vnode-Ca6MH3W8.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B_sAn-co.js → zoom-NdHelOog.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/graph.js +215 -5
- package/src/commands/orchestrate.js +61 -9
- package/src/commands/team-distributed.js +342 -2
- package/src/commands/team.js +169 -7
- package/src/data/changelog.json +46 -0
- package/src/gateways/ws/message-dispatcher.js +4 -0
- package/src/gateways/ws/session-protocol.js +89 -11
- package/src/gateways/ws/ws-agent-handler.js +65 -9
- package/src/gateways/ws/ws-approval-gate.js +181 -16
- package/src/gateways/ws/ws-server.js +16 -6
- package/src/generated/cc-agent-protocol.schema.json +154 -3
- package/src/lib/agent-team/task-lease.js +487 -3
- package/src/lib/agent-team/team-agent-stream.js +16 -0
- package/src/lib/agent-team/team-distributed-graph-bridge.js +257 -0
- package/src/lib/agent-team/team-distributed-graph-writer.js +523 -0
- package/src/lib/agent-team/team-distributed-queue.js +251 -0
- package/src/lib/agent-team/team-graph-runtime-adapter.js +1918 -0
- package/src/lib/agent-team/team-runner.js +285 -3
- package/src/lib/app-server/graph-runtime.js +1078 -0
- package/src/lib/app-server/rollout-store.js +32 -0
- package/src/lib/app-server/server.js +200 -0
- package/src/lib/approval-grant-ledger.js +69 -0
- package/src/lib/autonomous-agent.js +11 -0
- package/src/lib/codex-app-server-adapter.js +11 -20
- package/src/lib/cowork-graph-authority-adapter.js +455 -0
- package/src/lib/dynamic-workflow-facade.js +83 -16
- package/src/lib/graph-kernel/adapters.js +93 -20
- package/src/lib/graph-kernel/authority.js +324 -0
- package/src/lib/graph-kernel/compiler.js +67 -21
- package/src/lib/graph-kernel/cutover-authority-resolver.js +245 -0
- package/src/lib/graph-kernel/cutover-ledger.js +692 -0
- package/src/lib/graph-kernel/event-store.js +15 -1
- package/src/lib/graph-kernel/graph-runtime-surfaces.json +1427 -0
- package/src/lib/graph-kernel/index.js +4 -0
- package/src/lib/graph-kernel/migration-saga.js +309 -0
- package/src/lib/graph-kernel/retirement-evidence.js +567 -0
- package/src/lib/graph-kernel/runtime-surface-manifest.js +481 -0
- package/src/lib/graph-kernel/runtime.js +737 -38
- package/src/lib/graph-kernel/store-cutover-evidence.js +354 -0
- package/src/lib/graph-kernel/trace-reducer.js +6 -0
- package/src/lib/graph-kernel/trigger-adapter.js +60 -3
- package/src/lib/interaction-adapter.js +61 -9
- package/src/lib/legacy-runtime-authority.js +160 -0
- package/src/lib/orchestrator.js +28 -0
- package/src/lib/policy-decision-event.js +149 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +15 -1
- package/src/lib/record-replay/index.js +1 -0
- package/src/lib/record-replay/playwright-ui-driver.js +327 -0
- package/src/lib/scheduler-kernel/graph-authority-adapter.js +512 -0
- package/src/lib/scheduler-kernel/runtime.js +75 -2
- package/src/lib/webhook-security.js +484 -36
- package/src/runtime/coding-agent-events.cjs +13 -11
- package/src/runtime/headless-runner.js +17 -1
- package/src/runtime/headless-stream.js +66 -3
- package/src/runtime/session-slash-commands.js +57 -6
- package/src/assets/web-panel/assets/MarkdownRenderer-BwH414Ms.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-BiaZfQIM.js +0 -7
- package/src/assets/web-panel/assets/Projects-DiCGnMjz.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-s1gR38IO.js +0 -5
- package/src/assets/web-panel/assets/devWarning-DiSlgCC4.js +0 -1
- package/src/assets/web-panel/assets/index-9WIQEHvv.js +0 -1
- package/src/assets/web-panel/assets/index-B7mDobAl.js +0 -1
|
@@ -28,6 +28,8 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
28
28
|
|
|
29
29
|
export const DEFAULT_LEASE_TTL_MS = 60000;
|
|
30
30
|
export const DEFAULT_MAX_ATTEMPTS = 3;
|
|
31
|
+
export const DEFAULT_QUEUE_WAIT_SLO_MS = 10_000;
|
|
32
|
+
export const DEFAULT_AGING_WINDOW_MS = 2_500;
|
|
31
33
|
export const ADJUDICATION_DECISIONS = Object.freeze({
|
|
32
34
|
RETRY: "retry",
|
|
33
35
|
ACCEPT: "accept",
|
|
@@ -154,11 +156,21 @@ export class TaskLeaseRegistry {
|
|
|
154
156
|
now = () => Date.now(),
|
|
155
157
|
defaultTtlMs = DEFAULT_LEASE_TTL_MS,
|
|
156
158
|
maxAttempts = DEFAULT_MAX_ATTEMPTS,
|
|
159
|
+
queueWaitSloMs = DEFAULT_QUEUE_WAIT_SLO_MS,
|
|
160
|
+
agingWindowMs = DEFAULT_AGING_WINDOW_MS,
|
|
157
161
|
leaseEpoch = randomUUID(),
|
|
158
162
|
} = {}) {
|
|
159
163
|
this._now = typeof now === "function" ? now : () => now;
|
|
160
164
|
this.defaultTtlMs = defaultTtlMs > 0 ? defaultTtlMs : DEFAULT_LEASE_TTL_MS;
|
|
161
165
|
this.maxAttempts = maxAttempts > 0 ? maxAttempts : DEFAULT_MAX_ATTEMPTS;
|
|
166
|
+
this.queueWaitSloMs =
|
|
167
|
+
Number.isFinite(queueWaitSloMs) && queueWaitSloMs > 0
|
|
168
|
+
? Math.floor(queueWaitSloMs)
|
|
169
|
+
: DEFAULT_QUEUE_WAIT_SLO_MS;
|
|
170
|
+
this.agingWindowMs =
|
|
171
|
+
Number.isFinite(agingWindowMs) && agingWindowMs > 0
|
|
172
|
+
? Math.floor(agingWindowMs)
|
|
173
|
+
: DEFAULT_AGING_WINDOW_MS;
|
|
162
174
|
this._leaseEpoch = String(leaseEpoch || randomUUID());
|
|
163
175
|
this._leaseSequence = 0;
|
|
164
176
|
this._tasks = new SharedTaskList({ groupId, now: this._now });
|
|
@@ -175,6 +187,11 @@ export class TaskLeaseRegistry {
|
|
|
175
187
|
["low", new Set()],
|
|
176
188
|
]);
|
|
177
189
|
this._leasedKeys = new Set();
|
|
190
|
+
this._priorityInheritanceCache = null;
|
|
191
|
+
// The ready timestamp is part of the durable scheduler contract. Restarts
|
|
192
|
+
// must not reset a task's queue-wait clock and thereby reintroduce
|
|
193
|
+
// starvation under a sustained high-priority stream.
|
|
194
|
+
this._readySinceByKey = new Map();
|
|
178
195
|
}
|
|
179
196
|
|
|
180
197
|
/**
|
|
@@ -300,6 +317,7 @@ export class TaskLeaseRegistry {
|
|
|
300
317
|
}
|
|
301
318
|
dependents.add(key);
|
|
302
319
|
}
|
|
320
|
+
this._priorityInheritanceCache = null;
|
|
303
321
|
this._enqueueIfReady(key);
|
|
304
322
|
return { ok: true, key, id: created.id };
|
|
305
323
|
}
|
|
@@ -325,6 +343,9 @@ export class TaskLeaseRegistry {
|
|
|
325
343
|
attempts: t.metadata?.attempts || 0,
|
|
326
344
|
lease: lease ? { ...lease } : null,
|
|
327
345
|
assignee: t.assignee,
|
|
346
|
+
createdBy: t.createdBy ?? null,
|
|
347
|
+
createdAt: t.createdAt,
|
|
348
|
+
updatedAt: t.updatedAt,
|
|
328
349
|
rev: t.rev,
|
|
329
350
|
// Full metadata is exposed so executors can read user payload
|
|
330
351
|
// (e.g. a task's shell `command` or agent `prompt`) — it carries the
|
|
@@ -402,12 +423,75 @@ export class TaskLeaseRegistry {
|
|
|
402
423
|
this._requeueExpiredLeases(now);
|
|
403
424
|
const excluded =
|
|
404
425
|
excludeKeys instanceof Set ? excludeKeys : new Set(excludeKeys || []);
|
|
426
|
+
let selected = null;
|
|
405
427
|
for (const priority of ["high", "normal", "low"]) {
|
|
406
428
|
for (const key of this._readyByPriority.get(priority)) {
|
|
407
|
-
if (
|
|
429
|
+
if (excluded.has(key)) continue;
|
|
430
|
+
const scheduling = this.schedulingPriority(key, { now });
|
|
431
|
+
const bothUrgent =
|
|
432
|
+
scheduling.sloUrgent && selected?.scheduling?.sloUrgent;
|
|
433
|
+
if (
|
|
434
|
+
!selected ||
|
|
435
|
+
(bothUrgent &&
|
|
436
|
+
scheduling.queueWaitMs > selected.scheduling.queueWaitMs) ||
|
|
437
|
+
(!bothUrgent &&
|
|
438
|
+
(scheduling.total > selected.scheduling.total ||
|
|
439
|
+
(scheduling.total === selected.scheduling.total &&
|
|
440
|
+
scheduling.queueWaitMs > selected.scheduling.queueWaitMs)))
|
|
441
|
+
) {
|
|
442
|
+
selected = { key, scheduling };
|
|
443
|
+
}
|
|
444
|
+
// FIFO inside one priority class is sufficient: later members cannot
|
|
445
|
+
// have waited longer than the first non-excluded member.
|
|
446
|
+
break;
|
|
408
447
|
}
|
|
409
448
|
}
|
|
410
|
-
return null;
|
|
449
|
+
return selected?.key || null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Explain the effective scheduling priority for one task.
|
|
454
|
+
*
|
|
455
|
+
* A blocked high-priority descendant donates its priority transitively to
|
|
456
|
+
* the dependency that can unblock it. Ready tasks also age, and once they
|
|
457
|
+
* consume 75% of the declared queue-wait SLO they enter an urgent band that
|
|
458
|
+
* outranks newly arriving work. This makes the SLO enforceable rather than
|
|
459
|
+
* leaving aging as telemetry-only decoration.
|
|
460
|
+
*/
|
|
461
|
+
schedulingPriority(key, { now = this._now() } = {}) {
|
|
462
|
+
const task = this.getTask(key);
|
|
463
|
+
if (!task) return null;
|
|
464
|
+
const base = this._priorityScore(task.priority);
|
|
465
|
+
const readySince = this._readySinceByKey.get(key);
|
|
466
|
+
const queueWaitMs = Math.max(
|
|
467
|
+
0,
|
|
468
|
+
Number(now) -
|
|
469
|
+
Number(
|
|
470
|
+
readySince ??
|
|
471
|
+
task.createdAt ??
|
|
472
|
+
(Number.isFinite(now) ? Number(now) : 0),
|
|
473
|
+
),
|
|
474
|
+
);
|
|
475
|
+
const inheritance = this._priorityInheritanceFor(key);
|
|
476
|
+
const donatedBase = Math.max(base, inheritance.donatedBase);
|
|
477
|
+
const criticalPathBoost = inheritance.criticalPathBoost;
|
|
478
|
+
const aging = Math.min(1000, Math.floor(queueWaitMs / this.agingWindowMs));
|
|
479
|
+
const sloUrgent = queueWaitMs >= Math.floor(this.queueWaitSloMs * 0.75);
|
|
480
|
+
const sloBoost = sloUrgent ? 10_000 : 0;
|
|
481
|
+
return Object.freeze({
|
|
482
|
+
base,
|
|
483
|
+
donation: Math.max(0, donatedBase - base),
|
|
484
|
+
aging,
|
|
485
|
+
criticalPathBoost,
|
|
486
|
+
sloBoost,
|
|
487
|
+
sloUrgent,
|
|
488
|
+
total: donatedBase + aging + Math.min(1000, criticalPathBoost) + sloBoost,
|
|
489
|
+
queueWaitMs,
|
|
490
|
+
queueWaitSloMs: this.queueWaitSloMs,
|
|
491
|
+
readySince: Number(readySince ?? task.createdAt ?? now),
|
|
492
|
+
queueWaitDeadlineAt:
|
|
493
|
+
Number(readySince ?? task.createdAt ?? now) + this.queueWaitSloMs,
|
|
494
|
+
});
|
|
411
495
|
}
|
|
412
496
|
|
|
413
497
|
/**
|
|
@@ -1465,6 +1549,320 @@ export class TaskLeaseRegistry {
|
|
|
1465
1549
|
return { ok: true, priorStatus };
|
|
1466
1550
|
}
|
|
1467
1551
|
|
|
1552
|
+
/**
|
|
1553
|
+
* Repair the compatibility task view from a terminal canonical Graph node.
|
|
1554
|
+
* This exceptional path may correct a fail-closed recovery cancellation when
|
|
1555
|
+
* the immutable Graph ledger proves that the effect/attempt already settled.
|
|
1556
|
+
*/
|
|
1557
|
+
applyCanonicalTaskProjection(
|
|
1558
|
+
key,
|
|
1559
|
+
{
|
|
1560
|
+
runId,
|
|
1561
|
+
nodeId,
|
|
1562
|
+
graphStatus,
|
|
1563
|
+
authorityGeneration,
|
|
1564
|
+
eventHead,
|
|
1565
|
+
revisionDigest,
|
|
1566
|
+
evidence = null,
|
|
1567
|
+
now = this._now(),
|
|
1568
|
+
} = {},
|
|
1569
|
+
) {
|
|
1570
|
+
const terminalStatus =
|
|
1571
|
+
graphStatus === "succeeded"
|
|
1572
|
+
? TASK_STATUS.COMPLETED
|
|
1573
|
+
: ["failed", "blocked", "cancelled"].includes(graphStatus)
|
|
1574
|
+
? TASK_STATUS.CANCELLED
|
|
1575
|
+
: null;
|
|
1576
|
+
if (!terminalStatus) {
|
|
1577
|
+
return { ok: false, reason: "graph_not_terminal" };
|
|
1578
|
+
}
|
|
1579
|
+
if (
|
|
1580
|
+
!SAFE_HANDOFF_ID_PATTERN.test(String(runId || "")) ||
|
|
1581
|
+
!SAFE_HANDOFF_ID_PATTERN.test(String(nodeId || "")) ||
|
|
1582
|
+
!Number.isSafeInteger(Number(authorityGeneration)) ||
|
|
1583
|
+
Number(authorityGeneration) < 1 ||
|
|
1584
|
+
!SHA256_PATTERN.test(String(eventHead || "")) ||
|
|
1585
|
+
!SHA256_PATTERN.test(String(revisionDigest || ""))
|
|
1586
|
+
) {
|
|
1587
|
+
return { ok: false, reason: "invalid_graph_binding" };
|
|
1588
|
+
}
|
|
1589
|
+
let cleanEvidence;
|
|
1590
|
+
try {
|
|
1591
|
+
cleanEvidence = cloneValue(evidence);
|
|
1592
|
+
} catch {
|
|
1593
|
+
return { ok: false, reason: "invalid_graph_evidence" };
|
|
1594
|
+
}
|
|
1595
|
+
const binding = {
|
|
1596
|
+
schema: "chainlesschain.team-graph-task-projection/v1",
|
|
1597
|
+
runId: String(runId),
|
|
1598
|
+
nodeId: String(nodeId),
|
|
1599
|
+
graphStatus,
|
|
1600
|
+
authorityGeneration: Number(authorityGeneration),
|
|
1601
|
+
eventHead: String(eventHead),
|
|
1602
|
+
revisionDigest: String(revisionDigest),
|
|
1603
|
+
evidence: cleanEvidence,
|
|
1604
|
+
};
|
|
1605
|
+
const projectionDigest = digestValue(binding);
|
|
1606
|
+
const id = this._byKey.get(key);
|
|
1607
|
+
const task = id ? this._tasks.get(id) : null;
|
|
1608
|
+
if (!task) return { ok: false, reason: "not_found" };
|
|
1609
|
+
const prior = task.metadata?.canonicalGraphProjection || null;
|
|
1610
|
+
if (prior?.projectionDigest === projectionDigest) {
|
|
1611
|
+
return {
|
|
1612
|
+
ok: true,
|
|
1613
|
+
idempotent: true,
|
|
1614
|
+
status: task.status,
|
|
1615
|
+
projectionDigest,
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
if (
|
|
1619
|
+
prior &&
|
|
1620
|
+
(prior.runId !== binding.runId ||
|
|
1621
|
+
prior.nodeId !== binding.nodeId ||
|
|
1622
|
+
prior.revisionDigest !== binding.revisionDigest ||
|
|
1623
|
+
prior.graphStatus !== binding.graphStatus)
|
|
1624
|
+
) {
|
|
1625
|
+
return { ok: false, reason: "graph_projection_conflict" };
|
|
1626
|
+
}
|
|
1627
|
+
const snapshot = this._tasks.snapshot();
|
|
1628
|
+
const stored = snapshot.tasks.find((candidate) => candidate.id === task.id);
|
|
1629
|
+
if (!stored || stored.rev !== task.rev) {
|
|
1630
|
+
return { ok: false, reason: "concurrent" };
|
|
1631
|
+
}
|
|
1632
|
+
const projectedAt = Number.isFinite(Number(now))
|
|
1633
|
+
? Number(now)
|
|
1634
|
+
: this._now();
|
|
1635
|
+
const metadata = {
|
|
1636
|
+
...stored.metadata,
|
|
1637
|
+
lease: null,
|
|
1638
|
+
canonicalGraphProjection: {
|
|
1639
|
+
...binding,
|
|
1640
|
+
projectionDigest,
|
|
1641
|
+
projectedAt,
|
|
1642
|
+
},
|
|
1643
|
+
};
|
|
1644
|
+
delete metadata.adjudication;
|
|
1645
|
+
if (terminalStatus === TASK_STATUS.COMPLETED) {
|
|
1646
|
+
metadata.result = {
|
|
1647
|
+
canonicalGraph: true,
|
|
1648
|
+
terminalEvidence: cleanEvidence,
|
|
1649
|
+
};
|
|
1650
|
+
delete metadata.lastError;
|
|
1651
|
+
} else {
|
|
1652
|
+
metadata.lastError = `canonical Graph node settled ${graphStatus}`;
|
|
1653
|
+
}
|
|
1654
|
+
stored.status = terminalStatus;
|
|
1655
|
+
stored.assignee = null;
|
|
1656
|
+
stored.updatedAt = projectedAt;
|
|
1657
|
+
stored.rev += 1;
|
|
1658
|
+
stored.metadata = metadata;
|
|
1659
|
+
stored.history = [
|
|
1660
|
+
...(stored.history || []),
|
|
1661
|
+
{
|
|
1662
|
+
ts: projectedAt,
|
|
1663
|
+
actor: "graph-kernel",
|
|
1664
|
+
action: "canonical-projection-repaired",
|
|
1665
|
+
changes: ["status", "assignee", "metadata"],
|
|
1666
|
+
graphStatus,
|
|
1667
|
+
projectionDigest,
|
|
1668
|
+
},
|
|
1669
|
+
];
|
|
1670
|
+
this._tasks = SharedTaskList.restore(snapshot, { now: this._now });
|
|
1671
|
+
this._rebuildIndexes();
|
|
1672
|
+
if (terminalStatus === TASK_STATUS.COMPLETED) {
|
|
1673
|
+
for (const dependent of this._dependentsByKey.get(key) || []) {
|
|
1674
|
+
this._enqueueIfReady(dependent, projectedAt);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
return { ok: true, status: terminalStatus, projectionDigest };
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
applyCanonicalHandoffProjection(
|
|
1681
|
+
key,
|
|
1682
|
+
{ handoff: source, targetLease = null, now = this._now() } = {},
|
|
1683
|
+
) {
|
|
1684
|
+
if (!source || typeof source !== "object") {
|
|
1685
|
+
return { ok: false, reason: "invalid_handoff_projection" };
|
|
1686
|
+
}
|
|
1687
|
+
const id = normalizedHandoffId(source.id, () => null);
|
|
1688
|
+
const fromHolder = normalizedHolder(source.fromHolder);
|
|
1689
|
+
const toHolder = normalizedHolder(source.toHolder);
|
|
1690
|
+
const revisionDigest = normalizedDigest(source.revisionDigest);
|
|
1691
|
+
const authorityDigest = normalizedDigest(source.authorityDigest);
|
|
1692
|
+
const artifactIds = normalizedArtifactIds(source.artifactIds);
|
|
1693
|
+
const status = String(source.status || "");
|
|
1694
|
+
const ttlMs = Number(source.ttlMs);
|
|
1695
|
+
if (
|
|
1696
|
+
!id ||
|
|
1697
|
+
!fromHolder ||
|
|
1698
|
+
!toHolder ||
|
|
1699
|
+
fromHolder === toHolder ||
|
|
1700
|
+
!revisionDigest ||
|
|
1701
|
+
!authorityDigest ||
|
|
1702
|
+
!artifactIds ||
|
|
1703
|
+
!TEAM_CUSTODY_HANDOFF_STATUSES.includes(status) ||
|
|
1704
|
+
!Number.isSafeInteger(ttlMs) ||
|
|
1705
|
+
ttlMs <= 0 ||
|
|
1706
|
+
ttlMs > MAX_HANDOFF_TTL_MS ||
|
|
1707
|
+
source.taskKey !== key ||
|
|
1708
|
+
typeof source.fromLeaseId !== "string" ||
|
|
1709
|
+
source.fromLeaseId.length === 0 ||
|
|
1710
|
+
!["string", "number"].includes(typeof source.fromFence)
|
|
1711
|
+
) {
|
|
1712
|
+
return { ok: false, reason: "invalid_handoff_projection" };
|
|
1713
|
+
}
|
|
1714
|
+
let preconditions;
|
|
1715
|
+
let summary;
|
|
1716
|
+
try {
|
|
1717
|
+
preconditions = cloneValue(source.preconditions);
|
|
1718
|
+
summary = cloneValue(source.summary);
|
|
1719
|
+
} catch {
|
|
1720
|
+
return { ok: false, reason: "invalid_handoff_projection" };
|
|
1721
|
+
}
|
|
1722
|
+
const idempotencyKey = normalizedHandoffId(
|
|
1723
|
+
source.idempotencyKey,
|
|
1724
|
+
() => null,
|
|
1725
|
+
);
|
|
1726
|
+
if (!idempotencyKey) {
|
|
1727
|
+
return { ok: false, reason: "invalid_handoff_projection" };
|
|
1728
|
+
}
|
|
1729
|
+
const binding = {
|
|
1730
|
+
schema: TEAM_CUSTODY_HANDOFF_SCHEMA,
|
|
1731
|
+
id,
|
|
1732
|
+
taskKey: key,
|
|
1733
|
+
fromHolder,
|
|
1734
|
+
fromLeaseId: source.fromLeaseId,
|
|
1735
|
+
fromFence: source.fromFence,
|
|
1736
|
+
toHolder,
|
|
1737
|
+
revisionDigest,
|
|
1738
|
+
authorityDigest,
|
|
1739
|
+
artifactIds,
|
|
1740
|
+
preconditions,
|
|
1741
|
+
summary,
|
|
1742
|
+
idempotencyKey,
|
|
1743
|
+
ttlMs,
|
|
1744
|
+
};
|
|
1745
|
+
const bindingDigest = digestValue(binding);
|
|
1746
|
+
if (source.bindingDigest && source.bindingDigest !== bindingDigest) {
|
|
1747
|
+
return { ok: false, reason: "handoff_binding_conflict" };
|
|
1748
|
+
}
|
|
1749
|
+
let cleanTargetLease = null;
|
|
1750
|
+
if (status === "committed") {
|
|
1751
|
+
try {
|
|
1752
|
+
cleanTargetLease = cloneValue(targetLease || source.targetLease);
|
|
1753
|
+
} catch {
|
|
1754
|
+
return { ok: false, reason: "invalid_target_lease" };
|
|
1755
|
+
}
|
|
1756
|
+
if (
|
|
1757
|
+
!cleanTargetLease ||
|
|
1758
|
+
cleanTargetLease.holder !== toHolder ||
|
|
1759
|
+
typeof cleanTargetLease.leaseId !== "string" ||
|
|
1760
|
+
cleanTargetLease.leaseId.length === 0 ||
|
|
1761
|
+
!Number.isFinite(Number(cleanTargetLease.expiresAt))
|
|
1762
|
+
) {
|
|
1763
|
+
return { ok: false, reason: "invalid_target_lease" };
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
const taskId = this._byKey.get(key);
|
|
1767
|
+
const task = taskId ? this._tasks.get(taskId) : null;
|
|
1768
|
+
if (!task) return { ok: false, reason: "not_found" };
|
|
1769
|
+
const history = handoffHistory(task);
|
|
1770
|
+
const existing = history.find((entry) => entry.id === id);
|
|
1771
|
+
if (existing && existing.bindingDigest !== bindingDigest) {
|
|
1772
|
+
return { ok: false, reason: "handoff_binding_conflict" };
|
|
1773
|
+
}
|
|
1774
|
+
if (existing && existing.status !== status) {
|
|
1775
|
+
const transitions = {
|
|
1776
|
+
offered: new Set([
|
|
1777
|
+
"accepted",
|
|
1778
|
+
"rejected",
|
|
1779
|
+
"committed",
|
|
1780
|
+
"revoked",
|
|
1781
|
+
"expired",
|
|
1782
|
+
]),
|
|
1783
|
+
accepted: new Set(["committed", "revoked", "expired"]),
|
|
1784
|
+
};
|
|
1785
|
+
if (!transitions[existing.status]?.has(status)) {
|
|
1786
|
+
return { ok: false, reason: "handoff_projection_regressed" };
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
if (existing?.status === status) {
|
|
1790
|
+
return { ok: true, idempotent: true, handoff: cloneValue(existing) };
|
|
1791
|
+
}
|
|
1792
|
+
const projectedAt = Number.isFinite(Number(now))
|
|
1793
|
+
? Number(now)
|
|
1794
|
+
: this._now();
|
|
1795
|
+
const projected = {
|
|
1796
|
+
...(existing || binding),
|
|
1797
|
+
...binding,
|
|
1798
|
+
bindingDigest,
|
|
1799
|
+
expiresAtMs: Number(source.expiresAtMs),
|
|
1800
|
+
status,
|
|
1801
|
+
offeredAt: Number(source.offeredAt),
|
|
1802
|
+
acceptedAt: source.acceptedAt == null ? null : Number(source.acceptedAt),
|
|
1803
|
+
rejectedAt: source.rejectedAt == null ? null : Number(source.rejectedAt),
|
|
1804
|
+
committedAt:
|
|
1805
|
+
source.committedAt == null ? null : Number(source.committedAt),
|
|
1806
|
+
revokedAt: source.revokedAt == null ? null : Number(source.revokedAt),
|
|
1807
|
+
expiredAt: source.expiredAt == null ? null : Number(source.expiredAt),
|
|
1808
|
+
targetStartedAt: existing?.targetStartedAt ?? null,
|
|
1809
|
+
targetSettledAt: existing?.targetSettledAt ?? null,
|
|
1810
|
+
targetSettlement: existing?.targetSettlement ?? null,
|
|
1811
|
+
reason:
|
|
1812
|
+
source.reason == null ? null : String(source.reason).slice(0, 1024),
|
|
1813
|
+
acceptedByAttempt: existing?.acceptedByAttempt ?? null,
|
|
1814
|
+
targetLease:
|
|
1815
|
+
status === "committed"
|
|
1816
|
+
? cloneValue(existing?.targetLease || cleanTargetLease)
|
|
1817
|
+
: null,
|
|
1818
|
+
canonicalProjectedAt: projectedAt,
|
|
1819
|
+
updatedAt: Number(source.updatedAt) || projectedAt,
|
|
1820
|
+
};
|
|
1821
|
+
if (existing) {
|
|
1822
|
+
history[history.indexOf(existing)] = projected;
|
|
1823
|
+
} else {
|
|
1824
|
+
history.push(projected);
|
|
1825
|
+
while (history.length > MAX_HANDOFF_HISTORY) history.shift();
|
|
1826
|
+
}
|
|
1827
|
+
const snapshot = this._tasks.snapshot();
|
|
1828
|
+
const stored = snapshot.tasks.find((candidate) => candidate.id === task.id);
|
|
1829
|
+
if (!stored || stored.rev !== task.rev) {
|
|
1830
|
+
return { ok: false, reason: "concurrent" };
|
|
1831
|
+
}
|
|
1832
|
+
stored.rev += 1;
|
|
1833
|
+
stored.updatedAt = projectedAt;
|
|
1834
|
+
stored.metadata = { ...stored.metadata, custodyHandoffs: history };
|
|
1835
|
+
if (
|
|
1836
|
+
status === "committed" &&
|
|
1837
|
+
![TASK_STATUS.COMPLETED, TASK_STATUS.CANCELLED].includes(stored.status)
|
|
1838
|
+
) {
|
|
1839
|
+
stored.status = TASK_STATUS.IN_PROGRESS;
|
|
1840
|
+
stored.assignee = toHolder;
|
|
1841
|
+
stored.metadata.lease = cloneValue(projected.targetLease);
|
|
1842
|
+
} else if (
|
|
1843
|
+
status === "expired" &&
|
|
1844
|
+
stored.metadata?.lease?.leaseId === binding.fromLeaseId
|
|
1845
|
+
) {
|
|
1846
|
+
stored.status = TASK_STATUS.PENDING;
|
|
1847
|
+
stored.assignee = null;
|
|
1848
|
+
stored.metadata.lease = null;
|
|
1849
|
+
}
|
|
1850
|
+
stored.history = [
|
|
1851
|
+
...(stored.history || []),
|
|
1852
|
+
{
|
|
1853
|
+
ts: projectedAt,
|
|
1854
|
+
actor: "graph-kernel",
|
|
1855
|
+
action: "canonical-handoff-projected",
|
|
1856
|
+
changes: ["status", "assignee", "metadata"],
|
|
1857
|
+
handoffId: id,
|
|
1858
|
+
handoffStatus: status,
|
|
1859
|
+
},
|
|
1860
|
+
];
|
|
1861
|
+
this._tasks = SharedTaskList.restore(snapshot, { now: this._now });
|
|
1862
|
+
this._rebuildIndexes();
|
|
1863
|
+
return { ok: true, handoff: cloneValue(projected) };
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1468
1866
|
/** Bind a pending adjudication to its immutable durable case evidence. */
|
|
1469
1867
|
bindAdjudicationCase(key, { caseId, registryDigest, sideEffectDigest } = {}) {
|
|
1470
1868
|
const id = this._byKey.get(key);
|
|
@@ -1556,6 +1954,9 @@ export class TaskLeaseRegistry {
|
|
|
1556
1954
|
registry: {
|
|
1557
1955
|
defaultTtlMs: this.defaultTtlMs,
|
|
1558
1956
|
maxAttempts: this.maxAttempts,
|
|
1957
|
+
queueWaitSloMs: this.queueWaitSloMs,
|
|
1958
|
+
agingWindowMs: this.agingWindowMs,
|
|
1959
|
+
readySinceByKey: Array.from(this._readySinceByKey.entries()),
|
|
1559
1960
|
byKey: Array.from(this._byKey.entries()),
|
|
1560
1961
|
},
|
|
1561
1962
|
tasks: this._tasks.snapshot(),
|
|
@@ -1567,9 +1968,12 @@ export class TaskLeaseRegistry {
|
|
|
1567
1968
|
now,
|
|
1568
1969
|
defaultTtlMs: snapshot?.registry?.defaultTtlMs,
|
|
1569
1970
|
maxAttempts: snapshot?.registry?.maxAttempts,
|
|
1971
|
+
queueWaitSloMs: snapshot?.registry?.queueWaitSloMs,
|
|
1972
|
+
agingWindowMs: snapshot?.registry?.agingWindowMs,
|
|
1570
1973
|
});
|
|
1571
1974
|
reg._tasks = SharedTaskList.restore(snapshot.tasks, { now: reg._now });
|
|
1572
1975
|
reg._byKey = new Map(snapshot?.registry?.byKey || []);
|
|
1976
|
+
reg._readySinceByKey = new Map(snapshot?.registry?.readySinceByKey || []);
|
|
1573
1977
|
reg._rebuildIndexes();
|
|
1574
1978
|
return reg;
|
|
1575
1979
|
}
|
|
@@ -1584,6 +1988,7 @@ export class TaskLeaseRegistry {
|
|
|
1584
1988
|
patch,
|
|
1585
1989
|
actor: patch.assignee,
|
|
1586
1990
|
});
|
|
1991
|
+
if (patch.status !== undefined) this._priorityInheritanceCache = null;
|
|
1587
1992
|
return true;
|
|
1588
1993
|
} catch {
|
|
1589
1994
|
// ConcurrencyError (rev changed under us) — caller re-reads and retries.
|
|
@@ -1655,8 +2060,72 @@ export class TaskLeaseRegistry {
|
|
|
1655
2060
|
: "normal";
|
|
1656
2061
|
}
|
|
1657
2062
|
|
|
1658
|
-
|
|
2063
|
+
_priorityScore(priority) {
|
|
2064
|
+
return priority === "high" ? 2 : priority === "low" ? 0 : 1;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
_priorityInheritanceFor(key) {
|
|
2068
|
+
if (!this._priorityInheritanceCache) {
|
|
2069
|
+
const keys = [...this._byKey.keys()];
|
|
2070
|
+
const indegree = new Map(
|
|
2071
|
+
keys.map((candidate) => [
|
|
2072
|
+
candidate,
|
|
2073
|
+
(this._depsByKey.get(candidate) || []).filter((dependency) =>
|
|
2074
|
+
this._byKey.has(dependency),
|
|
2075
|
+
).length,
|
|
2076
|
+
]),
|
|
2077
|
+
);
|
|
2078
|
+
const ready = keys.filter((candidate) => indegree.get(candidate) === 0);
|
|
2079
|
+
const order = [];
|
|
2080
|
+
for (let index = 0; index < ready.length; index += 1) {
|
|
2081
|
+
const candidate = ready[index];
|
|
2082
|
+
order.push(candidate);
|
|
2083
|
+
for (const dependent of this._dependentsByKey.get(candidate) || []) {
|
|
2084
|
+
if (!indegree.has(dependent)) continue;
|
|
2085
|
+
indegree.set(dependent, indegree.get(dependent) - 1);
|
|
2086
|
+
if (indegree.get(dependent) === 0) ready.push(dependent);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
const inherited = new Map();
|
|
2090
|
+
for (const candidate of order.reverse()) {
|
|
2091
|
+
const task = this.getTask(candidate);
|
|
2092
|
+
let donatedBase = this._priorityScore(task?.priority);
|
|
2093
|
+
let criticalPathBoost = 0;
|
|
2094
|
+
for (const dependent of this._dependentsByKey.get(candidate) || []) {
|
|
2095
|
+
const dependentTask = this.getTask(dependent);
|
|
2096
|
+
if (
|
|
2097
|
+
!dependentTask ||
|
|
2098
|
+
[TASK_STATUS.COMPLETED, TASK_STATUS.CANCELLED].includes(
|
|
2099
|
+
dependentTask.status,
|
|
2100
|
+
)
|
|
2101
|
+
) {
|
|
2102
|
+
continue;
|
|
2103
|
+
}
|
|
2104
|
+
const child = inherited.get(dependent) || {
|
|
2105
|
+
donatedBase: this._priorityScore(dependentTask.priority),
|
|
2106
|
+
criticalPathBoost: 0,
|
|
2107
|
+
};
|
|
2108
|
+
donatedBase = Math.max(donatedBase, child.donatedBase);
|
|
2109
|
+
criticalPathBoost = Math.max(
|
|
2110
|
+
criticalPathBoost,
|
|
2111
|
+
child.criticalPathBoost + 1,
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
inherited.set(candidate, { donatedBase, criticalPathBoost });
|
|
2115
|
+
}
|
|
2116
|
+
this._priorityInheritanceCache = inherited;
|
|
2117
|
+
}
|
|
2118
|
+
return (
|
|
2119
|
+
this._priorityInheritanceCache.get(key) || {
|
|
2120
|
+
donatedBase: this._priorityScore(this.getTask(key)?.priority),
|
|
2121
|
+
criticalPathBoost: 0,
|
|
2122
|
+
}
|
|
2123
|
+
);
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
_removeReady(key, { clearWait = true } = {}) {
|
|
1659
2127
|
for (const queue of this._readyByPriority.values()) queue.delete(key);
|
|
2128
|
+
if (clearWait) this._readySinceByKey.delete(key);
|
|
1660
2129
|
}
|
|
1661
2130
|
|
|
1662
2131
|
_dependenciesCompleted(key) {
|
|
@@ -1697,6 +2166,9 @@ export class TaskLeaseRegistry {
|
|
|
1697
2166
|
return false;
|
|
1698
2167
|
}
|
|
1699
2168
|
this._leasedKeys.delete(key);
|
|
2169
|
+
if (!this._readySinceByKey.has(key)) {
|
|
2170
|
+
this._readySinceByKey.set(key, Number(now));
|
|
2171
|
+
}
|
|
1700
2172
|
this._readyByPriority.get(this._priorityFor(task)).add(key);
|
|
1701
2173
|
return true;
|
|
1702
2174
|
}
|
|
@@ -1712,6 +2184,7 @@ export class TaskLeaseRegistry {
|
|
|
1712
2184
|
}
|
|
1713
2185
|
|
|
1714
2186
|
_rebuildIndexes() {
|
|
2187
|
+
const restoredReadySince = new Map(this._readySinceByKey || []);
|
|
1715
2188
|
this._depsByKey = new Map();
|
|
1716
2189
|
this._dependentsByKey = new Map();
|
|
1717
2190
|
this._readyByPriority = new Map([
|
|
@@ -1720,6 +2193,8 @@ export class TaskLeaseRegistry {
|
|
|
1720
2193
|
["low", new Set()],
|
|
1721
2194
|
]);
|
|
1722
2195
|
this._leasedKeys = new Set();
|
|
2196
|
+
this._readySinceByKey = restoredReadySince;
|
|
2197
|
+
this._priorityInheritanceCache = null;
|
|
1723
2198
|
for (const task of this._tasks.list()) {
|
|
1724
2199
|
const key = task.metadata?.key;
|
|
1725
2200
|
const dependencies = task.metadata?.dependsOn || [];
|
|
@@ -1738,5 +2213,14 @@ export class TaskLeaseRegistry {
|
|
|
1738
2213
|
}
|
|
1739
2214
|
const now = this._now();
|
|
1740
2215
|
for (const key of this._byKey.keys()) this._enqueueIfReady(key, now);
|
|
2216
|
+
for (const priority of ["high", "normal", "low"]) {
|
|
2217
|
+
const ordered = [...this._readyByPriority.get(priority)].sort(
|
|
2218
|
+
(left, right) =>
|
|
2219
|
+
(this._readySinceByKey.get(left) || 0) -
|
|
2220
|
+
(this._readySinceByKey.get(right) || 0) ||
|
|
2221
|
+
left.localeCompare(right),
|
|
2222
|
+
);
|
|
2223
|
+
this._readyByPriority.set(priority, new Set(ordered));
|
|
2224
|
+
}
|
|
1741
2225
|
}
|
|
1742
2226
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* are never copied into parser state or returned to callers.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
10
11
|
import { TextDecoder } from "node:util";
|
|
11
12
|
|
|
12
13
|
export const DEFAULT_TEAM_AGENT_MAX_LINE_BYTES = 1024 * 1024;
|
|
@@ -96,6 +97,7 @@ export class TeamAgentStreamParser {
|
|
|
96
97
|
#tokenUsageEvents = 0;
|
|
97
98
|
#usageByIdentity = new Map();
|
|
98
99
|
#sawTerminalResult = false;
|
|
100
|
+
#terminalResultDigest = null;
|
|
99
101
|
#finished = false;
|
|
100
102
|
#failed = null;
|
|
101
103
|
#finalSummary = null;
|
|
@@ -204,6 +206,16 @@ export class TeamAgentStreamParser {
|
|
|
204
206
|
return this.#copyLiveSummary();
|
|
205
207
|
}
|
|
206
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Return an immutable digest of the exact terminal result record without
|
|
211
|
+
* retaining or exposing its prompt/result body.
|
|
212
|
+
*/
|
|
213
|
+
terminalEvidence() {
|
|
214
|
+
return this.#terminalResultDigest
|
|
215
|
+
? Object.freeze({ outputDigest: this.#terminalResultDigest })
|
|
216
|
+
: null;
|
|
217
|
+
}
|
|
218
|
+
|
|
207
219
|
#copyLiveSummary() {
|
|
208
220
|
return {
|
|
209
221
|
provider: this.#provider,
|
|
@@ -291,6 +303,9 @@ export class TeamAgentStreamParser {
|
|
|
291
303
|
}
|
|
292
304
|
if (line.trim() === "") return;
|
|
293
305
|
|
|
306
|
+
const lineDigest = `sha256:${createHash("sha256")
|
|
307
|
+
.update(line, "utf8")
|
|
308
|
+
.digest("hex")}`;
|
|
294
309
|
let event;
|
|
295
310
|
try {
|
|
296
311
|
event = JSON.parse(line);
|
|
@@ -345,6 +360,7 @@ export class TeamAgentStreamParser {
|
|
|
345
360
|
|
|
346
361
|
if (event.type === "result") {
|
|
347
362
|
this.#sawTerminalResult = true;
|
|
363
|
+
this.#terminalResultDigest = lineDigest;
|
|
348
364
|
this.#lastResultProvider =
|
|
349
365
|
typeof event.provider === "string" ? event.provider : this.#provider;
|
|
350
366
|
this.#lastResultModel =
|