glab-agent 0.2.13 → 0.2.14

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glab-agent",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "type": "module",
5
5
  "description": "Multi-agent GitLab To-Do watcher with YAML-defined agents, skills, and GitLab registry.",
6
6
  "license": "MIT",
@@ -36,6 +36,8 @@ export interface AgentNotifications {
36
36
  feishu_app_secret_env?: string;
37
37
  /** Email domain for mapping GitLab username → Feishu email (e.g. "taou.com" → "{username}@taou.com") */
38
38
  feishu_email_domain?: string;
39
+ /** Feishu group chat_id — send updatable cards to team group instead of webhook */
40
+ feishu_group_chat_id?: string;
39
41
  }
40
42
 
41
43
  export interface AgentLabelConfig {
@@ -135,6 +137,7 @@ function parseNotifications(raw: unknown): AgentNotifications {
135
137
  feishu_app_id_env: typeof obj.feishu_app_id_env === "string" ? obj.feishu_app_id_env : undefined,
136
138
  feishu_app_secret_env: typeof obj.feishu_app_secret_env === "string" ? obj.feishu_app_secret_env : undefined,
137
139
  feishu_email_domain: typeof obj.feishu_email_domain === "string" ? obj.feishu_email_domain : undefined,
140
+ feishu_group_chat_id: typeof obj.feishu_group_chat_id === "string" ? obj.feishu_group_chat_id : undefined,
138
141
  };
139
142
  }
140
143
 
@@ -59,6 +59,7 @@ const spawnedRuns = new Map<number, { // keyed by issueIid
59
59
  startedAt: string;
60
60
  feishuMessageId?: string;
61
61
  feishuOpenId?: string;
62
+ feishuGroupMessageId?: string;
62
63
  progressLines?: string[];
63
64
  }>();
64
65
 
@@ -81,6 +82,7 @@ export interface LocalAgentConfig {
81
82
  feishuAppId?: string;
82
83
  feishuAppSecret?: string;
83
84
  feishuEmailDomain?: string;
85
+ feishuGroupChatId?: string;
84
86
  }
85
87
 
86
88
  export interface ConfigResolutionOptions {
@@ -396,10 +398,9 @@ export async function runWatcherCycle(
396
398
  const logger = dependencies.logger ?? console;
397
399
  const state = await dependencies.stateStore.load();
398
400
 
399
- // When Feishu app client is configured, suppress group webhook notifications —
400
- // personal Feishu cards already cover the full lifecycle (accepted/running/completed/failed).
401
- // Mutate a local copy so the original config is untouched.
402
- if (dependencies.feishuClient && config.webhookUrl) {
401
+ // Suppress group webhook when Feishu group card is configured (strict replacement).
402
+ // Personal cards alone don't suppress webhook team still needs group visibility.
403
+ if (config.feishuGroupChatId && config.webhookUrl) {
403
404
  config = { ...config, webhookUrl: undefined };
404
405
  }
405
406
 
@@ -773,8 +774,8 @@ export async function runConcurrentWatcherCycle(
773
774
  const state = await dependencies.stateStore.load();
774
775
  const concurrency = config.agentDefinition?.concurrency ?? 1;
775
776
 
776
- // Suppress group webhook when Feishu personal cards are active
777
- if (dependencies.feishuClient && config.webhookUrl) {
777
+ // Suppress group webhook when Feishu group card is configured (strict replacement)
778
+ if (config.feishuGroupChatId && config.webhookUrl) {
778
779
  config = { ...config, webhookUrl: undefined };
779
780
  }
780
781
 
@@ -806,8 +807,8 @@ export async function runConcurrentWatcherCycle(
806
807
  } catch (e) { logger.warn(`Post-run cleanup failed: ${String(e).slice(0, 100)}`); }
807
808
  }
808
809
  await notifyFailed(config.agentDefinition?.name ?? "agent", issueIid, spawned.issue.title, result.summary, config.webhookUrl);
809
- // Feishu: update card to failed state
810
- if (dependencies.feishuClient && spawned.feishuMessageId) {
810
+ // Feishu: update cards to failed state
811
+ if (dependencies.feishuClient) {
811
812
  const card = buildProgressCard({
812
813
  agentName: config.agentDefinition?.name ?? "agent",
813
814
  issueIid,
@@ -818,7 +819,8 @@ export async function runConcurrentWatcherCycle(
818
819
  summary: result.summary.slice(0, 300),
819
820
  duration: `${durationSec}s`,
820
821
  });
821
- dependencies.feishuClient.updateCard(spawned.feishuMessageId, card).catch(() => {});
822
+ if (spawned.feishuMessageId) dependencies.feishuClient.updateCard(spawned.feishuMessageId, card).catch(() => {});
823
+ if (spawned.feishuGroupMessageId) dependencies.feishuClient.updateCard(spawned.feishuGroupMessageId, card).catch(() => {});
822
824
  }
823
825
  await replaceReactionOnTodo(dependencies.gitlabClient, spawned.todo, "eyes", "x", logger);
824
826
  await finalizeTodo(config, dependencies, state, spawned.todo, spawned.issue, {
@@ -829,8 +831,8 @@ export async function runConcurrentWatcherCycle(
829
831
  logger.info(`Agent completed issue #${issueIid} in ${durationSec}s. Summary: ${result.summary.slice(0, 200)}`);
830
832
  const mr = spawned.issue.projectId ? await dependencies.gitlabClient.findMergeRequestByBranch(spawned.issue.projectId, spawned.worktree.branch).catch(() => undefined) : undefined;
831
833
  await notifyCompleted(config.agentDefinition?.name ?? "agent", issueIid, spawned.issue.title, mr?.webUrl || spawned.issue.webUrl || undefined, config.webhookUrl);
832
- // Feishu: update card to completed state
833
- if (dependencies.feishuClient && spawned.feishuMessageId) {
834
+ // Feishu: update cards to completed state
835
+ if (dependencies.feishuClient) {
834
836
  const card = buildProgressCard({
835
837
  agentName: config.agentDefinition?.name ?? "agent",
836
838
  issueIid,
@@ -842,7 +844,8 @@ export async function runConcurrentWatcherCycle(
842
844
  summary: result.summary.slice(0, 300),
843
845
  duration: `${durationSec}s`,
844
846
  });
845
- dependencies.feishuClient.updateCard(spawned.feishuMessageId, card).catch(() => {});
847
+ if (spawned.feishuMessageId) dependencies.feishuClient.updateCard(spawned.feishuMessageId, card).catch(() => {});
848
+ if (spawned.feishuGroupMessageId) dependencies.feishuClient.updateCard(spawned.feishuGroupMessageId, card).catch(() => {});
846
849
  }
847
850
  await replaceReactionOnTodo(dependencies.gitlabClient, spawned.todo, "eyes", "white_check_mark", logger);
848
851
  await finalizeTodo(config, dependencies, state, spawned.todo, spawned.issue, {
@@ -977,7 +980,27 @@ export async function runConcurrentWatcherCycle(
977
980
  }
978
981
  }
979
982
 
980
- // Set up progress callback for webhook and/or feishu card updates
983
+ // Feishu: send accepted card to group chat
984
+ if (dependencies.feishuClient && config.feishuGroupChatId) {
985
+ try {
986
+ const card = buildProgressCard({
987
+ agentName: config.agentDefinition?.name ?? "agent",
988
+ issueIid: issue.iid,
989
+ issueTitle: issue.title,
990
+ issueUrl: issue.webUrl || undefined,
991
+ status: "accepted",
992
+ });
993
+ const groupMsgId = await dependencies.feishuClient.sendCard(config.feishuGroupChatId, card);
994
+ const entry = spawnedRuns.get(issue.iid);
995
+ if (entry && groupMsgId) {
996
+ entry.feishuGroupMessageId = groupMsgId;
997
+ }
998
+ } catch (e) {
999
+ logger.info?.(`Feishu group card send skipped: ${String(e).slice(0, 100)}`);
1000
+ }
1001
+ }
1002
+
1003
+ // Set up progress callback for feishu card updates
981
1004
  if (config.webhookUrl || dependencies.feishuClient) {
982
1005
  const agentName = config.agentDefinition?.name ?? "agent";
983
1006
  const issueRef = `#${issue.iid}`;
@@ -1036,14 +1059,13 @@ export async function runConcurrentWatcherCycle(
1036
1059
  duration: `${elapsedSec}s`,
1037
1060
  });
1038
1061
  dependencies.feishuClient.updateCard(spawnedEntry.feishuMessageId, card).catch(() => {});
1039
- } else if (config.webhookUrl) {
1040
- // Fallback to webhook when Feishu is not configured
1041
- sendWebhook(config.webhookUrl, {
1042
- title: `\u{1F527} ${agentName} ${issueRef}`,
1043
- message,
1044
- status: "accepted",
1045
- }).catch(() => {});
1062
+ // Also update group card if present
1063
+ if (spawnedEntry.feishuGroupMessageId) {
1064
+ dependencies.feishuClient.updateCard(spawnedEntry.feishuGroupMessageId, card).catch(() => {});
1065
+ }
1046
1066
  }
1067
+ // Webhook does NOT receive progress updates — only lifecycle events
1068
+ // (accepted/completed/failed) via notifyXxx functions. This prevents group flooding.
1047
1069
  };
1048
1070
  }
1049
1071
 
@@ -1753,6 +1775,7 @@ export function loadConfigFromAgentDefinition(
1753
1775
  feishuAppId: def.notifications?.feishu_app_id_env ? env[def.notifications.feishu_app_id_env]?.trim() : undefined,
1754
1776
  feishuAppSecret: def.notifications?.feishu_app_secret_env ? env[def.notifications.feishu_app_secret_env]?.trim() : undefined,
1755
1777
  feishuEmailDomain: def.notifications?.feishu_email_domain,
1778
+ feishuGroupChatId: def.notifications?.feishu_group_chat_id,
1756
1779
  };
1757
1780
  }
1758
1781