@keyou007/agent-bot 0.1.7 → 0.1.9

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 (42) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +6 -0
  3. package/README.zh.md +6 -0
  4. package/config.example.yaml +2 -0
  5. package/dist/cli/FeishuAppConfiguration.js +3 -6
  6. package/dist/cli/FeishuAppConfiguration.js.map +1 -1
  7. package/dist/codex/CodexEventMapper.js +13 -0
  8. package/dist/codex/CodexEventMapper.js.map +1 -1
  9. package/dist/codex/CodexRuntime.js +33 -4
  10. package/dist/codex/CodexRuntime.js.map +1 -1
  11. package/dist/config/loadConfig.js +1 -0
  12. package/dist/config/loadConfig.js.map +1 -1
  13. package/dist/config/schema.js +1 -0
  14. package/dist/config/schema.js.map +1 -1
  15. package/dist/feishu/CardRenderer.js +2 -2
  16. package/dist/feishu/CardRenderer.js.map +1 -1
  17. package/dist/feishu/FeishuBotIdentity.js +35 -0
  18. package/dist/feishu/FeishuBotIdentity.js.map +1 -0
  19. package/dist/feishu/FeishuConnector.js +20 -5
  20. package/dist/feishu/FeishuConnector.js.map +1 -1
  21. package/dist/presentation/TurnStateReducer.js +2 -1
  22. package/dist/presentation/TurnStateReducer.js.map +1 -1
  23. package/dist/proxy/ProxySessionController.js +80 -12
  24. package/dist/proxy/ProxySessionController.js.map +1 -1
  25. package/dist/utils/generatedImageMarkdown.js +11 -0
  26. package/dist/utils/generatedImageMarkdown.js.map +1 -0
  27. package/docs/technical-reference.md +7 -1
  28. package/docs/technical-reference.zh.md +7 -1
  29. package/npm-shrinkwrap.json +2 -2
  30. package/package.json +1 -1
  31. package/skills/agent-bot/SKILL.md +6 -2
  32. package/src/cli/FeishuAppConfiguration.ts +3 -6
  33. package/src/codex/CodexEventMapper.ts +13 -0
  34. package/src/codex/CodexRuntime.ts +48 -5
  35. package/src/config/loadConfig.ts +1 -0
  36. package/src/config/schema.ts +1 -0
  37. package/src/feishu/CardRenderer.ts +2 -2
  38. package/src/feishu/FeishuBotIdentity.ts +55 -0
  39. package/src/feishu/FeishuConnector.ts +24 -4
  40. package/src/presentation/TurnStateReducer.ts +6 -1
  41. package/src/proxy/ProxySessionController.ts +145 -22
  42. package/src/utils/generatedImageMarkdown.ts +10 -0
@@ -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(
@@ -1578,9 +1679,7 @@ export class ProxySessionController {
1578
1679
  );
1579
1680
  await this.outbound.sendText(
1580
1681
  sourceContextKey,
1581
- `已将${
1582
- incomingMessage?.threadContext ? prepared.sourceDescription : "当前任务"
1583
- } Fork 到飞书群:${group.name};新群当前任务为 ${taskDescription}。`,
1682
+ `已将${sourceSummary} Fork 到飞书群:${group.name};新群当前任务为 ${taskDescription}。`,
1584
1683
  );
1585
1684
  }
1586
1685
 
@@ -2277,6 +2376,16 @@ export class ProxySessionController {
2277
2376
  contextKey,
2278
2377
  },
2279
2378
  });
2379
+ actions.push({
2380
+ text: "NewGroup",
2381
+ value: {
2382
+ action: "session_new_group",
2383
+ sessionId: entry.id,
2384
+ ...(searchTerm ? { searchTerm } : {}),
2385
+ visibleCount: String(visibleCount),
2386
+ contextKey,
2387
+ },
2388
+ });
2280
2389
  actions.push({
2281
2390
  text: "Fork",
2282
2391
  value: {
@@ -2287,6 +2396,16 @@ export class ProxySessionController {
2287
2396
  contextKey,
2288
2397
  },
2289
2398
  });
2399
+ actions.push({
2400
+ text: "ForkGroup",
2401
+ value: {
2402
+ action: "session_fork_group",
2403
+ sessionId: entry.id,
2404
+ ...(searchTerm ? { searchTerm } : {}),
2405
+ visibleCount: String(visibleCount),
2406
+ contextKey,
2407
+ },
2408
+ });
2290
2409
  actions.push({
2291
2410
  text: "Status",
2292
2411
  value: {
@@ -2310,6 +2429,7 @@ export class ProxySessionController {
2310
2429
  [
2311
2430
  ...(remoteHint ? [remoteHint] : []),
2312
2431
  "点击 **New** 在对应任务的项目中创建新任务;点击 **Switch** 快速切换;点击 **Fork** 从任务最新已完成轮次创建分支;外部正在运行的任务显示 **Stop**,点击后发送 Interrupt 并变为 **Switch**。",
2432
+ "点击 **NewGroup** 在对应任务的项目中创建新群和新任务;点击 **ForkGroup** 从对应任务最新已完成轮次创建分支群。",
2313
2433
  "也可发送 **/switch [序号或任务 ID]**;不带参数切回上一个任务。外部正在执行的回合不会被接管。",
2314
2434
  "发送 **/fork [序号或任务 ID]**,可从当前或指定任务创建分支;任务运行中时使用最近已完成轮次。",
2315
2435
  "点击 **Status**,或发送 **/status [序号或任务 ID]**,查看当前或指定任务状态。",
@@ -3264,7 +3384,10 @@ function truncateTail(value: string, maxLength: number): string {
3264
3384
  function parseAgentGroupName(value: string): { agentName: string; title: string } | undefined {
3265
3385
  const match = /^\[([^[\]]+)\]\s+(.+)$/.exec(value.trim());
3266
3386
  const agentName = match?.[1]?.trim();
3267
- const title = match?.[2]?.trim();
3387
+ const remainder = match?.[2]?.trim();
3388
+ if (!agentName || !remainder) return undefined;
3389
+ const projectPrefix = /^\[([^[\]]+)\](?:\s+(.+))?$/.exec(remainder);
3390
+ const title = projectPrefix ? projectPrefix[2]?.trim() : remainder;
3268
3391
  return agentName && title ? { agentName, title } : undefined;
3269
3392
  }
3270
3393
 
@@ -0,0 +1,10 @@
1
+ export function appendGeneratedImageMarkdown(finalResponse: string, imagePaths: string[]): string {
2
+ const text = finalResponse.trim();
3
+ const normalizedText = text.replaceAll("\\", "/");
4
+ const images = [...new Set(imagePaths.map((imagePath) => imagePath.trim().replaceAll("\\", "/")).filter(Boolean))]
5
+ .filter((imagePath) => !normalizedText.includes(imagePath));
6
+ if (images.length === 0) return text;
7
+ const imageMarkdown = images.map((imagePath, index) =>
8
+ `![生成图片 ${index + 1}](<${imagePath}>)`).join("\n\n");
9
+ return text ? `${text}\n\n${imageMarkdown}` : imageMarkdown;
10
+ }