sessionpipe 0.12.0 → 0.13.1

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 (47) hide show
  1. package/README.md +61 -7
  2. package/dist/ask.d.ts +40 -0
  3. package/dist/ask.d.ts.map +1 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +4439 -2047
  6. package/dist/cli.js.map +4 -4
  7. package/dist/connect.d.ts +6 -1
  8. package/dist/connect.d.ts.map +1 -1
  9. package/dist/control/autoupdate.d.ts +1 -0
  10. package/dist/control/autoupdate.d.ts.map +1 -1
  11. package/dist/control/channel.d.ts +43 -0
  12. package/dist/control/channel.d.ts.map +1 -0
  13. package/dist/control/cli.d.ts +13 -1
  14. package/dist/control/cli.d.ts.map +1 -1
  15. package/dist/control/daemon.d.ts +189 -2
  16. package/dist/control/daemon.d.ts.map +1 -1
  17. package/dist/control/inbox.d.ts +119 -0
  18. package/dist/control/inbox.d.ts.map +1 -0
  19. package/dist/control/local.d.ts +69 -4
  20. package/dist/control/local.d.ts.map +1 -1
  21. package/dist/control/pair.d.ts.map +1 -1
  22. package/dist/control/reply.d.ts +25 -1
  23. package/dist/control/reply.d.ts.map +1 -1
  24. package/dist/control/route.d.ts +50 -2
  25. package/dist/control/route.d.ts.map +1 -1
  26. package/dist/control/sdk.d.ts.map +1 -1
  27. package/dist/control/store.d.ts +49 -4
  28. package/dist/control/store.d.ts.map +1 -1
  29. package/dist/fold.d.ts +29 -0
  30. package/dist/fold.d.ts.map +1 -0
  31. package/dist/help.d.ts.map +1 -1
  32. package/dist/hook.d.ts.map +1 -1
  33. package/dist/hook.js +15 -5
  34. package/dist/hook.js.map +2 -2
  35. package/dist/index.js +1272 -257
  36. package/dist/index.js.map +4 -4
  37. package/dist/run.d.ts +41 -5
  38. package/dist/run.d.ts.map +1 -1
  39. package/dist/secrets.d.ts +14 -4
  40. package/dist/secrets.d.ts.map +1 -1
  41. package/dist/wellknown.d.ts +31 -0
  42. package/dist/wellknown.d.ts.map +1 -0
  43. package/dist/worker.d.ts +7 -1
  44. package/dist/worker.d.ts.map +1 -1
  45. package/dist/worker.js +1372 -279
  46. package/dist/worker.js.map +4 -4
  47. package/package.json +1 -1
package/README.md CHANGED
@@ -34,6 +34,18 @@ machines by their id, so the pairing holds and its two cards become one.
34
34
  | 2 | + your prompts and the assistant's text, redacted |
35
35
  | 3 | + tool input and output |
36
36
 
37
+ A tool call is two events (`tool.started`, `tool.ended`), and a workflow that runs a few
38
+ hundred subagents fires thousands a minute. So a receiver is sent a subagent's finished
39
+ tool calls (`tool.ended`) at most one per session per minute, plus the one that answers a
40
+ subagent's permission prompt, the main thread's tool events as they happen, and at most
41
+ one request a second from this machine with everything that piled up in it. `--tools` on
42
+ `sink add` picks the first: `all` sends every tool event (the default), `main` is what a
43
+ receiver that meters requests asks for in its well-known file and is used where it does,
44
+ `beat` thins the main thread's tool events the same way. The local outbox keeps every event whichever you
45
+ pick; turns, prompts, attention and subagents are never thinned. `replay --tools all`
46
+ sends every tool event once, for a receiver rebuilt from the outbox, and `replay` says
47
+ how many it thinned.
48
+
37
49
  No telemetry. Secrets never leave (`secrets@1` runs above tier 0, always). Your
38
50
  server or none. Pre-release: the reference receiver is still being built; see the
39
51
  [roadmap](https://github.com/spacesheep-dev/sessionpipe/blob/main/ROADMAP.md).
@@ -72,17 +84,36 @@ is missing or doesn't match, the session doesn't start.
72
84
 
73
85
  One daemon per computer holds one long-poll per receiver and delivers each message the
74
86
  cheapest way the session allows: to `sessionpipe wait` parked in the session's
75
- background (in place, ~60 ms, no network), at the session's next Stop if it is
76
- mid-turn, through the Agent SDK for a session it started (if the SDK is installed), or
77
- as a headless `claude -p --resume` (a `--fork-session` copy when a live process holds
78
- it). A session stays reachable in place with zero idle turns by running, as a
79
- background command with the longest timeout it can (Claude Code: raise
80
- `BASH_MAX_TIMEOUT_MS`, else a background command is stopped after 2 hours at most):
87
+ background (in place, ~60 ms, no network), at the session's next Stop if it is mid-turn,
88
+ through its channel if it has one (below), into the live session's own inbox if it is
89
+ idle (every Claude Code session on macOS and Linux has one, the Claude app's included;
90
+ the session reads such a message as a teammate's request, so it won't change its
91
+ settings or CLAUDE.md for it), through the Agent SDK for a session it started (if the
92
+ SDK is installed), or as a headless `claude -p --resume` (a `--fork-session` copy when a
93
+ live process holds it and nothing else reaches it). A session stays reachable in place
94
+ with zero idle turns by running, as a background command with the longest timeout it can
95
+ (Claude Code: raise `BASH_MAX_TIMEOUT_MS`, else a background command is stopped after 2
96
+ hours at most):
81
97
 
82
98
  ```sh
83
99
  sessionpipe wait # Claude Code: the session id comes from CLAUDE_CODE_SESSION_ID
84
100
  ```
85
101
 
102
+ Or let Claude Code keep the waiter for you, the way its Telegram plugin reaches a
103
+ session: `sessionpipe channel` is a Claude Code channel (an MCP server that pushes
104
+ messages in). It parks for its session the whole time the session runs, and each message
105
+ sent while the session is idle arrives in the session's own thread, in the terminal and
106
+ in the Claude app, as a message from `sessionpipe`, in the session's own permission
107
+ mode, and the session reads it as your own words. Channels are a research preview: a
108
+ channel that isn't on Anthropic's allowlist loads only with
109
+ `--dangerously-load-development-channels`, which asks once at startup, and a session the
110
+ Claude app starts through `claude remote-control` can't be given one yet.
111
+
112
+ ```sh
113
+ claude mcp add --scope user sessionpipe -- sessionpipe channel
114
+ claude --dangerously-load-development-channels server:sessionpipe
115
+ ```
116
+
86
117
  The waiter and next-Stop modes work in any Claude Code session that runs your hooks,
87
118
  the Claude desktop app's included, and need nothing else. The Agent SDK, resume and fork
88
119
  modes run the `claude` command, which has its own login (`claude auth login`); the
@@ -178,6 +209,29 @@ a signed `pool` command; turning it on or off, removing or reordering accounts,
178
209
  finishing a sign-in need a fresh confirm with your passkey or device key, never a day
179
210
  grant alone. `sessionpipe uninstall` runs `pool off` first.
180
211
 
212
+ ### Agents that ask agents
213
+
214
+ A session can hand a task to another agent on this machine and get the answer back:
215
+ `sessionpipe ask codex "review the diff on this branch"` runs Codex headlessly, waits,
216
+ and prints its reply on stdout (the child's id and progress on stderr), so Claude Code
217
+ can use Codex for a review, Codex can ask Gemini, and any harness with a shell can use
218
+ any other. The request never leaves the machine: it goes over the daemon's local socket
219
+ (the one `wait` uses), the daemon runs the agent in an allowed folder at `safe` (or
220
+ `read`; never wider than the machine's mode), caps it as it caps a start, and stops a
221
+ chain at two deep. Both
222
+ sessions' events say what happened (`delegation.started` / `delegation.ended` on the
223
+ asker, `delegated_by` on the answerer). A receiver can do the same from outside with
224
+ `reply_to` on a `start` or a `prompt`: the answer comes back into that session as a
225
+ message. `sessionpipe ask on` turns it on, with or without a receiver; `sessionpipe ask
226
+ setup` tells Claude Code and Codex the command exists, in their own instruction files,
227
+ since hooks never inject anything.
228
+
229
+ ```sh
230
+ sessionpipe ask on # once; the daemon runs as a service
231
+ sessionpipe ask codex "review the diff on this branch" # from inside any session; the answer is stdout
232
+ sessionpipe ask status # on or off, the daemon, the agents it can ask
233
+ ```
234
+
181
235
  ## Updates
182
236
 
183
237
  The control daemon keeps its own copy current. About a minute after it starts, and then
@@ -263,7 +317,7 @@ names and meanings as `doctor --json`:
263
317
  |---|---|
264
318
  | `version`, `machine`, `state`, `config` | Strings; state and config are absolute paths. |
265
319
  | `harnesses` | Object keyed by harness name; each value is an array of `{file, state, note?}` hook checks. `file` uses `~` for home, as doctor does. Includes detected **or configured** harnesses. |
266
- | `sinks` | Array of `{name, url, tier, max_tier, pii, control, paused, pending_sessions}`. `tier` is the configured tier; the effective tier is `min(tier, max_tier ?? 3)`. `max_tier` and `paused` are `null` when unset. No token or secret is included. |
320
+ | `sinks` | Array of `{name, url, tier, max_tier, pii, tools, control, paused, pending_sessions}`. `tier` is the configured tier; the effective tier is `min(tier, max_tier ?? 3)`. `max_tier` and `paused` are `null` when unset. `tools` is which tool events the sink is sent (`all`, `main`, `beat`), its default resolved. No token or secret is included. |
267
321
  | `timing` | `{n, p50, p95}` from the last 100 local hook timings, in milliseconds; `null` when unavailable. |
268
322
  | `update` | Doctor's update summary: `{auto, off, last_check, latest, last_error, pending, updated, failed}`. `auto` is boolean; absent details are `null`. |
269
323
  | `jobs` | Number of entries waiting in the local jobs directory; zero when absent. |
package/dist/ask.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ export declare const ASK_HELP = " sessionpipe ask <agent|harness:id> \"<task>\" [--cwd DIR] [--permission read|safe] [--model M] [--json] [--quiet]\n (hand a task to another agent here; its answer comes back on stdout;\n `--text -` reads the task from stdin, words after `--` are the task)\n sessionpipe ask on | off | status (whether sessions on this machine may ask each other; `on` is the\n person's to run at the terminal, never a session's)\n sessionpipe ask setup [--remove] (tell Claude Code and Codex the command exists)";
2
+ /** What the asker writes where. `out` is one line on stdout: the answer, or a status
3
+ * line of on/off/status/setup. `err` is stderr, raw: progress and what went wrong. */
4
+ export interface AskIo {
5
+ out: (s?: string) => void;
6
+ err: (s: string) => void;
7
+ /** Stdin, whole, for `--text -`. */
8
+ stdin?: () => string;
9
+ }
10
+ export interface AskOptions {
11
+ env?: NodeJS.ProcessEnv;
12
+ /** Where the CLI's dist lives: the service points at its cli.js. */
13
+ distDir?: string;
14
+ /** The agents this machine can run headlessly (tests stand in for the PATH walk). */
15
+ harnesses?: (env: NodeJS.ProcessEnv) => string[];
16
+ }
17
+ /** Run `sessionpipe ask …`; `argv` starts with "ask". Returns the exit code: 0 with the
18
+ * answer on stdout, 1 when the daemon refused or the run failed, 2 for a usage error. */
19
+ export declare function askMain(argv: string[], io: AskIo, o?: AskOptions): Promise<number>;
20
+ export declare const ASK_OPEN = "<!-- sessionpipe ask -->";
21
+ export declare const ASK_CLOSE = "<!-- /sessionpipe ask -->";
22
+ /** The section a model reads, between the markers; `agents` is what this machine can run. */
23
+ export declare function askSection(agents: string[]): string;
24
+ /** The files `ask setup` writes: every Claude Code config dir's CLAUDE.md, and Codex's
25
+ * AGENTS.md. A file is written when it exists or its harness dir does; a harness that
26
+ * isn't here is left alone. */
27
+ export declare function setupFiles(env?: NodeJS.ProcessEnv): string[];
28
+ /** `text` with the section in: replaced in place when one is there, else after what is
29
+ * there with one newline between (reversible byte for byte by withoutAskSection). */
30
+ export declare function withAskSection(text: string | null, section: string): string;
31
+ /** `text` without the section and the newline that joined it on: what the file held
32
+ * before setup wrote it. Unchanged when there is no section. */
33
+ export declare function withoutAskSection(text: string): string;
34
+ export type SetupDid = "added" | "updated" | "unchanged" | "removed" | "absent" | "skipped";
35
+ /** Write (or take out) the section in every file setupFiles names. */
36
+ export declare function setupAsk(env: NodeJS.ProcessEnv, agents: string[], remove: boolean): {
37
+ file: string;
38
+ did: SetupDid;
39
+ }[];
40
+ //# sourceMappingURL=ask.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../src/ask.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,QAAQ,8qBAK8E,CAAC;AAEpG;uFACuF;AACvF,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1B,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;CAClD;AAUD;0FAC0F;AAC1F,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAmC5F;AAmPD,eAAO,MAAM,QAAQ,6BAA6B,CAAC;AACnD,eAAO,MAAM,SAAS,8BAA8B,CAAC;AAErD,6FAA6F;AAC7F,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAenD;AAED;;gCAEgC;AAChC,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,EAAE,CAMzE;AAQD;sFACsF;AACtF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAK3E;AAED;iEACiE;AACjE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5F,sEAAsE;AACtE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,QAAQ,CAAA;CAAE,EAAE,CA8BrH"}
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAwEA,MAAM,WAAW,EAAE;IACjB,0BAA0B;IAC1B,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1B,kDAAkD;IAClD,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,8FAA8F;AAC9F,wBAAsB,IAAI,CACxB,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,GAAE,EAA2F,GAC9F,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAM5E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAsFA,MAAM,WAAW,EAAE;IACjB,0BAA0B;IAC1B,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1B,kDAAkD;IAClD,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,8FAA8F;AAC9F,wBAAsB,IAAI,CACxB,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,GAAE,EAA2F,GAC9F,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAM5E"}