chainlesschain 0.163.4 → 0.163.5

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 (172) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-DB5X7qqV.js → AIOps-Cv2EE1Zy.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-DxHRtKLk.js → ActionButton-CcpxMLuW.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DIH5QrxT.js → Analytics-DE8mJoMI.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-uu0oxVz7.js → AppLayout-DKn9dk9L.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-BNSMiW9Y.js → Artifacts-Cp3mG3Py.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-DcAzB4_x.js → Audit-zvsQfF2C.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-ClSsvjIc.js → BackgroundAgents-Bi85SsjN.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BMciY0js.js → Backup-Cw47Dj6s.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Hn3b57Sh.js → BaseInput-C5Ip2afH.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CoSlC53V.js → Chat-C0iAFkJv.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CFevJ3So.js → ChatBubbleRenderer-BV9Wv7wO.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-B4L4Eu4w.js → Checkbox-3KZZCiAF.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-C77FgRxF.js → Codegen-C8OMbBAz.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-BrLtJLH3.js → Col-B-MB84LJ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-Be7pXXhA.js → Community-DpR7Dxao.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DuGPecjo.js → Compact-CyDtz0OS.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CzS66mjP.js → Compliance-5tiRa2An.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CEhjCoyK.js → Cowork-DnQaKZ5x.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-D7ir8Vja.js → Cron-BaWfoEDf.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BcXmuLxf.js → Crosschain-Bu0HoHhb.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Cto1dtBy.js → DID-31zk8upX.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-B7KjgwOX.js → Dashboard-BmVne-e2.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-COMqxijg.js → Dropdown-B4PkoOWd.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-BZrya-nf.js → EmailListRenderer-BZ6tyFtI.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-AmVeMq0J.js → FamilyGuardDashboard-Osozn4Ds.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-kZU8c-27.js → Federation-DaH8TOAP.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-sOV22v2-.js → FormItemContext-tbU0uwis.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BUuLtGCz.js → GenericCardRenderer-Df8LQBrL.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-K-ZxsLsH.js → Git-DsMB6-14.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-D3OEnawI.js → Governance-BnH0dZzd.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-Bll0gRmP.js → Inference-DhnN1N_P.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DBXwjr53.js → KnowledgeGraph-DnNshAgn.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CEzPwEfP.js → Logs-BmBFX3_N.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-DCslfQU3.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-BYyRyOPz.js → Marketplace-CPEgWDc3.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DVu8w_rM.js → McpTools-C36qjgF9.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-tenuZ_hO.js → Memory-tNHvrgqN.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-Bxgy4ozS.js → MobileBridge-Xaf6cKjc.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-Dgx1D6sc.js → MobileProjects-D2E5TTeO.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-CRfJ8r8j.js → Mtc-D4AJ2dwV.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-BBJq4Aiz.js → MtcAudit-Df-gtn2_.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-B3ii9lai.js → Multisig-DTCHLWld.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-FIi-Cubv.js → NLProgramming-DruZ_1Jb.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-DIq8MPf1.js → Notes-Dx3pdGDs.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-BFOD9eGd.js → NotificationSettings-Bi3L69oL.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer--41u7vre.js → OrderTableRenderer-BHY33m7C.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-B_0AWztW.js → Organization-BG2ZVEV-.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-B6I7TuXv.js → Overflow-Viwtg4Mn.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cdf6qens.js → P2P-Clm_yc1Z.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-B_0FcQJY.js → PdhVaultBrowser-NKmmDiOw.js} +5 -5
  52. package/src/assets/web-panel/assets/{Permissions-6nFBjsQp.js → Permissions-DIL8u9Nk.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-nQYV7xpn.js → PersonalDataHub-B-jM4wt-.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-CBVod5jm.js → Pipeline-627I__H5.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-D52jW1Nt.js → Privacy-2DN2qrkW.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-Bdx3BYhf.js → ProjectInit-CN4wC0sF.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-b7xr4DUa.js → ProjectSettings-K35vsh5H.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-B_wQadhX.js +1 -0
  59. package/src/assets/web-panel/assets/Providers-MckaQUhQ.js +1 -0
  60. package/src/assets/web-panel/assets/{QrScannerModal-FQKSmc3e.js → QrScannerModal-BS3mtA_O.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CSTrhzcF.js → QuickAsk-BHp_nofL.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-CxMABOUh.js → Recommend-DlmnPuZj.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-Cv2T8yRa.js → RemoteSession-tsyFbvpy.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-CE1k4Vvu.js → Reputation-Ba1iWv2D.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-BNgNcAV_.js → Row-NnlGC--f.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-5ZiBZ3ph.js → RssFeed-CjThSIRv.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-mDOIF1UV.js → Search-CojUyC5x.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CtuCTC-e.js → Security-c-sxeJEc.js} +3 -3
  69. package/src/assets/web-panel/assets/{Services-DxFO2Zg7.js → Services-CtcKEEmF.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-CKmKkH9y.js → Skeleton-CIBBXflN.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-Bmzl9qkk.js → Skills-D1XOOK9n.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-9RqaWHOC.js → Sla-hRfEp5Sn.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-CGdzFIYw.js → SpeechSettings-Dexb1f_6.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CvNuCeBT.js → SyncSettings-BJsSFGYy.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-B_sLdcnw.js → Tasks-CfSZ8A1L.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-D9_YijdK.js → Templates-idDf47W-.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-Cuc7BUSP.js → Tenant-C-UsX6w6.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-6VYRnJgd.js → Terminal-BgJtX5n1.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-GtK8xd8Y.js → TimelineRenderer-DP0BLKTl.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-BZ94Lt5W.js → Tokens-LwCzqETp.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DEcZgDaX.js → Trigger-D_9tVch0.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-CC3z9THe.js → Trust-BH2UlEWh.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-Cvtw8cV3.js → UkeySign-CIpD0nYb.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-CL4e6Bn3.js → VideoEditing-9ShhYxK_.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-ycEemP27.js → Wallet-C7afGNIG.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DgZ4X_aw.js → WebAuthn-bUKn6i5R.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-D0qMhSv7.js → WorkflowEditor-CxgQaGyp.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-DsAg01Zn.js → chat-DMVkmJwx.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-B0oqAhrQ.js → colors-CDzQh5nS.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-CmX0Rabz.js → compact-item-C51kEF4D.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-Be4cn1Ub.js → createContext-BTk9bXBn.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CdwPwYj8.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-D5v12kVq.js → hasIn-CMje33rb.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CyyGTMKd.js → index-0Auljb10.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-frSojpHj.js → index-B0-zMtKA.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B3mUHuVc.js → index-BAfY7m7i.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BA4oulaP.js → index-BBz9LIMS.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B1YGjOWC.js → index-BCRvG9lq.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BEeOdfEe.js +1 -0
  100. package/src/assets/web-panel/assets/index-BTe0TOu6.js +1 -0
  101. package/src/assets/web-panel/assets/{index-BBvyHguy.js → index-BTlsE8Df.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CGiIyTZw.js → index-BVePe_Rf.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BBREnn1V.js → index-BW4F5YDF.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-D0BSkxYQ.js → index-BXQBPLyE.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-0eWLkQKT.js → index-BeEaAEIp.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-D5YVopyr.js → index-BtlEqHLP.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BKmsS2jj.js → index-BzCkNylB.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BaE0Ui07.js → index-C1oZQmAA.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-8WvYGnON.js → index-C1oZReaY.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-_JjRLSD0.js → index-C4rQCS8T.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DKUBj36_.js → index-C5IWTMVl.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BoAIAZ5C.js → index-CCLj9hcD.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DikJ0Qqe.js → index-CDLkNkLj.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Hfe_dEGx.js → index-Cd0hmPxD.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-jK3ax6Z6.js → index-CfC-piWZ.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DyUaFWEk.js → index-CtM8OT_7.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BqltWojQ.js → index-CvSeVzqe.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dn2dSdXx.js → index-CwGPwjUu.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BRIqLy1m.js → index-CypsfcEq.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BXGSbelg.js → index-D9rC-fGW.js} +3 -3
  121. package/src/assets/web-panel/assets/{index-CZpfzcuy.js → index-DJfnCuMq.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-5LGY0JbG.js → index-DRG-aqOs.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CyWBir7N.js → index-DRLQyp2A.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DQRsQen0.js → index-DgivOKHA.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-7VKDn-t1.js → index-DpcrFxMI.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-q_fQ94SE.js → index-DqU9njjT.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-301ogenv.js → index-Ld_4mFmC.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CZ2wUEgr.js → index-OS-vfbTA.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CqoCZ5vR.js → index-h31MwrME.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DM724NgU.js → index-hg_emBsi.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-qMuZ-NRR.js → index-togc_uYn.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Ckrk3pIV.js → index-x53Vf7la.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-D4VdhJ9_.js → initDefaultProps-vrA8K-ac.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CZJA0MQW.js → motion-ByEzwIAl.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BqEs7Sb1.js → move-Ck4AWjw-.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-BpHEtsu1.js → mtc-parser-6p0l9SrB.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BW94tY9W.js → omit-B1CUPQ6R.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-BnBjPWP9.js → pickAttrs-B1STg1iW.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-uUkjQ_hu.js → placementArrow-DTTllkBV.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-B-klUqXK.js → responsiveObserve-AIrrCfBb.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-CvY1cNAs.js → slide-C_fPMjdR.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-Ix5sEDT2.js → statusUtils-D5ow2yIP.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-C8qcXOne.js → styleChecker-Dy3hEA--.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-KNF6Kj8f.js → useFlexGapSupport-WfzcfSh4.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DGJr345n.js → useFs-CKSejfas.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-BboN99rI.js → usePersonalDataHub-od3zpPBN.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-Bgt8Vgte.js → vnode-DRD_2DFA.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-BhJtErgt.js → zoom-DmNrEtgf.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agenda.js +52 -6
  151. package/src/commands/cowork.js +24 -12
  152. package/src/commands/routine.js +40 -10
  153. package/src/data/changelog.json +13 -0
  154. package/src/lib/agent-schedule-store.js +197 -4
  155. package/src/lib/cowork-cron.js +243 -25
  156. package/src/lib/micro-compact.js +11 -5
  157. package/src/lib/process-execution-broker/index.js +14 -3
  158. package/src/lib/routine-store.js +76 -3
  159. package/src/lib/scheduler-kernel/agenda-adapter.js +542 -0
  160. package/src/lib/scheduler-kernel/cowork-cron-adapter.js +774 -0
  161. package/src/lib/scheduler-kernel/routine-adapter.js +517 -0
  162. package/src/lib/scheduler-kernel/runtime.js +527 -0
  163. package/src/lib/scheduler-kernel/store.js +112 -2
  164. package/src/lib/session-message-provenance.js +4 -1
  165. package/src/repl/agent-repl.js +24 -5
  166. package/src/runtime/agent-core.js +36 -16
  167. package/src/assets/web-panel/assets/MarkdownRenderer-CLmCbHx6.js +0 -1
  168. package/src/assets/web-panel/assets/Projects-DhfifCKx.js +0 -1
  169. package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +0 -1
  170. package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +0 -1
  171. package/src/assets/web-panel/assets/index-CKB0vbJU.js +0 -1
  172. package/src/assets/web-panel/assets/index-DgoXmwqB.js +0 -1
@@ -53,6 +53,33 @@ export const _deps = {
53
53
  runTask: null, // injected at runtime to avoid circular import
54
54
  };
55
55
 
56
+ export const COWORK_SCHEDULER_UNKNOWN_LEASE_EXPIRES_AT =
57
+ "9999-12-31T23:59:59.999Z";
58
+
59
+ function coworkSchedulerError(
60
+ code,
61
+ message,
62
+ { retryable = false, retryAt } = {},
63
+ ) {
64
+ const error = new Error(message);
65
+ error.code = code;
66
+ error.retryable = retryable;
67
+ if (retryAt !== undefined) error.retryAt = retryAt;
68
+ return error;
69
+ }
70
+
71
+ function normalizedDate(value, label) {
72
+ const date =
73
+ value instanceof Date ? new Date(value.getTime()) : new Date(value);
74
+ if (!Number.isFinite(date.getTime())) {
75
+ throw coworkSchedulerError(
76
+ "COWORK_SCHEDULER_TIME_INVALID",
77
+ `${label} must be a valid date`,
78
+ );
79
+ }
80
+ return date;
81
+ }
82
+
56
83
  // ─── Cron parser ─────────────────────────────────────────────────────────────
57
84
 
58
85
  const FIELD_RANGES = [
@@ -293,9 +320,7 @@ function _withScheduleLock(cwd, body) {
293
320
 
294
321
  /** Atomically replace the full schedule list under a fail-closed lock. */
295
322
  export function saveSchedules(cwd, schedules) {
296
- return _withScheduleLock(cwd, () =>
297
- _saveSchedulesUnlocked(cwd, schedules),
298
- );
323
+ return _withScheduleLock(cwd, () => _saveSchedulesUnlocked(cwd, schedules));
299
324
  }
300
325
 
301
326
  // ─── CRUD ────────────────────────────────────────────────────────────────────
@@ -363,6 +388,191 @@ export function updateScheduleRunState(cwd, id, { lastRunAt, lastStatus }) {
363
388
  });
364
389
  }
365
390
 
391
+ /** Return one schedule by id, or null when it does not exist. */
392
+ export function getSchedule(cwd, id) {
393
+ if (typeof id !== "string" || id.length === 0) return null;
394
+ return (
395
+ loadSchedules(cwd, { failOnMalformed: true }).find(
396
+ (schedule) => schedule.id === id,
397
+ ) || null
398
+ );
399
+ }
400
+
401
+ /**
402
+ * Bind a scheduler-kernel occurrence before starting the Cowork task.
403
+ * The same JSONL lock used by the legacy driver closes both old-first and
404
+ * new-first claim races during migration.
405
+ */
406
+ export function bindSchedulerScheduleFire(
407
+ cwd,
408
+ id,
409
+ { deliveryId, occurrenceId, snapshotDigest, attempt, at = _deps.now() } = {},
410
+ ) {
411
+ if (
412
+ typeof deliveryId !== "string" ||
413
+ typeof occurrenceId !== "string" ||
414
+ typeof snapshotDigest !== "string" ||
415
+ deliveryId.length === 0 ||
416
+ occurrenceId.length === 0 ||
417
+ snapshotDigest.length === 0 ||
418
+ !Number.isSafeInteger(attempt) ||
419
+ attempt < 1
420
+ ) {
421
+ throw coworkSchedulerError(
422
+ "COWORK_SCHEDULER_BINDING_INVALID",
423
+ "Cowork scheduler binding requires deliveryId, occurrenceId, snapshotDigest, and a positive attempt",
424
+ );
425
+ }
426
+ const now = normalizedDate(at, "cowork scheduler binding time");
427
+ const nowMs = now.getTime();
428
+ return _withScheduleLock(cwd, () => {
429
+ const schedules = loadSchedules(cwd, { failOnMalformed: true });
430
+ const schedule = schedules.find((entry) => entry.id === id);
431
+ if (!schedule) {
432
+ throw coworkSchedulerError(
433
+ "COWORK_SCHEDULER_SCHEDULE_NOT_FOUND",
434
+ `Cowork schedule does not exist: ${id}`,
435
+ );
436
+ }
437
+ const prior = schedule.schedulerExecution;
438
+ if (prior?.status === "running") {
439
+ throw coworkSchedulerError(
440
+ "COWORK_SCHEDULER_OUTCOME_UNKNOWN",
441
+ `Cowork schedule outcome is unknown: ${id}`,
442
+ );
443
+ }
444
+ if (
445
+ prior?.status === "succeeded" &&
446
+ prior.occurrenceId === occurrenceId &&
447
+ prior.snapshotDigest === snapshotDigest
448
+ ) {
449
+ return schedule;
450
+ }
451
+ // A legacy runner may have completed after the kernel enqueued this fire
452
+ // but before it acquired the JSONL lock. Its durable delivery id is
453
+ // terminal evidence for the same logical fire, so never overwrite it with
454
+ // a new scheduler claim.
455
+ if (schedule.lastDeliveryId === deliveryId) return schedule;
456
+
457
+ const active = schedule.activeDelivery;
458
+ const activeExpiresAt = Date.parse(active?.leaseExpiresAt || "");
459
+ if (active && Number.isFinite(activeExpiresAt) && activeExpiresAt > nowMs) {
460
+ throw coworkSchedulerError(
461
+ "COWORK_SCHEDULER_LEGACY_CLAIM_ACTIVE",
462
+ `Cowork schedule is already claimed: ${id}`,
463
+ { retryable: true, retryAt: activeExpiresAt },
464
+ );
465
+ }
466
+
467
+ const fence =
468
+ Math.max(
469
+ Number(schedule.deliveryFence) || 0,
470
+ Number(active?.fence) || 0,
471
+ ) + 1;
472
+ schedule.deliveryFence = fence;
473
+ schedule.activeDelivery = {
474
+ deliveryId,
475
+ ownerId: `scheduler:${occurrenceId}:${attempt}`,
476
+ fence,
477
+ claimedAt: now.toISOString(),
478
+ leaseExpiresAt: COWORK_SCHEDULER_UNKNOWN_LEASE_EXPIRES_AT,
479
+ };
480
+ schedule.schedulerExecution = {
481
+ deliveryId,
482
+ occurrenceId,
483
+ snapshotDigest,
484
+ attempt,
485
+ status: "running",
486
+ startedAt: now.toISOString(),
487
+ };
488
+ _saveSchedulesUnlocked(cwd, schedules);
489
+ return schedule;
490
+ });
491
+ }
492
+
493
+ /**
494
+ * Atomically settle scheduler evidence and the legacy Cowork delivery state.
495
+ * A known failure may keep a short legacy-visible fence until retryAt; an
496
+ * unknown outcome never calls this function and therefore remains fenced.
497
+ */
498
+ export function completeSchedulerScheduleFire(
499
+ cwd,
500
+ id,
501
+ {
502
+ deliveryId,
503
+ occurrenceId,
504
+ snapshotDigest,
505
+ attempt,
506
+ outcome,
507
+ result = null,
508
+ error = null,
509
+ retryAt,
510
+ at = _deps.now(),
511
+ } = {},
512
+ ) {
513
+ if (!["succeeded", "failed"].includes(outcome)) {
514
+ throw coworkSchedulerError(
515
+ "COWORK_SCHEDULER_OUTCOME_INVALID",
516
+ "Cowork scheduler outcome must be succeeded or failed",
517
+ );
518
+ }
519
+ const now = normalizedDate(at, "cowork scheduler completion time");
520
+ const retryAtMs =
521
+ retryAt === undefined || retryAt === null ? null : Number(retryAt);
522
+ if (retryAtMs !== null && !Number.isSafeInteger(retryAtMs)) {
523
+ throw coworkSchedulerError(
524
+ "COWORK_SCHEDULER_RETRY_TIME_INVALID",
525
+ "Cowork scheduler retryAt must be a safe epoch millisecond",
526
+ );
527
+ }
528
+ return _withScheduleLock(cwd, () => {
529
+ const schedules = loadSchedules(cwd, { failOnMalformed: true });
530
+ const schedule = schedules.find((entry) => entry.id === id);
531
+ if (!schedule) {
532
+ throw coworkSchedulerError(
533
+ "COWORK_SCHEDULER_SCHEDULE_NOT_FOUND",
534
+ `Cowork schedule does not exist: ${id}`,
535
+ );
536
+ }
537
+ const evidence = schedule.schedulerExecution;
538
+ if (
539
+ !evidence ||
540
+ evidence.deliveryId !== deliveryId ||
541
+ evidence.occurrenceId !== occurrenceId ||
542
+ evidence.snapshotDigest !== snapshotDigest ||
543
+ evidence.attempt !== attempt ||
544
+ evidence.status !== "running"
545
+ ) {
546
+ throw coworkSchedulerError(
547
+ "COWORK_SCHEDULER_BINDING_MISMATCH",
548
+ `Cowork scheduler evidence does not match the active claim: ${id}`,
549
+ );
550
+ }
551
+
552
+ schedule.schedulerExecution = {
553
+ ...evidence,
554
+ status: outcome,
555
+ endedAt: now.toISOString(),
556
+ ...(outcome === "succeeded" ? { result } : { error }),
557
+ };
558
+ if (outcome === "succeeded") {
559
+ schedule.lastDeliveryId = deliveryId;
560
+ schedule.lastRunAt = now.toISOString();
561
+ schedule.lastStatus = result?.status || "completed";
562
+ schedule.activeDelivery = null;
563
+ } else if (retryAtMs !== null && retryAtMs > now.getTime()) {
564
+ schedule.activeDelivery = {
565
+ ...schedule.activeDelivery,
566
+ leaseExpiresAt: new Date(retryAtMs).toISOString(),
567
+ };
568
+ } else {
569
+ schedule.activeDelivery = null;
570
+ }
571
+ _saveSchedulesUnlocked(cwd, schedules);
572
+ return schedule;
573
+ });
574
+ }
575
+
366
576
  /**
367
577
  * Claim one concrete cron fire before launching its side effect. The persisted
368
578
  * delivery id prevents two scheduler processes from running the same fire; an
@@ -564,9 +774,7 @@ export class CoworkCronScheduler {
564
774
  this._emit({ type: "invalid-cron", id: s.id, error: err.message });
565
775
  continue;
566
776
  }
567
- const fireKey = `${s.id}:${
568
- matcher.hasSeconds ? _secondKey(now) : _minuteKey(now)
569
- }`;
777
+ const fireKey = coworkCronFireKey(s, now);
570
778
  if (this._firedKeys.has(fireKey)) continue;
571
779
  if (this._running.has(s.id)) continue;
572
780
  const isDue = matcher(now);
@@ -634,27 +842,30 @@ export class CoworkCronScheduler {
634
842
  deliveryId: claim.deliveryId,
635
843
  });
636
844
 
637
- const renewal = setInterval(() => {
638
- try {
639
- const renewed = renewScheduleFire(this.cwd, schedule.id, claim, {
640
- leaseMs: this.leaseMs,
641
- });
642
- if (!renewed) {
845
+ const renewal = setInterval(
846
+ () => {
847
+ try {
848
+ const renewed = renewScheduleFire(this.cwd, schedule.id, claim, {
849
+ leaseMs: this.leaseMs,
850
+ });
851
+ if (!renewed) {
852
+ this._emit({
853
+ type: "schedule-lease-lost",
854
+ id: schedule.id,
855
+ deliveryId: claim.deliveryId,
856
+ });
857
+ }
858
+ } catch (err) {
643
859
  this._emit({
644
- type: "schedule-lease-lost",
860
+ type: "schedule-lease-renewal-failed",
645
861
  id: schedule.id,
646
862
  deliveryId: claim.deliveryId,
863
+ error: err.message,
647
864
  });
648
865
  }
649
- } catch (err) {
650
- this._emit({
651
- type: "schedule-lease-renewal-failed",
652
- id: schedule.id,
653
- deliveryId: claim.deliveryId,
654
- error: err.message,
655
- });
656
- }
657
- }, Math.max(1000, Math.floor(this.leaseMs / 3)));
866
+ },
867
+ Math.max(1000, Math.floor(this.leaseMs / 3)),
868
+ );
658
869
  renewal.unref?.();
659
870
 
660
871
  let result = null;
@@ -718,12 +929,19 @@ export class CoworkCronScheduler {
718
929
  }
719
930
  }
720
931
 
721
- function _minuteKey(date) {
932
+ export function coworkCronMinuteKey(date) {
722
933
  return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getHours()}-${date.getMinutes()}`;
723
934
  }
724
935
 
725
- function _secondKey(date) {
726
- return `${_minuteKey(date)}-${date.getSeconds()}`;
936
+ export function coworkCronSecondKey(date) {
937
+ return `${coworkCronMinuteKey(date)}-${date.getSeconds()}`;
938
+ }
939
+
940
+ export function coworkCronFireKey(schedule, date) {
941
+ const matcher = parseCron(schedule.cron);
942
+ return `${schedule.id}:${
943
+ matcher.hasSeconds ? coworkCronSecondKey(date) : coworkCronMinuteKey(date)
944
+ }`;
727
945
  }
728
946
 
729
947
  // =====================================================================
@@ -23,10 +23,10 @@ export function microCompact(messages, opts = {}) {
23
23
  return { messages, stats: { trimmed: 0, saved: 0, kept: 0 } };
24
24
  }
25
25
  const keepRecent = Number.isFinite(opts.keepRecent)
26
- ? opts.keepRecent
26
+ ? Math.max(0, Math.trunc(opts.keepRecent))
27
27
  : DEFAULT_KEEP_RECENT;
28
28
  const maxChars = Number.isFinite(opts.maxToolChars)
29
- ? opts.maxToolChars
29
+ ? Math.max(0, Math.trunc(opts.maxToolChars))
30
30
  : DEFAULT_MAX_TOOL_CHARS;
31
31
  // messages at index >= cutoff are "recent" and never touched.
32
32
  const cutoff = Math.max(0, messages.length - keepRecent);
@@ -38,12 +38,18 @@ export function microCompact(messages, opts = {}) {
38
38
  if (!m || m.role !== "tool") return m; // only old tool results
39
39
  const content = typeof m.content === "string" ? m.content : "";
40
40
  if (content.length <= maxChars) return m; // small enough already
41
+ const fullMarker = `\n… [tool result trimmed — ${content.length} chars]`;
42
+ const marker =
43
+ fullMarker.length <= maxChars
44
+ ? fullMarker
45
+ : "…[trimmed]".slice(0, maxChars);
46
+ const head = content.slice(0, Math.max(0, maxChars - marker.length));
47
+ const compactedContent = `${head}${marker}`;
41
48
  trimmed += 1;
42
- saved += content.length - maxChars;
43
- const head = content.slice(0, maxChars);
49
+ saved += content.length - compactedContent.length;
44
50
  return {
45
51
  ...m,
46
- content: `${head}\n… [tool result trimmed — ${content.length} chars; older context dropped to save space]`,
52
+ content: compactedContent,
47
53
  _microCompacted: true,
48
54
  };
49
55
  });
@@ -66,7 +66,8 @@ const SUPPORTED_SANDBOX_PROFILES = new Set([
66
66
  // evidence, not authority to invoke a privileged post-spawn closure.
67
67
  const admittedWindowsMcpCodeSnapshotPlans = new WeakSet();
68
68
 
69
- // 延迟导入避免循环依赖
69
+ // Keep the broker on the shared CommonJS trace singleton so CommonJS and ESM
70
+ // consumers observe the same AsyncLocalStorage without loader warnings.
70
71
  let _traceCtx = null;
71
72
  const _ipcBus = null;
72
73
 
@@ -3354,7 +3355,12 @@ class ProcessExecutionBroker extends EventEmitter {
3354
3355
  delete spawnOpts.mcpStdioExecutableIdentityDigest;
3355
3356
  if (traceCtx) {
3356
3357
  spawnOpts.env = { ...(spawnOpts.env || process.env) };
3357
- spawnOpts.env.TRACEPARENT = traceCtx.traceparent;
3358
+ Object.assign(
3359
+ spawnOpts.env,
3360
+ getTraceCtx()?.traceContext?.getPropagationEnv?.() || {
3361
+ TRACEPARENT: traceCtx.traceparent,
3362
+ },
3363
+ );
3358
3364
  }
3359
3365
 
3360
3366
  // P0-1: Credential filtering (default-on) — strip secrets from env/args
@@ -3658,7 +3664,12 @@ class ProcessExecutionBroker extends EventEmitter {
3658
3664
  this._stripAuditControlOptions(spawnOpts);
3659
3665
  if (traceCtx) {
3660
3666
  spawnOpts.env = { ...(spawnOpts.env || process.env) };
3661
- spawnOpts.env.TRACEPARENT = traceCtx.traceparent;
3667
+ Object.assign(
3668
+ spawnOpts.env,
3669
+ getTraceCtx()?.traceContext?.getPropagationEnv?.() || {
3670
+ TRACEPARENT: traceCtx.traceparent,
3671
+ },
3672
+ );
3662
3673
  }
3663
3674
 
3664
3675
  // P0-1: Credential filtering agent — strip secrets from env/args before spawn
@@ -204,13 +204,24 @@ export class RoutineStore {
204
204
  }
205
205
 
206
206
  recordRunStart(routineId, meta = {}) {
207
- const runId = `run-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
207
+ const runId =
208
+ typeof meta.runId === "string" && meta.runId.trim()
209
+ ? meta.runId.trim()
210
+ : `run-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
211
+ if (
212
+ runId.length > 512 ||
213
+ [...runId].some((char) => char.codePointAt(0) < 32)
214
+ ) {
215
+ throw new Error("routine runId is invalid");
216
+ }
208
217
  this._appendRun({
209
218
  type: "start",
210
219
  runId,
211
220
  routineId,
212
221
  trigger: meta.trigger || null,
213
222
  pid: meta.pid || null,
223
+ schedulerOccurrenceId: meta.schedulerOccurrenceId || null,
224
+ schedulerSnapshotDigest: meta.schedulerSnapshotDigest || null,
214
225
  startedAt: this._now(),
215
226
  });
216
227
  return runId;
@@ -263,6 +274,15 @@ export class RoutineStore {
263
274
  .map(({ type: _t, ...rest }) => rest);
264
275
  }
265
276
 
277
+ getRun(runId) {
278
+ if (typeof runId !== "string" || runId.length === 0) return null;
279
+ return (
280
+ this.listRuns({ limit: Number.MAX_SAFE_INTEGER }).find(
281
+ (run) => run.runId === runId,
282
+ ) || null
283
+ );
284
+ }
285
+
266
286
  /** Per-routine aggregate for `cc routine list`: runs, ok/failed, cost. */
267
287
  summarize(routineId) {
268
288
  const runs = this.listRuns({ routineId, limit: 1000 });
@@ -286,10 +306,51 @@ export class RoutineStore {
286
306
  * lastFiredAt (and disable a fired `once`). `runAgent({prompt})` must resolve
287
307
  * `{ exitCode, output, usage?, costUsd?, pid? }`.
288
308
  */
289
- export async function fireRoutine(store, routine, runAgent, meta = {}) {
309
+ export async function executeRoutine(store, routine, runAgent, meta = {}) {
310
+ const requestedRunId =
311
+ typeof meta.runId === "string" && meta.runId.trim()
312
+ ? meta.runId.trim()
313
+ : null;
314
+ const existing = requestedRunId ? store.getRun(requestedRunId) : null;
315
+ if (existing) {
316
+ if (existing.routineId !== routine.id) {
317
+ const error = new Error("routine runId is bound to another routine");
318
+ error.code = "ROUTINE_RUN_ID_CONFLICT";
319
+ error.retryable = false;
320
+ throw error;
321
+ }
322
+ if (existing.status === "running") {
323
+ const error = new Error(
324
+ "routine run outcome is unknown; refusing duplicate execution",
325
+ );
326
+ error.code = "ROUTINE_RUN_OUTCOME_UNKNOWN";
327
+ error.retryable = false;
328
+ throw error;
329
+ }
330
+ const current = store.get(routine.id);
331
+ if (current) {
332
+ store.update(routine.id, {
333
+ lastFiredAt: existing.startedAt,
334
+ ...(current.trigger.kind === "once" ? { enabled: false } : {}),
335
+ });
336
+ }
337
+ return {
338
+ runId: existing.runId,
339
+ status: existing.status,
340
+ exitCode: existing.exitCode,
341
+ usage: existing.usage || null,
342
+ costUsd: Number.isFinite(existing.costUsd) ? existing.costUsd : null,
343
+ durationMs: existing.durationMs ?? null,
344
+ recovered: true,
345
+ };
346
+ }
347
+
290
348
  const startedAt = store._now();
291
349
  const runId = store.recordRunStart(routine.id, {
292
350
  trigger: meta.trigger || routine.trigger.kind,
351
+ ...(requestedRunId ? { runId: requestedRunId } : {}),
352
+ schedulerOccurrenceId: meta.schedulerOccurrenceId || null,
353
+ schedulerSnapshotDigest: meta.schedulerSnapshotDigest || null,
293
354
  });
294
355
  let result;
295
356
  try {
@@ -316,7 +377,19 @@ export async function fireRoutine(store, routine, runAgent, meta = {}) {
316
377
  lastFiredAt: startedAt,
317
378
  ...(routine.trigger.kind === "once" ? { enabled: false } : {}),
318
379
  });
319
- return runId;
380
+ return {
381
+ runId,
382
+ status: result.exitCode === 0 ? "ok" : "failed",
383
+ exitCode: result.exitCode,
384
+ usage: result.usage || null,
385
+ costUsd: Number.isFinite(result.costUsd) ? result.costUsd : null,
386
+ durationMs: store._now() - startedAt,
387
+ };
388
+ }
389
+
390
+ export async function fireRoutine(store, routine, runAgent, meta = {}) {
391
+ const execution = await executeRoutine(store, routine, runAgent, meta);
392
+ return execution.runId;
320
393
  }
321
394
 
322
395
  /**