Skip to content

Commit 5df0820

Browse files
authored
refactor(runtime): add prepared runtime foundation (#78248)
* docs(runtime): document prepared runtime guidance * refactor(provider-runtime): thread prepared provider handles * refactor(runtime-plan): add prepared runtime foundation * refactor(outbound): add prepared channel runtime facts * refactor(models): add scoped model reference helpers * refactor(plugin-sdk): expose prepared runtime helper surfaces
1 parent 70eabd3 commit 5df0820

22 files changed

Lines changed: 824 additions & 66 deletions

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
3737
- New seams: backwards-compatible, documented, versioned. Third-party plugins exist.
3838
- Channels: `src/channels/**` is implementation; plugin authors get SDK seams.
3939
- Providers: core owns generic loop; provider plugins own auth/catalog/runtime hooks.
40+
- Request-time runtime resolution: when a path already knows the provider id, model ref, channel id, outbound target, capability family, or attachment class, carry that as a prepared runtime fact instead of rediscovering it later.
41+
- Prepared runtime facts should be small typed values produced once near startup, reply dispatch, model selection, tool planning, or channel resolution, then passed through context to consumers. Prefer `AgentRuntimePlan`, `ProviderRuntimePluginHandle`, scoped model/catalog helpers, active/runtime registries, manifest/public-artifact lookups, single-provider resolvers, and lazy registry construction.
42+
- Avoid broad request-time rediscovery: hot reply/tool/outbound/media paths should not call broad plugin/provider/channel/capability loaders such as `loadOpenClawPlugins`, `resolveProviderPluginsForHooks`, `resolvePluginCapabilityProviders`, `resolvePluginDiscoveryProvidersRuntime`, `getChannelPlugin`, or broad model/tool/media registry builders just to answer a question the caller already knows. Do not build multimodal/provider registries for document-only or otherwise non-participating paths.
43+
- Compatibility fallbacks are allowed only for startup/setup/admin/standalone/legacy callers that genuinely lack prepared facts. Keep them explicit, tested, and outside migrated hot reply/tool/outbound paths.
44+
- Do not fix repeated request-time discovery by adding scattered cache layers. Move the canonical fact earlier, reuse the existing prepared-runtime object, and delete duplicate lookup branches when the last migrated caller stops needing them.
4045
- Gateway protocol changes: additive first; incompatible needs versioning/docs/client follow-through.
4146
- Config contract: exported types, schema/help, metadata, baselines, docs aligned. Retired public keys stay retired; compat in raw migration/doctor.
4247
- Direction: manifest-first control plane; targeted runtime loaders; no hidden contract bypasses; broad mutable registries transitional.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Docs: https://docs.openclaw.ai
6161
- Control UI/chat: add an agent-first filter to the chat session picker, keep chat controls/composer responsive across phone/tablet/desktop widths, keep desktop chat controls on one row, avoid duplicate avatar refreshes during initial chat load, and hide that row while scrolling down the transcript. Thanks @BunsDev.
6262
- Control UI/chat: collapse consecutive duplicate text messages into one bubble with a count so repeated text-only messages stay compact without hiding nearby context.
6363
- Control UI/chat and Sessions: label inherited thinking defaults separately from explicit overrides while preserving provider-supplied option labels. Fixes #77581. Thanks @BunsDev and @Beandon13.
64+
- Agents/runtime: add prepared runtime foundation contracts for carrying provider, model, tool, TTS, and outbound runtime facts through later reply-path migrations. Thanks @mcaxtr.
6465
- Agents/subagents: preserve every grouped child result when direct completion fallback has to bypass the requester-agent announce turn. Thanks @vincentkoc.
6566
- TTS/telephony: honor provider voice/model overrides in telephony synthesis providers so Google Meet agent speech logs match the backend that actually produced the audio. Thanks @vincentkoc.
6667
- Voice Call/realtime: bound the paced Twilio audio queue and close overloaded realtime streams before provider audio can pile up behind the websocket backpressure guard. Thanks @vincentkoc.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
259c4d22d5fe84ca6b27f9d061dddca4459e4eddd516729564f250d823dbd819 plugin-sdk-api-baseline.json
2-
b2bf16d3c5859b06240b5f41ed5803af8e82338da3e56ace1569520ac0732a5a plugin-sdk-api-baseline.jsonl
1+
28e280d21693216c99cfa8da553589b41741d37c0ada956e316ee01d3d6c202c plugin-sdk-api-baseline.json
2+
633dae33da97f6a073c5561709c57d5c0b7ff67af0512d0261f05455c24b38de plugin-sdk-api-baseline.jsonl

scripts/lib/plugin-sdk-doc-metadata.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ export const pluginSdkDocMetadata = {
101101
"runtime-store": {
102102
category: "runtime",
103103
},
104+
"agent-runtime": {
105+
category: "runtime",
106+
},
107+
"speech-core": {
108+
category: "provider",
109+
},
110+
"tts-runtime": {
111+
category: "runtime",
112+
},
104113
"allow-from": {
105114
category: "utilities",
106115
},

src/agents/model-catalog-scope.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { OpenClawConfig } from "../config/types.openclaw.js";
2+
import { findNormalizedProviderValue, normalizeProviderId } from "./provider-id.js";
3+
4+
function dedupeCatalogScopeRefs(values: Array<string | undefined>): string[] {
5+
const refs = new Set<string>();
6+
for (const value of values) {
7+
const trimmed = value?.trim();
8+
if (trimmed) {
9+
refs.add(trimmed);
10+
}
11+
}
12+
return [...refs];
13+
}
14+
15+
function providerFromModelRef(value: string | undefined): string | undefined {
16+
const trimmed = value?.trim();
17+
if (!trimmed) {
18+
return undefined;
19+
}
20+
const slash = trimmed.indexOf("/");
21+
if (slash <= 0) {
22+
return undefined;
23+
}
24+
const provider = normalizeProviderId(trimmed.slice(0, slash));
25+
return provider || undefined;
26+
}
27+
28+
export function resolveModelCatalogScope(params: {
29+
cfg?: OpenClawConfig;
30+
provider: string;
31+
model: string;
32+
}): { providerRefs: string[]; modelRefs: string[] } {
33+
const provider = params.provider.trim();
34+
const model = params.model.trim();
35+
const providerConfig = findNormalizedProviderValue(params.cfg?.models?.providers, provider);
36+
return {
37+
providerRefs: dedupeCatalogScopeRefs([provider, providerConfig?.api]),
38+
modelRefs: dedupeCatalogScopeRefs([provider && model ? `${provider}/${model}` : model, model]),
39+
};
40+
}
41+
42+
export function resolveProviderDiscoveryProviderIdsForCatalogScope(params: {
43+
providerRefs?: readonly string[];
44+
modelRefs?: readonly string[];
45+
}): string[] | undefined {
46+
const providerIds = dedupeCatalogScopeRefs([
47+
...(params.providerRefs ?? []),
48+
...(params.modelRefs ?? []).map(providerFromModelRef),
49+
]);
50+
return providerIds.length > 0 ? providerIds : undefined;
51+
}

src/agents/pi-embedded-runner/extra-params.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { ThinkLevel } from "../../auto-reply/thinking.js";
66
import type { OpenClawConfig } from "../../config/types.openclaw.js";
77
import {
88
prepareProviderExtraParams as prepareProviderExtraParamsRuntime,
9+
type ProviderRuntimePluginHandle,
910
resolveProviderExtraParamsForTransport as resolveProviderExtraParamsForTransportRuntime,
1011
wrapProviderStreamFn as wrapProviderStreamFnRuntime,
1112
} from "../../plugins/provider-hook-runtime.js";
@@ -207,6 +208,7 @@ export function resolvePreparedExtraParams(params: {
207208
resolvedExtraParams?: Record<string, unknown>;
208209
model?: ProviderRuntimeModel;
209210
resolvedTransport?: SupportedTransport;
211+
providerRuntimeHandle?: ProviderRuntimePluginHandle;
210212
}): Record<string, unknown> {
211213
const resolvedExtraParams =
212214
params.resolvedExtraParams ??
@@ -253,6 +255,7 @@ export function resolvePreparedExtraParams(params: {
253255
provider: params.provider,
254256
config: params.cfg,
255257
workspaceDir: params.workspaceDir,
258+
runtimeHandle: params.providerRuntimeHandle,
256259
context: {
257260
config: params.cfg,
258261
agentDir: params.agentDir,
@@ -267,6 +270,7 @@ export function resolvePreparedExtraParams(params: {
267270
provider: params.provider,
268271
config: params.cfg,
269272
workspaceDir: params.workspaceDir,
273+
runtimeHandle: params.providerRuntimeHandle,
270274
context: {
271275
config: params.cfg,
272276
agentDir: params.agentDir,

src/agents/pi-embedded-runner/run.overflow-compaction.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ function makeForwardedRuntimePlan(overrides: RuntimePlanOverrides = {}): AgentRu
105105
provider: "anthropic",
106106
modelId: "test-model",
107107
resolveSystemPromptContribution: vi.fn(),
108+
transformSystemPrompt: vi.fn((context) => context.systemPrompt),
108109
},
109110
transcript: {
110111
policy: transcriptPolicy,

src/agents/pi-embedded-runner/tool-schema-runtime.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { AgentTool } from "@mariozechner/pi-agent-core";
22
import type { TSchema } from "typebox";
33
import type { OpenClawConfig } from "../../config/types.openclaw.js";
4+
import type { ProviderRuntimePluginHandle } from "../../plugins/provider-hook-runtime.js";
45
import type { ProviderRuntimeModel } from "../../plugins/provider-runtime-model.types.js";
56
import {
67
inspectProviderToolSchemasWithPlugin,
@@ -19,6 +20,7 @@ type ProviderToolSchemaParams<TSchemaType extends TSchema = TSchema, TResult = u
1920
modelId?: string;
2021
modelApi?: string | null;
2122
model?: ProviderRuntimeModel;
23+
runtimeHandle?: ProviderRuntimePluginHandle;
2224
};
2325

2426
function buildProviderToolSchemaContext<TSchemaType extends TSchema = TSchema, TResult = unknown>(
@@ -51,6 +53,7 @@ export function normalizeProviderToolSchemas<
5153
config: params.config,
5254
workspaceDir: params.workspaceDir,
5355
env: params.env,
56+
runtimeHandle: params.runtimeHandle,
5457
context: buildProviderToolSchemaContext(params, provider),
5558
});
5659
return Array.isArray(pluginNormalized)
@@ -68,6 +71,7 @@ export function logProviderToolSchemaDiagnostics(params: ProviderToolSchemaParam
6871
config: params.config,
6972
workspaceDir: params.workspaceDir,
7073
env: params.env,
74+
runtimeHandle: params.runtimeHandle,
7175
context: buildProviderToolSchemaContext(params, provider),
7276
});
7377
if (!Array.isArray(diagnostics)) {

0 commit comments

Comments
 (0)