opencode-raven 1.2.7 → 1.2.8
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 +1 -1
- package/index.ts +66 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Restart opencode.
|
|
|
47
47
|
| `/raven model <name>` | Change Raven's model (requires restart) |
|
|
48
48
|
| `/raven effort <value>` | Change Raven's reasoning effort (requires restart) |
|
|
49
49
|
| `/raven timeout <seconds>` | Change raven_seek timeout (min 10s, takes effect immediately) |
|
|
50
|
-
| `/raven stats` | Show context
|
|
50
|
+
| `/raven stats` | Show context saved (session + all-time, bytes + tokens) |
|
|
51
51
|
|
|
52
52
|
Config persists across restarts in `~/.config/opencode/raven-config.json` (global, shared across all projects). Auto-created on first run.
|
|
53
53
|
|
package/index.ts
CHANGED
|
@@ -262,7 +262,9 @@ export default ((input: PluginInput) => {
|
|
|
262
262
|
let config = loadConfig()
|
|
263
263
|
const ravenSessions = new Set<string>()
|
|
264
264
|
const ravenTaskCalls = new Set<string>()
|
|
265
|
+
const ravenTaskPrompts = new Map<string, number>()
|
|
265
266
|
const sessionAgents = new Map<string, string>()
|
|
267
|
+
const ravenSessionParents = new Map<string, string>()
|
|
266
268
|
let updateInfo: { current: string; latest?: string; available: boolean } | undefined
|
|
267
269
|
let updateCheckPromise: Promise<{ current: string; latest?: string; available: boolean }> | undefined
|
|
268
270
|
let updateToastPending = false
|
|
@@ -343,6 +345,20 @@ export default ((input: PluginInput) => {
|
|
|
343
345
|
return { current: PACKAGE_VERSION, latest, available: !!latest && compareVersions(latest, PACKAGE_VERSION) > 0 }
|
|
344
346
|
}
|
|
345
347
|
|
|
348
|
+
async function countRavenSessionBytes(sessionId: string): Promise<number> {
|
|
349
|
+
// Get last assistant message token counts (matches TUI bottom bar)
|
|
350
|
+
const messagesResp = await client.session.messages({ path: { id: sessionId }, query: { limit: 200 } })
|
|
351
|
+
const messages = (messagesResp as any)?.data ?? []
|
|
352
|
+
// Find last assistant message with output tokens (same logic as TUI subagent-footer.tsx)
|
|
353
|
+
const last = [...messages].reverse().find((m: any) =>
|
|
354
|
+
m?.info?.role === "assistant" && m?.info?.tokens?.output > 0
|
|
355
|
+
)
|
|
356
|
+
const t = last?.info?.tokens
|
|
357
|
+
if (!t) return 0
|
|
358
|
+
const totalTokens = (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) + (t.cache?.read ?? 0) + (t.cache?.write ?? 0)
|
|
359
|
+
return totalTokens * 4
|
|
360
|
+
}
|
|
361
|
+
|
|
346
362
|
async function getUpdateInfo(): Promise<{ current: string; latest?: string; available: boolean }> {
|
|
347
363
|
if (updateInfo) return updateInfo
|
|
348
364
|
if (!updateCheckPromise) {
|
|
@@ -520,10 +536,23 @@ export default ((input: PluginInput) => {
|
|
|
520
536
|
.map((p: any) => p.text)
|
|
521
537
|
const output = textParts.join("\n") || "Raven returned no results."
|
|
522
538
|
|
|
523
|
-
//
|
|
524
|
-
|
|
539
|
+
// Get total Raven session context and subtract input/output to get context saved
|
|
540
|
+
let totalProcessed = 0
|
|
541
|
+
try {
|
|
542
|
+
totalProcessed = await countRavenSessionBytes(sessionId)
|
|
543
|
+
} catch { /* best-effort */ }
|
|
544
|
+
if (totalProcessed <= 0) {
|
|
545
|
+
for (const part of parts) {
|
|
546
|
+
if (part.text) totalProcessed += part.text.length
|
|
547
|
+
if (part.args) totalProcessed += JSON.stringify(part.args).length
|
|
548
|
+
if (part.content) totalProcessed += typeof part.content === "string" ? part.content.length : JSON.stringify(part.content).length
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
// Context saved = total session context − input query − compact answer returned
|
|
552
|
+
const saved = Math.max(0, totalProcessed - output.length - String(args.query).length)
|
|
553
|
+
addBytes(saved)
|
|
525
554
|
|
|
526
|
-
return { title: "Raven Seek", metadata: { sessionId }, output: `${output}\n\n*Raven searched for ${elapsed}s — ${formatBytes(
|
|
555
|
+
return { title: "Raven Seek", metadata: { sessionId }, output: `${output}\n\n*Raven searched for ${elapsed}s — ${formatBytes(totalProcessed)} processed, ${formatTokens(totalProcessed)} tokens*` }
|
|
527
556
|
} catch (err: any) {
|
|
528
557
|
const elapsed = ((Date.now() - started) / 1000).toFixed(1)
|
|
529
558
|
const msg = String(err?.message ?? err ?? "").toLowerCase()
|
|
@@ -550,7 +579,13 @@ export default ((input: PluginInput) => {
|
|
|
550
579
|
}
|
|
551
580
|
},
|
|
552
581
|
|
|
553
|
-
event() {
|
|
582
|
+
event(input: { event: any }) {
|
|
583
|
+
// Track subagent session → parent mapping for accurate context counting
|
|
584
|
+
const evt = input.event
|
|
585
|
+
if (evt?.type === "session.created" && evt?.properties?.parentID) {
|
|
586
|
+
ravenSessionParents.set(evt.properties.parentID, evt.properties.id)
|
|
587
|
+
}
|
|
588
|
+
|
|
554
589
|
if (!updateToastPending) return
|
|
555
590
|
updateToastPending = false
|
|
556
591
|
setTimeout(() => void notifyIfUpdateAvailable(), 500)
|
|
@@ -572,7 +607,7 @@ export default ((input: PluginInput) => {
|
|
|
572
607
|
saveConfig(config)
|
|
573
608
|
output.parts.push({ type: "text", text: "Raven search interception disabled. All agents can use search tools directly." })
|
|
574
609
|
} else if (arg === "stats") {
|
|
575
|
-
output.parts.push({ type: "text", text: `Raven context
|
|
610
|
+
output.parts.push({ type: "text", text: `Raven context saved:\n This session: ${formatBytes(sessionBytes)} (~${formatTokens(sessionBytes)} context)\n All time: ${formatBytes(totalBytes)} (~${formatTokens(totalBytes)} context)` })
|
|
576
611
|
} else if (arg === "update") {
|
|
577
612
|
try {
|
|
578
613
|
const info = await refreshUpdateInfo()
|
|
@@ -626,7 +661,7 @@ export default ((input: PluginInput) => {
|
|
|
626
661
|
? `Update: ${info.latest} available. Run /raven update, then restart opencode.`
|
|
627
662
|
: `Update: up to date${info.latest ? ` (latest ${info.latest})` : ""}.`
|
|
628
663
|
} catch { /* keep fallback */ }
|
|
629
|
-
output.parts.push({ type: "text", text: `Raven is ${enabled}. Version: ${PACKAGE_VERSION}. Model: ${model}. Reasoning: ${effort}. Timeout: ${timeout}s\n${update}\n\nCommands:\n /raven on — enable search interception\n /raven off — disable search interception\n /raven update — check npm, clear plugin cache if newer, then restart opencode\n /raven model <name> — change Raven's model (requires restart)\n /raven effort <value> — change Raven's reasoning effort (requires restart)\n /raven timeout <seconds> — change raven_seek timeout\n /raven stats — show
|
|
664
|
+
output.parts.push({ type: "text", text: `Raven is ${enabled}. Version: ${PACKAGE_VERSION}. Model: ${model}. Reasoning: ${effort}. Timeout: ${timeout}s\n${update}\n\nRaven context saved:\n This session: ${formatBytes(sessionBytes)} (~${formatTokens(sessionBytes)} context)\n All time: ${formatBytes(totalBytes)} (~${formatTokens(totalBytes)} context)\n\nCommands:\n /raven on — enable search interception\n /raven off — disable search interception\n /raven update — check npm, clear plugin cache if newer, then restart opencode\n /raven model <name> — change Raven's model (requires restart)\n /raven effort <value> — change Raven's reasoning effort (requires restart)\n /raven timeout <seconds> — change raven_seek timeout\n /raven stats — show context saved` })
|
|
630
665
|
}
|
|
631
666
|
},
|
|
632
667
|
|
|
@@ -645,6 +680,10 @@ export default ((input: PluginInput) => {
|
|
|
645
680
|
const subagentType = input.tool === "task" ? (output.args.subagent_type ?? "") : ""
|
|
646
681
|
if (subagentType === "raven") {
|
|
647
682
|
ravenTaskCalls.add(input.callID)
|
|
683
|
+
const promptField = ["prompt", "description", "request", "objective", "query"].find(
|
|
684
|
+
(f) => f in output.args
|
|
685
|
+
) ?? "prompt"
|
|
686
|
+
ravenTaskPrompts.set(input.callID, String(output.args[promptField] ?? "").length)
|
|
648
687
|
}
|
|
649
688
|
if (subagentType !== "raven" && !isExcluded(subagentType)) {
|
|
650
689
|
const field = ["prompt", "description", "request", "objective", "query"].find(
|
|
@@ -666,8 +705,27 @@ export default ((input: PluginInput) => {
|
|
|
666
705
|
"tool.execute.after"(input: any, output: any) {
|
|
667
706
|
if (ravenTaskCalls.has(input.callID)) {
|
|
668
707
|
ravenTaskCalls.delete(input.callID)
|
|
669
|
-
const
|
|
670
|
-
|
|
708
|
+
const promptBytes = ravenTaskPrompts.get(input.callID) ?? 0
|
|
709
|
+
ravenTaskPrompts.delete(input.callID)
|
|
710
|
+
// Try task metadata first (built-in tools preserve metadata)
|
|
711
|
+
const ravenSessionId = output.metadata?.sessionId ?? ravenSessionParents.get(input.sessionID)
|
|
712
|
+
if (ravenSessionId) {
|
|
713
|
+
if (ravenSessionParents.has(input.sessionID)) ravenSessionParents.delete(input.sessionID)
|
|
714
|
+
void countRavenSessionBytes(ravenSessionId)
|
|
715
|
+
.then((total) => {
|
|
716
|
+
const saved = Math.max(0, total - promptBytes - String(output.output ?? "").length)
|
|
717
|
+
if (saved > 0) addBytes(saved)
|
|
718
|
+
})
|
|
719
|
+
.catch(() => {
|
|
720
|
+
const outputLen = String(output.output ?? "").length
|
|
721
|
+
const saved = Math.max(0, outputLen - promptBytes)
|
|
722
|
+
if (saved > 0) addBytes(saved)
|
|
723
|
+
})
|
|
724
|
+
} else {
|
|
725
|
+
const outputLen = String(output.output ?? "").length
|
|
726
|
+
const saved = Math.max(0, outputLen - promptBytes)
|
|
727
|
+
if (saved > 0) addBytes(saved)
|
|
728
|
+
}
|
|
671
729
|
}
|
|
672
730
|
},
|
|
673
731
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-raven",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
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": {
|