@aiwg/cli 2026.9.12 → 2026.9.16
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/agentic/code/providers/capability-matrix.yaml +42 -0
- package/agentic/code/providers/model-capabilities.v1.json +119 -34
- package/agentic/code/providers/model-catalog.v1.json +259 -56
- package/dist/src/cli/handlers/sessions.js +348 -42
- package/dist/src/cli/handlers/use.js +20 -1
- package/dist/src/cli/scope-resolver.js +13 -0
- package/dist/src/cli/services/deployment-verification.js +33 -2
- package/dist/src/config/aiwg-config.js +39 -0
- package/dist/src/config/user-registry.js +9 -0
- package/dist/src/extensions/manifest.js +1 -0
- package/dist/src/models/model-capabilities.v1.json +119 -34
- package/dist/src/models/model-catalog.v1.json +259 -56
- package/dist/src/models/model-discovery.js +7 -0
- package/dist/src/models/provider-policy.js +1 -1
- package/dist/src/plugin/skill-command-translator.js +1 -0
- package/dist/src/providers/capability-matrix.yaml +42 -0
- package/dist/src/providers/grokbot-paths.js +103 -0
- package/dist/src/providers/provider-definitions.js +85 -1
- package/dist/src/providers/provider-inventory.js +1 -0
- package/dist/src/sessions/adapters/claude.js +127 -3
- package/dist/src/sessions/adapters/grokbot.js +49 -0
- package/dist/src/sessions/adapters/hermes.js +17 -9
- package/dist/src/sessions/adapters/opencode.js +12 -1
- package/dist/src/sessions/contracts.js +1 -1
- package/dist/src/sessions/export-plan.js +243 -0
- package/dist/src/sessions/fortemi-export-mapping.js +300 -0
- package/dist/src/sessions/fortemi-shard-blobs.js +78 -0
- package/dist/src/sessions/fortemi-shard-recovery.js +151 -0
- package/dist/src/sessions/index.js +6 -0
- package/dist/src/sessions/output-lineage.js +43 -0
- package/dist/src/sessions/workspace-discovery.js +1 -1
- package/dist/src/skills/deployer.js +17 -0
- package/dist/src/smiths/skillsmith/collision-detector.js +1 -0
- package/package.json +1 -1
- package/tools/agents/deploy-agents.mjs +3 -2
- package/tools/agents/providers/grokbot.mjs +285 -0
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { resolveHermesHomePath } from './hermes-home.js';
|
|
5
|
+
import { resolveGrokbotSkillsDir } from './grokbot-paths.js';
|
|
5
6
|
import { getProviderCapabilities, } from './capability-matrix.js';
|
|
6
7
|
const ArtifactPathsSchema = z.object({
|
|
7
8
|
agents: z.string().nullable(),
|
|
@@ -19,6 +20,7 @@ const ProviderDefinitionSchema = z.object({
|
|
|
19
20
|
'cursor',
|
|
20
21
|
'deepseek-harness',
|
|
21
22
|
'factory',
|
|
23
|
+
'grokbot',
|
|
22
24
|
'hermes',
|
|
23
25
|
'opencode',
|
|
24
26
|
'openclaw',
|
|
@@ -140,6 +142,7 @@ export const PROVIDER_IDS = [
|
|
|
140
142
|
'cursor',
|
|
141
143
|
'deepseek-harness',
|
|
142
144
|
'factory',
|
|
145
|
+
'grokbot',
|
|
143
146
|
'hermes',
|
|
144
147
|
'opencode',
|
|
145
148
|
'openclaw',
|
|
@@ -204,6 +207,17 @@ const CONTEXT_CONTRACTS = {
|
|
|
204
207
|
bootstrapTargets: ['AGENTS.md'], maxContextBytes: null, recommendedMaxLines: 150, nestedContext: true, support: 'supported',
|
|
205
208
|
verification: { method: 'official Factory AGENTS.md discovery hierarchy', source: 'https://docs.factory.ai/cli/configuration/agents-md', lastVerified: VERIFIED_ON },
|
|
206
209
|
},
|
|
210
|
+
grokbot: {
|
|
211
|
+
startupFiles: ['AGENTS.md'],
|
|
212
|
+
precedence: ['provider/system', 'project AGENTS.md when the host exposes it', 'explicit aiwg discover/show'],
|
|
213
|
+
loadMode: 'prose-directive', includeSyntax: null, configRegistration: null,
|
|
214
|
+
bootstrapTargets: ['AGENTS.md'], maxContextBytes: null, recommendedMaxLines: null, nestedContext: false, support: 'degraded',
|
|
215
|
+
verification: {
|
|
216
|
+
method: 'AIWG discover-first adapter contract; native Grok Bot startup/include path not yet verified — explicit-read guidance only',
|
|
217
|
+
source: 'docs/architecture/adr-grokbot-provider-target.md',
|
|
218
|
+
lastVerified: '2026-09-15',
|
|
219
|
+
},
|
|
220
|
+
},
|
|
207
221
|
hermes: {
|
|
208
222
|
startupFiles: ['.hermes.md', 'AGENTS.md'], precedence: ['provider/system', '.hermes.md', 'AGENTS.md'],
|
|
209
223
|
loadMode: 'prose-directive', includeSyntax: null, configRegistration: null,
|
|
@@ -556,6 +570,68 @@ const BUILT_IN_SEEDS = [
|
|
|
556
570
|
},
|
|
557
571
|
matrixRef: 'factory',
|
|
558
572
|
},
|
|
573
|
+
{
|
|
574
|
+
id: 'grokbot',
|
|
575
|
+
displayName: 'Grok Bot',
|
|
576
|
+
aliases: [],
|
|
577
|
+
// #210: promoted stable — Linux PUW + security + migration + maintainer Linux-only waiver 2026-09-16.
|
|
578
|
+
status: 'stable',
|
|
579
|
+
builtIn: true,
|
|
580
|
+
surfaces: {
|
|
581
|
+
primary: 'grokbot',
|
|
582
|
+
compatibility: [],
|
|
583
|
+
precedence: ['AGENTS.md', 'AIWG_GROKBOT_SKILLS_DIR when configured'],
|
|
584
|
+
related: [],
|
|
585
|
+
},
|
|
586
|
+
// Do not treat xAI API keys or generic GROK_* model env as Grok Bot evidence.
|
|
587
|
+
detection: { env: [], process: [], capabilityId: 'grokbot' },
|
|
588
|
+
paths: {
|
|
589
|
+
deployTarget: 'mixed',
|
|
590
|
+
artifacts: {
|
|
591
|
+
agents: null,
|
|
592
|
+
commands: null,
|
|
593
|
+
// Native skill copies only when AIWG_GROKBOT_SKILLS_DIR is set at deploy time.
|
|
594
|
+
skills: null,
|
|
595
|
+
rules: null,
|
|
596
|
+
behaviors: null,
|
|
597
|
+
},
|
|
598
|
+
kernelSkills: null,
|
|
599
|
+
contextDiscovery: {
|
|
600
|
+
agents: '.agents/agents',
|
|
601
|
+
skills: null,
|
|
602
|
+
rules: null,
|
|
603
|
+
behaviors: null,
|
|
604
|
+
},
|
|
605
|
+
configFile: 'AGENTS.md',
|
|
606
|
+
contextFiles: { aiwgMd: true, agentsMd: true, claudeMdHook: false, hookFile: null, contextFile: 'AGENTS.md' },
|
|
607
|
+
},
|
|
608
|
+
smithPaths: {
|
|
609
|
+
agents: null,
|
|
610
|
+
commands: null,
|
|
611
|
+
skills: null,
|
|
612
|
+
rules: null,
|
|
613
|
+
fileExtension: '.md',
|
|
614
|
+
configFile: 'AGENTS.md',
|
|
615
|
+
aggregated: false,
|
|
616
|
+
},
|
|
617
|
+
// Skills root is env-gated (AIWG_GROKBOT_SKILLS_DIR). skillsBaseDir is a
|
|
618
|
+
// documentation sentinel only — deployer resolves via resolveGrokbotSkillsDir
|
|
619
|
+
// and never joins ~/grokbot-skills.
|
|
620
|
+
skillNamespace: {
|
|
621
|
+
deploymentGroup: 'deep-recursion',
|
|
622
|
+
pathType: 'home-dir',
|
|
623
|
+
skillsBaseDir: 'AIWG_GROKBOT_SKILLS_DIR',
|
|
624
|
+
subdirLayout: true,
|
|
625
|
+
},
|
|
626
|
+
adapters: {
|
|
627
|
+
agentFormat: 'agents-md',
|
|
628
|
+
hookBridge: null,
|
|
629
|
+
mcpInjection: null,
|
|
630
|
+
contextAggregation: 'agents-md',
|
|
631
|
+
ruleFormat: 'agents-md-section',
|
|
632
|
+
},
|
|
633
|
+
matrixRef: 'grokbot',
|
|
634
|
+
},
|
|
559
635
|
{
|
|
560
636
|
id: 'hermes',
|
|
561
637
|
aliases: [],
|
|
@@ -1137,10 +1213,18 @@ export function getProviderArtifactPathStrings(provider) {
|
|
|
1137
1213
|
const definition = getProviderDefinition(provider);
|
|
1138
1214
|
if (!definition)
|
|
1139
1215
|
return undefined;
|
|
1216
|
+
const normalized = normalizeProviderDefinitionId(provider);
|
|
1217
|
+
// Grok Bot skills are env-gated (AIWG_GROKBOT_SKILLS_DIR). Resolve dynamically so
|
|
1218
|
+
// deploy counting / user-scope same-path inventory can see the configured root
|
|
1219
|
+
// without inventing ~/.grokbot (#210).
|
|
1220
|
+
let skills = expandProviderHomePath(definition.paths.artifacts.skills);
|
|
1221
|
+
if (normalized === 'grokbot' && !skills) {
|
|
1222
|
+
skills = resolveGrokbotSkillsDir() ?? '';
|
|
1223
|
+
}
|
|
1140
1224
|
return {
|
|
1141
1225
|
agents: expandProviderHomePath(definition.paths.artifacts.agents),
|
|
1142
1226
|
commands: expandProviderHomePath(definition.paths.artifacts.commands),
|
|
1143
|
-
skills
|
|
1227
|
+
skills,
|
|
1144
1228
|
rules: expandProviderHomePath(definition.paths.artifacts.rules),
|
|
1145
1229
|
behaviors: expandProviderHomePath(definition.paths.artifacts.behaviors),
|
|
1146
1230
|
};
|
|
@@ -12,6 +12,7 @@ const PROVIDER_EXECUTABLES = {
|
|
|
12
12
|
cursor: ['cursor'],
|
|
13
13
|
'deepseek-harness': ['dsh'],
|
|
14
14
|
factory: ['droid'],
|
|
15
|
+
// Grok Bot has no verified CLI executable name; omit rather than invent.
|
|
15
16
|
hermes: ['hermes'],
|
|
16
17
|
opencode: ['opencode'],
|
|
17
18
|
openclaw: ['openclaw'],
|
|
@@ -3,9 +3,12 @@ import { basename, extname, resolve } from 'node:path';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { SessionContractError, assertSupportedSchemaMajor, sha256, } from '../contracts.js';
|
|
5
5
|
import { redactSourceLocator } from '../discovery.js';
|
|
6
|
-
import { readBoundedJsonLines, streamBoundedJsonLines, } from '../readers.js';
|
|
7
|
-
export const CLAUDE_ADAPTER_VERSION = '1.
|
|
6
|
+
import { readBoundedJson, readBoundedJsonLines, streamBoundedJsonLines, } from '../readers.js';
|
|
7
|
+
export const CLAUDE_ADAPTER_VERSION = '1.1.0';
|
|
8
8
|
export const CLAUDE_TRANSCRIPT_SCHEMA_VERSION = '1.0.0';
|
|
9
|
+
/** AIWG's own understanding of the layout it supports; Claude web/account
|
|
10
|
+
* exports carry no in-band schema version to read instead. */
|
|
11
|
+
export const CLAUDE_WEB_EXPORT_SCHEMA_VERSION = '1.0.0';
|
|
9
12
|
const ClaudeRecordSchema = z.object({
|
|
10
13
|
type: z.string().min(1),
|
|
11
14
|
subtype: z.string().optional(),
|
|
@@ -40,6 +43,43 @@ const ClaudeHookSchema = z.object({
|
|
|
40
43
|
schemaVersion: z.string().optional(),
|
|
41
44
|
timestamp: z.string().datetime({ offset: true }).optional(),
|
|
42
45
|
}).passthrough();
|
|
46
|
+
/**
|
|
47
|
+
* Claude.ai web/account data export (`conversations.json`): a JSON array of
|
|
48
|
+
* conversations, each carrying its own `chat_messages`. This is a distinct
|
|
49
|
+
* source format from local JSONL transcripts/hooks -- renaming the file does
|
|
50
|
+
* not make it one (#2565). Unrecognized fields are captured via unknownFields
|
|
51
|
+
* rather than dropped, and content the export marks as an attachment/file is
|
|
52
|
+
* recorded as a reference (bytes are not embedded in this export format).
|
|
53
|
+
*/
|
|
54
|
+
const ClaudeWebExportAttachmentSchema = z.object({
|
|
55
|
+
file_name: z.string().optional(),
|
|
56
|
+
file_type: z.string().optional(),
|
|
57
|
+
file_size: z.number().optional(),
|
|
58
|
+
extracted_content: z.string().optional(),
|
|
59
|
+
}).passthrough();
|
|
60
|
+
const ClaudeWebExportContentBlockSchema = z.object({
|
|
61
|
+
type: z.string().optional(),
|
|
62
|
+
text: z.string().optional(),
|
|
63
|
+
}).passthrough();
|
|
64
|
+
const ClaudeWebExportMessageSchema = z.object({
|
|
65
|
+
uuid: z.string().min(1),
|
|
66
|
+
text: z.string().optional(),
|
|
67
|
+
sender: z.enum(['human', 'assistant']),
|
|
68
|
+
created_at: z.string().optional(),
|
|
69
|
+
updated_at: z.string().optional(),
|
|
70
|
+
content: z.array(ClaudeWebExportContentBlockSchema).optional(),
|
|
71
|
+
attachments: z.array(ClaudeWebExportAttachmentSchema).optional(),
|
|
72
|
+
files: z.array(ClaudeWebExportAttachmentSchema).optional(),
|
|
73
|
+
}).passthrough();
|
|
74
|
+
const ClaudeWebExportConversationSchema = z.object({
|
|
75
|
+
uuid: z.string().min(1),
|
|
76
|
+
name: z.string().optional(),
|
|
77
|
+
created_at: z.string().optional(),
|
|
78
|
+
updated_at: z.string().optional(),
|
|
79
|
+
account: z.object({ uuid: z.string().optional() }).passthrough().optional(),
|
|
80
|
+
chat_messages: z.array(ClaudeWebExportMessageSchema),
|
|
81
|
+
}).passthrough();
|
|
82
|
+
const ClaudeWebExportSchema = z.array(ClaudeWebExportConversationSchema);
|
|
43
83
|
export class ClaudeSessionAdapter {
|
|
44
84
|
limits;
|
|
45
85
|
discoveryLimits;
|
|
@@ -47,7 +87,7 @@ export class ClaudeSessionAdapter {
|
|
|
47
87
|
adapterVersion = CLAUDE_ADAPTER_VERSION;
|
|
48
88
|
disposition = 'implemented';
|
|
49
89
|
supportedOperations = ['discover', 'inspect', 'stream'];
|
|
50
|
-
acquisitionModes = ['jsonl', 'hook'];
|
|
90
|
+
acquisitionModes = ['jsonl', 'hook', 'manual-export'];
|
|
51
91
|
constructor(limits, discoveryLimits = { maxDepth: 8, maxFiles: 10_000 }) {
|
|
52
92
|
this.limits = limits;
|
|
53
93
|
this.discoveryLimits = discoveryLimits;
|
|
@@ -80,6 +120,13 @@ export class ClaudeSessionAdapter {
|
|
|
80
120
|
};
|
|
81
121
|
}
|
|
82
122
|
async *stream(source, cursor) {
|
|
123
|
+
if (source.locatorClass === 'claude-web-export-json') {
|
|
124
|
+
const parsed = await this.readWebExportSource(source);
|
|
125
|
+
const start = parseRecordCursor(cursor?.value);
|
|
126
|
+
for (const record of parsed.records.slice(start))
|
|
127
|
+
yield record;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
83
130
|
const start = parseRecordCursor(cursor?.value);
|
|
84
131
|
const input = await streamBoundedJsonLines({
|
|
85
132
|
selectedPath: source.locator,
|
|
@@ -112,6 +159,9 @@ export class ClaudeSessionAdapter {
|
|
|
112
159
|
}
|
|
113
160
|
}
|
|
114
161
|
async readSource(source) {
|
|
162
|
+
if (source.locatorClass === 'claude-web-export-json') {
|
|
163
|
+
return this.readWebExportSource(source);
|
|
164
|
+
}
|
|
115
165
|
const result = await readBoundedJsonLines({
|
|
116
166
|
selectedPath: source.locator,
|
|
117
167
|
allowedRoots: source.authorizedScope.allowedRoots,
|
|
@@ -132,7 +182,81 @@ export class ClaudeSessionAdapter {
|
|
|
132
182
|
consistency: normalized.complete ? 'complete' : 'provisional',
|
|
133
183
|
};
|
|
134
184
|
}
|
|
185
|
+
async readWebExportSource(source) {
|
|
186
|
+
const { value } = await readBoundedJson({ selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots }, this.limits);
|
|
187
|
+
const parsed = ClaudeWebExportSchema.safeParse(value);
|
|
188
|
+
if (!parsed.success) {
|
|
189
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Claude web/account export is not a recognized conversations.json layout');
|
|
190
|
+
}
|
|
191
|
+
if (parsed.data.length === 0) {
|
|
192
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Claude web/account export is empty');
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
records: normalizeWebExport(parsed.data),
|
|
196
|
+
schemaVersion: CLAUDE_WEB_EXPORT_SCHEMA_VERSION,
|
|
197
|
+
// The export is a completed snapshot at request time, not a live tail.
|
|
198
|
+
consistency: 'complete',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
135
201
|
}
|
|
202
|
+
function normalizeWebExport(conversations) {
|
|
203
|
+
const output = [];
|
|
204
|
+
const seenConversationIds = new Set();
|
|
205
|
+
for (const conversation of conversations) {
|
|
206
|
+
if (seenConversationIds.has(conversation.uuid)) {
|
|
207
|
+
throw new SessionContractError('DUPLICATE_NATIVE_ID', `Claude web export declares the same conversation uuid twice: ${conversation.uuid}`);
|
|
208
|
+
}
|
|
209
|
+
seenConversationIds.add(conversation.uuid);
|
|
210
|
+
const seenMessageIds = new Set();
|
|
211
|
+
for (const [index, message] of conversation.chat_messages.entries()) {
|
|
212
|
+
if (seenMessageIds.has(message.uuid)) {
|
|
213
|
+
throw new SessionContractError('DUPLICATE_NATIVE_ID', `Claude web export conversation ${conversation.uuid} declares the same message uuid twice: ${message.uuid}`);
|
|
214
|
+
}
|
|
215
|
+
seenMessageIds.add(message.uuid);
|
|
216
|
+
output.push(webExportProviderRecord(conversation, message, index));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return output;
|
|
220
|
+
}
|
|
221
|
+
function webExportProviderRecord(conversation, message, sequence) {
|
|
222
|
+
const text = message.text && message.text.length > 0
|
|
223
|
+
? message.text
|
|
224
|
+
: (message.content ?? []).map((block) => block.text ?? '').filter(Boolean).join('\n');
|
|
225
|
+
const attachments = [...(message.attachments ?? []), ...(message.files ?? [])];
|
|
226
|
+
return {
|
|
227
|
+
nativeSessionId: conversation.uuid,
|
|
228
|
+
nativeEventId: message.uuid,
|
|
229
|
+
sequence,
|
|
230
|
+
kind: 'message',
|
|
231
|
+
role: message.sender,
|
|
232
|
+
participant: message.sender,
|
|
233
|
+
occurredAt: message.created_at,
|
|
234
|
+
text,
|
|
235
|
+
rawReference: { locatorClass: 'claude-web-export-json', sequence },
|
|
236
|
+
extensions: {
|
|
237
|
+
conversationName: conversation.name,
|
|
238
|
+
conversationCreatedAt: conversation.created_at,
|
|
239
|
+
conversationUpdatedAt: conversation.updated_at,
|
|
240
|
+
provenance: { acquisition: 'claude-web-export-json', schema: CLAUDE_WEB_EXPORT_SCHEMA_VERSION },
|
|
241
|
+
// Web/account exports reference attachments/files by metadata only; the
|
|
242
|
+
// export format does not embed original bytes, so this is a coverage
|
|
243
|
+
// note (#2565 acceptance criteria), not a loss AIWG introduced.
|
|
244
|
+
...(attachments.length > 0 ? {
|
|
245
|
+
attachmentReferences: attachments.map((attachment) => ({
|
|
246
|
+
fileName: attachment.file_name,
|
|
247
|
+
fileType: attachment.file_type,
|
|
248
|
+
fileSize: attachment.file_size,
|
|
249
|
+
extractedContentAvailable: typeof attachment.extracted_content === 'string',
|
|
250
|
+
})),
|
|
251
|
+
metadataLoss: ['attachment bytes unavailable in web/account export format'],
|
|
252
|
+
} : {}),
|
|
253
|
+
unknownFields: unknownFields(message, WEB_EXPORT_MESSAGE_KEYS),
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const WEB_EXPORT_MESSAGE_KEYS = new Set([
|
|
258
|
+
'uuid', 'text', 'sender', 'created_at', 'updated_at', 'content', 'attachments', 'files',
|
|
259
|
+
]);
|
|
136
260
|
function normalizeTranscript(records, locator) {
|
|
137
261
|
const filenameSessionId = basename(locator, extname(locator));
|
|
138
262
|
const output = [];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grok Bot session adapter — manual-export only until product evidence exists
|
|
3
|
+
* for a native session filesystem or API locator.
|
|
4
|
+
*
|
|
5
|
+
* Auto-discover is unsupported and must never scrape invented ~/.grokbot,
|
|
6
|
+
* Cursor session paths, or other unverified roots.
|
|
7
|
+
*
|
|
8
|
+
* Authorized imports reuse the AIWG generic session interchange format so
|
|
9
|
+
* operators can supply an explicit export without inventing a Grok schema.
|
|
10
|
+
*
|
|
11
|
+
* @see docs/providers/grokbot-sessions.md
|
|
12
|
+
* @issue #215
|
|
13
|
+
*/
|
|
14
|
+
import { SessionContractError, } from '../contracts.js';
|
|
15
|
+
import { GenericSessionInterchangeAdapter, } from './generic.js';
|
|
16
|
+
export const GROKBOT_ADAPTER_VERSION = '1.0.0';
|
|
17
|
+
export const GROKBOT_EXPORT_SCHEMA_VERSION = '1.0.0';
|
|
18
|
+
export const GROKBOT_LOCATOR_CLASS = 'manual-export';
|
|
19
|
+
export class GrokbotSessionAdapter {
|
|
20
|
+
provider = 'grokbot';
|
|
21
|
+
adapterVersion = GROKBOT_ADAPTER_VERSION;
|
|
22
|
+
disposition = 'manual-only';
|
|
23
|
+
supportedOperations = ['inspect', 'stream'];
|
|
24
|
+
acquisitionModes = ['manual-export'];
|
|
25
|
+
interchange;
|
|
26
|
+
constructor(limits) {
|
|
27
|
+
this.interchange = new GenericSessionInterchangeAdapter(limits);
|
|
28
|
+
}
|
|
29
|
+
async *discover(_scope) {
|
|
30
|
+
throw new SessionContractError('UNSUPPORTED_OPERATION', 'Grok Bot session auto-discover is unsupported until a verified native locator exists; '
|
|
31
|
+
+ 'select an authorized manual export explicitly. AIWG does not scrape ~/.grokbot, '
|
|
32
|
+
+ '~/grokbot-skills, or Cursor session paths.');
|
|
33
|
+
}
|
|
34
|
+
async inspect(source) {
|
|
35
|
+
this.assertManualExport(source);
|
|
36
|
+
return this.interchange.inspect(source);
|
|
37
|
+
}
|
|
38
|
+
async *stream(source, cursor) {
|
|
39
|
+
this.assertManualExport(source);
|
|
40
|
+
yield* this.interchange.stream(source, cursor);
|
|
41
|
+
}
|
|
42
|
+
assertManualExport(source) {
|
|
43
|
+
if (source.locatorClass !== GROKBOT_LOCATOR_CLASS) {
|
|
44
|
+
throw new SessionContractError('UNSUPPORTED_OPERATION', `Grok Bot supports only locatorClass "${GROKBOT_LOCATOR_CLASS}" until native session evidence exists `
|
|
45
|
+
+ `(got "${source.locatorClass}"). Do not reuse Cursor session locators.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=grokbot.js.map
|
|
@@ -4,23 +4,31 @@ import { readBoundedJsonLines, streamBoundedJsonLines, } from '../readers.js';
|
|
|
4
4
|
export const HERMES_ADAPTER_VERSION = '1.0.0';
|
|
5
5
|
export const HERMES_EXPORT_SCHEMA_VERSION = '1.0.0';
|
|
6
6
|
export const HERMES_NATIVE_SCHEMA_VERSION = '23.0.0';
|
|
7
|
+
const sqliteBoolean = z.union([z.boolean(), z.literal(0), z.literal(1)])
|
|
8
|
+
.transform((value) => Boolean(value));
|
|
9
|
+
function nullableOptional(schema) {
|
|
10
|
+
return z.preprocess((value) => value === null ? undefined : value, schema.optional());
|
|
11
|
+
}
|
|
7
12
|
const MessageSchema = z.object({
|
|
8
13
|
id: z.union([z.string(), z.number()]),
|
|
9
14
|
role: z.string().min(1),
|
|
10
15
|
content: z.unknown().optional(),
|
|
11
|
-
tool_call_id: z.string()
|
|
16
|
+
tool_call_id: nullableOptional(z.string()),
|
|
12
17
|
tool_calls: z.unknown().optional(),
|
|
13
|
-
tool_name: z.string()
|
|
18
|
+
tool_name: nullableOptional(z.string()),
|
|
14
19
|
timestamp: z.number().optional(),
|
|
15
|
-
token_count: z.number()
|
|
16
|
-
finish_reason: z.string()
|
|
17
|
-
reasoning: z.string()
|
|
18
|
-
reasoning_content: z.string()
|
|
20
|
+
token_count: nullableOptional(z.number()),
|
|
21
|
+
finish_reason: nullableOptional(z.string()),
|
|
22
|
+
reasoning: nullableOptional(z.string()),
|
|
23
|
+
reasoning_content: nullableOptional(z.string()),
|
|
19
24
|
reasoning_details: z.unknown().optional(),
|
|
20
25
|
codex_reasoning_items: z.unknown().optional(),
|
|
21
26
|
}).passthrough();
|
|
22
27
|
const ExportSchema = z.object({
|
|
23
|
-
|
|
28
|
+
// Hermes CLI exports currently omit a schema marker. They use the same
|
|
29
|
+
// schema-23 record shape that the adapter already validates; explicit
|
|
30
|
+
// markers still pass through the major-version gate below.
|
|
31
|
+
schemaVersion: z.union([z.string(), z.number()]).default(HERMES_NATIVE_SCHEMA_VERSION),
|
|
24
32
|
id: z.string().min(1),
|
|
25
33
|
source: z.string().min(1),
|
|
26
34
|
user_id: z.string().nullable().optional(),
|
|
@@ -31,8 +39,8 @@ const ExportSchema = z.object({
|
|
|
31
39
|
started_at: z.number(),
|
|
32
40
|
ended_at: z.number().nullable().optional(),
|
|
33
41
|
end_reason: z.string().nullable().optional(),
|
|
34
|
-
archived:
|
|
35
|
-
inactive:
|
|
42
|
+
archived: sqliteBoolean.optional(),
|
|
43
|
+
inactive: sqliteBoolean.optional(),
|
|
36
44
|
cwd: z.string().nullable().optional(),
|
|
37
45
|
git_branch: z.string().nullable().optional(),
|
|
38
46
|
routing_key: z.string().nullable().optional(),
|
|
@@ -343,7 +343,18 @@ function normalizeMessage(message, sequence, locatorClass, common) {
|
|
|
343
343
|
toolCallId: part.callID,
|
|
344
344
|
toolState: part.state,
|
|
345
345
|
attachment: part.type === 'file'
|
|
346
|
-
? {
|
|
346
|
+
? {
|
|
347
|
+
mime: part.mime,
|
|
348
|
+
filename: part.filename,
|
|
349
|
+
displayName: part.filename,
|
|
350
|
+
urlPresent: Boolean(part.url),
|
|
351
|
+
// Preserve only locally recoverable byte locators. Remote URLs
|
|
352
|
+
// remain presence-only metadata and are never fetched by export.
|
|
353
|
+
dataBytes: part.url?.startsWith('data:')
|
|
354
|
+
? [...Buffer.from(part.url.slice(part.url.indexOf(',') + 1), part.url.includes(';base64,') ? 'base64' : 'utf8')]
|
|
355
|
+
: undefined,
|
|
356
|
+
fileUrl: part.url?.startsWith('file:') ? part.url : undefined,
|
|
357
|
+
}
|
|
347
358
|
: undefined,
|
|
348
359
|
opaqueContent: !['text', 'reasoning', 'tool', 'file'].includes(part.type),
|
|
349
360
|
unknownFields: unknownFields(part, PART_KEYS),
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
export const SESSION_CONTRACT_VERSION = '1.0.0';
|
|
4
4
|
export const SESSION_PROVIDER_IDS = [
|
|
5
5
|
'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
|
|
6
|
-
'opencode', 'openclaw', 'openhuman', 'pi', 'omp', 'deepseek-harness', 'warp', 'devin-desktop', 'generic',
|
|
6
|
+
'opencode', 'openclaw', 'openhuman', 'grokbot', 'pi', 'omp', 'deepseek-harness', 'warp', 'devin-desktop', 'generic',
|
|
7
7
|
];
|
|
8
8
|
export const SessionProviderIdSchema = z.enum(SESSION_PROVIDER_IDS);
|
|
9
9
|
export const SESSION_PROVIDER_ALIASES = Object.freeze({
|