chainlesschain 0.165.2 → 0.165.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +151 -29
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-lS1R0Cza.js → AIOps-C__nnraW.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-BmlgBPsj.js → ActionButton-CsKWHEZ-.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CNKczv1S.js → Analytics-BhIjfjWO.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-BuZGJARQ.js → AppLayout-BjuXFzWZ.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-Dm73Sl-O.js → Artifacts-k6whDkZJ.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-BGMW7A5e.js → Audit-C4FBMYRh.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BQ6M54JR.js → BackgroundAgents-C_vRrSXk.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DBzGP0bR.js → Backup-CngFZeVp.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-D1mLCoOV.js → BaseInput-B_H9EBRm.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-BUfcN7p8.js → Chat-DLEC5kt7.js} +4 -4
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-Du0Yg5Pi.js → Checkbox-m3ClR4xh.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-B2RXziHW.js → Codegen-CmLSFrgp.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-DWhekOrR.js → Col-DFGQteHN.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-HQhrkon4.js → Community-EUUVORHE.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-izHNb4uz.js → Compact-2PSg0Yr7.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BDwLy41-.js → Compliance-CcKur7gB.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DFJhLa7K.js → Cowork-DK_Fezfa.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-DYY9RG7-.js → Cron-Dbw89xl7.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-rr7tJTtv.js → Crosschain-CH66h_Ec.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-D2sfHRLi.js → DID-D1igRb17.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-q_eGdhha.js → Dashboard-DVAsrobk.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-Cufrh2Bz.js → Dropdown-Dl7W8AQs.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-B-kdv10f.js → EmailListRenderer-D5tpaEQC.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D21XjgfG.js → FamilyGuardDashboard-O_VQf8gq.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-CAwRTLZH.js → Federation-BTVcgS9n.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-vx3daNDF.js → FormItemContext-BRnkJAIz.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-CRWY2wU4.js → GenericCardRenderer-8OWSCkDj.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Q6SwauyD.js → Git-WXLYTQj2.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-BJ1q4IQZ.js → Governance-BezaUhtc.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CSWjROgD.js → Inference-CuRay3Gu.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-fzuQsWuY.js → KnowledgeGraph-DfWLDW1m.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-DVF8Mnns.js → Logs-C55y2pPY.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-BQP7LOA4.js → Marketplace-Dk08Uycg.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-BNlJCmNc.js → McpTools-C11fSB5V.js} +4 -4
  39. package/src/assets/web-panel/assets/{Memory-DLLg5WQ4.js → Memory-mr0eZc97.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-CR2skNAF.js → MobileBridge-C0iCSRbb.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-BxVavObl.js → MobileProjects-CY-xMG8Z.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-B17pgqMI.js → Mtc-Bplqc_Wc.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-4UmV5cIL.js → MtcAudit-BlzwsoSM.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-DoMEXEcU.js → Multisig-BBLmTty6.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-h9DMXFX4.js → NLProgramming-DtT1uf1W.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-Cihn-xX8.js → Notes-DcqQAxTK.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-BE6ZGa3R.js → NotificationSettings-cb0kWoZe.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-CnwojoSI.js → OrderTableRenderer-kw9HZQNN.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-B2vpwmjb.js → Organization-DlTlbkUN.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-C4z28R0-.js → Overflow-pp1qs-oY.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BVlFkwhW.js → P2P-BESLnyYz.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-BXbEdL5B.js → PdhVaultBrowser-B-J1mGuf.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-C28xs4lp.js → Permissions-B36PeT1_.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-Dgl_rBjM.js → PersonalDataHub-CTK2ad-3.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-3QUIhrKI.js → Pipeline-zJNGXsac.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-8Gh33OZ5.js → Privacy-b9fz0Qf0.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-BNmg4tZi.js → ProjectInit-DjZbZgiB.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-vgMfeCf9.js → ProjectSettings-C-hHJXlD.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-CXoOa8eX.js → Providers-Dz-CAYYU.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-BhfTF0oS.js → QrScannerModal-A0yoQ3-3.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-C746HEAj.js → QuickAsk-DrFgFJNC.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-BSej8Eo0.js → Recommend-KC8hF1g4.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-f3gmnlG3.js → RemoteSession-DGTPIk6u.js} +3 -3
  65. package/src/assets/web-panel/assets/{Reputation-CyvybpkN.js → Reputation-QmbwM7Yk.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DwV2PeYn.js → Row-BxjJylia.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-11GrzjZy.js → RssFeed-B7Wda1J6.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-BjKXkrAY.js → Search-_plQ_YSA.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-eWiFb9Yk.js → Security-CD2YJZfx.js} +2 -2
  70. package/src/assets/web-panel/assets/{Services-CdCsMAtt.js → Services-qQE7NmwE.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-4Q2UTKQ5.js → Skeleton-DgQqkFf7.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-v4rtYrRj.js → Skills-C4C0oFFt.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-CRhsFvd_.js → Sla-Px2N2B5c.js} +1 -1
  74. package/src/assets/web-panel/assets/SpeechSettings-DN6C3lQd.js +1 -0
  75. package/src/assets/web-panel/assets/{SyncSettings-D16zonl7.js → SyncSettings-jm5aH_m7.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-Xxozz0i_.js → Tasks-DlkJcv67.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-CXJpPtCT.js → Templates-DyrTHUBs.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-XRV0L1ZF.js → Tenant-mC434PtL.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-CYuO1i_w.js → Terminal-o0_s3NgK.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-BXqitWsu.js → TimelineRenderer-DAGlAXOw.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BvPrDHIy.js → Tokens-BnulS_bs.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DhCrSTf5.js → Trigger-suQbVXYM.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DmsuZD6K.js → Trust-DbIJAPo5.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-D_yrts5C.js → UkeySign-CQNpbIHi.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-B01ihPrm.js → VideoEditing-D8aWCERk.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-DpUjKa-N.js → Wallet-CuLpYfZc.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CL07yON6.js → WebAuthn-CsfG8ZMc.js} +2 -2
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CLMflnRr.js → WorkflowEditor-BYsEAZbd.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-DtUqe8h_.js → chat-FT4Um3-I.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-DPKrgVfy.js → colors-CKbS_Wlt.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-BTt2GtOm.js → compact-item-CFJtzuJM.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-D_VeUc1J.js → createContext-B9kSRW6K.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-1kndiylj.js → hasIn-DR-x0Xpi.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-dVc-CXY4.js → index-7jHr_A_k.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DFIZ_1Sf.js → index-B3ZX81mK.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-fk2Rjacw.js → index-B7osrXqy.js} +3 -3
  98. package/src/assets/web-panel/assets/{index-CHG-KcS4.js → index-BGbPnC5x.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-Dbbymxpj.js → index-BI5hP_qy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BaZAbw9A.js → index-BK3TiKVq.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CzjkKUmR.js → index-BVPU43b1.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BAXfRKfG.js → index-BW-t9n-q.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-8nz_fmlj.js → index-BfFCQHL3.js} +1 -1
  104. package/src/assets/web-panel/assets/index-Bizb4hkw.js +1 -0
  105. package/src/assets/web-panel/assets/{index-DdkplL9R.js → index-Bt4fS2eL.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DG2MZQ3f.js → index-BwR7yc2h.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DfKBRdSS.js → index-Bzz0C1BZ.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-GsoUQ2M7.js → index-C2JvmYRc.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-l1BETrDb.js → index-CGaXUsIs.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-4bna2_SR.js → index-CJmIQ9XR.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DfGl8CqG.js → index-C_enk9I_.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-D95OFht7.js → index-Cr2FLUsD.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DsJCDUJG.js → index-CvrCCqJt.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-sDoMHTNA.js → index-D4LfPvWA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-By5n8up9.js → index-DMGmg497.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-ocf06EVQ.js → index-DShSkww-.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Bhudc_Ez.js → index-DTxNy0ZU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CX81qSM4.js → index-DW4BWjTk.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BlDNVgBt.js → index-DWdqx7zI.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CcO04eDn.js → index-Dap9c3eD.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Dwqmtk65.js → index-DcfxhKXR.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-2eyAXE3d.js → index-DdkIhfi-.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-brCInrzq.js → index-DeSgw-WV.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DJ8vXGOa.js → index-DiNUEpSQ.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BHpBMWCh.js → index-DjEP7WkH.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-m31Ka8hZ.js → index-DtkFqPVA.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CtF7EN49.js → index-KqDaCApw.js} +1 -1
  128. package/src/assets/web-panel/assets/index-OHSl-xCh.js +1 -0
  129. package/src/assets/web-panel/assets/{index-B9KvcS7R.js → index-OSzjTzGM.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CauoBQkH.js → index-QBSeNVO3.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-FzdwcSy2.js → index-WE08gMJP.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DxdTdBix.js → index-r2VI1Ikk.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BepLZwgB.js → index-tqSEHPRT.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-CvyYJd7z.js → initDefaultProps-Dd7n7VgI.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-DYZkhUdz.js → motion-BwxouRw1.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-slnJRVT1.js → move-DObN0PG9.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-DVc0CKmy.js → mtc-parser-B3nOUQc7.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BCXAtHCq.js → omit-o9WU2Nbl.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-DBsemz_K.js → pickAttrs-CL_vvfQ_.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BRtkJ-nv.js → placementArrow-CYrM2tyK.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-Cebq7EZn.js → responsiveObserve-k_06cQ96.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-DT7qvId2.js → slide-CPKG0pxS.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-pQiITLkG.js → statusUtils-BDDEeLTw.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-c-19BVKQ.js → styleChecker-Cmb7RTh7.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-DsS5-0bE.js → useFlexGapSupport-e2IedC4N.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-CxGiMe5n.js → useFs-CE_gxVnm.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-D9btVID5.js → usePersonalDataHub-BkMIIKMz.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-CUYFsFEN.js → vnode-DW886XV1.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CXmn_35j.js → zoom-Dwo_adaf.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +3 -3
  152. package/src/commands/agent.js +105 -35
  153. package/src/commands/artifacts.js +363 -33
  154. package/src/commands/cowork.js +202 -36
  155. package/src/commands/session-budget.js +155 -0
  156. package/src/commands/session-location.js +948 -0
  157. package/src/commands/session.js +72 -46
  158. package/src/data/changelog.json +26 -0
  159. package/src/gateways/ws/artifact-protocol.js +77 -13
  160. package/src/gateways/ws/session-protocol.js +391 -24
  161. package/src/gateways/ws/ws-agent-handler.js +191 -14
  162. package/src/gateways/ws/ws-server.js +14 -3
  163. package/src/gateways/ws/ws-session-gateway.js +101 -5
  164. package/src/harness/jsonl-session-store.js +1210 -0
  165. package/src/lib/agent-team/team-runner.js +43 -13
  166. package/src/lib/artifact-access-ledger.js +461 -0
  167. package/src/lib/artifact-cleanup-ledger.js +861 -0
  168. package/src/lib/artifact-deletion-ledger.js +677 -0
  169. package/src/lib/artifact-store.js +256 -51
  170. package/src/lib/background-agent-keeper-protocol.js +75 -4
  171. package/src/lib/background-agent-launch-barrier.js +74 -0
  172. package/src/lib/background-agent-supervisor.js +149 -5
  173. package/src/lib/background-turn-bootstrap-protocol.js +4 -0
  174. package/src/lib/command-lifecycle-report.js +8 -4
  175. package/src/lib/cowork-adapter.js +9 -5
  176. package/src/lib/cowork-task-runner.js +20 -0
  177. package/src/lib/cowork-workflow.js +178 -20
  178. package/src/lib/direct-model-usage.js +62 -22
  179. package/src/lib/dynamic-workflow-runtime.js +3184 -111
  180. package/src/lib/execution-location-result-apply.js +339 -0
  181. package/src/lib/execution-location-result-artifact.js +271 -0
  182. package/src/lib/execution-location-result-review.js +253 -0
  183. package/src/lib/execution-location-result-store.js +331 -0
  184. package/src/lib/execution-location-result.js +617 -0
  185. package/src/lib/execution-location-target.js +236 -1
  186. package/src/lib/managed-tool-checkpoint.js +47 -4
  187. package/src/lib/process-execution-broker/workspace-transaction.js +3 -0
  188. package/src/lib/session-budget-production-root.js +376 -0
  189. package/src/lib/session-budget-runtime.js +109 -18
  190. package/src/lib/session-budget-usage.js +63 -0
  191. package/src/lib/session-host-snapshot.js +19 -2
  192. package/src/lib/session-resource-budget.js +766 -105
  193. package/src/lib/sub-agent-context.js +69 -22
  194. package/src/lib/web-ui-server.js +12 -1
  195. package/src/lib/with-file-lock.js +175 -26
  196. package/src/repl/agent-repl.js +237 -16
  197. package/src/runtime/agent-core.js +123 -0
  198. package/src/runtime/headless-runner.js +135 -46
  199. package/src/runtime/headless-stream.js +133 -25
  200. package/src/runtime/policies/agent-policy.js +10 -0
  201. package/src/workers/background-agent-keeper.js +112 -19
  202. package/src/workers/background-agent-worker.js +383 -134
  203. package/src/workers/background-turn-bootstrap.js +13 -8
  204. package/src/assets/web-panel/assets/ChatBubbleRenderer-CY5m8ZHg.js +0 -1
  205. package/src/assets/web-panel/assets/MarkdownRenderer-BLif4QSg.js +0 -1
  206. package/src/assets/web-panel/assets/Projects-BhtAVilc.js +0 -1
  207. package/src/assets/web-panel/assets/SpeechSettings-DLjKsDVu.js +0 -1
  208. package/src/assets/web-panel/assets/devWarning-Bj5nUW2v.js +0 -1
  209. package/src/assets/web-panel/assets/index-CPcM-eqo.js +0 -1
  210. package/src/assets/web-panel/assets/index-xeSyVoRe.js +0 -1
@@ -79,6 +79,110 @@ export const PID_IDENTITY_TOLERANCE_MS = 60000;
79
79
  export const PROCESS_START_TIME_CACHE_TTL_MS = 10_000;
80
80
  export const PROCESS_START_TIME_CACHE_MAX_ENTRIES = 4_096;
81
81
 
82
+ // Linux exposes these fields directly through procfs. `/proc/<pid>/stat`
83
+ // reports starttime in USER_HZ units, which Linux defines as 100 ticks/second
84
+ // for this userspace ABI independently of the kernel scheduler frequency.
85
+ const LINUX_PROC_CLOCK_TICKS_PER_SECOND = 100;
86
+ let linuxBootTimeMsCache;
87
+
88
+ export function parseLinuxProcStat(text, bootTimeMs = null) {
89
+ const value = String(text || "").trim();
90
+ const open = value.indexOf("(");
91
+ const close = value.lastIndexOf(")");
92
+ if (open <= 0 || close <= open || value.charAt(close + 1) !== " ") {
93
+ return null;
94
+ }
95
+ const pid = Number(value.slice(0, open).trim());
96
+ const fields = value
97
+ .slice(close + 2)
98
+ .trim()
99
+ .split(/\s+/u);
100
+ const state = fields[0];
101
+ const processGroupId = Number(fields[2]);
102
+ const startTimeTicks = Number(fields[19]);
103
+ if (
104
+ !Number.isSafeInteger(pid) ||
105
+ pid <= 0 ||
106
+ !/^[A-Z]$/iu.test(state || "") ||
107
+ !Number.isSafeInteger(processGroupId) ||
108
+ processGroupId <= 0 ||
109
+ !Number.isFinite(startTimeTicks) ||
110
+ startTimeTicks < 0
111
+ ) {
112
+ return null;
113
+ }
114
+ const boot = Number(bootTimeMs);
115
+ return {
116
+ pid,
117
+ state: state.toUpperCase(),
118
+ processGroupId,
119
+ startTimeTicks,
120
+ startedAtMs:
121
+ Number.isFinite(boot) && boot > 0
122
+ ? boot + (startTimeTicks * 1000) / LINUX_PROC_CLOCK_TICKS_PER_SECOND
123
+ : null,
124
+ };
125
+ }
126
+
127
+ function linuxBootTimeMs() {
128
+ if (linuxBootTimeMsCache !== undefined) return linuxBootTimeMsCache;
129
+ try {
130
+ const match = /^btime\s+(\d+)\s*$/mu.exec(
131
+ readFileSync("/proc/stat", "utf8"),
132
+ );
133
+ const seconds = match ? Number(match[1]) : null;
134
+ linuxBootTimeMsCache =
135
+ Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : null;
136
+ } catch {
137
+ linuxBootTimeMsCache = null;
138
+ }
139
+ return linuxBootTimeMsCache;
140
+ }
141
+
142
+ function readLinuxProcProcess(pid) {
143
+ const target = Number(pid);
144
+ if (!Number.isSafeInteger(target) || target <= 0) {
145
+ return { status: "unknown", process: null };
146
+ }
147
+ try {
148
+ const parsed = parseLinuxProcStat(
149
+ readFileSync(`/proc/${target}/stat`, "utf8"),
150
+ linuxBootTimeMs(),
151
+ );
152
+ return parsed
153
+ ? { status: "present", process: parsed }
154
+ : { status: "unknown", process: null };
155
+ } catch (error) {
156
+ return ["ENOENT", "ESRCH"].includes(error?.code)
157
+ ? { status: "absent", process: null }
158
+ : { status: "unknown", process: null };
159
+ }
160
+ }
161
+
162
+ function readLinuxProcProcessGroupStates(pgid) {
163
+ const target = Number(pgid);
164
+ if (!Number.isSafeInteger(target) || target <= 0) return null;
165
+ let entries;
166
+ try {
167
+ entries = readdirSync("/proc");
168
+ } catch {
169
+ return null;
170
+ }
171
+ const states = [];
172
+ for (const entry of entries) {
173
+ if (!/^\d+$/u.test(entry)) continue;
174
+ const observed = readLinuxProcProcess(Number(entry));
175
+ if (observed.status === "unknown") return null;
176
+ if (
177
+ observed.status === "present" &&
178
+ observed.process.processGroupId === target
179
+ ) {
180
+ states.push(observed.process.state);
181
+ }
182
+ }
183
+ return states;
184
+ }
185
+
82
186
  /**
83
187
  * A bounded FIFO/TTL cache for process creation-time probes.
84
188
  *
@@ -263,6 +367,13 @@ function defaultReadProcessStartTimeMs(pid) {
263
367
  }
264
368
  return null;
265
369
  }
370
+ if (process.platform === "linux") {
371
+ const observed = readLinuxProcProcess(target);
372
+ if (observed.status === "absent") return { status: "absent" };
373
+ if (Number.isFinite(observed.process?.startedAtMs)) {
374
+ return observed.process.startedAtMs;
375
+ }
376
+ }
266
377
  // POSIX: `ps -o lstart=` gives an absolute start timestamp on Linux + macOS
267
378
  // (procps and BSD ps both support it; busybox ps does not → null → open).
268
379
  try {
@@ -300,6 +411,12 @@ function defaultReadProcessState(pid) {
300
411
  ) {
301
412
  return null;
302
413
  }
414
+ if (process.platform === "linux") {
415
+ const observed = readLinuxProcProcess(target);
416
+ if (observed.status !== "unknown") {
417
+ return observed.process?.state || null;
418
+ }
419
+ }
303
420
  try {
304
421
  const result = runSupervisorCommand(
305
422
  "ps",
@@ -335,6 +452,10 @@ function defaultReadProcessGroupStates(pgid) {
335
452
  ) {
336
453
  return null;
337
454
  }
455
+ if (process.platform === "linux") {
456
+ const states = readLinuxProcProcessGroupStates(target);
457
+ if (Array.isArray(states)) return states;
458
+ }
338
459
  try {
339
460
  const result = runSupervisorCommand(
340
461
  "ps",
@@ -1128,7 +1249,7 @@ function mergeBackgroundAgentState(current, requested) {
1128
1249
  return next;
1129
1250
  }
1130
1251
 
1131
- function persistBackgroundAgentState(target, next) {
1252
+ function persistBackgroundAgentState(target, next, options = {}) {
1132
1253
  // Multiple async state writers can run in one process; a pid-only temporary
1133
1254
  // name lets them overwrite each other's staging file. A unique suffix keeps
1134
1255
  // each atomic replacement independent. Windows scanners/readers can also
@@ -1136,6 +1257,21 @@ function persistBackgroundAgentState(target, next) {
1136
1257
  // transient class rather than surfacing a spurious EPERM background failure.
1137
1258
  const tmp = `${target}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
1138
1259
  writeFileSync(tmp, JSON.stringify(next, null, 2), { mode: 0o600 });
1260
+ try {
1261
+ if (
1262
+ typeof options.publishReleaseAfterPathRemoved === "function" &&
1263
+ options.publishReleaseAfterPathRemoved(tmp) !== true
1264
+ ) {
1265
+ const error = new Error(
1266
+ `Could not publish state-lock release handoff: ${target}`,
1267
+ );
1268
+ error.code = "STATE_LOCK_RELEASE_HANDOFF_FAILED";
1269
+ throw error;
1270
+ }
1271
+ } catch (error) {
1272
+ rmSync(tmp, { force: true });
1273
+ throw error;
1274
+ }
1139
1275
  for (let attempt = 0; ; attempt++) {
1140
1276
  try {
1141
1277
  renameSync(tmp, target);
@@ -1179,7 +1315,7 @@ export function mutateBackgroundAgentState(id, updater, options = {}) {
1179
1315
  const target = statePath(safe);
1180
1316
  return withFileLock(
1181
1317
  target,
1182
- () => {
1318
+ ({ publishReleaseAfterPathRemoved }) => {
1183
1319
  const read = readBackgroundAgentStateResult(safe);
1184
1320
  if (read.error) throw read.error;
1185
1321
  if (read.missing && options.createIfMissing !== true) {
@@ -1193,7 +1329,9 @@ export function mutateBackgroundAgentState(id, updater, options = {}) {
1193
1329
  ...requested,
1194
1330
  id: safe,
1195
1331
  });
1196
- persistBackgroundAgentState(target, next);
1332
+ persistBackgroundAgentState(target, next, {
1333
+ publishReleaseAfterPathRemoved,
1334
+ });
1197
1335
  return { applied: true, state: next, previous: read.state };
1198
1336
  },
1199
1337
  {
@@ -2976,8 +3114,14 @@ function recordedBackgroundProcessesAlive(state) {
2976
3114
  );
2977
3115
  }
2978
3116
 
2979
- export function isBackgroundProcessTreeExecutionAlive(pid, startedAt) {
2980
- const identity = inspectDestructiveProcessIdentity(pid, startedAt);
3117
+ export function isBackgroundProcessTreeExecutionAlive(
3118
+ pid,
3119
+ startedAt,
3120
+ options = {},
3121
+ ) {
3122
+ const identity = inspectDestructiveProcessIdentity(pid, startedAt, {
3123
+ processGroup: options.processGroup !== false,
3124
+ });
2981
3125
  // `unverifiable` includes a vanished/zombie POSIX leader whose process group
2982
3126
  // is still executable or whose group snapshot failed. Both must block
2983
3127
  // worktree deletion just as a verified live leader does.
@@ -11,6 +11,10 @@ export const BACKGROUND_TURN_BOOTSTRAP_READY =
11
11
  "background-turn-bootstrap-ready";
12
12
  export const BACKGROUND_TURN_BOOTSTRAP_RELEASE =
13
13
  "background-turn-bootstrap-release";
14
+ // The formal launch gate permits 120 seconds for a 20-worker hosted runner to
15
+ // become ready. Keep the pre-main barrier alive for that same bounded window;
16
+ // worker/IPC death still fails immediately through the disconnect listener.
17
+ export const BACKGROUND_TURN_BOOTSTRAP_RELEASE_TIMEOUT_MS = 120_000;
14
18
 
15
19
  export const BACKGROUND_TURN_BOOTSTRAP_ENV = Object.freeze({
16
20
  nonce: "CC_BACKGROUND_TURN_BOOTSTRAP_NONCE",
@@ -38,12 +38,16 @@ export const COMMAND_ALIAS_DECISION_POLICY = Object.freeze({
38
38
  requiredPlatforms: Object.freeze(["linux", "macos", "windows"]),
39
39
  });
40
40
 
41
- // This repository-owned anchor intentionally remains unconfigured until the
42
- // first public release containing the DELTA lifecycle exporter is published.
43
- // Updating it requires a reviewed code change with all three exact values.
41
+ // Repository-owned anchor for the first public release containing the complete
42
+ // DELTA lifecycle exporter and repository-probe hardening. Coverage must begin
43
+ // at this exact npm publication boundary; report input cannot override it.
44
44
  export const COMMAND_LIFECYCLE_DELTA_RELEASE_POLICY = Object.freeze({
45
45
  schema: "chainlesschain.command-lifecycle-delta-release-policy.v1",
46
- firstPublicRelease: null,
46
+ firstPublicRelease: Object.freeze({
47
+ version: "0.163.4",
48
+ commitSha: "27ed0ac2005e16ce5ddff53990e85b1d13ea0b1d",
49
+ npmPublishedAt: "2026-08-10T19:42:11.053Z",
50
+ }),
47
51
  incompatibleReleases: Object.freeze(["0.162.198", "0.163.3"]),
48
52
  });
49
53
 
@@ -108,8 +108,9 @@ async function invokeChatCall(callWrapper, provider, model, call) {
108
108
  * @param {string} [options.apiKey] - API key override
109
109
  * @param {(request: {call: function, provider: string, model: string}) => Promise<object>} [options.callWrapper]
110
110
  * Optional host boundary around each real provider call. The callback
111
- * resolves to a private `{content, usage?}` envelope; callers still
112
- * receive only the response string.
111
+ * may invoke `call({ signal })` to bind cancellation and resolves to a
112
+ * private `{content, usage?}` envelope; callers still receive only the
113
+ * response string.
113
114
  * @returns {(messages: object[], opts?: object) => Promise<string>}
114
115
  */
115
116
  export function createChatFn(options = {}) {
@@ -145,9 +146,10 @@ export function createChatFn(options = {}) {
145
146
  callWrapper,
146
147
  provider,
147
148
  currentModel,
148
- async () => {
149
+ async ({ signal = opts.signal || resolved.signal } = {}) => {
149
150
  const res = await fetch(`${baseUrl}/api/chat`, {
150
151
  method: "POST",
152
+ signal,
151
153
  headers: { "Content-Type": "application/json" },
152
154
  body: JSON.stringify({
153
155
  model: currentModel,
@@ -185,9 +187,10 @@ export function createChatFn(options = {}) {
185
187
  callWrapper,
186
188
  provider,
187
189
  currentModel,
188
- async () => {
190
+ async ({ signal = opts.signal || resolved.signal } = {}) => {
189
191
  const res = await fetch(`${baseUrl}/messages`, {
190
192
  method: "POST",
193
+ signal,
191
194
  headers: {
192
195
  "Content-Type": "application/json",
193
196
  "x-api-key": key,
@@ -214,9 +217,10 @@ export function createChatFn(options = {}) {
214
217
  callWrapper,
215
218
  provider,
216
219
  currentModel,
217
- async () => {
220
+ async ({ signal = opts.signal || resolved.signal } = {}) => {
218
221
  const res = await fetch(`${baseUrl}/chat/completions`, {
219
222
  method: "POST",
223
+ signal,
220
224
  headers: {
221
225
  "Content-Type": "application/json",
222
226
  Authorization: `Bearer ${key}`,
@@ -518,8 +518,12 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
518
518
  * @param {string} [options.mcpSessionId] - Stable MCP ledger session for resume
519
519
  * @param {string} [options.workflowEffectId] - Canonical durable workflow effect
520
520
  * @param {boolean} [options.strictUsageTelemetry] - Require synchronous durable call observers
521
+ * @param {boolean} [options.managedCheckpoint] - Wrap mutating child tools in workspace transactions
522
+ * @param {string|null} [options.managedCheckpointStateDir] - Optional transaction state root
523
+ * @param {string[]} [options.managedCheckpointExclusions] - Explicit uncovered workspace paths
521
524
  * @param {function} [options.onUsageBoundary] - Provider-call durable start observer
522
525
  * @param {function} [options.onUsageSettlement] - Provider-call durable settlement observer
526
+ * @param {function} [options.onProviderReceipt] - Provider receipt durable prewrite observer
523
527
  * @param {function} [options.onToolCallBoundary] - Tool-call durable start observer
524
528
  * @param {function} [options.onToolCallSettlement] - Tool-call durable settlement observer
525
529
  * @param {(request: object) => boolean|Promise<boolean>} [options.approveMcpLocalCodeExecution]
@@ -540,8 +544,12 @@ export async function runCoworkTask(options = {}) {
540
544
  mcpSessionId = null,
541
545
  workflowEffectId = null,
542
546
  strictUsageTelemetry = false,
547
+ managedCheckpoint = false,
548
+ managedCheckpointStateDir = null,
549
+ managedCheckpointExclusions = [],
543
550
  onUsageBoundary = null,
544
551
  onUsageSettlement = null,
552
+ onProviderReceipt = null,
545
553
  onToolCallBoundary = null,
546
554
  onToolCallSettlement = null,
547
555
  approveMcpLocalCodeExecution = null,
@@ -640,6 +648,7 @@ export async function runCoworkTask(options = {}) {
640
648
  strictUsageTelemetry: true,
641
649
  onUsageBoundary,
642
650
  onUsageSettlement,
651
+ onProviderReceipt,
643
652
  onToolCallBoundary,
644
653
  onToolCallSettlement,
645
654
  }
@@ -678,6 +687,17 @@ export async function runCoworkTask(options = {}) {
678
687
  mcpRuntime.sessionId && mcpRuntime.bindingAllowed
679
688
  ? { sessionId: mcpRuntime.sessionId }
680
689
  : {};
690
+ if (managedCheckpoint === true) {
691
+ loopOptions.managedCheckpoint = true;
692
+ if (managedCheckpointStateDir) {
693
+ loopOptions.managedCheckpointStateDir = managedCheckpointStateDir;
694
+ }
695
+ if (Array.isArray(managedCheckpointExclusions)) {
696
+ loopOptions.managedCheckpointExclusions = [
697
+ ...managedCheckpointExclusions,
698
+ ];
699
+ }
700
+ }
681
701
  if (
682
702
  Array.isArray(template.shellPolicyOverrides) &&
683
703
  template.shellPolicyOverrides.length
@@ -59,6 +59,9 @@ import { canonicalJson } from "./scheduler-kernel/contract.js";
59
59
  /** Maximum number of items a single forEach step can expand into. */
60
60
  export const MAX_FAN_OUT = 500;
61
61
 
62
+ /** Maximum declarative choices accepted by one durable stage input gate. */
63
+ export const MAX_STAGE_INPUT_OPTIONS = 32;
64
+
62
65
  /** Absolute ceiling on a single loop step's iterations (infinite-loop guard). */
63
66
  export const MAX_LOOP_ITERATIONS = 100;
64
67
  export const COWORK_WORKFLOW_RUN_RECORD_SCHEMA = "cc-cowork-workflow-run/v1";
@@ -832,6 +835,53 @@ export function validateWorkflow(wf) {
832
835
  if (s.when !== undefined && typeof s.when !== "string") {
833
836
  errors.push(`steps[${i}].when must be a string expression`);
834
837
  }
838
+ if (s.needsInput !== undefined) {
839
+ const input = s.needsInput;
840
+ const inputKeys =
841
+ input && typeof input === "object" && !Array.isArray(input)
842
+ ? Object.keys(input)
843
+ : [];
844
+ if (
845
+ !input ||
846
+ typeof input !== "object" ||
847
+ Array.isArray(input) ||
848
+ inputKeys.some(
849
+ (key) => !["prompt", "options", "multiSelect"].includes(key),
850
+ ) ||
851
+ typeof input.prompt !== "string" ||
852
+ input.prompt.trim() !== input.prompt ||
853
+ input.prompt.length === 0 ||
854
+ input.prompt.length > 4096
855
+ ) {
856
+ errors.push(`steps[${i}].needsInput has an invalid prompt schema`);
857
+ } else {
858
+ if (
859
+ input.options !== undefined &&
860
+ (!Array.isArray(input.options) ||
861
+ input.options.length === 0 ||
862
+ input.options.length > MAX_STAGE_INPUT_OPTIONS ||
863
+ input.options.some(
864
+ (option) =>
865
+ typeof option !== "string" ||
866
+ option.trim() !== option ||
867
+ option.length === 0 ||
868
+ option.length > 512,
869
+ ) ||
870
+ new Set(input.options).size !== input.options.length)
871
+ ) {
872
+ errors.push(`steps[${i}].needsInput.options is invalid`);
873
+ }
874
+ if (
875
+ input.multiSelect !== undefined &&
876
+ typeof input.multiSelect !== "boolean"
877
+ ) {
878
+ errors.push(`steps[${i}].needsInput.multiSelect must be boolean`);
879
+ }
880
+ if (input.multiSelect === true && !Array.isArray(input.options)) {
881
+ errors.push(`steps[${i}].needsInput.multiSelect requires options`);
882
+ }
883
+ }
884
+ }
835
885
  if (s.forEach !== undefined) {
836
886
  const f = s.forEach;
837
887
  const ok =
@@ -1076,6 +1126,69 @@ export function substitutePlaceholders(template, resultsById) {
1076
1126
  );
1077
1127
  }
1078
1128
 
1129
+ function normalizeResolvedStageInput(value, spec) {
1130
+ const answer = snapshotAdmittedCanonicalValue(
1131
+ value,
1132
+ "workflow stage input answer",
1133
+ { maxNodes: 256, maxBytes: 64 * 1024 },
1134
+ );
1135
+ const options = Array.isArray(spec.options) ? spec.options : null;
1136
+ if (spec.multiSelect === true) {
1137
+ if (
1138
+ !Array.isArray(answer) ||
1139
+ answer.length === 0 ||
1140
+ answer.length > MAX_STAGE_INPUT_OPTIONS ||
1141
+ answer.some((item) => typeof item !== "string") ||
1142
+ new Set(answer).size !== answer.length ||
1143
+ answer.some((item) => !options.includes(item))
1144
+ ) {
1145
+ throw runResultError("workflow stage input answer is invalid");
1146
+ }
1147
+ return answer;
1148
+ }
1149
+ if (
1150
+ typeof answer !== "string" ||
1151
+ answer.length === 0 ||
1152
+ answer.length > 64 * 1024 ||
1153
+ (options && !options.includes(answer))
1154
+ ) {
1155
+ throw runResultError("workflow stage input answer is invalid");
1156
+ }
1157
+ return answer;
1158
+ }
1159
+
1160
+ async function resolveStageInputMessage(
1161
+ step,
1162
+ message,
1163
+ resultsById,
1164
+ resolveInput,
1165
+ ) {
1166
+ if (step.needsInput === undefined) return message;
1167
+ if (typeof resolveInput !== "function") {
1168
+ const error = new Error(
1169
+ `workflow step '${step.id}' requires a durable input resolver`,
1170
+ );
1171
+ error.code = "COWORK_WORKFLOW_INPUT_RESOLVER_REQUIRED";
1172
+ throw error;
1173
+ }
1174
+ const spec = step.needsInput;
1175
+ const prompt = substitutePlaceholders(spec.prompt, resultsById);
1176
+ const answer = normalizeResolvedStageInput(
1177
+ await resolveInput(
1178
+ Object.freeze({
1179
+ stepId: step.id,
1180
+ prompt,
1181
+ options: Array.isArray(spec.options)
1182
+ ? Object.freeze([...spec.options])
1183
+ : null,
1184
+ multiSelect: spec.multiSelect === true,
1185
+ }),
1186
+ ),
1187
+ spec,
1188
+ );
1189
+ return `${message}\n\n## Bound user input for stage ${step.id}\n${canonicalJson(answer, "workflowStageInput")}`;
1190
+ }
1191
+
1079
1192
  // ─── Persistence ─────────────────────────────────────────────────────────────
1080
1193
 
1081
1194
  function _storedWorkflowError(message) {
@@ -1600,6 +1713,7 @@ export async function runStepNode(step, ctx) {
1600
1713
  onStepComplete,
1601
1714
  taskSemaphore,
1602
1715
  runTask,
1716
+ resolveInput,
1603
1717
  admitted = false,
1604
1718
  } = ctx;
1605
1719
  const recordId = step.id;
@@ -1620,9 +1734,20 @@ export async function runStepNode(step, ctx) {
1620
1734
  }
1621
1735
  if (!runThis) return single("skipped", "when-condition false");
1622
1736
 
1623
- if (isLoopStep(step)) {
1737
+ const resolvedMessage = await resolveStageInputMessage(
1738
+ step,
1739
+ step.message,
1740
+ resultsById,
1741
+ resolveInput,
1742
+ );
1743
+ const activeStep =
1744
+ resolvedMessage === step.message
1745
+ ? step
1746
+ : { ...step, message: resolvedMessage };
1747
+
1748
+ if (isLoopStep(activeStep)) {
1624
1749
  const o = await runLoopStep({
1625
- step,
1750
+ step: activeStep,
1626
1751
  recordId,
1627
1752
  cwd,
1628
1753
  llmOptions,
@@ -1640,10 +1765,10 @@ export async function runStepNode(step, ctx) {
1640
1765
  };
1641
1766
  }
1642
1767
 
1643
- if (step.forEach !== undefined) {
1768
+ if (activeStep.forEach !== undefined) {
1644
1769
  let items;
1645
1770
  try {
1646
- items = resolveForEachItems(step.forEach, resultsById);
1771
+ items = resolveForEachItems(activeStep.forEach, resultsById);
1647
1772
  } catch (err) {
1648
1773
  return single("failed", err.message);
1649
1774
  }
@@ -1651,10 +1776,10 @@ export async function runStepNode(step, ctx) {
1651
1776
  const children = await Promise.all(
1652
1777
  items.map(async (item, k) => {
1653
1778
  const childId = `${recordId}[${k}]`;
1654
- const withItem = substituteItem(step.message, item);
1779
+ const withItem = substituteItem(activeStep.message, item);
1655
1780
  const msg = substitutePlaceholders(withItem, resultsById);
1656
1781
  const r = await runStepWithRetry({
1657
- step,
1782
+ step: activeStep,
1658
1783
  message: msg,
1659
1784
  cwd,
1660
1785
  llmOptions,
@@ -1687,9 +1812,9 @@ export async function runStepNode(step, ctx) {
1687
1812
  };
1688
1813
  }
1689
1814
 
1690
- const message = substitutePlaceholders(step.message, resultsById);
1815
+ const message = substitutePlaceholders(activeStep.message, resultsById);
1691
1816
  const r = await runStepWithRetry({
1692
- step,
1817
+ step: activeStep,
1693
1818
  message,
1694
1819
  cwd,
1695
1820
  llmOptions,
@@ -1724,6 +1849,7 @@ export async function runPipeline({
1724
1849
  onStepComplete,
1725
1850
  taskSemaphore,
1726
1851
  runTask = _deps.runTask,
1852
+ resolveInput,
1727
1853
  admitted = false,
1728
1854
  }) {
1729
1855
  const limit = Math.max(1, Math.floor(maxParallel) || 1);
@@ -1787,6 +1913,7 @@ export async function runPipeline({
1787
1913
  onStepComplete,
1788
1914
  taskSemaphore,
1789
1915
  runTask,
1916
+ resolveInput,
1790
1917
  admitted,
1791
1918
  });
1792
1919
  } catch (err) {
@@ -1950,6 +2077,7 @@ function normalizeAdmittedRunRecord(
1950
2077
  * @param {object} [options.llmOptions] - Forwarded to each task
1951
2078
  * @param {function} [options.onStepStart]
1952
2079
  * @param {function} [options.onStepComplete]
2080
+ * @param {function} [options.resolveInput] - Durable stage input resolver
1953
2081
  * @param {object} [options.runAdmission] - Secret-free, digest-bound dynamic
1954
2082
  * workflow admission. Production entry points require this even though the
1955
2083
  * low-level executor keeps it optional for existing library callers.
@@ -1973,6 +2101,7 @@ export async function executeWorkflow(options = {}) {
1973
2101
  onStepComplete,
1974
2102
  pipeline,
1975
2103
  runAdmission,
2104
+ resolveInput,
1976
2105
  runTask: requestedRunTask,
1977
2106
  } = options;
1978
2107
 
@@ -2019,6 +2148,16 @@ export async function executeWorkflow(options = {}) {
2019
2148
  "cowork-workflow: runTask is not available (wire runCoworkTask before executing)",
2020
2149
  );
2021
2150
  }
2151
+ if (
2152
+ runtimeWorkflow.steps.some((step) => step.needsInput !== undefined) &&
2153
+ typeof resolveInput !== "function"
2154
+ ) {
2155
+ const error = new Error(
2156
+ "workflow contains needsInput stages but no durable input resolver was provided",
2157
+ );
2158
+ error.code = "COWORK_WORKFLOW_INPUT_RESOLVER_REQUIRED";
2159
+ throw error;
2160
+ }
2022
2161
 
2023
2162
  const taskSemaphore = createWorkflowTaskSemaphore(concurrencyLimit);
2024
2163
  const resultsById = new Map();
@@ -2038,6 +2177,7 @@ export async function executeWorkflow(options = {}) {
2038
2177
  onStepComplete,
2039
2178
  taskSemaphore,
2040
2179
  runTask: runtimeRunTask,
2180
+ resolveInput,
2041
2181
  admitted: verifiedRunAdmission != null,
2042
2182
  }));
2043
2183
  } else {
@@ -2094,23 +2234,33 @@ export async function executeWorkflow(options = {}) {
2094
2234
  preOutcomes.push(outcome);
2095
2235
  continue;
2096
2236
  }
2237
+ const resolvedMessage = await resolveStageInputMessage(
2238
+ step,
2239
+ step.message,
2240
+ resultsById,
2241
+ resolveInput,
2242
+ );
2243
+ const activeStep =
2244
+ resolvedMessage === step.message
2245
+ ? step
2246
+ : { ...step, message: resolvedMessage };
2097
2247
  // loop node — runs its body repeatedly; per-iteration substitution
2098
2248
  // happens inside runLoopStep, so push the raw template.
2099
- if (isLoopStep(step)) {
2249
+ if (isLoopStep(activeStep)) {
2100
2250
  runnable.push({
2101
- step,
2102
- message: step.message,
2103
- recordId: step.id,
2251
+ step: activeStep,
2252
+ message: activeStep.message,
2253
+ recordId: activeStep.id,
2104
2254
  parentId: null,
2105
2255
  isLoop: true,
2106
2256
  });
2107
2257
  continue;
2108
2258
  }
2109
2259
  // forEach-expansion
2110
- if (step.forEach !== undefined) {
2260
+ if (activeStep.forEach !== undefined) {
2111
2261
  let items;
2112
2262
  try {
2113
- items = resolveForEachItems(step.forEach, resultsById);
2263
+ items = resolveForEachItems(activeStep.forEach, resultsById);
2114
2264
  } catch (err) {
2115
2265
  anyFailure = true;
2116
2266
  const outcome = {
@@ -2135,20 +2285,28 @@ export async function executeWorkflow(options = {}) {
2135
2285
  continue;
2136
2286
  }
2137
2287
  for (let k = 0; k < items.length; k++) {
2138
- const childId = `${step.id}[${k}]`;
2139
- const withItem = substituteItem(step.message, items[k]);
2288
+ const childId = `${activeStep.id}[${k}]`;
2289
+ const withItem = substituteItem(activeStep.message, items[k]);
2140
2290
  const msg = substitutePlaceholders(withItem, resultsById);
2141
2291
  runnable.push({
2142
- step,
2292
+ step: activeStep,
2143
2293
  message: msg,
2144
2294
  recordId: childId,
2145
- parentId: step.id,
2295
+ parentId: activeStep.id,
2146
2296
  });
2147
2297
  }
2148
2298
  continue;
2149
2299
  }
2150
- const message = substitutePlaceholders(step.message, resultsById);
2151
- runnable.push({ step, message, recordId: step.id, parentId: null });
2300
+ const message = substitutePlaceholders(
2301
+ activeStep.message,
2302
+ resultsById,
2303
+ );
2304
+ runnable.push({
2305
+ step: activeStep,
2306
+ message,
2307
+ recordId: activeStep.id,
2308
+ parentId: null,
2309
+ });
2152
2310
  }
2153
2311
 
2154
2312
  const promises = runnable.map(