@armadra/agent 0.6.3 → 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.
Files changed (69) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/CHANGELOG.zh-CN.md +15 -0
  3. package/dist/agent/queue.d.ts +9 -0
  4. package/dist/agent/queue.js +27 -0
  5. package/dist/agent/session-subagent.d.ts +1 -0
  6. package/dist/agent/session-subagent.js +9 -0
  7. package/dist/agent/session.d.ts +5 -0
  8. package/dist/agent/session.js +21 -2
  9. package/dist/agent/subagent-direct.d.ts +5 -2
  10. package/dist/agent/subagent-direct.js +34 -1
  11. package/dist/agent/subagent-registry.d.ts +2 -1
  12. package/dist/agent/subagent-registry.js +2 -2
  13. package/dist/agent/types.d.ts +7 -0
  14. package/dist/agents/task-record.d.ts +3 -3
  15. package/dist/ai/apis/chatgpt-rate-limits.js +7 -2
  16. package/dist/ai/types.d.ts +2 -1
  17. package/dist/bundle/ama.cjs +391 -100
  18. package/dist/config/json-schema.js +1 -0
  19. package/dist/config/key-docs.js +8 -1
  20. package/dist/config/schema.js +2 -0
  21. package/dist/config/settings-registry.js +1 -0
  22. package/dist/config/types.d.ts +2 -0
  23. package/dist/drivers/runner.js +29 -2
  24. package/dist/i18n/catalog.d.ts +13 -0
  25. package/dist/i18n/messages/agents.d.ts +4 -0
  26. package/dist/i18n/messages/agents.js +4 -0
  27. package/dist/i18n/messages/config-keys.d.ts +2 -0
  28. package/dist/i18n/messages/config-keys.js +2 -0
  29. package/dist/i18n/messages/config.d.ts +2 -0
  30. package/dist/i18n/messages/interactive-view.d.ts +8 -0
  31. package/dist/i18n/messages/interactive-view.js +10 -0
  32. package/dist/i18n/messages/interactive.d.ts +10 -0
  33. package/dist/i18n/messages/interactive.js +6 -0
  34. package/dist/i18n/messages/report.d.ts +8 -0
  35. package/dist/i18n/messages/report.js +8 -0
  36. package/dist/i18n/messages/settings.d.ts +2 -0
  37. package/dist/i18n/messages/settings.js +2 -0
  38. package/dist/modes/commands-core.js +11 -0
  39. package/dist/modes/interactive/agent-ui.js +1 -0
  40. package/dist/modes/interactive/agent-view.d.ts +5 -0
  41. package/dist/modes/interactive/agent-view.js +14 -3
  42. package/dist/modes/interactive/interactive-mode.js +13 -1
  43. package/dist/modes/interactive/interrupt-send.d.ts +25 -0
  44. package/dist/modes/interactive/interrupt-send.js +32 -0
  45. package/dist/modes/interactive/key-dispatch.d.ts +8 -1
  46. package/dist/modes/interactive/key-dispatch.js +37 -0
  47. package/dist/modes/interactive/line/line-mode.d.ts +1 -0
  48. package/dist/modes/interactive/line/line-mode.js +29 -7
  49. package/dist/modes/interactive/run-indicator.d.ts +19 -2
  50. package/dist/modes/interactive/run-indicator.js +29 -7
  51. package/dist/modes/interactive/status-bar.d.ts +4 -1
  52. package/dist/modes/interactive/status-bar.js +17 -6
  53. package/dist/modes/interactive/status-line.d.ts +10 -9
  54. package/dist/modes/interactive/status-line.js +46 -38
  55. package/dist/modes/interactive/status-quota.d.ts +16 -2
  56. package/dist/modes/interactive/status-quota.js +54 -34
  57. package/dist/modes/interactive/task-background.d.ts +2 -2
  58. package/dist/modes/rpc/commands.d.ts +2 -0
  59. package/dist/modes/rpc/commands.js +15 -1
  60. package/dist/rpc.d.ts +7 -0
  61. package/dist/tools/types.d.ts +6 -0
  62. package/dist/tui/keybindings.d.ts +7 -0
  63. package/dist/tui/keybindings.js +7 -0
  64. package/docs/en/rpc.md +9 -7
  65. package/docs/en/tui.md +49 -31
  66. package/docs/rpc.md +9 -7
  67. package/docs/session-format.md +1 -1
  68. package/docs/tui.md +49 -31
  69. package/package.json +1 -1
@@ -280,6 +280,7 @@ function buildBaseSchema() {
280
280
  logo: oneOf(["auto", "off"]),
281
281
  animation: bool(),
282
282
  restoreOnCancel: bool(),
283
+ enterWhileRunning: oneOf(["queue", "interrupt"]),
283
284
  statusLine: oneOf(STATUS_LINE_MODES),
284
285
  language: oneOf(LANGUAGE_SETTINGS),
285
286
  replyLanguage: str(),
@@ -23,7 +23,14 @@ const IMPLICIT_DEFAULTS = {
23
23
  codemode: { inlineBudget: DEFAULT_INLINE_BUDGET, requireStrict: false },
24
24
  cache: { ...DEFAULT_CACHE_CONFIG },
25
25
  request: { idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS },
26
- ui: { compact: false, logo: "auto", animation: true, restoreOnCancel: true, language: "auto" },
26
+ ui: {
27
+ compact: false,
28
+ logo: "auto",
29
+ animation: true,
30
+ restoreOnCancel: true,
31
+ enterWhileRunning: "queue",
32
+ language: "auto",
33
+ },
27
34
  // [W6-C0] 第六波键的缺省(行为由各批次实现)
28
35
  memory: {
29
36
  enabled: false,
@@ -260,6 +260,7 @@ export function validateConfig(value) {
260
260
  "logo",
261
261
  "animation",
262
262
  "restoreOnCancel",
263
+ "enterWhileRunning",
263
264
  ...W5_UI_KEYS,
264
265
  ...W6_UI_KEYS,
265
266
  ], (s, p) => {
@@ -273,6 +274,7 @@ export function validateConfig(value) {
273
274
  c.oneOf(s, "logo", p, ["auto", "off"]);
274
275
  c.boolean(s, "animation", p);
275
276
  c.boolean(s, "restoreOnCancel", p);
277
+ c.oneOf(s, "enterWhileRunning", p, ["queue", "interrupt"]);
276
278
  checkUiW5(c, s, p);
277
279
  checkUiW6(c, s, p);
278
280
  });
@@ -34,6 +34,7 @@ const ROWS = [
34
34
  row("ui.logo", "ui", "restart", "any"),
35
35
  row("ui.statusLine", "ui", "now", "any"),
36
36
  row("ui.restoreOnCancel", "ui", "nextSession", "any"),
37
+ row("ui.enterWhileRunning", "ui", "now", "any"),
37
38
  row("ui.quietStartup", "ui", "restart", "any"),
38
39
  row("ui.ascii", "ui", "restart", "any", { envOverride: "AMA_ASCII" }),
39
40
  row("ui.language", "ui", "restart", "any", { envOverride: "AMA_LANG" }),
@@ -151,6 +151,8 @@ export interface UiConfig extends UiConfigW6 {
151
151
  animation?: boolean;
152
152
  /** 运行中 Esc 中断、本回合还没有任何输出时撤回该回合并回填原消息,缺省 true。 */
153
153
  restoreOnCancel?: boolean;
154
+ /** 运行中按 Enter:queue(缺省)排队插话,interrupt 打断并立即发送(与 `app.message.interrupt` 互换)。 */
155
+ enterWhileRunning?: "queue" | "interrupt";
154
156
  /** [W5-C0] 底部信息行(W5-A):缺省独立终端 full、嵌入宿主(有 profile)compact。 */
155
157
  statusLine?: StatusLineMode;
156
158
  }
@@ -135,6 +135,16 @@ export class ProcessRunner {
135
135
  return this.deps;
136
136
  }
137
137
  }
138
+ /**
139
+ * 能中断单个回合、进程留着接着用的驱动(ACP `session/cancel`、Claude stream-json interrupt、Codex
140
+ * `turn/interrupt`);oneshot 每回合一个进程(中断 = 杀进程),宿主驱动不归 ama 管——这两种「打断并发送」退回排队。
141
+ */
142
+ const TURN_INTERRUPT = new Set([
143
+ "acp",
144
+ "acp-adapter",
145
+ "claude-stream",
146
+ "codex-app-server",
147
+ ]);
138
148
  class ProcessHandle {
139
149
  runner;
140
150
  chosen;
@@ -209,8 +219,25 @@ class ProcessHandle {
209
219
  }
210
220
  this.chain = this.chain.then(() => this.runTurn(text));
211
221
  }
212
- wait() {
213
- return this.chain;
222
+ /** 跟到链尾:回合进行中被 `interrupt` 接上的下一回合也算在这次运行里。 */
223
+ async wait() {
224
+ for (;;) {
225
+ const chain = this.chain;
226
+ const result = await chain;
227
+ if (chain === this.chain)
228
+ return result;
229
+ }
230
+ }
231
+ /** 子 Agent 视图的「打断并发送」:中断当前回合,紧接着以 `text` 开下一回合。 */
232
+ async interrupt(text) {
233
+ const session = this.session;
234
+ if (this.stopped || !this.busy || session === undefined)
235
+ return false;
236
+ if (!TURN_INTERRUPT.has(this.chosen.driver.kind))
237
+ return false;
238
+ this.chain = this.chain.then(() => this.runTurn(text));
239
+ await session.cancel();
240
+ return true;
214
241
  }
215
242
  async stop() {
216
243
  if (this.stopped)
@@ -497,6 +497,8 @@ declare const en: {
497
497
  clearedCtrlC: string;
498
498
  ctrlCAgain: string;
499
499
  interrupted: string;
500
+ interruptSent: string;
501
+ nothingToSend: string;
500
502
  toolsExpanded: string;
501
503
  toolsCollapsed: string;
502
504
  };
@@ -585,6 +587,7 @@ declare const en: {
585
587
  host: string;
586
588
  task: string;
587
589
  plan: string;
590
+ interrupt: string;
588
591
  };
589
592
  planHandoff: string;
590
593
  thinkingStreaming: (glyph: string, ellipsis: string) => string;
@@ -613,6 +616,8 @@ declare const en: {
613
616
  thinking: string;
614
617
  queueMore: (ellipsis: string, n: number) => string;
615
618
  queueHint: (arrow: string) => string;
619
+ queueHintNow: (arrow: string, key: string) => string;
620
+ sendHint: (mode: "queue" | "interrupt", key: string) => string;
616
621
  };
617
622
  tool: {
618
623
  interrupted: string;
@@ -1084,6 +1089,8 @@ declare const en: {
1084
1089
  tasks: string;
1085
1090
  agents: string;
1086
1091
  paste: string;
1092
+ interruptArgs: string;
1093
+ interrupt: string;
1087
1094
  exit: string;
1088
1095
  config: string;
1089
1096
  traceArgs: string;
@@ -1101,6 +1108,8 @@ declare const en: {
1101
1108
  cacheUsage: string;
1102
1109
  taskStopped: (id: string) => string;
1103
1110
  tasksUsage: string;
1111
+ interruptUsage: string;
1112
+ interruptSent: (text: string) => string;
1104
1113
  newSession: (id: string) => string;
1105
1114
  resumed: (id: string, messages: number) => string;
1106
1115
  forked: (id: string) => string;
@@ -1335,6 +1344,7 @@ declare const en: {
1335
1344
  "ui.logo": string;
1336
1345
  "ui.animation": string;
1337
1346
  "ui.restoreOnCancel": string;
1347
+ "ui.enterWhileRunning": string;
1338
1348
  "ui.statusLine": string;
1339
1349
  "ui.language": string;
1340
1350
  "ui.replyLanguage": string;
@@ -1781,6 +1791,8 @@ declare const en: {
1781
1791
  steered: (taskId: string) => string;
1782
1792
  queued: (taskId: string) => string;
1783
1793
  resumed: (taskId: string) => string;
1794
+ interrupted: (taskId: string) => string;
1795
+ queuedNoInterrupt: (taskId: string) => string;
1784
1796
  sendFailed: (message: string) => string;
1785
1797
  stopped: (taskId: string) => string;
1786
1798
  commandsHere: string;
@@ -2087,6 +2099,7 @@ declare const en: {
2087
2099
  "ui.logo": string;
2088
2100
  "ui.statusLine": string;
2089
2101
  "ui.restoreOnCancel": string;
2102
+ "ui.enterWhileRunning": string;
2090
2103
  "ui.quietStartup": string;
2091
2104
  "ui.ascii": string;
2092
2105
  "ui.language": string;
@@ -63,6 +63,8 @@ export declare const en: {
63
63
  steered: (taskId: string) => string;
64
64
  queued: (taskId: string) => string;
65
65
  resumed: (taskId: string) => string;
66
+ interrupted: (taskId: string) => string;
67
+ queuedNoInterrupt: (taskId: string) => string;
66
68
  sendFailed: (message: string) => string;
67
69
  stopped: (taskId: string) => string;
68
70
  commandsHere: string;
@@ -125,6 +127,8 @@ export declare const zh: {
125
127
  steered: (taskId: string) => string;
126
128
  queued: (taskId: string) => string;
127
129
  resumed: (taskId: string) => string;
130
+ interrupted: (taskId: string) => string;
131
+ queuedNoInterrupt: (taskId: string) => string;
128
132
  sendFailed: (message: string) => string;
129
133
  stopped: (taskId: string) => string;
130
134
  commandsHere: string;
@@ -64,6 +64,8 @@ export const en = {
64
64
  steered: (taskId) => `Sent to ${taskId}; delivered when its current turn ends`,
65
65
  queued: (taskId) => `Queued for ${taskId}; sent when its current run ends`,
66
66
  resumed: (taskId) => `Sent to ${taskId}; it continues in the background`,
67
+ interrupted: (taskId) => `Interrupted ${taskId}; your message starts its next turn now`,
68
+ queuedNoInterrupt: (taskId) => `${taskId} cannot be interrupted here; queued, sent when its current run ends`,
67
69
  sendFailed: (message) => `Could not send: ${message}`,
68
70
  stopped: (taskId) => `Stopped ${taskId}`,
69
71
  commandsHere: "Only /tasks stop and /tasks bg work here; press Esc to go back for other commands",
@@ -126,6 +128,8 @@ export const zh = {
126
128
  steered: (taskId) => `已发给 ${taskId},本轮结束后送达`,
127
129
  queued: (taskId) => `已排队,${taskId} 本次运行结束后发送`,
128
130
  resumed: (taskId) => `已发给 ${taskId},在后台继续`,
131
+ interrupted: (taskId) => `已打断 ${taskId},这条消息立即开始新一轮`,
132
+ queuedNoInterrupt: (taskId) => `${taskId} 不支持打断,已排队,本次运行结束后发送`,
129
133
  sendFailed: (message) => `发送失败:${message}`,
130
134
  stopped: (taskId) => `已停止 ${taskId}`,
131
135
  commandsHere: "这里只能用 /tasks stop 与 /tasks bg;其它命令按 Esc 返回后输入",
@@ -55,6 +55,7 @@ export declare const en: {
55
55
  "ui.logo": string;
56
56
  "ui.animation": string;
57
57
  "ui.restoreOnCancel": string;
58
+ "ui.enterWhileRunning": string;
58
59
  "ui.statusLine": string;
59
60
  "ui.language": string;
60
61
  "ui.replyLanguage": string;
@@ -202,6 +203,7 @@ export declare const zh: {
202
203
  "ui.logo": string;
203
204
  "ui.animation": string;
204
205
  "ui.restoreOnCancel": string;
206
+ "ui.enterWhileRunning": string;
205
207
  "ui.statusLine": string;
206
208
  "ui.language": string;
207
209
  "ui.replyLanguage": string;
@@ -102,6 +102,7 @@ const keysEn = {
102
102
  "ui.logo": "AMA logo in the startup header: auto draws it when the terminal is at least 48 columns wide, off shows only the info lines",
103
103
  "ui.animation": "false: the spinner stays still while running and redraws only when seconds change; the startup logo does not animate",
104
104
  "ui.restoreOnCancel": "When Esc interrupts a run before any reply or tool call in this turn, withdraw the turn and put the message back into the input box",
105
+ "ui.enterWhileRunning": "Enter while a run is in progress: queue (default) queues the message as a steer delivered at the next delivery point; interrupt stops the current turn and sends it at once. The app.message.interrupt key (default Ctrl+X) does the other one",
105
106
  "ui.statusLine": "Bottom info line: full shows two lines (rate line + status line), compact one; toggle at runtime with Ctrl+G or /statusline (effective from wave 5 W5-A)",
106
107
  "ui.language": "Interface language: auto decides from LC_ALL / LC_MESSAGES / LANG (zh* is Chinese, anything else English), zh, en; AMA_LANG and --lang override; affects the interface only, text sent to the model is always English",
107
108
  "ui.replyLanguage": "Reply language for the model (e.g. Chinese): one English rule is appended to the end of the rules section at session start; zero-byte change when unset; user level only (effective from wave 6)",
@@ -224,6 +225,7 @@ const keysZh = {
224
225
  "ui.logo": "启动头的 AMA 字符画:auto 在终端宽 48 列以上时画,off 只显示信息行",
225
226
  "ui.animation": "false:运行中 spinner 静止,只在秒数变化时重绘;启动字符画不播放动画",
226
227
  "ui.restoreOnCancel": "运行中 Esc 中断、本回合还没有任何回复或工具调用时,撤回该回合并把原消息放回输入框",
228
+ "ui.enterWhileRunning": "运行中按 Enter:queue(缺省)排队插话,等下一个投递点送达;interrupt 打断当前回合并立即发送。app.message.interrupt 键(缺省 Ctrl+X)做另一种",
227
229
  "ui.statusLine": "底部信息行:full 两行(速率行 + 状态行),compact 一行;运行时 Ctrl+G 或 /statusline 切换(第五波 W5-A 起生效)",
228
230
  "ui.language": "界面语言:auto 按 LC_ALL / LC_MESSAGES / LANG 判断(zh* 为中文,其余英文),zh,en;AMA_LANG、--lang 覆盖;只影响界面,发给模型的文本固定英文",
229
231
  "ui.replyLanguage": "模型回复语言(如 Chinese):会话开始在 rules 节末尾追加一句英文规则;不设时零字节变化;只认用户级(第六波起生效)",
@@ -55,6 +55,7 @@ export declare const en: {
55
55
  "ui.logo": string;
56
56
  "ui.animation": string;
57
57
  "ui.restoreOnCancel": string;
58
+ "ui.enterWhileRunning": string;
58
59
  "ui.statusLine": string;
59
60
  "ui.language": string;
60
61
  "ui.replyLanguage": string;
@@ -314,6 +315,7 @@ export declare const zh: {
314
315
  "ui.logo": string;
315
316
  "ui.animation": string;
316
317
  "ui.restoreOnCancel": string;
318
+ "ui.enterWhileRunning": string;
317
319
  "ui.statusLine": string;
318
320
  "ui.language": string;
319
321
  "ui.replyLanguage": string;
@@ -19,6 +19,7 @@ export declare const en: {
19
19
  host: string;
20
20
  task: string;
21
21
  plan: string;
22
+ interrupt: string;
22
23
  };
23
24
  planHandoff: string;
24
25
  thinkingStreaming: (glyph: string, ellipsis: string) => string;
@@ -47,6 +48,10 @@ export declare const en: {
47
48
  thinking: string;
48
49
  queueMore: (ellipsis: string, n: number) => string;
49
50
  queueHint: (arrow: string) => string;
51
+ /** 有排队的插话时(`key` = 立即送出它们的键:专用键,或 interrupt 模式下的 Enter)。 */
52
+ queueHintNow: (arrow: string, key: string) => string;
53
+ /** 输入框有字时:Enter 与专用键各做什么(`ui.enterWhileRunning`)。 */
54
+ sendHint: (mode: "queue" | "interrupt", key: string) => string;
50
55
  };
51
56
  tool: {
52
57
  interrupted: string;
@@ -85,6 +90,7 @@ export declare const zh: {
85
90
  host: string;
86
91
  task: string;
87
92
  plan: string;
93
+ interrupt: string;
88
94
  };
89
95
  planHandoff: string;
90
96
  thinkingStreaming: (glyph: string, ellipsis: string) => string;
@@ -113,6 +119,8 @@ export declare const zh: {
113
119
  thinking: string;
114
120
  queueMore: (ellipsis: string, n: number) => string;
115
121
  queueHint: (arrow: string) => string;
122
+ queueHintNow: (arrow: string, key: string) => string;
123
+ sendHint: (mode: "interrupt" | "queue", key: string) => string;
116
124
  };
117
125
  tool: {
118
126
  interrupted: string;
@@ -20,6 +20,7 @@ export const en = {
20
20
  host: "host",
21
21
  task: "subagent notice",
22
22
  plan: "plan",
23
+ interrupt: "interrupt",
23
24
  },
24
25
  planHandoff: "Start executing the approved plan",
25
26
  thinkingStreaming: (glyph, ellipsis) => `${glyph} Thinking${ellipsis}`,
@@ -48,6 +49,12 @@ export const en = {
48
49
  thinking: "Thinking",
49
50
  queueMore: (ellipsis, n) => `${ellipsis} ${n} more`,
50
51
  queueHint: (arrow) => `Alt+${arrow} take back · Esc refill and interrupt`,
52
+ /** 有排队的插话时(`key` = 立即送出它们的键:专用键,或 interrupt 模式下的 Enter)。 */
53
+ queueHintNow: (arrow, key) => `Alt+${arrow} take back · ${key} send now · Esc refill and interrupt`,
54
+ /** 输入框有字时:Enter 与专用键各做什么(`ui.enterWhileRunning`)。 */
55
+ sendHint: (mode, key) => mode === "queue"
56
+ ? `Enter queue · ${key} interrupt & send`
57
+ : `Enter interrupt & send · ${key} queue`,
51
58
  },
52
59
  tool: {
53
60
  interrupted: "interrupted",
@@ -86,6 +93,7 @@ export const zh = {
86
93
  host: "宿主",
87
94
  task: "子 Agent 通知",
88
95
  plan: "计划",
96
+ interrupt: "打断",
89
97
  },
90
98
  planHandoff: "按批准的计划开始执行",
91
99
  thinkingStreaming: (glyph, ellipsis) => `${glyph} 思考中${ellipsis}`,
@@ -114,6 +122,8 @@ export const zh = {
114
122
  thinking: "思考中",
115
123
  queueMore: (ellipsis, n) => `${ellipsis} 另 ${n} 条`,
116
124
  queueHint: (arrow) => `Alt+${arrow} 取回 · Esc 回填并中断`,
125
+ queueHintNow: (arrow, key) => `Alt+${arrow} 取回 · ${key} 立即发送 · Esc 回填并中断`,
126
+ sendHint: (mode, key) => mode === "queue" ? `Enter 排队 · ${key} 打断并发送` : `Enter 打断并发送 · ${key} 排队`,
117
127
  },
118
128
  tool: {
119
129
  interrupted: "已中断",
@@ -49,6 +49,8 @@ export declare const en: {
49
49
  clearedCtrlC: string;
50
50
  ctrlCAgain: string;
51
51
  interrupted: string;
52
+ interruptSent: string;
53
+ nothingToSend: string;
52
54
  toolsExpanded: string;
53
55
  toolsCollapsed: string;
54
56
  };
@@ -140,6 +142,7 @@ export declare const en: {
140
142
  host: string;
141
143
  task: string;
142
144
  plan: string;
145
+ interrupt: string;
143
146
  };
144
147
  planHandoff: string;
145
148
  thinkingStreaming: (glyph: string, ellipsis: string) => string;
@@ -168,6 +171,8 @@ export declare const en: {
168
171
  thinking: string;
169
172
  queueMore: (ellipsis: string, n: number) => string;
170
173
  queueHint: (arrow: string) => string;
174
+ queueHintNow: (arrow: string, key: string) => string;
175
+ sendHint: (mode: "queue" | "interrupt", key: string) => string;
171
176
  };
172
177
  tool: {
173
178
  interrupted: string;
@@ -292,6 +297,8 @@ export declare const zh: {
292
297
  clearedCtrlC: string;
293
298
  ctrlCAgain: string;
294
299
  interrupted: string;
300
+ interruptSent: string;
301
+ nothingToSend: string;
295
302
  toolsExpanded: string;
296
303
  toolsCollapsed: string;
297
304
  };
@@ -380,6 +387,7 @@ export declare const zh: {
380
387
  host: string;
381
388
  task: string;
382
389
  plan: string;
390
+ interrupt: string;
383
391
  };
384
392
  planHandoff: string;
385
393
  thinkingStreaming: (glyph: string, ellipsis: string) => string;
@@ -408,6 +416,8 @@ export declare const zh: {
408
416
  thinking: string;
409
417
  queueMore: (ellipsis: string, n: number) => string;
410
418
  queueHint: (arrow: string) => string;
419
+ queueHintNow: (arrow: string, key: string) => string;
420
+ sendHint: (mode: "interrupt" | "queue", key: string) => string;
411
421
  };
412
422
  tool: {
413
423
  interrupted: string;
@@ -20,6 +20,7 @@ export const en = {
20
20
  keyHints: [
21
21
  "Enter send (while running = steer) Alt+Enter queue after this turn Shift+Enter / Ctrl+J newline",
22
22
  "Esc interrupt (queued messages go back to the editor) Alt+↑ take back the last queued message",
23
+ "Ctrl+X (while running) interrupt the current turn and send the input now, queued steers first; ui.enterWhileRunning: interrupt swaps it with Enter",
23
24
  "Idle Esc Esc: empty input = rewind (/rewind), with text = clear (↑ to restore)",
24
25
  "Shift+Tab / Tab (empty input) cycle permission mode (confirm before Bypass) Ctrl+L model Ctrl+T thinking level Ctrl+O expand tool output and thinking",
25
26
  "Approval: 1–3 or ↑↓ Enter to choose, y allow a allow for this session n / Esc deny v full input",
@@ -62,6 +63,8 @@ export const en = {
62
63
  clearedCtrlC: "Input cleared · press Ctrl+C again to quit",
63
64
  ctrlCAgain: "Press Ctrl+C again to quit",
64
65
  interrupted: "Interrupted",
66
+ interruptSent: "Interrupted · sending now",
67
+ nothingToSend: "Nothing to send",
65
68
  toolsExpanded: "Tool output: expanded",
66
69
  toolsCollapsed: "Tool output: collapsed",
67
70
  },
@@ -152,6 +155,7 @@ export const zh = {
152
155
  keyHints: [
153
156
  "Enter 发送(运行中 = 插话) Alt+Enter 排到本轮之后 Shift+Enter / Ctrl+J 换行",
154
157
  "Esc 中断(排队消息回填编辑器) Alt+↑ 取回最后一条排队消息",
158
+ "Ctrl+X(运行中)打断当前回合并立即发送输入(排队的插话在前);ui.enterWhileRunning: interrupt 时与 Enter 互换",
155
159
  "空闲时 Esc Esc:输入框为空 = 回滚(/rewind),有字 = 清空(↑ 取回)",
156
160
  "Shift+Tab / Tab(输入为空时)切换权限模式(进入 Bypass 前确认) Ctrl+L 模型 Ctrl+T 思考级别 Ctrl+O 展开工具输出与思考",
157
161
  "审批:1–3 或 ↑↓ Enter 选择,y 允许 a 本会话允许同类 n / Esc 拒绝 v 完整输入",
@@ -192,6 +196,8 @@ export const zh = {
192
196
  clearedCtrlC: "已清空输入 · 再按 Ctrl+C 退出",
193
197
  ctrlCAgain: "再按一次 Ctrl+C 退出",
194
198
  interrupted: "已中断",
199
+ interruptSent: "已打断,立即发送",
200
+ nothingToSend: "没有可发送的内容",
195
201
  toolsExpanded: "工具输出:展开",
196
202
  toolsCollapsed: "工具输出:折叠",
197
203
  },
@@ -36,6 +36,8 @@ export declare const en: {
36
36
  tasks: string;
37
37
  agents: string;
38
38
  paste: string;
39
+ interruptArgs: string;
40
+ interrupt: string;
39
41
  exit: string;
40
42
  config: string;
41
43
  traceArgs: string;
@@ -54,6 +56,8 @@ export declare const en: {
54
56
  cacheUsage: string;
55
57
  taskStopped: (id: string) => string;
56
58
  tasksUsage: string;
59
+ interruptUsage: string;
60
+ interruptSent: (text: string) => string;
57
61
  newSession: (id: string) => string;
58
62
  resumed: (id: string, messages: number) => string;
59
63
  forked: (id: string) => string;
@@ -228,6 +232,8 @@ export declare const zh: {
228
232
  tasks: string;
229
233
  agents: string;
230
234
  paste: string;
235
+ interruptArgs: string;
236
+ interrupt: string;
231
237
  exit: string;
232
238
  config: string;
233
239
  traceArgs: string;
@@ -245,6 +251,8 @@ export declare const zh: {
245
251
  cacheUsage: string;
246
252
  taskStopped: (id: string) => string;
247
253
  tasksUsage: string;
254
+ interruptUsage: string;
255
+ interruptSent: (text: string) => string;
248
256
  newSession: (id: string) => string;
249
257
  resumed: (id: string, messages: number) => string;
250
258
  forked: (id: string) => string;
@@ -36,6 +36,8 @@ export const en = {
36
36
  tasks: "Sub-agent tasks: status, output, stop, background",
37
37
  agents: "Sub-agent types and external agents (installed, version)",
38
38
  paste: "Paste an image from the clipboard (Ctrl+V)",
39
+ interruptArgs: "<text>",
40
+ interrupt: "Interrupt the current turn and send this now, queued steers first (Ctrl+X)",
39
41
  exit: "Exit",
40
42
  config: "Settings panel; key=value sets one item directly (user level)",
41
43
  traceArgs: "[task id]",
@@ -54,6 +56,8 @@ export const en = {
54
56
  cacheUsage: "usage: /cache [warm off|streaming|idle | fingerprint]",
55
57
  taskStopped: (id) => `Stopped ${id}`,
56
58
  tasksUsage: "usage: /tasks [id] | /tasks stop <id> | /tasks bg [id]",
59
+ interruptUsage: "usage: /interrupt <text>",
60
+ interruptSent: (text) => `Interrupted; sending now: ${text}`,
57
61
  newSession: (id) => `New session ${id}`,
58
62
  resumed: (id, messages) => `Resumed session ${id} (${plural(messages, "message")})`,
59
63
  forked: (id) => `Forked to new session ${id}`,
@@ -279,6 +283,8 @@ export const zh = {
279
283
  tasks: "子 Agent 任务:状态、输出、停止、转后台",
280
284
  agents: "子 Agent 类型与外部 Agent(安装状态、版本)",
281
285
  paste: "粘贴剪贴板里的图片(Ctrl+V)",
286
+ interruptArgs: "<文本>",
287
+ interrupt: "打断当前回合并立即发送这段文字,排队的插话在前(Ctrl+X)",
282
288
  exit: "退出",
283
289
  config: "设置面板;key=value 直接设一项(用户级)",
284
290
  traceArgs: "[任务 id]",
@@ -296,6 +302,8 @@ export const zh = {
296
302
  cacheUsage: "用法:/cache [warm off|streaming|idle | fingerprint]",
297
303
  taskStopped: (id) => `已停止 ${id}`,
298
304
  tasksUsage: "用法:/tasks [id] | /tasks stop <id> | /tasks bg [id]",
305
+ interruptUsage: "用法:/interrupt <文本>",
306
+ interruptSent: (text) => `已打断,立即发送:${text}`,
299
307
  newSession: (id) => `已新建会话 ${id}`,
300
308
  resumed: (id, messages) => `已恢复会话 ${id}(${messages} 条消息)`,
301
309
  forked: (id) => `已分叉到新会话 ${id}`,
@@ -28,6 +28,7 @@ export declare const en: {
28
28
  "ui.logo": string;
29
29
  "ui.statusLine": string;
30
30
  "ui.restoreOnCancel": string;
31
+ "ui.enterWhileRunning": string;
31
32
  "ui.quietStartup": string;
32
33
  "ui.ascii": string;
33
34
  "ui.language": string;
@@ -205,6 +206,7 @@ export declare const zh: {
205
206
  "ui.logo": string;
206
207
  "ui.statusLine": string;
207
208
  "ui.restoreOnCancel": string;
209
+ "ui.enterWhileRunning": string;
208
210
  "ui.quietStartup": string;
209
211
  "ui.ascii": string;
210
212
  "ui.language": string;
@@ -28,6 +28,7 @@ export const en = {
28
28
  "ui.logo": "Startup logo",
29
29
  "ui.statusLine": "Status line",
30
30
  "ui.restoreOnCancel": "Restore on cancel",
31
+ "ui.enterWhileRunning": "Enter while running",
31
32
  "ui.quietStartup": "Startup screen",
32
33
  "ui.ascii": "ASCII glyphs",
33
34
  "ui.language": "Interface language",
@@ -199,6 +200,7 @@ export const zh = {
199
200
  "ui.logo": "启动字符画",
200
201
  "ui.statusLine": "底部信息行",
201
202
  "ui.restoreOnCancel": "中断后放回原消息",
203
+ "ui.enterWhileRunning": "运行中按 Enter",
202
204
  "ui.quietStartup": "启动画面",
203
205
  "ui.ascii": "ASCII 字形",
204
206
  "ui.language": "界面语言",
@@ -67,6 +67,7 @@ export const BUILTIN_COMMANDS = [
67
67
  command("tasks", "tasks", "[id] | stop <id> | bg [id]"),
68
68
  command("agents", "agents"),
69
69
  command("paste", "paste"),
70
+ command("interrupt", "interrupt", { key: "interruptArgs" }),
70
71
  command("exit", "exit"),
71
72
  // [W6-C0] 第六波(W6-S / W6-T1 / W6-M 实现)
72
73
  command("config", "config", "[key=value]"),
@@ -246,6 +247,16 @@ export async function runSlashCommand(line, ctx) {
246
247
  return planCommand(session, args, ctx);
247
248
  case "tasks":
248
249
  return { kind: "handled", message: await tasksCommand(session, args) };
250
+ case "interrupt": {
251
+ // 打断并立即发送:运行中中止当前回合、以「排队的插话 + 本条」开新回合;空闲时就是普通提示
252
+ if (args === "")
253
+ throw new AmaError("invalid_arguments", msg().report.command.interruptUsage);
254
+ const { isStreaming, isCompacting } = session.state;
255
+ if (!isStreaming && !isCompacting)
256
+ return { kind: "prompt", text: args };
257
+ void session.prompt(args, { interrupt: true }).catch(() => undefined);
258
+ return { kind: "handled", message: msg().report.command.interruptSent(args) };
259
+ }
249
260
  case "agents":
250
261
  return { kind: "handled", message: describeAgents(session.state.sessionId) };
251
262
  case "paste": {
@@ -376,6 +376,7 @@ export class AgentUi {
376
376
  close: () => this.closeView(),
377
377
  stop: (id) => stopTask(this.deps.session().state.sessionId, id),
378
378
  background: (id) => this.deps.session().backgroundTask(id, "user"),
379
+ enterMode: () => this.deps.area?.ui().enterWhileRunning === "interrupt" ? "interrupt" : "queue",
379
380
  messages: {
380
381
  ...(ui.showThinking !== undefined ? { showThinking: ui.showThinking } : {}),
381
382
  ...(ui.markdown !== undefined ? { markdown: ui.markdown } : {}),
@@ -18,6 +18,8 @@
18
18
  * 所查看的任务在等审批时标题显示「等待审批」,审批框照常以覆盖层弹在视图上面。
19
19
  * [W7-C] `Ctrl+B`(`app.tasks.background`)转后台正在看的任务(只对前台任务有效,否则落回编辑器);
20
20
  * 视图里另认 `/tasks bg [id]`。
21
+ * [打断并发送] `Ctrl+X`(`app.message.interrupt`)= 打断该子 Agent 当前回合并立即发送(`registry.message` 的
22
+ * interrupt);外部 Agent 的驱动不能中断回合时退回排队并提示。`ui.enterWhileRunning: "interrupt"` 时与 Enter 互换。
21
23
  */
22
24
  import { type Component, type Focusable, type Keybindings, type Theme } from "../../tui.js";
23
25
  import { type TaskSource } from "./agent-bar.js";
@@ -43,6 +45,8 @@ export interface AgentViewDeps {
43
45
  background?(taskId: string): string[];
44
46
  /** 消息区的显示选项(思考块、Markdown、紧凑)。 */
45
47
  messages?: Omit<MessageViewOptions, "theme">;
48
+ /** `ui.enterWhileRunning`:`interrupt` 时 Enter 打断并发送、专用键排队。 */
49
+ enterMode?(): "queue" | "interrupt";
46
50
  }
47
51
  export declare class AgentView implements Component, Focusable {
48
52
  private taskId;
@@ -67,6 +71,7 @@ export declare class AgentView implements Component, Focusable {
67
71
  /** 每秒:运行中的工具重画耗时。 */
68
72
  tick(): void;
69
73
  private switchTo;
74
+ private enterInterrupts;
70
75
  private submit;
71
76
  handleInput(data: string): void;
72
77
  private scroll;
@@ -18,6 +18,8 @@
18
18
  * 所查看的任务在等审批时标题显示「等待审批」,审批框照常以覆盖层弹在视图上面。
19
19
  * [W7-C] `Ctrl+B`(`app.tasks.background`)转后台正在看的任务(只对前台任务有效,否则落回编辑器);
20
20
  * 视图里另认 `/tasks bg [id]`。
21
+ * [打断并发送] `Ctrl+X`(`app.message.interrupt`)= 打断该子 Agent 当前回合并立即发送(`registry.message` 的
22
+ * interrupt);外部 Agent 的驱动不能中断回合时退回排队并提示。`ui.enterWhileRunning: "interrupt"` 时与 Enter 互换。
21
23
  */
22
24
  import { msg } from "../../i18n/index.js";
23
25
  import { Editor, isPrintableText, parseKey, truncateToWidth, visibleWidth, } from "../../tui.js";
@@ -47,7 +49,7 @@ export class AgentView {
47
49
  maxVisibleLines: 4,
48
50
  placeholder: msg().agents.view.placeholder(taskId),
49
51
  requestRender: () => deps.render(),
50
- onSubmit: (text) => void this.submit(text),
52
+ onSubmit: (text) => void this.submit(text, this.enterInterrupts()),
51
53
  });
52
54
  this.attach();
53
55
  }
@@ -137,7 +139,10 @@ export class AgentView {
137
139
  this.attach();
138
140
  this.deps.onSwitch?.(next);
139
141
  }
140
- async submit(text) {
142
+ enterInterrupts() {
143
+ return this.deps.enterMode?.() === "interrupt";
144
+ }
145
+ async submit(text, interrupt = false) {
141
146
  const m = msg().agents.view;
142
147
  const trimmed = text.trim();
143
148
  if (trimmed === "")
@@ -167,7 +172,7 @@ export class AgentView {
167
172
  try {
168
173
  if (registry === undefined)
169
174
  throw new Error(m.unknown(taskId));
170
- const reply = await registry.message(taskId, text);
175
+ const reply = await registry.message(taskId, text, interrupt);
171
176
  this.flash = m[reply](taskId);
172
177
  }
173
178
  catch (error) {
@@ -188,6 +193,12 @@ export class AgentView {
188
193
  return;
189
194
  }
190
195
  }
196
+ if (keys.matches(data, "app.message.interrupt") && !this.editor.isCompletionOpen) {
197
+ const text = this.editor.takeSubmission();
198
+ if (text !== null)
199
+ void this.submit(text, !this.enterInterrupts());
200
+ return;
201
+ }
191
202
  if (keys.matches(data, "tui.select.cancel") && !this.editor.isCompletionOpen) {
192
203
  if (!empty)
193
204
  this.editor.clear();