@rynx-ai/runtime 0.1.10 → 0.1.11-beta.10
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-bridge.js +3 -8
- package/dist/claude/native-integration.d.ts +12 -1
- package/dist/claude/native-integration.js +16 -2
- package/dist/claude/transcript.d.ts +0 -7
- package/dist/claude/transcript.js +6 -20
- package/dist/codex-app-server/client.d.ts +2 -1
- package/dist/codex-app-server/forwarder.d.ts +4 -1
- package/dist/codex-app-server/forwarder.js +19 -1
- package/dist/codex-app-server/protocol.d.ts +45 -1
- package/dist/codex-home.d.ts +9 -26
- package/dist/codex-home.js +37 -65
- package/dist/codex-session-store.d.ts +22 -10
- package/dist/codex-session-store.js +277 -12
- package/dist/host.d.ts +48 -47
- package/dist/host.js +810 -355
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -2
- package/dist/models-catalog.d.ts +3 -1
- package/dist/models-catalog.js +125 -4
- package/dist/provider-workspace.d.ts +56 -0
- package/dist/provider-workspace.js +83 -0
- package/dist/runner/child.d.ts +59 -6
- package/dist/runner/child.js +138 -19
- package/dist/runner/manager.d.ts +104 -19
- package/dist/runner/manager.js +922 -89
- package/dist/runner/protocol.d.ts +7 -18
- package/dist/runner-main.js +12 -4
- package/dist/runtime-state-paths.d.ts +10 -0
- package/dist/runtime-state-paths.js +53 -0
- package/dist/terminal/claude-tui.d.ts +10 -1
- package/dist/terminal/claude-tui.js +9 -1
- package/dist/terminal/codex-tui.d.ts +5 -1
- package/dist/terminal/codex-tui.js +12 -3
- package/dist/terminal/tmux.d.ts +11 -1
- package/dist/terminal/tmux.js +61 -12
- package/package.json +3 -3
- package/dist/codex/rollout-synth.d.ts +0 -42
- package/dist/codex/rollout-synth.js +0 -245
|
@@ -6,7 +6,7 @@
|
|
|
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
|
|
9
|
+
import { AgentRuntimeError, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionEvent, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
|
|
10
10
|
import type { ResolveInteractionResult } from "../interactions.js";
|
|
11
11
|
/** A runtime error flattened for the wire; rebuilt parent-side as `AgentRuntimeError`. */
|
|
12
12
|
export interface WireError {
|
|
@@ -76,22 +76,12 @@ export type ToChild = {
|
|
|
76
76
|
t: "live.ensure";
|
|
77
77
|
reqId: string;
|
|
78
78
|
localThreadId: string;
|
|
79
|
-
|
|
79
|
+
/** The two immutable Session snapshots. Provider launch/resume only
|
|
80
|
+
* projects these values and never resolves a Project or Agent. */
|
|
81
|
+
workspace: SessionWorkspaceSnapshot;
|
|
82
|
+
execution: ResolvedExecutionSnapshot;
|
|
80
83
|
cols?: number;
|
|
81
84
|
rows?: number;
|
|
82
|
-
/** Runtime this session co-drives (resolved from the agent spec by the
|
|
83
|
-
* control plane), so live picks the right backend per agent — not the
|
|
84
|
-
* global default. Absent ⇒ host falls back to the store record / default. */
|
|
85
|
-
runtime?: AgentRuntimeId;
|
|
86
|
-
/** Model-advertised reasoning effort for the live TUI and turn/start. */
|
|
87
|
-
reasoningEffort?: ReasoningEffort;
|
|
88
|
-
/** The session's preset agent id, so the host can load its spec and apply
|
|
89
|
-
* the agent's model / skills / instructions to the live launch (not just
|
|
90
|
-
* the runtime). Absent for inline-config or agent-less sessions. */
|
|
91
|
-
agentName?: string;
|
|
92
|
-
/** An inline agent spec (console session created from an inline config
|
|
93
|
-
* rather than a preset id) — same purpose as `agentName`. */
|
|
94
|
-
agentSpec?: AgentSpec;
|
|
95
85
|
/** Setup terminals only need the Provider pane to exist. Message delivery
|
|
96
86
|
* keeps the default and waits until the native thread is actually bound. */
|
|
97
87
|
waitForReady?: boolean;
|
|
@@ -166,9 +156,8 @@ export type FromChild = {
|
|
|
166
156
|
from: string;
|
|
167
157
|
to: string;
|
|
168
158
|
kind: "clear" | "fork";
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
cwd?: string;
|
|
159
|
+
workspace: SessionWorkspaceSnapshot;
|
|
160
|
+
execution: ResolvedExecutionSnapshot;
|
|
172
161
|
parentSessionId?: string;
|
|
173
162
|
}
|
|
174
163
|
/** Result of a `live.ensure`: `ok` once the requested gate is reached (pane
|
package/dist/runner-main.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* active-session display, which tolerate atomic-rename reads). A parent-of-record
|
|
17
17
|
* design (ship-in / emit-out) is the remote-ready follow-up.
|
|
18
18
|
*/
|
|
19
|
-
import { loadConfig
|
|
19
|
+
import { loadConfig } from "@rynx-ai/core";
|
|
20
20
|
import { FileCodexSessionStore, LocalAgentHost, resolveCodexSessionStorePath, } from "./host.js";
|
|
21
21
|
import { RunnerSession } from "./runner/child.js";
|
|
22
22
|
import { StdioRunnerTransport } from "./runner/transport.js";
|
|
@@ -33,15 +33,20 @@ for (const stream of [process.stdout, process.stderr]) {
|
|
|
33
33
|
throw err;
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
-
function main() {
|
|
36
|
+
async function main() {
|
|
37
37
|
const config = loadConfig();
|
|
38
38
|
const sessionStore = new FileCodexSessionStore(resolveCodexSessionStorePath(config));
|
|
39
|
+
const sessionId = process.env.RYNX_RUNNER_SESSION;
|
|
40
|
+
const binding = sessionId ? await sessionStore.get(sessionId) : null;
|
|
39
41
|
const executor = new LocalAgentHost({
|
|
40
42
|
config,
|
|
41
43
|
sessionStore,
|
|
42
44
|
// The manager spawns one child per session and passes its key here so the
|
|
43
45
|
// host's private CODEX_HOME is scoped to this session (see manager.spawnHandle).
|
|
44
|
-
...(
|
|
46
|
+
...(sessionId ? { sessionId } : {}),
|
|
47
|
+
...(binding?.runtimeHomeOwnerSessionId
|
|
48
|
+
? { runtimeHomeSessionId: binding.runtimeHomeOwnerSessionId }
|
|
49
|
+
: {}),
|
|
45
50
|
});
|
|
46
51
|
const transport = new StdioRunnerTransport(process.stdin, process.stdout);
|
|
47
52
|
let runner;
|
|
@@ -61,4 +66,7 @@ function main() {
|
|
|
61
66
|
// as the protocol shutdown before ending the process.
|
|
62
67
|
process.on("SIGTERM", () => runner?.shutdown());
|
|
63
68
|
}
|
|
64
|
-
main()
|
|
69
|
+
void main().catch((error) => {
|
|
70
|
+
console.error(error);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function runtimeSessionDigest(sessionId: string): string;
|
|
2
|
+
export declare function runtimeSessionStateDir(sessionId: string): string;
|
|
3
|
+
/** Pre-standardization uid-private temp root, read only for opaque directory adoption. */
|
|
4
|
+
export declare function legacyRuntimeStateRoot(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Adopt a pre-standardization runtime directory without interpreting Provider
|
|
7
|
+
* files. Rename when possible; cross-device filesystems fall back to an opaque
|
|
8
|
+
* recursive copy followed by removal.
|
|
9
|
+
*/
|
|
10
|
+
export declare function adoptLegacyRuntimeDirectory(legacyDirectory: string, canonicalDirectory: string): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, renameSync, rmSync, } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { rynxRuntimeDir } from "@rynx-ai/core";
|
|
6
|
+
export function runtimeSessionDigest(sessionId) {
|
|
7
|
+
return createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
|
|
8
|
+
}
|
|
9
|
+
export function runtimeSessionStateDir(sessionId) {
|
|
10
|
+
return join(rynxRuntimeDir(), "sessions", runtimeSessionDigest(sessionId));
|
|
11
|
+
}
|
|
12
|
+
/** Pre-standardization uid-private temp root, read only for opaque directory adoption. */
|
|
13
|
+
export function legacyRuntimeStateRoot() {
|
|
14
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : "nouid";
|
|
15
|
+
return join(tmpdir(), `rynx-${uid}`);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Adopt a pre-standardization runtime directory without interpreting Provider
|
|
19
|
+
* files. Rename when possible; cross-device filesystems fall back to an opaque
|
|
20
|
+
* recursive copy followed by removal.
|
|
21
|
+
*/
|
|
22
|
+
export function adoptLegacyRuntimeDirectory(legacyDirectory, canonicalDirectory) {
|
|
23
|
+
if (existsSync(canonicalDirectory) || !existsSync(legacyDirectory))
|
|
24
|
+
return;
|
|
25
|
+
mkdirSync(dirname(canonicalDirectory), { recursive: true, mode: 0o700 });
|
|
26
|
+
try {
|
|
27
|
+
renameSync(legacyDirectory, canonicalDirectory);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
if (existsSync(canonicalDirectory) || !existsSync(legacyDirectory))
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const stagingDirectory = `${canonicalDirectory}.adopt-${process.pid}-${Date.now().toString(36)}`;
|
|
35
|
+
try {
|
|
36
|
+
cpSync(legacyDirectory, stagingDirectory, {
|
|
37
|
+
recursive: true,
|
|
38
|
+
errorOnExist: true,
|
|
39
|
+
force: false,
|
|
40
|
+
});
|
|
41
|
+
try {
|
|
42
|
+
renameSync(stagingDirectory, canonicalDirectory);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (!existsSync(canonicalDirectory))
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
rmSync(legacyDirectory, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
rmSync(stagingDirectory, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -18,10 +18,19 @@ 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>`). */
|
|
24
26
|
resume?: string;
|
|
27
|
+
/** Fork the resumed conversation into a new native Session. */
|
|
28
|
+
forkSession?: boolean;
|
|
29
|
+
/** Deterministic native UUID for a fork target. */
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
/** Session workspace roots beyond the primary process cwd. Claude accepts
|
|
32
|
+
* these as one variadic `--add-dir` flag on fresh and resumed launches. */
|
|
33
|
+
additionalDirs?: string[];
|
|
25
34
|
/** Extra claude args placed before the injected flags (rarely needed). */
|
|
26
35
|
extraArgs?: string[];
|
|
27
36
|
}
|
|
@@ -29,4 +38,4 @@ export interface ClaudeTuiArgs {
|
|
|
29
38
|
* Build the `claude` argv for an interactive, co-drivable TUI:
|
|
30
39
|
* `[..extra] [--resume <id>] [--model <m>] [--append-system-prompt <text>] --settings <json>`.
|
|
31
40
|
*/
|
|
32
|
-
export declare function buildClaudeTuiArgs({ settingsJson, settingSources, model, appendSystemPrompt, resume, extraArgs, }: ClaudeTuiArgs): string[];
|
|
41
|
+
export declare function buildClaudeTuiArgs({ settingsJson, settingSources, model, reasoningEffort, appendSystemPrompt, resume, forkSession, sessionId, additionalDirs, extraArgs, }: ClaudeTuiArgs): string[];
|
|
@@ -2,14 +2,22 @@
|
|
|
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, extraArgs = [], }) {
|
|
5
|
+
export function buildClaudeTuiArgs({ settingsJson, settingSources, model, reasoningEffort, appendSystemPrompt, resume, forkSession = false, sessionId, additionalDirs = [], extraArgs = [], }) {
|
|
6
6
|
const args = [...extraArgs];
|
|
7
|
+
if (additionalDirs.length > 0)
|
|
8
|
+
args.push("--add-dir", ...additionalDirs);
|
|
7
9
|
if (settingSources !== undefined)
|
|
8
10
|
args.push("--setting-sources", settingSources);
|
|
9
11
|
if (resume)
|
|
10
12
|
args.push("--resume", resume);
|
|
13
|
+
if (forkSession)
|
|
14
|
+
args.push("--fork-session");
|
|
15
|
+
if (sessionId)
|
|
16
|
+
args.push("--session-id", sessionId);
|
|
11
17
|
if (model)
|
|
12
18
|
args.push("--model", model);
|
|
19
|
+
if (reasoningEffort)
|
|
20
|
+
args.push("--effort", reasoningEffort);
|
|
13
21
|
if (appendSystemPrompt)
|
|
14
22
|
args.push("--append-system-prompt", appendSystemPrompt);
|
|
15
23
|
args.push("--settings", settingsJson);
|
|
@@ -29,12 +29,16 @@ export interface CodexRemoteArgs {
|
|
|
29
29
|
configOverrides?: string[];
|
|
30
30
|
/** Extra codex args that precede the attach flags (e.g. `["--model", "..."]`). */
|
|
31
31
|
codexArgs?: string[];
|
|
32
|
+
/** Session workspace roots beyond the primary process cwd. Replayed on fresh
|
|
33
|
+
* launch and resume so the remote TUI uses the same immutable workspace
|
|
34
|
+
* snapshot as the App Server clients. */
|
|
35
|
+
additionalDirs?: string[];
|
|
32
36
|
}
|
|
33
37
|
/**
|
|
34
38
|
* Build the `codex` argv tail for an app-server-backed TUI. Mirrors reference implementation's
|
|
35
39
|
* `build_codex_remote_args` exactly: overrides → codexArgs → (resume) → --remote.
|
|
36
40
|
*/
|
|
37
|
-
export declare function buildCodexRemoteArgs({ remoteUrl, threadId, configOverrides, codexArgs, }: CodexRemoteArgs): string[];
|
|
41
|
+
export declare function buildCodexRemoteArgs({ remoteUrl, threadId, configOverrides, codexArgs, additionalDirs, }: CodexRemoteArgs): string[];
|
|
38
42
|
export interface LaunchCodexTuiOptions extends CodexRemoteArgs {
|
|
39
43
|
registry: TerminalRegistry;
|
|
40
44
|
/** Terminal id (one codex TUI per session — `terminal_codex_main`). */
|
|
@@ -2,12 +2,21 @@
|
|
|
2
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
|
-
export function buildCodexRemoteArgs({ remoteUrl, threadId, configOverrides = [], codexArgs = [], }) {
|
|
5
|
+
export function buildCodexRemoteArgs({ remoteUrl, threadId, configOverrides = [], codexArgs = [], additionalDirs = [], }) {
|
|
6
6
|
const overrideArgs = configOverrides.flatMap((override) => ["-c", override]);
|
|
7
|
+
const additionalDirArgs = additionalDirs.flatMap((dir) => ["--add-dir", dir]);
|
|
7
8
|
if (threadId === undefined) {
|
|
8
|
-
return [...overrideArgs, ...codexArgs, "--remote", remoteUrl];
|
|
9
|
+
return [...overrideArgs, ...codexArgs, ...additionalDirArgs, "--remote", remoteUrl];
|
|
9
10
|
}
|
|
10
|
-
return [
|
|
11
|
+
return [
|
|
12
|
+
...overrideArgs,
|
|
13
|
+
...codexArgs,
|
|
14
|
+
...additionalDirArgs,
|
|
15
|
+
"resume",
|
|
16
|
+
"--remote",
|
|
17
|
+
remoteUrl,
|
|
18
|
+
threadId,
|
|
19
|
+
];
|
|
11
20
|
}
|
|
12
21
|
/**
|
|
13
22
|
* Create (idempotently) the codex TUI terminal in the registry, running the real
|
package/dist/terminal/tmux.d.ts
CHANGED
|
@@ -43,6 +43,14 @@ export interface TmuxTerminalOptions {
|
|
|
43
43
|
/** Injectable for tests; defaults to the real `tmux` binary path. */
|
|
44
44
|
tmuxBin?: string;
|
|
45
45
|
}
|
|
46
|
+
/** Deterministic private socket owned by one terminal name. Parent-side
|
|
47
|
+
* shutdown uses the same mapping when the runner child is too wedged to clean
|
|
48
|
+
* up its own tmux server. */
|
|
49
|
+
export declare function tmuxSocketPath(name: string): string;
|
|
50
|
+
/** Kill one private tmux server and verify it no longer answers. Missing
|
|
51
|
+
* sockets are already stopped; an existing socket with an unusable tmux
|
|
52
|
+
* command is unproven and returns false. */
|
|
53
|
+
export declare function terminateTmuxServer(name: string, tmuxBin?: string): boolean;
|
|
46
54
|
/** Is a usable `tmux` on PATH? Cheap probe for the capability gate. */
|
|
47
55
|
export declare function isTmuxAvailable(tmuxBin?: string): boolean;
|
|
48
56
|
export declare class TmuxTerminal {
|
|
@@ -138,7 +146,9 @@ export declare class TmuxTerminal {
|
|
|
138
146
|
paste(text: string, bufferName?: string): void;
|
|
139
147
|
/**
|
|
140
148
|
* Attach a client. `role: "read-only"` passes tmux `-r` so the viewer cannot
|
|
141
|
-
* type
|
|
149
|
+
* type and `ignore-size` so even its initial PTY dimensions cannot resize the
|
|
150
|
+
* owner's pane (defense-in-depth on top of the WS bridge dropping input and
|
|
151
|
+
* resize frames).
|
|
142
152
|
*/
|
|
143
153
|
attach(role: "owner" | "read-only", dims?: {
|
|
144
154
|
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,6 +26,58 @@ 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
|
+
/** Deterministic private socket owned by one terminal name. Parent-side
|
|
30
|
+
* shutdown uses the same mapping when the runner child is too wedged to clean
|
|
31
|
+
* up its own tmux server. */
|
|
32
|
+
export function tmuxSocketPath(name) {
|
|
33
|
+
const key = createHash("sha256").update(name).digest("hex").slice(0, 16);
|
|
34
|
+
return join(tmpdir(), "rynx-term", `${key}.sock`);
|
|
35
|
+
}
|
|
36
|
+
/** Kill one private tmux server and verify it no longer answers. Missing
|
|
37
|
+
* sockets are already stopped; an existing socket with an unusable tmux
|
|
38
|
+
* command is unproven and returns false. */
|
|
39
|
+
export function terminateTmuxServer(name, tmuxBin = "tmux") {
|
|
40
|
+
const socketPath = tmuxSocketPath(name);
|
|
41
|
+
if (!existsSync(socketPath))
|
|
42
|
+
return true;
|
|
43
|
+
const base = ["-u", "-S", socketPath];
|
|
44
|
+
try {
|
|
45
|
+
execFileSync(tmuxBin, [...base, "kill-server"], { stdio: "ignore" });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// A server may have exited between existsSync and kill-server. Verify below.
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
execFileSync(tmuxBin, [...base, "has-session", "-t", TMUX_TARGET], {
|
|
52
|
+
stdio: "ignore",
|
|
53
|
+
});
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
// Only a real tmux exit status proves the server/session is absent. Spawn
|
|
58
|
+
// failures (missing/unexecutable tmux) leave liveness unproven and must not
|
|
59
|
+
// unlink a socket that could still belong to a live server.
|
|
60
|
+
if (!error ||
|
|
61
|
+
typeof error !== "object" ||
|
|
62
|
+
!("status" in error) ||
|
|
63
|
+
typeof error.status !== "number") {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
// tmux custom-socket servers can exit while leaving the filesystem entry
|
|
68
|
+
// behind. Remove that verified-stale socket so process/socket inventories
|
|
69
|
+
// do not accumulate dead terminals forever.
|
|
70
|
+
unlinkSync(socketPath);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
catch (unlinkError) {
|
|
74
|
+
return Boolean(unlinkError &&
|
|
75
|
+
typeof unlinkError === "object" &&
|
|
76
|
+
"code" in unlinkError &&
|
|
77
|
+
unlinkError.code === "ENOENT");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
29
81
|
/** Is a usable `tmux` on PATH? Cheap probe for the capability gate. */
|
|
30
82
|
export function isTmuxAvailable(tmuxBin = "tmux") {
|
|
31
83
|
try {
|
|
@@ -76,8 +128,7 @@ export class TmuxTerminal {
|
|
|
76
128
|
// under the unix-domain path limit (~104 chars on macOS) — a full session id
|
|
77
129
|
// like `sess_<uuid>-main` under $TMPDIR would overflow it and fail with
|
|
78
130
|
// "File name too long". The hash stays unique per terminal.
|
|
79
|
-
|
|
80
|
-
this.socketPath = join(dir, `${key}.sock`);
|
|
131
|
+
this.socketPath = tmuxSocketPath(opts.name);
|
|
81
132
|
}
|
|
82
133
|
/** tmux argv prefix targeting this terminal's private server. */
|
|
83
134
|
base() {
|
|
@@ -286,14 +337,17 @@ export class TmuxTerminal {
|
|
|
286
337
|
}
|
|
287
338
|
/**
|
|
288
339
|
* Attach a client. `role: "read-only"` passes tmux `-r` so the viewer cannot
|
|
289
|
-
* type
|
|
340
|
+
* type and `ignore-size` so even its initial PTY dimensions cannot resize the
|
|
341
|
+
* owner's pane (defense-in-depth on top of the WS bridge dropping input and
|
|
342
|
+
* resize frames).
|
|
290
343
|
*/
|
|
291
344
|
async attach(role, dims) {
|
|
292
345
|
this.start();
|
|
293
346
|
const spawn = this.injectedSpawn ?? (await loadPtySpawn());
|
|
294
|
-
const args = [...this.base(), "attach-session"
|
|
347
|
+
const args = [...this.base(), "attach-session"];
|
|
295
348
|
if (role === "read-only")
|
|
296
|
-
args.push("-r");
|
|
349
|
+
args.push("-r", "-f", "ignore-size");
|
|
350
|
+
args.push("-t", TMUX_TARGET);
|
|
297
351
|
const proc = spawn(this.tmuxBin, args, {
|
|
298
352
|
name: "xterm-256color",
|
|
299
353
|
cols: dims?.cols ?? this.cols,
|
|
@@ -353,12 +407,7 @@ export class TmuxTerminal {
|
|
|
353
407
|
}
|
|
354
408
|
/** Kill the tmux server (ends the session and all attaches). */
|
|
355
409
|
kill() {
|
|
356
|
-
|
|
357
|
-
execFileSync(this.tmuxBin, [...this.base(), "kill-server"], { stdio: "ignore" });
|
|
358
|
-
}
|
|
359
|
-
catch {
|
|
360
|
-
// Already gone — nothing to clean up.
|
|
361
|
-
}
|
|
410
|
+
terminateTmuxServer(this.name, this.tmuxBin);
|
|
362
411
|
this.started = false;
|
|
363
412
|
}
|
|
364
413
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/runtime",
|
|
3
|
-
"version": "0.1.10",
|
|
3
|
+
"version": "0.1.11-beta.10",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"node-pty": "
|
|
27
|
+
"node-pty": "1.2.0-beta.15",
|
|
28
28
|
"ws": "^8.21.0",
|
|
29
|
-
"@rynx-ai/core": "0.1.10"
|
|
29
|
+
"@rynx-ai/core": "0.1.11-beta.10"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/ws": "^8.18.1"
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { type SessionItem } from "@rynx-ai/core";
|
|
2
|
-
import { type CodexLineageRuntime } from "../codex-home.js";
|
|
3
|
-
export interface SynthesizeRolloutOptions {
|
|
4
|
-
threadId: string;
|
|
5
|
-
cwd: string;
|
|
6
|
-
items: SessionItem[];
|
|
7
|
-
/** The rynx session (localThreadId) — resolves the per-session private CODEX_HOME
|
|
8
|
-
* (`codexHomePath(sessionId)`). Required unless `codexHome` is passed directly. */
|
|
9
|
-
sessionId?: string;
|
|
10
|
-
/** Override the private CODEX_HOME (tests, or a pre-resolved home). */
|
|
11
|
-
codexHome?: string;
|
|
12
|
-
/** Codex-lineage runtime whose private home/session layout is being written. */
|
|
13
|
-
runtime?: CodexLineageRuntime;
|
|
14
|
-
/** Runtime-neutral alias for `codexHome`; preferred for Traex callers. */
|
|
15
|
-
runtimeHome?: string;
|
|
16
|
-
/** codex CLI version for `session_meta` (informational for ≥0.133; presence
|
|
17
|
-
* matters). */
|
|
18
|
-
cliVersion?: string;
|
|
19
|
-
/** `session_meta.model_provider` — an empty/unresolvable value silently drops the
|
|
20
|
-
* carried history on resume. Defaults to `openai`. */
|
|
21
|
-
modelProvider?: string;
|
|
22
|
-
/** Clock injection (tests). */
|
|
23
|
-
now?: () => number;
|
|
24
|
-
}
|
|
25
|
-
interface RolloutRecord {
|
|
26
|
-
timestamp: string;
|
|
27
|
-
type: "session_meta" | "turn_context" | "response_item" | "event_msg";
|
|
28
|
-
payload: Record<string, unknown>;
|
|
29
|
-
}
|
|
30
|
-
/** Locate an existing rollout for `threadId` under the runtime's sessions root. */
|
|
31
|
-
export declare function findCodexRollout(runtimeHome: string, threadId: string, runtime?: CodexLineageRuntime): string | null;
|
|
32
|
-
/** Build the ordered rollout records (session_meta first, then per-turn
|
|
33
|
-
* turn_context + per-item response_item + per-message event_msg). */
|
|
34
|
-
export declare function buildRolloutRecords(opts: SynthesizeRolloutOptions): RolloutRecord[];
|
|
35
|
-
/**
|
|
36
|
-
* Ensure a resumable codex rollout exists for `threadId`. No-op if one is already
|
|
37
|
-
* present (codex owns its own runtime rollouts). Otherwise synthesize one from the
|
|
38
|
-
* session items and write it atomically. Returns `"exists"`, `"written"`, or
|
|
39
|
-
* `"skipped"` (invalid thread id / nothing to carry). Best-effort: never throws.
|
|
40
|
-
*/
|
|
41
|
-
export declare function ensureCodexResumeRollout(opts: SynthesizeRolloutOptions): "exists" | "written" | "skipped";
|
|
42
|
-
export {};
|