@av-pi-studio/server 0.0.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 (315) hide show
  1. package/README.md +411 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/agent/agent-manager.d.ts +107 -0
  4. package/dist/agent/agent-manager.d.ts.map +1 -0
  5. package/dist/agent/agent-manager.js +218 -0
  6. package/dist/agent/agent-manager.js.map +1 -0
  7. package/dist/agent/agent-service.d.ts +36 -0
  8. package/dist/agent/agent-service.d.ts.map +1 -0
  9. package/dist/agent/agent-service.js +195 -0
  10. package/dist/agent/agent-service.js.map +1 -0
  11. package/dist/agent/index.d.ts +17 -0
  12. package/dist/agent/index.d.ts.map +1 -0
  13. package/dist/agent/index.js +18 -0
  14. package/dist/agent/index.js.map +1 -0
  15. package/dist/agent/manifest.d.ts +7 -0
  16. package/dist/agent/manifest.d.ts.map +1 -0
  17. package/dist/agent/manifest.js +27 -0
  18. package/dist/agent/manifest.js.map +1 -0
  19. package/dist/agent/mcp-server.d.ts +100 -0
  20. package/dist/agent/mcp-server.d.ts.map +1 -0
  21. package/dist/agent/mcp-server.js +163 -0
  22. package/dist/agent/mcp-server.js.map +1 -0
  23. package/dist/agent/permissions.d.ts +71 -0
  24. package/dist/agent/permissions.d.ts.map +1 -0
  25. package/dist/agent/permissions.js +132 -0
  26. package/dist/agent/permissions.js.map +1 -0
  27. package/dist/agent/provider-contract.d.ts +134 -0
  28. package/dist/agent/provider-contract.d.ts.map +1 -0
  29. package/dist/agent/provider-contract.js +2 -0
  30. package/dist/agent/provider-contract.js.map +1 -0
  31. package/dist/agent/provider-registry.d.ts +37 -0
  32. package/dist/agent/provider-registry.d.ts.map +1 -0
  33. package/dist/agent/provider-registry.js +92 -0
  34. package/dist/agent/provider-registry.js.map +1 -0
  35. package/dist/agent/provider-snapshot.d.ts +52 -0
  36. package/dist/agent/provider-snapshot.d.ts.map +1 -0
  37. package/dist/agent/provider-snapshot.js +73 -0
  38. package/dist/agent/provider-snapshot.js.map +1 -0
  39. package/dist/agent/providers/mock/mock-provider.d.ts +44 -0
  40. package/dist/agent/providers/mock/mock-provider.d.ts.map +1 -0
  41. package/dist/agent/providers/mock/mock-provider.js +162 -0
  42. package/dist/agent/providers/mock/mock-provider.js.map +1 -0
  43. package/dist/agent/providers/pi/agent.d.ts +80 -0
  44. package/dist/agent/providers/pi/agent.d.ts.map +1 -0
  45. package/dist/agent/providers/pi/agent.js +322 -0
  46. package/dist/agent/providers/pi/agent.js.map +1 -0
  47. package/dist/agent/providers/pi/event-mapper.d.ts +17 -0
  48. package/dist/agent/providers/pi/event-mapper.d.ts.map +1 -0
  49. package/dist/agent/providers/pi/event-mapper.js +150 -0
  50. package/dist/agent/providers/pi/event-mapper.js.map +1 -0
  51. package/dist/agent/providers/pi/rpc-transport.d.ts +53 -0
  52. package/dist/agent/providers/pi/rpc-transport.d.ts.map +1 -0
  53. package/dist/agent/providers/pi/rpc-transport.js +190 -0
  54. package/dist/agent/providers/pi/rpc-transport.js.map +1 -0
  55. package/dist/agent/providers/pi/session-hydration.d.ts +11 -0
  56. package/dist/agent/providers/pi/session-hydration.d.ts.map +1 -0
  57. package/dist/agent/providers/pi/session-hydration.js +167 -0
  58. package/dist/agent/providers/pi/session-hydration.js.map +1 -0
  59. package/dist/agent/rewind-rpc.d.ts +15 -0
  60. package/dist/agent/rewind-rpc.d.ts.map +1 -0
  61. package/dist/agent/rewind-rpc.js +30 -0
  62. package/dist/agent/rewind-rpc.js.map +1 -0
  63. package/dist/agent/session-operations.d.ts +32 -0
  64. package/dist/agent/session-operations.d.ts.map +1 -0
  65. package/dist/agent/session-operations.js +130 -0
  66. package/dist/agent/session-operations.js.map +1 -0
  67. package/dist/agent/structured-generation.d.ts +39 -0
  68. package/dist/agent/structured-generation.d.ts.map +1 -0
  69. package/dist/agent/structured-generation.js +78 -0
  70. package/dist/agent/structured-generation.js.map +1 -0
  71. package/dist/agent/timeline-rpc.d.ts +21 -0
  72. package/dist/agent/timeline-rpc.d.ts.map +1 -0
  73. package/dist/agent/timeline-rpc.js +59 -0
  74. package/dist/agent/timeline-rpc.js.map +1 -0
  75. package/dist/agent/timeline-store.d.ts +100 -0
  76. package/dist/agent/timeline-store.d.ts.map +1 -0
  77. package/dist/agent/timeline-store.js +255 -0
  78. package/dist/agent/timeline-store.js.map +1 -0
  79. package/dist/auth/index.d.ts +2 -0
  80. package/dist/auth/index.d.ts.map +1 -0
  81. package/dist/auth/index.js +3 -0
  82. package/dist/auth/index.js.map +1 -0
  83. package/dist/auth/password-auth.d.ts +44 -0
  84. package/dist/auth/password-auth.d.ts.map +1 -0
  85. package/dist/auth/password-auth.js +78 -0
  86. package/dist/auth/password-auth.js.map +1 -0
  87. package/dist/config/daemon-config.d.ts +634 -0
  88. package/dist/config/daemon-config.d.ts.map +1 -0
  89. package/dist/config/daemon-config.js +258 -0
  90. package/dist/config/daemon-config.js.map +1 -0
  91. package/dist/config/index.d.ts +3 -0
  92. package/dist/config/index.d.ts.map +1 -0
  93. package/dist/config/index.js +4 -0
  94. package/dist/config/index.js.map +1 -0
  95. package/dist/config/project-config.d.ts +104 -0
  96. package/dist/config/project-config.d.ts.map +1 -0
  97. package/dist/config/project-config.js +106 -0
  98. package/dist/config/project-config.js.map +1 -0
  99. package/dist/daemon/bootstrap.d.ts +37 -0
  100. package/dist/daemon/bootstrap.d.ts.map +1 -0
  101. package/dist/daemon/bootstrap.js +448 -0
  102. package/dist/daemon/bootstrap.js.map +1 -0
  103. package/dist/daemon/dev-bootstrap.d.ts +14 -0
  104. package/dist/daemon/dev-bootstrap.d.ts.map +1 -0
  105. package/dist/daemon/dev-bootstrap.js +231 -0
  106. package/dist/daemon/dev-bootstrap.js.map +1 -0
  107. package/dist/daemon/dev-main.d.ts +3 -0
  108. package/dist/daemon/dev-main.d.ts.map +1 -0
  109. package/dist/daemon/dev-main.js +36 -0
  110. package/dist/daemon/dev-main.js.map +1 -0
  111. package/dist/daemon/index.d.ts +15 -0
  112. package/dist/daemon/index.d.ts.map +1 -0
  113. package/dist/daemon/index.js +21 -0
  114. package/dist/daemon/index.js.map +1 -0
  115. package/dist/daemon/main.d.ts +3 -0
  116. package/dist/daemon/main.d.ts.map +1 -0
  117. package/dist/daemon/main.js +29 -0
  118. package/dist/daemon/main.js.map +1 -0
  119. package/dist/daemon/orchestration-rpc.d.ts +19 -0
  120. package/dist/daemon/orchestration-rpc.d.ts.map +1 -0
  121. package/dist/daemon/orchestration-rpc.js +136 -0
  122. package/dist/daemon/orchestration-rpc.js.map +1 -0
  123. package/dist/files/download-token-store.d.ts +14 -0
  124. package/dist/files/download-token-store.d.ts.map +1 -0
  125. package/dist/files/download-token-store.js +33 -0
  126. package/dist/files/download-token-store.js.map +1 -0
  127. package/dist/files/file-explorer.d.ts +54 -0
  128. package/dist/files/file-explorer.d.ts.map +1 -0
  129. package/dist/files/file-explorer.js +182 -0
  130. package/dist/files/file-explorer.js.map +1 -0
  131. package/dist/files/file-transfer.d.ts +30 -0
  132. package/dist/files/file-transfer.d.ts.map +1 -0
  133. package/dist/files/file-transfer.js +132 -0
  134. package/dist/files/file-transfer.js.map +1 -0
  135. package/dist/files/index.d.ts +4 -0
  136. package/dist/files/index.d.ts.map +1 -0
  137. package/dist/files/index.js +5 -0
  138. package/dist/files/index.js.map +1 -0
  139. package/dist/http/host-allowlist.d.ts +15 -0
  140. package/dist/http/host-allowlist.d.ts.map +1 -0
  141. package/dist/http/host-allowlist.js +52 -0
  142. package/dist/http/host-allowlist.js.map +1 -0
  143. package/dist/http/http-server.d.ts +27 -0
  144. package/dist/http/http-server.d.ts.map +1 -0
  145. package/dist/http/http-server.js +76 -0
  146. package/dist/http/http-server.js.map +1 -0
  147. package/dist/http/index.d.ts +3 -0
  148. package/dist/http/index.d.ts.map +1 -0
  149. package/dist/http/index.js +4 -0
  150. package/dist/http/index.js.map +1 -0
  151. package/dist/index.d.ts +18 -0
  152. package/dist/index.d.ts.map +1 -0
  153. package/dist/index.js +35 -0
  154. package/dist/index.js.map +1 -0
  155. package/dist/logging/index.d.ts +2 -0
  156. package/dist/logging/index.d.ts.map +1 -0
  157. package/dist/logging/index.js +2 -0
  158. package/dist/logging/index.js.map +1 -0
  159. package/dist/logging/logger.d.ts +48 -0
  160. package/dist/logging/logger.d.ts.map +1 -0
  161. package/dist/logging/logger.js +53 -0
  162. package/dist/logging/logger.js.map +1 -0
  163. package/dist/orchestration/chat-service.d.ts +61 -0
  164. package/dist/orchestration/chat-service.d.ts.map +1 -0
  165. package/dist/orchestration/chat-service.js +156 -0
  166. package/dist/orchestration/chat-service.js.map +1 -0
  167. package/dist/orchestration/cron.d.ts +30 -0
  168. package/dist/orchestration/cron.d.ts.map +1 -0
  169. package/dist/orchestration/cron.js +136 -0
  170. package/dist/orchestration/cron.js.map +1 -0
  171. package/dist/orchestration/index.d.ts +5 -0
  172. package/dist/orchestration/index.d.ts.map +1 -0
  173. package/dist/orchestration/index.js +6 -0
  174. package/dist/orchestration/index.js.map +1 -0
  175. package/dist/orchestration/loop-service.d.ts +129 -0
  176. package/dist/orchestration/loop-service.d.ts.map +1 -0
  177. package/dist/orchestration/loop-service.js +234 -0
  178. package/dist/orchestration/loop-service.js.map +1 -0
  179. package/dist/orchestration/schedule-service.d.ts +65 -0
  180. package/dist/orchestration/schedule-service.d.ts.map +1 -0
  181. package/dist/orchestration/schedule-service.js +201 -0
  182. package/dist/orchestration/schedule-service.js.map +1 -0
  183. package/dist/persistence/atomic-store.d.ts +45 -0
  184. package/dist/persistence/atomic-store.d.ts.map +1 -0
  185. package/dist/persistence/atomic-store.js +92 -0
  186. package/dist/persistence/atomic-store.js.map +1 -0
  187. package/dist/persistence/entity-schemas.d.ts +1707 -0
  188. package/dist/persistence/entity-schemas.d.ts.map +1 -0
  189. package/dist/persistence/entity-schemas.js +250 -0
  190. package/dist/persistence/entity-schemas.js.map +1 -0
  191. package/dist/persistence/entity-stores.d.ts +46 -0
  192. package/dist/persistence/entity-stores.d.ts.map +1 -0
  193. package/dist/persistence/entity-stores.js +167 -0
  194. package/dist/persistence/entity-stores.js.map +1 -0
  195. package/dist/persistence/index.d.ts +4 -0
  196. package/dist/persistence/index.d.ts.map +1 -0
  197. package/dist/persistence/index.js +5 -0
  198. package/dist/persistence/index.js.map +1 -0
  199. package/dist/projects/checkout-diff-manager.d.ts +44 -0
  200. package/dist/projects/checkout-diff-manager.d.ts.map +1 -0
  201. package/dist/projects/checkout-diff-manager.js +82 -0
  202. package/dist/projects/checkout-diff-manager.js.map +1 -0
  203. package/dist/projects/git-checkout-rpc.d.ts +18 -0
  204. package/dist/projects/git-checkout-rpc.d.ts.map +1 -0
  205. package/dist/projects/git-checkout-rpc.js +57 -0
  206. package/dist/projects/git-checkout-rpc.js.map +1 -0
  207. package/dist/projects/git-detect.d.ts +21 -0
  208. package/dist/projects/git-detect.d.ts.map +1 -0
  209. package/dist/projects/git-detect.js +34 -0
  210. package/dist/projects/git-detect.js.map +1 -0
  211. package/dist/projects/git-operations.d.ts +67 -0
  212. package/dist/projects/git-operations.d.ts.map +1 -0
  213. package/dist/projects/git-operations.js +200 -0
  214. package/dist/projects/git-operations.js.map +1 -0
  215. package/dist/projects/github-service.d.ts +80 -0
  216. package/dist/projects/github-service.d.ts.map +1 -0
  217. package/dist/projects/github-service.js +172 -0
  218. package/dist/projects/github-service.js.map +1 -0
  219. package/dist/projects/index.d.ts +13 -0
  220. package/dist/projects/index.d.ts.map +1 -0
  221. package/dist/projects/index.js +19 -0
  222. package/dist/projects/index.js.map +1 -0
  223. package/dist/projects/open-project.d.ts +35 -0
  224. package/dist/projects/open-project.d.ts.map +1 -0
  225. package/dist/projects/open-project.js +106 -0
  226. package/dist/projects/open-project.js.map +1 -0
  227. package/dist/projects/reconciliation.d.ts +15 -0
  228. package/dist/projects/reconciliation.d.ts.map +1 -0
  229. package/dist/projects/reconciliation.js +63 -0
  230. package/dist/projects/reconciliation.js.map +1 -0
  231. package/dist/projects/status-projection.d.ts +33 -0
  232. package/dist/projects/status-projection.d.ts.map +1 -0
  233. package/dist/projects/status-projection.js +98 -0
  234. package/dist/projects/status-projection.js.map +1 -0
  235. package/dist/projects/workspace-activity.d.ts +28 -0
  236. package/dist/projects/workspace-activity.d.ts.map +1 -0
  237. package/dist/projects/workspace-activity.js +74 -0
  238. package/dist/projects/workspace-activity.js.map +1 -0
  239. package/dist/projects/workspace-git-service.d.ts +27 -0
  240. package/dist/projects/workspace-git-service.d.ts.map +1 -0
  241. package/dist/projects/workspace-git-service.js +60 -0
  242. package/dist/projects/workspace-git-service.js.map +1 -0
  243. package/dist/projects/workspace-registry.d.ts +59 -0
  244. package/dist/projects/workspace-registry.d.ts.map +1 -0
  245. package/dist/projects/workspace-registry.js +149 -0
  246. package/dist/projects/workspace-registry.js.map +1 -0
  247. package/dist/projects/worktree-service.d.ts +100 -0
  248. package/dist/projects/worktree-service.d.ts.map +1 -0
  249. package/dist/projects/worktree-service.js +216 -0
  250. package/dist/projects/worktree-service.js.map +1 -0
  251. package/dist/proxy/index.d.ts +4 -0
  252. package/dist/proxy/index.d.ts.map +1 -0
  253. package/dist/proxy/index.js +5 -0
  254. package/dist/proxy/index.js.map +1 -0
  255. package/dist/proxy/service-hostname.d.ts +21 -0
  256. package/dist/proxy/service-hostname.d.ts.map +1 -0
  257. package/dist/proxy/service-hostname.js +44 -0
  258. package/dist/proxy/service-hostname.js.map +1 -0
  259. package/dist/proxy/service-port-registry.d.ts +36 -0
  260. package/dist/proxy/service-port-registry.d.ts.map +1 -0
  261. package/dist/proxy/service-port-registry.js +55 -0
  262. package/dist/proxy/service-port-registry.js.map +1 -0
  263. package/dist/proxy/service-proxy.d.ts +34 -0
  264. package/dist/proxy/service-proxy.d.ts.map +1 -0
  265. package/dist/proxy/service-proxy.js +54 -0
  266. package/dist/proxy/service-proxy.js.map +1 -0
  267. package/dist/terminal/index.d.ts +4 -0
  268. package/dist/terminal/index.d.ts.map +1 -0
  269. package/dist/terminal/index.js +5 -0
  270. package/dist/terminal/index.js.map +1 -0
  271. package/dist/terminal/pty-backend.d.ts +44 -0
  272. package/dist/terminal/pty-backend.d.ts.map +1 -0
  273. package/dist/terminal/pty-backend.js +119 -0
  274. package/dist/terminal/pty-backend.js.map +1 -0
  275. package/dist/terminal/screen-buffer.d.ts +21 -0
  276. package/dist/terminal/screen-buffer.d.ts.map +1 -0
  277. package/dist/terminal/screen-buffer.js +55 -0
  278. package/dist/terminal/screen-buffer.js.map +1 -0
  279. package/dist/terminal/terminal-manager.d.ts +79 -0
  280. package/dist/terminal/terminal-manager.d.ts.map +1 -0
  281. package/dist/terminal/terminal-manager.js +174 -0
  282. package/dist/terminal/terminal-manager.js.map +1 -0
  283. package/dist/terminal/terminal-rpc.d.ts +20 -0
  284. package/dist/terminal/terminal-rpc.d.ts.map +1 -0
  285. package/dist/terminal/terminal-rpc.js +122 -0
  286. package/dist/terminal/terminal-rpc.js.map +1 -0
  287. package/dist/util/concurrency.d.ts +16 -0
  288. package/dist/util/concurrency.d.ts.map +1 -0
  289. package/dist/util/concurrency.js +23 -0
  290. package/dist/util/concurrency.js.map +1 -0
  291. package/dist/util/index.d.ts +2 -0
  292. package/dist/util/index.d.ts.map +1 -0
  293. package/dist/util/index.js +2 -0
  294. package/dist/util/index.js.map +1 -0
  295. package/dist/ws/capability-store.d.ts +11 -0
  296. package/dist/ws/capability-store.d.ts.map +1 -0
  297. package/dist/ws/capability-store.js +10 -0
  298. package/dist/ws/capability-store.js.map +1 -0
  299. package/dist/ws/index.d.ts +5 -0
  300. package/dist/ws/index.d.ts.map +1 -0
  301. package/dist/ws/index.js +6 -0
  302. package/dist/ws/index.js.map +1 -0
  303. package/dist/ws/router.d.ts +34 -0
  304. package/dist/ws/router.d.ts.map +1 -0
  305. package/dist/ws/router.js +97 -0
  306. package/dist/ws/router.js.map +1 -0
  307. package/dist/ws/session.d.ts +28 -0
  308. package/dist/ws/session.d.ts.map +1 -0
  309. package/dist/ws/session.js +36 -0
  310. package/dist/ws/session.js.map +1 -0
  311. package/dist/ws/ws-server.d.ts +41 -0
  312. package/dist/ws/ws-server.d.ts.map +1 -0
  313. package/dist/ws/ws-server.js +104 -0
  314. package/dist/ws/ws-server.js.map +1 -0
  315. package/package.json +48 -0
package/README.md ADDED
@@ -0,0 +1,411 @@
1
+ # `@av-pi-studio/server`
2
+
3
+ The **Pi-Studio daemon** — the long-lived server process that runs on a developer's machine and is
4
+ the heart of Pi-Studio. It supervises AI-agent processes, PTY terminals, git worktrees, projects,
5
+ chat rooms, schedules, and loops, and exposes a single **WebSocket JSON+binary API** (plus a small
6
+ HTTP surface) that every client — the CLI, the web UI, and future native apps — connects to.
7
+
8
+ Your code never leaves your machine: the daemon runs locally, talks to the `pi` agent locally, and
9
+ persists all state under a local home directory.
10
+
11
+ ---
12
+
13
+ ## Table of contents
14
+
15
+ - [What the daemon does](#what-the-daemon-does)
16
+ - [Requirements](#requirements)
17
+ - [Install & build](#install--build)
18
+ - [Running the daemon](#running-the-daemon)
19
+ - [Configuration](#configuration)
20
+ - [The wire API](#the-wire-api)
21
+ - [Agent providers](#agent-providers)
22
+ - [Persistence](#persistence)
23
+ - [Architecture](#architecture)
24
+ - [Security model](#security-model)
25
+ - [Logging](#logging)
26
+ - [Development](#development)
27
+ - [Key invariants](#key-invariants)
28
+
29
+ ---
30
+
31
+ ## What the daemon does
32
+
33
+ A single daemon process owns all runtime state and mediates every operation:
34
+
35
+ - **Agents** — creates, runs, interrupts, updates, resumes, and archives AI coding-agent sessions
36
+ through a provider-neutral interface. Streams every turn event (assistant messages, reasoning,
37
+ tool calls, completion) to subscribed clients in real time.
38
+ - **Terminals** — spawns and multiplexes PTY processes over the same WebSocket using binary frames,
39
+ with screen-buffer snapshots so late subscribers see the current screen.
40
+ - **Projects & git** — opens projects, tracks workspaces, runs git status/branch/diff/commit,
41
+ manages worktrees, and integrates with the GitHub API for PRs/issues.
42
+ - **Orchestration** — chat rooms with `@mentions`, cron/interval **schedules** that fire agent
43
+ prompts, and iterative worker+verifier **loops**.
44
+ - **Files** — directory listing, text/binary file preview, and token-based chunked file download.
45
+ - **Service proxy** — an HTTP reverse proxy that routes to localhost services started by agents.
46
+
47
+ The daemon is **provider-agnostic**: the rest of the code only ever touches the `AgentClient` /
48
+ `AgentSession` interfaces in `src/agent/provider-contract.ts`. Two providers ship today — the real
49
+ `pi` provider and an in-process `mock`.
50
+
51
+ ---
52
+
53
+ ## Requirements
54
+
55
+ - **Node.js ≥ 20** (developed and tested on Node 24). ESM only (`"type": "module"`).
56
+ - **npm** with workspaces (this package lives in the Pi-Studio monorepo).
57
+ - For the real **`pi` provider**: pi *credentials* only. The `pi` CLI is bundled as a dependency
58
+ (`@earendil-works/pi-coding-agent`) — the daemon launches `node <pkg>/dist/cli.js --mode rpc`, so
59
+ **no global `pi` install is required**. Provide credentials via an API key
60
+ (`ANTHROPIC_API_KEY`, etc.) in the daemon's environment, or a configured `~/.pi/agent/auth.json`.
61
+ - The built-in **`mock` provider** needs **no credentials** and is ideal for smoke tests.
62
+
63
+ ---
64
+
65
+ ## Install & build
66
+
67
+ From the monorepo root:
68
+
69
+ ```bash
70
+ npm install # install all workspace deps
71
+ npm run build:server # build this package (compiles protocol + highlight first via project refs)
72
+ ```
73
+
74
+ Or build everything: `npm run build`.
75
+
76
+ The build emits `dist/`. The production entry point is `dist/daemon/main.js`, also exposed as the
77
+ `pi-studio-daemon` bin.
78
+
79
+ ---
80
+
81
+ ## Running the daemon
82
+
83
+ ### Simplest (from monorepo root)
84
+
85
+ ```bash
86
+ npm start # builds the server, then runs the daemon in the foreground
87
+ npm run start:server # runs the already-built daemon without rebuilding
88
+ ```
89
+
90
+ Directly:
91
+
92
+ ```bash
93
+ node packages/server/dist/daemon/main.js
94
+ ```
95
+
96
+ On startup the daemon prints its identity and readiness:
97
+
98
+ ```
99
+ pi-studio daemon listening on http://0.0.0.0:6767
100
+ serverId: 3f2a…
101
+ home: /home/you/.pi-studio
102
+ provider: pi
103
+ ws: ready
104
+ Press Ctrl+C to stop
105
+ ```
106
+
107
+ By default the daemon:
108
+
109
+ - listens on **`0.0.0.0:6767`** (override with `PI_STUDIO_LISTEN`) — reachable over the LAN
110
+ - stores all state under **`$PI_STUDIO_HOME`** (default **`~/.pi-studio`**)
111
+ - writes logs to **`$PI_STUDIO_HOME/logs/`**
112
+ - uses the **`pi`** provider
113
+
114
+ It runs in the foreground; **Ctrl-C** (SIGINT) or SIGTERM triggers a clean shutdown that closes the
115
+ HTTP/WS servers and releases resources.
116
+
117
+ ### Verify it's up
118
+
119
+ ```bash
120
+ curl http://127.0.0.1:6767/api/health
121
+ # → {"status":"ok"}
122
+ ```
123
+
124
+ `/api/health` is exempt from Host-allowlist and auth checks, so it always answers.
125
+
126
+ ### Dev daemon
127
+
128
+ `src/daemon/dev-main.ts` is a development entry that wires the full feature surface and binds
129
+ `0.0.0.0` with developer-friendly defaults. From the root:
130
+
131
+ ```bash
132
+ npm run dev:daemon
133
+ ```
134
+
135
+ > The production `bootstrap.ts` and dev `dev-bootstrap.ts` both register the full RPC surface;
136
+ > `bootstrap.ts` is production-grade (real provider, disk persistence, config loading) while
137
+ > `dev-bootstrap.ts` is for local testing and **must never be imported by `bootstrap.ts`**.
138
+
139
+ ---
140
+
141
+ ## Configuration
142
+
143
+ Configuration comes from two sources, merged with **environment variables winning** over the file:
144
+
145
+ 1. `$PI_STUDIO_HOME/config.json` (optional — a missing or corrupt file is treated as `{}`).
146
+ 2. Environment variables (overlaid last).
147
+
148
+ ### Environment variables
149
+
150
+ All optional.
151
+
152
+ | Variable | Default | Purpose |
153
+ |----------|---------|---------|
154
+ | `PI_STUDIO_HOME` | `~/.pi-studio` | State + config + logs directory |
155
+ | `PI_STUDIO_LISTEN` | `0.0.0.0:6767` | Daemon listen address (`host:port`) |
156
+ | `PI_STUDIO_PASSWORD` | _(unset)_ | Require this password for connections (bcrypt-checked) |
157
+ | `PI_STUDIO_HOSTNAMES` | `localhost,*.localhost` | Allowed `Host` header values (comma-separated, or `true` to allow all) |
158
+ | `PI_STUDIO_SERVER_ID` | _(persisted/generated)_ | Stable server identity |
159
+ | `PI_STUDIO_SERVICE_PROXY_LISTEN` | _(unset)_ | Service-proxy listen address |
160
+ | `PI_STUDIO_SERVICE_PROXY_PUBLIC_BASE_URL` | _(unset)_ | Public base URL advertised for proxied services |
161
+ | `PI_STUDIO_SERVICE_PROXY_ENABLED` | _(unset)_ | Enable the service proxy (`1`/`true`/`yes`/`on`) |
162
+
163
+ > Literal IP addresses always pass the Host allowlist, so binding `0.0.0.0` and connecting via the
164
+ > server's IP needs no extra config. To reach the daemon by **hostname**, add it to
165
+ > `PI_STUDIO_HOSTNAMES`.
166
+
167
+ Example — isolated home, custom port, password:
168
+
169
+ ```bash
170
+ PI_STUDIO_HOME=/tmp/pi-studio-dev \
171
+ PI_STUDIO_LISTEN=127.0.0.1:6790 \
172
+ PI_STUDIO_PASSWORD=hunter2 \
173
+ node packages/server/dist/daemon/main.js
174
+ ```
175
+
176
+ ### `config.json`
177
+
178
+ The persisted config is validated by a Zod schema (`src/config/daemon-config.ts`) with sane
179
+ defaults and `.passthrough()` tolerance for unknown/future keys. Notable sections:
180
+
181
+ ```json
182
+ {
183
+ "version": 1,
184
+ "daemon": {
185
+ "listen": "127.0.0.1:6767",
186
+ "hostnames": ["localhost", "*.localhost"],
187
+ "auth": { "password": "$2b$…bcrypt-hash…" },
188
+ "mcp": { "enabled": true, "injectIntoAgents": true },
189
+ "appendSystemPrompt": "",
190
+ "cors": { "allowedOrigins": [] },
191
+ "serviceProxy": { "enabled": false }
192
+ },
193
+ "agents": {
194
+ "providers": {
195
+ "pi": { "command": ["/abs/path/to/pi", "--mode", "rpc"] }
196
+ }
197
+ },
198
+ "log": { "level": "info", "format": "json" }
199
+ }
200
+ ```
201
+
202
+ To use a **different `pi` binary** than the bundled one, set
203
+ `agents.providers.pi.command` to an absolute path as shown above. Custom Pi-compatible profiles can
204
+ extend the `pi` provider via `"extends": "pi"` (a custom provider must also set a `label`).
205
+
206
+ ---
207
+
208
+ ## The wire API
209
+
210
+ All communication rides a **single WebSocket connection** per client.
211
+
212
+ ### Text frames — JSON envelopes discriminated by `type`
213
+
214
+ - `hello` (Client→Server, first frame) — handshake with `clientId`, `clientType`
215
+ (`mobile`/`browser`/`cli`/`mcp`), `protocolVersion`, optional `capabilities`.
216
+ - `status` (Server→Client) — `server_info` payload sent right after a successful hello.
217
+ - `ping` / `pong` — **JSON** liveness (not RFC 6455 ping, which browsers/RN can't send).
218
+ - `session` — the envelope wrapping every RPC request/response/broadcast (`{ type: "session",
219
+ message }`).
220
+ - `rpc_error` — a correlated error response (carries the originating `requestId`).
221
+
222
+ A non-`hello` first frame closes the socket. RPC names follow a dotted convention —
223
+ `domain.provider.operation.direction` (e.g. `agent.permission.respond.request`); legacy flat names
224
+ are accepted via aliases but never generated.
225
+
226
+ ### Binary frames — terminal + file transfer
227
+
228
+ Layout: `[1-byte opcode][1-byte slot][payload]`. The `slot` (0–255) demultiplexes multiple
229
+ terminals over the one connection. Codecs use `Uint8Array` (not Node `Buffer`) so they run
230
+ unchanged in browsers and React Native. **File downloads** also ride binary frames: a client
231
+ requests a token via the `file_download_token_request` RPC, then streams `Begin → Chunk* → End`
232
+ frames via `file_download_request`; uploads consume the same frame format.
233
+
234
+ ### HTTP surface
235
+
236
+ The HTTP server is intentionally minimal. Beyond liveness, its only application route in production
237
+ is the **service proxy** (reverse proxy to localhost services started by agents).
238
+
239
+ | Route | Auth | Purpose |
240
+ |-------|------|---------|
241
+ | `GET /api/health` | none | Liveness — `{ "status": "ok" }` |
242
+ | `OPTIONS *` | none | CORS preflight (`204`) |
243
+ | _other paths_ | bearer | Delegated to the service proxy; `404` if unmatched |
244
+
245
+ The request pipeline: health + preflight are exempt; then Host-allowlist (`403` on mismatch), CORS
246
+ headers, optional bearer auth (`401`), then application routes (`404` if unmatched).
247
+
248
+ The **schemas are the single source of truth** and are **append-only**: new fields are optional,
249
+ types are never narrowed, and fields/discriminants are never removed — so an older daemon can always
250
+ decode data written by a newer one. They live in `@av-pi-studio/protocol`.
251
+
252
+ ---
253
+
254
+ ## Agent providers
255
+
256
+ The daemon resolves a provider id string to an `AgentClient` via the `ProviderRegistry`. The only
257
+ surface the rest of the daemon depends on is `src/agent/provider-contract.ts`:
258
+
259
+ - `AgentClient.createSession(config, ctx)` → `AgentSession`
260
+ - `AgentSession.run(prompt, opts)` — start a turn; events emitted via `subscribe(handler)`
261
+ - `AgentSession.startTurn(prompt, opts)` — fire-and-forget turn start, returns `{ turnId }`
262
+ - `AgentSession.interrupt()` / `close()` / `update(patch)`
263
+ - `AgentSession.importSession(...)` — resume a provider-native session by its handle
264
+ - `RunOptions.images` carries `ImageAttachment[]` (wire shape `{ mimeType?, data? }`, base64); the
265
+ provider translates it into its native prompt-image format at the boundary.
266
+
267
+ ### `pi` (real)
268
+
269
+ - Spawns `pi --mode rpc` (bundled, or a configured `command`) and speaks **strict JSONL RPC** over
270
+ stdin/stdout (`PiRpcTransport`).
271
+ - `event-mapper.ts` maps raw Pi events (`assistant_message`, `tool_call`, `turn_completed`, …) into
272
+ the normalized `AgentStreamEvent` stream.
273
+ - Discovers models/modes via top-level `get_modes`/`get_models` RPCs (no scratch session).
274
+ - **Prompt images:** `startTurn` converts the wire shape `{ mimeType, data }` into Pi's
275
+ `ImageContent` shape `{ type: "image", data, mimeType }` before the `prompt` RPC.
276
+ - A literal `~` in `cwd` is expanded to the home directory before spawning.
277
+ - A missing/unresolvable `pi` surfaces as a clean `rpc_error` ("Pi provider unavailable…") rather
278
+ than crashing the daemon.
279
+
280
+ ### `mock` (in-process)
281
+
282
+ Emits synthetic events on a small timer loop. No credentials. Used for smoke tests and CI.
283
+
284
+ ---
285
+
286
+ ## Persistence
287
+
288
+ All state lives under `$PI_STUDIO_HOME/`. Every write goes through `AtomicStore`
289
+ (write-to-temp-then-rename) for crash safety.
290
+
291
+ ```
292
+ config.json Daemon config (password hash, provider overrides, service proxy, …)
293
+ server-id Stable server identity
294
+ logs/ Rotating NDJSON log files (pino)
295
+ agents/
296
+ <sanitized-cwd>/
297
+ <agentId>.json Agent record (status, config, timeline seq, labels, …)
298
+ chat/rooms.json Chat rooms + messages
299
+ loops/<loopId>.json Loop records
300
+ schedules/<scheduleId>.json Schedule records
301
+ projects.json Project registry
302
+ workspaces.json Workspace registry
303
+ ```
304
+
305
+ All entity schemas use `.passthrough()` and optional fields — unknown/future fields from a newer
306
+ daemon load silently, so there is no migration framework to maintain.
307
+
308
+ ---
309
+
310
+ ## Architecture
311
+
312
+ ```
313
+ src/
314
+ daemon/
315
+ main.ts Production entry: parse env, wire bootstrap.ts, listen, handle signals.
316
+ dev-main.ts Dev entry: wires dev-bootstrap.ts (all features, LAN bind).
317
+ bootstrap.ts Production handler wiring (full RPC surface, real provider, disk state).
318
+ dev-bootstrap.ts Dev handler wiring (local testing only).
319
+ orchestration-rpc.ts
320
+
321
+ agent/ Agent lifecycle, provider registry, timeline, permissions.
322
+ agent-manager.ts Single source of truth for agent state + FSM + persistence + broadcast.
323
+ agent-service.ts RPC handler wiring for agent operations.
324
+ provider-contract.ts AgentClient / AgentSession interfaces (the ONLY provider surface).
325
+ provider-registry.ts Register/resolve AgentClient by provider id.
326
+ timeline-store.ts Append/page/cursor the agent event log.
327
+ permissions.ts Park + resolve tool-call permission requests.
328
+ providers/pi/ Real Pi provider (spawn, JSONL transport, event mapper).
329
+ providers/mock/ In-process synthetic provider.
330
+
331
+ ws/ WebSocket server, per-connection Session, HandlerRegistry + frame router.
332
+ http/ HTTP server (/api/health, downloads), Host allowlist.
333
+ auth/ PasswordAuth (bcrypt + WS subprotocol bearer token).
334
+ config/ DaemonConfig (env + config.json merge) and per-project config.
335
+ persistence/ Zod entity schemas, JSON stores, AtomicStore.
336
+ terminal/ TerminalManager (PTY lifecycle, slot mux, snapshot, binary broadcast).
337
+ projects/ Workspaces, projects, git ops, worktrees, GitHub, reconciliation.
338
+ orchestration/ ChatService, ScheduleService, LoopService, cron.
339
+ files/ File explorer + chunked download token store.
340
+ proxy/ ServiceProxy + port registry for agent-started services.
341
+ logging/ Pino logger factory.
342
+ util/ Concurrency helpers.
343
+ ```
344
+
345
+ ### Lifecycle FSM
346
+
347
+ `AgentManager` enforces `initializing → idle ↔ running → error → closed`. Every transition persists
348
+ the record **and** broadcasts `agent_update` to subscribers. Archiving soft-deletes (sets
349
+ `archivedAt`). On startup, `running` agents are recovered (crash recovery), and `running` loops are
350
+ recovered as `stopped` with an interruption log entry.
351
+
352
+ For a deeper subsystem reference, see [`AGENTS.md`](AGENTS.md) in this package and the specs under
353
+ [`clean-room-scope/`](../../clean-room-scope/).
354
+
355
+ ---
356
+
357
+ ## Security model
358
+
359
+ - **Host-header allowlist** (`src/http/host-allowlist.ts`) rejects requests whose `Host` isn't
360
+ allowed — DNS-rebinding protection. Literal IPs always pass; `localhost`/`*.localhost` are always
361
+ allowed; add hostnames via `PI_STUDIO_HOSTNAMES`.
362
+ - **Password auth** (`src/auth/password-auth.ts`) — optional. When a password is configured it is
363
+ bcrypt-checked against either a `password` query param on the WS upgrade URL or a
364
+ `pi-studio-bearer.<base64(password)>` WS subprotocol. An unset password allows all connections
365
+ (fine for a trusted localhost-only setup; set one before exposing the daemon beyond a trusted
366
+ network).
367
+ - **Service-proxy auth bypass is intentional** — the proxy route is deliberately not gated by
368
+ daemon password auth (per spec).
369
+ - **RPC timeouts are operation-level**, never socket death — an `rpcTimeoutMs` expiry yields an
370
+ `rpc_error`, it does not close or reconnect the WebSocket.
371
+
372
+ ---
373
+
374
+ ## Logging
375
+
376
+ `createLogger(name, opts)` returns a `pino` logger that writes pretty output to stdout in
377
+ development and rotating NDJSON to `$PI_STUDIO_HOME/logs/` in production. The level comes from the
378
+ `log.level` config key (default `info`) or `LOG_LEVEL`.
379
+
380
+ ---
381
+
382
+ ## Development
383
+
384
+ ```bash
385
+ npm test -- --project packages/server # run this package's Vitest suite
386
+ npm run typecheck # tsc -b across all packages
387
+ npm run lint # oxlint
388
+ npm run fmt:check # oxfmt --check
389
+ ```
390
+
391
+ Tests are co-located as `*.test.ts` next to their source. Provider tests inject stub transports;
392
+ persistence tests use temporary directories; WebSocket tests use in-memory session stubs. Avoid
393
+ real wall-clock timers in tests — await real completion signals instead.
394
+
395
+ ---
396
+
397
+ ## Key invariants
398
+
399
+ 1. **`provider-contract.ts` is the only provider surface.** Never import `providers/pi/` or
400
+ `providers/mock/` from outside `agent/`.
401
+ 2. **Handler registration is explicit.** Register handlers in `bootstrap.ts`/`dev-bootstrap.ts`, not
402
+ via auto-discovery.
403
+ 3. **Agent status changes only via `AgentManager` transitions** — never mutate a record directly.
404
+ 4. **All entity + wire schemas use `.passthrough()` and optional fields** — newer data must load on
405
+ older daemons.
406
+ 5. **The wire protocol is append-only.** Never remove or narrow a field, never change a discriminant.
407
+ 6. **`dev-bootstrap.ts` must not be imported by `bootstrap.ts`.**
408
+ 7. **Binary frame codecs are cross-platform** (`Uint8Array`, no Node `Buffer`).
409
+ 8. **`~` in `cwd` is expanded server-side** before it reaches a provider.
410
+ ```
411
+