opencode-session-recall 0.3.0 → 0.5.0
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/LICENSE +0 -2
- package/README.md +6 -4
- package/dist/context.d.ts +2 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/extract.d.ts +1 -1
- package/dist/extract.d.ts.map +1 -1
- package/dist/get.d.ts.map +1 -1
- package/dist/messages.d.ts +2 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/opencode-session-recall.d.ts.map +1 -1
- package/dist/opencode-session-recall.js +98 -43
- package/dist/search.d.ts +2 -1
- package/dist/search.d.ts.map +1 -1
- package/dist/sessions.d.ts +2 -1
- package/dist/sessions.d.ts.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -2
package/LICENSE
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 maelos
|
|
4
|
-
|
|
5
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
4
|
of this software and associated documentation files (the "Software"), to deal
|
|
7
5
|
in the Software without restriction, including without limitation the rights
|
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# opencode-session-recall
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A plugin for [opencode](https://github.com/opencode-ai/opencode) that gives your agent a memory that survives compaction — without building another memory system.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
opencode is an open-source AI coding agent that runs in your terminal. It manages long conversations through compaction: summarizing older context to keep the active window focused. But compaction means the agent forgets — original tool outputs, earlier reasoning, the user's exact words.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This plugin adds five tools to the agent's toolkit that let it search and retrieve that lost context on demand, within the current session, across all sessions in the project, or across every project on the machine.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**It doesn't create a separate memory store.** Most agent "memory" solutions add vector databases, embedding pipelines, or knowledge graphs — duplicating your data into yet another system. `opencode-session-recall` does none of that. opencode already stores every message, every tool output, every reasoning trace in its database, even after compaction prunes them from context. This plugin simply gives the agent access to what's already there.
|
|
10
|
+
|
|
11
|
+
No embeddings. No vector store. No data duplication. No setup. Just install the plugin and the agent can remember.
|
|
10
12
|
|
|
11
13
|
## What this enables
|
|
12
14
|
|
package/dist/context.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ToolDefinition } from "@opencode-ai/plugin";
|
|
2
2
|
import type { OpencodeClient } from "@opencode-ai/sdk/v2";
|
|
3
|
-
|
|
3
|
+
import { type Limits } from "./types.js";
|
|
4
|
+
export declare function context(client: OpencodeClient, limits: Limits): ToolDefinition;
|
|
4
5
|
//# sourceMappingURL=context.d.ts.map
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAIL,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AAGpB,wBAAgB,OAAO,CACrB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,GACb,cAAc,CA2GhB"}
|
package/dist/extract.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Part, Message } from "@opencode-ai/sdk/v2";
|
|
2
|
-
import type
|
|
2
|
+
import { type PartOutput, type MessageItem } from "./types.js";
|
|
3
3
|
export declare function matches(text: string, query: string): boolean;
|
|
4
4
|
export declare function searchable(part: Part): string[];
|
|
5
5
|
export declare function snippet(text: string, query: string, width?: number): string;
|
package/dist/extract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EAEJ,OAAO,EAGR,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EAEJ,OAAO,EAGR,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAS,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAYtE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CA4B/C;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,MAAM,CAexE;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAI1C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,UAAU,CA0D7C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;CACpB,GAAG,WAAW,CAgBd"}
|
package/dist/get.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../src/get.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,wBAAgB,GAAG,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../src/get.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,wBAAgB,GAAG,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAkE1D"}
|
package/dist/messages.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ToolDefinition } from "@opencode-ai/plugin";
|
|
2
2
|
import type { OpencodeClient } from "@opencode-ai/sdk/v2";
|
|
3
|
-
|
|
3
|
+
import { type Limits } from "./types.js";
|
|
4
|
+
export declare function messages(client: OpencodeClient, limits: Limits): ToolDefinition;
|
|
4
5
|
//# sourceMappingURL=messages.d.ts.map
|
package/dist/messages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAIL,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AAYpB,wBAAgB,QAAQ,CACtB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,GACb,cAAc,CAgHhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode-session-recall.d.ts","sourceRoot":"","sources":["../src/opencode-session-recall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;;;;;
|
|
1
|
+
{"version":3,"file":"opencode-session-recall.d.ts","sourceRoot":"","sources":["../src/opencode-session-recall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;;;;;AAmFlD,wBAGE"}
|
|
@@ -7,6 +7,22 @@ import {
|
|
|
7
7
|
} from "@opencode-ai/plugin";
|
|
8
8
|
|
|
9
9
|
// src/types.ts
|
|
10
|
+
var TOOLS = [
|
|
11
|
+
"recall",
|
|
12
|
+
"recall_get",
|
|
13
|
+
"recall_sessions",
|
|
14
|
+
"recall_context",
|
|
15
|
+
"recall_messages"
|
|
16
|
+
];
|
|
17
|
+
var DEFAULTS = {
|
|
18
|
+
concurrency: 3,
|
|
19
|
+
maxSessions: 50,
|
|
20
|
+
maxResults: 50,
|
|
21
|
+
maxSessionList: 100,
|
|
22
|
+
maxMessages: 50,
|
|
23
|
+
maxWindow: 10,
|
|
24
|
+
defaultWidth: 200
|
|
25
|
+
};
|
|
10
26
|
function errmsg(e) {
|
|
11
27
|
if (e instanceof Error) return e.message;
|
|
12
28
|
if (typeof e === "string") return e;
|
|
@@ -22,13 +38,17 @@ function errmsg(e) {
|
|
|
22
38
|
}
|
|
23
39
|
|
|
24
40
|
// src/sessions.ts
|
|
25
|
-
function sessions(client, unscoped, global) {
|
|
41
|
+
function sessions(client, unscoped, global, limits) {
|
|
26
42
|
return tool({
|
|
27
|
-
description: `List sessions from the opencode database. Use this FIRST to discover which sessions exist, then search their content with recall. Returns session titles, directories, and timestamps. For cross-project discovery, use scope "global" (requires plugin option global: true)
|
|
43
|
+
description: `List sessions from the opencode database. Use this FIRST to discover which sessions exist, then search their content with recall. Returns session titles, directories, and timestamps. For cross-project discovery, use scope "global" (requires plugin option global: true).
|
|
44
|
+
|
|
45
|
+
Search filters by session title only (case-insensitive substring match \u2014 use recall for content search). Sessions are returned newest-updated first. This is a cheap metadata-only call.
|
|
46
|
+
|
|
47
|
+
Returns { ok, sessions: [{ id, title, directory, time, archived }], returned, scope }. All tools return JSON with ok: true on success or ok: false with error on failure.`,
|
|
28
48
|
args: {
|
|
29
49
|
scope: tool.schema.enum(["project", "global"]).default("project").describe("project = current project, global = all projects"),
|
|
30
|
-
search: tool.schema.string().optional().describe("
|
|
31
|
-
limit: tool.schema.number().min(1).max(
|
|
50
|
+
search: tool.schema.string().optional().describe("Case-insensitive substring match on session title"),
|
|
51
|
+
limit: tool.schema.number().min(1).max(limits.maxSessionList).default(Math.min(20, limits.maxSessionList)).describe("Max sessions to return (newest first)")
|
|
32
52
|
},
|
|
33
53
|
async execute(args, ctx) {
|
|
34
54
|
ctx.metadata({
|
|
@@ -116,6 +136,7 @@ import {
|
|
|
116
136
|
|
|
117
137
|
// src/extract.ts
|
|
118
138
|
var INPUT_SEARCH_LIMIT = 1e4;
|
|
139
|
+
var SELF = new Set(TOOLS);
|
|
119
140
|
function input(val) {
|
|
120
141
|
const raw = JSON.stringify(val);
|
|
121
142
|
return raw.length > INPUT_SEARCH_LIMIT ? raw.slice(0, INPUT_SEARCH_LIMIT) : raw;
|
|
@@ -124,6 +145,7 @@ function matches(text, query) {
|
|
|
124
145
|
return text.toLowerCase().includes(query.toLowerCase());
|
|
125
146
|
}
|
|
126
147
|
function searchable(part) {
|
|
148
|
+
if (part.type === "tool" && SELF.has(part.tool)) return [];
|
|
127
149
|
switch (part.type) {
|
|
128
150
|
case "text":
|
|
129
151
|
case "reasoning":
|
|
@@ -158,7 +180,7 @@ function snippet(text, query, width = 200) {
|
|
|
158
180
|
return text.slice(0, width) + (text.length > width ? "..." : "");
|
|
159
181
|
const half = Math.floor(width / 2);
|
|
160
182
|
let start = Math.max(0, idx - half);
|
|
161
|
-
|
|
183
|
+
const end = Math.min(text.length, start + width);
|
|
162
184
|
if (end - start < width && start > 0) start = Math.max(0, end - width);
|
|
163
185
|
let result = text.slice(start, end);
|
|
164
186
|
if (start > 0) result = "..." + result;
|
|
@@ -246,7 +268,6 @@ function formatMsg(msg) {
|
|
|
246
268
|
}
|
|
247
269
|
|
|
248
270
|
// src/search.ts
|
|
249
|
-
var CONCURRENCY = 3;
|
|
250
271
|
function meta(s) {
|
|
251
272
|
return { id: s.id, title: s.title, directory: s.directory };
|
|
252
273
|
}
|
|
@@ -289,27 +310,41 @@ function scan(messages2, session, query, type, role, limit, before, after, width
|
|
|
289
310
|
}
|
|
290
311
|
return { results, total };
|
|
291
312
|
}
|
|
292
|
-
function search(client, unscoped, global) {
|
|
313
|
+
function search(client, unscoped, global, limits) {
|
|
293
314
|
return tool2({
|
|
294
315
|
description: `Search your conversation history in the opencode database. Use this to recover context lost to compaction \u2014 original tool outputs, earlier messages, reasoning, and user instructions that were pruned from your context window.
|
|
295
316
|
|
|
296
|
-
Searches text content, tool inputs/outputs, and reasoning. Returns matching snippets with session/message IDs you can pass to recall_get for full content.
|
|
317
|
+
Searches text content, tool inputs/outputs, and reasoning via case-insensitive substring matching. Returns matching snippets with session/message IDs you can pass to recall_get for full content, or recall_context if you need surrounding messages.
|
|
318
|
+
|
|
319
|
+
Start with scope "session" (fastest). Widen to "project" if not found. Use sessionID param to target a specific session found via recall_sessions. Use role "user" to find original requirements.
|
|
320
|
+
|
|
321
|
+
Scope costs: "session" scans 1 session. "project" scans up to \`sessions\` sessions (default 10). "global" scans across all projects.
|
|
322
|
+
|
|
323
|
+
Returns { ok, results: [{ sessionID, messageID, role, time, partID, partType, pruned, snippet, toolName? }], scanned, total, truncated }. Each result includes a pruned flag \u2014 if true, the content was compacted from your context window and recall_get will return the original full output. Check truncated to know if more matches exist beyond your results limit.
|
|
297
324
|
|
|
298
|
-
|
|
325
|
+
This tool's own outputs are excluded from search results to prevent recursive noise, but remain visible via recall_get, recall_context, and recall_messages.`,
|
|
299
326
|
args: {
|
|
300
|
-
query: tool2.schema.string().min(1).describe("Text to search for (case-insensitive)"),
|
|
327
|
+
query: tool2.schema.string().min(1).describe("Text to search for (case-insensitive substring match)"),
|
|
301
328
|
scope: tool2.schema.enum(["session", "project", "global"]).default("session").describe(
|
|
302
329
|
"session = current, project = all project sessions, global = all"
|
|
303
330
|
),
|
|
304
331
|
sessionID: tool2.schema.string().optional().describe("Search a specific session (overrides scope)"),
|
|
305
332
|
type: tool2.schema.enum(["text", "tool", "reasoning", "all"]).default("all").describe("Filter by part type"),
|
|
306
333
|
role: tool2.schema.enum(["user", "assistant", "all"]).default("all").describe("Filter by message role"),
|
|
307
|
-
sessions: tool2.schema.number().min(1).max(
|
|
308
|
-
|
|
309
|
-
|
|
334
|
+
sessions: tool2.schema.number().min(1).max(limits.maxSessions).default(Math.min(10, limits.maxSessions)).describe(
|
|
335
|
+
"Max sessions to scan (controls cost for project/global scope)"
|
|
336
|
+
),
|
|
337
|
+
results: tool2.schema.number().min(1).max(limits.maxResults).default(Math.min(10, limits.maxResults)).describe(
|
|
338
|
+
"Max results to return. Check truncated in response for more."
|
|
339
|
+
),
|
|
340
|
+
title: tool2.schema.string().optional().describe(
|
|
341
|
+
"Filter sessions by title before scanning (same as recall_sessions search)"
|
|
342
|
+
),
|
|
310
343
|
before: tool2.schema.number().optional().describe("Only match messages before this timestamp (ms epoch)"),
|
|
311
344
|
after: tool2.schema.number().optional().describe("Only match messages after this timestamp (ms epoch)"),
|
|
312
|
-
width: tool2.schema.number().min(50).max(1e3).default(
|
|
345
|
+
width: tool2.schema.number().min(50).max(Math.max(limits.defaultWidth, 1e3)).default(limits.defaultWidth).describe(
|
|
346
|
+
"Characters of context around each match in the returned snippet. Only a snippet is returned \u2014 use recall_get for full content."
|
|
347
|
+
)
|
|
313
348
|
},
|
|
314
349
|
async execute(args, ctx) {
|
|
315
350
|
ctx.metadata({ title: `Searching ${args.scope} for "${args.query}"` });
|
|
@@ -379,7 +414,7 @@ Start with scope "session" (fastest). Widen to "project" if not found. Use sessi
|
|
|
379
414
|
let scanned = 0;
|
|
380
415
|
let total = 0;
|
|
381
416
|
let early = false;
|
|
382
|
-
for (let i = 0; i < targets.length; i +=
|
|
417
|
+
for (let i = 0; i < targets.length; i += limits.concurrency) {
|
|
383
418
|
if (ctx.abort.aborted) {
|
|
384
419
|
early = true;
|
|
385
420
|
break;
|
|
@@ -389,7 +424,7 @@ Start with scope "session" (fastest). Widen to "project" if not found. Use sessi
|
|
|
389
424
|
break;
|
|
390
425
|
}
|
|
391
426
|
const remaining = args.results - collected.length;
|
|
392
|
-
const batch = targets.slice(i, i +
|
|
427
|
+
const batch = targets.slice(i, i + limits.concurrency);
|
|
393
428
|
const loaded = await Promise.all(
|
|
394
429
|
batch.map(async (t) => {
|
|
395
430
|
try {
|
|
@@ -447,10 +482,16 @@ import {
|
|
|
447
482
|
} from "@opencode-ai/plugin";
|
|
448
483
|
function get(client) {
|
|
449
484
|
return tool3({
|
|
450
|
-
description: `Retrieve the full content of a specific message from any session, including all parts (text, tool outputs, reasoning, etc). Use after recall to get the complete content of a search result. For tool parts, returns the original output even if it was pruned from your context window. Large outputs may be truncated by the opencode runtime
|
|
485
|
+
description: `Retrieve the full content of a specific message from any session, including all parts (text, tool outputs, reasoning, etc). Use after recall to get the complete content of a search result. For tool parts, returns the original output even if it was pruned from your context window. Large outputs may be truncated by the opencode runtime.
|
|
486
|
+
|
|
487
|
+
Returns { message: { id, role, time, model }, parts: [{ type, content, toolName, input, output, pruned, ... }], context: { sessionTitle, directory } }. Each part has a pruned flag indicating whether it was compacted.
|
|
488
|
+
|
|
489
|
+
Use recall_context instead if you need surrounding messages for context, not just a single message. Use sessionID and messageID from recall search results.`,
|
|
451
490
|
args: {
|
|
452
|
-
sessionID: tool3.schema.string().describe(
|
|
453
|
-
|
|
491
|
+
sessionID: tool3.schema.string().describe(
|
|
492
|
+
"Session containing the message (from recall search results)"
|
|
493
|
+
),
|
|
494
|
+
messageID: tool3.schema.string().describe("Message to retrieve (from recall search results)")
|
|
454
495
|
},
|
|
455
496
|
async execute(args, ctx) {
|
|
456
497
|
ctx.metadata({
|
|
@@ -499,20 +540,24 @@ function get(client) {
|
|
|
499
540
|
import {
|
|
500
541
|
tool as tool4
|
|
501
542
|
} from "@opencode-ai/plugin";
|
|
502
|
-
function context(client) {
|
|
543
|
+
function context(client, limits) {
|
|
503
544
|
return tool4({
|
|
504
|
-
description: `Get messages surrounding a specific message in a session. Use after recall finds a match and you need conversation context \u2014 what was asked before, what came after. Returns a window of messages centered on the target
|
|
545
|
+
description: `Get messages surrounding a specific message in a session. Use after recall finds a match and you need conversation context \u2014 what was asked before, what came after. Returns a chronological window of full messages (with all parts) centered on the target.
|
|
546
|
+
|
|
547
|
+
Returns { ok, messages: [{ message, parts, center? }], context, hasMoreBefore, hasMoreAfter }. The center message is marked with center: true. Use hasMoreBefore/hasMoreAfter with asymmetric before/after params to expand in either direction. window=3 returns up to 3 before + target + 3 after = 7 messages. window=0 returns only the target.
|
|
548
|
+
|
|
549
|
+
Use recall_get for a single message without neighbors. Use recall_messages for paginated browsing of an entire session.`,
|
|
505
550
|
args: {
|
|
506
551
|
sessionID: tool4.schema.string().describe("Session containing the message"),
|
|
507
552
|
messageID: tool4.schema.string().describe("Center message to get context around"),
|
|
508
|
-
window: tool4.schema.number().min(0).max(
|
|
509
|
-
"
|
|
553
|
+
window: tool4.schema.number().min(0).max(limits.maxWindow).default(Math.min(3, limits.maxWindow)).describe(
|
|
554
|
+
"Messages on each side of target (window=3 \u2192 up to 7 total). Overridden by before/after if set."
|
|
510
555
|
),
|
|
511
|
-
before: tool4.schema.number().min(0).max(
|
|
512
|
-
"Messages
|
|
556
|
+
before: tool4.schema.number().min(0).max(limits.maxWindow).optional().describe(
|
|
557
|
+
"Messages before the target (overrides window for before side). Set before=0, after=5 to see only what followed."
|
|
513
558
|
),
|
|
514
|
-
after: tool4.schema.number().min(0).max(
|
|
515
|
-
"Messages
|
|
559
|
+
after: tool4.schema.number().min(0).max(limits.maxWindow).optional().describe(
|
|
560
|
+
"Messages after the target (overrides window for after side)"
|
|
516
561
|
)
|
|
517
562
|
},
|
|
518
563
|
async execute(args, ctx) {
|
|
@@ -588,9 +633,13 @@ function msgMatches(msg, query) {
|
|
|
588
633
|
}
|
|
589
634
|
return false;
|
|
590
635
|
}
|
|
591
|
-
function messages(client) {
|
|
636
|
+
function messages(client, limits) {
|
|
592
637
|
return tool5({
|
|
593
|
-
description: `Browse messages in a session chronologically with pagination.
|
|
638
|
+
description: `Browse messages in a session chronologically with pagination. Unlike recall (which searches for specific text and returns snippets), this returns full messages with all parts in chronological order. Use it to replay a session when you don't have a specific search term, or when you need complete message content rather than search hits.
|
|
639
|
+
|
|
640
|
+
Use reverse=true to start from the most recent messages (offset 0 = newest). Use offset to paginate through results. Good for finding the user's original requirements at the start of a session.
|
|
641
|
+
|
|
642
|
+
Returns { messages: [{ message: { id, role, time }, parts: [...] }], pagination: { offset, returned, total, hasMore } }. The query and role params filter client-side after fetching, so they may return fewer results than limit. pagination.total reflects the count after filtering.`,
|
|
594
643
|
args: {
|
|
595
644
|
sessionID: tool5.schema.string().optional().describe(
|
|
596
645
|
"Session to browse. Defaults to current session if not provided."
|
|
@@ -598,11 +647,13 @@ function messages(client) {
|
|
|
598
647
|
offset: tool5.schema.number().min(0).default(0).describe(
|
|
599
648
|
"Skip this many messages from the start (or end if reversed)"
|
|
600
649
|
),
|
|
601
|
-
limit: tool5.schema.number().min(1).max(
|
|
602
|
-
role: tool5.schema.enum(["user", "assistant", "all"]).default("all").describe(
|
|
650
|
+
limit: tool5.schema.number().min(1).max(limits.maxMessages).default(Math.min(10, limits.maxMessages)).describe("Max messages to return"),
|
|
651
|
+
role: tool5.schema.enum(["user", "assistant", "all"]).default("all").describe(
|
|
652
|
+
"Filter by message role (client-side, may return fewer than limit)"
|
|
653
|
+
),
|
|
603
654
|
reverse: tool5.schema.boolean().default(false).describe("If true, start from most recent messages"),
|
|
604
655
|
query: tool5.schema.string().min(1).optional().describe(
|
|
605
|
-
"
|
|
656
|
+
"Case-insensitive substring filter on message content (client-side, may return fewer than limit)"
|
|
606
657
|
)
|
|
607
658
|
},
|
|
608
659
|
async execute(args, ctx) {
|
|
@@ -667,17 +718,20 @@ function messages(client) {
|
|
|
667
718
|
}
|
|
668
719
|
|
|
669
720
|
// src/opencode-session-recall.ts
|
|
670
|
-
var TOOLS = [
|
|
671
|
-
"recall",
|
|
672
|
-
"recall_get",
|
|
673
|
-
"recall_sessions",
|
|
674
|
-
"recall_context",
|
|
675
|
-
"recall_messages"
|
|
676
|
-
];
|
|
677
721
|
var server = async (ctx, options) => {
|
|
678
722
|
const opts = options ?? {};
|
|
679
723
|
const primary = opts.primary !== false;
|
|
680
724
|
const global = opts.global === true;
|
|
725
|
+
const clamp = (val, fallback, min = 1) => Math.max(min, Math.floor(val ?? fallback));
|
|
726
|
+
const limits = {
|
|
727
|
+
concurrency: clamp(opts.concurrency, DEFAULTS.concurrency),
|
|
728
|
+
maxSessions: clamp(opts.maxSessions, DEFAULTS.maxSessions),
|
|
729
|
+
maxResults: clamp(opts.maxResults, DEFAULTS.maxResults),
|
|
730
|
+
maxSessionList: clamp(opts.maxSessionList, DEFAULTS.maxSessionList),
|
|
731
|
+
maxMessages: clamp(opts.maxMessages, DEFAULTS.maxMessages),
|
|
732
|
+
maxWindow: clamp(opts.maxWindow, DEFAULTS.maxWindow),
|
|
733
|
+
defaultWidth: clamp(opts.defaultWidth, DEFAULTS.defaultWidth, 50)
|
|
734
|
+
};
|
|
681
735
|
const inner = ctx.client._client;
|
|
682
736
|
if (!inner?.getConfig)
|
|
683
737
|
throw new Error(
|
|
@@ -700,13 +754,14 @@ var server = async (ctx, options) => {
|
|
|
700
754
|
});
|
|
701
755
|
return {
|
|
702
756
|
tool: {
|
|
703
|
-
recall_sessions: sessions(client, unscoped, global),
|
|
704
|
-
recall: search(client, unscoped, global),
|
|
757
|
+
recall_sessions: sessions(client, unscoped, global, limits),
|
|
758
|
+
recall: search(client, unscoped, global, limits),
|
|
705
759
|
recall_get: get(client),
|
|
706
|
-
recall_context: context(client),
|
|
707
|
-
recall_messages: messages(client)
|
|
760
|
+
recall_context: context(client, limits),
|
|
761
|
+
recall_messages: messages(client, limits)
|
|
708
762
|
},
|
|
709
763
|
...primary && {
|
|
764
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- opencode config type not exported
|
|
710
765
|
config: async (c) => {
|
|
711
766
|
c.experimental ??= {};
|
|
712
767
|
const existing = c.experimental.primary_tools ?? [];
|
package/dist/search.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ToolDefinition } from "@opencode-ai/plugin";
|
|
2
2
|
import type { OpencodeClient } from "@opencode-ai/sdk/v2";
|
|
3
|
-
|
|
3
|
+
import { type Limits } from "./types.js";
|
|
4
|
+
export declare function search(client: OpencodeClient, unscoped: OpencodeClient, global: boolean, limits: Limits): ToolDefinition;
|
|
4
5
|
//# sourceMappingURL=search.d.ts.map
|
package/dist/search.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AAqEpB,wBAAgB,MAAM,CACpB,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GACb,cAAc,CAyNhB"}
|
package/dist/sessions.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ToolDefinition } from "@opencode-ai/plugin";
|
|
2
2
|
import type { OpencodeClient } from "@opencode-ai/sdk/v2";
|
|
3
|
-
|
|
3
|
+
import { type Limits } from "./types.js";
|
|
4
|
+
export declare function sessions(client: OpencodeClient, unscoped: OpencodeClient, global: boolean, limits: Limits): ToolDefinition;
|
|
4
5
|
//# sourceMappingURL=sessions.d.ts.map
|
package/dist/sessions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AAEpB,wBAAgB,QAAQ,CACtB,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GACb,cAAc,CA8GhB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
export declare const TOOLS: readonly ["recall", "recall_get", "recall_sessions", "recall_context", "recall_messages"];
|
|
2
|
+
export type Limits = {
|
|
3
|
+
concurrency: number;
|
|
4
|
+
maxSessions: number;
|
|
5
|
+
maxResults: number;
|
|
6
|
+
maxSessionList: number;
|
|
7
|
+
maxMessages: number;
|
|
8
|
+
maxWindow: number;
|
|
9
|
+
defaultWidth: number;
|
|
10
|
+
};
|
|
11
|
+
export declare const DEFAULTS: Limits;
|
|
1
12
|
export type SearchResult = {
|
|
2
13
|
sessionID: string;
|
|
3
14
|
sessionTitle: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "opencode-session-recall",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Agent memory without a memory system — search and retrieve opencode conversation history that was lost to compaction, across sessions and projects",
|
|
7
7
|
"main": "./dist/opencode-session-recall.js",
|
|
@@ -57,8 +57,11 @@
|
|
|
57
57
|
"zod": "^4.3.6"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
+
"@eslint/js": "^10.0.1",
|
|
60
61
|
"@opencode-ai/plugin": "^1.4.3",
|
|
62
|
+
"eslint": "^10.2.0",
|
|
61
63
|
"tsup": "^8.5.1",
|
|
62
|
-
"typescript": "^6.0.2"
|
|
64
|
+
"typescript": "^6.0.2",
|
|
65
|
+
"typescript-eslint": "^8.58.1"
|
|
63
66
|
}
|
|
64
67
|
}
|