Files
openclaw/src/plugins/runtime/runtime-tasks.ts
Vincent Koc 774beb8e5c refactor(plugin-sdk): add task domain runtime surfaces (#59805)
* refactor(plugin-sdk): add task domain runtime views

* chore(plugin-sdk): refresh api baseline

* fix(plugin-sdk): preserve task runtime owner isolation
2026-04-03 02:11:21 +09:00

264 lines
7.9 KiB
TypeScript

import type { OpenClawConfig } from "../../config/config.js";
import { cancelTaskById, listTasksForFlowId } from "../../tasks/runtime-internal.js";
import {
mapTaskFlowDetail,
mapTaskFlowView,
mapTaskRunAggregateSummary,
mapTaskRunDetail,
mapTaskRunView,
} from "../../tasks/task-domain-views.js";
import { getFlowTaskSummary } from "../../tasks/task-executor.js";
import {
getTaskFlowByIdForOwner,
listTaskFlowsForOwner,
findLatestTaskFlowForOwner,
resolveTaskFlowForLookupTokenForOwner,
} from "../../tasks/task-flow-owner-access.js";
import {
findLatestTaskForRelatedSessionKeyForOwner,
getTaskByIdForOwner,
listTasksForRelatedSessionKeyForOwner,
resolveTaskForLookupTokenForOwner,
} from "../../tasks/task-owner-access.js";
import { normalizeDeliveryContext } from "../../utils/delivery-context.js";
import type { OpenClawPluginToolContext } from "../types.js";
import type { PluginRuntimeTaskFlow } from "./runtime-taskflow.js";
import type {
TaskFlowDetail,
TaskFlowView,
TaskRunAggregateSummary,
TaskRunCancelResult,
TaskRunDetail,
TaskRunView,
} from "./task-domain-types.js";
function assertSessionKey(sessionKey: string | undefined, errorMessage: string): string {
const normalized = sessionKey?.trim();
if (!normalized) {
throw new Error(errorMessage);
}
return normalized;
}
function mapCancelledTaskResult(
result: Awaited<ReturnType<typeof cancelTaskById>>,
): TaskRunCancelResult {
return {
found: result.found,
cancelled: result.cancelled,
...(result.reason ? { reason: result.reason } : {}),
...(result.task ? { task: mapTaskRunDetail(result.task) } : {}),
};
}
export type BoundTaskRunsRuntime = {
readonly sessionKey: string;
readonly requesterOrigin?: ReturnType<typeof normalizeDeliveryContext>;
get: (taskId: string) => TaskRunDetail | undefined;
list: () => TaskRunView[];
findLatest: () => TaskRunDetail | undefined;
resolve: (token: string) => TaskRunDetail | undefined;
cancel: (params: { taskId: string; cfg: OpenClawConfig }) => Promise<TaskRunCancelResult>;
};
export type PluginRuntimeTaskRuns = {
bindSession: (params: {
sessionKey: string;
requesterOrigin?: import("../../tasks/task-registry.types.js").TaskDeliveryState["requesterOrigin"];
}) => BoundTaskRunsRuntime;
fromToolContext: (
ctx: Pick<OpenClawPluginToolContext, "sessionKey" | "deliveryContext">,
) => BoundTaskRunsRuntime;
};
export type BoundTaskFlowsRuntime = {
readonly sessionKey: string;
readonly requesterOrigin?: ReturnType<typeof normalizeDeliveryContext>;
get: (flowId: string) => TaskFlowDetail | undefined;
list: () => TaskFlowView[];
findLatest: () => TaskFlowDetail | undefined;
resolve: (token: string) => TaskFlowDetail | undefined;
getTaskSummary: (flowId: string) => TaskRunAggregateSummary | undefined;
};
export type PluginRuntimeTaskFlows = {
bindSession: (params: {
sessionKey: string;
requesterOrigin?: import("../../tasks/task-registry.types.js").TaskDeliveryState["requesterOrigin"];
}) => BoundTaskFlowsRuntime;
fromToolContext: (
ctx: Pick<OpenClawPluginToolContext, "sessionKey" | "deliveryContext">,
) => BoundTaskFlowsRuntime;
};
export type PluginRuntimeTasks = {
runs: PluginRuntimeTaskRuns;
flows: PluginRuntimeTaskFlows;
/** @deprecated Use runtime.tasks.flows for DTO-based TaskFlow access. */
flow: PluginRuntimeTaskFlow;
};
function createBoundTaskRunsRuntime(params: {
sessionKey: string;
requesterOrigin?: import("../../tasks/task-registry.types.js").TaskDeliveryState["requesterOrigin"];
}): BoundTaskRunsRuntime {
const ownerKey = assertSessionKey(
params.sessionKey,
"Tasks runtime requires a bound sessionKey.",
);
const requesterOrigin = params.requesterOrigin
? normalizeDeliveryContext(params.requesterOrigin)
: undefined;
return {
sessionKey: ownerKey,
...(requesterOrigin ? { requesterOrigin } : {}),
get: (taskId) => {
const task = getTaskByIdForOwner({ taskId, callerOwnerKey: ownerKey });
return task ? mapTaskRunDetail(task) : undefined;
},
list: () =>
listTasksForRelatedSessionKeyForOwner({
relatedSessionKey: ownerKey,
callerOwnerKey: ownerKey,
}).map((task) => mapTaskRunView(task)),
findLatest: () => {
const task = findLatestTaskForRelatedSessionKeyForOwner({
relatedSessionKey: ownerKey,
callerOwnerKey: ownerKey,
});
return task ? mapTaskRunDetail(task) : undefined;
},
resolve: (token) => {
const task = resolveTaskForLookupTokenForOwner({
token,
callerOwnerKey: ownerKey,
});
return task ? mapTaskRunDetail(task) : undefined;
},
cancel: async ({ taskId, cfg }) => {
const task = getTaskByIdForOwner({
taskId,
callerOwnerKey: ownerKey,
});
if (!task) {
return {
found: false,
cancelled: false,
reason: "Task not found.",
};
}
return mapCancelledTaskResult(
await cancelTaskById({
cfg,
taskId: task.taskId,
}),
);
},
};
}
function createBoundTaskFlowsRuntime(params: {
sessionKey: string;
requesterOrigin?: import("../../tasks/task-registry.types.js").TaskDeliveryState["requesterOrigin"];
}): BoundTaskFlowsRuntime {
const ownerKey = assertSessionKey(
params.sessionKey,
"TaskFlow runtime requires a bound sessionKey.",
);
const requesterOrigin = params.requesterOrigin
? normalizeDeliveryContext(params.requesterOrigin)
: undefined;
const getDetail = (flowId: string): TaskFlowDetail | undefined => {
const flow = getTaskFlowByIdForOwner({
flowId,
callerOwnerKey: ownerKey,
});
if (!flow) {
return undefined;
}
const tasks = listTasksForFlowId(flow.flowId);
return mapTaskFlowDetail({
flow,
tasks,
summary: getFlowTaskSummary(flow.flowId),
});
};
return {
sessionKey: ownerKey,
...(requesterOrigin ? { requesterOrigin } : {}),
get: (flowId) => getDetail(flowId),
list: () =>
listTaskFlowsForOwner({
callerOwnerKey: ownerKey,
}).map((flow) => mapTaskFlowView(flow)),
findLatest: () => {
const flow = findLatestTaskFlowForOwner({
callerOwnerKey: ownerKey,
});
return flow ? getDetail(flow.flowId) : undefined;
},
resolve: (token) => {
const flow = resolveTaskFlowForLookupTokenForOwner({
token,
callerOwnerKey: ownerKey,
});
return flow ? getDetail(flow.flowId) : undefined;
},
getTaskSummary: (flowId) => {
const flow = getTaskFlowByIdForOwner({
flowId,
callerOwnerKey: ownerKey,
});
return flow ? mapTaskRunAggregateSummary(getFlowTaskSummary(flow.flowId)) : undefined;
},
};
}
export function createRuntimeTaskRuns(): PluginRuntimeTaskRuns {
return {
bindSession: (params) =>
createBoundTaskRunsRuntime({
sessionKey: params.sessionKey,
requesterOrigin: params.requesterOrigin,
}),
fromToolContext: (ctx) =>
createBoundTaskRunsRuntime({
sessionKey: assertSessionKey(
ctx.sessionKey,
"Tasks runtime requires tool context with a sessionKey.",
),
requesterOrigin: ctx.deliveryContext,
}),
};
}
export function createRuntimeTaskFlows(): PluginRuntimeTaskFlows {
return {
bindSession: (params) =>
createBoundTaskFlowsRuntime({
sessionKey: params.sessionKey,
requesterOrigin: params.requesterOrigin,
}),
fromToolContext: (ctx) =>
createBoundTaskFlowsRuntime({
sessionKey: assertSessionKey(
ctx.sessionKey,
"TaskFlow runtime requires tool context with a sessionKey.",
),
requesterOrigin: ctx.deliveryContext,
}),
};
}
export function createRuntimeTasks(params: {
legacyTaskFlow: PluginRuntimeTaskFlow;
}): PluginRuntimeTasks {
return {
runs: createRuntimeTaskRuns(),
flows: createRuntimeTaskFlows(),
flow: params.legacyTaskFlow,
};
}