@playola-radio/slipstream 0.1.0
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/LICENSE +202 -0
- package/README.md +132 -0
- package/SKILL.md +63 -0
- package/THIRD-PARTY-NOTICES.md +36 -0
- package/contracts/interface/v2/schema.json +480 -0
- package/dist/answer-forwarder.js +58 -0
- package/dist/attribution-engine.js +164 -0
- package/dist/attribution-producer.js +153 -0
- package/dist/attribution-scoring.js +86 -0
- package/dist/attribution.js +120 -0
- package/dist/blob-source.js +100 -0
- package/dist/boundary-registry.js +47 -0
- package/dist/cas.js +99 -0
- package/dist/change-view.js +201 -0
- package/dist/claude-root-transcript.js +110 -0
- package/dist/cli.js +629 -0
- package/dist/clip-blob-reader.js +104 -0
- package/dist/clip-function-parser.js +83 -0
- package/dist/clip-language.js +15 -0
- package/dist/clip-projection-service.js +240 -0
- package/dist/clip-projection-worker.js +18 -0
- package/dist/clip-projection.js +585 -0
- package/dist/clip-worker-pool.js +118 -0
- package/dist/config.js +154 -0
- package/dist/control-client.js +98 -0
- package/dist/control-protocol.js +72 -0
- package/dist/daemon-location.js +33 -0
- package/dist/daemon.js +951 -0
- package/dist/display-fold.js +332 -0
- package/dist/engine.js +112 -0
- package/dist/entrypoint.js +17 -0
- package/dist/event.js +79 -0
- package/dist/evidence-ingest.js +60 -0
- package/dist/harness-context/claude.js +42 -0
- package/dist/harness-context/codex.js +49 -0
- package/dist/harness-context.js +49 -0
- package/dist/health.js +65 -0
- package/dist/http-reader.js +655 -0
- package/dist/http-security.js +50 -0
- package/dist/interface-projection.js +243 -0
- package/dist/interface-range-resolver.js +230 -0
- package/dist/interface-service.js +509 -0
- package/dist/interface-ts-pool.js +111 -0
- package/dist/interface-ts-worker.js +35 -0
- package/dist/interface-v2-comparison.js +135 -0
- package/dist/interface-v2-core.js +123 -0
- package/dist/interface-v2-typescript.js +448 -0
- package/dist/lock.js +197 -0
- package/dist/log-reader.js +148 -0
- package/dist/log.js +59 -0
- package/dist/maintenance.js +255 -0
- package/dist/mcp-forwarder.js +161 -0
- package/dist/mcp-protocol.js +92 -0
- package/dist/platform.js +21 -0
- package/dist/projection-admission.js +296 -0
- package/dist/projection-trace.js +17 -0
- package/dist/public-events.js +27 -0
- package/dist/question-hook.js +110 -0
- package/dist/questions.js +167 -0
- package/dist/reader-runtime.js +38 -0
- package/dist/reader.js +93 -0
- package/dist/recovery.js +303 -0
- package/dist/schema.js +108 -0
- package/dist/session.js +866 -0
- package/dist/sibling-module.js +10 -0
- package/dist/snapshot.js +15 -0
- package/dist/storage.js +111 -0
- package/dist/store-reader.js +185 -0
- package/dist/swift-grammar.js +268 -0
- package/dist/swift-interface-extract.js +344 -0
- package/dist/swift-interface.js +62 -0
- package/dist/swift-parse-host.js +174 -0
- package/dist/swift-parse-worker.js +34 -0
- package/dist/swift-parse.js +134 -0
- package/dist/swift-spans.js +62 -0
- package/dist/task-forwarder.js +152 -0
- package/dist/transcript/claude.js +199 -0
- package/dist/transcript/codex.js +132 -0
- package/dist/transcript/discovery.js +365 -0
- package/dist/transcript/file-reader.js +128 -0
- package/dist/transcript/fs-io.js +287 -0
- package/dist/transcript/runner.js +62 -0
- package/dist/transcript/steppers.js +30 -0
- package/dist/transcript/types.js +30 -0
- package/dist/transcript/watcher.js +133 -0
- package/dist/tui.js +168 -0
- package/package.json +60 -0
- package/schemas/projections/clip.v1.json +195 -0
- package/schemas/projections/clip.v2.json +212 -0
- package/schemas/projections/clip.v3.json +212 -0
- package/schemas/slipstream.capture.baseline.completed.v1.json +62 -0
- package/schemas/slipstream.capture.gap.v1.json +112 -0
- package/schemas/slipstream.change.attribution.v1.json +115 -0
- package/schemas/slipstream.enrichment.configured.v1.json +77 -0
- package/schemas/slipstream.enrichment.coverage.v1.json +95 -0
- package/schemas/slipstream.file.baselined.v1.json +122 -0
- package/schemas/slipstream.file.changed.v1.json +179 -0
- package/schemas/slipstream.harness.evidence.v1.json +145 -0
- package/schemas/slipstream.question.answered.v1.json +29 -0
- package/schemas/slipstream.question.dispatch_attempted.v1.json +28 -0
- package/schemas/slipstream.question.queued.v1.json +55 -0
- package/schemas/slipstream.session.resumed.v1.json +70 -0
- package/schemas/slipstream.session.started.v1.json +69 -0
- package/schemas/slipstream.task.started.v1.json +72 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/** D3 correspondence and component deltas for interface.v2 written declarations. */
|
|
2
|
+
import { compare, hasDuplicate } from "./interface-projection.js";
|
|
3
|
+
/** v2 D8 excludes the private signature key used by the v1 matcher. */
|
|
4
|
+
function compareV2Rows(a, b) {
|
|
5
|
+
const rank = { removed: 0, signatureChanged: 1, added: 2 };
|
|
6
|
+
const byKind = rank[a.kind] - rank[b.kind];
|
|
7
|
+
if (byKind)
|
|
8
|
+
return byKind;
|
|
9
|
+
const cmp = (x, y) => x < y ? -1 : x > y ? 1 : 0;
|
|
10
|
+
const cmpArray = (x, y) => {
|
|
11
|
+
for (let i = 0; i < Math.min(x.length, y.length); i++) {
|
|
12
|
+
const c = cmp(x[i], y[i]);
|
|
13
|
+
if (c)
|
|
14
|
+
return c;
|
|
15
|
+
}
|
|
16
|
+
return cmp(x.length, y.length);
|
|
17
|
+
};
|
|
18
|
+
const compareSide = (x, y, side) => {
|
|
19
|
+
const left = x[side], right = y[side];
|
|
20
|
+
if (!left || !right)
|
|
21
|
+
return left ? 1 : right ? -1 : 0;
|
|
22
|
+
return cmp(left.display_name, right.display_name)
|
|
23
|
+
|| cmp(x.identity.kind, y.identity.kind)
|
|
24
|
+
|| cmpArray(x.identity.scope.flatMap(scope => [scope.kind, scope.name]), y.identity.scope.flatMap(scope => [scope.kind, scope.name]))
|
|
25
|
+
|| cmp(x.identity.name, y.identity.name)
|
|
26
|
+
|| cmpArray(x.identity.guards, y.identity.guards)
|
|
27
|
+
|| cmp(left.span.byte_start, right.span.byte_start)
|
|
28
|
+
|| cmp(left.span.byte_end, right.span.byte_end);
|
|
29
|
+
};
|
|
30
|
+
return compareSide(a, b, a.kind === 'added' ? 'after' : 'before') || compareSide(a, b, 'after');
|
|
31
|
+
}
|
|
32
|
+
function signature(d) {
|
|
33
|
+
return JSON.stringify([d.role ?? null, d.parameters.map(p => [p.name, p.label, p.binding,
|
|
34
|
+
p.type, p.optional, p.variadic, p.default, p.modifiers]), d.result, d.throws, d.header]);
|
|
35
|
+
}
|
|
36
|
+
function delta(before, after) {
|
|
37
|
+
const op = before === null ? 'added' : after === null ? 'removed'
|
|
38
|
+
: JSON.stringify(before) === JSON.stringify(after) ? 'equal' : 'changed';
|
|
39
|
+
return { op, before, after };
|
|
40
|
+
}
|
|
41
|
+
function parameterDeltas(before, after) {
|
|
42
|
+
const counts = (ps) => {
|
|
43
|
+
const result = new Map();
|
|
44
|
+
for (const p of ps)
|
|
45
|
+
if (p.binding === 'identifier')
|
|
46
|
+
result.set(p.name, (result.get(p.name) ?? 0) + 1);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
const beforeCounts = counts(before);
|
|
50
|
+
const afterCounts = counts(after);
|
|
51
|
+
const afterByName = new Map(after.map((p, index) => [p.name, index]));
|
|
52
|
+
const paired = new Set();
|
|
53
|
+
const rows = before.map(p => {
|
|
54
|
+
if (p.binding !== 'identifier' || beforeCounts.get(p.name) !== 1 || afterCounts.get(p.name) !== 1) {
|
|
55
|
+
return delta(p, null);
|
|
56
|
+
}
|
|
57
|
+
const index = afterByName.get(p.name);
|
|
58
|
+
paired.add(index);
|
|
59
|
+
return delta(p, after[index]);
|
|
60
|
+
});
|
|
61
|
+
after.forEach((p, i) => { if (!paired.has(i))
|
|
62
|
+
rows.push(delta(null, p)); });
|
|
63
|
+
return rows;
|
|
64
|
+
}
|
|
65
|
+
function outSide(d) {
|
|
66
|
+
if (!d)
|
|
67
|
+
return null;
|
|
68
|
+
return { display_name: d.displayName, span: { byte_start: d.span.byteStart, byte_end: d.span.byteEnd } };
|
|
69
|
+
}
|
|
70
|
+
function toChange(kind, before, after) {
|
|
71
|
+
const subject = before ?? after;
|
|
72
|
+
const result = before?.result ?? null;
|
|
73
|
+
const nextResult = after?.result ?? null;
|
|
74
|
+
return {
|
|
75
|
+
kind,
|
|
76
|
+
identity: subject.identity,
|
|
77
|
+
before: outSide(before), after: outSide(after),
|
|
78
|
+
parameters: parameterDeltas(before?.parameters ?? [], after?.parameters ?? []),
|
|
79
|
+
result: result === null && nextResult === null ? null : delta(result, nextResult),
|
|
80
|
+
throws: delta(before?.throws ?? null, after?.throws ?? null),
|
|
81
|
+
header: delta(before?.header ?? null, after?.header ?? null),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** Compare already extracted sides. Endpoint selection and file provenance belong to FD3/FD4. */
|
|
85
|
+
export function compareStructuredExtractions(before, after) {
|
|
86
|
+
if (before.status === 'incomplete')
|
|
87
|
+
return {
|
|
88
|
+
status: 'incomplete', fallback_reason: `before-${before.reason}`, changes: [],
|
|
89
|
+
};
|
|
90
|
+
if (after.status === 'incomplete')
|
|
91
|
+
return {
|
|
92
|
+
status: 'incomplete', fallback_reason: `after-${after.reason}`, changes: [],
|
|
93
|
+
};
|
|
94
|
+
const b = before.status === 'absent' ? [] : before.declarations;
|
|
95
|
+
const a = after.status === 'absent' ? [] : after.declarations;
|
|
96
|
+
const asV1 = (d) => ({
|
|
97
|
+
identity: d.identity, displayName: d.displayName, signature: signature(d), span: d.span,
|
|
98
|
+
});
|
|
99
|
+
const beforeV1 = b.map(asV1);
|
|
100
|
+
const afterV1 = a.map(asV1);
|
|
101
|
+
if (hasDuplicate(beforeV1) || hasDuplicate(afterV1))
|
|
102
|
+
return {
|
|
103
|
+
status: 'incomplete', fallback_reason: 'duplicate-declaration', changes: [],
|
|
104
|
+
};
|
|
105
|
+
const matched = compare(beforeV1, afterV1);
|
|
106
|
+
if (matched.ambiguous)
|
|
107
|
+
return {
|
|
108
|
+
status: 'incomplete', fallback_reason: 'ambiguous-correspondence', changes: [],
|
|
109
|
+
};
|
|
110
|
+
const lookupKey = (identity, name, signatureText, byteStart, byteEnd) => JSON.stringify([
|
|
111
|
+
identity.kind, identity.scope, identity.name, identity.guards,
|
|
112
|
+
name, signatureText, byteStart, byteEnd,
|
|
113
|
+
]);
|
|
114
|
+
const index = (original, converted) => new Map(converted.map((d, i) => [lookupKey(d.identity, d.displayName, d.signature, d.span.byteStart, d.span.byteEnd), original[i]]));
|
|
115
|
+
const beforeIndex = index(b, beforeV1);
|
|
116
|
+
const afterIndex = index(a, afterV1);
|
|
117
|
+
const find = (map, row, identity) => map.get(lookupKey(identity, row.display_name, row.signature, row.span.byte_start, row.span.byte_end)) ?? null;
|
|
118
|
+
const changes = [];
|
|
119
|
+
for (const row of matched.changes) {
|
|
120
|
+
const left = row.before ? find(beforeIndex, row.before, row.identity) : null;
|
|
121
|
+
const right = row.after ? find(afterIndex, row.after, row.identity) : null;
|
|
122
|
+
if ((row.before && !left) || (row.after && !right))
|
|
123
|
+
throw new Error('missing structured declaration');
|
|
124
|
+
const change = toChange(row.kind, left, right);
|
|
125
|
+
// Overload role is an internal disambiguator, not a written input/output
|
|
126
|
+
// component. A role-only transition has no public delta.
|
|
127
|
+
if (row.kind === 'signatureChanged' && change.parameters.every(p => p.op === 'equal')
|
|
128
|
+
&& (change.result === null || change.result.op === 'equal')
|
|
129
|
+
&& change.throws.op === 'equal' && change.header.op === 'equal')
|
|
130
|
+
continue;
|
|
131
|
+
changes.push(change);
|
|
132
|
+
}
|
|
133
|
+
changes.sort(compareV2Rows);
|
|
134
|
+
return { status: 'ready', changes };
|
|
135
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const identityKey = (d) => JSON.stringify([
|
|
2
|
+
d.identity.kind, d.identity.scope.map((s) => [s.kind, s.name]), d.identity.name, d.identity.guards,
|
|
3
|
+
]);
|
|
4
|
+
const exactKey = (d) => JSON.stringify([identityKey(d), d.signature]);
|
|
5
|
+
const equal = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
6
|
+
function delta(before, after) {
|
|
7
|
+
return { op: before === null ? 'added' : after === null ? 'removed' : equal(before, after) ? 'equal' : 'changed', before, after };
|
|
8
|
+
}
|
|
9
|
+
function side(d) {
|
|
10
|
+
return d && { display_name: d.displayName, span: { byte_start: d.span.byteStart, byte_end: d.span.byteEnd } };
|
|
11
|
+
}
|
|
12
|
+
function pairParameters(before, after) {
|
|
13
|
+
const counts = (ps) => {
|
|
14
|
+
const result = new Map();
|
|
15
|
+
for (const p of ps)
|
|
16
|
+
if (p.binding === 'identifier')
|
|
17
|
+
result.set(p.name, (result.get(p.name) ?? 0) + 1);
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
20
|
+
const beforeCount = counts(before), afterCount = counts(after);
|
|
21
|
+
const usable = (p) => p.binding === 'identifier' && beforeCount.get(p.name) === 1 && afterCount.get(p.name) === 1;
|
|
22
|
+
const afterByName = new Map(after.filter(usable).map((p) => [p.name, p]));
|
|
23
|
+
const rows = before.map((p) => delta(p, usable(p) ? afterByName.get(p.name) : null));
|
|
24
|
+
for (const p of after)
|
|
25
|
+
if (!usable(p))
|
|
26
|
+
rows.push(delta(null, p));
|
|
27
|
+
return rows;
|
|
28
|
+
}
|
|
29
|
+
function row(before, after) {
|
|
30
|
+
const kind = before === null ? 'added' : after === null ? 'removed' : 'signatureChanged';
|
|
31
|
+
return {
|
|
32
|
+
kind, identity: (before ?? after).identity, before: side(before), after: side(after),
|
|
33
|
+
parameters: pairParameters(before?.parameters ?? [], after?.parameters ?? []),
|
|
34
|
+
result: (before?.result ?? null) === null && (after?.result ?? null) === null ? null : delta(before?.result ?? null, after?.result ?? null),
|
|
35
|
+
throws: delta(before?.throws ?? null, after?.throws ?? null),
|
|
36
|
+
header: delta(before?.header ?? null, after?.header ?? null),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function cmp(a, b) { return a < b ? -1 : a > b ? 1 : 0; }
|
|
40
|
+
function cmpArray(a, b) {
|
|
41
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
|
42
|
+
const result = cmp(a[i], b[i]);
|
|
43
|
+
if (result)
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
return cmp(a.length, b.length);
|
|
47
|
+
}
|
|
48
|
+
/** D8 tuple, adapted to v2's declarationSide (no `signature`: the emitted `before`/`after`
|
|
49
|
+
* carry only `display_name` and `span`, per contracts/interface/v2/schema.json): display_name,
|
|
50
|
+
* identity.kind, identity.scope pairs, identity.name, identity.guards, span.byte_start,
|
|
51
|
+
* span.byte_end. */
|
|
52
|
+
function order(a, b) {
|
|
53
|
+
const scopeOrder = cmpArray(a.identity.scope.flatMap((s) => [s.kind, s.name]), b.identity.scope.flatMap((s) => [s.kind, s.name]));
|
|
54
|
+
const guardOrder = cmpArray(a.identity.guards, b.identity.guards);
|
|
55
|
+
for (const [x, y] of [
|
|
56
|
+
[a.displayName, b.displayName], [a.identity.kind, b.identity.kind],
|
|
57
|
+
]) {
|
|
58
|
+
const c = cmp(x, y);
|
|
59
|
+
if (c)
|
|
60
|
+
return c;
|
|
61
|
+
}
|
|
62
|
+
if (scopeOrder)
|
|
63
|
+
return scopeOrder;
|
|
64
|
+
const nameOrder = cmp(a.identity.name, b.identity.name);
|
|
65
|
+
if (nameOrder)
|
|
66
|
+
return nameOrder;
|
|
67
|
+
if (guardOrder)
|
|
68
|
+
return guardOrder;
|
|
69
|
+
for (const [x, y] of [
|
|
70
|
+
[a.span.byteStart, b.span.byteStart],
|
|
71
|
+
[a.span.byteEnd, b.span.byteEnd],
|
|
72
|
+
]) {
|
|
73
|
+
const c = cmp(x, y);
|
|
74
|
+
if (c)
|
|
75
|
+
return c;
|
|
76
|
+
}
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
/** D3 exact-first correspondence, with whole-file refusal for duplicate or ambiguous groups. */
|
|
80
|
+
export function compareV2(before, after) {
|
|
81
|
+
for (const side of [before, after]) {
|
|
82
|
+
const keys = side.map(exactKey);
|
|
83
|
+
if (new Set(keys).size !== keys.length)
|
|
84
|
+
return { status: 'incomplete', reason: 'duplicate-declaration' };
|
|
85
|
+
}
|
|
86
|
+
const beforeExact = new Set(before.map(exactKey)), afterExact = new Set(after.map(exactKey));
|
|
87
|
+
const groups = new Map();
|
|
88
|
+
for (const [side, values] of [
|
|
89
|
+
['before', before.filter((d) => !afterExact.has(exactKey(d)))],
|
|
90
|
+
['after', after.filter((d) => !beforeExact.has(exactKey(d)))],
|
|
91
|
+
]) {
|
|
92
|
+
for (const d of values) {
|
|
93
|
+
const key = identityKey(d);
|
|
94
|
+
let group = groups.get(key);
|
|
95
|
+
if (!group)
|
|
96
|
+
groups.set(key, group = { before: [], after: [] });
|
|
97
|
+
group[side].push(d);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const items = [];
|
|
101
|
+
for (const group of groups.values()) {
|
|
102
|
+
const b = group.before, a = group.after;
|
|
103
|
+
if (b.length && a.length) {
|
|
104
|
+
if (b.length !== 1 || a.length !== 1)
|
|
105
|
+
return { status: 'incomplete', reason: 'ambiguous-correspondence' };
|
|
106
|
+
items.push({ before: b[0], after: a[0] });
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
for (const d of b)
|
|
110
|
+
items.push({ before: d, after: null });
|
|
111
|
+
for (const d of a)
|
|
112
|
+
items.push({ before: null, after: d });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const rank = { removed: 0, signatureChanged: 1, added: 2 };
|
|
116
|
+
items.sort((x, y) => {
|
|
117
|
+
const xKind = x.before === null ? 'added' : x.after === null ? 'removed' : 'signatureChanged';
|
|
118
|
+
const yKind = y.before === null ? 'added' : y.after === null ? 'removed' : 'signatureChanged';
|
|
119
|
+
return rank[xKind] - rank[yKind] || order(x.before ?? x.after, y.before ?? y.after) ||
|
|
120
|
+
(x.after && y.after ? order(x.after, y.after) : 0);
|
|
121
|
+
});
|
|
122
|
+
return { status: 'ready', changes: items.map(({ before: b, after: a }) => row(b, a)) };
|
|
123
|
+
}
|