@aiwg/cli 2026.7.20 → 2026.7.21
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/README.md +4 -4
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/browser-export.js +7 -0
- package/dist/src/artifacts/citation-parser.js +96 -35
- package/dist/src/artifacts/index-builder.js +54 -17
- package/dist/src/artifacts/state-transfer.js +27 -0
- package/dist/src/artifacts/stats.js +8 -0
- package/dist/src/cli/cli-extension-loader.js +73 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/sessions.js +966 -0
- package/dist/src/cli/handlers/skill-lint.js +49 -45
- package/dist/src/cli/handlers/use.js +143 -60
- package/dist/src/cli/handlers/utilities.js +22 -8
- package/dist/src/cli/skill-usage.js +146 -24
- package/dist/src/extensions/commands/definitions.js +29 -0
- package/dist/src/extensions/manifest.js +29 -0
- package/dist/src/sessions/adapters/claude.js +357 -0
- package/dist/src/sessions/adapters/codex.js +521 -0
- package/dist/src/sessions/adapters/copilot.js +226 -0
- package/dist/src/sessions/adapters/cursor.js +372 -0
- package/dist/src/sessions/adapters/factory.js +345 -0
- package/dist/src/sessions/adapters/generic.js +225 -0
- package/dist/src/sessions/adapters/hermes.js +341 -0
- package/dist/src/sessions/adapters/openclaw.js +381 -0
- package/dist/src/sessions/adapters/opencode.js +454 -0
- package/dist/src/sessions/adapters/openhuman.js +315 -0
- package/dist/src/sessions/adapters/warp.js +160 -0
- package/dist/src/sessions/adapters/windsurf.js +212 -0
- package/dist/src/sessions/candidates.js +210 -0
- package/dist/src/sessions/contracts.js +310 -0
- package/dist/src/sessions/discovery.js +51 -0
- package/dist/src/sessions/fixtures.js +12 -0
- package/dist/src/sessions/importer.js +315 -0
- package/dist/src/sessions/index.js +25 -0
- package/dist/src/sessions/knowledge-shard.js +61 -0
- package/dist/src/sessions/optional-backends.js +238 -0
- package/dist/src/sessions/policy.js +192 -0
- package/dist/src/sessions/ports.js +2 -0
- package/dist/src/sessions/promotion.js +367 -0
- package/dist/src/sessions/readers.js +176 -0
- package/dist/src/sessions/repository.js +1551 -0
- package/dist/src/skills/adapters/agent-skills.js +59 -0
- package/dist/src/skills/adapters/local.js +19 -1
- package/dist/src/skills/agent-skills.js +249 -0
- package/dist/src/skills/cli.js +463 -7
- package/dist/src/skills/deployer.js +554 -0
- package/dist/src/skills/doctor.js +105 -0
- package/dist/src/skills/exporter.js +382 -0
- package/dist/src/skills/importer.js +921 -0
- package/dist/src/skills/registry.js +19 -0
- package/dist/src/skills/validator.js +323 -0
- package/package.json +2 -2
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { basename, extname } from 'node:path';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
4
|
+
import { readBoundedJson } from '../readers.js';
|
|
5
|
+
export const COPILOT_ADAPTER_VERSION = '1.0.0';
|
|
6
|
+
export const COPILOT_EXPORT_SCHEMA_VERSION = '1.0.0';
|
|
7
|
+
const ResponsePartSchema = z.object({
|
|
8
|
+
value: z.string().optional(),
|
|
9
|
+
kind: z.string().optional(),
|
|
10
|
+
}).passthrough();
|
|
11
|
+
const RequestSchema = z.object({
|
|
12
|
+
requestId: z.string().min(1).optional(),
|
|
13
|
+
message: z.union([
|
|
14
|
+
z.string(),
|
|
15
|
+
z.object({ text: z.string() }).passthrough(),
|
|
16
|
+
]),
|
|
17
|
+
response: z.union([
|
|
18
|
+
z.string(),
|
|
19
|
+
z.array(ResponsePartSchema),
|
|
20
|
+
]).optional(),
|
|
21
|
+
timestamp: z.union([z.string(), z.number()]).optional(),
|
|
22
|
+
modelId: z.string().optional(),
|
|
23
|
+
}).passthrough();
|
|
24
|
+
const ExportSchema = z.object({
|
|
25
|
+
version: z.union([z.number().int().positive(), z.string()]),
|
|
26
|
+
schemaVersion: z.string().optional(),
|
|
27
|
+
sessionId: z.string().min(1).optional(),
|
|
28
|
+
creationDate: z.union([z.string(), z.number()]).optional(),
|
|
29
|
+
lastMessageDate: z.union([z.string(), z.number()]).optional(),
|
|
30
|
+
requests: z.array(RequestSchema),
|
|
31
|
+
requesterUsername: z.string().optional(),
|
|
32
|
+
responderUsername: z.string().optional(),
|
|
33
|
+
state: z.string().optional(),
|
|
34
|
+
isArchived: z.boolean().optional(),
|
|
35
|
+
syncStatus: z.string().optional(),
|
|
36
|
+
workspace: z.object({
|
|
37
|
+
id: z.string().optional(),
|
|
38
|
+
repository: z.string().optional(),
|
|
39
|
+
}).passthrough().optional(),
|
|
40
|
+
}).passthrough();
|
|
41
|
+
export class CopilotSessionAdapter {
|
|
42
|
+
limits;
|
|
43
|
+
provider = 'copilot';
|
|
44
|
+
adapterVersion = COPILOT_ADAPTER_VERSION;
|
|
45
|
+
disposition = 'implemented';
|
|
46
|
+
supportedOperations = ['inspect', 'stream'];
|
|
47
|
+
acquisitionModes = ['manual-export'];
|
|
48
|
+
constructor(limits) {
|
|
49
|
+
this.limits = limits;
|
|
50
|
+
}
|
|
51
|
+
async *discover(_scope) {
|
|
52
|
+
// Supported VS Code exports require explicit user selection. Versioned
|
|
53
|
+
// workspaceStorage JSON/JSONL is intentionally not treated as a stable source.
|
|
54
|
+
}
|
|
55
|
+
async inspect(source) {
|
|
56
|
+
const parsed = await this.readSource(source);
|
|
57
|
+
return {
|
|
58
|
+
sourceSchemaVersion: parsed.schemaVersion,
|
|
59
|
+
consistency: parsed.consistency,
|
|
60
|
+
operationalState: 'available',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async *stream(source, cursor) {
|
|
64
|
+
const parsed = await this.readSource(source);
|
|
65
|
+
const start = parseCursor(cursor?.value);
|
|
66
|
+
for (const record of parsed.records.slice(start))
|
|
67
|
+
yield record;
|
|
68
|
+
}
|
|
69
|
+
async readSource(source) {
|
|
70
|
+
if (source.locatorClass !== 'copilot-chat-json-export') {
|
|
71
|
+
throw new SessionContractError('UNSUPPORTED_OPERATION', 'Copilot workspace-store parsing is experimental and not enabled by this adapter');
|
|
72
|
+
}
|
|
73
|
+
const { value } = await readBoundedJson({
|
|
74
|
+
selectedPath: source.locator,
|
|
75
|
+
allowedRoots: source.authorizedScope.allowedRoots,
|
|
76
|
+
}, this.limits);
|
|
77
|
+
const parsed = ExportSchema.safeParse(value);
|
|
78
|
+
if (!parsed.success) {
|
|
79
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Copilot chat export is malformed');
|
|
80
|
+
}
|
|
81
|
+
const schemaVersion = declaredSchemaVersion(parsed.data);
|
|
82
|
+
assertSupportedSchemaMajor(schemaVersion);
|
|
83
|
+
return {
|
|
84
|
+
schemaVersion,
|
|
85
|
+
consistency: parsed.data.isArchived || parsed.data.state === 'archived'
|
|
86
|
+
|| parsed.data.state === 'deleted' ? 'complete' : 'provisional',
|
|
87
|
+
records: normalizeExport(parsed.data, source.locator),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function normalizeExport(value, locator) {
|
|
92
|
+
const nativeSessionId = value.sessionId ?? basename(locator, extname(locator));
|
|
93
|
+
const lifecycle = value.isArchived || value.state === 'archived'
|
|
94
|
+
? 'archived'
|
|
95
|
+
: value.state === 'deleted' ? 'deleted' : 'active';
|
|
96
|
+
const output = [];
|
|
97
|
+
for (const [requestIndex, request] of value.requests.entries()) {
|
|
98
|
+
const requestId = request.requestId ?? `request-${requestIndex}`;
|
|
99
|
+
output.push({
|
|
100
|
+
nativeSessionId,
|
|
101
|
+
nativeEventId: `${requestId}:request`,
|
|
102
|
+
sequence: requestIndex * 2,
|
|
103
|
+
kind: 'message',
|
|
104
|
+
role: 'user',
|
|
105
|
+
occurredAt: timestamp(request.timestamp ?? value.creationDate),
|
|
106
|
+
text: messageText(request),
|
|
107
|
+
rawReference: { locatorClass: 'copilot-chat-json-export', sequence: requestIndex },
|
|
108
|
+
extensions: commonExtensions(value, request, lifecycle, {
|
|
109
|
+
direction: 'request',
|
|
110
|
+
unknownFields: unknownFields(request, REQUEST_KEYS),
|
|
111
|
+
metadataLoss: [],
|
|
112
|
+
}),
|
|
113
|
+
});
|
|
114
|
+
const response = responseText(request);
|
|
115
|
+
if (response.text !== null) {
|
|
116
|
+
output.push({
|
|
117
|
+
nativeSessionId,
|
|
118
|
+
nativeEventId: `${requestId}:response`,
|
|
119
|
+
sequence: requestIndex * 2 + 1,
|
|
120
|
+
kind: 'message',
|
|
121
|
+
role: 'assistant',
|
|
122
|
+
occurredAt: timestamp(request.timestamp ?? value.lastMessageDate),
|
|
123
|
+
text: response.text,
|
|
124
|
+
rawReference: { locatorClass: 'copilot-chat-json-export', sequence: requestIndex },
|
|
125
|
+
extensions: commonExtensions(value, request, lifecycle, {
|
|
126
|
+
direction: 'response',
|
|
127
|
+
unknownFields: response.unknownFields,
|
|
128
|
+
metadataLoss: response.losses,
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return output;
|
|
134
|
+
}
|
|
135
|
+
function commonExtensions(value, request, lifecycle, event) {
|
|
136
|
+
return {
|
|
137
|
+
...event,
|
|
138
|
+
lifecycle,
|
|
139
|
+
sync: {
|
|
140
|
+
status: value.syncStatus ?? 'unknown',
|
|
141
|
+
archiveState: value.isArchived ? 'archived' : 'not-reported',
|
|
142
|
+
deletionState: value.state === 'deleted' ? 'provider-reported' : 'not-reported',
|
|
143
|
+
},
|
|
144
|
+
workspace: {
|
|
145
|
+
id: value.workspace?.id,
|
|
146
|
+
repository: value.workspace?.repository,
|
|
147
|
+
},
|
|
148
|
+
model: request.modelId,
|
|
149
|
+
participants: {
|
|
150
|
+
requester: value.requesterUsername,
|
|
151
|
+
responder: value.responderUsername,
|
|
152
|
+
},
|
|
153
|
+
provenance: {
|
|
154
|
+
acquisition: 'vscode-chat-json-export',
|
|
155
|
+
schema: declaredSchemaVersion(value),
|
|
156
|
+
stableWorkspaceStoreDependency: false,
|
|
157
|
+
proposedApiDependency: false,
|
|
158
|
+
},
|
|
159
|
+
exportUnknownFields: unknownFields(value, EXPORT_KEYS),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function responseText(request) {
|
|
163
|
+
if (typeof request.response === 'string') {
|
|
164
|
+
return { text: request.response, losses: [], unknownFields: {} };
|
|
165
|
+
}
|
|
166
|
+
if (!request.response)
|
|
167
|
+
return { text: null, losses: [], unknownFields: {} };
|
|
168
|
+
const text = request.response
|
|
169
|
+
.map((part) => part.value)
|
|
170
|
+
.filter((part) => typeof part === 'string')
|
|
171
|
+
.join('\n\n');
|
|
172
|
+
const losses = request.response.flatMap((part, index) => (part.kind && part.kind !== 'markdownContent'
|
|
173
|
+
? [{
|
|
174
|
+
field: `response[${index}]`,
|
|
175
|
+
reason: `structured ${part.kind} part flattened or retained as opaque metadata`,
|
|
176
|
+
}]
|
|
177
|
+
: []));
|
|
178
|
+
return {
|
|
179
|
+
text,
|
|
180
|
+
losses,
|
|
181
|
+
unknownFields: {
|
|
182
|
+
responseParts: request.response.map((part) => unknownFields(part, RESPONSE_KEYS)),
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function messageText(request) {
|
|
187
|
+
return typeof request.message === 'string' ? request.message : request.message.text;
|
|
188
|
+
}
|
|
189
|
+
function declaredSchemaVersion(value) {
|
|
190
|
+
if (value.schemaVersion)
|
|
191
|
+
return value.schemaVersion;
|
|
192
|
+
const major = typeof value.version === 'number'
|
|
193
|
+
? value.version
|
|
194
|
+
: Number(String(value.version).split('.')[0]);
|
|
195
|
+
if (!Number.isInteger(major) || major < 1) {
|
|
196
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Copilot export version');
|
|
197
|
+
}
|
|
198
|
+
return `${major}.0.0`;
|
|
199
|
+
}
|
|
200
|
+
function timestamp(value) {
|
|
201
|
+
if (value === undefined)
|
|
202
|
+
return undefined;
|
|
203
|
+
const date = new Date(value);
|
|
204
|
+
return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
|
|
205
|
+
}
|
|
206
|
+
function unknownFields(value, known) {
|
|
207
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !known.has(key)).sort(([a], [b]) => a.localeCompare(b)));
|
|
208
|
+
}
|
|
209
|
+
function parseCursor(value) {
|
|
210
|
+
if (!value)
|
|
211
|
+
return 0;
|
|
212
|
+
if (!/^\d+$/.test(value)) {
|
|
213
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Copilot record cursor');
|
|
214
|
+
}
|
|
215
|
+
return Number(value);
|
|
216
|
+
}
|
|
217
|
+
const EXPORT_KEYS = new Set([
|
|
218
|
+
'version', 'schemaVersion', 'sessionId', 'creationDate', 'lastMessageDate',
|
|
219
|
+
'requests', 'requesterUsername', 'responderUsername', 'state', 'isArchived',
|
|
220
|
+
'syncStatus', 'workspace',
|
|
221
|
+
]);
|
|
222
|
+
const REQUEST_KEYS = new Set([
|
|
223
|
+
'requestId', 'message', 'response', 'timestamp', 'modelId',
|
|
224
|
+
]);
|
|
225
|
+
const RESPONSE_KEYS = new Set(['value', 'kind']);
|
|
226
|
+
//# sourceMappingURL=copilot.js.map
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { basename, extname } from 'node:path';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
4
|
+
import { readBoundedJsonLines, readBoundedText, streamBoundedJsonLines, } from '../readers.js';
|
|
5
|
+
export const CURSOR_ADAPTER_VERSION = '1.0.0';
|
|
6
|
+
export const CURSOR_SOURCE_SCHEMA_VERSION = '1.0.0';
|
|
7
|
+
const CliEventSchema = z.object({
|
|
8
|
+
schemaVersion: z.string().optional(),
|
|
9
|
+
cliVersion: z.string().optional(),
|
|
10
|
+
version: z.string().optional(),
|
|
11
|
+
type: z.string().min(1),
|
|
12
|
+
subtype: z.string().optional(),
|
|
13
|
+
session_id: z.string().min(1),
|
|
14
|
+
request_id: z.string().optional(),
|
|
15
|
+
call_id: z.string().optional(),
|
|
16
|
+
cwd: z.string().optional(),
|
|
17
|
+
model: z.string().optional(),
|
|
18
|
+
permissionMode: z.string().optional(),
|
|
19
|
+
message: z.object({
|
|
20
|
+
role: z.string().optional(),
|
|
21
|
+
content: z.array(z.object({
|
|
22
|
+
type: z.string(),
|
|
23
|
+
text: z.string().optional(),
|
|
24
|
+
}).passthrough()).optional(),
|
|
25
|
+
}).passthrough().optional(),
|
|
26
|
+
tool_call: z.record(z.unknown()).optional(),
|
|
27
|
+
result: z.string().optional(),
|
|
28
|
+
is_error: z.boolean().optional(),
|
|
29
|
+
}).passthrough();
|
|
30
|
+
const CloudEventSchema = z.object({
|
|
31
|
+
schemaVersion: z.string().optional(),
|
|
32
|
+
id: z.string().optional(),
|
|
33
|
+
event_id: z.string().optional(),
|
|
34
|
+
type: z.string().min(1),
|
|
35
|
+
agent: z.object({
|
|
36
|
+
id: z.string().min(1),
|
|
37
|
+
status: z.string().optional(),
|
|
38
|
+
}).passthrough().optional(),
|
|
39
|
+
run: z.object({
|
|
40
|
+
id: z.string().min(1),
|
|
41
|
+
status: z.string().optional(),
|
|
42
|
+
}).passthrough().optional(),
|
|
43
|
+
data: z.unknown().optional(),
|
|
44
|
+
}).passthrough().refine((value) => value.agent?.id || value.run?.id, {
|
|
45
|
+
message: 'cloud event requires an agent or run identity',
|
|
46
|
+
});
|
|
47
|
+
export class CursorSessionAdapter {
|
|
48
|
+
limits;
|
|
49
|
+
provider = 'cursor';
|
|
50
|
+
adapterVersion = CURSOR_ADAPTER_VERSION;
|
|
51
|
+
disposition = 'implemented';
|
|
52
|
+
supportedOperations = ['inspect', 'stream'];
|
|
53
|
+
acquisitionModes = ['api', 'jsonl', 'manual-export'];
|
|
54
|
+
constructor(limits) {
|
|
55
|
+
this.limits = limits;
|
|
56
|
+
}
|
|
57
|
+
async *discover(_scope) {
|
|
58
|
+
// Every supported Cursor surface requires an explicitly selected export.
|
|
59
|
+
// The undocumented editor SQLite store is intentionally excluded.
|
|
60
|
+
}
|
|
61
|
+
async inspect(source) {
|
|
62
|
+
const parsed = await this.readSource(source);
|
|
63
|
+
return {
|
|
64
|
+
sourceSchemaVersion: parsed.schemaVersion,
|
|
65
|
+
consistency: parsed.consistency,
|
|
66
|
+
operationalState: 'available',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async *stream(source, cursor) {
|
|
70
|
+
if (source.locatorClass === 'cursor-cli-stream-json'
|
|
71
|
+
|| source.locatorClass === 'cursor-cloud-events-jsonl') {
|
|
72
|
+
const input = await streamBoundedJsonLines({
|
|
73
|
+
selectedPath: source.locator,
|
|
74
|
+
allowedRoots: source.authorizedScope.allowedRoots,
|
|
75
|
+
}, { consistency: 'provisional', limits: this.limits });
|
|
76
|
+
const start = parseCursor(cursor?.value);
|
|
77
|
+
let outputIndex = 0;
|
|
78
|
+
let schemaVersion = null;
|
|
79
|
+
let cliSessionId;
|
|
80
|
+
let agentId;
|
|
81
|
+
let runId;
|
|
82
|
+
let sawRecord = false;
|
|
83
|
+
for await (const line of input) {
|
|
84
|
+
sawRecord = true;
|
|
85
|
+
const raw = asObject(line.value);
|
|
86
|
+
const currentSchema = typeof raw.schemaVersion === 'string'
|
|
87
|
+
? raw.schemaVersion : CURSOR_SOURCE_SCHEMA_VERSION;
|
|
88
|
+
if (schemaVersion && schemaVersion !== currentSchema) {
|
|
89
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'Cursor source declares mixed schema versions');
|
|
90
|
+
}
|
|
91
|
+
schemaVersion = currentSchema;
|
|
92
|
+
assertSupportedSchemaMajor(schemaVersion);
|
|
93
|
+
let record;
|
|
94
|
+
if (source.locatorClass === 'cursor-cli-stream-json') {
|
|
95
|
+
const parsed = CliEventSchema.safeParse(line.value);
|
|
96
|
+
if (!parsed.success) {
|
|
97
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor CLI event is malformed');
|
|
98
|
+
}
|
|
99
|
+
const event = parsed.data;
|
|
100
|
+
if (cliSessionId && cliSessionId !== event.session_id) {
|
|
101
|
+
throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor CLI stream changes session identity');
|
|
102
|
+
}
|
|
103
|
+
cliSessionId = event.session_id;
|
|
104
|
+
record = normalizeCli([line]).records[0];
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const parsed = CloudEventSchema.safeParse(line.value);
|
|
108
|
+
if (!parsed.success) {
|
|
109
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Cloud Agent event is malformed');
|
|
110
|
+
}
|
|
111
|
+
const event = parsed.data;
|
|
112
|
+
const nextAgentId = event.agent?.id ?? agentId;
|
|
113
|
+
const nextRunId = event.run?.id ?? runId;
|
|
114
|
+
if (agentId && nextAgentId && agentId !== nextAgentId) {
|
|
115
|
+
throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor cloud stream changes agent identity');
|
|
116
|
+
}
|
|
117
|
+
if (runId && nextRunId && runId !== nextRunId) {
|
|
118
|
+
throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor cloud stream changes run identity');
|
|
119
|
+
}
|
|
120
|
+
agentId = nextAgentId;
|
|
121
|
+
runId = nextRunId;
|
|
122
|
+
record = normalizeCloud([{
|
|
123
|
+
...line,
|
|
124
|
+
value: {
|
|
125
|
+
...event,
|
|
126
|
+
agent: event.agent ?? (agentId ? { id: agentId } : undefined),
|
|
127
|
+
run: event.run ?? (runId ? { id: runId } : undefined),
|
|
128
|
+
},
|
|
129
|
+
}]).records[0];
|
|
130
|
+
}
|
|
131
|
+
if (outputIndex++ >= start)
|
|
132
|
+
yield record;
|
|
133
|
+
}
|
|
134
|
+
if (!sawRecord && !input.incompleteTail) {
|
|
135
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor structured source is empty');
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const parsed = await this.readSource(source);
|
|
140
|
+
const start = parseCursor(cursor?.value);
|
|
141
|
+
for (const record of parsed.records.slice(start))
|
|
142
|
+
yield record;
|
|
143
|
+
}
|
|
144
|
+
async readSource(source) {
|
|
145
|
+
const authorization = {
|
|
146
|
+
selectedPath: source.locator,
|
|
147
|
+
allowedRoots: source.authorizedScope.allowedRoots,
|
|
148
|
+
};
|
|
149
|
+
if (source.locatorClass === 'cursor-editor-sqlite') {
|
|
150
|
+
throw new SessionContractError('UNSUPPORTED_OPERATION', 'Cursor editor SQLite is undocumented and unsupported; export the chat as Markdown');
|
|
151
|
+
}
|
|
152
|
+
if (source.locatorClass === 'cursor-editor-markdown') {
|
|
153
|
+
const input = await readBoundedText(authorization, this.limits);
|
|
154
|
+
return {
|
|
155
|
+
schemaVersion: CURSOR_SOURCE_SCHEMA_VERSION,
|
|
156
|
+
consistency: 'complete',
|
|
157
|
+
records: normalizeMarkdown(input.value, source.locator),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const input = await readBoundedJsonLines(authorization, { consistency: 'provisional', limits: this.limits });
|
|
161
|
+
if (input.records.length === 0 && !input.incompleteTail) {
|
|
162
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor structured source is empty');
|
|
163
|
+
}
|
|
164
|
+
const schemaVersion = declaredVersion(input.records);
|
|
165
|
+
assertSupportedSchemaMajor(schemaVersion);
|
|
166
|
+
if (source.locatorClass === 'cursor-cli-stream-json') {
|
|
167
|
+
const normalized = normalizeCli(input.records);
|
|
168
|
+
return {
|
|
169
|
+
schemaVersion,
|
|
170
|
+
consistency: normalized.complete && !input.incompleteTail ? 'complete' : 'provisional',
|
|
171
|
+
records: normalized.records,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (source.locatorClass === 'cursor-cloud-events-jsonl') {
|
|
175
|
+
const normalized = normalizeCloud(input.records);
|
|
176
|
+
return {
|
|
177
|
+
schemaVersion,
|
|
178
|
+
consistency: normalized.complete && !input.incompleteTail ? 'complete' : 'provisional',
|
|
179
|
+
records: normalized.records,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
throw new SessionContractError('UNSUPPORTED_OPERATION', 'unsupported Cursor source class');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function normalizeCli(input) {
|
|
186
|
+
const records = [];
|
|
187
|
+
let sessionId;
|
|
188
|
+
let complete = false;
|
|
189
|
+
for (const line of input) {
|
|
190
|
+
const parsed = CliEventSchema.safeParse(line.value);
|
|
191
|
+
if (!parsed.success) {
|
|
192
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor CLI event is malformed');
|
|
193
|
+
}
|
|
194
|
+
const event = parsed.data;
|
|
195
|
+
if (sessionId && sessionId !== event.session_id) {
|
|
196
|
+
throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor CLI stream changes session identity');
|
|
197
|
+
}
|
|
198
|
+
sessionId = event.session_id;
|
|
199
|
+
complete ||= event.type === 'result' && event.subtype === 'success' && event.is_error !== true;
|
|
200
|
+
const text = event.type === 'result'
|
|
201
|
+
? (event.result ?? '')
|
|
202
|
+
: (event.message?.content ?? []).flatMap((part) => part.text ?? []).join('');
|
|
203
|
+
records.push({
|
|
204
|
+
nativeSessionId: event.session_id,
|
|
205
|
+
nativeEventId: event.call_id ?? event.request_id ?? `${event.type}:${line.sequence}`,
|
|
206
|
+
sequence: line.sequence,
|
|
207
|
+
kind: event.type === 'tool_call' ? `tool.${event.subtype ?? 'event'}` : event.type,
|
|
208
|
+
role: event.message?.role ?? (event.type === 'system' ? 'system' : undefined),
|
|
209
|
+
participant: event.message?.role ?? (event.type === 'system' ? 'system' : undefined),
|
|
210
|
+
model: event.model,
|
|
211
|
+
toolName: typeof asObject(event.tool_call).name === 'string'
|
|
212
|
+
? String(asObject(event.tool_call).name) : undefined,
|
|
213
|
+
toolCallId: event.call_id,
|
|
214
|
+
text,
|
|
215
|
+
rawReference: { locatorClass: 'cursor-cli-stream-json', offset: line.byteOffset },
|
|
216
|
+
extensions: {
|
|
217
|
+
subtype: event.subtype,
|
|
218
|
+
cwd: event.cwd,
|
|
219
|
+
model: event.model,
|
|
220
|
+
permissionMode: event.permissionMode,
|
|
221
|
+
productVersion: event.cliVersion ?? event.version ?? 'not-reported',
|
|
222
|
+
toolCall: event.tool_call,
|
|
223
|
+
lifecycle: complete ? 'complete' : 'active',
|
|
224
|
+
provenance: { acquisition: 'cursor-cli-stream-json', schema: declaredEventVersion(event) },
|
|
225
|
+
unknownFields: unknownFields(event, CLI_KEYS),
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return { records, complete };
|
|
230
|
+
}
|
|
231
|
+
function normalizeCloud(input) {
|
|
232
|
+
const records = [];
|
|
233
|
+
let agentId;
|
|
234
|
+
let runId;
|
|
235
|
+
let complete = false;
|
|
236
|
+
for (const line of input) {
|
|
237
|
+
const parsed = CloudEventSchema.safeParse(line.value);
|
|
238
|
+
if (!parsed.success) {
|
|
239
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Cloud Agent event is malformed');
|
|
240
|
+
}
|
|
241
|
+
const event = parsed.data;
|
|
242
|
+
const nextAgentId = event.agent?.id ?? agentId;
|
|
243
|
+
const nextRunId = event.run?.id ?? runId;
|
|
244
|
+
if (agentId && nextAgentId && agentId !== nextAgentId) {
|
|
245
|
+
throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor cloud stream changes agent identity');
|
|
246
|
+
}
|
|
247
|
+
if (runId && nextRunId && runId !== nextRunId) {
|
|
248
|
+
throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Cursor cloud stream changes run identity');
|
|
249
|
+
}
|
|
250
|
+
agentId = nextAgentId;
|
|
251
|
+
runId = nextRunId;
|
|
252
|
+
const status = event.run?.status ?? event.agent?.status;
|
|
253
|
+
complete ||= isTerminal(status) || event.type === 'agent.deleted' || event.type === 'agent.archived';
|
|
254
|
+
const sessionId = runId ? `${agentId ?? 'agent'}:${runId}` : agentId;
|
|
255
|
+
records.push({
|
|
256
|
+
nativeSessionId: sessionId,
|
|
257
|
+
nativeEventId: event.event_id ?? event.id ?? `${event.type}:${line.sequence}`,
|
|
258
|
+
sequence: line.sequence,
|
|
259
|
+
kind: `cursor.cloud.${event.type}`,
|
|
260
|
+
role: 'system',
|
|
261
|
+
text: extractCloudText(event.data),
|
|
262
|
+
rawReference: { locatorClass: 'cursor-cloud-events-jsonl', offset: line.byteOffset },
|
|
263
|
+
extensions: {
|
|
264
|
+
agent: event.agent,
|
|
265
|
+
run: event.run,
|
|
266
|
+
status,
|
|
267
|
+
lifecycle: cloudLifecycle(event.type, status),
|
|
268
|
+
reconnect: {
|
|
269
|
+
eventId: event.event_id ?? event.id,
|
|
270
|
+
supported: true,
|
|
271
|
+
header: 'Last-Event-ID',
|
|
272
|
+
},
|
|
273
|
+
provenance: { acquisition: 'cursor-cloud-agents-api-v1', schema: declaredEventVersion(event) },
|
|
274
|
+
unknownFields: unknownFields(event, CLOUD_KEYS),
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return { records, complete };
|
|
279
|
+
}
|
|
280
|
+
function normalizeMarkdown(value, locator) {
|
|
281
|
+
const heading = /^#{1,3}\s+(User|Assistant|Cursor)\s*$/gim;
|
|
282
|
+
const matches = [...value.matchAll(heading)];
|
|
283
|
+
if (matches.length === 0) {
|
|
284
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Markdown export has no role headings');
|
|
285
|
+
}
|
|
286
|
+
const nativeSessionId = basename(locator, extname(locator));
|
|
287
|
+
return matches.map((match, index) => {
|
|
288
|
+
const start = match.index + match[0].length;
|
|
289
|
+
const end = matches[index + 1]?.index ?? value.length;
|
|
290
|
+
const role = match[1].toLowerCase() === 'user' ? 'user' : 'assistant';
|
|
291
|
+
return {
|
|
292
|
+
nativeSessionId,
|
|
293
|
+
nativeEventId: `markdown:${index}`,
|
|
294
|
+
sequence: index,
|
|
295
|
+
kind: 'message',
|
|
296
|
+
role,
|
|
297
|
+
text: value.slice(start, end).trim(),
|
|
298
|
+
rawReference: { locatorClass: 'cursor-editor-markdown', sequence: index },
|
|
299
|
+
extensions: {
|
|
300
|
+
metadataLoss: [
|
|
301
|
+
'timestamps unavailable',
|
|
302
|
+
'model unavailable',
|
|
303
|
+
'tool calls and results unavailable',
|
|
304
|
+
'provider lifecycle unavailable',
|
|
305
|
+
],
|
|
306
|
+
provenance: {
|
|
307
|
+
acquisition: 'cursor-editor-markdown-export',
|
|
308
|
+
nativeSessionIdDerivedFromFilename: true,
|
|
309
|
+
undocumentedSqliteDependency: false,
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
function declaredVersion(records) {
|
|
316
|
+
const versions = new Set(records.map((line) => {
|
|
317
|
+
const value = asObject(line.value);
|
|
318
|
+
return typeof value.schemaVersion === 'string' ? value.schemaVersion : CURSOR_SOURCE_SCHEMA_VERSION;
|
|
319
|
+
}));
|
|
320
|
+
if (versions.size !== 1) {
|
|
321
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'Cursor source declares mixed schema versions');
|
|
322
|
+
}
|
|
323
|
+
return [...versions][0];
|
|
324
|
+
}
|
|
325
|
+
function declaredEventVersion(event) {
|
|
326
|
+
return event.schemaVersion ?? CURSOR_SOURCE_SCHEMA_VERSION;
|
|
327
|
+
}
|
|
328
|
+
function extractCloudText(value) {
|
|
329
|
+
const object = asObject(value);
|
|
330
|
+
if (typeof object.text === 'string')
|
|
331
|
+
return object.text;
|
|
332
|
+
if (typeof object.message === 'string')
|
|
333
|
+
return object.message;
|
|
334
|
+
return '';
|
|
335
|
+
}
|
|
336
|
+
function cloudLifecycle(type, status) {
|
|
337
|
+
if (type === 'agent.deleted')
|
|
338
|
+
return 'deleted';
|
|
339
|
+
if (type === 'agent.archived')
|
|
340
|
+
return 'archived';
|
|
341
|
+
if (type === 'agent.unarchived')
|
|
342
|
+
return 'active';
|
|
343
|
+
if (status === 'cancelled' || status === 'canceled')
|
|
344
|
+
return 'cancelled';
|
|
345
|
+
return isTerminal(status) ? 'complete' : 'active';
|
|
346
|
+
}
|
|
347
|
+
function isTerminal(status) {
|
|
348
|
+
return ['completed', 'failed', 'cancelled', 'canceled'].includes(status ?? '');
|
|
349
|
+
}
|
|
350
|
+
function unknownFields(value, known) {
|
|
351
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !known.has(key)).sort(([a], [b]) => a.localeCompare(b)));
|
|
352
|
+
}
|
|
353
|
+
function asObject(value) {
|
|
354
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
355
|
+
? value
|
|
356
|
+
: {};
|
|
357
|
+
}
|
|
358
|
+
function parseCursor(value) {
|
|
359
|
+
if (!value)
|
|
360
|
+
return 0;
|
|
361
|
+
if (!/^\d+$/.test(value)) {
|
|
362
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Cursor record cursor');
|
|
363
|
+
}
|
|
364
|
+
return Number(value);
|
|
365
|
+
}
|
|
366
|
+
const CLI_KEYS = new Set([
|
|
367
|
+
'schemaVersion', 'type', 'subtype', 'session_id', 'request_id', 'call_id', 'cwd',
|
|
368
|
+
'model', 'permissionMode', 'cliVersion', 'version', 'message', 'tool_call', 'result', 'is_error',
|
|
369
|
+
'duration_ms', 'duration_api_ms',
|
|
370
|
+
]);
|
|
371
|
+
const CLOUD_KEYS = new Set(['schemaVersion', 'id', 'event_id', 'type', 'agent', 'run', 'data']);
|
|
372
|
+
//# sourceMappingURL=cursor.js.map
|