@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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ExtractionDrain,
3
3
  TurnIngestionCoordinator
4
- } from "./chunk-VYOAYFDV.js";
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-URS56HGW.js.map
443
+ //# sourceMappingURL=chunk-TD5VTVPT.js.map
package/dist/index.js CHANGED
@@ -181,7 +181,7 @@ import {
181
181
  writeManifest,
182
182
  writeMeetingEpisodeMemory,
183
183
  writeTimelineState
184
- } from "./chunk-VYOAYFDV.js";
184
+ } from "./chunk-HR6NTVYZ.js";
185
185
  import {
186
186
  WEARABLE_SOURCE_PREFIX,
187
187
  buildExtractionTurns,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BufferDrainRunner
3
- } from "../chunk-URS56HGW.js";
4
- import "../chunk-VYOAYFDV.js";
3
+ } from "../chunk-TD5VTVPT.js";
4
+ import "../chunk-HR6NTVYZ.js";
5
5
  import "../chunk-B36ACJG7.js";
6
6
  import "../chunk-PLGBPFEO.js";
7
7
  import "../chunk-5PHOU5QI.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  BufferDrainRunner,
3
3
  ClaimBufferStorage
4
- } from "../chunk-URS56HGW.js";
5
- import "../chunk-VYOAYFDV.js";
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. This is
86
- * the production entry point — the orchestrator's `queueBufferedExtraction`
87
- * builds each task closure and hands it here with its buffer key.
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":"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"}
1
+ {"version":3,"file":"extraction-queue-coordinator.d.ts","sourceRoot":"","sources":["../../src/orchestration/extraction-queue-coordinator.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import {
4
4
  graphPathRelativeToStorage,
5
5
  mergeGraphExpandedResults,
6
6
  qmdStartupCollectionChecksWithTimeout
7
- } from "./chunk-VYOAYFDV.js";
7
+ } from "./chunk-HR6NTVYZ.js";
8
8
  import "./chunk-B36ACJG7.js";
9
9
  import "./chunk-PLGBPFEO.js";
10
10
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "10.0.75",
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.75"
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. This is
116
- * the production entry point — the orchestrator's `queueBufferedExtraction`
117
- * builds each task closure and hands it here with its buffer key.
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 coalescing: a queued-not-started task is superseded by a newer
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("a newer enqueue for a queued-not-started key replaces the queued task", async () => {
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, 1, "the stale snapshot must be replaced, not stacked");
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"], "only the newest task for the key runs");
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("coalescing keeps the superseded entry's position for other keys", async () => {
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-fresh", "k3"], "k2 keeps its queue slot; the newest payload wins");
95
+ assert.deepEqual(ran, ["k1", "k2-stale", "k2-fresh", "k3"]);
94
96
  });
95
97
 
96
- test("a front enqueue supersedes a queued same-key task and keeps preemption", async () => {
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, 1, "the superseded queued task must be dropped");
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 never coalesce", async () => {
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("a coalesced enqueue of a running key waits behind the active task", async () => {
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
+ });