diff --git a/extensions/slack/runtime-api.ts b/extensions/slack/runtime-api.ts index 4bf07e691e2..f3b07ddedaf 100644 --- a/extensions/slack/runtime-api.ts +++ b/extensions/slack/runtime-api.ts @@ -5,32 +5,35 @@ export { type SlackActionContext, } from "./src/action-runtime.js"; export { listSlackDirectoryGroupsLive, listSlackDirectoryPeersLive } from "./src/directory-live.js"; +export { + listEnabledSlackAccounts, + listSlackAccountIds, + resolveDefaultSlackAccountId, + resolveSlackAccount, +} from "./src/accounts.js"; export { deleteSlackMessage, editSlackMessage, getSlackMemberInfo, - listEnabledSlackAccounts, - listSlackAccountIds, listSlackEmojis, listSlackPins, listSlackReactions, - monitorSlackProvider, pinSlackMessage, - probeSlack, reactSlackMessage, readSlackMessages, removeOwnSlackReactions, removeSlackReaction, - resolveDefaultSlackAccountId, - resolveSlackAccount, - resolveSlackAppToken, - resolveSlackBotToken, - resolveSlackGroupRequireMention, - resolveSlackGroupToolPolicy, - sendMessageSlack, sendSlackMessage, unpinSlackMessage, -} from "./src/index.js"; +} from "./src/actions.js"; +export { + resolveSlackGroupRequireMention, + resolveSlackGroupToolPolicy, +} from "./src/group-policy.js"; +export { monitorSlackProvider } from "./src/monitor.js"; +export { probeSlack } from "./src/probe.js"; +export { sendMessageSlack } from "./src/send.js"; +export { resolveSlackAppToken, resolveSlackBotToken } from "./src/token.js"; export { resolveSlackChannelAllowlist, type SlackChannelLookup, diff --git a/extensions/slack/src/index.ts b/extensions/slack/src/index.ts deleted file mode 100644 index 410afec99a5..00000000000 --- a/extensions/slack/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Slack plugin entrypoint registers its OpenClaw integration. -export { - listEnabledSlackAccounts, - listSlackAccountIds, - resolveDefaultSlackAccountId, - resolveSlackAccount, -} from "./accounts.js"; -export { - deleteSlackMessage, - editSlackMessage, - getSlackMemberInfo, - listSlackEmojis, - listSlackPins, - listSlackReactions, - pinSlackMessage, - reactSlackMessage, - readSlackMessages, - removeOwnSlackReactions, - removeSlackReaction, - sendSlackMessage, - unpinSlackMessage, -} from "./actions.js"; -export { monitorSlackProvider } from "./monitor.js"; -export { probeSlack } from "./probe.js"; -export { sendMessageSlack } from "./send.js"; -export { resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy } from "./group-policy.js"; -export { resolveSlackAppToken, resolveSlackBotToken } from "./token.js"; diff --git a/extensions/voice-call/index.ts b/extensions/voice-call/index.ts index c680eea57dc..18554fdc232 100644 --- a/extensions/voice-call/index.ts +++ b/extensions/voice-call/index.ts @@ -1,4 +1,5 @@ // Voice Call plugin entrypoint registers its OpenClaw integration. +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { ErrorCodes, errorShape } from "openclaw/plugin-sdk/gateway-runtime"; import { resolveGlobalSingleton } from "openclaw/plugin-sdk/global-singleton"; @@ -28,7 +29,6 @@ import { validateProviderConfig, type VoiceCallConfig, } from "./src/config.js"; -import type { CoreConfig } from "./src/core-bridge.js"; import { createVoiceCallContinueOperationStore } from "./src/gateway-continue-operation.js"; const VOICE_CALL_WRITE_METHOD_SCOPE = { scope: "operator.write" as const }; @@ -176,7 +176,7 @@ export default definePluginEntry({ }; const continueOperationStore = createVoiceCallContinueOperationStore({ config, - coreConfig: api.config as CoreConfig, + coreConfig: api.config as OpenClawConfig, }); const ensureRuntime = async (): Promise => { @@ -233,7 +233,7 @@ export default definePluginEntry({ const runtimePromise = createVoiceCallRuntime({ config, - coreConfig: api.config as CoreConfig, + coreConfig: api.config as OpenClawConfig, fullConfig: api.config, agentRuntime: api.runtime.agent, stateRuntime: api.runtime.state, diff --git a/extensions/voice-call/src/core-bridge.ts b/extensions/voice-call/src/core-bridge.ts deleted file mode 100644 index c6be739a940..00000000000 --- a/extensions/voice-call/src/core-bridge.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Voice Call plugin module implements core bridge behavior. -import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; -import type { OpenClawPluginApi } from "../api.js"; - -// Narrow core runtime/config contracts consumed by the voice-call plugin. - -/** Core config subset read by voice-call helpers. */ -export type CoreConfig = OpenClawConfig; - -/** Agent runtime API subset exposed through the plugin SDK. */ -export type CoreAgentDeps = OpenClawPluginApi["runtime"]["agent"]; diff --git a/extensions/voice-call/src/gateway-continue-operation.ts b/extensions/voice-call/src/gateway-continue-operation.ts index d3478495524..043a27b578d 100644 --- a/extensions/voice-call/src/gateway-continue-operation.ts +++ b/extensions/voice-call/src/gateway-continue-operation.ts @@ -1,9 +1,9 @@ // Voice Call plugin module implements gateway continue operation behavior. import { randomUUID } from "node:crypto"; +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime"; import type { VoiceCallConfig } from "./config.js"; -import type { CoreConfig } from "./core-bridge.js"; import type { VoiceCallRuntime } from "./runtime.js"; import { TELEPHONY_DEFAULT_TTS_TIMEOUT_MS } from "./telephony-tts.js"; @@ -75,7 +75,7 @@ type VoiceCallContinueOperationRequest = { /** Create a process-local operation store for gateway continue-call polling. */ export function createVoiceCallContinueOperationStore(params: { config: VoiceCallConfig; - coreConfig: CoreConfig; + coreConfig: OpenClawConfig; }) { const operations = new Map(); diff --git a/extensions/voice-call/src/realtime-agent-context.test.ts b/extensions/voice-call/src/realtime-agent-context.test.ts index fe2735d0bad..abba57b94e5 100644 --- a/extensions/voice-call/src/realtime-agent-context.test.ts +++ b/extensions/voice-call/src/realtime-agent-context.test.ts @@ -4,8 +4,8 @@ import { tmpdir } from "node:os"; import path from "node:path"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { afterEach, describe, expect, it, vi } from "vitest"; +import type { OpenClawPluginApi } from "../api.js"; import type { VoiceCallConfig } from "./config.js"; -import type { CoreAgentDeps } from "./core-bridge.js"; import { buildRealtimeVoiceInstructions } from "./realtime-agent-context.js"; import { createVoiceCallBaseConfig } from "./test-fixtures.js"; @@ -45,7 +45,7 @@ function createConfig(overrides?: Partial): VoiceCa return config; } -function createAgentRuntime(workspaceDir: string): CoreAgentDeps { +function createAgentRuntime(workspaceDir: string): OpenClawPluginApi["runtime"]["agent"] { return { resolveAgentIdentity: vi.fn(() => ({ name: "Claw Voice", @@ -55,7 +55,7 @@ function createAgentRuntime(workspaceDir: string): CoreAgentDeps { creature: "operator", })), resolveAgentWorkspaceDir: vi.fn(() => workspaceDir), - } as unknown as CoreAgentDeps; + } as unknown as OpenClawPluginApi["runtime"]["agent"]; } describe("buildRealtimeVoiceInstructions", () => { diff --git a/extensions/voice-call/src/realtime-agent-context.ts b/extensions/voice-call/src/realtime-agent-context.ts index 774bcab1af8..4a9f53ad19e 100644 --- a/extensions/voice-call/src/realtime-agent-context.ts +++ b/extensions/voice-call/src/realtime-agent-context.ts @@ -4,8 +4,8 @@ import { buildRealtimeVoiceAgentConsultPolicyInstructions } from "openclaw/plugi import { root } from "openclaw/plugin-sdk/security-runtime"; import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime"; import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime"; +import type { OpenClawPluginApi } from "../api.js"; import type { VoiceCallConfig } from "./config.js"; -import type { CoreAgentDeps } from "./core-bridge.js"; // Builds compact agent context injected into realtime voice sessions. @@ -60,7 +60,7 @@ export async function buildRealtimeVoiceInstructions(params: { baseInstructions: string; config: VoiceCallConfig; coreConfig: OpenClawConfig; - agentRuntime: CoreAgentDeps; + agentRuntime: OpenClawPluginApi["runtime"]["agent"]; agentId: string; }): Promise { const { config } = params; diff --git a/extensions/voice-call/src/response-generator.test.ts b/extensions/voice-call/src/response-generator.test.ts index ddbb2e0bcda..9e63853cb67 100644 --- a/extensions/voice-call/src/response-generator.test.ts +++ b/extensions/voice-call/src/response-generator.test.ts @@ -1,7 +1,8 @@ // Voice Call tests cover response generator plugin behavior. +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { describe, expect, it, vi } from "vitest"; +import type { OpenClawPluginApi } from "../api.js"; import { VoiceCallConfigSchema } from "./config.js"; -import type { CoreAgentDeps, CoreConfig } from "./core-bridge.js"; import { generateVoiceResponse } from "./response-generator.js"; type TestSessionEntry = { @@ -109,13 +110,13 @@ function createAgentRuntime( run: (signal: AbortSignal) => Promise, ) => await run(new AbortController().signal), ); - const resolveAgentDir = vi.fn((_cfg: CoreConfig, agentId: string) => { + const resolveAgentDir = vi.fn((_cfg: OpenClawConfig, agentId: string) => { return `/tmp/openclaw/agents/${agentId}`; }); - const resolveAgentWorkspaceDir = vi.fn((_cfg: CoreConfig, agentId: string) => { + const resolveAgentWorkspaceDir = vi.fn((_cfg: OpenClawConfig, agentId: string) => { return `/tmp/openclaw/workspace/${agentId}`; }); - const resolveAgentIdentity = vi.fn((_cfg: CoreConfig, agentId: string) => ({ + const resolveAgentIdentity = vi.fn((_cfg: OpenClawConfig, agentId: string) => ({ name: `${agentId} tester`, })); const resolveStorePath = vi.fn((_store: string | undefined, params: { agentId?: string }) => { @@ -150,7 +151,7 @@ function createAgentRuntime( runWithWorkAdmission, resolveSessionFilePath, }, - } as unknown as CoreAgentDeps; + } as unknown as OpenClawPluginApi["runtime"]["agent"]; return { runtime, @@ -192,7 +193,7 @@ function requireFirstMockCall(calls: readonly unknown[][], label: string): unkno async function runGenerateVoiceResponse( payloads: Array>, overrides?: { - runtime?: CoreAgentDeps; + runtime?: OpenClawPluginApi["runtime"]["agent"]; transcript?: Array<{ speaker: "user" | "bot"; text: string }>; onEarlyText?: (text: string) => Promise; }, @@ -200,7 +201,7 @@ async function runGenerateVoiceResponse( const voiceConfig = VoiceCallConfigSchema.parse({ responseTimeoutMs: 5000, }); - const coreConfig = {} as CoreConfig; + const coreConfig = {} as OpenClawConfig; const runtime = overrides?.runtime ?? createAgentRuntime(payloads).runtime; const result = await generateVoiceResponse({ @@ -531,7 +532,7 @@ describe("generateVoiceResponse", () => { const result = await generateVoiceResponse({ voiceConfig, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: runtime, callId: "call-123", from: "+15550001111", @@ -580,7 +581,7 @@ describe("generateVoiceResponse", () => { const result = await generateVoiceResponse({ voiceConfig, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: runtime, callId: "call-123", from: "+15550001111", @@ -626,7 +627,7 @@ describe("generateVoiceResponse", () => { const result = await generateVoiceResponse({ voiceConfig, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: runtime, callId: "call-123", sessionKey, @@ -657,7 +658,7 @@ describe("generateVoiceResponse", () => { const result = await generateVoiceResponse({ voiceConfig, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: runtime, callId: "call-123", sessionKey: "voice:call:call-123", @@ -687,7 +688,7 @@ describe("generateVoiceResponse", () => { await generateVoiceResponse({ voiceConfig, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: runtime, callId: "call-123", sessionKey: "meet-room-1", @@ -713,7 +714,7 @@ describe("generateVoiceResponse", () => { const generate = (sessionKey: string) => generateVoiceResponse({ voiceConfig, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: runtime, callId: "call-123", sessionKey, @@ -773,7 +774,7 @@ describe("generateVoiceResponse", () => { resolveStorePath, sessionStore, } = createAgentRuntime([{ text: '{"spoken":"Default agent."}' }]); - const coreConfig = {} as CoreConfig; + const coreConfig = {} as OpenClawConfig; await generateVoiceResponse({ voiceConfig: VoiceCallConfigSchema.parse({ responseTimeoutMs: 5000 }), @@ -818,7 +819,7 @@ describe("generateVoiceResponse", () => { resolveStorePath, sessionStore, } = createAgentRuntime([{ text: '{"spoken":"Voice agent."}' }]); - const coreConfig = {} as CoreConfig; + const coreConfig = {} as OpenClawConfig; const result = await generateVoiceResponse({ voiceConfig: VoiceCallConfigSchema.parse({ @@ -864,7 +865,7 @@ describe("generateVoiceResponse", () => { await generateVoiceResponse({ voiceConfig: VoiceCallConfigSchema.parse({ agentId: "voice", responseTimeoutMs: 5000 }), - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: runtime, callId: "call-123", agentId: "support", @@ -894,7 +895,7 @@ describe("generateVoiceResponse", () => { }, ], }, - } as CoreConfig; + } as OpenClawConfig; const result = await generateVoiceResponse({ voiceConfig: VoiceCallConfigSchema.parse({ diff --git a/extensions/voice-call/src/response-generator.ts b/extensions/voice-call/src/response-generator.ts index 26712b2e938..b2cbc76a20c 100644 --- a/extensions/voice-call/src/response-generator.ts +++ b/extensions/voice-call/src/response-generator.ts @@ -5,6 +5,7 @@ import crypto from "node:crypto"; import { resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime"; +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { applyModelOverrideWithAuthProfileCompatibility, ModelSelectionLockedError, @@ -16,8 +17,8 @@ import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, } from "openclaw/plugin-sdk/string-coerce-runtime"; +import type { OpenClawPluginApi } from "../api.js"; import { resolveVoiceCallSessionKey, type VoiceCallConfig } from "./config.js"; -import type { CoreAgentDeps, CoreConfig } from "./core-bridge.js"; import { resolveCallAgentId } from "./resolve-call-agent-id.js"; import { resolveVoiceResponseModel } from "./response-model.js"; @@ -25,9 +26,9 @@ type VoiceResponseParams = { /** Voice call config */ voiceConfig: VoiceCallConfig; /** Core OpenClaw config */ - coreConfig: CoreConfig; + coreConfig: OpenClawConfig; /** Injected host agent runtime */ - agentRuntime: CoreAgentDeps; + agentRuntime: OpenClawPluginApi["runtime"]["agent"]; /** Call ID for session tracking */ callId: string; /** Persisted call session key */ @@ -70,7 +71,10 @@ function readExplicitToolsAllow(value: unknown): string[] | undefined { return filterStringEntries(allow); } -function resolveVoiceAgentToolsAllow(config: CoreConfig, agentId: string): string[] | undefined { +function resolveVoiceAgentToolsAllow( + config: OpenClawConfig, + agentId: string, +): string[] | undefined { const agents = isRecord(config.agents) ? config.agents : undefined; const list = Array.isArray(agents?.list) ? agents.list : []; const agent = list.find((entry) => isRecord(entry) && entry.id === agentId); diff --git a/extensions/voice-call/src/response-model.test.ts b/extensions/voice-call/src/response-model.test.ts index c6f4400d797..dc64a5955e8 100644 --- a/extensions/voice-call/src/response-model.test.ts +++ b/extensions/voice-call/src/response-model.test.ts @@ -1,7 +1,7 @@ // Voice Call tests cover response model plugin behavior. import { describe, expect, it } from "vitest"; +import type { OpenClawPluginApi } from "../api.js"; import { VoiceCallConfigSchema } from "./config.js"; -import type { CoreAgentDeps } from "./core-bridge.js"; import { resolveVoiceResponseModel } from "./response-model.js"; const agentRuntime = { @@ -9,7 +9,7 @@ const agentRuntime = { provider: "together", model: "Qwen/Qwen2.5-7B-Instruct-Turbo", }, -} as unknown as CoreAgentDeps; +} as unknown as OpenClawPluginApi["runtime"]["agent"]; describe("resolveVoiceResponseModel", () => { it("falls back to the runtime default model", () => { diff --git a/extensions/voice-call/src/response-model.ts b/extensions/voice-call/src/response-model.ts index 90b6172c859..bc286833548 100644 --- a/extensions/voice-call/src/response-model.ts +++ b/extensions/voice-call/src/response-model.ts @@ -1,13 +1,13 @@ // Voice Call plugin module implements response model behavior. +import type { OpenClawPluginApi } from "../api.js"; import type { VoiceCallConfig } from "./config.js"; -import type { CoreAgentDeps } from "./core-bridge.js"; // Resolves the model used for voice-call text response generation. /** Resolve provider/model fields from explicit voice config or agent defaults. */ export function resolveVoiceResponseModel(params: { voiceConfig: VoiceCallConfig; - agentRuntime: CoreAgentDeps; + agentRuntime: OpenClawPluginApi["runtime"]["agent"]; }): { modelRef: string; provider: string; diff --git a/extensions/voice-call/src/runtime.test.ts b/extensions/voice-call/src/runtime.test.ts index 86f418ae511..a1dafc126a5 100644 --- a/extensions/voice-call/src/runtime.test.ts +++ b/extensions/voice-call/src/runtime.test.ts @@ -3,7 +3,6 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/core"; import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { VoiceCallConfig } from "./config.js"; -import type { CoreConfig } from "./core-bridge.js"; import { createVoiceCallBaseConfig } from "./test-fixtures.js"; const mocks = vi.hoisted(() => ({ @@ -293,7 +292,7 @@ describe("createVoiceCallRuntime lifecycle", () => { const runtime = await createVoiceCallRuntime({ config: createBaseConfig(), - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: {} as never, }); @@ -320,7 +319,7 @@ describe("createVoiceCallRuntime lifecycle", () => { }); it("passes fullConfig to the webhook server for streaming provider resolution", async () => { - const coreConfig = { tts: { provider: "openai" } } as CoreConfig; + const coreConfig = { tts: { provider: "openai" } } as OpenClawConfig; const fullConfig = { plugins: { entries: { @@ -359,7 +358,7 @@ describe("createVoiceCallRuntime lifecycle", () => { const runtime = await createVoiceCallRuntime({ config, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, fullConfig, agentRuntime: { resolveAgentIdentity, @@ -407,7 +406,7 @@ describe("createVoiceCallRuntime lifecycle", () => { await expect( createVoiceCallRuntime({ config: createExternalProviderConfig({ provider }), - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: {} as never, }), ).rejects.toThrow(`${provider} requires a publicly reachable webhook URL`); @@ -426,7 +425,7 @@ describe("createVoiceCallRuntime lifecycle", () => { provider: "twilio", publicUrl, }), - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: {} as never, }), ).rejects.toThrow("twilio requires a publicly reachable webhook URL"); @@ -439,7 +438,7 @@ describe("createVoiceCallRuntime lifecycle", () => { provider: "twilio", publicUrl: "https://voice.example.com/voice/webhook", }), - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: {} as never, }); @@ -461,7 +460,7 @@ describe("createVoiceCallRuntime lifecycle", () => { provider: "twilio", publicUrl: "https://voice.example.com/voice/webhook", }), - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: {} as never, logger, }); @@ -520,7 +519,7 @@ describe("createVoiceCallRuntime lifecycle", () => { await createVoiceCallRuntime({ config, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: agentRuntime as never, }); @@ -595,7 +594,7 @@ describe("createVoiceCallRuntime lifecycle", () => { await createVoiceCallRuntime({ config, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: agentRuntime as never, }); @@ -643,7 +642,7 @@ describe("createVoiceCallRuntime lifecycle", () => { await createVoiceCallRuntime({ config, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: agentRuntime as never, }); @@ -694,7 +693,7 @@ describe("createVoiceCallRuntime lifecycle", () => { await createVoiceCallRuntime({ config, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: agentRuntime as never, }); @@ -747,7 +746,7 @@ describe("createVoiceCallRuntime lifecycle", () => { await createVoiceCallRuntime({ config, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: agentRuntime as never, }); @@ -813,7 +812,7 @@ describe("createVoiceCallRuntime lifecycle", () => { await createVoiceCallRuntime({ config, - coreConfig: {} as CoreConfig, + coreConfig: {} as OpenClawConfig, agentRuntime: agentRuntime as never, }); diff --git a/extensions/voice-call/src/runtime.ts b/extensions/voice-call/src/runtime.ts index dbe950c26a0..c309d9b2212 100644 --- a/extensions/voice-call/src/runtime.ts +++ b/extensions/voice-call/src/runtime.ts @@ -14,6 +14,7 @@ import { type ResolvedRealtimeVoiceProvider, } from "openclaw/plugin-sdk/realtime-voice"; import { normalizeAgentId } from "openclaw/plugin-sdk/routing"; +import type { OpenClawPluginApi } from "../api.js"; import type { VoiceCallConfig } from "./config.js"; import { resolveVoiceCallEffectiveConfig, @@ -23,7 +24,6 @@ import { resolveVoiceCallConfig, validateProviderConfig, } from "./config.js"; -import type { CoreAgentDeps, CoreConfig } from "./core-bridge.js"; import { CallManager } from "./manager.js"; import type { VoiceCallProvider } from "./providers/base.js"; import type { TwilioProvider } from "./providers/twilio.js"; @@ -257,7 +257,7 @@ function listRealtimeAgentIds(config: VoiceCallConfig, coreConfig: OpenClawConfi async function createRealtimeInstructionsResolver(params: { config: VoiceCallConfig & { agentId: string }; coreConfig: OpenClawConfig; - agentRuntime: CoreAgentDeps; + agentRuntime: OpenClawPluginApi["runtime"]["agent"]; }): Promise<(call: CallRecord) => string> { const genericConfig: VoiceCallConfig = { ...params.config, @@ -297,9 +297,9 @@ async function createRealtimeInstructionsResolver(params: { export async function createVoiceCallRuntime(params: { config: VoiceCallConfig; - coreConfig: CoreConfig; + coreConfig: OpenClawConfig; fullConfig?: OpenClawConfig; - agentRuntime: CoreAgentDeps; + agentRuntime: OpenClawPluginApi["runtime"]["agent"]; stateRuntime?: VoiceCallStateRuntime["state"]; ttsRuntime?: TelephonyTtsRuntime; logger?: Logger; diff --git a/extensions/voice-call/src/webhook.ts b/extensions/voice-call/src/webhook.ts index 2fe5c6de784..8e3a74a87cc 100644 --- a/extensions/voice-call/src/webhook.ts +++ b/extensions/voice-call/src/webhook.ts @@ -23,6 +23,7 @@ import { readRequestBodyWithLimit, requestBodyErrorToText, } from "../api.js"; +import type { OpenClawPluginApi } from "../api.js"; import { isAllowlistedCaller, normalizePhoneNumber } from "./allowlist.js"; import { normalizeVoiceCallConfig, @@ -30,7 +31,6 @@ import { resolveVoiceCallNumberRouteKeyForCall, type VoiceCallConfig, } from "./config.js"; -import type { CoreAgentDeps, CoreConfig } from "./core-bridge.js"; import { getHeader } from "./http-headers.js"; import type { CallManager } from "./manager.js"; import type { MediaStreamConfig } from "./media-stream.js"; @@ -183,9 +183,9 @@ export class VoiceCallWebhookServer { private config: VoiceCallConfig; private manager: CallManager; private provider: VoiceCallProvider; - private coreConfig: CoreConfig | null; + private coreConfig: OpenClawConfig | null; private fullConfig: OpenClawConfig | null; - private agentRuntime: CoreAgentDeps | null; + private agentRuntime: OpenClawPluginApi["runtime"]["agent"] | null; private logger: Logger; private stopStaleCallReaper: (() => void) | null = null; private readonly webhookInFlightLimiter = createWebhookInFlightLimiter(); @@ -203,9 +203,9 @@ export class VoiceCallWebhookServer { config: VoiceCallConfig, manager: CallManager, provider: VoiceCallProvider, - coreConfig?: CoreConfig, + coreConfig?: OpenClawConfig, fullConfig?: OpenClawConfig, - agentRuntime?: CoreAgentDeps, + agentRuntime?: OpenClawPluginApi["runtime"]["agent"], logger?: Logger, ) { this.config = normalizeVoiceCallConfig(config); diff --git a/src/auto-reply/commands-registry.ts b/src/auto-reply/commands-registry.ts index 01d3153ade3..3c6e312844d 100644 --- a/src/auto-reply/commands-registry.ts +++ b/src/auto-reply/commands-registry.ts @@ -55,56 +55,28 @@ type NativeCommandProviderLookupOptions = { includeBundledChannelFallback?: boolean; }; -/** Resolves provider-specific native command names while preserving registry defaults. */ -function resolveNativeName( - command: ChatCommandDefinition, +function createNativeCommandNameMapper( provider?: string, options?: NativeCommandProviderLookupOptions, -): string | undefined { - if (!command.nativeName) { - return undefined; - } - if (!provider) { - return command.nativeName; - } - const channelPlugin = - options?.includeBundledChannelFallback === false - ? getLoadedChannelPlugin(provider) - : getChannelPlugin(provider); - return ( - channelPlugin?.commands?.resolveNativeCommandName?.({ - commandKey: command.key, - defaultName: command.nativeName, - }) ?? command.nativeName - ); -} - -function toNativeCommandSpec( - command: ChatCommandDefinition, - provider?: string, - options?: NativeCommandProviderLookupOptions, -): NativeCommandSpec { - const spec: NativeCommandSpec = { - name: resolveNativeName(command, provider, options) ?? command.key, - description: command.description, - acceptsArgs: Boolean(command.acceptsArgs), - args: command.args, +): (command: ChatCommandDefinition) => Array<{ name: string; normalizedName?: string }> { + // Registry state is lifecycle-owned, so resolve the adapter once per list or lookup operation. + const resolveNativeCommandName = !provider + ? undefined + : (options?.includeBundledChannelFallback === false + ? getLoadedChannelPlugin(provider) + : getChannelPlugin(provider) + )?.commands?.resolveNativeCommandName; + return (command) => { + const primary = command.nativeName + ? (resolveNativeCommandName?.({ + commandKey: command.key, + defaultName: command.nativeName, + }) ?? command.nativeName) + : undefined; + return [primary, ...(command.nativeAliases ?? [])] + .filter((name): name is string => Boolean(name)) + .map((name) => ({ name, normalizedName: normalizeOptionalLowercaseString(name) })); }; - if (command.descriptionLocalizations) { - spec.descriptionLocalizations = command.descriptionLocalizations; - } - return spec; -} - -function resolveNativeNames( - command: ChatCommandDefinition, - provider?: string, - options?: NativeCommandProviderLookupOptions, -): string[] { - const primary = resolveNativeName(command, provider, options); - return [primary, ...(command.nativeAliases ?? [])].filter((name): name is string => - Boolean(name), - ); } function supportsNativeProvider(command: ChatCommandDefinition, provider?: string): boolean { @@ -125,28 +97,28 @@ function listNativeSpecsFromCommands( provider?: string, options?: NativeCommandProviderLookupOptions, ): NativeCommandSpec[] { + const mapNativeCommandNames = createNativeCommandNameMapper(provider, options); return commands .filter( (command) => command.scope !== "text" && command.nativeName && supportsNativeProvider(command, provider), ) .flatMap((command) => { - const spec = toNativeCommandSpec(command, provider, options); - return resolveNativeNames(command, provider, options).map((name, index) => { + return mapNativeCommandNames(command).map(({ name }, index) => { const nativeSpec: NativeCommandSpec = { name, - description: spec.description, - acceptsArgs: spec.acceptsArgs, + description: command.description, + acceptsArgs: Boolean(command.acceptsArgs), }; // Native aliases carry the same payload shape but are marked for channel registration. if (index > 0) { nativeSpec.isAlias = true; } - if (spec.args) { - nativeSpec.args = spec.args; + if (command.args) { + nativeSpec.args = command.args; } - if (spec.descriptionLocalizations) { - nativeSpec.descriptionLocalizations = spec.descriptionLocalizations; + if (command.descriptionLocalizations) { + nativeSpec.descriptionLocalizations = command.descriptionLocalizations; } return nativeSpec; }); @@ -222,13 +194,12 @@ export function findCommandByNativeName( if (!normalized) { return undefined; } + const mapNativeCommandNames = createNativeCommandNameMapper(provider, options); return getChatCommands().find( (command) => command.scope !== "text" && supportsNativeProvider(command, provider) && - [resolveNativeName(command, provider, options), ...(command.nativeAliases ?? [])].some( - (nameLocal) => normalizeOptionalLowercaseString(nameLocal) === normalized, - ), + mapNativeCommandNames(command).some(({ normalizedName }) => normalizedName === normalized), ); } diff --git a/src/channels/message-access/index.ts b/src/channels/message-access/index.ts deleted file mode 100644 index a527f9ac623..00000000000 --- a/src/channels/message-access/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Public channel ingress/message-access barrel. Keep this as the narrow import -// point for callers that need access decisions without plugin internals. -export { defineStableChannelIngressIdentity } from "./runtime-identity.js"; -export { - channelIngressRoutes, - createChannelIngressResolver, - resolveChannelMessageIngress, - resolveStableChannelMessageIngress, -} from "./runtime.js"; -export { readChannelIngressStoreAllowFromForDmPolicy } from "./store-allow-from.js"; - -export type { - ChannelIngressAccessGroupMembershipResolver, - ChannelIngressCommandPresetInput, - ChannelIngressConfigInput, - ChannelIngressContextBinding, - ChannelIngressEventPresetInput, - ChannelIngressIdentityAlias, - ChannelIngressIdentityDescriptor, - ChannelIngressIdentityField, - ChannelIngressIdentitySubjectInput, - ChannelIngressRouteAccess, - ChannelIngressRouteDescriptor, - ChannelIngressResolver, - ChannelIngressResolverMessageParams, - ChannelMessageIngressCommandInput, - CreateChannelIngressResolverParams, - ResolvedChannelMessageIngress, - ResolveChannelMessageIngressParams, - ResolveStableChannelMessageIngressParams, - StableChannelIngressIdentityParams, -} from "./runtime-types.js"; -export type * from "./types.js"; diff --git a/src/channels/message-access/message-access.test.ts b/src/channels/message-access/message-access.test.ts index 7cd17715bc3..5257a204fdb 100644 --- a/src/channels/message-access/message-access.test.ts +++ b/src/channels/message-access/message-access.test.ts @@ -1,13 +1,13 @@ // Message access tests cover channel message visibility and permission helpers. import { describe, expect, it } from "vitest"; import { decideChannelIngress } from "./decision.js"; +import { resolveChannelIngressState } from "./state.js"; import type { ChannelIngressPolicyInput, ChannelIngressStateInput, InternalChannelIngressAdapter, InternalChannelIngressSubject, -} from "./index.js"; -import { resolveChannelIngressState } from "./state.js"; +} from "./types.js"; const subject = (value: string): InternalChannelIngressSubject => ({ identifiers: [{ opaqueId: "subject-1", kind: "stable-id", value }], diff --git a/src/channels/message/index.ts b/src/channels/message/index.ts deleted file mode 100644 index 0b329f47069..00000000000 --- a/src/channels/message/index.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Public barrel for channel message delivery, live preview, receipt, receive, and recovery -// contracts used by channel plugins and core delivery code. -export { deriveDurableFinalDeliveryRequirements } from "./capabilities.js"; -export { defineChannelMessageAdapter } from "./adapter.js"; -export { createChannelMessageAdapterFromOutbound } from "./outbound-bridge.js"; -export { createDurableInboundReceiveJournalFromQueue } from "./durable-receive.js"; -export { INGRESS_CLAIM_PROCESS_ID, processPidFromOwnerId } from "./ingress-claim-owner.js"; -export { - bindIngressLifecycleToReplyOptions, - createChannelIngressDrain, - DEFAULT_INGRESS_ADOPTION_STALL_MS, -} from "./ingress-drain.js"; -export { - CHANNEL_INGRESS_RETENTION_DEFAULTS, - createChannelIngressError, - createChannelIngressMonitor, -} from "./ingress-monitor.js"; -export { - DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS, - DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, -} from "./ingress-retry-policy.js"; - -export { - verifyChannelMessageAdapterCapabilityProofs, - verifyChannelMessageLiveCapabilityAdapterProofs, - verifyChannelMessageLiveFinalizerProofs, - verifyChannelMessageReceiveAckPolicyAdapterProofs, - verifyDurableFinalCapabilityProofs, -} from "./contracts.js"; -export { - createPreviewMessageReceipt, - defineFinalizableLivePreviewAdapter, - deliverWithFinalizableLivePreviewAdapter, -} from "./live.js"; -export { - createMessageReceiptFromOutboundResults, - listMessageReceiptPlatformIds, - resolveMessageReceiptPrimaryId, -} from "./receipt.js"; -export { createMessageReceiveContext } from "./receive.js"; -export { - createChannelReplyPipeline, - createReplyPrefixContext, - createReplyPrefixOptions, - createTypingCallbacks, - resolveChannelSourceReplyDeliveryMode, -} from "./reply-pipeline.js"; -export type { ChannelIngressDrain } from "./ingress-drain.js"; -export type { - ChannelIngressMonitorDeliveryResult, - ChannelIngressMonitorLifecycle, -} from "./ingress-monitor.js"; -export type { - ChannelIngressQueue, - ChannelIngressQueueClaim, - ChannelIngressQueueClaimRef, - ChannelIngressQueueCorruptClaim, - ChannelIngressQueueRecord, -} from "./ingress-queue.js"; -export type { MessageAckPolicy, MessageReceiveContext } from "./receive.js"; -export type { - ChannelMessageAdapterShape, - ChannelMessageDurableFinalAdapter, - ChannelMessageSendMediaContext, - ChannelMessageSendPayloadContext, - ChannelMessageSendResult, - ChannelMessageSendTextContext, - ChannelMessageUnknownSendContext, - ChannelMessageUnknownSendReconciliationResult, - MessageReceipt, - MessageReceiptPart, - MessageReceiptPartKind, - MessageReceiptSourceResult, -} from "./types.js"; diff --git a/src/cli/daemon-cli/install.test.ts b/src/cli/daemon-cli/install.test.ts index aeac47ae68a..b448953840b 100644 --- a/src/cli/daemon-cli/install.test.ts +++ b/src/cli/daemon-cli/install.test.ts @@ -109,15 +109,6 @@ vi.mock("../../config/paths.js", () => ({ resolveIsNixMode: resolveIsNixModeMock, })); -vi.mock("../../commands/gateway-install-token.persist.runtime.js", () => ({ - readConfigFileSnapshot: readConfigFileSnapshotMock, - readConfigFileSnapshotForWrite: vi.fn(async () => ({ - snapshot: await readConfigFileSnapshotMock(), - writeOptions: { expectedConfigPath: "/tmp/openclaw.json" }, - })), - replaceConfigFile: replaceConfigFileMock, -})); - vi.mock("../../config/types.secrets.js", () => ({ hasConfiguredSecretInput: hasConfiguredSecretInputMock, resolveSecretInputRef: resolveSecretInputRefMock, diff --git a/src/cli/daemon-cli/install.ts b/src/cli/daemon-cli/install.ts index 67ed0c67473..44eb5bc944b 100644 --- a/src/cli/daemon-cli/install.ts +++ b/src/cli/daemon-cli/install.ts @@ -309,6 +309,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) { explicitToken: opts.token, autoGenerateWhenMissing: true, persistGeneratedToken: true, + persistence: { readConfigFileSnapshotForWrite, replaceConfigFile }, }); if (tokenResolution.unavailableReason) { fail(`Gateway install blocked: ${tokenResolution.unavailableReason}`); diff --git a/src/commands/gateway-install-token.persist.runtime.ts b/src/commands/gateway-install-token.persist.runtime.ts deleted file mode 100644 index 18a4f17416e..00000000000 --- a/src/commands/gateway-install-token.persist.runtime.ts +++ /dev/null @@ -1,3 +0,0 @@ -/** Runtime persistence seam for gateway install token config writes. */ -export { readConfigFileSnapshotForWrite } from "../config/io.js"; -export { replaceConfigFile } from "../config/mutate.js"; diff --git a/src/commands/gateway-install-token.test.ts b/src/commands/gateway-install-token.test.ts index 1f3d772b023..6c3e426a6e1 100644 --- a/src/commands/gateway-install-token.test.ts +++ b/src/commands/gateway-install-token.test.ts @@ -19,11 +19,6 @@ const resolveSecretRefValuesMock = vi.hoisted(() => vi.fn()); const secretRefKeyMock = vi.hoisted(() => vi.fn(() => "env:default:OPENCLAW_GATEWAY_TOKEN")); const randomTokenMock = vi.hoisted(() => vi.fn(() => "generated-token")); -vi.mock("./gateway-install-token.persist.runtime.js", () => ({ - readConfigFileSnapshotForWrite: readConfigFileSnapshotForWriteMock, - replaceConfigFile: replaceConfigFileMock, -})); - vi.mock("../gateway/auth.js", () => ({ resolveGatewayAuth: resolveGatewayAuthMock, })); @@ -32,7 +27,8 @@ vi.mock("../gateway/auth-install-policy.js", () => ({ shouldRequireGatewayTokenForInstall: shouldRequireGatewayTokenForInstallMock, })); -vi.mock("../secrets/ref-contract.js", () => ({ +vi.mock("../secrets/ref-contract.js", async (importOriginal) => ({ + ...(await importOriginal()), secretRefKey: secretRefKeyMock, })); @@ -52,6 +48,11 @@ function firstReplaceConfigRequest(): unknown { return call[0]; } +const persistence = { + readConfigFileSnapshotForWrite: readConfigFileSnapshotForWriteMock, + replaceConfigFile: replaceConfigFileMock, +}; + describe("resolveGatewayInstallToken", () => { beforeEach(() => { vi.clearAllMocks(); @@ -135,6 +136,7 @@ describe("resolveGatewayInstallToken", () => { env: {} as NodeJS.ProcessEnv, autoGenerateWhenMissing: true, persistGeneratedToken: true, + persistence, }); expect(result.token).toBeUndefined(); @@ -171,6 +173,7 @@ describe("resolveGatewayInstallToken", () => { env: {} as NodeJS.ProcessEnv, autoGenerateWhenMissing: true, persistGeneratedToken: true, + persistence, }); expect(result.warnings.join("\n")).toContain("saving to config"); @@ -214,6 +217,7 @@ describe("resolveGatewayInstallToken", () => { env: {} as NodeJS.ProcessEnv, autoGenerateWhenMissing: true, persistGeneratedToken: true, + persistence, }); expect(result.token).toBeUndefined(); @@ -240,6 +244,7 @@ describe("resolveGatewayInstallToken", () => { env: {} as NodeJS.ProcessEnv, autoGenerateWhenMissing: true, persistGeneratedToken: true, + persistence, }); expect(result.token).toBeUndefined(); diff --git a/src/commands/gateway-install-token.ts b/src/commands/gateway-install-token.ts index 61ea6f81c7c..cf16dc30a89 100644 --- a/src/commands/gateway-install-token.ts +++ b/src/commands/gateway-install-token.ts @@ -1,7 +1,8 @@ /** Resolves the gateway token used when installing or updating the managed service. */ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { formatCliCommand } from "../cli/command-format.js"; -import type { ConfigWriteOptions } from "../config/io.js"; +import { readConfigFileSnapshotForWrite, type ConfigWriteOptions } from "../config/io.js"; +import { replaceConfigFile } from "../config/mutate.js"; import type { OpenClawConfig } from "../config/types.js"; import type { ConfigFileSnapshot } from "../config/types.openclaw.js"; import { resolveSecretInputRef } from "../config/types.secrets.js"; @@ -13,11 +14,17 @@ import { formatUnsafeGatewayTailscaleNoAuthMessage, isUnsafeGatewayTailscaleNoAuth, } from "../shared/gateway-tailscale-auth-policy.js"; -import { +import { randomToken } from "./random-token.js"; + +type GatewayInstallTokenPersistence = { + readConfigFileSnapshotForWrite: typeof readConfigFileSnapshotForWrite; + replaceConfigFile: typeof replaceConfigFile; +}; + +const defaultGatewayInstallTokenPersistence: GatewayInstallTokenPersistence = { readConfigFileSnapshotForWrite, replaceConfigFile, -} from "./gateway-install-token.persist.runtime.js"; -import { randomToken } from "./random-token.js"; +}; type GatewayInstallTokenOptions = { config: OpenClawConfig; @@ -27,6 +34,7 @@ type GatewayInstallTokenOptions = { explicitToken?: string; autoGenerateWhenMissing?: boolean; persistGeneratedToken?: boolean; + persistence?: GatewayInstallTokenPersistence; }; type GatewayInstallTokenResolution = { @@ -42,6 +50,7 @@ async function maybePersistAutoGeneratedGatewayInstallToken(params: { configSnapshot?: ConfigFileSnapshot; configWriteOptions?: ConfigWriteOptions; warnings: string[]; + persistence: GatewayInstallTokenPersistence; }): Promise { try { const prepared = @@ -50,7 +59,7 @@ async function maybePersistAutoGeneratedGatewayInstallToken(params: { snapshot: params.configSnapshot, writeOptions: params.configWriteOptions, } - : await readConfigFileSnapshotForWrite(); + : await params.persistence.readConfigFileSnapshotForWrite(); const snapshot = params.configSnapshot ?? prepared.snapshot; if (snapshot.exists && !snapshot.valid) { params.warnings.push( @@ -70,7 +79,7 @@ async function maybePersistAutoGeneratedGatewayInstallToken(params: { : normalizeOptionalString(baseConfig.gateway.auth.token); // Only persist a generated plaintext token when config has no token or SecretRef. if (!existingTokenRef && !baseConfigToken) { - await replaceConfigFile({ + await params.persistence.replaceConfigFile({ nextConfig: { ...baseConfig, gateway: { @@ -202,6 +211,7 @@ export async function resolveGatewayInstallToken( configSnapshot: options.configSnapshot, configWriteOptions: options.configWriteOptions, warnings, + persistence: options.persistence ?? defaultGatewayInstallTokenPersistence, }); } } diff --git a/src/commands/models/load-config.runtime.ts b/src/commands/models/load-config.runtime.ts deleted file mode 100644 index 4d326bb824b..00000000000 --- a/src/commands/models/load-config.runtime.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** Runtime seams for loading model command config and secret target ids. */ -export { getModelsCommandSecretTargetIds } from "../../cli/command-secret-targets.js"; -export { - getRuntimeConfig, - getRuntimeConfigSourceSnapshot, - setRuntimeConfigSnapshot, - type OpenClawConfig, -} from "../../config/config.js"; diff --git a/src/commands/models/load-config.ts b/src/commands/models/load-config.ts index 34c4115c17a..ac036df47ec 100644 --- a/src/commands/models/load-config.ts +++ b/src/commands/models/load-config.ts @@ -1,13 +1,13 @@ /** Config loader for model commands with command-scoped secret resolution. */ import { resolveCommandConfigWithSecrets } from "../../cli/command-config-resolution.js"; -import type { RuntimeEnv } from "../../runtime.js"; +import { getModelsCommandSecretTargetIds } from "../../cli/command-secret-targets.js"; import { getRuntimeConfig, getRuntimeConfigSourceSnapshot, setRuntimeConfigSnapshot, type OpenClawConfig, - getModelsCommandSecretTargetIds, -} from "./load-config.runtime.js"; +} from "../../config/config.js"; +import type { RuntimeEnv } from "../../runtime.js"; /** Source and resolved config pair returned by model command config loading. */ type LoadedModelsConfig = { diff --git a/src/gateway/server-chat.agent-events.test.ts b/src/gateway/server-chat.agent-events.test.ts index 6908661d6e7..5381b01a668 100644 --- a/src/gateway/server-chat.agent-events.test.ts +++ b/src/gateway/server-chat.agent-events.test.ts @@ -23,15 +23,11 @@ import { import { subscribePluginSessionsChanged } from "../plugins/gateway-events.js"; const persistGatewaySessionLifecycleEventMock = vi.fn(); +const loadGatewaySessionLifecycleSnapshotMock = vi.hoisted(() => vi.fn()); const logErrorMock = vi.fn(); const normalizeLiveAssistantBufferedTextMock = vi.hoisted(() => vi.fn()); const loadGatewaySessionRow = vi.hoisted(() => vi.fn()); -vi.mock("./server-chat.persist-session-lifecycle.runtime.js", () => ({ - persistGatewaySessionLifecycleEvent: (...args: unknown[]) => - persistGatewaySessionLifecycleEventMock(...args), -})); - vi.mock("../logger.js", () => ({ logError: (...args: unknown[]) => logErrorMock(...args), })); @@ -59,10 +55,6 @@ vi.mock("../infra/heartbeat-visibility.js", () => ({ })), })); -vi.mock("./server-chat.load-gateway-session-row.runtime.js", () => ({ - loadGatewaySessionLifecycleSnapshot: vi.fn(), -})); - vi.mock("./session-utils.js", () => { const loadSessionEntry = vi.fn(() => ({ cfg: {}, @@ -76,6 +68,8 @@ vi.mock("./session-utils.js", () => { return { loadSessionEntry, loadGatewaySessionEntryReadOnly: loadSessionEntry, + loadGatewaySessionLifecycleSnapshot: (...args: unknown[]) => + loadGatewaySessionLifecycleSnapshotMock(...args), }; }); @@ -97,7 +91,6 @@ import { resolveChatErrorKindFromError, type AgentEventHandlerOptions, } from "./server-chat.js"; -import { loadGatewaySessionLifecycleSnapshot } from "./server-chat.load-gateway-session-row.runtime.js"; import { loadSessionEntry } from "./session-utils.js"; function waitForFast( @@ -129,7 +122,7 @@ describe("agent event handler", () => { legacyKey: undefined, }); vi.mocked(loadGatewaySessionRow).mockReset().mockReturnValue(null); - vi.mocked(loadGatewaySessionLifecycleSnapshot) + loadGatewaySessionLifecycleSnapshotMock .mockReset() .mockImplementation((sessionKey, options) => ({ row: options @@ -183,7 +176,8 @@ describe("agent event handler", () => { toolEventRecipients, sessionEventSubscribers, sessionMessageSubscribers, - loadGatewaySessionLifecycleSnapshotForEvent: loadGatewaySessionLifecycleSnapshot, + loadGatewaySessionLifecycleSnapshotForEvent: loadGatewaySessionLifecycleSnapshotMock, + persistGatewaySessionLifecycleEventForEvent: persistGatewaySessionLifecycleEventMock, lifecycleErrorRetryGraceMs: params?.lifecycleErrorRetryGraceMs, isChatSendRunActive: params?.isChatSendRunActive, clearTrackedActiveRun: params?.clearTrackedActiveRun ?? clearTrackedActiveRun, @@ -2384,7 +2378,7 @@ describe("agent event handler", () => { ])( "projects older lifecycle timestamps only for the owning run ($eventRunId)", async ({ eventRunId, expectedStartedAt }) => { - vi.mocked(loadGatewaySessionLifecycleSnapshot).mockReturnValue({ + loadGatewaySessionLifecycleSnapshotMock.mockReturnValue({ lifecycleRunId: "run-current", row: { key: "session-owned", diff --git a/src/gateway/server-chat.load-gateway-session-row.runtime.ts b/src/gateway/server-chat.load-gateway-session-row.runtime.ts deleted file mode 100644 index 071bbb64e31..00000000000 --- a/src/gateway/server-chat.load-gateway-session-row.runtime.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Runtime barrel for loading Gateway session rows from chat paths without -// pulling the rest of session-utils into static startup imports. -export { loadGatewaySessionLifecycleSnapshot } from "./session-utils.js"; diff --git a/src/gateway/server-chat.persist-session-lifecycle.runtime.ts b/src/gateway/server-chat.persist-session-lifecycle.runtime.ts deleted file mode 100644 index 1c044f0b88a..00000000000 --- a/src/gateway/server-chat.persist-session-lifecycle.runtime.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Runtime barrel for persisting session lifecycle events from chat paths while -// keeping lifecycle-state behind a narrow lazy import boundary. -export { persistGatewaySessionLifecycleEvent } from "./session-lifecycle-state.js"; diff --git a/src/gateway/server-chat.ts b/src/gateway/server-chat.ts index da94c3f09b0..73358848ee3 100644 --- a/src/gateway/server-chat.ts +++ b/src/gateway/server-chat.ts @@ -52,18 +52,20 @@ import type { SessionMessageSubscriberRegistry, ToolEventRecipientRegistry, } from "./server-chat-state.js"; -import { loadGatewaySessionLifecycleSnapshot } from "./server-chat.load-gateway-session-row.runtime.js"; -import { persistGatewaySessionLifecycleEvent } from "./server-chat.persist-session-lifecycle.runtime.js"; import { hasSessionChangeReceivers } from "./session-change-receivers.js"; import { buildGatewaySessionEventRow } from "./session-event-payload.js"; import { deriveGatewaySessionLifecycleProjectionPatch, isRestartRecoveryLifecycleEvent, isStaleLifecycleEventForSession, + persistGatewaySessionLifecycleEvent, } from "./session-lifecycle-state.js"; import { tryResolveSessionCompatibilityOwnerAgentId } from "./session-request-agent.js"; import { resolveSessionSubscriptionKeys } from "./session-subscription-keys.js"; -import { loadGatewaySessionEntryReadOnly } from "./session-utils.js"; +import { + loadGatewaySessionEntryReadOnly, + loadGatewaySessionLifecycleSnapshot, +} from "./session-utils.js"; import { formatForLog } from "./ws-log.js"; export { @@ -338,6 +340,7 @@ export type AgentEventHandlerOptions = { sessionEventSubscribers: SessionEventSubscriberRegistry; sessionMessageSubscribers: SessionMessageSubscriberRegistry; loadGatewaySessionLifecycleSnapshotForEvent?: typeof loadGatewaySessionLifecycleSnapshot; + persistGatewaySessionLifecycleEventForEvent?: typeof persistGatewaySessionLifecycleEvent; lifecycleErrorRetryGraceMs?: number; isChatSendRunActive?: (runId: string) => boolean; clearTrackedActiveRun?: (params: { @@ -443,6 +446,7 @@ export function createAgentEventHandler({ sessionEventSubscribers, sessionMessageSubscribers, loadGatewaySessionLifecycleSnapshotForEvent = loadGatewaySessionLifecycleSnapshot, + persistGatewaySessionLifecycleEventForEvent = persistGatewaySessionLifecycleEvent, lifecycleErrorRetryGraceMs = AGENT_LIFECYCLE_ERROR_RETRY_GRACE_MS, isChatSendRunActive = () => false, clearTrackedActiveRun, @@ -876,7 +880,7 @@ export function createAgentEventHandler({ if (sessionKey) { clearTrackedActiveRun?.({ runId: evt.runId, clientRunId, sessionKey }); if (!suppressRestartRecoveryProjection && projectSessionLifecycle) { - const persistence = persistGatewaySessionLifecycleEvent({ + const persistence = persistGatewaySessionLifecycleEventForEvent({ sessionKey, agentId: sessionAgentId, event: evt, @@ -1753,7 +1757,7 @@ export function createAgentEventHandler({ } if (projectSessionLifecycle && sessionKey && lifecyclePhase === "start") { - void persistGatewaySessionLifecycleEvent({ + void persistGatewaySessionLifecycleEventForEvent({ sessionKey, agentId: sessionAgentId, event: evt, diff --git a/src/gateway/server-methods.ts b/src/gateway/server-methods.ts index 354d81da97c..49b922045f6 100644 --- a/src/gateway/server-methods.ts +++ b/src/gateway/server-methods.ts @@ -94,7 +94,8 @@ const CORE_GATEWAY_HANDLER_MODULES = { ), diagnostics: () => import("./server-methods/diagnostics.js").then((module) => module.diagnosticsHandlers), - doctor: () => import("./server-methods/doctor.js").then((module) => module.doctorHandlers), + doctor: () => + import("./server-methods/doctor.js").then((module) => module.createDoctorHandlers()), environments: () => import("./server-methods/environments.js").then((module) => module.environmentsHandlers), worktrees: () => diff --git a/src/gateway/server-methods/doctor.memory-core-runtime.ts b/src/gateway/server-methods/doctor.memory-core-runtime.ts deleted file mode 100644 index 0316a8987bb..00000000000 --- a/src/gateway/server-methods/doctor.memory-core-runtime.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Lazy boundary for doctor memory-core repair helpers. - * - * Doctor tests mock this file so the gateway method does not import bundled - * memory-core runtime code until a repair action actually needs it. - */ -export { - dedupeDreamDiaryEntries, - loadShortTermPromotionDreamingStats, - previewGroundedRemMarkdown, - removeBackfillDiaryEntries, - removeGroundedShortTermCandidates, - repairDreamingArtifacts, - writeBackfillDiaryEntries, -} from "../../plugin-sdk/memory-core-bundled-runtime.js"; diff --git a/src/gateway/server-methods/doctor.test.ts b/src/gateway/server-methods/doctor.test.ts index 717b504accf..8568c132e02 100644 --- a/src/gateway/server-methods/doctor.test.ts +++ b/src/gateway/server-methods/doctor.test.ts @@ -58,7 +58,9 @@ vi.mock("../../plugins/memory-runtime.js", () => ({ getActiveMemorySearchManagerCore: getMemorySearchManager, })); -vi.mock("./doctor.memory-core-runtime.js", () => ({ +import { createDoctorHandlers } from "./doctor.js"; + +const doctorHandlers = createDoctorHandlers({ dedupeDreamDiaryEntries, loadShortTermPromotionDreamingStats, previewGroundedRemMarkdown, @@ -66,9 +68,7 @@ vi.mock("./doctor.memory-core-runtime.js", () => ({ removeBackfillDiaryEntries, removeGroundedShortTermCandidates, repairDreamingArtifacts, -})); - -import { doctorHandlers } from "./doctor.js"; +}); const makeRuntimeContext = () => ({ getRuntimeConfig: () => getRuntimeConfig() }); diff --git a/src/gateway/server-methods/doctor.ts b/src/gateway/server-methods/doctor.ts index 66ece6fd2eb..bc5ce28996d 100644 --- a/src/gateway/server-methods/doctor.ts +++ b/src/gateway/server-methods/doctor.ts @@ -21,20 +21,23 @@ import { resolveMemoryDreamingWorkspaces, resolveMemoryRemDreamingConfig, } from "../../memory-host-sdk/dreaming.js"; +import * as defaultMemoryCoreRuntime from "../../plugin-sdk/memory-core-bundled-runtime.js"; import { getActiveMemorySearchManagerCore } from "../../plugins/memory-runtime.js"; import { normalizeAgentId } from "../../routing/session-key.js"; import { formatError } from "../server-utils.js"; -import { - dedupeDreamDiaryEntries, - loadShortTermPromotionDreamingStats, - previewGroundedRemMarkdown, - removeBackfillDiaryEntries, - removeGroundedShortTermCandidates, - repairDreamingArtifacts, - writeBackfillDiaryEntries, -} from "./doctor.memory-core-runtime.js"; import type { GatewayRequestContext, GatewayRequestHandlers, RespondFn } from "./types.js"; +type DoctorMemoryCoreRuntime = Pick< + typeof defaultMemoryCoreRuntime, + | "dedupeDreamDiaryEntries" + | "loadShortTermPromotionDreamingStats" + | "previewGroundedRemMarkdown" + | "removeBackfillDiaryEntries" + | "removeGroundedShortTermCandidates" + | "repairDreamingArtifacts" + | "writeBackfillDiaryEntries" +>; + const MANAGED_DEEP_SLEEP_CRON_NAME = "Memory Dreaming Promotion"; const MANAGED_DEEP_SLEEP_CRON_TAG = "[managed-by=memory-core.short-term-promotion]"; const DEEP_SLEEP_SYSTEM_EVENT_TEXT = "__openclaw_memory_core_short_term_promotion_dream__"; @@ -381,6 +384,7 @@ function trimDreamingEntries( async function loadDreamingStoreStats( workspaceDir: string, nowMs: number, + loadShortTermPromotionDreamingStats: DoctorMemoryCoreRuntime["loadShortTermPromotionDreamingStats"], timezone?: string, ): Promise { try { @@ -699,7 +703,9 @@ const SKIPPED_MEMORY_EMBEDDING_PROBE = { error: "memory embedding readiness not checked; run `openclaw memory status --deep` to probe", } as const; -export const doctorHandlers: GatewayRequestHandlers = { +export const createDoctorHandlers = ( + memoryCoreRuntime: DoctorMemoryCoreRuntime = defaultMemoryCoreRuntime, +): GatewayRequestHandlers => ({ "doctor.memory.status": async ({ respond, context, params }) => { const resolved = resolveDoctorMemoryAgent(context, params, respond); if (!resolved) { @@ -755,7 +761,12 @@ export const doctorHandlers: GatewayRequestHandlers = { ? mergeDreamingStoreStats( await Promise.all( allWorkspaces.map((entry) => - loadDreamingStoreStats(entry, nowMs, dreamingConfig.timezone), + loadDreamingStoreStats( + entry, + nowMs, + memoryCoreRuntime.loadShortTermPromotionDreamingStats, + dreamingConfig.timezone, + ), ), ), ) @@ -850,7 +861,7 @@ export const doctorHandlers: GatewayRequestHandlers = { respond(true, payload, undefined); return; } - const grounded = await previewGroundedRemMarkdown({ + const grounded = await memoryCoreRuntime.previewGroundedRemMarkdown({ workspaceDir, inputPaths: sourceFiles, }); @@ -871,7 +882,7 @@ export const doctorHandlers: GatewayRequestHandlers = { }; }) .filter((entry): entry is NonNullable => entry !== null); - const written = await writeBackfillDiaryEntries({ + const written = await memoryCoreRuntime.writeBackfillDiaryEntries({ workspaceDir, entries, timezone: remConfig.timezone, @@ -894,7 +905,7 @@ export const doctorHandlers: GatewayRequestHandlers = { return; } const { agentId, workspaceDir } = target; - const removed = await removeBackfillDiaryEntries({ workspaceDir }); + const removed = await memoryCoreRuntime.removeBackfillDiaryEntries({ workspaceDir }); const dreamDiary = await readDreamDiary(workspaceDir); const payload: DoctorMemoryDreamActionPayload = { agentId, @@ -911,7 +922,7 @@ export const doctorHandlers: GatewayRequestHandlers = { return; } const { agentId, workspaceDir } = target; - const removed = await removeGroundedShortTermCandidates({ workspaceDir }); + const removed = await memoryCoreRuntime.removeGroundedShortTermCandidates({ workspaceDir }); const payload: DoctorMemoryDreamActionPayload = { agentId, action: "resetGroundedShortTerm", @@ -925,7 +936,7 @@ export const doctorHandlers: GatewayRequestHandlers = { return; } const { agentId, workspaceDir } = target; - const repair = await repairDreamingArtifacts({ workspaceDir }); + const repair = await memoryCoreRuntime.repairDreamingArtifacts({ workspaceDir }); const payload: DoctorMemoryDreamActionPayload = { agentId, action: "repairDreamingArtifacts", @@ -944,7 +955,7 @@ export const doctorHandlers: GatewayRequestHandlers = { return; } const { agentId, workspaceDir } = target; - const dedupe = await dedupeDreamDiaryEntries({ workspaceDir }); + const dedupe = await memoryCoreRuntime.dedupeDreamDiaryEntries({ workspaceDir }); const dreamDiary = await readDreamDiary(workspaceDir); const payload: DoctorMemoryDreamActionPayload = { agentId, @@ -957,5 +968,5 @@ export const doctorHandlers: GatewayRequestHandlers = { }; respond(true, payload, undefined); }, -}; +}); /* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */ diff --git a/src/infra/openclaw-cli-shim.test.ts b/src/infra/openclaw-cli-shim.test.ts index 3db14c90c1c..9f7e1c33501 100644 --- a/src/infra/openclaw-cli-shim.test.ts +++ b/src/infra/openclaw-cli-shim.test.ts @@ -38,16 +38,17 @@ describe.skipIf(process.platform === "win32")("Gateway agent CLI shim", () => { mode: 0o700, }); - const shim = await prepareGatewayAgentCliShim({ + await prepareGatewayAgentCliShim({ env: testCase.profile ? { OPENCLAW_PROFILE: testCase.profile } : {}, invocation: { command: process.execPath, args: [entryPath], cwd: root }, stateDir, }); + const shimBinDir = path.join(stateDir, "tmp", "agent-cli"); const config = { tools: { exec: { pathPrepend: [staleBinDir] } }, } satisfies OpenClawConfig; const execConfig = resolveExecToolConfig({ cfg: config }); - expect(execConfig.pathPrepend?.slice(0, 2)).toEqual([shim.binDir, staleBinDir]); + expect(execConfig.pathPrepend?.slice(0, 2)).toEqual([shimBinDir, staleBinDir]); process.env.OPENCLAW_EXEC_SHELL_SNAPSHOT = "0"; process.env.PATH = `${staleBinDir}${path.delimiter}${process.env.PATH ?? ""}`; @@ -67,7 +68,7 @@ describe.skipIf(process.platform === "win32")("Gateway agent CLI shim", () => { expect(JSON.parse(readExecText(result))).toEqual({ source: "gateway", args: testCase.expectedArgs, - pathHead: shim.binDir, + pathHead: shimBinDir, }); }); }); @@ -75,7 +76,7 @@ describe.skipIf(process.platform === "win32")("Gateway agent CLI shim", () => { it("renders a Windows PATH launcher for the running CLI", async () => { await withTempDir("openclaw-agent-cli-shim-win-", async (root) => { - const result = await prepareGatewayAgentCliShim({ + await prepareGatewayAgentCliShim({ env: { OPENCLAW_PROFILE: "work" }, invocation: { command: "C:\\Program Files\\nodejs\\node.exe", @@ -86,8 +87,8 @@ it("renders a Windows PATH launcher for the running CLI", async () => { stateDir: root, }); - expect(path.basename(result.executablePath)).toBe("openclaw.cmd"); - expect(await fs.readFile(result.executablePath, "utf8")).toBe( + const executablePath = path.join(root, "tmp", "agent-cli", "openclaw.cmd"); + expect(await fs.readFile(executablePath, "utf8")).toBe( '@echo off\r\n"C:\\Program Files\\nodejs\\node.exe" C:\\OpenClaw\\dist\\index.js --profile work %*\r\n', ); }); diff --git a/src/infra/openclaw-cli-shim.ts b/src/infra/openclaw-cli-shim.ts index 33c5a4bf7aa..af12e46dde6 100644 --- a/src/infra/openclaw-cli-shim.ts +++ b/src/infra/openclaw-cli-shim.ts @@ -49,7 +49,7 @@ export async function prepareGatewayAgentCliShim( platform?: NodeJS.Platform; stateDir?: string; } = {}, -): Promise<{ binDir: string; executablePath: string }> { +): Promise { const env = options.env ?? process.env; const platform = options.platform ?? process.platform; const invocation = options.invocation ?? resolveCurrentOpenClawCliInvocation([]); @@ -70,7 +70,6 @@ export async function prepareGatewayAgentCliShim( tempPrefix: "openclaw-agent-cli", }); gatewayAgentCliState.binDir = binDir; - return { binDir, executablePath }; } /** Clear a prepared launcher after startup failure; normal Gateway close resets it globally. */ diff --git a/src/plugin-sdk/channel-ingress-runtime.ts b/src/plugin-sdk/channel-ingress-runtime.ts index bbcae72d6c4..65be74c8dd7 100644 --- a/src/plugin-sdk/channel-ingress-runtime.ts +++ b/src/plugin-sdk/channel-ingress-runtime.ts @@ -9,41 +9,43 @@ export { channelIngressRoutes, createChannelIngressResolver, - defineStableChannelIngressIdentity, - readChannelIngressStoreAllowFromForDmPolicy, resolveChannelMessageIngress, resolveStableChannelMessageIngress, -} from "../channels/message-access/index.js"; +} from "../channels/message-access/runtime.js"; +export { defineStableChannelIngressIdentity } from "../channels/message-access/runtime-identity.js"; +export { readChannelIngressStoreAllowFromForDmPolicy } from "../channels/message-access/store-allow-from.js"; export { resolveChannelImplicitMentions } from "../config/implicit-mentions.js"; export type { - AccessGroupMembershipFact, - ChannelIngressDecision, ChannelIngressAccessGroupMembershipResolver, ChannelIngressCommandPresetInput, ChannelIngressConfigInput, ChannelIngressContextBinding, - ChannelIngressEventInput, ChannelIngressEventPresetInput, ChannelIngressIdentityDescriptor, ChannelIngressIdentityAlias, ChannelIngressIdentityField, ChannelIngressIdentitySubjectInput, - ChannelIngressIdentifierKind, - ChannelIngressPolicyInput, ChannelIngressRouteAccess, ChannelIngressRouteDescriptor, ChannelIngressResolver, ChannelIngressResolverMessageParams, - ChannelIngressStateInput, - ChannelIngressState, ChannelMessageIngressCommandInput, CreateChannelIngressResolverParams, - IngressReasonCode, ResolvedChannelMessageIngress, ResolveChannelMessageIngressParams, ResolveStableChannelMessageIngressParams, StableChannelIngressIdentityParams, -} from "../channels/message-access/index.js"; +} from "../channels/message-access/runtime-types.js"; +export type { + AccessGroupMembershipFact, + ChannelIngressDecision, + ChannelIngressEventInput, + ChannelIngressIdentifierKind, + ChannelIngressPolicyInput, + ChannelIngressState, + ChannelIngressStateInput, + IngressReasonCode, +} from "../channels/message-access/types.js"; export type { ResolvedChannelImplicitMentions } from "../config/implicit-mentions.js"; import { diff --git a/src/plugin-sdk/channel-message.test.ts b/src/plugin-sdk/channel-message.test.ts index 9d6375aaa72..28d51ce0e78 100644 --- a/src/plugin-sdk/channel-message.test.ts +++ b/src/plugin-sdk/channel-message.test.ts @@ -2,7 +2,7 @@ * Tests channel message helper behavior and mocked runtime interactions. */ import { beforeAll, describe, expect, it, vi } from "vitest"; -import { defineChannelMessageAdapter as defineCoreChannelMessageAdapter } from "../channels/message/index.js"; +import { defineChannelMessageAdapter as defineCoreChannelMessageAdapter } from "../channels/message/adapter.js"; import { defineChannelMessageAdapter, type ChannelMessageDurableFinalAdapter, diff --git a/src/plugin-sdk/channel-outbound.ts b/src/plugin-sdk/channel-outbound.ts index 5aef2c6e9e2..1d571a609cf 100644 --- a/src/plugin-sdk/channel-outbound.ts +++ b/src/plugin-sdk/channel-outbound.ts @@ -21,25 +21,33 @@ export { export type { OutboundMessageIdentity } from "../channels/message/outbound-echo.js"; export { bindIngressLifecycleToReplyOptions, - CHANNEL_INGRESS_RETENTION_DEFAULTS, - createChannelIngressError, createChannelIngressDrain, - createChannelIngressMonitor, - createReplyPrefixContext, - createReplyPrefixOptions, - createTypingCallbacks, - createChannelReplyPipeline as createChannelMessageReplyPipeline, // Narrow drain seam by maintainer decision (#108924): factory, lifecycle binding, // tuning constants, and processPidFromOwnerId (telegram transport display). All other // claim/retry/adoption internals stay core-owned; test helpers live on the // private-local plugin-state-test-runtime subpath. DEFAULT_INGRESS_ADOPTION_STALL_MS, - DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, +} from "../channels/message/ingress-drain.js"; +export { + CHANNEL_INGRESS_RETENTION_DEFAULTS, + createChannelIngressError, + createChannelIngressMonitor, +} from "../channels/message/ingress-monitor.js"; +export { DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS, + DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, +} from "../channels/message/ingress-retry-policy.js"; +export { INGRESS_CLAIM_PROCESS_ID, processPidFromOwnerId, +} from "../channels/message/ingress-claim-owner.js"; +export { + createChannelReplyPipeline as createChannelMessageReplyPipeline, + createReplyPrefixContext, + createReplyPrefixOptions, + createTypingCallbacks, resolveChannelSourceReplyDeliveryMode as resolveChannelMessageSourceReplyDeliveryMode, -} from "../channels/message/index.js"; +} from "../channels/message/reply-pipeline.js"; // Bare interval/stop orchestration for channels that own their typing renewal // policy (e.g. per-message reply budgets) instead of the createTypingCallbacks lifecycle. export { createTypingKeepaliveLoop } from "../channels/typing-lifecycle.js"; @@ -151,24 +159,41 @@ export type { ChannelProgressDraftCompositorLine, ChannelProgressDraftCompositorSnapshot, } from "../channels/progress-draft-compositor.js"; +export { deriveDurableFinalDeliveryRequirements } from "../channels/message/capabilities.js"; +export { defineChannelMessageAdapter } from "../channels/message/adapter.js"; +export { createChannelMessageAdapterFromOutbound } from "../channels/message/outbound-bridge.js"; +export { createDurableInboundReceiveJournalFromQueue } from "../channels/message/durable-receive.js"; export { - createChannelMessageAdapterFromOutbound, - createDurableInboundReceiveJournalFromQueue, - createMessageReceiptFromOutboundResults, - listMessageReceiptPlatformIds, - createMessageReceiveContext, - createPreviewMessageReceipt, - defineFinalizableLivePreviewAdapter, - deriveDurableFinalDeliveryRequirements, - deliverWithFinalizableLivePreviewAdapter, - defineChannelMessageAdapter, - resolveMessageReceiptPrimaryId, verifyChannelMessageAdapterCapabilityProofs, verifyChannelMessageLiveCapabilityAdapterProofs, verifyChannelMessageLiveFinalizerProofs, verifyChannelMessageReceiveAckPolicyAdapterProofs, verifyDurableFinalCapabilityProofs, -} from "../channels/message/index.js"; +} from "../channels/message/contracts.js"; +export { + createPreviewMessageReceipt, + defineFinalizableLivePreviewAdapter, + deliverWithFinalizableLivePreviewAdapter, +} from "../channels/message/live.js"; +export { + createMessageReceiptFromOutboundResults, + listMessageReceiptPlatformIds, + resolveMessageReceiptPrimaryId, +} from "../channels/message/receipt.js"; +export { createMessageReceiveContext } from "../channels/message/receive.js"; +export type { ChannelIngressDrain } from "../channels/message/ingress-drain.js"; +export type { + ChannelIngressMonitorDeliveryResult, + ChannelIngressMonitorLifecycle, +} from "../channels/message/ingress-monitor.js"; +export type { + ChannelIngressQueue, + ChannelIngressQueueClaim, + ChannelIngressQueueClaimRef, + ChannelIngressQueueCorruptClaim, + ChannelIngressQueueRecord, +} from "../channels/message/ingress-queue.js"; +export type { MessageAckPolicy, MessageReceiveContext } from "../channels/message/receive.js"; export type { ChannelMessageAdapterShape, ChannelMessageDurableFinalAdapter, @@ -178,21 +203,11 @@ export type { ChannelMessageSendTextContext, ChannelMessageUnknownSendContext, ChannelMessageUnknownSendReconciliationResult, - ChannelIngressDrain, - ChannelIngressMonitorDeliveryResult, - ChannelIngressMonitorLifecycle, - ChannelIngressQueue, - ChannelIngressQueueClaim, - ChannelIngressQueueClaimRef, - ChannelIngressQueueCorruptClaim, - ChannelIngressQueueRecord, - MessageAckPolicy, - MessageReceiveContext, MessageReceipt, MessageReceiptPart, MessageReceiptPartKind, MessageReceiptSourceResult, -} from "../channels/message/index.js"; +} from "../channels/message/types.js"; /** Lazily forwards inbound reply delivery through the channel turn durable-delivery module. */ export const deliverInboundReplyWithMessageSendContext: ChannelDurableDeliveryModule["deliverInboundReplyWithMessageSendContextCore"] = diff --git a/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts b/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts index 8124edb80f4..ed88c179dd0 100644 --- a/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts +++ b/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts @@ -192,7 +192,13 @@ const RUNTIME_API_EXPORT_GUARDS: Record = { [contractPluginPath({ rootDir: ROOT_DIR, pluginId: "slack", relativePath: "runtime-api.ts" })]: [ 'export { handleSlackAction, slackActionRuntime, type SlackActionContext } from "./src/action-runtime.js";', 'export { listSlackDirectoryGroupsLive, listSlackDirectoryPeersLive } from "./src/directory-live.js";', - 'export { deleteSlackMessage, editSlackMessage, getSlackMemberInfo, listEnabledSlackAccounts, listSlackAccountIds, listSlackEmojis, listSlackPins, listSlackReactions, monitorSlackProvider, pinSlackMessage, probeSlack, reactSlackMessage, readSlackMessages, removeOwnSlackReactions, removeSlackReaction, resolveDefaultSlackAccountId, resolveSlackAccount, resolveSlackAppToken, resolveSlackBotToken, resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy, sendMessageSlack, sendSlackMessage, unpinSlackMessage } from "./src/index.js";', + 'export { listEnabledSlackAccounts, listSlackAccountIds, resolveDefaultSlackAccountId, resolveSlackAccount } from "./src/accounts.js";', + 'export { deleteSlackMessage, editSlackMessage, getSlackMemberInfo, listSlackEmojis, listSlackPins, listSlackReactions, pinSlackMessage, reactSlackMessage, readSlackMessages, removeOwnSlackReactions, removeSlackReaction, sendSlackMessage, unpinSlackMessage } from "./src/actions.js";', + 'export { resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy } from "./src/group-policy.js";', + 'export { monitorSlackProvider } from "./src/monitor.js";', + 'export { probeSlack } from "./src/probe.js";', + 'export { sendMessageSlack } from "./src/send.js";', + 'export { resolveSlackAppToken, resolveSlackBotToken } from "./src/token.js";', 'export { resolveSlackChannelAllowlist, type SlackChannelLookup, type SlackChannelResolution } from "./src/resolve-channels.js";', 'export { resolveSlackUserAllowlist, type SlackUserLookup, type SlackUserResolution } from "./src/resolve-users.js";', 'export { registerSlackPluginHttpRoutes } from "./src/http/plugin-routes.js";',