opencode-raven 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/index.ts +36 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ Restart opencode.
|
|
|
45
45
|
| `/raven off` | Disable interception — all agents can use search tools directly |
|
|
46
46
|
| `/raven model <name>` | Change Raven's model (requires restart) |
|
|
47
47
|
| `/raven effort <value>` | Change Raven's reasoning effort (requires restart) |
|
|
48
|
+
| `/raven stats` | Show context processed (session + all-time, bytes + tokens) |
|
|
48
49
|
|
|
49
50
|
Config persists across restarts in `~/.config/opencode/raven-config.json` (global, shared across all projects). Auto-created on first run.
|
|
50
51
|
|
|
@@ -69,7 +70,8 @@ Located at `~/.config/opencode/raven-config.json`. Auto-created on first run. Ed
|
|
|
69
70
|
"enabled": true,
|
|
70
71
|
"model": "opencode/deepseek-v4-flash-free",
|
|
71
72
|
"reasoning_effort": "low",
|
|
72
|
-
"excludeAgents": []
|
|
73
|
+
"excludeAgents": [],
|
|
74
|
+
"excludeTools": []
|
|
73
75
|
}
|
|
74
76
|
```
|
|
75
77
|
|
|
@@ -79,6 +81,8 @@ Located at `~/.config/opencode/raven-config.json`. Auto-created on first run. Ed
|
|
|
79
81
|
| `model` | *(from Raven.md)* | Override Raven's model without editing package files |
|
|
80
82
|
| `reasoning_effort` | *(from Raven.md)* | Override Raven's reasoning effort (e.g. `"low"`, `"medium"`, `"high"`) |
|
|
81
83
|
| `excludeAgents` | `[]` | Agents that bypass search tool blocking (case-insensitive). e.g. `["librarian", "explorer"]` |
|
|
84
|
+
| `excludeTools` | `[]` | Tools that never get blocked. e.g. `["glob", "webfetch"]` |
|
|
85
|
+
| `stats` | *(auto)* | Global blocked call count and context saved. Managed automatically. |
|
|
82
86
|
|
|
83
87
|
### MCP servers
|
|
84
88
|
|
|
@@ -120,10 +124,10 @@ To disable an MCP entirely:
|
|
|
120
124
|
| Hook | What it does |
|
|
121
125
|
|------|--------------|
|
|
122
126
|
| `config` | Registers Raven agent, adds Context7/Exa/Grep.app MCPs, loads MCP guidance |
|
|
123
|
-
| `tool` | Registers `raven_seek` — hidden Raven sessions with error recovery for API failures |
|
|
127
|
+
| `tool` | Registers `raven_seek` — hidden Raven sessions with error recovery for API failures. Tracks context processed for stats. |
|
|
124
128
|
| `chat.message` | Tracks agent ↔ session mapping for allowlist and Raven exclusion |
|
|
125
129
|
| `command.execute.before` | Handles `/raven on\|off\|model\|effort\|status` |
|
|
126
|
-
| `tool.execute.before` | Blocks search tools for non-Raven, non-excluded agents. Injects `<raven_guidance>` into subagent prompts.
|
|
130
|
+
| `tool.execute.before` | Blocks search tools for non-Raven, non-excluded agents (respects `excludeTools`). Injects `<raven_guidance>` into subagent prompts. |
|
|
127
131
|
|
|
128
132
|
### Blocked tools (redirected except for Raven and any agents in `excludeAgents`)
|
|
129
133
|
|
|
@@ -131,7 +135,7 @@ To disable an MCP entirely:
|
|
|
131
135
|
|
|
132
136
|
| Tool | Source |
|
|
133
137
|
|------|--------|
|
|
134
|
-
| `grep`, `glob`, `webfetch`, `fetch` | Built-in |
|
|
138
|
+
| `grep`, `glob`, `webfetch`, `fetch`, `websearch` | Built-in |
|
|
135
139
|
| `websearch_web_search_exa` | WebSearch MCP |
|
|
136
140
|
| `context7_resolve-library-id`, `context7_query-docs` | Context7 MCP |
|
|
137
141
|
| `exa_web_search_exa`, `exa_web_fetch_exa`, `exa_web_search_advanced_exa` | Exa AI MCP |
|
package/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ const SEARCH_TOOLS = [
|
|
|
17
17
|
"glob",
|
|
18
18
|
"webfetch",
|
|
19
19
|
"fetch",
|
|
20
|
+
"websearch",
|
|
20
21
|
// WebSearch MCP
|
|
21
22
|
"websearch_web_search_exa",
|
|
22
23
|
// Context7 MCP
|
|
@@ -65,6 +66,8 @@ interface RavenConfig {
|
|
|
65
66
|
model?: string
|
|
66
67
|
reasoning_effort?: string
|
|
67
68
|
excludeAgents?: string[]
|
|
69
|
+
excludeTools?: string[]
|
|
70
|
+
stats?: { bytes: number }
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
// ── Parse Raven.md frontmatter ──
|
|
@@ -76,6 +79,7 @@ const DEFAULT_CONFIG: RavenConfig = {
|
|
|
76
79
|
model: fm.model,
|
|
77
80
|
reasoning_effort: fm.reasoning_effort,
|
|
78
81
|
excludeAgents: [],
|
|
82
|
+
excludeTools: [],
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
function parseRavenMd(raw: string): { frontmatter: Record<string, any>; prompt: string } {
|
|
@@ -170,6 +174,8 @@ export default ((input: PluginInput) => {
|
|
|
170
174
|
model: raw.model,
|
|
171
175
|
reasoning_effort: raw.reasoning_effort,
|
|
172
176
|
excludeAgents: Array.isArray(raw.excludeAgents) ? raw.excludeAgents : [],
|
|
177
|
+
excludeTools: Array.isArray(raw.excludeTools) ? raw.excludeTools : [],
|
|
178
|
+
stats: raw.stats || undefined,
|
|
173
179
|
}
|
|
174
180
|
}
|
|
175
181
|
} catch { /* ignore corruption, use defaults */ }
|
|
@@ -196,10 +202,30 @@ export default ((input: PluginInput) => {
|
|
|
196
202
|
return config.excludeAgents.some((a) => a.toLowerCase() === lower)
|
|
197
203
|
}
|
|
198
204
|
|
|
199
|
-
// Throttle: show the full error message once per session, then silent
|
|
200
|
-
const throttledSessions = new Set<string>()
|
|
201
205
|
const REROUTE_MSG = "Search tools are blocked. Use raven_seek(query=\"...\") to search through Raven."
|
|
202
206
|
|
|
207
|
+
// ── Context processed by raven_seek ──
|
|
208
|
+
let sessionBytes = 0
|
|
209
|
+
let totalBytes = config.stats?.bytes ?? 0
|
|
210
|
+
|
|
211
|
+
function addBytes(bytes: number) {
|
|
212
|
+
sessionBytes += bytes
|
|
213
|
+
totalBytes += bytes
|
|
214
|
+
config.stats = { bytes: totalBytes }
|
|
215
|
+
saveConfig(config)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function formatBytes(bytes: number): string {
|
|
219
|
+
return bytes >= 1_000_000 ? `${(bytes / 1_000_000).toFixed(1)}MB`
|
|
220
|
+
: bytes >= 1000 ? `${(bytes / 1000).toFixed(1)}KB`
|
|
221
|
+
: `${bytes}B`
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function formatTokens(bytes: number): string {
|
|
225
|
+
const tokens = Math.round(bytes / 4)
|
|
226
|
+
return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}K` : `${tokens}`
|
|
227
|
+
}
|
|
228
|
+
|
|
203
229
|
return {
|
|
204
230
|
config(configInput: any) {
|
|
205
231
|
// MCP servers
|
|
@@ -285,6 +311,9 @@ export default ((input: PluginInput) => {
|
|
|
285
311
|
await client.session.delete({ path: { id: sessionId } })
|
|
286
312
|
} catch { /* non-fatal */ }
|
|
287
313
|
|
|
314
|
+
// Track context saved
|
|
315
|
+
addBytes(output.length)
|
|
316
|
+
|
|
288
317
|
return { title: "Raven Seek", output }
|
|
289
318
|
} catch (err: any) {
|
|
290
319
|
const msg = String(err?.message ?? err ?? "").toLowerCase()
|
|
@@ -326,6 +355,8 @@ export default ((input: PluginInput) => {
|
|
|
326
355
|
config.enabled = false
|
|
327
356
|
saveConfig(config)
|
|
328
357
|
output.parts.push({ type: "text", text: "Raven search interception disabled. All agents can use search tools directly." })
|
|
358
|
+
} else if (arg === "stats") {
|
|
359
|
+
output.parts.push({ type: "text", text: `Raven context processed:\n This session: ${formatBytes(sessionBytes)} (~${formatTokens(sessionBytes)} tokens)\n All time: ${formatBytes(totalBytes)} (~${formatTokens(totalBytes)} tokens)` })
|
|
329
360
|
} else if (arg.startsWith("model ")) {
|
|
330
361
|
const model = raw.slice(6).trim()
|
|
331
362
|
if (!model) {
|
|
@@ -348,7 +379,7 @@ export default ((input: PluginInput) => {
|
|
|
348
379
|
const enabled = config.enabled ? "enabled" : "disabled"
|
|
349
380
|
const model = config.model || fm.model || "(default)"
|
|
350
381
|
const effort = config.reasoning_effort || fm.reasoning_effort || "(default)"
|
|
351
|
-
output.parts.push({ type: "text", text: `Raven is ${enabled}. Model: ${model}. Reasoning: ${effort}\n\nCommands:\n /raven on — enable search interception\n /raven off — disable search interception\n /raven model <name> — change Raven's model (requires restart)\n /raven effort <value> — change Raven's reasoning effort (requires restart)` })
|
|
382
|
+
output.parts.push({ type: "text", text: `Raven is ${enabled}. Model: ${model}. Reasoning: ${effort}\n\nCommands:\n /raven on — enable search interception\n /raven off — disable search interception\n /raven model <name> — change Raven's model (requires restart)\n /raven effort <value> — change Raven's reasoning effort (requires restart)\n /raven stats — show blocked calls and context saved` })
|
|
352
383
|
}
|
|
353
384
|
},
|
|
354
385
|
|
|
@@ -356,6 +387,7 @@ export default ((input: PluginInput) => {
|
|
|
356
387
|
if (!config.enabled) return
|
|
357
388
|
if (ravenSessions.has(input.sessionID)) return
|
|
358
389
|
if (isExcluded(sessionAgents.get(input.sessionID))) return
|
|
390
|
+
if (config.excludeTools?.includes(input.tool)) return
|
|
359
391
|
|
|
360
392
|
// ── Subagent prompt injection: inject Raven guidance into every subagent ──
|
|
361
393
|
if ((input.tool === "task" || input.tool === "subtask") && output.args) {
|
|
@@ -373,16 +405,12 @@ export default ((input: PluginInput) => {
|
|
|
373
405
|
const isSearchBashCmd = isSearchBash(input.tool, output.args || input.args)
|
|
374
406
|
|
|
375
407
|
if (isSearchTool || isSearchBashCmd) {
|
|
376
|
-
if (throttledSessions.has(input.sessionID)) {
|
|
377
|
-
throw new Error("")
|
|
378
|
-
}
|
|
379
|
-
throttledSessions.add(input.sessionID)
|
|
380
408
|
throw new Error(REROUTE_MSG)
|
|
381
409
|
}
|
|
382
410
|
},
|
|
383
411
|
|
|
384
412
|
"tool.execute.after"(input: any, output: any) {
|
|
385
|
-
//
|
|
413
|
+
// Context saved is tracked in raven_seek instead
|
|
386
414
|
},
|
|
387
415
|
}
|
|
388
416
|
}) satisfies Plugin
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-raven",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Search-first subagent for opencode — intercepts search tools and routes them through a hidden Raven agent with Context7, Exa AI, and Grep.app MCPs",
|
|
5
5
|
"main": "./index.ts",
|
|
6
6
|
"exports": {
|