@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.
- package/agentic/code/providers/model-catalog.v1.json +62 -12
- package/dist/src/artifacts/binding-target-identity.js +51 -0
- package/dist/src/artifacts/checksum-manifest.js +12 -0
- package/dist/src/artifacts/index-builder.js +11 -13
- package/dist/src/artifacts/query-engine.js +5 -11
- package/dist/src/cli/handlers/doctor-help.js +32 -0
- package/dist/src/cli/handlers/gates.js +19 -18
- package/dist/src/cli/handlers/models-help.js +7 -1
- package/dist/src/cli/handlers/models.js +3 -3
- package/dist/src/cli/handlers/script-runner.js +8 -5
- package/dist/src/cli/handlers/sessions-intake.js +104 -0
- package/dist/src/cli/handlers/sessions-tail.js +172 -0
- package/dist/src/cli/handlers/sessions.js +90 -17
- package/dist/src/cli/handlers/skill-lint.js +10 -3
- package/dist/src/cli/handlers/steward-format.js +100 -0
- package/dist/src/cli/handlers/steward-models.js +167 -0
- package/dist/src/cli/handlers/steward.js +14 -248
- package/dist/src/cli/handlers/use-bundle-counts.js +61 -0
- package/dist/src/cli/handlers/use.js +6 -24
- package/dist/src/cli/handlers/utilities.js +1 -24
- package/dist/src/config/aiwg-config.js +7 -0
- package/dist/src/config/authorization-cli.js +89 -0
- package/dist/src/config/cli-help.js +1 -0
- package/dist/src/config/cli.js +10 -1
- package/dist/src/decision/context-pruning.js +3 -2
- package/dist/src/decision/ensemble/contract.js +15 -4
- package/dist/src/decision/ensemble-study/artifacts.js +5 -0
- package/dist/src/decision/ensemble-study/score.js +4 -3
- package/dist/src/decision/feature-export.js +3 -9
- package/dist/src/decision/heldout/collector.js +2 -2
- package/dist/src/decision/issue-triage/runtime.js +3 -9
- package/dist/src/decision/qualification/gates.js +2 -1
- package/dist/src/decision/qualification/release.js +2 -3
- package/dist/src/decision/routing/report.js +2 -0
- package/dist/src/decision/sdlc-screening.js +3 -5
- package/dist/src/decision/sensitivity/comparative.js +5 -3
- package/dist/src/gates/driver.js +6 -77
- package/dist/src/gates/evaluate.js +4 -11
- package/dist/src/gates/floors.js +4 -4
- package/dist/src/gates/index.js +3 -3
- package/dist/src/gates/lattice.js +26 -0
- package/dist/src/gates/registry.js +2 -1
- package/dist/src/gates/report.js +19 -7
- package/dist/src/issues/audit-cli.js +120 -0
- package/dist/src/issues/audit-contract.js +64 -0
- package/dist/src/issues/audit-local.js +82 -0
- package/dist/src/issues/audit.js +220 -0
- package/dist/src/lint/rule-activation.js +15 -1
- package/dist/src/mission-protocol/canonical-codecs.js +270 -0
- package/dist/src/mission-protocol/codecs.js +22 -550
- package/dist/src/mission-protocol/health-trace-codecs.js +281 -0
- package/dist/src/mission-protocol/health-trace-types.js +43 -0
- package/dist/src/mission-protocol/types.js +2 -42
- package/dist/src/mission-protocol/worker-contract-json.js +10 -0
- package/dist/src/mission-protocol/worker-review-gate.js +100 -0
- package/dist/src/mission-protocol/worker-review-types.js +2 -0
- package/dist/src/models/catalog-policy.js +280 -0
- package/dist/src/models/index.js +1 -1
- package/dist/src/models/model-catalog.v1.json +62 -12
- package/dist/src/models/policy-resources.js +28 -0
- package/dist/src/models/provider-policy.js +10 -546
- package/dist/src/models/resolver.js +26 -8
- package/dist/src/models/router.js +15 -2
- package/dist/src/models/tiers.js +13 -0
- package/dist/src/models/types.js +0 -6
- package/dist/src/models/worker-ceilings.js +59 -0
- package/dist/src/models/worker-invocation-policy.js +243 -0
- package/dist/src/models/worker-plan.js +16 -20
- package/dist/src/models/worker-review-lineage.js +55 -0
- package/dist/src/models/worker-selection-policy.js +1 -1
- package/dist/src/models/wrapper-route.js +6 -3
- package/dist/src/policy/authorization.js +1 -0
- package/dist/src/policy/hitl-config.js +63 -0
- package/dist/src/policy/hitl-receipt.js +57 -0
- package/dist/src/policy/hitl-types.js +4 -0
- package/dist/src/policy/hitl-validation.js +39 -0
- package/dist/src/policy/hitl.js +117 -0
- package/dist/src/rules/activation.d.mts +1 -1
- package/dist/src/rules/activation.mjs +0 -2
- package/dist/src/rules/gate-coverage.js +51 -0
- package/dist/src/rules/gate-references.js +16 -0
- package/dist/src/rules/rule-frontmatter.schema.json +7 -1
- package/dist/src/serve/worker-command-request.js +21 -4
- package/dist/src/serve/worker-invocation-preparation.js +3 -1
- package/dist/src/serve/worker-invocation.js +21 -10
- package/dist/src/sessions/adapters/deepseek-harness.js +1 -0
- package/dist/src/sessions/adapters/grok-build-native.js +269 -0
- package/dist/src/sessions/adapters/grok-build.js +57 -15
- package/dist/src/sessions/adapters/hermes-state-db.js +252 -0
- package/dist/src/sessions/adapters/hermes.js +42 -3
- package/dist/src/sessions/adapters/warp.js +1 -1
- package/dist/src/sessions/batch-import.js +10 -0
- package/dist/src/sessions/contracts.js +1 -1
- package/dist/src/sessions/discovery-capability.js +38 -0
- package/dist/src/sessions/importer.js +8 -251
- package/dist/src/sessions/index.js +4 -0
- package/dist/src/sessions/intake-dir.js +71 -0
- package/dist/src/sessions/normalization.js +251 -0
- package/dist/src/sessions/tail.js +91 -0
- package/dist/src/sessions/workspace-discovery.js +83 -7
- package/dist/src/skills/agent-skill-sidecar.js +28 -0
- package/dist/src/skills/agent-skills.js +4 -23
- package/dist/src/skills/claude-surface-projection.js +25 -0
- package/dist/src/skills/deployer.js +8 -288
- package/dist/src/skills/deployment-policy.js +114 -0
- package/dist/src/skills/deployment-projection.js +152 -0
- package/dist/src/skills/deployment-types.js +20 -0
- package/dist/src/skills/exporter.js +8 -31
- package/dist/src/skills/provider-extensions.js +41 -0
- package/dist/src/skills/run.js +6 -0
- package/dist/src/skills/types.js +0 -8
- package/dist/src/skills/validator-resources.js +68 -0
- package/dist/src/skills/validator.js +6 -55
- package/dist/src/storage/cli-migrate-options.js +125 -0
- package/dist/src/storage/cli-migrate.js +111 -0
- package/dist/src/storage/cli.js +4 -279
- package/dist/src/storage/migration-adapter.js +79 -0
- package/dist/src/storage/migration-copy.js +108 -0
- package/dist/src/storage/migration-protocol.js +14 -197
- package/dist/src/storage/migration-records.js +186 -0
- package/dist/src/storage/migration-types.js +12 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/tracker/issue-pages.js +110 -0
- package/package.json +1 -1
- package/schemas/mission-v1.schema.json +335 -0
- package/schemas/models/model-catalog.v1.schema.json +19 -1
- package/schemas/models/model-config-compatibility.v1.schema.json +3 -2
- package/schemas/models/model-policy.v1.schema.json +1 -1
- package/tools/agents/providers/provider-paths-staging.mjs +35 -0
- package/tools/agents/providers/rule-binding-targets.mjs +146 -0
- package/tools/agents/providers/rule-delivery.mjs +7 -126
- 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', '
|
|
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
|
|
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
|
-
|
|
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',
|
|
165
|
-
? `Pass --${
|
|
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
|
-
|
|
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
|