Let session creation (MCP / loops) set model, agent/variant, and permission settings #270

Closed
opened 2026-07-09 10:28:33 +02:00 by dries · 0 comments
Owner

Problem

When creating a session programmatically — via the MCP `new_session` tool, or from an agent loop's `spawn_child` / `spawn_worktree` action — the caller can only set the model (`provider/model` string). It cannot set:

  • Agent (composer role: `build` / `plan` / subagent)
  • Variant / reasoning (model thinking-budget: `high` / `max` / `low`)
  • Permission settings (per-session permission ruleset)

These should all be settable at creation time so an orchestrating agent can, e.g., spin up a `plan` child on a high-reasoning model with a locked-down permission set.

Key finding

OpenCode's `POST /session` API accepts only `{ parentID?, title? }` — model/agent/variant are not create-time fields; they ride on the first prompt (`/session/:id/prompt`), and permission is applied via a separate `PATCH /session/:id` `{ permission: [...] }`.

The deep layers already support all of this. `platforms.SendMessageRequest` already has `Model`, `Agent`, `Reasoning` (`internal/platforms/operations.go:16-31`) and the opencode adapter already maps them (`internal/platforms/opencode/operations.go:253-274`). `platforms.SetPermissionRulesRequest` + `Adapter.SetPermissionRules` already exist (`operations.go:111-116`, adapter `operations.go:470-492`). Do NOT expand `CreateSessionRequest`.

The gap is purely in the caller-facing structs and tool schemas, plus one new post-create `SetPermissionRules` call in `SessionLauncher.Launch`.

Scope

Thread Agent + Reasoning through to the first message

  • `internal/mcp/launcher.go`
    • `LaunchRequest` (`:26-52`): add `Agent`, `Reasoning` fields.
    • `Launch` `SendMessage` call (`:141-144`): pass `Agent`, `Reasoning`.
  • `internal/mcp/tools_split.go`: add `agent` + `reasoning` params to `new_session`; read them in `handleNewSession` / `launchWorktree`.
  • `internal/loops/types.go`
    • `SpawnRequest` (`:232-241`): add `Agent`, `Reasoning`.
    • `LoopSpec` (`:116-134`) + `LoopUpdate` (`:139-146`): add `Agent`, `Reasoning`.
    • `Messenger.SendPrompt` (`:218-220`): extend signature to carry agent/reasoning.
  • `internal/loops/actions.go`: pass agent/reasoning into `SpawnRequest` and `SendPrompt`.
  • `internal/server/loop_engine.go`: map `req.Agent`/`req.Reasoning` into `internalmcp.LaunchRequest`; forward them in the `SendPrompt` adapter into `SendMessageRequest`.
  • `internal/state/loops.go` `Loop` struct (`:15-48`) + a new migration: persist `Agent`/`Reasoning` alongside the existing `Model` column.
  • `create_loop` MCP tool + `POST /api/loops` schema: add `agent` + `reasoning` params.

Add per-session permission at creation

  • `internal/mcp/launcher.go`
    • `SessionClient` interface (`:64-67`): add `SetPermissionRules(ctx, SetPermissionRulesRequest) error`.
    • `LaunchRequest`: add `PermissionRules []platforms.PermissionRule` (or a simpler permission-mode enum translated to rules).
    • `Launch`: after `CreateSession` succeeds, if rules provided, call `SetPermissionRules`.
  • `internal/sessionsvc`: extend the `sessionsvc.Client` passthrough (the `Service` already exposes `SetPermissionRules`, `service.go:154`).
  • MCP `new_session` + loops schemas: add the permission param.

No changes needed

`platforms.CreateSessionRequest`, `SendMessageRequest`, `Adapter.SendMessage`, `Adapter.SetPermissionRules`, or the OpenCode API layer.

Notes

  • Terminology (per `internal/platforms/platform.go:1-18`): agent = composer role; variant/reasoning = model thinking-budget. Decide on the public param names (suggest `agent` and `reasoning`) before implementing.
  • Consider whether the permission input should be a full rule list or a simpler preset/mode; a mode enum is easier for an LLM caller to use.
  • Related: #101 (worktree sessions inheriting parent permissions) — that's inherit-by-default, this is explicit-set; they can share the permission plumbing.
  • Tests: new caller-facing fields need coverage (coverage ratchet is enforced), plus a migration test for the new `Loop` columns.
## Problem When creating a session programmatically — via the MCP \`new_session\` tool, or from an agent loop's \`spawn_child\` / \`spawn_worktree\` action — the caller can only set the **model** (\`provider/model\` string). It cannot set: - **Agent** (composer role: \`build\` / \`plan\` / subagent) - **Variant / reasoning** (model thinking-budget: \`high\` / \`max\` / \`low\`) - **Permission settings** (per-session permission ruleset) These should all be settable at creation time so an orchestrating agent can, e.g., spin up a \`plan\` child on a high-reasoning model with a locked-down permission set. ## Key finding OpenCode's \`POST /session\` API accepts only \`{ parentID?, title? }\` — model/agent/variant are **not** create-time fields; they ride on the **first prompt** (\`/session/:id/prompt\`), and permission is applied via a separate \`PATCH /session/:id\` \`{ permission: [...] }\`. **The deep layers already support all of this.** \`platforms.SendMessageRequest\` already has \`Model\`, \`Agent\`, \`Reasoning\` (\`internal/platforms/operations.go:16-31\`) and the opencode adapter already maps them (\`internal/platforms/opencode/operations.go:253-274\`). \`platforms.SetPermissionRulesRequest\` + \`Adapter.SetPermissionRules\` already exist (\`operations.go:111-116\`, adapter \`operations.go:470-492\`). **Do NOT expand \`CreateSessionRequest\`.** The gap is purely in the caller-facing structs and tool schemas, plus one new post-create \`SetPermissionRules\` call in \`SessionLauncher.Launch\`. ## Scope ### Thread Agent + Reasoning through to the first message - \`internal/mcp/launcher.go\` - \`LaunchRequest\` (\`:26-52\`): add \`Agent\`, \`Reasoning\` fields. - \`Launch\` \`SendMessage\` call (\`:141-144\`): pass \`Agent\`, \`Reasoning\`. - \`internal/mcp/tools_split.go\`: add \`agent\` + \`reasoning\` params to \`new_session\`; read them in \`handleNewSession\` / \`launchWorktree\`. - \`internal/loops/types.go\` - \`SpawnRequest\` (\`:232-241\`): add \`Agent\`, \`Reasoning\`. - \`LoopSpec\` (\`:116-134\`) + \`LoopUpdate\` (\`:139-146\`): add \`Agent\`, \`Reasoning\`. - \`Messenger.SendPrompt\` (\`:218-220\`): extend signature to carry agent/reasoning. - \`internal/loops/actions.go\`: pass agent/reasoning into \`SpawnRequest\` and \`SendPrompt\`. - \`internal/server/loop_engine.go\`: map \`req.Agent\`/\`req.Reasoning\` into \`internalmcp.LaunchRequest\`; forward them in the \`SendPrompt\` adapter into \`SendMessageRequest\`. - \`internal/state/loops.go\` \`Loop\` struct (\`:15-48\`) + a new migration: persist \`Agent\`/\`Reasoning\` alongside the existing \`Model\` column. - \`create_loop\` MCP tool + \`POST /api/loops\` schema: add \`agent\` + \`reasoning\` params. ### Add per-session permission at creation - \`internal/mcp/launcher.go\` - \`SessionClient\` interface (\`:64-67\`): add \`SetPermissionRules(ctx, SetPermissionRulesRequest) error\`. - \`LaunchRequest\`: add \`PermissionRules []platforms.PermissionRule\` (or a simpler permission-mode enum translated to rules). - \`Launch\`: after \`CreateSession\` succeeds, if rules provided, call \`SetPermissionRules\`. - \`internal/sessionsvc\`: extend the \`sessionsvc.Client\` passthrough (the \`Service\` already exposes \`SetPermissionRules\`, \`service.go:154\`). - MCP \`new_session\` + loops schemas: add the permission param. ### No changes needed \`platforms.CreateSessionRequest\`, \`SendMessageRequest\`, \`Adapter.SendMessage\`, \`Adapter.SetPermissionRules\`, or the OpenCode API layer. ## Notes - Terminology (per \`internal/platforms/platform.go:1-18\`): *agent* = composer role; *variant/reasoning* = model thinking-budget. Decide on the public param names (suggest \`agent\` and \`reasoning\`) before implementing. - Consider whether the permission input should be a full rule list or a simpler preset/mode; a mode enum is easier for an LLM caller to use. - Related: #101 (worktree sessions inheriting parent permissions) — that's inherit-by-default, this is explicit-set; they can share the permission plumbing. - Tests: new caller-facing fields need coverage (coverage ratchet is enforced), plus a migration test for the new \`Loop\` columns.
dries closed this issue 2026-07-09 20:35:56 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
dries/ocman#270
No description provided.