@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.
Files changed (55) hide show
  1. package/README.md +427 -101
  2. package/SKILL.md +4 -1
  3. package/contracts/interface/v2/schema.json +3 -3
  4. package/dist/attach-workflow.js +557 -0
  5. package/dist/attribution-engine.js +2 -3
  6. package/dist/attribution-scoring.js +70 -13
  7. package/dist/boundary-registry.js +25 -0
  8. package/dist/capture-intent.js +70 -0
  9. package/dist/capture-scope.js +135 -0
  10. package/dist/cas.js +61 -14
  11. package/dist/claude-root-transcript.js +18 -11
  12. package/dist/cli.js +426 -26
  13. package/dist/clip-function-parser.js +9 -2
  14. package/dist/clip-projection-service.js +28 -0
  15. package/dist/clip-projection-worker.js +4 -0
  16. package/dist/clip-worker-pool.js +13 -1
  17. package/dist/codex-root-transcript.js +46 -0
  18. package/dist/conductor-settings.js +178 -0
  19. package/dist/conductor.js +313 -0
  20. package/dist/control-protocol.js +2 -1
  21. package/dist/daemon.js +1718 -470
  22. package/dist/engine.js +26 -10
  23. package/dist/ensure-daemon.js +30 -0
  24. package/dist/http-reader.js +20 -8
  25. package/dist/intent-store.js +196 -0
  26. package/dist/interface-range-resolver.js +28 -1
  27. package/dist/launch-agent.js +179 -0
  28. package/dist/lock.js +1 -1
  29. package/dist/public-events.js +26 -2
  30. package/dist/question-hook.js +11 -1
  31. package/dist/questions.js +102 -3
  32. package/dist/recovery.js +62 -6
  33. package/dist/session.js +604 -104
  34. package/dist/store-reader.js +112 -7
  35. package/dist/task-forwarder.js +8 -8
  36. package/dist/transcript/discovery.js +242 -111
  37. package/dist/transcript/fs-io.js +10 -1
  38. package/dist/transcript/scheduler.js +114 -0
  39. package/dist/transcript/watcher.js +5 -3
  40. package/dist/withdraw-workflow.js +106 -0
  41. package/dist/workspace.js +133 -0
  42. package/package.json +3 -2
  43. package/schemas/slipstream.agent.binding.v1.json +90 -0
  44. package/schemas/slipstream.agent.connection.v1.json +34 -0
  45. package/schemas/slipstream.agent.unlinked.v1.json +68 -0
  46. package/schemas/slipstream.capture.gap.v1.json +1 -0
  47. package/schemas/slipstream.capture.ready.v1.json +55 -0
  48. package/schemas/slipstream.capture.scope.v1.json +85 -0
  49. package/schemas/slipstream.capture.stopped.v1.json +65 -0
  50. package/schemas/slipstream.question.dispatch_attempted.v1.json +11 -1
  51. package/schemas/slipstream.question.exchange_recorded.v1.json +65 -0
  52. package/schemas/slipstream.question.relay_updated.v1.json +64 -0
  53. package/schemas/slipstream.session.resumed.v1.json +4 -1
  54. package/schemas/slipstream.session.started.v1.json +4 -1
  55. package/dist/transcript/runner.js +0 -62
package/README.md CHANGED
@@ -1,132 +1,458 @@
1
1
  # Slipstream
2
2
 
3
- **Review your coding agent's work as it happens, instead of facing a pile of
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 streams the changes an AI coding agent makes to your code, live, so
7
- you can follow along and review in near-real-time while the agent works. The name
8
- is the thesis: a slipstream is the low-pressure pocket behind a fast-moving
9
- object, and riding in it makes following far cheaper than leading.
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
- Status: **pre-implementation.** The architecture is settled and the MVP plan is
12
- written. No code yet.
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
- - [`IMPLEMENTATION_PLAN.md`](IMPLEMENTATION_PLAN.md) — the five-stage MVP plan
15
- - [`NOTES-architecture.md`](NOTES-architecture.md) — decision record, verified
16
- findings, and the Codex adversarial review
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
- Node.js 24 or newer is required. The published package ships compiled
25
- JavaScript; developing in this repo still runs the TypeScript sources directly
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
- ## License
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
- Apache-2.0. Copyright Playola Radio, Incorporated. See [`LICENSE`](LICENSE).
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
- ## What it promises — and does not
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
- > Slipstream streams **a live history of observed filesystem states, with
37
- > explicit coverage gaps.** It does not claim to be a complete record of every
38
- > write.
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
- Capture is watcher-primary, so it observes states rather than writes. If a file
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
- Similarly, attribution is revisable inference with an explicit status
46
- (`pending` / `heuristic` / `ambiguous` / `unknown`), never a verified claim of
47
- authorship. A timestamp near an agent's tool call is evidence, not proof.
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
- ## Design constraints
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
- - **The event schema is the public interface.** The on-disk JSONL log plus
52
- content-addressed blobs are the source of truth; the HTTP/SSE reader is a thin
53
- view over them; the bundled UI is one client among possible many. You must be
54
- able to delete the front-end and replace it.
55
- - **Slipstream does not launch agents.** Conductor stays the launcher.
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
- ## Architecture
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
- A headless local daemon (TypeScript/Node) captures bytes and writes an
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
- ## Queue a question about a captured change
176
+ ### Capture every Conductor workspace (optional host)
69
177
 
70
- With the shared daemon running (`slipstream start`) and explicitly attached to a
71
- Claude Code or Codex session, an independent local client can queue a question:
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 ask --store /path/to/store --session <capture-uuid> \
75
- --request-id <caller-generated-lowercase-uuid> --input question.json
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
- `question.json` contains the question and immutable after-snapshot identity read
79
- from the public events API (sequence numbers are decimal strings):
80
-
81
- ```json
82
- {
83
- "text": "Why is this check needed?",
84
- "context": {
85
- "change_seq": "42",
86
- "path": "src/example.ts",
87
- "snapshot_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
88
- "line_start": 3,
89
- "line_end": 5
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
- A follow-up adds `"reply_to_question_id"` naming the earlier question; its
95
- `context` must be identical. The example hash is a placeholder: use the actual change's `after.sha256`. The
96
- daemon validates the durable event and original blob, derives the selected text,
97
- and copies the explicit attach target. It never reads the current working file
98
- or accepts client-supplied authorship. Only after-content up to 1 MiB is eligible;
99
- select at most 200 lines / 16 KiB. Questions are trimmed and capped at 8192 UTF-8
100
- bytes. `serve` has no attach/control path and does not support `ask`.
101
-
102
- Success prints JSON `{v:1,ok:true,session_id,request_id,question_id,seq,
103
- queued_at_ms,expires_at_ms,duplicate}`. It means **durably queued**, not delivered
104
- to an agent or answered. Read `slipstream.question.queued.v1` through the existing
105
- authenticated `GET /v1/sessions/<id>/events` endpoint; its self-contained schema is
106
- available at `GET /v1/schemas/slipstream.question.queued.v1`.
107
-
108
- Codex and Claude Code delivery require an explicit root transcript binding at
109
- `attach` and a `PostToolUse` hook configured before the chat starts. See
110
- [the delivery contract](docs/ask-agent/contract.md#delivery-through-posttooluse)
111
- for the control fields and setup. The public
112
- `slipstream.question.dispatch_attempted.v1` event means the daemon committed an
113
- attempt before answering the hook; it does not confirm receipt or an answer.
114
- Attach Claude shortly after its first root tool call, and keep the chat's
115
- current directory at the attached worktree when claiming a question.
116
-
117
- Retry with the **same request ID, capture ID and input**. Same-ID/body retries
118
- return the original result without extending the 30-minute TTL. A changed body
119
- returns `REQUEST_CONFLICT`; more than 16 unexpired questions returns
120
- `QUESTION_LIMIT`. Other domain errors are `INVALID_QUESTION`, `INVALID_CONTEXT`,
121
- `SESSION_NOT_SELECTED`, `CAPTURE_NOT_READY`, and `STORAGE_UNAVAILABLE`. Errors
122
- print structured JSON on stderr (exit 1); local file/syntax errors exit 2.
123
- `STORAGE_UNAVAILABLE`, or a lost/malformed response after transmission, exits 3:
124
- the outcome is unknown and the record may already be committed. Preserve the same
125
- request ID, capture ID and input when retrying. A new capture never resumes an old queue; inspect
126
- the old public log by `request_id` rather than retargeting a retry.
127
-
128
- The control wire request is `{v:1,verb:"ask",session_id,request_id,text,context}`
129
- over the owner-only `<store>/control.sock` using existing NDJSON framing. No
130
- harness identity is required from the client. Full details, limits and lifecycle:
131
- [queue contract](docs/ask-agent/contract.md). Receiving-answer UI remains
132
- separate work.
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": "Static capture policy, always exactly [\"store-directory\", \".git\", \"symlinks\"] (checked in code; src/schema.ts const is not deep): these targets are never read, and the baseline records no event for them."
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
  {