@remnic/core 9.3.648 → 9.3.649

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.
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-ZRWB5D4H.js";
5
5
  import {
6
6
  TranscriptManager
7
- } from "./chunk-XUGQQPGO.js";
7
+ } from "./chunk-AGRPGAKR.js";
8
8
  import {
9
9
  resolveCommitmentLedgerDir,
10
10
  validateCommitmentLedgerEntry
@@ -268,4 +268,4 @@ export {
268
268
  recordResumeBundle,
269
269
  getResumeBundleStatus
270
270
  };
271
- //# sourceMappingURL=chunk-6BNFVP7Y.js.map
271
+ //# sourceMappingURL=chunk-RZOBQ23O.js.map
@@ -231,7 +231,7 @@ import {
231
231
  } from "./chunk-ZRWB5D4H.js";
232
232
  import {
233
233
  TranscriptManager
234
- } from "./chunk-XUGQQPGO.js";
234
+ } from "./chunk-AGRPGAKR.js";
235
235
  import {
236
236
  NamespaceStorageRouter
237
237
  } from "./chunk-D6WVJIS3.js";
@@ -14302,4 +14302,4 @@ export {
14302
14302
  resolvePersistedMemoryRelativePath,
14303
14303
  Orchestrator
14304
14304
  };
14305
- //# sourceMappingURL=chunk-TWVRDGTX.js.map
14305
+ //# sourceMappingURL=chunk-XUGVP7ZU.js.map
package/dist/cli.js CHANGED
@@ -89,7 +89,7 @@ import {
89
89
  runWorkProductStatusCliCommand,
90
90
  runWorkProjectCliCommand,
91
91
  runWorkTaskCliCommand
92
- } from "./chunk-AEIZEAP7.js";
92
+ } from "./chunk-FQYFMIKG.js";
93
93
  import "./chunk-MC4FJXPA.js";
94
94
  import "./chunk-LQHDIS7L.js";
95
95
  import "./chunk-7F7Z6MOS.js";
@@ -119,7 +119,7 @@ import "./chunk-OUELRE5E.js";
119
119
  import "./chunk-GQL52GQ5.js";
120
120
  import "./chunk-EVWIEEKZ.js";
121
121
  import "./chunk-OTC2KOZ2.js";
122
- import "./chunk-6BNFVP7Y.js";
122
+ import "./chunk-RZOBQ23O.js";
123
123
  import "./chunk-WLGE6KEO.js";
124
124
  import "./chunk-TGQ2NTWH.js";
125
125
  import "./chunk-ZT6R3WR3.js";
@@ -140,7 +140,7 @@ import "./chunk-UMKPSD35.js";
140
140
  import "./chunk-W4RVMTHR.js";
141
141
  import "./chunk-4BISW7RX.js";
142
142
  import "./chunk-ZRWB5D4H.js";
143
- import "./chunk-XUGQQPGO.js";
143
+ import "./chunk-AGRPGAKR.js";
144
144
  import "./chunk-S4DDLTPX.js";
145
145
  import "./chunk-SFQ6QNL7.js";
146
146
  import "./chunk-D6WVJIS3.js";
package/dist/index.js CHANGED
@@ -94,7 +94,7 @@ import {
94
94
  registerTrainingExportAdapter,
95
95
  runBulkImportCliCommand,
96
96
  runWearablesCliCommand
97
- } from "./chunk-AEIZEAP7.js";
97
+ } from "./chunk-FQYFMIKG.js";
98
98
  import "./chunk-MC4FJXPA.js";
99
99
  import "./chunk-LQHDIS7L.js";
100
100
  import "./chunk-7F7Z6MOS.js";
@@ -137,7 +137,7 @@ import {
137
137
  import "./chunk-GQL52GQ5.js";
138
138
  import "./chunk-EVWIEEKZ.js";
139
139
  import "./chunk-OTC2KOZ2.js";
140
- import "./chunk-6BNFVP7Y.js";
140
+ import "./chunk-RZOBQ23O.js";
141
141
  import {
142
142
  parseXrayBudgetFlag,
143
143
  parseXrayCliOptions
@@ -191,7 +191,7 @@ import {
191
191
  saveTaxonomy,
192
192
  validateSlug,
193
193
  validateTaxonomy
194
- } from "./chunk-TWVRDGTX.js";
194
+ } from "./chunk-XUGVP7ZU.js";
195
195
  import {
196
196
  WEARABLE_SOURCE_PREFIX,
197
197
  buildExtractionTurns,
@@ -389,7 +389,7 @@ import "./chunk-UMKPSD35.js";
389
389
  import "./chunk-W4RVMTHR.js";
390
390
  import "./chunk-4BISW7RX.js";
391
391
  import "./chunk-ZRWB5D4H.js";
392
- import "./chunk-XUGQQPGO.js";
392
+ import "./chunk-AGRPGAKR.js";
393
393
  import {
394
394
  SESSION_CHANNEL_TYPE,
395
395
  legacyParserReadbackDir,
@@ -28,7 +28,7 @@ import {
28
28
  sanitizeSessionKeyForFilename,
29
29
  shouldFilterLifecycleRecallCandidate,
30
30
  summarizeGraphShadowComparison
31
- } from "./chunk-TWVRDGTX.js";
31
+ } from "./chunk-XUGVP7ZU.js";
32
32
  import "./chunk-DDRNDPX4.js";
33
33
  import "./chunk-7HYPN2GC.js";
34
34
  import "./chunk-666A3MOW.js";
@@ -107,7 +107,7 @@ import "./chunk-UMKPSD35.js";
107
107
  import "./chunk-W4RVMTHR.js";
108
108
  import "./chunk-4BISW7RX.js";
109
109
  import "./chunk-ZRWB5D4H.js";
110
- import "./chunk-XUGQQPGO.js";
110
+ import "./chunk-AGRPGAKR.js";
111
111
  import "./chunk-S4DDLTPX.js";
112
112
  import "./chunk-SFQ6QNL7.js";
113
113
  import "./chunk-D6WVJIS3.js";
@@ -4,9 +4,9 @@ import {
4
4
  recordResumeBundle,
5
5
  resolveResumeBundleDir,
6
6
  validateResumeBundle
7
- } from "./chunk-6BNFVP7Y.js";
7
+ } from "./chunk-RZOBQ23O.js";
8
8
  import "./chunk-ZRWB5D4H.js";
9
- import "./chunk-XUGQQPGO.js";
9
+ import "./chunk-AGRPGAKR.js";
10
10
  import "./chunk-S4DDLTPX.js";
11
11
  import "./chunk-KFY3SGN7.js";
12
12
  import "./chunk-LMDRGRJ2.js";
@@ -3,6 +3,23 @@ import { SessionIntegrityReport } from './session-integrity.js';
3
3
  import './types-ByK7T3L6.js';
4
4
  import './index-DJ9QWMw-.js';
5
5
 
6
+ /**
7
+ * Compute the half-open UTC instant range `[start, end)` that covers an entire
8
+ * calendar day, suitable for {@link TranscriptManager.readRange}.
9
+ *
10
+ * `readRange` filters with an EXCLUSIVE upper bound (`entryTime < end`, CLAUDE.md
11
+ * rule #35 / AGENTS.md rule 23). The end is therefore the NEXT day's
12
+ * `00:00:00Z`, not `${date}T23:59:59Z`: a literal `23:59:59Z` end (== `.000Z`)
13
+ * would drop any entry stamped in the final second of the day
14
+ * (`23:59:59.000Z`–`23:59:59.999Z`). Using the next day's midnight keeps the
15
+ * `[start, end)` semantics intact while including the whole day.
16
+ *
17
+ * @param date - A `YYYY-MM-DD` calendar day (UTC).
18
+ */
19
+ declare function utcDayRange(date: string): {
20
+ start: string;
21
+ end: string;
22
+ };
6
23
  /**
7
24
  * Manages conversation transcript storage, checkpointing, and recall formatting.
8
25
  *
@@ -196,4 +213,4 @@ declare class TranscriptManager {
196
213
  }>;
197
214
  }
198
215
 
199
- export { TranscriptManager };
216
+ export { TranscriptManager, utcDayRange };
@@ -1,12 +1,14 @@
1
1
  import {
2
- TranscriptManager
3
- } from "./chunk-XUGQQPGO.js";
2
+ TranscriptManager,
3
+ utcDayRange
4
+ } from "./chunk-AGRPGAKR.js";
4
5
  import "./chunk-S4DDLTPX.js";
5
6
  import "./chunk-KFY3SGN7.js";
6
7
  import "./chunk-FF4KLI5W.js";
7
8
  import "./chunk-2ODBA7MQ.js";
8
9
  import "./chunk-PZ5AY32C.js";
9
10
  export {
10
- TranscriptManager
11
+ TranscriptManager,
12
+ utcDayRange
11
13
  };
12
14
  //# sourceMappingURL=transcript.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.3.648",
3
+ "version": "9.3.649",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/cli.ts CHANGED
@@ -5,6 +5,7 @@ import { createHash } from "node:crypto";
5
5
  import type { Readable, Writable } from "node:stream";
6
6
  import type { Orchestrator } from "./orchestrator.js";
7
7
  import { ThreadingManager } from "./threading.js";
8
+ import { utcDayRange } from "./transcript.js";
8
9
  import { runWearablesCliCommand } from "./wearables/cli.js";
9
10
  import type {
10
11
  BehaviorSignalEvent,
@@ -8630,12 +8631,11 @@ export function registerCli(
8630
8631
  }
8631
8632
 
8632
8633
  if (date) {
8633
- // Read specific date
8634
- const entries = await orchestrator.transcript.readRange(
8635
- `${date}T00:00:00Z`,
8636
- `${date}T23:59:59Z`,
8637
- channel,
8638
- );
8634
+ // Read specific date. Use a half-open [start, next-day-00:00:00Z)
8635
+ // window so `readRange`'s exclusive upper bound still covers the
8636
+ // final second of the day (rule #35).
8637
+ const { start, end } = utcDayRange(date);
8638
+ const entries = await orchestrator.transcript.readRange(start, end, channel);
8639
8639
  console.log(formatTranscript(entries));
8640
8640
  } else if (recent) {
8641
8641
  // Parse duration (e.g., "12h", "30m")
@@ -8643,13 +8643,11 @@ export function registerCli(
8643
8643
  const entries = await orchestrator.transcript.readRecent(hours, channel);
8644
8644
  console.log(formatTranscript(entries));
8645
8645
  } else {
8646
- // Default: show today's transcript
8646
+ // Default: show today's transcript. Same half-open day window as
8647
+ // the --date branch so the final second of the day is included.
8647
8648
  const today = new Date().toISOString().slice(0, 10);
8648
- const entries = await orchestrator.transcript.readRange(
8649
- `${today}T00:00:00Z`,
8650
- `${today}T23:59:59Z`,
8651
- channel,
8652
- );
8649
+ const { start, end } = utcDayRange(today);
8650
+ const entries = await orchestrator.transcript.readRange(start, end, channel);
8653
8651
  console.log(formatTranscript(entries));
8654
8652
  }
8655
8653
  });
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Tests for the CLI transcript day-view window helper `utcDayRange`.
3
+ *
4
+ * The `transcript --date <day>` and default "today" CLI views read a single
5
+ * calendar day via {@link TranscriptManager.readRange}, which uses a half-open
6
+ * `[start, end)` interval (`entryTime < end`, CLAUDE.md rule #35). The day end
7
+ * must therefore be the NEXT day's `00:00:00Z`, not `${date}T23:59:59Z`:
8
+ * a literal `23:59:59Z` end (== `.000Z`) silently drops any entry stamped in
9
+ * the final second of the day (`23:59:59.000Z`–`23:59:59.999Z`).
10
+ *
11
+ * (Pre-existing boundary gap flagged as a P2 on PR #1507, fixed out of band.)
12
+ */
13
+
14
+ import assert from "node:assert/strict";
15
+ import { mkdtemp, realpath, rm } from "node:fs/promises";
16
+ import os from "node:os";
17
+ import path from "node:path";
18
+ import test from "node:test";
19
+
20
+ import { TranscriptManager, utcDayRange } from "./transcript.js";
21
+ import type { PluginConfig, TranscriptEntry } from "./types.js";
22
+
23
+ function makeConfig(memoryDir: string): PluginConfig {
24
+ // TranscriptManager only reads memoryDir + transcriptSkipChannelTypes.
25
+ return {
26
+ memoryDir,
27
+ transcriptSkipChannelTypes: [],
28
+ } as unknown as PluginConfig;
29
+ }
30
+
31
+ // On macOS `os.tmpdir()` is a `/var/folders/...` symlink to `/private/var/...`.
32
+ // Canonicalize the test root upfront (issue #691 symlink convention).
33
+ async function makeMemoryDir(): Promise<string> {
34
+ return realpath(await mkdtemp(path.join(os.tmpdir(), "remnic-tx-dayrange-")));
35
+ }
36
+
37
+ function entryAt(timestamp: string, turnId: string): TranscriptEntry {
38
+ return {
39
+ sessionKey: "agent:generalist:main",
40
+ turnId,
41
+ role: "user",
42
+ content: `content-${turnId}`,
43
+ timestamp,
44
+ } as TranscriptEntry;
45
+ }
46
+
47
+ // ── utcDayRange: half-open [start, next-day-00:00:00Z) ───────────────────────
48
+
49
+ test("utcDayRange end is the next day's 00:00:00Z (half-open day window)", () => {
50
+ assert.deepEqual(utcDayRange("2025-03-15"), {
51
+ start: "2025-03-15T00:00:00Z",
52
+ end: "2025-03-16T00:00:00Z",
53
+ });
54
+ });
55
+
56
+ test("utcDayRange rolls over month boundaries", () => {
57
+ assert.deepEqual(utcDayRange("2025-01-31"), {
58
+ start: "2025-01-31T00:00:00Z",
59
+ end: "2025-02-01T00:00:00Z",
60
+ });
61
+ });
62
+
63
+ test("utcDayRange rolls over leap-day and year boundaries", () => {
64
+ assert.deepEqual(utcDayRange("2024-02-28"), {
65
+ start: "2024-02-28T00:00:00Z",
66
+ end: "2024-02-29T00:00:00Z",
67
+ });
68
+ assert.deepEqual(utcDayRange("2025-12-31"), {
69
+ start: "2025-12-31T00:00:00Z",
70
+ end: "2026-01-01T00:00:00Z",
71
+ });
72
+ });
73
+
74
+ // ── Day-view range includes the final second of the day (the bug fix) ────────
75
+
76
+ test("day-view range returns an entry stamped at 23:59:59.500Z of the queried day", async () => {
77
+ const memoryDir = await makeMemoryDir();
78
+ try {
79
+ const tm = new TranscriptManager(makeConfig(memoryDir));
80
+ const date = "2025-03-15";
81
+ const channel = "agent:generalist:main";
82
+
83
+ // Entry in the final second of the day — dropped by the old
84
+ // `${date}T23:59:59Z` (== `.000Z`) exclusive upper bound.
85
+ await tm.append(entryAt(`${date}T23:59:59.500Z`, "final-second"));
86
+ // Midday control entry, comfortably inside the window.
87
+ await tm.append(entryAt(`${date}T12:00:00.000Z`, "midday"));
88
+ // Start-of-next-day entry must stay OUT — the upper bound is exclusive
89
+ // (`[start, end)`, rule #35), so `nextDate T00:00:00.000Z` is not the
90
+ // queried day.
91
+ await tm.append(entryAt("2025-03-16T00:00:00.000Z", "next-day"));
92
+
93
+ const { start, end } = utcDayRange(date);
94
+ const entries = await tm.readRange(start, end, channel);
95
+ const ids = entries.map((e) => e.turnId).sort();
96
+
97
+ assert.deepEqual(ids, ["final-second", "midday"]);
98
+ } finally {
99
+ await rm(memoryDir, { recursive: true, force: true });
100
+ }
101
+ });
package/src/transcript.ts CHANGED
@@ -33,6 +33,32 @@ function makeRawLineDeduper(): (rawLine: string) => boolean {
33
33
  };
34
34
  }
35
35
 
36
+ /**
37
+ * Compute the half-open UTC instant range `[start, end)` that covers an entire
38
+ * calendar day, suitable for {@link TranscriptManager.readRange}.
39
+ *
40
+ * `readRange` filters with an EXCLUSIVE upper bound (`entryTime < end`, CLAUDE.md
41
+ * rule #35 / AGENTS.md rule 23). The end is therefore the NEXT day's
42
+ * `00:00:00Z`, not `${date}T23:59:59Z`: a literal `23:59:59Z` end (== `.000Z`)
43
+ * would drop any entry stamped in the final second of the day
44
+ * (`23:59:59.000Z`–`23:59:59.999Z`). Using the next day's midnight keeps the
45
+ * `[start, end)` semantics intact while including the whole day.
46
+ *
47
+ * @param date - A `YYYY-MM-DD` calendar day (UTC).
48
+ */
49
+ export function utcDayRange(date: string): { start: string; end: string } {
50
+ const start = `${date}T00:00:00Z`;
51
+ const startMs = Date.parse(start);
52
+ if (Number.isNaN(startMs)) {
53
+ // Malformed date: preserve the pre-existing "empty range" behavior (an
54
+ // unparseable bound makes `readRange` match nothing) rather than throwing.
55
+ return { start, end: start };
56
+ }
57
+ const next = new Date(startMs);
58
+ next.setUTCDate(next.getUTCDate() + 1);
59
+ return { start, end: `${next.toISOString().slice(0, 10)}T00:00:00Z` };
60
+ }
61
+
36
62
  /**
37
63
  * Manages conversation transcript storage, checkpointing, and recall formatting.
38
64
  *