@evomap/evolver-core 2.0.0-beta.1 → 2.0.0-beta.3
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/assets/gep/genes.jsonl +5 -0
- package/dist/algo/candidateAssembly.js +23 -14
- package/dist/algo/capabilityCandidates.d.ts +2 -0
- package/dist/algo/capabilityCandidates.js +5 -0
- package/dist/algo/conversationSniffer.d.ts +18 -0
- package/dist/algo/conversationSniffer.js +132 -0
- package/dist/algo/cycleEngine.d.ts +11 -0
- package/dist/algo/cycleEngine.js +14 -6
- package/dist/algo/cycleFailureClassifier.d.ts +1 -1
- package/dist/algo/cycleFailureClassifier.js +13 -5
- package/dist/algo/geneIntake.d.ts +14 -4
- package/dist/algo/geneIntake.js +37 -9
- package/dist/algo/geneSelection.d.ts +18 -1
- package/dist/algo/geneSelection.js +42 -18
- package/dist/algo/index.d.ts +2 -0
- package/dist/algo/index.js +2 -0
- package/dist/algo/memoryGraph.d.ts +62 -0
- package/dist/algo/memoryGraph.js +86 -0
- package/dist/algo/orchestrator.d.ts +3 -0
- package/dist/algo/orchestrator.js +14 -2
- package/dist/assetstore/assetSidecarRecords.d.ts +23 -0
- package/dist/assetstore/assetSidecarRecords.js +142 -0
- package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
- package/dist/assetstore/assetSidecarRecovery.js +288 -0
- package/dist/assetstore/assetStoreHealth.d.ts +75 -0
- package/dist/assetstore/assetStoreHealth.js +277 -0
- package/dist/assetstore/assetStoreLayout.d.ts +2 -0
- package/dist/assetstore/assetStoreLayout.js +6 -0
- package/dist/assetstore/assetStoreStorage.d.ts +42 -0
- package/dist/assetstore/assetStoreStorage.js +318 -0
- package/dist/assetstore/assetSyncLedger.d.ts +32 -0
- package/dist/assetstore/assetSyncLedger.js +66 -0
- package/dist/assetstore/index.d.ts +5 -1
- package/dist/assetstore/index.js +5 -1
- package/dist/assetstore/localJsonl.d.ts +10 -2
- package/dist/assetstore/localJsonl.js +93 -37
- package/dist/assetstore/pendingSignals.js +3 -1
- package/dist/assetstore/provenance.d.ts +20 -2
- package/dist/assetstore/provenance.js +92 -56
- package/dist/assetstore/provider.d.ts +2 -0
- package/dist/assetstore/reviewFilter.js +5 -2
- package/dist/assetstore/reviewLedger.d.ts +14 -2
- package/dist/assetstore/reviewLedger.js +78 -43
- package/dist/assetstore/seedGenes.d.ts +3 -0
- package/dist/assetstore/seedGenes.js +134 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +11 -1
- package/dist/benchmark/antiGeneImpact.d.ts +16 -0
- package/dist/benchmark/antiGeneImpact.js +34 -0
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +13 -1
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +2 -1
- package/dist/benchmark/triggerShift.d.ts +62 -0
- package/dist/benchmark/triggerShift.js +106 -0
- package/dist/events/eventArchive.d.ts +65 -0
- package/dist/events/eventArchive.js +343 -0
- package/dist/events/eventStore.js +2 -12
- package/dist/events/ingest.d.ts +1 -1
- package/dist/events/ingest.js +2 -0
- package/dist/events/paths.d.ts +1 -1
- package/dist/events/paths.js +2 -2
- package/dist/events/public.d.ts +3 -1
- package/dist/events/public.js +2 -1
- package/dist/events/retention.d.ts +24 -1
- package/dist/events/retention.js +133 -37
- package/dist/exec/autoExec.d.ts +6 -1
- package/dist/exec/autoExec.js +34 -0
- package/dist/exec/autonomousCycle.d.ts +2 -0
- package/dist/exec/autonomousCycle.js +5 -0
- package/dist/exec/claudeBridge.d.ts +7 -2
- package/dist/exec/claudeBridge.js +115 -14
- package/dist/exec/prompt.js +9 -0
- package/dist/exec/runnerRegistry.d.ts +64 -18
- package/dist/exec/runnerRegistry.js +341 -38
- package/dist/exec/selfPrObfuscation.d.ts +2 -1
- package/dist/exec/selfPrObfuscation.js +19 -6
- package/dist/hub/agentDirectory.d.ts +90 -0
- package/dist/hub/agentDirectory.js +104 -0
- package/dist/hub/bindings.js +23 -3
- package/dist/hub/capability.d.ts +14 -2
- package/dist/hub/fake.d.ts +4 -2
- package/dist/hub/fake.js +3 -2
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/mailbox/catalog.js +3 -0
- package/dist/mailbox/ipcServer.js +2 -2
- package/dist/mailbox/store.d.ts +14 -0
- package/dist/mailbox/store.js +44 -2
- package/dist/material/consumer.js +9 -6
- package/dist/material/index.d.ts +1 -0
- package/dist/material/index.js +1 -0
- package/dist/material/materialArchive.d.ts +81 -0
- package/dist/material/materialArchive.js +466 -0
- package/dist/material/materialStore.d.ts +1 -0
- package/dist/material/materialStore.js +6 -13
- package/dist/ops/savingsCore.js +1 -2
- package/dist/ops/selfUpdate.d.ts +10 -1
- package/dist/ops/selfUpdate.js +64 -15
- package/dist/schema/common.d.ts +1 -1
- package/dist/schema/common.js +1 -1
- package/dist/schema/material.d.ts +5 -5
- package/dist/trace/trajectoryExport.js +2 -2
- package/dist/util/fileLock.d.ts +19 -2
- package/dist/util/fileLock.js +166 -31
- package/dist/wire/geneHints.d.ts +42 -1
- package/dist/wire/geneHints.js +52 -1
- package/dist/wire/index.d.ts +14 -2
- package/dist/wire/index.js +1 -1
- package/package.json +6 -1
|
@@ -4,6 +4,8 @@ import { type GeneHealth } from './geneHealth.js';
|
|
|
4
4
|
import type { GeneLearningView } from '../assetstore/learningHistory.js';
|
|
5
5
|
import type { AssetRecord } from '../assetstore/provider.js';
|
|
6
6
|
import { type ExplorationInput } from './exploration.js';
|
|
7
|
+
import type { GenerationSource } from '../wire/index.js';
|
|
8
|
+
import type { MemoryGraphGeneEvidence } from './memoryGraph.js';
|
|
7
9
|
/** 一个候选 gene 的选择期素材. */
|
|
8
10
|
export interface GeneCandidateInput {
|
|
9
11
|
geneId: string;
|
|
@@ -34,6 +36,8 @@ export interface GeneCandidateInput {
|
|
|
34
36
|
* set the hard gates already produced, so a high confidence can never resurrect a gene that was excluded.
|
|
35
37
|
*/
|
|
36
38
|
confidence?: number;
|
|
39
|
+
/** Scoped local MemoryGraph outcome signal in [-1,1]. Data-only and never executable prompt content. */
|
|
40
|
+
memoryBoost?: number;
|
|
37
41
|
/**
|
|
38
42
|
* Cross-runtime reuse sentiment in [-1, 1] for this gene (#268 phase 1): the net of self-reported reuse
|
|
39
43
|
* SUCCESSes vs negatives (failed/mismatched/stale/unsafe), computed upstream from reuse-outcome events
|
|
@@ -43,6 +47,11 @@ export interface GeneCandidateInput {
|
|
|
43
47
|
* effect, so the factor is dormant until a caller injects the signal (default-off).
|
|
44
48
|
*/
|
|
45
49
|
reuseAdjust?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Authoritative provenance tag from Gene.generation_meta.source. The legacy `gene_distilled_` prefix remains a
|
|
52
|
+
* fallback only for old candidates that do not carry this field.
|
|
53
|
+
*/
|
|
54
|
+
generationSource?: GenerationSource;
|
|
46
55
|
}
|
|
47
56
|
export interface SelectionInput {
|
|
48
57
|
signals: readonly string[];
|
|
@@ -72,6 +81,8 @@ export interface SelectionInput {
|
|
|
72
81
|
* for prompt rendering only; they never enter scoring, fallback, or forced selection.
|
|
73
82
|
*/
|
|
74
83
|
antiWarnings?: readonly AntiWarning[];
|
|
84
|
+
/** Structured, scoped outcome evidence selected upstream. Contains no raw memory text or instructions. */
|
|
85
|
+
memoryEvidence?: readonly MemoryGraphGeneEvidence[];
|
|
75
86
|
}
|
|
76
87
|
export interface ScoredCandidate {
|
|
77
88
|
geneId: string;
|
|
@@ -99,6 +110,10 @@ export interface GeneDecision {
|
|
|
99
110
|
antiWarnings?: AntiWarning[];
|
|
100
111
|
weightsVersion: string;
|
|
101
112
|
strategyName: string;
|
|
113
|
+
/** Human-readable explanation for the winning candidate. */
|
|
114
|
+
selectedReason?: string;
|
|
115
|
+
/** Bounded structured outcome evidence for prompt enrichment. */
|
|
116
|
+
memoryEvidence?: MemoryGraphGeneEvidence[];
|
|
102
117
|
}
|
|
103
118
|
/**
|
|
104
119
|
* Weight of the preferred-gene confidence factor (fourth factor, positive cross-cycle learning). Kept small so
|
|
@@ -112,12 +127,14 @@ export declare const CONFIDENCE_WEIGHT = 0.15;
|
|
|
112
127
|
* ±REUSE_WEIGHT and can never dominate health/signal-match.
|
|
113
128
|
*/
|
|
114
129
|
export declare const REUSE_WEIGHT = 0.1;
|
|
130
|
+
/** Weight of scoped local MemoryGraph outcome evidence. */
|
|
131
|
+
export declare const MEMORY_GRAPH_WEIGHT = 0.12;
|
|
115
132
|
/**
|
|
116
133
|
* Version of the full engine-health weight vector (health 0.6 + signal-match 0.4 − epigenetic penalty
|
|
117
134
|
* + CONFIDENCE_WEIGHT × confidence + REUSE_WEIGHT × reuse-sentiment). Bumped whenever a factor is added so golden
|
|
118
135
|
* weight snapshots track the change. Composed from the health-weights version so a change to either layer shows.
|
|
119
136
|
*/
|
|
120
|
-
export declare const SELECTION_WEIGHTS_VERSION = "sel-
|
|
137
|
+
export declare const SELECTION_WEIGHTS_VERSION = "sel-4(gh-1,conf=0.15,memory=0.12,reuse=0.1)";
|
|
121
138
|
/** 实现1: engine 健康分主导(health 0.6 + 信号匹配 0.4). */
|
|
122
139
|
export declare const engineHealthSelection: Strategy<SelectionInput, GeneDecision>;
|
|
123
140
|
/** 实现2: 纯信号匹配采样(忽略 health, 对照基线 — 经验主义要可对比). */
|
|
@@ -4,9 +4,17 @@ import { tagOverlapScore, bagCosine } from '../signals/expand.js';
|
|
|
4
4
|
import { driftSelect } from './exploration.js';
|
|
5
5
|
import { isDistilledGeneId } from './geneIntake.js';
|
|
6
6
|
function decisionWithWarnings(input, decision) {
|
|
7
|
+
const selected = decision.selectedGeneId
|
|
8
|
+
? decision.candidates.find((candidate) => candidate.geneId === decision.selectedGeneId || candidate.assetId === decision.selectedAssetId)
|
|
9
|
+
: undefined;
|
|
10
|
+
const enriched = {
|
|
11
|
+
...decision,
|
|
12
|
+
...(selected ? { selectedReason: selected.reasons.join('; ') } : {}),
|
|
13
|
+
...(input.memoryEvidence && input.memoryEvidence.length > 0 ? { memoryEvidence: [...input.memoryEvidence] } : {}),
|
|
14
|
+
};
|
|
7
15
|
return input.antiWarnings && input.antiWarnings.length > 0
|
|
8
|
-
? { ...
|
|
9
|
-
:
|
|
16
|
+
? { ...enriched, antiWarnings: [...input.antiWarnings] }
|
|
17
|
+
: enriched;
|
|
10
18
|
}
|
|
11
19
|
function signalMatchScore(signals, match) {
|
|
12
20
|
if (signals.length === 0 || match.length === 0)
|
|
@@ -29,12 +37,14 @@ export const CONFIDENCE_WEIGHT = 0.15;
|
|
|
29
37
|
* ±REUSE_WEIGHT and can never dominate health/signal-match.
|
|
30
38
|
*/
|
|
31
39
|
export const REUSE_WEIGHT = 0.1;
|
|
40
|
+
/** Weight of scoped local MemoryGraph outcome evidence. */
|
|
41
|
+
export const MEMORY_GRAPH_WEIGHT = 0.12;
|
|
32
42
|
/**
|
|
33
43
|
* Version of the full engine-health weight vector (health 0.6 + signal-match 0.4 − epigenetic penalty
|
|
34
44
|
* + CONFIDENCE_WEIGHT × confidence + REUSE_WEIGHT × reuse-sentiment). Bumped whenever a factor is added so golden
|
|
35
45
|
* weight snapshots track the change. Composed from the health-weights version so a change to either layer shows.
|
|
36
46
|
*/
|
|
37
|
-
export const SELECTION_WEIGHTS_VERSION = `sel-
|
|
47
|
+
export const SELECTION_WEIGHTS_VERSION = `sel-4(${HEALTH_WEIGHTS_VERSION},conf=${CONFIDENCE_WEIGHT},memory=${MEMORY_GRAPH_WEIGHT},reuse=${REUSE_WEIGHT})`;
|
|
38
48
|
/**
|
|
39
49
|
* Match score with semantic signal expansion (ported from v1): literal coverage first, then fill the
|
|
40
50
|
* recall gap with semantic tag overlap, then with bag-of-words cosine similarity. A perfect literal match
|
|
@@ -65,13 +75,16 @@ function scoreCandidate(signals, c) {
|
|
|
65
75
|
// Cross-runtime reuse sentiment (#268 phase 1): clamped to [-1,1] so an upstream bug cannot turn the soft nudge
|
|
66
76
|
// into a dominant (or unbounded) term. Absent → 0 → no effect (default-off until a caller injects it).
|
|
67
77
|
const reuse = Math.max(-1, Math.min(1, c.reuseAdjust ?? 0));
|
|
68
|
-
const
|
|
78
|
+
const memory = Math.max(-1, Math.min(1, c.memoryBoost ?? 0));
|
|
79
|
+
const score = 0.6 * health.score + 0.4 * m.score - epi + CONFIDENCE_WEIGHT * conf + MEMORY_GRAPH_WEIGHT * memory + REUSE_WEIGHT * reuse;
|
|
69
80
|
const breakdown = m.tag > 0 || m.cos > 0 ? `(literal=${m.literal.toFixed(2)}+tag=${m.tag.toFixed(2)}+cos=${m.cos.toFixed(2)})` : '';
|
|
70
81
|
const reasons = [`health=${health.score.toFixed(3)}(succ=${health.successRate.toFixed(2)},reuse=${health.reuseCount})`, `信号匹配=${m.score.toFixed(2)}${breakdown}`];
|
|
71
82
|
if (epi > 0)
|
|
72
83
|
reasons.push(`epigenetic 环境抑制 -${epi.toFixed(2)}`);
|
|
73
84
|
if (conf > 0)
|
|
74
85
|
reasons.push(`preferred-gene confidence +${(CONFIDENCE_WEIGHT * conf).toFixed(3)} (conf=${conf.toFixed(2)})`);
|
|
86
|
+
if (memory !== 0)
|
|
87
|
+
reasons.push(`scoped memory-graph outcome ${memory >= 0 ? '+' : ''}${(MEMORY_GRAPH_WEIGHT * memory).toFixed(3)} (boost=${memory.toFixed(2)})`);
|
|
75
88
|
if (reuse !== 0)
|
|
76
89
|
reasons.push(`cross-runtime reuse ${reuse >= 0 ? '+' : ''}${(REUSE_WEIGHT * reuse).toFixed(3)} (sentiment=${reuse.toFixed(2)})`);
|
|
77
90
|
return { geneId: c.geneId, ...(c.assetId ? { assetId: c.assetId } : {}), score, health, reasons };
|
|
@@ -88,6 +101,14 @@ function matchesScoredIdentity(a, b) {
|
|
|
88
101
|
function withoutCandidateIdentity(scored, c) {
|
|
89
102
|
return scored.filter((s) => !matchesCandidateIdentity(s, c));
|
|
90
103
|
}
|
|
104
|
+
function isReusableGenerationSource(source) {
|
|
105
|
+
return source === 'distilled' || source === 'evolved';
|
|
106
|
+
}
|
|
107
|
+
function isReusableFallbackCandidate(c) {
|
|
108
|
+
if (c.generationSource !== undefined)
|
|
109
|
+
return isReusableGenerationSource(c.generationSource);
|
|
110
|
+
return isDistilledGeneId(c.geneId);
|
|
111
|
+
}
|
|
91
112
|
function forcedSelection(input, scored, scoreForcedFallback, floor) {
|
|
92
113
|
const forcedGeneId = input.forcedGeneId?.trim();
|
|
93
114
|
if (!forcedGeneId)
|
|
@@ -116,7 +137,7 @@ function forcedSelection(input, scored, scoreForcedFallback, floor) {
|
|
|
116
137
|
forcedFallback.reasons.push('forced gene rejected: epigenetic suppression');
|
|
117
138
|
return { scoredForChoice: scoredCandidatesOnly, forceRejected: true };
|
|
118
139
|
}
|
|
119
|
-
if (!
|
|
140
|
+
if (!isReusableFallbackCandidate(fallback)) {
|
|
120
141
|
forcedFallback.reasons.push('forced gene rejected: suppressed fallback');
|
|
121
142
|
return { scoredForChoice: scoredCandidatesOnly, forceRejected: true };
|
|
122
143
|
}
|
|
@@ -141,26 +162,29 @@ export const engineHealthSelection = {
|
|
|
141
162
|
let selectedAssetId = forced.selectedAssetId ?? chosen?.assetId;
|
|
142
163
|
if (!selectedGeneId) {
|
|
143
164
|
// Issue #97 (ported from v1 selector.js): no candidate matched any live signal (every gene scored <= 0). Rather
|
|
144
|
-
// than fall straight through to a blind innovate, reuse a *distilled* gene if one is available.
|
|
145
|
-
//
|
|
146
|
-
// evolved
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
165
|
+
// than fall straight through to a blind innovate, reuse a *distilled* (or evolved) gene if one is available.
|
|
166
|
+
// The fallback pool is built by candidateAssembly, which now filters by generation_meta.source ∈ {distilled,
|
|
167
|
+
// evolved} (V1 #302), falling back to the `gene_distilled_` id namespace for legacy genes. The id-prefix check
|
|
168
|
+
// below is a defense-in-depth filter for callers that bypass candidateAssembly; the authoritative provenance
|
|
169
|
+
// lives on generation_meta (see geneGenerationSource). The safety rests on the upstream gates (trust #30 /
|
|
170
|
+
// review #89-91 / signal-ban / inert-ban #195) plus the epigenetic skip below, not on a skill-provenance
|
|
171
|
+
// low-blast-radius assumption. We skip any distilled fallback epigenetically suppressed in this env; v2's
|
|
172
|
+
// event-log-derived epigeneticPenalty is the analog of v1's asset-mark hard suppression (related band, not the
|
|
173
|
+
// identical predicate). No usable distilled fallback → stay null so the caller still innovates (the contract).
|
|
153
174
|
//
|
|
154
175
|
// v1 #97 parity: the distilled fallback is a *no-signal-match* last resort — it must fire only when NOTHING
|
|
155
176
|
// scored above zero, NOT merely when nothing cleared a positive `floor`. Otherwise a real but weak signal match
|
|
156
177
|
// (0 < score <= floor) would be preempted by an unrelated zero-evidence distilled gene; V1 (which has no floor)
|
|
157
178
|
// keeps such a match. When candidates scored > 0 but below floor, defer to V2's floor policy (innovate).
|
|
158
179
|
const noSignalMatch = !forced.forceRejected && forced.scoredForChoice.every((s) => s.score <= 0);
|
|
159
|
-
const
|
|
160
|
-
? (input.distilledFallback ?? [])
|
|
180
|
+
const fallback = noSignalMatch
|
|
181
|
+
? (input.distilledFallback ?? [])
|
|
182
|
+
.filter((candidate) => isReusableFallbackCandidate(candidate) && (candidate.epigeneticPenalty ?? 0) === 0)
|
|
183
|
+
.map((candidate) => ({ candidate, scored: scoreCandidate(input.signals, candidate) }))
|
|
184
|
+
.sort((left, right) => right.scored.score - left.scored.score || left.candidate.geneId.localeCompare(right.candidate.geneId))[0]
|
|
161
185
|
: undefined;
|
|
162
|
-
if (
|
|
163
|
-
const fbScored =
|
|
186
|
+
if (fallback) {
|
|
187
|
+
const { candidate: fb, scored: fbScored } = fallback;
|
|
164
188
|
fbScored.reasons.push('distilled_fallback(#97): 无信号匹配, 低置信度复用蒸馏 gene');
|
|
165
189
|
scored.push(fbScored);
|
|
166
190
|
selectedGeneId = fb.geneId;
|
package/dist/algo/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ export * from './geneHealth.js';
|
|
|
2
2
|
export * from './exploration.js';
|
|
3
3
|
export * from './epigenetics.js';
|
|
4
4
|
export * from './confidence.js';
|
|
5
|
+
export * from './memoryGraph.js';
|
|
5
6
|
export * from './capabilityCandidates.js';
|
|
7
|
+
export * from './conversationSniffer.js';
|
|
6
8
|
export * from './geneIntake.js';
|
|
7
9
|
export * from './candidateAssembly.js';
|
|
8
10
|
export * from './genePromotion.js';
|
package/dist/algo/index.js
CHANGED
|
@@ -2,7 +2,9 @@ export * from './geneHealth.js';
|
|
|
2
2
|
export * from './exploration.js';
|
|
3
3
|
export * from './epigenetics.js';
|
|
4
4
|
export * from './confidence.js';
|
|
5
|
+
export * from './memoryGraph.js';
|
|
5
6
|
export * from './capabilityCandidates.js';
|
|
7
|
+
export * from './conversationSniffer.js';
|
|
6
8
|
export * from './geneIntake.js';
|
|
7
9
|
export * from './candidateAssembly.js';
|
|
8
10
|
export * from './genePromotion.js';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type MemoryGraphOutcomeStatus = 'success' | 'failed';
|
|
2
|
+
export interface MemoryGraphOutcomeRecord {
|
|
3
|
+
version: 2;
|
|
4
|
+
kind: 'outcome';
|
|
5
|
+
provenance: 'v2_local' | 'v1_import';
|
|
6
|
+
workspaceScope: string;
|
|
7
|
+
userScope: string;
|
|
8
|
+
signalFingerprint: string;
|
|
9
|
+
signals: readonly string[];
|
|
10
|
+
geneId: string;
|
|
11
|
+
status: MemoryGraphOutcomeStatus;
|
|
12
|
+
score: number;
|
|
13
|
+
at: string;
|
|
14
|
+
/** Stable local-source identity used to make one-time imports crash-retry safe. */
|
|
15
|
+
sourceFingerprint?: string;
|
|
16
|
+
successCount?: number;
|
|
17
|
+
failCount?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface MemoryGraphGeneEvidence {
|
|
20
|
+
geneId: string;
|
|
21
|
+
boost: number;
|
|
22
|
+
expectedSuccess: number;
|
|
23
|
+
successCount: number;
|
|
24
|
+
failCount: number;
|
|
25
|
+
attempts: number;
|
|
26
|
+
similarity: number;
|
|
27
|
+
lastAt: string;
|
|
28
|
+
}
|
|
29
|
+
export interface MemoryGraphDiagnostics {
|
|
30
|
+
bytesRead: number;
|
|
31
|
+
recordsRead: number;
|
|
32
|
+
corruptLines: number;
|
|
33
|
+
oversizedLines: number;
|
|
34
|
+
scopeRejected: number;
|
|
35
|
+
provenanceRejected: number;
|
|
36
|
+
truncated: boolean;
|
|
37
|
+
recovery: 'healthy' | 'degraded' | 'recovered' | 'empty';
|
|
38
|
+
/** The graph was healthy enough to address, but another process held its maintenance lock. */
|
|
39
|
+
busy?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface MemoryGraphAdvice {
|
|
42
|
+
genes: readonly MemoryGraphGeneEvidence[];
|
|
43
|
+
diagnostics: MemoryGraphDiagnostics;
|
|
44
|
+
}
|
|
45
|
+
export interface MemoryGraphQueryInput {
|
|
46
|
+
workspace: string;
|
|
47
|
+
signals: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
export interface MemoryGraphRecordInput extends MemoryGraphQueryInput {
|
|
50
|
+
geneId: string;
|
|
51
|
+
status: MemoryGraphOutcomeStatus;
|
|
52
|
+
score: number;
|
|
53
|
+
at: string;
|
|
54
|
+
}
|
|
55
|
+
export interface MemoryGraphProvider {
|
|
56
|
+
query(input: MemoryGraphQueryInput): Promise<MemoryGraphAdvice> | MemoryGraphAdvice;
|
|
57
|
+
recordOutcome(input: MemoryGraphRecordInput): Promise<void> | void;
|
|
58
|
+
}
|
|
59
|
+
export declare function normalizeMemorySignals(signals: readonly string[]): string[];
|
|
60
|
+
export declare function memorySignalFingerprint(signals: readonly string[]): string;
|
|
61
|
+
export declare function safeMemoryGeneId(value: string): string;
|
|
62
|
+
export declare function deriveMemoryGraphAdvice(records: readonly MemoryGraphOutcomeRecord[], signals: readonly string[], nowMs: number, diagnostics: MemoryGraphDiagnostics): MemoryGraphAdvice;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const MAX_SIGNALS = 32;
|
|
2
|
+
const MAX_SIGNAL_CHARS = 120;
|
|
3
|
+
const MAX_GENE_CHARS = 240;
|
|
4
|
+
const MIN_SIMILARITY = 0.34;
|
|
5
|
+
const HALF_LIFE_DAYS = 30;
|
|
6
|
+
export function normalizeMemorySignals(signals) {
|
|
7
|
+
return [...new Set(signals
|
|
8
|
+
.filter((signal) => typeof signal === 'string')
|
|
9
|
+
.map((signal) => signal.trim().toLowerCase().slice(0, MAX_SIGNAL_CHARS))
|
|
10
|
+
.filter(Boolean))]
|
|
11
|
+
.sort()
|
|
12
|
+
.slice(0, MAX_SIGNALS);
|
|
13
|
+
}
|
|
14
|
+
export function memorySignalFingerprint(signals) {
|
|
15
|
+
return normalizeMemorySignals(signals).join('|') || '(none)';
|
|
16
|
+
}
|
|
17
|
+
export function safeMemoryGeneId(value) {
|
|
18
|
+
return value.trim().slice(0, MAX_GENE_CHARS);
|
|
19
|
+
}
|
|
20
|
+
export function deriveMemoryGraphAdvice(records, signals, nowMs, diagnostics) {
|
|
21
|
+
const currentSignals = normalizeMemorySignals(signals);
|
|
22
|
+
const aggregates = new Map();
|
|
23
|
+
for (const record of records) {
|
|
24
|
+
const similarity = jaccard(currentSignals, record.signals);
|
|
25
|
+
if (similarity < MIN_SIMILARITY)
|
|
26
|
+
continue;
|
|
27
|
+
const successCount = boundedCount(record.successCount ?? (record.status === 'success' ? 1 : 0));
|
|
28
|
+
const failCount = boundedCount(record.failCount ?? (record.status === 'failed' ? 1 : 0));
|
|
29
|
+
const total = successCount + failCount;
|
|
30
|
+
if (total === 0)
|
|
31
|
+
continue;
|
|
32
|
+
const decay = decayWeight(record.at, nowMs);
|
|
33
|
+
const weight = similarity * decay;
|
|
34
|
+
const current = aggregates.get(record.geneId) ?? { success: 0, fail: 0, weightedSimilarity: 0, weight: 0, lastAt: record.at };
|
|
35
|
+
current.success += successCount * weight;
|
|
36
|
+
current.fail += failCount * weight;
|
|
37
|
+
current.weightedSimilarity += similarity * total;
|
|
38
|
+
current.weight += total;
|
|
39
|
+
if (Date.parse(record.at) > Date.parse(current.lastAt))
|
|
40
|
+
current.lastAt = record.at;
|
|
41
|
+
aggregates.set(record.geneId, current);
|
|
42
|
+
}
|
|
43
|
+
const genes = [];
|
|
44
|
+
for (const [geneId, aggregate] of aggregates) {
|
|
45
|
+
const successCount = Math.round(aggregate.success);
|
|
46
|
+
const failCount = Math.round(aggregate.fail);
|
|
47
|
+
const attempts = successCount + failCount;
|
|
48
|
+
if (attempts === 0)
|
|
49
|
+
continue;
|
|
50
|
+
const expectedSuccess = (aggregate.success + 1) / (aggregate.success + aggregate.fail + 2);
|
|
51
|
+
const similarity = aggregate.weight > 0 ? Math.min(1, aggregate.weightedSimilarity / aggregate.weight) : 0;
|
|
52
|
+
const confidence = Math.min(1, Math.log2(attempts + 1) / 3);
|
|
53
|
+
const boost = clamp((expectedSuccess - 0.5) * 2 * similarity * confidence, -1, 1);
|
|
54
|
+
genes.push({ geneId, boost, expectedSuccess, successCount, failCount, attempts, similarity, lastAt: aggregate.lastAt });
|
|
55
|
+
}
|
|
56
|
+
genes.sort((left, right) => Math.abs(right.boost) - Math.abs(left.boost) || right.attempts - left.attempts || left.geneId.localeCompare(right.geneId));
|
|
57
|
+
return { genes: genes.slice(0, 64), diagnostics };
|
|
58
|
+
}
|
|
59
|
+
function boundedCount(value) {
|
|
60
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
61
|
+
return 0;
|
|
62
|
+
return Math.min(1_000_000, Math.floor(value));
|
|
63
|
+
}
|
|
64
|
+
function jaccard(left, right) {
|
|
65
|
+
const a = new Set(normalizeMemorySignals(left));
|
|
66
|
+
const b = new Set(normalizeMemorySignals(right));
|
|
67
|
+
if (a.size === 0 && b.size === 0)
|
|
68
|
+
return 1;
|
|
69
|
+
if (a.size === 0 || b.size === 0)
|
|
70
|
+
return 0;
|
|
71
|
+
let intersection = 0;
|
|
72
|
+
for (const value of a)
|
|
73
|
+
if (b.has(value))
|
|
74
|
+
intersection += 1;
|
|
75
|
+
return intersection / (a.size + b.size - intersection);
|
|
76
|
+
}
|
|
77
|
+
function decayWeight(at, nowMs) {
|
|
78
|
+
const timestamp = Date.parse(at);
|
|
79
|
+
if (!Number.isFinite(timestamp))
|
|
80
|
+
return 0;
|
|
81
|
+
const ageDays = Math.max(0, nowMs - timestamp) / 86_400_000;
|
|
82
|
+
return Math.pow(0.5, ageDays / HALF_LIFE_DAYS);
|
|
83
|
+
}
|
|
84
|
+
function clamp(value, min, max) {
|
|
85
|
+
return Math.max(min, Math.min(max, value));
|
|
86
|
+
}
|
|
@@ -7,6 +7,7 @@ import type { GeneCandidateInput } from './geneSelection.js';
|
|
|
7
7
|
import { CycleEngine, type CycleInput, type CycleResult, type SolidifyPermitGate } from './cycleEngine.js';
|
|
8
8
|
import { type PendingSignalsContext } from '../assetstore/pendingSignals.js';
|
|
9
9
|
import { type ReuseOutcomeSummary, type ReuseOutcomeEvent } from '../ops/reuseOutcomes.js';
|
|
10
|
+
import type { MemoryGraphAdvice } from './memoryGraph.js';
|
|
10
11
|
export interface RunCycleOptions {
|
|
11
12
|
cycleId: string;
|
|
12
13
|
problem: ProblemPattern;
|
|
@@ -60,6 +61,8 @@ export interface RunCycleOptions {
|
|
|
60
61
|
* agent self-report) gains teeth on selection. Omit → no recall contribution. Never feeds quarantine.
|
|
61
62
|
*/
|
|
62
63
|
recallEvents?: readonly ReuseOutcomeEvent[];
|
|
64
|
+
/** Scoped local MemoryGraph query result. Omit to keep selection unchanged. */
|
|
65
|
+
memoryGraphAdvice?: MemoryGraphAdvice;
|
|
63
66
|
}
|
|
64
67
|
/** Drive one full evolution cycle end-to-end: assemble candidates from the store, then run the cycle. */
|
|
65
68
|
export declare function runEvolutionCycle(engine: CycleEngine, store: AssetStoreProvider, opts: RunCycleOptions): Promise<CycleResult>;
|
|
@@ -38,7 +38,18 @@ export async function runEvolutionCycle(engine, store, opts) {
|
|
|
38
38
|
...(opts.hubCandidates && opts.hubCandidates.length > 0 ? { hubCandidates: opts.hubCandidates } : {}),
|
|
39
39
|
...(reuseCounts.size > 0 ? { reuseCounts } : {}),
|
|
40
40
|
};
|
|
41
|
-
const { candidates, distilledFallback, antiWarnings } = await assembleSelectionPool(store, signals, asmOpts);
|
|
41
|
+
const { candidates: assembledCandidates, distilledFallback, antiWarnings } = await assembleSelectionPool(store, signals, asmOpts);
|
|
42
|
+
const memoryByGene = new Map((opts.memoryGraphAdvice?.genes ?? []).map((evidence) => [evidence.geneId, evidence]));
|
|
43
|
+
const candidates = assembledCandidates.map((candidate) => {
|
|
44
|
+
const evidence = memoryByGene.get(candidate.geneId) ?? (candidate.assetId ? memoryByGene.get(candidate.assetId) : undefined);
|
|
45
|
+
return evidence ? { ...candidate, memoryBoost: evidence.boost } : candidate;
|
|
46
|
+
});
|
|
47
|
+
const memoryDistilledFallback = distilledFallback.map((candidate) => {
|
|
48
|
+
const evidence = memoryByGene.get(candidate.geneId) ?? (candidate.assetId ? memoryByGene.get(candidate.assetId) : undefined);
|
|
49
|
+
return evidence ? { ...candidate, memoryBoost: evidence.boost } : candidate;
|
|
50
|
+
});
|
|
51
|
+
const memoryEligible = [...candidates, ...memoryDistilledFallback];
|
|
52
|
+
const memoryEvidence = opts.memoryGraphAdvice?.genes.filter((evidence) => memoryEligible.some((candidate) => candidate.geneId === evidence.geneId || candidate.assetId === evidence.geneId)).slice(0, 3) ?? [];
|
|
42
53
|
return engine.runCycle({
|
|
43
54
|
cycleId: opts.cycleId,
|
|
44
55
|
problem: opts.problem,
|
|
@@ -57,7 +68,8 @@ export async function runEvolutionCycle(engine, store, opts) {
|
|
|
57
68
|
...(opts.solidifyPermit ? { solidifyPermit: opts.solidifyPermit } : {}),
|
|
58
69
|
// #97: forward the distilled-gene fallback pool so a no-signal-match cycle reuses a distilled strategy
|
|
59
70
|
// (instead of a blind innovate) when nothing clears the floor.
|
|
60
|
-
...(
|
|
71
|
+
...(memoryDistilledFallback.length > 0 ? { distilledFallback: memoryDistilledFallback } : {}),
|
|
61
72
|
...(antiWarnings.length > 0 ? { antiWarnings } : {}),
|
|
73
|
+
...(memoryEvidence.length > 0 ? { memoryEvidence } : {}),
|
|
62
74
|
});
|
|
63
75
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AssetSyncRecord } from './assetSyncLedger.js';
|
|
2
|
+
import type { ProvenanceRecord } from './provenance.js';
|
|
3
|
+
import type { ReviewRecord } from './reviewLedger.js';
|
|
4
|
+
export type AssetSidecarKind = 'provenance' | 'review' | 'asset-sync';
|
|
5
|
+
export type AssetSidecarCorruptionReason = 'invalid_row' | 'unterminated';
|
|
6
|
+
export interface ParsedSidecarJsonl<T> {
|
|
7
|
+
records: T[];
|
|
8
|
+
rows: number;
|
|
9
|
+
validRows: number;
|
|
10
|
+
corruptRows: number;
|
|
11
|
+
unterminated: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare class CorruptAssetSidecarError extends Error {
|
|
14
|
+
readonly sidecar: AssetSidecarKind;
|
|
15
|
+
readonly reason: AssetSidecarCorruptionReason;
|
|
16
|
+
readonly code = "CORRUPT_ASSET_SIDECAR";
|
|
17
|
+
constructor(sidecar: AssetSidecarKind, reason: AssetSidecarCorruptionReason);
|
|
18
|
+
}
|
|
19
|
+
export declare function parseSidecarJsonl<T>(raw: string, parseRecord: (value: unknown) => T | null): ParsedSidecarJsonl<T>;
|
|
20
|
+
export declare function assertTrustSidecarHealthy<T>(sidecar: Extract<AssetSidecarKind, 'provenance' | 'review'>, parsed: ParsedSidecarJsonl<T>): void;
|
|
21
|
+
export declare function parseProvenanceRecord(value: unknown): ProvenanceRecord | null;
|
|
22
|
+
export declare function parseReviewRecord(value: unknown): ReviewRecord | null;
|
|
23
|
+
export declare function parseAssetSyncRecord(value: unknown): AssetSyncRecord | null;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
export class CorruptAssetSidecarError extends Error {
|
|
2
|
+
sidecar;
|
|
3
|
+
reason;
|
|
4
|
+
code = 'CORRUPT_ASSET_SIDECAR';
|
|
5
|
+
constructor(sidecar, reason) {
|
|
6
|
+
super(`corrupt asset sidecar: ${sidecar}/${reason}`);
|
|
7
|
+
this.sidecar = sidecar;
|
|
8
|
+
this.reason = reason;
|
|
9
|
+
this.name = 'CorruptAssetSidecarError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function parseSidecarJsonl(raw, parseRecord) {
|
|
13
|
+
const records = [];
|
|
14
|
+
let rows = 0;
|
|
15
|
+
let corruptRows = 0;
|
|
16
|
+
for (const line of raw.split('\n')) {
|
|
17
|
+
if (!line.trim())
|
|
18
|
+
continue;
|
|
19
|
+
rows += 1;
|
|
20
|
+
try {
|
|
21
|
+
const record = parseRecord(JSON.parse(line));
|
|
22
|
+
if (record)
|
|
23
|
+
records.push(record);
|
|
24
|
+
else
|
|
25
|
+
corruptRows += 1;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
corruptRows += 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
records,
|
|
33
|
+
rows,
|
|
34
|
+
validRows: records.length,
|
|
35
|
+
corruptRows,
|
|
36
|
+
unterminated: raw.length > 0 && !raw.endsWith('\n'),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function assertTrustSidecarHealthy(sidecar, parsed) {
|
|
40
|
+
if (parsed.unterminated)
|
|
41
|
+
throw new CorruptAssetSidecarError(sidecar, 'unterminated');
|
|
42
|
+
if (parsed.corruptRows > 0)
|
|
43
|
+
throw new CorruptAssetSidecarError(sidecar, 'invalid_row');
|
|
44
|
+
}
|
|
45
|
+
export function parseProvenanceRecord(value) {
|
|
46
|
+
const record = objectRecord(value);
|
|
47
|
+
if (!record)
|
|
48
|
+
return null;
|
|
49
|
+
const assetId = stringField(record, 'assetId');
|
|
50
|
+
const source = stringField(record, 'source');
|
|
51
|
+
const at = stringField(record, 'at');
|
|
52
|
+
if (!assetId
|
|
53
|
+
|| (source !== 'local' && source !== 'migrated' && source !== 'hub')
|
|
54
|
+
|| typeof record['trusted'] !== 'boolean'
|
|
55
|
+
|| !at
|
|
56
|
+
|| Number.isNaN(Date.parse(at))) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const decision = record['decision'] === 'promoted' || record['decision'] === 'revoked'
|
|
60
|
+
? record['decision']
|
|
61
|
+
: undefined;
|
|
62
|
+
const decidedBy = stringField(record, 'decidedBy');
|
|
63
|
+
const promotedBy = stringField(record, 'promotedBy');
|
|
64
|
+
const reason = stringField(record, 'reason');
|
|
65
|
+
return {
|
|
66
|
+
assetId,
|
|
67
|
+
source,
|
|
68
|
+
trusted: record['trusted'],
|
|
69
|
+
at,
|
|
70
|
+
...(decision ? { decision } : {}),
|
|
71
|
+
...(decidedBy ? { decidedBy } : {}),
|
|
72
|
+
...(promotedBy ? { promotedBy } : {}),
|
|
73
|
+
...(reason ? { reason } : {}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function parseReviewRecord(value) {
|
|
77
|
+
const record = objectRecord(value);
|
|
78
|
+
if (!record)
|
|
79
|
+
return null;
|
|
80
|
+
const assetId = stringField(record, 'assetId');
|
|
81
|
+
const state = stringField(record, 'state');
|
|
82
|
+
const at = stringField(record, 'at');
|
|
83
|
+
if (!assetId
|
|
84
|
+
|| (state !== 'quarantined' && state !== 'approved' && state !== 'rejected')
|
|
85
|
+
|| !at
|
|
86
|
+
|| Number.isNaN(Date.parse(at))) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const by = stringField(record, 'by');
|
|
90
|
+
const reason = stringField(record, 'reason');
|
|
91
|
+
return {
|
|
92
|
+
assetId,
|
|
93
|
+
state,
|
|
94
|
+
at,
|
|
95
|
+
...(by ? { by } : {}),
|
|
96
|
+
...(reason ? { reason } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function parseAssetSyncRecord(value) {
|
|
100
|
+
const record = objectRecord(value);
|
|
101
|
+
if (!record)
|
|
102
|
+
return null;
|
|
103
|
+
const assetId = stringField(record, 'assetId');
|
|
104
|
+
const type = stringField(record, 'type');
|
|
105
|
+
const source = stringField(record, 'source');
|
|
106
|
+
const scope = stringField(record, 'scope');
|
|
107
|
+
const syncedAt = stringField(record, 'syncedAt');
|
|
108
|
+
const remoteAssetId = stringField(record, 'remoteAssetId');
|
|
109
|
+
if (!assetId
|
|
110
|
+
|| (type !== 'Gene' && type !== 'Capsule')
|
|
111
|
+
|| source !== 'hub'
|
|
112
|
+
|| (scope !== 'purchased' && scope !== 'published')
|
|
113
|
+
|| !syncedAt
|
|
114
|
+
|| !remoteAssetId) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const logicalId = stringField(record, 'logicalId');
|
|
118
|
+
const status = stringField(record, 'status');
|
|
119
|
+
const forced = record['forced'] === true;
|
|
120
|
+
const collisionWithAssetId = stringField(record, 'collisionWithAssetId');
|
|
121
|
+
return {
|
|
122
|
+
assetId,
|
|
123
|
+
type,
|
|
124
|
+
source,
|
|
125
|
+
scope,
|
|
126
|
+
syncedAt,
|
|
127
|
+
remoteAssetId,
|
|
128
|
+
...(logicalId ? { logicalId } : {}),
|
|
129
|
+
...(status ? { status } : {}),
|
|
130
|
+
...(forced ? { forced: true } : {}),
|
|
131
|
+
...(collisionWithAssetId ? { collisionWithAssetId } : {}),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function objectRecord(value) {
|
|
135
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
136
|
+
? value
|
|
137
|
+
: null;
|
|
138
|
+
}
|
|
139
|
+
function stringField(value, key) {
|
|
140
|
+
const raw = value[key];
|
|
141
|
+
return typeof raw === 'string' && raw.trim() ? raw.trim() : undefined;
|
|
142
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type ReleaseLockReason } from '../util/fileLock.js';
|
|
2
|
+
import { type AssetStoreLockDeps } from './assetStoreStorage.js';
|
|
3
|
+
import { type AssetSidecarKind } from './assetSidecarRecords.js';
|
|
4
|
+
export type { AssetSidecarKind } from './assetSidecarRecords.js';
|
|
5
|
+
export type AssetSidecarRecoveryFailureReason = 'sidecar_missing' | 'sidecar_not_degraded' | 'replacement_missing' | 'replacement_too_large' | 'replacement_invalid_utf8' | 'replacement_invalid' | 'replacement_unterminated' | 'valid_history_missing' | 'corrective_record_required' | 'unsafe_corrective_record' | 'acknowledgement_required' | 'sidecar_too_large' | 'sidecar_changed' | 'replacement_changed' | 'backup_conflict';
|
|
6
|
+
export declare class AssetSidecarRecoveryError extends Error {
|
|
7
|
+
readonly reason: AssetSidecarRecoveryFailureReason;
|
|
8
|
+
readonly code = "ASSET_SIDECAR_RECOVERY_FAILED";
|
|
9
|
+
constructor(reason: AssetSidecarRecoveryFailureReason);
|
|
10
|
+
}
|
|
11
|
+
export interface AssetSidecarRecoveryFileReport {
|
|
12
|
+
rows: number;
|
|
13
|
+
validRows: number;
|
|
14
|
+
corruptRows: number;
|
|
15
|
+
unterminated: boolean;
|
|
16
|
+
digest: string;
|
|
17
|
+
}
|
|
18
|
+
export interface AssetSidecarReplacementReport extends AssetSidecarRecoveryFileReport {
|
|
19
|
+
preservedValidRows: number;
|
|
20
|
+
addedRecords: number;
|
|
21
|
+
correctiveRecords: number;
|
|
22
|
+
}
|
|
23
|
+
export interface AssetSidecarRecoveryReport {
|
|
24
|
+
mode: 'preview' | 'write';
|
|
25
|
+
sidecar: AssetSidecarKind;
|
|
26
|
+
changed: boolean;
|
|
27
|
+
wouldWrite: boolean;
|
|
28
|
+
acknowledgementRequired: boolean;
|
|
29
|
+
current: AssetSidecarRecoveryFileReport;
|
|
30
|
+
replacement: AssetSidecarReplacementReport;
|
|
31
|
+
backupId?: string;
|
|
32
|
+
lockReleaseWarning?: ReleaseLockReason;
|
|
33
|
+
}
|
|
34
|
+
export interface RecoverAssetSidecarOptions {
|
|
35
|
+
baseDir: string;
|
|
36
|
+
sidecar: AssetSidecarKind;
|
|
37
|
+
replacementPath: string;
|
|
38
|
+
write?: boolean;
|
|
39
|
+
acknowledgeCorruptHistory?: boolean;
|
|
40
|
+
maxFileBytes?: number;
|
|
41
|
+
deps?: {
|
|
42
|
+
beforeBackup?: () => void;
|
|
43
|
+
beforeReplace?: () => void;
|
|
44
|
+
lock?: AssetStoreLockDeps;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export declare const DEFAULT_SIDECAR_RECOVERY_MAX_FILE_BYTES: number;
|
|
48
|
+
export declare function recoverAssetSidecar(opts: RecoverAssetSidecarOptions): AssetSidecarRecoveryReport;
|