@armadra/agent 0.3.0 → 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.
- package/CHANGELOG.md +84 -0
- package/README.md +93 -44
- package/dist/agent/retry.d.ts +1 -1
- package/dist/agent/retry.js +2 -1
- package/dist/agent/session-cache.js +1 -1
- package/dist/agent/session-classifier.d.ts +18 -0
- package/dist/agent/session-classifier.js +103 -0
- package/dist/agent/session-core.d.ts +13 -0
- package/dist/agent/session-tools.js +24 -3
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.js +12 -3
- package/dist/agent/tool-runner.js +15 -2
- package/dist/agent/types.d.ts +9 -1
- package/dist/ai/apis/anthropic-messages.js +3 -2
- package/dist/ai/apis/google-generative-ai.js +3 -2
- package/dist/ai/apis/openai-completions.js +3 -2
- package/dist/ai/apis/openai-responses.js +3 -2
- package/dist/ai/http.d.ts +28 -6
- package/dist/ai/http.js +41 -8
- package/dist/ai/providers/registry.d.ts +8 -1
- package/dist/ai/providers/registry.js +30 -22
- package/dist/ai/providers/suggest.d.ts +18 -0
- package/dist/ai/providers/suggest.js +72 -0
- package/dist/ai/sse.d.ts +6 -2
- package/dist/ai/sse.js +22 -2
- package/dist/ai/types.d.ts +9 -2
- package/dist/bundle/ama.cjs +15279 -10151
- package/dist/cli/args.d.ts +19 -4
- package/dist/cli/args.js +83 -10
- package/dist/cli/bootstrap.js +27 -3
- package/dist/cli/codemode-notice.d.ts +20 -0
- package/dist/cli/codemode-notice.js +55 -0
- package/dist/cli/compose-session.d.ts +5 -0
- package/dist/cli/compose-session.js +32 -1
- package/dist/cli/compose-store.d.ts +1 -1
- package/dist/cli/compose-store.js +3 -1
- package/dist/cli/compose.d.ts +14 -4
- package/dist/cli/compose.js +29 -10
- package/dist/cli/default-model.d.ts +38 -1
- package/dist/cli/default-model.js +96 -9
- package/dist/cli/deps.d.ts +36 -0
- package/dist/cli/exit-codes.d.ts +2 -0
- package/dist/cli/exit-codes.js +3 -0
- package/dist/cli/fake-visibility.d.ts +13 -0
- package/dist/cli/fake-visibility.js +26 -0
- package/dist/cli/from-prompt.d.ts +18 -0
- package/dist/cli/from-prompt.js +49 -0
- package/dist/cli/main.d.ts +9 -2
- package/dist/cli/main.js +77 -3
- package/dist/cli/proxy.d.ts +51 -0
- package/dist/cli/proxy.js +135 -0
- package/dist/cli/startup-screen.d.ts +29 -0
- package/dist/cli/startup-screen.js +47 -0
- package/dist/cli/startup-steps.d.ts +1 -1
- package/dist/cli/startup-steps.js +14 -12
- package/dist/cli/subcommands/config.d.ts +19 -3
- package/dist/cli/subcommands/config.js +108 -21
- package/dist/cli/subcommands/context.js +4 -1
- package/dist/cli/subcommands/doctor.js +12 -1
- package/dist/cli/subcommands/init.js +2 -1
- package/dist/cli/subcommands/models-discover.d.ts +15 -9
- package/dist/cli/subcommands/models-discover.js +53 -46
- package/dist/cli/subcommands/probe-runner.d.ts +96 -0
- package/dist/cli/subcommands/probe-runner.js +264 -0
- package/dist/cli/subcommands/providers-probe.d.ts +34 -0
- package/dist/cli/subcommands/providers-probe.js +87 -0
- package/dist/cli/subcommands/providers.d.ts +3 -2
- package/dist/cli/subcommands/providers.js +48 -45
- package/dist/cli/subcommands/sessions-export.d.ts +10 -0
- package/dist/cli/subcommands/sessions-export.js +59 -0
- package/dist/cli/subcommands/sessions-search.d.ts +13 -0
- package/dist/cli/subcommands/sessions-search.js +103 -0
- package/dist/cli/subcommands/sessions.d.ts +3 -2
- package/dist/cli/subcommands/sessions.js +22 -1
- package/dist/cli/subcommands/stats.d.ts +17 -0
- package/dist/cli/subcommands/stats.js +198 -0
- package/dist/cli/system-prompt-arg.d.ts +11 -0
- package/dist/cli/system-prompt-arg.js +34 -0
- package/dist/codemode/modes.d.ts +4 -11
- package/dist/codemode/modes.js +5 -27
- package/dist/codemode/tool.d.ts +18 -14
- package/dist/codemode/tool.js +71 -28
- package/dist/config/init.d.ts +6 -2
- package/dist/config/init.js +12 -5
- package/dist/config/json-schema.d.ts +1 -0
- package/dist/config/json-schema.js +36 -3
- package/dist/config/key-docs.d.ts +22 -0
- package/dist/config/key-docs.js +105 -0
- package/dist/config/merge.d.ts +9 -8
- package/dist/config/merge.js +22 -7
- package/dist/config/schema.d.ts +1 -1
- package/dist/config/schema.js +24 -5
- package/dist/config/types.d.ts +34 -6
- package/dist/config/types.js +17 -1
- package/dist/modes/commands-core.js +6 -5
- package/dist/modes/interactive/approval-dialog.d.ts +21 -5
- package/dist/modes/interactive/approval-dialog.js +106 -27
- package/dist/modes/interactive/commands.d.ts +11 -2
- package/dist/modes/interactive/commands.js +56 -16
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +65 -81
- package/dist/modes/interactive/key-dispatch.d.ts +3 -1
- package/dist/modes/interactive/key-dispatch.js +5 -6
- package/dist/modes/interactive/line/line-mode.d.ts +1 -0
- package/dist/modes/interactive/line/line-mode.js +6 -4
- package/dist/modes/interactive/line/line-render.d.ts +4 -0
- package/dist/modes/interactive/line/line-render.js +28 -6
- package/dist/modes/interactive/message-view.d.ts +48 -9
- package/dist/modes/interactive/message-view.js +238 -44
- package/dist/modes/interactive/panels.d.ts +18 -0
- package/dist/modes/interactive/panels.js +143 -0
- package/dist/modes/interactive/pickers.d.ts +23 -2
- package/dist/modes/interactive/pickers.js +48 -15
- package/dist/modes/interactive/run-indicator.d.ts +51 -0
- package/dist/modes/interactive/run-indicator.js +189 -0
- package/dist/modes/interactive/startup-header.d.ts +40 -0
- package/dist/modes/interactive/startup-header.js +169 -0
- package/dist/modes/interactive/status-bar.d.ts +18 -15
- package/dist/modes/interactive/status-bar.js +98 -56
- package/dist/modes/interactive/tool-summary.d.ts +46 -0
- package/dist/modes/interactive/tool-summary.js +218 -0
- package/dist/modes/interactive/tool-view.d.ts +48 -15
- package/dist/modes/interactive/tool-view.js +203 -145
- package/dist/modes/print/print-mode.d.ts +31 -4
- package/dist/modes/print/print-mode.js +116 -7
- package/dist/modes/rpc/commands.js +5 -0
- package/dist/permissions/auto-safe.d.ts +60 -0
- package/dist/permissions/auto-safe.js +529 -0
- package/dist/permissions/classifier.d.ts +64 -0
- package/dist/permissions/classifier.js +184 -0
- package/dist/permissions/dangerous.d.ts +5 -0
- package/dist/permissions/dangerous.js +1 -1
- package/dist/permissions/modes.d.ts +30 -0
- package/dist/permissions/modes.js +78 -0
- package/dist/permissions/pipeline.d.ts +31 -4
- package/dist/permissions/pipeline.js +196 -6
- package/dist/permissions/protected.d.ts +19 -0
- package/dist/permissions/protected.js +74 -0
- package/dist/permissions/rules.js +3 -0
- package/dist/permissions/types.d.ts +50 -3
- package/dist/permissions/types.js +3 -0
- package/dist/sdk.d.ts +9 -3
- package/dist/sdk.js +10 -2
- package/dist/session/export.d.ts +32 -0
- package/dist/session/export.js +187 -0
- package/dist/session/redact.d.ts +15 -0
- package/dist/session/redact.js +55 -0
- package/dist/session/reuse.d.ts +33 -0
- package/dist/session/reuse.js +86 -0
- package/dist/session/scan.d.ts +34 -0
- package/dist/session/scan.js +140 -0
- package/dist/session/search.d.ts +52 -0
- package/dist/session/search.js +211 -0
- package/dist/session/stats-aggregate.d.ts +63 -0
- package/dist/session/stats-aggregate.js +163 -0
- package/dist/session/stats-index.d.ts +26 -0
- package/dist/session/stats-index.js +91 -0
- package/dist/session/stats-scan.d.ts +54 -0
- package/dist/session/stats-scan.js +236 -0
- package/dist/tools/presets.d.ts +35 -8
- package/dist/tools/presets.js +56 -17
- package/dist/tui/component.d.ts +8 -2
- package/dist/tui/component.js +3 -1
- package/dist/tui/components/box.d.ts +6 -1
- package/dist/tui/components/box.js +16 -6
- package/dist/tui/components/card.d.ts +23 -0
- package/dist/tui/components/card.js +37 -0
- package/dist/tui/components/editor-history.d.ts +6 -0
- package/dist/tui/components/editor-history.js +45 -0
- package/dist/tui/components/editor-paste.d.ts +1 -1
- package/dist/tui/components/editor-paste.js +4 -4
- package/dist/tui/components/editor.d.ts +11 -5
- package/dist/tui/components/editor.js +52 -58
- package/dist/tui/components/key-value.d.ts +3 -0
- package/dist/tui/components/key-value.js +16 -6
- package/dist/tui/components/loader.d.ts +37 -7
- package/dist/tui/components/loader.js +84 -21
- package/dist/tui/components/markdown.d.ts +5 -1
- package/dist/tui/components/markdown.js +45 -21
- package/dist/tui/components/meter.d.ts +3 -3
- package/dist/tui/components/meter.js +13 -11
- package/dist/tui/components/select-list.d.ts +23 -1
- package/dist/tui/components/select-list.js +76 -13
- package/dist/tui/glyphs.d.ts +68 -0
- package/dist/tui/glyphs.js +114 -0
- package/dist/tui/theme.d.ts +30 -6
- package/dist/tui/theme.js +103 -17
- package/dist/tui.d.ts +4 -2
- package/dist/tui.js +3 -1
- package/docs/codemode.md +23 -9
- package/docs/hooks.md +10 -10
- package/docs/permissions.md +148 -0
- package/docs/providers.md +33 -10
- package/docs/rpc.md +36 -36
- package/docs/session-format.md +2 -1
- package/docs/sessions.md +134 -0
- package/docs/tui.md +139 -62
- package/package.json +3 -1
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 状态栏(设计 §12.6
|
|
2
|
+
* 状态栏(设计 §12.6;终端界面视觉设计 v1 §3.10):编辑器下方一行,永远是最后一行。[B7]
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 两区:左区「权限模式 · shift+tab 切换」,右区用量,中间用空格撑开;空隙不足 4 列时退回单区(` · ` 连接)。
|
|
5
|
+
* `Accept edits · shift+tab 切换 sonnet-4-5 · medium · ↑12.3k ↓1.2k · cache 80% ♨ · $0.12 · ctx 34%`
|
|
6
6
|
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
17
|
-
* codemode
|
|
7
|
+
* - 右区顺序固定:模型 · 思考级别(只显示值,off 不显示)· `↑` 输入(含缓存读写)`↓` 输出 · cache · 费用 ·
|
|
8
|
+
* rebill · ctx · queue · codemode · 预设(非 default 才显示)· [宿主状态];分隔符固定为 ` · `,
|
|
9
|
+
* 模式永远在最左(tmux 宿主可按此解析)。
|
|
10
|
+
* - 用量来自 `session.getStats()`,只在 `refresh()` 时读取(事件驱动),渲染只拼字符串;
|
|
11
|
+
* - [W3-C2] `cache` 是**最近一次**请求的命中率;端点三态 `cache —` / `cache 未报告`;保温中追加 `♨`;
|
|
12
|
+
* 重计费 `rebill $x`(无价模型显示 token);codemode 生效时 `codemode on|only`,网络未隔离追加 `net!`;
|
|
13
|
+
* - 着色:整行 dim;模式名正文色(Bypass permissions warning、Plan accent);模型 accent;ctx 按阈值
|
|
14
|
+
* success / warning / error;rebill、queue warning;`net!` error。宽 ≥ 110 时 ctx 用余量表。
|
|
15
|
+
* - 模型名缩写:宽 < 100 去掉供应商前缀,< 60 再去掉 `@渠道`,< 48 去掉版本后缀(第一个 `-数字` 起)。
|
|
16
|
+
* - 宽度不够时按优先级丢弃(数字大的先丢):切换提示 12 → 宿主 11 → 预设 10 → rebill 9 → 费用 8 →
|
|
17
|
+
* cache 7 → token 6 → 思考 5 → 队列 4 → codemode 3 → ctx 1 → 模型 0;模式永不丢。
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
19
|
+
import { permissionModeLabel } from "../../permissions/modes.js";
|
|
20
|
+
import { formatModelRef } from "../../ai/providers/channels.js";
|
|
21
|
+
import { Meter, levelColor, truncateToWidth, visibleWidth, } from "../../tui.js";
|
|
20
22
|
export function formatTokens(count) {
|
|
21
23
|
if (count < 1000)
|
|
22
24
|
return String(count);
|
|
@@ -32,14 +34,29 @@ export function formatCost(cost) {
|
|
|
32
34
|
return `$${cost.toFixed(3)}`;
|
|
33
35
|
}
|
|
34
36
|
/** `cache 83%` / `cache —` / `cache 未报告`,保温计时中追加 ` ♨`。 */
|
|
35
|
-
export function cacheText(cache) {
|
|
37
|
+
export function cacheText(cache, warm = "♨") {
|
|
36
38
|
const rate = cache.lastHitRate ?? cache.hitRate;
|
|
37
39
|
const value = cache.reporting === "silent"
|
|
38
40
|
? "未报告"
|
|
39
41
|
: cache.reporting === "reported" && rate !== undefined
|
|
40
42
|
? `${Math.round(rate * 100)}%`
|
|
41
43
|
: "—";
|
|
42
|
-
return `cache ${value}${cache.warming.state === "scheduled" ?
|
|
44
|
+
return `cache ${value}${cache.warming.state === "scheduled" ? ` ${warm}` : ""}`;
|
|
45
|
+
}
|
|
46
|
+
/** 两区之间至少留的空格;不足时退回单区。 */
|
|
47
|
+
const MIN_GAP = 4;
|
|
48
|
+
/** ctx 改用余量表的最小宽度。 */
|
|
49
|
+
const METER_WIDTH = 110;
|
|
50
|
+
/** 按终端宽度缩写模型名(§3.10)。 */
|
|
51
|
+
export function abbreviateModel(ref, width) {
|
|
52
|
+
let out = ref;
|
|
53
|
+
if (width < 100)
|
|
54
|
+
out = out.slice(out.indexOf("/") + 1);
|
|
55
|
+
if (width < 60)
|
|
56
|
+
out = out.replace(/@.*$/, "");
|
|
57
|
+
if (width < 48)
|
|
58
|
+
out = out.replace(/-\d.*$/, "");
|
|
59
|
+
return out;
|
|
43
60
|
}
|
|
44
61
|
export class StatusBar {
|
|
45
62
|
source;
|
|
@@ -57,81 +74,106 @@ export class StatusBar {
|
|
|
57
74
|
setQueue(steering, followUp) {
|
|
58
75
|
this.queue = steering + followUp;
|
|
59
76
|
}
|
|
60
|
-
parts() {
|
|
77
|
+
parts(width) {
|
|
61
78
|
const theme = this.theme;
|
|
79
|
+
const g = theme.glyphs;
|
|
62
80
|
const session = this.source.session();
|
|
63
81
|
const state = session.state;
|
|
64
82
|
const stats = this.stats ?? session.getStats();
|
|
65
83
|
this.stats = stats;
|
|
66
84
|
const parts = [];
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
parts.push({ text:
|
|
85
|
+
/** 普通项整项 dim;`colored` 的项已自带颜色。 */
|
|
86
|
+
const tone = (text, colored) => colored ? text : theme.fg("dim", text);
|
|
87
|
+
const left = (text, priority, colored = false) => void parts.push({ text: tone(text, colored), priority, zone: "left" });
|
|
88
|
+
const right = (text, priority, colored = false) => void parts.push({ text: tone(text, colored), priority, zone: "right" });
|
|
89
|
+
const mode = permissionModeLabel(state.permissionMode);
|
|
90
|
+
left(state.permissionMode === "full-auto"
|
|
91
|
+
? theme.fg("warning", mode)
|
|
92
|
+
: state.permissionMode === "plan"
|
|
93
|
+
? theme.fg("accent", mode)
|
|
94
|
+
: theme.fg("text", mode), undefined, true);
|
|
95
|
+
if (width >= 40)
|
|
96
|
+
left("shift+tab 切换", 12);
|
|
97
|
+
const model = state.model === undefined ? "?" : abbreviateModel(formatModelRef(state.model), width);
|
|
98
|
+
right(theme.fg("accent", model), 0, true);
|
|
99
|
+
if (state.thinkingLevel !== "off")
|
|
100
|
+
right(state.thinkingLevel, 5);
|
|
70
101
|
const t = stats.tokens;
|
|
71
102
|
const prompt = t.input + t.cacheRead + t.cacheWrite;
|
|
103
|
+
const cache = stats.cache;
|
|
72
104
|
if (prompt + t.output > 0) {
|
|
73
|
-
|
|
74
|
-
const cache = stats.cache;
|
|
105
|
+
right(`${g.arrowUp}${formatTokens(prompt)} ${g.arrowDown}${formatTokens(t.output)}`, 6);
|
|
75
106
|
if (cache !== undefined)
|
|
76
|
-
|
|
107
|
+
right(cacheText(cache, g.warm), 7);
|
|
77
108
|
else if (stats.cacheHitRate !== undefined) {
|
|
78
|
-
|
|
109
|
+
right(`cache ${Math.round(stats.cacheHitRate * 100)}%`, 7);
|
|
79
110
|
}
|
|
80
111
|
if (stats.cost === undefined)
|
|
81
|
-
|
|
112
|
+
right("$?", 8);
|
|
82
113
|
else if (stats.cost > 0)
|
|
83
|
-
|
|
114
|
+
right(formatCost(stats.cost), 8);
|
|
84
115
|
if (cache !== undefined && cache.reBilledTokens > 0) {
|
|
85
116
|
const amount = cache.reBilledUsd === undefined
|
|
86
117
|
? `${formatTokens(cache.reBilledTokens)} tok`
|
|
87
118
|
: formatCost(cache.reBilledUsd);
|
|
88
|
-
|
|
119
|
+
right(theme.fg("warning", `rebill ${amount}`), 9, true);
|
|
89
120
|
}
|
|
90
121
|
}
|
|
91
|
-
|
|
92
|
-
const ctxText = ctx === undefined ? "ctx ?" : `ctx ${Math.round(ctx)}%`;
|
|
93
|
-
parts.push({
|
|
94
|
-
text: ctx !== undefined && ctx >= 90
|
|
95
|
-
? theme.fg("error", ctxText)
|
|
96
|
-
: ctx !== undefined && ctx >= 70
|
|
97
|
-
? theme.fg("warning", ctxText)
|
|
98
|
-
: ctxText,
|
|
99
|
-
priority: 1,
|
|
100
|
-
});
|
|
122
|
+
right(this.ctxText(stats.contextPercent, width), 1, true);
|
|
101
123
|
if (this.queue > 0)
|
|
102
|
-
|
|
103
|
-
const mode = `mode:${state.permissionMode}`;
|
|
104
|
-
parts.push({
|
|
105
|
-
text: state.permissionMode === "full-auto" ? theme.fg("warning", mode) : mode,
|
|
106
|
-
priority: 2,
|
|
107
|
-
});
|
|
124
|
+
right(theme.fg("warning", `queue ${this.queue}`), 4, true);
|
|
108
125
|
const codemode = this.source.codemode?.();
|
|
109
126
|
if (codemode !== undefined) {
|
|
110
127
|
const strict = this.source.sandboxStrict?.() ?? true;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
priority: 3,
|
|
114
|
-
});
|
|
128
|
+
const label = theme.fg("dim", `codemode ${codemode}`);
|
|
129
|
+
right(strict ? label : `${label} ${theme.fg("error", "net!")}`, 3, true);
|
|
115
130
|
}
|
|
116
|
-
|
|
131
|
+
const preset = this.source.preset();
|
|
132
|
+
if (preset !== "default")
|
|
133
|
+
right(`preset ${preset}`, 10);
|
|
117
134
|
const host = this.source.hostStatus?.();
|
|
118
135
|
if (host !== undefined && host.size > 0) {
|
|
119
136
|
const values = [...host.values()].filter((v) => v.trim() !== "");
|
|
120
137
|
if (values.length > 0)
|
|
121
|
-
|
|
138
|
+
right(`[${values.join(" · ")}]`, 11);
|
|
122
139
|
}
|
|
123
140
|
return parts;
|
|
124
141
|
}
|
|
142
|
+
ctxText(percent, width) {
|
|
143
|
+
const theme = this.theme;
|
|
144
|
+
if (percent === undefined)
|
|
145
|
+
return theme.fg("dim", "ctx ?");
|
|
146
|
+
const color = levelColor(percent / 100);
|
|
147
|
+
if (width >= METER_WIDTH) {
|
|
148
|
+
const meter = new Meter(percent / 100, { label: theme.fg("dim", "ctx"), theme });
|
|
149
|
+
return meter.render(80)[0] ?? "";
|
|
150
|
+
}
|
|
151
|
+
return theme.fg("dim", "ctx ") + theme.fg(color, `${Math.round(percent)}%`);
|
|
152
|
+
}
|
|
125
153
|
render(width) {
|
|
126
|
-
const
|
|
127
|
-
const sep =
|
|
154
|
+
const theme = this.theme;
|
|
155
|
+
const sep = theme.fg("dim", " · ");
|
|
128
156
|
const join = (list) => list.map((p) => p.text).join(sep);
|
|
129
|
-
let kept = parts;
|
|
130
|
-
|
|
131
|
-
|
|
157
|
+
let kept = this.parts(width);
|
|
158
|
+
const zones = () => [
|
|
159
|
+
join(kept.filter((p) => p.zone === "left")),
|
|
160
|
+
join(kept.filter((p) => p.zone === "right")),
|
|
161
|
+
];
|
|
162
|
+
const fits = () => {
|
|
163
|
+
const [l, r] = zones();
|
|
164
|
+
return visibleWidth(l) + (r === "" ? 0 : 3 + visibleWidth(r)) <= width;
|
|
165
|
+
};
|
|
166
|
+
while (!fits()) {
|
|
167
|
+
const droppable = kept.filter((p) => p.priority !== undefined);
|
|
168
|
+
if (droppable.length === 0)
|
|
169
|
+
break;
|
|
170
|
+
const drop = droppable.reduce((worst, p) => (p.priority > worst.priority ? p : worst));
|
|
132
171
|
kept = kept.filter((p) => p !== drop);
|
|
133
172
|
}
|
|
134
|
-
|
|
173
|
+
const [l, r] = zones();
|
|
174
|
+
const gap = width - visibleWidth(l) - visibleWidth(r);
|
|
175
|
+
const line = r === "" ? l : gap >= MIN_GAP ? l + " ".repeat(gap) + r : l + sep + r;
|
|
176
|
+
return [truncateToWidth(line, width)];
|
|
135
177
|
}
|
|
136
178
|
invalidate() {
|
|
137
179
|
this.stats = undefined;
|
|
@@ -0,0 +1,46 @@
|
|
|
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 type { ToolResult } from "../../tools/types.js";
|
|
12
|
+
import { type Theme } from "../../tui.js";
|
|
13
|
+
/** 工具输出 → 可安全显示的行。 */
|
|
14
|
+
export declare function cleanLines(text: string): string[];
|
|
15
|
+
export declare function flat(text: string, max?: number): string;
|
|
16
|
+
export declare function displayPath(path: string, cwd: string | undefined): string;
|
|
17
|
+
/** 标题行里的参数摘要。 */
|
|
18
|
+
export declare function toolSummary(name: string, args: unknown, cwd?: string): string;
|
|
19
|
+
/** 完成耗时:< 10s 一位小数(`2.1s`),< 60s 整秒,再长 `1m05s`。 */
|
|
20
|
+
export declare function formatDuration(ms: number): string;
|
|
21
|
+
export interface DiffRow {
|
|
22
|
+
kind: "hunk" | "add" | "del" | "ctx";
|
|
23
|
+
/** 原行(含 `+` / `-` / ` ` 前缀)。 */
|
|
24
|
+
text: string;
|
|
25
|
+
/** 行号:删除行取旧号,其余取新号;hunk 行没有。 */
|
|
26
|
+
num?: number;
|
|
27
|
+
}
|
|
28
|
+
/** 统一 diff → 行(去掉 `---` / `+++` 文件头),带行号。 */
|
|
29
|
+
export declare function parseDiff(diff: string): DiffRow[];
|
|
30
|
+
export declare function diffOf(result: ToolResult | undefined): string | undefined;
|
|
31
|
+
/** bash 完成后的正文:优先 `details.output`(不含退出码等附注)。 */
|
|
32
|
+
export declare function bashOutput(result: ToolResult): string | undefined;
|
|
33
|
+
export interface SummaryInput {
|
|
34
|
+
name: string;
|
|
35
|
+
args: unknown;
|
|
36
|
+
result: ToolResult;
|
|
37
|
+
isError: boolean;
|
|
38
|
+
/** 结果正文(已清洗)。 */
|
|
39
|
+
lines: readonly string[];
|
|
40
|
+
/** 完成耗时(ms);重放的历史调用没有,bash 取 `details.wall_time_seconds`。 */
|
|
41
|
+
elapsedMs: number | undefined;
|
|
42
|
+
/** 已显示的内层调用数(codemode 没有 details 时用)。 */
|
|
43
|
+
nestedCount?: number;
|
|
44
|
+
}
|
|
45
|
+
/** 完成后的一行结果摘要(已着色,不含 `⎿`)。 */
|
|
46
|
+
export declare function resultSummary(input: SummaryInput, theme: Theme): string;
|
|
@@ -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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
* -
|
|
12
|
-
*
|
|
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:切换全部调用的展开状态。 */
|