import { afterEach, describe, expect, it } from "vitest"; import { withTempDir } from "../test-helpers/temp-dir.js"; import { findLatestTaskForRelatedSessionKeyForOwner, findTaskByRunIdForOwner, getTaskByIdForOwner, resolveTaskForLookupTokenForOwner, } from "./task-owner-access.js"; import { createTaskRecord, resetTaskRegistryForTests } from "./task-registry.js"; const ORIGINAL_STATE_DIR = process.env.OPENCLAW_STATE_DIR; afterEach(() => { resetTaskRegistryForTests({ persist: false }); if (ORIGINAL_STATE_DIR == null) { delete process.env.OPENCLAW_STATE_DIR; } else { process.env.OPENCLAW_STATE_DIR = ORIGINAL_STATE_DIR; } }); async function withTaskRegistryTempDir(run: () => Promise | T): Promise { return await withTempDir({ prefix: "openclaw-task-owner-access-" }, async (root) => { const previousStateDir = process.env.OPENCLAW_STATE_DIR; process.env.OPENCLAW_STATE_DIR = root; resetTaskRegistryForTests({ persist: false }); try { return await run(); } finally { resetTaskRegistryForTests({ persist: false }); if (previousStateDir == null) { delete process.env.OPENCLAW_STATE_DIR; } else { process.env.OPENCLAW_STATE_DIR = previousStateDir; } } }); } describe("task owner access", () => { it("returns owner-scoped tasks for owner and child-session lookups", async () => { await withTaskRegistryTempDir(() => { const task = createTaskRecord({ runtime: "subagent", ownerKey: "agent:main:main", scopeKind: "session", childSessionKey: "agent:main:subagent:child-1", runId: "owner-visible-run", task: "Owner visible task", status: "running", }); expect( findLatestTaskForRelatedSessionKeyForOwner({ relatedSessionKey: "agent:main:subagent:child-1", callerOwnerKey: "agent:main:main", })?.taskId, ).toBe(task.taskId); expect( findTaskByRunIdForOwner({ runId: "owner-visible-run", callerOwnerKey: "agent:main:main", })?.taskId, ).toBe(task.taskId); }); }); it("denies cross-owner task reads", async () => { await withTaskRegistryTempDir(() => { const task = createTaskRecord({ runtime: "acp", ownerKey: "agent:main:main", scopeKind: "session", childSessionKey: "agent:main:acp:child-1", runId: "owner-hidden-run", task: "Hidden task", status: "queued", }); expect( getTaskByIdForOwner({ taskId: task.taskId, callerOwnerKey: "agent:main:subagent:other-parent", }), ).toBeUndefined(); expect( findTaskByRunIdForOwner({ runId: "owner-hidden-run", callerOwnerKey: "agent:main:subagent:other-parent", }), ).toBeUndefined(); expect( resolveTaskForLookupTokenForOwner({ token: "agent:main:acp:child-1", callerOwnerKey: "agent:main:subagent:other-parent", }), ).toBeUndefined(); }); }); it("requires an exact owner-key match", async () => { await withTaskRegistryTempDir(() => { const task = createTaskRecord({ runtime: "acp", ownerKey: "agent:main:MixedCase", scopeKind: "session", runId: "case-sensitive-owner-run", task: "Case-sensitive owner", status: "queued", }); expect( getTaskByIdForOwner({ taskId: task.taskId, callerOwnerKey: "agent:main:mixedcase", }), ).toBeUndefined(); }); }); it("does not expose system-owned tasks through owner-scoped readers", async () => { await withTaskRegistryTempDir(() => { const task = createTaskRecord({ runtime: "cron", ownerKey: "system:cron:nightly", scopeKind: "system", requesterSessionKey: "system:cron:nightly", childSessionKey: "agent:main:cron:nightly", runId: "system-task-run", task: "Nightly cron", status: "running", deliveryStatus: "not_applicable", }); expect( getTaskByIdForOwner({ taskId: task.taskId, callerOwnerKey: "agent:main:main", }), ).toBeUndefined(); expect( resolveTaskForLookupTokenForOwner({ token: "system-task-run", callerOwnerKey: "agent:main:main", }), ).toBeUndefined(); }); }); });