@josxa/opencode-recall
Advanced tools
| You are the recall subagent for OpenCode history. | ||
| Your job is narrow: search past OpenCode sessions, read only the transcript windows needed to answer, and report the result back to the parent agent. You do not inspect the live filesystem, run commands, edit files, browse the web, or verify whether recalled information is still true. If you mention files, commands, decisions, or outcomes, make clear they are facts from past sessions only. | ||
| Available tools: | ||
| - history_search: find ranked cursor anchors in past OpenCode sessions. | ||
| - history_read: read bounded ChatML transcript windows around returned cursors. | ||
| Search strategy: | ||
| - Start with one distinctive token, exact phrase, filename, command, error string, package name, issue id, or domain phrase from the user's request. | ||
| - If the request combines unrelated concepts, split it into multiple searches instead of sending a bag of words. | ||
| - Use directory, after, and before filters when the user gives project or timeframe hints. | ||
| - If a search is thin, retry with a narrower distinctive token from the user request or from promising snippets. | ||
| Read strategy: | ||
| - Read the best 1 to 3 search results first. | ||
| - Start with mode="around" for a specific message cursor, or mode="tail" for a session cursor when the user asks for the final outcome. | ||
| - Page only with cursors returned by history_search or the <nav> element from history_read. Never invent cursor suffixes. | ||
| - Stop as soon as the answer is grounded. Do not read entire sessions by default. | ||
| Output contract: | ||
| - Return one concise answer for the parent agent. | ||
| - Focus on specific facts, decisions, file paths, commands, and outcomes found in the recalled sessions. | ||
| - Cite useful source sessions with session ids, titles, and directories when available. Session ids are useful because the user can resume or inspect those sessions later. | ||
| - Do not report `msg_...` message ids, encoded cursors, or <nav> cursors in the final answer unless the user explicitly asks for raw history_read anchors. Those are internal navigation details for this subagent, not useful final evidence. | ||
| - State explicitly that you only checked past session history and did not verify current files, commands, or runtime state. | ||
| - If the history does not contain the answer, say that directly and mention the strongest searches you tried. | ||
| Follow-up rule: | ||
| If the parent asks for more detail from the same recalled session or another question about the lookup, it should invoke this recall subagent again with the follow-up question. Do not rely on the parent agent's memory of your previous answer for additional historical detail. |
+39
-2
| import { tool } from '@opencode-ai/plugin'; | ||
| import { ChatmlRenderer } from './src/chatml-renderer'; | ||
| import { HISTORY_READ_COMMAND, HISTORY_SEARCH_COMMAND } from './src/commands'; | ||
| import { HISTORY_READ_COMMAND, HISTORY_SEARCH_COMMAND, RECALL_AGENT_DESCRIPTION, RECALL_AGENT_NAME, } from './src/commands'; | ||
| import { decodeCursor } from './src/cursor'; | ||
@@ -16,3 +16,8 @@ import { HistoryDatabase } from './src/db'; | ||
| const MAX_READ_LIMIT = 50; | ||
| const RECALL_AGENT_PROMPT_PATHS = [ | ||
| `${import.meta.dir}/prompts/recall-agent-prompt.txt`, | ||
| `${import.meta.dir}/../prompts/recall-agent-prompt.txt`, | ||
| ]; | ||
| export const RecallPlugin = async () => { | ||
| const recallAgentPrompt = await loadRecallAgentPrompt(); | ||
| return { | ||
@@ -29,2 +34,10 @@ config: async (config) => { | ||
| }; | ||
| config.agent ??= {}; | ||
| const recallAgent = { | ||
| description: RECALL_AGENT_DESCRIPTION, | ||
| mode: 'subagent', | ||
| prompt: recallAgentPrompt, | ||
| permission: recallAgentPermission(), | ||
| }; | ||
| config.agent[RECALL_AGENT_NAME] = recallAgent; | ||
| }, | ||
@@ -46,2 +59,3 @@ tool: { | ||
| async execute(args, context) { | ||
| assertRecallAgent(context.agent); | ||
| const query = args.q ?? ''; | ||
@@ -94,3 +108,4 @@ const includeCurrentSession = args.includeCurrentSession === true; | ||
| }, | ||
| async execute(args) { | ||
| async execute(args, context) { | ||
| assertRecallAgent(context.agent); | ||
| const cursorValue = args.cursor; | ||
@@ -119,2 +134,24 @@ if (cursorValue === undefined || cursorValue.length === 0) { | ||
| }; | ||
| async function loadRecallAgentPrompt() { | ||
| for (const path of RECALL_AGENT_PROMPT_PATHS) { | ||
| const file = Bun.file(path); | ||
| if (await file.exists()) { | ||
| return file.text(); | ||
| } | ||
| } | ||
| throw new Error('Cannot load recall-agent-prompt.txt from package prompts directory'); | ||
| } | ||
| function recallAgentPermission() { | ||
| return { | ||
| '*': 'deny', | ||
| [HISTORY_SEARCH_COMMAND]: 'allow', | ||
| [HISTORY_READ_COMMAND]: 'allow', | ||
| }; | ||
| } | ||
| function assertRecallAgent(agent) { | ||
| if (agent === RECALL_AGENT_NAME) { | ||
| return; | ||
| } | ||
| throw new Error('OpenCode history tools are only available through the @recall subagent.'); | ||
| } | ||
| function requiredSessionId(value) { | ||
@@ -121,0 +158,0 @@ if (value !== undefined) { |
| export declare const HISTORY_SEARCH_COMMAND = "history_search"; | ||
| export declare const HISTORY_READ_COMMAND = "history_read"; | ||
| export declare const RECALL_AGENT_NAME = "recall"; | ||
| export declare const RECALL_AGENT_DESCRIPTION = "Past OpenCode session recall. Source-grounded. Sole history-tool path; starts out with fresh context window. **Reinvoke** subagent for follow-ups/detail."; | ||
| export declare function commandNames(): readonly string[]; |
| export const HISTORY_SEARCH_COMMAND = 'history_search'; | ||
| export const HISTORY_READ_COMMAND = 'history_read'; | ||
| export const RECALL_AGENT_NAME = 'recall'; | ||
| export const RECALL_AGENT_DESCRIPTION = 'Past OpenCode session recall. Source-grounded. Sole history-tool path; starts out with fresh context window. **Reinvoke** subagent for follow-ups/detail.'; | ||
| export function commandNames() { | ||
| return [HISTORY_SEARCH_COMMAND, HISTORY_READ_COMMAND]; | ||
| } |
+2
-1
| { | ||
| "name": "@josxa/opencode-recall", | ||
| "version": "0.2.0", | ||
| "version": "0.3.0", | ||
| "description": "OpenCode plugin that adds semantic + lexical recall over your local session history, with cursor-paginated ChatML transcript reads.", | ||
@@ -62,2 +62,3 @@ "type": "module", | ||
| "dist", | ||
| "prompts", | ||
| "schema" | ||
@@ -64,0 +65,0 @@ ], |
+15
-3
@@ -102,4 +102,16 @@ # @josxa/opencode-recall | ||
| Recall is explicit. The agent searches when you ask it to. Some prompts that work well: | ||
| Recall is explicit. Ask the dedicated subagent when you want old session context: | ||
| ```text | ||
| @recall how did we set up trusted publishing last time? | ||
| @recall find the session where we debugged recall lookup accuracy | ||
| @recall what did we decide about the rate limiter design? | ||
| ``` | ||
| The `recall` subagent starts with fresh context, searches and reads past sessions, then returns only a concise answer to the parent agent. It reports useful source session ids, titles, and directories, but not internal `msg_...` cursors unless you explicitly ask for raw read anchors. That gives you the useful part of session-level querying without dumping raw transcript pages into the main conversation. If you ask a follow-up about the same recalled session, the parent should invoke `@recall` again instead of answering from memory. | ||
| The subagent only checks OpenCode history. It does not read current files, run commands, or verify whether old session facts are still true. | ||
| Some natural-language prompts that should trigger recall: | ||
| - *"Recall how we set up the GitHub Actions release workflow."* | ||
@@ -111,3 +123,3 @@ - *"Did we ever debug the Postgres connection pool exhaustion? Find that conversation."* | ||
| The agent runs `history_search`, picks a promising hit, calls `history_read` to load the surrounding messages, and can page forward or back if more context is needed. | ||
| The recall subagent runs `history_search`, picks a promising hit, calls `history_read` to load the surrounding messages, and can page forward or back if more context is needed. | ||
@@ -158,3 +170,3 @@ ## Configuration | ||
| Recall exposes two tools to the agent. | ||
| Recall exposes two history tools. They are intended to be called by the `recall` subagent, not by the main agent directly. | ||
@@ -161,0 +173,0 @@ <details> |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
129412
4.45%33
3.13%2861
1.45%266
4.72%