@aliyunrds/ctxdb 1.0.4 → 1.0.6

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.
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  listKnowledgeBases
4
- } from "./chunk-3PFMHU3C.js";
4
+ } from "./chunk-L4GVGVOP.js";
5
5
  import {
6
6
  isConnectionError,
7
7
  resetCircuit,
8
8
  tripCircuit
9
- } from "./chunk-USMJLDBD.js";
9
+ } from "./chunk-B7JCQL4O.js";
10
10
  import {
11
11
  CtxdbError
12
- } from "./chunk-JFTKYEVN.js";
12
+ } from "./chunk-ARZX2RLR.js";
13
13
 
14
14
  // src/lib/kb-catalog.ts
15
15
  function sanitizeKeyEntities(raw) {
@@ -39,14 +39,19 @@ async function fetchKbCatalogBlock(client, agent) {
39
39
  }
40
40
 
41
41
  // src/lib/recall-orchestrator.ts
42
+ import { randomUUID } from "crypto";
42
43
  import {
43
- buildExternalKnowledgeBlock
44
+ buildExternalKnowledgeBlock,
45
+ createRecallFinishRecord,
46
+ createRecallStartRecord,
47
+ writeRecallTrace
44
48
  } from "@aliyunrds/ctxdb-shared";
45
49
 
46
50
  // src/lib/format-recall.ts
47
51
  import {
48
52
  RECALLED_MEMORIES_SAFETY_NOTICE,
49
- formatRecalledMemoriesBlock
53
+ formatRecalledMemoriesBlock,
54
+ selectAndFormatRecalledMemories
50
55
  } from "@aliyunrds/ctxdb-shared";
51
56
  var OPEN_TAG = "<recalled-memories>";
52
57
  var CLOSE_TAG = "</recalled-memories>";
@@ -55,10 +60,19 @@ var IMPORTANCE_SUFFIX_RE = /\s*\(\d+%\)\s*$/;
55
60
  var CATEGORY_LABEL_RE = /^[A-Z][A-Za-z0-9_-]*:$/;
56
61
  function slimRecalledMemoriesBlock(memories, userId) {
57
62
  const raw = formatRecalledMemoriesBlock(memories, userId);
63
+ if (!raw.includes("Stored memories for ")) return raw;
64
+ return slimRewrite(raw, userId, memories.length);
65
+ }
66
+ function selectAndSlimRecalledMemoriesBlock(memories, userId) {
67
+ const selection = selectAndFormatRecalledMemories(memories, userId);
68
+ const raw = selection.context;
58
69
  if (!raw.includes("Stored memories for ")) {
59
- return raw;
70
+ return selection;
60
71
  }
61
- return slimRewrite(raw, userId, memories.length);
72
+ return {
73
+ ...selection,
74
+ context: slimRewrite(raw, userId, memories.length)
75
+ };
62
76
  }
63
77
  function slimRewrite(raw, userId, memoryCount) {
64
78
  const lines = raw.split("\n");
@@ -118,6 +132,15 @@ function stripSystemReminders(raw) {
118
132
  const cleaned = raw.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
119
133
  return cleaned || raw;
120
134
  }
135
+ function emptySelection() {
136
+ return {
137
+ returned: [],
138
+ selected: [],
139
+ excluded: [],
140
+ context: "",
141
+ tokenEstimate: 0
142
+ };
143
+ }
121
144
  var EMPTY = {
122
145
  ok: false,
123
146
  reason: "",
@@ -125,13 +148,78 @@ var EMPTY = {
125
148
  memoryCount: 0,
126
149
  knowledgeChunkCount: 0
127
150
  };
128
- async function recallTurn(prompt, cfg, client, agent = "default") {
151
+ async function recallTurn(prompt, cfg, client, agentOrOptions = "default") {
152
+ const options = typeof agentOrOptions === "string" ? { agent: agentOrOptions } : agentOrOptions;
153
+ const agent = options.agent ?? "default";
129
154
  if (!prompt.trim()) return { ...EMPTY, reason: "empty_prompt" };
130
155
  if (!cfg.apiKey || !cfg.baseUrl) {
131
156
  return { ...EMPTY, reason: "config_incomplete" };
132
157
  }
133
- if (!cfg.autoRecall) return { ...EMPTY, reason: "auto_recall_disabled" };
158
+ if (!cfg.autoRecall) {
159
+ return { ...EMPTY, reason: "auto_recall_disabled" };
160
+ }
134
161
  const query = stripSystemReminders(prompt);
162
+ const trace = cfg.debug ? {
163
+ agent,
164
+ kind: options.kind ?? "prompt",
165
+ sessionId: options.sessionId?.trim() || null,
166
+ recallEventId: options.recallEventId ?? randomUUID(),
167
+ startedAt: Date.now(),
168
+ request: {
169
+ user_id: cfg.userId,
170
+ top_k: cfg.topK,
171
+ threshold: cfg.threshold,
172
+ ...cfg.recallKnowledge ? { knowledge: { enable: true, top_k: cfg.knowledgeTopK } } : {},
173
+ ...cfg.agentId ? { agent_id: cfg.agentId } : {},
174
+ ...cfg.appId ? { app_id: cfg.appId } : {}
175
+ },
176
+ store: {
177
+ ...options.traceStore,
178
+ secrets: [...options.traceStore?.secrets ?? [], cfg.apiKey]
179
+ }
180
+ } : null;
181
+ if (trace) {
182
+ writeRecallTrace(
183
+ createRecallStartRecord({
184
+ recall_event_id: trace.recallEventId,
185
+ session_id: trace.sessionId,
186
+ agent: trace.agent,
187
+ kind: trace.kind,
188
+ query,
189
+ request: trace.request
190
+ }),
191
+ trace.store
192
+ );
193
+ }
194
+ const finish = (result, outcome, failureReason = null, returnedMemories = [], returnedKnowledge = [], selection2) => {
195
+ if (trace) {
196
+ const tracedSelection = selection2 ?? emptySelection();
197
+ writeRecallTrace(
198
+ createRecallFinishRecord({
199
+ recall_event_id: trace.recallEventId,
200
+ session_id: trace.sessionId,
201
+ agent: trace.agent,
202
+ kind: trace.kind,
203
+ query,
204
+ request: trace.request,
205
+ duration_ms: Date.now() - trace.startedAt,
206
+ outcome,
207
+ failure_reason: failureReason,
208
+ returned_memories: returnedMemories,
209
+ returned_knowledge: returnedKnowledge,
210
+ selection: {
211
+ selected: tracedSelection.selected,
212
+ excluded: tracedSelection.excluded,
213
+ token_estimate: tracedSelection.tokenEstimate
214
+ },
215
+ recall_context: result.additionalContext
216
+ }),
217
+ trace.store
218
+ );
219
+ }
220
+ return result;
221
+ };
222
+ const emptyResult = (reason) => ({ ...EMPTY, reason });
135
223
  const body = {
136
224
  query,
137
225
  user_id: cfg.userId,
@@ -150,32 +238,61 @@ async function recallTurn(prompt, cfg, client, agent = "default") {
150
238
  } catch (err) {
151
239
  const msg = err instanceof CtxdbError ? err.message : String(err);
152
240
  if (isConnectionError(err)) tripCircuit(agent, cfg.baseUrl, msg);
153
- return { ...EMPTY, reason: `http_error: ${msg}` };
241
+ const reason = `http_error: ${msg}`;
242
+ const outcome = /timeout|aborted/i.test(msg) ? "timeout" : "http_error";
243
+ return finish(emptyResult(reason), outcome, reason);
154
244
  }
155
245
  if (!resp || typeof resp !== "object") {
156
- return { ...EMPTY, reason: "bad_response_shape" };
246
+ return finish(
247
+ emptyResult("bad_response_shape"),
248
+ "malformed_response",
249
+ "bad_response_shape"
250
+ );
157
251
  }
158
252
  const r = resp;
159
253
  const memories = Array.isArray(r.results) ? r.results : Array.isArray(r.memories) ? r.memories : [];
160
254
  const knowledge = r.knowledge && typeof r.knowledge === "object" ? r.knowledge : null;
161
255
  const chunks = Array.isArray(knowledge?.chunks) ? knowledge.chunks : [];
162
256
  const blocks = [];
257
+ let selection = null;
163
258
  if (memories.length > 0) {
164
- blocks.push(slimRecalledMemoriesBlock(memories, cfg.userId));
259
+ if (trace) {
260
+ selection = selectAndSlimRecalledMemoriesBlock(memories, cfg.userId);
261
+ blocks.push(selection.context);
262
+ } else {
263
+ blocks.push(slimRecalledMemoriesBlock(memories, cfg.userId));
264
+ }
165
265
  }
166
266
  if (chunks.length > 0) {
167
267
  blocks.push(buildExternalKnowledgeBlock(chunks, cfg.userId));
168
268
  }
169
269
  if (blocks.length === 0) {
170
- return { ...EMPTY, ok: true, reason: "nothing_to_inject" };
270
+ return finish(
271
+ {
272
+ ...emptyResult("nothing_to_inject"),
273
+ ok: true
274
+ },
275
+ "empty",
276
+ null,
277
+ memories,
278
+ chunks,
279
+ selection ?? void 0
280
+ );
171
281
  }
172
- return {
173
- ok: true,
174
- reason: "ok",
175
- additionalContext: blocks.join("\n\n"),
176
- memoryCount: memories.length,
177
- knowledgeChunkCount: chunks.length
178
- };
282
+ return finish(
283
+ {
284
+ ok: true,
285
+ reason: "ok",
286
+ additionalContext: blocks.join("\n\n"),
287
+ memoryCount: memories.length,
288
+ knowledgeChunkCount: chunks.length
289
+ },
290
+ "success",
291
+ null,
292
+ memories,
293
+ chunks,
294
+ selection ?? void 0
295
+ );
179
296
  }
180
297
 
181
298
  export {
@@ -148,9 +148,8 @@ import { dirname as dirname2, join } from "path";
148
148
  import semver2 from "semver";
149
149
  var VERSION_CHECK_SCHEMA = 1;
150
150
  var SUCCESS_TTL_MS = 24 * 60 * 60 * 1e3;
151
- var FAILURE_BACKOFF_BASE_MS = 60 * 60 * 1e3;
152
- var FAILURE_BACKOFF_MAX_MS = 6 * 60 * 60 * 1e3;
153
- var NOTIFICATION_THROTTLE_MS = 24 * 60 * 60 * 1e3;
151
+ var FAILURE_RETRY_INTERVAL_MS = SUCCESS_TTL_MS;
152
+ var NOTIFICATION_THROTTLE_MS = 3 * SUCCESS_TTL_MS;
154
153
  var LOCK_STALE_AFTER_MS = 10 * 60 * 1e3;
155
154
  function versionCheckPaths(home = homedir()) {
156
155
  const cacheDir = join(home, ".ctxdb", "cache");
@@ -210,19 +209,26 @@ function writeVersionCheckState(statePath, state) {
210
209
  return false;
211
210
  }
212
211
  }
212
+ function retainedNextCheckAt(state) {
213
+ return Math.max(
214
+ state.nextCheckAt,
215
+ state.lastSuccessAt === null ? 0 : state.lastSuccessAt + SUCCESS_TTL_MS,
216
+ state.lastFailureAt === null ? 0 : state.lastFailureAt + FAILURE_RETRY_INTERVAL_MS
217
+ );
218
+ }
213
219
  function stateForCurrentVersion(state, currentVersion) {
214
- return state?.currentVersion === currentVersion ? state : createInitialVersionCheckState(currentVersion);
220
+ if (!state) return createInitialVersionCheckState(currentVersion);
221
+ if (state.currentVersion === currentVersion) return state;
222
+ return {
223
+ ...createInitialVersionCheckState(currentVersion),
224
+ nextCheckAt: retainedNextCheckAt(state),
225
+ lastNotifiedAt: state.lastNotifiedAt,
226
+ lastNotifiedVersion: state.lastNotifiedVersion
227
+ };
215
228
  }
216
229
  function isVersionCheckDue(state, now) {
217
230
  return now >= state.nextCheckAt;
218
231
  }
219
- function failureBackoffMs(consecutiveFailures) {
220
- const exponent = Math.max(0, consecutiveFailures - 1);
221
- return Math.min(
222
- FAILURE_BACKOFF_BASE_MS * 2 ** exponent,
223
- FAILURE_BACKOFF_MAX_MS
224
- );
225
- }
226
232
  function recordVersionCheckSuccess(paths, currentVersion, latestVersion, now = Date.now()) {
227
233
  if (!semver2.valid(currentVersion) || !semver2.valid(latestVersion)) return false;
228
234
  const state = stateForCurrentVersion(
@@ -247,7 +253,7 @@ function recordVersionCheckFailure(paths, currentVersion, now = Date.now()) {
247
253
  return writeVersionCheckState(paths.statePath, {
248
254
  ...state,
249
255
  lastFailureAt: now,
250
- nextCheckAt: now + failureBackoffMs(consecutiveFailures),
256
+ nextCheckAt: now + FAILURE_RETRY_INTERVAL_MS,
251
257
  consecutiveFailures
252
258
  });
253
259
  }
@@ -316,13 +322,13 @@ function getUpdateHintCandidate(state, now = Date.now()) {
316
322
  const current = semver2.valid(state.currentVersion);
317
323
  const latest = state.latestVersion ? semver2.valid(state.latestVersion) : null;
318
324
  if (!current || !latest || !semver2.gt(latest, current)) return null;
319
- if (state.lastNotifiedVersion === latest && state.lastNotifiedAt !== null && now - state.lastNotifiedAt < NOTIFICATION_THROTTLE_MS) {
325
+ if (state.lastNotifiedAt !== null && now - state.lastNotifiedAt < NOTIFICATION_THROTTLE_MS) {
320
326
  return null;
321
327
  }
322
328
  return {
323
329
  currentVersion: current,
324
330
  latestVersion: latest,
325
- message: `ctxdb: update available v${current} \u2192 v${latest}; run \`ctxdb update\`.
331
+ message: `ctxdb: update available v${current} \u2192 v${latest}; update with \`ctxdb update\` (informational only; do not run automatically).
326
332
  `
327
333
  };
328
334
  }
@@ -393,7 +399,6 @@ function isCiEnvironment(env = process.env) {
393
399
  }
394
400
  function shouldUseUpdateNotifier(argv, options = {}) {
395
401
  const env = options.env ?? process.env;
396
- if (options.stderrIsTTY !== true) return false;
397
402
  if (options.installMethod !== "npm") return false;
398
403
  if (env.CTXDB_DISABLE_UPDATE_CHECK === "1") return false;
399
404
  if (isCiEnvironment(env)) return false;
@@ -408,7 +413,6 @@ function beginUpdateNotification(argv, options) {
408
413
  const installMethod = options.installMethod ?? detectInstallMethod();
409
414
  if (!shouldUseUpdateNotifier(argv, {
410
415
  env: options.env ?? process.env,
411
- stderrIsTTY: options.stderrIsTTY ?? process.stderr.isTTY,
412
416
  installMethod
413
417
  })) {
414
418
  return null;