diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c6148060d..25f47361e16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai ### Changes +- Plugins/runtime: expose provider-backed thinking policy and normalization through `api.runtime.agent`, letting tool plugins validate thinking overrides without duplicating provider/model level lists. Thanks @openclaw. - Providers: add Cerebras as a bundled plugin with onboarding, static model catalog, docs, and manifest-owned endpoint metadata. - Memory/OpenAI-compatible: add optional `memorySearch.inputType`, `queryInputType`, and `documentInputType` config for asymmetric embedding endpoints, including direct query embeddings and provider batch indexing. Carries forward #63313 and #60727. Thanks @HOYALIM and @prospect1314521. - Ollama/memory: add model-specific retrieval query prefixes for `nomic-embed-text`, `qwen3-embedding`, and `mxbai-embed-large` memory-search queries while leaving document batches unchanged. Carries forward #45013. Thanks @laolin5564. diff --git a/docs/.generated/plugin-sdk-api-baseline.sha256 b/docs/.generated/plugin-sdk-api-baseline.sha256 index 00cac5fc495..a46623a757d 100644 --- a/docs/.generated/plugin-sdk-api-baseline.sha256 +++ b/docs/.generated/plugin-sdk-api-baseline.sha256 @@ -1,2 +1,2 @@ -491267e919c6bf426f673a9066e703811c7779a32de87edd0ce493147fd4438e plugin-sdk-api-baseline.json -590d21aeb520f34b5bf23abb7b17602b204f170547c772d60b604bb34a3940bb plugin-sdk-api-baseline.jsonl +b81647828ee6599cdd1d76d96ea02c92ccdebb8c1b3b443cefe10ca8bd2ddbfe plugin-sdk-api-baseline.json +ca9f3569352522621857b51872f30b3c31881505fd9eff2451b1b46d77670726 plugin-sdk-api-baseline.jsonl diff --git a/docs/plugins/sdk-runtime.md b/docs/plugins/sdk-runtime.md index 12cc977a56d..3c1571f4376 100644 --- a/docs/plugins/sdk-runtime.md +++ b/docs/plugins/sdk-runtime.md @@ -62,7 +62,18 @@ Internal OpenClaw runtime code has the same direction: load config once at the C const identity = api.runtime.agent.resolveAgentIdentity(cfg); // Get default thinking level - const thinking = api.runtime.agent.resolveThinkingDefault(cfg, provider, model); + const thinking = api.runtime.agent.resolveThinkingDefault({ + cfg, + provider, + model, + }); + + // Validate a user-provided thinking level against the active provider profile + const policy = api.runtime.agent.resolveThinkingPolicy({ provider, model }); + const level = api.runtime.agent.normalizeThinkingLevel("extra high"); + if (level && policy.levels.some((entry) => entry.id === level)) { + // pass level to an embedded run + } // Get agent timeout const timeoutMs = api.runtime.agent.resolveAgentTimeoutMs(cfg); @@ -86,6 +97,10 @@ Internal OpenClaw runtime code has the same direction: load config once at the C `runEmbeddedPiAgent(...)` remains as a compatibility alias. + `resolveThinkingPolicy(...)` returns the provider/model's supported thinking levels and optional default. Provider plugins own the model-specific profile through their thinking hooks, so tool plugins should call this runtime helper instead of importing or duplicating provider lists. + + `normalizeThinkingLevel(...)` converts user text such as `on`, `x-high`, or `extra high` to the canonical stored level before checking it against the resolved policy. + **Session store helpers** are under `api.runtime.agent.session`: ```typescript diff --git a/docs/plugins/sdk-subpaths.md b/docs/plugins/sdk-subpaths.md index 9e42d9a4f5a..e3adf2818b6 100644 --- a/docs/plugins/sdk-subpaths.md +++ b/docs/plugins/sdk-subpaths.md @@ -138,7 +138,7 @@ For the plugin authoring guide, see [Plugin SDK overview](/plugins/sdk-overview) | `plugin-sdk/allow-from` | `formatAllowFromLowercase` | | `plugin-sdk/channel-secret-runtime` | Narrow secret-contract collection helpers for channel/plugin secret surfaces | | `plugin-sdk/secret-ref-runtime` | Narrow `coerceSecretRef` and SecretRef typing helpers for secret-contract/config parsing | - | `plugin-sdk/security-runtime` | Shared trust, DM gating, external-content, constant-time secret comparison, and secret-collection helpers | + | `plugin-sdk/security-runtime` | Shared trust, DM gating, external-content, sensitive text redaction, constant-time secret comparison, and secret-collection helpers | | `plugin-sdk/ssrf-policy` | Host allowlist and private-network SSRF policy helpers | | `plugin-sdk/ssrf-dispatcher` | Narrow pinned-dispatcher helpers without the broad infra runtime surface | | `plugin-sdk/ssrf-runtime` | Pinned-dispatcher, SSRF-guarded fetch, SSRF error, and SSRF policy helpers | @@ -201,7 +201,7 @@ For the plugin authoring guide, see [Plugin SDK overview](/plugins/sdk-overview) | `plugin-sdk/provider-zai-endpoint` | Z.AI endpoint detection helpers | | `plugin-sdk/infra-runtime` | System event/heartbeat helpers | | `plugin-sdk/collection-runtime` | Small bounded cache helpers | - | `plugin-sdk/diagnostic-runtime` | Diagnostic flag and event helpers | + | `plugin-sdk/diagnostic-runtime` | Diagnostic flag, event, and trace-context helpers | | `plugin-sdk/error-runtime` | Error graph, formatting, shared error classification helpers, `isApprovalNotFoundError` | | `plugin-sdk/fetch-runtime` | Wrapped fetch, proxy, and pinned lookup helpers | | `plugin-sdk/runtime-fetch` | Dispatcher-aware runtime fetch without proxy/guarded-fetch imports | diff --git a/docs/tools/thinking.md b/docs/tools/thinking.md index a398f3f53ed..7693e4228e3 100644 --- a/docs/tools/thinking.md +++ b/docs/tools/thinking.md @@ -124,5 +124,6 @@ Malformed local-model reasoning tags are handled conservatively. Closed ` - Provider plugins can expose `resolveThinkingProfile(ctx)` to define the model's supported levels and default. - Each profile level has a stored canonical `id` (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `adaptive`, or `max`) and may include a display `label`. Binary providers use `{ id: "low", label: "on" }`. +- Tool plugins that need to validate an explicit thinking override should use `api.runtime.agent.resolveThinkingPolicy({ provider, model })` plus `api.runtime.agent.normalizeThinkingLevel(...)`; they should not keep their own provider/model level lists. - Published legacy hooks (`supportsXHighThinking`, `isBinaryThinking`, and `resolveDefaultThinkingLevel`) remain as compatibility adapters, but new custom level sets should use `resolveThinkingProfile`. - Gateway rows/defaults expose `thinkingLevels`, `thinkingOptions`, and `thinkingDefault` so ACP/chat clients render the same profile ids and labels that runtime validation uses. diff --git a/extensions/diagnostics-otel/api.ts b/extensions/diagnostics-otel/api.ts index 01d7aed8989..d06167958bc 100644 --- a/extensions/diagnostics-otel/api.ts +++ b/extensions/diagnostics-otel/api.ts @@ -1 +1,20 @@ -export * from "openclaw/plugin-sdk/diagnostics-otel"; +export { + createChildDiagnosticTraceContext, + createDiagnosticTraceContext, + emitDiagnosticEvent, + formatDiagnosticTraceparent, + isValidDiagnosticSpanId, + isValidDiagnosticTraceFlags, + isValidDiagnosticTraceId, + onDiagnosticEvent, + parseDiagnosticTraceparent, + type DiagnosticEventMetadata, + type DiagnosticEventPayload, + type DiagnosticTraceContext, +} from "openclaw/plugin-sdk/diagnostic-runtime"; +export { emptyPluginConfigSchema, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; +export type { + OpenClawPluginService, + OpenClawPluginServiceContext, +} from "openclaw/plugin-sdk/plugin-entry"; +export { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime"; diff --git a/extensions/diagnostics-prometheus/api.ts b/extensions/diagnostics-prometheus/api.ts index 079cfbecd8c..513af17d202 100644 --- a/extensions/diagnostics-prometheus/api.ts +++ b/extensions/diagnostics-prometheus/api.ts @@ -1 +1,12 @@ -export * from "openclaw/plugin-sdk/diagnostics-prometheus"; +export type { + DiagnosticEventMetadata, + DiagnosticEventPayload, +} from "openclaw/plugin-sdk/diagnostic-runtime"; +export { + emptyPluginConfigSchema, + type OpenClawPluginApi, + type OpenClawPluginHttpRouteHandler, + type OpenClawPluginService, + type OpenClawPluginServiceContext, +} from "openclaw/plugin-sdk/plugin-entry"; +export { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime"; diff --git a/extensions/diffs/api.ts b/extensions/diffs/api.ts index e6fbaf9022a..dbbc492439a 100644 --- a/extensions/diffs/api.ts +++ b/extensions/diffs/api.ts @@ -1 +1,10 @@ -export * from "openclaw/plugin-sdk/diffs"; +export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; +export { + definePluginEntry, + type AnyAgentTool, + type OpenClawPluginApi, + type OpenClawPluginConfigSchema, + type OpenClawPluginToolContext, + type PluginLogger, +} from "openclaw/plugin-sdk/plugin-entry"; +export { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path"; diff --git a/extensions/googlechat/runtime-api.ts b/extensions/googlechat/runtime-api.ts index f56b9de6945..4df77e72bce 100644 --- a/extensions/googlechat/runtime-api.ts +++ b/extensions/googlechat/runtime-api.ts @@ -30,6 +30,7 @@ export { export { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status"; export { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking"; export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; +export { GoogleChatConfigSchema } from "openclaw/plugin-sdk/channel-config-schema-legacy"; export { GROUP_POLICY_BLOCKED_LABEL, isDangerousNameMatchingEnabled, @@ -41,11 +42,7 @@ export { fetchRemoteMedia, resolveChannelMediaMaxBytes } from "openclaw/plugin-s export { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media"; export type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store"; export { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime"; -export { - GoogleChatConfigSchema, - type GoogleChatAccountConfig, - type GoogleChatConfig, -} from "openclaw/plugin-sdk/googlechat-runtime-shared"; +export type { GoogleChatAccountConfig, GoogleChatConfig } from "openclaw/plugin-sdk/config-runtime"; export { extractToolSend } from "openclaw/plugin-sdk/tool-send"; export { resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-inbound"; export { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope"; diff --git a/extensions/llm-task/api.ts b/extensions/llm-task/api.ts index 8eebdd06e0b..19d4e17880f 100644 --- a/extensions/llm-task/api.ts +++ b/extensions/llm-task/api.ts @@ -1 +1,6 @@ -export * from "openclaw/plugin-sdk/llm-task"; +export * from "./src/runtime-api.js"; +export { + definePluginEntry, + type AnyAgentTool, + type OpenClawPluginApi, +} from "openclaw/plugin-sdk/plugin-entry"; diff --git a/extensions/llm-task/src/llm-task-tool.test.ts b/extensions/llm-task/src/llm-task-tool.test.ts index 546ef31fa53..1f52eae3a5b 100644 --- a/extensions/llm-task/src/llm-task-tool.test.ts +++ b/extensions/llm-task/src/llm-task-tool.test.ts @@ -41,7 +41,6 @@ vi.mock("../api.js", async () => { return { ...actual, resolvePreferredOpenClawTmpDir: () => "/tmp", - supportsXHighThinking: () => false, }; }); @@ -52,6 +51,30 @@ const runEmbeddedPiAgent = vi.fn(async () => ({ payloads: [{ text: "{}" }], })); +const resolveThinkingPolicy = vi.fn(() => ({ + levels: [ + { id: "off", label: "off" }, + { id: "minimal", label: "minimal" }, + { id: "low", label: "low" }, + { id: "medium", label: "medium" }, + { id: "high", label: "high" }, + ], +})); + +const normalizeThinkingLevel = vi.fn((raw?: string | null) => { + const value = raw?.trim().toLowerCase(); + if (!value) { + return undefined; + } + if (value === "on") { + return "low"; + } + if (["off", "minimal", "low", "medium", "high", "xhigh", "adaptive", "max"].includes(value)) { + return value; + } + return undefined; +}); + function fakeApi(overrides: any = {}) { return { id: "llm-task", @@ -65,6 +88,8 @@ function fakeApi(overrides: any = {}) { version: "test", agent: { runEmbeddedPiAgent, + resolveThinkingPolicy, + normalizeThinkingLevel, }, }, logger: { debug() {}, info() {}, warn() {}, error() {} }, @@ -170,6 +195,10 @@ describe("llm-task tool (json-only)", () => { mockEmbeddedRunJson({ ok: true }); const call = await executeEmbeddedRun({ prompt: "x", thinking: "high" }); expect(call.thinkLevel).toBe("high"); + expect(resolveThinkingPolicy).toHaveBeenCalledWith({ + provider: "openai-codex", + model: "gpt-5.2", + }); }); it("normalizes thinking aliases", async () => { diff --git a/extensions/llm-task/src/llm-task-tool.ts b/extensions/llm-task/src/llm-task-tool.ts index 4d195724183..6449fb3f0a4 100644 --- a/extensions/llm-task/src/llm-task-tool.ts +++ b/extensions/llm-task/src/llm-task-tool.ts @@ -3,12 +3,7 @@ import path from "node:path"; import Ajv from "ajv"; import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { Type } from "typebox"; -import { - formatThinkingLevels, - isThinkingLevelSupported, - normalizeThinkLevel, - resolvePreferredOpenClawTmpDir, -} from "../api.js"; +import { resolvePreferredOpenClawTmpDir } from "../api.js"; import type { OpenClawPluginApi } from "../api.js"; const AjvCtor = Ajv as unknown as typeof import("ajv").default; @@ -70,8 +65,18 @@ type LlmTaskParams = { timeoutMs?: unknown; }; -const INVALID_THINKING_LEVELS_HINT = - "off, minimal, low, medium, high, adaptive, xhigh where supported, and max where supported"; +type ThinkingPolicy = ReturnType; + +function formatThinkingPolicy(policy: ThinkingPolicy): string { + return policy.levels.map((level) => level.label).join(", "); +} + +function supportsThinkingPolicyLevel( + policy: ThinkingPolicy, + level: ReturnType, +): boolean { + return !!level && policy.levels.some((entry) => entry.id === level); +} export function createLlmTaskTool(api: OpenClawPluginApi) { return { @@ -148,24 +153,22 @@ export function createLlmTaskTool(api: OpenClawPluginApi) { const thinkingRaw = typeof params.thinking === "string" && params.thinking.trim() ? params.thinking : undefined; - const thinkLevel = thinkingRaw ? normalizeThinkLevel(thinkingRaw) : undefined; - if (thinkingRaw && !thinkLevel) { - throw new Error( - `Invalid thinking level "${thinkingRaw}". Use one of: ${INVALID_THINKING_LEVELS_HINT}.`, - ); - } - let resolvedThinkLevel = thinkLevel; - if ( - thinkLevel && - !isThinkingLevelSupported({ - provider, - model, - level: thinkLevel, - }) - ) { - throw new Error( - `Thinking level "${thinkLevel}" is not supported for ${provider}/${model}. Use one of: ${formatThinkingLevels(provider, model)}.`, - ); + let thinkLevel: ReturnType = + undefined; + if (thinkingRaw) { + const thinkingPolicy = api.runtime.agent.resolveThinkingPolicy({ provider, model }); + const thinkingLevelsHint = formatThinkingPolicy(thinkingPolicy); + thinkLevel = api.runtime.agent.normalizeThinkingLevel(thinkingRaw); + if (!thinkLevel) { + throw new Error( + `Invalid thinking level "${thinkingRaw}". Use one of: ${thinkingLevelsHint}.`, + ); + } + if (!supportsThinkingPolicyLevel(thinkingPolicy, thinkLevel)) { + throw new Error( + `Thinking level "${thinkLevel}" is not supported for ${provider}/${model}. Use one of: ${thinkingLevelsHint}.`, + ); + } } const timeoutMs = @@ -225,7 +228,7 @@ export function createLlmTaskTool(api: OpenClawPluginApi) { model, authProfileId, authProfileIdSource: authProfileId ? "user" : "auto", - thinkLevel: resolvedThinkLevel, + thinkLevel, streamParams, disableTools: true, }); diff --git a/extensions/llm-task/src/runtime-api.ts b/extensions/llm-task/src/runtime-api.ts new file mode 100644 index 00000000000..df7c6b16db6 --- /dev/null +++ b/extensions/llm-task/src/runtime-api.ts @@ -0,0 +1 @@ +export { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path"; diff --git a/extensions/matrix/runtime-api.ts b/extensions/matrix/runtime-api.ts index 594a280bc81..743963e89da 100644 --- a/extensions/matrix/runtime-api.ts +++ b/extensions/matrix/runtime-api.ts @@ -29,13 +29,12 @@ export { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store"; export type { ChannelDirectoryEntry, ChannelMessageActionContext, - OpenClawConfig, - PluginRuntime, - RuntimeLogger, - RuntimeEnv, - WizardPrompter, -} from "openclaw/plugin-sdk/matrix-runtime-shared"; -export { formatZonedTimestamp } from "openclaw/plugin-sdk/matrix-runtime-shared"; +} from "openclaw/plugin-sdk/channel-contract"; +export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; +export { formatZonedTimestamp } from "openclaw/plugin-sdk/core"; +export type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk/plugin-runtime"; +export type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env"; +export type { WizardPrompter } from "openclaw/plugin-sdk/setup"; export function chunkTextForOutbound(text: string, limit: number): string[] { const chunks: string[] = []; diff --git a/extensions/matrix/src/onboarding.ts b/extensions/matrix/src/onboarding.ts index 1c48c1e1a6a..9d26e548283 100644 --- a/extensions/matrix/src/onboarding.ts +++ b/extensions/matrix/src/onboarding.ts @@ -1,7 +1,5 @@ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id"; import type { DmPolicy } from "openclaw/plugin-sdk/config-runtime"; -import type { WizardPrompter } from "openclaw/plugin-sdk/matrix-runtime-shared"; -import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime"; import { type ChannelSetupDmPolicy, type ChannelSetupWizardAdapter, @@ -34,6 +32,7 @@ import { } from "./matrix/client/url-validation.js"; import { resolveMatrixConfigFieldPath, updateMatrixAccountConfig } from "./matrix/config-update.js"; import { ensureMatrixSdkInstalled, isMatrixSdkAvailable } from "./matrix/deps.js"; +import type { RuntimeEnv, WizardPrompter } from "./runtime-api.js"; import { moveSingleMatrixAccountConfigToNamedAccount } from "./setup-config.js"; import { resolveMatrixSetupDmAllowFrom } from "./setup-dm-policy.js"; import type { CoreConfig, MatrixConfig } from "./types.js"; diff --git a/extensions/matrix/src/runtime-api.ts b/extensions/matrix/src/runtime-api.ts index 0db659215fb..dd92cbc4f48 100644 --- a/extensions/matrix/src/runtime-api.ts +++ b/extensions/matrix/src/runtime-api.ts @@ -42,7 +42,7 @@ export type { GroupPolicy, } from "openclaw/plugin-sdk/config-runtime"; export type { GroupToolPolicyConfig } from "openclaw/plugin-sdk/config-runtime"; -export type { WizardPrompter } from "openclaw/plugin-sdk/matrix-runtime-shared"; +export type { WizardPrompter } from "openclaw/plugin-sdk/setup"; export type { SecretInput } from "openclaw/plugin-sdk/secret-input"; export { GROUP_POLICY_BLOCKED_LABEL, @@ -107,7 +107,7 @@ export { formatZonedTimestamp, type PluginRuntime, type RuntimeLogger, -} from "openclaw/plugin-sdk/matrix-runtime-shared"; +} from "openclaw/plugin-sdk/core"; export type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime"; // resolveMatrixAccountStringValues already comes from plugin-sdk/matrix. // Re-exporting auth-precedence here makes Jiti try to define the same export twice. diff --git a/extensions/memory-core/api.ts b/extensions/memory-core/api.ts index afcd41faff9..a7e4fe6c9bc 100644 --- a/extensions/memory-core/api.ts +++ b/extensions/memory-core/api.ts @@ -1,4 +1,4 @@ -export type { OpenClawConfig } from "openclaw/plugin-sdk/memory-core"; +export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; export type { MemoryEmbeddingProbeResult, MemoryProviderStatus, diff --git a/extensions/memory-core/src/dreaming-command.ts b/extensions/memory-core/src/dreaming-command.ts index 165382bceec..dac286805b1 100644 --- a/extensions/memory-core/src/dreaming-command.ts +++ b/extensions/memory-core/src/dreaming-command.ts @@ -1,5 +1,6 @@ -import type { OpenClawConfig, OpenClawPluginApi } from "openclaw/plugin-sdk/memory-core"; +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; import { resolveMemoryDreamingConfig } from "openclaw/plugin-sdk/memory-core-host-status"; +import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; import { asRecord } from "./dreaming-shared.js"; import { resolveShortTermPromotionDreamingConfig } from "./dreaming.js"; diff --git a/extensions/memory-core/src/dreaming-phases.ts b/extensions/memory-core/src/dreaming-phases.ts index 8ed7ced68c8..84cb6bee262 100644 --- a/extensions/memory-core/src/dreaming-phases.ts +++ b/extensions/memory-core/src/dreaming-phases.ts @@ -2,7 +2,6 @@ import { createHash } from "node:crypto"; import type { Dirent } from "node:fs"; import fs from "node:fs/promises"; import path from "node:path"; -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/memory-core"; import { buildSessionEntry, listSessionFilesForAgent, @@ -18,6 +17,7 @@ import { resolveMemoryLightDreamingConfig, resolveMemoryRemDreamingConfig, } from "openclaw/plugin-sdk/memory-core-host-status"; +import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; import { writeDailyDreamingPhaseBlock } from "./dreaming-markdown.js"; import { generateAndAppendDreamNarrative, type NarrativePhaseData } from "./dreaming-narrative.js"; import { asRecord, formatErrorMessage, normalizeTrimmedString } from "./dreaming-shared.js"; diff --git a/extensions/memory-core/src/dreaming.ts b/extensions/memory-core/src/dreaming.ts index 38dfe952ee0..68838bd194e 100644 --- a/extensions/memory-core/src/dreaming.ts +++ b/extensions/memory-core/src/dreaming.ts @@ -1,5 +1,5 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; import { peekSystemEventEntries } from "openclaw/plugin-sdk/infra-runtime"; -import type { OpenClawConfig, OpenClawPluginApi } from "openclaw/plugin-sdk/memory-core"; import { DEFAULT_MEMORY_DREAMING_FREQUENCY as DEFAULT_MEMORY_DREAMING_CRON_EXPR, DEFAULT_MEMORY_DEEP_DREAMING_LIMIT as DEFAULT_MEMORY_DREAMING_LIMIT, @@ -20,6 +20,7 @@ import { resolveMemoryDeepDreamingConfig, resolveMemoryDreamingWorkspaces, } from "openclaw/plugin-sdk/memory-core-host-status"; +import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; import { writeDeepDreamingReport } from "./dreaming-markdown.js"; import { generateAndAppendDreamNarrative, type NarrativePhaseData } from "./dreaming-narrative.js"; diff --git a/extensions/memory-lancedb/api.ts b/extensions/memory-lancedb/api.ts index c1bd12dd4b7..e0935591d64 100644 --- a/extensions/memory-lancedb/api.ts +++ b/extensions/memory-lancedb/api.ts @@ -1 +1,2 @@ -export * from "openclaw/plugin-sdk/memory-lancedb"; +export { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; +export { resolveStateDir } from "openclaw/plugin-sdk/state-paths"; diff --git a/extensions/thread-ownership/api.ts b/extensions/thread-ownership/api.ts index d94a5fd68e1..6be7975e16a 100644 --- a/extensions/thread-ownership/api.ts +++ b/extensions/thread-ownership/api.ts @@ -1 +1,7 @@ -export * from "openclaw/plugin-sdk/thread-ownership"; +export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; +export { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; +export { + fetchWithSsrFGuard, + ssrfPolicyFromAllowPrivateNetwork, + ssrfPolicyFromDangerouslyAllowPrivateNetwork, +} from "openclaw/plugin-sdk/ssrf-runtime"; diff --git a/src/plugin-sdk/config-runtime.ts b/src/plugin-sdk/config-runtime.ts index c31a274e22a..225bdf8d8a3 100644 --- a/src/plugin-sdk/config-runtime.ts +++ b/src/plugin-sdk/config-runtime.ts @@ -128,6 +128,8 @@ export type { DiscordSlashCommandConfig, DmConfig, DmPolicy, + GoogleChatAccountConfig, + GoogleChatConfig, ContextVisibilityMode, GroupPolicy, GroupToolPolicyBySenderConfig, diff --git a/src/plugin-sdk/diagnostic-runtime.ts b/src/plugin-sdk/diagnostic-runtime.ts index 338bbe8f672..0a5587bbfc7 100644 --- a/src/plugin-sdk/diagnostic-runtime.ts +++ b/src/plugin-sdk/diagnostic-runtime.ts @@ -1,4 +1,22 @@ // Diagnostic flag/event helpers for plugins that want narrow runtime gating. export { isDiagnosticFlagEnabled } from "../infra/diagnostic-flags.js"; -export { isDiagnosticsEnabled } from "../infra/diagnostic-events.js"; +export type { + DiagnosticEventMetadata, + DiagnosticEventPayload, +} from "../infra/diagnostic-events.js"; +export { + emitDiagnosticEvent, + isDiagnosticsEnabled, + onDiagnosticEvent, +} from "../infra/diagnostic-events.js"; +export type { DiagnosticTraceContext } from "../infra/diagnostic-trace-context.js"; +export { + createChildDiagnosticTraceContext, + createDiagnosticTraceContext, + formatDiagnosticTraceparent, + isValidDiagnosticSpanId, + isValidDiagnosticTraceFlags, + isValidDiagnosticTraceId, + parseDiagnosticTraceparent, +} from "../infra/diagnostic-trace-context.js"; diff --git a/src/plugin-sdk/plugin-entry.ts b/src/plugin-sdk/plugin-entry.ts index 53b1a6d6943..49930b0fd24 100644 --- a/src/plugin-sdk/plugin-entry.ts +++ b/src/plugin-sdk/plugin-entry.ts @@ -16,6 +16,7 @@ import type { OpenClawPluginCommandDefinition, OpenClawPluginConfigSchema, OpenClawPluginDefinition, + OpenClawPluginHttpRouteHandler, OpenClawPluginNodeHostCommand, OpenClawPluginReloadRegistration, OpenClawPluginSecurityAuditCollector, @@ -104,6 +105,7 @@ export type { PluginCommandContext, PluginCommandResult, OpenClawPluginConfigSchema, + OpenClawPluginHttpRouteHandler, ProviderDiscoveryContext, ProviderCatalogContext, ProviderCatalogResult, diff --git a/src/plugin-sdk/security-runtime.ts b/src/plugin-sdk/security-runtime.ts index 46f68a97095..7d847e5f401 100644 --- a/src/plugin-sdk/security-runtime.ts +++ b/src/plugin-sdk/security-runtime.ts @@ -9,4 +9,5 @@ export * from "../security/context-visibility.js"; export * from "../security/dm-policy-shared.js"; export * from "../security/external-content.js"; export * from "../security/safe-regex.js"; +export { redactSensitiveText } from "../logging/redact.js"; export { safeEqualSecret } from "../security/secret-equal.js"; 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 55a57379222..f891281fbd0 100644 --- a/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts +++ b/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts @@ -67,12 +67,13 @@ const RUNTIME_API_EXPORT_GUARDS: Record = { 'export { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";', 'export { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";', 'export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";', + 'export { GoogleChatConfigSchema } from "openclaw/plugin-sdk/channel-config-schema-legacy";', 'export { GROUP_POLICY_BLOCKED_LABEL, isDangerousNameMatchingEnabled, resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/config-runtime";', 'export { fetchRemoteMedia, resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";', 'export { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";', 'export type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store";', 'export { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";', - 'export { GoogleChatConfigSchema, type GoogleChatAccountConfig, type GoogleChatConfig } from "openclaw/plugin-sdk/googlechat-runtime-shared";', + 'export type { GoogleChatAccountConfig, GoogleChatConfig } from "openclaw/plugin-sdk/config-runtime";', 'export { extractToolSend } from "openclaw/plugin-sdk/tool-send";', 'export { resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-inbound";', 'export { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";', @@ -125,8 +126,12 @@ const RUNTIME_API_EXPORT_GUARDS: Record = { 'export { setMatrixThreadBindingIdleTimeoutBySessionKey, setMatrixThreadBindingMaxAgeBySessionKey } from "./src/matrix/thread-bindings-shared.js";', 'export { setMatrixRuntime } from "./src/runtime.js";', 'export { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";', - 'export type { ChannelDirectoryEntry, ChannelMessageActionContext, OpenClawConfig, PluginRuntime, RuntimeLogger, RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/matrix-runtime-shared";', - 'export { formatZonedTimestamp } from "openclaw/plugin-sdk/matrix-runtime-shared";', + 'export type { ChannelDirectoryEntry, ChannelMessageActionContext } from "openclaw/plugin-sdk/channel-contract";', + 'export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";', + 'export { formatZonedTimestamp } from "openclaw/plugin-sdk/core";', + 'export type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk/plugin-runtime";', + 'export type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";', + 'export type { WizardPrompter } from "openclaw/plugin-sdk/setup";', 'export function chunkTextForOutbound(text: string, limit: number): string[] { const chunks: string[] = []; let remaining = text; while (remaining.length > limit) { const window = remaining.slice(0, limit); const splitAt = Math.max(window.lastIndexOf("\\n"), window.lastIndexOf(" ")); const breakAt = splitAt > 0 ? splitAt : limit; chunks.push(remaining.slice(0, breakAt).trimEnd()); remaining = remaining.slice(breakAt).trimStart(); } if (remaining.length > 0 || text.length === 0) { chunks.push(remaining); } return chunks; }', ], [bundledPluginFile({ diff --git a/src/plugins/runtime/index.test.ts b/src/plugins/runtime/index.test.ts index 23a75dd1065..40e335ae3ec 100644 --- a/src/plugins/runtime/index.test.ts +++ b/src/plugins/runtime/index.test.ts @@ -218,6 +218,8 @@ describe("plugin runtime command execution", () => { expectFunctionKeys(runtime.agent as Record, [ "runEmbeddedAgent", "runEmbeddedPiAgent", + "normalizeThinkingLevel", + "resolveThinkingPolicy", "resolveAgentDir", ]); expectFunctionKeys(runtime.agent.session as Record, [ diff --git a/src/plugins/runtime/runtime-agent.ts b/src/plugins/runtime/runtime-agent.ts index 4b233201195..78a6d165d1a 100644 --- a/src/plugins/runtime/runtime-agent.ts +++ b/src/plugins/runtime/runtime-agent.ts @@ -4,6 +4,7 @@ import { resolveAgentIdentity } from "../../agents/identity.js"; import { resolveThinkingDefault } from "../../agents/model-selection.js"; import { resolveAgentTimeoutMs } from "../../agents/timeout.js"; import { ensureAgentWorkspace } from "../../agents/workspace.js"; +import { normalizeThinkLevel, resolveThinkingProfile } from "../../auto-reply/thinking.js"; import { resolveSessionFilePath, resolveStorePath } from "../../config/sessions/paths.js"; import { loadSessionStore, saveSessionStore } from "../../config/sessions/store.js"; import { createLazyRuntimeMethod, createLazyRuntimeModule } from "../../shared/lazy-runtime.js"; @@ -24,6 +25,17 @@ export function createRuntimeAgent(): PluginRuntime["agent"] { resolveAgentWorkspaceDir, resolveAgentIdentity, resolveThinkingDefault, + normalizeThinkingLevel: normalizeThinkLevel, + resolveThinkingPolicy: (params) => { + const profile = resolveThinkingProfile(params); + const policy: Omit< + ReturnType, + "defaultLevel" + > = { + levels: profile.levels.map(({ id, label }) => ({ id, label })), + }; + return profile.defaultLevel ? { ...policy, defaultLevel: profile.defaultLevel } : policy; + }, resolveAgentTimeoutMs, ensureAgentWorkspace, } satisfies Omit & diff --git a/src/plugins/runtime/types-core.ts b/src/plugins/runtime/types-core.ts index 5e087414ead..96d428f2106 100644 --- a/src/plugins/runtime/types-core.ts +++ b/src/plugins/runtime/types-core.ts @@ -47,6 +47,19 @@ type RuntimeReplaceConfigFileParams = { afterWrite: RuntimeConfigAfterWrite; writeOptions?: RuntimeWriteConfigOptions; }; +export type PluginRuntimeThinkingPolicyRequest = { + provider?: string | null; + model?: string | null; + catalog?: import("../../auto-reply/thinking.js").ThinkingCatalogEntry[]; +}; +export type PluginRuntimeThinkingPolicyLevel = { + id: import("../../auto-reply/thinking.js").ThinkLevel; + label: string; +}; +export type PluginRuntimeThinkingPolicy = { + levels: PluginRuntimeThinkingPolicyLevel[]; + defaultLevel?: import("../../auto-reply/thinking.js").ThinkLevel | null; +}; /** Structured logger surface injected into runtime-backed plugin helpers. */ export type RuntimeLogger = { @@ -116,6 +129,12 @@ export type PluginRuntimeCore = { model: string; catalog?: import("../../agents/model-catalog.types.js").ModelCatalogEntry[]; }) => import("../../auto-reply/thinking.js").ThinkLevel; + normalizeThinkingLevel: ( + raw?: string | null, + ) => import("../../auto-reply/thinking.js").ThinkLevel | undefined; + resolveThinkingPolicy: ( + params: PluginRuntimeThinkingPolicyRequest, + ) => PluginRuntimeThinkingPolicy; runEmbeddedAgent: import("../../agents/pi-embedded-runtime.types.js").RunEmbeddedAgentFn; runEmbeddedPiAgent: import("../../agents/pi-embedded-runtime.types.js").RunEmbeddedPiAgentFn; resolveAgentTimeoutMs: typeof import("../../agents/timeout.js").resolveAgentTimeoutMs; diff --git a/test/helpers/plugins/plugin-runtime-mock.ts b/test/helpers/plugins/plugin-runtime-mock.ts index 1846490c22c..a276a29724a 100644 --- a/test/helpers/plugins/plugin-runtime-mock.ts +++ b/test/helpers/plugins/plugin-runtime-mock.ts @@ -123,6 +123,18 @@ export function createPluginRuntimeMock(overrides: DeepPartial = resolveThinkingDefault: vi.fn( () => "off", ) as unknown as PluginRuntime["agent"]["resolveThinkingDefault"], + normalizeThinkingLevel: vi.fn( + (raw?: string | null) => raw, + ) as unknown as PluginRuntime["agent"]["normalizeThinkingLevel"], + resolveThinkingPolicy: vi.fn(() => ({ + levels: [ + { id: "off", label: "off" }, + { id: "minimal", label: "minimal" }, + { id: "low", label: "low" }, + { id: "medium", label: "medium" }, + { id: "high", label: "high" }, + ], + })) as unknown as PluginRuntime["agent"]["resolveThinkingPolicy"], runEmbeddedPiAgent: vi.fn().mockResolvedValue({ payloads: [], meta: {},