chainlesschain 0.162.94 → 0.162.96

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 (224) hide show
  1. package/README.md +1 -1
  2. package/bin/chainlesschain.js +8 -18
  3. package/package.json +1 -1
  4. package/src/assets/web-panel/.build-hash +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-CiEoV_Hg.js → AIOps-Bo9Ux24z.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-DrrjQMuK.js → ActionButton-CExgXSQT.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-ChlhPe9a.js → Analytics-BNnZhRTZ.js} +2 -2
  8. package/src/assets/web-panel/assets/{AppLayout-CIkI7d5o.js → AppLayout-D9xfqFCT.js} +4 -4
  9. package/src/assets/web-panel/assets/{Audit-_sY9bNSN.js → Audit-Cpgu8uCw.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DvzaThN8.js → Backup-CHdEJnNE.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-DDZW_DU8.js → BaseInput-Cbw-PxBq.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-DqErkog2.js → Chat-ChHTZK_6.js} +5 -5
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BcnNHS-r.js → ChatBubbleRenderer-Dl6z6ZH7.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-NWDuyRK6.js → Checkbox-Cj8d9bru.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-B_jnSC8a.js → Codegen-C2YobW4j.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-B-uyUy0z.js → Col-CxfV5FGT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-Dmf1MpG7.js → Community-CiwOASMj.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-fGI68M5y.js → Compact-CTg_2iKW.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-PV9qT-iV.js → Compliance-HMbsTmtw.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-BM6BvCgv.js → Cowork-DyNDqGR2.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-BsSWwFFo.js → Cron-9-x5M7qy.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DUdPVYvS.js → Crosschain-4msgDaYJ.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-PPLLhrJs.js → DID--2Beb5Po.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-Cn_Xjj80.js → Dashboard-T6nq0Lzs.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DkTXjy5D.js → Dropdown-D_Q6T10v.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BnA27cjB.js → EmailListRenderer-TnN21bJx.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-jqrv5VCp.js → FamilyGuardDashboard-kky_uIij.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-DESwXwtR.js → Federation-C1GkNY4B.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C9gbyY1r.js → FormItemContext-5-_Fc5YV.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-CkZB9crH.js → GenericCardRenderer-CmHiB7sr.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-B6niu0W9.js → Git-Cuci_BPI.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-BrF28dEi.js → Governance-BU0cO2LD.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CZDnV8SN.js → Inference-BDFNgEcx.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-eqZ0hP0l.js → KnowledgeGraph-DITXUzHz.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-BlHS7DoS.js → Logs-Ea-07CRd.js} +2 -2
  36. package/src/assets/web-panel/assets/{Marketplace-NOp9G83e.js → Marketplace-D6OBbRVG.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-B6wMx173.js → McpTools-eiKpIJMi.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-BKVeTAeT.js → Memory-M-X-T8UF.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-Da2y6iYW.js → MobileBridge-xcZb9Ekv.js} +2 -2
  40. package/src/assets/web-panel/assets/MobileProjects-DCfoTPRC.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-B7Hy5rAT.js → Mtc-BWfGDzD8.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-BgBoX0XI.js → MtcAudit-imBuld-U.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-Db7bwi5J.js → Multisig-C2kcsq7I.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-f9ca2vst.js → NLProgramming-DObvXSXP.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-meVqoOMz.js → Notes-C-6OGJMe.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BfEhBXNo.js → NotificationSettings-ub7Ozxci.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-e25Q6ALJ.js → OrderTableRenderer-CR2W89Vk.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-BpgOG9lj.js → Organization-DNA_Wty7.js} +2 -2
  49. package/src/assets/web-panel/assets/{Overflow-fI3L05Rq.js → Overflow-rT4Wb4rQ.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-wD0wpJJY.js → P2P-CK-hARKv.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-BWV9J4VZ.js → PdhVaultBrowser-C2pbtNbT.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-BJH7W2iv.js → Permissions-B8EIEqGJ.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CqQ3XpMz.js → PersonalDataHub-BxNVNU3s.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-CHyXCU1i.js → Pipeline-CT9pbkyY.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-CJtfNlc7.js → Privacy-DhCNd2CS.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-CKOaHFGR.js → ProjectInit-Qngnx-vQ.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-QD8wp_EM.js → ProjectSettings-Dfn-nQc1.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-BKdiSaPR.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-BPESqCEg.js → Providers-4o9B_n-X.js} +1 -1
  60. package/src/assets/web-panel/assets/{QuickAsk-C5AzvvRo.js → QuickAsk-bs1qvF30.js} +1 -1
  61. package/src/assets/web-panel/assets/{Recommend-cGpF3tsA.js → Recommend-CuEnKA2K.js} +1 -1
  62. package/src/assets/web-panel/assets/{Reputation-Duaxvbt9.js → Reputation-DbfvmRb2.js} +1 -1
  63. package/src/assets/web-panel/assets/{Row-xsUwB5Ay.js → Row-DvMJGAI3.js} +1 -1
  64. package/src/assets/web-panel/assets/{RssFeed-DmUUsqaF.js → RssFeed-oJ0BZvmA.js} +3 -3
  65. package/src/assets/web-panel/assets/{Search-Do9YuDbX.js → Search-DnIVrY9O.js} +1 -1
  66. package/src/assets/web-panel/assets/{Security-06BgEn-d.js → Security-CIWhI_Tc.js} +3 -3
  67. package/src/assets/web-panel/assets/{Services-DyaCes7r.js → Services-DLrnpjwm.js} +2 -2
  68. package/src/assets/web-panel/assets/{Skeleton-Cj_VbkFG.js → Skeleton-CYmqgtVb.js} +1 -1
  69. package/src/assets/web-panel/assets/{Skills-DCOWMJOR.js → Skills-BUzCkoeu.js} +1 -1
  70. package/src/assets/web-panel/assets/{Sla-C44_86bu.js → Sla-DYRE4bnS.js} +1 -1
  71. package/src/assets/web-panel/assets/{SpeechSettings-BXhBJSMB.js → SpeechSettings-_6MurdOg.js} +1 -1
  72. package/src/assets/web-panel/assets/{SyncSettings-BpHsaNWg.js → SyncSettings-UIdnF5Li.js} +2 -2
  73. package/src/assets/web-panel/assets/Tasks-COx2CjT8.js +1 -0
  74. package/src/assets/web-panel/assets/{Templates-aEWbCOdo.js → Templates-BcHAmkZo.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tenant-DkOLw1Yz.js → Tenant-CjYQS8yZ.js} +1 -1
  76. package/src/assets/web-panel/assets/{Terminal-5Z4paIoh.js → Terminal-BmfOPcWt.js} +2 -2
  77. package/src/assets/web-panel/assets/{TimelineRenderer-CYhublOm.js → TimelineRenderer-D5HuarNQ.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tokens-h57dBT4T.js → Tokens-C_fbProA.js} +1 -1
  79. package/src/assets/web-panel/assets/{Trigger-abE21FpD.js → Trigger-JPulpRx8.js} +1 -1
  80. package/src/assets/web-panel/assets/{Trust-urB3VZip.js → Trust-CnO-062O.js} +1 -1
  81. package/src/assets/web-panel/assets/{UkeySign-D6uXV9pp.js → UkeySign-HpNovvau.js} +1 -1
  82. package/src/assets/web-panel/assets/{VideoEditing-C9MxXAzw.js → VideoEditing-yMncxH7y.js} +1 -1
  83. package/src/assets/web-panel/assets/{Wallet-DTn5rW4u.js → Wallet-Cug-t7Np.js} +3 -3
  84. package/src/assets/web-panel/assets/{WebAuthn-BNYEqeqr.js → WebAuthn--U1-NR-3.js} +2 -2
  85. package/src/assets/web-panel/assets/{WorkflowEditor-CsRoOJPT.js → WorkflowEditor-R-XvSJ3S.js} +1 -1
  86. package/src/assets/web-panel/assets/{chat-B5GoUHDS.js → chat-CCI1i6Rp.js} +1 -1
  87. package/src/assets/web-panel/assets/{colors-BF_Rqaab.js → colors-C-mqoaLt.js} +1 -1
  88. package/src/assets/web-panel/assets/{compact-item-BNPfqT4P.js → compact-item-De_N2iRR.js} +1 -1
  89. package/src/assets/web-panel/assets/{createContext-C3qLKXX-.js → createContext-DbTM6sjZ.js} +1 -1
  90. package/src/assets/web-panel/assets/devWarning-OcLPHSl5.js +1 -0
  91. package/src/assets/web-panel/assets/{hasIn-BLpAeFXB.js → hasIn-D0Adylyc.js} +1 -1
  92. package/src/assets/web-panel/assets/index-7E_mHkDU.js +1 -0
  93. package/src/assets/web-panel/assets/{index-BOoTjHGU.js → index-AY937BxS.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-Cv1WRH-j.js → index-B6hgy5VC.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-C2wJowV1.js → index-B7XnpLHk.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-Dj6rUk3f.js → index-B7gw-tDm.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Dz7o1Zt6.js → index-BBgDgkwW.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CMRdeB2r.js → index-BMmeiw88.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-2-7AD4Fq.js → index-BU6XtnSx.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-7Tt41tHa.js → index-Bi7VUG0R.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CaROOJ5Z.js → index-BtE3cw9m.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DmrPbYXh.js → index-C9ej-MF0.js} +3 -3
  103. package/src/assets/web-panel/assets/{index-CgIrRLBP.js → index-CIEiQl1j.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DudglWTC.js → index-CKgaW9E5.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C8B3rHnr.js → index-CY0KKR5f.js} +1 -1
  106. package/src/assets/web-panel/assets/index-CbV3-WlB.js +1 -0
  107. package/src/assets/web-panel/assets/{index-ixN0R0We.js → index-Co1UkcFX.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Cqb8TXEp.js → index-CzP5d5LW.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DH95hKUN.js → index-D0NJ5lRi.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C6yRwT6e.js → index-D0gBsqTX.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-o1doEeT3.js → index-D5F6shtn.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BxWEK9FS.js → index-DVtamtlm.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Cxd2RsZR.js → index-DWT18hDG.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-vXHPZOmO.js → index-DaXb4B9P.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BJsVUj80.js → index-DjQA0Vez.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CIjPwa7h.js → index-Ds8NJVEL.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DMKiwMJa.js → index-DuP-8HWu.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BIpGZeh2.js → index-DwLTiJ31.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Dn95Blhx.js → index-T3VhH8cO.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BqjU6Jp9.js → index-TDY1o0w3.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CBFekqcY.js → index-TRy2Gc3V.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D-PNuV4r.js → index-W1OMxCmI.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BlEDjdTc.js → index-ftFRZbuc.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DMKbLaTg.js → index-iGPGaORA.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DYWExa2Z.js → index-jhy5KQvg.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BryugSCS.js → index-qUSZJcgw.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-81hgV6U6.js → index-w3f6lqkD.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CmBMP52w.js → index-xdMc71Kh.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-KUdyAwqr.js → index-xhX5eg2Z.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DGVf-eOG.js → index-zkTiW6d6.js} +1 -1
  131. package/src/assets/web-panel/assets/{initDefaultProps-Dhfx-jOJ.js → initDefaultProps-CIHqouWk.js} +1 -1
  132. package/src/assets/web-panel/assets/{motion-DzZPc4Mq.js → motion-D1iUKftk.js} +1 -1
  133. package/src/assets/web-panel/assets/{move-D6R8EIjz.js → move-YOpWjmjL.js} +1 -1
  134. package/src/assets/web-panel/assets/{omit-CPniUUR1.js → omit-C_1357UY.js} +1 -1
  135. package/src/assets/web-panel/assets/{pickAttrs-DbzxHNNa.js → pickAttrs-CDb4dq6o.js} +1 -1
  136. package/src/assets/web-panel/assets/{placementArrow-B_MxmwXz.js → placementArrow-D-OGYyhd.js} +1 -1
  137. package/src/assets/web-panel/assets/{responsiveObserve-Em-jKJ7U.js → responsiveObserve-hS-j69A0.js} +1 -1
  138. package/src/assets/web-panel/assets/{slide-B2RV1mQl.js → slide-tnjuvphU.js} +1 -1
  139. package/src/assets/web-panel/assets/{statusUtils-C9Ps6HQe.js → statusUtils-BERfMMcU.js} +1 -1
  140. package/src/assets/web-panel/assets/{styleChecker-DCoehh1S.js → styleChecker-DKpl2K9E.js} +1 -1
  141. package/src/assets/web-panel/assets/{useFlexGapSupport-DZOaLVto.js → useFlexGapSupport-C_sX4D3N.js} +1 -1
  142. package/src/assets/web-panel/assets/{useFs-Dk_JA91R.js → useFs-DBcxC1vK.js} +1 -1
  143. package/src/assets/web-panel/assets/{usePersonalDataHub-CJR6Dqtz.js → usePersonalDataHub-CeoDf2XI.js} +1 -1
  144. package/src/assets/web-panel/assets/{vnode-BaFNaDQL.js → vnode-DC9Pmj_u.js} +1 -1
  145. package/src/assets/web-panel/assets/{zoom-CxA9IYpM.js → zoom-CZA58J7M.js} +1 -1
  146. package/src/assets/web-panel/index.html +1 -1
  147. package/src/commands/activitypub.js +19 -3
  148. package/src/commands/agent.js +25 -0
  149. package/src/commands/ask.js +13 -0
  150. package/src/commands/config.js +17 -1
  151. package/src/commands/cost.js +6 -1
  152. package/src/commands/dao.js +16 -3
  153. package/src/commands/dlp.js +7 -1
  154. package/src/commands/hub.js +13 -0
  155. package/src/commands/mtc.js +37 -1
  156. package/src/commands/pdh.js +162 -0
  157. package/src/gateways/discord/discord-formatter.js +18 -1
  158. package/src/gateways/telegram/telegram-formatter.js +6 -3
  159. package/src/gateways/ws/ws-server.js +19 -1
  160. package/src/harness/mcp-client.js +19 -2
  161. package/src/harness/plugin-manager.js +20 -0
  162. package/src/harness/worktree-isolator.js +17 -1
  163. package/src/index.js +2 -0
  164. package/src/lib/__tests__/model-deprecation.test.js +194 -0
  165. package/src/lib/a2a-protocol.js +23 -9
  166. package/src/lib/agent-core.js +2 -5
  167. package/src/lib/audit-mtc.js +40 -2
  168. package/src/lib/chat-core.js +141 -18
  169. package/src/lib/claude-code-bridge.js +14 -2
  170. package/src/lib/cli-anything-bridge.js +5 -0
  171. package/src/lib/cli-numeric.js +38 -0
  172. package/src/lib/config-manager.js +37 -6
  173. package/src/lib/cowork/ab-comparator-cli.js +6 -2
  174. package/src/lib/cowork-cron.js +45 -34
  175. package/src/lib/cowork-learning.js +31 -1
  176. package/src/lib/cowork-share.js +39 -3
  177. package/src/lib/cowork-template-marketplace.js +54 -2
  178. package/src/lib/cowork-workflow.js +33 -6
  179. package/src/lib/cross-chain-mtc.js +41 -12
  180. package/src/lib/crypto-manager.js +12 -2
  181. package/src/lib/dbevo.js +19 -8
  182. package/src/lib/downloader.js +24 -9
  183. package/src/lib/evomap-governance.js +4 -4
  184. package/src/lib/evomap-manager.js +54 -7
  185. package/src/lib/fatal-handler.js +48 -0
  186. package/src/lib/file-checkpoint.js +56 -4
  187. package/src/lib/git-integration.js +28 -0
  188. package/src/lib/goal-store.js +36 -10
  189. package/src/lib/hierarchical-memory.js +6 -0
  190. package/src/lib/llm-pricing.js +52 -2
  191. package/src/lib/mcp-oauth.js +37 -7
  192. package/src/lib/memory-manager.js +23 -2
  193. package/src/lib/model-deprecation.js +249 -0
  194. package/src/lib/pdh-feedback-ledger.js +131 -0
  195. package/src/lib/permanent-memory.js +30 -2
  196. package/src/lib/permission-rules.cjs +11 -3
  197. package/src/lib/personal-data-hub-aichat-wizard.js +33 -3
  198. package/src/lib/personal-data-hub-wiring.js +36 -13
  199. package/src/lib/project-instructions.js +10 -4
  200. package/src/lib/runnable-provider.js +62 -31
  201. package/src/lib/session-tail.js +6 -1
  202. package/src/lib/session-usage.js +34 -1
  203. package/src/lib/skill-loader.js +4 -1
  204. package/src/lib/skill-packs/generator.js +25 -2
  205. package/src/lib/stream-retry.js +56 -0
  206. package/src/lib/sync-credentials.js +53 -17
  207. package/src/lib/token-tracker.js +12 -2
  208. package/src/lib/with-file-lock.js +87 -0
  209. package/src/repl/agent-repl.js +52 -13
  210. package/src/repl/chat-repl.js +10 -0
  211. package/src/repl/session-cost.js +31 -3
  212. package/src/runtime/agent-core.js +474 -53
  213. package/src/runtime/coding-agent-contract-shared.cjs +58 -1
  214. package/src/runtime/coding-agent-policy.cjs +10 -0
  215. package/src/runtime/file-ref-expander.js +29 -1
  216. package/src/runtime/headless-runner.js +51 -6
  217. package/src/runtime/headless-stream.js +219 -2
  218. package/src/skills/video-editing/tools/commit.js +15 -1
  219. package/src/assets/web-panel/assets/MobileProjects-CTFPhn4A.js +0 -1
  220. package/src/assets/web-panel/assets/Projects-Dj33CO-H.js +0 -1
  221. package/src/assets/web-panel/assets/Tasks-C83eyBn8.js +0 -1
  222. package/src/assets/web-panel/assets/devWarning-D5Rwbl_Y.js +0 -1
  223. package/src/assets/web-panel/assets/index-7ls1Z3dH.js +0 -1
  224. package/src/assets/web-panel/assets/index-DoldiBPq.js +0 -1
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Deprecated-model warning — Claude-Code 2.1.183 parity:
3
+ * "a warning when the requested model is deprecated or automatically updated
4
+ * to a newer model".
5
+ *
6
+ * The "automatically updated to a newer model" half is already covered by the
7
+ * fallback-model machinery (a model-not-found error switches to the next model
8
+ * in the chain and surfaces an onFallback notice — see fallback-model.js +
9
+ * agent.js / agent-repl.js). This module covers the PROACTIVE half: warn once,
10
+ * up front, when the user's *configured / requested* model id is on a small,
11
+ * high-confidence list of provider-retired model snapshots — before the run
12
+ * fails with an opaque "model not found".
13
+ *
14
+ * The Anthropic entries below are kept in sync with the authoritative model
15
+ * deprecation table (platform.claude.com/docs/en/about-claude/model-deprecations,
16
+ * verified 2026-06-21). `state` distinguishes "retired" (requests now fail) from
17
+ * "deprecated" (still works, not recommended, has a retirement date).
18
+ *
19
+ * Design constraints (this project hates model-id churn noise):
20
+ * - Only ids the provider has officially retired or deprecated. A false warning
21
+ * on a live model is worse than a missing one.
22
+ * - CRITICAL: the Claude 4 family has BOTH active and retired members that
23
+ * differ only by date suffix (claude-opus-4-8 active vs claude-opus-4-20250514
24
+ * retired), so those entries use EXACT dated ids and must never be turned into
25
+ * bare prefixes. Older families (1 / 2 / 3 / 3.5 / 3.7) are fully retired, so a
26
+ * family prefix is safe there.
27
+ * - Pure + fail-open: a lookup never throws and is trivially unit-testable.
28
+ * - The notice goes to stderr (keeps stdout clean for JSON/headless output),
29
+ * fires at most once per model per process, and is silenced with
30
+ * CC_MODEL_NOTICE=0.
31
+ */
32
+
33
+ import chalk from "chalk";
34
+
35
+ /**
36
+ * Retired / deprecated model snapshots, most-specific first. `id` is matched
37
+ * case-insensitively as an exact id OR a prefix (so `claude-2` covers
38
+ * `claude-2.0` / `claude-2.1`, and `claude-3-5-sonnet` covers both 3.5 Sonnet
39
+ * snapshots). Prefixes are only used for fully-retired families — never for the
40
+ * Claude 4 family, whose active and retired members share a prefix.
41
+ *
42
+ * @type {Array<{id: string, state: "retired"|"deprecated", reason: string, replacement: string}>}
43
+ */
44
+ export const DEPRECATED_MODELS = [
45
+ // ── Anthropic — Claude 4 family (EXACT dated ids only; prefixes would hit
46
+ // the active claude-opus-4-8 / -4-7 / -4-6 / -4-5-20251101 / sonnet-4-6 /
47
+ // sonnet-4-5-20250929 / haiku-4-5-20251001 models) ──────────────────────
48
+ {
49
+ id: "claude-opus-4-1-20250805",
50
+ state: "deprecated",
51
+ reason: "Anthropic deprecated Claude Opus 4.1 (retires 2026-08-05)",
52
+ replacement: "claude-opus-4-8",
53
+ },
54
+ {
55
+ id: "claude-opus-4-20250514",
56
+ state: "retired",
57
+ reason: "Anthropic retired the original Claude Opus 4 snapshot",
58
+ replacement: "claude-opus-4-8",
59
+ },
60
+ {
61
+ id: "claude-sonnet-4-20250514",
62
+ state: "retired",
63
+ reason: "Anthropic retired the original Claude Sonnet 4 snapshot",
64
+ replacement: "claude-sonnet-4-6",
65
+ },
66
+ // ── Anthropic — fully-retired older families (family prefix is safe) ───────
67
+ {
68
+ id: "claude-3-7-sonnet",
69
+ state: "retired",
70
+ reason: "Anthropic retired Claude Sonnet 3.7",
71
+ replacement: "claude-sonnet-4-6",
72
+ },
73
+ {
74
+ id: "claude-3-5-sonnet",
75
+ state: "retired",
76
+ reason: "Anthropic retired the Claude 3.5 Sonnet snapshots",
77
+ replacement: "claude-sonnet-4-6",
78
+ },
79
+ {
80
+ id: "claude-3-5-haiku",
81
+ state: "retired",
82
+ reason: "Anthropic retired Claude Haiku 3.5",
83
+ replacement: "claude-haiku-4-5",
84
+ },
85
+ {
86
+ id: "claude-3-opus-20240229",
87
+ state: "retired",
88
+ reason: "Anthropic retired Claude 3 Opus",
89
+ replacement: "claude-opus-4-8",
90
+ },
91
+ {
92
+ id: "claude-3-sonnet-20240229",
93
+ state: "retired",
94
+ reason: "Anthropic retired Claude 3 Sonnet",
95
+ replacement: "claude-sonnet-4-6",
96
+ },
97
+ {
98
+ id: "claude-3-haiku-20240307",
99
+ state: "retired",
100
+ reason: "Anthropic retired Claude 3 Haiku",
101
+ replacement: "claude-haiku-4-5",
102
+ },
103
+ {
104
+ id: "claude-2",
105
+ state: "retired",
106
+ reason: "Anthropic retired the Claude 2 family",
107
+ replacement: "claude-sonnet-4-6 or claude-opus-4-8",
108
+ },
109
+ {
110
+ id: "claude-instant",
111
+ state: "retired",
112
+ reason: "Anthropic retired Claude Instant",
113
+ replacement: "claude-haiku-4-5",
114
+ },
115
+ {
116
+ id: "claude-1",
117
+ state: "retired",
118
+ reason: "Anthropic retired the Claude 1 family",
119
+ replacement: "claude-sonnet-4-6 or claude-opus-4-8",
120
+ },
121
+ // ── OpenAI (retired snapshots) ────────────────────────────────────────────
122
+ {
123
+ id: "gpt-4-vision-preview",
124
+ state: "retired",
125
+ reason: "OpenAI retired the gpt-4 vision preview",
126
+ replacement: "gpt-5 (natively multimodal)",
127
+ },
128
+ {
129
+ id: "gpt-4-1106-vision-preview",
130
+ state: "retired",
131
+ reason: "OpenAI retired the gpt-4 vision preview",
132
+ replacement: "gpt-5 (natively multimodal)",
133
+ },
134
+ {
135
+ id: "gpt-4-32k",
136
+ state: "retired",
137
+ reason: "OpenAI retired the gpt-4-32k snapshots",
138
+ replacement: "gpt-5 or gpt-4o",
139
+ },
140
+ {
141
+ id: "gpt-4-0314",
142
+ state: "retired",
143
+ reason: "OpenAI retired this gpt-4 snapshot",
144
+ replacement: "gpt-5 or gpt-4o",
145
+ },
146
+ {
147
+ id: "gpt-4-0613",
148
+ state: "retired",
149
+ reason: "OpenAI retired this gpt-4 snapshot",
150
+ replacement: "gpt-5 or gpt-4o",
151
+ },
152
+ {
153
+ id: "gpt-3.5-turbo-0301",
154
+ state: "retired",
155
+ reason: "OpenAI retired this gpt-3.5-turbo snapshot",
156
+ replacement: "gpt-4o-mini or gpt-5-mini",
157
+ },
158
+ {
159
+ id: "gpt-3.5-turbo-0613",
160
+ state: "retired",
161
+ reason: "OpenAI retired this gpt-3.5-turbo snapshot",
162
+ replacement: "gpt-4o-mini or gpt-5-mini",
163
+ },
164
+ {
165
+ id: "text-davinci-002",
166
+ state: "retired",
167
+ reason: "OpenAI retired the text-davinci completion models",
168
+ replacement: "gpt-4o-mini or gpt-5-mini",
169
+ },
170
+ {
171
+ id: "text-davinci-003",
172
+ state: "retired",
173
+ reason: "OpenAI retired the text-davinci completion models",
174
+ replacement: "gpt-4o-mini or gpt-5-mini",
175
+ },
176
+ ];
177
+
178
+ /**
179
+ * Look up a model id against the retired list.
180
+ *
181
+ * @param {string} modelId
182
+ * @returns {{id: string, state: string, reason: string, replacement: string}|null}
183
+ * the matched entry (with `id` = the configured model), or null.
184
+ */
185
+ export function checkModelDeprecation(modelId) {
186
+ if (!modelId || typeof modelId !== "string") return null;
187
+ const norm = modelId.trim().toLowerCase();
188
+ if (!norm) return null;
189
+ for (const entry of DEPRECATED_MODELS) {
190
+ const pat = entry.id.toLowerCase();
191
+ if (norm === pat || norm.startsWith(pat)) {
192
+ return { ...entry, id: modelId };
193
+ }
194
+ }
195
+ return null;
196
+ }
197
+
198
+ /**
199
+ * Human-readable one-line warning for a matched deprecation.
200
+ *
201
+ * @param {{id: string, state?: string, reason: string, replacement: string}} info
202
+ * @returns {string}
203
+ */
204
+ export function formatModelDeprecationWarning(info) {
205
+ const verb = info.state === "retired" ? "has been retired" : "is deprecated";
206
+ return (
207
+ `Warning: model "${info.id}" ${verb} — ${info.reason}. ` +
208
+ `Switch to ${info.replacement}. (CC_MODEL_NOTICE=0 to hide)`
209
+ );
210
+ }
211
+
212
+ /** Models already warned about in this process — one notice per id. */
213
+ export const _warned = new Set();
214
+
215
+ /**
216
+ * Entry-point hook: warn once on stderr if `model` is a retired snapshot.
217
+ * Cheap, fail-open, never throws.
218
+ *
219
+ * @param {object} opts
220
+ * @param {string} opts.model the resolved model id
221
+ * @param {object} [opts.env] defaults to process.env
222
+ * @param {(line:string)=>void} [opts.print] defaults to a yellow stderr writer
223
+ * @param {Set} [opts.seen] dedup set (defaults to module `_warned`)
224
+ * @returns {{ warned: boolean, info: object|null }}
225
+ */
226
+ export function maybeWarnDeprecatedModel(opts = {}) {
227
+ const env = opts.env || process.env;
228
+ const seen = opts.seen || _warned;
229
+ const print =
230
+ opts.print || ((line) => process.stderr.write(chalk.yellow(line) + "\n"));
231
+ const out = { warned: false, info: null };
232
+ try {
233
+ if (env.CC_MODEL_NOTICE === "0") return out;
234
+ const info = checkModelDeprecation(opts.model);
235
+ if (!info) return out;
236
+ const key = String(opts.model).toLowerCase();
237
+ if (seen.has(key)) {
238
+ out.info = info;
239
+ return out;
240
+ }
241
+ seen.add(key);
242
+ print(formatModelDeprecationWarning(info));
243
+ out.warned = true;
244
+ out.info = info;
245
+ } catch {
246
+ /* fail-open: a deprecation notice must never affect the run */
247
+ }
248
+ return out;
249
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * PDH feedback ledger (design module 101 §3.5.13) — durable, cross-session
3
+ * persistence of the self-learning feedback the personal-data chat collects.
4
+ *
5
+ * The Android 纠正卡 sends {type:feedback,turn_id,kind,comment} to the cc agent;
6
+ * headless-stream.js consumes it within the session (ack + correction turn /
7
+ * preference note). That is in-session only. This ledger appends every feedback
8
+ * to <home>/pdh-feedback.jsonl so future sessions can learn the user's standing
9
+ * preferences — the "越用越聪明" flywheel (§3.5.13/§8.2). Append-only and
10
+ * best-effort: a learning ledger must never break the live chat.
11
+ *
12
+ * Pure CLI, no Android dependency, fully unit-testable through `_deps`.
13
+ */
14
+ import fs from "node:fs";
15
+ import path from "node:path";
16
+ import { getHomeDir } from "./paths.js";
17
+
18
+ const VALID_KINDS = new Set(["positive", "negative", "correction"]);
19
+
20
+ export const _deps = {
21
+ homeDir: () => getHomeDir(),
22
+ appendFile: (p, s) => fs.appendFileSync(p, s, "utf-8"),
23
+ readFile: (p) => fs.readFileSync(p, "utf-8"),
24
+ exists: (p) => fs.existsSync(p),
25
+ mkdir: (d) => fs.mkdirSync(d, { recursive: true }),
26
+ now: () => Date.now(),
27
+ };
28
+
29
+ /** `<home>/pdh-feedback.jsonl`. */
30
+ export function feedbackLedgerPath(deps = _deps) {
31
+ return path.join(deps.homeDir(), "pdh-feedback.jsonl");
32
+ }
33
+
34
+ /**
35
+ * Append one feedback entry. Best-effort: never throws (a learning ledger must
36
+ * not break the live chat). Returns the written record, or null on skip/failure.
37
+ * A `comment` is only kept for `correction` (thumbs carry no ground truth).
38
+ */
39
+ export function appendFeedback(entry, deps = _deps) {
40
+ const kind = String(entry?.kind || "").toLowerCase();
41
+ if (!VALID_KINDS.has(kind)) return null;
42
+ const rec = {
43
+ ts: deps.now(),
44
+ sessionId: entry.sessionId != null ? String(entry.sessionId) : null,
45
+ turnId: entry.turnId != null ? String(entry.turnId) : null,
46
+ kind,
47
+ comment:
48
+ kind === "correction" &&
49
+ typeof entry.comment === "string" &&
50
+ entry.comment
51
+ ? entry.comment
52
+ : null,
53
+ };
54
+ try {
55
+ const file = feedbackLedgerPath(deps);
56
+ const dir = path.dirname(file);
57
+ if (!deps.exists(dir)) deps.mkdir(dir);
58
+ deps.appendFile(file, JSON.stringify(rec) + "\n");
59
+ return rec;
60
+ } catch {
61
+ return null; // persistence is best-effort; the live turn carries on
62
+ }
63
+ }
64
+
65
+ /** Read all ledger entries (oldest-first), skipping malformed/invalid lines. */
66
+ export function readFeedback(deps = _deps) {
67
+ const file = feedbackLedgerPath(deps);
68
+ let raw;
69
+ try {
70
+ if (!deps.exists(file)) return [];
71
+ raw = deps.readFile(file);
72
+ } catch {
73
+ return [];
74
+ }
75
+ const out = [];
76
+ for (const line of raw.split("\n")) {
77
+ const t = line.trim();
78
+ if (!t) continue;
79
+ try {
80
+ const o = JSON.parse(t);
81
+ if (o && VALID_KINDS.has(o.kind)) out.push(o);
82
+ } catch {
83
+ /* skip malformed */
84
+ }
85
+ }
86
+ return out;
87
+ }
88
+
89
+ /**
90
+ * Summarize the ledger into the user's standing preferences:
91
+ * { total, positive, negative, corrections:[newest-first], sentiment }
92
+ * Pure over a list (caller passes readFeedback()). Corrections are the most
93
+ * actionable ground truth, so the newest few are surfaced verbatim.
94
+ */
95
+ export function summarizeFeedback(entries, { maxCorrections = 5 } = {}) {
96
+ const list = Array.isArray(entries) ? entries : [];
97
+ let positive = 0;
98
+ let negative = 0;
99
+ const corrections = [];
100
+ for (const e of list) {
101
+ if (e.kind === "positive") positive++;
102
+ else if (e.kind === "negative") negative++;
103
+ else if (e.kind === "correction" && e.comment) corrections.push(e.comment);
104
+ }
105
+ return {
106
+ total: list.length,
107
+ positive,
108
+ negative,
109
+ corrections: corrections.slice(-maxCorrections).reverse(),
110
+ sentiment: positive - negative,
111
+ };
112
+ }
113
+
114
+ /**
115
+ * A compact Chinese system-note from a summary, for re-injecting learned
116
+ * preferences into a new session's prompt. "" when nothing has been learned.
117
+ */
118
+ export function feedbackSystemNote(summary) {
119
+ if (!summary || !summary.total) return "";
120
+ const parts = [];
121
+ if (summary.corrections.length) {
122
+ parts.push(
123
+ "用户过往纠正(请遵循):" +
124
+ summary.corrections.map((c) => `「${c}」`).join(";"),
125
+ );
126
+ }
127
+ if (summary.sentiment > 0) parts.push("用户总体认可你的回复风格。");
128
+ else if (summary.sentiment < 0)
129
+ parts.push("用户对过往回复多有不满,请更严谨、更贴合需求。");
130
+ return parts.join(" ");
131
+ }
@@ -17,6 +17,34 @@ export const _deps = {
17
17
  BM25Search,
18
18
  };
19
19
 
20
+ /**
21
+ * Atomically rewrite a UTF-8 file via _deps.fs. MEMORY.md is the memory index
22
+ * (re-read every session); a crash mid-rewrite truncates it and loses the
23
+ * index. Temp sibling + rename (atomic within a filesystem). Graceful-degrade
24
+ * to a direct write when the injected fs has no renameSync (partial test mocks);
25
+ * the real fs always has it, so production is always atomic.
26
+ */
27
+ function _atomicWriteFile(filePath, data) {
28
+ const fsx = _deps.fs;
29
+ if (typeof fsx.renameSync !== "function") {
30
+ fsx.writeFileSync(filePath, data, "utf-8");
31
+ return;
32
+ }
33
+ const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
34
+ try {
35
+ fsx.writeFileSync(tmp, data, "utf-8");
36
+ fsx.renameSync(tmp, filePath);
37
+ } catch (err) {
38
+ try {
39
+ if (fsx.existsSync && fsx.existsSync(tmp) && fsx.unlinkSync)
40
+ fsx.unlinkSync(tmp);
41
+ } catch {
42
+ /* best-effort temp cleanup */
43
+ }
44
+ throw err;
45
+ }
46
+ }
47
+
20
48
  export class CLIPermanentMemory {
21
49
  /**
22
50
  * @param {object} options
@@ -148,13 +176,13 @@ export class CLIPermanentMemory {
148
176
  existing.slice(0, sectionIdx) +
149
177
  `${sectionHeader}\n\n${content}\n` +
150
178
  existing.slice(endIdx);
151
- _deps.fs.writeFileSync(filePath, newContent, "utf-8");
179
+ _atomicWriteFile(filePath, newContent);
152
180
  } else {
153
181
  // Append new section
154
182
  const append = existing
155
183
  ? `\n${sectionHeader}\n\n${content}\n`
156
184
  : `# Memory\n\n${sectionHeader}\n\n${content}\n`;
157
- _deps.fs.writeFileSync(filePath, existing + append, "utf-8");
185
+ _atomicWriteFile(filePath, existing + append);
158
186
  }
159
187
 
160
188
  this._loadMemoryFile();
@@ -113,9 +113,17 @@ function globToRegExp(glob) {
113
113
  const c = s[i];
114
114
  if (c === "*") {
115
115
  if (s[i + 1] === "*") {
116
- re += ".*";
117
- i++;
118
- if (s[i + 1] === "/") i++; // `**/` also matches zero segments
116
+ i++; // consume the second `*`
117
+ if (s[i + 1] === "/") {
118
+ i++; // consume the `/` too
119
+ // `**/` = zero or more WHOLE path segments. Must be boundary-aware:
120
+ // `.*` here would let `**/secret` match `notsecret` (no `/` before the
121
+ // suffix), over-matching permission patterns. `(?:.*/)?` requires the
122
+ // suffix to start a path segment (or sit at the root).
123
+ re += "(?:.*/)?";
124
+ } else {
125
+ re += ".*"; // bare `**` (no trailing slash) — matches across segments
126
+ }
119
127
  } else {
120
128
  re += "[^/]*";
121
129
  }
@@ -15,12 +15,42 @@
15
15
  * Reference: docs/design/Personal_Data_Hub_Phase_10_3_AIChat_WebView_Wizard.md §2 §11
16
16
  */
17
17
 
18
- import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
18
+ import {
19
+ readFileSync,
20
+ writeFileSync,
21
+ mkdirSync,
22
+ unlinkSync,
23
+ renameSync,
24
+ existsSync,
25
+ } from "node:fs";
19
26
  import { join } from "node:path";
20
27
  import { createRequire } from "node:module";
21
28
 
22
29
  const _require = createRequire(import.meta.url);
23
30
 
31
+ /**
32
+ * Atomically persist the accounts JSON at mode 0600. The store holds AI-chat
33
+ * vendor API keys, and `_readAll` swallows a parse error → returns {}, so a
34
+ * crash mid-write would silently drop every saved vendor. Temp sibling (created
35
+ * 0600 so the secret is never world-readable) + rename, atomic within a
36
+ * filesystem. (Mirrors the config / mcp-oauth / sync-credentials / PDH-account
37
+ * atomic-write fixes.) Exported as a test seam.
38
+ */
39
+ export function _atomicWriteAccounts(filePath, data) {
40
+ const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
41
+ try {
42
+ writeFileSync(tmp, data, { mode: 0o600 });
43
+ renameSync(tmp, filePath);
44
+ } catch (err) {
45
+ try {
46
+ if (existsSync(tmp)) unlinkSync(tmp);
47
+ } catch {
48
+ /* best-effort temp cleanup */
49
+ }
50
+ throw err;
51
+ }
52
+ }
53
+
24
54
  // Lazy-load `@chainlesschain/personal-data-hub/adapters/ai-chat-history` —
25
55
  // older nested copies of the hub package (e.g. PDH 0.2.0 left in
26
56
  // `packages/cli/node_modules/@chainlesschain/personal-data-hub/` by a stale
@@ -76,7 +106,7 @@ export function createAccountsStore({ hubDir }) {
76
106
  } catch (err) {
77
107
  if (!err || err.code !== "EEXIST") throw err;
78
108
  }
79
- writeFileSync(filePath, JSON.stringify(all, null, 2), { mode: 0o600 });
109
+ _atomicWriteAccounts(filePath, JSON.stringify(all, null, 2));
80
110
  });
81
111
  return writeChain;
82
112
  }
@@ -93,7 +123,7 @@ export function createAccountsStore({ hubDir }) {
93
123
  /* missing is fine */
94
124
  }
95
125
  } else {
96
- writeFileSync(filePath, JSON.stringify(all, null, 2), { mode: 0o600 });
126
+ _atomicWriteAccounts(filePath, JSON.stringify(all, null, 2));
97
127
  }
98
128
  });
99
129
  return writeChain;
@@ -126,7 +126,13 @@ const {
126
126
  runAnalysisSkill,
127
127
  ANALYSIS_SKILL_NAMES,
128
128
  } = hub;
129
- import { readFileSync, writeFileSync, existsSync } from "node:fs";
129
+ import {
130
+ readFileSync,
131
+ writeFileSync,
132
+ existsSync,
133
+ renameSync,
134
+ unlinkSync,
135
+ } from "node:fs";
130
136
  import { getElectronUserDataDir } from "./paths.js";
131
137
 
132
138
  import {
@@ -1382,11 +1388,34 @@ function loadWechatAccounts(filePath) {
1382
1388
  }
1383
1389
  }
1384
1390
 
1391
+ /**
1392
+ * Atomically persist a JSON state file at mode 0600. The PDH account stores hold
1393
+ * login credentials (cookies/tokens), so a crash mid-write must not truncate the
1394
+ * file — loadXAccounts swallows a parse error and returns [], silently dropping
1395
+ * every saved account. Temp sibling (created 0600 so the secret is never world-
1396
+ * readable) + rename, which is atomic within a filesystem. Exported as a test
1397
+ * seam. (Mirrors the config / mcp-oauth / sync-credentials atomic-write fixes.)
1398
+ */
1399
+ export function _atomicWriteJson600(filePath, obj) {
1400
+ const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
1401
+ try {
1402
+ writeFileSync(tmp, JSON.stringify(obj, null, 2), {
1403
+ encoding: "utf-8",
1404
+ mode: 0o600,
1405
+ });
1406
+ renameSync(tmp, filePath);
1407
+ } catch (err) {
1408
+ try {
1409
+ if (existsSync(tmp)) unlinkSync(tmp);
1410
+ } catch {
1411
+ /* best-effort temp cleanup */
1412
+ }
1413
+ throw err;
1414
+ }
1415
+ }
1416
+
1385
1417
  function saveWechatAccounts(filePath, accounts) {
1386
- writeFileSync(filePath, JSON.stringify(accounts, null, 2), {
1387
- encoding: "utf-8",
1388
- mode: 0o600,
1389
- });
1418
+ _atomicWriteJson600(filePath, accounts);
1390
1419
  }
1391
1420
 
1392
1421
  // ─── Email account persistence (Phase 5.6) ───────────────────────────────
@@ -1403,10 +1432,7 @@ function loadEmailAccounts(filePath) {
1403
1432
  }
1404
1433
 
1405
1434
  function saveEmailAccounts(filePath, accounts) {
1406
- writeFileSync(filePath, JSON.stringify(accounts, null, 2), {
1407
- encoding: "utf-8",
1408
- mode: 0o600,
1409
- });
1435
+ _atomicWriteJson600(filePath, accounts);
1410
1436
  }
1411
1437
 
1412
1438
  // ─── Alipay account persistence (Phase 6) ───────────────────────────────
@@ -1423,10 +1449,7 @@ function loadAlipayAccounts(filePath) {
1423
1449
  }
1424
1450
 
1425
1451
  function saveAlipayAccounts(filePath, accounts) {
1426
- writeFileSync(filePath, JSON.stringify(accounts, null, 2), {
1427
- encoding: "utf-8",
1428
- mode: 0o600,
1429
- });
1452
+ _atomicWriteJson600(filePath, accounts);
1430
1453
  }
1431
1454
 
1432
1455
  export async function getHub() {
@@ -133,9 +133,11 @@ export function findInstructionFiles(opts = {}) {
133
133
  for (const d of chain) {
134
134
  push(firstExisting(fs, path, d, PROJECT_FILE_NAMES), "project");
135
135
  push(firstExisting(fs, path, d, LOCAL_FILE_NAMES), "local");
136
- // Template-scaffolded project rules (`cc init -t` writes these) join the
137
- // chain too, so scaffold-flow and memory-flow projects both feed the agent.
138
- push(path.join(d, ".chainlesschain", "rules.md"), "rules");
136
+ // NOTE: `.chainlesschain/rules.md` is intentionally NOT loaded here — it is
137
+ // already injected by buildSystemPrompt() (as "## Project Rules"), which is
138
+ // the base of every composeSystemPrompt() call. Loading it here too sent it
139
+ // TWICE in the system prompt on every turn. Path-scoped `.claude/rules/*.md`
140
+ // below are NOT covered by buildSystemPrompt, so they stay.
139
141
  // Path-scoped rule files (`.claude/rules/*.md`, YAML frontmatter `paths:`
140
142
  // globs). Glob filtering happens at LOAD time where content is available.
141
143
  try {
@@ -211,7 +213,11 @@ export function ruleApplies(globs, relCwd) {
211
213
  const star = g.search(/[*?[]/);
212
214
  const prefix = (star === -1 ? g : g.slice(0, star)).replace(/\/+$/, "");
213
215
  if (!prefix) return true; // "**/*.js" — applies everywhere
214
- if (cwd === prefix || cwd.startsWith(`${prefix}/`) || prefix.startsWith(`${cwd}/`)) {
216
+ if (
217
+ cwd === prefix ||
218
+ cwd.startsWith(`${prefix}/`) ||
219
+ prefix.startsWith(`${cwd}/`)
220
+ ) {
215
221
  return true;
216
222
  }
217
223
  }