@aliyunrds/ctxdb 1.0.0 → 1.0.1-beta.2
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 +7 -1
- package/dist/{chunk-QPFFMW52.js → chunk-CYCD234A.js} +4 -2
- package/dist/{chunk-QHYJ7OXC.js → chunk-TKMIWM6Q.js} +6 -0
- package/dist/chunk-UEKR2Z3S.js +11 -0
- package/dist/{chunk-PLWEIEFH.js → chunk-UULWJJT4.js} +1 -1
- package/dist/cli/main.js +6 -1
- package/dist/hooks/pre-tool-use.js +4 -0
- package/dist/hooks/session-start.js +7 -3
- package/dist/hooks/stop.js +16 -4
- package/dist/hooks/user-prompt-submit.js +7 -3
- package/dist/opencode/index.js +19 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,6 +117,8 @@ Field reference:
|
|
|
117
117
|
| Key | Type | Default | Meaning |
|
|
118
118
|
|---|---|---|---|
|
|
119
119
|
| `user_id` | str | `"default"` | mem0-layer bucket key on every capture/recall call. The server's tenant isolation runs on `member_id` (injected from `X-API-Key`), so this is an optional per-user/agent slice. Override with `ctxdb setup --agent <a> --user-id <bucket>` or `CTXDB_USER_ID=<bucket>` when you need to isolate this install from another access layer or machine sharing the same workspace |
|
|
120
|
+
| `agent_id` | str | null | Optional scope dimension on every capture/recall call. **Default null = not sent** — the server counts `agent_id` as scope on the search path, so emitting it by default would narrow every existing user's recall. Set only when you want per-agent isolation: `CTXDB_AGENT_ID=<id>` (env-only; no setup flag) |
|
|
121
|
+
| `app_id` | str | null | Optional app-scope dimension, same contract as `agent_id` (default null = not sent). Set via `CTXDB_APP_ID=<id>` (env-only) |
|
|
120
122
|
| `auto_capture` | bool | `true` | Stop hook captures each turn into long-term memory for the selected agent |
|
|
121
123
|
| `auto_recall` | bool | `true` | UserPromptSubmit hook recalls memory on each user prompt for the selected agent |
|
|
122
124
|
| `warmup_recall` | bool | `false` | SessionStart hook recalls cwd/git-related memories for the selected agent. **Default is off** — open it per-agent when you want session-start warmup (adds one bounded, circuit-protected recall call on session start) |
|
|
@@ -125,7 +127,11 @@ Field reference:
|
|
|
125
127
|
| `knowledge_top_k` | int | `6` | KB chunks pulled per recall (only effective when `recall_knowledge: true`) |
|
|
126
128
|
| `debug` | bool | `false` | Verbose hook logging (`~/.ctxdb/logs/ctxdb.log`) |
|
|
127
129
|
|
|
128
|
-
Env-var overrides apply to the selected agent config (env wins): `CTXDB_AGENT` / `CTXDB_API_KEY` / `CTXDB_BASE_URL` / `CTXDB_USER_ID`.
|
|
130
|
+
Env-var overrides apply to the selected agent config (env wins): `CTXDB_AGENT` / `CTXDB_API_KEY` / `CTXDB_BASE_URL` / `CTXDB_USER_ID` / `CTXDB_AGENT_ID` / `CTXDB_APP_ID`. The last two default to unset — the request body omits `agent_id`/`app_id` entirely; set them only when you want per-agent / per-app scope isolation on the server.
|
|
131
|
+
|
|
132
|
+
Hook/plugin escape hatch: set `CTXDB_SKIP_HOOKS=TRUE` on the agent process to make all ctxdb hook entrypoints exit immediately before reading config or calling the API. Direct `ctxdb memory` / `ctxdb kb` / `ctxdb setup` CLI commands are unchanged.
|
|
133
|
+
|
|
134
|
+
`run_id` is **not** env-driven: the Stop hook reads the host's `session_id` from the hook payload (Qoder / Codex / Claude Code all pass it at the top level; opencode reads `ev.properties.sessionID`) and sends it as `run_id` on capture only, so the server can associate turns within one session for richer extraction context. Recall never sends `run_id`. If the payload has no `session_id`, `run_id` is omitted and capture proceeds normally (the server falls back to baseMessages).
|
|
129
135
|
|
|
130
136
|
## Architecture
|
|
131
137
|
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
isConnectionError,
|
|
7
7
|
resetCircuit,
|
|
8
8
|
tripCircuit
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UULWJJT4.js";
|
|
10
10
|
import {
|
|
11
11
|
CtxdbError
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-TKMIWM6Q.js";
|
|
13
13
|
|
|
14
14
|
// src/lib/recall-orchestrator.ts
|
|
15
15
|
import {
|
|
@@ -109,6 +109,8 @@ async function recallTurn(prompt, cfg, client, agent = "default") {
|
|
|
109
109
|
if (cfg.recallKnowledge) {
|
|
110
110
|
body.knowledge = { enable: true, top_k: cfg.knowledgeTopK };
|
|
111
111
|
}
|
|
112
|
+
if (cfg.agentId) body.agent_id = cfg.agentId;
|
|
113
|
+
if (cfg.appId) body.app_id = cfg.appId;
|
|
112
114
|
let resp;
|
|
113
115
|
try {
|
|
114
116
|
resp = await client.postJson("/v3/memories/search/", body);
|
|
@@ -387,6 +387,8 @@ function configFromDisk(raw) {
|
|
|
387
387
|
apiKey: typeof raw.api_key === "string" && raw.api_key ? raw.api_key : null,
|
|
388
388
|
baseUrl: typeof raw.base_url === "string" && raw.base_url ? String(raw.base_url).replace(/\/+$/, "") : DEFAULT_BASE_URL,
|
|
389
389
|
userId: typeof raw.user_id === "string" && raw.user_id ? raw.user_id : DEFAULT_USER_ID,
|
|
390
|
+
agentId: typeof raw.agent_id === "string" && raw.agent_id ? raw.agent_id : null,
|
|
391
|
+
appId: typeof raw.app_id === "string" && raw.app_id ? raw.app_id : null,
|
|
390
392
|
autoCapture: coerceBool(raw.auto_capture, true),
|
|
391
393
|
autoRecall: coerceBool(raw.auto_recall, true),
|
|
392
394
|
warmupRecall: coerceBool(raw.warmup_recall, false),
|
|
@@ -402,6 +404,8 @@ function applyEnv(cfg, env) {
|
|
|
402
404
|
if (env.CTXDB_API_KEY) cfg.apiKey = env.CTXDB_API_KEY;
|
|
403
405
|
if (env.CTXDB_BASE_URL) cfg.baseUrl = env.CTXDB_BASE_URL.replace(/\/+$/, "");
|
|
404
406
|
if (env.CTXDB_USER_ID) cfg.userId = env.CTXDB_USER_ID;
|
|
407
|
+
if (env.CTXDB_AGENT_ID) cfg.agentId = env.CTXDB_AGENT_ID;
|
|
408
|
+
if (env.CTXDB_APP_ID) cfg.appId = env.CTXDB_APP_ID;
|
|
405
409
|
return cfg;
|
|
406
410
|
}
|
|
407
411
|
function load(options = {}) {
|
|
@@ -437,6 +441,8 @@ function configToDisk(cfg) {
|
|
|
437
441
|
api_key: cfg.apiKey,
|
|
438
442
|
base_url: cfg.baseUrl,
|
|
439
443
|
user_id: cfg.userId,
|
|
444
|
+
agent_id: cfg.agentId,
|
|
445
|
+
app_id: cfg.appId,
|
|
440
446
|
auto_capture: cfg.autoCapture,
|
|
441
447
|
auto_recall: cfg.autoRecall,
|
|
442
448
|
warmup_recall: cfg.warmupRecall,
|
package/dist/cli/main.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
removeAgent,
|
|
32
32
|
save,
|
|
33
33
|
writeInstalledPkgVersion
|
|
34
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-TKMIWM6Q.js";
|
|
35
35
|
|
|
36
36
|
// src/cli/util.ts
|
|
37
37
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -2257,6 +2257,11 @@ AGENT RESOLUTION (when --agent is omitted):
|
|
|
2257
2257
|
|
|
2258
2258
|
ENV VARS (override selected ~/.ctxdb/ctxdb.json agent config):
|
|
2259
2259
|
CTXDB_AGENT CTXDB_API_KEY CTXDB_BASE_URL CTXDB_USER_ID
|
|
2260
|
+
CTXDB_AGENT_ID CTXDB_APP_ID
|
|
2261
|
+
|
|
2262
|
+
HOOK ENV VARS:
|
|
2263
|
+
CTXDB_SKIP_HOOKS=TRUE Hook/plugin entrypoints exit immediately.
|
|
2264
|
+
Direct ctxdb CLI commands are unchanged.
|
|
2260
2265
|
|
|
2261
2266
|
CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.qoderwork / agents.codex / agents.claude / agents.opencode)
|
|
2262
2267
|
LOGS: ~/.ctxdb/logs/ctxdb.log
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
fetchKbCatalogBlock,
|
|
4
4
|
recallTurn
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-CYCD234A.js";
|
|
6
6
|
import "../chunk-6S5RJYBC.js";
|
|
7
7
|
import {
|
|
8
8
|
isCircuitOpen
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-UULWJJT4.js";
|
|
10
10
|
import {
|
|
11
11
|
HttpClient,
|
|
12
12
|
agentFromArgvWithFallback,
|
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
isComplete,
|
|
15
15
|
load,
|
|
16
16
|
setDebug
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-TKMIWM6Q.js";
|
|
18
|
+
import {
|
|
19
|
+
shouldSkipHooks
|
|
20
|
+
} from "../chunk-UEKR2Z3S.js";
|
|
18
21
|
|
|
19
22
|
// src/hooks/session-start.ts
|
|
20
23
|
import { pathToFileURL } from "url";
|
|
@@ -120,6 +123,7 @@ async function readStdinJson() {
|
|
|
120
123
|
}
|
|
121
124
|
async function main() {
|
|
122
125
|
try {
|
|
126
|
+
if (shouldSkipHooks()) return 0;
|
|
123
127
|
const event = await readStdinJson();
|
|
124
128
|
const { agent, fellBack } = agentFromArgvWithFallback();
|
|
125
129
|
if (fellBack) {
|
package/dist/hooks/stop.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
isConnectionError,
|
|
5
5
|
resetCircuit,
|
|
6
6
|
tripCircuit
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-UULWJJT4.js";
|
|
8
8
|
import {
|
|
9
9
|
CtxdbError,
|
|
10
10
|
HttpClient,
|
|
@@ -13,7 +13,10 @@ import {
|
|
|
13
13
|
isDebug,
|
|
14
14
|
load,
|
|
15
15
|
setDebug
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-TKMIWM6Q.js";
|
|
17
|
+
import {
|
|
18
|
+
shouldSkipHooks
|
|
19
|
+
} from "../chunk-UEKR2Z3S.js";
|
|
17
20
|
|
|
18
21
|
// src/lib/capture-orchestrator.ts
|
|
19
22
|
import {
|
|
@@ -304,7 +307,7 @@ function extractSkillSignals(rows) {
|
|
|
304
307
|
}
|
|
305
308
|
|
|
306
309
|
// src/lib/capture-orchestrator.ts
|
|
307
|
-
async function captureTurn(transcriptPath, cfg, client, agent = "default") {
|
|
310
|
+
async function captureTurn(transcriptPath, cfg, client, agent = "default", sessionId) {
|
|
308
311
|
if (!cfg.apiKey || !cfg.baseUrl) {
|
|
309
312
|
return { captured: false, reason: "config_incomplete", messageCount: 0 };
|
|
310
313
|
}
|
|
@@ -373,6 +376,9 @@ async function captureTurn(transcriptPath, cfg, client, agent = "default") {
|
|
|
373
376
|
user_id: cfg.userId,
|
|
374
377
|
async_mode: true
|
|
375
378
|
};
|
|
379
|
+
if (cfg.agentId) payload.agent_id = cfg.agentId;
|
|
380
|
+
if (cfg.appId) payload.app_id = cfg.appId;
|
|
381
|
+
if (sessionId) payload.run_id = sessionId;
|
|
376
382
|
let resp;
|
|
377
383
|
try {
|
|
378
384
|
resp = await client.postJson("/v3/memories/add/", payload);
|
|
@@ -504,8 +510,13 @@ async function readStdinJson() {
|
|
|
504
510
|
return {};
|
|
505
511
|
}
|
|
506
512
|
}
|
|
513
|
+
function extractHookSessionId(event) {
|
|
514
|
+
const v = event.session_id ?? event.sessionId;
|
|
515
|
+
return typeof v === "string" && v ? v : void 0;
|
|
516
|
+
}
|
|
507
517
|
async function main() {
|
|
508
518
|
try {
|
|
519
|
+
if (shouldSkipHooks()) return 0;
|
|
509
520
|
const event = await readStdinJson();
|
|
510
521
|
const { agent, fellBack } = agentFromArgvWithFallback();
|
|
511
522
|
if (fellBack) {
|
|
@@ -516,6 +527,7 @@ async function main() {
|
|
|
516
527
|
}
|
|
517
528
|
const transcriptPath = event.transcript_path;
|
|
518
529
|
if (typeof transcriptPath !== "string" || !transcriptPath) return 0;
|
|
530
|
+
const sessionId = extractHookSessionId(event);
|
|
519
531
|
const cfg = load({ agent });
|
|
520
532
|
setDebug(cfg.debug);
|
|
521
533
|
debug("capture", "start", { transcriptPath, userId: cfg.userId });
|
|
@@ -524,7 +536,7 @@ async function main() {
|
|
|
524
536
|
return 0;
|
|
525
537
|
}
|
|
526
538
|
const client = new HttpClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, timeoutMs: 5e3 });
|
|
527
|
-
const result = await captureTurn(transcriptPath, cfg, client, agent);
|
|
539
|
+
const result = await captureTurn(transcriptPath, cfg, client, agent, sessionId);
|
|
528
540
|
if (result.captured) {
|
|
529
541
|
debug("capture", `ok (${result.messageCount} msgs)`, result);
|
|
530
542
|
process.stderr.write(
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
fetchKbCatalogBlock,
|
|
4
4
|
recallTurn
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-CYCD234A.js";
|
|
6
6
|
import "../chunk-6S5RJYBC.js";
|
|
7
7
|
import {
|
|
8
8
|
isCircuitOpen
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-UULWJJT4.js";
|
|
10
10
|
import {
|
|
11
11
|
HttpClient,
|
|
12
12
|
agentFromArgvWithFallback,
|
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
isComplete,
|
|
15
15
|
load,
|
|
16
16
|
setDebug
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-TKMIWM6Q.js";
|
|
18
|
+
import {
|
|
19
|
+
shouldSkipHooks
|
|
20
|
+
} from "../chunk-UEKR2Z3S.js";
|
|
18
21
|
|
|
19
22
|
// src/hooks/user-prompt-submit.ts
|
|
20
23
|
import { pathToFileURL } from "url";
|
|
@@ -52,6 +55,7 @@ async function readStdinJson() {
|
|
|
52
55
|
}
|
|
53
56
|
async function main() {
|
|
54
57
|
try {
|
|
58
|
+
if (shouldSkipHooks()) return 0;
|
|
55
59
|
const event = await readStdinJson();
|
|
56
60
|
const { agent, fellBack } = agentFromArgvWithFallback();
|
|
57
61
|
if (fellBack) {
|
package/dist/opencode/index.js
CHANGED
|
@@ -54,6 +54,8 @@ function applyEnv(cfg, env) {
|
|
|
54
54
|
if (env.CTXDB_API_KEY) cfg.apiKey = env.CTXDB_API_KEY;
|
|
55
55
|
if (env.CTXDB_BASE_URL) cfg.baseUrl = env.CTXDB_BASE_URL.replace(/\/+$/, "");
|
|
56
56
|
if (env.CTXDB_USER_ID) cfg.userId = env.CTXDB_USER_ID;
|
|
57
|
+
if (env.CTXDB_AGENT_ID) cfg.agentId = env.CTXDB_AGENT_ID;
|
|
58
|
+
if (env.CTXDB_APP_ID) cfg.appId = env.CTXDB_APP_ID;
|
|
57
59
|
return cfg;
|
|
58
60
|
}
|
|
59
61
|
function loadOpencodeConfig(options = {}) {
|
|
@@ -65,6 +67,8 @@ function loadOpencodeConfig(options = {}) {
|
|
|
65
67
|
apiKey: typeof section.api_key === "string" && section.api_key ? section.api_key : null,
|
|
66
68
|
baseUrl: typeof section.base_url === "string" && section.base_url ? String(section.base_url).replace(/\/+$/, "") : DEFAULT_BASE_URL,
|
|
67
69
|
userId: typeof section.user_id === "string" && section.user_id ? section.user_id : DEFAULT_USER_ID,
|
|
70
|
+
agentId: typeof section.agent_id === "string" && section.agent_id ? section.agent_id : null,
|
|
71
|
+
appId: typeof section.app_id === "string" && section.app_id ? section.app_id : null,
|
|
68
72
|
autoCapture: coerceBool(section.auto_capture, true),
|
|
69
73
|
autoRecall: coerceBool(section.auto_recall, true),
|
|
70
74
|
warmupRecall: coerceBool(section.warmup_recall, false),
|
|
@@ -590,6 +594,8 @@ async function searchAndFormatRecall(prompt, cfg, client, timeoutMs) {
|
|
|
590
594
|
if (cfg.recallKnowledge) {
|
|
591
595
|
body.knowledge = { enable: true, top_k: cfg.knowledgeTopK };
|
|
592
596
|
}
|
|
597
|
+
if (cfg.agentId) body.agent_id = cfg.agentId;
|
|
598
|
+
if (cfg.appId) body.app_id = cfg.appId;
|
|
593
599
|
let resp;
|
|
594
600
|
try {
|
|
595
601
|
resp = await client.postJson("/v3/memories/search/", body, { timeoutMs });
|
|
@@ -772,7 +778,7 @@ function fingerprintMessages(messages) {
|
|
|
772
778
|
}
|
|
773
779
|
return h.digest("hex");
|
|
774
780
|
}
|
|
775
|
-
async function runCapture(messages, cfg, client, timeoutMs, lastFingerprint) {
|
|
781
|
+
async function runCapture(messages, cfg, client, timeoutMs, lastFingerprint, sessionId) {
|
|
776
782
|
if (!cfg.autoCapture) return { ...EMPTY2, reason: "auto_capture_disabled" };
|
|
777
783
|
if (!cfg.baseUrl) return { ...EMPTY2, reason: "config_incomplete" };
|
|
778
784
|
if (messages.length === 0) return { ...EMPTY2, reason: "empty_transcript" };
|
|
@@ -796,6 +802,9 @@ async function runCapture(messages, cfg, client, timeoutMs, lastFingerprint) {
|
|
|
796
802
|
user_id: cfg.userId,
|
|
797
803
|
async_mode: true
|
|
798
804
|
};
|
|
805
|
+
if (cfg.agentId) payload.agent_id = cfg.agentId;
|
|
806
|
+
if (cfg.appId) payload.app_id = cfg.appId;
|
|
807
|
+
if (sessionId) payload.run_id = sessionId;
|
|
799
808
|
let resp;
|
|
800
809
|
try {
|
|
801
810
|
resp = await client.postJson("/v3/memories/add/", payload, { timeoutMs });
|
|
@@ -816,6 +825,12 @@ async function runCapture(messages, cfg, client, timeoutMs, lastFingerprint) {
|
|
|
816
825
|
};
|
|
817
826
|
}
|
|
818
827
|
|
|
828
|
+
// src/hook-env.ts
|
|
829
|
+
var CTXDB_SKIP_HOOKS = "CTXDB_SKIP_HOOKS";
|
|
830
|
+
function shouldSkipHooks(env = process.env) {
|
|
831
|
+
return env[CTXDB_SKIP_HOOKS] === "TRUE";
|
|
832
|
+
}
|
|
833
|
+
|
|
819
834
|
// src/hooks.ts
|
|
820
835
|
var SESSION_TTL_MS = 15 * 60 * 1e3;
|
|
821
836
|
var SESSION_MAX = 100;
|
|
@@ -861,6 +876,7 @@ function extractTextPrompt(parts) {
|
|
|
861
876
|
return chunks.join("\n").trim();
|
|
862
877
|
}
|
|
863
878
|
async function buildHooks(input) {
|
|
879
|
+
if (shouldSkipHooks()) return {};
|
|
864
880
|
const config = loadOpencodeConfig();
|
|
865
881
|
if (!isConfigured(config)) {
|
|
866
882
|
logDebug(
|
|
@@ -944,7 +960,8 @@ async function buildHooks(input) {
|
|
|
944
960
|
rt.config,
|
|
945
961
|
rt.http,
|
|
946
962
|
CAPTURE_TIMEOUT_MS,
|
|
947
|
-
state.lastFingerprint
|
|
963
|
+
state.lastFingerprint,
|
|
964
|
+
sessionID
|
|
948
965
|
);
|
|
949
966
|
if (outcome.captured && outcome.fingerprint) {
|
|
950
967
|
state.lastFingerprint = outcome.fingerprint;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aliyunrds/ctxdb",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Unified access layer for RDS ContextDatabase: `ctxdb` CLI (memory + KB ops), one-shot `setup --agent <qoder|qoderwork|codex|claude|opencode>` installer, per-agent config, hooks/plugins, and SKILL.md.",
|
|
6
6
|
"license": "Apache-2.0",
|