@keyou007/agent-bot 0.1.24-alpha.5 → 0.1.24-alpha.7

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 (105) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +29 -3
  3. package/README.zh.md +29 -3
  4. package/SECURITY.md +12 -0
  5. package/config.example.yaml +5 -0
  6. package/dist/cli/controlProtocol.js.map +1 -1
  7. package/dist/cli/serverStatus.js +1 -0
  8. package/dist/cli/serverStatus.js.map +1 -1
  9. package/dist/cli.js +6 -1
  10. package/dist/cli.js.map +1 -1
  11. package/dist/codex/CodexEventMapper.js +1 -0
  12. package/dist/codex/CodexEventMapper.js.map +1 -1
  13. package/dist/codex/CodexRuntime.js +247 -120
  14. package/dist/codex/CodexRuntime.js.map +1 -1
  15. package/dist/codex/ProviderSwitch.js +12 -13
  16. package/dist/codex/ProviderSwitch.js.map +1 -1
  17. package/dist/codex/TraePlanEntry.js +70 -0
  18. package/dist/codex/TraePlanEntry.js.map +1 -0
  19. package/dist/config/AgentExecutionDefaultsStore.js +4 -0
  20. package/dist/config/AgentExecutionDefaultsStore.js.map +1 -1
  21. package/dist/config/loadConfig.js +4 -0
  22. package/dist/config/loadConfig.js.map +1 -1
  23. package/dist/config/schema.js +3 -0
  24. package/dist/config/schema.js.map +1 -1
  25. package/dist/feishu/CardRenderer.js +92 -8
  26. package/dist/feishu/CardRenderer.js.map +1 -1
  27. package/dist/feishu/FeishuConnector.js +1 -0
  28. package/dist/feishu/FeishuConnector.js.map +1 -1
  29. package/dist/feishu/FeishuTurnPresenter.js +121 -17
  30. package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
  31. package/dist/index.js +57 -7
  32. package/dist/index.js.map +1 -1
  33. package/dist/local-files/LocalFileViewerServer.js +51 -4
  34. package/dist/local-files/LocalFileViewerServer.js.map +1 -1
  35. package/dist/local-files/TurnPreviewPage.js +7 -2
  36. package/dist/local-files/TurnPreviewPage.js.map +1 -1
  37. package/dist/presentation/TurnStateReducer.js +41 -6
  38. package/dist/presentation/TurnStateReducer.js.map +1 -1
  39. package/dist/proxy/ProxySessionController.js +620 -214
  40. package/dist/proxy/ProxySessionController.js.map +1 -1
  41. package/dist/proxy/SessionOperationQueue.js +14 -0
  42. package/dist/proxy/SessionOperationQueue.js.map +1 -0
  43. package/dist/runtime/AcpRuntimeAdapter.js +4 -2
  44. package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
  45. package/dist/state/StateStore.js +22 -0
  46. package/dist/state/StateStore.js.map +1 -1
  47. package/dist/state/TurnPreviewJournal.js +19 -7
  48. package/dist/state/TurnPreviewJournal.js.map +1 -1
  49. package/dist/state/migrations.js +6 -0
  50. package/dist/state/migrations.js.map +1 -1
  51. package/dist/supervision/SafeRestartNotifier.js +33 -1
  52. package/dist/supervision/SafeRestartNotifier.js.map +1 -1
  53. package/dist/supervision/SafeRestartScheduler.js +22 -6
  54. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  55. package/dist/terminal/TerminalCards.js +13 -0
  56. package/dist/terminal/TerminalCards.js.map +1 -0
  57. package/dist/terminal/TerminalCommandTracker.js +74 -0
  58. package/dist/terminal/TerminalCommandTracker.js.map +1 -0
  59. package/dist/terminal/TerminalLinks.js +76 -0
  60. package/dist/terminal/TerminalLinks.js.map +1 -0
  61. package/dist/terminal/TerminalPage.js +212 -0
  62. package/dist/terminal/TerminalPage.js.map +1 -0
  63. package/dist/terminal/TerminalProcess.js +95 -0
  64. package/dist/terminal/TerminalProcess.js.map +1 -0
  65. package/dist/terminal/TerminalServer.js +676 -0
  66. package/dist/terminal/TerminalServer.js.map +1 -0
  67. package/docs/technical-reference.md +48 -0
  68. package/docs/technical-reference.zh.md +48 -0
  69. package/npm-shrinkwrap.json +55 -5
  70. package/package.json +11 -4
  71. package/skills/agent-bot/SKILL.md +11 -2
  72. package/src/cli/controlProtocol.ts +2 -1
  73. package/src/cli/serverStatus.ts +1 -0
  74. package/src/cli.ts +6 -1
  75. package/src/codex/CodexEventMapper.ts +1 -0
  76. package/src/codex/CodexRuntime.ts +244 -128
  77. package/src/codex/ProviderSwitch.ts +11 -12
  78. package/src/codex/TraePlanEntry.ts +63 -0
  79. package/src/config/AgentExecutionDefaultsStore.ts +2 -0
  80. package/src/config/loadConfig.ts +4 -0
  81. package/src/config/schema.ts +5 -1
  82. package/src/feishu/CardRenderer.ts +109 -8
  83. package/src/feishu/FeishuConnector.ts +1 -0
  84. package/src/feishu/FeishuTurnPresenter.ts +108 -16
  85. package/src/feishu/types.ts +1 -0
  86. package/src/index.ts +56 -8
  87. package/src/local-files/LocalFileViewerServer.ts +48 -4
  88. package/src/local-files/TurnPreviewPage.ts +7 -2
  89. package/src/presentation/TurnStateReducer.ts +46 -7
  90. package/src/presentation/turnViewTypes.ts +5 -0
  91. package/src/proxy/ProxySessionController.ts +629 -211
  92. package/src/proxy/SessionOperationQueue.ts +13 -0
  93. package/src/runtime/AcpRuntimeAdapter.ts +5 -3
  94. package/src/runtime/types.ts +8 -3
  95. package/src/state/StateStore.ts +38 -1
  96. package/src/state/TurnPreviewJournal.ts +19 -7
  97. package/src/state/migrations.ts +6 -0
  98. package/src/supervision/SafeRestartNotifier.ts +35 -2
  99. package/src/supervision/SafeRestartScheduler.ts +25 -6
  100. package/src/terminal/TerminalCards.ts +18 -0
  101. package/src/terminal/TerminalCommandTracker.ts +74 -0
  102. package/src/terminal/TerminalLinks.ts +61 -0
  103. package/src/terminal/TerminalPage.ts +213 -0
  104. package/src/terminal/TerminalProcess.ts +93 -0
  105. package/src/terminal/TerminalServer.ts +551 -0
@@ -1,10 +1,11 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { approvalActivityId } from "../presentation/TurnStateReducer.js";
2
3
  import path from "node:path";
3
4
  import { truncateMiddle, truncateText } from "../utils/markdown.js";
4
5
  import { localCardImage } from "./LocalCardImage.js";
5
6
  const EXECUTION_SETTING_EFFECTS = {
6
7
  agent: "只影响新建任务,不切换当前任务。",
7
- provider: "只影响当前任务,需空闲时切换。",
8
+ provider: "只影响当前任务;运行中选择将在本轮结束后生效。",
8
9
  model: "只影响当前任务,下一轮生效。",
9
10
  thinking: "只影响当前任务,下一轮生效。",
10
11
  permission: "只影响当前任务,下一轮生效。",
@@ -199,6 +200,9 @@ export class CardRenderer {
199
200
  ...(outputWasTruncated ? ["输出过长,已保留开头和结尾并截断中间内容。"] : []),
200
201
  ].join("\n")),
201
202
  ];
203
+ if (view.terminalUrl) {
204
+ elements.push(markdown(`[Open Terminal](${view.terminalUrl}) · 独立终端,需在本会话由所有者授权`));
205
+ }
202
206
  if (view.status === "running" && view.jobId && view.contextKey) {
203
207
  elements.push({
204
208
  tag: "button",
@@ -234,6 +238,7 @@ export class CardRenderer {
234
238
  `**思考强度 / 权限**:${inlineCode(view.currentEffort ?? "自动")} / ${inlineCode(permissionModeLabel(view.currentPermissionMode))}`,
235
239
  ]
236
240
  : []),
241
+ ...(view.pendingNotice ? [view.pendingNotice] : []),
237
242
  ...(view.notice ? [view.notice] : []),
238
243
  ...(view.warning ? [`**提示**:${escapeCardHtml(view.warning)}`] : []),
239
244
  ].join("\n")),
@@ -267,7 +272,8 @@ export class CardRenderer {
267
272
  inlineCode(provider.id),
268
273
  provider.isDefault ? " · 默认" : "",
269
274
  ].join(""),
270
- current: provider.id === view.currentProvider,
275
+ current: provider.id === view.currentProvider && !view.pendingFailed,
276
+ currentLabel: view.pendingNotice ? "待生效" : undefined,
271
277
  action: {
272
278
  text: "Switch",
273
279
  value: {
@@ -284,18 +290,24 @@ export class CardRenderer {
284
290
  elements.push(markdown("当前运行时未返回可用模型。"));
285
291
  }
286
292
  else {
287
- elements.push(...view.models.map((model) => settingsOptionRow({
293
+ const page = paginateModels(view.models, view.currentModel, view.modelPage, {
294
+ action: "settings_tab_open", ...baseAction, tab: "model", provider: view.currentProvider ?? "",
295
+ });
296
+ elements.push(...page.models.map((model) => settingsOptionRow({
288
297
  label: `${inlineCode(model.id)}${model.isDefault ? " · 默认" : ""}`,
289
- current: model.id === view.currentModel,
298
+ current: model.id === view.currentModel && !view.pendingFailed,
299
+ currentLabel: view.pendingNotice ? "待生效" : undefined,
290
300
  action: {
291
301
  text: "Switch",
292
302
  value: {
293
303
  action: "settings_model_select",
294
304
  ...baseAction,
305
+ provider: view.currentProvider ?? "",
295
306
  model: model.id,
296
307
  },
297
308
  },
298
309
  })));
310
+ elements.push(...page.navigation);
299
311
  }
300
312
  }
301
313
  else if (view.activeTab === "thinking") {
@@ -310,11 +322,13 @@ export class CardRenderer {
310
322
  elements.push(...view.reasoningOptions.map((option) => settingsOptionRow({
311
323
  label: inlineCode(option.value),
312
324
  current: option.value === view.currentEffort,
325
+ currentLabel: view.pendingNotice ? "待生效" : undefined,
313
326
  action: {
314
327
  text: "Switch",
315
328
  value: {
316
329
  action: "settings_thinking_select",
317
330
  ...baseAction,
331
+ provider: view.currentProvider ?? "",
318
332
  model: currentModel,
319
333
  effort: option.value,
320
334
  },
@@ -326,6 +340,7 @@ export class CardRenderer {
326
340
  elements.push(...["auto", "confirm"].map((mode) => settingsOptionRow({
327
341
  label: `${inlineCode(mode)} · ${permissionModeLabel(mode)}`,
328
342
  current: mode === view.currentPermissionMode,
343
+ currentLabel: view.pendingNotice ? "待生效" : undefined,
329
344
  action: {
330
345
  text: "Switch",
331
346
  value: {
@@ -337,7 +352,7 @@ export class CardRenderer {
337
352
  })));
338
353
  }
339
354
  elements.push(markdown(`> ${EXECUTION_SETTING_EFFECTS[view.activeTab]}`));
340
- return sectionCard("运行设置", elements, view.warning ? "orange" : view.notice ? "green" : "blue");
355
+ return sectionCard("运行设置", elements, view.warning || view.pendingFailed ? "orange" : view.notice ? "green" : "blue");
341
356
  }
342
357
  renderProviderSelector(view) {
343
358
  const elements = [
@@ -470,7 +485,14 @@ export class CardRenderer {
470
485
  elements.push(markdown("当前运行时未返回可用模型。"));
471
486
  }
472
487
  else {
473
- elements.push(...view.models.map((model) => {
488
+ const page = paginateModels(view.models, view.currentModel, view.modelPage, {
489
+ action: view.unifiedSettings ? "provider_model_open" : "model_open",
490
+ sessionId: view.sessionId,
491
+ contextKey: view.contextKey,
492
+ ...(view.modelProvider ? { provider: view.modelProvider } : {}),
493
+ ...(view.permissionMode ? { permissionMode: view.permissionMode } : {}),
494
+ });
495
+ elements.push(...page.models.map((model) => {
474
496
  const isCurrent = model.id === view.currentModel;
475
497
  return {
476
498
  tag: "column_set",
@@ -506,6 +528,7 @@ export class CardRenderer {
506
528
  ],
507
529
  };
508
530
  }));
531
+ elements.push(...page.navigation);
509
532
  }
510
533
  if (view.unifiedSettings) {
511
534
  elements.push({ tag: "hr" }, taskActionRow([{
@@ -665,6 +688,24 @@ export class CardRenderer {
665
688
  }
666
689
  return sectionCard("Agent Bot 更新提醒", elements, view.status === "completed" ? "green" : view.status === "cancelled" ? "grey" : "blue");
667
690
  }
691
+ renderTerminalAuthorization(view) {
692
+ const action = (decision) => ({ action: "terminal_authorize", contextKey: view.contextKey, requestId: view.requestId, token: view.approvalToken, decision });
693
+ const elements = [markdown(`**目录**:${inlineCode(view.cwd)}\n**终端**:${inlineCode(view.reopenToken)}`)];
694
+ if (view.state === "pending") {
695
+ elements.push(markdown(`**验证码**:${inlineCode(view.code)}\n仅 AgentBot 所有者可授权。授权后,该浏览器可在本机执行任意命令,拥有 AgentBot 的系统用户权限。请核对网页验证码,仅批准本人发起的请求。5 分钟内有效。${view.recovery ? (view.shellStarted ? "恢复原 Shell,不创建新终端;授权后旧网页失去访问权限。拒绝不影响原终端。" : "继续打开同一个终端;授权后旧网页失去访问权限,Shell 在连接成功后启动。拒绝不影响原授权。") : "不执行原卡片命令。"}`), taskActionRow([{ text: "Allow", value: action("allow") }, { text: "Deny", type: "danger", value: action("deny") }, { text: "刷新", value: action("refresh") }]));
696
+ }
697
+ else if (view.state === "closed") {
698
+ elements.push(markdown(`**状态**:${view.message ? escapeCardHtml(view.message) : "已关闭"}\n原终端已结束时,请从命令卡片创建新终端。`));
699
+ }
700
+ else {
701
+ const waiting = view.state === "approved";
702
+ const label = waiting ? "打开终端" : "重新打开终端";
703
+ elements.push(markdown(`**状态**:${waiting ? "已授权,等待网页连接" : view.connected ? "已连接" : "网页已断开,Shell 仍在运行"}\n${waiting ? "请返回原网页连接;原网页已关闭时,点击下方“打开终端”。Shell 尚未启动。" : "点击下方“重新打开终端”恢复原 Shell。前台连接时不因空闲关闭;后台空闲 30 分钟或网页断开 3 分钟后会自动关闭 Shell,成功重连取消断开计时并重置空闲计时;“关闭终端”会中断其中的命令。"}`));
704
+ elements.push(markdown(`[${label}](${view.reopenUrl}) · 新网页需核对验证码重新授权${waiting ? ",连接成功后启动 Shell" : ",恢复原 Shell"}`));
705
+ elements.push(taskActionRow([{ text: "关闭终端", type: "danger", value: action("close") }, { text: "刷新", value: action("refresh") }]));
706
+ }
707
+ return sectionCard(view.state === "pending" ? "网页终端授权" : "网页终端管理", elements, view.state === "closed" ? "grey" : view.state === "pending" ? "orange" : "blue");
708
+ }
668
709
  renderSafeRestartStatus(view) {
669
710
  const actionable = view.phase !== "restarting" && view.phase !== "cancelled" && view.phase !== "superseded";
670
711
  const countdown = view.phase === "countdown"
@@ -692,8 +733,35 @@ export class CardRenderer {
692
733
  `**重启原因**:${inlineCode(view.reason)}`,
693
734
  `**重启倒计时**:${countdown}`,
694
735
  `**待投递结果**:${view.pendingFinalDeliveries} 条`,
736
+ ...(view.activeTerminals ? [`**活动终端**:${view.activeTerminals} 个。结束终端会中断其中的命令;ForceRestart 会中断所有终端。`] : []),
695
737
  ];
696
738
  const elements = [markdown(lines.join("\n"))];
739
+ if (view.terminalBlockers?.length) {
740
+ elements.push({ tag: "hr" }, markdown(`**阻止重启的终端(${view.terminalBlockers.length})**`));
741
+ for (const terminal of view.terminalBlockers) {
742
+ const command = terminal.state === "closing" ? "正在结束,等待 Shell 退出"
743
+ : terminal.state === "starting" ? "Shell 启动中"
744
+ : terminal.command.status === "idle" ? "无(Shell 空闲)"
745
+ : terminal.command.command ? inlineCode(escapeCardHtml(truncateText(terminal.command.command.replace(/\s+/gu, " "), 240)))
746
+ : terminal.command.status === "running" ? "命令执行中(命令内容不可用)"
747
+ : "未知(Shell 未提供命令信息)";
748
+ const connection = terminal.connected ? terminal.foreground ? "网页前台" : "网页已连接" : "网页已断开";
749
+ const label = `**终端**:${inlineCode(terminal.token)} · ${terminal.active ? "活跃" : "不活跃"} · ${connection}\n**目录**:${inlineCode(escapeCardHtml(truncateText(terminal.cwd, 240)))}\n**${terminal.command.source === "process" ? "前台进程" : "正在执行"}**:${command}`;
750
+ elements.push({
751
+ tag: "column_set", flex_mode: "none", horizontal_spacing: "8px",
752
+ columns: [
753
+ { tag: "column", width: "weighted", weight: 1, elements: [markdown(label)] },
754
+ ...(actionable && terminal.state !== "closing" ? [{
755
+ tag: "column", width: "auto", elements: [taskActionElement({
756
+ text: "结束终端", type: "danger",
757
+ value: { action: "safe_restart_terminal_stop", scheduleId: String(view.scheduleId), terminalId: terminal.terminalId, ...(view.contextKey ? { contextKey: view.contextKey } : {}) },
758
+ })],
759
+ }] : []),
760
+ ],
761
+ });
762
+ }
763
+ elements.push(markdown("活跃:前台连接、正在执行命令或最近 30 秒有输入输出。不活跃的 Shell 仍会阻止安全重启。"));
764
+ }
697
765
  if (view.waitingTasks.length > 0) {
698
766
  const visible = view.waitingTasks.slice(0, 10);
699
767
  elements.push({ tag: "hr" }, markdown(`**当前等待的任务(${view.waitingTasks.length})**`));
@@ -1396,6 +1464,10 @@ function renderApprovalElements(state, request) {
1396
1464
  request.reason,
1397
1465
  ].filter(Boolean).join("\n")));
1398
1466
  }
1467
+ if (request.automatic) {
1468
+ elements.push(markdown("自动执行模式:正在自动同意。"));
1469
+ return elements;
1470
+ }
1399
1471
  elements.push({
1400
1472
  tag: "column_set",
1401
1473
  flex_mode: "flow",
@@ -1781,7 +1853,8 @@ function removeMarkdownBold(value) {
1781
1853
  }
1782
1854
  function turnActivities(state) {
1783
1855
  if (state.activities?.length)
1784
- return state.activities;
1856
+ return state.approval
1857
+ ? state.activities.filter((a) => a.id !== approvalActivityId(state.turnId, state.approval.id)) : state.activities;
1785
1858
  const activities = [];
1786
1859
  if (state.progressText) {
1787
1860
  activities.push({ kind: "reasoning", id: "legacy-progress", text: state.progressText });
@@ -2334,6 +2407,17 @@ function acpPermissionOptionLabel(option) {
2334
2407
  };
2335
2408
  return labels[option.kind] ?? (/^[\x20-\x7E]+$/.test(option.name) ? option.name : "Select");
2336
2409
  }
2410
+ const MODEL_PAGE_SIZE = 20;
2411
+ function paginateModels(models, currentModel, requestedPage, action) {
2412
+ const totalPages = Math.max(1, Math.ceil(models.length / MODEL_PAGE_SIZE));
2413
+ const currentPage = Math.floor(models.findIndex((model) => model.id === currentModel) / MODEL_PAGE_SIZE);
2414
+ const page = Math.max(0, Math.min(requestedPage ?? currentPage, totalPages - 1));
2415
+ const navigation = totalPages > 1 ? [taskActionRow([
2416
+ ...(page > 0 ? [{ text: "上一页", value: { ...action, page: String(page - 1) } }] : []),
2417
+ ...(page < totalPages - 1 ? [{ text: "下一页", value: { ...action, page: String(page + 1) } }] : []),
2418
+ ], `第 ${page + 1}/${totalPages} 页 · 共 ${models.length} 个模型`)] : [];
2419
+ return { models: models.slice(page * MODEL_PAGE_SIZE, (page + 1) * MODEL_PAGE_SIZE), navigation };
2420
+ }
2337
2421
  function settingsTabRow(activeTab, baseAction, showAgentTab, showRuntimeTabs) {
2338
2422
  const tabs = [
2339
2423
  ...(showAgentTab ? [{ id: "agent", label: "Agent" }] : []),
@@ -2412,7 +2496,7 @@ function settingsOptionRow(input) {
2412
2496
  tag: "column",
2413
2497
  width: "auto",
2414
2498
  vertical_align: "center",
2415
- elements: input.current ? [markdown("✅ 当前")] : [taskActionElement(input.action)],
2499
+ elements: input.current ? [markdown(`✅ ${input.currentLabel ?? "当前"}`)] : [taskActionElement(input.action)],
2416
2500
  },
2417
2501
  ],
2418
2502
  };