@remnic/core 10.0.75 → 10.0.76
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/access-cli.js +1 -1
- package/dist/{chunk-VYOAYFDV.js → chunk-HR6NTVYZ.js} +5 -20
- package/dist/{chunk-VYOAYFDV.js.map → chunk-HR6NTVYZ.js.map} +1 -1
- package/dist/{chunk-URS56HGW.js → chunk-TD5VTVPT.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/orchestration/backlog-scheduler.js +2 -2
- package/dist/orchestration/buffer-drain-runner.js +2 -2
- package/dist/orchestration/extraction-queue-coordinator.d.ts +4 -9
- package/dist/orchestration/extraction-queue-coordinator.d.ts.map +1 -1
- package/dist/orchestrator.js +1 -1
- package/package.json +2 -2
- package/src/orchestration/extraction-queue-coordinator.ts +4 -19
- package/src/orchestrator-extraction-queue.test.ts +70 -13
- /package/dist/{chunk-URS56HGW.js.map → chunk-TD5VTVPT.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ExtractionDrain,
|
|
3
3
|
TurnIngestionCoordinator
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HR6NTVYZ.js";
|
|
5
5
|
import {
|
|
6
6
|
headerConnector,
|
|
7
7
|
renderExtractionConversation,
|
|
@@ -440,4 +440,4 @@ export {
|
|
|
440
440
|
ClaimBufferStorage,
|
|
441
441
|
BufferDrainRunner
|
|
442
442
|
};
|
|
443
|
-
//# sourceMappingURL=chunk-
|
|
443
|
+
//# sourceMappingURL=chunk-TD5VTVPT.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BufferDrainRunner,
|
|
3
3
|
ClaimBufferStorage
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-TD5VTVPT.js";
|
|
5
|
+
import "../chunk-HR6NTVYZ.js";
|
|
6
6
|
import "../chunk-B36ACJG7.js";
|
|
7
7
|
import "../chunk-PLGBPFEO.js";
|
|
8
8
|
import "../chunk-5PHOU5QI.js";
|
|
@@ -82,15 +82,10 @@ export declare class ExtractionQueueCoordinator {
|
|
|
82
82
|
/** How many tasks may run at once (test seam + diagnostics). */
|
|
83
83
|
get maxConcurrency(): number;
|
|
84
84
|
/**
|
|
85
|
-
* Enqueue a task and start the processor when capacity is free.
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* Per-key coalescing: a queued-not-started task for key K is superseded by
|
|
90
|
-
* a newer enqueue for K instead of stacking another full turn snapshot
|
|
91
|
-
* behind it (extraction reads live state at run time, so the newest
|
|
92
|
-
* closure is the authoritative one). Keyless tasks are independent work
|
|
93
|
-
* and never coalesce.
|
|
85
|
+
* Enqueue a task and start the processor when capacity is free. Each task
|
|
86
|
+
* keeps its own captured work and settlement; keyed tasks serialize behind
|
|
87
|
+
* other tasks for that key. A front task may jump the queue but does not
|
|
88
|
+
* discard queued work.
|
|
94
89
|
*/
|
|
95
90
|
enqueue(task: ExtractionQueueTask, options?: ExtractionQueueEnqueueOptions): boolean;
|
|
96
91
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extraction-queue-coordinator.d.ts","sourceRoot":"","sources":["../../src/orchestration/extraction-queue-coordinator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAKH;mEACmE;AACnE,MAAM,WAAW,uBAAuB;IACtC,uEAAuE;IACvE,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC7B,mDAAmD;IACnD,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,2EAA2E;AAC3E,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,CAAC,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvF,MAAM,WAAW,6BAA6B;IAC5C,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAYD;;;GAGG;AACH,qBAAa,0BAA0B;IACrC,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,mDAAmD;IACnD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAQ;IACzB,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,UAAU,CAAK;IACvB,wEAAwE;IACxE,OAAO,CAAC,IAAI,CAAC,CAAa;gBAEd,WAAW,SAAI;IAI3B,mDAAmD;IACnD,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,2EAA2E;IAC3E,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,gEAAgE;IAChE,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED
|
|
1
|
+
{"version":3,"file":"extraction-queue-coordinator.d.ts","sourceRoot":"","sources":["../../src/orchestration/extraction-queue-coordinator.ts"],"names":[],"mappings":"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"}
|
package/dist/orchestrator.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.76",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -3386,7 +3386,7 @@
|
|
|
3386
3386
|
"core"
|
|
3387
3387
|
],
|
|
3388
3388
|
"peerDependencies": {
|
|
3389
|
-
"@remnic/coding-graph": "^10.0.
|
|
3389
|
+
"@remnic/coding-graph": "^10.0.76"
|
|
3390
3390
|
},
|
|
3391
3391
|
"peerDependenciesMeta": {
|
|
3392
3392
|
"@remnic/coding-graph": {
|
|
@@ -112,29 +112,14 @@ export class ExtractionQueueCoordinator {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
* Enqueue a task and start the processor when capacity is free.
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* Per-key coalescing: a queued-not-started task for key K is superseded by
|
|
120
|
-
* a newer enqueue for K instead of stacking another full turn snapshot
|
|
121
|
-
* behind it (extraction reads live state at run time, so the newest
|
|
122
|
-
* closure is the authoritative one). Keyless tasks are independent work
|
|
123
|
-
* and never coalesce.
|
|
115
|
+
* Enqueue a task and start the processor when capacity is free. Each task
|
|
116
|
+
* keeps its own captured work and settlement; keyed tasks serialize behind
|
|
117
|
+
* other tasks for that key. A front task may jump the queue but does not
|
|
118
|
+
* discard queued work.
|
|
124
119
|
*/
|
|
125
120
|
enqueue(task: ExtractionQueueTask, options?: ExtractionQueueEnqueueOptions): boolean {
|
|
126
121
|
if (!this.accepting) return false;
|
|
127
122
|
const key = options?.key;
|
|
128
|
-
if (key !== undefined) {
|
|
129
|
-
const queuedAt = this.queue.findIndex((entry) => entry.key === key);
|
|
130
|
-
if (!options?.front && queuedAt >= 0) {
|
|
131
|
-
this.queue[queuedAt] = { run: task, key };
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
|
-
if (options?.front && queuedAt >= 0) {
|
|
135
|
-
this.queue.splice(queuedAt, 1);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
123
|
const entry: QueueEntry = { run: task, key };
|
|
139
124
|
if (options?.front) {
|
|
140
125
|
this.preemptRunning(options.key);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
|
|
4
|
+
import { queueBufferedCaptureScopeGroups } from "./orchestration/extraction-task-queueing.js";
|
|
4
5
|
import { ExtractionQueueCoordinator, type ExtractionPreemptHandle, type ExtractionQueueTask } from "./orchestration/extraction-queue-coordinator.js";
|
|
5
6
|
import { initLogger, type LoggerBackend } from "./logger.js";
|
|
6
7
|
import { abortError } from "./abort-error.js";
|
|
7
8
|
|
|
9
|
+
import type { BufferTurn } from "./types.js";
|
|
10
|
+
|
|
8
11
|
interface LogEntry {
|
|
9
12
|
level: "info" | "warn" | "error" | "debug";
|
|
10
13
|
message: string;
|
|
@@ -35,10 +38,9 @@ function createCoordinator(): ExtractionQueueCoordinator {
|
|
|
35
38
|
return new ExtractionQueueCoordinator();
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
// ── Per-key
|
|
39
|
-
// enqueue for the same key instead of stacking another full snapshot ────────
|
|
41
|
+
// ── Per-key FIFO: queued tasks retain their own captured scope and settlement ─
|
|
40
42
|
|
|
41
|
-
test("
|
|
43
|
+
test("same-key queued tasks both run in FIFO order", async () => {
|
|
42
44
|
const coordinator = createCoordinator();
|
|
43
45
|
const busy = Promise.withResolvers<void>();
|
|
44
46
|
const releaseBusy = Promise.withResolvers<void>();
|
|
@@ -56,15 +58,15 @@ test("a newer enqueue for a queued-not-started key replaces the queued task", as
|
|
|
56
58
|
ran.push("fresh");
|
|
57
59
|
}, { key: "s1" });
|
|
58
60
|
|
|
59
|
-
assert.equal(coordinator.length,
|
|
61
|
+
assert.equal(coordinator.length, 2, "same-key tasks remain queued independently");
|
|
60
62
|
|
|
61
63
|
releaseBusy.resolve();
|
|
62
64
|
assert.equal(await coordinator.waitForIdle(5_000), true);
|
|
63
|
-
assert.deepEqual(ran, ["fresh"], "
|
|
65
|
+
assert.deepEqual(ran, ["stale", "fresh"], "each caller's task runs in FIFO order");
|
|
64
66
|
assert.equal(coordinator.isProcessing, false);
|
|
65
67
|
});
|
|
66
68
|
|
|
67
|
-
test("
|
|
69
|
+
test("same-key tasks keep their FIFO positions relative to other keys", async () => {
|
|
68
70
|
const coordinator = createCoordinator();
|
|
69
71
|
const busy = Promise.withResolvers<void>();
|
|
70
72
|
const releaseBusy = Promise.withResolvers<void>();
|
|
@@ -90,10 +92,10 @@ test("coalescing keeps the superseded entry's position for other keys", async ()
|
|
|
90
92
|
|
|
91
93
|
releaseBusy.resolve();
|
|
92
94
|
assert.equal(await coordinator.waitForIdle(5_000), true);
|
|
93
|
-
assert.deepEqual(ran, ["k1", "k2-
|
|
95
|
+
assert.deepEqual(ran, ["k1", "k2-stale", "k2-fresh", "k3"]);
|
|
94
96
|
});
|
|
95
97
|
|
|
96
|
-
test("a front enqueue
|
|
98
|
+
test("a front enqueue preserves queued same-key work and keeps preemption", async () => {
|
|
97
99
|
const coordinator = createCoordinator();
|
|
98
100
|
const busy = Promise.withResolvers<void>();
|
|
99
101
|
const releaseBusy = Promise.withResolvers<void>();
|
|
@@ -121,15 +123,15 @@ test("a front enqueue supersedes a queued same-key task and keeps preemption", a
|
|
|
121
123
|
ran.push("flush-fresh");
|
|
122
124
|
}, { key: "s1", front: true });
|
|
123
125
|
|
|
124
|
-
assert.equal(coordinator.length,
|
|
126
|
+
assert.equal(coordinator.length, 2, "front work does not discard queued same-key work");
|
|
125
127
|
|
|
126
128
|
releaseBusy.resolve();
|
|
127
129
|
assert.equal(await coordinator.waitForIdle(5_000), true);
|
|
128
130
|
assert.equal(preempted, true, "front enqueue still preempts the running automatic work");
|
|
129
|
-
assert.deepEqual(ran, ["flush-fresh"]);
|
|
131
|
+
assert.deepEqual(ran, ["flush-fresh", "queued-stale"]);
|
|
130
132
|
});
|
|
131
133
|
|
|
132
|
-
test("keyless tasks
|
|
134
|
+
test("keyless tasks remain independent", async () => {
|
|
133
135
|
const coordinator = createCoordinator();
|
|
134
136
|
const busy = Promise.withResolvers<void>();
|
|
135
137
|
const releaseBusy = Promise.withResolvers<void>();
|
|
@@ -153,7 +155,7 @@ test("keyless tasks never coalesce", async () => {
|
|
|
153
155
|
assert.deepEqual(ran, ["first", "second"]);
|
|
154
156
|
});
|
|
155
157
|
|
|
156
|
-
test("
|
|
158
|
+
test("same-key enqueues behind an active task retain both queued tasks in FIFO order", async () => {
|
|
157
159
|
const coordinator = createCoordinator();
|
|
158
160
|
const busy = Promise.withResolvers<void>();
|
|
159
161
|
const releaseBusy = Promise.withResolvers<void>();
|
|
@@ -174,7 +176,7 @@ test("a coalesced enqueue of a running key waits behind the active task", async
|
|
|
174
176
|
|
|
175
177
|
releaseBusy.resolve();
|
|
176
178
|
assert.equal(await coordinator.waitForIdle(5_000), true);
|
|
177
|
-
assert.deepEqual(order, ["running", "queued-second"]);
|
|
179
|
+
assert.deepEqual(order, ["running", "queued-first", "queued-second"]);
|
|
178
180
|
});
|
|
179
181
|
|
|
180
182
|
// ── Issue #549 ─────────────────────────────────────────────────────────────
|
|
@@ -627,3 +629,58 @@ test("tryReserveKey refuses active and queued keys, and releases idempotently",
|
|
|
627
629
|
assert.equal(queue.tryReserveKey("s1"), true, "a settled key reserves again");
|
|
628
630
|
queue.releaseKeyReservation("s1");
|
|
629
631
|
});
|
|
632
|
+
|
|
633
|
+
test("same-key capture scope groups both run and settle their caller promises", async () => {
|
|
634
|
+
const coordinator = createCoordinator();
|
|
635
|
+
const busy = Promise.withResolvers<void>();
|
|
636
|
+
const releaseBusy = Promise.withResolvers<void>();
|
|
637
|
+
const ran: string[] = [];
|
|
638
|
+
const extractions: Promise<void>[] = [];
|
|
639
|
+
|
|
640
|
+
coordinator.enqueue(async () => {
|
|
641
|
+
busy.resolve();
|
|
642
|
+
await releaseBusy.promise;
|
|
643
|
+
});
|
|
644
|
+
await busy.promise;
|
|
645
|
+
|
|
646
|
+
const acceptedTurns: BufferTurn[] = [
|
|
647
|
+
{
|
|
648
|
+
role: "user",
|
|
649
|
+
content: "scope A",
|
|
650
|
+
timestamp: "2026-11-05T00:00:00.000Z",
|
|
651
|
+
sessionKey: "s1",
|
|
652
|
+
acceptedCapture: true,
|
|
653
|
+
writeNamespaceOverride: "namespace-a",
|
|
654
|
+
principalOverride: "principal-a",
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
role: "user",
|
|
658
|
+
content: "scope B",
|
|
659
|
+
timestamp: "2026-11-05T00:00:01.000Z",
|
|
660
|
+
sessionKey: "s1",
|
|
661
|
+
acceptedCapture: true,
|
|
662
|
+
writeNamespaceOverride: "namespace-b",
|
|
663
|
+
principalOverride: "principal-b",
|
|
664
|
+
},
|
|
665
|
+
];
|
|
666
|
+
queueBufferedCaptureScopeGroups(
|
|
667
|
+
async (_turns, _reason, options) => {
|
|
668
|
+
const accepted = coordinator.enqueue(async () => {
|
|
669
|
+
const namespace = options?.writeNamespaceOverride;
|
|
670
|
+
assert.ok(namespace);
|
|
671
|
+
ran.push(namespace);
|
|
672
|
+
options?.onTaskSettled?.();
|
|
673
|
+
}, { key: options?.bufferKey });
|
|
674
|
+
if (!accepted) throw new Error("queue unexpectedly rejected extraction");
|
|
675
|
+
},
|
|
676
|
+
acceptedTurns,
|
|
677
|
+
"s1",
|
|
678
|
+
{ onExtractionQueued: (promise) => extractions.push(promise) },
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
assert.equal(extractions.length, 2, "each captured scope has its own completion promise");
|
|
682
|
+
releaseBusy.resolve();
|
|
683
|
+
assert.equal(await coordinator.waitForIdle(5_000), true);
|
|
684
|
+
await Promise.all(extractions);
|
|
685
|
+
assert.deepEqual(ran, ["namespace-a", "namespace-b"]);
|
|
686
|
+
});
|
|
File without changes
|