@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
@@ -0,0 +1,218 @@
1
+ /**
2
+ * 工具调用的文字摘要(终端界面视觉设计 v1 §3.5):标题行的参数摘要、`⎿` 后的一行结果摘要、
3
+ * 统一 diff 解析与输出清洗。纯函数,ToolView 负责排版与着色之外的状态。
4
+ *
5
+ * 结果摘要(完成后):
6
+ * - read `读取 N 行`(图片 `图片 mime`、空文件 `空文件`);edit `N 处修改 · +a −b`;write `新建 / 覆盖 · N 行`;
7
+ * - bash `退出 c · 耗时 · N 行`(超时 `超时 · 耗时`、中断 `已中断 · 耗时`);
8
+ * - grep `N 处匹配 · M 个文件`;glob `N 个文件`;codemode `N 个内层调用 · 脚本输出 M 行`;
9
+ * - task `完成 · 耗时 · ↑in ↓out`;其它 `N 行输出`;失败(非 bash)取错误首行。
10
+ */
11
+ import { isAbsolute, relative } from "node:path";
12
+ import { stripAnsi } from "../../tui.js";
13
+ import { formatTokens } from "./status-bar.js";
14
+ /** 工具输出 → 可安全显示的行。 */
15
+ export function cleanLines(text) {
16
+ const clean = stripAnsi(text)
17
+ .replace(/\r\n?/g, "\n")
18
+ .replace(/\t/g, " ")
19
+ .replace(/[\x00-\x08\x0b-\x1f\x7f]/g, "");
20
+ const lines = clean.split("\n");
21
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "")
22
+ lines.pop();
23
+ return lines;
24
+ }
25
+ export function flat(text, max = 120) {
26
+ const one = text.replace(/\s*\n\s*/g, " ⏎ ").trim();
27
+ return one.length > max ? `${one.slice(0, max - 1)}…` : one;
28
+ }
29
+ export function displayPath(path, cwd) {
30
+ if (cwd === undefined || !isAbsolute(path))
31
+ return path;
32
+ const rel = relative(cwd, path);
33
+ return rel === "" ? "." : rel.startsWith("..") || isAbsolute(rel) ? path : rel;
34
+ }
35
+ /** 标题行里的参数摘要。 */
36
+ export function toolSummary(name, args, cwd) {
37
+ if (typeof args !== "object" || args === null)
38
+ return "";
39
+ const a = args;
40
+ const str = (key) => typeof a[key] === "string" && a[key] !== "" ? a[key] : undefined;
41
+ const path = str("path") ?? str("file_path");
42
+ switch (name) {
43
+ case "bash":
44
+ return flat(str("command") ?? "");
45
+ case "read": {
46
+ if (path === undefined)
47
+ return "";
48
+ const offset = typeof a["offset"] === "number" ? a["offset"] : undefined;
49
+ const limit = typeof a["limit"] === "number" ? a["limit"] : undefined;
50
+ const range = offset !== undefined || limit !== undefined
51
+ ? `:${offset ?? 1}${limit !== undefined ? `+${limit}` : ""}`
52
+ : "";
53
+ return displayPath(path, cwd) + range;
54
+ }
55
+ case "grep":
56
+ case "glob": {
57
+ const pattern = str("pattern") ?? "";
58
+ return path !== undefined ? `${flat(pattern)} in ${displayPath(path, cwd)}` : flat(pattern);
59
+ }
60
+ case "task":
61
+ return flat(str("description") ?? str("prompt") ?? "");
62
+ default:
63
+ if (path !== undefined)
64
+ return displayPath(path, cwd);
65
+ for (const key of [
66
+ "command",
67
+ "pattern",
68
+ "description",
69
+ "query",
70
+ "url",
71
+ "name",
72
+ "code",
73
+ "script",
74
+ ]) {
75
+ const value = str(key);
76
+ if (value !== undefined)
77
+ return flat(value);
78
+ }
79
+ return "";
80
+ }
81
+ }
82
+ /** 完成耗时:< 10s 一位小数(`2.1s`),< 60s 整秒,再长 `1m05s`。 */
83
+ export function formatDuration(ms) {
84
+ const s = Math.max(0, ms) / 1000;
85
+ if (s < 10)
86
+ return `${s.toFixed(1)}s`;
87
+ if (s < 60)
88
+ return `${Math.floor(s)}s`;
89
+ const total = Math.floor(s);
90
+ return `${Math.floor(total / 60)}m${String(total % 60).padStart(2, "0")}s`;
91
+ }
92
+ /** 统一 diff → 行(去掉 `---` / `+++` 文件头),带行号。 */
93
+ export function parseDiff(diff) {
94
+ const rows = [];
95
+ let oldNo = 0;
96
+ let newNo = 0;
97
+ for (const line of cleanLines(diff)) {
98
+ if (line.startsWith("---") || line.startsWith("+++"))
99
+ continue;
100
+ const hunk = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
101
+ if (hunk) {
102
+ oldNo = Number(hunk[1]);
103
+ newNo = Number(hunk[2]);
104
+ rows.push({ kind: "hunk", text: line });
105
+ }
106
+ else if (line.startsWith("+"))
107
+ rows.push({ kind: "add", text: line, num: newNo++ });
108
+ else if (line.startsWith("-"))
109
+ rows.push({ kind: "del", text: line, num: oldNo++ });
110
+ else if (line.startsWith("@@"))
111
+ rows.push({ kind: "hunk", text: line });
112
+ else {
113
+ rows.push({ kind: "ctx", text: line, num: newNo });
114
+ oldNo++;
115
+ newNo++;
116
+ }
117
+ }
118
+ return rows;
119
+ }
120
+ function record(value) {
121
+ return typeof value === "object" && value !== null ? value : {};
122
+ }
123
+ function num(value) {
124
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
125
+ }
126
+ export function diffOf(result) {
127
+ const diff = record(result?.details)["diff"];
128
+ return typeof diff === "string" && diff !== "" ? diff : undefined;
129
+ }
130
+ /** bash 完成后的正文:优先 `details.output`(不含退出码等附注)。 */
131
+ export function bashOutput(result) {
132
+ const output = record(result.details)["output"];
133
+ return typeof output === "string" ? output : undefined;
134
+ }
135
+ /** 完成后的一行结果摘要(已着色,不含 `⎿`)。 */
136
+ export function resultSummary(input, theme) {
137
+ const { name, result, isError, lines } = input;
138
+ const details = record(result.details);
139
+ const muted = (s) => theme.fg("muted", s);
140
+ const sep = theme.fg("dim", " · ");
141
+ const wall = num(details["wall_time_seconds"]);
142
+ const elapsed = input.elapsedMs ?? (wall !== undefined ? wall * 1000 : 0);
143
+ const duration = formatDuration(elapsed);
144
+ const count = lines.length;
145
+ if (name === "bash") {
146
+ const code = num(details["exit_code"]) ?? (isError ? 1 : 0);
147
+ const total = num(details["totalLines"]) ?? count;
148
+ const head = details["aborted"] === true
149
+ ? "已中断"
150
+ : details["timedOut"] === true
151
+ ? "超时"
152
+ : `退出 ${code}`;
153
+ const parts = [head, duration, ...(total > 0 ? [`${total} 行`] : [])];
154
+ const text = parts.join(" · ");
155
+ return isError || code !== 0 ? theme.fg("error", text) : parts.map(muted).join(sep);
156
+ }
157
+ if (isError)
158
+ return theme.fg("error", `${theme.glyphs.fail} ${lines[0] ?? "失败"}`);
159
+ switch (name) {
160
+ case "read": {
161
+ const mime = details["mimeType"];
162
+ if (typeof mime === "string")
163
+ return muted(`图片 ${mime}`);
164
+ const first = num(details["firstLine"]);
165
+ const last = num(details["lastLine"]);
166
+ if (num(details["totalLines"]) === 0)
167
+ return muted("空文件");
168
+ const n = first !== undefined && last !== undefined ? last - first + 1 : count;
169
+ return muted(`读取 ${n} 行`);
170
+ }
171
+ case "edit": {
172
+ const rows = parseDiff(diffOf(result) ?? "");
173
+ const add = rows.filter((r) => r.kind === "add").length;
174
+ const del = rows.filter((r) => r.kind === "del").length;
175
+ const edits = record(input.args)["edits"];
176
+ const n = num(details["replacements"]) ?? (Array.isArray(edits) ? edits.length : 1);
177
+ return (muted(`${n} 处修改`) +
178
+ sep +
179
+ theme.fg("success", `+${add}`) +
180
+ " " +
181
+ theme.fg("error", `−${del}`));
182
+ }
183
+ case "write": {
184
+ const content = record(input.args)["content"];
185
+ const n = typeof content === "string" ? cleanLines(content).length : 0;
186
+ return muted(`${details["created"] === true ? "新建" : "覆盖"} · ${n} 行`);
187
+ }
188
+ case "grep": {
189
+ const matches = num(details["matches"]);
190
+ if (matches === 0)
191
+ return muted("无匹配");
192
+ if (matches === undefined)
193
+ return muted(`${count} 行`);
194
+ return muted(`${matches} 处匹配 · ${num(details["files"]) ?? "?"} 个文件`);
195
+ }
196
+ case "glob": {
197
+ const n = num(details["count"]);
198
+ return muted(n === 0 ? "无匹配" : `${n ?? count} 个文件`);
199
+ }
200
+ case "codemode": {
201
+ const calls = num(details["toolCalls"]) ?? input.nestedCount ?? 0;
202
+ return muted(`${calls} 个内层调用 · 脚本输出 ${count} 行`);
203
+ }
204
+ case "task": {
205
+ const usage = record(details["usage"]);
206
+ const input_ = num(usage["input"]);
207
+ const output = num(usage["output"]);
208
+ const tokens = input_ !== undefined && output !== undefined
209
+ ? [
210
+ `${theme.glyphs.arrowUp}${formatTokens(input_)} ${theme.glyphs.arrowDown}${formatTokens(output)}`,
211
+ ]
212
+ : [];
213
+ return muted(["完成", duration, ...tokens].join(" · "));
214
+ }
215
+ default:
216
+ return muted(count === 0 ? "完成" : `${count} 行输出`);
217
+ }
218
+ }
@@ -1,20 +1,26 @@
1
1
  /**
2
- * 工具调用显示(设计 §12.5)。[B7]
2
+ * 工具调用显示(设计 §12.5;终端界面视觉设计 v1 §3.5)。[B7]
3
3
  *
4
- * - 一行标题 `● bash git status`:圆点运行中为强调色,成功绿、失败红;摘要取最能代表这次调用的参数
5
- * (bash 命令、文件路径、搜索模式、task 描述……),路径相对会话 cwd。
6
- * - 折叠(缺省):结果前 3 行 + 剩余行数;`edit` 显示 `details.diff`(+/− 着色,前 12 行);
7
- * `bash` 运行中流式显示尾部 8 行;错误红色。`Ctrl+O` 展开全部(上限 400 行)。
8
- * - 嵌套:带 `parentToolCallId` 的调用(codemode 脚本里的 `tools.*`,B10)挂在外层调用下;折叠时只列
9
- * 内层调用的标题(最近 5 个),展开时完整显示。
4
+ * 三级层级,用缩进表达:
5
+ * ```
6
+ * ⏺ read src/tui/tui.ts:1+120 第 0 列:⏺(运行中 accent、成功 success、失败 error)+ 工具名 + 摘要
7
+ * ⎿ 读取 120 行 第 2 列:⎿ + 一行结果摘要(tool-summary.ts)
8
+ * 1 /** 第 4 列:输出正文
9
+ * … 另 117 行(Ctrl+O 展开)
10
+ * ```
11
+ * - 运行中摘要行 `⎿ ⠋ 运行中 · 4s`:spinner 与底部 Loader 同帧(ToolTracker.tick 由 Loader.onFrame 驱动),
12
+ * 标题行不变;bash 流式显示尾部 8 行(muted)。task 显示 `子 Agent · 运行中 1m05s`。
13
+ * - 折叠(缺省):结果前 3 行;edit 显示 `details.diff`(`@@` dim、`+` success、`-` error、上下文 muted,
14
+ * 宽 ≥ 60 时带行号列),前 12 行;`Ctrl+O` 展开全部(上限 400 行,长行折行)。
15
+ * - 嵌套:带 `parentToolCallId` 的调用(codemode 脚本里的 `tools.*`)挂在外层调用下、右移 4 列;折叠时只列
16
+ * 最近 5 个内层调用的标题与摘要行,展开时完整显示。
10
17
  * - 工具输出先去掉 ANSI 与控制字符、Tab 换成空格,避免打乱布局。
11
- * - 自定义渲染(W3-B9a-2,`ToolDefinition.renderCall / renderResult`):`getTool` 能取到定义时,标题摘要
12
- * 优先取 `renderCall(input, width)` 的第一行(codemode 显示脚本首行);成功的结果优先用
13
- * `renderResult(result, width, expanded)`(去控制字符、按宽截断、上限 400 行);自定义渲染抛错或返回
14
- * 空时回到缺省显示;错误结果始终用缺省的红色显示。
18
+ * - 自定义渲染(`ToolDefinition.renderCall / renderResult`):标题摘要优先取 `renderCall` 第一行;成功的结果
19
+ * 正文优先用 `renderResult`;抛错或返回空时回到缺省显示;错误结果始终用缺省显示。
15
20
  */
16
21
  import type { ToolDefinition, ToolResult } from "../../tools/types.js";
17
22
  import { type Component, type Theme } from "../../tui.js";
23
+ export { cleanLines, toolSummary } from "./tool-summary.js";
18
24
  export declare const COLLAPSED_RESULT_LINES = 3;
19
25
  export declare const COLLAPSED_DIFF_LINES = 12;
20
26
  export declare const BASH_TAIL_LINES = 8;
@@ -26,11 +32,11 @@ export interface ToolViewOptions {
26
32
  cwd?: string;
27
33
  /** 取工具定义(自定义渲染用);缺省或取不到时用内置摘要与结果显示。 */
28
34
  getTool?(name: string): ToolDefinition | undefined;
35
+ /** 计时(耗时与运行中秒数);缺省 Date.now。 */
36
+ now?(): number;
37
+ /** 运行中摘要行的 spinner 帧(与 Loader 同帧);缺省静态字形。 */
38
+ spinner?(): string;
29
39
  }
30
- /** 工具输出 → 可安全显示的行。 */
31
- export declare function cleanLines(text: string): string[];
32
- /** 标题行里的参数摘要。 */
33
- export declare function toolSummary(name: string, args: unknown, cwd?: string): string;
34
40
  export declare class ToolView implements Component {
35
41
  readonly toolCallId: string;
36
42
  readonly toolName: string;
@@ -43,9 +49,16 @@ export declare class ToolView implements Component {
43
49
  private readonly nested;
44
50
  private cache;
45
51
  private version;
52
+ private startedAt;
53
+ /** 审批对话框打开时还没开始执行(摘要行显示「等待确认」,关闭后重新计时)。 */
54
+ private awaiting;
55
+ private elapsedMs;
56
+ /** 重放的历史调用(没有真实耗时)。 */
57
+ replayed: boolean;
46
58
  /** 外层调用(嵌套时):子视图变化要让外层缓存失效。 */
47
59
  private parent;
48
60
  constructor(toolCallId: string, toolName: string, args: unknown, options: ToolViewOptions);
61
+ private now;
49
62
  get isRunning(): boolean;
50
63
  get isExpanded(): boolean;
51
64
  get children(): readonly ToolView[];
@@ -54,18 +67,32 @@ export declare class ToolView implements Component {
54
67
  update(partial: string): void;
55
68
  finish(result: ToolResult, isError: boolean): void;
56
69
  addNested(child: ToolView): void;
70
+ /** 审批打开 / 关闭:还没有输出的运行中调用显示「等待确认」,关闭后从此刻重新计时。 */
71
+ setAwaiting(awaiting: boolean): void;
72
+ /** spinner 换帧 / 秒数变化:运行中的视图重画摘要行。 */
73
+ tick(): void;
57
74
  private touch;
58
75
  invalidate(): void;
59
76
  render(width: number): string[];
60
77
  header(width: number): string;
78
+ /** ` ⎿ 摘要`:运行中带 spinner 与秒数,完成后是结果摘要。 */
79
+ summaryLine(width: number): string | undefined;
61
80
  private custom;
62
81
  /** `renderCall` 的第一行(去控制字符、折叠空白);没有或为空时 undefined。 */
63
82
  private customCall;
64
83
  /** 成功结果的 `renderResult` 行;没有或为空时 undefined。 */
65
84
  private customResult;
85
+ /** 完成后的结果正文(已清洗):bash 取 `details.output`。 */
86
+ private resultLines;
66
87
  private indent;
67
88
  private more;
89
+ private nestedLines;
68
90
  private body;
91
+ /** read:行号右对齐(按最长行号,至少 3 位)dim,与正文隔两格。 */
92
+ private readLines;
93
+ /** grep:路径正文色,`:行号:` dim,命中片段 muted。 */
94
+ private grepLines;
95
+ private diffLines;
69
96
  }
70
97
  /** 按 toolCallId 管理视图与嵌套;`Ctrl+O` 的全局展开状态也在这里。 */
71
98
  export declare class ToolTracker {
@@ -75,6 +102,8 @@ export declare class ToolTracker {
75
102
  constructor(options: ToolViewOptions);
76
103
  get isExpanded(): boolean;
77
104
  get(toolCallId: string): ToolView | undefined;
105
+ /** 运行中的调用数(Loader 动词用)。 */
106
+ running(): ToolView[];
78
107
  /** 新调用;返回视图与它是否是顶层(顶层由调用方加进消息区)。 */
79
108
  start(event: {
80
109
  toolCallId: string;
@@ -87,6 +116,10 @@ export declare class ToolTracker {
87
116
  };
88
117
  update(toolCallId: string, partial: string): void;
89
118
  end(toolCallId: string, result: ToolResult, isError: boolean): void;
119
+ /** 审批对话框打开 / 关闭(见 ToolView.setAwaiting)。 */
120
+ setAwaiting(awaiting: boolean): void;
121
+ /** Loader 换帧:运行中的视图重画摘要行。 */
122
+ tick(): void;
90
123
  /** 已完成的调用(重放历史)。 */
91
124
  completed(toolCallId: string, toolName: string, args: unknown, result: ToolResult | undefined, isError: boolean): ToolView;
92
125
  /** Ctrl+O:切换全部调用的展开状态。 */