@aiwg/cli 2026.7.21 → 2026.7.23

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.
@@ -1,4 +1,5 @@
1
- import { basename, extname } from 'node:path';
1
+ import { opendir } from 'node:fs/promises';
2
+ import { basename, dirname, extname, resolve, } from 'node:path';
2
3
  import { z } from 'zod';
3
4
  import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
4
5
  import { readBoundedJsonLines, readBoundedText, streamBoundedJsonLines, } from '../readers.js';
@@ -44,19 +45,52 @@ const CloudEventSchema = z.object({
44
45
  }).passthrough().refine((value) => value.agent?.id || value.run?.id, {
45
46
  message: 'cloud event requires an agent or run identity',
46
47
  });
48
+ const AgentTranscriptEventSchema = z.object({
49
+ schemaVersion: z.string().optional(),
50
+ id: z.string().optional(),
51
+ event_id: z.string().optional(),
52
+ request_id: z.string().optional(),
53
+ timestamp: z.string().datetime({ offset: true }).optional(),
54
+ type: z.string().min(1).optional(),
55
+ status: z.string().min(1).optional(),
56
+ role: z.string().min(1).optional(),
57
+ message: z.object({
58
+ id: z.string().optional(),
59
+ role: z.string().optional(),
60
+ content: z.union([z.string(), z.array(z.unknown())]).optional(),
61
+ }).passthrough().optional(),
62
+ }).passthrough().refine((value) => value.role || value.type, {
63
+ message: 'agent transcript event requires role or type',
64
+ });
47
65
  export class CursorSessionAdapter {
48
66
  limits;
67
+ discoveryLimits;
49
68
  provider = 'cursor';
50
69
  adapterVersion = CURSOR_ADAPTER_VERSION;
51
70
  disposition = 'implemented';
52
- supportedOperations = ['inspect', 'stream'];
71
+ supportedOperations = ['discover', 'inspect', 'stream'];
53
72
  acquisitionModes = ['api', 'jsonl', 'manual-export'];
54
- constructor(limits) {
73
+ constructor(limits, discoveryLimits = { maxDepth: 8, maxFiles: 10_000 }) {
55
74
  this.limits = limits;
75
+ this.discoveryLimits = discoveryLimits;
56
76
  }
57
- async *discover(_scope) {
58
- // Every supported Cursor surface requires an explicitly selected export.
59
- // The undocumented editor SQLite store is intentionally excluded.
77
+ async *discover(scope) {
78
+ if (scope.allowedRoots.length === 0) {
79
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Cursor discovery requires an explicitly authorized agent-transcripts root');
80
+ }
81
+ let emitted = 0;
82
+ for (const root of [...scope.allowedRoots].sort()) {
83
+ for await (const locator of discoverJsonl(resolve(root), this.discoveryLimits.maxDepth)) {
84
+ if (++emitted > this.discoveryLimits.maxFiles) {
85
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Cursor source discovery exceeded the authorized file limit');
86
+ }
87
+ yield {
88
+ provider: 'cursor',
89
+ locator,
90
+ locatorClass: 'cursor-agent-transcript-jsonl',
91
+ };
92
+ }
93
+ }
60
94
  }
61
95
  async inspect(source) {
62
96
  const parsed = await this.readSource(source);
@@ -179,9 +213,43 @@ export class CursorSessionAdapter {
179
213
  records: normalized.records,
180
214
  };
181
215
  }
216
+ if (source.locatorClass === 'cursor-agent-transcript-jsonl') {
217
+ const normalized = normalizeAgentTranscript(input.records, source.locator);
218
+ return {
219
+ schemaVersion,
220
+ consistency: normalized.complete && !input.incompleteTail ? 'complete' : 'provisional',
221
+ records: normalized.records,
222
+ };
223
+ }
182
224
  throw new SessionContractError('UNSUPPORTED_OPERATION', 'unsupported Cursor source class');
183
225
  }
184
226
  }
227
+ async function* discoverJsonl(root, remainingDepth) {
228
+ if (remainingDepth < 0)
229
+ return;
230
+ let directory;
231
+ try {
232
+ directory = await opendir(root);
233
+ }
234
+ catch {
235
+ return;
236
+ }
237
+ const entries = [];
238
+ for await (const entry of directory)
239
+ entries.push(entry);
240
+ entries.sort((left, right) => left.name.localeCompare(right.name));
241
+ for (const entry of entries) {
242
+ const path = resolve(root, entry.name);
243
+ if (entry.isSymbolicLink())
244
+ continue;
245
+ if (entry.isDirectory()) {
246
+ yield* discoverJsonl(path, remainingDepth - 1);
247
+ }
248
+ else if (entry.isFile() && extname(entry.name) === '.jsonl') {
249
+ yield path;
250
+ }
251
+ }
252
+ }
185
253
  function normalizeCli(input) {
186
254
  const records = [];
187
255
  let sessionId;
@@ -196,7 +264,10 @@ function normalizeCli(input) {
196
264
  throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor CLI stream changes session identity');
197
265
  }
198
266
  sessionId = event.session_id;
199
- complete ||= event.type === 'result' && event.subtype === 'success' && event.is_error !== true;
267
+ const terminal = event.type === 'result'
268
+ && event.subtype === 'success'
269
+ && event.is_error !== true;
270
+ complete ||= terminal;
200
271
  const text = event.type === 'result'
201
272
  ? (event.result ?? '')
202
273
  : (event.message?.content ?? []).flatMap((part) => part.text ?? []).join('');
@@ -220,7 +291,7 @@ function normalizeCli(input) {
220
291
  permissionMode: event.permissionMode,
221
292
  productVersion: event.cliVersion ?? event.version ?? 'not-reported',
222
293
  toolCall: event.tool_call,
223
- lifecycle: complete ? 'complete' : 'active',
294
+ ...(terminal ? { lifecycle: 'complete' } : {}),
224
295
  provenance: { acquisition: 'cursor-cli-stream-json', schema: declaredEventVersion(event) },
225
296
  unknownFields: unknownFields(event, CLI_KEYS),
226
297
  },
@@ -250,6 +321,7 @@ function normalizeCloud(input) {
250
321
  agentId = nextAgentId;
251
322
  runId = nextRunId;
252
323
  const status = event.run?.status ?? event.agent?.status;
324
+ const lifecycle = cloudLifecycle(event.type, status);
253
325
  complete ||= isTerminal(status) || event.type === 'agent.deleted' || event.type === 'agent.archived';
254
326
  const sessionId = runId ? `${agentId ?? 'agent'}:${runId}` : agentId;
255
327
  records.push({
@@ -258,13 +330,28 @@ function normalizeCloud(input) {
258
330
  sequence: line.sequence,
259
331
  kind: `cursor.cloud.${event.type}`,
260
332
  role: 'system',
333
+ activityBoundary: event.type === 'agent.unarchived'
334
+ ? 'resume'
335
+ : event.type === 'agent.archived' || event.type === 'agent.deleted'
336
+ ? 'end'
337
+ : undefined,
338
+ activityBoundaryBasis: event.type === 'agent.unarchived'
339
+ || event.type === 'agent.archived'
340
+ || event.type === 'agent.deleted'
341
+ ? `cursor-cloud:${event.type}`
342
+ : undefined,
343
+ activityBoundaryConfidence: event.type === 'agent.unarchived'
344
+ || event.type === 'agent.archived'
345
+ || event.type === 'agent.deleted'
346
+ ? 'high'
347
+ : undefined,
261
348
  text: extractCloudText(event.data),
262
349
  rawReference: { locatorClass: 'cursor-cloud-events-jsonl', offset: line.byteOffset },
263
350
  extensions: {
264
351
  agent: event.agent,
265
352
  run: event.run,
266
353
  status,
267
- lifecycle: cloudLifecycle(event.type, status),
354
+ ...(lifecycle ? { lifecycle } : {}),
268
355
  reconnect: {
269
356
  eventId: event.event_id ?? event.id,
270
357
  supported: true,
@@ -277,6 +364,49 @@ function normalizeCloud(input) {
277
364
  }
278
365
  return { records, complete };
279
366
  }
367
+ function normalizeAgentTranscript(input, locator) {
368
+ const nativeSessionId = agentTranscriptSessionId(locator);
369
+ const records = [];
370
+ let complete = false;
371
+ for (const line of input) {
372
+ const parsed = AgentTranscriptEventSchema.safeParse(line.value);
373
+ if (!parsed.success) {
374
+ throw new SessionContractError('MALFORMED_SOURCE', 'Cursor agent transcript event is malformed');
375
+ }
376
+ const event = parsed.data;
377
+ const terminal = event.type === 'turn_ended' && event.status === 'success';
378
+ complete ||= terminal;
379
+ const messageRole = event.message?.role ?? event.role ?? 'system';
380
+ records.push({
381
+ nativeSessionId,
382
+ nativeEventId: event.event_id ?? event.id ?? event.request_id ?? `${messageRole}:${line.sequence}`,
383
+ sequence: line.sequence,
384
+ kind: event.type ? `cursor.agent.${event.type}` : 'message',
385
+ role: messageRole,
386
+ participant: messageRole,
387
+ occurredAt: event.timestamp,
388
+ activityBoundary: event.type === 'turn_ended' ? 'end' : undefined,
389
+ activityBoundaryBasis: event.type === 'turn_ended'
390
+ ? `cursor-agent:${event.status ?? 'unknown'}`
391
+ : undefined,
392
+ activityBoundaryConfidence: event.type === 'turn_ended' ? 'high' : undefined,
393
+ text: messageText(event.message?.content),
394
+ rawReference: { locatorClass: 'cursor-agent-transcript-jsonl', offset: line.byteOffset },
395
+ extensions: {
396
+ transcriptRole: event.role,
397
+ status: event.status,
398
+ ...(terminal ? { lifecycle: 'complete' } : {}),
399
+ provenance: {
400
+ acquisition: 'cursor-agent-transcript-jsonl',
401
+ schema: declaredEventVersion(event),
402
+ nativeSessionIdDerivedFromPath: true,
403
+ },
404
+ unknownFields: unknownFields(event, AGENT_TRANSCRIPT_KEYS),
405
+ },
406
+ });
407
+ }
408
+ return { records, complete };
409
+ }
280
410
  function normalizeMarkdown(value, locator) {
281
411
  const heading = /^#{1,3}\s+(User|Assistant|Cursor)\s*$/gim;
282
412
  const matches = [...value.matchAll(heading)];
@@ -312,6 +442,26 @@ function normalizeMarkdown(value, locator) {
312
442
  };
313
443
  });
314
444
  }
445
+ function agentTranscriptSessionId(locator) {
446
+ const fileIdentity = basename(locator, extname(locator));
447
+ const directoryIdentity = basename(dirname(locator));
448
+ return directoryIdentity && directoryIdentity === fileIdentity
449
+ ? directoryIdentity : fileIdentity;
450
+ }
451
+ function messageText(value) {
452
+ if (typeof value === 'string')
453
+ return value;
454
+ if (!Array.isArray(value))
455
+ return '';
456
+ return value.map((item) => {
457
+ const block = asObject(item);
458
+ if (typeof block.text === 'string')
459
+ return block.text;
460
+ if (typeof block.content === 'string')
461
+ return block.content;
462
+ return '';
463
+ }).filter(Boolean).join('\n');
464
+ }
315
465
  function declaredVersion(records) {
316
466
  const versions = new Set(records.map((line) => {
317
467
  const value = asObject(line.value);
@@ -342,7 +492,9 @@ function cloudLifecycle(type, status) {
342
492
  return 'active';
343
493
  if (status === 'cancelled' || status === 'canceled')
344
494
  return 'cancelled';
345
- return isTerminal(status) ? 'complete' : 'active';
495
+ if (status === 'active')
496
+ return 'active';
497
+ return isTerminal(status) ? 'complete' : undefined;
346
498
  }
347
499
  function isTerminal(status) {
348
500
  return ['completed', 'failed', 'cancelled', 'canceled'].includes(status ?? '');
@@ -369,4 +521,8 @@ const CLI_KEYS = new Set([
369
521
  'duration_ms', 'duration_api_ms',
370
522
  ]);
371
523
  const CLOUD_KEYS = new Set(['schemaVersion', 'id', 'event_id', 'type', 'agent', 'run', 'data']);
524
+ const AGENT_TRANSCRIPT_KEYS = new Set([
525
+ 'schemaVersion', 'id', 'event_id', 'request_id', 'timestamp', 'type', 'status',
526
+ 'role', 'message',
527
+ ]);
372
528
  //# sourceMappingURL=cursor.js.map
@@ -18,7 +18,7 @@ const FactoryRecordSchema = z.object({
18
18
  createdAt: z.number().optional(),
19
19
  updatedAt: z.number().optional(),
20
20
  cwd: z.string().optional(),
21
- version: z.string().optional(),
21
+ version: z.union([z.string(), z.number()]).optional(),
22
22
  message: z.object({
23
23
  id: z.string().optional(),
24
24
  role: z.string().optional(),
@@ -80,7 +80,7 @@ export class FactorySessionAdapter {
80
80
  sawRecord = true;
81
81
  const parsed = FactoryRecordSchema.safeParse(line.value);
82
82
  if (!parsed.success) {
83
- throw new SessionContractError('MALFORMED_SOURCE', 'Factory session record is malformed');
83
+ throw factoryMalformedRecord(source, line, parsed.error);
84
84
  }
85
85
  const value = parsed.data;
86
86
  const currentSchema = value.schemaVersion ?? FACTORY_SOURCE_SCHEMA_VERSION;
@@ -90,12 +90,12 @@ export class FactorySessionAdapter {
90
90
  schemaVersion = currentSchema;
91
91
  assertSupportedSchemaMajor(schemaVersion);
92
92
  const filenameId = basename(source.locator, extname(source.locator));
93
- const currentId = value.sessionId ?? value.session_id ?? establishedId ?? filenameId;
93
+ const currentId = factoryNativeSessionId(value, establishedId, filenameId);
94
94
  if (establishedId && establishedId !== currentId) {
95
95
  throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Factory source changes session identity');
96
96
  }
97
97
  establishedId = currentId;
98
- for (const record of normalize([line], source).records) {
98
+ for (const record of normalize([line], source, establishedId).records) {
99
99
  if (outputIndex++ >= start)
100
100
  yield record;
101
101
  }
@@ -162,24 +162,28 @@ export class FactorySessionAdapter {
162
162
  }));
163
163
  }
164
164
  }
165
- function normalize(input, source) {
165
+ function normalize(input, source, initialSessionId) {
166
166
  const output = [];
167
167
  const filenameId = basename(source.locator, extname(source.locator));
168
168
  let complete = false;
169
- let establishedId;
169
+ let establishedId = initialSessionId;
170
170
  for (const line of input) {
171
171
  const parsed = FactoryRecordSchema.safeParse(line.value);
172
172
  if (!parsed.success) {
173
- throw new SessionContractError('MALFORMED_SOURCE', 'Factory session record is malformed');
173
+ throw factoryMalformedRecord(source, line, parsed.error);
174
174
  }
175
175
  const value = parsed.data;
176
- const nativeSessionId = value.sessionId ?? value.session_id ?? establishedId ?? filenameId;
176
+ const nativeSessionId = factoryNativeSessionId(value, establishedId, filenameId);
177
177
  if (establishedId && nativeSessionId !== establishedId) {
178
178
  throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Factory source changes session identity');
179
179
  }
180
180
  establishedId = nativeSessionId;
181
181
  complete ||= ['session_end', 'result'].includes(value.type)
182
182
  || ['completed', 'archived', 'deleted'].includes(value.status ?? '');
183
+ const explicitLifecycle = ['session_end', 'result'].includes(value.type)
184
+ || ['completed', 'archived', 'deleted'].includes(value.status ?? '')
185
+ ? lifecycle(value.status)
186
+ : undefined;
183
187
  const blocks = contentBlocks(value);
184
188
  if (blocks.length === 0)
185
189
  blocks.push({
@@ -199,13 +203,30 @@ function normalize(input, source) {
199
203
  kind: block.kind,
200
204
  role: value.message?.role,
201
205
  occurredAt: timestamp(value.timestamp ?? value.updatedAt ?? value.createdAt),
206
+ activityBoundary: value.type === 'session_end'
207
+ ? 'end'
208
+ : value.type === 'session_start' && value.subtype === 'resume'
209
+ ? 'resume'
210
+ : value.type === 'session_start' && value.subtype === 'continuation'
211
+ ? 'continuation'
212
+ : undefined,
213
+ activityBoundaryBasis: value.type === 'session_end'
214
+ || (value.type === 'session_start'
215
+ && (value.subtype === 'resume' || value.subtype === 'continuation'))
216
+ ? `factory:${value.type}:${value.subtype ?? 'none'}`
217
+ : undefined,
218
+ activityBoundaryConfidence: value.type === 'session_end'
219
+ || (value.type === 'session_start'
220
+ && (value.subtype === 'resume' || value.subtype === 'continuation'))
221
+ ? 'high'
222
+ : undefined,
202
223
  text: block.text,
203
224
  rawReference: { locatorClass: source.locatorClass, offset: line.byteOffset },
204
225
  extensions: {
205
226
  subtype: value.subtype,
206
227
  parentUuid: value.parentUuid,
207
228
  productVersion: value.version ?? 'not-reported',
208
- lifecycle: complete ? lifecycle(value.status) : 'active',
229
+ ...(explicitLifecycle ? { lifecycle: explicitLifecycle } : {}),
209
230
  workspace: { cwdClass: value.cwd ? '<workspace>' : undefined },
210
231
  settings: value.sessionSettings ?? value.settings,
211
232
  opaque: block.opaque,
@@ -263,6 +284,15 @@ function contentBlocks(value) {
263
284
  return { kind: `factory.${type}`, text: '', opaque: true, nativeId, unknownFields: { ...block } };
264
285
  });
265
286
  }
287
+ function factoryNativeSessionId(value, establishedId, filenameId) {
288
+ if (value.sessionId)
289
+ return value.sessionId;
290
+ if (value.session_id)
291
+ return value.session_id;
292
+ if (value.type === 'session_start' && value.id)
293
+ return value.id;
294
+ return establishedId ?? filenameId;
295
+ }
266
296
  async function* discoverJsonl(root, maxDepth) {
267
297
  const pending = [{ path: root, depth: 0 }];
268
298
  while (pending.length) {
@@ -291,6 +321,17 @@ async function* discoverJsonl(root, maxDepth) {
291
321
  pending.push({ path, depth: current.depth + 1 });
292
322
  }
293
323
  }
324
+ function factoryMalformedRecord(source, record, error) {
325
+ const value = asObject(record.value);
326
+ const rawType = typeof value.type === 'string' ? value.type : '';
327
+ const type = /^[a-zA-Z0-9_.:-]{1,64}$/.test(rawType) ? rawType : '<missing-or-invalid>';
328
+ const issue = error.issues[0];
329
+ const requirement = issue?.path.length
330
+ ? issue.path.map(String).join('.')
331
+ : 'record';
332
+ return new SessionContractError('MALFORMED_SOURCE', `Factory source ${basename(source.locator)} record ${record.sequence + 1} `
333
+ + `type ${type} failed requirement ${requirement} (${issue?.code ?? 'invalid_type'})`);
334
+ }
294
335
  function declaredVersion(records) {
295
336
  const versions = new Set(records.flatMap((record) => {
296
337
  const value = asObject(record.value);
@@ -0,0 +1,121 @@
1
+ export const BATCH_IMPORT_VERSION = '1.0.0';
2
+ export const COVERAGE_VERSION = '1.0.0';
3
+ export function coverageFromBatchRun(run, now = new Date(), staleAfterMs = 24 * 60 * 60 * 1_000) {
4
+ if (!run)
5
+ return unknownCoverage();
6
+ const coveredStatuses = new Set([
7
+ 'committed', 'duplicate', 'previously-committed',
8
+ ]);
9
+ const accepted = run.sources.filter((source) => source.status === 'committed').length;
10
+ const rejected = run.sources.filter((source) => source.status === 'rejected').length;
11
+ const skipped = run.sources.filter((source) => source.status === 'skipped').length;
12
+ const duplicated = run.sources.filter((source) => source.status === 'duplicate').length;
13
+ const previouslyCommitted = run.sources.filter((source) => source.status === 'previously-committed').length;
14
+ const pending = run.sources.filter((source) => source.status === 'pending' || source.status === 'running').length;
15
+ const checked = providerNames(run, 'checked');
16
+ const unavailable = providerNames(run, 'unavailable');
17
+ const exportRequired = providerNames(run, 'export-required');
18
+ const notChecked = providerNames(run, 'not-checked');
19
+ const manifestAgeMs = Math.max(0, now.getTime() - Date.parse(run.manifestCreatedAt));
20
+ const isStale = manifestAgeMs > staleAfterMs;
21
+ const incomplete = rejected > 0 || pending > 0 || skipped > 0
22
+ || exportRequired.length > 0 || notChecked.length > 0;
23
+ const rejectionCounts = {};
24
+ for (const source of run.sources) {
25
+ if (source.status !== 'rejected')
26
+ continue;
27
+ const code = source.errorCode ?? 'UNKNOWN_REJECTION';
28
+ rejectionCounts[code] = (rejectionCounts[code] ?? 0) + 1;
29
+ }
30
+ const sourceTimestamps = run.providers.flatMap((provider) => [
31
+ provider.dateRange.earliest,
32
+ provider.dateRange.latest,
33
+ ]).filter((value) => value !== null).sort();
34
+ const importedTimestamps = run.sources
35
+ .filter((source) => coveredStatuses.has(source.status))
36
+ .map((source) => source.updatedAt)
37
+ .sort();
38
+ const remediation = [
39
+ ...(rejected > 0
40
+ ? ['Run `aiwg sessions import-discovered --resume --confirm` after correcting rejected sources.']
41
+ : []),
42
+ ...(exportRequired.length > 0
43
+ ? ['Export and explicitly authorize providers marked export-required, then run discovery again.']
44
+ : []),
45
+ ...(isStale ? ['Run `aiwg sessions discover` to refresh the stale manifest.'] : []),
46
+ ];
47
+ return {
48
+ schemaVersion: COVERAGE_VERSION,
49
+ status: isStale ? 'stale' : incomplete ? 'partial' : 'complete',
50
+ workspaceId: run.workspaceId,
51
+ manifestId: run.manifestId,
52
+ batchRunId: run.runId,
53
+ manifestCreatedAt: run.manifestCreatedAt,
54
+ manifestAgeMs,
55
+ providers: { checked, unavailable, exportRequired, notChecked },
56
+ sources: {
57
+ discovered: run.sources.length,
58
+ accepted,
59
+ rejected,
60
+ skipped,
61
+ duplicated,
62
+ previouslyCommitted,
63
+ pending,
64
+ },
65
+ sessionsAccepted: run.sources.reduce((sum, source) => sum + source.sessionsAccepted, 0),
66
+ eventsAccepted: run.sources.reduce((sum, source) => sum + source.eventsAccepted, 0),
67
+ coverageRatio: run.sources.length === 0
68
+ ? null
69
+ : (accepted + duplicated + previouslyCommitted) / run.sources.length,
70
+ rejectionCounts,
71
+ sourceDateRange: {
72
+ earliest: sourceTimestamps.at(0) ?? null,
73
+ latest: sourceTimestamps.at(-1) ?? null,
74
+ },
75
+ importedDateRange: {
76
+ earliest: importedTimestamps.at(0) ?? null,
77
+ latest: importedTimestamps.at(-1) ?? null,
78
+ },
79
+ remediation,
80
+ };
81
+ }
82
+ function providerNames(run, status) {
83
+ return run.providers
84
+ .filter((provider) => provider.status === status)
85
+ .map((provider) => provider.provider)
86
+ .sort();
87
+ }
88
+ function unknownCoverage() {
89
+ return {
90
+ schemaVersion: COVERAGE_VERSION,
91
+ status: 'unknown',
92
+ workspaceId: '',
93
+ manifestId: null,
94
+ batchRunId: null,
95
+ manifestCreatedAt: null,
96
+ manifestAgeMs: null,
97
+ providers: {
98
+ checked: [],
99
+ unavailable: [],
100
+ exportRequired: [],
101
+ notChecked: [],
102
+ },
103
+ sources: {
104
+ discovered: 0,
105
+ accepted: 0,
106
+ rejected: 0,
107
+ skipped: 0,
108
+ duplicated: 0,
109
+ previouslyCommitted: 0,
110
+ pending: 0,
111
+ },
112
+ sessionsAccepted: 0,
113
+ eventsAccepted: 0,
114
+ coverageRatio: null,
115
+ rejectionCounts: {},
116
+ sourceDateRange: { earliest: null, latest: null },
117
+ importedDateRange: { earliest: null, latest: null },
118
+ remediation: ['Run `aiwg sessions discover --workspace <path>` to establish coverage.'],
119
+ };
120
+ }
121
+ //# sourceMappingURL=batch-contracts.js.map