@rynx-ai/runtime 0.1.0 → 0.1.10-beta.2

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.
Files changed (56) hide show
  1. package/dist/claude/executor.d.ts +3 -5
  2. package/dist/claude/executor.js +3 -5
  3. package/dist/claude/native-bridge.d.ts +74 -17
  4. package/dist/claude/native-bridge.js +225 -30
  5. package/dist/claude/native-hook-main.js +327 -38
  6. package/dist/claude/native-hooks.d.ts +3 -2
  7. package/dist/claude/native-hooks.js +15 -6
  8. package/dist/claude/native-integration.d.ts +123 -16
  9. package/dist/claude/native-integration.js +624 -81
  10. package/dist/claude/settings.d.ts +8 -0
  11. package/dist/claude/settings.js +50 -0
  12. package/dist/claude/transcript.d.ts +2 -2
  13. package/dist/claude/transcript.js +14 -3
  14. package/dist/codex/rollout-synth.d.ts +8 -3
  15. package/dist/codex/rollout-synth.js +65 -32
  16. package/dist/codex-app-server/client.d.ts +27 -40
  17. package/dist/codex-app-server/client.js +1134 -99
  18. package/dist/codex-app-server/forwarder.d.ts +36 -10
  19. package/dist/codex-app-server/forwarder.js +146 -28
  20. package/dist/codex-app-server/mapping.d.ts +1 -1
  21. package/dist/codex-app-server/mapping.js +64 -5
  22. package/dist/codex-app-server/protocol.d.ts +269 -4
  23. package/dist/codex-app-server/transport.d.ts +20 -5
  24. package/dist/codex-app-server/transport.js +93 -40
  25. package/dist/codex-app-server/ws-channel.d.ts +3 -3
  26. package/dist/codex-app-server/ws-channel.js +23 -7
  27. package/dist/codex-child-env.js +33 -0
  28. package/dist/codex-home.d.ts +16 -6
  29. package/dist/codex-home.js +46 -15
  30. package/dist/codex-session-store.d.ts +2 -1
  31. package/dist/host.d.ts +38 -38
  32. package/dist/host.js +626 -121
  33. package/dist/index.d.ts +4 -3
  34. package/dist/index.js +1 -1
  35. package/dist/input-resources.d.ts +13 -0
  36. package/dist/input-resources.js +67 -0
  37. package/dist/interactions.d.ts +61 -0
  38. package/dist/interactions.js +236 -0
  39. package/dist/models-catalog.d.ts +5 -13
  40. package/dist/models-catalog.js +60 -9
  41. package/dist/runner/child.d.ts +9 -1
  42. package/dist/runner/child.js +100 -19
  43. package/dist/runner/manager.d.ts +79 -11
  44. package/dist/runner/manager.js +423 -43
  45. package/dist/runner/protocol.d.ts +30 -11
  46. package/dist/runner-main.js +9 -6
  47. package/dist/runtime-status.js +1 -1
  48. package/dist/terminal/claude-tui.d.ts +8 -3
  49. package/dist/terminal/claude-tui.js +6 -2
  50. package/dist/terminal/codex-tui.d.ts +3 -3
  51. package/dist/terminal/codex-tui.js +1 -1
  52. package/dist/terminal/registry.d.ts +1 -1
  53. package/dist/terminal/registry.js +1 -1
  54. package/dist/terminal/tmux.d.ts +6 -6
  55. package/dist/terminal/tmux.js +10 -10
  56. package/package.json +8 -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 RuntimeUserInput, 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
- /** A codex approval decision the user returns for an interactive approval. */
22
- export type ApprovalDecisionWire = "acceptForSession" | "accept" | "decline" | "cancel";
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 omnigent's "inject failure ⇒ response.failed, never re-run locally". */
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: "approval.resolve";
65
+ t: "interaction.resolve";
66
+ reqId: string;
64
67
  localThreadId: string;
65
- approvalId: string;
66
- decision: ApprovalDecisionWire;
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. */
@@ -87,16 +92,24 @@ export type ToChild = {
87
92
  /** An inline agent spec (console session created from an inline config
88
93
  * rather than a preset id) — same purpose as `agentName`. */
89
94
  agentSpec?: AgentSpec;
95
+ /** Setup terminals only need the Provider pane to exist. Message delivery
96
+ * keeps the default and waits until the native thread is actually bound. */
97
+ waitForReady?: boolean;
90
98
  }
91
99
  /** Inject a user turn into the session's live codex thread (`turn/start` or
92
100
  * `turn/steer`). The forwarder mirrors the output — this is the single-writer
93
101
  * web send path. The child replies `injected` (echoing `reqId`). */
94
- | {
102
+ | ({
95
103
  t: "inject";
96
104
  reqId: string;
97
105
  localThreadId: string;
106
+ } & ({
107
+ input: RuntimeUserInput;
108
+ text?: never;
109
+ } | {
98
110
  text: string;
99
- }
111
+ input?: never;
112
+ }))
100
113
  /** Interrupt the session's active turn — the web Stop button. codex: app-server
101
114
  * `turn/interrupt`; claude: an Escape keystroke to the pane. The child replies
102
115
  * `interrupted` (echoing `reqId`). */
@@ -135,6 +148,7 @@ export type FromChild = {
135
148
  } | {
136
149
  t: "term.error";
137
150
  attachId: string;
151
+ code: TerminalOpenErrorCode;
138
152
  message: string;
139
153
  }
140
154
  /** A canonical {@link SessionEvent} mirrored by the session's persistent codex
@@ -157,8 +171,8 @@ export type FromChild = {
157
171
  cwd?: string;
158
172
  parentSessionId?: string;
159
173
  }
160
- /** Result of a `live.ensure`: `ok` once the codex thread is bound (or a no-op
161
- * for a non-codex / non-live session). Echoes the request `reqId`. */
174
+ /** Result of a `live.ensure`: `ok` once the requested gate is reached (pane
175
+ * started for setup, otherwise native thread bound). Echoes `reqId`. */
162
176
  | {
163
177
  t: "live.ready";
164
178
  reqId: string;
@@ -184,6 +198,11 @@ export type FromChild = {
184
198
  localThreadId: string;
185
199
  ok: boolean;
186
200
  error?: string;
201
+ } | {
202
+ t: "interaction.resolved";
203
+ reqId: string;
204
+ localThreadId: string;
205
+ result: ResolveInteractionResult;
187
206
  };
188
207
  /** Encode a message as a single NDJSON line (newline included). */
189
208
  export declare function encodeMessage(msg: ToChild | FromChild): string;
@@ -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
- transport.onClose(() => process.exit(0));
48
- // eslint-disable-next-line no-new -- the session wires itself to the transport.
49
- new RunnerSession({
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) ends the process; the OS tears down
58
- // the child app-server / PTY with it.
59
- process.on("SIGTERM", () => process.exit(0));
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();
@@ -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 omnigent's model,
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
- /** The claude `--settings` JSON (hook registration). */
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 omnigent's verified
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 omnigent's working implementation
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 omnigent's
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 omnigent's
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 omnigent's terminal attach (`terminal_attach.py`: `read_only`
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 omnigent's terminal attach (`terminal_attach.py`: `read_only`
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
@@ -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 omnigent's tmux option suite to the private server (inner/terminal.py).
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. omnigent keeps the status line only to show
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 omnigent's `_terminal.is_alive`. */
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"). omnigent's `_tmux_session_alive` uses an async
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 omnigent's `inject_interrupt`. */
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 (omnigent). */
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;
@@ -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 omnigent's `TerminalEnvSpec.scrollback`. */
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. omnigent inherits a UTF-8 locale from its launch
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 omnigent's tmux option suite to the private server (inner/terminal.py).
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. omnigent keeps the status line only to show
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 omnigent)
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 omnigent's `_terminal.is_alive`. */
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"). omnigent's `_tmux_session_alive` uses an async
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 omnigent's `inject_interrupt`. */
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 (omnigent). */
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
- // (omnigent: the server closes the socket on pane death → StatusOverlay).
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,11 @@
1
1
  {
2
2
  "name": "@rynx-ai/runtime",
3
- "version": "0.1.0",
3
+ "version": "0.1.10-beta.2",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/rynx-ai/rynx.git",
7
+ "directory": "packages/runtime"
8
+ },
4
9
  "description": "Runtime host that drives local coding-agent CLIs (codex / traex / claude) behind one AgentExecutor + AgentCapabilities contract.",
5
10
  "type": "module",
6
11
  "publishConfig": {
@@ -21,12 +26,12 @@
21
26
  "dependencies": {
22
27
  "node-pty": "^1.0.0",
23
28
  "ws": "^8.21.0",
24
- "@rynx-ai/core": "0.1.0"
29
+ "@rynx-ai/core": "0.1.10-beta.2"
25
30
  },
26
31
  "devDependencies": {
27
32
  "@types/ws": "^8.18.1"
28
33
  },
29
34
  "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"
35
+ "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
36
  }
32
37
  }