chainlesschain 0.162.189 → 0.162.197

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 (317) hide show
  1. package/README.md +84 -11
  2. package/bin/chainlesschain.js +7 -52
  3. package/completions/_cc +209 -0
  4. package/completions/cc.bash +18 -0
  5. package/completions/cc.fish +13 -0
  6. package/completions/cc.ps1 +226 -0
  7. package/package.json +10 -2
  8. package/src/assets/web-panel/assets/{AIOps-DbZft8w0.js → AIOps-D3Gu1a5d.js} +1 -1
  9. package/src/assets/web-panel/assets/{ActionButton-DoOZpwfN.js → ActionButton-DrwAoKaz.js} +1 -1
  10. package/src/assets/web-panel/assets/{Analytics-B0J0KLTW.js → Analytics-C7Q-gd_3.js} +3 -3
  11. package/src/assets/web-panel/assets/{AppLayout-D9eIR0qq.js → AppLayout-DgBtae-n.js} +4 -4
  12. package/src/assets/web-panel/assets/{Artifacts-BnZXEVeo.js → Artifacts-pQ6LcA-W.js} +1 -1
  13. package/src/assets/web-panel/assets/{Audit-X1ce4Bjn.js → Audit-DK61tocB.js} +1 -1
  14. package/src/assets/web-panel/assets/{BackgroundAgents-BvWR-y5v.js → BackgroundAgents-BOKgDFRh.js} +1 -1
  15. package/src/assets/web-panel/assets/{Backup-BublSGEd.js → Backup-Bo9G4vTP.js} +1 -1
  16. package/src/assets/web-panel/assets/{BaseInput-CR_77Hso.js → BaseInput-Do_dE9_v.js} +1 -1
  17. package/src/assets/web-panel/assets/{Chat-DixW52tq.js → Chat-COVvZzOL.js} +6 -6
  18. package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +1 -0
  19. package/src/assets/web-panel/assets/{Checkbox-CCv1C87K.js → Checkbox-Z9MJMolt.js} +1 -1
  20. package/src/assets/web-panel/assets/{Codegen-B03Cc3dd.js → Codegen-BVzWKVbf.js} +1 -1
  21. package/src/assets/web-panel/assets/{Col-CA06-6pU.js → Col-C8MnYcbT.js} +1 -1
  22. package/src/assets/web-panel/assets/{Community-Bj881J52.js → Community-DQxqw15l.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compact-UZLTPoaL.js → Compact-BAUmrh3y.js} +1 -1
  24. package/src/assets/web-panel/assets/{Compliance-kGwzRb_0.js → Compliance-jPAkQ3zK.js} +1 -1
  25. package/src/assets/web-panel/assets/{Cowork-KundoYM8.js → Cowork-BBQyxbh8.js} +2 -2
  26. package/src/assets/web-panel/assets/{Cron-BYZyoqDe.js → Cron-W5koq8Z5.js} +2 -2
  27. package/src/assets/web-panel/assets/{Crosschain-BhrrppBX.js → Crosschain-BQhnu5hS.js} +1 -1
  28. package/src/assets/web-panel/assets/{DID-CdC6EguZ.js → DID-DD7PlI18.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dashboard-CcQx3J4y.js → Dashboard-w_ILuwKI.js} +2 -2
  30. package/src/assets/web-panel/assets/{Dropdown-BpijtEzQ.js → Dropdown-qiDUAyAo.js} +1 -1
  31. package/src/assets/web-panel/assets/{EmailListRenderer-DfZjeZTe.js → EmailListRenderer-PDeBcI5H.js} +1 -1
  32. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BFYSf69C.js → FamilyGuardDashboard-SuArscRM.js} +1 -1
  33. package/src/assets/web-panel/assets/{Federation-Okdi3pJm.js → Federation-BCovZaUn.js} +1 -1
  34. package/src/assets/web-panel/assets/{FormItemContext-mH-HebKW.js → FormItemContext-DjlFdVFk.js} +1 -1
  35. package/src/assets/web-panel/assets/{GenericCardRenderer-xivV69x7.js → GenericCardRenderer-DoEZ7fcS.js} +1 -1
  36. package/src/assets/web-panel/assets/{Git-1rh_YBwg.js → Git-CV0-6GPI.js} +2 -2
  37. package/src/assets/web-panel/assets/{Governance-eAXkNxdY.js → Governance-B_SFN50w.js} +1 -1
  38. package/src/assets/web-panel/assets/{Inference-CS6dMVTN.js → Inference-heI0Eo6k.js} +1 -1
  39. package/src/assets/web-panel/assets/{KnowledgeGraph-Bq_TnkvD.js → KnowledgeGraph-Cz5INS5p.js} +1 -1
  40. package/src/assets/web-panel/assets/{Logs-KchHtiHt.js → Logs-BSFxiHkg.js} +2 -2
  41. package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +1 -0
  42. package/src/assets/web-panel/assets/{Marketplace-CsmGcX5X.js → Marketplace-0TWTpE7k.js} +1 -1
  43. package/src/assets/web-panel/assets/{McpTools-BaPFnnyz.js → McpTools-CV4-wBLm.js} +4 -4
  44. package/src/assets/web-panel/assets/{Memory-2m-sUUf7.js → Memory-x5bH5rw5.js} +2 -2
  45. package/src/assets/web-panel/assets/{MobileBridge-D5nVZBRu.js → MobileBridge-DaBmUu9G.js} +1 -1
  46. package/src/assets/web-panel/assets/{MobileProjects-Bo8MLZvT.js → MobileProjects-DqnoFPgR.js} +1 -1
  47. package/src/assets/web-panel/assets/{Mtc-3GDvamGa.js → Mtc-B_zKEBif.js} +4 -4
  48. package/src/assets/web-panel/assets/{MtcAudit-D2zGuZCH.js → MtcAudit-BCfKD2yg.js} +4 -4
  49. package/src/assets/web-panel/assets/{Multisig-BBVQ4t6t.js → Multisig-DgYfj6c_.js} +3 -3
  50. package/src/assets/web-panel/assets/{NLProgramming-BIOCyOGp.js → NLProgramming-Dz09lFUm.js} +1 -1
  51. package/src/assets/web-panel/assets/{Notes-DVwBGpzc.js → Notes-Cuj6ugBE.js} +3 -3
  52. package/src/assets/web-panel/assets/{NotificationSettings-BDnHfIfX.js → NotificationSettings-DK2WXjhg.js} +1 -1
  53. package/src/assets/web-panel/assets/{OrderTableRenderer-C7Ek20ag.js → OrderTableRenderer-D1IAOQoo.js} +1 -1
  54. package/src/assets/web-panel/assets/{Organization-D3SRKaa7.js → Organization-9fqnPvTw.js} +4 -4
  55. package/src/assets/web-panel/assets/{Overflow-C2B8ahyn.js → Overflow-n5xHkhow.js} +1 -1
  56. package/src/assets/web-panel/assets/{P2P-2iwQzG65.js → P2P-B5AUWwH-.js} +2 -2
  57. package/src/assets/web-panel/assets/{PdhVaultBrowser-CoPiWqMN.js → PdhVaultBrowser-CoUDV6dy.js} +4 -4
  58. package/src/assets/web-panel/assets/{Permissions-yV5XNXo5.js → Permissions-DPRmQf_H.js} +4 -4
  59. package/src/assets/web-panel/assets/{PersonalDataHub-uCDJhMUF.js → PersonalDataHub-B37bsbEN.js} +4 -4
  60. package/src/assets/web-panel/assets/{Pipeline-hN7ucMOA.js → Pipeline-CCAuPqu9.js} +1 -1
  61. package/src/assets/web-panel/assets/{Privacy-CgU0ptN4.js → Privacy-B81msZdv.js} +1 -1
  62. package/src/assets/web-panel/assets/{ProjectInit-CfYPiT6j.js → ProjectInit-B-Py2NbF.js} +2 -2
  63. package/src/assets/web-panel/assets/{ProjectSettings-B0A2C4nw.js → ProjectSettings-CYsneiRB.js} +2 -2
  64. package/src/assets/web-panel/assets/{Projects-BE5C0ajn.js → Projects-Y975Mh6R.js} +1 -1
  65. package/src/assets/web-panel/assets/{Providers-CstQPf8W.js → Providers-uO-10aP1.js} +1 -1
  66. package/src/assets/web-panel/assets/{QrScannerModal-Co3z_ynk.js → QrScannerModal-Bj_6gJ0l.js} +1 -1
  67. package/src/assets/web-panel/assets/{QuickAsk-DMTS6pQ6.js → QuickAsk-BgpBCMPE.js} +1 -1
  68. package/src/assets/web-panel/assets/{Recommend-ETw4odUa.js → Recommend-DhUW3YUA.js} +1 -1
  69. package/src/assets/web-panel/assets/RemoteSession-DdeyfbkA.js +5 -0
  70. package/src/assets/web-panel/assets/{Reputation-BTVqQgKq.js → Reputation-fxW421xd.js} +1 -1
  71. package/src/assets/web-panel/assets/{Row-CBKKwwtO.js → Row-De73w1og.js} +1 -1
  72. package/src/assets/web-panel/assets/{RssFeed-BHUQ438I.js → RssFeed-3US_Q_lj.js} +2 -2
  73. package/src/assets/web-panel/assets/{Search-B4NqeUsF.js → Search-DbZ_DU74.js} +1 -1
  74. package/src/assets/web-panel/assets/{Security-BRjoYG5_.js → Security-DoIU1vMF.js} +4 -4
  75. package/src/assets/web-panel/assets/{Services-CxQzu3p3.js → Services-DB_RGqpc.js} +2 -2
  76. package/src/assets/web-panel/assets/{Skeleton-BKzwwmWO.js → Skeleton-DnzLaayC.js} +1 -1
  77. package/src/assets/web-panel/assets/{Skills-DrsFcgth.js → Skills-VzVXHENF.js} +1 -1
  78. package/src/assets/web-panel/assets/{Sla-D21tmXCH.js → Sla-DMfvjCAI.js} +1 -1
  79. package/src/assets/web-panel/assets/{SpeechSettings-CjyjLfp8.js → SpeechSettings-DQQOYWbF.js} +1 -1
  80. package/src/assets/web-panel/assets/{SyncSettings-Cu45-tnx.js → SyncSettings-Bmx3FG1P.js} +2 -2
  81. package/src/assets/web-panel/assets/{Tasks-oMcILYOj.js → Tasks-DOQyfb-2.js} +1 -1
  82. package/src/assets/web-panel/assets/{Templates-Bh-Zcr5R.js → Templates-D6hTRT7c.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tenant-68ns63aI.js → Tenant-CTPQZAzs.js} +1 -1
  84. package/src/assets/web-panel/assets/{Terminal-BLJkFykE.js → Terminal-B7vII3oH.js} +2 -2
  85. package/src/assets/web-panel/assets/{TimelineRenderer-xcrJLq-O.js → TimelineRenderer-BkiFJeXV.js} +1 -1
  86. package/src/assets/web-panel/assets/{Tokens-C6PpFJtf.js → Tokens-BPPdQMNo.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trigger-BaKA7tdM.js → Trigger-BxG8JAEI.js} +1 -1
  88. package/src/assets/web-panel/assets/{Trust-BzhA57Jc.js → Trust-DHD7nMpM.js} +1 -1
  89. package/src/assets/web-panel/assets/{UkeySign-4_U3JkIR.js → UkeySign-B4BJpU47.js} +1 -1
  90. package/src/assets/web-panel/assets/{VideoEditing-162AANxX.js → VideoEditing-CRiGpFx2.js} +1 -1
  91. package/src/assets/web-panel/assets/{Wallet-B1viuql_.js → Wallet-BSDSNiem.js} +4 -4
  92. package/src/assets/web-panel/assets/{WebAuthn-ZVWazhCy.js → WebAuthn-DLTUh5Zv.js} +5 -5
  93. package/src/assets/web-panel/assets/WorkflowEditor-tW2MwjhD.js +1 -0
  94. package/src/assets/web-panel/assets/{chat-DmrHNuwq.js → chat-SgsLQ3FC.js} +1 -1
  95. package/src/assets/web-panel/assets/{colors-DAJ_M-PK.js → colors-BHjjLIKO.js} +1 -1
  96. package/src/assets/web-panel/assets/{compact-item-uIzmYwLs.js → compact-item-VceErdKq.js} +1 -1
  97. package/src/assets/web-panel/assets/{createContext-D_ZmuLwb.js → createContext-BZ332MYl.js} +1 -1
  98. package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +1 -0
  99. package/src/assets/web-panel/assets/{hasIn-CDt_shTL.js → hasIn-CBTjJOjn.js} +1 -1
  100. package/src/assets/web-panel/assets/index-0O5ePnUq.js +1 -0
  101. package/src/assets/web-panel/assets/{index-rXdwUeE3.js → index-1n7xjKDU.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BAvSmJXW.js → index-29wVzi-k.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-C16fYKFD.js → index-B2u26AS3.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-C5WcdJH2.js → index-B40dQn0G.js} +3 -3
  105. package/src/assets/web-panel/assets/{index-CaelyMgF.js → index-B5KAGxNo.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-OzTxFIJP.js → index-BC1xuQgX.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CzXqvMuP.js → index-BETEjuNr.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-clSX4PEc.js → index-BOtvPdHt.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-saf3VNeP.js → index-BPXFLYdW.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DdUMcwRB.js → index-BQSmZGgs.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DfSSy9uy.js → index-BRWhLbWF.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-GcKOHD_c.js → index-BTlc-Bve.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BHvlm0In.js → index-BY1wexYG.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Cvak6Oe2.js → index-BcUenmCX.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CKyd472S.js → index-BeiqkxJG.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DvFhhe6r.js → index-BhXZEUAc.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DBFmcaCQ.js → index-By5jBtkV.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DqDSF2RW.js → index-CLc19alH.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DeGBoLQA.js → index-CMVvxIS8.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-FyplHuNt.js → index-C_B9rFQE.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CeUpKjvi.js → index-CaIS_TNX.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BRa4kJ_S.js → index-CawpdGTW.js} +1 -1
  123. package/src/assets/web-panel/assets/index-ChAzPigF.js +1 -0
  124. package/src/assets/web-panel/assets/{index-QsgfSZrV.js → index-CpumS98o.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BDF6EQBs.js → index-Cr4rjlW5.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-rS3gl_68.js → index-CrUgDQkf.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-D4n1lGTB.js → index-CtjLcCNU.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-ifxmu8SY.js → index-DUkVN794.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BTCXf1t-.js → index-D_SePDWj.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Bbxjfstq.js → index-DaCCy6lq.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-D9OE8B3d.js → index-DdefYBSw.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CG0lY4h3.js → index-DePOeMDM.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-Bx4d6c1k.js → index-DgPvuIn2.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-Z8P4BexE.js → index-FJ3zC9LK.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-ByzGvpR9.js → index-_PHrIO4O.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-DwfvPe-j.js → index-fU7wNHDB.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-CcZ9xJI9.js → index-iYQNU9za.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-CbD_WW_6.js → index-vIXOpcjD.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-pmfaD-aK.js → initDefaultProps-D1oZNz0D.js} +1 -1
  140. package/src/assets/web-panel/assets/{motion-CaQ2CpYF.js → motion-C8k6doMO.js} +1 -1
  141. package/src/assets/web-panel/assets/{move-CkKTqNbI.js → move-DJ4qSbYt.js} +1 -1
  142. package/src/assets/web-panel/assets/{mtc-parser-Dd4xsgRI.js → mtc-parser-CdpJD85j.js} +1 -1
  143. package/src/assets/web-panel/assets/{omit-BFuA5YEs.js → omit-B0V4slKA.js} +1 -1
  144. package/src/assets/web-panel/assets/{pickAttrs-DDfxOAJc.js → pickAttrs-Di5NiW1F.js} +1 -1
  145. package/src/assets/web-panel/assets/{placementArrow-Be595JCp.js → placementArrow-B5OMHgEM.js} +1 -1
  146. package/src/assets/web-panel/assets/{responsiveObserve-7byljUM3.js → responsiveObserve-C6U9IWql.js} +1 -1
  147. package/src/assets/web-panel/assets/{slide-CTWfOTaY.js → slide-BMyTH3rN.js} +1 -1
  148. package/src/assets/web-panel/assets/{statusUtils-CNZOuIxM.js → statusUtils-C7d6BG7Z.js} +1 -1
  149. package/src/assets/web-panel/assets/{styleChecker-DwOcBV78.js → styleChecker-CR50QnQy.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFlexGapSupport-B6MNwAO9.js → useFlexGapSupport-DBEeoMb8.js} +1 -1
  151. package/src/assets/web-panel/assets/{useFs-CzyQjH9r.js → useFs-3r099cjv.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-Dxh3KiVA.js → usePersonalDataHub-C5IYNdTK.js} +1 -1
  153. package/src/assets/web-panel/assets/{vnode-DVfZiS5N.js → vnode-DmEOLRHf.js} +1 -1
  154. package/src/assets/web-panel/assets/{zoom-BYme7fbY.js → zoom-DnpUiVCB.js} +1 -1
  155. package/src/assets/web-panel/index.html +1 -1
  156. package/src/cli-help.js +181 -0
  157. package/src/command-help-index.json +181 -0
  158. package/src/command-manifest.json +1271 -175
  159. package/src/command-surface-policy.js +312 -0
  160. package/src/commands/agent.js +76 -33
  161. package/src/commands/artifacts.js +400 -2
  162. package/src/commands/background-session.js +41 -3
  163. package/src/commands/checkpoint-restore-recovery.js +1476 -0
  164. package/src/commands/checkpoint.js +947 -22
  165. package/src/commands/compact.js +101 -25
  166. package/src/commands/config.js +506 -20
  167. package/src/commands/doctor.js +4 -1
  168. package/src/commands/hub.js +1 -1
  169. package/src/commands/mcp.js +688 -24
  170. package/src/commands/pack.js +16 -2
  171. package/src/commands/plugin.js +20 -12
  172. package/src/commands/session-mcp-recovery.js +218 -0
  173. package/src/commands/session.js +470 -110
  174. package/src/commands/setup.js +36 -10
  175. package/src/commands/skill.js +181 -110
  176. package/src/commands/status.js +21 -5
  177. package/src/commands/update.js +68 -6
  178. package/src/constants.js +11 -0
  179. package/src/data/changelog.json +14 -1
  180. package/src/gateways/ws/action-protocol.js +10 -0
  181. package/src/gateways/ws/session-protocol.js +555 -27
  182. package/src/gateways/ws/ws-agent-handler.js +541 -14
  183. package/src/gateways/ws/ws-session-gateway.js +129 -22
  184. package/src/gateways/ws/ws-session-state.js +431 -0
  185. package/src/harness/background-task-manager.js +233 -36
  186. package/src/harness/jsonl-session-store.js +2551 -167
  187. package/src/harness/mcp-client.js +802 -61
  188. package/src/harness/prompt-compressor.js +227 -27
  189. package/src/harness/session-index.js +89 -38
  190. package/src/harness/session-list-index.js +254 -0
  191. package/src/harness/structured-handoff.js +410 -0
  192. package/src/lazy-dispatch.js +747 -84
  193. package/src/lib/__tests__/logger.test.js +50 -2
  194. package/src/lib/advisor-runtime.js +1047 -0
  195. package/src/lib/agent-core.js +1 -0
  196. package/src/lib/agent-sandbox.js +120 -26
  197. package/src/lib/agent-session-export.js +20 -0
  198. package/src/lib/agent-team/team-runner.js +230 -8
  199. package/src/lib/artifact-store.js +131 -8
  200. package/src/lib/background-agent-supervisor.js +185 -13
  201. package/src/lib/background-interaction-journal.js +37 -9
  202. package/src/lib/background-launch-profile.js +1058 -0
  203. package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
  204. package/src/lib/checkpoint-restore-orchestrator.js +987 -0
  205. package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
  206. package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
  207. package/src/lib/checkpoint-restore-recovery.js +884 -0
  208. package/src/lib/checkpoint-restore-retention.js +126 -0
  209. package/src/lib/checkpoint-restore-saga.js +5079 -0
  210. package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
  211. package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
  212. package/src/lib/checkpoint-store.js +1688 -97
  213. package/src/lib/checkpoint-timeline-authority.js +522 -0
  214. package/src/lib/checkpoint-timeline.js +642 -0
  215. package/src/lib/config-keys.js +18 -119
  216. package/src/lib/config-manager.js +446 -27
  217. package/src/lib/config-redaction.js +57 -0
  218. package/src/lib/config-schema.js +853 -0
  219. package/src/lib/cowork-mcp-tools.js +5 -0
  220. package/src/lib/cowork-task-runner.js +458 -63
  221. package/src/lib/delivery-coordinator.js +1234 -0
  222. package/src/lib/delivery-evidence.js +450 -0
  223. package/src/lib/doctor-checkup.js +199 -14
  224. package/src/lib/executable-path.js +34 -0
  225. package/src/lib/fatal-handler.js +16 -0
  226. package/src/lib/file-checkpoint.js +4909 -186
  227. package/src/lib/file-lines.js +125 -0
  228. package/src/lib/headless-config-command.js +16 -5
  229. package/src/lib/hook-runner.cjs +139 -38
  230. package/src/lib/hooks-v2-workspace-context.js +10 -0
  231. package/src/lib/impacted-gate-selector.js +424 -0
  232. package/src/lib/interaction-adapter.js +62 -0
  233. package/src/lib/jsonl-session-store.js +1 -35
  234. package/src/lib/logger.js +66 -14
  235. package/src/lib/mcp-call-ledger-store.js +1069 -0
  236. package/src/lib/mcp-call-ledger.js +747 -0
  237. package/src/lib/mcp-client.js +5 -0
  238. package/src/lib/mcp-conflict-scheduler.js +468 -0
  239. package/src/lib/mcp-effect-contract.js +102 -0
  240. package/src/lib/mcp-headers-helper-trust.js +145 -0
  241. package/src/lib/mcp-headers-helper.js +616 -0
  242. package/src/lib/mcp-host-recovery-runtime.js +131 -0
  243. package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
  244. package/src/lib/mcp-recovery-adjudication.js +443 -0
  245. package/src/lib/output-context.js +86 -0
  246. package/src/lib/packer/file-identity.js +170 -0
  247. package/src/lib/packer/index.js +3 -0
  248. package/src/lib/packer/native-update-state.js +277 -0
  249. package/src/lib/packer/pack-update-applier.js +1983 -136
  250. package/src/lib/packer/pack-update-checker.js +34 -0
  251. package/src/lib/packer/pack-update-downloader.js +875 -86
  252. package/src/lib/packer/pack-update-signature.js +86 -0
  253. package/src/lib/packer/pkg-config-generator.js +6 -0
  254. package/src/lib/paths.js +65 -10
  255. package/src/lib/plan-mode.js +831 -14
  256. package/src/lib/plan-persistence.js +540 -0
  257. package/src/lib/plugin-runtime/hooks.js +227 -69
  258. package/src/lib/plugin-runtime/plugin-options.js +189 -44
  259. package/src/lib/pr-automation-policy.js +146 -8
  260. package/src/lib/process-execution-broker/credential-agent.js +7 -0
  261. package/src/lib/process-execution-broker/index.js +53 -8
  262. package/src/lib/process-execution-broker/workspace-transaction.js +976 -73
  263. package/src/lib/project-instructions.js +165 -29
  264. package/src/lib/project-mcp-trust.js +67 -0
  265. package/src/lib/public-ide-capability-manifest.js +278 -0
  266. package/src/lib/public-ide-capability-validation.js +351 -0
  267. package/src/lib/secret-store.js +267 -43
  268. package/src/lib/secure-fs.js +1089 -0
  269. package/src/lib/security-audit.js +58 -0
  270. package/src/lib/service-manager.js +34 -13
  271. package/src/lib/session-budget-runtime.js +1452 -0
  272. package/src/lib/session-host-snapshot.js +388 -0
  273. package/src/lib/session-message-provenance.js +429 -0
  274. package/src/lib/session-projection.js +804 -0
  275. package/src/lib/session-resource-budget.js +1495 -0
  276. package/src/lib/settings-hooks.cjs +138 -22
  277. package/src/lib/settings-loader.cjs +25 -2
  278. package/src/lib/side-effect-ledger-store.js +45 -23
  279. package/src/lib/skill-execution-identity.js +217 -0
  280. package/src/lib/skill-loader.js +299 -18
  281. package/src/lib/sub-agent-context.js +236 -28
  282. package/src/lib/sub-agent-registry.js +5 -0
  283. package/src/lib/subagent-contract.js +94 -0
  284. package/src/lib/todo-manager.js +185 -18
  285. package/src/lib/todo-persistence.js +417 -0
  286. package/src/lib/turn-binding-store.js +41 -23
  287. package/src/lib/with-file-lock.js +25 -1
  288. package/src/lib/ws-chat-handler.js +21 -0
  289. package/src/program-base.js +18 -0
  290. package/src/repl/advisor-command.js +103 -0
  291. package/src/repl/agent-repl.js +2332 -154
  292. package/src/repl/btw-command.js +93 -10
  293. package/src/repl/clipboard-image.js +119 -0
  294. package/src/repl/config-summary.js +64 -108
  295. package/src/repl/prompt-editor.js +233 -0
  296. package/src/repl/prompt-interactions.js +404 -0
  297. package/src/repl/prompt-stash.js +354 -0
  298. package/src/repl/prompt-suggestions.js +312 -0
  299. package/src/repl/repl-keybindings.js +227 -0
  300. package/src/repl/session-recap.js +217 -0
  301. package/src/repl/slash-command-registry.js +25 -4
  302. package/src/repl/terminal-layout.js +88 -0
  303. package/src/runtime/agent-core.js +1264 -325
  304. package/src/runtime/coding-agent-contract-shared.cjs +13 -1
  305. package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
  306. package/src/runtime/diagnostics.js +32 -13
  307. package/src/runtime/headless-runner.js +298 -118
  308. package/src/runtime/headless-stream.js +353 -95
  309. package/src/runtime/mcp-config.js +148 -14
  310. package/src/runtime/status-diagnostics-lite.js +137 -0
  311. package/src/assets/web-panel/assets/ChatBubbleRenderer-DDHDpBBA.js +0 -1
  312. package/src/assets/web-panel/assets/MarkdownRenderer-TabqHW9B.js +0 -1
  313. package/src/assets/web-panel/assets/RemoteSession-B0rkE09d.js +0 -5
  314. package/src/assets/web-panel/assets/WorkflowEditor-tzdyUk-1.js +0 -1
  315. package/src/assets/web-panel/assets/devWarning-DIrncrV1.js +0 -1
  316. package/src/assets/web-panel/assets/index-D-Swwqld.js +0 -1
  317. package/src/assets/web-panel/assets/index-FMZAMKy8.js +0 -1
@@ -20,9 +20,106 @@
20
20
  */
21
21
 
22
22
  import chalk from "chalk";
23
+ import { createHash } from "node:crypto";
23
24
  import { resolve } from "path";
25
+ import {
26
+ buildCheckpointTimeline,
27
+ projectCheckpointTimeline,
28
+ } from "../lib/checkpoint-timeline.js";
29
+ import {
30
+ CHECKPOINT_TIMELINE_AUDIT_EVENT,
31
+ CHECKPOINT_TIMELINE_INTENT_EVENT,
32
+ CHECKPOINT_TIMELINE_RESULT_SCHEMA,
33
+ CHECKPOINT_TIMELINE_RESULT_VERSION,
34
+ checkpointTimelineConfirmationsMatch,
35
+ planCheckpointTimelineAction,
36
+ timelineActionError,
37
+ validateCheckpointTimelineConfirmationSubmission,
38
+ validateCheckpointTimelineSubmission,
39
+ } from "../lib/checkpoint-timeline-authority.js";
24
40
  import { logger } from "../lib/logger.js";
41
+ import {
42
+ loadTurnBindingLog,
43
+ TURN_BINDING_TIMELINE_EVENT,
44
+ } from "../lib/turn-binding-store.js";
45
+ import {
46
+ createBranchSession,
47
+ findLatestEvent,
48
+ readVerifiedMessages,
49
+ withSessionAuthorityTransaction,
50
+ } from "../harness/jsonl-session-store.js";
51
+ import { withWorkspaceLockSync as withCanonicalWorkspaceLockSync } from "../lib/process-execution-broker/workspace-transaction.js";
52
+ import {
53
+ CheckpointRestoreSagaStore,
54
+ computeCheckpointRestoreWorkspaceLockOwnerDigest,
55
+ } from "../lib/checkpoint-restore-saga.js";
56
+ import {
57
+ computeCheckpointRestoreDigest,
58
+ runCheckpointRestoreOperation,
59
+ } from "../lib/checkpoint-restore-orchestrator.js";
25
60
  import { registerManagedCheckpointCommands } from "./checkpoint-managed.js";
61
+ import { registerCheckpointRestoreRecoveryCommands } from "./checkpoint-restore-recovery.js";
62
+
63
+ function stableCheckpointRestoreValue(value) {
64
+ if (value === undefined) return "null";
65
+ if (
66
+ value === null ||
67
+ typeof value === "string" ||
68
+ typeof value === "boolean"
69
+ ) {
70
+ return JSON.stringify(value);
71
+ }
72
+ if (typeof value === "number" && Number.isFinite(value)) {
73
+ return JSON.stringify(value);
74
+ }
75
+ if (Array.isArray(value)) {
76
+ return `[${value.map((entry) => stableCheckpointRestoreValue(entry)).join(",")}]`;
77
+ }
78
+ if (value && typeof value === "object") {
79
+ return `{${Object.keys(value)
80
+ .sort()
81
+ .map(
82
+ (key) =>
83
+ `${JSON.stringify(key)}:${stableCheckpointRestoreValue(value[key])}`,
84
+ )
85
+ .join(",")}}`;
86
+ }
87
+ throw new TypeError("checkpoint restore evidence is not canonical JSON");
88
+ }
89
+
90
+ function checkpointRestoreDigest(domain, value) {
91
+ return `sha256:${createHash("sha256")
92
+ .update(`${domain}\0${stableCheckpointRestoreValue(value)}`)
93
+ .digest("hex")}`;
94
+ }
95
+
96
+ function boundedCheckpointRestoreText(value, fallback, maximum = 2_048) {
97
+ let sanitized = "";
98
+ for (const character of String(value || fallback)) {
99
+ const code = character.charCodeAt(0);
100
+ sanitized += code < 32 || code === 127 ? " " : character;
101
+ }
102
+ const normalized = sanitized.trim();
103
+ return (normalized || fallback).slice(0, maximum);
104
+ }
105
+
106
+ function checkpointRestoreErrorCode(error) {
107
+ return boundedCheckpointRestoreText(
108
+ error?.code,
109
+ "CHECKPOINT_RESTORE_FAILED",
110
+ 128,
111
+ );
112
+ }
113
+
114
+ function checkpointRestoreTargetCount(planned) {
115
+ const total = planned?.preview?.code?.summary?.total;
116
+ if (!Number.isSafeInteger(total) || total < 0) {
117
+ const error = new Error("checkpoint restore target count is unavailable");
118
+ error.code = "TIMELINE_WORKSPACE_BINDING_INVALID";
119
+ throw error;
120
+ }
121
+ return total;
122
+ }
26
123
 
27
124
  /** git-plumbing engine adapter (normalized interface). */
28
125
  function gitEngine(gs, dir, session) {
@@ -43,12 +140,37 @@ function gitEngine(gs, dir, session) {
43
140
  label: r.label,
44
141
  createdAt: r.createdAt,
45
142
  fileCount: null,
143
+ identity: r.commit ? `git:${r.commit}` : null,
46
144
  })),
47
145
  show: (id) => gs.showCheckpoint(dir, id, { session }),
48
- status: (id) => gs.statusAgainst(dir, id, { session }),
146
+ status: (id, o) => {
147
+ const status = gs.statusAgainst(dir, id, {
148
+ session,
149
+ expectedIdentity: o?.expectedIdentity,
150
+ });
151
+ return {
152
+ checkpointIdentity: status.checkpointIdentity,
153
+ modified: status.modified,
154
+ added: status.added,
155
+ deleted: status.deleted,
156
+ ...(o?.includeWorkspaceBinding
157
+ ? { workspaceBinding: status.workspaceBinding }
158
+ : {}),
159
+ };
160
+ },
49
161
  diffText: (id, o) => gs.diffCheckpoint(dir, id, { session, stat: o?.stat }),
50
162
  restore: (id, o) => {
51
- const r = gs.rewindTo(dir, id, { session, dryRun: o?.dryRun });
163
+ const r = gs.rewindTo(dir, id, {
164
+ session,
165
+ dryRun: o?.dryRun,
166
+ skipSafety: o?.skipSafety,
167
+ expectedIdentity: o?.expectedIdentity,
168
+ expectedWorkspaceBinding: o?.expectedWorkspaceBinding,
169
+ expectedSafetyPlanIdentity: o?.expectedSafetyPlanIdentity,
170
+ onSafetyReady: o?.onSafetyReady,
171
+ onMutationStarted: o?.onMutationStarted,
172
+ onWorkspaceApplied: o?.onWorkspaceApplied,
173
+ });
52
174
  return {
53
175
  dryRun: !!r.dryRun,
54
176
  restoredCount: r.modified + r.recreated,
@@ -56,6 +178,9 @@ function gitEngine(gs, dir, session) {
56
178
  recreated: r.recreated,
57
179
  deleted: r.deleted,
58
180
  safetyId: r.safetyId,
181
+ safetyIdentity: r.safetyIdentity,
182
+ safetyPlanIdentity: r.safetyPlanIdentity,
183
+ safetyCoverage: r.safetyCoverage,
59
184
  };
60
185
  },
61
186
  remove: (id) => gs.deleteCheckpoint(dir, id, { session }),
@@ -77,14 +202,15 @@ function copyEngine(cs, dir) {
77
202
  };
78
203
  },
79
204
  list: () =>
80
- cs.listCheckpoints().map((c) => ({
205
+ cs.listCheckpoints({ cwd: dir }).map((c) => ({
81
206
  id: c.id,
82
207
  label: c.label,
83
208
  createdAt: c.createdAt,
84
209
  fileCount: c.fileCount,
210
+ identity: c.identity || null,
85
211
  })),
86
212
  show: (id) => {
87
- const m = cs.getCheckpoint(id);
213
+ const m = cs.getCheckpoint(id, { cwd: dir });
88
214
  if (!m) throw new Error(`no such checkpoint: ${id}`);
89
215
  return {
90
216
  id: m.id,
@@ -94,27 +220,50 @@ function copyEngine(cs, dir) {
94
220
  files: m.files.map((f) => ({ rel: f.rel, bytes: f.bytes })),
95
221
  };
96
222
  },
97
- status: (id) => {
98
- const d = cs.diffCheckpoint(id);
99
- return { modified: d.modified, added: [], deleted: d.deleted };
223
+ status: (id, o) => {
224
+ const d = cs.diffCheckpoint(id, {
225
+ cwd: dir,
226
+ expectedIdentity: o?.expectedIdentity,
227
+ expectedSafetyPlanIdentity: o?.expectedSafetyPlanIdentity,
228
+ });
229
+ return {
230
+ checkpointIdentity: d.checkpointIdentity,
231
+ modified: d.modified,
232
+ added: [],
233
+ deleted: d.deleted,
234
+ ...(o?.includeWorkspaceBinding
235
+ ? { workspaceBinding: d.workspaceBinding }
236
+ : {}),
237
+ };
100
238
  },
101
239
  diffText: () => null, // copy engine has no raw patch — caller uses status()
102
240
  restore: (id, o) => {
103
- const r = cs.restoreCheckpoint(id, { cwd: dir, dryRun: o?.dryRun });
241
+ const r = cs.restoreCheckpoint(id, {
242
+ cwd: dir,
243
+ dryRun: o?.dryRun,
244
+ skipSafety: o?.skipSafety,
245
+ expectedIdentity: o?.expectedIdentity,
246
+ expectedWorkspaceBinding: o?.expectedWorkspaceBinding,
247
+ expectedSafetyPlanIdentity: o?.expectedSafetyPlanIdentity,
248
+ onSafetyReady: o?.onSafetyReady,
249
+ onMutationStarted: o?.onMutationStarted,
250
+ onWorkspaceApplied: o?.onWorkspaceApplied,
251
+ });
104
252
  return {
105
253
  dryRun: !!r.dryRun,
106
254
  restoredCount: r.restored.length,
107
255
  restored: r.restored,
108
256
  missingBlob: r.missingBlob,
109
257
  safetyId: r.safetyId,
258
+ safetyIdentity: r.safetyIdentity,
259
+ safetyPlanIdentity: r.safetyPlanIdentity,
260
+ safetyCoverage: r.safetyCoverage,
261
+ createdPaths: r.createdPaths,
262
+ deletedPaths: r.deletedPaths,
110
263
  };
111
264
  },
112
- remove: (id) => cs.deleteCheckpoint(id),
113
- clear: () => {
114
- const all = cs.listCheckpoints();
115
- for (const c of all) cs.deleteCheckpoint(c.id);
116
- return all.length;
117
- },
265
+ remove: (id) => cs.deleteCheckpoint(id, { cwd: dir }),
266
+ clear: () => cs.clearCheckpoints({ cwd: dir }),
118
267
  };
119
268
  }
120
269
 
@@ -126,14 +275,148 @@ async function pickEngine(dir, session) {
126
275
  return copyEngine(cs, dir);
127
276
  }
128
277
 
278
+ function loadTimelineContext(engine, sessionId) {
279
+ const binding = loadTurnBindingLog(sessionId);
280
+ const checkpoints = engine.list();
281
+ const headHash = findLatestEvent(sessionId, null)?.hash || null;
282
+ const timeline = buildCheckpointTimeline({
283
+ sessionId,
284
+ turns: binding,
285
+ checkpoints,
286
+ headHash,
287
+ });
288
+ return {
289
+ binding,
290
+ checkpoints,
291
+ headHash,
292
+ timeline,
293
+ messages: readVerifiedMessages(sessionId),
294
+ };
295
+ }
296
+
297
+ function printTimelineActionResult(result, asJson) {
298
+ if (asJson) {
299
+ console.log(JSON.stringify(result, null, 2));
300
+ return;
301
+ }
302
+ if (result?.ok === false) {
303
+ logger.error(
304
+ chalk.red(
305
+ `${result.code || "TIMELINE_ACTION_FAILED"}: ${result.error || "request rejected"}`,
306
+ ),
307
+ );
308
+ return;
309
+ }
310
+ logger.log(
311
+ chalk.green(
312
+ `${result.mode === "preview" ? "Preview" : "Completed"}: ${result.action} at ${result.turnId}`,
313
+ ),
314
+ );
315
+ for (const warning of result.warnings || []) {
316
+ logger.log(chalk.yellow(` ${warning}`));
317
+ }
318
+ }
319
+
129
320
  const tag = (engine) => chalk.dim(engine.kind === "git" ? "[git]" : "[copy]");
130
321
 
131
- export function registerCheckpointCommand(program) {
322
+ function isCodeRestoreAction(action) {
323
+ return action === "restore-code" || action === "restore-both";
324
+ }
325
+
326
+ function workspaceStaleError(message = "workspace changed; preview again") {
327
+ const error = new Error(message);
328
+ error.code = "TIMELINE_WORKSPACE_STALE";
329
+ return error;
330
+ }
331
+
332
+ function restoreWorkspaceStaleError(
333
+ message = "workspace changed before the restore lock was acquired; retry the restore",
334
+ ) {
335
+ const error = new Error(message);
336
+ error.code = "CHECKPOINT_WORKSPACE_STALE";
337
+ return error;
338
+ }
339
+
340
+ function directRestoreTargetCount(preview) {
341
+ const groups = [preview?.modified, preview?.added, preview?.deleted];
342
+ if (groups.some((group) => !Array.isArray(group))) {
343
+ throw restoreWorkspaceStaleError(
344
+ "checkpoint restore target count is unavailable; retry the restore",
345
+ );
346
+ }
347
+ const total = groups.reduce((sum, group) => sum + group.length, 0);
348
+ if (!Number.isSafeInteger(total)) {
349
+ throw restoreWorkspaceStaleError(
350
+ "checkpoint restore target count is invalid; retry the restore",
351
+ );
352
+ }
353
+ return total;
354
+ }
355
+
356
+ function directRestorePlan({
357
+ engine,
358
+ checkpointId,
359
+ checkpointNamespace,
360
+ preview,
361
+ authorization,
362
+ }) {
363
+ const checkpointIdentity = preview?.checkpointIdentity;
364
+ const workspaceBinding = preview?.workspaceBinding;
365
+ const workspaceRoot = workspaceBinding?.workspaceRoot;
366
+ if (
367
+ typeof checkpointIdentity !== "string" ||
368
+ checkpointIdentity.length === 0 ||
369
+ !workspaceRoot
370
+ ) {
371
+ throw restoreWorkspaceStaleError(
372
+ "checkpoint immutable identity or workspace scope is unavailable; retry the restore",
373
+ );
374
+ }
375
+ const targetCount = directRestoreTargetCount(preview);
376
+ return {
377
+ restoreKind: engine.kind,
378
+ restoreSurface: "direct",
379
+ checkpointId,
380
+ checkpointIdentity,
381
+ checkpointNamespace,
382
+ workspaceRoot,
383
+ workspaceBinding,
384
+ targetCount,
385
+ confirmationDigest: computeCheckpointRestoreDigest(
386
+ "cc-checkpoint-restore-direct-confirmation-v1",
387
+ {
388
+ authorization,
389
+ restoreKind: engine.kind,
390
+ checkpointId,
391
+ checkpointIdentity,
392
+ checkpointNamespace,
393
+ workspaceBinding,
394
+ targetCount,
395
+ },
396
+ ),
397
+ };
398
+ }
399
+
400
+ export function registerCheckpointCommand(program, dependencies = {}) {
401
+ const withWorkspaceLockSync =
402
+ dependencies.withWorkspaceLockSync || withCanonicalWorkspaceLockSync;
403
+ const createCheckpointRestoreSagaStore =
404
+ dependencies.createCheckpointRestoreSagaStore ||
405
+ ((options) => new CheckpointRestoreSagaStore(options));
406
+ const workspaceLockOwnerDigest =
407
+ dependencies.computeCheckpointRestoreWorkspaceLockOwnerDigest ||
408
+ computeCheckpointRestoreWorkspaceLockOwnerDigest;
409
+ const runCheckpointRestore =
410
+ dependencies.runCheckpointRestoreOperation || runCheckpointRestoreOperation;
132
411
  const cp = program
133
412
  .command("checkpoint")
134
413
  .description("Snapshot / rewind file state (git-plumbing, copy fallback)");
135
414
 
136
415
  registerManagedCheckpointCommands(cp);
416
+ registerCheckpointRestoreRecoveryCommands(
417
+ cp,
418
+ dependencies.checkpointRestoreRecovery,
419
+ );
137
420
 
138
421
  cp.command("create [paths...]")
139
422
  .description(
@@ -226,6 +509,566 @@ export function registerCheckpointCommand(program) {
226
509
  }
227
510
  });
228
511
 
512
+ cp.command("timeline")
513
+ .description(
514
+ "Project persisted turns, checkpoints, side effects, and rewind actions",
515
+ )
516
+ .option("-d, --dir <dir>", "Target directory", ".")
517
+ .option("-s, --session <id>", "Checkpoint/session binding", "default")
518
+ .option("--json", "Output the canonical versioned projection as JSON")
519
+ .action(async (options) => {
520
+ try {
521
+ const dir = resolve(options.dir);
522
+ const engine = await pickEngine(dir, options.session);
523
+ const { timeline } = loadTimelineContext(engine, options.session);
524
+ if (options.json) {
525
+ console.log(JSON.stringify(timeline, null, 2));
526
+ return;
527
+ }
528
+
529
+ const projection = projectCheckpointTimeline(timeline);
530
+ if (projection.entries.length === 0) {
531
+ logger.log(
532
+ chalk.gray(
533
+ timeline.unboundMarkers.length > 0
534
+ ? `${timeline.unboundMarkers.length} checkpoint/marker(s) are not bound to a persisted turn.`
535
+ : "No persisted checkpoint timeline for this session.",
536
+ ),
537
+ );
538
+ return;
539
+ }
540
+ for (const entry of projection.entries) {
541
+ const coverage =
542
+ entry.coverage === "full"
543
+ ? chalk.green(entry.coverage)
544
+ : entry.coverage === "partial"
545
+ ? chalk.yellow(entry.coverage)
546
+ : chalk.red(entry.coverage);
547
+ logger.log(
548
+ `${chalk.cyan(entry.turnId)} ${coverage} ` +
549
+ `${entry.markerKinds.join(", ") || "no markers"} ` +
550
+ `${entry.enabledActions.join(", ") || "no actions"}`,
551
+ );
552
+ if (entry.excludedPaths.length > 0) {
553
+ logger.log(
554
+ chalk.yellow(` excluded: ${entry.excludedPaths.join(", ")}`),
555
+ );
556
+ }
557
+ if (entry.irreversibleSideEffects.length > 0) {
558
+ logger.log(
559
+ chalk.red(
560
+ ` irreversible: ${entry.irreversibleSideEffects.join(", ")}`,
561
+ ),
562
+ );
563
+ }
564
+ }
565
+ } catch (err) {
566
+ logger.error(chalk.red(`checkpoint timeline failed: ${err.message}`));
567
+ process.exitCode = 1;
568
+ }
569
+ });
570
+
571
+ cp.command("action")
572
+ .description(
573
+ "Preview or commit a CLI-authored checkpoint timeline submission",
574
+ )
575
+ .requiredOption(
576
+ "--submission <json>",
577
+ "Exact action envelope embedded in checkpoint timeline --json",
578
+ )
579
+ .option("-d, --dir <dir>", "Target directory", ".")
580
+ .option("-s, --session <id>", "Checkpoint/session binding", "default")
581
+ .option("--preview", "Validate and preview without writing")
582
+ .option("--confirm", "Commit after an IDE/user confirmation")
583
+ .option("--json", "Output the versioned result as JSON")
584
+ .action(async (options) => {
585
+ let output;
586
+ try {
587
+ if (options.preview === options.confirm) {
588
+ const error = new Error(
589
+ "choose exactly one of --preview or --confirm",
590
+ );
591
+ error.code = "TIMELINE_CONFIRMATION_REQUIRED";
592
+ throw error;
593
+ }
594
+ if (String(options.submission).length > 16_384) {
595
+ const error = new Error("timeline submission is too large");
596
+ error.code = "TIMELINE_SUBMISSION_INVALID";
597
+ throw error;
598
+ }
599
+ let submittedEnvelope;
600
+ try {
601
+ submittedEnvelope = JSON.parse(options.submission);
602
+ } catch {
603
+ const error = new Error("timeline submission must be valid JSON");
604
+ error.code = "TIMELINE_SUBMISSION_INVALID";
605
+ throw error;
606
+ }
607
+
608
+ const dir = resolve(options.dir);
609
+ const engine = await pickEngine(dir, options.session);
610
+ const planForContext = (authoritativeContext) => {
611
+ const validation = options.preview
612
+ ? validateCheckpointTimelineSubmission(
613
+ authoritativeContext.timeline,
614
+ submittedEnvelope,
615
+ )
616
+ : validateCheckpointTimelineConfirmationSubmission(
617
+ authoritativeContext.timeline,
618
+ submittedEnvelope,
619
+ );
620
+ if (!validation.ok) {
621
+ const error = new Error(
622
+ validation.code === "TIMELINE_STALE"
623
+ ? "checkpoint timeline is stale; refresh before retrying"
624
+ : "checkpoint timeline submission was rejected",
625
+ );
626
+ error.code = validation.code;
627
+ throw error;
628
+ }
629
+ const authoritativeSubmission = validation.submission;
630
+ const checkpointIdentity =
631
+ authoritativeSubmission.checkpointIdentity || null;
632
+ let codePreview = null;
633
+ if (isCodeRestoreAction(authoritativeSubmission.action)) {
634
+ if (!checkpointIdentity) {
635
+ const error = new Error(
636
+ "checkpoint immutable identity is unavailable; refresh or recreate it",
637
+ );
638
+ error.code = "TIMELINE_CHECKPOINT_IDENTITY_INVALID";
639
+ throw error;
640
+ }
641
+ codePreview = engine.status(authoritativeSubmission.checkpointId, {
642
+ expectedIdentity: checkpointIdentity,
643
+ includeWorkspaceBinding: true,
644
+ });
645
+ }
646
+ const authoritativePlan = planCheckpointTimelineAction({
647
+ timeline: authoritativeContext.timeline,
648
+ submission: authoritativeSubmission,
649
+ messages: authoritativeContext.messages,
650
+ codePreview,
651
+ });
652
+ if (!authoritativePlan.ok) {
653
+ const error = new Error(
654
+ authoritativePlan.code === "TIMELINE_STALE"
655
+ ? "checkpoint timeline is stale; refresh before retrying"
656
+ : "checkpoint timeline submission was rejected",
657
+ );
658
+ error.code = authoritativePlan.code;
659
+ throw error;
660
+ }
661
+ return {
662
+ submission: authoritativeSubmission,
663
+ planned: authoritativePlan,
664
+ codePreview,
665
+ };
666
+ };
667
+
668
+ let context = loadTimelineContext(engine, options.session);
669
+ let { submission, planned } = planForContext(context);
670
+ if (options.preview) {
671
+ output = planned.preview;
672
+ printTimelineActionResult(output, options.json);
673
+ return;
674
+ }
675
+
676
+ if (
677
+ !checkpointTimelineConfirmationsMatch(
678
+ planned.preview.confirmationSubmission,
679
+ submittedEnvelope,
680
+ )
681
+ ) {
682
+ const error = new Error(
683
+ isCodeRestoreAction(submission.action)
684
+ ? "workspace changed after checkpoint preview; preview again"
685
+ : "timeline confirmation no longer matches the current plan",
686
+ );
687
+ error.code = isCodeRestoreAction(submission.action)
688
+ ? "TIMELINE_WORKSPACE_STALE"
689
+ : "TIMELINE_CONFIRMATION_INVALID";
690
+ throw error;
691
+ }
692
+
693
+ let checkpointIdentity = submission.checkpointIdentity || null;
694
+ let restoreExecution = null;
695
+ let restoreOperationId = null;
696
+
697
+ const executeUnderSessionAuthority = ({
698
+ operationId = null,
699
+ workspaceLease = null,
700
+ commitIntent = null,
701
+ restoreWorkspace = null,
702
+ } = {}) => {
703
+ let durableSessionCommitHash = null;
704
+ const transactionResult = withSessionAuthorityTransaction(
705
+ options.session,
706
+ context.headHash,
707
+ (transaction) => {
708
+ workspaceLease?.assertOwned();
709
+ const intent = transaction.appendAuthorityEvent(
710
+ CHECKPOINT_TIMELINE_INTENT_EVENT,
711
+ {
712
+ ...(operationId ? { operationId } : {}),
713
+ revision: context.timeline.revision,
714
+ action: submission.action,
715
+ turnId: submission.turnId,
716
+ checkpointId: submission.checkpointId || null,
717
+ checkpointIdentity,
718
+ workspaceDir: dir,
719
+ workspaceScopeIdentity:
720
+ planned.workspaceBinding?.scopeIdentity || null,
721
+ workspacePrestateIdentity:
722
+ planned.workspaceBinding?.prestateIdentity || null,
723
+ workspaceWritePlanIdentity:
724
+ planned.workspaceBinding?.writePlanIdentity || null,
725
+ workspaceTargetPoststateIdentity:
726
+ planned.workspaceBinding?.targetPoststateIdentity || null,
727
+ confirmationDigest:
728
+ planned.preview.confirmationSubmission.digest,
729
+ },
730
+ );
731
+ const result = {};
732
+ try {
733
+ if (commitIntent) {
734
+ try {
735
+ commitIntent(
736
+ checkpointRestoreDigest(
737
+ "cc-checkpoint-restore-intent-commit-v1",
738
+ intent.hash,
739
+ ),
740
+ );
741
+ } catch (error) {
742
+ // The transcript intent append precedes the short saga CAS.
743
+ // If the CAS fails, recovery must reconcile the durable
744
+ // transcript instead of treating this as a pre-intent abort.
745
+ if (error && typeof error === "object") {
746
+ error.checkpointRestoreSessionIntentCommitted = true;
747
+ // Let the orchestrator attach the post-settlement saga
748
+ // position rather than leaking this failed CAS's stale
749
+ // prepared-phase snapshot as the recovery status.
750
+ error.checkpointRestoreSagaPhase = null;
751
+ error.checkpointRestoreSagaSeq = null;
752
+ error.checkpointRestoreSagaHeadHash = null;
753
+ }
754
+ throw error;
755
+ }
756
+ }
757
+
758
+ if (restoreWorkspace) {
759
+ workspaceLease?.assertOwned();
760
+ result.code = restoreWorkspace();
761
+ transaction.retainRecoveryEvidence({
762
+ safetyId: result.code?.safetyId,
763
+ safetyIdentity: result.code?.safetyIdentity,
764
+ safetyPlanIdentity: result.code?.safetyPlanIdentity,
765
+ safetyCoverage: result.code?.safetyCoverage,
766
+ restorePhase: "workspace-applied",
767
+ createdPaths: result.code?.createdPaths,
768
+ });
769
+ workspaceLease?.assertOwned();
770
+ }
771
+
772
+ if (planned.commit.branchPlan) {
773
+ result.branch = createBranchSession({
774
+ branchSessionId: planned.commit.branchPlan.branchSessionId,
775
+ parentSessionId: options.session,
776
+ parentTurnId: submission.turnId,
777
+ messages: planned.commit.messages,
778
+ meta: { title: `Branch of ${options.session}` },
779
+ });
780
+ transaction.retainRecoveryEvidence({
781
+ branchSessionId:
782
+ result.branch?.branchSessionId ||
783
+ planned.commit.branchPlan.branchSessionId,
784
+ });
785
+ } else if (planned.commit.messages) {
786
+ context.binding.pruneFromOffset(
787
+ planned.commit.bindingPruneOffset,
788
+ );
789
+ const committed = transaction.appendAuthorityEvent(
790
+ TURN_BINDING_TIMELINE_EVENT,
791
+ {
792
+ ...(operationId ? { operationId } : {}),
793
+ action: submission.action,
794
+ sourceRevision: context.timeline.revision,
795
+ turnId: submission.turnId,
796
+ messages: planned.commit.messages,
797
+ binding: context.binding.toJSON(),
798
+ },
799
+ );
800
+ result.conversation = {
801
+ messages: planned.commit.messages.length,
802
+ commitHash: committed.hash,
803
+ };
804
+ }
805
+
806
+ workspaceLease?.assertOwned();
807
+ const completedAudit = transaction.appendAuthorityEvent(
808
+ CHECKPOINT_TIMELINE_AUDIT_EVENT,
809
+ {
810
+ ...(operationId ? { operationId } : {}),
811
+ revision: context.timeline.revision,
812
+ action: submission.action,
813
+ turnId: submission.turnId,
814
+ checkpointId: submission.checkpointId || null,
815
+ checkpointIdentity,
816
+ workspaceDir: dir,
817
+ workspaceScopeIdentity:
818
+ planned.workspaceBinding?.scopeIdentity || null,
819
+ workspacePrestateIdentity:
820
+ planned.workspaceBinding?.prestateIdentity || null,
821
+ workspaceWritePlanIdentity:
822
+ planned.workspaceBinding?.writePlanIdentity || null,
823
+ workspaceTargetPoststateIdentity:
824
+ planned.workspaceBinding?.targetPoststateIdentity || null,
825
+ confirmationDigest:
826
+ planned.preview.confirmationSubmission.digest,
827
+ status: "completed",
828
+ branchSessionId: result.branch?.branchSessionId || null,
829
+ safetyCheckpointId: result.code?.safetyId || null,
830
+ safetyCheckpointIdentity:
831
+ result.code?.safetyIdentity || null,
832
+ safetyPlanIdentity: result.code?.safetyPlanIdentity || null,
833
+ safetyCoverage: result.code?.safetyCoverage || null,
834
+ },
835
+ );
836
+ durableSessionCommitHash = completedAudit.hash;
837
+ } catch (error) {
838
+ if (error && typeof error === "object") {
839
+ error.safetyId ||= result.code?.safetyId || null;
840
+ error.safetyIdentity ||= result.code?.safetyIdentity || null;
841
+ error.safetyPlanIdentity ||=
842
+ result.code?.safetyPlanIdentity || null;
843
+ error.safetyCoverage ||= result.code?.safetyCoverage || null;
844
+ if (result.code && !error.restorePhase) {
845
+ error.restorePhase = "workspace-applied";
846
+ }
847
+ if (!Array.isArray(error.createdPaths)) {
848
+ error.createdPaths = Array.isArray(
849
+ result.code?.createdPaths,
850
+ )
851
+ ? [...result.code.createdPaths]
852
+ : [];
853
+ }
854
+ error.branchSessionId ||=
855
+ result.branch?.branchSessionId ||
856
+ planned.commit.branchPlan?.branchSessionId ||
857
+ null;
858
+ }
859
+ try {
860
+ transaction.appendAuthorityEvent(
861
+ CHECKPOINT_TIMELINE_AUDIT_EVENT,
862
+ {
863
+ ...(operationId ? { operationId } : {}),
864
+ revision: context.timeline.revision,
865
+ action: submission.action,
866
+ turnId: submission.turnId,
867
+ checkpointId: submission.checkpointId || null,
868
+ checkpointIdentity,
869
+ workspaceDir: dir,
870
+ workspaceScopeIdentity:
871
+ planned.workspaceBinding?.scopeIdentity || null,
872
+ workspacePrestateIdentity:
873
+ planned.workspaceBinding?.prestateIdentity || null,
874
+ workspaceWritePlanIdentity:
875
+ planned.workspaceBinding?.writePlanIdentity || null,
876
+ workspaceTargetPoststateIdentity:
877
+ planned.workspaceBinding?.targetPoststateIdentity ||
878
+ null,
879
+ confirmationDigest:
880
+ planned.preview.confirmationSubmission.digest,
881
+ status: "failed",
882
+ failureCode: checkpointRestoreErrorCode(error),
883
+ workspaceState: restoreWorkspace
884
+ ? "unknown"
885
+ : "unchanged",
886
+ branchSessionId:
887
+ result.branch?.branchSessionId ||
888
+ planned.commit.branchPlan?.branchSessionId ||
889
+ null,
890
+ safetyCheckpointId:
891
+ result.code?.safetyId || error?.safetyId || null,
892
+ safetyCheckpointIdentity:
893
+ result.code?.safetyIdentity ||
894
+ error?.safetyIdentity ||
895
+ null,
896
+ safetyPlanIdentity:
897
+ result.code?.safetyPlanIdentity ||
898
+ error?.safetyPlanIdentity ||
899
+ null,
900
+ safetyCoverage:
901
+ result.code?.safetyCoverage ||
902
+ error?.safetyCoverage ||
903
+ null,
904
+ createdPaths: Array.isArray(error?.createdPaths)
905
+ ? error.createdPaths.slice(0, 256)
906
+ : [],
907
+ },
908
+ );
909
+ } catch (auditError) {
910
+ // An authority append can fail after bytes reach the transcript.
911
+ // Retain the first operation error as the transaction's nested
912
+ // diagnosis and let a poisoned writer/final settlement report
913
+ // commitState=unknown instead of appending from a stale head.
914
+ if (error && typeof error === "object") {
915
+ error.checkpointAuditError = auditError;
916
+ }
917
+ }
918
+ throw error;
919
+ }
920
+ return result;
921
+ },
922
+ );
923
+
924
+ if (operationId) {
925
+ workspaceLease?.assertOwned();
926
+ if (!durableSessionCommitHash) {
927
+ const error = new Error(
928
+ "checkpoint restore session settlement hash is unavailable",
929
+ );
930
+ error.code = "CHECKPOINT_RESTORE_SESSION_BOUNDARY_MISSING";
931
+ error.commitState = "unknown";
932
+ throw error;
933
+ }
934
+ return {
935
+ result: transactionResult,
936
+ sessionCommitDigest: checkpointRestoreDigest(
937
+ "cc-checkpoint-restore-session-commit-v1",
938
+ durableSessionCommitHash,
939
+ ),
940
+ };
941
+ }
942
+ return transactionResult;
943
+ };
944
+
945
+ const buildTimelineRestorePlan = () => {
946
+ const workspaceRoot = planned.workspaceBinding?.workspaceRoot;
947
+ if (!workspaceRoot) {
948
+ throw workspaceStaleError(
949
+ "checkpoint restore scope is unavailable; preview again",
950
+ );
951
+ }
952
+ return {
953
+ restoreKind: engine.kind,
954
+ restoreSurface: "timeline",
955
+ checkpointId: submission.checkpointId,
956
+ checkpointIdentity,
957
+ checkpointNamespace: options.session,
958
+ workspaceRoot,
959
+ workspaceBinding: planned.workspaceBinding,
960
+ targetCount: checkpointRestoreTargetCount(planned),
961
+ confirmationDigest: planned.preview.confirmationSubmission.digest,
962
+ sessionId: options.session,
963
+ timelineEntryId: submission.turnId,
964
+ };
965
+ };
966
+
967
+ let result;
968
+ if (isCodeRestoreAction(submission.action)) {
969
+ const initialRestorePlan = buildTimelineRestorePlan();
970
+ restoreExecution = runCheckpointRestore({
971
+ plan: initialRestorePlan,
972
+ revalidate: () => {
973
+ // Both the session head and the full workspace write plan can
974
+ // change while this process waits for the workspace lock.
975
+ const lockedContext = loadTimelineContext(
976
+ engine,
977
+ options.session,
978
+ );
979
+ const lockedAction = planForContext(lockedContext);
980
+ if (
981
+ !checkpointTimelineConfirmationsMatch(
982
+ lockedAction.planned.preview.confirmationSubmission,
983
+ submittedEnvelope,
984
+ )
985
+ ) {
986
+ throw workspaceStaleError(
987
+ "workspace or checkpoint timeline changed while waiting for the restore lock; preview again",
988
+ );
989
+ }
990
+ context = lockedContext;
991
+ submission = lockedAction.submission;
992
+ planned = lockedAction.planned;
993
+ checkpointIdentity = submission.checkpointIdentity || null;
994
+ return buildTimelineRestorePlan();
995
+ },
996
+ restore: ({ expectedIdentity, expectedWorkspaceBinding, hooks }) =>
997
+ engine.restore(submission.checkpointId, {
998
+ expectedIdentity,
999
+ expectedWorkspaceBinding,
1000
+ ...hooks,
1001
+ }),
1002
+ withSessionAuthority: (authority) => {
1003
+ try {
1004
+ return executeUnderSessionAuthority(authority);
1005
+ } catch (error) {
1006
+ const retained = error?.transactionRecoveryEvidence;
1007
+ if (
1008
+ error &&
1009
+ typeof error === "object" &&
1010
+ retained &&
1011
+ typeof retained === "object"
1012
+ ) {
1013
+ error.safetyId ||= retained.safetyId || null;
1014
+ error.safetyIdentity ||= retained.safetyIdentity || null;
1015
+ error.safetyPlanIdentity ||=
1016
+ retained.safetyPlanIdentity || null;
1017
+ error.safetyCoverage ||= retained.safetyCoverage || null;
1018
+ error.restorePhase ||= retained.restorePhase || null;
1019
+ }
1020
+ throw error;
1021
+ }
1022
+ },
1023
+ dependencies: {
1024
+ withWorkspaceLockSync,
1025
+ createSagaStore: createCheckpointRestoreSagaStore,
1026
+ computeWorkspaceLockOwnerDigest: workspaceLockOwnerDigest,
1027
+ },
1028
+ });
1029
+ restoreOperationId = restoreExecution.operationId;
1030
+ result = restoreExecution.result;
1031
+ } else {
1032
+ result = executeUnderSessionAuthority();
1033
+ }
1034
+ let nextContext = null;
1035
+ let nextContextWarning = null;
1036
+ try {
1037
+ nextContext = loadTimelineContext(engine, options.session);
1038
+ } catch (reloadError) {
1039
+ if (restoreExecution?.saga?.phase !== "completed") throw reloadError;
1040
+ nextContextWarning = boundedCheckpointRestoreText(
1041
+ `restore completed, but the next timeline revision could not be reloaded: ${reloadError?.code || reloadError?.message || "reload failed"}`,
1042
+ "restore completed, but the next timeline revision could not be reloaded",
1043
+ 512,
1044
+ );
1045
+ }
1046
+ output = {
1047
+ schema: CHECKPOINT_TIMELINE_RESULT_SCHEMA,
1048
+ version: CHECKPOINT_TIMELINE_RESULT_VERSION,
1049
+ ok: true,
1050
+ mode: "executed",
1051
+ action: submission.action,
1052
+ sessionId: options.session,
1053
+ turnId: submission.turnId,
1054
+ revision: context.timeline.revision,
1055
+ nextRevision: nextContext?.timeline?.revision || null,
1056
+ ...(restoreOperationId ? { operationId: restoreOperationId } : {}),
1057
+ result,
1058
+ warnings: [
1059
+ ...planned.preview.warnings,
1060
+ ...(restoreExecution?.warnings || []),
1061
+ ...(nextContextWarning ? [nextContextWarning] : []),
1062
+ ],
1063
+ };
1064
+ printTimelineActionResult(output, options.json);
1065
+ } catch (error) {
1066
+ output = timelineActionError(error);
1067
+ printTimelineActionResult(output, options.json);
1068
+ process.exitCode = 1;
1069
+ }
1070
+ });
1071
+
229
1072
  cp.command("show <id>")
230
1073
  .description("Show a checkpoint's files, or its diff vs current state")
231
1074
  .option("-d, --dir <dir>", "Target directory", ".")
@@ -310,12 +1153,18 @@ export function registerCheckpointCommand(program) {
310
1153
  return;
311
1154
  }
312
1155
 
313
- // Destructive: overwrites current files. Require --force when not a TTY;
314
- // prompt when interactive.
1156
+ // Destructive: bind the exact workspace state that is displayed (or
1157
+ // immediately preflighted for --force), then require the engine to
1158
+ // observe the same full state before safety creation or file writes.
1159
+ // Require --force when not a TTY; prompt when interactive.
1160
+ const restorePreview = engine.status(id, {
1161
+ includeWorkspaceBinding: true,
1162
+ });
315
1163
  if (!options.force) {
316
- const d = engine.status(id);
317
1164
  const willChange =
318
- d.modified.length + d.deleted.length + (d.added?.length || 0);
1165
+ restorePreview.modified.length +
1166
+ restorePreview.deleted.length +
1167
+ (restorePreview.added?.length || 0);
319
1168
  if (process.stdin.isTTY) {
320
1169
  const { confirm } = await import("@inquirer/prompts");
321
1170
  const ok = await confirm({
@@ -337,9 +1186,50 @@ export function registerCheckpointCommand(program) {
337
1186
  }
338
1187
  }
339
1188
 
340
- const r = engine.restore(id);
1189
+ const authorization = options.force ? "force" : "interactive";
1190
+ const initialPlan = directRestorePlan({
1191
+ engine,
1192
+ checkpointId: id,
1193
+ checkpointNamespace: options.session,
1194
+ preview: restorePreview,
1195
+ authorization,
1196
+ });
1197
+ const execution = runCheckpointRestore({
1198
+ plan: initialPlan,
1199
+ revalidate: () => {
1200
+ const lockedPreview = engine.status(id, {
1201
+ expectedIdentity: initialPlan.checkpointIdentity,
1202
+ includeWorkspaceBinding: true,
1203
+ });
1204
+ return directRestorePlan({
1205
+ engine,
1206
+ checkpointId: id,
1207
+ checkpointNamespace: options.session,
1208
+ preview: lockedPreview,
1209
+ authorization,
1210
+ });
1211
+ },
1212
+ restore: ({ expectedIdentity, expectedWorkspaceBinding, hooks }) =>
1213
+ engine.restore(id, {
1214
+ expectedIdentity,
1215
+ expectedWorkspaceBinding,
1216
+ ...hooks,
1217
+ }),
1218
+ dependencies: {
1219
+ withWorkspaceLockSync,
1220
+ createSagaStore: createCheckpointRestoreSagaStore,
1221
+ computeWorkspaceLockOwnerDigest: workspaceLockOwnerDigest,
1222
+ },
1223
+ });
1224
+ const r = execution.result;
1225
+ const directOutput = {
1226
+ ...r,
1227
+ operationId: execution.operationId,
1228
+ phase: execution.saga.phase,
1229
+ warnings: execution.warnings,
1230
+ };
341
1231
  if (options.json) {
342
- console.log(JSON.stringify(r, null, 2));
1232
+ console.log(JSON.stringify(directOutput, null, 2));
343
1233
  return;
344
1234
  }
345
1235
  logger.log(
@@ -365,8 +1255,43 @@ export function registerCheckpointCommand(program) {
365
1255
  chalk.red(` missing blobs (skipped): ${r.missingBlob.join(", ")}`),
366
1256
  );
367
1257
  }
1258
+ logger.log(
1259
+ chalk.gray(
1260
+ ` restore operation: ${execution.operationId} (${execution.saga.phase})`,
1261
+ ),
1262
+ );
1263
+ for (const warning of execution.warnings) {
1264
+ logger.log(chalk.yellow(` ${warning}`));
1265
+ }
368
1266
  } catch (err) {
369
- logger.error(chalk.red(`checkpoint restore failed: ${err.message}`));
1267
+ if (options.json) {
1268
+ console.log(
1269
+ JSON.stringify(
1270
+ {
1271
+ ok: false,
1272
+ code: err?.code || "CHECKPOINT_RESTORE_FAILED",
1273
+ error: err?.message || String(err),
1274
+ operationId:
1275
+ err?.checkpointRestoreOperationId || err?.operationId || null,
1276
+ phase: err?.checkpointRestoreSagaPhase || null,
1277
+ blockingOperationId:
1278
+ err?.ownerTransactionId ||
1279
+ err?.retainedOwner?.transactionId ||
1280
+ err?.priorOwner?.transactionId ||
1281
+ null,
1282
+ recoveryRequired: Boolean(
1283
+ err?.checkpointRestoreRecoveryRequired ||
1284
+ err?.workspaceLockRetained,
1285
+ ),
1286
+ workspaceLockRetained: err?.workspaceLockRetained === true,
1287
+ },
1288
+ null,
1289
+ 2,
1290
+ ),
1291
+ );
1292
+ } else {
1293
+ logger.error(chalk.red(`checkpoint restore failed: ${err.message}`));
1294
+ }
370
1295
  process.exitCode = 1;
371
1296
  }
372
1297
  });