Skip to content

Commit 712e231

Browse files
committed
fix(agent): forward resolved outbound session context for delivery
1 parent da9f24d commit 712e231

File tree

4 files changed

+69
-8
lines changed

4 files changed

+69
-8
lines changed

src/commands/agent.delivery.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ describe("deliverAgentCommandResult", () => {
4848

4949
async function runDelivery(params: {
5050
opts: Record<string, unknown>;
51+
outboundSession?: { key?: string; agentId?: string };
5152
sessionEntry?: SessionEntry;
5253
runtime?: RuntimeEnv;
5354
resultText?: string;
@@ -62,6 +63,7 @@ describe("deliverAgentCommandResult", () => {
6263
deps,
6364
runtime,
6465
opts: params.opts as never,
66+
outboundSession: params.outboundSession,
6567
sessionEntry: params.sessionEntry,
6668
result,
6769
payloads: result.payloads,
@@ -234,6 +236,30 @@ describe("deliverAgentCommandResult", () => {
234236
);
235237
});
236238

239+
it("uses caller-provided outbound session context when opts.sessionKey is absent", async () => {
240+
await runDelivery({
241+
opts: {
242+
message: "hello",
243+
deliver: true,
244+
channel: "whatsapp",
245+
to: "+15551234567",
246+
},
247+
outboundSession: {
248+
key: "agent:exec:hook:gmail:thread-1",
249+
agentId: "exec",
250+
},
251+
});
252+
253+
expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith(
254+
expect.objectContaining({
255+
session: expect.objectContaining({
256+
key: "agent:exec:hook:gmail:thread-1",
257+
agentId: "exec",
258+
}),
259+
}),
260+
);
261+
});
262+
237263
it("prefixes nested agent outputs with context", async () => {
238264
const runtime = createRuntime();
239265
await runDelivery({

src/commands/agent.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { setActivePluginRegistry } from "../plugins/runtime.js";
1414
import type { RuntimeEnv } from "../runtime.js";
1515
import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js";
1616
import { agentCommand } from "./agent.js";
17+
import * as agentDeliveryModule from "./agent/delivery.js";
1718

1819
vi.mock("../agents/auth-profiles.js", async (importOriginal) => {
1920
const actual = await importOriginal<typeof import("../agents/auth-profiles.js")>();
@@ -49,6 +50,7 @@ const runtime: RuntimeEnv = {
4950

5051
const configSpy = vi.spyOn(configModule, "loadConfig");
5152
const runCliAgentSpy = vi.spyOn(cliRunnerModule, "runCliAgent");
53+
const deliverAgentCommandResultSpy = vi.spyOn(agentDeliveryModule, "deliverAgentCommandResult");
5254

5355
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
5456
return withTempHomeBase(fn, { prefix: "openclaw-agent-" });
@@ -230,6 +232,35 @@ describe("agentCommand", () => {
230232
});
231233
});
232234

235+
it("forwards resolved outbound session context when resuming by sessionId", async () => {
236+
await withTempHome(async (home) => {
237+
const storePattern = path.join(home, "sessions", "{agentId}", "sessions.json");
238+
const execStore = path.join(home, "sessions", "exec", "sessions.json");
239+
writeSessionStoreSeed(execStore, {
240+
"agent:exec:hook:gmail:thread-1": {
241+
sessionId: "session-exec-hook",
242+
updatedAt: Date.now(),
243+
systemSent: true,
244+
},
245+
});
246+
mockConfig(home, storePattern, undefined, undefined, [
247+
{ id: "dev" },
248+
{ id: "exec", default: true },
249+
]);
250+
251+
await agentCommand({ message: "resume me", sessionId: "session-exec-hook" }, runtime);
252+
253+
const deliverCall = deliverAgentCommandResultSpy.mock.calls.at(-1)?.[0];
254+
expect(deliverCall?.opts.sessionKey).toBeUndefined();
255+
expect(deliverCall?.outboundSession).toEqual(
256+
expect.objectContaining({
257+
key: "agent:exec:hook:gmail:thread-1",
258+
agentId: "exec",
259+
}),
260+
);
261+
});
262+
});
263+
233264
it("resolves resumed session transcript path from custom session store directory", async () => {
234265
await withTempHome(async (home) => {
235266
const customStoreDir = path.join(home, "custom-state");

src/commands/agent.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import {
5959
emitAgentEvent,
6060
registerAgentRunContext,
6161
} from "../infra/agent-events.js";
62+
import { buildOutboundSessionContext } from "../infra/outbound/session-context.js";
6263
import { getRemoteSkillEligibility } from "../infra/skills-remote.js";
6364
import { normalizeAgentId } from "../routing/session-key.js";
6465
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
@@ -316,6 +317,11 @@ export async function agentCommand(
316317
sessionKey: sessionKey ?? opts.sessionKey?.trim(),
317318
config: cfg,
318319
});
320+
const outboundSession = buildOutboundSessionContext({
321+
cfg,
322+
agentId: sessionAgentId,
323+
sessionKey,
324+
});
319325
const workspaceDirRaw = resolveAgentWorkspaceDir(cfg, sessionAgentId);
320326
const agentDir = resolveAgentDir(cfg, sessionAgentId);
321327
const workspace = await ensureAgentWorkspace({
@@ -461,6 +467,7 @@ export async function agentCommand(
461467
deps,
462468
runtime,
463469
opts,
470+
outboundSession,
464471
sessionEntry,
465472
result,
466473
payloads,
@@ -809,6 +816,7 @@ export async function agentCommand(
809816
deps,
810817
runtime,
811818
opts,
819+
outboundSession,
812820
sessionEntry,
813821
result,
814822
payloads,

src/commands/agent/delivery.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
normalizeOutboundPayloads,
1717
normalizeOutboundPayloadsForJson,
1818
} from "../../infra/outbound/payloads.js";
19-
import { buildOutboundSessionContext } from "../../infra/outbound/session-context.js";
19+
import type { OutboundSessionContext } from "../../infra/outbound/session-context.js";
2020
import type { RuntimeEnv } from "../../runtime.js";
2121
import { isInternalMessageChannel } from "../../utils/message-channel.js";
2222
import type { AgentCommandOpts } from "./types.js";
@@ -64,11 +64,12 @@ export async function deliverAgentCommandResult(params: {
6464
deps: CliDeps;
6565
runtime: RuntimeEnv;
6666
opts: AgentCommandOpts;
67+
outboundSession: OutboundSessionContext | undefined;
6768
sessionEntry: SessionEntry | undefined;
6869
result: RunResult;
6970
payloads: RunResult["payloads"];
7071
}) {
71-
const { cfg, deps, runtime, opts, sessionEntry, payloads, result } = params;
72+
const { cfg, deps, runtime, opts, outboundSession, sessionEntry, payloads, result } = params;
7273
const deliver = opts.deliver === true;
7374
const bestEffortDeliver = opts.bestEffortDeliver === true;
7475
const turnSourceChannel = opts.runContext?.messageChannel ?? opts.messageChannel;
@@ -212,18 +213,13 @@ export async function deliverAgentCommandResult(params: {
212213
}
213214
if (deliver && deliveryChannel && !isInternalMessageChannel(deliveryChannel)) {
214215
if (deliveryTarget) {
215-
const deliverySession = buildOutboundSessionContext({
216-
cfg,
217-
agentId: opts.agentId,
218-
sessionKey: opts.sessionKey,
219-
});
220216
await deliverOutboundPayloads({
221217
cfg,
222218
channel: deliveryChannel,
223219
to: deliveryTarget,
224220
accountId: resolvedAccountId,
225221
payloads: deliveryPayloads,
226-
session: deliverySession,
222+
session: outboundSession,
227223
replyToId: resolvedReplyToId ?? null,
228224
threadId: resolvedThreadTarget ?? null,
229225
bestEffort: bestEffortDeliver,

0 commit comments

Comments
 (0)