opencode-raven 1.2.1 → 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 +4 -4
- package/index.ts +14 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +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
|
|
48
|
+
| `/raven stats` | Show context processed (session + all-time, bytes + tokens) |
|
|
49
49
|
|
|
50
50
|
Config persists across restarts in `~/.config/opencode/raven-config.json` (global, shared across all projects). Auto-created on first run.
|
|
51
51
|
|
|
@@ -124,10 +124,10 @@ To disable an MCP entirely:
|
|
|
124
124
|
| Hook | What it does |
|
|
125
125
|
|------|--------------|
|
|
126
126
|
| `config` | Registers Raven agent, adds Context7/Exa/Grep.app MCPs, loads MCP guidance |
|
|
127
|
-
| `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. |
|
|
128
128
|
| `chat.message` | Tracks agent ↔ session mapping for allowlist and Raven exclusion |
|
|
129
129
|
| `command.execute.before` | Handles `/raven on\|off\|model\|effort\|status` |
|
|
130
|
-
| `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. |
|
|
131
131
|
|
|
132
132
|
### Blocked tools (redirected except for Raven and any agents in `excludeAgents`)
|
|
133
133
|
|
|
@@ -135,7 +135,7 @@ To disable an MCP entirely:
|
|
|
135
135
|
|
|
136
136
|
| Tool | Source |
|
|
137
137
|
|------|--------|
|
|
138
|
-
| `grep`, `glob`, `webfetch`, `fetch` | Built-in |
|
|
138
|
+
| `grep`, `glob`, `webfetch`, `fetch`, `websearch` | Built-in |
|
|
139
139
|
| `websearch_web_search_exa` | WebSearch MCP |
|
|
140
140
|
| `context7_resolve-library-id`, `context7_query-docs` | Context7 MCP |
|
|
141
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
|
|
@@ -66,7 +67,7 @@ interface RavenConfig {
|
|
|
66
67
|
reasoning_effort?: string
|
|
67
68
|
excludeAgents?: string[]
|
|
68
69
|
excludeTools?: string[]
|
|
69
|
-
stats?: {
|
|
70
|
+
stats?: { bytes: number }
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
// ── Parse Raven.md frontmatter ──
|
|
@@ -201,55 +202,16 @@ export default ((input: PluginInput) => {
|
|
|
201
202
|
return config.excludeAgents.some((a) => a.toLowerCase() === lower)
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
// Throttle: show the full error message once per session, then silent
|
|
205
|
-
const throttledSessions = new Set<string>()
|
|
206
205
|
const REROUTE_MSG = "Search tools are blocked. Use raven_seek(query=\"...\") to search through Raven."
|
|
207
206
|
|
|
208
|
-
// ──
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// Restore global stats from config
|
|
212
|
-
if (config.stats) {
|
|
213
|
-
globalStats.blocked = config.stats.blocked ?? 0
|
|
214
|
-
globalStats.bytesSaved = config.stats.bytesSaved ?? 0
|
|
215
|
-
for (const [t, n] of Object.entries(config.stats.tools ?? {})) {
|
|
216
|
-
globalStats.tools.set(t, n)
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function getBytesEstimate(tool: string): number {
|
|
221
|
-
const ESTIMATES: Record<string, number> = {
|
|
222
|
-
grep: 2000, glob: 2000,
|
|
223
|
-
webfetch: 16000, fetch: 16000,
|
|
224
|
-
websearch_web_search_exa: 8000,
|
|
225
|
-
context7_resolve_library_id: 1000, context7_query_docs: 4000,
|
|
226
|
-
exa_web_search_exa: 8000, exa_web_fetch_exa: 16000,
|
|
227
|
-
exa_web_search_advanced_exa: 8000, exa_company_research_exa: 8000,
|
|
228
|
-
exa_crawling_exa: 12000, exa_people_search_exa: 6000,
|
|
229
|
-
exa_linkedin_search_exa: 6000, exa_get_code_context_exa: 4000,
|
|
230
|
-
exa_deep_researcher_start: 8000, exa_deep_researcher_check: 2000,
|
|
231
|
-
exa_deep_search_exa: 8000,
|
|
232
|
-
grep_app_searchGitHub: 4000,
|
|
233
|
-
}
|
|
234
|
-
return ESTIMATES[tool] ?? 2000 // default for bash search / unknown
|
|
235
|
-
}
|
|
207
|
+
// ── Context processed by raven_seek ──
|
|
208
|
+
let sessionBytes = 0
|
|
209
|
+
let totalBytes = config.stats?.bytes ?? 0
|
|
236
210
|
|
|
237
|
-
function
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
stats = { blocked: 0, tools: new Map(), bytesSaved: 0 }
|
|
242
|
-
sessionStats.set(sessionID, stats)
|
|
243
|
-
}
|
|
244
|
-
stats.blocked++
|
|
245
|
-
stats.tools.set(tool, (stats.tools.get(tool) ?? 0) + 1)
|
|
246
|
-
stats.bytesSaved += getBytesEstimate(tool)
|
|
247
|
-
// Global stats
|
|
248
|
-
globalStats.blocked++
|
|
249
|
-
globalStats.tools.set(tool, (globalStats.tools.get(tool) ?? 0) + 1)
|
|
250
|
-
globalStats.bytesSaved += getBytesEstimate(tool)
|
|
251
|
-
// Persist to config
|
|
252
|
-
config.stats = { blocked: globalStats.blocked, bytesSaved: globalStats.bytesSaved, tools: Object.fromEntries(globalStats.tools) }
|
|
211
|
+
function addBytes(bytes: number) {
|
|
212
|
+
sessionBytes += bytes
|
|
213
|
+
totalBytes += bytes
|
|
214
|
+
config.stats = { bytes: totalBytes }
|
|
253
215
|
saveConfig(config)
|
|
254
216
|
}
|
|
255
217
|
|
|
@@ -349,6 +311,9 @@ export default ((input: PluginInput) => {
|
|
|
349
311
|
await client.session.delete({ path: { id: sessionId } })
|
|
350
312
|
} catch { /* non-fatal */ }
|
|
351
313
|
|
|
314
|
+
// Track context saved
|
|
315
|
+
addBytes(output.length)
|
|
316
|
+
|
|
352
317
|
return { title: "Raven Seek", output }
|
|
353
318
|
} catch (err: any) {
|
|
354
319
|
const msg = String(err?.message ?? err ?? "").toLowerCase()
|
|
@@ -391,14 +356,7 @@ export default ((input: PluginInput) => {
|
|
|
391
356
|
saveConfig(config)
|
|
392
357
|
output.parts.push({ type: "text", text: "Raven search interception disabled. All agents can use search tools directly." })
|
|
393
358
|
} else if (arg === "stats") {
|
|
394
|
-
|
|
395
|
-
const sessionStr = !session || session.blocked === 0
|
|
396
|
-
? " No blocks this session."
|
|
397
|
-
: ` Session: ${session.blocked} blocked, ~${formatBytes(session.bytesSaved)} (~${formatTokens(session.bytesSaved)} tokens)`
|
|
398
|
-
const globalStr = globalStats.blocked === 0
|
|
399
|
-
? " Global: no blocks yet."
|
|
400
|
-
: ` Global: ${globalStats.blocked} blocked, ~${formatBytes(globalStats.bytesSaved)} (~${formatTokens(globalStats.bytesSaved)} tokens)`
|
|
401
|
-
output.parts.push({ type: "text", text: `Raven stats:\n${sessionStr}\n${globalStr}` })
|
|
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)` })
|
|
402
360
|
} else if (arg.startsWith("model ")) {
|
|
403
361
|
const model = raw.slice(6).trim()
|
|
404
362
|
if (!model) {
|
|
@@ -447,17 +405,12 @@ export default ((input: PluginInput) => {
|
|
|
447
405
|
const isSearchBashCmd = isSearchBash(input.tool, output.args || input.args)
|
|
448
406
|
|
|
449
407
|
if (isSearchTool || isSearchBashCmd) {
|
|
450
|
-
trackBlock(input.sessionID, isSearchBashCmd ? "bash(search)" : input.tool)
|
|
451
|
-
if (throttledSessions.has(input.sessionID)) {
|
|
452
|
-
throw new Error("")
|
|
453
|
-
}
|
|
454
|
-
throttledSessions.add(input.sessionID)
|
|
455
408
|
throw new Error(REROUTE_MSG)
|
|
456
409
|
}
|
|
457
410
|
},
|
|
458
411
|
|
|
459
412
|
"tool.execute.after"(input: any, output: any) {
|
|
460
|
-
//
|
|
413
|
+
// Context saved is tracked in raven_seek instead
|
|
461
414
|
},
|
|
462
415
|
}
|
|
463
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": {
|