chainlesschain 0.162.188 → 0.162.193

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 (337) hide show
  1. package/README.md +61 -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-Brr1LVcE.js → AIOps-DBLkaTje.js} +1 -1
  9. package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-BAy-Qebo.js} +1 -1
  10. package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-Dfb1Mlau.js} +3 -3
  11. package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-BbFkN8m0.js} +5 -5
  12. package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-kM4-cEEH.js} +1 -1
  13. package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-BSQfYWf3.js} +1 -1
  14. package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-CWDbI_fG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BxLPbq_Q.js} +1 -1
  16. package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-DuIbdrp6.js} +1 -1
  17. package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-NEmjGahU.js} +5 -5
  18. package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DfJEo-5_.js} +1 -1
  19. package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CaoeG3Ns.js} +1 -1
  20. package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-BFf_g3xY.js} +1 -1
  21. package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CNOd8mdQ.js} +1 -1
  22. package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-BhpDqz-i.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-CQ42T7Z6.js} +1 -1
  24. package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-DI5PcLMN.js} +1 -1
  25. package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-wYYZe0wR.js} +3 -3
  26. package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-Dlq03DD_.js} +2 -2
  27. package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BzqS8PP3.js} +1 -1
  28. package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-It55Yem7.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-9HkH6oxn.js} +2 -2
  30. package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-00e2YWRB.js} +1 -1
  31. package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-By-ugRDL.js} +1 -1
  32. package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-C396yu5m.js} +1 -1
  33. package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-BME5sbZY.js} +1 -1
  34. package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-B4TEZCr-.js} +1 -1
  35. package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-_9CX9ieY.js} +1 -1
  36. package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-BrS6WVSr.js} +2 -2
  37. package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-BMdCWnKF.js} +1 -1
  38. package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-Bcp3vzcA.js} +1 -1
  39. package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-DlLuqGY5.js} +1 -1
  40. package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-D-W_6x09.js} +2 -2
  41. package/src/assets/web-panel/assets/MarkdownRenderer-DElGPxCZ.js +1 -0
  42. package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-Dzn2guvI.js} +1 -1
  43. package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-uPict-KL.js} +6 -6
  44. package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-BCw43kOb.js} +2 -2
  45. package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-DmV8Wv7F.js} +1 -1
  46. package/src/assets/web-panel/assets/{MobileProjects-COBbEOlj.js → MobileProjects-C-HaCmSM.js} +1 -1
  47. package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-C6jxJ34Y.js} +5 -5
  48. package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-Za8ExwhQ.js} +5 -5
  49. package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-KKDbuEgb.js} +3 -3
  50. package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-Dj97zbCk.js} +1 -1
  51. package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DBXvMraU.js} +4 -4
  52. package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-DOxITLYl.js} +1 -1
  53. package/src/assets/web-panel/assets/OrderTableRenderer-7DVjZwMM.js +1 -0
  54. package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D1i73mjb.js} +4 -4
  55. package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-kW6Q3rvg.js} +1 -1
  56. package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-C-v_8p2v.js} +2 -2
  57. package/src/assets/web-panel/assets/PdhVaultBrowser-tUbbY64W.js +7 -0
  58. package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-DkHxEZ58.js} +4 -4
  59. package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-DWuMPGrK.js} +5 -5
  60. package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-30IYXWWm.js} +1 -1
  61. package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-DZNQ4RrH.js} +1 -1
  62. package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-De2MCElf.js} +2 -2
  63. package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-5uHk9wsC.js} +2 -2
  64. package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-XjDfrFLb.js} +1 -1
  65. package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-Bgwj6gcE.js} +1 -1
  66. package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-gKql9umv.js} +1 -1
  67. package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-BWvmwkh7.js} +1 -1
  68. package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-Uok4hZPd.js} +1 -1
  69. package/src/assets/web-panel/assets/{RemoteSession-oOVIv9wq.js → RemoteSession-DLJzTk9a.js} +3 -3
  70. package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-ZVwIPMSO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CN04HxWR.js} +1 -1
  72. package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BdSrEG4g.js} +3 -3
  73. package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-5PwVd0IR.js} +1 -1
  74. package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-DdBo4zjN.js} +3 -3
  75. package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-D3letU7M.js} +2 -2
  76. package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton--Jb83f1v.js} +1 -1
  77. package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-Bg-CulHX.js} +1 -1
  78. package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-7QOa7S-X.js} +1 -1
  79. package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-DQwppjRw.js} +1 -1
  80. package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-dNPQGL-s.js} +2 -2
  81. package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-Ggs-Z48q.js} +1 -1
  82. package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-78TYHgRO.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-xHwwv9vO.js} +1 -1
  84. package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BJXSJfm4.js} +2 -2
  85. package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-fLbjRhJw.js} +1 -1
  86. package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-oMSBSBmB.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-CfnY_wX_.js} +1 -1
  88. package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-AdyTgYKO.js} +1 -1
  89. package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-C5K_81dS.js} +1 -1
  90. package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-vcGVDqki.js} +1 -1
  91. package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-hhOc3hlY.js} +4 -4
  92. package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-CVloC11s.js} +3 -3
  93. package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-CYeE32R5.js} +1 -1
  94. package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-CNROR5bN.js} +1 -1
  95. package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-Cjx-cnwU.js} +1 -1
  96. package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-DOIfelvD.js} +1 -1
  97. package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-DfA7UZ8u.js} +1 -1
  98. package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +1 -0
  99. package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-BMSCzcfM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-5-HXgOX5.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-AWg-0M1S.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-B4iYzpkL.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-B5Udeb_6.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-B7gf77CJ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-BEMwBBHk.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-BM1y_9hM.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-BUonmKSE.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-BbQ2eQ8H.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-BemJ2u_E.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-BolaOQxx.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-BxOk9vkG.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-By61ojUx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-C2K9SNVX.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-C3L5gdiD.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-C7xn0Nub.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-COG61sRv.js → index-CAnvhwg6.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CEKMcOyU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-CNC5onm4.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CVL27kXC.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-CVm1_i3t.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-CdYBye8B.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-ClNMrqEY.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-Cs6l7uRu.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-CshQbilz.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-Cyweh0Hy.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-DBpWXFed.js} +3 -3
  127. package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-DHtpHZAQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-DLmn0BoK.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-DPAWBrMf.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-DjEsxO5h.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-FBc_CJBd.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Icdoqmzt.js} +1 -1
  133. package/src/assets/web-panel/assets/index-Q2IajL54.js +1 -0
  134. package/src/assets/web-panel/assets/index-bRkHgOMc.js +1 -0
  135. package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-dCd8cSUq.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-m2vG9gG_.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-CaslYch6.js → index-mTpHoEzr.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-ti_lnTWl.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-Bu8k7H1Y.js} +1 -1
  140. package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-s3iVK41k.js} +1 -1
  141. package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-B6B-E5Jz.js} +1 -1
  142. package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-BiYgTidA.js} +1 -1
  143. package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-pU1RdUDA.js} +1 -1
  144. package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-Cdoq_SeM.js} +1 -1
  145. package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-DIuvAn2K.js} +1 -1
  146. package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-8w16YR2D.js} +1 -1
  147. package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-VZ01cZe3.js} +1 -1
  148. package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-lh8YMN8Z.js} +1 -1
  149. package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-CmDyreec.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-hiJWMU5y.js} +1 -1
  151. package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-Thnu-Iaj.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-LBdOxjwY.js} +1 -1
  153. package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-Cg6lFY_X.js} +1 -1
  154. package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-DVfsR1YO.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 +1107 -175
  159. package/src/command-surface-policy.js +197 -0
  160. package/src/commands/agent.js +98 -33
  161. package/src/commands/artifacts.js +400 -2
  162. package/src/commands/background-session.js +41 -3
  163. package/src/commands/checkpoint-managed.js +1059 -0
  164. package/src/commands/checkpoint-restore-recovery.js +1476 -0
  165. package/src/commands/checkpoint.js +950 -22
  166. package/src/commands/compact.js +101 -25
  167. package/src/commands/config.js +506 -20
  168. package/src/commands/doctor.js +4 -1
  169. package/src/commands/hub.js +1 -1
  170. package/src/commands/mcp.js +688 -24
  171. package/src/commands/pack.js +16 -2
  172. package/src/commands/plugin.js +20 -12
  173. package/src/commands/session-mcp-recovery.js +218 -0
  174. package/src/commands/session.js +470 -110
  175. package/src/commands/setup.js +36 -10
  176. package/src/commands/skill.js +181 -110
  177. package/src/commands/status.js +21 -5
  178. package/src/commands/team-distributed.js +3324 -0
  179. package/src/commands/team.js +1130 -74
  180. package/src/commands/update.js +68 -6
  181. package/src/constants.js +11 -0
  182. package/src/data/changelog.json +26 -0
  183. package/src/gateways/ws/action-protocol.js +10 -0
  184. package/src/gateways/ws/session-protocol.js +555 -27
  185. package/src/gateways/ws/ws-agent-handler.js +541 -14
  186. package/src/gateways/ws/ws-session-gateway.js +129 -22
  187. package/src/gateways/ws/ws-session-state.js +431 -0
  188. package/src/harness/background-task-manager.js +233 -36
  189. package/src/harness/jsonl-session-store.js +2551 -167
  190. package/src/harness/mcp-client.js +802 -61
  191. package/src/harness/prompt-compressor.js +227 -27
  192. package/src/harness/session-index.js +89 -38
  193. package/src/harness/session-list-index.js +254 -0
  194. package/src/harness/structured-handoff.js +410 -0
  195. package/src/lazy-dispatch.js +747 -84
  196. package/src/lib/__tests__/logger.test.js +50 -2
  197. package/src/lib/advisor-runtime.js +1047 -0
  198. package/src/lib/agent-core.js +2 -0
  199. package/src/lib/agent-sandbox.js +120 -26
  200. package/src/lib/agent-session-export.js +20 -0
  201. package/src/lib/agent-team/task-lease.js +271 -3
  202. package/src/lib/agent-team/team-adjudication.js +1501 -0
  203. package/src/lib/agent-team/team-budget.js +4 -1
  204. package/src/lib/agent-team/team-control-store.js +582 -0
  205. package/src/lib/agent-team/team-distributed-queue.js +5246 -0
  206. package/src/lib/agent-team/team-process-checkpoint.js +211 -0
  207. package/src/lib/agent-team/team-runner.js +543 -24
  208. package/src/lib/agent-team/team-worktree.js +1403 -95
  209. package/src/lib/artifact-store.js +131 -8
  210. package/src/lib/background-agent-supervisor.js +185 -13
  211. package/src/lib/background-interaction-journal.js +37 -9
  212. package/src/lib/background-launch-profile.js +1058 -0
  213. package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
  214. package/src/lib/checkpoint-restore-orchestrator.js +987 -0
  215. package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
  216. package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
  217. package/src/lib/checkpoint-restore-recovery.js +884 -0
  218. package/src/lib/checkpoint-restore-retention.js +126 -0
  219. package/src/lib/checkpoint-restore-saga.js +5079 -0
  220. package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
  221. package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
  222. package/src/lib/checkpoint-store.js +1688 -97
  223. package/src/lib/checkpoint-timeline-authority.js +522 -0
  224. package/src/lib/checkpoint-timeline.js +642 -0
  225. package/src/lib/config-keys.js +18 -119
  226. package/src/lib/config-manager.js +446 -27
  227. package/src/lib/config-redaction.js +57 -0
  228. package/src/lib/config-schema.js +853 -0
  229. package/src/lib/cowork-mcp-tools.js +5 -0
  230. package/src/lib/cowork-task-runner.js +458 -63
  231. package/src/lib/delivery-coordinator.js +1234 -0
  232. package/src/lib/delivery-evidence.js +450 -0
  233. package/src/lib/doctor-checkup.js +199 -14
  234. package/src/lib/executable-path.js +34 -0
  235. package/src/lib/fatal-handler.js +16 -0
  236. package/src/lib/file-checkpoint.js +4909 -186
  237. package/src/lib/file-lines.js +125 -0
  238. package/src/lib/headless-config-command.js +16 -5
  239. package/src/lib/hook-runner.cjs +139 -38
  240. package/src/lib/hooks-v2-workspace-context.js +10 -0
  241. package/src/lib/impacted-gate-selector.js +424 -0
  242. package/src/lib/interaction-adapter.js +62 -0
  243. package/src/lib/jsonl-session-store.js +1 -35
  244. package/src/lib/logger.js +66 -14
  245. package/src/lib/managed-checkpoint-render.js +60 -0
  246. package/src/lib/managed-tool-checkpoint.js +194 -0
  247. package/src/lib/mcp-call-ledger-store.js +1069 -0
  248. package/src/lib/mcp-call-ledger.js +747 -0
  249. package/src/lib/mcp-client.js +5 -0
  250. package/src/lib/mcp-conflict-scheduler.js +468 -0
  251. package/src/lib/mcp-effect-contract.js +102 -0
  252. package/src/lib/mcp-headers-helper-trust.js +145 -0
  253. package/src/lib/mcp-headers-helper.js +616 -0
  254. package/src/lib/mcp-host-recovery-runtime.js +131 -0
  255. package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
  256. package/src/lib/mcp-recovery-adjudication.js +443 -0
  257. package/src/lib/output-context.js +86 -0
  258. package/src/lib/packer/file-identity.js +170 -0
  259. package/src/lib/packer/index.js +3 -0
  260. package/src/lib/packer/native-update-state.js +277 -0
  261. package/src/lib/packer/pack-update-applier.js +1983 -136
  262. package/src/lib/packer/pack-update-checker.js +34 -0
  263. package/src/lib/packer/pack-update-downloader.js +875 -86
  264. package/src/lib/packer/pack-update-signature.js +86 -0
  265. package/src/lib/packer/pkg-config-generator.js +6 -0
  266. package/src/lib/paths.js +65 -10
  267. package/src/lib/plan-mode.js +831 -14
  268. package/src/lib/plan-persistence.js +540 -0
  269. package/src/lib/plugin-runtime/hooks.js +227 -69
  270. package/src/lib/plugin-runtime/plugin-options.js +189 -44
  271. package/src/lib/pr-automation-policy.js +146 -8
  272. package/src/lib/process-execution-broker/credential-agent.js +7 -0
  273. package/src/lib/process-execution-broker/index.js +491 -13
  274. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
  275. package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
  276. package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
  277. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  278. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  279. package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
  280. package/src/lib/process-execution-broker/workspace-transaction.js +4459 -0
  281. package/src/lib/project-instructions.js +165 -29
  282. package/src/lib/project-mcp-trust.js +67 -0
  283. package/src/lib/public-ide-capability-manifest.js +272 -0
  284. package/src/lib/public-ide-capability-validation.js +351 -0
  285. package/src/lib/secret-store.js +267 -43
  286. package/src/lib/secure-file-identity.js +433 -0
  287. package/src/lib/secure-fs.js +1089 -0
  288. package/src/lib/security-audit.js +58 -0
  289. package/src/lib/service-manager.js +34 -13
  290. package/src/lib/session-budget-runtime.js +1452 -0
  291. package/src/lib/session-host-snapshot.js +388 -0
  292. package/src/lib/session-message-provenance.js +429 -0
  293. package/src/lib/session-projection.js +804 -0
  294. package/src/lib/session-resource-budget.js +1495 -0
  295. package/src/lib/settings-hooks.cjs +138 -22
  296. package/src/lib/settings-loader.cjs +25 -2
  297. package/src/lib/side-effect-ledger-store.js +45 -23
  298. package/src/lib/skill-execution-identity.js +217 -0
  299. package/src/lib/skill-loader.js +299 -18
  300. package/src/lib/sub-agent-context.js +236 -28
  301. package/src/lib/sub-agent-registry.js +5 -0
  302. package/src/lib/subagent-contract.js +94 -0
  303. package/src/lib/todo-manager.js +185 -18
  304. package/src/lib/todo-persistence.js +417 -0
  305. package/src/lib/turn-binding-store.js +41 -23
  306. package/src/lib/turn-binding.js +160 -2
  307. package/src/lib/with-file-lock.js +165 -24
  308. package/src/lib/ws-chat-handler.js +21 -0
  309. package/src/program-base.js +18 -0
  310. package/src/repl/advisor-command.js +103 -0
  311. package/src/repl/agent-repl.js +2175 -146
  312. package/src/repl/btw-command.js +93 -10
  313. package/src/repl/clipboard-image.js +119 -0
  314. package/src/repl/config-summary.js +64 -108
  315. package/src/repl/prompt-editor.js +233 -0
  316. package/src/repl/prompt-interactions.js +344 -0
  317. package/src/repl/prompt-stash.js +354 -0
  318. package/src/repl/prompt-suggestions.js +312 -0
  319. package/src/repl/repl-keybindings.js +227 -0
  320. package/src/repl/session-recap.js +217 -0
  321. package/src/repl/slash-command-registry.js +22 -0
  322. package/src/repl/terminal-layout.js +88 -0
  323. package/src/runtime/agent-core.js +1630 -447
  324. package/src/runtime/coding-agent-contract-shared.cjs +13 -1
  325. package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
  326. package/src/runtime/diagnostics.js +32 -13
  327. package/src/runtime/headless-runner.js +310 -118
  328. package/src/runtime/headless-stream.js +356 -95
  329. package/src/runtime/mcp-config.js +148 -14
  330. package/src/runtime/policies/agent-policy.js +14 -0
  331. package/src/runtime/status-diagnostics-lite.js +137 -0
  332. package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
  333. package/src/assets/web-panel/assets/OrderTableRenderer-BU2Yqq5H.js +0 -1
  334. package/src/assets/web-panel/assets/PdhVaultBrowser-69ca_WeR.js +0 -7
  335. package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
  336. package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
  337. package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Durable prompt stash used by `/stash`.
3
+ *
4
+ * Prompts may contain private source code or credentials, so the state lives in
5
+ * the owner-only ChainlessChain state directory and is replaced atomically.
6
+ * Mutations share the normal cross-process file lock used by CLI state stores.
7
+ */
8
+ import {
9
+ existsSync,
10
+ lstatSync,
11
+ readFileSync,
12
+ renameSync,
13
+ rmSync,
14
+ writeFileSync,
15
+ } from "node:fs";
16
+ import { randomUUID } from "node:crypto";
17
+ import { dirname, join } from "node:path";
18
+ import { getStatePath } from "../lib/paths.js";
19
+ import {
20
+ ensurePrivateDirectory,
21
+ ensurePrivateFile,
22
+ PRIVATE_FILE_MODE,
23
+ } from "../lib/secure-fs.js";
24
+ import { withFileLock } from "../lib/with-file-lock.js";
25
+
26
+ export const PROMPT_STASH_SCHEMA = 1;
27
+ export const MAX_STASH_ENTRIES = 50;
28
+ export const MAX_STASH_PROMPT_CHARS = 100_000;
29
+ export const MAX_STASH_FILE_BYTES = 32 * 1024 * 1024;
30
+
31
+ export function getPromptStashPath() {
32
+ return join(getStatePath(), "prompt-stash-v1.json");
33
+ }
34
+
35
+ function emptyDocument() {
36
+ return { schema: PROMPT_STASH_SCHEMA, entries: [] };
37
+ }
38
+
39
+ function normalizePrompt(value) {
40
+ const text = String(value ?? "").replace(/\0/g, "");
41
+ if (!text.trim()) throw new Error("prompt stash requires non-empty text");
42
+ if (text.length > MAX_STASH_PROMPT_CHARS) {
43
+ throw new Error(
44
+ `prompt exceeds stash limit (${MAX_STASH_PROMPT_CHARS} characters)`,
45
+ );
46
+ }
47
+ return text;
48
+ }
49
+
50
+ function normalizeEntry(entry) {
51
+ if (!entry || typeof entry !== "object") return null;
52
+ if (typeof entry.id !== "string" || typeof entry.text !== "string") {
53
+ return null;
54
+ }
55
+ if (!entry.text.trim() || entry.text.length > MAX_STASH_PROMPT_CHARS) {
56
+ return null;
57
+ }
58
+ return {
59
+ id: entry.id.slice(0, 100),
60
+ text: entry.text.replace(/\0/g, ""),
61
+ createdAt: Number.isFinite(Number(entry.createdAt))
62
+ ? Number(entry.createdAt)
63
+ : 0,
64
+ };
65
+ }
66
+
67
+ export class PromptStash {
68
+ constructor(options = {}) {
69
+ this.filePath = options.filePath || getPromptStashPath();
70
+ this.maxEntries = Math.max(
71
+ 1,
72
+ Math.floor(Number(options.maxEntries) || MAX_STASH_ENTRIES),
73
+ );
74
+ this.now = options.now || Date.now;
75
+ this.uuid = options.uuid || randomUUID;
76
+ this.deps = {
77
+ existsSync,
78
+ lstatSync,
79
+ readFileSync,
80
+ renameSync,
81
+ rmSync,
82
+ writeFileSync,
83
+ ensurePrivateDirectory,
84
+ ensurePrivateFile,
85
+ withFileLock,
86
+ ...(options.deps || {}),
87
+ };
88
+ }
89
+
90
+ _assertReadablePath() {
91
+ if (!this.deps.existsSync(this.filePath)) return;
92
+ const stat = this.deps.lstatSync(this.filePath);
93
+ if (stat.isSymbolicLink()) {
94
+ const error = new Error("refusing prompt stash through a symbolic link");
95
+ error.code = "PROMPT_STASH_SYMLINK";
96
+ throw error;
97
+ }
98
+ if (Number(stat.size) > MAX_STASH_FILE_BYTES) {
99
+ const error = new Error(
100
+ `prompt stash exceeds ${MAX_STASH_FILE_BYTES} bytes`,
101
+ );
102
+ error.code = "PROMPT_STASH_TOO_LARGE";
103
+ throw error;
104
+ }
105
+ this.deps.ensurePrivateFile(this.filePath, {
106
+ applyWindowsAcl: true,
107
+ failIfUnavailable: true,
108
+ });
109
+ }
110
+
111
+ _read() {
112
+ this._assertReadablePath();
113
+ if (!this.deps.existsSync(this.filePath)) return emptyDocument();
114
+ let parsed;
115
+ try {
116
+ parsed = JSON.parse(this.deps.readFileSync(this.filePath, "utf8"));
117
+ } catch (cause) {
118
+ const error = new Error(`prompt stash is malformed: ${cause.message}`);
119
+ error.code = "PROMPT_STASH_CORRUPT";
120
+ error.cause = cause;
121
+ throw error;
122
+ }
123
+ if (
124
+ parsed?.schema !== PROMPT_STASH_SCHEMA ||
125
+ !Array.isArray(parsed.entries)
126
+ ) {
127
+ const error = new Error("prompt stash has an unsupported schema");
128
+ error.code = "PROMPT_STASH_SCHEMA";
129
+ throw error;
130
+ }
131
+ return {
132
+ schema: PROMPT_STASH_SCHEMA,
133
+ entries: parsed.entries
134
+ .map(normalizeEntry)
135
+ .filter(Boolean)
136
+ .slice(-this.maxEntries),
137
+ };
138
+ }
139
+
140
+ _write(document) {
141
+ const directory = dirname(this.filePath);
142
+ this.deps.ensurePrivateDirectory(directory, {
143
+ applyWindowsAcl: true,
144
+ failIfUnavailable: true,
145
+ });
146
+ const tempPath = `${this.filePath}.${process.pid}.${this.uuid().slice(0, 8)}.tmp`;
147
+ try {
148
+ this.deps.writeFileSync(
149
+ tempPath,
150
+ `${JSON.stringify(document, null, 2)}\n`,
151
+ { encoding: "utf8", mode: PRIVATE_FILE_MODE },
152
+ );
153
+ this.deps.ensurePrivateFile(tempPath, {
154
+ applyWindowsAcl: true,
155
+ failIfUnavailable: true,
156
+ });
157
+ this._renameWithRetry(tempPath);
158
+ this.deps.ensurePrivateFile(this.filePath, {
159
+ applyWindowsAcl: true,
160
+ failIfUnavailable: true,
161
+ });
162
+ } finally {
163
+ if (this.deps.existsSync(tempPath)) {
164
+ this.deps.rmSync(tempPath, { force: true });
165
+ }
166
+ }
167
+ }
168
+
169
+ _renameWithRetry(tempPath) {
170
+ for (let attempt = 0; ; attempt += 1) {
171
+ try {
172
+ this.deps.renameSync(tempPath, this.filePath);
173
+ return;
174
+ } catch (error) {
175
+ const transient = ["EACCES", "EBUSY", "EEXIST", "EPERM"].includes(
176
+ error?.code,
177
+ );
178
+ if (!transient || attempt >= 7) throw error;
179
+ try {
180
+ Atomics.wait(
181
+ new Int32Array(new SharedArrayBuffer(4)),
182
+ 0,
183
+ 0,
184
+ 10 * (attempt + 1),
185
+ );
186
+ } catch {
187
+ // A runtime without Atomics.wait simply retries immediately.
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ _mutate(fn) {
194
+ this.deps.ensurePrivateDirectory(dirname(this.filePath), {
195
+ applyWindowsAcl: true,
196
+ failIfUnavailable: true,
197
+ });
198
+ return this.deps.withFileLock(
199
+ this.filePath,
200
+ () => {
201
+ const document = this._read();
202
+ const result = fn(document);
203
+ this._write(document);
204
+ return result;
205
+ },
206
+ { failIfUnavailable: true, timeoutMs: 5_000 },
207
+ );
208
+ }
209
+
210
+ stash(prompt) {
211
+ const text = normalizePrompt(prompt);
212
+ return this._mutate((document) => {
213
+ const createdAt = this.now();
214
+ const entry = {
215
+ id: `stash-${createdAt}-${this.uuid().slice(0, 8)}`,
216
+ text,
217
+ createdAt,
218
+ };
219
+ document.entries.push(entry);
220
+ if (document.entries.length > this.maxEntries) {
221
+ document.entries.splice(0, document.entries.length - this.maxEntries);
222
+ }
223
+ return entry;
224
+ });
225
+ }
226
+
227
+ list() {
228
+ return this._read().entries.slice().reverse();
229
+ }
230
+
231
+ pop() {
232
+ return this._mutate((document) => document.entries.pop() || null);
233
+ }
234
+
235
+ clear() {
236
+ return this._mutate((document) => {
237
+ const count = document.entries.length;
238
+ document.entries = [];
239
+ return count;
240
+ });
241
+ }
242
+ }
243
+
244
+ export function promptStashPreview(text, maxChars = 72) {
245
+ const flat = String(text || "")
246
+ .replace(/[\p{Cc}\p{Cf}]+/gu, " ")
247
+ .replace(/\s+/g, " ")
248
+ .trim();
249
+ const limit = Math.max(20, Number(maxChars) || 72);
250
+ return flat.length > limit
251
+ ? `${flat.slice(0, Math.max(1, limit - 1))}…`
252
+ : flat;
253
+ }
254
+
255
+ export function renderPromptStash(entries, options = {}) {
256
+ const rows = Array.isArray(entries) ? entries : [];
257
+ if (!rows.length) return "Prompt stash is empty.";
258
+ const lines = ["Stashed prompts (newest first):"];
259
+ rows.forEach((entry, index) => {
260
+ let when = "unknown time";
261
+ if (entry.createdAt) {
262
+ const date = new Date(entry.createdAt);
263
+ if (!Number.isNaN(date.getTime())) {
264
+ when = date.toISOString().replace("T", " ").slice(0, 19);
265
+ }
266
+ }
267
+ lines.push(
268
+ ` ${index + 1}. ${promptStashPreview(entry.text, options.previewChars)} (${when})`,
269
+ );
270
+ });
271
+ return lines.join("\n");
272
+ }
273
+
274
+ /** Parse the text after `/stash`. */
275
+ export function parsePromptStashCommand(args, currentPrompt = "") {
276
+ const raw = String(args || "");
277
+ const trimmed = raw.trim();
278
+ if (!trimmed) {
279
+ return String(currentPrompt || "").trim()
280
+ ? { action: "stash", prompt: String(currentPrompt) }
281
+ : { action: "help" };
282
+ }
283
+ const command = trimmed.split(/\s+/, 1)[0].toLowerCase();
284
+ if (command === "list" || command === "ls") return { action: "list" };
285
+ if (command === "pop") return { action: "pop" };
286
+ if (command === "clear") return { action: "clear" };
287
+ if (command === "add" || command === "push") {
288
+ const prompt = trimmed.slice(command.length).trim();
289
+ return prompt ? { action: "stash", prompt } : { action: "help" };
290
+ }
291
+ return { action: "stash", prompt: trimmed };
292
+ }
293
+
294
+ /**
295
+ * Execute a `/stash` operation. A popped prompt is returned, never submitted;
296
+ * the REPL wiring should place it back in the editable input buffer.
297
+ */
298
+ export function runPromptStashCommand(args, options = {}) {
299
+ const stash = options.stash || new PromptStash(options);
300
+ const parsed = parsePromptStashCommand(args, options.currentPrompt);
301
+ switch (parsed.action) {
302
+ case "stash": {
303
+ const entry = stash.stash(parsed.prompt);
304
+ return {
305
+ ok: true,
306
+ action: "stash",
307
+ entry,
308
+ message: `Prompt stashed (${promptStashPreview(entry.text)}).`,
309
+ };
310
+ }
311
+ case "list": {
312
+ const entries = stash.list();
313
+ return {
314
+ ok: true,
315
+ action: "list",
316
+ entries,
317
+ message: renderPromptStash(entries),
318
+ };
319
+ }
320
+ case "pop": {
321
+ const entry = stash.pop();
322
+ return entry
323
+ ? {
324
+ ok: true,
325
+ action: "pop",
326
+ entry,
327
+ prompt: entry.text,
328
+ message: "Restored the newest stashed prompt for editing.",
329
+ }
330
+ : {
331
+ ok: false,
332
+ action: "pop",
333
+ prompt: null,
334
+ message: "Prompt stash is empty.",
335
+ };
336
+ }
337
+ case "clear": {
338
+ const count = stash.clear();
339
+ return {
340
+ ok: true,
341
+ action: "clear",
342
+ count,
343
+ message: `Cleared ${count} stashed prompt${count === 1 ? "" : "s"}.`,
344
+ };
345
+ }
346
+ default:
347
+ return {
348
+ ok: false,
349
+ action: "help",
350
+ message:
351
+ "Usage: /stash <prompt> | /stash list | /stash pop | /stash clear",
352
+ };
353
+ }
354
+ }
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Non-blocking, provider-neutral prompt suggestions for the agent REPL.
3
+ *
4
+ * The controller accepts an injected `generate(context, { signal })` callback,
5
+ * so it can use whichever provider/runtime already owns the conversation. A
6
+ * small local heuristic is the no-provider fallback. Scheduling never blocks
7
+ * readline and a newer turn cancels an obsolete generation.
8
+ */
9
+
10
+ const TRUE_VALUES = new Set(["1", "true", "yes", "on"]);
11
+ const FALSE_VALUES = new Set(["0", "false", "no", "off"]);
12
+ export const MAX_PROMPT_SUGGESTIONS = 3;
13
+ export const MAX_SUGGESTION_CHARS = 180;
14
+
15
+ function boolValue(value) {
16
+ if (typeof value === "boolean") return value;
17
+ const normalized = String(value ?? "")
18
+ .trim()
19
+ .toLowerCase();
20
+ if (TRUE_VALUES.has(normalized)) return true;
21
+ if (FALSE_VALUES.has(normalized)) return false;
22
+ return null;
23
+ }
24
+
25
+ /** Environment override > effective config > default. */
26
+ export function resolvePromptSuggestionsEnabled(options = {}) {
27
+ const envValue = boolValue(
28
+ (options.env || process.env).CC_PROMPT_SUGGESTIONS,
29
+ );
30
+ if (envValue !== null) return envValue;
31
+ const config = options.config || {};
32
+ const configured = boolValue(
33
+ config.cli?.promptSuggestions ?? config.features?.promptSuggestions,
34
+ );
35
+ return configured ?? options.defaultEnabled ?? true;
36
+ }
37
+
38
+ function messageText(message) {
39
+ const content = message?.content;
40
+ if (typeof content === "string") return content;
41
+ if (!Array.isArray(content)) return "";
42
+ return content
43
+ .map((part) => (typeof part?.text === "string" ? part.text : ""))
44
+ .join(" ");
45
+ }
46
+
47
+ function lastAssistantText(context) {
48
+ if (typeof context?.lastAssistantText === "string") {
49
+ return context.lastAssistantText;
50
+ }
51
+ const messages = Array.isArray(context?.messages) ? context.messages : [];
52
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
53
+ if (messages[index]?.role === "assistant") {
54
+ return messageText(messages[index]);
55
+ }
56
+ }
57
+ return "";
58
+ }
59
+
60
+ /** Local fallback used when no model-backed generator is supplied. */
61
+ export async function deriveLocalPromptSuggestions(context = {}) {
62
+ const text = lastAssistantText(context).toLowerCase();
63
+ const suggestions = [];
64
+ if (/\b(error|failed|failure|exception)\b|失败|报错|异常/.test(text)) {
65
+ suggestions.push("定位刚才失败的根因,修复后重新验证");
66
+ }
67
+ if (/\b(test|tests|tested|coverage)\b|测试|覆盖率/.test(text)) {
68
+ suggestions.push("运行相关测试并汇总仍需处理的问题");
69
+ }
70
+ if (
71
+ /\b(todo|remaining|next step|follow[- ]?up)\b|待办|剩余|下一步/.test(text)
72
+ ) {
73
+ suggestions.push("继续完成尚未处理的下一项,并给出验证结果");
74
+ }
75
+ if (/\b(diff|changed|modified|implementation)\b|变更|修改|实现/.test(text)) {
76
+ suggestions.push("审查最新变更,检查边界情况和回归风险");
77
+ }
78
+ if (!suggestions.length && text.trim()) {
79
+ suggestions.push("基于当前结果继续推进最合适的下一步");
80
+ }
81
+ return suggestions;
82
+ }
83
+
84
+ function splitSuggestionText(value) {
85
+ if (typeof value !== "string") return [];
86
+ return value
87
+ .split(/\r?\n/)
88
+ .map((line) => line.replace(/^\s*(?:[-*]|\d+[.)])\s*/, "").trim())
89
+ .filter(Boolean);
90
+ }
91
+
92
+ export function normalizePromptSuggestions(value, options = {}) {
93
+ const input = Array.isArray(value)
94
+ ? value
95
+ : Array.isArray(value?.suggestions)
96
+ ? value.suggestions
97
+ : splitSuggestionText(value);
98
+ const maxItems = Math.max(
99
+ 1,
100
+ Number(options.maxItems) || MAX_PROMPT_SUGGESTIONS,
101
+ );
102
+ const maxChars = Math.max(
103
+ 20,
104
+ Number(options.maxChars) || MAX_SUGGESTION_CHARS,
105
+ );
106
+ const seen = new Set();
107
+ const result = [];
108
+ for (const item of input) {
109
+ const candidate =
110
+ typeof item === "string"
111
+ ? item
112
+ : typeof item?.prompt === "string"
113
+ ? item.prompt
114
+ : typeof item?.text === "string"
115
+ ? item.text
116
+ : "";
117
+ const text = String(candidate)
118
+ .replace(/[\p{Cc}\p{Cf}]+/gu, " ")
119
+ .replace(/\s+/g, " ")
120
+ .trim();
121
+ if (!text) continue;
122
+ const bounded =
123
+ text.length > maxChars
124
+ ? `${text.slice(0, Math.max(1, maxChars - 1))}…`
125
+ : text;
126
+ const identity = bounded.toLowerCase();
127
+ if (seen.has(identity)) continue;
128
+ seen.add(identity);
129
+ result.push(bounded);
130
+ if (result.length >= maxItems) break;
131
+ }
132
+ return result;
133
+ }
134
+
135
+ export function renderPromptSuggestions(suggestions) {
136
+ const list = normalizePromptSuggestions(suggestions);
137
+ if (!list.length) return "No prompt suggestions available.";
138
+ return [
139
+ "Suggested next prompts:",
140
+ ...list.map((suggestion, index) => ` ${index + 1}. ${suggestion}`),
141
+ ].join("\n");
142
+ }
143
+
144
+ export class PromptSuggestionController {
145
+ constructor(options = {}) {
146
+ this.enabled = options.enabled !== false;
147
+ this.generate = options.generate || deriveLocalPromptSuggestions;
148
+ this.debounceMs = Math.max(0, Number(options.debounceMs) || 0);
149
+ this.onUpdate =
150
+ typeof options.onUpdate === "function" ? options.onUpdate : () => {};
151
+ this.setTimer = options.setTimer || setTimeout;
152
+ this.clearTimer = options.clearTimer || clearTimeout;
153
+ this.latest = [];
154
+ this.error = null;
155
+ this._generation = 0;
156
+ this._active = null;
157
+ }
158
+
159
+ _cancelActive(reason = "superseded") {
160
+ const active = this._active;
161
+ if (!active) return;
162
+ if (active.timer != null) this.clearTimer(active.timer);
163
+ active.controller.abort(reason);
164
+ active.resolve({ status: "cancelled", reason, suggestions: [] });
165
+ this._active = null;
166
+ }
167
+
168
+ setEnabled(enabled) {
169
+ this.enabled = enabled === true;
170
+ if (!this.enabled) {
171
+ this._cancelActive("disabled");
172
+ this.latest = [];
173
+ this.error = null;
174
+ }
175
+ return this.enabled;
176
+ }
177
+
178
+ schedule(context = {}) {
179
+ if (!this.enabled) {
180
+ return {
181
+ scheduled: false,
182
+ generation: this._generation,
183
+ promise: Promise.resolve({
184
+ status: "disabled",
185
+ suggestions: [],
186
+ }),
187
+ };
188
+ }
189
+ this._cancelActive();
190
+ const generation = ++this._generation;
191
+ const controller = new AbortController();
192
+ let resolvePromise;
193
+ const promise = new Promise((resolve) => {
194
+ resolvePromise = resolve;
195
+ });
196
+ const active = {
197
+ generation,
198
+ controller,
199
+ resolve: resolvePromise,
200
+ timer: null,
201
+ promise,
202
+ };
203
+ this._active = active;
204
+ active.timer = this.setTimer(async () => {
205
+ active.timer = null;
206
+ try {
207
+ const generated = await this.generate(context, {
208
+ signal: controller.signal,
209
+ });
210
+ if (controller.signal.aborted || this._active !== active) return;
211
+ const suggestions = normalizePromptSuggestions(generated);
212
+ this.latest = suggestions.slice();
213
+ this.error = null;
214
+ this._active = null;
215
+ resolvePromise({ status: "ready", suggestions: suggestions.slice() });
216
+ try {
217
+ this.onUpdate(suggestions.slice(), { generation });
218
+ } catch {
219
+ // Rendering callbacks cannot break suggestion lifecycle state.
220
+ }
221
+ } catch (error) {
222
+ if (controller.signal.aborted || this._active !== active) return;
223
+ this.error = error;
224
+ this._active = null;
225
+ resolvePromise({ status: "failed", error, suggestions: [] });
226
+ }
227
+ }, this.debounceMs);
228
+ return { scheduled: true, generation, promise };
229
+ }
230
+
231
+ status() {
232
+ return {
233
+ enabled: this.enabled,
234
+ generating: this._active !== null,
235
+ generation: this._generation,
236
+ suggestions: this.latest.slice(),
237
+ error: this.error ? "generation failed" : null,
238
+ };
239
+ }
240
+
241
+ dispose() {
242
+ this._cancelActive("disposed");
243
+ }
244
+ }
245
+
246
+ export function parsePromptSuggestionsCommand(args) {
247
+ const command = String(args || "")
248
+ .trim()
249
+ .toLowerCase();
250
+ if (!command || command === "status") return { action: "status" };
251
+ if (TRUE_VALUES.has(command)) return { action: "set", enabled: true };
252
+ if (FALSE_VALUES.has(command)) return { action: "set", enabled: false };
253
+ if (command === "refresh") return { action: "refresh" };
254
+ return { action: "help" };
255
+ }
256
+
257
+ export function runPromptSuggestionsCommand(args, options = {}) {
258
+ const controller = options.controller;
259
+ if (!controller) throw new Error("prompt suggestion controller is required");
260
+ const parsed = parsePromptSuggestionsCommand(args);
261
+ if (parsed.action === "set") {
262
+ try {
263
+ if (typeof options.persistEnabled === "function") {
264
+ options.persistEnabled(parsed.enabled);
265
+ }
266
+ } catch (error) {
267
+ return {
268
+ ok: false,
269
+ action: "set",
270
+ enabled: controller.status().enabled,
271
+ message: `Could not update prompt suggestions: ${error.message}`,
272
+ };
273
+ }
274
+ controller.setEnabled(parsed.enabled);
275
+ return {
276
+ ok: true,
277
+ action: "set",
278
+ enabled: parsed.enabled,
279
+ message: `Prompt suggestions ${parsed.enabled ? "enabled" : "disabled"}.`,
280
+ };
281
+ }
282
+ if (parsed.action === "refresh") {
283
+ const scheduled = controller.schedule(options.context || {});
284
+ return {
285
+ ok: scheduled.scheduled,
286
+ action: "refresh",
287
+ ...scheduled,
288
+ message: scheduled.scheduled
289
+ ? "Refreshing prompt suggestions in the background."
290
+ : "Prompt suggestions are disabled.",
291
+ };
292
+ }
293
+ if (parsed.action === "status") {
294
+ const status = controller.status();
295
+ return {
296
+ ok: true,
297
+ action: "status",
298
+ ...status,
299
+ message:
300
+ `Prompt suggestions: ${status.enabled ? "on" : "off"}` +
301
+ (status.generating ? " (generating)" : "") +
302
+ (status.suggestions.length
303
+ ? `\n${renderPromptSuggestions(status.suggestions)}`
304
+ : ""),
305
+ };
306
+ }
307
+ return {
308
+ ok: false,
309
+ action: "help",
310
+ message: "Usage: /suggestions on|off|status|refresh",
311
+ };
312
+ }