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

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 (96) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +27 -3
  3. package/README.zh.md +27 -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/CodexRuntime.js +202 -115
  12. package/dist/codex/CodexRuntime.js.map +1 -1
  13. package/dist/codex/ProviderSwitch.js +12 -13
  14. package/dist/codex/ProviderSwitch.js.map +1 -1
  15. package/dist/config/AgentExecutionDefaultsStore.js +4 -0
  16. package/dist/config/AgentExecutionDefaultsStore.js.map +1 -1
  17. package/dist/config/loadConfig.js +4 -0
  18. package/dist/config/loadConfig.js.map +1 -1
  19. package/dist/config/schema.js +3 -0
  20. package/dist/config/schema.js.map +1 -1
  21. package/dist/feishu/CardRenderer.js +66 -8
  22. package/dist/feishu/CardRenderer.js.map +1 -1
  23. package/dist/feishu/FeishuConnector.js +1 -0
  24. package/dist/feishu/FeishuConnector.js.map +1 -1
  25. package/dist/feishu/FeishuTurnPresenter.js +121 -17
  26. package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
  27. package/dist/index.js +43 -6
  28. package/dist/index.js.map +1 -1
  29. package/dist/local-files/LocalFileViewerServer.js +51 -4
  30. package/dist/local-files/LocalFileViewerServer.js.map +1 -1
  31. package/dist/local-files/TurnPreviewPage.js +7 -2
  32. package/dist/local-files/TurnPreviewPage.js.map +1 -1
  33. package/dist/presentation/TurnStateReducer.js +41 -6
  34. package/dist/presentation/TurnStateReducer.js.map +1 -1
  35. package/dist/proxy/ProxySessionController.js +615 -214
  36. package/dist/proxy/ProxySessionController.js.map +1 -1
  37. package/dist/proxy/SessionOperationQueue.js +14 -0
  38. package/dist/proxy/SessionOperationQueue.js.map +1 -0
  39. package/dist/runtime/AcpRuntimeAdapter.js +4 -2
  40. package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
  41. package/dist/state/StateStore.js +22 -0
  42. package/dist/state/StateStore.js.map +1 -1
  43. package/dist/state/TurnPreviewJournal.js +19 -7
  44. package/dist/state/TurnPreviewJournal.js.map +1 -1
  45. package/dist/state/migrations.js +6 -0
  46. package/dist/state/migrations.js.map +1 -1
  47. package/dist/supervision/SafeRestartNotifier.js +1 -0
  48. package/dist/supervision/SafeRestartNotifier.js.map +1 -1
  49. package/dist/supervision/SafeRestartScheduler.js +19 -6
  50. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  51. package/dist/terminal/TerminalCards.js +13 -0
  52. package/dist/terminal/TerminalCards.js.map +1 -0
  53. package/dist/terminal/TerminalLinks.js +76 -0
  54. package/dist/terminal/TerminalLinks.js.map +1 -0
  55. package/dist/terminal/TerminalPage.js +212 -0
  56. package/dist/terminal/TerminalPage.js.map +1 -0
  57. package/dist/terminal/TerminalProcess.js +71 -0
  58. package/dist/terminal/TerminalProcess.js.map +1 -0
  59. package/dist/terminal/TerminalServer.js +639 -0
  60. package/dist/terminal/TerminalServer.js.map +1 -0
  61. package/docs/technical-reference.md +48 -0
  62. package/docs/technical-reference.zh.md +48 -0
  63. package/npm-shrinkwrap.json +55 -5
  64. package/package.json +11 -4
  65. package/skills/agent-bot/SKILL.md +9 -2
  66. package/src/cli/controlProtocol.ts +2 -1
  67. package/src/cli/serverStatus.ts +1 -0
  68. package/src/cli.ts +6 -1
  69. package/src/codex/CodexRuntime.ts +200 -123
  70. package/src/codex/ProviderSwitch.ts +11 -12
  71. package/src/config/AgentExecutionDefaultsStore.ts +2 -0
  72. package/src/config/loadConfig.ts +4 -0
  73. package/src/config/schema.ts +5 -1
  74. package/src/feishu/CardRenderer.ts +81 -8
  75. package/src/feishu/FeishuConnector.ts +1 -0
  76. package/src/feishu/FeishuTurnPresenter.ts +108 -16
  77. package/src/feishu/types.ts +1 -0
  78. package/src/index.ts +44 -7
  79. package/src/local-files/LocalFileViewerServer.ts +48 -4
  80. package/src/local-files/TurnPreviewPage.ts +7 -2
  81. package/src/presentation/TurnStateReducer.ts +46 -7
  82. package/src/presentation/turnViewTypes.ts +5 -0
  83. package/src/proxy/ProxySessionController.ts +625 -211
  84. package/src/proxy/SessionOperationQueue.ts +13 -0
  85. package/src/runtime/AcpRuntimeAdapter.ts +5 -3
  86. package/src/runtime/types.ts +8 -3
  87. package/src/state/StateStore.ts +38 -1
  88. package/src/state/TurnPreviewJournal.ts +19 -7
  89. package/src/state/migrations.ts +6 -0
  90. package/src/supervision/SafeRestartNotifier.ts +1 -0
  91. package/src/supervision/SafeRestartScheduler.ts +18 -6
  92. package/src/terminal/TerminalCards.ts +18 -0
  93. package/src/terminal/TerminalLinks.ts +61 -0
  94. package/src/terminal/TerminalPage.ts +213 -0
  95. package/src/terminal/TerminalProcess.ts +71 -0
  96. package/src/terminal/TerminalServer.ts +513 -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,6 +733,7 @@ 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"))];
697
739
  if (view.waitingTasks.length > 0) {
@@ -1396,6 +1438,10 @@ function renderApprovalElements(state, request) {
1396
1438
  request.reason,
1397
1439
  ].filter(Boolean).join("\n")));
1398
1440
  }
1441
+ if (request.automatic) {
1442
+ elements.push(markdown("自动执行模式:正在自动同意。"));
1443
+ return elements;
1444
+ }
1399
1445
  elements.push({
1400
1446
  tag: "column_set",
1401
1447
  flex_mode: "flow",
@@ -1781,7 +1827,8 @@ function removeMarkdownBold(value) {
1781
1827
  }
1782
1828
  function turnActivities(state) {
1783
1829
  if (state.activities?.length)
1784
- return state.activities;
1830
+ return state.approval
1831
+ ? state.activities.filter((a) => a.id !== approvalActivityId(state.turnId, state.approval.id)) : state.activities;
1785
1832
  const activities = [];
1786
1833
  if (state.progressText) {
1787
1834
  activities.push({ kind: "reasoning", id: "legacy-progress", text: state.progressText });
@@ -2334,6 +2381,17 @@ function acpPermissionOptionLabel(option) {
2334
2381
  };
2335
2382
  return labels[option.kind] ?? (/^[\x20-\x7E]+$/.test(option.name) ? option.name : "Select");
2336
2383
  }
2384
+ const MODEL_PAGE_SIZE = 20;
2385
+ function paginateModels(models, currentModel, requestedPage, action) {
2386
+ const totalPages = Math.max(1, Math.ceil(models.length / MODEL_PAGE_SIZE));
2387
+ const currentPage = Math.floor(models.findIndex((model) => model.id === currentModel) / MODEL_PAGE_SIZE);
2388
+ const page = Math.max(0, Math.min(requestedPage ?? currentPage, totalPages - 1));
2389
+ const navigation = totalPages > 1 ? [taskActionRow([
2390
+ ...(page > 0 ? [{ text: "上一页", value: { ...action, page: String(page - 1) } }] : []),
2391
+ ...(page < totalPages - 1 ? [{ text: "下一页", value: { ...action, page: String(page + 1) } }] : []),
2392
+ ], `第 ${page + 1}/${totalPages} 页 · 共 ${models.length} 个模型`)] : [];
2393
+ return { models: models.slice(page * MODEL_PAGE_SIZE, (page + 1) * MODEL_PAGE_SIZE), navigation };
2394
+ }
2337
2395
  function settingsTabRow(activeTab, baseAction, showAgentTab, showRuntimeTabs) {
2338
2396
  const tabs = [
2339
2397
  ...(showAgentTab ? [{ id: "agent", label: "Agent" }] : []),
@@ -2412,7 +2470,7 @@ function settingsOptionRow(input) {
2412
2470
  tag: "column",
2413
2471
  width: "auto",
2414
2472
  vertical_align: "center",
2415
- elements: input.current ? [markdown("✅ 当前")] : [taskActionElement(input.action)],
2473
+ elements: input.current ? [markdown(`✅ ${input.currentLabel ?? "当前"}`)] : [taskActionElement(input.action)],
2416
2474
  },
2417
2475
  ],
2418
2476
  };