@remnic/core 9.3.647 → 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.
- package/dist/access-cli.js +10 -10
- package/dist/access-http.js +10 -10
- package/dist/access-mcp.js +9 -9
- package/dist/access-service.js +8 -8
- package/dist/{chunk-L7W5YW6Y.js → chunk-5ETA6OAS.js} +2 -2
- package/dist/{chunk-DCGT4FPP.js → chunk-76QTEJ2Q.js} +2 -2
- package/dist/{chunk-XUGQQPGO.js → chunk-AGRPGAKR.js} +12 -1
- package/dist/chunk-AGRPGAKR.js.map +1 -0
- package/dist/{chunk-RAELB5NX.js → chunk-CNRZ6WJU.js} +3 -3
- package/dist/{chunk-3MAONBX3.js → chunk-FOVPSMGI.js} +2 -2
- package/dist/{chunk-3D6L7CEP.js → chunk-FQYFMIKG.js} +14 -17
- package/dist/chunk-FQYFMIKG.js.map +1 -0
- package/dist/{chunk-ZPPFKVSD.js → chunk-FUXV6HSO.js} +2 -2
- package/dist/{chunk-MUKXANAM.js → chunk-I4COC5XW.js} +49 -6
- package/dist/{chunk-MUKXANAM.js.map → chunk-I4COC5XW.js.map} +1 -1
- package/dist/{chunk-APWJRJFW.js → chunk-NMIOW7XG.js} +86 -8
- package/dist/chunk-NMIOW7XG.js.map +1 -0
- package/dist/{chunk-FAV25DUZ.js → chunk-QT4THOLT.js} +1 -1
- package/dist/{chunk-FAV25DUZ.js.map → chunk-QT4THOLT.js.map} +1 -1
- package/dist/{chunk-FAYDM5WD.js → chunk-RRRCNIPK.js} +2 -2
- package/dist/{chunk-6BNFVP7Y.js → chunk-RZOBQ23O.js} +2 -2
- package/dist/{chunk-RG3LBSGH.js → chunk-TQUWNX7C.js} +2 -2
- package/dist/{chunk-U55D5UD5.js → chunk-WPCCNSWO.js} +5 -5
- package/dist/{chunk-TA4LQ5SR.js → chunk-XUGVP7ZU.js} +5 -5
- package/dist/{chunk-DC66QVL2.js → chunk-ZT6R3WR3.js} +2 -2
- package/dist/cli.js +15 -15
- package/dist/index.js +16 -16
- package/dist/namespaces/migrate.js +8 -8
- package/dist/namespaces/search.d.ts +1 -0
- package/dist/namespaces/search.js +7 -7
- package/dist/operator-toolkit.js +9 -9
- package/dist/orchestrator.js +9 -9
- package/dist/qmd.d.ts +1 -0
- package/dist/qmd.js +2 -2
- package/dist/resume-bundles.js +2 -2
- package/dist/schemas.d.ts +22 -22
- package/dist/search/factory.js +6 -6
- package/dist/search/index.js +6 -6
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +6 -0
- package/dist/search/port.js +1 -1
- package/dist/transcript.d.ts +18 -1
- package/dist/transcript.js +5 -3
- package/dist/transfer/types.d.ts +12 -12
- package/package.json +1 -1
- package/src/cli.ts +10 -12
- package/src/namespaces/search.test.ts +218 -18
- package/src/namespaces/search.ts +122 -7
- package/src/qmd-client.test.ts +74 -1
- package/src/qmd.ts +52 -6
- package/src/search/port.ts +9 -0
- package/src/transcript-day-range.test.ts +101 -0
- package/src/transcript.ts +26 -0
- package/dist/chunk-3D6L7CEP.js.map +0 -1
- package/dist/chunk-APWJRJFW.js.map +0 -1
- package/dist/chunk-XUGQQPGO.js.map +0 -1
- /package/dist/{chunk-L7W5YW6Y.js.map → chunk-5ETA6OAS.js.map} +0 -0
- /package/dist/{chunk-DCGT4FPP.js.map → chunk-76QTEJ2Q.js.map} +0 -0
- /package/dist/{chunk-RAELB5NX.js.map → chunk-CNRZ6WJU.js.map} +0 -0
- /package/dist/{chunk-3MAONBX3.js.map → chunk-FOVPSMGI.js.map} +0 -0
- /package/dist/{chunk-ZPPFKVSD.js.map → chunk-FUXV6HSO.js.map} +0 -0
- /package/dist/{chunk-FAYDM5WD.js.map → chunk-RRRCNIPK.js.map} +0 -0
- /package/dist/{chunk-6BNFVP7Y.js.map → chunk-RZOBQ23O.js.map} +0 -0
- /package/dist/{chunk-RG3LBSGH.js.map → chunk-TQUWNX7C.js.map} +0 -0
- /package/dist/{chunk-U55D5UD5.js.map → chunk-WPCCNSWO.js.map} +0 -0
- /package/dist/{chunk-TA4LQ5SR.js.map → chunk-XUGVP7ZU.js.map} +0 -0
- /package/dist/{chunk-DC66QVL2.js.map → chunk-ZT6R3WR3.js.map} +0 -0
package/src/qmd.ts
CHANGED
|
@@ -2700,8 +2700,7 @@ export class QmdClient implements SearchBackend {
|
|
|
2700
2700
|
}
|
|
2701
2701
|
}
|
|
2702
2702
|
|
|
2703
|
-
async
|
|
2704
|
-
memoryDir: string,
|
|
2703
|
+
async checkCollection(
|
|
2705
2704
|
collectionOrExecution?: string | SearchExecutionOptions,
|
|
2706
2705
|
execution?: SearchExecutionOptions,
|
|
2707
2706
|
): Promise<"present" | "missing" | "unknown" | "skipped"> {
|
|
@@ -2723,6 +2722,7 @@ export class QmdClient implements SearchBackend {
|
|
|
2723
2722
|
if (collectionRegex.test(stdout)) {
|
|
2724
2723
|
return "present";
|
|
2725
2724
|
}
|
|
2725
|
+
return "missing";
|
|
2726
2726
|
} catch (err) {
|
|
2727
2727
|
// Treat command/probe failures as unknown so callers do not disable features
|
|
2728
2728
|
// permanently after a transient CLI or daemon hiccup.
|
|
@@ -2731,12 +2731,58 @@ export class QmdClient implements SearchBackend {
|
|
|
2731
2731
|
);
|
|
2732
2732
|
return "unknown";
|
|
2733
2733
|
}
|
|
2734
|
+
}
|
|
2734
2735
|
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2736
|
+
async ensureCollection(
|
|
2737
|
+
memoryDir: string,
|
|
2738
|
+
collectionOrExecution?: string | SearchExecutionOptions,
|
|
2739
|
+
execution?: SearchExecutionOptions,
|
|
2740
|
+
): Promise<"present" | "missing" | "unknown" | "skipped"> {
|
|
2741
|
+
const { collection, execution: effectiveExecution } = resolveEnsureCollectionArgs(
|
|
2742
|
+
collectionOrExecution,
|
|
2743
|
+
execution,
|
|
2738
2744
|
);
|
|
2739
|
-
|
|
2745
|
+
const targetCollection = collection ?? this.collection;
|
|
2746
|
+
const collectionState = await this.checkCollection(targetCollection, effectiveExecution);
|
|
2747
|
+
if (collectionState !== "missing") return collectionState;
|
|
2748
|
+
|
|
2749
|
+
try {
|
|
2750
|
+
await this.runQmdCommand(
|
|
2751
|
+
["collection", "add", memoryDir, "--name", targetCollection],
|
|
2752
|
+
QMD_TIMEOUT_MS,
|
|
2753
|
+
effectiveExecution?.signal,
|
|
2754
|
+
);
|
|
2755
|
+
log.info(
|
|
2756
|
+
`QMD collection "${targetCollection}" auto-created at ${memoryDir}`,
|
|
2757
|
+
);
|
|
2758
|
+
return "present";
|
|
2759
|
+
} catch (err) {
|
|
2760
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2761
|
+
if (isCallerCancellation(err, effectiveExecution?.signal)) {
|
|
2762
|
+
log.debug(
|
|
2763
|
+
`QMD collection auto-create for "${targetCollection}" was cancelled; keeping collection state unknown`,
|
|
2764
|
+
);
|
|
2765
|
+
return "unknown";
|
|
2766
|
+
}
|
|
2767
|
+
const postCreateState = await this.checkCollection(targetCollection, effectiveExecution);
|
|
2768
|
+
if (postCreateState === "present") {
|
|
2769
|
+
log.info(
|
|
2770
|
+
`QMD collection "${targetCollection}" is present after auto-create failure; continuing`,
|
|
2771
|
+
);
|
|
2772
|
+
return "present";
|
|
2773
|
+
}
|
|
2774
|
+
if (/\balready exists\b|\bexists already\b/i.test(msg)) {
|
|
2775
|
+
log.info(
|
|
2776
|
+
`QMD collection "${targetCollection}" already exists after concurrent auto-create; continuing`,
|
|
2777
|
+
);
|
|
2778
|
+
return "present";
|
|
2779
|
+
}
|
|
2780
|
+
if (postCreateState !== "missing") return postCreateState;
|
|
2781
|
+
log.warn(
|
|
2782
|
+
`QMD collection "${targetCollection}" not found and auto-create failed: ${msg}`,
|
|
2783
|
+
);
|
|
2784
|
+
return "missing";
|
|
2785
|
+
}
|
|
2740
2786
|
}
|
|
2741
2787
|
}
|
|
2742
2788
|
|
package/src/search/port.ts
CHANGED
|
@@ -93,6 +93,15 @@ export interface SearchBackend {
|
|
|
93
93
|
embedCollection(collection: string): Promise<void>;
|
|
94
94
|
|
|
95
95
|
// ── Collection management ──
|
|
96
|
+
/**
|
|
97
|
+
* Optional non-mutating collection probe. Backends that can distinguish a
|
|
98
|
+
* missing collection from a transient probe failure should implement this so
|
|
99
|
+
* callers can avoid auto-creating collections in unsafe layouts.
|
|
100
|
+
*/
|
|
101
|
+
checkCollection?(
|
|
102
|
+
collectionOrExecution?: string | SearchExecutionOptions,
|
|
103
|
+
execution?: SearchExecutionOptions,
|
|
104
|
+
): Promise<"present" | "missing" | "unknown" | "skipped">;
|
|
96
105
|
ensureCollection(
|
|
97
106
|
memoryDir: string,
|
|
98
107
|
execution?: SearchExecutionOptions,
|
|
@@ -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
|
*
|