@aliyunrds/ctxdb 1.0.1 → 1.0.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/dist/chunk-2CSEFSLC.js +33 -0
- package/dist/{chunk-RZONPKY4.js → chunk-WTP4ZX22.js} +2 -84
- package/dist/cli/main.js +10 -8
- package/dist/hooks/hermes-pre-llm-call.js +2 -2
- package/dist/hooks/session-start.js +86 -4
- package/dist/hooks/user-prompt-submit.js +81 -4
- package/package.json +3 -1
- package/dist/chunk-FD3AMXVU.js +0 -111
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
fetchKbCatalogBlock,
|
|
4
|
+
recallTurn
|
|
5
|
+
} from "./chunk-EUQ3OFCQ.js";
|
|
6
|
+
|
|
7
|
+
// src/lib/user-prompt-submit-compose.ts
|
|
8
|
+
async function composeUserPromptSubmit(cfg, agent, client, prompt) {
|
|
9
|
+
const [recall, kbBlock] = await Promise.all([
|
|
10
|
+
recallTurn(prompt, cfg, client, agent),
|
|
11
|
+
cfg.kbCatalogInjection === "user_prompt_submit" ? fetchKbCatalogBlock(client, agent).catch(() => "") : Promise.resolve("")
|
|
12
|
+
]);
|
|
13
|
+
let ctx = recall.additionalContext || "";
|
|
14
|
+
if (kbBlock) ctx = ctx ? `${ctx}
|
|
15
|
+
|
|
16
|
+
${kbBlock}` : kbBlock;
|
|
17
|
+
return { ctx, recall, kbBlock };
|
|
18
|
+
}
|
|
19
|
+
function formatUserPromptSubmitStdout(agent, ctx) {
|
|
20
|
+
if (agent === "codex") return ctx + "\n";
|
|
21
|
+
const out = {
|
|
22
|
+
hookSpecificOutput: {
|
|
23
|
+
hookEventName: "UserPromptSubmit",
|
|
24
|
+
additionalContext: ctx
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
return JSON.stringify(out) + "\n";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
composeUserPromptSubmit,
|
|
32
|
+
formatUserPromptSubmitStdout
|
|
33
|
+
};
|
|
@@ -4,22 +4,8 @@ import {
|
|
|
4
4
|
recallTurn
|
|
5
5
|
} from "./chunk-EUQ3OFCQ.js";
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
} from "./chunk-TGVURF54.js";
|
|
9
|
-
import {
|
|
10
|
-
HttpClient,
|
|
11
|
-
agentFromArgvWithFallback,
|
|
12
|
-
debug,
|
|
13
|
-
isComplete,
|
|
14
|
-
load,
|
|
15
|
-
setDebug
|
|
7
|
+
debug
|
|
16
8
|
} from "./chunk-6FZL67GH.js";
|
|
17
|
-
import {
|
|
18
|
-
shouldSkipHooks
|
|
19
|
-
} from "./chunk-UEKR2Z3S.js";
|
|
20
|
-
|
|
21
|
-
// src/hooks/session-start.ts
|
|
22
|
-
import { pathToFileURL } from "url";
|
|
23
9
|
|
|
24
10
|
// src/lib/warmup-recall.ts
|
|
25
11
|
import { execSync } from "child_process";
|
|
@@ -72,7 +58,7 @@ async function warmupRecall(cwd, cfg, client) {
|
|
|
72
58
|
return recallTurn(query, cfg, client);
|
|
73
59
|
}
|
|
74
60
|
|
|
75
|
-
// src/
|
|
61
|
+
// src/lib/session-start-compose.ts
|
|
76
62
|
var HOOK_TIMEOUT_MS = 5e3;
|
|
77
63
|
function timeout(ms) {
|
|
78
64
|
return new Promise((resolve) => setTimeout(() => resolve(null), ms));
|
|
@@ -109,74 +95,6 @@ function formatSessionStartStdout(agent, ctx) {
|
|
|
109
95
|
};
|
|
110
96
|
return JSON.stringify(out) + "\n";
|
|
111
97
|
}
|
|
112
|
-
async function readStdinJson() {
|
|
113
|
-
let raw = "";
|
|
114
|
-
for await (const chunk of process.stdin) raw += chunk;
|
|
115
|
-
if (!raw.trim()) return {};
|
|
116
|
-
try {
|
|
117
|
-
const obj = JSON.parse(raw);
|
|
118
|
-
return obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {};
|
|
119
|
-
} catch {
|
|
120
|
-
return {};
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
async function main() {
|
|
124
|
-
try {
|
|
125
|
-
if (shouldSkipHooks()) return 0;
|
|
126
|
-
const event = await readStdinJson();
|
|
127
|
-
const { agent, fellBack } = agentFromArgvWithFallback();
|
|
128
|
-
if (fellBack) {
|
|
129
|
-
process.stderr.write(
|
|
130
|
-
`ctxdb warmup: agent unspecified, defaulting to ${agent}
|
|
131
|
-
`
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
const cwd = typeof event.cwd === "string" ? event.cwd : "";
|
|
135
|
-
if (!cwd) return 0;
|
|
136
|
-
const cfg = load({ agent });
|
|
137
|
-
setDebug(cfg.debug);
|
|
138
|
-
debug("warmup", "start", { cwd, userId: cfg.userId });
|
|
139
|
-
const kbInjectHere = cfg.kbCatalogInjection === "session_start";
|
|
140
|
-
if (!isComplete(cfg) || !cfg.warmupRecall && !kbInjectHere) {
|
|
141
|
-
debug("warmup", "skip (config incomplete or both warmup+kb off)");
|
|
142
|
-
return 0;
|
|
143
|
-
}
|
|
144
|
-
if (isCircuitOpen(agent, cfg.baseUrl)) {
|
|
145
|
-
debug("warmup", "skip (circuit open)");
|
|
146
|
-
return 0;
|
|
147
|
-
}
|
|
148
|
-
const client = new HttpClient({
|
|
149
|
-
baseUrl: cfg.baseUrl,
|
|
150
|
-
apiKey: cfg.apiKey,
|
|
151
|
-
timeoutMs: HOOK_TIMEOUT_MS
|
|
152
|
-
});
|
|
153
|
-
const composed = await composeSessionStart(cfg, agent, client, cwd);
|
|
154
|
-
if (composed.warmupTimedOut) {
|
|
155
|
-
process.stderr.write("ctxdb warmup: timeout\n");
|
|
156
|
-
}
|
|
157
|
-
if (!composed.ctx) return 0;
|
|
158
|
-
const { memoryCount, kbChunkCount, kbCatalogLines, ctx } = composed;
|
|
159
|
-
debug(
|
|
160
|
-
"warmup",
|
|
161
|
-
`ok, memories=${memoryCount} kb=${kbChunkCount} kb_catalog=${kbCatalogLines}`
|
|
162
|
-
);
|
|
163
|
-
process.stderr.write(
|
|
164
|
-
`ctxdb warmup: ok (${memoryCount} memories, ${kbChunkCount} kb chunks, kb_catalog=${kbCatalogLines} lines)
|
|
165
|
-
`
|
|
166
|
-
);
|
|
167
|
-
process.stdout.write(formatSessionStartStdout(agent, ctx));
|
|
168
|
-
return 0;
|
|
169
|
-
} catch (err) {
|
|
170
|
-
process.stderr.write(`ctxdb warmup: unexpected error: ${err?.message ?? err}
|
|
171
|
-
`);
|
|
172
|
-
return 0;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
176
|
-
main().then((code) => {
|
|
177
|
-
process.exitCode = code;
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
98
|
|
|
181
99
|
export {
|
|
182
100
|
HOOK_TIMEOUT_MS,
|
package/dist/cli/main.js
CHANGED
|
@@ -2000,6 +2000,7 @@ ${h}
|
|
|
2000
2000
|
import { spawnSync } from "child_process";
|
|
2001
2001
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2002
2002
|
import { dirname as dirname2 } from "path";
|
|
2003
|
+
import semver from "semver";
|
|
2003
2004
|
var PACKAGE_NAME = "@aliyunrds/ctxdb";
|
|
2004
2005
|
var NPM_VIEW_TIMEOUT_MS = 1e4;
|
|
2005
2006
|
function npmCommand(platform = process.platform) {
|
|
@@ -2042,12 +2043,11 @@ function spawnFailureMessage(result, commandLabel) {
|
|
|
2042
2043
|
return `${commandLabel} exited with code ${result.status}`;
|
|
2043
2044
|
}
|
|
2044
2045
|
function isNewerVersion(candidate, current) {
|
|
2045
|
-
const
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
return cPat > uPat;
|
|
2046
|
+
const candidateVersion = semver.valid(candidate);
|
|
2047
|
+
const currentVersion = semver.valid(current);
|
|
2048
|
+
return Boolean(
|
|
2049
|
+
candidateVersion && currentVersion && semver.gt(candidateVersion, currentVersion)
|
|
2050
|
+
);
|
|
2051
2051
|
}
|
|
2052
2052
|
function runSelfUpdate(currentVersion, passthroughArgs = [], options = {}) {
|
|
2053
2053
|
const method = options.installMethod ?? detectInstallMethod();
|
|
@@ -2069,8 +2069,10 @@ function runSelfUpdate(currentVersion, passthroughArgs = [], options = {}) {
|
|
|
2069
2069
|
};
|
|
2070
2070
|
}
|
|
2071
2071
|
if (!check.isNewer) {
|
|
2072
|
-
process.stderr.write(
|
|
2073
|
-
`)
|
|
2072
|
+
process.stderr.write(
|
|
2073
|
+
`ctxdb: package already up to date (current v${currentVersion}, latest v${check.latest})
|
|
2074
|
+
`
|
|
2075
|
+
);
|
|
2074
2076
|
return { ok: true, updated: false, fromVersion: currentVersion };
|
|
2075
2077
|
}
|
|
2076
2078
|
process.stderr.write(`ctxdb: updating ${PACKAGE_NAME} v${currentVersion} \u2192 v${check.latest}...
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HOOK_TIMEOUT_MS,
|
|
4
4
|
composeSessionStart
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-WTP4ZX22.js";
|
|
6
6
|
import {
|
|
7
7
|
composeUserPromptSubmit
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-2CSEFSLC.js";
|
|
9
9
|
import {
|
|
10
10
|
fetchKbCatalogBlock
|
|
11
11
|
} from "../chunk-EUQ3OFCQ.js";
|
|
@@ -3,12 +3,94 @@ import {
|
|
|
3
3
|
HOOK_TIMEOUT_MS,
|
|
4
4
|
composeSessionStart,
|
|
5
5
|
formatSessionStartStdout
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-WTP4ZX22.js";
|
|
7
7
|
import "../chunk-EUQ3OFCQ.js";
|
|
8
8
|
import "../chunk-UH7AJF6F.js";
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import {
|
|
10
|
+
isCircuitOpen
|
|
11
|
+
} from "../chunk-TGVURF54.js";
|
|
12
|
+
import {
|
|
13
|
+
HttpClient,
|
|
14
|
+
agentFromArgvWithFallback,
|
|
15
|
+
debug,
|
|
16
|
+
isComplete,
|
|
17
|
+
load,
|
|
18
|
+
setDebug
|
|
19
|
+
} from "../chunk-6FZL67GH.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,
|
|
@@ -2,12 +2,89 @@
|
|
|
2
2
|
import {
|
|
3
3
|
composeUserPromptSubmit,
|
|
4
4
|
formatUserPromptSubmitStdout
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-2CSEFSLC.js";
|
|
6
6
|
import "../chunk-EUQ3OFCQ.js";
|
|
7
7
|
import "../chunk-UH7AJF6F.js";
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import {
|
|
9
|
+
isCircuitOpen
|
|
10
|
+
} from "../chunk-TGVURF54.js";
|
|
11
|
+
import {
|
|
12
|
+
HttpClient,
|
|
13
|
+
agentFromArgvWithFallback,
|
|
14
|
+
debug,
|
|
15
|
+
isComplete,
|
|
16
|
+
load,
|
|
17
|
+
setDebug
|
|
18
|
+
} from "../chunk-6FZL67GH.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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aliyunrds/ctxdb",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.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|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
|
-
};
|