Gateway: keep outbound session metadata in owner store

This commit is contained in:
Gustavo Madeira Santana
2026-04-05 17:42:05 -04:00
parent 06f9677b5b
commit e29d370969
4 changed files with 54 additions and 7 deletions

View File

@@ -253,7 +253,6 @@ export const sendHandlers: GatewayRequestHandlers = {
if (outboundRoute) {
await ensureOutboundSessionEntry({
cfg,
agentId: effectiveAgentId,
channel,
accountId,
route: outboundRoute,

View File

@@ -56,7 +56,6 @@ export async function prepareOutboundMirrorRoute(params: {
) => Promise<OutboundSessionRoute | null>;
ensureOutboundSessionEntry: (params: {
cfg: OpenClawConfig;
agentId: string;
channel: ChannelId;
accountId?: string | null;
route: OutboundSessionRoute;
@@ -90,7 +89,6 @@ export async function prepareOutboundMirrorRoute(params: {
if (outboundRoute && params.agentId && !params.dryRun) {
await params.ensureOutboundSessionEntry({
cfg: params.cfg,
agentId: params.agentId,
channel: params.channel,
accountId: params.accountId,
route: outboundRoute,

View File

@@ -1,10 +1,24 @@
import { beforeEach, describe, expect, it } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import { resolveOutboundSessionRoute } from "./outbound-session.js";
import { ensureOutboundSessionEntry, resolveOutboundSessionRoute } from "./outbound-session.js";
import { setMinimalOutboundSessionPluginRegistryForTests } from "./outbound-session.test-helpers.js";
const mocks = vi.hoisted(() => ({
recordSessionMetaFromInbound: vi.fn(async () => ({ ok: true })),
resolveStorePath: vi.fn(
(_store: unknown, params?: { agentId?: string }) => `/stores/${params?.agentId ?? "main"}.json`,
),
}));
vi.mock("../../config/sessions/inbound.runtime.js", () => ({
recordSessionMetaFromInbound: mocks.recordSessionMetaFromInbound,
resolveStorePath: mocks.resolveStorePath,
}));
describe("resolveOutboundSessionRoute", () => {
beforeEach(() => {
mocks.recordSessionMetaFromInbound.mockClear();
mocks.resolveStorePath.mockClear();
setMinimalOutboundSessionPluginRegistryForTests();
});
@@ -394,3 +408,39 @@ describe("resolveOutboundSessionRoute", () => {
).rejects.toThrow(/Ambiguous Discord recipient/);
});
});
describe("ensureOutboundSessionEntry", () => {
beforeEach(() => {
mocks.recordSessionMetaFromInbound.mockClear();
mocks.resolveStorePath.mockClear();
});
it("persists metadata in the owning session store for the route session key", async () => {
await ensureOutboundSessionEntry({
cfg: {
session: {
store: "/stores/{agentId}.json",
},
} as OpenClawConfig,
channel: "slack",
route: {
sessionKey: "agent:main:slack:channel:c1",
baseSessionKey: "agent:work:slack:channel:resolved",
peer: { kind: "channel", id: "c1" },
chatType: "channel",
from: "slack:channel:C1",
to: "channel:C1",
},
});
expect(mocks.resolveStorePath).toHaveBeenCalledWith("/stores/{agentId}.json", {
agentId: "main",
});
expect(mocks.recordSessionMetaFromInbound).toHaveBeenCalledWith(
expect.objectContaining({
storePath: "/stores/main.json",
sessionKey: "agent:main:slack:channel:c1",
}),
);
});
});

View File

@@ -8,6 +8,7 @@ import {
resolveStorePath,
} from "../../config/sessions/inbound.runtime.js";
import { buildAgentSessionKey, type RoutePeer } from "../../routing/resolve-route.js";
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import type { ResolvedMessagingTarget } from "./target-resolver.js";
export type OutboundSessionRoute = {
@@ -148,13 +149,12 @@ export async function resolveOutboundSessionRoute(
export async function ensureOutboundSessionEntry(params: {
cfg: OpenClawConfig;
agentId: string;
channel: ChannelId;
accountId?: string | null;
route: OutboundSessionRoute;
}): Promise<void> {
const storePath = resolveStorePath(params.cfg.session?.store, {
agentId: params.agentId,
agentId: resolveAgentIdFromSessionKey(params.route.sessionKey),
});
const ctx: MsgContext = {
From: params.route.from,