@keyou007/agent-bot 0.1.8 → 0.1.10

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 (65) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +9 -1
  3. package/README.zh.md +9 -1
  4. package/dist/cli/FeishuAppConfiguration.js +87 -25
  5. package/dist/cli/FeishuAppConfiguration.js.map +1 -1
  6. package/dist/cli/FeishuAppRegistration.js +12 -9
  7. package/dist/cli/FeishuAppRegistration.js.map +1 -1
  8. package/dist/cli/Initializer.js +7 -6
  9. package/dist/cli/Initializer.js.map +1 -1
  10. package/dist/cli/LocalControlClient.js +3 -2
  11. package/dist/cli/LocalControlClient.js.map +1 -1
  12. package/dist/cli/LocalControlServer.js +2 -1
  13. package/dist/cli/LocalControlServer.js.map +1 -1
  14. package/dist/cli/OptionalAuthorizationSkip.js +27 -6
  15. package/dist/cli/OptionalAuthorizationSkip.js.map +1 -1
  16. package/dist/cli/ServerStarter.js +12 -3
  17. package/dist/cli/ServerStarter.js.map +1 -1
  18. package/dist/cli/SkillRegistry.js +5 -4
  19. package/dist/cli/SkillRegistry.js.map +1 -1
  20. package/dist/cli/VerificationOutput.js +2 -1
  21. package/dist/cli/VerificationOutput.js.map +1 -1
  22. package/dist/cli/help.js +48 -1
  23. package/dist/cli/help.js.map +1 -1
  24. package/dist/cli/i18n.js +27 -0
  25. package/dist/cli/i18n.js.map +1 -0
  26. package/dist/cli/initOptions.js +5 -4
  27. package/dist/cli/initOptions.js.map +1 -1
  28. package/dist/cli/packageVersion.js +2 -1
  29. package/dist/cli/packageVersion.js.map +1 -1
  30. package/dist/cli/profile.js +4 -3
  31. package/dist/cli/profile.js.map +1 -1
  32. package/dist/cli/serverStatus.js +15 -14
  33. package/dist/cli/serverStatus.js.map +1 -1
  34. package/dist/cli/taskChatRoute.js +3 -2
  35. package/dist/cli/taskChatRoute.js.map +1 -1
  36. package/dist/cli.js +165 -107
  37. package/dist/cli.js.map +1 -1
  38. package/dist/commands/CommandRouter.js +49 -3
  39. package/dist/commands/CommandRouter.js.map +1 -1
  40. package/dist/proxy/ProxySessionController.js +81 -11
  41. package/dist/proxy/ProxySessionController.js.map +1 -1
  42. package/docs/technical-reference.md +16 -3
  43. package/docs/technical-reference.zh.md +16 -3
  44. package/npm-shrinkwrap.json +2 -2
  45. package/package.json +1 -1
  46. package/skills/agent-bot/SKILL.md +7 -7
  47. package/src/cli/FeishuAppConfiguration.ts +133 -21
  48. package/src/cli/FeishuAppRegistration.ts +24 -9
  49. package/src/cli/Initializer.ts +25 -6
  50. package/src/cli/LocalControlClient.ts +9 -2
  51. package/src/cli/LocalControlServer.ts +2 -1
  52. package/src/cli/OptionalAuthorizationSkip.ts +51 -6
  53. package/src/cli/ServerStarter.ts +20 -3
  54. package/src/cli/SkillRegistry.ts +17 -4
  55. package/src/cli/VerificationOutput.ts +4 -1
  56. package/src/cli/help.ts +50 -1
  57. package/src/cli/i18n.ts +38 -0
  58. package/src/cli/initOptions.ts +17 -4
  59. package/src/cli/packageVersion.ts +5 -1
  60. package/src/cli/profile.ts +13 -3
  61. package/src/cli/serverStatus.ts +19 -14
  62. package/src/cli/taskChatRoute.ts +9 -2
  63. package/src/cli.ts +290 -103
  64. package/src/commands/CommandRouter.ts +58 -3
  65. package/src/proxy/ProxySessionController.ts +147 -21
@@ -1,5 +1,39 @@
1
1
  import type { Command } from "./commandTypes.js";
2
2
 
3
+ const COMMAND_NAMES = [
4
+ "agent",
5
+ "agents",
6
+ "ask",
7
+ "fork",
8
+ "forkgroup",
9
+ "goal",
10
+ "help",
11
+ "mode",
12
+ "model",
13
+ "modes",
14
+ "new",
15
+ "newgroup",
16
+ "nosteer",
17
+ "permissions",
18
+ "queue",
19
+ "restart",
20
+ "sessions",
21
+ "status",
22
+ "stop",
23
+ "switch",
24
+ "thinking",
25
+ "title",
26
+ "use",
27
+ ] as const;
28
+
29
+ type CommandName = (typeof COMMAND_NAMES)[number];
30
+
31
+ const COMMAND_INITIALISMS: Partial<Record<CommandName, string>> = {
32
+ forkgroup: "fg",
33
+ newgroup: "ng",
34
+ nosteer: "ns",
35
+ };
36
+
3
37
  export class CommandRouter {
4
38
  parse(text: string): Command {
5
39
  const trimmed = text.trim();
@@ -13,7 +47,7 @@ export class CommandRouter {
13
47
  }
14
48
 
15
49
  const [rawCommand, ...args] = splitArgs(trimmed);
16
- const command = rawCommand.slice(1).toLowerCase();
50
+ const command = resolveCommandName(rawCommand);
17
51
 
18
52
  switch (command) {
19
53
  case "agents":
@@ -86,12 +120,33 @@ export class CommandRouter {
86
120
  }
87
121
  case "help":
88
122
  return { type: "help" };
89
- default:
90
- throw new Error(`未知命令:${rawCommand}。发送 /help 查看可用命令。`);
91
123
  }
92
124
  }
93
125
  }
94
126
 
127
+ function resolveCommandName(rawCommand: string): CommandName {
128
+ const input = rawCommand.slice(1).toLowerCase();
129
+ if (!input) throw unknownCommand(rawCommand);
130
+
131
+ const exact = COMMAND_NAMES.find((command) => command === input);
132
+ if (exact) return exact;
133
+
134
+ const matches = COMMAND_NAMES.filter(
135
+ (command) => command.startsWith(input) || COMMAND_INITIALISMS[command] === input,
136
+ );
137
+ if (matches.length === 1) return matches[0]!;
138
+ if (matches.length > 1) {
139
+ throw new Error(
140
+ `命令前缀 ${rawCommand} 不唯一,可匹配:${matches.map((command) => `/${command}`).join("、")}。请输入更长的命令前缀。`,
141
+ );
142
+ }
143
+ throw unknownCommand(rawCommand);
144
+ }
145
+
146
+ function unknownCommand(rawCommand: string): Error {
147
+ return new Error(`未知命令:${rawCommand}。发送 /help 查看可用命令。`);
148
+ }
149
+
95
150
  function requireArg<T>(value: string | undefined, name: string, create: (value: string) => T): T {
96
151
  if (!value) {
97
152
  throw new Error(`Missing ${name}.`);
@@ -82,6 +82,14 @@ interface SessionExecutionSettings {
82
82
  permissionMode?: PermissionMode;
83
83
  }
84
84
 
85
+ interface ProjectSessionReference {
86
+ source?: SessionRecord;
87
+ agentName: string;
88
+ remoteSessionId: string;
89
+ cwd: string;
90
+ executionSettings: SessionExecutionSettings;
91
+ }
92
+
85
93
  interface ForkSessionPlan {
86
94
  source?: SessionRecord;
87
95
  sourceLabel: string;
@@ -349,10 +357,18 @@ export class ProxySessionController {
349
357
  const sessionId = String(scopedAction.value.sessionId ?? "");
350
358
  await this.forkSessionReference(contextKey, sessionId);
351
359
  await this.refreshSessionsCardFromAction(scopedAction);
360
+ } else if (kind === "session_fork_group") {
361
+ const sessionId = String(scopedAction.value.sessionId ?? "");
362
+ await this.forkSessionReferenceToFeishuGroup(contextKey, sessionId, scopedAction.userId);
363
+ await this.refreshSessionsCardFromAction(scopedAction);
352
364
  } else if (kind === "session_new") {
353
365
  const sessionId = String(scopedAction.value.sessionId ?? "");
354
366
  await this.createProjectSessionFromReference(contextKey, sessionId);
355
367
  await this.refreshSessionsCardFromAction(scopedAction);
368
+ } else if (kind === "session_new_group") {
369
+ const sessionId = String(scopedAction.value.sessionId ?? "");
370
+ await this.createFeishuGroupFromReference(contextKey, sessionId, scopedAction.userId);
371
+ await this.refreshSessionsCardFromAction(scopedAction);
356
372
  } else if (kind === "session_stop") {
357
373
  const sessionId = String(scopedAction.value.sessionId ?? "");
358
374
  await this.stopSessionReference(contextKey, sessionId);
@@ -1301,6 +1317,32 @@ export class ProxySessionController {
1301
1317
  }
1302
1318
 
1303
1319
  private async createProjectSessionFromReference(contextKey: string, reference: string): Promise<void> {
1320
+ const resolved = await this.resolveProjectSessionReference(contextKey, reference);
1321
+ const created = await this.createSession(
1322
+ contextKey,
1323
+ resolved.agentName,
1324
+ resolved.cwd,
1325
+ true,
1326
+ false,
1327
+ undefined,
1328
+ undefined,
1329
+ undefined,
1330
+ resolved.executionSettings,
1331
+ );
1332
+ this.store.audit(contextKey, "project_session_created", {
1333
+ sourceLocalSessionId: resolved.source?.localSessionId,
1334
+ sourceRemoteSessionId: resolved.remoteSessionId,
1335
+ createdLocalSessionId: created.localSessionId,
1336
+ createdRemoteSessionId: created.remoteSessionId,
1337
+ cwd: resolved.cwd,
1338
+ ...resolved.executionSettings,
1339
+ });
1340
+ }
1341
+
1342
+ private async resolveProjectSessionReference(
1343
+ contextKey: string,
1344
+ reference: string,
1345
+ ): Promise<ProjectSessionReference> {
1304
1346
  const taskId = this.resolveSessionReference(contextKey, reference);
1305
1347
  const direct = this.store.getSession(taskId);
1306
1348
  if (direct?.status === "closed") {
@@ -1346,25 +1388,13 @@ export class ProxySessionController {
1346
1388
  reasoningEffort: remote?.reasoningEffort ?? source?.reasoningEffort,
1347
1389
  permissionMode: remote?.permissionMode ?? source?.permissionMode ?? "auto",
1348
1390
  };
1349
- const created = await this.createSession(
1350
- contextKey,
1391
+ return {
1392
+ source,
1351
1393
  agentName,
1394
+ remoteSessionId,
1352
1395
  cwd,
1353
- true,
1354
- false,
1355
- undefined,
1356
- undefined,
1357
- undefined,
1358
1396
  executionSettings,
1359
- );
1360
- this.store.audit(contextKey, "project_session_created", {
1361
- sourceLocalSessionId: source?.localSessionId,
1362
- sourceRemoteSessionId: remoteSessionId,
1363
- createdLocalSessionId: created.localSessionId,
1364
- createdRemoteSessionId: created.remoteSessionId,
1365
- cwd,
1366
- ...executionSettings,
1367
- });
1397
+ };
1368
1398
  }
1369
1399
 
1370
1400
  private async startTurn(
@@ -1473,8 +1503,6 @@ export class ProxySessionController {
1473
1503
  ): Promise<void> {
1474
1504
  const source = this.currentSession(sourceContextKey);
1475
1505
  const boundProjectCwd = this.currentProjectCwd(sourceContextKey);
1476
- const explicitTitle = normalizeTaskTitle(requestedTitle);
1477
- const taskTitle = explicitTitle ?? "新任务";
1478
1506
  const executionSettings: SessionExecutionSettings = source?.agentName === agentName
1479
1507
  ? {
1480
1508
  model: source.model,
@@ -1482,6 +1510,43 @@ export class ProxySessionController {
1482
1510
  permissionMode: source.permissionMode,
1483
1511
  }
1484
1512
  : {};
1513
+ await this.createFeishuGroupWithTask(
1514
+ sourceContextKey,
1515
+ agentName,
1516
+ requestedTitle,
1517
+ userId,
1518
+ boundProjectCwd,
1519
+ executionSettings,
1520
+ );
1521
+ }
1522
+
1523
+ private async createFeishuGroupFromReference(
1524
+ sourceContextKey: string,
1525
+ reference: string,
1526
+ userId: string | undefined,
1527
+ ): Promise<void> {
1528
+ const resolved = await this.resolveProjectSessionReference(sourceContextKey, reference);
1529
+ const boundProjectCwd = detectProjectlessWorkspace(resolved.cwd) ? undefined : resolved.cwd;
1530
+ await this.createFeishuGroupWithTask(
1531
+ sourceContextKey,
1532
+ resolved.agentName,
1533
+ undefined,
1534
+ userId,
1535
+ boundProjectCwd,
1536
+ resolved.executionSettings,
1537
+ );
1538
+ }
1539
+
1540
+ private async createFeishuGroupWithTask(
1541
+ sourceContextKey: string,
1542
+ agentName: string,
1543
+ requestedTitle: string | undefined,
1544
+ userId: string | undefined,
1545
+ boundProjectCwd: string | undefined,
1546
+ executionSettings: SessionExecutionSettings,
1547
+ ): Promise<void> {
1548
+ const explicitTitle = normalizeTaskTitle(requestedTitle);
1549
+ const taskTitle = explicitTitle ?? "新任务";
1485
1550
  const group = await this.createFeishuGroupContext(
1486
1551
  sourceContextKey,
1487
1552
  agentName,
@@ -1542,6 +1607,42 @@ export class ProxySessionController {
1542
1607
  throw new Error("/forkgroup 只能由具有 open_id 的飞书用户消息触发。");
1543
1608
  }
1544
1609
  const prepared = await this.prepareForkGroupSession(sourceContextKey, requestedTitle, incomingMessage);
1610
+ await this.forkPreparedSessionToFeishuGroup(
1611
+ sourceContextKey,
1612
+ prepared,
1613
+ userId,
1614
+ incomingMessage?.threadContext ? prepared.sourceDescription : "当前任务",
1615
+ );
1616
+ }
1617
+
1618
+ private async forkSessionReferenceToFeishuGroup(
1619
+ sourceContextKey: string,
1620
+ reference: string,
1621
+ userId: string | undefined,
1622
+ ): Promise<void> {
1623
+ if (!userId?.startsWith("ou_")) {
1624
+ throw new Error("ForkGroup 只能由具有 open_id 的飞书用户触发。");
1625
+ }
1626
+ const plan = await this.prepareForkSession(sourceContextKey, reference);
1627
+ await this.forkPreparedSessionToFeishuGroup(
1628
+ sourceContextKey,
1629
+ {
1630
+ plan,
1631
+ sourceDescription: plan.forkedFromHistoricalTurn
1632
+ ? "指定任务最近已完成轮次"
1633
+ : "指定任务最新轮次",
1634
+ },
1635
+ userId,
1636
+ "指定任务",
1637
+ );
1638
+ }
1639
+
1640
+ private async forkPreparedSessionToFeishuGroup(
1641
+ sourceContextKey: string,
1642
+ prepared: ForkGroupSessionPlan,
1643
+ userId: string,
1644
+ sourceSummary: string,
1645
+ ): Promise<void> {
1545
1646
  const { plan } = prepared;
1546
1647
  const boundProjectCwd = detectProjectlessWorkspace(plan.cwd) ? undefined : plan.cwd;
1547
1648
  const group = await this.createFeishuGroupContext(
@@ -1574,13 +1675,14 @@ export class ProxySessionController {
1574
1675
  `已从${prepared.sourceDescription}创建分支。`,
1575
1676
  `当前任务:${taskDescription}`,
1576
1677
  `当前 Project 目录:${boundProjectCwd ?? "未绑定(Projectless)"}`,
1678
+ `当前模型:${forked.session.model ?? "默认"}`,
1679
+ `思考强度:${forked.session.reasoningEffort ?? "自动"}`,
1680
+ `权限类型:${forked.session.permissionMode === "confirm" ? "执行前确认" : "自动执行"}`,
1577
1681
  ].join("\n"),
1578
1682
  );
1579
1683
  await this.outbound.sendText(
1580
1684
  sourceContextKey,
1581
- `已将${
1582
- incomingMessage?.threadContext ? prepared.sourceDescription : "当前任务"
1583
- } Fork 到飞书群:${group.name};新群当前任务为 ${taskDescription}。`,
1685
+ `已将${sourceSummary} Fork 到飞书群:${group.name};新群当前任务为 ${taskDescription}。`,
1584
1686
  );
1585
1687
  }
1586
1688
 
@@ -2277,6 +2379,16 @@ export class ProxySessionController {
2277
2379
  contextKey,
2278
2380
  },
2279
2381
  });
2382
+ actions.push({
2383
+ text: "NewGroup",
2384
+ value: {
2385
+ action: "session_new_group",
2386
+ sessionId: entry.id,
2387
+ ...(searchTerm ? { searchTerm } : {}),
2388
+ visibleCount: String(visibleCount),
2389
+ contextKey,
2390
+ },
2391
+ });
2280
2392
  actions.push({
2281
2393
  text: "Fork",
2282
2394
  value: {
@@ -2287,6 +2399,16 @@ export class ProxySessionController {
2287
2399
  contextKey,
2288
2400
  },
2289
2401
  });
2402
+ actions.push({
2403
+ text: "ForkGroup",
2404
+ value: {
2405
+ action: "session_fork_group",
2406
+ sessionId: entry.id,
2407
+ ...(searchTerm ? { searchTerm } : {}),
2408
+ visibleCount: String(visibleCount),
2409
+ contextKey,
2410
+ },
2411
+ });
2290
2412
  actions.push({
2291
2413
  text: "Status",
2292
2414
  value: {
@@ -2310,6 +2432,7 @@ export class ProxySessionController {
2310
2432
  [
2311
2433
  ...(remoteHint ? [remoteHint] : []),
2312
2434
  "点击 **New** 在对应任务的项目中创建新任务;点击 **Switch** 快速切换;点击 **Fork** 从任务最新已完成轮次创建分支;外部正在运行的任务显示 **Stop**,点击后发送 Interrupt 并变为 **Switch**。",
2435
+ "点击 **NewGroup** 在对应任务的项目中创建新群和新任务;点击 **ForkGroup** 从对应任务最新已完成轮次创建分支群。",
2313
2436
  "也可发送 **/switch [序号或任务 ID]**;不带参数切回上一个任务。外部正在执行的回合不会被接管。",
2314
2437
  "发送 **/fork [序号或任务 ID]**,可从当前或指定任务创建分支;任务运行中时使用最近已完成轮次。",
2315
2438
  "点击 **Status**,或发送 **/status [序号或任务 ID]**,查看当前或指定任务状态。",
@@ -2707,6 +2830,9 @@ export class ProxySessionController {
2707
2830
  {
2708
2831
  lines: [
2709
2832
  "直接发送消息即可继续当前任务;执行中发送的新消息会追加到本次任务。",
2833
+ "> 命令前缀示例:/sess 等同于 /sessions。",
2834
+ "> 命令缩写示例:/fg 等同于 /forkgroup。",
2835
+ "前缀或缩写匹配多个命令时,需要输入更长的形式。",
2710
2836
  "**[参数]** 可选 **&#60;参数&#62;** 必填",
2711
2837
  ],
2712
2838
  },