mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 14:44:32 +00:00
fix(agent): forward resolved outbound session context for delivery
This commit is contained in:
@@ -48,6 +48,7 @@ describe("deliverAgentCommandResult", () => {
|
|||||||
|
|
||||||
async function runDelivery(params: {
|
async function runDelivery(params: {
|
||||||
opts: Record<string, unknown>;
|
opts: Record<string, unknown>;
|
||||||
|
outboundSession?: { key?: string; agentId?: string };
|
||||||
sessionEntry?: SessionEntry;
|
sessionEntry?: SessionEntry;
|
||||||
runtime?: RuntimeEnv;
|
runtime?: RuntimeEnv;
|
||||||
resultText?: string;
|
resultText?: string;
|
||||||
@@ -62,6 +63,7 @@ describe("deliverAgentCommandResult", () => {
|
|||||||
deps,
|
deps,
|
||||||
runtime,
|
runtime,
|
||||||
opts: params.opts as never,
|
opts: params.opts as never,
|
||||||
|
outboundSession: params.outboundSession,
|
||||||
sessionEntry: params.sessionEntry,
|
sessionEntry: params.sessionEntry,
|
||||||
result,
|
result,
|
||||||
payloads: result.payloads,
|
payloads: result.payloads,
|
||||||
@@ -234,6 +236,30 @@ describe("deliverAgentCommandResult", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses caller-provided outbound session context when opts.sessionKey is absent", async () => {
|
||||||
|
await runDelivery({
|
||||||
|
opts: {
|
||||||
|
message: "hello",
|
||||||
|
deliver: true,
|
||||||
|
channel: "whatsapp",
|
||||||
|
to: "+15551234567",
|
||||||
|
},
|
||||||
|
outboundSession: {
|
||||||
|
key: "agent:exec:hook:gmail:thread-1",
|
||||||
|
agentId: "exec",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
session: expect.objectContaining({
|
||||||
|
key: "agent:exec:hook:gmail:thread-1",
|
||||||
|
agentId: "exec",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("prefixes nested agent outputs with context", async () => {
|
it("prefixes nested agent outputs with context", async () => {
|
||||||
const runtime = createRuntime();
|
const runtime = createRuntime();
|
||||||
await runDelivery({
|
await runDelivery({
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { setActivePluginRegistry } from "../plugins/runtime.js";
|
|||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js";
|
import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||||
import { agentCommand } from "./agent.js";
|
import { agentCommand } from "./agent.js";
|
||||||
|
import * as agentDeliveryModule from "./agent/delivery.js";
|
||||||
|
|
||||||
vi.mock("../agents/auth-profiles.js", async (importOriginal) => {
|
vi.mock("../agents/auth-profiles.js", async (importOriginal) => {
|
||||||
const actual = await importOriginal<typeof import("../agents/auth-profiles.js")>();
|
const actual = await importOriginal<typeof import("../agents/auth-profiles.js")>();
|
||||||
@@ -49,6 +50,7 @@ const runtime: RuntimeEnv = {
|
|||||||
|
|
||||||
const configSpy = vi.spyOn(configModule, "loadConfig");
|
const configSpy = vi.spyOn(configModule, "loadConfig");
|
||||||
const runCliAgentSpy = vi.spyOn(cliRunnerModule, "runCliAgent");
|
const runCliAgentSpy = vi.spyOn(cliRunnerModule, "runCliAgent");
|
||||||
|
const deliverAgentCommandResultSpy = vi.spyOn(agentDeliveryModule, "deliverAgentCommandResult");
|
||||||
|
|
||||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||||
return withTempHomeBase(fn, { prefix: "openclaw-agent-" });
|
return withTempHomeBase(fn, { prefix: "openclaw-agent-" });
|
||||||
@@ -230,6 +232,35 @@ describe("agentCommand", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("forwards resolved outbound session context when resuming by sessionId", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const storePattern = path.join(home, "sessions", "{agentId}", "sessions.json");
|
||||||
|
const execStore = path.join(home, "sessions", "exec", "sessions.json");
|
||||||
|
writeSessionStoreSeed(execStore, {
|
||||||
|
"agent:exec:hook:gmail:thread-1": {
|
||||||
|
sessionId: "session-exec-hook",
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
systemSent: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
mockConfig(home, storePattern, undefined, undefined, [
|
||||||
|
{ id: "dev" },
|
||||||
|
{ id: "exec", default: true },
|
||||||
|
]);
|
||||||
|
|
||||||
|
await agentCommand({ message: "resume me", sessionId: "session-exec-hook" }, runtime);
|
||||||
|
|
||||||
|
const deliverCall = deliverAgentCommandResultSpy.mock.calls.at(-1)?.[0];
|
||||||
|
expect(deliverCall?.opts.sessionKey).toBeUndefined();
|
||||||
|
expect(deliverCall?.outboundSession).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
key: "agent:exec:hook:gmail:thread-1",
|
||||||
|
agentId: "exec",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("resolves resumed session transcript path from custom session store directory", async () => {
|
it("resolves resumed session transcript path from custom session store directory", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const customStoreDir = path.join(home, "custom-state");
|
const customStoreDir = path.join(home, "custom-state");
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ import {
|
|||||||
emitAgentEvent,
|
emitAgentEvent,
|
||||||
registerAgentRunContext,
|
registerAgentRunContext,
|
||||||
} from "../infra/agent-events.js";
|
} from "../infra/agent-events.js";
|
||||||
|
import { buildOutboundSessionContext } from "../infra/outbound/session-context.js";
|
||||||
import { getRemoteSkillEligibility } from "../infra/skills-remote.js";
|
import { getRemoteSkillEligibility } from "../infra/skills-remote.js";
|
||||||
import { normalizeAgentId } from "../routing/session-key.js";
|
import { normalizeAgentId } from "../routing/session-key.js";
|
||||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||||
@@ -316,6 +317,11 @@ export async function agentCommand(
|
|||||||
sessionKey: sessionKey ?? opts.sessionKey?.trim(),
|
sessionKey: sessionKey ?? opts.sessionKey?.trim(),
|
||||||
config: cfg,
|
config: cfg,
|
||||||
});
|
});
|
||||||
|
const outboundSession = buildOutboundSessionContext({
|
||||||
|
cfg,
|
||||||
|
agentId: sessionAgentId,
|
||||||
|
sessionKey,
|
||||||
|
});
|
||||||
const workspaceDirRaw = resolveAgentWorkspaceDir(cfg, sessionAgentId);
|
const workspaceDirRaw = resolveAgentWorkspaceDir(cfg, sessionAgentId);
|
||||||
const agentDir = resolveAgentDir(cfg, sessionAgentId);
|
const agentDir = resolveAgentDir(cfg, sessionAgentId);
|
||||||
const workspace = await ensureAgentWorkspace({
|
const workspace = await ensureAgentWorkspace({
|
||||||
@@ -461,6 +467,7 @@ export async function agentCommand(
|
|||||||
deps,
|
deps,
|
||||||
runtime,
|
runtime,
|
||||||
opts,
|
opts,
|
||||||
|
outboundSession,
|
||||||
sessionEntry,
|
sessionEntry,
|
||||||
result,
|
result,
|
||||||
payloads,
|
payloads,
|
||||||
@@ -809,6 +816,7 @@ export async function agentCommand(
|
|||||||
deps,
|
deps,
|
||||||
runtime,
|
runtime,
|
||||||
opts,
|
opts,
|
||||||
|
outboundSession,
|
||||||
sessionEntry,
|
sessionEntry,
|
||||||
result,
|
result,
|
||||||
payloads,
|
payloads,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
normalizeOutboundPayloads,
|
normalizeOutboundPayloads,
|
||||||
normalizeOutboundPayloadsForJson,
|
normalizeOutboundPayloadsForJson,
|
||||||
} from "../../infra/outbound/payloads.js";
|
} from "../../infra/outbound/payloads.js";
|
||||||
import { buildOutboundSessionContext } from "../../infra/outbound/session-context.js";
|
import type { OutboundSessionContext } from "../../infra/outbound/session-context.js";
|
||||||
import type { RuntimeEnv } from "../../runtime.js";
|
import type { RuntimeEnv } from "../../runtime.js";
|
||||||
import { isInternalMessageChannel } from "../../utils/message-channel.js";
|
import { isInternalMessageChannel } from "../../utils/message-channel.js";
|
||||||
import type { AgentCommandOpts } from "./types.js";
|
import type { AgentCommandOpts } from "./types.js";
|
||||||
@@ -64,11 +64,12 @@ export async function deliverAgentCommandResult(params: {
|
|||||||
deps: CliDeps;
|
deps: CliDeps;
|
||||||
runtime: RuntimeEnv;
|
runtime: RuntimeEnv;
|
||||||
opts: AgentCommandOpts;
|
opts: AgentCommandOpts;
|
||||||
|
outboundSession: OutboundSessionContext | undefined;
|
||||||
sessionEntry: SessionEntry | undefined;
|
sessionEntry: SessionEntry | undefined;
|
||||||
result: RunResult;
|
result: RunResult;
|
||||||
payloads: RunResult["payloads"];
|
payloads: RunResult["payloads"];
|
||||||
}) {
|
}) {
|
||||||
const { cfg, deps, runtime, opts, sessionEntry, payloads, result } = params;
|
const { cfg, deps, runtime, opts, outboundSession, sessionEntry, payloads, result } = params;
|
||||||
const deliver = opts.deliver === true;
|
const deliver = opts.deliver === true;
|
||||||
const bestEffortDeliver = opts.bestEffortDeliver === true;
|
const bestEffortDeliver = opts.bestEffortDeliver === true;
|
||||||
const turnSourceChannel = opts.runContext?.messageChannel ?? opts.messageChannel;
|
const turnSourceChannel = opts.runContext?.messageChannel ?? opts.messageChannel;
|
||||||
@@ -212,18 +213,13 @@ export async function deliverAgentCommandResult(params: {
|
|||||||
}
|
}
|
||||||
if (deliver && deliveryChannel && !isInternalMessageChannel(deliveryChannel)) {
|
if (deliver && deliveryChannel && !isInternalMessageChannel(deliveryChannel)) {
|
||||||
if (deliveryTarget) {
|
if (deliveryTarget) {
|
||||||
const deliverySession = buildOutboundSessionContext({
|
|
||||||
cfg,
|
|
||||||
agentId: opts.agentId,
|
|
||||||
sessionKey: opts.sessionKey,
|
|
||||||
});
|
|
||||||
await deliverOutboundPayloads({
|
await deliverOutboundPayloads({
|
||||||
cfg,
|
cfg,
|
||||||
channel: deliveryChannel,
|
channel: deliveryChannel,
|
||||||
to: deliveryTarget,
|
to: deliveryTarget,
|
||||||
accountId: resolvedAccountId,
|
accountId: resolvedAccountId,
|
||||||
payloads: deliveryPayloads,
|
payloads: deliveryPayloads,
|
||||||
session: deliverySession,
|
session: outboundSession,
|
||||||
replyToId: resolvedReplyToId ?? null,
|
replyToId: resolvedReplyToId ?? null,
|
||||||
threadId: resolvedThreadTarget ?? null,
|
threadId: resolvedThreadTarget ?? null,
|
||||||
bestEffort: bestEffortDeliver,
|
bestEffort: bestEffortDeliver,
|
||||||
|
|||||||
Reference in New Issue
Block a user