@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.
- package/README.md +66 -14
- package/dist/{chunk-SAQT6VL6.js → chunk-6BB65W5W.js} +2 -2
- package/dist/{chunk-BVCDRUU4.js → chunk-7EIJB5LA.js} +7 -3
- package/dist/{chunk-JFTKYEVN.js → chunk-ARZX2RLR.js} +55 -7
- package/dist/{chunk-USMJLDBD.js → chunk-B7JCQL4O.js} +1 -1
- package/dist/{chunk-CWGF5D52.js → chunk-BHRWAU3E.js} +13 -6
- package/dist/{chunk-3PFMHU3C.js → chunk-L4GVGVOP.js} +4 -3
- package/dist/{chunk-Q4JYST7K.js → chunk-VHNHVMCA.js} +137 -20
- package/dist/{chunk-BQA7YSXT.js → chunk-Z6OHSEF2.js} +20 -16
- package/dist/cli/main.js +291 -77
- package/dist/hooks/hermes-post-llm-call.js +3 -3
- package/dist/hooks/hermes-pre-llm-call.js +31 -9
- package/dist/hooks/session-start.js +14 -6
- package/dist/hooks/stop.js +3 -3
- package/dist/hooks/user-prompt-submit.js +13 -6
- package/dist/opencode/index.js +657 -208
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +7 -5
- package/dist/setup/skills/contextdb-memory/SKILL.md +1 -1
- package/dist/workers/version-check.js +1 -1
- package/package.json +2 -2
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
listKnowledgeBases
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-L4GVGVOP.js";
|
|
5
5
|
import {
|
|
6
6
|
isConnectionError,
|
|
7
7
|
resetCircuit,
|
|
8
8
|
tripCircuit
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-B7JCQL4O.js";
|
|
10
10
|
import {
|
|
11
11
|
CtxdbError
|
|
12
|
-
} from "./chunk-
|
|
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
|
|
70
|
+
return selection;
|
|
60
71
|
}
|
|
61
|
-
return
|
|
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,
|
|
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)
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
|
152
|
-
var
|
|
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
|
-
|
|
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 +
|
|
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.
|
|
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};
|
|
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;
|