diff --git a/docs/plugins/sdk-migration.md b/docs/plugins/sdk-migration.md index 394c59c3d42..82aab4afcc9 100644 --- a/docs/plugins/sdk-migration.md +++ b/docs/plugins/sdk-migration.md @@ -725,18 +725,18 @@ canonical replacement. - + **Old**: `runtime.tasks.flow` (singular) returned a live task-flow accessor. - **New**: `runtime.tasks.flows` (plural) returns DTO-based TaskFlow access, - which is import-safe and does not require the full task runtime to be - loaded. + **New**: `runtime.tasks.managedFlows` keeps the managed TaskFlow mutation + runtime for plugins that create, update, cancel, or run child tasks from a + flow. Use `runtime.tasks.flows` when the plugin only needs DTO-based reads. ```typescript // Before - const flow = api.runtime.tasks.flow(ctx); + const flow = api.runtime.tasks.flow.fromToolContext(ctx); // After - const flows = api.runtime.tasks.flows(ctx); + const flow = api.runtime.tasks.managedFlows.fromToolContext(ctx); ``` diff --git a/docs/plugins/sdk-runtime.md b/docs/plugins/sdk-runtime.md index 20a3ee0650a..63cc530c34e 100644 --- a/docs/plugins/sdk-runtime.md +++ b/docs/plugins/sdk-runtime.md @@ -179,11 +179,11 @@ Internal OpenClaw runtime code has the same direction: load config once at the C Inside the Gateway this runtime is in-process. In plugin CLI commands it calls the configured Gateway over RPC, so commands such as `openclaw googlemeet recover-tab` can inspect paired nodes from the terminal. Node commands still go through normal Gateway node pairing, command allowlists, and node-local command handling. - + Bind a Task Flow runtime to an existing OpenClaw session key or trusted tool context, then create and manage Task Flows without passing an owner on every call. ```typescript - const taskFlow = api.runtime.taskFlow.fromToolContext(ctx); + const taskFlow = api.runtime.tasks.managedFlows.fromToolContext(ctx); const created = taskFlow.createManaged({ controllerId: "my-plugin/review-batch", diff --git a/docs/plugins/webhooks.md b/docs/plugins/webhooks.md index 3be601ac0e6..52b1dfc836c 100644 --- a/docs/plugins/webhooks.md +++ b/docs/plugins/webhooks.md @@ -89,7 +89,7 @@ The plugin applies: - Request body size and timeout guards - Fixed-window rate limiting - In-flight request limiting -- Owner-bound TaskFlow access through `api.runtime.taskFlow.bindSession(...)` +- Owner-bound TaskFlow access through `api.runtime.tasks.managedFlows.bindSession(...)` ## Request format diff --git a/extensions/lobster/index.ts b/extensions/lobster/index.ts index 5163fad561d..901b9c7c546 100644 --- a/extensions/lobster/index.ts +++ b/extensions/lobster/index.ts @@ -13,8 +13,8 @@ export default definePluginEntry({ return null; } const taskFlow = - api.runtime?.taskFlow && ctx.sessionKey - ? api.runtime.taskFlow.fromToolContext(ctx) + api.runtime?.tasks.managedFlows && ctx.sessionKey + ? api.runtime.tasks.managedFlows.fromToolContext(ctx) : undefined; return createLobsterTool(api, { taskFlow }) as AnyAgentTool; }) as OpenClawPluginToolFactory, diff --git a/extensions/lobster/src/lobster-taskflow.ts b/extensions/lobster/src/lobster-taskflow.ts index a46e6be5f42..8ca2c92b1d8 100644 --- a/extensions/lobster/src/lobster-taskflow.ts +++ b/extensions/lobster/src/lobster-taskflow.ts @@ -12,7 +12,7 @@ type JsonLike = }; type BoundTaskFlow = ReturnType< - NonNullable["taskFlow"]["bindSession"] + NonNullable["tasks"]["managedFlows"]["bindSession"] >; type FlowRecord = ReturnType; diff --git a/extensions/lobster/src/lobster-tool.ts b/extensions/lobster/src/lobster-tool.ts index f2d1b06eabe..d9ed36d6db2 100644 --- a/extensions/lobster/src/lobster-tool.ts +++ b/extensions/lobster/src/lobster-tool.ts @@ -13,7 +13,7 @@ import { } from "./lobster-taskflow.js"; type BoundTaskFlow = ReturnType< - NonNullable["taskFlow"]["bindSession"] + NonNullable["tasks"]["managedFlows"]["bindSession"] >; type JsonLike = diff --git a/extensions/lobster/src/taskflow-test-helpers.ts b/extensions/lobster/src/taskflow-test-helpers.ts index 8e2f89abc50..4845663d466 100644 --- a/extensions/lobster/src/taskflow-test-helpers.ts +++ b/extensions/lobster/src/taskflow-test-helpers.ts @@ -2,7 +2,7 @@ import { vi } from "vitest"; import type { OpenClawPluginApi } from "../runtime-api.js"; export type BoundTaskFlow = ReturnType< - NonNullable["taskFlow"]["bindSession"] + NonNullable["tasks"]["managedFlows"]["bindSession"] >; export function createFakeTaskFlow(overrides?: Partial): BoundTaskFlow { diff --git a/extensions/webhooks/index.test.ts b/extensions/webhooks/index.test.ts index 65be0461a8c..b984d48ff5b 100644 --- a/extensions/webhooks/index.test.ts +++ b/extensions/webhooks/index.test.ts @@ -14,8 +14,10 @@ function createApi(params?: { source: "test", pluginConfig: params?.pluginConfig ?? {}, runtime: { - taskFlow: { - bindSession: vi.fn(({ sessionKey }: { sessionKey: string }) => ({ sessionKey })), + tasks: { + managedFlows: { + bindSession: vi.fn(({ sessionKey }: { sessionKey: string }) => ({ sessionKey })), + }, }, } as unknown as OpenClawPluginApi["runtime"], registerHttpRoute: params?.registerHttpRoute ?? vi.fn(), diff --git a/extensions/webhooks/index.ts b/extensions/webhooks/index.ts index abcc6c43112..a3cc509d562 100644 --- a/extensions/webhooks/index.ts +++ b/extensions/webhooks/index.ts @@ -17,7 +17,7 @@ function registerWebhookRoutes(api: OpenClawPluginApi): void { }); for (const route of routes) { - const taskFlow = api.runtime.taskFlow.bindSession({ + const taskFlow = api.runtime.tasks.managedFlows.bindSession({ sessionKey: route.sessionKey, }); const target: TaskFlowWebhookTarget = { diff --git a/extensions/webhooks/src/http.ts b/extensions/webhooks/src/http.ts index 349c2514aca..5f2d136cae5 100644 --- a/extensions/webhooks/src/http.ts +++ b/extensions/webhooks/src/http.ts @@ -18,7 +18,7 @@ import { } from "../runtime-api.js"; import type { WebhookSecretInput } from "./config.js"; -type BoundTaskFlowRuntime = ReturnType; +type BoundTaskFlowRuntime = ReturnType; type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }; diff --git a/src/plugin-sdk/test-helpers/plugin-runtime-mock.ts b/src/plugin-sdk/test-helpers/plugin-runtime-mock.ts index 636486eccc4..f1ba68c2a45 100644 --- a/src/plugin-sdk/test-helpers/plugin-runtime-mock.ts +++ b/src/plugin-sdk/test-helpers/plugin-runtime-mock.ts @@ -73,10 +73,10 @@ export function createPluginRuntimeMock(overrides: DeepPartial = const taskFlow = { bindSession: vi.fn( createTaskFlowSessionMock, - ) as unknown as PluginRuntime["taskFlow"]["bindSession"], + ) as unknown as PluginRuntime["tasks"]["managedFlows"]["bindSession"], fromToolContext: vi.fn( createTaskFlowSessionMock, - ) as unknown as PluginRuntime["taskFlow"]["fromToolContext"], + ) as unknown as PluginRuntime["tasks"]["managedFlows"]["fromToolContext"], }; const base: PluginRuntime = { version: "1.0.0-test", @@ -468,6 +468,7 @@ export function createPluginRuntimeMock(overrides: DeepPartial = bindSession: vi.fn(), fromToolContext: vi.fn(), } as PluginRuntime["tasks"]["flows"], + managedFlows: taskFlow, flow: taskFlow, }, taskFlow, diff --git a/src/plugins/compat/registry.ts b/src/plugins/compat/registry.ts index 03a577076ca..54e085be411 100644 --- a/src/plugins/compat/registry.ts +++ b/src/plugins/compat/registry.ts @@ -720,7 +720,8 @@ export const PLUGIN_COMPAT_RECORDS = [ deprecated: "2026-04-26", warningStarts: "2026-04-26", removeAfter: "2026-07-26", - replacement: "`api.runtime.tasks.flows`", + replacement: + "`api.runtime.tasks.managedFlows` for managed mutations or `api.runtime.tasks.flows` for DTO reads", docsPath: "/plugins/sdk-runtime", surfaces: ["api.runtime.taskFlow", "api.runtime.tasks.flow"], diagnostics: ["plugin runtime compatibility warning"], diff --git a/src/plugins/runtime/index.test.ts b/src/plugins/runtime/index.test.ts index 40e335ae3ec..ae1dcaa5ce9 100644 --- a/src/plugins/runtime/index.test.ts +++ b/src/plugins/runtime/index.test.ts @@ -191,7 +191,7 @@ describe("plugin runtime command execution", () => { }, }, { - name: "exposes canonical runtime.tasks.runs and runtime.tasks.flows while keeping legacy TaskFlow aliases", + name: "exposes canonical runtime.tasks task runtimes while keeping legacy TaskFlow aliases", assert: (runtime: ReturnType) => { expectFunctionKeys(runtime.tasks.runs as Record, [ "bindSession", @@ -201,11 +201,16 @@ describe("plugin runtime command execution", () => { "bindSession", "fromToolContext", ]); + expectFunctionKeys(runtime.tasks.managedFlows as Record, [ + "bindSession", + "fromToolContext", + ]); expectFunctionKeys(runtime.tasks.flow as Record, [ "bindSession", "fromToolContext", ]); - expect(runtime.taskFlow).toBe(runtime.tasks.flow); + expect(runtime.tasks.managedFlows).toBe(runtime.tasks.flow); + expect(runtime.taskFlow).toBe(runtime.tasks.managedFlows); }, }, { diff --git a/src/plugins/runtime/runtime-tasks.ts b/src/plugins/runtime/runtime-tasks.ts index 9406a218305..fed143ee07d 100644 --- a/src/plugins/runtime/runtime-tasks.ts +++ b/src/plugins/runtime/runtime-tasks.ts @@ -217,6 +217,7 @@ export function createRuntimeTasks(params: { return { runs: createRuntimeTaskRuns(), flows: createRuntimeTaskFlows(), + managedFlows: params.legacyTaskFlow, flow: params.legacyTaskFlow, }; } diff --git a/src/plugins/runtime/runtime-tasks.types.ts b/src/plugins/runtime/runtime-tasks.types.ts index 28f7db24fe3..6de41ac08c1 100644 --- a/src/plugins/runtime/runtime-tasks.types.ts +++ b/src/plugins/runtime/runtime-tasks.types.ts @@ -64,6 +64,7 @@ export type PluginRuntimeTaskFlows = { export type PluginRuntimeTasks = { runs: PluginRuntimeTaskRuns; flows: PluginRuntimeTaskFlows; + managedFlows: PluginRuntimeTaskFlow; /** @deprecated Use runtime.tasks.flows for DTO-based TaskFlow access. */ flow: PluginRuntimeTaskFlow; }; diff --git a/src/plugins/runtime/types-core.ts b/src/plugins/runtime/types-core.ts index 96d428f2106..3c75c02ed7d 100644 --- a/src/plugins/runtime/types-core.ts +++ b/src/plugins/runtime/types-core.ts @@ -231,6 +231,7 @@ export type PluginRuntimeCore = { tasks: { runs: PluginRuntimeTaskRuns; flows: PluginRuntimeTaskFlows; + managedFlows: import("./runtime-taskflow.types.js").PluginRuntimeTaskFlow; /** @deprecated Use runtime.tasks.flows for DTO-based TaskFlow access. */ flow: import("./runtime-taskflow.types.js").PluginRuntimeTaskFlow; };