@rynx-ai/runtime 0.1.11-beta.3 → 0.1.11-beta.4

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.d.ts CHANGED
@@ -191,6 +191,7 @@ export declare class LocalAgentHost implements CodexCapabilities {
191
191
  args: string[];
192
192
  cwd: string;
193
193
  env?: Record<string, string>;
194
+ skipTraexStartupPrompts?: boolean;
194
195
  } | null>;
195
196
  /**
196
197
  * Bring up (idempotently) a session's persistent codex forwarder and emit its
package/dist/host.js CHANGED
@@ -472,6 +472,9 @@ export class LocalAgentHost {
472
472
  // to the same effective sandbox.
473
473
  const sandbox = live?.sandbox ?? this.sessionSandbox ?? this.config.AGENT_SANDBOX;
474
474
  const approvalPolicy = live?.approvalPolicy ?? this.sessionApprovalPolicy ?? this.config.AGENT_APPROVAL_POLICY;
475
+ const traexYolo = runtime === "traex" &&
476
+ sandbox === "danger-full-access" &&
477
+ approvalPolicy === "never";
475
478
  const configOverrides = sandbox === "workspace-write"
476
479
  ? ["sandbox_workspace_write.network_access=true"]
477
480
  : [];
@@ -500,9 +503,11 @@ export class LocalAgentHost {
500
503
  remoteUrl,
501
504
  ...(activeThreadId ? { threadId: activeThreadId } : {}),
502
505
  configOverrides,
506
+ codexArgs: traexYolo ? ["--dangerously-bypass-hook-trust"] : [],
503
507
  additionalDirs: providerAdditionalDirs(live.workspace),
504
508
  }),
505
509
  cwd: live.workspace.cwd,
510
+ ...(runtime === "traex" ? { skipTraexStartupPrompts: true } : {}),
506
511
  // Share the app-server's private CODEX_HOME so the TUI inherits the same
507
512
  // login/settings and skips the real home's update/NUX prompt. RYNX_SESSION_ID
508
513
  // scopes agent-run CLIs (rynx-emulator) to this session at the daemon.
@@ -31,6 +31,7 @@ interface LiveCodexProvider {
31
31
  args: string[];
32
32
  cwd: string;
33
33
  env?: Record<string, string>;
34
+ skipTraexStartupPrompts?: boolean;
34
35
  } | null>;
35
36
  ensureLiveCodexSession?(localThreadId: string, emit: (event: SessionEvent) => void, opts: {
36
37
  workspace: SessionWorkspaceSnapshot;
@@ -71,6 +72,8 @@ export declare class RunnerSession {
71
72
  /** Live terminals hosted by this session, and per-attach client handles. */
72
73
  private readonly terminals;
73
74
  private readonly attachments;
75
+ private readonly attachmentThreadIds;
76
+ private readonly traexStartupWatchers;
74
77
  /** Opens are async; a close received before attach resolves tombstones the id. */
75
78
  private readonly pendingTerminalOpens;
76
79
  private readonly cancelledTerminalOpens;
@@ -101,6 +104,8 @@ export declare class RunnerSession {
101
104
  * `codexTerminalSpec`. Shares the `${id}-main` terminal id with `term.open`,
102
105
  * so the web attach reuses the same detached pane. */
103
106
  private launchCodexPane;
107
+ private cancelTraexStartupWatcher;
108
+ private skipTraexStartupPrompts;
104
109
  private stopLive;
105
110
  /** Stop event forwarding, kill native terminals/hooks, then synchronously
106
111
  * scrub provider handoff files before the child process is allowed to exit. */
@@ -7,6 +7,8 @@ export class RunnerSession {
7
7
  /** Live terminals hosted by this session, and per-attach client handles. */
8
8
  terminals = new TerminalRegistry();
9
9
  attachments = new Map();
10
+ attachmentThreadIds = new Map();
11
+ traexStartupWatchers = new Map();
10
12
  /** Opens are async; a close received before attach resolves tombstones the id. */
11
13
  pendingTerminalOpens = new Set();
12
14
  cancelledTerminalOpens = new Set();
@@ -46,6 +48,7 @@ export class RunnerSession {
46
48
  });
47
49
  return;
48
50
  case "term.input":
51
+ this.cancelTraexStartupWatcher(this.attachmentThreadIds.get(msg.attachId));
49
52
  this.attachments.get(msg.attachId)?.write(Buffer.from(msg.dataB64, "base64").toString("utf8"));
50
53
  return;
51
54
  case "term.resize":
@@ -57,6 +60,7 @@ export class RunnerSession {
57
60
  }
58
61
  const attachment = this.attachments.get(msg.attachId);
59
62
  this.attachments.delete(msg.attachId);
63
+ this.attachmentThreadIds.delete(msg.attachId);
60
64
  attachment?.kill();
61
65
  return;
62
66
  }
@@ -211,6 +215,8 @@ export class RunnerSession {
211
215
  try {
212
216
  const input = msg.input ?? msg.text;
213
217
  const outcome = (await provider.injectMessage?.(msg.localThreadId, input)) ?? "notLive";
218
+ if (outcome === "injected")
219
+ this.cancelTraexStartupWatcher(msg.localThreadId);
214
220
  this.transport.send({ t: "injected", reqId: msg.reqId, localThreadId: msg.localThreadId, outcome });
215
221
  }
216
222
  catch (error) {
@@ -248,7 +254,9 @@ export class RunnerSession {
248
254
  const spec = await this.liveProvider.codexTerminalSpec?.(localThreadId);
249
255
  if (!spec)
250
256
  return;
251
- const term = this.terminals.getOrCreate(`${localThreadId}-main`, {
257
+ const terminalId = `${localThreadId}-main`;
258
+ const coldStart = !this.terminals.has(terminalId);
259
+ const term = this.terminals.getOrCreate(terminalId, {
252
260
  cwd: spec.cwd,
253
261
  command: spec.command,
254
262
  args: spec.args,
@@ -256,8 +264,68 @@ export class RunnerSession {
256
264
  rows: rows ?? 40,
257
265
  ...(spec.env ? { env: spec.env } : {}),
258
266
  });
267
+ if (coldStart && spec.skipTraexStartupPrompts) {
268
+ const watcher = Symbol(localThreadId);
269
+ this.traexStartupWatchers.set(localThreadId, watcher);
270
+ void this.skipTraexStartupPrompts(localThreadId, term, watcher);
271
+ }
259
272
  this.liveProvider.attachTerminalInjector?.(localThreadId, term);
260
273
  }
274
+ cancelTraexStartupWatcher(localThreadId) {
275
+ if (localThreadId)
276
+ this.traexStartupWatchers.delete(localThreadId);
277
+ }
278
+ async skipTraexStartupPrompts(localThreadId, terminal, watcher) {
279
+ const prompts = [
280
+ {
281
+ id: "welcome",
282
+ matches: (pane) => pane.includes("Welcome to TRAE CLI") && pane.includes("Press enter to continue"),
283
+ dismiss: () => terminal.sendEnter(),
284
+ },
285
+ {
286
+ id: "migration",
287
+ matches: (pane) => pane.includes("Legacy TRAE CLI data detected") &&
288
+ pane.includes("Would you like to migrate detected data to the new TRAE CLI storage?") &&
289
+ pane.includes("Select what to import:"),
290
+ dismiss: () => terminal.interrupt(),
291
+ },
292
+ {
293
+ id: "hooks",
294
+ matches: (pane) => pane.includes("Hooks need review") &&
295
+ pane.includes("hooks are new or changed.") &&
296
+ pane.includes("Trust all and continue") &&
297
+ pane.includes("Continue without trusting"),
298
+ dismiss: () => terminal.interrupt(),
299
+ },
300
+ ];
301
+ const dismissed = new Set();
302
+ const deadline = Date.now() + 20_000;
303
+ const terminalId = `${localThreadId}-main`;
304
+ while (!this.shuttingDown &&
305
+ Date.now() < deadline &&
306
+ this.traexStartupWatchers.get(localThreadId) === watcher &&
307
+ this.terminals.get(terminalId) === terminal) {
308
+ const pane = terminal.capturePane();
309
+ // Once the normal composer is visible, startup is complete and there is
310
+ // no reason to keep polling the tmux pane for the remainder of the window.
311
+ if (pane.includes("TRAE CLI Next"))
312
+ break;
313
+ const prompt = prompts.find((candidate) => candidate.matches(pane));
314
+ if (prompt && !dismissed.has(prompt.id)) {
315
+ prompt.dismiss();
316
+ dismissed.add(prompt.id);
317
+ if (dismissed.size === prompts.length)
318
+ break;
319
+ }
320
+ await new Promise((resolve) => {
321
+ const timer = setTimeout(resolve, 100);
322
+ timer.unref();
323
+ });
324
+ }
325
+ if (this.traexStartupWatchers.get(localThreadId) === watcher) {
326
+ this.traexStartupWatchers.delete(localThreadId);
327
+ }
328
+ }
261
329
  stopLive() {
262
330
  for (const id of this.liveIds) {
263
331
  this.liveProvider.stopLiveCodexSession?.(id, {
@@ -336,6 +404,8 @@ export class RunnerSession {
336
404
  return;
337
405
  }
338
406
  this.attachments.set(msg.attachId, attachment);
407
+ if (msg.localThreadId)
408
+ this.attachmentThreadIds.set(msg.attachId, msg.localThreadId);
339
409
  attachment.onData((chunk) => this.transport.send({
340
410
  t: "term.data",
341
411
  attachId: msg.attachId,
@@ -343,6 +413,7 @@ export class RunnerSession {
343
413
  }));
344
414
  attachment.onExit((info) => {
345
415
  this.attachments.delete(msg.attachId);
416
+ this.attachmentThreadIds.delete(msg.attachId);
346
417
  this.transport.send({ t: "term.exit", attachId: msg.attachId, exitCode: info.exitCode });
347
418
  });
348
419
  this.transport.send({ t: "term.opened", attachId: msg.attachId, role });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/runtime",
3
- "version": "0.1.11-beta.3",
3
+ "version": "0.1.11-beta.4",
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.3"
29
+ "@rynx-ai/core": "0.1.11-beta.4"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/ws": "^8.18.1"