@rynx-ai/runtime 0.1.11-beta.2 → 0.1.11-beta.20
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/models.d.ts +0 -5
- package/dist/claude/models.js +1 -7
- package/dist/claude/native-integration.d.ts +12 -6
- package/dist/claude/native-integration.js +137 -28
- package/dist/codex-app-server/forwarder.d.ts +33 -0
- package/dist/codex-app-server/forwarder.js +159 -0
- package/dist/codex-app-server/mcp-startup.d.ts +13 -0
- package/dist/codex-app-server/mcp-startup.js +63 -0
- package/dist/codex-app-server/protocol.d.ts +7 -4
- package/dist/codex-app-server/ws-channel.js +19 -19
- package/dist/codex-home.js +2 -4
- package/dist/host.d.ts +27 -7
- package/dist/host.js +298 -55
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/models-catalog.d.ts +2 -1
- package/dist/models-catalog.js +82 -3
- package/dist/runner/child.d.ts +22 -6
- package/dist/runner/child.js +237 -30
- package/dist/runner/manager.d.ts +72 -4
- package/dist/runner/manager.js +539 -54
- package/dist/runner/protocol.d.ts +6 -4
- package/dist/runner/startup-policy.d.ts +4 -0
- package/dist/runner/startup-policy.js +5 -0
- package/dist/terminal/claude-tui.d.ts +3 -1
- package/dist/terminal/claude-tui.js +3 -1
- package/dist/terminal/tmux.d.ts +14 -2
- package/dist/terminal/tmux.js +72 -14
- package/package.json +4 -3
|
@@ -70,8 +70,9 @@ export type ToChild = {
|
|
|
70
70
|
}
|
|
71
71
|
/** Eagerly bring up a session's live codex TUI + forwarder (codex-native), so
|
|
72
72
|
* its turns mirror to the bus regardless of whether the web has attached the
|
|
73
|
-
* terminal.
|
|
74
|
-
* the thread
|
|
73
|
+
* terminal. For Codex-lineage fresh sessions, `live.ready` acknowledges that
|
|
74
|
+
* the pane and observer exist; background thread discovery races the
|
|
75
|
+
* executor's bridge wait. */
|
|
75
76
|
| {
|
|
76
77
|
t: "live.ensure";
|
|
77
78
|
reqId: string;
|
|
@@ -160,8 +161,9 @@ export type FromChild = {
|
|
|
160
161
|
execution: ResolvedExecutionSnapshot;
|
|
161
162
|
parentSessionId?: string;
|
|
162
163
|
}
|
|
163
|
-
/** Result of a `live.ensure`: `ok`
|
|
164
|
-
*
|
|
164
|
+
/** Result of a `live.ensure`: for Codex-lineage sessions `ok` means the pane
|
|
165
|
+
* and observer started; thread discovery continues in parallel with
|
|
166
|
+
* injection. Other Providers retain their own readiness gate. */
|
|
165
167
|
| {
|
|
166
168
|
t: "live.ready";
|
|
167
169
|
reqId: string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AgentRuntimeId } from "@rynx-ai/core";
|
|
2
|
+
/** Current live providers are native CLI sessions. Keep this explicit so a
|
|
3
|
+
* future non-native provider does not silently inherit their startup budget. */
|
|
4
|
+
export declare function isManagedNativeProvider(provider: AgentRuntimeId | undefined): provider is AgentRuntimeId;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Current live providers are native CLI sessions. Keep this explicit so a
|
|
2
|
+
* future non-native provider does not silently inherit their startup budget. */
|
|
3
|
+
export function isManagedNativeProvider(provider) {
|
|
4
|
+
return provider === "codex" || provider === "traex" || provider === "claude";
|
|
5
|
+
}
|
|
@@ -18,6 +18,8 @@ export interface ClaudeTuiArgs {
|
|
|
18
18
|
settingSources?: string;
|
|
19
19
|
/** Launch model (`--model`); omit to use claude's default. */
|
|
20
20
|
model?: string;
|
|
21
|
+
/** Native Claude Code effort (`--effort`); omit to use its default. */
|
|
22
|
+
reasoningEffort?: string;
|
|
21
23
|
/** Agent instructions appended to Claude Code's native system prompt. */
|
|
22
24
|
appendSystemPrompt?: string;
|
|
23
25
|
/** Resume a specific prior claude session (`--resume <id>`). */
|
|
@@ -36,4 +38,4 @@ export interface ClaudeTuiArgs {
|
|
|
36
38
|
* Build the `claude` argv for an interactive, co-drivable TUI:
|
|
37
39
|
* `[..extra] [--resume <id>] [--model <m>] [--append-system-prompt <text>] --settings <json>`.
|
|
38
40
|
*/
|
|
39
|
-
export declare function buildClaudeTuiArgs({ settingsJson, settingSources, model, appendSystemPrompt, resume, forkSession, sessionId, additionalDirs, extraArgs, }: ClaudeTuiArgs): string[];
|
|
41
|
+
export declare function buildClaudeTuiArgs({ settingsJson, settingSources, model, reasoningEffort, appendSystemPrompt, resume, forkSession, sessionId, additionalDirs, extraArgs, }: ClaudeTuiArgs): string[];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Build the `claude` argv for an interactive, co-drivable TUI:
|
|
3
3
|
* `[..extra] [--resume <id>] [--model <m>] [--append-system-prompt <text>] --settings <json>`.
|
|
4
4
|
*/
|
|
5
|
-
export function buildClaudeTuiArgs({ settingsJson, settingSources, model, appendSystemPrompt, resume, forkSession = false, sessionId, additionalDirs = [], extraArgs = [], }) {
|
|
5
|
+
export function buildClaudeTuiArgs({ settingsJson, settingSources, model, reasoningEffort, appendSystemPrompt, resume, forkSession = false, sessionId, additionalDirs = [], extraArgs = [], }) {
|
|
6
6
|
const args = [...extraArgs];
|
|
7
7
|
if (additionalDirs.length > 0)
|
|
8
8
|
args.push("--add-dir", ...additionalDirs);
|
|
@@ -16,6 +16,8 @@ export function buildClaudeTuiArgs({ settingsJson, settingSources, model, append
|
|
|
16
16
|
args.push("--session-id", sessionId);
|
|
17
17
|
if (model)
|
|
18
18
|
args.push("--model", model);
|
|
19
|
+
if (reasoningEffort)
|
|
20
|
+
args.push("--effort", reasoningEffort);
|
|
19
21
|
if (appendSystemPrompt)
|
|
20
22
|
args.push("--append-system-prompt", appendSystemPrompt);
|
|
21
23
|
args.push("--settings", settingsJson);
|
package/dist/terminal/tmux.d.ts
CHANGED
|
@@ -43,6 +43,16 @@ export interface TmuxTerminalOptions {
|
|
|
43
43
|
/** Injectable for tests; defaults to the real `tmux` binary path. */
|
|
44
44
|
tmuxBin?: string;
|
|
45
45
|
}
|
|
46
|
+
/** Resolve the exact tmux executable selected by the current Rynx distribution. */
|
|
47
|
+
export declare function resolveTmuxBin(explicit?: string, env?: NodeJS.ProcessEnv): string;
|
|
48
|
+
/** Deterministic private socket owned by one terminal name. Parent-side
|
|
49
|
+
* shutdown uses the same mapping when the runner child is too wedged to clean
|
|
50
|
+
* up its own tmux server. */
|
|
51
|
+
export declare function tmuxSocketPath(name: string): string;
|
|
52
|
+
/** Kill one private tmux server and verify it no longer answers. Missing
|
|
53
|
+
* sockets are already stopped; an existing socket with an unusable tmux
|
|
54
|
+
* command is unproven and returns false. */
|
|
55
|
+
export declare function terminateTmuxServer(name: string, tmuxBin?: string): boolean;
|
|
46
56
|
/** Is a usable `tmux` on PATH? Cheap probe for the capability gate. */
|
|
47
57
|
export declare function isTmuxAvailable(tmuxBin?: string): boolean;
|
|
48
58
|
export declare class TmuxTerminal {
|
|
@@ -102,7 +112,7 @@ export declare class TmuxTerminal {
|
|
|
102
112
|
* there stalls the runner child's event loop (freezing the PTY stream → the
|
|
103
113
|
* terminal appears "stuck"). reference implementation's `_tmux_session_alive` uses an async
|
|
104
114
|
* subprocess + timeout for exactly this reason. */
|
|
105
|
-
|
|
115
|
+
isAliveAsync(): Promise<boolean>;
|
|
106
116
|
/** Type literal text into the pane (agent injection / co-drive from a
|
|
107
117
|
* non-PTY caller). `-l` sends the text literally rather than as key names. */
|
|
108
118
|
sendKeys(text: string): void;
|
|
@@ -138,7 +148,9 @@ export declare class TmuxTerminal {
|
|
|
138
148
|
paste(text: string, bufferName?: string): void;
|
|
139
149
|
/**
|
|
140
150
|
* Attach a client. `role: "read-only"` passes tmux `-r` so the viewer cannot
|
|
141
|
-
* type
|
|
151
|
+
* type and `ignore-size` so even its initial PTY dimensions cannot resize the
|
|
152
|
+
* owner's pane (defense-in-depth on top of the WS bridge dropping input and
|
|
153
|
+
* resize frames).
|
|
142
154
|
*/
|
|
143
155
|
attach(role: "owner" | "read-only", dims?: {
|
|
144
156
|
cols?: number;
|
package/dist/terminal/tmux.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { execFile, execFileSync } from "node:child_process";
|
|
18
18
|
import { createHash } from "node:crypto";
|
|
19
|
-
import { mkdirSync } from "node:fs";
|
|
19
|
+
import { existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
20
20
|
import { tmpdir } from "node:os";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
const TMUX_TARGET = "main";
|
|
@@ -26,8 +26,69 @@ const DEFAULT_ROWS = 24;
|
|
|
26
26
|
* wheel-scroll (tmux copy-mode, enabled by `mouse on`) reaches as far back as
|
|
27
27
|
* the terminal shows. Mirrors reference implementation's `TerminalEnvSpec.scrollback`. */
|
|
28
28
|
const DEFAULT_SCROLLBACK = 20000;
|
|
29
|
+
/** Resolve the exact tmux executable selected by the current Rynx distribution. */
|
|
30
|
+
export function resolveTmuxBin(explicit, env = process.env) {
|
|
31
|
+
if (explicit !== undefined)
|
|
32
|
+
return explicit;
|
|
33
|
+
if (Object.hasOwn(env, "RYNX_TMUX_BIN")) {
|
|
34
|
+
return env.RYNX_TMUX_BIN?.trim() ?? "";
|
|
35
|
+
}
|
|
36
|
+
return "tmux";
|
|
37
|
+
}
|
|
38
|
+
/** Deterministic private socket owned by one terminal name. Parent-side
|
|
39
|
+
* shutdown uses the same mapping when the runner child is too wedged to clean
|
|
40
|
+
* up its own tmux server. */
|
|
41
|
+
export function tmuxSocketPath(name) {
|
|
42
|
+
const key = createHash("sha256").update(name).digest("hex").slice(0, 16);
|
|
43
|
+
return join(tmpdir(), "rynx-term", `${key}.sock`);
|
|
44
|
+
}
|
|
45
|
+
/** Kill one private tmux server and verify it no longer answers. Missing
|
|
46
|
+
* sockets are already stopped; an existing socket with an unusable tmux
|
|
47
|
+
* command is unproven and returns false. */
|
|
48
|
+
export function terminateTmuxServer(name, tmuxBin = resolveTmuxBin()) {
|
|
49
|
+
const socketPath = tmuxSocketPath(name);
|
|
50
|
+
if (!existsSync(socketPath))
|
|
51
|
+
return true;
|
|
52
|
+
const base = ["-u", "-S", socketPath];
|
|
53
|
+
try {
|
|
54
|
+
execFileSync(tmuxBin, [...base, "kill-server"], { stdio: "ignore" });
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// A server may have exited between existsSync and kill-server. Verify below.
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
execFileSync(tmuxBin, [...base, "has-session", "-t", TMUX_TARGET], {
|
|
61
|
+
stdio: "ignore",
|
|
62
|
+
});
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
// Only a real tmux exit status proves the server/session is absent. Spawn
|
|
67
|
+
// failures (missing/unexecutable tmux) leave liveness unproven and must not
|
|
68
|
+
// unlink a socket that could still belong to a live server.
|
|
69
|
+
if (!error ||
|
|
70
|
+
typeof error !== "object" ||
|
|
71
|
+
!("status" in error) ||
|
|
72
|
+
typeof error.status !== "number") {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
// tmux custom-socket servers can exit while leaving the filesystem entry
|
|
77
|
+
// behind. Remove that verified-stale socket so process/socket inventories
|
|
78
|
+
// do not accumulate dead terminals forever.
|
|
79
|
+
unlinkSync(socketPath);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch (unlinkError) {
|
|
83
|
+
return Boolean(unlinkError &&
|
|
84
|
+
typeof unlinkError === "object" &&
|
|
85
|
+
"code" in unlinkError &&
|
|
86
|
+
unlinkError.code === "ENOENT");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
29
90
|
/** Is a usable `tmux` on PATH? Cheap probe for the capability gate. */
|
|
30
|
-
export function isTmuxAvailable(tmuxBin =
|
|
91
|
+
export function isTmuxAvailable(tmuxBin = resolveTmuxBin()) {
|
|
31
92
|
try {
|
|
32
93
|
execFileSync(tmuxBin, ["-V"], { stdio: "ignore" });
|
|
33
94
|
return true;
|
|
@@ -68,7 +129,7 @@ export class TmuxTerminal {
|
|
|
68
129
|
this.env = withUtf8Locale(opts.env ?? process.env);
|
|
69
130
|
this.cols = opts.cols ?? DEFAULT_COLS;
|
|
70
131
|
this.rows = opts.rows ?? DEFAULT_ROWS;
|
|
71
|
-
this.tmuxBin = opts.tmuxBin
|
|
132
|
+
this.tmuxBin = resolveTmuxBin(opts.tmuxBin);
|
|
72
133
|
this.injectedSpawn = opts.ptySpawn;
|
|
73
134
|
const dir = join(tmpdir(), "rynx-term");
|
|
74
135
|
mkdirSync(dir, { recursive: true });
|
|
@@ -76,8 +137,7 @@ export class TmuxTerminal {
|
|
|
76
137
|
// under the unix-domain path limit (~104 chars on macOS) — a full session id
|
|
77
138
|
// like `sess_<uuid>-main` under $TMPDIR would overflow it and fail with
|
|
78
139
|
// "File name too long". The hash stays unique per terminal.
|
|
79
|
-
|
|
80
|
-
this.socketPath = join(dir, `${key}.sock`);
|
|
140
|
+
this.socketPath = tmuxSocketPath(opts.name);
|
|
81
141
|
}
|
|
82
142
|
/** tmux argv prefix targeting this terminal's private server. */
|
|
83
143
|
base() {
|
|
@@ -286,14 +346,17 @@ export class TmuxTerminal {
|
|
|
286
346
|
}
|
|
287
347
|
/**
|
|
288
348
|
* Attach a client. `role: "read-only"` passes tmux `-r` so the viewer cannot
|
|
289
|
-
* type
|
|
349
|
+
* type and `ignore-size` so even its initial PTY dimensions cannot resize the
|
|
350
|
+
* owner's pane (defense-in-depth on top of the WS bridge dropping input and
|
|
351
|
+
* resize frames).
|
|
290
352
|
*/
|
|
291
353
|
async attach(role, dims) {
|
|
292
354
|
this.start();
|
|
293
355
|
const spawn = this.injectedSpawn ?? (await loadPtySpawn());
|
|
294
|
-
const args = [...this.base(), "attach-session"
|
|
356
|
+
const args = [...this.base(), "attach-session"];
|
|
295
357
|
if (role === "read-only")
|
|
296
|
-
args.push("-r");
|
|
358
|
+
args.push("-r", "-f", "ignore-size");
|
|
359
|
+
args.push("-t", TMUX_TARGET);
|
|
297
360
|
const proc = spawn(this.tmuxBin, args, {
|
|
298
361
|
name: "xterm-256color",
|
|
299
362
|
cols: dims?.cols ?? this.cols,
|
|
@@ -353,12 +416,7 @@ export class TmuxTerminal {
|
|
|
353
416
|
}
|
|
354
417
|
/** Kill the tmux server (ends the session and all attaches). */
|
|
355
418
|
kill() {
|
|
356
|
-
|
|
357
|
-
execFileSync(this.tmuxBin, [...this.base(), "kill-server"], { stdio: "ignore" });
|
|
358
|
-
}
|
|
359
|
-
catch {
|
|
360
|
-
// Already gone — nothing to clean up.
|
|
361
|
-
}
|
|
419
|
+
terminateTmuxServer(this.name, this.tmuxBin);
|
|
362
420
|
this.started = false;
|
|
363
421
|
}
|
|
364
422
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/runtime",
|
|
3
|
-
"version": "0.1.11-beta.
|
|
3
|
+
"version": "0.1.11-beta.20",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"node-pty": "
|
|
27
|
+
"node-pty": "1.2.0-beta.15",
|
|
28
|
+
"smol-toml": "1.7.1",
|
|
28
29
|
"ws": "^8.21.0",
|
|
29
|
-
"@rynx-ai/core": "0.1.11-beta.
|
|
30
|
+
"@rynx-ai/core": "0.1.11-beta.20"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@types/ws": "^8.18.1"
|