@armadra/agent 0.5.1 → 0.6.1

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 (486) hide show
  1. package/CHANGELOG.md +203 -304
  2. package/CHANGELOG.zh-CN.md +462 -0
  3. package/README.md +408 -364
  4. package/README.zh-CN.md +705 -0
  5. package/dist/agent/session-plan.js +2 -2
  6. package/dist/agent/session-state.d.ts +3 -0
  7. package/dist/agent/session-state.js +20 -0
  8. package/dist/agent/session-subagent.d.ts +15 -1
  9. package/dist/agent/session-subagent.js +26 -3
  10. package/dist/agent/session-telemetry.d.ts +16 -2
  11. package/dist/agent/session-telemetry.js +29 -7
  12. package/dist/agent/session-tools.js +5 -2
  13. package/dist/agent/session-trace-writer.d.ts +27 -0
  14. package/dist/agent/session-trace-writer.js +253 -0
  15. package/dist/agent/session.d.ts +2 -0
  16. package/dist/agent/session.js +12 -0
  17. package/dist/agent/subagent-direct.d.ts +20 -0
  18. package/dist/agent/subagent-direct.js +76 -0
  19. package/dist/agent/subagent-registry.d.ts +8 -1
  20. package/dist/agent/subagent-registry.js +28 -1
  21. package/dist/agent/system-prompt.d.ts +3 -1
  22. package/dist/agent/system-prompt.js +3 -0
  23. package/dist/agent/tool-runner.d.ts +2 -1
  24. package/dist/agent/tool-runner.js +5 -4
  25. package/dist/agent/types-w6.d.ts +39 -0
  26. package/dist/agent/types-w6.js +4 -0
  27. package/dist/agent/types.d.ts +5 -2
  28. package/dist/agents/external.js +1 -1
  29. package/dist/agents/task-record.d.ts +74 -1
  30. package/dist/agents/task-record.js +100 -0
  31. package/dist/ai/apis/cache-params.js +2 -1
  32. package/dist/ai/apis/chatgpt-backend.d.ts +52 -0
  33. package/dist/ai/apis/chatgpt-backend.js +224 -0
  34. package/dist/ai/apis/chatgpt-rate-limits.d.ts +26 -0
  35. package/dist/ai/apis/chatgpt-rate-limits.js +91 -0
  36. package/dist/ai/apis/openai-responses-request.d.ts +2 -1
  37. package/dist/ai/apis/openai-responses-request.js +8 -2
  38. package/dist/ai/apis/openai-responses.d.ts +2 -0
  39. package/dist/ai/apis/openai-responses.js +51 -16
  40. package/dist/ai/overflow.d.ts +1 -1
  41. package/dist/ai/overflow.js +10 -7
  42. package/dist/ai/providers/auth.d.ts +15 -2
  43. package/dist/ai/providers/auth.js +69 -2
  44. package/dist/ai/providers/builtin.js +27 -0
  45. package/dist/ai/providers/catalog-data.js +1 -0
  46. package/dist/ai/providers/discovered-cache.d.ts +48 -0
  47. package/dist/ai/providers/discovered-cache.js +102 -0
  48. package/dist/ai/providers/enrich.js +2 -7
  49. package/dist/ai/providers/model-visibility.d.ts +33 -0
  50. package/dist/ai/providers/model-visibility.js +70 -0
  51. package/dist/ai/providers/models-dev-cache.js +15 -19
  52. package/dist/ai/providers/models-dev-snapshot.js +8 -7
  53. package/dist/ai/providers/models-dev.js +5 -15
  54. package/dist/ai/providers/registry.d.ts +2 -0
  55. package/dist/ai/providers/registry.js +6 -1
  56. package/dist/ai/providers/suggest.js +2 -16
  57. package/dist/ai/types.d.ts +27 -1
  58. package/dist/auth/chatgpt/backend-client.d.ts +26 -0
  59. package/dist/auth/chatgpt/backend-client.js +74 -0
  60. package/dist/auth/chatgpt/claims.d.ts +17 -0
  61. package/dist/auth/chatgpt/claims.js +46 -0
  62. package/dist/auth/chatgpt/cli.d.ts +35 -0
  63. package/dist/auth/chatgpt/cli.js +279 -0
  64. package/dist/auth/chatgpt/doctor.d.ts +17 -0
  65. package/dist/auth/chatgpt/doctor.js +36 -0
  66. package/dist/auth/chatgpt/host-id.d.ts +7 -0
  67. package/dist/auth/chatgpt/host-id.js +26 -0
  68. package/dist/auth/chatgpt/login.d.ts +30 -0
  69. package/dist/auth/chatgpt/login.js +130 -0
  70. package/dist/auth/chatgpt/presets.d.ts +66 -0
  71. package/dist/auth/chatgpt/presets.js +120 -0
  72. package/dist/auth/chatgpt/quota-text.d.ts +13 -0
  73. package/dist/auth/chatgpt/quota-text.js +36 -0
  74. package/dist/auth/oauth/browser.d.ts +12 -0
  75. package/dist/auth/oauth/browser.js +27 -0
  76. package/dist/auth/oauth/callback-server.d.ts +47 -0
  77. package/dist/auth/oauth/callback-server.js +147 -0
  78. package/dist/auth/oauth/flows.d.ts +62 -0
  79. package/dist/auth/oauth/flows.js +113 -0
  80. package/dist/auth/oauth/jwt.d.ts +24 -0
  81. package/dist/auth/oauth/jwt.js +81 -0
  82. package/dist/auth/oauth/live.d.ts +21 -0
  83. package/dist/auth/oauth/live.js +25 -0
  84. package/dist/auth/oauth/oidc.d.ts +26 -0
  85. package/dist/auth/oauth/oidc.js +72 -0
  86. package/dist/auth/oauth/pkce.d.ts +15 -0
  87. package/dist/auth/oauth/pkce.js +20 -0
  88. package/dist/auth/oauth/refresh.d.ts +35 -0
  89. package/dist/auth/oauth/refresh.js +129 -0
  90. package/dist/auth/oauth/token-client.d.ts +33 -0
  91. package/dist/auth/oauth/token-client.js +121 -0
  92. package/dist/auth/oauth/token-store.d.ts +23 -0
  93. package/dist/auth/oauth/token-store.js +125 -0
  94. package/dist/auth/testing/fake-oauth.d.ts +73 -0
  95. package/dist/auth/testing/fake-oauth.js +271 -0
  96. package/dist/auth/testing/refresh-child.d.ts +5 -0
  97. package/dist/auth/testing/refresh-child.js +16 -0
  98. package/dist/bundle/ama.cjs +26532 -11045
  99. package/dist/checkpoints/backend.js +6 -5
  100. package/dist/checkpoints/restore.js +3 -2
  101. package/dist/checkpoints/settings.js +2 -1
  102. package/dist/checkpoints/shadow-git.js +7 -6
  103. package/dist/checkpoints/shadow-restore.js +3 -2
  104. package/dist/checkpoints/tracker.js +4 -3
  105. package/dist/cli/args.d.ts +9 -2
  106. package/dist/cli/args.js +74 -27
  107. package/dist/cli/bootstrap.js +53 -31
  108. package/dist/cli/choice-prompt.js +8 -6
  109. package/dist/cli/codemode-notice.js +2 -2
  110. package/dist/cli/compose-agents.js +2 -1
  111. package/dist/cli/compose-extensions.js +2 -0
  112. package/dist/cli/compose-memory.d.ts +53 -0
  113. package/dist/cli/compose-memory.js +127 -0
  114. package/dist/cli/compose-providers.d.ts +6 -0
  115. package/dist/cli/compose-providers.js +38 -2
  116. package/dist/cli/compose-session.d.ts +5 -0
  117. package/dist/cli/compose-session.js +27 -13
  118. package/dist/cli/compose-store.d.ts +2 -0
  119. package/dist/cli/compose-store.js +9 -5
  120. package/dist/cli/compose.d.ts +2 -0
  121. package/dist/cli/compose.js +13 -5
  122. package/dist/cli/default-model.d.ts +0 -2
  123. package/dist/cli/default-model.js +5 -8
  124. package/dist/cli/deps.d.ts +5 -1
  125. package/dist/cli/exit-codes.d.ts +1 -1
  126. package/dist/cli/exit-codes.js +7 -16
  127. package/dist/cli/from-prompt.js +3 -2
  128. package/dist/cli/help-text.d.ts +2 -1
  129. package/dist/cli/help-text.js +5 -99
  130. package/dist/cli/main.d.ts +5 -0
  131. package/dist/cli/main.js +21 -2
  132. package/dist/cli/proxy.js +14 -9
  133. package/dist/cli/runtime.d.ts +5 -0
  134. package/dist/cli/startup-screen.js +20 -27
  135. package/dist/cli/startup-steps.js +6 -8
  136. package/dist/cli/subcommands/auth.d.ts +6 -3
  137. package/dist/cli/subcommands/auth.js +52 -22
  138. package/dist/cli/subcommands/config-set.d.ts +21 -0
  139. package/dist/cli/subcommands/config-set.js +145 -0
  140. package/dist/cli/subcommands/config.d.ts +2 -1
  141. package/dist/cli/subcommands/config.js +51 -37
  142. package/dist/cli/subcommands/doctor.d.ts +2 -1
  143. package/dist/cli/subcommands/doctor.js +97 -58
  144. package/dist/cli/subcommands/init.d.ts +1 -1
  145. package/dist/cli/subcommands/init.js +8 -7
  146. package/dist/cli/subcommands/memory.d.ts +18 -0
  147. package/dist/cli/subcommands/memory.js +189 -0
  148. package/dist/cli/subcommands/model-meta.js +11 -9
  149. package/dist/cli/subcommands/models-cache-probe.js +23 -20
  150. package/dist/cli/subcommands/models-discover.d.ts +4 -0
  151. package/dist/cli/subcommands/models-discover.js +60 -21
  152. package/dist/cli/subcommands/models-enable.d.ts +13 -0
  153. package/dist/cli/subcommands/models-enable.js +145 -0
  154. package/dist/cli/subcommands/models.d.ts +3 -1
  155. package/dist/cli/subcommands/models.js +39 -24
  156. package/dist/cli/subcommands/probe-runner.js +8 -7
  157. package/dist/cli/subcommands/providers-list.js +32 -21
  158. package/dist/cli/subcommands/providers-plan.js +24 -25
  159. package/dist/cli/subcommands/providers-probe.js +5 -6
  160. package/dist/cli/subcommands/providers.d.ts +1 -1
  161. package/dist/cli/subcommands/providers.js +45 -50
  162. package/dist/cli/subcommands/sessions-export.d.ts +1 -1
  163. package/dist/cli/subcommands/sessions-export.js +10 -9
  164. package/dist/cli/subcommands/sessions-search.d.ts +1 -1
  165. package/dist/cli/subcommands/sessions-search.js +11 -10
  166. package/dist/cli/subcommands/sessions-trace.d.ts +30 -0
  167. package/dist/cli/subcommands/sessions-trace.js +156 -0
  168. package/dist/cli/subcommands/sessions.d.ts +4 -3
  169. package/dist/cli/subcommands/sessions.js +40 -37
  170. package/dist/cli/subcommands/stats.d.ts +1 -1
  171. package/dist/cli/subcommands/stats.js +49 -30
  172. package/dist/cli/system-prompt-arg.js +3 -2
  173. package/dist/codemode/capability.js +3 -2
  174. package/dist/compaction/post-compact.d.ts +1 -0
  175. package/dist/compaction/post-compact.js +15 -1
  176. package/dist/compaction/prune-tier.d.ts +1 -1
  177. package/dist/compaction/prune-tier.js +3 -3
  178. package/dist/compaction/serialize.js +2 -1
  179. package/dist/config/auth-file.d.ts +12 -2
  180. package/dist/config/auth-file.js +32 -10
  181. package/dist/config/checker.js +12 -11
  182. package/dist/config/context-files.js +3 -2
  183. package/dist/config/edit.d.ts +106 -0
  184. package/dist/config/edit.js +350 -0
  185. package/dist/config/init.d.ts +4 -3
  186. package/dist/config/init.js +10 -18
  187. package/dist/config/json-schema.d.ts +2 -1
  188. package/dist/config/json-schema.js +102 -69
  189. package/dist/config/key-docs.d.ts +18 -7
  190. package/dist/config/key-docs.js +43 -128
  191. package/dist/config/load.js +7 -6
  192. package/dist/config/merge.d.ts +4 -1
  193. package/dist/config/merge.js +42 -22
  194. package/dist/config/paths.js +6 -5
  195. package/dist/config/profile.d.ts +5 -1
  196. package/dist/config/profile.js +7 -2
  197. package/dist/config/schema-w5.js +12 -4
  198. package/dist/config/schema-w6.d.ts +19 -0
  199. package/dist/config/schema-w6.js +115 -0
  200. package/dist/config/schema.js +34 -20
  201. package/dist/config/settings-registry.d.ts +81 -0
  202. package/dist/config/settings-registry.js +185 -0
  203. package/dist/config/types-w5.d.ts +7 -0
  204. package/dist/config/types-w5.js +2 -0
  205. package/dist/config/types-w6.d.ts +109 -0
  206. package/dist/config/types-w6.js +19 -0
  207. package/dist/config/types.d.ts +11 -8
  208. package/dist/config/types.js +1 -0
  209. package/dist/drivers/acp/client.js +1 -1
  210. package/dist/drivers/acp/driver.js +13 -8
  211. package/dist/drivers/agents.js +4 -3
  212. package/dist/drivers/base.js +3 -2
  213. package/dist/drivers/host-runners.js +2 -1
  214. package/dist/drivers/native/claude-stream.js +12 -11
  215. package/dist/drivers/native/codex-app-server.js +17 -12
  216. package/dist/drivers/native/oneshot.js +8 -7
  217. package/dist/drivers/pool.js +1 -1
  218. package/dist/drivers/runner.d.ts +3 -1
  219. package/dist/drivers/runner.js +68 -20
  220. package/dist/drivers/turn.js +1 -1
  221. package/dist/hooks/config.js +3 -2
  222. package/dist/hooks/protocol.js +18 -12
  223. package/dist/host/api-impl.js +11 -10
  224. package/dist/host/loader.js +9 -8
  225. package/dist/host/types.d.ts +3 -0
  226. package/dist/i18n/catalog.d.ts +2209 -0
  227. package/dist/i18n/catalog.js +72 -0
  228. package/dist/i18n/format.d.ts +21 -0
  229. package/dist/i18n/format.js +58 -0
  230. package/dist/i18n/index.d.ts +58 -0
  231. package/dist/i18n/index.js +72 -0
  232. package/dist/i18n/messages/agents.d.ts +84 -0
  233. package/dist/i18n/messages/agents.js +85 -0
  234. package/dist/i18n/messages/approval.d.ts +99 -0
  235. package/dist/i18n/messages/approval.js +100 -0
  236. package/dist/i18n/messages/auth.d.ts +189 -0
  237. package/dist/i18n/messages/auth.js +201 -0
  238. package/dist/i18n/messages/cli-args.d.ts +46 -0
  239. package/dist/i18n/messages/cli-args.js +46 -0
  240. package/dist/i18n/messages/cli-help.d.ts +12 -0
  241. package/dist/i18n/messages/cli-help.js +248 -0
  242. package/dist/i18n/messages/cli.d.ts +336 -0
  243. package/dist/i18n/messages/cli.js +311 -0
  244. package/dist/i18n/messages/config-keys.d.ts +293 -0
  245. package/dist/i18n/messages/config-keys.js +296 -0
  246. package/dist/i18n/messages/config.d.ts +508 -0
  247. package/dist/i18n/messages/config.js +241 -0
  248. package/dist/i18n/messages/drivers.d.ts +123 -0
  249. package/dist/i18n/messages/drivers.js +124 -0
  250. package/dist/i18n/messages/errors.d.ts +83 -0
  251. package/dist/i18n/messages/errors.js +171 -0
  252. package/dist/i18n/messages/interactive-line.d.ts +61 -0
  253. package/dist/i18n/messages/interactive-line.js +69 -0
  254. package/dist/i18n/messages/interactive-startup.d.ts +99 -0
  255. package/dist/i18n/messages/interactive-startup.js +100 -0
  256. package/dist/i18n/messages/interactive-view.d.ts +140 -0
  257. package/dist/i18n/messages/interactive-view.js +141 -0
  258. package/dist/i18n/messages/interactive.d.ts +489 -0
  259. package/dist/i18n/messages/interactive.js +248 -0
  260. package/dist/i18n/messages/memory.d.ts +123 -0
  261. package/dist/i18n/messages/memory.js +128 -0
  262. package/dist/i18n/messages/panels.d.ts +233 -0
  263. package/dist/i18n/messages/panels.js +232 -0
  264. package/dist/i18n/messages/permissions.d.ts +126 -0
  265. package/dist/i18n/messages/permissions.js +151 -0
  266. package/dist/i18n/messages/plan.d.ts +123 -0
  267. package/dist/i18n/messages/plan.js +124 -0
  268. package/dist/i18n/messages/print.d.ts +86 -0
  269. package/dist/i18n/messages/print.js +91 -0
  270. package/dist/i18n/messages/report.d.ts +391 -0
  271. package/dist/i18n/messages/report.js +490 -0
  272. package/dist/i18n/messages/rewind.d.ts +198 -0
  273. package/dist/i18n/messages/rewind.js +217 -0
  274. package/dist/i18n/messages/session.d.ts +230 -0
  275. package/dist/i18n/messages/session.js +258 -0
  276. package/dist/i18n/messages/settings.d.ts +355 -0
  277. package/dist/i18n/messages/settings.js +343 -0
  278. package/dist/i18n/messages/subcommands-config.d.ts +113 -0
  279. package/dist/i18n/messages/subcommands-config.js +129 -0
  280. package/dist/i18n/messages/subcommands-models.d.ts +34 -0
  281. package/dist/i18n/messages/subcommands-models.js +34 -0
  282. package/dist/i18n/messages/subcommands.d.ts +576 -0
  283. package/dist/i18n/messages/subcommands.js +477 -0
  284. package/dist/i18n/messages/trace.d.ts +243 -0
  285. package/dist/i18n/messages/trace.js +238 -0
  286. package/dist/i18n/types.d.ts +15 -0
  287. package/dist/i18n/types.js +7 -0
  288. package/dist/index.d.ts +6 -0
  289. package/dist/index.js +2 -0
  290. package/dist/memory/edit.d.ts +24 -0
  291. package/dist/memory/edit.js +63 -0
  292. package/dist/memory/frontmatter.d.ts +37 -0
  293. package/dist/memory/frontmatter.js +79 -0
  294. package/dist/memory/index.d.ts +28 -0
  295. package/dist/memory/index.js +126 -0
  296. package/dist/memory/lock.d.ts +17 -0
  297. package/dist/memory/lock.js +62 -0
  298. package/dist/memory/paths.d.ts +57 -0
  299. package/dist/memory/paths.js +175 -0
  300. package/dist/memory/report.d.ts +36 -0
  301. package/dist/memory/report.js +86 -0
  302. package/dist/memory/runtime.d.ts +42 -0
  303. package/dist/memory/runtime.js +36 -0
  304. package/dist/memory/secrets.d.ts +8 -0
  305. package/dist/memory/secrets.js +27 -0
  306. package/dist/memory/section.d.ts +28 -0
  307. package/dist/memory/section.js +59 -0
  308. package/dist/memory/store.d.ts +70 -0
  309. package/dist/memory/store.js +284 -0
  310. package/dist/memory/tool.d.ts +31 -0
  311. package/dist/memory/tool.js +94 -0
  312. package/dist/modes/acp/acp-events.js +2 -1
  313. package/dist/modes/acp/acp-server.js +13 -12
  314. package/dist/modes/commands-core.d.ts +11 -0
  315. package/dist/modes/commands-core.js +93 -62
  316. package/dist/modes/image-input.js +20 -6
  317. package/dist/modes/interactive/agent-bar.d.ts +95 -0
  318. package/dist/modes/interactive/agent-bar.js +248 -0
  319. package/dist/modes/interactive/agent-panels.js +27 -26
  320. package/dist/modes/interactive/agent-transcript.d.ts +47 -0
  321. package/dist/modes/interactive/agent-transcript.js +191 -0
  322. package/dist/modes/interactive/agent-ui.d.ts +36 -1
  323. package/dist/modes/interactive/agent-ui.js +180 -14
  324. package/dist/modes/interactive/agent-view.d.ts +72 -0
  325. package/dist/modes/interactive/agent-view.js +281 -0
  326. package/dist/modes/interactive/approval-dialog.js +46 -34
  327. package/dist/modes/interactive/clipboard-paste.d.ts +2 -2
  328. package/dist/modes/interactive/clipboard-paste.js +9 -4
  329. package/dist/modes/interactive/commands.d.ts +22 -1
  330. package/dist/modes/interactive/commands.js +100 -49
  331. package/dist/modes/interactive/completion.js +2 -1
  332. package/dist/modes/interactive/config-panel.d.ts +70 -0
  333. package/dist/modes/interactive/config-panel.js +319 -0
  334. package/dist/modes/interactive/config-ui.d.ts +96 -0
  335. package/dist/modes/interactive/config-ui.js +397 -0
  336. package/dist/modes/interactive/confirm-dialog.js +6 -7
  337. package/dist/modes/interactive/event-notices.js +11 -9
  338. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  339. package/dist/modes/interactive/interactive-mode.js +21 -22
  340. package/dist/modes/interactive/key-dispatch.d.ts +12 -0
  341. package/dist/modes/interactive/key-dispatch.js +24 -9
  342. package/dist/modes/interactive/line/line-editor.js +2 -1
  343. package/dist/modes/interactive/line/line-mode.js +18 -7
  344. package/dist/modes/interactive/line/line-render.js +25 -24
  345. package/dist/modes/interactive/memory-panel.d.ts +64 -0
  346. package/dist/modes/interactive/memory-panel.js +221 -0
  347. package/dist/modes/interactive/message-view.d.ts +4 -2
  348. package/dist/modes/interactive/message-view.js +42 -33
  349. package/dist/modes/interactive/model-items.d.ts +45 -0
  350. package/dist/modes/interactive/model-items.js +134 -0
  351. package/dist/modes/interactive/model-picker.d.ts +57 -0
  352. package/dist/modes/interactive/model-picker.js +191 -0
  353. package/dist/modes/interactive/panels.js +39 -31
  354. package/dist/modes/interactive/pickers.d.ts +2 -1
  355. package/dist/modes/interactive/pickers.js +10 -6
  356. package/dist/modes/interactive/plan-command.d.ts +1 -1
  357. package/dist/modes/interactive/plan-command.js +24 -27
  358. package/dist/modes/interactive/plan-dialog.js +18 -17
  359. package/dist/modes/interactive/plan-flow.js +6 -5
  360. package/dist/modes/interactive/rewind-command.d.ts +1 -1
  361. package/dist/modes/interactive/rewind-command.js +18 -16
  362. package/dist/modes/interactive/rewind-flow.d.ts +0 -1
  363. package/dist/modes/interactive/rewind-flow.js +13 -14
  364. package/dist/modes/interactive/rewind-list.js +7 -6
  365. package/dist/modes/interactive/rewind-panel.js +40 -40
  366. package/dist/modes/interactive/rewind-text.d.ts +5 -3
  367. package/dist/modes/interactive/rewind-text.js +48 -38
  368. package/dist/modes/interactive/run-indicator.js +15 -12
  369. package/dist/modes/interactive/session-events.js +3 -2
  370. package/dist/modes/interactive/startup-header.js +22 -23
  371. package/dist/modes/interactive/startup-ui.d.ts +3 -9
  372. package/dist/modes/interactive/startup-ui.js +44 -88
  373. package/dist/modes/interactive/status-area.d.ts +2 -0
  374. package/dist/modes/interactive/status-area.js +8 -2
  375. package/dist/modes/interactive/status-bar.js +4 -3
  376. package/dist/modes/interactive/subagent-view.js +9 -7
  377. package/dist/modes/interactive/tasks-report.d.ts +6 -0
  378. package/dist/modes/interactive/tasks-report.js +28 -31
  379. package/dist/modes/interactive/terminal-setup.d.ts +9 -0
  380. package/dist/modes/interactive/terminal-setup.js +23 -0
  381. package/dist/modes/interactive/tool-summary.js +19 -17
  382. package/dist/modes/interactive/tool-view.js +7 -4
  383. package/dist/modes/interactive/trace-view.d.ts +89 -0
  384. package/dist/modes/interactive/trace-view.js +332 -0
  385. package/dist/modes/print/print-mode.js +14 -15
  386. package/dist/modes/rpc/commands.js +7 -3
  387. package/dist/modes/rpc/rpc-mode.js +9 -3
  388. package/dist/modes/session-report.d.ts +2 -0
  389. package/dist/modes/session-report.js +101 -105
  390. package/dist/modes/startup-ui-text.js +11 -6
  391. package/dist/permissions/bypass.d.ts +8 -10
  392. package/dist/permissions/bypass.js +19 -10
  393. package/dist/permissions/memory-class.d.ts +23 -0
  394. package/dist/permissions/memory-class.js +54 -0
  395. package/dist/permissions/modes.d.ts +3 -3
  396. package/dist/permissions/modes.js +21 -16
  397. package/dist/permissions/pipeline.d.ts +1 -1
  398. package/dist/permissions/pipeline.js +9 -2
  399. package/dist/permissions/preview.js +49 -42
  400. package/dist/permissions/rules.js +5 -2
  401. package/dist/permissions/types.d.ts +4 -0
  402. package/dist/plan/store.js +2 -1
  403. package/dist/rpc.d.ts +39 -1
  404. package/dist/rpc.js +3 -0
  405. package/dist/sandbox/bash.js +11 -8
  406. package/dist/sandbox/detect.js +14 -11
  407. package/dist/sdk.d.ts +17 -1
  408. package/dist/sdk.js +29 -7
  409. package/dist/session/export.js +31 -30
  410. package/dist/session/manager.d.ts +1 -1
  411. package/dist/session/manager.js +5 -3
  412. package/dist/session/reuse.js +5 -4
  413. package/dist/session/scan.js +3 -2
  414. package/dist/session/stats-aggregate.d.ts +3 -1
  415. package/dist/session/stats-aggregate.js +5 -1
  416. package/dist/session/stats-index.js +2 -1
  417. package/dist/session/stats-scan.d.ts +4 -1
  418. package/dist/session/stats-scan.js +6 -2
  419. package/dist/session/store.d.ts +11 -0
  420. package/dist/session/store.js +48 -1
  421. package/dist/session/types.d.ts +2 -0
  422. package/dist/skills/builtin.js +2 -1
  423. package/dist/tools/image-file.d.ts +26 -1
  424. package/dist/tools/image-file.js +55 -13
  425. package/dist/tools/presets.d.ts +14 -1
  426. package/dist/tools/presets.js +3 -3
  427. package/dist/tools/registry.js +1 -1
  428. package/dist/tools/truncate.d.ts +1 -1
  429. package/dist/tools/truncate.js +2 -2
  430. package/dist/tools/types.d.ts +17 -1
  431. package/dist/trace/build-index.d.ts +91 -0
  432. package/dist/trace/build-index.js +127 -0
  433. package/dist/trace/build-nodes.d.ts +24 -0
  434. package/dist/trace/build-nodes.js +279 -0
  435. package/dist/trace/build-util.d.ts +43 -0
  436. package/dist/trace/build-util.js +120 -0
  437. package/dist/trace/build.d.ts +82 -0
  438. package/dist/trace/build.js +382 -0
  439. package/dist/trace/detail.d.ts +35 -0
  440. package/dist/trace/detail.js +205 -0
  441. package/dist/trace/flatten.d.ts +69 -0
  442. package/dist/trace/flatten.js +173 -0
  443. package/dist/trace/format.d.ts +47 -0
  444. package/dist/trace/format.js +329 -0
  445. package/dist/trace/html-template.d.ts +19 -0
  446. package/dist/trace/html-template.js +150 -0
  447. package/dist/trace/html.d.ts +104 -0
  448. package/dist/trace/html.js +292 -0
  449. package/dist/trace/preview.d.ts +29 -0
  450. package/dist/trace/preview.js +59 -0
  451. package/dist/trace/query-session.d.ts +14 -0
  452. package/dist/trace/query-session.js +38 -0
  453. package/dist/trace/query.d.ts +55 -0
  454. package/dist/trace/query.js +188 -0
  455. package/dist/trace/session.d.ts +40 -0
  456. package/dist/trace/session.js +168 -0
  457. package/dist/trace/types.d.ts +243 -0
  458. package/dist/trace/types.js +13 -0
  459. package/dist/tui/components/editor-paste.d.ts +2 -0
  460. package/dist/tui/components/editor-paste.js +6 -3
  461. package/dist/tui/components/editor.js +2 -2
  462. package/dist/tui/components/loader.d.ts +3 -0
  463. package/dist/tui/components/loader.js +14 -0
  464. package/dist/tui/components/select-list.d.ts +2 -0
  465. package/dist/tui/components/select-list.js +7 -0
  466. package/dist/tui/components/settings-list.d.ts +61 -0
  467. package/dist/tui/components/settings-list.js +135 -0
  468. package/dist/tui/keybindings.d.ts +5 -0
  469. package/dist/tui/keybindings.js +17 -5
  470. package/dist/tui.d.ts +1 -0
  471. package/dist/tui.js +2 -0
  472. package/docs/en/host-api.md +167 -0
  473. package/docs/en/permissions.md +214 -0
  474. package/docs/en/providers.md +627 -0
  475. package/docs/en/rpc.md +361 -0
  476. package/docs/en/sessions.md +219 -0
  477. package/docs/en/tui.md +534 -0
  478. package/docs/host-api.md +18 -17
  479. package/docs/memory.md +172 -0
  480. package/docs/permissions.md +2 -2
  481. package/docs/providers.md +73 -1
  482. package/docs/rpc.md +31 -3
  483. package/docs/session-format.md +8 -2
  484. package/docs/sessions.md +29 -1
  485. package/docs/tui.md +140 -4
  486. package/package.json +8 -3
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 记忆条目的 frontmatter(docs/wave6-plan.md §3.2)。[W6-M]
3
+ *
4
+ * ```markdown
5
+ * ---
6
+ * name: 测试数据库重置
7
+ * description: 跑集成测试前要先执行的重置命令
8
+ * type: project # user | feedback | project | reference
9
+ * updated: 2026-10-03
10
+ * ---
11
+ *
12
+ * 正文
13
+ * ```
14
+ *
15
+ * 只认单行 `key: value`(值可带成对引号);未知键保留原顺序。写入时规范化:`name` 缺省用文件名、
16
+ * `description` 缺省用正文首个非空行、`type` 不在四类里用作用域缺省、`updated` 总是写当天。
17
+ */
18
+ export const MEMORY_TYPES = ["user", "feedback", "project", "reference"];
19
+ const ORDER = ["name", "description", "type", "updated"];
20
+ const MAX_DESCRIPTION = 150;
21
+ function unquote(value) {
22
+ const v = value.trim();
23
+ if (v.length >= 2 &&
24
+ ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))))
25
+ return v.slice(1, -1);
26
+ return v;
27
+ }
28
+ export function parseEntry(text) {
29
+ const normalized = text.replace(/\r\n/g, "\n").replace(/^\uFEFF/, "");
30
+ if (!normalized.startsWith("---\n"))
31
+ return { meta: {}, body: normalized };
32
+ const end = normalized.indexOf("\n---", 3);
33
+ if (end < 0)
34
+ return { meta: {}, body: normalized };
35
+ const after = normalized.slice(end + 4);
36
+ if (after !== "" && !after.startsWith("\n"))
37
+ return { meta: {}, body: normalized };
38
+ const meta = {};
39
+ for (const line of normalized.slice(4, end).split("\n")) {
40
+ const m = /^([A-Za-z][\w-]*)\s*:\s*(.*)$/.exec(line);
41
+ if (m?.[1] !== undefined)
42
+ meta[m[1]] = unquote(m[2] ?? "");
43
+ }
44
+ return { meta, body: after.replace(/^\n+/, "") };
45
+ }
46
+ function oneLine(text) {
47
+ return text.replace(/\s+/g, " ").trim();
48
+ }
49
+ /** 正文首个非空行(去掉标题井号),截到 150 字符。 */
50
+ export function firstLineOf(body) {
51
+ for (const raw of body.split("\n")) {
52
+ const line = oneLine(raw.replace(/^#+\s*/, ""));
53
+ if (line !== "")
54
+ return line.length > MAX_DESCRIPTION ? `${line.slice(0, MAX_DESCRIPTION - 1)}…` : line;
55
+ }
56
+ return "";
57
+ }
58
+ export function serializeEntry(meta, body) {
59
+ const keys = [
60
+ ...ORDER.filter((k) => meta[k] !== undefined),
61
+ ...Object.keys(meta).filter((k) => !ORDER.includes(k)),
62
+ ];
63
+ const lines = keys.map((k) => `${k}: ${oneLine(meta[k] ?? "")}`);
64
+ return `---\n${lines.join("\n")}\n---\n\n${body.trim()}\n`;
65
+ }
66
+ /** 写入前规范化:补全 name / description / type,`updated` 写 `today`。 */
67
+ export function normalizeEntry(text, defaults) {
68
+ const parsed = parseEntry(text);
69
+ const meta = { ...parsed.meta };
70
+ const body = parsed.body.trim();
71
+ if (oneLine(meta["name"] ?? "") === "")
72
+ meta["name"] = defaults.name;
73
+ if (oneLine(meta["description"] ?? "") === "")
74
+ meta["description"] = firstLineOf(body);
75
+ if (!MEMORY_TYPES.includes(meta["type"] ?? ""))
76
+ meta["type"] = defaults.type;
77
+ meta["updated"] = defaults.today;
78
+ return { text: serializeEntry(meta, body), meta, body };
79
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 作用域条目列表与 `MEMORY.md` 索引(docs/wave6-plan.md §3.2)。[W6-M]
3
+ *
4
+ * - 条目 = 作用域目录(含子目录)里的 `*.md`,跳过 `MEMORY.md`、隐藏文件 / 目录与符号链接;
5
+ * - 排序:`updated` 降序,同日按文件路径;
6
+ * - `MEMORY.md`:`- [name](file.md) — description` 一行一条,写操作后在作用域锁内重建(幂等,内容不变不写)。
7
+ * 节渲染不读它,直接列条目(用户手改条目后 `/memory reload` 或下次会话即生效)。
8
+ */
9
+ import { type MemoryScope } from "./paths.js";
10
+ export interface MemoryEntry {
11
+ scope: MemoryScope;
12
+ /** 作用域内的相对路径(`/` 分隔),如 `prefers-pnpm.md`。 */
13
+ file: string;
14
+ name: string;
15
+ description: string;
16
+ type?: string;
17
+ /** `YYYY-MM-DD`(缺 frontmatter 时取文件修改日期)。 */
18
+ updated: string;
19
+ bytes: number;
20
+ }
21
+ /** 作用域里全部条目的相对路径(目录不存在 → 空)。`under` 限定子目录。 */
22
+ export declare function entryFiles(root: string, under?: string): string[];
23
+ export declare function readEntry(scope: MemoryScope, root: string, file: string): MemoryEntry | undefined;
24
+ export declare function sortEntries(entries: MemoryEntry[]): MemoryEntry[];
25
+ export declare function listEntries(scope: MemoryScope, root: string, under?: string): MemoryEntry[];
26
+ export declare function renderIndexFile(entries: readonly MemoryEntry[]): string;
27
+ /** 重建 `MEMORY.md`(在作用域锁内调用;内容不变不写)。 */
28
+ export declare function rebuildIndex(scope: MemoryScope, root: string): void;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * 作用域条目列表与 `MEMORY.md` 索引(docs/wave6-plan.md §3.2)。[W6-M]
3
+ *
4
+ * - 条目 = 作用域目录(含子目录)里的 `*.md`,跳过 `MEMORY.md`、隐藏文件 / 目录与符号链接;
5
+ * - 排序:`updated` 降序,同日按文件路径;
6
+ * - `MEMORY.md`:`- [name](file.md) — description` 一行一条,写操作后在作用域锁内重建(幂等,内容不变不写)。
7
+ * 节渲染不读它,直接列条目(用户手改条目后 `/memory reload` 或下次会话即生效)。
8
+ */
9
+ import { lstatSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { firstLineOf, parseEntry } from "./frontmatter.js";
12
+ import { INDEX_FILE } from "./paths.js";
13
+ const MAX_DEPTH = 4;
14
+ function isoDate(ms) {
15
+ return new Date(ms).toISOString().slice(0, 10);
16
+ }
17
+ function walk(dir, prefix, depth, out) {
18
+ let names;
19
+ try {
20
+ names = readdirSync(dir);
21
+ }
22
+ catch {
23
+ return;
24
+ }
25
+ for (const name of names.sort()) {
26
+ if (name.startsWith("."))
27
+ continue;
28
+ const abs = join(dir, name);
29
+ let info;
30
+ try {
31
+ info = lstatSync(abs);
32
+ }
33
+ catch {
34
+ continue;
35
+ }
36
+ if (info.isSymbolicLink())
37
+ continue;
38
+ const rel = prefix === "" ? name : `${prefix}/${name}`;
39
+ if (info.isDirectory()) {
40
+ if (depth < MAX_DEPTH)
41
+ walk(abs, rel, depth + 1, out);
42
+ }
43
+ else if (info.isFile() && name.endsWith(".md") && !(prefix === "" && name === INDEX_FILE)) {
44
+ out.push(rel);
45
+ }
46
+ }
47
+ }
48
+ /** 作用域里全部条目的相对路径(目录不存在 → 空)。`under` 限定子目录。 */
49
+ export function entryFiles(root, under = "") {
50
+ const out = [];
51
+ walk(under === "" ? root : join(root, ...under.split("/")), under, under.split("/").length, out);
52
+ return out;
53
+ }
54
+ function stem(file) {
55
+ return (file.split("/").at(-1) ?? file).replace(/\.md$/, "");
56
+ }
57
+ export function readEntry(scope, root, file) {
58
+ const abs = join(root, ...file.split("/"));
59
+ let text;
60
+ let mtime;
61
+ try {
62
+ text = readFileSync(abs, "utf8");
63
+ mtime = lstatSync(abs).mtimeMs;
64
+ }
65
+ catch {
66
+ return undefined;
67
+ }
68
+ const { meta, body } = parseEntry(text);
69
+ const updated = /^\d{4}-\d{2}-\d{2}$/.test(meta["updated"] ?? "")
70
+ ? meta["updated"]
71
+ : isoDate(mtime);
72
+ const entry = {
73
+ scope,
74
+ file,
75
+ name: (meta["name"] ?? "").trim() || stem(file),
76
+ description: (meta["description"] ?? "").trim() || firstLineOf(body),
77
+ updated,
78
+ bytes: Buffer.byteLength(text, "utf8"),
79
+ };
80
+ if (meta["type"] !== undefined && meta["type"] !== "")
81
+ entry.type = meta["type"];
82
+ return entry;
83
+ }
84
+ export function sortEntries(entries) {
85
+ return entries.sort((a, b) => a.updated === b.updated ? a.file.localeCompare(b.file) : a.updated < b.updated ? 1 : -1);
86
+ }
87
+ export function listEntries(scope, root, under = "") {
88
+ const entries = [];
89
+ for (const file of entryFiles(root, under)) {
90
+ const entry = readEntry(scope, root, file);
91
+ if (entry !== undefined)
92
+ entries.push(entry);
93
+ }
94
+ return sortEntries(entries);
95
+ }
96
+ function clean(text) {
97
+ return text.replace(/\s+/g, " ").replace(/[[\]]/g, "").trim();
98
+ }
99
+ export function renderIndexFile(entries) {
100
+ const lines = entries.map((e) => `- [${clean(e.name)}](${e.file})${e.description === "" ? "" : ` — ${clean(e.description)}`}`);
101
+ return [
102
+ "# Memory index",
103
+ "",
104
+ "<!-- Maintained by ama after each memory write; edit the entries, not this file. -->",
105
+ "",
106
+ ...(lines.length === 0 ? ["(no entries)"] : lines),
107
+ "",
108
+ ].join("\n");
109
+ }
110
+ /** 重建 `MEMORY.md`(在作用域锁内调用;内容不变不写)。 */
111
+ export function rebuildIndex(scope, root) {
112
+ const text = renderIndexFile(listEntries(scope, root));
113
+ const path = join(root, INDEX_FILE);
114
+ let current;
115
+ try {
116
+ current = readFileSync(path, "utf8");
117
+ }
118
+ catch {
119
+ current = undefined;
120
+ }
121
+ if (current === text)
122
+ return;
123
+ const tmp = join(root, `.${INDEX_FILE}.${process.pid}.tmp`);
124
+ writeFileSync(tmp, text, { mode: 0o600 });
125
+ renameSync(tmp, path);
126
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 每作用域一把锁(docs/wave6-plan.md §3.2)。[W6-M]
3
+ *
4
+ * 进程内按目录串行(`tools/file-mutex.ts`);跨进程用作用域目录里的 `.lock`(`O_EXCL` 创建,写 pid)。
5
+ * 等锁至多 `timeoutMs`(缺省 5 s);锁文件超过 `staleMs`(缺省 30 s)视为陈旧,删掉重试。
6
+ * 写操作(create / str_replace / delete 与索引重建)都在锁内;读不加锁(原子 rename 保证读到整文件)。
7
+ */
8
+ export declare const LOCK_FILE = ".lock";
9
+ export declare class MemoryLockError extends Error {
10
+ readonly code = "locked";
11
+ }
12
+ export interface LockOptions {
13
+ timeoutMs?: number;
14
+ staleMs?: number;
15
+ now?(): number;
16
+ }
17
+ export declare function withScopeLock<T>(dir: string, fn: () => Promise<T> | T, options?: LockOptions): Promise<T>;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * 每作用域一把锁(docs/wave6-plan.md §3.2)。[W6-M]
3
+ *
4
+ * 进程内按目录串行(`tools/file-mutex.ts`);跨进程用作用域目录里的 `.lock`(`O_EXCL` 创建,写 pid)。
5
+ * 等锁至多 `timeoutMs`(缺省 5 s);锁文件超过 `staleMs`(缺省 30 s)视为陈旧,删掉重试。
6
+ * 写操作(create / str_replace / delete 与索引重建)都在锁内;读不加锁(原子 rename 保证读到整文件)。
7
+ */
8
+ import { closeSync, mkdirSync, openSync, rmSync, statSync, writeSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { withFileMutex } from "../tools/file-mutex.js";
11
+ export const LOCK_FILE = ".lock";
12
+ export class MemoryLockError extends Error {
13
+ code = "locked";
14
+ }
15
+ function stale(path, staleMs, now) {
16
+ try {
17
+ return now - statSync(path).mtimeMs > staleMs;
18
+ }
19
+ catch {
20
+ return true;
21
+ }
22
+ }
23
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
24
+ export async function withScopeLock(dir, fn, options = {}) {
25
+ const timeoutMs = options.timeoutMs ?? 5_000;
26
+ const staleMs = options.staleMs ?? 30_000;
27
+ const now = options.now ?? Date.now;
28
+ const lock = join(dir, LOCK_FILE);
29
+ return withFileMutex(lock, async () => {
30
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
31
+ const start = now();
32
+ for (;;) {
33
+ try {
34
+ const fd = openSync(lock, "wx", 0o600);
35
+ try {
36
+ writeSync(fd, String(process.pid));
37
+ }
38
+ finally {
39
+ closeSync(fd);
40
+ }
41
+ break;
42
+ }
43
+ catch (error) {
44
+ if (error.code !== "EEXIST")
45
+ throw error;
46
+ if (stale(lock, staleMs, now())) {
47
+ rmSync(lock, { force: true });
48
+ continue;
49
+ }
50
+ if (now() - start > timeoutMs)
51
+ throw new MemoryLockError("memory is locked by another process; try again");
52
+ await sleep(25);
53
+ }
54
+ }
55
+ try {
56
+ return await fn();
57
+ }
58
+ finally {
59
+ rmSync(lock, { force: true });
60
+ }
61
+ });
62
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * 记忆的目录与逻辑路径(docs/wave6-plan.md §3.2、D9、D11)。[W6-M]
3
+ *
4
+ * - 独立终端:`<dataDir>/memory/user/` 与 `<dataDir>/memory/projects/<slug>-<sha8>/`;sha8 = 项目根真实路径的
5
+ * sha256 前 8 位。项目根:git 顶层(worktree 取主仓库,经 `commondir`);子模块取子模块自己的检出;
6
+ * 不在 git 里取 cwd。不起 git 进程。
7
+ * - 嵌入宿主:只有 `workspace` 一个作用域,映射到 profile / SDK 给的 `dir`。
8
+ * - 模型看到的是逻辑路径 `/memories/<scope>/<file>.md`。解析时拒绝 `..`、`.`、隐藏段、反斜杠、控制字符、
9
+ * 百分号编码(`%2e%2e%2f` 之类)、非 `.md` 文件;映射后的绝对路径必须仍在作用域根内;根以下任何一段是
10
+ * 符号链接都拒绝(`assertNoSymlink`,访问磁盘前调用)。
11
+ */
12
+ export type MemoryScope = "user" | "project" | "workspace";
13
+ /** 作用域的固定顺序(节、列表、CLI 输出都按它排)。 */
14
+ export declare const MEMORY_SCOPE_ORDER: readonly MemoryScope[];
15
+ export declare const MEMORY_ROOT = "/memories";
16
+ /** 每个作用域自动重建的索引文件。 */
17
+ export declare const INDEX_FILE = "MEMORY.md";
18
+ /** 项目作用域目录里记下项目根(`{ root, createdAt }`)。 */
19
+ export declare const META_FILE = "meta.json";
20
+ /** 作用域 → 绝对目录(只列启用的作用域)。 */
21
+ export type ScopeRoots = Partial<Record<MemoryScope, string>>;
22
+ /** 路径不合法(给模型的英文说明)。 */
23
+ export declare class MemoryPathError extends Error {
24
+ readonly code = "invalid_path";
25
+ }
26
+ /** 记忆按哪个目录分桶:git 顶层(worktree → 主仓库);不在 git 里取 cwd。 */
27
+ export declare function projectRootOf(cwd: string): string;
28
+ /** `<slug>-<sha8>`:slug 取目录名(只留 `[A-Za-z0-9._-]`,至多 40 个字符)。 */
29
+ export declare function projectBucket(root: string): string;
30
+ export declare function memoryBaseDir(dataDir: string): string;
31
+ /** 独立终端的作用域目录(不创建;首次写入时才建)。 */
32
+ export declare function standaloneRoots(input: {
33
+ dataDir: string;
34
+ /** 项目根(`projectRootOf(cwd)`);undefined = 不启用项目作用域。 */
35
+ projectRoot?: string | undefined;
36
+ user: boolean;
37
+ }): ScopeRoots;
38
+ export declare function logicalPath(scope: MemoryScope, file?: string): string;
39
+ export type MemoryTarget = {
40
+ kind: "root";
41
+ } | {
42
+ kind: "dir";
43
+ scope: MemoryScope;
44
+ root: string;
45
+ file: string;
46
+ abs: string;
47
+ } | {
48
+ kind: "file";
49
+ scope: MemoryScope;
50
+ root: string;
51
+ file: string;
52
+ abs: string;
53
+ };
54
+ /** 逻辑路径 → 目标;不合法抛 MemoryPathError。 */
55
+ export declare function parseMemoryPath(path: unknown, roots: ScopeRoots): MemoryTarget;
56
+ /** 作用域根以下(不含根)的每一段若已存在且是符号链接,拒绝。 */
57
+ export declare function assertNoSymlink(root: string, abs: string): void;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * 记忆的目录与逻辑路径(docs/wave6-plan.md §3.2、D9、D11)。[W6-M]
3
+ *
4
+ * - 独立终端:`<dataDir>/memory/user/` 与 `<dataDir>/memory/projects/<slug>-<sha8>/`;sha8 = 项目根真实路径的
5
+ * sha256 前 8 位。项目根:git 顶层(worktree 取主仓库,经 `commondir`);子模块取子模块自己的检出;
6
+ * 不在 git 里取 cwd。不起 git 进程。
7
+ * - 嵌入宿主:只有 `workspace` 一个作用域,映射到 profile / SDK 给的 `dir`。
8
+ * - 模型看到的是逻辑路径 `/memories/<scope>/<file>.md`。解析时拒绝 `..`、`.`、隐藏段、反斜杠、控制字符、
9
+ * 百分号编码(`%2e%2e%2f` 之类)、非 `.md` 文件;映射后的绝对路径必须仍在作用域根内;根以下任何一段是
10
+ * 符号链接都拒绝(`assertNoSymlink`,访问磁盘前调用)。
11
+ */
12
+ import { createHash } from "node:crypto";
13
+ import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
14
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
15
+ /** 作用域的固定顺序(节、列表、CLI 输出都按它排)。 */
16
+ export const MEMORY_SCOPE_ORDER = ["user", "project", "workspace"];
17
+ export const MEMORY_ROOT = "/memories";
18
+ /** 每个作用域自动重建的索引文件。 */
19
+ export const INDEX_FILE = "MEMORY.md";
20
+ /** 项目作用域目录里记下项目根(`{ root, createdAt }`)。 */
21
+ export const META_FILE = "meta.json";
22
+ const MAX_PATH = 512;
23
+ const MAX_SEGMENT = 128;
24
+ /** 路径不合法(给模型的英文说明)。 */
25
+ export class MemoryPathError extends Error {
26
+ code = "invalid_path";
27
+ }
28
+ function readText(path) {
29
+ try {
30
+ return readFileSync(path, "utf8");
31
+ }
32
+ catch {
33
+ return "";
34
+ }
35
+ }
36
+ function real(path) {
37
+ try {
38
+ return realpathSync.native(path);
39
+ }
40
+ catch {
41
+ return resolve(path);
42
+ }
43
+ }
44
+ /** 记忆按哪个目录分桶:git 顶层(worktree → 主仓库);不在 git 里取 cwd。 */
45
+ export function projectRootOf(cwd) {
46
+ const start = real(cwd);
47
+ let dir = start;
48
+ for (;;) {
49
+ const dotGit = join(dir, ".git");
50
+ let kind;
51
+ try {
52
+ const info = statSync(dotGit);
53
+ kind = info.isDirectory() ? "dir" : info.isFile() ? "file" : undefined;
54
+ }
55
+ catch {
56
+ kind = undefined;
57
+ }
58
+ if (kind === "dir")
59
+ return dir;
60
+ if (kind === "file") {
61
+ const match = /^gitdir:\s*(.+?)\s*$/m.exec(readText(dotGit));
62
+ const target = match?.[1];
63
+ if (target !== undefined) {
64
+ const gitDir = isAbsolute(target) ? target : resolve(dir, target);
65
+ const common = readText(join(gitDir, "commondir")).trim();
66
+ if (common !== "") {
67
+ const commonDir = real(isAbsolute(common) ? common : resolve(gitDir, common));
68
+ return basename(commonDir) === ".git" ? dirname(commonDir) : commonDir;
69
+ }
70
+ }
71
+ return dir;
72
+ }
73
+ const parent = dirname(dir);
74
+ if (parent === dir)
75
+ return start;
76
+ dir = parent;
77
+ }
78
+ }
79
+ /** `<slug>-<sha8>`:slug 取目录名(只留 `[A-Za-z0-9._-]`,至多 40 个字符)。 */
80
+ export function projectBucket(root) {
81
+ const slug = basename(root)
82
+ .replace(/[^A-Za-z0-9._-]+/g, "-")
83
+ .replace(/^[-.]+/, "")
84
+ .slice(0, 40)
85
+ .replace(/-+$/, "") || "root";
86
+ const hash = createHash("sha256").update(root).digest("hex").slice(0, 8);
87
+ return `${slug}-${hash}`;
88
+ }
89
+ export function memoryBaseDir(dataDir) {
90
+ return join(dataDir, "memory");
91
+ }
92
+ /** 独立终端的作用域目录(不创建;首次写入时才建)。 */
93
+ export function standaloneRoots(input) {
94
+ const base = memoryBaseDir(input.dataDir);
95
+ const roots = {};
96
+ if (input.user)
97
+ roots.user = join(base, "user");
98
+ if (input.projectRoot !== undefined)
99
+ roots.project = join(base, "projects", projectBucket(input.projectRoot));
100
+ return roots;
101
+ }
102
+ export function logicalPath(scope, file) {
103
+ return file === undefined || file === ""
104
+ ? `${MEMORY_ROOT}/${scope}`
105
+ : `${MEMORY_ROOT}/${scope}/${file}`;
106
+ }
107
+ function scopesText(roots) {
108
+ const names = MEMORY_SCOPE_ORDER.filter((s) => roots[s] !== undefined);
109
+ return names.length === 0 ? "(none)" : names.map((s) => logicalPath(s)).join(", ");
110
+ }
111
+ /** 逻辑路径 → 目标;不合法抛 MemoryPathError。 */
112
+ export function parseMemoryPath(path, roots) {
113
+ if (typeof path !== "string" || path === "")
114
+ throw new MemoryPathError("path must be a non-empty string such as /memories/user/note.md");
115
+ if (path.length > MAX_PATH)
116
+ throw new MemoryPathError("path is too long");
117
+ if (path.includes("\\"))
118
+ throw new MemoryPathError("path must use forward slashes");
119
+ if (/[\u0000-\u001f\u007f]/.test(path))
120
+ throw new MemoryPathError("path contains control characters");
121
+ if (/%[0-9a-fA-F]{2}/.test(path))
122
+ throw new MemoryPathError("path must not be URL-encoded");
123
+ const trimmed = path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
124
+ if (trimmed === MEMORY_ROOT)
125
+ return { kind: "root" };
126
+ if (!trimmed.startsWith(`${MEMORY_ROOT}/`))
127
+ throw new MemoryPathError(`path must start with ${MEMORY_ROOT}/ (scopes: ${scopesText(roots)})`);
128
+ const segments = trimmed.slice(MEMORY_ROOT.length + 1).split("/");
129
+ for (const segment of segments) {
130
+ if (segment === "" || segment === "." || segment === "..")
131
+ throw new MemoryPathError("path must not contain empty, '.' or '..' segments");
132
+ if (segment.startsWith("."))
133
+ throw new MemoryPathError("hidden files are not allowed");
134
+ if (segment.length > MAX_SEGMENT)
135
+ throw new MemoryPathError("path segment is too long");
136
+ }
137
+ const scope = segments[0];
138
+ const root = MEMORY_SCOPE_ORDER.includes(scope) ? roots[scope] : undefined;
139
+ if (root === undefined)
140
+ throw new MemoryPathError(`unknown memory scope "${segments[0]}"; use ${scopesText(roots)}`);
141
+ const rest = segments.slice(1);
142
+ const abs = rest.length === 0 ? root : join(root, ...rest);
143
+ const rel = relative(root, abs);
144
+ if (rel.startsWith("..") || isAbsolute(rel))
145
+ throw new MemoryPathError("path escapes memory");
146
+ const file = rest.join("/");
147
+ const last = rest.at(-1);
148
+ if (last !== undefined && last.toLowerCase().endsWith(".md")) {
149
+ if (!last.endsWith(".md"))
150
+ throw new MemoryPathError("memory files must end with .md");
151
+ return { kind: "file", scope, root, file, abs };
152
+ }
153
+ if (last !== undefined && /\.[A-Za-z0-9]+$/.test(last))
154
+ throw new MemoryPathError("memory files must be Markdown (.md)");
155
+ return { kind: "dir", scope, root, file, abs };
156
+ }
157
+ /** 作用域根以下(不含根)的每一段若已存在且是符号链接,拒绝。 */
158
+ export function assertNoSymlink(root, abs) {
159
+ const rel = relative(root, abs);
160
+ if (rel === "")
161
+ return;
162
+ let current = root;
163
+ for (const part of rel.split(/[\\/]/)) {
164
+ current = join(current, part);
165
+ let isLink = false;
166
+ try {
167
+ isLink = lstatSync(current).isSymbolicLink();
168
+ }
169
+ catch {
170
+ return;
171
+ }
172
+ if (isLink)
173
+ throw new MemoryPathError("symbolic links are not allowed in memory");
174
+ }
175
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 记忆的人读文本(`/memory` 行式、`ama memory`、面板共用)。[W6-M] 界面层:文案走 `msg().memory`。
3
+ */
4
+ import type { MemoryEntry } from "./index.js";
5
+ import { type MemoryScope } from "./paths.js";
6
+ import type { MemoryRuntime } from "./runtime.js";
7
+ import { type MemoryStore } from "./store.js";
8
+ export declare const STALE_DAYS = 90;
9
+ export declare function scopeLabel(scope: MemoryScope): string;
10
+ /** 条目是否超过 90 天未更新(`/memory` 标灰)。 */
11
+ export declare function isStale(entry: Pick<MemoryEntry, "updated">, now: number): boolean;
12
+ /** 存储错误 → 本地化说明;不是存储错误时原样抛出。 */
13
+ export declare function memoryErrorText(error: unknown): string;
14
+ export type FindResult = {
15
+ ok: true;
16
+ entry: MemoryEntry;
17
+ } | {
18
+ ok: false;
19
+ message: string;
20
+ };
21
+ export declare function findOne(store: MemoryStore, name: string): FindResult;
22
+ export interface ScopeSummary {
23
+ scope: MemoryScope;
24
+ entries: MemoryEntry[];
25
+ indexBytes: number;
26
+ omitted: number;
27
+ }
28
+ export declare function scopeSummaries(store: MemoryStore, scopes?: MemoryScope[]): ScopeSummary[];
29
+ /** 纯文本列表(行式 `/memory`、`ama memory list`)。 */
30
+ export declare function listText(store: MemoryStore, options?: {
31
+ scopes?: MemoryScope[];
32
+ now?: number;
33
+ notes?: string[];
34
+ }): string;
35
+ /** 运行期附加说明:项目作用域因未信任跳过、本会话禁止写入。 */
36
+ export declare function runtimeNotes(runtime: MemoryRuntime): string[];
@@ -0,0 +1,86 @@
1
+ /**
2
+ * 记忆的人读文本(`/memory` 行式、`ama memory`、面板共用)。[W6-M] 界面层:文案走 `msg().memory`。
3
+ */
4
+ import { formatBytes } from "../checkpoints/gc.js";
5
+ import { msg } from "../i18n/index.js";
6
+ import { MemoryLockError } from "./lock.js";
7
+ import { logicalPath } from "./paths.js";
8
+ import { scopeIndex } from "./section.js";
9
+ import { MemoryError } from "./store.js";
10
+ export const STALE_DAYS = 90;
11
+ export function scopeLabel(scope) {
12
+ return msg().memory.scope[scope];
13
+ }
14
+ /** 条目是否超过 90 天未更新(`/memory` 标灰)。 */
15
+ export function isStale(entry, now) {
16
+ const at = Date.parse(`${entry.updated}T00:00:00Z`);
17
+ return Number.isFinite(at) && now - at > STALE_DAYS * 86_400_000;
18
+ }
19
+ /** 存储错误 → 本地化说明;不是存储错误时原样抛出。 */
20
+ export function memoryErrorText(error) {
21
+ const m = msg().memory.error;
22
+ if (error instanceof MemoryLockError)
23
+ return m.locked;
24
+ if (!(error instanceof MemoryError))
25
+ throw error;
26
+ const d = error.detail ?? {};
27
+ switch (error.code) {
28
+ case "credential":
29
+ return m.credential(String(d["kind"] ?? "secret"));
30
+ case "too_large":
31
+ return m.tooLarge(Number(d["bytes"]), Number(d["limit"]));
32
+ case "too_many":
33
+ return m.tooMany(Number(d["limit"]));
34
+ default:
35
+ return m.other(error.message);
36
+ }
37
+ }
38
+ export function findOne(store, name) {
39
+ const m = msg().memory.command;
40
+ const found = store.find(name);
41
+ if (found.length === 0)
42
+ return { ok: false, message: m.notFound(name) };
43
+ if (found.length > 1)
44
+ return {
45
+ ok: false,
46
+ message: m.ambiguous(name, found.map((e) => `${e.scope}/${e.file}`).join(", ")),
47
+ };
48
+ return { ok: true, entry: found[0] };
49
+ }
50
+ export function scopeSummaries(store, scopes = store.scopes()) {
51
+ return scopes.map((scope) => {
52
+ const entries = store.entries(scope);
53
+ const index = scopeIndex(scope, entries, store.limits.indexMaxBytes);
54
+ return { scope, entries, indexBytes: index.bytes, omitted: index.omitted };
55
+ });
56
+ }
57
+ /** 纯文本列表(行式 `/memory`、`ama memory list`)。 */
58
+ export function listText(store, options = {}) {
59
+ const m = msg().memory.panel;
60
+ const now = options.now ?? Date.now();
61
+ const lines = [];
62
+ for (const s of scopeSummaries(store, options.scopes)) {
63
+ lines.push(m.scopeLine(`${scopeLabel(s.scope)} ${logicalPath(s.scope)}/`, s.entries.length, formatBytes(s.indexBytes), formatBytes(store.limits.indexMaxBytes)));
64
+ if (s.entries.length === 0)
65
+ lines.push(` ${m.noEntries}`);
66
+ for (const e of s.entries) {
67
+ const stale = isStale(e, now) ? ` (${m.stale})` : "";
68
+ const description = e.description === "" ? "" : ` — ${e.description}`;
69
+ lines.push(` ${e.name} [${e.file}]${description} · ${m.entryUpdated(e.updated)}${stale}`);
70
+ }
71
+ if (s.omitted > 0)
72
+ lines.push(` ${m.truncated(s.omitted)}`);
73
+ }
74
+ lines.push(...(options.notes ?? []));
75
+ return lines.join("\n");
76
+ }
77
+ /** 运行期附加说明:项目作用域因未信任跳过、本会话禁止写入。 */
78
+ export function runtimeNotes(runtime) {
79
+ const m = msg().memory.panel;
80
+ const notes = [];
81
+ if (runtime.skipped.some((s) => s.reason === "untrusted"))
82
+ notes.push(m.untrusted);
83
+ if (!runtime.writesEnabled)
84
+ notes.push(m.writesOff);
85
+ return notes;
86
+ }