fix: preserve session visibility semantics for runSessionKey (#76708)

- Track isSemanticCurrentRequest before key rewrite
- Skip early explicit-agent-key visibility check for current requests
- Add isSemanticCurrentRequest to shouldTreatVisibilityTargetAsSelf
- Fix test to use default/tree visibility instead of 'all'
- Add negative guard test for explicit cross-session key rejection
This commit is contained in:
Alex Knight
2026-05-04 09:43:09 +10:00
committed by Alex Knight
parent 066980d5a5
commit abe204f6e0
2 changed files with 45 additions and 14 deletions

View File

@@ -492,7 +492,7 @@ describe("session_status tool", () => {
expect(details.sessionKey).toBe("main");
});
it("resolves sessionKey=current to runSessionKey when sandbox key differs from live session (#76708)", async () => {
it("resolves sessionKey=current to runSessionKey under default tree visibility (#76708)", async () => {
resetSessionStore({
"agent:main:telegram:default:direct:1234": {
sessionId: "s-tg-direct",
@@ -506,15 +506,13 @@ describe("session_status tool", () => {
},
});
// The tool is constructed with the Telegram sandbox key as agentSessionKey
// but the actual live run session key as runSessionKey.
// Default visibility is "tree". The tool is constructed with the Telegram
// sandbox key as agentSessionKey but the live run session key as runSessionKey.
// semantic-current must be treated as self for visibility purposes.
const tool = createSessionStatusTool({
agentSessionKey: "agent:main:telegram:default:direct:1234",
runSessionKey: "agent:main:main",
config: {
...mockConfig,
tools: { ...mockConfig.tools, sessions: { visibility: "all" } },
} as never,
config: mockConfig as never,
});
const result = await tool.execute("call-current-run-session", { sessionKey: "current" });
@@ -523,6 +521,32 @@ describe("session_status tool", () => {
expect(details.sessionKey).toBe("agent:main:main");
});
it("rejects explicit cross-session key under tree visibility even when it equals runSessionKey (#76708)", async () => {
resetSessionStore({
"agent:main:telegram:default:direct:1234": {
sessionId: "s-tg-direct",
updatedAt: 5,
status: "done",
},
"agent:main:main": {
sessionId: "s-main",
updatedAt: 10,
status: "running",
},
});
// Same setup but with an explicit key — should NOT bypass visibility.
const tool = createSessionStatusTool({
agentSessionKey: "agent:main:telegram:default:direct:1234",
runSessionKey: "agent:main:main",
config: mockConfig as never,
});
await expect(
tool.execute("call-explicit-key", { sessionKey: "agent:main:main" }),
).rejects.toThrow(/visibility is restricted/);
});
it("treats the TUI client label as the current requester session", async () => {
resetSessionStore({
"agent:main:main": {

View File

@@ -353,9 +353,18 @@ export function createSessionStatusTool(opts?: {
const requestedKeyParam = readStringParam(params, "sessionKey");
let requestedKeyRaw = requestedKeyParam ?? opts?.agentSessionKey;
// When sessionKey is literally "current" and a runSessionKey is provided,
// resolve directly to the live run session instead of falling through to
// stale sandbox/policy key resolution (#76708).
// 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" ||
Boolean(
resolveCurrentSessionClientAlias({
key: requestedKeyRaw ?? "",
requesterInternalKey: effectiveRequesterKey,
}),
);
// Resolve "current" to the live run session key for lookup purposes (#76708).
if (requestedKeyRaw === "current" && opts?.runSessionKey) {
requestedKeyRaw = opts.runSessionKey;
}
@@ -365,9 +374,6 @@ export function createSessionStatusTool(opts?: {
requesterInternalKey: effectiveRequesterKey,
});
if (currentSessionAlias) {
// When a runSessionKey is provided (e.g. the live run session key), prefer it
// over the sandbox/policy key so "current" resolves to the active run session
// instead of a stale sandbox key (e.g. a Telegram direct peer key).
requestedKeyRaw = opts?.runSessionKey ?? currentSessionAlias;
}
const requestedKeyInput = requestedKeyRaw?.trim() ?? "";
@@ -391,7 +397,7 @@ export function createSessionStatusTool(opts?: {
}
};
if (requestedKeyRaw.startsWith("agent:")) {
if (requestedKeyRaw.startsWith("agent:") && !isSemanticCurrentRequest) {
const requestedAgentId = resolveAgentIdFromSessionKey(requestedKeyRaw);
ensureAgentAccess(requestedAgentId);
const access = visibilityGuard.check(
@@ -518,6 +524,7 @@ export function createSessionStatusTool(opts?: {
// Preserve caller-scoped raw-key/current lookups as "self" for visibility checks.
const shouldTreatVisibilityTargetAsSelf =
isSemanticCurrentRequest ||
resolvedViaImplicitCurrentFallback ||
(!resolvedViaSessionId &&
(requestedKeyInput === "current" || resolved.key === requestedKeyInput));