@alanzhao/dsh-memory-lite 0.1.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/LICENSE +21 -0
- package/README.md +112 -0
- package/cordis.patch.yml +9 -0
- package/lib/catalog.d.ts +35 -0
- package/lib/catalog.js +145 -0
- package/lib/catalog.js.map +1 -0
- package/lib/client.js +631 -0
- package/lib/config.d.ts +133 -0
- package/lib/config.js +173 -0
- package/lib/config.js.map +1 -0
- package/lib/extract/checkpoint.d.ts +60 -0
- package/lib/extract/checkpoint.js +42 -0
- package/lib/extract/checkpoint.js.map +1 -0
- package/lib/extract/decision.d.ts +36 -0
- package/lib/extract/decision.js +118 -0
- package/lib/extract/decision.js.map +1 -0
- package/lib/extract/digest.d.ts +12 -0
- package/lib/extract/digest.js +25 -0
- package/lib/extract/digest.js.map +1 -0
- package/lib/extract/index.d.ts +52 -0
- package/lib/extract/index.js +489 -0
- package/lib/extract/index.js.map +1 -0
- package/lib/extract/prompt.d.ts +20 -0
- package/lib/extract/prompt.js +63 -0
- package/lib/extract/prompt.js.map +1 -0
- package/lib/extract/triggers.d.ts +15 -0
- package/lib/extract/triggers.js +26 -0
- package/lib/extract/triggers.js.map +1 -0
- package/lib/extract/window.d.ts +48 -0
- package/lib/extract/window.js +110 -0
- package/lib/extract/window.js.map +1 -0
- package/lib/index.d.ts +29 -0
- package/lib/index.js +92 -0
- package/lib/index.js.map +1 -0
- package/lib/inject.d.ts +45 -0
- package/lib/inject.js +102 -0
- package/lib/inject.js.map +1 -0
- package/lib/memory-store.d.ts +147 -0
- package/lib/memory-store.js +494 -0
- package/lib/memory-store.js.map +1 -0
- package/lib/path.d.ts +24 -0
- package/lib/path.js +54 -0
- package/lib/path.js.map +1 -0
- package/lib/peer.d.ts +16 -0
- package/lib/peer.js +38 -0
- package/lib/peer.js.map +1 -0
- package/lib/status.d.ts +44 -0
- package/lib/status.js +42 -0
- package/lib/status.js.map +1 -0
- package/lib/tool-utils.d.ts +25 -0
- package/lib/tool-utils.js +16 -0
- package/lib/tool-utils.js.map +1 -0
- package/lib/tools/forget-memory.d.ts +9 -0
- package/lib/tools/forget-memory.js +39 -0
- package/lib/tools/forget-memory.js.map +1 -0
- package/lib/tools/read-memory.d.ts +8 -0
- package/lib/tools/read-memory.js +41 -0
- package/lib/tools/read-memory.js.map +1 -0
- package/lib/tools/remember.d.ts +9 -0
- package/lib/tools/remember.js +65 -0
- package/lib/tools/remember.js.map +1 -0
- package/lib/tools/search-memory.d.ts +8 -0
- package/lib/tools/search-memory.js +49 -0
- package/lib/tools/search-memory.js.map +1 -0
- package/lib/tools/update-memory.d.ts +9 -0
- package/lib/tools/update-memory.js +46 -0
- package/lib/tools/update-memory.js.map +1 -0
- package/lib/types.d.ts +35 -0
- package/lib/types.js +7 -0
- package/lib/types.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/extract/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,4DAA4D;AAC5D,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,oIAAoI;QACpI,YAAY;QACZ,0IAA0I;QAC1I,4DAA4D;QAC5D,iIAAiI;QACjI,uDAAuD;QACvD,EAAE;QACF,4EAA4E;QAC5E,yMAAyM;QACzM,yIAAyI;QACzI,sKAAsK;QACtK,yHAAyH;QACzH,gGAAgG;QAChG,EAAE;QACF,oMAAoM;QACpM,EAAE;QACF,iFAAiF;QACjF,6JAA6J;QAC7J,uDAAuD;QACvD,mFAAmF;QACnF,4FAA4F;QAC5F,EAAE;QACF,mFAAmF;QACnF,0GAA0G;QAC1G,mDAAmD;QACnD,oDAAoD;QACpD,oCAAoC;QACpC,EAAE;QACF,sBAAsB;QACtB,uEAAuE;QACvE,8EAA8E;QAC9E,2EAA2E;QAC3E,8FAA8F;QAC9F,yCAAyC;KAC1C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAQD,qEAAqE;AACrE,MAAM,UAAU,mBAAmB,CACjC,MAA0B,EAC1B,UAAkB,EAClB,YAAuC,EACvC,QAAgC;IAEhC,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAA;IAC1C,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;IACtC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,kBAAkB,EAAE,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC9G,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC,sBAAsB,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACrG,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,wEAAwE,CAAC,CAAA;IACvF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AAC9B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure trigger decisions for implicit extraction. The integration layer owns
|
|
3
|
+
* event wiring and job dispatch; these functions own the arithmetic (message
|
|
4
|
+
* counts, turn-boundary thresholds, debounce, idle) so they are unit-testable.
|
|
5
|
+
* @module dsh-memory-lite/src/extract/triggers
|
|
6
|
+
*/
|
|
7
|
+
/** Window trigger: enough new surface messages accumulated. */
|
|
8
|
+
export declare function shouldExtractWindow(pendingCount: number, windowTurns: number): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Turn-boundary trigger: at least `minTurnExtract` unextracted messages and
|
|
11
|
+
* outside the `turnDebounceMs` window since the last extraction.
|
|
12
|
+
*/
|
|
13
|
+
export declare function shouldExtractTurn(pendingCount: number, now: number, lastExtractAt: number | null, minTurnExtract: number, turnDebounceMs: number): boolean;
|
|
14
|
+
/** Idle trigger: anything left unextracted. */
|
|
15
|
+
export declare function shouldExtractIdle(pendingCount: number): boolean;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure trigger decisions for implicit extraction. The integration layer owns
|
|
3
|
+
* event wiring and job dispatch; these functions own the arithmetic (message
|
|
4
|
+
* counts, turn-boundary thresholds, debounce, idle) so they are unit-testable.
|
|
5
|
+
* @module dsh-memory-lite/src/extract/triggers
|
|
6
|
+
*/
|
|
7
|
+
/** Window trigger: enough new surface messages accumulated. */
|
|
8
|
+
export function shouldExtractWindow(pendingCount, windowTurns) {
|
|
9
|
+
return pendingCount >= windowTurns;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Turn-boundary trigger: at least `minTurnExtract` unextracted messages and
|
|
13
|
+
* outside the `turnDebounceMs` window since the last extraction.
|
|
14
|
+
*/
|
|
15
|
+
export function shouldExtractTurn(pendingCount, now, lastExtractAt, minTurnExtract, turnDebounceMs) {
|
|
16
|
+
if (pendingCount < minTurnExtract)
|
|
17
|
+
return false;
|
|
18
|
+
if (lastExtractAt !== null && now - lastExtractAt < turnDebounceMs)
|
|
19
|
+
return false;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
/** Idle trigger: anything left unextracted. */
|
|
23
|
+
export function shouldExtractIdle(pendingCount) {
|
|
24
|
+
return pendingCount > 0;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=triggers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triggers.js","sourceRoot":"","sources":["../../src/extract/triggers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,+DAA+D;AAC/D,MAAM,UAAU,mBAAmB,CAAC,YAAoB,EAAE,WAAmB;IAC3E,OAAO,YAAY,IAAI,WAAW,CAAA;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,YAAoB,EACpB,GAAW,EACX,aAA4B,EAC5B,cAAsB,EACtB,cAAsB;IAEtB,IAAI,YAAY,GAAG,cAAc;QAAE,OAAO,KAAK,CAAA;IAC/C,IAAI,aAAa,KAAK,IAAI,IAAI,GAAG,GAAG,aAAa,GAAG,cAAc;QAAE,OAAO,KAAK,CAAA;IAChF,OAAO,IAAI,CAAA;AACb,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,iBAAiB,CAAC,YAAoB;IACpD,OAAO,YAAY,GAAG,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extraction windowing: which session-log events feed an extraction run, how
|
|
3
|
+
* the window is cropped, and how each event is rendered into text. Pure
|
|
4
|
+
* functions over a minimal structural event shape (structurally satisfied by
|
|
5
|
+
* DSH SessionEvent) so they are unit-testable without a live runtime.
|
|
6
|
+
* @module dsh-memory-lite/src/extract/window
|
|
7
|
+
*/
|
|
8
|
+
/** The three surface event types (v6 定死, confirmed in dsh-session source). */
|
|
9
|
+
export declare const SURFACE_TYPES: readonly ["user/message", "assistant/message", "tool/result"];
|
|
10
|
+
export type SurfaceEventType = (typeof SURFACE_TYPES)[number];
|
|
11
|
+
/** Minimal structural view of a session-log event; DSH SessionEvent satisfies it. */
|
|
12
|
+
export interface SurfaceEventLike {
|
|
13
|
+
readonly type: string;
|
|
14
|
+
readonly seq: number;
|
|
15
|
+
readonly data: unknown;
|
|
16
|
+
}
|
|
17
|
+
/** Whether an event type belongs to the message-producing surface set. */
|
|
18
|
+
export declare function isSurfaceType(type: string): type is SurfaceEventType;
|
|
19
|
+
/** Keep at most the trailing `max` items (most recent first by position). */
|
|
20
|
+
export declare function takeTail<T>(items: readonly T[], max: number): T[];
|
|
21
|
+
/**
|
|
22
|
+
* Whether a surface event is real conversation content rather than a
|
|
23
|
+
* system-injected message. Real user input is stamped `source.kind === 'user'`;
|
|
24
|
+
* injections (runtime context, skill catalog, memory catalog, agent
|
|
25
|
+
* instructions, goals) carry their own kinds and are excluded so boilerplate
|
|
26
|
+
* never reaches the extraction model or inflates its cost. Assistant messages
|
|
27
|
+
* and tool results are always conversation content.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isConversationEvent(event: SurfaceEventLike): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Select the extraction window: real conversation surface events with seq
|
|
32
|
+
* strictly after `fromSeqExclusive`, capped to the trailing `maxMessages`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function selectWindow(events: readonly SurfaceEventLike[], fromSeqExclusive: number, maxMessages: number): SurfaceEventLike[];
|
|
35
|
+
/**
|
|
36
|
+
* Byte-aware truncation that never splits a UTF-8 sequence: the boundary is
|
|
37
|
+
* walked back to a clean continuation start, so CJK and other multibyte text
|
|
38
|
+
* stays valid (no replacement chars introduced).
|
|
39
|
+
*/
|
|
40
|
+
export declare function truncateBytes(text: string, maxBytes: number): string;
|
|
41
|
+
/** The message-content block list carried by an event, tolerant of both shapes. */
|
|
42
|
+
export declare function messageBlocksOf(data: unknown): unknown[];
|
|
43
|
+
/** Render one content block to its text contribution. */
|
|
44
|
+
export declare function blockText(block: unknown, toolResultMaxBytes: number): string | undefined;
|
|
45
|
+
/** Concatenate block texts, truncating per `toolResultMaxBytes`. */
|
|
46
|
+
export declare function renderBlocks(blocks: readonly unknown[], toolResultMaxBytes: number): string;
|
|
47
|
+
/** One rendered line for an event, prefixed by its role. */
|
|
48
|
+
export declare function renderEventText(event: SurfaceEventLike, toolResultMaxBytes: number): string;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extraction windowing: which session-log events feed an extraction run, how
|
|
3
|
+
* the window is cropped, and how each event is rendered into text. Pure
|
|
4
|
+
* functions over a minimal structural event shape (structurally satisfied by
|
|
5
|
+
* DSH SessionEvent) so they are unit-testable without a live runtime.
|
|
6
|
+
* @module dsh-memory-lite/src/extract/window
|
|
7
|
+
*/
|
|
8
|
+
/** The three surface event types (v6 定死, confirmed in dsh-session source). */
|
|
9
|
+
export const SURFACE_TYPES = ['user/message', 'assistant/message', 'tool/result'];
|
|
10
|
+
/** Whether an event type belongs to the message-producing surface set. */
|
|
11
|
+
export function isSurfaceType(type) {
|
|
12
|
+
return SURFACE_TYPES.includes(type);
|
|
13
|
+
}
|
|
14
|
+
/** Keep at most the trailing `max` items (most recent first by position). */
|
|
15
|
+
export function takeTail(items, max) {
|
|
16
|
+
if (max <= 0)
|
|
17
|
+
return [];
|
|
18
|
+
return items.length <= max ? [...items] : items.slice(items.length - max);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Whether a surface event is real conversation content rather than a
|
|
22
|
+
* system-injected message. Real user input is stamped `source.kind === 'user'`;
|
|
23
|
+
* injections (runtime context, skill catalog, memory catalog, agent
|
|
24
|
+
* instructions, goals) carry their own kinds and are excluded so boilerplate
|
|
25
|
+
* never reaches the extraction model or inflates its cost. Assistant messages
|
|
26
|
+
* and tool results are always conversation content.
|
|
27
|
+
*/
|
|
28
|
+
export function isConversationEvent(event) {
|
|
29
|
+
if (event.type !== 'user/message')
|
|
30
|
+
return true;
|
|
31
|
+
const source = event.data?.source;
|
|
32
|
+
return source?.kind === 'user';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Select the extraction window: real conversation surface events with seq
|
|
36
|
+
* strictly after `fromSeqExclusive`, capped to the trailing `maxMessages`.
|
|
37
|
+
*/
|
|
38
|
+
export function selectWindow(events, fromSeqExclusive, maxMessages) {
|
|
39
|
+
const selected = events.filter(event => isSurfaceType(event.type) && isConversationEvent(event) && event.seq > fromSeqExclusive);
|
|
40
|
+
return takeTail(selected, maxMessages);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Byte-aware truncation that never splits a UTF-8 sequence: the boundary is
|
|
44
|
+
* walked back to a clean continuation start, so CJK and other multibyte text
|
|
45
|
+
* stays valid (no replacement chars introduced).
|
|
46
|
+
*/
|
|
47
|
+
export function truncateBytes(text, maxBytes) {
|
|
48
|
+
if (maxBytes <= 0)
|
|
49
|
+
return '';
|
|
50
|
+
const bytes = Buffer.from(text, 'utf8');
|
|
51
|
+
if (bytes.length <= maxBytes)
|
|
52
|
+
return text;
|
|
53
|
+
let end = maxBytes;
|
|
54
|
+
while (end > 0 && end <= bytes.length && (bytes[end] & 0b11000000) === 0b10000000)
|
|
55
|
+
end -= 1;
|
|
56
|
+
return bytes.subarray(0, end).toString('utf8');
|
|
57
|
+
}
|
|
58
|
+
/** The message-content block list carried by an event, tolerant of both shapes. */
|
|
59
|
+
export function messageBlocksOf(data) {
|
|
60
|
+
const record = data;
|
|
61
|
+
if (record !== null && typeof record === 'object') {
|
|
62
|
+
const message = record.message;
|
|
63
|
+
if (typeof message === 'object' && message !== null && Array.isArray(message.content)) {
|
|
64
|
+
return message.content;
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(record.content))
|
|
67
|
+
return record.content;
|
|
68
|
+
}
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
/** Render one content block to its text contribution. */
|
|
72
|
+
export function blockText(block, toolResultMaxBytes) {
|
|
73
|
+
if (typeof block !== 'object' || block === null)
|
|
74
|
+
return undefined;
|
|
75
|
+
const b = block;
|
|
76
|
+
switch (b.type) {
|
|
77
|
+
case 'text':
|
|
78
|
+
return typeof b.text === 'string' ? b.text : undefined;
|
|
79
|
+
case 'tool-call': {
|
|
80
|
+
const name = typeof b.name === 'string' ? b.name : '?';
|
|
81
|
+
const args = typeof b.arguments === 'string' ? ' ' + truncateBytes(b.arguments, 200) : '';
|
|
82
|
+
return `[tool-call ${name}${args}]`;
|
|
83
|
+
}
|
|
84
|
+
case 'tool-result': {
|
|
85
|
+
const inner = Array.isArray(b.content) ? renderBlocks(b.content, toolResultMaxBytes) : '';
|
|
86
|
+
const id = typeof b.id === 'string' ? b.id : '';
|
|
87
|
+
return `[tool-result ${id}] ${truncateBytes(inner, toolResultMaxBytes)}`;
|
|
88
|
+
}
|
|
89
|
+
default:
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Concatenate block texts, truncating per `toolResultMaxBytes`. */
|
|
94
|
+
export function renderBlocks(blocks, toolResultMaxBytes) {
|
|
95
|
+
const parts = [];
|
|
96
|
+
for (const block of blocks) {
|
|
97
|
+
const text = blockText(block, toolResultMaxBytes);
|
|
98
|
+
if (text !== undefined && text !== '')
|
|
99
|
+
parts.push(text);
|
|
100
|
+
}
|
|
101
|
+
return parts.join(' ');
|
|
102
|
+
}
|
|
103
|
+
/** One rendered line for an event, prefixed by its role. */
|
|
104
|
+
export function renderEventText(event, toolResultMaxBytes) {
|
|
105
|
+
const blocks = messageBlocksOf(event.data);
|
|
106
|
+
const body = renderBlocks(blocks, toolResultMaxBytes);
|
|
107
|
+
const prefix = event.type === 'user/message' ? 'user' : event.type === 'tool/result' ? 'tool' : 'assistant';
|
|
108
|
+
return body === '' ? `[${prefix} (no text)]` : `[${prefix}] ${body}`;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=window.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.js","sourceRoot":"","sources":["../../src/extract/window.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-memory-lite: lightweight Markdown-file memory for DeepSeek Harness.
|
|
3
|
+
*
|
|
4
|
+
* - Five model-facing tools: read_memory, search_memory, remember,
|
|
5
|
+
* update_memory, forget_memory (explicit capture only — the implicit
|
|
6
|
+
* extraction channel is Phase 2).
|
|
7
|
+
* - L0 catalog injection (`agent/pre-step` waterfall): the peer's memory
|
|
8
|
+
* index is published as a durable user message and re-published when the
|
|
9
|
+
* index changes or compaction moves it off the visible surface.
|
|
10
|
+
* - Peer isolation: each session's cwd derives its own memory root.
|
|
11
|
+
* - All file access goes through a containment boundary; subagent sessions
|
|
12
|
+
* get no catalog and no tool access.
|
|
13
|
+
*
|
|
14
|
+
* Model Experience: the 5 tool schemas are injected every step; the L0
|
|
15
|
+
* catalog is one durable surface message resident for the session (re-sent
|
|
16
|
+
* after compaction or index changes). See README.md and IMPLEMENTATION.md.
|
|
17
|
+
* @module dsh-memory-lite
|
|
18
|
+
*/
|
|
19
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
20
|
+
import { Config } from './config.js';
|
|
21
|
+
import type { MemoryConfig } from './config.js';
|
|
22
|
+
/** Cordis plugin name used by loader diagnostics. */
|
|
23
|
+
export declare const name = "dsh-memory-lite";
|
|
24
|
+
/** Services this plugin needs: the tool registry, the agent registry, the LLM stream,
|
|
25
|
+
* and the timer service (ctx.timeout for the extraction idle backstop). */
|
|
26
|
+
export declare const inject: string[];
|
|
27
|
+
export { Config };
|
|
28
|
+
/** Register the memory capability for the lifetime of `ctx`. */
|
|
29
|
+
export declare function apply(ctx: Context, config?: MemoryConfig): void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-memory-lite: lightweight Markdown-file memory for DeepSeek Harness.
|
|
3
|
+
*
|
|
4
|
+
* - Five model-facing tools: read_memory, search_memory, remember,
|
|
5
|
+
* update_memory, forget_memory (explicit capture only — the implicit
|
|
6
|
+
* extraction channel is Phase 2).
|
|
7
|
+
* - L0 catalog injection (`agent/pre-step` waterfall): the peer's memory
|
|
8
|
+
* index is published as a durable user message and re-published when the
|
|
9
|
+
* index changes or compaction moves it off the visible surface.
|
|
10
|
+
* - Peer isolation: each session's cwd derives its own memory root.
|
|
11
|
+
* - All file access goes through a containment boundary; subagent sessions
|
|
12
|
+
* get no catalog and no tool access.
|
|
13
|
+
*
|
|
14
|
+
* Model Experience: the 5 tool schemas are injected every step; the L0
|
|
15
|
+
* catalog is one durable surface message resident for the session (re-sent
|
|
16
|
+
* after compaction or index changes). See README.md and IMPLEMENTATION.md.
|
|
17
|
+
* @module dsh-memory-lite
|
|
18
|
+
*/
|
|
19
|
+
import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
20
|
+
import { Config, resolveConfig } from './config.js';
|
|
21
|
+
import { MemoryStore } from './memory-store.js';
|
|
22
|
+
import { applyReadMemoryTool } from './tools/read-memory.js';
|
|
23
|
+
import { applySearchMemoryTool } from './tools/search-memory.js';
|
|
24
|
+
import { applyRememberTool } from './tools/remember.js';
|
|
25
|
+
import { applyUpdateMemoryTool } from './tools/update-memory.js';
|
|
26
|
+
import { applyForgetMemoryTool } from './tools/forget-memory.js';
|
|
27
|
+
import { applyMemoryCatalogInjection } from './inject.js';
|
|
28
|
+
import { applyExtraction } from './extract/index.js';
|
|
29
|
+
import { createStatusTracker } from './status.js';
|
|
30
|
+
/** Cordis plugin name used by loader diagnostics. */
|
|
31
|
+
export const name = 'dsh-memory-lite';
|
|
32
|
+
/** Services this plugin needs: the tool registry, the agent registry, the LLM stream,
|
|
33
|
+
* and the timer service (ctx.timeout for the extraction idle backstop). */
|
|
34
|
+
export const inject = ['tools', 'agents', 'llm', 'timer', 'connection'];
|
|
35
|
+
export { Config };
|
|
36
|
+
/** Register the memory capability for the lifetime of `ctx`. */
|
|
37
|
+
export function apply(ctx, config = {}) {
|
|
38
|
+
// The live config is a mutable reference; settings changes swap it in place
|
|
39
|
+
// (see installSettingsSection below), so every consumer reading through the
|
|
40
|
+
// deps getter sees the newest value without re-registering.
|
|
41
|
+
let live = resolveConfig(config);
|
|
42
|
+
const store = new MemoryStore(live.root, live.sharing);
|
|
43
|
+
const defaultModel = () => {
|
|
44
|
+
const svc = ctx.get('agentDefaultModel');
|
|
45
|
+
if (svc?.currentSelection === undefined)
|
|
46
|
+
return { provider: '', model: '' };
|
|
47
|
+
try {
|
|
48
|
+
return svc.currentSelection();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return { provider: '', model: '' };
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const deps = { config: () => live, store, defaultModel };
|
|
55
|
+
const readTool = applyReadMemoryTool(ctx, deps);
|
|
56
|
+
applySearchMemoryTool(ctx, deps);
|
|
57
|
+
applyRememberTool(ctx, deps);
|
|
58
|
+
applyUpdateMemoryTool(ctx, deps);
|
|
59
|
+
applyForgetMemoryTool(ctx, deps);
|
|
60
|
+
applyMemoryCatalogInjection(ctx, deps, readTool);
|
|
61
|
+
const tracker = createStatusTracker();
|
|
62
|
+
applyExtraction(ctx, deps, tracker);
|
|
63
|
+
// Settings-backed overrides (方案 A, IMPLEMENTATION.md §16): the cordis row
|
|
64
|
+
// config is the composition base; user edits land in ~/.dsh/settings.yaml
|
|
65
|
+
// under the dsh-memory-lite namespace. root/sharing stay pinned to the store
|
|
66
|
+
// snapshot (restart-applies — MemoryStore holds them at construction); every
|
|
67
|
+
// other field resolves live on each change.
|
|
68
|
+
let source = () => config;
|
|
69
|
+
installSettingsSection(ctx, settingsNamespace('dsh-memory-lite'), Config, config, {
|
|
70
|
+
setSource: (get) => { source = get; },
|
|
71
|
+
onChange: () => {
|
|
72
|
+
const next = resolveConfig(source());
|
|
73
|
+
live = { ...next, root: live.root, sharing: live.sharing };
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
// Browser indicator channel: the header dot polls this snapshot.
|
|
77
|
+
ctx.connection.rpc.handle('/memory-status', async (_endpoint, _payload, _signal) => ({
|
|
78
|
+
ok: true,
|
|
79
|
+
value: tracker.snapshot(live.extraction.mode),
|
|
80
|
+
}), { authority: 'loopback' });
|
|
81
|
+
// Settings UI: list existing peers so sharing.mounts can be edited as
|
|
82
|
+
// checkboxes instead of raw YAML (IMPLEMENTATION.md §16.9 known limit).
|
|
83
|
+
ctx.connection.rpc.handle('/memory-peers', async (_endpoint, _payload, _signal) => ({
|
|
84
|
+
ok: true,
|
|
85
|
+
value: {
|
|
86
|
+
peers: await store.listPeers(),
|
|
87
|
+
mounts: live.sharing.mounts.map(m => ({ name: m.name, peer: m.peer, subpath: m.subpath, readonly: m.readonly })),
|
|
88
|
+
},
|
|
89
|
+
}), { authority: 'loopback' });
|
|
90
|
+
ctx.logger.info(`dsh-memory-lite: memory enabled (root ${live.root}, default peer ${live.defaultPeer}, extraction ${live.extraction.mode})`);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/inject.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L0 catalog injection: an `agent/pre-step` waterfall listener that publishes
|
|
3
|
+
* the peer's memory catalog as a durable user message, tracking visibility
|
|
4
|
+
* and digest exactly like `@deepseek-ai/dsh-tool-skill`.
|
|
5
|
+
* @module dsh-memory-lite/src/inject
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import type { PreStepDecision } from '@deepseek-ai/dsh-agent';
|
|
9
|
+
import type { UserMessage } from '@deepseek-ai/dsh-session';
|
|
10
|
+
import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
|
|
11
|
+
import type { MemoryDeps } from './tool-utils.js';
|
|
12
|
+
import type { IndexEntry } from './types.js';
|
|
13
|
+
import { type SharedMountNotice } from './catalog.js';
|
|
14
|
+
/** Inputs to the catalog decision; all derived state is resolved by the caller. */
|
|
15
|
+
export interface CatalogDecisionParams {
|
|
16
|
+
readonly decision: PreStepDecision;
|
|
17
|
+
readonly history: {
|
|
18
|
+
readonly visibleDigest?: string;
|
|
19
|
+
readonly published: boolean;
|
|
20
|
+
};
|
|
21
|
+
readonly existing: {
|
|
22
|
+
readonly message: UserMessage;
|
|
23
|
+
readonly entries: readonly IndexEntry[];
|
|
24
|
+
} | undefined;
|
|
25
|
+
readonly entries: readonly IndexEntry[];
|
|
26
|
+
/** Shared mounts to advertise (Phase 3); drives the empty-index exception. */
|
|
27
|
+
readonly sharedMounts: readonly SharedMountNotice[];
|
|
28
|
+
readonly peer: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Cap the injected catalog entries so the rendered catalog stays within
|
|
32
|
+
* `index.maxTokens` (recency-first: entries arrive newest-first, oldest are
|
|
33
|
+
* dropped from the injection only — the index file keeps them all and
|
|
34
|
+
* search_memory still finds them). This is the design's bounded L0 window.
|
|
35
|
+
*/
|
|
36
|
+
export declare function capCatalogEntries(entries: readonly IndexEntry[], maxTokens: number): readonly IndexEntry[];
|
|
37
|
+
/**
|
|
38
|
+
* Decide how the entering batch should carry the memory catalog. The branches
|
|
39
|
+
* mirror `@deepseek-ai/dsh-tool-skill`: publish on first sight, replace or
|
|
40
|
+
* drop a stale in-batch catalog, republish after compaction, and publish
|
|
41
|
+
* nothing for a fresh empty index.
|
|
42
|
+
*/
|
|
43
|
+
export declare function applyCatalogDecision(params: CatalogDecisionParams): PreStepDecision;
|
|
44
|
+
/** Register the pre-step catalog injector for the lifetime of `ctx`. */
|
|
45
|
+
export declare function applyMemoryCatalogInjection(ctx: Context, deps: MemoryDeps, readTool: ToolDefinition): void;
|
package/lib/inject.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L0 catalog injection: an `agent/pre-step` waterfall listener that publishes
|
|
3
|
+
* the peer's memory catalog as a durable user message, tracking visibility
|
|
4
|
+
* and digest exactly like `@deepseek-ai/dsh-tool-skill`.
|
|
5
|
+
* @module dsh-memory-lite/src/inject
|
|
6
|
+
*/
|
|
7
|
+
import { peerForHeader } from './peer.js';
|
|
8
|
+
import { digestApproxTokens } from './extract/digest.js';
|
|
9
|
+
import { catalogHistory, catalogMessage, digestIndexEntries, renderCatalogMessage, renderCatalogUpdate } from './catalog.js';
|
|
10
|
+
/** Fixed catalog overhead (framing + guidance + section headers), approximate tokens. */
|
|
11
|
+
const CATALOG_FIXED_TOKENS = 200;
|
|
12
|
+
/**
|
|
13
|
+
* Cap the injected catalog entries so the rendered catalog stays within
|
|
14
|
+
* `index.maxTokens` (recency-first: entries arrive newest-first, oldest are
|
|
15
|
+
* dropped from the injection only — the index file keeps them all and
|
|
16
|
+
* search_memory still finds them). This is the design's bounded L0 window.
|
|
17
|
+
*/
|
|
18
|
+
export function capCatalogEntries(entries, maxTokens) {
|
|
19
|
+
if (entries.length === 0)
|
|
20
|
+
return entries;
|
|
21
|
+
const budget = maxTokens - CATALOG_FIXED_TOKENS;
|
|
22
|
+
if (budget <= 0)
|
|
23
|
+
return [];
|
|
24
|
+
let used = 0;
|
|
25
|
+
const result = [];
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
const lineTokens = digestApproxTokens(`- ${entry.path}: ${entry.summary}\n`);
|
|
28
|
+
if (used + lineTokens > budget)
|
|
29
|
+
break;
|
|
30
|
+
result.push(entry);
|
|
31
|
+
used += lineTokens;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Decide how the entering batch should carry the memory catalog. The branches
|
|
37
|
+
* mirror `@deepseek-ai/dsh-tool-skill`: publish on first sight, replace or
|
|
38
|
+
* drop a stale in-batch catalog, republish after compaction, and publish
|
|
39
|
+
* nothing for a fresh empty index.
|
|
40
|
+
*/
|
|
41
|
+
export function applyCatalogDecision(params) {
|
|
42
|
+
const { decision, history, existing, entries, sharedMounts = [], peer } = params;
|
|
43
|
+
if (decision.kind === 'reject')
|
|
44
|
+
return decision;
|
|
45
|
+
const digest = digestIndexEntries(entries);
|
|
46
|
+
if (history.visibleDigest === digest) {
|
|
47
|
+
return existing === undefined
|
|
48
|
+
? decision
|
|
49
|
+
: { kind: 'enter', messages: decision.messages.filter(message => message.id !== existing.message.id) };
|
|
50
|
+
}
|
|
51
|
+
if (existing !== undefined && digestIndexEntries(existing.entries) === digest)
|
|
52
|
+
return decision;
|
|
53
|
+
// Phase 3: an empty local index still publishes a catalog when there are
|
|
54
|
+
// shared mounts to advertise (otherwise the shared region is undiscoverable).
|
|
55
|
+
if (!history.published && entries.length === 0 && sharedMounts.length === 0) {
|
|
56
|
+
return existing === undefined
|
|
57
|
+
? decision
|
|
58
|
+
: { kind: 'enter', messages: decision.messages.filter(message => message.id !== existing.message.id) };
|
|
59
|
+
}
|
|
60
|
+
const catalog = history.published
|
|
61
|
+
? renderCatalogUpdate(entries, peer, sharedMounts)
|
|
62
|
+
: renderCatalogMessage(entries, peer, sharedMounts);
|
|
63
|
+
return {
|
|
64
|
+
kind: 'enter',
|
|
65
|
+
messages: existing === undefined
|
|
66
|
+
? [...decision.messages, catalog]
|
|
67
|
+
: decision.messages.map(message => message.id === existing.message.id ? catalog : message),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Register the pre-step catalog injector for the lifetime of `ctx`. */
|
|
71
|
+
export function applyMemoryCatalogInjection(ctx, deps, readTool) {
|
|
72
|
+
ctx.on('agent/pre-step', async ({ agent, signal }, next) => {
|
|
73
|
+
const decision = await next();
|
|
74
|
+
if (decision.kind === 'reject')
|
|
75
|
+
return decision;
|
|
76
|
+
signal.throwIfAborted();
|
|
77
|
+
// Locked decision (IMPLEMENTATION.md §7): subagent sessions get no catalog.
|
|
78
|
+
if (agent.session.header.origin === 'subagent')
|
|
79
|
+
return decision;
|
|
80
|
+
// The anchor tool's exact definition is the visibility gate: a restriction
|
|
81
|
+
// or scoped shadow of read_memory removes both the schema and the catalog.
|
|
82
|
+
if (ctx.tools.get(readTool.name, agent) !== readTool)
|
|
83
|
+
return decision;
|
|
84
|
+
const config = deps.config();
|
|
85
|
+
const peer = peerForHeader(agent.session.header, config);
|
|
86
|
+
const entries = capCatalogEntries(await deps.store.readIndex(peer), config.index.maxTokens);
|
|
87
|
+
const sharedMounts = config.sharing.enabled
|
|
88
|
+
? config.sharing.mounts
|
|
89
|
+
.filter(mount => mount.peer !== peer)
|
|
90
|
+
.map(mount => ({ name: mount.name, peer: mount.peer, readonly: mount.readonly }))
|
|
91
|
+
: [];
|
|
92
|
+
return applyCatalogDecision({
|
|
93
|
+
decision,
|
|
94
|
+
history: catalogHistory(agent),
|
|
95
|
+
existing: catalogMessage(decision.messages),
|
|
96
|
+
entries,
|
|
97
|
+
sharedMounts,
|
|
98
|
+
peer,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=inject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject.js","sourceRoot":"","sources":["../src/inject.ts"],"names":[],"mappings":"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"}
|