@heixui/dsh-session-suite 0.3.0 → 0.3.1

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 (2) hide show
  1. package/lib/memory-host.js +45 -2
  2. package/package.json +2 -1
@@ -110,6 +110,21 @@ export function installMemory({ ctx, root, config, log }) {
110
110
  */
111
111
  const recallByScope = new Map()
112
112
  let lastRecall = ''
113
+ // Diagnostics for the settings page: "did the last rebuild fail, and why" must
114
+ // be answerable, because the failure mode is invisible otherwise — the model
115
+ // simply behaves as if the memory is missing.
116
+ let lastRecallFailed = false
117
+ let lastRecallError = ''
118
+
119
+ /**
120
+ * How long a computed recall may be reused before it must be recomputed.
121
+ *
122
+ * `agent/pre-step` normally refreshes it every step, so this only matters when
123
+ * a step does not run the hook (a resumed session, a step served from a cached
124
+ * decision). Without a bound, a memory written elsewhere could stay invisible
125
+ * for the life of the process.
126
+ */
127
+ const RECALL_TTL_MS = 60_000
113
128
 
114
129
  // --- 1. Recall into the system prompt -----------------------------------
115
130
  const systemPrompt = ctx.get('systemPrompt')
@@ -122,7 +137,11 @@ export function installMemory({ ctx, root, config, log }) {
122
137
  order: 900,
123
138
  text: context => {
124
139
  const scoped = context?.scope ? recallByScope.get(context.scope) : undefined
125
- return scoped?.text || lastRecall || ''
140
+ // Expiry is checked here rather than only at write time: a cached
141
+ // value outlives its usefulness the moment the library changes
142
+ // underneath it, and this is the only place that can see the age.
143
+ if (scoped && Date.now() - scoped.at < RECALL_TTL_MS) return scoped.text
144
+ return lastRecall || ''
126
145
  },
127
146
  }),
128
147
  'session-suite: memory context',
@@ -160,7 +179,19 @@ export function installMemory({ ctx, root, config, log }) {
160
179
  const text = await buildRecallText({ engine, cwd, question, config })
161
180
  if (agent) recallByScope.set(agent, { text, at: Date.now() })
162
181
  lastRecall = text
182
+ lastRecallFailed = false
163
183
  } catch (error) {
184
+ // A failed rebuild must not leave the previous answer in place.
185
+ //
186
+ // Observed in the wild: one failed rebuild, and every later turn kept
187
+ // injecting the memory text computed BEFORE it — a memory written after
188
+ // that point was invisible for the rest of the process, which reads as
189
+ // "the other workspace does not know". Stale recall is worse than no
190
+ // recall: empty at least tells the truth.
191
+ lastRecall = ''
192
+ if (agent) recallByScope.delete(agent)
193
+ lastRecallFailed = true
194
+ lastRecallError = error instanceof Error ? error.message : String(error)
164
195
  log('memory recall failed', error)
165
196
  }
166
197
  return decision
@@ -329,7 +360,19 @@ export function installMemory({ ctx, root, config, log }) {
329
360
  }, 'session-suite: memory tool')
330
361
  }
331
362
 
332
- return { engine, dispose: () => {} }
363
+ return {
364
+ engine,
365
+ dispose: () => {},
366
+ // Exposed so the settings page and the status endpoint can report whether the
367
+ // last recall rebuild failed. Without this the failure is silent: the model
368
+ // simply acts as if the memory is not there.
369
+ recallStatus: () => ({
370
+ failed: lastRecallFailed,
371
+ error: lastRecallError,
372
+ hasText: Boolean(lastRecall) || recallByScope.size > 0,
373
+ scopes: recallByScope.size,
374
+ }),
375
+ }
333
376
  }
334
377
 
335
378
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heixui/dsh-session-suite",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Session checkpoints for DSH: rewind a conversation with its files and memory, permanent session deletion, and shareable exports",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -56,6 +56,7 @@
56
56
  "THIRD-PARTY.md"
57
57
  ],
58
58
  "dsh": {
59
+ "icon": "./icon.svg",
59
60
  "bundle": {
60
61
  "patch": "./cordis.patch.yml"
61
62
  },