kitty-hive 0.6.12 → 0.7.2

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.
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Codex daemon supervisor.
3
+ *
4
+ * On hive serve boot, spawns one codex-channel daemon child per local agent
5
+ * registered with `tool='codex'`. Daemons get push delivery via WebSocket to
6
+ * a codex app-server they manage; the supervisor just keeps them alive.
7
+ *
8
+ * Design choices (settled with the user, 2026-05-20):
9
+ * - DB is the single source of truth: `WHERE tool='codex' AND origin_peer=''`
10
+ * decides what gets spawned. No separate config list, no opt-in flag —
11
+ * same UX as Claude plugin (install once, just works).
12
+ * - Restart with exponential backoff (1s → 2s → 4s → ... → cap 60s).
13
+ * - On serve SIGTERM, supervisor kills all children with SIGTERM and waits.
14
+ * - Daemon stderr/stdout are tee'd into serve's own stderr with an
15
+ * `[codex:<display_name>]` prefix.
16
+ *
17
+ * Thread persistence (v0.7.0):
18
+ * - daemon's codex thread_id is stored on agents.thread_id after first ready.
19
+ * - On respawn, supervisor injects HIVE_AGENT_THREAD_ID so daemon calls
20
+ * `thread/resume` (against the jsonl codex app-server already wrote to
21
+ * ~/.codex/sessions/) instead of `thread/start`. Conversation survives
22
+ * daemon kill / serve restart / machine reboot.
23
+ *
24
+ * Out of scope (deferred):
25
+ * - Hot reload on DB changes (poll / watch). Add/remove agents → must
26
+ * restart serve to pick up. Acceptable for now since codex agents are
27
+ * long-lived once configured.
28
+ */
29
+ export declare function startCodexSupervisor(port?: number): void;
30
+ export declare function stopCodexSupervisor(): Promise<void>;
31
+ export interface DaemonSnapshot {
32
+ agent_id: string;
33
+ display_name: string;
34
+ pid: number;
35
+ uptime_ms: number;
36
+ restart_count: number;
37
+ ws_url: string | null;
38
+ thread_id: string | null;
39
+ ready: boolean;
40
+ }
41
+ export declare function getDaemonSnapshots(): DaemonSnapshot[];
42
+ /** Look up the live daemon for an agent, by agent_id or external_key.
43
+ * Returns null when no daemon is running for that agent. */
44
+ export declare function getDaemonForAgent(lookup: {
45
+ agentId?: string;
46
+ agentKey?: string;
47
+ }): DaemonSnapshot | null;
48
+ /** Called by /admin/codex-daemon-ready when a daemon's codex app-server is up
49
+ * and its thread is created. Stores ws_url + thread_id on the DaemonInfo so
50
+ * outside callers can attach via `codex --remote <ws_url>`. */
51
+ export declare function markDaemonReady(agentId: string, wsUrl: string, threadId: string): boolean;
52
+ /** Called by /admin/notify-agent-created when a CLI-side `agent register`
53
+ * inserts a row. The CLI runs in its own process so the in-process
54
+ * onAgentCreated hook doesn't reach the supervisor; this is the bridge.
55
+ * Idempotent: returns false if already spawned, true if a new spawn started. */
56
+ export declare function notifyAgentCreated(agentId: string): boolean;
57
+ /** Kill the daemon (if any) for an agent that just got removed. Mirror of
58
+ * notifyAgentCreated for the delete path. Without this, `kitty-hive agent
59
+ * remove` deletes the row but leaves a ghost daemon (and its codex
60
+ * app-server + ws) running — see Bug 1 reported on 2026-05-20: a stale
61
+ * ws confused TUI routing into a schism. SIGTERM the child; the existing
62
+ * child.on('exit') handler in spawnDaemon checks `getAgentById(agentId)`
63
+ * before respawning, so a deleted row naturally short-circuits the
64
+ * respawn logic — no per-daemon "intentional kill" flag needed.
65
+ * Returns true if a daemon was killed. */
66
+ export declare function notifyAgentRemoved(agentId: string): boolean;
67
+ /** Force a daemon to (re)spawn for an agent, then wait up to `timeoutMs` for
68
+ * it to reach ready state. Used by /admin/codex-set-thread when the caller
69
+ * has just mutated agents.thread_id and needs the daemon to pick up the
70
+ * new value (either via thread/resume of a different thread, or thread/start
71
+ * for a fresh thread when thread_id was cleared).
72
+ *
73
+ * Two paths:
74
+ * - Daemon already running → SIGTERM it; supervisor's on-exit handler
75
+ * respawns automatically (with backoff, but immediate for the first
76
+ * restart). The new daemon will pick up the fresh agents.thread_id at
77
+ * its env-injection step.
78
+ * - No daemon → call notifyAgentCreated (skips if not eligible).
79
+ *
80
+ * After kicking, polls `daemons.get(agentId)` for readiness every 200ms.
81
+ * Returns the ready snapshot, or null on timeout. The caller is expected to
82
+ * have validated that the agent exists and is tool=codex; this function
83
+ * trusts that. */
84
+ export declare function requestDaemonRespawn(agentId: string, timeoutMs?: number): Promise<DaemonSnapshot | null>;
@@ -0,0 +1,379 @@
1
+ /**
2
+ * Codex daemon supervisor.
3
+ *
4
+ * On hive serve boot, spawns one codex-channel daemon child per local agent
5
+ * registered with `tool='codex'`. Daemons get push delivery via WebSocket to
6
+ * a codex app-server they manage; the supervisor just keeps them alive.
7
+ *
8
+ * Design choices (settled with the user, 2026-05-20):
9
+ * - DB is the single source of truth: `WHERE tool='codex' AND origin_peer=''`
10
+ * decides what gets spawned. No separate config list, no opt-in flag —
11
+ * same UX as Claude plugin (install once, just works).
12
+ * - Restart with exponential backoff (1s → 2s → 4s → ... → cap 60s).
13
+ * - On serve SIGTERM, supervisor kills all children with SIGTERM and waits.
14
+ * - Daemon stderr/stdout are tee'd into serve's own stderr with an
15
+ * `[codex:<display_name>]` prefix.
16
+ *
17
+ * Thread persistence (v0.7.0):
18
+ * - daemon's codex thread_id is stored on agents.thread_id after first ready.
19
+ * - On respawn, supervisor injects HIVE_AGENT_THREAD_ID so daemon calls
20
+ * `thread/resume` (against the jsonl codex app-server already wrote to
21
+ * ~/.codex/sessions/) instead of `thread/start`. Conversation survives
22
+ * daemon kill / serve restart / machine reboot.
23
+ *
24
+ * Out of scope (deferred):
25
+ * - Hot reload on DB changes (poll / watch). Add/remove agents → must
26
+ * restart serve to pick up. Acceptable for now since codex agents are
27
+ * long-lived once configured.
28
+ */
29
+ import { spawn } from 'node:child_process';
30
+ import { existsSync } from 'node:fs';
31
+ import { dirname, join } from 'node:path';
32
+ import { fileURLToPath } from 'node:url';
33
+ import { execSync } from 'node:child_process';
34
+ import { log } from './log.js';
35
+ import { listLocalCodexAgents, getAgentById, onAgentCreated, setAgentThreadId } from './db.js';
36
+ const __filename = fileURLToPath(import.meta.url);
37
+ const __dirname = dirname(__filename);
38
+ const daemons = new Map();
39
+ let shuttingDown = false;
40
+ let supervisorPort = 4123; // set by startCodexSupervisor
41
+ function findNpx() {
42
+ const probe = process.platform === 'win32' ? 'where npx' : 'command -v npx';
43
+ try {
44
+ const out = execSync(probe, { encoding: 'utf8' }).trim().split(/\r?\n/)[0];
45
+ return out || 'npx';
46
+ }
47
+ catch {
48
+ return 'npx';
49
+ }
50
+ }
51
+ function locateCodexChannelScript() {
52
+ // dist/codex-supervisor.js → ../codex-channel.ts (package root)
53
+ const p = join(__dirname, '..', 'codex-channel.ts');
54
+ if (existsSync(p))
55
+ return p;
56
+ // Fallback if running from src/ directly via tsx (dev)
57
+ const dev = join(__dirname, '..', '..', 'codex-channel.ts');
58
+ if (existsSync(dev))
59
+ return dev;
60
+ throw new Error(`cannot locate codex-channel.ts (tried ${p})`);
61
+ }
62
+ function spawnDaemon(agentId, displayName, restartCount = 0) {
63
+ if (shuttingDown)
64
+ return;
65
+ if (daemons.has(agentId))
66
+ return; // already running
67
+ let scriptPath;
68
+ try {
69
+ scriptPath = locateCodexChannelScript();
70
+ }
71
+ catch (err) {
72
+ log('warn', `[codex-supervisor] ${err}`);
73
+ return;
74
+ }
75
+ log('info', `[codex-supervisor] spawning daemon for "${displayName}" (${agentId.slice(-12)}) restart=${restartCount}`);
76
+ // CRITICAL: scrub inherited HIVE_* env vars from the operator's shell.
77
+ // Without this, if `kitty-hive serve` was launched from a shell with
78
+ // HIVE_AGENT_KEY set (e.g. for a Claude session bound to a different
79
+ // agent), the daemon would carry that key over, and codex-channel.ts's
80
+ // hive_start() would `key`-match to the operator's agent — silently
81
+ // taking it over (display_name + tool overwritten). Real incident on
82
+ // 2026-05-20: smoke test renamed `kitty-hive` to `test-codex-1` because
83
+ // the operator shell had HIVE_AGENT_KEY=<kitty-hive's external_key>.
84
+ //
85
+ // Daemon gets a minimal env: system basics (PATH, HOME, etc.) + EXACTLY
86
+ // the hive identity overrides we want.
87
+ const cleanEnv = {};
88
+ for (const [k, v] of Object.entries(process.env)) {
89
+ if (k.startsWith('HIVE_'))
90
+ continue; // drop any HIVE_* inherited from operator shell
91
+ cleanEnv[k] = v;
92
+ }
93
+ cleanEnv.HIVE_AGENT_ID = agentId;
94
+ cleanEnv.HIVE_AGENT_NAME = displayName;
95
+ // HIVE_URL: explicitly set so daemon talks to *this* serve (whose port the
96
+ // supervisor knows), not whatever was in the operator shell or the daemon's
97
+ // own fallback default.
98
+ cleanEnv.HIVE_URL = `http://127.0.0.1:${supervisorPort}/mcp`;
99
+ // CODEX_APPSERVER_CWD: codex-channel.ts reads this and passes to thread/start
100
+ // as cwd. Determines where the codex agent thinks it's running — must match
101
+ // the operator's intent (the project the kitty pane was launched for), not
102
+ // the cwd hive serve happens to be running from. agent.project_dir is
103
+ // operator-set via `agent register --project-dir`; empty falls back to
104
+ // serve's own cwd.
105
+ const fresh = getAgentById(agentId);
106
+ if (fresh?.project_dir)
107
+ cleanEnv.CODEX_APPSERVER_CWD = fresh.project_dir;
108
+ // HIVE_AGENT_THREAD_ID: presence tells codex-channel to `thread/resume` an
109
+ // existing codex thread (jsonl already on disk in ~/.codex/sessions/)
110
+ // instead of `thread/start`. Set on every spawn after the first ready
111
+ // (markDaemonReady persists it to agents.thread_id). On a respawn after
112
+ // resume failure, codex-channel announces a brand-new thread_id and
113
+ // markDaemonReady overwrites — old jsonl is orphaned but content survives
114
+ // on disk.
115
+ if (fresh?.thread_id)
116
+ cleanEnv.HIVE_AGENT_THREAD_ID = fresh.thread_id;
117
+ const child = spawn(findNpx(), ['-y', 'tsx', scriptPath], {
118
+ env: cleanEnv,
119
+ stdio: ['ignore', 'pipe', 'pipe'],
120
+ });
121
+ if (!child.pid) {
122
+ log('warn', `[codex-supervisor] failed to spawn daemon for ${displayName}: no pid`);
123
+ return;
124
+ }
125
+ const info = {
126
+ agentId, displayName, child, pid: child.pid,
127
+ startedAt: new Date(), restartCount,
128
+ wsUrl: null, threadId: null, readyAt: null,
129
+ };
130
+ daemons.set(agentId, info);
131
+ // Tee daemon output into serve's stderr with a label so user can see what
132
+ // each daemon is doing without separate log files. We forward both stdout
133
+ // and stderr to stderr (stdout would interleave with the http log lines).
134
+ const prefix = `[codex:${displayName}]`;
135
+ child.stdout?.on('data', (chunk) => process.stderr.write(`${prefix} ${chunk}`));
136
+ child.stderr?.on('data', (chunk) => process.stderr.write(`${prefix} ${chunk}`));
137
+ child.on('exit', (code, signal) => {
138
+ daemons.delete(agentId);
139
+ log('info', `[codex-supervisor] daemon "${displayName}" exited (code=${code}, signal=${signal})`);
140
+ if (shuttingDown)
141
+ return;
142
+ // Refresh agent record — it may have been removed or modified
143
+ const fresh = getAgentById(agentId);
144
+ if (!fresh || fresh.tool !== 'codex' || fresh.origin_peer !== '') {
145
+ log('info', `[codex-supervisor] daemon "${displayName}" no longer eligible for spawn (deleted / tool changed / now remote); not restarting`);
146
+ return;
147
+ }
148
+ // Exponential backoff: 1s, 2s, 4s, 8s, ... cap 60s
149
+ const delayMs = Math.min(60000, 1000 * Math.pow(2, restartCount));
150
+ log('warn', `[codex-supervisor] restarting "${fresh.display_name}" in ${delayMs}ms (attempt ${restartCount + 1})`);
151
+ setTimeout(() => {
152
+ spawnDaemon(fresh.id, fresh.display_name, restartCount + 1);
153
+ }, delayMs);
154
+ });
155
+ child.on('error', (err) => {
156
+ log('warn', `[codex-supervisor] daemon "${displayName}" process error: ${err}`);
157
+ });
158
+ }
159
+ export function startCodexSupervisor(port = 4123) {
160
+ supervisorPort = port;
161
+ const agents = listLocalCodexAgents();
162
+ if (agents.length === 0) {
163
+ log('info', '[codex-supervisor] no local tool=codex agents — nothing to spawn at boot');
164
+ }
165
+ else {
166
+ log('info', `[codex-supervisor] starting ${agents.length} codex daemon(s): ${agents.map(a => a.display_name).join(', ')}`);
167
+ for (const agent of agents) {
168
+ spawnDaemon(agent.id, agent.display_name, 0);
169
+ }
170
+ }
171
+ // Dynamic spawn: kitty / any other launcher may register a NEW codex agent
172
+ // while serve is running. Listen for agent-create events and spawn a daemon
173
+ // on the fly so the launcher doesn't have to restart serve. Subscribe AFTER
174
+ // initial boot scan so we don't double-spawn the already-spawned set above.
175
+ onAgentCreated((agent) => {
176
+ if (agent.tool !== 'codex' || agent.origin_peer !== '')
177
+ return;
178
+ if (daemons.has(agent.id))
179
+ return; // already spawning
180
+ log('info', `[codex-supervisor] new local tool=codex agent registered: "${agent.display_name}" (${agent.id.slice(-12)}) → spawning daemon`);
181
+ spawnDaemon(agent.id, agent.display_name, 0);
182
+ });
183
+ }
184
+ export function stopCodexSupervisor() {
185
+ shuttingDown = true;
186
+ if (daemons.size === 0)
187
+ return Promise.resolve();
188
+ log('info', `[codex-supervisor] stopping ${daemons.size} daemon(s)...`);
189
+ return new Promise((resolve) => {
190
+ const pending = new Set();
191
+ for (const info of daemons.values()) {
192
+ pending.add(info.agentId);
193
+ info.child.once('exit', () => {
194
+ pending.delete(info.agentId);
195
+ if (pending.size === 0)
196
+ resolve();
197
+ });
198
+ try {
199
+ info.child.kill('SIGTERM');
200
+ }
201
+ catch { /* ignore */ }
202
+ }
203
+ // Hard timeout: if children don't exit in 5s, SIGKILL and resolve
204
+ setTimeout(() => {
205
+ if (pending.size === 0)
206
+ return;
207
+ log('warn', `[codex-supervisor] ${pending.size} daemon(s) didn't exit cleanly; SIGKILL`);
208
+ for (const info of daemons.values()) {
209
+ if (pending.has(info.agentId)) {
210
+ try {
211
+ info.child.kill('SIGKILL');
212
+ }
213
+ catch { /* ignore */ }
214
+ }
215
+ }
216
+ resolve();
217
+ }, 5000);
218
+ });
219
+ }
220
+ export function getDaemonSnapshots() {
221
+ const now = Date.now();
222
+ return [...daemons.values()].map(info => ({
223
+ agent_id: info.agentId,
224
+ display_name: info.displayName,
225
+ pid: info.pid,
226
+ uptime_ms: now - info.startedAt.getTime(),
227
+ restart_count: info.restartCount,
228
+ ws_url: info.wsUrl,
229
+ thread_id: info.threadId,
230
+ ready: !!info.readyAt,
231
+ }));
232
+ }
233
+ /** Look up the live daemon for an agent, by agent_id or external_key.
234
+ * Returns null when no daemon is running for that agent. */
235
+ export function getDaemonForAgent(lookup) {
236
+ let info;
237
+ if (lookup.agentId) {
238
+ info = daemons.get(lookup.agentId);
239
+ }
240
+ else if (lookup.agentKey) {
241
+ // No reverse index — small N, just scan. Need DB lookup to map key→id.
242
+ // To avoid pulling db in here, callers should resolve key→agent_id
243
+ // upstream (the MCP tool does that) and pass agent_id.
244
+ return null;
245
+ }
246
+ if (!info)
247
+ return null;
248
+ return {
249
+ agent_id: info.agentId,
250
+ display_name: info.displayName,
251
+ pid: info.pid,
252
+ uptime_ms: Date.now() - info.startedAt.getTime(),
253
+ restart_count: info.restartCount,
254
+ ws_url: info.wsUrl,
255
+ thread_id: info.threadId,
256
+ ready: !!info.readyAt,
257
+ };
258
+ }
259
+ /** Called by /admin/codex-daemon-ready when a daemon's codex app-server is up
260
+ * and its thread is created. Stores ws_url + thread_id on the DaemonInfo so
261
+ * outside callers can attach via `codex --remote <ws_url>`. */
262
+ export function markDaemonReady(agentId, wsUrl, threadId) {
263
+ const info = daemons.get(agentId);
264
+ if (!info)
265
+ return false;
266
+ info.wsUrl = wsUrl;
267
+ info.threadId = threadId;
268
+ info.readyAt = new Date();
269
+ // Persist thread_id so next spawn (after daemon kill / serve restart) can
270
+ // resume the same codex thread instead of starting fresh. Idempotent —
271
+ // setting the same id is a cheap UPDATE.
272
+ try {
273
+ setAgentThreadId(agentId, threadId);
274
+ }
275
+ catch (err) {
276
+ log('warn', `[codex-supervisor] failed to persist thread_id for ${agentId}: ${err}`);
277
+ }
278
+ log('info', `[codex-supervisor] daemon "${info.displayName}" ready: ws=${wsUrl} thread=${threadId.slice(0, 8)}...`);
279
+ return true;
280
+ }
281
+ /** Called by /admin/notify-agent-created when a CLI-side `agent register`
282
+ * inserts a row. The CLI runs in its own process so the in-process
283
+ * onAgentCreated hook doesn't reach the supervisor; this is the bridge.
284
+ * Idempotent: returns false if already spawned, true if a new spawn started. */
285
+ export function notifyAgentCreated(agentId) {
286
+ if (daemons.has(agentId))
287
+ return false;
288
+ const agent = getAgentById(agentId);
289
+ if (!agent)
290
+ return false;
291
+ if (agent.tool !== 'codex' || agent.origin_peer !== '')
292
+ return false;
293
+ log('info', `[codex-supervisor] notify-agent-created: "${agent.display_name}" (${agentId.slice(-12)}) → spawning daemon`);
294
+ spawnDaemon(agent.id, agent.display_name, 0);
295
+ return true;
296
+ }
297
+ /** Kill the daemon (if any) for an agent that just got removed. Mirror of
298
+ * notifyAgentCreated for the delete path. Without this, `kitty-hive agent
299
+ * remove` deletes the row but leaves a ghost daemon (and its codex
300
+ * app-server + ws) running — see Bug 1 reported on 2026-05-20: a stale
301
+ * ws confused TUI routing into a schism. SIGTERM the child; the existing
302
+ * child.on('exit') handler in spawnDaemon checks `getAgentById(agentId)`
303
+ * before respawning, so a deleted row naturally short-circuits the
304
+ * respawn logic — no per-daemon "intentional kill" flag needed.
305
+ * Returns true if a daemon was killed. */
306
+ export function notifyAgentRemoved(agentId) {
307
+ const info = daemons.get(agentId);
308
+ if (!info)
309
+ return false;
310
+ log('info', `[codex-supervisor] notify-agent-removed: "${info.displayName}" (${agentId.slice(-12)}) → killing daemon`);
311
+ try {
312
+ info.child.kill('SIGTERM');
313
+ }
314
+ catch { /* ignore */ }
315
+ return true;
316
+ }
317
+ /** Force a daemon to (re)spawn for an agent, then wait up to `timeoutMs` for
318
+ * it to reach ready state. Used by /admin/codex-set-thread when the caller
319
+ * has just mutated agents.thread_id and needs the daemon to pick up the
320
+ * new value (either via thread/resume of a different thread, or thread/start
321
+ * for a fresh thread when thread_id was cleared).
322
+ *
323
+ * Two paths:
324
+ * - Daemon already running → SIGTERM it; supervisor's on-exit handler
325
+ * respawns automatically (with backoff, but immediate for the first
326
+ * restart). The new daemon will pick up the fresh agents.thread_id at
327
+ * its env-injection step.
328
+ * - No daemon → call notifyAgentCreated (skips if not eligible).
329
+ *
330
+ * After kicking, polls `daemons.get(agentId)` for readiness every 200ms.
331
+ * Returns the ready snapshot, or null on timeout. The caller is expected to
332
+ * have validated that the agent exists and is tool=codex; this function
333
+ * trusts that. */
334
+ export async function requestDaemonRespawn(agentId, timeoutMs = 30_000) {
335
+ const existing = daemons.get(agentId);
336
+ if (existing) {
337
+ log('info', `[codex-supervisor] requestDaemonRespawn: SIGTERM existing daemon for "${existing.displayName}" (${agentId.slice(-12)})`);
338
+ try {
339
+ existing.child.kill('SIGTERM');
340
+ }
341
+ catch { /* ignore */ }
342
+ }
343
+ else {
344
+ log('info', `[codex-supervisor] requestDaemonRespawn: no live daemon for ${agentId.slice(-12)}, requesting spawn`);
345
+ notifyAgentCreated(agentId);
346
+ }
347
+ const startedAt = Date.now();
348
+ // Wait for the OLD daemon (if any) to exit and the supervisor's restart
349
+ // backoff to fire, plus the new daemon to call markDaemonReady (which
350
+ // happens after thread/resume or thread/start succeeds and the daemon
351
+ // POSTs /admin/codex-daemon-ready). Poll cheaply: 200ms is well below the
352
+ // typical daemon spawn-to-ready time (~5s) and short enough to bound
353
+ // perceived latency for the kitty UI caller.
354
+ while (Date.now() - startedAt < timeoutMs) {
355
+ await new Promise(r => setTimeout(r, 200));
356
+ const snap = daemons.get(agentId);
357
+ if (!snap)
358
+ continue; // still respawning; existing was killed, new not yet started
359
+ if (!snap.readyAt)
360
+ continue; // spawned but codex app-server / thread not ready yet
361
+ // Don't return the old daemon's snapshot if we just SIGTERM'd it — the
362
+ // pid will have changed when the new daemon spawns. Use startedAt as a
363
+ // sentinel: the new daemon's startedAt > our startedAt.
364
+ if (existing && snap.startedAt.getTime() <= startedAt)
365
+ continue;
366
+ return {
367
+ agent_id: snap.agentId,
368
+ display_name: snap.displayName,
369
+ pid: snap.pid,
370
+ uptime_ms: Date.now() - snap.startedAt.getTime(),
371
+ restart_count: snap.restartCount,
372
+ ws_url: snap.wsUrl,
373
+ thread_id: snap.threadId,
374
+ ready: true,
375
+ };
376
+ }
377
+ return null;
378
+ }
379
+ //# sourceMappingURL=codex-supervisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-supervisor.js","sourceRoot":"","sources":["../src/codex-supervisor.ts"],"names":[],"mappings":"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"}
package/dist/db.d.ts CHANGED
@@ -19,9 +19,22 @@ export interface CreateAgentOpts {
19
19
  externalKey?: string;
20
20
  originPeer?: string;
21
21
  remoteId?: string;
22
+ projectDir?: string;
22
23
  }
24
+ type AgentCreateListener = (agent: Agent) => void;
25
+ export declare function onAgentCreated(fn: AgentCreateListener): void;
23
26
  export declare function createAgent(displayName: string, tool: string, roles: string, expertise: string, opts?: CreateAgentOpts): Agent;
24
27
  export declare function getAgentByExternalKey(key: string): Agent | undefined;
28
+ /** Update an agent's project_dir hint (used by codex-supervisor as cwd when
29
+ * spawning the daemon's codex app-server). Idempotent; empty string = clear.
30
+ * No-op for unknown ids. */
31
+ export declare function setAgentProjectDir(agentId: string, projectDir: string): void;
32
+ /** Persist the codex thread/session uuid for a codex agent. Supervisor reads
33
+ * this on daemon (re)spawn and injects via HIVE_AGENT_THREAD_ID env, so the
34
+ * daemon calls `thread/resume` instead of `thread/start` — preserving the
35
+ * jsonl-backed conversation history across daemon kill / serve restart.
36
+ * Empty string clears (used as fallback when resume fails on a stale id). */
37
+ export declare function setAgentThreadId(agentId: string, threadId: string): void;
25
38
  /** Try to set agent.external_key. Silently no-ops on UNIQUE conflict (another
26
39
  * agent already owns that key). Returns true on success, false on conflict. */
27
40
  export declare function trySetAgentExternalKey(agentId: string, key: string): boolean;
@@ -33,6 +46,12 @@ export declare function getAgentByToken(token: string): Agent | undefined;
33
46
  export declare function getAgentById(id: string): Agent | undefined;
34
47
  export declare function getAgentsByName(name: string): Agent[];
35
48
  export declare function listAllAgents(activeOnly?: boolean): Agent[];
49
+ /**
50
+ * Local agents registered with tool='codex'. Used by the codex-supervisor
51
+ * to decide which daemons to auto-spawn on hive serve boot. Excludes remote
52
+ * placeholders (origin_peer != '') since their codex lives on another node.
53
+ */
54
+ export declare function listLocalCodexAgents(): Agent[];
36
55
  /**
37
56
  * Find an active local agent matching a role string.
38
57
  *
@@ -113,6 +132,12 @@ export declare function getAgentTasks(agentId: string, status?: string): Task[];
113
132
  export declare function getTeamTasks(teamId: string, status?: string): Task[];
114
133
  export declare function appendTaskEvent(taskId: string, type: TaskEventType, actorAgentId: string | null, payload?: object): TaskEvent;
115
134
  export declare function getTaskEvents(taskId: string, since?: number, limit?: number): TaskEvent[];
135
+ /** Latest seq number in task_events for a task (0 if no events). Used by the
136
+ * MCP layer to build stable event_id values for push notifications — calling
137
+ * this RIGHT AFTER a handler that appended events gives the seq of that
138
+ * handler's last event. Stable across re-delivery, so channel-side dedup
139
+ * can absorb duplicates from queue replay / daemon retry. */
140
+ export declare function getLatestTaskEventSeq(taskId: string): number;
116
141
  export declare function getReadCursor(agentId: string, targetType: string, targetId: string): number;
117
142
  export declare function setReadCursor(agentId: string, targetType: string, targetId: string, seq: number): void;
118
143
  export interface UnreadSummary {
@@ -167,3 +192,4 @@ export declare function deletePendingInvite(tokenId: string): void;
167
192
  export declare function cleanupExpiredInvites(): number;
168
193
  export declare function isPeerExposed(peerName: string, agentName: string): boolean;
169
194
  export declare function cleanupStaleTasks(maxAgeDays?: number): number;
195
+ export {};
package/dist/db.js CHANGED
@@ -169,6 +169,17 @@ export function initDB(dbPath) {
169
169
  CREATE UNIQUE INDEX IF NOT EXISTS idx_agents_external_key
170
170
  ON agents(external_key) WHERE external_key != '';
171
171
  `);
172
+ // project_dir: operator-supplied working directory hint. codex-supervisor
173
+ // uses it as cwd when spawning the daemon's codex app-server, so the TUI
174
+ // user attaches via `codex --remote` lands in the right project — without
175
+ // it, codex sees serve's cwd and the user sees the wrong files.
176
+ addColumnIfMissing('agents', 'project_dir', "TEXT NOT NULL DEFAULT ''");
177
+ // thread_id: persisted codex thread/session uuid for the agent's daemon.
178
+ // When the daemon respawns, supervisor injects this as HIVE_AGENT_THREAD_ID;
179
+ // daemon then calls `thread/resume {threadId}` instead of `thread/start`,
180
+ // so conversation history (stored as jsonl by codex app-server) survives
181
+ // daemon kill / serve restart / machine reboot.
182
+ addColumnIfMissing('agents', 'thread_id', "TEXT NOT NULL DEFAULT ''");
172
183
  // Migrate tasks.status CHECK constraint when an older DB lacks
173
184
  // 'awaiting_approval' (added in v0.5.6 but the schema migration was missed
174
185
  // until v0.5.9). SQLite has no ALTER COLUMN, so we recreate the table.
@@ -314,6 +325,22 @@ function migrateTeamsAddRules(db) {
314
325
  db.exec("ALTER TABLE teams ADD COLUMN rules TEXT NOT NULL DEFAULT ''");
315
326
  console.log('[db] added teams.rules column');
316
327
  }
328
+ const agentCreateListeners = [];
329
+ export function onAgentCreated(fn) {
330
+ agentCreateListeners.push(fn);
331
+ }
332
+ function emitAgentCreated(agent) {
333
+ for (const fn of agentCreateListeners) {
334
+ try {
335
+ fn(agent);
336
+ }
337
+ catch (err) {
338
+ // listener errors are isolated; don't break createAgent
339
+ // eslint-disable-next-line no-console
340
+ console.error('[db] onAgentCreated listener threw:', err);
341
+ }
342
+ }
343
+ }
317
344
  export function createAgent(displayName, tool, roles, expertise, opts = {}) {
318
345
  const agent = {
319
346
  id: opts.id || ulid(),
@@ -324,11 +351,14 @@ export function createAgent(displayName, tool, roles, expertise, opts = {}) {
324
351
  origin_peer: opts.originPeer || '',
325
352
  remote_id: opts.remoteId || '',
326
353
  external_key: opts.externalKey || '',
354
+ project_dir: opts.projectDir || '',
355
+ thread_id: '',
327
356
  };
328
357
  getDB().prepare(`
329
- INSERT INTO agents (id, display_name, token, tool, roles, expertise, status, created_at, last_seen, origin_peer, remote_id, external_key)
330
- VALUES (@id, @display_name, @token, @tool, @roles, @expertise, @status, @created_at, @last_seen, @origin_peer, @remote_id, @external_key)
358
+ INSERT INTO agents (id, display_name, token, tool, roles, expertise, status, created_at, last_seen, origin_peer, remote_id, external_key, project_dir)
359
+ VALUES (@id, @display_name, @token, @tool, @roles, @expertise, @status, @created_at, @last_seen, @origin_peer, @remote_id, @external_key, @project_dir)
331
360
  `).run(agent);
361
+ emitAgentCreated(agent);
332
362
  return agent;
333
363
  }
334
364
  export function getAgentByExternalKey(key) {
@@ -336,6 +366,20 @@ export function getAgentByExternalKey(key) {
336
366
  return undefined;
337
367
  return getDB().prepare('SELECT * FROM agents WHERE external_key = ?').get(key);
338
368
  }
369
+ /** Update an agent's project_dir hint (used by codex-supervisor as cwd when
370
+ * spawning the daemon's codex app-server). Idempotent; empty string = clear.
371
+ * No-op for unknown ids. */
372
+ export function setAgentProjectDir(agentId, projectDir) {
373
+ getDB().prepare('UPDATE agents SET project_dir = ? WHERE id = ?').run(projectDir || '', agentId);
374
+ }
375
+ /** Persist the codex thread/session uuid for a codex agent. Supervisor reads
376
+ * this on daemon (re)spawn and injects via HIVE_AGENT_THREAD_ID env, so the
377
+ * daemon calls `thread/resume` instead of `thread/start` — preserving the
378
+ * jsonl-backed conversation history across daemon kill / serve restart.
379
+ * Empty string clears (used as fallback when resume fails on a stale id). */
380
+ export function setAgentThreadId(agentId, threadId) {
381
+ getDB().prepare('UPDATE agents SET thread_id = ? WHERE id = ?').run(threadId || '', agentId);
382
+ }
339
383
  /** Try to set agent.external_key. Silently no-ops on UNIQUE conflict (another
340
384
  * agent already owns that key). Returns true on success, false on conflict. */
341
385
  export function trySetAgentExternalKey(agentId, key) {
@@ -388,6 +432,14 @@ export function listAllAgents(activeOnly = false) {
388
432
  : 'SELECT * FROM agents ORDER BY last_seen DESC';
389
433
  return getDB().prepare(sql).all();
390
434
  }
435
+ /**
436
+ * Local agents registered with tool='codex'. Used by the codex-supervisor
437
+ * to decide which daemons to auto-spawn on hive serve boot. Excludes remote
438
+ * placeholders (origin_peer != '') since their codex lives on another node.
439
+ */
440
+ export function listLocalCodexAgents() {
441
+ return getDB().prepare("SELECT * FROM agents WHERE tool = 'codex' AND origin_peer = '' ORDER BY display_name").all();
442
+ }
391
443
  /**
392
444
  * Find an active local agent matching a role string.
393
445
  *
@@ -700,6 +752,15 @@ export function appendTaskEvent(taskId, type, actorAgentId, payload = {}) {
700
752
  export function getTaskEvents(taskId, since = 0, limit = 100) {
701
753
  return getDB().prepare('SELECT * FROM task_events WHERE task_id = ? AND seq > ? ORDER BY seq ASC LIMIT ?').all(taskId, since, limit);
702
754
  }
755
+ /** Latest seq number in task_events for a task (0 if no events). Used by the
756
+ * MCP layer to build stable event_id values for push notifications — calling
757
+ * this RIGHT AFTER a handler that appended events gives the seq of that
758
+ * handler's last event. Stable across re-delivery, so channel-side dedup
759
+ * can absorb duplicates from queue replay / daemon retry. */
760
+ export function getLatestTaskEventSeq(taskId) {
761
+ const row = getDB().prepare('SELECT COALESCE(MAX(seq), 0) AS max_seq FROM task_events WHERE task_id = ?').get(taskId);
762
+ return row.max_seq;
763
+ }
703
764
  // --- Read cursors ---
704
765
  export function getReadCursor(agentId, targetType, targetId) {
705
766
  const row = getDB().prepare('SELECT last_seq FROM read_cursors WHERE agent_id = ? AND target_type = ? AND target_id = ?').get(agentId, targetType, targetId);