@evomap/evolver-core 2.0.0-beta.19 → 2.0.0-beta.22
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 -5
- package/dist/algo/bans.d.ts +10 -1
- package/dist/algo/bans.js +56 -6
- package/dist/algo/candidateAssembly.d.ts +25 -2
- package/dist/algo/candidateAssembly.js +143 -20
- package/dist/algo/capabilityCandidates.js +10 -0
- package/dist/algo/cycleEngine.d.ts +40 -5
- package/dist/algo/cycleEngine.js +277 -37
- package/dist/algo/evolutionEvent.d.ts +26 -0
- package/dist/algo/evolutionEvent.js +32 -0
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +16 -3
- package/dist/algo/geneHealth.d.ts +34 -1
- package/dist/algo/geneHealth.js +42 -0
- package/dist/algo/geneIntake.d.ts +46 -1
- package/dist/algo/geneIntake.js +121 -10
- package/dist/algo/geneSelection.d.ts +115 -6
- package/dist/algo/geneSelection.js +459 -32
- package/dist/algo/index.d.ts +4 -1
- package/dist/algo/index.js +4 -1
- package/dist/algo/kautoProjection.d.ts +41 -0
- package/dist/algo/kautoProjection.js +95 -0
- package/dist/algo/kautoValidator.d.ts +68 -0
- package/dist/algo/kautoValidator.js +256 -0
- package/dist/algo/orchestrator.d.ts +14 -1
- package/dist/algo/orchestrator.js +9 -2
- package/dist/algo/solidify.d.ts +11 -2
- package/dist/algo/solidify.js +37 -7
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +156 -0
- package/dist/assetrepair/hubRejection.d.ts +12 -0
- package/dist/assetrepair/hubRejection.js +109 -0
- package/dist/assetrepair/index.d.ts +2 -0
- package/dist/assetrepair/index.js +2 -0
- package/dist/assetrepair/repair.d.ts +33 -0
- package/dist/assetrepair/repair.js +155 -0
- package/dist/assetstore/assetSidecarRecords.js +5 -1
- package/dist/assetstore/assetStoreHealth.d.ts +2 -3
- package/dist/assetstore/assetStoreHealth.js +4 -10
- package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
- package/dist/assetstore/foreignJsonlSource.js +150 -0
- package/dist/assetstore/index.d.ts +3 -0
- package/dist/assetstore/index.js +3 -0
- package/dist/assetstore/learningHistory.js +3 -3
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +8 -0
- package/dist/assetstore/localJsonl.js +128 -3
- package/dist/assetstore/provenance.d.ts +38 -3
- package/dist/assetstore/provenance.js +103 -45
- package/dist/assetstore/provider.d.ts +4 -0
- package/dist/assetstore/reviewFilter.d.ts +19 -1
- package/dist/assetstore/reviewFilter.js +36 -0
- package/dist/assetstore/unionReadStore.d.ts +25 -0
- package/dist/assetstore/unionReadStore.js +119 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +4 -3
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +4 -3
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +2 -1
- package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
- package/dist/benchmark/selectionFlatAbstention.js +481 -0
- 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/lifecycleBootstrap.d.ts +111 -0
- package/dist/bootstrap/lifecycleBootstrap.js +433 -0
- package/dist/bootstrap/v1EnvCompat.d.ts +3 -0
- package/dist/bootstrap/v1EnvCompat.js +48 -1
- 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 +2 -1
- package/dist/events/ingest.js +6 -0
- package/dist/exec/autoExec.d.ts +30 -3
- package/dist/exec/autoExec.js +294 -29
- package/dist/exec/autonomousCycle.d.ts +7 -0
- package/dist/exec/autonomousCycle.js +2 -0
- package/dist/exec/claudeBridge.d.ts +28 -6
- package/dist/exec/claudeBridge.js +411 -258
- package/dist/exec/executionBinding.d.ts +414 -0
- package/dist/exec/executionBinding.js +588 -0
- package/dist/exec/index.d.ts +1 -0
- package/dist/exec/index.js +1 -0
- package/dist/exec/proofOfWork.d.ts +1 -1
- package/dist/exec/proofOfWork.js +2 -2
- package/dist/exec/runnerRegistry.d.ts +5 -0
- package/dist/exec/runnerRegistry.js +54 -14
- package/dist/hub/capability.d.ts +40 -1
- package/dist/hub/conversationDistiller.d.ts +19 -0
- package/dist/hub/conversationDistiller.js +115 -37
- package/dist/hub/fake.d.ts +1 -0
- package/dist/hub/fake.js +1 -0
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/hub/recipeCompose.d.ts +27 -0
- package/dist/hub/recipeCompose.js +90 -0
- package/dist/hub/sanitize.js +119 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mailbox/ipcServer.d.ts +2 -0
- package/dist/mailbox/ipcServer.js +20 -0
- package/dist/mailbox/store.d.ts +52 -2
- package/dist/mailbox/store.js +546 -81
- package/dist/modelCompatibility.d.ts +164 -0
- package/dist/modelCompatibility.js +309 -0
- package/dist/observers/valueDigestObserver.d.ts +9 -0
- package/dist/observers/valueDigestObserver.js +35 -2
- package/dist/ops/cleanup.js +1 -1
- 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/ops/valueOutreach.d.ts +3 -1
- package/dist/ops/valueOutreach.js +5 -1
- package/dist/personality/schema.d.ts +18 -18
- package/dist/schema/evolutionGraph.d.ts +784 -0
- package/dist/schema/evolutionGraph.js +187 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/proofOfWork.d.ts +125 -6
- package/dist/schema/proofOfWork.js +102 -4
- package/dist/shadow/shadowHub.js +1 -0
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +148 -0
- package/dist/signals/index.d.ts +3 -1
- package/dist/signals/index.js +3 -1
- package/dist/signals/scopeVocabulary.d.ts +75 -0
- package/dist/signals/scopeVocabulary.js +91 -0
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +43 -0
- package/dist/trace/learningTrace.d.ts +7 -0
- package/dist/trace/learningTrace.js +22 -0
- package/dist/trace/trajectory.d.ts +8 -0
- package/dist/trace/trajectory.js +14 -2
- package/dist/util/fileLock.d.ts +92 -5
- package/dist/util/fileLock.js +809 -70
- package/dist/util/index.d.ts +2 -1
- package/dist/util/index.js +1 -1
- package/dist/verify/index.d.ts +2 -1
- package/dist/verify/index.js +1 -1
- package/dist/verify/sandboxRunner.d.ts +2 -0
- package/dist/verify/sandboxRunner.js +154 -10
- package/dist/verify/sandboxedValidation.d.ts +7 -2
- package/dist/verify/sandboxedValidation.js +102 -24
- package/dist/verify/validation.d.ts +24 -3
- package/dist/verify/validation.js +141 -24
- package/dist/wire/geneHints.d.ts +88 -16
- package/dist/wire/geneHints.js +124 -15
- package/dist/wire/index.d.ts +8 -3
- package/dist/wire/index.js +2 -2
- package/dist/wire/schemaGate.d.ts +21 -0
- package/dist/wire/schemaGate.js +119 -10
- package/package.json +5 -4
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { posix, win32 } from 'node:path';
|
|
2
|
+
import { parseFileLockProcessStartIdentity, sameFileLockProcessStartIdentity, } from '../util/fileLock.js';
|
|
3
|
+
export const LIFECYCLE_BOOTSTRAP_MARKER_SCHEMA = 'evolver.lifecycle-bootstrap.v1';
|
|
4
|
+
export const LIFECYCLE_BOOTSTRAP_LEGACY_BINDING = 'legacy-v907';
|
|
5
|
+
export const LIFECYCLE_BOOTSTRAP_LEGACY_ENV_FILE_STATE_ROOT_PROOF = 'legacy-v907-env-file';
|
|
6
|
+
export const LIFECYCLE_BOOTSTRAP_DEADLINE_ENV = 'EVOLVER_INTERNAL_BOOTSTRAP_DEADLINE_MS';
|
|
7
|
+
export const LIFECYCLE_BOOTSTRAP_TRANSACTION_ENV = 'EVOLVER_INTERNAL_BOOTSTRAP_TRANSACTION_ID';
|
|
8
|
+
export const LIFECYCLE_BOOTSTRAP_SUCCESS_FILE = 'bootstrap.json';
|
|
9
|
+
export const LIFECYCLE_BOOTSTRAP_JOURNAL_FILE = 'bootstrap-transaction.json';
|
|
10
|
+
export const LIFECYCLE_BOOTSTRAP_OWNER_LOCK_FILE = 'bootstrap-owner.lock';
|
|
11
|
+
export const LIFECYCLE_BOOTSTRAP_READINESS_FILE = 'bootstrap-readiness.json';
|
|
12
|
+
export const LIFECYCLE_BOOTSTRAP_READINESS_LOCK_FILE = 'bootstrap-readiness.lock';
|
|
13
|
+
export const LIFECYCLE_BOOTSTRAP_READINESS_SCHEMA = 'evolver.lifecycle-bootstrap-readiness.v1';
|
|
14
|
+
export const LIFECYCLE_BOOTSTRAP_MANUAL_TRANSITION_FILE = 'bootstrap-manual-transition.json';
|
|
15
|
+
export const LIFECYCLE_BOOTSTRAP_MANUAL_TRANSITION_SCHEMA = 'evolver.lifecycle-bootstrap-manual-transition.v1';
|
|
16
|
+
export const MAX_LIFECYCLE_BOOTSTRAP_MANUAL_TRANSITION_BYTES = 4 * 1024;
|
|
17
|
+
export const LIFECYCLE_BOOTSTRAP_REGISTRATION_INTENT_FILE = 'bootstrap-registration.intent.json';
|
|
18
|
+
export const LIFECYCLE_BOOTSTRAP_REGISTRATION_INTENT_TERMINAL_FILE = 'bootstrap-registration.intent.terminal';
|
|
19
|
+
export const LIFECYCLE_BOOTSTRAP_REGISTRATION_INTENT_CLEARING_FILE = 'bootstrap-registration.intent.clearing';
|
|
20
|
+
export const LIFECYCLE_BOOTSTRAP_REGISTRATION_INTENT_SCHEMA = 'evolver.bootstrap-registration-intent.v1';
|
|
21
|
+
export const LIFECYCLE_BOOTSTRAP_REGISTRATION_TOKEN_ENV = 'EVOLVER_INTERNAL_BOOTSTRAP_REGISTRATION_TOKEN';
|
|
22
|
+
export const MAX_LIFECYCLE_BOOTSTRAP_REGISTRATION_INTENT_BYTES = 4 * 1024;
|
|
23
|
+
const TARGETS = new Set(['launchd', 'systemd', 'windows']);
|
|
24
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
25
|
+
const SHA256_RE = /^[0-9a-f]{64}$/;
|
|
26
|
+
function canonicalBootstrapPath(path, target) {
|
|
27
|
+
return target === 'windows'
|
|
28
|
+
? win32.isAbsolute(path) && win32.normalize(path) === path
|
|
29
|
+
: posix.isAbsolute(path) && posix.normalize(path) === path;
|
|
30
|
+
}
|
|
31
|
+
function bootstrapPathKey(path, target) {
|
|
32
|
+
return target === 'windows' ? win32.normalize(path).toLowerCase() : path;
|
|
33
|
+
}
|
|
34
|
+
function boundedText(value, maxLength) {
|
|
35
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > maxLength)
|
|
36
|
+
return false;
|
|
37
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
38
|
+
const code = value.charCodeAt(index);
|
|
39
|
+
if (code <= 0x1f || code === 0x7f)
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
function exactRecordKeys(record, expected) {
|
|
45
|
+
const actual = Object.keys(record).sort();
|
|
46
|
+
const sortedExpected = [...expected].sort();
|
|
47
|
+
return actual.length === sortedExpected.length
|
|
48
|
+
&& actual.every((key, index) => key === sortedExpected[index]);
|
|
49
|
+
}
|
|
50
|
+
function exactIsoTimestamp(value) {
|
|
51
|
+
if (typeof value !== 'string')
|
|
52
|
+
return false;
|
|
53
|
+
const parsed = Date.parse(value);
|
|
54
|
+
return Number.isFinite(parsed) && new Date(parsed).toISOString() === value;
|
|
55
|
+
}
|
|
56
|
+
export function parseLifecycleBootstrapRegistrationToken(value) {
|
|
57
|
+
return typeof value === 'string' && UUID_RE.test(value) ? value : undefined;
|
|
58
|
+
}
|
|
59
|
+
function exactProcessStartIdentityKeys(value) {
|
|
60
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
61
|
+
return false;
|
|
62
|
+
const record = value;
|
|
63
|
+
if (record['source'] === 'linux-proc') {
|
|
64
|
+
return exactRecordKeys(record, ['source', 'bootId', 'startTicks']);
|
|
65
|
+
}
|
|
66
|
+
if (record['source'] === 'windows-powershell') {
|
|
67
|
+
return exactRecordKeys(record, ['source', 'startTimeTicks']);
|
|
68
|
+
}
|
|
69
|
+
if (record['source'] === 'darwin-ps') {
|
|
70
|
+
return exactRecordKeys(record, ['source', 'startTime']);
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
export function parseLifecycleBootstrapManualTransition(value) {
|
|
75
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
76
|
+
return undefined;
|
|
77
|
+
const record = value;
|
|
78
|
+
if (!exactRecordKeys(record, ['schema', 'transitionId', 'removedTransactionId', 'target', 'service', 'createdAt'])
|
|
79
|
+
|| record['schema'] !== LIFECYCLE_BOOTSTRAP_MANUAL_TRANSITION_SCHEMA
|
|
80
|
+
|| typeof record['transitionId'] !== 'string' || !UUID_RE.test(record['transitionId'])
|
|
81
|
+
|| typeof record['removedTransactionId'] !== 'string'
|
|
82
|
+
|| !UUID_RE.test(record['removedTransactionId'])
|
|
83
|
+
|| typeof record['target'] !== 'string'
|
|
84
|
+
|| !TARGETS.has(record['target'])
|
|
85
|
+
|| !boundedText(record['service'], 128)
|
|
86
|
+
|| !exactIsoTimestamp(record['createdAt'])) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
schema: LIFECYCLE_BOOTSTRAP_MANUAL_TRANSITION_SCHEMA,
|
|
91
|
+
transitionId: record['transitionId'],
|
|
92
|
+
removedTransactionId: record['removedTransactionId'],
|
|
93
|
+
target: record['target'],
|
|
94
|
+
service: record['service'],
|
|
95
|
+
createdAt: record['createdAt'],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function parseLifecycleBootstrapManualTransitionJson(raw) {
|
|
99
|
+
try {
|
|
100
|
+
return parseLifecycleBootstrapManualTransition(JSON.parse(raw));
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function parseLifecycleBootstrapRegistrationIntent(value) {
|
|
107
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
108
|
+
return undefined;
|
|
109
|
+
const record = value;
|
|
110
|
+
const ownerValue = record['owner'];
|
|
111
|
+
if (record['schema'] !== LIFECYCLE_BOOTSTRAP_REGISTRATION_INTENT_SCHEMA
|
|
112
|
+
|| !ownerValue || typeof ownerValue !== 'object' || Array.isArray(ownerValue)
|
|
113
|
+
|| !exactRecordKeys(ownerValue, ['pid', 'token', 'processStartIdentity'])
|
|
114
|
+
|| !Number.isSafeInteger(ownerValue['pid'])
|
|
115
|
+
|| ownerValue['pid'] <= 0
|
|
116
|
+
|| !parseLifecycleBootstrapRegistrationToken(ownerValue['token'])
|
|
117
|
+
|| !exactProcessStartIdentityKeys(ownerValue['processStartIdentity'])
|
|
118
|
+
|| !exactIsoTimestamp(record['createdAt'])) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const processStartIdentity = parseFileLockProcessStartIdentity(ownerValue['processStartIdentity']);
|
|
122
|
+
if (!processStartIdentity)
|
|
123
|
+
return undefined;
|
|
124
|
+
const owner = {
|
|
125
|
+
pid: ownerValue['pid'],
|
|
126
|
+
token: ownerValue['token'],
|
|
127
|
+
processStartIdentity,
|
|
128
|
+
};
|
|
129
|
+
const base = {
|
|
130
|
+
schema: LIFECYCLE_BOOTSTRAP_REGISTRATION_INTENT_SCHEMA,
|
|
131
|
+
owner,
|
|
132
|
+
createdAt: record['createdAt'],
|
|
133
|
+
};
|
|
134
|
+
if (record['state'] === 'registering') {
|
|
135
|
+
if (!exactRecordKeys(record, ['schema', 'state', 'owner', 'createdAt']))
|
|
136
|
+
return undefined;
|
|
137
|
+
return { ...base, state: 'registering' };
|
|
138
|
+
}
|
|
139
|
+
const terminalOutcomes = new Set([
|
|
140
|
+
'committed',
|
|
141
|
+
'rolled_back',
|
|
142
|
+
'no_child',
|
|
143
|
+
'cancelled',
|
|
144
|
+
]);
|
|
145
|
+
if (record['state'] !== 'terminal'
|
|
146
|
+
|| typeof record['outcome'] !== 'string'
|
|
147
|
+
|| !terminalOutcomes.has(record['outcome'])
|
|
148
|
+
|| !exactIsoTimestamp(record['terminalAt'])) {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
const outcome = record['outcome'];
|
|
152
|
+
if (outcome === 'committed') {
|
|
153
|
+
if (!exactRecordKeys(record, ['schema', 'state', 'owner', 'createdAt', 'outcome', 'terminalAt', 'transactionId'])
|
|
154
|
+
|| typeof record['transactionId'] !== 'string'
|
|
155
|
+
|| !UUID_RE.test(record['transactionId'])) {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
...base,
|
|
160
|
+
state: 'terminal',
|
|
161
|
+
outcome,
|
|
162
|
+
terminalAt: record['terminalAt'],
|
|
163
|
+
transactionId: record['transactionId'],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (!exactRecordKeys(record, ['schema', 'state', 'owner', 'createdAt', 'outcome', 'terminalAt'])
|
|
167
|
+
|| record['transactionId'] !== undefined) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
return { ...base, state: 'terminal', outcome, terminalAt: record['terminalAt'] };
|
|
171
|
+
}
|
|
172
|
+
export function parseLifecycleBootstrapRegistrationIntentJson(raw) {
|
|
173
|
+
try {
|
|
174
|
+
return parseLifecycleBootstrapRegistrationIntent(JSON.parse(raw));
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export function parseLifecycleBootstrapMarker(value) {
|
|
181
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
182
|
+
return undefined;
|
|
183
|
+
const record = value;
|
|
184
|
+
if (record['schema'] !== LIFECYCLE_BOOTSTRAP_MARKER_SCHEMA)
|
|
185
|
+
return undefined;
|
|
186
|
+
const markerKeys = [
|
|
187
|
+
'schema',
|
|
188
|
+
'transactionId',
|
|
189
|
+
'bootstrappedAt',
|
|
190
|
+
'target',
|
|
191
|
+
'service',
|
|
192
|
+
'files',
|
|
193
|
+
'managerArtifactPath',
|
|
194
|
+
'artifacts',
|
|
195
|
+
...(record['managerBindingKind'] === undefined ? [] : ['managerBindingKind']),
|
|
196
|
+
...(record['preservedArtifacts'] === undefined ? [] : ['preservedArtifacts']),
|
|
197
|
+
...(record['legacyStateRootProof'] === undefined ? [] : ['legacyStateRootProof']),
|
|
198
|
+
];
|
|
199
|
+
if (!exactRecordKeys(record, markerKeys))
|
|
200
|
+
return undefined;
|
|
201
|
+
if (typeof record['transactionId'] !== 'string' || !UUID_RE.test(record['transactionId']))
|
|
202
|
+
return undefined;
|
|
203
|
+
if (typeof record['bootstrappedAt'] !== 'string' || Number.isNaN(Date.parse(record['bootstrappedAt'])))
|
|
204
|
+
return undefined;
|
|
205
|
+
if (typeof record['target'] !== 'string' || !TARGETS.has(record['target']))
|
|
206
|
+
return undefined;
|
|
207
|
+
if (!boundedText(record['service'], 128))
|
|
208
|
+
return undefined;
|
|
209
|
+
if (!Array.isArray(record['files']) || record['files'].length === 0 || record['files'].length > 32)
|
|
210
|
+
return undefined;
|
|
211
|
+
if (!record['files'].every((file) => boundedText(file, 4_096)))
|
|
212
|
+
return undefined;
|
|
213
|
+
const files = record['files'];
|
|
214
|
+
const target = record['target'];
|
|
215
|
+
if (files.some((file) => !canonicalBootstrapPath(file, target)))
|
|
216
|
+
return undefined;
|
|
217
|
+
const pathKeys = files.map((file) => bootstrapPathKey(file, target));
|
|
218
|
+
if (new Set(pathKeys).size !== pathKeys.length)
|
|
219
|
+
return undefined;
|
|
220
|
+
if (!boundedText(record['managerArtifactPath'], 4_096)
|
|
221
|
+
|| !canonicalBootstrapPath(record['managerArtifactPath'], target)
|
|
222
|
+
|| !pathKeys.includes(bootstrapPathKey(record['managerArtifactPath'], target)))
|
|
223
|
+
return undefined;
|
|
224
|
+
const managerBindingKind = record['managerBindingKind'];
|
|
225
|
+
if (managerBindingKind !== undefined
|
|
226
|
+
&& managerBindingKind !== 'transaction'
|
|
227
|
+
&& managerBindingKind !== LIFECYCLE_BOOTSTRAP_LEGACY_BINDING)
|
|
228
|
+
return undefined;
|
|
229
|
+
if (!Array.isArray(record['artifacts']))
|
|
230
|
+
return undefined;
|
|
231
|
+
const parseArtifact = (artifact) => {
|
|
232
|
+
if (!artifact || typeof artifact !== 'object' || Array.isArray(artifact))
|
|
233
|
+
return undefined;
|
|
234
|
+
const value = artifact;
|
|
235
|
+
if (!exactRecordKeys(value, ['path', 'size', 'sha256', 'device', 'inode']))
|
|
236
|
+
return undefined;
|
|
237
|
+
if (!boundedText(value['path'], 4_096) || !canonicalBootstrapPath(value['path'], target)
|
|
238
|
+
|| !Number.isSafeInteger(value['size']) || value['size'] < 0
|
|
239
|
+
|| typeof value['sha256'] !== 'string' || !SHA256_RE.test(value['sha256']))
|
|
240
|
+
return undefined;
|
|
241
|
+
if (typeof value['device'] !== 'string' || !/^[0-9]+$/.test(value['device'])
|
|
242
|
+
|| BigInt(value['device']) <= 0n
|
|
243
|
+
|| typeof value['inode'] !== 'string' || !/^[0-9]+$/.test(value['inode'])
|
|
244
|
+
|| BigInt(value['inode']) <= 0n)
|
|
245
|
+
return undefined;
|
|
246
|
+
return {
|
|
247
|
+
path: value['path'],
|
|
248
|
+
size: value['size'],
|
|
249
|
+
sha256: value['sha256'],
|
|
250
|
+
device: value['device'],
|
|
251
|
+
inode: value['inode'],
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
const artifacts = record['artifacts'].map(parseArtifact);
|
|
255
|
+
const preservedArtifacts = record['preservedArtifacts'] === undefined
|
|
256
|
+
? []
|
|
257
|
+
: Array.isArray(record['preservedArtifacts'])
|
|
258
|
+
? record['preservedArtifacts'].map(parseArtifact)
|
|
259
|
+
: [undefined];
|
|
260
|
+
if (record['preservedArtifacts'] !== undefined
|
|
261
|
+
&& (managerBindingKind !== LIFECYCLE_BOOTSTRAP_LEGACY_BINDING
|
|
262
|
+
|| preservedArtifacts.length === 0))
|
|
263
|
+
return undefined;
|
|
264
|
+
const legacyStateRootProofValue = record['legacyStateRootProof'];
|
|
265
|
+
let legacyStateRootProof;
|
|
266
|
+
if (legacyStateRootProofValue !== undefined) {
|
|
267
|
+
if (managerBindingKind !== LIFECYCLE_BOOTSTRAP_LEGACY_BINDING
|
|
268
|
+
|| !legacyStateRootProofValue
|
|
269
|
+
|| typeof legacyStateRootProofValue !== 'object'
|
|
270
|
+
|| Array.isArray(legacyStateRootProofValue))
|
|
271
|
+
return undefined;
|
|
272
|
+
const proof = legacyStateRootProofValue;
|
|
273
|
+
if (!exactRecordKeys(proof, ['kind', 'envFilePath', 'stateDir'])
|
|
274
|
+
|| proof['kind'] !== LIFECYCLE_BOOTSTRAP_LEGACY_ENV_FILE_STATE_ROOT_PROOF
|
|
275
|
+
|| !boundedText(proof['envFilePath'], 4_096)
|
|
276
|
+
|| !canonicalBootstrapPath(proof['envFilePath'], target)
|
|
277
|
+
|| !boundedText(proof['stateDir'], 4_096)
|
|
278
|
+
|| !canonicalBootstrapPath(proof['stateDir'], target))
|
|
279
|
+
return undefined;
|
|
280
|
+
legacyStateRootProof = {
|
|
281
|
+
kind: LIFECYCLE_BOOTSTRAP_LEGACY_ENV_FILE_STATE_ROOT_PROOF,
|
|
282
|
+
envFilePath: proof['envFilePath'],
|
|
283
|
+
stateDir: proof['stateDir'],
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (managerBindingKind !== LIFECYCLE_BOOTSTRAP_LEGACY_BINDING
|
|
287
|
+
&& artifacts.length !== files.length)
|
|
288
|
+
return undefined;
|
|
289
|
+
const receipts = [...artifacts, ...preservedArtifacts];
|
|
290
|
+
if (receipts.length !== files.length
|
|
291
|
+
|| receipts.some((artifact) => artifact === undefined)
|
|
292
|
+
|| receipts.some((artifact, index) => bootstrapPathKey(artifact.path, target) !== pathKeys[index]))
|
|
293
|
+
return undefined;
|
|
294
|
+
const ownedKeys = new Set(artifacts.map((artifact) => bootstrapPathKey(artifact.path, target)));
|
|
295
|
+
if (!ownedKeys.has(bootstrapPathKey(record['managerArtifactPath'], target)))
|
|
296
|
+
return undefined;
|
|
297
|
+
if (legacyStateRootProof) {
|
|
298
|
+
const proofKey = bootstrapPathKey(legacyStateRootProof.envFilePath, target);
|
|
299
|
+
const preservedKeys = new Set(preservedArtifacts.map((artifact) => bootstrapPathKey(artifact.path, target)));
|
|
300
|
+
if (!preservedKeys.has(proofKey) || ownedKeys.has(proofKey))
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
schema: LIFECYCLE_BOOTSTRAP_MARKER_SCHEMA,
|
|
305
|
+
transactionId: record['transactionId'],
|
|
306
|
+
bootstrappedAt: record['bootstrappedAt'],
|
|
307
|
+
target,
|
|
308
|
+
service: record['service'],
|
|
309
|
+
files: [...files],
|
|
310
|
+
managerArtifactPath: record['managerArtifactPath'],
|
|
311
|
+
...(managerBindingKind === undefined ? {} : {
|
|
312
|
+
managerBindingKind: managerBindingKind,
|
|
313
|
+
}),
|
|
314
|
+
artifacts: artifacts,
|
|
315
|
+
...(preservedArtifacts.length === 0 ? {} : {
|
|
316
|
+
preservedArtifacts: preservedArtifacts,
|
|
317
|
+
}),
|
|
318
|
+
...(legacyStateRootProof ? { legacyStateRootProof } : {}),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
export function parseLegacyLifecycleBootstrapMarker(value) {
|
|
322
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
323
|
+
return undefined;
|
|
324
|
+
const record = value;
|
|
325
|
+
if (!exactRecordKeys(record, ['bootstrappedAt', 'target', 'service', 'files'])
|
|
326
|
+
|| !exactIsoTimestamp(record['bootstrappedAt'])
|
|
327
|
+
|| typeof record['target'] !== 'string'
|
|
328
|
+
|| !TARGETS.has(record['target'])
|
|
329
|
+
|| !boundedText(record['service'], 128)
|
|
330
|
+
|| !Array.isArray(record['files'])
|
|
331
|
+
|| record['files'].length === 0
|
|
332
|
+
|| record['files'].length > 32
|
|
333
|
+
|| !record['files'].every((file) => boundedText(file, 4_096)))
|
|
334
|
+
return undefined;
|
|
335
|
+
const target = record['target'];
|
|
336
|
+
const service = record['service'];
|
|
337
|
+
const expectedService = target === 'systemd'
|
|
338
|
+
? 'systemd-user'
|
|
339
|
+
: target === 'launchd'
|
|
340
|
+
? 'launchd'
|
|
341
|
+
: 'windows-scheduled-task';
|
|
342
|
+
const files = record['files'];
|
|
343
|
+
if (service !== expectedService
|
|
344
|
+
|| files.some((file) => !canonicalBootstrapPath(file, target)))
|
|
345
|
+
return undefined;
|
|
346
|
+
const pathKeys = files.map((file) => bootstrapPathKey(file, target));
|
|
347
|
+
if (new Set(pathKeys).size !== pathKeys.length)
|
|
348
|
+
return undefined;
|
|
349
|
+
return {
|
|
350
|
+
bootstrappedAt: record['bootstrappedAt'],
|
|
351
|
+
target,
|
|
352
|
+
service,
|
|
353
|
+
files: [...files],
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
export function parseLegacyLifecycleBootstrapMarkerJson(raw) {
|
|
357
|
+
try {
|
|
358
|
+
return parseLegacyLifecycleBootstrapMarker(JSON.parse(raw));
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
export function parseLifecycleBootstrapMarkerJson(raw) {
|
|
365
|
+
try {
|
|
366
|
+
return parseLifecycleBootstrapMarker(JSON.parse(raw));
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
export function parseLifecycleBootstrapReadiness(value) {
|
|
373
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
374
|
+
return undefined;
|
|
375
|
+
const record = value;
|
|
376
|
+
if (!exactRecordKeys(record, [
|
|
377
|
+
'schema',
|
|
378
|
+
'transactionId',
|
|
379
|
+
'pid',
|
|
380
|
+
'pidProcessStartIdentity',
|
|
381
|
+
'supervisorPid',
|
|
382
|
+
'supervisorProcessStartIdentity',
|
|
383
|
+
'startedAt',
|
|
384
|
+
'ipcUrl',
|
|
385
|
+
])
|
|
386
|
+
|| record['schema'] !== LIFECYCLE_BOOTSTRAP_READINESS_SCHEMA
|
|
387
|
+
|| typeof record['transactionId'] !== 'string' || !UUID_RE.test(record['transactionId'])
|
|
388
|
+
|| !Number.isSafeInteger(record['pid']) || record['pid'] <= 0
|
|
389
|
+
|| !Number.isSafeInteger(record['supervisorPid']) || record['supervisorPid'] <= 0
|
|
390
|
+
|| !exactProcessStartIdentityKeys(record['pidProcessStartIdentity'])
|
|
391
|
+
|| !exactProcessStartIdentityKeys(record['supervisorProcessStartIdentity'])
|
|
392
|
+
|| !exactIsoTimestamp(record['startedAt'])
|
|
393
|
+
|| !boundedText(record['ipcUrl'], 2_048))
|
|
394
|
+
return undefined;
|
|
395
|
+
const pidProcessStartIdentity = parseFileLockProcessStartIdentity(record['pidProcessStartIdentity']);
|
|
396
|
+
const supervisorProcessStartIdentity = parseFileLockProcessStartIdentity(record['supervisorProcessStartIdentity']);
|
|
397
|
+
if (!pidProcessStartIdentity || !supervisorProcessStartIdentity)
|
|
398
|
+
return undefined;
|
|
399
|
+
if (record['pid'] === record['supervisorPid']
|
|
400
|
+
&& !sameFileLockProcessStartIdentity(pidProcessStartIdentity, supervisorProcessStartIdentity))
|
|
401
|
+
return undefined;
|
|
402
|
+
try {
|
|
403
|
+
const url = new URL(record['ipcUrl']);
|
|
404
|
+
const port = Number(url.port);
|
|
405
|
+
if (url.protocol !== 'http:' || !['127.0.0.1', '[::1]'].includes(url.hostname.toLowerCase())
|
|
406
|
+
|| !Number.isSafeInteger(port) || port <= 0 || port > 65_535
|
|
407
|
+
|| url.username !== '' || url.password !== '' || url.pathname !== '/'
|
|
408
|
+
|| url.search !== '' || url.hash !== '') {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
schema: LIFECYCLE_BOOTSTRAP_READINESS_SCHEMA,
|
|
417
|
+
transactionId: record['transactionId'],
|
|
418
|
+
pid: record['pid'],
|
|
419
|
+
pidProcessStartIdentity,
|
|
420
|
+
supervisorPid: record['supervisorPid'],
|
|
421
|
+
supervisorProcessStartIdentity,
|
|
422
|
+
startedAt: record['startedAt'],
|
|
423
|
+
ipcUrl: record['ipcUrl'],
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
export function parseLifecycleBootstrapReadinessJson(raw) {
|
|
427
|
+
try {
|
|
428
|
+
return parseLifecycleBootstrapReadiness(JSON.parse(raw));
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* | `OPENCLAW_WORKSPACE` | *(manual)* | Partial V2 support does not preserve V1 workspace and bridge semantics |
|
|
15
15
|
* | `EVOLVER_NO_PARENT_GIT` | *(none)* | V2 uses `EVOLVER_REPO_ROOT` or nearest Git root |
|
|
16
16
|
* | `EVOLVER_VERBOSE` | *(none)* | V2 has no global switch; opt in to feature-specific diagnostics manually |
|
|
17
|
+
* | `EVOLVER_OPENAI_COMPATIBLE_BASE_URLS` | *(manual)* | Defensive compatibility spelling; canonical V1 used the EVOMAP-prefixed key |
|
|
18
|
+
* | `EVOMAP_OPENAI_COMPATIBLE_BASE_URLS` | *(manual)* | Canonical V1 compatible-origin allowlist; selected base and credential were separate |
|
|
17
19
|
* | `EVOLVER_AUTO_ISSUE` | *(none)* | V2 creates local drafts; submit requires explicit approval-gated flow |
|
|
18
20
|
* | `EVOLVER_ROLLBACK_MODE` | *(none)* | V2 uses worktree/snapshot/recovery policy |
|
|
19
21
|
* | `WORKER_ENABLED` | *(none)* | No merchant-worker resolver in V2 |
|
|
@@ -98,6 +100,7 @@ export interface V1EnvTranslationReport {
|
|
|
98
100
|
readonly manualCount: number;
|
|
99
101
|
readonly removableCount: number;
|
|
100
102
|
readonly githubTokenPresent: boolean;
|
|
103
|
+
readonly caveats?: readonly string[];
|
|
101
104
|
}
|
|
102
105
|
/**
|
|
103
106
|
* Build an offline V1→V2 env translation report.
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* | `OPENCLAW_WORKSPACE` | *(manual)* | Partial V2 support does not preserve V1 workspace and bridge semantics |
|
|
15
15
|
* | `EVOLVER_NO_PARENT_GIT` | *(none)* | V2 uses `EVOLVER_REPO_ROOT` or nearest Git root |
|
|
16
16
|
* | `EVOLVER_VERBOSE` | *(none)* | V2 has no global switch; opt in to feature-specific diagnostics manually |
|
|
17
|
+
* | `EVOLVER_OPENAI_COMPATIBLE_BASE_URLS` | *(manual)* | Defensive compatibility spelling; canonical V1 used the EVOMAP-prefixed key |
|
|
18
|
+
* | `EVOMAP_OPENAI_COMPATIBLE_BASE_URLS` | *(manual)* | Canonical V1 compatible-origin allowlist; selected base and credential were separate |
|
|
17
19
|
* | `EVOLVER_AUTO_ISSUE` | *(none)* | V2 creates local drafts; submit requires explicit approval-gated flow |
|
|
18
20
|
* | `EVOLVER_ROLLBACK_MODE` | *(none)* | V2 uses worktree/snapshot/recovery policy |
|
|
19
21
|
* | `WORKER_ENABLED` | *(none)* | No merchant-worker resolver in V2 |
|
|
@@ -56,6 +58,27 @@ export const V1_DEPRECATION_TABLE = [
|
|
|
56
58
|
guidance: 'V2 has no global verbose switch. Remove this variable and opt in to a ' +
|
|
57
59
|
'feature-specific flag such as EVOLVER_HOOK_VERBOSE=1 only when hook diagnostics are needed.',
|
|
58
60
|
},
|
|
61
|
+
{
|
|
62
|
+
v1Name: 'EVOLVER_OPENAI_COMPATIBLE_BASE_URLS',
|
|
63
|
+
v2Equivalent: null,
|
|
64
|
+
migrationAction: 'manual',
|
|
65
|
+
guidance: 'This compatibility spelling was not resolved by the canonical V1 runtime, but it is tracked defensively. ' +
|
|
66
|
+
'V2 has no multi-base or arbitrary OpenAI-compatible resolver. Review the workload before cutover. ' +
|
|
67
|
+
'While either legacy list key remains set, configure the exact canonical pair EVOLVER_LLM_OPENAI_BASE_URL ' +
|
|
68
|
+
'and EVOLVER_LLM_OPENAI_API_KEY for one official https://*.api.openai.com/v1 endpoint. ' +
|
|
69
|
+
'LiteLLM, OpenRouter, Azure OpenAI, MiniMax, DeepSeek, Moonshot, and custom endpoints are not drop-in routes; ' +
|
|
70
|
+
'do not copy their credentials into the V2 OpenAI route.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
v1Name: 'EVOMAP_OPENAI_COMPATIBLE_BASE_URLS',
|
|
74
|
+
v2Equivalent: null,
|
|
75
|
+
migrationAction: 'manual',
|
|
76
|
+
guidance: 'Canonical V1 treated this comma-separated value as an allowed-origin list; EVOMAP_OPENAI_BASE_URL still selected one ' +
|
|
77
|
+
'endpoint and EVOMAP_OPENAI_API_KEY or OPENAI_API_KEY supplied its credential. V2 requires a workload-by-workload review ' +
|
|
78
|
+
'and has no automatic mapping. While the legacy list remains set, only the exact canonical pair ' +
|
|
79
|
+
'EVOLVER_LLM_OPENAI_BASE_URL and EVOLVER_LLM_OPENAI_API_KEY acknowledges migration; otherwise keep the incompatible ' +
|
|
80
|
+
'workload outside the V2 OpenAI route.',
|
|
81
|
+
},
|
|
59
82
|
{
|
|
60
83
|
v1Name: 'EVOLVER_AUTO_ISSUE',
|
|
61
84
|
v2Equivalent: null,
|
|
@@ -109,6 +132,23 @@ export const V1_DEPRECATION_TABLE = [
|
|
|
109
132
|
guidance: 'V2 LocalMemoryGraph retains three archives by default and prunes older archives automatically. ' +
|
|
110
133
|
'There is no environment override; remove this variable.',
|
|
111
134
|
},
|
|
135
|
+
{
|
|
136
|
+
v1Name: 'EVOLVER_ATP',
|
|
137
|
+
v2Equivalent: null,
|
|
138
|
+
migrationAction: 'manual',
|
|
139
|
+
guidance: 'V1 auto/on/off controlled merchant/provider lifecycle, which has no one-to-one V2 resolver. '
|
|
140
|
+
+ 'Review the missing merchant workflow before removing this key. Do not map it to `evolver atp enable` '
|
|
141
|
+
+ 'or EVOLVER_ATP_AUTOBUY; those controls authorize autonomous buyer spending. If V1 off meant no '
|
|
142
|
+
+ 'provider or delivery activity, set EVOLVER_ATP_AUTODELIVER=off explicitly as hardening, not as an equivalent mapping.',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
v1Name: 'EVOLVER_SESSION_SOURCE',
|
|
146
|
+
v2Equivalent: null,
|
|
147
|
+
migrationAction: 'manual',
|
|
148
|
+
guidance: 'V1 auto/cursor/openclaw/merge selected session collectors, which has no one-to-one V2 resolver. '
|
|
149
|
+
+ 'Review the workload before removing this key. Supported trajectory export requires explicit runtime '
|
|
150
|
+
+ 'discovery or transcript-directory controls; setting another aggregate key does not restore OpenClaw collection.',
|
|
151
|
+
},
|
|
112
152
|
];
|
|
113
153
|
/**
|
|
114
154
|
* Scan the provided environment for deprecated V1 env vars and return
|
|
@@ -173,6 +213,10 @@ export function checkV1EnvCompat(env = typeof process !== 'undefined' ? process.
|
|
|
173
213
|
}
|
|
174
214
|
return result;
|
|
175
215
|
}
|
|
216
|
+
const V1_IMPLICIT_DEFAULT_CAVEATS = [
|
|
217
|
+
'V1 treated unset EVOLVER_ATP and EVOLVER_SESSION_SOURCE as auto. A clean key scan therefore does not prove '
|
|
218
|
+
+ 'merchant/provider or session-collection workflow parity; audit both before cutover.',
|
|
219
|
+
];
|
|
176
220
|
/**
|
|
177
221
|
* Build an offline V1→V2 env translation report.
|
|
178
222
|
* Does not write files; callers decide how to present or apply suggestions.
|
|
@@ -224,6 +268,7 @@ export function translateV1Env(env = typeof process !== 'undefined' ? process.en
|
|
|
224
268
|
manualCount: suggestions.filter((s) => s.action === 'manual').length,
|
|
225
269
|
removableCount: suggestions.filter((s) => s.action === 'remove').length,
|
|
226
270
|
githubTokenPresent,
|
|
271
|
+
caveats: V1_IMPLICIT_DEFAULT_CAVEATS,
|
|
227
272
|
};
|
|
228
273
|
}
|
|
229
274
|
/** Human-readable report (never prints secret-looking values; only key names + actions). */
|
|
@@ -232,8 +277,10 @@ export function formatV1EnvTranslationReport(report) {
|
|
|
232
277
|
'V1 → V2 env translation (offline, no writes):',
|
|
233
278
|
` detected_deprecated=${report.detectedCount} map=${report.mappableCount} manual=${report.manualCount} remove=${report.removableCount} github_token=${report.githubTokenPresent ? 'present' : 'absent'}`,
|
|
234
279
|
];
|
|
280
|
+
for (const caveat of report.caveats ?? [])
|
|
281
|
+
lines.push(` caveat: ${caveat}`);
|
|
235
282
|
if (report.suggestions.length === 0) {
|
|
236
|
-
lines.push(' (no V1-only knobs detected)');
|
|
283
|
+
lines.push(' (no explicit V1-only knobs detected)');
|
|
237
284
|
return `${lines.join('\n')}\n`;
|
|
238
285
|
}
|
|
239
286
|
for (const s of report.suggestions) {
|
|
@@ -61,5 +61,7 @@ export declare function rootEventArchiveSegmentName(firstSeq: number, lastSeq: n
|
|
|
61
61
|
export declare function planRootEventArchive(opts: RootEventArchiveOptions): RootEventArchivePlan;
|
|
62
62
|
export declare function archiveRootEvents(opts: RootEventArchiveOptions): RootEventArchiveResult;
|
|
63
63
|
export declare function readRootEventHistory(path: string, archiveDir?: string): RootEvent[];
|
|
64
|
+
/** Strict replay for control-plane decisions that must fail closed on partial or conflicting history. */
|
|
65
|
+
export declare function readRootEventHistoryStrict(path: string): RootEvent[];
|
|
64
66
|
export declare function validateRootEventHistory(path: string): void;
|
|
65
67
|
export declare function inspectRootEventArchive(path: string): RootEventArchiveStats;
|
|
@@ -131,6 +131,9 @@ export function archiveRootEvents(opts) {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
export function readRootEventHistory(path, archiveDir = rootEventArchiveDir(path)) {
|
|
134
|
+
// Snapshot active first. Writers publish archive segments before replacing active, so this prevents a reader
|
|
135
|
+
// from combining an old archive inventory with the new active tail and temporarily dropping the rotated prefix.
|
|
136
|
+
const active = readEventsLenient(path);
|
|
134
137
|
const bySeq = new Map();
|
|
135
138
|
for (const file of archiveSegmentFiles(archiveDir)) {
|
|
136
139
|
for (const event of readEventsLenient(join(archiveDir, file))) {
|
|
@@ -143,13 +146,20 @@ export function readRootEventHistory(path, archiveDir = rootEventArchiveDir(path
|
|
|
143
146
|
}
|
|
144
147
|
// Preserve the pre-archive fail-soft behavior for routine commands. The active log is the current writer
|
|
145
148
|
// source, so it deterministically wins an active/archive conflict without crashing status/report readers.
|
|
146
|
-
for (const event of
|
|
149
|
+
for (const event of active)
|
|
147
150
|
bySeq.set(event.seq, event);
|
|
148
151
|
return [...bySeq.values()].sort((a, b) => a.seq - b.seq);
|
|
149
152
|
}
|
|
150
|
-
|
|
153
|
+
/** Strict replay for control-plane decisions that must fail closed on partial or conflicting history. */
|
|
154
|
+
export function readRootEventHistoryStrict(path) {
|
|
151
155
|
const active = readActiveStrict(path);
|
|
152
|
-
readValidatedArchive(path, active);
|
|
156
|
+
const bySeq = readValidatedArchive(path, active);
|
|
157
|
+
for (const line of active)
|
|
158
|
+
bySeq.set(line.event.seq, line.event);
|
|
159
|
+
return [...bySeq.values()].sort((left, right) => left.seq - right.seq);
|
|
160
|
+
}
|
|
161
|
+
export function validateRootEventHistory(path) {
|
|
162
|
+
void readRootEventHistoryStrict(path);
|
|
153
163
|
}
|
|
154
164
|
export function inspectRootEventArchive(path) {
|
|
155
165
|
const archiveDir = rootEventArchiveDir(path);
|
|
@@ -81,6 +81,11 @@ export declare const rootEvent: z.ZodObject<{
|
|
|
81
81
|
}>>;
|
|
82
82
|
}, "strip", z.ZodTypeAny, {
|
|
83
83
|
type: string;
|
|
84
|
+
actor: {
|
|
85
|
+
kind: "machine" | "human";
|
|
86
|
+
id?: string | undefined;
|
|
87
|
+
};
|
|
88
|
+
schemaVersion: string;
|
|
84
89
|
ts: string;
|
|
85
90
|
human: {
|
|
86
91
|
severity: "warn" | "info" | "notice" | "error";
|
|
@@ -92,12 +97,7 @@ export declare const rootEvent: z.ZodObject<{
|
|
|
92
97
|
};
|
|
93
98
|
seq: number;
|
|
94
99
|
eventId: string;
|
|
95
|
-
schemaVersion: string;
|
|
96
100
|
replayability: "deterministic" | "stochastic_recorded" | "stochastic_unreproducible";
|
|
97
|
-
actor: {
|
|
98
|
-
kind: "machine" | "human";
|
|
99
|
-
id?: string | undefined;
|
|
100
|
-
};
|
|
101
101
|
payload?: unknown;
|
|
102
102
|
}, {
|
|
103
103
|
type: string;
|
|
@@ -113,12 +113,12 @@ export declare const rootEvent: z.ZodObject<{
|
|
|
113
113
|
seq: number;
|
|
114
114
|
eventId: string;
|
|
115
115
|
payload?: unknown;
|
|
116
|
-
schemaVersion?: string | undefined;
|
|
117
|
-
replayability?: "deterministic" | "stochastic_recorded" | "stochastic_unreproducible" | undefined;
|
|
118
116
|
actor?: {
|
|
119
117
|
kind?: "machine" | "human" | undefined;
|
|
120
118
|
id?: string | undefined;
|
|
121
119
|
} | undefined;
|
|
120
|
+
schemaVersion?: string | undefined;
|
|
121
|
+
replayability?: "deterministic" | "stochastic_recorded" | "stochastic_unreproducible" | undefined;
|
|
122
122
|
}>;
|
|
123
123
|
export type RootEvent = z.infer<typeof rootEvent>;
|
|
124
124
|
/** append 入参: seq/eventId/ts 由 store 生成. */
|
|
@@ -22,6 +22,8 @@ export declare class EventStore {
|
|
|
22
22
|
private appendLocked;
|
|
23
23
|
/** 读全部事件 (跳过尾部半行/损坏行). */
|
|
24
24
|
readAll(): RootEvent[];
|
|
25
|
+
/** Fail-closed replay for decision policies that cannot safely consume a partial history. */
|
|
26
|
+
readAllStrict(): RootEvent[];
|
|
25
27
|
iterate(fromSeq?: number): Generator<RootEvent>;
|
|
26
28
|
tail(n?: number): RootEvent[];
|
|
27
29
|
/** 截断尾部半行 (崩溃恢复). */
|
|
@@ -2,7 +2,7 @@ import { openSync, writeSync, fsyncSync, closeSync, statSync, readSync, mkdirSyn
|
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { monotonicFactory } from 'ulid';
|
|
4
4
|
import { acquireLock, releaseLock } from '../util/fileLock.js';
|
|
5
|
-
import { readRootEventHistory } from './eventArchive.js';
|
|
5
|
+
import { readRootEventHistory, readRootEventHistoryStrict } from './eventArchive.js';
|
|
6
6
|
// Monotonic ULIDs: plain ulid() randomizes the low 80 bits, so two ids minted in the SAME millisecond can sort
|
|
7
7
|
// out of order — the event log wants time-sortable eventIds (and a flaky CI test proved it). The monotonic
|
|
8
8
|
// factory increments within a millisecond, guaranteeing each successive eventId is strictly greater.
|
|
@@ -76,6 +76,10 @@ export class EventStore {
|
|
|
76
76
|
readAll() {
|
|
77
77
|
return readRootEventHistory(this.path);
|
|
78
78
|
}
|
|
79
|
+
/** Fail-closed replay for decision policies that cannot safely consume a partial history. */
|
|
80
|
+
readAllStrict() {
|
|
81
|
+
return readRootEventHistoryStrict(this.path);
|
|
82
|
+
}
|
|
79
83
|
*iterate(fromSeq = 0) {
|
|
80
84
|
for (const e of this.readAll())
|
|
81
85
|
if (e.seq >= fromSeq)
|