@playola-radio/slipstream 0.1.0 → 0.3.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/README.md +427 -101
- package/SKILL.md +4 -1
- package/contracts/interface/v2/schema.json +3 -3
- package/dist/attach-workflow.js +557 -0
- package/dist/attribution-engine.js +2 -3
- package/dist/attribution-scoring.js +70 -13
- package/dist/boundary-registry.js +25 -0
- package/dist/capture-intent.js +70 -0
- package/dist/capture-scope.js +135 -0
- package/dist/cas.js +61 -14
- package/dist/claude-root-transcript.js +18 -11
- package/dist/cli.js +426 -26
- package/dist/clip-function-parser.js +9 -2
- package/dist/clip-projection-service.js +28 -0
- package/dist/clip-projection-worker.js +4 -0
- package/dist/clip-worker-pool.js +13 -1
- package/dist/codex-root-transcript.js +46 -0
- package/dist/conductor-settings.js +178 -0
- package/dist/conductor.js +313 -0
- package/dist/control-protocol.js +2 -1
- package/dist/daemon.js +1718 -470
- package/dist/engine.js +26 -10
- package/dist/ensure-daemon.js +30 -0
- package/dist/http-reader.js +20 -8
- package/dist/intent-store.js +196 -0
- package/dist/interface-range-resolver.js +28 -1
- package/dist/launch-agent.js +179 -0
- package/dist/lock.js +1 -1
- package/dist/public-events.js +26 -2
- package/dist/question-hook.js +11 -1
- package/dist/questions.js +102 -3
- package/dist/recovery.js +62 -6
- package/dist/session.js +604 -104
- package/dist/store-reader.js +112 -7
- package/dist/task-forwarder.js +8 -8
- package/dist/transcript/discovery.js +242 -111
- package/dist/transcript/fs-io.js +10 -1
- package/dist/transcript/scheduler.js +114 -0
- package/dist/transcript/watcher.js +5 -3
- package/dist/withdraw-workflow.js +106 -0
- package/dist/workspace.js +133 -0
- package/package.json +3 -2
- package/schemas/slipstream.agent.binding.v1.json +90 -0
- package/schemas/slipstream.agent.connection.v1.json +34 -0
- package/schemas/slipstream.agent.unlinked.v1.json +68 -0
- package/schemas/slipstream.capture.gap.v1.json +1 -0
- package/schemas/slipstream.capture.ready.v1.json +55 -0
- package/schemas/slipstream.capture.scope.v1.json +85 -0
- package/schemas/slipstream.capture.stopped.v1.json +65 -0
- package/schemas/slipstream.question.dispatch_attempted.v1.json +11 -1
- package/schemas/slipstream.question.exchange_recorded.v1.json +65 -0
- package/schemas/slipstream.question.relay_updated.v1.json +64 -0
- package/schemas/slipstream.session.resumed.v1.json +4 -1
- package/schemas/slipstream.session.started.v1.json +4 -1
- package/dist/transcript/runner.js +0 -62
package/README.md
CHANGED
|
@@ -1,132 +1,458 @@
|
|
|
1
1
|
# Slipstream
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
files at the end.**
|
|
3
|
+
**Watch your coding agent's work as it happens, instead of facing a pile of
|
|
4
|
+
changed files at the end.**
|
|
5
5
|
|
|
6
|
-
Slipstream
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
Slipstream is a local daemon that watches the folder an AI coding agent (Claude
|
|
7
|
+
Code, Codex, or any other) is working in and records every file change it
|
|
8
|
+
observes into an append-only event log. You can follow that log live in your
|
|
9
|
+
terminal, read it over a local HTTP API, or build your own viewer on top of it.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
The name is the idea: a slipstream is the low-pressure pocket behind a
|
|
12
|
+
fast-moving object. Following there costs far less than leading. Reviewing an
|
|
13
|
+
agent's changes as they land is easier than reviewing them all at once.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
> **Status: early and pre-1.0.** The capture engine, log format, reader API, and
|
|
16
|
+
> agent integrations work and are tested. They are developed and used daily on
|
|
17
|
+
> macOS. The event format may still change between releases. The Linux test
|
|
18
|
+
> suite runs in CI, but live capture on Linux gets far less use.
|
|
19
|
+
|
|
20
|
+
## What it is honest about
|
|
21
|
+
|
|
22
|
+
Slipstream makes narrow claims on purpose, and the rest of the design follows
|
|
23
|
+
from them.
|
|
24
|
+
|
|
25
|
+
- **It records observed states, not every write.** Slipstream watches the
|
|
26
|
+
filesystem, so it sees what a file looked like when it read it. If a file goes
|
|
27
|
+
`A → B → C` faster than Slipstream can read it, you see `A → C`. A file that is
|
|
28
|
+
created and deleted within a fraction of a second may never appear. When
|
|
29
|
+
Slipstream knows it missed something, it records an explicit gap event.
|
|
30
|
+
- **"Which agent made this change?" is a guess, and labeled as one.** Slipstream
|
|
31
|
+
can read an agent's local transcript and match file changes to the agent's tool
|
|
32
|
+
calls by timing. The result is marked `heuristic`, `ambiguous`, `unknown`, or
|
|
33
|
+
`pending`, and can be revised later. It is never presented as proof of
|
|
34
|
+
authorship.
|
|
35
|
+
- **Missing content is marked missing.** If a file could not be read (too large,
|
|
36
|
+
permission denied, removed mid-read), the event says so and gives the reason.
|
|
37
|
+
It never shows a fake empty file.
|
|
17
38
|
|
|
18
39
|
## Install
|
|
19
40
|
|
|
41
|
+
Requires **Node.js 24 or newer**. Day-to-day use is on **macOS**. Slipstream
|
|
42
|
+
watches files with [`@parcel/watcher`](https://github.com/parcel-bundler/watcher),
|
|
43
|
+
which also supports Linux.
|
|
44
|
+
|
|
20
45
|
```sh
|
|
21
46
|
npm install -g @playola-radio/slipstream
|
|
22
47
|
```
|
|
23
48
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(`npm run slipstream`, `npm test`) with no build step. To put a clone on your
|
|
27
|
-
PATH with `npm link`, run `npm run build` first, and again after pulling: the
|
|
28
|
-
linked command runs the compiled `dist/`, not the sources.
|
|
49
|
+
This installs two commands: `slipstream`, and `slipstream-mcp` (the connector
|
|
50
|
+
agents use, see [Group changes by task](#group-changes-by-task)).
|
|
29
51
|
|
|
30
|
-
|
|
52
|
+
**From a clone:** run `npm ci`, then `npm run slipstream -- <command>`. That
|
|
53
|
+
runs the TypeScript sources directly, with no build step. To put a clone's
|
|
54
|
+
commands on your PATH, run `npm run build && npm link`. Rebuild after pulling,
|
|
55
|
+
because the linked commands run the compiled `dist/`, not the sources.
|
|
31
56
|
|
|
32
|
-
|
|
57
|
+
## Quick start: watch a folder
|
|
58
|
+
|
|
59
|
+
The fastest way to see Slipstream work is to point it at a folder and follow
|
|
60
|
+
the feed from a second terminal.
|
|
61
|
+
|
|
62
|
+
**Terminal 1** — capture a folder and serve its feed:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
slipstream serve ~/code/my-project --store ~/.slipstream-demo
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
It prints the new session's ID:
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
slipstream: watching /Users/you/code/my-project
|
|
72
|
+
slipstream: session 3a8aee17-d12c-42f0-9798-f72d81819c0e
|
|
73
|
+
...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Terminal 2** — follow it live:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
slipstream view --store ~/.slipstream-demo --session <session-id>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
1 · slipstream.session.started.v1 · -
|
|
84
|
+
2 · slipstream.file.baselined.v1 · src/app.ts
|
|
85
|
+
3 · slipstream.capture.baseline.completed.v1 · -
|
|
86
|
+
4 · slipstream.enrichment.configured.v1 · -
|
|
87
|
+
5 · slipstream.file.changed.v1 · src/app.ts · 2751a3a
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Add `--changes` to see the changed lines of each file, not just one line per
|
|
91
|
+
event. Edit something in the project, or let an agent work there, and the
|
|
92
|
+
changes appear as they are captured. Press Ctrl-C in terminal 1 to stop. The
|
|
93
|
+
history stays in the store.
|
|
94
|
+
|
|
95
|
+
> If you create the store folder yourself, make it private first
|
|
96
|
+
> (`mkdir -m 700 <dir>`). Slipstream refuses to use a store that other users on
|
|
97
|
+
> the machine can read, because the store holds copies of your source code.
|
|
98
|
+
|
|
99
|
+
## What gets captured
|
|
33
100
|
|
|
34
|
-
|
|
101
|
+
In a git repository, Slipstream records the files git would include in a
|
|
102
|
+
commit. A file that git ignores and doesn't track is skipped. Git itself makes
|
|
103
|
+
that call, so it matches your `.gitignore` exactly. Outside a git repository,
|
|
104
|
+
every file is captured.
|
|
35
105
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
106
|
+
To skip more, add a **`.slipstreamignore`** at the root of the repository. It
|
|
107
|
+
uses `.gitignore` syntax. Unlike `.gitignore`, it can also skip files git
|
|
108
|
+
tracks, such as a committed but noisy generated file. Slipstream reads it when a
|
|
109
|
+
recording starts, so edits apply to the next recording. It only works inside a
|
|
110
|
+
git repository. Skipped files are never read or stored, and reports from the
|
|
111
|
+
reader API say when this filter was active.
|
|
39
112
|
|
|
40
|
-
|
|
41
|
-
goes `A → B → C` faster than it can be read, Slipstream reports `A → C`; an
|
|
42
|
-
`A → B → A` cycle can be invisible entirely. Those gaps are disclosed in the
|
|
43
|
-
event schema and surfaced in the UI rather than papered over.
|
|
113
|
+
## Connect an agent chat
|
|
44
114
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
115
|
+
For real work, Slipstream runs as one long-lived daemon that records the
|
|
116
|
+
workspace an agent chat is working in. From inside the Claude Code or Codex
|
|
117
|
+
chat, run this in the workspace (or ask the agent to run it):
|
|
48
118
|
|
|
49
|
-
|
|
119
|
+
```sh
|
|
120
|
+
slipstream attach
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This is the path for a chat that runs outside the Slipstream app, such as a
|
|
124
|
+
terminal or Conductor chat. The Slipstream app is being designed to link its own
|
|
125
|
+
chat for you instead, naming it explicitly, with no command to run.
|
|
126
|
+
|
|
127
|
+
It works out which chat it is in, starts the daemon if needed, and starts
|
|
128
|
+
recording. It also installs a hook and a tool in that workspace's agent config,
|
|
129
|
+
which let you [ask the agent about a change](#experimental-ask-the-agent-about-a-change).
|
|
130
|
+
If the chat has to reload to pick those up, `attach` prints the exact command.
|
|
131
|
+
Then:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
slipstream status # every capture, its state and linked chat, and the reader URL
|
|
135
|
+
slipstream status <session-id or dir> # one capture
|
|
136
|
+
slipstream view --store ~/.slipstream --session <capture-session-id>
|
|
137
|
+
slipstream unlink --session <id> # disconnect the chat; the workspace keeps recording
|
|
138
|
+
slipstream detach [<session-id or dir>] # stop recording (default: this directory); history is kept
|
|
139
|
+
```
|
|
50
140
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
Slipstream rides in through user-level MCP and skill configuration.
|
|
57
|
-
- **Claude Code and Codex CLI from day one**, via a capture path that does not
|
|
58
|
-
depend on either.
|
|
141
|
+
Several workspaces can record at once, each with its own session ID. To record
|
|
142
|
+
a workspace without connecting a chat, run `slipstream attach --watch-only [dir]`
|
|
143
|
+
(add `--wait` to return only once it is recording). Running `attach` from a chat
|
|
144
|
+
in a workspace that is already recording connects the chat to that same capture:
|
|
145
|
+
no new baseline and no gap.
|
|
59
146
|
|
|
60
|
-
|
|
147
|
+
After a detach, first resume recording with `slipstream attach --watch-only`,
|
|
148
|
+
then run `slipstream attach` from the chat. A chat attach by itself returns
|
|
149
|
+
`CAPTURE_PAUSED` and does not resume recording. Each capture has at most one
|
|
150
|
+
linked chat, and a chat is linked to at most one capture; attach explains and
|
|
151
|
+
changes nothing otherwise.
|
|
61
152
|
|
|
62
|
-
|
|
63
|
-
append-only event log; a browser client (React + Monaco) renders the feed. The
|
|
64
|
-
MCP server is a thin stdio forwarder per agent session — verified necessary,
|
|
65
|
-
since each session spawns its own server process — and the daemon owns the store.
|
|
153
|
+
Current limits:
|
|
66
154
|
|
|
155
|
+
- **Any Claude Code version can attach; Codex only in versions Slipstream has
|
|
156
|
+
verified.** A Claude Code chat attaches if its transcript matches the chat and
|
|
157
|
+
the workspace, and shows as setup pending until it answers the setup check.
|
|
158
|
+
For Codex the verified versions are 0.154.0 and 0.155.1, launched through its
|
|
159
|
+
SDK; anything else is refused and nothing is recorded. The Codex version
|
|
160
|
+
Conductor ships today isn't on the list yet, so Conductor's Codex chats are
|
|
161
|
+
refused for now.
|
|
162
|
+
- At most 32 workspaces record at once. Past that, an attach is refused
|
|
163
|
+
(`CAPTURE_LIMIT_REACHED`) and nothing is recorded or queued; pause another
|
|
164
|
+
workspace, then attach again. Freeing a slot starts nothing on its own.
|
|
165
|
+
- A paused workspace stays paused: a chat attach is refused (`CAPTURE_PAUSED`)
|
|
166
|
+
until the workspace is recorded again in Slipstream (or with
|
|
167
|
+
`slipstream attach --watch-only`).
|
|
168
|
+
- At most 10 chats are linked at once. Past that, the workspace keeps recording
|
|
169
|
+
without a chat (`BINDING_LIMIT_REACHED`); unlink another chat to connect it.
|
|
170
|
+
- Captures survive a daemon restart. Each one resumes under the same session
|
|
171
|
+
ID, with a `restart` gap covering the time nobody was watching. A linked chat
|
|
172
|
+
comes back as `setup_pending` and counts as connected again only after it
|
|
173
|
+
answers a fresh setup check. A workspace whose folder was deleted or replaced
|
|
174
|
+
in the meantime is not resumed; it shows as stopped (`root-missing`).
|
|
67
175
|
|
|
68
|
-
|
|
176
|
+
### Capture every Conductor workspace (optional host)
|
|
69
177
|
|
|
70
|
-
|
|
71
|
-
|
|
178
|
+
Conductor is an optional host. The Slipstream app can create and record
|
|
179
|
+
workspaces itself (see [Workspaces without Conductor](#workspaces-without-conductor));
|
|
180
|
+
this section is for capturing workspaces that Conductor creates.
|
|
72
181
|
|
|
73
182
|
```sh
|
|
74
|
-
slipstream
|
|
75
|
-
|
|
183
|
+
slipstream conductor install # once, and again after upgrading Node or Slipstream
|
|
184
|
+
slipstream conductor enroll # capture the workspaces that already exist
|
|
76
185
|
```
|
|
77
186
|
|
|
78
|
-
`
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
187
|
+
`install` adds a LaunchAgent that keeps the daemon running and restarts it after
|
|
188
|
+
a crash or a login (log: `~/Library/Logs/slipstream/daemon.log`). It also puts
|
|
189
|
+
`slipstream conductor setup` and `slipstream conductor archive` in your personal
|
|
190
|
+
Conductor scripts (`~/.conductor/settings.toml`), ahead of anything already
|
|
191
|
+
there. Both the LaunchAgent and the scripts use the absolute paths of the Node and
|
|
192
|
+
Slipstream that ran `install`; nothing depends on nvm or your shell profile.
|
|
193
|
+
|
|
194
|
+
A repo with its own Conductor setup or archive script overrides your personal one.
|
|
195
|
+
For each such repo, `install` writes `.conductor/settings.local.toml` in the repo's
|
|
196
|
+
folder. That file runs Slipstream first and then the repo's own command, and keeps
|
|
197
|
+
anything else in the file. It is for this machine only; `install` warns you if git
|
|
198
|
+
would commit it. If the repo's script changes later, `conductor setup` warns
|
|
199
|
+
until you run `install` again.
|
|
200
|
+
|
|
201
|
+
From then on, a new workspace starts recording before the repo's own setup runs.
|
|
202
|
+
The repo's own setup always runs, even when recording can't start (for example,
|
|
203
|
+
when all 32 capture slots are in use). In that case Slipstream prints the reason
|
|
204
|
+
and the setup script still fails, so the workspace shows that it isn't recording.
|
|
205
|
+
Archiving a workspace stops its capture first and keeps the history; the repo's
|
|
206
|
+
own archive script runs even if that stop fails.
|
|
207
|
+
Conductor does not wait for setup to finish before starting the agent. On this
|
|
208
|
+
machine (October 2026), the agent's first tool call came about 10–15 seconds
|
|
209
|
+
after the workspace was created, whatever the setup was doing. In a repo whose
|
|
210
|
+
setup builds container images, setup ran for about 100 seconds and the agent
|
|
211
|
+
made 14 tool calls before it ended. Slipstream runs first in setup, but whether
|
|
212
|
+
recording is ready before the agent's first edit was not measured. If the agent
|
|
213
|
+
edits first, the first recorded state is whatever was there when recording
|
|
214
|
+
started, and those earlier edits are not shown as changes. This is a known gap, accepted as is: Slipstream does not add a hook, a second capture source, or a wait to close it.
|
|
215
|
+
`slipstream workspace create` (below) does not have this gap: it waits for setup to
|
|
216
|
+
finish, and no agent exists in the workspace yet.
|
|
217
|
+
|
|
218
|
+
Conductor removes an archived workspace's folder. Archive scripts were seen to
|
|
219
|
+
run while the folder was still there (they could still use files in it), but
|
|
220
|
+
whether Conductor waits for an archive script to finish before removing the
|
|
221
|
+
folder was not observed. [real-use-check.md](docs/autocapture/real-use-check.md)
|
|
222
|
+
has a direct check: an archived workspace's capture should end `stopped
|
|
223
|
+
(archived)`, not `root-missing`.
|
|
224
|
+
|
|
225
|
+
`enroll` reads Conductor's own list of workspaces that aren't archived, including
|
|
226
|
+
sleeping ones. It captures them most recently active first. At 32 recording
|
|
227
|
+
workspaces, additional enrollments are refused and freeing a slot starts nothing
|
|
228
|
+
on its own. Archive a workspace you no longer use, then retry `enroll` or attach
|
|
229
|
+
the workspace after capacity is available. `enroll` is safe to run again.
|
|
230
|
+
|
|
231
|
+
**Recording any chat, without Ask Agent.** If your agent version isn't
|
|
232
|
+
verified, you can still record its workspace by naming the chat yourself:
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
slipstream start --enable claude-code & # use --enable codex for a Codex chat
|
|
236
|
+
# Wait until it prints "slipstream: reader ..." before attaching.
|
|
237
|
+
slipstream attach ~/code/my-project \
|
|
238
|
+
--harness claude-code --harness-session-id <chat-session-id>
|
|
92
239
|
```
|
|
93
240
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
`
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
`
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
the
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
241
|
+
`--harness` is `claude-code` or `codex`. The session ID is the chat
|
|
242
|
+
transcript's file name. `--enable` tells the daemon to read that harness's
|
|
243
|
+
transcripts and estimate which changes possibly came from the agent; without it,
|
|
244
|
+
Slipstream still records filesystem states but leaves that attribution unavailable:
|
|
245
|
+
|
|
246
|
+
- Claude Code: `~/.claude/projects/<project>/<session-id>.jsonl`
|
|
247
|
+
- Codex: `~/.codex/sessions/<yyyy>/<mm>/<dd>/rollout-<timestamp>-<session-id>.jsonl`
|
|
248
|
+
|
|
249
|
+
Everything about `attach`, including exactly which files it edits:
|
|
250
|
+
[docs/connect-agent-chat.md](docs/connect-agent-chat.md).
|
|
251
|
+
|
|
252
|
+
### Workspaces without Conductor
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
slipstream workspace create ~/code/myrepo
|
|
256
|
+
slipstream workspace archive ~/slipstream/workspaces/myrepo/workspace-1
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`create` makes a git worktree at `~/slipstream/workspaces/<repo>/workspace-N` on
|
|
260
|
+
a new branch `slipstream/workspace-N`, using the smallest N that is free. It
|
|
261
|
+
fetches `origin` and starts from `origin/develop`, or from origin's default
|
|
262
|
+
branch when there is no `develop`. A repo with no `origin` is refused. Then it
|
|
263
|
+
starts the daemon if needed, starts recording the workspace, and runs the repo's
|
|
264
|
+
committed setup script (`.conductor/settings.toml` or `conductor.json`, the same
|
|
265
|
+
files Conductor reads). It waits for setup to finish. Whenever the folder was
|
|
266
|
+
made, even if recording or setup then failed, it prints `workspace_path: <folder>`
|
|
267
|
+
twice: once before setup runs, and again as its last line. A failed setup leaves
|
|
268
|
+
the workspace in place.
|
|
269
|
+
|
|
270
|
+
`archive` only works on a folder directly under `~/slipstream/workspaces/<repo>/`.
|
|
271
|
+
It refuses a workspace with uncommitted or untracked changes (ignored files are
|
|
272
|
+
fine). Otherwise it stops the recording, runs the repo's archive script, and
|
|
273
|
+
removes the worktree. The branch is kept. If the archive script fails, the
|
|
274
|
+
worktree stays and you can run `archive` again.
|
|
275
|
+
|
|
276
|
+
Both scripts run in the workspace with `CONDUCTOR_ROOT_PATH` (the repo's main
|
|
277
|
+
checkout) and `CONDUCTOR_WORKSPACE_PATH` (the workspace) set, so scripts written
|
|
278
|
+
for Conductor work unchanged.
|
|
279
|
+
|
|
280
|
+
Open question: setup and archive scripts are read only from Conductor's files
|
|
281
|
+
(`.conductor/settings.toml`, `conductor.json`). Whether Slipstream gets its own
|
|
282
|
+
native config for them is not decided.
|
|
283
|
+
|
|
284
|
+
## Group changes by task
|
|
285
|
+
|
|
286
|
+
An agent can label its work so the feed groups changes under titles like "Fix
|
|
287
|
+
the pagination off-by-one" instead of by time alone. Slipstream offers this
|
|
288
|
+
through an MCP server, `slipstream-mcp`, which gives the agent a
|
|
289
|
+
`slipstream_begin_task` tool. The included [`SKILL.md`](SKILL.md) tells the
|
|
290
|
+
agent when to call it.
|
|
291
|
+
|
|
292
|
+
Copy-paste setup for Claude Code and Codex is in
|
|
293
|
+
[docs/mcp-forwarder-config.md](docs/mcp-forwarder-config.md). If the daemon isn't
|
|
294
|
+
running, the tool returns an error the agent is told to ignore. Slipstream never
|
|
295
|
+
blocks the agent's work.
|
|
296
|
+
|
|
297
|
+
## Build on it: the data is the interface
|
|
298
|
+
|
|
299
|
+
The bundled terminal viewer is just one client. Everything it shows is available
|
|
300
|
+
to any program you write, through two public routes. The daemon has no private
|
|
301
|
+
channel to its own UI.
|
|
302
|
+
|
|
303
|
+
**On disk.** The store (`~/.slipstream` by default) is plain files:
|
|
304
|
+
|
|
305
|
+
```text
|
|
306
|
+
~/.slipstream/
|
|
307
|
+
sessions/<session-id>/events.jsonl # append-only log, one JSON event per line
|
|
308
|
+
blobs/sha256/<ab>/<sha256> # file contents, stored by hash
|
|
309
|
+
runtime/<id>.json # how to reach the running HTTP reader
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Each line of `events.jsonl` is a [CloudEvents](https://cloudevents.io/)-style
|
|
313
|
+
record with a type such as `slipstream.file.changed.v1`. File contents are not
|
|
314
|
+
stored inline. Each change names its before and after content by SHA-256, and
|
|
315
|
+
the bytes live under `blobs/`. Contents are written to disk before any event
|
|
316
|
+
that refers to them, so a crash never leaves an event pointing at missing bytes.
|
|
317
|
+
Every event type has a JSON Schema in [`schemas/`](schemas/).
|
|
318
|
+
|
|
319
|
+
**Over HTTP.** The reader listens only on `127.0.0.1` and requires a bearer
|
|
320
|
+
token. Both the URL and the token are in the `runtime/*.json` file, readable only
|
|
321
|
+
by you.
|
|
322
|
+
|
|
323
|
+
```sh
|
|
324
|
+
STORE=~/.slipstream # or whichever --store you used
|
|
325
|
+
DESC=$(ls -t "$STORE"/runtime/*.json | head -1) # the newest running reader
|
|
326
|
+
URL=$(node -p "require('$DESC').url")
|
|
327
|
+
# Put the token in a private file so it never appears in the process list.
|
|
328
|
+
HDR=$(mktemp); node -p "'Authorization: Bearer ' + require('$DESC').token" > "$HDR"
|
|
329
|
+
|
|
330
|
+
curl -H @"$HDR" "$URL/v1/sessions"
|
|
331
|
+
curl -H @"$HDR" "$URL/v1/sessions/<id>/events?after=0&follow=true"
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
| Route | Returns |
|
|
335
|
+
|---|---|
|
|
336
|
+
| `GET /v1/sessions` | Captured sessions |
|
|
337
|
+
| `GET /v1/sessions/:id/events?after=N[&follow=true]` | Events after sequence `N`, streamed live with `follow=true` |
|
|
338
|
+
| `GET /v1/blobs/sha256/:hash` | Raw file contents |
|
|
339
|
+
| `GET /v1/sessions/:id/changes/:seq/clips` | The changed regions of one file change ([CLIP-PROJECTION.md](CLIP-PROJECTION.md)) |
|
|
340
|
+
| `GET /v1/sessions/:id/interfaces?before_seq=B&after_seq=A` | Functions added, removed, or changed from sequence `B` through `A` in a session, for TypeScript and Swift ([FUNCTION-CHANGES.md](FUNCTION-CHANGES.md)) |
|
|
341
|
+
| `GET /v1/schemas/:type` | The JSON Schema for an event type |
|
|
342
|
+
|
|
343
|
+
To show the feed the same way the bundled viewer does, follow
|
|
344
|
+
[DISPLAY-FOLD.md](DISPLAY-FOLD.md). It explains how to turn the event stream
|
|
345
|
+
into what's displayed, and includes test cases a new client can check itself
|
|
346
|
+
against.
|
|
347
|
+
|
|
348
|
+
## Experimental: ask the agent about a change
|
|
349
|
+
|
|
350
|
+
From a captured change, you can queue a question about specific lines
|
|
351
|
+
("Why is this check needed?"). Slipstream delivers it to the attached chat the
|
|
352
|
+
next time the agent uses a tool, and the agent's answer is recorded back into
|
|
353
|
+
the log.
|
|
354
|
+
|
|
355
|
+
```sh
|
|
356
|
+
slipstream ask --store ~/.slipstream --session <capture-id> \
|
|
357
|
+
--request-id <uuid> --input question.json
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
This needs a chat connected with the one-command `slipstream attach` above.
|
|
361
|
+
Until the chat confirms it can answer, questions are refused with
|
|
362
|
+
`AGENT_NOT_CONNECTED`. A successful `ask` means the question is safely queued,
|
|
363
|
+
not that the agent has seen it. The input format, limits, retry rules, and error
|
|
364
|
+
codes are in [docs/ask-agent/contract.md](docs/ask-agent/contract.md).
|
|
365
|
+
|
|
366
|
+
## Privacy and what Slipstream stores
|
|
367
|
+
|
|
368
|
+
- **Everything stays on your machine.** There is no telemetry and no network
|
|
369
|
+
access beyond the local reader on `127.0.0.1`.
|
|
370
|
+
- **The store contains real copies of your source files**, including any secrets
|
|
371
|
+
that pass through the watched folder and aren't excluded by git or
|
|
372
|
+
`.slipstreamignore`. It is created readable only by you.
|
|
373
|
+
- **Slipstream reads your agents' local transcripts** (under `~/.claude` and
|
|
374
|
+
`~/.codex`) to estimate which changes came from the agent. It never writes to
|
|
375
|
+
them.
|
|
376
|
+
- **`slipstream attach` edits the workspace's agent config** (`.claude/` or
|
|
377
|
+
`.codex/`) to add its hook and tool. Those entries contain local paths, so keep
|
|
378
|
+
them out of commits.
|
|
379
|
+
- **History is kept until you delete it.** While the daemon is detached,
|
|
380
|
+
`slipstream delete <session-id>` removes a session and `slipstream gc` removes
|
|
381
|
+
file contents no remaining session refers to.
|
|
382
|
+
|
|
383
|
+
## How it works
|
|
384
|
+
|
|
385
|
+
```text
|
|
386
|
+
agent edits files ─▶ worktree ─▶ file watcher ─▶ daemon ─▶ blobs/ + events.jsonl
|
|
387
|
+
│ │
|
|
388
|
+
agent's transcript ─(read-only, async)─▶ attribution┘ ▼
|
|
389
|
+
HTTP/SSE reader ─▶ any client
|
|
390
|
+
agent ─▶ slipstream-mcp (stdio, one per chat) ─▶ daemon control socket
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
- **The file watcher is the only capture source.** Agent transcripts can
|
|
394
|
+
refine *who probably* made a change, but never create a change event. Each
|
|
395
|
+
change is therefore recorded once.
|
|
396
|
+
- **Analysis never slows capture.** Transcript matching and code analysis run
|
|
397
|
+
separately and only add to the log. If a step fails, the view has less
|
|
398
|
+
detail, but no events are lost.
|
|
399
|
+
- **The daemon never launches your agent.** The Slipstream app can run the chat
|
|
400
|
+
itself; an outside chat joins through its own MCP and hook configuration. Each
|
|
401
|
+
chat starts its own small `slipstream-mcp` process, which only relays to the
|
|
402
|
+
single daemon that owns the store.
|
|
403
|
+
|
|
404
|
+
The full decision record is in [NOTES-architecture.md](NOTES-architecture.md).
|
|
405
|
+
|
|
406
|
+
## Command reference
|
|
407
|
+
|
|
408
|
+
| Command | Does |
|
|
409
|
+
|---|---|
|
|
410
|
+
| `watch [dir] [--store dir]` | Capture a folder into its own store, with no daemon or HTTP |
|
|
411
|
+
| `serve [dir] [--store dir]` | Same as `watch`, plus an HTTP reader |
|
|
412
|
+
| `start [--store dir] [--config file] [...]` | Run the shared daemon |
|
|
413
|
+
| `attach [dir]` | Run inside an outside agent chat (terminal, Conductor): connect it and start recording |
|
|
414
|
+
| `attach [dir] --harness h --harness-session-id id` | Record a worktree for a chat you name yourself |
|
|
415
|
+
| `status` / `detach` | Show or stop the active recording |
|
|
416
|
+
| `conductor install` / `conductor enroll` | Keep the daemon running and capture every Conductor workspace |
|
|
417
|
+
| `conductor setup` / `conductor archive` | Run by Conductor's setup and archive scripts |
|
|
418
|
+
| `workspace create <repo>` / `workspace archive <folder>` | Make a worktree workspace, set it up and record it / remove it |
|
|
419
|
+
| `view --store dir [--session id] [--changes] [--context N] [--full] [--disk]` | List sessions, or follow one live in the terminal |
|
|
420
|
+
| `ask --store dir --session id --request-id uuid --input file` | Queue a question for the agent (experimental) |
|
|
421
|
+
| `hook <codex\|claude-code> post-tool-use` | The hook command agents run to receive questions |
|
|
422
|
+
| `delete <session-id>` / `gc` | Remove a session / unused file contents (daemon detached) |
|
|
423
|
+
|
|
424
|
+
Run `slipstream` with no arguments for every option. The daemon commands accept
|
|
425
|
+
`--store <dir>`, which defaults to `~/.slipstream`; `view` needs it spelled out.
|
|
426
|
+
The daemon, the CLI, and `slipstream-mcp` must all use the same store.
|
|
427
|
+
|
|
428
|
+
## Contributing
|
|
429
|
+
|
|
430
|
+
Pull requests go to `develop`. Read [CLAUDE.md](CLAUDE.md) first. It lists the
|
|
431
|
+
rules that keep Slipstream honest, and the product decisions that need
|
|
432
|
+
discussion before a change.
|
|
433
|
+
|
|
434
|
+
```sh
|
|
435
|
+
npm run typecheck
|
|
436
|
+
npm test # fast, deterministic tier; runs in CI on macOS and Linux
|
|
437
|
+
npm run test:os # real-filesystem tier; needs macOS FSEvents
|
|
438
|
+
npm run test:tools
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
[TESTING.md](TESTING.md) covers the test layout and the fake filesystem the fast
|
|
442
|
+
tier uses.
|
|
443
|
+
|
|
444
|
+
### Design documents
|
|
445
|
+
|
|
446
|
+
| Document | Covers |
|
|
447
|
+
|---|---|
|
|
448
|
+
| [IMPLEMENTATION_PLAN.md](IMPLEMENTATION_PLAN.md) | Staged plan, success criteria, and status |
|
|
449
|
+
| [NOTES-architecture.md](NOTES-architecture.md) | Architecture decisions and the evidence behind them |
|
|
450
|
+
| [DISPLAY-FOLD.md](DISPLAY-FOLD.md) | How a client turns events into a display |
|
|
451
|
+
| [CLIP-PROJECTION.md](CLIP-PROJECTION.md), [FUNCTION-CHANGES.md](FUNCTION-CHANGES.md), [INTERFACE-PROJECTION.md](INTERFACE-PROJECTION.md) | Derived views: changed regions and function-level changes |
|
|
452
|
+
| [SWIFT-INTERFACE.md](SWIFT-INTERFACE.md), [INTERFACE-V2-TYPESCRIPT.md](INTERFACE-V2-TYPESCRIPT.md) | Language support for function-level changes |
|
|
453
|
+
| [STAGE-1-REPORT.md](STAGE-1-REPORT.md), [B2-MEASUREMENT-REPORT.md](B2-MEASUREMENT-REPORT.md) | Measured capture latency and loss |
|
|
454
|
+
| [docs/](docs/) | Agent setup, the ask-agent feature, reader API notes |
|
|
455
|
+
|
|
456
|
+
## License
|
|
457
|
+
|
|
458
|
+
Apache-2.0. Copyright Playola Radio, Incorporated. See [`LICENSE`](LICENSE).
|
package/SKILL.md
CHANGED
|
@@ -22,7 +22,10 @@ Each declaration marks the start of its own group; a second task is never a
|
|
|
22
22
|
retry of the first.
|
|
23
23
|
|
|
24
24
|
Do **not** call it for every individual edit, and do not re-call it to "resume"
|
|
25
|
-
a task you already declared — one declaration per task is enough.
|
|
25
|
+
a task you already declared — one declaration per task is enough. The one
|
|
26
|
+
exception: after `slipstream attach` runs again following a detach, declare
|
|
27
|
+
your current task again. A resumed attach starts with no current task; an
|
|
28
|
+
attach that reports `already_active` changes nothing and does not reset it.
|
|
26
29
|
|
|
27
30
|
## How to use it
|
|
28
31
|
|
|
@@ -98,8 +98,8 @@
|
|
|
98
98
|
},
|
|
99
99
|
"policy_exclusions": {
|
|
100
100
|
"type": "array",
|
|
101
|
-
"items": { "enum": ["store-directory", ".git", "symlinks"] },
|
|
102
|
-
"$comment": "
|
|
101
|
+
"items": { "enum": ["store-directory", ".git", "symlinks", "git-ignored", "slipstream-ignored"] },
|
|
102
|
+
"$comment": "Capture policy (checked in code; src/schema.ts const is not deep): these targets are never read, and the baseline records no event for them. Always [\"store-directory\", \".git\", \"symlinks\"], followed by \"git-ignored\" when a capture.scope event with policy git is recorded by after_seq: paths git ignores and does not track are outside capture. Then \"slipstream-ignored\" when such an event also carries slipstream_ignore active: a root .slipstreamignore excluded extra paths, including tracked ones."
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
},
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
"required": ["seq", "reason", "scope"],
|
|
152
152
|
"properties": {
|
|
153
153
|
"seq": { "$ref": "#/$defs/seq" },
|
|
154
|
-
"reason": { "enum": ["coalesced", "baseline-unreadable", "watcher-error", "restart", "storage"] },
|
|
154
|
+
"reason": { "enum": ["coalesced", "baseline-unreadable", "watcher-error", "restart", "storage", "capture-scope-unavailable"] },
|
|
155
155
|
"scope": {
|
|
156
156
|
"oneOf": [
|
|
157
157
|
{
|