fix: sync delivery routing context

Co-authored-by: adam91holt <adam91holt@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-17 06:01:30 +00:00
parent e59d8c5436
commit 285ed8bac3
10 changed files with 208 additions and 51 deletions

View File

@@ -130,6 +130,10 @@ describe("sessions", () => {
expect(store[mainSessionKey]?.updatedAt).toBeGreaterThanOrEqual(123);
expect(store[mainSessionKey]?.lastChannel).toBe("telegram");
expect(store[mainSessionKey]?.lastTo).toBe("12345");
expect(store[mainSessionKey]?.deliveryContext).toEqual({
channel: "telegram",
to: "12345",
});
expect(store[mainSessionKey]?.responseUsage).toBe("on");
expect(store[mainSessionKey]?.queueDebounceMs).toBe(1234);
expect(store[mainSessionKey]?.reasoningLevel).toBe("on");
@@ -176,6 +180,11 @@ describe("sessions", () => {
expect(store["agent:main:main"]?.lastChannel).toBe("whatsapp");
expect(store["agent:main:main"]?.lastTo).toBe("+1555");
expect(store["agent:main:main"]?.lastAccountId).toBe("acct-1");
expect(store["agent:main:main"]?.deliveryContext).toEqual({
channel: "whatsapp",
to: "+1555",
accountId: "acct-1",
});
});
it("updateSessionStore keeps deletions when concurrent writes happen", async () => {

View File

@@ -4,8 +4,7 @@ import path from "node:path";
import JSON5 from "json5";
import { getFileMtimeMs, isCacheEnabled, resolveCacheTtlMs } from "../cache-utils.js";
import { normalizeAccountId } from "../../utils/account-id.js";
import { normalizeMessageChannel } from "../../utils/message-channel.js";
import { normalizeSessionDeliveryFields } from "../../utils/delivery-context.js";
import { mergeSessionEntry, type SessionEntry } from "./types.js";
// ============================================================================
@@ -44,21 +43,23 @@ function invalidateSessionStoreCache(storePath: string): void {
}
function normalizeSessionEntryDelivery(entry: SessionEntry): SessionEntry {
const normalizedLastChannel = normalizeMessageChannel(entry.lastChannel) ?? undefined;
const normalizedLastTo = typeof entry.lastTo === "string" ? entry.lastTo.trim() : undefined;
const normalizedLastAccountId = normalizeAccountId(entry.lastAccountId);
if (
normalizedLastChannel === entry.lastChannel &&
normalizedLastTo === entry.lastTo &&
normalizedLastAccountId === entry.lastAccountId
) {
return entry;
}
const normalized = normalizeSessionDeliveryFields(entry);
const nextDelivery = normalized.deliveryContext;
const sameDelivery =
(entry.deliveryContext?.channel ?? undefined) === nextDelivery?.channel &&
(entry.deliveryContext?.to ?? undefined) === nextDelivery?.to &&
(entry.deliveryContext?.accountId ?? undefined) === nextDelivery?.accountId;
const sameLast =
entry.lastChannel === normalized.lastChannel &&
entry.lastTo === normalized.lastTo &&
entry.lastAccountId === normalized.lastAccountId;
if (sameDelivery && sameLast) return entry;
return {
...entry,
lastChannel: normalizedLastChannel,
lastTo: normalizedLastTo || undefined,
lastAccountId: normalizedLastAccountId,
deliveryContext: nextDelivery,
lastChannel: normalized.lastChannel,
lastTo: normalized.lastTo,
lastAccountId: normalized.lastAccountId,
};
}
@@ -331,11 +332,24 @@ export async function updateLastRoute(params: {
const store = loadSessionStore(storePath);
const existing = store[sessionKey];
const now = Date.now();
const trimmedAccountId = accountId?.trim();
const resolvedAccountId =
trimmedAccountId && trimmedAccountId.length > 0
? trimmedAccountId
: existing?.lastAccountId ?? existing?.deliveryContext?.accountId;
const normalized = normalizeSessionDeliveryFields({
deliveryContext: {
channel: channel ?? existing?.lastChannel,
to,
accountId: resolvedAccountId,
},
});
const next = mergeSessionEntry(existing, {
updatedAt: Math.max(existing?.updatedAt ?? 0, now),
lastChannel: channel,
lastTo: to?.trim() ? to.trim() : undefined,
lastAccountId: accountId?.trim() ? accountId.trim() : existing?.lastAccountId,
deliveryContext: normalized.deliveryContext,
lastChannel: normalized.lastChannel ?? channel,
lastTo: normalized.lastTo ?? (to?.trim() ? to.trim() : undefined),
lastAccountId: normalized.lastAccountId ?? resolvedAccountId,
});
store[sessionKey] = next;
await saveSessionStoreUnlocked(storePath, store);

View File

@@ -2,6 +2,7 @@ import crypto from "node:crypto";
import type { Skill } from "@mariozechner/pi-coding-agent";
import type { ChannelId } from "../../channels/plugins/types.js";
import type { DeliveryContext } from "../../utils/delivery-context.js";
export type SessionScope = "per-sender" | "global";
@@ -69,6 +70,7 @@ export type SessionEntry = {
subject?: string;
room?: string;
space?: string;
deliveryContext?: DeliveryContext;
lastChannel?: SessionChannelId;
lastTo?: string;
lastAccountId?: string;