fix(feishu): resolve correct accountId for subagent group replies#58228
fix(feishu): resolve correct accountId for subagent group replies#58228EronFan wants to merge 3 commits into
Conversation
… flag Before writing autoLastSuccessVersion, parse afterVersion from the JSON stdout of 'openclaw update --json' and verify it matches the resolved version AND differs from the current running VERSION. This prevents a crash loop where: 1. npm install exits 0 but binary is not actually updated 2. autoLastSuccessVersion is written anyway 3. Gateway restarts, plugin version checks fail due to minGatewayVersion mismatch, all plugins skip loading fix openclaw#58041
Fixes openclaw#58107 by ensuring subagents use their own accountId when sending Feishu group messages. Previously, subagents would use the default accountId ('default') instead of their assigned accountId, causing messages to be sent from the wrong account. The fix prioritizes agentAccountId from the tool execution context (available for subagents) over the channel's accountId.
|
Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 74c9cca7e4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const { monitorFeishuProvider } = await import("./monitor.js"); | ||
| const account = resolveFeishuRuntimeAccount( | ||
| { cfg: ctx.cfg, accountId: ctx.accountId }, | ||
| { cfg: ctx.cfg, accountId: effectiveAccountId }, |
There was a problem hiding this comment.
Restore ctx.accountId usage in Feishu gateway startup
gateway.startAccount now references effectiveAccountId, but that identifier is only declared inside actions.handleAction and is out of scope here. When Feishu startup reaches this line, it will throw a ReferenceError and prevent the Feishu account monitor from starting. This regresses all Feishu runtime startup paths, not just subagent message actions.
Useful? React with 👍 / 👎.
|
|
||
| // Parse version from output (format: "openclaw 2026.3.28") | ||
| const versionOutput = versionCheckStep.stdoutTail || ""; | ||
| const versionMatch = versionOutput.match(/openclaw\s+(\d+\.\d+\.\d+)/); |
There was a problem hiding this comment.
Parse version output using the actual CLI version format
The version check parses --version output with a case-sensitive pattern openclaw\s+(\d+\.\d+\.\d+), but the CLI emits OpenClaw <version> (see src/cli/program/help.ts), and may include prerelease/commit suffixes. In those normal cases versionMatch is null, so git updates fail with version-parse-failed before running doctor, even when the updated checkout is healthy.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR bundles three concerns: (1) the stated Feishu fix for subagent The Feishu fix itself ( The auto-update changes in Key issues:
Confidence Score: 2/5Not safe to merge: two P1 correctness bugs (scope error in startAccount, wrong JSON key for afterVersion) and a committed backup file need to be resolved first. Three P1 issues remain: the
|
| Filename | Overview |
|---|---|
| extensions/feishu/src/channel.ts | Core Feishu fix: effectiveAccountId correctly introduced in handleAction but mistakenly referenced in sibling startAccount where it is out of scope (compile error / runtime ReferenceError). Also uses (ctx as any) for the new agentAccountId field. |
| extensions/feishu/src/channel.ts.backup | Development artefact (mid-edit snapshot of channel.ts) accidentally committed. Should be deleted before merge. |
| src/infra/update-runner.ts | Unrelated to the PR's stated Feishu fix: adds a pre-flight binary/package-version consistency check before running doctor --fix during git-mode updates, guarding against issue #58041. Logic looks correct in isolation. |
| src/infra/update-startup.ts | Unrelated to the Feishu fix; tightens the auto-update success condition but parses the version from the wrong JSON key (parsed.afterVersion instead of parsed.after.version), meaning autoLastSuccessVersion is never recorded and updates are retried indefinitely. |
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts
Line: 1140-1152
Comment:
**`effectiveAccountId` is out of scope here — will cause a `ReferenceError` at runtime**
`effectiveAccountId` is declared with `const` inside the `handleAction` callback at line 621. The `startAccount` callback (here) is a sibling property of a different sub-object (`gateway`), not nested inside `handleAction`, so it cannot close over that variable. TypeScript should flag this as a compile error and, if somehow bypassed, calling `startAccount` will throw `ReferenceError: effectiveAccountId is not defined`.
Each usage on lines 1140, 1144, 1146, and 1152 needs to resolve the effective account directly within this function:
```suggestion
const account = resolveFeishuRuntimeAccount(
{ cfg: ctx.cfg, accountId: (ctx as any).agentAccountId ?? ctx.accountId },
{ requireEventSecrets: true },
);
const port = account.config?.webhookPort ?? null;
const startEffectiveAccountId = (ctx as any).agentAccountId ?? ctx.accountId;
ctx.setStatus({ accountId: startEffectiveAccountId, port });
ctx.log?.info(
`starting feishu[${startEffectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
);
return monitorFeishuProvider({
config: ctx.cfg,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
accountId: startEffectiveAccountId,
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/infra/update-startup.ts
Line: 465-466
Comment:
**Wrong JSON field path — `afterVersion` will always be `null`**
The update command outputs a JSON object where the version is nested at `after.version` (e.g. `{ "after": { "version": "2026.3.28" } }`), not at a top-level `afterVersion` key. As a result `parsed.afterVersion` is always `undefined`, `afterVersion` is always `null`, the `if (outcome.ok && afterVersion != null …)` branch is never taken, and `autoLastSuccessVersion` is never written. Every successful auto-update will be logged as "auto-update skipped (version mismatch or unchanged)" and the system will keep retrying on every subsequent check.
The field access should be:
```suggestion
if (parsed && parsed.after && typeof parsed.after.version === "string") {
return parsed.after.version;
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts.backup
Line: 1
Comment:
**Backup file should not be committed to the repository**
`channel.ts.backup` is a development artefact (a mid-edit snapshot of `channel.ts`) and has no place in version control. It adds ~1,190 lines of near-duplicate code that will cause confusion and will never be kept up to date. Please remove this file before merging.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts
Line: 621
Comment:
**`(ctx as any)` bypasses TypeScript's type safety**
Accessing `agentAccountId` via `(ctx as any)` sidesteps type checking and makes it invisible to refactoring tools. If `agentAccountId` is a legitimate field that subagent contexts are expected to carry, it should be added to the relevant context type (or a subtype/intersection) so that access is type-safe and the intent is self-documenting.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "chore: update runner file" | Re-trigger Greptile
| { cfg: ctx.cfg, accountId: effectiveAccountId }, | ||
| { requireEventSecrets: true }, | ||
| ); | ||
| const port = account.config?.webhookPort ?? null; | ||
| ctx.setStatus({ accountId: ctx.accountId, port }); | ||
| ctx.setStatus({ accountId: effectiveAccountId, port }); | ||
| ctx.log?.info( | ||
| `starting feishu[${ctx.accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`, | ||
| `starting feishu[${effectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`, | ||
| ); | ||
| return monitorFeishuProvider({ | ||
| config: ctx.cfg, | ||
| runtime: ctx.runtime, | ||
| abortSignal: ctx.abortSignal, | ||
| accountId: ctx.accountId, | ||
| accountId: effectiveAccountId, |
There was a problem hiding this comment.
effectiveAccountId is out of scope here — will cause a ReferenceError at runtime
effectiveAccountId is declared with const inside the handleAction callback at line 621. The startAccount callback (here) is a sibling property of a different sub-object (gateway), not nested inside handleAction, so it cannot close over that variable. TypeScript should flag this as a compile error and, if somehow bypassed, calling startAccount will throw ReferenceError: effectiveAccountId is not defined.
Each usage on lines 1140, 1144, 1146, and 1152 needs to resolve the effective account directly within this function:
| { cfg: ctx.cfg, accountId: effectiveAccountId }, | |
| { requireEventSecrets: true }, | |
| ); | |
| const port = account.config?.webhookPort ?? null; | |
| ctx.setStatus({ accountId: ctx.accountId, port }); | |
| ctx.setStatus({ accountId: effectiveAccountId, port }); | |
| ctx.log?.info( | |
| `starting feishu[${ctx.accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`, | |
| `starting feishu[${effectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`, | |
| ); | |
| return monitorFeishuProvider({ | |
| config: ctx.cfg, | |
| runtime: ctx.runtime, | |
| abortSignal: ctx.abortSignal, | |
| accountId: ctx.accountId, | |
| accountId: effectiveAccountId, | |
| const account = resolveFeishuRuntimeAccount( | |
| { cfg: ctx.cfg, accountId: (ctx as any).agentAccountId ?? ctx.accountId }, | |
| { requireEventSecrets: true }, | |
| ); | |
| const port = account.config?.webhookPort ?? null; | |
| const startEffectiveAccountId = (ctx as any).agentAccountId ?? ctx.accountId; | |
| ctx.setStatus({ accountId: startEffectiveAccountId, port }); | |
| ctx.log?.info( | |
| `starting feishu[${startEffectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`, | |
| ); | |
| return monitorFeishuProvider({ | |
| config: ctx.cfg, | |
| runtime: ctx.runtime, | |
| abortSignal: ctx.abortSignal, | |
| accountId: startEffectiveAccountId, |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts
Line: 1140-1152
Comment:
**`effectiveAccountId` is out of scope here — will cause a `ReferenceError` at runtime**
`effectiveAccountId` is declared with `const` inside the `handleAction` callback at line 621. The `startAccount` callback (here) is a sibling property of a different sub-object (`gateway`), not nested inside `handleAction`, so it cannot close over that variable. TypeScript should flag this as a compile error and, if somehow bypassed, calling `startAccount` will throw `ReferenceError: effectiveAccountId is not defined`.
Each usage on lines 1140, 1144, 1146, and 1152 needs to resolve the effective account directly within this function:
```suggestion
const account = resolveFeishuRuntimeAccount(
{ cfg: ctx.cfg, accountId: (ctx as any).agentAccountId ?? ctx.accountId },
{ requireEventSecrets: true },
);
const port = account.config?.webhookPort ?? null;
const startEffectiveAccountId = (ctx as any).agentAccountId ?? ctx.accountId;
ctx.setStatus({ accountId: startEffectiveAccountId, port });
ctx.log?.info(
`starting feishu[${startEffectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
);
return monitorFeishuProvider({
config: ctx.cfg,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
accountId: startEffectiveAccountId,
```
How can I resolve this? If you propose a fix, please make it concise.| if (parsed && typeof parsed.afterVersion === "string") { | ||
| return parsed.afterVersion; |
There was a problem hiding this comment.
Wrong JSON field path —
afterVersion will always be null
The update command outputs a JSON object where the version is nested at after.version (e.g. { "after": { "version": "2026.3.28" } }), not at a top-level afterVersion key. As a result parsed.afterVersion is always undefined, afterVersion is always null, the if (outcome.ok && afterVersion != null …) branch is never taken, and autoLastSuccessVersion is never written. Every successful auto-update will be logged as "auto-update skipped (version mismatch or unchanged)" and the system will keep retrying on every subsequent check.
The field access should be:
| if (parsed && typeof parsed.afterVersion === "string") { | |
| return parsed.afterVersion; | |
| if (parsed && parsed.after && typeof parsed.after.version === "string") { | |
| return parsed.after.version; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/update-startup.ts
Line: 465-466
Comment:
**Wrong JSON field path — `afterVersion` will always be `null`**
The update command outputs a JSON object where the version is nested at `after.version` (e.g. `{ "after": { "version": "2026.3.28" } }`), not at a top-level `afterVersion` key. As a result `parsed.afterVersion` is always `undefined`, `afterVersion` is always `null`, the `if (outcome.ok && afterVersion != null …)` branch is never taken, and `autoLastSuccessVersion` is never written. Every successful auto-update will be logged as "auto-update skipped (version mismatch or unchanged)" and the system will keep retrying on every subsequent check.
The field access should be:
```suggestion
if (parsed && parsed.after && typeof parsed.after.version === "string") {
return parsed.after.version;
```
How can I resolve this? If you propose a fix, please make it concise.| @@ -0,0 +1,1190 @@ | |||
| import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers"; | |||
There was a problem hiding this comment.
Backup file should not be committed to the repository
channel.ts.backup is a development artefact (a mid-edit snapshot of channel.ts) and has no place in version control. It adds ~1,190 lines of near-duplicate code that will cause confusion and will never be kept up to date. Please remove this file before merging.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts.backup
Line: 1
Comment:
**Backup file should not be committed to the repository**
`channel.ts.backup` is a development artefact (a mid-edit snapshot of `channel.ts`) and has no place in version control. It adds ~1,190 lines of near-duplicate code that will cause confusion and will never be kept up to date. Please remove this file before merging.
How can I resolve this? If you propose a fix, please make it concise.| describeMessageTool: describeFeishuMessageTool, | ||
| handleAction: async (ctx) => { | ||
| // Use agentAccountId if available (for subagents), otherwise fall back to ctx.accountId | ||
| const effectiveAccountId = (ctx as any).agentAccountId ?? ctx.accountId ?? undefined; |
There was a problem hiding this comment.
(ctx as any) bypasses TypeScript's type safety
Accessing agentAccountId via (ctx as any) sidesteps type checking and makes it invisible to refactoring tools. If agentAccountId is a legitimate field that subagent contexts are expected to carry, it should be added to the relevant context type (or a subtype/intersection) so that access is type-safe and the intent is self-documenting.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts
Line: 621
Comment:
**`(ctx as any)` bypasses TypeScript's type safety**
Accessing `agentAccountId` via `(ctx as any)` sidesteps type checking and makes it invisible to refactoring tools. If `agentAccountId` is a legitimate field that subagent contexts are expected to carry, it should be added to the relevant context type (or a subtype/intersection) so that access is type-safe and the intent is self-documenting.
How can I resolve this? If you propose a fix, please make it concise.…kup removal) 1. Remove channel.ts.backup development artefact 2. Fix effectiveAccountId scope error in startAccount: declare local startEffectiveAccountId instead of referencing out-of-scope variable 3. Fix update-startup.ts JSON path: parsed.afterVersion -> parsed.after.version
Fixes #58107
Problem
When multiple Feishu group agents (xixi, ling, aoao, weiwei) send messages in a group chat, only the main agent's messages are delivered correctly. Subagents were using the wrong accountId ('default') instead of their assigned accountId.
Root Cause
The Feishu channel's handleAction function was using ctx.accountId which may be undefined or default for subagents. Subagents have their own agentAccountId in the tool execution context, but this wasn't being used.
Solution
Modified extensions/feishu/src/channel.ts to prioritize agentAccountId (available for subagents) over ctx.accountId when resolving the account to use for sending messages.
Changes
Testing
The fix ensures that: