@aiwg/cli 2026.7.20 → 2026.7.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/browser-export.js +7 -0
- package/dist/src/artifacts/citation-parser.js +96 -35
- package/dist/src/artifacts/index-builder.js +54 -17
- package/dist/src/artifacts/state-transfer.js +27 -0
- package/dist/src/artifacts/stats.js +8 -0
- package/dist/src/cli/cli-extension-loader.js +73 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/sessions.js +966 -0
- package/dist/src/cli/handlers/skill-lint.js +49 -45
- package/dist/src/cli/handlers/use.js +143 -60
- package/dist/src/cli/handlers/utilities.js +22 -8
- package/dist/src/cli/skill-usage.js +146 -24
- package/dist/src/extensions/commands/definitions.js +29 -0
- package/dist/src/extensions/manifest.js +29 -0
- package/dist/src/sessions/adapters/claude.js +357 -0
- package/dist/src/sessions/adapters/codex.js +521 -0
- package/dist/src/sessions/adapters/copilot.js +226 -0
- package/dist/src/sessions/adapters/cursor.js +372 -0
- package/dist/src/sessions/adapters/factory.js +345 -0
- package/dist/src/sessions/adapters/generic.js +225 -0
- package/dist/src/sessions/adapters/hermes.js +341 -0
- package/dist/src/sessions/adapters/openclaw.js +381 -0
- package/dist/src/sessions/adapters/opencode.js +454 -0
- package/dist/src/sessions/adapters/openhuman.js +315 -0
- package/dist/src/sessions/adapters/warp.js +160 -0
- package/dist/src/sessions/adapters/windsurf.js +212 -0
- package/dist/src/sessions/candidates.js +210 -0
- package/dist/src/sessions/contracts.js +310 -0
- package/dist/src/sessions/discovery.js +51 -0
- package/dist/src/sessions/fixtures.js +12 -0
- package/dist/src/sessions/importer.js +315 -0
- package/dist/src/sessions/index.js +25 -0
- package/dist/src/sessions/knowledge-shard.js +61 -0
- package/dist/src/sessions/optional-backends.js +238 -0
- package/dist/src/sessions/policy.js +192 -0
- package/dist/src/sessions/ports.js +2 -0
- package/dist/src/sessions/promotion.js +367 -0
- package/dist/src/sessions/readers.js +176 -0
- package/dist/src/sessions/repository.js +1551 -0
- package/dist/src/skills/adapters/agent-skills.js +59 -0
- package/dist/src/skills/adapters/local.js +19 -1
- package/dist/src/skills/agent-skills.js +249 -0
- package/dist/src/skills/cli.js +463 -7
- package/dist/src/skills/deployer.js +554 -0
- package/dist/src/skills/doctor.js +105 -0
- package/dist/src/skills/exporter.js +382 -0
- package/dist/src/skills/importer.js +921 -0
- package/dist/src/skills/registry.js +19 -0
- package/dist/src/skills/validator.js +323 -0
- package/package.json +2 -2
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { IntelligenceCandidateSchema, IntelligenceCandidateTypeSchema, SESSION_CONTRACT_VERSION, SessionContractError, sha256, } from './contracts.js';
|
|
3
|
+
export const ExtractedCandidateDraftSchema = z.object({
|
|
4
|
+
type: IntelligenceCandidateTypeSchema,
|
|
5
|
+
assertion: z.string().min(1),
|
|
6
|
+
subject: z.string().min(1).nullable(),
|
|
7
|
+
predicate: z.string().min(1).nullable(),
|
|
8
|
+
object: z.string().min(1).nullable(),
|
|
9
|
+
evidence: z.array(z.object({
|
|
10
|
+
eventId: z.string().min(1),
|
|
11
|
+
start: z.number().int().nonnegative(),
|
|
12
|
+
end: z.number().int().positive(),
|
|
13
|
+
}).strict()).min(1),
|
|
14
|
+
confidence: z.number().min(0).max(1),
|
|
15
|
+
conflictsWith: z.array(z.string().min(1)).default([]),
|
|
16
|
+
supersedes: z.array(z.string().min(1)).default([]),
|
|
17
|
+
}).strict().superRefine((candidate, context) => {
|
|
18
|
+
if (candidate.type === 'relationship'
|
|
19
|
+
&& (!candidate.subject || !candidate.predicate || !candidate.object)) {
|
|
20
|
+
context.addIssue({
|
|
21
|
+
code: z.ZodIssueCode.custom,
|
|
22
|
+
message: 'relationship candidates require subject, predicate, and object',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
export class CandidateExtractionService {
|
|
27
|
+
store;
|
|
28
|
+
constructor(store) {
|
|
29
|
+
this.store = store;
|
|
30
|
+
}
|
|
31
|
+
async extract(input) {
|
|
32
|
+
const evidenceById = new Map(input.documents.map((document) => [document.eventId, document]));
|
|
33
|
+
const raw = await input.extractor.extract(input.documents.map((document) => Object.freeze({
|
|
34
|
+
eventId: document.eventId,
|
|
35
|
+
text: document.searchableText,
|
|
36
|
+
role: document.role,
|
|
37
|
+
})));
|
|
38
|
+
const drafts = z.array(ExtractedCandidateDraftSchema).parse(raw);
|
|
39
|
+
const candidates = drafts
|
|
40
|
+
.filter((draft) => draft.confidence >= input.policy.minimumConfidence)
|
|
41
|
+
.map((draft) => {
|
|
42
|
+
const evidence = draft.evidence.map((span) => {
|
|
43
|
+
const document = evidenceById.get(span.eventId);
|
|
44
|
+
if (!document) {
|
|
45
|
+
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'candidate cites evidence outside the authorized extraction scope');
|
|
46
|
+
}
|
|
47
|
+
if (span.start >= span.end || span.end > document.searchableText.length) {
|
|
48
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'candidate evidence span is invalid');
|
|
49
|
+
}
|
|
50
|
+
const quote = document.searchableText.slice(span.start, span.end);
|
|
51
|
+
if (!quote.trim()) {
|
|
52
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'candidate evidence span must contain redacted source text');
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
...span,
|
|
56
|
+
quoteDigest: sha256(quote),
|
|
57
|
+
quote,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
const sensitivity = evidence.some((span) => evidenceById.get(span.eventId)?.sensitivity === 'sensitive') ? 'sensitive' : 'none';
|
|
61
|
+
if (!evidence.some((span) => evidenceSupportsAssertion(draft.assertion, span.quote))) {
|
|
62
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'candidate assertion is not supported by its cited redacted evidence span');
|
|
63
|
+
}
|
|
64
|
+
const security = classifyCandidateSecurity({
|
|
65
|
+
assertion: draft.assertion,
|
|
66
|
+
subject: draft.subject,
|
|
67
|
+
predicate: draft.predicate,
|
|
68
|
+
object: draft.object,
|
|
69
|
+
});
|
|
70
|
+
return IntelligenceCandidateSchema.parse({
|
|
71
|
+
contractVersion: SESSION_CONTRACT_VERSION,
|
|
72
|
+
candidateId: stableCandidateId(draft),
|
|
73
|
+
version: 1,
|
|
74
|
+
type: draft.type,
|
|
75
|
+
assertion: draft.assertion,
|
|
76
|
+
subject: draft.subject,
|
|
77
|
+
predicate: draft.predicate,
|
|
78
|
+
object: draft.object,
|
|
79
|
+
evidence,
|
|
80
|
+
confidence: draft.confidence,
|
|
81
|
+
temporalScope: input.policy.temporalScope,
|
|
82
|
+
projectScope: input.policy.projectScope,
|
|
83
|
+
extractionMethod: input.extractor.method,
|
|
84
|
+
extractionVersion: input.extractor.version,
|
|
85
|
+
extractionPolicyVersion: input.policy.version,
|
|
86
|
+
model: input.extractor.model,
|
|
87
|
+
sensitivity,
|
|
88
|
+
security,
|
|
89
|
+
reviewState: 'pending',
|
|
90
|
+
conflictsWith: draft.conflictsWith,
|
|
91
|
+
supersedes: draft.supersedes,
|
|
92
|
+
createdAt: new Date().toISOString(),
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
return this.store.saveCandidates(candidates);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export class StructuralCandidateExtractor {
|
|
99
|
+
method = 'structural-labels';
|
|
100
|
+
version = '1.0.0';
|
|
101
|
+
model = null;
|
|
102
|
+
extract(evidence) {
|
|
103
|
+
const drafts = [];
|
|
104
|
+
for (const item of evidence) {
|
|
105
|
+
let offset = 0;
|
|
106
|
+
for (const line of item.text.split(/\n/)) {
|
|
107
|
+
const match = STRUCTURAL_PATTERN.exec(line);
|
|
108
|
+
if (match) {
|
|
109
|
+
const label = match[1].toLowerCase();
|
|
110
|
+
const assertion = match[2].trim();
|
|
111
|
+
const start = offset + line.indexOf(assertion);
|
|
112
|
+
const type = STRUCTURAL_TYPES[label];
|
|
113
|
+
const relationship = type === 'relationship'
|
|
114
|
+
? parseRelationship(assertion)
|
|
115
|
+
: { subject: null, predicate: null, object: null };
|
|
116
|
+
if (type && (type !== 'relationship' || relationship.subject)) {
|
|
117
|
+
drafts.push({
|
|
118
|
+
type,
|
|
119
|
+
assertion,
|
|
120
|
+
...relationship,
|
|
121
|
+
evidence: [{ eventId: item.eventId, start, end: start + assertion.length }],
|
|
122
|
+
confidence: 0.8,
|
|
123
|
+
conflictsWith: [],
|
|
124
|
+
supersedes: [],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
offset += line.length + 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return drafts;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const SECURITY_POLICY_VERSION = '1.0.0';
|
|
135
|
+
const INSTRUCTION_PATTERN = /\b(?:ignore|override|disregard)\b.{0,40}\b(?:instruction|prompt|policy|rule)s?\b|\b(?:system|developer)\s+(?:message|instruction|prompt)\b|\b(?:execute|invoke|run)\b.{0,30}\b(?:tool|command|shell|script)\b/i;
|
|
136
|
+
const SECRET_PATTERN = /\b(?:api[_-]?key|access[_-]?token|password|passwd|private[_-]?key|authorization|cookie|secret)\b\s*(?:[:=]|\bis\b)/i;
|
|
137
|
+
const STRUCTURE_PATTERN = /(?:^|\n)\s*(?:---|\.\.\.)\s*(?:\n|$)|```|~~~|<[/!?A-Za-z]|!\[[^\]]*\]\(|\[[^\]]+\]\([^)]*\)|\{\{|\{%/;
|
|
138
|
+
const CONTROL_PATTERN = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/;
|
|
139
|
+
const BIDI_PATTERN = /[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/u;
|
|
140
|
+
const LATIN_PATTERN = /\p{Script=Latin}/u;
|
|
141
|
+
const CONFUSABLE_SCRIPT_PATTERN = /[\p{Script=Cyrillic}\p{Script=Greek}]/u;
|
|
142
|
+
export function classifyCandidateSecurity(input) {
|
|
143
|
+
const text = [input.assertion, input.subject, input.predicate, input.object]
|
|
144
|
+
.filter((value) => Boolean(value))
|
|
145
|
+
.join('\n');
|
|
146
|
+
const warnings = [];
|
|
147
|
+
if (INSTRUCTION_PATTERN.test(text))
|
|
148
|
+
warnings.push('instruction-like');
|
|
149
|
+
if (STRUCTURE_PATTERN.test(text))
|
|
150
|
+
warnings.push('structure-breaking');
|
|
151
|
+
if (CONTROL_PATTERN.test(text))
|
|
152
|
+
warnings.push('control-character');
|
|
153
|
+
if (BIDI_PATTERN.test(text))
|
|
154
|
+
warnings.push('bidi-control');
|
|
155
|
+
if (LATIN_PATTERN.test(text) && CONFUSABLE_SCRIPT_PATTERN.test(text)) {
|
|
156
|
+
warnings.push('unicode-confusable');
|
|
157
|
+
}
|
|
158
|
+
if (/(?:javascript|data|vbscript):|<\s*(?:script|iframe|object|embed)\b/i.test(text)) {
|
|
159
|
+
warnings.push('active-content');
|
|
160
|
+
}
|
|
161
|
+
if (SECRET_PATTERN.test(text))
|
|
162
|
+
warnings.push('secret-bearing');
|
|
163
|
+
return {
|
|
164
|
+
disposition: warnings.length === 0 ? 'clear' : 'suspicious',
|
|
165
|
+
warnings: [...new Set(warnings)],
|
|
166
|
+
requiresAcknowledgement: warnings.length > 0,
|
|
167
|
+
acknowledged: false,
|
|
168
|
+
policyVersion: SECURITY_POLICY_VERSION,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function evidenceSupportsAssertion(assertion, quote) {
|
|
172
|
+
const tokens = (value) => new Set(value.normalize('NFKC').toLocaleLowerCase('en-US')
|
|
173
|
+
.match(/[\p{L}\p{N}]{3,}/gu) ?? []);
|
|
174
|
+
const assertionTokens = tokens(assertion);
|
|
175
|
+
const quoteTokens = tokens(quote);
|
|
176
|
+
return [...assertionTokens].some((token) => quoteTokens.has(token));
|
|
177
|
+
}
|
|
178
|
+
const STRUCTURAL_PATTERN = /^(Decision|Requirement|Constraint|Preference|Task|Discovery|Fix|Failed approach|Procedure|Risk|Contradiction|Question|Entity|Relationship):\s*(.+)$/i;
|
|
179
|
+
const STRUCTURAL_TYPES = {
|
|
180
|
+
decision: 'decision',
|
|
181
|
+
requirement: 'requirement',
|
|
182
|
+
constraint: 'constraint',
|
|
183
|
+
preference: 'preference',
|
|
184
|
+
task: 'task',
|
|
185
|
+
discovery: 'discovery',
|
|
186
|
+
fix: 'fix',
|
|
187
|
+
'failed approach': 'failed-approach',
|
|
188
|
+
procedure: 'procedure',
|
|
189
|
+
risk: 'risk',
|
|
190
|
+
contradiction: 'contradiction',
|
|
191
|
+
question: 'question',
|
|
192
|
+
entity: 'entity',
|
|
193
|
+
relationship: 'relationship',
|
|
194
|
+
};
|
|
195
|
+
function parseRelationship(assertion) {
|
|
196
|
+
const parts = assertion.split('|').map((part) => part.trim());
|
|
197
|
+
return parts.length === 3 && parts.every(Boolean)
|
|
198
|
+
? { subject: parts[0], predicate: parts[1], object: parts[2] }
|
|
199
|
+
: { subject: null, predicate: null, object: null };
|
|
200
|
+
}
|
|
201
|
+
function stableCandidateId(draft) {
|
|
202
|
+
return sha256(JSON.stringify({
|
|
203
|
+
type: draft.type,
|
|
204
|
+
evidenceEventIds: [...new Set(draft.evidence.map((span) => span.eventId))].sort(),
|
|
205
|
+
subject: draft.subject,
|
|
206
|
+
predicate: draft.predicate,
|
|
207
|
+
object: draft.object,
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=candidates.js.map
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export const SESSION_CONTRACT_VERSION = '1.0.0';
|
|
4
|
+
export const SESSION_PROVIDER_IDS = [
|
|
5
|
+
'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
|
|
6
|
+
'opencode', 'openclaw', 'openhuman', 'warp', 'devin-desktop', 'generic',
|
|
7
|
+
];
|
|
8
|
+
export const SessionProviderIdSchema = z.enum(SESSION_PROVIDER_IDS);
|
|
9
|
+
export const SESSION_PROVIDER_ALIASES = Object.freeze({
|
|
10
|
+
windsurf: 'devin-desktop',
|
|
11
|
+
});
|
|
12
|
+
const CompatibleSessionProviderIdSchema = z.preprocess((value) => typeof value === 'string'
|
|
13
|
+
? (SESSION_PROVIDER_ALIASES[value] ?? value)
|
|
14
|
+
: value, SessionProviderIdSchema);
|
|
15
|
+
export const CapabilityDispositionSchema = z.enum([
|
|
16
|
+
'implemented', 'manual-only', 'degraded', 'unsupported',
|
|
17
|
+
]);
|
|
18
|
+
export const OperationalStateSchema = z.enum([
|
|
19
|
+
'available', 'unavailable', 'inaccessible', 'version-unknown',
|
|
20
|
+
'schema-unsupported', 'degraded',
|
|
21
|
+
]);
|
|
22
|
+
export const ConsistencyStateSchema = z.enum([
|
|
23
|
+
'provisional', 'consistent-snapshot', 'complete',
|
|
24
|
+
]);
|
|
25
|
+
export const SessionErrorCodeSchema = z.enum([
|
|
26
|
+
'UNKNOWN_PROVIDER', 'UNKNOWN_SCHEMA_MAJOR', 'SOURCE_NOT_AUTHORIZED',
|
|
27
|
+
'SOURCE_OUTSIDE_ALLOWED_ROOT', 'SOURCE_SYMLINK', 'SOURCE_NOT_REGULAR_FILE',
|
|
28
|
+
'RESOURCE_LIMIT_EXCEEDED', 'NETWORK_NOT_AUTHORIZED', 'SCHEMA_DRIFT',
|
|
29
|
+
'OPERATION_NOT_AUTHORIZED',
|
|
30
|
+
'IMPORT_CONFLICT', 'IMPORT_INTERRUPTED', 'MALFORMED_SOURCE',
|
|
31
|
+
'DUPLICATE_NATIVE_ID', 'AMBIGUOUS_TIMESTAMP', 'TRUNCATED_SOURCE',
|
|
32
|
+
'UNSUPPORTED_OPERATION',
|
|
33
|
+
'INVALID_SEARCH_QUERY',
|
|
34
|
+
]);
|
|
35
|
+
const VersionSchema = z.string().regex(/^\d+\.\d+\.\d+(?:[-+].+)?$/);
|
|
36
|
+
const DigestSchema = z.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
37
|
+
const NativeExtensionsSchema = z.record(z.unknown()).superRefine((value, context) => {
|
|
38
|
+
for (const key of Object.keys(value)) {
|
|
39
|
+
if (!key.startsWith('native.')) {
|
|
40
|
+
context.addIssue({ code: z.ZodIssueCode.custom, message: `native extension must use native.<provider>: ${key}` });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
export const SessionSourceSchema = z.object({
|
|
45
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
46
|
+
sourceId: z.string().min(1),
|
|
47
|
+
provider: CompatibleSessionProviderIdSchema,
|
|
48
|
+
providerProfile: z.string().min(1),
|
|
49
|
+
locatorClass: z.string().min(1),
|
|
50
|
+
redactedLocator: z.string().min(1),
|
|
51
|
+
adapterVersion: VersionSchema,
|
|
52
|
+
sourceSchemaVersion: VersionSchema,
|
|
53
|
+
disposition: CapabilityDispositionSchema,
|
|
54
|
+
operationalState: OperationalStateSchema,
|
|
55
|
+
consistency: ConsistencyStateSchema,
|
|
56
|
+
authorizedAt: z.string().datetime({ offset: true }),
|
|
57
|
+
extensions: NativeExtensionsSchema.default({}),
|
|
58
|
+
});
|
|
59
|
+
export const SessionEventSchema = z.object({
|
|
60
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
61
|
+
eventId: z.string().min(1),
|
|
62
|
+
sessionId: z.string().min(1),
|
|
63
|
+
sourceId: z.string().min(1),
|
|
64
|
+
importRunId: z.string().min(1),
|
|
65
|
+
nativeId: z.string().min(1).nullable(),
|
|
66
|
+
sequence: z.number().int().nonnegative(),
|
|
67
|
+
kind: z.string().min(1),
|
|
68
|
+
role: z.string().min(1).nullable(),
|
|
69
|
+
participant: z.string().min(1).nullable().default(null),
|
|
70
|
+
toolName: z.string().min(1).nullable().default(null),
|
|
71
|
+
toolCallId: z.string().min(1).nullable().default(null),
|
|
72
|
+
model: z.string().min(1).nullable().default(null),
|
|
73
|
+
entities: z.array(z.string().min(1)).default([]),
|
|
74
|
+
extractionState: z.string().min(1).nullable().default(null),
|
|
75
|
+
occurredAt: z.string().datetime({ offset: true }).nullable(),
|
|
76
|
+
searchableText: z.string(),
|
|
77
|
+
digest: DigestSchema,
|
|
78
|
+
rawReference: z.object({
|
|
79
|
+
locatorClass: z.string().min(1),
|
|
80
|
+
offset: z.number().int().nonnegative().optional(),
|
|
81
|
+
sequence: z.number().int().nonnegative().optional(),
|
|
82
|
+
}),
|
|
83
|
+
adapterVersion: VersionSchema,
|
|
84
|
+
consistency: ConsistencyStateSchema,
|
|
85
|
+
sensitivity: z.object({
|
|
86
|
+
classification: z.enum(['none', 'sensitive']),
|
|
87
|
+
classes: z.array(z.string().min(1)),
|
|
88
|
+
}),
|
|
89
|
+
opaque: z.boolean().default(false),
|
|
90
|
+
extensions: NativeExtensionsSchema.default({}),
|
|
91
|
+
});
|
|
92
|
+
export const SessionSchema = z.object({
|
|
93
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
94
|
+
sessionId: z.string().min(1),
|
|
95
|
+
sourceId: z.string().min(1),
|
|
96
|
+
provider: CompatibleSessionProviderIdSchema,
|
|
97
|
+
nativeSessionId: z.string().min(1),
|
|
98
|
+
workspaceId: z.string().min(1),
|
|
99
|
+
startedAt: z.string().datetime({ offset: true }).nullable(),
|
|
100
|
+
updatedAt: z.string().datetime({ offset: true }).nullable(),
|
|
101
|
+
consistency: ConsistencyStateSchema,
|
|
102
|
+
lifecycle: z.enum(['active', 'complete', 'tombstoned']),
|
|
103
|
+
sourceDigest: DigestSchema,
|
|
104
|
+
extensions: NativeExtensionsSchema.default({}),
|
|
105
|
+
});
|
|
106
|
+
export const ImportCheckpointSchema = z.object({
|
|
107
|
+
cursor: z.string(),
|
|
108
|
+
recordsRead: z.number().int().nonnegative(),
|
|
109
|
+
bytesRead: z.number().int().nonnegative(),
|
|
110
|
+
checkpointVersion: z.literal('2').optional(),
|
|
111
|
+
positionKind: z.enum(['record-index', 'byte-offset', 'provider-native']).optional(),
|
|
112
|
+
sourceGeneration: z.string().min(1).optional(),
|
|
113
|
+
locatorClass: z.string().min(1).optional(),
|
|
114
|
+
adapterVersion: VersionSchema.optional(),
|
|
115
|
+
sourceSchemaVersion: VersionSchema.optional(),
|
|
116
|
+
policyVersion: VersionSchema.optional(),
|
|
117
|
+
continuity: z.enum([
|
|
118
|
+
'new-generation',
|
|
119
|
+
'validated-append',
|
|
120
|
+
'unchanged-replay',
|
|
121
|
+
'unverified',
|
|
122
|
+
]).optional(),
|
|
123
|
+
sourceSize: z.number().int().nonnegative().optional(),
|
|
124
|
+
sourceMtimeMs: z.number().nonnegative().optional(),
|
|
125
|
+
sourceFileIdentity: z.string().min(1).optional(),
|
|
126
|
+
prefixDigest: DigestSchema.optional(),
|
|
127
|
+
});
|
|
128
|
+
export const ImportRunSchema = z.object({
|
|
129
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
130
|
+
importRunId: z.string().min(1),
|
|
131
|
+
sourceId: z.string().min(1),
|
|
132
|
+
parserVersion: VersionSchema,
|
|
133
|
+
policyVersion: VersionSchema,
|
|
134
|
+
sourceSchemaVersion: VersionSchema,
|
|
135
|
+
consistency: ConsistencyStateSchema,
|
|
136
|
+
status: z.enum(['running', 'committed', 'rolled-back', 'failed']),
|
|
137
|
+
checkpoint: ImportCheckpointSchema,
|
|
138
|
+
startedAt: z.string().datetime({ offset: true }),
|
|
139
|
+
completedAt: z.string().datetime({ offset: true }).nullable(),
|
|
140
|
+
errorCode: SessionErrorCodeSchema.nullable(),
|
|
141
|
+
});
|
|
142
|
+
export const ProvenanceEdgeSchema = z.object({
|
|
143
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
144
|
+
edgeId: z.string().min(1),
|
|
145
|
+
relation: z.enum([
|
|
146
|
+
'acquired-from', 'normalized-from', 'derived-from', 'promoted-to',
|
|
147
|
+
'supersedes', 'invalidates',
|
|
148
|
+
]),
|
|
149
|
+
fromId: z.string().min(1),
|
|
150
|
+
toId: z.string().min(1),
|
|
151
|
+
importRunId: z.string().min(1),
|
|
152
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
153
|
+
});
|
|
154
|
+
export const IntelligenceCandidateTypeSchema = z.enum([
|
|
155
|
+
'decision', 'requirement', 'constraint', 'preference', 'task', 'discovery',
|
|
156
|
+
'fix', 'failed-approach', 'procedure', 'risk', 'contradiction', 'question',
|
|
157
|
+
'entity', 'relationship',
|
|
158
|
+
]);
|
|
159
|
+
export const CandidateSecurityWarningSchema = z.enum([
|
|
160
|
+
'instruction-like', 'structure-breaking', 'control-character', 'bidi-control',
|
|
161
|
+
'unicode-confusable', 'active-content', 'secret-bearing',
|
|
162
|
+
]);
|
|
163
|
+
export const CandidateSecuritySchema = z.object({
|
|
164
|
+
disposition: z.enum(['clear', 'suspicious']),
|
|
165
|
+
warnings: z.array(CandidateSecurityWarningSchema),
|
|
166
|
+
requiresAcknowledgement: z.boolean(),
|
|
167
|
+
acknowledged: z.boolean(),
|
|
168
|
+
policyVersion: VersionSchema,
|
|
169
|
+
}).strict();
|
|
170
|
+
export const IntelligenceCandidateSchema = z.object({
|
|
171
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
172
|
+
candidateId: z.string().min(1),
|
|
173
|
+
version: z.number().int().positive(),
|
|
174
|
+
type: IntelligenceCandidateTypeSchema,
|
|
175
|
+
assertion: z.string().min(1),
|
|
176
|
+
subject: z.string().min(1).nullable(),
|
|
177
|
+
predicate: z.string().min(1).nullable(),
|
|
178
|
+
object: z.string().min(1).nullable(),
|
|
179
|
+
evidence: z.array(z.object({
|
|
180
|
+
eventId: z.string().min(1),
|
|
181
|
+
start: z.number().int().nonnegative(),
|
|
182
|
+
end: z.number().int().positive(),
|
|
183
|
+
quoteDigest: DigestSchema,
|
|
184
|
+
quote: z.string().min(1).optional(),
|
|
185
|
+
}).strict()).min(1),
|
|
186
|
+
confidence: z.number().min(0).max(1),
|
|
187
|
+
temporalScope: z.string().min(1),
|
|
188
|
+
projectScope: z.string().min(1),
|
|
189
|
+
extractionMethod: z.string().min(1),
|
|
190
|
+
extractionVersion: VersionSchema,
|
|
191
|
+
extractionPolicyVersion: VersionSchema,
|
|
192
|
+
model: z.string().min(1).nullable(),
|
|
193
|
+
sensitivity: z.enum(['none', 'sensitive']),
|
|
194
|
+
security: CandidateSecuritySchema.default({
|
|
195
|
+
disposition: 'clear',
|
|
196
|
+
warnings: [],
|
|
197
|
+
requiresAcknowledgement: false,
|
|
198
|
+
acknowledged: false,
|
|
199
|
+
policyVersion: '1.0.0',
|
|
200
|
+
}),
|
|
201
|
+
reviewState: z.enum([
|
|
202
|
+
'pending', 'accepted', 'rejected', 'deferred', 'promoted', 'superseded',
|
|
203
|
+
]),
|
|
204
|
+
conflictsWith: z.array(z.string().min(1)).default([]),
|
|
205
|
+
supersedes: z.array(z.string().min(1)).default([]),
|
|
206
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
207
|
+
}).strict().superRefine((candidate, context) => {
|
|
208
|
+
if (candidate.type === 'relationship'
|
|
209
|
+
&& (!candidate.subject || !candidate.predicate || !candidate.object)) {
|
|
210
|
+
context.addIssue({
|
|
211
|
+
code: z.ZodIssueCode.custom,
|
|
212
|
+
message: 'relationship candidates require subject, predicate, and object',
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
export const CandidateReviewReceiptSchema = z.object({
|
|
217
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
218
|
+
receiptId: z.string().min(1),
|
|
219
|
+
candidateId: z.string().min(1),
|
|
220
|
+
candidateVersion: z.number().int().positive(),
|
|
221
|
+
fromState: z.enum([
|
|
222
|
+
'pending', 'accepted', 'rejected', 'deferred', 'promoted', 'superseded',
|
|
223
|
+
]),
|
|
224
|
+
toState: z.enum([
|
|
225
|
+
'pending', 'accepted', 'rejected', 'deferred', 'promoted', 'superseded',
|
|
226
|
+
]),
|
|
227
|
+
reviewer: z.string().min(1),
|
|
228
|
+
reason: z.string().min(1),
|
|
229
|
+
securityWarnings: z.array(CandidateSecurityWarningSchema).default([]),
|
|
230
|
+
securityAcknowledged: z.boolean().default(false),
|
|
231
|
+
occurredAt: z.string().datetime({ offset: true }),
|
|
232
|
+
}).strict();
|
|
233
|
+
export const PromotionReceiptSchema = z.object({
|
|
234
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
235
|
+
receiptId: z.string().min(1),
|
|
236
|
+
operationId: z.string().min(1),
|
|
237
|
+
candidateId: z.string().min(1),
|
|
238
|
+
candidateVersion: z.number().int().positive(),
|
|
239
|
+
consumer: z.string().min(1),
|
|
240
|
+
destinationRef: z.string().min(1),
|
|
241
|
+
reviewer: z.string().min(1),
|
|
242
|
+
approvedAt: z.string().datetime({ offset: true }),
|
|
243
|
+
evidenceEventIds: z.array(z.string().min(1)).min(1),
|
|
244
|
+
conflictsWith: z.array(z.string().min(1)),
|
|
245
|
+
supersedes: z.array(z.string().min(1)),
|
|
246
|
+
beforeHash: DigestSchema.nullable(),
|
|
247
|
+
afterHash: DigestSchema,
|
|
248
|
+
dryRun: z.boolean(),
|
|
249
|
+
duplicate: z.boolean(),
|
|
250
|
+
}).strict();
|
|
251
|
+
export const DeletionReceiptSchema = z.object({
|
|
252
|
+
contractVersion: z.literal(SESSION_CONTRACT_VERSION),
|
|
253
|
+
receiptId: z.string().min(1),
|
|
254
|
+
operationId: z.string().min(1),
|
|
255
|
+
scopeClass: z.string().min(1),
|
|
256
|
+
counts: z.record(z.number().int().nonnegative()),
|
|
257
|
+
survivingDependentIds: z.array(z.string().min(1)),
|
|
258
|
+
actorClass: z.string().min(1),
|
|
259
|
+
reasonCode: z.string().min(1),
|
|
260
|
+
orphanCounts: z.record(z.number().int().nonnegative()),
|
|
261
|
+
outcome: z.enum(['preview', 'committed', 'failed']),
|
|
262
|
+
occurredAt: z.string().datetime({ offset: true }),
|
|
263
|
+
}).strict();
|
|
264
|
+
export const PromotionDependencyDecisionSchema = z.object({
|
|
265
|
+
dependentId: z.string().min(1),
|
|
266
|
+
action: z.enum([
|
|
267
|
+
'revoke', 'supersede', 'retain', 'origin_unavailable', 'delete', 'abort',
|
|
268
|
+
]),
|
|
269
|
+
basis: z.string().min(1),
|
|
270
|
+
}).strict();
|
|
271
|
+
export function assertSessionProviderId(value) {
|
|
272
|
+
const canonical = SESSION_PROVIDER_ALIASES[value] ?? value;
|
|
273
|
+
const parsed = SessionProviderIdSchema.safeParse(canonical);
|
|
274
|
+
if (!parsed.success)
|
|
275
|
+
throw new SessionContractError('UNKNOWN_PROVIDER', `unknown session provider: ${value}`);
|
|
276
|
+
return parsed.data;
|
|
277
|
+
}
|
|
278
|
+
export function assertSupportedSchemaMajor(version, supportedMajor = 1) {
|
|
279
|
+
const match = /^(\d+)\./.exec(version);
|
|
280
|
+
if (!match || Number(match[1]) !== supportedMajor) {
|
|
281
|
+
throw new SessionContractError('UNKNOWN_SCHEMA_MAJOR', `unsupported session schema major: ${version}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
export function stableSessionId(provider, sourceId, nativeSessionId) {
|
|
285
|
+
const canonical = assertSessionProviderId(provider);
|
|
286
|
+
// Preserve the legacy identity seed through the published compatibility
|
|
287
|
+
// window so existing Windsurf catalogs do not duplicate normalized rows.
|
|
288
|
+
const identitySeed = canonical === 'devin-desktop' ? 'windsurf' : canonical;
|
|
289
|
+
return stableId('session', identitySeed, sourceId, nativeSessionId);
|
|
290
|
+
}
|
|
291
|
+
export function stableEventId(sourceId, record, digest) {
|
|
292
|
+
return record.nativeEventId
|
|
293
|
+
? stableId('event', sourceId, record.nativeEventId)
|
|
294
|
+
: stableId('event', sourceId, record.nativeSessionId, record.sequence, record.kind, digest);
|
|
295
|
+
}
|
|
296
|
+
export function sha256(value) {
|
|
297
|
+
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
298
|
+
}
|
|
299
|
+
function stableId(prefix, ...parts) {
|
|
300
|
+
return `${prefix}_${createHash('sha256').update(parts.join('\0')).digest('hex')}`;
|
|
301
|
+
}
|
|
302
|
+
export class SessionContractError extends Error {
|
|
303
|
+
code;
|
|
304
|
+
constructor(code, message) {
|
|
305
|
+
super(message);
|
|
306
|
+
this.code = code;
|
|
307
|
+
this.name = 'SessionContractError';
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=contracts.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SessionContractError, assertSessionProviderId, } from './contracts.js';
|
|
2
|
+
export class SessionSourceAdapterRegistry {
|
|
3
|
+
adapters = new Map();
|
|
4
|
+
register(adapter) {
|
|
5
|
+
const provider = assertSessionProviderId(adapter.provider);
|
|
6
|
+
if (this.adapters.has(provider))
|
|
7
|
+
throw new Error(`session adapter already registered: ${provider}`);
|
|
8
|
+
this.adapters.set(provider, adapter);
|
|
9
|
+
}
|
|
10
|
+
get(providerInput) {
|
|
11
|
+
const provider = assertSessionProviderId(providerInput);
|
|
12
|
+
const adapter = this.adapters.get(provider);
|
|
13
|
+
if (!adapter) {
|
|
14
|
+
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', `no session adapter is registered for ${provider}`);
|
|
15
|
+
}
|
|
16
|
+
return adapter;
|
|
17
|
+
}
|
|
18
|
+
assertOperation(providerInput, operation) {
|
|
19
|
+
const adapter = this.get(providerInput);
|
|
20
|
+
if (!adapter.supportedOperations.includes(operation)) {
|
|
21
|
+
throw new SessionContractError('UNSUPPORTED_OPERATION', `${operation} is unsupported for provider ${adapter.provider}`);
|
|
22
|
+
}
|
|
23
|
+
return adapter;
|
|
24
|
+
}
|
|
25
|
+
async *discover(providerInput, scope) {
|
|
26
|
+
const adapter = this.assertOperation(providerInput, 'discover');
|
|
27
|
+
if (scope.allowedRoots.length === 0 && (scope.authorizedAccounts?.length ?? 0) === 0) {
|
|
28
|
+
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'discovery requires at least one explicitly authorized root or account');
|
|
29
|
+
}
|
|
30
|
+
yield* adapter.discover({
|
|
31
|
+
...scope,
|
|
32
|
+
allowedRoots: [...scope.allowedRoots],
|
|
33
|
+
authorizedAccounts: scope.authorizedAccounts ? [...scope.authorizedAccounts] : undefined,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
report(providerInput, input) {
|
|
37
|
+
const adapter = this.get(providerInput);
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
provider: adapter.provider,
|
|
40
|
+
classification: adapter.disposition,
|
|
41
|
+
supportedOperations: Object.freeze([...adapter.supportedOperations]),
|
|
42
|
+
acquisitionModes: Object.freeze([...adapter.acquisitionModes]),
|
|
43
|
+
...input,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function redactSourceLocator(locator) {
|
|
48
|
+
const leaf = locator.replaceAll('\\', '/').split('/').filter(Boolean).at(-1) ?? '<source>';
|
|
49
|
+
return `<session-source>/${leaf.replace(/[^A-Za-z0-9._-]/g, '_')}`;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function defineSessionAdapterFixture(fixture) {
|
|
2
|
+
if (fixture.synthetic !== true)
|
|
3
|
+
throw new Error('session fixtures must be synthetic or separately redacted');
|
|
4
|
+
if (fixture.disposition === 'implemented' && fixture.records.length === 0) {
|
|
5
|
+
throw new Error('implemented adapters require at least one synthetic fixture record');
|
|
6
|
+
}
|
|
7
|
+
if (fixture.disposition !== 'implemented' && !fixture.unsupportedReason) {
|
|
8
|
+
throw new Error(`${fixture.disposition} fixtures require an explicit reason`);
|
|
9
|
+
}
|
|
10
|
+
return Object.freeze({ ...fixture, records: Object.freeze([...fixture.records]) });
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=fixtures.js.map
|