Skip to content

feat: Allow setting custom session ID when creating a session #12916

@sjawhar

Description

@sjawhar

Problem

When building automation on top of OpenCode (e.g. agent orchestrators that spawn opencode serve instances), it's useful to pre-compute deterministic session IDs so the orchestrator knows the session ID before the session exists. This enables:

  • Sending prompt_async to a known session ID immediately after spawning a server
  • Resuming workers by computing the expected session ID without querying state
  • Idempotent session creation (same inputs → same session ID)

Currently, POST /session always auto-generates the session ID internally. There's no way to specify one from the client side.

Current Behavior

The public Session.create() function (packages/opencode/src/session/index.ts) wraps Session.createNext() but doesn't pass through the id parameter:

// Session.create() - public API (line ~140)
export const create = fn(
  z.object({
    parentID: Identifier.schema("session").optional(),
    title: z.string().optional(),
    permission: Info.shape.permission,
  }).optional(),
  async (input) => {
    return createNext({
      parentID: input?.parentID,
      directory: Instance.directory,
      title: input?.title,
      permission: input?.permission,
      // ← no id parameter
    })
  },
)

However, the internal createNext() function already supports it:

// Session.createNext() - internal (line ~206)
export async function createNext(input: {
  id?: string              // ← accepts optional id
  title?: string
  parentID?: string
  directory: string
  permission?: PermissionNext.Ruleset
}) {
  const result: Info = {
    id: Identifier.descending("session", input.id),  // ← uses it if provided
    // ...
  }
}

And Identifier.descending() in packages/opencode/src/id/id.ts already handles provided IDs — validates the prefix and returns it directly:

function generateID(prefix, descending, given?) {
  if (!given) return create(prefix, descending)
  if (!given.startsWith(prefixes[prefix]))
    throw new Error(`ID \${given} does not start with \${prefixes[prefix]}`)
  return given
}

Desired Behavior

Accept an optional id field in POST /session and thread it through to createNext():

// Updated Session.create() schema
z.object({
  id: Identifier.schema("session").optional(),  // ← new
  parentID: Identifier.schema("session").optional(),
  title: z.string().optional(),
  permission: Info.shape.permission,
}).optional()

When id is provided:

  • Use the given ID instead of auto-generating one
  • Validate it has the correct ses_ prefix (already handled by Identifier.descending())
  • Define behavior for duplicate IDs (return existing session, or 409 conflict)

When id is omitted:

  • Behavior unchanged — auto-generate as today

Implementation Notes

The internal plumbing already exists. The changes needed are minimal:

  1. packages/opencode/src/session/index.ts — Add id to Session.create()'s Zod schema and pass it to createNext()
  2. packages/opencode/src/server/routes/session.ts — The HTTP route uses Session.create.schema so it should pick up the new field automatically
  3. SDK regeneration — If the SDK is auto-generated from the OpenAPI spec, regenerate to include the new id field

Optionally:
4. CLI --session flag — Currently only resumes existing sessions. Could be extended to create-if-not-exists with the given ID.

Use Case

We spawn opencode serve instances as autonomous workers and need to send prompts to known session IDs. We pre-compute deterministic session IDs (UUIDv5 from worker parameters) but have no way to tell OpenCode to use them.

Current (broken):

  1. Compute sessionId = UUIDv5(namespace, workerParams)
  2. Spawn opencode serve --port X
  3. POST /session/{sessionId}/prompt_async404 (session doesn't exist)

With this feature:

  1. Compute sessionId (same)
  2. Spawn opencode serve --port X
  3. POST /session with {"id": sessionId} → creates session with our ID
  4. POST /session/{sessionId}/prompt_async → works

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