@excitedjs/agent-runtime-claude-code 0.2.0-alpha.g0ddd418597ca
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/LICENSE +21 -0
- package/README.md +32 -0
- package/dist/args.d.ts +71 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +87 -0
- package/dist/args.js.map +1 -0
- package/dist/config.d.ts +71 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/diagnostic.d.ts +12 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +25 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/completion-body.d.ts +38 -0
- package/dist/internal/completion-body.d.ts.map +1 -0
- package/dist/internal/completion-body.js +62 -0
- package/dist/internal/completion-body.js.map +1 -0
- package/dist/internal/config-validate.d.ts +23 -0
- package/dist/internal/config-validate.d.ts.map +1 -0
- package/dist/internal/config-validate.js +122 -0
- package/dist/internal/config-validate.js.map +1 -0
- package/dist/internal/os.d.ts +30 -0
- package/dist/internal/os.d.ts.map +1 -0
- package/dist/internal/os.js +81 -0
- package/dist/internal/os.js.map +1 -0
- package/dist/internal/turn-render.d.ts +22 -0
- package/dist/internal/turn-render.d.ts.map +1 -0
- package/dist/internal/turn-render.js +40 -0
- package/dist/internal/turn-render.js.map +1 -0
- package/dist/mcp-config.d.ts +19 -0
- package/dist/mcp-config.d.ts.map +1 -0
- package/dist/mcp-config.js +22 -0
- package/dist/mcp-config.js.map +1 -0
- package/dist/provider-ref.d.ts +8 -0
- package/dist/provider-ref.d.ts.map +1 -0
- package/dist/provider-ref.js +8 -0
- package/dist/provider-ref.js.map +1 -0
- package/dist/provider.d.ts +60 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +110 -0
- package/dist/provider.js.map +1 -0
- package/dist/rpc.d.ts +45 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +212 -0
- package/dist/rpc.js.map +1 -0
- package/dist/runtime.d.ts +174 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +464 -0
- package/dist/runtime.js.map +1 -0
- package/dist/stream.d.ts +96 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +289 -0
- package/dist/stream.js.map +1 -0
- package/dist/supervisor.d.ts +17 -0
- package/dist/supervisor.d.ts.map +1 -0
- package/dist/supervisor.js +170 -0
- package/dist/supervisor.js.map +1 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `builtin:claude-code` AgentRuntime (issue #110 PR6, resident stream-json
|
|
3
|
+
* transport since issue #120; extracted into `@excitedjs/agent-runtime-claude-code`
|
|
4
|
+
* in issue #209).
|
|
5
|
+
*
|
|
6
|
+
* A real second agent runtime that proves the AgentRuntimeProvider abstraction
|
|
7
|
+
* is not "Codex renamed". Like Codex it runs a **resident child process**
|
|
8
|
+
* supervised for the runtime's lifetime — but it differs in every
|
|
9
|
+
* runtime-specific dimension:
|
|
10
|
+
*
|
|
11
|
+
* - **Stream-json over stdio, not an app-server WebSocket.** The resident child
|
|
12
|
+
* is `claude --print --input-format stream-json --output-format stream-json`
|
|
13
|
+
* (see `./args.ts`); turns are NDJSON `user` lines on stdin and
|
|
14
|
+
* `init`/`assistant`/`result` envelopes on stdout (see `./stream.ts`). There
|
|
15
|
+
* is no `initialize` handshake — the child emits `init` lazily with the first
|
|
16
|
+
* turn — so readiness is "child 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 / remote_control / extra_args / extra_env), distinct from
|
|
21
|
+
* the Codex config.
|
|
22
|
+
* - **Completion delivery** is a plain user turn (no fake task-notification),
|
|
23
|
+
* not the Codex inbox-then-trigger path.
|
|
24
|
+
*
|
|
25
|
+
* Process spawning goes through an injectable {@link ClaudeCodeSessionFactory}
|
|
26
|
+
* seam (mirroring Codex's process-factory seam), so the lifecycle contract is
|
|
27
|
+
* fully unit-testable with a fake session. A live `claude` binary is exercised
|
|
28
|
+
* only by the opt-in live test.
|
|
29
|
+
*
|
|
30
|
+
* Failure contract (unchanged by #120): a turn failure (spawn error, child
|
|
31
|
+
* exit, error `result`) is never swallowed. For inbound/restart turns it drives
|
|
32
|
+
* the runtime to `degraded` with a persisted `last_error` (observable via
|
|
33
|
+
* status/doctor). For `completionInput` it surfaces as a `failed`
|
|
34
|
+
* result the caller can act on (PR8 delivery retry). `channelInput` still
|
|
35
|
+
* returns after accept (submit != completion) so the channel can ack promptly.
|
|
36
|
+
*
|
|
37
|
+
* Restart: an unexpected child exit marks the runtime `degraded`; the next turn
|
|
38
|
+
* re-spawns the resident child with `--resume <session_id>`, restoring the
|
|
39
|
+
* conversation. There is no background backoff timer — re-spawn is lazy and
|
|
40
|
+
* bound to the (serialized) turn queue, so it stays deterministic.
|
|
41
|
+
*
|
|
42
|
+
* Per-turn idle deadline: a turn whose still-alive child goes silent — never
|
|
43
|
+
* emitting another stream line (a stall, or a wait on input the runtime cannot
|
|
44
|
+
* satisfy) — would otherwise pend forever and wedge the serial queue, and behind
|
|
45
|
+
* it TeamMate completion delivery, which awaits this runtime. `turn_timeout_ms`
|
|
46
|
+
* is a *max-idle* window (issue #156): it is reset on every inbound stream line,
|
|
47
|
+
* so a long but continuously-streaming turn (e.g. a deep audit running many
|
|
48
|
+
* tool calls for far longer than the window) is never reaped, while a child that
|
|
49
|
+
* emits nothing for the whole window still is — turning an infinite hang into a
|
|
50
|
+
* normal degraded + `last_error` (inbound) or `failed` delivery result.
|
|
51
|
+
*
|
|
52
|
+
* Reference: the resident stream-json protocol model and process-supervision
|
|
53
|
+
* shape are adapted from the Claudemux `next` implementation; the AgentRuntime /
|
|
54
|
+
* Channel / DispatcherService boundaries (provider seam, runtime-owned MCP
|
|
55
|
+
* injection, degraded/last_error status, TeamMate delivery result contract) are
|
|
56
|
+
* Dreamux's own, per `.agents/decisions/agent-runtime-provider.md`.
|
|
57
|
+
*/
|
|
58
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
59
|
+
import { dirname, join } from 'node:path';
|
|
60
|
+
import { BUILTIN_CLAUDE_CODE_PROVIDER_REF } from './provider-ref.js';
|
|
61
|
+
import { claudeCodeResidentArgs } from './args.js';
|
|
62
|
+
import { stringifyClaudeCodeMcpConfig } from './mcp-config.js';
|
|
63
|
+
import { renderChannelInput, resolveCompletionBody } from '@excitedjs/dreamux-utils';
|
|
64
|
+
import { CLAUDE_CODE_AGENT_RUNTIME_CAPABILITIES } from './provider.js';
|
|
65
|
+
let nextRuntimeInstanceId = 0;
|
|
66
|
+
/**
|
|
67
|
+
* Status line opening a TeamMate completion turn. Plain English, status-varied —
|
|
68
|
+
* NOT claude-code's native `<task-notification>` XML. The old XML mimicked
|
|
69
|
+
* claude-code's real task-notification system, so the model could mistake the
|
|
70
|
+
* fabricated task-id / output-file for a live background task and act on them
|
|
71
|
+
* (hallucination / harness collision). A plain user turn avoids that entirely.
|
|
72
|
+
*/
|
|
73
|
+
function completionStatusLine(completion) {
|
|
74
|
+
switch (completion.status) {
|
|
75
|
+
case 'completed':
|
|
76
|
+
return `TeamMate ${completion.source} has finished its task.`;
|
|
77
|
+
case 'failed':
|
|
78
|
+
return `TeamMate ${completion.source}'s task failed.`;
|
|
79
|
+
case 'stopped':
|
|
80
|
+
return `TeamMate ${completion.source}'s task was stopped.`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Build the plain-text completion turn. The result is inlined when short; when
|
|
85
|
+
* it overflows the inline budget the full result is spilled to a file (see
|
|
86
|
+
* {@link resolveCompletionBody}) and only the path is inlined.
|
|
87
|
+
*/
|
|
88
|
+
async function buildCompletionTurnText(completion, spillDir) {
|
|
89
|
+
const line = completionStatusLine(completion);
|
|
90
|
+
const body = await resolveCompletionBody(completion, spillDir);
|
|
91
|
+
return body.kind === 'inline'
|
|
92
|
+
? `${line} Output below:\n\n${body.text}`
|
|
93
|
+
: `${line} The output is too long, so the full result was saved to a file:\n\n${body.path}`;
|
|
94
|
+
}
|
|
95
|
+
function errMessage(err) {
|
|
96
|
+
return err instanceof Error ? err.message : String(err);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The Claude Code agent runtime for one dispatcher. A single resident
|
|
100
|
+
* stream-json child serves every turn. Turns run serially (one at a time) and
|
|
101
|
+
* `channelInput` returns after the message is accepted — not after the turn
|
|
102
|
+
* completes — matching the Codex runtime's submit-then-serialize contract.
|
|
103
|
+
*/
|
|
104
|
+
export class ClaudeCodeRuntime {
|
|
105
|
+
deps;
|
|
106
|
+
providerRef = BUILTIN_CLAUDE_CODE_PROVIDER_REF;
|
|
107
|
+
dispatcherId;
|
|
108
|
+
config;
|
|
109
|
+
bin;
|
|
110
|
+
cwd;
|
|
111
|
+
mcpConfigPath;
|
|
112
|
+
mcpConfigDoc;
|
|
113
|
+
stderrLogPath;
|
|
114
|
+
completionSpillDir;
|
|
115
|
+
logger;
|
|
116
|
+
status = 'declared';
|
|
117
|
+
threadId;
|
|
118
|
+
resumed;
|
|
119
|
+
stopped = false;
|
|
120
|
+
seen = new Set();
|
|
121
|
+
queue = Promise.resolve();
|
|
122
|
+
runtimeInstanceId = ++nextRuntimeInstanceId;
|
|
123
|
+
turnCounter = 0;
|
|
124
|
+
session = null;
|
|
125
|
+
lastResult = null;
|
|
126
|
+
activeChannelTurn = null;
|
|
127
|
+
constructor(identity, deps) {
|
|
128
|
+
this.deps = deps;
|
|
129
|
+
this.dispatcherId = identity.runtime_id;
|
|
130
|
+
this.config = deps.config;
|
|
131
|
+
this.bin = deps.resolveBinPath(this.config.bin);
|
|
132
|
+
this.cwd = deps.cwd;
|
|
133
|
+
this.mcpConfigPath = join(deps.paths.dispatcherDir(this.dispatcherId), 'mcp.json');
|
|
134
|
+
this.mcpConfigDoc = stringifyClaudeCodeMcpConfig(deps.mcpServers);
|
|
135
|
+
// Compose the resident stream-json child's stderr log under the neutral
|
|
136
|
+
// central logs root (B2): core no longer names a per-runtime log file. The
|
|
137
|
+
// host supplies a unique, filesystem-safe `runtime_id`.
|
|
138
|
+
this.stderrLogPath = join(deps.paths.logsDir(), 'claude-code', `${this.dispatcherId}.stderr.log`);
|
|
139
|
+
this.completionSpillDir = deps.paths.completionSpillDir(this.dispatcherId);
|
|
140
|
+
this.threadId = identity.checkpoint_id ?? null;
|
|
141
|
+
this.resumed = (identity.checkpoint_id ?? null) !== null;
|
|
142
|
+
this.logger = deps.logger ?? consoleFallbackLogger(this.dispatcherId);
|
|
143
|
+
}
|
|
144
|
+
getStatus() {
|
|
145
|
+
return this.status;
|
|
146
|
+
}
|
|
147
|
+
getCapabilities() {
|
|
148
|
+
return CLAUDE_CODE_AGENT_RUNTIME_CAPABILITIES;
|
|
149
|
+
}
|
|
150
|
+
getThreadId() {
|
|
151
|
+
return this.threadId;
|
|
152
|
+
}
|
|
153
|
+
wasThreadResumed() {
|
|
154
|
+
return this.resumed;
|
|
155
|
+
}
|
|
156
|
+
async getLast() {
|
|
157
|
+
return this.lastResult;
|
|
158
|
+
}
|
|
159
|
+
async getContext() {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
async resume(input = {}) {
|
|
163
|
+
if (input.checkpoint !== undefined && input.checkpoint !== null) {
|
|
164
|
+
if (input.checkpoint.kind !== 'claudeCodeSession') {
|
|
165
|
+
throw new Error(`unsupported resume checkpoint for Claude Code runtime: ${input.checkpoint.kind}`);
|
|
166
|
+
}
|
|
167
|
+
this.threadId = input.checkpoint.id;
|
|
168
|
+
this.resumed = true;
|
|
169
|
+
}
|
|
170
|
+
await this.start();
|
|
171
|
+
}
|
|
172
|
+
async start() {
|
|
173
|
+
await this.setStatus('starting');
|
|
174
|
+
try {
|
|
175
|
+
await mkdir(dirname(this.mcpConfigPath), { recursive: true });
|
|
176
|
+
await writeFile(this.mcpConfigPath, this.mcpConfigDoc, { mode: 0o600 });
|
|
177
|
+
// Spawn the resident child up front so the runtime is truly resident
|
|
178
|
+
// (Codex-aligned). A missing/broken `claude` binary fails here and drives
|
|
179
|
+
// the runtime to degraded + throws, rather than a silent no-op.
|
|
180
|
+
await this.ensureSession();
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
await this.setStatus('degraded', err);
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
await this.setStatus('ready');
|
|
187
|
+
}
|
|
188
|
+
async stop() {
|
|
189
|
+
if (this.stopped)
|
|
190
|
+
return;
|
|
191
|
+
this.stopped = true;
|
|
192
|
+
await this.setStatus('stopping');
|
|
193
|
+
const session = this.session;
|
|
194
|
+
this.session = null;
|
|
195
|
+
if (session !== null) {
|
|
196
|
+
try {
|
|
197
|
+
await session.stop();
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
this.log('warn', 'claude-code session stop errored', err);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
await this.setStatus('stopped');
|
|
204
|
+
}
|
|
205
|
+
async systemInput(notice) {
|
|
206
|
+
if (this.stopped)
|
|
207
|
+
return { status: 'stopped' };
|
|
208
|
+
const turnId = this.nextTurnId('system');
|
|
209
|
+
void this.runTurnOnQueue(notice.text, turnId).then(() => this.markTurnSucceeded(turnId), (err) => this.markTurnFailed(turnId, err));
|
|
210
|
+
return { status: 'submitted', turnId };
|
|
211
|
+
}
|
|
212
|
+
async channelInput(input, hooks = {}) {
|
|
213
|
+
if (this.stopped)
|
|
214
|
+
return { status: 'stopped' };
|
|
215
|
+
const key = input.sourceId;
|
|
216
|
+
if (key !== '' && this.seen.has(key))
|
|
217
|
+
return { status: 'duplicate' };
|
|
218
|
+
if (key !== '')
|
|
219
|
+
this.seen.add(key);
|
|
220
|
+
try {
|
|
221
|
+
await hooks.onAccepted?.(input);
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
// onAccepted is a best-effort side effect (e.g. a channel reaction); a
|
|
225
|
+
// failure there must not drop the turn.
|
|
226
|
+
this.log('warn', 'claude-code onAccepted hook failed', err);
|
|
227
|
+
}
|
|
228
|
+
// This runtime owns wrapping the channel input into its delivery shape: a
|
|
229
|
+
// structured channel turn becomes the native `<channel source="…">` block;
|
|
230
|
+
// a plain turn passes through unchanged.
|
|
231
|
+
const text = renderChannelInput(input);
|
|
232
|
+
const active = this.activeChannelTurn;
|
|
233
|
+
if (active !== null) {
|
|
234
|
+
try {
|
|
235
|
+
await this.steerChannelTurn(active, text);
|
|
236
|
+
return { status: 'submitted', turnId: active.turnId };
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
return {
|
|
240
|
+
status: 'failed',
|
|
241
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const turnId = this.nextTurnId('turn');
|
|
246
|
+
const channelTurn = {
|
|
247
|
+
turnId,
|
|
248
|
+
pendingSteers: [],
|
|
249
|
+
session: null,
|
|
250
|
+
steerQueue: Promise.resolve(),
|
|
251
|
+
};
|
|
252
|
+
this.activeChannelTurn = channelTurn;
|
|
253
|
+
// Submit-then-serialize: return after accept (so the channel can ack
|
|
254
|
+
// promptly), run the turn on the serial queue. A turn failure cannot be
|
|
255
|
+
// returned to this caller without blocking the channel ack on full turn
|
|
256
|
+
// completion. Instead, a failed turn drives the runtime to `degraded` with a
|
|
257
|
+
// persisted `last_error` (visible via status/doctor) — never swallowed.
|
|
258
|
+
void this.runChannelTurnOnQueue(text, channelTurn).then(() => this.markTurnSucceeded(turnId), (err) => this.markTurnFailed(turnId, err));
|
|
259
|
+
return { status: 'submitted', turnId };
|
|
260
|
+
}
|
|
261
|
+
async completionInput(completion) {
|
|
262
|
+
if (this.stopped) {
|
|
263
|
+
return { status: 'unsupported', reason: 'runtime stopped' };
|
|
264
|
+
}
|
|
265
|
+
// Plain user-turn delivery: a stream-json user message marked
|
|
266
|
+
// `isSynthetic: false`, so claude-code treats it as ordinary human input
|
|
267
|
+
// rather than routing it through its native task-notification harness path.
|
|
268
|
+
// Submit-then-serialize: return accepted at enqueue so delivery acceptance
|
|
269
|
+
// is decoupled from model thinking time.
|
|
270
|
+
let text;
|
|
271
|
+
try {
|
|
272
|
+
text = await buildCompletionTurnText(completion, this.completionSpillDir);
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
return {
|
|
276
|
+
status: 'failed',
|
|
277
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const turnId = `claude-teammate-${completion.id}`;
|
|
281
|
+
void this.runTurnOnQueue(text, turnId, { isSynthetic: false }).then(() => this.markTurnSucceeded(turnId), (err) => this.markTurnFailed(turnId, err));
|
|
282
|
+
return { status: 'accepted' };
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Chain a turn onto the serial queue. Returns a promise that resolves when
|
|
286
|
+
* this turn completes and rejects when it fails, so awaiting callers (delivery)
|
|
287
|
+
* see the real outcome. The queue itself continues regardless of outcome so a
|
|
288
|
+
* failed turn does not wedge later turns.
|
|
289
|
+
*/
|
|
290
|
+
runTurnOnQueue(prompt, turnId, options) {
|
|
291
|
+
const run = this.queue.then(() => this.runTurn(prompt, turnId, options));
|
|
292
|
+
this.queue = run.then(() => undefined, () => undefined);
|
|
293
|
+
return run;
|
|
294
|
+
}
|
|
295
|
+
runChannelTurnOnQueue(prompt, active) {
|
|
296
|
+
const run = this.queue.then(() => this.runChannelTurn(prompt, active));
|
|
297
|
+
this.queue = run.then(() => undefined, () => undefined);
|
|
298
|
+
return run;
|
|
299
|
+
}
|
|
300
|
+
async runChannelTurn(prompt, active) {
|
|
301
|
+
const session = await this.ensureSession();
|
|
302
|
+
const steers = active.pendingSteers.splice(0);
|
|
303
|
+
const fullPrompt = steers.length === 0 ? prompt : [prompt, ...steers].join('\n\n');
|
|
304
|
+
const outcome = session.submitTurn(fullPrompt);
|
|
305
|
+
active.session = session;
|
|
306
|
+
try {
|
|
307
|
+
await this.applyTurnOutcome(await outcome, active.turnId);
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
active.session = null;
|
|
311
|
+
if (this.activeChannelTurn === active)
|
|
312
|
+
this.activeChannelTurn = null;
|
|
313
|
+
active.pendingSteers = [];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async steerChannelTurn(active, prompt) {
|
|
317
|
+
const session = active.session;
|
|
318
|
+
if (session === null) {
|
|
319
|
+
active.pendingSteers.push(prompt);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const steer = active.steerQueue.then(() => session.steerTurn(prompt, { priority: 'next' }));
|
|
323
|
+
active.steerQueue = steer.then(() => undefined, () => undefined);
|
|
324
|
+
await steer;
|
|
325
|
+
}
|
|
326
|
+
async markTurnSucceeded(turnId) {
|
|
327
|
+
this.deps.onTurnSettled?.({ turnId, status: 'completed' });
|
|
328
|
+
if (this.stopped)
|
|
329
|
+
return;
|
|
330
|
+
if (this.status !== 'ready')
|
|
331
|
+
await this.setStatus('ready');
|
|
332
|
+
}
|
|
333
|
+
async markTurnFailed(turnId, err) {
|
|
334
|
+
this.log('error', `claude-code turn ${turnId} failed`, err);
|
|
335
|
+
// A turn that fails after stop() was requested (the resident child is being
|
|
336
|
+
// torn down) is a `stopped` settlement; otherwise it is a genuine `failed`.
|
|
337
|
+
// Fire before the stopped early-return so an interrupted teammate turn is
|
|
338
|
+
// never lost.
|
|
339
|
+
this.deps.onTurnSettled?.({
|
|
340
|
+
turnId,
|
|
341
|
+
status: this.stopped ? 'stopped' : 'failed',
|
|
342
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
343
|
+
});
|
|
344
|
+
if (this.stopped)
|
|
345
|
+
return;
|
|
346
|
+
// Surface the failure as durable runtime state rather than swallowing it.
|
|
347
|
+
await this.setStatus('degraded', err);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Ensure a live resident session exists, spawning (or re-spawning after an
|
|
351
|
+
* unexpected exit) as needed. Re-spawn resumes the persisted session id so the
|
|
352
|
+
* conversation survives a crash.
|
|
353
|
+
*/
|
|
354
|
+
async ensureSession() {
|
|
355
|
+
if (this.session !== null && this.session.isAlive())
|
|
356
|
+
return this.session;
|
|
357
|
+
const args = claudeCodeResidentArgs({
|
|
358
|
+
config: this.config,
|
|
359
|
+
mcpConfigPath: this.mcpConfigPath,
|
|
360
|
+
resumeSessionId: this.threadId,
|
|
361
|
+
systemPromptContent: this.deps.systemPromptContent,
|
|
362
|
+
skillSources: this.deps.skillSources,
|
|
363
|
+
});
|
|
364
|
+
const session = this.deps.sessionFactory({
|
|
365
|
+
bin: this.bin,
|
|
366
|
+
args,
|
|
367
|
+
cwd: this.cwd,
|
|
368
|
+
env: this.buildProcessEnv(this.config.extra_env),
|
|
369
|
+
stderrLogPath: this.stderrLogPath,
|
|
370
|
+
turnTimeoutMs: this.config.turn_timeout_ms,
|
|
371
|
+
remoteControl: this.config.remote_control,
|
|
372
|
+
onRemoteControlUrl: this.config.remote_control
|
|
373
|
+
? (url) => {
|
|
374
|
+
this.log('info', `claude-code remote control URL: ${url}`);
|
|
375
|
+
}
|
|
376
|
+
: undefined,
|
|
377
|
+
log: (level, msg, err) => this.log(level, msg, err),
|
|
378
|
+
});
|
|
379
|
+
session.setOnExit(() => {
|
|
380
|
+
void this.onSessionExit(session);
|
|
381
|
+
});
|
|
382
|
+
await session.start();
|
|
383
|
+
this.session = session;
|
|
384
|
+
return session;
|
|
385
|
+
}
|
|
386
|
+
/** React to an unexpected resident-child exit: degrade and drop the session. */
|
|
387
|
+
async onSessionExit(session) {
|
|
388
|
+
if (this.session !== session)
|
|
389
|
+
return; // already replaced/stopped
|
|
390
|
+
this.session = null;
|
|
391
|
+
if (this.stopped)
|
|
392
|
+
return;
|
|
393
|
+
this.log('error', 'claude-code resident child exited unexpectedly');
|
|
394
|
+
await this.setStatus('degraded', new Error('claude resident child exited'));
|
|
395
|
+
}
|
|
396
|
+
async runTurn(prompt, turnId, options) {
|
|
397
|
+
const session = await this.ensureSession();
|
|
398
|
+
await this.runTurnWithSession(session, prompt, turnId, options);
|
|
399
|
+
}
|
|
400
|
+
async runTurnWithSession(session, prompt, turnId, options) {
|
|
401
|
+
const outcome = await session.submitTurn(prompt, options);
|
|
402
|
+
await this.applyTurnOutcome(outcome, turnId);
|
|
403
|
+
}
|
|
404
|
+
async applyTurnOutcome(outcome, turnId) {
|
|
405
|
+
if (outcome.sessionId !== null &&
|
|
406
|
+
outcome.sessionId !== '' &&
|
|
407
|
+
outcome.sessionId !== this.threadId) {
|
|
408
|
+
this.threadId = outcome.sessionId;
|
|
409
|
+
await this.deps.state.setThreadId(this.dispatcherId, outcome.sessionId);
|
|
410
|
+
}
|
|
411
|
+
if (!outcome.isError)
|
|
412
|
+
this.lastResult = { text: outcome.text };
|
|
413
|
+
if (outcome.isError) {
|
|
414
|
+
const detail = outcome.errors.length > 0
|
|
415
|
+
? outcome.errors.join('; ')
|
|
416
|
+
: (outcome.subtype ?? 'unknown error');
|
|
417
|
+
throw new Error(`claude turn ${turnId} returned an error result: ${detail}`);
|
|
418
|
+
}
|
|
419
|
+
this.log('info', `claude-code turn ${turnId} completed`);
|
|
420
|
+
}
|
|
421
|
+
nextTurnId(kind) {
|
|
422
|
+
return `claude-${kind}-${this.runtimeInstanceId}-${++this.turnCounter}`;
|
|
423
|
+
}
|
|
424
|
+
buildProcessEnv(extraEnv) {
|
|
425
|
+
// Neutral env boundary: { ...process.env, ...injectEnv, ...extra_env }.
|
|
426
|
+
// `injectEnv` is the host's optional injection seam (empty today); `extraEnv`
|
|
427
|
+
// is this provider's own `config.extra_env`, merged last so it can override.
|
|
428
|
+
return {
|
|
429
|
+
...globalThis.process.env,
|
|
430
|
+
...(this.deps.injectEnv ?? {}),
|
|
431
|
+
...extraEnv,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
async setStatus(status, err) {
|
|
435
|
+
this.status = status;
|
|
436
|
+
await this.deps.state.setStatus(this.dispatcherId, status, err !== undefined ? { last_error: errMessage(err) } : {});
|
|
437
|
+
}
|
|
438
|
+
log(level, msg, err) {
|
|
439
|
+
this.logger[level](msg, err !== undefined ? { err } : undefined);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Minimal `console.error`-backed logger the runtime falls back to when the host
|
|
444
|
+
* passes none (the bare generic-loader / standalone path). Owned here, never in
|
|
445
|
+
* `@excitedjs/dreamux-types` (which is declaration-only).
|
|
446
|
+
*/
|
|
447
|
+
function consoleFallbackLogger(dispatcherId) {
|
|
448
|
+
const sink = (level) => (message, fields) => {
|
|
449
|
+
const prefix = `[claude-code ${dispatcherId}] ${level}`;
|
|
450
|
+
const err = fields?.['err'];
|
|
451
|
+
if (err !== undefined)
|
|
452
|
+
console.error(prefix, message, err);
|
|
453
|
+
else
|
|
454
|
+
console.error(prefix, message);
|
|
455
|
+
};
|
|
456
|
+
return {
|
|
457
|
+
error: sink('error'),
|
|
458
|
+
warn: sink('warn'),
|
|
459
|
+
info: sink('info'),
|
|
460
|
+
debug: () => { },
|
|
461
|
+
trace: () => { },
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/stream.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Claude Code stream-json wire protocol (issue #120).
|
|
3
|
+
*
|
|
4
|
+
* A clean-room model of the NDJSON envelopes the `claude` CLI emits and accepts
|
|
5
|
+
* on stdio under `--input-format stream-json --output-format stream-json`. This
|
|
6
|
+
* is what makes the `builtin:claude-code` runtime *resident*: one long-lived
|
|
7
|
+
* `claude --print` process consumes user-message lines on stdin and streams
|
|
8
|
+
* `init` / `assistant` / `result` envelopes on stdout, instead of a fresh
|
|
9
|
+
* one-shot process per turn.
|
|
10
|
+
*
|
|
11
|
+
* Two design rules, both load-bearing:
|
|
12
|
+
*
|
|
13
|
+
* - **Forward-tolerant by construction.** The CLI's real envelope set is much
|
|
14
|
+
* wider than anything Dreamux consumes (extra `system` subtypes, rate-limit
|
|
15
|
+
* events, hook lifecycle, streamlined variants, and extra `result` fields).
|
|
16
|
+
* This parser never validates a closed schema and never throws on an unknown
|
|
17
|
+
* `type` / `subtype`; it reads only the fields the runtime needs and ignores
|
|
18
|
+
* the rest. A wider or newer CLI build cannot break a turn.
|
|
19
|
+
*
|
|
20
|
+
* - **Pure, no IO.** No process, no clock, no filesystem — so the line framer,
|
|
21
|
+
* the line parser, and the turn aggregator are unit-tested against synthetic
|
|
22
|
+
* envelope sequences (hand-authored to the real wire shapes) with no live
|
|
23
|
+
* `claude` binary.
|
|
24
|
+
*
|
|
25
|
+
* Public-safety note: this module is a generic protocol model. It carries no
|
|
26
|
+
* Feishu identifiers, tokens, private paths, or environment-specific details.
|
|
27
|
+
*/
|
|
28
|
+
import type { JsonObject, ParsedLine, TurnOutcome, TurnSubmitOptions } from './types.js';
|
|
29
|
+
export type { JsonObject, ParsedLine, ResultEnvelope, TurnOutcome, } from './types.js';
|
|
30
|
+
/**
|
|
31
|
+
* Incremental newline framer. The child's stdout is NDJSON but arrives in
|
|
32
|
+
* arbitrary chunks; `push` returns the complete lines so far and buffers a
|
|
33
|
+
* trailing partial line until its newline lands. Blank lines are dropped.
|
|
34
|
+
*/
|
|
35
|
+
export declare class LineBuffer {
|
|
36
|
+
private buf;
|
|
37
|
+
push(chunk: string): string[];
|
|
38
|
+
/** Any buffered bytes with no trailing newline (e.g. at stream end). */
|
|
39
|
+
flush(): string | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Join the text blocks of an Anthropic assistant `message.content` array.
|
|
43
|
+
* `thinking` and `tool_use` blocks contribute no visible text and are skipped.
|
|
44
|
+
*/
|
|
45
|
+
export declare function assistantText(message: unknown): string;
|
|
46
|
+
/**
|
|
47
|
+
* Decode one stdout line. Never throws: a non-JSON line becomes `parse_error`,
|
|
48
|
+
* and a JSON object of an unmodelled type becomes `other`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseLine(line: string): ParsedLine;
|
|
51
|
+
/**
|
|
52
|
+
* One user turn as a stream-json `user` message line (no trailing newline).
|
|
53
|
+
*
|
|
54
|
+
* `isSynthetic` / `priority` are siblings of `message` on the stdin envelope
|
|
55
|
+
* (claude-code SDKUserMessage schema), not part of the message body. They are
|
|
56
|
+
* only set for the native completion-notification idiom; a plain channel turn
|
|
57
|
+
* omits them entirely and reads as a normal human user turn.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildUserMessage(text: string, options?: TurnSubmitOptions): string;
|
|
60
|
+
/** Enable Claude Code Remote Control via a stream-json control request. */
|
|
61
|
+
export declare function buildRemoteControlEnable(requestId: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* Answer a `can_use_tool` control request with `allow`. A Dreamux dispatcher
|
|
64
|
+
* runs unattended, so the answer for a runtime that has no human to consult is
|
|
65
|
+
* "allow"; `updatedInput` echoes the tool's original input back unchanged.
|
|
66
|
+
*
|
|
67
|
+
* This is a defensive path: under a bypassing permission mode the CLI does not
|
|
68
|
+
* gate tools, so `can_use_tool` is not normally emitted. It is wired so that if
|
|
69
|
+
* a build or mode does emit one, the runtime answers it rather than leaving the
|
|
70
|
+
* turn waiting on an unanswered control request.
|
|
71
|
+
*/
|
|
72
|
+
export declare function buildCanUseToolAllow(requestId: string, input: JsonObject): string;
|
|
73
|
+
/** Acknowledge any other control request with a bare success so the CLI proceeds. */
|
|
74
|
+
export declare function buildControlAck(requestId: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Accumulates the envelopes of a single turn and resolves a `TurnOutcome` when
|
|
77
|
+
* the `result` lands. One aggregator per turn: feed every `ParsedLine`, then
|
|
78
|
+
* read `outcome()` once `done` is true.
|
|
79
|
+
*
|
|
80
|
+
* The final text prefers the `result.result` (the CLI's own canonical answer)
|
|
81
|
+
* and falls back to the latest `assistant` snapshot — so a turn that ends
|
|
82
|
+
* tool-only or whose result text is empty still surfaces whatever the model
|
|
83
|
+
* last said.
|
|
84
|
+
*/
|
|
85
|
+
export declare class TurnAggregator {
|
|
86
|
+
private lastAssistantText;
|
|
87
|
+
private result;
|
|
88
|
+
private initSessionId;
|
|
89
|
+
/** Returns `true` once the terminal `result` has been seen. */
|
|
90
|
+
get done(): boolean;
|
|
91
|
+
/** The session id from `init` (or the result), once known. */
|
|
92
|
+
get sessionId(): string | null;
|
|
93
|
+
accept(line: ParsedLine): void;
|
|
94
|
+
outcome(): TurnOutcome | null;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EAEV,WAAW,EACX,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,WAAW,GACZ,MAAM,YAAY,CAAC;AAYpB;;;;GAIG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAM;IAEjB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAc7B,wEAAwE;IACxE,KAAK,IAAI,MAAM,GAAG,IAAI;CAKvB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CActD;AAuBD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAkElD;AAID;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAQR;AAED,2EAA2E;AAC3E,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CASjF;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKzD;AAID;;;;;;;;;GASG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,aAAa,CAAuB;IAE5C,+DAA+D;IAC/D,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,8DAA8D;IAC9D,IAAI,SAAS,IAAI,MAAM,GAAG,IAAI,CAE7B;IAED,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAgB9B,OAAO,IAAI,WAAW,GAAG,IAAI;CAa9B"}
|