@remnic/plugin-claude-code 10.0.39 → 10.0.41
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.
|
@@ -122,7 +122,7 @@ function cursorPath(home, sessionId) {
|
|
|
122
122
|
test("session-start: healthy server returns recall context with codingContext cleared outside a repo", async () => {
|
|
123
123
|
const home = mkHome();
|
|
124
124
|
const { server, port, calls } = await startServer((req, res) => {
|
|
125
|
-
if (req.url === "/engram/v1/
|
|
125
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
126
126
|
if (req.url === "/engram/v1/recall") {
|
|
127
127
|
return res.writeHead(200, { "Content-Type": "application/json" }).end(
|
|
128
128
|
JSON.stringify({ context: "remembered preferences", count: 3, mode: "auto" }),
|
|
@@ -154,7 +154,7 @@ test("session-start: health probe carries the bearer token (auth-gated daemons)"
|
|
|
154
154
|
const home = mkHome();
|
|
155
155
|
let healthAuth = "unset";
|
|
156
156
|
const { server, port } = await startServer((req, res) => {
|
|
157
|
-
if (req.url === "/engram/v1/
|
|
157
|
+
if (req.url === "/engram/v1/live") {
|
|
158
158
|
healthAuth = req.headers.authorization || null;
|
|
159
159
|
return res.writeHead(200).end("ok");
|
|
160
160
|
}
|
|
@@ -176,10 +176,85 @@ test("session-start: health probe carries the bearer token (auth-gated daemons)"
|
|
|
176
176
|
}
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
-
test("session-start:
|
|
179
|
+
test("session-start: hung diagnostic /health does not stall the live probe (#3257)", async () => {
|
|
180
|
+
const home = mkHome();
|
|
181
|
+
const { server, port, calls } = await startServer((req, res) => {
|
|
182
|
+
// The expensive diagnostic route never answers; only /live is healthy.
|
|
183
|
+
if (req.url === "/engram/v1/health") return;
|
|
184
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
185
|
+
if (req.url === "/engram/v1/recall") {
|
|
186
|
+
return res.writeHead(200, { "Content-Type": "application/json" }).end(
|
|
187
|
+
JSON.stringify({ context: "live recall", count: 1, mode: "auto" }),
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
res.writeHead(404).end();
|
|
191
|
+
});
|
|
192
|
+
try {
|
|
193
|
+
const { json } = await runHook("session-start", { session_id: "s1", cwd: home }, { port, home });
|
|
194
|
+
assert.equal(json.continue, true);
|
|
195
|
+
assert.match(json.hookSpecificOutput.additionalContext, /live recall/);
|
|
196
|
+
assert.ok(calls.some((c) => c.url === "/engram/v1/recall"), "recall reached the daemon");
|
|
197
|
+
} finally {
|
|
198
|
+
server.close();
|
|
199
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("session-start: 404 live falls back to legacy health within one budget", async () => {
|
|
180
204
|
const home = mkHome();
|
|
181
205
|
const { server, port, calls } = await startServer((req, res) => {
|
|
206
|
+
if (req.url === "/engram/v1/live") return res.writeHead(404).end();
|
|
182
207
|
if (req.url === "/engram/v1/health") return res.writeHead(200).end("ok");
|
|
208
|
+
if (req.url === "/engram/v1/recall") {
|
|
209
|
+
return res.writeHead(200, { "Content-Type": "application/json" }).end(
|
|
210
|
+
JSON.stringify({ context: "legacy recall", count: 1, mode: "auto" }),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
res.writeHead(404).end();
|
|
214
|
+
});
|
|
215
|
+
try {
|
|
216
|
+
const { json } = await runHook("session-start", { session_id: "s1", cwd: home }, { port, home });
|
|
217
|
+
assert.equal(json.continue, true);
|
|
218
|
+
assert.match(json.hookSpecificOutput.additionalContext, /legacy recall/);
|
|
219
|
+
const probeUrls = calls.filter((c) => c.url === "/engram/v1/live" || c.url === "/engram/v1/health").map((c) => c.url);
|
|
220
|
+
assert.deepEqual(probeUrls, ["/engram/v1/live", "/engram/v1/health"]);
|
|
221
|
+
} finally {
|
|
222
|
+
server.close();
|
|
223
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("session-start: 503 live is a final answer with no legacy retry", async () => {
|
|
228
|
+
const home = mkHome();
|
|
229
|
+
const { server, port, calls } = await startServer((req, res) => {
|
|
230
|
+
if (req.url === "/engram/v1/live") return res.writeHead(503).end();
|
|
231
|
+
// A 503 must NOT be re-probed on the legacy route, even when it would pass.
|
|
232
|
+
if (req.url === "/engram/v1/health") return res.writeHead(200).end("ok");
|
|
233
|
+
if (req.url === "/engram/v1/recall") {
|
|
234
|
+
return res.writeHead(200, { "Content-Type": "application/json" }).end(
|
|
235
|
+
JSON.stringify({ context: "ctx", count: 1, mode: "auto" }),
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
res.writeHead(404).end();
|
|
239
|
+
});
|
|
240
|
+
try {
|
|
241
|
+
const { json } = await runHook("session-start", { session_id: "s1", cwd: home }, { port, home });
|
|
242
|
+
assert.match(json.hookSpecificOutput.additionalContext, /daemon not running/);
|
|
243
|
+
assert.ok(!calls.some((c) => c.url === "/engram/v1/recall"), "recall is skipped");
|
|
244
|
+
assert.deepEqual(
|
|
245
|
+
calls.filter((c) => c.url.startsWith("/engram/v1/")).map((c) => c.url),
|
|
246
|
+
["/engram/v1/live", "/engram/v1/live"],
|
|
247
|
+
);
|
|
248
|
+
} finally {
|
|
249
|
+
server.close();
|
|
250
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("session-start: REMNIC_DAEMON_URL routes recall to the remote base URL (#2483)", async () => {
|
|
255
|
+
const home = mkHome();
|
|
256
|
+
const { server, port, calls } = await startServer((req, res) => {
|
|
257
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
183
258
|
if (req.url === "/engram/v1/recall") {
|
|
184
259
|
return res.writeHead(200, { "Content-Type": "application/json" }).end(
|
|
185
260
|
JSON.stringify({ context: "remote recall", count: 1, mode: "auto" }),
|
|
@@ -217,7 +292,7 @@ test("session-start: REMNIC_DAEMON_URL routes recall to the remote base URL (#24
|
|
|
217
292
|
test("recall body targets REMNIC_NAMESPACE when set, and omits namespace when unset", async () => {
|
|
218
293
|
const mk = () =>
|
|
219
294
|
startServer((req, res) => {
|
|
220
|
-
if (req.url === "/engram/v1/
|
|
295
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
221
296
|
if (req.url === "/engram/v1/recall") {
|
|
222
297
|
return res
|
|
223
298
|
.writeHead(200, { "Content-Type": "application/json" })
|
|
@@ -274,7 +349,7 @@ test("namespace targeting: ENGRAM_NAMESPACE fallback (recall) and observe body b
|
|
|
274
349
|
{
|
|
275
350
|
const home = mkHome();
|
|
276
351
|
const { server, port, calls } = await startServer((req, res) => {
|
|
277
|
-
if (req.url === "/engram/v1/
|
|
352
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
278
353
|
if (req.url === "/engram/v1/recall") {
|
|
279
354
|
return res
|
|
280
355
|
.writeHead(200, { "Content-Type": "application/json" })
|
|
@@ -325,7 +400,7 @@ test("session-start: falls back to minimal mode when full recall fails", async (
|
|
|
325
400
|
const home = mkHome();
|
|
326
401
|
let recallHits = 0;
|
|
327
402
|
const { server, port, calls } = await startServer((req, res) => {
|
|
328
|
-
if (req.url === "/engram/v1/
|
|
403
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
329
404
|
if (req.url === "/engram/v1/recall") {
|
|
330
405
|
recallHits += 1;
|
|
331
406
|
if (recallHits === 1) return res.writeHead(500).end("boom");
|
|
@@ -349,7 +424,7 @@ test("session-start: falls back to minimal mode when full recall fails", async (
|
|
|
349
424
|
test("session-start: no token → claude-code install hint, no recall call", async () => {
|
|
350
425
|
const home = mkHome();
|
|
351
426
|
const { server, port, calls } = await startServer((req, res) => {
|
|
352
|
-
if (req.url === "/engram/v1/
|
|
427
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
353
428
|
res.writeHead(200).end("{}");
|
|
354
429
|
});
|
|
355
430
|
try {
|
|
@@ -617,7 +692,7 @@ test("token resolution: claude-code connector wins over openclaw in tokens.json"
|
|
|
617
692
|
}),
|
|
618
693
|
);
|
|
619
694
|
const { server, port, calls } = await startServer((req, res) => {
|
|
620
|
-
if (req.url === "/engram/v1/
|
|
695
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
621
696
|
if (req.url === "/engram/v1/recall") return res.writeHead(200).end("{}");
|
|
622
697
|
res.writeHead(200).end("{}");
|
|
623
698
|
});
|
|
@@ -644,7 +719,7 @@ test("token resolution: legacy ~/.engram/tokens.json is the read fallback", asyn
|
|
|
644
719
|
JSON.stringify({ tokens: [{ connector: "openclaw", token: "legacy-tok" }] }),
|
|
645
720
|
);
|
|
646
721
|
const { server, port, calls } = await startServer((req, res) => {
|
|
647
|
-
if (req.url === "/engram/v1/
|
|
722
|
+
if (req.url === "/engram/v1/live") return res.writeHead(200).end("ok");
|
|
648
723
|
if (req.url === "/engram/v1/recall") return res.writeHead(200).end("{}");
|
|
649
724
|
res.writeHead(200).end("{}");
|
|
650
725
|
});
|
|
@@ -671,7 +746,7 @@ test("token resolution: legacy ~/.engram/tokens.json is the read fallback", asyn
|
|
|
671
746
|
function authEnvServer() {
|
|
672
747
|
const seen = { health: "unset", recall: "unset" };
|
|
673
748
|
return startServer((req, res) => {
|
|
674
|
-
if (req.url === "/engram/v1/
|
|
749
|
+
if (req.url === "/engram/v1/live") {
|
|
675
750
|
seen.health = req.headers.authorization || null;
|
|
676
751
|
return res.writeHead(200).end("ok");
|
|
677
752
|
}
|
|
@@ -366,34 +366,48 @@ function run(config) {
|
|
|
366
366
|
// ignore) and green-light a probe whose recall then 401s.
|
|
367
367
|
//
|
|
368
368
|
// When the daemon has an auth token configured, every route — including
|
|
369
|
-
// /engram/v1/
|
|
369
|
+
// /engram/v1/live — returns 401 to unauthenticated requests, so an
|
|
370
370
|
// unauthenticated probe makes the hook wrongly report "daemon not running"
|
|
371
371
|
// and skip recall/observe. Unauthenticated daemons ignore the header.
|
|
372
|
+
//
|
|
373
|
+
// One aggregate budget: readiness-gated /engram/v1/live first, and only a
|
|
374
|
+
// 404 (daemon predating the live route) retries the legacy diagnostic
|
|
375
|
+
// /engram/v1/health with the remaining time. 401/503 are final answers.
|
|
376
|
+
// Diagnostic health can outlive this liveness probe's deadline, which is
|
|
377
|
+
// why the gate probes liveness at all (#3257).
|
|
372
378
|
function httpHealthy(timeoutMs, token) {
|
|
373
379
|
if (DAEMON_URL === null) return Promise.resolve(false);
|
|
374
380
|
const transport = DAEMON_URL.protocol === "https:" ? https : http;
|
|
375
|
-
|
|
381
|
+
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
|
382
|
+
const deadline = Date.now() + timeoutMs;
|
|
383
|
+
const probe = (path, budget) => new Promise((resolve) => {
|
|
376
384
|
const req = transport.request(
|
|
377
385
|
{
|
|
378
386
|
protocol: DAEMON_URL.protocol,
|
|
379
387
|
hostname: DAEMON_URL.hostname,
|
|
380
388
|
port: DAEMON_URL.port || (DAEMON_URL.protocol === "https:" ? 443 : 80),
|
|
381
|
-
path: DAEMON_BASE_PATH +
|
|
389
|
+
path: DAEMON_BASE_PATH + path,
|
|
382
390
|
method: "GET",
|
|
383
|
-
headers
|
|
391
|
+
headers,
|
|
384
392
|
},
|
|
385
393
|
(res) => {
|
|
386
394
|
res.resume();
|
|
387
|
-
resolve(res.statusCode
|
|
395
|
+
resolve(res.statusCode || 0);
|
|
388
396
|
},
|
|
389
397
|
);
|
|
390
|
-
req.on("error", () => resolve(
|
|
391
|
-
req.setTimeout(
|
|
398
|
+
req.on("error", () => resolve(0));
|
|
399
|
+
req.setTimeout(budget, () => {
|
|
392
400
|
req.destroy();
|
|
393
|
-
resolve(
|
|
401
|
+
resolve(0);
|
|
394
402
|
});
|
|
395
403
|
req.end();
|
|
396
404
|
});
|
|
405
|
+
return probe("/engram/v1/live", timeoutMs).then((status) => {
|
|
406
|
+
if (status !== 404) return status >= 200 && status < 300;
|
|
407
|
+
const remaining = deadline - Date.now();
|
|
408
|
+
if (remaining <= 0) return false;
|
|
409
|
+
return probe("/engram/v1/health", remaining).then((legacy) => legacy >= 200 && legacy < 300);
|
|
410
|
+
});
|
|
397
411
|
}
|
|
398
412
|
|
|
399
413
|
// ── git coding-context (mirrors @remnic/core git-context.ts) ───────────────
|