diff --git a/dist/index.js b/dist/index.js index 18c6561..7dcfb71 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,6 +1,6 @@ import { getPluginRuntimeGatewayRequestScope } from "openclaw/plugin-sdk/plugin-runtime"; import { collectAndSnapshotXWorkmateArtifacts, exportXWorkmateArtifacts, prepareXWorkmateArtifacts, readXWorkmateArtifact, formatArtifactManifestMarkdown, } from "./src/exportArtifacts.js"; -import { getXWorkmateTaskSnapshot, recordXWorkmateSessionMapping, registerXWorkmateDetachedTaskRuntime, registerXWorkmateSessionExtension, } from "./src/taskState.js"; +import { getXWorkmateTaskSnapshot, recordXWorkmateSessionMapping, registerXWorkmateSessionExtension, } from "./src/taskState.js"; function scopedGatewayParams(params) { const sessionScope = getPluginRuntimeGatewayRequestScope()?.sessionScope; const runScope = resolveRunScope({ sessionScope }); @@ -9,7 +9,7 @@ function scopedGatewayParams(params) { } return { ...params, - sessionKey: runScope.sessionKey, + openclawSessionKey: runScope.sessionKey, runId: runScope.runId, ...(runScope.workspaceDir ? { workspaceDir: runScope.workspaceDir } : {}), ...(runScope.artifactScope ? { artifactScope: runScope.artifactScope } : {}), @@ -40,13 +40,11 @@ const plugin = { }; export default plugin; function register(api) { - const taskStore = {}; registerXWorkmateSessionExtension(api); - registerXWorkmateDetachedTaskRuntime(api, taskStore); api.registerHook("session_start", async (event) => { try { const params = scopedGatewayParams(event?.context ?? event); - const openclawSessionKey = stringParam(params.openclawSessionKey) || stringParam(params.sessionKey); + const openclawSessionKey = stringParam(params.openclawSessionKey); if (openclawSessionKey && params.runId) { const hookParams = { ...params, openclawSessionKey }; const prepared = await prepareXWorkmateArtifacts({ @@ -56,7 +54,6 @@ function register(api) { }); await recordXWorkmateSessionMapping({ api, - taskStore, params: hookParams, artifactScope: prepared.artifactScope, source: "session_start", @@ -72,7 +69,6 @@ function register(api) { const params = scopedGatewayParams(opts.params); const mapping = await recordXWorkmateSessionMapping({ api, - taskStore, params, source: "bridge_prepare", }); @@ -104,7 +100,6 @@ function register(api) { try { const payload = await getXWorkmateTaskSnapshot({ api, - taskStore, params: scopedGatewayParams(opts.params), }); opts.respond(true, payload, undefined); @@ -238,10 +233,10 @@ function createXWorkmateArtifactsTool(api, ctx) { throw new Error("runId required"); } const workspaceDir = ctx.sessionScope?.workspaceDir || ctx.workspaceDir; - const { sessionKey: _ignoredSessionKey, runId: _ignoredRunId, workspaceDir: _ignoredWorkspaceDir, ...operationParams } = params; + const { sessionKey: _ignoredSessionKey, openclawSessionKey: _ignoredOpenclawSessionKey, runId: _ignoredRunId, workspaceDir: _ignoredWorkspaceDir, ...operationParams } = params; const baseParams = { ...operationParams, - sessionKey, + openclawSessionKey: sessionKey, runId, ...(workspaceDir ? { workspaceDir } : {}), ...(runScope?.artifactScope ? { artifactScope: runScope.artifactScope } : {}), diff --git a/dist/src/exportArtifacts.js b/dist/src/exportArtifacts.js index 05f2820..7dcbaa7 100644 --- a/dist/src/exportArtifacts.js +++ b/dist/src/exportArtifacts.js @@ -21,7 +21,7 @@ export async function prepareXWorkmateArtifacts(input) { const params = input.params ?? {}; const pluginConfig = input.pluginConfig ?? {}; const runId = requiredString(params.runId, "runId required"); - const sessionKey = requiredString(params.openclawSessionKey ?? params.sessionKey, "openclawSessionKey required"); + const sessionKey = requiredString(params.openclawSessionKey, "openclawSessionKey required"); const expectedArtifactDirs = normalizeExpectedArtifactDirs(params.expectedArtifactDirs); const expectedArtifactScope = artifactScopeFor(sessionKey, runId); const requestedArtifactScope = optionalArtifactScope(params.artifactScope); @@ -57,7 +57,7 @@ export async function collectAndSnapshotXWorkmateArtifacts(input) { const params = input.params ?? {}; const pluginConfig = input.pluginConfig ?? {}; const runId = requiredString(params.runId, "runId required"); - const sessionKey = requiredString(params.openclawSessionKey ?? params.sessionKey, "openclawSessionKey required"); + const sessionKey = requiredString(params.openclawSessionKey, "openclawSessionKey required"); const sinceUnixMs = nonNegativeNumber(params.sinceUnixMs, 0); const maxFiles = positiveInteger(params.maxFiles, pluginConfig.snapshotMaxFiles, DEFAULT_MAX_FILES); const expectedArtifactScope = artifactScopeFor(sessionKey, runId); @@ -124,7 +124,7 @@ export async function exportXWorkmateArtifacts(input) { const params = input.params ?? {}; const pluginConfig = input.pluginConfig ?? {}; const runId = requiredString(params.runId, "runId required"); - const sessionKey = requiredString(params.openclawSessionKey ?? params.sessionKey, "openclawSessionKey required"); + const sessionKey = requiredString(params.openclawSessionKey, "openclawSessionKey required"); const maxFiles = positiveInteger(params.maxFiles, pluginConfig.maxFiles, DEFAULT_MAX_FILES); const maxInlineBytes = nonNegativeInteger(params.maxInlineBytes, pluginConfig.maxInlineBytes, DEFAULT_MAX_INLINE_BYTES); const sinceUnixMs = nonNegativeNumber(params.sinceUnixMs, 0); @@ -157,7 +157,9 @@ export async function exportXWorkmateArtifacts(input) { const scopeStat = await fs.stat(scopeRoot); effectiveSince = Math.min(sinceUnixMs, scopeStat.birthtimeMs || scopeStat.mtimeMs); } - catch { } + catch (error) { + warnings.push(`Unable to read artifact scope timestamp: ${String(error)}`); + } } const scopedCandidates = (await directoryExists(scopeRoot)) ? await collectCandidates({ @@ -256,7 +258,7 @@ export async function readXWorkmateArtifact(input) { const params = input.params ?? {}; const pluginConfig = input.pluginConfig ?? {}; const runId = requiredString(params.runId, "runId required"); - const sessionKey = requiredString(params.openclawSessionKey ?? params.sessionKey, "openclawSessionKey required"); + const sessionKey = requiredString(params.openclawSessionKey, "openclawSessionKey required"); const expectedArtifactScope = artifactScopeFor(sessionKey, runId); const expectedSessionScope = taskSessionScopeFor(sessionKey); const requestedArtifactRef = optionalString(params.artifactRef); diff --git a/dist/src/taskState.d.ts b/dist/src/taskState.d.ts index b3637ad..3ddcd14 100644 --- a/dist/src/taskState.d.ts +++ b/dist/src/taskState.d.ts @@ -28,13 +28,9 @@ export type XWorkmateTaskLookupError = { mapping?: XWorkmateSessionMappingV1; expectedArtifactDirs?: string[]; }; -export type XWorkmateTaskStore = Record; -export declare function createXWorkmateTaskStore(): XWorkmateTaskStore; export declare function registerXWorkmateSessionExtension(api: OpenClawPluginApi): void; -export declare function registerXWorkmateDetachedTaskRuntime(_api: OpenClawPluginApi, _taskStore: XWorkmateTaskStore): void; export declare function recordXWorkmateSessionMapping(input: { api: OpenClawPluginApi; - taskStore?: XWorkmateTaskStore; params: Record; artifactScope?: string; source?: XWorkmateSessionMappingSource; @@ -52,6 +48,5 @@ export declare function readXWorkmateSessionMapping(api: OpenClawPluginApi, look }): Promise; export declare function getXWorkmateTaskSnapshot(input: { api: OpenClawPluginApi; - taskStore?: XWorkmateTaskStore; params: Record; }): Promise>; diff --git a/dist/src/taskState.js b/dist/src/taskState.js index b2881a7..904e7f4 100644 --- a/dist/src/taskState.js +++ b/dist/src/taskState.js @@ -1,9 +1,6 @@ import { exportXWorkmateArtifacts } from "./exportArtifacts.js"; export const XWORKMATE_PLUGIN_ID = "openclaw-multi-session-plugins"; export const XWORKMATE_SESSION_EXTENSION_NAMESPACE = "xworkmate.sessionMapping"; -export function createXWorkmateTaskStore() { - return {}; -} export function registerXWorkmateSessionExtension(api) { const registerExtension = api.session?.state?.registerSessionExtension ?? api.registerSessionExtension; if (typeof registerExtension !== "function") { @@ -19,9 +16,6 @@ export function registerXWorkmateSessionExtension(api) { }, }); } -export function registerXWorkmateDetachedTaskRuntime(_api, _taskStore) { - // OpenClaw native task-registry is the only task status source for this plugin. -} export async function recordXWorkmateSessionMapping(input) { const metadata = normalizeXWorkmateTaskMetadataV1(input.params); const openclawSessionKey = requiredString(input.params.openclawSessionKey ?? metadata.openclawSessionKey, "openclawSessionKey required"); diff --git a/index.test.ts b/index.test.ts index a70f07d..612c0ea 100644 --- a/index.test.ts +++ b/index.test.ts @@ -92,12 +92,12 @@ describe("plugin registration", () => { openclawSessionKey: "thread-main", runId: "turn-1", }); - console.log(prepared); expect(prepared.ok).toBe(true); + expect(prepared.ok).toBe(true); expect(prepared.payload?.artifactScope).toBe("tasks/thread-main/turn-1"); const artifactDirectory = String(prepared.payload?.artifactDirectory); const emptyExport = await callGatewayMethod(methods, "xworkmate.artifacts.export", { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.payload?.artifactScope, }); @@ -109,7 +109,7 @@ describe("plugin registration", () => { await fs.promises.writeFile(path.join(artifactDirectory, "reports", "final.md"), "final"); const listed = await callGatewayMethod(methods, "xworkmate.artifacts.list", { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.payload?.artifactScope, }); @@ -119,7 +119,7 @@ describe("plugin registration", () => { expect(listedArtifacts[0]).not.toHaveProperty("content"); const read = await callGatewayMethod(methods, "xworkmate.artifacts.read", { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.payload?.artifactScope, relativePath: "reports/final.md", @@ -128,7 +128,7 @@ describe("plugin registration", () => { expect(read.payload?.artifacts).toMatchObject([{ relativePath: "reports/final.md", encoding: "base64" }]); const unprepared = await callGatewayMethod(methods, "xworkmate.artifacts.export", { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-unprepared", }); expect(unprepared.ok).toBe(true); @@ -211,15 +211,21 @@ describe("plugin registration", () => { sessionEntrySlotKey: "xworkmate", }); const projected = (sessionExtensions[0]?.project as (ctx: Record) => unknown)({ - sessionKey: "draft:1780636411666238-3", + openclawSessionKey: "draft:1780636411666238-3", state: {}, }); expect(projected).toMatchObject({}); expect(detachedRuntimes).toHaveLength(0); + await hooks.get("session_start")?.({ + appThreadKey: "draft:legacy-session-key-only", + sessionKey: "draft:legacy-session-key-only", + runId: "turn-legacy", + }); + expect(sessionExtensionPatches).toHaveLength(0); + await hooks.get("session_start")?.({ appThreadKey: "draft:1780636411666238-3", - sessionKey: "draft-1780636411666238-3", openclawSessionKey: "draft:1780636411666238-3", threadId: "draft-1780636411666238-3", runId: "turn-1", @@ -302,11 +308,11 @@ describe("plugin registration", () => { it("uses host context scope for the optional agent tool", async () => { const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-tool-")); const current = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); const other = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-2" }, + params: { openclawSessionKey: "thread-main", runId: "turn-2" }, pluginConfig: { workspaceDir: root }, }); await fs.promises.writeFile(path.join(current.artifactDirectory, "current.txt"), "current"); @@ -340,7 +346,7 @@ describe("plugin registration", () => { }); const result = await tool.execute("call-1", { action: "list", - sessionKey: "thread-other", + openclawSessionKey: "thread-other", runId: "turn-2", workspaceDir: "/", }); diff --git a/index.ts b/index.ts index 24979b3..ec5ab8b 100644 --- a/index.ts +++ b/index.ts @@ -14,7 +14,6 @@ import { import { getXWorkmateTaskSnapshot, recordXWorkmateSessionMapping, - registerXWorkmateDetachedTaskRuntime, registerXWorkmateSessionExtension, } from "./src/taskState.js"; @@ -53,7 +52,7 @@ function scopedGatewayParams(params: Record): Record { try { const params = scopedGatewayParams(event?.context ?? event); - const openclawSessionKey = stringParam(params.openclawSessionKey) || stringParam(params.sessionKey); + const openclawSessionKey = stringParam(params.openclawSessionKey); if (openclawSessionKey && params.runId) { const hookParams = { ...params, openclawSessionKey }; const prepared = await prepareXWorkmateArtifacts({ @@ -113,7 +110,6 @@ function register(api: OpenClawPluginApi) { }); await recordXWorkmateSessionMapping({ api, - taskStore, params: hookParams, artifactScope: prepared.artifactScope, source: "session_start", @@ -131,7 +127,6 @@ function register(api: OpenClawPluginApi) { const params = scopedGatewayParams(opts.params); const mapping = await recordXWorkmateSessionMapping({ api, - taskStore, params, source: "bridge_prepare", }); @@ -167,7 +162,6 @@ function register(api: OpenClawPluginApi) { try { const payload = await getXWorkmateTaskSnapshot({ api, - taskStore, params: scopedGatewayParams(opts.params), }); opts.respond(true, payload, undefined); @@ -303,13 +297,14 @@ function createXWorkmateArtifactsTool( const workspaceDir = ctx.sessionScope?.workspaceDir || ctx.workspaceDir; const { sessionKey: _ignoredSessionKey, + openclawSessionKey: _ignoredOpenclawSessionKey, runId: _ignoredRunId, workspaceDir: _ignoredWorkspaceDir, ...operationParams } = params; const baseParams = { ...operationParams, - sessionKey, + openclawSessionKey: sessionKey, runId, ...(workspaceDir ? { workspaceDir } : {}), ...(runScope?.artifactScope ? { artifactScope: runScope.artifactScope } : {}), diff --git a/src/exportArtifacts.test.ts b/src/exportArtifacts.test.ts index 25d1ed3..5ecbf1a 100644 --- a/src/exportArtifacts.test.ts +++ b/src/exportArtifacts.test.ts @@ -15,11 +15,11 @@ describe("exportXWorkmateArtifacts", () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const first = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); const second = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-2" }, + params: { openclawSessionKey: "thread-main", runId: "turn-2" }, pluginConfig: { workspaceDir: root }, }); @@ -31,11 +31,22 @@ describe("exportXWorkmateArtifacts", () => { expect(first.scopeKind).toBe("task"); }); + it("rejects legacy sessionKey artifact params", async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); + + await expect( + prepareXWorkmateArtifacts({ + params: { sessionKey: "thread-main", runId: "turn-1" }, + pluginConfig: { workspaceDir: root }, + }), + ).rejects.toThrow("openclawSessionKey required"); + }); + it("normalizes task scope segments like the OpenClaw session scope runtime", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "agent::main:main", runId: "run alpha" }, + params: { openclawSessionKey: "agent::main:main", runId: "run alpha" }, pluginConfig: { workspaceDir: root }, }); @@ -45,7 +56,7 @@ describe("exportXWorkmateArtifacts", () => { it("exports changed files with metadata and base64 content", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "run-1" }, + params: { openclawSessionKey: "thread-main", runId: "run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(prepared.artifactDirectory, "reports"), { recursive: true }); @@ -55,7 +66,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", sinceUnixMs: stat.mtimeMs - 1, }, @@ -84,7 +95,7 @@ describe("exportXWorkmateArtifacts", () => { const prepared = await prepareXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-expected", expectedArtifactDirs: ["artifacts/", "assets/images"], }, @@ -92,7 +103,7 @@ describe("exportXWorkmateArtifacts", () => { }); const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-expected", artifactScope: prepared.artifactScope, expectedArtifactDirs: ["artifacts/", "assets/images"], @@ -114,7 +125,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( prepareXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-unsafe", expectedArtifactDirs: ["../outside"], }, @@ -130,7 +141,7 @@ describe("exportXWorkmateArtifacts", () => { const mediaRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-media-")); const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-global-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "run-1" }, + params: { openclawSessionKey: "thread-main", runId: "run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(mediaRoot, "browser"), { recursive: true }); @@ -144,7 +155,7 @@ describe("exportXWorkmateArtifacts", () => { const snapshot = await collectAndSnapshotXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", artifactScope: prepared.artifactScope, sinceUnixMs: snapshotSinceUnixMs, @@ -165,7 +176,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", artifactScope: prepared.artifactScope, includeContent: false, @@ -182,7 +193,7 @@ describe("exportXWorkmateArtifacts", () => { it("skips excluded directories and symlinks", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "run-1" }, + params: { openclawSessionKey: "thread-main", runId: "run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(prepared.artifactDirectory, ".git"), { recursive: true }); @@ -194,7 +205,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", }, pluginConfig: { workspaceDir: root }, @@ -207,7 +218,7 @@ describe("exportXWorkmateArtifacts", () => { it("applies artifact-ignore.md inside the current task scope", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "run-1" }, + params: { openclawSessionKey: "thread-main", runId: "run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(prepared.artifactDirectory, "tmp"), { recursive: true }); @@ -234,7 +245,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", }, pluginConfig: { workspaceDir: root }, @@ -246,11 +257,11 @@ describe("exportXWorkmateArtifacts", () => { it("exports only files inside a task artifact scope", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const first = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); const second = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-2" }, + params: { openclawSessionKey: "thread-main", runId: "turn-2" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(first.artifactDirectory, "reports"), { recursive: true }); @@ -260,7 +271,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: first.artifactScope, }, @@ -279,7 +290,7 @@ describe("exportXWorkmateArtifacts", () => { it("exports nested dist and build deliverables inside the task scope", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(prepared.artifactDirectory, "dist"), { recursive: true }); @@ -289,7 +300,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", maxInlineBytes: 0, }, @@ -305,11 +316,11 @@ describe("exportXWorkmateArtifacts", () => { it("uses the current task scope when artifactScope is omitted", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const current = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); const other = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-2" }, + params: { openclawSessionKey: "thread-main", runId: "turn-2" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(root, "global.txt"), "global"); @@ -318,7 +329,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", }, pluginConfig: { workspaceDir: root }, @@ -332,14 +343,14 @@ describe("exportXWorkmateArtifacts", () => { it("does not scan the workspace root without a current-run timestamp", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(root, "global.txt"), "global"); const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", }, pluginConfig: { workspaceDir: root }, @@ -356,7 +367,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "draft-article", + openclawSessionKey: "draft-article", runId: "openclaw-run-1", sinceUnixMs, maxInlineBytes: 0, @@ -372,7 +383,7 @@ describe("exportXWorkmateArtifacts", () => { it("exports explicitly expected artifact dirs when the task scope is empty", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "draft-article", runId: "openclaw-run-1" }, + params: { openclawSessionKey: "draft-article", runId: "openclaw-run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(root, "assets", "images"), { recursive: true }); @@ -383,7 +394,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "draft-article", + openclawSessionKey: "draft-article", runId: "openclaw-run-1", artifactScope: prepared.artifactScope, expectedArtifactDirs: ["assets/images", "reports"], @@ -403,7 +414,7 @@ describe("exportXWorkmateArtifacts", () => { it("keeps scoped artifacts authoritative over expected artifact dirs", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "draft-article", runId: "openclaw-run-1" }, + params: { openclawSessionKey: "draft-article", runId: "openclaw-run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(prepared.artifactDirectory, "reports"), { recursive: true }); @@ -413,7 +424,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "draft-article", + openclawSessionKey: "draft-article", runId: "openclaw-run-1", artifactScope: prepared.artifactScope, expectedArtifactDirs: ["reports"], @@ -428,7 +439,7 @@ describe("exportXWorkmateArtifacts", () => { it("does not adopt old workspace root files into a later task scope", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(root, "old-root.md"), "old"); @@ -436,7 +447,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", sinceUnixMs: stat.mtimeMs + 10_000, }, @@ -450,18 +461,18 @@ describe("exportXWorkmateArtifacts", () => { it("rejects scoped exports that do not match the requested session/run", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const first = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-2" }, + params: { openclawSessionKey: "thread-main", runId: "turn-2" }, pluginConfig: { workspaceDir: root }, }); await expect( exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-2", artifactScope: first.artifactScope, }, @@ -473,11 +484,11 @@ describe("exportXWorkmateArtifacts", () => { it("does not adopt old workspace files when the scoped directory is empty", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); const otherTask = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-2" }, + params: { openclawSessionKey: "thread-main", runId: "turn-2" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(root, "existing.pdf"), "pdf"); @@ -488,7 +499,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.artifactScope, sinceUnixMs: stat.mtimeMs + 10_000, @@ -505,11 +516,11 @@ describe("exportXWorkmateArtifacts", () => { it("does not borrow previous session task files when current task scope is empty", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const previousTask = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-previous" }, + params: { openclawSessionKey: "thread-main", runId: "turn-previous" }, pluginConfig: { workspaceDir: root }, }); await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-follow-up" }, + params: { openclawSessionKey: "thread-main", runId: "turn-follow-up" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(previousTask.artifactDirectory, "k8s-networking.pdf"), "pdf"); @@ -517,7 +528,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-follow-up", sinceUnixMs: Date.now() + 10_000, }, @@ -534,7 +545,7 @@ describe("exportXWorkmateArtifacts", () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); await prepareXWorkmateArtifacts({ params: { - sessionKey: "draft:1779524982823421-3", + openclawSessionKey: "draft:1779524982823421-3", runId: "turn-1779685283403237342", }, pluginConfig: { workspaceDir: root }, @@ -561,7 +572,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "draft:1779524982823421-3", + openclawSessionKey: "draft:1779524982823421-3", runId: "turn-1779685283403237342", sinceUnixMs: Date.now() + 10_000, }, @@ -594,7 +605,7 @@ describe("exportXWorkmateArtifacts", () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); await prepareXWorkmateArtifacts({ params: { - sessionKey: "draft:1779524982823421-3", + openclawSessionKey: "draft:1779524982823421-3", runId: "turn-1779685283403237342", }, pluginConfig: { workspaceDir: root }, @@ -605,7 +616,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "draft:1779524982823421-3", + openclawSessionKey: "draft:1779524982823421-3", runId: "turn-1779685283403237342", }, pluginConfig: { workspaceDir: root }, @@ -630,15 +641,15 @@ describe("exportXWorkmateArtifacts", () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await Promise.all([ prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-a", runId: "turn-1" }, + params: { openclawSessionKey: "thread-a", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }), prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-b", runId: "turn-1" }, + params: { openclawSessionKey: "thread-b", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }), prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-a", runId: "turn-2" }, + params: { openclawSessionKey: "thread-a", runId: "turn-2" }, pluginConfig: { workspaceDir: root }, }), ]); @@ -648,15 +659,15 @@ describe("exportXWorkmateArtifacts", () => { const results = await Promise.all([ exportXWorkmateArtifacts({ - params: { sessionKey: "thread-a", runId: "turn-1" }, + params: { openclawSessionKey: "thread-a", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }), exportXWorkmateArtifacts({ - params: { sessionKey: "thread-b", runId: "turn-1" }, + params: { openclawSessionKey: "thread-b", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }), exportXWorkmateArtifacts({ - params: { sessionKey: "thread-a", runId: "turn-2" }, + params: { openclawSessionKey: "thread-a", runId: "turn-2" }, pluginConfig: { workspaceDir: root }, }), ]); @@ -672,14 +683,14 @@ describe("exportXWorkmateArtifacts", () => { it("leaves oversized artifacts out of inline content", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "run-1" }, + params: { openclawSessionKey: "thread-main", runId: "run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(prepared.artifactDirectory, "large.pdf"), Buffer.from("large-content")); const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", maxInlineBytes: 2, }, @@ -695,14 +706,14 @@ describe("exportXWorkmateArtifacts", () => { it("can list artifacts without inline content", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "run-1" }, + params: { openclawSessionKey: "thread-main", runId: "run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(prepared.artifactDirectory, "small.txt"), "small"); const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", maxInlineBytes: 0, }, @@ -718,7 +729,7 @@ describe("exportXWorkmateArtifacts", () => { it("limits exported files", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "run-1" }, + params: { openclawSessionKey: "thread-main", runId: "run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(prepared.artifactDirectory, "a.txt"), "a"); @@ -726,7 +737,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", maxFiles: 1, }, @@ -742,14 +753,14 @@ describe("exportXWorkmateArtifacts", () => { const agentRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-agent-")); await fs.writeFile(path.join(mainRoot, "main.txt"), "main"); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "agent:research:thread-1", runId: "run-1" }, + params: { openclawSessionKey: "agent:research:thread-1", runId: "run-1" }, pluginConfig: { workspaceDir: agentRoot }, }); await fs.writeFile(path.join(prepared.artifactDirectory, "agent.txt"), "agent"); const result = await exportXWorkmateArtifacts({ params: { - sessionKey: "agent:research:thread-1", + openclawSessionKey: "agent:research:thread-1", runId: "run-1", }, config: { @@ -771,7 +782,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", relativePath: "reports/final.txt", }, @@ -783,7 +794,7 @@ describe("exportXWorkmateArtifacts", () => { it("reads one artifact inside a task artifact scope", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.mkdir(path.join(prepared.artifactDirectory, "reports"), { recursive: true }); @@ -791,7 +802,7 @@ describe("exportXWorkmateArtifacts", () => { const result = await readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.artifactScope, relativePath: "reports/final.txt", @@ -814,7 +825,7 @@ describe("exportXWorkmateArtifacts", () => { it("rejects direct reads from another run artifact scope", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const first = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(first.artifactDirectory, "first.txt"), "first"); @@ -822,7 +833,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-2", artifactScope: first.artifactScope, relativePath: "first.txt", @@ -835,13 +846,13 @@ describe("exportXWorkmateArtifacts", () => { it("rejects signed task artifact refs from another session", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(prepared.artifactDirectory, "first.txt"), "first"); const exported = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.artifactScope, }, @@ -851,7 +862,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-other", + openclawSessionKey: "thread-other", runId: "turn-1", artifactRef: exported.artifacts[0]?.artifactRef, }, @@ -863,14 +874,14 @@ describe("exportXWorkmateArtifacts", () => { it("rejects signed task artifact refs from another run", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(prepared.artifactDirectory, "existing.txt"), "existing"); const exported = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.artifactScope, }, @@ -880,7 +891,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-2", artifactRef: exported.artifacts[0]?.artifactRef, }, @@ -892,13 +903,13 @@ describe("exportXWorkmateArtifacts", () => { it("rejects tampered artifact refs", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "run-1" }, + params: { openclawSessionKey: "thread-main", runId: "run-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(prepared.artifactDirectory, "existing.txt"), "existing"); const exported = await exportXWorkmateArtifacts({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", }, pluginConfig: { workspaceDir: root }, @@ -909,7 +920,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", artifactRef: tampered, }, @@ -938,7 +949,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", artifactRef: legacyRef, }, @@ -950,14 +961,14 @@ describe("exportXWorkmateArtifacts", () => { it("reads artifact metadata without inline content when the file exceeds the limit", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.writeFile(path.join(prepared.artifactDirectory, "large.bin"), Buffer.from("large-content")); const result = await readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.artifactScope, relativePath: "large.bin", @@ -981,14 +992,14 @@ describe("exportXWorkmateArtifacts", () => { it("rejects relative path traversal when reading artifacts", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.artifactScope, relativePath: "../outside.txt", @@ -1004,7 +1015,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "run-1", artifactScope: "../outside", relativePath: "secret.txt", @@ -1020,7 +1031,7 @@ describe("exportXWorkmateArtifacts", () => { const outsideFile = path.join(outsideRoot, "secret.txt"); await fs.writeFile(outsideFile, "secret"); const prepared = await prepareXWorkmateArtifacts({ - params: { sessionKey: "thread-main", runId: "turn-1" }, + params: { openclawSessionKey: "thread-main", runId: "turn-1" }, pluginConfig: { workspaceDir: root }, }); await fs.symlink(outsideFile, path.join(prepared.artifactDirectory, "linked-secret.txt")); @@ -1028,7 +1039,7 @@ describe("exportXWorkmateArtifacts", () => { await expect( readXWorkmateArtifact({ params: { - sessionKey: "thread-main", + openclawSessionKey: "thread-main", runId: "turn-1", artifactScope: prepared.artifactScope, relativePath: "linked-secret.txt", diff --git a/src/exportArtifacts.ts b/src/exportArtifacts.ts index e0b522c..0ab0c4b 100644 --- a/src/exportArtifacts.ts +++ b/src/exportArtifacts.ts @@ -117,7 +117,7 @@ export async function prepareXWorkmateArtifacts(input: ExportInput): Promise; - type SessionEntry = Record & { pluginExtensions?: Record>; }; @@ -64,10 +62,6 @@ type BoundTaskRunsRuntime = { resolve?: (token: string) => unknown; }; -export function createXWorkmateTaskStore(): XWorkmateTaskStore { - return {}; -} - export function registerXWorkmateSessionExtension(api: OpenClawPluginApi) { const registerExtension = api.session?.state?.registerSessionExtension ?? (api as any).registerSessionExtension; @@ -85,13 +79,8 @@ export function registerXWorkmateSessionExtension(api: OpenClawPluginApi) { }); } -export function registerXWorkmateDetachedTaskRuntime(_api: OpenClawPluginApi, _taskStore: XWorkmateTaskStore) { - // OpenClaw native task-registry is the only task status source for this plugin. -} - export async function recordXWorkmateSessionMapping(input: { api: OpenClawPluginApi; - taskStore?: XWorkmateTaskStore; params: Record; artifactScope?: string; source?: XWorkmateSessionMappingSource; @@ -235,7 +224,6 @@ export async function readXWorkmateSessionMapping( export async function getXWorkmateTaskSnapshot(input: { api: OpenClawPluginApi; - taskStore?: XWorkmateTaskStore; params: Record; }): Promise> { const params = input.params ?? {};