opencode-deja 0.21.5 → 0.21.7

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/index.js +24 -7
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  English | [中文](https://github.com/vshulcz/deja-vu/blob/main/extensions/opencode/docs/zh.md)
4
4
 
5
5
  opencode remembers its own sessions. This plugin answers the other question:
6
- what you did in the thirty-four other coding agents on this machine — Claude Code,
6
+ what you did in the thirty-seven other coding agents on this machine — Claude Code,
7
7
  Codex, Cursor, Gemini and Zed among them — including the months before you
8
8
  installed anything.
9
9
 
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // opencode-deja — the sessions you already have, inside opencode.
2
2
  //
3
3
  // opencode remembers its own sessions. This plugin answers the other question:
4
- // what you did in Claude Code, Codex, Cursor, Gemini and thirty-one more agents on
4
+ // what you did in Claude Code, Codex, Cursor, Gemini and thirty-four more agents on
5
5
  // this machine, including months before deja existed. The index is deja's; this
6
6
  // file is the seam: six tools the model can call, plus recall that arrives
7
7
  // without being asked for.
@@ -166,6 +166,8 @@ export const DejaPlugin = async ({ client, directory }, options = {}) => {
166
166
  // again. Counted, so a store that really is empty is still only asked a few
167
167
  // times.
168
168
  const empties = new Map()
169
+ // Sessions between their compacting hook and the summary request.
170
+ const compacting = new Set()
169
171
  // Every session the per-prompt hook stamped live. opencode never says a
170
172
  // session ended, so without ending them here the stamp sat out its whole
171
173
  // window and the next session's MCP recall left a finished one out (#4546).
@@ -204,7 +206,7 @@ export const DejaPlugin = async ({ client, directory }, options = {}) => {
204
206
  const specs = {
205
207
  deja_recall: {
206
208
  description:
207
- "Search this machine's own past AI coding sessions — every agent used on it, including months before deja was installed. Use before debugging an error or re-implementing anything that may already exist. Match on the most specific token available: an exact error string, function name, file path or flag.",
209
+ "Search this machine's own past AI coding sessions — every agent used on it, including months before deja was installed. Use before debugging an error or re-implementing anything that may already exist, and before changing code, config, a dependency or a schedule. Match on the most specific token available: an exact error string, function name, file path or flag.",
208
210
  args: {
209
211
  query: { type: "string", description: "Specific tokens to match. Several words are ANDed." },
210
212
  limit: { type: "number", optional: true, description: "How many sessions to return. Default 5." },
@@ -281,6 +283,10 @@ export const DejaPlugin = async ({ client, directory }, options = {}) => {
281
283
  // the same place Claude Code's SessionStart output lands.
282
284
  hooks["experimental.chat.system.transform"] = async (input, output) => {
283
285
  try {
286
+ // The summary request is the last one a compaction makes, and the digest
287
+ // has no business in it: asked here, deja would hand the summariser the
288
+ // recovery packet meant for the turn after.
289
+ if (input?.sessionID && compacting.delete(input.sessionID)) return
284
290
  const key = input?.sessionID || "default"
285
291
  if (!digests.has(key)) {
286
292
  // The session id, as the plugin `deja install opencode-auto` writes
@@ -306,7 +312,9 @@ export const DejaPlugin = async ({ client, directory }, options = {}) => {
306
312
  // opencode sends each entry as its own system message, and a backend
307
313
  // whose template allows one (vLLM or SGLang serving Qwen) rejects the
308
314
  // request: "System message must be at the beginning." (#4058)
309
- if (output.system.length) output.system[0] = context + "\n\n" + output.system[0]
315
+ // After opencode's own prompt, not before it: that prompt is the same
316
+ // in every session and is what the provider's prompt cache keeps.
317
+ if (output.system.length) output.system[0] = output.system[0] + "\n\n" + context
310
318
  else output.system.push(context)
311
319
  return
312
320
  }
@@ -335,6 +343,11 @@ export const DejaPlugin = async ({ client, directory }, options = {}) => {
335
343
  // by what the user just asked. Silent when nothing matches.
336
344
  hooks["experimental.chat.messages.transform"] = async (input, output) => {
337
345
  try {
346
+ // The compaction's own request runs this transform right after the
347
+ // compacting hook. Recall there would land in the summariser's input and
348
+ // the recovery packet would count as delivered to a turn that never saw it.
349
+ const owner = (output?.messages || []).find((m) => m?.info?.sessionID)?.info?.sessionID
350
+ if (owner && compacting.has(owner)) return
338
351
  const { parts, prompt, sessionID } = lastUserText(output?.messages)
339
352
  // A turn with no text, an image alone, still goes to hook-prompt: that
340
353
  // call is what stamps the session live again after session.idle ended
@@ -356,11 +369,15 @@ export const DejaPlugin = async ({ client, directory }, options = {}) => {
356
369
  }
357
370
  }
358
371
 
359
- // Compaction is about to throw the working transcript away. Index what
360
- // exists now, so the session survives in memory after the window collapses.
361
- hooks["experimental.session.compacting"] = async () => {
372
+ // Compaction is about to summarise the session away. The turns are still in
373
+ // opencode's store, so deja reads them by the session id and keeps a
374
+ // recovery packet — commands, how they went, what was open — for the next
375
+ // turn, as Claude Code gets through PreCompact.
376
+ hooks["experimental.session.compacting"] = async (input) => {
362
377
  try {
363
- await ask(["hook-precompact"], undefined, 60000)
378
+ const sessionID = input?.sessionID || ""
379
+ if (sessionID) compacting.add(sessionID)
380
+ await ask(["hook-precompact"], JSON.stringify({ session_id: sessionID, cwd, harness: "opencode" }), 60000)
364
381
  } catch {
365
382
  // memory is optional: never break a compaction over it
366
383
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-deja",
3
- "version": "0.21.5",
4
- "description": "deja-vu memory for opencode: the session history of thirty-four other coding agents, searchable and recalled on every request — a local index, no LLM.",
3
+ "version": "0.21.7",
4
+ "description": "deja-vu memory for opencode: the session history of thirty-seven other coding agents, searchable and recalled on every request — a local index, no LLM.",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "exports": {
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@opencode-ai/plugin": ">=1.0.0",
55
- "@vshulcz/deja-vu": "^0.21.5"
55
+ "@vshulcz/deja-vu": "^0.21.7"
56
56
  },
57
57
  "scripts": {
58
58
  "test": "node --test test/*.test.js"