From dfca707e4b16564aa9e34d6b5570122d7de3d06d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 23 Apr 2026 07:57:04 +0100 Subject: [PATCH] fix: resolve implicit default Telegram status sessions --- .../openclaw-tools.session-status.test.ts | 49 +++++++++++++++ src/agents/tools/session-status-tool.ts | 60 +++++++++++++++++-- 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/src/agents/openclaw-tools.session-status.test.ts b/src/agents/openclaw-tools.session-status.test.ts index 437e230f34d..51c7b4ae319 100644 --- a/src/agents/openclaw-tools.session-status.test.ts +++ b/src/agents/openclaw-tools.session-status.test.ts @@ -467,6 +467,55 @@ describe("session_status tool", () => { expect(details.sessionKey).toBe("main"); }); + it("falls back from implicit default-account direct policy keys to persisted direct sessions", async () => { + resetSessionStore({ + "agent:main:telegram:direct:1053274893": { + sessionId: "s-direct", + updatedAt: 10, + }, + }); + + const tool = getSessionStatusTool("agent:main:telegram:default:direct:1053274893"); + + const result = await tool.execute("call-default-direct", {}); + const details = result.details as { ok?: boolean; sessionKey?: string }; + expect(details.ok).toBe(true); + expect(details.sessionKey).toBe("agent:main:telegram:direct:1053274893"); + }); + + it("falls back from implicit default-account direct policy keys to main sessions", async () => { + resetSessionStore({ + "agent:main:main": { + sessionId: "s-main", + updatedAt: 10, + }, + }); + + const tool = getSessionStatusTool("agent:main:telegram:default:direct:1053274893"); + + const result = await tool.execute("call-default-direct-main", {}); + const details = result.details as { ok?: boolean; sessionKey?: string }; + expect(details.ok).toBe(true); + expect(details.sessionKey).toBe("agent:main:main"); + }); + + it("keeps explicit default-account direct session lookups strict", async () => { + resetSessionStore({ + "agent:main:main": { + sessionId: "s-main", + updatedAt: 10, + }, + }); + + const tool = getSessionStatusTool("agent:main:telegram:default:direct:1053274893"); + + await expect( + tool.execute("call-default-direct-explicit", { + sessionKey: "agent:main:telegram:default:direct:1053274893", + }), + ).rejects.toThrow("Unknown sessionKey: agent:main:telegram:default:direct:1053274893"); + }); + it("prefers a literal current session key in session_status", async () => { resetSessionStore({ main: { diff --git a/src/agents/tools/session-status-tool.ts b/src/agents/tools/session-status-tool.ts index a8c5058741f..05a116c79db 100644 --- a/src/agents/tools/session-status-tool.ts +++ b/src/agents/tools/session-status-tool.ts @@ -133,6 +133,33 @@ function resolveStoreScopedRequesterKey(params: { return parsed.rest === params.mainKey ? params.mainKey : params.requesterKey; } +function listImplicitDefaultDirectFallbackKeys(params: { + keyRaw: string; + mainKey: string; +}): string[] { + const parsed = parseAgentSessionKey(params.keyRaw.trim()); + if (!parsed) { + return []; + } + const parts = parsed.rest.split(":"); + if (parts.length < 4 || parts[1] !== "default" || parts[2] !== "direct") { + return []; + } + const [channel, , , ...peerParts] = parts; + if (!channel || peerParts.length === 0) { + return []; + } + const candidates = [ + `agent:${parsed.agentId}:${channel}:direct:${peerParts.join(":")}`, + buildAgentMainSessionKey({ + agentId: parsed.agentId, + mainKey: params.mainKey, + }), + params.mainKey, + ]; + return [...new Set(candidates)]; +} + function formatSessionTaskLine(params: { relatedSessionKey: string; callerOwnerKey: string; @@ -295,6 +322,7 @@ export function createSessionStatusTool(opts?: { let requestedKeyRaw = requestedKeyParam ?? opts?.agentSessionKey; const requestedKeyInput = requestedKeyRaw?.trim() ?? ""; let resolvedViaSessionId = false; + let resolvedViaImplicitCurrentFallback = false; if (!requestedKeyRaw?.trim()) { throw new Error("sessionKey required"); } @@ -402,17 +430,39 @@ export function createSessionStatusTool(opts?: { }); } + if (!resolved && requestedKeyParam === undefined) { + for (const fallbackKey of listImplicitDefaultDirectFallbackKeys({ + keyRaw: requestedKeyRaw, + mainKey, + })) { + resolved = resolveSessionEntry({ + store, + keyRaw: fallbackKey, + alias, + mainKey, + requesterInternalKey: storeScopedRequesterKey, + includeAliasFallback: true, + }); + if (resolved) { + resolvedViaImplicitCurrentFallback = true; + break; + } + } + } + if (!resolved) { const kind = shouldResolveSessionIdInput(requestedKeyRaw) ? "sessionId" : "sessionKey"; throw new Error(`Unknown ${kind}: ${requestedKeyRaw}`); } // Preserve caller-scoped raw-key/current lookups as "self" for visibility checks. - const visibilityTargetKey = - !resolvedViaSessionId && - (requestedKeyInput === "current" || resolved.key === requestedKeyInput) - ? visibilityRequesterKey - : normalizeVisibilityTargetSessionKey(resolved.key, agentId); + const shouldTreatVisibilityTargetAsSelf = + resolvedViaImplicitCurrentFallback || + (!resolvedViaSessionId && + (requestedKeyInput === "current" || resolved.key === requestedKeyInput)); + const visibilityTargetKey = shouldTreatVisibilityTargetAsSelf + ? visibilityRequesterKey + : normalizeVisibilityTargetSessionKey(resolved.key, agentId); const access = visibilityGuard.check(visibilityTargetKey); if (!access.allowed) { throw new Error(access.error);