@dsh-libing/memory-manager 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1,256 @@
1
+ /** Database authority. Every change is serialized and revisions/approvals commit atomically. */
2
+ import { DatabaseSync } from 'node:sqlite';
3
+ import { parentPort, workerData } from 'node:worker_threads';
4
+ import { join } from 'node:path';
5
+ import { randomUUID } from 'node:crypto';
6
+ import { z } from 'zod';
7
+ import { activitySchema, candidateSchema, draftSchema, entrySchema, jobSchema, mutationSchema, scopeSchema, settingsSchema, sourceSchema, visible, MemoryError } from "./shared.js";
8
+ const directory = z.string().parse(workerData), port = parentPort;
9
+ if (!port)
10
+ throw new Error('Database worker requires a message port');
11
+ const db = new DatabaseSync(join(directory, 'memory.sqlite'));
12
+ db.exec(`PRAGMA journal_mode=WAL; PRAGMA busy_timeout=3000;
13
+ CREATE TABLE IF NOT EXISTS records (id TEXT PRIMARY KEY, body TEXT NOT NULL);
14
+ CREATE TABLE IF NOT EXISTS versions (id TEXT NOT NULL, version INTEGER NOT NULL, body TEXT NOT NULL, PRIMARY KEY(id,version));
15
+ CREATE TABLE IF NOT EXISTS candidates (id TEXT PRIMARY KEY, body TEXT NOT NULL);
16
+ CREATE TABLE IF NOT EXISTS activities (id TEXT PRIMARY KEY, body TEXT NOT NULL);
17
+ CREATE TABLE IF NOT EXISTS jobs (id TEXT PRIMARY KEY, body TEXT NOT NULL);
18
+ CREATE TABLE IF NOT EXISTS budgets (day TEXT PRIMARY KEY, calls INTEGER NOT NULL);
19
+ CREATE TABLE IF NOT EXISTS config (id TEXT PRIMARY KEY, body TEXT NOT NULL);`);
20
+ const now = () => new Date().toISOString();
21
+ function list(table, schema) { return db.prepare(`SELECT body FROM ${table} ORDER BY rowid DESC`).all().map(row => schema.parse(JSON.parse(z.string().parse(row.body)))); }
22
+ function put(table, id, body) { db.prepare(`INSERT INTO ${table}(id,body) VALUES(?,?) ON CONFLICT(id) DO UPDATE SET body=excluded.body`).run(id, JSON.stringify(body)); }
23
+ function one(table, id, schema) { const row = db.prepare(`SELECT body FROM ${table} WHERE id=?`).get(id); if (!row)
24
+ throw new MemoryError('notFound', 404); return schema.parse(JSON.parse(z.string().parse(row.body))); }
25
+ function transaction(fn) { db.exec('BEGIN IMMEDIATE'); try {
26
+ const value = fn();
27
+ db.exec('COMMIT');
28
+ return value;
29
+ }
30
+ catch (error) {
31
+ db.exec('ROLLBACK');
32
+ throw error;
33
+ } }
34
+ function settings() { const row = db.prepare("SELECT body FROM config WHERE id='settings'").get(); return settingsSchema.parse(row ? JSON.parse(z.string().parse(row.body)) : {}); }
35
+ function checkScope(value, scope) { if (!visible(value, scope))
36
+ throw new MemoryError('outsideScope', 403); }
37
+ function save(value) { entrySchema.parse(value); put('records', value.id, value); db.prepare('INSERT INTO versions(id,version,body) VALUES(?,?,?)').run(value.id, value.version, JSON.stringify(value)); return value; }
38
+ const storedJobSchema = jobSchema.extend({ input: z.string().max(12000), source: sourceSchema, provider: z.string(), model: z.string() });
39
+ function proposed(raw) {
40
+ const args = z.object({ scope: scopeSchema, draft: draftSchema, source: sourceSchema, target: z.string().optional(), expected: z.number().int().optional(), id: z.string().optional() }).strict().parse(raw);
41
+ if (args.id) {
42
+ const found = db.prepare('SELECT body FROM candidates WHERE id=?').get(args.id);
43
+ if (found) {
44
+ const old = candidateSchema.parse(JSON.parse(z.string().parse(found.body)));
45
+ checkScope(old, args.scope);
46
+ if (JSON.stringify(old.draft) !== JSON.stringify(args.draft) || old.target !== (args.target ?? null) || old.expected !== (args.expected ?? 0))
47
+ throw new MemoryError('revisionConflict');
48
+ return old;
49
+ }
50
+ }
51
+ if (args.target) {
52
+ const target = one('records', args.target, entrySchema);
53
+ checkScope(target, args.scope);
54
+ if (target.state !== 'active' || target.version !== args.expected)
55
+ throw new MemoryError('revisionConflict');
56
+ }
57
+ if (list('candidates', candidateSchema).filter(row => row.state === 'pending').length >= 500)
58
+ throw new MemoryError('candidateLimit');
59
+ db.exec("DELETE FROM candidates WHERE rowid NOT IN (SELECT rowid FROM candidates ORDER BY rowid DESC LIMIT 2000) AND json_extract(body,'$.state') != 'pending'");
60
+ const item = { id: args.id ?? randomUUID(), revision: randomUUID(), target: args.target ?? null, expected: args.expected ?? 0, workspace: args.draft.range === 'personal' ? 'personal' : args.scope.workspace, session: args.draft.range === 'session' ? args.scope.session : null, draft: args.draft, source: args.source, state: 'pending', updated: now() };
61
+ if (item.draft.range === 'session' && !item.session)
62
+ throw new MemoryError('sessionRequired');
63
+ put('candidates', item.id, item);
64
+ return item;
65
+ }
66
+ function current(id, scope, version) { const value = one('records', id, entrySchema); checkScope(value, scope); if (value.version !== version)
67
+ throw new MemoryError('revisionConflict'); return value; }
68
+ function state(raw) {
69
+ const scope = scopeSchema.parse(raw);
70
+ return { entries: list('records', entrySchema).filter(row => visible(row, scope)), candidates: list('candidates', candidateSchema).filter(row => row.state === 'pending' && visible(row, scope)), activities: list('activities', activitySchema).filter(row => visible(row, scope)).slice(0, 200), jobs: list('jobs', storedJobSchema).filter(row => visible(row, scope)).map(({ input: _input, source: _source, provider: _provider, model: _model, ...row }) => row).slice(0, 100), settings: settings() };
71
+ }
72
+ function mutate(raw) {
73
+ const { scope, mutation, source } = z.object({ scope: scopeSchema, mutation: mutationSchema, source: sourceSchema }).strict().parse(raw);
74
+ const stamp = now();
75
+ if (mutation.action === 'settings') {
76
+ if (settings().revision !== mutation.settings.revision)
77
+ throw new MemoryError('revisionConflict');
78
+ const value = { ...mutation.settings, revision: mutation.settings.revision + 1 };
79
+ put('config', 'settings', value);
80
+ if (!value.autoCapture)
81
+ cancelJobs(true);
82
+ return value;
83
+ }
84
+ if (mutation.action === 'propose')
85
+ return proposed({ scope, source, draft: mutation.draft, ...mutation.target ? { target: mutation.target, expected: mutation.expected } : {} });
86
+ if (mutation.action === 'create') {
87
+ if (list('records', entrySchema).length >= 1000)
88
+ throw new MemoryError('memoryLimit');
89
+ if (mutation.draft.range === 'session' && !scope.session)
90
+ throw new MemoryError('sessionRequired');
91
+ return save({ ...mutation.draft, id: randomUUID(), version: 1, workspace: mutation.draft.range === 'personal' ? 'personal' : scope.workspace, session: mutation.draft.range === 'session' ? scope.session : null, source, state: 'active', updated: stamp });
92
+ }
93
+ if (mutation.action === 'approve' || mutation.action === 'reject') {
94
+ const row = one('candidates', mutation.id, candidateSchema);
95
+ checkScope(row, scope);
96
+ if (row.revision !== mutation.revision || row.state !== 'pending')
97
+ throw new MemoryError('revisionConflict');
98
+ if (mutation.action === 'reject') {
99
+ put('candidates', row.id, { ...row, state: 'rejected', updated: stamp });
100
+ return { committed: true };
101
+ }
102
+ const old = row.target ? current(row.target, scope, row.expected) : null;
103
+ if (old?.state === 'trash')
104
+ throw new MemoryError('revisionConflict');
105
+ if (!old && list('records', entrySchema).length >= 1000)
106
+ throw new MemoryError('memoryLimit');
107
+ const draft = mutation.draft;
108
+ if (draft.range === 'session' && !scope.session)
109
+ throw new MemoryError('sessionRequired');
110
+ const result = save({ ...draft, id: old?.id ?? randomUUID(), version: (old?.version ?? 0) + 1, workspace: draft.range === 'personal' ? 'personal' : scope.workspace, session: draft.range === 'session' ? scope.session : null, state: 'active', source: row.source, updated: stamp });
111
+ put('candidates', row.id, { ...row, draft, state: 'accepted', target: result.id, updated: stamp });
112
+ return result;
113
+ }
114
+ if (mutation.action === 'capture' || mutation.action === 'cancel')
115
+ throw new MemoryError('invalidOperation');
116
+ const old = current(mutation.id, scope, mutation.version);
117
+ if (mutation.action === 'purge') {
118
+ if (old.state !== 'trash' || mutation.confirmTitle !== old.title)
119
+ throw new MemoryError('purgeConfirmation');
120
+ db.prepare('DELETE FROM records WHERE id=?').run(old.id);
121
+ db.prepare('DELETE FROM versions WHERE id=?').run(old.id);
122
+ for (const row of list('candidates', candidateSchema))
123
+ if (row.target === old.id)
124
+ db.prepare('DELETE FROM candidates WHERE id=?').run(row.id);
125
+ for (const row of list('activities', activitySchema))
126
+ if (row.items.some(item => item.id === old.id))
127
+ put('activities', row.id, { ...row, items: row.items.map(item => item.id === old.id ? { ...item, title: '', summary: '', status: 'deleted' } : item) });
128
+ return { committed: true };
129
+ }
130
+ if ((mutation.action === 'trash' || mutation.action === 'toggle') && old.state !== 'active' || mutation.action === 'restore' && old.state !== 'trash')
131
+ throw new MemoryError('revisionConflict');
132
+ return save({ ...old, version: old.version + 1, state: mutation.action === 'trash' ? 'trash' : 'active', recall: mutation.action === 'toggle' ? !old.recall : old.recall, updated: stamp });
133
+ }
134
+ function cancelJobs(automatic) { return list('jobs', storedJobSchema).filter(row => (!automatic || row.automatic) && ['queued', 'running'].includes(row.state)).map(row => { put('jobs', row.id, { ...row, state: 'cancelled', input: '', reason: 'userCancelled', updated: now() }); return row.id; }); }
135
+ function operation(op, raw) {
136
+ if (op === 'ready') {
137
+ const { identity } = z.object({ identity: z.string() }).parse(raw), owner = db.prepare("SELECT body FROM config WHERE id='owner'").get();
138
+ if (owner && JSON.parse(z.string().parse(owner.body)) !== identity)
139
+ throw new MemoryError('profileMismatch');
140
+ if (!owner)
141
+ put('config', 'owner', identity);
142
+ for (const row of list('jobs', storedJobSchema))
143
+ if (row.state === 'queued' || row.state === 'running')
144
+ put('jobs', row.id, { ...row, state: 'interrupted', reason: 'restart', input: '', updated: now() });
145
+ for (const row of list('activities', activitySchema))
146
+ if (row.kind === 'prepared')
147
+ put('activities', row.id, { ...row, kind: 'notAdmitted', reason: 'restart', items: [], updated: now() });
148
+ for (const row of list('records', entrySchema)) {
149
+ const version = db.prepare('SELECT body FROM versions WHERE id=? AND version=?').get(row.id, row.version);
150
+ if (!version || JSON.stringify(entrySchema.parse(JSON.parse(z.string().parse(version.body)))) !== JSON.stringify(row))
151
+ throw new MemoryError('invariantViolation');
152
+ }
153
+ return null;
154
+ }
155
+ if (op === 'state')
156
+ return state(raw);
157
+ if (op === 'settings')
158
+ return settings();
159
+ if (op === 'mutate')
160
+ return transaction(() => mutate(raw));
161
+ if (op === 'propose')
162
+ return transaction(() => proposed(raw));
163
+ if (op === 'history') {
164
+ const args = z.object({ id: z.string(), scope: scopeSchema }).strict().parse(raw);
165
+ checkScope(one('records', args.id, entrySchema), args.scope);
166
+ return db.prepare('SELECT body FROM versions WHERE id=? ORDER BY version DESC LIMIT 100').all(args.id).map(row => entrySchema.parse(JSON.parse(z.string().parse(row.body)))).filter(row => visible(row, args.scope));
167
+ }
168
+ if (op === 'read') {
169
+ const args = z.object({ id: z.string(), version: z.number().int().optional(), scope: scopeSchema, history: z.boolean().default(false) }).strict().parse(raw), row = one('records', args.id, entrySchema);
170
+ checkScope(row, args.scope);
171
+ if (!args.history && (row.state !== 'active' || !row.recall))
172
+ throw new MemoryError('notReadable', 403);
173
+ const old = args.version && args.version !== row.version ? db.prepare('SELECT body FROM versions WHERE id=? AND version=?').get(row.id, args.version) : null;
174
+ const value = old ? entrySchema.parse(JSON.parse(z.string().parse(old.body))) : row;
175
+ if (args.version && value.version !== args.version)
176
+ throw new MemoryError('notFound', 404);
177
+ checkScope(value, args.scope);
178
+ if (!args.history && value.state !== 'active')
179
+ throw new MemoryError('notReadable', 403);
180
+ return value;
181
+ }
182
+ if (op === 'activity') {
183
+ const parsed = activitySchema.parse(raw), row = { ...parsed, items: parsed.items.map(item => db.prepare('SELECT id FROM records WHERE id=?').get(item.id) ? item : { ...item, title: '', summary: '', status: 'deleted' }) };
184
+ put('activities', row.id, row);
185
+ db.exec('DELETE FROM activities WHERE rowid NOT IN (SELECT rowid FROM activities ORDER BY rowid DESC LIMIT 2000)');
186
+ return row;
187
+ }
188
+ if (op === 'settle') {
189
+ const args = z.object({ scope: scopeSchema, turn: z.number(), reason: z.string() }).parse(raw);
190
+ for (const row of list('activities', activitySchema))
191
+ if (row.session === args.scope.session && row.turn === args.turn && row.kind === 'prepared')
192
+ put('activities', row.id, { ...row, kind: 'notAdmitted', reason: args.reason, items: [], updated: now() });
193
+ return null;
194
+ }
195
+ if (op === 'enqueue')
196
+ return transaction(() => {
197
+ const job = storedJobSchema.parse(raw), duplicate = db.prepare('SELECT body FROM jobs WHERE id=?').get(job.id);
198
+ if (duplicate)
199
+ return jobSchema.parse(JSON.parse(z.string().parse(duplicate.body)));
200
+ const all = list('jobs', storedJobSchema), config = settings();
201
+ if (all.filter(row => ['queued', 'running'].includes(row.state)).length >= 20)
202
+ throw new MemoryError('queueFull');
203
+ const day = now().slice(0, 10), budget = db.prepare('SELECT calls FROM budgets WHERE day=?').get(day);
204
+ const count = budget ? z.number().parse(budget.calls) : 0;
205
+ const value = job.automatic && !config.autoCapture ? { ...job, state: 'skipped', reason: 'autoCaptureOff', input: '' } : count >= config.maxDailyCalls ? { ...job, state: 'skipped', reason: 'dailyBudget', input: '' } : job;
206
+ if (value.state !== 'skipped')
207
+ db.prepare('INSERT INTO budgets(day,calls) VALUES(?,1) ON CONFLICT(day) DO UPDATE SET calls=calls+1').run(day);
208
+ db.exec("DELETE FROM jobs WHERE rowid NOT IN (SELECT rowid FROM jobs ORDER BY rowid DESC LIMIT 2000) AND json_extract(body,'$.state') NOT IN ('queued','running')");
209
+ put('jobs', job.id, value);
210
+ return jobSchema.parse(value);
211
+ });
212
+ if (op === 'next') {
213
+ const job = list('jobs', storedJobSchema).reverse().find(row => row.state === 'queued');
214
+ if (!job)
215
+ return null;
216
+ const value = { ...job, state: 'running', updated: now() };
217
+ put('jobs', job.id, value);
218
+ return value;
219
+ }
220
+ if (op === 'finish')
221
+ return transaction(() => {
222
+ const args = z.object({ id: z.string(), state: z.enum(['done', 'failed', 'cancelled']), reason: z.string(), drafts: z.array(draftSchema).max(4) }).strict().parse(raw), job = one('jobs', args.id, storedJobSchema);
223
+ if (job.state !== 'running')
224
+ return jobSchema.parse(job);
225
+ const scope = { workspace: job.workspace, session: job.session };
226
+ const ids = args.state === 'done' ? args.drafts.map((draft, index) => proposed({ scope, draft: { ...draft, range: 'workspace' }, source: job.source, id: `${job.id}:${index}` }).id) : [];
227
+ const value = { ...job, state: args.state, reason: args.reason, candidateIds: ids, input: '', updated: now() };
228
+ put('jobs', job.id, value);
229
+ return jobSchema.parse(value);
230
+ });
231
+ if (op === 'cancel') {
232
+ const args = z.object({ id: z.string(), scope: scopeSchema }).parse(raw), row = one('jobs', args.id, storedJobSchema);
233
+ checkScope(row, args.scope);
234
+ if (!['queued', 'running'].includes(row.state))
235
+ return jobSchema.parse(row);
236
+ const value = { ...row, state: 'cancelled', input: '', reason: 'userCancelled', updated: now() };
237
+ put('jobs', row.id, value);
238
+ return jobSchema.parse(value);
239
+ }
240
+ if (op === 'cancelAll')
241
+ return transaction(() => cancelJobs(false));
242
+ if (op === 'close') {
243
+ db.close();
244
+ return null;
245
+ }
246
+ throw new MemoryError('invalidOperation');
247
+ }
248
+ port.on('message', (raw) => {
249
+ const request = z.object({ id: z.number(), op: z.string(), args: z.unknown() }).parse(raw);
250
+ try {
251
+ port.postMessage({ id: request.id, value: operation(request.op, request.args) });
252
+ }
253
+ catch (error) {
254
+ port.postMessage({ id: request.id, error: error instanceof MemoryError ? error.code : 'storageFailed', status: error instanceof MemoryError ? error.status : 503 });
255
+ }
256
+ });
package/lib/store.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod';
2
+ export declare class Store {
3
+ readonly ready: Promise<void>;
4
+ private worker?;
5
+ private root;
6
+ private id;
7
+ private closing;
8
+ private owned;
9
+ private pending;
10
+ constructor(directory: string, profileDirectory?: string);
11
+ private start;
12
+ private request;
13
+ call<T>(op: string, args: unknown, schema: z.ZodType<T>): Promise<T>;
14
+ close(): Promise<void>;
15
+ }
package/lib/store.js ADDED
@@ -0,0 +1,113 @@
1
+ /** One worker owns SQLite; all responses cross a schema checked boundary. */
2
+ import { Worker } from 'node:worker_threads';
3
+ import { mkdir, realpath, open, unlink, readFile } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { z } from 'zod';
6
+ import { createHash } from 'node:crypto';
7
+ import { MemoryError } from "./shared.js";
8
+ const roots = new Set();
9
+ export class Store {
10
+ ready;
11
+ worker;
12
+ root = '';
13
+ id = 0;
14
+ closing = false;
15
+ owned = false;
16
+ pending = new Map();
17
+ constructor(directory, profileDirectory) { this.ready = this.start(directory, profileDirectory); }
18
+ async start(directory, profileDirectory) {
19
+ await mkdir(directory, { recursive: true, mode: 0o700 });
20
+ this.root = await realpath(directory);
21
+ if (roots.has(this.root))
22
+ throw new MemoryError('dataRootBusy');
23
+ roots.add(this.root);
24
+ try {
25
+ // Kernel lock file is process-labelled; a live foreign owner is never stolen.
26
+ const lock = join(this.root, 'owner.lock');
27
+ const claim = async () => { const file = await open(lock, 'wx', 0o600); try {
28
+ await file.writeFile(String(process.pid));
29
+ }
30
+ finally {
31
+ await file.close();
32
+ } };
33
+ try {
34
+ await claim();
35
+ }
36
+ catch (error) {
37
+ if (!(error instanceof Error && 'code' in error && error.code === 'EEXIST'))
38
+ throw error;
39
+ const pid = Number(await readFile(lock, 'utf8'));
40
+ if (!Number.isSafeInteger(pid) || pid <= 0)
41
+ throw new MemoryError('dataRootBusy');
42
+ try {
43
+ process.kill(pid, 0);
44
+ throw new MemoryError('dataRootBusy');
45
+ }
46
+ catch (reason) {
47
+ if (!(reason instanceof Error && 'code' in reason && reason.code === 'ESRCH'))
48
+ throw reason;
49
+ }
50
+ // Only one stale-owner recoverer may remove the dead owner's file.
51
+ const guard = await open(join(this.root, 'owner.recover'), 'wx', 0o600);
52
+ try {
53
+ if (Number(await readFile(lock, 'utf8')) !== pid)
54
+ throw new MemoryError('dataRootBusy');
55
+ await unlink(lock);
56
+ await claim();
57
+ }
58
+ finally {
59
+ await guard.close();
60
+ await unlink(join(this.root, 'owner.recover'));
61
+ }
62
+ }
63
+ this.owned = true;
64
+ }
65
+ catch (error) {
66
+ roots.delete(this.root);
67
+ throw error;
68
+ }
69
+ const worker = this.worker = new Worker(new URL(import.meta.url.endsWith('.ts') ? './storage-worker.ts' : './storage-worker.js', import.meta.url), { workerData: this.root });
70
+ worker.on('message', (raw) => {
71
+ const reply = z.object({ id: z.number(), value: z.unknown().optional(), error: z.string().optional(), status: z.number().optional() }).parse(raw), slot = this.pending.get(reply.id);
72
+ if (!slot)
73
+ return;
74
+ this.pending.delete(reply.id);
75
+ if (reply.error)
76
+ slot.reject(new MemoryError(reply.error, reply.status ?? 503));
77
+ else
78
+ slot.resolve(reply.value);
79
+ });
80
+ const fail = (error) => { this.closing = true; for (const slot of this.pending.values())
81
+ slot.reject(error); this.pending.clear(); };
82
+ worker.on('error', fail);
83
+ worker.on('exit', code => { if (this.pending.size)
84
+ fail(new Error(`Database worker exited: ${code}`)); });
85
+ const identity = profileDirectory ? createHash('sha256').update(await realpath(profileDirectory)).digest('hex') : 'test';
86
+ await this.request('ready', { identity });
87
+ }
88
+ request(op, args) {
89
+ if (this.closing || !this.worker)
90
+ return Promise.reject(new MemoryError('closed', 503));
91
+ if (this.pending.size >= 128)
92
+ return Promise.reject(new MemoryError('busy', 503));
93
+ const id = ++this.id;
94
+ return new Promise((resolve, reject) => { this.pending.set(id, { resolve, reject }); this.worker?.postMessage({ id, op, args }); });
95
+ }
96
+ async call(op, args, schema) { await this.ready; return schema.parse(await this.request(op, args)); }
97
+ async close() {
98
+ try {
99
+ await this.ready;
100
+ if (!this.closing)
101
+ await this.request('close', {});
102
+ }
103
+ finally {
104
+ this.closing = true;
105
+ await this.worker?.terminate();
106
+ if (this.owned) {
107
+ this.owned = false;
108
+ roots.delete(this.root);
109
+ await unlink(join(this.root, 'owner.lock'));
110
+ }
111
+ }
112
+ }
113
+ }
package/package.json CHANGED
@@ -1,6 +1,126 @@
1
1
  {
2
2
  "name": "@dsh-libing/memory-manager",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "记忆中心:候选复核、按会话召回与可追溯的记忆管理",
5
+ "type": "module",
6
+ "icon": "icon.svg",
7
+ "main": "lib/index.js",
8
+ "types": "lib/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./lib/index.d.ts",
12
+ "default": "./lib/index.js"
13
+ },
14
+ "./client": {
15
+ "types": "./lib/client-types/client/index.d.ts",
16
+ "default": "./lib/client.js"
17
+ },
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "icon.svg",
22
+ "lib",
23
+ "cordis.patch.yml",
24
+ "README.md",
25
+ "CHANGELOG.md"
26
+ ],
27
+ "license": "UNLICENSED",
28
+ "engines": {
29
+ "node": "^22.19.0 || >=24.0.0"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://gitcode.com/LiBing-DSH/dsh-libing.git",
34
+ "directory": "plugins/memory-manager"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "registry": "https://registry.npmjs.org/"
39
+ },
40
+ "dependencies": {
41
+ "zod": "4.4.3"
42
+ },
43
+ "peerDependencies": {
44
+ "@deepseek-ai/cordis": "4.0.4",
45
+ "@deepseek-ai/dsh-app-boot": "0.2.0-rc.2",
46
+ "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
47
+ "@deepseek-ai/dsh-session": "0.2.0-rc.2",
48
+ "@deepseek-ai/dsh-session-query": "0.2.0-rc.2",
49
+ "@deepseek-ai/dsh-workspace": "0.2.0-rc.2",
50
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
51
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
52
+ "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
53
+ "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
54
+ "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
55
+ "@deepseek-ai/dsh-client-ui-layout": "0.2.0-rc.2",
56
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-rc.2",
57
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
58
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
59
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
60
+ "@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.2",
61
+ "@deepseek-ai/dsh-client-ui-chat": "0.2.0-rc.2",
62
+ "@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
63
+ "@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
64
+ "@deepseek-ai/dsh-client-ui-workspace": "0.2.0-rc.2",
65
+ "@deepseek-ai/dsh-agent-default-model": "0.2.0-rc.2"
66
+ },
67
+ "devDependencies": {
68
+ "@deepseek-ai/cordis": "4.0.4",
69
+ "@deepseek-ai/dsh-app-boot": "0.2.0-rc.2",
70
+ "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
71
+ "@deepseek-ai/dsh-session": "0.2.0-rc.2",
72
+ "@deepseek-ai/dsh-session-query": "0.2.0-rc.2",
73
+ "@deepseek-ai/dsh-workspace": "0.2.0-rc.2",
74
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
75
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
76
+ "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
77
+ "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
78
+ "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
79
+ "@deepseek-ai/dsh-client-ui-layout": "0.2.0-rc.2",
80
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-rc.2",
81
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
82
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
83
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
84
+ "@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.2",
85
+ "@deepseek-ai/dsh-client-ui-chat": "0.2.0-rc.2",
86
+ "@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
87
+ "@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
88
+ "@deepseek-ai/dsh-client-ui-workspace": "0.2.0-rc.2",
89
+ "@deepseek-ai/dsh-agent-default-model": "0.2.0-rc.2"
90
+ },
91
+ "dsh": {
92
+ "bundle": {
93
+ "patch": "./cordis.patch.yml"
94
+ },
95
+ "client": {
96
+ "platform": "web",
97
+ "inject": [
98
+ "@deepseek-ai/dsh-client-locale",
99
+ "@deepseek-ai/dsh-client-ui-layout",
100
+ "@deepseek-ai/dsh-client-ui-sidebar",
101
+ "@deepseek-ai/dsh-client-ui-conversation",
102
+ "@deepseek-ai/dsh-client-ui-chat",
103
+ "@deepseek-ai/dsh-client-ui-workspace"
104
+ ]
105
+ },
106
+ "market": {
107
+ "displayName": {
108
+ "zh": "记忆中心",
109
+ "en": "Memory Center"
110
+ },
111
+ "category": "utilities",
112
+ "icon": "icon.svg",
113
+ "maintainers": [
114
+ "@lujinxing"
115
+ ],
116
+ "compatibility": {
117
+ "dsh": "0.2.0-rc.2"
118
+ },
119
+ "allowedBuilds": []
120
+ }
121
+ },
122
+ "scripts": {
123
+ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.client.build.json && node --import tsx/esm ../../scripts/build-client.ts memory-manager",
124
+ "typecheck:client": "tsc -p tsconfig.client.json --noEmit"
125
+ }
6
126
  }