visual-remote 0.3.1 → 0.3.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.
@@ -25,27 +25,530 @@ var AgentCanceledError = class extends Error {
25
25
  }
26
26
  };
27
27
 
28
+ // ../../packages/bridge-core/src/agents/claude-event-parser.ts
29
+ function asRecord(value) {
30
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
31
+ }
32
+ function asText(value) {
33
+ return typeof value === "string" && value.length > 0 ? value : void 0;
34
+ }
35
+ function asNumber(value) {
36
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
37
+ }
38
+ function contentBlocks(record) {
39
+ const message = asRecord(record.message);
40
+ return Array.isArray(message?.content) ? message.content.flatMap((block) => {
41
+ const parsed = asRecord(block);
42
+ return parsed === void 0 ? [] : [parsed];
43
+ }) : [];
44
+ }
45
+ function toolSummary(name, input) {
46
+ return asText(input.description) ?? asText(input.command) ?? asText(input.file_path) ?? asText(input.path) ?? (name === "Bash" ? "Run command" : void 0);
47
+ }
48
+ function filePath(input) {
49
+ return asText(input.file_path) ?? asText(input.path) ?? asText(input.notebook_path);
50
+ }
51
+ var ClaudeEventParser = class {
52
+ #defaultCwd;
53
+ #tools = /* @__PURE__ */ new Map();
54
+ #sessionId;
55
+ constructor(defaultCwd = "") {
56
+ this.#defaultCwd = defaultCwd;
57
+ }
58
+ parse(line) {
59
+ const trimmed = line.trim();
60
+ if (!trimmed) return [];
61
+ let value;
62
+ try {
63
+ value = JSON.parse(trimmed);
64
+ } catch {
65
+ return [{ type: "warning", text: trimmed }];
66
+ }
67
+ const record = asRecord(value);
68
+ if (!record) return [{ type: "message", text: trimmed }];
69
+ const events = [];
70
+ const foundSession = asText(record.session_id) ?? asText(record.sessionId);
71
+ if (foundSession !== void 0 && foundSession !== this.#sessionId) {
72
+ this.#sessionId = foundSession;
73
+ events.push({ type: "session", sessionId: foundSession });
74
+ }
75
+ const type = asText(record.type) ?? "unknown";
76
+ if (type === "system") {
77
+ const subtype = asText(record.subtype);
78
+ if (subtype) events.push({ type: "phase", name: subtype });
79
+ return events;
80
+ }
81
+ if (type === "assistant") {
82
+ for (const block of contentBlocks(record)) {
83
+ if (block.type === "text") {
84
+ const text = asText(block.text);
85
+ if (text) events.push({ type: "message", text });
86
+ continue;
87
+ }
88
+ if (block.type !== "tool_use") continue;
89
+ const name = asText(block.name) ?? "tool";
90
+ const id = asText(block.id);
91
+ const input = asRecord(block.input) ?? {};
92
+ if (id) this.#tools.set(id, name);
93
+ const summary = toolSummary(name, input);
94
+ events.push(
95
+ summary ? { type: "tool_start", name, summary } : { type: "tool_start", name }
96
+ );
97
+ const command = name === "Bash" ? asText(input.command) : void 0;
98
+ if (command) {
99
+ events.push({ type: "command", command, cwd: this.#defaultCwd });
100
+ }
101
+ const path = filePath(input);
102
+ if (path && ["Edit", "Write", "NotebookEdit"].includes(name)) {
103
+ events.push({ type: "file_hint", path });
104
+ }
105
+ }
106
+ return events;
107
+ }
108
+ if (type === "user") {
109
+ for (const block of contentBlocks(record)) {
110
+ if (block.type !== "tool_result") continue;
111
+ const id = asText(block.tool_use_id);
112
+ const name = (id ? this.#tools.get(id) : void 0) ?? "tool";
113
+ if (id) this.#tools.delete(id);
114
+ events.push({ type: "tool_end", name, ok: block.is_error !== true });
115
+ }
116
+ return events;
117
+ }
118
+ if (type === "result") {
119
+ const usage = asRecord(record.usage);
120
+ if (usage) {
121
+ const cachedInputTokens = asNumber(usage.cache_read_input_tokens);
122
+ events.push({
123
+ type: "usage",
124
+ inputTokens: asNumber(usage.input_tokens) + asNumber(usage.cache_creation_input_tokens) + cachedInputTokens,
125
+ outputTokens: asNumber(usage.output_tokens),
126
+ ...cachedInputTokens > 0 ? { cachedInputTokens } : {}
127
+ });
128
+ }
129
+ const result = asText(record.result);
130
+ const subtype = asText(record.subtype);
131
+ if (record.is_error === true || subtype?.startsWith("error") === true) {
132
+ events.push({ type: "error", text: result ?? "Claude reported an error" });
133
+ } else {
134
+ events.push(result ? { type: "complete", summary: result } : { type: "complete" });
135
+ }
136
+ return events;
137
+ }
138
+ return events;
139
+ }
140
+ };
141
+
142
+ // ../../packages/bridge-core/src/agents/claude-adapter.ts
143
+ import { execFile, spawn } from "node:child_process";
144
+ import { promisify } from "node:util";
145
+
146
+ // ../../packages/bridge-core/src/runtime/managed-process.ts
147
+ function safeChildProcessId(child) {
148
+ const pid = child.pid;
149
+ if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 1 || pid === process.pid) {
150
+ return void 0;
151
+ }
152
+ return pid;
153
+ }
154
+ function safeDetachedProcessGroupId(child) {
155
+ return process.platform === "win32" ? void 0 : safeChildProcessId(child);
156
+ }
157
+ function isMissingProcess(error) {
158
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH";
159
+ }
160
+ function processGroupIsAlive(processGroupId) {
161
+ try {
162
+ process.kill(-processGroupId, 0);
163
+ return true;
164
+ } catch (error) {
165
+ if (isMissingProcess(error)) return false;
166
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "EPERM") {
167
+ return true;
168
+ }
169
+ throw error;
170
+ }
171
+ }
172
+ function childIsAlive(child) {
173
+ return child.exitCode === null && child.signalCode === null;
174
+ }
175
+ function signalChildProcessTree(child, signal) {
176
+ const pid = safeChildProcessId(child);
177
+ if (pid === void 0) return false;
178
+ try {
179
+ if (process.platform === "win32") {
180
+ if (!childIsAlive(child)) return false;
181
+ return child.kill(signal);
182
+ }
183
+ process.kill(-pid, signal);
184
+ return true;
185
+ } catch (error) {
186
+ if (isMissingProcess(error)) return false;
187
+ throw error;
188
+ }
189
+ }
190
+ function installEmergencyChildExitHook(child, processLike = process) {
191
+ let removed = false;
192
+ const emergencyExit = () => {
193
+ removed = true;
194
+ try {
195
+ signalChildProcessTree(child, "SIGKILL");
196
+ } catch {
197
+ }
198
+ };
199
+ processLike.once("exit", emergencyExit);
200
+ return () => {
201
+ if (removed) return;
202
+ removed = true;
203
+ processLike.off("exit", emergencyExit);
204
+ };
205
+ }
206
+ async function waitForProcessTreeExit(child, processGroupId, timeoutMs) {
207
+ const deadline = Date.now() + timeoutMs;
208
+ while (processGroupId === void 0 ? childIsAlive(child) : processGroupIsAlive(processGroupId)) {
209
+ const remaining = deadline - Date.now();
210
+ if (remaining <= 0) return false;
211
+ await new Promise((resolve8) => {
212
+ setTimeout(resolve8, Math.min(25, remaining));
213
+ });
214
+ }
215
+ return true;
216
+ }
217
+ async function terminateChildProcessTree(child, killGraceMs = 3e3) {
218
+ const processGroupId = safeDetachedProcessGroupId(child);
219
+ if (processGroupId === void 0 && !childIsAlive(child)) return;
220
+ const sendSignal = (signal) => {
221
+ signalChildProcessTree(child, signal);
222
+ };
223
+ sendSignal("SIGTERM");
224
+ if (await waitForProcessTreeExit(
225
+ child,
226
+ processGroupId,
227
+ Math.max(0, killGraceMs)
228
+ )) {
229
+ return;
230
+ }
231
+ sendSignal("SIGKILL");
232
+ await waitForProcessTreeExit(
233
+ child,
234
+ processGroupId,
235
+ Math.min(Math.max(0, killGraceMs), 1e3)
236
+ );
237
+ }
238
+
239
+ // ../../packages/bridge-core/src/agents/async-queue.ts
240
+ var AsyncQueue = class {
241
+ #values = [];
242
+ #waiters = [];
243
+ #ended = false;
244
+ #error;
245
+ push(value) {
246
+ if (this.#ended) return;
247
+ const waiter = this.#waiters.shift();
248
+ if (waiter) waiter.resolve({ value, done: false });
249
+ else this.#values.push(value);
250
+ }
251
+ end(error) {
252
+ if (this.#ended) return;
253
+ this.#ended = true;
254
+ this.#error = error;
255
+ for (const waiter of this.#waiters.splice(0)) {
256
+ if (error !== void 0) waiter.reject(error);
257
+ else waiter.resolve({ value: void 0, done: true });
258
+ }
259
+ }
260
+ [Symbol.asyncIterator]() {
261
+ return {
262
+ next: async () => {
263
+ const value = this.#values.shift();
264
+ if (value !== void 0) return { value, done: false };
265
+ if (this.#ended) {
266
+ if (this.#error !== void 0) throw this.#error;
267
+ return { value: void 0, done: true };
268
+ }
269
+ return await new Promise((resolve8, reject) => {
270
+ this.#waiters.push({ resolve: resolve8, reject });
271
+ });
272
+ }
273
+ };
274
+ }
275
+ };
276
+
277
+ // ../../packages/bridge-core/src/agents/claude-adapter.ts
278
+ var execFileAsync = promisify(execFile);
279
+ var INHERITED_ENVIRONMENT = [
280
+ "PATH",
281
+ "HOME",
282
+ "USER",
283
+ "LOGNAME",
284
+ "SHELL",
285
+ "LANG",
286
+ "LC_ALL",
287
+ "TERM",
288
+ "TMPDIR",
289
+ "XDG_CONFIG_HOME",
290
+ "XDG_DATA_HOME",
291
+ "XDG_STATE_HOME",
292
+ "ANTHROPIC_API_KEY",
293
+ "CLAUDE_CODE_OAUTH_TOKEN",
294
+ "HTTPS_PROXY",
295
+ "HTTP_PROXY",
296
+ "NO_PROXY",
297
+ "USERPROFILE",
298
+ "APPDATA",
299
+ "LOCALAPPDATA",
300
+ "SystemRoot",
301
+ "COMSPEC",
302
+ "PATHEXT"
303
+ ];
304
+ var EMPTY_MCP_CONFIG = JSON.stringify({ mcpServers: {} });
305
+ var CLAUDE_SETTINGS = JSON.stringify({
306
+ permissions: {
307
+ disableBypassPermissionsMode: "disable",
308
+ deny: [
309
+ "Read(./.env)",
310
+ "Read(./.env.*)",
311
+ "Read(./**/*.pem)",
312
+ "Read(./**/*.key)",
313
+ "Edit(./.git/**)",
314
+ "Edit(./.visualdev/runtime/**)",
315
+ "Edit(./node_modules/**)"
316
+ ]
317
+ },
318
+ sandbox: {
319
+ enabled: true,
320
+ autoAllowBashIfSandboxed: true,
321
+ allowUnsandboxedCommands: false,
322
+ network: { strictAllowlist: true }
323
+ }
324
+ });
325
+ function processEnv(overrides) {
326
+ const environment = {};
327
+ for (const key of INHERITED_ENVIRONMENT) {
328
+ const value = process.env[key];
329
+ if (value !== void 0) environment[key] = value;
330
+ }
331
+ return { ...environment, ...overrides };
332
+ }
333
+ function splitLines(chunk, previous, onLine) {
334
+ const combined = previous + chunk.toString();
335
+ const lines = combined.split(/\r?\n/);
336
+ const remainder = lines.pop() ?? "";
337
+ for (const line of lines) onLine(line);
338
+ return remainder;
339
+ }
340
+ var ClaudeAdapter = class {
341
+ id = "claude";
342
+ #executable;
343
+ #killGraceMs;
344
+ #model;
345
+ #reasoningEffort;
346
+ #rtkExecutable;
347
+ #rtkVersion;
348
+ constructor(options = {}) {
349
+ this.#executable = options.executable ?? "claude";
350
+ this.#killGraceMs = options.killGraceMs ?? 2e3;
351
+ this.#model = options.model;
352
+ this.#reasoningEffort = options.reasoningEffort;
353
+ this.#rtkExecutable = options.rtkExecutable ?? "rtk";
354
+ }
355
+ #probeRtk(environment) {
356
+ if (this.#rtkExecutable === false) return Promise.resolve(void 0);
357
+ this.#rtkVersion ??= execFileAsync(this.#rtkExecutable, ["--version"], {
358
+ encoding: "utf8",
359
+ env: environment,
360
+ timeout: 1e3,
361
+ windowsHide: true,
362
+ maxBuffer: 16 * 1024
363
+ }).then(({ stdout }) => stdout.trim().split(/\r?\n/, 1)[0] || void 0).catch(() => void 0);
364
+ return this.#rtkVersion;
365
+ }
366
+ async #runtimePrompt(input, environment) {
367
+ if (this.#rtkExecutable === false) return input.prompt;
368
+ const guidance = await this.#probeRtk(environment).then(
369
+ (version) => version ? `RTK command proxy:
370
+ - ${version} is installed and available in this runtime.
371
+ - Prefix shell commands with RTK by default (for example: rtk git status, rtk rg <pattern>, rtk read <file>, rtk npm test).
372
+ - Use the native command only when RTK has no suitable proxy or RTK execution fails. Do not spend time rediscovering or reinstalling RTK.` : `RTK command proxy:
373
+ - RTK was not detected in this runtime. Use native repository commands directly and do not spend time searching for RTK.`
374
+ );
375
+ return `${input.prompt.trimEnd()}
376
+
377
+ ${guidance}
378
+ `;
379
+ }
380
+ #baseArgs() {
381
+ return [
382
+ "-p",
383
+ "--output-format",
384
+ "stream-json",
385
+ "--verbose",
386
+ "--permission-mode",
387
+ "acceptEdits",
388
+ "--strict-mcp-config",
389
+ "--mcp-config",
390
+ EMPTY_MCP_CONFIG,
391
+ "--no-chrome",
392
+ "--tools",
393
+ "Read,Glob,Grep,Edit,Write,Bash",
394
+ "--settings",
395
+ CLAUDE_SETTINGS,
396
+ ...this.#model === void 0 ? [] : ["--model", this.#model],
397
+ ...this.#reasoningEffort === void 0 ? [] : ["--effort", this.#reasoningEffort]
398
+ ];
399
+ }
400
+ async probe() {
401
+ return await new Promise((resolve8) => {
402
+ const child = spawn(this.#executable, ["--version"], {
403
+ stdio: ["ignore", "pipe", "ignore"],
404
+ shell: false
405
+ });
406
+ let output = "";
407
+ child.stdout?.on("data", (chunk) => {
408
+ output += chunk.toString();
409
+ });
410
+ child.once("error", () => {
411
+ resolve8({ available: false, supportsResume: true, structuredOutput: true });
412
+ });
413
+ child.once("close", (code) => {
414
+ const match = output.match(/(\d+\.\d+\.\d+(?:[-+][^\s]+)?)/);
415
+ const capabilities = {
416
+ available: code === 0,
417
+ supportsResume: true,
418
+ structuredOutput: true
419
+ };
420
+ if (match?.[1]) capabilities.version = match[1];
421
+ resolve8(capabilities);
422
+ });
423
+ });
424
+ }
425
+ async *run(input, signal) {
426
+ yield* this.#execute(input, signal, this.#baseArgs());
427
+ }
428
+ async *resume(input, signal) {
429
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/.test(input.sessionId)) {
430
+ throw new Error("Refusing to resume an invalid Claude session id");
431
+ }
432
+ yield* this.#execute(
433
+ input,
434
+ signal,
435
+ [...this.#baseArgs(), "--resume", input.sessionId]
436
+ );
437
+ }
438
+ async *#execute(input, signal, args) {
439
+ const queue = new AsyncQueue();
440
+ const environment = processEnv(input.environment);
441
+ const prompt = await this.#runtimePrompt(input, environment);
442
+ const parser = new ClaudeEventParser(input.workspaceRoot);
443
+ const child = spawn(this.#executable, args, {
444
+ cwd: input.workspaceRoot,
445
+ env: environment,
446
+ detached: process.platform !== "win32",
447
+ shell: false,
448
+ stdio: ["pipe", "pipe", "pipe"]
449
+ });
450
+ const removeEmergencyExitHook = installEmergencyChildExitHook(child);
451
+ let stdoutRemainder = "";
452
+ let stderrRemainder = "";
453
+ let timedOut = false;
454
+ let aborted = signal.aborted;
455
+ let termination;
456
+ const requestTermination = () => {
457
+ termination ??= terminateChildProcessTree(
458
+ child,
459
+ this.#killGraceMs
460
+ ).finally(removeEmergencyExitHook);
461
+ return termination;
462
+ };
463
+ const timeout = setTimeout(() => {
464
+ timedOut = true;
465
+ void requestTermination();
466
+ }, Math.max(1, input.maxRunMs));
467
+ timeout.unref();
468
+ const abort = () => {
469
+ aborted = true;
470
+ void requestTermination();
471
+ };
472
+ signal.addEventListener("abort", abort, { once: true });
473
+ if (signal.aborted) abort();
474
+ child.stdout.on("data", (chunk) => {
475
+ stdoutRemainder = splitLines(chunk, stdoutRemainder, (line) => {
476
+ for (const event of parser.parse(line)) queue.push(event);
477
+ });
478
+ });
479
+ child.stderr.on("data", (chunk) => {
480
+ stderrRemainder = splitLines(chunk, stderrRemainder, (line) => {
481
+ if (line.trim()) queue.push({ type: "warning", text: line });
482
+ });
483
+ });
484
+ child.once("error", (error) => queue.end(error));
485
+ child.once("close", (code, closeSignal) => {
486
+ clearTimeout(timeout);
487
+ signal.removeEventListener("abort", abort);
488
+ void (async () => {
489
+ await requestTermination();
490
+ if (stdoutRemainder.trim()) {
491
+ for (const event of parser.parse(stdoutRemainder)) queue.push(event);
492
+ }
493
+ if (stderrRemainder.trim()) queue.push({ type: "warning", text: stderrRemainder });
494
+ if (timedOut) queue.end(new AgentTimeoutError());
495
+ else if (aborted) queue.end(new AgentCanceledError());
496
+ else if (code !== 0) {
497
+ queue.end(
498
+ new AgentProcessError(
499
+ `Claude exited with code ${String(code)}`,
500
+ code,
501
+ closeSignal
502
+ )
503
+ );
504
+ } else {
505
+ queue.end();
506
+ }
507
+ })().catch((error) => queue.end(error));
508
+ });
509
+ child.stdin.on("error", (error) => {
510
+ if (error.code !== "EPIPE") queue.end(error);
511
+ });
512
+ child.stdin.end(prompt);
513
+ try {
514
+ for await (const event of queue) yield event;
515
+ } finally {
516
+ clearTimeout(timeout);
517
+ signal.removeEventListener("abort", abort);
518
+ try {
519
+ if (child.exitCode === null && child.signalCode === null) {
520
+ await requestTermination();
521
+ } else if (termination) {
522
+ await termination;
523
+ }
524
+ } finally {
525
+ removeEmergencyExitHook();
526
+ }
527
+ }
528
+ }
529
+ };
530
+
28
531
  // ../../packages/bridge-core/src/agents/codex-event-parser.ts
29
- function asRecord(value) {
532
+ function asRecord2(value) {
30
533
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
31
534
  }
32
- function asText(value) {
535
+ function asText2(value) {
33
536
  return typeof value === "string" && value.length > 0 ? value : void 0;
34
537
  }
35
538
  function sessionId(record) {
36
- const thread = asRecord(record.thread);
37
- return asText(record.thread_id) ?? asText(record.threadId) ?? asText(record.session_id) ?? asText(record.sessionId) ?? (thread ? asText(thread.id) : void 0);
539
+ const thread = asRecord2(record.thread);
540
+ return asText2(record.thread_id) ?? asText2(record.threadId) ?? asText2(record.session_id) ?? asText2(record.sessionId) ?? (thread ? asText2(thread.id) : void 0);
38
541
  }
39
542
  function itemFiles(item) {
40
543
  const changes = Array.isArray(item.changes) ? item.changes : [];
41
544
  const files = changes.flatMap((change) => {
42
- const record = asRecord(change);
545
+ const record = asRecord2(change);
43
546
  if (!record) return [];
44
- return [asText(record.path) ?? asText(record.file_path) ?? asText(record.filePath)].filter(
547
+ return [asText2(record.path) ?? asText2(record.file_path) ?? asText2(record.filePath)].filter(
45
548
  (path) => path !== void 0
46
549
  );
47
550
  });
48
- const direct = asText(item.path) ?? asText(item.file_path) ?? asText(item.filePath);
551
+ const direct = asText2(item.path) ?? asText2(item.file_path) ?? asText2(item.filePath);
49
552
  if (direct) files.push(direct);
50
553
  return [...new Set(files)];
51
554
  }
@@ -59,28 +562,28 @@ function isDirectExecItem(item) {
59
562
  return item.type === "mcp_tool_call" && item.server === "visual_remote_exec" && item.tool === "run_readonly";
60
563
  }
61
564
  function directExecSummary(item) {
62
- const arguments_ = asRecord(item.arguments);
565
+ const arguments_ = asRecord2(item.arguments);
63
566
  const commands = Array.isArray(arguments_?.commands) ? arguments_.commands : [];
64
567
  const summaries = commands.flatMap((candidate) => {
65
- const command = asRecord(candidate);
568
+ const command = asRecord2(candidate);
66
569
  const argv = stringArray(command?.argv);
67
570
  return argv === void 0 ? [] : [formatArgv(argv)];
68
571
  });
69
572
  return summaries.length === 0 ? void 0 : summaries.join(" \xB7 ");
70
573
  }
71
574
  function directExecResults(item, defaultCwd) {
72
- const result = asRecord(item.result);
73
- const structured = asRecord(result?.structured_content ?? result?.structuredContent);
575
+ const result = asRecord2(item.result);
576
+ const structured = asRecord2(result?.structured_content ?? result?.structuredContent);
74
577
  const results = Array.isArray(structured?.results) ? structured.results : [];
75
578
  return results.flatMap((candidate) => {
76
- const command = asRecord(candidate);
579
+ const command = asRecord2(candidate);
77
580
  const argv = stringArray(command?.argv);
78
581
  if (argv === void 0) return [];
79
582
  const exitCode = typeof command?.exitCode === "number" ? command.exitCode : void 0;
80
583
  const durationMs = typeof command?.durationMs === "number" ? command.durationMs : void 0;
81
584
  return [{
82
585
  command: formatArgv(argv),
83
- cwd: asText(command?.cwd) ?? defaultCwd,
586
+ cwd: asText2(command?.cwd) ?? defaultCwd,
84
587
  ok: exitCode === 0,
85
588
  ...exitCode === void 0 ? {} : { exitCode },
86
589
  ...durationMs === void 0 ? {} : { durationMs },
@@ -91,8 +594,8 @@ function directExecResults(item, defaultCwd) {
91
594
  });
92
595
  }
93
596
  function normalizedUsage(record) {
94
- const result = asRecord(record.result);
95
- const usage = asRecord(record.usage) ?? (result ? asRecord(result.usage) : void 0);
597
+ const result = asRecord2(record.result);
598
+ const usage = asRecord2(record.usage) ?? (result ? asRecord2(result.usage) : void 0);
96
599
  if (usage === void 0) return void 0;
97
600
  const inputTokens = usage.input_tokens ?? usage.inputTokens;
98
601
  const outputTokens = usage.output_tokens ?? usage.outputTokens;
@@ -116,17 +619,17 @@ function parseCodexJsonLine(line, defaultCwd = "") {
116
619
  } catch {
117
620
  return [{ type: "warning", text: trimmed }];
118
621
  }
119
- const record = asRecord(value);
622
+ const record = asRecord2(value);
120
623
  if (!record) return [{ type: "message", text: trimmed }];
121
- const type = asText(record.type) ?? "unknown";
624
+ const type = asText2(record.type) ?? "unknown";
122
625
  const events = [];
123
626
  const foundSession = sessionId(record);
124
627
  if (foundSession) events.push({ type: "session", sessionId: foundSession });
125
628
  if (type === "thread.started" || type === "thread.created") return events;
126
629
  if (type === "turn.started") return [...events, { type: "phase", name: "turn.started" }];
127
630
  if (type === "turn.completed") {
128
- const result = asRecord(record.result);
129
- const summary = asText(record.summary) ?? (result ? asText(result.summary) : void 0);
631
+ const result = asRecord2(record.result);
632
+ const summary = asText2(record.summary) ?? (result ? asText2(result.summary) : void 0);
130
633
  const usage = normalizedUsage(record);
131
634
  return [
132
635
  ...events,
@@ -135,31 +638,31 @@ function parseCodexJsonLine(line, defaultCwd = "") {
135
638
  ];
136
639
  }
137
640
  if (type === "turn.failed" || type === "error") {
138
- const error = asRecord(record.error);
139
- const text = asText(record.message) ?? (error ? asText(error.message) : void 0) ?? "Codex reported an error";
641
+ const error = asRecord2(record.error);
642
+ const text = asText2(record.message) ?? (error ? asText2(error.message) : void 0) ?? "Codex reported an error";
140
643
  return [...events, { type: "error", text }];
141
644
  }
142
- const item = asRecord(record.item);
645
+ const item = asRecord2(record.item);
143
646
  if (type === "item.started" && item) {
144
- const itemType = asText(item.type) ?? "item";
647
+ const itemType = asText2(item.type) ?? "item";
145
648
  const directExec = isDirectExecItem(item);
146
- const summary = directExec ? directExecSummary(item) : asText(item.command) ?? asText(item.text);
649
+ const summary = directExec ? directExecSummary(item) : asText2(item.command) ?? asText2(item.text);
147
650
  const start = summary ? { type: "tool_start", name: directExec ? "direct_exec" : itemType, summary } : { type: "tool_start", name: directExec ? "direct_exec" : itemType };
148
651
  return [...events, start];
149
652
  }
150
653
  if (type === "item.completed" && item) {
151
- const itemType = asText(item.type) ?? "item";
654
+ const itemType = asText2(item.type) ?? "item";
152
655
  if (itemType === "agent_message") {
153
- const text = asText(item.text) ?? asText(item.message);
656
+ const text = asText2(item.text) ?? asText2(item.message);
154
657
  return text ? [...events, { type: "message", text }] : events;
155
658
  }
156
659
  if (itemType === "command_execution") {
157
- const command = asText(item.command);
660
+ const command = asText2(item.command);
158
661
  if (command) {
159
662
  events.push({
160
663
  type: "command",
161
664
  command,
162
- cwd: asText(item.cwd) ?? defaultCwd
665
+ cwd: asText2(item.cwd) ?? defaultCwd
163
666
  });
164
667
  }
165
668
  }
@@ -185,151 +688,18 @@ function parseCodexJsonLine(line, defaultCwd = "") {
185
688
  });
186
689
  return events;
187
690
  }
188
- const message = asText(record.message);
691
+ const message = asText2(record.message);
189
692
  if (message) events.push({ type: "message", text: message });
190
693
  return events;
191
694
  }
192
695
 
193
696
  // ../../packages/bridge-core/src/agents/codex-adapter.ts
194
- import { execFile, spawn } from "node:child_process";
697
+ import { execFile as execFile2, spawn as spawn2 } from "node:child_process";
195
698
  import { existsSync } from "node:fs";
196
699
  import { fileURLToPath } from "node:url";
197
- import { promisify } from "node:util";
198
-
199
- // ../../packages/bridge-core/src/agents/async-queue.ts
200
- var AsyncQueue = class {
201
- #values = [];
202
- #waiters = [];
203
- #ended = false;
204
- #error;
205
- push(value) {
206
- if (this.#ended) return;
207
- const waiter = this.#waiters.shift();
208
- if (waiter) waiter.resolve({ value, done: false });
209
- else this.#values.push(value);
210
- }
211
- end(error) {
212
- if (this.#ended) return;
213
- this.#ended = true;
214
- this.#error = error;
215
- for (const waiter of this.#waiters.splice(0)) {
216
- if (error !== void 0) waiter.reject(error);
217
- else waiter.resolve({ value: void 0, done: true });
218
- }
219
- }
220
- [Symbol.asyncIterator]() {
221
- return {
222
- next: async () => {
223
- const value = this.#values.shift();
224
- if (value !== void 0) return { value, done: false };
225
- if (this.#ended) {
226
- if (this.#error !== void 0) throw this.#error;
227
- return { value: void 0, done: true };
228
- }
229
- return await new Promise((resolve8, reject) => {
230
- this.#waiters.push({ resolve: resolve8, reject });
231
- });
232
- }
233
- };
234
- }
235
- };
236
-
237
- // ../../packages/bridge-core/src/runtime/managed-process.ts
238
- function safeChildProcessId(child) {
239
- const pid = child.pid;
240
- if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 1 || pid === process.pid) {
241
- return void 0;
242
- }
243
- return pid;
244
- }
245
- function safeDetachedProcessGroupId(child) {
246
- return process.platform === "win32" ? void 0 : safeChildProcessId(child);
247
- }
248
- function isMissingProcess(error) {
249
- return typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH";
250
- }
251
- function processGroupIsAlive(processGroupId) {
252
- try {
253
- process.kill(-processGroupId, 0);
254
- return true;
255
- } catch (error) {
256
- if (isMissingProcess(error)) return false;
257
- if (typeof error === "object" && error !== null && "code" in error && error.code === "EPERM") {
258
- return true;
259
- }
260
- throw error;
261
- }
262
- }
263
- function childIsAlive(child) {
264
- return child.exitCode === null && child.signalCode === null;
265
- }
266
- function signalChildProcessTree(child, signal) {
267
- const pid = safeChildProcessId(child);
268
- if (pid === void 0) return false;
269
- try {
270
- if (process.platform === "win32") {
271
- if (!childIsAlive(child)) return false;
272
- return child.kill(signal);
273
- }
274
- process.kill(-pid, signal);
275
- return true;
276
- } catch (error) {
277
- if (isMissingProcess(error)) return false;
278
- throw error;
279
- }
280
- }
281
- function installEmergencyChildExitHook(child, processLike = process) {
282
- let removed = false;
283
- const emergencyExit = () => {
284
- removed = true;
285
- try {
286
- signalChildProcessTree(child, "SIGKILL");
287
- } catch {
288
- }
289
- };
290
- processLike.once("exit", emergencyExit);
291
- return () => {
292
- if (removed) return;
293
- removed = true;
294
- processLike.off("exit", emergencyExit);
295
- };
296
- }
297
- async function waitForProcessTreeExit(child, processGroupId, timeoutMs) {
298
- const deadline = Date.now() + timeoutMs;
299
- while (processGroupId === void 0 ? childIsAlive(child) : processGroupIsAlive(processGroupId)) {
300
- const remaining = deadline - Date.now();
301
- if (remaining <= 0) return false;
302
- await new Promise((resolve8) => {
303
- setTimeout(resolve8, Math.min(25, remaining));
304
- });
305
- }
306
- return true;
307
- }
308
- async function terminateChildProcessTree(child, killGraceMs = 3e3) {
309
- const processGroupId = safeDetachedProcessGroupId(child);
310
- if (processGroupId === void 0 && !childIsAlive(child)) return;
311
- const sendSignal = (signal) => {
312
- signalChildProcessTree(child, signal);
313
- };
314
- sendSignal("SIGTERM");
315
- if (await waitForProcessTreeExit(
316
- child,
317
- processGroupId,
318
- Math.max(0, killGraceMs)
319
- )) {
320
- return;
321
- }
322
- sendSignal("SIGKILL");
323
- await waitForProcessTreeExit(
324
- child,
325
- processGroupId,
326
- Math.min(Math.max(0, killGraceMs), 1e3)
327
- );
328
- }
329
-
330
- // ../../packages/bridge-core/src/agents/codex-adapter.ts
331
- var execFileAsync = promisify(execFile);
332
- var INHERITED_ENVIRONMENT = [
700
+ import { promisify as promisify2 } from "node:util";
701
+ var execFileAsync2 = promisify2(execFile2);
702
+ var INHERITED_ENVIRONMENT2 = [
333
703
  "PATH",
334
704
  "HOME",
335
705
  "USER",
@@ -354,15 +724,15 @@ var INHERITED_ENVIRONMENT = [
354
724
  "COMSPEC",
355
725
  "PATHEXT"
356
726
  ];
357
- function processEnv(overrides) {
727
+ function processEnv2(overrides) {
358
728
  const environment = {};
359
- for (const key of INHERITED_ENVIRONMENT) {
729
+ for (const key of INHERITED_ENVIRONMENT2) {
360
730
  const value = process.env[key];
361
731
  if (value !== void 0) environment[key] = value;
362
732
  }
363
733
  return { ...environment, ...overrides };
364
734
  }
365
- function splitLines(chunk, previous, onLine) {
735
+ function splitLines2(chunk, previous, onLine) {
366
736
  const combined = previous + chunk.toString();
367
737
  const lines = combined.split(/\r?\n/);
368
738
  const remainder = lines.pop() ?? "";
@@ -382,18 +752,24 @@ var CodexAdapter = class {
382
752
  id = "codex";
383
753
  #executable;
384
754
  #killGraceMs;
755
+ #model;
756
+ #profile;
757
+ #reasoningEffort;
385
758
  #rtkExecutable;
386
759
  #directExecMcpScript;
387
760
  #rtkVersion;
388
761
  constructor(options = {}) {
389
762
  this.#executable = options.executable ?? "codex";
390
763
  this.#killGraceMs = options.killGraceMs ?? 2e3;
764
+ this.#model = options.model;
765
+ this.#profile = options.profile;
766
+ this.#reasoningEffort = options.reasoningEffort;
391
767
  this.#rtkExecutable = options.rtkExecutable ?? "rtk";
392
768
  this.#directExecMcpScript = options.directExecMcpScript === false ? void 0 : options.directExecMcpScript ?? defaultDirectExecMcpScript();
393
769
  }
394
770
  #probeRtk(environment) {
395
771
  if (this.#rtkExecutable === false) return Promise.resolve(void 0);
396
- this.#rtkVersion ??= execFileAsync(this.#rtkExecutable, ["--version"], {
772
+ this.#rtkVersion ??= execFileAsync2(this.#rtkExecutable, ["--version"], {
397
773
  encoding: "utf8",
398
774
  env: environment,
399
775
  timeout: 1e3,
@@ -436,9 +812,16 @@ ${guidance}
436
812
  `mcp_servers.visual_remote_exec.args=${JSON.stringify(serverArgs)}`
437
813
  ];
438
814
  }
815
+ #modelConfig() {
816
+ return [
817
+ ...this.#profile === void 0 ? [] : ["--profile", this.#profile],
818
+ ...this.#model === void 0 ? [] : ["--model", this.#model],
819
+ ...this.#reasoningEffort === void 0 ? [] : ["-c", `model_reasoning_effort=${JSON.stringify(this.#reasoningEffort)}`]
820
+ ];
821
+ }
439
822
  async probe() {
440
823
  return await new Promise((resolve8) => {
441
- const child = spawn(this.#executable, ["--version"], {
824
+ const child = spawn2(this.#executable, ["--version"], {
442
825
  stdio: ["ignore", "pipe", "ignore"],
443
826
  shell: false
444
827
  });
@@ -471,6 +854,7 @@ ${guidance}
471
854
  "workspace-write",
472
855
  "-C",
473
856
  input.workspaceRoot,
857
+ ...this.#modelConfig(),
474
858
  ...this.#directExecConfig(input),
475
859
  "-"
476
860
  ];
@@ -489,6 +873,7 @@ ${guidance}
489
873
  "workspace-write",
490
874
  "-C",
491
875
  input.workspaceRoot,
876
+ ...this.#modelConfig(),
492
877
  ...this.#directExecConfig(input),
493
878
  "resume",
494
879
  input.sessionId,
@@ -498,9 +883,9 @@ ${guidance}
498
883
  }
499
884
  async *#execute(input, signal, args) {
500
885
  const queue = new AsyncQueue();
501
- const environment = processEnv(input.environment);
886
+ const environment = processEnv2(input.environment);
502
887
  const prompt = await this.#runtimePrompt(input, environment);
503
- const child = spawn(this.#executable, args, {
888
+ const child = spawn2(this.#executable, args, {
504
889
  cwd: input.workspaceRoot,
505
890
  env: environment,
506
891
  detached: process.platform !== "win32",
@@ -532,12 +917,12 @@ ${guidance}
532
917
  signal.addEventListener("abort", abort, { once: true });
533
918
  if (signal.aborted) abort();
534
919
  child.stdout.on("data", (chunk) => {
535
- stdoutRemainder = splitLines(chunk, stdoutRemainder, (line) => {
920
+ stdoutRemainder = splitLines2(chunk, stdoutRemainder, (line) => {
536
921
  for (const event of parseCodexJsonLine(line, input.workspaceRoot)) queue.push(event);
537
922
  });
538
923
  });
539
924
  child.stderr.on("data", (chunk) => {
540
- stderrRemainder = splitLines(chunk, stderrRemainder, (line) => {
925
+ stderrRemainder = splitLines2(chunk, stderrRemainder, (line) => {
541
926
  if (line.trim()) queue.push({ type: "warning", text: line });
542
927
  });
543
928
  });
@@ -615,6 +1000,7 @@ import { ZodError } from "zod";
615
1000
  import { z } from "zod";
616
1001
  var servicePortSchema = z.number().int().min(10001).max(65535);
617
1002
  var commandSchema = z.array(z.string().min(1)).min(1);
1003
+ var environmentVariableSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "Expected an environment variable name");
618
1004
  var readySchema = z.object({
619
1005
  path: z.string().startsWith("/").default("/"),
620
1006
  timeoutMs: z.number().int().positive().default(6e4)
@@ -643,9 +1029,38 @@ var visualDevConfigSchema = z.object({
643
1029
  }).strict(),
644
1030
  agent: z.object({
645
1031
  adapter: z.enum(["codex", "claude", "opencode"]),
1032
+ model: z.string().trim().min(1).optional(),
1033
+ reasoningEffort: z.enum(["minimal", "low", "medium", "high", "xhigh", "max"]).optional(),
1034
+ profile: z.string().trim().regex(
1035
+ /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,
1036
+ "Expected a Codex profile name"
1037
+ ).optional(),
1038
+ inheritEnv: z.array(environmentVariableSchema).default([]),
646
1039
  maxRunMs: z.number().int().positive(),
647
1040
  resumeMode: z.enum(["auto", "new"]).default("auto")
648
- }).strict(),
1041
+ }).strict().superRefine((agent, context) => {
1042
+ if (agent.adapter === "claude" && agent.reasoningEffort === "minimal") {
1043
+ context.addIssue({
1044
+ code: "custom",
1045
+ path: ["reasoningEffort"],
1046
+ message: "minimal reasoning effort is only supported by the Codex adapter"
1047
+ });
1048
+ }
1049
+ if (agent.adapter === "codex" && agent.reasoningEffort === "max") {
1050
+ context.addIssue({
1051
+ code: "custom",
1052
+ path: ["reasoningEffort"],
1053
+ message: "max reasoning effort is only supported by the Claude adapter"
1054
+ });
1055
+ }
1056
+ if (agent.adapter !== "codex" && agent.profile !== void 0) {
1057
+ context.addIssue({
1058
+ code: "custom",
1059
+ path: ["profile"],
1060
+ message: "agent.profile is only supported by the Codex adapter"
1061
+ });
1062
+ }
1063
+ }),
649
1064
  queue: z.object({
650
1065
  maxPending: z.number().int().positive()
651
1066
  }).strict(),
@@ -689,6 +1104,7 @@ function createDefaultConfig(projectId) {
689
1104
  },
690
1105
  agent: {
691
1106
  adapter: "codex",
1107
+ inheritEnv: [],
692
1108
  maxRunMs: 9e5,
693
1109
  resumeMode: "auto"
694
1110
  },
@@ -761,17 +1177,17 @@ function mergeConfigValues(base, override) {
761
1177
  }
762
1178
  return merged;
763
1179
  }
764
- async function readYamlMapping(filePath, required) {
1180
+ async function readYamlMapping(filePath2, required) {
765
1181
  let source;
766
1182
  try {
767
- source = await readFile(filePath, "utf8");
1183
+ source = await readFile(filePath2, "utf8");
768
1184
  } catch (error) {
769
1185
  if (!required && typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
770
1186
  return void 0;
771
1187
  }
772
- throw new VisualDevConfigError(`Unable to read config: ${filePath}`, {
1188
+ throw new VisualDevConfigError(`Unable to read config: ${filePath2}`, {
773
1189
  cause: error,
774
- filePath
1190
+ filePath: filePath2
775
1191
  });
776
1192
  }
777
1193
  try {
@@ -784,9 +1200,9 @@ async function readYamlMapping(filePath, required) {
784
1200
  }
785
1201
  return value;
786
1202
  } catch (error) {
787
- throw new VisualDevConfigError(`Invalid YAML in ${filePath}`, {
1203
+ throw new VisualDevConfigError(`Invalid YAML in ${filePath2}`, {
788
1204
  cause: error,
789
- filePath
1205
+ filePath: filePath2
790
1206
  });
791
1207
  }
792
1208
  }
@@ -925,10 +1341,10 @@ var RevertConflictError = class extends RepositorySafetyError {
925
1341
  };
926
1342
 
927
1343
  // ../../packages/bridge-core/src/git/git-command.ts
928
- import { spawn as spawn2 } from "node:child_process";
1344
+ import { spawn as spawn3 } from "node:child_process";
929
1345
  async function runGit(args, options) {
930
1346
  return await new Promise((resolve8, reject) => {
931
- const child = spawn2("git", [...args], {
1347
+ const child = spawn3("git", [...args], {
932
1348
  cwd: options.cwd,
933
1349
  env: { ...process.env, ...options.env ?? {} },
934
1350
  shell: false,
@@ -1997,7 +2413,7 @@ import { relative as relative5, resolve as resolve6, sep as sep5 } from "node:pa
1997
2413
  // ../../packages/bridge-core/src/source/path-normalizer.ts
1998
2414
  import { access, readFile as readFile3, realpath as realpath6 } from "node:fs/promises";
1999
2415
  import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5, sep as sep4 } from "node:path";
2000
- import { spawn as spawn3 } from "node:child_process";
2416
+ import { spawn as spawn4 } from "node:child_process";
2001
2417
  function toPosix(value) {
2002
2418
  return value.split(sep4).join("/").replaceAll("\\", "/");
2003
2419
  }
@@ -2032,7 +2448,7 @@ function isWithin2(root, candidate) {
2032
2448
  }
2033
2449
  async function gitFiles(repoRoot) {
2034
2450
  return await new Promise((resolvePromise, reject) => {
2035
- const child = spawn3(
2451
+ const child = spawn4(
2036
2452
  "git",
2037
2453
  ["ls-files", "--cached", "--others", "--exclude-standard", "-z"],
2038
2454
  {
@@ -2102,8 +2518,8 @@ async function normalizeSourceLocation(input, repoRootInput) {
2102
2518
  candidates: matches.slice(0, 20)
2103
2519
  };
2104
2520
  }
2105
- const filePath = matches[0];
2106
- const absolutePath = resolve5(repoRoot, filePath);
2521
+ const filePath2 = matches[0];
2522
+ const absolutePath = resolve5(repoRoot, filePath2);
2107
2523
  const canonical = await realpath6(absolutePath);
2108
2524
  if (!isWithin2(repoRoot, canonical)) {
2109
2525
  return { input, confidence: "unknown", candidates: [] };
@@ -2111,7 +2527,7 @@ async function normalizeSourceLocation(input, repoRootInput) {
2111
2527
  const lineNumber = await boundedLine(canonical, input.lineNumber);
2112
2528
  return {
2113
2529
  input,
2114
- filePath,
2530
+ filePath: filePath2,
2115
2531
  absolutePath: canonical,
2116
2532
  ...lineNumber === void 0 ? {} : { lineNumber },
2117
2533
  ...input.columnNumber === void 0 ? {} : { columnNumber: input.columnNumber },
@@ -3039,11 +3455,11 @@ var TaskService = class {
3039
3455
  for (const target of result.selection.targets) {
3040
3456
  const sanitize = async (location) => {
3041
3457
  try {
3042
- const filePath = await this.#git.pathPolicy.assertFilesystemPathAllowed(
3458
+ const filePath2 = await this.#git.pathPolicy.assertFilesystemPathAllowed(
3043
3459
  location.filePath,
3044
3460
  false
3045
3461
  );
3046
- return { ...location, filePath };
3462
+ return { ...location, filePath: filePath2 };
3047
3463
  } catch {
3048
3464
  return void 0;
3049
3465
  }
@@ -3255,7 +3671,7 @@ var BrowserSessionManager = class {
3255
3671
  };
3256
3672
 
3257
3673
  // ../../packages/bridge-core/src/verification/commands.ts
3258
- import { spawn as spawn4 } from "node:child_process";
3674
+ import { spawn as spawn5 } from "node:child_process";
3259
3675
  var MAX_OUTPUT_CHARS = 8e3;
3260
3676
  var KILL_GRACE_MS = 250;
3261
3677
  function appendOutput(current, chunk) {
@@ -3269,7 +3685,7 @@ async function runVerificationCommand(configured, cwd, signal) {
3269
3685
  }
3270
3686
  const startedAt = Date.now();
3271
3687
  return await new Promise((resolveResult, rejectResult) => {
3272
- const child = spawn4(executable, arguments_, {
3688
+ const child = spawn5(executable, arguments_, {
3273
3689
  cwd,
3274
3690
  detached: process.platform !== "win32",
3275
3691
  shell: false,
@@ -3844,15 +4260,40 @@ ${output}` : ""}`
3844
4260
  }
3845
4261
 
3846
4262
  // ../../packages/bridge-core/src/bridge/default-control-service.ts
4263
+ function createAgentAdapter(agent) {
4264
+ if (agent.adapter === "claude") {
4265
+ if (agent.reasoningEffort === "minimal") {
4266
+ throw new Error("Claude does not support minimal reasoning effort");
4267
+ }
4268
+ return new ClaudeAdapter({
4269
+ ...agent.model === void 0 ? {} : { model: agent.model },
4270
+ ...agent.reasoningEffort === void 0 ? {} : { reasoningEffort: agent.reasoningEffort }
4271
+ });
4272
+ }
4273
+ if (agent.adapter === "codex") {
4274
+ if (agent.reasoningEffort === "max") {
4275
+ throw new Error("Codex does not support max reasoning effort");
4276
+ }
4277
+ return new CodexAdapter({
4278
+ ...agent.model === void 0 ? {} : { model: agent.model },
4279
+ ...agent.reasoningEffort === void 0 ? {} : { reasoningEffort: agent.reasoningEffort },
4280
+ ...agent.profile === void 0 ? {} : { profile: agent.profile }
4281
+ });
4282
+ }
4283
+ throw new Error(`Agent adapter ${agent.adapter} is not implemented in this build`);
4284
+ }
4285
+ function inheritedAgentEnvironment(names, environment) {
4286
+ return Object.fromEntries(
4287
+ names.flatMap((name) => {
4288
+ const value = environment[name];
4289
+ return value === void 0 ? [] : [[name, value]];
4290
+ })
4291
+ );
4292
+ }
3847
4293
  async function createDefaultControlService(context, environment = process.env) {
3848
4294
  const loaded = await loadVisualDevConfig(context.repoRoot, {
3849
4295
  ...context.configRoot === void 0 ? {} : { configRoot: context.configRoot }
3850
4296
  });
3851
- if (loaded.config.agent.adapter !== "codex") {
3852
- throw new Error(
3853
- `Agent adapter ${loaded.config.agent.adapter} is not implemented in this MVP build`
3854
- );
3855
- }
3856
4297
  const git = await GitTransactionManager.open(context.repoRoot, {
3857
4298
  allowed: rebaseWorkspacePatterns(
3858
4299
  context.repoRoot,
@@ -3871,13 +4312,16 @@ async function createDefaultControlService(context, environment = process.env) {
3871
4312
  projectId: context.projectId,
3872
4313
  workspaceRoot: context.workspaceRoot,
3873
4314
  upstreamUrl: context.upstreamUrl,
3874
- adapter: new CodexAdapter(),
4315
+ adapter: createAgentAdapter(loaded.config.agent),
3875
4316
  store,
3876
4317
  git,
3877
4318
  maxRunMs: loaded.config.agent.maxRunMs,
3878
4319
  maxPending: loaded.config.queue.maxPending,
3879
4320
  resumeMode: loaded.config.agent.resumeMode,
3880
- environment: {}
4321
+ environment: inheritedAgentEnvironment(
4322
+ loaded.config.agent.inheritEnv,
4323
+ environment
4324
+ )
3881
4325
  });
3882
4326
  const controlService = createTaskControlService({
3883
4327
  taskService,
@@ -4208,10 +4652,10 @@ async function acquireWorktreeLock(repositoryRoot, options = {}) {
4208
4652
  }
4209
4653
 
4210
4654
  // ../../packages/bridge-core/src/runtime/repository.ts
4211
- import { execFile as execFile2 } from "node:child_process";
4655
+ import { execFile as execFile3 } from "node:child_process";
4212
4656
  import { realpath as realpath9 } from "node:fs/promises";
4213
- import { promisify as promisify2 } from "node:util";
4214
- var execFileAsync2 = promisify2(execFile2);
4657
+ import { promisify as promisify3 } from "node:util";
4658
+ var execFileAsync3 = promisify3(execFile3);
4215
4659
  var GitWorktreeNotFoundError = class extends Error {
4216
4660
  constructor(cwd, options = {}) {
4217
4661
  super(
@@ -4223,7 +4667,7 @@ var GitWorktreeNotFoundError = class extends Error {
4223
4667
  };
4224
4668
  async function discoverGitWorktreeRoot(cwd = process.cwd()) {
4225
4669
  try {
4226
- const { stdout } = await execFileAsync2(
4670
+ const { stdout } = await execFileAsync3(
4227
4671
  "git",
4228
4672
  ["-C", cwd, "rev-parse", "--show-toplevel"],
4229
4673
  {
@@ -5253,7 +5697,7 @@ async function startBridgeCore(options, dependencies) {
5253
5697
  try {
5254
5698
  lock = options.lock ?? await acquireWorktreeLock(loadedConfig.repoRoot, { environment });
5255
5699
  const host = options.host ?? loadedConfig.config.gateway.host;
5256
- const configuredPublicUrl = options.publicUrl ?? loadedConfig.config.gateway.publicUrl;
5700
+ const configuredPublicUrl = options.publicUrl ?? loadedConfig.config.gateway.publicUrl ?? options.fallbackPublicUrl;
5257
5701
  const publicUrl = configuredPublicUrl === void 0 ? void 0 : normalizePublicUrl(configuredPublicUrl);
5258
5702
  const gatewayPort = await findAvailablePort(startPort(loadedConfig, options.listen), host);
5259
5703
  const token = generatePairingToken();
@@ -5373,7 +5817,8 @@ async function startAttachBridge(options, dependencies = {}) {
5373
5817
  upstreamUrl: normalizeUpstream(options.upstream),
5374
5818
  ...options.listen === void 0 ? {} : { listen: options.listen },
5375
5819
  ...options.host === void 0 ? {} : { host: options.host },
5376
- ...options.publicUrl === void 0 ? {} : { publicUrl: options.publicUrl }
5820
+ ...options.publicUrl === void 0 ? {} : { publicUrl: options.publicUrl },
5821
+ ...options.fallbackPublicUrl === void 0 ? {} : { fallbackPublicUrl: options.fallbackPublicUrl }
5377
5822
  },
5378
5823
  dependencies
5379
5824
  );
@@ -5409,10 +5854,17 @@ function commandLinePort(argv) {
5409
5854
  }
5410
5855
  return void 0;
5411
5856
  }
5857
+ function resolveNextPort(options, argv, environment) {
5858
+ return validPort(options.appPort) ?? commandLinePort(argv) ?? validPort(environment.PORT) ?? 3e3;
5859
+ }
5412
5860
  function resolveNextUpstream(options = {}, argv = process.argv, environment = process.env) {
5413
- const port = validPort(options.appPort) ?? commandLinePort(argv) ?? validPort(environment.PORT) ?? 3e3;
5861
+ const port = resolveNextPort(options, argv, environment);
5414
5862
  return `http://127.0.0.1:${port}`;
5415
5863
  }
5864
+ function resolveNextPublicUrl(options = {}, argv = process.argv, environment = process.env) {
5865
+ const port = resolveNextPort(options, argv, environment);
5866
+ return `http://localhost:${port}`;
5867
+ }
5416
5868
  function isNextDetachedTelemetryProcess(argv = process.argv) {
5417
5869
  return argv.some(
5418
5870
  (argument) => /[\\/]next[\\/]dist[\\/]telemetry[\\/]detached-flush\.js$/.test(argument)
@@ -5460,6 +5912,7 @@ async function startOrReuseBridge(options, cwd) {
5460
5912
  const ownedBridge = await startAttachBridge(
5461
5913
  {
5462
5914
  upstream: resolveNextUpstream(options),
5915
+ fallbackPublicUrl: resolveNextPublicUrl(options),
5463
5916
  ...options.bridgeHost === void 0 ? {} : { host: options.bridgeHost },
5464
5917
  ...options.bridgePort === void 0 ? {} : { listen: options.bridgePort }
5465
5918
  },
@@ -5531,6 +5984,7 @@ export {
5531
5984
  next_default as default,
5532
5985
  isNextDetachedTelemetryProcess,
5533
5986
  mergeNextRewrites,
5987
+ resolveNextPublicUrl,
5534
5988
  resolveNextUpstream,
5535
5989
  withVisualRemote
5536
5990
  };