@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.
Files changed (40) hide show
  1. package/dist/claude/models.d.ts +0 -5
  2. package/dist/claude/models.js +1 -7
  3. package/dist/claude/native-bridge.js +3 -8
  4. package/dist/claude/native-integration.d.ts +12 -1
  5. package/dist/claude/native-integration.js +16 -2
  6. package/dist/claude/transcript.d.ts +0 -7
  7. package/dist/claude/transcript.js +6 -20
  8. package/dist/codex-app-server/client.d.ts +2 -1
  9. package/dist/codex-app-server/forwarder.d.ts +4 -1
  10. package/dist/codex-app-server/forwarder.js +19 -1
  11. package/dist/codex-app-server/protocol.d.ts +45 -1
  12. package/dist/codex-home.d.ts +9 -26
  13. package/dist/codex-home.js +37 -65
  14. package/dist/codex-session-store.d.ts +22 -10
  15. package/dist/codex-session-store.js +277 -12
  16. package/dist/host.d.ts +48 -47
  17. package/dist/host.js +810 -355
  18. package/dist/index.d.ts +2 -3
  19. package/dist/index.js +1 -2
  20. package/dist/models-catalog.d.ts +3 -1
  21. package/dist/models-catalog.js +125 -4
  22. package/dist/provider-workspace.d.ts +56 -0
  23. package/dist/provider-workspace.js +83 -0
  24. package/dist/runner/child.d.ts +59 -6
  25. package/dist/runner/child.js +138 -19
  26. package/dist/runner/manager.d.ts +104 -19
  27. package/dist/runner/manager.js +922 -89
  28. package/dist/runner/protocol.d.ts +7 -18
  29. package/dist/runner-main.js +12 -4
  30. package/dist/runtime-state-paths.d.ts +10 -0
  31. package/dist/runtime-state-paths.js +53 -0
  32. package/dist/terminal/claude-tui.d.ts +10 -1
  33. package/dist/terminal/claude-tui.js +9 -1
  34. package/dist/terminal/codex-tui.d.ts +5 -1
  35. package/dist/terminal/codex-tui.js +12 -3
  36. package/dist/terminal/tmux.d.ts +11 -1
  37. package/dist/terminal/tmux.js +61 -12
  38. package/package.json +3 -3
  39. package/dist/codex/rollout-synth.d.ts +0 -42
  40. package/dist/codex/rollout-synth.js +0 -245
@@ -1,245 +0,0 @@
1
- /**
2
- * Synthesize a codex rollout file from rynx's canonical session log, so
3
- * `codex --remote resume <threadId>` works when the local rollout is missing
4
- * (fork / worktree / cross-machine). Ports reference implementation's
5
- * `_ensure_local_codex_resume_rollout`, adapted to rynx: the daemon (control-api)
6
- * has BOTH the session items (`SessionLogStore`) and — since the private CODEX_HOME
7
- * is a deterministic uid-scoped path — the app-server's rollout dir, so this runs
8
- * entirely daemon-side with no runner round-trip.
9
- *
10
- * The record shapes match codex 0.142.5 (empirically captured). The minimal set
11
- * codex needs to resume with VISIBLE turns is: a `session_meta` first line, then
12
- * per item a `response_item`, and — critically — an `event_msg` mirror per message
13
- * (codex ≥0.136 renders an empty thread without them). `turn_context` groups items
14
- * into turns.
15
- */
16
- import { createHash } from "node:crypto";
17
- import { copyFileSync, mkdirSync, readdirSync, renameSync, writeFileSync } from "node:fs";
18
- import { join, relative } from "node:path";
19
- import { getRuntimeProfile, resolveRuntimeHome, rynxHome, } from "@rynx-ai/core";
20
- import { legacyCodexHomePath, runtimeHomePath, } from "../codex-home.js";
21
- /** codex validates the thread id straight into a filename + resume arg. */
22
- const THREAD_ID_RE = /^[0-9a-fA-F-]+$/;
23
- function sessionsRoot(runtimeHome, runtime) {
24
- return join(runtimeHome, ...getRuntimeProfile(runtime).sessionsSubpath);
25
- }
26
- /** Locate an existing rollout for `threadId` under the runtime's sessions root. */
27
- export function findCodexRollout(runtimeHome, threadId, runtime = "codex") {
28
- const root = sessionsRoot(runtimeHome, runtime);
29
- const suffix = `-${threadId}.jsonl`;
30
- const stack = [root];
31
- while (stack.length) {
32
- const dir = stack.pop();
33
- let entries;
34
- try {
35
- entries = readdirSync(dir, { withFileTypes: true });
36
- }
37
- catch {
38
- continue; // missing dir — fine
39
- }
40
- for (const e of entries) {
41
- const full = join(dir, e.name);
42
- if (e.isDirectory())
43
- stack.push(full);
44
- else if (e.name.startsWith("rollout-") && e.name.endsWith(suffix))
45
- return full;
46
- }
47
- }
48
- return null;
49
- }
50
- /**
51
- * Back-compat: before per-session homes, rollouts lived under one shared
52
- * {@link legacyCodexHomePath}. If a session's rollout is only there, copy it forward
53
- * into this session's home (preserving the `sessions/YYYY/MM/DD/` layout codex
54
- * expects) so the per-session app-server can resume it. Best-effort; gated by
55
- * `RYNX_CODEX_HOME_LEGACY_FALLBACK` (default on; set `0`/`false` to disable).
56
- */
57
- function adoptLegacyRollout(runtimeHome, threadId, runtime) {
58
- const flag = (process.env.RYNX_RUNTIME_HOME_LEGACY_FALLBACK
59
- ?? process.env.RYNX_CODEX_HOME_LEGACY_FALLBACK)?.trim().toLowerCase();
60
- if (flag === "0" || flag === "false")
61
- return false;
62
- const legacy = runtime === "codex"
63
- ? legacyCodexHomePath()
64
- : resolveRuntimeHome(getRuntimeProfile(runtime));
65
- if (legacy === runtimeHome)
66
- return false;
67
- const src = findCodexRollout(legacy, threadId, runtime);
68
- if (!src)
69
- return false;
70
- const rel = relative(sessionsRoot(legacy, runtime), src);
71
- const dst = join(sessionsRoot(runtimeHome, runtime), rel);
72
- try {
73
- mkdirSync(join(dst, ".."), { recursive: true, mode: 0o700 });
74
- copyFileSync(src, dst);
75
- return true;
76
- }
77
- catch {
78
- return false;
79
- }
80
- }
81
- function iso(ms) {
82
- return new Date(ms).toISOString();
83
- }
84
- /** `resp_codex_<turnId>` → `<turnId>` (rynx derives the responseId from the codex
85
- * turn id); anything else is used verbatim as a stable turn key. */
86
- function turnIdOf(responseId) {
87
- return responseId.startsWith("resp_codex_") ? responseId.slice("resp_codex_".length) : responseId;
88
- }
89
- function textOf(item) {
90
- return item.data.content
91
- .filter((part) => "text" in part)
92
- .map((part) => part.text)
93
- .join("");
94
- }
95
- /** Convert one canonical {@link SessionItem} to its codex `response_item` payload,
96
- * or null for types codex doesn't carry (reasoning/terminal_command/error). */
97
- function responseItemPayload(item) {
98
- switch (item.type) {
99
- case "message": {
100
- const apiType = item.data.role === "assistant" ? "output_text" : "input_text";
101
- const content = item.data.content.flatMap((part) => "text" in part && part.text
102
- ? [{ type: apiType, text: part.text }]
103
- : []);
104
- if (content.length === 0)
105
- return null;
106
- return { type: "message", role: item.data.role, content };
107
- }
108
- case "function_call":
109
- return {
110
- type: "function_call",
111
- name: item.data.name,
112
- call_id: item.data.callId,
113
- arguments: item.data.arguments,
114
- };
115
- case "function_call_output":
116
- return { type: "function_call_output", call_id: item.data.callId, output: item.data.output };
117
- default:
118
- return null;
119
- }
120
- }
121
- /** The `event_msg` mirror for a message (required for a VISIBLE turn on codex ≥0.136). */
122
- function eventMsgPayload(item, sessionId) {
123
- const message = textOf(item).trim();
124
- const localImages = item.data.role === "user" && sessionId
125
- ? item.data.content.flatMap((part) => part.type === "input_image" ? [resourcePath(sessionId, part)] : [])
126
- : [];
127
- if (!message && localImages.length === 0)
128
- return null;
129
- if (item.data.role === "user") {
130
- return {
131
- type: "user_message",
132
- message,
133
- images: [],
134
- local_images: localImages,
135
- text_elements: [],
136
- };
137
- }
138
- if (item.data.role === "assistant") {
139
- return { type: "agent_message", message, phase: "final_answer", memory_citation: null };
140
- }
141
- return null;
142
- }
143
- function resourcePath(sessionId, part) {
144
- const sessionKey = createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
145
- const extension = part.mediaType === "image/png"
146
- ? "png"
147
- : part.mediaType === "image/jpeg"
148
- ? "jpg"
149
- : "webp";
150
- return join(rynxHome(), "resources", sessionKey, `${part.resourceId}.${extension}`);
151
- }
152
- /** Build the ordered rollout records (session_meta first, then per-turn
153
- * turn_context + per-item response_item + per-message event_msg). */
154
- export function buildRolloutRecords(opts) {
155
- const now = opts.now ?? (() => Date.now());
156
- const modelProvider = opts.modelProvider ?? (opts.runtime === "traex" ? "trae" : "openai");
157
- const cliVersion = opts.cliVersion ?? "0.0.0";
158
- const metaTs = iso(opts.items[0]?.createdAt ?? now());
159
- const records = [
160
- {
161
- timestamp: metaTs,
162
- type: "session_meta",
163
- payload: {
164
- id: opts.threadId,
165
- session_id: opts.threadId,
166
- timestamp: metaTs,
167
- cwd: opts.cwd,
168
- originator: "rynx",
169
- cli_version: cliVersion,
170
- source: "rynx",
171
- thread_source: "user",
172
- model_provider: modelProvider,
173
- },
174
- },
175
- ];
176
- const seenTurns = new Set();
177
- for (const item of opts.items) {
178
- const payload = responseItemPayload(item);
179
- const eventPayload = item.type === "message"
180
- ? eventMsgPayload(item, opts.sessionId)
181
- : null;
182
- if (!payload && !eventPayload)
183
- continue;
184
- const ts = iso(item.createdAt);
185
- const turnId = turnIdOf(item.responseId);
186
- if (!seenTurns.has(turnId)) {
187
- seenTurns.add(turnId);
188
- records.push({
189
- timestamp: ts,
190
- type: "turn_context",
191
- payload: { turn_id: turnId, cwd: opts.cwd, approval_policy: "on-request" },
192
- });
193
- }
194
- if (payload)
195
- records.push({ timestamp: ts, type: "response_item", payload });
196
- if (eventPayload)
197
- records.push({ timestamp: ts, type: "event_msg", payload: eventPayload });
198
- }
199
- return records;
200
- }
201
- /**
202
- * Ensure a resumable codex rollout exists for `threadId`. No-op if one is already
203
- * present (codex owns its own runtime rollouts). Otherwise synthesize one from the
204
- * session items and write it atomically. Returns `"exists"`, `"written"`, or
205
- * `"skipped"` (invalid thread id / nothing to carry). Best-effort: never throws.
206
- */
207
- export function ensureCodexResumeRollout(opts) {
208
- if (!THREAD_ID_RE.test(opts.threadId))
209
- return "skipped";
210
- const runtime = opts.runtime ?? "codex";
211
- const runtimeHome = opts.runtimeHome
212
- ?? opts.codexHome
213
- ?? (opts.sessionId ? runtimeHomePath(opts.sessionId, runtime) : undefined);
214
- if (!runtimeHome)
215
- return "skipped"; // no session context → can't locate the home
216
- if (findCodexRollout(runtimeHome, opts.threadId, runtime))
217
- return "exists";
218
- // Back-compat: a pre-per-session rollout may live under the OLD shared home. Copy
219
- // it forward into this session's home so the per-session app-server can resume it.
220
- if (adoptLegacyRollout(runtimeHome, opts.threadId, runtime))
221
- return "exists";
222
- const records = buildRolloutRecords(opts);
223
- // Nothing but the session_meta header → no history to carry; skip (a fresh
224
- // thread resumes fine without a rollout once it has run a turn).
225
- if (records.length <= 1)
226
- return "skipped";
227
- const now = opts.now ?? (() => Date.now());
228
- const d = new Date(now());
229
- const yyyy = String(d.getUTCFullYear());
230
- const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
231
- const dd = String(d.getUTCDate()).padStart(2, "0");
232
- const stamp = iso(d.getTime()).slice(0, 19).replace(/:/g, "-"); // YYYY-MM-DDTHH-MM-SS
233
- const dir = join(sessionsRoot(runtimeHome, runtime), yyyy, mm, dd);
234
- const target = join(dir, `rollout-${stamp}-${opts.threadId}.jsonl`);
235
- const tmp = `${target}.tmp`;
236
- try {
237
- mkdirSync(dir, { recursive: true, mode: 0o700 });
238
- writeFileSync(tmp, records.map((r) => JSON.stringify(r)).join("\n") + "\n");
239
- renameSync(tmp, target);
240
- return "written";
241
- }
242
- catch {
243
- return "skipped"; // best-effort: a failed synth just leaves resume to fail as before
244
- }
245
- }