Skip to content

Regression introduced by merged PR #23068 -- bug fix #30734

@FlorianOtel

Description

@FlorianOtel

Description

What ddc30cd15 was supposed to fix

  • Commit: ddc30cd15 ? feat(core): add session metadata support (#23068)
  • Date: 2026-05-30 21:58 UTC
  • PR purpose: Add explicit agent and model fields (alongside a new metadata field) to the Session.create() / createNext() input shape. Prior to this refactor, those values lived implicitly inside the session creation flow; the PR's goal was to make them first-class, explicitly-passed inputs so that callers control them directly rather than relying on context-derived defaults.
  • Affected surfaces:
    • packages/opencode/src/session/session.ts ? create() (~lines 740-785) and createNext() (~lines 580ff) now accept agent?: string and model?: Schema.Schema.Type<typeof Model> in their input shape.
    • packages/core/src/session/projector.ts ? sessionRow() (~lines 38-70) extracts these fields directly from the Info object when writing the DB row.

Regression it introduced

  • Caller never updated. packages/opencode/src/tool/task.ts lines ~147-162 ? the only call site that creates a child session for a dispatched subagent ? still calls sessions.create() with only parentID, title, and permission. It never passes agent or model, even though the refactor made these explicit input fields.
  • The model value is already computed at the same call site but never used at create. Lines ~170-198 of the same file derive const model = next.model ?? { modelID: msg.info.modelID, providerID: msg.info.providerID } and use it for the prompt invocation and metadata publish ? but never thread it back to the sessions.create() call eighteen lines above.
  • Historic projector-based back-fill is no longer dependable. Before ddc30cd15, the DB columns were populated by projector handlers reacting to SessionEvent.AgentSwitched and SessionEvent.ModelSwitched events that session/prompt.ts emits on first prompt (the current?.agent !== info.agent comparison fires the event because current is undefined for a fresh session). After the refactor, this path no longer reliably populates the columns on Task-tool-spawned children ? likely because the emitted event payloads or comparison surfaces shifted, but the practical observable effect is unambiguous: child session rows have agent IS NULL, model IS NULL.

Net effect

  • Every child session created via the Task tool from ddc30cd15 onwards has agent IS NULL and model IS NULL in the SQLite session table.
  • Downstream consumers (any tool that reads ~/.local/share/opencode/opencode.db for subagent attribution ? telemetry, status-line, ad-hoc SQL, future tooling) cannot determine which subagent ran or which model handled a child session.
  • The breakage is in upstream OpenCode dev HEAD AND in release v1.15.13 and later (since ddc30cd15 was tagged into v1.15.13 on 2026-05-30 23:40 UTC, ~1h42m after the bug landed).
  • v1.15.12 and earlier are unaffected.

Plugins

None

OpenCode version

The reproduction only requires an OpenCode daemon built from dev HEAD (or any release v1.15.13+).

Steps to reproduce

Prerequisites

  1. A built opencode binary from dev HEAD (or any release v1.15.13+). Verify version contains 0.0.0-{branch}-{timestamp} (local build) or 1.15.13+ (release).
  2. An OpenCode server running on a known port (default 4096).
  3. At least one agent definition installed at ~/.config/opencode/agents/<some-agent>.md with at minimum:
    ---
    name: minimal-test-agent
    description: Synthetic agent for reproducing the agent/model regression.
    model: anthropic/claude-haiku-4-5
    tools:
      Read: true
    ---
    You are a synthetic test agent. Respond with the literal string "ok" and nothing else.
  4. sqlite3 CLI available.

Steps

# 1. Pick a synthetic working directory (not a real project ? avoids cross-test pollution)
TMPDIR=$(mktemp -d /tmp/oc-regression-XXXXXX)
cd "$TMPDIR"

# 2. Create a top-level (parent) session via SDK or REST against the running daemon.
#    Capture the returned session ID as PARENT_SID.
PARENT_SID=$(curl -sS -X POST \
  -H "x-opencode-directory: $TMPDIR" \
  -H "Content-Type: application/json" \
  -d '{"title":"regression-repro-parent"}' \
  "http://localhost:4096/session" | jq -r '.id')
echo "PARENT_SID=$PARENT_SID"

# 3. Send a user message that triggers a Task tool call dispatching the test agent.
#    The exact request shape depends on the SDK version; the goal is one assistant
#    turn that calls Task with subagent_type: "minimal-test-agent".
#    Pseudocode:
#
#    POST /session/{PARENT_SID}/message
#    {
#      "messageID": "msg_<descending_ulid>",
#      "model": { "modelID": "claude-haiku-4-5", "providerID": "anthropic" },
#      "agent": "general",
#      "parts": [{ "type": "text", "text": "Dispatch the minimal-test-agent via the Task tool with prompt: ok" }]
#    }
#
#    The assistant turn that follows will invoke Task, which creates a child session.

# 4. Wait for session-idle (the child session is now created and the agent has responded).

# 5. Query the SQLite session table for the child session row.
sqlite3 ~/.local/share/opencode/opencode.db <<SQL
SELECT id, agent, model, parent_id
FROM session
WHERE parent_id = '$PARENT_SID';
SQL

Observed (broken) output

ses_<child_id>|||ses_<parent_id>

(agent and model columns empty / NULL.)

Expected (post-fix) output

ses_<child_id>|minimal-test-agent|{"id":"claude-haiku-4-5","providerID":"anthropic","variant":"default"}|ses_<parent_id>

(agent populated with the dispatched subagent's name; model populated with the resolved model JSON.)

Pure-unit reproduction (no daemon, no DB)

A faster reproduction that doesn't need a running daemon is to call Session.create() directly with no agent/model (the same way task.ts does today) and assert via the projector that the row written to the DB has NULL agent and model. See §4 step 2 for the test shape ? this is the failing test that will be written first.


Screenshot and/or share link

None needed

Operating System

Linux Debian testing

Terminal

xterm

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions