Official

opencode-v2

OpenCode 2 agent provider: runs opencode serve (2.x) and talks to its /api over HTTP and server-sent events.

The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/opencode-v2@0.1.0

Permissions in 0.1.0

  • Provide agents agents.provideMediumAdds agents to the app.Provide the OpenCode 2 agent, and serve plugin tools to it through the host's loopback MCP server
  • Run named programs processMediumStarts the listed programs.Run the OpenCode 2 server (`opencode serve`, or the binary you choose in Settings) and read its versionPrograms: opencode${settings.binaryPath}
  • Network access netMediumConnects to the listed hosts.Talk to the OpenCode server it starts on this computer, at the port it picks for each launch, and to the external server you choose in SettingsHosts: localhost:*${settings.serverUrl}
  • Environment variables envMediumReads the listed environment variables.Read the user name and password of the OpenCode serverVariables: OPENCODE_SERVER_USERNAMEOPENCODE_SERVER_PASSWORDCONVERGENCE_OPENCODE_SERVER_PASSWORD

Files

permission.ts2.7 KB
// The session permission ruleset Convergence pushes for a chat. OpenCode
// decides a request against the agent's rules followed by the session's,
// and the last matching rule wins, so the session's rules settle what the
// composer's permission mode promises.
import type { PermissionRequest } from "./api.ts";
import { permissionMode } from "../sdk/agent.ts";

export interface Rule {
  action: string;
  resource: string;
  effect: "allow" | "ask" | "deny";
}

const rule = (action: string, resource: string, effect: Rule["effect"]): Rule => ({ action, resource, effect });

/// The ruleset for a shared permission mode. Order matters: a narrow rule
/// only wins when it comes after the broad one it refines.
///
/// Auto is read as Supervised: OpenCode has no approval reviewer, so the
/// honest reading of "approve routine actions" is to keep asking.
export function ruleset(mode: string): Rule[] {
  // Reads outside the workspace are an action of their own that the
  // wildcard does not cover.
  if (mode === permissionMode.FULL) return [rule("*", "*", "allow"), rule("external_directory", "*", "allow")];
  const edits = mode === permissionMode.AUTO_EDITS ? "allow" : "ask";
  return [
    rule("*", "*", "ask"),
    rule("read", "*", "allow"),
    // OpenCode asks before reading environment files whatever the agent
    // allows; a blanket read rule must not quietly remove that.
    rule("read", "*.env", "ask"),
    rule("read", "*.env.*", "ask"),
    rule("read", "*.env.example", "allow"),
    rule("glob", "*", "allow"),
    rule("grep", "*", "allow"),
    rule("list", "*", "allow"),
    rule("lsp", "*", "allow"),
    rule("skill", "*", "allow"),
    rule("todowrite", "*", "allow"),
    // Starting a subagent is not an action on the machine; its own tools
    // ask under the same rules.
    rule("subagent", "*", "allow"),
    // The question tool only asks the user something; asking to ask is noise.
    rule("question", "*", "allow"),
    rule("edit", "*", edits),
    rule("external_directory", "*", "ask"),
  ];
}

/// Whether the mode answers permission requests without the user. The
/// ruleset alone is not enough: a subagent session can carry rules of its
/// own.
export const repliesAutomatically = (mode: string) => mode === permissionMode.FULL;

/// The title of an approval card: the action and what it touches.
export function approvalTitle(request: PermissionRequest): string {
  if (request.message) return request.message;
  const action = request.action === "shell" ? "Run" : request.action[0]?.toUpperCase() + request.action.slice(1);
  const resources = request.resources.filter(Boolean);
  return resources.length ? `${action} ${resources.join(", ")}` : action;
}

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.1.0latestOct 5, 2026>=2 <345.1 KB4 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.