@rynx-ai/runtime 0.1.11-beta.4 → 0.1.11-beta.6
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/host.js +3 -0
- package/dist/models-catalog.d.ts +2 -1
- package/dist/models-catalog.js +82 -3
- package/dist/runner/child.js +45 -26
- package/dist/terminal/claude-tui.d.ts +3 -1
- package/dist/terminal/claude-tui.js +3 -1
- package/dist/terminal/tmux.d.ts +3 -1
- package/dist/terminal/tmux.js +6 -3
- package/package.json +2 -2
package/dist/host.js
CHANGED
|
@@ -1536,6 +1536,9 @@ export class LocalAgentHost {
|
|
|
1536
1536
|
// Explicit rynx hooks and selected-skill plugin dirs remain enabled.
|
|
1537
1537
|
settingSources: "",
|
|
1538
1538
|
model: live.execution.model,
|
|
1539
|
+
...(live.execution.reasoningEffort
|
|
1540
|
+
? { reasoningEffort: live.execution.reasoningEffort }
|
|
1541
|
+
: {}),
|
|
1539
1542
|
...(live.execution.instructions
|
|
1540
1543
|
? { appendSystemPrompt: live.execution.instructions }
|
|
1541
1544
|
: {}),
|
package/dist/models-catalog.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type AgentRuntimeId, type AppConfig } from "@rynx-ai/core";
|
|
2
2
|
import type { ModelListResponse } from "./codex-app-server/protocol.js";
|
|
3
3
|
export interface RuntimeModelCatalogDeps {
|
|
4
|
+
readCodexModels?: () => Promise<unknown>;
|
|
4
5
|
readTraexModels?: () => Promise<unknown>;
|
|
5
6
|
readTraexDebugModels?: () => Promise<unknown>;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* The model list for a runtime, without an execution backend.
|
|
9
|
-
* Falls back to the configured model if
|
|
10
|
+
* Falls back to the configured model if local/native discovery is unavailable.
|
|
10
11
|
*/
|
|
11
12
|
export declare function listRuntimeModels(config: AppConfig, runtime: AgentRuntimeId, deps?: RuntimeModelCatalogDeps): Promise<ModelListResponse | null>;
|
package/dist/models-catalog.js
CHANGED
|
@@ -3,24 +3,37 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The parent control plane no longer holds an app-server, so `/models` can't be
|
|
5
5
|
* a live `model/list` RPC anymore. Following reference implementation's static-catalog model, we
|
|
6
|
-
* serve
|
|
6
|
+
* serve Codex's local model cache and Traex's native `models --json` catalog.
|
|
7
7
|
* claude already has its own static list ({@link listClaudeModels}).
|
|
8
8
|
*/
|
|
9
9
|
import { execFile } from "node:child_process";
|
|
10
|
+
import { readFile } from "node:fs/promises";
|
|
11
|
+
import { join } from "node:path";
|
|
10
12
|
import { promisify } from "node:util";
|
|
11
|
-
import { resolveRuntimeBinary, resolveRuntimeModel, } from "@rynx-ai/core";
|
|
13
|
+
import { getRuntimeProfile, resolveRuntimeBinary, resolveRuntimeHome, resolveRuntimeModel, } from "@rynx-ai/core";
|
|
12
14
|
import { listClaudeModels } from "./claude/models.js";
|
|
13
15
|
const execFileAsync = promisify(execFile);
|
|
14
16
|
const TRAEX_MODELS_TIMEOUT_MS = 8_000;
|
|
15
17
|
const TRAEX_MODELS_MAX_BYTES = 2 * 1024 * 1024;
|
|
16
18
|
/**
|
|
17
19
|
* The model list for a runtime, without an execution backend.
|
|
18
|
-
* Falls back to the configured model if
|
|
20
|
+
* Falls back to the configured model if local/native discovery is unavailable.
|
|
19
21
|
*/
|
|
20
22
|
export async function listRuntimeModels(config, runtime, deps = {}) {
|
|
21
23
|
if (runtime === "claude") {
|
|
22
24
|
return listClaudeModels();
|
|
23
25
|
}
|
|
26
|
+
if (runtime === "codex") {
|
|
27
|
+
try {
|
|
28
|
+
const configuredDefault = resolveRuntimeModel(config, runtime).trim();
|
|
29
|
+
const models = normalizeCodexModels(await (deps.readCodexModels ?? readCodexModels)(), configuredDefault);
|
|
30
|
+
if (models.length > 0)
|
|
31
|
+
return { data: models };
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Keep the configured fallback usable before Codex has populated its cache.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
24
37
|
if (runtime === "traex") {
|
|
25
38
|
try {
|
|
26
39
|
const value = await (deps.readTraexModels ?? readTraexModels)();
|
|
@@ -48,6 +61,72 @@ export async function listRuntimeModels(config, runtime, deps = {}) {
|
|
|
48
61
|
}
|
|
49
62
|
return { data: [{ id: model, model, isDefault: true }] };
|
|
50
63
|
}
|
|
64
|
+
async function readCodexModels() {
|
|
65
|
+
const cachePath = join(resolveRuntimeHome(getRuntimeProfile("codex")), "models_cache.json");
|
|
66
|
+
return JSON.parse(await readFile(cachePath, "utf8"));
|
|
67
|
+
}
|
|
68
|
+
function normalizeCodexModels(value, configuredDefault) {
|
|
69
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
70
|
+
return [];
|
|
71
|
+
const entries = value.models;
|
|
72
|
+
if (!Array.isArray(entries))
|
|
73
|
+
return [];
|
|
74
|
+
const models = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const item of entries) {
|
|
77
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
78
|
+
continue;
|
|
79
|
+
const record = item;
|
|
80
|
+
const id = typeof record.slug === "string" ? record.slug.trim() : "";
|
|
81
|
+
if (!id || seen.has(id) || record.visibility === "hide")
|
|
82
|
+
continue;
|
|
83
|
+
seen.add(id);
|
|
84
|
+
const supportedReasoningEfforts = Array.isArray(record.supported_reasoning_levels)
|
|
85
|
+
? record.supported_reasoning_levels.flatMap((raw) => {
|
|
86
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
87
|
+
return [];
|
|
88
|
+
const effort = typeof raw.effort === "string"
|
|
89
|
+
? raw.effort.trim()
|
|
90
|
+
: "";
|
|
91
|
+
if (!effort)
|
|
92
|
+
return [];
|
|
93
|
+
const description = raw.description;
|
|
94
|
+
return [{
|
|
95
|
+
reasoningEffort: effort,
|
|
96
|
+
...(typeof description === "string" && description.trim()
|
|
97
|
+
? { description: description.trim() }
|
|
98
|
+
: {}),
|
|
99
|
+
}];
|
|
100
|
+
})
|
|
101
|
+
: [];
|
|
102
|
+
const displayName = typeof record.display_name === "string"
|
|
103
|
+
? record.display_name.trim()
|
|
104
|
+
: "";
|
|
105
|
+
const description = typeof record.description === "string"
|
|
106
|
+
? record.description.trim()
|
|
107
|
+
: "";
|
|
108
|
+
const defaultReasoningEffort = typeof record.default_reasoning_level === "string"
|
|
109
|
+
? record.default_reasoning_level.trim()
|
|
110
|
+
: "";
|
|
111
|
+
models.push({
|
|
112
|
+
id,
|
|
113
|
+
model: id,
|
|
114
|
+
...(displayName ? { displayName } : {}),
|
|
115
|
+
...(description ? { description } : {}),
|
|
116
|
+
isDefault: id === configuredDefault,
|
|
117
|
+
...(supportedReasoningEfforts.length > 0 ? { supportedReasoningEfforts } : {}),
|
|
118
|
+
...(defaultReasoningEffort ? { defaultReasoningEffort } : {}),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (configuredDefault && !seen.has(configuredDefault)) {
|
|
122
|
+
models.unshift({
|
|
123
|
+
id: configuredDefault,
|
|
124
|
+
model: configuredDefault,
|
|
125
|
+
isDefault: true,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return models;
|
|
129
|
+
}
|
|
51
130
|
async function readTraexModels() {
|
|
52
131
|
const { stdout } = await execFileAsync(resolveRuntimeBinary("traex"), ["models", "--json"], {
|
|
53
132
|
encoding: "utf8",
|
package/dist/runner/child.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { TerminalRegistry } from "../terminal/registry.js";
|
|
2
2
|
import { toWireError } from "./protocol.js";
|
|
3
|
+
const TRAEX_STARTUP_WATCH_MS = 20_000;
|
|
4
|
+
const TRAEX_STARTUP_POLL_MS = 100;
|
|
5
|
+
const TRAEX_PROMPT_RETRY_MS = 500;
|
|
6
|
+
function normalizeTraexPane(pane) {
|
|
7
|
+
return pane.toLowerCase().replace(/\s+/g, " ").trim();
|
|
8
|
+
}
|
|
9
|
+
function isTerminalProtocolResponse(input) {
|
|
10
|
+
return /^(?:\x1b\[(?:(?:\?|>)[0-9;]*c|[0-9;]*(?:n|R|t)|[IO]))+$/.test(input);
|
|
11
|
+
}
|
|
3
12
|
export class RunnerSession {
|
|
4
13
|
transport;
|
|
5
14
|
executor;
|
|
@@ -48,8 +57,17 @@ export class RunnerSession {
|
|
|
48
57
|
});
|
|
49
58
|
return;
|
|
50
59
|
case "term.input":
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
{
|
|
61
|
+
const localThreadId = this.attachmentThreadIds.get(msg.attachId);
|
|
62
|
+
const input = Buffer.from(msg.dataB64, "base64").toString("utf8");
|
|
63
|
+
// xterm sends device/focus reports through the same onData channel as
|
|
64
|
+
// keystrokes. They must reach the TUI without pretending the user has
|
|
65
|
+
// taken over startup prompt handling.
|
|
66
|
+
if (!isTerminalProtocolResponse(input)) {
|
|
67
|
+
this.cancelTraexStartupWatcher(localThreadId);
|
|
68
|
+
}
|
|
69
|
+
this.attachments.get(msg.attachId)?.write(input);
|
|
70
|
+
}
|
|
53
71
|
return;
|
|
54
72
|
case "term.resize":
|
|
55
73
|
this.attachments.get(msg.attachId)?.resize(msg.cols, msg.rows);
|
|
@@ -215,8 +233,8 @@ export class RunnerSession {
|
|
|
215
233
|
try {
|
|
216
234
|
const input = msg.input ?? msg.text;
|
|
217
235
|
const outcome = (await provider.injectMessage?.(msg.localThreadId, input)) ?? "notLive";
|
|
218
|
-
|
|
219
|
-
|
|
236
|
+
// App-server injection is independent of the Terminal TUI startup, so it
|
|
237
|
+
// must not cancel prompt handling for the pane that is still starting.
|
|
220
238
|
this.transport.send({ t: "injected", reqId: msg.reqId, localThreadId: msg.localThreadId, outcome });
|
|
221
239
|
}
|
|
222
240
|
catch (error) {
|
|
@@ -255,7 +273,6 @@ export class RunnerSession {
|
|
|
255
273
|
if (!spec)
|
|
256
274
|
return;
|
|
257
275
|
const terminalId = `${localThreadId}-main`;
|
|
258
|
-
const coldStart = !this.terminals.has(terminalId);
|
|
259
276
|
const term = this.terminals.getOrCreate(terminalId, {
|
|
260
277
|
cwd: spec.cwd,
|
|
261
278
|
command: spec.command,
|
|
@@ -264,7 +281,7 @@ export class RunnerSession {
|
|
|
264
281
|
rows: rows ?? 40,
|
|
265
282
|
...(spec.env ? { env: spec.env } : {}),
|
|
266
283
|
});
|
|
267
|
-
if (
|
|
284
|
+
if (spec.skipTraexStartupPrompts) {
|
|
268
285
|
const watcher = Symbol(localThreadId);
|
|
269
286
|
this.traexStartupWatchers.set(localThreadId, watcher);
|
|
270
287
|
void this.skipTraexStartupPrompts(localThreadId, term, watcher);
|
|
@@ -279,46 +296,48 @@ export class RunnerSession {
|
|
|
279
296
|
const prompts = [
|
|
280
297
|
{
|
|
281
298
|
id: "welcome",
|
|
282
|
-
matches: (pane) => pane.includes("
|
|
299
|
+
matches: (pane) => pane.includes("welcome to trae cli") && pane.includes("press enter to continue"),
|
|
283
300
|
dismiss: () => terminal.sendEnter(),
|
|
284
301
|
},
|
|
285
302
|
{
|
|
286
303
|
id: "migration",
|
|
287
|
-
matches: (pane) => pane.includes("
|
|
288
|
-
pane.includes("
|
|
289
|
-
pane.includes("
|
|
304
|
+
matches: (pane) => pane.includes("legacy trae cli data detected") &&
|
|
305
|
+
pane.includes("select what to import") &&
|
|
306
|
+
(pane.includes("skip for now") || pane.includes("don't ask again")),
|
|
290
307
|
dismiss: () => terminal.interrupt(),
|
|
291
308
|
},
|
|
292
309
|
{
|
|
293
310
|
id: "hooks",
|
|
294
|
-
matches: (pane) => pane.includes("
|
|
295
|
-
pane.includes("
|
|
296
|
-
pane.includes("
|
|
297
|
-
pane.includes("Continue without trusting"),
|
|
311
|
+
matches: (pane) => pane.includes("hooks need review") &&
|
|
312
|
+
pane.includes("trust all and continue") &&
|
|
313
|
+
pane.includes("continue without trusting"),
|
|
298
314
|
dismiss: () => terminal.interrupt(),
|
|
299
315
|
},
|
|
300
316
|
];
|
|
301
|
-
|
|
302
|
-
|
|
317
|
+
let activePromptId;
|
|
318
|
+
let lastDismissedAt = 0;
|
|
319
|
+
const deadline = Date.now() + TRAEX_STARTUP_WATCH_MS;
|
|
303
320
|
const terminalId = `${localThreadId}-main`;
|
|
304
321
|
while (!this.shuttingDown &&
|
|
305
322
|
Date.now() < deadline &&
|
|
306
323
|
this.traexStartupWatchers.get(localThreadId) === watcher &&
|
|
307
324
|
this.terminals.get(terminalId) === terminal) {
|
|
308
|
-
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
if (pane.includes("TRAE CLI Next"))
|
|
312
|
-
break;
|
|
325
|
+
// Do not treat a composer frame as completion: Traex can render it before
|
|
326
|
+
// the startup modals arrive. The bounded deadline stops this watcher.
|
|
327
|
+
const pane = normalizeTraexPane(terminal.capturePane());
|
|
313
328
|
const prompt = prompts.find((candidate) => candidate.matches(pane));
|
|
314
|
-
|
|
329
|
+
const now = Date.now();
|
|
330
|
+
if (!prompt) {
|
|
331
|
+
activePromptId = undefined;
|
|
332
|
+
}
|
|
333
|
+
else if (prompt.id !== activePromptId ||
|
|
334
|
+
now - lastDismissedAt >= TRAEX_PROMPT_RETRY_MS) {
|
|
315
335
|
prompt.dismiss();
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
break;
|
|
336
|
+
activePromptId = prompt.id;
|
|
337
|
+
lastDismissedAt = now;
|
|
319
338
|
}
|
|
320
339
|
await new Promise((resolve) => {
|
|
321
|
-
const timer = setTimeout(resolve,
|
|
340
|
+
const timer = setTimeout(resolve, TRAEX_STARTUP_POLL_MS);
|
|
322
341
|
timer.unref();
|
|
323
342
|
});
|
|
324
343
|
}
|
|
@@ -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
|
@@ -146,7 +146,9 @@ export declare class TmuxTerminal {
|
|
|
146
146
|
paste(text: string, bufferName?: string): void;
|
|
147
147
|
/**
|
|
148
148
|
* Attach a client. `role: "read-only"` passes tmux `-r` so the viewer cannot
|
|
149
|
-
* 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).
|
|
150
152
|
*/
|
|
151
153
|
attach(role: "owner" | "read-only", dims?: {
|
|
152
154
|
cols?: number;
|
package/dist/terminal/tmux.js
CHANGED
|
@@ -319,14 +319,17 @@ export class TmuxTerminal {
|
|
|
319
319
|
}
|
|
320
320
|
/**
|
|
321
321
|
* Attach a client. `role: "read-only"` passes tmux `-r` so the viewer cannot
|
|
322
|
-
* type
|
|
322
|
+
* type and `ignore-size` so even its initial PTY dimensions cannot resize the
|
|
323
|
+
* owner's pane (defense-in-depth on top of the WS bridge dropping input and
|
|
324
|
+
* resize frames).
|
|
323
325
|
*/
|
|
324
326
|
async attach(role, dims) {
|
|
325
327
|
this.start();
|
|
326
328
|
const spawn = this.injectedSpawn ?? (await loadPtySpawn());
|
|
327
|
-
const args = [...this.base(), "attach-session"
|
|
329
|
+
const args = [...this.base(), "attach-session"];
|
|
328
330
|
if (role === "read-only")
|
|
329
|
-
args.push("-r");
|
|
331
|
+
args.push("-r", "-f", "ignore-size");
|
|
332
|
+
args.push("-t", TMUX_TARGET);
|
|
330
333
|
const proc = spawn(this.tmuxBin, args, {
|
|
331
334
|
name: "xterm-256color",
|
|
332
335
|
cols: dims?.cols ?? this.cols,
|
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.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"node-pty": "^1.0.0",
|
|
28
28
|
"ws": "^8.21.0",
|
|
29
|
-
"@rynx-ai/core": "0.1.11-beta.
|
|
29
|
+
"@rynx-ai/core": "0.1.11-beta.6"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/ws": "^8.18.1"
|