@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,49 @@
|
|
|
1
|
+
import { isAbsolute } from 'node:path';
|
|
2
|
+
function isObject(v) {
|
|
3
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Codex identity adapter (fail-closed).
|
|
7
|
+
*
|
|
8
|
+
* Per the SC3 probe (tools/identity-probe/FINDINGS.md): Codex exposes NO identity
|
|
9
|
+
* environment variable and a clean env at startup — identity arrives ONLY on the
|
|
10
|
+
* tool call, in `_meta`. The carrier is `_meta.threadId` (== session_id) plus
|
|
11
|
+
* `_meta['x-codex-turn-metadata'].workspaces`, a map from the canonical worktree
|
|
12
|
+
* path to its origin/commit. `cwd` is unreliable (sometimes `/`) and is never a
|
|
13
|
+
* binding input.
|
|
14
|
+
*
|
|
15
|
+
* Fail closed unless `_meta` is present, `threadId` is present, and `workspaces`
|
|
16
|
+
* has EXACTLY one key: an absent, empty, or multi-root map is ambiguous, so we
|
|
17
|
+
* refuse rather than guess. (A dropped Codex transport is terminal for the binding
|
|
18
|
+
* — recovery needs a new conversation — but that is the transport's concern, not
|
|
19
|
+
* this pure derivation's.)
|
|
20
|
+
*/
|
|
21
|
+
export function codexIdentity(toolCallParams) {
|
|
22
|
+
if (!isObject(toolCallParams))
|
|
23
|
+
return { unresolved: 'tool call has no params' };
|
|
24
|
+
const meta = toolCallParams._meta;
|
|
25
|
+
if (!isObject(meta))
|
|
26
|
+
return { unresolved: 'tool call _meta is absent' };
|
|
27
|
+
const threadId = meta.threadId;
|
|
28
|
+
if (typeof threadId !== 'string' || threadId.length === 0) {
|
|
29
|
+
return { unresolved: 'tool call _meta.threadId is absent' };
|
|
30
|
+
}
|
|
31
|
+
const turn = meta['x-codex-turn-metadata'];
|
|
32
|
+
if (!isObject(turn))
|
|
33
|
+
return { unresolved: 'tool call _meta.x-codex-turn-metadata is absent' };
|
|
34
|
+
const workspaces = turn.workspaces;
|
|
35
|
+
if (!isObject(workspaces))
|
|
36
|
+
return { unresolved: 'tool call _meta workspaces map is absent' };
|
|
37
|
+
const keys = Object.keys(workspaces);
|
|
38
|
+
if (keys.length === 0)
|
|
39
|
+
return { unresolved: 'workspaces map is empty; worktree root is ambiguous' };
|
|
40
|
+
if (keys.length > 1)
|
|
41
|
+
return { unresolved: `workspaces map has ${keys.length} roots; ambiguous` };
|
|
42
|
+
const worktree = keys[0];
|
|
43
|
+
// The worktree root must be an absolute path. A relative key (`.`, or an array's
|
|
44
|
+
// `0`) would be resolved against the daemon's own CWD, which is never a binding
|
|
45
|
+
// input — reject it before contacting the daemon.
|
|
46
|
+
if (!isAbsolute(worktree))
|
|
47
|
+
return { unresolved: `workspace root ${worktree} is not an absolute path` };
|
|
48
|
+
return { harness: 'codex', harness_session_id: threadId, worktree };
|
|
49
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The forwarder's harness-identity resolver.
|
|
3
|
+
*
|
|
4
|
+
* SC3 requires verified harness session context; CWD alone is insufficient, and
|
|
5
|
+
* ambiguous identity must FAIL rather than guess. The two harnesses expose
|
|
6
|
+
* identity by different mechanisms and at different times (see FINDINGS.md), so
|
|
7
|
+
* this dispatches by `clientInfo.name` at `initialize`:
|
|
8
|
+
* - claude-code → identity is the env triple, observable eagerly at initialize
|
|
9
|
+
* and cached (tool-call params carry no identity).
|
|
10
|
+
* - codex-mcp-client → identity arrives only per tool call, derived each time.
|
|
11
|
+
*
|
|
12
|
+
* Anything else — an unrecognized client, or a call before initialize — fails
|
|
13
|
+
* closed. `worktree`/`harness_session_id` here are DECLARED context the forwarder
|
|
14
|
+
* ships to the daemon; the daemon does the selection comparison.
|
|
15
|
+
*/
|
|
16
|
+
import { claudeIdentity } from "./harness-context/claude.js";
|
|
17
|
+
import { codexIdentity } from "./harness-context/codex.js";
|
|
18
|
+
export function isResolved(result) {
|
|
19
|
+
return !('unresolved' in result);
|
|
20
|
+
}
|
|
21
|
+
export const CLAUDE_CLIENT = 'claude-code';
|
|
22
|
+
export const CODEX_CLIENT = 'codex-mcp-client';
|
|
23
|
+
function isObject(v) {
|
|
24
|
+
return typeof v === 'object' && v !== null;
|
|
25
|
+
}
|
|
26
|
+
export function createHarnessContext() {
|
|
27
|
+
let resolve = () => ({
|
|
28
|
+
unresolved: 'harness not initialized',
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
initialize(clientInfo, env) {
|
|
32
|
+
const name = isObject(clientInfo) && typeof clientInfo.name === 'string' ? clientInfo.name : undefined;
|
|
33
|
+
if (name === CLAUDE_CLIENT) {
|
|
34
|
+
const cached = claudeIdentity(env); // eager: the env triple is available now
|
|
35
|
+
resolve = () => cached;
|
|
36
|
+
}
|
|
37
|
+
else if (name === CODEX_CLIENT) {
|
|
38
|
+
resolve = (params) => codexIdentity(params); // per-call: identity only at declaration
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const reason = `unrecognized harness clientInfo.name: ${name ?? '(absent)'}`;
|
|
42
|
+
resolve = () => ({ unresolved: reason });
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
identityForCall(toolCallParams) {
|
|
46
|
+
return resolve(toolCallParams);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
package/dist/health.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export function createHealth(durableSeq = 0n) {
|
|
2
|
+
let state = 'starting';
|
|
3
|
+
let seq = durableSeq;
|
|
4
|
+
let failure;
|
|
5
|
+
let gapPending = false;
|
|
6
|
+
const listeners = new Set();
|
|
7
|
+
return {
|
|
8
|
+
snapshot: () => ({
|
|
9
|
+
state,
|
|
10
|
+
durable_seq: seq.toString(),
|
|
11
|
+
...(failure ? { failure } : {}),
|
|
12
|
+
gap_pending: gapPending,
|
|
13
|
+
}),
|
|
14
|
+
setDurableSeq: (next) => {
|
|
15
|
+
seq = next;
|
|
16
|
+
// A listener that throws (violating its contract) must not corrupt the
|
|
17
|
+
// caller that just advanced the durable seq — e.g. break the append path
|
|
18
|
+
// between a durable commit and the bookkeeping that follows it. Report and
|
|
19
|
+
// keep notifying the rest.
|
|
20
|
+
for (const l of listeners) {
|
|
21
|
+
try {
|
|
22
|
+
l();
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
// Reporting must itself be crash-proof: String(err) can re-enter user
|
|
26
|
+
// code (a custom toString / Symbol.toPrimitive) that throws again, and
|
|
27
|
+
// that second exception would escape into the caller — re-opening the
|
|
28
|
+
// exact post-commit hole this catch exists to close.
|
|
29
|
+
let detail;
|
|
30
|
+
try {
|
|
31
|
+
detail = String(err);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
detail = '<unstringifiable listener error>';
|
|
35
|
+
}
|
|
36
|
+
console.error(`slipstream: health listener threw: ${detail}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
markHealthy: () => {
|
|
41
|
+
state = 'healthy';
|
|
42
|
+
failure = undefined;
|
|
43
|
+
gapPending = false;
|
|
44
|
+
},
|
|
45
|
+
markFailing: (next) => {
|
|
46
|
+
// Keep the first failure of an outage: it marks when acknowledgment
|
|
47
|
+
// stopped. A failed recovery attempt re-enters `failing` from
|
|
48
|
+
// `recovering`; preserve that original failure rather than overwriting it.
|
|
49
|
+
if (state !== 'failing') {
|
|
50
|
+
state = 'failing';
|
|
51
|
+
failure ??= next;
|
|
52
|
+
gapPending = true;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
markRecovering: () => {
|
|
56
|
+
state = 'recovering';
|
|
57
|
+
},
|
|
58
|
+
subscribe: (listener) => {
|
|
59
|
+
listeners.add(listener);
|
|
60
|
+
return () => {
|
|
61
|
+
listeners.delete(listener);
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|