Centralized tool factory and registry for the OpenCode plugin system. This directory defines all executable tools exposed to OpenCode agents, including:
/preset managerThese tools enable agents to perform file operations, manage background tasks, and interact with external systems while maintaining security boundaries through the OpenCode tool schema. Multi-LLM council orchestration is agent-level (dynamic councillor-<name> subagents in src/agents/), not a tool.
Each tool is implemented as a factory function that returns a ToolDefinition record compatible with the @opencode-ai/plugin SDK. The pattern provides:
src/tools/index.ts| Tool Family | Purpose | Key Components |
|---|---|---|
| Task Management | Background task communication, cancellation, status, results, revival, and HITL continuation control | task-message.ts, cancel-task.ts, task-status.ts, task-result.ts, task-revive.ts, wait-for-user.ts |
| Task Policy & Activity | Shared live-status policy and activity tracking consumed by task_status and event wiring |
task-policy.ts (summarizeTaskStatus), task-activity.ts (TaskActivityTracker) |
| ACP Integration | External agent protocol execution | acp-run.ts, ACP client implementation |
| Marketplace | Local offline package lifecycle and status | marketplace.ts wrapping MarketplaceService |
| Code Intelligence | AST-based code manipulation | ast-grep/ directory, tools.ts |
| Web Fetching | Intelligent web content retrieval | smartfetch/ directory, tool.ts |
| Preset Switching | On-disk preset persistence for the TUI /preset manager |
preset-switch.ts, TUI state integration |
ctx.ask()preset-switch.ts) persists the preset name to the user config file; the sidebar is NOT refreshed mid-session (the agent registry is unchanged until reload) — hot-swapping the agent tree during an active conversation risks context truncation, drifted prior turns, and stale subagent referencestask_status reports live-confirmed host status with explicit uncertainty when the live read is unavailable1. Plugin Initialization (src/index.ts)
└─> registerTools() calls each tool factory with dependencies
2. Tool Factory Execution
├─> Accepts PluginInput context and domain-specific dependencies
├─> Validates configuration and environment
├─> Returns ToolDefinition record with execute() handler
└─> Registers tool with OpenCode via plugin API
3. Tool Invocation
├─> Agent calls tool with validated arguments
├─> Tool executes business logic
├─> May call ctx.ask() for user permission
├─> Returns structured result or error
└─> OpenCode presents result to agent
1. Orchestrator invokes task_cancel
├─> Validates calling agent is 'orchestrator'
├─> Resolves task_id to BackgroundJobBoard entry
├─> Calls abortSessionWithTimeout() to signal cancellation
├─> Verifies session stopped via status polling
├─> Marks job as cancelled in BackgroundJobBoard
└─> Returns cancellation confirmation while retaining the child session
2. Orchestrator invokes task_message
├─> Resolves task_id to a live BackgroundJobBoard entry
├─> Acquires a generation-scoped message lease
├─> Queues a bounded no-reply message without interrupting or resuming the child
└─> Returns transport-confirmed queue status
3. Orchestrator invokes task_revive
├─> Resolves the retained BackgroundJobBoard entry
├─> Cancels a running generation when necessary
├─> Launches a new prompt in the existing child session
├─> Registers the new generation and tracks its completion
└─> Returns the new running generation
1. Orchestrator gives the user concrete manual steps
└─> Invokes wait_for_user as its final tool action
├─> Validates session ID, agent identity, and managed-session ownership
├─> Arms task-session-manager.beginUserWait() (process-global latch)
├─> Revokes pending automatic-continuation reservations
└─> Returns the versioned waiting_for_user protocol marker
1. Agent invokes task_status
├─> Resolves task_id to a BackgroundJobBoard entry
├─> Reads the bounded live session-status snapshot (session-runtime-status)
├─> summarizeTaskStatus() (task-policy.ts) prefers the live-confirmed host
│ status; board state is only reported with explicit uncertainty
└─> possibly_stuck requires a live-confirmed busy/retry signal beyond the
STUCK_IDLE_THRESHOLD_MS idle threshold
1. Agent invokes acp_run tool
├─> Validates calling agent matches configured agent name
├─> Spawns ACP client process with config
├─> Sends prompt via JSON-RPC over stdin/stdout
├─> Handles permission requests via ctx.ask()
├─> Collects streaming output chunks
├─> Enforces timeout if configured
└─> Returns concatenated output or error
1. Agent invokes ast_grep_search or ast_grep_replace
├─> Validates language support and pattern syntax
├─> Ensures CLI binary available (downloads if needed)
├─> Executes sg (AST-grep CLI) process
├─> Parses JSON output into structured matches/edits
└─> Returns typed results to agent
1. Agent invokes webfetch tool
├─> Validates URL and configuration
├─> Checks cache for fresh content
├─> If cache miss, fetches via network with timeout
├─> Optionally processes with secondary model
├─> Caches result for future requests
└─> Returns extracted content to agent
Main Plugin (src/index.ts):
registerTools() - Registers all exported tools with OpenCodegetToolDefinitions() - Composes tool set for plugin initialization
Agents (src/agents/):
acp_run tool for specialized tasksast_grep_search/ast_grep_replace for code manipulationTUI (src/tui-preset.ts):
/preset manager uses switchPresetOnDisk / writePreset / deletePreset from preset-switch.ts| Dependency | Purpose |
|---|---|
@opencode-ai/plugin |
Tool schema and execution framework |
BackgroundJobBoard (src/utils/) |
Background task tracking and cleanup |
Session Runtime Status (src/utils/session-runtime-status.ts) |
Bounded live session-status reads for task_status |
Config System (src/config/) |
ACP agent configurations and presets |
TUI State (src/tui-state.ts) |
Preset visualization in terminal UI |
AST-grep CLI |
Pattern matching and transformation engine |
Network Utilities |
Web fetching and caching |
Tools Layer → Background Layer
├─ task_cancel → BackgroundJobBoard.resolve() → abortSessionWithTimeout()
├─ task_message → BackgroundJobBoard.resolve() → no-reply prompt transport
├─ task_revive → BackgroundJobBoard.resolve() → retained-session relaunch
├─ task_status → BackgroundJobBoard.resolve() → live session-status snapshot
└─> Returns lifecycle, transport, or status report
Tools Layer → Config Layer
├─ acp_run tool → AcpAgentsConfig from config system
├─ preset-switch → reads/writes the user config file's `presets`/`preset` fields
└─> Validates and persists preset state
Tools Layer → AST-grep Layer
├─ ast_grep_search/ast_grep_replace → CLI binary execution
└─> Returns typed AST matches and edit results
Tools Layer → Web Layer
└─ webfetch tool → Network utilities with caching and model processing
src/config/agents.ts, consumed by acp_run.tsoh-my-opencode-slim.jsonc), persisted by preset-switch.ts for the TUI /preset managertask-status.ts consumes summarizeTaskStatus from task-policy.ts and the live session-status snapshotcancel-task.ts implements robust abort verification with polling and cleanupctx.ask()ast-grep/ tools auto-download CLI on first use// AST-grep tools
export { createAcpRunTool } from './acp-run';
export { ast_grep_replace, ast_grep_search } from './ast-grep';
export { createCancelTaskTool } from './cancel-task';
export { createWebfetchTool } from './smartfetch';
export { createTaskMessageTool } from './task-message';
export { createTaskResultTool } from './task-result';
export { createTaskReviveTool } from './task-revive';
export { createTaskStatusTool } from './task-status';
export { createMarketplaceTool } from './marketplace';
export { createWaitForUserTool } from './wait-for-user';
Preset switching is not a tool: preset-switch.ts exposes on-disk helpers
(switchPresetOnDisk, writePreset, deletePreset, setAgentOverride,
removeAgentFromPreset) consumed by the TUI /preset manager
(src/tui-preset.ts).
src/config/agents.ts as AcpAgentsConfigcommand, args, cwd, permissionModeask (prompt user), reject (auto-deny), allow (auto-approve)src/agents/council-agents.ts builds a prefixed councillor-<name> subagent per preset seatpresets fieldAgentOverrideConfigswitchPresetOnDisk persists the preset name to the user config file; changes take effect on the next reload*.test.ts files