minovative-mind-cli 2.11.5 → 2.13.0

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/README.md +27 -1
  2. package/dist/commands/chat.js +3 -1
  3. package/dist/commands/eval.d.ts +22 -0
  4. package/dist/commands/eval.js +141 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/services/agent/slashCommands.js +4 -2
  8. package/dist/services/agent/toolLoop.d.ts +4 -0
  9. package/dist/services/agent/toolLoop.js +61 -10
  10. package/dist/services/agent-tools.d.ts +5 -5
  11. package/dist/services/agent-tools.js +150 -11
  12. package/dist/services/contextAgent.d.ts +1 -0
  13. package/dist/services/contextAgent.js +29 -6
  14. package/dist/services/ideOptimization.d.ts +15 -0
  15. package/dist/services/ideOptimization.js +169 -0
  16. package/dist/services/metrics.d.ts +10 -0
  17. package/dist/services/metrics.js +24 -0
  18. package/dist/services/orchestration/messageBus.d.ts +81 -41
  19. package/dist/services/orchestration/messageBus.js +242 -98
  20. package/dist/services/orchestration/orchestrator.d.ts +6 -6
  21. package/dist/services/orchestration/orchestrator.js +32 -21
  22. package/dist/services/orchestration/scopedTools.d.ts +7 -1
  23. package/dist/services/orchestration/scopedTools.js +45 -9
  24. package/dist/services/orchestration/subAgent.d.ts +19 -17
  25. package/dist/services/orchestration/subAgent.js +98 -81
  26. package/dist/services/swebench/gitDiffExtractor.d.ts +57 -0
  27. package/dist/services/swebench/gitDiffExtractor.js +209 -0
  28. package/dist/services/swebench/index.d.ts +4 -0
  29. package/dist/services/swebench/index.js +4 -0
  30. package/dist/services/swebench/instanceLoader.d.ts +21 -0
  31. package/dist/services/swebench/instanceLoader.js +171 -0
  32. package/dist/services/swebench/sweBenchRunnerService.d.ts +38 -0
  33. package/dist/services/swebench/sweBenchRunnerService.js +618 -0
  34. package/dist/services/swebench/types.d.ts +167 -0
  35. package/dist/services/swebench/types.js +7 -0
  36. package/dist/services/verificationService.js +3 -0
  37. package/dist/utils/fuzzyMatch.d.ts +51 -21
  38. package/dist/utils/fuzzyMatch.js +37 -122
  39. package/dist/utils/projectStorage.js +10 -5
  40. package/dist/utils/systemPrompts.d.ts +1 -1
  41. package/dist/utils/systemPrompts.js +10 -4
  42. package/oclif.manifest.json +137 -1
  43. package/package.json +1 -1
@@ -51,8 +51,15 @@ function boundToolOutput(output) {
51
51
  truncationMarker: `\n\n... [Tool output truncated: exceeded ${MAX_TOOL_OUTPUT_TOKENS} tokens limit] ...\n`,
52
52
  });
53
53
  }
54
- async function detectProjectType(workspaceRoot) {
54
+ export async function detectProjectType(workspaceRoot) {
55
55
  const types = [];
56
+ // Detect Host Platform & Architecture
57
+ const platformName = process.platform === 'darwin' ? 'Darwin' : process.platform === 'win32' ? 'Windows' : 'Linux';
58
+ const isAppleSilicon = process.platform === 'darwin' && process.arch === 'arm64';
59
+ const hostDesc = isAppleSilicon
60
+ ? `Host: ${platformName} ${process.arch} (Apple Silicon)`
61
+ : `Host: ${platformName} ${process.arch}`;
62
+ types.push(hostDesc);
56
63
  const fileExists = async (fileName) => {
57
64
  try {
58
65
  await fs.access(path.join(workspaceRoot, fileName));
@@ -62,6 +69,13 @@ async function detectProjectType(workspaceRoot) {
62
69
  return false;
63
70
  }
64
71
  };
72
+ // C / C++ / Native Build Toolchains
73
+ if (await fileExists('CMakeLists.txt'))
74
+ types.push('CMake');
75
+ if (await fileExists('Makefile'))
76
+ types.push('Makefile');
77
+ if (await fileExists('meson.build'))
78
+ types.push('Meson');
65
79
  // Node.js Ecosystem
66
80
  if (await fileExists('package.json')) {
67
81
  types.push('Node.js');
@@ -93,17 +107,17 @@ async function detectProjectType(workspaceRoot) {
93
107
  types.push('NestJS');
94
108
  if (deps['vite'])
95
109
  types.push('Vite');
96
- if (deps['tailwindcss'])
97
- types.push('Tailwind CSS');
98
- if (deps['firebase'])
99
- types.push('Firebase');
100
110
  }
101
111
  catch { }
102
112
  if (await fileExists('tsconfig.json'))
103
113
  types.push('TypeScript');
104
114
  }
105
115
  // Python Ecosystem
106
- if ((await fileExists('pyproject.toml')) || (await fileExists('requirements.txt')) || (await fileExists('Pipfile'))) {
116
+ if ((await fileExists('pyproject.toml')) ||
117
+ (await fileExists('requirements.txt')) ||
118
+ (await fileExists('setup.py')) ||
119
+ (await fileExists('setup.cfg')) ||
120
+ (await fileExists('Pipfile'))) {
107
121
  types.push('Python');
108
122
  try {
109
123
  const reqs = (await fileExists('requirements.txt'))
@@ -119,6 +133,15 @@ async function detectProjectType(workspaceRoot) {
119
133
  types.push('Flask');
120
134
  if (combined.includes('fastapi'))
121
135
  types.push('FastAPI');
136
+ if (combined.includes('cython') || (await fileExists('setup.py'))) {
137
+ try {
138
+ const files = await fs.readdir(workspaceRoot);
139
+ if (files.some((f) => f.endsWith('.pyx') || f.endsWith('.pxd') || f.endsWith('.c') || f.endsWith('.cpp'))) {
140
+ types.push('Cython/C-Extensions');
141
+ }
142
+ }
143
+ catch { }
144
+ }
122
145
  }
123
146
  catch { }
124
147
  }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Optimizes the `.vscode/settings.json` file in the given workspace directory.
3
+ * Performs an idempotent, non-destructive merge that preserves all existing user settings,
4
+ * formatting preferences, and custom watcher rules.
5
+ *
6
+ * @param targetRoot - The workspace root directory to optimize.
7
+ */
8
+ export declare function optimizeSingleWorkspaceIDESettings(targetRoot: string): Promise<void>;
9
+ /**
10
+ * Automatically and silently optimizes IDE settings and ignore rules across the primary workspace
11
+ * and all registered sub-workspaces in the background on startup.
12
+ *
13
+ * @param workspaceRoot - The primary workspace root directory.
14
+ */
15
+ export declare function optimizeWorkspaceIDESettings(workspaceRoot: string): Promise<void>;
@@ -0,0 +1,169 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { debugLog } from '../utils/logger.js';
4
+ import { ensureIgnored } from '../utils/projectStorage.js';
5
+ import { workspaceRegistry } from './workspaceRegistry.js';
6
+ /**
7
+ * Recommended file watcher exclusion patterns to prevent IDE file watchers
8
+ * (fsevents, inotify, ReadDirectoryChangesW) and Language Server Protocol indexers
9
+ * from churning CPU on internal CLI state, logs, and temporary scratch files.
10
+ */
11
+ const RECOMMENDED_WATCHER_EXCLUDES = {
12
+ '**/.minovativemind/**': true,
13
+ '**/.tmp/**': true,
14
+ '**/tmp/**': true,
15
+ '**/scratch/**': true,
16
+ };
17
+ /**
18
+ * Recommended search exclusion patterns to keep agent internal data and temp files
19
+ * out of fuzzy file search (Cmd+P) and global text search (Cmd+Shift+F).
20
+ */
21
+ const RECOMMENDED_SEARCH_EXCLUDES = {
22
+ '**/.minovativemind': true,
23
+ '**/.tmp': true,
24
+ '**/scratch': true,
25
+ };
26
+ /**
27
+ * Safely strips single-line and multi-line comments and trailing commas from a JSONC string
28
+ * while strictly preserving string literals (such as glob patterns containing "//").
29
+ */
30
+ function parseJsonc(content) {
31
+ let insideString = false;
32
+ let escaped = false;
33
+ let result = '';
34
+ for (let i = 0; i < content.length; i++) {
35
+ const char = content[i];
36
+ const nextChar = content[i + 1];
37
+ if (insideString) {
38
+ result += char;
39
+ if (char === '\\' && !escaped) {
40
+ escaped = true;
41
+ }
42
+ else {
43
+ if (char === '"' && !escaped) {
44
+ insideString = false;
45
+ }
46
+ escaped = false;
47
+ }
48
+ continue;
49
+ }
50
+ if (char === '"') {
51
+ insideString = true;
52
+ result += char;
53
+ continue;
54
+ }
55
+ // Single-line comment // outside strings
56
+ if (char === '/' && nextChar === '/') {
57
+ const lineEnd = content.indexOf('\n', i + 2);
58
+ if (lineEnd === -1) {
59
+ break;
60
+ }
61
+ i = lineEnd - 1;
62
+ continue;
63
+ }
64
+ // Block comment /* ... */ outside strings
65
+ if (char === '/' && nextChar === '*') {
66
+ const blockEnd = content.indexOf('*/', i + 2);
67
+ if (blockEnd === -1) {
68
+ break;
69
+ }
70
+ i = blockEnd + 1;
71
+ continue;
72
+ }
73
+ result += char;
74
+ }
75
+ const sanitized = result.replace(/,\s*([}\]])/g, '$1').trim();
76
+ if (!sanitized) {
77
+ return {};
78
+ }
79
+ return JSON.parse(sanitized);
80
+ }
81
+ /**
82
+ * Optimizes the `.vscode/settings.json` file in the given workspace directory.
83
+ * Performs an idempotent, non-destructive merge that preserves all existing user settings,
84
+ * formatting preferences, and custom watcher rules.
85
+ *
86
+ * @param targetRoot - The workspace root directory to optimize.
87
+ */
88
+ export async function optimizeSingleWorkspaceIDESettings(targetRoot) {
89
+ try {
90
+ const vscodeDir = path.join(targetRoot, '.vscode');
91
+ const settingsPath = path.join(vscodeDir, 'settings.json');
92
+ let settings = {};
93
+ let fileExisted = false;
94
+ if (fs.existsSync(settingsPath)) {
95
+ try {
96
+ const raw = await fs.promises.readFile(settingsPath, 'utf-8');
97
+ settings = parseJsonc(raw);
98
+ fileExisted = true;
99
+ }
100
+ catch (err) {
101
+ debugLog(`Failed to parse existing .vscode/settings.json at ${settingsPath}: ${err instanceof Error ? err.message : String(err)}`);
102
+ return;
103
+ }
104
+ }
105
+ let modified = false;
106
+ // 1. Ensure files.watcherExclude
107
+ const watcherExclude = typeof settings['files.watcherExclude'] === 'object' && settings['files.watcherExclude'] !== null
108
+ ? { ...settings['files.watcherExclude'] }
109
+ : {};
110
+ for (const [pattern, val] of Object.entries(RECOMMENDED_WATCHER_EXCLUDES)) {
111
+ if (watcherExclude[pattern] === undefined) {
112
+ watcherExclude[pattern] = val;
113
+ modified = true;
114
+ }
115
+ }
116
+ if (modified || settings['files.watcherExclude'] === undefined) {
117
+ settings['files.watcherExclude'] = watcherExclude;
118
+ }
119
+ // 2. Ensure search.exclude
120
+ const searchExclude = typeof settings['search.exclude'] === 'object' && settings['search.exclude'] !== null
121
+ ? { ...settings['search.exclude'] }
122
+ : {};
123
+ for (const [pattern, val] of Object.entries(RECOMMENDED_SEARCH_EXCLUDES)) {
124
+ if (searchExclude[pattern] === undefined) {
125
+ searchExclude[pattern] = val;
126
+ modified = true;
127
+ }
128
+ }
129
+ if (modified || settings['search.exclude'] === undefined) {
130
+ settings['search.exclude'] = searchExclude;
131
+ }
132
+ // Only write if changes were made or if .vscode directory exists and settings are missing
133
+ if (modified) {
134
+ if (!fs.existsSync(vscodeDir)) {
135
+ await fs.promises.mkdir(vscodeDir, { recursive: true });
136
+ }
137
+ await fs.promises.writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
138
+ debugLog(`Optimized IDE settings at ${settingsPath} (fileExisted=${fileExisted})`);
139
+ }
140
+ }
141
+ catch (err) {
142
+ debugLog(`Error optimizing IDE settings for ${targetRoot}: ${err instanceof Error ? err.message : String(err)}`);
143
+ }
144
+ }
145
+ /**
146
+ * Automatically and silently optimizes IDE settings and ignore rules across the primary workspace
147
+ * and all registered sub-workspaces in the background on startup.
148
+ *
149
+ * @param workspaceRoot - The primary workspace root directory.
150
+ */
151
+ export async function optimizeWorkspaceIDESettings(workspaceRoot) {
152
+ try {
153
+ // 1. Ensure ignore rules (.gitignore, .dockerignore, .minovativemindignore) are updated
154
+ ensureIgnored(workspaceRoot);
155
+ // 2. Optimize primary workspace IDE settings
156
+ await optimizeSingleWorkspaceIDESettings(workspaceRoot);
157
+ // 3. Optimize registered sub-workspaces if present
158
+ const subWorkspaces = workspaceRegistry.list();
159
+ for (const workspace of subWorkspaces) {
160
+ if (workspace.absolutePath && workspace.absolutePath !== workspaceRoot && fs.existsSync(workspace.absolutePath)) {
161
+ ensureIgnored(workspace.absolutePath);
162
+ await optimizeSingleWorkspaceIDESettings(workspace.absolutePath);
163
+ }
164
+ }
165
+ }
166
+ catch (err) {
167
+ debugLog(`Background IDE optimization encountered an error: ${err instanceof Error ? err.message : String(err)}`);
168
+ }
169
+ }
@@ -19,6 +19,8 @@ export interface MetricCollector {
19
19
  recordCacheHit(cacheType?: 'investigation' | 'read'): void;
20
20
  recordCacheMiss(cacheType?: 'investigation' | 'read'): void;
21
21
  recordCachePerformance(cacheType: 'investigation' | 'read', durationMs: number): void;
22
+ recordCircuitBreakerTrip?(): void;
23
+ recordPrunedLogVolume?(lines: number, chars: number): void;
22
24
  recordWriteFailure?(): void;
23
25
  recordModifyFailure?(): void;
24
26
  recordToolFailure?(toolName?: string): void;
@@ -33,3 +35,11 @@ export declare function getTurnTotals(): {
33
35
  outputTokens: number;
34
36
  cachedTokens: number;
35
37
  };
38
+ export declare function recordRecoveryCircuitBreakerTrip(): void;
39
+ export declare function recordRecoveryPrunedLogVolume(lines: number, chars: number): void;
40
+ export declare function getRecoveryMetrics(): {
41
+ circuitBreakerTrips: number;
42
+ prunedLines: number;
43
+ prunedChars: number;
44
+ };
45
+ export declare function resetRecoveryMetrics(): void;
@@ -8,6 +8,9 @@ export function getMetricCollector() {
8
8
  let turnPromptTokens = 0;
9
9
  let turnOutputTokens = 0;
10
10
  let turnCachedTokens = 0;
11
+ let sessionCircuitBreakerTrips = 0;
12
+ let sessionPrunedLines = 0;
13
+ let sessionPrunedChars = 0;
11
14
  export function resetTurnAccumulator() {
12
15
  turnPromptTokens = 0;
13
16
  turnOutputTokens = 0;
@@ -23,3 +26,24 @@ export function accumulateUsage(usage) {
23
26
  export function getTurnTotals() {
24
27
  return { promptTokens: turnPromptTokens, outputTokens: turnOutputTokens, cachedTokens: turnCachedTokens };
25
28
  }
29
+ export function recordRecoveryCircuitBreakerTrip() {
30
+ sessionCircuitBreakerTrips++;
31
+ globalCollector?.recordCircuitBreakerTrip?.();
32
+ }
33
+ export function recordRecoveryPrunedLogVolume(lines, chars) {
34
+ sessionPrunedLines += lines;
35
+ sessionPrunedChars += chars;
36
+ globalCollector?.recordPrunedLogVolume?.(lines, chars);
37
+ }
38
+ export function getRecoveryMetrics() {
39
+ return {
40
+ circuitBreakerTrips: sessionCircuitBreakerTrips,
41
+ prunedLines: sessionPrunedLines,
42
+ prunedChars: sessionPrunedChars,
43
+ };
44
+ }
45
+ export function resetRecoveryMetrics() {
46
+ sessionCircuitBreakerTrips = 0;
47
+ sessionPrunedLines = 0;
48
+ sessionPrunedChars = 0;
49
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Two-Layer Message Bus for Sub-Agent Orchestration.
2
+ * @file Two-Layer Message Bus for Sub-Agent Orchestration.
3
3
  *
4
4
  * Provides the core inter-agent communication primitive for the orchestration system.
5
5
  * Two layers of communication:
@@ -68,6 +68,27 @@ export interface CompletionSignal {
68
68
  summary: string;
69
69
  exports: Record<string, string>;
70
70
  }
71
+ export declare const MUTATING_TOOLS: Set<string>;
72
+ export interface BusQueryOptions {
73
+ agentId: string;
74
+ file?: string;
75
+ fromAgent?: string;
76
+ type?: 'discovery' | 'warning' | 'request' | 'completion';
77
+ onlyMutations?: boolean;
78
+ targetFiles?: string[];
79
+ dependsOn?: string[];
80
+ advanceCursor?: boolean;
81
+ }
82
+ interface CoalescedEntry {
83
+ agentId: string;
84
+ tool: string;
85
+ target: string;
86
+ action: string;
87
+ count: number;
88
+ status: 'success' | 'error';
89
+ resultSummary?: string;
90
+ }
91
+ export declare function coalesceActivityEntries(entries: ActivityEntry[]): CoalescedEntry[];
71
92
  /**
72
93
  * Two-layer, disk-backed message bus for sub-agent coordination.
73
94
  *
@@ -79,41 +100,35 @@ export interface CompletionSignal {
79
100
  * a CLI crash and resume from the last known state.
80
101
  */
81
102
  export declare class MessageBus {
82
- private activityLog;
83
- private signals;
103
+ /** Maximum semantic signals any single agent can post */
104
+ static readonly MAX_SIGNALS_PER_AGENT = 50;
84
105
  private activityCursors;
85
- private signalCursors;
86
- private persistQueue;
106
+ private activityLog;
87
107
  private readonly persistPath;
108
+ private persistQueue;
109
+ private signalCursors;
110
+ private signals;
88
111
  private readonly workspaceRoot;
89
- /** Maximum semantic signals any single agent can post */
90
- static readonly MAX_SIGNALS_PER_AGENT = 50;
91
112
  constructor(workspaceRoot: string, conversationId: string);
92
113
  /**
93
- * Records a tool execution into the activity log. Called by the scoped tool
94
- * wrapper in `scopedTools.ts` zero cost to the agent.
114
+ * Formats a batch of activity entries into a compact, human-readable string
115
+ * without token-wasteful column whitespace padding. Coalesces repeated actions.
95
116
  */
96
- logActivity(entry: ActivityEntry): void;
117
+ static formatActivityEntries(entries: ActivityEntry[]): string;
97
118
  /**
98
- * Posts a semantic signal from an agent. These cost tokens (the agent calls
99
- * `post_message`) but carry intent that raw tool logs cannot express.
100
- *
101
- * Enforces per-agent signal cap to prevent runaway agents from flooding the bus.
102
- *
103
- * @returns `true` if the signal was accepted, `false` if the agent hit the cap.
119
+ * Formats semantic signals into a readable string for agent context injection.
104
120
  */
105
- postSignal(signal: BusSignal): boolean;
121
+ static formatSignals(signals: BusSignal[]): string;
106
122
  /**
107
- * Retrieves all unread activity entries and semantic signals for a specific agent.
108
- * Advances the agent's cursors so subsequent calls return only new entries.
109
- *
110
- * Filters out the requesting agent's own entries (an agent doesn't need to
111
- * re-read its own tool logs or signals).
123
+ * Clears all bus state and removes the persistence file.
124
+ * Called when orchestration completes successfully (no crash recovery needed).
112
125
  */
113
- getUnread(agentId: string): {
114
- activities: ActivityEntry[];
115
- signals: BusSignal[];
116
- };
126
+ cleanup(): Promise<void>;
127
+ /**
128
+ * Returns all activity entries for a specific agent (used for dead agent
129
+ * recovery — collecting partial progress before re-dispatch).
130
+ */
131
+ getAgentActivity(agentId: string): ActivityEntry[];
117
132
  /**
118
133
  * Returns the complete, unfiltered bus state for PM reconciliation.
119
134
  * Used after all sub-agents complete to give the PM full visibility.
@@ -122,11 +137,6 @@ export declare class MessageBus {
122
137
  activities: ActivityEntry[];
123
138
  signals: BusSignal[];
124
139
  };
125
- /**
126
- * Returns all activity entries for a specific agent (used for dead agent
127
- * recovery — collecting partial progress before re-dispatch).
128
- */
129
- getAgentActivity(agentId: string): ActivityEntry[];
130
140
  /**
131
141
  * Returns the total number of activity entries and signals in the bus.
132
142
  * Used for terminal display and diagnostics.
@@ -136,14 +146,48 @@ export declare class MessageBus {
136
146
  signalCount: number;
137
147
  };
138
148
  /**
139
- * Formats a batch of activity entries into a compact, human-readable string
140
- * suitable for injection into an agent's context window.
149
+ * Retrieves all unread activity entries and semantic signals for a specific agent.
150
+ * Defaults to state-mutating events and unicast signal routing to eliminate token noise.
141
151
  */
142
- static formatActivityEntries(entries: ActivityEntry[]): string;
152
+ getUnread(agentId: string, options?: {
153
+ onlyMutations?: boolean;
154
+ advanceCursor?: boolean;
155
+ }): {
156
+ activities: ActivityEntry[];
157
+ signals: BusSignal[];
158
+ };
143
159
  /**
144
- * Formats semantic signals into a readable string for agent context injection.
160
+ * Records a tool execution into the activity log. Called by the scoped tool
161
+ * wrapper in `scopedTools.ts` — zero cost to the agent.
145
162
  */
146
- static formatSignals(signals: BusSignal[]): string;
163
+ logActivity(entry: ActivityEntry): void;
164
+ /**
165
+ * Peeks urgent signals (breaking warnings or direct requests) that are relevant
166
+ * to a sub-agent's task scope without advancing its cursor. Used for in-band notice
167
+ * delivery in scopedTools.
168
+ */
169
+ peekUrgentSignals(agentId: string, scope?: {
170
+ targetFiles?: string[];
171
+ dependsOn?: string[];
172
+ }): BusSignal[];
173
+ /**
174
+ * Posts a semantic signal from an agent. These cost tokens (the agent calls
175
+ * `post_message`) but carry intent that raw tool logs cannot express.
176
+ *
177
+ * Enforces per-agent signal cap to prevent runaway agents from flooding the bus.
178
+ *
179
+ * @returns `true` if the signal was accepted, `false` if the agent hit the cap.
180
+ */
181
+ postSignal(signal: BusSignal): boolean;
182
+ /**
183
+ * Performs an intelligent, scoped query against unread bus activity and signals.
184
+ * Filters out read-only noise for peer sub-agents, enforces unicast delivery for targeted
185
+ * requests, and supports selective querying by file, agent, or signal type.
186
+ */
187
+ queryBus(options: BusQueryOptions): {
188
+ activities: ActivityEntry[];
189
+ signals: BusSignal[];
190
+ };
147
191
  /**
148
192
  * Writes the full bus state to disk atomically. Called after every mutation
149
193
  * to ensure crash resilience. Uses fire-and-forget to avoid blocking the
@@ -155,9 +199,5 @@ export declare class MessageBus {
155
199
  * If the file doesn't exist or is corrupted, starts with a clean slate.
156
200
  */
157
201
  private restoreFromDisk;
158
- /**
159
- * Clears all bus state and removes the persistence file.
160
- * Called when orchestration completes successfully (no crash recovery needed).
161
- */
162
- cleanup(): Promise<void>;
163
202
  }
203
+ export {};