chainlesschain 0.166.7 → 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 +3 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BlrmQxa2.js → AIOps-DJvpifGx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-zyRyLITg.js → ActionButton-D2UhuF_A.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DwGz7OGN.js → Analytics-C9ho9obu.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Dc-nj7Zd.js → AppLayout-BvRCpFK3.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-7Hu3O73p.js → Artifacts-jxrySvpk.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bnoe3-fR.js → Audit-ovEw0ZYl.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BqYFrDKZ.js → BackgroundAgents-D7H5z6zS.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-B3hwW1nz.js → Backup-BuXob8za.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-XFVxLA4w.js → BaseInput-CtnszuQ2.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CWRDNw7e.js → Chat-B_mYE8fJ.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-Bf30VM8G.js → ChatBubbleRenderer-DaiA6CsX.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-kFeeBgac.js → Checkbox-ierdtcJD.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CCxp8xoI.js → Codegen-3VKPJD_B.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BLKZalle.js → Col-1EdMyf5P.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DD0eWCez.js → Community-D6H7UA8K.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CHqHWdy0.js → Compact-BBAZvYIm.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bw54uPiK.js → Compliance-CgsTrAoj.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DZ13zKNJ.js → Cowork-tnZgLoTE.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dlj5Jevg.js → Cron-CPpZw2Nh.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Nvz0aR0j.js → Crosschain-BymBcp5z.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Ct89eUyY.js → DID-DKPxS_K1.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BHUH5IAe.js → Dashboard-DvJhEmH9.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BhZG-PO7.js → Dropdown-b8iQyQIZ.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-ae4RVwwJ.js → EmailListRenderer--0-A-zKb.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-AOcxLfLr.js → FamilyGuardDashboard-C12iDN06.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BK2PJQOF.js → Federation-BRhaE2Z8.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C2n3xO2_.js → FormItemContext-C4dnq7yf.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DcyUVFZq.js → GenericCardRenderer-CGAdbR5n.js} +1 -1
- package/src/assets/web-panel/assets/{Git-tBae9Lt8.js → Git-4eA2fYzL.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DmNR3AP4.js → Governance-BJuY9Y-9.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CoseEigi.js → Inference-DZ5Dy4up.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Bdjd1sxr.js → KnowledgeGraph-BMTjRs4A.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CtlkKIcn.js → Logs-BKEeQEfJ.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-B05DgASC.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BTYb7nEx.js → Marketplace-Dhho4fEg.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-D0iUdW3P.js → McpTools-BW6xxSlU.js} +2 -2
- package/src/assets/web-panel/assets/{Memory--gwI7w4Y.js → Memory-mPqlpSkq.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-JmFBi24T.js → MobileBridge-Cb9N_DQI.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BK01cQRx.js → MobileProjects-BNs8tah_.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-u7avzyoO.js → Mtc-CaxBt8tn.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-gT3z5fo_.js → MtcAudit-DRPQbp3t.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BIqZTz5n.js → Multisig-CRm-d8bK.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DdhVfXYq.js → NLProgramming-BCCk3LPs.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CBlyUmY3.js → Notes-Cv8SN8gN.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DSyI26N3.js → NotificationSettings-CggIvTh5.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-B7IEuNz4.js → OrderTableRenderer-BW3_bV12.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-ozhi4kGS.js → Organization-BuRE-VCO.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CBy57RVM.js → Overflow-DBrm1gju.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CA-ZRv77.js → P2P-CWVnDYgS.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DOHc9zm8.js → PdhVaultBrowser-8RKUIpte.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DiP632GP.js → Permissions-tDUAxogW.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-AnU8O-SS.js → PersonalDataHub-Dv2-CFis.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-DDibOUaP.js → Pipeline-DM6kvFni.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-D421t36T.js → Privacy-DzK-yL0d.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DR879ZPJ.js → ProjectInit-B5UrNpkj.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Ded8o6aY.js → ProjectSettings-CxJD43oW.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Z2Orm8Ma.js +1 -0
- package/src/assets/web-panel/assets/{Providers-Boivuu-E.js → Providers-CYl4jRYK.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-xKD_ntKp.js → QrScannerModal-DeH2T0fH.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-D3y_A6FV.js → QuickAsk-Bw9k_kTk.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BjKM-YC2.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-Ci2GjH5p.js → Reputation-Ds_Dfr-k.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B0-T2BSh.js → Row-BpQUQTNj.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Bet-ur-r.js → RssFeed-BatpZBGt.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DWtRAS2R.js → Search-D7hOObzi.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BDfu2Qn6.js → Security-dXQ4FKNt.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CvJmvSMM.js → Services-D8wInGPa.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CEbrKpdC.js → Skeleton-DXQCoYpM.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CjVeiETf.js → Skills-D3oLRIVn.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Dibypxum.js → Sla-BmP4-LnJ.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-XflYXp7a.js → SpeechSettings-DLj-xzro.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-ro1Ivx1E.js → SyncSettings-8KKaaRMr.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CHq1qFbo.js → Tasks-GyK42I7C.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CRnUZxtZ.js → Templates-n1y_8UBG.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-jUX72JCA.js → Tenant-B39nBph8.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BBhAs7pt.js → Terminal-DCQoOKXe.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CD5M3RGp.js → TimelineRenderer-BkhbDfXk.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-sah3v0gu.js → Tokens-BeHIIU8D.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BNT3Uz3s.js → Trigger-BHvWXVOb.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BytMGPMo.js → Trust-y2WaOSto.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BkYVMRQs.js → UkeySign-DBFeJBzt.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-psvb5Q7Y.js → VideoEditing-BEOWitC-.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-PFQTW0MH.js → Wallet-BITedTe6.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DgO-OwRT.js → WebAuthn-DMwlJVin.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-DL2l9VNl.js → WorkflowEditor-CKfGFEu1.js} +1 -1
- package/src/assets/web-panel/assets/{chat-C84akEzN.js → chat-DR6gnBGF.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DzTWuQKP.js → colors-C_Hw5NCn.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CR9LJQFv.js → compact-item-B8IPwsEu.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CTTov5Kn.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-DALj523o.js → hasIn-CZubdS4D.js} +1 -1
- package/src/assets/web-panel/assets/{index-Be6Os5S8.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-CXaCRXN4.js → index-BXS6wYJZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-7ux_XohH.js → index-Bi21Etg4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXTEpfGe.js → index-Bizm87cX.js} +1 -1
- package/src/assets/web-panel/assets/{index-DH6OG-ci.js → index-Bv2132PO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZhhFQi9.js → index-BzoFanEQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6QOJbSn.js → index-C25rOz7O.js} +1 -1
- package/src/assets/web-panel/assets/{index-CunGDV0g.js → index-C2VmtNaQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmELknPe.js → index-Cbm8F0M2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZER6Lj3.js → index-CfniPP5p.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2qpjU-D.js → index-Cg2bCeol.js} +1 -1
- package/src/assets/web-panel/assets/{index-QW0EXx14.js → index-CiXkhOkY.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5eeMl_2.js → index-Cl5RpreD.js} +1 -1
- package/src/assets/web-panel/assets/{index-s4CFyn-n.js → index-CqmLw8cs.js} +1 -1
- package/src/assets/web-panel/assets/{index-eJcFleIV.js → index-Ctje8Vl3.js} +1 -1
- package/src/assets/web-panel/assets/{index-3-XJoYgX.js → index-D1w96gg9.js} +1 -1
- package/src/assets/web-panel/assets/{index-_8vetPbU.js → index-D47jt8ET.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cef0n4k_.js → index-DJ6pG79m.js} +1 -1
- package/src/assets/web-panel/assets/{index-AXnERsyV.js → index-DTosx2Wu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtW6Zr_i.js → index-DY6MHo9D.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNmTDXmf.js → index-DcFjRT-K.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dah8M4l2.js → index-DfnzrUxU.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0MqUnKO.js → index-DjRrqooR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqUgXYIQ.js → index-DmRcgiYT.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9ScRFxz.js → index-DpjHpRoq.js} +3 -3
- package/src/assets/web-panel/assets/{index-DjE_ew7U.js → index-Dt-w3xQ7.js} +1 -1
- package/src/assets/web-panel/assets/{index-W9axN_Ce.js → index-DuOU5apg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDyt57zL.js → index-LvjAKDSl.js} +1 -1
- package/src/assets/web-panel/assets/{index-hzzTITR9.js → index-_nL79NT8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNaQt49r.js → index-auyNYjLo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBIPnoTj.js → index-cVqbas5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnxjXIsK.js → index-ffe7bvqP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHRXKiU1.js → index-idZKyADQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_Bg0LdK.js → index-k1Xa5iw1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BKNIzw0U.js → index-mYZJxGH6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxdzSljS.js → index-rYBHSDcJ.js} +1 -1
- package/src/assets/web-panel/assets/index-vJagfET2.js +1 -0
- package/src/assets/web-panel/assets/{index-CjkPDSX4.js → index-xoNssgz_.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-JuW8mFj7.js → initDefaultProps-lcU_dT2r.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Cwb0HBve.js → motion-Dc7UNXZ1.js} +1 -1
- package/src/assets/web-panel/assets/{move-DHokzMgu.js → move-Cll8ZXaL.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DX31y6QS.js → mtc-parser-CAb_yLIM.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BCIUYDA6.js → omit-DurYMyTe.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DMCj4EoZ.js → pickAttrs-BITQyOlw.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Dy5K-u1s.js → placementArrow-Dg1rH91i.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DkGeZu54.js → responsiveObserve-D_EWIeac.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Dc5dyJdg.js → slide-lK4d3zsh.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIUBjOaC.js → statusUtils-Bflq-Git.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DmyEOxm5.js → styleChecker-Ccp0I-wt.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-FRhakk1e.js → useFlexGapSupport-CYZbByYA.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-clXyV1WQ.js → useFs-B_x0nxFD.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-UF6DG41z.js → usePersonalDataHub-DW55rL85.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CoCt95G7.js → vnode-Ca6MH3W8.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-yVWt-ENm.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/orchestrate.js +61 -9
- package/src/data/changelog.json +34 -1
- 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 +148 -2
- package/src/lib/agent-team/task-lease.js +172 -3
- package/src/lib/agent-team/team-runner.js +60 -1
- package/src/lib/app-server/graph-runtime.js +326 -2
- package/src/lib/app-server/server.js +60 -0
- package/src/lib/approval-grant-ledger.js +69 -0
- package/src/lib/codex-app-server-adapter.js +11 -20
- package/src/lib/graph-kernel/compiler.js +66 -13
- package/src/lib/graph-kernel/cutover-authority-resolver.js +26 -0
- package/src/lib/graph-kernel/cutover-ledger.js +53 -11
- package/src/lib/graph-kernel/retirement-evidence.js +567 -0
- package/src/lib/graph-kernel/runtime.js +76 -14
- package/src/lib/interaction-adapter.js +61 -9
- 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/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-CZsTBEQE.js +0 -1
- package/src/assets/web-panel/assets/Projects-BHPxiucB.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-CgqKMXG4.js +0 -5
- package/src/assets/web-panel/assets/devWarning-BOhvmlpc.js +0 -1
- package/src/assets/web-panel/assets/index-DDyMJexx.js +0 -1
- package/src/assets/web-panel/assets/index-btiojFYn.js +0 -1
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
compileGraphDefinition,
|
|
5
5
|
executionAttemptId,
|
|
6
6
|
graphDigest,
|
|
7
|
+
validateGraphDefinitionMigrationEvidence,
|
|
7
8
|
writeScopesOverlap,
|
|
8
9
|
} from "./compiler.js";
|
|
9
10
|
import { GraphEventStore } from "./event-store.js";
|
|
@@ -356,6 +357,7 @@ function stateSnapshot(run) {
|
|
|
356
357
|
version: 1,
|
|
357
358
|
id: run.id,
|
|
358
359
|
definition: run.compiled.definition,
|
|
360
|
+
definitionMigration: clone(run.definitionMigration),
|
|
359
361
|
subgraphDefinitions: run.compiled.subgraphDefinitions,
|
|
360
362
|
phase: run.phase,
|
|
361
363
|
status: run.status,
|
|
@@ -404,6 +406,11 @@ function restoreState(run, snapshot, compiled = null) {
|
|
|
404
406
|
subgraphs: persistedSubgraphRegistry(snapshot.subgraphDefinitions),
|
|
405
407
|
allowUnresolvedSubgraphs: snapshot.subgraphDefinitions == null,
|
|
406
408
|
});
|
|
409
|
+
run.definitionMigration = snapshot.definitionMigration
|
|
410
|
+
? clone(
|
|
411
|
+
validateGraphDefinitionMigrationEvidence(snapshot.definitionMigration),
|
|
412
|
+
)
|
|
413
|
+
: clone(run.compiled.definitionMigration || null);
|
|
407
414
|
run.phase = snapshot.phase;
|
|
408
415
|
run.status = snapshot.status;
|
|
409
416
|
run.graphRevision = snapshot.graphRevision;
|
|
@@ -475,12 +482,35 @@ function attemptProjection(attempt) {
|
|
|
475
482
|
return Object.freeze(clone(attempt));
|
|
476
483
|
}
|
|
477
484
|
|
|
485
|
+
function humanTaskProjection(task) {
|
|
486
|
+
const projected = clone(task);
|
|
487
|
+
if (
|
|
488
|
+
projected.claimExpiresAt == null &&
|
|
489
|
+
Number.isFinite(projected.claimExpiresAtMs)
|
|
490
|
+
) {
|
|
491
|
+
projected.claimExpiresAt = nowIso(() => projected.claimExpiresAtMs);
|
|
492
|
+
}
|
|
493
|
+
delete projected.claimExpiresAtMs;
|
|
494
|
+
delete projected.expiresAtMs;
|
|
495
|
+
return Object.freeze(projected);
|
|
496
|
+
}
|
|
497
|
+
|
|
478
498
|
function runProjection(run) {
|
|
479
499
|
return Object.freeze({
|
|
480
500
|
id: run.id,
|
|
481
501
|
definitionId: run.compiled.definitionId,
|
|
482
502
|
revision: run.compiled.revision,
|
|
483
503
|
revisionDigest: run.revisionDigest,
|
|
504
|
+
definitionMigration: run.definitionMigration
|
|
505
|
+
? Object.freeze({
|
|
506
|
+
schema: run.definitionMigration.schema,
|
|
507
|
+
fromVersion: run.definitionMigration.fromVersion,
|
|
508
|
+
toVersion: run.definitionMigration.toVersion,
|
|
509
|
+
revisionDigest: run.definitionMigration.revisionDigest,
|
|
510
|
+
rollbackDigest: run.definitionMigration.rollbackDigest,
|
|
511
|
+
backupAvailable: true,
|
|
512
|
+
})
|
|
513
|
+
: null,
|
|
484
514
|
occurrenceRef: clone(run.occurrenceRef),
|
|
485
515
|
phase: run.phase,
|
|
486
516
|
status: run.status,
|
|
@@ -591,6 +621,7 @@ function buildRun(compiled, options, now, writerIdentity, writerLeaseTtlMs) {
|
|
|
591
621
|
const run = {
|
|
592
622
|
id,
|
|
593
623
|
compiled,
|
|
624
|
+
definitionMigration: clone(compiled.definitionMigration || null),
|
|
594
625
|
phase: "open",
|
|
595
626
|
status: "running",
|
|
596
627
|
graphRevision: compiled.revision,
|
|
@@ -963,6 +994,7 @@ function sweepExpirations(run, now) {
|
|
|
963
994
|
task.status = "open";
|
|
964
995
|
task.claimActorId = null;
|
|
965
996
|
task.claimLeaseId = null;
|
|
997
|
+
task.claimExpiresAt = null;
|
|
966
998
|
task.claimExpiresAtMs = null;
|
|
967
999
|
task.updatedAt = nowIso(() => now);
|
|
968
1000
|
changed = true;
|
|
@@ -1170,6 +1202,13 @@ function classifyQuiescence(run, now) {
|
|
|
1170
1202
|
) {
|
|
1171
1203
|
return "running";
|
|
1172
1204
|
}
|
|
1205
|
+
if (
|
|
1206
|
+
[...run.humanTasks.values()].some((task) =>
|
|
1207
|
+
["open", "claimed"].includes(task.status),
|
|
1208
|
+
)
|
|
1209
|
+
) {
|
|
1210
|
+
return "waiting_human";
|
|
1211
|
+
}
|
|
1173
1212
|
if (
|
|
1174
1213
|
[...run.attempts.values()].some((attempt) =>
|
|
1175
1214
|
ACTIVE_ATTEMPT.has(attempt.status),
|
|
@@ -1191,13 +1230,6 @@ function classifyQuiescence(run, now) {
|
|
|
1191
1230
|
) {
|
|
1192
1231
|
return "reconciliation_required";
|
|
1193
1232
|
}
|
|
1194
|
-
if (
|
|
1195
|
-
[...run.humanTasks.values()].some((task) =>
|
|
1196
|
-
["open", "claimed"].includes(task.status),
|
|
1197
|
-
)
|
|
1198
|
-
) {
|
|
1199
|
-
return "waiting_human";
|
|
1200
|
-
}
|
|
1201
1233
|
if (
|
|
1202
1234
|
[...run.messages.values()].some((message) =>
|
|
1203
1235
|
["admitted", "delivered", "read"].includes(message.status),
|
|
@@ -1582,6 +1614,19 @@ export class GraphKernel {
|
|
|
1582
1614
|
);
|
|
1583
1615
|
}
|
|
1584
1616
|
|
|
1617
|
+
humanTasks(runId) {
|
|
1618
|
+
const run = this._run(runId);
|
|
1619
|
+
return Object.freeze(
|
|
1620
|
+
[...run.humanTasks.values()]
|
|
1621
|
+
.sort(
|
|
1622
|
+
(left, right) =>
|
|
1623
|
+
left.createdAt.localeCompare(right.createdAt) ||
|
|
1624
|
+
left.id.localeCompare(right.id),
|
|
1625
|
+
)
|
|
1626
|
+
.map(humanTaskProjection),
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1585
1630
|
registerAgent(runId, { agentId, capacity = 1, resident = true } = {}) {
|
|
1586
1631
|
const run = this._run(runId);
|
|
1587
1632
|
const id = safeIdentifier(agentId, "agentId");
|
|
@@ -4146,11 +4191,12 @@ export class GraphKernel {
|
|
|
4146
4191
|
authorityDigest: run.authorityDigest,
|
|
4147
4192
|
status: "open",
|
|
4148
4193
|
nonce: safeIdentifier(nonce, "nonce"),
|
|
4149
|
-
quorum: Math.max(1, Number(quorum) || 1),
|
|
4194
|
+
quorum: Math.min(64, Math.max(1, Math.trunc(Number(quorum)) || 1)),
|
|
4150
4195
|
separationOfDuties: separationOfDuties === true,
|
|
4151
4196
|
decisions: [],
|
|
4152
4197
|
claimActorId: null,
|
|
4153
4198
|
claimLeaseId: null,
|
|
4199
|
+
claimExpiresAt: null,
|
|
4154
4200
|
claimExpiresAtMs: null,
|
|
4155
4201
|
createdAt: nowIso(() => now),
|
|
4156
4202
|
updatedAt: nowIso(() => now),
|
|
@@ -4160,7 +4206,7 @@ export class GraphKernel {
|
|
|
4160
4206
|
};
|
|
4161
4207
|
run.humanTasks.set(id, task);
|
|
4162
4208
|
run.status = "waiting_human";
|
|
4163
|
-
return
|
|
4209
|
+
return humanTaskProjection(task);
|
|
4164
4210
|
},
|
|
4165
4211
|
{ idempotencyKey: `human-task:${id}` },
|
|
4166
4212
|
);
|
|
@@ -4206,8 +4252,9 @@ export class GraphKernel {
|
|
|
4206
4252
|
task.claimActorId = actor;
|
|
4207
4253
|
task.claimLeaseId = safeIdentifier(claimLeaseId, "claimLeaseId");
|
|
4208
4254
|
task.claimExpiresAtMs = now + finitePositive(ttlMs, 300_000);
|
|
4255
|
+
task.claimExpiresAt = nowIso(() => task.claimExpiresAtMs);
|
|
4209
4256
|
task.updatedAt = nowIso(() => now);
|
|
4210
|
-
return
|
|
4257
|
+
return humanTaskProjection(task);
|
|
4211
4258
|
},
|
|
4212
4259
|
);
|
|
4213
4260
|
}
|
|
@@ -4312,11 +4359,15 @@ export class GraphKernel {
|
|
|
4312
4359
|
task.status = "open";
|
|
4313
4360
|
task.claimActorId = null;
|
|
4314
4361
|
task.claimLeaseId = null;
|
|
4362
|
+
task.claimExpiresAt = null;
|
|
4315
4363
|
task.claimExpiresAtMs = null;
|
|
4316
4364
|
}
|
|
4317
4365
|
task.updatedAt = nowIso(() => now);
|
|
4318
4366
|
run.status = classifyQuiescence(run, now);
|
|
4319
|
-
|
|
4367
|
+
if (TERMINAL_RUN.has(run.status)) {
|
|
4368
|
+
run.completedAt = nowIso(() => now);
|
|
4369
|
+
}
|
|
4370
|
+
return humanTaskProjection(task);
|
|
4320
4371
|
},
|
|
4321
4372
|
);
|
|
4322
4373
|
}
|
|
@@ -4336,14 +4387,25 @@ export class GraphKernel {
|
|
|
4336
4387
|
"human task is already terminal",
|
|
4337
4388
|
);
|
|
4338
4389
|
}
|
|
4390
|
+
const now = this.now();
|
|
4339
4391
|
task.status = "cancelled";
|
|
4340
4392
|
task.reason = String(reason).slice(0, 1024);
|
|
4341
|
-
task.updatedAt = nowIso(
|
|
4393
|
+
task.updatedAt = nowIso(() => now);
|
|
4394
|
+
const attempt = run.attempts.get(task.attemptId);
|
|
4395
|
+
if (attempt && attempt.status === "waiting_human") {
|
|
4396
|
+
attempt.status = "cancelled";
|
|
4397
|
+
attempt.participationStatus = "human_cancelled";
|
|
4398
|
+
attempt.updatedAt = nowIso(() => now);
|
|
4399
|
+
}
|
|
4342
4400
|
const state = run.nodeStates.get(task.nodeId);
|
|
4343
4401
|
state.status = "cancelled";
|
|
4344
4402
|
state.blockedRoot = task.id;
|
|
4345
|
-
state.updatedAt = nowIso(
|
|
4346
|
-
|
|
4403
|
+
state.updatedAt = nowIso(() => now);
|
|
4404
|
+
run.status = classifyQuiescence(run, now);
|
|
4405
|
+
if (TERMINAL_RUN.has(run.status)) {
|
|
4406
|
+
run.completedAt = nowIso(() => now);
|
|
4407
|
+
}
|
|
4408
|
+
return humanTaskProjection(task);
|
|
4347
4409
|
},
|
|
4348
4410
|
);
|
|
4349
4411
|
}
|
|
@@ -205,6 +205,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
205
205
|
// null (the default for plain questions) → no binding check, unchanged.
|
|
206
206
|
binding: options.binding || null,
|
|
207
207
|
bindingType: options.bindingType || null,
|
|
208
|
+
requireBinding: options.requireBinding === true,
|
|
208
209
|
});
|
|
209
210
|
|
|
210
211
|
if (approvalRequest) {
|
|
@@ -253,6 +254,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
253
254
|
// Approval binding pass-through: a permission-gate producer supplies
|
|
254
255
|
// it; plain questions never carry one (message stays byte-identical).
|
|
255
256
|
...(options.binding ? { binding: options.binding } : {}),
|
|
257
|
+
...(options.requireBinding === true ? { requireBinding: true } : {}),
|
|
256
258
|
},
|
|
257
259
|
);
|
|
258
260
|
}
|
|
@@ -290,6 +292,45 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
290
292
|
return answer === "true" || answer === "yes" || answer === "y";
|
|
291
293
|
}
|
|
292
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Structured approval variant used by Desktop/WebSocket hosts. Unlike the
|
|
297
|
+
* legacy boolean askConfirm path, this preserves the canonical decision
|
|
298
|
+
* lifetime and requires the trusted host to echo the exact request binding.
|
|
299
|
+
*/
|
|
300
|
+
async askApproval(question, options = {}) {
|
|
301
|
+
const {
|
|
302
|
+
binding = null,
|
|
303
|
+
default: dflt = false,
|
|
304
|
+
approval = {},
|
|
305
|
+
...extra
|
|
306
|
+
} = options || {};
|
|
307
|
+
const answer = await this._ask(
|
|
308
|
+
"confirm",
|
|
309
|
+
question,
|
|
310
|
+
{ default: dflt, approval, ...extra },
|
|
311
|
+
binding ? { binding, requireBinding: true } : {},
|
|
312
|
+
);
|
|
313
|
+
const candidate =
|
|
314
|
+
answer?.decision && typeof answer.decision === "object"
|
|
315
|
+
? answer.decision
|
|
316
|
+
: answer;
|
|
317
|
+
if (
|
|
318
|
+
candidate &&
|
|
319
|
+
typeof candidate === "object" &&
|
|
320
|
+
[
|
|
321
|
+
"acceptOnce",
|
|
322
|
+
"acceptForTurn",
|
|
323
|
+
"acceptForSession",
|
|
324
|
+
"decline",
|
|
325
|
+
"cancel",
|
|
326
|
+
].includes(candidate.kind)
|
|
327
|
+
) {
|
|
328
|
+
return candidate;
|
|
329
|
+
}
|
|
330
|
+
if (answer === true) return { kind: "acceptOnce" };
|
|
331
|
+
return { kind: "decline" };
|
|
332
|
+
}
|
|
333
|
+
|
|
293
334
|
/**
|
|
294
335
|
* Agent-core AskUserQuestion transport for Desktop/WebSocket consumers.
|
|
295
336
|
* It uses the same pending map and `session-answer` resolver as MCP
|
|
@@ -369,7 +410,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
369
410
|
* Resolves the corresponding pending promise.
|
|
370
411
|
*/
|
|
371
412
|
resolveAnswer(requestId, answer, binding = null) {
|
|
372
|
-
this._resolvePending(requestId, answer, "question", binding);
|
|
413
|
+
return this._resolvePending(requestId, answer, "question", binding);
|
|
373
414
|
}
|
|
374
415
|
|
|
375
416
|
/**
|
|
@@ -441,7 +482,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
441
482
|
}
|
|
442
483
|
|
|
443
484
|
resolveHostTool(requestId, payload) {
|
|
444
|
-
this._resolvePending(requestId, payload, "host-tool");
|
|
485
|
+
return this._resolvePending(requestId, payload, "host-tool");
|
|
445
486
|
}
|
|
446
487
|
|
|
447
488
|
rejectAllPending(reason = createAbortError("Interaction interrupted")) {
|
|
@@ -549,7 +590,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
549
590
|
) {
|
|
550
591
|
const pending = this._pending.get(requestId);
|
|
551
592
|
if (!pending) {
|
|
552
|
-
return;
|
|
593
|
+
return Object.freeze({ settled: false, reason: "not_pending" });
|
|
553
594
|
}
|
|
554
595
|
// Kind guard: a question-answer must not settle a host-tool request (or
|
|
555
596
|
// vice versa). On a mismatch, IGNORE the message — leave the request pending
|
|
@@ -559,7 +600,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
559
600
|
// value). Backward-compatible: callers passing no expectedKind, and
|
|
560
601
|
// pre-kind pending entries, behave exactly as before.
|
|
561
602
|
if (expectedKind && pending.kind && pending.kind !== expectedKind) {
|
|
562
|
-
return;
|
|
603
|
+
return Object.freeze({ settled: false, reason: "kind_mismatch" });
|
|
563
604
|
}
|
|
564
605
|
|
|
565
606
|
// Approval-binding guard (authority §"权限来源"): if this request was raised
|
|
@@ -572,17 +613,18 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
572
613
|
pending.bindingType === "interaction" &&
|
|
573
614
|
!sameInteractionBinding(pending.binding, incomingBinding)
|
|
574
615
|
) {
|
|
575
|
-
return;
|
|
616
|
+
return Object.freeze({ settled: false, reason: "binding_mismatch" });
|
|
576
617
|
}
|
|
577
618
|
|
|
578
619
|
let effective = payload;
|
|
579
620
|
if (
|
|
580
621
|
pending.bindingType !== "interaction" &&
|
|
581
622
|
pending.binding &&
|
|
582
|
-
incomingBinding
|
|
583
|
-
|
|
623
|
+
((pending.requireBinding && !incomingBinding) ||
|
|
624
|
+
(incomingBinding &&
|
|
625
|
+
!verifyApprovalBinding(pending.binding, incomingBinding)))
|
|
584
626
|
) {
|
|
585
|
-
effective = false;
|
|
627
|
+
effective = pending.requireBinding ? { kind: "decline" } : false;
|
|
586
628
|
}
|
|
587
629
|
|
|
588
630
|
this._pending.delete(requestId);
|
|
@@ -594,13 +636,23 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
594
636
|
effective === true ||
|
|
595
637
|
effective === "true" ||
|
|
596
638
|
effective === "yes" ||
|
|
597
|
-
effective === "y"
|
|
639
|
+
effective === "y" ||
|
|
640
|
+
["acceptOnce", "acceptForTurn", "acceptForSession"].includes(
|
|
641
|
+
effective?.kind || effective?.decision?.kind,
|
|
642
|
+
);
|
|
598
643
|
this._notifyPendingApproval("approval.settled", {
|
|
599
644
|
requestId,
|
|
600
645
|
reason: normalizedApproval ? "approved" : "denied",
|
|
601
646
|
});
|
|
602
647
|
}
|
|
603
648
|
pending.resolve(effective);
|
|
649
|
+
return Object.freeze({
|
|
650
|
+
settled: true,
|
|
651
|
+
reason:
|
|
652
|
+
effective !== payload && pending.requireBinding
|
|
653
|
+
? "binding_mismatch_denied"
|
|
654
|
+
: "resolved",
|
|
655
|
+
});
|
|
604
656
|
}
|
|
605
657
|
}
|
|
606
658
|
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { redactSecrets } from "./secret-scan.js";
|
|
3
|
+
|
|
4
|
+
export const POLICY_DECISION_EVENT_VERSION = 1;
|
|
5
|
+
|
|
6
|
+
const MAX_CHAIN = 8;
|
|
7
|
+
|
|
8
|
+
function boundedText(value, max) {
|
|
9
|
+
if (value == null) return null;
|
|
10
|
+
let text;
|
|
11
|
+
if (typeof value === "string") {
|
|
12
|
+
text = value;
|
|
13
|
+
} else {
|
|
14
|
+
try {
|
|
15
|
+
text = JSON.stringify(value);
|
|
16
|
+
} catch {
|
|
17
|
+
text = String(value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const redacted = redactSecrets(String(text));
|
|
21
|
+
return redacted.length > max
|
|
22
|
+
? `${redacted.slice(0, Math.max(0, max - 1))}…`
|
|
23
|
+
: redacted;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function stableValue(value) {
|
|
27
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
28
|
+
if (!value || typeof value !== "object") return value;
|
|
29
|
+
return Object.fromEntries(
|
|
30
|
+
Object.keys(value)
|
|
31
|
+
.filter((key) => value[key] !== undefined)
|
|
32
|
+
.sort()
|
|
33
|
+
.map((key) => [key, stableValue(value[key])]),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function digest(value) {
|
|
38
|
+
return `sha256:${createHash("sha256")
|
|
39
|
+
.update(JSON.stringify(stableValue(value)))
|
|
40
|
+
.digest("hex")}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function canonicalDecision(
|
|
44
|
+
value,
|
|
45
|
+
{ blocked = false, requiresApproval = false } = {},
|
|
46
|
+
) {
|
|
47
|
+
const normalized = String(value || "")
|
|
48
|
+
.trim()
|
|
49
|
+
.toLowerCase();
|
|
50
|
+
if (
|
|
51
|
+
blocked ||
|
|
52
|
+
["block", "blocked", "deny", "denied", "decline", "cancel"].includes(
|
|
53
|
+
normalized,
|
|
54
|
+
)
|
|
55
|
+
) {
|
|
56
|
+
return "deny";
|
|
57
|
+
}
|
|
58
|
+
if (
|
|
59
|
+
requiresApproval ||
|
|
60
|
+
["ask", "confirm", "approval_required", "require_confirmation"].includes(
|
|
61
|
+
normalized,
|
|
62
|
+
)
|
|
63
|
+
) {
|
|
64
|
+
return "ask";
|
|
65
|
+
}
|
|
66
|
+
return "allow";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalizeChain(value) {
|
|
70
|
+
if (!Array.isArray(value)) return [];
|
|
71
|
+
return value.slice(0, MAX_CHAIN).map((candidate) => {
|
|
72
|
+
const row =
|
|
73
|
+
candidate && typeof candidate === "object" && !Array.isArray(candidate)
|
|
74
|
+
? candidate
|
|
75
|
+
: {};
|
|
76
|
+
return {
|
|
77
|
+
layer: boundedText(row.layer, 80),
|
|
78
|
+
outcome: boundedText(row.outcome, 80),
|
|
79
|
+
via: boundedText(row.via || row.source, 120),
|
|
80
|
+
rule: boundedText(row.rule, 256),
|
|
81
|
+
reason: boundedText(row.reason, 512),
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function finalizePolicyDecision(value) {
|
|
87
|
+
const projection = Object.fromEntries(
|
|
88
|
+
Object.entries(value).filter(([, entry]) => entry !== undefined),
|
|
89
|
+
);
|
|
90
|
+
const policyDigest = digest(projection);
|
|
91
|
+
return Object.freeze({
|
|
92
|
+
type: "policy_decision",
|
|
93
|
+
schema_version: POLICY_DECISION_EVENT_VERSION,
|
|
94
|
+
decision_id:
|
|
95
|
+
boundedText(projection.decision_id, 160) ||
|
|
96
|
+
`policy:${policyDigest.slice("sha256:".length, "sha256:".length + 48)}`,
|
|
97
|
+
...projection,
|
|
98
|
+
policy_digest: policyDigest,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function projectHookPolicyDecision(event = {}) {
|
|
103
|
+
if (event?.type !== "hook_response") return null;
|
|
104
|
+
return finalizePolicyDecision({
|
|
105
|
+
source: "hook",
|
|
106
|
+
decision: canonicalDecision(event.decision, {
|
|
107
|
+
blocked: event.blocked === true || Boolean(event.error),
|
|
108
|
+
requiresApproval: event.requires_approval === true,
|
|
109
|
+
}),
|
|
110
|
+
session_id: boundedText(event.session_id, 160) || undefined,
|
|
111
|
+
turn_id: boundedText(event.turn_id, 160) || undefined,
|
|
112
|
+
tool_use_id: boundedText(event.tool_use_id, 160) || undefined,
|
|
113
|
+
tool: boundedText(event.tool || event.tool_name, 256) || undefined,
|
|
114
|
+
hook_event: boundedText(event.hook_event, 96) || undefined,
|
|
115
|
+
via: boundedText(
|
|
116
|
+
event.via || (event.hook_event ? `hook:${event.hook_event}` : "hook"),
|
|
117
|
+
120,
|
|
118
|
+
),
|
|
119
|
+
reason: event.error ? "hook_runtime_error" : undefined,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function projectToolPolicyDecision(event = {}, context = {}) {
|
|
124
|
+
const permission =
|
|
125
|
+
event?.permission_decision &&
|
|
126
|
+
typeof event.permission_decision === "object" &&
|
|
127
|
+
!Array.isArray(event.permission_decision)
|
|
128
|
+
? event.permission_decision
|
|
129
|
+
: null;
|
|
130
|
+
if (!permission) return null;
|
|
131
|
+
return finalizePolicyDecision({
|
|
132
|
+
decision_id:
|
|
133
|
+
boundedText(permission.id || event.permission_decision_id, 160) ||
|
|
134
|
+
undefined,
|
|
135
|
+
source: "tool",
|
|
136
|
+
decision: canonicalDecision(permission.decision),
|
|
137
|
+
session_id:
|
|
138
|
+
boundedText(context.sessionId || event.session_id, 160) || undefined,
|
|
139
|
+
turn_id: boundedText(context.turnId || event.turn_id, 160) || undefined,
|
|
140
|
+
tool_use_id:
|
|
141
|
+
boundedText(context.toolUseId || event.tool_use_id || event.id, 160) ||
|
|
142
|
+
undefined,
|
|
143
|
+
tool: boundedText(event.tool || permission.tool, 256) || undefined,
|
|
144
|
+
via: boundedText(permission.via, 120) || undefined,
|
|
145
|
+
rule: boundedText(permission.rule, 256) || undefined,
|
|
146
|
+
reason: boundedText(permission.reason, 512) || undefined,
|
|
147
|
+
chain: normalizeChain(permission.chain),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
@@ -2208,7 +2208,7 @@ function cleanupWindowsTemporaryPath(
|
|
|
2208
2208
|
}
|
|
2209
2209
|
if (
|
|
2210
2210
|
!expectedIdentity ||
|
|
2211
|
-
!
|
|
2211
|
+
!sameWindowsFileObjectIdentity(inspected.identity, expectedIdentity)
|
|
2212
2212
|
) {
|
|
2213
2213
|
return false;
|
|
2214
2214
|
}
|
|
@@ -2719,6 +2719,20 @@ function sameWindowsFileIdentity(left, right) {
|
|
|
2719
2719
|
);
|
|
2720
2720
|
}
|
|
2721
2721
|
|
|
2722
|
+
// Cleanup is authorized by the stable file-object identity, not by metadata
|
|
2723
|
+
// that Windows may settle after the helper closes its last write handle. The
|
|
2724
|
+
// strict identity above remains authoritative while content is read or
|
|
2725
|
+
// attested; this cleanup-only comparison still rejects a same-path replacement
|
|
2726
|
+
// because that changes the inode/birth identity.
|
|
2727
|
+
function sameWindowsFileObjectIdentity(left, right) {
|
|
2728
|
+
return (
|
|
2729
|
+
left.dev === right.dev &&
|
|
2730
|
+
left.ino === right.ino &&
|
|
2731
|
+
left.mode === right.mode &&
|
|
2732
|
+
left.birthtimeNs === right.birthtimeNs
|
|
2733
|
+
);
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2722
2736
|
function inspectWindowsAdapterAssembly(runtime, assemblyPath) {
|
|
2723
2737
|
const boundBefore = inspectWindowsAdapterBoundPath(
|
|
2724
2738
|
runtime,
|