anomalyco / anomalyco/opencode
[FEATURE]: `/api/experimental/generate` accepts only `{prompt, model}` while `LLM.request` already accepts `system`, `messages` and `generation`
@jlongster is already working on this.
Since Sep 17, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
The stateless generate endpoint is the only way a plugin can run a model call without
creating a session. It is useful exactly as designed, but it can only send a bare prompt on
default generation settings, and the layer directly beneath it already accepts more.
packages/protocol/src/groups/generate.ts:8-16 defines the payload as:
payload: Schema.Struct({
prompt: Schema.String,
model: Model.Ref.pipe(Schema.optional),
}),
packages/core/src/generate.ts forwards exactly those two:
const response = yield* llm.generate(LLM.request({ model: resolved.model, prompt: input.prompt }))
But LLM.request at packages/ai/src/llm.ts:42-62 destructures system, prompt, messages,
tools, toolChoice, generation, providerOptions and http. And prompt is itself sugar
for a single user message (llm.ts:59):
messages: [...(messages?.map(Message.make) ?? []), ...(prompt === undefined ? [] : [Message.user(prompt)])],
The session path already exercises this. packages/core/src/session/model-request.ts:229-231
splits a prepared options object into generation (keys in GenerationOptions.fields) and
providerOptions, and attaches both at :252-253. So the capability is present and used; the
stateless entry simply does not offer it.
Request: widen the generate.text payload to accept messages? and generation?, forwarding
them to the LLM.request call that already accepts them. messages? rather than system?
because a real [system, user] pair is the general shape and prompt is the sugar over it.
Why it matters: a plugin that needs a system prompt today has to concatenate it into the user
turn. For anything calibrated against a fixed system/user split that is a behaviour change, not a
formatting preference. A downstream consumer building on this — the Magic Context plugin — runs a
hidden summarisation pass calibrated on a [system, user] shape at temperature 0.1 with a 32k
output cap; none of those three survive the current payload. Their wire capture against a
two-model mock provider confirms the request goes out with no instructions, no temperature and
no max_output_tokens.
I am happy to open a PR for this if the shape is agreeable.
Separately, and only if you want it answered here: is the /api/experimental/ prefix a
"may change" or a "will be removed" signal? session.import/session.export and
experimental.terminal live under it and look load-bearing, so I would rather ask than assume.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.