@evomap/evolver-proxy 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.
Files changed (43) hide show
  1. package/dist/bin/evolver-proxy.d.ts +12 -0
  2. package/dist/bin/evolver-proxy.js +257 -56
  3. package/dist/daemon/proxyDaemon.d.ts +12 -0
  4. package/dist/daemon/proxyDaemon.js +313 -18
  5. package/dist/daemon/systemdNotifier.d.ts +2 -0
  6. package/dist/daemon/systemdNotifier.js +11 -1
  7. package/dist/llm/upstream.js +54 -7
  8. package/dist/private/accountAssetCompatibility.d.ts +1 -0
  9. package/dist/private/accountAssetCompatibility.js +3 -3
  10. package/dist/private/adapterLoader.js +4 -3
  11. package/dist/router/messagesRoute.js +9 -3
  12. package/dist/router/providerRoutes.js +7 -3
  13. package/dist/selfUpdate/bootstrap.d.ts +162 -0
  14. package/dist/selfUpdate/bootstrap.js +3524 -0
  15. package/dist/selfUpdate/bootstrapReadiness.d.ts +9 -0
  16. package/dist/selfUpdate/bootstrapReadiness.js +153 -0
  17. package/dist/selfUpdate/builtinKey.d.ts +4 -0
  18. package/dist/selfUpdate/builtinKey.js +16 -0
  19. package/dist/selfUpdate/controllerLifecycleAuthority.d.ts +45 -0
  20. package/dist/selfUpdate/controllerLifecycleAuthority.js +61 -0
  21. package/dist/selfUpdate/executor.d.ts +18 -7
  22. package/dist/selfUpdate/executor.js +159 -59
  23. package/dist/selfUpdate/failureCodes.d.ts +4 -0
  24. package/dist/selfUpdate/failureCodes.js +7 -0
  25. package/dist/selfUpdate/index.d.ts +2 -1
  26. package/dist/selfUpdate/index.js +2 -1
  27. package/dist/selfUpdate/migration.d.ts +158 -0
  28. package/dist/selfUpdate/migration.js +2672 -0
  29. package/dist/selfUpdate/policy.d.ts +19 -2
  30. package/dist/selfUpdate/policy.js +76 -2
  31. package/dist/selfUpdate/recoveryChildStartGate.d.ts +29 -0
  32. package/dist/selfUpdate/recoveryChildStartGate.js +319 -0
  33. package/dist/selfUpdate/releaseBinary.d.ts +3 -0
  34. package/dist/selfUpdate/releaseBinary.js +50 -4
  35. package/dist/selfUpdate/transaction.d.ts +8 -0
  36. package/dist/selfUpdate/transaction.js +166 -18
  37. package/dist/selfUpdate/unixController.d.ts +8 -0
  38. package/dist/selfUpdate/unixController.js +366 -38
  39. package/dist/selfUpdate/windowsController.d.ts +14 -2
  40. package/dist/selfUpdate/windowsController.js +484 -103
  41. package/dist/selfUpdate/windowsUpdater.d.ts +25 -0
  42. package/dist/selfUpdate/windowsUpdater.js +174 -7
  43. package/package.json +3 -3
@@ -39,15 +39,44 @@ function isOpenAiMode(upstreamMode) {
39
39
  * Warn once per process to avoid per-request spam.
40
40
  */
41
41
  let warnedDeprecatedOpenAICompatible = false;
42
+ function hasDeprecatedOpenAICompatibleConfig(env) {
43
+ return [
44
+ env['EVOLVER_OPENAI_COMPATIBLE_BASE_URLS'],
45
+ env['EVOMAP_OPENAI_COMPATIBLE_BASE_URLS'],
46
+ ].some((value) => typeof value === 'string' && value.trim() !== '');
47
+ }
48
+ function explicitOpenAIBaseUrl(env) {
49
+ return env['EVOLVER_LLM_OPENAI_BASE_URL'] || env['EVOMAP_OPENAI_BASE_URL'] || env['OPENAI_BASE_URL'];
50
+ }
51
+ function hasCanonicalOpenAIMigrationPair(env) {
52
+ const baseUrl = env['EVOLVER_LLM_OPENAI_BASE_URL']?.trim();
53
+ if (!baseUrl || !env['EVOLVER_LLM_OPENAI_API_KEY']?.trim())
54
+ return false;
55
+ try {
56
+ normalizeOpenAIBaseUrl(baseUrl);
57
+ return true;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
42
63
  function warnDeprecatedOpenAICompatible(env) {
43
- const deprecatedValue = env['EVOMAP_OPENAI_COMPATIBLE_BASE_URLS'] || env['EVOLVER_OPENAI_COMPATIBLE_BASE_URLS'];
44
- if (!deprecatedValue || warnedDeprecatedOpenAICompatible)
64
+ if (!hasDeprecatedOpenAICompatibleConfig(env) || warnedDeprecatedOpenAICompatible)
45
65
  return;
46
66
  warnedDeprecatedOpenAICompatible = true;
47
67
  console.warn('[proxy] DEPRECATED: EVOMAP_OPENAI_COMPATIBLE_BASE_URLS / EVOLVER_OPENAI_COMPATIBLE_BASE_URLS is ignored for routing. ' +
48
- 'V2 accepts only https://*.api.openai.com/v1 via EVOLVER_LLM_OPENAI_BASE_URL, EVOMAP_OPENAI_BASE_URL, or OPENAI_BASE_URL. ' +
49
- 'LiteLLM / OpenRouter / Azure OpenAI-compatible hosts are not accepted on this OpenAI path — use a reverse proxy in front of api.openai.com or a non-OpenAI provider mode. ' +
50
- 'Remove the deprecated multi-base env var.');
68
+ 'Manual migration is required: explicitly bind one official https://*.api.openai.com/v1 endpoint and its credential ' +
69
+ 'with EVOLVER_LLM_OPENAI_BASE_URL and EVOLVER_LLM_OPENAI_API_KEY. ' +
70
+ 'LiteLLM, OpenRouter, Azure OpenAI, MiniMax, DeepSeek, Moonshot, and other custom OpenAI-compatible hosts have no drop-in V2 route. ' +
71
+ 'Do not reuse their credentials as an OpenAI credential; keep that workload outside this proxy until a supported provider-specific route exists.');
72
+ }
73
+ function assertDeprecatedOpenAICompatibleMigrated(env) {
74
+ warnDeprecatedOpenAICompatible(env);
75
+ if (hasDeprecatedOpenAICompatibleConfig(env) && !hasCanonicalOpenAIMigrationPair(env)) {
76
+ throw new Error('[proxy] manual migration of deprecated OpenAI-compatible base URLs requires an explicit '
77
+ + 'EVOLVER_LLM_OPENAI_BASE_URL and '
78
+ + 'EVOLVER_LLM_OPENAI_API_KEY migration pair before the OpenAI route can start');
79
+ }
51
80
  }
52
81
  /** Test helper: reset once-warn latch (unit tests only). */
53
82
  function resetDeprecatedOpenAICompatibleWarning() {
@@ -77,9 +106,23 @@ function normalizeOpenAIBaseUrl(raw) {
77
106
  }
78
107
  return value;
79
108
  }
109
+ function openAIBaseUrlIdentity(raw) {
110
+ return new URL(normalizeOpenAIBaseUrl(raw)).href;
111
+ }
112
+ function assertDeprecatedOpenAIRequestBaseBound(env, requestBaseUrl) {
113
+ if (!requestBaseUrl || !hasDeprecatedOpenAICompatibleConfig(env))
114
+ return;
115
+ const canonicalBaseUrl = env['EVOLVER_LLM_OPENAI_BASE_URL'];
116
+ if (typeof canonicalBaseUrl !== 'string'
117
+ || openAIBaseUrlIdentity(requestBaseUrl) !== openAIBaseUrlIdentity(canonicalBaseUrl)) {
118
+ throw new Error('[proxy] request-scoped OpenAI base URL must match EVOLVER_LLM_OPENAI_BASE_URL '
119
+ + 'while deprecated OpenAI-compatible routing remains configured');
120
+ }
121
+ }
80
122
  export function resolveOpenAIUpstreamUrl(env = process.env) {
81
- warnDeprecatedOpenAICompatible(env);
82
- return normalizeOpenAIBaseUrl(env['EVOLVER_LLM_OPENAI_BASE_URL'] || env['EVOMAP_OPENAI_BASE_URL'] || env['OPENAI_BASE_URL'] || DEFAULT_OPENAI_UPSTREAM_URL);
123
+ assertDeprecatedOpenAICompatibleMigrated(env);
124
+ const explicitBaseUrl = explicitOpenAIBaseUrl(env);
125
+ return normalizeOpenAIBaseUrl(explicitBaseUrl || DEFAULT_OPENAI_UPSTREAM_URL);
83
126
  }
84
127
  function pathForOpenAIBase(path) {
85
128
  if (path === '/v1')
@@ -478,6 +521,10 @@ export function makeOpenAIUpstream(opts = {}) {
478
521
  const headersTimeoutMs = opts.headersTimeoutMs ?? DEFAULT_HEADERS_TIMEOUT_MS;
479
522
  return async (path, body, callOpts) => {
480
523
  const env = opts.env ?? process.env;
524
+ // A request-scoped base override is routing data, not evidence that the daemon's
525
+ // legacy provider credential/base pair was deliberately migrated.
526
+ assertDeprecatedOpenAICompatibleMigrated(env);
527
+ assertDeprecatedOpenAIRequestBaseBound(env, callOpts.baseUrl);
481
528
  const baseUrl = callOpts.baseUrl ? normalizeOpenAIBaseUrl(callOpts.baseUrl) : resolveOpenAIUpstreamUrl(env);
482
529
  return fetchUpstream(`${baseUrl}${path}`, body, callOpts, buildOpenAIHeaders(callOpts.inboundHeaders, env), fetchImpl, headersTimeoutMs, 'openai', (headers) => contentTypeIncludes(headers, 'text/event-stream'));
483
530
  };
@@ -25,4 +25,5 @@ interface PrivateAccountAssetCompatibilityOptions {
25
25
  * future adapter's native implementation.
26
26
  */
27
27
  export declare function withPrivateAccountAssetCompatibility<T extends object>(hubCapability: T, opts: PrivateAccountAssetCompatibilityOptions): T & PrivateAccountAssetHub;
28
+ export declare function normalizePrivateHubBaseUrl(raw: string, env: Record<string, string | undefined>): string;
28
29
  export {};
@@ -159,14 +159,14 @@ function malformedPublishedPage() {
159
159
  context: `GET ${PRIVATE_PUBLISHED_ASSETS_PATH}`,
160
160
  });
161
161
  }
162
- function normalizePrivateHubBaseUrl(raw, env) {
162
+ export function normalizePrivateHubBaseUrl(raw, env) {
163
163
  const normalized = raw.trim().replace(/\/+$/, '');
164
164
  assertPrivateCompatibilityUrlSecure(normalized, env);
165
165
  const parsed = new URL(normalized);
166
- if (parsed.username || parsed.password || parsed.search || parsed.hash) {
166
+ if (parsed.username || parsed.password || normalized.includes('?') || normalized.includes('#')) {
167
167
  throw new Error('Private Hub URL must not contain credentials, query parameters, or a fragment');
168
168
  }
169
- return normalized;
169
+ return parsed.href.replace(/[/]+$/, '');
170
170
  }
171
171
  function assertPrivateCompatibilityUrlSecure(url, env) {
172
172
  let parsed;
@@ -1,6 +1,6 @@
1
1
  import { hub as hubNs } from '@evomap/evolver-core';
2
2
  import { createHash } from 'node:crypto';
3
- import { withPrivateAccountAssetCompatibility, } from './accountAssetCompatibility.js';
3
+ import { normalizePrivateHubBaseUrl, withPrivateAccountAssetCompatibility, } from './accountAssetCompatibility.js';
4
4
  const DEFAULT_PRIVATE_ADAPTER_MODULE = '@evomap/evolver-adapter-private';
5
5
  export function resolvePrivateEnterpriseToken(env) {
6
6
  return firstEnv(env, 'EVOMAP_ENTERPRISE_TOKEN', 'EVOMAP_PRIVATE_HUB_TOKEN', 'PHUB_ENTERPRISE_TOKEN', 'PRIVATE_HUB_ENTERPRISE_TOKEN');
@@ -33,12 +33,13 @@ export async function connectPrivateProxyHub(opts) {
33
33
  if (!token && !usableInvitationToken && !nodeSecret) {
34
34
  throw new Error('EVOMAP_HUB_MODE=private 需要 A2A_NODE_SECRET / EVOMAP_NODE_SECRET、A2A_INVITATION_TOKEN 或 EVOMAP_ENTERPRISE_TOKEN');
35
35
  }
36
+ const hubUrl = normalizePrivateHubBaseUrl(opts.hubUrl, opts.env);
36
37
  const moduleName = opts.env['EVOMAP_PRIVATE_ADAPTER_MODULE']?.trim() || DEFAULT_PRIVATE_ADAPTER_MODULE;
37
38
  const connectPrivateHub = await loadConnectPrivateHub(moduleName, opts.importer ?? ((specifier) => import(specifier)));
38
39
  const now = opts.now ?? (() => Date.now());
39
40
  const subject = resolvePrivateEnterpriseSubject(opts.env);
40
41
  const baseConnectionOptions = {
41
- hubUrl: opts.hubUrl,
42
+ hubUrl,
42
43
  senderId: opts.senderId,
43
44
  env: opts.env,
44
45
  now,
@@ -79,7 +80,7 @@ export async function connectPrivateProxyHub(opts) {
79
80
  hub.agentDirectory = hubNs.unsupportedAgentDirectoryCapability('private_hub_agent_directory_not_supported');
80
81
  }
81
82
  const compatibleHub = withPrivateAccountAssetCompatibility(hub, {
82
- baseUrl: opts.hubUrl,
83
+ baseUrl: hubUrl,
83
84
  auth,
84
85
  senderId: opts.senderId,
85
86
  env: opts.env,
@@ -235,9 +235,15 @@ function sessionIdFromUserField(value) {
235
235
  }
236
236
  }
237
237
  if (parsed && typeof parsed === 'object') {
238
- const sid = parsed['session_id'];
239
- if (typeof sid === 'string' && sid.length > 0)
240
- return safePlainSessionId(sid);
238
+ // Accept both spellings. The archive-side session key (agentic-trace-pipeline
239
+ // derive_session_key) reads `session_id ?? sessionId` from this same field, so a
240
+ // camelCase producer yields a `cc::<sid>` key upstream while we recorded null —
241
+ // the two datasets then silently fail to join. Both spellings still pass through
242
+ // safePlainSessionId; this widens the accepted key name, never the value guard.
243
+ const raw = parsed['session_id']
244
+ ?? parsed['sessionId'];
245
+ if (typeof raw === 'string' && raw.length > 0)
246
+ return safePlainSessionId(raw);
241
247
  }
242
248
  return '';
243
249
  }
@@ -998,9 +998,13 @@ function sessionIdFromUserField(value) {
998
998
  }
999
999
  }
1000
1000
  if (parsed && typeof parsed === 'object') {
1001
- const sid = parsed['session_id'];
1002
- if (typeof sid === 'string' && sid.length > 0)
1003
- return safePlainSessionId(sid);
1001
+ // Accept both spellings — see the matching note in messagesRoute.ts. The
1002
+ // archive-side session key reads `session_id ?? sessionId` from this field, so
1003
+ // dropping camelCase here costs a join, not just a field.
1004
+ const raw = parsed['session_id']
1005
+ ?? parsed['sessionId'];
1006
+ if (typeof raw === 'string' && raw.length > 0)
1007
+ return safePlainSessionId(raw);
1004
1008
  }
1005
1009
  return '';
1006
1010
  }
@@ -0,0 +1,162 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { util } from '@evomap/evolver-core';
3
+ import { type MigrationOptions } from './migration.js';
4
+ export declare const RECOVERY_CONTROLLER_LIFECYCLE_OWNER_CAPABILITY_ENV = "EVOLVER_INTERNAL_RECOVERY_CONTROLLER_LIFECYCLE_OWNER";
5
+ type BootstrapProcessKill = (pid: number, signal: NodeJS.Signals | 0) => boolean;
6
+ type BootstrapSkipReason = 'already_supervised' | 'already_bootstrapped' | 'unsupported_install_shape' | 'policy_not_auto' | 'bootstrap_disabled' | 'ci_environment' | 'container_environment' | 'recent_failure' | 'migration_ambiguous' | 'bootstrap_attempt_invalid' | 'bootstrap_intent_pending' | 'bootstrap_attempt_pending';
7
+ export interface BootstrapDecision {
8
+ proceed: boolean;
9
+ reason?: BootstrapSkipReason;
10
+ }
11
+ export interface BootstrapOutcome {
12
+ ok: boolean;
13
+ /**
14
+ * 'bootstrapped' / 'bootstrapped_lock_release_unconfirmed' / 'migrated' on success;
15
+ * failure/skip reason otherwise. Migration failures record 'migration_failed' /
16
+ * 'migration_timeout' (both cooldown-worthy).
17
+ */
18
+ reason: string;
19
+ detail?: string;
20
+ /** The child may own manager or IPC state, so the foreground proxy must exit. */
21
+ requiresForegroundExit?: true;
22
+ }
23
+ export interface BootstrapRunOptions {
24
+ env: NodeJS.ProcessEnv;
25
+ platform?: NodeJS.Platform;
26
+ execPath?: string;
27
+ argv1?: string;
28
+ /** Parent force-timeout; it must exceed the child's transaction budget. */
29
+ timeoutMs?: number;
30
+ /** Absolute child deadline offset. Production uses the complete transaction budget. */
31
+ transactionBudgetMs?: number;
32
+ /** Bounded wait for OS confirmation after force-terminating the process tree. */
33
+ terminationGraceMs?: number;
34
+ now?: number;
35
+ exists?: (path: string) => boolean;
36
+ readFile?: (path: string) => string;
37
+ writeFile?: (path: string, content: string) => void;
38
+ spawnFn?: typeof spawn;
39
+ treeKillSpawnFn?: typeof spawn;
40
+ processKill?: BootstrapProcessKill;
41
+ /** Test-only failure seam; production publication always uses the strict exclusive writer. */
42
+ beforeIntentPublish?: () => void;
43
+ /** Test-only crash seam for each durable initial-publication boundary. */
44
+ afterIntentPublicationStep?: (step: 'create' | 'partial_write' | 'file_fsync' | 'link' | 'directory_fsync', path: string) => void;
45
+ /** Test-only crash seams around the atomic terminal/clear state transitions. */
46
+ beforeIntentTerminalFsync?: (path: string) => void;
47
+ afterIntentTerminalPublish?: () => void;
48
+ afterIntentClearRename?: () => void;
49
+ /** Test-only trust seams. Production always uses native owner/DACL validation. */
50
+ assertIntentDirectoryTrust?: (directory: string) => void;
51
+ assertIntentFileTrust?: (path: string) => void;
52
+ assertLegacyProofDirectoryTrust?: (directory: string) => void;
53
+ assertLegacyProofFileTrust?: (path: string) => void;
54
+ afterLegacyProofRead?: (path: string) => void;
55
+ /** Test-only process identity seams; production uses fresh native core observations. */
56
+ readRegistrationProcessStartIdentity?: (pid: number) => util.FileLockProcessStartIdentity | null;
57
+ registrationOwnerProcessStatus?: (owner: Pick<util.FileLockOwnerRecord, 'pid' | 'processStartIdentity'>) => util.FileLockOwnerProcessStatus;
58
+ registrationPublisherProcessStatus?: (publisher: Readonly<{
59
+ pid: number;
60
+ token: string;
61
+ processIdentityDigest: string;
62
+ }>) => util.FileLockOwnerProcessStatus;
63
+ /**
64
+ * Extra seams forwarded to the one-time npm/JS → standalone migration (migration.ts).
65
+ * Bootstrap-level seams (exists/readFile/writeFile/spawnFn/now/execPath) win when both
66
+ * are supplied, so the decision and the migration observe the same injected world.
67
+ */
68
+ migration?: MigrationOptions;
69
+ }
70
+ /** Lifecycle state dir mirror of evolver-cli lifecyclePaths (kept dependency-free across packages). */
71
+ export declare function resolveBootstrapStateDir(env: NodeJS.ProcessEnv): string;
72
+ export declare function withRecoveryControllerLifecycleOwnerCapability(env: NodeJS.ProcessEnv, owner: util.FileLockOwnerRecord): NodeJS.ProcessEnv;
73
+ export interface PreparedRecoveryControllerLifecycleOwnerCapability {
74
+ env: NodeJS.ProcessEnv;
75
+ startupAckToken: string;
76
+ }
77
+ export declare function prepareRecoveryControllerLifecycleOwnerCapability(env: NodeJS.ProcessEnv, owner: util.FileLockOwnerRecord): PreparedRecoveryControllerLifecycleOwnerCapability;
78
+ export declare function clearRecoveryControllerLifecycleOwnerCapability(env: NodeJS.ProcessEnv): void;
79
+ export declare function publishRecoveryControllerLifecycleStartupAttestation(env: NodeJS.ProcessEnv, descriptor?: number): boolean;
80
+ export declare function lifecycleBootstrapStatePresent(env: NodeJS.ProcessEnv, exists?: (path: string) => boolean): boolean;
81
+ export type BootstrapDurableStateOptions = Pick<BootstrapRunOptions, 'exists' | 'readFile' | 'assertIntentFileTrust' | 'assertLegacyProofDirectoryTrust' | 'assertLegacyProofFileTrust' | 'afterLegacyProofRead' | 'registrationOwnerProcessStatus' | 'now'> & {
82
+ /** Exact owner currently holding the shared lifecycle mutation lock. */
83
+ expectedRecoveryOwner?: util.FileLockOwnerRecord;
84
+ };
85
+ export interface LifecycleBootstrapOwnerLease {
86
+ readonly path: string;
87
+ readonly owner: util.FileLockOwnerRecord;
88
+ assertOwned(): void;
89
+ armProcess(pid: number): util.FileLockOwnerRecord;
90
+ disarmProcess(): void;
91
+ retainProcess(): void;
92
+ transferToProcess(pid: number): util.FileLockOwnerRecord;
93
+ release(): void;
94
+ }
95
+ export declare function assertSupervisedLifecycleBootstrapState(env: NodeJS.ProcessEnv, options?: BootstrapDurableStateOptions & {
96
+ requireLifecycleState?: boolean;
97
+ /** Test-only parent identity seam; production always binds to process.ppid. */
98
+ recoveryControllerParentPid?: number;
99
+ }): void;
100
+ /**
101
+ * Revalidate the narrow parent-owned activation window used by a newly launched recovery
102
+ * controller. This deliberately rejects committed supervision: callers use it only as delegated
103
+ * authority while the lifecycle parent still owns the mutation lock and is waiting for readiness.
104
+ */
105
+ export declare function assertActiveSupervisedLifecycleBootstrapDelegation(env: NodeJS.ProcessEnv, options?: BootstrapDurableStateOptions): void;
106
+ /**
107
+ * Revalidate a transaction-bound launcher before any self-update operation. Unlike the startup
108
+ * assertion above, this never accepts the narrow activating window used to publish readiness.
109
+ */
110
+ export declare function assertCommittedLifecycleBootstrapState(env: NodeJS.ProcessEnv, options?: BootstrapDurableStateOptions): void;
111
+ /**
112
+ * Serialize self-update with every lifecycle bootstrap, recovery, and manual-transition writer.
113
+ * Acquisition is deliberately fail-fast: a heartbeat must not block the daemon while another
114
+ * lifecycle owner is active. Transaction-bound launchers revalidate the committed receipt under
115
+ * the exact acquired generation; legacy launchers still hold and recheck the shared owner lock.
116
+ */
117
+ export declare function acquireLifecycleBootstrapOwnerLease(env: NodeJS.ProcessEnv, lockOptions?: {
118
+ maxTries?: number;
119
+ waitMs?: number;
120
+ }): LifecycleBootstrapOwnerLease;
121
+ export declare function looksLikeContainer(exists: (path: string) => boolean, readFile: (path: string) => string): boolean;
122
+ /** True when a recent bootstrap/migration attempt failed within the cooldown window. */
123
+ export declare function recentBootstrapFailure(env: NodeJS.ProcessEnv, optionsOrReadFile: Pick<BootstrapRunOptions, 'exists' | 'readFile' | 'assertIntentFileTrust'> | ((path: string) => string), now: number): boolean;
124
+ /**
125
+ * Decide whether an unsupervised (degraded) startup should attempt first-run bootstrap.
126
+ * Pure — filesystem access is injectable for tests.
127
+ */
128
+ export declare function shouldBootstrap(env: NodeJS.ProcessEnv, platform?: NodeJS.Platform, options?: Pick<BootstrapRunOptions, 'exists' | 'readFile' | 'now' | 'execPath' | 'assertIntentFileTrust'>): BootstrapDecision;
129
+ /**
130
+ * Resolve the `lifecycle bootstrap` invocation for the current install shape: standalone binary,
131
+ * CLI entry through node, or the npm-installed @evomap/evolver-cli sibling. Returns undefined when
132
+ * no CLI can be located. The caller may continue only after confirming durable state is clean.
133
+ */
134
+ export declare function resolveBootstrapCliInvocation(options?: Pick<BootstrapRunOptions, 'execPath' | 'argv1' | 'exists'>): {
135
+ command: string;
136
+ args: string[];
137
+ } | undefined;
138
+ /** Best-effort attempt marker; never throws — bootstrap bookkeeping must not break startup. */
139
+ export declare function recordBootstrapAttempt(env: NodeJS.ProcessEnv, outcome: BootstrapOutcome, options?: Pick<BootstrapRunOptions, 'writeFile' | 'now'>): void;
140
+ /** Spawn `evolver lifecycle bootstrap` and await its result within a bounded timeout. */
141
+ export declare function runBootstrap(options: BootstrapRunOptions): Promise<BootstrapOutcome>;
142
+ export type DegradedStartupBootstrapResult = {
143
+ disposition: 'continue';
144
+ handedOver: false;
145
+ message: string;
146
+ } | {
147
+ disposition: 'handoff';
148
+ handedOver: true;
149
+ exitCode: 0;
150
+ message: string;
151
+ } | {
152
+ disposition: 'fail_closed';
153
+ handedOver: false;
154
+ exitCode: 1;
155
+ message: string;
156
+ };
157
+ /**
158
+ * Orchestrate bootstrap for a degraded (default-auto, unsupervised) startup: decide, attempt,
159
+ * record, and produce the operator message. Never throws.
160
+ */
161
+ export declare function bootstrapDegradedSelfUpdateStartup(env: NodeJS.ProcessEnv, platform?: NodeJS.Platform, options?: Omit<BootstrapRunOptions, 'env' | 'platform'>): Promise<DegradedStartupBootstrapResult>;
162
+ export {};