@lightupai/polaris 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.
package/PLAN.md ADDED
@@ -0,0 +1,438 @@
1
+ # Polaris: Bidirectional Coding Agent Session Bridge
2
+
3
+ ## Context
4
+
5
+ We're building the first feature for the Lightup project — a system that **captures every interaction** in a coding agent session (user messages, agent responses, tool calls, tool results) and **injects external messages** into the session. This enables multiplayer collaboration where teammates and external systems can observe and participate in coding agent sessions. The system is model-agnostic — it works with any coding agent (e.g., Claude Code, Cursor, Windsurf).
6
+
7
+ Polaris is a **SaaS service**. Organizations sign up, connect their Slack workspace, and team members authenticate via SSO. The cloud service is the central broker and system of record.
8
+
9
+ ## Organization & Auth
10
+
11
+ ### Org Signup (Admin, web app at polaris.dev)
12
+
13
+ 1. Admin visits `polaris.dev`, signs up with **Google SSO**
14
+ 2. Creates an **organization** (e.g., `lightup`)
15
+ 3. Admin dashboard:
16
+ - **Connect Slack workspace** — OAuth flow, polaris gets a bot token
17
+ - **Invite team members** — by email, or auto-join by email domain
18
+ - **Manage projects** — create, archive, view activity
19
+
20
+ ### Slack Connection (Admin, web app)
21
+
22
+ Admin clicks "Connect Slack" → Slack OAuth → polaris receives bot token + workspace user list (with emails). The **Slack bridge is server-side** — managed by the SaaS, not deployed by the user. One bridge per org.
23
+
24
+ ### User Auth (each team member, one-time per machine)
25
+
26
+ ```sh
27
+ npx @lightup/polaris login
28
+ ```
29
+
30
+ Opens browser → Google SSO (same provider as the org) → callback stores a token in `~/.polaris/credentials.json`.
31
+
32
+ The token carries:
33
+ - **User identity** — derived from Google profile (e.g., `user:manu`)
34
+ - **Org membership** — which org this user belongs to
35
+ - **Service URL** — the polaris SaaS endpoint
36
+
37
+ The `login` command also installs:
38
+ - Local daemon (as a persistent service — launchd on Mac, systemd on Linux)
39
+ - MCP server config → `~/.claude/` (global, works in every project and Claude Desktop)
40
+ - Hook config → `~/.claude/settings.json`
41
+ - Status line config → `~/.claude/settings.json`
42
+ - `/polaris` skill → `~/.claude/skills/polaris/SKILL.md`
43
+
44
+ **One command sets up everything.**
45
+
46
+ ### Identity Mapping (Slack ↔ Polaris)
47
+
48
+ The user's **email is the common key** between Google SSO and Slack:
49
+
50
+ - When admin connects Slack (OAuth), polaris gets the workspace user list with emails
51
+ - When a user logs in via Google SSO, polaris has their email
52
+ - **Automatic match**: polaris user `user:manu` (manu@lightup.com) ↔ Slack user `@manu` (manu@lightup.com)
53
+
54
+ When a polaris event is posted to Slack, it shows the user's Slack identity (avatar, display name). When someone posts in Slack, the bridge resolves Slack user → email → polaris identity.
55
+
56
+ **Edge cases:**
57
+ - **Email mismatch** (different Google vs Slack emails) — admin dashboard has manual override to link accounts
58
+ - **No Slack account** — polaris posts as the bot with attribution: `polaris-bot: [user:manu] built the auth middleware`
59
+ - **Slack-only advisor** (no polaris account) — Slack display name used as identity: `slack:krishna`
60
+
61
+ ## Participants: Humans and Agents
62
+
63
+ Every participant in polaris — whether human or AI agent — has an **identity** with a type prefix:
64
+
65
+ - `user:manu`, `user:krishna`, `user:priya` — humans (identity from SSO)
66
+ - `agent:test-writer`, `agent:security-reviewer` — agents
67
+ - `slack:someone` — Slack-only participants without a polaris account
68
+
69
+ Agents are **first-class participants**. They can be drivers or advisors, same as humans. Polaris treats them identically — the cloud service doesn't distinguish between human and agent clients.
70
+
71
+ **Spawning**: humans create sessions and start agents out of band (not managed by polaris in v1).
72
+
73
+ **Privileges & HITL**: agent permissions, approval flows, and escalation paths are configured outside polaris. Polaris's job is context pooling, capture, injection, and broadcast — not authorization.
74
+
75
+ **Addressed messaging**: every injection specifies a **target** — a specific session within the project. No blind broadcast into all sessions.
76
+
77
+ ## Data Model: Projects, Sessions, Drivers, Advisors
78
+
79
+ ### Organizations
80
+
81
+ An **organization** is the top-level account. Created via signup on `polaris.dev`. All projects, users, and integrations are scoped to an org.
82
+
83
+ ### Projects
84
+
85
+ A **project** is the context container within an org. All context is pooled at the project level. A project has a flat, human-readable name (e.g., `pj`). One Slack channel per project (`#pj`).
86
+
87
+ ### Sessions
88
+
89
+ A project contains one or more **sessions**, each representing a concurrent workstream (e.g., feature, bugfix). Sessions have names scoped to their project (e.g., `fxm`, `fxk`).
90
+
91
+ ### Drivers
92
+
93
+ Each session has **one driver** — a human or agent actively building in a coding agent (e.g., Claude Code). Multiple sessions in a project can have concurrent drivers (one per session). A driver's activity (prompts, agent responses, tool calls) is captured and broadcast.
94
+
95
+ ### Advisors
96
+
97
+ **Advisors** (human or agent) contribute context via Slack or another client. Advisory messages are injected into a **specific target session's** driver. Multiple advisors can participate simultaneously. Every injection must specify its target session.
98
+
99
+ ### Context Pooling
100
+
101
+ All events across all sessions in a project are persisted together as the project's shared context. This means:
102
+
103
+ - **Real-time**: advisors' messages are injected into the target driver's session immediately
104
+ - **Sibling activity**: events from other sessions (e.g., Krishna's `fxk` work) are available on-demand to a driver (e.g., Manu on `fxm`) but not auto-injected to avoid noise
105
+ - **Handoff/new session**: when a driver takes over or starts a new session, they can be seeded with full project context
106
+
107
+ ### Handoff
108
+
109
+ The driver role on a session can transfer from one person to another:
110
+
111
+ 1. Current driver releases (via command or API)
112
+ 2. Session goes to "open" state (no driver)
113
+ 3. New driver claims the session, connects their coding agent CLI
114
+ 4. New driver's session is seeded with context from project history
115
+ 5. Slack channel shows the transition
116
+
117
+ ## Setup Flow
118
+
119
+ ### Step 1: Admin signs up org (once)
120
+
121
+ On `polaris.dev`:
122
+ 1. Google SSO → org created
123
+ 2. Connect Slack workspace → OAuth
124
+ 3. Invite team (or set auto-join by email domain)
125
+
126
+ ### Step 2: User sets up machine (once per machine)
127
+
128
+ ```sh
129
+ npx @lightup/polaris login
130
+ ```
131
+
132
+ Browser opens → Google SSO → token stored → daemon installed → MCP/hooks/skill/status line configured in `~/.claude/`. Done.
133
+
134
+ ### Step 3: User connects to a session (each time)
135
+
136
+ Inside any coding agent:
137
+ ```
138
+ /polaris join my-project fxm
139
+ ```
140
+
141
+ Project is scoped to the user's org (from token). User identity is from SSO. Session is created if it doesn't exist, or joined if it does. The Slack channel `#my-project` is auto-created by the server-side bridge if this is the first session in this project.
142
+
143
+ That's the complete flow: **one web signup, one CLI command, one slash command.**
144
+
145
+ ## Example: Project `pj` with Humans + Agents
146
+
147
+ ```
148
+ Project pj (org: lightup)
149
+ ├── session fxm driver: user:manu auth middleware
150
+ ├── session fxk driver: user:krishna database schema
151
+ ├── session tests driver: agent:test-writer tests for fxm + fxk
152
+
153
+ ├── advisor: agent:security-reviewer (watches project, advises sessions)
154
+ └── advisor: user:priya (via Slack)
155
+ ```
156
+
157
+ 1. **Manu** creates project `pj` with session `fxm`, connects as driver
158
+ 2. **Krishna** creates session `fxk`, connects as driver
159
+ 3. `agent:test-writer` is started out of band, drives session `tests`
160
+ 4. `agent:security-reviewer` connects as an advisor, watching project events
161
+ 5. Slack `#pj` shows an interleaved, attributed log:
162
+
163
+ ```
164
+ [user:manu/fxm → agent] "Let's implement the auth middleware"
165
+ [agent → user:manu/fxm] "I'll create src/middleware/auth.ts..."
166
+ [user:krishna/fxk → agent] "Set up the database schema for users"
167
+ [agent → user:krishna/fxk] "Creating migrations/001_users.sql..."
168
+ [user:priya → fxk] "Remember we need GDPR compliance on the users table"
169
+ [agent → user:krishna/fxk] "Good point from Priya. Adding data retention fields..."
170
+ [agent:security-reviewer → fxm] "This auth endpoint needs rate limiting"
171
+ [agent → user:manu/fxm] "Adding rate limiting middleware..."
172
+ [agent:test-writer/tests → agent] "Writing integration tests for auth middleware"
173
+ [agent → agent:test-writer/tests] "Created tests/auth.test.ts..."
174
+ [user:manu/fxm → agent] "What has Krishna done on fxk so far?"
175
+ [agent → user:manu/fxm] "Krishna has set up the users schema with GDPR fields..."
176
+ ```
177
+
178
+ 6. Manu finishes `fxm`, hands off `fxk` to himself to continue Krishna's work
179
+ 7. The Slack log is continuous and complete — humans, agents, handoffs, all in one stream
180
+
181
+ ## Architecture
182
+
183
+ ```
184
+ ┌───────────────────────────────────────────────────────────────┐
185
+ │ Polaris SaaS (polaris.dev) │
186
+ │ │
187
+ │ Web App: │
188
+ │ Signup (Google SSO) | Org dashboard | Slack OAuth │
189
+ │ │
190
+ │ Cloud Service (Broker / System of Record): │
191
+ │ REST API (all endpoints authenticated via token) │
192
+ │ POST /projects create │
193
+ │ GET /projects/:proj metadata │
194
+ │ GET /projects/:proj/messages full history │
195
+ │ GET /projects/:proj/events SSE (all) │
196
+ │ POST /projects/:proj/sessions create session │
197
+ │ GET /projects/:proj/sessions/:sess metadata │
198
+ │ POST /projects/:proj/sessions/:sess/events push │
199
+ │ GET /projects/:proj/sessions/:sess/events SSE │
200
+ │ POST /projects/:proj/sessions/:sess/inject inject │
201
+ │ GET /projects/:proj/sessions/:sess/messages history │
202
+ │ POST /projects/:proj/sessions/:sess/handoff release │
203
+ │ POST /projects/:proj/sessions/:sess/driver claim │
204
+ │ │
205
+ │ WebSocket (authenticated): │
206
+ │ /projects/:proj/ws project-level │
207
+ │ /projects/:proj/sessions/:sess/ws session-level │
208
+ │ │
209
+ │ Slack Bridge (server-side, managed per org): │
210
+ │ project ↔ Slack channel (auto-created) │
211
+ │ Email-based identity mapping (SSO ↔ Slack) │
212
+ │ │
213
+ │ Persistence: Postgres (Hetzner VPS) │
214
+ └────────┬──────────────────────────────────────────────────────┘
215
+
216
+ │ authenticated WebSocket + REST
217
+
218
+ ┌────────┴──────────────────────────────────────────────────────┐
219
+ │ User's Machine │
220
+ │ │
221
+ │ Daemon (port 4321, one per machine): │
222
+ │ Routes hook events by CC session_id │
223
+ │ Manages WS connections to cloud (one per polaris session) │
224
+ │ Auth token from ~/.polaris/credentials.json │
225
+ │ │
226
+ │ MCP Server (one per coding agent session, stdio): │
227
+ │ Registers with daemon on startup │
228
+ │ polaris_connect | polaris_reply | polaris_context tools │
229
+ │ claude/channel capability for advisor injection │
230
+ │ │
231
+ │ Hooks (capture.sh → POST localhost:4321/events) │
232
+ │ Status Line (queries daemon for connection state) │
233
+ │ /polaris Skill (slash command UX) │
234
+ └───────────────────────────────────────────────────────────────┘
235
+ ```
236
+
237
+ ## File Structure
238
+
239
+ ```
240
+ src/
241
+ types.ts # Shared event types + zod schemas
242
+ service/
243
+ server.ts # Cloud service: HTTP + WebSocket + persistence
244
+ db.ts # Postgres persistence layer
245
+ auth.ts # Token validation, Google SSO, org scoping
246
+ daemon/
247
+ daemon.ts # Local daemon: hook routing, WS management
248
+ client/
249
+ client.ts # MCP channel server (stdio only)
250
+ slack/
251
+ bridge.ts # Slack ↔ cloud bridge (server-side, Socket Mode)
252
+ format.ts # Event → Slack mrkdwn formatting
253
+ identity.ts # Email-based Slack ↔ polaris user mapping
254
+ cli/
255
+ cli.ts # CLI: login, daemon, status
256
+ web/
257
+ app.ts # Web app: signup, dashboard, Slack OAuth
258
+ hooks/
259
+ capture.sh # Shell script: reads hook JSON stdin, POSTs to daemon
260
+ statusline.sh # Status line script: queries daemon, formats output
261
+ skills/
262
+ polaris/SKILL.md # /polaris slash command skill definition
263
+ tests/
264
+ types.test.ts # Schema validation tests
265
+ db.test.ts # Persistence layer tests
266
+ service.test.ts # Cloud service API tests
267
+ auth.test.ts # Auth + org scoping tests
268
+ daemon.test.ts # Daemon routing tests
269
+ client.test.ts # MCP client tests
270
+ slack.test.ts # Slack bridge tests
271
+ format.test.ts # Formatting tests
272
+ identity.test.ts # Identity mapping tests
273
+ capture.test.ts # Hook script test
274
+ tsconfig.json
275
+ .mcp.json # MCP server registration
276
+ docker-compose.yml # Local dev (Postgres)
277
+ ```
278
+
279
+ ## Implementation Phases
280
+
281
+ ### Phase 1: Types + Persistence ✓
282
+ - `src/types.ts` — zod schemas for participant IDs, hook payloads, inject/reply messages, event envelope, project/session models
283
+ - `src/service/db.ts` — Postgres persistence layer
284
+ - Tests, deps, tsconfig
285
+
286
+ ### Phase 2: Cloud Service ✓
287
+ - `src/service/server.ts` — Bun HTTP + WebSocket server with full REST API
288
+ - Project and session endpoints, WebSocket at project and session levels
289
+ - Tests
290
+
291
+ ### Phase 3: Local Client + Hooks ✓ (v1, pre-daemon)
292
+ - `src/client/client.ts` — MCP channel server + HTTP relay + WS connection
293
+ - `hooks/capture.sh` — hook event relay
294
+ - Tests
295
+
296
+ ### Phase 4: Auth + Org Model
297
+ - `src/service/auth.ts` — Google SSO, token issuance/validation, org scoping
298
+ - `src/web/app.ts` — web app: signup flow, dashboard, Slack OAuth
299
+ - Add org + user tables to Postgres schema
300
+ - All API endpoints require auth, scope to org
301
+ - `src/cli/cli.ts` — `polaris login` command (browser SSO flow + local setup)
302
+
303
+ ### Phase 5: Local Daemon + `/polaris` Skill + Status Line
304
+
305
+ The local architecture uses a **single daemon per machine** that routes between multiple concurrent coding agent sessions. Users interact through the `/polaris` slash command.
306
+
307
+ #### `/polaris` Slash Command (Skill)
308
+
309
+ `skills/polaris/SKILL.md` — the primary UX. Installed to `~/.claude/skills/polaris/` by `polaris login`.
310
+
311
+ - `/polaris join <project> <session>` — connects the current coding agent session to a polaris session. Creates the session if it doesn't exist. Project scoped to user's org (from token). User identity from SSO.
312
+ - `/polaris` (no args) — shows status: connected project/session/user, or "not connected".
313
+ - `/polaris disconnect` — disconnects from the current session.
314
+
315
+ Example:
316
+ ```
317
+ /polaris join pj fxm ← connect to project pj, session fxm
318
+ /polaris ← check status
319
+ /polaris join pj fxk ← switch to different session
320
+ /polaris disconnect ← disconnect
321
+ ```
322
+
323
+ #### Status Line
324
+
325
+ Persistent bar at the bottom of the coding agent CLI. Always visible, updates after every turn.
326
+
327
+ When connected:
328
+ ```
329
+ polaris: pj/fxm (user:manu) ● connected
330
+ ```
331
+
332
+ When disconnected:
333
+ ```
334
+ polaris: not connected
335
+ ```
336
+
337
+ - `hooks/statusline.sh` — queries daemon's `GET /status/:cc-session-id`, formats output
338
+ - Installed to `~/.claude/settings.json` by `polaris login`
339
+
340
+ #### Daemon
341
+
342
+ `src/daemon/daemon.ts` — persistent process on port 4321 (one per machine):
343
+ - `POST /events` ← hooks POST here (routes via `session_id` in hook JSON)
344
+ - `POST /register` ← MCP servers register their CC session → polaris session mapping on startup
345
+ - `POST /connect` ← `/polaris join` triggers this to bind a CC session to a polaris session
346
+ - `POST /disconnect` ← `/polaris disconnect` triggers this
347
+ - `GET /status/:cc-session-id` ← status line script queries this
348
+ - Auth token from `~/.polaris/credentials.json` for all cloud API calls
349
+ - Manages WebSocket connections to cloud service (one per active polaris session)
350
+ - Routes advisor messages from cloud to the correct MCP server instance
351
+
352
+ #### MCP Client
353
+
354
+ `src/client/client.ts` — MCP channel server (stdio only, no HTTP):
355
+ - Registers with daemon on startup (sends CC session_id)
356
+ - Exposes tools: `polaris_connect`, `polaris_disconnect`, `polaris_status`, `polaris_reply`, `polaris_context`
357
+ - `claude/channel` capability for injecting advisor messages
358
+ - Receives advisor messages from daemon via IPC
359
+
360
+ #### CLI
361
+
362
+ `src/cli/cli.ts`:
363
+ - `polaris login` — browser SSO → token stored → daemon + MCP + hooks + skill + status line installed
364
+ - `polaris daemon` — starts the daemon (normally auto-started)
365
+ - `polaris status` — shows all active sessions and daemon health
366
+ - `polaris logout` — removes token and local config
367
+
368
+ ### Phase 6: Slack Bridge (Floor, server-side)
369
+
370
+ The Slack bridge runs server-side as part of the SaaS — not deployed by the user.
371
+
372
+ #### Slack Channel Lifecycle
373
+
374
+ - **Channel name = project name** — project `pj` → Slack `#pj`
375
+ - **Created automatically** when the first session is created in a project (if Slack is connected for the org)
376
+ - **Channel ID stored in the project record** on the cloud service
377
+ - Users never manually create or configure the Slack channel
378
+
379
+ #### Bridge
380
+
381
+ `src/slack/bridge.ts` — runs server-side, one instance per org:
382
+ - Connects to Slack via Socket Mode
383
+ - Watches all projects in the org via project-level WebSocket connections
384
+ - **Project → Slack**: session events → attributed, formatted Slack posts. `UserPromptSubmit` and `Stop` events posted; tool calls skipped or collapsed.
385
+ - **Slack → Project**: advisor messages → injected into target session (must specify target, e.g., `@fxm use RS256`)
386
+ - **Handoff events** → Slack notification showing the transition
387
+
388
+ #### Identity Mapping
389
+
390
+ `src/slack/identity.ts` — email-based Slack ↔ polaris user mapping:
391
+ - Automatic match via shared email between Google SSO and Slack workspace
392
+ - Admin override in dashboard for email mismatches
393
+ - Slack-only participants get `slack:displayname` identity
394
+
395
+ #### Formatter
396
+
397
+ `src/slack/format.ts` — event → Slack mrkdwn conversion:
398
+ - Attribution: `[user:manu/fxm → agent]`, `[agent:security-reviewer → fxm]`
399
+ - Markdown → Slack mrkdwn (different bold/italic/link syntax)
400
+ - Long messages truncated with "see full message" link
401
+
402
+ #### Slack App Requirements
403
+
404
+ - Socket Mode enabled
405
+ - Bot token scopes: `channels:manage`, `channels:join`, `chat:write`, `channels:read`, `users:read`, `users:read.email`
406
+ - Tokens stored per org in the database (from the Slack OAuth flow in the dashboard)
407
+
408
+ ## Key Design Decisions
409
+
410
+ 1. **SaaS with SSO** — organizations sign up on polaris.dev, users authenticate via Google SSO. No self-hosted setup for end users.
411
+ 2. **Email-based identity mapping** — Google SSO email = Slack email. Automatic, no manual mapping. Admin override for mismatches.
412
+ 3. **One command machine setup** — `polaris login` installs everything: daemon, MCP, hooks, skill, status line.
413
+ 4. **One slash command to connect** — `/polaris join project session` from inside any coding agent. No terminal switching.
414
+ 5. **Agents are first-class** — same identity model as humans (`agent:name` vs `user:name`). Spawned out of band. Privileges/HITL managed externally.
415
+ 6. **Addressed messaging** — every injection targets a specific session. No blind broadcasts.
416
+ 7. **Project-level context pooling** — all events persisted under the project. Sibling activity on-demand, not auto-injected.
417
+ 8. **One driver per session, multiple sessions per project** — concurrent human and agent drivers.
418
+ 9. **Status line** — persistent polaris connection indicator in the coding agent CLI. Queries daemon, updates every turn.
419
+ 10. **Single daemon per machine** — routes hook events and cloud messages between multiple concurrent coding agent sessions.
420
+ 11. **Server-side Slack bridge** — managed by the SaaS, not by the user. Auto-creates channels. Email-based identity mapping.
421
+ 12. **`/polaris` slash command** — single entry point: join, disconnect, status.
422
+ 13. **Postgres on Hetzner** — persistent, production-ready from day one.
423
+ 14. **Flat names** — project names unique within an org, session names unique within a project.
424
+
425
+ ## Verification
426
+
427
+ - `bun test` covers all unit + integration tests
428
+ - Manual end-to-end test:
429
+ 1. Sign up org on polaris.dev, connect Slack
430
+ 2. `npx @lightup/polaris login` — authenticate, install local components
431
+ 3. Open coding agent, `/polaris join pj fxm` — verify status line shows connected
432
+ 4. Send a prompt → verify it appears in Slack `#pj` attributed to `user:manu/fxm`
433
+ 5. Another user: `/polaris join pj fxk` — verify both sessions broadcast to `#pj`
434
+ 6. Post in Slack targeting `fxk` → verify it appears in Krishna's session
435
+ 7. Manu asks "what has Krishna done?" → `polaris_context` fetches `fxk` history
436
+ 8. Handoff `fxm` from Manu → Krishna → verify transition in Slack
437
+ 9. Verify Slack identity mapping (polaris user shows as correct Slack profile)
438
+ - CI gate: GitHub Actions runs `bun test` on PRs
@@ -0,0 +1,14 @@
1
+ services:
2
+ polaris-postgres:
3
+ image: postgres:17
4
+ environment:
5
+ POSTGRES_USER: polaris
6
+ POSTGRES_PASSWORD: polaris
7
+ POSTGRES_DB: polaris
8
+ ports:
9
+ - "5432:5432"
10
+ volumes:
11
+ - polaris-pgdata:/var/lib/postgresql/data
12
+
13
+ volumes:
14
+ polaris-pgdata: