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
package/dist/admin-http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-http.js","sourceRoot":"","sources":["../src/admin-http.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"admin-http.js","sourceRoot":"","sources":["../src/admin-http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codex-channel-runtime — the testable core of codex-channel.ts.
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates everything that talks to a codex app-server's JSON-RPC v2
|
|
5
|
+
* protocol (https://github.com/openai/codex 0.124+): issuing `turn/start`,
|
|
6
|
+
* tracking in-flight turns by their server-assigned turn id, and reacting to
|
|
7
|
+
* `turn/completed` / `turn/interrupt` / `error` notifications.
|
|
8
|
+
*
|
|
9
|
+
* The 2026-05-26 monkeys-tester incident exposed two bugs in the previous
|
|
10
|
+
* implementation that this module replaces:
|
|
11
|
+
*
|
|
12
|
+
* 1. **FIFO waiter mismatch.** sendTurn pushed a bare `resolve` onto an
|
|
13
|
+
* array; the WS handler shifted from the front on any `turn/completed`.
|
|
14
|
+
* A timeout left orphan resolvers behind, so later `turn/completed`s
|
|
15
|
+
* woke the wrong waiter — cascading misroutes silently failed all
|
|
16
|
+
* subsequent turns.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Retry-after-turn/start duplication.** When the daemon's wait for
|
|
19
|
+
* `turn/completed` timed out at 10 min, the old retry loop issued a
|
|
20
|
+
* fresh `turn/start` — but codex had already created the turn and
|
|
21
|
+
* written the input text into the thread. Each retry produced another
|
|
22
|
+
* duplicate turn. Affected agent saw the same DM 2-3× in their codex
|
|
23
|
+
* thread, sometimes more.
|
|
24
|
+
*
|
|
25
|
+
* Design:
|
|
26
|
+
*
|
|
27
|
+
* - One `TurnTracker` instance per daemon, owns a `Map<turnId, Waiter>`
|
|
28
|
+
* keyed by the turn id from `turn/start` response. WS notifications are
|
|
29
|
+
* routed by `turnId` (or `turn.id`) — no FIFO assumption.
|
|
30
|
+
*
|
|
31
|
+
* - `sendTurn(text)` returns a `TurnOutcome` discriminated-union. Callers
|
|
32
|
+
* branch on `outcome.kind` instead of catching errors. The kind is one of
|
|
33
|
+
* `completed | failed | interrupted | timeout | rpc_send_error |
|
|
34
|
+
* skipped_duplicate`. This makes the "do not retry on timeout" rule
|
|
35
|
+
* explicit in the type rather than relying on caller discipline.
|
|
36
|
+
*
|
|
37
|
+
* - `sendTurn(text, {eventId})` dedups by `eventId`. Once a turn/start has
|
|
38
|
+
* been issued for an eventId (whether successful, timeout, or anything
|
|
39
|
+
* else short of a pre-send error), the tracker refuses to issue another
|
|
40
|
+
* turn/start for the same eventId in this process lifetime. Conservative
|
|
41
|
+
* trade-off: prefer losing one event over duplicating it. Hive's SSE
|
|
42
|
+
* replay/pending_pushes can recover lost events; nothing recovers a
|
|
43
|
+
* duplicated turn from a user's codex thread.
|
|
44
|
+
*/
|
|
45
|
+
export interface CodexTurn {
|
|
46
|
+
id: string;
|
|
47
|
+
status?: string;
|
|
48
|
+
[k: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
export interface TurnStartResponse {
|
|
51
|
+
turn: CodexTurn;
|
|
52
|
+
}
|
|
53
|
+
export interface ErrorNotificationParams {
|
|
54
|
+
error: unknown;
|
|
55
|
+
threadId: string;
|
|
56
|
+
turnId: string;
|
|
57
|
+
willRetry: boolean;
|
|
58
|
+
}
|
|
59
|
+
export type TurnOutcome = {
|
|
60
|
+
kind: 'completed';
|
|
61
|
+
turnId: string;
|
|
62
|
+
turn: CodexTurn;
|
|
63
|
+
} | {
|
|
64
|
+
kind: 'failed';
|
|
65
|
+
turnId: string;
|
|
66
|
+
willRetry: boolean;
|
|
67
|
+
error: unknown;
|
|
68
|
+
} | {
|
|
69
|
+
kind: 'interrupted';
|
|
70
|
+
turnId: string;
|
|
71
|
+
turn?: CodexTurn;
|
|
72
|
+
} | {
|
|
73
|
+
kind: 'timeout';
|
|
74
|
+
turnId: string;
|
|
75
|
+
afterMs: number;
|
|
76
|
+
} | {
|
|
77
|
+
kind: 'rpc_send_error';
|
|
78
|
+
error: Error;
|
|
79
|
+
} | {
|
|
80
|
+
kind: 'skipped_duplicate';
|
|
81
|
+
eventId: string;
|
|
82
|
+
};
|
|
83
|
+
/** Adapter so the tracker can stay agnostic about whether the underlying
|
|
84
|
+
* transport is a real WebSocket (codex-channel.ts) or a stub (tests). */
|
|
85
|
+
export interface RpcTransport {
|
|
86
|
+
/** Send a JSON-RPC request and resolve to its `result`. Reject with Error
|
|
87
|
+
* on network failure, RPC-level error, or timeout. */
|
|
88
|
+
call(method: string, params: unknown, timeoutMs?: number): Promise<unknown>;
|
|
89
|
+
}
|
|
90
|
+
export interface TurnTrackerOptions {
|
|
91
|
+
/** How long to wait for `turn/completed` after `turn/start` succeeds.
|
|
92
|
+
* Default 10 min — matches codex's typical long-running turn budget. */
|
|
93
|
+
turnTimeoutMs?: number;
|
|
94
|
+
/** Hook for tests / observability. Called once per terminal outcome. */
|
|
95
|
+
onOutcome?: (outcome: TurnOutcome, eventId?: string) => void;
|
|
96
|
+
}
|
|
97
|
+
export declare class TurnTracker {
|
|
98
|
+
private readonly transport;
|
|
99
|
+
private readonly threadId;
|
|
100
|
+
private waiters;
|
|
101
|
+
private injectedEventIds;
|
|
102
|
+
private readonly turnTimeoutMs;
|
|
103
|
+
private readonly onOutcome?;
|
|
104
|
+
constructor(transport: RpcTransport, threadId: string, opts?: TurnTrackerOptions);
|
|
105
|
+
/** Issue `turn/start` and resolve when codex reports the turn's terminal
|
|
106
|
+
* state. Never throws — failure modes are encoded in the returned
|
|
107
|
+
* `TurnOutcome.kind` so callers can branch declaratively. */
|
|
108
|
+
sendTurn(text: string, opts?: {
|
|
109
|
+
eventId?: string;
|
|
110
|
+
}): Promise<TurnOutcome>;
|
|
111
|
+
/** Route a codex JSON-RPC notification (anything that isn't an RPC response
|
|
112
|
+
* to a request the daemon issued). The caller — typically the WS message
|
|
113
|
+
* handler — should invoke this for every parsed notification. Returns
|
|
114
|
+
* true if the notification matched a waiter, false otherwise (useful for
|
|
115
|
+
* diagnostic logging). */
|
|
116
|
+
handleNotification(method: string, params: unknown): boolean;
|
|
117
|
+
/** Diagnostic snapshot of in-flight turns. Not used at runtime; exposed
|
|
118
|
+
* for the optional `/admin/codex-daemons` extension and tests. */
|
|
119
|
+
snapshot(): {
|
|
120
|
+
activeTurns: string[];
|
|
121
|
+
injectedEventIds: number;
|
|
122
|
+
};
|
|
123
|
+
/** Test-only: drop all pending waiters (no resolution). Use to simulate
|
|
124
|
+
* daemon shutdown for cleanup tests. */
|
|
125
|
+
destroyForTest(): void;
|
|
126
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codex-channel-runtime — the testable core of codex-channel.ts.
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates everything that talks to a codex app-server's JSON-RPC v2
|
|
5
|
+
* protocol (https://github.com/openai/codex 0.124+): issuing `turn/start`,
|
|
6
|
+
* tracking in-flight turns by their server-assigned turn id, and reacting to
|
|
7
|
+
* `turn/completed` / `turn/interrupt` / `error` notifications.
|
|
8
|
+
*
|
|
9
|
+
* The 2026-05-26 monkeys-tester incident exposed two bugs in the previous
|
|
10
|
+
* implementation that this module replaces:
|
|
11
|
+
*
|
|
12
|
+
* 1. **FIFO waiter mismatch.** sendTurn pushed a bare `resolve` onto an
|
|
13
|
+
* array; the WS handler shifted from the front on any `turn/completed`.
|
|
14
|
+
* A timeout left orphan resolvers behind, so later `turn/completed`s
|
|
15
|
+
* woke the wrong waiter — cascading misroutes silently failed all
|
|
16
|
+
* subsequent turns.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Retry-after-turn/start duplication.** When the daemon's wait for
|
|
19
|
+
* `turn/completed` timed out at 10 min, the old retry loop issued a
|
|
20
|
+
* fresh `turn/start` — but codex had already created the turn and
|
|
21
|
+
* written the input text into the thread. Each retry produced another
|
|
22
|
+
* duplicate turn. Affected agent saw the same DM 2-3× in their codex
|
|
23
|
+
* thread, sometimes more.
|
|
24
|
+
*
|
|
25
|
+
* Design:
|
|
26
|
+
*
|
|
27
|
+
* - One `TurnTracker` instance per daemon, owns a `Map<turnId, Waiter>`
|
|
28
|
+
* keyed by the turn id from `turn/start` response. WS notifications are
|
|
29
|
+
* routed by `turnId` (or `turn.id`) — no FIFO assumption.
|
|
30
|
+
*
|
|
31
|
+
* - `sendTurn(text)` returns a `TurnOutcome` discriminated-union. Callers
|
|
32
|
+
* branch on `outcome.kind` instead of catching errors. The kind is one of
|
|
33
|
+
* `completed | failed | interrupted | timeout | rpc_send_error |
|
|
34
|
+
* skipped_duplicate`. This makes the "do not retry on timeout" rule
|
|
35
|
+
* explicit in the type rather than relying on caller discipline.
|
|
36
|
+
*
|
|
37
|
+
* - `sendTurn(text, {eventId})` dedups by `eventId`. Once a turn/start has
|
|
38
|
+
* been issued for an eventId (whether successful, timeout, or anything
|
|
39
|
+
* else short of a pre-send error), the tracker refuses to issue another
|
|
40
|
+
* turn/start for the same eventId in this process lifetime. Conservative
|
|
41
|
+
* trade-off: prefer losing one event over duplicating it. Hive's SSE
|
|
42
|
+
* replay/pending_pushes can recover lost events; nothing recovers a
|
|
43
|
+
* duplicated turn from a user's codex thread.
|
|
44
|
+
*/
|
|
45
|
+
export class TurnTracker {
|
|
46
|
+
transport;
|
|
47
|
+
threadId;
|
|
48
|
+
waiters = new Map();
|
|
49
|
+
injectedEventIds = new Set();
|
|
50
|
+
turnTimeoutMs;
|
|
51
|
+
onOutcome;
|
|
52
|
+
constructor(transport, threadId, opts = {}) {
|
|
53
|
+
this.transport = transport;
|
|
54
|
+
this.threadId = threadId;
|
|
55
|
+
this.turnTimeoutMs = opts.turnTimeoutMs ?? 600_000;
|
|
56
|
+
this.onOutcome = opts.onOutcome;
|
|
57
|
+
}
|
|
58
|
+
/** Issue `turn/start` and resolve when codex reports the turn's terminal
|
|
59
|
+
* state. Never throws — failure modes are encoded in the returned
|
|
60
|
+
* `TurnOutcome.kind` so callers can branch declaratively. */
|
|
61
|
+
async sendTurn(text, opts = {}) {
|
|
62
|
+
const { eventId } = opts;
|
|
63
|
+
// Idempotency: refuse to re-issue turn/start for an event we've already
|
|
64
|
+
// tried, regardless of how that prior attempt ended. Without this, a
|
|
65
|
+
// caller that catches an outcome.kind === 'timeout' and re-queues the
|
|
66
|
+
// event would re-trigger the original duplication bug — codex would
|
|
67
|
+
// create a second turn in the thread for the same logical hive event.
|
|
68
|
+
if (eventId && this.injectedEventIds.has(eventId)) {
|
|
69
|
+
const outcome = { kind: 'skipped_duplicate', eventId };
|
|
70
|
+
this.onOutcome?.(outcome, eventId);
|
|
71
|
+
return outcome;
|
|
72
|
+
}
|
|
73
|
+
if (eventId)
|
|
74
|
+
this.injectedEventIds.add(eventId);
|
|
75
|
+
let resp;
|
|
76
|
+
try {
|
|
77
|
+
resp = (await this.transport.call('turn/start', {
|
|
78
|
+
threadId: this.threadId,
|
|
79
|
+
input: [{ type: 'text', text }],
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
// Pre-RPC-response failure: TCP/WS error, or rpcCall timeout (codex
|
|
84
|
+
// didn't reply within rpcCall's own deadline — much shorter than the
|
|
85
|
+
// turn deadline). NOTE we still keep the eventId in injectedEventIds
|
|
86
|
+
// because we cannot prove codex didn't receive the start; resending
|
|
87
|
+
// the same eventId might create a duplicate turn there. Conservative
|
|
88
|
+
// by design.
|
|
89
|
+
const outcome = {
|
|
90
|
+
kind: 'rpc_send_error',
|
|
91
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
92
|
+
};
|
|
93
|
+
this.onOutcome?.(outcome, eventId);
|
|
94
|
+
return outcome;
|
|
95
|
+
}
|
|
96
|
+
const turnId = resp?.turn?.id;
|
|
97
|
+
if (!turnId) {
|
|
98
|
+
const outcome = {
|
|
99
|
+
kind: 'rpc_send_error',
|
|
100
|
+
error: new Error(`turn/start response missing turn.id: ${JSON.stringify(resp)}`),
|
|
101
|
+
};
|
|
102
|
+
this.onOutcome?.(outcome, eventId);
|
|
103
|
+
return outcome;
|
|
104
|
+
}
|
|
105
|
+
// Register waiter keyed by turn id. Codex's notifications include turnId
|
|
106
|
+
// — we use that to route, not the FIFO order. Multiple concurrent turns
|
|
107
|
+
// on the same thread (rare for our use case but supported by codex) work
|
|
108
|
+
// correctly because each waiter is independently identified.
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
const timer = setTimeout(() => {
|
|
111
|
+
// Codex never fired turn/completed within budget. The turn is still
|
|
112
|
+
// (possibly) in-flight on codex's side and the input text is in
|
|
113
|
+
// thread history. We do NOT re-issue turn/start. Caller drops the
|
|
114
|
+
// event; if codex eventually completes the turn, that completion
|
|
115
|
+
// notification will hit our handler with no waiter to resolve, and
|
|
116
|
+
// is safely ignored.
|
|
117
|
+
if (this.waiters.has(turnId)) {
|
|
118
|
+
this.waiters.delete(turnId);
|
|
119
|
+
const outcome = { kind: 'timeout', turnId, afterMs: this.turnTimeoutMs };
|
|
120
|
+
this.onOutcome?.(outcome, eventId);
|
|
121
|
+
resolve(outcome);
|
|
122
|
+
}
|
|
123
|
+
}, this.turnTimeoutMs);
|
|
124
|
+
this.waiters.set(turnId, {
|
|
125
|
+
turnId,
|
|
126
|
+
resolve: (outcome) => {
|
|
127
|
+
clearTimeout(timer);
|
|
128
|
+
this.waiters.delete(turnId);
|
|
129
|
+
this.onOutcome?.(outcome, eventId);
|
|
130
|
+
resolve(outcome);
|
|
131
|
+
},
|
|
132
|
+
timer,
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/** Route a codex JSON-RPC notification (anything that isn't an RPC response
|
|
137
|
+
* to a request the daemon issued). The caller — typically the WS message
|
|
138
|
+
* handler — should invoke this for every parsed notification. Returns
|
|
139
|
+
* true if the notification matched a waiter, false otherwise (useful for
|
|
140
|
+
* diagnostic logging). */
|
|
141
|
+
handleNotification(method, params) {
|
|
142
|
+
if (method === 'turn/completed') {
|
|
143
|
+
const p = params;
|
|
144
|
+
const turnId = p?.turn?.id;
|
|
145
|
+
if (!turnId)
|
|
146
|
+
return false;
|
|
147
|
+
const w = this.waiters.get(turnId);
|
|
148
|
+
if (!w)
|
|
149
|
+
return false; // unknown turn (late completion after timeout etc.)
|
|
150
|
+
w.resolve({ kind: 'completed', turnId, turn: p.turn });
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (method === 'turn/interrupt') {
|
|
154
|
+
const p = params;
|
|
155
|
+
const turnId = p?.turn?.id;
|
|
156
|
+
if (!turnId)
|
|
157
|
+
return false;
|
|
158
|
+
const w = this.waiters.get(turnId);
|
|
159
|
+
if (!w)
|
|
160
|
+
return false;
|
|
161
|
+
w.resolve({ kind: 'interrupted', turnId, turn: p.turn });
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
if (method === 'error') {
|
|
165
|
+
const p = params;
|
|
166
|
+
const turnId = p?.turnId;
|
|
167
|
+
if (!turnId)
|
|
168
|
+
return false;
|
|
169
|
+
const w = this.waiters.get(turnId);
|
|
170
|
+
if (!w)
|
|
171
|
+
return false;
|
|
172
|
+
// willRetry === true means codex's own retry layer will try the turn
|
|
173
|
+
// again internally — leave the waiter pending and wait for the eventual
|
|
174
|
+
// completion / final error. Only resolve when willRetry is false.
|
|
175
|
+
if (p.willRetry === true)
|
|
176
|
+
return false;
|
|
177
|
+
w.resolve({
|
|
178
|
+
kind: 'failed',
|
|
179
|
+
turnId,
|
|
180
|
+
willRetry: !!p.willRetry,
|
|
181
|
+
error: p.error ?? null,
|
|
182
|
+
});
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
/** Diagnostic snapshot of in-flight turns. Not used at runtime; exposed
|
|
188
|
+
* for the optional `/admin/codex-daemons` extension and tests. */
|
|
189
|
+
snapshot() {
|
|
190
|
+
return {
|
|
191
|
+
activeTurns: [...this.waiters.keys()],
|
|
192
|
+
injectedEventIds: this.injectedEventIds.size,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/** Test-only: drop all pending waiters (no resolution). Use to simulate
|
|
196
|
+
* daemon shutdown for cleanup tests. */
|
|
197
|
+
destroyForTest() {
|
|
198
|
+
for (const w of this.waiters.values())
|
|
199
|
+
clearTimeout(w.timer);
|
|
200
|
+
this.waiters.clear();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=codex-channel-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-channel-runtime.js","sourceRoot":"","sources":["../src/codex-channel-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
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;
|