Plugin SDK: add host-owned structured runtime LLM#80203
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 2, 2026, 1:07 AM ET / 05:07 UTC. Summary PR surface: Source +421, Tests +537, Docs +43, Generated 0. Total +1001 across 20 files. Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Review detailsBest possible solution: Retry the Codex review after fixing the execution failure. Do we have a high-confidence way to reproduce the issue? Unclear. The review failed before ClawSweeper could establish a reproduction path. Is this the best way to solve the issue? Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction. AGENTS.md: unclear because the file could not be read completely. Codex review notes: model gpt-5.5, reasoning high; reviewed against 93fd17447abd. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +421, Tests +537, Docs +43, Generated 0. Total +1001 across 20 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Follow-up hardening on the latest head (
Focused validation rerun on this head:
|
|
This pull request has been automatically marked as stale due to inactivity. |
Why this should exist
OpenClaw already has two useful but incomplete lanes for plugin-side model work:
api.runtime.llm.complete(...)for trusted host-owned text completionapi.runtime.mediaUnderstanding.extractStructuredWithModel(...)in #79334 for provider-owned media-first structured extractionWhat is still missing is the general middle lane for plugins that need bounded host-owned structured inference without:
That gap shows up across a lot more than one plugin family:
The goal of this PR is to make that generic host-owned lane first-class.
What this adds
This PR adds:
api.runtime.llm.completeStructured(...)jsonMode,jsonSchema,schemaName,timeoutMs, andprofileapi.runtime.llm.complete(...)Why this belongs under
runtime.llmThis API is deliberately the generic agent-bound runtime lane.
It reuses the same host-owned completion path as
complete(...), then adds the structured affordances plugin authors actually need:That makes it the right home for general structured plugin inference.
Boundary vs
#79334This PR is a sister, not a replacement.
runtime.llm.completeStructured(...)is the generic agent-bound structured inference lanemediaUnderstanding.extractStructuredWithModel(...)in#79334remains the narrower provider-owned media capability laneThat separation keeps both seams honest:
runtime.llm.completeStructured(...)when the plugin wants host-owned structured inference against the active agent/runtime pathmediaUnderstanding.extractStructuredWithModel(...)when the plugin wants explicit provider/model media-routing behaviorSafety and trust model
This stays host-owned end to end.
Plugins do not receive raw OAuth tokens, refresh tokens, or provider secrets.
The host still owns:
This PR also closes a subtle trust gap by treating auth-profile selection as a real override.
profilenow requires explicit opt-in via:plugins.entries.<id>.llm.allowProfileOverride: trueThe full runtime trust picture is now:
allowModelOverride+ optionalallowedModelsallowAgentIdOverrideallowProfileOverrideEmbedded model-ref suffixes now flow through that same gate, and conflicting
profilevsmodel@profileinputs fail closed.Behavior details
A few implementation details here are deliberate and worth calling out:
jsonMode: trueorjsonSchemais providedcompleteStructured(...)alongsidecomplete(...)Example
What changed
LlmCompleteStructured*runtime typesruntime.llm.completeStructured(...)to the plugin runtime facadeplugins.entries.<id>.llm.allowProfileOverridecompleteStructured(...)to context-engine runtime hooksmodel@profilebypasses and reject conflicting explicit vs embedded auth-profile selectionValidation
pnpm test -- src/plugins/runtime/runtime-llm.runtime.test.ts src/plugins/runtime/index.test.ts src/plugins/config-state.test.ts src/config/schema.help.quality.test.tspnpm plugin-sdk:api:genpnpm plugin-sdk:api:checkpnpm check:changedReal behavior proof
Behavior or issue addressed:
profile: "..."or as a trailing model-ref suffix likeopenai/gpt-5.5@work.profilefield, so embeddedmodel@profilerequests could steer credential selection withoutallowProfileOverride.Real environment tested:
codex/plugin-inference-followupusing the real runtime implementation throughnode --import tsxon macOS, with no test mocks in the proof command.Exact steps or command run after this patch:
node --import tsx -e 'import { createRuntimeLlm } from "./src/plugins/runtime/runtime-llm.runtime.ts"; const cfg = { agents: { defaults: { model: "openai/gpt-5.5" } } }; const llm = createRuntimeLlm({ getConfig: () => cfg, authority: { caller: { kind: "host", id: "proof" }, allowComplete: true, allowModelOverride: true, agentId: "ada" } }); for (const [label, params] of [["embedded-profile-blocked", { model: "openai/gpt-5.5@openai-codex:work", instructions: "Extract summary.", input: [{ type: "text", text: "Hello" }] }],["conflicting-profile-rejected", { model: "openai/gpt-5.5@openai-codex:work", profile: "openai-codex:other", instructions: "Extract summary.", input: [{ type: "text", text: "Hello" }] }]]) { try { await llm.completeStructured(params); console.log(label + ": UNEXPECTED_SUCCESS"); } catch (error) { console.log(label + ": " + (error instanceof Error ? error.message : String(error))); } }'Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
Terminal capture:
Supplemental focused regression coverage:
Observed result after fix:
model@profileunlessallowProfileOverrideis explicitly trusted.What was not tested:
Before evidence (optional but encouraged):
profilewas gated but the embeddedmodel@profileform was not normalized before auth-profile policy evaluation.Non-goals
#79334Closes #80188.