1. Virtual Worker Send Commands
  2. OpenCode Commands

Virtual Worker Send Commands

OpenCode Commands

Runs an OpenCode coding agent on a Virtual Worker. Covers worker setup, provider credentials, prompts, sessions, and asynchronous feedback for results, questions, and permissions.

Use these commands inside the params.commands object of a Virtual Worker Send Commands task. Each command needs a unique __order.

OpenCode runs on the worker and can work with files and repositories there. A prompt starts work in the background. Results and requests for input arrive on the originating stub through the _update_from_virtual_worker feedback action.

Worker setup

Use the github worker flavour, which pre-installs the OpenCode executable alongside Git and GitHub tools. Set params.runtime.cluster.worker.tag to github; this selects the latest-github image. The worker starts its shared OpenCode server automatically when an OpenCode command first runs.

For example, store this runtime object in template.data.settings.opencode.runtime to use with the tasks below:

        {
  "cluster": {
    "worker": {
      "name": "{{stub.stubref}}",
      "tag": "github"
    }
  }
}

      

Use the same worker for credential setup, prompts, and replies. Set params.directory on OpenCode commands to the project directory on that worker. The directory must already exist; use cli_run to create it or clone a repository first. If omitted, OpenCode uses the worker process's current working directory. Keep the directory consistent when continuing a session or replying to its requests.

Configure a provider key

Store the provider API key in a Generic credential. The following example uses cli_run to write an Anthropic key into the container's ~/.local/share/opencode/auth.json before sending any prompts:

loading...

Set template.data.settings.claude_api_key_credentialuuid to the credential UUID. The target helper resolves the key in the Virtual Worker Hub. This command replaces auth.json with the shown provider entry. Run it again when using a new worker container.

Send a prompt

opencode_session_prompt sends message parts to the coding agent. You can omit sessionID to create a session automatically, or supply an existing session ID to continue its conversation.

This example starts a new session in a project directory that has already been prepared on the worker:

loading...

Prompt parameters

Parameter Required Description
parts Yes Array of message parts. For a text prompt, use { "type": "text", "text": "Your instruction" }. OpenCode also accepts file, agent, and subtask parts.
sessionID No Existing session to continue. If omitted, the worker creates a session before sending the prompt.
directory No Project directory on the worker. Defaults to the worker process's current directory.
workspace No OpenCode workspace scope, if used.
model No Model selection: { "providerID": "anthropic", "modelID": "<model ID>" }. Use opencode_config_providers to discover provider and model IDs.
agent No Agent name. Use opencode_app_agents to discover available agents.
system No Additional system instruction string.
variant No Model variant supported by the selected model.
messageID No Explicit ID for the submitted message.
noReply No When true, adds the message without generating an agent reply.
format No OpenCode output format configuration.
tools No Map of tool names to booleans controlling tool availability for the prompt.

Immediate acknowledgement and later result

The command prepares the OpenCode client and event subscription and creates a session if sessionID was omitted. It then starts the prompt in the background and returns without waiting for the agent to finish:

        {
  "prompt": {
    "success": true,
    "message": "OpenCode prompt accepted",
    "payload": {
      "session_id": "ses_example"
    }
  }
}

      

Every successful acknowledgement includes payload.session_id, whether you supplied an existing sessionID or the worker created a new session. Save this ID to continue the conversation. The same ID is included in later feedback updates at stubpost.data.opencode.session_id. You can also use opencode_session_create to create a session separately before sending a prompt.

An accepted prompt does not mean the agent has completed its work or that the model request succeeded. Subsequent commands in the task can run while it is still working. Continue your workflow from the feedback updates for the prompt result. Errors returned by the background model request are included in those updates. If session creation fails, the command returns a technical error immediately and does not send the prompt.

To continue a conversation, pass the saved ID as params.sessionID and send new parts on the same worker and directory. Wait for the current prompt to finish, and resolve any outstanding questions or permission requests first.

Pending input blocks a new prompt

For an existing sessionID, the command checks for pending permissions and questions before submitting another prompt. If either exists, it sends no new prompt and returns:

        {
  "prompt": {
    "success": false,
    "message": "OpenCode prompt was not sent: this session has pending permissions or questions. Reply to or reject them before sending another prompt.",
    "error": {
      "type": "conceptual",
      "details": {
        "code": "opencode_input_required",
        "session_id": "ses_example",
        "pending_permissions": [],
        "pending_questions": [
          {
            "id": "que_example",
            "sessionID": "ses_example",
            "questions": [
              {
                "header": "Tests",
                "question": "Which tests should I run?",
                "options": [{ "label": "Unit tests", "description": "Run the unit suite." }]
              }
            ]
          }
        ]
      }
    }
  }
}

      

Use the request IDs in these arrays with the reply commands, or handle the original question.asked and permission.asked feedback events as they arrive.

Feedback updates

Define or override _update_from_virtual_worker in your template to handle the agent's updates. The worker authenticates updates with its worker token, and the hub routes the action to the originating stub.

Feedback field Meaning
stubpost.message Readable response text or event summary. It may be absent if the response has no extracted text.
stubpost.data.opencode.session_id Session associated with the update. Save this to continue the conversation.
stubpost.data.opencode.event Native OpenCode event, including type and properties. Present for event updates.
stubpost.data.opencode.response Full prompt response with message info and parts. Present when the prompt request returns successfully.
stubpost.data.opencode.error OpenCode error, when the prompt request returns an error.

Route event updates using stubpost.data.opencode.event.type. Route prompt results using the presence of opencode.response or opencode.error. A tool result is an intermediate update; it does not indicate that the whole prompt is finished.

Prompt result

The following is an abbreviated example of the feedback stubpost after a successful prompt:

        {
  "message": "Run npm test to execute the unit tests.",
  "data": {
    "opencode": {
      "session_id": "ses_example",
      "response": {
        "info": {
          "id": "msg_example",
          "sessionID": "ses_example",
          "role": "assistant",
          "finish": "stop"
        },
        "parts": [
          {
            "type": "text",
            "text": "Run npm test to execute the unit tests."
          }
        ]
      }
    }
  }
}

      

For a normal response with info.finish equal to stop, the worker joins text parts with newlines to form stubpost.message. It also extracts tool errors for a tool-calls finish and formats message-level errors when present. Use the full response and error fields for workflow decisions, since a readable message is not always available.

Tool results and input events

The worker forwards these native events:

Event type What to handle
message.part.updated Tool parts whose status is completed or error. Read the tool name at event.properties.part.tool, input at part.state.input, output at part.state.output, or failure at part.state.error.
question.asked A request for answers. Read the request ID at event.properties.id and the questions at event.properties.questions.
permission.asked A request to allow an operation. Read the request ID at event.properties.id, permission at event.properties.permission, and affected patterns at event.properties.patterns.
question.replied Confirmation that a question received answers.
question.rejected Confirmation that a question was rejected.
permission.replied Confirmation that a permission request received a decision.

Text/token streaming and tool parts with pending or running status are filtered out. Session status events are not forwarded; use opencode_session_status if you need to inspect whether a session is busy, idle, or retrying.

For example, a completed tool call produces a feedback stubpost shaped like this (abbreviated):

        {
  "message": "tool: bash | Run unit tests",
  "data": {
    "opencode": {
      "session_id": "ses_example",
      "event": {
        "type": "message.part.updated",
        "properties": {
          "sessionID": "ses_example",
          "part": {
            "type": "tool",
            "tool": "bash",
            "state": {
              "status": "completed",
              "input": { "command": "npm test" },
              "output": "All tests passed",
              "title": "Run unit tests"
            }
          }
        }
      }
    }
  }
}

      

Reply to questions and permissions

Send replies in another virtual_worker_send_commands task using the same runtime and directory. Replying lets the waiting agent continue its current prompt; you do not need to send that prompt again.

Answer a question

Use opencode_question_reply with the request ID from question.asked. answers is an array in question order, with an array of selected option labels for each question. For a question that allows a custom answer, put the answer text in that question's array.

The following command object can be placed inside params.commands of the feedback action's task:

loading...

Run this command only when handling a question.asked event, and replace the sample answer with the user's or workflow's decision. To decline the request, use opencode_question_reject with the same requestID, omitting answers.

Decide a permission request

Use opencode_permission_reply with the request ID from permission.asked. reply accepts once, always, or reject. once allows this request; always remembers approval for the request's permission patterns in the session; reject denies it. An optional message supplies decision context.

loading...

Run this command only when handling a permission.asked event, after deciding whether to allow the requested operation.

Session and discovery commands

These commands return the OpenCode result in the command's payload. They share the optional directory and workspace parameters. Required IDs and all other parameters go directly inside params.

Create a session

opencode_session_create creates a session without sending a prompt. Save payload.id and pass it as sessionID on later prompts.

loading...

Optional parameters include title, parentID, agent, model, metadata, permission, and workspaceID. For this command, model uses { "providerID": "<provider ID>", "id": "<model ID>" }; the prompt command uses modelID instead of id. permission is an OpenCode permission ruleset array.

Inspect, stop, or remove sessions

Command type Parameters beyond directory / workspace Payload
opencode_session_list Optional scope (project), path, roots, start, search, limit. Array of sessions, each including id, title, and directory details.
opencode_session_get Required sessionID. Session details.
opencode_session_messages Required sessionID; optional limit and before (message ID). Array of messages with info and parts.
opencode_session_status No additional parameters. Map of session IDs to status objects with type equal to idle, busy, or retry. Retry entries also include retry details.
opencode_session_abort Required sessionID. Boolean result for stopping an active prompt. The session remains available.
opencode_session_delete Required sessionID. Boolean result for deleting the session.

For example, retrieve the conversation after receiving an update:

loading...

Discover providers and agents

Command type Payload
opencode_config_providers Object containing providers and a default model mapping. Inspect provider IDs and their model IDs before specifying params.model on a prompt.
opencode_app_agents Array of available agent definitions. Use an agent's name in params.agent.
loading...