openclaw-multi-session-plugins/dist/index.js
2026-06-01 10:54:17 +08:00

309 lines
13 KiB
JavaScript

import { getPluginRuntimeGatewayRequestScope } from "openclaw/plugin-sdk/plugin-runtime";
import { exportXWorkmateArtifacts, prepareXWorkmateArtifacts, readXWorkmateArtifact, } from "./src/exportArtifacts.js";
import { runXWorkmateBridgeAgents } from "./src/bridgeAgents.js";
function scopedGatewayParams(params) {
const sessionScope = getPluginRuntimeGatewayRequestScope()?.sessionScope;
const runScope = resolveRunScope({ sessionScope });
if (!runScope) {
return params;
}
return {
...params,
sessionKey: runScope.sessionKey,
runId: runScope.runId,
...(runScope.workspaceDir ? { workspaceDir: runScope.workspaceDir } : {}),
...(runScope.artifactScope ? { artifactScope: runScope.artifactScope } : {}),
};
}
function resolveRunScope(ctx) {
const scope = ctx.sessionScope;
const sessionKey = scope?.sessionKey || ctx.sessionKey;
const runId = scope?.runId || ctx.runId || "";
if (!sessionKey || !runId) {
return undefined;
}
return {
sessionKey,
runId,
...(scope?.workspaceDir || ctx.workspaceDir ? { workspaceDir: scope?.workspaceDir || ctx.workspaceDir } : {}),
...(scope?.relativeTaskDirectory ? { artifactScope: scope.relativeTaskDirectory } : {}),
};
}
const plugin = {
id: "openclaw-multi-session-plugins",
name: "openclaw-multi-session-plugins",
description: "OpenClaw logical isolation support for multi-session plugin runtimes and scoped XWorkmate artifacts.",
register,
};
export default plugin;
function register(api) {
api.registerGatewayMethod("xworkmate.artifacts.prepare", async (opts) => {
try {
const payload = await prepareXWorkmateArtifacts({
params: scopedGatewayParams(opts.params),
config: api.config,
pluginConfig: api.pluginConfig,
});
opts.respond(true, payload, undefined);
}
catch (error) {
opts.respond(false, undefined, {
code: "INVALID_REQUEST",
message: error instanceof Error ? error.message : String(error),
});
}
});
api.registerGatewayMethod("xworkmate.artifacts.export", async (opts) => {
try {
const payload = await exportXWorkmateArtifacts({
params: scopedGatewayParams(opts.params),
config: api.config,
pluginConfig: api.pluginConfig,
});
opts.respond(true, payload, undefined);
}
catch (error) {
opts.respond(false, undefined, {
code: "INVALID_REQUEST",
message: error instanceof Error ? error.message : String(error),
});
}
});
api.registerGatewayMethod("xworkmate.artifacts.list", async (opts) => {
try {
const payload = await exportXWorkmateArtifacts({
params: { ...scopedGatewayParams(opts.params), includeContent: false },
config: api.config,
pluginConfig: api.pluginConfig,
});
opts.respond(true, payload, undefined);
}
catch (error) {
opts.respond(false, undefined, {
code: "INVALID_REQUEST",
message: error instanceof Error ? error.message : String(error),
});
}
});
api.registerGatewayMethod("xworkmate.artifacts.read", async (opts) => {
try {
const payload = await readXWorkmateArtifact({
params: scopedGatewayParams(opts.params),
config: api.config,
pluginConfig: api.pluginConfig,
});
opts.respond(true, payload, undefined);
}
catch (error) {
opts.respond(false, undefined, {
code: "INVALID_REQUEST",
message: error instanceof Error ? error.message : String(error),
});
}
});
api.registerGatewayMethod("xworkmate.agents.run", async (opts) => {
try {
const payload = await runXWorkmateBridgeAgents({
params: scopedGatewayParams(opts.params),
config: api.config,
pluginConfig: api.pluginConfig,
});
opts.respond(true, payload, undefined);
}
catch (error) {
opts.respond(false, undefined, {
code: "INVALID_REQUEST",
message: error instanceof Error ? error.message : String(error),
});
}
});
api.registerTool((ctx) => createXWorkmateArtifactsTool(api, ctx), {
names: ["openclaw_multi_session_artifacts"],
optional: true,
});
api.registerTool((ctx) => createXWorkmateAgentsTool(api, ctx), {
names: ["openclaw_multi_session_agents"],
optional: true,
});
}
function createXWorkmateArtifactsTool(api, ctx) {
return {
name: "openclaw_multi_session_artifacts",
label: "openclaw-multi-session-plugins",
description: "List generated artifacts in the current OpenClaw workspace or read one small artifact as base64 for XWorkmate.",
parameters: {
type: "object",
additionalProperties: false,
properties: {
action: {
type: "string",
enum: ["list", "read"],
description: "Use list to show workspace artifacts, or read to return one small file.",
},
relativePath: {
type: "string",
description: "Artifact path relative to artifactScope. Required for action=read without artifactRef.",
},
artifactScope: {
type: "string",
description: "Task artifact scope returned by prepare/export, for example tasks/<session>/<run>.",
},
artifactRef: {
type: "string",
description: "Plugin-signed artifact reference returned by export/list. Bound to the issuing task scope.",
},
sinceUnixMs: {
type: "number",
description: "Only list files changed at or after this Unix timestamp in milliseconds.",
},
maxFiles: {
type: "number",
description: "Maximum number of files to list.",
},
maxInlineBytes: {
type: "number",
description: "Maximum bytes to inline when reading an artifact.",
},
},
required: ["action"],
},
async execute(_id, params) {
const action = typeof params.action === "string" ? params.action : "";
const runScope = resolveRunScope(ctx);
const sessionKey = ctx.sessionScope?.sessionKey || ctx.sessionKey;
const runId = ctx.sessionScope?.runId || ctx.runId || "";
if (!sessionKey) {
throw new Error("sessionKey required");
}
if (!runId) {
throw new Error("runId required");
}
const workspaceDir = ctx.sessionScope?.workspaceDir || ctx.workspaceDir;
const { sessionKey: _ignoredSessionKey, runId: _ignoredRunId, workspaceDir: _ignoredWorkspaceDir, ...operationParams } = params;
const baseParams = {
...operationParams,
sessionKey,
runId,
...(workspaceDir ? { workspaceDir } : {}),
...(runScope?.artifactScope ? { artifactScope: runScope.artifactScope } : {}),
};
if (action === "list") {
const payload = await exportXWorkmateArtifacts({
params: { ...baseParams, includeContent: false },
config: ctx.config ?? api.config,
pluginConfig: api.pluginConfig,
});
return { content: [{ type: "text", text: payload.manifestMarkdown }], details: {} };
}
if (action === "read") {
const payload = await readXWorkmateArtifact({
params: baseParams,
config: ctx.config ?? api.config,
pluginConfig: api.pluginConfig,
});
const artifact = payload.artifacts[0];
const text = artifact
? [
payload.manifestMarkdown,
"",
artifact.content
? `Base64 content for \`${artifact.relativePath}\`:\n\n\`\`\`base64\n${artifact.content}\n\`\`\``
: `\`${artifact.relativePath}\` is larger than maxInlineBytes; use the workspace path to download it directly.`,
].join("\n")
: payload.manifestMarkdown;
return { content: [{ type: "text", text }], details: {} };
}
throw new Error("action must be list or read");
},
};
}
function createXWorkmateAgentsTool(api, ctx) {
return {
name: "openclaw_multi_session_agents",
label: "XWorkmate multi-agent bridge",
description: "Ask XWorkmate Bridge to coordinate multiple configured agents, then save the result into the current task artifact scope.",
parameters: {
type: "object",
additionalProperties: false,
properties: {
taskPrompt: {
type: "string",
description: "Overall multi-agent task prompt.",
},
mode: {
type: "string",
enum: ["sequence", "parallel", "race", "conversation"],
description: "Multi-agent orchestration mode.",
},
steps: {
type: "array",
description: "Agent steps. Each item needs providerId and prompt.",
items: {
type: "object",
additionalProperties: false,
properties: {
providerId: { type: "string" },
prompt: { type: "string" },
outputAs: { type: "string" },
timeoutMs: { type: "number" },
},
required: ["providerId", "prompt"],
},
},
participants: {
type: "array",
description: "Conversation participants by providerId.",
items: { type: "string" },
},
maxTurns: {
type: "number",
description: "Maximum turns for conversation mode.",
},
stopConditions: {
type: "array",
description: "Text markers that stop conversation mode.",
items: { type: "string" },
},
timeoutMs: {
type: "number",
description: "Overall bridge request timeout.",
},
},
required: ["taskPrompt"],
},
async execute(_id, params) {
const runScope = resolveRunScope(ctx);
const sessionKey = ctx.sessionScope?.sessionKey || ctx.sessionKey;
const runId = ctx.sessionScope?.runId || ctx.runId || "";
if (!sessionKey) {
throw new Error("sessionKey required");
}
if (!runId) {
throw new Error("runId required");
}
const workspaceDir = ctx.sessionScope?.workspaceDir || ctx.workspaceDir;
const { sessionKey: _ignoredSessionKey, runId: _ignoredRunId, workspaceDir: _ignoredWorkspaceDir, ...operationParams } = params;
const payload = await runXWorkmateBridgeAgents({
params: {
...operationParams,
sessionKey,
runId,
...(workspaceDir ? { workspaceDir } : {}),
...(runScope?.artifactScope ? { artifactScope: runScope.artifactScope } : {}),
},
config: ctx.config ?? api.config,
pluginConfig: api.pluginConfig,
});
const summary = typeof payload.bridgeResult.summary === "string"
? payload.bridgeResult.summary
: typeof payload.bridgeResult.output === "string"
? payload.bridgeResult.output
: "Multi-agent run completed.";
return {
content: [{ type: "text", text: [summary, "", payload.manifestMarkdown].join("\n") }],
details: { artifacts: payload.artifacts, bridgeResult: payload.bridgeResult },
};
},
};
}