@mondaydotcomorg/atp-server 0.21.6 → 0.23.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mondaydotcomorg/atp-server",
3
- "version": "0.21.6",
3
+ "version": "0.23.0",
4
4
  "description": "Server implementation for Agent Tool Protocol",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -49,11 +49,11 @@
49
49
  "@babel/parser": "^7.26.0",
50
50
  "@babel/traverse": "^7.26.0",
51
51
  "@babel/types": "^7.26.0",
52
- "@mondaydotcomorg/atp-compiler": "0.19.26",
53
- "@mondaydotcomorg/atp-protocol": "0.19.22",
54
- "@mondaydotcomorg/atp-provenance": "0.19.21",
55
- "@mondaydotcomorg/atp-providers": "0.19.22",
56
- "@mondaydotcomorg/atp-runtime": "0.19.21",
52
+ "@mondaydotcomorg/atp-compiler": "0.21.0",
53
+ "@mondaydotcomorg/atp-protocol": "0.21.0",
54
+ "@mondaydotcomorg/atp-provenance": "0.21.0",
55
+ "@mondaydotcomorg/atp-providers": "0.21.0",
56
+ "@mondaydotcomorg/atp-runtime": "0.21.0",
57
57
  "@opentelemetry/api": "^1.9.0",
58
58
  "@opentelemetry/auto-instrumentations-node": "^0.66.0",
59
59
  "@opentelemetry/core": "^2.2.0",
@@ -18,10 +18,15 @@ import {
18
18
  resumablePromiseAll,
19
19
  resumablePromiseAllSettled,
20
20
  batchParallel,
21
+ initializeCheckpointRuntime,
22
+ initializeCheckpointRuntimeWithProvenance,
23
+ cleanupCheckpointRuntime,
24
+ getCheckpointRuntime,
25
+ getCheckpointDataForError,
21
26
  type TransformResult,
22
27
  type DetectionResult,
23
28
  type ICompiler,
24
- type CacheStats,
29
+ type CheckpointProvenanceMetadata,
25
30
  } from '@mondaydotcomorg/atp-compiler';
26
31
  import { ATP_COMPILER_ENABLED, ATP_BATCH_SIZE_THRESHOLD } from './constants.js';
27
32
 
@@ -31,8 +36,11 @@ import { ATP_COMPILER_ENABLED, ATP_BATCH_SIZE_THRESHOLD } from './constants.js';
31
36
  class ATPCompilerAdapter implements ICompiler {
32
37
  private compiler: ATPCompiler;
33
38
 
34
- constructor(config: { enableBatchParallel: boolean; batchSizeThreshold: number }) {
35
- this.compiler = new ATPCompiler(config);
39
+ constructor(config: { enableBatchParallel: boolean; batchSizeThreshold: number; enableOperationCheckpoints?: boolean }) {
40
+ this.compiler = new ATPCompiler({
41
+ ...config,
42
+ enableOperationCheckpoints: config.enableOperationCheckpoints ?? true,
43
+ });
36
44
  }
37
45
 
38
46
  detect(code: string): DetectionResult {
@@ -58,8 +66,11 @@ class ATPCompilerAdapter implements ICompiler {
58
66
  class PluggableCompilerAdapter implements ICompiler {
59
67
  private compiler: ReturnType<typeof createDefaultCompiler>;
60
68
 
61
- constructor(config: { enableBatchParallel: boolean; batchSizeThreshold: number }) {
62
- this.compiler = createDefaultCompiler(config);
69
+ constructor(config: { enableBatchParallel: boolean; batchSizeThreshold: number; enableOperationCheckpoints?: boolean }) {
70
+ this.compiler = createDefaultCompiler({
71
+ ...config,
72
+ enableOperationCheckpoints: config.enableOperationCheckpoints ?? true,
73
+ });
63
74
  }
64
75
 
65
76
  async detect(code: string): Promise<DetectionResult> {
@@ -84,7 +95,7 @@ class PluggableCompilerAdapter implements ICompiler {
84
95
  * This is where you can easily add new compiler types
85
96
  */
86
97
  class CompilerFactory {
87
- static create(config: { enableBatchParallel: boolean; batchSizeThreshold: number }): ICompiler {
98
+ static create(config: { enableBatchParallel: boolean; batchSizeThreshold: number; enableOperationCheckpoints?: boolean }): ICompiler {
88
99
  const compilerType = process.env.ATP_USE_PLUGGABLE_COMPILER === 'true' ? 'pluggable' : 'atp';
89
100
 
90
101
  switch (compilerType) {
@@ -173,6 +184,16 @@ export function getCompilerRuntime() {
173
184
  };
174
185
  }
175
186
 
187
+ // Re-export checkpoint functions for executor use
188
+ export {
189
+ initializeCheckpointRuntime,
190
+ initializeCheckpointRuntimeWithProvenance,
191
+ cleanupCheckpointRuntime,
192
+ getCheckpointRuntime,
193
+ getCheckpointDataForError,
194
+ type CheckpointProvenanceMetadata,
195
+ };
196
+
176
197
  export async function transformCodeWithCompiler(
177
198
  code: string,
178
199
  executionId: string,
@@ -202,13 +223,19 @@ export async function transformCodeWithCompiler(
202
223
  // Detect patterns (abstracted sync/async handling)
203
224
  const detection = await compiler.detect(code);
204
225
 
226
+ // With enableOperationCheckpoints, we always need to transform to wrap API calls
227
+ const enableCheckpoints = process.env.ATP_DISABLE_CHECKPOINTS !== 'true';
228
+ const needsTransform = detection.needsTransform || enableCheckpoints;
229
+
205
230
  executionLogger.info('ATP Compiler detection result', {
206
231
  needsTransform: detection.needsTransform,
207
232
  patterns: detection.patterns,
208
233
  batchable: detection.batchableParallel,
234
+ checkpointsEnabled: enableCheckpoints,
235
+ willTransform: needsTransform,
209
236
  });
210
237
 
211
- if (detection.needsTransform) {
238
+ if (needsTransform) {
212
239
  const codeHash = getCodeHash(code);
213
240
  const cached = transformCache.get(codeHash);
214
241
  if (cached) {
@@ -244,6 +271,7 @@ export async function transformCodeWithCompiler(
244
271
  loopCount: transformed.metadata.loopCount,
245
272
  arrayMethodCount: transformed.metadata.arrayMethodCount,
246
273
  parallelCallCount: transformed.metadata.parallelCallCount,
274
+ checkpointCount: transformed.metadata.checkpointCount,
247
275
  batchSizeThreshold: ATP_BATCH_SIZE_THRESHOLD,
248
276
  };
249
277
 
@@ -1,6 +1,6 @@
1
1
  import ivm from 'isolated-vm';
2
2
  import { ExecutionStatus } from '@mondaydotcomorg/atp-protocol';
3
- import type { ExecutionResult } from '@mondaydotcomorg/atp-protocol';
3
+ import type { ExecutionResult, ExecutionCheckpointData } from '@mondaydotcomorg/atp-protocol';
4
4
  import type { Logger } from '@mondaydotcomorg/atp-runtime';
5
5
  import {
6
6
  isPauseError,
@@ -10,17 +10,20 @@ import {
10
10
  getAPICallResults,
11
11
  clearAPICallResults,
12
12
  setCurrentExecutionId,
13
- clearCurrentExecutionId,
14
13
  type PauseExecutionError,
15
14
  } from '@mondaydotcomorg/atp-runtime';
16
- import { isBatchPauseError, type BatchPauseExecutionError } from '@mondaydotcomorg/atp-compiler';
15
+ import {
16
+ isBatchPauseError,
17
+ getCheckpointDataForError,
18
+ type BatchPauseExecutionError,
19
+ } from '@mondaydotcomorg/atp-compiler';
17
20
  import { randomUUID } from 'node:crypto';
18
21
  import type { CallbackRecord } from '../execution-state/index.js';
19
22
  import type { RuntimeContext } from './types.js';
20
23
  import { categorizeError } from './error-handler.js';
21
24
  import { PAUSE_EXECUTION_MARKER } from './constants.js';
22
25
 
23
- export function handleExecutionError(
26
+ export async function handleExecutionError(
24
27
  error: unknown,
25
28
  pauseError: unknown,
26
29
  context: RuntimeContext,
@@ -30,7 +33,7 @@ export function handleExecutionError(
30
33
  executionLogger: Logger,
31
34
  isolate: ivm.Isolate,
32
35
  transformedCode?: string
33
- ): ExecutionResult {
36
+ ): Promise<ExecutionResult> {
34
37
  const errMsg = error instanceof Error ? error.message : String(error);
35
38
 
36
39
  if (errMsg.includes(PAUSE_EXECUTION_MARKER) && pauseError) {
@@ -192,6 +195,35 @@ export function handleExecutionError(
192
195
  const memoryAfter = process.memoryUsage().heapUsed;
193
196
  const memoryUsed = Math.max(0, memoryAfter - memoryBefore);
194
197
 
198
+ // Collect checkpoint data if available
199
+ // This also flushes buffered checkpoints to cache for recovery
200
+ let checkpointData: ExecutionCheckpointData | undefined;
201
+ try {
202
+ const rawCheckpointData = await getCheckpointDataForError();
203
+ if (rawCheckpointData && rawCheckpointData.checkpoints.length > 0) {
204
+ checkpointData = {
205
+ checkpoints: rawCheckpointData.checkpoints.map((cp) => ({
206
+ id: cp.id,
207
+ type: cp.type,
208
+ operation: cp.operation,
209
+ description: cp.description,
210
+ timestamp: cp.timestamp,
211
+ })),
212
+ restoreInstructions: rawCheckpointData.restoreInstructions,
213
+ stats: rawCheckpointData.stats,
214
+ };
215
+ executionLogger.info('Checkpoint data included in error response', {
216
+ checkpointCount: checkpointData.checkpoints.length,
217
+ fullSnapshots: checkpointData.stats.fullSnapshots,
218
+ references: checkpointData.stats.references,
219
+ });
220
+ }
221
+ } catch (checkpointError) {
222
+ executionLogger.debug('No checkpoint data available', {
223
+ reason: checkpointError instanceof Error ? checkpointError.message : String(checkpointError),
224
+ });
225
+ }
226
+
195
227
  try {
196
228
  isolate.dispose();
197
229
  } catch (e) {}
@@ -208,6 +240,7 @@ export function handleExecutionError(
208
240
  stack: err.stack,
209
241
  retryable: errorInfo.retryable,
210
242
  suggestion: errorInfo.suggestion,
243
+ checkpointData,
211
244
  },
212
245
  stats: {
213
246
  duration: Date.now() - context.startTime,
@@ -28,24 +28,38 @@ import type { ExecutorConfig, RuntimeContext } from './types.js';
28
28
  import { SandboxBuilder } from './sandbox-builder.js';
29
29
  import { CodeInstrumentor, StateManager } from '../instrumentation/index.js';
30
30
  import { ATP_COMPILER_ENABLED } from './constants.js';
31
- import { getCompilerRuntime, transformCodeWithCompiler } from './compiler-config.js';
31
+ import {
32
+ getCompilerRuntime,
33
+ transformCodeWithCompiler,
34
+ initializeCheckpointRuntime,
35
+ initializeCheckpointRuntimeWithProvenance,
36
+ cleanupCheckpointRuntime,
37
+ getCheckpointRuntime,
38
+ type CheckpointProvenanceMetadata,
39
+ } from './compiler-config.js';
32
40
  import { setupResumeExecution } from './resume-handler.js';
33
41
  import {
34
42
  injectSandbox,
35
43
  injectTimerPolyfills,
36
44
  setupAPINamespace,
37
45
  setupRuntimeNamespace,
46
+ setupCheckpointNamespace,
38
47
  } from './sandbox-injector.js';
39
48
  import { handleExecutionError } from './execution-error-handler.js';
40
49
  import {
50
+ attachProvenanceMetaForCheckpoint,
41
51
  captureProvenanceSnapshot,
42
52
  cleanupProvenanceForExecution,
43
53
  clearProvenanceExecutionId,
54
+ createProvenanceProxy,
44
55
  createTrackingRuntime,
56
+ getProvenance,
57
+ getAllProvenance,
45
58
  instrumentCode as astInstrumentCode,
46
59
  registerProvenanceMetadata,
47
60
  SecurityPolicyEngine,
48
61
  setProvenanceExecutionId,
62
+ type ProvenanceMetadata,
49
63
  } from '@mondaydotcomorg/atp-provenance';
50
64
  import {
51
65
  createASTProvenanceChecker,
@@ -297,11 +311,72 @@ export class SandboxExecutor {
297
311
  };
298
312
  }
299
313
 
300
- if (ATP_COMPILER_ENABLED) {
301
- sandbox.__runtime = getCompilerRuntime();
314
+ if (ATP_COMPILER_ENABLED) {
315
+ sandbox.__runtime = getCompilerRuntime();
316
+ }
317
+
318
+ // Initialize checkpoint runtime if cache provider is available
319
+ if (this.config.cacheProvider) {
320
+ if (provenanceMode !== ProvenanceMode.NONE) {
321
+ // Provenance-aware checkpoint initialization
322
+ initializeCheckpointRuntimeWithProvenance({
323
+ executionId,
324
+ cache: this.config.cacheProvider,
325
+ config: { enabled: true },
326
+ provenanceMetaAttacher: attachProvenanceMetaForCheckpoint,
327
+ provenanceExtractor: (value: unknown) => {
328
+ // Just return ProvenanceMetadata as-is - types are now compatible
329
+ return getProvenance(value);
330
+ },
331
+ provenanceAttacher: (
332
+ value: unknown,
333
+ metadata,
334
+ primitives?
335
+ ): unknown => {
336
+ // Skip null values (primitive registration calls)
337
+ if (value === null) {
338
+ // Re-register primitive taints if present
339
+ if (primitives) {
340
+ for (const [key, primMeta] of primitives) {
341
+ registerProvenanceMetadata(key, primMeta, executionId);
342
+ }
343
+ }
344
+ return null;
345
+ }
346
+
347
+ // Re-attach provenance to restored value using the metadata as-is
348
+ const restored = createProvenanceProxy(
349
+ value,
350
+ metadata.source,
351
+ metadata.readers,
352
+ metadata.dependencies
353
+ );
354
+
355
+ // Re-register primitive taints if present
356
+ if (primitives) {
357
+ for (const [key, primMeta] of primitives) {
358
+ registerProvenanceMetadata(key, primMeta, executionId);
359
+ }
360
+ }
361
+
362
+ return restored;
363
+ },
364
+ });
365
+ executionLogger.debug('Checkpoint runtime initialized with provenance integration', {
366
+ provenanceMode,
367
+ });
368
+ } else {
369
+ // Standard checkpoint initialization (no provenance)
370
+ initializeCheckpointRuntime({
371
+ executionId,
372
+ cache: this.config.cacheProvider,
373
+ config: { enabled: true },
374
+ });
302
375
  }
376
+ sandbox.__checkpoint = getCheckpointRuntime();
377
+ }
303
378
 
304
- let hintMetadata: Map<string, any> | undefined;
379
+ let hintMetadata: Map<string, any> | undefined;
305
380
  if (provenanceMode === ProvenanceMode.AST) {
306
381
  hintMetadata = getHintMap(executionId);
307
382
 
@@ -336,11 +411,16 @@ export class SandboxExecutor {
336
411
 
337
412
  await setupAPINamespace(ivmContext, sandbox, provenanceMode);
338
413
 
339
- if (ATP_COMPILER_ENABLED) {
340
- await setupRuntimeNamespace(ivmContext, sandbox);
341
- }
414
+ if (ATP_COMPILER_ENABLED) {
415
+ await setupRuntimeNamespace(ivmContext, sandbox);
416
+ }
342
417
 
343
- let useCompiler = false;
418
+ // Setup checkpoint namespace if available
419
+ if (this.config.cacheProvider) {
420
+ await setupCheckpointNamespace(ivmContext, sandbox);
421
+ }
422
+
423
+ let useCompiler = false;
344
424
  let astInstrumented = false;
345
425
 
346
426
  const isResume = resumeData !== undefined;
@@ -366,13 +446,44 @@ export class SandboxExecutor {
366
446
  codePreview: code.substring(0, 100),
367
447
  });
368
448
 
369
- if (provenanceMode === ProvenanceMode.AST && !useCompiler && !alreadyTransformed) {
449
+ // STEP 1: Checkpoint transformation FIRST (before AST instrumentation)
450
+ // This ensures checkpoints are tracked even in AST provenance mode
451
+ if (
452
+ ATP_COMPILER_ENABLED &&
453
+ this.config.cacheProvider &&
454
+ !alreadyTransformed
455
+ ) {
456
+ const compilerResult = await transformCodeWithCompiler(
457
+ code,
458
+ executionId,
459
+ this.config.cacheProvider,
460
+ executionLogger,
461
+ this.compiler
462
+ );
463
+ codeToExecute = compilerResult.code;
464
+ useCompiler = compilerResult.useCompiler;
465
+ executionLogger.debug('Checkpoint transformation applied', {
466
+ useCompiler,
467
+ originalLength: code.length,
468
+ transformedLength: codeToExecute.length,
469
+ });
470
+ } else if (alreadyTransformed) {
471
+ codeToExecute = code;
472
+ useCompiler = true;
473
+ executionLogger.debug('Using already-transformed code on resume');
474
+ }
475
+
476
+ // STEP 2: AST instrumentation AFTER checkpoint transformation
477
+ // This ensures provenance tracking works with checkpoint-wrapped code
478
+ if (provenanceMode === ProvenanceMode.AST && !alreadyTransformed) {
370
479
  try {
371
- const instrumentResult = astInstrumentCode(code);
480
+ // Instrument the (potentially checkpoint-transformed) code
481
+ const instrumentResult = astInstrumentCode(codeToExecute);
372
482
  codeToExecute = instrumentResult.code;
373
483
  astInstrumented = true;
374
484
  executionLogger.info('Code instrumented for provenance tracking (AST mode)', {
375
485
  trackingCalls: instrumentResult.metadata.trackingCalls,
486
+ checkpointsPreserved: useCompiler,
376
487
  instrumentedCodeStart: codeToExecute.substring(0, 150),
377
488
  instrumentedCodeEnd: codeToExecute.substring(codeToExecute.length - 150),
378
489
  });
@@ -402,27 +513,6 @@ export class SandboxExecutor {
402
513
  }
403
514
  }
404
515
 
405
- if (
406
- ATP_COMPILER_ENABLED &&
407
- this.config.cacheProvider &&
408
- !astInstrumented &&
409
- !alreadyTransformed
410
- ) {
411
- const compilerResult = await transformCodeWithCompiler(
412
- code,
413
- executionId,
414
- this.config.cacheProvider,
415
- executionLogger,
416
- this.compiler
417
- );
418
- codeToExecute = compilerResult.code;
419
- useCompiler = compilerResult.useCompiler;
420
- } else if (alreadyTransformed) {
421
- codeToExecute = code;
422
- useCompiler = true;
423
- executionLogger.debug('Using already-transformed code on resume');
424
- }
425
-
426
516
  if (!useCompiler && !astInstrumented && stateManager) {
427
517
  try {
428
518
  const instrumentor = new CodeInstrumentor();
@@ -580,7 +670,7 @@ export class SandboxExecutor {
580
670
  }
581
671
  }
582
672
 
583
- return handleExecutionError(
673
+ return await handleExecutionError(
584
674
  error,
585
675
  pauseError,
586
676
  context,
@@ -643,6 +733,11 @@ export class SandboxExecutor {
643
733
 
644
734
  clearVectorStoreExecutionId();
645
735
 
736
+ // Cleanup checkpoint runtime
737
+ try {
738
+ cleanupCheckpointRuntime();
739
+ } catch (e) {}
740
+
646
741
  if (executionId) {
647
742
  try {
648
743
  cleanupExecutionState(executionId);
@@ -1,7 +1,7 @@
1
1
  import ivm from 'isolated-vm';
2
2
  import type { Logger } from '@mondaydotcomorg/atp-runtime';
3
3
  import { isPauseError, runInExecutionContext } from '@mondaydotcomorg/atp-runtime';
4
- import { isBatchPauseError } from '@mondaydotcomorg/atp-compiler';
4
+ import { isBatchPauseError, CHECKPOINT_RUNTIME_NAMESPACE } from '@mondaydotcomorg/atp-compiler';
5
5
  import { PAUSE_EXECUTION_MARKER } from './constants.js';
6
6
  import { isInIsolateFunction, getInIsolateImplementation } from './in-isolate-runtime.js';
7
7
 
@@ -117,14 +117,16 @@ export async function injectSandbox(
117
117
  // In AST mode, tag result with provenance ID before copying so tag survives
118
118
  if (isASTMode && result && typeof result === 'object') {
119
119
  try {
120
- // Generate unique ID for this API result
121
- const provId = `tracked_${Date.now()}_${Math.random().toString(36).substring(7)}`;
122
- Object.defineProperty(result, '__prov_id__', {
123
- value: provId,
124
- writable: false,
125
- enumerable: true,
126
- configurable: true,
127
- });
120
+ // Only add __prov_id__ if not already present (avoids overwriting UUID from createProvenanceProxy)
121
+ if (!Object.prototype.hasOwnProperty.call(result, '__prov_id__')) {
122
+ const provId = `tracked_${Date.now()}_${Math.random().toString(36).substring(7)}`;
123
+ Object.defineProperty(result, '__prov_id__', {
124
+ value: provId,
125
+ writable: false,
126
+ enumerable: true,
127
+ configurable: true,
128
+ });
129
+ }
128
130
  } catch (e) {
129
131
  // If can't define property, that's ok
130
132
  }
@@ -176,6 +178,41 @@ export async function injectSandbox(
176
178
  continue;
177
179
  }
178
180
 
181
+ // Handle checkpoint namespace
182
+ if (namespace === '__checkpoint' && typeof value === 'object' && value !== null) {
183
+ for (const [key, fn] of Object.entries(value)) {
184
+ if (typeof fn === 'function') {
185
+ await jail.set(
186
+ `__checkpoint_${key}_impl`,
187
+ new ivm.Reference(async (...args: unknown[]) => {
188
+ try {
189
+ const execute = async () => {
190
+ const result = await fn(...args);
191
+ return new ivm.ExternalCopy(result).copyInto();
192
+ };
193
+
194
+ if (executionId) {
195
+ return await runInExecutionContext(executionId, execute);
196
+ } else {
197
+ return await execute();
198
+ }
199
+ } catch (error) {
200
+ const err = error as Error;
201
+ if (isPauseError(error) || err.message === PAUSE_EXECUTION_MARKER) {
202
+ if (isPauseError(error)) {
203
+ onPauseError(error);
204
+ }
205
+ throw new Error(PAUSE_EXECUTION_MARKER);
206
+ }
207
+ throw error;
208
+ }
209
+ })
210
+ );
211
+ }
212
+ }
213
+ continue;
214
+ }
215
+
179
216
  if (namespace === '__runtime' && typeof value === 'object' && value !== null) {
180
217
  for (const [key, fn] of Object.entries(value)) {
181
218
  if (typeof fn === 'function') {
@@ -352,3 +389,32 @@ ${newAccessPath} = async function(...args) {
352
389
  setupNestedAPI(apiObject, '', 'globalThis.api');
353
390
  await ivmContext.eval(apiSetup);
354
391
  }
392
+
393
+ export async function setupCheckpointNamespace(
394
+ ivmContext: ivm.Context,
395
+ sandbox: Record<string, unknown>
396
+ ): Promise<void> {
397
+ const checkpointObject = sandbox.__checkpoint as Record<string, unknown>;
398
+ if (!checkpointObject || typeof checkpointObject !== 'object') {
399
+ return;
400
+ }
401
+
402
+ const checkpointKeys = Object.keys(checkpointObject).filter(
403
+ (k) => typeof checkpointObject[k] === 'function'
404
+ );
405
+ if (checkpointKeys.length === 0) {
406
+ return;
407
+ }
408
+
409
+ // Setup __checkpoint namespace for internal use by transformed code
410
+ let checkpointSetup = `globalThis.${CHECKPOINT_RUNTIME_NAMESPACE} = {\n`;
411
+ checkpointSetup += checkpointKeys
412
+ .map(
413
+ (key) =>
414
+ `\t${key}: async (...args) => {\n\t\treturn await ${CHECKPOINT_RUNTIME_NAMESPACE}_${key}_impl.apply(undefined, args, { arguments: { copy: true }, result: { promise: true } });\n\t}`
415
+ )
416
+ .join(',\n');
417
+ checkpointSetup += '\n};';
418
+
419
+ await ivmContext.eval(checkpointSetup);
420
+ }