Context
V2 has session.generate (SessionGenerate): one physical LLM attempt from the current Session model context, with a transient prompt, that must not mutate the Session.
Primary callers today:
- Session warming (
opencode.warming plugin)
- HTTP
POST /api/session/:sessionID/generate
- Plugin
ctx.session.generate
Original design notes live in plans/session-generate.md (status: in progress). Implementation has drifted from parts of that plan and needs an explicit product/architecture contract.
Current behavior (as implemented)
| Aspect |
Today |
| Input |
{ sessionID, prompt } |
| Output |
string (assistant text only) |
| History |
Read-only preview of committed context + in-memory prompt |
| Tools |
Tool definitions are included (for prompt-cache parity with agent turns) |
toolChoice |
Not forced to none (aligned with normal agent request shape after recent fix) |
| Tool calls |
If the model emits tool calls: ignored — not executed, not settled, not written to the Session |
| Durability |
No admission, events, usage, instruction commit, or compaction |
| Prep path |
Own request construction in generate-node.ts, not fully shared with SessionModelRequest.prepare |
Open questions to solidify
1. What is generate for?
Rank and lock intended use cases:
- Cache warming / keep-alive (must preserve provider prompt-cache identity with agent turns)
- Ephemeral recap / title / UI blurbs
- Plugin one-shots over live Session context
- Something closer to “run one step without durability”?
2. Tools policy
Choose one explicit contract:
- A. Definitions only, ignore calls (current) — best cache match; empty text possible if model only tool-calls
- B. No tools on the wire — simpler semantics; breaks Anthropic/Bedrock tool-prefix cache for warming
- C.
toolChoice: none with tools present — forbid calls where API supports it; Bedrock still cannot
- D. Execute tools once, still non-durable — AI SDK–like; blurs the no-settlement boundary
3. Request preparation parity
Plan required sharing prep with the durable runner then diverging before settlement. Today generate rebuilds the request separately.
- Must generate be byte-equivalent to a durable step’s prepared request (minus the transient user message)?
- Should thinking/effort/variant/
providerOptions always match the runner?
- Should generate go through
SessionModelRequest with a generate-specific shape?
4. Return value / API surface
Is text-only enough, or do callers need:
- usage / cache read-write diagnostics
- finish reason
- toolCalls (even if not executed)
- model identity
- optional revision/seq for stale-result detection
5. Failure modes
Lock behavior for:
- context overflow (no auto-compaction today — correct?)
- unsettled assistant / open tool loop in history
- concurrent durable drain while generate is in flight (snapshot semantics)
- instruction initialization blocked
6. Warming-specific requirements
If warming remains a first-class consumer:
- Same tools + system + history prefix as agent turns
- Cache breakpoint placement (not on the keep-alive placeholder user message?)
- TTL / interval interaction with Anthropic 5m vs 1h ephemeral cache
- Whether warm requests should be tagged for hooks (today prompt-text matching)
Non-goals (unless we explicitly promote them)
- Mutating Session history or usage
- Multi-step tool loops
- Auto-compaction from generate
- Becoming a second full runner
Proposed outcome
A short written contract (docs or design note) that answers:
- Guaranteed invariants
- Allowed request shape (tools / choice / prep sharing)
- Return type
- Supported callers and what they may rely on
Then align implementation + tests to that contract (and update plans/session-generate.md).
Related
- Warming + Anthropic cache investigation (tools stripped on
toolChoice: none, auto breakpoints on warm prompt)
- PRs: Anthropic/Gemini
toolChoice: none lowering; stop forcing none on generate
Ask
Please grill / decide the tools policy and return-value shape first; those drive prep parity and warming correctness.
Context
V2 has
session.generate(SessionGenerate): one physical LLM attempt from the current Session model context, with a transient prompt, that must not mutate the Session.Primary callers today:
opencode.warmingplugin)POST /api/session/:sessionID/generatectx.session.generateOriginal design notes live in
plans/session-generate.md(status: in progress). Implementation has drifted from parts of that plan and needs an explicit product/architecture contract.Current behavior (as implemented)
{ sessionID, prompt }string(assistant text only)toolChoicenone(aligned with normal agent request shape after recent fix)generate-node.ts, not fully shared withSessionModelRequest.prepareOpen questions to solidify
1. What is generate for?
Rank and lock intended use cases:
2. Tools policy
Choose one explicit contract:
toolChoice: nonewith tools present — forbid calls where API supports it; Bedrock still cannot3. Request preparation parity
Plan required sharing prep with the durable runner then diverging before settlement. Today generate rebuilds the request separately.
providerOptionsalways match the runner?SessionModelRequestwith a generate-specific shape?4. Return value / API surface
Is text-only enough, or do callers need:
5. Failure modes
Lock behavior for:
6. Warming-specific requirements
If warming remains a first-class consumer:
Non-goals (unless we explicitly promote them)
Proposed outcome
A short written contract (docs or design note) that answers:
Then align implementation + tests to that contract (and update
plans/session-generate.md).Related
toolChoice: none, auto breakpoints on warm prompt)toolChoice: nonelowering; stop forcingnoneon generateAsk
Please grill / decide the tools policy and return-value shape first; those drive prep parity and warming correctness.