@armadra/agent 0.6.1 → 0.6.3
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 +80 -0
- package/CHANGELOG.zh-CN.md +56 -0
- package/README.md +168 -575
- package/README.zh-CN.md +165 -596
- package/dist/agent/session-settings.d.ts +4 -1
- package/dist/agent/session-settings.js +7 -2
- package/dist/agent/session.d.ts +6 -0
- package/dist/agent/session.js +8 -0
- package/dist/agent/subagent-background.d.ts +75 -0
- package/dist/agent/subagent-background.js +209 -0
- package/dist/agent/subagent-registry.d.ts +24 -5
- package/dist/agent/subagent-registry.js +64 -92
- package/dist/agent/types-w5.d.ts +11 -1
- package/dist/agent/types.d.ts +5 -0
- package/dist/agents/builtin.js +0 -1
- package/dist/agents/external.js +0 -1
- package/dist/agents/parse.js +4 -3
- package/dist/agents/result.d.ts +7 -1
- package/dist/agents/result.js +20 -1
- package/dist/agents/task-control.d.ts +13 -2
- package/dist/agents/task-record.d.ts +13 -1
- package/dist/agents/task-record.js +32 -0
- package/dist/agents/types.d.ts +5 -1
- package/dist/ai/apis/chatgpt-backend.d.ts +7 -2
- package/dist/ai/apis/chatgpt-backend.js +12 -4
- package/dist/ai/providers/discovered-cache.d.ts +29 -12
- package/dist/ai/providers/discovered-cache.js +72 -12
- package/dist/ai/providers/registry.js +7 -6
- package/dist/ai/types.d.ts +5 -0
- package/dist/auth/chatgpt/backend-client.d.ts +14 -4
- package/dist/auth/chatgpt/backend-client.js +41 -8
- package/dist/auth/chatgpt/cli.d.ts +2 -1
- package/dist/auth/chatgpt/cli.js +14 -7
- package/dist/auth/chatgpt/follow.d.ts +16 -0
- package/dist/auth/chatgpt/follow.js +51 -0
- package/dist/auth/chatgpt/presets.d.ts +8 -0
- package/dist/auth/chatgpt/presets.js +12 -0
- package/dist/auth/testing/fake-oauth.d.ts +4 -1
- package/dist/auth/testing/fake-oauth.js +24 -1
- package/dist/bundle/ama.cjs +2016 -664
- package/dist/cli/compose-agents.d.ts +2 -1
- package/dist/cli/compose-agents.js +11 -2
- package/dist/cli/compose-providers.d.ts +2 -1
- package/dist/cli/compose-providers.js +6 -3
- package/dist/cli/startup-steps.js +4 -1
- package/dist/cli/subcommands/models-discover.d.ts +5 -2
- package/dist/cli/subcommands/models-discover.js +52 -10
- package/dist/config/json-schema.js +10 -2
- package/dist/config/key-docs.js +9 -3
- package/dist/config/merge.d.ts +1 -1
- package/dist/config/merge.js +20 -3
- package/dist/config/schema-w5.js +4 -2
- package/dist/config/schema-w6.d.ts +1 -1
- package/dist/config/schema-w6.js +2 -1
- package/dist/config/schema.js +2 -0
- package/dist/config/settings-registry.js +4 -0
- package/dist/config/types-w5.d.ts +10 -0
- package/dist/config/types-w5.js +2 -0
- package/dist/config/types-w6.d.ts +5 -0
- package/dist/config/types.d.ts +3 -1
- package/dist/git/info.d.ts +19 -0
- package/dist/git/info.js +64 -8
- package/dist/i18n/catalog.d.ts +52 -8
- package/dist/i18n/messages/agents.d.ts +56 -0
- package/dist/i18n/messages/agents.js +58 -2
- package/dist/i18n/messages/auth.d.ts +12 -0
- package/dist/i18n/messages/auth.js +20 -0
- package/dist/i18n/messages/config-keys.d.ts +8 -0
- package/dist/i18n/messages/config-keys.js +18 -10
- package/dist/i18n/messages/config.d.ts +8 -0
- package/dist/i18n/messages/interactive-startup.d.ts +0 -16
- package/dist/i18n/messages/interactive-startup.js +0 -16
- package/dist/i18n/messages/interactive.d.ts +23 -16
- package/dist/i18n/messages/interactive.js +25 -0
- package/dist/i18n/messages/print.d.ts +4 -0
- package/dist/i18n/messages/print.js +4 -0
- package/dist/i18n/messages/report.js +4 -4
- package/dist/i18n/messages/settings.d.ts +6 -0
- package/dist/i18n/messages/settings.js +6 -0
- package/dist/i18n/messages/subcommands-config.d.ts +4 -0
- package/dist/i18n/messages/subcommands-config.js +4 -0
- package/dist/i18n/messages/subcommands.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/modes/commands-core.js +8 -4
- package/dist/modes/interactive/agent-bar.d.ts +3 -1
- package/dist/modes/interactive/agent-bar.js +12 -5
- package/dist/modes/interactive/agent-ui.d.ts +21 -3
- package/dist/modes/interactive/agent-ui.js +81 -12
- package/dist/modes/interactive/agent-view.d.ts +4 -0
- package/dist/modes/interactive/agent-view.js +14 -1
- package/dist/modes/interactive/approval-dock.d.ts +51 -0
- package/dist/modes/interactive/approval-dock.js +112 -0
- package/dist/modes/interactive/approval-ui.d.ts +43 -0
- package/dist/modes/interactive/approval-ui.js +64 -0
- package/dist/modes/interactive/commands.js +4 -1
- package/dist/modes/interactive/event-notices.d.ts +6 -1
- package/dist/modes/interactive/event-notices.js +7 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -2
- package/dist/modes/interactive/interactive-mode.js +33 -38
- package/dist/modes/interactive/key-dispatch.d.ts +21 -4
- package/dist/modes/interactive/key-dispatch.js +60 -8
- package/dist/modes/interactive/line/line-mode.js +5 -0
- package/dist/modes/interactive/run-indicator.d.ts +15 -0
- package/dist/modes/interactive/run-indicator.js +37 -5
- package/dist/modes/interactive/session-events.js +5 -1
- package/dist/modes/interactive/startup-header.d.ts +29 -14
- package/dist/modes/interactive/startup-header.js +93 -59
- package/dist/modes/interactive/startup-logo.d.ts +83 -0
- package/dist/modes/interactive/startup-logo.js +183 -0
- package/dist/modes/interactive/status-area.d.ts +18 -0
- package/dist/modes/interactive/status-area.js +67 -1
- package/dist/modes/interactive/status-bar.d.ts +9 -1
- package/dist/modes/interactive/status-bar.js +43 -11
- package/dist/modes/interactive/status-line.d.ts +2 -0
- package/dist/modes/interactive/status-line.js +11 -6
- package/dist/modes/interactive/status-quota.d.ts +44 -0
- package/dist/modes/interactive/status-quota.js +135 -0
- package/dist/modes/interactive/subagent-view.d.ts +1 -0
- package/dist/modes/interactive/subagent-view.js +8 -0
- package/dist/modes/interactive/task-background.d.ts +31 -0
- package/dist/modes/interactive/task-background.js +68 -0
- package/dist/modes/interactive/tool-view.d.ts +7 -1
- package/dist/modes/interactive/tool-view.js +24 -1
- package/dist/modes/print/print-mode.d.ts +11 -0
- package/dist/modes/print/print-mode.js +36 -1
- package/dist/modes/rpc/commands.d.ts +2 -1
- package/dist/modes/rpc/commands.js +9 -1
- package/dist/rpc.d.ts +13 -0
- package/dist/rpc.js +3 -0
- package/dist/tools/task-ctl.d.ts +2 -0
- package/dist/tools/task-ctl.js +7 -2
- package/dist/tools/task.d.ts +11 -0
- package/dist/tools/task.js +20 -2
- package/dist/tui/components/editor.d.ts +2 -0
- package/dist/tui/components/editor.js +4 -0
- package/dist/tui/components/loader.d.ts +5 -1
- package/dist/tui/components/loader.js +18 -5
- package/dist/tui/keybindings.d.ts +8 -3
- package/dist/tui/keybindings.js +8 -3
- package/docs/agents.md +52 -28
- package/docs/en/host-api.md +5 -1
- package/docs/en/providers.md +8 -4
- package/docs/en/rpc.md +19 -7
- package/docs/en/sessions.md +3 -1
- package/docs/en/tui.md +84 -66
- package/docs/host-api.md +5 -1
- package/docs/providers.md +29 -12
- package/docs/rpc.md +19 -7
- package/docs/sessions.md +2 -1
- package/docs/tui-design.md +42 -29
- package/docs/tui.md +67 -49
- package/package.json +1 -1
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
* - 费用 = `stats.cost` + 外部 Agent 以美元计的用量(`stats.external`,W5-E;其它单位只在 /session 显示)。
|
|
20
20
|
* - 着色:整行 dim;模式名正文色(Bypass permissions warning、Plan accent);模型 accent;ctx 按阈值
|
|
21
21
|
* success / warning / error;rebill、queue warning;`net!` error。宽 ≥ 110 时 ctx 用余量表。
|
|
22
|
+
* [W6] full 本行按用户参考配色:思考级别 `user`(浅蓝)、目录与分支 success、短提交 dim、`↑N` 领先 `code`(橙)/
|
|
23
|
+
* `↓N` 落后 error、`(+a,-d)` 的 +a success / -d error、费用 warning、时长 `tool`(紫);compact 着色不变。
|
|
24
|
+
* - [W6] git 段在短提交后显示领先 / 落后上游 `↑N ↓N`(只 full;为 0 不显示,没有上游不显示)。
|
|
25
|
+
* - [W6] compact 在行尾(宿主状态之后)追加订阅配额短项 `5h 10% wk 31%`(status-quota.ts),最先丢;
|
|
26
|
+
* 既有记号顺序不变。full 的配额在状态栏下方单独一行。
|
|
22
27
|
* - [W5-U] bash 在操作系统沙箱里跑时(S2)用量类项多一个 `沙箱`(与 codemode 同一丢弃优先级)。
|
|
23
28
|
* - [W5-U] 模型回退中(`model_fallback`)模型项显示 `主模型 → 回退模型`,切回主模型后恢复。
|
|
24
29
|
* - 模型名缩写:宽 < 100 去掉供应商前缀,< 60 再去掉 `@渠道`,< 48 去掉版本后缀(第一个 `-数字` 起)。
|
|
@@ -29,6 +34,7 @@
|
|
|
29
34
|
import type { AgentSession, SessionCacheStats, SessionStats } from "../../agent/types.js";
|
|
30
35
|
import type { StatusLineMode } from "../../config/types.js";
|
|
31
36
|
import type { GitInfo } from "../../git/info.js";
|
|
37
|
+
import { type QuotaView } from "./status-quota.js";
|
|
32
38
|
import { type Component, type Theme } from "../../tui.js";
|
|
33
39
|
export interface StatusBarSource {
|
|
34
40
|
session(): AgentSession;
|
|
@@ -58,6 +64,8 @@ export interface StatusBarSource {
|
|
|
58
64
|
from: string;
|
|
59
65
|
to: string;
|
|
60
66
|
} | undefined;
|
|
67
|
+
/** [W6] 订阅配额(当前模型走 ChatGPT 订阅时);compact 行尾短项用。 */
|
|
68
|
+
quota?(): QuotaView;
|
|
61
69
|
}
|
|
62
70
|
export declare function formatTokens(count: number): string;
|
|
63
71
|
export declare function formatCost(cost: number): string;
|
|
@@ -131,7 +139,7 @@ export declare class StatusBar implements Component {
|
|
|
131
139
|
queued(): number;
|
|
132
140
|
layout(): StatusLineMode;
|
|
133
141
|
private parts;
|
|
134
|
-
/** `目录 ⎇ 分支 短提交 +a −b
|
|
142
|
+
/** `目录 ⎇ 分支 短提交 +a −b`:三项同组、各自按优先级丢弃;full 另带 `↑N ↓N` 并按参考配色。 */
|
|
135
143
|
private gitParts;
|
|
136
144
|
private ctxText;
|
|
137
145
|
render(width: number): string[];
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
* - 费用 = `stats.cost` + 外部 Agent 以美元计的用量(`stats.external`,W5-E;其它单位只在 /session 显示)。
|
|
20
20
|
* - 着色:整行 dim;模式名正文色(Bypass permissions warning、Plan accent);模型 accent;ctx 按阈值
|
|
21
21
|
* success / warning / error;rebill、queue warning;`net!` error。宽 ≥ 110 时 ctx 用余量表。
|
|
22
|
+
* [W6] full 本行按用户参考配色:思考级别 `user`(浅蓝)、目录与分支 success、短提交 dim、`↑N` 领先 `code`(橙)/
|
|
23
|
+
* `↓N` 落后 error、`(+a,-d)` 的 +a success / -d error、费用 warning、时长 `tool`(紫);compact 着色不变。
|
|
24
|
+
* - [W6] git 段在短提交后显示领先 / 落后上游 `↑N ↓N`(只 full;为 0 不显示,没有上游不显示)。
|
|
25
|
+
* - [W6] compact 在行尾(宿主状态之后)追加订阅配额短项 `5h 10% wk 31%`(status-quota.ts),最先丢;
|
|
26
|
+
* 既有记号顺序不变。full 的配额在状态栏下方单独一行。
|
|
22
27
|
* - [W5-U] bash 在操作系统沙箱里跑时(S2)用量类项多一个 `沙箱`(与 codemode 同一丢弃优先级)。
|
|
23
28
|
* - [W5-U] 模型回退中(`model_fallback`)模型项显示 `主模型 → 回退模型`,切回主模型后恢复。
|
|
24
29
|
* - 模型名缩写:宽 < 100 去掉供应商前缀,< 60 再去掉 `@渠道`,< 48 去掉版本后缀(第一个 `-数字` 起)。
|
|
@@ -29,6 +34,7 @@
|
|
|
29
34
|
import { msg } from "../../i18n/index.js";
|
|
30
35
|
import { permissionModeLabel } from "../../permissions/modes.js";
|
|
31
36
|
import { formatModelRef } from "../../ai/providers/channels.js";
|
|
37
|
+
import { compactQuotaItem } from "./status-quota.js";
|
|
32
38
|
import { Meter, levelColor, truncateToWidth, visibleWidth, } from "../../tui.js";
|
|
33
39
|
export function formatTokens(count) {
|
|
34
40
|
if (count < 1000)
|
|
@@ -209,6 +215,7 @@ const COMPACT = {
|
|
|
209
215
|
preset: 14,
|
|
210
216
|
host: 15,
|
|
211
217
|
hint: 16,
|
|
218
|
+
quota: 17,
|
|
212
219
|
};
|
|
213
220
|
/** full 布局本行的优先级(§1.2 下行表)。 */
|
|
214
221
|
const FULL = { model: 0, ctx: 1, cost: 2, duration: 3, branch: 4, dir: 5, diff: 6, thinking: 7 };
|
|
@@ -279,7 +286,8 @@ export class StatusBar {
|
|
|
279
286
|
theme.fg("warning", abbreviateModel(fallback.to, width));
|
|
280
287
|
right(modelText, p.model, full ? { group: "model" } : {});
|
|
281
288
|
if (state.thinkingLevel !== "off") {
|
|
282
|
-
|
|
289
|
+
const level = full ? theme.fg("user", state.thinkingLevel) : dim(state.thinkingLevel);
|
|
290
|
+
right(level, p.thinking, full ? { group: "model" } : {});
|
|
283
291
|
}
|
|
284
292
|
const usage = full ? {} : usageItems(stats, this.queue, this.source, theme);
|
|
285
293
|
if (usage.tokens !== undefined)
|
|
@@ -297,13 +305,15 @@ export class StatusBar {
|
|
|
297
305
|
const used = stats.tokens.input + stats.tokens.output + stats.tokens.cacheRead > 0;
|
|
298
306
|
if (cost === undefined && used)
|
|
299
307
|
right(dim("$?"), FULL.cost);
|
|
300
|
-
else if (cost !== undefined && cost > 0)
|
|
301
|
-
right(
|
|
308
|
+
else if (cost !== undefined && cost > 0) {
|
|
309
|
+
right(theme.fg("warning", formatCost(cost)), FULL.cost);
|
|
310
|
+
}
|
|
302
311
|
}
|
|
303
312
|
const started = this.source.sessionStartedAt?.() ?? stats.telemetry?.sessionStartedAt;
|
|
304
313
|
if (started !== undefined) {
|
|
305
314
|
const now = this.source.now?.() ?? Date.now();
|
|
306
|
-
|
|
315
|
+
const text = formatDuration(now - started);
|
|
316
|
+
right(full ? theme.fg("tool", text) : dim(text), p.duration, { reserve: 6 });
|
|
307
317
|
}
|
|
308
318
|
if (usage.queue !== undefined)
|
|
309
319
|
right(usage.queue, COMPACT.queue);
|
|
@@ -315,9 +325,13 @@ export class StatusBar {
|
|
|
315
325
|
right(usage.preset, COMPACT.preset);
|
|
316
326
|
if (usage.host !== undefined)
|
|
317
327
|
right(usage.host, COMPACT.host);
|
|
328
|
+
const quota = full ? undefined : this.source.quota?.();
|
|
329
|
+
const quotaItem = typeof quota === "object" ? compactQuotaItem(quota.quota, theme) : undefined;
|
|
330
|
+
if (quotaItem !== undefined)
|
|
331
|
+
right(quotaItem, COMPACT.quota);
|
|
318
332
|
return parts;
|
|
319
333
|
}
|
|
320
|
-
/** `目录 ⎇ 分支 短提交 +a −b
|
|
334
|
+
/** `目录 ⎇ 分支 短提交 +a −b`:三项同组、各自按优先级丢弃;full 另带 `↑N ↓N` 并按参考配色。 */
|
|
321
335
|
gitParts(p, full) {
|
|
322
336
|
const git = this.source.git?.();
|
|
323
337
|
if (git === undefined)
|
|
@@ -325,19 +339,37 @@ export class StatusBar {
|
|
|
325
339
|
const theme = this.theme;
|
|
326
340
|
const g = theme.glyphs;
|
|
327
341
|
const dim = (text) => theme.fg("dim", text);
|
|
342
|
+
const ok = (text) => (full ? theme.fg("success", text) : dim(text));
|
|
328
343
|
const out = [];
|
|
329
|
-
const add = (text, priority) => void out.push({ text
|
|
344
|
+
const add = (text, priority) => void out.push({ text, priority, zone: "right", group: "git" });
|
|
330
345
|
if (git.dir !== "")
|
|
331
|
-
add(git.dir, p.dir);
|
|
346
|
+
add(ok(git.dir), p.dir);
|
|
332
347
|
const info = git.info;
|
|
333
348
|
if (info === undefined)
|
|
334
349
|
return out;
|
|
335
|
-
const head = [
|
|
336
|
-
if (
|
|
337
|
-
|
|
350
|
+
const head = [];
|
|
351
|
+
if (info.branch !== undefined)
|
|
352
|
+
head.push(ok(info.branch));
|
|
353
|
+
if (info.shortHead !== undefined)
|
|
354
|
+
head.push(dim(info.shortHead));
|
|
355
|
+
if (full && info.ahead !== undefined && info.ahead > 0) {
|
|
356
|
+
head.push(theme.fg("code", `${g.arrowUp}${info.ahead}`));
|
|
357
|
+
}
|
|
358
|
+
if (full && info.behind !== undefined && info.behind > 0) {
|
|
359
|
+
head.push(theme.fg("error", `${g.arrowDown}${info.behind}`));
|
|
360
|
+
}
|
|
361
|
+
if (!full) {
|
|
362
|
+
const text = [info.branch, info.shortHead].filter((s) => s !== undefined).join(" ");
|
|
363
|
+
if (text !== "")
|
|
364
|
+
add(dim(`${g.branch} ${text}`), p.branch);
|
|
365
|
+
}
|
|
366
|
+
else if (head.length > 0)
|
|
367
|
+
add(`${dim(g.branch)} ${head.join(" ")}`, p.branch);
|
|
338
368
|
if (info.insertions !== undefined && info.deletions !== undefined) {
|
|
339
369
|
const { insertions: a, deletions: d } = info;
|
|
340
|
-
add(full
|
|
370
|
+
add(full
|
|
371
|
+
? `${dim("(")}${theme.fg("success", `+${a}`)}${dim(",")}${theme.fg("error", `-${d}`)}${dim(")")}`
|
|
372
|
+
: dim(`+${a} ${g.ascii ? "-" : "−"}${d}`), p.diff);
|
|
341
373
|
}
|
|
342
374
|
return out;
|
|
343
375
|
}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* - 丢弃顺序(§1.2,数字大先丢;`tps` 与 `[-]` 永不丢):tok/耗时 10 → codemode 9 → queue 8 → token 7 →
|
|
15
15
|
* cache 6 → rebill 5 → 预设 4 → 宿主 3 → avg 2 → ttft 1;数字按最宽形状占位,不随数值抖动;
|
|
16
16
|
* - `compact` 时不占行(render 返回空)。
|
|
17
|
+
* - [W6] 配色(用户参考):标签、单位、`•`、括号 dim;速率数字 `tool`(紫);输出量与耗时、avg 数字 accent(蓝);
|
|
18
|
+
* ttft 数字 `tool`;右区用量项不变。
|
|
17
19
|
*/
|
|
18
20
|
import { type Component, type Theme } from "../../tui.js";
|
|
19
21
|
import { type StatusBar, type StatusBarSource } from "./status-bar.js";
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* - 丢弃顺序(§1.2,数字大先丢;`tps` 与 `[-]` 永不丢):tok/耗时 10 → codemode 9 → queue 8 → token 7 →
|
|
15
15
|
* cache 6 → rebill 5 → 预设 4 → 宿主 3 → avg 2 → ttft 1;数字按最宽形状占位,不随数值抖动;
|
|
16
16
|
* - `compact` 时不占行(render 返回空)。
|
|
17
|
+
* - [W6] 配色(用户参考):标签、单位、`•`、括号 dim;速率数字 `tool`(紫);输出量与耗时、avg 数字 accent(蓝);
|
|
18
|
+
* ttft 数字 `tool`;右区用量项不变。
|
|
17
19
|
*/
|
|
18
20
|
import { truncateToWidth } from "../../tui.js";
|
|
19
21
|
import { formatTokens, layoutRow, usageItems, } from "./status-bar.js";
|
|
@@ -68,29 +70,32 @@ export class StatusLine {
|
|
|
68
70
|
if (text !== undefined)
|
|
69
71
|
parts.push({ text, priority, zone: "right" });
|
|
70
72
|
};
|
|
73
|
+
const blue = (text) => theme.fg("accent", text);
|
|
74
|
+
const purple = (text) => theme.fg("tool", text);
|
|
75
|
+
const amountText = (tokens, ms) => `${blue(formatTokens(tokens))}${dim(" tok / ")}${blue(formatSeconds(ms))}`;
|
|
71
76
|
let tps;
|
|
72
77
|
let amount;
|
|
73
78
|
if (live !== undefined) {
|
|
74
79
|
tps = live.tps;
|
|
75
|
-
amount =
|
|
80
|
+
amount = amountText(live.outputTokens, live.elapsedMs);
|
|
76
81
|
}
|
|
77
82
|
else if (last?.doneAt !== undefined) {
|
|
78
83
|
tps = last.tps;
|
|
79
84
|
if (last.outputTokens !== undefined && last.firstTokenAt !== undefined) {
|
|
80
|
-
amount =
|
|
85
|
+
amount = amountText(last.outputTokens, last.doneAt - last.firstTokenAt);
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
const label = live !== undefined ? theme.fg("accent", "tps:") : dim("tps:");
|
|
84
|
-
left(`${label} ${
|
|
89
|
+
left(`${label} ${tps === undefined ? dim("—") : `${purple(formatRate(tps))}${dim(" tok/s")}`}`, undefined, RESERVE.tps);
|
|
85
90
|
if (amount !== undefined) {
|
|
86
|
-
left(
|
|
91
|
+
left(amount, PRIORITY.amount, RESERVE.amount, { lead: ` ${theme.glyphs.dot} ` });
|
|
87
92
|
}
|
|
88
93
|
const stat = { group: "stats", lead: " " };
|
|
89
94
|
if (telemetry?.avgTps !== undefined) {
|
|
90
|
-
left(dim(
|
|
95
|
+
left(`${dim("avg ")}${blue(formatRate(telemetry.avgTps))}`, PRIORITY.avg, RESERVE.avg, stat);
|
|
91
96
|
}
|
|
92
97
|
if (last?.ttftMs !== undefined) {
|
|
93
|
-
left(dim(
|
|
98
|
+
left(`${dim("ttft ")}${purple(formatSeconds(last.ttftMs))}`, PRIORITY.ttft, RESERVE.ttft, stat);
|
|
94
99
|
}
|
|
95
100
|
const usage = usageItems(stats, this.bar.queued(), this.source, theme);
|
|
96
101
|
right(usage.tokens, PRIORITY.tokens);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 订阅配额行(full 布局第三行,状态栏下方;compact 只在行尾追加一个短项)。[W6]
|
|
3
|
+
*
|
|
4
|
+
* ```text
|
|
5
|
+
* Session: 10.0% | Reset: 2h 18m | Weekly: 31.0% | Weekly Reset: 6d 5h ≥ 80 列
|
|
6
|
+
* 5h 10% ↻2h18m · wk 31% ↻6d5h < 80 列
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* - 数据:会话的 `quota_update`(codex flavor 的 `x-codex-primary/secondary-*` 头、`codex.rate_limits` 事件;
|
|
10
|
+
* SIWC 只有 429)。primary = 5 小时窗口、secondary = 周窗口;窗口长度不是 5 h / 7 d 时标签换成实际时长。
|
|
11
|
+
* - 只在当前模型走 ChatGPT 订阅(compat `chatgptBackend`)时出现;codex flavor 还没有数据时显示
|
|
12
|
+
* 「配额:首次请求后显示」(第一次请求就会带回配额,先占住行免得行数跳动),SIWC 没有数据时不占行
|
|
13
|
+
* (只有超限才有配额,占位会一直挂着误导)。
|
|
14
|
+
* - 着色:标签与分隔 dim;百分比按阈值(≥ 70% warning、≥ 90% error,否则 success);重置时间 `tool`(紫)。
|
|
15
|
+
* - 重置时间是相对时长(`2h 18m`、`6d 5h`),按渲染时刻算;StatusArea 每分钟重画一次。
|
|
16
|
+
*/
|
|
17
|
+
import type { QuotaUpdateEvent } from "../../agent/types-w6.js";
|
|
18
|
+
import type { StatusLineMode } from "../../config/types.js";
|
|
19
|
+
import { type Component, type Theme } from "../../tui.js";
|
|
20
|
+
import { type Part } from "./status-bar.js";
|
|
21
|
+
/** 配额行的数据:有数据 / 等首次请求 / 不显示(undefined)。 */
|
|
22
|
+
export type QuotaView = {
|
|
23
|
+
quota: QuotaUpdateEvent;
|
|
24
|
+
} | "pending" | undefined;
|
|
25
|
+
/** 第三行改用短格式的宽度。 */
|
|
26
|
+
export declare const QUOTA_NARROW_WIDTH = 80;
|
|
27
|
+
/** 距重置:`6d 5h` / `2h 18m` / `18m`(已过或不足一分钟 `0m`);`tight` 去掉空格。 */
|
|
28
|
+
export declare function formatRemaining(ms: number, tight?: boolean): string;
|
|
29
|
+
/** compact 行尾的短项 `5h 10% wk 31%`(项内只用空格,不含 ` · `,宿主解析不受影响)。 */
|
|
30
|
+
export declare function compactQuotaItem(quota: QuotaUpdateEvent, theme: Theme): string | undefined;
|
|
31
|
+
/** 配额行各项(宽 / 窄两种格式);重置时间先丢,主窗口百分比永不丢。 */
|
|
32
|
+
export declare function quotaParts(quota: QuotaUpdateEvent, width: number, now: number, theme: Theme): Part[];
|
|
33
|
+
export interface QuotaLineSource {
|
|
34
|
+
layout(): StatusLineMode;
|
|
35
|
+
quota(): QuotaView;
|
|
36
|
+
now(): number;
|
|
37
|
+
}
|
|
38
|
+
export declare class QuotaLine implements Component {
|
|
39
|
+
private readonly source;
|
|
40
|
+
private readonly theme;
|
|
41
|
+
constructor(source: QuotaLineSource, theme: Theme);
|
|
42
|
+
render(width: number): string[];
|
|
43
|
+
invalidate(): void;
|
|
44
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 订阅配额行(full 布局第三行,状态栏下方;compact 只在行尾追加一个短项)。[W6]
|
|
3
|
+
*
|
|
4
|
+
* ```text
|
|
5
|
+
* Session: 10.0% | Reset: 2h 18m | Weekly: 31.0% | Weekly Reset: 6d 5h ≥ 80 列
|
|
6
|
+
* 5h 10% ↻2h18m · wk 31% ↻6d5h < 80 列
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* - 数据:会话的 `quota_update`(codex flavor 的 `x-codex-primary/secondary-*` 头、`codex.rate_limits` 事件;
|
|
10
|
+
* SIWC 只有 429)。primary = 5 小时窗口、secondary = 周窗口;窗口长度不是 5 h / 7 d 时标签换成实际时长。
|
|
11
|
+
* - 只在当前模型走 ChatGPT 订阅(compat `chatgptBackend`)时出现;codex flavor 还没有数据时显示
|
|
12
|
+
* 「配额:首次请求后显示」(第一次请求就会带回配额,先占住行免得行数跳动),SIWC 没有数据时不占行
|
|
13
|
+
* (只有超限才有配额,占位会一直挂着误导)。
|
|
14
|
+
* - 着色:标签与分隔 dim;百分比按阈值(≥ 70% warning、≥ 90% error,否则 success);重置时间 `tool`(紫)。
|
|
15
|
+
* - 重置时间是相对时长(`2h 18m`、`6d 5h`),按渲染时刻算;StatusArea 每分钟重画一次。
|
|
16
|
+
*/
|
|
17
|
+
import { msg } from "../../i18n/index.js";
|
|
18
|
+
import { levelColor, truncateToWidth, visibleWidth, } from "../../tui.js";
|
|
19
|
+
import { layoutRow } from "./status-bar.js";
|
|
20
|
+
/** 第三行改用短格式的宽度。 */
|
|
21
|
+
export const QUOTA_NARROW_WIDTH = 80;
|
|
22
|
+
const FIVE_HOURS = 300;
|
|
23
|
+
const WEEK = 10_080;
|
|
24
|
+
/** 距重置:`6d 5h` / `2h 18m` / `18m`(已过或不足一分钟 `0m`);`tight` 去掉空格。 */
|
|
25
|
+
export function formatRemaining(ms, tight = false) {
|
|
26
|
+
const minutes = Math.max(0, Math.floor(ms / 60_000));
|
|
27
|
+
const days = Math.floor(minutes / 1440);
|
|
28
|
+
const hours = Math.floor((minutes % 1440) / 60);
|
|
29
|
+
const sep = tight ? "" : " ";
|
|
30
|
+
if (days > 0)
|
|
31
|
+
return `${days}d${sep}${hours}h`;
|
|
32
|
+
if (hours > 0)
|
|
33
|
+
return `${hours}h${sep}${minutes % 60}m`;
|
|
34
|
+
return `${minutes}m`;
|
|
35
|
+
}
|
|
36
|
+
/** 窗口长度的紧凑写法(标签用):`5h`、`7d`、`90m`。 */
|
|
37
|
+
function windowSpan(minutes) {
|
|
38
|
+
if (minutes % 1440 === 0)
|
|
39
|
+
return `${minutes / 1440}d`;
|
|
40
|
+
if (minutes % 60 === 0)
|
|
41
|
+
return `${minutes / 60}h`;
|
|
42
|
+
return `${Math.round(minutes)}m`;
|
|
43
|
+
}
|
|
44
|
+
function labels(w, which) {
|
|
45
|
+
const m = msg().interactive.statusLine.quota;
|
|
46
|
+
const minutes = w.windowMinutes;
|
|
47
|
+
const standard = minutes === undefined || minutes === (which === "primary" ? FIVE_HOURS : WEEK);
|
|
48
|
+
if (!standard)
|
|
49
|
+
return { pct: m.window(windowSpan(minutes)), reset: m.windowReset(windowSpan(minutes)) };
|
|
50
|
+
return which === "primary"
|
|
51
|
+
? { pct: m.session, reset: m.reset }
|
|
52
|
+
: { pct: m.weekly, reset: m.weeklyReset };
|
|
53
|
+
}
|
|
54
|
+
function shortLabel(w, which) {
|
|
55
|
+
const m = msg().interactive.statusLine.quota;
|
|
56
|
+
const minutes = w.windowMinutes;
|
|
57
|
+
if (minutes === FIVE_HOURS || (minutes === undefined && which === "primary"))
|
|
58
|
+
return m.short5h;
|
|
59
|
+
if (minutes === WEEK || (minutes === undefined && which === "secondary"))
|
|
60
|
+
return m.shortWeek;
|
|
61
|
+
return windowSpan(minutes);
|
|
62
|
+
}
|
|
63
|
+
function percent(theme, used, decimal) {
|
|
64
|
+
return theme.fg(levelColor(used / 100), decimal ? `${used.toFixed(1)}%` : `${Math.round(used)}%`);
|
|
65
|
+
}
|
|
66
|
+
function windows(quota) {
|
|
67
|
+
const out = [];
|
|
68
|
+
if (quota.primary !== undefined)
|
|
69
|
+
out.push(["primary", quota.primary]);
|
|
70
|
+
if (quota.secondary !== undefined)
|
|
71
|
+
out.push(["secondary", quota.secondary]);
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
/** compact 行尾的短项 `5h 10% wk 31%`(项内只用空格,不含 ` · `,宿主解析不受影响)。 */
|
|
75
|
+
export function compactQuotaItem(quota, theme) {
|
|
76
|
+
const dim = (text) => theme.fg("dim", text);
|
|
77
|
+
const items = windows(quota).map(([which, w]) => `${dim(shortLabel(w, which))} ${percent(theme, w.usedPercent, false)}`);
|
|
78
|
+
return items.length === 0 ? undefined : items.join(" ");
|
|
79
|
+
}
|
|
80
|
+
/** 配额行各项(宽 / 窄两种格式);重置时间先丢,主窗口百分比永不丢。 */
|
|
81
|
+
export function quotaParts(quota, width, now, theme) {
|
|
82
|
+
const dim = (text) => theme.fg("dim", text);
|
|
83
|
+
const narrow = width < QUOTA_NARROW_WIDTH;
|
|
84
|
+
const parts = [];
|
|
85
|
+
windows(quota).forEach(([which, w], i) => {
|
|
86
|
+
const first = i === 0;
|
|
87
|
+
const group = narrow ? which : undefined;
|
|
88
|
+
const name = narrow ? `${shortLabel(w, which)} ` : labels(w, which).pct;
|
|
89
|
+
parts.push({
|
|
90
|
+
text: dim(name) + percent(theme, w.usedPercent, !narrow),
|
|
91
|
+
priority: first ? undefined : 2,
|
|
92
|
+
zone: "left",
|
|
93
|
+
...(group !== undefined ? { group } : {}),
|
|
94
|
+
});
|
|
95
|
+
if (w.resetsAt === undefined)
|
|
96
|
+
return;
|
|
97
|
+
const left = formatRemaining(w.resetsAt - now, narrow);
|
|
98
|
+
parts.push({
|
|
99
|
+
text: narrow
|
|
100
|
+
? theme.fg("tool", `${theme.glyphs.retry}${left}`)
|
|
101
|
+
: dim(labels(w, which).reset) + theme.fg("tool", left),
|
|
102
|
+
priority: first ? 3 : 1,
|
|
103
|
+
zone: "left",
|
|
104
|
+
// 宽格式按最宽形状占位(`23h 59m`),数值变化不让某项时有时无
|
|
105
|
+
reserve: narrow ? 0 : visibleWidth(labels(w, which).reset) + 7,
|
|
106
|
+
...(group !== undefined ? { group } : {}),
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
return parts;
|
|
110
|
+
}
|
|
111
|
+
export class QuotaLine {
|
|
112
|
+
source;
|
|
113
|
+
theme;
|
|
114
|
+
constructor(source, theme) {
|
|
115
|
+
this.source = source;
|
|
116
|
+
this.theme = theme;
|
|
117
|
+
}
|
|
118
|
+
render(width) {
|
|
119
|
+
if (this.source.layout() !== "full")
|
|
120
|
+
return [];
|
|
121
|
+
const view = this.source.quota();
|
|
122
|
+
if (view === undefined)
|
|
123
|
+
return [];
|
|
124
|
+
const theme = this.theme;
|
|
125
|
+
if (view === "pending") {
|
|
126
|
+
return [truncateToWidth(theme.fg("dim", msg().interactive.statusLine.quota.pending), width)];
|
|
127
|
+
}
|
|
128
|
+
const parts = quotaParts(view.quota, width, this.source.now(), theme);
|
|
129
|
+
if (parts.length === 0)
|
|
130
|
+
return [];
|
|
131
|
+
const sep = width < QUOTA_NARROW_WIDTH ? " · " : " | ";
|
|
132
|
+
return [layoutRow(parts, width, theme, { sep })];
|
|
133
|
+
}
|
|
134
|
+
invalidate() { }
|
|
135
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* 后台任务的工具调用立即返回,工具行下面多一行跟随状态(`↳ t2 explore · 完成 · 7 轮 …`,运行中每秒刷新);
|
|
13
13
|
* 完成后父会话收到的 `<task-notification>` 只显示一行(`notificationSummary`),失败 / 停止另有一行提示。
|
|
14
14
|
* 外部 runner(claude / codex)同一组事件,用量可能缺。
|
|
15
|
+
* [W7-C] `subagent_background`(前台转后台):置 `background`,工具行出跟随行。
|
|
15
16
|
*/
|
|
16
17
|
import type { SessionEvent, SubagentStatus } from "../../agent/types.js";
|
|
17
18
|
import type { Usage } from "../../ai/types.js";
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* 后台任务的工具调用立即返回,工具行下面多一行跟随状态(`↳ t2 explore · 完成 · 7 轮 …`,运行中每秒刷新);
|
|
13
13
|
* 完成后父会话收到的 `<task-notification>` 只显示一行(`notificationSummary`),失败 / 停止另有一行提示。
|
|
14
14
|
* 外部 runner(claude / codex)同一组事件,用量可能缺。
|
|
15
|
+
* [W7-C] `subagent_background`(前台转后台):置 `background`,工具行出跟随行。
|
|
15
16
|
*/
|
|
16
17
|
import { msg } from "../../i18n/index.js";
|
|
17
18
|
import { formatElapsed } from "../../tui.js";
|
|
@@ -61,6 +62,13 @@ export class SubagentTracker {
|
|
|
61
62
|
}
|
|
62
63
|
return state;
|
|
63
64
|
}
|
|
65
|
+
case "subagent_background": {
|
|
66
|
+
const state = this.tasks.get(event.taskId);
|
|
67
|
+
if (state === undefined)
|
|
68
|
+
return undefined;
|
|
69
|
+
state.background = true;
|
|
70
|
+
return state;
|
|
71
|
+
}
|
|
64
72
|
case "subagent_end": {
|
|
65
73
|
const state = this.tasks.get(event.taskId);
|
|
66
74
|
if (state === undefined)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 前台子 Agent 任务转后台与栏内停止的界面接线(docs/agents-concurrency-plan.md §2.4、§2.8)。[W7-C]
|
|
3
|
+
*
|
|
4
|
+
* - `Ctrl+B`(`app.tasks.background`):有阻塞中的前台任务(`registry.blocking()`,含 `task_ctl wait`)时
|
|
5
|
+
* 全部转后台(`session.backgroundTask(undefined, "user")`),不看输入框;没有时落回编辑器。
|
|
6
|
+
* - 栏内 `b` / `Ctrl+B`:转后台选中的任务;`x`:1.5 s 内连按两次停止(`StopArm`)。
|
|
7
|
+
* - `/tasks bg [id]`(commands-core.ts,line 模式同):同 Ctrl+B / 指定任务。
|
|
8
|
+
* - Esc 中断只连带前台任务:仍在运行的后台任务写进中断提示(`runningBackground`)。
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentSession } from "../../agent/types.js";
|
|
11
|
+
import type { Keybindings } from "../../tui.js";
|
|
12
|
+
/** 栏内 `x` 两次之间的最长间隔。 */
|
|
13
|
+
export declare const STOP_CONFIRM_MS = 1500;
|
|
14
|
+
/** 正在阻塞父回合的任务(前台运行中、或有 `task_ctl wait` 在等)。 */
|
|
15
|
+
export declare function blockingTasks(session: AgentSession): string[];
|
|
16
|
+
/** 人工转后台:不给 taskId = 全部阻塞中的任务(没有时不调用,返回空)。 */
|
|
17
|
+
export declare function backgroundTasks(session: AgentSession, taskId?: string): string[];
|
|
18
|
+
/** 仍在运行的后台任务(Esc 中断不影响它们)。 */
|
|
19
|
+
export declare function runningBackground(session: AgentSession): string[];
|
|
20
|
+
/** 转后台后的一行提示;没有转任何任务时给「没有可转的」。 */
|
|
21
|
+
export declare function backgroundedText(ids: readonly string[]): string;
|
|
22
|
+
/** `ctrl+b` → `Ctrl+B`(提示行用);动作没有绑定时 undefined。 */
|
|
23
|
+
export declare function keyLabel(keys: Keybindings, action: "app.tasks.background"): string | undefined;
|
|
24
|
+
/** 栏内 `x` 的双击确认:第一次 `arm`(提示「再按 x 停止」),间隔内对同一任务再按为 `stop`。 */
|
|
25
|
+
export declare class StopArm {
|
|
26
|
+
private readonly windowMs;
|
|
27
|
+
private armed;
|
|
28
|
+
constructor(windowMs?: number);
|
|
29
|
+
press(taskId: string, now: number): "arm" | "stop";
|
|
30
|
+
reset(): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 前台子 Agent 任务转后台与栏内停止的界面接线(docs/agents-concurrency-plan.md §2.4、§2.8)。[W7-C]
|
|
3
|
+
*
|
|
4
|
+
* - `Ctrl+B`(`app.tasks.background`):有阻塞中的前台任务(`registry.blocking()`,含 `task_ctl wait`)时
|
|
5
|
+
* 全部转后台(`session.backgroundTask(undefined, "user")`),不看输入框;没有时落回编辑器。
|
|
6
|
+
* - 栏内 `b` / `Ctrl+B`:转后台选中的任务;`x`:1.5 s 内连按两次停止(`StopArm`)。
|
|
7
|
+
* - `/tasks bg [id]`(commands-core.ts,line 模式同):同 Ctrl+B / 指定任务。
|
|
8
|
+
* - Esc 中断只连带前台任务:仍在运行的后台任务写进中断提示(`runningBackground`)。
|
|
9
|
+
*/
|
|
10
|
+
import { registryOf } from "../../agent/subagent-registry.js";
|
|
11
|
+
import { msg } from "../../i18n/index.js";
|
|
12
|
+
/** 栏内 `x` 两次之间的最长间隔。 */
|
|
13
|
+
export const STOP_CONFIRM_MS = 1500;
|
|
14
|
+
/** 正在阻塞父回合的任务(前台运行中、或有 `task_ctl wait` 在等)。 */
|
|
15
|
+
export function blockingTasks(session) {
|
|
16
|
+
return registryOf(session.state.sessionId)?.blocking() ?? [];
|
|
17
|
+
}
|
|
18
|
+
/** 人工转后台:不给 taskId = 全部阻塞中的任务(没有时不调用,返回空)。 */
|
|
19
|
+
export function backgroundTasks(session, taskId) {
|
|
20
|
+
if (taskId === undefined && blockingTasks(session).length === 0)
|
|
21
|
+
return [];
|
|
22
|
+
return session.backgroundTask(taskId, "user");
|
|
23
|
+
}
|
|
24
|
+
/** 仍在运行的后台任务(Esc 中断不影响它们)。 */
|
|
25
|
+
export function runningBackground(session) {
|
|
26
|
+
const registry = registryOf(session.state.sessionId);
|
|
27
|
+
if (registry === undefined)
|
|
28
|
+
return [];
|
|
29
|
+
return registry
|
|
30
|
+
.list()
|
|
31
|
+
.filter((task) => task.status === "running" && task.background)
|
|
32
|
+
.map((task) => task.taskId);
|
|
33
|
+
}
|
|
34
|
+
/** 转后台后的一行提示;没有转任何任务时给「没有可转的」。 */
|
|
35
|
+
export function backgroundedText(ids) {
|
|
36
|
+
const m = msg().agents.background;
|
|
37
|
+
return ids.length === 0 ? m.none : m.done(ids.join(", "));
|
|
38
|
+
}
|
|
39
|
+
/** `ctrl+b` → `Ctrl+B`(提示行用);动作没有绑定时 undefined。 */
|
|
40
|
+
export function keyLabel(keys, action) {
|
|
41
|
+
const id = keys.keys(action)[0];
|
|
42
|
+
if (id === undefined)
|
|
43
|
+
return undefined;
|
|
44
|
+
return id
|
|
45
|
+
.split("+")
|
|
46
|
+
.map((part) => part.length === 1 ? part.toUpperCase() : part[0].toUpperCase() + part.slice(1))
|
|
47
|
+
.join("+");
|
|
48
|
+
}
|
|
49
|
+
/** 栏内 `x` 的双击确认:第一次 `arm`(提示「再按 x 停止」),间隔内对同一任务再按为 `stop`。 */
|
|
50
|
+
export class StopArm {
|
|
51
|
+
windowMs;
|
|
52
|
+
armed;
|
|
53
|
+
constructor(windowMs = STOP_CONFIRM_MS) {
|
|
54
|
+
this.windowMs = windowMs;
|
|
55
|
+
}
|
|
56
|
+
press(taskId, now) {
|
|
57
|
+
const armed = this.armed;
|
|
58
|
+
if (armed !== undefined && armed.taskId === taskId && now - armed.at < this.windowMs) {
|
|
59
|
+
this.armed = undefined;
|
|
60
|
+
return "stop";
|
|
61
|
+
}
|
|
62
|
+
this.armed = { taskId, at: now };
|
|
63
|
+
return "arm";
|
|
64
|
+
}
|
|
65
|
+
reset() {
|
|
66
|
+
this.armed = undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* - 运行中摘要行 `⎿ ⠋ 运行中 · 4s`:spinner 与底部 Loader 同帧(ToolTracker.tick 由 Loader.onFrame 驱动),
|
|
12
12
|
* 标题行不变;bash 流式显示尾部 8 行(muted)。task 显示 `子 Agent · 运行中 1m05s`;有 `subagent_*`
|
|
13
13
|
* 事件时(W5-U,subagent-view.ts)改为 `explore · 运行中 1m05s · 3 轮 · 最近 3 个工具 · ↑↓`,后台任务
|
|
14
|
-
* 在结果摘要下多一行跟随状态。
|
|
14
|
+
* 在结果摘要下多一行跟随状态。[W7-C] 工具已返回而任务仍在后台跑(`details.status: "running"`):摘要行
|
|
15
|
+
* `⎿ 已转后台 · 12s`(前台转后台)/ `⎿ 后台运行`(直接后台),给模型的说明文字折叠时不显示。
|
|
15
16
|
* - 折叠(缺省):结果前 3 行;edit 显示 `details.diff`(`@@` dim、`+` success、`-` error、上下文 muted,
|
|
16
17
|
* 宽 ≥ 60 时带行号列),前 12 行;`Ctrl+O` 展开全部(上限 400 行,长行折行)。
|
|
17
18
|
* - 嵌套:带 `parentToolCallId` 的调用(codemode 脚本里的 `tools.*`)挂在外层调用下、右移 4 列;折叠时只列
|
|
@@ -85,6 +86,11 @@ export declare class ToolView implements Component {
|
|
|
85
86
|
header(width: number): string;
|
|
86
87
|
/** ` ⎿ 摘要`:运行中带 spinner 与秒数,完成后是结果摘要。 */
|
|
87
88
|
summaryLine(width: number): string | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* [W7-C] task 已返回、任务仍在后台跑:true = 前台转后台(固定英文结果文本),false = 直接后台;
|
|
91
|
+
* 其它情况 undefined。
|
|
92
|
+
*/
|
|
93
|
+
private backgrounded;
|
|
88
94
|
/** 后台子 Agent:结果摘要下一行跟随状态 ` ↳ t2 explore · 完成 2m · 7 轮 …`。 */
|
|
89
95
|
private followLine;
|
|
90
96
|
private custom;
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* - 运行中摘要行 `⎿ ⠋ 运行中 · 4s`:spinner 与底部 Loader 同帧(ToolTracker.tick 由 Loader.onFrame 驱动),
|
|
12
12
|
* 标题行不变;bash 流式显示尾部 8 行(muted)。task 显示 `子 Agent · 运行中 1m05s`;有 `subagent_*`
|
|
13
13
|
* 事件时(W5-U,subagent-view.ts)改为 `explore · 运行中 1m05s · 3 轮 · 最近 3 个工具 · ↑↓`,后台任务
|
|
14
|
-
* 在结果摘要下多一行跟随状态。
|
|
14
|
+
* 在结果摘要下多一行跟随状态。[W7-C] 工具已返回而任务仍在后台跑(`details.status: "running"`):摘要行
|
|
15
|
+
* `⎿ 已转后台 · 12s`(前台转后台)/ `⎿ 后台运行`(直接后台),给模型的说明文字折叠时不显示。
|
|
15
16
|
* - 折叠(缺省):结果前 3 行;edit 显示 `details.diff`(`@@` dim、`+` success、`-` error、上下文 muted,
|
|
16
17
|
* 宽 ≥ 60 时带行号列),前 12 行;`Ctrl+O` 展开全部(上限 400 行,长行折行)。
|
|
17
18
|
* - 嵌套:带 `parentToolCallId` 的调用(codemode 脚本里的 `tools.*`)挂在外层调用下、右移 4 列;折叠时只列
|
|
@@ -176,6 +177,12 @@ export class ToolView {
|
|
|
176
177
|
return truncateToWidth(lead + theme.fg("accent", frame) + " " + theme.fg("muted", what), width);
|
|
177
178
|
}
|
|
178
179
|
const result = this.result;
|
|
180
|
+
const moved = this.backgrounded();
|
|
181
|
+
if (moved !== undefined) {
|
|
182
|
+
const m = msg().agents.background;
|
|
183
|
+
const text = moved ? m.moved(formatElapsed(this.elapsedMs)) : m.started;
|
|
184
|
+
return truncateToWidth(lead + theme.fg("muted", text), width);
|
|
185
|
+
}
|
|
179
186
|
const text = resultSummary({
|
|
180
187
|
name: this.toolName,
|
|
181
188
|
args: this.args,
|
|
@@ -187,6 +194,19 @@ export class ToolView {
|
|
|
187
194
|
}, theme);
|
|
188
195
|
return truncateToWidth(lead + text, width);
|
|
189
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* [W7-C] task 已返回、任务仍在后台跑:true = 前台转后台(固定英文结果文本),false = 直接后台;
|
|
199
|
+
* 其它情况 undefined。
|
|
200
|
+
*/
|
|
201
|
+
backgrounded() {
|
|
202
|
+
const result = this.result;
|
|
203
|
+
if (this.toolName !== "task" || this.state !== "done" || result === undefined)
|
|
204
|
+
return undefined;
|
|
205
|
+
const details = result.details;
|
|
206
|
+
if (details?.status !== "running")
|
|
207
|
+
return undefined;
|
|
208
|
+
return /moved to the background/i.test(contentText(result.content));
|
|
209
|
+
}
|
|
190
210
|
/** 后台子 Agent:结果摘要下一行跟随状态 ` ↳ t2 explore · 完成 2m · 7 轮 …`。 */
|
|
191
211
|
followLine(width) {
|
|
192
212
|
const sub = this.subagentState();
|
|
@@ -292,6 +312,9 @@ export class ToolView {
|
|
|
292
312
|
}
|
|
293
313
|
return out;
|
|
294
314
|
}
|
|
315
|
+
// [W7-C] 后台运行中的 task:结果是给模型的说明,折叠时不显示(跟随行已有状态)
|
|
316
|
+
if (!this.expanded && this.backgrounded() !== undefined)
|
|
317
|
+
return out;
|
|
295
318
|
const custom = this.customResult(width);
|
|
296
319
|
if (custom !== undefined)
|
|
297
320
|
return [...out, ...custom];
|
|
@@ -21,12 +21,17 @@
|
|
|
21
21
|
* - [W5-H2] plan 模式产出计划、`plan.unattended: stop`(缺省)没人审批:stderr 一行(计划文件与审批办法)、
|
|
22
22
|
* json 带 `planPending{planId, version, filePath}`、退出码 9。
|
|
23
23
|
* - [W5-H2] `--image` / `@图片` 超限时按 config `images.resize` 缩放(缺省 auto)。
|
|
24
|
+
* - [W7-B2] 后台子 Agent(显式 `background:true` 或 `subagents.background: always`):主回合结束后若还有任务
|
|
25
|
+
* 在跑或通知待投递,stderr 一行提示并等它们结束、跑完通知回合再输出(最终文本 = 最后一条助手回复);
|
|
26
|
+
* 受 `--max-turns` / `--max-cost` / `limits.*` 约束(到限即停止等待,退出码 8),SIGINT / SIGTERM 照常中止
|
|
27
|
+
* (未结束的任务随会话关闭被停止);json 结果带 `tasks`(同 `getStats().tasks`)。
|
|
24
28
|
* - 退出码:最终助手消息 `error / aborted` 或提示被拒 → 1;有工具调用被拒 → 7;到达预算 → 8;
|
|
25
29
|
* 计划待审批 → 9;SIGINT 130、SIGTERM 143(先 abort)。
|
|
26
30
|
*/
|
|
27
31
|
import type { CliIo, ModeContext } from "../../cli/deps.js";
|
|
28
32
|
import type { Runtime } from "../../cli/runtime.js";
|
|
29
33
|
import type { LimitReachedEvent, PlanProposedEvent } from "../../agent/types.js";
|
|
34
|
+
import type { AgentSession } from "../../agent/types.js";
|
|
30
35
|
export declare function joinPrompt(argument: string | undefined, piped: string): string;
|
|
31
36
|
/** 等 stdin 多久后提示一次「正在等待」。 */
|
|
32
37
|
export declare const STDIN_WAIT_HINT_MS = 3000;
|
|
@@ -39,6 +44,12 @@ export declare function stdinWaitMs(env: Readonly<Record<string, string | undefi
|
|
|
39
44
|
*/
|
|
40
45
|
export declare function readPromptStdin(io: CliIo, prompt: string | undefined, mode: "auto" | "explicit" | "off", hintMs?: number): Promise<string>;
|
|
41
46
|
export declare function runPrintMode(runtime: Runtime, context: ModeContext): Promise<number>;
|
|
47
|
+
/**
|
|
48
|
+
* [W7-B2] 主回合结束后等后台任务与它们的通知回合(docs/agents-concurrency-plan.md §2.6、§6 Q5)。
|
|
49
|
+
* `settled()`(subagent-background.ts)等到没有运行中任务且通知投递链(含通知回合)结束;`stopped`:SIGINT /
|
|
50
|
+
* SIGTERM、stdout 关闭或预算到限时 resolve,立即停止等待。没有子 Agent 注册表时直接返回。
|
|
51
|
+
*/
|
|
52
|
+
export declare function waitBackgroundTasks(session: AgentSession, stopped: Promise<void>, io: Pick<CliIo, "stderr">): Promise<void>;
|
|
42
53
|
/** stderr 一行:计划已落盘待审批(-p 不替人批准)。 */
|
|
43
54
|
export declare function describePlanPending(plan: PlanProposedEvent): string;
|
|
44
55
|
/** stderr 一行:哪个预算到限。 */
|