@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.
- package/README.md +18 -7
- package/dist/src/api/index.d.ts +2 -0
- package/dist/src/api/index.js +2 -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 +1265 -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/config/aiwg-config.js +12 -0
- package/dist/src/config/cli.js +16 -3
- package/dist/src/extensions/commands/definitions.js +29 -0
- package/dist/src/extensions/manifest.js +29 -0
- package/dist/src/security/threat-assessment-config.js +296 -0
- package/dist/src/sessions/adapters/claude.js +385 -0
- package/dist/src/sessions/adapters/codex.js +548 -0
- package/dist/src/sessions/adapters/copilot.js +226 -0
- package/dist/src/sessions/adapters/cursor.js +528 -0
- package/dist/src/sessions/adapters/factory.js +386 -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/batch-contracts.js +121 -0
- package/dist/src/sessions/batch-import.js +265 -0
- package/dist/src/sessions/candidates.js +210 -0
- package/dist/src/sessions/contracts.js +337 -0
- package/dist/src/sessions/discovery.js +51 -0
- package/dist/src/sessions/fixtures.js +12 -0
- package/dist/src/sessions/import-lease.js +152 -0
- package/dist/src/sessions/importer.js +464 -0
- package/dist/src/sessions/index.js +31 -0
- package/dist/src/sessions/knowledge-shard.js +61 -0
- package/dist/src/sessions/optional-backends.js +238 -0
- package/dist/src/sessions/origin.js +117 -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 +1892 -0
- package/dist/src/sessions/timeline.js +148 -0
- package/dist/src/sessions/workspace-discovery.js +319 -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,464 @@
|
|
|
1
|
+
import { SESSION_CONTRACT_VERSION, SessionEventSchema, SessionSchema, assertSupportedSchemaMajor, sha256, stableEventId, stableSessionId, SessionContractError, } from './contracts.js';
|
|
2
|
+
import { redactSessionText, sanitizeNativeExtensions } from './policy.js';
|
|
3
|
+
import { fingerprintSourcePrefix } from './readers.js';
|
|
4
|
+
import { classifySessionEventOrigin, deriveSessionIntent, } from './origin.js';
|
|
5
|
+
const DEFAULT_LIMITS = {
|
|
6
|
+
maxRecords: 1_000_000,
|
|
7
|
+
maxRecordBytes: 8 * 1024 * 1024,
|
|
8
|
+
maxTotalBytes: 1024 * 1024 * 1024,
|
|
9
|
+
batchSize: 1_000,
|
|
10
|
+
};
|
|
11
|
+
export class SessionImportFailure extends SessionContractError {
|
|
12
|
+
failureReceipt;
|
|
13
|
+
constructor(failureReceipt, cause) {
|
|
14
|
+
super(cause instanceof SessionContractError ? cause.code : 'IMPORT_INTERRUPTED', cause instanceof Error ? cause.message : 'session import failed');
|
|
15
|
+
this.failureReceipt = failureReceipt;
|
|
16
|
+
this.name = 'SessionImportFailure';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class IncrementalSessionImporter {
|
|
20
|
+
repository;
|
|
21
|
+
constructor(repository) {
|
|
22
|
+
this.repository = repository;
|
|
23
|
+
}
|
|
24
|
+
async import(request) {
|
|
25
|
+
assertSupportedSchemaMajor(request.source.sourceSchemaVersion);
|
|
26
|
+
if (request.inactivityThresholdMs !== undefined
|
|
27
|
+
&& (!Number.isFinite(request.inactivityThresholdMs) || request.inactivityThresholdMs < 0)) {
|
|
28
|
+
throw new SessionContractError('INVALID_ARGUMENT', 'session inactivity threshold must be a non-negative duration');
|
|
29
|
+
}
|
|
30
|
+
if (request.workspaceId !== request.selectedSource.authorizedScope.workspaceId) {
|
|
31
|
+
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'session import workspace is outside the authorized source scope');
|
|
32
|
+
}
|
|
33
|
+
const limits = { ...DEFAULT_LIMITS, ...request.limits };
|
|
34
|
+
const previous = request.batchRunId
|
|
35
|
+
? this.repository.getBatchCheckpoint(request.source.sourceId, request.adapter.adapterVersion, request.batchRunId) ?? this.repository.getCheckpoint(request.source.sourceId, request.adapter.adapterVersion)
|
|
36
|
+
: this.repository.getCheckpoint(request.source.sourceId, request.adapter.adapterVersion);
|
|
37
|
+
const continuity = await sourceContinuity(request, previous);
|
|
38
|
+
const sourceObservedAt = continuity
|
|
39
|
+
? new Date(continuity.mtimeMs).toISOString()
|
|
40
|
+
: undefined;
|
|
41
|
+
const sourceGeneration = continuity?.sourceGeneration ?? sourceGenerationDigest(request);
|
|
42
|
+
if (previous?.sourceGeneration && previous.sourceGeneration !== sourceGeneration) {
|
|
43
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source generation changed; explicit restart or migration is required');
|
|
44
|
+
}
|
|
45
|
+
if (previous?.adapterVersion && previous.adapterVersion !== request.adapter.adapterVersion) {
|
|
46
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint adapter version changed; checkpoint migration is required');
|
|
47
|
+
}
|
|
48
|
+
if (previous?.sourceSchemaVersion
|
|
49
|
+
&& previous.sourceSchemaVersion !== request.source.sourceSchemaVersion) {
|
|
50
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint source schema changed; checkpoint migration is required');
|
|
51
|
+
}
|
|
52
|
+
if (previous?.policyVersion && previous.policyVersion !== request.policyVersion) {
|
|
53
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint policy version changed; checkpoint migration is required');
|
|
54
|
+
}
|
|
55
|
+
const checkpoint = {
|
|
56
|
+
cursor: previous?.cursor ?? '',
|
|
57
|
+
recordsRead: previous?.recordsRead ?? 0,
|
|
58
|
+
bytesRead: previous?.bytesRead ?? 0,
|
|
59
|
+
checkpointVersion: '2',
|
|
60
|
+
positionKind: previous?.positionKind ?? 'record-index',
|
|
61
|
+
sourceGeneration,
|
|
62
|
+
locatorClass: request.selectedSource.locatorClass,
|
|
63
|
+
adapterVersion: request.adapter.adapterVersion,
|
|
64
|
+
sourceSchemaVersion: request.source.sourceSchemaVersion,
|
|
65
|
+
policyVersion: request.policyVersion,
|
|
66
|
+
continuity: continuity
|
|
67
|
+
? (!previous || previous.positionKind === 'byte-offset'
|
|
68
|
+
? continuity.outcome
|
|
69
|
+
: 'unverified')
|
|
70
|
+
: (previous ? 'unverified' : 'new-generation'),
|
|
71
|
+
sourceSize: continuity?.size,
|
|
72
|
+
sourceMtimeMs: continuity?.mtimeMs,
|
|
73
|
+
sourceFileIdentity: continuity?.fileIdentity,
|
|
74
|
+
prefixDigest: continuity?.prefixDigest,
|
|
75
|
+
};
|
|
76
|
+
const receipts = [];
|
|
77
|
+
let batch = [];
|
|
78
|
+
let batchStart = checkpoint.recordsRead;
|
|
79
|
+
let durableCheckpoint = { ...checkpoint };
|
|
80
|
+
const importStarted = performance.now();
|
|
81
|
+
let peakHeap = process.memoryUsage().heapUsed;
|
|
82
|
+
let peakRss = process.memoryUsage().rss;
|
|
83
|
+
const flush = () => {
|
|
84
|
+
if (batch.length === 0)
|
|
85
|
+
return;
|
|
86
|
+
if (request.signal?.aborted) {
|
|
87
|
+
throw new SessionContractError('IMPORT_INTERRUPTED', 'session import was cancelled at a durable batch boundary');
|
|
88
|
+
}
|
|
89
|
+
const runId = sha256([
|
|
90
|
+
request.source.sourceId, request.adapter.adapterVersion,
|
|
91
|
+
batchStart, checkpoint.recordsRead,
|
|
92
|
+
...batch.map((record) => sha256(JSON.stringify(record))),
|
|
93
|
+
].join('\0'));
|
|
94
|
+
const normalized = normalizeBatch(request, batch, runId, sourceObservedAt);
|
|
95
|
+
const run = {
|
|
96
|
+
contractVersion: SESSION_CONTRACT_VERSION,
|
|
97
|
+
importRunId: runId,
|
|
98
|
+
sourceId: request.source.sourceId,
|
|
99
|
+
parserVersion: request.adapter.adapterVersion,
|
|
100
|
+
policyVersion: request.policyVersion,
|
|
101
|
+
sourceSchemaVersion: request.source.sourceSchemaVersion,
|
|
102
|
+
consistency: request.source.consistency,
|
|
103
|
+
status: 'running',
|
|
104
|
+
checkpoint: { ...checkpoint },
|
|
105
|
+
startedAt: new Date().toISOString(),
|
|
106
|
+
completedAt: null,
|
|
107
|
+
errorCode: null,
|
|
108
|
+
};
|
|
109
|
+
const flushStarted = performance.now();
|
|
110
|
+
const sanitizedSource = {
|
|
111
|
+
...request.source,
|
|
112
|
+
extensions: sanitizeNativeExtensions(request.source.extensions).value,
|
|
113
|
+
};
|
|
114
|
+
const receipt = this.repository.applyImport({
|
|
115
|
+
source: sanitizedSource, run,
|
|
116
|
+
sessions: normalized.sessions, events: normalized.events,
|
|
117
|
+
batchRunId: request.batchRunId,
|
|
118
|
+
}, { ...checkpoint }, request.publish ?? request.source.consistency !== 'complete');
|
|
119
|
+
const memory = process.memoryUsage();
|
|
120
|
+
peakHeap = Math.max(peakHeap, memory.heapUsed);
|
|
121
|
+
peakRss = Math.max(peakRss, memory.rss);
|
|
122
|
+
receipt.metrics = {
|
|
123
|
+
records: batch.length,
|
|
124
|
+
normalizedEvents: normalized.events.length,
|
|
125
|
+
normalizedBytes: batch.reduce((total, record) => total + Buffer.byteLength(JSON.stringify(record)), 0),
|
|
126
|
+
durationMs: performance.now() - importStarted,
|
|
127
|
+
batchLatencyMs: performance.now() - flushStarted,
|
|
128
|
+
heapUsedBytes: peakHeap,
|
|
129
|
+
rssBytes: peakRss,
|
|
130
|
+
checkpointDurable: true,
|
|
131
|
+
};
|
|
132
|
+
receipts.push(receipt);
|
|
133
|
+
durableCheckpoint = { ...checkpoint };
|
|
134
|
+
batch = [];
|
|
135
|
+
batchStart = checkpoint.recordsRead;
|
|
136
|
+
};
|
|
137
|
+
try {
|
|
138
|
+
for await (const record of request.adapter.stream(request.selectedSource, previous ? { value: previous.cursor } : undefined)) {
|
|
139
|
+
if (request.signal?.aborted) {
|
|
140
|
+
throw new SessionContractError('IMPORT_INTERRUPTED', 'session import was cancelled and can resume from its durable checkpoint');
|
|
141
|
+
}
|
|
142
|
+
const bytes = Buffer.byteLength(JSON.stringify(record));
|
|
143
|
+
if (bytes > limits.maxRecordBytes
|
|
144
|
+
|| checkpoint.bytesRead + bytes > limits.maxTotalBytes
|
|
145
|
+
|| checkpoint.recordsRead + 1 > limits.maxRecords) {
|
|
146
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'session import exceeded an authorized resource limit');
|
|
147
|
+
}
|
|
148
|
+
batch.push(record);
|
|
149
|
+
checkpoint.recordsRead += 1;
|
|
150
|
+
if (record.sourceCursor?.startsWith('byte:')) {
|
|
151
|
+
checkpoint.positionKind = 'byte-offset';
|
|
152
|
+
checkpoint.cursor = record.sourceCursor;
|
|
153
|
+
checkpoint.bytesRead = Number(record.sourceCursor.slice(5));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
checkpoint.bytesRead += bytes;
|
|
157
|
+
checkpoint.cursor = String(checkpoint.recordsRead);
|
|
158
|
+
}
|
|
159
|
+
if (batch.length >= limits.batchSize)
|
|
160
|
+
flush();
|
|
161
|
+
}
|
|
162
|
+
flush();
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
const published = request.publish ?? request.source.consistency !== 'complete';
|
|
166
|
+
const committed = published ? receipts : [];
|
|
167
|
+
throw new SessionImportFailure({
|
|
168
|
+
contractVersion: '1.0.0',
|
|
169
|
+
outcome: 'terminal-failure',
|
|
170
|
+
sourceId: request.source.sourceId,
|
|
171
|
+
sourceGeneration,
|
|
172
|
+
consistency: request.source.consistency,
|
|
173
|
+
committedPrefix: {
|
|
174
|
+
batches: committed.length,
|
|
175
|
+
records: committed.reduce((total, receipt) => total + (receipt.metrics?.records ?? 0), 0),
|
|
176
|
+
events: committed.reduce((total, receipt) => total + receipt.eventsInserted, 0),
|
|
177
|
+
},
|
|
178
|
+
resumableCheckpoint: !published
|
|
179
|
+
? {
|
|
180
|
+
...durableCheckpoint,
|
|
181
|
+
cursor: '',
|
|
182
|
+
recordsRead: 0,
|
|
183
|
+
bytesRead: 0,
|
|
184
|
+
continuity: 'new-generation',
|
|
185
|
+
}
|
|
186
|
+
: durableCheckpoint,
|
|
187
|
+
errorCode: error instanceof SessionContractError
|
|
188
|
+
? error.code : 'IMPORT_INTERRUPTED',
|
|
189
|
+
}, error);
|
|
190
|
+
}
|
|
191
|
+
if (request.publish !== false && request.source.consistency === 'complete') {
|
|
192
|
+
this.repository.commitStagedImports(request.source.sourceId, request.adapter.adapterVersion);
|
|
193
|
+
for (const receipt of receipts) {
|
|
194
|
+
if (receipt.outcome === 'staged')
|
|
195
|
+
receipt.outcome = 'committed';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return receipts;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function normalizeBatch(request, records, importRunId, sourceObservedAt) {
|
|
202
|
+
const sessions = new Map();
|
|
203
|
+
const events = records.map((record) => {
|
|
204
|
+
const sessionId = stableSessionId(request.source.provider, request.source.sourceId, record.nativeSessionId);
|
|
205
|
+
const redacted = redactSessionText(record.text);
|
|
206
|
+
const native = sanitizeNativeExtensions(record.extensions ?? {});
|
|
207
|
+
const digest = sha256(JSON.stringify(record));
|
|
208
|
+
const origin = classifySessionEventOrigin(request.source.provider, record);
|
|
209
|
+
const lifecycle = sessionLifecycle(request, record, sourceObservedAt);
|
|
210
|
+
const lifecycleEvidence = sessionLifecycleEvidence(request, record, sourceObservedAt);
|
|
211
|
+
const extensions = {
|
|
212
|
+
[`native.${request.source.provider}`]: native.value,
|
|
213
|
+
};
|
|
214
|
+
const event = SessionEventSchema.parse({
|
|
215
|
+
contractVersion: SESSION_CONTRACT_VERSION,
|
|
216
|
+
eventId: stableEventId(request.source.provider, request.source.sourceId, record, digest),
|
|
217
|
+
sessionId, sourceId: request.source.sourceId, importRunId,
|
|
218
|
+
nativeId: record.nativeEventId ?? null, sequence: record.sequence,
|
|
219
|
+
kind: record.kind, role: record.role ?? null, occurredAt: record.occurredAt ?? null,
|
|
220
|
+
activityBoundary: record.activityBoundary ?? null,
|
|
221
|
+
activityBoundaryBasis: record.activityBoundaryBasis ?? null,
|
|
222
|
+
activityBoundaryConfidence: record.activityBoundaryConfidence ?? null,
|
|
223
|
+
origin: origin.origin,
|
|
224
|
+
originRule: origin.rule,
|
|
225
|
+
originClassifierVersion: origin.classifierVersion,
|
|
226
|
+
participant: record.participant ?? null,
|
|
227
|
+
toolName: record.toolName ?? null,
|
|
228
|
+
toolCallId: record.toolCallId ?? null,
|
|
229
|
+
model: record.model ?? null,
|
|
230
|
+
entities: record.entities ?? [],
|
|
231
|
+
extractionState: record.extractionState ?? null,
|
|
232
|
+
searchableText: redacted.text, digest, rawReference: record.rawReference,
|
|
233
|
+
adapterVersion: request.adapter.adapterVersion,
|
|
234
|
+
consistency: request.source.consistency,
|
|
235
|
+
sensitivity: {
|
|
236
|
+
classification: redacted.sensitivity === 'sensitive' || native.sensitivity === 'sensitive'
|
|
237
|
+
? 'sensitive' : 'none',
|
|
238
|
+
classes: [...new Set([...redacted.classes, ...native.classes])].sort(),
|
|
239
|
+
},
|
|
240
|
+
opaque: !KNOWN_EVENT_KINDS.has(record.kind),
|
|
241
|
+
extensions,
|
|
242
|
+
});
|
|
243
|
+
if (!sessions.has(sessionId)) {
|
|
244
|
+
sessions.set(sessionId, SessionSchema.parse({
|
|
245
|
+
contractVersion: SESSION_CONTRACT_VERSION,
|
|
246
|
+
sessionId, sourceId: request.source.sourceId, provider: request.source.provider,
|
|
247
|
+
nativeSessionId: record.nativeSessionId, workspaceId: request.workspaceId,
|
|
248
|
+
startedAt: record.occurredAt ?? null, updatedAt: record.occurredAt ?? null,
|
|
249
|
+
consistency: request.source.consistency,
|
|
250
|
+
lifecycle,
|
|
251
|
+
sourceDigest: sha256(`${sourceGenerationDigest(request)}\0${record.nativeSessionId}`),
|
|
252
|
+
extensions: {
|
|
253
|
+
[`native.${request.source.provider}`]: {
|
|
254
|
+
lifecycleEvidence,
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
const session = sessions.get(sessionId);
|
|
261
|
+
session.startedAt = earlierTimestamp(session.startedAt, record.occurredAt ?? null);
|
|
262
|
+
session.updatedAt = laterTimestamp(session.updatedAt, record.occurredAt ?? null);
|
|
263
|
+
session.consistency = strongerConsistency(session.consistency, request.source.consistency);
|
|
264
|
+
const nativeKey = `native.${request.source.provider}`;
|
|
265
|
+
const nativeEnvelope = session.extensions[nativeKey];
|
|
266
|
+
const nativeRecord = nativeEnvelope && typeof nativeEnvelope === 'object' && !Array.isArray(nativeEnvelope)
|
|
267
|
+
? nativeEnvelope
|
|
268
|
+
: {};
|
|
269
|
+
const selected = selectLifecycleTransition(session.lifecycle, nativeRecord.lifecycleEvidence, lifecycle, lifecycleEvidence);
|
|
270
|
+
session.lifecycle = selected.lifecycle;
|
|
271
|
+
session.extensions[nativeKey] = {
|
|
272
|
+
...nativeRecord,
|
|
273
|
+
lifecycleEvidence: selected.evidence,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return event;
|
|
277
|
+
});
|
|
278
|
+
for (const session of sessions.values()) {
|
|
279
|
+
session.intent = deriveSessionIntent(events.filter((event) => event.sessionId === session.sessionId));
|
|
280
|
+
}
|
|
281
|
+
return { sessions: [...sessions.values()], events };
|
|
282
|
+
}
|
|
283
|
+
const KNOWN_EVENT_KINDS = new Set(['message', 'tool-call', 'tool-result', 'artifact', 'attachment', 'summary']);
|
|
284
|
+
function earlierTimestamp(left, right) {
|
|
285
|
+
if (!left)
|
|
286
|
+
return right;
|
|
287
|
+
if (!right)
|
|
288
|
+
return left;
|
|
289
|
+
return Date.parse(left) <= Date.parse(right) ? left : right;
|
|
290
|
+
}
|
|
291
|
+
function laterTimestamp(left, right) {
|
|
292
|
+
if (!left)
|
|
293
|
+
return right;
|
|
294
|
+
if (!right)
|
|
295
|
+
return left;
|
|
296
|
+
return Date.parse(left) >= Date.parse(right) ? left : right;
|
|
297
|
+
}
|
|
298
|
+
function strongerConsistency(left, right) {
|
|
299
|
+
const rank = {
|
|
300
|
+
provisional: 0,
|
|
301
|
+
'consistent-snapshot': 1,
|
|
302
|
+
complete: 2,
|
|
303
|
+
};
|
|
304
|
+
return rank[left] >= rank[right] ? left : right;
|
|
305
|
+
}
|
|
306
|
+
function sessionLifecycle(request, record, sourceObservedAt) {
|
|
307
|
+
const explicit = nativeLifecycle(record.extensions);
|
|
308
|
+
if (explicit)
|
|
309
|
+
return explicit;
|
|
310
|
+
if (request.source.consistency === 'complete')
|
|
311
|
+
return 'complete';
|
|
312
|
+
const observedAt = record.occurredAt ?? sourceObservedAt;
|
|
313
|
+
if (observedAt && isStaleHistoricalTimestamp(observedAt, inactivityThresholdMs(request)))
|
|
314
|
+
return 'inactive';
|
|
315
|
+
return 'active';
|
|
316
|
+
}
|
|
317
|
+
function sessionLifecycleEvidence(request, record, sourceObservedAt) {
|
|
318
|
+
const observedAt = record.occurredAt ?? sourceObservedAt;
|
|
319
|
+
const explicit = nativeLifecycle(record.extensions);
|
|
320
|
+
if (explicit) {
|
|
321
|
+
return {
|
|
322
|
+
basis: 'provider-explicit-event',
|
|
323
|
+
state: explicit,
|
|
324
|
+
observedAt: observedAt ?? request.source.authorizedAt,
|
|
325
|
+
confidence: 'high',
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
if (request.source.consistency === 'complete') {
|
|
329
|
+
return {
|
|
330
|
+
basis: 'complete-source',
|
|
331
|
+
state: 'complete',
|
|
332
|
+
observedAt: observedAt ?? request.source.authorizedAt,
|
|
333
|
+
confidence: 'high',
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const thresholdMs = inactivityThresholdMs(request);
|
|
337
|
+
if (observedAt && isStaleHistoricalTimestamp(observedAt, thresholdMs)) {
|
|
338
|
+
return {
|
|
339
|
+
basis: 'inactivity-threshold',
|
|
340
|
+
state: 'inactive',
|
|
341
|
+
observedAt,
|
|
342
|
+
confidence: 'medium',
|
|
343
|
+
thresholdMs,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
basis: 'open-provisional-source',
|
|
348
|
+
state: 'active',
|
|
349
|
+
observedAt: observedAt ?? request.source.authorizedAt,
|
|
350
|
+
confidence: 'provisional',
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function nativeLifecycle(extensions) {
|
|
354
|
+
const raw = typeof extensions?.lifecycle === 'string' ? extensions.lifecycle : undefined;
|
|
355
|
+
if (!raw)
|
|
356
|
+
return null;
|
|
357
|
+
const normalized = {
|
|
358
|
+
active: 'active',
|
|
359
|
+
inactive: 'inactive',
|
|
360
|
+
paused: 'paused',
|
|
361
|
+
complete: 'complete',
|
|
362
|
+
completed: 'complete',
|
|
363
|
+
archived: 'archived',
|
|
364
|
+
deleted: 'tombstoned',
|
|
365
|
+
cancelled: 'interrupted',
|
|
366
|
+
canceled: 'interrupted',
|
|
367
|
+
failed: 'interrupted',
|
|
368
|
+
interrupted: 'interrupted',
|
|
369
|
+
unknown: 'unknown',
|
|
370
|
+
'unknown-at-import': 'unknown',
|
|
371
|
+
'completed-at-import': 'complete',
|
|
372
|
+
};
|
|
373
|
+
return normalized[raw] ?? null;
|
|
374
|
+
}
|
|
375
|
+
function selectLifecycleTransition(leftLifecycle, left, rightLifecycle, right) {
|
|
376
|
+
const leftEvidence = asLifecycleEvidence(left);
|
|
377
|
+
if (leftLifecycle === 'tombstoned') {
|
|
378
|
+
return { lifecycle: leftLifecycle, evidence: leftEvidence ?? right };
|
|
379
|
+
}
|
|
380
|
+
if (!leftEvidence)
|
|
381
|
+
return { lifecycle: rightLifecycle, evidence: right };
|
|
382
|
+
const rank = {
|
|
383
|
+
'open-provisional-source': 1,
|
|
384
|
+
'inactivity-threshold': 1,
|
|
385
|
+
'complete-source': 2,
|
|
386
|
+
'provider-explicit-event': 3,
|
|
387
|
+
};
|
|
388
|
+
const leftRank = rank[String(leftEvidence.basis)] ?? 0;
|
|
389
|
+
const rightRank = rank[String(right.basis)] ?? 0;
|
|
390
|
+
const rightIsNewer = evidenceTimestamp(right) > evidenceTimestamp(leftEvidence);
|
|
391
|
+
return rightRank > leftRank || (rightRank === leftRank && rightIsNewer)
|
|
392
|
+
? { lifecycle: rightLifecycle, evidence: right }
|
|
393
|
+
: { lifecycle: leftLifecycle, evidence: leftEvidence };
|
|
394
|
+
}
|
|
395
|
+
function asLifecycleEvidence(value) {
|
|
396
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
397
|
+
? value
|
|
398
|
+
: null;
|
|
399
|
+
}
|
|
400
|
+
function evidenceTimestamp(value) {
|
|
401
|
+
const parsed = Date.parse(String(value.observedAt ?? ''));
|
|
402
|
+
return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
|
|
403
|
+
}
|
|
404
|
+
const DEFAULT_INACTIVITY_THRESHOLD_MS = 24 * 60 * 60 * 1000;
|
|
405
|
+
function inactivityThresholdMs(request) {
|
|
406
|
+
return request.inactivityThresholdMs ?? DEFAULT_INACTIVITY_THRESHOLD_MS;
|
|
407
|
+
}
|
|
408
|
+
function isStaleHistoricalTimestamp(value, thresholdMs) {
|
|
409
|
+
const timestamp = Date.parse(value);
|
|
410
|
+
return Number.isFinite(timestamp)
|
|
411
|
+
&& Date.now() - timestamp > thresholdMs;
|
|
412
|
+
}
|
|
413
|
+
function sourceGenerationDigest(request) {
|
|
414
|
+
return sha256([
|
|
415
|
+
request.source.sourceId,
|
|
416
|
+
request.source.provider,
|
|
417
|
+
request.selectedSource.locatorClass,
|
|
418
|
+
request.source.sourceSchemaVersion,
|
|
419
|
+
request.adapter.adapterVersion,
|
|
420
|
+
].join('\0'));
|
|
421
|
+
}
|
|
422
|
+
async function sourceContinuity(request, previous) {
|
|
423
|
+
if (request.selectedSource.authorizedScope.allowedRoots.length === 0
|
|
424
|
+
|| /^[a-z][a-z0-9+.-]*:\/\//i.test(request.selectedSource.locator)
|
|
425
|
+
|| request.selectedSource.locator.startsWith('<')) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
const authorization = {
|
|
429
|
+
selectedPath: request.selectedSource.locator,
|
|
430
|
+
allowedRoots: request.selectedSource.authorizedScope.allowedRoots,
|
|
431
|
+
};
|
|
432
|
+
const metadata = await fingerprintSourcePrefix(authorization, 0);
|
|
433
|
+
if (previous?.sourceSize !== undefined) {
|
|
434
|
+
if (metadata.size < previous.sourceSize) {
|
|
435
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source was truncated before its durable checkpoint');
|
|
436
|
+
}
|
|
437
|
+
if (previous.sourceFileIdentity
|
|
438
|
+
&& previous.sourceFileIdentity !== metadata.fileIdentity) {
|
|
439
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source file generation was replaced or rotated');
|
|
440
|
+
}
|
|
441
|
+
const priorPrefix = await fingerprintSourcePrefix(authorization, previous.sourceSize);
|
|
442
|
+
if (previous.prefixDigest && priorPrefix.digest !== previous.prefixDigest) {
|
|
443
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source prefix was rewritten before its durable checkpoint');
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const current = await fingerprintSourcePrefix(authorization, metadata.size);
|
|
447
|
+
const generation = previous?.sourceGeneration ?? sha256([
|
|
448
|
+
sourceGenerationDigest(request),
|
|
449
|
+
metadata.fileIdentity,
|
|
450
|
+
].join('\0'));
|
|
451
|
+
return {
|
|
452
|
+
sourceGeneration: generation,
|
|
453
|
+
outcome: !previous
|
|
454
|
+
? 'new-generation'
|
|
455
|
+
: metadata.size === previous.sourceSize
|
|
456
|
+
&& current.digest === previous.prefixDigest
|
|
457
|
+
? 'unchanged-replay' : 'validated-append',
|
|
458
|
+
size: metadata.size,
|
|
459
|
+
mtimeMs: metadata.mtimeMs,
|
|
460
|
+
fileIdentity: metadata.fileIdentity,
|
|
461
|
+
prefixDigest: current.digest,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
//# sourceMappingURL=importer.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export * from './contracts.js';
|
|
2
|
+
export * from './fixtures.js';
|
|
3
|
+
export * from './policy.js';
|
|
4
|
+
export * from './discovery.js';
|
|
5
|
+
export * from './readers.js';
|
|
6
|
+
export * from './repository.js';
|
|
7
|
+
export * from './ports.js';
|
|
8
|
+
export * from './optional-backends.js';
|
|
9
|
+
export * from './knowledge-shard.js';
|
|
10
|
+
export * from './candidates.js';
|
|
11
|
+
export * from './promotion.js';
|
|
12
|
+
export * from './importer.js';
|
|
13
|
+
export * from './import-lease.js';
|
|
14
|
+
export * from './batch-contracts.js';
|
|
15
|
+
export * from './batch-import.js';
|
|
16
|
+
export * from './workspace-discovery.js';
|
|
17
|
+
export * from './timeline.js';
|
|
18
|
+
export * from './origin.js';
|
|
19
|
+
export * from './adapters/generic.js';
|
|
20
|
+
export * from './adapters/claude.js';
|
|
21
|
+
export * from './adapters/codex.js';
|
|
22
|
+
export * from './adapters/copilot.js';
|
|
23
|
+
export * from './adapters/cursor.js';
|
|
24
|
+
export * from './adapters/factory.js';
|
|
25
|
+
export * from './adapters/hermes.js';
|
|
26
|
+
export * from './adapters/opencode.js';
|
|
27
|
+
export * from './adapters/openclaw.js';
|
|
28
|
+
export * from './adapters/openhuman.js';
|
|
29
|
+
export * from './adapters/warp.js';
|
|
30
|
+
export * from './adapters/windsurf.js';
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function convertSessionEventsToKnowledgeShard(source, events) {
|
|
2
|
+
const losses = [];
|
|
3
|
+
const records = events.map((event) => {
|
|
4
|
+
if (Object.values(event.extensions).some(hasPortableValue)) {
|
|
5
|
+
losses.push({
|
|
6
|
+
code: 'NATIVE_EXTENSION_NOT_PORTABLE',
|
|
7
|
+
eventId: event.eventId,
|
|
8
|
+
field: 'extensions',
|
|
9
|
+
reason: 'Knowledge Shard v1 has no canonical provider-native extension field',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
if (event.rawReference.offset !== undefined) {
|
|
13
|
+
losses.push({
|
|
14
|
+
code: 'RAW_OFFSET_NOT_PORTABLE',
|
|
15
|
+
eventId: event.eventId,
|
|
16
|
+
field: 'rawReference.offset',
|
|
17
|
+
reason: 'Knowledge Shard v1 preserves locator class but not byte offsets',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
if (event.rawReference.sequence !== undefined) {
|
|
21
|
+
losses.push({
|
|
22
|
+
code: 'RAW_SEQUENCE_NOT_PORTABLE',
|
|
23
|
+
eventId: event.eventId,
|
|
24
|
+
field: 'rawReference.sequence',
|
|
25
|
+
reason: 'Knowledge Shard v1 preserves event identity but not provider sequence locators',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
id: event.eventId,
|
|
30
|
+
content: event.searchableText,
|
|
31
|
+
metadata: {
|
|
32
|
+
provider: source.provider,
|
|
33
|
+
sessionId: event.sessionId,
|
|
34
|
+
eventId: event.eventId,
|
|
35
|
+
importRunId: event.importRunId,
|
|
36
|
+
sourceId: event.sourceId,
|
|
37
|
+
locatorClass: source.locatorClass,
|
|
38
|
+
role: event.role,
|
|
39
|
+
occurredAt: event.occurredAt,
|
|
40
|
+
sensitivity: event.sensitivity.classification,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
contractVersion: '1.0.0',
|
|
46
|
+
target: 'knowledge-shard-v1',
|
|
47
|
+
records,
|
|
48
|
+
losses,
|
|
49
|
+
lossless: losses.length === 0,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function hasPortableValue(value) {
|
|
53
|
+
if (value === null || value === undefined)
|
|
54
|
+
return false;
|
|
55
|
+
if (Array.isArray(value))
|
|
56
|
+
return value.length > 0;
|
|
57
|
+
if (typeof value === 'object')
|
|
58
|
+
return Object.keys(value).length > 0;
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=knowledge-shard.js.map
|