@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.
- package/CHANGELOG.md +99 -0
- package/README.md +142 -49
- package/dist/agent/retry.d.ts +1 -1
- package/dist/agent/retry.js +2 -1
- package/dist/agent/session-cache.js +6 -3
- 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-state.js +2 -1
- package/dist/agent/session-sync.js +9 -2
- package/dist/agent/session-tools.js +24 -3
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.js +20 -10
- 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 +5 -3
- package/dist/ai/cache/fingerprint.d.ts +1 -1
- package/dist/ai/cache/fingerprint.js +1 -1
- package/dist/ai/cache/reporting.js +2 -1
- package/dist/ai/http.d.ts +28 -6
- package/dist/ai/http.js +41 -8
- package/dist/ai/providers/channels.d.ts +51 -0
- package/dist/ai/providers/channels.js +96 -0
- package/dist/ai/providers/enrich.d.ts +34 -0
- package/dist/ai/providers/enrich.js +86 -0
- package/dist/ai/providers/models-dev-cache.d.ts +53 -0
- package/dist/ai/providers/models-dev-cache.js +147 -0
- package/dist/ai/providers/models-dev.d.ts +99 -0
- package/dist/ai/providers/models-dev.js +315 -0
- package/dist/ai/providers/registry.d.ts +42 -4
- package/dist/ai/providers/registry.js +195 -34
- 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 +30 -3
- package/dist/bundle/ama.cjs +17893 -10698
- package/dist/cli/args.d.ts +23 -5
- package/dist/cli/args.js +113 -13
- package/dist/cli/bootstrap.js +28 -3
- package/dist/cli/codemode-notice.d.ts +20 -0
- package/dist/cli/codemode-notice.js +55 -0
- package/dist/cli/compose-providers.js +3 -0
- package/dist/cli/compose-session.d.ts +8 -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 +17 -5
- package/dist/cli/compose.js +53 -19
- package/dist/cli/default-model.d.ts +38 -1
- package/dist/cli/default-model.js +96 -9
- package/dist/cli/deps.d.ts +38 -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 +10 -2
- package/dist/cli/main.js +88 -2
- 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 +20 -13
- package/dist/cli/subcommands/config.d.ts +29 -4
- package/dist/cli/subcommands/config.js +188 -25
- package/dist/cli/subcommands/context.js +5 -1
- package/dist/cli/subcommands/doctor.js +12 -1
- package/dist/cli/subcommands/init.d.ts +6 -0
- package/dist/cli/subcommands/init.js +23 -0
- package/dist/cli/subcommands/model-meta.d.ts +16 -0
- package/dist/cli/subcommands/model-meta.js +54 -0
- package/dist/cli/subcommands/models-cache-probe.js +1 -1
- package/dist/cli/subcommands/models-discover.d.ts +18 -6
- package/dist/cli/subcommands/models-discover.js +87 -52
- package/dist/cli/subcommands/models.js +26 -18
- package/dist/cli/subcommands/probe-runner.d.ts +96 -0
- package/dist/cli/subcommands/probe-runner.js +264 -0
- package/dist/cli/subcommands/providers-list.d.ts +8 -0
- package/dist/cli/subcommands/providers-list.js +97 -0
- package/dist/cli/subcommands/providers-plan.d.ts +79 -0
- package/dist/cli/subcommands/providers-plan.js +215 -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 +28 -0
- package/dist/cli/subcommands/providers.js +436 -0
- 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 +34 -0
- package/dist/config/init.js +99 -0
- package/dist/config/json-schema.d.ts +15 -0
- package/dist/config/json-schema.js +215 -0
- 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 +97 -15
- package/dist/config/types.d.ts +57 -11
- package/dist/config/types.js +19 -1
- package/dist/modes/commands-core.js +6 -5
- package/dist/modes/image-input.d.ts +27 -0
- package/dist/modes/image-input.js +78 -0
- 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 +58 -19
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +70 -82
- 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 +9 -5
- 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/startup-ui.d.ts +7 -2
- package/dist/modes/interactive/startup-ui.js +41 -11
- 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 +33 -4
- package/dist/modes/print/print-mode.js +128 -8
- package/dist/modes/rpc/commands.js +12 -2
- 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/rpc.d.ts +2 -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/projection.js +5 -1
- 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/session/types.d.ts +2 -0
- package/dist/tools/image-file.d.ts +31 -0
- package/dist/tools/image-file.js +114 -0
- package/dist/tools/presets.d.ts +35 -8
- package/dist/tools/presets.js +56 -17
- package/dist/tools/read.d.ts +4 -8
- package/dist/tools/read.js +15 -64
- 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 +241 -3
- package/docs/rpc.md +38 -38
- package/docs/session-format.md +3 -2
- package/docs/sessions.md +134 -0
- package/docs/tui.md +140 -61
- package/package.json +3 -1
|
@@ -7,14 +7,19 @@
|
|
|
7
7
|
* - 执行前预览(W3-B9a-2,`request.preview`):输入摘要之后列出会碰到的路径与规模,danger 红、
|
|
8
8
|
* warn 黄、info 暗色;`other` 类预览与输入摘要重复,不显示;
|
|
9
9
|
* - 原因:mode(当前权限模式需要确认)/ dangerous / hook + Hook 给的文本;
|
|
10
|
-
* -
|
|
10
|
+
* - 选项(终端界面视觉设计 v1 §3.11):编号列表 `1. 允许 y` / `2. 本会话允许同类 a` / `3. 拒绝 n Esc`,
|
|
11
|
+
* 选中行 `›` + selection 底色;缺省选中:危险命令 → 拒绝,其余 → 允许。按键 `y / a / n / Esc / Ctrl+C /
|
|
12
|
+
* 1 2 3 / ↑↓ Enter / v` 都有效;
|
|
13
|
+
* - 标题按原因:需要确认 / 危险命令 / Hook 要求确认(子 Agent 发起前缀 `[task] `);边框颜色随预览严重度
|
|
14
|
+
* (danger error、warn warning);宽 < 56 时紧凑:工具名单独一行、去掉空行、按键提示缩短;
|
|
11
15
|
* - 超时与中断:broker 链在超时(缺省 10 分钟)或运行被中断时 abort `signal`,对话框关闭并返回
|
|
12
16
|
* undefined(链按 deny 处理),消息区留一行说明。
|
|
13
17
|
*
|
|
14
18
|
* broker 链本身串行化审批,所以同一时刻最多一个对话框。
|
|
15
19
|
*/
|
|
20
|
+
import { AUTO_LAYER_TEXT, isPermissionMode, permissionModeLabel } from "../../permissions/modes.js";
|
|
16
21
|
import { previewDisplayLines } from "../../permissions/preview.js";
|
|
17
|
-
import { Box,
|
|
22
|
+
import { Box, defaultKeybindings, matchesKey, padToWidth, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "../../tui.js";
|
|
18
23
|
import { toolSummary } from "./tool-view.js";
|
|
19
24
|
const COMMAND_LINES = 8;
|
|
20
25
|
const EDIT_LINES = 3;
|
|
@@ -28,7 +33,17 @@ function lines(text, max) {
|
|
|
28
33
|
function record(input) {
|
|
29
34
|
return typeof input === "object" && input !== null ? input : {};
|
|
30
35
|
}
|
|
31
|
-
/**
|
|
36
|
+
/** 原因 → 标题文字(子 Agent 发起加 `[task] `)。 */
|
|
37
|
+
export function approvalTitle(request) {
|
|
38
|
+
const task = (request.context?.depth ?? 0) > 0 ? "[task] " : "";
|
|
39
|
+
const reason = request.reason === "dangerous"
|
|
40
|
+
? "危险命令"
|
|
41
|
+
: request.reason === "hook"
|
|
42
|
+
? "Hook 要求确认"
|
|
43
|
+
: "需要确认";
|
|
44
|
+
return task + reason;
|
|
45
|
+
}
|
|
46
|
+
/** 对话框正文(不含选项与按键行)。 */
|
|
32
47
|
export function describeRequest(request, theme, options = {}) {
|
|
33
48
|
const out = [];
|
|
34
49
|
const task = (request.context?.depth ?? 0) > 0 ? theme.fg("warning", "[task] ") : "";
|
|
@@ -37,7 +52,8 @@ export function describeRequest(request, theme, options = {}) {
|
|
|
37
52
|
: request.reason === "hook"
|
|
38
53
|
? theme.fg("warning", "Hook 要求确认")
|
|
39
54
|
: theme.fg("dim", "需要确认");
|
|
40
|
-
|
|
55
|
+
const name = theme.bold(theme.fg("tool", request.toolName));
|
|
56
|
+
out.push(options.compact === true ? `${task}${name}` : `${task}${name} ${tag}`);
|
|
41
57
|
const input = record(request.input);
|
|
42
58
|
const more = (n) => theme.fg("dim", `… 另 ${n} 行(v 查看)`);
|
|
43
59
|
if (options.expanded === true) {
|
|
@@ -84,42 +100,42 @@ export function describeRequest(request, theme, options = {}) {
|
|
|
84
100
|
const severity = request.preview?.severity;
|
|
85
101
|
const color = severity === "danger" ? "error" : severity === "warn" ? "warning" : "dim";
|
|
86
102
|
out.push(...previewDisplayLines(request.preview).map((l) => theme.fg(color, l)));
|
|
103
|
+
const auto = request.autoDecision;
|
|
87
104
|
if (request.reason === "hook") {
|
|
88
105
|
out.push(theme.fg("warning", `Hook:${request.hookReason ?? "(无说明)"}`));
|
|
89
106
|
}
|
|
90
107
|
else if (request.reason === "dangerous") {
|
|
91
108
|
out.push(theme.fg("error", "这条命令可能有破坏性,请确认"));
|
|
92
109
|
}
|
|
110
|
+
else if (auto !== undefined) {
|
|
111
|
+
out.push(theme.fg("warning", `Auto ${AUTO_LAYER_TEXT[auto.layer]}:${auto.reason}`));
|
|
112
|
+
}
|
|
93
113
|
else if (options.permissionMode !== undefined) {
|
|
94
|
-
|
|
114
|
+
const mode = options.permissionMode;
|
|
115
|
+
const label = isPermissionMode(mode) ? permissionModeLabel(mode) : mode;
|
|
116
|
+
out.push(theme.fg("dim", `权限模式 ${label} 下需要确认`));
|
|
95
117
|
}
|
|
96
118
|
return out;
|
|
97
119
|
}
|
|
98
|
-
export const
|
|
120
|
+
export const APPROVAL_OPTIONS = [
|
|
121
|
+
{ decision: "allow", label: "允许", keys: "y" },
|
|
122
|
+
{ decision: "allow_session", label: "本会话允许同类", keys: "a" },
|
|
123
|
+
{ decision: "deny", label: "拒绝", keys: "n Esc" },
|
|
124
|
+
];
|
|
125
|
+
/** 宽度低于它(Box 外宽)时用紧凑布局。 */
|
|
126
|
+
const COMPACT_WIDTH = 56;
|
|
99
127
|
class ApprovalDialog {
|
|
100
128
|
request;
|
|
101
129
|
host;
|
|
102
130
|
decide;
|
|
103
131
|
focused = false;
|
|
104
132
|
expanded = false;
|
|
105
|
-
|
|
133
|
+
selected;
|
|
106
134
|
constructor(request, host, decide) {
|
|
107
135
|
this.request = request;
|
|
108
136
|
this.host = host;
|
|
109
137
|
this.decide = decide;
|
|
110
|
-
this.
|
|
111
|
-
}
|
|
112
|
-
rebuild() {
|
|
113
|
-
const { theme } = this.host;
|
|
114
|
-
this.body.clear();
|
|
115
|
-
const options = {
|
|
116
|
-
permissionMode: this.host.permissionMode(),
|
|
117
|
-
expanded: this.expanded,
|
|
118
|
-
};
|
|
119
|
-
if (this.host.cwd !== undefined)
|
|
120
|
-
options.cwd = this.host.cwd;
|
|
121
|
-
this.body.addChild(new Text(describeRequest(this.request, theme, options).join("\n")));
|
|
122
|
-
this.body.addChild(new Text(theme.fg("accent", APPROVAL_KEYS_HINT)));
|
|
138
|
+
this.selected = request.reason === "dangerous" ? 2 : 0;
|
|
123
139
|
}
|
|
124
140
|
handleInput(data) {
|
|
125
141
|
const keys = this.host.keybindings ?? defaultKeybindings;
|
|
@@ -134,17 +150,74 @@ class ApprovalDialog {
|
|
|
134
150
|
keys.matches(data, "tui.select.cancel")) {
|
|
135
151
|
this.decide("deny");
|
|
136
152
|
}
|
|
137
|
-
else if (
|
|
138
|
-
this.
|
|
139
|
-
|
|
153
|
+
else if (/^[1-3]$/.test(data))
|
|
154
|
+
this.decide(APPROVAL_OPTIONS[Number(data) - 1].decision);
|
|
155
|
+
else if (keys.matches(data, "tui.select.up")) {
|
|
156
|
+
this.selected = (this.selected + APPROVAL_OPTIONS.length - 1) % APPROVAL_OPTIONS.length;
|
|
140
157
|
}
|
|
158
|
+
else if (keys.matches(data, "tui.select.down")) {
|
|
159
|
+
this.selected = (this.selected + 1) % APPROVAL_OPTIONS.length;
|
|
160
|
+
}
|
|
161
|
+
else if (matchesKey(data, "enter"))
|
|
162
|
+
this.decide(APPROVAL_OPTIONS[this.selected].decision);
|
|
163
|
+
else if (key === "v")
|
|
164
|
+
this.expanded = !this.expanded;
|
|
141
165
|
}
|
|
166
|
+
/** `width` 是 Box 内宽;紧凑与否按外宽(+ 4)判断。 */
|
|
142
167
|
render(width) {
|
|
143
|
-
|
|
168
|
+
const { theme } = this.host;
|
|
169
|
+
const compact = width + 4 < COMPACT_WIDTH;
|
|
170
|
+
const options = {
|
|
171
|
+
permissionMode: this.host.permissionMode(),
|
|
172
|
+
expanded: this.expanded,
|
|
173
|
+
compact,
|
|
174
|
+
};
|
|
175
|
+
if (this.host.cwd !== undefined)
|
|
176
|
+
options.cwd = this.host.cwd;
|
|
177
|
+
const body = describeRequest(this.request, theme, options);
|
|
178
|
+
const lines = [];
|
|
179
|
+
// 输入摘要(标题 + 命令)与预览 / 原因之间空一行
|
|
180
|
+
const head = this.inputLines(body);
|
|
181
|
+
for (const line of body.slice(0, head))
|
|
182
|
+
lines.push(...wrapTextWithAnsi(line, width));
|
|
183
|
+
if (!compact && head < body.length)
|
|
184
|
+
lines.push("");
|
|
185
|
+
for (const line of body.slice(head))
|
|
186
|
+
lines.push(...wrapTextWithAnsi(line, width));
|
|
187
|
+
if (!compact)
|
|
188
|
+
lines.push("");
|
|
189
|
+
lines.push(...this.optionLines(width, compact));
|
|
190
|
+
if (!compact)
|
|
191
|
+
lines.push("");
|
|
192
|
+
const arrows = theme.glyphs.arrowUp + theme.glyphs.arrowDown;
|
|
193
|
+
const hint = compact
|
|
194
|
+
? `${arrows} Enter · v 完整输入`
|
|
195
|
+
: `${arrows} 选择 · Enter 确认 · v 完整输入`;
|
|
196
|
+
lines.push(theme.fg("dim", hint));
|
|
197
|
+
return lines.map((l) => truncateToWidth(l, width));
|
|
144
198
|
}
|
|
145
|
-
|
|
146
|
-
|
|
199
|
+
/** 正文里属于「输入摘要」的行数(首行 + 命令 / 路径 / 修改摘要,止于预览或原因行)。 */
|
|
200
|
+
inputLines(body) {
|
|
201
|
+
const preview = previewDisplayLines(this.request.preview).length;
|
|
202
|
+
const reasonLines = body.length > 0 ? 1 : 0;
|
|
203
|
+
return Math.max(1, body.length - preview - reasonLines);
|
|
204
|
+
}
|
|
205
|
+
optionLines(width, compact) {
|
|
206
|
+
const { theme } = this.host;
|
|
207
|
+
const g = theme.glyphs;
|
|
208
|
+
const keyWidth = compact ? 1 : Math.max(...APPROVAL_OPTIONS.map((o) => o.keys.length));
|
|
209
|
+
return APPROVAL_OPTIONS.map((option, i) => {
|
|
210
|
+
const selected = i === this.selected;
|
|
211
|
+
const keys = compact ? option.keys.slice(0, 1) : option.keys;
|
|
212
|
+
const left = `${selected ? g.prompt : " "} ${i + 1}. ${option.label}`;
|
|
213
|
+
const right = keys.padEnd(keyWidth);
|
|
214
|
+
const room = Math.max(1, width - visibleWidth(right) - 1);
|
|
215
|
+
const label = selected ? theme.fg("accent", theme.bold(left)) : left;
|
|
216
|
+
const line = padToWidth(truncateToWidth(label, room), room) + " " + theme.fg("dim", right);
|
|
217
|
+
return selected ? theme.bg("selection", line) : line;
|
|
218
|
+
});
|
|
147
219
|
}
|
|
220
|
+
invalidate() { }
|
|
148
221
|
}
|
|
149
222
|
/** UI broker:每个请求开一个底部覆盖层,等用户按键或 signal abort。 */
|
|
150
223
|
export class ApprovalDialogBroker {
|
|
@@ -177,9 +250,15 @@ export class ApprovalDialogBroker {
|
|
|
177
250
|
const dialog = new ApprovalDialog(request, this.host, settle);
|
|
178
251
|
this.open++;
|
|
179
252
|
this.host.onOpen?.();
|
|
253
|
+
const severity = request.preview?.severity;
|
|
180
254
|
handle = this.host.showOverlay(new Box(dialog, {
|
|
181
|
-
title: (request
|
|
255
|
+
title: approvalTitle(request),
|
|
182
256
|
theme: this.host.theme,
|
|
257
|
+
...(severity === "danger"
|
|
258
|
+
? { borderColor: "error" }
|
|
259
|
+
: severity === "warn"
|
|
260
|
+
? { borderColor: "warning" }
|
|
261
|
+
: {}),
|
|
183
262
|
}));
|
|
184
263
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
185
264
|
});
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
* 交互模式的斜杠命令:把 commands-core 的 `CommandResult` 变成界面动作。[B7]
|
|
3
3
|
*
|
|
4
4
|
* - `handled` → 消息区显示文本;`prompt` → 发提示;`exit` → 退出;
|
|
5
|
-
* - `pick` → 打开对应选择器:模型(setModel
|
|
5
|
+
* - `pick` → 打开对应选择器:模型(setModel;不列测试供应商 fake,`AMA_SHOW_FAKE=1` 或 `AMA_FAKE_SCRIPT` 时照列)、会话(resume)、树(/fork 无参数:从选中的用户消息
|
|
6
6
|
* 之前分叉,消息文本回填编辑器)、权限模式、思考级别;
|
|
7
7
|
* - 交互模式自有命令:`/tree`(同一文件内换叶子到选中消息之前,文本回填编辑器,可改后重发形成新分支)、
|
|
8
8
|
* `/permissions`(当前模式、判定顺序与已加载规则);`/help` 追加这两条与按键说明。
|
|
9
|
+
* - `/session`、`/cache`(无参数)与 `/permissions` 在消息区画左竖条面板(panels.ts),不再拍成文本。
|
|
9
10
|
* - 不是命令(含模板与 `/skill:`)返回 false,调用方把整行当提示发出。
|
|
10
11
|
*/
|
|
11
12
|
import type { AgentSession } from "../../agent/types.js";
|
|
12
13
|
import type { SwitchRequest } from "../../cli/compose-session.js";
|
|
13
14
|
import type { Runtime } from "../../cli/runtime.js";
|
|
14
|
-
import type { SelectItem } from "../../tui.js";
|
|
15
|
+
import type { Component, SelectItem, Theme } from "../../tui.js";
|
|
15
16
|
import { type CommandInfo } from "../commands-core.js";
|
|
16
17
|
import { type NoticeLevel } from "./message-view.js";
|
|
17
18
|
import { type PickerSpec } from "./pickers.js";
|
|
@@ -26,6 +27,8 @@ export interface CommandUi {
|
|
|
26
27
|
switchSession(request: SwitchRequest): Promise<AgentSession>;
|
|
27
28
|
pick(spec: PickerSpec): Promise<SelectItem | undefined>;
|
|
28
29
|
notice(level: NoticeLevel, text: string): void;
|
|
30
|
+
/** 消息区面板(/session、/cache、/permissions);缺省回落为文本通知。 */
|
|
31
|
+
panel?(component: Component): void;
|
|
29
32
|
setEditorText(text: string): void;
|
|
30
33
|
/** 发一条提示(不等运行结束)。 */
|
|
31
34
|
prompt(text: string): void;
|
|
@@ -33,6 +36,12 @@ export interface CommandUi {
|
|
|
33
36
|
reload(): void;
|
|
34
37
|
exit(code: number): void;
|
|
35
38
|
now(): number;
|
|
39
|
+
/** 面板用的主题(与 panel 一起提供)。 */
|
|
40
|
+
theme?(): Theme;
|
|
41
|
+
/** 家目录(面板里路径缩写为 ~)。 */
|
|
42
|
+
home?: string;
|
|
43
|
+
/** 进程环境:给出时 /model 选择器按 fake-visibility 规则藏起测试供应商 fake。 */
|
|
44
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
36
45
|
}
|
|
37
46
|
/** 当前分支上的条目 id(树选择器标 ●)。 */
|
|
38
47
|
export declare function activeBranchIds(session: AgentSession): Set<string>;
|
|
@@ -2,17 +2,22 @@
|
|
|
2
2
|
* 交互模式的斜杠命令:把 commands-core 的 `CommandResult` 变成界面动作。[B7]
|
|
3
3
|
*
|
|
4
4
|
* - `handled` → 消息区显示文本;`prompt` → 发提示;`exit` → 退出;
|
|
5
|
-
* - `pick` → 打开对应选择器:模型(setModel
|
|
5
|
+
* - `pick` → 打开对应选择器:模型(setModel;不列测试供应商 fake,`AMA_SHOW_FAKE=1` 或 `AMA_FAKE_SCRIPT` 时照列)、会话(resume)、树(/fork 无参数:从选中的用户消息
|
|
6
6
|
* 之前分叉,消息文本回填编辑器)、权限模式、思考级别;
|
|
7
7
|
* - 交互模式自有命令:`/tree`(同一文件内换叶子到选中消息之前,文本回填编辑器,可改后重发形成新分支)、
|
|
8
8
|
* `/permissions`(当前模式、判定顺序与已加载规则);`/help` 追加这两条与按键说明。
|
|
9
|
+
* - `/session`、`/cache`(无参数)与 `/permissions` 在消息区画左竖条面板(panels.ts),不再拍成文本。
|
|
9
10
|
* - 不是命令(含模板与 `/skill:`)返回 false,调用方把整行当提示发出。
|
|
10
11
|
*/
|
|
12
|
+
import { formatModelRef } from "../../ai/providers/channels.js";
|
|
11
13
|
import { AgentSessionImpl } from "../../agent/session.js";
|
|
12
14
|
import { listSessions } from "../../cli/compose-store.js";
|
|
15
|
+
import { hideFakeProvider } from "../../cli/fake-visibility.js";
|
|
16
|
+
import { AUTO_LAYER_TEXT, permissionModeLabel } from "../../permissions/modes.js";
|
|
13
17
|
import { BUILTIN_COMMANDS, parseSlash, runSlashCommand, } from "../commands-core.js";
|
|
14
18
|
import { contentText } from "./message-view.js";
|
|
15
|
-
import {
|
|
19
|
+
import { cachePanel, permissionsPanel, sessionPanel } from "./panels.js";
|
|
20
|
+
import { modelItems, permissionPickerSpec, sessionItems, thinkingItems, treeItems, } from "./pickers.js";
|
|
16
21
|
export const INTERACTIVE_COMMANDS = [
|
|
17
22
|
{ name: "tree", description: "浏览会话树,回到某条消息之前重写" },
|
|
18
23
|
{ name: "permissions", description: "权限模式、判定顺序与规则" },
|
|
@@ -20,11 +25,15 @@ export const INTERACTIVE_COMMANDS = [
|
|
|
20
25
|
/** 补全与 /help 用的完整命令表。 */
|
|
21
26
|
export const ALL_COMMANDS = [...BUILTIN_COMMANDS, ...INTERACTIVE_COMMANDS];
|
|
22
27
|
export const KEY_HINTS = [
|
|
23
|
-
"Enter 发送(运行中 =
|
|
28
|
+
"Enter 发送(运行中 = 插话) Alt+Enter 排到本轮之后 Shift+Enter / Ctrl+J 换行",
|
|
24
29
|
"Esc 中断(排队消息回填编辑器) Alt+↑ 取回最后一条排队消息",
|
|
25
|
-
"Shift+Tab 切换权限模式 Ctrl+L 模型 Ctrl+T 思考级别 Ctrl+O
|
|
30
|
+
"Shift+Tab 切换权限模式 Ctrl+L 模型 Ctrl+T 思考级别 Ctrl+O 展开工具输出与思考",
|
|
31
|
+
"审批:1–3 或 ↑↓ Enter 选择,y 允许 a 本会话允许同类 n / Esc 拒绝 v 完整输入",
|
|
26
32
|
"Ctrl+C 清空输入(再按退出) Ctrl+D 空输入时退出 Tab 补全 @ 引用文件",
|
|
27
33
|
].join("\n");
|
|
34
|
+
function homeOf(ui) {
|
|
35
|
+
return ui.home !== undefined ? { home: ui.home } : {};
|
|
36
|
+
}
|
|
28
37
|
function userEntryText(entry) {
|
|
29
38
|
if (entry?.type !== "message" || entry.message.role !== "user")
|
|
30
39
|
return undefined;
|
|
@@ -38,12 +47,26 @@ export function activeBranchIds(session) {
|
|
|
38
47
|
}
|
|
39
48
|
export function permissionsText(runtime, session) {
|
|
40
49
|
const rules = runtime.permission.rules;
|
|
50
|
+
const mode = session.state.permissionMode;
|
|
51
|
+
const order = mode === "auto"
|
|
52
|
+
? "deny 规则 → Hook deny → 危险命令确认 → 规则层(受保护路径、项目外写入、网络、删除类)→ Hook ask → allow 规则 / Hook allow / 本会话记忆 → 静态判定(只读、项目内写入、安全名单)→ 模型分类器 → 询问"
|
|
53
|
+
: mode === "allowlist"
|
|
54
|
+
? "deny 规则 → Hook deny → 危险命令(拒绝)→ 只读工具 / allow 规则 / Hook allow 放行 → 其余拒绝(从不询问)"
|
|
55
|
+
: "deny 规则 → Hook deny → 危险命令确认 → 权限模式 → allow 规则 / Hook allow / 本会话记忆 → 询问";
|
|
41
56
|
const lines = [
|
|
42
|
-
`权限模式:${
|
|
43
|
-
|
|
57
|
+
`权限模式:${permissionModeLabel(mode)}(${mode})`,
|
|
58
|
+
`判定顺序:${order}`,
|
|
44
59
|
rules.length === 0 ? "规则:(无)" : `规则(${rules.length}):`,
|
|
45
60
|
...rules.map((r) => ` ${r.effect === "deny" ? "deny " : "allow"} ${r.raw} [${r.source}]`),
|
|
46
61
|
];
|
|
62
|
+
const recent = runtime.permission.autoDecisions?.() ?? [];
|
|
63
|
+
if (recent.length > 0) {
|
|
64
|
+
lines.push(`最近的 auto 判定(${recent.length}):`);
|
|
65
|
+
for (const d of recent) {
|
|
66
|
+
const cached = d.cached === true ? "(缓存)" : "";
|
|
67
|
+
lines.push(` ${AUTO_LAYER_TEXT[d.layer]} ${d.decision} ${d.toolName} ${d.summary} — ${d.reason}${cached}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
47
70
|
return lines.join("\n");
|
|
48
71
|
}
|
|
49
72
|
async function pickTree(ui, title) {
|
|
@@ -85,11 +108,15 @@ async function handlePick(what, ui) {
|
|
|
85
108
|
switch (what) {
|
|
86
109
|
case "model": {
|
|
87
110
|
const current = session.state.model;
|
|
111
|
+
const ref = current === undefined ? undefined : `${current.provider}/${current.id}`;
|
|
88
112
|
const picked = await ui.pick({
|
|
89
113
|
title: "选择模型",
|
|
90
|
-
items: await modelItems(ui.
|
|
114
|
+
items: await modelItems(ui.env === undefined
|
|
115
|
+
? ui.runtime.providers
|
|
116
|
+
: hideFakeProvider(ui.runtime.providers, ui.env)),
|
|
91
117
|
filterable: true,
|
|
92
|
-
|
|
118
|
+
showCount: true,
|
|
119
|
+
...(ref !== undefined ? { selected: ref, currentValue: ref } : {}),
|
|
93
120
|
});
|
|
94
121
|
if (picked === undefined)
|
|
95
122
|
return;
|
|
@@ -128,20 +155,17 @@ async function handlePick(what, ui) {
|
|
|
128
155
|
return;
|
|
129
156
|
}
|
|
130
157
|
case "permission": {
|
|
131
|
-
const picked = await ui.pick(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
session.setPermissionMode(picked.value);
|
|
158
|
+
const picked = await ui.pick(permissionPickerSpec(session.state.permissionMode, ui.runtime.config.permission?.mode ?? "default"));
|
|
159
|
+
if (picked === undefined)
|
|
160
|
+
return;
|
|
161
|
+
const mode = picked.value;
|
|
162
|
+
session.setPermissionMode(mode);
|
|
163
|
+
ui.notice("info", `权限模式:${permissionModeLabel(mode)}`);
|
|
138
164
|
return;
|
|
139
165
|
}
|
|
140
166
|
case "thinking": {
|
|
141
167
|
const model = session.state.model;
|
|
142
|
-
const found = model === undefined
|
|
143
|
-
? undefined
|
|
144
|
-
: ui.runtime.providers.findModel(`${model.provider}/${model.id}`);
|
|
168
|
+
const found = model === undefined ? undefined : ui.runtime.providers.findModel(formatModelRef(model));
|
|
145
169
|
const reasoning = found?.ok === true ? found.model.reasoning : true;
|
|
146
170
|
const picked = await ui.pick({
|
|
147
171
|
title: "思考级别",
|
|
@@ -164,8 +188,23 @@ export async function runInteractiveCommand(line, ui) {
|
|
|
164
188
|
await treeCommand(ui);
|
|
165
189
|
return true;
|
|
166
190
|
}
|
|
191
|
+
const theme = ui.theme?.();
|
|
167
192
|
if (parsed.name === "permissions") {
|
|
168
|
-
|
|
193
|
+
if (ui.panel !== undefined && theme !== undefined) {
|
|
194
|
+
ui.panel(permissionsPanel(ui.runtime, ui.session(), theme));
|
|
195
|
+
}
|
|
196
|
+
else
|
|
197
|
+
ui.notice("info", permissionsText(ui.runtime, ui.session()));
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
if ((parsed.name === "session" || parsed.name === "cache") &&
|
|
201
|
+
parsed.args.trim() === "" &&
|
|
202
|
+
ui.panel !== undefined &&
|
|
203
|
+
theme !== undefined) {
|
|
204
|
+
const session = ui.session();
|
|
205
|
+
ui.panel(parsed.name === "session"
|
|
206
|
+
? sessionPanel(session, theme, { now: ui.now(), ...homeOf(ui) })
|
|
207
|
+
: cachePanel(session, theme, ui.now()));
|
|
169
208
|
return true;
|
|
170
209
|
}
|
|
171
210
|
const result = await runSlashCommand(line, {
|
|
@@ -11,7 +11,8 @@
|
|
|
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 type { AgentSession } from "../../agent/types.js";
|
|
17
18
|
import type { ModeContext } from "../../cli/deps.js";
|