fix(cycles): split runtime delivery and registry seams

This commit is contained in:
Vincent Koc
2026-04-11 13:20:41 +01:00
parent 41ab0f7d5c
commit 543c14a4ed
15 changed files with 253 additions and 184 deletions

View File

@@ -1,15 +1,35 @@
import type { PluginRegistry } from "./registry.js";
export const PLUGIN_REGISTRY_STATE = Symbol.for("openclaw.pluginRegistryState");
export type RuntimeTrackedPluginRecord = {
id: string;
status?: string;
format?: string;
};
export type RuntimeTrackedChannelEntry = {
plugin: {
id?: string | null;
meta?: {
aliases?: string[];
markdownCapable?: boolean;
} | null;
};
};
export type RuntimeTrackedPluginRegistry = {
plugins: RuntimeTrackedPluginRecord[];
httpRoutes?: unknown[];
channels?: RuntimeTrackedChannelEntry[];
};
export type RegistrySurfaceState = {
registry: PluginRegistry | null;
registry: RuntimeTrackedPluginRegistry | null;
pinned: boolean;
version: number;
};
export type RegistryState = {
activeRegistry: PluginRegistry | null;
activeRegistry: RuntimeTrackedPluginRegistry | null;
activeVersion: number;
httpRoute: RegistrySurfaceState;
channel: RegistrySurfaceState;
@@ -27,7 +47,7 @@ export function getPluginRegistryState(): RegistryState | undefined {
return (globalThis as GlobalRegistryState)[PLUGIN_REGISTRY_STATE];
}
export function getActivePluginChannelRegistryFromState(): PluginRegistry | null {
export function getActivePluginChannelRegistryFromState(): RuntimeTrackedPluginRegistry | null {
const state = getPluginRegistryState();
return state?.channel.registry ?? state?.activeRegistry ?? null;
}

View File

@@ -6,6 +6,10 @@ import {
type RegistrySurfaceState,
} from "./runtime-state.js";
function asPluginRegistry(registry: RegistryState["activeRegistry"]): PluginRegistry | null {
return registry as PluginRegistry | null;
}
const state: RegistryState = (() => {
const globalState = globalThis as typeof globalThis & {
[PLUGIN_REGISTRY_STATE]?: RegistryState;
@@ -85,7 +89,7 @@ export function setActivePluginRegistry(
}
export function getActivePluginRegistry(): PluginRegistry | null {
return state.activeRegistry;
return asPluginRegistry(state.activeRegistry);
}
export function getActivePluginRegistryWorkspaceDir(): string | undefined {
@@ -114,7 +118,7 @@ export function releasePinnedPluginHttpRouteRegistry(registry?: PluginRegistry)
}
export function getActivePluginHttpRouteRegistry(): PluginRegistry | null {
return state.httpRoute.registry ?? state.activeRegistry;
return asPluginRegistry(state.httpRoute.registry ?? state.activeRegistry);
}
export function getActivePluginHttpRouteRegistryVersion(): number {
@@ -163,7 +167,7 @@ export function releasePinnedPluginChannelRegistry(registry?: PluginRegistry) {
* When pinned, this returns the startup registry regardless of subsequent
* `setActivePluginRegistry` calls. */
export function getActivePluginChannelRegistry(): PluginRegistry | null {
return state.channel.registry ?? state.activeRegistry;
return asPluginRegistry(state.channel.registry ?? state.activeRegistry);
}
export function getActivePluginChannelRegistryVersion(): number {

View File

@@ -28,7 +28,7 @@ import type {
TaskRegistrySummary,
TaskRuntime,
} from "../../tasks/task-registry.types.js";
import { normalizeDeliveryContext } from "../../utils/delivery-context.js";
import { normalizeDeliveryContext } from "../../utils/delivery-context.shared.js";
import type { OpenClawPluginToolContext } from "../tool-types.js";
export type ManagedTaskFlowRecord = TaskFlowRecord & {

View File

@@ -20,7 +20,7 @@ import {
listTasksForRelatedSessionKeyForOwner,
resolveTaskForLookupTokenForOwner,
} from "../../tasks/task-owner-access.js";
import { normalizeDeliveryContext } from "../../utils/delivery-context.js";
import { normalizeDeliveryContext } from "../../utils/delivery-context.shared.js";
import type { OpenClawPluginToolContext } from "../tool-types.js";
import type { PluginRuntimeTaskFlow } from "./runtime-taskflow.js";
import type {

View File

@@ -9,6 +9,10 @@ type ReadChannelAllowFromStore =
typeof import("../../pairing/pairing-store.js").readChannelAllowFromStore;
type UpsertChannelPairingRequest =
typeof import("../../pairing/pairing-store.js").upsertChannelPairingRequest;
type ReadSessionUpdatedAt = import("../../config/sessions/runtime-types.js").ReadSessionUpdatedAt;
type RecordSessionMetaFromInbound =
import("../../config/sessions/runtime-types.js").RecordSessionMetaFromInbound;
type UpdateLastRoute = import("../../config/sessions/runtime-types.js").UpdateLastRoute;
type ReadChannelAllowFromStoreForAccount = (params: {
channel: Parameters<ReadChannelAllowFromStore>[0];
@@ -105,11 +109,11 @@ export type PluginRuntimeChannel = {
get: typeof import("../../infra/channel-activity.js").getChannelActivity;
};
session: {
resolveStorePath: typeof import("../../config/sessions.js").resolveStorePath;
readSessionUpdatedAt: typeof import("../../config/sessions.js").readSessionUpdatedAt;
recordSessionMetaFromInbound: typeof import("../../config/sessions.js").recordSessionMetaFromInbound;
resolveStorePath: typeof import("../../config/sessions/paths.js").resolveStorePath;
readSessionUpdatedAt: ReadSessionUpdatedAt;
recordSessionMetaFromInbound: RecordSessionMetaFromInbound;
recordInboundSession: typeof import("../../channels/session.js").recordInboundSession;
updateLastRoute: typeof import("../../config/sessions.js").updateLastRoute;
updateLastRoute: UpdateLastRoute;
};
mentions: {
buildMentionRegexes: typeof import("../../auto-reply/reply/mentions.js").buildMentionRegexes;

View File

@@ -54,10 +54,10 @@ export type PluginRuntimeCore = {
resolveAgentTimeoutMs: typeof import("../../agents/timeout.js").resolveAgentTimeoutMs;
ensureAgentWorkspace: typeof import("../../agents/workspace.js").ensureAgentWorkspace;
session: {
resolveStorePath: typeof import("../../config/sessions.js").resolveStorePath;
loadSessionStore: typeof import("../../config/sessions.js").loadSessionStore;
saveSessionStore: typeof import("../../config/sessions.js").saveSessionStore;
resolveSessionFilePath: typeof import("../../config/sessions.js").resolveSessionFilePath;
resolveStorePath: typeof import("../../config/sessions/paths.js").resolveStorePath;
loadSessionStore: typeof import("../../config/sessions/store-load.js").loadSessionStore;
saveSessionStore: typeof import("../../config/sessions/store.js").saveSessionStore;
resolveSessionFilePath: typeof import("../../config/sessions/paths.js").resolveSessionFilePath;
};
};
system: {

View File

@@ -2,7 +2,7 @@ import type { ToolFsPolicy } from "../agents/tool-fs-policy.js";
import type { AnyAgentTool } from "../agents/tools/common.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { HookEntry } from "../hooks/types.js";
import type { DeliveryContext } from "../utils/delivery-context.js";
import type { DeliveryContext } from "../utils/delivery-context.shared.js";
/** Trusted execution context passed to plugin-owned agent tool factories. */
export type OpenClawPluginToolContext = {