@aliyunrds/ctxdb 1.0.1 → 1.0.3
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 +174 -5
- package/dist/chunk-3PFMHU3C.js +442 -0
- package/dist/chunk-BQA7YSXT.js +462 -0
- package/dist/chunk-BVCDRUU4.js +33 -0
- package/dist/{chunk-RZONPKY4.js → chunk-CWGF5D52.js} +4 -86
- package/dist/{chunk-6FZL67GH.js → chunk-JFTKYEVN.js} +333 -45
- package/dist/{chunk-EUQ3OFCQ.js → chunk-Q4JYST7K.js} +3 -3
- package/dist/{chunk-IMYLU5C2.js → chunk-SAQT6VL6.js} +2 -2
- package/dist/{chunk-TGVURF54.js → chunk-USMJLDBD.js} +1 -1
- package/dist/cli/main.js +347 -158
- package/dist/hooks/hermes-post-llm-call.js +3 -3
- package/dist/hooks/hermes-pre-llm-call.js +6 -6
- package/dist/hooks/session-start.js +88 -6
- package/dist/hooks/stop.js +3 -3
- package/dist/hooks/user-prompt-submit.js +83 -6
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +13 -3
- package/dist/workers/version-check.js +65 -0
- package/package.json +3 -1
- package/dist/chunk-FD3AMXVU.js +0 -111
- package/dist/chunk-UH7AJF6F.js +0 -204
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
captureParsedMessages
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-SAQT6VL6.js";
|
|
5
|
+
import "../chunk-USMJLDBD.js";
|
|
6
6
|
import {
|
|
7
7
|
HttpClient,
|
|
8
8
|
agentFromArgvWithFallback,
|
|
9
9
|
debug,
|
|
10
10
|
load,
|
|
11
11
|
setDebug
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-JFTKYEVN.js";
|
|
13
13
|
import {
|
|
14
14
|
shouldSkipHooks
|
|
15
15
|
} from "../chunk-UEKR2Z3S.js";
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HOOK_TIMEOUT_MS,
|
|
4
4
|
composeSessionStart
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-CWGF5D52.js";
|
|
6
6
|
import {
|
|
7
7
|
composeUserPromptSubmit
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-BVCDRUU4.js";
|
|
9
9
|
import {
|
|
10
10
|
fetchKbCatalogBlock
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import "../chunk-
|
|
11
|
+
} from "../chunk-Q4JYST7K.js";
|
|
12
|
+
import "../chunk-3PFMHU3C.js";
|
|
13
13
|
import {
|
|
14
14
|
isCircuitOpen
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-USMJLDBD.js";
|
|
16
16
|
import {
|
|
17
17
|
HttpClient,
|
|
18
18
|
agentFromArgvWithFallback,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
isComplete,
|
|
21
21
|
load,
|
|
22
22
|
setDebug
|
|
23
|
-
} from "../chunk-
|
|
23
|
+
} from "../chunk-JFTKYEVN.js";
|
|
24
24
|
import {
|
|
25
25
|
shouldSkipHooks
|
|
26
26
|
} from "../chunk-UEKR2Z3S.js";
|
|
@@ -3,12 +3,94 @@ import {
|
|
|
3
3
|
HOOK_TIMEOUT_MS,
|
|
4
4
|
composeSessionStart,
|
|
5
5
|
formatSessionStartStdout
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
} from "../chunk-CWGF5D52.js";
|
|
7
|
+
import "../chunk-Q4JYST7K.js";
|
|
8
|
+
import "../chunk-3PFMHU3C.js";
|
|
9
|
+
import {
|
|
10
|
+
isCircuitOpen
|
|
11
|
+
} from "../chunk-USMJLDBD.js";
|
|
12
|
+
import {
|
|
13
|
+
HttpClient,
|
|
14
|
+
agentFromArgvWithFallback,
|
|
15
|
+
debug,
|
|
16
|
+
isComplete,
|
|
17
|
+
load,
|
|
18
|
+
setDebug
|
|
19
|
+
} from "../chunk-JFTKYEVN.js";
|
|
20
|
+
import {
|
|
21
|
+
shouldSkipHooks
|
|
22
|
+
} from "../chunk-UEKR2Z3S.js";
|
|
23
|
+
|
|
24
|
+
// src/hooks/session-start.ts
|
|
25
|
+
import { pathToFileURL } from "url";
|
|
26
|
+
async function readStdinJson() {
|
|
27
|
+
let raw = "";
|
|
28
|
+
for await (const chunk of process.stdin) raw += chunk;
|
|
29
|
+
if (!raw.trim()) return {};
|
|
30
|
+
try {
|
|
31
|
+
const obj = JSON.parse(raw);
|
|
32
|
+
return obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {};
|
|
33
|
+
} catch {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function main() {
|
|
38
|
+
try {
|
|
39
|
+
if (shouldSkipHooks()) return 0;
|
|
40
|
+
const event = await readStdinJson();
|
|
41
|
+
const { agent, fellBack } = agentFromArgvWithFallback();
|
|
42
|
+
if (fellBack) {
|
|
43
|
+
process.stderr.write(
|
|
44
|
+
`ctxdb warmup: agent unspecified, defaulting to ${agent}
|
|
45
|
+
`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const cwd = typeof event.cwd === "string" ? event.cwd : "";
|
|
49
|
+
if (!cwd) return 0;
|
|
50
|
+
const cfg = load({ agent });
|
|
51
|
+
setDebug(cfg.debug);
|
|
52
|
+
debug("warmup", "start", { cwd, userId: cfg.userId });
|
|
53
|
+
const kbInjectHere = cfg.kbCatalogInjection === "session_start";
|
|
54
|
+
if (!isComplete(cfg) || !cfg.warmupRecall && !kbInjectHere) {
|
|
55
|
+
debug("warmup", "skip (config incomplete or both warmup+kb off)");
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
if (isCircuitOpen(agent, cfg.baseUrl)) {
|
|
59
|
+
debug("warmup", "skip (circuit open)");
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
const client = new HttpClient({
|
|
63
|
+
baseUrl: cfg.baseUrl,
|
|
64
|
+
apiKey: cfg.apiKey,
|
|
65
|
+
timeoutMs: HOOK_TIMEOUT_MS
|
|
66
|
+
});
|
|
67
|
+
const composed = await composeSessionStart(cfg, agent, client, cwd);
|
|
68
|
+
if (composed.warmupTimedOut) {
|
|
69
|
+
process.stderr.write("ctxdb warmup: timeout\n");
|
|
70
|
+
}
|
|
71
|
+
if (!composed.ctx) return 0;
|
|
72
|
+
const { memoryCount, kbChunkCount, kbCatalogLines, ctx } = composed;
|
|
73
|
+
debug(
|
|
74
|
+
"warmup",
|
|
75
|
+
`ok, memories=${memoryCount} kb=${kbChunkCount} kb_catalog=${kbCatalogLines}`
|
|
76
|
+
);
|
|
77
|
+
process.stderr.write(
|
|
78
|
+
`ctxdb warmup: ok (${memoryCount} memories, ${kbChunkCount} kb chunks, kb_catalog=${kbCatalogLines} lines)
|
|
79
|
+
`
|
|
80
|
+
);
|
|
81
|
+
process.stdout.write(formatSessionStartStdout(agent, ctx));
|
|
82
|
+
return 0;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
process.stderr.write(`ctxdb warmup: unexpected error: ${err?.message ?? err}
|
|
85
|
+
`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
90
|
+
main().then((code) => {
|
|
91
|
+
process.exitCode = code;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
12
94
|
export {
|
|
13
95
|
HOOK_TIMEOUT_MS,
|
|
14
96
|
composeSessionStart,
|
package/dist/hooks/stop.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
captureTurn
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-SAQT6VL6.js";
|
|
5
|
+
import "../chunk-USMJLDBD.js";
|
|
6
6
|
import {
|
|
7
7
|
HttpClient,
|
|
8
8
|
agentFromArgvWithFallback,
|
|
9
9
|
debug,
|
|
10
10
|
load,
|
|
11
11
|
setDebug
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-JFTKYEVN.js";
|
|
13
13
|
import {
|
|
14
14
|
shouldSkipHooks
|
|
15
15
|
} from "../chunk-UEKR2Z3S.js";
|
|
@@ -2,12 +2,89 @@
|
|
|
2
2
|
import {
|
|
3
3
|
composeUserPromptSubmit,
|
|
4
4
|
formatUserPromptSubmitStdout
|
|
5
|
-
} from "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
} from "../chunk-BVCDRUU4.js";
|
|
6
|
+
import "../chunk-Q4JYST7K.js";
|
|
7
|
+
import "../chunk-3PFMHU3C.js";
|
|
8
|
+
import {
|
|
9
|
+
isCircuitOpen
|
|
10
|
+
} from "../chunk-USMJLDBD.js";
|
|
11
|
+
import {
|
|
12
|
+
HttpClient,
|
|
13
|
+
agentFromArgvWithFallback,
|
|
14
|
+
debug,
|
|
15
|
+
isComplete,
|
|
16
|
+
load,
|
|
17
|
+
setDebug
|
|
18
|
+
} from "../chunk-JFTKYEVN.js";
|
|
19
|
+
import {
|
|
20
|
+
shouldSkipHooks
|
|
21
|
+
} from "../chunk-UEKR2Z3S.js";
|
|
22
|
+
|
|
23
|
+
// src/hooks/user-prompt-submit.ts
|
|
24
|
+
import { pathToFileURL } from "url";
|
|
25
|
+
async function readStdinJson() {
|
|
26
|
+
let raw = "";
|
|
27
|
+
for await (const chunk of process.stdin) raw += chunk;
|
|
28
|
+
if (!raw.trim()) return {};
|
|
29
|
+
try {
|
|
30
|
+
const obj = JSON.parse(raw);
|
|
31
|
+
return obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {};
|
|
32
|
+
} catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function main() {
|
|
37
|
+
try {
|
|
38
|
+
if (shouldSkipHooks()) return 0;
|
|
39
|
+
const event = await readStdinJson();
|
|
40
|
+
const { agent, fellBack } = agentFromArgvWithFallback();
|
|
41
|
+
if (fellBack) {
|
|
42
|
+
process.stderr.write(
|
|
43
|
+
`ctxdb recall: agent unspecified, defaulting to ${agent}
|
|
44
|
+
`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const prompt = typeof event.prompt === "string" ? event.prompt : "";
|
|
48
|
+
if (!prompt.trim()) return 0;
|
|
49
|
+
const cfg = load({ agent });
|
|
50
|
+
setDebug(cfg.debug);
|
|
51
|
+
debug("recall", "start", { prompt: prompt.slice(0, 200), userId: cfg.userId });
|
|
52
|
+
if (!isComplete(cfg) || !cfg.autoRecall) {
|
|
53
|
+
debug("recall", "skip (config incomplete or autoRecall=false)");
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
if (isCircuitOpen(agent, cfg.baseUrl)) {
|
|
57
|
+
debug("recall", "skip (circuit open)");
|
|
58
|
+
process.stderr.write(`ctxdb recall: skip (circuit_open: ${cfg.baseUrl})
|
|
59
|
+
`);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
const client = new HttpClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, timeoutMs: 5e3 });
|
|
63
|
+
const { ctx, recall, kbBlock } = await composeUserPromptSubmit(cfg, agent, client, prompt);
|
|
64
|
+
if (!recall.ok && !recall.additionalContext && !kbBlock) {
|
|
65
|
+
const reason = recall.reason ?? "no_context";
|
|
66
|
+
debug("recall", `no result: ${reason}`, recall);
|
|
67
|
+
if (recall.reason && recall.reason.startsWith("http_error:")) {
|
|
68
|
+
process.stderr.write(`ctxdb recall: ${recall.reason}
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
if (!ctx) return 0;
|
|
74
|
+
debug("recall", `ok, additionalContext length=${ctx.length}`);
|
|
75
|
+
process.stdout.write(formatUserPromptSubmitStdout(agent, ctx));
|
|
76
|
+
return 0;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
process.stderr.write(`ctxdb recall: unexpected error: ${err?.message ?? err}
|
|
79
|
+
`);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
84
|
+
main().then((code) => {
|
|
85
|
+
process.exitCode = code;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
11
88
|
export {
|
|
12
89
|
composeUserPromptSubmit,
|
|
13
90
|
formatUserPromptSubmitStdout
|
|
@@ -79,7 +79,9 @@ ctxdb kb documents-list <kb-name> --agent={{agent}}
|
|
|
79
79
|
ctxdb kb upload-text <kb-name> <doc-name> --text="<body>" --agent={{agent}}
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
副作用操作。命令默认在服务端接受文档后立即返回;此时
|
|
83
|
+
`ingest_status=processing` 只表示已受理,不表示入库成功。调用方必须等到文本可检索时加
|
|
84
|
+
`--wait`。**用户没明示 KB 名前先 recipe 2 列出来确认**(见 §5 注意事项 3)。KB 不存在会直接报错,不自动创建。
|
|
83
85
|
|
|
84
86
|
**何时不适用**:内容是本地文件 → recipe 5。
|
|
85
87
|
|
|
@@ -91,7 +93,11 @@ ctxdb kb upload-text <kb-name> <doc-name> --text="<body>" --agent={{agent}}
|
|
|
91
93
|
ctxdb kb upload-file <kb-name> <local-path> --agent={{agent}}
|
|
92
94
|
```
|
|
93
95
|
|
|
94
|
-
|
|
96
|
+
文件格式和大小限制由服务端决定,CLI 不维护本地允许列表;未知扩展名也会提交给服务端
|
|
97
|
+
判断。CLI 先尝试普通流式上传,服务端明确要求时会自动切换为分片上传。默认在服务端接受
|
|
98
|
+
文档后立即返回;调用方必须等到 chunking 结束时加 `--wait`。`--no-wait` 仍可使用,
|
|
99
|
+
行为与默认值相同(见 §6)。分片 init、part、complete 当前均为单次请求,超时或断网后
|
|
100
|
+
不会自动重试,以免服务端已成功但响应丢失时产生重复副作用。
|
|
95
101
|
|
|
96
102
|
**何时不适用**:要上传的内容是字符串而非本地文件 → recipe 4。
|
|
97
103
|
|
|
@@ -173,7 +179,11 @@ ctxdb kb upload-file <kb-name> <local-path> --agent={{agent}}
|
|
|
173
179
|
|------|------|
|
|
174
180
|
| `--doc-name` | (`upload-file`)服务端文档名,默认取本地文件名 |
|
|
175
181
|
| `--file-path` | 服务端逻辑路径(归档/分类用) |
|
|
176
|
-
| `--
|
|
182
|
+
| `--wait` | 等到 ingest 成功、失败或达到原有超时;用于迁移旧的默认等待行为 |
|
|
183
|
+
| `--no-wait` | 兼容参数;与新的“受理后立即返回”默认行为相同 |
|
|
184
|
+
|
|
185
|
+
`--wait` 与 `--no-wait` 互斥。无等待模式返回的
|
|
186
|
+
`ingest_status=processing` 不能当作终态成功。
|
|
177
187
|
|
|
178
188
|
### `kb create` 全部 flag
|
|
179
189
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
checkLatestVersion,
|
|
4
|
+
recordVersionCheckFailure,
|
|
5
|
+
recordVersionCheckSuccess,
|
|
6
|
+
releaseVersionCheckLock
|
|
7
|
+
} from "../chunk-BQA7YSXT.js";
|
|
8
|
+
|
|
9
|
+
// src/workers/version-check.ts
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import { dirname, resolve } from "path";
|
|
12
|
+
function runVersionCheckWorker(options) {
|
|
13
|
+
const now = options.now ?? Date.now;
|
|
14
|
+
try {
|
|
15
|
+
const result = (options.check ?? checkLatestVersion)(options.currentVersion);
|
|
16
|
+
if (result.error || result.latest === null) {
|
|
17
|
+
recordVersionCheckFailure(options.paths, options.currentVersion, now());
|
|
18
|
+
} else {
|
|
19
|
+
recordVersionCheckSuccess(
|
|
20
|
+
options.paths,
|
|
21
|
+
options.currentVersion,
|
|
22
|
+
result.latest,
|
|
23
|
+
now()
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
recordVersionCheckFailure(options.paths, options.currentVersion, now());
|
|
28
|
+
} finally {
|
|
29
|
+
releaseVersionCheckLock(options.paths, options.lockToken);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseWorkerArgs(argv) {
|
|
33
|
+
const values = /* @__PURE__ */ new Map();
|
|
34
|
+
for (const argument of argv) {
|
|
35
|
+
if (!argument.startsWith("--")) continue;
|
|
36
|
+
const separator = argument.indexOf("=");
|
|
37
|
+
if (separator < 3) continue;
|
|
38
|
+
values.set(argument.slice(2, separator), argument.slice(separator + 1));
|
|
39
|
+
}
|
|
40
|
+
const currentVersion = values.get("current-version");
|
|
41
|
+
const statePath = values.get("state-path");
|
|
42
|
+
const lockPath = values.get("lock-path");
|
|
43
|
+
const lockToken = values.get("lock-token");
|
|
44
|
+
if (!currentVersion || !statePath || !lockPath || !lockToken) return null;
|
|
45
|
+
return {
|
|
46
|
+
currentVersion,
|
|
47
|
+
lockToken,
|
|
48
|
+
paths: { cacheDir: dirname(statePath), statePath, lockPath }
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function isProcessEntryPoint() {
|
|
52
|
+
if (!process.argv[1]) return false;
|
|
53
|
+
try {
|
|
54
|
+
return resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (isProcessEntryPoint()) {
|
|
60
|
+
const options = parseWorkerArgs(process.argv.slice(2));
|
|
61
|
+
if (options) runVersionCheckWorker(options);
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
runVersionCheckWorker
|
|
65
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aliyunrds/ctxdb",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
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|hermes>` installer, per-agent config, hooks/plugins, and SKILL.md.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -30,11 +30,13 @@
|
|
|
30
30
|
"node": ">=20"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"semver": "^7.8.5",
|
|
33
34
|
"yaml": "^2.9.0",
|
|
34
35
|
"@aliyunrds/ctxdb-shared": "~0.0.5"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@types/node": "^22.15.0",
|
|
39
|
+
"@types/semver": "^7.7.1",
|
|
38
40
|
"@vitest/coverage-v8": "^4.0.18",
|
|
39
41
|
"tsup": "^8.5.0",
|
|
40
42
|
"typescript": "^5.8.3",
|
package/dist/chunk-FD3AMXVU.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
fetchKbCatalogBlock,
|
|
4
|
-
recallTurn
|
|
5
|
-
} from "./chunk-EUQ3OFCQ.js";
|
|
6
|
-
import {
|
|
7
|
-
isCircuitOpen
|
|
8
|
-
} from "./chunk-TGVURF54.js";
|
|
9
|
-
import {
|
|
10
|
-
HttpClient,
|
|
11
|
-
agentFromArgvWithFallback,
|
|
12
|
-
debug,
|
|
13
|
-
isComplete,
|
|
14
|
-
load,
|
|
15
|
-
setDebug
|
|
16
|
-
} from "./chunk-6FZL67GH.js";
|
|
17
|
-
import {
|
|
18
|
-
shouldSkipHooks
|
|
19
|
-
} from "./chunk-UEKR2Z3S.js";
|
|
20
|
-
|
|
21
|
-
// src/hooks/user-prompt-submit.ts
|
|
22
|
-
import { pathToFileURL } from "url";
|
|
23
|
-
async function composeUserPromptSubmit(cfg, agent, client, prompt) {
|
|
24
|
-
const [recall, kbBlock] = await Promise.all([
|
|
25
|
-
recallTurn(prompt, cfg, client, agent),
|
|
26
|
-
cfg.kbCatalogInjection === "user_prompt_submit" ? fetchKbCatalogBlock(client, agent).catch(() => "") : Promise.resolve("")
|
|
27
|
-
]);
|
|
28
|
-
let ctx = recall.additionalContext || "";
|
|
29
|
-
if (kbBlock) ctx = ctx ? `${ctx}
|
|
30
|
-
|
|
31
|
-
${kbBlock}` : kbBlock;
|
|
32
|
-
return { ctx, recall, kbBlock };
|
|
33
|
-
}
|
|
34
|
-
function formatUserPromptSubmitStdout(agent, ctx) {
|
|
35
|
-
if (agent === "codex") return ctx + "\n";
|
|
36
|
-
const out = {
|
|
37
|
-
hookSpecificOutput: {
|
|
38
|
-
hookEventName: "UserPromptSubmit",
|
|
39
|
-
additionalContext: ctx
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
return JSON.stringify(out) + "\n";
|
|
43
|
-
}
|
|
44
|
-
async function readStdinJson() {
|
|
45
|
-
let raw = "";
|
|
46
|
-
for await (const chunk of process.stdin) raw += chunk;
|
|
47
|
-
if (!raw.trim()) return {};
|
|
48
|
-
try {
|
|
49
|
-
const obj = JSON.parse(raw);
|
|
50
|
-
return obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {};
|
|
51
|
-
} catch {
|
|
52
|
-
return {};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
async function main() {
|
|
56
|
-
try {
|
|
57
|
-
if (shouldSkipHooks()) return 0;
|
|
58
|
-
const event = await readStdinJson();
|
|
59
|
-
const { agent, fellBack } = agentFromArgvWithFallback();
|
|
60
|
-
if (fellBack) {
|
|
61
|
-
process.stderr.write(
|
|
62
|
-
`ctxdb recall: agent unspecified, defaulting to ${agent}
|
|
63
|
-
`
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
const prompt = typeof event.prompt === "string" ? event.prompt : "";
|
|
67
|
-
if (!prompt.trim()) return 0;
|
|
68
|
-
const cfg = load({ agent });
|
|
69
|
-
setDebug(cfg.debug);
|
|
70
|
-
debug("recall", "start", { prompt: prompt.slice(0, 200), userId: cfg.userId });
|
|
71
|
-
if (!isComplete(cfg) || !cfg.autoRecall) {
|
|
72
|
-
debug("recall", "skip (config incomplete or autoRecall=false)");
|
|
73
|
-
return 0;
|
|
74
|
-
}
|
|
75
|
-
if (isCircuitOpen(agent, cfg.baseUrl)) {
|
|
76
|
-
debug("recall", "skip (circuit open)");
|
|
77
|
-
process.stderr.write(`ctxdb recall: skip (circuit_open: ${cfg.baseUrl})
|
|
78
|
-
`);
|
|
79
|
-
return 0;
|
|
80
|
-
}
|
|
81
|
-
const client = new HttpClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, timeoutMs: 5e3 });
|
|
82
|
-
const { ctx, recall, kbBlock } = await composeUserPromptSubmit(cfg, agent, client, prompt);
|
|
83
|
-
if (!recall.ok && !recall.additionalContext && !kbBlock) {
|
|
84
|
-
const reason = recall.reason ?? "no_context";
|
|
85
|
-
debug("recall", `no result: ${reason}`, recall);
|
|
86
|
-
if (recall.reason && recall.reason.startsWith("http_error:")) {
|
|
87
|
-
process.stderr.write(`ctxdb recall: ${recall.reason}
|
|
88
|
-
`);
|
|
89
|
-
}
|
|
90
|
-
return 0;
|
|
91
|
-
}
|
|
92
|
-
if (!ctx) return 0;
|
|
93
|
-
debug("recall", `ok, additionalContext length=${ctx.length}`);
|
|
94
|
-
process.stdout.write(formatUserPromptSubmitStdout(agent, ctx));
|
|
95
|
-
return 0;
|
|
96
|
-
} catch (err) {
|
|
97
|
-
process.stderr.write(`ctxdb recall: unexpected error: ${err?.message ?? err}
|
|
98
|
-
`);
|
|
99
|
-
return 0;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
103
|
-
main().then((code) => {
|
|
104
|
-
process.exitCode = code;
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export {
|
|
109
|
-
composeUserPromptSubmit,
|
|
110
|
-
formatUserPromptSubmitStdout
|
|
111
|
-
};
|