fix(tui): preserve case-sensitive Matrix and Signal conversations (#113800)

* test(tui): reproduce opaque provider session identity loss

* fix(tui): preserve case-sensitive provider session identities

* test(tui): cover cross-agent opaque session identities

---------

Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
This commit is contained in:
Peter Steinberger
2026-07-25 12:10:43 -07:00
committed by GitHub
parent 0e2c60e605
commit 31190d6469
3 changed files with 78 additions and 5 deletions
+28
View File
@@ -916,6 +916,34 @@ describe.sequential("TUI PTY harness", () => {
TEST_TIMEOUT_MS,
);
it.each([
{
sessionKey: "agent:main:matrix:channel:!MixedRoomAbCdEf:example.org",
message: "mixed-case matrix session identity proof",
},
{
sessionKey: "agent:main:signal:group:AbC123=",
message: "mixed-case signal session identity proof",
},
])(
"preserves provider-owned identity when selecting $sessionKey in the terminal",
async ({ sessionKey, message }) => {
await fixture.run.write(`/session ${sessionKey}\r`, { delay: false });
await fixture.waitForLogEntry(
(entry) =>
entry.method === "loadHistory" && objectFieldEquals(entry, "sessionKey", sessionKey),
);
await fixture.run.write(`${message}\r`, { delay: false });
const sent = await fixture.waitForLogEntry(
(entry) => entry.method === "sendChat" && objectFieldEquals(entry, "message", message),
);
expect(sent.payload).toMatchObject({ sessionKey, message });
},
TEST_TIMEOUT_MS,
);
it(
"creates a backend session from /new and adopts its canonical key",
async () => {
+44
View File
@@ -186,6 +186,50 @@ describe("resolveTuiSessionKey", () => {
).toBe("agent:ops:incident");
});
it.each([
{
raw: "agent:main:matrix:channel:!MixedRoomAbCdEf:example.org",
expected: "agent:main:matrix:channel:!MixedRoomAbCdEf:example.org",
},
{
raw: "Matrix:Channel:!MixedRoomAbCdEf:example.org",
expected: "agent:main:matrix:channel:!MixedRoomAbCdEf:example.org",
},
{
raw: "Agent:Main:Matrix:Channel:!MixedRoomAbCdEf:example.org:Thread:$EventAbCdEf",
expected: "agent:main:matrix:channel:!MixedRoomAbCdEf:example.org:thread:$EventAbCdEf",
},
{
raw: "Agent:Ops:Matrix:Channel:!MixedRoomAbCdEf:example.org",
expected: "agent:ops:matrix:channel:!MixedRoomAbCdEf:example.org",
},
{
raw: "agent:main:signal:group:AbC123=",
expected: "agent:main:signal:group:AbC123=",
},
{
raw: "Agent:Ops:Signal:Group:AbC123=",
expected: "agent:ops:signal:group:AbC123=",
},
{
raw: "Signal:Group:AbC123=",
expected: "agent:main:signal:group:AbC123=",
},
{
raw: "Telegram:Group:MixedHandle",
expected: "agent:main:telegram:group:mixedhandle",
},
])("preserves canonical provider-owned session identity for $raw", ({ raw, expected }) => {
expect(
resolveTuiSessionKey({
raw,
sessionScope: "per-sender",
currentAgentId: "main",
sessionMainKey: "main",
}),
).toBe(expected);
});
it("lowercases session keys with uppercase characters", () => {
// Uppercase in agent-prefixed form
expect(
+6 -5
View File
@@ -13,7 +13,6 @@ import {
Text,
TUI,
} from "@earendil-works/pi-tui";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import type { CommandEntry } from "../../packages/gateway-protocol/src/index.js";
import { resolveAgentIdByWorkspacePath, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { normalizeThinkLevel } from "../auto-reply/thinking.shared.js";
@@ -35,6 +34,7 @@ import {
normalizeAgentId,
normalizeMainKey,
parseAgentSessionKey,
toAgentStoreSessionKey,
} from "../routing/session-key.js";
import { getSlashCommands, shouldSubmitExactArgumentCompletion } from "./commands.js";
import { ChatLog } from "./components/chat-log.js";
@@ -213,10 +213,11 @@ export function resolveTuiSessionKey(params: {
if (trimmed === "global" || trimmed === "unknown") {
return trimmed;
}
if (trimmed.startsWith("agent:")) {
return normalizeLowercaseStringOrEmpty(trimmed);
}
return `agent:${params.currentAgentId}:${normalizeLowercaseStringOrEmpty(trimmed)}`;
return toAgentStoreSessionKey({
agentId: params.currentAgentId,
requestKey: trimmed,
mainKey: params.sessionMainKey,
});
}
export function resolveInitialTuiAgentId(params: {