Skip to content

Commit 18b1d8e

Browse files
HCLclaude
andcommitted
fix(exec): preserve turnSourceChannel as messageProvider in approval followup runs
When an exec-approval followup run has no deliverable route and no gateway-internal channel, buildAgentFollowupArgs was passing channel=undefined to the spawned agent. This left defaults.messageProvider=undefined in the followup run, causing tools.elevated.allowFrom.<provider> checks to always fail with provider=null after the user approved an async elevated command. Thread turnSourceChannel through buildAgentFollowupArgs and use it as a fallback when sessionOnlyOriginChannel is absent. Fixes #74646. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent c538906 commit 18b1d8e

3 files changed

Lines changed: 30 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Docs: https://docs.openclaw.ai
4141
- Plugin SDK/testing: lazy-load TypeScript from the plugin test-contract runtime and add release checks for critical SDK contract entrypoint imports and bundle size, so published packages fail preflight before shipping ESM-incompatible or oversized contract helpers. Thanks @vincentkoc.
4242
- Channels/Microsoft Teams: treat configured `19:[email protected]` and legacy `19:[email protected]` team/channel IDs as already resolved during startup, avoiding false `channels unresolved` warnings while preserving Graph name lookup for display-name entries. Fixes #74683. Thanks @dseravalli.
4343
- CLI/browser: preserve parent flags while lazy-loading browser subcommands, so `openclaw browser --json open` and `openclaw browser --json tabs` keep machine-readable output after reparsing. Fixes #74574. Thanks @devintegeritsm.
44+
- Exec/elevated: preserve `turnSourceChannel` as `messageProvider` on approval-followup runs so `tools.elevated.allowFrom.<provider>` checks no longer fail with `provider=null` after the user approves an async elevated command. Fixes #74646. Thanks @xhd2015.
4445
- Plugins/runtime-deps: add `openclaw plugins deps` inspection and repair with script-free package-manager defaults shared across plugin installers, so operators can repair missing bundled runtime deps without corrupting JSON output or blocking unrelated conflict-free deps. Thanks @vincentkoc.
4546
- Agents/output: strip internal `[tool calls omitted]` replay placeholders from user-facing replies while preserving visible reply whitespace. Fixes #74573. Thanks @blaspat.
4647
- Providers/Google Vertex: route authorized_user ADC credentials through OpenClaw's REST transport so Docker installs using gcloud application-default credentials no longer crash in the Google SDK before requests are sent. Fixes #74628. Thanks @frankhal2001-design.

src/agents/bash-tools.exec-approval-followup.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,28 @@ describe("exec approval followup", () => {
252252
expect(sendMessage).not.toHaveBeenCalled();
253253
});
254254

255+
it("preserves turnSourceChannel as messageProvider on the followup run when no deliverable route exists", async () => {
256+
// Regression: #74646 — tools.elevated.allowFrom.<provider> fails in approval followup
257+
await sendExecApprovalFollowup({
258+
approvalId: "req-elevated-74646",
259+
sessionKey: "agent:main:telegram:-100123",
260+
turnSourceChannel: "telegram",
261+
resultText: "Exec completed: systemctl status gateway",
262+
});
263+
264+
expect(callGatewayTool).toHaveBeenCalledWith(
265+
"agent",
266+
expect.any(Object),
267+
expect.objectContaining({
268+
sessionKey: "agent:main:telegram:-100123",
269+
deliver: false,
270+
channel: "telegram",
271+
}),
272+
{ expectFinal: true },
273+
);
274+
expect(sendMessage).not.toHaveBeenCalled();
275+
});
276+
255277
it("throws when neither a session nor a deliverable route is available", async () => {
256278
await expect(
257279
sendExecApprovalFollowup({

src/agents/bash-tools.exec-approval-followup.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,22 @@ function buildAgentFollowupArgs(params: {
145145
resultText: string;
146146
deliveryTarget: ExternalBestEffortDeliveryTarget;
147147
sessionOnlyOriginChannel?: string;
148+
turnSourceChannel?: string;
148149
turnSourceTo?: string;
149150
turnSourceAccountId?: string;
150151
turnSourceThreadId?: string | number;
151152
}) {
152153
const { deliveryTarget, sessionOnlyOriginChannel } = params;
154+
// When the followup run has no deliverable route and no gateway-internal channel,
155+
// preserve the raw turnSourceChannel so the spawned agent inherits messageProvider.
156+
// Without this, tools.elevated.allowFrom.<provider> checks fail with provider=null.
157+
const fallbackChannel = sessionOnlyOriginChannel ?? params.turnSourceChannel;
153158
return {
154159
sessionKey: params.sessionKey,
155160
message: buildExecApprovalFollowupPrompt(params.resultText),
156161
deliver: deliveryTarget.deliver,
157162
...(deliveryTarget.deliver ? { bestEffortDeliver: true as const } : {}),
158-
channel: deliveryTarget.deliver ? deliveryTarget.channel : sessionOnlyOriginChannel,
163+
channel: deliveryTarget.deliver ? deliveryTarget.channel : fallbackChannel,
159164
to: deliveryTarget.deliver
160165
? deliveryTarget.to
161166
: sessionOnlyOriginChannel
@@ -241,6 +246,7 @@ export async function sendExecApprovalFollowup(
241246
resultText,
242247
deliveryTarget,
243248
sessionOnlyOriginChannel,
249+
turnSourceChannel: params.turnSourceChannel,
244250
turnSourceTo: params.turnSourceTo,
245251
turnSourceAccountId: params.turnSourceAccountId,
246252
turnSourceThreadId: params.turnSourceThreadId,

0 commit comments

Comments
 (0)