mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
fix(agents): resolve webchat current session status
* fix(agents): resolve webchat current session status * fix(agents): resolve webchat current session status --------- Co-authored-by: Cornna <96944678+ymylive@users.noreply.github.com> Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
This commit is contained in:
@@ -538,6 +538,99 @@ describe("session_status tool", () => {
|
||||
expect(details.sessionKey).toBe("main");
|
||||
});
|
||||
|
||||
it("resolves webchat sessionKey=current to the full requester main key (#89773)", async () => {
|
||||
resetSessionStore({
|
||||
main: {
|
||||
sessionId: "s-fallback-main",
|
||||
updatedAt: 5,
|
||||
thinkingLevel: "high",
|
||||
},
|
||||
"agent:admin:main": {
|
||||
sessionId: "s-admin-main",
|
||||
updatedAt: 10,
|
||||
thinkingLevel: "low",
|
||||
},
|
||||
});
|
||||
|
||||
const tool = createSessionStatusTool({
|
||||
agentSessionKey: "agent:admin:main",
|
||||
activeDeliveryContext: {
|
||||
channel: "webchat",
|
||||
to: "control-ui-conversation",
|
||||
},
|
||||
config: mockConfig as never,
|
||||
});
|
||||
|
||||
const result = await tool.execute("call-current-webchat-main", { sessionKey: "current" });
|
||||
const details = result.details as { ok?: boolean; sessionKey?: string };
|
||||
expect(details.ok).toBe(true);
|
||||
expect(details.sessionKey).toBe("agent:admin:main");
|
||||
|
||||
const statusArg = mockCallArg(buildStatusMessageMock) as Record<string, unknown>;
|
||||
expectRecordFields(statusArg.sessionEntry, {
|
||||
sessionId: "s-admin-main",
|
||||
thinkingLevel: "low",
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves whitespace-decorated webchat sessionKey=current to the full requester main key (#89800)", async () => {
|
||||
resetSessionStore({
|
||||
main: {
|
||||
sessionId: "s-fallback-main",
|
||||
updatedAt: 5,
|
||||
thinkingLevel: "high",
|
||||
},
|
||||
"agent:admin:main": {
|
||||
sessionId: "s-admin-main",
|
||||
updatedAt: 10,
|
||||
thinkingLevel: "low",
|
||||
},
|
||||
});
|
||||
|
||||
const tool = createSessionStatusTool({
|
||||
agentSessionKey: "agent:admin:main",
|
||||
activeDeliveryContext: {
|
||||
channel: "webchat",
|
||||
to: "control-ui-conversation",
|
||||
},
|
||||
config: mockConfig as never,
|
||||
});
|
||||
|
||||
const result = await tool.execute("call-current-webchat-main-spaced", {
|
||||
sessionKey: " current ",
|
||||
});
|
||||
const details = result.details as { ok?: boolean; sessionKey?: string };
|
||||
expect(details.ok).toBe(true);
|
||||
expect(details.sessionKey).toBe("agent:admin:main");
|
||||
|
||||
const statusArg = mockCallArg(buildStatusMessageMock) as Record<string, unknown>;
|
||||
expectRecordFields(statusArg.sessionEntry, {
|
||||
sessionId: "s-admin-main",
|
||||
thinkingLevel: "low",
|
||||
});
|
||||
});
|
||||
|
||||
it("synthesizes webchat sessionKey=current from the full requester main key (#89773)", async () => {
|
||||
resetSessionStore({});
|
||||
|
||||
const tool = createSessionStatusTool({
|
||||
agentSessionKey: "agent:admin:main",
|
||||
activeDeliveryContext: {
|
||||
channel: "webchat",
|
||||
to: "control-ui-conversation",
|
||||
},
|
||||
config: mockConfig as never,
|
||||
});
|
||||
|
||||
const result = await tool.execute("call-current-webchat-main-unpersisted", {
|
||||
sessionKey: "current",
|
||||
});
|
||||
const details = result.details as { ok?: boolean; sessionKey?: string; statusText?: string };
|
||||
expect(details.ok).toBe(true);
|
||||
expect(details.sessionKey).toBe("agent:admin:main");
|
||||
expect(details.statusText).toContain("OpenClaw");
|
||||
});
|
||||
|
||||
it("uses runSessionKey thinking level for implicit no-arg status lookups (#82669)", async () => {
|
||||
resetSessionStore({
|
||||
"agent:main:telegram:default:direct:1234": {
|
||||
|
||||
@@ -574,15 +574,16 @@ export function createSessionStatusTool(opts?: {
|
||||
if (isImplicitRunSessionStatus) {
|
||||
requestedKeyRaw = opts?.runSessionKey;
|
||||
}
|
||||
let requestedKeyInput = requestedKeyRaw?.trim() ?? "";
|
||||
|
||||
// Track whether this is a semantic-current request (literal "current" or a
|
||||
// current-client alias) BEFORE any rewrite, so visibility treats it as self.
|
||||
const isSemanticCurrentRequest =
|
||||
requestedKeyRaw === "current" ||
|
||||
requestedKeyInput === "current" ||
|
||||
isImplicitRunSessionStatus ||
|
||||
Boolean(
|
||||
resolveCurrentSessionClientAlias({
|
||||
key: requestedKeyRaw ?? "",
|
||||
key: requestedKeyInput,
|
||||
requesterInternalKey: effectiveRequesterKey,
|
||||
}),
|
||||
);
|
||||
@@ -590,23 +591,26 @@ export function createSessionStatusTool(opts?: {
|
||||
// Resolve semantic "current" to the live run session key for lookup purposes (#76708).
|
||||
// In sandboxed channel runs there may be no separate runSessionKey because the sandbox
|
||||
// key already is the live requester; avoid probing literal "current" through the gateway.
|
||||
if (requestedKeyRaw === "current" && (opts?.runSessionKey || opts?.sandboxed === true)) {
|
||||
if (requestedKeyInput === "current" && (opts?.runSessionKey || opts?.sandboxed === true)) {
|
||||
requestedKeyRaw = opts.runSessionKey ?? effectiveRequesterKey;
|
||||
requestedKeyInput = requestedKeyRaw?.trim() ?? "";
|
||||
}
|
||||
|
||||
const currentSessionAlias = resolveCurrentSessionClientAlias({
|
||||
key: requestedKeyRaw ?? "",
|
||||
key: requestedKeyInput,
|
||||
requesterInternalKey: effectiveRequesterKey,
|
||||
});
|
||||
if (currentSessionAlias) {
|
||||
requestedKeyRaw = opts?.runSessionKey ?? currentSessionAlias;
|
||||
requestedKeyInput = requestedKeyRaw?.trim() ?? "";
|
||||
}
|
||||
const requestedKeyInput = requestedKeyRaw?.trim() ?? "";
|
||||
const effectiveRequesterLookupKey = effectiveRequesterKey.trim();
|
||||
let resolvedViaSessionId = false;
|
||||
let resolvedViaImplicitCurrentFallback = false;
|
||||
if (!requestedKeyRaw?.trim()) {
|
||||
if (!requestedKeyInput) {
|
||||
throw new Error("sessionKey required");
|
||||
}
|
||||
requestedKeyRaw = requestedKeyInput;
|
||||
const ensureAgentAccess = (targetAgentId: string) => {
|
||||
if (targetAgentId === requesterAgentId) {
|
||||
return;
|
||||
@@ -622,20 +626,20 @@ export function createSessionStatusTool(opts?: {
|
||||
}
|
||||
};
|
||||
|
||||
if (requestedKeyRaw.startsWith("agent:") && !isSemanticCurrentRequest) {
|
||||
const requestedAgentId = resolveAgentIdFromSessionKey(requestedKeyRaw);
|
||||
if (requestedKeyInput.startsWith("agent:") && !isSemanticCurrentRequest) {
|
||||
const requestedAgentId = resolveAgentIdFromSessionKey(requestedKeyInput);
|
||||
ensureAgentAccess(requestedAgentId);
|
||||
const access = visibilityGuard.check(
|
||||
normalizeVisibilityTargetSessionKey(requestedKeyRaw, requestedAgentId),
|
||||
normalizeVisibilityTargetSessionKey(requestedKeyInput, requestedAgentId),
|
||||
);
|
||||
if (!access.allowed) {
|
||||
throw new Error(access.error);
|
||||
}
|
||||
}
|
||||
|
||||
const isExplicitAgentKey = requestedKeyRaw.startsWith("agent:");
|
||||
const isExplicitAgentKey = requestedKeyInput.startsWith("agent:");
|
||||
let agentId = isExplicitAgentKey
|
||||
? resolveAgentIdFromSessionKey(requestedKeyRaw)
|
||||
? resolveAgentIdFromSessionKey(requestedKeyInput)
|
||||
: requesterAgentId;
|
||||
let storePath = resolveStorePath(cfg.session?.store, { agentId });
|
||||
let store = loadSessionStore(storePath);
|
||||
@@ -652,15 +656,15 @@ export function createSessionStatusTool(opts?: {
|
||||
alias,
|
||||
mainKey,
|
||||
requesterInternalKey: storeScopedRequesterKey,
|
||||
includeAliasFallback: requestedKeyRaw !== "current",
|
||||
includeAliasFallback: requestedKeyInput !== "current",
|
||||
});
|
||||
|
||||
if (
|
||||
!resolved &&
|
||||
(requestedKeyRaw === "current" || shouldResolveSessionIdInput(requestedKeyRaw))
|
||||
(requestedKeyInput === "current" || shouldResolveSessionIdInput(requestedKeyInput))
|
||||
) {
|
||||
const resolvedSession = await resolveSessionReference({
|
||||
sessionKey: requestedKeyRaw,
|
||||
sessionKey: requestedKeyInput,
|
||||
alias,
|
||||
mainKey,
|
||||
requesterInternalKey: effectiveRequesterKey,
|
||||
@@ -671,7 +675,7 @@ export function createSessionStatusTool(opts?: {
|
||||
resolvedSession,
|
||||
requesterSessionKey: effectiveRequesterKey,
|
||||
restrictToSpawned: opts?.sandboxed === true,
|
||||
visibilitySessionKey: requestedKeyRaw,
|
||||
visibilitySessionKey: requestedKeyInput,
|
||||
});
|
||||
if (!visibleSession.ok) {
|
||||
throw new Error("Session status visibility is restricted to the current session tree.");
|
||||
@@ -680,6 +684,7 @@ export function createSessionStatusTool(opts?: {
|
||||
ensureAgentAccess(resolveAgentIdFromSessionKey(visibleSession.key));
|
||||
resolvedViaSessionId = true;
|
||||
requestedKeyRaw = visibleSession.key;
|
||||
requestedKeyInput = requestedKeyRaw.trim();
|
||||
agentId = resolveAgentIdFromSessionKey(visibleSession.key);
|
||||
storePath = resolveStorePath(cfg.session?.store, { agentId });
|
||||
store = loadSessionStore(storePath);
|
||||
@@ -700,7 +705,18 @@ export function createSessionStatusTool(opts?: {
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolved && requestedKeyRaw === "current") {
|
||||
if (!resolved && requestedKeyInput === "current" && effectiveRequesterLookupKey) {
|
||||
resolved = resolveSessionEntry({
|
||||
store,
|
||||
keyRaw: effectiveRequesterLookupKey,
|
||||
alias,
|
||||
mainKey,
|
||||
requesterInternalKey: storeScopedRequesterKey,
|
||||
includeAliasFallback: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (!resolved && requestedKeyInput === "current") {
|
||||
resolved = resolveSessionEntry({
|
||||
store,
|
||||
keyRaw: requestedKeyRaw,
|
||||
@@ -732,12 +748,15 @@ export function createSessionStatusTool(opts?: {
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
const runSessionFallbackKey = opts?.runSessionKey?.trim();
|
||||
const fallback = resolveImplicitCurrentSessionFallback({
|
||||
allowFallback: isSemanticCurrentRequest || requestedKeyParam === undefined,
|
||||
fallbackKey:
|
||||
(isSemanticCurrentRequest || isImplicitRunSessionStatus) && opts?.runSessionKey
|
||||
? opts.runSessionKey
|
||||
: storeScopedRequesterKey,
|
||||
(isSemanticCurrentRequest || isImplicitRunSessionStatus) && runSessionFallbackKey
|
||||
? runSessionFallbackKey
|
||||
: isSemanticCurrentRequest
|
||||
? effectiveRequesterLookupKey
|
||||
: storeScopedRequesterKey,
|
||||
});
|
||||
if (fallback) {
|
||||
resolved = fallback;
|
||||
@@ -746,8 +765,8 @@ export function createSessionStatusTool(opts?: {
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
const kind = shouldResolveSessionIdInput(requestedKeyRaw) ? "sessionId" : "sessionKey";
|
||||
throw new Error(`Unknown ${kind}: ${requestedKeyRaw}`);
|
||||
const kind = shouldResolveSessionIdInput(requestedKeyInput) ? "sessionId" : "sessionKey";
|
||||
throw new Error(`Unknown ${kind}: ${requestedKeyInput}`);
|
||||
}
|
||||
|
||||
// Preserve caller-scoped raw-key/current lookups as "self" for visibility checks.
|
||||
|
||||
Reference in New Issue
Block a user