@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,315 @@
|
|
|
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
|
+
const DEFAULT_LIMITS = {
|
|
5
|
+
maxRecords: 1_000_000,
|
|
6
|
+
maxRecordBytes: 4 * 1024 * 1024,
|
|
7
|
+
maxTotalBytes: 1024 * 1024 * 1024,
|
|
8
|
+
batchSize: 1_000,
|
|
9
|
+
};
|
|
10
|
+
export class SessionImportFailure extends SessionContractError {
|
|
11
|
+
failureReceipt;
|
|
12
|
+
constructor(failureReceipt, cause) {
|
|
13
|
+
super(cause instanceof SessionContractError ? cause.code : 'IMPORT_INTERRUPTED', cause instanceof Error ? cause.message : 'session import failed');
|
|
14
|
+
this.failureReceipt = failureReceipt;
|
|
15
|
+
this.name = 'SessionImportFailure';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class IncrementalSessionImporter {
|
|
19
|
+
repository;
|
|
20
|
+
constructor(repository) {
|
|
21
|
+
this.repository = repository;
|
|
22
|
+
}
|
|
23
|
+
async import(request) {
|
|
24
|
+
assertSupportedSchemaMajor(request.source.sourceSchemaVersion);
|
|
25
|
+
if (request.workspaceId !== request.selectedSource.authorizedScope.workspaceId) {
|
|
26
|
+
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'session import workspace is outside the authorized source scope');
|
|
27
|
+
}
|
|
28
|
+
const limits = { ...DEFAULT_LIMITS, ...request.limits };
|
|
29
|
+
const previous = this.repository.getCheckpoint(request.source.sourceId, request.adapter.adapterVersion);
|
|
30
|
+
const continuity = await sourceContinuity(request, previous);
|
|
31
|
+
const sourceGeneration = continuity?.sourceGeneration ?? sourceGenerationDigest(request);
|
|
32
|
+
if (previous?.sourceGeneration && previous.sourceGeneration !== sourceGeneration) {
|
|
33
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source generation changed; explicit restart or migration is required');
|
|
34
|
+
}
|
|
35
|
+
if (previous?.adapterVersion && previous.adapterVersion !== request.adapter.adapterVersion) {
|
|
36
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint adapter version changed; checkpoint migration is required');
|
|
37
|
+
}
|
|
38
|
+
if (previous?.sourceSchemaVersion
|
|
39
|
+
&& previous.sourceSchemaVersion !== request.source.sourceSchemaVersion) {
|
|
40
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint source schema changed; checkpoint migration is required');
|
|
41
|
+
}
|
|
42
|
+
if (previous?.policyVersion && previous.policyVersion !== request.policyVersion) {
|
|
43
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint policy version changed; checkpoint migration is required');
|
|
44
|
+
}
|
|
45
|
+
const checkpoint = {
|
|
46
|
+
cursor: previous?.cursor ?? '',
|
|
47
|
+
recordsRead: previous?.recordsRead ?? 0,
|
|
48
|
+
bytesRead: previous?.bytesRead ?? 0,
|
|
49
|
+
checkpointVersion: '2',
|
|
50
|
+
positionKind: previous?.positionKind ?? 'record-index',
|
|
51
|
+
sourceGeneration,
|
|
52
|
+
locatorClass: request.selectedSource.locatorClass,
|
|
53
|
+
adapterVersion: request.adapter.adapterVersion,
|
|
54
|
+
sourceSchemaVersion: request.source.sourceSchemaVersion,
|
|
55
|
+
policyVersion: request.policyVersion,
|
|
56
|
+
continuity: continuity
|
|
57
|
+
? (!previous || previous.positionKind === 'byte-offset'
|
|
58
|
+
? continuity.outcome
|
|
59
|
+
: 'unverified')
|
|
60
|
+
: (previous ? 'unverified' : 'new-generation'),
|
|
61
|
+
sourceSize: continuity?.size,
|
|
62
|
+
sourceMtimeMs: continuity?.mtimeMs,
|
|
63
|
+
sourceFileIdentity: continuity?.fileIdentity,
|
|
64
|
+
prefixDigest: continuity?.prefixDigest,
|
|
65
|
+
};
|
|
66
|
+
const receipts = [];
|
|
67
|
+
let batch = [];
|
|
68
|
+
let batchStart = checkpoint.recordsRead;
|
|
69
|
+
let durableCheckpoint = { ...checkpoint };
|
|
70
|
+
const importStarted = performance.now();
|
|
71
|
+
let peakHeap = process.memoryUsage().heapUsed;
|
|
72
|
+
let peakRss = process.memoryUsage().rss;
|
|
73
|
+
const flush = () => {
|
|
74
|
+
if (batch.length === 0)
|
|
75
|
+
return;
|
|
76
|
+
const runId = sha256([
|
|
77
|
+
request.source.sourceId, request.adapter.adapterVersion,
|
|
78
|
+
batchStart, checkpoint.recordsRead,
|
|
79
|
+
...batch.map((record) => sha256(JSON.stringify(record))),
|
|
80
|
+
].join('\0'));
|
|
81
|
+
const normalized = normalizeBatch(request, batch, runId);
|
|
82
|
+
const run = {
|
|
83
|
+
contractVersion: SESSION_CONTRACT_VERSION,
|
|
84
|
+
importRunId: runId,
|
|
85
|
+
sourceId: request.source.sourceId,
|
|
86
|
+
parserVersion: request.adapter.adapterVersion,
|
|
87
|
+
policyVersion: request.policyVersion,
|
|
88
|
+
sourceSchemaVersion: request.source.sourceSchemaVersion,
|
|
89
|
+
consistency: request.source.consistency,
|
|
90
|
+
status: 'running',
|
|
91
|
+
checkpoint: { ...checkpoint },
|
|
92
|
+
startedAt: new Date().toISOString(),
|
|
93
|
+
completedAt: null,
|
|
94
|
+
errorCode: null,
|
|
95
|
+
};
|
|
96
|
+
const flushStarted = performance.now();
|
|
97
|
+
const sanitizedSource = {
|
|
98
|
+
...request.source,
|
|
99
|
+
extensions: sanitizeNativeExtensions(request.source.extensions).value,
|
|
100
|
+
};
|
|
101
|
+
const receipt = this.repository.applyImport({
|
|
102
|
+
source: sanitizedSource, run,
|
|
103
|
+
sessions: normalized.sessions, events: normalized.events,
|
|
104
|
+
}, { ...checkpoint }, request.source.consistency !== 'complete');
|
|
105
|
+
const memory = process.memoryUsage();
|
|
106
|
+
peakHeap = Math.max(peakHeap, memory.heapUsed);
|
|
107
|
+
peakRss = Math.max(peakRss, memory.rss);
|
|
108
|
+
receipt.metrics = {
|
|
109
|
+
records: batch.length,
|
|
110
|
+
normalizedEvents: normalized.events.length,
|
|
111
|
+
normalizedBytes: batch.reduce((total, record) => total + Buffer.byteLength(JSON.stringify(record)), 0),
|
|
112
|
+
durationMs: performance.now() - importStarted,
|
|
113
|
+
batchLatencyMs: performance.now() - flushStarted,
|
|
114
|
+
heapUsedBytes: peakHeap,
|
|
115
|
+
rssBytes: peakRss,
|
|
116
|
+
checkpointDurable: true,
|
|
117
|
+
};
|
|
118
|
+
receipts.push(receipt);
|
|
119
|
+
durableCheckpoint = { ...checkpoint };
|
|
120
|
+
batch = [];
|
|
121
|
+
batchStart = checkpoint.recordsRead;
|
|
122
|
+
};
|
|
123
|
+
try {
|
|
124
|
+
for await (const record of request.adapter.stream(request.selectedSource, previous ? { value: previous.cursor } : undefined)) {
|
|
125
|
+
const bytes = Buffer.byteLength(JSON.stringify(record));
|
|
126
|
+
if (bytes > limits.maxRecordBytes
|
|
127
|
+
|| checkpoint.bytesRead + bytes > limits.maxTotalBytes
|
|
128
|
+
|| checkpoint.recordsRead + 1 > limits.maxRecords) {
|
|
129
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'session import exceeded an authorized resource limit');
|
|
130
|
+
}
|
|
131
|
+
batch.push(record);
|
|
132
|
+
checkpoint.recordsRead += 1;
|
|
133
|
+
if (record.sourceCursor?.startsWith('byte:')) {
|
|
134
|
+
checkpoint.positionKind = 'byte-offset';
|
|
135
|
+
checkpoint.cursor = record.sourceCursor;
|
|
136
|
+
checkpoint.bytesRead = Number(record.sourceCursor.slice(5));
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
checkpoint.bytesRead += bytes;
|
|
140
|
+
checkpoint.cursor = String(checkpoint.recordsRead);
|
|
141
|
+
}
|
|
142
|
+
if (batch.length >= limits.batchSize)
|
|
143
|
+
flush();
|
|
144
|
+
}
|
|
145
|
+
flush();
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
const committed = request.source.consistency === 'complete' ? [] : receipts;
|
|
149
|
+
throw new SessionImportFailure({
|
|
150
|
+
contractVersion: '1.0.0',
|
|
151
|
+
outcome: 'terminal-failure',
|
|
152
|
+
sourceId: request.source.sourceId,
|
|
153
|
+
sourceGeneration,
|
|
154
|
+
consistency: request.source.consistency,
|
|
155
|
+
committedPrefix: {
|
|
156
|
+
batches: committed.length,
|
|
157
|
+
records: committed.reduce((total, receipt) => total + (receipt.metrics?.records ?? 0), 0),
|
|
158
|
+
events: committed.reduce((total, receipt) => total + receipt.eventsInserted, 0),
|
|
159
|
+
},
|
|
160
|
+
resumableCheckpoint: request.source.consistency === 'complete'
|
|
161
|
+
? {
|
|
162
|
+
...durableCheckpoint,
|
|
163
|
+
cursor: '',
|
|
164
|
+
recordsRead: 0,
|
|
165
|
+
bytesRead: 0,
|
|
166
|
+
continuity: 'new-generation',
|
|
167
|
+
}
|
|
168
|
+
: durableCheckpoint,
|
|
169
|
+
errorCode: error instanceof SessionContractError
|
|
170
|
+
? error.code : 'IMPORT_INTERRUPTED',
|
|
171
|
+
}, error);
|
|
172
|
+
}
|
|
173
|
+
if (request.source.consistency === 'complete') {
|
|
174
|
+
this.repository.commitStagedImports(request.source.sourceId, request.adapter.adapterVersion);
|
|
175
|
+
for (const receipt of receipts) {
|
|
176
|
+
if (receipt.outcome === 'staged')
|
|
177
|
+
receipt.outcome = 'committed';
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return receipts;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function normalizeBatch(request, records, importRunId) {
|
|
184
|
+
const sessions = new Map();
|
|
185
|
+
const events = records.map((record) => {
|
|
186
|
+
const sessionId = stableSessionId(request.source.provider, request.source.sourceId, record.nativeSessionId);
|
|
187
|
+
const redacted = redactSessionText(record.text);
|
|
188
|
+
const native = sanitizeNativeExtensions(record.extensions ?? {});
|
|
189
|
+
const digest = sha256(JSON.stringify(record));
|
|
190
|
+
const extensions = {
|
|
191
|
+
[`native.${request.source.provider}`]: native.value,
|
|
192
|
+
};
|
|
193
|
+
const event = SessionEventSchema.parse({
|
|
194
|
+
contractVersion: SESSION_CONTRACT_VERSION,
|
|
195
|
+
eventId: stableEventId(request.source.sourceId, record, digest),
|
|
196
|
+
sessionId, sourceId: request.source.sourceId, importRunId,
|
|
197
|
+
nativeId: record.nativeEventId ?? null, sequence: record.sequence,
|
|
198
|
+
kind: record.kind, role: record.role ?? null, occurredAt: record.occurredAt ?? null,
|
|
199
|
+
participant: record.participant ?? null,
|
|
200
|
+
toolName: record.toolName ?? null,
|
|
201
|
+
toolCallId: record.toolCallId ?? null,
|
|
202
|
+
model: record.model ?? null,
|
|
203
|
+
entities: record.entities ?? [],
|
|
204
|
+
extractionState: record.extractionState ?? null,
|
|
205
|
+
searchableText: redacted.text, digest, rawReference: record.rawReference,
|
|
206
|
+
adapterVersion: request.adapter.adapterVersion,
|
|
207
|
+
consistency: request.source.consistency,
|
|
208
|
+
sensitivity: {
|
|
209
|
+
classification: redacted.sensitivity === 'sensitive' || native.sensitivity === 'sensitive'
|
|
210
|
+
? 'sensitive' : 'none',
|
|
211
|
+
classes: [...new Set([...redacted.classes, ...native.classes])].sort(),
|
|
212
|
+
},
|
|
213
|
+
opaque: !KNOWN_EVENT_KINDS.has(record.kind),
|
|
214
|
+
extensions,
|
|
215
|
+
});
|
|
216
|
+
if (!sessions.has(sessionId)) {
|
|
217
|
+
sessions.set(sessionId, SessionSchema.parse({
|
|
218
|
+
contractVersion: SESSION_CONTRACT_VERSION,
|
|
219
|
+
sessionId, sourceId: request.source.sourceId, provider: request.source.provider,
|
|
220
|
+
nativeSessionId: record.nativeSessionId, workspaceId: request.workspaceId,
|
|
221
|
+
startedAt: record.occurredAt ?? null, updatedAt: record.occurredAt ?? null,
|
|
222
|
+
consistency: request.source.consistency,
|
|
223
|
+
lifecycle: request.source.consistency === 'complete' ? 'complete' : 'active',
|
|
224
|
+
sourceDigest: sha256(`${sourceGenerationDigest(request)}\0${record.nativeSessionId}`),
|
|
225
|
+
extensions: { [`native.${request.source.provider}`]: {} },
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
const session = sessions.get(sessionId);
|
|
230
|
+
session.startedAt = earlierTimestamp(session.startedAt, record.occurredAt ?? null);
|
|
231
|
+
session.updatedAt = laterTimestamp(session.updatedAt, record.occurredAt ?? null);
|
|
232
|
+
session.consistency = strongerConsistency(session.consistency, request.source.consistency);
|
|
233
|
+
if (session.lifecycle !== 'tombstoned' && request.source.consistency === 'complete') {
|
|
234
|
+
session.lifecycle = 'complete';
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return event;
|
|
238
|
+
});
|
|
239
|
+
return { sessions: [...sessions.values()], events };
|
|
240
|
+
}
|
|
241
|
+
const KNOWN_EVENT_KINDS = new Set(['message', 'tool-call', 'tool-result', 'artifact', 'attachment', 'summary']);
|
|
242
|
+
function earlierTimestamp(left, right) {
|
|
243
|
+
if (!left)
|
|
244
|
+
return right;
|
|
245
|
+
if (!right)
|
|
246
|
+
return left;
|
|
247
|
+
return Date.parse(left) <= Date.parse(right) ? left : right;
|
|
248
|
+
}
|
|
249
|
+
function laterTimestamp(left, right) {
|
|
250
|
+
if (!left)
|
|
251
|
+
return right;
|
|
252
|
+
if (!right)
|
|
253
|
+
return left;
|
|
254
|
+
return Date.parse(left) >= Date.parse(right) ? left : right;
|
|
255
|
+
}
|
|
256
|
+
function strongerConsistency(left, right) {
|
|
257
|
+
const rank = {
|
|
258
|
+
provisional: 0,
|
|
259
|
+
'consistent-snapshot': 1,
|
|
260
|
+
complete: 2,
|
|
261
|
+
};
|
|
262
|
+
return rank[left] >= rank[right] ? left : right;
|
|
263
|
+
}
|
|
264
|
+
function sourceGenerationDigest(request) {
|
|
265
|
+
return sha256([
|
|
266
|
+
request.source.sourceId,
|
|
267
|
+
request.source.provider,
|
|
268
|
+
request.selectedSource.locatorClass,
|
|
269
|
+
request.source.sourceSchemaVersion,
|
|
270
|
+
request.adapter.adapterVersion,
|
|
271
|
+
].join('\0'));
|
|
272
|
+
}
|
|
273
|
+
async function sourceContinuity(request, previous) {
|
|
274
|
+
if (request.selectedSource.authorizedScope.allowedRoots.length === 0
|
|
275
|
+
|| /^[a-z][a-z0-9+.-]*:\/\//i.test(request.selectedSource.locator)
|
|
276
|
+
|| request.selectedSource.locator.startsWith('<')) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
const authorization = {
|
|
280
|
+
selectedPath: request.selectedSource.locator,
|
|
281
|
+
allowedRoots: request.selectedSource.authorizedScope.allowedRoots,
|
|
282
|
+
};
|
|
283
|
+
const metadata = await fingerprintSourcePrefix(authorization, 0);
|
|
284
|
+
if (previous?.sourceSize !== undefined) {
|
|
285
|
+
if (metadata.size < previous.sourceSize) {
|
|
286
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source was truncated before its durable checkpoint');
|
|
287
|
+
}
|
|
288
|
+
if (previous.sourceFileIdentity
|
|
289
|
+
&& previous.sourceFileIdentity !== metadata.fileIdentity) {
|
|
290
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source file generation was replaced or rotated');
|
|
291
|
+
}
|
|
292
|
+
const priorPrefix = await fingerprintSourcePrefix(authorization, previous.sourceSize);
|
|
293
|
+
if (previous.prefixDigest && priorPrefix.digest !== previous.prefixDigest) {
|
|
294
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source prefix was rewritten before its durable checkpoint');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const current = await fingerprintSourcePrefix(authorization, metadata.size);
|
|
298
|
+
const generation = previous?.sourceGeneration ?? sha256([
|
|
299
|
+
sourceGenerationDigest(request),
|
|
300
|
+
metadata.fileIdentity,
|
|
301
|
+
].join('\0'));
|
|
302
|
+
return {
|
|
303
|
+
sourceGeneration: generation,
|
|
304
|
+
outcome: !previous
|
|
305
|
+
? 'new-generation'
|
|
306
|
+
: metadata.size === previous.sourceSize
|
|
307
|
+
&& current.digest === previous.prefixDigest
|
|
308
|
+
? 'unchanged-replay' : 'validated-append',
|
|
309
|
+
size: metadata.size,
|
|
310
|
+
mtimeMs: metadata.mtimeMs,
|
|
311
|
+
fileIdentity: metadata.fileIdentity,
|
|
312
|
+
prefixDigest: current.digest,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=importer.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
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 './adapters/generic.js';
|
|
14
|
+
export * from './adapters/claude.js';
|
|
15
|
+
export * from './adapters/codex.js';
|
|
16
|
+
export * from './adapters/copilot.js';
|
|
17
|
+
export * from './adapters/cursor.js';
|
|
18
|
+
export * from './adapters/factory.js';
|
|
19
|
+
export * from './adapters/hermes.js';
|
|
20
|
+
export * from './adapters/opencode.js';
|
|
21
|
+
export * from './adapters/openclaw.js';
|
|
22
|
+
export * from './adapters/openhuman.js';
|
|
23
|
+
export * from './adapters/warp.js';
|
|
24
|
+
export * from './adapters/windsurf.js';
|
|
25
|
+
//# 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
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { sha256, SessionContractError } from './contracts.js';
|
|
2
|
+
export class FortemiSessionRepositoryBackend {
|
|
3
|
+
options;
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
}
|
|
7
|
+
previewImport(batch) {
|
|
8
|
+
const workspaceIds = [...new Set(batch.sessions.map((session) => session.workspaceId))];
|
|
9
|
+
if (workspaceIds.length !== 1) {
|
|
10
|
+
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'optional backend import must contain exactly one workspace scope');
|
|
11
|
+
}
|
|
12
|
+
return repositoryPreview({
|
|
13
|
+
operation: 'upsert',
|
|
14
|
+
workspaceId: workspaceIds[0],
|
|
15
|
+
sourceId: batch.source.sourceId,
|
|
16
|
+
eventIdentities: batch.events.map((event) => `${event.eventId}:${event.digest}`),
|
|
17
|
+
transfersApprovedText: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async applyImport(batch, checkpoint, authorization) {
|
|
21
|
+
this.assertCapabilities();
|
|
22
|
+
const preview = this.previewImport(batch);
|
|
23
|
+
assertRepositoryAuthorization(preview, authorization);
|
|
24
|
+
const result = await this.options.client.callTool('upsert_external_notes', {
|
|
25
|
+
source_namespace: `aiwg.sessions.${batch.source.provider}`,
|
|
26
|
+
source_id: batch.source.sourceId,
|
|
27
|
+
source_schema_version: batch.source.sourceSchemaVersion,
|
|
28
|
+
import_run_id: batch.run.importRunId,
|
|
29
|
+
workspace_id: preview.workspaceId,
|
|
30
|
+
items: batch.events.map((event) => ({
|
|
31
|
+
external_id: event.eventId,
|
|
32
|
+
content: event.searchableText,
|
|
33
|
+
content_digest: event.digest,
|
|
34
|
+
metadata: {
|
|
35
|
+
session_id: event.sessionId,
|
|
36
|
+
import_run_id: event.importRunId,
|
|
37
|
+
locator_class: batch.source.locatorClass,
|
|
38
|
+
role: event.role,
|
|
39
|
+
sensitivity: event.sensitivity.classification,
|
|
40
|
+
},
|
|
41
|
+
})),
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
operationId: batch.run.importRunId,
|
|
45
|
+
outcome: result?.outcome ?? 'committed',
|
|
46
|
+
sessionsInserted: result?.sessions_inserted ?? batch.sessions.length,
|
|
47
|
+
eventsInserted: result?.events_inserted ?? batch.events.length,
|
|
48
|
+
checkpoint,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
previewTombstone(workspaceId, sessionId, eventIds) {
|
|
52
|
+
return repositoryPreview({
|
|
53
|
+
operation: 'tombstone',
|
|
54
|
+
workspaceId,
|
|
55
|
+
sessionId,
|
|
56
|
+
eventIdentities: eventIds,
|
|
57
|
+
transfersApprovedText: false,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async tombstone(workspaceId, sessionId, eventIds, authorization) {
|
|
61
|
+
this.assertCapabilities(true);
|
|
62
|
+
const preview = this.previewTombstone(workspaceId, sessionId, eventIds);
|
|
63
|
+
assertRepositoryAuthorization(preview, authorization);
|
|
64
|
+
const result = await this.options.client.callTool('purge_external_notes', {
|
|
65
|
+
workspace_id: workspaceId,
|
|
66
|
+
session_id: sessionId,
|
|
67
|
+
external_ids: [...eventIds],
|
|
68
|
+
mode: 'tombstone',
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
operationId: preview.operationId,
|
|
72
|
+
outcome: 'committed',
|
|
73
|
+
affected: result?.affected ?? eventIds.length,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
assertCapabilities(requireGraphPurge = false) {
|
|
77
|
+
if (!this.options.capabilities.sourceAddressedUpsert
|
|
78
|
+
|| !this.options.capabilities.typedMetadataPredicates
|
|
79
|
+
|| !this.options.capabilities.evidenceLocators
|
|
80
|
+
|| (requireGraphPurge && !this.options.capabilities.graphPurge)) {
|
|
81
|
+
throw new SessionContractError('UNSUPPORTED_OPERATION', 'Fortemi session repository requires source upsert, typed predicates, evidence locators, and graph purge for deletion');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class FortemiSessionBackend {
|
|
86
|
+
options;
|
|
87
|
+
id = 'fortemi';
|
|
88
|
+
requiresNetwork = true;
|
|
89
|
+
requiresModel = true;
|
|
90
|
+
constructor(options) {
|
|
91
|
+
this.options = options;
|
|
92
|
+
}
|
|
93
|
+
async rank(request) {
|
|
94
|
+
if (!this.options.capabilities.sourceAddressedUpsert
|
|
95
|
+
|| !this.options.capabilities.typedMetadataPredicates
|
|
96
|
+
|| !this.options.capabilities.evidenceLocators) {
|
|
97
|
+
throw new SessionContractError('UNSUPPORTED_OPERATION', 'Fortemi session integration requires source upsert, typed predicates, and evidence locators');
|
|
98
|
+
}
|
|
99
|
+
const result = await this.options.client.callTool('search', {
|
|
100
|
+
query: request.query,
|
|
101
|
+
mode: 'hybrid',
|
|
102
|
+
predicates: [{ path: 'workspace_id', op: 'eq', value: request.workspaceId }],
|
|
103
|
+
candidates: request.documents.map((document) => ({
|
|
104
|
+
external_id: document.eventId,
|
|
105
|
+
content: document.text,
|
|
106
|
+
citation: document.citation,
|
|
107
|
+
})),
|
|
108
|
+
});
|
|
109
|
+
return (result?.results ?? [])
|
|
110
|
+
.filter((entry) => typeof entry.external_id === 'string' && typeof entry.score === 'number')
|
|
111
|
+
.map((entry) => ({ eventId: entry.external_id, score: entry.score }));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export class SessionSearchService {
|
|
115
|
+
lexical;
|
|
116
|
+
constructor(lexical) {
|
|
117
|
+
this.lexical = lexical;
|
|
118
|
+
}
|
|
119
|
+
preview(options, backend) {
|
|
120
|
+
const documents = this.documents(options);
|
|
121
|
+
return createPreview(options, backend, documents);
|
|
122
|
+
}
|
|
123
|
+
async search(request) {
|
|
124
|
+
if ((request.mode ?? 'lexical') === 'lexical' || !request.backend) {
|
|
125
|
+
return this.lexical.search(request.options);
|
|
126
|
+
}
|
|
127
|
+
const documents = this.documents(request.options);
|
|
128
|
+
const preview = createPreview(request.options, request.backend, documents);
|
|
129
|
+
if (!request.authorization?.approved
|
|
130
|
+
|| request.authorization.operationId !== preview.operationId) {
|
|
131
|
+
throw new SessionContractError(request.backend.requiresNetwork ? 'NETWORK_NOT_AUTHORIZED' : 'OPERATION_NOT_AUTHORIZED', 'optional semantic/backend search requires approval of the exact preview operation');
|
|
132
|
+
}
|
|
133
|
+
const ranked = await request.backend.rank({
|
|
134
|
+
query: request.options.query,
|
|
135
|
+
workspaceId: request.options.workspaceId,
|
|
136
|
+
documents: documents.map((document) => ({
|
|
137
|
+
eventId: document.eventId,
|
|
138
|
+
text: document.searchableText,
|
|
139
|
+
citation: document.citation,
|
|
140
|
+
})),
|
|
141
|
+
});
|
|
142
|
+
const authorized = new Map(documents.map((document) => [document.eventId, document]));
|
|
143
|
+
const semanticByEvent = new Map();
|
|
144
|
+
for (const candidate of ranked) {
|
|
145
|
+
if (!Number.isFinite(candidate.score) || !authorized.has(candidate.eventId))
|
|
146
|
+
continue;
|
|
147
|
+
semanticByEvent.set(candidate.eventId, Math.max(semanticByEvent.get(candidate.eventId) ?? Number.NEGATIVE_INFINITY, candidate.score));
|
|
148
|
+
}
|
|
149
|
+
const semantic = [...semanticByEvent.entries()]
|
|
150
|
+
.map(([eventId, score]) => ({ eventId, score }))
|
|
151
|
+
.sort((left, right) => right.score - left.score || left.eventId.localeCompare(right.eventId));
|
|
152
|
+
const lexical = this.lexical.search({
|
|
153
|
+
...request.options,
|
|
154
|
+
limit: Math.min(500, Math.max(request.options.limit * 2, request.options.limit)),
|
|
155
|
+
cursor: undefined,
|
|
156
|
+
}).items;
|
|
157
|
+
const scores = new Map();
|
|
158
|
+
lexical.forEach((hit, index) => scores.set(hit.eventId, 1 / (60 + index + 1)));
|
|
159
|
+
semantic.forEach((hit, index) => {
|
|
160
|
+
scores.set(hit.eventId, (scores.get(hit.eventId) ?? 0) + 1 / (60 + index + 1));
|
|
161
|
+
});
|
|
162
|
+
const publicHits = new Map([
|
|
163
|
+
...documents.map(({ searchableText: _text, ...hit }) => [hit.eventId, hit]),
|
|
164
|
+
...lexical.map((hit) => [hit.eventId, hit]),
|
|
165
|
+
]);
|
|
166
|
+
const items = [...scores.entries()]
|
|
167
|
+
.map(([eventId, score]) => ({ hit: publicHits.get(eventId), score }))
|
|
168
|
+
.filter((entry) => Boolean(entry.hit))
|
|
169
|
+
.sort((left, right) => right.score - left.score
|
|
170
|
+
|| left.hit.eventId.localeCompare(right.hit.eventId))
|
|
171
|
+
.slice(0, request.options.limit)
|
|
172
|
+
.map(({ hit, score }) => ({ ...hit, score }));
|
|
173
|
+
return { items, nextCursor: null };
|
|
174
|
+
}
|
|
175
|
+
documents(options) {
|
|
176
|
+
const { query: _query, cursor: _cursor, ...scope } = options;
|
|
177
|
+
return this.lexical.authorizedSearchDocuments({
|
|
178
|
+
...scope,
|
|
179
|
+
limit: Math.min(500, Math.max(100, options.limit * 20)),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function createPreview(options, backend, documents) {
|
|
184
|
+
const operationId = sha256(JSON.stringify({
|
|
185
|
+
backend: backend.id,
|
|
186
|
+
mode: 'hybrid',
|
|
187
|
+
workspaceId: options.workspaceId,
|
|
188
|
+
queryDigest: sha256(options.query),
|
|
189
|
+
scopeDigest: sha256(JSON.stringify({
|
|
190
|
+
providers: options.providers ?? [],
|
|
191
|
+
dateFrom: options.dateFrom ?? null,
|
|
192
|
+
dateTo: options.dateTo ?? null,
|
|
193
|
+
participant: options.participant ?? null,
|
|
194
|
+
model: options.model ?? null,
|
|
195
|
+
role: options.role ?? null,
|
|
196
|
+
tool: options.tool ?? null,
|
|
197
|
+
tag: options.tag ?? null,
|
|
198
|
+
entity: options.entity ?? null,
|
|
199
|
+
sensitivity: options.sensitivity ?? null,
|
|
200
|
+
extractionState: options.extractionState ?? null,
|
|
201
|
+
limit: options.limit,
|
|
202
|
+
})),
|
|
203
|
+
eventIds: documents.map((document) => document.eventId),
|
|
204
|
+
requiresNetwork: backend.requiresNetwork,
|
|
205
|
+
requiresModel: backend.requiresModel,
|
|
206
|
+
}));
|
|
207
|
+
return {
|
|
208
|
+
contractVersion: '1.0.0',
|
|
209
|
+
operationId,
|
|
210
|
+
backend: backend.id,
|
|
211
|
+
mode: 'hybrid',
|
|
212
|
+
workspaceId: options.workspaceId,
|
|
213
|
+
candidateCount: documents.length,
|
|
214
|
+
transfersApprovedText: true,
|
|
215
|
+
requiresNetwork: backend.requiresNetwork,
|
|
216
|
+
requiresModel: backend.requiresModel,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function repositoryPreview(input) {
|
|
220
|
+
return {
|
|
221
|
+
contractVersion: '1.0.0',
|
|
222
|
+
operationId: sha256(JSON.stringify(input)),
|
|
223
|
+
backend: 'fortemi',
|
|
224
|
+
operation: input.operation,
|
|
225
|
+
workspaceId: input.workspaceId,
|
|
226
|
+
sourceId: input.sourceId,
|
|
227
|
+
sessionId: input.sessionId,
|
|
228
|
+
eventCount: input.eventIdentities.length,
|
|
229
|
+
transfersApprovedText: input.transfersApprovedText,
|
|
230
|
+
requiresNetwork: true,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function assertRepositoryAuthorization(preview, authorization) {
|
|
234
|
+
if (!authorization?.approved || authorization.operationId !== preview.operationId) {
|
|
235
|
+
throw new SessionContractError('NETWORK_NOT_AUTHORIZED', 'optional repository operation requires approval of the exact preview operation');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=optional-backends.js.map
|