@aiwg/cli 2026.7.20 → 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.
Files changed (62) hide show
  1. package/README.md +18 -7
  2. package/dist/src/api/index.d.ts +2 -0
  3. package/dist/src/api/index.js +2 -0
  4. package/dist/src/artifacts/browser-export.js +7 -0
  5. package/dist/src/artifacts/citation-parser.js +96 -35
  6. package/dist/src/artifacts/index-builder.js +54 -17
  7. package/dist/src/artifacts/state-transfer.js +27 -0
  8. package/dist/src/artifacts/stats.js +8 -0
  9. package/dist/src/cli/cli-extension-loader.js +73 -0
  10. package/dist/src/cli/handlers/index.js +3 -1
  11. package/dist/src/cli/handlers/sessions.js +1265 -0
  12. package/dist/src/cli/handlers/skill-lint.js +49 -45
  13. package/dist/src/cli/handlers/use.js +143 -60
  14. package/dist/src/cli/handlers/utilities.js +22 -8
  15. package/dist/src/cli/skill-usage.js +146 -24
  16. package/dist/src/config/aiwg-config.js +12 -0
  17. package/dist/src/config/cli.js +16 -3
  18. package/dist/src/extensions/commands/definitions.js +29 -0
  19. package/dist/src/extensions/manifest.js +29 -0
  20. package/dist/src/security/threat-assessment-config.js +296 -0
  21. package/dist/src/sessions/adapters/claude.js +385 -0
  22. package/dist/src/sessions/adapters/codex.js +548 -0
  23. package/dist/src/sessions/adapters/copilot.js +226 -0
  24. package/dist/src/sessions/adapters/cursor.js +528 -0
  25. package/dist/src/sessions/adapters/factory.js +386 -0
  26. package/dist/src/sessions/adapters/generic.js +225 -0
  27. package/dist/src/sessions/adapters/hermes.js +341 -0
  28. package/dist/src/sessions/adapters/openclaw.js +381 -0
  29. package/dist/src/sessions/adapters/opencode.js +454 -0
  30. package/dist/src/sessions/adapters/openhuman.js +315 -0
  31. package/dist/src/sessions/adapters/warp.js +160 -0
  32. package/dist/src/sessions/adapters/windsurf.js +212 -0
  33. package/dist/src/sessions/batch-contracts.js +121 -0
  34. package/dist/src/sessions/batch-import.js +265 -0
  35. package/dist/src/sessions/candidates.js +210 -0
  36. package/dist/src/sessions/contracts.js +337 -0
  37. package/dist/src/sessions/discovery.js +51 -0
  38. package/dist/src/sessions/fixtures.js +12 -0
  39. package/dist/src/sessions/import-lease.js +152 -0
  40. package/dist/src/sessions/importer.js +464 -0
  41. package/dist/src/sessions/index.js +31 -0
  42. package/dist/src/sessions/knowledge-shard.js +61 -0
  43. package/dist/src/sessions/optional-backends.js +238 -0
  44. package/dist/src/sessions/origin.js +117 -0
  45. package/dist/src/sessions/policy.js +192 -0
  46. package/dist/src/sessions/ports.js +2 -0
  47. package/dist/src/sessions/promotion.js +367 -0
  48. package/dist/src/sessions/readers.js +176 -0
  49. package/dist/src/sessions/repository.js +1892 -0
  50. package/dist/src/sessions/timeline.js +148 -0
  51. package/dist/src/sessions/workspace-discovery.js +319 -0
  52. package/dist/src/skills/adapters/agent-skills.js +59 -0
  53. package/dist/src/skills/adapters/local.js +19 -1
  54. package/dist/src/skills/agent-skills.js +249 -0
  55. package/dist/src/skills/cli.js +463 -7
  56. package/dist/src/skills/deployer.js +554 -0
  57. package/dist/src/skills/doctor.js +105 -0
  58. package/dist/src/skills/exporter.js +382 -0
  59. package/dist/src/skills/importer.js +921 -0
  60. package/dist/src/skills/registry.js +19 -0
  61. package/dist/src/skills/validator.js +323 -0
  62. package/package.json +2 -2
@@ -0,0 +1,528 @@
1
+ import { opendir } from 'node:fs/promises';
2
+ import { basename, dirname, extname, resolve, } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
5
+ import { readBoundedJsonLines, readBoundedText, streamBoundedJsonLines, } from '../readers.js';
6
+ export const CURSOR_ADAPTER_VERSION = '1.0.0';
7
+ export const CURSOR_SOURCE_SCHEMA_VERSION = '1.0.0';
8
+ const CliEventSchema = z.object({
9
+ schemaVersion: z.string().optional(),
10
+ cliVersion: z.string().optional(),
11
+ version: z.string().optional(),
12
+ type: z.string().min(1),
13
+ subtype: z.string().optional(),
14
+ session_id: z.string().min(1),
15
+ request_id: z.string().optional(),
16
+ call_id: z.string().optional(),
17
+ cwd: z.string().optional(),
18
+ model: z.string().optional(),
19
+ permissionMode: z.string().optional(),
20
+ message: z.object({
21
+ role: z.string().optional(),
22
+ content: z.array(z.object({
23
+ type: z.string(),
24
+ text: z.string().optional(),
25
+ }).passthrough()).optional(),
26
+ }).passthrough().optional(),
27
+ tool_call: z.record(z.unknown()).optional(),
28
+ result: z.string().optional(),
29
+ is_error: z.boolean().optional(),
30
+ }).passthrough();
31
+ const CloudEventSchema = z.object({
32
+ schemaVersion: z.string().optional(),
33
+ id: z.string().optional(),
34
+ event_id: z.string().optional(),
35
+ type: z.string().min(1),
36
+ agent: z.object({
37
+ id: z.string().min(1),
38
+ status: z.string().optional(),
39
+ }).passthrough().optional(),
40
+ run: z.object({
41
+ id: z.string().min(1),
42
+ status: z.string().optional(),
43
+ }).passthrough().optional(),
44
+ data: z.unknown().optional(),
45
+ }).passthrough().refine((value) => value.agent?.id || value.run?.id, {
46
+ message: 'cloud event requires an agent or run identity',
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
+ });
65
+ export class CursorSessionAdapter {
66
+ limits;
67
+ discoveryLimits;
68
+ provider = 'cursor';
69
+ adapterVersion = CURSOR_ADAPTER_VERSION;
70
+ disposition = 'implemented';
71
+ supportedOperations = ['discover', 'inspect', 'stream'];
72
+ acquisitionModes = ['api', 'jsonl', 'manual-export'];
73
+ constructor(limits, discoveryLimits = { maxDepth: 8, maxFiles: 10_000 }) {
74
+ this.limits = limits;
75
+ this.discoveryLimits = discoveryLimits;
76
+ }
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
+ }
94
+ }
95
+ async inspect(source) {
96
+ const parsed = await this.readSource(source);
97
+ return {
98
+ sourceSchemaVersion: parsed.schemaVersion,
99
+ consistency: parsed.consistency,
100
+ operationalState: 'available',
101
+ };
102
+ }
103
+ async *stream(source, cursor) {
104
+ if (source.locatorClass === 'cursor-cli-stream-json'
105
+ || source.locatorClass === 'cursor-cloud-events-jsonl') {
106
+ const input = await streamBoundedJsonLines({
107
+ selectedPath: source.locator,
108
+ allowedRoots: source.authorizedScope.allowedRoots,
109
+ }, { consistency: 'provisional', limits: this.limits });
110
+ const start = parseCursor(cursor?.value);
111
+ let outputIndex = 0;
112
+ let schemaVersion = null;
113
+ let cliSessionId;
114
+ let agentId;
115
+ let runId;
116
+ let sawRecord = false;
117
+ for await (const line of input) {
118
+ sawRecord = true;
119
+ const raw = asObject(line.value);
120
+ const currentSchema = typeof raw.schemaVersion === 'string'
121
+ ? raw.schemaVersion : CURSOR_SOURCE_SCHEMA_VERSION;
122
+ if (schemaVersion && schemaVersion !== currentSchema) {
123
+ throw new SessionContractError('SCHEMA_DRIFT', 'Cursor source declares mixed schema versions');
124
+ }
125
+ schemaVersion = currentSchema;
126
+ assertSupportedSchemaMajor(schemaVersion);
127
+ let record;
128
+ if (source.locatorClass === 'cursor-cli-stream-json') {
129
+ const parsed = CliEventSchema.safeParse(line.value);
130
+ if (!parsed.success) {
131
+ throw new SessionContractError('MALFORMED_SOURCE', 'Cursor CLI event is malformed');
132
+ }
133
+ const event = parsed.data;
134
+ if (cliSessionId && cliSessionId !== event.session_id) {
135
+ throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor CLI stream changes session identity');
136
+ }
137
+ cliSessionId = event.session_id;
138
+ record = normalizeCli([line]).records[0];
139
+ }
140
+ else {
141
+ const parsed = CloudEventSchema.safeParse(line.value);
142
+ if (!parsed.success) {
143
+ throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Cloud Agent event is malformed');
144
+ }
145
+ const event = parsed.data;
146
+ const nextAgentId = event.agent?.id ?? agentId;
147
+ const nextRunId = event.run?.id ?? runId;
148
+ if (agentId && nextAgentId && agentId !== nextAgentId) {
149
+ throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor cloud stream changes agent identity');
150
+ }
151
+ if (runId && nextRunId && runId !== nextRunId) {
152
+ throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor cloud stream changes run identity');
153
+ }
154
+ agentId = nextAgentId;
155
+ runId = nextRunId;
156
+ record = normalizeCloud([{
157
+ ...line,
158
+ value: {
159
+ ...event,
160
+ agent: event.agent ?? (agentId ? { id: agentId } : undefined),
161
+ run: event.run ?? (runId ? { id: runId } : undefined),
162
+ },
163
+ }]).records[0];
164
+ }
165
+ if (outputIndex++ >= start)
166
+ yield record;
167
+ }
168
+ if (!sawRecord && !input.incompleteTail) {
169
+ throw new SessionContractError('MALFORMED_SOURCE', 'Cursor structured source is empty');
170
+ }
171
+ return;
172
+ }
173
+ const parsed = await this.readSource(source);
174
+ const start = parseCursor(cursor?.value);
175
+ for (const record of parsed.records.slice(start))
176
+ yield record;
177
+ }
178
+ async readSource(source) {
179
+ const authorization = {
180
+ selectedPath: source.locator,
181
+ allowedRoots: source.authorizedScope.allowedRoots,
182
+ };
183
+ if (source.locatorClass === 'cursor-editor-sqlite') {
184
+ throw new SessionContractError('UNSUPPORTED_OPERATION', 'Cursor editor SQLite is undocumented and unsupported; export the chat as Markdown');
185
+ }
186
+ if (source.locatorClass === 'cursor-editor-markdown') {
187
+ const input = await readBoundedText(authorization, this.limits);
188
+ return {
189
+ schemaVersion: CURSOR_SOURCE_SCHEMA_VERSION,
190
+ consistency: 'complete',
191
+ records: normalizeMarkdown(input.value, source.locator),
192
+ };
193
+ }
194
+ const input = await readBoundedJsonLines(authorization, { consistency: 'provisional', limits: this.limits });
195
+ if (input.records.length === 0 && !input.incompleteTail) {
196
+ throw new SessionContractError('MALFORMED_SOURCE', 'Cursor structured source is empty');
197
+ }
198
+ const schemaVersion = declaredVersion(input.records);
199
+ assertSupportedSchemaMajor(schemaVersion);
200
+ if (source.locatorClass === 'cursor-cli-stream-json') {
201
+ const normalized = normalizeCli(input.records);
202
+ return {
203
+ schemaVersion,
204
+ consistency: normalized.complete && !input.incompleteTail ? 'complete' : 'provisional',
205
+ records: normalized.records,
206
+ };
207
+ }
208
+ if (source.locatorClass === 'cursor-cloud-events-jsonl') {
209
+ const normalized = normalizeCloud(input.records);
210
+ return {
211
+ schemaVersion,
212
+ consistency: normalized.complete && !input.incompleteTail ? 'complete' : 'provisional',
213
+ records: normalized.records,
214
+ };
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
+ }
224
+ throw new SessionContractError('UNSUPPORTED_OPERATION', 'unsupported Cursor source class');
225
+ }
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
+ }
253
+ function normalizeCli(input) {
254
+ const records = [];
255
+ let sessionId;
256
+ let complete = false;
257
+ for (const line of input) {
258
+ const parsed = CliEventSchema.safeParse(line.value);
259
+ if (!parsed.success) {
260
+ throw new SessionContractError('MALFORMED_SOURCE', 'Cursor CLI event is malformed');
261
+ }
262
+ const event = parsed.data;
263
+ if (sessionId && sessionId !== event.session_id) {
264
+ throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor CLI stream changes session identity');
265
+ }
266
+ sessionId = event.session_id;
267
+ const terminal = event.type === 'result'
268
+ && event.subtype === 'success'
269
+ && event.is_error !== true;
270
+ complete ||= terminal;
271
+ const text = event.type === 'result'
272
+ ? (event.result ?? '')
273
+ : (event.message?.content ?? []).flatMap((part) => part.text ?? []).join('');
274
+ records.push({
275
+ nativeSessionId: event.session_id,
276
+ nativeEventId: event.call_id ?? event.request_id ?? `${event.type}:${line.sequence}`,
277
+ sequence: line.sequence,
278
+ kind: event.type === 'tool_call' ? `tool.${event.subtype ?? 'event'}` : event.type,
279
+ role: event.message?.role ?? (event.type === 'system' ? 'system' : undefined),
280
+ participant: event.message?.role ?? (event.type === 'system' ? 'system' : undefined),
281
+ model: event.model,
282
+ toolName: typeof asObject(event.tool_call).name === 'string'
283
+ ? String(asObject(event.tool_call).name) : undefined,
284
+ toolCallId: event.call_id,
285
+ text,
286
+ rawReference: { locatorClass: 'cursor-cli-stream-json', offset: line.byteOffset },
287
+ extensions: {
288
+ subtype: event.subtype,
289
+ cwd: event.cwd,
290
+ model: event.model,
291
+ permissionMode: event.permissionMode,
292
+ productVersion: event.cliVersion ?? event.version ?? 'not-reported',
293
+ toolCall: event.tool_call,
294
+ ...(terminal ? { lifecycle: 'complete' } : {}),
295
+ provenance: { acquisition: 'cursor-cli-stream-json', schema: declaredEventVersion(event) },
296
+ unknownFields: unknownFields(event, CLI_KEYS),
297
+ },
298
+ });
299
+ }
300
+ return { records, complete };
301
+ }
302
+ function normalizeCloud(input) {
303
+ const records = [];
304
+ let agentId;
305
+ let runId;
306
+ let complete = false;
307
+ for (const line of input) {
308
+ const parsed = CloudEventSchema.safeParse(line.value);
309
+ if (!parsed.success) {
310
+ throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Cloud Agent event is malformed');
311
+ }
312
+ const event = parsed.data;
313
+ const nextAgentId = event.agent?.id ?? agentId;
314
+ const nextRunId = event.run?.id ?? runId;
315
+ if (agentId && nextAgentId && agentId !== nextAgentId) {
316
+ throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor cloud stream changes agent identity');
317
+ }
318
+ if (runId && nextRunId && runId !== nextRunId) {
319
+ throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor cloud stream changes run identity');
320
+ }
321
+ agentId = nextAgentId;
322
+ runId = nextRunId;
323
+ const status = event.run?.status ?? event.agent?.status;
324
+ const lifecycle = cloudLifecycle(event.type, status);
325
+ complete ||= isTerminal(status) || event.type === 'agent.deleted' || event.type === 'agent.archived';
326
+ const sessionId = runId ? `${agentId ?? 'agent'}:${runId}` : agentId;
327
+ records.push({
328
+ nativeSessionId: sessionId,
329
+ nativeEventId: event.event_id ?? event.id ?? `${event.type}:${line.sequence}`,
330
+ sequence: line.sequence,
331
+ kind: `cursor.cloud.${event.type}`,
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,
348
+ text: extractCloudText(event.data),
349
+ rawReference: { locatorClass: 'cursor-cloud-events-jsonl', offset: line.byteOffset },
350
+ extensions: {
351
+ agent: event.agent,
352
+ run: event.run,
353
+ status,
354
+ ...(lifecycle ? { lifecycle } : {}),
355
+ reconnect: {
356
+ eventId: event.event_id ?? event.id,
357
+ supported: true,
358
+ header: 'Last-Event-ID',
359
+ },
360
+ provenance: { acquisition: 'cursor-cloud-agents-api-v1', schema: declaredEventVersion(event) },
361
+ unknownFields: unknownFields(event, CLOUD_KEYS),
362
+ },
363
+ });
364
+ }
365
+ return { records, complete };
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
+ }
410
+ function normalizeMarkdown(value, locator) {
411
+ const heading = /^#{1,3}\s+(User|Assistant|Cursor)\s*$/gim;
412
+ const matches = [...value.matchAll(heading)];
413
+ if (matches.length === 0) {
414
+ throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Markdown export has no role headings');
415
+ }
416
+ const nativeSessionId = basename(locator, extname(locator));
417
+ return matches.map((match, index) => {
418
+ const start = match.index + match[0].length;
419
+ const end = matches[index + 1]?.index ?? value.length;
420
+ const role = match[1].toLowerCase() === 'user' ? 'user' : 'assistant';
421
+ return {
422
+ nativeSessionId,
423
+ nativeEventId: `markdown:${index}`,
424
+ sequence: index,
425
+ kind: 'message',
426
+ role,
427
+ text: value.slice(start, end).trim(),
428
+ rawReference: { locatorClass: 'cursor-editor-markdown', sequence: index },
429
+ extensions: {
430
+ metadataLoss: [
431
+ 'timestamps unavailable',
432
+ 'model unavailable',
433
+ 'tool calls and results unavailable',
434
+ 'provider lifecycle unavailable',
435
+ ],
436
+ provenance: {
437
+ acquisition: 'cursor-editor-markdown-export',
438
+ nativeSessionIdDerivedFromFilename: true,
439
+ undocumentedSqliteDependency: false,
440
+ },
441
+ },
442
+ };
443
+ });
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
+ }
465
+ function declaredVersion(records) {
466
+ const versions = new Set(records.map((line) => {
467
+ const value = asObject(line.value);
468
+ return typeof value.schemaVersion === 'string' ? value.schemaVersion : CURSOR_SOURCE_SCHEMA_VERSION;
469
+ }));
470
+ if (versions.size !== 1) {
471
+ throw new SessionContractError('SCHEMA_DRIFT', 'Cursor source declares mixed schema versions');
472
+ }
473
+ return [...versions][0];
474
+ }
475
+ function declaredEventVersion(event) {
476
+ return event.schemaVersion ?? CURSOR_SOURCE_SCHEMA_VERSION;
477
+ }
478
+ function extractCloudText(value) {
479
+ const object = asObject(value);
480
+ if (typeof object.text === 'string')
481
+ return object.text;
482
+ if (typeof object.message === 'string')
483
+ return object.message;
484
+ return '';
485
+ }
486
+ function cloudLifecycle(type, status) {
487
+ if (type === 'agent.deleted')
488
+ return 'deleted';
489
+ if (type === 'agent.archived')
490
+ return 'archived';
491
+ if (type === 'agent.unarchived')
492
+ return 'active';
493
+ if (status === 'cancelled' || status === 'canceled')
494
+ return 'cancelled';
495
+ if (status === 'active')
496
+ return 'active';
497
+ return isTerminal(status) ? 'complete' : undefined;
498
+ }
499
+ function isTerminal(status) {
500
+ return ['completed', 'failed', 'cancelled', 'canceled'].includes(status ?? '');
501
+ }
502
+ function unknownFields(value, known) {
503
+ return Object.fromEntries(Object.entries(value).filter(([key]) => !known.has(key)).sort(([a], [b]) => a.localeCompare(b)));
504
+ }
505
+ function asObject(value) {
506
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
507
+ ? value
508
+ : {};
509
+ }
510
+ function parseCursor(value) {
511
+ if (!value)
512
+ return 0;
513
+ if (!/^\d+$/.test(value)) {
514
+ throw new SessionContractError('SCHEMA_DRIFT', 'invalid Cursor record cursor');
515
+ }
516
+ return Number(value);
517
+ }
518
+ const CLI_KEYS = new Set([
519
+ 'schemaVersion', 'type', 'subtype', 'session_id', 'request_id', 'call_id', 'cwd',
520
+ 'model', 'permissionMode', 'cliVersion', 'version', 'message', 'tool_call', 'result', 'is_error',
521
+ 'duration_ms', 'duration_api_ms',
522
+ ]);
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
+ ]);
528
+ //# sourceMappingURL=cursor.js.map