kitty-hive 0.6.11 → 0.7.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/channel.ts +10 -1
- package/codex-channel.ts +463 -3
- package/dist/admin-http.js +89 -0
- package/dist/admin-http.js.map +1 -1
- package/dist/codex-channel-runtime.d.ts +126 -0
- package/dist/codex-channel-runtime.js +203 -0
- package/dist/codex-channel-runtime.js.map +1 -0
- package/dist/codex-supervisor.d.ts +66 -0
- package/dist/codex-supervisor.js +317 -0
- package/dist/codex-supervisor.js.map +1 -0
- package/dist/db.d.ts +27 -0
- package/dist/db.js +82 -5
- package/dist/db.js.map +1 -1
- package/dist/index.js +428 -18
- package/dist/index.js.map +1 -1
- package/dist/mcp/agent-tools.js +62 -1
- package/dist/mcp/agent-tools.js.map +1 -1
- package/dist/mcp/dm-tools.js +6 -5
- package/dist/mcp/dm-tools.js.map +1 -1
- package/dist/mcp/server.js +16 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/task-tools.js +30 -10
- package/dist/mcp/task-tools.js.map +1 -1
- package/dist/mcp/team-tools.js +19 -1
- package/dist/mcp/team-tools.js.map +1 -1
- package/dist/models.d.ts +6 -0
- package/dist/models.js.map +1 -1
- package/dist/preview.js +7 -1
- package/dist/preview.js.map +1 -1
- package/dist/server.js +14 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/start.d.ts +1 -0
- package/dist/tools/start.js +55 -12
- package/dist/tools/start.js.map +1 -1
- package/dist/tools/team.d.ts +13 -0
- package/dist/tools/team.js +25 -1
- package/dist/tools/team.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,317 @@
|
|
|
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
|
+
//# 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
|
*
|
|
@@ -75,6 +94,7 @@ export declare function listTeams(activeOnly?: boolean): Team[];
|
|
|
75
94
|
export declare function listTeamsHostedBy(agentId: string): Team[];
|
|
76
95
|
export declare function countTeamMembers(teamId: string): number;
|
|
77
96
|
export declare function transferTeamHost(teamId: string, newHostAgentId: string): void;
|
|
97
|
+
export declare function setTeamRules(teamId: string, rules: string): void;
|
|
78
98
|
export declare function getAgentTeams(agentId: string, activeOnly?: boolean): Team[];
|
|
79
99
|
export declare function addTeamMember(teamId: string, agentId: string, nickname?: string | null): TeamMember;
|
|
80
100
|
export declare function removeTeamMember(teamId: string, agentId: string): void;
|
|
@@ -112,6 +132,12 @@ export declare function getAgentTasks(agentId: string, status?: string): Task[];
|
|
|
112
132
|
export declare function getTeamTasks(teamId: string, status?: string): Task[];
|
|
113
133
|
export declare function appendTaskEvent(taskId: string, type: TaskEventType, actorAgentId: string | null, payload?: object): TaskEvent;
|
|
114
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;
|
|
115
141
|
export declare function getReadCursor(agentId: string, targetType: string, targetId: string): number;
|
|
116
142
|
export declare function setReadCursor(agentId: string, targetType: string, targetId: string, seq: number): void;
|
|
117
143
|
export interface UnreadSummary {
|
|
@@ -166,3 +192,4 @@ export declare function deletePendingInvite(tokenId: string): void;
|
|
|
166
192
|
export declare function cleanupExpiredInvites(): number;
|
|
167
193
|
export declare function isPeerExposed(peerName: string, agentName: string): boolean;
|
|
168
194
|
export declare function cleanupStaleTasks(maxAgeDays?: number): number;
|
|
195
|
+
export {};
|
package/dist/db.js
CHANGED
|
@@ -34,7 +34,8 @@ export function initDB(dbPath) {
|
|
|
34
34
|
name TEXT NOT NULL UNIQUE,
|
|
35
35
|
host_agent_id TEXT REFERENCES agents(id),
|
|
36
36
|
created_at TEXT NOT NULL,
|
|
37
|
-
closed_at TEXT
|
|
37
|
+
closed_at TEXT,
|
|
38
|
+
rules TEXT NOT NULL DEFAULT ''
|
|
38
39
|
);
|
|
39
40
|
|
|
40
41
|
CREATE TABLE IF NOT EXISTS team_members (
|
|
@@ -168,12 +169,24 @@ export function initDB(dbPath) {
|
|
|
168
169
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_agents_external_key
|
|
169
170
|
ON agents(external_key) WHERE external_key != '';
|
|
170
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 ''");
|
|
171
183
|
// Migrate tasks.status CHECK constraint when an older DB lacks
|
|
172
184
|
// 'awaiting_approval' (added in v0.5.6 but the schema migration was missed
|
|
173
185
|
// until v0.5.9). SQLite has no ALTER COLUMN, so we recreate the table.
|
|
174
186
|
migrateTasksStatusCheck(db);
|
|
175
187
|
migrateTeamEventsTypeCheck(db);
|
|
176
188
|
migrateClearChannelRoles(db);
|
|
189
|
+
migrateTeamsAddRules(db);
|
|
177
190
|
db.exec(`
|
|
178
191
|
CREATE INDEX IF NOT EXISTS idx_agents_remote ON agents(origin_peer, remote_id);
|
|
179
192
|
CREATE INDEX IF NOT EXISTS idx_tasks_delegated ON tasks(delegated_peer, delegated_task_id);
|
|
@@ -302,6 +315,32 @@ function migrateClearChannelRoles(db) {
|
|
|
302
315
|
db.prepare("UPDATE agents SET roles = '' WHERE roles = 'channel'").run();
|
|
303
316
|
console.log(`[db] cleared channel-polluted roles on ${before.n} agent(s)`);
|
|
304
317
|
}
|
|
318
|
+
// Add teams.rules column on older DBs (introduced in v0.6.12).
|
|
319
|
+
// SQLite ALTER TABLE supports adding a NOT NULL column when a DEFAULT is given,
|
|
320
|
+
// so this is a single-statement, idempotent migration (PRAGMA detects existence).
|
|
321
|
+
function migrateTeamsAddRules(db) {
|
|
322
|
+
const cols = db.prepare("PRAGMA table_info(teams)").all();
|
|
323
|
+
if (cols.some(c => c.name === 'rules'))
|
|
324
|
+
return; // already migrated (or fresh DB with new schema)
|
|
325
|
+
db.exec("ALTER TABLE teams ADD COLUMN rules TEXT NOT NULL DEFAULT ''");
|
|
326
|
+
console.log('[db] added teams.rules column');
|
|
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
|
+
}
|
|
305
344
|
export function createAgent(displayName, tool, roles, expertise, opts = {}) {
|
|
306
345
|
const agent = {
|
|
307
346
|
id: opts.id || ulid(),
|
|
@@ -312,11 +351,14 @@ export function createAgent(displayName, tool, roles, expertise, opts = {}) {
|
|
|
312
351
|
origin_peer: opts.originPeer || '',
|
|
313
352
|
remote_id: opts.remoteId || '',
|
|
314
353
|
external_key: opts.externalKey || '',
|
|
354
|
+
project_dir: opts.projectDir || '',
|
|
355
|
+
thread_id: '',
|
|
315
356
|
};
|
|
316
357
|
getDB().prepare(`
|
|
317
|
-
INSERT INTO agents (id, display_name, token, tool, roles, expertise, status, created_at, last_seen, origin_peer, remote_id, external_key)
|
|
318
|
-
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)
|
|
319
360
|
`).run(agent);
|
|
361
|
+
emitAgentCreated(agent);
|
|
320
362
|
return agent;
|
|
321
363
|
}
|
|
322
364
|
export function getAgentByExternalKey(key) {
|
|
@@ -324,6 +366,20 @@ export function getAgentByExternalKey(key) {
|
|
|
324
366
|
return undefined;
|
|
325
367
|
return getDB().prepare('SELECT * FROM agents WHERE external_key = ?').get(key);
|
|
326
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
|
+
}
|
|
327
383
|
/** Try to set agent.external_key. Silently no-ops on UNIQUE conflict (another
|
|
328
384
|
* agent already owns that key). Returns true on success, false on conflict. */
|
|
329
385
|
export function trySetAgentExternalKey(agentId, key) {
|
|
@@ -376,6 +432,14 @@ export function listAllAgents(activeOnly = false) {
|
|
|
376
432
|
: 'SELECT * FROM agents ORDER BY last_seen DESC';
|
|
377
433
|
return getDB().prepare(sql).all();
|
|
378
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
|
+
}
|
|
379
443
|
/**
|
|
380
444
|
* Find an active local agent matching a role string.
|
|
381
445
|
*
|
|
@@ -476,10 +540,11 @@ export function createTeam(name, hostAgentId) {
|
|
|
476
540
|
id: ulid(), name,
|
|
477
541
|
host_agent_id: hostAgentId,
|
|
478
542
|
created_at: nowISO(), closed_at: null,
|
|
543
|
+
rules: '',
|
|
479
544
|
};
|
|
480
545
|
getDB().prepare(`
|
|
481
|
-
INSERT INTO teams (id, name, host_agent_id, created_at, closed_at)
|
|
482
|
-
VALUES (@id, @name, @host_agent_id, @created_at, @closed_at)
|
|
546
|
+
INSERT INTO teams (id, name, host_agent_id, created_at, closed_at, rules)
|
|
547
|
+
VALUES (@id, @name, @host_agent_id, @created_at, @closed_at, @rules)
|
|
483
548
|
`).run(team);
|
|
484
549
|
return team;
|
|
485
550
|
}
|
|
@@ -505,6 +570,9 @@ export function countTeamMembers(teamId) {
|
|
|
505
570
|
export function transferTeamHost(teamId, newHostAgentId) {
|
|
506
571
|
getDB().prepare('UPDATE teams SET host_agent_id = ? WHERE id = ?').run(newHostAgentId, teamId);
|
|
507
572
|
}
|
|
573
|
+
export function setTeamRules(teamId, rules) {
|
|
574
|
+
getDB().prepare('UPDATE teams SET rules = ? WHERE id = ?').run(rules ?? '', teamId);
|
|
575
|
+
}
|
|
508
576
|
export function getAgentTeams(agentId, activeOnly = true) {
|
|
509
577
|
const sql = activeOnly
|
|
510
578
|
? `SELECT t.* FROM teams t JOIN team_members tm ON tm.team_id = t.id WHERE tm.agent_id = ? AND t.closed_at IS NULL ORDER BY tm.joined_at DESC`
|
|
@@ -684,6 +752,15 @@ export function appendTaskEvent(taskId, type, actorAgentId, payload = {}) {
|
|
|
684
752
|
export function getTaskEvents(taskId, since = 0, limit = 100) {
|
|
685
753
|
return getDB().prepare('SELECT * FROM task_events WHERE task_id = ? AND seq > ? ORDER BY seq ASC LIMIT ?').all(taskId, since, limit);
|
|
686
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
|
+
}
|
|
687
764
|
// --- Read cursors ---
|
|
688
765
|
export function getReadCursor(agentId, targetType, targetId) {
|
|
689
766
|
const row = getDB().prepare('SELECT last_seq FROM read_cursors WHERE agent_id = ? AND target_type = ? AND target_id = ?').get(agentId, targetType, targetId);
|