@aiwg/cli 2026.7.19 → 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 +397 -385
- 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/cli.js +59 -5
- package/dist/src/artifacts/discover-facets.js +15 -0
- package/dist/src/artifacts/discovery-eval.js +290 -0
- package/dist/src/artifacts/fortemi-core-query-adapter.js +1 -1
- package/dist/src/artifacts/fortemi-shard-export.js +1 -1
- package/dist/src/artifacts/index-builder.js +54 -17
- package/dist/src/artifacts/query-engine.js +10 -6
- 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/help.js +2 -1
- package/dist/src/cli/handlers/index.js +6 -2
- package/dist/src/cli/handlers/resource-versions.js +247 -0
- package/dist/src/cli/handlers/sessions.js +966 -0
- package/dist/src/cli/handlers/skill-lint.js +49 -45
- package/dist/src/cli/handlers/subcommands.js +55 -3
- package/dist/src/cli/handlers/use.js +154 -59
- package/dist/src/cli/handlers/utilities.js +49 -34
- package/dist/src/cli/skill-usage.js +146 -24
- package/dist/src/config/cli.js +13 -9
- package/dist/src/config/project-artifacts-runtime.mjs +68 -0
- package/dist/src/config/project-artifacts.js +1 -68
- package/dist/src/extensions/commands/definitions.js +65 -2
- package/dist/src/extensions/manifest.js +29 -0
- package/dist/src/extensions/project-local-discovery.js +86 -2
- package/dist/src/extensions/project-local-remove.js +52 -56
- package/dist/src/extensions/shadow-resolver.js +3 -1
- package/dist/src/plugins/standalone-packager.js +143 -0
- package/dist/src/resources/cache-cleanup.js +67 -0
- package/dist/src/resources/doctor.js +107 -0
- package/dist/src/resources/lockfile.js +125 -0
- package/dist/src/resources/resolver.js +133 -0
- package/dist/src/resources/web-release.d.ts +8 -0
- package/dist/src/resources/web-release.js +159 -1
- 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/dist/src/smiths/context-pipeline/aiwg-md.js +5 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +21 -1
- package/dist/src/smiths/context-pipeline/finalization.js +5 -3
- package/dist/src/smiths/context-pipeline/generator.js +4 -1
- package/dist/src/smiths/context-pipeline/parallelism-section.js +34 -1
- package/dist/src/smiths/context-pipeline/workspace-context.js +15 -3
- package/dist/src/smiths/mcpsmith/example.js +3 -1
- package/dist/src/smiths/mcpsmith/generator.js +3 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +2 -1
- package/dist/src/storage/cli.js +3 -2
- package/dist/src/storage/subsystem-cli.js +7 -2
- package/dist/src/update/notifier.mjs +1 -1
- package/dist/src/update/service.mjs +123 -0
- package/package.json +3 -2
|
@@ -0,0 +1,966 @@
|
|
|
1
|
+
import { mkdirSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { CLAUDE_ADAPTER_VERSION, ClaudeSessionAdapter, CODEX_ADAPTER_VERSION, CodexSessionAdapter, COPILOT_ADAPTER_VERSION, CopilotSessionAdapter, CURSOR_ADAPTER_VERSION, CursorSessionAdapter, FACTORY_ADAPTER_VERSION, FactorySessionAdapter, HERMES_ADAPTER_VERSION, HermesSessionAdapter, OPENCODE_ADAPTER_VERSION, OpenCodeSessionAdapter, OPENCLAW_ADAPTER_VERSION, OpenClawSessionAdapter, OPENHUMAN_ADAPTER_VERSION, OpenHumanSessionAdapter, WARP_ADAPTER_VERSION, WarpSessionAdapter, DEVIN_DESKTOP_ADAPTER_VERSION, DevinDesktopSessionAdapter, CandidateExtractionService, GENERIC_ADAPTER_VERSION, GenericSessionInterchangeAdapter, IncrementalSessionImporter, FilesystemMemoryDestination, FilesystemPromotionDispositionCoordinator, MemoryPromotionGateway, SESSION_CONTRACT_VERSION, SESSION_PROVIDER_IDS, SessionContractError, SessionRepository, SessionSourceSchema, StructuralCandidateExtractor, resolveMemoryConsumerManifest, assertSessionProviderId, redactSourceLocator, } from '../../sessions/index.js';
|
|
4
|
+
const JSON_CONTRACT_VERSION = '1.0.0';
|
|
5
|
+
const EXIT = {
|
|
6
|
+
ok: 0, usage: 2, unsupported: 3, unavailable: 4, contract: 5, storage: 6,
|
|
7
|
+
};
|
|
8
|
+
const HELP = `Usage: aiwg sessions <command> [options]
|
|
9
|
+
|
|
10
|
+
Commands:
|
|
11
|
+
sources Show all canonical provider dispositions
|
|
12
|
+
import <file> --source-id <id> Import a supported provider JSONL source
|
|
13
|
+
list [--limit N] [--cursor N] List normalized sessions
|
|
14
|
+
show <session-id> Show a session with events and tags
|
|
15
|
+
search <query> --workspace <id> Search authorized normalized content
|
|
16
|
+
extract [session-id] --workspace <id> Extract structural candidates
|
|
17
|
+
candidates [--state <state>] List the candidate review queue
|
|
18
|
+
review <id> <version> <state> Record an explicit review transition
|
|
19
|
+
promote <id> <version> Preview promotion; use --confirm to write
|
|
20
|
+
tag <session-id> <tag> Add a catalog tag
|
|
21
|
+
relocate <source-id> <file> Update AIWG source-location metadata
|
|
22
|
+
reindex Rebuild catalog indexes
|
|
23
|
+
delete <session-id> Preview deletion; use --confirm to tombstone
|
|
24
|
+
restore <session-id> Restore a reversible catalog tombstone
|
|
25
|
+
purge <session-id> Preview terminal AIWG-copy purge
|
|
26
|
+
audit --workspace <id> Read content-free mutation events
|
|
27
|
+
doctor Check catalog availability and integrity
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
--json Emit the versioned JSON contract
|
|
31
|
+
--dry-run Preview a mutation without changing state
|
|
32
|
+
--db <path> Override .aiwg/sessions/catalog.sqlite
|
|
33
|
+
--provider <id> Filter list or select an import adapter
|
|
34
|
+
--consumer <id> Select a named memory consumer for promotion
|
|
35
|
+
--workspace <id>, --tag <tag>, --limit <n>, --cursor <n>
|
|
36
|
+
--page-size <n> Extraction scan page size (default 250, maximum 500)
|
|
37
|
+
--max-documents <n> Explicit extraction safety limit; returns a partial receipt`;
|
|
38
|
+
export const sessionsHandler = {
|
|
39
|
+
id: 'sessions',
|
|
40
|
+
name: 'Sessions',
|
|
41
|
+
description: 'Manage the normalized session catalog (the singular `session` command remains the launcher)',
|
|
42
|
+
category: 'project',
|
|
43
|
+
aliases: [],
|
|
44
|
+
async execute(ctx) {
|
|
45
|
+
const json = ctx.args.includes('--json');
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = parseArgs(ctx.args);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const normalized = normalizeError(error);
|
|
52
|
+
const output = envelope('sessions', 'error', null, normalized.error);
|
|
53
|
+
if (json)
|
|
54
|
+
emit(output);
|
|
55
|
+
else
|
|
56
|
+
console.error(`${normalized.error.code}: ${normalized.error.message}`);
|
|
57
|
+
return { exitCode: normalized.exitCode, message: normalized.error.message };
|
|
58
|
+
}
|
|
59
|
+
if (!parsed.command || parsed.flags.has('--help') || parsed.flags.has('-h')) {
|
|
60
|
+
if (json)
|
|
61
|
+
emit(envelope('sessions.help', 'ok', { usage: HELP }, null));
|
|
62
|
+
else
|
|
63
|
+
console.log(HELP);
|
|
64
|
+
return { exitCode: parsed.command ? EXIT.ok : EXIT.usage };
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const result = await executeCommand(ctx, parsed);
|
|
68
|
+
if (json)
|
|
69
|
+
emit(result.envelope);
|
|
70
|
+
else
|
|
71
|
+
printHuman(result.envelope);
|
|
72
|
+
return { exitCode: result.exitCode };
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const normalized = normalizeError(error);
|
|
76
|
+
const output = envelope(`sessions.${parsed.command}`, 'error', null, normalized.error);
|
|
77
|
+
if (json)
|
|
78
|
+
emit(output);
|
|
79
|
+
else
|
|
80
|
+
console.error(`${normalized.error.code}: ${normalized.error.message}`);
|
|
81
|
+
return { exitCode: normalized.exitCode, message: normalized.error.message };
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
async function executeCommand(ctx, args) {
|
|
86
|
+
const command = args.command;
|
|
87
|
+
if (command === 'sources') {
|
|
88
|
+
const reports = [...SESSION_PROVIDER_IDS].sort().map(providerDisposition);
|
|
89
|
+
return ok(command, { providers: reports, count: reports.length });
|
|
90
|
+
}
|
|
91
|
+
if (command === 'import')
|
|
92
|
+
return importSource(ctx, args);
|
|
93
|
+
const repository = openRepository(ctx, args);
|
|
94
|
+
try {
|
|
95
|
+
switch (command) {
|
|
96
|
+
case 'list': {
|
|
97
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
98
|
+
const limit = boundedInteger(args.values.get('--limit'), 50, 1, 500, '--limit');
|
|
99
|
+
const cursor = args.values.get('--cursor');
|
|
100
|
+
const providerInput = args.values.get('--provider');
|
|
101
|
+
const provider = providerInput ? assertSessionProviderId(providerInput) : undefined;
|
|
102
|
+
const result = repository.listSessions({
|
|
103
|
+
provider,
|
|
104
|
+
workspaceId,
|
|
105
|
+
tag: args.values.get('--tag'),
|
|
106
|
+
limit,
|
|
107
|
+
cursor,
|
|
108
|
+
});
|
|
109
|
+
return ok(command, {
|
|
110
|
+
items: result.items,
|
|
111
|
+
page: {
|
|
112
|
+
limit,
|
|
113
|
+
cursor: cursor ?? null,
|
|
114
|
+
nextCursor: result.nextCursor,
|
|
115
|
+
snapshotRowid: result.snapshotRowid,
|
|
116
|
+
total: result.total,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
case 'show': {
|
|
121
|
+
const id = requiredPositional(args, 0, 'session-id');
|
|
122
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
123
|
+
const session = repository.getSession(id, workspaceId);
|
|
124
|
+
if (!session)
|
|
125
|
+
throw new CliError('SESSION_NOT_FOUND', `session not found: ${id}`, EXIT.unavailable);
|
|
126
|
+
return ok(command, {
|
|
127
|
+
session,
|
|
128
|
+
tags: repository.listTags(id, workspaceId),
|
|
129
|
+
events: repository.listEvents(id, workspaceId),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
case 'search': {
|
|
133
|
+
const query = requiredPositional(args, 0, 'query');
|
|
134
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
135
|
+
const providerInput = args.values.get('--provider');
|
|
136
|
+
const provider = providerInput ? assertSessionProviderId(providerInput) : undefined;
|
|
137
|
+
const result = repository.search({
|
|
138
|
+
query,
|
|
139
|
+
workspaceId,
|
|
140
|
+
providers: provider ? [provider] : undefined,
|
|
141
|
+
dateFrom: args.values.get('--date-from'),
|
|
142
|
+
dateTo: args.values.get('--date-to'),
|
|
143
|
+
participant: args.values.get('--participant'),
|
|
144
|
+
model: args.values.get('--model'),
|
|
145
|
+
role: args.values.get('--role'),
|
|
146
|
+
tool: args.values.get('--tool'),
|
|
147
|
+
tag: args.values.get('--tag'),
|
|
148
|
+
entity: args.values.get('--entity'),
|
|
149
|
+
sensitivity: args.values.get('--sensitivity'),
|
|
150
|
+
extractionState: args.values.get('--extraction-state'),
|
|
151
|
+
limit: boundedInteger(args.values.get('--limit'), 50, 1, 500, '--limit'),
|
|
152
|
+
cursor: args.values.get('--cursor'),
|
|
153
|
+
});
|
|
154
|
+
return ok(command, {
|
|
155
|
+
items: result.items,
|
|
156
|
+
page: { limit: boundedInteger(args.values.get('--limit'), 50, 1, 500, '--limit'),
|
|
157
|
+
nextCursor: result.nextCursor },
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
case 'extract': {
|
|
161
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
162
|
+
const sessionId = args.positionals[0];
|
|
163
|
+
const pageSize = boundedInteger(args.values.get('--page-size'), 250, 1, 500, '--page-size');
|
|
164
|
+
const maxDocuments = args.values.has('--max-documents')
|
|
165
|
+
? boundedInteger(args.values.get('--max-documents'), pageSize, 1, 1_000_000, '--max-documents')
|
|
166
|
+
: null;
|
|
167
|
+
const documents = [];
|
|
168
|
+
let cursor;
|
|
169
|
+
let truncated = false;
|
|
170
|
+
do {
|
|
171
|
+
const remaining = maxDocuments === null
|
|
172
|
+
? pageSize
|
|
173
|
+
: Math.min(pageSize, maxDocuments - documents.length);
|
|
174
|
+
if (remaining <= 0) {
|
|
175
|
+
truncated = true;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
const page = repository.authorizedSearchDocumentPage({
|
|
179
|
+
workspaceId,
|
|
180
|
+
sessionIds: sessionId ? [sessionId] : undefined,
|
|
181
|
+
limit: remaining,
|
|
182
|
+
cursor,
|
|
183
|
+
});
|
|
184
|
+
documents.push(...page.items);
|
|
185
|
+
cursor = page.nextCursor ?? undefined;
|
|
186
|
+
if (maxDocuments !== null && documents.length >= maxDocuments && cursor) {
|
|
187
|
+
truncated = true;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
} while (cursor);
|
|
191
|
+
if (sessionId && documents.length === 0) {
|
|
192
|
+
throw new CliError('SESSION_NOT_FOUND', `no authorized evidence found for session: ${sessionId}`, EXIT.unavailable);
|
|
193
|
+
}
|
|
194
|
+
const dryRun = isDryRun(ctx, args);
|
|
195
|
+
const service = new CandidateExtractionService(dryRun
|
|
196
|
+
? { saveCandidates: (candidates) => [...candidates] }
|
|
197
|
+
: repository);
|
|
198
|
+
const items = await service.extract({
|
|
199
|
+
documents,
|
|
200
|
+
extractor: new StructuralCandidateExtractor(),
|
|
201
|
+
policy: {
|
|
202
|
+
version: args.values.get('--policy-version') ?? '1.0.0',
|
|
203
|
+
projectScope: workspaceId,
|
|
204
|
+
temporalScope: 'source-event',
|
|
205
|
+
minimumConfidence: boundedNumber(args.values.get('--min-confidence'), 0.5, 0, 1, '--min-confidence'),
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
const scan = {
|
|
209
|
+
complete: !truncated,
|
|
210
|
+
documentsScanned: documents.length,
|
|
211
|
+
pageSize,
|
|
212
|
+
nextCursor: truncated ? cursor ?? null : null,
|
|
213
|
+
limit: maxDocuments,
|
|
214
|
+
};
|
|
215
|
+
return dryRun
|
|
216
|
+
? preview(command, { items, count: items.length, durableMemoryWrites: 0, scan })
|
|
217
|
+
: ok(command, { items, count: items.length, durableMemoryWrites: 0, scan });
|
|
218
|
+
}
|
|
219
|
+
case 'candidates': {
|
|
220
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
221
|
+
const state = candidateState(args.values.get('--state'));
|
|
222
|
+
return ok(command, {
|
|
223
|
+
items: repository.listCandidates(state, workspaceId),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
case 'review': {
|
|
227
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
228
|
+
const candidateId = requiredPositional(args, 0, 'candidate-id');
|
|
229
|
+
const version = boundedInteger(requiredPositional(args, 1, 'version'), 1, 1, Number.MAX_SAFE_INTEGER, 'version');
|
|
230
|
+
const toState = candidateState(requiredPositional(args, 2, 'state'));
|
|
231
|
+
if (!toState)
|
|
232
|
+
throw new CliError('INVALID_ARGUMENT', 'review state is required', EXIT.usage);
|
|
233
|
+
if (isDryRun(ctx, args)) {
|
|
234
|
+
return preview(command, {
|
|
235
|
+
candidateId, version, toState,
|
|
236
|
+
reviewer: requiredValue(args, '--reviewer'),
|
|
237
|
+
reason: requiredValue(args, '--reason'),
|
|
238
|
+
securityAcknowledged: args.flags.has('--acknowledge-security-risk'),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
return ok(command, repository.reviewCandidate({
|
|
242
|
+
candidateId,
|
|
243
|
+
version,
|
|
244
|
+
toState,
|
|
245
|
+
reviewer: requiredValue(args, '--reviewer'),
|
|
246
|
+
reason: requiredValue(args, '--reason'),
|
|
247
|
+
securityAcknowledged: args.flags.has('--acknowledge-security-risk'),
|
|
248
|
+
workspaceId,
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
case 'promote': {
|
|
252
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
253
|
+
const candidateId = requiredPositional(args, 0, 'candidate-id');
|
|
254
|
+
const version = boundedInteger(requiredPositional(args, 1, 'version'), 1, 1, Number.MAX_SAFE_INTEGER, 'version');
|
|
255
|
+
const consumer = requiredValue(args, '--consumer');
|
|
256
|
+
const destination = new FilesystemMemoryDestination({
|
|
257
|
+
projectRoot: ctx.cwd,
|
|
258
|
+
consumer,
|
|
259
|
+
manifestPath: resolveMemoryConsumerManifest(ctx.cwd, consumer),
|
|
260
|
+
});
|
|
261
|
+
const scopedPromotionStore = {
|
|
262
|
+
getCandidate: (id, candidateVersion) => repository.getCandidate(id, candidateVersion, workspaceId),
|
|
263
|
+
getPromotionReceipt: (id, candidateVersion, namedConsumer) => repository.getCandidate(id, candidateVersion, workspaceId)
|
|
264
|
+
? repository.getPromotionReceipt(id, candidateVersion, namedConsumer)
|
|
265
|
+
: null,
|
|
266
|
+
recordPromotion: (receipt) => {
|
|
267
|
+
if (!repository.getCandidate(receipt.candidateId, receipt.candidateVersion, workspaceId)) {
|
|
268
|
+
throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'candidate version is not available in the authorized workspace');
|
|
269
|
+
}
|
|
270
|
+
return repository.recordPromotion(receipt);
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
const gateway = new MemoryPromotionGateway(scopedPromotionStore);
|
|
274
|
+
const promotionPreview = gateway.preview({ candidateId, version, destination });
|
|
275
|
+
if (!args.flags.has('--confirm') || isDryRun(ctx, args)) {
|
|
276
|
+
return preview(command, promotionPreview);
|
|
277
|
+
}
|
|
278
|
+
const receipt = await gateway.promote({
|
|
279
|
+
candidateId,
|
|
280
|
+
version,
|
|
281
|
+
destination,
|
|
282
|
+
reviewer: requiredValue(args, '--reviewer'),
|
|
283
|
+
operationId: promotionPreview.operationId,
|
|
284
|
+
});
|
|
285
|
+
return ok(command, { preview: promotionPreview, receipt });
|
|
286
|
+
}
|
|
287
|
+
case 'tag': {
|
|
288
|
+
const id = requiredPositional(args, 0, 'session-id');
|
|
289
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
290
|
+
const tag = requiredPositional(args, 1, 'tag');
|
|
291
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/.test(tag)) {
|
|
292
|
+
throw new CliError('INVALID_TAG', 'tag must be 1-64 safe identifier characters', EXIT.usage);
|
|
293
|
+
}
|
|
294
|
+
if (isDryRun(ctx, args))
|
|
295
|
+
return preview(command, { sessionId: id, tag, wouldAdd: true });
|
|
296
|
+
if (!repository.tagSession(id, tag, workspaceId)) {
|
|
297
|
+
if (!repository.getSession(id, workspaceId)) {
|
|
298
|
+
throw new CliError('SESSION_NOT_FOUND', `session not found: ${id}`, EXIT.unavailable);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return ok(command, { sessionId: id, tag, tags: repository.listTags(id, workspaceId) });
|
|
302
|
+
}
|
|
303
|
+
case 'relocate': {
|
|
304
|
+
const sourceId = requiredPositional(args, 0, 'source-id');
|
|
305
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
306
|
+
const locator = requiredPositional(args, 1, 'file');
|
|
307
|
+
if (!repository.getSource(sourceId, workspaceId)) {
|
|
308
|
+
throw new CliError('SOURCE_NOT_FOUND', `source not found: ${sourceId}`, EXIT.unavailable);
|
|
309
|
+
}
|
|
310
|
+
const redactedLocator = redactSourceLocator(locator);
|
|
311
|
+
if (isDryRun(ctx, args))
|
|
312
|
+
return preview(command, { sourceId, redactedLocator });
|
|
313
|
+
repository.relocateSource(sourceId, redactedLocator, workspaceId);
|
|
314
|
+
return ok(command, { sourceId, redactedLocator });
|
|
315
|
+
}
|
|
316
|
+
case 'reindex': {
|
|
317
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
318
|
+
if (isDryRun(ctx, args))
|
|
319
|
+
return preview(command, { operation: 'reindex' });
|
|
320
|
+
repository.reindex(workspaceId);
|
|
321
|
+
return ok(command, { operation: 'reindex' });
|
|
322
|
+
}
|
|
323
|
+
case 'audit': {
|
|
324
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
325
|
+
const limit = boundedInteger(args.values.get('--limit'), 100, 1, 500, '--limit');
|
|
326
|
+
const cursor = args.values.get('--cursor');
|
|
327
|
+
if (args.flags.has('--otel')) {
|
|
328
|
+
return ok(command, repository.exportMutationEventsOtel({
|
|
329
|
+
workspaceId,
|
|
330
|
+
limit,
|
|
331
|
+
cursor,
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
334
|
+
const page = repository.listMutationEvents({ workspaceId, limit, cursor });
|
|
335
|
+
return ok(command, {
|
|
336
|
+
items: page.items,
|
|
337
|
+
page: { limit, nextCursor: page.nextCursor },
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
case 'delete': {
|
|
341
|
+
const id = requiredPositional(args, 0, 'session-id');
|
|
342
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
343
|
+
const counts = repository.deletionPreview(id, workspaceId);
|
|
344
|
+
if (counts.sessions === 0) {
|
|
345
|
+
throw new CliError('SESSION_NOT_FOUND', `session not found: ${id}`, EXIT.unavailable);
|
|
346
|
+
}
|
|
347
|
+
if (!args.flags.has('--confirm') || isDryRun(ctx, args)) {
|
|
348
|
+
return preview(command, {
|
|
349
|
+
sessionId: id, counts, providerLogsModified: false,
|
|
350
|
+
confirmationRequired: true,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
repository.tombstoneSession(id, workspaceId);
|
|
354
|
+
return ok(command, {
|
|
355
|
+
sessionId: id, counts, providerLogsModified: false, outcome: 'tombstoned',
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
case 'restore': {
|
|
359
|
+
const id = requiredPositional(args, 0, 'session-id');
|
|
360
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
361
|
+
if (isDryRun(ctx, args))
|
|
362
|
+
return preview(command, { sessionId: id, wouldRestore: true });
|
|
363
|
+
if (!repository.restoreSession(id, workspaceId)) {
|
|
364
|
+
throw new CliError('SESSION_NOT_FOUND', `tombstoned session not found: ${id}`, EXIT.unavailable);
|
|
365
|
+
}
|
|
366
|
+
return ok(command, { sessionId: id, outcome: 'restored', providerLogsModified: false });
|
|
367
|
+
}
|
|
368
|
+
case 'purge': {
|
|
369
|
+
const id = requiredPositional(args, 0, 'session-id');
|
|
370
|
+
const { workspaceId } = authorizationContext(args, command);
|
|
371
|
+
const completed = repository.getCompletedPurge(id, workspaceId);
|
|
372
|
+
if (completed) {
|
|
373
|
+
return ok(command, {
|
|
374
|
+
receipt: completed,
|
|
375
|
+
dependentDecisions: repository.listPromotionDependencyDecisions(completed.operationId),
|
|
376
|
+
duplicate: true,
|
|
377
|
+
providerLogsModified: false,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const purgePreview = repository.previewPurge(id, workspaceId);
|
|
381
|
+
const requestedAction = args.values.get('--dependent-action');
|
|
382
|
+
const action = purgePreview.promotedDependents.length > 0 && requestedAction
|
|
383
|
+
? dependentAction(requestedAction)
|
|
384
|
+
: 'origin_unavailable';
|
|
385
|
+
const basis = purgePreview.promotedDependents.length > 0
|
|
386
|
+
? (args.values.get('--basis') ?? 'not-yet-confirmed')
|
|
387
|
+
: (args.values.get('--basis') ?? 'no-promoted-dependents');
|
|
388
|
+
const decisions = purgePreview.promotedDependents.map((item) => ({
|
|
389
|
+
dependentId: item.dependentId,
|
|
390
|
+
action,
|
|
391
|
+
basis,
|
|
392
|
+
}));
|
|
393
|
+
const dispositionCoordinator = new FilesystemPromotionDispositionCoordinator({
|
|
394
|
+
projectRoot: ctx.cwd,
|
|
395
|
+
allowedRoots: ['.aiwg'],
|
|
396
|
+
});
|
|
397
|
+
if (!args.flags.has('--confirm') || isDryRun(ctx, args)) {
|
|
398
|
+
return preview(command, {
|
|
399
|
+
...purgePreview,
|
|
400
|
+
artifactEffects: requestedAction
|
|
401
|
+
? dispositionCoordinator.preview(purgePreview, decisions)
|
|
402
|
+
: purgePreview.promotedDependents.map((item) => ({
|
|
403
|
+
dependentId: item.dependentId,
|
|
404
|
+
destinationRef: item.destinationRef,
|
|
405
|
+
allowedActions: [
|
|
406
|
+
'origin_unavailable', 'retain', 'revoke',
|
|
407
|
+
'supersede', 'delete', 'abort',
|
|
408
|
+
],
|
|
409
|
+
confirmationRequired: true,
|
|
410
|
+
})),
|
|
411
|
+
providerLogsModified: false,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
if (purgePreview.promotedDependents.length > 0) {
|
|
415
|
+
dependentAction(requiredValue(args, '--dependent-action'));
|
|
416
|
+
requiredValue(args, '--basis');
|
|
417
|
+
}
|
|
418
|
+
const disposition = dispositionCoordinator.apply(purgePreview, decisions);
|
|
419
|
+
const receipt = repository.purgeSession({
|
|
420
|
+
preview: purgePreview,
|
|
421
|
+
actorClass: requiredValue(args, '--actor-class'),
|
|
422
|
+
reasonCode: requiredValue(args, '--reason-code'),
|
|
423
|
+
decisions,
|
|
424
|
+
});
|
|
425
|
+
dispositionCoordinator.catalogCommitted(receipt.operationId);
|
|
426
|
+
return ok(command, {
|
|
427
|
+
receipt,
|
|
428
|
+
artifactDisposition: disposition,
|
|
429
|
+
dependentDecisions: repository.listPromotionDependencyDecisions(receipt.operationId),
|
|
430
|
+
providerLogsModified: false,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
case 'doctor':
|
|
434
|
+
return ok(command, {
|
|
435
|
+
database: redactSourceLocator(databasePath(ctx, args)),
|
|
436
|
+
health: repository.doctor(),
|
|
437
|
+
jsonContractVersion: JSON_CONTRACT_VERSION,
|
|
438
|
+
});
|
|
439
|
+
default:
|
|
440
|
+
throw new CliError('UNKNOWN_COMMAND', `unknown sessions command: ${command}`, EXIT.usage);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
finally {
|
|
444
|
+
repository.close();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async function importSource(ctx, args) {
|
|
448
|
+
const input = resolve(ctx.cwd, requiredPositional(args, 0, 'file'));
|
|
449
|
+
const providerInput = args.values.get('--provider') ?? 'generic';
|
|
450
|
+
const provider = assertSessionProviderId(providerInput);
|
|
451
|
+
if (provider !== 'generic' && provider !== 'claude' && provider !== 'codex'
|
|
452
|
+
&& provider !== 'copilot' && provider !== 'cursor' && provider !== 'factory'
|
|
453
|
+
&& provider !== 'hermes' && provider !== 'opencode' && provider !== 'openclaw'
|
|
454
|
+
&& provider !== 'openhuman' && provider !== 'warp' && provider !== 'devin-desktop') {
|
|
455
|
+
throw new CliError('UNSUPPORTED_OPERATION', `session import is not implemented for ${provider}`, EXIT.unsupported);
|
|
456
|
+
}
|
|
457
|
+
const sourceId = requiredValue(args, '--source-id');
|
|
458
|
+
const workspaceId = args.values.get('--workspace') ?? 'default';
|
|
459
|
+
const isClaude = provider === 'claude';
|
|
460
|
+
const isCodex = provider === 'codex';
|
|
461
|
+
const isCopilot = provider === 'copilot';
|
|
462
|
+
const isCursor = provider === 'cursor';
|
|
463
|
+
const isFactory = provider === 'factory';
|
|
464
|
+
const isHermes = provider === 'hermes';
|
|
465
|
+
const isOpenCode = provider === 'opencode';
|
|
466
|
+
const isOpenClaw = provider === 'openclaw';
|
|
467
|
+
const isOpenHuman = provider === 'openhuman';
|
|
468
|
+
const isWarp = provider === 'warp';
|
|
469
|
+
const isDevinDesktop = provider === 'devin-desktop';
|
|
470
|
+
const adapter = isClaude
|
|
471
|
+
? new ClaudeSessionAdapter()
|
|
472
|
+
: isCodex
|
|
473
|
+
? new CodexSessionAdapter()
|
|
474
|
+
: isCopilot
|
|
475
|
+
? new CopilotSessionAdapter()
|
|
476
|
+
: isCursor
|
|
477
|
+
? new CursorSessionAdapter()
|
|
478
|
+
: isFactory
|
|
479
|
+
? new FactorySessionAdapter()
|
|
480
|
+
: isHermes
|
|
481
|
+
? new HermesSessionAdapter()
|
|
482
|
+
: isOpenCode
|
|
483
|
+
? new OpenCodeSessionAdapter()
|
|
484
|
+
: isOpenClaw
|
|
485
|
+
? new OpenClawSessionAdapter()
|
|
486
|
+
: isOpenHuman
|
|
487
|
+
? new OpenHumanSessionAdapter()
|
|
488
|
+
: isWarp
|
|
489
|
+
? new WarpSessionAdapter()
|
|
490
|
+
: isDevinDesktop
|
|
491
|
+
? new DevinDesktopSessionAdapter()
|
|
492
|
+
: new GenericSessionInterchangeAdapter();
|
|
493
|
+
const locatorClass = isClaude
|
|
494
|
+
? (input.endsWith('.hooks.jsonl') ? 'claude-hook-jsonl' : 'claude-transcript-jsonl')
|
|
495
|
+
: isCodex
|
|
496
|
+
? (input.endsWith('.app-server.jsonl') ? 'codex-app-server-jsonl' : 'codex-rollout-jsonl')
|
|
497
|
+
: isCopilot
|
|
498
|
+
? 'copilot-chat-json-export'
|
|
499
|
+
: isCursor
|
|
500
|
+
? cursorLocatorClass(input)
|
|
501
|
+
: isFactory
|
|
502
|
+
? 'factory-droid-jsonl'
|
|
503
|
+
: isHermes
|
|
504
|
+
? 'hermes-export-jsonl'
|
|
505
|
+
: isOpenCode
|
|
506
|
+
? 'opencode-export-json'
|
|
507
|
+
: isOpenClaw
|
|
508
|
+
? 'openclaw-consistent-snapshot-jsonl'
|
|
509
|
+
: isOpenHuman
|
|
510
|
+
? 'openhuman-enriched-jsonl'
|
|
511
|
+
: isWarp
|
|
512
|
+
? 'warp-markdown-export'
|
|
513
|
+
: isDevinDesktop
|
|
514
|
+
? 'devin-desktop-cascade-hook-jsonl'
|
|
515
|
+
: 'manual-export';
|
|
516
|
+
const selectedSource = {
|
|
517
|
+
provider, locator: input, locatorClass, sourceId,
|
|
518
|
+
authorizedScope: { workspaceId, allowedRoots: [dirname(input)] },
|
|
519
|
+
};
|
|
520
|
+
const probe = await adapter.inspect(selectedSource);
|
|
521
|
+
const source = SessionSourceSchema.parse({
|
|
522
|
+
contractVersion: SESSION_CONTRACT_VERSION, sourceId, provider,
|
|
523
|
+
providerProfile: isClaude
|
|
524
|
+
? 'documented-local-jsonl'
|
|
525
|
+
: isCodex
|
|
526
|
+
? 'app-server-v2-rollout-fallback'
|
|
527
|
+
: isCopilot
|
|
528
|
+
? 'vscode-chat-json-export'
|
|
529
|
+
: isCursor
|
|
530
|
+
? cursorProviderProfile(locatorClass)
|
|
531
|
+
: isFactory
|
|
532
|
+
? 'documented-project-jsonl'
|
|
533
|
+
: isHermes
|
|
534
|
+
? 'native-schema-23-export'
|
|
535
|
+
: isOpenCode
|
|
536
|
+
? 'sanitized-json-export'
|
|
537
|
+
: isOpenClaw
|
|
538
|
+
? 'schema-16-event-v3-consistent-snapshot'
|
|
539
|
+
: isOpenHuman
|
|
540
|
+
? 'schema-1-session-raw-enriched'
|
|
541
|
+
: isWarp
|
|
542
|
+
? 'manual-lossy-markdown-export'
|
|
543
|
+
: isDevinDesktop
|
|
544
|
+
? 'opt-in-cascade-transcript-hook'
|
|
545
|
+
: 'manual-interchange',
|
|
546
|
+
locatorClass, redactedLocator: redactSourceLocator(input),
|
|
547
|
+
adapterVersion: isClaude
|
|
548
|
+
? CLAUDE_ADAPTER_VERSION
|
|
549
|
+
: isCodex
|
|
550
|
+
? CODEX_ADAPTER_VERSION
|
|
551
|
+
: isCopilot
|
|
552
|
+
? COPILOT_ADAPTER_VERSION
|
|
553
|
+
: isCursor
|
|
554
|
+
? CURSOR_ADAPTER_VERSION
|
|
555
|
+
: isFactory
|
|
556
|
+
? FACTORY_ADAPTER_VERSION
|
|
557
|
+
: isHermes
|
|
558
|
+
? HERMES_ADAPTER_VERSION
|
|
559
|
+
: isOpenCode
|
|
560
|
+
? OPENCODE_ADAPTER_VERSION
|
|
561
|
+
: isOpenClaw
|
|
562
|
+
? OPENCLAW_ADAPTER_VERSION
|
|
563
|
+
: isOpenHuman
|
|
564
|
+
? OPENHUMAN_ADAPTER_VERSION
|
|
565
|
+
: isWarp
|
|
566
|
+
? WARP_ADAPTER_VERSION
|
|
567
|
+
: isDevinDesktop
|
|
568
|
+
? DEVIN_DESKTOP_ADAPTER_VERSION
|
|
569
|
+
: GENERIC_ADAPTER_VERSION,
|
|
570
|
+
sourceSchemaVersion: probe.sourceSchemaVersion,
|
|
571
|
+
disposition: isWarp
|
|
572
|
+
? 'manual-only'
|
|
573
|
+
: isClaude || isCodex || isCopilot || isCursor || isFactory || isHermes
|
|
574
|
+
|| isOpenCode || isOpenClaw || isOpenHuman || isDevinDesktop
|
|
575
|
+
? 'implemented' : 'manual-only',
|
|
576
|
+
operationalState: probe.operationalState,
|
|
577
|
+
consistency: probe.consistency, authorizedAt: new Date().toISOString(),
|
|
578
|
+
extensions: isClaude
|
|
579
|
+
? { 'native.claude': {} }
|
|
580
|
+
: isCodex
|
|
581
|
+
? { 'native.codex': {} }
|
|
582
|
+
: isCopilot
|
|
583
|
+
? { 'native.copilot': {} }
|
|
584
|
+
: isCursor
|
|
585
|
+
? { 'native.cursor': {} }
|
|
586
|
+
: isFactory
|
|
587
|
+
? { 'native.factory': {} }
|
|
588
|
+
: isHermes
|
|
589
|
+
? { 'native.hermes': {} }
|
|
590
|
+
: isOpenCode
|
|
591
|
+
? { 'native.opencode': {} }
|
|
592
|
+
: isOpenClaw
|
|
593
|
+
? { 'native.openclaw': {} }
|
|
594
|
+
: isOpenHuman
|
|
595
|
+
? { 'native.openhuman': {} }
|
|
596
|
+
: isWarp
|
|
597
|
+
? { 'native.warp': {} }
|
|
598
|
+
: isDevinDesktop
|
|
599
|
+
? { 'native.devin-desktop': {
|
|
600
|
+
product: 'Devin Desktop',
|
|
601
|
+
compatibilityProviderId: 'windsurf',
|
|
602
|
+
} }
|
|
603
|
+
: { 'native.generic': {} },
|
|
604
|
+
});
|
|
605
|
+
if (isDryRun(ctx, args)) {
|
|
606
|
+
return preview('import', { source, wouldInspect: true, wouldPersist: false });
|
|
607
|
+
}
|
|
608
|
+
const repository = openRepository(ctx, args);
|
|
609
|
+
try {
|
|
610
|
+
const receipts = await new IncrementalSessionImporter(repository).import({
|
|
611
|
+
source, selectedSource, adapter, workspaceId, policyVersion: '1.0.0',
|
|
612
|
+
});
|
|
613
|
+
return ok('import', {
|
|
614
|
+
sourceId,
|
|
615
|
+
receipts,
|
|
616
|
+
totals: {
|
|
617
|
+
sessionsInserted: receipts.reduce((sum, item) => sum + item.sessionsInserted, 0),
|
|
618
|
+
eventsInserted: receipts.reduce((sum, item) => sum + item.eventsInserted, 0),
|
|
619
|
+
},
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
finally {
|
|
623
|
+
repository.close();
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function providerDisposition(provider) {
|
|
627
|
+
if (provider === 'codex') {
|
|
628
|
+
return {
|
|
629
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
630
|
+
supportedOperations: ['discover', 'inspect', 'stream'],
|
|
631
|
+
acquisitionModes: ['api', 'jsonl'],
|
|
632
|
+
reasonCode: null,
|
|
633
|
+
remediation: 'Authorize an App Server export or Codex sessions root, then import an explicit JSONL source.',
|
|
634
|
+
evidence: {
|
|
635
|
+
adapterVersion: CODEX_ADAPTER_VERSION,
|
|
636
|
+
verifiedAt: '2026-07-27',
|
|
637
|
+
documentation: 'https://github.com/openai/codex/blob/main/codex-rs/app-server/README.md',
|
|
638
|
+
},
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
if (provider === 'claude') {
|
|
642
|
+
return {
|
|
643
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
644
|
+
supportedOperations: ['discover', 'inspect', 'stream'],
|
|
645
|
+
acquisitionModes: ['jsonl', 'hook'],
|
|
646
|
+
reasonCode: null,
|
|
647
|
+
remediation: 'Authorize a Claude projects or hook root, then import an explicit JSONL file.',
|
|
648
|
+
evidence: {
|
|
649
|
+
adapterVersion: CLAUDE_ADAPTER_VERSION,
|
|
650
|
+
verifiedAt: '2026-07-27',
|
|
651
|
+
documentation: 'https://code.claude.com/docs/en/sessions',
|
|
652
|
+
},
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
if (provider === 'copilot') {
|
|
656
|
+
return {
|
|
657
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
658
|
+
supportedOperations: ['inspect', 'stream'],
|
|
659
|
+
acquisitionModes: ['manual-export'],
|
|
660
|
+
reasonCode: null,
|
|
661
|
+
remediation: 'Use Chat: Export Chat in VS Code, then import the explicitly selected JSON file.',
|
|
662
|
+
evidence: {
|
|
663
|
+
adapterVersion: COPILOT_ADAPTER_VERSION,
|
|
664
|
+
verifiedAt: '2026-07-27',
|
|
665
|
+
documentation: 'https://code.visualstudio.com/docs/chat/chat-sessions#_export-a-chat-session-as-a-json-file',
|
|
666
|
+
},
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
if (provider === 'cursor') {
|
|
670
|
+
return {
|
|
671
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
672
|
+
supportedOperations: ['inspect', 'stream'],
|
|
673
|
+
acquisitionModes: ['api', 'jsonl', 'manual-export'],
|
|
674
|
+
reasonCode: null,
|
|
675
|
+
remediation: 'Import Cursor CLI stream-json, captured Cloud Agent v1 events, or an editor Markdown export.',
|
|
676
|
+
evidence: {
|
|
677
|
+
adapterVersion: CURSOR_ADAPTER_VERSION,
|
|
678
|
+
verifiedAt: '2026-07-27',
|
|
679
|
+
documentation: 'https://docs.cursor.com/en/cli/reference/output-format',
|
|
680
|
+
},
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
if (provider === 'factory') {
|
|
684
|
+
return {
|
|
685
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
686
|
+
supportedOperations: ['discover', 'inspect', 'stream'],
|
|
687
|
+
acquisitionModes: ['jsonl', 'api'],
|
|
688
|
+
reasonCode: null,
|
|
689
|
+
remediation: 'Authorize a Factory projects root and import a Droid JSONL transcript; API and Exec require explicit negotiated transports.',
|
|
690
|
+
evidence: {
|
|
691
|
+
adapterVersion: FACTORY_ADAPTER_VERSION,
|
|
692
|
+
verifiedAt: '2026-07-27',
|
|
693
|
+
documentation: 'https://docs.factory.ai/reference/hooks-reference',
|
|
694
|
+
},
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
if (provider === 'hermes') {
|
|
698
|
+
return {
|
|
699
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
700
|
+
supportedOperations: ['inspect', 'stream'],
|
|
701
|
+
acquisitionModes: ['jsonl', 'api', 'sqlite-snapshot'],
|
|
702
|
+
reasonCode: null,
|
|
703
|
+
remediation: 'Use `hermes sessions export` or an explicitly verified sqlite3.backup() snapshot export.',
|
|
704
|
+
evidence: {
|
|
705
|
+
adapterVersion: HERMES_ADAPTER_VERSION,
|
|
706
|
+
verifiedAt: '2026-07-27',
|
|
707
|
+
documentation: 'https://hermes-agent.nousresearch.com/docs/user-guide/sessions/',
|
|
708
|
+
},
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
if (provider === 'opencode') {
|
|
712
|
+
return {
|
|
713
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
714
|
+
supportedOperations: ['inspect', 'stream'],
|
|
715
|
+
acquisitionModes: ['manual-export', 'api', 'jsonl'],
|
|
716
|
+
reasonCode: null,
|
|
717
|
+
remediation: 'Use `opencode export <sessionID>` or an explicitly authorized negotiated local API/SSE transport.',
|
|
718
|
+
evidence: {
|
|
719
|
+
adapterVersion: OPENCODE_ADAPTER_VERSION,
|
|
720
|
+
verifiedAt: '2026-07-27',
|
|
721
|
+
documentation: 'https://opencode.ai/docs/cli/',
|
|
722
|
+
},
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
if (provider === 'openclaw') {
|
|
726
|
+
return {
|
|
727
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
728
|
+
supportedOperations: ['inspect', 'stream'],
|
|
729
|
+
acquisitionModes: ['api', 'sqlite-snapshot', 'jsonl'],
|
|
730
|
+
reasonCode: null,
|
|
731
|
+
remediation: 'Use an authorized read-only Gateway transport or a schema-16/event-v3 sqlite3.backup() projection.',
|
|
732
|
+
evidence: {
|
|
733
|
+
adapterVersion: OPENCLAW_ADAPTER_VERSION,
|
|
734
|
+
verifiedAt: '2026-07-27',
|
|
735
|
+
documentation: 'https://docs.openclaw.ai/reference/session-management-compaction',
|
|
736
|
+
},
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
if (provider === 'openhuman') {
|
|
740
|
+
return {
|
|
741
|
+
provider, disposition: 'implemented', operationalState: 'available',
|
|
742
|
+
supportedOperations: ['inspect', 'stream'],
|
|
743
|
+
acquisitionModes: ['jsonl'],
|
|
744
|
+
reasonCode: null,
|
|
745
|
+
remediation: 'Import an explicitly selected schema-1 session_raw JSONL file, optionally bundled with thread/turn enrichment.',
|
|
746
|
+
evidence: {
|
|
747
|
+
adapterVersion: OPENHUMAN_ADAPTER_VERSION,
|
|
748
|
+
verifiedAt: '2026-07-27',
|
|
749
|
+
documentation: 'https://github.com/tinyhumansai/openhuman/releases',
|
|
750
|
+
},
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
if (provider === 'warp') {
|
|
754
|
+
return {
|
|
755
|
+
provider, disposition: 'manual-only', operationalState: 'available',
|
|
756
|
+
supportedOperations: ['inspect', 'stream'],
|
|
757
|
+
acquisitionModes: ['manual-export'],
|
|
758
|
+
reasonCode: 'LOSSY_MARKDOWN_ONLY',
|
|
759
|
+
remediation: 'Run `/export-to-file` in Warp and import the explicitly selected Markdown file.',
|
|
760
|
+
evidence: {
|
|
761
|
+
adapterVersion: WARP_ADAPTER_VERSION,
|
|
762
|
+
verifiedAt: '2026-07-27',
|
|
763
|
+
documentation: 'https://docs.warp.dev/agent-platform/capabilities/slash-commands',
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
if (provider === 'devin-desktop') {
|
|
768
|
+
return {
|
|
769
|
+
provider,
|
|
770
|
+
product: 'Devin Desktop',
|
|
771
|
+
compatibilityAliases: ['windsurf'],
|
|
772
|
+
aliasDeprecatedAfter: '2027-07-27',
|
|
773
|
+
disposition: 'implemented', operationalState: 'available',
|
|
774
|
+
supportedOperations: ['inspect', 'stream'],
|
|
775
|
+
acquisitionModes: ['hook', 'jsonl'],
|
|
776
|
+
reasonCode: null,
|
|
777
|
+
remediation: 'Enable Devin Desktop post_cascade_response_with_transcript, then explicitly select its JSONL output.',
|
|
778
|
+
evidence: {
|
|
779
|
+
adapterVersion: DEVIN_DESKTOP_ADAPTER_VERSION,
|
|
780
|
+
verifiedAt: '2026-07-27',
|
|
781
|
+
documentation: 'https://docs.devin.ai/desktop/cascade/hooks',
|
|
782
|
+
},
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
if (provider === 'generic') {
|
|
786
|
+
return {
|
|
787
|
+
provider, disposition: 'manual-only', operationalState: 'available',
|
|
788
|
+
supportedOperations: ['inspect', 'stream'], acquisitionModes: ['manual-export'],
|
|
789
|
+
reasonCode: 'MANUAL_SOURCE_SELECTION_REQUIRED',
|
|
790
|
+
remediation: 'Pass a declared interchange file to `aiwg sessions import`.',
|
|
791
|
+
evidence: { adapterVersion: GENERIC_ADAPTER_VERSION, verifiedAt: '2026-07-26' },
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
return {
|
|
795
|
+
provider, disposition: 'unsupported', operationalState: 'unavailable',
|
|
796
|
+
supportedOperations: [], acquisitionModes: [],
|
|
797
|
+
reasonCode: 'ADAPTER_NOT_IMPLEMENTED',
|
|
798
|
+
remediation: 'Use the generic interchange until the provider adapter milestone is delivered.',
|
|
799
|
+
evidence: { adapterVersion: null, verifiedAt: '2026-07-26' },
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function cursorLocatorClass(input) {
|
|
803
|
+
if (input.endsWith('.md') || input.endsWith('.markdown'))
|
|
804
|
+
return 'cursor-editor-markdown';
|
|
805
|
+
if (input.endsWith('.cloud.jsonl'))
|
|
806
|
+
return 'cursor-cloud-events-jsonl';
|
|
807
|
+
return 'cursor-cli-stream-json';
|
|
808
|
+
}
|
|
809
|
+
function cursorProviderProfile(locatorClass) {
|
|
810
|
+
if (locatorClass === 'cursor-editor-markdown')
|
|
811
|
+
return 'editor-markdown-lossy';
|
|
812
|
+
if (locatorClass === 'cursor-cloud-events-jsonl')
|
|
813
|
+
return 'cloud-agents-api-v1';
|
|
814
|
+
return 'cli-stream-json';
|
|
815
|
+
}
|
|
816
|
+
function openRepository(ctx, args) {
|
|
817
|
+
const path = databasePath(ctx, args);
|
|
818
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
819
|
+
try {
|
|
820
|
+
return new SessionRepository(path);
|
|
821
|
+
}
|
|
822
|
+
catch (error) {
|
|
823
|
+
throw new CliError('CATALOG_UNAVAILABLE', error instanceof Error ? error.message : String(error), EXIT.storage);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
function databasePath(ctx, args) {
|
|
827
|
+
return resolve(ctx.cwd, args.values.get('--db') ?? '.aiwg/sessions/catalog.sqlite');
|
|
828
|
+
}
|
|
829
|
+
function parseArgs(argv) {
|
|
830
|
+
const flags = new Set();
|
|
831
|
+
const values = new Map();
|
|
832
|
+
const positionals = [];
|
|
833
|
+
const valueFlags = new Set([
|
|
834
|
+
'--db', '--provider', '--workspace', '--tag', '--limit', '--cursor', '--source-id',
|
|
835
|
+
'--date-from', '--date-to', '--participant', '--model', '--role', '--tool',
|
|
836
|
+
'--entity', '--sensitivity', '--extraction-state', '--page-size', '--max-documents',
|
|
837
|
+
'--state', '--reviewer', '--reason', '--policy-version', '--min-confidence',
|
|
838
|
+
'--consumer', '--actor-class', '--reason-code', '--dependent-action', '--basis',
|
|
839
|
+
]);
|
|
840
|
+
let command;
|
|
841
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
842
|
+
const arg = argv[index];
|
|
843
|
+
if (valueFlags.has(arg)) {
|
|
844
|
+
const value = argv[index + 1];
|
|
845
|
+
if (!value || value.startsWith('--')) {
|
|
846
|
+
throw new CliError('MISSING_OPTION_VALUE', `missing value for ${arg}`, EXIT.usage);
|
|
847
|
+
}
|
|
848
|
+
values.set(arg, value);
|
|
849
|
+
index += 1;
|
|
850
|
+
}
|
|
851
|
+
else if (arg.startsWith('-')) {
|
|
852
|
+
flags.add(arg);
|
|
853
|
+
}
|
|
854
|
+
else if (!command) {
|
|
855
|
+
command = arg;
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
positionals.push(arg);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return { command, positionals, flags, values };
|
|
862
|
+
}
|
|
863
|
+
function requiredPositional(args, index, name) {
|
|
864
|
+
const value = args.positionals[index];
|
|
865
|
+
if (!value)
|
|
866
|
+
throw new CliError('MISSING_ARGUMENT', `missing required ${name}`, EXIT.usage);
|
|
867
|
+
return value;
|
|
868
|
+
}
|
|
869
|
+
function requiredValue(args, flag) {
|
|
870
|
+
const value = args.values.get(flag);
|
|
871
|
+
if (!value)
|
|
872
|
+
throw new CliError('MISSING_ARGUMENT', `missing required ${flag}`, EXIT.usage);
|
|
873
|
+
return value;
|
|
874
|
+
}
|
|
875
|
+
function authorizationContext(args, operation) {
|
|
876
|
+
return {
|
|
877
|
+
actorId: 'local-catalog-owner',
|
|
878
|
+
workspaceId: requiredValue(args, '--workspace'),
|
|
879
|
+
operation,
|
|
880
|
+
catalogScope: 'workspace',
|
|
881
|
+
mode: 'local-owner',
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function boundedInteger(value, fallback, min, max, name) {
|
|
885
|
+
if (value === undefined)
|
|
886
|
+
return fallback;
|
|
887
|
+
if (!/^\d+$/.test(value))
|
|
888
|
+
throw new CliError('INVALID_ARGUMENT', `${name} must be an integer`, EXIT.usage);
|
|
889
|
+
const parsed = Number(value);
|
|
890
|
+
if (parsed < min || parsed > max) {
|
|
891
|
+
throw new CliError('INVALID_ARGUMENT', `${name} must be between ${min} and ${max}`, EXIT.usage);
|
|
892
|
+
}
|
|
893
|
+
return parsed;
|
|
894
|
+
}
|
|
895
|
+
function boundedNumber(value, fallback, min, max, name) {
|
|
896
|
+
if (value === undefined)
|
|
897
|
+
return fallback;
|
|
898
|
+
const parsed = Number(value);
|
|
899
|
+
if (!Number.isFinite(parsed) || parsed < min || parsed > max) {
|
|
900
|
+
throw new CliError('INVALID_ARGUMENT', `${name} must be between ${min} and ${max}`, EXIT.usage);
|
|
901
|
+
}
|
|
902
|
+
return parsed;
|
|
903
|
+
}
|
|
904
|
+
function candidateState(value) {
|
|
905
|
+
if (value === undefined)
|
|
906
|
+
return undefined;
|
|
907
|
+
const states = new Set([
|
|
908
|
+
'pending', 'accepted', 'rejected', 'deferred', 'promoted', 'superseded',
|
|
909
|
+
]);
|
|
910
|
+
if (!states.has(value)) {
|
|
911
|
+
throw new CliError('INVALID_ARGUMENT', `invalid candidate state: ${value}`, EXIT.usage);
|
|
912
|
+
}
|
|
913
|
+
return value;
|
|
914
|
+
}
|
|
915
|
+
function dependentAction(value) {
|
|
916
|
+
const actions = new Set([
|
|
917
|
+
'revoke', 'supersede', 'retain', 'origin_unavailable', 'delete', 'abort',
|
|
918
|
+
]);
|
|
919
|
+
if (!actions.has(value)) {
|
|
920
|
+
throw new CliError('INVALID_ARGUMENT', `invalid dependent action: ${value}`, EXIT.usage);
|
|
921
|
+
}
|
|
922
|
+
return value;
|
|
923
|
+
}
|
|
924
|
+
function isDryRun(ctx, args) {
|
|
925
|
+
return Boolean(ctx.dryRun || args.flags.has('--dry-run'));
|
|
926
|
+
}
|
|
927
|
+
function ok(command, data) {
|
|
928
|
+
return { envelope: envelope(`sessions.${command}`, 'ok', data, null), exitCode: EXIT.ok };
|
|
929
|
+
}
|
|
930
|
+
function preview(command, data) {
|
|
931
|
+
return { envelope: envelope(`sessions.${command}`, 'preview', data, null), exitCode: EXIT.ok };
|
|
932
|
+
}
|
|
933
|
+
function envelope(command, status, data, error) {
|
|
934
|
+
return { contractVersion: JSON_CONTRACT_VERSION, command, status, data, error };
|
|
935
|
+
}
|
|
936
|
+
function emit(value) {
|
|
937
|
+
console.log(JSON.stringify(value, null, 2));
|
|
938
|
+
}
|
|
939
|
+
function printHuman(value) {
|
|
940
|
+
if (value.status === 'preview')
|
|
941
|
+
console.log('Preview (no changes applied)');
|
|
942
|
+
console.log(JSON.stringify(value.data, null, 2));
|
|
943
|
+
}
|
|
944
|
+
class CliError extends Error {
|
|
945
|
+
code;
|
|
946
|
+
exitCode;
|
|
947
|
+
constructor(code, message, exitCode) {
|
|
948
|
+
super(message);
|
|
949
|
+
this.code = code;
|
|
950
|
+
this.exitCode = exitCode;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
function normalizeError(error) {
|
|
954
|
+
if (error instanceof CliError) {
|
|
955
|
+
return { error: { code: error.code, message: error.message }, exitCode: error.exitCode };
|
|
956
|
+
}
|
|
957
|
+
if (error instanceof SessionContractError) {
|
|
958
|
+
const exitCode = error.code === 'UNSUPPORTED_OPERATION' ? EXIT.unsupported : EXIT.contract;
|
|
959
|
+
return { error: { code: error.code, message: error.message }, exitCode };
|
|
960
|
+
}
|
|
961
|
+
return {
|
|
962
|
+
error: { code: 'INTERNAL_ERROR', message: error instanceof Error ? error.message : String(error) },
|
|
963
|
+
exitCode: EXIT.storage,
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
//# sourceMappingURL=sessions.js.map
|