Skip to content

Latest commit

 

History

History
308 lines (214 loc) · 17.4 KB

File metadata and controls

308 lines (214 loc) · 17.4 KB

hiai-opencode Architecture

This file explains how the plugin is assembled and where to change each layer. It reflects the actual on-disk code layout — every path below is real.

High-Level Structure

The plugin has five main layers:

  1. config and defaults
  2. agent registration and prompt assembly
  3. skill tooling
  4. MCP and LSP wiring
  5. execution gates and hooks

Request Flow

User input
    │
    ▼
┌─────────┐
│   Bob    │ ← orchestrator / router (visible, primary)
└────┬────┘
     │ routes by complexity
     ▼
┌──────────────────────────────────────────────────────┐
│                                                      │
│  Simple / small (<5 todos, no parallelism)            │
│    └─► build   (hidden, deep/bounded implementation)  │
│    └─► general (visible, quick/fallback executor)     │
│                                                      │
│  Planning / architecture                              │
│    └─► plan (visible, deep planning, read-only)       │
│                                                      │
│  Specialist tiers (always delegated, not routed):     │
│    explore  ◄── discovery (grep, firecrawl, context7) │
│    writer   ◄── copy / SEO / messaging               │
│    designer ◄── visual direction                      │
│    critic   ◄── review gate (APPROVED/REJECTED)      │
│    manager  ◄── delegation orchestrator, memory       │
│    vision   ◄── browser operator, multimodal          │
│                                                      │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─────────┐
│   Bob    │ ← collects results, verifies, reports
└────┬────┘
     │
     ▼
  User response

Key wiring rules:

  • OpenCode plugins are NOT MCP servers. hiai-opencode only provides the OpenCode-side launch wiring for MCP servers through its mcp config and helper launchers.
  • Model credentials go through OpenCode Connect, not bob.json.
  • Service keys (e.g. FIRECRAWL_API_KEY) are configured in bob.env (env file) or as shell variables. Use {env:VAR_NAME} placeholders in bob.json — never put raw API keys there.

Repository Layout

  • src/config.ts: config loading, defaults, env-file loading, validation
  • src/index.ts: plugin entry — agent registration via hooks.config, tool registration, hook wiring
  • src/agents/: agent factories, prompts (bob.ts, build.ts, plan.ts, manager.ts, critic.ts, designer.ts, writer.ts, vision.ts, explore.ts, general.ts, index.ts)
  • src/prompt-library/: shared prompt fragments (browser, caveman, native-memory, postgres-rules, workspace, worktree)
  • src/shared/: closure protocol, env-var resolution, event helpers
  • src/permissions.ts: per-agent permission/tool-disable resolution
  • src/hooks/: all runtime hooks (circuit breaker, quality gate, closure injector, legal gate, worktree lifecycle, etc.)
  • src/features/: background-manager, completion-controller, dream-distill, mcp, shell-env, telemetry, workspace-adapter, worktree
  • src/tools/: tool registrations (skill, lsp, session-manager, background-task, worktree, agent-browser, firecrawl, memory-tool)
  • assets/cli/hiai-opencode.mjs: the hiai-opencode CLI (doctor, mcp-status, export-mcp, diagnose)
  • assets/runtime/: npm bootstrap helper for MCP/LSP tools
  • skills/: packaged project skills
  • config/: packaged sample OpenCode config and schema

Agent Model

Visible agents (picker)

  • bob (primary) — Orchestrator, router, entry point
  • plan (mode: all) — Principal Architect, deep planning
  • general (mode: all) — Cheap bounded executor, fallback

Hidden subagents

  • build — Senior Staff Engineer, implementation (deep/bounded)
  • explore — discovery (firecrawl + grep_app + context7)
  • critic — review gate (binary APPROVED/REJECTED)
  • designer — UI/visual direction
  • writer — copy/positioning/SEO
  • vision — browser operator, multimodal
  • manager — delegation orchestrator, memory steward
  • dream-consolidator — memory consolidation (auto-triggered)
  • distill-packager — workflow packaging (auto-triggered)

Counts: createAllAgents() returns 8 agent definitions; 4 more (explore, plan, build, general) are native-upgraded inline in src/index.ts. Total registered: 12. The 10 user-facing model slots are bob, build, plan, manager, critic, designer, explore, writer, vision, general (validated by REQUIRED_AGENT_KEYS in src/config.ts).

Legacy name mapping

There is no separate migration module. Legacy agent keys are mapped only in two places:

  • DEPRECATED_MODEL_KEYS in assets/cli/hiai-opencode.mjs (doctor diagnostics): coderbuild, strategistplan, researcherexplore, subgeneral, guardmanager, brainstormerwriter.
  • The prompts reference agents by canonical runtime key (explore, build, plan, general).

Canonical Source Files

  • src/agents/index.tscreateAllAgents() registers 8 agents with visibility, mode, description, and prompts; resolveAgentModel / applyPromptOverride helpers
  • src/index.tshooks.config callback native-upgrades explore/plan/build/general and merges all agents into OpenCode's cfg.agent dict; assembles MCP config
  • src/types.tsAgentConfig, BobConfig, CompletionConfig, WorktreeConfig, ClosureBlock types

Models

Source of truth

User-facing model IDs live in one place:

  • bob.json — the single config file. The plugin ships bundled defaults; a bob.json in the project root or .opencode/ overrides them.

The runtime loader is:

  • src/config.tsloadConfig() reads bob.json from plugin root, project dir, .opencode/, and global config dir (first found wins); mergeConfig() applies defaults; REQUIRED_AGENT_KEYS validates the 10 slots.

Users configure the 10 primary agent model slots under models: bob, build, plan, manager, critic, designer, explore, writer, vision, general. Use fully qualified provider/model-id strings. Do not invent prefixes — run opencode models and copy exact IDs.

Prompting

Prompting is layered. src/agents/ is the main authoring layer, but runtime prompts are assembled from several sources.

Layer 1: Agent prompt source files

Each agent's prompt is a template literal assembled from imported fragments:

Layer 2: Shared prompt fragments

Reusable policy/behavior blocks imported by the agent prompts:

Layer 3: Runtime prompt injection

Prompt content appended/transformed after the source prompt is built:

Layer 4: Agent registration assembly

Final agent registration into OpenCode's cfg.agent dict happens in the hooks.config callback in src/index.ts. This is where model resolution, visibility, mode, temperature, thinking budgets, and per-agent permissions are applied.

Prompting Change Rules

  • change bob.json when a model slot should change
  • change src/config.ts (DEFAULT_CONFIG) when defaults (permissions, mcp, lsp, completion, etc.) should change
  • change src/agents/*.ts when prompt content or behavior should change
  • change src/prompt-library/*.ts when a shared prompt fragment should change
  • change src/index.ts hooks.config when agent registration, visibility, or model resolution should change

Skills

Skills are loaded by a single file:

  • src/tools/skill.tscreateSkillTool() walks the packaged skills/ directory, indexes every SKILL.md by both namespaced path and leaf name, and serves content via the skill tool.

Packaged skills live in skills/, organized by agent group (build/, designer/, plan/, explore/, bob/, critic/, general/, vision/, writer/).

Note: The skill_discovery / skills.sources / skills.disable config documented in older versions is not implemented in the current codebase. Skills are discovered from the packaged skills/ directory only. The skills dir is hardcoded relative to dist/.

CLI skills (not MCP, invoked via skill("...") or shell):

  • firecrawl — web scraping (requires FIRECRAWL_API_KEY)
  • context7 — on-demand library docs via skill("explore/context7")
  • agent-browser — browser automation via Chrome CDP (uses /agent-browser skill)

MCP

Registry (source of truth)

Current MCP set (v0.3.0+)

  • sequential-thinking — local, npx-backed reasoning server
  • grep_app — remote code-search endpoint (no key required)

Removed in v0.3.0: context7 (now on-demand CLI skill), stitch, mempalace.

Runtime helpers

Static export (auto + manual)

The plugin auto-exports .opencode/.mcp.json at startup so hosts whose opencode mcp list only reads static config can see hiai-managed servers. Controlled by:

  • HIAI_OPENCODE_AUTO_EXPORT_MCP: if-missing (default) | always | off
  • HIAI_OPENCODE_MCP_EXPORT_PATH: override output path
  • HIAI_OPENCODE_EXPORT_MCP_MODE: safe (default) | force (overwrite policy in always mode)

Manual refresh: hiai-opencode export-mcp .opencode/.mcp.json

LSP

LSP defaults live in:

Current defaults: typescript, svelte, eslint, pyright.

LSP tool registrations: src/tools/lsp/ (lsp_diagnostics, lsp_goto_definition, lsp_find_references, lsp_symbols, lsp_prepare_rename, lsp_rename).

Server definitions: src/tools/lsp/server-definitions.ts.

Execution Gates

The plugin enforces a layered safety model. From strongest to weakest:

Hard gates (throw / mutate state)

  • Legal gatesrc/hooks/legal-gate.ts: three-tier deny list (browser automation always blocked, military/malicious always blocked, contextual dual-use with offensive-intent regex). Throws BlockingHookError.
  • Per-agent permission mapssrc/permissions.ts: applyAgentPermissions() converts agent_restrictions into permission.deny / tools.<key>=false. Applied in src/index.ts hooks.config.
  • Completion controllersrc/features/completion-controller/: state machine that gates task completion. decide() (decide.ts) requires: no blocker, no incomplete todos, quality gate passed, LSP diagnostics run (if edits made), and Critic approval (if require_critic and changed files exist).
  • Circuit breakersrc/hooks/circuit-breaker.ts + src/features/background-manager/index.ts: feeds every tool.execute.after call into BackgroundManager.recordSessionToolCall(); trips on N consecutive identical calls (default 20) or total tool calls (default 4000), aborting the session via client.session.abort.
  • Agent-browser guardsrc/tools/agent-browser/index.ts: browserGateGuard() throws for non-vision/general agents.
  • LSP sandboxsrc/tools/lsp/index.ts: throws on paths resolving outside ctx.directory.

Soft gates (mutate output / state)

Closure protocol

All agents must emit a <CLOSURE> block. The completion controller parses Critic verdicts from it.

Worktree Isolation

Git worktree-based task isolation for parallel work.

Components

Gated on config.worktreeConfig?.enabled === true (default: enabled).

Native Background Delegation and Circuit Breaker

The deprecated background_output / background_cancel tools and task-status CLI command were removed because they could not manage native OpenCode task IDs.

CLI

The hiai-opencode CLI is installed via the bin field in package.json:

Commands: doctor, mcp-status, export-mcp, diagnose, task-status. doctor/mcp-status exit non-zero on hard failures (usable as CI gate).

Root Documentation Policy

The root documentation set should stay small:

  • README.md
  • AGENTS.md
  • ARCHITECTURE.md
  • LICENSE.md

Avoid adding more root docs unless they serve a genuinely new role.

Open Source Maintenance Rules

When changing the plugin, keep these invariants:

  • bob.json is the source of truth for user-facing runtime defaults and model IDs
  • src/config.ts DEFAULT_CONFIG is the loader for internal defaults — not a second model map
  • root docs should use canonical runtime names (bob, build, plan, explore, general), not stale aliases
  • the CLI's MCP_REGISTRY must mirror src/features/mcp/registry.ts — keep them in sync
  • user-facing docs should describe visible agents first and hidden/system agents second
  • third-party MCPs should follow upstream install/launch conventions whenever possible