@rynx-ai/runtime 0.1.0 → 0.1.9
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/dist/claude/executor.d.ts +3 -5
- package/dist/claude/executor.js +3 -5
- package/dist/claude/native-bridge.d.ts +74 -17
- package/dist/claude/native-bridge.js +225 -30
- package/dist/claude/native-hook-main.js +291 -39
- package/dist/claude/native-hooks.d.ts +3 -2
- package/dist/claude/native-hooks.js +15 -6
- package/dist/claude/native-integration.d.ts +78 -5
- package/dist/claude/native-integration.js +417 -26
- package/dist/claude/settings.d.ts +8 -0
- package/dist/claude/settings.js +50 -0
- package/dist/claude/transcript.d.ts +2 -2
- package/dist/claude/transcript.js +3 -3
- package/dist/codex/rollout-synth.js +1 -1
- package/dist/codex-app-server/client.d.ts +26 -40
- package/dist/codex-app-server/client.js +1128 -99
- package/dist/codex-app-server/forwarder.d.ts +7 -7
- package/dist/codex-app-server/forwarder.js +11 -5
- package/dist/codex-app-server/mapping.d.ts +1 -1
- package/dist/codex-app-server/mapping.js +27 -2
- package/dist/codex-app-server/protocol.d.ts +238 -4
- package/dist/codex-app-server/transport.d.ts +20 -5
- package/dist/codex-app-server/transport.js +93 -40
- package/dist/codex-app-server/ws-channel.d.ts +3 -3
- package/dist/codex-app-server/ws-channel.js +23 -7
- package/dist/codex-child-env.js +33 -0
- package/dist/codex-home.d.ts +6 -6
- package/dist/codex-home.js +8 -9
- package/dist/codex-session-store.d.ts +2 -1
- package/dist/host.d.ts +34 -33
- package/dist/host.js +531 -91
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/interactions.d.ts +61 -0
- package/dist/interactions.js +236 -0
- package/dist/models-catalog.d.ts +1 -1
- package/dist/models-catalog.js +1 -1
- package/dist/runner/child.d.ts +9 -1
- package/dist/runner/child.js +93 -15
- package/dist/runner/manager.d.ts +59 -10
- package/dist/runner/manager.js +385 -41
- package/dist/runner/protocol.d.ts +18 -7
- package/dist/runner-main.js +9 -6
- package/dist/runtime-status.js +1 -1
- package/dist/terminal/claude-tui.d.ts +8 -3
- package/dist/terminal/claude-tui.js +6 -2
- package/dist/terminal/codex-tui.d.ts +3 -3
- package/dist/terminal/codex-tui.js +1 -1
- package/dist/terminal/registry.d.ts +1 -1
- package/dist/terminal/registry.js +1 -1
- package/dist/terminal/tmux.d.ts +6 -6
- package/dist/terminal/tmux.js +10 -10
- package/package.json +3 -3
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* (`live.ensure` / `inject` / `live.interrupt`) plus per-thread capabilities and
|
|
7
7
|
* terminal attachment; the reply channels mirror each request's `reqId`.
|
|
8
8
|
*/
|
|
9
|
-
import { AgentRuntimeError, type AgentRuntimeId, type AgentSpec, type SessionEvent } from "@rynx-ai/core";
|
|
9
|
+
import { AgentRuntimeError, type AgentRuntimeId, type AgentSpec, type ReasoningEffort, type SessionInteractionResolution, type SessionEvent } from "@rynx-ai/core";
|
|
10
|
+
import type { ResolveInteractionResult } from "../interactions.js";
|
|
10
11
|
/** A runtime error flattened for the wire; rebuilt parent-side as `AgentRuntimeError`. */
|
|
11
12
|
export interface WireError {
|
|
12
13
|
message: string;
|
|
@@ -18,14 +19,15 @@ export interface WireError {
|
|
|
18
19
|
export type CapName = "getGoal" | "setGoal" | "clearGoal" | "forkSession";
|
|
19
20
|
/** A live terminal role: `owner` is read-write, `read-only` a viewer. */
|
|
20
21
|
export type TerminalRole = "owner" | "read-only";
|
|
21
|
-
/**
|
|
22
|
-
|
|
22
|
+
/** Stable reason for a terminal attach failure. `terminal_not_live` is an
|
|
23
|
+
* expected, retryable absence; `terminal_open_failed` is an infrastructure error. */
|
|
24
|
+
export type TerminalOpenErrorCode = "terminal_not_live" | "terminal_open_failed";
|
|
23
25
|
/** Outcome of an `inject`. `injected` = the app-server / tmux accepted the turn.
|
|
24
26
|
* `notLive` = no live forwarder for this session (caller may use the run path).
|
|
25
27
|
* `notReady` = live but not ready within the park window; `failed` = injection
|
|
26
28
|
* threw. For BOTH notReady and failed the caller reports an error and MUST NOT
|
|
27
29
|
* re-run the turn (re-running would double-write alongside the forwarder).
|
|
28
|
-
* Mirrors
|
|
30
|
+
* Mirrors reference implementation's "inject failure ⇒ response.failed, never re-run locally". */
|
|
29
31
|
export type InjectOutcome = "injected" | "notLive" | "notReady" | "failed";
|
|
30
32
|
/** Parent → child. Terminal PTY bytes ride `term.input` base64-encoded so the
|
|
31
33
|
* NDJSON line framing stays intact (raw bytes contain newlines). */
|
|
@@ -60,10 +62,11 @@ export type ToChild = {
|
|
|
60
62
|
t: "term.close";
|
|
61
63
|
attachId: string;
|
|
62
64
|
} | {
|
|
63
|
-
t: "
|
|
65
|
+
t: "interaction.resolve";
|
|
66
|
+
reqId: string;
|
|
64
67
|
localThreadId: string;
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
interactionId: string;
|
|
69
|
+
resolution: SessionInteractionResolution;
|
|
67
70
|
}
|
|
68
71
|
/** Eagerly bring up a session's live codex TUI + forwarder (codex-native), so
|
|
69
72
|
* its turns mirror to the bus regardless of whether the web has attached the
|
|
@@ -80,6 +83,8 @@ export type ToChild = {
|
|
|
80
83
|
* control plane), so live picks the right backend per agent — not the
|
|
81
84
|
* global default. Absent ⇒ host falls back to the store record / default. */
|
|
82
85
|
runtime?: AgentRuntimeId;
|
|
86
|
+
/** Model-advertised reasoning effort for the live TUI and turn/start. */
|
|
87
|
+
reasoningEffort?: ReasoningEffort;
|
|
83
88
|
/** The session's preset agent id, so the host can load its spec and apply
|
|
84
89
|
* the agent's model / skills / instructions to the live launch (not just
|
|
85
90
|
* the runtime). Absent for inline-config or agent-less sessions. */
|
|
@@ -135,6 +140,7 @@ export type FromChild = {
|
|
|
135
140
|
} | {
|
|
136
141
|
t: "term.error";
|
|
137
142
|
attachId: string;
|
|
143
|
+
code: TerminalOpenErrorCode;
|
|
138
144
|
message: string;
|
|
139
145
|
}
|
|
140
146
|
/** A canonical {@link SessionEvent} mirrored by the session's persistent codex
|
|
@@ -184,6 +190,11 @@ export type FromChild = {
|
|
|
184
190
|
localThreadId: string;
|
|
185
191
|
ok: boolean;
|
|
186
192
|
error?: string;
|
|
193
|
+
} | {
|
|
194
|
+
t: "interaction.resolved";
|
|
195
|
+
reqId: string;
|
|
196
|
+
localThreadId: string;
|
|
197
|
+
result: ResolveInteractionResult;
|
|
187
198
|
};
|
|
188
199
|
/** Encode a message as a single NDJSON line (newline included). */
|
|
189
200
|
export declare function encodeMessage(msg: ToChild | FromChild): string;
|
package/dist/runner-main.js
CHANGED
|
@@ -44,9 +44,12 @@ function main() {
|
|
|
44
44
|
...(process.env.RYNX_RUNNER_SESSION ? { sessionId: process.env.RYNX_RUNNER_SESSION } : {}),
|
|
45
45
|
});
|
|
46
46
|
const transport = new StdioRunnerTransport(process.stdin, process.stdout);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
let runner;
|
|
48
|
+
transport.onClose(() => {
|
|
49
|
+
runner?.shutdown();
|
|
50
|
+
process.exit(0);
|
|
51
|
+
});
|
|
52
|
+
runner = new RunnerSession({
|
|
50
53
|
transport,
|
|
51
54
|
executor,
|
|
52
55
|
onShutdown: () => {
|
|
@@ -54,8 +57,8 @@ function main() {
|
|
|
54
57
|
process.exit(0);
|
|
55
58
|
},
|
|
56
59
|
});
|
|
57
|
-
// A parent SIGTERM (idle reap / shutdown)
|
|
58
|
-
// the
|
|
59
|
-
process.on("SIGTERM", () =>
|
|
60
|
+
// A parent SIGTERM (idle reap / shutdown) uses the same ordered cleanup path
|
|
61
|
+
// as the protocol shutdown before ending the process.
|
|
62
|
+
process.on("SIGTERM", () => runner?.shutdown());
|
|
60
63
|
}
|
|
61
64
|
main();
|
package/dist/runtime-status.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The parent control plane no longer holds an execution backend (runners own
|
|
5
5
|
* the codex app-server / claude PTY), so it must answer "is this runtime usable
|
|
6
|
-
* on this machine?" without spinning one up. This mirrors
|
|
6
|
+
* on this machine?" without spinning one up. This mirrors reference implementation's model,
|
|
7
7
|
* where readiness is `which(binary) + a usable credential` rather than a live
|
|
8
8
|
* app-server. For codex/traex that is a one-shot `<bin> login status` (no
|
|
9
9
|
* app-server); for claude it reuses {@link readClaudeStatus} (SDK + credentials).
|
|
@@ -11,10 +11,15 @@
|
|
|
11
11
|
* launch so the daemon-spawned TUI never blocks on a first-run prompt.
|
|
12
12
|
*/
|
|
13
13
|
export interface ClaudeTuiArgs {
|
|
14
|
-
/**
|
|
14
|
+
/** Claude `--settings` value: a JSON document or private settings path. */
|
|
15
15
|
settingsJson: string;
|
|
16
|
+
/** Comma-separated native setting sources. An empty string isolates a
|
|
17
|
+
* managed session from host/project hooks while keeping explicit settings. */
|
|
18
|
+
settingSources?: string;
|
|
16
19
|
/** Launch model (`--model`); omit to use claude's default. */
|
|
17
20
|
model?: string;
|
|
21
|
+
/** Agent instructions appended to Claude Code's native system prompt. */
|
|
22
|
+
appendSystemPrompt?: string;
|
|
18
23
|
/** Resume a specific prior claude session (`--resume <id>`). */
|
|
19
24
|
resume?: string;
|
|
20
25
|
/** Extra claude args placed before the injected flags (rarely needed). */
|
|
@@ -22,6 +27,6 @@ export interface ClaudeTuiArgs {
|
|
|
22
27
|
}
|
|
23
28
|
/**
|
|
24
29
|
* Build the `claude` argv for an interactive, co-drivable TUI:
|
|
25
|
-
* `[..extra] [--resume <id>] [--model <m>] --settings <json>`.
|
|
30
|
+
* `[..extra] [--resume <id>] [--model <m>] [--append-system-prompt <text>] --settings <json>`.
|
|
26
31
|
*/
|
|
27
|
-
export declare function buildClaudeTuiArgs({ settingsJson, model, resume, extraArgs, }: ClaudeTuiArgs): string[];
|
|
32
|
+
export declare function buildClaudeTuiArgs({ settingsJson, settingSources, model, appendSystemPrompt, resume, extraArgs, }: ClaudeTuiArgs): string[];
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Build the `claude` argv for an interactive, co-drivable TUI:
|
|
3
|
-
* `[..extra] [--resume <id>] [--model <m>] --settings <json>`.
|
|
3
|
+
* `[..extra] [--resume <id>] [--model <m>] [--append-system-prompt <text>] --settings <json>`.
|
|
4
4
|
*/
|
|
5
|
-
export function buildClaudeTuiArgs({ settingsJson, model, resume, extraArgs = [], }) {
|
|
5
|
+
export function buildClaudeTuiArgs({ settingsJson, settingSources, model, appendSystemPrompt, resume, extraArgs = [], }) {
|
|
6
6
|
const args = [...extraArgs];
|
|
7
|
+
if (settingSources !== undefined)
|
|
8
|
+
args.push("--setting-sources", settingSources);
|
|
7
9
|
if (resume)
|
|
8
10
|
args.push("--resume", resume);
|
|
9
11
|
if (model)
|
|
10
12
|
args.push("--model", model);
|
|
13
|
+
if (appendSystemPrompt)
|
|
14
|
+
args.push("--append-system-prompt", appendSystemPrompt);
|
|
11
15
|
args.push("--settings", settingsJson);
|
|
12
16
|
return args;
|
|
13
17
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* codex-native live-terminal launcher (Phase D). Encodes
|
|
2
|
+
* codex-native live-terminal launcher (Phase D). Encodes reference implementation's verified
|
|
3
3
|
* co-drive recipe: a separate `codex` TUI process attaches to the session's
|
|
4
4
|
* already-running app-server over `--remote` and resumes the SAME thread, so the
|
|
5
5
|
* TUI, the structured data channel, and the web bridge all drive one thread.
|
|
6
6
|
*
|
|
7
|
-
* Two facts carried over from
|
|
7
|
+
* Two facts carried over from reference implementation's working implementation
|
|
8
8
|
* (`codex_native_app_server.build_codex_remote_args`):
|
|
9
9
|
* - the app-server must listen on a multi-client transport (`ws://IP:PORT` or
|
|
10
10
|
* `unix://PATH`) — stdio only admits the one client that spawned it, so it
|
|
@@ -31,7 +31,7 @@ export interface CodexRemoteArgs {
|
|
|
31
31
|
codexArgs?: string[];
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Build the `codex` argv tail for an app-server-backed TUI. Mirrors
|
|
34
|
+
* Build the `codex` argv tail for an app-server-backed TUI. Mirrors reference implementation's
|
|
35
35
|
* `build_codex_remote_args` exactly: overrides → codexArgs → (resume) → --remote.
|
|
36
36
|
*/
|
|
37
37
|
export declare function buildCodexRemoteArgs({ remoteUrl, threadId, configOverrides, codexArgs, }: CodexRemoteArgs): string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Build the `codex` argv tail for an app-server-backed TUI. Mirrors
|
|
2
|
+
* Build the `codex` argv tail for an app-server-backed TUI. Mirrors reference implementation's
|
|
3
3
|
* `build_codex_remote_args` exactly: overrides → codexArgs → (resume) → --remote.
|
|
4
4
|
*/
|
|
5
5
|
export function buildCodexRemoteArgs({ remoteUrl, threadId, configOverrides = [], codexArgs = [], }) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Owns terminal lifecycle. Read-only vs read-write is decided PURELY by the
|
|
5
5
|
* requested role (the caller derives it from the viewer's permission level),
|
|
6
|
-
* exactly like
|
|
6
|
+
* exactly like reference implementation's terminal attach (`terminal_attach.py`: `read_only`
|
|
7
7
|
* from the URL → `-r`). There is deliberately NO stateful "first owner wins,
|
|
8
8
|
* later owners downgraded" slot: that rynx-only mechanism leaked its owner slot
|
|
9
9
|
* across a detach (the release ran on the attachment's `onExit`, which the
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Owns terminal lifecycle. Read-only vs read-write is decided PURELY by the
|
|
5
5
|
* requested role (the caller derives it from the viewer's permission level),
|
|
6
|
-
* exactly like
|
|
6
|
+
* exactly like reference implementation's terminal attach (`terminal_attach.py`: `read_only`
|
|
7
7
|
* from the URL → `-r`). There is deliberately NO stateful "first owner wins,
|
|
8
8
|
* later owners downgraded" slot: that rynx-only mechanism leaked its owner slot
|
|
9
9
|
* across a detach (the release ran on the attachment's `onExit`, which the
|
package/dist/terminal/tmux.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ export declare class TmuxTerminal {
|
|
|
64
64
|
* command. Idempotent: a second call is a no-op once the session exists. */
|
|
65
65
|
start(): void;
|
|
66
66
|
/**
|
|
67
|
-
* Apply
|
|
67
|
+
* Apply reference implementation's tmux option suite to the private server (inner/terminal.py).
|
|
68
68
|
* These are NOT cosmetic — several fix real co-drive behavior that the tmux
|
|
69
69
|
* defaults break:
|
|
70
70
|
* - `mouse on`: the web terminal's wheel scrolls the pane's scrollback (and
|
|
@@ -83,7 +83,7 @@ export declare class TmuxTerminal {
|
|
|
83
83
|
* inner CLI exits so its last output stays capturable and `#{pane_dead}` reads
|
|
84
84
|
* the exit (liveness probe), instead of the server vanishing.
|
|
85
85
|
* - `MouseDown3*` unbinds: no right-click menu to spawn extra panes/windows.
|
|
86
|
-
* - `status off`: hide tmux chrome.
|
|
86
|
+
* - `status off`: hide tmux chrome. reference implementation keeps the status line only to show
|
|
87
87
|
* a conversation link; rynx has none, so the whole line (and its
|
|
88
88
|
* `[main] 0:node*` window list) is hidden.
|
|
89
89
|
* `-q`/`-gq`/`-sq` keep an older tmux that lacks an option from failing launch.
|
|
@@ -95,12 +95,12 @@ export declare class TmuxTerminal {
|
|
|
95
95
|
* `remain-on-exit on` the session/server deliberately outlive the inner CLI's
|
|
96
96
|
* exit (a dead pane shows tmux's "Pane is dead"), so `has-session` succeeding
|
|
97
97
|
* no longer implies a live process. Alive only when the session exists AND its
|
|
98
|
-
* pane process has not exited. Mirrors
|
|
98
|
+
* pane process has not exited. Mirrors reference implementation's `_terminal.is_alive`. */
|
|
99
99
|
isAlive(): boolean;
|
|
100
100
|
/** Async pane-liveness probe — MUST NOT block the event loop. The attach
|
|
101
101
|
* pane-death watcher polls this on an interval; a synchronous `execFileSync`
|
|
102
102
|
* there stalls the runner child's event loop (freezing the PTY stream → the
|
|
103
|
-
* terminal appears "stuck").
|
|
103
|
+
* terminal appears "stuck"). reference implementation's `_tmux_session_alive` uses an async
|
|
104
104
|
* subprocess + timeout for exactly this reason. */
|
|
105
105
|
private isAliveAsync;
|
|
106
106
|
/** Type literal text into the pane (agent injection / co-drive from a
|
|
@@ -119,11 +119,11 @@ export declare class TmuxTerminal {
|
|
|
119
119
|
sendEnter(): void;
|
|
120
120
|
/** Interrupt the pane's running TUI turn with an Escape key — codex/claude both
|
|
121
121
|
* cancel an in-flight response on a single Esc ("esc to interrupt"). A key NAME
|
|
122
|
-
* (no `-l`) so tmux interprets it. Mirrors
|
|
122
|
+
* (no `-l`) so tmux interprets it. Mirrors reference implementation's `inject_interrupt`. */
|
|
123
123
|
interrupt(): void;
|
|
124
124
|
/** Clear the current input line before an injection so leftover keystrokes
|
|
125
125
|
* can't prepend to the pasted draft. `C-a` (Home) + `C-k` (kill-to-end) is
|
|
126
|
-
* the safe pair — `C-u` only clears backwards from the cursor (
|
|
126
|
+
* the safe pair — `C-u` only clears backwards from the cursor (reference implementation). */
|
|
127
127
|
clearInputLine(): void;
|
|
128
128
|
/** Send one or more tmux key NAMES (e.g. `Enter`, `C-a`) to the pane. */
|
|
129
129
|
private sendKeyNames;
|
package/dist/terminal/tmux.js
CHANGED
|
@@ -24,7 +24,7 @@ const DEFAULT_COLS = 80;
|
|
|
24
24
|
const DEFAULT_ROWS = 24;
|
|
25
25
|
/** tmux pane scrollback depth. Matches the browser xterm `scrollback` so a
|
|
26
26
|
* wheel-scroll (tmux copy-mode, enabled by `mouse on`) reaches as far back as
|
|
27
|
-
* the terminal shows. Mirrors
|
|
27
|
+
* the terminal shows. Mirrors reference implementation's `TerminalEnvSpec.scrollback`. */
|
|
28
28
|
const DEFAULT_SCROLLBACK = 20000;
|
|
29
29
|
/** Is a usable `tmux` on PATH? Cheap probe for the capability gate. */
|
|
30
30
|
export function isTmuxAvailable(tmuxBin = "tmux") {
|
|
@@ -42,7 +42,7 @@ async function loadPtySpawn() {
|
|
|
42
42
|
}
|
|
43
43
|
/** Ensure a UTF-8 locale so tmux renders multibyte (CJK) output correctly. A
|
|
44
44
|
* pm2/systemd-spawned daemon often has no LANG/LC_*, so tmux treats CJK bytes as
|
|
45
|
-
* single-byte and mangles Chinese.
|
|
45
|
+
* single-byte and mangles Chinese. reference implementation inherits a UTF-8 locale from its launch
|
|
46
46
|
* env; a pm2 daemon may not — so inject one when none is present. */
|
|
47
47
|
function withUtf8Locale(env) {
|
|
48
48
|
const hasUtf8 = [env.LC_ALL, env.LC_CTYPE, env.LANG].some((v) => v != null && /utf-?8/i.test(v));
|
|
@@ -121,7 +121,7 @@ export class TmuxTerminal {
|
|
|
121
121
|
this.started = true;
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
|
-
* Apply
|
|
124
|
+
* Apply reference implementation's tmux option suite to the private server (inner/terminal.py).
|
|
125
125
|
* These are NOT cosmetic — several fix real co-drive behavior that the tmux
|
|
126
126
|
* defaults break:
|
|
127
127
|
* - `mouse on`: the web terminal's wheel scrolls the pane's scrollback (and
|
|
@@ -140,7 +140,7 @@ export class TmuxTerminal {
|
|
|
140
140
|
* inner CLI exits so its last output stays capturable and `#{pane_dead}` reads
|
|
141
141
|
* the exit (liveness probe), instead of the server vanishing.
|
|
142
142
|
* - `MouseDown3*` unbinds: no right-click menu to spawn extra panes/windows.
|
|
143
|
-
* - `status off`: hide tmux chrome.
|
|
143
|
+
* - `status off`: hide tmux chrome. reference implementation keeps the status line only to show
|
|
144
144
|
* a conversation link; rynx has none, so the whole line (and its
|
|
145
145
|
* `[main] 0:node*` window list) is hidden.
|
|
146
146
|
* `-q`/`-gq`/`-sq` keep an older tmux that lacks an option from failing launch.
|
|
@@ -170,7 +170,7 @@ export class TmuxTerminal {
|
|
|
170
170
|
["unbind-key", "-q", "-T", "root", "M-MouseDown3Status"],
|
|
171
171
|
["unbind-key", "-q", "-T", "root", "MouseDown3StatusLeft"],
|
|
172
172
|
["unbind-key", "-q", "-T", "root", "M-MouseDown3StatusLeft"],
|
|
173
|
-
// hide tmux chrome (no conversation link to show, unlike
|
|
173
|
+
// hide tmux chrome (no conversation link to show, unlike reference implementation)
|
|
174
174
|
["set-option", "-g", "status", "off"],
|
|
175
175
|
];
|
|
176
176
|
const argv = [...this.base()];
|
|
@@ -186,7 +186,7 @@ export class TmuxTerminal {
|
|
|
186
186
|
* `remain-on-exit on` the session/server deliberately outlive the inner CLI's
|
|
187
187
|
* exit (a dead pane shows tmux's "Pane is dead"), so `has-session` succeeding
|
|
188
188
|
* no longer implies a live process. Alive only when the session exists AND its
|
|
189
|
-
* pane process has not exited. Mirrors
|
|
189
|
+
* pane process has not exited. Mirrors reference implementation's `_terminal.is_alive`. */
|
|
190
190
|
isAlive() {
|
|
191
191
|
if (!this.started)
|
|
192
192
|
return false;
|
|
@@ -205,7 +205,7 @@ export class TmuxTerminal {
|
|
|
205
205
|
/** Async pane-liveness probe — MUST NOT block the event loop. The attach
|
|
206
206
|
* pane-death watcher polls this on an interval; a synchronous `execFileSync`
|
|
207
207
|
* there stalls the runner child's event loop (freezing the PTY stream → the
|
|
208
|
-
* terminal appears "stuck").
|
|
208
|
+
* terminal appears "stuck"). reference implementation's `_tmux_session_alive` uses an async
|
|
209
209
|
* subprocess + timeout for exactly this reason. */
|
|
210
210
|
isAliveAsync() {
|
|
211
211
|
if (!this.started)
|
|
@@ -251,13 +251,13 @@ export class TmuxTerminal {
|
|
|
251
251
|
}
|
|
252
252
|
/** Interrupt the pane's running TUI turn with an Escape key — codex/claude both
|
|
253
253
|
* cancel an in-flight response on a single Esc ("esc to interrupt"). A key NAME
|
|
254
|
-
* (no `-l`) so tmux interprets it. Mirrors
|
|
254
|
+
* (no `-l`) so tmux interprets it. Mirrors reference implementation's `inject_interrupt`. */
|
|
255
255
|
interrupt() {
|
|
256
256
|
this.sendKeyNames("Escape");
|
|
257
257
|
}
|
|
258
258
|
/** Clear the current input line before an injection so leftover keystrokes
|
|
259
259
|
* can't prepend to the pasted draft. `C-a` (Home) + `C-k` (kill-to-end) is
|
|
260
|
-
* the safe pair — `C-u` only clears backwards from the cursor (
|
|
260
|
+
* the safe pair — `C-u` only clears backwards from the cursor (reference implementation). */
|
|
261
261
|
clearInputLine() {
|
|
262
262
|
this.sendKeyNames("C-a", "C-k");
|
|
263
263
|
}
|
|
@@ -305,7 +305,7 @@ export class TmuxTerminal {
|
|
|
305
305
|
// process alive (so `proc.onExit` never fires) while tmux paints "Pane is dead".
|
|
306
306
|
// Poll `#{pane_dead}` and synthesize an exit so the web bridge closes the WS and
|
|
307
307
|
// shows the dead-pane overlay instead of leaking tmux's "Pane is dead" text
|
|
308
|
-
// (
|
|
308
|
+
// (reference implementation: the server closes the socket on pane death → StatusOverlay).
|
|
309
309
|
// Multi-listener, not a single slot: the runner child (and any future
|
|
310
310
|
// subscriber) register `onExit`; a single-slot setter would let a later
|
|
311
311
|
// `onExit` silently CLOBBER an earlier one (that clobber is exactly how the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Runtime host that drives local coding-agent CLIs (codex / traex / claude) behind one AgentExecutor + AgentCapabilities contract.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"node-pty": "^1.0.0",
|
|
23
23
|
"ws": "^8.21.0",
|
|
24
|
-
"@rynx-ai/core": "0.1.
|
|
24
|
+
"@rynx-ai/core": "0.1.9"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/ws": "^8.18.1"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
|
-
"build": "rm -rf dist && tsc -p tsconfig.json && chmod +x dist/runner-main.js dist/claude/native-hook-main.js dist/claude/native-message-display-main.js dist/claude/native-status-main.js"
|
|
30
|
+
"build": "node ../../scripts/verify-node-pty.cjs && rm -rf dist && tsc -p tsconfig.json && chmod +x dist/runner-main.js dist/claude/native-hook-main.js dist/claude/native-message-display-main.js dist/claude/native-status-main.js"
|
|
31
31
|
}
|
|
32
32
|
}
|