@remnic/plugin-codex 9.3.612 → 9.3.614
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 +2 -2
- package/hooks/bin/remnic-codex-hook.cjs +969 -0
- package/hooks/bin/remnic-codex-hook.ps1 +10 -0
- package/hooks/bin/remnic-codex-hook.sh +7 -0
- package/hooks/bin/remnic-codex-hook.test.cjs +512 -0
- package/hooks/hooks.json +8 -4
- package/package.json +2 -2
- package/hooks/bin/post-tool-observe.sh +0 -280
- package/hooks/bin/session-end.sh +0 -281
- package/hooks/bin/session-start.sh +0 -257
- package/hooks/bin/user-prompt-recall.sh +0 -114
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Remnic SessionStart hook for Codex.
|
|
3
|
-
# Recalls project context and user preferences at session start.
|
|
4
|
-
# Tries auto mode (45s) then falls back to minimal mode (20s).
|
|
5
|
-
# Starts daemon if not running.
|
|
6
|
-
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
|
|
9
|
-
ensure_migrated() {
|
|
10
|
-
if [ -f "${HOME}/.remnic/.migrated-from-engram" ]; then
|
|
11
|
-
return 0
|
|
12
|
-
fi
|
|
13
|
-
if [ ! -d "${HOME}/.engram" ] && [ ! -f "${HOME}/.config/engram/config.json" ]; then
|
|
14
|
-
return 0
|
|
15
|
-
fi
|
|
16
|
-
if command -v remnic >/dev/null 2>&1; then
|
|
17
|
-
remnic migrate >/dev/null 2>&1 || true
|
|
18
|
-
elif command -v engram >/dev/null 2>&1; then
|
|
19
|
-
engram migrate >/dev/null 2>&1 || true
|
|
20
|
-
fi
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
ensure_migrated
|
|
24
|
-
|
|
25
|
-
REMNIC_HOST="${REMNIC_HOST:-${ENGRAM_HOST:-127.0.0.1}}"
|
|
26
|
-
REMNIC_PORT="${REMNIC_PORT:-${ENGRAM_PORT:-4318}}"
|
|
27
|
-
REMNIC_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/recall"
|
|
28
|
-
REMNIC_HEALTH_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/health"
|
|
29
|
-
TOKEN_FILES=("${HOME}/.remnic/tokens.json" "${HOME}/.engram/tokens.json")
|
|
30
|
-
|
|
31
|
-
LOG="${HOME}/.remnic/logs/remnic-session-recall.log"
|
|
32
|
-
mkdir -p "$(dirname "$LOG")"
|
|
33
|
-
log() { echo "$(date '+%F %T') [codex-session-start] $*" >> "$LOG"; }
|
|
34
|
-
|
|
35
|
-
# Read token from per-plugin token store
|
|
36
|
-
REMNIC_TOKEN=""
|
|
37
|
-
for TOKEN_FILE in "${TOKEN_FILES[@]}"; do
|
|
38
|
-
[ ! -f "$TOKEN_FILE" ] && continue
|
|
39
|
-
REMNIC_TOKEN="$(node -e "
|
|
40
|
-
const store = JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));
|
|
41
|
-
const tokens = store.tokens || [];
|
|
42
|
-
const cxc = tokens.find(t => t.connector === 'codex-cli');
|
|
43
|
-
const cx = tokens.find(t => t.connector === 'codex');
|
|
44
|
-
const oc = tokens.find(t => t.connector === 'openclaw');
|
|
45
|
-
let tok = (cxc && cxc.token) || (cx && cx.token) || (oc && oc.token) || '';
|
|
46
|
-
if (!tok) { tok = store['codex-cli'] || store['codex'] || store['openclaw'] || ''; }
|
|
47
|
-
process.stdout.write(tok);
|
|
48
|
-
" "$TOKEN_FILE" 2>/dev/null || echo "")"
|
|
49
|
-
[ -n "$REMNIC_TOKEN" ] && break
|
|
50
|
-
done
|
|
51
|
-
|
|
52
|
-
# Fallback to env var
|
|
53
|
-
[ -z "$REMNIC_TOKEN" ] && REMNIC_TOKEN="${OPENCLAW_REMNIC_ACCESS_TOKEN:-${OPENCLAW_ENGRAM_ACCESS_TOKEN:-}}"
|
|
54
|
-
|
|
55
|
-
INPUT="$(cat)"
|
|
56
|
-
SESSION_ID="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.session_id||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
57
|
-
CWD="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.cwd||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
58
|
-
PROJECT_NAME="$(basename "$CWD" 2>/dev/null || echo "unknown")"
|
|
59
|
-
|
|
60
|
-
# Resolve git context for the session's cwd (issue #569 PR 6).
|
|
61
|
-
# Produces either a JSON `codingContext` object to embed in the recall
|
|
62
|
-
# request, or an empty string when the cwd is not inside a git repo.
|
|
63
|
-
# Any failure silently drops back to no-context (CLAUDE.md #30 escape hatch).
|
|
64
|
-
CODING_CONTEXT_JSON=""
|
|
65
|
-
if [ -n "$CWD" ] && [ -d "$CWD" ] && command -v git >/dev/null 2>&1; then
|
|
66
|
-
REMNIC_GIT_TOP="$(git -C "$CWD" rev-parse --show-toplevel 2>/dev/null || echo "")"
|
|
67
|
-
if [ -n "$REMNIC_GIT_TOP" ]; then
|
|
68
|
-
REMNIC_GIT_BRANCH="$(git -C "$REMNIC_GIT_TOP" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD")"
|
|
69
|
-
[ "$REMNIC_GIT_BRANCH" = "HEAD" ] && REMNIC_GIT_BRANCH=""
|
|
70
|
-
REMNIC_GIT_ORIGIN="$(git -C "$REMNIC_GIT_TOP" remote get-url origin 2>/dev/null || echo "")"
|
|
71
|
-
REMNIC_GIT_DEFAULT_BRANCH="$(git -C "$REMNIC_GIT_TOP" symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||' || echo "")"
|
|
72
|
-
CODING_CONTEXT_JSON="$(REMNIC_GIT_TOP="$REMNIC_GIT_TOP" REMNIC_GIT_BRANCH="$REMNIC_GIT_BRANCH" REMNIC_GIT_ORIGIN="$REMNIC_GIT_ORIGIN" REMNIC_GIT_DEFAULT_BRANCH="$REMNIC_GIT_DEFAULT_BRANCH" node -e "
|
|
73
|
-
// Mirror @remnic/core's resolveGitContext for projectId derivation.
|
|
74
|
-
// FNV-1a 32-bit stable hash.
|
|
75
|
-
const rootPath = process.env.REMNIC_GIT_TOP || '';
|
|
76
|
-
const branch = process.env.REMNIC_GIT_BRANCH || null;
|
|
77
|
-
const origin = process.env.REMNIC_GIT_ORIGIN || '';
|
|
78
|
-
const defaultBranch = process.env.REMNIC_GIT_DEFAULT_BRANCH || null;
|
|
79
|
-
function stableHash(input) {
|
|
80
|
-
let hash = 0x811c9dc5;
|
|
81
|
-
for (let i = 0; i < input.length; i++) {
|
|
82
|
-
hash ^= input.charCodeAt(i);
|
|
83
|
-
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
84
|
-
}
|
|
85
|
-
return hash.toString(16).padStart(8, '0');
|
|
86
|
-
}
|
|
87
|
-
// Mirrors packages/remnic-core/src/coding/git-context.ts
|
|
88
|
-
// normalizeOriginUrl. Keep the two in sync so the hook-computed
|
|
89
|
-
// projectId matches what the daemon computes on the same origin.
|
|
90
|
-
function normalizeOriginUrl(raw) {
|
|
91
|
-
let u = (raw || '').trim();
|
|
92
|
-
if (!u) return '';
|
|
93
|
-
// Case-insensitive .git strip — matches the TS canonical form.
|
|
94
|
-
if (/\\.git\$/i.test(u)) u = u.slice(0, -4);
|
|
95
|
-
// Windows drive-letter: short-circuit scp parsing.
|
|
96
|
-
if (/^[A-Za-z]:[\\\\/]/.test(u)) return u.toLowerCase();
|
|
97
|
-
// Protocol form: handles ssh://, https://, file:///, bracketed
|
|
98
|
-
// IPv6 hosts, optional user, optional port, and empty host
|
|
99
|
-
// (file:///path).
|
|
100
|
-
const proto = /^[a-z][a-z0-9+.-]*:\\/\\/(?:[^@/]+@)?(\\[[^\\]]+\\]|[^/:]*)(?::(\\d+))?(\\/.*)?\$/i.exec(u);
|
|
101
|
-
if (proto) {
|
|
102
|
-
let host = proto[1] || '';
|
|
103
|
-
const wasBracketed = host.startsWith('[') && host.endsWith(']');
|
|
104
|
-
if (wasBracketed) host = host.slice(1, -1);
|
|
105
|
-
const port = proto[2];
|
|
106
|
-
const p = (proto[3] || '').replace(/^\\/+/, '');
|
|
107
|
-
const hostPort = port
|
|
108
|
-
? (wasBracketed ? '[' + host + ']:' + port : host + ':' + port)
|
|
109
|
-
: host;
|
|
110
|
-
const prefix = hostPort.length > 0 ? hostPort : 'localhost';
|
|
111
|
-
return (prefix + '/' + p).toLowerCase();
|
|
112
|
-
}
|
|
113
|
-
// scp form: [user@]host:path — user@ optional, bracketed IPv6 host
|
|
114
|
-
// supported. A matched path starting with // is a protocol-URL
|
|
115
|
-
// leftover and is rejected.
|
|
116
|
-
const scp = /^(?:([^@\\s\\/]+)@)?(\\[[^\\]]+\\]|[^:@\\s\\/]+):(.+)\$/.exec(u);
|
|
117
|
-
if (scp) {
|
|
118
|
-
let host = scp[2] || '';
|
|
119
|
-
if (host.startsWith('[') && host.endsWith(']')) host = host.slice(1, -1);
|
|
120
|
-
const p = scp[3] || '';
|
|
121
|
-
if (p.startsWith('//')) return u.toLowerCase();
|
|
122
|
-
return (host + '/' + p.replace(/^\\/+/, '')).toLowerCase();
|
|
123
|
-
}
|
|
124
|
-
return u.toLowerCase();
|
|
125
|
-
}
|
|
126
|
-
const normalized = normalizeOriginUrl(origin);
|
|
127
|
-
const projectId = normalized ? 'origin:' + stableHash(normalized) : 'root:' + stableHash(rootPath);
|
|
128
|
-
process.stdout.write(JSON.stringify({
|
|
129
|
-
projectId,
|
|
130
|
-
branch: branch || null,
|
|
131
|
-
rootPath,
|
|
132
|
-
defaultBranch: defaultBranch || null,
|
|
133
|
-
}));
|
|
134
|
-
" 2>/dev/null || echo "")"
|
|
135
|
-
fi
|
|
136
|
-
fi
|
|
137
|
-
|
|
138
|
-
log "session=$SESSION_ID project=$PROJECT_NAME coding-context=${CODING_CONTEXT_JSON:+yes}"
|
|
139
|
-
|
|
140
|
-
# Health check — start daemon if not running
|
|
141
|
-
if ! curl -sf --max-time 2 "$REMNIC_HEALTH_URL" >/dev/null 2>&1; then
|
|
142
|
-
log "daemon not responding, attempting start..."
|
|
143
|
-
if command -v remnic >/dev/null 2>&1; then
|
|
144
|
-
remnic daemon start >/dev/null 2>&1 &
|
|
145
|
-
elif command -v engram >/dev/null 2>&1; then
|
|
146
|
-
engram daemon start >/dev/null 2>&1 &
|
|
147
|
-
fi
|
|
148
|
-
sleep 2
|
|
149
|
-
if ! curl -sf --max-time 2 "$REMNIC_HEALTH_URL" >/dev/null 2>&1; then
|
|
150
|
-
log "daemon still not responding after start attempt"
|
|
151
|
-
echo '{"continue":true,"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[Remnic: daemon not running — start with: remnic daemon start]"}}'
|
|
152
|
-
exit 0
|
|
153
|
-
fi
|
|
154
|
-
fi
|
|
155
|
-
|
|
156
|
-
if [ -z "$REMNIC_TOKEN" ]; then
|
|
157
|
-
log "skipping: no token found"
|
|
158
|
-
echo '{"continue":true,"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[Remnic: no auth token — run: remnic connectors install codex-cli]"}}'
|
|
159
|
-
exit 0
|
|
160
|
-
fi
|
|
161
|
-
|
|
162
|
-
QUERY="Starting a new coding session in project: ${PROJECT_NAME}. Recall relevant memories, preferences, decisions, patterns, and context about this project and the user."
|
|
163
|
-
|
|
164
|
-
REQUEST_BODY="$(REMNIC_CODING_CONTEXT_JSON="$CODING_CONTEXT_JSON" node -e "
|
|
165
|
-
const body = {
|
|
166
|
-
query: process.argv[1],
|
|
167
|
-
sessionKey: process.argv[2],
|
|
168
|
-
topK: 12,
|
|
169
|
-
mode: 'auto',
|
|
170
|
-
};
|
|
171
|
-
const raw = process.env.REMNIC_CODING_CONTEXT_JSON || '';
|
|
172
|
-
if (raw) {
|
|
173
|
-
try { body.codingContext = JSON.parse(raw); } catch (_) {
|
|
174
|
-
// Context envelope was provided but failed to parse. Explicitly
|
|
175
|
-
// clear any previously-attached context for this session so a
|
|
176
|
-
// malformed envelope does not silently keep stale state.
|
|
177
|
-
body.codingContext = null;
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
// No git context resolvable for this cwd. Explicitly clear any
|
|
181
|
-
// previously-attached context so a session that moves out of a repo
|
|
182
|
-
// does not keep routing to the old project namespace.
|
|
183
|
-
body.codingContext = null;
|
|
184
|
-
}
|
|
185
|
-
process.stdout.write(JSON.stringify(body));
|
|
186
|
-
" "$QUERY" "$SESSION_ID" 2>/dev/null)"
|
|
187
|
-
|
|
188
|
-
[ -z "$REQUEST_BODY" ] && echo '{"continue":true}' && exit 0
|
|
189
|
-
|
|
190
|
-
log "attempting full recall (auto mode)..."
|
|
191
|
-
RAW="$(curl -s -w "\n%{http_code}" --max-time 45 \
|
|
192
|
-
-X POST "$REMNIC_URL" \
|
|
193
|
-
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
194
|
-
-H "Content-Type: application/json" \
|
|
195
|
-
-H "X-Engram-Client-Id: codex" \
|
|
196
|
-
-d "$REQUEST_BODY" 2>/dev/null)"
|
|
197
|
-
CURL_EXIT=$?
|
|
198
|
-
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
199
|
-
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
200
|
-
|
|
201
|
-
if [ $CURL_EXIT -ne 0 ] || ! [[ "$HTTP_STATUS" =~ ^2 ]] || [ -z "$RESPONSE" ]; then
|
|
202
|
-
log "full recall failed (curl=$CURL_EXIT http=$HTTP_STATUS) — falling back to minimal"
|
|
203
|
-
MINIMAL_BODY="$(REMNIC_CODING_CONTEXT_JSON="$CODING_CONTEXT_JSON" node -e "
|
|
204
|
-
const body = {
|
|
205
|
-
query: process.argv[1],
|
|
206
|
-
sessionKey: process.argv[2],
|
|
207
|
-
topK: 8,
|
|
208
|
-
mode: 'minimal',
|
|
209
|
-
};
|
|
210
|
-
const raw = process.env.REMNIC_CODING_CONTEXT_JSON || '';
|
|
211
|
-
if (raw) {
|
|
212
|
-
try { body.codingContext = JSON.parse(raw); } catch (_) {
|
|
213
|
-
body.codingContext = null;
|
|
214
|
-
}
|
|
215
|
-
} else {
|
|
216
|
-
body.codingContext = null;
|
|
217
|
-
}
|
|
218
|
-
process.stdout.write(JSON.stringify(body));
|
|
219
|
-
" "$QUERY" "$SESSION_ID" 2>/dev/null)"
|
|
220
|
-
RAW="$(curl -s -w "\n%{http_code}" --max-time 20 \
|
|
221
|
-
-X POST "$REMNIC_URL" \
|
|
222
|
-
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
223
|
-
-H "Content-Type: application/json" \
|
|
224
|
-
-H "X-Engram-Client-Id: codex" \
|
|
225
|
-
-d "${MINIMAL_BODY:-$REQUEST_BODY}" 2>/dev/null)"
|
|
226
|
-
CURL_EXIT=$?
|
|
227
|
-
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
228
|
-
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
229
|
-
[[ "$CURL_EXIT" -eq 0 && "$HTTP_STATUS" =~ ^2 ]] && log "minimal recall succeeded" || { log "minimal recall also failed"; CURL_EXIT=1; }
|
|
230
|
-
fi
|
|
231
|
-
|
|
232
|
-
if [ $CURL_EXIT -eq 0 ] && [[ "$HTTP_STATUS" =~ ^2 ]] && [ -n "$RESPONSE" ]; then
|
|
233
|
-
CONTEXT="$(node -e "
|
|
234
|
-
const d = JSON.parse(process.argv[1]);
|
|
235
|
-
const ctx = d.context || '';
|
|
236
|
-
const count = d.count || 0;
|
|
237
|
-
const mode = d.mode || '';
|
|
238
|
-
if (ctx) {
|
|
239
|
-
const label = '[Remnic Memory Recall — ' + count + ' memories' + (mode ? ', ' + mode + ' mode' : '') + ']';
|
|
240
|
-
process.stdout.write(label + '\n\n' + ctx);
|
|
241
|
-
} else {
|
|
242
|
-
process.stdout.write('[Remnic: no relevant memories found for this session]');
|
|
243
|
-
}
|
|
244
|
-
" "$RESPONSE" 2>/dev/null || echo "[Remnic: recall parse error]")"
|
|
245
|
-
log "recall complete: $(echo "$CONTEXT" | head -1)"
|
|
246
|
-
else
|
|
247
|
-
CONTEXT="[Remnic: server unreachable — continuing without memory recall]"
|
|
248
|
-
log "$CONTEXT"
|
|
249
|
-
fi
|
|
250
|
-
|
|
251
|
-
node -e "
|
|
252
|
-
const context = process.argv[1];
|
|
253
|
-
process.stdout.write(JSON.stringify({
|
|
254
|
-
continue: true,
|
|
255
|
-
hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: context }
|
|
256
|
-
}));
|
|
257
|
-
" "$CONTEXT"
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Remnic UserPromptSubmit hook for Codex.
|
|
3
|
-
# Recalls per-prompt context using the user's message as query.
|
|
4
|
-
# Skips short prompts (<4 words). Minimal mode, 20s timeout.
|
|
5
|
-
|
|
6
|
-
set -euo pipefail
|
|
7
|
-
|
|
8
|
-
ensure_migrated() {
|
|
9
|
-
if [ -f "${HOME}/.remnic/.migrated-from-engram" ]; then
|
|
10
|
-
return 0
|
|
11
|
-
fi
|
|
12
|
-
if [ ! -d "${HOME}/.engram" ] && [ ! -f "${HOME}/.config/engram/config.json" ]; then
|
|
13
|
-
return 0
|
|
14
|
-
fi
|
|
15
|
-
if command -v remnic >/dev/null 2>&1; then
|
|
16
|
-
remnic migrate >/dev/null 2>&1 || true
|
|
17
|
-
elif command -v engram >/dev/null 2>&1; then
|
|
18
|
-
engram migrate >/dev/null 2>&1 || true
|
|
19
|
-
fi
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
ensure_migrated
|
|
23
|
-
|
|
24
|
-
REMNIC_HOST="${REMNIC_HOST:-${ENGRAM_HOST:-127.0.0.1}}"
|
|
25
|
-
REMNIC_PORT="${REMNIC_PORT:-${ENGRAM_PORT:-4318}}"
|
|
26
|
-
REMNIC_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/recall"
|
|
27
|
-
|
|
28
|
-
LOG="${HOME}/.remnic/logs/remnic-user-prompt-recall.log"
|
|
29
|
-
mkdir -p "$(dirname "$LOG")"
|
|
30
|
-
log() { echo "$(date '+%F %T') [codex-user-prompt] $*" >> "$LOG"; }
|
|
31
|
-
|
|
32
|
-
# Read token
|
|
33
|
-
REMNIC_TOKEN=""
|
|
34
|
-
for TOKEN_FILE in "${HOME}/.remnic/tokens.json" "${HOME}/.engram/tokens.json"; do
|
|
35
|
-
[ ! -f "$TOKEN_FILE" ] && continue
|
|
36
|
-
REMNIC_TOKEN="$(node -e "
|
|
37
|
-
const fs = require('fs');
|
|
38
|
-
const tokenFile = process.argv[1];
|
|
39
|
-
const store = JSON.parse(fs.readFileSync(tokenFile, 'utf8'));
|
|
40
|
-
const tokens = store.tokens || [];
|
|
41
|
-
const cxc = tokens.find(t => t.connector === 'codex-cli');
|
|
42
|
-
const cx = tokens.find(t => t.connector === 'codex');
|
|
43
|
-
const oc = tokens.find(t => t.connector === 'openclaw');
|
|
44
|
-
let tok = (cxc && cxc.token) || (cx && cx.token) || (oc && oc.token) || '';
|
|
45
|
-
if (!tok) { tok = store['codex-cli'] || store['codex'] || store['openclaw'] || ''; }
|
|
46
|
-
process.stdout.write(tok);
|
|
47
|
-
" "$TOKEN_FILE" 2>/dev/null || echo "")"
|
|
48
|
-
[ -n "$REMNIC_TOKEN" ] && break
|
|
49
|
-
done
|
|
50
|
-
[ -z "$REMNIC_TOKEN" ] && REMNIC_TOKEN="${OPENCLAW_REMNIC_ACCESS_TOKEN:-${OPENCLAW_ENGRAM_ACCESS_TOKEN:-}}"
|
|
51
|
-
|
|
52
|
-
INPUT="$(cat)"
|
|
53
|
-
|
|
54
|
-
if [ -z "$REMNIC_TOKEN" ]; then
|
|
55
|
-
echo '{"continue":true}'
|
|
56
|
-
exit 0
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
SESSION_ID="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.session_id||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
60
|
-
PROMPT="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.prompt||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
61
|
-
|
|
62
|
-
# Skip very short prompts
|
|
63
|
-
WORD_COUNT="$(echo "$PROMPT" | wc -w | tr -d ' ')"
|
|
64
|
-
if [ "$WORD_COUNT" -lt 4 ]; then
|
|
65
|
-
echo '{"continue":true}'
|
|
66
|
-
exit 0
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
log "session=$SESSION_ID words=$WORD_COUNT"
|
|
70
|
-
|
|
71
|
-
REQUEST_BODY="$(node -e "process.stdout.write(JSON.stringify({
|
|
72
|
-
query: process.argv[1],
|
|
73
|
-
sessionKey: process.argv[2],
|
|
74
|
-
topK: 8,
|
|
75
|
-
mode: 'minimal'
|
|
76
|
-
}))" "$PROMPT" "$SESSION_ID" 2>/dev/null)"
|
|
77
|
-
|
|
78
|
-
[ -z "$REQUEST_BODY" ] && echo '{"continue":true}' && exit 0
|
|
79
|
-
|
|
80
|
-
RAW="$(curl -s -w "\n%{http_code}" --max-time 20 \
|
|
81
|
-
-X POST "$REMNIC_URL" \
|
|
82
|
-
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
83
|
-
-H "Content-Type: application/json" \
|
|
84
|
-
-H "X-Engram-Client-Id: codex" \
|
|
85
|
-
-d "$REQUEST_BODY" 2>/dev/null)"
|
|
86
|
-
CURL_EXIT=$?
|
|
87
|
-
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
88
|
-
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
89
|
-
|
|
90
|
-
if [ $CURL_EXIT -ne 0 ] || ! [[ "$HTTP_STATUS" =~ ^2 ]] || [ -z "$RESPONSE" ]; then
|
|
91
|
-
log "recall failed (curl=$CURL_EXIT http=$HTTP_STATUS)"
|
|
92
|
-
echo '{"continue":true}'
|
|
93
|
-
exit 0
|
|
94
|
-
fi
|
|
95
|
-
|
|
96
|
-
node -e "
|
|
97
|
-
const d = JSON.parse(process.argv[1]);
|
|
98
|
-
const ctx = d.context || '';
|
|
99
|
-
const count = d.count || 0;
|
|
100
|
-
if (!ctx || count === 0) {
|
|
101
|
-
process.stdout.write(JSON.stringify({continue: true}));
|
|
102
|
-
} else {
|
|
103
|
-
process.stdout.write(JSON.stringify({
|
|
104
|
-
continue: true,
|
|
105
|
-
hookSpecificOutput: {
|
|
106
|
-
hookEventName: 'UserPromptSubmit',
|
|
107
|
-
additionalContext: '<remnic-memory count=\"' + count + '\">\n' + ctx + '\n</remnic-memory>'
|
|
108
|
-
}
|
|
109
|
-
}));
|
|
110
|
-
}
|
|
111
|
-
" "$RESPONSE" 2>/dev/null || echo '{"continue":true}'
|
|
112
|
-
|
|
113
|
-
COUNT="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(String(d.count||0))" "$RESPONSE" 2>/dev/null || echo "?")"
|
|
114
|
-
log "done: ${COUNT} memories injected"
|