@keyou007/agent-bot 0.1.10 → 0.1.11

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 (68) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +16 -10
  3. package/README.zh.md +16 -10
  4. package/assets/agent-bot-logo.png +0 -0
  5. package/dist/acp/AcpSessionManager.js +78 -46
  6. package/dist/acp/AcpSessionManager.js.map +1 -1
  7. package/dist/codex/AppServerConnection.js +15 -1
  8. package/dist/codex/AppServerConnection.js.map +1 -1
  9. package/dist/codex/CodexLocalActivityDetector.js +4 -1
  10. package/dist/codex/CodexLocalActivityDetector.js.map +1 -1
  11. package/dist/codex/CodexProcessManager.js +1 -0
  12. package/dist/codex/CodexProcessManager.js.map +1 -1
  13. package/dist/codex/CodexRuntime.js +77 -2
  14. package/dist/codex/CodexRuntime.js.map +1 -1
  15. package/dist/commands/CommandRouter.js +64 -49
  16. package/dist/commands/CommandRouter.js.map +1 -1
  17. package/dist/console/ConsoleTurnPresenter.js +1 -1
  18. package/dist/console/ConsoleTurnPresenter.js.map +1 -1
  19. package/dist/feishu/CardRenderer.js +345 -11
  20. package/dist/feishu/CardRenderer.js.map +1 -1
  21. package/dist/feishu/FeishuMessageClient.js +51 -5
  22. package/dist/feishu/FeishuMessageClient.js.map +1 -1
  23. package/dist/feishu/FeishuTurnPresenter.js +8 -3
  24. package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
  25. package/dist/index.js +18 -17
  26. package/dist/index.js.map +1 -1
  27. package/dist/proxy/ProxySessionController.js +589 -205
  28. package/dist/proxy/ProxySessionController.js.map +1 -1
  29. package/dist/runtime/AcpRuntimeAdapter.js +1 -0
  30. package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
  31. package/dist/runtime/AgentRuntimeRegistry.js +7 -4
  32. package/dist/runtime/AgentRuntimeRegistry.js.map +1 -1
  33. package/dist/runtime/createAgentRuntimeRegistry.js +22 -0
  34. package/dist/runtime/createAgentRuntimeRegistry.js.map +1 -0
  35. package/dist/startup/SessionMetadataHydrator.js +2 -2
  36. package/dist/startup/SessionMetadataHydrator.js.map +1 -1
  37. package/dist/startup/StartupNotifier.js +1 -0
  38. package/dist/startup/StartupNotifier.js.map +1 -1
  39. package/dist/state/StateStore.js +21 -15
  40. package/dist/state/StateStore.js.map +1 -1
  41. package/dist/supervision/SafeRestartScheduler.js +5 -0
  42. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  43. package/docs/technical-reference.md +15 -5
  44. package/docs/technical-reference.zh.md +15 -5
  45. package/npm-shrinkwrap.json +2 -2
  46. package/package.json +2 -1
  47. package/skills/agent-bot/SKILL.md +13 -3
  48. package/src/acp/AcpSessionManager.ts +73 -54
  49. package/src/codex/AppServerConnection.ts +21 -1
  50. package/src/codex/CodexLocalActivityDetector.ts +8 -1
  51. package/src/codex/CodexProcessManager.ts +1 -0
  52. package/src/codex/CodexRuntime.ts +91 -2
  53. package/src/commands/CommandRouter.ts +71 -49
  54. package/src/commands/commandTypes.ts +6 -11
  55. package/src/console/ConsoleTurnPresenter.ts +1 -1
  56. package/src/feishu/CardRenderer.ts +418 -11
  57. package/src/feishu/FeishuMessageClient.ts +66 -3
  58. package/src/feishu/FeishuTurnPresenter.ts +7 -2
  59. package/src/index.ts +21 -22
  60. package/src/proxy/ProxySessionController.ts +706 -208
  61. package/src/runtime/AcpRuntimeAdapter.ts +1 -0
  62. package/src/runtime/AgentRuntimeRegistry.ts +7 -6
  63. package/src/runtime/createAgentRuntimeRegistry.ts +25 -0
  64. package/src/runtime/types.ts +18 -0
  65. package/src/startup/SessionMetadataHydrator.ts +2 -2
  66. package/src/startup/StartupNotifier.ts +1 -0
  67. package/src/state/StateStore.ts +29 -15
  68. package/src/supervision/SafeRestartScheduler.ts +5 -0
@@ -2,19 +2,16 @@ import type { Command } from "./commandTypes.js";
2
2
 
3
3
  const COMMAND_NAMES = [
4
4
  "agent",
5
- "agents",
6
- "ask",
7
5
  "fork",
8
6
  "forkgroup",
9
7
  "goal",
10
8
  "help",
11
- "mode",
12
9
  "model",
13
- "modes",
14
10
  "new",
15
11
  "newgroup",
16
12
  "nosteer",
17
13
  "permissions",
14
+ "provider",
18
15
  "queue",
19
16
  "restart",
20
17
  "sessions",
@@ -23,7 +20,6 @@ const COMMAND_NAMES = [
23
20
  "switch",
24
21
  "thinking",
25
22
  "title",
26
- "use",
27
23
  ] as const;
28
24
 
29
25
  type CommandName = (typeof COMMAND_NAMES)[number];
@@ -34,6 +30,8 @@ const COMMAND_INITIALISMS: Partial<Record<CommandName, string>> = {
34
30
  nosteer: "ns",
35
31
  };
36
32
 
33
+ const DISABLED_COMMAND_NAMES = new Set(["mode", "modes"]);
34
+
37
35
  export class CommandRouter {
38
36
  parse(text: string): Command {
39
37
  const trimmed = text.trim();
@@ -50,25 +48,17 @@ export class CommandRouter {
50
48
  const command = resolveCommandName(rawCommand);
51
49
 
52
50
  switch (command) {
53
- case "agents":
54
- return { type: "agent" };
55
51
  case "new":
56
52
  return parseNewCommand(args);
57
53
  case "newgroup":
58
54
  return parseNewGroupCommand(args);
59
55
  case "forkgroup":
60
- return {
61
- type: "forkgroup",
62
- title: args.join(" ").trim() || undefined,
63
- };
56
+ return parseForkGroupCommand(args);
64
57
  case "fork":
65
- if (args.length > 1) throw new Error("/fork 只接受一个可选的任务序号或任务 ID。");
66
- return args[0] ? { type: "fork", sessionId: args[0] } : { type: "fork" };
58
+ return parseForkCommand(args);
67
59
  case "title":
68
60
  if (args.length === 0) throw new Error("请输入新标题,例如:/title 修复会话列表。");
69
61
  return { type: "title", title: args.join(" ") };
70
- case "ask":
71
- return { type: "ask", text: trimmed.slice(rawCommand.length).trim() };
72
62
  case "queue":
73
63
  case "nosteer": {
74
64
  const prompt = trimmed.slice(rawCommand.length).trim();
@@ -81,8 +71,6 @@ export class CommandRouter {
81
71
  return args[0] ? { type: "switch", sessionId: args[0] } : { type: "switch" };
82
72
  case "agent":
83
73
  return args[0] ? { type: "agent", agent: args[0] } : { type: "agent" };
84
- case "use":
85
- return requireArg(args[0], "agent name", (agent) => ({ type: "use", agent, cwd: args[1] }));
86
74
  case "stop":
87
75
  return { type: "stop" };
88
76
  case "status":
@@ -102,22 +90,17 @@ export class CommandRouter {
102
90
  return { type: "goal", action: "set", objective: args.join(" ") };
103
91
  }
104
92
  case "restart":
105
- return { type: "restart" };
106
- case "modes":
107
- return { type: "modes" };
108
- case "mode":
109
- return requireArg(args[0], "mode value", (value) => ({ type: "mode", value }));
93
+ if (args.length === 0) return { type: "restart" };
94
+ if (args.length === 1 && args[0] === "--force") return { type: "restart", force: true };
95
+ throw new Error("/restart 只接受一个可选的 --force 参数。");
110
96
  case "model":
111
- return { type: "model", model: args[0] };
97
+ return settingsCommand("/model", args, { type: "model" });
98
+ case "provider":
99
+ return settingsCommand("/provider", args, { type: "provider" });
112
100
  case "thinking":
113
- return { type: "thinking", effort: args[0] };
114
- case "permissions": {
115
- const mode = args[0];
116
- if (mode !== undefined && mode !== "auto" && mode !== "confirm") {
117
- throw new Error("权限模式只能是 auto 或 confirm。");
118
- }
119
- return { type: "permissions", mode };
120
- }
101
+ return settingsCommand("/thinking", args, { type: "thinking" });
102
+ case "permissions":
103
+ return settingsCommand("/permissions", args, { type: "permissions" });
121
104
  case "help":
122
105
  return { type: "help" };
123
106
  }
@@ -127,6 +110,7 @@ export class CommandRouter {
127
110
  function resolveCommandName(rawCommand: string): CommandName {
128
111
  const input = rawCommand.slice(1).toLowerCase();
129
112
  if (!input) throw unknownCommand(rawCommand);
113
+ if (DISABLED_COMMAND_NAMES.has(input)) throw unknownCommand(rawCommand);
130
114
 
131
115
  const exact = COMMAND_NAMES.find((command) => command === input);
132
116
  if (exact) return exact;
@@ -147,23 +131,47 @@ function unknownCommand(rawCommand: string): Error {
147
131
  return new Error(`未知命令:${rawCommand}。发送 /help 查看可用命令。`);
148
132
  }
149
133
 
150
- function requireArg<T>(value: string | undefined, name: string, create: (value: string) => T): T {
151
- if (!value) {
152
- throw new Error(`Missing ${name}.`);
134
+ function settingsCommand<T extends Command>(name: string, args: string[], command: T): T {
135
+ if (args.length > 0) {
136
+ throw new Error(`${name} 不接受参数,请在设置卡片中完成选择。`);
153
137
  }
154
-
155
- return create(value);
138
+ return command;
156
139
  }
157
140
 
158
141
  function parseNewCommand(args: string[]): Extract<Command, { type: "new" }> {
142
+ const options = parseNewTaskOptions("/new", args, "工作目录", "D:\\dev\\project");
143
+ return {
144
+ type: "new",
145
+ title: options.title,
146
+ cwd: options.cwd,
147
+ ...(options.projectless ? { projectless: true } : {}),
148
+ };
149
+ }
150
+
151
+ function parseNewGroupCommand(args: string[]): Extract<Command, { type: "newgroup" }> {
152
+ const options = parseNewTaskOptions("/newgroup", args, "项目目录", "~/dev/project");
153
+ return {
154
+ type: "newgroup",
155
+ title: options.title,
156
+ ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
157
+ ...(options.projectless ? { projectless: true } : {}),
158
+ };
159
+ }
160
+
161
+ function parseNewTaskOptions(
162
+ commandName: "/new" | "/newgroup",
163
+ args: string[],
164
+ directoryLabel: string,
165
+ exampleDirectory: string,
166
+ ): { title?: string; cwd?: string; projectless: boolean } {
159
167
  const titleParts: string[] = [];
160
168
  let cwd: string | undefined;
161
169
  let projectless = false;
162
170
  for (let index = 0; index < args.length; index += 1) {
163
171
  const argument = args[index]!;
164
172
  if (argument === "--nodir") {
165
- if (projectless) throw new Error("/new 只能指定一次 --nodir。");
166
- if (cwd !== undefined) throw new Error("/new 的 --dir 和 --nodir 不能同时使用。");
173
+ if (projectless) throw new Error(`${commandName} 只能指定一次 --nodir。`);
174
+ if (cwd !== undefined) throw new Error(`${commandName} 的 --dir 和 --nodir 不能同时使用。`);
167
175
  projectless = true;
168
176
  continue;
169
177
  }
@@ -171,33 +179,47 @@ function parseNewCommand(args: string[]): Extract<Command, { type: "new" }> {
171
179
  titleParts.push(argument);
172
180
  continue;
173
181
  }
174
- if (projectless) throw new Error("/new 的 --dir 和 --nodir 不能同时使用。");
175
- if (cwd !== undefined) throw new Error("/new 只能指定一次 --dir。");
182
+ if (projectless) throw new Error(`${commandName} 的 --dir 和 --nodir 不能同时使用。`);
183
+ if (cwd !== undefined) throw new Error(`${commandName} 只能指定一次 --dir。`);
176
184
  const directory = args[index + 1];
177
- if (!directory || directory === "--dir") {
178
- throw new Error("请在 --dir 后指定工作目录,例如:/new 修复会话列表 --dir D:\\dev\\project。");
185
+ if (!directory || directory === "--dir" || directory === "--nodir") {
186
+ throw new Error(
187
+ `请在 --dir 后指定${directoryLabel},例如:${commandName} 修复会话列表 --dir ${exampleDirectory}。`,
188
+ );
179
189
  }
180
190
  cwd = directory;
181
191
  index += 1;
182
192
  }
183
193
  return {
184
- type: "new",
185
194
  title: titleParts.join(" ").trim() || undefined,
186
195
  cwd,
187
- ...(projectless ? { projectless: true } : {}),
196
+ projectless,
188
197
  };
189
198
  }
190
199
 
191
- function parseNewGroupCommand(args: string[]): Extract<Command, { type: "newgroup" }> {
192
- if (args.includes("--dir")) {
193
- throw new Error("/newgroup 只创建飞书群,不支持 --dir;请在新群中使用 /new --dir <cwd> 创建任务。");
194
- }
200
+ function parseForkCommand(args: string[]): Extract<Command, { type: "fork" }> {
201
+ rejectOptions("/fork", args);
202
+ if (args.length > 1) throw new Error("/fork 只接受一个可选的任务序号或任务 ID。");
195
203
  return {
196
- type: "newgroup",
204
+ type: "fork",
205
+ ...(args[0] ? { sessionId: args[0] } : {}),
206
+ };
207
+ }
208
+
209
+ function parseForkGroupCommand(args: string[]): Extract<Command, { type: "forkgroup" }> {
210
+ rejectOptions("/forkgroup", args);
211
+ return {
212
+ type: "forkgroup",
197
213
  title: args.join(" ").trim() || undefined,
198
214
  };
199
215
  }
200
216
 
217
+ function rejectOptions(command: string, args: string[]): void {
218
+ for (const argument of args) {
219
+ if (argument.startsWith("--")) throw new Error(`${command} 不支持参数:${argument}。`);
220
+ }
221
+ }
222
+
201
223
  function splitArgs(input: string): string[] {
202
224
  const args: string[] = [];
203
225
  let current = "";
@@ -1,28 +1,23 @@
1
- import type { PermissionMode } from "../runtime/types.js";
2
-
3
1
  export type Command =
4
2
  | { type: "shell"; command: string }
5
3
  | { type: "new"; title?: string; cwd?: string; projectless?: boolean }
6
- | { type: "newgroup"; title?: string }
4
+ | { type: "newgroup"; title?: string; cwd?: string; projectless?: boolean }
7
5
  | { type: "forkgroup"; title?: string }
8
6
  | { type: "fork"; sessionId?: string }
9
7
  | { type: "title"; title: string }
10
- | { type: "ask"; text: string }
11
8
  | { type: "nosteer"; text: string }
12
9
  | { type: "sessions"; searchTerm?: string }
13
10
  | { type: "switch"; sessionId?: string }
14
11
  | { type: "agent"; agent?: string }
15
- | { type: "use"; agent: string; cwd?: string }
16
12
  | { type: "stop" }
17
13
  | { type: "status"; sessionId?: string }
18
14
  | { type: "goal"; action: "show" }
19
15
  | { type: "goal"; action: "set" | "edit"; objective: string }
20
16
  | { type: "goal"; action: "pause" | "resume" | "clear" }
21
- | { type: "restart" }
22
- | { type: "modes" }
23
- | { type: "mode"; value: string }
24
- | { type: "model"; model?: string }
25
- | { type: "thinking"; effort?: string }
26
- | { type: "permissions"; mode?: PermissionMode }
17
+ | { type: "restart"; force?: boolean }
18
+ | { type: "model" }
19
+ | { type: "provider" }
20
+ | { type: "thinking" }
21
+ | { type: "permissions" }
27
22
  | { type: "help" }
28
23
  | { type: "prompt"; text: string };
@@ -39,7 +39,7 @@ export class ConsoleTurnPresenter implements TurnPresenter {
39
39
  } else if (event.type === "tool_started") {
40
40
  await this.outbound.sendText(contextKey, `> ${event.tool.title}`);
41
41
  } else if (event.type === "approval_requested") {
42
- await this.outbound.sendText(contextKey, `Approval required: ${event.request.title}. Use Feishu or /permissions auto.`);
42
+ await this.outbound.sendText(contextKey, `Approval required: ${event.request.title}. Use Feishu's Permission settings card.`);
43
43
  } else if (event.type === "turn_completed" && !this.delivered.has(event.turnId)) {
44
44
  this.delivered.add(event.turnId);
45
45
  await this.outbound.sendMarkdown(contextKey, event.finalResponse || "(completed without text)");