@gmickel/gno 2.2.1 → 2.3.1
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/README.md +50 -4
- package/assets/skill/README.md +60 -21
- package/assets/skill/SKILL.md +7 -0
- package/assets/skill/cli-reference.md +16 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v2.2.1.zip → gno-browser-clipper-v2.3.1.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.3.1.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/spec/cli.md +21 -0
- package/spec/mcp.md +9 -0
- package/spec/output-schemas/status.schema.json +41 -0
- package/src/cli/commands/shared.ts +6 -0
- package/src/cli/commands/status.ts +9 -0
- package/src/config/chunking.ts +46 -0
- package/src/config/types.ts +6 -1
- package/src/core/chunking-status.ts +30 -0
- package/src/core/mutation-generations.ts +2 -0
- package/src/ingestion/chunking.ts +108 -0
- package/src/ingestion/record-container.ts +9 -8
- package/src/ingestion/sync-options.ts +3 -1
- package/src/ingestion/sync.ts +33 -24
- package/src/ingestion/types.ts +15 -2
- package/src/mcp/tools/status.ts +5 -0
- package/src/sdk/client.ts +1 -0
- package/src/serve/status-model.ts +2 -0
- package/src/serve/status.ts +2 -0
- package/src/store/chunking.ts +31 -0
- package/src/store/sqlite/adapter.ts +104 -2
- package/src/store/sqlite/chunking-policy.ts +212 -0
- package/src/store/types.ts +32 -1
- package/browser-extension/artifacts/gno-browser-clipper-v2.2.1.zip.sha256 +0 -1
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "../core/change-diff";
|
|
24
24
|
import { createEgressLineage } from "../core/egress-provenance";
|
|
25
25
|
import { normalizeTag, validateTag } from "../core/tags";
|
|
26
|
+
import { persistChunkLayout } from "./chunking";
|
|
26
27
|
import { runRecordAdapter } from "./record-adapter";
|
|
27
28
|
import { recordVirtualPath } from "./record-path";
|
|
28
29
|
import { reconcileRecordSnapshot, type RecordSyncPlan } from "./record-sync";
|
|
@@ -397,7 +398,7 @@ const persistRecord = async (
|
|
|
397
398
|
const chunks: ChunkInput[] = input.chunker
|
|
398
399
|
.chunk(
|
|
399
400
|
record.markdown,
|
|
400
|
-
DEFAULT_CHUNK_PARAMS,
|
|
401
|
+
input.options.chunkingToken?.params ?? DEFAULT_CHUNK_PARAMS,
|
|
401
402
|
record.languageHint ?? input.collection.languageHint,
|
|
402
403
|
virtualPath
|
|
403
404
|
)
|
|
@@ -410,13 +411,13 @@ const persistRecord = async (
|
|
|
410
411
|
language: chunk.language ?? undefined,
|
|
411
412
|
tokenCount: chunk.tokenCount ?? undefined,
|
|
412
413
|
}));
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
414
|
+
await persistChunkLayout(
|
|
415
|
+
input.store,
|
|
416
|
+
record.mirrorHash,
|
|
417
|
+
chunks,
|
|
418
|
+
input.options.chunkingToken,
|
|
419
|
+
virtualPath,
|
|
420
|
+
record.languageHint ?? input.collection.languageHint
|
|
420
421
|
);
|
|
421
422
|
mustOk(
|
|
422
423
|
await input.store.setDocTags(document.id, categories, "frontmatter"),
|
|
@@ -20,11 +20,13 @@ export function resolveContentTypeRules(
|
|
|
20
20
|
|
|
21
21
|
export function withContentTypeRules(
|
|
22
22
|
options: SyncOptions = {},
|
|
23
|
-
config?: Pick<Config, "contentTypes">
|
|
23
|
+
config?: Pick<Config, "contentTypes" | "chunking">
|
|
24
24
|
): SyncOptions {
|
|
25
25
|
const rules = options.contentTypeRules ?? resolveContentTypeRules(config);
|
|
26
|
+
const chunking = options.chunking ?? config?.chunking;
|
|
26
27
|
return {
|
|
27
28
|
...options,
|
|
29
|
+
...(chunking ? { chunking } : {}),
|
|
28
30
|
contentTypeRules: rules,
|
|
29
31
|
contentTypeRulesFingerprint:
|
|
30
32
|
options.contentTypeRulesFingerprint ??
|
package/src/ingestion/sync.ts
CHANGED
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
import { extractMemoryScopes } from "../core/memory-record";
|
|
59
59
|
import { normalizeTag, validateTag } from "../core/tags";
|
|
60
60
|
import { defaultChunker } from "./chunker";
|
|
61
|
+
import { persistChunkLayout, prepareChunking } from "./chunking";
|
|
61
62
|
import {
|
|
62
63
|
extractHashtags,
|
|
63
64
|
parseFrontmatter,
|
|
@@ -937,7 +938,7 @@ export class SyncService {
|
|
|
937
938
|
// 9. Chunk content
|
|
938
939
|
const chunks = this.chunker.chunk(
|
|
939
940
|
artifact.markdown,
|
|
940
|
-
DEFAULT_CHUNK_PARAMS,
|
|
941
|
+
options.chunkingToken?.params ?? DEFAULT_CHUNK_PARAMS,
|
|
941
942
|
artifact.languageHint ?? collection.languageHint,
|
|
942
943
|
entry.relPath
|
|
943
944
|
);
|
|
@@ -953,21 +954,15 @@ export class SyncService {
|
|
|
953
954
|
tokenCount: c.tokenCount ?? undefined,
|
|
954
955
|
}));
|
|
955
956
|
|
|
956
|
-
// 11.
|
|
957
|
-
|
|
957
|
+
// 11-12. Apply chunks and lexical projection under the policy token.
|
|
958
|
+
await persistChunkLayout(
|
|
959
|
+
store,
|
|
958
960
|
artifact.mirrorHash,
|
|
959
|
-
chunkInputs
|
|
961
|
+
chunkInputs,
|
|
962
|
+
options.chunkingToken,
|
|
963
|
+
entry.relPath,
|
|
964
|
+
artifact.languageHint ?? collection.languageHint
|
|
960
965
|
);
|
|
961
|
-
mustOk(chunksResult, "upsertChunks", {
|
|
962
|
-
mirrorHash: artifact.mirrorHash,
|
|
963
|
-
chunkCount: chunkInputs.length,
|
|
964
|
-
});
|
|
965
|
-
|
|
966
|
-
// 12. Rebuild FTS for this hash - CHECKED
|
|
967
|
-
const ftsResult = await store.rebuildFtsForHash(artifact.mirrorHash);
|
|
968
|
-
mustOk(ftsResult, "rebuildFtsForHash", {
|
|
969
|
-
mirrorHash: artifact.mirrorHash,
|
|
970
|
-
});
|
|
971
966
|
|
|
972
967
|
// 13. Extract and store tags from frontmatter and body hashtags
|
|
973
968
|
// Always call setDocTags to clear removed tags on re-sync
|
|
@@ -1249,8 +1244,9 @@ export class SyncService {
|
|
|
1249
1244
|
options: SyncOptions = {}
|
|
1250
1245
|
): Promise<CollectionSyncResult> {
|
|
1251
1246
|
const startedAt = Date.now();
|
|
1247
|
+
const prepared = await prepareChunking(store, this.chunker, options);
|
|
1252
1248
|
const syncOptions: SyncOptions = {
|
|
1253
|
-
...options,
|
|
1249
|
+
...prepared.options,
|
|
1254
1250
|
contentTypeRules: options.contentTypeRules ?? [],
|
|
1255
1251
|
contentTypeRulesFingerprint:
|
|
1256
1252
|
options.contentTypeRulesFingerprint ??
|
|
@@ -1511,13 +1507,18 @@ export class SyncService {
|
|
|
1511
1507
|
syncOptions.projectTypedEdges === false
|
|
1512
1508
|
? []
|
|
1513
1509
|
: await this.projectTypedEdges(store, syncOptions, projectionSourceIds);
|
|
1514
|
-
return
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1510
|
+
return {
|
|
1511
|
+
...summarizePathResults(
|
|
1512
|
+
collection.name,
|
|
1513
|
+
results,
|
|
1514
|
+
markedInactive,
|
|
1515
|
+
startedAt,
|
|
1516
|
+
errors
|
|
1517
|
+
),
|
|
1518
|
+
...(prepared.rechunkedMirrors
|
|
1519
|
+
? { rechunkedMirrors: prepared.rechunkedMirrors }
|
|
1520
|
+
: {}),
|
|
1521
|
+
};
|
|
1521
1522
|
}
|
|
1522
1523
|
|
|
1523
1524
|
/**
|
|
@@ -1717,8 +1718,9 @@ export class SyncService {
|
|
|
1717
1718
|
options: SyncOptions = {}
|
|
1718
1719
|
): Promise<CollectionSyncResult> {
|
|
1719
1720
|
const startTime = Date.now();
|
|
1721
|
+
const prepared = await prepareChunking(store, this.chunker, options);
|
|
1720
1722
|
const syncOptions: SyncOptions = {
|
|
1721
|
-
...options,
|
|
1723
|
+
...prepared.options,
|
|
1722
1724
|
contentTypeRules: options.contentTypeRules ?? [],
|
|
1723
1725
|
contentTypeRulesFingerprint:
|
|
1724
1726
|
options.contentTypeRulesFingerprint ??
|
|
@@ -2038,6 +2040,9 @@ export class SyncService {
|
|
|
2038
2040
|
|
|
2039
2041
|
return {
|
|
2040
2042
|
collection: collection.name,
|
|
2043
|
+
...(prepared.rechunkedMirrors
|
|
2044
|
+
? { rechunkedMirrors: prepared.rechunkedMirrors }
|
|
2045
|
+
: {}),
|
|
2041
2046
|
filesProcessed: entries.length + inventoryErrored,
|
|
2042
2047
|
filesAdded: added,
|
|
2043
2048
|
filesUpdated: updated,
|
|
@@ -2060,9 +2065,10 @@ export class SyncService {
|
|
|
2060
2065
|
options: SyncOptions = {}
|
|
2061
2066
|
): Promise<SyncResult> {
|
|
2062
2067
|
const startTime = Date.now();
|
|
2068
|
+
const prepared = await prepareChunking(store, this.chunker, options);
|
|
2063
2069
|
const results: CollectionSyncResult[] = [];
|
|
2064
2070
|
const deferredProjectionOptions: SyncOptions = {
|
|
2065
|
-
...options,
|
|
2071
|
+
...prepared.options,
|
|
2066
2072
|
projectTypedEdges: false,
|
|
2067
2073
|
};
|
|
2068
2074
|
|
|
@@ -2094,6 +2100,9 @@ export class SyncService {
|
|
|
2094
2100
|
|
|
2095
2101
|
return {
|
|
2096
2102
|
collections: results,
|
|
2103
|
+
...(prepared.rechunkedMirrors
|
|
2104
|
+
? { rechunkedMirrors: prepared.rechunkedMirrors }
|
|
2105
|
+
: {}),
|
|
2097
2106
|
totalDurationMs: Date.now() - startTime,
|
|
2098
2107
|
totalFilesProcessed: totals.processed,
|
|
2099
2108
|
totalFilesAdded: totals.added,
|
package/src/ingestion/types.ts
CHANGED
|
@@ -12,8 +12,14 @@ import type {
|
|
|
12
12
|
RecordAttachmentInventoryItem,
|
|
13
13
|
} from "../converters/types";
|
|
14
14
|
import type { EgressLineage } from "../core/egress-provenance";
|
|
15
|
+
import type { ChunkingPolicyToken } from "../store/chunking";
|
|
15
16
|
import type { DirectoryAvailabilityPort } from "./source-availability/types";
|
|
16
17
|
|
|
18
|
+
import {
|
|
19
|
+
DEFAULT_CHUNKING_PARAMS,
|
|
20
|
+
type ChunkingParams,
|
|
21
|
+
} from "../config/chunking";
|
|
22
|
+
|
|
17
23
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
24
|
// Walker Types
|
|
19
25
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -115,8 +121,7 @@ export interface ChunkParams {
|
|
|
115
121
|
|
|
116
122
|
/** Default chunk params */
|
|
117
123
|
export const DEFAULT_CHUNK_PARAMS: ChunkParams = {
|
|
118
|
-
|
|
119
|
-
overlapPercent: 0.15,
|
|
124
|
+
...DEFAULT_CHUNKING_PARAMS,
|
|
120
125
|
};
|
|
121
126
|
|
|
122
127
|
/** Chunked output */
|
|
@@ -157,6 +162,10 @@ export interface ChunkerPort {
|
|
|
157
162
|
|
|
158
163
|
/** Sync options */
|
|
159
164
|
export interface SyncOptions {
|
|
165
|
+
/** Index-wide configured policy; omitted means the existing defaults. */
|
|
166
|
+
chunking?: Partial<ChunkingParams>;
|
|
167
|
+
/** Internal token passed from an outer sync; never a public CLI override. */
|
|
168
|
+
chunkingToken?: ChunkingPolicyToken;
|
|
160
169
|
/** Run git pull before scanning */
|
|
161
170
|
gitPull?: boolean;
|
|
162
171
|
/** Run collection updateCmd before scanning */
|
|
@@ -266,6 +275,8 @@ export interface FileSyncResult {
|
|
|
266
275
|
|
|
267
276
|
/** Collection sync summary */
|
|
268
277
|
export interface CollectionSyncResult {
|
|
278
|
+
/** Cached layouts updated independently of source-refresh file counters. */
|
|
279
|
+
rechunkedMirrors?: number;
|
|
269
280
|
collection: string;
|
|
270
281
|
filesProcessed: number;
|
|
271
282
|
filesAdded: number;
|
|
@@ -285,6 +296,8 @@ export interface CollectionSyncResult {
|
|
|
285
296
|
|
|
286
297
|
/** Full sync summary */
|
|
287
298
|
export interface SyncResult {
|
|
299
|
+
/** Cached layouts updated independently of source-refresh file counters. */
|
|
300
|
+
rechunkedMirrors?: number;
|
|
288
301
|
collections: CollectionSyncResult[];
|
|
289
302
|
totalDurationMs: number;
|
|
290
303
|
totalFilesProcessed: number;
|
package/src/mcp/tools/status.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { IndexStatus } from "../../store/types";
|
|
|
8
8
|
import type { ToolContext } from "../server";
|
|
9
9
|
|
|
10
10
|
import { buildContentTypeBoostStatus } from "../../config/content-types";
|
|
11
|
+
import { formatChunkingStatus } from "../../core/chunking-status";
|
|
11
12
|
import { resolveModelUri } from "../../llm/registry";
|
|
12
13
|
import { createStandaloneResidentStatus } from "../../serve/resident-status";
|
|
13
14
|
import { runTool, type ToolResult } from "./index";
|
|
@@ -55,6 +56,9 @@ function formatStatus(status: IndexStatus): string {
|
|
|
55
56
|
lines.push(`Embedding backlog: ${status.embeddingBacklog} chunks`);
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
const chunking = formatChunkingStatus(status.chunking);
|
|
60
|
+
if (chunking) lines.push(chunking);
|
|
61
|
+
|
|
58
62
|
if (status.recentErrors > 0) {
|
|
59
63
|
lines.push(`Recent errors: ${status.recentErrors} (last 24h)`);
|
|
60
64
|
}
|
|
@@ -79,6 +83,7 @@ export function handleStatus(
|
|
|
79
83
|
async () => {
|
|
80
84
|
const result = await ctx.store.getStatus({
|
|
81
85
|
embedModel: resolveModelUri(ctx.config, "embed"),
|
|
86
|
+
chunking: ctx.config.chunking ?? {},
|
|
82
87
|
});
|
|
83
88
|
if (!result.ok) {
|
|
84
89
|
throw new Error(result.error.message);
|
package/src/sdk/client.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ContentTypeBoostStatus } from "../config/content-types";
|
|
2
2
|
import type { ActivationStatus } from "../core/activation-status";
|
|
3
|
+
import type { ChunkingStatus } from "../store/chunking";
|
|
3
4
|
|
|
4
5
|
export type HealthCheckStatus = "ok" | "warn" | "error";
|
|
5
6
|
|
|
@@ -176,6 +177,7 @@ export interface BootstrapState {
|
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
export interface AppStatusResponse {
|
|
180
|
+
chunking?: ChunkingStatus;
|
|
179
181
|
resident: ResidentStatus;
|
|
180
182
|
indexName: string;
|
|
181
183
|
configPath: string;
|
package/src/serve/status.ts
CHANGED
|
@@ -654,6 +654,7 @@ export async function buildAppStatus(
|
|
|
654
654
|
): Promise<AppStatusResponse> {
|
|
655
655
|
const result = await ctx.store.getStatus({
|
|
656
656
|
embedModel: resolveModelUri(ctx.config, "embed"),
|
|
657
|
+
chunking: ctx.config.chunking ?? {},
|
|
657
658
|
});
|
|
658
659
|
if (!result.ok) {
|
|
659
660
|
throw result.error;
|
|
@@ -733,6 +734,7 @@ export async function buildAppStatus(
|
|
|
733
734
|
totalDocuments: status.activeDocuments,
|
|
734
735
|
totalChunks: status.totalChunks,
|
|
735
736
|
embeddingBacklog: status.embeddingBacklog,
|
|
737
|
+
chunking: status.chunking,
|
|
736
738
|
lastUpdated: status.lastUpdatedAt,
|
|
737
739
|
recentErrors: status.recentErrors,
|
|
738
740
|
healthy: checks.every((check) => check.status === "ok"),
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ChunkingParams } from "../config/chunking";
|
|
2
|
+
|
|
3
|
+
export interface ChunkingPolicyToken {
|
|
4
|
+
params: ChunkingParams;
|
|
5
|
+
generation: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface PendingChunkingMirror {
|
|
9
|
+
mirrorHash: string;
|
|
10
|
+
sourcePath: string;
|
|
11
|
+
languageHint?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ChunkingStatus {
|
|
15
|
+
configured: ChunkingParams;
|
|
16
|
+
applied: ChunkingParams | null;
|
|
17
|
+
state: "empty" | "legacy-default" | "current" | "pending" | "mixed";
|
|
18
|
+
pendingDocuments: number;
|
|
19
|
+
pendingMirrors: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class ChunkingPolicyConflictError extends Error {
|
|
23
|
+
readonly code = "CHUNKING_POLICY_CONFLICT";
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
super(
|
|
27
|
+
"CHUNKING_POLICY_CONFLICT: The index chunking policy changed. Reopen the client or restart the resident runtime with the intended configuration."
|
|
28
|
+
);
|
|
29
|
+
this.name = "ChunkingPolicyConflictError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -113,6 +113,11 @@ import type {
|
|
|
113
113
|
import type { SqliteDbProvider } from "./types";
|
|
114
114
|
|
|
115
115
|
import { buildUri, deriveDocid, stripUriIndex } from "../../app/constants";
|
|
116
|
+
import {
|
|
117
|
+
DEFAULT_CHUNKING_PARAMS,
|
|
118
|
+
resolveChunkingParams,
|
|
119
|
+
type ChunkingParams,
|
|
120
|
+
} from "../../config/chunking";
|
|
116
121
|
import {
|
|
117
122
|
type Collection,
|
|
118
123
|
type Context,
|
|
@@ -140,6 +145,11 @@ import {
|
|
|
140
145
|
parseActivationReceipt,
|
|
141
146
|
serializeActivationReceipt,
|
|
142
147
|
} from "../activation-receipts";
|
|
148
|
+
import {
|
|
149
|
+
ChunkingPolicyConflictError,
|
|
150
|
+
type ChunkingPolicyToken,
|
|
151
|
+
type PendingChunkingMirror,
|
|
152
|
+
} from "../chunking";
|
|
143
153
|
import { getSchemaVersion, migrations, runMigrations } from "../migrations";
|
|
144
154
|
import { err, ok } from "../types";
|
|
145
155
|
import { getStoredEmbeddingFingerprint } from "../vector/freshness";
|
|
@@ -164,6 +174,15 @@ import {
|
|
|
164
174
|
purgeDocumentChanges as purgeStoredDocumentChanges,
|
|
165
175
|
snapshotDocumentChange,
|
|
166
176
|
} from "./change-journal-store";
|
|
177
|
+
import {
|
|
178
|
+
assertChunkingTarget,
|
|
179
|
+
claimChunkingTarget,
|
|
180
|
+
getChunkingStatus,
|
|
181
|
+
markChunkingApplied,
|
|
182
|
+
pendingChunkingMirrors,
|
|
183
|
+
pruneChunkingMetadata,
|
|
184
|
+
readChunkingTarget,
|
|
185
|
+
} from "./chunking-policy";
|
|
167
186
|
import {
|
|
168
187
|
appendEgressAuditReceipt as appendStoredEgressAuditReceipt,
|
|
169
188
|
appendEgressAuditReceiptWithRetention as appendStoredEgressAuditReceiptWithRetention,
|
|
@@ -535,6 +554,7 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
535
554
|
private shutdownFenced = false;
|
|
536
555
|
private shutdownDeadline?: number;
|
|
537
556
|
private contextGeneration = 0;
|
|
557
|
+
private chunkingGeneration = 0;
|
|
538
558
|
|
|
539
559
|
// ─────────────────────────────────────────────────────────────────────────
|
|
540
560
|
// Lifecycle
|
|
@@ -603,6 +623,7 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
603
623
|
return result;
|
|
604
624
|
}
|
|
605
625
|
|
|
626
|
+
this.chunkingGeneration = readChunkingTarget(this.db).generation;
|
|
606
627
|
this.contextGeneration += 1;
|
|
607
628
|
return result;
|
|
608
629
|
} catch (cause) {
|
|
@@ -2631,12 +2652,20 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2631
2652
|
|
|
2632
2653
|
async upsertChunks(
|
|
2633
2654
|
mirrorHash: string,
|
|
2634
|
-
chunks: ChunkInput[]
|
|
2655
|
+
chunks: ChunkInput[],
|
|
2656
|
+
policy?: ChunkingPolicyToken
|
|
2635
2657
|
): Promise<StoreResult<void>> {
|
|
2636
2658
|
try {
|
|
2637
2659
|
const db = this.ensureOpen();
|
|
2638
2660
|
|
|
2639
2661
|
const transaction = db.transaction(() => {
|
|
2662
|
+
assertChunkingTarget(
|
|
2663
|
+
db,
|
|
2664
|
+
policy ?? {
|
|
2665
|
+
params: DEFAULT_CHUNKING_PARAMS,
|
|
2666
|
+
generation: this.chunkingGeneration,
|
|
2667
|
+
}
|
|
2668
|
+
);
|
|
2640
2669
|
// Retain stable rows: DELETE cascades erase valid legacy vectors even
|
|
2641
2670
|
// when duplicate ingestion produces exactly the same embedding input.
|
|
2642
2671
|
const nextBySequence = new Map(
|
|
@@ -2693,13 +2722,83 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2693
2722
|
return ok(undefined);
|
|
2694
2723
|
} catch (cause) {
|
|
2695
2724
|
return err(
|
|
2696
|
-
|
|
2725
|
+
cause instanceof ChunkingPolicyConflictError
|
|
2726
|
+
? cause.code
|
|
2727
|
+
: "QUERY_FAILED",
|
|
2697
2728
|
cause instanceof Error ? cause.message : "Failed to upsert chunks",
|
|
2698
2729
|
cause
|
|
2699
2730
|
);
|
|
2700
2731
|
}
|
|
2701
2732
|
}
|
|
2702
2733
|
|
|
2734
|
+
async claimChunkingPolicy(
|
|
2735
|
+
input: ChunkingParams
|
|
2736
|
+
): Promise<StoreResult<ChunkingPolicyToken>> {
|
|
2737
|
+
const params = resolveChunkingParams(input);
|
|
2738
|
+
const result = await this.withTransaction(async () =>
|
|
2739
|
+
claimChunkingTarget(this.ensureOpen(), this.chunkingGeneration, params)
|
|
2740
|
+
);
|
|
2741
|
+
if (result.ok) this.chunkingGeneration = result.value.generation;
|
|
2742
|
+
else if (result.error.cause instanceof ChunkingPolicyConflictError) {
|
|
2743
|
+
return err(
|
|
2744
|
+
"CHUNKING_POLICY_CONFLICT",
|
|
2745
|
+
result.error.message,
|
|
2746
|
+
result.error.cause
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
return result;
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
async listPendingChunkingMirrors(
|
|
2753
|
+
policy: ChunkingPolicyToken,
|
|
2754
|
+
afterHash = ""
|
|
2755
|
+
): Promise<StoreResult<PendingChunkingMirror[]>> {
|
|
2756
|
+
try {
|
|
2757
|
+
return ok(pendingChunkingMirrors(this.ensureOpen(), policy, afterHash));
|
|
2758
|
+
} catch (cause) {
|
|
2759
|
+
return err(
|
|
2760
|
+
cause instanceof ChunkingPolicyConflictError
|
|
2761
|
+
? cause.code
|
|
2762
|
+
: "QUERY_FAILED",
|
|
2763
|
+
cause instanceof Error
|
|
2764
|
+
? cause.message
|
|
2765
|
+
: "Failed to list pending chunk layouts",
|
|
2766
|
+
cause
|
|
2767
|
+
);
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
async applyChunkLayout(
|
|
2772
|
+
mirrorHash: string,
|
|
2773
|
+
chunks: ChunkInput[],
|
|
2774
|
+
policy: ChunkingPolicyToken,
|
|
2775
|
+
sourcePath: string,
|
|
2776
|
+
languageHint?: string
|
|
2777
|
+
): Promise<StoreResult<void>> {
|
|
2778
|
+
const result = await this.withTransaction(async () => {
|
|
2779
|
+
const db = this.ensureOpen();
|
|
2780
|
+
assertChunkingTarget(db, policy);
|
|
2781
|
+
const applied = await this.upsertChunks(mirrorHash, chunks, policy);
|
|
2782
|
+
if (!applied.ok)
|
|
2783
|
+
throw applied.error.cause ?? new Error(applied.error.message);
|
|
2784
|
+
const indexed = await this.rebuildFtsForHash(mirrorHash);
|
|
2785
|
+
if (!indexed.ok)
|
|
2786
|
+
throw indexed.error.cause ?? new Error(indexed.error.message);
|
|
2787
|
+
markChunkingApplied(db, mirrorHash, policy, sourcePath, languageHint);
|
|
2788
|
+
});
|
|
2789
|
+
if (
|
|
2790
|
+
!result.ok &&
|
|
2791
|
+
result.error.cause instanceof ChunkingPolicyConflictError
|
|
2792
|
+
) {
|
|
2793
|
+
return err(
|
|
2794
|
+
"CHUNKING_POLICY_CONFLICT",
|
|
2795
|
+
result.error.message,
|
|
2796
|
+
result.error.cause
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2799
|
+
return result;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2703
2802
|
async getChunks(mirrorHash: string): Promise<StoreResult<ChunkRow[]>> {
|
|
2704
2803
|
try {
|
|
2705
2804
|
const db = this.ensureOpen();
|
|
@@ -5690,6 +5789,7 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
5690
5789
|
async getStatus(options?: {
|
|
5691
5790
|
embedModel?: string;
|
|
5692
5791
|
embedFingerprint?: string;
|
|
5792
|
+
chunking?: Partial<ChunkingParams>;
|
|
5693
5793
|
}): Promise<StoreResult<IndexStatus>> {
|
|
5694
5794
|
try {
|
|
5695
5795
|
const db = this.ensureOpen();
|
|
@@ -5884,6 +5984,7 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
5884
5984
|
activeDocuments: totalsRow?.active ?? 0,
|
|
5885
5985
|
totalChunks: chunkCount,
|
|
5886
5986
|
embeddingBacklog: variantStatus?.backlog ?? backlogRow?.count ?? 0,
|
|
5987
|
+
chunking: getChunkingStatus(db, options?.chunking),
|
|
5887
5988
|
recentErrors,
|
|
5888
5989
|
lastUpdatedAt: lastUpdatedRow?.last_updated ?? null,
|
|
5889
5990
|
healthy,
|
|
@@ -5968,6 +6069,7 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
5968
6069
|
)
|
|
5969
6070
|
`);
|
|
5970
6071
|
orphanedContent = contentResult.changes;
|
|
6072
|
+
pruneChunkingMetadata(db);
|
|
5971
6073
|
|
|
5972
6074
|
// Delete chunks for deleted content
|
|
5973
6075
|
const chunksResult = db.run(`
|