@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
|
@@ -11,30 +11,33 @@
|
|
|
11
11
|
* - 晚绑定:`runtime.approvals.setUiBroker`(审批对话框)与 `runtime.notifier.set`(宿主通知进消息区),
|
|
12
12
|
* 退出时撤下。
|
|
13
13
|
* - 键位:Enter 发送(运行中 = steer),其余应用级键位见 `key-dispatch.ts`。
|
|
14
|
-
* -
|
|
14
|
+
* - 启动头按 `ui.quietStartup`(startup-header.ts):normal 框 + 模型 / 目录 / 模式 / 资源清单(窄屏或
|
|
15
|
+
* `ui.compact` 去框),header 一行,silent 不输出。
|
|
15
16
|
*/
|
|
16
17
|
import { promptImages, sessionModel } from "../image-input.js";
|
|
17
18
|
import { join } from "node:path";
|
|
18
19
|
import { AgentSessionImpl } from "../../agent/session.js";
|
|
19
20
|
import { currentSession, switchSession } from "../../cli/compose-session.js";
|
|
20
|
-
import {
|
|
21
|
+
import { startupInfo, startupScreenLevel } from "../../cli/startup-screen.js";
|
|
21
22
|
import { KEYBINDINGS_FILE } from "../../config/paths.js";
|
|
22
23
|
import { AmaError, isAmaError } from "../../errors.js";
|
|
23
24
|
import { detectSandboxCapability } from "../../codemode/capability.js";
|
|
24
25
|
import { effectiveCodemodeMode } from "../../tools/presets.js";
|
|
25
|
-
import { Container, Editor, Keybindings, Loader, ProcessTerminal, Spacer, TUI, Text, createTheme, detectCapabilities, loadKeybindingsFile, truncateToWidth, } from "../../tui.js";
|
|
26
|
+
import { Container, Editor, Keybindings, Loader, ProcessTerminal, Spacer, TUI, Text, createTheme, detectCapabilities, resolveAscii, resolveThemeName, loadKeybindingsFile, truncateToWidth, } from "../../tui.js";
|
|
26
27
|
import { cacheEventNotice, cacheNoticesEnabled } from "../session-report.js";
|
|
27
28
|
import { onTerminationSignals } from "../shared.js";
|
|
28
29
|
import { ApprovalDialogBroker, approvalOutcomeText } from "./approval-dialog.js";
|
|
29
30
|
import { ALL_COMMANDS, runInteractiveCommand } from "./commands.js";
|
|
30
31
|
import { InteractiveCompletion } from "./completion.js";
|
|
31
32
|
import { createKeyDispatch } from "./key-dispatch.js";
|
|
32
|
-
import { MessageView } from "./message-view.js";
|
|
33
|
+
import { MessageView, exitSummaryLines } from "./message-view.js";
|
|
33
34
|
import { openPicker } from "./pickers.js";
|
|
35
|
+
import { StartupHeader } from "./startup-header.js";
|
|
36
|
+
import { QueueView, RunIndicator } from "./run-indicator.js";
|
|
34
37
|
import { StatusBar } from "./status-bar.js";
|
|
35
38
|
import { ToolTracker } from "./tool-view.js";
|
|
36
39
|
const HINT_MS = 2500;
|
|
37
|
-
const
|
|
40
|
+
const EDITOR_PLACEHOLDER = "输入消息,/ 命令,@ 文件,Shift+Enter 换行";
|
|
38
41
|
function processTerminal() {
|
|
39
42
|
const stdin = process.stdin;
|
|
40
43
|
if (stdin.isTTY !== true || typeof stdin.setRawMode !== "function" || !process.stdout.isTTY) {
|
|
@@ -59,17 +62,17 @@ class HintLine {
|
|
|
59
62
|
}
|
|
60
63
|
invalidate() { }
|
|
61
64
|
}
|
|
62
|
-
function oneLine(text) {
|
|
63
|
-
return text.replace(/\s+/g, " ").trim();
|
|
64
|
-
}
|
|
65
65
|
export function runInteractiveMode(runtime, context, options = {}) {
|
|
66
66
|
const terminal = options.terminal ?? processTerminal();
|
|
67
67
|
const ui = runtime.config.ui ?? {};
|
|
68
|
+
const env = context.io.env;
|
|
68
69
|
const theme = options.theme ??
|
|
69
|
-
createTheme(ui.theme
|
|
70
|
-
caps: detectCapabilities(
|
|
70
|
+
createTheme(resolveThemeName(ui.theme, env), {
|
|
71
|
+
caps: detectCapabilities(env, context.io.stdoutIsTTY),
|
|
72
|
+
ascii: resolveAscii(ui.ascii, env),
|
|
71
73
|
});
|
|
72
74
|
const now = options.now ?? Date.now;
|
|
75
|
+
const startedAt = now();
|
|
73
76
|
const startupWarnings = [];
|
|
74
77
|
const keys = options.keybindings ?? loadKeys(runtime, (m) => startupWarnings.push(m));
|
|
75
78
|
let session = currentSession(runtime);
|
|
@@ -78,20 +81,25 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
78
81
|
theme,
|
|
79
82
|
...(ui.showThinking !== undefined ? { showThinking: ui.showThinking } : {}),
|
|
80
83
|
...(ui.markdown !== undefined ? { markdown: ui.markdown } : {}),
|
|
84
|
+
...(ui.compact === true ? { compact: true } : {}),
|
|
81
85
|
});
|
|
82
86
|
const tools = new ToolTracker({
|
|
83
87
|
theme,
|
|
84
88
|
cwd: session.state.cwd,
|
|
85
89
|
getTool: (name) => session.getTools().find((tool) => tool.name === name),
|
|
90
|
+
now,
|
|
91
|
+
spinner: () => loader.frame,
|
|
86
92
|
});
|
|
87
|
-
const
|
|
93
|
+
const queueView = new QueueView(theme);
|
|
88
94
|
const loaderSlot = new Container();
|
|
89
95
|
const loader = new Loader(() => tui.requestRender(), {
|
|
90
96
|
theme,
|
|
91
|
-
message: "
|
|
97
|
+
message: "思考中",
|
|
92
98
|
now,
|
|
99
|
+
...(ui.animation === false ? { animation: false } : {}),
|
|
93
100
|
...(options.spinnerIntervalMs !== undefined ? { intervalMs: options.spinnerIntervalMs } : {}),
|
|
94
101
|
});
|
|
102
|
+
loader.onFrame(() => tools.tick());
|
|
95
103
|
const hint = new HintLine();
|
|
96
104
|
let sandboxStrict;
|
|
97
105
|
const status = new StatusBar({
|
|
@@ -119,6 +127,7 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
119
127
|
theme,
|
|
120
128
|
keybindings: keys,
|
|
121
129
|
maxVisibleLines: 8,
|
|
130
|
+
placeholder: EDITOR_PLACEHOLDER,
|
|
122
131
|
autocomplete: completion,
|
|
123
132
|
requestRender: () => tui.requestRender(),
|
|
124
133
|
...(historyFile !== undefined ? { historyFile } : {}),
|
|
@@ -126,15 +135,13 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
126
135
|
});
|
|
127
136
|
tui.addChild(view);
|
|
128
137
|
tui.addChild(new Spacer());
|
|
129
|
-
tui.addChild(
|
|
138
|
+
tui.addChild(queueView);
|
|
130
139
|
tui.addChild(loaderSlot);
|
|
131
140
|
tui.addChild(editor);
|
|
132
141
|
tui.addChild(hint);
|
|
133
142
|
tui.addChild(status);
|
|
134
143
|
// ---- 小工具 ---------------------------------------------------------------
|
|
135
144
|
let finished = false;
|
|
136
|
-
let running = false;
|
|
137
|
-
let compacting = false;
|
|
138
145
|
let hintTimer;
|
|
139
146
|
let resolveExit = () => undefined;
|
|
140
147
|
const render = () => tui.requestRender();
|
|
@@ -156,47 +163,20 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
156
163
|
}
|
|
157
164
|
render();
|
|
158
165
|
};
|
|
159
|
-
const
|
|
160
|
-
const active = running || compacting;
|
|
161
|
-
if (active && loaderSlot.children.length === 0) {
|
|
162
|
-
loader.setMessage((compacting && !running ? "压缩上下文" : "工作中") + theme.fg("dim", " · Esc 中断"));
|
|
163
|
-
loaderSlot.addChild(loader);
|
|
164
|
-
loader.start();
|
|
165
|
-
}
|
|
166
|
-
else if (!active && loaderSlot.children.length > 0) {
|
|
167
|
-
loader.stop();
|
|
168
|
-
loaderSlot.clear();
|
|
169
|
-
}
|
|
170
|
-
render();
|
|
171
|
-
};
|
|
166
|
+
const indicator = new RunIndicator({ theme, loader, slot: loaderSlot, tools, render });
|
|
172
167
|
const setQueue = (steering, followUp) => {
|
|
173
168
|
status.setQueue(steering.length, followUp.length);
|
|
174
|
-
|
|
175
|
-
...steering.map((t) => `↳ steer ${oneLine(t)}`),
|
|
176
|
-
...followUp.map((t) => `↳ followUp ${oneLine(t)}`),
|
|
177
|
-
];
|
|
178
|
-
const shown = rows.slice(-QUEUE_PREVIEW);
|
|
179
|
-
if (rows.length > shown.length)
|
|
180
|
-
shown.unshift(`… 另 ${rows.length - shown.length} 条`);
|
|
181
|
-
if (rows.length > 0)
|
|
182
|
-
shown.push("Alt+↑ 取回 · Esc 全部回填并中断");
|
|
183
|
-
queueText.setText(shown.map((l) => theme.fg("dim", l)).join("\n"));
|
|
169
|
+
queueView.setQueue(steering, followUp);
|
|
184
170
|
render();
|
|
185
171
|
};
|
|
186
172
|
// ---- 会话事件 -------------------------------------------------------------
|
|
187
173
|
const onEvent = (event) => {
|
|
188
174
|
switch (event.type) {
|
|
189
|
-
case "agent_start":
|
|
190
|
-
running = true;
|
|
191
|
-
syncLoader();
|
|
192
|
-
return;
|
|
193
175
|
case "agent_settled":
|
|
194
|
-
running = false;
|
|
195
176
|
if (event.warning !== undefined)
|
|
196
177
|
view.addNotice("warn", event.warning);
|
|
197
178
|
status.refresh();
|
|
198
|
-
|
|
199
|
-
return;
|
|
179
|
+
break;
|
|
200
180
|
case "message_start": {
|
|
201
181
|
const message = event.message;
|
|
202
182
|
if (message.role === "user")
|
|
@@ -206,26 +186,22 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
206
186
|
else if (message.role === "custom" && message.display) {
|
|
207
187
|
view.addNotice("info", typeof message.content === "string" ? message.content : "");
|
|
208
188
|
}
|
|
209
|
-
|
|
210
|
-
return;
|
|
189
|
+
break;
|
|
211
190
|
}
|
|
212
191
|
case "message_update":
|
|
213
192
|
view.updateAssistant(event.message);
|
|
214
|
-
|
|
215
|
-
return;
|
|
193
|
+
break;
|
|
216
194
|
case "message_end":
|
|
217
195
|
if (event.message.role === "assistant") {
|
|
218
196
|
view.endAssistant(event.message);
|
|
219
197
|
status.refresh();
|
|
220
198
|
}
|
|
221
|
-
|
|
222
|
-
return;
|
|
199
|
+
break;
|
|
223
200
|
case "tool_execution_start": {
|
|
224
201
|
const started = tools.start(event);
|
|
225
202
|
if (started.topLevel)
|
|
226
|
-
view.
|
|
227
|
-
|
|
228
|
-
return;
|
|
203
|
+
view.addTool(started.view);
|
|
204
|
+
break;
|
|
229
205
|
}
|
|
230
206
|
case "tool_execution_update":
|
|
231
207
|
tools.update(event.toolCallId, event.partial);
|
|
@@ -233,17 +209,11 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
233
209
|
return;
|
|
234
210
|
case "tool_execution_end":
|
|
235
211
|
tools.end(event.toolCallId, event.result, event.isError);
|
|
236
|
-
|
|
237
|
-
return;
|
|
212
|
+
break;
|
|
238
213
|
case "queue_update":
|
|
239
214
|
setQueue(event.steering, event.followUp);
|
|
240
215
|
return;
|
|
241
|
-
case "compaction_start":
|
|
242
|
-
compacting = true;
|
|
243
|
-
syncLoader();
|
|
244
|
-
return;
|
|
245
216
|
case "compaction_end":
|
|
246
|
-
compacting = false;
|
|
247
217
|
if (event.result !== undefined)
|
|
248
218
|
view.addCompaction(event.result);
|
|
249
219
|
else if (event.error !== undefined)
|
|
@@ -251,17 +221,14 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
251
221
|
else if (event.aborted)
|
|
252
222
|
view.addNotice("info", "压缩已取消");
|
|
253
223
|
status.refresh();
|
|
254
|
-
|
|
255
|
-
return;
|
|
224
|
+
break;
|
|
256
225
|
case "auto_retry_start":
|
|
257
226
|
view.addRetry(event.attempt, event.maxAttempts, event.delayMs, event.errorMessage);
|
|
258
|
-
|
|
259
|
-
return;
|
|
227
|
+
break;
|
|
260
228
|
case "auto_retry_end":
|
|
261
229
|
if (!event.success)
|
|
262
230
|
view.addRetryFailed(event.finalError);
|
|
263
|
-
|
|
264
|
-
return;
|
|
231
|
+
break;
|
|
265
232
|
case "cache_miss":
|
|
266
233
|
case "context_pressure": {
|
|
267
234
|
const shown = cacheEventNotice(event, cacheNoticesEnabled(session));
|
|
@@ -280,15 +247,21 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
280
247
|
render();
|
|
281
248
|
return;
|
|
282
249
|
default:
|
|
283
|
-
|
|
250
|
+
break;
|
|
284
251
|
}
|
|
252
|
+
indicator.onEvent(event);
|
|
253
|
+
render();
|
|
285
254
|
};
|
|
286
255
|
let unsubscribe = session.subscribe(onEvent);
|
|
287
|
-
const
|
|
288
|
-
|
|
256
|
+
const startupLevel = startupScreenLevel(runtime);
|
|
257
|
+
const home = env["HOME"] ?? env["USERPROFILE"];
|
|
258
|
+
const info = startupInfo(runtime, home);
|
|
259
|
+
const header = (level) => new StartupHeader(info, { theme, level, ...(ui.compact === true ? { compact: true } : {}) });
|
|
260
|
+
/** 清空消息区(切换会话、/tree):只留一行头。 */
|
|
289
261
|
const resetView = () => {
|
|
290
262
|
view.reset();
|
|
291
|
-
|
|
263
|
+
if (startupLevel !== "silent")
|
|
264
|
+
view.add(header("header"));
|
|
292
265
|
};
|
|
293
266
|
const replay = () => {
|
|
294
267
|
view.replay(session.messages, {
|
|
@@ -308,11 +281,9 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
308
281
|
resetView();
|
|
309
282
|
tools.clear();
|
|
310
283
|
replay();
|
|
311
|
-
running = false;
|
|
312
|
-
compacting = false;
|
|
313
284
|
setQueue([], []);
|
|
314
285
|
status.refresh();
|
|
315
|
-
|
|
286
|
+
indicator.reset();
|
|
316
287
|
if (next instanceof AgentSessionImpl)
|
|
317
288
|
next.announceStart(reason);
|
|
318
289
|
};
|
|
@@ -356,6 +327,13 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
356
327
|
columns: () => terminal.columns,
|
|
357
328
|
}, spec),
|
|
358
329
|
notice,
|
|
330
|
+
panel: (component) => {
|
|
331
|
+
view.add(component);
|
|
332
|
+
render();
|
|
333
|
+
},
|
|
334
|
+
theme: () => theme,
|
|
335
|
+
env,
|
|
336
|
+
...(home !== undefined ? { home } : {}),
|
|
359
337
|
setEditorText: (text) => {
|
|
360
338
|
editor.setText(text);
|
|
361
339
|
render();
|
|
@@ -388,7 +366,7 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
388
366
|
send(text, via);
|
|
389
367
|
}
|
|
390
368
|
function send(text, via) {
|
|
391
|
-
if (!running)
|
|
369
|
+
if (!indicator.running)
|
|
392
370
|
startPrompt(text);
|
|
393
371
|
else if (via === "followUp")
|
|
394
372
|
void session.followUp(text).catch(() => undefined);
|
|
@@ -403,9 +381,10 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
403
381
|
status,
|
|
404
382
|
session: () => session,
|
|
405
383
|
inactive: () => finished || tui.hasOverlay,
|
|
406
|
-
busy: () =>
|
|
384
|
+
busy: () => indicator.busy,
|
|
407
385
|
now,
|
|
408
386
|
showHint,
|
|
387
|
+
onExpandToggle: (expanded) => view.setThinkingExpanded(expanded),
|
|
409
388
|
submit: (text, via) => submit(text, via),
|
|
410
389
|
runCommand: (line) => void runCommand(line),
|
|
411
390
|
exit: (code) => exit(code),
|
|
@@ -419,11 +398,13 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
419
398
|
showOverlay: (component) => tui.showOverlay(component, { anchor: "bottom" }),
|
|
420
399
|
onOpen: () => {
|
|
421
400
|
editor.disableSubmit = true;
|
|
422
|
-
|
|
401
|
+
tools.setAwaiting(true);
|
|
402
|
+
indicator.setApproval(true);
|
|
423
403
|
},
|
|
424
404
|
onClose: () => {
|
|
425
405
|
editor.disableSubmit = false;
|
|
426
|
-
|
|
406
|
+
tools.setAwaiting(false);
|
|
407
|
+
indicator.setApproval(false);
|
|
427
408
|
},
|
|
428
409
|
report: (request, outcome) => {
|
|
429
410
|
if (outcome === "deny" || outcome === "cancelled") {
|
|
@@ -439,9 +420,11 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
439
420
|
finished = true;
|
|
440
421
|
if (hintTimer !== undefined)
|
|
441
422
|
clearTimeout(hintTimer);
|
|
442
|
-
hint.setText("");
|
|
443
423
|
loader.stop();
|
|
444
|
-
|
|
424
|
+
// 退出摘要留在回滚里;输入框、状态栏等底部区域撤掉,屏幕停在摘要下面
|
|
425
|
+
view.addExitSummary(exitSummaryLines(session.getStats(), now() - startedAt, theme));
|
|
426
|
+
tui.clear();
|
|
427
|
+
tui.addChild(view);
|
|
445
428
|
offSignals();
|
|
446
429
|
runtime.approvals.setUiBroker(undefined);
|
|
447
430
|
runtime.notifier.set(undefined);
|
|
@@ -455,7 +438,8 @@ export function runInteractiveMode(runtime, context, options = {}) {
|
|
|
455
438
|
resolveExit(code);
|
|
456
439
|
});
|
|
457
440
|
}
|
|
458
|
-
|
|
441
|
+
if (startupLevel !== "silent")
|
|
442
|
+
view.add(header(startupLevel));
|
|
459
443
|
for (const warning of startupWarnings)
|
|
460
444
|
view.addNotice("warn", warning);
|
|
461
445
|
replay();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 编辑器之前的输入监听:Ctrl+C 清空输入 / 再按退出、Esc 中断(clearQueue 回填编辑器后 abort)、
|
|
5
5
|
* Ctrl+D 空输入退出、Alt+Enter followUp、Alt+↑ 取回最后一条排队消息、Shift+Tab 循环权限模式、
|
|
6
|
-
* Ctrl+O
|
|
6
|
+
* Ctrl+O 展开工具输出与思考块、Ctrl+L 模型、Ctrl+T 思考级别。键位由 `keybindings.json` 覆盖。
|
|
7
7
|
*/
|
|
8
8
|
import type { AgentSession } from "../../agent/types.js";
|
|
9
9
|
import type { Editor, Keybindings } from "../../tui.js";
|
|
@@ -21,6 +21,8 @@ export interface KeyDispatchDeps {
|
|
|
21
21
|
busy(): boolean;
|
|
22
22
|
now(): number;
|
|
23
23
|
showHint(text: string): void;
|
|
24
|
+
/** Ctrl+O 之后(消息区思考块跟着展开 / 折叠)。 */
|
|
25
|
+
onExpandToggle?(expanded: boolean): void;
|
|
24
26
|
submit(text: string, via: "followUp"): void;
|
|
25
27
|
runCommand(line: string): void;
|
|
26
28
|
exit(code: number): void;
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 编辑器之前的输入监听:Ctrl+C 清空输入 / 再按退出、Esc 中断(clearQueue 回填编辑器后 abort)、
|
|
5
5
|
* Ctrl+D 空输入退出、Alt+Enter followUp、Alt+↑ 取回最后一条排队消息、Shift+Tab 循环权限模式、
|
|
6
|
-
* Ctrl+O
|
|
6
|
+
* Ctrl+O 展开工具输出与思考块、Ctrl+L 模型、Ctrl+T 思考级别。键位由 `keybindings.json` 覆盖。
|
|
7
7
|
*/
|
|
8
8
|
import { ExitCode } from "../../cli/exit-codes.js";
|
|
9
|
-
import {
|
|
9
|
+
import { nextCycleMode, permissionModeLabel } from "../../permissions/modes.js";
|
|
10
10
|
const DOUBLE_CTRL_C_MS = 1500;
|
|
11
11
|
/** 返回输入监听器:已处理返回 true,交给编辑器返回 false。 */
|
|
12
12
|
export function createKeyDispatch(deps) {
|
|
@@ -39,12 +39,10 @@ export function createKeyDispatch(deps) {
|
|
|
39
39
|
};
|
|
40
40
|
const cyclePermission = () => {
|
|
41
41
|
const session = deps.session();
|
|
42
|
-
const
|
|
43
|
-
const index = modes.indexOf(session.state.permissionMode);
|
|
44
|
-
const next = modes[(index + 1) % modes.length];
|
|
42
|
+
const next = nextCycleMode(session.state.permissionMode);
|
|
45
43
|
session.setPermissionMode(next);
|
|
46
44
|
status.refresh();
|
|
47
|
-
deps.showHint(`权限模式:${next}`);
|
|
45
|
+
deps.showHint(`权限模式:${permissionModeLabel(next)}`);
|
|
48
46
|
};
|
|
49
47
|
return (data) => {
|
|
50
48
|
if (deps.inactive())
|
|
@@ -91,6 +89,7 @@ export function createKeyDispatch(deps) {
|
|
|
91
89
|
}
|
|
92
90
|
if (is("app.tools.expand")) {
|
|
93
91
|
const expanded = tools.toggleExpanded();
|
|
92
|
+
deps.onExpandToggle?.(expanded);
|
|
94
93
|
deps.showHint(expanded ? "工具输出:展开" : "工具输出:折叠");
|
|
95
94
|
return true;
|
|
96
95
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* = steer;Ctrl+C 运行中中断、空闲时连按两次退出(130);空行 Ctrl+D 退出;审批 y / a / N 单键问答。
|
|
6
6
|
* 问句之前逐行打印执行前预览(`request.preview`,W3-B9a-2)。
|
|
7
7
|
* - 否则(管道):逐行读 stdin,每行依次执行(等上一条运行结束);没有审批 UI,ask → deny。
|
|
8
|
+
* 模型错误在运行结束时只打印一次(重试中只显示 ↻);有运行最终失败时退出码 1。
|
|
8
9
|
* - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选。
|
|
9
10
|
*/
|
|
10
11
|
import type { ModeContext } from "../../../cli/deps.js";
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* = steer;Ctrl+C 运行中中断、空闲时连按两次退出(130);空行 Ctrl+D 退出;审批 y / a / N 单键问答。
|
|
6
6
|
* 问句之前逐行打印执行前预览(`request.preview`,W3-B9a-2)。
|
|
7
7
|
* - 否则(管道):逐行读 stdin,每行依次执行(等上一条运行结束);没有审批 UI,ask → deny。
|
|
8
|
+
* 模型错误在运行结束时只打印一次(重试中只显示 ↻);有运行最终失败时退出码 1。
|
|
8
9
|
* - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选。
|
|
9
10
|
*/
|
|
10
11
|
import { promptImages, sessionModel } from "../../image-input.js";
|
|
@@ -68,6 +69,7 @@ export async function runLineMode(runtime, context, options = {}) {
|
|
|
68
69
|
}
|
|
69
70
|
catch (error) {
|
|
70
71
|
printer.endLine();
|
|
72
|
+
printer.failures++;
|
|
71
73
|
err(`ama: ${errorText(error)}\n`);
|
|
72
74
|
}
|
|
73
75
|
return undefined;
|
|
@@ -78,7 +80,7 @@ export async function runLineMode(runtime, context, options = {}) {
|
|
|
78
80
|
printer.endLine();
|
|
79
81
|
};
|
|
80
82
|
if (!raw)
|
|
81
|
-
return runPiped(stdin, context, handle, () => session, () => unsubscribe());
|
|
83
|
+
return runPiped(stdin, context, handle, () => session, () => unsubscribe(), () => printer.failures > 0);
|
|
82
84
|
// ---- raw 终端 ----
|
|
83
85
|
const tty = stdin;
|
|
84
86
|
editor = new LineEditor({ prompt: "› ", write: (t) => io.stdout(t) });
|
|
@@ -182,8 +184,8 @@ export async function runLineMode(runtime, context, options = {}) {
|
|
|
182
184
|
ed.render();
|
|
183
185
|
});
|
|
184
186
|
}
|
|
185
|
-
/** 管道:逐行依次执行;stdin
|
|
186
|
-
function runPiped(stdin, context, handle, session, unsubscribe) {
|
|
187
|
+
/** 管道:逐行依次执行;stdin 结束后等最后一条跑完退出——有失败的运行时 1,否则 0。 */
|
|
188
|
+
function runPiped(stdin, context, handle, session, unsubscribe, failed) {
|
|
187
189
|
return new Promise((resolve) => {
|
|
188
190
|
let chain = Promise.resolve(undefined);
|
|
189
191
|
let finished = false;
|
|
@@ -205,7 +207,7 @@ function runPiped(stdin, context, handle, session, unsubscribe) {
|
|
|
205
207
|
});
|
|
206
208
|
};
|
|
207
209
|
const reader = createLineReader(stdin, enqueue, () => {
|
|
208
|
-
void chain.then(() => finish(ExitCode.Ok));
|
|
210
|
+
void chain.then(() => finish(failed() ? ExitCode.RuntimeError : ExitCode.Ok));
|
|
209
211
|
});
|
|
210
212
|
const offSignals = onTerminationSignals((code) => {
|
|
211
213
|
void session()
|
|
@@ -23,6 +23,10 @@ export declare class EventPrinter {
|
|
|
23
23
|
private readonly err;
|
|
24
24
|
private readonly options;
|
|
25
25
|
private midLine;
|
|
26
|
+
/** 本次运行的模型错误:等 agent_settled 再打(重试中的失败尝试只显示 ↻ 那一行)。 */
|
|
27
|
+
private pendingError;
|
|
28
|
+
/** 最终失败的运行次数(管道模式据此返回退出码 1)。 */
|
|
29
|
+
failures: number;
|
|
26
30
|
constructor(out: (text: string) => void, err: (text: string) => void, options?: EventPrinterOptions);
|
|
27
31
|
private line;
|
|
28
32
|
/** 收尾:保证光标在行首。 */
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { THINKING_LEVELS } from "../../../ai/thinking.js";
|
|
6
6
|
import { listSessions } from "../../../cli/compose-store.js";
|
|
7
|
-
import {
|
|
7
|
+
import { AUTO_LAYER_TEXT, permissionModeLines } from "../../../permissions/modes.js";
|
|
8
8
|
import { cacheEventNotice, warmSentNotice } from "../../session-report.js";
|
|
9
9
|
const SUMMARY_KEYS = ["command", "path", "pattern", "file_path", "url", "description", "name"];
|
|
10
10
|
/** 工具参数的一行摘要(第一个常见字段,折叠换行、截到 80 字符)。 */
|
|
@@ -24,11 +24,14 @@ export function argsSummary(args) {
|
|
|
24
24
|
export function approvalQuestion(request) {
|
|
25
25
|
const task = (request.context?.depth ?? 0) > 0 ? "[task] " : "";
|
|
26
26
|
const summary = argsSummary(request.input);
|
|
27
|
+
const auto = request.autoDecision;
|
|
27
28
|
const why = request.reason === "dangerous"
|
|
28
29
|
? "(危险命令)"
|
|
29
30
|
: request.hookReason !== undefined
|
|
30
31
|
? `(${request.hookReason})`
|
|
31
|
-
:
|
|
32
|
+
: auto !== undefined
|
|
33
|
+
? `(Auto ${AUTO_LAYER_TEXT[auto.layer]}:${auto.reason})`
|
|
34
|
+
: "";
|
|
32
35
|
return `${task}允许 ${request.toolName}${summary !== "" ? ` ${summary}` : ""}${why}?[y 允许 / a 本会话都允许 / N 拒绝] `;
|
|
33
36
|
}
|
|
34
37
|
/** `AMA_LOG=info|debug`。 */
|
|
@@ -40,6 +43,10 @@ export class EventPrinter {
|
|
|
40
43
|
err;
|
|
41
44
|
options;
|
|
42
45
|
midLine = false;
|
|
46
|
+
/** 本次运行的模型错误:等 agent_settled 再打(重试中的失败尝试只显示 ↻ 那一行)。 */
|
|
47
|
+
pendingError;
|
|
48
|
+
/** 最终失败的运行次数(管道模式据此返回退出码 1)。 */
|
|
49
|
+
failures = 0;
|
|
43
50
|
constructor(out, err, options = {}) {
|
|
44
51
|
this.out = out;
|
|
45
52
|
this.err = err;
|
|
@@ -72,9 +79,13 @@ export class EventPrinter {
|
|
|
72
79
|
if (event.message.role === "assistant") {
|
|
73
80
|
this.endLine();
|
|
74
81
|
if (event.message.stopReason === "error")
|
|
75
|
-
this.
|
|
82
|
+
this.pendingError = event.message.errorMessage ?? "模型调用失败";
|
|
76
83
|
}
|
|
77
84
|
return;
|
|
85
|
+
case "agent_end":
|
|
86
|
+
if (event.willRetry)
|
|
87
|
+
this.pendingError = undefined;
|
|
88
|
+
return;
|
|
78
89
|
case "tool_execution_start": {
|
|
79
90
|
const summary = argsSummary(event.args);
|
|
80
91
|
this.line(`● ${event.toolName}${summary !== "" ? ` ${summary}` : ""}`);
|
|
@@ -112,11 +123,19 @@ export class EventPrinter {
|
|
|
112
123
|
this.line(`ama: ${text}`, true);
|
|
113
124
|
return;
|
|
114
125
|
}
|
|
115
|
-
case "agent_settled":
|
|
126
|
+
case "agent_settled": {
|
|
116
127
|
this.endLine();
|
|
117
|
-
|
|
128
|
+
const error = this.pendingError;
|
|
129
|
+
this.pendingError = undefined;
|
|
130
|
+
if (error !== undefined) {
|
|
131
|
+
this.failures++;
|
|
132
|
+
this.line(`ama: 错误:${error}`, true);
|
|
133
|
+
}
|
|
134
|
+
// 失败时 warning 就是同一条错误文本,不再重复打印
|
|
135
|
+
if (event.warning !== undefined && event.warning !== error)
|
|
118
136
|
this.line(`ama: ${event.warning}`, true);
|
|
119
137
|
return;
|
|
138
|
+
}
|
|
120
139
|
default:
|
|
121
140
|
return;
|
|
122
141
|
}
|
|
@@ -164,7 +183,10 @@ export async function pickHint(what, runtime, session) {
|
|
|
164
183
|
].join("\n");
|
|
165
184
|
}
|
|
166
185
|
case "permission":
|
|
167
|
-
return
|
|
186
|
+
return [
|
|
187
|
+
"Mode(/permission <模式>):",
|
|
188
|
+
...permissionModeLines(session.state.permissionMode, runtime.config.permission?.mode ?? "default").map((line) => ` ${line}`),
|
|
189
|
+
].join("\n");
|
|
168
190
|
case "thinking":
|
|
169
191
|
return `思考级别(/thinking <级别>):${THINKING_LEVELS.join(" | ")};当前 ${session.state.thinkingLevel}`;
|
|
170
192
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 消息区(设计 §12.5)。[B7]
|
|
3
3
|
*
|
|
4
|
-
* 一个纵向 `Container
|
|
5
|
-
*
|
|
4
|
+
* 一个纵向 `Container`,块与块之间空一行(终端界面视觉设计 v1 §1 原则 2:相邻工具调用之间不空行,
|
|
5
|
+
* `ui.compact` 时全部不空行):
|
|
6
|
+
* - 用户消息 `› 文本`(续行缩进 2 列);插话 / 之后 / 宿主注入按 `UserMessage.origin` 标
|
|
7
|
+
* `↳ 插话` / `↳ 之后` / `↳ 宿主`(整行 muted,其它 origin 原样显示);
|
|
6
8
|
* - 助手消息按内容块渲染:文本走 `Markdown`(`ui.markdown: false` 时纯文本),流式时只改末块
|
|
7
|
-
* (Markdown 自己按块缓存);思考块按 `ui.showThinking`:`collapsed`(缺省)一行
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* (Markdown 自己按块缓存);思考块按 `ui.showThinking`:`collapsed`(缺省)一行 `✻ 思考 · N token`
|
|
10
|
+
* (流式中 `✻ 思考中…`),Ctrl+O 展开为标题 + 缩进 2 列的正文(最多 60 行);`full` 总是展开且不限行;
|
|
11
|
+
* `hidden` 不显示;
|
|
12
|
+
* - 压缩摘要卡(左竖条 Card)、重试提示、Hook 阻止提示、宿主 / 系统通知;字形取 `theme.glyphs`;
|
|
10
13
|
* - `replay()` 把已有消息(恢复会话、切换会话、/tree 之后)重新画出来,工具调用交给调用方构造。
|
|
11
14
|
*
|
|
12
15
|
* 滚动交给终端回滚:消息区只追加,切换会话时整体清空重画。
|
|
13
16
|
*/
|
|
14
17
|
import type { AssistantMessage, ContentBlock, ToolCallBlock, UserMessage } from "../../ai/types.js";
|
|
15
|
-
import type { CompactionResult, ToolResultMessage } from "../../agent/types.js";
|
|
18
|
+
import type { CompactionResult, SessionStats, ToolResultMessage } from "../../agent/types.js";
|
|
16
19
|
import type { AgentMessage } from "../../session/types.js";
|
|
17
20
|
import { Container, type Component, type Theme } from "../../tui.js";
|
|
18
21
|
export type ThinkingDisplay = "full" | "collapsed" | "hidden";
|
|
@@ -22,6 +25,27 @@ export interface MessageViewOptions {
|
|
|
22
25
|
showThinking?: ThinkingDisplay;
|
|
23
26
|
/** false:助手文本不做 Markdown 渲染。 */
|
|
24
27
|
markdown?: boolean;
|
|
28
|
+
/** `ui.compact`:块间不空行。 */
|
|
29
|
+
compact?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 退出摘要(§3.14):`─ 会话 <id> · 时长 · N 回合 · ↑in ↓out · cache x% · $y(重计费 $z)` 与
|
|
33
|
+
* ` 恢复:ama --resume <id>`。整体 dim,会话 id 与恢复命令正文色;0 回合只到「回合」;会话没落盘不给恢复行。
|
|
34
|
+
*/
|
|
35
|
+
export declare function exitSummaryLines(stats: Pick<SessionStats, "sessionId" | "sessionFile" | "userMessages" | "tokens" | "cost" | "cache" | "cacheHitRate">, elapsedMs: number, theme: Theme): string[];
|
|
36
|
+
/** 思考块展开时的正文行数上限(`full` 不限)。 */
|
|
37
|
+
export declare const THINKING_EXPANDED_LINES = 60;
|
|
38
|
+
/** origin → 中文标签(未知 origin 原样显示)。 */
|
|
39
|
+
export declare const ORIGIN_LABELS: Readonly<Record<string, string>>;
|
|
40
|
+
/** 首行带前缀、续行按 `indent` 缩进的文本(用户消息、排队消息)。 */
|
|
41
|
+
export declare class PrefixedText implements Component {
|
|
42
|
+
private readonly prefix;
|
|
43
|
+
private readonly body;
|
|
44
|
+
private readonly indent;
|
|
45
|
+
private cache;
|
|
46
|
+
constructor(prefix: string, body: string, indent?: number);
|
|
47
|
+
render(width: number): string[];
|
|
48
|
+
invalidate(): void;
|
|
25
49
|
}
|
|
26
50
|
/** 粗估 token(4 字符 ≈ 1 token),只用于思考块的流式计数。 */
|
|
27
51
|
export declare function estimateTokens(text: string): number;
|
|
@@ -32,7 +56,10 @@ export declare class AssistantView extends Container {
|
|
|
32
56
|
private readonly parts;
|
|
33
57
|
private readonly status;
|
|
34
58
|
private streaming;
|
|
59
|
+
private thinkingExpanded;
|
|
35
60
|
constructor(options: MessageViewOptions);
|
|
61
|
+
/** Ctrl+O:展开 / 折叠本消息的思考块(`collapsed` 模式下)。 */
|
|
62
|
+
setThinkingExpanded(expanded: boolean): void;
|
|
36
63
|
update(message: AssistantMessage): void;
|
|
37
64
|
/** 收尾:错误 / 中断 / 长度截断提示,思考计数换成用量里的值。 */
|
|
38
65
|
finish(message: AssistantMessage): void;
|
|
@@ -47,25 +74,37 @@ export declare class MessageView extends Container {
|
|
|
47
74
|
private readonly options;
|
|
48
75
|
private current;
|
|
49
76
|
private readonly blocks;
|
|
77
|
+
/** 工具调用块(相邻的不空行)。 */
|
|
78
|
+
private readonly toolBlocks;
|
|
79
|
+
private readonly assistants;
|
|
80
|
+
private thinkingExpanded;
|
|
50
81
|
constructor(options: MessageViewOptions);
|
|
51
82
|
private get theme();
|
|
52
|
-
/**
|
|
83
|
+
/** 追加一块(与上一块之间空一行;`compact` 不空行)。 */
|
|
53
84
|
add(component: Component): void;
|
|
85
|
+
/** 追加工具调用块:紧跟在另一个工具调用后面时不空行。 */
|
|
86
|
+
addTool(component: Component): void;
|
|
87
|
+
private push;
|
|
54
88
|
/** 去掉一块(空的助手消息等)。 */
|
|
55
89
|
remove(component: Component): void;
|
|
56
90
|
reset(): void;
|
|
57
91
|
get blockCount(): number;
|
|
58
|
-
/**
|
|
92
|
+
/** Ctrl+O:所有助手消息的思考块一起展开 / 折叠(之后的新消息沿用)。 */
|
|
93
|
+
setThinkingExpanded(expanded: boolean): void;
|
|
94
|
+
/** 纯文本启动画面(line 模式风格):首行加粗,其余变暗。交互模式用 StartupHeader。 */
|
|
59
95
|
addHeader(lines: readonly string[]): void;
|
|
60
96
|
addUser(message: Pick<UserMessage, "content" | "origin">): void;
|
|
97
|
+
private newAssistant;
|
|
61
98
|
startAssistant(message: AssistantMessage): AssistantView;
|
|
62
99
|
updateAssistant(message: AssistantMessage): void;
|
|
63
100
|
endAssistant(message: AssistantMessage): void;
|
|
64
101
|
addNotice(level: NoticeLevel, message: string): void;
|
|
65
102
|
addHookBlocked(reason: string): void;
|
|
66
103
|
addRetry(attempt: number, maxAttempts: number, delayMs: number, error: string): void;
|
|
104
|
+
/** 退出前追加的会话摘要(留在终端回滚里)。 */
|
|
105
|
+
addExitSummary(lines: readonly string[]): void;
|
|
67
106
|
addRetryFailed(error: string | undefined): void;
|
|
68
|
-
/** 压缩 /
|
|
107
|
+
/** 压缩 / 分支摘要卡(左竖条):标题 + token 变化(有则显示)+ 摘要前 3 行。 */
|
|
69
108
|
addSummaryCard(summary: string, options?: {
|
|
70
109
|
title?: string;
|
|
71
110
|
tokensBefore?: number;
|