@aiwg/cli 2026.10.11 → 2026.10.12

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.
Files changed (132) hide show
  1. package/agentic/code/providers/model-catalog.v1.json +62 -12
  2. package/dist/src/artifacts/binding-target-identity.js +51 -0
  3. package/dist/src/artifacts/checksum-manifest.js +12 -0
  4. package/dist/src/artifacts/index-builder.js +11 -13
  5. package/dist/src/artifacts/query-engine.js +5 -11
  6. package/dist/src/cli/handlers/doctor-help.js +32 -0
  7. package/dist/src/cli/handlers/gates.js +19 -18
  8. package/dist/src/cli/handlers/models-help.js +7 -1
  9. package/dist/src/cli/handlers/models.js +3 -3
  10. package/dist/src/cli/handlers/script-runner.js +8 -5
  11. package/dist/src/cli/handlers/sessions-intake.js +104 -0
  12. package/dist/src/cli/handlers/sessions-tail.js +172 -0
  13. package/dist/src/cli/handlers/sessions.js +90 -17
  14. package/dist/src/cli/handlers/skill-lint.js +10 -3
  15. package/dist/src/cli/handlers/steward-format.js +100 -0
  16. package/dist/src/cli/handlers/steward-models.js +167 -0
  17. package/dist/src/cli/handlers/steward.js +14 -248
  18. package/dist/src/cli/handlers/use-bundle-counts.js +61 -0
  19. package/dist/src/cli/handlers/use.js +6 -24
  20. package/dist/src/cli/handlers/utilities.js +1 -24
  21. package/dist/src/config/aiwg-config.js +7 -0
  22. package/dist/src/config/authorization-cli.js +89 -0
  23. package/dist/src/config/cli-help.js +1 -0
  24. package/dist/src/config/cli.js +10 -1
  25. package/dist/src/decision/context-pruning.js +3 -2
  26. package/dist/src/decision/ensemble/contract.js +15 -4
  27. package/dist/src/decision/ensemble-study/artifacts.js +5 -0
  28. package/dist/src/decision/ensemble-study/score.js +4 -3
  29. package/dist/src/decision/feature-export.js +3 -9
  30. package/dist/src/decision/heldout/collector.js +2 -2
  31. package/dist/src/decision/issue-triage/runtime.js +3 -9
  32. package/dist/src/decision/qualification/gates.js +2 -1
  33. package/dist/src/decision/qualification/release.js +2 -3
  34. package/dist/src/decision/routing/report.js +2 -0
  35. package/dist/src/decision/sdlc-screening.js +3 -5
  36. package/dist/src/decision/sensitivity/comparative.js +5 -3
  37. package/dist/src/gates/driver.js +6 -77
  38. package/dist/src/gates/evaluate.js +4 -11
  39. package/dist/src/gates/floors.js +4 -4
  40. package/dist/src/gates/index.js +3 -3
  41. package/dist/src/gates/lattice.js +26 -0
  42. package/dist/src/gates/registry.js +2 -1
  43. package/dist/src/gates/report.js +19 -7
  44. package/dist/src/issues/audit-cli.js +120 -0
  45. package/dist/src/issues/audit-contract.js +64 -0
  46. package/dist/src/issues/audit-local.js +82 -0
  47. package/dist/src/issues/audit.js +220 -0
  48. package/dist/src/lint/rule-activation.js +15 -1
  49. package/dist/src/mission-protocol/canonical-codecs.js +270 -0
  50. package/dist/src/mission-protocol/codecs.js +22 -550
  51. package/dist/src/mission-protocol/health-trace-codecs.js +281 -0
  52. package/dist/src/mission-protocol/health-trace-types.js +43 -0
  53. package/dist/src/mission-protocol/types.js +2 -42
  54. package/dist/src/mission-protocol/worker-contract-json.js +10 -0
  55. package/dist/src/mission-protocol/worker-review-gate.js +100 -0
  56. package/dist/src/mission-protocol/worker-review-types.js +2 -0
  57. package/dist/src/models/catalog-policy.js +280 -0
  58. package/dist/src/models/index.js +1 -1
  59. package/dist/src/models/model-catalog.v1.json +62 -12
  60. package/dist/src/models/policy-resources.js +28 -0
  61. package/dist/src/models/provider-policy.js +10 -546
  62. package/dist/src/models/resolver.js +26 -8
  63. package/dist/src/models/router.js +15 -2
  64. package/dist/src/models/tiers.js +13 -0
  65. package/dist/src/models/types.js +0 -6
  66. package/dist/src/models/worker-ceilings.js +59 -0
  67. package/dist/src/models/worker-invocation-policy.js +243 -0
  68. package/dist/src/models/worker-plan.js +16 -20
  69. package/dist/src/models/worker-review-lineage.js +55 -0
  70. package/dist/src/models/worker-selection-policy.js +1 -1
  71. package/dist/src/models/wrapper-route.js +6 -3
  72. package/dist/src/policy/authorization.js +1 -0
  73. package/dist/src/policy/hitl-config.js +63 -0
  74. package/dist/src/policy/hitl-receipt.js +57 -0
  75. package/dist/src/policy/hitl-types.js +4 -0
  76. package/dist/src/policy/hitl-validation.js +39 -0
  77. package/dist/src/policy/hitl.js +117 -0
  78. package/dist/src/rules/activation.d.mts +1 -1
  79. package/dist/src/rules/activation.mjs +0 -2
  80. package/dist/src/rules/gate-coverage.js +51 -0
  81. package/dist/src/rules/gate-references.js +16 -0
  82. package/dist/src/rules/rule-frontmatter.schema.json +7 -1
  83. package/dist/src/serve/worker-command-request.js +21 -4
  84. package/dist/src/serve/worker-invocation-preparation.js +3 -1
  85. package/dist/src/serve/worker-invocation.js +21 -10
  86. package/dist/src/sessions/adapters/deepseek-harness.js +1 -0
  87. package/dist/src/sessions/adapters/grok-build-native.js +269 -0
  88. package/dist/src/sessions/adapters/grok-build.js +57 -15
  89. package/dist/src/sessions/adapters/hermes-state-db.js +252 -0
  90. package/dist/src/sessions/adapters/hermes.js +42 -3
  91. package/dist/src/sessions/adapters/warp.js +1 -1
  92. package/dist/src/sessions/batch-import.js +10 -0
  93. package/dist/src/sessions/contracts.js +1 -1
  94. package/dist/src/sessions/discovery-capability.js +38 -0
  95. package/dist/src/sessions/importer.js +8 -251
  96. package/dist/src/sessions/index.js +4 -0
  97. package/dist/src/sessions/intake-dir.js +71 -0
  98. package/dist/src/sessions/normalization.js +251 -0
  99. package/dist/src/sessions/tail.js +91 -0
  100. package/dist/src/sessions/workspace-discovery.js +83 -7
  101. package/dist/src/skills/agent-skill-sidecar.js +28 -0
  102. package/dist/src/skills/agent-skills.js +4 -23
  103. package/dist/src/skills/claude-surface-projection.js +25 -0
  104. package/dist/src/skills/deployer.js +8 -288
  105. package/dist/src/skills/deployment-policy.js +114 -0
  106. package/dist/src/skills/deployment-projection.js +152 -0
  107. package/dist/src/skills/deployment-types.js +20 -0
  108. package/dist/src/skills/exporter.js +8 -31
  109. package/dist/src/skills/provider-extensions.js +41 -0
  110. package/dist/src/skills/run.js +6 -0
  111. package/dist/src/skills/types.js +0 -8
  112. package/dist/src/skills/validator-resources.js +68 -0
  113. package/dist/src/skills/validator.js +6 -55
  114. package/dist/src/storage/cli-migrate-options.js +125 -0
  115. package/dist/src/storage/cli-migrate.js +111 -0
  116. package/dist/src/storage/cli.js +4 -279
  117. package/dist/src/storage/migration-adapter.js +79 -0
  118. package/dist/src/storage/migration-copy.js +108 -0
  119. package/dist/src/storage/migration-protocol.js +14 -197
  120. package/dist/src/storage/migration-records.js +186 -0
  121. package/dist/src/storage/migration-types.js +12 -0
  122. package/dist/src/tracker/forge-http.js +2 -1
  123. package/dist/src/tracker/issue-pages.js +110 -0
  124. package/package.json +1 -1
  125. package/schemas/mission-v1.schema.json +335 -0
  126. package/schemas/models/model-catalog.v1.schema.json +19 -1
  127. package/schemas/models/model-config-compatibility.v1.schema.json +3 -2
  128. package/schemas/models/model-policy.v1.schema.json +1 -1
  129. package/tools/agents/providers/provider-paths-staging.mjs +35 -0
  130. package/tools/agents/providers/rule-binding-targets.mjs +146 -0
  131. package/tools/agents/providers/rule-delivery.mjs +7 -126
  132. package/vscode-extension/schemas/aiwg.config.v1.json +177 -0
@@ -0,0 +1,251 @@
1
+ // Shared event normalization, image extraction, redaction, and lifecycle derivation.
2
+ import { SESSION_CONTRACT_VERSION, SessionEventSchema, SessionSchema, sha256, stableEventId, stableSessionId, } from './contracts.js';
3
+ import { redactSessionValue, sanitizeSessionEvent, sanitizeNativeExtensions } from './policy.js';
4
+ import { extractEventImages } from './image-blobs.js';
5
+ import { DEFAULT_INACTIVITY_THRESHOLD_MS } from './lifecycle.js';
6
+ import { classifySessionEventOrigin, deriveSessionIntent } from './origin.js';
7
+ export function normalizeSessionBatch(request, records, digests, importRunId, sourceObservedAt) {
8
+ const sessions = new Map();
9
+ // Extract inline images before sanitization: provider image blocks in
10
+ // extensions are redacted to markers below, so bytes must be lifted out
11
+ // first while the raw record is still intact (#3265).
12
+ const imageBlobs = new Map();
13
+ const events = records.map((record, index) => {
14
+ const sessionId = stableSessionId(request.source.provider, request.source.sourceId, record.nativeSessionId);
15
+ const native = sanitizeNativeExtensions(record.extensions ?? {});
16
+ const lineage = request.source.provider === 'codex' && native.value.lineage
17
+ ? { lineage: native.value.lineage } : {};
18
+ const images = extractEventImages({
19
+ searchableText: record.text,
20
+ extensions: record.extensions,
21
+ rawReference: record.rawReference,
22
+ });
23
+ for (const image of images.images) {
24
+ if (!imageBlobs.has(image.digest))
25
+ imageBlobs.set(image.digest, image);
26
+ }
27
+ const digest = digests[index];
28
+ const origin = classifySessionEventOrigin(request.source.provider, record);
29
+ const lifecycle = sessionLifecycle(request, record, sourceObservedAt);
30
+ const lifecycleEvidence = sessionLifecycleEvidence(request, record, sourceObservedAt);
31
+ const extensions = {
32
+ [`native.${request.source.provider}`]: images.extensions,
33
+ };
34
+ const event = sanitizeSessionEvent(SessionEventSchema.parse({
35
+ contractVersion: SESSION_CONTRACT_VERSION,
36
+ eventId: stableEventId(request.source.provider, request.source.sourceId, record, digest),
37
+ sessionId, sourceId: request.source.sourceId, importRunId,
38
+ nativeId: record.nativeEventId ?? null, sequence: record.sequence,
39
+ kind: record.kind, role: record.role ?? null, occurredAt: record.occurredAt ?? null,
40
+ activityBoundary: record.activityBoundary ?? null,
41
+ activityBoundaryBasis: record.activityBoundaryBasis ?? null,
42
+ activityBoundaryConfidence: record.activityBoundaryConfidence ?? null,
43
+ origin: origin.origin,
44
+ originRule: origin.rule,
45
+ originClassifierVersion: origin.classifierVersion,
46
+ participant: record.participant ?? null,
47
+ toolName: record.toolName ?? null,
48
+ toolCallId: record.toolCallId ?? null,
49
+ model: record.model ?? null,
50
+ entities: record.entities ?? [],
51
+ extractionState: record.extractionState ?? null,
52
+ searchableText: images.searchableText, imageRefs: images.imageRefs,
53
+ digest, rawReference: record.rawReference,
54
+ adapterVersion: request.adapter.adapterVersion,
55
+ consistency: request.source.consistency,
56
+ sensitivity: { classification: 'none', classes: [] },
57
+ opaque: !isKnownEventKind(record.kind),
58
+ extensions,
59
+ }));
60
+ if (!sessions.has(sessionId)) {
61
+ sessions.set(sessionId, SessionSchema.parse({
62
+ contractVersion: SESSION_CONTRACT_VERSION,
63
+ sessionId, sourceId: request.source.sourceId, provider: request.source.provider,
64
+ nativeSessionId: record.nativeSessionId, workspaceId: request.workspaceId,
65
+ startedAt: record.occurredAt ?? null, updatedAt: record.occurredAt ?? null,
66
+ consistency: request.source.consistency,
67
+ lifecycle,
68
+ sourceDigest: sha256(`${sourceGenerationDigest(request)}\0${record.nativeSessionId}`),
69
+ extensions: {
70
+ [`native.${request.source.provider}`]: {
71
+ ...lineage,
72
+ lifecycleEvidence,
73
+ },
74
+ },
75
+ }));
76
+ }
77
+ else {
78
+ const session = sessions.get(sessionId);
79
+ session.startedAt = earlierTimestamp(session.startedAt, record.occurredAt ?? null);
80
+ session.updatedAt = laterTimestamp(session.updatedAt, record.occurredAt ?? null);
81
+ session.consistency = strongerConsistency(session.consistency, request.source.consistency);
82
+ const nativeKey = `native.${request.source.provider}`;
83
+ const nativeEnvelope = session.extensions[nativeKey];
84
+ const nativeRecord = nativeEnvelope && typeof nativeEnvelope === 'object' && !Array.isArray(nativeEnvelope)
85
+ ? nativeEnvelope
86
+ : {};
87
+ const selected = selectLifecycleTransition(session.lifecycle, nativeRecord.lifecycleEvidence, lifecycle, lifecycleEvidence);
88
+ session.lifecycle = selected.lifecycle;
89
+ session.extensions[nativeKey] = {
90
+ ...nativeRecord,
91
+ ...lineage,
92
+ lifecycleEvidence: selected.evidence,
93
+ };
94
+ }
95
+ return event;
96
+ });
97
+ for (const session of sessions.values()) {
98
+ session.intent = deriveSessionIntent(events.filter((event) => event.sessionId === session.sessionId));
99
+ }
100
+ return {
101
+ sessions: redactSessionValue([...sessions.values()]).value,
102
+ events,
103
+ imageBlobs: [...imageBlobs.values()],
104
+ };
105
+ }
106
+ const KNOWN_EVENT_KINDS = new Set(['message', 'tool-call', 'tool-result', 'artifact', 'attachment', 'summary']);
107
+ function isKnownEventKind(kind) {
108
+ const normalized = kind.toLowerCase();
109
+ return KNOWN_EVENT_KINDS.has(normalized)
110
+ || /^(?:tool-call|tool-result)(?:[._-]|$)/.test(normalized)
111
+ || /^(?:sandbox|permission|approval|hitl|human|lifecycle)(?:[._-]|$)/.test(normalized);
112
+ }
113
+ function earlierTimestamp(left, right) {
114
+ if (!left)
115
+ return right;
116
+ if (!right)
117
+ return left;
118
+ return Date.parse(left) <= Date.parse(right) ? left : right;
119
+ }
120
+ function laterTimestamp(left, right) {
121
+ if (!left)
122
+ return right;
123
+ if (!right)
124
+ return left;
125
+ return Date.parse(left) >= Date.parse(right) ? left : right;
126
+ }
127
+ function strongerConsistency(left, right) {
128
+ const rank = {
129
+ provisional: 0,
130
+ 'consistent-snapshot': 1,
131
+ complete: 2,
132
+ };
133
+ return rank[left] >= rank[right] ? left : right;
134
+ }
135
+ function sessionLifecycle(request, record, sourceObservedAt) {
136
+ const explicit = nativeLifecycle(record.extensions);
137
+ if (explicit)
138
+ return explicit;
139
+ if (request.source.consistency === 'complete')
140
+ return 'complete';
141
+ const observedAt = record.occurredAt ?? sourceObservedAt;
142
+ if (observedAt && isStaleHistoricalTimestamp(observedAt, inactivityThresholdMs(request)))
143
+ return 'inactive';
144
+ return 'active';
145
+ }
146
+ function sessionLifecycleEvidence(request, record, sourceObservedAt) {
147
+ const observedAt = record.occurredAt ?? sourceObservedAt;
148
+ const explicit = nativeLifecycle(record.extensions);
149
+ if (explicit) {
150
+ return {
151
+ basis: 'provider-explicit-event',
152
+ state: explicit,
153
+ observedAt: observedAt ?? request.source.authorizedAt,
154
+ confidence: 'high',
155
+ };
156
+ }
157
+ if (request.source.consistency === 'complete') {
158
+ return {
159
+ basis: 'complete-source',
160
+ state: 'complete',
161
+ observedAt: observedAt ?? request.source.authorizedAt,
162
+ confidence: 'high',
163
+ };
164
+ }
165
+ const thresholdMs = inactivityThresholdMs(request);
166
+ if (observedAt && isStaleHistoricalTimestamp(observedAt, thresholdMs)) {
167
+ return {
168
+ basis: 'inactivity-threshold',
169
+ state: 'inactive',
170
+ observedAt,
171
+ confidence: 'medium',
172
+ thresholdMs,
173
+ };
174
+ }
175
+ return {
176
+ basis: 'open-provisional-source',
177
+ state: 'active',
178
+ observedAt: observedAt ?? request.source.authorizedAt,
179
+ confidence: 'provisional',
180
+ thresholdMs,
181
+ };
182
+ }
183
+ function nativeLifecycle(extensions) {
184
+ const raw = typeof extensions?.lifecycle === 'string' ? extensions.lifecycle : undefined;
185
+ if (!raw)
186
+ return null;
187
+ const normalized = {
188
+ active: 'active',
189
+ inactive: 'inactive',
190
+ paused: 'paused',
191
+ complete: 'complete',
192
+ completed: 'complete',
193
+ archived: 'archived',
194
+ deleted: 'tombstoned',
195
+ cancelled: 'interrupted',
196
+ canceled: 'interrupted',
197
+ failed: 'interrupted',
198
+ interrupted: 'interrupted',
199
+ unknown: 'unknown',
200
+ 'unknown-at-import': 'unknown',
201
+ 'completed-at-import': 'complete',
202
+ };
203
+ return normalized[raw] ?? null;
204
+ }
205
+ function selectLifecycleTransition(leftLifecycle, left, rightLifecycle, right) {
206
+ const leftEvidence = asLifecycleEvidence(left);
207
+ if (leftLifecycle === 'tombstoned') {
208
+ return { lifecycle: leftLifecycle, evidence: leftEvidence ?? right };
209
+ }
210
+ if (!leftEvidence)
211
+ return { lifecycle: rightLifecycle, evidence: right };
212
+ const rank = {
213
+ 'open-provisional-source': 1,
214
+ 'inactivity-threshold': 1,
215
+ 'complete-source': 2,
216
+ 'provider-explicit-event': 3,
217
+ };
218
+ const leftRank = rank[String(leftEvidence.basis)] ?? 0;
219
+ const rightRank = rank[String(right.basis)] ?? 0;
220
+ const rightIsNewer = evidenceTimestamp(right) > evidenceTimestamp(leftEvidence);
221
+ return rightRank > leftRank || (rightRank === leftRank && rightIsNewer)
222
+ ? { lifecycle: rightLifecycle, evidence: right }
223
+ : { lifecycle: leftLifecycle, evidence: leftEvidence };
224
+ }
225
+ function asLifecycleEvidence(value) {
226
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
227
+ ? value
228
+ : null;
229
+ }
230
+ function evidenceTimestamp(value) {
231
+ const parsed = Date.parse(String(value.observedAt ?? ''));
232
+ return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
233
+ }
234
+ function inactivityThresholdMs(request) {
235
+ return request.inactivityThresholdMs ?? DEFAULT_INACTIVITY_THRESHOLD_MS;
236
+ }
237
+ function isStaleHistoricalTimestamp(value, thresholdMs) {
238
+ const timestamp = Date.parse(value);
239
+ return Number.isFinite(timestamp)
240
+ && Date.now() - timestamp > thresholdMs;
241
+ }
242
+ export function sourceGenerationDigest(request) {
243
+ return sha256([
244
+ request.source.sourceId,
245
+ request.source.provider,
246
+ request.selectedSource.locatorClass,
247
+ request.source.sourceSchemaVersion,
248
+ request.adapter.adapterVersion,
249
+ ].join('\0'));
250
+ }
251
+ //# sourceMappingURL=normalization.js.map
@@ -0,0 +1,91 @@
1
+ // Follow a native source through the importer's normalization and checkpoint rules.
2
+ import { setTimeout } from 'node:timers/promises';
3
+ import { SessionContractError, assertSessionResourceLimit, assertSupportedSchemaMajor, sha256 } from './contracts.js';
4
+ import { IncrementalSessionImporter } from './importer.js';
5
+ import { normalizeSessionBatch } from './normalization.js';
6
+ import { assertImportByteLimit, DEFAULT_IMPORT_BYTE_LIMITS } from './import-limits.js';
7
+ export function assertTailSupported(adapter) {
8
+ if (typeof adapter.stream !== 'function' || !adapter.supportedOperations.includes('stream')) {
9
+ throw new SessionContractError('UNSUPPORTED_PROVIDER', `sessions tail is unsupported for provider ${adapter.provider}: adapter has no stream operation`);
10
+ }
11
+ }
12
+ export async function tailSession(options) {
13
+ assertTailSupported(options.adapter);
14
+ assertSupportedSchemaMajor(options.source.sourceSchemaVersion, options.adapter.sourceSchemaMajor ?? 1);
15
+ if (options.workspaceId !== options.selectedSource.authorizedScope.workspaceId) {
16
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'tail workspace is outside the authorized source scope');
17
+ }
18
+ const pollMs = options.pollMs ?? 500;
19
+ if (!Number.isInteger(pollMs) || pollMs < 1 || pollMs > 500) {
20
+ throw new SessionContractError('INVALID_ARGUMENT', 'tail poll interval must be between 1 and 500 ms');
21
+ }
22
+ let checkpoint = options.repository.getCheckpoint(options.source.sourceId, options.adapter.adapterVersion);
23
+ const port = {
24
+ getCheckpoint: () => checkpoint,
25
+ getBatchCheckpoint: () => null,
26
+ commitStagedImports: () => 0,
27
+ applyImport: (batch, next) => {
28
+ // Publish through the existing import transaction only with explicit opt-in.
29
+ const receipt = options.repository.applyImport(batch, next, true);
30
+ checkpoint = { ...next };
31
+ for (const event of batch.events) {
32
+ if (event.sessionId === options.sessionId)
33
+ options.onEvent(event);
34
+ }
35
+ return receipt;
36
+ },
37
+ };
38
+ const importer = new IncrementalSessionImporter(port);
39
+ while (!options.signal?.aborted) {
40
+ try {
41
+ if (options.import) {
42
+ await importer.import({
43
+ ...options, publish: true, batchRunId: undefined,
44
+ limits: { ...options.limits, batchSize: 1 },
45
+ });
46
+ }
47
+ else {
48
+ // Do not fingerprint or persist snapshots: export adapters may rewrite
49
+ // a JSON document as it grows. Each adapter owns its resume semantics.
50
+ for await (const record of options.adapter.stream(options.selectedSource, checkpoint ? { value: checkpoint.cursor } : undefined)) {
51
+ if (options.signal?.aborted)
52
+ return;
53
+ const serialized = JSON.stringify(record);
54
+ const limits = { ...DEFAULT_IMPORT_BYTE_LIMITS, ...options.limits };
55
+ assertImportByteLimit('maxRecordBytes', limits, Buffer.byteLength(serialized));
56
+ const count = (checkpoint?.recordsRead ?? 0) + 1;
57
+ assertSessionResourceLimit('maxRecords', options.limits?.maxRecords ?? 1_000_000, count);
58
+ const digest = sha256(serialized);
59
+ const runId = sha256([options.source.sourceId, options.adapter.adapterVersion,
60
+ count - 1, count, digest].join('\0'));
61
+ const { events } = normalizeSessionBatch(options, [record], [digest], runId);
62
+ checkpoint = { ...checkpoint, cursor: record.sourceCursor ?? String(count),
63
+ recordsRead: count, bytesRead: (checkpoint?.bytesRead ?? 0) + Buffer.byteLength(serialized) };
64
+ for (const event of events) {
65
+ if (event.sessionId === options.sessionId)
66
+ options.onEvent(event);
67
+ }
68
+ }
69
+ }
70
+ }
71
+ catch (error) {
72
+ if (options.signal?.aborted)
73
+ return;
74
+ // Some native JSONL adapters report an in-progress final line explicitly.
75
+ if (!(options.follow && error instanceof SessionContractError
76
+ && error.code === 'TRUNCATED_SOURCE'))
77
+ throw error;
78
+ }
79
+ await options.onPoll?.();
80
+ if (!options.follow || options.signal?.aborted)
81
+ return;
82
+ try {
83
+ await setTimeout(pollMs, undefined, { signal: options.signal });
84
+ }
85
+ catch (error) {
86
+ if (!options.signal?.aborted)
87
+ throw error;
88
+ }
89
+ }
90
+ }
91
+ //# sourceMappingURL=tail.js.map
@@ -4,6 +4,8 @@ import { basename, dirname, join, resolve } from 'node:path';
4
4
  import { inspectMuseNativeWorkspace } from './adapters/muse.js';
5
5
  import { readOmpSessionHeader } from './adapters/omp.js';
6
6
  import { grokbotConversationIdFromLocator } from './adapters/grokbot-locator.js';
7
+ import { GROK_BUILD_ACTIVE_FRESHNESS_MS, grokBuildLiveState, grokBuildSourceWorkspaceMatch, readGrokBuildActiveRegistry, } from './adapters/grok-build-native.js';
8
+ import { hermesSnapshotStagingDir } from './adapters/hermes-state-db.js';
7
9
  import { resolveOmpPaths } from '../providers/omp-paths.mjs';
8
10
  import { readAiwgConfig } from '../config/aiwg-config.js';
9
11
  import { SessionCoveragePreflightSchema, SessionContractError, sha256, } from './contracts.js';
@@ -15,7 +17,7 @@ import { matchSessionWorkspace } from './workspace-matching.js';
15
17
  import { matchImportExclusion, nativeSessionIdFromLocator, resolveImportExclusions } from './import-exclusions.js';
16
18
  export const DISCOVERY_MANIFEST_VERSION = '1.0.0';
17
19
  const MANUAL_EXPORT_PROVIDERS = new Set([
18
- 'copilot', 'hermes', 'opencode', 'openclaw', 'openhuman', 'grok-build',
20
+ 'copilot', 'opencode', 'openclaw', 'openhuman',
19
21
  'warp', 'devin-desktop', 'generic',
20
22
  ]);
21
23
  export async function discoverWorkspaceHistories(options) {
@@ -31,6 +33,8 @@ export async function discoverWorkspaceHistories(options) {
31
33
  ...(options.museRoot ? ['muse'] : []),
32
34
  ...(options.dshRoot ? ['deepseek-harness'] : []),
33
35
  ...(options.grokbotRoot ? ['grokbot'] : []),
36
+ ...(options.grokRoot ? ['grok-build'] : []),
37
+ ...(options.hermesRoot ? ['hermes'] : []),
34
38
  ];
35
39
  const savedRoots = options.authorizedRoots ?? options.refreshFrom?.authorizedRoots;
36
40
  const authorizedProviders = options.refreshFrom
@@ -68,6 +72,15 @@ export async function discoverWorkspaceHistories(options) {
68
72
  // no-filtering behavior already used for `pi`/`deepseek-harness`).
69
73
  { provider: 'grokbot', roots: options.grokbotRoot
70
74
  ? [resolve(options.grokbotRoot)] : [] },
75
+ // Grok Build authorizes the sessions directory itself, never Grok home:
76
+ // `<root>/<url-encoded cwd>/<id>/chat_history.jsonl`. No fallback through
77
+ // $HOME, GROK_HOME, or --provider-home grants access.
78
+ { provider: 'grok-build', roots: options.grokRoot
79
+ ? [resolve(options.grokRoot)] : [] },
80
+ // Hermes authorizes `<root>/state.db` for a read-only backup snapshot.
81
+ // Staging joins the scope below; nothing else under the root is read.
82
+ { provider: 'hermes', roots: options.hermesRoot
83
+ ? [resolve(options.hermesRoot)] : [] },
71
84
  {
72
85
  provider: 'claude',
73
86
  // Same scope rule as codex: configured providers keep their root grant
@@ -155,14 +168,17 @@ export async function discoverWorkspaceHistories(options) {
155
168
  }
156
169
  }
157
170
  if (availableRoots.length === 0) {
158
- const codexNeedsAuthorization = (entry.provider === 'codex' || entry.provider === 'omp'
171
+ const needsExplicitRoot = (entry.provider === 'codex' || entry.provider === 'omp'
159
172
  || entry.provider === 'deepseek-harness' || entry.provider === 'muse'
160
- || entry.provider === 'grokbot')
173
+ || entry.provider === 'grokbot' || entry.provider === 'grok-build'
174
+ || entry.provider === 'hermes')
161
175
  && entry.roots.length === 0;
162
- reports.set(entry.provider, providerReport(entry.provider, codexNeedsAuthorization ? 'export-required' : 'unavailable', codexNeedsAuthorization ? 'manual-export' : 'discoverable', [], codexNeedsAuthorization
176
+ const expectedFlag = entry.provider === 'deepseek-harness' ? 'dsh' : entry.provider;
177
+ const rootKind = entry.provider === 'hermes' ? 'Hermes home (state.db is snapshotted read-only)' : 'sessions root';
178
+ reports.set(entry.provider, providerReport(entry.provider, needsExplicitRoot ? 'export-required' : 'unavailable', needsExplicitRoot ? 'manual-export' : 'discoverable', [], needsExplicitRoot
163
179
  ? 'SHARED_ROOT_AUTHORIZATION_REQUIRED'
164
- : 'PROVIDER_ROOT_UNAVAILABLE', codexNeedsAuthorization
165
- ? `Pass --${entry.provider === 'deepseek-harness' ? 'dsh' : entry.provider}-root with an explicitly authorized sessions root.`
180
+ : 'PROVIDER_ROOT_UNAVAILABLE', needsExplicitRoot
181
+ ? `Pass --${expectedFlag}-root with an explicitly authorized ${rootKind}.`
166
182
  : `No authorized ${entry.provider} workspace history root was found.`));
167
183
  if (entry.provider === 'codex')
168
184
  reports.get(entry.provider).rootCounts = [...rootCounts.values()];
@@ -173,13 +189,48 @@ export async function discoverWorkspaceHistories(options) {
173
189
  workspaceId,
174
190
  allowedRoots: availableRoots,
175
191
  };
192
+ if (entry.provider === 'hermes') {
193
+ // The consistent snapshot is staged under owned workspace staging (the
194
+ // provider home stays read-only); staged locators need scope cover, and
195
+ // refresh must retain both the origin grant and the staging grant.
196
+ const staging = hermesSnapshotStagingDir(workspacePath);
197
+ if (!scope.allowedRoots.includes(staging))
198
+ scope.allowedRoots.push(staging);
199
+ authorizedRoots[entry.provider] = [...new Set([...availableRoots, staging])];
200
+ if (!rootCounts.has(staging)) {
201
+ rootCounts.set(staging, {
202
+ root: redactSourceLocator(staging), rootId: sha256(staging),
203
+ status: 'checked', sourceCount: 0, duplicateCount: 0,
204
+ });
205
+ }
206
+ }
176
207
  const providerSources = [];
177
208
  let matchedBySubfolder = 0;
178
209
  let matchedByWorktree = 0;
179
210
  const museSourceInspections = [];
211
+ // Grok Build live detection reads exactly one explicitly authorized file
212
+ // (never the sessions root's parent implicitly); absence means unknown.
213
+ let grokActiveRegistry = null;
214
+ let grokActiveRegistryLoaded = false;
215
+ const grokActiveEvidence = async () => {
216
+ if (grokActiveRegistryLoaded || entry.provider !== 'grok-build' || !options.grokActiveSessions)
217
+ return grokActiveRegistry;
218
+ grokActiveRegistryLoaded = true;
219
+ try {
220
+ grokActiveRegistry = await readGrokBuildActiveRegistry(resolve(options.grokActiveSessions), [resolve(options.grokActiveSessions)].map((file) => dirname(file)));
221
+ }
222
+ catch {
223
+ grokActiveRegistry = null;
224
+ }
225
+ return grokActiveRegistry;
226
+ };
180
227
  for await (const descriptor of adapter.discover(scope)) {
181
228
  const known = options.knownSources?.find(source => source.provider === descriptor.provider && source.locator === descriptor.locator);
182
- const nativeSessionId = known?.nativeSessionId ?? descriptor.nativeSessionId ?? nativeSessionIdFromLocator(descriptor);
229
+ // A Hermes projection holds many sessions, so its locator basename is
230
+ // never a session identity.
231
+ const nativeSessionId = entry.provider === 'hermes'
232
+ ? (known?.nativeSessionId ?? descriptor.nativeSessionId)
233
+ : (known?.nativeSessionId ?? descriptor.nativeSessionId ?? nativeSessionIdFromLocator(descriptor));
183
234
  const exclusion = matchImportExclusion({ ...descriptor, nativeSessionId }, exclusions);
184
235
  if (exclusion) {
185
236
  const locator = resolve(descriptor.locator);
@@ -224,6 +275,13 @@ export async function discoverWorkspaceHistories(options) {
224
275
  if (!workspaceMatch)
225
276
  continue;
226
277
  }
278
+ if (entry.provider === 'grok-build') {
279
+ // The url-encoded cwd directory is the only workspace evidence; an
280
+ // unresolvable or conflicting value never attaches to the caller.
281
+ workspaceMatch = await grokBuildSourceWorkspaceMatch(locator, workspacePath);
282
+ if (!workspaceMatch)
283
+ continue;
284
+ }
227
285
  const ompHeader = entry.provider === 'omp' ? await readOmpSessionHeader({ ...descriptor, sourceId: 'discovery', authorizedScope: scope }) : undefined;
228
286
  if (ompHeader) {
229
287
  workspaceMatch = await matchSessionWorkspace(ompHeader.cwd, workspacePath);
@@ -262,6 +320,24 @@ export async function discoverWorkspaceHistories(options) {
262
320
  rootCount.sourceCount += 1;
263
321
  const source = sourceFromDescriptor(descriptor, locator, authorizedRoot, details, fingerprint.digest);
264
322
  source.nativeSessionId = rolloutId ?? ompHeader?.id ?? nativeSessionId;
323
+ if (entry.provider === 'hermes') {
324
+ // A state.db projection holds many sessions; the locator basename is
325
+ // not a session identity.
326
+ delete source.nativeSessionId;
327
+ }
328
+ if (entry.provider === 'grok-build' && source.nativeSessionId) {
329
+ // Provider-reported activity only; histories stay usable without it.
330
+ const observedAt = options.createdAt ?? new Date().toISOString();
331
+ const registry = await grokActiveEvidence();
332
+ const evidence = grokBuildLiveState(registry, source.nativeSessionId);
333
+ source.liveEvidence = {
334
+ ...evidence,
335
+ observedAt,
336
+ expiresAt: evidence.state === 'active'
337
+ ? new Date(Date.parse(observedAt) + GROK_BUILD_ACTIVE_FRESHNESS_MS).toISOString()
338
+ : null,
339
+ };
340
+ }
265
341
  if (ompHeader)
266
342
  source.sourceId = sha256(['omp-native-source-v1', workspaceId, ompHeader.id].join('\0'));
267
343
  if (entry.provider === 'grokbot') {
@@ -0,0 +1,28 @@
1
+ // Preserve non-portable skill metadata across the portable boundary.
2
+ import { AGENT_SKILLS_SIDECAR_SCHEMA } from './agent-skills.js';
3
+ export function createAgentSkillSidecar(document, provenance, validationProfile, trust) {
4
+ return {
5
+ $schema: AGENT_SKILLS_SIDECAR_SCHEMA,
6
+ schemaVersion: 1,
7
+ aiwg: structuredClone(document.aiwg),
8
+ ...(document.providers ? { providers: structuredClone(document.providers) } : {}),
9
+ provenance: structuredClone(provenance),
10
+ validationProfile,
11
+ trust: { ...trust },
12
+ };
13
+ }
14
+ /**
15
+ * Restore AIWG metadata omitted at a strict portability boundary. Portable
16
+ * content remains authoritative for standard fields, body, and resources.
17
+ */
18
+ export function restoreAgentSkillFromSidecar(standard, body, resources, sidecar) {
19
+ return {
20
+ standard: structuredClone(standard),
21
+ body,
22
+ resources: resources.map((resource) => ({ ...resource })),
23
+ aiwg: structuredClone(sidecar.aiwg),
24
+ ...(sidecar.providers ? { providers: structuredClone(sidecar.providers) } : {}),
25
+ unknownFields: [],
26
+ };
27
+ }
28
+ //# sourceMappingURL=agent-skill-sidecar.js.map
@@ -6,6 +6,9 @@
6
6
  *
7
7
  * @implements #1875
8
8
  */
9
+ import { CLAUDE_SKILL_FIELDS } from './provider-extensions.js';
10
+ export { CLAUDE_SKILL_FIELDS } from './provider-extensions.js';
11
+ export { createAgentSkillSidecar, restoreAgentSkillFromSidecar } from './agent-skill-sidecar.js';
9
12
  export const AGENT_SKILLS_BASELINE = {
10
13
  repository: 'agentskills/agentskills',
11
14
  revision: '38a2ff82958afee88dadf4831509e6f7e9d8ef4e',
@@ -174,6 +177,7 @@ export function validateCompatibleAgentSkillMetadata(frontmatter, directoryName,
174
177
  const recognizedFields = new Set([
175
178
  ...STANDARD_SKILL_FIELDS,
176
179
  ...AIWG_SKILL_CONTROL_FIELDS,
180
+ ...CLAUDE_SKILL_FIELDS,
177
181
  ]);
178
182
  for (const key of Object.keys(frontmatter).sort()) {
179
183
  if (!recognizedFields.has(key)) {
@@ -223,27 +227,4 @@ export function equivalentPortableAllowedTools(aiwg) {
223
227
  }
224
228
  return aiwg.allowedTools.join(' ');
225
229
  }
226
- export function createAgentSkillSidecar(document, provenance, validationProfile, trust) {
227
- return {
228
- $schema: AGENT_SKILLS_SIDECAR_SCHEMA,
229
- schemaVersion: 1,
230
- aiwg: structuredClone(document.aiwg),
231
- provenance: structuredClone(provenance),
232
- validationProfile,
233
- trust: { ...trust },
234
- };
235
- }
236
- /**
237
- * Restore AIWG metadata omitted at a strict portability boundary. Portable
238
- * content remains authoritative for standard fields, body, and resources.
239
- */
240
- export function restoreAgentSkillFromSidecar(standard, body, resources, sidecar) {
241
- return {
242
- standard: structuredClone(standard),
243
- body,
244
- resources: resources.map((resource) => ({ ...resource })),
245
- aiwg: structuredClone(sidecar.aiwg),
246
- unknownFields: [],
247
- };
248
- }
249
230
  //# sourceMappingURL=agent-skills.js.map
@@ -0,0 +1,25 @@
1
+ /** Inspection only: account-enabled skills are a separate delivery mechanism. */
2
+ export function projectClaudeSkillSurfaces(body) {
3
+ const shell = /!`[^`]+`/.test(body);
4
+ return ['cli', 'desktop-code', 'cowork', 'cloud'].map((surface) => {
5
+ const local = surface === 'cli' || surface === 'desktop-code';
6
+ const shellStatus = local ? 'native' : surface === 'cowork' ? 'degraded' : 'unsupported';
7
+ return {
8
+ provider: 'claude',
9
+ surface,
10
+ status: local ? 'native' : 'unsupported',
11
+ ...(shell ? { shellInterpolation: shellStatus } : {}),
12
+ reasons: [
13
+ local
14
+ ? 'Reads filesystem SKILL.md bundles from .claude/skills.'
15
+ : 'Does not read local .claude/skills; requires skills enabled on the claude.ai account.',
16
+ ...(shell ? [local
17
+ ? '! shell interpolation is native; AIWG does not execute it during validation or deploy.'
18
+ : surface === 'cowork'
19
+ ? '! shell interpolation is degraded to a policy notice for account-enabled Cowork skills.'
20
+ : '! shell interpolation from a local skill is unsupported on cloud.'] : []),
21
+ ],
22
+ };
23
+ });
24
+ }
25
+ //# sourceMappingURL=claude-surface-projection.js.map