opencode-agent-monitor
v1.2.1
Published
Monitor, audit, and analyze agent/subagent routing and tool usage in OpenCode sessions
Downloads
1,811
Maintainers
Readme
opencode-agent-monitor
Monitor, audit, and analyze agent/subagent routing and tool usage in OpenCode sessions.
Overview
Agent Monitor is an OpenCode plugin that provides observability into how AI agents and subagents are routed, what tools they use, and whether task-domain assignments are appropriate. It writes structured JSON-line logs with optional sensitive data redaction, log rotation, and domain detection.
Key Features
- Open Domain Detection — Define your own domain categories with custom keyword patterns, or use the built-in defaults. No hardcoded types.
- Agent-to-Domain Mapping — Tell the plugin which of your agents handle which domains, regardless of what you name them.
- Routing Mismatch Alerts — Warns when a task's detected domain doesn't match the assigned agent's configured responsibilities.
- Tool Usage Tracking — Logs all tool executions with session context.
- Permission Auditing — Tracks permission requests and decisions.
- Session Lifecycle — Monitors session creation, updates, compaction, errors, and deletion.
- Sensitive Data Redaction — Automatically redacts API keys, tokens, passwords, and connection strings from logs.
- Log Rotation — Automatic log file rotation with configurable size limits and retention.
- Structured Logging — JSON-line format for easy parsing and analysis.
- Zero Configuration — Works out of the box with sensible, secure defaults.
Installation
From npm (Recommended)
- Add the plugin to your
opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-agent-monitor"]
}- OpenCode will automatically install the plugin on next startup.
From Local File
- Clone or download this repository:
git clone https://github.com/tenondecrpc/opencode-agent-monitor.git
cd opencode-agent-monitor
npm install
npm run build- Copy the built plugin to your OpenCode plugins directory:
# Project-level (inside your project's .opencode/ directory)
cp dist/index.js .opencode/plugins/agent-monitor.js
# Or global (shared across all projects — in your home directory)
cp dist/index.js ~/.config/opencode/plugins/agent-monitor.jsFrom Source (Development)
git clone https://github.com/tenondecrpc/opencode-agent-monitor.git
cd opencode-agent-monitor
npm install
npm run dev # Watch mode for developmentThen reference the source file in your config:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["./path/to/opencode-agent-monitor/src/index.ts"]
}Usage
Once installed, the plugin automatically starts monitoring. Logs are written inside the project/repo where OpenCode is running — not in the global OpenCode config directory.
Where logs are stored
OpenCode has two levels of config directories. The plugin uses the project-level directory for logs:
| Directory | Scope | Purpose |
| ---------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------ |
| ~/.config/opencode/ | Global (home directory) | OpenCode CLI global config — providers, models, TUI settings. The plugin does NOT write logs here. |
| <project>/.opencode/ | Per-project (inside the repo) | Project-level agents, commands, plugins. This is where the plugin writes logs by default. |
Default log location:
<your-project>/.opencode/agent-monitor.logReal example: if you run OpenCode in ~/projects/my-app/, the log file will be:
~/projects/my-app/.opencode/agent-monitor.logEach project gets its own log file. Logs are never shared across projects.
How the plugin resolves config
The plugin loads configuration from multiple sources. Project-level config always takes priority over global config:
| Priority | Location | Purpose |
|---|---|---|
| 1 (highest) | <project>/.opencode/agent-monitor.json | Project-specific settings — overrides everything |
| 2 | <project>/.config/opencode/agent-monitor.json | Alternative project location |
| 3 | ~/.config/opencode/agent-monitor.json | Global defaults — shared across all projects |
| 4 (lowest) | Built-in defaults | Sensible, secure defaults |
This means you can set global defaults in ~/.config/opencode/agent-monitor.json and override them per-project in <project>/.opencode/agent-monitor.json.
Important:
~/.config/opencode/is the global OpenCode config in your home directory.<project>/.config/opencode/is a directory inside your project (rarely used). Most projects only have.opencode/at the root.
Log File Permissions
The plugin automatically manages file and directory permissions for security:
| Resource | Permission | Meaning |
| ------------------------------------------- | ---------- | -------------------------------------------- |
| Log directory (.opencode/ inside project) | 0o700 | Only the owner can read, write, and traverse |
| Log file (agent-monitor.log) | 0o600 | Only the owner can read and write |
Can I use a different log location? Yes. Set "logPath" in your config to any path. The plugin creates the directory if it doesn't exist and sets restricted permissions automatically.
Supported path formats:
| Format | Example | Resolves to |
|---|---|---|
| Relative | ".opencode/agent-monitor.log" | <project>/.opencode/agent-monitor.log |
| Tilde (~) | "~/.config/opencode/agent-monitor.log" | Your home directory |
| Absolute | "/var/log/my-monitor.log" | Exact path (as-is) |
Log Format
Each line is a JSON object:
{"ts":"2025-04-26T10:30:00.000Z","type":"tool.execute.after","tool":"bash","sessionID":"abc123","guessedDomains":["backend","cloud"],"domainConfidence":0.45}
{"ts":"2025-04-26T10:30:01.000Z","type":"routing.mismatch","expectedDomains":["frontend"],"actualAgent":"backend","mismatches":["frontend"],"confidence":0.62}
{"ts":"2025-04-26T10:30:02.000Z","type":"permission.asked","tool":"write","sessionID":"abc123"}Analyzing Logs
Use jq or any JSON-line parser to analyze logs. All examples below show the 10 most recent entries (newest first):
# View the 10 most recent routing mismatches
jq 'select(.type == "routing.mismatch")' .opencode/agent-monitor.log | tail -r | head -n 40
# Count tool usage by tool name
jq -r 'select(.type | startswith("tool.")) | .tool' .opencode/agent-monitor.log | sort | uniq -c | sort -rn | head -10
# View the 10 most recent permission requests
jq 'select(.type | startswith("permission."))' .opencode/agent-monitor.log | tail -r | head -n 40
# View the 10 most recent entries for a specific session
jq 'select(.sessionID == "your-session-id")' .opencode/agent-monitor.log | tail -r | head -n 40
# View the 10 most recent log entries of any type
tail -r .opencode/agent-monitor.log | head -10 | jq '.'Tip:
tail -rreverses the file (newest lines first) andhead -10limits output. Since each JSON object spans one line in the log file,tail -r | head -10gives you the 10 most recent entries. For filtered queries, pipe throughtail -r | head -n 40(40 lines ≈ 10 JSON objects) to avoid loading the entire file.
Configuration
The plugin works with zero configuration. All settings use secure defaults.
Quick Start: Simple JSON Config (No Code Required)
Global config (shared across all projects):
~/.config/opencode/agent-monitor.jsonProject config (overrides global for this project only):
<your-project>/.opencode/agent-monitor.jsonProject config always takes priority over global config. If both exist, they are merged with project settings winning.
{
"autoDetectAgents": true,
"domains": [
{ "name": "data-science", "patterns": ["pandas", "numpy", "matplotlib"] }
],
"mergeDomains": true
}That's it. The plugin will:
- Auto-detect your agents from
opencode.jsonand.opencode/agents/*.md - Analyze their descriptions and prompts to figure out what domains they handle
- Generate agent-to-domain mappings automatically
- Merge your custom domains with the built-in defaults
Full JSON Config Options
Note: You don't need this file to get started. The plugin works with zero configuration — just install it and logs are written automatically. Only create
agent-monitor.jsonif you want to override defaults.
Config resolution order (later sources override earlier ones):
~/.config/opencode/agent-monitor.json— global defaults<project>/.opencode/agent-monitor.json— project overrides<project>/.config/opencode/agent-monitor.json— alternative project locationuserConfig— programmatic config (if using a TypeScript wrapper)
About logPath: This field is optional. If omitted, logs go to <project>/.opencode/agent-monitor.log. If you set it, any path is accepted — relative (resolved against project), absolute, or tilde-prefixed (~).
{
"enabled": true,
"maxLogSize": 10485760,
"maxRotatedFiles": 5,
"enableDomainDetection": true,
"enableToolTracking": true,
"enablePermissionTracking": true,
"enableSessionTracking": true,
"redactSensitiveData": true,
"logLevel": "info",
"emitRoutingWarnings": true,
"excludedTools": [],
"autoDetectAgents": true,
"domains": [
{ "name": "data-science", "patterns": ["pandas", "numpy", "matplotlib"] },
{ "name": "mobile", "patterns": ["react native", "flutter", "swift"] }
],
"mergeDomains": true,
"agentMappings": [
{ "agentName": "my-ui-agent", "domains": ["frontend", "vision"] },
{ "agentName": "api-builder", "domains": ["backend", "cloud"] }
],
"display": {
"toasts": true,
"structuredLogging": true
}
}Example with explicit logPath:
// Write logs to your global OpenCode config directory (shared across projects)
{ "logPath": "~/.config/opencode/agent-monitor.log" }
// Or keep it project-local (default behavior)
{ "logPath": ".opencode/agent-monitor.log" }Display Options
The plugin has three levels of visibility, from always-on to optional:
| Level | What | Default | Configurable? |
| ----------------------- | -------------------------------------------------------------------- | ------------ | -------------- |
| File logging | All events written to <project>/.opencode/agent-monitor.log | ✅ Always on | No (mandatory) |
| Structured logging | Events sent to OpenCode's internal log viewer via client.app.log() | ✅ On | Yes |
| Toast notifications | Brief pop-up messages in the TUI for important events | ✅ On | Yes |
Toast Notifications
When enabled, the plugin shows brief, non-intrusive toast messages for:
- Routing mismatches — when a task's detected domain doesn't match the assigned agent
- Session errors — when a session encounters an error
- Permission denials — when a permission request is denied
{
"display": {
"toasts": true
}
}Example toast: ⚠ Routing mismatch: "backend" handling [frontend]
Toasts auto-dismiss and don't interrupt the workflow.
Structured Logging
When enabled (default), events are sent to OpenCode's internal logging system. These can be viewed through OpenCode's log viewer and filtered by service name agent-monitor.
{
"display": {
"structuredLogging": false
}
}How Auto-Detection Works
When autoDetectAgents is true (default), the plugin:
- Reads
opencode.json— finds all agents in the"agent"section - Reads agent markdown files — finds agents in
<project>/.opencode/agents/*.md(or~/.config/opencode/agents/*.mdfor globally-defined agents) - Analyzes descriptions and prompts — uses the domain detection engine to figure out what each agent does
- Generates mappings — creates
agentName → domainsmappings automatically
For built-in agents (build, plan, general, explore), the plugin already knows their domains.
For custom agents, it analyzes text like:
{
"agent": {
"security-reviewer": {
"description": "Reviews code for security vulnerabilities, OWASP compliance, and encryption",
"prompt": "You are a security expert. Look for XSS, CSRF, and injection attacks."
}
}
}This auto-generates: { agentName: "security-reviewer", domains: ["security"] }
Manual Override
Auto-detected mappings are merged with manually configured ones. Manual mappings always take precedence:
{
"autoDetectAgents": true,
"agentMappings": [{ "agentName": "my-agent", "domains": ["custom-domain"] }]
}If auto-detection finds my-agent handles ["frontend"], the manual mapping ["custom-domain"] wins.
Disable Auto-Detection
{
"autoDetectAgents": false
}When disabled, only manually configured agentMappings are used. If no mappings are configured, the plugin falls back to substring matching (checks if agent name contains domain name).
Basic Options
| Option | Default | Description |
| -------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| enabled | true | Enable or disable the monitor |
| logPath | <project>/.opencode/agent-monitor.log | Optional. Relative paths resolve against the project root. Tilde (~) expands to home. Absolute paths are used as-is. |
| maxLogSize | 10485760 (10 MB) | Max log file size before rotation (0 = disabled) |
| maxRotatedFiles | 5 | Number of rotated log files to keep |
| enableDomainDetection | true | Enable domain detection and routing analysis |
| enableToolTracking | true | Enable tool usage tracking |
| enablePermissionTracking | true | Enable permission request tracking |
| enableSessionTracking | true | Enable session lifecycle tracking |
| redactSensitiveData | true | Redact secrets, keys, and tokens from logs |
| logLevel | "info" | Log level for structured logging |
| emitRoutingWarnings | true | Emit warnings for routing mismatches |
| excludedTools | [] | Tools to exclude from tracking |
TypeScript Configuration (Advanced)
For full programmatic control, create a plugin wrapper in TypeScript:
// <your-project>/.opencode/plugins/agent-monitor.ts (project-level)
// or ~/.config/opencode/plugins/agent-monitor.ts (global)
import { AgentMonitor } from "opencode-agent-monitor"
import {
resolveConfigAsync,
StructuredLogger,
detectDomains,
detectAgentMismatch,
} from "opencode-agent-monitor/exports"
// Option 1: Use the plugin as-is (auto-detects agents, uses defaults)
export const AgentMonitorDefault = async (ctx) => {
return AgentMonitor(ctx)
}
// Option 2: Build your own monitor with custom config
export const AgentMonitorCustom = async ({ client, directory }) => {
const config = await resolveConfigAsync(
{
domains: [
{
name: "data-science",
patterns: ["pandas", "numpy", "matplotlib", "jupyter", "sklearn"],
},
],
mergeDomainDefinitions: true,
},
directory
)
const logger = new StructuredLogger(config)
return {
event: async ({ event }) => {
await logger.write({ type: "event", eventType: event?.type })
},
// ... other hooks
}
}Agent-to-Domain Mappings
With auto-detection enabled (default), you don't need to configure mappings manually — the plugin discovers your agents and generates mappings automatically.
For manual configuration, use the JSON config file:
{
"agentMappings": [
{ "agentName": "my-ui-specialist", "domains": ["frontend", "vision"] },
{ "agentName": "api-builder", "domains": ["backend"] },
{ "agentName": "cloud-deployer", "domains": ["cloud", "security"] },
{
"agentName": "fullstack-dev",
"domains": ["frontend", "backend", "cloud"]
}
]
}With these mappings:
- If a task about "React components" is given to
my-ui-specialist→ no mismatch (handles frontend) - If a task about "React components" is given to
api-builder→ mismatch (doesn't handle frontend) - If a task about "React + Docker" is given to
fullstack-dev→ no mismatch (handles both)
Fallback behavior (no mappings configured)
If you don't configure agentMappings and auto-detection finds no agents, the plugin checks if the agent name contains the domain name:
- Agent
"frontend-agent"+ task about "React" → no mismatch (name contains "frontend") - Agent
"backend"+ task about "React" → mismatch (name doesn't contain "frontend")
Built-in Default Domains
These domains ship as defaults but are not enforced as types. You can use any domain name you want.
| Domain | Sample Keywords |
| ------------ | ----------------------------------------------------------------- |
| frontend | React, Vue, CSS, UI, responsive, accessibility, component, layout |
| backend | API, REST, GraphQL, database, route, controller, service, webhook |
| cloud | AWS, GCP, Azure, Docker, Kubernetes, Terraform, CI/CD, deploy |
| security | IAM, secrets, encryption, vulnerability, OWASP, OAuth, JWT, RBAC |
| architect | Design pattern, SOLID, microservice, DDD, scalability, coupling |
| qa | Test, Jest, Cypress, regression, coverage, edge case, E2E |
| documenter | README, docs, changelog, migration, runbook, tutorial |
| refiner | Refactor, cleanup, lint, formatting, naming, dead code |
| explore | Search, discover, dependency, trace, convention, map |
| vision | Screenshot, image, visual, mockup, design, Figma |
| general | Summary, rewrite, utility, helper, script, automation |
Security
This plugin follows security best practices:
- Sensitive data redaction is enabled by default, protecting API keys, tokens, passwords, and connection strings
- No data leaves your machine — all logs are written locally
- No network calls — the plugin does not make any HTTP requests
- Safe error handling — log failures never crash the plugin
- Log rotation prevents unbounded disk usage
See SECURITY.md for the full security policy.
Project Structure
opencode-agent-monitor/
├── src/
│ ├── index.ts # Plugin entry point
│ ├── types.ts # TypeScript type definitions (open Domain type)
│ ├── config.ts # Configuration resolution + agent mappings
│ ├── logger.ts # Structured logger with rotation
│ └── domain-detector.ts # Domain detection engine
├── tests/
│ ├── domain-detector.test.ts
│ ├── logger.test.ts
│ └── config.test.ts
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # CI pipeline
│ │ └── release.yml # Release automation
│ ├── ISSUE_TEMPLATE/
│ └── pull_request_template.md
├── package.json
├── tsconfig.json
├── eslint.config.js
├── .prettierrc
├── .gitignore
├── README.md
├── CONTRIBUTING.md
├── SECURITY.md
└── LICENSEDevelopment
Prerequisites
Scripts
npm install # Install dependencies
npm run build # Build the plugin
npm run dev # Watch mode
npm test # Run tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage
npm run lint # Run ESLint
npm run lint:fix # Fix linting issues
npm run format # Format code with Prettier
npm run typecheck # Type check without emittingRunning Tests
npm testPublishing
- Update version in
package.json - Create a git tag:
git tag v1.0.0 - Push the tag:
git push origin v1.0.0 - The release workflow will publish to npm automatically
Contributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Ways to Contribute
- Report bugs via GitHub Issues
- Suggest features or improvements
- Submit pull requests
- Improve documentation
- Add new domain patterns to the defaults
- Write tests
License
This project is licensed under the MIT License.
Acknowledgments
- Built for OpenCode — the open-source AI coding agent
- Inspired by the need for agent observability in multi-agent workflows
