echoes-vault-opencode 1.2.2 → 1.2.3

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 CHANGED
@@ -100,6 +100,8 @@ Reads the last 3 daily logs and the full index, then summarizes where you left o
100
100
  ```
101
101
  The AI distills the session into a dense technical summary, writes new encyclopedia pages for any concepts decided today, and updates the index. All via a single tool call.
102
102
 
103
+ Vault status is never automatic: the user controls its transitions with `/echoes-init`, `/echoes-start`, and `/echoes-end`. In particular, `Memory Saved` is shown only after `/echoes-end`. Agents may add intermediate scratchpad notes during work, but those notes do not change the session status.
104
+
103
105
  ---
104
106
 
105
107
  ### Slash commands reference
package/index.ts CHANGED
@@ -188,6 +188,14 @@ const parseCommandFrontmatter = (cmd: string): { template: string; description?:
188
188
  return { template: match[2], description: frontmatter.description, agent: frontmatter.agent }
189
189
  }
190
190
 
191
+ const statusActions = {
192
+ "echoes-init": "init",
193
+ "echoes-start": "start",
194
+ "echoes-end": "end",
195
+ } as const
196
+
197
+ type StatusAction = (typeof statusActions)[keyof typeof statusActions]
198
+
191
199
  const OpenCodeEchoes: Plugin = async ({ directory }) => {
192
200
  const pluginDir = path.dirname(new URL(import.meta.url).pathname)
193
201
 
@@ -229,10 +237,11 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
229
237
  await ensureCommands(directory, commands)
230
238
  await ensureSkills(directory, skills)
231
239
 
240
+ // Sidebar status can change only through the matching user-invoked command.
241
+ const authorizedStatusActions = new Map<string, StatusAction>()
242
+
232
243
  const state = await readState(directory)
233
244
  state.pluginVersion = await getPluginVersion()
234
- state.session.started = false
235
- state.session.saved = false
236
245
  state.stats = await collectStats(paths)
237
246
  await writeState(directory, state)
238
247
 
@@ -244,10 +253,17 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
244
253
  input.command[name.replace(".md", "")] = { template, description, agent }
245
254
  }
246
255
  },
256
+ "command.execute.before": async (input) => {
257
+ const command = input.command.replace(/^\//, "") as keyof typeof statusActions
258
+ const action = statusActions[command]
259
+ if (action) {
260
+ authorizedStatusActions.set(input.sessionID, action)
261
+ }
262
+ },
247
263
  tool: {
248
264
  commit_memory_to_echoes_vault: tool({
249
265
  description:
250
- "Save session memory to EchoesVault. Writes a daily summary, creates new knowledge base pages, and updates the Vault index. Call this at session end to persist all context.",
266
+ "Save final session memory to EchoesVault. This succeeds only after the user explicitly runs /echoes-end; never call it merely because a task is complete.",
251
267
  args: {
252
268
  dailySummary: tool.schema
253
269
  .string()
@@ -285,7 +301,14 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
285
301
  .optional()
286
302
  .describe("Lines to find and replace in place within EchoesVault/index.md"),
287
303
  },
288
- async execute(args, _ctx) {
304
+ async execute(args, ctx) {
305
+ if (authorizedStatusActions.get(ctx.sessionID) !== "end") {
306
+ return [
307
+ "Final memory was not saved.",
308
+ "Only an explicit user /echoes-end command can save final memory and set the status to Memory Saved.",
309
+ ].join("\n")
310
+ }
311
+
289
312
  const today = getDateStr()
290
313
 
291
314
  await ensureVaultDirs(paths)
@@ -334,6 +357,7 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
334
357
  st.session.lastSave = new Date().toISOString()
335
358
  st.stats = await collectStats(paths)
336
359
  await writeState(directory, st)
360
+ authorizedStatusActions.delete(ctx.sessionID)
337
361
 
338
362
  return [
339
363
  `✅ Memory committed to EchoesVault.`,
@@ -454,26 +478,36 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
454
478
  }),
455
479
  echoes_activate_vault: tool({
456
480
  description:
457
- "Mark the EchoesVault as activated. Called automatically during /echoes-init to register the vault in the status tracker.",
481
+ "Mark the EchoesVault as activated. This succeeds only after the user explicitly runs /echoes-init.",
458
482
  args: {},
459
- async execute(_args, _ctx) {
483
+ async execute(_args, ctx) {
484
+ if (authorizedStatusActions.get(ctx.sessionID) !== "init") {
485
+ return "Vault status was not changed. Only an explicit user /echoes-init command can activate EchoesVault."
486
+ }
487
+
460
488
  const st = await readState(directory)
461
489
  st.initialized = true
462
490
  st.stats = await collectStats(paths)
463
491
  await writeState(directory, st)
492
+ authorizedStatusActions.delete(ctx.sessionID)
464
493
  return "EchoesVault activated."
465
494
  },
466
495
  }),
467
496
  echoes_start_session: tool({
468
497
  description:
469
- "Mark the current EchoesVault session as started. Called automatically during /echoes-start to update the status tracker.",
498
+ "Mark the current EchoesVault session as started. This succeeds only after the user explicitly runs /echoes-start.",
470
499
  args: {},
471
- async execute(_args, _ctx) {
500
+ async execute(_args, ctx) {
501
+ if (authorizedStatusActions.get(ctx.sessionID) !== "start") {
502
+ return "Vault status was not changed. Only an explicit user /echoes-start command can start an EchoesVault session."
503
+ }
504
+
472
505
  const st = await readState(directory)
473
506
  st.session.started = true
474
507
  st.session.saved = false
475
508
  st.session.lastStart = new Date().toISOString()
476
509
  await writeState(directory, st)
510
+ authorizedStatusActions.delete(ctx.sessionID)
477
511
  return "EchoesVault session started."
478
512
  },
479
513
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "echoes-vault-opencode",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "EchoesVault — persistent memory plugin for OpenCode. Obsidian-style knowledge base with daily logs, encyclopedia pages, and session resumption.",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -6,6 +6,8 @@ agent: build
6
6
  # SYSTEM MESSAGE: Session Distillation (Distill & Save)
7
7
  Our current session is coming to an end. Your task is to crystallize the knowledge we've gained today and commit it to EchoesVault.
8
8
 
9
+ This command is the user's explicit approval to finalize the session. Never invoke the final-memory tool outside this command, including after an ordinary task completion.
10
+
9
11
  Adhere to the principle of technical density: we do not need a transcript of our chat. We need dry architectural facts, bug fixes, applied configurations, and explicit decisions. Remember to use `> [!warning] DEPRECATED` tags if we rewrote legacy logic today.
10
12
 
11
13
  ## ACTION
package/tui.tsx CHANGED
@@ -74,7 +74,7 @@ const tui: TuiPlugin = async (api) => {
74
74
  const statusDesc = () => {
75
75
  const s = state()
76
76
  if (!s || !s.initialized) return null
77
- if (s.session.saved) return "Vault memorized session data"
77
+ if (s.session.saved) return "Final memory saved by /echoes-end"
78
78
  if (s.session.started) return null
79
79
  return null
80
80
  }