opencode-raven 2.1.2 → 2.1.4
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 +162 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -188,7 +188,7 @@ Edit config manually or use `/raven` commands:
|
|
|
188
188
|
},
|
|
189
189
|
"excludeAgents": [],
|
|
190
190
|
"excludeTools": [],
|
|
191
|
-
"timeout":
|
|
191
|
+
"timeout": 600
|
|
192
192
|
}
|
|
193
193
|
```
|
|
194
194
|
|
|
@@ -204,7 +204,7 @@ Edit config manually or use `/raven` commands:
|
|
|
204
204
|
| `onDemandMcpServers` | Context7, Exa, Grep.app | MCPs Raven connects to internally on demand, without registering full schemas in OpenCode global MCP config. Supports OpenCode-style `remote` and `local` entries. Each server supports `enabled`, defaulting to `true`. |
|
|
205
205
|
| `excludeAgents` | `[]` | Agents that bypass Raven routing, case-insensitive. |
|
|
206
206
|
| `excludeTools` | `[]` | Exact tools that never get blocked, even if matched by auto-routed global MCP prefixes. |
|
|
207
|
-
| `timeout` | `
|
|
207
|
+
| `timeout` | `600` | Max seconds for a `raven_seek` call. Provider/API retry errors and no-initial-response startup failures are reported quickly; after provider failures, routed tools tell the user how to switch Raven's model instead of retrying Raven. On timeout the session is kept for inspection. |
|
|
208
208
|
| `stats` | auto | Session and global estimated context saved by Raven. Managed automatically. |
|
|
209
209
|
|
|
210
210
|
Use `ravenInstructions` for extra Raven-only guidance, such as how to use custom MCPs:
|
|
@@ -285,10 +285,10 @@ MCPs configured directly in OpenCode `opencode.jsonc` still load globally and ma
|
|
|
285
285
|
| `chat.message` | Tracks agent to session mapping for allowlist and Raven exclusion. |
|
|
286
286
|
| `event` | Shows startup update and MCP failure notifications after the TUI event stream is ready. |
|
|
287
287
|
| `command.execute.before` | Handles `/raven help`, routing commands, MCP commands, update checks, model settings, timeout, and stats. |
|
|
288
|
-
| `tool.execute.before` | Hard-blocks configured tools/MCPs for non-Raven, non-excluded agents. Error output gives the next `raven_seek(query="...")` call
|
|
288
|
+
| `tool.execute.before` | Hard-blocks configured tools/MCPs for non-Raven, non-excluded agents. Error output gives the next `raven_seek(query="...")` call or recovery instructions if Raven's provider is unavailable. Tracks direct Raven subagent calls for stats. |
|
|
289
289
|
| `tool.execute.after` | Tracks direct `@Raven` calls for context-saved stats. |
|
|
290
290
|
|
|
291
|
-
|
|
291
|
+
Raven routing guidance is loaded through OpenCode instructions at startup. Subagent prompts are not modified; if a non-Raven agent calls a routed tool, Raven blocks it and returns the exact `raven_seek(query="...")` call to use.
|
|
292
292
|
|
|
293
293
|
## Agent Capabilities
|
|
294
294
|
|
package/index.ts
CHANGED
|
@@ -203,7 +203,7 @@ const DEFAULT_CONFIG: RavenConfig = {
|
|
|
203
203
|
onDemandMcpServers: DEFAULT_ON_DEMAND_MCP_SERVERS,
|
|
204
204
|
excludeAgents: [],
|
|
205
205
|
excludeTools: [],
|
|
206
|
-
timeout:
|
|
206
|
+
timeout: 600,
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
function parseRavenMd(raw: string): { frontmatter: Record<string, any>; prompt: string } {
|
|
@@ -494,6 +494,8 @@ export default ((input: PluginInput) => {
|
|
|
494
494
|
const ravenTaskCalls = new Set<string>()
|
|
495
495
|
const sessionAgents = new Map<string, string>()
|
|
496
496
|
const ravenSessionParents = new Map<string, string>()
|
|
497
|
+
const ravenErrorWaiters = new Map<string, (error: Error) => void>()
|
|
498
|
+
const ravenProgressWaiters = new Map<string, () => void>()
|
|
497
499
|
let globalMcpServers: string[] = []
|
|
498
500
|
let duplicateMcpServers: string[] = []
|
|
499
501
|
let updateInfo: { current: string; latest?: string; available: boolean } | undefined
|
|
@@ -504,6 +506,7 @@ export default ((input: PluginInput) => {
|
|
|
504
506
|
let mcpRefreshScheduled = false
|
|
505
507
|
let mcpRefreshPromise: Promise<{ refreshed: string[]; failed: string[] }> | undefined
|
|
506
508
|
let lastNotifiedMcpFailureKey = ""
|
|
509
|
+
let ravenProviderIssue: string | undefined
|
|
507
510
|
|
|
508
511
|
resetOnDemandMcpRuntimeStatuses()
|
|
509
512
|
|
|
@@ -528,13 +531,6 @@ export default ((input: PluginInput) => {
|
|
|
528
531
|
return config.excludeAgents.some((a) => a.toLowerCase() === lower)
|
|
529
532
|
}
|
|
530
533
|
|
|
531
|
-
function ravenGuidance(): string {
|
|
532
|
-
const tools = config.routeTools?.length ? config.routeTools.join(", ") : "none"
|
|
533
|
-
const mcps = globalMcpServers.length ? globalMcpServers.map((server) => `${server}_*`).join(", ") : "none"
|
|
534
|
-
const keywords = config.routeToolKeywords?.length ? config.routeToolKeywords.join(", ") : "none"
|
|
535
|
-
return `Some tools/MCPs are routed through Raven to save context. Routed tools: ${tools}. Auto-routed global MCP prefixes: ${mcps}. Routed tool-name keywords: ${keywords}. ${onDemandMcpGuidance("delegate")} If one is blocked, your next tool call should be raven_seek(query="<same request>"). Include the original tool/MCP name and relevant arguments.`
|
|
536
|
-
}
|
|
537
|
-
|
|
538
534
|
function isRouteConfigured(toolName: string): boolean {
|
|
539
535
|
const tool = toolName.toLowerCase()
|
|
540
536
|
if (NEVER_ROUTE_TOOLS.has(tool)) return false
|
|
@@ -570,9 +566,26 @@ export default ((input: PluginInput) => {
|
|
|
570
566
|
}
|
|
571
567
|
|
|
572
568
|
function rerouteMessage(tool: string, args: any): string {
|
|
569
|
+
if (ravenProviderIssue) {
|
|
570
|
+
return `Raven is currently unavailable: ${ravenProviderIssue}
|
|
571
|
+
|
|
572
|
+
${ravenRecoveryInstructions()}`
|
|
573
|
+
}
|
|
573
574
|
return `The '${tool}' tool call is blocked by Raven. Your next tool call should be raven_seek(query="${attemptedQuery(tool, args).replace(/"/g, "'")}").`
|
|
574
575
|
}
|
|
575
576
|
|
|
577
|
+
function ravenRecoveryInstructions(): string {
|
|
578
|
+
return `Stop trying Raven-routed tools or MCPs for this request. Inform the user that Raven cannot run until its model/provider is changed or fixed.
|
|
579
|
+
|
|
580
|
+
Tell the user to switch Raven's model with:
|
|
581
|
+
/raven model <provider/model>
|
|
582
|
+
|
|
583
|
+
Example:
|
|
584
|
+
/raven model openai/gpt-5.5
|
|
585
|
+
|
|
586
|
+
Then restart opencode for the model change to take effect. If the task can be completed without Raven, continue without routed tools and clearly mention what could not be checked.`
|
|
587
|
+
}
|
|
588
|
+
|
|
576
589
|
function routeSummary(): string {
|
|
577
590
|
const tools = config.routeTools?.length ? config.routeTools.join(", ") : "(none)"
|
|
578
591
|
const mcps = globalMcpServers.length ? `${globalMcpServers.join(", ")} (auto-detected)` : "none detected"
|
|
@@ -673,6 +686,49 @@ export default ((input: PluginInput) => {
|
|
|
673
686
|
}
|
|
674
687
|
}
|
|
675
688
|
|
|
689
|
+
function providerErrorMessage(error: any): string {
|
|
690
|
+
const name = String(error?.name ?? "")
|
|
691
|
+
const data = error?.data && typeof error.data === "object" ? error.data : {}
|
|
692
|
+
const status = typeof data.statusCode === "number" ? ` HTTP ${data.statusCode}.` : ""
|
|
693
|
+
const body = typeof data.responseBody === "string" && data.responseBody.trim() ? ` ${truncateText(data.responseBody, 500)}` : ""
|
|
694
|
+
const message = String(data.message ?? error?.message ?? error ?? "Unknown provider error")
|
|
695
|
+
return `Raven provider/API error${name ? ` (${name})` : ""}.${status} ${message}${body}`.trim()
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function waitForRavenProviderError(sessionId: string): Promise<never> {
|
|
699
|
+
return new Promise((_resolve, reject) => {
|
|
700
|
+
ravenErrorWaiters.set(sessionId, reject)
|
|
701
|
+
})
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function waitForRavenFirstProgress(sessionId: string, ms: number): Promise<never> {
|
|
705
|
+
return new Promise((_resolve, reject) => {
|
|
706
|
+
const timer = setTimeout(() => {
|
|
707
|
+
ravenProgressWaiters.delete(sessionId)
|
|
708
|
+
reject(new Error(`Raven did not produce an initial response within ${Math.round(ms / 1000)}s. If the Raven provider showed a retry dialog, this is likely an auth/quota/subscription issue. Check the Raven subagent/provider account, or retry after switching Raven to a working model.`))
|
|
709
|
+
}, ms)
|
|
710
|
+
ravenProgressWaiters.set(sessionId, () => {
|
|
711
|
+
clearTimeout(timer)
|
|
712
|
+
ravenProgressWaiters.delete(sessionId)
|
|
713
|
+
})
|
|
714
|
+
})
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function reportRavenProviderError(sessionId: string, error: any) {
|
|
718
|
+
const waiter = ravenErrorWaiters.get(sessionId)
|
|
719
|
+
if (!waiter) return
|
|
720
|
+
waiter(new Error(providerErrorMessage(error)))
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function reportRavenProgress(sessionId: string) {
|
|
724
|
+
ravenProgressWaiters.get(sessionId)?.()
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function clearRavenWaiters(sessionId: string) {
|
|
728
|
+
ravenErrorWaiters.delete(sessionId)
|
|
729
|
+
ravenProgressWaiters.get(sessionId)?.()
|
|
730
|
+
}
|
|
731
|
+
|
|
676
732
|
type McpConnection = {
|
|
677
733
|
client: Client
|
|
678
734
|
transport: any
|
|
@@ -1238,7 +1294,7 @@ export default ((input: PluginInput) => {
|
|
|
1238
1294
|
},
|
|
1239
1295
|
async execute(args, context) {
|
|
1240
1296
|
const started = Date.now()
|
|
1241
|
-
const timeout = (config.timeout ??
|
|
1297
|
+
const timeout = (config.timeout ?? 600) * 1000
|
|
1242
1298
|
try {
|
|
1243
1299
|
// Create a Raven session
|
|
1244
1300
|
const session = await client.session.create({
|
|
@@ -1266,55 +1322,76 @@ export default ((input: PluginInput) => {
|
|
|
1266
1322
|
appendFileSync(logFile, `${ts} ${sessionId} "${q}"\n`)
|
|
1267
1323
|
} catch { /* non-fatal */ }
|
|
1268
1324
|
|
|
1269
|
-
// Send the query to Raven with timeout
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
parts: [{ type: "text", text: args.query }],
|
|
1276
|
-
},
|
|
1277
|
-
}),
|
|
1278
|
-
new Promise<never>((_, reject) =>
|
|
1279
|
-
setTimeout(() => reject(new Error(`Raven timed out after ${timeout / 1000}s — session kept: ${sessionId}`)), timeout)
|
|
1280
|
-
),
|
|
1281
|
-
])
|
|
1282
|
-
|
|
1283
|
-
const elapsed = ((Date.now() - started) / 1000).toFixed(1)
|
|
1284
|
-
|
|
1285
|
-
// Extract text from the response
|
|
1286
|
-
const parts = (result as any)?.data?.parts ?? []
|
|
1287
|
-
const textParts = parts
|
|
1288
|
-
.filter((p: any) => p.type === "text" && p.text)
|
|
1289
|
-
.map((p: any) => p.text)
|
|
1290
|
-
const output = textParts.join("\n") || "Raven returned no results."
|
|
1291
|
-
|
|
1292
|
-
// Context saved = Raven output/reasoning/cache context - compact answer returned to main session.
|
|
1293
|
-
let savedCandidate = 0
|
|
1325
|
+
// Send the query to Raven with timeout. Provider/API errors are also
|
|
1326
|
+
// surfaced through message.updated events; fail as soon as one lands
|
|
1327
|
+
// so the caller does not wait for the full timeout on quota/auth issues.
|
|
1328
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
1329
|
+
const providerError = waitForRavenProviderError(sessionId)
|
|
1330
|
+
const firstProgress = waitForRavenFirstProgress(sessionId, Math.min(timeout, 30_000))
|
|
1294
1331
|
try {
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1332
|
+
const result = await Promise.race([
|
|
1333
|
+
client.session.prompt({
|
|
1334
|
+
path: { id: sessionId },
|
|
1335
|
+
body: {
|
|
1336
|
+
agent: "raven",
|
|
1337
|
+
parts: [{ type: "text", text: args.query }],
|
|
1338
|
+
},
|
|
1339
|
+
}),
|
|
1340
|
+
providerError,
|
|
1341
|
+
firstProgress,
|
|
1342
|
+
new Promise<never>((_, reject) => {
|
|
1343
|
+
timer = setTimeout(() => reject(new Error(`Raven timed out after ${timeout / 1000}s — session kept: ${sessionId}`)), timeout)
|
|
1344
|
+
}),
|
|
1345
|
+
])
|
|
1346
|
+
if (timer) clearTimeout(timer)
|
|
1347
|
+
clearRavenWaiters(sessionId)
|
|
1348
|
+
|
|
1349
|
+
const elapsed = ((Date.now() - started) / 1000).toFixed(1)
|
|
1350
|
+
|
|
1351
|
+
// Extract text from the response
|
|
1352
|
+
const parts = (result as any)?.data?.parts ?? []
|
|
1353
|
+
const textParts = parts
|
|
1354
|
+
.filter((p: any) => p.type === "text" && p.text)
|
|
1355
|
+
.map((p: any) => p.text)
|
|
1356
|
+
const output = textParts.join("\n") || "Raven returned no results."
|
|
1357
|
+
|
|
1358
|
+
// Context saved = Raven output/reasoning/cache context - compact answer returned to main session.
|
|
1359
|
+
let savedCandidate = 0
|
|
1360
|
+
try {
|
|
1361
|
+
savedCandidate = await countRavenSavedCandidateBytes(sessionId)
|
|
1362
|
+
} catch { /* best-effort */ }
|
|
1363
|
+
if (savedCandidate <= 0) {
|
|
1364
|
+
for (const part of parts) {
|
|
1365
|
+
if (part.args) savedCandidate += JSON.stringify(part.args).length
|
|
1366
|
+
if (part.content) savedCandidate += typeof part.content === "string" ? part.content.length : JSON.stringify(part.content).length
|
|
1367
|
+
}
|
|
1301
1368
|
}
|
|
1369
|
+
const saved = Math.max(0, savedCandidate - output.length)
|
|
1370
|
+
addBytes(saved)
|
|
1371
|
+
ravenProviderIssue = undefined
|
|
1372
|
+
|
|
1373
|
+
return { title: "Raven Seek", metadata: { sessionId }, output: `${output}\n\n*Raven searched for ${elapsed}s — ${formatBytes(savedCandidate)} handled, ${formatTokens(savedCandidate)} tokens*` }
|
|
1374
|
+
} finally {
|
|
1375
|
+
if (timer) clearTimeout(timer)
|
|
1376
|
+
clearRavenWaiters(sessionId)
|
|
1302
1377
|
}
|
|
1303
|
-
const saved = Math.max(0, savedCandidate - output.length)
|
|
1304
|
-
addBytes(saved)
|
|
1305
|
-
|
|
1306
|
-
return { title: "Raven Seek", metadata: { sessionId }, output: `${output}\n\n*Raven searched for ${elapsed}s — ${formatBytes(savedCandidate)} handled, ${formatTokens(savedCandidate)} tokens*` }
|
|
1307
1378
|
} catch (err: any) {
|
|
1308
1379
|
const elapsed = ((Date.now() - started) / 1000).toFixed(1)
|
|
1309
1380
|
const msg = String(err?.message ?? err ?? "").toLowerCase()
|
|
1310
1381
|
const hint =
|
|
1311
1382
|
/rate.?limit|too many requests|429/i.test(msg) ? "Raven rate limited — wait 30s then retry with a narrower query."
|
|
1312
|
-
: /quota|usage.?limit|billing|insufficient.*(?:credit|balance|quota)/i.test(msg) ? "Raven API quota exhausted — proceed without
|
|
1383
|
+
: /quota|usage.?limit|usage exceeded|free usage|subscribe to go|subscription|billing|insufficient.*(?:credit|balance|quota)/i.test(msg) ? "Raven API quota/subscription exhausted — proceed without Raven, tell the user Raven could not run, and mention what information may be missing."
|
|
1384
|
+
: /auth|unauthorized|forbidden|401|403|api key|login/i.test(msg) ? "Raven provider authentication failed — proceed without Raven and tell the user to check the Raven model/provider login or API key."
|
|
1313
1385
|
: /token|context.?length|too large|too long/i.test(msg) ? "Raven query too large — shorten your query and retry."
|
|
1314
1386
|
: /model|unavailable|down|not found/i.test(msg) ? "Raven model unavailable — retry later, or proceed without search."
|
|
1387
|
+
: /did not produce an initial response|retry dialog/i.test(msg) ? err.message
|
|
1315
1388
|
: /timeout|timed.?out|session kept/i.test(msg) ? err.message
|
|
1316
1389
|
: `Raven search failed. Proceed without search — note gaps for the user. [${err.message || err}]`
|
|
1317
|
-
|
|
1390
|
+
if (/quota|usage.?limit|usage exceeded|free usage|subscribe to go|subscription|billing|auth|unauthorized|forbidden|401|403|api key|login|model|unavailable|down|not found|did not produce an initial response|retry dialog/i.test(msg)) {
|
|
1391
|
+
ravenProviderIssue = hint
|
|
1392
|
+
}
|
|
1393
|
+
const recovery = ravenProviderIssue ? `\n\n${ravenRecoveryInstructions()}` : ""
|
|
1394
|
+
return { title: "Raven Seek", output: `${hint}${recovery}\n\n*Attempt took ${elapsed}s*` }
|
|
1318
1395
|
}
|
|
1319
1396
|
},
|
|
1320
1397
|
}),
|
|
@@ -1332,7 +1409,7 @@ export default ((input: PluginInput) => {
|
|
|
1332
1409
|
}
|
|
1333
1410
|
|
|
1334
1411
|
const operation = args.operation as RavenMcpOperation
|
|
1335
|
-
const timeout = (config.timeout ??
|
|
1412
|
+
const timeout = (config.timeout ?? 600) * 1000
|
|
1336
1413
|
let connected = false
|
|
1337
1414
|
try {
|
|
1338
1415
|
const connection = await getMcpConnection(context.sessionID, args.server)
|
|
@@ -1415,6 +1492,37 @@ export default ((input: PluginInput) => {
|
|
|
1415
1492
|
if (evt?.type === "session.created" && evt?.properties?.parentID) {
|
|
1416
1493
|
ravenSessionParents.set(evt.properties.parentID, evt.properties.id)
|
|
1417
1494
|
}
|
|
1495
|
+
if (evt?.type === "message.updated") {
|
|
1496
|
+
const info = evt.properties?.info
|
|
1497
|
+
if (info?.role === "assistant" && info?.sessionID && info.error && ravenSessions.has(info.sessionID)) {
|
|
1498
|
+
reportRavenProviderError(info.sessionID, info.error)
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
if (evt?.type === "message.part.updated") {
|
|
1502
|
+
const part = evt.properties?.part
|
|
1503
|
+
if (part?.sessionID && ravenSessions.has(part.sessionID)) {
|
|
1504
|
+
if (part.type === "retry" && part.error) {
|
|
1505
|
+
reportRavenProviderError(part.sessionID, part.error)
|
|
1506
|
+
} else {
|
|
1507
|
+
reportRavenProgress(part.sessionID)
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
if (evt?.type === "session.status") {
|
|
1512
|
+
const sessionId = evt.properties?.sessionID
|
|
1513
|
+
const status = evt.properties?.status
|
|
1514
|
+
if (sessionId && ravenSessions.has(sessionId)) {
|
|
1515
|
+
if (status?.type === "retry") {
|
|
1516
|
+
reportRavenProviderError(sessionId, { name: "RetryError", data: { message: status.message } })
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
if (evt?.type === "session.error") {
|
|
1521
|
+
const sessionId = evt.properties?.sessionID
|
|
1522
|
+
if (sessionId && evt.properties?.error && ravenSessions.has(sessionId)) {
|
|
1523
|
+
reportRavenProviderError(sessionId, evt.properties.error)
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1418
1526
|
|
|
1419
1527
|
if (updateToastPending) {
|
|
1420
1528
|
updateToastPending = false
|
|
@@ -1515,6 +1623,7 @@ export default ((input: PluginInput) => {
|
|
|
1515
1623
|
} else {
|
|
1516
1624
|
config.model = model
|
|
1517
1625
|
saveConfig(config)
|
|
1626
|
+
ravenProviderIssue = undefined
|
|
1518
1627
|
output.parts.push({ type: "text", text: `Raven model set to: ${model}\nRestart opencode for the change to take effect.` })
|
|
1519
1628
|
}
|
|
1520
1629
|
} else if (arg.startsWith("effort ")) {
|
|
@@ -1529,7 +1638,7 @@ export default ((input: PluginInput) => {
|
|
|
1529
1638
|
} else if (arg.startsWith("timeout ")) {
|
|
1530
1639
|
const secs = parseInt(raw.slice(8).trim(), 10)
|
|
1531
1640
|
if (!secs || secs < 10) {
|
|
1532
|
-
output.parts.push({ type: "text", text: `Usage: /raven timeout <seconds>\nMust be at least 10. Current: ${config.timeout ??
|
|
1641
|
+
output.parts.push({ type: "text", text: `Usage: /raven timeout <seconds>\nMust be at least 10. Current: ${config.timeout ?? 600}s` })
|
|
1533
1642
|
} else {
|
|
1534
1643
|
config.timeout = secs
|
|
1535
1644
|
saveConfig(config)
|
|
@@ -1539,7 +1648,8 @@ export default ((input: PluginInput) => {
|
|
|
1539
1648
|
const enabled = config.enabled ? "enabled" : "disabled"
|
|
1540
1649
|
const model = config.model || fm.model || "(default)"
|
|
1541
1650
|
const effort = config.reasoning_effort || fm.reasoning_effort || "(default)"
|
|
1542
|
-
const timeout = config.timeout ??
|
|
1651
|
+
const timeout = config.timeout ?? 600
|
|
1652
|
+
const providerIssue = ravenProviderIssue ? `\nProvider issue: ${ravenProviderIssue}` : ""
|
|
1543
1653
|
let update = "Update: unable to check npm."
|
|
1544
1654
|
try {
|
|
1545
1655
|
const info = await getUpdateInfo()
|
|
@@ -1547,7 +1657,7 @@ export default ((input: PluginInput) => {
|
|
|
1547
1657
|
? `Update: ${info.latest} available. Run /raven update, then restart opencode.`
|
|
1548
1658
|
: `Update: up to date${info.latest ? ` (latest ${info.latest})` : ""}.`
|
|
1549
1659
|
} catch { /* keep fallback */ }
|
|
1550
|
-
output.parts.push({ type: "text", text: `Raven is ${enabled}. Version: ${PACKAGE_VERSION}. Model: ${model}. Reasoning: ${effort}. Timeout: ${timeout}s\n${update}\n\n${routeSummary()}\n\n${mcpSummary()}\n\n${statsText()}\n\nRun /raven help for commands.` })
|
|
1660
|
+
output.parts.push({ type: "text", text: `Raven is ${enabled}. Version: ${PACKAGE_VERSION}. Model: ${model}. Reasoning: ${effort}. Timeout: ${timeout}s${providerIssue}\n${update}\n\n${routeSummary()}\n\n${mcpSummary()}\n\n${statsText()}\n\nRun /raven help for commands.` })
|
|
1551
1661
|
}
|
|
1552
1662
|
},
|
|
1553
1663
|
|
|
@@ -1565,18 +1675,8 @@ export default ((input: PluginInput) => {
|
|
|
1565
1675
|
if (isExcluded(sessionAgents.get(input.sessionID))) return
|
|
1566
1676
|
if (config.excludeTools?.some((name) => name.toLowerCase() === input.tool.toLowerCase())) return
|
|
1567
1677
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
const subagentType = input.tool === "task" ? (output.args.subagent_type ?? "") : ""
|
|
1571
|
-
if (subagentType === "raven") {
|
|
1572
|
-
ravenTaskCalls.add(input.callID)
|
|
1573
|
-
}
|
|
1574
|
-
if (subagentType !== "raven" && !isExcluded(subagentType)) {
|
|
1575
|
-
const field = ["prompt", "description", "request", "objective", "query"].find(
|
|
1576
|
-
(f) => f in output.args
|
|
1577
|
-
) ?? "prompt"
|
|
1578
|
-
output.args[field] = `${output.args[field] ?? ""}\n\n<raven_guidance>\n${ravenGuidance()}\n</raven_guidance>`
|
|
1579
|
-
}
|
|
1678
|
+
if (input.tool === "task" && output.args?.subagent_type === "raven") {
|
|
1679
|
+
ravenTaskCalls.add(input.callID)
|
|
1580
1680
|
}
|
|
1581
1681
|
|
|
1582
1682
|
// ── Block routed tools/MCPs for non-Raven agents ──
|
package/package.json
CHANGED