@armadra/agent 0.6.2 → 0.6.4
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 +85 -0
- package/CHANGELOG.zh-CN.md +59 -0
- package/README.md +168 -575
- package/README.zh-CN.md +165 -596
- package/dist/agent/queue.d.ts +9 -0
- package/dist/agent/queue.js +27 -0
- package/dist/agent/session-subagent.d.ts +1 -0
- package/dist/agent/session-subagent.js +9 -0
- package/dist/agent/session.d.ts +11 -0
- package/dist/agent/session.js +29 -2
- package/dist/agent/subagent-background.d.ts +75 -0
- package/dist/agent/subagent-background.js +209 -0
- package/dist/agent/subagent-direct.d.ts +5 -2
- package/dist/agent/subagent-direct.js +34 -1
- package/dist/agent/subagent-registry.d.ts +26 -6
- package/dist/agent/subagent-registry.js +66 -94
- package/dist/agent/types-w5.d.ts +11 -1
- package/dist/agent/types.d.ts +12 -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 +16 -4
- package/dist/agents/task-record.js +32 -0
- package/dist/agents/types.d.ts +5 -1
- package/dist/ai/apis/chatgpt-rate-limits.js +7 -2
- package/dist/ai/providers/discovered-cache.d.ts +10 -4
- package/dist/ai/providers/discovered-cache.js +30 -14
- package/dist/ai/types.d.ts +2 -1
- package/dist/auth/chatgpt/backend-client.d.ts +6 -5
- package/dist/auth/chatgpt/backend-client.js +8 -7
- package/dist/bundle/ama.cjs +2034 -552
- package/dist/cli/compose-agents.d.ts +2 -1
- package/dist/cli/compose-agents.js +11 -2
- package/dist/cli/subcommands/models-discover.d.ts +1 -0
- package/dist/cli/subcommands/models-discover.js +12 -2
- package/dist/config/json-schema.js +10 -2
- package/dist/config/key-docs.js +9 -2
- 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.js +4 -0
- package/dist/config/settings-registry.js +5 -0
- package/dist/config/types-w5.d.ts +10 -0
- package/dist/config/types-w5.js +2 -0
- package/dist/config/types.d.ts +5 -1
- package/dist/drivers/runner.js +29 -2
- package/dist/git/info.d.ts +19 -0
- package/dist/git/info.js +64 -8
- package/dist/i18n/catalog.d.ts +58 -8
- package/dist/i18n/messages/agents.d.ts +60 -0
- package/dist/i18n/messages/agents.js +62 -2
- 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-view.d.ts +8 -0
- package/dist/i18n/messages/interactive-view.js +10 -0
- package/dist/i18n/messages/interactive.d.ts +33 -16
- package/dist/i18n/messages/interactive.js +31 -0
- package/dist/i18n/messages/print.d.ts +4 -0
- package/dist/i18n/messages/print.js +4 -0
- package/dist/i18n/messages/report.d.ts +8 -0
- package/dist/i18n/messages/report.js +12 -4
- package/dist/i18n/messages/settings.d.ts +8 -0
- package/dist/i18n/messages/settings.js +8 -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 +19 -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 +82 -12
- package/dist/modes/interactive/agent-view.d.ts +9 -0
- package/dist/modes/interactive/agent-view.js +28 -4
- 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 +46 -39
- package/dist/modes/interactive/interrupt-send.d.ts +25 -0
- package/dist/modes/interactive/interrupt-send.js +32 -0
- package/dist/modes/interactive/key-dispatch.d.ts +29 -5
- package/dist/modes/interactive/key-dispatch.js +97 -8
- package/dist/modes/interactive/line/line-mode.d.ts +1 -0
- package/dist/modes/interactive/line/line-mode.js +34 -7
- package/dist/modes/interactive/run-indicator.d.ts +34 -2
- package/dist/modes/interactive/run-indicator.js +62 -8
- 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 +13 -2
- package/dist/modes/interactive/status-bar.js +60 -17
- package/dist/modes/interactive/status-line.d.ts +11 -8
- package/dist/modes/interactive/status-line.js +52 -39
- package/dist/modes/interactive/status-quota.d.ts +58 -0
- package/dist/modes/interactive/status-quota.js +155 -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 +4 -1
- package/dist/modes/rpc/commands.js +24 -2
- package/dist/rpc.d.ts +20 -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/tools/types.d.ts +6 -0
- 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 +15 -3
- package/dist/tui/keybindings.js +15 -3
- package/docs/agents.md +52 -28
- package/docs/en/host-api.md +5 -1
- package/docs/en/providers.md +1 -1
- package/docs/en/rpc.md +28 -14
- package/docs/en/sessions.md +3 -1
- package/docs/en/tui.md +106 -70
- package/docs/host-api.md +5 -1
- package/docs/providers.md +4 -2
- package/docs/rpc.md +28 -14
- package/docs/session-format.md +1 -1
- package/docs/sessions.md +2 -1
- package/docs/tui-design.md +42 -29
- package/docs/tui.md +106 -70
- package/package.json +1 -1
|
@@ -21,6 +21,10 @@
|
|
|
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
|
*/
|
|
@@ -29,6 +33,7 @@ import { ExitCode } from "../../cli/exit-codes.js";
|
|
|
29
33
|
import { errorText, lastAssistant, onStdoutClosed, onTerminationSignals } from "../shared.js";
|
|
30
34
|
import { toJsonLine, toWireEvent } from "./json-event.js";
|
|
31
35
|
import { formatUsd } from "../../agent/limits.js";
|
|
36
|
+
import { registryOf } from "../../agent/subagent-registry.js";
|
|
32
37
|
import { promptImages, sessionModel } from "../image-input.js";
|
|
33
38
|
import { msg } from "../../i18n/index.js";
|
|
34
39
|
export function joinPrompt(argument, piped) {
|
|
@@ -118,9 +123,15 @@ export async function runPrintMode(runtime, context) {
|
|
|
118
123
|
const denied = [];
|
|
119
124
|
let limit;
|
|
120
125
|
let plan;
|
|
126
|
+
let stopWaiting = () => undefined;
|
|
127
|
+
const stopped = new Promise((resolve) => {
|
|
128
|
+
stopWaiting = resolve;
|
|
129
|
+
});
|
|
121
130
|
const unsubscribe = session.subscribe((event) => {
|
|
122
|
-
if (event.type === "limit_reached")
|
|
131
|
+
if (event.type === "limit_reached") {
|
|
123
132
|
limit ??= event;
|
|
133
|
+
stopWaiting();
|
|
134
|
+
}
|
|
124
135
|
else if (event.type === "plan_proposed")
|
|
125
136
|
plan = event;
|
|
126
137
|
else if (event.type === "plan_resolved" && event.planId === plan?.planId)
|
|
@@ -139,16 +150,25 @@ export async function runPrintMode(runtime, context) {
|
|
|
139
150
|
let signalled;
|
|
140
151
|
const offSignals = onTerminationSignals((code) => {
|
|
141
152
|
signalled ??= code;
|
|
153
|
+
stopWaiting();
|
|
142
154
|
void session.abort();
|
|
143
155
|
});
|
|
144
156
|
let stdoutClosed = false;
|
|
145
157
|
const offEpipe = onStdoutClosed(() => {
|
|
146
158
|
stdoutClosed = true;
|
|
159
|
+
stopWaiting();
|
|
147
160
|
void session.abort();
|
|
148
161
|
});
|
|
149
162
|
let failure;
|
|
150
163
|
try {
|
|
151
164
|
await session.prompt(prompt, images.length > 0 ? { images } : {});
|
|
165
|
+
const ended = lastAssistant(session)?.stopReason;
|
|
166
|
+
if (signalled === undefined &&
|
|
167
|
+
!stdoutClosed &&
|
|
168
|
+
limit === undefined &&
|
|
169
|
+
ended !== "error" &&
|
|
170
|
+
ended !== "aborted")
|
|
171
|
+
await waitBackgroundTasks(session, stopped, io);
|
|
152
172
|
}
|
|
153
173
|
catch (error) {
|
|
154
174
|
failure = errorText(error);
|
|
@@ -183,6 +203,7 @@ export async function runPrintMode(runtime, context) {
|
|
|
183
203
|
cost: stats.cost,
|
|
184
204
|
cacheHitRate: stats.cacheHitRate,
|
|
185
205
|
...(stats.cache !== undefined ? { cache: stats.cache } : {}),
|
|
206
|
+
...(stats.tasks !== undefined ? { tasks: stats.tasks } : {}),
|
|
186
207
|
...(denied.length > 0 ? { deniedTools: denied } : {}),
|
|
187
208
|
...(plan !== undefined
|
|
188
209
|
? {
|
|
@@ -230,6 +251,20 @@ export async function runPrintMode(runtime, context) {
|
|
|
230
251
|
return ExitCode.ToolDenied;
|
|
231
252
|
return ExitCode.Ok;
|
|
232
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* [W7-B2] 主回合结束后等后台任务与它们的通知回合(docs/agents-concurrency-plan.md §2.6、§6 Q5)。
|
|
256
|
+
* `settled()`(subagent-background.ts)等到没有运行中任务且通知投递链(含通知回合)结束;`stopped`:SIGINT /
|
|
257
|
+
* SIGTERM、stdout 关闭或预算到限时 resolve,立即停止等待。没有子 Agent 注册表时直接返回。
|
|
258
|
+
*/
|
|
259
|
+
export async function waitBackgroundTasks(session, stopped, io) {
|
|
260
|
+
const registry = registryOf(session.state.sessionId);
|
|
261
|
+
if (registry === undefined)
|
|
262
|
+
return;
|
|
263
|
+
const running = registry.list().filter((task) => task.status === "running").length;
|
|
264
|
+
if (running > 0)
|
|
265
|
+
io.stderr(msg().print.print.waitingTasks(running));
|
|
266
|
+
await Promise.race([registry.settled(), stopped]);
|
|
267
|
+
}
|
|
233
268
|
/** stderr 一行:计划已落盘待审批(-p 不替人批准)。 */
|
|
234
269
|
export function describePlanPending(plan) {
|
|
235
270
|
const where = plan.filePath ?? plan.planId;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* RPC 命令分派表(设计 §13.2、src/rpc.ts
|
|
2
|
+
* RPC 命令分派表(设计 §13.2、src/rpc.ts 的命令表,现 44 条)。[B6]
|
|
3
3
|
*
|
|
4
4
|
* - 提示类(prompt / steer / follow_up)不等运行结束:会话开始运行(`before_agent_start` /
|
|
5
5
|
* `agent_start`)、入队或被处理后立刻应答 `{ disposition }`;运行中的进展走事件。
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* - [W5-F] 计划:声明 `plans` 能力后计划审批交客户端(`plan_proposed` → `plan_response`),未声明按
|
|
12
12
|
* `plan.unattended`;`approve_fresh` 在这里新建会话并以计划全文开新回合。`get_tasks / get_agents`
|
|
13
13
|
* 读 `RpcContext.tasks / agents`(W5-G 的注册表与发现结果;未装配时回空表)。
|
|
14
|
+
* - [W7-B2] `background_task { taskId? }` → `{ backgrounded }`(`session.backgroundTask`,B1)。
|
|
15
|
+
* - 打断并发送:`prompt / steer` 的 `interrupt: true` → `session.prompt(message, { interrupt })`(中止当前回合、
|
|
16
|
+
* 立刻以「排队的 steer + 本条」开新回合),应答 `started`。
|
|
14
17
|
*/
|
|
15
18
|
import type { AgentInfo } from "../../agents/types.js";
|
|
16
19
|
import type { TaskRegistryView } from "../../tools/types.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* RPC 命令分派表(设计 §13.2、src/rpc.ts
|
|
2
|
+
* RPC 命令分派表(设计 §13.2、src/rpc.ts 的命令表,现 44 条)。[B6]
|
|
3
3
|
*
|
|
4
4
|
* - 提示类(prompt / steer / follow_up)不等运行结束:会话开始运行(`before_agent_start` /
|
|
5
5
|
* `agent_start`)、入队或被处理后立刻应答 `{ disposition }`;运行中的进展走事件。
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* - [W5-F] 计划:声明 `plans` 能力后计划审批交客户端(`plan_proposed` → `plan_response`),未声明按
|
|
12
12
|
* `plan.unattended`;`approve_fresh` 在这里新建会话并以计划全文开新回合。`get_tasks / get_agents`
|
|
13
13
|
* 读 `RpcContext.tasks / agents`(W5-G 的注册表与发现结果;未装配时回空表)。
|
|
14
|
+
* - [W7-B2] `background_task { taskId? }` → `{ backgrounded }`(`session.backgroundTask`,B1)。
|
|
15
|
+
* - 打断并发送:`prompt / steer` 的 `interrupt: true` → `session.prompt(message, { interrupt })`(中止当前回合、
|
|
16
|
+
* 立刻以「排队的 steer + 本条」开新回合),应答 `started`。
|
|
14
17
|
*/
|
|
15
18
|
import { formatModelRef } from "../../ai/providers/channels.js";
|
|
16
19
|
import { AgentSessionImpl } from "../../agent/session.js";
|
|
@@ -81,6 +84,12 @@ function applyPlanAttendance(ctx) {
|
|
|
81
84
|
planController(ctx.session())?.setAttendance(ctx.capabilities.has("plans") ? "client" : "unattended");
|
|
82
85
|
}
|
|
83
86
|
/** 开始一次运行:开始 / 入队 / 被处理后即返回,不等运行结束。 */
|
|
87
|
+
/** `prompt / steer` 的 `interrupt`:可选布尔。 */
|
|
88
|
+
function interruptFlag(value) {
|
|
89
|
+
if (value !== undefined && typeof value !== "boolean")
|
|
90
|
+
throw new AmaError("invalid_arguments", "interrupt must be a boolean");
|
|
91
|
+
return value === true;
|
|
92
|
+
}
|
|
84
93
|
async function startRun(ctx, run) {
|
|
85
94
|
const session = ctx.session();
|
|
86
95
|
let settled = false;
|
|
@@ -154,8 +163,14 @@ export const handlers = {
|
|
|
154
163
|
prompt: (p, ctx) => startRun(ctx, () => ctx.session().prompt(p.message, {
|
|
155
164
|
...(p.images !== undefined ? { images: p.images } : {}),
|
|
156
165
|
...(p.streamingBehavior !== undefined ? { streamingBehavior: p.streamingBehavior } : {}),
|
|
166
|
+
...(interruptFlag(p.interrupt) ? { interrupt: true } : {}),
|
|
157
167
|
})),
|
|
158
|
-
steer: (p, ctx) => startRun(ctx, () =>
|
|
168
|
+
steer: (p, ctx) => startRun(ctx, () => interruptFlag(p.interrupt)
|
|
169
|
+
? ctx.session().prompt(p.message, {
|
|
170
|
+
interrupt: true,
|
|
171
|
+
...(p.images !== undefined ? { images: p.images } : {}),
|
|
172
|
+
})
|
|
173
|
+
: ctx.session().steer(p.message)),
|
|
159
174
|
follow_up: (p, ctx) => startRun(ctx, () => ctx.session().followUp(p.message)),
|
|
160
175
|
abort: async (_p, ctx) => {
|
|
161
176
|
await ctx.session().abort();
|
|
@@ -312,5 +327,12 @@ export const handlers = {
|
|
|
312
327
|
get_agents: async (_p, ctx) => ({ agents: [...(ctx.agents?.() ?? [])] }),
|
|
313
328
|
// [W6-T2] 轨迹(docs/wave6-plan.md §2.6;分页 / 增量 / 脱敏见 trace/query.ts)
|
|
314
329
|
get_trace: async (p, ctx) => sessionTrace(ctx.session(), p ?? {}),
|
|
330
|
+
// [W7-B2] 前台任务转后台:工具调用立即返回、任务继续,完成后照常通知
|
|
331
|
+
background_task: async (p, ctx) => {
|
|
332
|
+
const taskId = p?.taskId;
|
|
333
|
+
if (taskId !== undefined && typeof taskId !== "string")
|
|
334
|
+
throw new AmaError("invalid_arguments", msg().print.rpc.badTaskId);
|
|
335
|
+
return { backgrounded: ctx.session().backgroundTask(taskId) };
|
|
336
|
+
},
|
|
315
337
|
};
|
|
316
338
|
export const RPC_COMMAND_TYPES = Object.keys(handlers);
|
package/dist/rpc.d.ts
CHANGED
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
* - [W6-C0] 第六波(docs/wave6-plan.md §2.6、§4.4):命令 `get_trace`(参数 `RpcGetTraceParams`、返回
|
|
20
20
|
* `RpcW6Results`,W6-T2 实现,之前回 `not_implemented`);事件 `quota_update`;`permission_request.context`
|
|
21
21
|
* 可带 `toolCallId`;`keySource` 可为 `oauth`。`RPC_PROTOCOL_VERSION` 不变。
|
|
22
|
+
* - [W7-B2] 后台子 Agent(docs/agents-concurrency-plan.md §2.5):命令 `background_task { taskId? }` →
|
|
23
|
+
* `RpcW7Results`(无 taskId = 全部前台运行中任务;对已结束 / 已在后台的任务回空表);事件
|
|
24
|
+
* `subagent_background` 由 `SessionEvent` 派生。`RPC_PROTOCOL_VERSION` 不变。
|
|
22
25
|
*/
|
|
23
26
|
import type { AssistantEvent, ImageBlock, ModelThinkingLevel, Usage } from "./ai/types.js";
|
|
24
27
|
import type { PlanData, PlanDecisionKind, QueueMode, SessionEvent, TodoItemView } from "./agent/types.js";
|
|
@@ -42,14 +45,21 @@ export interface RpcHello {
|
|
|
42
45
|
/** 无参数命令。 */
|
|
43
46
|
export type NoParams = Record<never, never>;
|
|
44
47
|
export interface RpcCommandMap {
|
|
48
|
+
/**
|
|
49
|
+
* `interrupt: true`:运行中中止当前回合(工具按 abort 收尾),立刻以「排队的 steer + 本条」开新回合
|
|
50
|
+
* (user 消息 origin `interrupt`),应答 `started`;空闲时等同不填。优先于 `streamingBehavior`。
|
|
51
|
+
*/
|
|
45
52
|
prompt: {
|
|
46
53
|
message: string;
|
|
47
54
|
images?: ImageBlock[];
|
|
48
55
|
streamingBehavior?: "steer" | "followUp";
|
|
56
|
+
interrupt?: boolean;
|
|
49
57
|
};
|
|
58
|
+
/** `interrupt: true` 同 `prompt`。 */
|
|
50
59
|
steer: {
|
|
51
60
|
message: string;
|
|
52
61
|
images?: ImageBlock[];
|
|
62
|
+
interrupt?: boolean;
|
|
53
63
|
};
|
|
54
64
|
follow_up: {
|
|
55
65
|
message: string;
|
|
@@ -139,6 +149,16 @@ export interface RpcCommandMap {
|
|
|
139
149
|
get_tasks: NoParams;
|
|
140
150
|
get_agents: NoParams;
|
|
141
151
|
get_trace: RpcGetTraceParams;
|
|
152
|
+
background_task: {
|
|
153
|
+
taskId?: string;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/** [W7-B2] 第七波命令成功时的 `data` 形状。 */
|
|
157
|
+
export interface RpcW7Results {
|
|
158
|
+
/** 实际转了后台的 taskId(含被打断的 `task_ctl wait`);没有可转的为空表。 */
|
|
159
|
+
background_task: {
|
|
160
|
+
backgrounded: string[];
|
|
161
|
+
};
|
|
142
162
|
}
|
|
143
163
|
/** [W6-C0] `get_trace` 的参数。 */
|
|
144
164
|
export interface RpcGetTraceParams {
|
package/dist/rpc.js
CHANGED
|
@@ -19,5 +19,8 @@
|
|
|
19
19
|
* - [W6-C0] 第六波(docs/wave6-plan.md §2.6、§4.4):命令 `get_trace`(参数 `RpcGetTraceParams`、返回
|
|
20
20
|
* `RpcW6Results`,W6-T2 实现,之前回 `not_implemented`);事件 `quota_update`;`permission_request.context`
|
|
21
21
|
* 可带 `toolCallId`;`keySource` 可为 `oauth`。`RPC_PROTOCOL_VERSION` 不变。
|
|
22
|
+
* - [W7-B2] 后台子 Agent(docs/agents-concurrency-plan.md §2.5):命令 `background_task { taskId? }` →
|
|
23
|
+
* `RpcW7Results`(无 taskId = 全部前台运行中任务;对已结束 / 已在后台的任务回空表);事件
|
|
24
|
+
* `subagent_background` 由 `SessionEvent` 派生。`RPC_PROTOCOL_VERSION` 不变。
|
|
22
25
|
*/
|
|
23
26
|
export const RPC_PROTOCOL_VERSION = 1;
|
package/dist/tools/task-ctl.d.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* 与父一致,运行时拒绝;
|
|
8
8
|
* - `wait` / `output` 是轮询类调用(`annotations.pollable`,重复调用检测豁免);
|
|
9
9
|
* - `send` = 续聊放后台:等价于 `task{taskId, prompt, background:true}`,完成后同样收到通知。
|
|
10
|
+
* - [W7-B1] `wait` 被转后台(`background()`,TUI `Ctrl+B` / RPC `background_task`)打断时立即返回固定
|
|
11
|
+
* 文案,要求模型不要再等。
|
|
10
12
|
*/
|
|
11
13
|
import type { ToolDefinition } from "./types.js";
|
|
12
14
|
export declare const TASK_CTL_TOOL = "task_ctl";
|
package/dist/tools/task-ctl.js
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* 与父一致,运行时拒绝;
|
|
8
8
|
* - `wait` / `output` 是轮询类调用(`annotations.pollable`,重复调用检测豁免);
|
|
9
9
|
* - `send` = 续聊放后台:等价于 `task{taskId, prompt, background:true}`,完成后同样收到通知。
|
|
10
|
+
* - [W7-B1] `wait` 被转后台(`background()`,TUI `Ctrl+B` / RPC `background_task`)打断时立即返回固定
|
|
11
|
+
* 文案,要求模型不要再等。
|
|
10
12
|
*/
|
|
11
|
-
import { capTaskText, formatTokens } from "../agents/result.js";
|
|
13
|
+
import { capTaskText, formatTokens, waitDetachedText } from "../agents/result.js";
|
|
12
14
|
import { taskControl } from "../agents/task-control.js";
|
|
13
15
|
export const TASK_CTL_TOOL = "task_ctl";
|
|
14
16
|
export const DEFAULT_WAIT_MS = 30_000;
|
|
@@ -96,7 +98,10 @@ export function createTaskCtlTool() {
|
|
|
96
98
|
}
|
|
97
99
|
case "wait": {
|
|
98
100
|
const timeout = Math.min(Math.max(input.timeoutMs ?? DEFAULT_WAIT_MS, 0), MAX_WAIT_MS);
|
|
99
|
-
const
|
|
101
|
+
const signal = control.detachSignal(taskId);
|
|
102
|
+
const result = await control.wait(taskId, timeout, { signal });
|
|
103
|
+
if (result === undefined && signal.aborted)
|
|
104
|
+
return { content: waitDetachedText(taskId) };
|
|
100
105
|
if (result === undefined) {
|
|
101
106
|
const turns = control.get(taskId)?.turns ?? 0;
|
|
102
107
|
return {
|
package/dist/tools/task.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* - `agent`:子 Agent 类型(内置 general / explore / plan、`.ama/agents/*.md`)或外部 Agent;缺省
|
|
6
6
|
* general。描述里列出可用类型(catalog 由 compose-agents.ts 经 `bindTaskAgents` 绑定;没绑定时
|
|
7
7
|
* 只列内置类型)。
|
|
8
|
+
* - [W7-B1] 缺省前台 / 后台由 `subagents.background` 决定(交互 / RPC / ACP 后台,`-p` 前台),描述随之
|
|
9
|
+
* 二选一(`bindTaskBackground`);前台任务可被 `background()` 转后台,工具立即以固定文案返回。
|
|
8
10
|
* - `background`:立即返回 taskId,完成后父会话收到 `<task-notification>`;`taskId`:向已有子会话
|
|
9
11
|
* 追加消息(续聊,忽略 agent / tools / model);`isolation: "worktree"`:在独立 git worktree 里跑。
|
|
10
12
|
* - **parallel**:同一回复里的多个 task 真并行(会话注册表的池限流,缺省 4);与 sequential 工具
|
|
@@ -33,9 +35,18 @@ export interface TaskInput {
|
|
|
33
35
|
/** 预留(并发上限在会话的任务注册表,见 agent/subagent-registry.ts)。 */
|
|
34
36
|
export interface TaskToolOptions {
|
|
35
37
|
}
|
|
38
|
+
/** 缺省前台(`-p`、`subagents.background: never`)时的描述。 */
|
|
39
|
+
export declare const FOREGROUND_DESCRIPTION: string;
|
|
40
|
+
/** [W7-B1] 缺省后台(交互 / RPC / ACP,docs/agents-concurrency-plan.md §2.6)时的描述。 */
|
|
41
|
+
export declare const BACKGROUND_DESCRIPTION: string;
|
|
36
42
|
/** 描述里类型清单的标题(清单本身另有 400 token 预算,见 agents/catalog.ts)。 */
|
|
37
43
|
export declare const TASK_AGENTS_HEADING = "\nAgents:\n";
|
|
38
44
|
/** 组装根把会话的类型目录绑到 task 工具上(描述里的类型清单)。 */
|
|
39
45
|
export declare function bindTaskAgents(tool: ToolDefinition | undefined, catalog: AgentCatalog): void;
|
|
46
|
+
/**
|
|
47
|
+
* [W7-B1] 组装根把本进程的缺省前台 / 后台(`subagents.background` 按运行模式解析后)绑到 task 工具:
|
|
48
|
+
* 决定描述用哪一版。会话内不变(缓存前缀稳定);没绑定按缺省前台。
|
|
49
|
+
*/
|
|
50
|
+
export declare function bindTaskBackground(tool: ToolDefinition | undefined, background: boolean): void;
|
|
40
51
|
export declare function buildSubagentRequest(input: TaskInput, ctx: ToolContext): SubagentRequest | string;
|
|
41
52
|
export declare function createTaskTool(_options?: TaskToolOptions): ToolDefinition<TaskInput>;
|
package/dist/tools/task.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* - `agent`:子 Agent 类型(内置 general / explore / plan、`.ama/agents/*.md`)或外部 Agent;缺省
|
|
6
6
|
* general。描述里列出可用类型(catalog 由 compose-agents.ts 经 `bindTaskAgents` 绑定;没绑定时
|
|
7
7
|
* 只列内置类型)。
|
|
8
|
+
* - [W7-B1] 缺省前台 / 后台由 `subagents.background` 决定(交互 / RPC / ACP 后台,`-p` 前台),描述随之
|
|
9
|
+
* 二选一(`bindTaskBackground`);前台任务可被 `background()` 转后台,工具立即以固定文案返回。
|
|
8
10
|
* - `background`:立即返回 taskId,完成后父会话收到 `<task-notification>`;`taskId`:向已有子会话
|
|
9
11
|
* 追加消息(续聊,忽略 agent / tools / model);`isolation: "worktree"`:在独立 git worktree 里跑。
|
|
10
12
|
* - **parallel**:同一回复里的多个 task 真并行(会话注册表的池限流,缺省 4);与 sequential 工具
|
|
@@ -24,17 +26,32 @@ const THINKING = [
|
|
|
24
26
|
"high",
|
|
25
27
|
"xhigh",
|
|
26
28
|
];
|
|
27
|
-
|
|
29
|
+
/** 缺省前台(`-p`、`subagents.background: never`)时的描述。 */
|
|
30
|
+
export const FOREGROUND_DESCRIPTION = "Delegate to a sub-agent (fresh context; give full instructions). Returns its final report. " +
|
|
28
31
|
"Tasks in one reply run in parallel; writers should use isolation worktree. background: " +
|
|
29
32
|
"returns a taskId now (see task_ctl); taskId: continue that task.";
|
|
33
|
+
/** [W7-B1] 缺省后台(交互 / RPC / ACP,docs/agents-concurrency-plan.md §2.6)时的描述。 */
|
|
34
|
+
export const BACKGROUND_DESCRIPTION = "Delegate to a sub-agent (fresh context; give full instructions). Runs in the background by " +
|
|
35
|
+
"default: returns a taskId; a <task-notification> follows when done, so keep working. " +
|
|
36
|
+
"background:false if your next step needs the result. taskId: continue a task. Tasks in one " +
|
|
37
|
+
"reply run in parallel; writers use isolation worktree.";
|
|
30
38
|
/** 描述里类型清单的标题(清单本身另有 400 token 预算,见 agents/catalog.ts)。 */
|
|
31
39
|
export const TASK_AGENTS_HEADING = "\nAgents:\n";
|
|
32
40
|
const catalogs = new WeakMap();
|
|
41
|
+
const backgrounds = new WeakMap();
|
|
33
42
|
/** 组装根把会话的类型目录绑到 task 工具上(描述里的类型清单)。 */
|
|
34
43
|
export function bindTaskAgents(tool, catalog) {
|
|
35
44
|
if (tool !== undefined)
|
|
36
45
|
catalogs.set(tool, catalog);
|
|
37
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* [W7-B1] 组装根把本进程的缺省前台 / 后台(`subagents.background` 按运行模式解析后)绑到 task 工具:
|
|
49
|
+
* 决定描述用哪一版。会话内不变(缓存前缀稳定);没绑定按缺省前台。
|
|
50
|
+
*/
|
|
51
|
+
export function bindTaskBackground(tool, background) {
|
|
52
|
+
if (tool !== undefined)
|
|
53
|
+
backgrounds.set(tool, background);
|
|
54
|
+
}
|
|
38
55
|
function fail(message) {
|
|
39
56
|
return { content: message, isError: true };
|
|
40
57
|
}
|
|
@@ -87,7 +104,8 @@ export function createTaskTool(_options = {}) {
|
|
|
87
104
|
label: "Task",
|
|
88
105
|
get description() {
|
|
89
106
|
const catalog = catalogs.get(tool) ?? new AgentCatalog();
|
|
90
|
-
|
|
107
|
+
const base = backgrounds.get(tool) === true ? BACKGROUND_DESCRIPTION : FOREGROUND_DESCRIPTION;
|
|
108
|
+
return `${base}${TASK_AGENTS_HEADING}${catalog.describe()}`;
|
|
91
109
|
},
|
|
92
110
|
parameters: {
|
|
93
111
|
type: "object",
|
package/dist/tools/types.d.ts
CHANGED
|
@@ -181,6 +181,12 @@ export interface RunnerHandle {
|
|
|
181
181
|
send(text: string): Promise<void>;
|
|
182
182
|
wait(): Promise<SubagentResult>;
|
|
183
183
|
stop(): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* 打断当前回合并立即以 `text` 开下一回合(子 Agent 视图的「打断并发送」);`wait()` 跟到这一回合结束。
|
|
186
|
+
* 驱动能中断回合(ACP `session/cancel`、Claude stream-json interrupt、Codex `turn/interrupt`)且正在
|
|
187
|
+
* 运行时返回 true;否则 false(调用方退回排队)。
|
|
188
|
+
*/
|
|
189
|
+
interrupt?(text: string): Promise<boolean>;
|
|
184
190
|
}
|
|
185
191
|
/** `ama`(AmaRunner)| `claude` | `codex` | `acp:<program>`(ProcessRunner,W5-E)| 宿主 id。 */
|
|
186
192
|
export interface SubagentRunner {
|
|
@@ -88,6 +88,8 @@ export declare class Editor implements Component, Focusable {
|
|
|
88
88
|
col: number;
|
|
89
89
|
};
|
|
90
90
|
get isCompletionOpen(): boolean;
|
|
91
|
+
/** 正在用 ↑↓ 浏览输入历史(编辑后即退出)。 */
|
|
92
|
+
get isBrowsingHistory(): boolean;
|
|
91
93
|
getHistory(): readonly string[];
|
|
92
94
|
setHistory(entries: readonly string[]): void;
|
|
93
95
|
addToHistory(text: string): void;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* 运行指示(设计 §12.1、§12.6;终端界面视觉设计 v1 §3.7)。[B4]
|
|
3
3
|
*
|
|
4
4
|
* 一行 `⠋ 动词 · 已用时 · 附加项…`:spinner `accent`、动词正文色、其余 `dim`,以 ` · ` 连接。
|
|
5
|
-
* - `setVerb(verb, extras, { elapsed })` 换动词(思考中 / 回复中 / 运行 bash / 等待确认……);
|
|
5
|
+
* - `setVerb(verb, extras, { elapsed, optional })` 换动词(思考中 / 回复中 / 运行 bash / 等待确认……);
|
|
6
|
+
* `optional` 是排在附加项之后的可丢弃项(按键提示),整行放不下时从后往前丢,而不是被截断;
|
|
6
7
|
* `setMessage(text)` 是只换动词的旧接口。
|
|
7
8
|
* - 帧取 `theme.glyphs.spinner`(Unicode 10 帧 80 ms、ASCII 4 帧 250 ms);`frame` 暴露当前帧字形,
|
|
8
9
|
* `onFrame` 让别的组件(运行中的工具摘要行)与 Loader 同帧换字,保证一帧只多改一行。
|
|
@@ -27,6 +28,8 @@ export interface LoaderOptions {
|
|
|
27
28
|
export interface LoaderVerbOptions {
|
|
28
29
|
/** 本动词是否显示已用时(缺省跟随 `showElapsed`)。 */
|
|
29
30
|
elapsed?: boolean;
|
|
31
|
+
/** 可丢弃的附加项:宽度不够时从后往前整项丢掉(缺省没有)。 */
|
|
32
|
+
optional?: readonly string[];
|
|
30
33
|
}
|
|
31
34
|
export declare function formatElapsed(ms: number): string;
|
|
32
35
|
export declare class Loader implements Component {
|
|
@@ -37,6 +40,7 @@ export declare class Loader implements Component {
|
|
|
37
40
|
private startedAt;
|
|
38
41
|
private verb;
|
|
39
42
|
private extras;
|
|
43
|
+
private optional;
|
|
40
44
|
private verbElapsed;
|
|
41
45
|
private lastElapsed;
|
|
42
46
|
private readonly listeners;
|
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
* 运行指示(设计 §12.1、§12.6;终端界面视觉设计 v1 §3.7)。[B4]
|
|
3
3
|
*
|
|
4
4
|
* 一行 `⠋ 动词 · 已用时 · 附加项…`:spinner `accent`、动词正文色、其余 `dim`,以 ` · ` 连接。
|
|
5
|
-
* - `setVerb(verb, extras, { elapsed })` 换动词(思考中 / 回复中 / 运行 bash / 等待确认……);
|
|
5
|
+
* - `setVerb(verb, extras, { elapsed, optional })` 换动词(思考中 / 回复中 / 运行 bash / 等待确认……);
|
|
6
|
+
* `optional` 是排在附加项之后的可丢弃项(按键提示),整行放不下时从后往前丢,而不是被截断;
|
|
6
7
|
* `setMessage(text)` 是只换动词的旧接口。
|
|
7
8
|
* - 帧取 `theme.glyphs.spinner`(Unicode 10 帧 80 ms、ASCII 4 帧 250 ms);`frame` 暴露当前帧字形,
|
|
8
9
|
* `onFrame` 让别的组件(运行中的工具摘要行)与 Loader 同帧换字,保证一帧只多改一行。
|
|
9
10
|
* - `animation: false`:spinner 固定为 `glyphs.spinnerStatic`,定时器每秒一次,已用时变了才请求重绘。
|
|
10
11
|
* - `stop()` 后不再触发渲染。时钟可注入便于测试。
|
|
11
12
|
*/
|
|
12
|
-
import { truncateToWidth } from "../ansi.js";
|
|
13
|
+
import { truncateToWidth, visibleWidth } from "../ansi.js";
|
|
13
14
|
import { UNICODE_GLYPHS } from "../glyphs.js";
|
|
14
15
|
/** Unicode 帧表(兼容旧导出;实际帧取 `theme.glyphs.spinner`)。 */
|
|
15
16
|
export const LOADER_FRAMES = UNICODE_GLYPHS.spinner;
|
|
17
|
+
function sameList(a, b) {
|
|
18
|
+
return a.length === b.length && a.every((e, i) => e === b[i]);
|
|
19
|
+
}
|
|
16
20
|
export function formatElapsed(ms) {
|
|
17
21
|
const total = Math.max(0, Math.floor(ms / 1000));
|
|
18
22
|
if (total < 60)
|
|
@@ -29,6 +33,7 @@ export class Loader {
|
|
|
29
33
|
startedAt;
|
|
30
34
|
verb;
|
|
31
35
|
extras = [];
|
|
36
|
+
optional = [];
|
|
32
37
|
verbElapsed;
|
|
33
38
|
lastElapsed = "";
|
|
34
39
|
listeners = new Set();
|
|
@@ -65,12 +70,13 @@ export class Loader {
|
|
|
65
70
|
setVerb(verb, extras = [], options = {}) {
|
|
66
71
|
const same = verb === this.verb &&
|
|
67
72
|
options.elapsed === this.verbElapsed &&
|
|
68
|
-
extras
|
|
69
|
-
|
|
73
|
+
sameList(extras, this.extras) &&
|
|
74
|
+
sameList(options.optional ?? [], this.optional);
|
|
70
75
|
if (same)
|
|
71
76
|
return;
|
|
72
77
|
this.verb = verb;
|
|
73
78
|
this.extras = [...extras];
|
|
79
|
+
this.optional = [...(options.optional ?? [])];
|
|
74
80
|
this.verbElapsed = options.elapsed;
|
|
75
81
|
this.requestRender();
|
|
76
82
|
}
|
|
@@ -142,7 +148,14 @@ export class Loader {
|
|
|
142
148
|
for (const extra of this.extras)
|
|
143
149
|
parts.push(dim(extra));
|
|
144
150
|
const spinner = theme ? theme.fg("accent", this.frame) : this.frame;
|
|
145
|
-
|
|
151
|
+
const line = (items) => `${spinner} ${items.join(dim(" · "))}`;
|
|
152
|
+
for (const extra of this.optional) {
|
|
153
|
+
const next = [...parts, dim(extra)];
|
|
154
|
+
if (visibleWidth(line(next)) > width)
|
|
155
|
+
break;
|
|
156
|
+
parts.push(dim(extra));
|
|
157
|
+
}
|
|
158
|
+
return [truncateToWidth(line(parts), width)];
|
|
146
159
|
}
|
|
147
160
|
invalidate() { }
|
|
148
161
|
}
|
|
@@ -36,6 +36,13 @@ export declare const DEFAULT_KEYBINDINGS: {
|
|
|
36
36
|
readonly "app.exit": readonly ["ctrl+d"];
|
|
37
37
|
readonly "app.message.followUp": readonly ["alt+enter"];
|
|
38
38
|
readonly "app.message.dequeue": readonly ["alt+up"];
|
|
39
|
+
/**
|
|
40
|
+
* 运行中打断当前回合并立即发送输入框的文字(连同排队的插话);`ui.enterWhileRunning: "interrupt"` 时与
|
|
41
|
+
* Enter 互换(它变成排队)。缺省 `Ctrl+X`:各终端(macOS Terminal、iTerm2、tmux、Windows Terminal)都原样
|
|
42
|
+
* 送达 0x18,且没有别的绑定;`Ctrl+Enter` 不开 kitty 键盘协议时与 Enter 无法区分,`Ctrl+S` 可能被 XOFF
|
|
43
|
+
* 流控吃掉,`Alt+Enter` 已是 followUp,`Alt+<字母>` 在 macOS 缺省不送 Meta,`Ctrl+]` 在非美式键盘难按。
|
|
44
|
+
*/
|
|
45
|
+
readonly "app.message.interrupt": readonly ["ctrl+x"];
|
|
39
46
|
readonly "app.permission.cycle": readonly ["shift+tab", "tab"];
|
|
40
47
|
readonly "app.tools.expand": readonly ["ctrl+o"];
|
|
41
48
|
readonly "app.model.select": readonly ["ctrl+l"];
|
|
@@ -45,10 +52,15 @@ export declare const DEFAULT_KEYBINDINGS: {
|
|
|
45
52
|
/** [W5-U] 粘贴剪贴板图片(写进数据目录,输入框插入 `@路径`)。 */
|
|
46
53
|
readonly "app.paste.image": readonly ["ctrl+v"];
|
|
47
54
|
/**
|
|
48
|
-
* [W6-A] 进入 Agent
|
|
49
|
-
*
|
|
55
|
+
* [W6-A / W7-A] 进入 Agent 栏:输入为空、补全未开、有子 Agent 任务即可(不要求栏可见);有字时 `↓` 仍是
|
|
56
|
+
* 下移 / 历史,并给一次提示。`Ctrl+B` 不再进栏(tmux 缺省前缀会吃掉它),留给 `app.tasks.background`。
|
|
57
|
+
*/
|
|
58
|
+
readonly "app.agents.focus": readonly ["down"];
|
|
59
|
+
/**
|
|
60
|
+
* [W7-C] 有阻塞中的前台子 Agent 任务(或 `task_ctl wait`)时全部转后台,不看输入框;没有时落回编辑器
|
|
61
|
+
* (`tui.editor.cursorLeft` 的 `ctrl+b`)。tmux 里按 `C-b C-b` 透传。
|
|
50
62
|
*/
|
|
51
|
-
readonly "app.
|
|
63
|
+
readonly "app.tasks.background": readonly ["ctrl+b"];
|
|
52
64
|
};
|
|
53
65
|
export type ActionId = keyof typeof DEFAULT_KEYBINDINGS;
|
|
54
66
|
export type KeybindingOverrides = Partial<Record<ActionId, readonly string[]>>;
|
package/dist/tui/keybindings.js
CHANGED
|
@@ -39,6 +39,13 @@ export const DEFAULT_KEYBINDINGS = {
|
|
|
39
39
|
"app.exit": ["ctrl+d"],
|
|
40
40
|
"app.message.followUp": ["alt+enter"],
|
|
41
41
|
"app.message.dequeue": ["alt+up"],
|
|
42
|
+
/**
|
|
43
|
+
* 运行中打断当前回合并立即发送输入框的文字(连同排队的插话);`ui.enterWhileRunning: "interrupt"` 时与
|
|
44
|
+
* Enter 互换(它变成排队)。缺省 `Ctrl+X`:各终端(macOS Terminal、iTerm2、tmux、Windows Terminal)都原样
|
|
45
|
+
* 送达 0x18,且没有别的绑定;`Ctrl+Enter` 不开 kitty 键盘协议时与 Enter 无法区分,`Ctrl+S` 可能被 XOFF
|
|
46
|
+
* 流控吃掉,`Alt+Enter` 已是 followUp,`Alt+<字母>` 在 macOS 缺省不送 Meta,`Ctrl+]` 在非美式键盘难按。
|
|
47
|
+
*/
|
|
48
|
+
"app.message.interrupt": ["ctrl+x"],
|
|
42
49
|
"app.permission.cycle": ["shift+tab", "tab"],
|
|
43
50
|
"app.tools.expand": ["ctrl+o"],
|
|
44
51
|
"app.model.select": ["ctrl+l"],
|
|
@@ -48,10 +55,15 @@ export const DEFAULT_KEYBINDINGS = {
|
|
|
48
55
|
/** [W5-U] 粘贴剪贴板图片(写进数据目录,输入框插入 `@路径`)。 */
|
|
49
56
|
"app.paste.image": ["ctrl+v"],
|
|
50
57
|
/**
|
|
51
|
-
* [W6-A] 进入 Agent
|
|
52
|
-
*
|
|
58
|
+
* [W6-A / W7-A] 进入 Agent 栏:输入为空、补全未开、有子 Agent 任务即可(不要求栏可见);有字时 `↓` 仍是
|
|
59
|
+
* 下移 / 历史,并给一次提示。`Ctrl+B` 不再进栏(tmux 缺省前缀会吃掉它),留给 `app.tasks.background`。
|
|
60
|
+
*/
|
|
61
|
+
"app.agents.focus": ["down"],
|
|
62
|
+
/**
|
|
63
|
+
* [W7-C] 有阻塞中的前台子 Agent 任务(或 `task_ctl wait`)时全部转后台,不看输入框;没有时落回编辑器
|
|
64
|
+
* (`tui.editor.cursorLeft` 的 `ctrl+b`)。tmux 里按 `C-b C-b` 透传。
|
|
53
65
|
*/
|
|
54
|
-
"app.
|
|
66
|
+
"app.tasks.background": ["ctrl+b"],
|
|
55
67
|
};
|
|
56
68
|
export function isActionId(id) {
|
|
57
69
|
return Object.prototype.hasOwnProperty.call(DEFAULT_KEYBINDINGS, id);
|