chainlesschain 0.165.3 → 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-BiHNUlbU.js → AIOps-C__nnraW.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-D-UFjDRQ.js → ActionButton-CsKWHEZ-.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DazVsDgW.js → Analytics-BhIjfjWO.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DXh9UfNJ.js → AppLayout-BjuXFzWZ.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CZ6tsceY.js → Artifacts-k6whDkZJ.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-RyxnBr_h.js → Audit-C4FBMYRh.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BWS51Q6X.js → BackgroundAgents-C_vRrSXk.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BRtPcUul.js → Backup-CngFZeVp.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CGd4vZYY.js → BaseInput-B_H9EBRm.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-h8ijVwCg.js → Chat-DLEC5kt7.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-NyF6dPl8.js → Checkbox-m3ClR4xh.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Ci4OOxr5.js → Codegen-CmLSFrgp.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CwTkc9BM.js → Col-DFGQteHN.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DW_tM82m.js → Community-EUUVORHE.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DEvHRV4S.js → Compact-2PSg0Yr7.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BbjBEEfD.js → Compliance-CcKur7gB.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-10IXBaBa.js → Cowork-DK_Fezfa.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BZkbpd5I.js → Cron-Dbw89xl7.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CFQ8L_6v.js → Crosschain-CH66h_Ec.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BDF6AkXe.js → DID-D1igRb17.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CoOdWMi4.js → Dashboard-DVAsrobk.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DEsGmu8Y.js → Dropdown-Dl7W8AQs.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BS89SWlK.js → EmailListRenderer-D5tpaEQC.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C1FpCHiI.js → FamilyGuardDashboard-O_VQf8gq.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BzvpVmLA.js → Federation-BTVcgS9n.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BeC259PQ.js → FormItemContext-BRnkJAIz.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-azgG3bmV.js → GenericCardRenderer-8OWSCkDj.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BmSSowc5.js → Git-WXLYTQj2.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CTBCSyQZ.js → Governance-BezaUhtc.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DA5xyk4a.js → Inference-CuRay3Gu.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-D1gPHNig.js → KnowledgeGraph-DfWLDW1m.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BPmLf1AR.js → Logs-C55y2pPY.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-DtUFcR0E.js → Marketplace-Dk08Uycg.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DS_kqBv-.js → McpTools-C11fSB5V.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Fvh7XL7V.js → Memory-mr0eZc97.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C7itAP45.js → MobileBridge-C0iCSRbb.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-CY-xMG8Z.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B57MiNLK.js → Mtc-Bplqc_Wc.js} +6 -6
- package/src/assets/web-panel/assets/{MtcAudit-BXfCUcM7.js → MtcAudit-BlzwsoSM.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-yaJ-ZcF6.js → Multisig-BBLmTty6.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CoaUCKrO.js → NLProgramming-DtT1uf1W.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BKLM_65R.js → Notes-DcqQAxTK.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DoDtK9uZ.js → NotificationSettings-cb0kWoZe.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BJ28TGr-.js → OrderTableRenderer-kw9HZQNN.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-MuGsdoIb.js → Organization-DlTlbkUN.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BLUN9J4R.js → Overflow-pp1qs-oY.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DWuoBUPd.js → P2P-BESLnyYz.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CUi37YPj.js → PdhVaultBrowser-B-J1mGuf.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYQg2pcr.js → Permissions-B36PeT1_.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CAxoV2xu.js → PersonalDataHub-CTK2ad-3.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-B33wGvIO.js → Pipeline-zJNGXsac.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-A8yXTORP.js → Privacy-b9fz0Qf0.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-D49I2I1O.js → ProjectInit-DjZbZgiB.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Ci73jJDw.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-Dz-CAYYU.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-Brza0ykJ.js → QrScannerModal-A0yoQ3-3.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DFydhjxL.js → QuickAsk-DrFgFJNC.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DLF8_NhA.js → Recommend-KC8hF1g4.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-dyuStW3x.js → RemoteSession-DGTPIk6u.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-5F3tA6yw.js → Reputation-QmbwM7Yk.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Ca2A57S5.js → Row-BxjJylia.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B0bxBkq_.js → RssFeed-B7Wda1J6.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CFNvHalZ.js → Search-_plQ_YSA.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DSoH3Hav.js → Security-CD2YJZfx.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BHpfEmBQ.js → Services-qQE7NmwE.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Dsh-QNmn.js → Skeleton-DgQqkFf7.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BhT1NkSU.js → Skills-C4C0oFFt.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DY9snPeJ.js → Sla-Px2N2B5c.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CWaX0_Cx.js → SpeechSettings-DN6C3lQd.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B690y_TC.js → SyncSettings-jm5aH_m7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-2N-w3zWd.js → Tasks-DlkJcv67.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BjwQOQYC.js → Templates-DyrTHUBs.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CUqHErxY.js → Tenant-mC434PtL.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-C13rXvNy.js → Terminal-o0_s3NgK.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CpbWcya_.js → TimelineRenderer-DAGlAXOw.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BXHLGXYC.js → Tokens-BnulS_bs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-1e5i65o3.js → Trigger-suQbVXYM.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-FLGqNHs5.js → Trust-DbIJAPo5.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BbaR_LJB.js → UkeySign-CQNpbIHi.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-NOp4RIov.js → VideoEditing-D8aWCERk.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B7DW7bG4.js → Wallet-CuLpYfZc.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-BptSUWix.js → WebAuthn-CsfG8ZMc.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CAJnDOXS.js → WorkflowEditor-BYsEAZbd.js} +1 -1
- package/src/assets/web-panel/assets/{chat-I8LgG1-Z.js → chat-FT4Um3-I.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CWe7SOVD.js → colors-CKbS_Wlt.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cx3u3CA3.js → compact-item-CFJtzuJM.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CgD5ArwY.js → createContext-B9kSRW6K.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BfQWiXN8.js → hasIn-DR-x0Xpi.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAVy98u7.js → index-7jHr_A_k.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlIyt1qF.js → index-B3ZX81mK.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWZ3REg6.js → index-B7osrXqy.js} +3 -3
- package/src/assets/web-panel/assets/{index-DGMYTN5W.js → index-BGbPnC5x.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFb-XIKy.js → index-BI5hP_qy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAUnl4Jv.js → index-BK3TiKVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUAR7RY-.js → index-BVPU43b1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd79NQAx.js → index-BW-t9n-q.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiI-xPRH.js → index-BfFCQHL3.js} +1 -1
- package/src/assets/web-panel/assets/index-Bizb4hkw.js +1 -0
- package/src/assets/web-panel/assets/{index-B6yChzt1.js → index-Bt4fS2eL.js} +1 -1
- package/src/assets/web-panel/assets/{index-e8BM1yY1.js → index-BwR7yc2h.js} +1 -1
- package/src/assets/web-panel/assets/{index-V-kIHzAV.js → index-Bzz0C1BZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bx7BNIRH.js → index-C2JvmYRc.js} +1 -1
- package/src/assets/web-panel/assets/{index-87gMxqhN.js → index-CGaXUsIs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cy_cCJlr.js → index-CJmIQ9XR.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1UjHdgZ.js → index-C_enk9I_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi24S7Dw.js → index-Cr2FLUsD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcX4FSPf.js → index-CvrCCqJt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmrdUwzU.js → index-D4LfPvWA.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dcdm_Z2c.js → index-DMGmg497.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCJEY2T0.js → index-DShSkww-.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0ztiqtB.js → index-DTxNy0ZU.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-rIKccl.js → index-DW4BWjTk.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdRkCwm4.js → index-DWdqx7zI.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0nDKhWv.js → index-Dap9c3eD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQDKYbnv.js → index-DcfxhKXR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuEQSorC.js → index-DdkIhfi-.js} +1 -1
- package/src/assets/web-panel/assets/{index-3iKP9OTJ.js → index-DeSgw-WV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Doc0Ftpd.js → index-DiNUEpSQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BREnPKGo.js → index-DjEP7WkH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNYFQxAW.js → index-DtkFqPVA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DslTuqro.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-CMu16vPC.js → index-OSzjTzGM.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsQcURkA.js → index-QBSeNVO3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeizoQVZ.js → index-WE08gMJP.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1cX4Rxu.js → index-r2VI1Ikk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmgtY58H.js → index-tqSEHPRT.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DiBszVdG.js → initDefaultProps-Dd7n7VgI.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BGIFFozv.js → motion-BwxouRw1.js} +1 -1
- package/src/assets/web-panel/assets/{move-D5W7mp7B.js → move-DObN0PG9.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-JFIlxJ7F.js → mtc-parser-B3nOUQc7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CQ3o6LIU.js → omit-o9WU2Nbl.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-R8MIlXo3.js → pickAttrs-CL_vvfQ_.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-sW7ylSky.js → placementArrow-CYrM2tyK.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Cnl39Vbz.js → responsiveObserve-k_06cQ96.js} +1 -1
- package/src/assets/web-panel/assets/{slide-sn8tFd14.js → slide-CPKG0pxS.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-IKKY_QxZ.js → statusUtils-BDDEeLTw.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BNpV4Nqs.js → styleChecker-Cmb7RTh7.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BWyT_eoU.js → useFlexGapSupport-e2IedC4N.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-D09p6_U9.js → useFs-CE_gxVnm.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CzW3TkJZ.js → usePersonalDataHub-BkMIIKMz.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-3s3WoAKB.js → vnode-DW886XV1.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjy_673s.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 +13 -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/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/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/assets/web-panel/assets/ChatBubbleRenderer-D7tcdDS9.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-SVLOg49H.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DGbP36-3.js +0 -1
- package/src/assets/web-panel/assets/Projects-CF-yD6Cn.js +0 -1
- package/src/assets/web-panel/assets/Providers-D-etEhg5.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DLv_872g.js +0 -1
- package/src/assets/web-panel/assets/index-CdiGCibe.js +0 -1
- package/src/assets/web-panel/assets/index-D_0z-gyz.js +0 -1
|
@@ -62,6 +62,13 @@ import {
|
|
|
62
62
|
readVerifiedMessages,
|
|
63
63
|
settleWsTurnClaim,
|
|
64
64
|
} from "../../harness/jsonl-session-store.js";
|
|
65
|
+
import { sessionBudgetAdmissionError } from "../../lib/session-budget-production-root.js";
|
|
66
|
+
import {
|
|
67
|
+
beginSessionBudgetUsage,
|
|
68
|
+
markSessionBudgetUsageUnknown,
|
|
69
|
+
recordSessionBudgetUsage,
|
|
70
|
+
rejectSessionBudgetUsageUnknown,
|
|
71
|
+
} from "../../lib/session-budget-usage.js";
|
|
65
72
|
|
|
66
73
|
const CANONICAL_WS_TURN_QUEUES = new Map();
|
|
67
74
|
const CANONICAL_WS_CLAIM_CAS_ATTEMPTS = 4;
|
|
@@ -155,6 +162,7 @@ export class WSAgentHandler {
|
|
|
155
162
|
compactionChatFn,
|
|
156
163
|
canonicalSessionStore,
|
|
157
164
|
sessionHostLease = null,
|
|
165
|
+
sessionBudgetRoot = null,
|
|
158
166
|
}) {
|
|
159
167
|
this.session = session;
|
|
160
168
|
this.interaction = interaction;
|
|
@@ -163,6 +171,8 @@ export class WSAgentHandler {
|
|
|
163
171
|
this._abortController = null;
|
|
164
172
|
this._activeRequestId = null;
|
|
165
173
|
this._sessionHostLease = sessionHostLease;
|
|
174
|
+
this._sessionBudgetRoot = sessionBudgetRoot;
|
|
175
|
+
this._sessionBudget = sessionBudgetRoot?.budget || null;
|
|
166
176
|
this._onSessionHostLeaseAbort = null;
|
|
167
177
|
if (sessionHostLease?.signal) {
|
|
168
178
|
this._onSessionHostLeaseAbort = () => {
|
|
@@ -181,6 +191,26 @@ export class WSAgentHandler {
|
|
|
181
191
|
);
|
|
182
192
|
if (sessionHostLease.signal.aborted) this._onSessionHostLeaseAbort();
|
|
183
193
|
}
|
|
194
|
+
this._sessionBudgetSignal = sessionBudgetRoot?.options?.signal || null;
|
|
195
|
+
this._onSessionBudgetAbort = null;
|
|
196
|
+
if (this._sessionBudgetSignal) {
|
|
197
|
+
this._onSessionBudgetAbort = () => {
|
|
198
|
+
const reason = sessionBudgetAdmissionError(
|
|
199
|
+
this._sessionBudget?.reason?.(),
|
|
200
|
+
"WebSocket session",
|
|
201
|
+
);
|
|
202
|
+
if (this._abortController && !this._abortController.signal.aborted) {
|
|
203
|
+
this._abortController.abort(reason);
|
|
204
|
+
}
|
|
205
|
+
this.interaction?.rejectAllPending?.(reason);
|
|
206
|
+
};
|
|
207
|
+
this._sessionBudgetSignal.addEventListener(
|
|
208
|
+
"abort",
|
|
209
|
+
this._onSessionBudgetAbort,
|
|
210
|
+
{ once: true },
|
|
211
|
+
);
|
|
212
|
+
if (this._sessionBudgetSignal.aborted) this._onSessionBudgetAbort();
|
|
213
|
+
}
|
|
184
214
|
// P0-2: monotonic sequence for crash-safe side-effect ledger op ids. The
|
|
185
215
|
// session id + a per-op nonce keep ids unique across turns and processes.
|
|
186
216
|
this._sideEffectSeq = 0;
|
|
@@ -251,6 +281,58 @@ export class WSAgentHandler {
|
|
|
251
281
|
: Object.freeze(hostSystemPrefix);
|
|
252
282
|
}
|
|
253
283
|
|
|
284
|
+
attachInteraction(interaction) {
|
|
285
|
+
if (!interaction || interaction === this.interaction) return false;
|
|
286
|
+
const previous = this.interaction;
|
|
287
|
+
this.interaction = interaction;
|
|
288
|
+
this.session.interaction = interaction;
|
|
289
|
+
previous?.rejectAllPending?.(
|
|
290
|
+
createAbortError("WebSocket session transport was reattached"),
|
|
291
|
+
);
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
assertSessionBudgetAdmission(operation = "WebSocket operation") {
|
|
296
|
+
if (!this._sessionBudget) return true;
|
|
297
|
+
if (
|
|
298
|
+
this._sessionBudget.signal?.aborted === true ||
|
|
299
|
+
this._sessionBudgetSignal?.aborted === true
|
|
300
|
+
) {
|
|
301
|
+
throw sessionBudgetAdmissionError(
|
|
302
|
+
this._sessionBudget.reason?.(),
|
|
303
|
+
operation,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
_turnSignal(turnSignal) {
|
|
310
|
+
const budgetSignal = this._sessionBudgetSignal;
|
|
311
|
+
if (!budgetSignal) return turnSignal;
|
|
312
|
+
return AbortSignal.any([turnSignal, budgetSignal]);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
_recordSessionBudgetUsage(event, operation = "WebSocket usage settlement") {
|
|
316
|
+
return recordSessionBudgetUsage(this._sessionBudget, event, operation);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
_beginSessionBudgetUsage(event, operation = "WebSocket provider call") {
|
|
320
|
+
return beginSessionBudgetUsage(this._sessionBudget, event, operation);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
_markSessionBudgetUsageUnknown(event) {
|
|
324
|
+
return markSessionBudgetUsageUnknown(this._sessionBudget, event);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
_consumeSessionBudgetTurn(id, operation) {
|
|
328
|
+
if (!this._sessionBudget?.consumeTurn) return null;
|
|
329
|
+
const admission = this._sessionBudget.consumeTurn({ id });
|
|
330
|
+
if (!admission?.ok) {
|
|
331
|
+
throw sessionBudgetAdmissionError(admission?.reason, operation);
|
|
332
|
+
}
|
|
333
|
+
return admission;
|
|
334
|
+
}
|
|
335
|
+
|
|
254
336
|
_applyCanonicalMessages(messages) {
|
|
255
337
|
const canonicalMessages = Array.isArray(messages) ? messages : [];
|
|
256
338
|
this.session.messages = [
|
|
@@ -383,7 +465,12 @@ export class WSAgentHandler {
|
|
|
383
465
|
model: this.session.model || null,
|
|
384
466
|
source: "semantic-compaction",
|
|
385
467
|
});
|
|
468
|
+
this._consumeSessionBudgetTurn(
|
|
469
|
+
`turn:${call.callId}`,
|
|
470
|
+
"WebSocket compaction",
|
|
471
|
+
);
|
|
386
472
|
this._persistCanonicalUsageBoundary(call, "started");
|
|
473
|
+
this._beginSessionBudgetUsage(call, "WebSocket compaction provider call");
|
|
387
474
|
return call;
|
|
388
475
|
}
|
|
389
476
|
|
|
@@ -402,19 +489,36 @@ export class WSAgentHandler {
|
|
|
402
489
|
},
|
|
403
490
|
"unknown",
|
|
404
491
|
);
|
|
492
|
+
if (this._markSessionBudgetUsageUnknown(call)) {
|
|
493
|
+
rejectSessionBudgetUsageUnknown(
|
|
494
|
+
call,
|
|
495
|
+
"WebSocket compaction provider call",
|
|
496
|
+
);
|
|
497
|
+
}
|
|
405
498
|
return;
|
|
406
499
|
}
|
|
407
500
|
if (result?.usageEvent?.usage) {
|
|
408
|
-
|
|
501
|
+
const usageEvent = {
|
|
409
502
|
...call,
|
|
410
503
|
usage: result.usageEvent.usage,
|
|
411
|
-
}
|
|
504
|
+
};
|
|
505
|
+
this._persistCanonicalTokenUsage(usageEvent);
|
|
506
|
+
this._recordSessionBudgetUsage(
|
|
507
|
+
usageEvent,
|
|
508
|
+
"WebSocket compaction usage settlement",
|
|
509
|
+
);
|
|
412
510
|
return;
|
|
413
511
|
}
|
|
414
512
|
this._persistCanonicalUsageBoundary(
|
|
415
513
|
{ ...call, code: "provider_usage_missing" },
|
|
416
514
|
"unknown",
|
|
417
515
|
);
|
|
516
|
+
if (this._markSessionBudgetUsageUnknown(call)) {
|
|
517
|
+
rejectSessionBudgetUsageUnknown(
|
|
518
|
+
call,
|
|
519
|
+
"WebSocket compaction provider call",
|
|
520
|
+
);
|
|
521
|
+
}
|
|
418
522
|
}
|
|
419
523
|
|
|
420
524
|
_settleCanonicalCompactionFailure(call) {
|
|
@@ -423,6 +527,7 @@ export class WSAgentHandler {
|
|
|
423
527
|
{ ...call, code: "provider_call_failed" },
|
|
424
528
|
"unknown",
|
|
425
529
|
);
|
|
530
|
+
this._markSessionBudgetUsageUnknown(call);
|
|
426
531
|
}
|
|
427
532
|
|
|
428
533
|
_latchCompactionSettlement({
|
|
@@ -511,7 +616,7 @@ export class WSAgentHandler {
|
|
|
511
616
|
return null;
|
|
512
617
|
}
|
|
513
618
|
|
|
514
|
-
async _compactCanonicalHistoryBeforeClaim(requestId) {
|
|
619
|
+
async _compactCanonicalHistoryBeforeClaim(requestId, signal = null) {
|
|
515
620
|
const { session } = this;
|
|
516
621
|
if (
|
|
517
622
|
session.autoCompact === false ||
|
|
@@ -525,6 +630,8 @@ export class WSAgentHandler {
|
|
|
525
630
|
const expectedMessages = [...session.messages];
|
|
526
631
|
const strictUsageTelemetry =
|
|
527
632
|
this._resolveCanonicalUsageProtocol() === "call-ledger";
|
|
633
|
+
const meterCompaction =
|
|
634
|
+
strictUsageTelemetry || Boolean(this._sessionBudget);
|
|
528
635
|
let compactionUsageCall = null;
|
|
529
636
|
let compactionUsageSettled = false;
|
|
530
637
|
let result;
|
|
@@ -535,7 +642,7 @@ export class WSAgentHandler {
|
|
|
535
642
|
model: session.model,
|
|
536
643
|
baseUrl: session.baseUrl,
|
|
537
644
|
apiKey: session.apiKey,
|
|
538
|
-
signal: this._sessionHostLease?.signal,
|
|
645
|
+
signal: signal || this._sessionBudgetSignal || this._sessionHostLease?.signal,
|
|
539
646
|
onlyIfNeeded: true,
|
|
540
647
|
preserveCompletedExchange: true,
|
|
541
648
|
llmQuery: this._compactionLlmQuery,
|
|
@@ -544,7 +651,7 @@ export class WSAgentHandler {
|
|
|
544
651
|
cwd: session.projectRoot,
|
|
545
652
|
sessionId: session.id,
|
|
546
653
|
},
|
|
547
|
-
...(
|
|
654
|
+
...(meterCompaction
|
|
548
655
|
? {
|
|
549
656
|
onProviderCallStart: () => {
|
|
550
657
|
const call = this._beginCanonicalCompactionUsage();
|
|
@@ -1012,6 +1119,7 @@ export class WSAgentHandler {
|
|
|
1012
1119
|
|
|
1013
1120
|
this._processing = true;
|
|
1014
1121
|
const abortController = new AbortController();
|
|
1122
|
+
const turnSignal = this._turnSignal(abortController.signal);
|
|
1015
1123
|
this._abortController = abortController;
|
|
1016
1124
|
this._activeRequestId = requestId || null;
|
|
1017
1125
|
let sideEffectLedger = null;
|
|
@@ -1022,6 +1130,7 @@ export class WSAgentHandler {
|
|
|
1022
1130
|
|
|
1023
1131
|
try {
|
|
1024
1132
|
const { session } = this;
|
|
1133
|
+
this.assertSessionBudgetAdmission("WebSocket turn");
|
|
1025
1134
|
this._sessionHostLease?.assert?.();
|
|
1026
1135
|
|
|
1027
1136
|
if (session.canonicalJsonlSession === true) {
|
|
@@ -1044,7 +1153,10 @@ export class WSAgentHandler {
|
|
|
1044
1153
|
this._resolveCanonicalUsageProtocol() === "call-ledger";
|
|
1045
1154
|
if (strictUsageTelemetry) this._assertUsageLedgerWritable();
|
|
1046
1155
|
const compacted =
|
|
1047
|
-
await this._compactCanonicalHistoryBeforeClaim(
|
|
1156
|
+
await this._compactCanonicalHistoryBeforeClaim(
|
|
1157
|
+
requestId,
|
|
1158
|
+
turnSignal,
|
|
1159
|
+
);
|
|
1048
1160
|
if (!compacted) return;
|
|
1049
1161
|
}
|
|
1050
1162
|
canonicalTurn ||= this._claimCanonicalTurn(userMessage, requestId);
|
|
@@ -1143,7 +1255,8 @@ export class WSAgentHandler {
|
|
|
1143
1255
|
shellPolicyOverrides: session.shellPolicyOverrides || null,
|
|
1144
1256
|
slotFiller,
|
|
1145
1257
|
interaction: this.interaction,
|
|
1146
|
-
signal:
|
|
1258
|
+
signal: turnSignal,
|
|
1259
|
+
sessionBudget: this._sessionBudget,
|
|
1147
1260
|
// P0 authority: null unless opted in (byte-identical default — agent
|
|
1148
1261
|
// core already defaults `options.approvalGate || null`).
|
|
1149
1262
|
approvalGate: await this._ensureApprovalGate(),
|
|
@@ -1374,18 +1487,28 @@ export class WSAgentHandler {
|
|
|
1374
1487
|
this._persistCanonicalUsageBoundary(event, "unknown");
|
|
1375
1488
|
}
|
|
1376
1489
|
this._emitCompactionUsageUnknown(requestId, event);
|
|
1490
|
+
if (this._markSessionBudgetUsageUnknown(event)) {
|
|
1491
|
+
rejectSessionBudgetUsageUnknown(
|
|
1492
|
+
event,
|
|
1493
|
+
"WebSocket semantic compaction",
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1377
1496
|
break;
|
|
1378
1497
|
|
|
1379
1498
|
case "model-usage-started":
|
|
1380
1499
|
if (strictUsageTelemetry && event.ledgerPersisted !== true) {
|
|
1381
1500
|
this._persistCanonicalUsageBoundary(event, "started");
|
|
1382
1501
|
}
|
|
1502
|
+
this._beginSessionBudgetUsage(event);
|
|
1383
1503
|
break;
|
|
1384
1504
|
|
|
1385
1505
|
case "model-usage-unknown":
|
|
1386
1506
|
if (strictUsageTelemetry && event.ledgerPersisted !== true) {
|
|
1387
1507
|
this._persistCanonicalUsageBoundary(event, "unknown");
|
|
1388
1508
|
}
|
|
1509
|
+
if (this._markSessionBudgetUsageUnknown(event)) {
|
|
1510
|
+
rejectSessionBudgetUsageUnknown(event, "WebSocket provider call");
|
|
1511
|
+
}
|
|
1389
1512
|
break;
|
|
1390
1513
|
|
|
1391
1514
|
case "compaction":
|
|
@@ -1396,9 +1519,14 @@ export class WSAgentHandler {
|
|
|
1396
1519
|
break;
|
|
1397
1520
|
|
|
1398
1521
|
case "token-usage":
|
|
1399
|
-
if (
|
|
1522
|
+
if (
|
|
1523
|
+
session.canonicalJsonlSession === true &&
|
|
1524
|
+
(strictUsageTelemetry || this._sessionBudget) &&
|
|
1525
|
+
event.ledgerPersisted !== true
|
|
1526
|
+
) {
|
|
1400
1527
|
this._persistCanonicalTokenUsage(event);
|
|
1401
1528
|
}
|
|
1529
|
+
this._recordSessionBudgetUsage(event);
|
|
1402
1530
|
this.interaction.emit("token-usage", {
|
|
1403
1531
|
requestId,
|
|
1404
1532
|
provider: event.provider,
|
|
@@ -1473,13 +1601,29 @@ export class WSAgentHandler {
|
|
|
1473
1601
|
session.lastActivity = new Date().toISOString();
|
|
1474
1602
|
} catch (err) {
|
|
1475
1603
|
let surfacedError = err;
|
|
1604
|
+
const sessionBudgetFailed =
|
|
1605
|
+
err?.code === "CC_SESSION_BUDGET_EXHAUSTED" ||
|
|
1606
|
+
err?.code === "CC_SESSION_BUDGET_USAGE_UNKNOWN" ||
|
|
1607
|
+
this._sessionBudget?.signal?.aborted === true;
|
|
1608
|
+
if (
|
|
1609
|
+
sessionBudgetFailed &&
|
|
1610
|
+
err?.code !== "CC_SESSION_BUDGET_EXHAUSTED" &&
|
|
1611
|
+
err?.code !== "CC_SESSION_BUDGET_USAGE_UNKNOWN"
|
|
1612
|
+
) {
|
|
1613
|
+
surfacedError = sessionBudgetAdmissionError(
|
|
1614
|
+
this._sessionBudget?.reason?.(),
|
|
1615
|
+
"WebSocket turn",
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1476
1618
|
if (
|
|
1477
1619
|
canonicalTurn?.claimAcquired &&
|
|
1478
1620
|
!canonicalTurn.settled &&
|
|
1479
1621
|
!canonicalTurn.settlementAttempted
|
|
1480
1622
|
) {
|
|
1481
|
-
const failureCode =
|
|
1482
|
-
? "
|
|
1623
|
+
const failureCode = sessionBudgetFailed
|
|
1624
|
+
? surfacedError?.code || "CC_SESSION_BUDGET_EXHAUSTED"
|
|
1625
|
+
: isAbortError(err)
|
|
1626
|
+
? "CC_WS_TURN_INTERRUPTED"
|
|
1483
1627
|
: err?.code === "CC_WS_EMPTY_ASSISTANT_RESPONSE"
|
|
1484
1628
|
? "CC_WS_EMPTY_ASSISTANT_RESPONSE"
|
|
1485
1629
|
: "CC_WS_TURN_FAILED";
|
|
@@ -1521,7 +1665,10 @@ export class WSAgentHandler {
|
|
|
1521
1665
|
) {
|
|
1522
1666
|
persistSideEffectLedger(this.session.id, sideEffectLedger);
|
|
1523
1667
|
}
|
|
1524
|
-
if (
|
|
1668
|
+
if (
|
|
1669
|
+
!sessionBudgetFailed &&
|
|
1670
|
+
(isAbortError(err) || abortController.signal.aborted)
|
|
1671
|
+
) {
|
|
1525
1672
|
return;
|
|
1526
1673
|
}
|
|
1527
1674
|
|
|
@@ -1609,8 +1756,34 @@ export class WSAgentHandler {
|
|
|
1609
1756
|
);
|
|
1610
1757
|
}
|
|
1611
1758
|
this._onSessionHostLeaseAbort = null;
|
|
1612
|
-
this.
|
|
1759
|
+
if (this._sessionBudgetSignal && this._onSessionBudgetAbort) {
|
|
1760
|
+
this._sessionBudgetSignal.removeEventListener(
|
|
1761
|
+
"abort",
|
|
1762
|
+
this._onSessionBudgetAbort,
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
this._onSessionBudgetAbort = null;
|
|
1766
|
+
this._sessionBudgetSignal = null;
|
|
1767
|
+
const cleanupErrors = [];
|
|
1768
|
+
try {
|
|
1769
|
+
this._sessionBudgetRoot?.close?.();
|
|
1770
|
+
} catch (error) {
|
|
1771
|
+
cleanupErrors.push(error);
|
|
1772
|
+
}
|
|
1773
|
+
this._sessionBudgetRoot = null;
|
|
1774
|
+
this._sessionBudget = null;
|
|
1775
|
+
try {
|
|
1776
|
+
this._sessionHostLease?.release?.();
|
|
1777
|
+
} catch (error) {
|
|
1778
|
+
cleanupErrors.push(error);
|
|
1779
|
+
}
|
|
1613
1780
|
this._sessionHostLease = null;
|
|
1781
|
+
if (cleanupErrors.length > 0) {
|
|
1782
|
+
throw new AggregateError(
|
|
1783
|
+
cleanupErrors,
|
|
1784
|
+
"WebSocket session authority cleanup failed",
|
|
1785
|
+
);
|
|
1786
|
+
}
|
|
1614
1787
|
}
|
|
1615
1788
|
|
|
1616
1789
|
/**
|
|
@@ -1727,6 +1900,8 @@ export class WSAgentHandler {
|
|
|
1727
1900
|
break;
|
|
1728
1901
|
}
|
|
1729
1902
|
}
|
|
1903
|
+
const meterCompaction =
|
|
1904
|
+
strictUsageTelemetry || Boolean(this._sessionBudget);
|
|
1730
1905
|
|
|
1731
1906
|
const before = session.messages.length;
|
|
1732
1907
|
const expectedMessages = [...session.messages];
|
|
@@ -1735,13 +1910,15 @@ export class WSAgentHandler {
|
|
|
1735
1910
|
let compactionUsageCall = null;
|
|
1736
1911
|
let compactionUsageSettled = false;
|
|
1737
1912
|
try {
|
|
1913
|
+
this.assertSessionBudgetAdmission("WebSocket compaction");
|
|
1738
1914
|
this._sessionHostLease?.assert?.();
|
|
1739
1915
|
result = await compactConversationWithProvider(session.messages, {
|
|
1740
1916
|
provider: session.provider,
|
|
1741
1917
|
model: session.model,
|
|
1742
1918
|
baseUrl: session.baseUrl,
|
|
1743
1919
|
apiKey: session.apiKey,
|
|
1744
|
-
signal:
|
|
1920
|
+
signal:
|
|
1921
|
+
this._sessionBudgetSignal || this._sessionHostLease?.signal,
|
|
1745
1922
|
force: true,
|
|
1746
1923
|
preserveCompletedExchange: true,
|
|
1747
1924
|
llmQuery: this._compactionLlmQuery,
|
|
@@ -1750,7 +1927,7 @@ export class WSAgentHandler {
|
|
|
1750
1927
|
cwd: session.projectRoot,
|
|
1751
1928
|
sessionId: session.id,
|
|
1752
1929
|
},
|
|
1753
|
-
...(
|
|
1930
|
+
...(meterCompaction
|
|
1754
1931
|
? {
|
|
1755
1932
|
onProviderCallStart: () => {
|
|
1756
1933
|
const call = this._beginCanonicalCompactionUsage();
|
|
@@ -415,6 +415,7 @@ export class ChainlessChainWSServer extends EventEmitter {
|
|
|
415
415
|
|
|
416
416
|
/** Stop the server and clean up */
|
|
417
417
|
async stop() {
|
|
418
|
+
const cleanupErrors = [];
|
|
418
419
|
if (this._heartbeatTimer) {
|
|
419
420
|
clearInterval(this._heartbeatTimer);
|
|
420
421
|
this._heartbeatTimer = null;
|
|
@@ -423,13 +424,17 @@ export class ChainlessChainWSServer extends EventEmitter {
|
|
|
423
424
|
// Close all session handlers
|
|
424
425
|
for (const [sessionId, handler] of this.sessionHandlers) {
|
|
425
426
|
if (handler && handler.destroy) {
|
|
426
|
-
|
|
427
|
+
try {
|
|
428
|
+
handler.destroy();
|
|
429
|
+
} catch (error) {
|
|
430
|
+
cleanupErrors.push(error);
|
|
431
|
+
}
|
|
427
432
|
}
|
|
428
433
|
if (this.sessionManager) {
|
|
429
434
|
try {
|
|
430
435
|
this.sessionManager.closeSession(sessionId);
|
|
431
|
-
} catch (
|
|
432
|
-
|
|
436
|
+
} catch (error) {
|
|
437
|
+
cleanupErrors.push(error);
|
|
433
438
|
}
|
|
434
439
|
}
|
|
435
440
|
}
|
|
@@ -477,6 +482,12 @@ export class ChainlessChainWSServer extends EventEmitter {
|
|
|
477
482
|
}
|
|
478
483
|
|
|
479
484
|
this.emit("stopped");
|
|
485
|
+
if (cleanupErrors.length > 0) {
|
|
486
|
+
throw new AggregateError(
|
|
487
|
+
cleanupErrors,
|
|
488
|
+
"WebSocket server session authority cleanup failed",
|
|
489
|
+
);
|
|
490
|
+
}
|
|
480
491
|
}
|
|
481
492
|
|
|
482
493
|
/** @private */
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
getSession as dbGetSession,
|
|
33
33
|
listSessions as dbListSessions,
|
|
34
34
|
updateSession as dbUpdateSession,
|
|
35
|
+
deleteSession as dbDeleteSession,
|
|
35
36
|
} from "../../lib/session-manager.js";
|
|
36
37
|
import { buildSystemPrompt } from "../../runtime/agent-core.js";
|
|
37
38
|
import { SubAgentRegistry } from "../../lib/sub-agent-registry.js";
|
|
@@ -136,6 +137,7 @@ function bindSessionHooksV2Workspace(
|
|
|
136
137
|
* @property {string|null} rulesContent
|
|
137
138
|
* @property {string[]} enabledToolNames
|
|
138
139
|
* @property {object|null} hostManagedToolPolicy
|
|
140
|
+
* @property {object|null} sessionBudgetRoot
|
|
139
141
|
* @property {Array<object>} externalToolDefinitions
|
|
140
142
|
* @property {object} externalToolDescriptors
|
|
141
143
|
* @property {object} externalToolExecutors
|
|
@@ -398,6 +400,8 @@ export class WSSessionManager {
|
|
|
398
400
|
* @param {string} [options.apiKey]
|
|
399
401
|
* @param {string} [options.baseUrl]
|
|
400
402
|
* @param {object} [options.hostManagedToolPolicy]
|
|
403
|
+
* @param {object} [options.sessionBudgetRoot]
|
|
404
|
+
* @param {boolean} [options.requireDurable=false]
|
|
401
405
|
* @returns {{ sessionId: string }}
|
|
402
406
|
*/
|
|
403
407
|
createSession(options = {}) {
|
|
@@ -407,6 +411,14 @@ export class WSSessionManager {
|
|
|
407
411
|
);
|
|
408
412
|
}
|
|
409
413
|
const sessionId = this._generateId();
|
|
414
|
+
const requireDurable = options.requireDurable === true;
|
|
415
|
+
if (requireDurable && !this.db) {
|
|
416
|
+
const error = new Error(
|
|
417
|
+
"Durable WebSocket session creation requires a configured database",
|
|
418
|
+
);
|
|
419
|
+
error.code = "CC_WS_DURABLE_SESSION_REQUIRED";
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
410
422
|
const type = options.type || "agent";
|
|
411
423
|
const baseProjectRoot = options.projectRoot || this.defaultProjectRoot;
|
|
412
424
|
const hostAuthorizedBaseRoot =
|
|
@@ -479,6 +491,7 @@ export class WSSessionManager {
|
|
|
479
491
|
const messages = [{ role: "system", content: systemPrompt }];
|
|
480
492
|
|
|
481
493
|
// Persist to DB
|
|
494
|
+
let databaseCreated = false;
|
|
482
495
|
if (this.db) {
|
|
483
496
|
try {
|
|
484
497
|
dbCreateSession(this.db, {
|
|
@@ -488,8 +501,17 @@ export class WSSessionManager {
|
|
|
488
501
|
model: model || "",
|
|
489
502
|
messages,
|
|
490
503
|
});
|
|
491
|
-
|
|
492
|
-
|
|
504
|
+
databaseCreated = true;
|
|
505
|
+
} catch (error) {
|
|
506
|
+
if (requireDurable) {
|
|
507
|
+
this._discardSessionResources({
|
|
508
|
+
planManager,
|
|
509
|
+
worktree,
|
|
510
|
+
baseProjectRoot,
|
|
511
|
+
});
|
|
512
|
+
error.code ||= "CC_WS_SESSION_PERSISTENCE_FAILED";
|
|
513
|
+
throw error;
|
|
514
|
+
}
|
|
493
515
|
}
|
|
494
516
|
}
|
|
495
517
|
|
|
@@ -505,6 +527,7 @@ export class WSSessionManager {
|
|
|
505
527
|
mcpClient: this.mcpClient,
|
|
506
528
|
enabledToolNames,
|
|
507
529
|
hostManagedToolPolicy: options.hostManagedToolPolicy || null,
|
|
530
|
+
sessionBudgetRoot: options.sessionBudgetRoot || null,
|
|
508
531
|
externalToolDefinitions: externalTools.definitions,
|
|
509
532
|
externalToolDescriptors: externalTools.descriptors,
|
|
510
533
|
externalToolExecutors: externalTools.executors,
|
|
@@ -540,13 +563,33 @@ export class WSSessionManager {
|
|
|
540
563
|
},
|
|
541
564
|
);
|
|
542
565
|
|
|
543
|
-
if (this.db) {
|
|
566
|
+
if (this.db && databaseCreated) {
|
|
544
567
|
try {
|
|
545
568
|
dbUpdateSession(this.db, sessionId, {
|
|
546
569
|
metadata: this._serializeSessionMetadata(session),
|
|
547
570
|
});
|
|
548
|
-
} catch (
|
|
549
|
-
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if (requireDurable) {
|
|
573
|
+
const cleanupErrors = [];
|
|
574
|
+
try {
|
|
575
|
+
this._discardSessionResources(session);
|
|
576
|
+
} catch (cleanupError) {
|
|
577
|
+
cleanupErrors.push(cleanupError);
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
dbDeleteSession(this.db, sessionId);
|
|
581
|
+
} catch (cleanupError) {
|
|
582
|
+
cleanupErrors.push(cleanupError);
|
|
583
|
+
}
|
|
584
|
+
error.code ||= "CC_WS_SESSION_PERSISTENCE_FAILED";
|
|
585
|
+
if (cleanupErrors.length > 0) {
|
|
586
|
+
throw new AggregateError(
|
|
587
|
+
[error, ...cleanupErrors],
|
|
588
|
+
"Durable WebSocket session creation and rollback both failed",
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
550
593
|
}
|
|
551
594
|
}
|
|
552
595
|
|
|
@@ -556,6 +599,57 @@ export class WSSessionManager {
|
|
|
556
599
|
return { sessionId };
|
|
557
600
|
}
|
|
558
601
|
|
|
602
|
+
/**
|
|
603
|
+
* Remove a newly-created session that was never exposed to a client.
|
|
604
|
+
* Unlike closeSession(), this compensates the database insert instead of
|
|
605
|
+
* persisting a misleading closed session after bootstrap failed.
|
|
606
|
+
*/
|
|
607
|
+
rollbackSessionCreation(sessionId) {
|
|
608
|
+
const session = this.sessions.get(sessionId);
|
|
609
|
+
const cleanupErrors = [];
|
|
610
|
+
if (session) {
|
|
611
|
+
try {
|
|
612
|
+
this._discardSessionResources(session);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
cleanupErrors.push(error);
|
|
615
|
+
}
|
|
616
|
+
this.sessions.delete(sessionId);
|
|
617
|
+
}
|
|
618
|
+
if (this.db) {
|
|
619
|
+
try {
|
|
620
|
+
dbDeleteSession(this.db, sessionId);
|
|
621
|
+
} catch (error) {
|
|
622
|
+
cleanupErrors.push(error);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (cleanupErrors.length > 0) {
|
|
626
|
+
throw new AggregateError(
|
|
627
|
+
cleanupErrors,
|
|
628
|
+
`WebSocket session creation rollback failed: ${sessionId}`,
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
return Boolean(session);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
_discardSessionResources(session) {
|
|
635
|
+
if (!session) return;
|
|
636
|
+
if (
|
|
637
|
+
session._releaseHooksV2WorkspaceBindingOnClose === true &&
|
|
638
|
+
session.hooksV2WorkspaceBindingId
|
|
639
|
+
) {
|
|
640
|
+
releaseRegisteredHostHooksV2Workspace(session.hooksV2WorkspaceBindingId);
|
|
641
|
+
}
|
|
642
|
+
if (typeof session._planPersistenceCleanup === "function") {
|
|
643
|
+
session._planPersistenceCleanup();
|
|
644
|
+
}
|
|
645
|
+
session.planManager?.removeAllListeners?.();
|
|
646
|
+
if (session.worktree?.path && session.baseProjectRoot) {
|
|
647
|
+
removeWorktree(session.baseProjectRoot, session.worktree.path, {
|
|
648
|
+
deleteBranch: true,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
559
653
|
/**
|
|
560
654
|
* Resume an existing session from DB.
|
|
561
655
|
*
|
|
@@ -645,6 +739,7 @@ export class WSSessionManager {
|
|
|
645
739
|
metadata.enabledToolNames,
|
|
646
740
|
),
|
|
647
741
|
hostManagedToolPolicy: metadata.hostManagedToolPolicy || null,
|
|
742
|
+
sessionBudgetRoot: metadata.sessionBudgetRoot || null,
|
|
648
743
|
externalToolDefinitions: externalTools.definitions,
|
|
649
744
|
externalToolDescriptors: externalTools.descriptors,
|
|
650
745
|
externalToolExecutors: externalTools.executors,
|
|
@@ -1545,6 +1640,7 @@ export class WSSessionManager {
|
|
|
1545
1640
|
baseProjectRoot: session.baseProjectRoot || session.projectRoot || null,
|
|
1546
1641
|
baseUrl: session.baseUrl || null,
|
|
1547
1642
|
hostManagedToolPolicy: session.hostManagedToolPolicy || null,
|
|
1643
|
+
sessionBudgetRoot: session.sessionBudgetRoot || null,
|
|
1548
1644
|
enabledToolNames: session.enabledToolNames || [],
|
|
1549
1645
|
canonicalHostSystemPrefix: Array.isArray(
|
|
1550
1646
|
session._canonicalHostSystemPrefix,
|