@ai-zen/cli 1.0.0-alpha.3 → 1.0.0-alpha.30

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 (338) hide show
  1. package/CHANGELOG.md +885 -0
  2. package/LICENSE +21 -0
  3. package/README.md +135 -30
  4. package/README.zh.md +135 -30
  5. package/dist/agent-creator.d.ts +11 -0
  6. package/dist/agent-creator.d.ts.map +1 -1
  7. package/dist/agent-creator.js +50 -20
  8. package/dist/agent-creator.js.map +1 -1
  9. package/dist/agent-switch.d.ts +61 -0
  10. package/dist/agent-switch.d.ts.map +1 -0
  11. package/dist/agent-switch.js +83 -0
  12. package/dist/agent-switch.js.map +1 -0
  13. package/dist/agents-store.d.ts +6 -1
  14. package/dist/agents-store.d.ts.map +1 -1
  15. package/dist/agents-store.js +46 -4
  16. package/dist/agents-store.js.map +1 -1
  17. package/dist/auto-migrate-confirm.d.ts +43 -0
  18. package/dist/auto-migrate-confirm.d.ts.map +1 -0
  19. package/dist/auto-migrate-confirm.js +47 -0
  20. package/dist/auto-migrate-confirm.js.map +1 -0
  21. package/dist/clipboard.d.ts +68 -0
  22. package/dist/clipboard.d.ts.map +1 -0
  23. package/dist/clipboard.js +177 -0
  24. package/dist/clipboard.js.map +1 -0
  25. package/dist/config-editor.d.ts +42 -5
  26. package/dist/config-editor.d.ts.map +1 -1
  27. package/dist/config-editor.js +80 -27
  28. package/dist/config-editor.js.map +1 -1
  29. package/dist/config.d.ts +76 -17
  30. package/dist/config.d.ts.map +1 -1
  31. package/dist/config.js +143 -39
  32. package/dist/config.js.map +1 -1
  33. package/dist/conversation-commands/registry.d.ts +127 -4
  34. package/dist/conversation-commands/registry.d.ts.map +1 -1
  35. package/dist/conversation-commands/registry.js +93 -14
  36. package/dist/conversation-commands/registry.js.map +1 -1
  37. package/dist/conversation-persist-plugin.d.ts +0 -1
  38. package/dist/conversation-persist-plugin.d.ts.map +1 -1
  39. package/dist/conversation-persist-plugin.js +7 -16
  40. package/dist/conversation-persist-plugin.js.map +1 -1
  41. package/dist/conversation-repository.d.ts +79 -18
  42. package/dist/conversation-repository.d.ts.map +1 -1
  43. package/dist/conversation-repository.js +96 -30
  44. package/dist/conversation-repository.js.map +1 -1
  45. package/dist/cwd-tracker-plugin.d.ts.map +1 -1
  46. package/dist/cwd-tracker-plugin.js +22 -7
  47. package/dist/cwd-tracker-plugin.js.map +1 -1
  48. package/dist/format-time.d.ts +17 -6
  49. package/dist/format-time.d.ts.map +1 -1
  50. package/dist/format-time.js +32 -14
  51. package/dist/format-time.js.map +1 -1
  52. package/dist/handoff-service.d.ts +19 -0
  53. package/dist/handoff-service.d.ts.map +1 -0
  54. package/dist/handoff-service.js +89 -0
  55. package/dist/handoff-service.js.map +1 -0
  56. package/dist/hook.d.ts.map +1 -1
  57. package/dist/hook.js +69 -15
  58. package/dist/hook.js.map +1 -1
  59. package/dist/i18n/en-wizard.d.ts +8 -0
  60. package/dist/i18n/en-wizard.d.ts.map +1 -0
  61. package/dist/i18n/en-wizard.js +303 -0
  62. package/dist/i18n/en-wizard.js.map +1 -0
  63. package/dist/i18n/en.d.ts +7 -0
  64. package/dist/i18n/en.d.ts.map +1 -0
  65. package/dist/i18n/en.js +247 -0
  66. package/dist/i18n/en.js.map +1 -0
  67. package/dist/i18n/index.d.ts +57 -0
  68. package/dist/i18n/index.d.ts.map +1 -0
  69. package/dist/i18n/index.js +109 -0
  70. package/dist/i18n/index.js.map +1 -0
  71. package/dist/i18n/preferences.d.ts +37 -0
  72. package/dist/i18n/preferences.d.ts.map +1 -0
  73. package/dist/i18n/preferences.js +73 -0
  74. package/dist/i18n/preferences.js.map +1 -0
  75. package/dist/i18n/zh-wizard.d.ts +11 -0
  76. package/dist/i18n/zh-wizard.d.ts.map +1 -0
  77. package/dist/i18n/zh-wizard.js +306 -0
  78. package/dist/i18n/zh-wizard.js.map +1 -0
  79. package/dist/i18n/zh.d.ts +10 -0
  80. package/dist/i18n/zh.d.ts.map +1 -0
  81. package/dist/i18n/zh.js +251 -0
  82. package/dist/i18n/zh.js.map +1 -0
  83. package/dist/index.js +72 -25
  84. package/dist/index.js.map +1 -1
  85. package/dist/mcp-command.d.ts +32 -0
  86. package/dist/mcp-command.d.ts.map +1 -0
  87. package/dist/mcp-command.js +37 -0
  88. package/dist/mcp-command.js.map +1 -0
  89. package/dist/mode.d.ts +31 -4
  90. package/dist/mode.d.ts.map +1 -1
  91. package/dist/mode.js +42 -7
  92. package/dist/mode.js.map +1 -1
  93. package/dist/model-switch.d.ts +58 -0
  94. package/dist/model-switch.d.ts.map +1 -0
  95. package/dist/model-switch.js +62 -0
  96. package/dist/model-switch.js.map +1 -0
  97. package/dist/reasoning-effort.d.ts +96 -0
  98. package/dist/reasoning-effort.d.ts.map +1 -0
  99. package/dist/reasoning-effort.js +164 -0
  100. package/dist/reasoning-effort.js.map +1 -0
  101. package/dist/reasoning-presets.d.ts +34 -0
  102. package/dist/reasoning-presets.d.ts.map +1 -0
  103. package/dist/reasoning-presets.js +85 -0
  104. package/dist/reasoning-presets.js.map +1 -0
  105. package/dist/sdk-log.d.ts +84 -0
  106. package/dist/sdk-log.d.ts.map +1 -0
  107. package/dist/sdk-log.js +167 -0
  108. package/dist/sdk-log.js.map +1 -0
  109. package/dist/session-pointer.d.ts.map +1 -1
  110. package/dist/session-pointer.js +10 -4
  111. package/dist/session-pointer.js.map +1 -1
  112. package/dist/stdio-runner.d.ts.map +1 -1
  113. package/dist/stdio-runner.js +32 -30
  114. package/dist/stdio-runner.js.map +1 -1
  115. package/dist/sub-agent-guard-plugin.d.ts +10 -7
  116. package/dist/sub-agent-guard-plugin.d.ts.map +1 -1
  117. package/dist/sub-agent-guard-plugin.js +12 -9
  118. package/dist/sub-agent-guard-plugin.js.map +1 -1
  119. package/dist/tui/chat-history.d.ts +29 -0
  120. package/dist/tui/chat-history.d.ts.map +1 -0
  121. package/dist/tui/chat-history.js +55 -0
  122. package/dist/tui/chat-history.js.map +1 -0
  123. package/dist/tui/chat-session.d.ts +177 -57
  124. package/dist/tui/chat-session.d.ts.map +1 -1
  125. package/dist/tui/chat-session.js +362 -126
  126. package/dist/tui/chat-session.js.map +1 -1
  127. package/dist/tui/components.d.ts +113 -7
  128. package/dist/tui/components.d.ts.map +1 -1
  129. package/dist/tui/components.js +177 -12
  130. package/dist/tui/components.js.map +1 -1
  131. package/dist/tui/config-wizard/detail.d.ts +18 -10
  132. package/dist/tui/config-wizard/detail.d.ts.map +1 -1
  133. package/dist/tui/config-wizard/detail.js +53 -46
  134. package/dist/tui/config-wizard/detail.js.map +1 -1
  135. package/dist/tui/config-wizard/engine.d.ts +2 -2
  136. package/dist/tui/config-wizard/engine.d.ts.map +1 -1
  137. package/dist/tui/config-wizard/engine.js +48 -26
  138. package/dist/tui/config-wizard/engine.js.map +1 -1
  139. package/dist/tui/config-wizard/fields.d.ts +3 -1
  140. package/dist/tui/config-wizard/fields.d.ts.map +1 -1
  141. package/dist/tui/config-wizard/fields.js +202 -167
  142. package/dist/tui/config-wizard/fields.js.map +1 -1
  143. package/dist/tui/config-wizard/frame.js +1 -1
  144. package/dist/tui/config-wizard/kv-editor.d.ts.map +1 -1
  145. package/dist/tui/config-wizard/kv-editor.js +13 -5
  146. package/dist/tui/config-wizard/kv-editor.js.map +1 -1
  147. package/dist/tui/config-wizard/list.d.ts +3 -1
  148. package/dist/tui/config-wizard/list.d.ts.map +1 -1
  149. package/dist/tui/config-wizard/list.js +4 -4
  150. package/dist/tui/config-wizard/list.js.map +1 -1
  151. package/dist/tui/config-wizard/mcp-helpers.d.ts.map +1 -1
  152. package/dist/tui/config-wizard/mcp-helpers.js +4 -3
  153. package/dist/tui/config-wizard/mcp-helpers.js.map +1 -1
  154. package/dist/tui/config-wizard/menu.d.ts.map +1 -1
  155. package/dist/tui/config-wizard/menu.js +33 -21
  156. package/dist/tui/config-wizard/menu.js.map +1 -1
  157. package/dist/tui/config-wizard/prompt-editor.d.ts +8 -1
  158. package/dist/tui/config-wizard/prompt-editor.d.ts.map +1 -1
  159. package/dist/tui/config-wizard/prompt-editor.js +11 -7
  160. package/dist/tui/config-wizard/prompt-editor.js.map +1 -1
  161. package/dist/tui/config-wizard/prompt.d.ts.map +1 -1
  162. package/dist/tui/config-wizard/prompt.js +8 -7
  163. package/dist/tui/config-wizard/prompt.js.map +1 -1
  164. package/dist/tui/config-wizard/step-agents.d.ts.map +1 -1
  165. package/dist/tui/config-wizard/step-agents.js +50 -29
  166. package/dist/tui/config-wizard/step-agents.js.map +1 -1
  167. package/dist/tui/config-wizard/step-defaults.d.ts.map +1 -1
  168. package/dist/tui/config-wizard/step-defaults.js +77 -27
  169. package/dist/tui/config-wizard/step-defaults.js.map +1 -1
  170. package/dist/tui/config-wizard/step-endpoints.d.ts.map +1 -1
  171. package/dist/tui/config-wizard/step-endpoints.js +27 -14
  172. package/dist/tui/config-wizard/step-endpoints.js.map +1 -1
  173. package/dist/tui/config-wizard/step-mcp.d.ts.map +1 -1
  174. package/dist/tui/config-wizard/step-mcp.js +29 -25
  175. package/dist/tui/config-wizard/step-mcp.js.map +1 -1
  176. package/dist/tui/config-wizard/step-models.d.ts.map +1 -1
  177. package/dist/tui/config-wizard/step-models.js +98 -43
  178. package/dist/tui/config-wizard/step-models.js.map +1 -1
  179. package/dist/tui/config-wizard/step-new-endpoint.d.ts.map +1 -1
  180. package/dist/tui/config-wizard/step-new-endpoint.js +16 -12
  181. package/dist/tui/config-wizard/step-new-endpoint.js.map +1 -1
  182. package/dist/tui/config-wizard/step-simple.d.ts.map +1 -1
  183. package/dist/tui/config-wizard/step-simple.js +18 -17
  184. package/dist/tui/config-wizard/step-simple.js.map +1 -1
  185. package/dist/tui/config-wizard/types.d.ts +31 -3
  186. package/dist/tui/config-wizard/types.d.ts.map +1 -1
  187. package/dist/tui/config-wizard/types.js.map +1 -1
  188. package/dist/tui/file-index.d.ts +38 -0
  189. package/dist/tui/file-index.d.ts.map +1 -0
  190. package/dist/tui/file-index.js +117 -0
  191. package/dist/tui/file-index.js.map +1 -0
  192. package/dist/tui/index.d.ts +18 -1
  193. package/dist/tui/index.d.ts.map +1 -1
  194. package/dist/tui/index.js +107 -49
  195. package/dist/tui/index.js.map +1 -1
  196. package/dist/tui/input-editing.d.ts +52 -0
  197. package/dist/tui/input-editing.d.ts.map +1 -0
  198. package/dist/tui/input-editing.js +91 -0
  199. package/dist/tui/input-editing.js.map +1 -0
  200. package/dist/tui/key-debug.d.ts +62 -0
  201. package/dist/tui/key-debug.d.ts.map +1 -0
  202. package/dist/tui/key-debug.js +92 -0
  203. package/dist/tui/key-debug.js.map +1 -0
  204. package/dist/tui/mention.d.ts +47 -0
  205. package/dist/tui/mention.d.ts.map +1 -0
  206. package/dist/tui/mention.js +152 -0
  207. package/dist/tui/mention.js.map +1 -0
  208. package/dist/tui/mouse.d.ts +55 -0
  209. package/dist/tui/mouse.d.ts.map +1 -0
  210. package/dist/tui/mouse.js +74 -0
  211. package/dist/tui/mouse.js.map +1 -0
  212. package/dist/tui/phase.d.ts +64 -0
  213. package/dist/tui/phase.d.ts.map +1 -0
  214. package/dist/tui/phase.js +112 -0
  215. package/dist/tui/phase.js.map +1 -0
  216. package/dist/tui/screens/agent-tabs.d.ts +53 -0
  217. package/dist/tui/screens/agent-tabs.d.ts.map +1 -0
  218. package/dist/tui/screens/agent-tabs.js +81 -0
  219. package/dist/tui/screens/agent-tabs.js.map +1 -0
  220. package/dist/tui/screens/chat-commands.d.ts +82 -3
  221. package/dist/tui/screens/chat-commands.d.ts.map +1 -1
  222. package/dist/tui/screens/chat-commands.js +688 -160
  223. package/dist/tui/screens/chat-commands.js.map +1 -1
  224. package/dist/tui/screens/chat.d.ts +57 -5
  225. package/dist/tui/screens/chat.d.ts.map +1 -1
  226. package/dist/tui/screens/chat.js +799 -304
  227. package/dist/tui/screens/chat.js.map +1 -1
  228. package/dist/tui/screens/common-keys.d.ts +36 -0
  229. package/dist/tui/screens/common-keys.d.ts.map +1 -0
  230. package/dist/tui/screens/common-keys.js +67 -0
  231. package/dist/tui/screens/common-keys.js.map +1 -0
  232. package/dist/tui/screens/frame-buffer.d.ts +47 -0
  233. package/dist/tui/screens/frame-buffer.d.ts.map +1 -0
  234. package/dist/tui/screens/frame-buffer.js +78 -0
  235. package/dist/tui/screens/frame-buffer.js.map +1 -0
  236. package/dist/tui/screens/handoff-choice.d.ts +37 -0
  237. package/dist/tui/screens/handoff-choice.d.ts.map +1 -0
  238. package/dist/tui/screens/handoff-choice.js +42 -0
  239. package/dist/tui/screens/handoff-choice.js.map +1 -0
  240. package/dist/tui/screens/index.d.ts +11 -9
  241. package/dist/tui/screens/index.d.ts.map +1 -1
  242. package/dist/tui/screens/index.js +9 -8
  243. package/dist/tui/screens/index.js.map +1 -1
  244. package/dist/tui/screens/input-row.d.ts +64 -0
  245. package/dist/tui/screens/input-row.d.ts.map +1 -0
  246. package/dist/tui/screens/input-row.js +67 -0
  247. package/dist/tui/screens/input-row.js.map +1 -0
  248. package/dist/tui/screens/input.d.ts +77 -1
  249. package/dist/tui/screens/input.d.ts.map +1 -1
  250. package/dist/tui/screens/input.js +125 -8
  251. package/dist/tui/screens/input.js.map +1 -1
  252. package/dist/tui/screens/layout.d.ts +42 -3
  253. package/dist/tui/screens/layout.d.ts.map +1 -1
  254. package/dist/tui/screens/layout.js +43 -13
  255. package/dist/tui/screens/layout.js.map +1 -1
  256. package/dist/tui/screens/lines.d.ts +6 -36
  257. package/dist/tui/screens/lines.d.ts.map +1 -1
  258. package/dist/tui/screens/lines.js +8 -71
  259. package/dist/tui/screens/lines.js.map +1 -1
  260. package/dist/tui/screens/messages-view.d.ts +29 -0
  261. package/dist/tui/screens/messages-view.d.ts.map +1 -0
  262. package/dist/tui/screens/messages-view.js +162 -0
  263. package/dist/tui/screens/messages-view.js.map +1 -0
  264. package/dist/tui/screens/notice-bar.d.ts +5 -0
  265. package/dist/tui/screens/notice-bar.d.ts.map +1 -0
  266. package/dist/tui/screens/notice-bar.js +21 -0
  267. package/dist/tui/screens/notice-bar.js.map +1 -0
  268. package/dist/tui/screens/notice.d.ts +66 -0
  269. package/dist/tui/screens/notice.d.ts.map +1 -0
  270. package/dist/tui/screens/notice.js +106 -0
  271. package/dist/tui/screens/notice.js.map +1 -0
  272. package/dist/tui/screens/overlays.d.ts.map +1 -1
  273. package/dist/tui/screens/overlays.js +4 -3
  274. package/dist/tui/screens/overlays.js.map +1 -1
  275. package/dist/tui/screens/render.d.ts +2 -4
  276. package/dist/tui/screens/render.d.ts.map +1 -1
  277. package/dist/tui/screens/render.js +19 -50
  278. package/dist/tui/screens/render.js.map +1 -1
  279. package/dist/tui/screens/splash.d.ts +1 -1
  280. package/dist/tui/screens/splash.d.ts.map +1 -1
  281. package/dist/tui/screens/splash.js +12 -3
  282. package/dist/tui/screens/splash.js.map +1 -1
  283. package/dist/tui/screens/use-chat-viewport.d.ts +22 -0
  284. package/dist/tui/screens/use-chat-viewport.d.ts.map +1 -0
  285. package/dist/tui/screens/use-chat-viewport.js +50 -0
  286. package/dist/tui/screens/use-chat-viewport.js.map +1 -0
  287. package/dist/tui/screens/use-input-editing.d.ts +69 -0
  288. package/dist/tui/screens/use-input-editing.d.ts.map +1 -0
  289. package/dist/tui/screens/use-input-editing.js +220 -0
  290. package/dist/tui/screens/use-input-editing.js.map +1 -0
  291. package/dist/tui/screens/use-overlays.d.ts +25 -0
  292. package/dist/tui/screens/use-overlays.d.ts.map +1 -0
  293. package/dist/tui/screens/use-overlays.js +32 -0
  294. package/dist/tui/screens/use-overlays.js.map +1 -0
  295. package/dist/tui/screens/use-wizard-host.d.ts +46 -0
  296. package/dist/tui/screens/use-wizard-host.d.ts.map +1 -0
  297. package/dist/tui/screens/use-wizard-host.js +123 -0
  298. package/dist/tui/screens/use-wizard-host.js.map +1 -0
  299. package/dist/tui/status-segments.d.ts +37 -0
  300. package/dist/tui/status-segments.d.ts.map +1 -0
  301. package/dist/tui/status-segments.js +166 -0
  302. package/dist/tui/status-segments.js.map +1 -0
  303. package/dist/tui/text.d.ts +27 -7
  304. package/dist/tui/text.d.ts.map +1 -1
  305. package/dist/tui/text.js +116 -38
  306. package/dist/tui/text.js.map +1 -1
  307. package/dist/tui/theme.d.ts +2 -0
  308. package/dist/tui/theme.d.ts.map +1 -1
  309. package/dist/tui/theme.js +2 -0
  310. package/dist/tui/theme.js.map +1 -1
  311. package/dist/tui/token-rate.d.ts +83 -0
  312. package/dist/tui/token-rate.d.ts.map +1 -0
  313. package/dist/tui/token-rate.js +195 -0
  314. package/dist/tui/token-rate.js.map +1 -0
  315. package/dist/types.d.ts +7 -8
  316. package/dist/types.d.ts.map +1 -1
  317. package/dist/user-content.d.ts +41 -0
  318. package/dist/user-content.d.ts.map +1 -0
  319. package/dist/user-content.js +37 -0
  320. package/dist/user-content.js.map +1 -0
  321. package/dist/version-compare.d.ts +37 -0
  322. package/dist/version-compare.d.ts.map +1 -0
  323. package/dist/version-compare.js +105 -0
  324. package/dist/version-compare.js.map +1 -0
  325. package/dist/version.js.map +1 -1
  326. package/package.json +5 -4
  327. package/dist/auto-migrate-confirm-plugin.d.ts +0 -58
  328. package/dist/auto-migrate-confirm-plugin.d.ts.map +0 -1
  329. package/dist/auto-migrate-confirm-plugin.js +0 -61
  330. package/dist/auto-migrate-confirm-plugin.js.map +0 -1
  331. package/dist/migration-service.d.ts +0 -15
  332. package/dist/migration-service.d.ts.map +0 -1
  333. package/dist/migration-service.js +0 -59
  334. package/dist/migration-service.js.map +0 -1
  335. package/dist/tui/screens/state.d.ts +0 -30
  336. package/dist/tui/screens/state.d.ts.map +0 -1
  337. package/dist/tui/screens/state.js +0 -100
  338. package/dist/tui/screens/state.js.map +0 -1
@@ -8,21 +8,100 @@
8
8
  * 新增、改名或调整说明只需改动本文件。本模块为纯数据与纯函数,
9
9
  * 不依赖 chalk / React 等 UI 依赖,可直接单测。
10
10
  */
11
- /** 命令元数据表(数组顺序即展示顺序) */
12
- export const COMMAND_REGISTRY = [
13
- { name: "save", description: "保存当前对话" },
14
- { name: "load", description: "加载已保存的对话(替换当前会话)" },
15
- { name: "new", description: "重置会话(清空历史)" },
16
- { name: "back", description: "撤回消息(可修改后重发)" },
17
- { name: "editor", description: "使用系统编辑器输入长消息" },
18
- { name: "clear", description: "清屏" },
19
- { name: "config", description: "打开配置中心(默认模型 / 端点管理)" },
20
- { name: "key", description: "设置当前端点的 API Key" },
21
- { name: "migrate", description: "手动任务迁移(生成交接文档并开启新会话)" },
22
- { name: "help", description: "显示此帮助" },
11
+ import { t } from "../i18n/index.js";
12
+ /**
13
+ * 命令元数据表(数组顺序即展示顺序)。
14
+ *
15
+ * 用 `as const` 声明以保留命令名的字面量类型,从而派生出 `CommandName`:
16
+ * 命令处理器表以 `Record<CommandName, …>` 声明,于是「新增命令但忘了写处理器」是
17
+ * **编译错误**,而不是运行时静默落到「未知命令」。
18
+ *
19
+ * 但 `as const` 的代价是联合类型里**没有别名的条目压根没有 `aliases` 字段**,
20
+ * 遍历时访问会报错。所以这里只把它作为内部字面量,对外导出的
21
+ * {@link COMMAND_REGISTRY} 放宽成 `readonly CommandMeta[]` —— 类型保护一分不少,
22
+ * 而所有遍历点都能直接读 `command.aliases`,不需要额外的取值 helper。
23
+ */
24
+ const REGISTRY_ENTRIES = [
25
+ { name: "save", descriptionKey: "cmd.save.desc" },
26
+ { name: "load", descriptionKey: "cmd.load.desc" },
27
+ { name: "new", descriptionKey: "cmd.new.desc" },
28
+ { name: "back", descriptionKey: "cmd.back.desc" },
29
+ { name: "editor", descriptionKey: "cmd.editor.desc" },
30
+ { name: "clear", descriptionKey: "cmd.clear.desc" },
31
+ { name: "config", descriptionKey: "cmd.config.desc" },
32
+ { name: "model", descriptionKey: "cmd.model.desc" },
33
+ { name: "agent", descriptionKey: "cmd.agent.desc" },
34
+ { name: "reload", descriptionKey: "cmd.reload.desc" },
35
+ { name: "update", descriptionKey: "cmd.update.desc", blocksInput: true },
36
+ { name: "mcp", descriptionKey: "cmd.mcp.desc" },
37
+ { name: "lang", descriptionKey: "cmd.lang.desc" },
38
+ { name: "think", aliases: ["effort"], descriptionKey: "cmd.think.desc" },
39
+ { name: "key", descriptionKey: "cmd.key.desc" },
40
+ { name: "handoff", descriptionKey: "cmd.handoff.desc", blocksInput: true },
41
+ { name: "migrate", descriptionKey: "cmd.migrate.desc", blocksInput: true },
42
+ { name: "help", descriptionKey: "cmd.help.desc" },
23
43
  // exit 置于末尾:斜杠菜单回车执行高亮项,避免「只输入 / 就回车」直接退出
24
- { name: "exit", aliases: ["quit"], description: "退出对话(会提示是否保存)" },
44
+ { name: "exit", aliases: ["quit"], descriptionKey: "cmd.exit.desc" },
25
45
  ];
46
+ /** 命令元数据表:值来自上面的字面量,类型放宽到 `readonly CommandMeta[]` 便于遍历 */
47
+ export const COMMAND_REGISTRY = REGISTRY_ENTRIES;
48
+ /**
49
+ * 把命令名(含别名)归一到主名;未知命令返回 `undefined`。
50
+ *
51
+ * `/think` 与 `/effort` 是同一条命令的两种写法,处理器只按主名注册一份。
52
+ */
53
+ export function canonicalCommandName(name) {
54
+ const normalized = name.trim().toLowerCase();
55
+ // 在放宽类型的表上匹配(可直接读 `aliases`),再用同一下标取回字面量类型
56
+ const index = COMMAND_REGISTRY.findIndex((command) => command.name === normalized || command.aliases?.includes(normalized));
57
+ return index < 0 ? undefined : REGISTRY_ENTRIES[index].name;
58
+ }
59
+ /** 命中命令的元数据(别名可查) */
60
+ export function findCommand(name) {
61
+ const canonical = canonicalCommandName(name);
62
+ return COMMAND_REGISTRY.find((command) => command.name === canonical);
63
+ }
64
+ /** 该命令执行期间是否应拦住输入框(见 {@link CommandMeta.blocksInput}) */
65
+ export function commandBlocksInput(name) {
66
+ return findCommand(name)?.blocksInput ?? false;
67
+ }
68
+ /**
69
+ * 把原始斜杠输入切成「命令名 + 参数」。
70
+ *
71
+ * 两种返回形状共用同一个切分规则(`/migrate self ` → `migrate` + `["self"]`):
72
+ * 命令名由 {@link parseCommandInput} 取,参数由 {@link parseCommandArgs} 取。
73
+ * 以前命令层自己又 `raw.trim().split(/\s+/).slice(1)` 了一遍 —— 命令名收敛了,
74
+ * 参数没有,于是「参数怎么切」仍然有两份实现。
75
+ */
76
+ function splitCommandInput(text) {
77
+ return text.trim().replace(/^\//, "").split(/\s+/);
78
+ }
79
+ /**
80
+ * 从原始输入里取命令参数(首个 token 之后的全部 token)。
81
+ *
82
+ * 与 {@link parseCommandInput} 共用 {@link splitCommandInput},保证「命令名边界」与
83
+ * 「参数边界」用的是同一把尺子。
84
+ */
85
+ export function parseCommandArgs(text) {
86
+ return splitCommandInput(text).slice(1);
87
+ }
88
+ /**
89
+ * 解析斜杠输入:`/migrate summary` → `{ name: "migrate", args: ["summary"] }`。
90
+ *
91
+ * 命令名的解析**只有这一处**:此前宿主自己也 `slice(1).split(/\s+/)[0]` 了一遍,
92
+ * 于是「命令名怎么算」在命令层与宿主层各有一份实现。
93
+ *
94
+ * 注意「只打了个 `/`」也算命令(`name` 为空串):宿主按斜杠前缀分流,空命令名
95
+ * 落到「未知命令」——与拆出本函数之前的行为一致。
96
+ *
97
+ * @returns 非斜杠输入返回 `null`
98
+ */
99
+ export function parseCommandInput(text) {
100
+ if (!text.trim().startsWith("/"))
101
+ return null;
102
+ const [name, ...args] = splitCommandInput(text);
103
+ return { name: name.toLowerCase(), args };
104
+ }
26
105
  /** 展开全部命令为展示条目(别名折叠) */
27
106
  export function getCommandHints() {
28
107
  return COMMAND_REGISTRY.map((command) => {
@@ -30,7 +109,7 @@ export function getCommandHints() {
30
109
  return {
31
110
  names,
32
111
  label: names.map((name) => `/${name}`).join(" "),
33
- description: command.description,
112
+ description: t(command.descriptionKey),
34
113
  };
35
114
  });
36
115
  }
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/conversation-commands/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAYH,wBAAwB;AACxB,MAAM,CAAC,MAAM,gBAAgB,GAA2B;IACtD,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE;IACvC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE;IACjD,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE;IAC1C,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE;IAC7C,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE;IAC/C,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE;IACpC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACtD,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE;IAC/C,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,sBAAsB,EAAE;IACxD,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE;IACtC,0CAA0C;IAC1C,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,eAAe,EAAE;CAClE,CAAC;AAYF,wBAAwB;AACxB,MAAM,UAAU,eAAe;IAC7B,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACtC,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;QACzD,OAAO;YACL,KAAK;YACL,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YAChD,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAC9B,IAAI,CAAC,GAAG,CAAC,GAAG,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;AAEtE;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChD,IAAI,CAAC,UAAU;QAAE,OAAO,eAAe,EAAE,CAAC;IAE1C,OAAO,eAAe,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACvC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CACvD,CAAC;AACJ,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,eAAe;IAC7B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/conversation-commands/registry.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,6 @@ export interface ConversationPersistPluginOptions {
12
12
  /** 动态返回当前会话 id(迁移切换会话后随之变化) */
13
13
  getId: () => string;
14
14
  agentId: string;
15
- modelId: string;
16
15
  cwd?: string;
17
16
  /** 落盘失败时的上报(TUI 走 Ink notice);默认丢弃 */
18
17
  onError?: (text: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-persist-plugin.d.ts","sourceRoot":"","sources":["../src/conversation-persist-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKnE,MAAM,WAAW,gCAAgC;IAC/C,+BAA+B;IAC/B,KAAK,EAAE,MAAM,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,qBAAa,yBAA0B,YAAW,WAAW;IAC/C,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,gCAAgC;IAEvD,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAyBtD"}
1
+ {"version":3,"file":"conversation-persist-plugin.d.ts","sourceRoot":"","sources":["../src/conversation-persist-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKnE,MAAM,WAAW,gCAAgC;IAC/C,+BAA+B;IAC/B,KAAK,EAAE,MAAM,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,qBAAa,yBAA0B,YAAW,WAAW;IAC/C,OAAO,CAAC,OAAO;IAA3B,YAAoB,OAAO,EAAE,gCAAgC,EAAI;IAE3D,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAepD;CACF"}
@@ -8,36 +8,27 @@
8
8
  * 会话 id 通过 `getId()` 动态获取,以便任务迁移切换新会话后依然落盘到正确的文件。
9
9
  */
10
10
  import { AgentNS } from "@ai-zen/agents-core";
11
- import { conversationRepository } from "./conversation-repository.js";
12
- import { writeLastSession } from "./session-pointer.js";
11
+ import { persistConversation } from "./conversation-repository.js";
12
+ import { t } from "./i18n/index.js";
13
13
  export class ConversationPersistPlugin {
14
14
  options;
15
15
  constructor(options) {
16
16
  this.options = options;
17
17
  }
18
18
  async onInnerLoopEnd(ctx) {
19
- const { getId, agentId, modelId, cwd, onError } = this.options;
19
+ const { getId, agentId, cwd, onError } = this.options;
20
20
  const messages = ctx.agent.messages;
21
21
  // 尚无实质内容(没有用户消息)时不落盘,避免产生空会话文件
22
22
  if (!messages.some((m) => m.role === AgentNS.Role.User))
23
23
  return;
24
24
  const id = getId();
25
25
  try {
26
- const existing = await conversationRepository.read(id);
27
- const now = new Date().toISOString();
28
- await conversationRepository.write({
29
- id,
30
- agentId,
31
- modelId,
32
- messages,
33
- cwd,
34
- createdAt: existing?.createdAt ?? now,
35
- updatedAt: now,
36
- });
37
- await writeLastSession(cwd ?? process.cwd(), id);
26
+ // 每轮自动落盘也算「本目录的上一轮会话」—— 下次在该目录启动时续接它。
27
+ // `SendContext.cwd` 是 SDK 的可选字段,这里(调用点)是**唯一**需要兜底的地方。
28
+ await persistConversation({ id, agentId, messages, cwd: cwd ?? process.cwd() });
38
29
  }
39
30
  catch (err) {
40
- onError?.(`⚠️ 会话落盘失败:${err?.message ?? err}`);
31
+ onError?.(t("chat.persistFailed", { error: err?.message ?? err }));
41
32
  }
42
33
  }
43
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-persist-plugin.js","sourceRoot":"","sources":["../src/conversation-persist-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAYxD,MAAM,OAAO,yBAAyB;IAChB;IAApB,YAAoB,OAAyC;QAAzC,YAAO,GAAP,OAAO,CAAkC;IAAG,CAAC;IAEjE,KAAK,CAAC,cAAc,CAAC,GAAgB;QACnC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAC/D,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;QAEpC,+BAA+B;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO;QAEhE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACvD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,sBAAsB,CAAC,KAAK,CAAC;gBACjC,EAAE;gBACF,OAAO;gBACP,OAAO;gBACP,QAAQ;gBACR,GAAG;gBACH,SAAS,EAAE,QAAQ,EAAE,SAAS,IAAI,GAAG;gBACrC,SAAS,EAAE,GAAG;aACf,CAAC,CAAC;YACH,MAAM,gBAAgB,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,EAAE,CAAC,aAAa,GAAG,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"conversation-persist-plugin.js","sourceRoot":"","sources":["../src/conversation-persist-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAWpC,MAAM,OAAO,yBAAyB;IAChB,OAAO;IAA3B,YAAoB,OAAyC;uBAAzC,OAAO;IAAqC,CAAC;IAEjE,KAAK,CAAC,cAAc,CAAC,GAAgB;QACnC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QACtD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;QAEpC,+BAA+B;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO;QAEhE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,sCAAsC;YACtC,uDAAuD;YACvD,MAAM,mBAAmB,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClF,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,EAAE,CAAC,CAAC,CAAC,oBAAoB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;CACF"}
@@ -7,38 +7,99 @@
7
7
  * 「上一轮会话 id」指针(session-pointer.ts)随每次落盘更新,供下次启动续接。
8
8
  */
9
9
  import { EntityRepository } from "@ai-zen/agents-sdk";
10
- import type { AgentNS } from "@ai-zen/agents-core";
10
+ import { AgentNS } from "@ai-zen/agents-core";
11
11
  import type { ConversationContext } from "./types.js";
12
12
  export interface Conversation {
13
13
  id: string;
14
14
  agentId: string;
15
- modelId: string;
15
+ /**
16
+ * @deprecated 已不再读**也不写**(收敛前 stdio 的 `--save` 曾写入它)。
17
+ * 模型真相源是 `AgentDefinition.modelId`;保留字段仅为兼容旧存档的反序列化。
18
+ */
19
+ modelId?: string;
16
20
  messages: AgentNS.Message[];
17
21
  lastPromptTokens?: number;
18
22
  cwd?: string;
19
- createdAt: string;
20
- updatedAt: string;
21
23
  }
22
- /** 会话仓储:每个会话一个 JSON 文件(${conversationsDir}/${id}.json) */
23
- export declare const conversationRepository: EntityRepository<Conversation>;
24
24
  /**
25
- * 生成文件名安全的会话 id(语义化中文时间戳,天然不含非法字符)。
26
- * 形如 `对话_2025年07月01日14时30分00秒`。
25
+ * 会话仓储:每个会话一个 JSON 文件(${conversationsDir}/${id}.json),**写盘是原子的**。
26
+ *
27
+ * 为什么要覆写 `write()`:SDK 的实现是直接 `fs.writeFile`,进程在写一半时被杀
28
+ * (Ctrl+C、OOM、断电)就留下半截 JSON,`read()` 解析失败返回 null —— 整段历史
29
+ * 就此丢失。会话文件可达数 MB,写入窗口并不小。这里改成「写临时文件 → rename」:
30
+ * 同一文件系统内 rename 是原子操作,任何时刻读到的要么是旧文件、要么是完整新文件。
31
+ *
32
+ * 临时文件名为 `<id>.json.tmp`:`list()` 按 `.json` 后缀过滤,不会把它当成会话。
33
+ */
34
+ declare class AtomicConversationRepository extends EntityRepository<Conversation> {
35
+ write(entity: Conversation): Promise<void>;
36
+ /**
37
+ * 会话文件的绝对路径(`/save <名字>` 删旧文件名时用)。
38
+ *
39
+ * 只是把 SDK 的 `protected path()` 公开出来,**不另写一套拼接** —— 待 L2 在 `path()`
40
+ * 里加上路径穿越校验后,`/save ../x` 这类名字会自然被拦住。
41
+ */
42
+ filePath(id: string): string;
43
+ }
44
+ /**
45
+ * 会话仓储。
46
+ *
47
+ * 每次调用新建一个轻对象:仓储本身无内部状态(只包一个目录),而目录(`CONVERSATIONS_DIR`)
48
+ * 是会变的活绑定。若在模块加载时固化成单例,进程内共享模块跑测试时后一个用例会
49
+ * 继续读写前一个用例的目录。
50
+ */
51
+ export declare function conversationRepository(): AtomicConversationRepository;
52
+ /**
53
+ * 生成文件名安全的会话 id:英文前缀 + 全下划线时间戳,中英一致。
54
+ * 形如 `chat_2026_10_28_00_56_34`。
27
55
  */
28
56
  export declare function generateConversationId(date?: Date): string;
57
+ /** 落盘一份会话所需的全部输入 */
58
+ export interface PersistConversationInput {
59
+ id: string;
60
+ agentId?: string;
61
+ messages: AgentNS.Message[];
62
+ /**
63
+ * 会话所属工作目录。**必填**:调用点都知道自己属于哪个目录,
64
+ * 留成可选只会在入口处多一层 `?? process.cwd()`,与调用点自己的兜底重复。
65
+ */
66
+ cwd: string;
67
+ /**
68
+ * 是否同时把「本目录上一轮会话」指针指向它(默认 true)。
69
+ *
70
+ * stdio 的 `--save` 传 false:那是一次性管道调用的归档,不该让下次 `ai` 续接到它
71
+ * (否则连着跑几条 `cat … | ai --save` 之后,`ai` 会续接到最后一条管道结果上)。
72
+ */
73
+ setAsLastSession?: boolean;
74
+ }
29
75
  /**
30
- * 保存当前对话到 conversations 目录,并更新「上一轮会话 id」指针。
31
- * 迁移前落盘旧会话(onBeforeMigrate)、手动 /save 等场景共同调用,保证 id 计算一致。
76
+ * 落盘一份会话 —— **唯一的写入入口**。
77
+ *
78
+ * 此前「读旧记录拿 createdAt → 写 → 更新指针」这段在 5 个地方各写了一遍,并且已经
79
+ * 漂移:`saveConversation()` 漏写 `cwd`、stdio 的 `--save` 不更新指针、还写了一个已
80
+ * 废弃的 `modelId`。收敛到这里后,各处差异只剩「是否更新指针」一个显式开关。
32
81
  */
33
- export declare function saveConversation(ctx: ConversationContext): Promise<string>;
82
+ export declare function persistConversation(input: PersistConversationInput): Promise<string>;
34
83
  /**
35
- * 列出已保存的会话(仅读文件名与 mtime,不解析 JSON 内容,快),按更新时间倒序。
36
- * 供对话内 `/load` 选择器使用。
84
+ * 保存当前对话(交接前落盘旧会话时用):以 `ctx` 为真相源,走统一的写入入口。
37
85
  */
38
- export declare function listConversations(): Promise<Array<{
86
+ export declare function saveConversation(ctx: ConversationContext): Promise<string>;
87
+ /** `/load` 选择器的一行:只有会话文件名(id) */
88
+ export interface ConversationSummary {
39
89
  id: string;
40
- name: string;
41
- updatedAt: string;
42
- size: number;
43
- }>>;
90
+ }
91
+ /**
92
+ * 会话文件是否已存在(`/save <名字>` 的重名判断)。
93
+ *
94
+ * 只 `access`:判断重名不需要解析内容,`read()` 会把整个会话 JSON.parse 一遍。
95
+ */
96
+ export declare function conversationExists(id: string): Promise<boolean>;
97
+ /**
98
+ * 列出已保存的会话,按文件 mtime 倒序。供对话内 `/load` 选择器使用。
99
+ *
100
+ * 只 `readdir` + `stat`,**不 read 任何会话文件**:列表只显示文件名,摘要(首条消息)
101
+ * 已取消 —— 会话攒到上百条时,逐个 `JSON.parse` 会明显拖慢 `/load`。
102
+ */
103
+ export declare function listConversations(): Promise<ConversationSummary[]>;
104
+ export {};
44
105
  //# sourceMappingURL=conversation-repository.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-repository.d.ts","sourceRoot":"","sources":["../src/conversation-repository.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAInD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,gCAAwD,CAAC;AAE5F;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,GAAE,IAAiB,GAAG,MAAM,CAEtE;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAchF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAChD,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CACrE,CAmBA"}
1
+ {"version":3,"file":"conversation-repository.d.ts","sourceRoot":"","sources":["../src/conversation-repository.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAI9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;CASd;AAED;;;;;;;;;GASG;AACH,cAAM,4BAA6B,SAAQ,gBAAgB,CAAC,YAAY,CAAC;IACxD,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxD;IAED;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3B;CACF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,4BAA4B,CAErE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,GAAE,IAAiB,GAAG,MAAM,CAEtE;AAED,oBAAoB;AACpB,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IAC5B;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAW1F;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAOhF;AAED,iCAAiC;AACjC,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOrE;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAqBxE"}
@@ -7,64 +7,130 @@
7
7
  * 「上一轮会话 id」指针(session-pointer.ts)随每次落盘更新,供下次启动续接。
8
8
  */
9
9
  import { promises as fs } from "node:fs";
10
- import { join } from "node:path";
10
+ import { dirname, join } from "node:path";
11
11
  import { EntityRepository } from "@ai-zen/agents-sdk";
12
12
  import { CONVERSATIONS_DIR } from "./config.js";
13
- import { formatShortTime } from "./format-time.js";
13
+ import { formatFileStamp } from "./format-time.js";
14
14
  import { writeLastSession } from "./session-pointer.js";
15
- /** 会话仓储:每个会话一个 JSON 文件(${conversationsDir}/${id}.json) */
16
- export const conversationRepository = new EntityRepository(CONVERSATIONS_DIR);
17
15
  /**
18
- * 生成文件名安全的会话 id(语义化中文时间戳,天然不含非法字符)。
19
- * 形如 `对话_2025年07月01日14时30分00秒`。
16
+ * 会话仓储:每个会话一个 JSON 文件(${conversationsDir}/${id}.json),**写盘是原子的**。
17
+ *
18
+ * 为什么要覆写 `write()`:SDK 的实现是直接 `fs.writeFile`,进程在写一半时被杀
19
+ * (Ctrl+C、OOM、断电)就留下半截 JSON,`read()` 解析失败返回 null —— 整段历史
20
+ * 就此丢失。会话文件可达数 MB,写入窗口并不小。这里改成「写临时文件 → rename」:
21
+ * 同一文件系统内 rename 是原子操作,任何时刻读到的要么是旧文件、要么是完整新文件。
22
+ *
23
+ * 临时文件名为 `<id>.json.tmp`:`list()` 按 `.json` 后缀过滤,不会把它当成会话。
24
+ */
25
+ class AtomicConversationRepository extends EntityRepository {
26
+ async write(entity) {
27
+ const target = this.path(entity.id);
28
+ const tmp = `${target}.tmp`;
29
+ await fs.mkdir(dirname(target), { recursive: true });
30
+ await fs.writeFile(tmp, JSON.stringify(entity, null, 2), "utf-8");
31
+ await fs.rename(tmp, target);
32
+ }
33
+ /**
34
+ * 会话文件的绝对路径(`/save <名字>` 删旧文件名时用)。
35
+ *
36
+ * 只是把 SDK 的 `protected path()` 公开出来,**不另写一套拼接** —— 待 L2 在 `path()`
37
+ * 里加上路径穿越校验后,`/save ../x` 这类名字会自然被拦住。
38
+ */
39
+ filePath(id) {
40
+ return this.path(id);
41
+ }
42
+ }
43
+ /**
44
+ * 会话仓储。
45
+ *
46
+ * 每次调用新建一个轻对象:仓储本身无内部状态(只包一个目录),而目录(`CONVERSATIONS_DIR`)
47
+ * 是会变的活绑定。若在模块加载时固化成单例,进程内共享模块跑测试时后一个用例会
48
+ * 继续读写前一个用例的目录。
49
+ */
50
+ export function conversationRepository() {
51
+ return new AtomicConversationRepository(CONVERSATIONS_DIR);
52
+ }
53
+ /**
54
+ * 生成文件名安全的会话 id:英文前缀 + 全下划线时间戳,中英一致。
55
+ * 形如 `chat_2026_10_28_00_56_34`。
20
56
  */
21
57
  export function generateConversationId(date = new Date()) {
22
- return `对话_${formatShortTime(date.toISOString())}`.replace(/[\\/:*?"<>|]/g, "_");
58
+ return `chat_${formatFileStamp(date.toISOString())}`;
23
59
  }
24
60
  /**
25
- * 保存当前对话到 conversations 目录,并更新「上一轮会话 id」指针。
26
- * 迁移前落盘旧会话(onBeforeMigrate)、手动 /save 等场景共同调用,保证 id 计算一致。
61
+ * 落盘一份会话 —— **唯一的写入入口**。
62
+ *
63
+ * 此前「读旧记录拿 createdAt → 写 → 更新指针」这段在 5 个地方各写了一遍,并且已经
64
+ * 漂移:`saveConversation()` 漏写 `cwd`、stdio 的 `--save` 不更新指针、还写了一个已
65
+ * 废弃的 `modelId`。收敛到这里后,各处差异只剩「是否更新指针」一个显式开关。
66
+ */
67
+ export async function persistConversation(input) {
68
+ // 仓储**只取一次**:`CONVERSATIONS_DIR` 是活绑定,同一份落盘必须只读一个目录。
69
+ const repository = conversationRepository();
70
+ await repository.write({
71
+ id: input.id,
72
+ agentId: input.agentId || "default",
73
+ messages: input.messages,
74
+ cwd: input.cwd,
75
+ });
76
+ if (input.setAsLastSession !== false)
77
+ await writeLastSession(input.cwd, input.id);
78
+ return input.id;
79
+ }
80
+ /**
81
+ * 保存当前对话(交接前落盘旧会话时用):以 `ctx` 为真相源,走统一的写入入口。
27
82
  */
28
83
  export async function saveConversation(ctx) {
29
- const id = ctx.currentId || generateConversationId();
30
- const existing = await conversationRepository.read(id);
31
- const now = new Date().toISOString();
32
- await conversationRepository.write({
33
- id,
34
- agentId: ctx.agentId || "default",
35
- modelId: ctx.modelId,
84
+ return persistConversation({
85
+ id: ctx.currentId || generateConversationId(),
86
+ agentId: ctx.agentId,
36
87
  messages: ctx.agent.messages,
37
- createdAt: existing?.createdAt ?? now,
38
- updatedAt: now,
88
+ cwd: process.cwd(),
39
89
  });
40
- await writeLastSession(process.cwd(), id);
41
- return id;
42
90
  }
43
91
  /**
44
- * 列出已保存的会话(仅读文件名与 mtime,不解析 JSON 内容,快),按更新时间倒序。
45
- * 供对话内 `/load` 选择器使用。
92
+ * 会话文件是否已存在(`/save <名字>` 的重名判断)。
93
+ *
94
+ * 只 `access`:判断重名不需要解析内容,`read()` 会把整个会话 JSON.parse 一遍。
95
+ */
96
+ export async function conversationExists(id) {
97
+ try {
98
+ await fs.access(conversationRepository().filePath(id));
99
+ return true;
100
+ }
101
+ catch {
102
+ return false;
103
+ }
104
+ }
105
+ /**
106
+ * 列出已保存的会话,按文件 mtime 倒序。供对话内 `/load` 选择器使用。
107
+ *
108
+ * 只 `readdir` + `stat`,**不 read 任何会话文件**:列表只显示文件名,摘要(首条消息)
109
+ * 已取消 —— 会话攒到上百条时,逐个 `JSON.parse` 会明显拖慢 `/load`。
46
110
  */
47
111
  export async function listConversations() {
112
+ const dir = CONVERSATIONS_DIR;
113
+ let files;
48
114
  try {
49
- await fs.access(CONVERSATIONS_DIR);
115
+ files = await fs.readdir(dir);
50
116
  }
51
117
  catch {
52
- return [];
118
+ return []; // 首次启动:会话目录还没建
53
119
  }
54
- const files = await fs.readdir(CONVERSATIONS_DIR);
55
- const result = [];
120
+ const entries = [];
56
121
  for (const file of files) {
57
122
  if (!file.endsWith(".json"))
58
123
  continue;
59
124
  const id = file.replace(/\.json$/, "");
60
125
  try {
61
- const st = await fs.stat(join(CONVERSATIONS_DIR, file));
62
- result.push({ id, name: id, updatedAt: st.mtime.toISOString(), size: st.size });
126
+ const st = await fs.stat(join(dir, file));
127
+ entries.push({ id, mtime: st.mtimeMs });
63
128
  }
64
129
  catch {
65
- /* 跳过读取失败的条目 */
130
+ /* 读目录与 stat 之间被别的进程删掉:跳过(不是错误,也不该让 /load 崩) */
66
131
  }
67
132
  }
68
- return result.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
133
+ entries.sort((a, b) => b.mtime - a.mtime);
134
+ return entries.map((entry) => ({ id: entry.id }));
69
135
  }
70
136
  //# sourceMappingURL=conversation-repository.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-repository.js","sourceRoot":"","sources":["../src/conversation-repository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversation-repository.js","sourceRoot":"","sources":["../src/conversation-repository.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cwd-tracker-plugin.d.ts","sourceRoot":"","sources":["../src/cwd-tracker-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEnE,qBAAa,gBAAiB,YAAW,WAAW;IAClD,8BAA8B;IAC9B,OAAO,CAAC,OAAO,CAAuB;IAEhC,gBAAgB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAmBxD"}
1
+ {"version":3,"file":"cwd-tracker-plugin.d.ts","sourceRoot":"","sources":["../src/cwd-tracker-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEnE,qBAAa,gBAAiB,YAAW,WAAW;IAClD,8BAA8B;IAC9B,OAAO,CAAC,OAAO,CAAuB;IAEhC,gBAAgB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BtD;CACF"}
@@ -39,14 +39,29 @@ export class CwdTrackerPlugin {
39
39
  return;
40
40
  const isFirstTime = this.lastCwd === null;
41
41
  this.lastCwd = currentCwd;
42
- // 找到最后一条 user 消息,将 cwd 信息追加到其末尾
43
- const lastUserMsg = [...ctx.agent.messages]
44
- .reverse()
45
- .find((m) => m.role === AgentNS.Role.User && typeof m.content === "string");
46
- if (!lastUserMsg || typeof lastUserMsg.content !== "string")
42
+ // 找到最后一条 user 消息,将 cwd 信息追加到其末尾。
43
+ const lastUserMsg = [...ctx.agent.messages].reverse().find((m) => m.role === AgentNS.Role.User);
44
+ if (!lastUserMsg)
47
45
  return;
48
- const prefix = isFirstTime ? "" : "\n\n[工作目录变更]";
49
- lastUserMsg.content += `${prefix}\n当前工作目录: ${currentCwd}`;
46
+ // 注入给模型的提示词:用英文,并显式标注「系统注入」—— 翻存档 / 交接文档时
47
+ // 一眼能分辨哪些内容不是用户敲的。(协议内容,不走 i18n)
48
+ const tag = isFirstTime
49
+ ? "[system-injected: current working directory — not written by the user]"
50
+ : "[system-injected: working directory changed — not written by the user]";
51
+ const note = `${tag}\nCurrent working directory: ${currentCwd}`;
52
+ // 粘贴过图片的消息,`content` 是内容块数组(图片 / 文本混排):cwd 要追加到
53
+ // **最后一个 text 段**上,而不是另起一段——另起会多出一条「只有 cwd」的文本块。
54
+ if (Array.isArray(lastUserMsg.content)) {
55
+ const textSection = [...lastUserMsg.content].reverse().find((section) => section.type === "text");
56
+ if (textSection && textSection.type === "text")
57
+ textSection.text += `\n\n${note}`;
58
+ else
59
+ lastUserMsg.content.push({ type: "text", text: note });
60
+ return;
61
+ }
62
+ if (typeof lastUserMsg.content !== "string")
63
+ return;
64
+ lastUserMsg.content += `\n\n${note}`;
50
65
  }
51
66
  }
52
67
  //# sourceMappingURL=cwd-tracker-plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cwd-tracker-plugin.js","sourceRoot":"","sources":["../src/cwd-tracker-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAG9C,MAAM,OAAO,gBAAgB;IAC3B,8BAA8B;IACtB,OAAO,GAAkB,IAAI,CAAC;IAEtC,KAAK,CAAC,gBAAgB,CAAC,GAAgB;QACrC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAEjC,gBAAgB;QAChB,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU;YAAE,OAAO;QAExC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;QAC1C,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC;QAE1B,gCAAgC;QAChC,MAAM,WAAW,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;aACxC,OAAO,EAAE;aACT,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC;QAE9E,IAAI,CAAC,WAAW,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO;QAEpE,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC;QACjD,WAAW,CAAC,OAAO,IAAI,GAAG,MAAM,aAAa,UAAU,EAAE,CAAC;IAC5D,CAAC;CACF"}
1
+ {"version":3,"file":"cwd-tracker-plugin.js","sourceRoot":"","sources":["../src/cwd-tracker-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAG9C,MAAM,OAAO,gBAAgB;IAC3B,8BAA8B;IACtB,OAAO,GAAkB,IAAI,CAAC;IAEtC,KAAK,CAAC,gBAAgB,CAAC,GAAgB;QACrC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAEjC,gBAAgB;QAChB,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU;YAAE,OAAO;QAExC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;QAC1C,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC;QAE1B,iCAAiC;QACjC,MAAM,WAAW,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChG,IAAI,CAAC,WAAW;YAAE,OAAO;QAEzB,0CAA0C;QAC1C,iCAAiC;QACjC,MAAM,GAAG,GAAG,WAAW;YACrB,CAAC,CAAC,wEAAwE;YAC1E,CAAC,CAAC,wEAAwE,CAAC;QAC7E,MAAM,IAAI,GAAG,GAAG,GAAG,gCAAgC,UAAU,EAAE,CAAC;QAEhE,gDAAgD;QAChD,iDAAiD;QACjD,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,MAAM,WAAW,GAAG,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;YAClG,IAAI,WAAW,IAAI,WAAW,CAAC,IAAI,KAAK,MAAM;gBAAE,WAAW,CAAC,IAAI,IAAI,OAAO,IAAI,EAAE,CAAC;;gBAC7E,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5D,OAAO;QACT,CAAC;QACD,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO;QACpD,WAAW,CAAC,OAAO,IAAI,OAAO,IAAI,EAAE,CAAC;IACvC,CAAC;CACF"}
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * 统一时间格式化工具
3
3
  *
4
- * 所有用户可见的时间文案统一使用 dayjs + zh-cn locale,
5
- * 确保各种场景(会话列表、会话标签等)显示一致。
4
+ * 所有用户可见的时间文案统一走本模块,格式随界面语言变化:
5
+ * - 中文:`7月1日 14:30`、`昨天 14:30`
6
+ * - 英文:`Jul 1, 14:30`、`Yesterday 14:30`
7
+ *
8
+ * 语言从 `i18n` 现取(不同模块级常量),因此 `/lang` 切完下一帧就生效。
9
+ * 例外:`formatFileStamp()` 用于**文件名 / 会话 id**,不随语言变(否则新旧文件混排)。
6
10
  */
7
11
  import "dayjs/locale/zh-cn.js";
8
12
  /**
@@ -16,13 +20,20 @@ import "dayjs/locale/zh-cn.js";
16
20
  */
17
21
  export declare function formatRelativeTime(isoString: string): string;
18
22
  /**
19
- * 格式化时间戳为完整中文格式(用于详情展示)
20
- * 例:2025年7月1日 14:30:00
23
+ * 格式化时间戳为完整格式(用于详情展示)
24
+ * 例(中):2025年7月1日 14:30:00 (英):Jul 1, 2025 14:30:00
21
25
  */
22
26
  export declare function formatFullTime(isoString: string): string;
23
27
  /**
24
- * 格式化时间戳为保存名称用的格式,兼容文件名
25
- * 例:2025年07月01日_14时30分00秒
28
+ * 文件名安全的时间戳:全下划线、纯数字,用作会话 id。
29
+ * 例:2026_10_28_00_56_34
30
+ *
31
+ * **刻意不随界面语言变**:它进了文件名与会话 id,跟着语言变会让历史文件与新文件混排。
32
+ */
33
+ export declare function formatFileStamp(isoString: string): string;
34
+ /**
35
+ * 紧凑时间戳(列表/详情提示显示用),随界面语言变:
36
+ * 中文 `2025年07月01日15时30分00秒`,英文 `Jul 1, 2025 15:30:00`。
26
37
  */
27
38
  export declare function formatShortTime(isoString: string): string;
28
39
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"format-time.d.ts","sourceRoot":"","sources":["../src/format-time.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,uBAAuB,CAAC;AAK/B;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAc5D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD"}
1
+ {"version":3,"file":"format-time.d.ts","sourceRoot":"","sources":["../src/format-time.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,uBAAuB,CAAC;AAW/B;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAc5D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD"}