teamshare-bridge 0.1.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 (53) hide show
  1. package/README.md +343 -0
  2. package/dist/bridge/daemon.d.ts +1 -0
  3. package/dist/bridge/daemon.js +230 -0
  4. package/dist/bridge/daemon.js.map +1 -0
  5. package/dist/bridge/index.d.ts +2 -0
  6. package/dist/bridge/index.js +273 -0
  7. package/dist/bridge/index.js.map +1 -0
  8. package/dist/bridge/local-server.d.ts +29 -0
  9. package/dist/bridge/local-server.js +87 -0
  10. package/dist/bridge/local-server.js.map +1 -0
  11. package/dist/bridge/protocol.d.ts +4 -0
  12. package/dist/bridge/protocol.js +151 -0
  13. package/dist/bridge/protocol.js.map +1 -0
  14. package/dist/bridge/service.d.ts +32 -0
  15. package/dist/bridge/service.js +248 -0
  16. package/dist/bridge/service.js.map +1 -0
  17. package/dist/bridge/spawn.d.ts +15 -0
  18. package/dist/bridge/spawn.js +271 -0
  19. package/dist/bridge/spawn.js.map +1 -0
  20. package/dist/cli/index.d.ts +2 -0
  21. package/dist/cli/index.js +543 -0
  22. package/dist/cli/index.js.map +1 -0
  23. package/dist/lib/api.d.ts +191 -0
  24. package/dist/lib/api.js +209 -0
  25. package/dist/lib/api.js.map +1 -0
  26. package/dist/lib/brief.d.ts +16 -0
  27. package/dist/lib/brief.js +115 -0
  28. package/dist/lib/brief.js.map +1 -0
  29. package/dist/lib/chat-reply.d.ts +29 -0
  30. package/dist/lib/chat-reply.js +126 -0
  31. package/dist/lib/chat-reply.js.map +1 -0
  32. package/dist/lib/config.d.ts +25 -0
  33. package/dist/lib/config.js +81 -0
  34. package/dist/lib/config.js.map +1 -0
  35. package/dist/lib/doc-reply.d.ts +22 -0
  36. package/dist/lib/doc-reply.js +108 -0
  37. package/dist/lib/doc-reply.js.map +1 -0
  38. package/dist/lib/llm.d.ts +50 -0
  39. package/dist/lib/llm.js +444 -0
  40. package/dist/lib/llm.js.map +1 -0
  41. package/dist/lib/lock.d.ts +41 -0
  42. package/dist/lib/lock.js +148 -0
  43. package/dist/lib/lock.js.map +1 -0
  44. package/dist/lib/models.d.ts +30 -0
  45. package/dist/lib/models.js +51 -0
  46. package/dist/lib/models.js.map +1 -0
  47. package/dist/lib/partial-stream.d.ts +25 -0
  48. package/dist/lib/partial-stream.js +99 -0
  49. package/dist/lib/partial-stream.js.map +1 -0
  50. package/dist/lib/session-stream.d.ts +38 -0
  51. package/dist/lib/session-stream.js +191 -0
  52. package/dist/lib/session-stream.js.map +1 -0
  53. package/package.json +44 -0
package/README.md ADDED
@@ -0,0 +1,343 @@
1
+ # teamshare-bridge
2
+
3
+ The local bridge that lets AI agents work TeamShare tasks from your machine.
4
+
5
+ TeamShare is the **coordination hub**: agents are registered in the app
6
+ (identity, capabilities, task assignments, run history). The AI itself runs
7
+ here — on your computer — connected to TeamShare through three methods, all
8
+ provided by this package:
9
+
10
+ | Method | Binary | Trigger | Requirements |
11
+ |---|---|---|---|
12
+ | **Terminal session** | `teamshare-agent` | You paste a command (or click "Copy run command" in the app) | Node 18+, no daemon |
13
+ | **Auto-wake daemon** | `teamshare-bridge start` | A task is assigned to your agent (or it is @mentioned) → the daemon spawns a session automatically | The daemon must be running (`autostart on` keeps it alive across reboots) |
14
+ | **Run button (deep link)** | `teamshare-bridge register` + `serve` | The TeamShare website's "Run agent" button hands off via `teamshare://` | One-time registration; OS cold-launches the bridge |
15
+
16
+ All three share the same core: fetch a task brief (task, comments, project
17
+ files), drive a working session (opencode / Claude / built-in headless LLM
18
+ loop), and report back through the TeamShare API.
19
+
20
+ ---
21
+
22
+ ## Install
23
+
24
+ Requires **Node.js 18+**. One shot (Windows):
25
+
26
+ ```powershell
27
+ irm https://raw.githubusercontent.com/dominionbanjo/teamshare-bridge/main/scripts/install.ps1 | iex
28
+ ```
29
+
30
+ One shot (macOS/Linux):
31
+
32
+ ```sh
33
+ curl -fsSL https://raw.githubusercontent.com/dominionbanjo/teamshare-bridge/main/scripts/install.sh | bash
34
+ ```
35
+
36
+ Or install directly from npm (global):
37
+
38
+ ```sh
39
+ npm i -g teamshare-bridge # published package
40
+ npm i -g . # from this repo
41
+ ```
42
+
43
+ You now have two commands:
44
+
45
+ ```sh
46
+ teamshare-agent # CLI session runner (methods 1)
47
+ teamshare-bridge # daemon + protocol handler (methods 2 and 3)
48
+ ```
49
+
50
+ ## Run
51
+
52
+ ```sh
53
+ teamshare-bridge start # daemon in the background (pid file + ~/.teamshare/daemon.log)
54
+ teamshare-bridge stop # stop it
55
+ teamshare-bridge restart # stop + start
56
+ teamshare-bridge status # pid/log/autostart + per-agent busy/idle
57
+ teamshare-bridge daemon # foreground (debugging)
58
+ teamshare-bridge autostart on # start at login: Task Scheduler (admin) or Startup-folder launcher (no admin)
59
+ teamshare-bridge autostart off # remove it
60
+ ```
61
+
62
+ ## Quick start
63
+
64
+ ```sh
65
+ # 1. Save your agent's key (from the app: Agents → your agent → Launch Console)
66
+ teamshare-agent connect --agent <agent-id> --key ts_...
67
+
68
+ # 2a. Work one task explicitly
69
+ teamshare-agent run --task <task-id> --agent <agent-id> --key ts_...
70
+
71
+ # 2b. Or let the agent pick up its next assigned task (priority first, then oldest)
72
+ teamshare-agent run --agent <agent-id> --key ts_...
73
+
74
+ # 2c. Auto-wake: keep the daemon running, sessions spawn on assignment
75
+ teamshare-bridge start
76
+ teamshare-bridge autostart on # ...and it comes back after a reboot
77
+
78
+ # 3. Run button on the website (registers the teamshare:// scheme)
79
+ teamshare-bridge register
80
+ ```
81
+
82
+ ### Headless mode (no harness, no opencode/Claude — just an LLM API key)
83
+
84
+ ```sh
85
+ LLM_BASE_URL=https://openrouter.ai/api/v1 \
86
+ LLM_API_KEY=sk-... \
87
+ LLM_MODEL=deepseek/deepseek-chat \
88
+ teamshare-agent run --task <task-id> --agent <agent-id> --key ts_... --self
89
+ ```
90
+
91
+ The headless loop is an OpenAI-compatible **function-calling** loop: the model
92
+ gets the task brief plus tools (`get_task`, `list_tasks`, `create_task`,
93
+ `update_task`, `add_comment`, `list_documents`, `read_document`, `search`,
94
+ `ask_human`, `wait_for_answer`, `list_subtasks`, `create_subtask`,
95
+ `update_subtask`). It is instructed to break the task into 3-8 real
96
+ subtasks (`create_subtask`) and check each off (`update_subtask done: true`)
97
+ as work progresses — the human sees the live breakdown in the app. It posts
98
+ milestone comments and finishes by moving the task to `in_review` /
99
+ `resolved`. Any OpenAI-compatible endpoint works (OpenAI, DeepSeek, OpenRouter,
100
+ Together, local vLLM/Ollama…).
101
+
102
+ **One session per agent:** `run`/`chat`/`doc` acquire a per-agent lock
103
+ (`~/.teamshare/locks/<agentId>.lock`, auto-released on exit, stale locks
104
+ reclaimed) and wait for a busy agent instead of starting a second session.
105
+ The daemon queues wakes for a busy agent and spawns them when it frees up;
106
+ `teamshare-bridge status` shows `BUSY` per agent.
107
+
108
+ ---
109
+
110
+ ## How a session works
111
+
112
+ 1. **Heartbeat** — the agent's status flips to `working` in the app.
113
+ 2. **Brief** — the CLI fetches the task (description, comment thread, project
114
+ name) and downloads project files (text files truncated to ~20k chars;
115
+ links return their URL). The brief is written to
116
+ `<temp>/teamshare-task-<taskId>.md` and printed as a preview.
117
+ 3. **Work** — either a harness is spawned (`opencode run "…"`, `claude -p "…"`,
118
+ auto-detected in that order) or the `--self` LLM loop runs.
119
+ 4. **Report** — a closing summary comment is posted (skip with `--no-report`).
120
+ 5. **Watch** — the CLI polls the task status every 15s (up to 20 min) until it
121
+ leaves `open`/`in_progress`, then heartbeats back to `online`.
122
+
123
+ ## Command reference
124
+
125
+ ### `teamshare-agent`
126
+
127
+ ```
128
+ teamshare-agent connect --agent <id> --key <ts_...>
129
+ Test the connection and save the agent key in ~/.teamshare/config.json.
130
+
131
+ teamshare-agent run --task <taskId> --agent <id> --key <ts_...> [options]
132
+ Work a specific task. Without --task, picks the agent's next assigned task
133
+ from its inbox (urgent > high > medium > low, then oldest; skips
134
+ in_review/resolved/closed).
135
+
136
+ teamshare-agent chat --project <projectId> --agent <id> --key <ts_...> [--mention <token>]
137
+ Reply to every project-chat message that @mentions the agent. Replies
138
+ stream token-by-token into the channel when the provider supports it
139
+ (Phase C); the final message is still posted via the API.
140
+
141
+ teamshare-agent doc --document <documentId> --agent <id> --key <ts_...> [--mention <token>]
142
+ Document assistant (Phase F): read the document (Word/PPT/Excel/PDF text
143
+ via GET /documents/:id/extract) and reply to every comment that @mentions
144
+ the agent in its thread.
145
+
146
+ teamshare-agent continue --task <taskId> --agent <id> --key <ts_...> [--self]
147
+ Resume a previous --self session for a task (Phase E continuation).
148
+
149
+ --harness auto|opencode|claude|none session driver (default: config or auto)
150
+ --self headless LLM loop (needs LLM_API_KEY)
151
+ --no-report skip the closing summary comment
152
+ --session <id> live-console session id (default: auto)
153
+ --model <provider:model> model override (catalog id, e.g. gemini:gemini-3.1-flash-lite)
154
+ --temperature <0-2> sampling temperature override
155
+ --max-tokens <n> token budget override
156
+ --system-prompt <text> | --system-prompt-file <path>
157
+ --ask-timeout <sec> ask_human wait limit (env TEAMSHARE_ASK_TIMEOUT)
158
+ --continue resume a previous --self session
159
+ --api-url <url> TeamShare API base override
160
+
161
+ teamshare-agent --help
162
+ ```
163
+
164
+ Model precedence: **CLI flag > agent settings (set in the app, delivered via
165
+ deep link or auto-wake) > env (`LLM_MODEL`/`LLM_TEMPERATURE`/`LLM_MAX_TOKENS`)
166
+ > defaults**. Model ids use the curated `<provider>:<model>` catalog
167
+ (`docs/agent-models.md`) — the provider's OpenAI-compatible base URL is
168
+ resolved automatically; `LLM_BASE_URL` always wins when set.
169
+
170
+ ### `teamshare-bridge`
171
+
172
+ ```
173
+ teamshare-bridge daemon [--port N]
174
+ Auto-wake listener: keeps a WebSocket to TeamShare per configured agent
175
+ and spawns a session on wake events (task assigned, @mention in a comment
176
+ or chat). Also serves the localhost probe the website detects.
177
+
178
+ teamshare-bridge register
179
+ Registers the teamshare:// URI scheme (per-user; Windows registry /
180
+ macOS app bundle / Linux .desktop). No admin rights needed.
181
+
182
+ teamshare-bridge serve "<teamshare://agent/run?runToken=...&task=...>"
183
+ OS-invoked: redeems the run token and spawns the session.
184
+
185
+ teamshare-bridge add-agent --agent <id> --key <ts_...>
186
+ Saves an agent key without testing the connection.
187
+
188
+ teamshare-bridge status [--port N]
189
+ Shows config path, port, per-agent BUSY/idle (stale locks flagged),
190
+ harness, quiet hours, scheme.
191
+
192
+ teamshare-bridge clear-lock --agent <id>
193
+ Force-removes a stale session lock. Crashed sessions (dead PID) and
194
+ locks older than 8h are auto-reclaimed; this is the manual override.
195
+
196
+ teamshare-bridge probe [--port N]
197
+ Starts just the localhost probe server (for testing).
198
+ ```
199
+
200
+ ## Configuration
201
+
202
+ `~/.teamshare/config.json` — created on first run with defaults:
203
+
204
+ ```json
205
+ {
206
+ "agents": [{ "agentId": "...", "apiKey": "ts_..." }],
207
+ "port": 48242,
208
+ "harness": "auto",
209
+ "quietHours": null,
210
+ "wakeRules": null
211
+ }
212
+ ```
213
+
214
+ | Key | Meaning |
215
+ |---|---|
216
+ | `agents` | Agent id + API key pairs the daemon listens for (`teamshare-agent connect` / `teamshare-bridge add-agent` populate this) |
217
+ | `port` | Localhost probe port. Picked at **random** from `[48231, 48242, 48253, 48264, 48275, 48286, 48297]` on first run; the website probes all candidates, so any pick is discoverable. Override with `--port`. |
218
+ | `harness` | Preferred session driver: `auto`, `opencode`, `claude`, `none` |
219
+ | `quietHours` | `{ "start": "22:00", "end": "07:00" }` — wake events are ignored during this window |
220
+ | `wakeRules` | `{ "priorities": ["high", "urgent"] }` — only wake for tasks matching these priorities |
221
+
222
+ ## Environment variables
223
+
224
+ | Variable | Default | Used by |
225
+ |---|---|---|
226
+ | `TEAMSHARE_API_URL` | `http://localhost:4000/api` | all commands |
227
+ | `TEAMSHARE_WS_URL` | `ws://localhost:4000` | daemon + live console + streaming |
228
+ | `LLM_BASE_URL` | (catalog per model) | `--self`, chat/doc replies |
229
+ | `LLM_API_KEY` | — (required for `--self`) | `--self`, chat/doc replies |
230
+ | `LLM_MODEL` | `deepseek-chat` | `--self` (catalog ids resolve the base URL) |
231
+ | `LLM_TEMPERATURE` | `0.2` | `--self`, chat/doc replies |
232
+ | `LLM_MAX_TOKENS` | — (provider default) | `--self`, chat/doc replies |
233
+ | `TEAMSHARE_ASK_TIMEOUT` | `900` (seconds) | `ask_human` wait limit |
234
+ | `TS_CHAT_ECHO` | — | Debug: post canned acknowledgments (deterministic E2E) |
235
+
236
+ ## Live console & streaming
237
+
238
+ - Every session (`run`/`chat`/`doc`) opens its own `/agents` socket and emits
239
+ `session:start` / `session:output` (batched ~200ms) / `session:end` — the
240
+ app's **Live console** on the agent detail page shows the output in real
241
+ time, and a capped transcript is persisted server-side at the end.
242
+ - Local mirror + continuation history: `~/.teamshare/sessions/`
243
+ (`<sessionId>.json` transcripts, `task-<taskId>.json` for `--continue`).
244
+ - Chat replies additionally stream into the project channel as
245
+ `message:partial` frames over a `/chat` socket (apiKey auth) — the web UI
246
+ renders a live draft bubble that is swapped for the persisted message.
247
+
248
+ ## The localhost probe
249
+
250
+ The website detects the bridge by calling `GET http://localhost:<port>/ping`
251
+ with permissive CORS (the browser can reach it from the web app):
252
+
253
+ ```json
254
+ { "ok": true, "version": "0.1.0", "agents": ["<agent-id>"] }
255
+ ```
256
+
257
+ When the probe answers, the app shows "Bridge detected on this computer" and
258
+ enables the **Run agent** button; otherwise it degrades gracefully to the
259
+ copy-paste command.
260
+
261
+ ## Security model
262
+
263
+ - **Agent API keys** (`ts_...`) are minted by TeamShare with scopes derived
264
+ from the agent's capabilities and can never exceed them; the raw key is
265
+ shown only once in the app. Store it via `connect` — it lives in
266
+ `~/.teamshare/config.json` (chmod 600 on unix).
267
+ - **Run tokens** in `teamshare://` deep links are short-lived (10 min),
268
+ single-use, and exchanged server-side for a session credential — the
269
+ long-lived key never appears in a URL.
270
+ - The bridge never sees environment-variable values; TeamShare's viewer-denied
271
+ rule extends to agents automatically.
272
+ - The probe server binds to `127.0.0.1` only.
273
+
274
+ ## Platform notes
275
+
276
+ | Platform | Terminal spawn | Protocol registration |
277
+ |---|---|---|
278
+ | Windows | `cmd /c start cmd /k …` (detached window, stays open) | `HKCU\Software\Classes\teamshare` via `reg.exe`, per-user |
279
+ | macOS | Terminal.app via `osascript` (always present, no installs) | `~/Applications/TeamShareBridge.app` (CFBundleURLTypes); open the bundle once |
280
+ | Linux (desktop) | `xdg-terminal-exec` → `gnome-terminal` → `konsole` → `xterm` (first found on PATH) | `~/.local/share/applications/teamshare-bridge.desktop` (`x-scheme-handler/teamshare`) |
281
+ | Linux (server/SSH) | Headless fallback — no window; watch via the web live console | n/a |
282
+
283
+ ## Troubleshooting
284
+
285
+ | Symptom | Fix |
286
+ |---|---|
287
+ | `EADDRINUSE` on probe | `--port <candidate>` or delete the `port` key from config; the website probes all candidates |
288
+ | "Agent not configured locally" on a deep link | Run `teamshare-agent connect --agent <id> --key ts_...` |
289
+ | Run button disabled with a connect hint | The agent's key isn't saved on this PC - run `connect`; keys are shown once (lost key = delete + recreate the agent) |
290
+ | Deep-link window flashes shut | The `serve` handler failed (usually a missing local key) - the error is now kept on screen via a 30s pause |
291
+ | No harness found on `run` | Install opencode or Claude, or use `--self` |
292
+ | Wake events never arrive | `teamshare-bridge status`; the daemon must be running and the key saved in config |
293
+ | Wakes queued but never spawn | Stale lock from a crashed session - `teamshare-bridge status` flags it, `clear-lock --agent <id>` removes it (dead locks auto-reclaim within seconds) |
294
+ | Session opens but exits instantly | Run `teamshare-agent run …` manually to see the error; check the API URL and key scopes; output is captured in `~/.teamshare/sessions/spawn-*.log` |
295
+
296
+ ## Development
297
+
298
+ ```sh
299
+ npm install
300
+ npm run build # tsc -> dist/
301
+ npm run typecheck # tsc --noEmit
302
+ npm run dev # watch mode
303
+ ```
304
+
305
+ Runtime dependency: `socket.io-client` only — everything else is Node
306
+ built-ins (fetch, http, crypto, child_process). Tests for the full loop run
307
+ against a live TeamShare API (see `docs/agent-setup.md`).
308
+
309
+ ## Project layout
310
+
311
+ ```
312
+ src/
313
+ ├── lib/
314
+ │ ├── config.ts # ~/.teamshare/config.json (port candidates, wake rules)
315
+ │ ├── api.ts # typed REST client (envelope-aware, retries, timeouts)
316
+ │ ├── brief.ts # task brief fetcher + heartbeat/report helpers
317
+ │ ├── llm.ts # --self headless function-calling loop (+ SSE streaming)
318
+ │ ├── models.ts # curated <provider>:<model> catalog -> base URL resolution
319
+ │ ├── chat-reply.ts # chat reply loop (mention-driven, streaming)
320
+ │ ├── doc-reply.ts # Phase F document assistant reply loop
321
+ │ ├── session-stream.ts # live-console streaming over /agents + local transcripts
322
+ │ ├── partial-stream.ts # Phase C message:partial draft streaming over /chat
323
+ │ └── lock.ts # per-agent session lock (~/.teamshare/locks/<agentId>.lock)
324
+ ├── cli/
325
+ │ └── index.ts # teamshare-agent (connect / run / chat / doc / continue)
326
+ └── bridge/
327
+ ├── index.ts # teamshare-bridge CLI (daemon/register/serve/status/probe)
328
+ ├── daemon.ts # WS auto-wake listener + wake rules + busy-queueing
329
+ ├── local-server.ts# 127.0.0.1 probe server (/ping, /status)
330
+ ├── spawn.ts # detached terminal session spawner (+ output capture)
331
+ └── protocol.ts # teamshare:// registration + deep-link dispatch
332
+ ```
333
+
334
+ ## Related docs
335
+
336
+ - `docs/agent-setup.md` — plain-language setup guide (mirrors the app's
337
+ Launch Console)
338
+ - `docs/agent-tasks/agent-hub-connection.md` (repo root) — the full agent
339
+ contract: REST endpoints, MCP tools, WS events, capabilities
340
+ - `docs/agent-models.md` (repo root) — curated model catalog + update process
341
+ - `docs/agent-capabilities.md` (repo root) — provider matrix + feature menu
342
+ - `docs/agent-roadmap-verification.md` (repo root) — step-by-step E2E playbook
343
+ - `AGENTS.md` (repo root) — project conventions
@@ -0,0 +1 @@
1
+ export declare function runDaemon(portOverride?: number): Promise<void>;
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runDaemon = runDaemon;
4
+ /**
5
+ * `teamshare-bridge daemon` - keeps a WebSocket to TeamShare per configured
6
+ * agent and spawns a session when a `wake` event arrives (contract section 4).
7
+ * Also serves the localhost probe so the website can detect it.
8
+ */
9
+ const node_fs_1 = require("node:fs");
10
+ const node_os_1 = require("node:os");
11
+ const node_path_1 = require("node:path");
12
+ const socket_io_client_1 = require("socket.io-client");
13
+ const api_1 = require("../lib/api");
14
+ const config_1 = require("../lib/config");
15
+ const lock_1 = require("../lib/lock");
16
+ const local_server_1 = require("./local-server");
17
+ const spawn_1 = require("./spawn");
18
+ const WS_DEFAULT = 'ws://localhost:4000';
19
+ const LOG_PATH = (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'daemon.log');
20
+ function log(line) {
21
+ const stamp = new Date().toISOString();
22
+ const text = `[${stamp}] ${line}`;
23
+ console.log(text);
24
+ try {
25
+ (0, node_fs_1.appendFileSync)(LOG_PATH, text + '\n');
26
+ }
27
+ catch {
28
+ /* log file best-effort */
29
+ }
30
+ }
31
+ function inQuietHours(config) {
32
+ if (!config.quietHours)
33
+ return false;
34
+ const now = new Date();
35
+ const mins = now.getHours() * 60 + now.getMinutes();
36
+ const parse = (t) => {
37
+ const [h, m] = t.split(':').map(Number);
38
+ return h * 60 + (Number.isFinite(m) ? m : 0);
39
+ };
40
+ const start = parse(config.quietHours.start);
41
+ const end = parse(config.quietHours.end);
42
+ return start <= end ? mins >= start && mins < end : mins >= start || mins < end;
43
+ }
44
+ async function runDaemon(portOverride) {
45
+ const config = (0, config_1.loadConfig)();
46
+ const port = portOverride ?? config.port;
47
+ const apiBase = process.env.TEAMSHARE_API_URL ?? api_1.DEFAULT_API_URL;
48
+ const wsBase = (process.env.TEAMSHARE_WS_URL ?? WS_DEFAULT).replace(/\/$/, '');
49
+ /** agentId -> queued wakes waiting for the session lock to free up. */
50
+ const wakeQueue = new Map();
51
+ /** agentId -> ISO timestamp of the last wake event (probe/status). */
52
+ const lastWakeByAgent = new Map();
53
+ const getStatus = () => ({
54
+ configPath: (0, config_1.configPath)(),
55
+ agents: config.agents.map((a) => {
56
+ const lock = (0, lock_1.readLock)(a.agentId);
57
+ const base = {
58
+ agentId: a.agentId,
59
+ lastWake: lastWakeByAgent.get(a.agentId),
60
+ queued: wakeQueue.get(a.agentId)?.length ?? 0,
61
+ };
62
+ if ((0, lock_1.isLockBusy)(a.agentId) && lock) {
63
+ base.busy = true;
64
+ base.since = lock.startedAt;
65
+ base.kind = lock.kind;
66
+ base.taskId = lock.taskId;
67
+ base.projectId = lock.projectId;
68
+ base.documentId = lock.documentId;
69
+ }
70
+ else {
71
+ base.busy = false;
72
+ }
73
+ return base;
74
+ }),
75
+ registeredSchemes: false,
76
+ });
77
+ const { server, port: boundPort } = await (0, local_server_1.startProbeServer)(port, getStatus);
78
+ log(`probe server listening on http://localhost:${boundPort}/ping`);
79
+ if (config.agents.length === 0) {
80
+ log('no agents configured - run: teamshare-agent connect --agent <id> --key ts_...');
81
+ }
82
+ const sockets = [];
83
+ const timers = [];
84
+ /** agentId -> Phase D settings snapshot (refreshed on every reconnect). */
85
+ const settingsByAgent = new Map();
86
+ for (const agent of config.agents) {
87
+ const api = api_1.TeamshareApi.forAgent(apiBase, agent);
88
+ /** Keeps the agent marked online; errors are surfaced, never swallowed. */
89
+ const heartbeat = () => {
90
+ api
91
+ .heartbeat(agent.agentId, 'online')
92
+ .then(() => log(`agent ${agent.agentId}: heartbeat online`))
93
+ .catch((err) => log(`agent ${agent.agentId}: heartbeat failed: ${(0, config_1.msg)(err)}`));
94
+ };
95
+ const socket = (0, socket_io_client_1.io)(`${wsBase}/agents`, {
96
+ transports: ['websocket'],
97
+ auth: { apiKey: agent.apiKey },
98
+ reconnection: true,
99
+ reconnectionDelay: 2_000,
100
+ reconnectionDelayMax: 30_000,
101
+ });
102
+ socket.on('connect', () => {
103
+ log(`agent ${agent.agentId}: connected to /agents`);
104
+ // The gateway authenticates asynchronously after the socket connects -
105
+ // retry the join until the ack confirms client.agentId is set (which
106
+ // also carries the Phase D settings snapshot).
107
+ const joinUntilReady = (attempt) => {
108
+ socket.emit('join', { ack: true }, (res) => {
109
+ if (res?.success) {
110
+ const settings = res.data?.settings ?? null;
111
+ settingsByAgent.set(agent.agentId, settings);
112
+ if (settings) {
113
+ log(`agent ${agent.agentId}: settings ${JSON.stringify({
114
+ model: settings.model ?? null,
115
+ temperature: settings.temperature ?? null,
116
+ maxTokens: settings.maxTokens ?? null,
117
+ })}`);
118
+ }
119
+ }
120
+ else if (attempt < 10) {
121
+ setTimeout(() => joinUntilReady(attempt + 1), 1000);
122
+ }
123
+ else {
124
+ log(`agent ${agent.agentId}: join ack failed after retries`);
125
+ }
126
+ });
127
+ };
128
+ joinUntilReady(0);
129
+ heartbeat();
130
+ timers.push(setInterval(heartbeat, 60_000));
131
+ });
132
+ socket.on('disconnect', (reason) => {
133
+ log(`agent ${agent.agentId}: disconnected (${reason})`);
134
+ });
135
+ socket.on('connect_error', (err) => {
136
+ log(`agent ${agent.agentId}: connect_error ${(0, config_1.msg)(err)}`);
137
+ });
138
+ socket.on('wake', (event) => {
139
+ lastWakeByAgent.set(agent.agentId, new Date().toISOString());
140
+ void handleWake(api, agent.agentId, agent.apiKey, event, config, {
141
+ settings: settingsByAgent.get(agent.agentId),
142
+ wakeQueue,
143
+ }).catch((err) => log(`agent ${agent.agentId}: wake failed: ${(0, config_1.msg)(err)}`));
144
+ });
145
+ sockets.push(socket);
146
+ wakeQueue.set(agent.agentId, []);
147
+ }
148
+ // Queue flush: spawn queued wakes once the agent's session lock frees up
149
+ // (staleness-aware - a crashed session's dead-pid lock is treated as free).
150
+ const flushInterval = setInterval(() => {
151
+ for (const [agentId, queue] of wakeQueue) {
152
+ if (queue.length === 0)
153
+ continue;
154
+ if ((0, lock_1.isLockBusy)(agentId))
155
+ continue; // still busy - keep waiting
156
+ const agent = config.agents.find((a) => a.agentId === agentId);
157
+ const event = queue.shift();
158
+ if (!agent || !event)
159
+ continue;
160
+ void spawnWake(api_1.TeamshareApi.forAgent(apiBase, agent), agentId, agent.apiKey, event, config, { settings: settingsByAgent.get(agentId) }).catch((err) => log(`agent ${agentId}: queued wake failed: ${(0, config_1.msg)(err)}`));
161
+ log(`agent ${agentId}: spawned queued wake ${event.type}`);
162
+ }
163
+ }, lock_1.LOCK_POLL_MS);
164
+ timers.push(flushInterval);
165
+ const shutdown = () => {
166
+ log('shutting down');
167
+ for (const t of timers)
168
+ clearInterval(t);
169
+ for (const s of sockets)
170
+ s.close();
171
+ server.close();
172
+ process.exit(0);
173
+ };
174
+ process.on('SIGINT', shutdown);
175
+ process.on('SIGTERM', shutdown);
176
+ }
177
+ async function handleWake(api, agentId, apiKey, event, config, extra) {
178
+ const { wakeQueue, settings } = extra;
179
+ if (inQuietHours(config)) {
180
+ log(`agent ${agentId}: wake ${event.type} skipped (quiet hours)`);
181
+ return;
182
+ }
183
+ if (event.taskId && config.wakeRules?.priorities?.length) {
184
+ try {
185
+ const task = await api.getTask(event.taskId);
186
+ if (!config.wakeRules.priorities.includes(task.priority)) {
187
+ log(`agent ${agentId}: wake ${event.type} skipped (priority ${task.priority})`);
188
+ return;
189
+ }
190
+ }
191
+ catch {
192
+ /* fetch failed - proceed anyway */
193
+ }
194
+ }
195
+ const lock = (0, lock_1.readLock)(agentId);
196
+ if ((0, lock_1.isLockBusy)(agentId) && lock) {
197
+ // The agent is busy with a live session (daemon-spawned or manual
198
+ // terminal) - queue this wake; the flush interval spawns it once the
199
+ // lock frees up (dead-pid locks are treated as free immediately).
200
+ const queue = wakeQueue.get(agentId) ?? [];
201
+ queue.push(event);
202
+ wakeQueue.set(agentId, queue);
203
+ log(`agent ${agentId}: wake ${event.type} queued (busy since ${lock.startedAt}, ${lock.kind}) - ${queue.length} waiting`);
204
+ return;
205
+ }
206
+ await spawnWake(api, agentId, apiKey, event, config, { settings });
207
+ }
208
+ async function spawnWake(api, agentId, apiKey, event, config, extra = {}) {
209
+ log(`agent ${agentId}: wake ${event.type}${event.taskId ? ` task=${event.taskId}` : ''} - spawning session`);
210
+ if (event.type === 'chat.mention') {
211
+ if (!event.projectId) {
212
+ log(`agent ${agentId}: chat.mention without projectId - skipping`);
213
+ return;
214
+ }
215
+ // Chat mentions spawn a chat-reply loop (no task context).
216
+ (0, spawn_1.spawnChatSession)(agentId, apiKey, event.projectId, event.mention ?? '', extra.settings);
217
+ return;
218
+ }
219
+ if (event.type === 'document.mention') {
220
+ if (!event.documentId) {
221
+ log(`agent ${agentId}: document.mention without documentId - skipping`);
222
+ return;
223
+ }
224
+ // Document mentions spawn the document assistant loop (Phase F).
225
+ (0, spawn_1.spawnDocumentSession)(agentId, apiKey, event.documentId, event.mention ?? '', extra.settings);
226
+ return;
227
+ }
228
+ (0, spawn_1.spawnAgentSession)(agentId, apiKey, event.taskId ?? '', extra.settings);
229
+ }
230
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/bridge/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};