chainlesschain 0.164.0 → 0.165.2

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 (228) hide show
  1. package/README.md +248 -2
  2. package/package.json +7 -4
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-D2MEn7fI.js → AIOps-lS1R0Cza.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-lCW_4Gf0.js → ActionButton-BmlgBPsj.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-Bs7i-nsS.js → Analytics-CNKczv1S.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CK7B6S_j.js → AppLayout-BuZGJARQ.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-C8ILjpyo.js → Artifacts-Dm73Sl-O.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-Bb9cNaWc.js → Audit-BGMW7A5e.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-sC9ooXIf.js → BackgroundAgents-BQ6M54JR.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-D-DrsplT.js → Backup-DBzGP0bR.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-uCNZpwSK.js → BaseInput-D1mLCoOV.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-CsDK1uE9.js → Chat-BUfcN7p8.js} +6 -6
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-OqSDt50w.js → ChatBubbleRenderer-CY5m8ZHg.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-JhTXM1-K.js → Checkbox-Du0Yg5Pi.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-BbzZLnyD.js → Codegen-B2RXziHW.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-D4VW4m4j.js → Col-DWhekOrR.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-BXrYawxV.js → Community-HQhrkon4.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-DCihGy1u.js → Compact-izHNb4uz.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-DlyZ-lk8.js → Compliance-BDwLy41-.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DvGZrhbO.js → Cowork-DFJhLa7K.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-DUncZi8h.js → Cron-DYY9RG7-.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-C_hLuUcm.js → Crosschain-rr7tJTtv.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-b6YSUqxr.js → DID-D2sfHRLi.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-uvM15Cfb.js → Dashboard-q_eGdhha.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-D1-Nd4qY.js → Dropdown-Cufrh2Bz.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-8ApvL6Zc.js → EmailListRenderer-B-kdv10f.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-LYJt8811.js → FamilyGuardDashboard-D21XjgfG.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BbI3HGpv.js → Federation-CAwRTLZH.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-DSoEKuFu.js → FormItemContext-vx3daNDF.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-B0S9OWd5.js → GenericCardRenderer-CRWY2wU4.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DIs9Zhf3.js → Git-Q6SwauyD.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-jzZXrxcl.js → Governance-BJ1q4IQZ.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-JUPjf2ZI.js → Inference-CSWjROgD.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-DxnfJvlP.js → KnowledgeGraph-fzuQsWuY.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-iXiz8BEE.js → Logs-DVF8Mnns.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-BLif4QSg.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-Y3Xch312.js → Marketplace-BQP7LOA4.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-D7kGGbO7.js → McpTools-BNlJCmNc.js} +4 -4
  40. package/src/assets/web-panel/assets/{Memory-Do_hVxzh.js → Memory-DLLg5WQ4.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-CtrR0Tas.js → MobileBridge-CR2skNAF.js} +1 -1
  42. package/src/assets/web-panel/assets/MobileProjects-BxVavObl.js +1 -0
  43. package/src/assets/web-panel/assets/{Mtc-qo5QCTGd.js → Mtc-B17pgqMI.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-cq5mnjYj.js → MtcAudit-4UmV5cIL.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-Bq0MAQHw.js → Multisig-DoMEXEcU.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-DdWE6Vi2.js → NLProgramming-h9DMXFX4.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-BxMBFcIp.js → Notes-Cihn-xX8.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-RTrAcbUe.js → NotificationSettings-BE6ZGa3R.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-DJbpGqzT.js → OrderTableRenderer-CnwojoSI.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-SQzIPdqC.js → Organization-B2vpwmjb.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-02yECh22.js → Overflow-C4z28R0-.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-BNg4pNCM.js → P2P-BVlFkwhW.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-BxPJZsD3.js → PdhVaultBrowser-BXbEdL5B.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-CLnDnPAz.js → Permissions-C28xs4lp.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-C5m0AjGf.js → PersonalDataHub-Dgl_rBjM.js} +4 -4
  56. package/src/assets/web-panel/assets/{Pipeline-VwOf5Dhv.js → Pipeline-3QUIhrKI.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-CC0W2hZd.js → Privacy-8Gh33OZ5.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-Dz893Bob.js → ProjectInit-BNmg4tZi.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CxQkvl0k.js → ProjectSettings-vgMfeCf9.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-BhtAVilc.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-DeB9IkxK.js → Providers-CXoOa8eX.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-OCeoaswn.js → QrScannerModal-BhfTF0oS.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-DenotaAj.js → QuickAsk-C746HEAj.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-Cies_jjI.js → Recommend-BSej8Eo0.js} +1 -1
  65. package/src/assets/web-panel/assets/RemoteSession-f3gmnlG3.js +5 -0
  66. package/src/assets/web-panel/assets/{Reputation-CW0mvxgZ.js → Reputation-CyvybpkN.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-SEd32qZU.js → Row-DwV2PeYn.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-BSwnJ3Lm.js → RssFeed-11GrzjZy.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-xe2m8bkC.js → Search-BjKXkrAY.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Mq2F9Nro.js → Security-eWiFb9Yk.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-BKGw2V3P.js → Services-CdCsMAtt.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DW95-Rxf.js → Skeleton-4Q2UTKQ5.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-CHgChCv_.js → Skills-v4rtYrRj.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-tKh6k3R7.js → Sla-CRhsFvd_.js} +1 -1
  75. package/src/assets/web-panel/assets/SpeechSettings-DLjKsDVu.js +1 -0
  76. package/src/assets/web-panel/assets/{SyncSettings-D0Fw8fVL.js → SyncSettings-D16zonl7.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-20x1GB3D.js → Tasks-Xxozz0i_.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-DJ-lts-a.js → Templates-CXJpPtCT.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-BFdGpbs7.js → Tenant-XRV0L1ZF.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-Xpx61vHL.js → Terminal-CYuO1i_w.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-C6UIvyZQ.js → TimelineRenderer-BXqitWsu.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-Dellq08f.js → Tokens-BvPrDHIy.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-VKw8d0J-.js → Trigger-DhCrSTf5.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-DNftdKT5.js → Trust-DmsuZD6K.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DX9VAKJI.js → UkeySign-D_yrts5C.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-B2h3jO8q.js → VideoEditing-B01ihPrm.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-CjGkof7f.js → Wallet-DpUjKa-N.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-2Zfcu55P.js → WebAuthn-CL07yON6.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-B9eZ8UxZ.js → WorkflowEditor-CLMflnRr.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-CQNfD-YO.js → chat-DtUqe8h_.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-D4usyFT2.js → colors-DPKrgVfy.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-s4TpjvjX.js → compact-item-BTt2GtOm.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-BRCSvYxj.js → createContext-D_VeUc1J.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-Bj5nUW2v.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-AdkUbkV-.js → hasIn-1kndiylj.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BIoG5yYf.js → index-2eyAXE3d.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-nXyk8GZ9.js → index-4bna2_SR.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B2V-eKfK.js → index-8nz_fmlj.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-D_XBHopw.js → index-B9KvcS7R.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-s_buFMEG.js → index-BAXfRKfG.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DZT36ZYq.js → index-BHpBMWCh.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BMcimU-2.js → index-BaZAbw9A.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DzRjHxEv.js → index-BepLZwgB.js} +1 -1
  104. package/src/assets/web-panel/assets/{index--00U6vXZ.js → index-Bhudc_Ez.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C7Y8Aluc.js → index-BlDNVgBt.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BtobShro.js → index-By5n8up9.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Cvyt-zhm.js → index-CHG-KcS4.js} +1 -1
  108. package/src/assets/web-panel/assets/index-CPcM-eqo.js +1 -0
  109. package/src/assets/web-panel/assets/{index-MMN9V6JF.js → index-CX81qSM4.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Bad4VNvz.js → index-CauoBQkH.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CCsM_Ybx.js → index-CcO04eDn.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DARsAyUp.js → index-CtF7EN49.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-C5KAQFPQ.js → index-CzjkKUmR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-RfMr79MW.js → index-D95OFht7.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DdCB70SS.js → index-DFIZ_1Sf.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CYdoQsa1.js → index-DG2MZQ3f.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BPhkFZ1I.js → index-DJ8vXGOa.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-_IRVsV9X.js → index-Dbbymxpj.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DVKfQ0Ux.js → index-DdkplL9R.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CkwiSLhU.js → index-DfGl8CqG.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CJVEOa4i.js → index-DfKBRdSS.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-C1mJtE6q.js → index-DsJCDUJG.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BgiXR_-_.js → index-Dwqmtk65.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CJCFe2Qf.js → index-DxdTdBix.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-C484EJiS.js → index-FzdwcSy2.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-B4JJCfQ2.js → index-GsoUQ2M7.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CQhAVU5t.js → index-brCInrzq.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CKC1t3Ic.js → index-dVc-CXY4.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BQ3DXBtD.js → index-fk2Rjacw.js} +3 -3
  130. package/src/assets/web-panel/assets/{index-Cduu2Hhr.js → index-l1BETrDb.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Bw2MEnUk.js → index-m31Ka8hZ.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D2GjI2oe.js → index-ocf06EVQ.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CJdQrZth.js → index-sDoMHTNA.js} +1 -1
  134. package/src/assets/web-panel/assets/index-xeSyVoRe.js +1 -0
  135. package/src/assets/web-panel/assets/{initDefaultProps-Blmpk7HL.js → initDefaultProps-CvyYJd7z.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-DPBJMv1n.js → motion-DYZkhUdz.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-ButdzCM7.js → move-slnJRVT1.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-cyRC_QCe.js → mtc-parser-DVc0CKmy.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-1epSV7hz.js → omit-BCXAtHCq.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-DuPY8xJs.js → pickAttrs-DBsemz_K.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-6XZqarhe.js → placementArrow-BRtkJ-nv.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-B40pXBWM.js → responsiveObserve-Cebq7EZn.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-jbZ9Wu4n.js → slide-DT7qvId2.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-BKQQcVy4.js → statusUtils-pQiITLkG.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-NiKfWjUT.js → styleChecker-c-19BVKQ.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-bQUCvPeb.js → useFlexGapSupport-DsS5-0bE.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CUGji7ph.js → useFs-CxGiMe5n.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-4veyN2N2.js → usePersonalDataHub-D9btVID5.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-1-nLug-Z.js → vnode-CUYFsFEN.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-dhCeRDMo.js → zoom-CXmn_35j.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +1 -1
  153. package/src/commands/cowork.js +559 -62
  154. package/src/commands/plugin.js +819 -64
  155. package/src/commands/session-location.js +318 -7
  156. package/src/constants.js +2 -2
  157. package/src/data/changelog.json +28 -2
  158. package/src/gateways/ws/action-protocol.js +293 -19
  159. package/src/gateways/ws/message-dispatcher.js +30 -0
  160. package/src/gateways/ws/remote-session-protocol.js +542 -35
  161. package/src/gateways/ws/ws-approval-gate.js +5 -0
  162. package/src/gateways/ws/ws-server.js +462 -38
  163. package/src/harness/jsonl-session-store.js +638 -2
  164. package/src/harness/prompt-compressor.js +4 -0
  165. package/src/harness/remote-session-audit.js +1 -0
  166. package/src/harness/remote-session-crypto.js +750 -50
  167. package/src/harness/remote-session-registry.js +795 -27
  168. package/src/lib/auto-mode-config.js +98 -3
  169. package/src/lib/background-agent-keeper-client.js +29 -0
  170. package/src/lib/background-agent-keeper-protocol.js +158 -10
  171. package/src/lib/background-agent-supervisor.js +870 -48
  172. package/src/lib/background-session-transport.js +23 -6
  173. package/src/lib/background-turn-bootstrap-protocol.js +64 -0
  174. package/src/lib/cowork-task-runner.js +70 -0
  175. package/src/lib/cowork-workflow.js +887 -62
  176. package/src/lib/delivery-production-adapter.js +38 -2
  177. package/src/lib/doctor-checkup.js +34 -10
  178. package/src/lib/dynamic-workflow-draft.js +535 -0
  179. package/src/lib/dynamic-workflow-facade.js +1105 -17
  180. package/src/lib/dynamic-workflow-runtime.js +2974 -0
  181. package/src/lib/execution-location-contract.js +111 -7
  182. package/src/lib/execution-location-runtime.js +3 -1
  183. package/src/lib/execution-location-target.js +910 -0
  184. package/src/lib/llm-providers.js +1 -0
  185. package/src/lib/mcp-call-ledger-store.js +35 -0
  186. package/src/lib/mcp-call-ledger.js +30 -0
  187. package/src/lib/mcp-stdio-package-materialization.js +1 -1
  188. package/src/lib/plugin-runtime/__tests__/remote-source.test.js +34 -16
  189. package/src/lib/plugin-runtime/install.js +4956 -657
  190. package/src/lib/plugin-runtime/manifest.js +3 -1
  191. package/src/lib/plugin-runtime/marketplace-artifact-readback.js +671 -31
  192. package/src/lib/plugin-runtime/marketplace-catalog.js +68 -2
  193. package/src/lib/plugin-runtime/marketplace-impact.js +208 -15
  194. package/src/lib/plugin-runtime/marketplace-network.js +317 -0
  195. package/src/lib/plugin-runtime/marketplace-pac-worker.js +15 -0
  196. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +25 -2
  197. package/src/lib/plugin-runtime/marketplace-source-cache.js +359 -0
  198. package/src/lib/plugin-runtime/policy.js +21 -6
  199. package/src/lib/plugin-runtime/publisher-trust.js +332 -0
  200. package/src/lib/plugin-runtime/remote-source.js +323 -27
  201. package/src/lib/plugin-runtime/scopes.js +317 -41
  202. package/src/lib/plugin-runtime/transaction-journal.js +806 -0
  203. package/src/lib/public-ide-capability-manifest.js +1 -1
  204. package/src/lib/remote-approval-bridge.js +745 -108
  205. package/src/lib/remote-control.js +2 -0
  206. package/src/lib/remote-membership-coordinator.js +612 -46
  207. package/src/lib/remote-membership-host-store.js +59 -10
  208. package/src/lib/runtime-provenance-ledger.js +171 -46
  209. package/src/lib/shell-approval.js +169 -6
  210. package/src/lib/sub-agent-context.js +268 -2
  211. package/src/lib/task-model-selector.js +1 -1
  212. package/src/lib/with-file-lock.js +167 -42
  213. package/src/repl/agent-repl.js +70 -29
  214. package/src/repl/remote-approval.js +29 -7
  215. package/src/runtime/agent-core.js +1695 -194
  216. package/src/runtime/headless-runner.js +22 -5
  217. package/src/runtime/headless-stream.js +19 -3
  218. package/src/workers/background-agent-keeper.js +279 -58
  219. package/src/workers/background-agent-worker.js +7 -0
  220. package/src/workers/background-turn-bootstrap.js +29 -2
  221. package/src/assets/web-panel/assets/MarkdownRenderer-GmB6DOr6.js +0 -1
  222. package/src/assets/web-panel/assets/MobileProjects-BL0Czcoj.js +0 -1
  223. package/src/assets/web-panel/assets/Projects-DDAjkUkT.js +0 -1
  224. package/src/assets/web-panel/assets/RemoteSession-Dxzl5n2T.js +0 -5
  225. package/src/assets/web-panel/assets/SpeechSettings-D6UL3dA5.js +0 -1
  226. package/src/assets/web-panel/assets/devWarning-DKJjSWRn.js +0 -1
  227. package/src/assets/web-panel/assets/index-C61cnl3K.js +0 -1
  228. package/src/assets/web-panel/assets/index-D4XFHxBr.js +0 -1
@@ -22,6 +22,8 @@
22
22
  * @module cowork-workflow
23
23
  */
24
24
 
25
+ import { createHash } from "node:crypto";
26
+ import { types as utilTypes } from "node:util";
25
27
  import fs, {
26
28
  existsSync,
27
29
  mkdirSync,
@@ -52,6 +54,7 @@ import {
52
54
  normalizeWorkflowDefinitionDigest,
53
55
  verifyCoworkWorkflowRecord,
54
56
  } from "./workflow-definition-contract.js";
57
+ import { canonicalJson } from "./scheduler-kernel/contract.js";
55
58
 
56
59
  /** Maximum number of items a single forEach step can expand into. */
57
60
  export const MAX_FAN_OUT = 500;
@@ -59,6 +62,512 @@ export const MAX_FAN_OUT = 500;
59
62
  /** Absolute ceiling on a single loop step's iterations (infinite-loop guard). */
60
63
  export const MAX_LOOP_ITERATIONS = 100;
61
64
  export const COWORK_WORKFLOW_RUN_RECORD_SCHEMA = "cc-cowork-workflow-run/v1";
65
+ export const COWORK_WORKFLOW_RUN_ADMISSION_INVALID_CODE =
66
+ "COWORK_WORKFLOW_RUN_ADMISSION_INVALID";
67
+ export const COWORK_WORKFLOW_RUN_RESULT_INVALID_CODE =
68
+ "COWORK_WORKFLOW_RUN_RESULT_INVALID";
69
+ export const COWORK_WORKFLOW_CONTROL_SIGNAL_CODE =
70
+ "COWORK_WORKFLOW_CONTROL_SIGNAL";
71
+
72
+ const DYNAMIC_WORKFLOW_RUN_ADMISSION_SCHEMA =
73
+ "cc-dynamic-workflow-run-admission/v1";
74
+ const EXECUTION_LOCATION_AUTHORITY_SCHEMA =
75
+ "cc-session-execution-location-authority/v1";
76
+ const EXECUTION_LOCATION_BINDING_SCHEMA = "cc-execution-location-binding/v1";
77
+ const COWORK_WORKFLOW_RECORD_SCHEMA = "cc-cowork-workflow-record/v1";
78
+ const RUN_ADMISSION_FIELDS = new Set([
79
+ "schema",
80
+ "definition",
81
+ "executionLocation",
82
+ "executionPolicy",
83
+ "definitionDigest",
84
+ "executionLocationDigest",
85
+ "preflightDigest",
86
+ "maxParallel",
87
+ "credentialValuesTransferred",
88
+ "admissionDigest",
89
+ ]);
90
+ const RUN_ADMISSION_DEFINITION_FIELDS = new Set([
91
+ "schema",
92
+ "definitionDigest",
93
+ "authority",
94
+ ]);
95
+ const RUN_ADMISSION_DEFINITION_AUTHORITY_FIELDS = new Set([
96
+ "status",
97
+ "recordSchema",
98
+ "definitionSchema",
99
+ "definitionDigest",
100
+ ]);
101
+ const RUN_ADMISSION_EXECUTION_LOCATION_FIELDS = new Set([
102
+ "authoritySchema",
103
+ "authority",
104
+ "session",
105
+ "bindingSchema",
106
+ "location",
107
+ ]);
108
+ const RUN_ADMISSION_SESSION_FIELDS = new Set([
109
+ "sessionId",
110
+ "headHash",
111
+ "eventCount",
112
+ ]);
113
+ const RUN_ADMISSION_EXECUTION_POLICY_FIELDS = new Set([
114
+ "cwd",
115
+ "continueOnError",
116
+ "pipeline",
117
+ "provider",
118
+ "model",
119
+ ]);
120
+ const RUN_ADMISSION_LLM_OPTION_FIELDS = new Set(["provider", "model"]);
121
+ const ADMITTED_TASK_ENTRY_REQUIRED_FIELDS = new Set([
122
+ "taskId",
123
+ "status",
124
+ "result",
125
+ ]);
126
+ const ADMITTED_STEP_STATUSES = new Set([
127
+ "completed",
128
+ "failed",
129
+ "partial",
130
+ "skipped",
131
+ ]);
132
+ const MAX_ADMITTED_RUN_STEPS = 64;
133
+ const MAX_ADMITTED_SNAPSHOT_NODES = 25_000;
134
+ const MAX_ADMITTED_SNAPSHOT_BYTES = 1024 * 1024;
135
+ const STEP_TIMEOUT_CODE = "COWORK_WORKFLOW_STEP_TIMEOUT";
136
+
137
+ function runAdmissionError(message) {
138
+ const error = new Error(message);
139
+ error.code = COWORK_WORKFLOW_RUN_ADMISSION_INVALID_CODE;
140
+ return error;
141
+ }
142
+
143
+ function runResultError(message) {
144
+ const error = new Error(message);
145
+ error.code = COWORK_WORKFLOW_RUN_RESULT_INVALID_CODE;
146
+ return error;
147
+ }
148
+
149
+ /**
150
+ * Snapshot an admitted runner value without invoking accessors or accepting
151
+ * proxies/non-JSON values. The returned graph is detached and deeply frozen,
152
+ * so callbacks and a hostile runner cannot mutate executor control state.
153
+ */
154
+ function snapshotAdmittedCanonicalValue(
155
+ value,
156
+ name,
157
+ {
158
+ maxNodes = MAX_ADMITTED_SNAPSHOT_NODES,
159
+ maxBytes = MAX_ADMITTED_SNAPSHOT_BYTES,
160
+ } = {},
161
+ ) {
162
+ const seen = new WeakSet();
163
+ let nodes = 0;
164
+ let roughCharacters = 0;
165
+ const invalid = (reason) => {
166
+ throw runResultError(`${name} ${reason}`);
167
+ };
168
+ const account = (characters = 1) => {
169
+ nodes += 1;
170
+ roughCharacters += characters;
171
+ if (nodes > maxNodes || roughCharacters > maxBytes) {
172
+ invalid("exceeds snapshot limits");
173
+ }
174
+ };
175
+ const visit = (current, depth) => {
176
+ if (depth > 64) invalid("exceeds snapshot depth");
177
+ if (current === null || typeof current === "boolean") {
178
+ account();
179
+ return current;
180
+ }
181
+ if (typeof current === "string") {
182
+ account(current.length);
183
+ return current;
184
+ }
185
+ if (typeof current === "number") {
186
+ if (!Number.isFinite(current)) invalid("contains a non-finite number");
187
+ account();
188
+ return current;
189
+ }
190
+ if (!current || typeof current !== "object" || utilTypes.isProxy(current)) {
191
+ invalid("must contain only non-proxy canonical JSON values");
192
+ }
193
+ if (seen.has(current)) invalid("contains a cycle or repeated reference");
194
+ seen.add(current);
195
+ account();
196
+
197
+ if (Array.isArray(current)) {
198
+ if (Object.getPrototypeOf(current) !== Array.prototype) {
199
+ invalid("contains a non-plain array");
200
+ }
201
+ if (current.length > maxNodes) invalid("contains an oversized array");
202
+ const keys = Reflect.ownKeys(current);
203
+ if (keys.length !== current.length + 1) {
204
+ invalid("contains a sparse or extended array");
205
+ }
206
+ const output = [];
207
+ for (let index = 0; index < current.length; index += 1) {
208
+ const descriptor = Object.getOwnPropertyDescriptor(
209
+ current,
210
+ String(index),
211
+ );
212
+ if (
213
+ !descriptor ||
214
+ descriptor.enumerable !== true ||
215
+ !Object.hasOwn(descriptor, "value")
216
+ ) {
217
+ invalid("contains an accessor or sparse array entry");
218
+ }
219
+ output.push(visit(descriptor.value, depth + 1));
220
+ }
221
+ return Object.freeze(output);
222
+ }
223
+
224
+ const prototype = Object.getPrototypeOf(current);
225
+ if (prototype !== Object.prototype && prototype !== null) {
226
+ invalid("contains a non-plain object");
227
+ }
228
+ const output = {};
229
+ for (const key of Reflect.ownKeys(current)) {
230
+ if (typeof key !== "string") invalid("contains a symbol key");
231
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
232
+ if (
233
+ !descriptor ||
234
+ descriptor.enumerable !== true ||
235
+ !Object.hasOwn(descriptor, "value")
236
+ ) {
237
+ invalid("contains an accessor or non-enumerable property");
238
+ }
239
+ roughCharacters += key.length;
240
+ if (roughCharacters > maxBytes) invalid("exceeds snapshot limits");
241
+ output[key] = visit(descriptor.value, depth + 1);
242
+ }
243
+ return Object.freeze(output);
244
+ };
245
+
246
+ const snapshot = visit(value, 0);
247
+ let serialized;
248
+ try {
249
+ serialized = JSON.stringify(snapshot);
250
+ } catch {
251
+ invalid("cannot be serialized");
252
+ }
253
+ if (Buffer.byteLength(serialized, "utf8") > maxBytes) {
254
+ invalid("exceeds snapshot limits");
255
+ }
256
+ return snapshot;
257
+ }
258
+
259
+ function safeRunErrorMessage(error) {
260
+ if (!error || typeof error !== "object" || utilTypes.isProxy(error)) {
261
+ return "task failed";
262
+ }
263
+ const descriptor = Object.getOwnPropertyDescriptor(error, "message");
264
+ if (
265
+ descriptor &&
266
+ Object.hasOwn(descriptor, "value") &&
267
+ typeof descriptor.value === "string"
268
+ ) {
269
+ return descriptor.value.slice(0, 4096);
270
+ }
271
+ return "task failed";
272
+ }
273
+
274
+ function normalizeAdmittedTaskEntry(value) {
275
+ const entry = snapshotAdmittedCanonicalValue(value, "task result");
276
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
277
+ throw runResultError("task result must be a plain object");
278
+ }
279
+ if (
280
+ [...ADMITTED_TASK_ENTRY_REQUIRED_FIELDS].some(
281
+ (field) => !Object.hasOwn(entry, field),
282
+ ) ||
283
+ (entry.status !== "completed" && entry.status !== "failed") ||
284
+ typeof entry.taskId !== "string" ||
285
+ entry.taskId.length === 0 ||
286
+ entry.taskId.length > 512 ||
287
+ !entry.result ||
288
+ typeof entry.result !== "object" ||
289
+ Array.isArray(entry.result)
290
+ ) {
291
+ throw runResultError("task result has an invalid schema");
292
+ }
293
+ return Object.freeze({
294
+ taskId: entry.taskId,
295
+ status: entry.status,
296
+ result: entry.result,
297
+ });
298
+ }
299
+
300
+ function normalizeAdmittedOutcome(value, name = "workflow step outcome") {
301
+ const outcome = snapshotAdmittedCanonicalValue(value, name);
302
+ if (
303
+ !outcome ||
304
+ typeof outcome !== "object" ||
305
+ Array.isArray(outcome) ||
306
+ Reflect.ownKeys(outcome).length !== 4 ||
307
+ !["id", "status", "taskId", "result"].every((field) =>
308
+ Object.hasOwn(outcome, field),
309
+ ) ||
310
+ typeof outcome.id !== "string" ||
311
+ outcome.id.length === 0 ||
312
+ outcome.id.length > 512 ||
313
+ !ADMITTED_STEP_STATUSES.has(outcome.status) ||
314
+ (outcome.taskId !== null &&
315
+ (typeof outcome.taskId !== "string" ||
316
+ outcome.taskId.length === 0 ||
317
+ outcome.taskId.length > 512)) ||
318
+ !outcome.result ||
319
+ typeof outcome.result !== "object" ||
320
+ Array.isArray(outcome.result)
321
+ ) {
322
+ throw runResultError(`${name} has an invalid schema`);
323
+ }
324
+ return outcome;
325
+ }
326
+
327
+ function notifyWorkflowCallback(callback, value, admitted, name) {
328
+ if (typeof callback !== "function") return;
329
+ const view = admitted
330
+ ? snapshotAdmittedCanonicalValue(value, `${name} callback view`)
331
+ : value;
332
+ try {
333
+ callback(view);
334
+ } catch {
335
+ // Observation callbacks must not mutate or steer workflow control flow.
336
+ }
337
+ }
338
+
339
+ function assertAdmissionObject(value, fields, name) {
340
+ if (
341
+ !value ||
342
+ typeof value !== "object" ||
343
+ Array.isArray(value) ||
344
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value)) ||
345
+ Object.keys(value).length !== fields.size ||
346
+ Object.keys(value).some((key) => !fields.has(key))
347
+ ) {
348
+ throw runAdmissionError(`${name} is invalid`);
349
+ }
350
+ }
351
+
352
+ function normalizeAdmittedLlmOptions(value) {
353
+ if (
354
+ !value ||
355
+ typeof value !== "object" ||
356
+ Array.isArray(value) ||
357
+ utilTypes.isProxy(value) ||
358
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value))
359
+ ) {
360
+ throw runAdmissionError("admitted workflow llmOptions is invalid");
361
+ }
362
+ const keys = Reflect.ownKeys(value);
363
+ if (
364
+ keys.length !== RUN_ADMISSION_LLM_OPTION_FIELDS.size ||
365
+ keys.some(
366
+ (key) =>
367
+ typeof key !== "string" || !RUN_ADMISSION_LLM_OPTION_FIELDS.has(key),
368
+ )
369
+ ) {
370
+ throw runAdmissionError("admitted workflow llmOptions is not exact");
371
+ }
372
+ const snapshot = {};
373
+ for (const key of RUN_ADMISSION_LLM_OPTION_FIELDS) {
374
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
375
+ if (
376
+ !descriptor ||
377
+ !Object.hasOwn(descriptor, "value") ||
378
+ descriptor.enumerable !== true
379
+ ) {
380
+ throw runAdmissionError(
381
+ "admitted workflow llmOptions must use enumerable data properties",
382
+ );
383
+ }
384
+ snapshot[key] = descriptor.value;
385
+ }
386
+ return Object.freeze(snapshot);
387
+ }
388
+
389
+ function normalizeAdmissionDigest(value, name) {
390
+ const digest = normalizeWorkflowDefinitionDigest(value);
391
+ if (!digest || digest !== value) {
392
+ throw runAdmissionError(`${name} is invalid`);
393
+ }
394
+ return digest;
395
+ }
396
+
397
+ function admissionDigest(material) {
398
+ return `sha256:${createHash("sha256")
399
+ .update("chainlesschain.dynamic-workflow.admission.v1\0", "utf8")
400
+ .update(canonicalJson(material, "dynamicWorkflowAdmission"), "utf8")
401
+ .digest("hex")}`;
402
+ }
403
+
404
+ function normalizeWorkflowRunAdmission(
405
+ value,
406
+ definitionAuthority,
407
+ maxParallel,
408
+ execution,
409
+ ) {
410
+ try {
411
+ value = structuredClone(value);
412
+ } catch {
413
+ throw runAdmissionError("run admission could not be safely snapshotted");
414
+ }
415
+ assertAdmissionObject(value, RUN_ADMISSION_FIELDS, "run admission");
416
+ assertAdmissionObject(
417
+ value.definition,
418
+ RUN_ADMISSION_DEFINITION_FIELDS,
419
+ "run admission definition",
420
+ );
421
+ assertAdmissionObject(
422
+ value.definition.authority,
423
+ RUN_ADMISSION_DEFINITION_AUTHORITY_FIELDS,
424
+ "run admission definition authority",
425
+ );
426
+ assertAdmissionObject(
427
+ value.executionLocation,
428
+ RUN_ADMISSION_EXECUTION_LOCATION_FIELDS,
429
+ "run admission execution location",
430
+ );
431
+ assertAdmissionObject(
432
+ value.executionLocation.session,
433
+ RUN_ADMISSION_SESSION_FIELDS,
434
+ "run admission session proof",
435
+ );
436
+ assertAdmissionObject(
437
+ value.executionPolicy,
438
+ RUN_ADMISSION_EXECUTION_POLICY_FIELDS,
439
+ "run admission execution policy",
440
+ );
441
+ assertAdmissionObject(
442
+ execution.llmOptions,
443
+ RUN_ADMISSION_LLM_OPTION_FIELDS,
444
+ "admitted workflow llmOptions",
445
+ );
446
+
447
+ const definitionDigest = normalizeAdmissionDigest(
448
+ value.definitionDigest,
449
+ "run admission definition digest",
450
+ );
451
+ const executionLocationDigest = normalizeAdmissionDigest(
452
+ value.executionLocationDigest,
453
+ "run admission execution-location digest",
454
+ );
455
+ const preflightDigest = normalizeAdmissionDigest(
456
+ value.preflightDigest,
457
+ "run admission preflight digest",
458
+ );
459
+ const declaredAdmissionDigest = normalizeAdmissionDigest(
460
+ value.admissionDigest,
461
+ "run admission digest",
462
+ );
463
+ const authority = value.definition.authority;
464
+ const session = value.executionLocation.session;
465
+ const executionPolicy = value.executionPolicy;
466
+ const hasControlCharacters = (input) =>
467
+ [...input].some((character) => {
468
+ const codePoint = character.codePointAt(0);
469
+ return codePoint <= 0x1f || codePoint === 0x7f;
470
+ });
471
+ const validOptionalString = (input) =>
472
+ input === null ||
473
+ (typeof input === "string" &&
474
+ input.length > 0 &&
475
+ input.trim() === input &&
476
+ !hasControlCharacters(input));
477
+
478
+ if (
479
+ value.schema !== DYNAMIC_WORKFLOW_RUN_ADMISSION_SCHEMA ||
480
+ value.definition.schema !== definitionAuthority.schema ||
481
+ value.definition.definitionDigest !== definitionDigest ||
482
+ definitionDigest !== definitionAuthority.definitionDigest ||
483
+ authority.status !== "versioned" ||
484
+ authority.recordSchema !== COWORK_WORKFLOW_RECORD_SCHEMA ||
485
+ authority.definitionSchema !== definitionAuthority.schema ||
486
+ authority.definitionDigest !== definitionDigest ||
487
+ value.executionLocation.authoritySchema !==
488
+ EXECUTION_LOCATION_AUTHORITY_SCHEMA ||
489
+ !["verified-session-start", "verified-session-location-handoff"].includes(
490
+ value.executionLocation.authority,
491
+ ) ||
492
+ value.executionLocation.bindingSchema !==
493
+ EXECUTION_LOCATION_BINDING_SCHEMA ||
494
+ typeof value.executionLocation.location !== "string" ||
495
+ value.executionLocation.location.trim() !==
496
+ value.executionLocation.location ||
497
+ value.executionLocation.location.length === 0 ||
498
+ typeof session.sessionId !== "string" ||
499
+ session.sessionId.trim() !== session.sessionId ||
500
+ session.sessionId.length === 0 ||
501
+ !/^[a-f0-9]{64}$/u.test(session.headHash) ||
502
+ !Number.isSafeInteger(session.eventCount) ||
503
+ session.eventCount < 1 ||
504
+ typeof executionPolicy.cwd !== "string" ||
505
+ executionPolicy.cwd.length === 0 ||
506
+ hasControlCharacters(executionPolicy.cwd) ||
507
+ typeof executionPolicy.continueOnError !== "boolean" ||
508
+ typeof executionPolicy.pipeline !== "boolean" ||
509
+ !validOptionalString(executionPolicy.provider) ||
510
+ !validOptionalString(executionPolicy.model) ||
511
+ executionPolicy.cwd !== execution.cwd ||
512
+ executionPolicy.continueOnError !== execution.continueOnError ||
513
+ executionPolicy.pipeline !== execution.pipeline ||
514
+ executionPolicy.provider !== execution.llmOptions.provider ||
515
+ executionPolicy.model !== execution.llmOptions.model ||
516
+ !Number.isSafeInteger(maxParallel) ||
517
+ maxParallel < 1 ||
518
+ maxParallel > 64 ||
519
+ value.maxParallel !== maxParallel ||
520
+ value.credentialValuesTransferred !== false
521
+ ) {
522
+ throw runAdmissionError(
523
+ "run admission does not match the workflow definition or execution options",
524
+ );
525
+ }
526
+
527
+ const normalized = Object.freeze({
528
+ schema: DYNAMIC_WORKFLOW_RUN_ADMISSION_SCHEMA,
529
+ definition: Object.freeze({
530
+ schema: definitionAuthority.schema,
531
+ definitionDigest,
532
+ authority: Object.freeze({
533
+ status: "versioned",
534
+ recordSchema: COWORK_WORKFLOW_RECORD_SCHEMA,
535
+ definitionSchema: definitionAuthority.schema,
536
+ definitionDigest,
537
+ }),
538
+ }),
539
+ executionLocation: Object.freeze({
540
+ authoritySchema: EXECUTION_LOCATION_AUTHORITY_SCHEMA,
541
+ authority: value.executionLocation.authority,
542
+ session: Object.freeze({
543
+ sessionId: session.sessionId,
544
+ headHash: session.headHash,
545
+ eventCount: session.eventCount,
546
+ }),
547
+ bindingSchema: EXECUTION_LOCATION_BINDING_SCHEMA,
548
+ location: value.executionLocation.location,
549
+ }),
550
+ executionPolicy: Object.freeze({
551
+ cwd: executionPolicy.cwd,
552
+ continueOnError: executionPolicy.continueOnError,
553
+ pipeline: executionPolicy.pipeline,
554
+ provider: executionPolicy.provider,
555
+ model: executionPolicy.model,
556
+ }),
557
+ definitionDigest,
558
+ executionLocationDigest,
559
+ preflightDigest,
560
+ maxParallel,
561
+ credentialValuesTransferred: false,
562
+ });
563
+ if (admissionDigest(normalized) !== declaredAdmissionDigest) {
564
+ throw runAdmissionError("run admission digest does not match its material");
565
+ }
566
+ return Object.freeze({
567
+ ...normalized,
568
+ admissionDigest: declaredAdmissionDigest,
569
+ });
570
+ }
62
571
 
63
572
  export const _deps = {
64
573
  existsSync,
@@ -751,7 +1260,9 @@ export async function withTimeout(factory, timeoutMs) {
751
1260
  let timer = null;
752
1261
  const timeout = new Promise((_resolve, reject) => {
753
1262
  timer = _deps.setTimeout(() => {
754
- reject(new Error(`step timed out after ${timeoutMs}ms`));
1263
+ const error = new Error(`step timed out after ${timeoutMs}ms`);
1264
+ error.code = STEP_TIMEOUT_CODE;
1265
+ reject(error);
755
1266
  }, timeoutMs);
756
1267
  });
757
1268
  try {
@@ -762,38 +1273,129 @@ export async function withTimeout(factory, timeoutMs) {
762
1273
  }
763
1274
  }
764
1275
 
1276
+ function createWorkflowTaskSemaphore(limit) {
1277
+ let active = 0;
1278
+ const waiters = [];
1279
+ return Object.freeze({
1280
+ acquire() {
1281
+ return new Promise((resolve) => {
1282
+ const grant = () => {
1283
+ active += 1;
1284
+ let released = false;
1285
+ resolve(() => {
1286
+ if (released) return;
1287
+ released = true;
1288
+ active -= 1;
1289
+ const next = waiters.shift();
1290
+ if (next) next();
1291
+ });
1292
+ };
1293
+ if (active < limit) grant();
1294
+ else waiters.push(grant);
1295
+ });
1296
+ },
1297
+ });
1298
+ }
1299
+
765
1300
  /**
766
1301
  * Run one step's task with optional `timeoutMs` and `retries` (with `fixed` or
767
1302
  * `exponential` `retryDelayMs` backoff). An attempt counts as a failure if the
768
1303
  * task throws, times out, or returns a non-`completed` status. Returns
769
1304
  * `{ ok, entry?, error?, attempts }`. `attempts` is the total number of tries.
770
1305
  */
771
- export async function runStepWithRetry({ step, message, cwd, llmOptions }) {
1306
+ export async function runStepWithRetry({
1307
+ step,
1308
+ message,
1309
+ cwd,
1310
+ llmOptions,
1311
+ taskSemaphore,
1312
+ runTask = _deps.runTask,
1313
+ recordId = step.id,
1314
+ iteration = 1,
1315
+ onTaskStart,
1316
+ admitted = false,
1317
+ }) {
772
1318
  const maxRetries = Math.max(0, Math.floor(Number(step.retries) || 0));
773
1319
  const timeoutMs = Number(step.timeoutMs) || 0;
774
1320
  let lastEntry = null;
775
1321
  let lastErr = null;
776
1322
  for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
1323
+ let taskPromise = null;
1324
+ let abortController = null;
1325
+ let release = null;
777
1326
  try {
778
- const entry = await withTimeout(
779
- () =>
780
- _deps.runTask({
781
- templateId: step.templateId || null,
782
- userMessage: message,
783
- files: step.files || [],
784
- cwd,
785
- llmOptions,
1327
+ release = taskSemaphore ? await taskSemaphore.acquire() : () => {};
1328
+ if (admitted && timeoutMs > 0) abortController = new AbortController();
1329
+ if (attempt === 1) {
1330
+ notifyWorkflowCallback(
1331
+ onTaskStart,
1332
+ Object.freeze({ stepId: recordId, message }),
1333
+ admitted,
1334
+ "step start",
1335
+ );
1336
+ }
1337
+ taskPromise = Promise.resolve().then(() =>
1338
+ runTask({
1339
+ templateId: step.templateId || null,
1340
+ userMessage: message,
1341
+ files: step.files || [],
1342
+ cwd,
1343
+ llmOptions,
1344
+ workflowEffect: Object.freeze({
1345
+ stepId: recordId,
1346
+ iteration,
1347
+ attempt,
1348
+ timeoutMs,
786
1349
  }),
787
- timeoutMs,
1350
+ ...(abortController ? { signal: abortController.signal } : {}),
1351
+ }),
788
1352
  );
1353
+ // A timeout abandons only the caller's wait. Keep the physical task's
1354
+ // permit until its promise really settles; retries and parallel children
1355
+ // must queue behind that late task instead of exceeding maxParallel.
1356
+ taskPromise.then(release, release);
1357
+ release = null; // the physical task now owns the permit
1358
+ const rawEntry = await withTimeout(() => taskPromise, timeoutMs);
1359
+ const entry = admitted ? normalizeAdmittedTaskEntry(rawEntry) : rawEntry;
789
1360
  if (entry && entry.status === "completed") {
790
1361
  return { ok: true, entry, attempts: attempt };
791
1362
  }
792
1363
  lastEntry = entry; // ran but not completed → retry-eligible
793
1364
  lastErr = null;
794
1365
  } catch (err) {
795
- lastErr = err; // threw or timed out → retry-eligible
796
- lastEntry = null;
1366
+ if (release) release();
1367
+ if (err?.code === COWORK_WORKFLOW_CONTROL_SIGNAL_CODE) throw err;
1368
+ if (err?.code === COWORK_WORKFLOW_RUN_RESULT_INVALID_CODE) throw err;
1369
+ if (admitted && err?.code === STEP_TIMEOUT_CODE && taskPromise != null) {
1370
+ abortController.abort(err);
1371
+ // A terminal admitted record must never be written while the physical
1372
+ // task can still produce side effects. Confirm settlement before a
1373
+ // retry or final failure; a runner that ignores abort safely blocks.
1374
+ // A late completed result is authoritative and must not be replayed.
1375
+ try {
1376
+ const lateRawEntry = await taskPromise;
1377
+ const lateEntry = normalizeAdmittedTaskEntry(lateRawEntry);
1378
+ if (lateEntry.status === "completed") {
1379
+ return { ok: true, entry: lateEntry, attempts: attempt };
1380
+ }
1381
+ lastEntry = lateEntry;
1382
+ lastErr = null;
1383
+ } catch (lateError) {
1384
+ if (
1385
+ lateError?.code === COWORK_WORKFLOW_CONTROL_SIGNAL_CODE ||
1386
+ lateError?.code === COWORK_WORKFLOW_RUN_RESULT_INVALID_CODE
1387
+ ) {
1388
+ throw lateError;
1389
+ }
1390
+ // The timeout remains the attempt's externally visible failure when
1391
+ // a non-authoritative runner error arrives after the deadline.
1392
+ lastErr = err;
1393
+ lastEntry = null;
1394
+ }
1395
+ } else {
1396
+ lastErr = err; // threw or timed out → retry-eligible
1397
+ lastEntry = null;
1398
+ }
797
1399
  }
798
1400
  if (attempt <= maxRetries) {
799
1401
  const delay = retryDelayFor(step, attempt);
@@ -813,24 +1415,27 @@ function _withAttempts(result, attempts) {
813
1415
  }
814
1416
 
815
1417
  /** Build a single-step outcome object from a `runStepWithRetry` result. */
816
- function outcomeFromRetry(recordId, r) {
1418
+ function outcomeFromRetry(recordId, r, admitted = false) {
1419
+ let outcome;
817
1420
  if (r.ok || r.entry) {
818
- return {
1421
+ outcome = {
819
1422
  id: recordId,
820
1423
  status: r.entry.status,
821
1424
  taskId: r.entry.taskId,
822
1425
  result: _withAttempts(r.entry.result, r.attempts),
823
1426
  };
1427
+ } else {
1428
+ outcome = {
1429
+ id: recordId,
1430
+ status: "failed",
1431
+ taskId: null,
1432
+ result: _withAttempts(
1433
+ { summary: `Step threw: ${safeRunErrorMessage(r.error)}` },
1434
+ r.attempts,
1435
+ ),
1436
+ };
824
1437
  }
825
- return {
826
- id: recordId,
827
- status: "failed",
828
- taskId: null,
829
- result: _withAttempts(
830
- { summary: `Step threw: ${r.error.message}` },
831
- r.attempts,
832
- ),
833
- };
1438
+ return admitted ? normalizeAdmittedOutcome(outcome) : outcome;
834
1439
  }
835
1440
 
836
1441
  // ─── while / until loop nodes ──────────────────────────────────────────────────
@@ -901,11 +1506,16 @@ export async function runLoopStep({
901
1506
  cwd,
902
1507
  llmOptions,
903
1508
  resultsById,
1509
+ taskSemaphore,
1510
+ runTask = _deps.runTask,
1511
+ onStepStart,
1512
+ admitted = false,
904
1513
  }) {
905
1514
  const cap = loopIterationCap(step);
906
1515
  let last = null;
907
1516
  let iterations = 0;
908
1517
  let stopReason = "cap";
1518
+ let startAnnounced = false;
909
1519
  for (let iter = 1; iter <= cap; iter++) {
910
1520
  iterations = iter;
911
1521
  const withSelf = substituteLoopVars(step.message, {
@@ -914,8 +1524,23 @@ export async function runLoopStep({
914
1524
  resultsById,
915
1525
  });
916
1526
  const message = substitutePlaceholders(withSelf, resultsById);
917
- const r = await runStepWithRetry({ step, message, cwd, llmOptions });
918
- last = outcomeFromRetry(recordId, r);
1527
+ const r = await runStepWithRetry({
1528
+ step,
1529
+ message,
1530
+ cwd,
1531
+ llmOptions,
1532
+ taskSemaphore,
1533
+ runTask,
1534
+ recordId,
1535
+ iteration: iter,
1536
+ onTaskStart: (event) => {
1537
+ if (startAnnounced) return;
1538
+ startAnnounced = true;
1539
+ notifyWorkflowCallback(onStepStart, event, admitted, "step start");
1540
+ },
1541
+ admitted,
1542
+ });
1543
+ last = outcomeFromRetry(recordId, r, admitted);
919
1544
  resultsById.set(recordId, last);
920
1545
  if (last.status === "failed") {
921
1546
  stopReason = "failed";
@@ -942,7 +1567,7 @@ export async function runLoopStep({
942
1567
  break;
943
1568
  }
944
1569
  }
945
- return {
1570
+ const outcome = {
946
1571
  id: recordId,
947
1572
  status: last ? last.status : "skipped",
948
1573
  taskId: last?.taskId ?? null,
@@ -953,6 +1578,7 @@ export async function runLoopStep({
953
1578
  loopStop: stopReason,
954
1579
  },
955
1580
  };
1581
+ return admitted ? normalizeAdmittedOutcome(outcome) : outcome;
956
1582
  }
957
1583
 
958
1584
  // ─── Step node + no-barrier pipeline ──────────────────────────────────────────
@@ -966,7 +1592,16 @@ export async function runLoopStep({
966
1592
  * is not "completed". Used by the no-barrier pipeline scheduler.
967
1593
  */
968
1594
  export async function runStepNode(step, ctx) {
969
- const { resultsById, cwd, llmOptions, onStepStart, onStepComplete } = ctx;
1595
+ const {
1596
+ resultsById,
1597
+ cwd,
1598
+ llmOptions,
1599
+ onStepStart,
1600
+ onStepComplete,
1601
+ taskSemaphore,
1602
+ runTask,
1603
+ admitted = false,
1604
+ } = ctx;
970
1605
  const recordId = step.id;
971
1606
  const single = (status, summary) => {
972
1607
  const o = { id: recordId, status, taskId: null, result: { summary } };
@@ -986,16 +1621,19 @@ export async function runStepNode(step, ctx) {
986
1621
  if (!runThis) return single("skipped", "when-condition false");
987
1622
 
988
1623
  if (isLoopStep(step)) {
989
- if (onStepStart) onStepStart({ stepId: recordId, message: step.message });
990
1624
  const o = await runLoopStep({
991
1625
  step,
992
1626
  recordId,
993
1627
  cwd,
994
1628
  llmOptions,
995
1629
  resultsById,
1630
+ taskSemaphore,
1631
+ runTask,
1632
+ onStepStart,
1633
+ admitted,
996
1634
  });
997
1635
  resultsById.set(recordId, o);
998
- if (onStepComplete) onStepComplete(o);
1636
+ notifyWorkflowCallback(onStepComplete, o, admitted, "step complete");
999
1637
  return {
1000
1638
  outcomes: [o],
1001
1639
  failed: o.status !== "completed" && o.status !== "skipped",
@@ -1015,16 +1653,20 @@ export async function runStepNode(step, ctx) {
1015
1653
  const childId = `${recordId}[${k}]`;
1016
1654
  const withItem = substituteItem(step.message, item);
1017
1655
  const msg = substitutePlaceholders(withItem, resultsById);
1018
- if (onStepStart) onStepStart({ stepId: childId, message: msg });
1019
1656
  const r = await runStepWithRetry({
1020
1657
  step,
1021
1658
  message: msg,
1022
1659
  cwd,
1023
1660
  llmOptions,
1661
+ taskSemaphore,
1662
+ runTask,
1663
+ recordId: childId,
1664
+ onTaskStart: onStepStart,
1665
+ admitted,
1024
1666
  });
1025
- const co = outcomeFromRetry(childId, r);
1667
+ const co = outcomeFromRetry(childId, r, admitted);
1026
1668
  resultsById.set(childId, co);
1027
- if (onStepComplete) onStepComplete(co);
1669
+ notifyWorkflowCallback(onStepComplete, co, admitted, "step complete");
1028
1670
  return co;
1029
1671
  }),
1030
1672
  );
@@ -1046,11 +1688,20 @@ export async function runStepNode(step, ctx) {
1046
1688
  }
1047
1689
 
1048
1690
  const message = substitutePlaceholders(step.message, resultsById);
1049
- if (onStepStart) onStepStart({ stepId: recordId, message });
1050
- const r = await runStepWithRetry({ step, message, cwd, llmOptions });
1051
- const o = outcomeFromRetry(recordId, r);
1691
+ const r = await runStepWithRetry({
1692
+ step,
1693
+ message,
1694
+ cwd,
1695
+ llmOptions,
1696
+ taskSemaphore,
1697
+ runTask,
1698
+ recordId,
1699
+ onTaskStart: onStepStart,
1700
+ admitted,
1701
+ });
1702
+ const o = outcomeFromRetry(recordId, r, admitted);
1052
1703
  resultsById.set(recordId, o);
1053
- if (onStepComplete) onStepComplete(o);
1704
+ notifyWorkflowCallback(onStepComplete, o, admitted, "step complete");
1054
1705
  return { outcomes: [o], failed: o.status !== "completed" };
1055
1706
  }
1056
1707
 
@@ -1071,6 +1722,9 @@ export async function runPipeline({
1071
1722
  llmOptions = {},
1072
1723
  onStepStart,
1073
1724
  onStepComplete,
1725
+ taskSemaphore,
1726
+ runTask = _deps.runTask,
1727
+ admitted = false,
1074
1728
  }) {
1075
1729
  const limit = Math.max(1, Math.floor(maxParallel) || 1);
1076
1730
  const remainingDeps = new Map(
@@ -1087,8 +1741,9 @@ export async function runPipeline({
1087
1741
  let anyFailure = false;
1088
1742
  let halted = false;
1089
1743
  let active = 0;
1744
+ let fatalError = null;
1090
1745
 
1091
- await new Promise((resolve) => {
1746
+ await new Promise((resolve, reject) => {
1092
1747
  function pump() {
1093
1748
  if (!halted) {
1094
1749
  for (const s of steps) {
@@ -1101,6 +1756,10 @@ export async function runPipeline({
1101
1756
  }
1102
1757
  }
1103
1758
  if (active === 0 && (halted || scheduled.size === steps.length)) {
1759
+ if (fatalError) {
1760
+ reject(fatalError);
1761
+ return;
1762
+ }
1104
1763
  for (const s of steps) {
1105
1764
  if (scheduled.has(s.id)) continue;
1106
1765
  const o = {
@@ -1126,13 +1785,26 @@ export async function runPipeline({
1126
1785
  llmOptions,
1127
1786
  onStepStart,
1128
1787
  onStepComplete,
1788
+ taskSemaphore,
1789
+ runTask,
1790
+ admitted,
1129
1791
  });
1130
1792
  } catch (err) {
1793
+ if (
1794
+ err?.code === COWORK_WORKFLOW_RUN_RESULT_INVALID_CODE ||
1795
+ err?.code === COWORK_WORKFLOW_CONTROL_SIGNAL_CODE
1796
+ ) {
1797
+ fatalError ||= err;
1798
+ halted = true;
1799
+ active--;
1800
+ pump();
1801
+ return;
1802
+ }
1131
1803
  const o = {
1132
1804
  id: step.id,
1133
1805
  status: "failed",
1134
1806
  taskId: null,
1135
- result: { summary: `Step threw: ${err.message}` },
1807
+ result: { summary: `Step threw: ${safeRunErrorMessage(err)}` },
1136
1808
  };
1137
1809
  resultsById.set(step.id, o);
1138
1810
  res = { outcomes: [o], failed: true };
@@ -1157,6 +1829,98 @@ export async function runPipeline({
1157
1829
 
1158
1830
  // ─── Execution ───────────────────────────────────────────────────────────────
1159
1831
 
1832
+ function normalizeAdmittedRunRecord(
1833
+ value,
1834
+ runtimeWorkflow,
1835
+ definitionAuthority,
1836
+ runAdmission,
1837
+ ) {
1838
+ const record = snapshotAdmittedCanonicalValue(value, "workflow run record");
1839
+ const expectedFields = new Set([
1840
+ "schema",
1841
+ "workflowId",
1842
+ "workflowName",
1843
+ "definitionSchema",
1844
+ "definitionDigest",
1845
+ "runAdmission",
1846
+ "status",
1847
+ "steps",
1848
+ "startedAt",
1849
+ "finishedAt",
1850
+ ]);
1851
+ if (
1852
+ !record ||
1853
+ typeof record !== "object" ||
1854
+ Array.isArray(record) ||
1855
+ Reflect.ownKeys(record).length !== expectedFields.size ||
1856
+ Reflect.ownKeys(record).some(
1857
+ (field) => typeof field !== "string" || !expectedFields.has(field),
1858
+ ) ||
1859
+ record.schema !== COWORK_WORKFLOW_RUN_RECORD_SCHEMA ||
1860
+ record.workflowId !== runtimeWorkflow.id ||
1861
+ record.workflowName !== runtimeWorkflow.name ||
1862
+ record.definitionSchema !== definitionAuthority.schema ||
1863
+ record.definitionDigest !== definitionAuthority.definitionDigest ||
1864
+ !["completed", "failed", "partial"].includes(record.status) ||
1865
+ !Array.isArray(record.steps) ||
1866
+ record.steps.length === 0 ||
1867
+ record.steps.length > MAX_ADMITTED_RUN_STEPS ||
1868
+ typeof record.startedAt !== "string" ||
1869
+ typeof record.finishedAt !== "string" ||
1870
+ !Number.isFinite(Date.parse(record.startedAt)) ||
1871
+ !Number.isFinite(Date.parse(record.finishedAt)) ||
1872
+ new Date(record.startedAt).toISOString() !== record.startedAt ||
1873
+ new Date(record.finishedAt).toISOString() !== record.finishedAt ||
1874
+ Date.parse(record.finishedAt) < Date.parse(record.startedAt) ||
1875
+ canonicalJson(record.runAdmission, "recordRunAdmission") !==
1876
+ canonicalJson(runAdmission, "admittedRunAdmission")
1877
+ ) {
1878
+ throw runResultError("workflow run record has an invalid schema");
1879
+ }
1880
+
1881
+ const outcomeIds = new Set();
1882
+ for (const [index, outcome] of record.steps.entries()) {
1883
+ normalizeAdmittedOutcome(outcome, `workflow run record step ${index}`);
1884
+ if (outcomeIds.has(outcome.id)) {
1885
+ throw runResultError("workflow run record contains duplicate step ids");
1886
+ }
1887
+ outcomeIds.add(outcome.id);
1888
+ }
1889
+
1890
+ for (const step of runtimeWorkflow.steps) {
1891
+ if (step.forEach === undefined) {
1892
+ if (!outcomeIds.has(step.id)) {
1893
+ throw runResultError("workflow run record is missing a step outcome");
1894
+ }
1895
+ continue;
1896
+ }
1897
+ const childPrefix = `${step.id}[`;
1898
+ if (
1899
+ !outcomeIds.has(step.id) &&
1900
+ ![...outcomeIds].some(
1901
+ (outcomeId) =>
1902
+ outcomeId.startsWith(childPrefix) && outcomeId.endsWith("]"),
1903
+ )
1904
+ ) {
1905
+ throw runResultError("workflow run record is missing a forEach outcome");
1906
+ }
1907
+ }
1908
+
1909
+ const expectedStatus = record.steps.every(
1910
+ (outcome) => outcome.status === "completed",
1911
+ )
1912
+ ? "completed"
1913
+ : record.steps.some((outcome) => outcome.status === "completed")
1914
+ ? "partial"
1915
+ : "failed";
1916
+ if (record.status !== expectedStatus) {
1917
+ throw runResultError(
1918
+ "workflow run record status does not match its step outcomes",
1919
+ );
1920
+ }
1921
+ return record;
1922
+ }
1923
+
1160
1924
  /**
1161
1925
  * Execute a workflow. The runner for individual tasks must be injected via
1162
1926
  * `_deps.runTask` (signature matches `runCoworkTask`).
@@ -1186,6 +1950,9 @@ export async function runPipeline({
1186
1950
  * @param {object} [options.llmOptions] - Forwarded to each task
1187
1951
  * @param {function} [options.onStepStart]
1188
1952
  * @param {function} [options.onStepComplete]
1953
+ * @param {object} [options.runAdmission] - Secret-free, digest-bound dynamic
1954
+ * workflow admission. Production entry points require this even though the
1955
+ * low-level executor keeps it optional for existing library callers.
1189
1956
  * @returns {Promise<{
1190
1957
  * workflowId: string,
1191
1958
  * status: "completed"|"failed"|"partial",
@@ -1205,11 +1972,22 @@ export async function executeWorkflow(options = {}) {
1205
1972
  onStepStart,
1206
1973
  onStepComplete,
1207
1974
  pipeline,
1975
+ runAdmission,
1976
+ runTask: requestedRunTask,
1208
1977
  } = options;
1209
1978
 
1210
- const { valid, errors } = validateWorkflow(workflow);
1211
- if (!valid) throw new Error(`Invalid workflow: ${errors.join("; ")}`);
1212
1979
  const definitionAuthority = createWorkflowDefinitionAuthority(workflow);
1980
+ const runtimeWorkflow = definitionAuthority.definition;
1981
+ const { valid, errors } = validateWorkflow(runtimeWorkflow);
1982
+ if (!valid) throw new Error(`Invalid workflow: ${errors.join("; ")}`);
1983
+ const numericMaxParallel = Number(maxParallel);
1984
+ const concurrencyLimit =
1985
+ Number.isSafeInteger(numericMaxParallel) && numericMaxParallel > 0
1986
+ ? numericMaxParallel
1987
+ : 1;
1988
+ const usePipeline = pipeline ?? runtimeWorkflow.pipeline ?? false;
1989
+ const runtimeLlmOptions =
1990
+ runAdmission == null ? llmOptions : normalizeAdmittedLlmOptions(llmOptions);
1213
1991
  if (requestedDefinitionDigest != null) {
1214
1992
  const normalizedDigest = normalizeWorkflowDefinitionDigest(
1215
1993
  requestedDefinitionDigest,
@@ -1221,13 +1999,28 @@ export async function executeWorkflow(options = {}) {
1221
1999
  throw new Error("workflow definition digest mismatch");
1222
2000
  }
1223
2001
  }
1224
- if (typeof _deps.runTask !== "function") {
2002
+ const verifiedRunAdmission =
2003
+ runAdmission == null
2004
+ ? null
2005
+ : normalizeWorkflowRunAdmission(
2006
+ runAdmission,
2007
+ definitionAuthority,
2008
+ concurrencyLimit,
2009
+ {
2010
+ cwd,
2011
+ continueOnError,
2012
+ pipeline: usePipeline,
2013
+ llmOptions: runtimeLlmOptions,
2014
+ },
2015
+ );
2016
+ const runtimeRunTask = requestedRunTask ?? _deps.runTask;
2017
+ if (typeof runtimeRunTask !== "function") {
1225
2018
  throw new Error(
1226
- "cowork-workflow: _deps.runTask is not injected (wire runCoworkTask in CLI before executing)",
2019
+ "cowork-workflow: runTask is not available (wire runCoworkTask before executing)",
1227
2020
  );
1228
2021
  }
1229
2022
 
1230
- const usePipeline = pipeline ?? workflow.pipeline ?? false;
2023
+ const taskSemaphore = createWorkflowTaskSemaphore(concurrencyLimit);
1231
2024
  const resultsById = new Map();
1232
2025
  const startedAt = new Date(_deps.now()).toISOString();
1233
2026
  let stepOutcomes;
@@ -1235,24 +2028,27 @@ export async function executeWorkflow(options = {}) {
1235
2028
 
1236
2029
  if (usePipeline) {
1237
2030
  ({ stepOutcomes, anyFailure } = await runPipeline({
1238
- steps: workflow.steps,
2031
+ steps: runtimeWorkflow.steps,
1239
2032
  resultsById,
1240
- maxParallel,
2033
+ maxParallel: concurrencyLimit,
1241
2034
  continueOnError,
1242
2035
  cwd,
1243
- llmOptions,
2036
+ llmOptions: runtimeLlmOptions,
1244
2037
  onStepStart,
1245
2038
  onStepComplete,
2039
+ taskSemaphore,
2040
+ runTask: runtimeRunTask,
2041
+ admitted: verifiedRunAdmission != null,
1246
2042
  }));
1247
2043
  } else {
1248
2044
  stepOutcomes = [];
1249
2045
  anyFailure = false;
1250
- const batches = planBatches(workflow.steps);
2046
+ const batches = planBatches(runtimeWorkflow.steps);
1251
2047
  for (const batch of batches) {
1252
2048
  // Respect maxParallel by slicing batch into chunks
1253
2049
  const chunks = [];
1254
- for (let i = 0; i < batch.length; i += maxParallel) {
1255
- chunks.push(batch.slice(i, i + maxParallel));
2050
+ for (let i = 0; i < batch.length; i += concurrencyLimit) {
2051
+ chunks.push(batch.slice(i, i + concurrencyLimit));
1256
2052
  }
1257
2053
 
1258
2054
  for (const chunk of chunks) {
@@ -1357,33 +2153,55 @@ export async function executeWorkflow(options = {}) {
1357
2153
 
1358
2154
  const promises = runnable.map(
1359
2155
  async ({ step, message, recordId, isLoop }) => {
1360
- if (onStepStart) onStepStart({ stepId: recordId, message });
1361
2156
  let outcome;
1362
2157
  if (isLoop) {
1363
2158
  outcome = await runLoopStep({
1364
2159
  step,
1365
2160
  recordId,
1366
2161
  cwd,
1367
- llmOptions,
2162
+ llmOptions: runtimeLlmOptions,
1368
2163
  resultsById,
2164
+ taskSemaphore,
2165
+ runTask: runtimeRunTask,
2166
+ onStepStart,
2167
+ admitted: verifiedRunAdmission != null,
1369
2168
  });
1370
2169
  } else {
1371
2170
  const r = await runStepWithRetry({
1372
2171
  step,
1373
2172
  message,
1374
2173
  cwd,
1375
- llmOptions,
2174
+ llmOptions: runtimeLlmOptions,
2175
+ taskSemaphore,
2176
+ runTask: runtimeRunTask,
2177
+ recordId,
2178
+ onTaskStart: onStepStart,
2179
+ admitted: verifiedRunAdmission != null,
1376
2180
  });
1377
- outcome = outcomeFromRetry(recordId, r);
2181
+ outcome = outcomeFromRetry(
2182
+ recordId,
2183
+ r,
2184
+ verifiedRunAdmission != null,
2185
+ );
1378
2186
  }
1379
2187
  if (outcome.status !== "completed") anyFailure = true;
1380
2188
  resultsById.set(recordId, outcome);
1381
- if (onStepComplete) onStepComplete(outcome);
2189
+ notifyWorkflowCallback(
2190
+ onStepComplete,
2191
+ outcome,
2192
+ verifiedRunAdmission != null,
2193
+ "step complete",
2194
+ );
1382
2195
  return outcome;
1383
2196
  },
1384
2197
  );
1385
2198
 
1386
- const results = await Promise.all(promises);
2199
+ const settledResults = await Promise.allSettled(promises);
2200
+ const rejected = settledResults.find(
2201
+ (settled) => settled.status === "rejected",
2202
+ );
2203
+ if (rejected) throw rejected.reason;
2204
+ const results = settledResults.map((settled) => settled.value);
1387
2205
  stepOutcomes.push(...preOutcomes, ...results);
1388
2206
 
1389
2207
  // Aggregate forEach children into a parent entry so downstream
@@ -1410,8 +2228,6 @@ export async function executeWorkflow(options = {}) {
1410
2228
  });
1411
2229
  }
1412
2230
  }
1413
-
1414
- if (anyFailure && !continueOnError) break;
1415
2231
  }
1416
2232
  }
1417
2233
 
@@ -1425,17 +2241,26 @@ export async function executeWorkflow(options = {}) {
1425
2241
 
1426
2242
  const record = {
1427
2243
  schema: COWORK_WORKFLOW_RUN_RECORD_SCHEMA,
1428
- workflowId: workflow.id,
1429
- workflowName: workflow.name,
2244
+ workflowId: runtimeWorkflow.id,
2245
+ workflowName: runtimeWorkflow.name,
1430
2246
  definitionSchema: definitionAuthority.schema,
1431
2247
  definitionDigest: definitionAuthority.definitionDigest,
2248
+ ...(verifiedRunAdmission ? { runAdmission: verifiedRunAdmission } : {}),
1432
2249
  status,
1433
2250
  steps: stepOutcomes,
1434
2251
  startedAt,
1435
2252
  finishedAt,
1436
2253
  };
1437
- _appendHistory(cwd, record);
1438
- return record;
2254
+ const finalRecord = verifiedRunAdmission
2255
+ ? normalizeAdmittedRunRecord(
2256
+ record,
2257
+ runtimeWorkflow,
2258
+ definitionAuthority,
2259
+ verifiedRunAdmission,
2260
+ )
2261
+ : record;
2262
+ _appendHistory(cwd, finalRecord);
2263
+ return finalRecord;
1439
2264
  }
1440
2265
 
1441
2266
  function _appendHistory(cwd, record) {