glab-agent 0.2.12 → 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.12",
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,6 +398,12 @@ export async function runWatcherCycle(
396
398
  const logger = dependencies.logger ?? console;
397
399
  const state = await dependencies.stateStore.load();
398
400
 
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) {
404
+ config = { ...config, webhookUrl: undefined };
405
+ }
406
+
399
407
  if (state.activeRun && isPidAlive(state.activeRun.pid) && state.activeRun.pid !== pid) {
400
408
  logger.info(`Watcher is busy with issue #${state.activeRun.issueIid}.`);
401
409
  await updateAgentUserStatus(dependencies, "busy", `#${state.activeRun.issueIid}`);
@@ -766,6 +774,11 @@ export async function runConcurrentWatcherCycle(
766
774
  const state = await dependencies.stateStore.load();
767
775
  const concurrency = config.agentDefinition?.concurrency ?? 1;
768
776
 
777
+ // Suppress group webhook when Feishu group card is configured (strict replacement)
778
+ if (config.feishuGroupChatId && config.webhookUrl) {
779
+ config = { ...config, webhookUrl: undefined };
780
+ }
781
+
769
782
  // 1. Collect finished tasks
770
783
  for (const [issueIid, spawned] of spawnedRuns) {
771
784
  const result = await spawned.run.poll();
@@ -794,8 +807,8 @@ export async function runConcurrentWatcherCycle(
794
807
  } catch (e) { logger.warn(`Post-run cleanup failed: ${String(e).slice(0, 100)}`); }
795
808
  }
796
809
  await notifyFailed(config.agentDefinition?.name ?? "agent", issueIid, spawned.issue.title, result.summary, config.webhookUrl);
797
- // Feishu: update card to failed state
798
- if (dependencies.feishuClient && spawned.feishuMessageId) {
810
+ // Feishu: update cards to failed state
811
+ if (dependencies.feishuClient) {
799
812
  const card = buildProgressCard({
800
813
  agentName: config.agentDefinition?.name ?? "agent",
801
814
  issueIid,
@@ -806,7 +819,8 @@ export async function runConcurrentWatcherCycle(
806
819
  summary: result.summary.slice(0, 300),
807
820
  duration: `${durationSec}s`,
808
821
  });
809
- 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(() => {});
810
824
  }
811
825
  await replaceReactionOnTodo(dependencies.gitlabClient, spawned.todo, "eyes", "x", logger);
812
826
  await finalizeTodo(config, dependencies, state, spawned.todo, spawned.issue, {
@@ -817,8 +831,8 @@ export async function runConcurrentWatcherCycle(
817
831
  logger.info(`Agent completed issue #${issueIid} in ${durationSec}s. Summary: ${result.summary.slice(0, 200)}`);
818
832
  const mr = spawned.issue.projectId ? await dependencies.gitlabClient.findMergeRequestByBranch(spawned.issue.projectId, spawned.worktree.branch).catch(() => undefined) : undefined;
819
833
  await notifyCompleted(config.agentDefinition?.name ?? "agent", issueIid, spawned.issue.title, mr?.webUrl || spawned.issue.webUrl || undefined, config.webhookUrl);
820
- // Feishu: update card to completed state
821
- if (dependencies.feishuClient && spawned.feishuMessageId) {
834
+ // Feishu: update cards to completed state
835
+ if (dependencies.feishuClient) {
822
836
  const card = buildProgressCard({
823
837
  agentName: config.agentDefinition?.name ?? "agent",
824
838
  issueIid,
@@ -830,7 +844,8 @@ export async function runConcurrentWatcherCycle(
830
844
  summary: result.summary.slice(0, 300),
831
845
  duration: `${durationSec}s`,
832
846
  });
833
- 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(() => {});
834
849
  }
835
850
  await replaceReactionOnTodo(dependencies.gitlabClient, spawned.todo, "eyes", "white_check_mark", logger);
836
851
  await finalizeTodo(config, dependencies, state, spawned.todo, spawned.issue, {
@@ -965,7 +980,27 @@ export async function runConcurrentWatcherCycle(
965
980
  }
966
981
  }
967
982
 
968
- // 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
969
1004
  if (config.webhookUrl || dependencies.feishuClient) {
970
1005
  const agentName = config.agentDefinition?.name ?? "agent";
971
1006
  const issueRef = `#${issue.iid}`;
@@ -1024,14 +1059,13 @@ export async function runConcurrentWatcherCycle(
1024
1059
  duration: `${elapsedSec}s`,
1025
1060
  });
1026
1061
  dependencies.feishuClient.updateCard(spawnedEntry.feishuMessageId, card).catch(() => {});
1027
- } else if (config.webhookUrl) {
1028
- // Fallback to webhook when Feishu is not configured
1029
- sendWebhook(config.webhookUrl, {
1030
- title: `\u{1F527} ${agentName} ${issueRef}`,
1031
- message,
1032
- status: "accepted",
1033
- }).catch(() => {});
1062
+ // Also update group card if present
1063
+ if (spawnedEntry.feishuGroupMessageId) {
1064
+ dependencies.feishuClient.updateCard(spawnedEntry.feishuGroupMessageId, card).catch(() => {});
1065
+ }
1034
1066
  }
1067
+ // Webhook does NOT receive progress updates — only lifecycle events
1068
+ // (accepted/completed/failed) via notifyXxx functions. This prevents group flooding.
1035
1069
  };
1036
1070
  }
1037
1071
 
@@ -1741,6 +1775,7 @@ export function loadConfigFromAgentDefinition(
1741
1775
  feishuAppId: def.notifications?.feishu_app_id_env ? env[def.notifications.feishu_app_id_env]?.trim() : undefined,
1742
1776
  feishuAppSecret: def.notifications?.feishu_app_secret_env ? env[def.notifications.feishu_app_secret_env]?.trim() : undefined,
1743
1777
  feishuEmailDomain: def.notifications?.feishu_email_domain,
1778
+ feishuGroupChatId: def.notifications?.feishu_group_chat_id,
1744
1779
  };
1745
1780
  }
1746
1781