dsh-memory-recall 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -173,7 +173,7 @@ Other guards:
173
173
 
174
174
  ## Requirements
175
175
 
176
- - dsh 0.1.5 or later, with a `web` profile
176
+ - dsh 0.1.5 or later, with a `web` profile — from 0.2.1 also dsh 0.1.7+ and 0.2.x
177
177
  - Node 22+
178
178
  - No runtime dependencies — the client bundle requires only `react`
179
179
 
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-memory-recall",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "main": "./src/index.js",
5
5
  "description": "Persistent memory for dsh in the style of Hermes Agent: a memory tool that keeps a user profile and the model's own notes, delivered to every chat as a logged context message — at the start, after a change and after compaction — and a session_search tool that reads earlier chats only after the user approves, with a Memory panel to see and edit what is kept.",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-memory-recall",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Persistent memory for dsh in the style of Hermes Agent: a memory tool that keeps a user profile and the model's own notes, delivered to every chat as a logged context message — at the start, after a change and after compaction — and a session_search tool that reads earlier chats only after the user approves, with a Memory panel to see and edit what is kept.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -113,30 +113,62 @@ export async function apply(ctx, config = {}, deps = {}) {
113
113
  tooling.error = 'this dsh has no tool registry';
114
114
  }
115
115
 
116
- // ---- each new chat: memory in its prompt, frozen for its lifetime ---
117
- const briefed = new WeakSet();
116
+ // ---- each new chat: memory guidance in its prompt, frozen for its
117
+ // lifetime. dsh 0.1.7 removed `agent/session-start`; `agent/created` and
118
+ // `agent/disposed` exist on every dsh from 0.1.0-rc.7 through 0.2.x, so
119
+ // they are the seam — session-start stays wired as a back-compat path,
120
+ // deduped by the same map.
121
+ /** Agent contexts already carrying the section -> the registering fiber. */
122
+ const briefed = new WeakMap();
118
123
  // sessionId -> what that chat's prompt was given, for the composer chip.
119
124
  const briefings = createBriefings();
120
- ctx.on?.('agent/session-start', (payload) => {
121
- const agent = payload?.agent;
125
+
126
+ const installGuidance = (agent) => {
122
127
  const agentCtx = agent?.ctx;
123
128
  if (!agentCtx || briefed.has(agentCtx) || !isTopLevel(agent)) return;
124
- briefed.add(agentCtx);
125
129
  try {
126
- const prompt = service(agentCtx, 'systemPrompt');
127
- if (typeof prompt?.section !== 'function') return;
128
130
  // Read now, once: this text is what the chat keeps, so a save made later
129
131
  // in it never rewrites its prompt (which would cost the cached prefix).
130
132
  // The guidance only: fixed text, so the cached prompt prefix holds.
131
133
  const text = renderSection(null, store.limits, { tools: tooling.ok });
132
- const register = () => prompt.section({ name: SECTION_NAME, order: SECTION_ORDER, text });
133
- if (typeof agentCtx.effect === 'function') agentCtx.effect(register, 'dsh-memory-recall: memory snapshot');
134
- else register();
134
+ // `inject` rather than `get`: at agent/created the prompt service may not
135
+ // be resolvable yet; the callback runs once it is, and the fiber's
136
+ // dispose() takes the section back out.
137
+ const fiber = agentCtx.inject?.(['systemPrompt'], (scope) => {
138
+ if (!isTopLevel(agent)) return; // the header may have attached since created
139
+ try {
140
+ scope.systemPrompt?.section?.({ name: SECTION_NAME, order: SECTION_ORDER, text });
141
+ } catch {
142
+ // Already registered (e.g. after a plugin reload): a duplicate name throws.
143
+ }
144
+ });
145
+ if (fiber) briefed.set(agentCtx, fiber);
135
146
  } catch (error) {
136
- // Losing the snapshot costs this chat its memory, never the chat.
147
+ // Losing the guidance costs this chat its memory, never the chat.
137
148
  ctx.logger?.warn?.(`[dsh-memory-recall] no memory for this chat: ${error?.message ?? error}`);
138
149
  }
150
+ };
151
+
152
+ const uninstallGuidance = (agent) => {
153
+ const agentCtx = agent?.ctx;
154
+ const fiber = agentCtx && briefed.get(agentCtx);
155
+ if (!fiber) return;
156
+ briefed.delete(agentCtx);
157
+ try {
158
+ fiber.dispose?.();
159
+ } catch {
160
+ // Teardown must not throw.
161
+ }
162
+ };
163
+
164
+ // Agents that already exist when this plugin mounts (a reload).
165
+ ctx.inject?.(['agents'], (scope) => {
166
+ for (const agent of scope.agents?.list?.() ?? []) installGuidance(agent);
139
167
  });
168
+ ctx.on?.('agent/created', (payload) => installGuidance(payload?.agent));
169
+ ctx.on?.('agent/disposed', (payload) => uninstallGuidance(payload?.agent));
170
+ // Older hosts fire this instead of agent/created; the map dedupes.
171
+ ctx.on?.('agent/session-start', (payload) => installGuidance(payload?.agent));
140
172
 
141
173
  // ---- the memory itself: a logged context message, whenever the chat
142
174
  // cannot see the current version (start, after compaction, after a change).