@remnic/core 9.69.90 → 9.69.91
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "9.69.
|
|
3
|
+
"version": "9.69.91",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -3327,7 +3327,7 @@
|
|
|
3327
3327
|
"core"
|
|
3328
3328
|
],
|
|
3329
3329
|
"peerDependencies": {
|
|
3330
|
-
"@remnic/coding-graph": "^9.69.
|
|
3330
|
+
"@remnic/coding-graph": "^9.69.91"
|
|
3331
3331
|
},
|
|
3332
3332
|
"peerDependenciesMeta": {
|
|
3333
3333
|
"@remnic/coding-graph": {
|
|
@@ -989,11 +989,19 @@ test("#2128: post-flush retained cleanup stops on abort or deadline", async (t)
|
|
|
989
989
|
/replay extraction deadline exceeded \(retained_turn_cleanup\)/,
|
|
990
990
|
);
|
|
991
991
|
});
|
|
992
|
-
test("#3140: a duration deadline reaches scope resolution and aborts it when it elapses", async () => {
|
|
992
|
+
test("#3140: a duration deadline reaches scope resolution and aborts it when it elapses", async (t) => {
|
|
993
993
|
// Regression for issue #3140: `deadlineMs` is a BUDGET from now. The old
|
|
994
994
|
// absolute-epoch reading compared a relative value (e.g. 30000) against
|
|
995
995
|
// Date.now(), so a live-session force flush was rejected INSTANTLY with
|
|
996
996
|
// `replay extraction deadline exceeded (scope_resolution)` before any work.
|
|
997
|
+
// The flush deadline is a real, unref'd timer in production
|
|
998
|
+
// (session-context.ts), so racing a 30ms wall-clock budget lets the event
|
|
999
|
+
// loop drain first under CI load: the flush promise then never settles and
|
|
1000
|
+
// the runner cancels the rest of the file. Mocked timers make the ordering
|
|
1001
|
+
// deterministic — the budget elapses only when ticked. The clock starts at
|
|
1002
|
+
// an epoch-scale `now` so a duration budget (30) is genuinely distinguished
|
|
1003
|
+
// from the old absolute-epoch reading the test guards against.
|
|
1004
|
+
t.mock.timers.enable({ apis: ["setTimeout", "Date"], now: 1_700_000_000_000 });
|
|
997
1005
|
const probe = makeParityProbe({ namespacesEnabled: false } as Partial<PluginConfig>);
|
|
998
1006
|
const service = new EngramAccessService(probe.orch);
|
|
999
1007
|
let scopeResolutionStarted = false;
|
|
@@ -1005,19 +1013,25 @@ test("#3140: a duration deadline reaches scope resolution and aborts it when it
|
|
|
1005
1013
|
return new Promise<never>(() => {});
|
|
1006
1014
|
};
|
|
1007
1015
|
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
);
|
|
1016
|
+
const flush = service.extractionForceFlush({
|
|
1017
|
+
sessionKey: "duration-deadline-scope-resolution",
|
|
1018
|
+
deadlineMs: 30,
|
|
1019
|
+
});
|
|
1020
|
+
// Yield once so the stub is entered before the budget is spent; the mocked
|
|
1021
|
+
// clock does not move on its own.
|
|
1022
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
1016
1023
|
assert.equal(scopeResolutionStarted, true, "the budget lets scope resolution start");
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
)
|
|
1024
|
+
// Under mocked timers the budget cannot have elapsed yet, so the flush
|
|
1025
|
+
// must still be pending — proves the deadline does not fire before its
|
|
1026
|
+
// budget (#3140 regression: an absolute-epoch reading would reject
|
|
1027
|
+
// instantly with `scope_resolution` before the stub ran).
|
|
1028
|
+
const stillPending = Promise.race([
|
|
1029
|
+
flush.then(() => "rejected", () => "rejected"),
|
|
1030
|
+
new Promise<string>((resolve) => setImmediate(() => resolve("pending"))),
|
|
1031
|
+
]);
|
|
1032
|
+
assert.equal(await stillPending, "pending");
|
|
1033
|
+
t.mock.timers.tick(30);
|
|
1034
|
+
await assert.rejects(flush, /scope_resolution/);
|
|
1021
1035
|
assert.equal(probe.extractionForceFlushCalls.length, 0);
|
|
1022
1036
|
});
|
|
1023
1037
|
|
|
@@ -1076,7 +1090,14 @@ test("#2206: abort during scope resolution cancels pre-resolution observe prepar
|
|
|
1076
1090
|
assert.equal(probe.extractionCalls.length, 0);
|
|
1077
1091
|
});
|
|
1078
1092
|
|
|
1079
|
-
test("#2206: scope-resolution deadline cancels preparations by raw projectTag and cwd hints", async () => {
|
|
1093
|
+
test("#2206: scope-resolution deadline cancels preparations by raw projectTag and cwd hints", async (t) => {
|
|
1094
|
+
// The deadline budget is a real, unref'd timer in production
|
|
1095
|
+
// (session-context.ts), so a 1ms wall-clock budget can elapse BEFORE the
|
|
1096
|
+
// flush enters the blocked scope stub on a loaded runner (assertion then
|
|
1097
|
+
// sees flushScopeStarted === false), and a drained event loop can leave
|
|
1098
|
+
// the flush pending forever. Mocked timers pin the order: the stub is
|
|
1099
|
+
// entered first, the budget elapses only when ticked, and the observe tail
|
|
1100
|
+
// runs after timers are restored.
|
|
1080
1101
|
const rawHintCases = [
|
|
1081
1102
|
{ projectTag: "Acme/Webshop" },
|
|
1082
1103
|
{ cwd: "/workspace/acme/webshop" },
|
|
@@ -1119,17 +1140,29 @@ test("#2206: scope-resolution deadline cancels preparations by raw projectTag an
|
|
|
1119
1140
|
...rawHintCase,
|
|
1120
1141
|
}));
|
|
1121
1142
|
await observeScopeStarted;
|
|
1122
|
-
|
|
1123
|
-
|
|
1143
|
+
// Mock the clock only for the flush: the observe is already parked on
|
|
1144
|
+
// the gate, and its tail below needs the real clock back. A try/finally
|
|
1145
|
+
// makes sure a failing assertion cannot leak the mocked window or the
|
|
1146
|
+
// parked observe, which would otherwise reproduce the same
|
|
1147
|
+
// event-loop-drain cascade this PR fixes.
|
|
1148
|
+
t.mock.timers.enable({ apis: ["setTimeout", "Date"] });
|
|
1149
|
+
try {
|
|
1150
|
+
const flush = service.extractionForceFlush({
|
|
1124
1151
|
sessionKey: "pi-geek:raw-hint-deadline",
|
|
1125
1152
|
authenticatedPrincipal: "pi-geek",
|
|
1126
1153
|
deadlineMs: 1,
|
|
1127
1154
|
...rawHintCase,
|
|
1128
|
-
})
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1155
|
+
});
|
|
1156
|
+
// Yield once so the stub is entered before the budget is spent; the
|
|
1157
|
+
// mocked clock does not move on its own.
|
|
1158
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
1159
|
+
assert.equal(flushScopeStarted, true);
|
|
1160
|
+
t.mock.timers.tick(1);
|
|
1161
|
+
await assert.rejects(flush, /scope_resolution/);
|
|
1162
|
+
} finally {
|
|
1163
|
+
t.mock.timers.reset();
|
|
1164
|
+
releaseObserveScope();
|
|
1165
|
+
}
|
|
1133
1166
|
const response = await observe;
|
|
1134
1167
|
assert.equal(response.extractionQueued, false);
|
|
1135
1168
|
assert.equal(probe.extractionCalls.length, 0);
|
|
@@ -8,9 +8,19 @@ import { parseConfig } from "./config.js";
|
|
|
8
8
|
import { Orchestrator } from "./orchestrator.js";
|
|
9
9
|
import { NoopSearchBackend } from "./search/noop-backend.js";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Watchdog for startup lifecycle awaits. This is a HANG guard, not a speed
|
|
13
|
+
* budget: a real Orchestrator.initialize() does real fs/sqlite discovery and
|
|
14
|
+
* can take well over a second on a loaded CI runner (a merge to main failed
|
|
15
|
+
* its shard when initialize settled ~40ms after the old 1250ms deadline had
|
|
16
|
+
* already resolved), and the discovery paths can block the loop so even
|
|
17
|
+
* in-file timers stall meanwhile. The deadline only needs to fire when the
|
|
18
|
+
* awaited lifecycle genuinely never settles, so keep it far above any
|
|
19
|
+
* legitimate startup cost.
|
|
20
|
+
*/
|
|
11
21
|
async function withTestDeadline<T>(
|
|
12
22
|
promise: Promise<T>,
|
|
13
|
-
timeoutMs =
|
|
23
|
+
timeoutMs = 10_000,
|
|
14
24
|
): Promise<T | "deadline"> {
|
|
15
25
|
let timer: NodeJS.Timeout | undefined;
|
|
16
26
|
const deadline = new Promise<"deadline">((resolve) => {
|