@excitedjs/agent-runtime-claude-code 0.6.1 → 0.7.0-alpha.g1e011989dff1
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/README.md +92 -44
- package/dist/config.d.ts +2 -2
- package/dist/control-rpc.d.ts +32 -0
- package/dist/control-rpc.d.ts.map +1 -0
- package/dist/control-rpc.js +112 -0
- package/dist/control-rpc.js.map +1 -0
- package/dist/rpc.d.ts +37 -93
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +242 -405
- package/dist/rpc.js.map +1 -1
- package/dist/runtime-activity.d.ts +33 -0
- package/dist/runtime-activity.d.ts.map +1 -0
- package/dist/runtime-activity.js +221 -0
- package/dist/runtime-activity.js.map +1 -0
- package/dist/runtime-session.d.ts +6 -2
- package/dist/runtime-session.d.ts.map +1 -1
- package/dist/runtime-session.js +20 -11
- package/dist/runtime-session.js.map +1 -1
- package/dist/runtime.d.ts +13 -15
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +53 -152
- package/dist/runtime.js.map +1 -1
- package/dist/stream.d.ts +17 -3
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +75 -11
- package/dist/stream.js.map +1 -1
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +35 -31
- package/dist/supervisor.js.map +1 -1
- package/dist/types.d.ts +38 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
- package/dist/admission-classify.d.ts +0 -7
- package/dist/admission-classify.d.ts.map +0 -1
- package/dist/admission-classify.js +0 -17
- package/dist/admission-classify.js.map +0 -1
- package/dist/runtime-submissions.d.ts +0 -58
- package/dist/runtime-submissions.d.ts.map +0 -1
- package/dist/runtime-submissions.js +0 -281
- package/dist/runtime-submissions.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,68 +7,116 @@ The built-in **Claude Code** Agent Runtime provider for
|
|
|
7
7
|
It implements the public `AgentRuntimeProvider` contract from
|
|
8
8
|
[`@excitedjs/dreamux-types`](../../dreamux-types) against a resident `claude`
|
|
9
9
|
stream-json child: process supervision, the stream-json wire protocol (line
|
|
10
|
-
framing,
|
|
11
|
-
|
|
10
|
+
framing, result aggregation, control-request replies), pending-request idle
|
|
11
|
+
deadlines, MCP config translation (`--mcp-config`), teammate completion delivery
|
|
12
12
|
as a plain user turn, bounded native transcript pagination, and Claude Code
|
|
13
13
|
doctor diagnostics.
|
|
14
14
|
|
|
15
15
|
## Boundary
|
|
16
16
|
|
|
17
|
-
This package
|
|
17
|
+
This package uses `@excitedjs/dreamux-types` and shared `@excitedjs/dreamux-utils`.
|
|
18
|
+
It never imports
|
|
18
19
|
`@excitedjs/dreamux` core. Everything host-specific — per-dispatcher paths, the
|
|
19
20
|
durable state sink, the process `PATH` seeded from the host package bins — is
|
|
20
21
|
supplied by the Dreamux host through the neutral `AgentRuntimeCreateContext` and
|
|
21
22
|
the provider factory options. The package owns only Claude Code engine mechanics
|
|
22
23
|
and its own runtime config parsing; it reconstructs no Dreamux host
|
|
23
|
-
layout/path/log contracts.
|
|
24
|
-
|
|
24
|
+
layout/path/log contracts. Shared process supervision and state-write fencing
|
|
25
|
+
come from `@excitedjs/dreamux-utils`.
|
|
25
26
|
|
|
26
27
|
## Loading
|
|
27
28
|
|
|
28
|
-
Dreamux core resolves `builtin:claude-code`
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
default-exports a generic provider-loader factory, so
|
|
29
|
+
Dreamux core resolves `builtin:claude-code` through the generic provider loader
|
|
30
|
+
and supplies host contracts through the neutral create context. The package
|
|
31
|
+
default-exports that provider factory, so
|
|
32
32
|
`loadExternalAgentRuntimeProviders({ refs: ['builtin:claude-code'] })` can load it
|
|
33
33
|
through the same package-loader path as external `npm:` providers.
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## Resident session and request settlement
|
|
36
36
|
|
|
37
37
|
The public runtime boundary returns one `RuntimeSubmission` handle per accepted
|
|
38
|
-
send
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
send. Every input follows the same native write path; admission resolves after
|
|
39
|
+
write acknowledgement or positive native evidence. RPC holds one table of
|
|
40
|
+
unanswered requests and settles them directly from native results or command
|
|
41
|
+
cancellation/refusal. There is no enclosing request window or aggregate drainage.
|
|
42
|
+
|
|
43
|
+
A native result answering requests creates one immutable `RuntimeCompletion`
|
|
44
|
+
shared by those requests. Folded inputs share that object; queued inputs wait
|
|
45
|
+
for their own result. Consumption lifecycle and a matching submitted result UUID
|
|
46
|
+
are positive attribution evidence. A completed lifecycle frame may precede or
|
|
47
|
+
follow its result and does not decide when another input can be submitted.
|
|
48
|
+
|
|
49
|
+
Background native turns may run without a submission. Their activity and result
|
|
50
|
+
boundaries remain observable, but they settle no unrelated request and do not
|
|
51
|
+
terminate the resident process. Explicit inputs steered into a background turn
|
|
52
|
+
settle normally once they join it. Concurrent input requires lifecycle evidence;
|
|
53
|
+
sessions without it retain single-input compatibility. Native `cancelled` can
|
|
54
|
+
describe a hard failure; it does not imply a user stop. Consumed commands retain
|
|
55
|
+
their result membership through terminal lifecycle frames. An unconsumed command
|
|
56
|
+
that is cancelled, refused or discarded settles as failed. Every native error
|
|
57
|
+
result remains observable, including UUID-less errors and the `success` arm with
|
|
58
|
+
`is_error: true` carrying API error text.
|
|
59
|
+
|
|
60
|
+
A setup error can precede `started` and omit the input UUID. It reports a failed
|
|
61
|
+
native end with its error details but cannot identify a queued request. A later
|
|
62
|
+
named `cancelled` fails that request with its protocol state; the adapter does not
|
|
63
|
+
guess that a preceding unbound error belongs to it.
|
|
64
|
+
|
|
65
|
+
Core owns source deduplication, captured recipients and completion-token delivery.
|
|
66
|
+
The provider owns native admission: `failed` means the command was proven not
|
|
67
|
+
written, while `ambiguous` means a native write may have been accepted and must
|
|
68
|
+
not be retried automatically. Runtime stop synchronously fences new input,
|
|
69
|
+
releases pending capability/write waiters, terminates the supervised process
|
|
70
|
+
group with absence proof, resolves unsettled submissions as stopped, and drains
|
|
71
|
+
already-started admission calls before it resolves.
|
|
56
72
|
|
|
57
73
|
## Native sessions and transcripts
|
|
58
74
|
|
|
59
|
-
For a fresh runtime this package generates the native UUID before launch
|
|
60
|
-
passes it through Claude Code's `--session-id
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
is
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
For a fresh runtime this package generates the native UUID before launch and
|
|
76
|
+
passes it through Claude Code's `--session-id`. The native identity is durably
|
|
77
|
+
published before input is written. Resume uses that same authoritative session
|
|
78
|
+
id. Recent activity reads locate Claude's native history independently of the
|
|
79
|
+
live process; transcript discovery never controls request settlement.
|
|
80
|
+
|
|
81
|
+
`readRecentActivity` is a cold bounded read that never starts a Claude process.
|
|
82
|
+
It returns neutral assistant/tool activity records and provider-owned opaque
|
|
83
|
+
cursors. It is not a completion source.
|
|
84
|
+
|
|
85
|
+
## Custom session factories
|
|
86
|
+
|
|
87
|
+
A custom `ClaudeCodeSessionFactory` now implements `submit()` returning
|
|
88
|
+
`RuntimeAdmission`; accepted submissions carry their own settlement promises.
|
|
89
|
+
The old `submitTurn()`/`steerTurn()` window interface is removed. Session specs
|
|
90
|
+
provide the pinned `sessionId` and optional `outputSchemaEnabled` result contract;
|
|
91
|
+
the exit handler receives its failure cause.
|
|
92
|
+
|
|
93
|
+
Session implementations own result validation and settlement. Their
|
|
94
|
+
`onProtocolEvent` callback reports native activity independently: result events
|
|
95
|
+
retain `commandUuids` for observation, and the public `command_lifecycle` variant
|
|
96
|
+
reports native command state without deciding settlement in runtime or Core.
|
|
97
|
+
An `interrupted` event can report an independently known interruption boundary.
|
|
98
|
+
Emitting a callback alone no longer settles a
|
|
99
|
+
request. These are breaking changes to the Claude-specific extension seam;
|
|
100
|
+
the neutral `AgentRuntime` and `RuntimeSubmission` contracts are unchanged.
|
|
101
|
+
|
|
102
|
+
## Direct stream RPC consumers
|
|
103
|
+
|
|
104
|
+
The exported `ClaudeCodeStreamRpc` has the same single `submit()` path, returning
|
|
105
|
+
`Promise<RuntimeAdmission>` in place of `submitTurn()` and `steerTurn()`. Accepted
|
|
106
|
+
handles own eventual settlement; callers no longer await an aggregate window.
|
|
107
|
+
Replace `failPending(error)` with `fail(error)` for transport failure or `stop()`
|
|
108
|
+
for deliberate teardown. A session retired after an unexpected exit retains
|
|
109
|
+
that failure for subsequent admission, even after cleanup calls `stop()`.
|
|
110
|
+
|
|
111
|
+
`ClaudeCodeStreamRpcOptions` now requires `sessionId` (a pinned native ID or
|
|
112
|
+
`null`) and accepts optional `outputSchemaEnabled` for result validation.
|
|
113
|
+
`reapOnTimeout(error)` receives the failure `Error`. Protocol callbacks are
|
|
114
|
+
observation only, as described above. Parsed `ResultEnvelope` and `TurnOutcome`
|
|
115
|
+
also expose `terminalReason`, a string or `null`, for native terminal diagnostics.
|
|
116
|
+
These changes break direct RPC consumers as well as custom session factories.
|
|
117
|
+
|
|
118
|
+
The default adapter uses consumption events because the observed background
|
|
119
|
+
folds omit both result UUID echo fields. Claude Code 2.1.263 marks
|
|
120
|
+
`command_lifecycle` as internal; it is not a documented stable SDK contract.
|
|
121
|
+
Compatibility tests and real native captures are distinguished in the task's
|
|
122
|
+
verification record.
|
package/dist/config.d.ts
CHANGED
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
* is reset on every inbound stream line, so it bounds the max time the
|
|
29
29
|
* still-alive child may emit *no* stream activity — not the total turn duration.
|
|
30
30
|
* A child silent for the whole window is failed and reaped/re-spawned (rather
|
|
31
|
-
* than
|
|
32
|
-
*
|
|
31
|
+
* than blocking request settlement and TeamMate completion delivery), while a
|
|
32
|
+
* long but actively-streaming turn never trips it.
|
|
33
33
|
*/
|
|
34
34
|
export interface DispatcherClaudeCodeConfig {
|
|
35
35
|
bin: string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Writable } from 'node:stream';
|
|
2
|
+
export declare class ClaudeCodeControlRpc {
|
|
3
|
+
private readonly stdin;
|
|
4
|
+
private readonly options;
|
|
5
|
+
private remoteControlRequestId;
|
|
6
|
+
private pendingInterrupt;
|
|
7
|
+
constructor(stdin: Writable, options: {
|
|
8
|
+
log?: (level: 'info' | 'warn' | 'error', msg: string, err?: unknown) => void;
|
|
9
|
+
onRemoteControlUrl?: (url: string) => void;
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Ask claude to interrupt whatever it is doing, and answer when it replies.
|
|
13
|
+
*
|
|
14
|
+
* Which turn this belongs to is the caller's fact, not this class's: this
|
|
15
|
+
* only correlates one control request with its `control_response`. A second
|
|
16
|
+
* ask while one is outstanding joins it rather than sending a second request.
|
|
17
|
+
*/
|
|
18
|
+
requestInterrupt(reason: string): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Answer an outstanding interrupt from the turn's own ending instead.
|
|
21
|
+
*
|
|
22
|
+
* The turn can reach its terminal before claude answers the control request;
|
|
23
|
+
* the caller, which owns the turn, decides that has happened and says how it
|
|
24
|
+
* ended. A no-op when nothing is outstanding, because the ordinary path is
|
|
25
|
+
* the `control_response` below.
|
|
26
|
+
*/
|
|
27
|
+
settleInterrupt(failure?: Error, interrupted?: boolean): void;
|
|
28
|
+
enableRemoteControl(): void;
|
|
29
|
+
onControlRequest(requestId: string | null, subtype: string | null, request: Record<string, unknown>): void;
|
|
30
|
+
onControlResponse(requestId: string | null, ok: boolean, response: Record<string, unknown> | null, error: string | null): void;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=control-rpc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-rpc.d.ts","sourceRoot":"","sources":["../src/control-rpc.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAgB5C,qBAAa,oBAAoB;IAK7B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,gBAAgB,CAAiC;gBAGtC,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE;QACxB,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;QAC7E,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KAC5C;IAGH;;;;;;OAMG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBlD;;;;;;;OAOG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,WAAW,UAAQ,GAAG,IAAI;IAQ3D,mBAAmB,IAAI,IAAI;IAM3B,gBAAgB,CACd,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,IAAI;IAoBP,iBAAiB,CACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,EAAE,EAAE,OAAO,EACX,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACxC,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,IAAI;CA4BR"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** Claude Code stream-json control requests and their one pending reply. */
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { buildCanUseToolAllow, buildControlAck, buildInterruptRequest, buildRemoteControlEnable, } from './stream.js';
|
|
4
|
+
export class ClaudeCodeControlRpc {
|
|
5
|
+
stdin;
|
|
6
|
+
options;
|
|
7
|
+
remoteControlRequestId = null;
|
|
8
|
+
pendingInterrupt = null;
|
|
9
|
+
constructor(stdin, options) {
|
|
10
|
+
this.stdin = stdin;
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Ask claude to interrupt whatever it is doing, and answer when it replies.
|
|
15
|
+
*
|
|
16
|
+
* Which turn this belongs to is the caller's fact, not this class's: this
|
|
17
|
+
* only correlates one control request with its `control_response`. A second
|
|
18
|
+
* ask while one is outstanding joins it rather than sending a second request.
|
|
19
|
+
*/
|
|
20
|
+
requestInterrupt(reason) {
|
|
21
|
+
if (this.pendingInterrupt !== null)
|
|
22
|
+
return this.pendingInterrupt.promise;
|
|
23
|
+
const requestId = randomUUID();
|
|
24
|
+
let resolve;
|
|
25
|
+
let reject;
|
|
26
|
+
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
27
|
+
resolve = resolvePromise;
|
|
28
|
+
reject = rejectPromise;
|
|
29
|
+
});
|
|
30
|
+
const pending = { requestId, promise, resolve, reject };
|
|
31
|
+
this.pendingInterrupt = pending;
|
|
32
|
+
this.stdin.write(`${buildInterruptRequest(requestId, reason)}\n`, (error) => {
|
|
33
|
+
if (error != null && this.pendingInterrupt === pending) {
|
|
34
|
+
this.pendingInterrupt = null;
|
|
35
|
+
reject(error);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return promise;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Answer an outstanding interrupt from the turn's own ending instead.
|
|
42
|
+
*
|
|
43
|
+
* The turn can reach its terminal before claude answers the control request;
|
|
44
|
+
* the caller, which owns the turn, decides that has happened and says how it
|
|
45
|
+
* ended. A no-op when nothing is outstanding, because the ordinary path is
|
|
46
|
+
* the `control_response` below.
|
|
47
|
+
*/
|
|
48
|
+
settleInterrupt(failure, interrupted = false) {
|
|
49
|
+
const pending = this.pendingInterrupt;
|
|
50
|
+
if (pending === null)
|
|
51
|
+
return;
|
|
52
|
+
this.pendingInterrupt = null;
|
|
53
|
+
if (failure !== undefined)
|
|
54
|
+
pending.reject(failure);
|
|
55
|
+
else
|
|
56
|
+
pending.resolve(interrupted);
|
|
57
|
+
}
|
|
58
|
+
enableRemoteControl() {
|
|
59
|
+
if (!this.stdin.writable)
|
|
60
|
+
return;
|
|
61
|
+
this.remoteControlRequestId = randomUUID();
|
|
62
|
+
this.stdin.write(`${buildRemoteControlEnable(this.remoteControlRequestId)}\n`);
|
|
63
|
+
}
|
|
64
|
+
onControlRequest(requestId, subtype, request) {
|
|
65
|
+
if (requestId === null || !this.stdin.writable)
|
|
66
|
+
return;
|
|
67
|
+
// Unattended posture: answer permission callbacks so a turn never wedges
|
|
68
|
+
// waiting on a human.
|
|
69
|
+
let reply;
|
|
70
|
+
if (subtype === 'can_use_tool') {
|
|
71
|
+
const rawInput = request['input'];
|
|
72
|
+
const input = typeof rawInput === 'object' &&
|
|
73
|
+
rawInput !== null &&
|
|
74
|
+
!Array.isArray(rawInput)
|
|
75
|
+
? rawInput
|
|
76
|
+
: {};
|
|
77
|
+
reply = buildCanUseToolAllow(requestId, input);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
reply = buildControlAck(requestId);
|
|
81
|
+
}
|
|
82
|
+
this.stdin.write(`${reply}\n`);
|
|
83
|
+
}
|
|
84
|
+
onControlResponse(requestId, ok, response, error) {
|
|
85
|
+
if (requestId === null)
|
|
86
|
+
return;
|
|
87
|
+
const interrupt = this.pendingInterrupt;
|
|
88
|
+
if (interrupt !== null && requestId === interrupt.requestId) {
|
|
89
|
+
this.pendingInterrupt = null;
|
|
90
|
+
if (ok)
|
|
91
|
+
interrupt.resolve(true);
|
|
92
|
+
else
|
|
93
|
+
interrupt.reject(new Error(error ?? 'claude interrupt request failed'));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (requestId !== this.remoteControlRequestId)
|
|
97
|
+
return;
|
|
98
|
+
this.remoteControlRequestId = null;
|
|
99
|
+
if (ok && response !== null) {
|
|
100
|
+
const url = response['session_url'] ?? response['connect_url'];
|
|
101
|
+
if (typeof url === 'string') {
|
|
102
|
+
this.options.onRemoteControlUrl?.(url);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.options.log?.('warn', 'claude remote control enable succeeded without a URL');
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
this.options.log?.('warn', `claude remote control enable failed${error !== null ? `: ${error}` : ''}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=control-rpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-rpc.js","sourceRoot":"","sources":["../src/control-rpc.ts"],"names":[],"mappings":"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"}
|
package/dist/rpc.d.ts
CHANGED
|
@@ -1,45 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Claude Code stream-json turn RPC.
|
|
3
|
-
*
|
|
4
|
-
* The supervisor owns the child process. This class owns one in-flight command
|
|
5
|
-
* group, stdout line demux, command drainage, and defensive control replies.
|
|
6
|
-
*
|
|
7
|
-
* One resident CLI execution window can span several submitted commands: a
|
|
8
|
-
* live steer is written while the CLI is already running. How the CLI answers them is
|
|
9
|
-
* not fixed, which is what makes settlement subtle (probed against a live
|
|
10
|
-
* 2.1.231 resident session):
|
|
11
|
-
*
|
|
12
|
-
* - **Commands fold.** A message that arrives while the in-flight turn is
|
|
13
|
-
* inside a tool call is absorbed into that turn at the next query-loop
|
|
14
|
-
* boundary. Several commands then share ONE `result` (3 → 1 observed), and
|
|
15
|
-
* a folded command's uuid never appears on any `result`.
|
|
16
|
-
* - **Or they do not.** A command that arrives between turns runs on its own
|
|
17
|
-
* and gets its own `result`.
|
|
18
|
-
* - **`result.user_message_uuid` is not a completion ledger.** It is present
|
|
19
|
-
* only sometimes, is not reliably the first-submitted uuid of a fold, and
|
|
20
|
-
* is absent entirely on the `error_during_execution` artifact an
|
|
21
|
-
* interrupt produces.
|
|
22
|
-
* - **`command_lifecycle` is the attribution signal.** Started commands identify
|
|
23
|
-
* the submissions represented by the next native `result`; terminal states
|
|
24
|
-
* drain the resident execution window. Its ordering against `result` is not
|
|
25
|
-
* stable.
|
|
26
|
-
*
|
|
27
|
-
* Every valid `result` is forwarded immediately as its own native completion
|
|
28
|
-
* boundary. Lifecycle terminality only decides when the command group has
|
|
29
|
-
* drained and the resident session may accept a new initial command; it never
|
|
30
|
-
* aggregates several results into one completion.
|
|
31
|
-
*/
|
|
1
|
+
/** Resident Claude input admission, request settlement and native stream handling. */
|
|
32
2
|
import type { Writable } from 'node:stream';
|
|
33
3
|
import type { TurnSubmitOptions } from './types.js';
|
|
34
|
-
|
|
35
|
-
export declare class ClaudeSteerAdmissionError extends Error {
|
|
36
|
-
readonly admission: 'failed' | 'ambiguous';
|
|
37
|
-
constructor(admission: 'failed' | 'ambiguous', message: string, options?: ErrorOptions);
|
|
38
|
-
}
|
|
4
|
+
import type { RuntimeAdmission } from '@excitedjs/dreamux-types';
|
|
39
5
|
export interface ClaudeCodeStreamRpcOptions {
|
|
6
|
+
sessionId: string | null;
|
|
7
|
+
outputSchemaEnabled?: boolean;
|
|
40
8
|
turnTimeoutMs: number;
|
|
41
9
|
log?: (level: 'info' | 'warn' | 'error', msg: string, err?: unknown) => void;
|
|
42
|
-
reapOnTimeout: () => void;
|
|
10
|
+
reapOnTimeout: (error: Error) => void;
|
|
43
11
|
onRemoteControlUrl?: (url: string) => void;
|
|
44
12
|
onProtocolEvent?: import('./types.js').ClaudeCodeSessionSpec['onProtocolEvent'];
|
|
45
13
|
}
|
|
@@ -47,71 +15,47 @@ export declare class ClaudeCodeStreamRpc {
|
|
|
47
15
|
private readonly stdin;
|
|
48
16
|
private readonly options;
|
|
49
17
|
private readonly lineBuf;
|
|
50
|
-
private
|
|
18
|
+
private readonly aggregator;
|
|
19
|
+
private readonly requests;
|
|
20
|
+
private readonly consumed;
|
|
51
21
|
private lifecycleSupported;
|
|
52
|
-
private
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
steerTurn(prompt: string, options?: TurnSubmitOptions, commandUuid?: string): Promise<void>;
|
|
56
|
-
private writeSteer;
|
|
57
|
-
onStdoutChunk(chunk: string): void;
|
|
58
|
-
failPending(err: Error): void;
|
|
59
|
-
enableRemoteControl(): void;
|
|
22
|
+
private timer;
|
|
23
|
+
private closed;
|
|
24
|
+
private readonly control;
|
|
60
25
|
/**
|
|
61
|
-
*
|
|
62
|
-
* returning it so the caller can resolve or reject it exactly once.
|
|
63
|
-
*/
|
|
64
|
-
private settlePending;
|
|
65
|
-
/**
|
|
66
|
-
* Mark a submitted command as producing nothing further, then re-check
|
|
67
|
-
* settlement. `abnormalReason` is `null` for the normal ending (`completed`)
|
|
68
|
-
* and a short phrase for a command that never ran — those are logged,
|
|
69
|
-
* because the CLI gives no other trace of a command it declined.
|
|
26
|
+
* An interrupt this session asked for has not been answered yet.
|
|
70
27
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
28
|
+
* It is the session's fact, not a request's: claude interrupts whatever it is
|
|
29
|
+
* doing, and the artifact it leaves behind names the command it was in the
|
|
30
|
+
* middle of. This only decides who is still waiting for an answer — what
|
|
31
|
+
* ended the work is read from the result itself.
|
|
75
32
|
*/
|
|
76
|
-
private
|
|
33
|
+
private interruptRequested;
|
|
34
|
+
constructor(stdin: Writable, options: ClaudeCodeStreamRpcOptions);
|
|
35
|
+
submit(prompt: string, options?: TurnSubmitOptions, commandUuid?: string): Promise<RuntimeAdmission>;
|
|
36
|
+
private acceptRequest;
|
|
37
|
+
private failWrite;
|
|
38
|
+
onStdoutChunk(chunk: string): void;
|
|
39
|
+
/** Actual transport loss fails each outstanding request, without a completion. */
|
|
40
|
+
fail(error: Error): void;
|
|
41
|
+
/** Explicit teardown stops requests and converges unconfirmed admissions. */
|
|
42
|
+
stop(): void;
|
|
43
|
+
private close;
|
|
77
44
|
/**
|
|
78
|
-
*
|
|
79
|
-
* lifecycle state AND at least one valid `result` has been seen. Result
|
|
80
|
-
* identity and settlement have already been forwarded one-by-one.
|
|
81
|
-
*
|
|
82
|
-
* Two escapes, both anti-hang:
|
|
83
|
-
*
|
|
84
|
-
* - no lifecycle signal at all (`msg_lifecycle_v1` absent, so no
|
|
85
|
-
* `command_lifecycle` will ever arrive) — the `result` is then the only
|
|
86
|
-
* terminal event there is, so settle on it;
|
|
87
|
-
* - every command terminal, none of them ever ran, and no result — nothing
|
|
88
|
-
* can answer this turn, so fail it loudly. The idle deadline is not an
|
|
89
|
-
* acceptable backstop here: it reaps the resident child, and any inbound
|
|
90
|
-
* line re-arms it, so a healthy session could be killed long after the
|
|
91
|
-
* turn became unanswerable.
|
|
45
|
+
* Ask claude to interrupt whatever it is doing.
|
|
92
46
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*/
|
|
98
|
-
private settleIfReady;
|
|
99
|
-
/**
|
|
100
|
-
* (Re)arm the per-turn idle deadline. `turnTimeoutMs` is a *max-idle* window,
|
|
101
|
-
* not a total-turn cap: any inbound stream line for this turn pushes it out
|
|
102
|
-
* (see `onLine`). A genuinely wedged child (no stream activity for the whole
|
|
103
|
-
* window) is still reaped — preserving the #120 anti-hang intent — but a long
|
|
104
|
-
* but continuously-streaming turn never trips the deadline (#156).
|
|
47
|
+
* Whatever it is doing, not only what this host asked for: a resident session
|
|
48
|
+
* runs turns of its own, and a `/stop` that skipped those would leave the
|
|
49
|
+
* conversation watching work it cannot stop. Answers false only when there is
|
|
50
|
+
* no session left to ask.
|
|
105
51
|
*/
|
|
52
|
+
interrupt(reason: string): Promise<boolean>;
|
|
53
|
+
enableRemoteControl(): void;
|
|
54
|
+
private clearIdleIfEmpty;
|
|
55
|
+
/** Outstanding work has a max-idle deadline; pure background work has none. */
|
|
106
56
|
private armIdleTimer;
|
|
107
57
|
private onLine;
|
|
108
58
|
private decideLifecycleSupport;
|
|
109
|
-
private
|
|
110
|
-
private rejectCapabilityWaiters;
|
|
111
|
-
private resolveWriteWaiter;
|
|
112
|
-
private rejectWriteWaiter;
|
|
113
|
-
private rejectWriteWaiters;
|
|
114
|
-
private onControlRequest;
|
|
115
|
-
private onControlResponse;
|
|
59
|
+
private rejectWaitingRequests;
|
|
116
60
|
}
|
|
117
61
|
//# sourceMappingURL=rpc.d.ts.map
|
package/dist/rpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAW5C,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,0BAA0B,CAAC;AAWlC,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7E,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACtC,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,eAAe,CAAC,EAAE,OAAO,YAAY,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;CACjF;AAkBD,qBAAa,mBAAmB;IAoB5B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IApB1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,kBAAkB,CAAwB;IAClD,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C;;;;;;;OAOG;IACH,OAAO,CAAC,kBAAkB,CAAS;gBAGhB,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,0BAA0B;IAKtD,MAAM,CACJ,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,iBAAsB,EAC/B,WAAW,GAAE,MAAqB,GACjC,OAAO,CAAC,gBAAgB,CAAC;IAwC5B,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,SAAS;IAUjB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQlC,kFAAkF;IAClF,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAIxB,6EAA6E;IAC7E,IAAI,IAAI,IAAI;IAIZ,OAAO,CAAC,KAAK;IA2Bb;;;;;;;OAOG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWjD,mBAAmB,IAAI,IAAI;IAK3B,OAAO,CAAC,gBAAgB;IAOxB,+EAA+E;IAC/E,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,MAAM;IAwGd,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,qBAAqB;CAY9B"}
|