@excitedjs/dreamux 0.11.3 → 0.12.1-beta.49
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/CHANGELOG.json +12 -0
- package/CHANGELOG.md +8 -1
- package/README.md +170 -61
- package/dist/admin/methods.js +72 -0
- package/dist/admin/methods.js.map +1 -1
- package/dist/agent-runtime/catalog.js +66 -0
- package/dist/agent-runtime/catalog.js.map +1 -0
- package/dist/agent-runtime/claude-code-session.js +268 -0
- package/dist/agent-runtime/claude-code-session.js.map +1 -0
- package/dist/agent-runtime/claude-code.js +318 -0
- package/dist/agent-runtime/claude-code.js.map +1 -0
- package/dist/agent-runtime/codex.js +60 -0
- package/dist/agent-runtime/codex.js.map +1 -0
- package/dist/agent-runtime/index.js +6 -0
- package/dist/agent-runtime/index.js.map +1 -0
- package/dist/agent-runtime/types.js +2 -0
- package/dist/agent-runtime/types.js.map +1 -0
- package/dist/channel/channel-providers.js +43 -0
- package/dist/channel/channel-providers.js.map +1 -0
- package/dist/channel/feishu-provider.js +58 -0
- package/dist/channel/feishu-provider.js.map +1 -0
- package/dist/channel/provider.js +51 -0
- package/dist/channel/provider.js.map +1 -0
- package/dist/cli/doctor.js +108 -25
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/dreamux.js +35 -1
- package/dist/cli/dreamux.js.map +1 -1
- package/dist/cli/server.js +21 -13
- package/dist/cli/server.js.map +1 -1
- package/dist/codex/mcp-config.js +18 -8
- package/dist/codex/mcp-config.js.map +1 -1
- package/dist/daemon/install.js +34 -3
- package/dist/daemon/install.js.map +1 -1
- package/dist/dispatcher/runtime.js +51 -0
- package/dist/dispatcher/runtime.js.map +1 -1
- package/dist/mcp/teammate-mcp.js +277 -0
- package/dist/mcp/teammate-mcp.js.map +1 -0
- package/dist/onboard/config-files.js +39 -40
- package/dist/onboard/config-files.js.map +1 -1
- package/dist/onboard/run.js +12 -9
- package/dist/onboard/run.js.map +1 -1
- package/dist/onboard/service.js +6 -1
- package/dist/onboard/service.js.map +1 -1
- package/dist/registry/builtins.js +99 -0
- package/dist/registry/builtins.js.map +1 -0
- package/dist/registry/index.js +11 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/provider-ref.js +100 -0
- package/dist/registry/provider-ref.js.map +1 -0
- package/dist/registry/registry.js +124 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/runtime/claude-code-args.js +69 -0
- package/dist/runtime/claude-code-args.js.map +1 -0
- package/dist/runtime/claude-code-stream.js +251 -0
- package/dist/runtime/claude-code-stream.js.map +1 -0
- package/dist/runtime/codex-args.js +16 -11
- package/dist/runtime/codex-args.js.map +1 -1
- package/dist/runtime/config.js +257 -69
- package/dist/runtime/config.js.map +1 -1
- package/dist/runtime/dispatcher-store.js +18 -11
- package/dist/runtime/dispatcher-store.js.map +1 -1
- package/dist/runtime/paths.js +43 -0
- package/dist/runtime/paths.js.map +1 -1
- package/dist/runtime/secrets.js +7 -3
- package/dist/runtime/secrets.js.map +1 -1
- package/dist/server.js +226 -54
- package/dist/server.js.map +1 -1
- package/dist/teammate/delivery.js +124 -0
- package/dist/teammate/delivery.js.map +1 -0
- package/dist/teammate/ledger.js +505 -0
- package/dist/teammate/ledger.js.map +1 -0
- package/dist/teammate/mcp-config.js +19 -0
- package/dist/teammate/mcp-config.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The resident Claude Code stream-json session (issue #120).
|
|
3
|
+
*
|
|
4
|
+
* This is the process-supervision seam for `builtin:claude-code`. It replaces
|
|
5
|
+
* the retired one-shot `claude --print <prompt>` model — where every turn paid a
|
|
6
|
+
* fresh process cold-start and there was no session continuity beyond
|
|
7
|
+
* `--resume` — with a single long-lived `claude --print --input-format
|
|
8
|
+
* stream-json` child that holds stdin/stdout open and serves many turns.
|
|
9
|
+
*
|
|
10
|
+
* Responsibilities (mirrors the role `codex/supervisor.ts` plays for Codex):
|
|
11
|
+
*
|
|
12
|
+
* - **Spawn + hold.** `start()` spawns the child in its own process group
|
|
13
|
+
* (so a leaked grandchild can be group-killed on reap) and resolves once the
|
|
14
|
+
* child is up. The child does NOT emit `init` until the first user message
|
|
15
|
+
* arrives, so readiness must not wait on it — the session id is captured from
|
|
16
|
+
* the first turn's `init` / `result`.
|
|
17
|
+
* - **Serialize turns.** `submitTurn()` writes one `user` message line to
|
|
18
|
+
* stdin and resolves with the aggregated `TurnOutcome` when the terminal
|
|
19
|
+
* `result` lands. Callers MUST serialize (the runtime's turn queue does);
|
|
20
|
+
* a second concurrent `submitTurn` is rejected rather than interleaving two
|
|
21
|
+
* turns on one stdin.
|
|
22
|
+
* - **Demux stdout.** Each NDJSON line is parsed by the pure
|
|
23
|
+
* `runtime/claude-code-stream.ts` model; data-plane envelopes feed the
|
|
24
|
+
* in-flight aggregator, control requests are answered defensively so an
|
|
25
|
+
* unattended turn never wedges on a permission callback.
|
|
26
|
+
* - **Surface exit.** A child exit mid-turn rejects the in-flight turn; an
|
|
27
|
+
* exit at any time fires `onExit` so the runtime can mark itself degraded and
|
|
28
|
+
* re-spawn (with `--resume`) on the next turn.
|
|
29
|
+
*
|
|
30
|
+
* The seam is injectable ({@link ClaudeCodeSessionFactory}) so the runtime
|
|
31
|
+
* lifecycle is unit-testable with a fake session and no live `claude` binary.
|
|
32
|
+
*/
|
|
33
|
+
import { spawn } from 'node:child_process';
|
|
34
|
+
import { mkdir, open } from 'node:fs/promises';
|
|
35
|
+
import { dirname } from 'node:path';
|
|
36
|
+
import { isProcessAlive, killProcessGroup } from '../codex/supervisor.js';
|
|
37
|
+
import { buildCanUseToolAllow, buildControlAck, buildUserMessage, LineBuffer, parseLine, TurnAggregator, } from '../runtime/claude-code-stream.js';
|
|
38
|
+
/** The live session: spawns and supervises the real `claude` child. */
|
|
39
|
+
class LiveClaudeCodeSession {
|
|
40
|
+
spec;
|
|
41
|
+
child = null;
|
|
42
|
+
pid = null;
|
|
43
|
+
exited = false;
|
|
44
|
+
stopped = false;
|
|
45
|
+
lineBuf = new LineBuffer();
|
|
46
|
+
pending = null;
|
|
47
|
+
constructor(spec) {
|
|
48
|
+
this.spec = spec;
|
|
49
|
+
}
|
|
50
|
+
isAlive() {
|
|
51
|
+
return this.child !== null && !this.exited;
|
|
52
|
+
}
|
|
53
|
+
async start() {
|
|
54
|
+
if (this.child !== null) {
|
|
55
|
+
throw new Error('ClaudeCodeSession.start: already started');
|
|
56
|
+
}
|
|
57
|
+
await mkdir(this.spec.cwd, { recursive: true });
|
|
58
|
+
await mkdir(dirname(this.spec.stderrLogPath), { recursive: true });
|
|
59
|
+
// Open the stderr log as a FileHandle and hand its fd to the child. The
|
|
60
|
+
// handle is closed once the child owns the inherited fd (the finally),
|
|
61
|
+
// matching the timing discipline in codex/supervisor.ts.
|
|
62
|
+
const stderrHandle = await open(this.spec.stderrLogPath, 'a', 0o600);
|
|
63
|
+
const spawnOpts = {
|
|
64
|
+
cwd: this.spec.cwd,
|
|
65
|
+
env: this.spec.env,
|
|
66
|
+
// Own process group so a leaked grandchild (the rust/node split a CLI may
|
|
67
|
+
// fork) is group-killable on reap, never a leak.
|
|
68
|
+
detached: true,
|
|
69
|
+
stdio: ['pipe', 'pipe', stderrHandle.fd],
|
|
70
|
+
};
|
|
71
|
+
let child;
|
|
72
|
+
try {
|
|
73
|
+
child = await new Promise((resolve, reject) => {
|
|
74
|
+
let settled = false;
|
|
75
|
+
const c = spawn(this.spec.bin, this.spec.args, spawnOpts);
|
|
76
|
+
c.once('error', (e) => {
|
|
77
|
+
if (settled)
|
|
78
|
+
return;
|
|
79
|
+
settled = true;
|
|
80
|
+
reject(e instanceof Error ? e : new Error(String(e)));
|
|
81
|
+
});
|
|
82
|
+
c.once('spawn', () => {
|
|
83
|
+
if (settled)
|
|
84
|
+
return;
|
|
85
|
+
settled = true;
|
|
86
|
+
resolve(c);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await stderrHandle.close();
|
|
92
|
+
}
|
|
93
|
+
if (child.pid === undefined) {
|
|
94
|
+
throw new Error('claude resident child spawned without a pid');
|
|
95
|
+
}
|
|
96
|
+
this.child = child;
|
|
97
|
+
this.pid = child.pid;
|
|
98
|
+
// Post-spawn `error` must not crash the host event loop.
|
|
99
|
+
child.on('error', (err) => {
|
|
100
|
+
this.spec.log?.('warn', 'claude resident child error', err);
|
|
101
|
+
});
|
|
102
|
+
child.stdout?.setEncoding('utf8');
|
|
103
|
+
child.stdout?.on('data', (chunk) => {
|
|
104
|
+
for (const line of this.lineBuf.push(chunk))
|
|
105
|
+
this.onLine(parseLine(line));
|
|
106
|
+
});
|
|
107
|
+
child.once('exit', () => this.onChildExit());
|
|
108
|
+
}
|
|
109
|
+
async submitTurn(prompt) {
|
|
110
|
+
if (this.stopped) {
|
|
111
|
+
return Promise.reject(new Error('claude resident session is stopped'));
|
|
112
|
+
}
|
|
113
|
+
const stdin = this.child?.stdin;
|
|
114
|
+
if (this.child === null || this.exited || stdin == null || !stdin.writable) {
|
|
115
|
+
return Promise.reject(new Error('claude resident child is not running'));
|
|
116
|
+
}
|
|
117
|
+
if (this.pending !== null) {
|
|
118
|
+
return Promise.reject(new Error('claude resident session is already mid-turn'));
|
|
119
|
+
}
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
const pending = {
|
|
122
|
+
resolve,
|
|
123
|
+
reject,
|
|
124
|
+
aggregator: new TurnAggregator(),
|
|
125
|
+
timer: null,
|
|
126
|
+
};
|
|
127
|
+
// Per-turn deadline: a still-alive child that never emits a terminal
|
|
128
|
+
// `result` (e.g. it stalls waiting on input, or only streams
|
|
129
|
+
// `init`/`assistant`/control envelopes) must not pend forever. On the
|
|
130
|
+
// deadline, fail this turn and reap the child — `isAlive()` then goes
|
|
131
|
+
// false, so the runtime re-spawns (with `--resume`) on the next turn
|
|
132
|
+
// rather than reusing a child with half a turn's output buffered.
|
|
133
|
+
pending.timer = setTimeout(() => {
|
|
134
|
+
if (this.pending !== pending)
|
|
135
|
+
return;
|
|
136
|
+
this.pending = null;
|
|
137
|
+
this.spec.log?.('error', `claude turn timed out after ${this.spec.turnTimeoutMs}ms; reaping resident child`);
|
|
138
|
+
reject(new Error(`claude resident turn timed out after ${this.spec.turnTimeoutMs}ms without a result`));
|
|
139
|
+
// Reap is fire-and-forget: the turn has already been rejected, and the
|
|
140
|
+
// next turn will re-spawn a fresh child.
|
|
141
|
+
void this.stop().catch(() => {
|
|
142
|
+
/* reap is best-effort */
|
|
143
|
+
});
|
|
144
|
+
}, this.spec.turnTimeoutMs);
|
|
145
|
+
this.pending = pending;
|
|
146
|
+
// A failed stdin write must fail the turn (and not leave it dangling).
|
|
147
|
+
stdin.write(`${buildUserMessage(prompt)}\n`, (err) => {
|
|
148
|
+
if (err != null && this.pending === pending) {
|
|
149
|
+
this.settlePending()?.reject(err instanceof Error ? err : new Error(String(err)));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Detach the in-flight turn: clear its deadline timer and null `pending`,
|
|
156
|
+
* returning it so the caller can resolve or reject it exactly once.
|
|
157
|
+
*/
|
|
158
|
+
settlePending() {
|
|
159
|
+
const pending = this.pending;
|
|
160
|
+
if (pending === null)
|
|
161
|
+
return null;
|
|
162
|
+
if (pending.timer !== null)
|
|
163
|
+
clearTimeout(pending.timer);
|
|
164
|
+
this.pending = null;
|
|
165
|
+
return pending;
|
|
166
|
+
}
|
|
167
|
+
async stop() {
|
|
168
|
+
if (this.stopped)
|
|
169
|
+
return;
|
|
170
|
+
this.stopped = true;
|
|
171
|
+
// Mark exited up front so the child's own `exit` event (fired by the kill
|
|
172
|
+
// below) is treated as a deliberate stop, never an unexpected exit that
|
|
173
|
+
// would fire `onExit` → degrade the runtime we are intentionally tearing
|
|
174
|
+
// down.
|
|
175
|
+
this.exited = true;
|
|
176
|
+
this.failPending(new Error('claude resident session stopped mid-turn'));
|
|
177
|
+
const pid = this.pid;
|
|
178
|
+
if (pid !== null) {
|
|
179
|
+
if (isProcessAlive(pid)) {
|
|
180
|
+
killProcessGroup(pid, 'SIGTERM');
|
|
181
|
+
const deadline = Date.now() + 1000;
|
|
182
|
+
while (Date.now() < deadline) {
|
|
183
|
+
if (!isProcessAlive(pid))
|
|
184
|
+
break;
|
|
185
|
+
await new Promise((r) => setTimeout(r, 25));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Always SIGKILL the group — a reparented grandchild outliving its leader
|
|
189
|
+
// is the exact leak this guards against.
|
|
190
|
+
killProcessGroup(pid, 'SIGKILL');
|
|
191
|
+
}
|
|
192
|
+
this.exited = true;
|
|
193
|
+
this.child = null;
|
|
194
|
+
}
|
|
195
|
+
onLine(line) {
|
|
196
|
+
switch (line.kind) {
|
|
197
|
+
case 'init':
|
|
198
|
+
case 'assistant':
|
|
199
|
+
this.pending?.aggregator.accept(line);
|
|
200
|
+
break;
|
|
201
|
+
case 'result': {
|
|
202
|
+
if (this.pending === null)
|
|
203
|
+
break;
|
|
204
|
+
this.pending.aggregator.accept(line);
|
|
205
|
+
const outcome = this.pending.aggregator.outcome();
|
|
206
|
+
const pending = this.settlePending();
|
|
207
|
+
if (pending === null)
|
|
208
|
+
break;
|
|
209
|
+
if (outcome !== null)
|
|
210
|
+
pending.resolve(outcome);
|
|
211
|
+
else
|
|
212
|
+
pending.reject(new Error('claude turn ended without a result'));
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case 'control_request':
|
|
216
|
+
this.onControlRequest(line.requestId, line.subtype, line.request);
|
|
217
|
+
break;
|
|
218
|
+
case 'parse_error':
|
|
219
|
+
this.spec.log?.('warn', `claude stream-json parse error: ${line.raw}`);
|
|
220
|
+
break;
|
|
221
|
+
default:
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
onControlRequest(requestId, subtype, request) {
|
|
226
|
+
const stdin = this.child?.stdin;
|
|
227
|
+
if (requestId === null || stdin == null || !stdin.writable)
|
|
228
|
+
return;
|
|
229
|
+
// Unattended posture: answer permission callbacks so a turn never wedges
|
|
230
|
+
// waiting on a human. Under a bypassing permission mode the CLI does not
|
|
231
|
+
// ask, but answering defensively is harmless and crash-proof.
|
|
232
|
+
let reply;
|
|
233
|
+
if (subtype === 'can_use_tool') {
|
|
234
|
+
const rawInput = request['input'];
|
|
235
|
+
const input = typeof rawInput === 'object' && rawInput !== null && !Array.isArray(rawInput)
|
|
236
|
+
? rawInput
|
|
237
|
+
: {};
|
|
238
|
+
reply = buildCanUseToolAllow(requestId, input);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
reply = buildControlAck(requestId);
|
|
242
|
+
}
|
|
243
|
+
stdin.write(`${reply}\n`);
|
|
244
|
+
}
|
|
245
|
+
onChildExit() {
|
|
246
|
+
if (this.exited)
|
|
247
|
+
return;
|
|
248
|
+
this.exited = true;
|
|
249
|
+
this.failPending(new Error('claude resident child exited mid-turn'));
|
|
250
|
+
this.onExitHandler?.();
|
|
251
|
+
}
|
|
252
|
+
failPending(err) {
|
|
253
|
+
this.settlePending()?.reject(err);
|
|
254
|
+
}
|
|
255
|
+
onExitHandler = null;
|
|
256
|
+
setOnExit(handler) {
|
|
257
|
+
this.onExitHandler = handler;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* The default factory: spawns the real `claude` binary. The returned session
|
|
262
|
+
* exposes a `setOnExit` registration the runtime uses to react to an unexpected
|
|
263
|
+
* child death (degrade + re-spawn next turn).
|
|
264
|
+
*/
|
|
265
|
+
export function createDefaultClaudeCodeSession(spec) {
|
|
266
|
+
return new LiveClaudeCodeSession(spec);
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=claude-code-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-session.js","sourceRoot":"","sources":["../../src/agent-runtime/claude-code-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `builtin:claude-code` AgentRuntimeProvider (issue #110 PR6, resident
|
|
3
|
+
* stream-json transport since issue #120).
|
|
4
|
+
*
|
|
5
|
+
* A real second agent runtime that proves the AgentRuntimeProvider abstraction
|
|
6
|
+
* is not "Codex renamed". Like Codex it now runs a **resident child process**
|
|
7
|
+
* supervised for the dispatcher's lifetime — but it differs in every
|
|
8
|
+
* runtime-specific dimension:
|
|
9
|
+
*
|
|
10
|
+
* - **Stream-json over stdio, not an app-server WebSocket.** The resident child
|
|
11
|
+
* is `claude --print --input-format stream-json --output-format stream-json`
|
|
12
|
+
* (see `runtime/claude-code-args.ts`); turns are NDJSON `user` lines on stdin
|
|
13
|
+
* and `init`/`assistant`/`result` envelopes on stdout (see
|
|
14
|
+
* `runtime/claude-code-stream.ts`). There is no `initialize` handshake — the
|
|
15
|
+
* child emits `init` lazily with the first turn — so readiness is "child
|
|
16
|
+
* spawned", not "handshake completed".
|
|
17
|
+
* - **MCP injection is a JSON config document** (`--mcp-config <file>`), not
|
|
18
|
+
* Codex's `-c mcp_servers.*` TOML CLI flags.
|
|
19
|
+
* - **Runtime-owned config** is `DispatcherClaudeCodeConfig` (bin / model /
|
|
20
|
+
* permission_mode / extra_args / extra_env), distinct from the Codex config.
|
|
21
|
+
* - **Completion delivery** is the Claude Code task-notification path, not the
|
|
22
|
+
* Codex inbox-then-trigger path.
|
|
23
|
+
*
|
|
24
|
+
* Process spawning goes through an injectable {@link ClaudeCodeSessionFactory}
|
|
25
|
+
* seam (mirroring Codex's process-factory seam), so the lifecycle contract is
|
|
26
|
+
* fully unit-testable with a fake session. A live `claude` binary is exercised
|
|
27
|
+
* only by the opt-in live test.
|
|
28
|
+
*
|
|
29
|
+
* Failure contract (unchanged by #120): a turn failure (spawn error, child
|
|
30
|
+
* exit, error `result`) is never swallowed. For inbound/restart turns it drives
|
|
31
|
+
* the runtime to `degraded` with a persisted `last_error` (observable via
|
|
32
|
+
* status/doctor). For `deliverTeamMateCompletion` it surfaces as a `failed`
|
|
33
|
+
* result the caller can act on (PR8 delivery retry). `enqueueInbound` still
|
|
34
|
+
* returns after accept (submit != completion) so the channel can ack promptly.
|
|
35
|
+
*
|
|
36
|
+
* Restart: an unexpected child exit marks the runtime `degraded`; the next turn
|
|
37
|
+
* re-spawns the resident child with `--resume <session_id>`, restoring the
|
|
38
|
+
* conversation. There is no background backoff timer — re-spawn is lazy and
|
|
39
|
+
* bound to the (serialized) turn queue, so it stays deterministic.
|
|
40
|
+
*
|
|
41
|
+
* Per-turn deadline: a turn whose still-alive child never emits a terminal
|
|
42
|
+
* `result` (a stall, or a wait on input the runtime cannot satisfy) would
|
|
43
|
+
* otherwise pend forever and wedge the serial queue — and, behind it, TeamMate
|
|
44
|
+
* completion delivery, which awaits this runtime. `turn_timeout_ms` bounds every
|
|
45
|
+
* turn at the session layer: on the deadline the turn fails and the child is
|
|
46
|
+
* reaped (so the next turn re-spawns), turning an infinite hang into a normal
|
|
47
|
+
* degraded + `last_error` (inbound) or `failed` delivery result.
|
|
48
|
+
*
|
|
49
|
+
* Reference: the resident stream-json protocol model and process-supervision
|
|
50
|
+
* shape are adapted from the Claudemux `next` implementation; the AgentRuntime /
|
|
51
|
+
* Channel / DispatcherService boundaries (provider seam, runtime-owned MCP
|
|
52
|
+
* injection, degraded/last_error status, TeamMate delivery result contract) are
|
|
53
|
+
* Dreamux's own, per `.agents/decisions/agent-runtime-provider.md`.
|
|
54
|
+
*/
|
|
55
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
56
|
+
import { dirname } from 'node:path';
|
|
57
|
+
import { createBuiltinRegistry } from '../registry/index.js';
|
|
58
|
+
import { BUILTIN_CLAUDE_CODE_PROVIDER_REF, defaultDispatcherClaudeCodeConfig, dispatcherClaudeCodeConfig, } from '../runtime/config.js';
|
|
59
|
+
import { dispatcherClaudeCodeMcpConfigPath, dispatcherClaudeCodeStreamLogPath, dispatcherCodexCwd, } from '../runtime/paths.js';
|
|
60
|
+
import { dispatcherProcessEnv } from '../runtime/package-bin.js';
|
|
61
|
+
import { claudeCodeResidentArgs, stringifyClaudeCodeMcpConfig, } from '../runtime/claude-code-args.js';
|
|
62
|
+
import { createDefaultClaudeCodeSession, } from './claude-code-session.js';
|
|
63
|
+
/** Format a TeamMate completion as a Claude Code task-notification turn. */
|
|
64
|
+
function formatTaskNotification(completion) {
|
|
65
|
+
return [
|
|
66
|
+
`<teammate_task_completion task_id="${completion.taskId}" ` +
|
|
67
|
+
`teammate_id="${completion.teammateId}" status="${completion.status}">`,
|
|
68
|
+
completion.finalText,
|
|
69
|
+
'</teammate_task_completion>',
|
|
70
|
+
].join('\n');
|
|
71
|
+
}
|
|
72
|
+
function errMessage(err) {
|
|
73
|
+
return err instanceof Error ? err.message : String(err);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The Claude Code agent runtime for one dispatcher. A single resident
|
|
77
|
+
* stream-json child serves every turn. Turns run serially (one at a time) and
|
|
78
|
+
* `enqueueInbound` returns after the message is accepted — not after the turn
|
|
79
|
+
* completes — matching the Codex runtime's submit-then-serialize contract.
|
|
80
|
+
*/
|
|
81
|
+
export class ClaudeCodeRuntime {
|
|
82
|
+
context;
|
|
83
|
+
deps;
|
|
84
|
+
providerRef = BUILTIN_CLAUDE_CODE_PROVIDER_REF;
|
|
85
|
+
dispatcherId;
|
|
86
|
+
config;
|
|
87
|
+
bin;
|
|
88
|
+
cwd;
|
|
89
|
+
mcpConfigPath;
|
|
90
|
+
mcpConfigDoc;
|
|
91
|
+
stderrLogPath;
|
|
92
|
+
status = 'declared';
|
|
93
|
+
threadId;
|
|
94
|
+
resumed;
|
|
95
|
+
stopped = false;
|
|
96
|
+
seen = new Set();
|
|
97
|
+
queue = Promise.resolve();
|
|
98
|
+
turnCounter = 0;
|
|
99
|
+
session = null;
|
|
100
|
+
constructor(context, deps) {
|
|
101
|
+
this.context = context;
|
|
102
|
+
this.deps = deps;
|
|
103
|
+
this.dispatcherId = context.row.dispatcher_id;
|
|
104
|
+
this.config =
|
|
105
|
+
context.dispatcher === null
|
|
106
|
+
? defaultDispatcherClaudeCodeConfig()
|
|
107
|
+
: dispatcherClaudeCodeConfig(context.dispatcher);
|
|
108
|
+
this.bin = deps.resolveBinPath(this.config.bin);
|
|
109
|
+
this.cwd = context.row.codex_cwd ?? dispatcherCodexCwd(this.dispatcherId);
|
|
110
|
+
this.mcpConfigPath = dispatcherClaudeCodeMcpConfigPath(this.dispatcherId);
|
|
111
|
+
this.mcpConfigDoc = stringifyClaudeCodeMcpConfig(context.mcpServers);
|
|
112
|
+
this.stderrLogPath = dispatcherClaudeCodeStreamLogPath(this.dispatcherId);
|
|
113
|
+
this.threadId = context.row.thread_id;
|
|
114
|
+
this.resumed = context.row.thread_id !== null;
|
|
115
|
+
}
|
|
116
|
+
getStatus() {
|
|
117
|
+
return this.status;
|
|
118
|
+
}
|
|
119
|
+
getThreadId() {
|
|
120
|
+
return this.threadId;
|
|
121
|
+
}
|
|
122
|
+
wasThreadResumed() {
|
|
123
|
+
return this.resumed;
|
|
124
|
+
}
|
|
125
|
+
async start() {
|
|
126
|
+
await this.setStatus('starting');
|
|
127
|
+
try {
|
|
128
|
+
await mkdir(dirname(this.mcpConfigPath), { recursive: true });
|
|
129
|
+
await writeFile(this.mcpConfigPath, this.mcpConfigDoc, { mode: 0o600 });
|
|
130
|
+
// Spawn the resident child up front so the runtime is truly resident
|
|
131
|
+
// (Codex-aligned). A missing/broken `claude` binary fails here and drives
|
|
132
|
+
// the runtime to degraded + throws, rather than a silent no-op.
|
|
133
|
+
await this.ensureSession();
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
await this.setStatus('degraded', err);
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
await this.setStatus('ready');
|
|
140
|
+
}
|
|
141
|
+
async stop() {
|
|
142
|
+
if (this.stopped)
|
|
143
|
+
return;
|
|
144
|
+
this.stopped = true;
|
|
145
|
+
await this.setStatus('stopping');
|
|
146
|
+
const session = this.session;
|
|
147
|
+
this.session = null;
|
|
148
|
+
if (session !== null) {
|
|
149
|
+
try {
|
|
150
|
+
await session.stop();
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
this.log('warn', 'claude-code session stop errored', err);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
await this.setStatus('stopped');
|
|
157
|
+
}
|
|
158
|
+
async enqueueInbound(input, hooks = {}) {
|
|
159
|
+
if (this.stopped)
|
|
160
|
+
return { status: 'stopped' };
|
|
161
|
+
const key = input.source_message_id ?? '';
|
|
162
|
+
if (key !== '' && this.seen.has(key))
|
|
163
|
+
return { status: 'duplicate' };
|
|
164
|
+
if (key !== '')
|
|
165
|
+
this.seen.add(key);
|
|
166
|
+
try {
|
|
167
|
+
await hooks.onAccepted?.(input);
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
// onAccepted is a best-effort side effect (e.g. a channel reaction); a
|
|
171
|
+
// failure there must not drop the turn.
|
|
172
|
+
this.log('warn', 'claude-code onAccepted hook failed', err);
|
|
173
|
+
}
|
|
174
|
+
const turnId = `claude-turn-${++this.turnCounter}`;
|
|
175
|
+
// Submit-then-serialize: return after accept (so the channel can ack
|
|
176
|
+
// promptly), run the turn on the serial queue. A turn failure cannot be
|
|
177
|
+
// returned to this caller without blocking the channel ack on full turn
|
|
178
|
+
// completion. Instead, a failed turn drives the runtime to `degraded` with a
|
|
179
|
+
// persisted `last_error` (visible via status/doctor) — never swallowed.
|
|
180
|
+
void this.runTurnOnQueue(input.parsed_text, turnId).then(() => this.markTurnSucceeded(), (err) => this.markTurnFailed(turnId, err));
|
|
181
|
+
return { status: 'submitted', turnId };
|
|
182
|
+
}
|
|
183
|
+
async injectRestartNotice(text) {
|
|
184
|
+
if (this.stopped)
|
|
185
|
+
return;
|
|
186
|
+
const turnId = `claude-restart-${++this.turnCounter}`;
|
|
187
|
+
void this.runTurnOnQueue(text, turnId).then(() => this.markTurnSucceeded(), (err) => this.markTurnFailed(turnId, err));
|
|
188
|
+
}
|
|
189
|
+
async deliverTeamMateCompletion(completion) {
|
|
190
|
+
if (this.stopped) {
|
|
191
|
+
return { status: 'unsupported', reason: 'runtime stopped' };
|
|
192
|
+
}
|
|
193
|
+
// Task-notification delivery entry: notify the Claude Code session with a
|
|
194
|
+
// task-completion turn. Delivery AWAITS the turn so the result is real —
|
|
195
|
+
// `accepted` only after the notification turn actually ran, `failed`
|
|
196
|
+
// otherwise — which is the semantics the PR8 Dispatcher Service relies on
|
|
197
|
+
// for delivery retry. The executable retry/pull loop itself stays in PR8.
|
|
198
|
+
try {
|
|
199
|
+
await this.runTurnOnQueue(formatTaskNotification(completion), `claude-teammate-${completion.taskId}`);
|
|
200
|
+
return { status: 'accepted' };
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
return {
|
|
204
|
+
status: 'failed',
|
|
205
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Chain a turn onto the serial queue. Returns a promise that resolves when
|
|
211
|
+
* this turn completes and rejects when it fails, so awaiting callers (delivery)
|
|
212
|
+
* see the real outcome. The queue itself continues regardless of outcome so a
|
|
213
|
+
* failed turn does not wedge later turns.
|
|
214
|
+
*/
|
|
215
|
+
runTurnOnQueue(prompt, turnId) {
|
|
216
|
+
const run = this.queue.then(() => this.runTurn(prompt, turnId));
|
|
217
|
+
this.queue = run.then(() => undefined, () => undefined);
|
|
218
|
+
return run;
|
|
219
|
+
}
|
|
220
|
+
async markTurnSucceeded() {
|
|
221
|
+
if (this.stopped)
|
|
222
|
+
return;
|
|
223
|
+
if (this.status !== 'ready')
|
|
224
|
+
await this.setStatus('ready');
|
|
225
|
+
}
|
|
226
|
+
async markTurnFailed(turnId, err) {
|
|
227
|
+
this.log('error', `claude-code turn ${turnId} failed`, err);
|
|
228
|
+
if (this.stopped)
|
|
229
|
+
return;
|
|
230
|
+
// Surface the failure as durable runtime state rather than swallowing it.
|
|
231
|
+
await this.setStatus('degraded', err);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Ensure a live resident session exists, spawning (or re-spawning after an
|
|
235
|
+
* unexpected exit) as needed. Re-spawn resumes the persisted session id so the
|
|
236
|
+
* conversation survives a crash.
|
|
237
|
+
*/
|
|
238
|
+
async ensureSession() {
|
|
239
|
+
if (this.session !== null && this.session.isAlive())
|
|
240
|
+
return this.session;
|
|
241
|
+
const args = claudeCodeResidentArgs({
|
|
242
|
+
config: this.config,
|
|
243
|
+
mcpConfigPath: this.mcpConfigPath,
|
|
244
|
+
resumeSessionId: this.threadId,
|
|
245
|
+
});
|
|
246
|
+
const session = this.deps.sessionFactory({
|
|
247
|
+
bin: this.bin,
|
|
248
|
+
args,
|
|
249
|
+
cwd: this.cwd,
|
|
250
|
+
env: dispatcherProcessEnv(globalThis.process.env, this.config.extra_env),
|
|
251
|
+
stderrLogPath: this.stderrLogPath,
|
|
252
|
+
turnTimeoutMs: this.config.turn_timeout_ms,
|
|
253
|
+
log: (level, msg, err) => this.log(level, msg, err),
|
|
254
|
+
});
|
|
255
|
+
session.setOnExit(() => {
|
|
256
|
+
void this.onSessionExit(session);
|
|
257
|
+
});
|
|
258
|
+
await session.start();
|
|
259
|
+
this.session = session;
|
|
260
|
+
return session;
|
|
261
|
+
}
|
|
262
|
+
/** React to an unexpected resident-child exit: degrade and drop the session. */
|
|
263
|
+
async onSessionExit(session) {
|
|
264
|
+
if (this.session !== session)
|
|
265
|
+
return; // already replaced/stopped
|
|
266
|
+
this.session = null;
|
|
267
|
+
if (this.stopped)
|
|
268
|
+
return;
|
|
269
|
+
this.log('error', 'claude-code resident child exited unexpectedly');
|
|
270
|
+
await this.setStatus('degraded', new Error('claude resident child exited'));
|
|
271
|
+
}
|
|
272
|
+
async runTurn(prompt, turnId) {
|
|
273
|
+
const session = await this.ensureSession();
|
|
274
|
+
const outcome = await session.submitTurn(prompt);
|
|
275
|
+
if (outcome.sessionId !== null &&
|
|
276
|
+
outcome.sessionId !== '' &&
|
|
277
|
+
outcome.sessionId !== this.threadId) {
|
|
278
|
+
this.threadId = outcome.sessionId;
|
|
279
|
+
await this.context.dispatchers.setThreadId(this.dispatcherId, outcome.sessionId);
|
|
280
|
+
}
|
|
281
|
+
if (outcome.isError) {
|
|
282
|
+
const detail = outcome.errors.length > 0
|
|
283
|
+
? outcome.errors.join('; ')
|
|
284
|
+
: (outcome.subtype ?? 'unknown error');
|
|
285
|
+
throw new Error(`claude turn ${turnId} returned an error result: ${detail}`);
|
|
286
|
+
}
|
|
287
|
+
this.log('info', `claude-code turn ${turnId} completed`);
|
|
288
|
+
}
|
|
289
|
+
async setStatus(status, err) {
|
|
290
|
+
this.status = status;
|
|
291
|
+
await this.context.dispatchers.setStatus(this.dispatcherId, status, err !== undefined ? { last_error: errMessage(err) } : {});
|
|
292
|
+
}
|
|
293
|
+
log(level, msg, err) {
|
|
294
|
+
this.context.log(level, msg, err);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/** Build the Phase 1 `builtin:claude-code` agent runtime provider. */
|
|
298
|
+
export function createClaudeCodeAgentRuntimeProvider(options = {}) {
|
|
299
|
+
const descriptor = createBuiltinRegistry().resolve(BUILTIN_CLAUDE_CODE_PROVIDER_REF);
|
|
300
|
+
const sessionFactory = options.sessionFactory ?? createDefaultClaudeCodeSession;
|
|
301
|
+
const resolveBinPath = options.resolveBinPath ?? ((bin) => bin);
|
|
302
|
+
return {
|
|
303
|
+
ref: BUILTIN_CLAUDE_CODE_PROVIDER_REF,
|
|
304
|
+
descriptor,
|
|
305
|
+
delivery: {
|
|
306
|
+
teammateCompletion: [
|
|
307
|
+
{
|
|
308
|
+
kind: 'claudeCodeTaskNotification',
|
|
309
|
+
description: 'notify the runtime through a Claude Code task notification path',
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
},
|
|
313
|
+
createRuntime(context) {
|
|
314
|
+
return new ClaudeCodeRuntime(context, { sessionFactory, resolveBinPath });
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/agent-runtime/claude-code.ts"],"names":[],"mappings":"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"}
|