chainlesschain 0.166.89 → 0.166.91

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.
Files changed (181) hide show
  1. package/README.md +91 -5
  2. package/package.json +3 -3
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-C8AkN9R5.js → AIOps-DgV7xb8F.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-BrAzSQor.js → ActionButton-CgANpkKu.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-Cu56X8Oh.js → Analytics-DnvpX7RB.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-BP1Riofl.js → AppLayout-C2bHccBM.js} +4 -4
  9. package/src/assets/web-panel/assets/{Artifacts-CIW-nNAz.js → Artifacts-BIhijC9g.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-CJtDabk-.js → Audit-CKRjWGdp.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-DYIHeQ_P.js → BackgroundAgents-CblYtgBE.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-B4CsFzj7.js → Backup-Cq35b78a.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-3eoJ5kfb.js → BaseInput-DcB7NoRz.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-BBhX2L_E.js → Chat-tc3b-s7i.js} +5 -5
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-RN8cbv7w.js → ChatBubbleRenderer-fcc_Tvqu.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-5UvmwHVk.js → Checkbox-BLD5bH5u.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-BFy8zsBN.js → Codegen-BLOsoazN.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-4M6bKRRO.js → Col-Bdr7JwIx.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-D6LC0j7_.js → Community-C84BsWE0.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-Dd48xMcv.js → Compact-Dzdwd04z.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-Ba85ggmy.js → Compliance-CCG-R0EV.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-D1BXlkuK.js → Cowork-BYj72SK0.js} +3 -3
  23. package/src/assets/web-panel/assets/{Cron-Ca2Uy7Ia.js → Cron-B6iKAwOf.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-C2bDRXZg.js → Crosschain-BJ-oDC6u.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-DySZKAJf.js → DID-BnMckGLr.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-DDpYf-TT.js → Dashboard-Cyje-gCO.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-DGjROfPv.js → Dropdown-BpNlenjK.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-DxX9QYiB.js → EmailListRenderer-BhonO7Nv.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-TBCZyrSJ.js → EvolutionSettings-DVoNU-Id.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BeqwTXBa.js → FamilyGuardDashboard-tdn-KC67.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-CiCpKJQ1.js → Federation--EEVrHhz.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-CdTmbLkU.js → FormItemContext-Cy8zr0-4.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-BwbkEY-5.js → GenericCardRenderer-BuUhpgFi.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-VnAga2vv.js → Git-Ctd3E5Pe.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-i-4Ebs-H.js → Governance-Ce7dV6aU.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-CirjTgNw.js → Inference-CD1vExIH.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-CunS7vhA.js → KnowledgeGraph-Dn64OdQ7.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-DBuTF0c6.js → Logs-PmElVRrT.js} +2 -2
  39. package/src/assets/web-panel/assets/{MarkdownRenderer-B-iE-tt_.js → MarkdownRenderer-BFiVKf5t.js} +1 -1
  40. package/src/assets/web-panel/assets/{Marketplace-BK6ZivTS.js → Marketplace-OW4jTXwx.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-CUnCRnd3.js → McpTools-BEq2Ib4x.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-CBGGr8Gy.js → Memory-Can_P2oK.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-reB6iaWD.js → MobileBridge-DJ8R-dn3.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-o1YdVB7i.js → MobileProjects-B7kV9GOt.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-if5OE3GH.js → Mtc-BfxamolS.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-BTZWpq0j.js → MtcAudit-DHtuv7av.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-8cvMAYFL.js → Multisig-DLkLpvxc.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-BMvUXAWo.js → NLProgramming-u4XtJTIt.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-BWWjColh.js → Notes-BwuuPJLS.js} +3 -3
  50. package/src/assets/web-panel/assets/{NotificationSettings-Ccr4lIhS.js → NotificationSettings-HEeu-E9v.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-DhksGy6D.js → OrderTableRenderer-BtvN-Qwh.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-DciXQfdY.js → Organization-Yh-czs3b.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-8DR7uMSx.js → Overflow-DmE8mYVM.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-DE6euHYl.js → P2P-CWVck8WN.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-ByuWoauq.js → PdhVaultBrowser-DgFqekod.js} +4 -4
  56. package/src/assets/web-panel/assets/{Permissions-DkjM87ac.js → Permissions-BtJBVOHV.js} +3 -3
  57. package/src/assets/web-panel/assets/{PersonalDataHub-2DLp0KTL.js → PersonalDataHub-6VBObXzd.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-Ci2Qlkfr.js → Pipeline-D222FmqY.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-DlwDnDVA.js → Privacy-Bi9FMikp.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-CmMifW39.js → ProjectInit-Bo52H47H.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-DJVFIh-6.js → ProjectSettings-BPkesfOc.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-P2nWBS70.js → Projects-GynohSFX.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-B2cxaGCU.js → Providers-DVdMg04n.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-CpO77kcE.js → QrScannerModal-DqJCb595.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-BDQl1hb0.js → QuickAsk-J7loQbXi.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-DTD2f3IN.js → Recommend-BI5aURVc.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-DGZAnwvs.js → RemoteSession-BN2sqGvz.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-B_R4vitb.js → Reputation-BGOND-Fi.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-4Jj6JyXN.js → Row-77dfwaUk.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-D1t1MCZ-.js → RssFeed-FtBAxS3g.js} +2 -2
  71. package/src/assets/web-panel/assets/{Search-CyOUlIn7.js → Search-BaYVLFb7.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-BmknPT5Q.js → Security-LE3Vv6ab.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-DkEK8mpa.js → Services-C5pP-Whz.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-Bvo5bpuk.js → Skeleton-CLqKUVsF.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-AMmG8fim.js → Skills-BPjN0SX5.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-Dl7-OKOV.js → Sla-14jD70kg.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-DtiGKu0E.js → SpeechSettings-Dr9ij42W.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-Ch7mp3ES.js → SyncSettings-BzLHP7B8.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-Ck-sEgj-.js → Tasks-YqV9b0rV.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-BlV22Il8.js → Templates-CxxAE4PK.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-CaaAu0mX.js → Tenant-CRb3PcKJ.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-lA9g3FtJ.js → Terminal-CTzPpBaP.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-CG26IFC3.js → TimelineRenderer-DjFUj_hy.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-iaJUBLBm.js → Tokens-Bbb8tPvx.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-DWQJtLK3.js → Trigger-CSeWGM0W.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-CTjmUwqp.js → Trust-JgESMBo3.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-CMnxufgD.js → UkeySign-CiObLk0T.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-diCFSWJw.js → VideoEditing-CFEZaQ2x.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-Ba1oND1K.js → Wallet-DR8ZGDNn.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-DZDqPS-S.js → WebAuthn-DxZukvu7.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-CfN8fPMe.js → WorkflowEditor-CKgSZQwn.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-BiqMsO_i.js → chat-CTxEQE66.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-By8LkjqH.js → colors-CAjKMUu3.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-B0mxvRe3.js → compact-item-BXuz49at.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-Li2ub348.js → createContext-CFN_Hwr9.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-CfB8DxT2.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-DYbHPkbl.js → hasIn-BwoSBSfP.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CVZNYRzM.js → index-B0QbLlWn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CGZzexNa.js → index-B2o_BBjg.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CYv3JIzl.js → index-B2tEnBPx.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-1lVoRUrQ.js → index-B7kp-zRE.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CTVPeUh3.js → index-B9yR7zqr.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BFhksbXp.js → index-BK4ftQ4d.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B0D0HkkM.js → index-Bj4hIiLW.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CEp_3fKJ.js → index-BmQITjMU.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DIImi0cd.js → index-BmXpdcHI.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-D6qL3QBU.js → index-Bqv_Cray.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BSk0xuXP.js → index-Bu2P6u8m.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-zSzXsF9b.js → index-Bw9s2d3c.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-beDji-cB.js → index-C8er4W9u.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C-RE3mFh.js → index-CB3kVd9c.js} +1 -1
  112. package/src/assets/web-panel/assets/index-CCsRfd0k.js +1 -0
  113. package/src/assets/web-panel/assets/{index-CA0AOURT.js → index-CF9lWi1o.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CMh3VxgO.js → index-CN53dE_u.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-KWt8Owo8.js → index-CSmFby3Q.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DB4DCzqU.js → index-CehdwJ_Q.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CHgiSV6R.js → index-Cgk0YEiq.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-b4O_YBVR.js → index-Cp5y7lX_.js} +1 -1
  119. package/src/assets/web-panel/assets/index-CsmiZIQ4.js +1 -0
  120. package/src/assets/web-panel/assets/{index-DAOpBv3C.js → index-DTX9CJPx.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BZqTG7fP.js → index-DViK2bkv.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Bc6bxU24.js → index-DY-gBSm8.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Czt7WAtA.js → index-DjF5BOZx.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-KqfqSdAC.js → index-DmkbAADP.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Bd5znvCV.js → index-DpdDnZX_.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C5Se6Lnv.js → index-Dv7V2kg6.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Cpfn4YbT.js → index-DxunxaTw.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BcOJEgBE.js → index-F2E5RCwz.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CgyrklzQ.js → index-IY9bd5az.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-ClsJN8sW.js → index-IahILygB.js} +3 -3
  131. package/src/assets/web-panel/assets/{index-hw3sCYAz.js → index-T3MTE6jB.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Bm2P2Kgp.js → index-Tz5dheZa.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BRzewWeE.js → index-_ZtCyUdz.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DPRgnazi.js → index-o9DpkZKf.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-ByqFKMWL.js → index-t2Ch_JKn.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-DgMFnBOv.js → index-u9_DM3ZC.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-TgDL_7OY.js → initDefaultProps-BoiGJDoO.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-fHosyV5c.js → motion-5eL4E0pA.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-C_QfW5qg.js → move-DjggDi_1.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-u7ywDVOg.js → mtc-parser-Ce8Vo2T3.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-BEJdUgE9.js → omit-D1yOBaO2.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-DGkQFAJ5.js → pickAttrs-8NLceQe8.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-DUyZXAjt.js → placementArrow-CsIdBRmQ.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-B4xDxO7Q.js → responsiveObserve-GM5NE8En.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-B8WuJtst.js → slide-44yj3u49.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-DLUqy7JO.js → statusUtils-B8tqtWcX.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-BYOJOyqL.js → styleChecker-NhDPBbyB.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-WChWQURv.js → useFlexGapSupport-C8Qz1GtN.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-vLNpm3dv.js → useFs-ClUv4kih.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-DSZ8unoU.js → usePersonalDataHub-BwkOVEh6.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-C4Xf7Kg1.js → vnode--gMF33R0.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-BulmeAGy.js → zoom-DXg3TqOP.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +5 -5
  155. package/src/commands/agent-process-ownership.js +121 -0
  156. package/src/commands/agent.js +2 -0
  157. package/src/commands/automation.js +35 -8
  158. package/src/commands/hub.js +224 -0
  159. package/src/commands/project.js +162 -0
  160. package/src/data/changelog.json +26 -0
  161. package/src/gateways/ws/personal-data-hub-protocol.js +54 -8
  162. package/src/lib/audit-logger.js +59 -46
  163. package/src/lib/audit-sanitizer.js +279 -0
  164. package/src/lib/automation-center.js +14 -4
  165. package/src/lib/automation-engine.js +151 -39
  166. package/src/lib/checkpoint-restore-saga.js +11 -1
  167. package/src/lib/eval/verify01-host-evidence.js +36 -3
  168. package/src/lib/knowledge-graph.js +27 -0
  169. package/src/lib/personal-data-hub-wiring.js +44 -1
  170. package/src/lib/process-execution-broker/index.js +91 -1
  171. package/src/lib/process-execution-broker/platform-sandbox.js +42 -0
  172. package/src/lib/process-execution-broker/windows-native-evaluator-capabilities.js +757 -0
  173. package/src/lib/process-execution-broker/windows-native-evaluator.js +316 -0
  174. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  175. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  176. package/src/lib/process-execution-broker/windows-sandbox.cs +383 -11
  177. package/src/lib/scheduler-kernel/automation-adapter.js +15 -3
  178. package/src/lib/scheduler-kernel/automation-event-adapter.js +15 -3
  179. package/src/assets/web-panel/assets/devWarning-D90arbUg.js +0 -1
  180. package/src/assets/web-panel/assets/index-CeP1oROI.js +0 -1
  181. package/src/assets/web-panel/assets/index-Cu9gkyRc.js +0 -1
@@ -27,6 +27,16 @@ export const EXECUTION_STATUS = Object.freeze({
27
27
  SUCCESS: "success",
28
28
  FAILED: "failed",
29
29
  CANCELLED: "cancelled",
30
+ SIMULATED: "simulated",
31
+ UNSUPPORTED: "unsupported",
32
+ UNVERIFIED: "legacy-unverified",
33
+ });
34
+
35
+ export const AUTOMATION_EXECUTION_CAPABILITY = Object.freeze({
36
+ live: false,
37
+ simulation: true,
38
+ reason:
39
+ "Connector catalog only; live automation execution is not implemented",
30
40
  });
31
41
 
32
42
  export const TRIGGER_TYPE = Object.freeze({
@@ -125,12 +135,22 @@ export const CONNECTOR_CATALOG = Object.freeze([
125
135
  const CONNECTOR_INDEX = new Map(CONNECTOR_CATALOG.map((c) => [c.id, c]));
126
136
 
127
137
  export function listConnectors() {
128
- return CONNECTOR_CATALOG.map((c) => ({ ...c, actions: [...c.actions] }));
138
+ return CONNECTOR_CATALOG.map((c) => ({
139
+ ...c,
140
+ actions: [...c.actions],
141
+ executionCapability: AUTOMATION_EXECUTION_CAPABILITY,
142
+ }));
129
143
  }
130
144
 
131
145
  export function getConnector(id) {
132
146
  const c = CONNECTOR_INDEX.get(id);
133
- return c ? { ...c, actions: [...c.actions] } : null;
147
+ return c
148
+ ? {
149
+ ...c,
150
+ actions: [...c.actions],
151
+ executionCapability: AUTOMATION_EXECUTION_CAPABILITY,
152
+ }
153
+ : null;
134
154
  }
135
155
 
136
156
  /* ── Built-in flow templates ───────────────────────────────── */
@@ -462,14 +482,42 @@ function _rowToFlow(row) {
462
482
 
463
483
  function _rowToExecution(row) {
464
484
  if (!row) return null;
485
+ const outputData = _parseJSON(row.output_data, null);
486
+ const stepsLog = _parseJSON(row.steps_log, []);
487
+ const simulated =
488
+ row.test_mode === 1 ||
489
+ outputData?.simulated === true ||
490
+ stepsLog.some((step) => step.output?.simulated === true);
491
+ const adjudication = _parseJSON(row.error, null);
492
+ const confirmedApplied =
493
+ adjudication?.authority === "scheduler-adjudication" &&
494
+ adjudication?.decision === "confirmed_applied";
495
+ const legacyControlOnly =
496
+ row.status === EXECUTION_STATUS.SUCCESS &&
497
+ !confirmedApplied &&
498
+ stepsLog.every((step) => step.nodeType && step.nodeType !== "action") &&
499
+ (outputData == null ||
500
+ outputData.parallel === true ||
501
+ outputData.loop === true ||
502
+ (typeof outputData.branch === "string" && "expression" in outputData));
503
+ // Old releases persisted simulator results as success. Do not recover those
504
+ // rows as evidence that an external action was applied.
505
+ const status =
506
+ row.status === EXECUTION_STATUS.SUCCESS && simulated
507
+ ? EXECUTION_STATUS.SIMULATED
508
+ : legacyControlOnly
509
+ ? EXECUTION_STATUS.UNVERIFIED
510
+ : row.status;
465
511
  return {
466
512
  id: row.id,
467
513
  flowId: row.flow_id,
468
514
  triggerType: row.trigger_type,
469
515
  inputData: _parseJSON(row.input_data, null),
470
- outputData: _parseJSON(row.output_data, null),
471
- status: row.status,
472
- stepsLog: _parseJSON(row.steps_log, []),
516
+ outputData,
517
+ status,
518
+ ...(status !== row.status ? { recordedStatus: row.status } : {}),
519
+ mode: simulated ? "simulation" : legacyControlOnly ? "unverified" : "live",
520
+ stepsLog,
473
521
  durationMs: row.duration_ms || 0,
474
522
  error: row.error || null,
475
523
  testMode: row.test_mode === 1,
@@ -1161,13 +1209,20 @@ export function executeFlow(db, flowId, options = {}) {
1161
1209
  executionAuthority = null,
1162
1210
  assertRuntimeBoundary = null,
1163
1211
  } = options;
1212
+ if (typeof testMode !== "boolean") {
1213
+ throw new TypeError("testMode must be a boolean");
1214
+ }
1164
1215
  const execId =
1165
1216
  executionId === undefined
1166
1217
  ? _genId("exec")
1167
1218
  : _normalizeExecutionId(executionId);
1168
1219
  const existing = getExecution(db, execId);
1169
1220
  if (existing) {
1170
- return _resolveExistingExecution(existing, { flowId, triggerType });
1221
+ return _resolveExistingExecution(existing, {
1222
+ flowId,
1223
+ triggerType,
1224
+ testMode,
1225
+ });
1171
1226
  }
1172
1227
  // Validate every declared action boundary before creating an execution row
1173
1228
  // or invoking the first effect. The per-node assertion below remains as the
@@ -1210,20 +1265,34 @@ export function executeFlow(db, flowId, options = {}) {
1210
1265
  // the winner from durable evidence instead of running the flow twice.
1211
1266
  const concurrent = getExecution(db, execId);
1212
1267
  if (!concurrent) throw error;
1213
- return _resolveExistingExecution(concurrent, { flowId, triggerType });
1268
+ return _resolveExistingExecution(concurrent, {
1269
+ flowId,
1270
+ triggerType,
1271
+ testMode,
1272
+ });
1214
1273
  }
1215
1274
 
1216
1275
  const stepsLog = [];
1217
- let finalStatus = EXECUTION_STATUS.SUCCESS;
1276
+ let finalStatus = testMode
1277
+ ? EXECUTION_STATUS.SIMULATED
1278
+ : EXECUTION_STATUS.UNSUPPORTED;
1218
1279
  let finalError = null;
1219
1280
  let outputData = null;
1220
1281
 
1221
1282
  try {
1222
1283
  const ordered = _topoOrder(flow.nodes, flow.edges);
1284
+ if (!testMode) {
1285
+ finalError = AUTOMATION_EXECUTION_CAPABILITY.reason;
1286
+ outputData = {
1287
+ executed: false,
1288
+ code: "AUTOMATION_EXECUTION_UNSUPPORTED",
1289
+ capability: AUTOMATION_EXECUTION_CAPABILITY,
1290
+ };
1291
+ }
1223
1292
  const stepInputs = new Map();
1224
1293
  stepInputs.set("__initial__", inputData);
1225
1294
 
1226
- for (const node of ordered) {
1295
+ for (const node of testMode ? ordered : []) {
1227
1296
  const stepStart = Date.now();
1228
1297
  const parentEdges = flow.edges.filter((e) => e.to === node.id);
1229
1298
  let merged;
@@ -1251,7 +1320,7 @@ export function executeFlow(db, flowId, options = {}) {
1251
1320
  nodeType: node.type || "action",
1252
1321
  connector: node.connector || null,
1253
1322
  action: node.action || null,
1254
- status: "success",
1323
+ status: EXECUTION_STATUS.SIMULATED,
1255
1324
  durationMs: Date.now() - stepStart,
1256
1325
  output,
1257
1326
  });
@@ -1389,8 +1458,15 @@ function _normalizeExecutionId(value) {
1389
1458
  return value;
1390
1459
  }
1391
1460
 
1392
- function _resolveExistingExecution(existing, { flowId, triggerType }) {
1393
- if (existing.flowId !== flowId || existing.triggerType !== triggerType) {
1461
+ function _resolveExistingExecution(
1462
+ existing,
1463
+ { flowId, triggerType, testMode },
1464
+ ) {
1465
+ if (
1466
+ existing.flowId !== flowId ||
1467
+ existing.triggerType !== triggerType ||
1468
+ existing.testMode !== testMode
1469
+ ) {
1394
1470
  const error = new Error(
1395
1471
  `Execution id is already bound to another automation request: ${existing.id}`,
1396
1472
  );
@@ -1407,7 +1483,7 @@ function _resolveExistingExecution(existing, { flowId, triggerType }) {
1407
1483
  return existing;
1408
1484
  }
1409
1485
 
1410
- export function fireTrigger(db, triggerId, inputData = {}) {
1486
+ export function fireTrigger(db, triggerId, inputData = {}, options = {}) {
1411
1487
  const trig = getTrigger(db, triggerId);
1412
1488
  if (!trig) throw new Error(`Trigger not found: ${triggerId}`);
1413
1489
  if (!trig.enabled) throw new Error(`Trigger disabled: ${triggerId}`);
@@ -1434,6 +1510,7 @@ export function fireTrigger(db, triggerId, inputData = {}) {
1434
1510
  return executeFlow(db, trig.flowId, {
1435
1511
  inputData,
1436
1512
  triggerType: trig.type,
1513
+ testMode: options.testMode ?? false,
1437
1514
  });
1438
1515
  }
1439
1516
 
@@ -1445,31 +1522,64 @@ export function getExecution(db, execId) {
1445
1522
 
1446
1523
  export function listExecutions(db, filters = {}) {
1447
1524
  const { flowId, status, limit = 50 } = filters;
1448
- let rows;
1449
- if (flowId && status) {
1450
- rows = db
1451
- .prepare(
1452
- `SELECT * FROM auto_executions WHERE flow_id = ? AND status = ? ORDER BY started_at DESC LIMIT ?`,
1453
- )
1454
- .all(flowId, status, limit);
1455
- } else if (flowId) {
1456
- rows = db
1457
- .prepare(
1458
- `SELECT * FROM auto_executions WHERE flow_id = ? ORDER BY started_at DESC LIMIT ?`,
1459
- )
1460
- .all(flowId, limit);
1461
- } else if (status) {
1462
- rows = db
1525
+ if (!Number.isSafeInteger(limit) || limit < 0) {
1526
+ throw new RangeError(
1527
+ "Execution history limit must be a non-negative integer",
1528
+ );
1529
+ }
1530
+ if (limit === 0) return [];
1531
+
1532
+ const clauses = [];
1533
+ const params = [];
1534
+ if (flowId) {
1535
+ clauses.push("flow_id = ?");
1536
+ params.push(flowId);
1537
+ }
1538
+ const needsLegacyProjection = [
1539
+ EXECUTION_STATUS.SUCCESS,
1540
+ EXECUTION_STATUS.SIMULATED,
1541
+ EXECUTION_STATUS.UNVERIFIED,
1542
+ ].includes(status);
1543
+ if (status) {
1544
+ if (needsLegacyProjection && status !== EXECUTION_STATUS.SUCCESS) {
1545
+ clauses.push("status IN (?, ?)");
1546
+ params.push(status, EXECUTION_STATUS.SUCCESS);
1547
+ } else {
1548
+ clauses.push("status = ?");
1549
+ params.push(status);
1550
+ }
1551
+ }
1552
+ const where = clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
1553
+ if (!needsLegacyProjection) {
1554
+ return db
1463
1555
  .prepare(
1464
- `SELECT * FROM auto_executions WHERE status = ? ORDER BY started_at DESC LIMIT ?`,
1556
+ `SELECT * FROM auto_executions${where} ORDER BY started_at DESC LIMIT ?`,
1465
1557
  )
1466
- .all(status, limit);
1467
- } else {
1468
- rows = db
1469
- .prepare(`SELECT * FROM auto_executions ORDER BY started_at DESC LIMIT ?`)
1470
- .all(limit);
1558
+ .all(...params, limit)
1559
+ .map(_rowToExecution);
1560
+ }
1561
+
1562
+ // A stored success can project to simulated/unverified. Scan only candidate
1563
+ // statuses in bounded pages and stop once the caller's normalized limit is
1564
+ // met, instead of loading all execution payloads into memory. The id tie
1565
+ // breaker keeps page boundaries stable when timestamps are identical.
1566
+ const pageSize = 128;
1567
+ const statement = db.prepare(
1568
+ `SELECT * FROM auto_executions${where} ORDER BY started_at DESC, id DESC LIMIT ? OFFSET ?`,
1569
+ );
1570
+ const executions = [];
1571
+ let offset = 0;
1572
+ while (executions.length < limit) {
1573
+ const rows = statement.all(...params, pageSize, offset);
1574
+ for (const row of rows) {
1575
+ const execution = _rowToExecution(row);
1576
+ if (execution.status === status) executions.push(execution);
1577
+ if (executions.length === limit) return executions;
1578
+ }
1579
+ if (rows.length < pageSize) break;
1580
+ offset += rows.length;
1471
1581
  }
1472
- return rows.map(_rowToExecution);
1582
+ return executions;
1473
1583
  }
1474
1584
 
1475
1585
  /* ── Stats ─────────────────────────────────────────────────── */
@@ -1477,7 +1587,7 @@ export function listExecutions(db, filters = {}) {
1477
1587
  export function getStats(db) {
1478
1588
  const flowsByStatus = {};
1479
1589
  for (const status of Object.values(FLOW_STATUS)) flowsByStatus[status] = 0;
1480
- for (const row of db.data?.get("auto_flows") || []) {
1590
+ for (const row of db.prepare("SELECT * FROM auto_flows").all()) {
1481
1591
  flowsByStatus[row.status] = (flowsByStatus[row.status] || 0) + 1;
1482
1592
  }
1483
1593
 
@@ -1486,15 +1596,16 @@ export function getStats(db) {
1486
1596
  execByStatus[status] = 0;
1487
1597
  let totalDuration = 0;
1488
1598
  let execCount = 0;
1489
- for (const row of db.data?.get("auto_executions") || []) {
1490
- execByStatus[row.status] = (execByStatus[row.status] || 0) + 1;
1599
+ for (const row of db.prepare("SELECT * FROM auto_executions").all()) {
1600
+ const { status } = _rowToExecution(row);
1601
+ execByStatus[status] = (execByStatus[status] || 0) + 1;
1491
1602
  totalDuration += row.duration_ms || 0;
1492
1603
  execCount++;
1493
1604
  }
1494
1605
 
1495
1606
  const triggersByType = {};
1496
1607
  for (const type of Object.values(TRIGGER_TYPE)) triggersByType[type] = 0;
1497
- for (const row of db.data?.get("auto_triggers") || []) {
1608
+ for (const row of db.prepare("SELECT * FROM auto_triggers").all()) {
1498
1609
  triggersByType[row.type] = (triggersByType[row.type] || 0) + 1;
1499
1610
  }
1500
1611
 
@@ -1528,6 +1639,7 @@ export function getStats(db) {
1528
1639
 
1529
1640
  export function getConfig() {
1530
1641
  return {
1642
+ executionCapability: AUTOMATION_EXECUTION_CAPABILITY,
1531
1643
  connectors: CONNECTOR_CATALOG.length,
1532
1644
  templates: FLOW_TEMPLATES.length,
1533
1645
  flowStatuses: Object.values(FLOW_STATUS),
@@ -464,6 +464,16 @@ function isMissingPathError(error) {
464
464
  return error?.code === "ENOENT" || error?.cause?.code === "ENOENT";
465
465
  }
466
466
 
467
+ function hasMissingLockPathCause(error) {
468
+ // A bounded owner read wraps secure-parent errors, which themselves wrap
469
+ // ENOENT when a cooperating process detaches its released lock directory.
470
+ // Only unlocked lock inspection uses this; losing our held owner still fails.
471
+ for (let depth = 0; error && depth < 8; depth++, error = error.cause) {
472
+ if (error.code === "ENOENT") return true;
473
+ }
474
+ return false;
475
+ }
476
+
467
477
  function attachCommitState(error, details) {
468
478
  if (!error || typeof error !== "object") return error;
469
479
  for (const [key, value] of Object.entries(details)) {
@@ -1887,7 +1897,7 @@ export class CheckpointRestoreSagaStore {
1887
1897
  // The same valid release can happen after readdir observed owner.json
1888
1898
  // but before its lstat/read. Only tolerate absence during the unlocked
1889
1899
  // pre/postflight inspection; losing our own owner still fails closed.
1890
- if (isMissingPathError(cause) && !requireOwner) return null;
1900
+ if (hasMissingLockPathCause(cause) && !requireOwner) return null;
1891
1901
  throw sagaError(
1892
1902
  CHECKPOINT_RESTORE_SAGA_ERROR_CODES.LOCK_FAILED,
1893
1903
  "Saga state lock owner is corrupt or unbounded",
@@ -149,13 +149,46 @@ export function inspectIdeProtocol(records, { prompt, comparison } = {}) {
149
149
  resultRecords[0].event.session_id === sessionId,
150
150
  "IDE terminal is missing or ambiguous",
151
151
  );
152
+ const terminal = resultRecords[0].event;
153
+ // Persistent headless streams emit one lifecycle end after stdin closes.
154
+ // Accept only that exact, same-session one-turn trailer; task output after
155
+ // the result stays ambiguous. Keep the complete capture intact.
156
+ const trailing = outputs.slice(outputs.indexOf(resultRecords[0]) + 1);
157
+ const end = trailing[0]?.event;
158
+ const lifecycleFrames = outputs.filter(
159
+ (record) =>
160
+ record.event.type === "system" && record.event.subtype === "end",
161
+ );
162
+ const lifecycleEnded =
163
+ trailing.length === 1 &&
164
+ lifecycleFrames.length === 1 &&
165
+ end.type === "system" &&
166
+ end.subtype === "end" &&
167
+ end.session_id === sessionId &&
168
+ end.turns === 1 &&
169
+ terminal.turn === 1 &&
170
+ Object.keys(end).every((key) =>
171
+ ["type", "subtype", "session_id", "turns", "trace_id", "seq"].includes(
172
+ key,
173
+ ),
174
+ ) &&
175
+ (end.trace_id === undefined ||
176
+ (typeof end.trace_id === "string" &&
177
+ end.trace_id === terminal.trace_id)) &&
178
+ (end.seq === undefined ||
179
+ (Number.isSafeInteger(end.seq) &&
180
+ Number.isSafeInteger(terminal.seq) &&
181
+ end.seq === terminal.seq + 1));
182
+ const taskOutputs = lifecycleEnded ? outputs.slice(0, -1) : outputs;
152
183
  const parsed = verifyAgentTerminal(
153
- outputs.map((record) => JSON.stringify(record.event)).join("\n"),
184
+ taskOutputs.map((record) => JSON.stringify(record.event)).join("\n"),
154
185
  );
155
- const terminal = resultRecords[0].event;
186
+ if (lifecycleFrames.length > 0 && !lifecycleEnded)
187
+ parsed.terminalVerified = false;
156
188
  // A complete explicit failure is an observation. It never becomes success.
157
189
  const knownFailure =
158
- outputs.at(-1) === resultRecords[0] &&
190
+ taskOutputs.at(-1) === resultRecords[0] &&
191
+ (lifecycleFrames.length === 0 || lifecycleEnded) &&
159
192
  terminal.is_error === true &&
160
193
  typeof terminal.subtype === "string" &&
161
194
  terminal.subtype.length > 0 &&
@@ -239,6 +239,33 @@ export function getEntity(id) {
239
239
  return e ? _strip(e) : null;
240
240
  }
241
241
 
242
+ /** Replace supplied entity fields while preserving identity and relationships. */
243
+ export function updateEntity(db, id, config = {}) {
244
+ const current = _entities.get(id);
245
+ if (!current) throw new Error(`Entity not found: ${id}`);
246
+ const name = String(config.name ?? current.name).trim();
247
+ const type = String(config.type ?? current.type).trim();
248
+ if (!name) throw new Error("entity name is required");
249
+ if (!type) throw new Error("entity type is required");
250
+ const entity = {
251
+ ...current,
252
+ name,
253
+ type,
254
+ properties: Object.hasOwn(config, "properties")
255
+ ? config.properties
256
+ : current.properties,
257
+ tags: Object.hasOwn(config, "tags")
258
+ ? Array.isArray(config.tags)
259
+ ? [...config.tags]
260
+ : null
261
+ : current.tags,
262
+ updatedAt: Number(config.now ?? Date.now()),
263
+ };
264
+ _persistEntity(db, entity);
265
+ _entities.set(id, entity);
266
+ return _strip(entity);
267
+ }
268
+
242
269
  export function listEntities(opts = {}) {
243
270
  let rows = [..._entities.values()];
244
271
  if (opts.type) {
@@ -368,6 +368,17 @@ async function loadBm25() {
368
368
  let _hub = null;
369
369
  let _initPromise = null;
370
370
  let _bm25 = null;
371
+ let _processExitHook = null;
372
+
373
+ function installProcessExitHook() {
374
+ if (_processExitHook) return;
375
+ _processExitHook = (code) => {
376
+ // exit callbacks are synchronous. Nonzero exits keep the generation active;
377
+ // exit(0) still has to pass the registry's in-flight operation guard.
378
+ close({ retireConsumer: code === 0 });
379
+ };
380
+ process.once("exit", _processExitHook);
381
+ }
371
382
 
372
383
  // LLM override — see file header. Default cli-side hub uses a direct
373
384
  // OllamaClient; when the desktop main process embeds this wiring via
@@ -453,6 +464,11 @@ async function initHub() {
453
464
  kgSink = new CcKgSink({
454
465
  addEntity: kgMod.addEntity,
455
466
  addRelation: kgMod.addRelation,
467
+ updateEntity: kgMod.updateEntity,
468
+ getEntity: kgMod.getEntity,
469
+ removeEntity: kgMod.removeEntity,
470
+ listRelations: kgMod.listRelations,
471
+ removeRelation: kgMod.removeRelation,
456
472
  db: null,
457
473
  });
458
474
  }
@@ -550,6 +566,8 @@ async function initHub() {
550
566
  vault,
551
567
  kgSink: kgSink ? kgSink.write.bind(kgSink) : null,
552
568
  ragSink: ragSink ? ragSink.write.bind(ragSink) : null,
569
+ kgRemove: kgSink?.remove?.bind(kgSink) || null,
570
+ ragRemove: ragSink?.remove?.bind(ragSink) || null,
553
571
  entityResolver,
554
572
  adbReadiness: {
555
573
  probe: adbReadinessProbe,
@@ -557,6 +575,20 @@ async function initHub() {
557
575
  },
558
576
  });
559
577
 
578
+ // Both projections are in memory. A fresh registry consumer must rebuild
579
+ // from durable desired state before serving retrieval; leave failures
580
+ // visible and explicitly retryable instead of blocking startup forever.
581
+ if (typeof registry.retryDerivations === "function") {
582
+ for (let page = 0; page < 100; page += 1) {
583
+ const result = await registry.retryDerivations({ limit: 1000 });
584
+ if (!result.remaining) break;
585
+ // A referenced KG endpoint can occur on a later page. Visit fresh
586
+ // work before concluding that a failed dependency is unrecoverable.
587
+ if (result.summary?.pending > 0) continue;
588
+ if (!result.succeeded || result.unsupported || result.blocked) break;
589
+ }
590
+ }
591
+
560
592
  const engine = new AnalysisEngine({
561
593
  vault,
562
594
  llm,
@@ -2106,6 +2138,7 @@ export async function getHub() {
2106
2138
  _initPromise = initHub()
2107
2139
  .then((h) => {
2108
2140
  _hub = h;
2141
+ installProcessExitHook();
2109
2142
  return h;
2110
2143
  })
2111
2144
  .catch((err) => {
@@ -2226,6 +2259,7 @@ export async function getHubMinimal() {
2226
2259
  _hubMinimalInitPromise = initHubMinimal()
2227
2260
  .then((h) => {
2228
2261
  _hubMinimal = h;
2262
+ installProcessExitHook();
2229
2263
  return h;
2230
2264
  })
2231
2265
  .catch((err) => {
@@ -2236,7 +2270,11 @@ export async function getHubMinimal() {
2236
2270
  return _hubMinimalInitPromise;
2237
2271
  }
2238
2272
 
2239
- export function close() {
2273
+ export function close({ retireConsumer = true } = {}) {
2274
+ if (_processExitHook) {
2275
+ process.removeListener("exit", _processExitHook);
2276
+ _processExitHook = null;
2277
+ }
2240
2278
  if (_hub && _hub.aichatHealthChecker) {
2241
2279
  try {
2242
2280
  _hub.aichatHealthChecker.stop();
@@ -2245,6 +2283,11 @@ export function close() {
2245
2283
  }
2246
2284
  }
2247
2285
  if (_hub && _hub.vault) {
2286
+ try {
2287
+ if (retireConsumer) _hub.registry?.retireDerivationConsumer?.();
2288
+ } catch {
2289
+ // Busy/unknown generations retain their receipts for explicit recovery.
2290
+ }
2248
2291
  try {
2249
2292
  _hub.vault.close();
2250
2293
  } catch {
@@ -46,6 +46,7 @@ import {
46
46
  SANDBOX_BOUNDARIES,
47
47
  } from "./platform-sandbox.js";
48
48
  import { normalizeLinuxCgroupPolicy } from "./linux-cgroup-v2.js";
49
+ import { consumeWindowsNativeEvaluatorPolicy } from "./windows-native-evaluator.js";
49
50
  import { acquireLinuxSubreaperHelper } from "./linux-subreaper-helper.js";
50
51
  import { spawnLinuxSubreaperChild } from "./linux-subreaper-process.js";
51
52
  import {
@@ -1625,7 +1626,49 @@ class ProcessExecutionBroker extends EventEmitter {
1625
1626
  requiredBoundaries,
1626
1627
  launch,
1627
1628
  );
1628
- return { profile, requiredBoundaries, linuxCgroup, executionContract };
1629
+ const wallTimeMs = rawPolicy?.limits?.wallTimeMs;
1630
+ if (wallTimeMs !== undefined) {
1631
+ if (
1632
+ !Number.isSafeInteger(wallTimeMs) ||
1633
+ wallTimeMs < 1 ||
1634
+ wallTimeMs > 3600000
1635
+ ) {
1636
+ throw this._sandboxBoundaryError(
1637
+ "invalid_wall_time_limit",
1638
+ "Native wall-time limit must be an integer from 1 to 3600000 milliseconds",
1639
+ );
1640
+ }
1641
+ if (!requiredBoundaries.includes(SANDBOX_BOUNDARIES.RESOURCE_LIMITS)) {
1642
+ requiredBoundaries.push(SANDBOX_BOUNDARIES.RESOURCE_LIMITS);
1643
+ }
1644
+ }
1645
+ const windowsNativeEvaluator =
1646
+ rawPolicy?.windowsNativeEvaluator === undefined
1647
+ ? null
1648
+ : consumeWindowsNativeEvaluatorPolicy(
1649
+ rawPolicy.windowsNativeEvaluator,
1650
+ launch,
1651
+ options,
1652
+ );
1653
+ if (windowsNativeEvaluator) {
1654
+ for (const boundary of [
1655
+ SANDBOX_BOUNDARIES.FILESYSTEM,
1656
+ SANDBOX_BOUNDARIES.NETWORK,
1657
+ SANDBOX_BOUNDARIES.PROCESS_TREE,
1658
+ SANDBOX_BOUNDARIES.RESOURCE_LIMITS,
1659
+ ]) {
1660
+ if (!requiredBoundaries.includes(boundary))
1661
+ requiredBoundaries.push(boundary);
1662
+ }
1663
+ }
1664
+ return {
1665
+ profile,
1666
+ requiredBoundaries,
1667
+ linuxCgroup,
1668
+ executionContract,
1669
+ ...(windowsNativeEvaluator ? { windowsNativeEvaluator } : {}),
1670
+ ...(wallTimeMs !== undefined ? { wallTimeMs } : {}),
1671
+ };
1629
1672
  }
1630
1673
 
1631
1674
  _stripSandboxControlOptions(options) {
@@ -3660,6 +3703,12 @@ class ProcessExecutionBroker extends EventEmitter {
3660
3703
  ...(sandboxPolicy.executionContract
3661
3704
  ? { executionContract: sandboxPolicy.executionContract }
3662
3705
  : {}),
3706
+ ...(sandboxPolicy.wallTimeMs !== undefined
3707
+ ? { limits: Object.freeze({ wallTimeMs: sandboxPolicy.wallTimeMs }) }
3708
+ : {}),
3709
+ ...(sandboxPolicy.windowsNativeEvaluator
3710
+ ? { windowsNativeEvaluator: sandboxPolicy.windowsNativeEvaluator }
3711
+ : {}),
3663
3712
  });
3664
3713
  // Keep the legacy string profile in argument four. The built-in adapter
3665
3714
  // reserves argument five for runtime injection, so the typed request is
@@ -3667,6 +3716,12 @@ class ProcessExecutionBroker extends EventEmitter {
3667
3716
  const sandboxAdapter = this._sandboxAdapter;
3668
3717
  const applySandboxAdapter = sandboxAdapter.applySandbox;
3669
3718
  const builtInSandboxAdapter = applySandboxAdapter === _applySandbox;
3719
+ if (sandboxPolicy.wallTimeMs !== undefined && !builtInSandboxAdapter) {
3720
+ throw this._sandboxBoundaryError(
3721
+ "native_wall_time_adapter_unattested",
3722
+ "Native wall-time enforcement requires the built-in Windows supervisor",
3723
+ );
3724
+ }
3670
3725
  // Never dispatch the built-in through a mutable Function property or a
3671
3726
  // global reflective helper after consulting an injected adapter getter.
3672
3727
  // The lexical import is the identity we checked and the implementation we
@@ -3690,6 +3745,41 @@ class ProcessExecutionBroker extends EventEmitter {
3690
3745
  ]);
3691
3746
  let plan;
3692
3747
  try {
3748
+ if (
3749
+ sandboxPolicy.wallTimeMs !== undefined &&
3750
+ (!rawPlan?.applied ||
3751
+ rawPlan.platform !== "win32" ||
3752
+ ![
3753
+ "windows-job-restricted-token",
3754
+ "windows-appcontainer-job-restricted-token",
3755
+ ].includes(rawPlan.backend) ||
3756
+ rawPlan.windowsWallTimeMs !== sandboxPolicy.wallTimeMs)
3757
+ ) {
3758
+ throw this._sandboxBoundaryError(
3759
+ "native_wall_time_limit_unavailable",
3760
+ `The requested native wall-time limit was not enforced: ${rawPlan?.reason || "unattested_plan"}`,
3761
+ {
3762
+ requiredBoundaries,
3763
+ actualGuarantees: rawPlan?.guarantees || [],
3764
+ sandboxBackend: rawPlan?.backend,
3765
+ sandboxCandidateBackend: rawPlan?.candidateBackend,
3766
+ sandboxRuntimeProbe: rawPlan?.runtimeProbe,
3767
+ sandboxCandidateReason: rawPlan?.reason,
3768
+ },
3769
+ );
3770
+ }
3771
+ if (
3772
+ sandboxPolicy.windowsNativeEvaluator &&
3773
+ (rawPlan.backend !== "windows-appcontainer-job-restricted-token" ||
3774
+ rawPlan.policyAttested !== true ||
3775
+ rawPlan.windowsNativeEvaluatorDigest !==
3776
+ sandboxPolicy.windowsNativeEvaluator.manifestDigest)
3777
+ ) {
3778
+ throw this._sandboxBoundaryError(
3779
+ "native_evaluator_plan_unattested",
3780
+ "The staged native evaluator policy was not enforced",
3781
+ );
3782
+ }
3693
3783
  plan = this._validateSandboxPlan(rawPlan, {
3694
3784
  command,
3695
3785
  args: Object.freeze([...(args || [])]),