@evomap/evolver-core 2.0.0-beta.9 → 2.0.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/dist/algo/antiDistill.js +1 -167
- package/dist/algo/bans.js +1 -33
- package/dist/algo/candidateAssembly.d.ts +7 -2
- package/dist/algo/candidateAssembly.js +1 -205
- package/dist/algo/capabilityCandidates.js +1 -136
- package/dist/algo/confidence.js +1 -196
- package/dist/algo/conversationSniffer.js +1 -149
- package/dist/algo/cycleEngine.d.ts +23 -2
- package/dist/algo/cycleEngine.js +1 -425
- package/dist/algo/cycleFailureClassifier.js +1 -112
- package/dist/algo/epigenetics.js +1 -41
- package/dist/algo/evolutionEvent.js +1 -24
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +1 -55
- package/dist/algo/geneHealth.d.ts +2 -2
- package/dist/algo/geneHealth.js +1 -16
- package/dist/algo/geneIntake.js +1 -128
- package/dist/algo/genePromotion.js +1 -53
- package/dist/algo/geneSelection.d.ts +63 -5
- package/dist/algo/geneSelection.js +1 -246
- package/dist/algo/index.d.ts +2 -0
- package/dist/algo/index.js +1 -20
- package/dist/algo/memoryGraph.js +1 -86
- package/dist/algo/mutation.js +1 -22
- package/dist/algo/orchestrator.d.ts +8 -1
- package/dist/algo/orchestrator.js +1 -75
- package/dist/algo/publishEligibility.d.ts +34 -0
- package/dist/algo/publishEligibility.js +1 -0
- package/dist/algo/solidify.js +1 -63
- package/dist/algo/strategyPresets.js +1 -61
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +1 -0
- package/dist/assetstore/assetSidecarRecords.d.ts +7 -2
- package/dist/assetstore/assetSidecarRecords.js +238 -0
- package/dist/assetstore/assetSidecarRecovery.js +2 -2
- package/dist/assetstore/assetStoreHealth.d.ts +6 -0
- package/dist/assetstore/assetStoreHealth.js +56 -14
- package/dist/assetstore/assetStoreStorage.d.ts +1 -1
- package/dist/assetstore/assetStoreStorage.js +27 -9
- package/dist/assetstore/assetSyncLedger.d.ts +86 -0
- package/dist/assetstore/assetSyncLedger.js +683 -4
- package/dist/assetstore/index.d.ts +1 -0
- package/dist/assetstore/index.js +1 -0
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +4 -2
- package/dist/assetstore/localJsonl.js +89 -9
- package/dist/assetstore/provenance.d.ts +80 -4
- package/dist/assetstore/provenance.js +313 -5
- package/dist/assetstore/provider.d.ts +58 -1
- package/dist/assetstore/provider.js +97 -6
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +1 -251
- package/dist/benchmark/antiGeneImpact.js +1 -34
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +1 -268
- package/dist/benchmark/benchmark.js +1 -26
- package/dist/benchmark/evolutionThesisSolver.js +1 -41
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +1 -6
- package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
- package/dist/benchmark/selectionFlatAbstention.js +1 -0
- package/dist/benchmark/thesis.js +1 -150
- package/dist/benchmark/triggerShift.js +1 -106
- package/dist/bootstrap/envFingerprint.d.ts +9 -0
- package/dist/bootstrap/envFingerprint.js +5 -0
- package/dist/bootstrap/index.d.ts +2 -1
- package/dist/bootstrap/index.js +2 -1
- package/dist/bootstrap/v1EnvCompat.d.ts +111 -0
- package/dist/bootstrap/v1EnvCompat.js +280 -0
- package/dist/cycle/cycleTimeline.js +1 -74
- package/dist/cycle/index.js +1 -2
- package/dist/cycle/stateMachine.js +1 -25
- package/dist/events/eventArchive.d.ts +2 -0
- package/dist/events/eventArchive.js +13 -3
- package/dist/events/eventSchema.d.ts +7 -7
- package/dist/events/eventStore.d.ts +2 -0
- package/dist/events/eventStore.js +5 -1
- package/dist/events/ingest.d.ts +1 -0
- package/dist/events/ingest.js +1 -0
- package/dist/events/paths.d.ts +3 -1
- package/dist/events/paths.js +4 -0
- package/dist/events/public.d.ts +2 -2
- package/dist/events/public.js +2 -2
- package/dist/events/reports.d.ts +2 -0
- package/dist/events/reports.js +4 -0
- package/dist/exec/autoExec.d.ts +43 -2
- package/dist/exec/autoExec.js +76 -9
- package/dist/exec/autonomousCycle.d.ts +22 -4
- package/dist/exec/autonomousCycle.js +64 -13
- package/dist/exec/claudeBridge.d.ts +32 -7
- package/dist/exec/claudeBridge.js +281 -29
- package/dist/exec/prompt.js +5 -1
- package/dist/exec/runnerRegistry.d.ts +68 -26
- package/dist/exec/runnerRegistry.js +307 -72
- package/dist/exec/selfPr.js +1 -7
- package/dist/feedback/envelope.d.ts +61 -0
- package/dist/feedback/envelope.js +168 -0
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +1 -0
- package/dist/hooks/hooks.js +1 -0
- package/dist/hub/agentDirectory.js +1 -104
- package/dist/hub/assetCallLog.d.ts +35 -1
- package/dist/hub/assetCallLog.js +1 -72
- package/dist/hub/bindings.d.ts +8 -1
- package/dist/hub/bindings.js +1 -110
- package/dist/hub/capability.d.ts +90 -3
- package/dist/hub/capability.js +1 -1
- package/dist/hub/conversationDistiller.js +1 -264
- package/dist/hub/fake.d.ts +2 -2
- package/dist/hub/fake.js +1 -70
- package/dist/hub/hubReview.js +1 -106
- package/dist/hub/index.js +1 -11
- package/dist/hub/ingest.js +1 -15
- package/dist/hub/questionGenerator.d.ts +5 -1
- package/dist/hub/questionGenerator.js +1 -406
- package/dist/hub/reuseDecision.js +1 -127
- package/dist/hub/sanitize.js +1 -322
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -1
- package/dist/mailbox/dispatch.d.ts +1 -1
- package/dist/mailbox/dispatch.js +22 -6
- package/dist/mailbox/envelope.d.ts +7 -1
- package/dist/mailbox/envelope.js +9 -2
- package/dist/mailbox/ipcServer.d.ts +10 -2
- package/dist/mailbox/ipcServer.js +163 -13
- package/dist/mailbox/store.d.ts +89 -3
- package/dist/mailbox/store.js +895 -41
- package/dist/material/boundary.js +1 -14
- package/dist/material/consumer.js +1 -55
- package/dist/material/emit.js +1 -52
- package/dist/material/factory.js +1 -25
- package/dist/material/index.js +1 -9
- package/dist/material/materialArchive.js +1 -466
- package/dist/material/materialStore.js +1 -69
- package/dist/material/sampling.js +1 -39
- package/dist/material/sources.js +1 -33
- package/dist/material/watermark.js +1 -76
- package/dist/ops/evolutionGraphProjection.d.ts +20 -0
- package/dist/ops/evolutionGraphProjection.js +315 -0
- package/dist/ops/index.d.ts +2 -1
- package/dist/ops/index.js +2 -1
- package/dist/ops/selfUpdate.d.ts +8 -0
- package/dist/ops/selfUpdate.js +24 -8
- package/dist/personality/drift.js +1 -106
- package/dist/personality/events.js +1 -37
- package/dist/personality/evolveOps.js +1 -93
- package/dist/personality/index.js +1 -11
- package/dist/personality/mutate.js +1 -39
- package/dist/personality/pivot.js +1 -22
- package/dist/personality/prompt.js +1 -62
- package/dist/personality/riskGate.js +1 -80
- package/dist/personality/schema.d.ts +16 -16
- package/dist/personality/schema.js +1 -119
- package/dist/personality/select.js +1 -71
- package/dist/personality/stats.js +1 -82
- package/dist/personality/store.js +1 -92
- package/dist/schema/common.js +1 -17
- package/dist/schema/evolutionGraph.d.ts +784 -0
- package/dist/schema/evolutionGraph.js +1 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -5
- package/dist/schema/material.js +1 -50
- package/dist/schema/problem.js +1 -45
- package/dist/schema/proofOfWork.js +1 -9
- package/dist/schema/signal.d.ts +3 -3
- package/dist/schema/signal.js +1 -19
- package/dist/signals/curriculum.d.ts +55 -0
- package/dist/signals/curriculum.js +1 -0
- package/dist/signals/cycleHistoryFromEvents.js +1 -96
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +1 -125
- package/dist/signals/extractor.d.ts +2 -2
- package/dist/signals/extractor.js +1 -57
- package/dist/signals/index.d.ts +3 -1
- package/dist/signals/index.js +1 -6
- package/dist/signals/metaSignals.d.ts +4 -0
- package/dist/signals/metaSignals.js +1 -158
- package/dist/signals/signalGate.js +1 -40
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +1 -0
- package/dist/signals/traceSignals.js +1 -123
- package/dist/strategy/constraintAblation.js +1 -3074
- package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
- package/dist/strategy/constraintAblationPredicates.js +1 -0
- package/dist/strategy/experiment.js +1 -63
- package/dist/strategy/index.js +1 -3
- package/dist/strategy/strategyPoint.js +1 -23
- package/dist/trace/index.d.ts +3 -1
- package/dist/trace/index.js +3 -1
- package/dist/trace/learningTrace.d.ts +209 -0
- package/dist/trace/learningTrace.js +276 -0
- package/dist/trace/proxyTurns.d.ts +31 -0
- package/dist/trace/proxyTurns.js +137 -0
- package/dist/verify/sandboxRunner.d.ts +28 -0
- package/dist/verify/sandboxRunner.js +218 -19
- package/dist/verify/sandboxedValidation.d.ts +9 -0
- package/dist/verify/sandboxedValidation.js +113 -13
- package/dist/verify/validation.d.ts +11 -1
- package/dist/verify/validation.js +31 -0
- package/package.json +5 -2
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { lstatSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { acquireLock, releaseLock } from '../util/fileLock.js';
|
|
4
|
-
import { validateWire, verifyAssetId } from '../wire/index.js';
|
|
4
|
+
import { computeAssetId, validateWire, verifyAssetId } from '../wire/index.js';
|
|
5
5
|
import { LOCAL_ASSET_FILES } from './assetStoreLayout.js';
|
|
6
6
|
import { assertOptionalRegularFile, isReliableAssetStoreLockRelease, readUtf8Regular, UnsafeAssetStorePathError, } from './assetStoreStorage.js';
|
|
7
|
-
import {
|
|
7
|
+
import { isActiveUnverifiedProvenance } from './provenance.js';
|
|
8
|
+
import { parseAssetSyncSidecarRecord, parseProvenanceRecord, parseReviewRecord, parseSidecarJsonl, } from './assetSidecarRecords.js';
|
|
8
9
|
export const DEFAULT_ASSET_HEALTH_MAX_FILE_BYTES = 64 * 1024 * 1024;
|
|
9
10
|
const LOCAL_ASSET_SIDECARS = [
|
|
10
11
|
{ kind: 'provenance', file: 'provenance.jsonl', parseRecord: parseProvenanceRecord },
|
|
11
12
|
{ kind: 'review', file: 'review.jsonl', parseRecord: parseReviewRecord },
|
|
12
|
-
{ kind: 'asset-sync', file: 'asset-sync.jsonl', parseRecord:
|
|
13
|
+
{ kind: 'asset-sync', file: 'asset-sync.jsonl', parseRecord: parseAssetSyncSidecarRecord },
|
|
13
14
|
];
|
|
14
15
|
function healthScanLimit(value) {
|
|
15
16
|
if (value === undefined || !Number.isFinite(value))
|
|
@@ -31,6 +32,7 @@ function emptyFile(kind, file, status, reason) {
|
|
|
31
32
|
duplicateRows: 0,
|
|
32
33
|
corruptRows: 0,
|
|
33
34
|
hashMismatchRows: 0,
|
|
35
|
+
unverifiedRows: 0,
|
|
34
36
|
schemaInvalidRows: 0,
|
|
35
37
|
unterminated: false,
|
|
36
38
|
...(reason ? { reason } : {}),
|
|
@@ -69,6 +71,7 @@ function summarize(files, sidecars) {
|
|
|
69
71
|
duplicateRows: files.reduce((sum, file) => sum + file.duplicateRows, 0),
|
|
70
72
|
corruptRows: files.reduce((sum, file) => sum + file.corruptRows, 0),
|
|
71
73
|
hashMismatchRows: files.reduce((sum, file) => sum + file.hashMismatchRows, 0),
|
|
74
|
+
unverifiedRows: files.reduce((sum, file) => sum + file.unverifiedRows, 0),
|
|
72
75
|
schemaInvalidRows: files.reduce((sum, file) => sum + file.schemaInvalidRows, 0),
|
|
73
76
|
unterminatedFiles: files.filter((file) => file.unterminated).length,
|
|
74
77
|
};
|
|
@@ -105,7 +108,7 @@ function summarize(files, sidecars) {
|
|
|
105
108
|
sidecars: [...sidecars],
|
|
106
109
|
};
|
|
107
110
|
}
|
|
108
|
-
function inspectFile(baseDir, kind, file, maxFileBytes) {
|
|
111
|
+
function inspectFile(baseDir, kind, file, maxFileBytes, unverifiedContentIds) {
|
|
109
112
|
try {
|
|
110
113
|
const path = join(baseDir, file);
|
|
111
114
|
const stat = assertOptionalRegularFile(path);
|
|
@@ -114,7 +117,7 @@ function inspectFile(baseDir, kind, file, maxFileBytes) {
|
|
|
114
117
|
if (stat.size > maxFileBytes) {
|
|
115
118
|
return { ...emptyFile(kind, file, 'unavailable', 'scan_limit_exceeded'), bytes: stat.size };
|
|
116
119
|
}
|
|
117
|
-
const raw = readUtf8Regular(path);
|
|
120
|
+
const raw = readUtf8Regular(path, maxFileBytes);
|
|
118
121
|
if (raw === null)
|
|
119
122
|
return emptyFile(kind, file, 'unavailable', 'read_unavailable');
|
|
120
123
|
const rows = raw.split('\n').filter((line) => line.trim().length > 0);
|
|
@@ -123,6 +126,7 @@ function inspectFile(baseDir, kind, file, maxFileBytes) {
|
|
|
123
126
|
let duplicateRows = 0;
|
|
124
127
|
let corruptRows = 0;
|
|
125
128
|
let hashMismatchRows = 0;
|
|
129
|
+
let unverifiedRows = 0;
|
|
126
130
|
let schemaInvalidRows = 0;
|
|
127
131
|
for (const line of rows) {
|
|
128
132
|
try {
|
|
@@ -141,15 +145,22 @@ function inspectFile(baseDir, kind, file, maxFileBytes) {
|
|
|
141
145
|
duplicateRows += 1;
|
|
142
146
|
else
|
|
143
147
|
seen.add(assetId);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
const hashMatches = verifyAssetId(record);
|
|
149
|
+
if (!hashMatches) {
|
|
150
|
+
// A hash mismatch is corruption UNLESS provenance says this id is an unverified hub reuse: the hub
|
|
151
|
+
// rewrote the delivered bytes and reuse froze them untrusted on purpose (#570). That is expected, not
|
|
152
|
+
// store rot, so it lands in the benign unverifiedRows bucket and never degrades the store.
|
|
153
|
+
if (unverifiedContentIds.get(assetId) === computeAssetId(record))
|
|
154
|
+
unverifiedRows += 1;
|
|
155
|
+
else
|
|
156
|
+
hashMismatchRows += 1;
|
|
147
157
|
}
|
|
148
|
-
|
|
158
|
+
const schemaValid = kind === 'AntiGene' || validateWire(record).ok;
|
|
159
|
+
if (!schemaValid) {
|
|
149
160
|
schemaInvalidRows += 1;
|
|
150
|
-
continue;
|
|
151
161
|
}
|
|
152
|
-
|
|
162
|
+
if (hashMatches && schemaValid)
|
|
163
|
+
validRows += 1;
|
|
153
164
|
}
|
|
154
165
|
catch {
|
|
155
166
|
corruptRows += 1;
|
|
@@ -174,6 +185,7 @@ function inspectFile(baseDir, kind, file, maxFileBytes) {
|
|
|
174
185
|
duplicateRows,
|
|
175
186
|
corruptRows,
|
|
176
187
|
hashMismatchRows,
|
|
188
|
+
unverifiedRows,
|
|
177
189
|
schemaInvalidRows,
|
|
178
190
|
unterminated,
|
|
179
191
|
};
|
|
@@ -184,6 +196,33 @@ function inspectFile(baseDir, kind, file, maxFileBytes) {
|
|
|
184
196
|
return emptyFile(kind, file, 'unavailable', 'read_unavailable');
|
|
185
197
|
}
|
|
186
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Asset ids whose latest provenance decision marks a supported unverified frozen ingest.
|
|
201
|
+
* Read directly from `<baseDir>/provenance.jsonl` — NOT via ProvenanceStore — because inspectLocalAssetStore
|
|
202
|
+
* already holds the shared `.assetstore.lock`, and ProvenanceStore would try to re-acquire it. A missing or
|
|
203
|
+
* unreadable sidecar yields an empty map: without provenance every mismatch stays classified as corruption.
|
|
204
|
+
*/
|
|
205
|
+
function readUnverifiedReuseContentIds(baseDir, maxFileBytes) {
|
|
206
|
+
let raw;
|
|
207
|
+
try {
|
|
208
|
+
raw = readUtf8Regular(join(baseDir, 'provenance.jsonl'), maxFileBytes);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return new Map();
|
|
212
|
+
}
|
|
213
|
+
if (raw === null)
|
|
214
|
+
return new Map();
|
|
215
|
+
const ids = new Map();
|
|
216
|
+
for (const record of parseSidecarJsonl(raw, parseProvenanceRecord).records) {
|
|
217
|
+
const frozenContentId = record.frozenContentId;
|
|
218
|
+
if (frozenContentId && isActiveUnverifiedProvenance(record, frozenContentId)) {
|
|
219
|
+
ids.set(record.assetId, frozenContentId);
|
|
220
|
+
}
|
|
221
|
+
else
|
|
222
|
+
ids.delete(record.assetId);
|
|
223
|
+
}
|
|
224
|
+
return ids;
|
|
225
|
+
}
|
|
187
226
|
function inspectSidecar(baseDir, definition, maxFileBytes) {
|
|
188
227
|
const { kind, file, parseRecord } = definition;
|
|
189
228
|
try {
|
|
@@ -194,7 +233,7 @@ function inspectSidecar(baseDir, definition, maxFileBytes) {
|
|
|
194
233
|
if (stat.size > maxFileBytes) {
|
|
195
234
|
return { ...emptySidecar(kind, file, 'unavailable', 'scan_limit_exceeded'), bytes: stat.size };
|
|
196
235
|
}
|
|
197
|
-
const raw = readUtf8Regular(path);
|
|
236
|
+
const raw = readUtf8Regular(path, maxFileBytes);
|
|
198
237
|
if (raw === null)
|
|
199
238
|
return emptySidecar(kind, file, 'unavailable', 'read_unavailable');
|
|
200
239
|
const parsed = parseSidecarJsonl(raw, parseRecord);
|
|
@@ -255,11 +294,14 @@ export function inspectLocalAssetStore(baseDir, opts = {}, deps = {}) {
|
|
|
255
294
|
catch {
|
|
256
295
|
return summarize(allFiles('unavailable', 'lock_unavailable'), allSidecars('unavailable', 'lock_unavailable'));
|
|
257
296
|
}
|
|
258
|
-
const maxFileBytes = healthScanLimit(opts.maxFileBytes);
|
|
259
297
|
let report;
|
|
260
298
|
try {
|
|
299
|
+
const maxFileBytes = healthScanLimit(opts.maxFileBytes);
|
|
300
|
+
// Read the unverified-reuse set once, under the lock we already hold, so every asset file classifies its
|
|
301
|
+
// hash mismatches consistently against the same bounded provenance snapshot.
|
|
302
|
+
const unverifiedContentIds = readUnverifiedReuseContentIds(baseDir, maxFileBytes);
|
|
261
303
|
report = summarize(Object.entries(LOCAL_ASSET_FILES)
|
|
262
|
-
.map(([kind, file]) => inspectFile(baseDir, kind, file, maxFileBytes)), LOCAL_ASSET_SIDECARS.map((definition) => inspectSidecar(baseDir, definition, maxFileBytes)));
|
|
304
|
+
.map(([kind, file]) => inspectFile(baseDir, kind, file, maxFileBytes, unverifiedContentIds)), LOCAL_ASSET_SIDECARS.map((definition) => inspectSidecar(baseDir, definition, maxFileBytes)));
|
|
263
305
|
}
|
|
264
306
|
catch {
|
|
265
307
|
report = summarize(allFiles('unavailable', 'read_unavailable'), allSidecars('unavailable', 'read_unavailable'));
|
|
@@ -33,7 +33,7 @@ export declare function withAssetStoreLock<T>(lockPath: string, operation: () =>
|
|
|
33
33
|
export declare function assertOptionalRegularFile(path: string, role?: AssetStorePathRole): Stats | null;
|
|
34
34
|
export declare function regularFileFingerprint(path: string): string;
|
|
35
35
|
export declare function readRegularBuffer(path: string, maxBytes?: number): Buffer | null;
|
|
36
|
-
export declare function readUtf8Regular(path: string): string | null;
|
|
36
|
+
export declare function readUtf8Regular(path: string, maxBytes?: number): string | null;
|
|
37
37
|
export declare function createBufferDurableExclusive(path: string, value: Buffer, opts?: DurableWriteOptions): void;
|
|
38
38
|
export declare function fsyncDirectoryBestEffort(path: string): void;
|
|
39
39
|
export declare function appendUtf8Durable(path: string, value: string, opts?: DurableWriteOptions): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { closeSync, constants, fstatSync, fsyncSync, ftruncateSync, lstatSync, mkdirSync, openSync,
|
|
2
|
+
import { closeSync, constants, fstatSync, fsyncSync, ftruncateSync, lstatSync, mkdirSync, openSync, readSync, renameSync, unlinkSync, writeSync, } from 'node:fs';
|
|
3
3
|
import { basename, dirname, join } from 'node:path';
|
|
4
4
|
import { acquireLock, LockReleaseError, releaseLock, } from '../util/fileLock.js';
|
|
5
5
|
export class UnsafeAssetStorePathError extends Error {
|
|
@@ -152,17 +152,26 @@ export function readRegularBuffer(path, maxBytes = Number.MAX_SAFE_INTEGER) {
|
|
|
152
152
|
assertOpenedPathMatches(fd, path, 'asset_file');
|
|
153
153
|
if (fstatSync(fd).size > maxBytes)
|
|
154
154
|
throw new AssetStoreReadLimitError();
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
const chunks = [];
|
|
156
|
+
let total = 0;
|
|
157
|
+
while (total <= maxBytes) {
|
|
158
|
+
const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes - total + 1));
|
|
159
|
+
const bytesRead = readSync(fd, chunk, 0, chunk.byteLength, null);
|
|
160
|
+
if (bytesRead === 0)
|
|
161
|
+
break;
|
|
162
|
+
total += bytesRead;
|
|
163
|
+
if (total > maxBytes)
|
|
164
|
+
throw new AssetStoreReadLimitError();
|
|
165
|
+
chunks.push(bytesRead === chunk.byteLength ? chunk : chunk.subarray(0, bytesRead));
|
|
166
|
+
}
|
|
167
|
+
return Buffer.concat(chunks, total);
|
|
159
168
|
}
|
|
160
169
|
finally {
|
|
161
170
|
closeSync(fd);
|
|
162
171
|
}
|
|
163
172
|
}
|
|
164
|
-
export function readUtf8Regular(path) {
|
|
165
|
-
return readRegularBuffer(path)?.toString('utf8') ?? null;
|
|
173
|
+
export function readUtf8Regular(path, maxBytes = Number.MAX_SAFE_INTEGER) {
|
|
174
|
+
return readRegularBuffer(path, maxBytes)?.toString('utf8') ?? null;
|
|
166
175
|
}
|
|
167
176
|
function writeAll(fd, value) {
|
|
168
177
|
const bytes = typeof value === 'string' ? Buffer.from(value, 'utf8') : value;
|
|
@@ -237,10 +246,19 @@ export function appendUtf8Durable(path, value, opts = {}) {
|
|
|
237
246
|
const parent = dirname(path);
|
|
238
247
|
assertAssetStoreDirectory(parent);
|
|
239
248
|
const existed = assertOptionalRegularFile(path) !== null;
|
|
240
|
-
const fd = openNoFollow(path, constants.
|
|
249
|
+
const fd = openNoFollow(path, constants.O_RDWR | constants.O_APPEND | constants.O_CREAT, 0o600);
|
|
241
250
|
try {
|
|
242
251
|
assertOpenedPathMatches(fd, path, 'asset_file');
|
|
243
|
-
|
|
252
|
+
const stat = fstatSync(fd);
|
|
253
|
+
let needsLineSeparator = false;
|
|
254
|
+
if (stat.size > 0 && value.length > 0) {
|
|
255
|
+
const tail = Buffer.allocUnsafe(1);
|
|
256
|
+
const bytesRead = readSync(fd, tail, 0, 1, stat.size - 1);
|
|
257
|
+
if (bytesRead !== 1)
|
|
258
|
+
throw new Error('asset store tail read made no progress');
|
|
259
|
+
needsLineSeparator = tail[0] !== 0x0a;
|
|
260
|
+
}
|
|
261
|
+
writeAll(fd, needsLineSeparator ? `\n${value}` : value);
|
|
244
262
|
(opts.syncFile ?? fsyncSync)(fd);
|
|
245
263
|
}
|
|
246
264
|
finally {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { AssetKind } from './provider.js';
|
|
2
|
+
export declare const ASSET_SYNC_INVENTORY_MAX_SEGMENTS = 24;
|
|
3
|
+
export declare const ASSET_SYNC_INVENTORY_MAX_ITEMS_PER_SEGMENT = 10000;
|
|
4
|
+
export declare const ASSET_SYNC_INVENTORY_MAX_UNIQUE_ITEMS = 240000;
|
|
5
|
+
export declare const ASSET_SYNC_INVENTORY_MAX_TOTAL_BYTES: number;
|
|
2
6
|
export type AssetSyncSource = 'hub';
|
|
3
7
|
export type AssetSyncScope = 'purchased' | 'published';
|
|
4
8
|
export interface AssetSyncRecord {
|
|
@@ -8,25 +12,107 @@ export interface AssetSyncRecord {
|
|
|
8
12
|
scope: AssetSyncScope;
|
|
9
13
|
syncedAt: string;
|
|
10
14
|
remoteAssetId: string;
|
|
15
|
+
/** Opaque sync identity hash. Legacy records omit it and are not used for identity-scoped reconciliation. */
|
|
16
|
+
runKey?: string;
|
|
17
|
+
/** Parameter-independent remote inventory identity used for reconciliation across sync runs. */
|
|
18
|
+
inventoryKey?: string;
|
|
11
19
|
logicalId?: string;
|
|
12
20
|
status?: string;
|
|
13
21
|
forced?: true;
|
|
14
22
|
collisionWithAssetId?: string;
|
|
15
23
|
}
|
|
24
|
+
export type AssetSyncRunState = 'started' | 'progress' | 'completed';
|
|
25
|
+
export type AssetSyncRunOutcome = 'imported' | 'already_local' | 'failed' | 'remote_missing';
|
|
26
|
+
export interface AssetSyncRunRecord {
|
|
27
|
+
recordType: 'run';
|
|
28
|
+
runId: string;
|
|
29
|
+
runKey: string;
|
|
30
|
+
state: AssetSyncRunState;
|
|
31
|
+
remoteAssetId?: string;
|
|
32
|
+
outcome?: AssetSyncRunOutcome;
|
|
33
|
+
reason?: string;
|
|
34
|
+
/** Ordered candidate IDs selected when this run was started. */
|
|
35
|
+
plan?: readonly string[];
|
|
36
|
+
syncedAt: string;
|
|
37
|
+
}
|
|
38
|
+
export interface AssetSyncRunSnapshot extends AssetSyncRunRecord {
|
|
39
|
+
readonly processed: ReadonlyMap<string, AssetSyncRunRecord>;
|
|
40
|
+
}
|
|
41
|
+
export type AssetSyncInventoryOutcome = 'imported' | 'already_local' | 'blocked' | 'failed' | 'pending';
|
|
42
|
+
export interface AssetSyncInventoryCursorFingerprints {
|
|
43
|
+
readonly purchased: string | null;
|
|
44
|
+
readonly published: string | null;
|
|
45
|
+
}
|
|
46
|
+
export interface AssetSyncInventoryItem {
|
|
47
|
+
readonly remoteAssetId: string;
|
|
48
|
+
readonly outcome: AssetSyncInventoryOutcome;
|
|
49
|
+
}
|
|
50
|
+
export interface AssetSyncInventorySegmentRecord {
|
|
51
|
+
readonly recordType: 'inventory_scan';
|
|
52
|
+
readonly scanId: string;
|
|
53
|
+
readonly inventoryKey: string;
|
|
54
|
+
readonly scope: 'purchased' | 'published' | 'all';
|
|
55
|
+
readonly index: number;
|
|
56
|
+
readonly inputCursorFingerprints: AssetSyncInventoryCursorFingerprints;
|
|
57
|
+
readonly nextCursorFingerprints: AssetSyncInventoryCursorFingerprints;
|
|
58
|
+
readonly items: readonly AssetSyncInventoryItem[];
|
|
59
|
+
readonly anonymousBlocked: number;
|
|
60
|
+
/** This segment must be replayed from its input cursor before the scan may advance. */
|
|
61
|
+
readonly cursorHeld?: true;
|
|
62
|
+
/** Multi-segment batches are applied only after every physical segment is present. */
|
|
63
|
+
readonly batchId?: string;
|
|
64
|
+
readonly batchIndex?: number;
|
|
65
|
+
readonly batchSize?: number;
|
|
66
|
+
readonly syncedAt: string;
|
|
67
|
+
}
|
|
68
|
+
export type AssetSyncInventoryBatch = Omit<AssetSyncInventorySegmentRecord, 'recordType' | 'syncedAt' | 'batchId' | 'batchIndex' | 'batchSize'> & {
|
|
69
|
+
syncedAt?: string;
|
|
70
|
+
};
|
|
71
|
+
export interface AssetSyncInventorySnapshot {
|
|
72
|
+
readonly scanId: string;
|
|
73
|
+
readonly inventoryKey: string;
|
|
74
|
+
readonly scope: 'purchased' | 'published' | 'all';
|
|
75
|
+
readonly segmentCount: number;
|
|
76
|
+
readonly nextCursorFingerprints: AssetSyncInventoryCursorFingerprints;
|
|
77
|
+
readonly outcomes: ReadonlyMap<string, AssetSyncInventoryOutcome>;
|
|
78
|
+
readonly anonymousBlocked: number;
|
|
79
|
+
readonly complete: boolean;
|
|
80
|
+
/** Physical segment index whose input cursor must be replayed before this scan can advance. */
|
|
81
|
+
readonly retryIndex?: number;
|
|
82
|
+
}
|
|
16
83
|
export declare class AssetSyncLedger {
|
|
17
84
|
private readonly now;
|
|
18
85
|
private readonly path;
|
|
19
86
|
private readonly lockPath;
|
|
20
87
|
private readonly index;
|
|
88
|
+
private readonly runKeyIndex;
|
|
89
|
+
private readonly inventoryKeyIndex;
|
|
21
90
|
private fileState;
|
|
22
91
|
private loaded;
|
|
23
92
|
constructor(baseDir: string, now?: () => number);
|
|
24
93
|
append(rec: Omit<AssetSyncRecord, 'syncedAt'> & {
|
|
25
94
|
syncedAt?: string;
|
|
26
95
|
}): AssetSyncRecord;
|
|
96
|
+
appendRun(rec: Omit<AssetSyncRunRecord, 'recordType' | 'syncedAt'> & {
|
|
97
|
+
syncedAt?: string;
|
|
98
|
+
}): AssetSyncRunRecord;
|
|
99
|
+
appendInventorySegment(rec: Omit<AssetSyncInventorySegmentRecord, 'recordType' | 'syncedAt'> & {
|
|
100
|
+
syncedAt?: string;
|
|
101
|
+
}): AssetSyncInventorySegmentRecord | null;
|
|
102
|
+
appendInventoryBatch(rec: AssetSyncInventoryBatch): readonly AssetSyncInventorySegmentRecord[] | null;
|
|
103
|
+
replaceInventoryRetryBatch(rec: AssetSyncInventoryBatch): readonly AssetSyncInventorySegmentRecord[] | null;
|
|
104
|
+
clearInventoryScan(inventoryKey: string): void;
|
|
105
|
+
runRecords(runId: string): AssetSyncRunRecord[];
|
|
106
|
+
latestIncompleteRun(runKey: string): AssetSyncRunSnapshot | undefined;
|
|
107
|
+
latestInventoryScan(inventoryKey: string): AssetSyncInventorySnapshot | undefined;
|
|
27
108
|
get(assetId: string): AssetSyncRecord | null;
|
|
109
|
+
getForRunKey(runKey: string, assetId: string): AssetSyncRecord | null;
|
|
28
110
|
list(): AssetSyncRecord[];
|
|
111
|
+
listForRunKey(runKey: string): AssetSyncRecord[];
|
|
112
|
+
listForInventoryKey(inventoryKey: string): AssetSyncRecord[];
|
|
29
113
|
private rebuildIndex;
|
|
30
114
|
private refreshUnderLock;
|
|
31
115
|
private withFreshRead;
|
|
116
|
+
private readRunRecordsUnderLock;
|
|
117
|
+
private latestInventorySnapshotUnderLock;
|
|
32
118
|
}
|