dsh-tdai-memory 0.2.13 → 0.3.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.
Files changed (72) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +15 -13
  3. package/README.zh.md +9 -7
  4. package/THIRD-PARTY-NOTICES.md +20 -20
  5. package/client.js +21 -4
  6. package/cordis.patch.yml +8 -8
  7. package/index.js +36 -14
  8. package/package.json +77 -78
  9. package/recall-inject.js +86 -86
  10. package/vendor/tdai/adapters/index.js +15 -15
  11. package/vendor/tdai/adapters/openclaw/host-adapter.js +83 -83
  12. package/vendor/tdai/adapters/openclaw/index.js +5 -5
  13. package/vendor/tdai/adapters/openclaw/llm-runner.js +69 -69
  14. package/vendor/tdai/adapters/standalone/host-adapter.js +58 -58
  15. package/vendor/tdai/adapters/standalone/index.js +5 -5
  16. package/vendor/tdai/adapters/standalone/llm-runner.js +34 -6
  17. package/vendor/tdai/config.js +3 -0
  18. package/vendor/tdai/core/conversation/l0-recorder.js +420 -420
  19. package/vendor/tdai/core/hooks/auto-capture.js +236 -236
  20. package/vendor/tdai/core/hooks/auto-recall.js +640 -640
  21. package/vendor/tdai/core/index.js +8 -8
  22. package/vendor/tdai/core/persona/persona-generator.js +184 -184
  23. package/vendor/tdai/core/persona/persona-trigger.js +94 -94
  24. package/vendor/tdai/core/profile/profile-sync.js +200 -200
  25. package/vendor/tdai/core/prompts/l1-dedup.js +72 -72
  26. package/vendor/tdai/core/prompts/l1-extraction.js +34 -34
  27. package/vendor/tdai/core/prompts/persona-generation.js +35 -35
  28. package/vendor/tdai/core/prompts/scene-extraction.js +45 -45
  29. package/vendor/tdai/core/record/l1-dedup.js +318 -318
  30. package/vendor/tdai/core/record/l1-extractor.js +379 -379
  31. package/vendor/tdai/core/record/l1-reader.js +178 -178
  32. package/vendor/tdai/core/record/l1-writer.js +141 -141
  33. package/vendor/tdai/core/report/reporter.js +86 -86
  34. package/vendor/tdai/core/scene/filename-normalizer.js +161 -161
  35. package/vendor/tdai/core/scene/scene-extractor.js +410 -410
  36. package/vendor/tdai/core/scene/scene-format.js +53 -53
  37. package/vendor/tdai/core/scene/scene-index.js +82 -82
  38. package/vendor/tdai/core/scene/scene-navigation.js +57 -57
  39. package/vendor/tdai/core/seed/input.js +404 -404
  40. package/vendor/tdai/core/seed/seed-runtime.js +4 -0
  41. package/vendor/tdai/core/seed/types.js +9 -9
  42. package/vendor/tdai/core/store/bm25-client.js +117 -117
  43. package/vendor/tdai/core/store/bm25-local.js +68 -68
  44. package/vendor/tdai/core/store/embedding.js +632 -632
  45. package/vendor/tdai/core/store/factory.js +95 -95
  46. package/vendor/tdai/core/store/search-utils.js +54 -54
  47. package/vendor/tdai/core/store/sqlite.js +1632 -1632
  48. package/vendor/tdai/core/store/tcvdb-client.js +221 -221
  49. package/vendor/tdai/core/store/tcvdb.js +1047 -1047
  50. package/vendor/tdai/core/store/types.js +17 -17
  51. package/vendor/tdai/core/tdai-core.js +4 -0
  52. package/vendor/tdai/core/tools/conversation-search.js +199 -199
  53. package/vendor/tdai/core/tools/memory-search.js +207 -207
  54. package/vendor/tdai/core/types.js +12 -12
  55. package/vendor/tdai/utils/backup.js +189 -189
  56. package/vendor/tdai/utils/checkpoint.js +360 -360
  57. package/vendor/tdai/utils/clean-context-runner.js +472 -472
  58. package/vendor/tdai/utils/ensure-hook-policy.js +213 -213
  59. package/vendor/tdai/utils/env.js +13 -13
  60. package/vendor/tdai/utils/managed-timer.js +127 -127
  61. package/vendor/tdai/utils/manifest.js +91 -91
  62. package/vendor/tdai/utils/memory-cleaner.js +300 -300
  63. package/vendor/tdai/utils/no-think-fetch.js +109 -109
  64. package/vendor/tdai/utils/openclaw-state-dir.js +26 -26
  65. package/vendor/tdai/utils/pipeline-factory.js +517 -517
  66. package/vendor/tdai/utils/pipeline-manager.js +892 -892
  67. package/vendor/tdai/utils/sanitize.js +360 -360
  68. package/vendor/tdai/utils/serial-queue.js +103 -103
  69. package/vendor/tdai/utils/session-filter.js +88 -88
  70. package/vendor/tdai/utils/session-header-fetch.js +88 -0
  71. package/vendor/tdai/utils/text-utils.js +29 -29
  72. package/vendor/tdai/utils/time.js +252 -252
@@ -1,221 +1,221 @@
1
- /**
2
- * Tencent Cloud VectorDB HTTP Client.
3
- *
4
- * Thin wrapper around the VectorDB HTTP API. Handles authentication, timeouts,
5
- * retries (5xx / timeout), and error normalization.
6
- *
7
- * API docs: https://cloud.tencent.com/document/product/1709
8
- */
9
- import fs from "node:fs";
10
- import { request as undiciRequest, Agent as UndiciAgent } from "undici";
11
- export class TcvdbApiError extends Error {
12
- apiCode;
13
- constructor(path, code, msg) {
14
- super(`VectorDB ${path}: code=${code}, msg=${msg}`);
15
- this.name = "TcvdbApiError";
16
- this.apiCode = code;
17
- }
18
- }
19
- // ============================
20
- // Client
21
- // ============================
22
- const TAG = "[memory-tdai][tcvdb-client]";
23
- const MAX_RETRIES = 2;
24
- export class TcvdbClient {
25
- baseUrl;
26
- authHeader;
27
- database;
28
- timeout;
29
- logger;
30
- /** undici dispatcher for HTTPS + custom CA. */
31
- dispatcher;
32
- constructor(config, logger) {
33
- this.baseUrl = config.url.replace(/\/+$/, "");
34
- this.authHeader = `Bearer account=${config.username}&api_key=${config.apiKey}`;
35
- this.database = config.database;
36
- this.timeout = config.timeout;
37
- this.logger = logger;
38
- // Log connection info at construction time.
39
- this.logger?.debug?.(`${TAG} url=${this.baseUrl} db=${this.database} timeout=${this.timeout}${this.baseUrl.startsWith("https://") ? ` https=true caPemPath=${config.caPemPath ?? "(none)"}` : ""}`);
40
- // For HTTPS with a custom CA certificate, create a dedicated undici Agent.
41
- // We use undici.request() instead of global fetch because fetch's
42
- // `dispatcher` option is unreliable across Node versions.
43
- if (this.baseUrl.startsWith("https://") && config.caPemPath) {
44
- try {
45
- const ca = fs.readFileSync(config.caPemPath, "utf-8");
46
- this.dispatcher = new UndiciAgent({ connect: { ca } });
47
- this.logger?.debug?.(`${TAG} HTTPS enabled with CA from ${config.caPemPath}`);
48
- }
49
- catch (err) {
50
- this.logger?.error(`${TAG} Failed to load CA PEM from ${config.caPemPath}: ${err instanceof Error ? err.message : String(err)}`);
51
- }
52
- }
53
- }
54
- // ── Generic request ─────────────────────────────────────
55
- /**
56
- * Send a POST request to VectorDB API.
57
- * Handles auth, timeout, retries (5xx/timeout), and error unwrapping.
58
- */
59
- async request(path, body) {
60
- let lastError;
61
- const t0 = performance.now();
62
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
63
- const tAttempt = performance.now();
64
- try {
65
- this.logger?.debug?.(`${TAG} → ${path} attempt=${attempt} body=${JSON.stringify(body).slice(0, 500)}`);
66
- const { statusCode, body: respBody } = await undiciRequest(`${this.baseUrl}${path}`, {
67
- method: "POST",
68
- headers: {
69
- "Content-Type": "application/json",
70
- "Authorization": this.authHeader,
71
- },
72
- body: JSON.stringify(body),
73
- signal: AbortSignal.timeout(this.timeout),
74
- ...(this.dispatcher ? { dispatcher: this.dispatcher } : {}),
75
- });
76
- const text = await respBody.text();
77
- const json = JSON.parse(text);
78
- const attemptMs = Math.round(performance.now() - tAttempt);
79
- this.logger?.debug?.(`${TAG} ← ${path} status=${statusCode} code=${json.code} attemptMs=${attemptMs} attempt=${attempt}`);
80
- if (json.code !== 0) {
81
- const err = new TcvdbApiError(path, json.code, json.msg);
82
- if (statusCode !== undefined && statusCode >= 400 && statusCode < 500)
83
- throw err;
84
- lastError = err;
85
- continue;
86
- }
87
- // Always log completion at info level (one line per request)
88
- const totalMs = Math.round(performance.now() - t0);
89
- this.logger?.info(`${TAG} ${path} ${totalMs}ms${attempt > 0 ? ` (${attempt + 1} attempts)` : ""}`);
90
- return json;
91
- }
92
- catch (err) {
93
- const attemptMs = Math.round(performance.now() - tAttempt);
94
- if (err instanceof TcvdbApiError && err.apiCode !== 0)
95
- throw err;
96
- lastError = err instanceof Error ? err : new Error(String(err));
97
- if (attempt < MAX_RETRIES) {
98
- const delay = 500 * (attempt + 1);
99
- this.logger?.debug?.(`${TAG} ${path} retry ${attempt + 1}/${MAX_RETRIES} in ${delay}ms (lastAttemptMs=${attemptMs}, error=${lastError.message})`);
100
- await new Promise((r) => setTimeout(r, delay));
101
- }
102
- }
103
- }
104
- const totalMs = Math.round(performance.now() - t0);
105
- this.logger?.debug?.(`${TAG} ✗ ${path} totalMs=${totalMs} attempts=${MAX_RETRIES + 1} error=${lastError?.message}`);
106
- throw lastError ?? new Error(`${TAG} ${path} failed after retries`);
107
- }
108
- // ── Database operations ─────────────────────────────────
109
- async createDatabase(dbName) {
110
- const name = dbName ?? this.database;
111
- // SDK pattern: list first, create only if not found
112
- const listResp = await this.request("/database/list", {});
113
- const exists = (listResp.databases ?? []).includes(name);
114
- if (exists) {
115
- this.logger?.debug?.(`${TAG} Database already exists: ${name}`);
116
- return false;
117
- }
118
- await this.request("/database/create", { database: name });
119
- this.logger?.info(`${TAG} Database created: ${name}`);
120
- return true;
121
- }
122
- // ── Collection operations ───────────────────────────────
123
- async createCollection(params) {
124
- const name = String(params.collection ?? "");
125
- // SDK pattern: try describe first, create only if not found (code 15302)
126
- try {
127
- await this.describeCollection(name);
128
- this.logger?.debug?.(`${TAG} Collection already exists: ${name}`);
129
- return;
130
- }
131
- catch (err) {
132
- if (!(err instanceof TcvdbApiError && err.apiCode === 15302)) {
133
- throw err; // unexpected error
134
- }
135
- // 15302 = collection not found → proceed to create
136
- }
137
- try {
138
- await this.request("/collection/create", {
139
- database: this.database,
140
- ...params,
141
- });
142
- this.logger?.info(`${TAG} Collection created: ${name}`);
143
- }
144
- catch (err) {
145
- // 15202 = collection already exists — race between describe and create.
146
- // Semantically identical to "describe found it", so treat as success.
147
- if (err instanceof TcvdbApiError && err.apiCode === 15202) {
148
- this.logger?.debug?.(`${TAG} Collection already exists (race): ${name}`);
149
- return;
150
- }
151
- throw err;
152
- }
153
- }
154
- async describeCollection(collection) {
155
- const resp = await this.request("/collection/describe", {
156
- database: this.database,
157
- collection,
158
- });
159
- return resp.collection;
160
- }
161
- // ── Document operations ─────────────────────────────────
162
- async upsert(collection, documents) {
163
- await this.request("/document/upsert", {
164
- database: this.database,
165
- collection,
166
- buildIndex: true,
167
- documents,
168
- });
169
- }
170
- async search(collection, searchParams) {
171
- return this.request("/document/search", {
172
- database: this.database,
173
- collection,
174
- readConsistency: "strongConsistency",
175
- search: searchParams,
176
- });
177
- }
178
- async hybridSearch(collection, searchParams) {
179
- return this.request("/document/hybridSearch", {
180
- database: this.database,
181
- collection,
182
- readConsistency: "strongConsistency",
183
- search: searchParams,
184
- });
185
- }
186
- async query(collection, queryParams) {
187
- return this.request("/document/query", {
188
- database: this.database,
189
- collection,
190
- readConsistency: "strongConsistency",
191
- query: queryParams,
192
- });
193
- }
194
- async deleteDoc(collection, params) {
195
- await this.request("/document/delete", {
196
- database: this.database,
197
- collection,
198
- ...params,
199
- });
200
- }
201
- /**
202
- * Count documents matching an optional filter.
203
- * Uses the dedicated /document/count endpoint.
204
- */
205
- async count(collection, filter) {
206
- const query = {};
207
- if (filter)
208
- query.filter = filter;
209
- const resp = await this.request("/document/count", {
210
- database: this.database,
211
- collection,
212
- readConsistency: "strongConsistency",
213
- query,
214
- });
215
- return resp.count ?? 0;
216
- }
217
- // ── Convenience getters ─────────────────────────────────
218
- getDatabase() {
219
- return this.database;
220
- }
221
- }
1
+ /**
2
+ * Tencent Cloud VectorDB HTTP Client.
3
+ *
4
+ * Thin wrapper around the VectorDB HTTP API. Handles authentication, timeouts,
5
+ * retries (5xx / timeout), and error normalization.
6
+ *
7
+ * API docs: https://cloud.tencent.com/document/product/1709
8
+ */
9
+ import fs from "node:fs";
10
+ import { request as undiciRequest, Agent as UndiciAgent } from "undici";
11
+ export class TcvdbApiError extends Error {
12
+ apiCode;
13
+ constructor(path, code, msg) {
14
+ super(`VectorDB ${path}: code=${code}, msg=${msg}`);
15
+ this.name = "TcvdbApiError";
16
+ this.apiCode = code;
17
+ }
18
+ }
19
+ // ============================
20
+ // Client
21
+ // ============================
22
+ const TAG = "[memory-tdai][tcvdb-client]";
23
+ const MAX_RETRIES = 2;
24
+ export class TcvdbClient {
25
+ baseUrl;
26
+ authHeader;
27
+ database;
28
+ timeout;
29
+ logger;
30
+ /** undici dispatcher for HTTPS + custom CA. */
31
+ dispatcher;
32
+ constructor(config, logger) {
33
+ this.baseUrl = config.url.replace(/\/+$/, "");
34
+ this.authHeader = `Bearer account=${config.username}&api_key=${config.apiKey}`;
35
+ this.database = config.database;
36
+ this.timeout = config.timeout;
37
+ this.logger = logger;
38
+ // Log connection info at construction time.
39
+ this.logger?.debug?.(`${TAG} url=${this.baseUrl} db=${this.database} timeout=${this.timeout}${this.baseUrl.startsWith("https://") ? ` https=true caPemPath=${config.caPemPath ?? "(none)"}` : ""}`);
40
+ // For HTTPS with a custom CA certificate, create a dedicated undici Agent.
41
+ // We use undici.request() instead of global fetch because fetch's
42
+ // `dispatcher` option is unreliable across Node versions.
43
+ if (this.baseUrl.startsWith("https://") && config.caPemPath) {
44
+ try {
45
+ const ca = fs.readFileSync(config.caPemPath, "utf-8");
46
+ this.dispatcher = new UndiciAgent({ connect: { ca } });
47
+ this.logger?.debug?.(`${TAG} HTTPS enabled with CA from ${config.caPemPath}`);
48
+ }
49
+ catch (err) {
50
+ this.logger?.error(`${TAG} Failed to load CA PEM from ${config.caPemPath}: ${err instanceof Error ? err.message : String(err)}`);
51
+ }
52
+ }
53
+ }
54
+ // ── Generic request ─────────────────────────────────────
55
+ /**
56
+ * Send a POST request to VectorDB API.
57
+ * Handles auth, timeout, retries (5xx/timeout), and error unwrapping.
58
+ */
59
+ async request(path, body) {
60
+ let lastError;
61
+ const t0 = performance.now();
62
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
63
+ const tAttempt = performance.now();
64
+ try {
65
+ this.logger?.debug?.(`${TAG} → ${path} attempt=${attempt} body=${JSON.stringify(body).slice(0, 500)}`);
66
+ const { statusCode, body: respBody } = await undiciRequest(`${this.baseUrl}${path}`, {
67
+ method: "POST",
68
+ headers: {
69
+ "Content-Type": "application/json",
70
+ "Authorization": this.authHeader,
71
+ },
72
+ body: JSON.stringify(body),
73
+ signal: AbortSignal.timeout(this.timeout),
74
+ ...(this.dispatcher ? { dispatcher: this.dispatcher } : {}),
75
+ });
76
+ const text = await respBody.text();
77
+ const json = JSON.parse(text);
78
+ const attemptMs = Math.round(performance.now() - tAttempt);
79
+ this.logger?.debug?.(`${TAG} ← ${path} status=${statusCode} code=${json.code} attemptMs=${attemptMs} attempt=${attempt}`);
80
+ if (json.code !== 0) {
81
+ const err = new TcvdbApiError(path, json.code, json.msg);
82
+ if (statusCode !== undefined && statusCode >= 400 && statusCode < 500)
83
+ throw err;
84
+ lastError = err;
85
+ continue;
86
+ }
87
+ // Always log completion at info level (one line per request)
88
+ const totalMs = Math.round(performance.now() - t0);
89
+ this.logger?.info(`${TAG} ${path} ${totalMs}ms${attempt > 0 ? ` (${attempt + 1} attempts)` : ""}`);
90
+ return json;
91
+ }
92
+ catch (err) {
93
+ const attemptMs = Math.round(performance.now() - tAttempt);
94
+ if (err instanceof TcvdbApiError && err.apiCode !== 0)
95
+ throw err;
96
+ lastError = err instanceof Error ? err : new Error(String(err));
97
+ if (attempt < MAX_RETRIES) {
98
+ const delay = 500 * (attempt + 1);
99
+ this.logger?.debug?.(`${TAG} ${path} retry ${attempt + 1}/${MAX_RETRIES} in ${delay}ms (lastAttemptMs=${attemptMs}, error=${lastError.message})`);
100
+ await new Promise((r) => setTimeout(r, delay));
101
+ }
102
+ }
103
+ }
104
+ const totalMs = Math.round(performance.now() - t0);
105
+ this.logger?.debug?.(`${TAG} ✗ ${path} totalMs=${totalMs} attempts=${MAX_RETRIES + 1} error=${lastError?.message}`);
106
+ throw lastError ?? new Error(`${TAG} ${path} failed after retries`);
107
+ }
108
+ // ── Database operations ─────────────────────────────────
109
+ async createDatabase(dbName) {
110
+ const name = dbName ?? this.database;
111
+ // SDK pattern: list first, create only if not found
112
+ const listResp = await this.request("/database/list", {});
113
+ const exists = (listResp.databases ?? []).includes(name);
114
+ if (exists) {
115
+ this.logger?.debug?.(`${TAG} Database already exists: ${name}`);
116
+ return false;
117
+ }
118
+ await this.request("/database/create", { database: name });
119
+ this.logger?.info(`${TAG} Database created: ${name}`);
120
+ return true;
121
+ }
122
+ // ── Collection operations ───────────────────────────────
123
+ async createCollection(params) {
124
+ const name = String(params.collection ?? "");
125
+ // SDK pattern: try describe first, create only if not found (code 15302)
126
+ try {
127
+ await this.describeCollection(name);
128
+ this.logger?.debug?.(`${TAG} Collection already exists: ${name}`);
129
+ return;
130
+ }
131
+ catch (err) {
132
+ if (!(err instanceof TcvdbApiError && err.apiCode === 15302)) {
133
+ throw err; // unexpected error
134
+ }
135
+ // 15302 = collection not found → proceed to create
136
+ }
137
+ try {
138
+ await this.request("/collection/create", {
139
+ database: this.database,
140
+ ...params,
141
+ });
142
+ this.logger?.info(`${TAG} Collection created: ${name}`);
143
+ }
144
+ catch (err) {
145
+ // 15202 = collection already exists — race between describe and create.
146
+ // Semantically identical to "describe found it", so treat as success.
147
+ if (err instanceof TcvdbApiError && err.apiCode === 15202) {
148
+ this.logger?.debug?.(`${TAG} Collection already exists (race): ${name}`);
149
+ return;
150
+ }
151
+ throw err;
152
+ }
153
+ }
154
+ async describeCollection(collection) {
155
+ const resp = await this.request("/collection/describe", {
156
+ database: this.database,
157
+ collection,
158
+ });
159
+ return resp.collection;
160
+ }
161
+ // ── Document operations ─────────────────────────────────
162
+ async upsert(collection, documents) {
163
+ await this.request("/document/upsert", {
164
+ database: this.database,
165
+ collection,
166
+ buildIndex: true,
167
+ documents,
168
+ });
169
+ }
170
+ async search(collection, searchParams) {
171
+ return this.request("/document/search", {
172
+ database: this.database,
173
+ collection,
174
+ readConsistency: "strongConsistency",
175
+ search: searchParams,
176
+ });
177
+ }
178
+ async hybridSearch(collection, searchParams) {
179
+ return this.request("/document/hybridSearch", {
180
+ database: this.database,
181
+ collection,
182
+ readConsistency: "strongConsistency",
183
+ search: searchParams,
184
+ });
185
+ }
186
+ async query(collection, queryParams) {
187
+ return this.request("/document/query", {
188
+ database: this.database,
189
+ collection,
190
+ readConsistency: "strongConsistency",
191
+ query: queryParams,
192
+ });
193
+ }
194
+ async deleteDoc(collection, params) {
195
+ await this.request("/document/delete", {
196
+ database: this.database,
197
+ collection,
198
+ ...params,
199
+ });
200
+ }
201
+ /**
202
+ * Count documents matching an optional filter.
203
+ * Uses the dedicated /document/count endpoint.
204
+ */
205
+ async count(collection, filter) {
206
+ const query = {};
207
+ if (filter)
208
+ query.filter = filter;
209
+ const resp = await this.request("/document/count", {
210
+ database: this.database,
211
+ collection,
212
+ readConsistency: "strongConsistency",
213
+ query,
214
+ });
215
+ return resp.count ?? 0;
216
+ }
217
+ // ── Convenience getters ─────────────────────────────────
218
+ getDatabase() {
219
+ return this.database;
220
+ }
221
+ }