@armadra/agent 0.2.1 → 0.4.0

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 (232) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +142 -49
  3. package/dist/agent/retry.d.ts +1 -1
  4. package/dist/agent/retry.js +2 -1
  5. package/dist/agent/session-cache.js +6 -3
  6. package/dist/agent/session-classifier.d.ts +18 -0
  7. package/dist/agent/session-classifier.js +103 -0
  8. package/dist/agent/session-core.d.ts +13 -0
  9. package/dist/agent/session-state.js +2 -1
  10. package/dist/agent/session-sync.js +9 -2
  11. package/dist/agent/session-tools.js +24 -3
  12. package/dist/agent/session.d.ts +3 -0
  13. package/dist/agent/session.js +20 -10
  14. package/dist/agent/tool-runner.js +15 -2
  15. package/dist/agent/types.d.ts +9 -1
  16. package/dist/ai/apis/anthropic-messages.js +3 -2
  17. package/dist/ai/apis/google-generative-ai.js +3 -2
  18. package/dist/ai/apis/openai-completions.js +3 -2
  19. package/dist/ai/apis/openai-responses.js +5 -3
  20. package/dist/ai/cache/fingerprint.d.ts +1 -1
  21. package/dist/ai/cache/fingerprint.js +1 -1
  22. package/dist/ai/cache/reporting.js +2 -1
  23. package/dist/ai/http.d.ts +28 -6
  24. package/dist/ai/http.js +41 -8
  25. package/dist/ai/providers/channels.d.ts +51 -0
  26. package/dist/ai/providers/channels.js +96 -0
  27. package/dist/ai/providers/enrich.d.ts +34 -0
  28. package/dist/ai/providers/enrich.js +86 -0
  29. package/dist/ai/providers/models-dev-cache.d.ts +53 -0
  30. package/dist/ai/providers/models-dev-cache.js +147 -0
  31. package/dist/ai/providers/models-dev.d.ts +99 -0
  32. package/dist/ai/providers/models-dev.js +315 -0
  33. package/dist/ai/providers/registry.d.ts +42 -4
  34. package/dist/ai/providers/registry.js +195 -34
  35. package/dist/ai/providers/suggest.d.ts +18 -0
  36. package/dist/ai/providers/suggest.js +72 -0
  37. package/dist/ai/sse.d.ts +6 -2
  38. package/dist/ai/sse.js +22 -2
  39. package/dist/ai/types.d.ts +30 -3
  40. package/dist/bundle/ama.cjs +17893 -10698
  41. package/dist/cli/args.d.ts +23 -5
  42. package/dist/cli/args.js +113 -13
  43. package/dist/cli/bootstrap.js +28 -3
  44. package/dist/cli/codemode-notice.d.ts +20 -0
  45. package/dist/cli/codemode-notice.js +55 -0
  46. package/dist/cli/compose-providers.js +3 -0
  47. package/dist/cli/compose-session.d.ts +8 -0
  48. package/dist/cli/compose-session.js +32 -1
  49. package/dist/cli/compose-store.d.ts +1 -1
  50. package/dist/cli/compose-store.js +3 -1
  51. package/dist/cli/compose.d.ts +17 -5
  52. package/dist/cli/compose.js +53 -19
  53. package/dist/cli/default-model.d.ts +38 -1
  54. package/dist/cli/default-model.js +96 -9
  55. package/dist/cli/deps.d.ts +38 -0
  56. package/dist/cli/exit-codes.d.ts +2 -0
  57. package/dist/cli/exit-codes.js +3 -0
  58. package/dist/cli/fake-visibility.d.ts +13 -0
  59. package/dist/cli/fake-visibility.js +26 -0
  60. package/dist/cli/from-prompt.d.ts +18 -0
  61. package/dist/cli/from-prompt.js +49 -0
  62. package/dist/cli/main.d.ts +10 -2
  63. package/dist/cli/main.js +88 -2
  64. package/dist/cli/proxy.d.ts +51 -0
  65. package/dist/cli/proxy.js +135 -0
  66. package/dist/cli/startup-screen.d.ts +29 -0
  67. package/dist/cli/startup-screen.js +47 -0
  68. package/dist/cli/startup-steps.d.ts +1 -1
  69. package/dist/cli/startup-steps.js +20 -13
  70. package/dist/cli/subcommands/config.d.ts +29 -4
  71. package/dist/cli/subcommands/config.js +188 -25
  72. package/dist/cli/subcommands/context.js +5 -1
  73. package/dist/cli/subcommands/doctor.js +12 -1
  74. package/dist/cli/subcommands/init.d.ts +6 -0
  75. package/dist/cli/subcommands/init.js +23 -0
  76. package/dist/cli/subcommands/model-meta.d.ts +16 -0
  77. package/dist/cli/subcommands/model-meta.js +54 -0
  78. package/dist/cli/subcommands/models-cache-probe.js +1 -1
  79. package/dist/cli/subcommands/models-discover.d.ts +18 -6
  80. package/dist/cli/subcommands/models-discover.js +87 -52
  81. package/dist/cli/subcommands/models.js +26 -18
  82. package/dist/cli/subcommands/probe-runner.d.ts +96 -0
  83. package/dist/cli/subcommands/probe-runner.js +264 -0
  84. package/dist/cli/subcommands/providers-list.d.ts +8 -0
  85. package/dist/cli/subcommands/providers-list.js +97 -0
  86. package/dist/cli/subcommands/providers-plan.d.ts +79 -0
  87. package/dist/cli/subcommands/providers-plan.js +215 -0
  88. package/dist/cli/subcommands/providers-probe.d.ts +34 -0
  89. package/dist/cli/subcommands/providers-probe.js +87 -0
  90. package/dist/cli/subcommands/providers.d.ts +28 -0
  91. package/dist/cli/subcommands/providers.js +436 -0
  92. package/dist/cli/subcommands/sessions-export.d.ts +10 -0
  93. package/dist/cli/subcommands/sessions-export.js +59 -0
  94. package/dist/cli/subcommands/sessions-search.d.ts +13 -0
  95. package/dist/cli/subcommands/sessions-search.js +103 -0
  96. package/dist/cli/subcommands/sessions.d.ts +3 -2
  97. package/dist/cli/subcommands/sessions.js +22 -1
  98. package/dist/cli/subcommands/stats.d.ts +17 -0
  99. package/dist/cli/subcommands/stats.js +198 -0
  100. package/dist/cli/system-prompt-arg.d.ts +11 -0
  101. package/dist/cli/system-prompt-arg.js +34 -0
  102. package/dist/codemode/modes.d.ts +4 -11
  103. package/dist/codemode/modes.js +5 -27
  104. package/dist/codemode/tool.d.ts +18 -14
  105. package/dist/codemode/tool.js +71 -28
  106. package/dist/config/init.d.ts +34 -0
  107. package/dist/config/init.js +99 -0
  108. package/dist/config/json-schema.d.ts +15 -0
  109. package/dist/config/json-schema.js +215 -0
  110. package/dist/config/key-docs.d.ts +22 -0
  111. package/dist/config/key-docs.js +105 -0
  112. package/dist/config/merge.d.ts +9 -8
  113. package/dist/config/merge.js +22 -7
  114. package/dist/config/schema.d.ts +1 -1
  115. package/dist/config/schema.js +97 -15
  116. package/dist/config/types.d.ts +57 -11
  117. package/dist/config/types.js +19 -1
  118. package/dist/modes/commands-core.js +6 -5
  119. package/dist/modes/image-input.d.ts +27 -0
  120. package/dist/modes/image-input.js +78 -0
  121. package/dist/modes/interactive/approval-dialog.d.ts +21 -5
  122. package/dist/modes/interactive/approval-dialog.js +106 -27
  123. package/dist/modes/interactive/commands.d.ts +11 -2
  124. package/dist/modes/interactive/commands.js +58 -19
  125. package/dist/modes/interactive/interactive-mode.d.ts +2 -1
  126. package/dist/modes/interactive/interactive-mode.js +70 -82
  127. package/dist/modes/interactive/key-dispatch.d.ts +3 -1
  128. package/dist/modes/interactive/key-dispatch.js +5 -6
  129. package/dist/modes/interactive/line/line-mode.d.ts +1 -0
  130. package/dist/modes/interactive/line/line-mode.js +9 -5
  131. package/dist/modes/interactive/line/line-render.d.ts +4 -0
  132. package/dist/modes/interactive/line/line-render.js +28 -6
  133. package/dist/modes/interactive/message-view.d.ts +48 -9
  134. package/dist/modes/interactive/message-view.js +238 -44
  135. package/dist/modes/interactive/panels.d.ts +18 -0
  136. package/dist/modes/interactive/panels.js +143 -0
  137. package/dist/modes/interactive/pickers.d.ts +23 -2
  138. package/dist/modes/interactive/pickers.js +48 -15
  139. package/dist/modes/interactive/run-indicator.d.ts +51 -0
  140. package/dist/modes/interactive/run-indicator.js +189 -0
  141. package/dist/modes/interactive/startup-header.d.ts +40 -0
  142. package/dist/modes/interactive/startup-header.js +169 -0
  143. package/dist/modes/interactive/startup-ui.d.ts +7 -2
  144. package/dist/modes/interactive/startup-ui.js +41 -11
  145. package/dist/modes/interactive/status-bar.d.ts +18 -15
  146. package/dist/modes/interactive/status-bar.js +98 -56
  147. package/dist/modes/interactive/tool-summary.d.ts +46 -0
  148. package/dist/modes/interactive/tool-summary.js +218 -0
  149. package/dist/modes/interactive/tool-view.d.ts +48 -15
  150. package/dist/modes/interactive/tool-view.js +203 -145
  151. package/dist/modes/print/print-mode.d.ts +33 -4
  152. package/dist/modes/print/print-mode.js +128 -8
  153. package/dist/modes/rpc/commands.js +12 -2
  154. package/dist/permissions/auto-safe.d.ts +60 -0
  155. package/dist/permissions/auto-safe.js +529 -0
  156. package/dist/permissions/classifier.d.ts +64 -0
  157. package/dist/permissions/classifier.js +184 -0
  158. package/dist/permissions/dangerous.d.ts +5 -0
  159. package/dist/permissions/dangerous.js +1 -1
  160. package/dist/permissions/modes.d.ts +30 -0
  161. package/dist/permissions/modes.js +78 -0
  162. package/dist/permissions/pipeline.d.ts +31 -4
  163. package/dist/permissions/pipeline.js +196 -6
  164. package/dist/permissions/protected.d.ts +19 -0
  165. package/dist/permissions/protected.js +74 -0
  166. package/dist/permissions/rules.js +3 -0
  167. package/dist/permissions/types.d.ts +50 -3
  168. package/dist/permissions/types.js +3 -0
  169. package/dist/rpc.d.ts +2 -0
  170. package/dist/sdk.d.ts +9 -3
  171. package/dist/sdk.js +10 -2
  172. package/dist/session/export.d.ts +32 -0
  173. package/dist/session/export.js +187 -0
  174. package/dist/session/projection.js +5 -1
  175. package/dist/session/redact.d.ts +15 -0
  176. package/dist/session/redact.js +55 -0
  177. package/dist/session/reuse.d.ts +33 -0
  178. package/dist/session/reuse.js +86 -0
  179. package/dist/session/scan.d.ts +34 -0
  180. package/dist/session/scan.js +140 -0
  181. package/dist/session/search.d.ts +52 -0
  182. package/dist/session/search.js +211 -0
  183. package/dist/session/stats-aggregate.d.ts +63 -0
  184. package/dist/session/stats-aggregate.js +163 -0
  185. package/dist/session/stats-index.d.ts +26 -0
  186. package/dist/session/stats-index.js +91 -0
  187. package/dist/session/stats-scan.d.ts +54 -0
  188. package/dist/session/stats-scan.js +236 -0
  189. package/dist/session/types.d.ts +2 -0
  190. package/dist/tools/image-file.d.ts +31 -0
  191. package/dist/tools/image-file.js +114 -0
  192. package/dist/tools/presets.d.ts +35 -8
  193. package/dist/tools/presets.js +56 -17
  194. package/dist/tools/read.d.ts +4 -8
  195. package/dist/tools/read.js +15 -64
  196. package/dist/tui/component.d.ts +8 -2
  197. package/dist/tui/component.js +3 -1
  198. package/dist/tui/components/box.d.ts +6 -1
  199. package/dist/tui/components/box.js +16 -6
  200. package/dist/tui/components/card.d.ts +23 -0
  201. package/dist/tui/components/card.js +37 -0
  202. package/dist/tui/components/editor-history.d.ts +6 -0
  203. package/dist/tui/components/editor-history.js +45 -0
  204. package/dist/tui/components/editor-paste.d.ts +1 -1
  205. package/dist/tui/components/editor-paste.js +4 -4
  206. package/dist/tui/components/editor.d.ts +11 -5
  207. package/dist/tui/components/editor.js +52 -58
  208. package/dist/tui/components/key-value.d.ts +3 -0
  209. package/dist/tui/components/key-value.js +16 -6
  210. package/dist/tui/components/loader.d.ts +37 -7
  211. package/dist/tui/components/loader.js +84 -21
  212. package/dist/tui/components/markdown.d.ts +5 -1
  213. package/dist/tui/components/markdown.js +45 -21
  214. package/dist/tui/components/meter.d.ts +3 -3
  215. package/dist/tui/components/meter.js +13 -11
  216. package/dist/tui/components/select-list.d.ts +23 -1
  217. package/dist/tui/components/select-list.js +76 -13
  218. package/dist/tui/glyphs.d.ts +68 -0
  219. package/dist/tui/glyphs.js +114 -0
  220. package/dist/tui/theme.d.ts +30 -6
  221. package/dist/tui/theme.js +103 -17
  222. package/dist/tui.d.ts +4 -2
  223. package/dist/tui.js +3 -1
  224. package/docs/codemode.md +23 -9
  225. package/docs/hooks.md +10 -10
  226. package/docs/permissions.md +148 -0
  227. package/docs/providers.md +241 -3
  228. package/docs/rpc.md +38 -38
  229. package/docs/session-format.md +3 -2
  230. package/docs/sessions.md +134 -0
  231. package/docs/tui.md +140 -61
  232. package/package.json +3 -1
package/dist/tui/theme.js CHANGED
@@ -1,15 +1,20 @@
1
1
  /**
2
2
  * 主题与颜色能力(设计 §12.7)。[B4]
3
3
  *
4
- * - `dark` / `light` 两套语义色(§12.7 的 11 个名字),以 24 位 RGB 定义。
5
- * - 能力降级:truecolor → 256 色(6×6×6 立方 + 灰阶取近)→ 16 色(xterm 缺省调色板取近)→ 无色。
4
+ * - `dark` / `light` 两套语义色(14 个名字,终端界面视觉设计 v1 §2.1),以 24 位 RGB 定义;取值都落在
5
+ * xterm 256 色立方 / 灰阶上,所以 256 色回退无损。
6
+ * - 能力降级:truecolor → 256 色(6×6×6 立方 + 灰阶取近)→ 16 色(内置色板按 `THEME_ANSI16` 查表,
7
+ * 覆盖色按 xterm 缺省调色板取近)→ 无色。
8
+ * - `bg("selection")` 在 < 256 色时退化为 `accent` 粗体前景(16 色下底色太跳,且常留「尾巴」)。
6
9
  * - `NO_COLOR`(非空)或 `TERM=dumb` → 无色;不探测终端背景(不发查询序列)。
7
10
  * - 样式用各自的关闭码(39 / 49 / 22 / 23 / 24)而不是全重置,可以嵌套。
8
11
  */
12
+ import { detectAscii, glyphsFor } from "./glyphs.js";
9
13
  export const THEME_PALETTES = {
10
14
  dark: {
11
15
  text: "#e4e4e4",
12
- dim: "#808080",
16
+ muted: "#a8a8a8",
17
+ dim: "#6c6c6c",
13
18
  accent: "#5fafff",
14
19
  success: "#5fd787",
15
20
  warning: "#ffd75f",
@@ -17,12 +22,15 @@ export const THEME_PALETTES = {
17
22
  user: "#87afff",
18
23
  assistant: "#e4e4e4",
19
24
  tool: "#af87ff",
20
- border: "#5f5f5f",
25
+ border: "#585858",
21
26
  code: "#ffaf5f",
27
+ link: "#87d7ff",
28
+ selection: "#303030",
22
29
  },
23
30
  light: {
24
31
  text: "#1c1c1c",
25
- dim: "#6c6c6c",
32
+ muted: "#585858",
33
+ dim: "#8a8a8a",
26
34
  accent: "#005fd7",
27
35
  success: "#008700",
28
36
  warning: "#af5f00",
@@ -30,10 +38,73 @@ export const THEME_PALETTES = {
30
38
  user: "#005faf",
31
39
  assistant: "#1c1c1c",
32
40
  tool: "#8700af",
33
- border: "#a8a8a8",
41
+ border: "#bcbcbc",
34
42
  code: "#875f00",
43
+ link: "#0087af",
44
+ selection: "#e4e4e4",
45
+ },
46
+ };
47
+ /**
48
+ * 16 色下的索引(xterm 0–15)。距离取近对饱和色不可靠(`#ff5f5f` 会落到灰 8),所以内置色板查表;
49
+ * `selection` 在 16 色下不作 bg,表里给前景兜底值。
50
+ */
51
+ export const THEME_ANSI16 = {
52
+ dark: {
53
+ text: 7,
54
+ muted: 7,
55
+ dim: 8,
56
+ accent: 12,
57
+ success: 10,
58
+ warning: 11,
59
+ error: 9,
60
+ user: 12,
61
+ assistant: 7,
62
+ tool: 13,
63
+ border: 8,
64
+ code: 11,
65
+ link: 14,
66
+ selection: 8,
67
+ },
68
+ light: {
69
+ text: 0,
70
+ muted: 8,
71
+ dim: 8,
72
+ accent: 4,
73
+ success: 2,
74
+ warning: 3,
75
+ error: 1,
76
+ user: 4,
77
+ assistant: 0,
78
+ tool: 5,
79
+ border: 7,
80
+ code: 3,
81
+ link: 6,
82
+ selection: 7,
35
83
  },
36
84
  };
85
+ /** 阈值档位:≥ dangerAt error,≥ warnAt warning,否则 success(状态栏 ctx、Meter、/session 共用)。 */
86
+ export function levelColor(ratio, thresholds = {}) {
87
+ if (ratio >= (thresholds.dangerAt ?? 0.9))
88
+ return "error";
89
+ if (ratio >= (thresholds.warnAt ?? 0.7))
90
+ return "warning";
91
+ return "success";
92
+ }
93
+ /**
94
+ * `auto` → dark / light:不发查询序列,只看环境。`COLORFGBG` 的最后一段是背景色号(0–6、8 暗,
95
+ * 7、9–15 亮);没有时 Apple Terminal 按 dark;其余缺省 dark。只是猜,文档建议显式配置。
96
+ */
97
+ export function resolveThemeName(name, env = process.env) {
98
+ if (name === "dark" || name === "light")
99
+ return name;
100
+ const colorfgbg = env["COLORFGBG"];
101
+ if (colorfgbg !== undefined && colorfgbg !== "") {
102
+ const bg = Number(colorfgbg.split(";").at(-1));
103
+ if (Number.isInteger(bg) && bg >= 0 && bg <= 15)
104
+ return bg === 7 || bg >= 9 ? "light" : "dark";
105
+ }
106
+ return "dark";
107
+ }
37
108
  /** 由环境推断颜色深度。 */
38
109
  export function detectColorDepth(env = process.env, isTTY = process.stdout.isTTY === true) {
39
110
  if (env["NO_COLOR"] !== undefined && env["NO_COLOR"] !== "")
@@ -116,8 +187,10 @@ export function rgbTo16(rgb) {
116
187
  }
117
188
  return best;
118
189
  }
119
- /** 生成前景 / 背景开启序列;depth 0 返回空串。 */
120
- export function colorCode(hex, depth, background) {
190
+ /**
191
+ * 生成前景 / 背景开启序列;depth 0 返回空串。`ansi16` 给定时 16 色下直接用它(内置色板查表)。
192
+ */
193
+ export function colorCode(hex, depth, background, ansi16) {
121
194
  if (depth === 0)
122
195
  return "";
123
196
  const rgb = parseHex(hex);
@@ -125,23 +198,25 @@ export function colorCode(hex, depth, background) {
125
198
  return `\x1b[${background ? 48 : 38};2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
126
199
  if (depth === 256)
127
200
  return `\x1b[${background ? 48 : 38};5;${rgbTo256(rgb)}m`;
128
- const idx = rgbTo16(rgb);
201
+ const idx = ansi16 ?? rgbTo16(rgb);
129
202
  const base = idx < 8 ? (background ? 40 : 30) + idx : (background ? 100 : 90) + idx - 8;
130
203
  return `\x1b[${base}m`;
131
204
  }
132
205
  class PaletteTheme {
133
206
  name;
134
207
  caps;
208
+ glyphs;
135
209
  fgCodes;
136
210
  bgCodes;
137
- constructor(name, palette, caps) {
211
+ constructor(name, palette, caps, glyphs, ansi16 = {}) {
138
212
  this.name = name;
139
213
  this.caps = caps;
214
+ this.glyphs = glyphs;
140
215
  const fg = {};
141
216
  const bg = {};
142
217
  for (const key of Object.keys(palette)) {
143
- fg[key] = colorCode(palette[key], caps.colors, false);
144
- bg[key] = colorCode(palette[key], caps.colors, true);
218
+ fg[key] = colorCode(palette[key], caps.colors, false, ansi16[key]);
219
+ bg[key] = colorCode(palette[key], caps.colors, true, ansi16[key]);
145
220
  }
146
221
  this.fgCodes = fg;
147
222
  this.bgCodes = bg;
@@ -151,6 +226,8 @@ class PaletteTheme {
151
226
  return code === "" ? text : `${code}${text}\x1b[39m`;
152
227
  }
153
228
  bg(color, text) {
229
+ if (color === "selection" && this.caps.colors < 256)
230
+ return this.bold(this.fg("accent", text));
154
231
  const code = this.bgCodes[color];
155
232
  return code === "" ? text : `${code}${text}\x1b[49m`;
156
233
  }
@@ -168,16 +245,25 @@ class PaletteTheme {
168
245
  }
169
246
  }
170
247
  export function createTheme(name = "dark", options = {}) {
171
- const palette = { ...THEME_PALETTES[name], ...options.overrides };
172
- return new PaletteTheme(name, palette, options.caps ?? detectCapabilities());
248
+ const overrides = options.overrides ?? {};
249
+ const palette = { ...THEME_PALETTES[name], ...overrides };
250
+ const ansi16 = { ...THEME_ANSI16[name] };
251
+ for (const key of Object.keys(overrides))
252
+ delete ansi16[key];
253
+ const glyphs = glyphsFor(options.ascii ?? detectAscii());
254
+ return new PaletteTheme(name, palette, options.caps ?? detectCapabilities(), glyphs, ansi16);
173
255
  }
174
- /** 无色主题(测试、line 模式、NO_COLOR):所有方法原样返回文本。 */
175
- export function plainTheme() {
176
- return new PlainTheme();
256
+ /** 无色主题(测试、line 模式、NO_COLOR):所有方法原样返回文本;字形缺省 Unicode。 */
257
+ export function plainTheme(options = {}) {
258
+ return new PlainTheme(glyphsFor(options.ascii === true));
177
259
  }
178
260
  class PlainTheme {
261
+ glyphs;
179
262
  name = "plain";
180
263
  caps = { colors: 0 };
264
+ constructor(glyphs) {
265
+ this.glyphs = glyphs;
266
+ }
181
267
  fg(_color, text) {
182
268
  return text;
183
269
  }
package/dist/tui.d.ts CHANGED
@@ -13,7 +13,8 @@ export { VirtualScreen, type ScreenModes } from "./tui/vt-screen.js";
13
13
  export { StdinBuffer, defaultEscTimeout, type StdinBufferOptions } from "./tui/stdin-buffer.js";
14
14
  export { parseKey, matchesKey, normalizeKeyId, isPasteData, unwrapPaste, isPrintableText, PASTE_START, PASTE_END, type KeyEvent, } from "./tui/keys.js";
15
15
  export { visibleWidth, truncateToWidth, sliceByColumn, wrapTextWithAnsi, padToWidth, stripAnsi, codePointWidth, graphemeWidth, SGR_RESET, } from "./tui/ansi.js";
16
- export { createTheme, plainTheme, detectCapabilities, detectColorDepth, colorCode, rgbTo256, rgbTo16, THEME_PALETTES, type ThemeName, type CreateThemeOptions, } from "./tui/theme.js";
16
+ export { createTheme, plainTheme, detectCapabilities, detectColorDepth, colorCode, rgbTo256, rgbTo16, THEME_PALETTES, THEME_ANSI16, resolveThemeName, levelColor, type ThemeName, type CreateThemeOptions, } from "./tui/theme.js";
17
+ export { UNICODE_GLYPHS, ASCII_GLYPHS, detectAscii, resolveAscii, glyphsFor, } from "./tui/glyphs.js";
17
18
  export { Keybindings, defaultKeybindings, DEFAULT_KEYBINDINGS, parseKeybindings, loadKeybindingsFile, isActionId, type ActionId, type KeybindingOverrides, type ParsedKeybindings, } from "./tui/keybindings.js";
18
19
  export { Container } from "./tui/components/container.js";
19
20
  export { Text, TruncatedText, type TextOptions } from "./tui/components/text.js";
@@ -23,8 +24,9 @@ export { Editor, loadHistoryFile, type EditorOptions, type AutocompleteProvider,
23
24
  export { PasteStore, shouldCollapse, formatMarker, PASTE_LINE_THRESHOLD, PASTE_CHAR_THRESHOLD, } from "./tui/components/editor-paste.js";
24
25
  export { SelectList, filterItems, type SelectItem, type SelectListOptions, } from "./tui/components/select-list.js";
25
26
  export { Box, type BoxOptions } from "./tui/components/box.js";
27
+ export { Card, type CardOptions } from "./tui/components/card.js";
26
28
  export { Spacer } from "./tui/components/spacer.js";
27
- export { Loader, LOADER_FRAMES, formatElapsed, type LoaderOptions, } from "./tui/components/loader.js";
29
+ export { Loader, LOADER_FRAMES, formatElapsed, type LoaderOptions, type LoaderVerbOptions, } from "./tui/components/loader.js";
28
30
  export { compositeOverlays, type OverlayAnchor, type OverlayOptions, type OverlayLayer, } from "./tui/components/overlay.js";
29
31
  export { KeyValue, type KeyValueRow, type KeyValueOptions } from "./tui/components/key-value.js";
30
32
  export { Meter, METER_FULL, METER_EMPTY, type MeterOptions } from "./tui/components/meter.js";
package/dist/tui.js CHANGED
@@ -13,7 +13,8 @@ export { VirtualScreen } from "./tui/vt-screen.js";
13
13
  export { StdinBuffer, defaultEscTimeout } from "./tui/stdin-buffer.js";
14
14
  export { parseKey, matchesKey, normalizeKeyId, isPasteData, unwrapPaste, isPrintableText, PASTE_START, PASTE_END, } from "./tui/keys.js";
15
15
  export { visibleWidth, truncateToWidth, sliceByColumn, wrapTextWithAnsi, padToWidth, stripAnsi, codePointWidth, graphemeWidth, SGR_RESET, } from "./tui/ansi.js";
16
- export { createTheme, plainTheme, detectCapabilities, detectColorDepth, colorCode, rgbTo256, rgbTo16, THEME_PALETTES, } from "./tui/theme.js";
16
+ export { createTheme, plainTheme, detectCapabilities, detectColorDepth, colorCode, rgbTo256, rgbTo16, THEME_PALETTES, THEME_ANSI16, resolveThemeName, levelColor, } from "./tui/theme.js";
17
+ export { UNICODE_GLYPHS, ASCII_GLYPHS, detectAscii, resolveAscii, glyphsFor, } from "./tui/glyphs.js";
17
18
  export { Keybindings, defaultKeybindings, DEFAULT_KEYBINDINGS, parseKeybindings, loadKeybindingsFile, isActionId, } from "./tui/keybindings.js";
18
19
  export { Container } from "./tui/components/container.js";
19
20
  export { Text, TruncatedText } from "./tui/components/text.js";
@@ -23,6 +24,7 @@ export { Editor, loadHistoryFile, } from "./tui/components/editor.js";
23
24
  export { PasteStore, shouldCollapse, formatMarker, PASTE_LINE_THRESHOLD, PASTE_CHAR_THRESHOLD, } from "./tui/components/editor-paste.js";
24
25
  export { SelectList, filterItems, } from "./tui/components/select-list.js";
25
26
  export { Box } from "./tui/components/box.js";
27
+ export { Card } from "./tui/components/card.js";
26
28
  export { Spacer } from "./tui/components/spacer.js";
27
29
  export { Loader, LOADER_FRAMES, formatElapsed, } from "./tui/components/loader.js";
28
30
  export { compositeOverlays, } from "./tui/components/overlay.js";
package/docs/codemode.md CHANGED
@@ -2,23 +2,37 @@
2
2
 
3
3
  设计依据见 [design.md](design.md) §5.5、§5.6、§9.1。
4
4
 
5
- `codemode` 工具让模型写一段 JavaScript,在脚本里经 `tools.*` 编排多次工具调用,只有脚本输出回到模型。长流程、工具密集的任务里,它把多次往返合成一次,减少往返次数与缓存读取;短任务收益不明显,因此是可选的调用方式。
5
+ `codemode` 工具让模型写一段 JavaScript,在脚本里经 `tools.*` 编排多次工具调用,只有脚本输出回到模型。长流程、工具密集的任务里,它把多次往返合成一次,减少往返次数与缓存读取;短任务里模型照常直接调用工具,codemode 只多占约 400 token 前缀,所以 `default` 预设在网络隔离的运行时上缺省开着它。
6
6
 
7
7
  ## 打开
8
8
 
9
- | 写法 | 模型看到的工具 |
10
- | ------------------------------------------- | ----------------------------------------------------------------------------------- |
11
- | `--tools-preset codemode`(`only`) | 只有 `codemode`;全部内置工具与宿主工具只能在脚本里调用,声明列在 `codemode` 描述里 |
12
- | `--codemode on` / `codemode.mode: "on"` | 预设的工具 + `codemode`;其它工具描述末尾加一行提示 |
13
- | `--codemode off` / 缺省(非 codemode 预设) | 不注册 `codemode` |
9
+ | 写法 | 模型看到的工具 |
10
+ | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
11
+ | `--tools-preset codemode-only`(`only`;旧名 `codemode` 仍可用) | 只有 `codemode`;全部内置工具与宿主工具只能在脚本里调用,声明列在 `codemode` 描述里 |
12
+ | `--codemode on` / `codemode.mode: "on"` | 预设的工具 + `codemode`;其它工具描述不变,`codemode` 描述一行列出可在脚本里调用的直接工具(参数相同)与仅脚本可调的工具名 |
13
+ | `--codemode off` / `codemode.mode: "off"` | 不注册 `codemode` |
14
+
15
+ `codemode.mode` 不写时**跟随预设**:
16
+
17
+ | 预设 | Node ≥ 25(沙箱隔离网络) | Node 22 / 24 |
18
+ | ------------------------- | ------------------------- | ---------------------------------------------------------------------- |
19
+ | `default` | `on` | `off`,启动时提示一次(每个配置目录一次,记在数据目录 `notices.json`) |
20
+ | `codemode-only` | `only` | `only`(`execute` 类,见下) |
21
+ | `minimal` / `coordinator` | `off` | `off` |
22
+
23
+ 缺省配置里不写 `codemode.mode`(`ama init` 生成的 `config.json` 也不写),所以这张映射以后调整时老用户同样生效。`ama config show` / `ama doctor` 显示生效模式与原因(跟随哪个预设、Node 是否隔离网络)。
24
+
25
+ `coordinator` 预设即使显式 `on`,脚本里能调用的工具也只限它的活动集(read 与宿主工具):`tools.bash`、`tools.write` 在脚本里同样不存在,协调者「不写文件、不跑 bash」的约定不能经 codemode 绕过。
14
26
 
15
27
  `codemode` 本身的权限类随沙箱能力:网络隔离(Node ≥ 25,见下文沙箱)时是 `read` 类,`default` 权限模式下免审批——脚本只能经 `tools.*` 做事,每次内层调用仍逐个经过权限管线;网络未隔离(Node 22 / 24)时是 `execute` 类,`default` 模式下每次都要审批,`-p` 等无人值守场景直接拒绝,此时常用做法是在配置里放行它:
16
28
 
17
29
  ```json
18
- { "version": 1, "tools": { "preset": "codemode" }, "permission": { "allow": ["codemode"] } }
30
+ { "version": 1, "tools": { "preset": "codemode-only" }, "permission": { "allow": ["codemode"] } }
19
31
  ```
20
32
 
21
- 其它配置:`codemode.inlineBudget`(描述里内联声明的预算,估算 token,缺省 3000,超出只列名字)、`codemode.requireStrict`(见下文沙箱)。项目级配置只能把 `codemode.mode` 设为 `off`。
33
+ 其它配置:`codemode.inlineBudget`(`only` 模式在描述里内联声明的预算,估算 token,缺省 3000,超出只列名字;`on` 模式不内联)、`codemode.requireStrict`(见下文沙箱)。项目级配置只能把 `codemode.mode` 设为 `off`。
34
+
35
+ `on` 模式的前缀开销:去重前 `codemode` 描述把已直接暴露的六个工具的声明又内联一遍,其它工具各追加一行提示,系统提示 + 工具表比 `off` 多约 1356 token;现在只多约 390 token(字符 / 4 估算,测试锁定 ≤ 500)。升级后续接的旧会话因为描述字节变化会有一次缓存未命中。
22
36
 
23
37
  ## 脚本
24
38
 
@@ -82,7 +96,7 @@
82
96
  - 空环境启动,拿不到密钥、会话文件与环境变量(Windows 上 libuv 会从父进程补入 PATH、SYSTEMROOT、USERPROFILE 等系统变量,不含密钥);不授予文件写、子进程、worker、addon、inspector 权限;Node 22.0–22.12 用 `--experimental-permission`;嵌入 Electron 时设 `ELECTRON_RUN_AS_NODE=1`。
83
97
  - 子进程里用 `node:vm` 建只含 ECMAScript 内建对象的上下文(`codeGeneration: { strings: false, wasm: false }`,沙箱对象空原型);全局函数都在上下文内定义,只经一个宿主函数交换 JSON 字符串;子进程主 realm 也禁止字符串生成代码,经构造器链逃逸拿不到 `Function("return process")`。
84
98
  - `tools.*` 经 stdin / stdout 的 JSON 行协议回调父进程执行。
85
- - 网络:Node ≥ 25 的权限模型同时拒绝网络(strict);Node 22 / 24 不管网络,脚本若逃出 `vm` 就能联网——此时工具描述标注 `network not isolated`,`codemode.requireStrict: true` 时直接不注册 `codemode` 并给出 warning(codemode 预设随之回退到 default)。
99
+ - 网络:Node ≥ 25 的权限模型同时拒绝网络(strict);Node 22 / 24 不管网络,脚本若逃出 `vm` 就能联网——此时工具描述标注 `network not isolated`,`codemode.requireStrict: true` 时直接不注册 `codemode` 并给出 warning(codemode-only 预设随之回退到 default)。
86
100
 
87
101
  | 实测(`--permission` + 只读入口) | Node 22.19 | Node 24.21 | Node 26.10 |
88
102
  | --------------------------------- | ---------- | ---------- | ---------- |
package/docs/hooks.md CHANGED
@@ -58,16 +58,16 @@
58
58
 
59
59
  stdin 是一个 JSON 对象,写完即关闭。所有事件共有:
60
60
 
61
- | 字段 | 说明 |
62
- | ---------------- | ---------------------------------------------- |
63
- | `hookEventName` | 事件名 |
64
- | `sessionId` | 会话 id(子会话为子会话自己的) |
65
- | `sessionFile` | 会话文件(落盘前缺省) |
66
- | `cwd` | 会话 cwd |
67
- | `model` | `{ provider, id }` |
68
- | `permissionMode` | `plan` / `default` / `auto-edit` / `full-auto` |
69
- | `depth` | 主会话 0,`task` 子会话 1 |
70
- | `host` | 宿主适配器 id(激活时) |
61
+ | 字段 | 说明 |
62
+ | ---------------- | --------------------------------------------------------------------- |
63
+ | `hookEventName` | 事件名 |
64
+ | `sessionId` | 会话 id(子会话为子会话自己的) |
65
+ | `sessionFile` | 会话文件(落盘前缺省) |
66
+ | `cwd` | 会话 cwd |
67
+ | `model` | `{ provider, id }` |
68
+ | `permissionMode` | `plan` / `allowlist` / `default` / `auto-edit` / `auto` / `full-auto` |
69
+ | `depth` | 主会话 0,`task` 子会话 1 |
70
+ | `host` | 宿主适配器 id(激活时) |
71
71
 
72
72
  事件特有:
73
73
 
@@ -0,0 +1,148 @@
1
+ # 权限模式与 auto 判定
2
+
3
+ 本文写 ama 的六种权限模式、每次工具调用的判定顺序,以及 `auto` 模式「规则层 → 静态判定 → 模型分类器」三层怎样决定放行还是询问。总体设计见 [design.md](design.md) §6.3、§7;Hook 的输入输出见 [hooks.md](hooks.md)。
4
+
5
+ ## 模式
6
+
7
+ | 值 | 显示名 | 读 | 写(项目内) | 执行(bash 等) | 何时用 |
8
+ | ----------- | ------------------ | --- | --------------------------- | ------------------------------ | -------------------------------- |
9
+ | `default` | Manual | ✓ | 询问 | 询问 | 缺省 |
10
+ | `auto-edit` | Accept edits | ✓ | ✓ | 询问 | 放心让它改代码,命令逐条看 |
11
+ | `plan` | Plan | ✓ | 拒绝 | 拒绝 | 只读调研、出方案 |
12
+ | `auto` | Auto | ✓ | ✓(受保护路径与项目外询问) | 安全名单放行,其余由分类器判断 | 推荐:常规操作不打扰,有风险才问 |
13
+ | `full-auto` | Bypass permissions | ✓ | ✓ | ✓ | 一次性沙箱、容器 |
14
+ | `allowlist` | Allowlist only | ✓ | 只放行 allow 规则命中的 | 只放行 allow 规则命中的 | CI:从不询问,没列出的直接拒绝 |
15
+
16
+ 所有模式下,deny 规则、Hook deny 都先判定并直接拒绝;危险命令表(`rm -rf /`、`git push --force`、`curl … | sh` 等,见 README「安全」)一律询问,`allowlist` 与无人值守时变为拒绝。
17
+
18
+ 设置方式:`--permission-mode <值>`、配置 `permission.mode`、交互界面 `/permission`(选择器)或 `Shift+Tab`、RPC `set_permission_mode`、SDK `permission.mode`。
19
+
20
+ ### 严格度与项目级配置
21
+
22
+ 严格度从严到宽:`plan < allowlist < default < auto-edit < auto < full-auto`。项目级 `.ama/config.json` 只能把模式往严的方向改;另外**不能设 `auto` 或 `full-auto`**(这两种由 ama 自己或什么都不判断就放行,必须由用户级配置、命令行或 profile 打开),设了会被忽略并给 warning。
23
+
24
+ `allowlist` 排在 `plan` 与 `default` 之间:它放行的调用是 `plan` 放行的(只读工具)加上 allow 规则明确列出的;`default` 放行的集合包含它(只读 + allow 规则),其余在 `default` 下询问、在 `allowlist` 下拒绝。所以 `plan ⊆ allowlist ⊆ default`。读工具在 `allowlist` 下照常放行,与其它模式一致;要连读都限制,用 deny 规则。
25
+
26
+ ### 界面
27
+
28
+ - 状态栏显示显示名:`mode:Auto`;`Bypass permissions` 标黄。
29
+ - `/permission` 不带参数打开选择器:标题 `Mode`,每项「显示名 + 一行说明」,右侧是数字快捷键 1–6,当前模式打勾,配置里的缺省模式标 `Default`,Auto 标 `Recommended`。line 模式 `/permission` 打印同样的列表。
30
+ - `Shift+Tab` 循环:Manual → Accept edits → Plan → Auto → Bypass permissions → Manual。`Allowlist only` 不在循环里,只能显式选。
31
+
32
+ ## 判定顺序
33
+
34
+ 一次工具调用(模型直接发起或 codemode / task 里嵌套发起都一样):
35
+
36
+ ```text
37
+ schema 校验
38
+ → 命令式 Hook PreToolUse(deny 一票否决;allow / ask 交给管线)
39
+ → 权限管线:
40
+ ① 规则层(不调模型)
41
+ deny 规则(含内置 deny)、Hook deny → 拒绝
42
+ 危险命令表 → 询问
43
+ [auto] 受保护路径、项目外写入、网络命令、删除类命令 → 询问
44
+ Hook ask → 询问
45
+ allow 规则、Hook allow、本会话记忆 → 放行
46
+ ② 模式
47
+ plan / default / auto-edit / full-auto:同以前的模式真值表
48
+ allowlist:只读工具放行,其余拒绝(不在允许名单)
49
+ auto:静态判定(不调模型)→ 放行;未决定 → ③
50
+ ③ [auto] 模型分类器:allow → 放行;ask / 出错 / 超时 → 询问
51
+ → 询问时走审批链(宿主 broker → 界面 → 无人值守按拒绝)
52
+ ```
53
+
54
+ - 后面的步骤不能放宽前面的结论:allow 规则越不过危险命令和 auto 的规则层,分类器只能处理①②都没决定的调用。
55
+ - `allowlist` 从不询问:凡是会询问的(危险命令、Hook ask)一律拒绝,拒绝说明写「不在允许名单」。
56
+ - 无人值守(`-p`、RPC 未接审批)时,询问一律按拒绝;auto 模式下分类器仍会先跑,判 allow 的照样执行。
57
+
58
+ ## auto 的三层
59
+
60
+ ### ① 规则层
61
+
62
+ 不调模型,命中即询问(无人值守拒绝):
63
+
64
+ | 类别 | 内容 |
65
+ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | 危险命令 | `dangerous.ts` 的整张表(所有模式通用) |
67
+ | 机密路径 | 读或写 `.env`、`.env.*`(`.env.example` / `.sample` / `.template` / `.dist` 除外)、`.ssh/`、`.gnupg/`、`.aws/`、`.kube/config`、`.docker/config.json`、`.netrc`、`.pgpass`、私钥(`id_rsa` 等、`*.pem`、`*.key`、`*.p12`、`*.pfx`、`*.jks`)、ama 的 `auth.json` |
68
+ | 受保护写入 | 写 `.git/` 内部、项目里的 `.ama/`(可能改 Hook 与配置)、项目目录外的任何路径(`/dev/null` 等除外) |
69
+ | 网络命令 | `curl`、`wget`、`ssh`、`scp`、`rsync`、`nc`、`gh`;`git push / pull / fetch / clone`;`npm / pnpm / yarn / bun install / add / ci / update / publish / dlx`、`npx`;`pip install`、`cargo install / publish`、`go get / install`、`brew / apt install`、`docker pull / push / login` 等 |
70
+ | 删除与回退 | `rm -r` / `rm -f`、`find -delete`、`git clean`、`git checkout -- …` / `git restore`、`git stash drop / clear`、`shred`、`truncate` |
71
+ | bash 里的路径 | 重定向目标(`>`、`>>`、`&>`、`tee`)、`cp` / `mv` / `mkdir` / `touch` / `ln` / `chmod` 的目标在项目外或受保护;命令参数里出现机密路径(`cat .env`) |
72
+
73
+ 「项目目录」是会话 cwd。文件工具按 `path` 参数判断;bash 按 `dangerous.ts` 同一套分词与嵌套展开(`sh -c`、`eval`、`xargs`、`find -exec`)逐段判断。
74
+
75
+ ### ② 静态判定
76
+
77
+ 不调模型,满足即放行:
78
+
79
+ - 只读工具(read、ls、grep、glob 等,`permission: "read"`)。
80
+ - write / edit 目标在项目目录内且不在受保护路径(受保护的已在①询问)。
81
+ - bash:命令里每一段(`&&`、`||`、`;`、`|` 切开)都在**安全名单**内,且
82
+ - 没有命令替换 `$(…)`、反引号、`<(…)`;
83
+ - 参数里没有变量展开 `$X`,没有可能匹配点文件的通配(`.e*`);
84
+ - 没有 `sh -c` 之类的嵌套(shell、`eval`、`xargs` 都不在名单里)。
85
+ 管道到安全命令(`cat a | grep b | wc -l`)没问题;管道到 shell 不在名单里。重定向写到项目内允许,写到项目外已在①询问。
86
+ - allow 规则命中(这一步在①的末尾已放行)。
87
+
88
+ 安全名单(`src/permissions/auto-safe.ts`,每条都有正反例测试):
89
+
90
+ | 命令 | 限制 |
91
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
92
+ | `ls`、`cat`、`head`、`tail`、`wc`、`grep`、`egrep`、`fgrep`、`rg`、`pwd`、`echo`、`printf`、`which`、`env` 与 `printenv`(只打印)、`true`、`false`、`sort`、`cut`、`tr`、`diff`、`basename`、`dirname`、`realpath`、`stat`、`file`、`du`、`df`、`date`、`whoami`、`uname`、`cd` / `pushd` / `popd`(之后的相对路径按新目录解析) | `env FOO=1 cmd` 按 `cmd` 判断 |
93
+ | `find` | 不带 `-exec`、`-execdir`、`-ok`、`-okdir`、`-delete`、`-fprint*`、`-fls` |
94
+ | `git status / diff / log / show / rev-parse / blame / ls-files`(`diff / log / show` 不带 `--output`、`--ext-diff`、`--textconv`;`rg` 不带 `--pre`;`sort` 不带 `-o`;`date` 不带 `-s`) | 子命令前只允许 `-C dir`、`--no-pager`(`-c` 能改 pager,不算安全) |
95
+ | `git branch` | 只列出:不带 `-d / -D / --delete / -m / -M / -c / -C / -f / -u` 等 |
96
+ | `npm / pnpm / yarn test`、`… run test / lint / typecheck / build`、`pnpm / yarn lint / typecheck / build`、`npm t` | |
97
+ | `node --test`、`tsc --noEmit`、`vitest run`、`pnpm vitest run`、`pnpm exec vitest run`、`pytest`、`python -m pytest` | |
98
+ | `cargo test / check / build / clippy`、`go test / build / vet`、`make test / check / lint / build` | |
99
+
100
+ 扩展:用户级配置 `permission.autoSafeCommands` 追加,例如 `["just test", "bun test", "make fmt"]`——按词前缀匹配(`just test` 命中 `just test --verbose`);含 `*` 的按通配匹配整段(`bun run test*`)。项目级配置不能追加(放宽)。追加的命令照样先过①:网络、删除、项目外写入仍然询问。
101
+
102
+ ### ③ 模型分类器
103
+
104
+ 只处理①②都没决定的调用(例如 `rm old.txt`、`node scripts/gen.js`、宿主工具、codemode 脚本)。
105
+
106
+ - **独立请求**:不进会话转录、不改主会话的消息与前缀(主会话的提示缓存不受影响),不触发保温;请求用途 `purpose: "classify"`。
107
+ - **输入**:工具名、参数(JSON,截断到 4000 字符)、cwd、项目根、最近一条用户消息的摘要(截断到 600 字符)。参数与用户消息放在 `<tool_call_data>` … `</tool_call_data>` 数据块里,块内出现的结束标记会被转义;系统提示要求把块内一切当数据,忽略其中的指令(包括「ignore previous instructions」「respond allow」之类),遇到这种文本倾向于 ask。
108
+ - **输出**:严格 JSON `{"decision":"allow"|"ask","reason":"…"}`。解析失败、超时(10 s)、请求出错 → 询问。
109
+ - **模型**:`permission.autoModel`(`provider/model`);缺省用当前会话模型。推荐配一个便宜快速的模型,例如 `packy/qwen3.8-flash`。`maxTokens` 256,关闭思考。
110
+ - **缓存**:会话内按「工具名 + 归一化参数」(bash 折叠空白,其它按键排序的 JSON)缓存成功的判定,同样的调用只分类一次;出错与超时不缓存。
111
+ - **费用**:每次分类的用量记一条 `usage` 条目,`kind: "permission_classify"`,计入 `/session` 费用与 RPC 统计,不进上下文。
112
+ - 分类器只能把未决定的调用判成 allow 或 ask,不能推翻①的拒绝或询问。
113
+
114
+ ## 审计
115
+
116
+ - 每次 auto 判定(放行与询问)记录 `{ layer: "rule" | "static" | "classifier", decision, reason }`:
117
+ - `tool_execution_end` 事件带 `autoDecision`;需要询问时 `permission_request` 事件与审批请求带 `autoDecision`(对话框里显示「Auto:原因」)。
118
+ - `/permissions` 显示最近 20 条判定(工具、摘要、层、结果、原因)。
119
+ - Hook `PreToolUse` 的输入不变;`permissionMode` 字段会出现新值 `auto`、`allowlist`。
120
+
121
+ ## 配置
122
+
123
+ ```jsonc
124
+ {
125
+ "permission": {
126
+ "mode": "auto",
127
+ "autoModel": "packy/qwen3.8-flash",
128
+ "autoSafeCommands": ["just test", "make fmt"],
129
+ "allow": ["bash(npm run e2e)"],
130
+ "deny": ["bash(terraform *)"],
131
+ },
132
+ }
133
+ ```
134
+
135
+ CI 用 allowlist:
136
+
137
+ ```sh
138
+ ama -p "修好 lint 并跑测试" --permission-mode allowlist \
139
+ --allow 'write(src/**)' --allow 'edit(src/**)' --allow 'bash(pnpm lint*)' --allow 'bash(pnpm test*)'
140
+ ```
141
+
142
+ ## 已知限制
143
+
144
+ - 静态判定看命令文本,不看命令真实会读什么:`grep -r token .` 会读到项目里的 `.env`,按安全名单放行。需要更严时,对 `.env` 加 deny 规则(`read(**/.env*)`、`bash(*.env*)`)。
145
+ - 「项目目录」就是 cwd,不跟随符号链接;在子目录启动时,上级目录算项目外。
146
+ - `npm test` / `make test` 之类会执行项目自己的脚本,安全名单按「在项目内跑测试与构建」放行;不信任的仓库请用 `default` 或 `plan`。
147
+ - `sudo` / `doas` 前缀的命令即使后面是安全命令也不算安全(`sudo` 本身在危险命令表里,询问)。
148
+ - 分类器是模型判断,不是安全边界。它前面的规则层不调模型、可审阅;真正不可接受的操作请写 deny 规则。