agent-relay-server 0.3.6 → 0.3.7

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/README.md CHANGED
@@ -77,6 +77,31 @@ codex-relay
77
77
  incoming messages as live turns, and cleans up sidecar processes when Codex
78
78
  exits.
79
79
 
80
+ ### Codex approval prompts
81
+
82
+ Replying to relay messages is usually done with a shell command (`curl` to
83
+ `/api/messages`), so Codex may prompt for approval in stricter modes.
84
+
85
+ `codex-relay` now forwards your Codex runtime mode to the sidecar, including
86
+ `--ask-for-approval`, `--sandbox`, `--full-auto`, and `--yolo`.
87
+
88
+ Useful setups:
89
+
90
+ ```bash
91
+ # no approval prompts, still sandboxed to workspace boundaries
92
+ codex-relay -- --ask-for-approval never --sandbox workspace-write
93
+ ```
94
+
95
+ ```python
96
+ # ~/.codex/rules/default.rules
97
+ # allow only relay message sends without repeated prompts
98
+ prefix_rule(
99
+ pattern = ["curl", "-sS", "-X", "POST", "http://127.0.0.1:4850/api/messages"],
100
+ decision = "allow",
101
+ justification = "Allow local Agent Relay message posts",
102
+ )
103
+ ```
104
+
80
105
  Use a remote relay server by setting:
81
106
 
82
107
  ```bash
@@ -329,6 +329,56 @@ function spawnFallbackSidecar(runDir: string, env: Record<string, string | undef
329
329
  return sidecar.pid;
330
330
  }
331
331
 
332
+ type SessionPermissions = {
333
+ approvalPolicy?: string;
334
+ sandbox?: string;
335
+ };
336
+
337
+ function resolveSessionPermissions(codexArgs: string[]): SessionPermissions {
338
+ let approvalPolicy: string | undefined;
339
+ let sandbox: string | undefined;
340
+
341
+ for (let index = 0; index < codexArgs.length; index += 1) {
342
+ const arg = codexArgs[index]!;
343
+ if (arg === "--yolo" || arg === "--dangerously-bypass-approvals-and-sandbox") {
344
+ approvalPolicy = "never";
345
+ sandbox = "danger-full-access";
346
+ continue;
347
+ }
348
+ if (arg === "--full-auto") {
349
+ approvalPolicy = "on-request";
350
+ sandbox = "workspace-write";
351
+ continue;
352
+ }
353
+ if (arg === "--ask-for-approval" || arg === "-a") {
354
+ const next = codexArgs[index + 1];
355
+ if (next && !next.startsWith("-")) {
356
+ approvalPolicy = next;
357
+ index += 1;
358
+ }
359
+ continue;
360
+ }
361
+ if (arg.startsWith("--ask-for-approval=")) {
362
+ approvalPolicy = arg.slice("--ask-for-approval=".length);
363
+ continue;
364
+ }
365
+ if (arg === "--sandbox" || arg === "-s") {
366
+ const next = codexArgs[index + 1];
367
+ if (next && !next.startsWith("-")) {
368
+ sandbox = next;
369
+ index += 1;
370
+ }
371
+ continue;
372
+ }
373
+ if (arg.startsWith("--sandbox=")) {
374
+ sandbox = arg.slice("--sandbox=".length);
375
+ continue;
376
+ }
377
+ }
378
+
379
+ return { approvalPolicy, sandbox };
380
+ }
381
+
332
382
  function cleanupRun(runDir: string, appServer: ReturnType<typeof Bun.spawn> | null): void {
333
383
  if (existsSync(runDir)) {
334
384
  const pidsPath = join(runDir, "sidecar-pids.txt");
@@ -489,6 +539,7 @@ async function start(args: string[]): Promise<void> {
489
539
  }
490
540
 
491
541
  if (!listenUrl) listenUrl = await pickLoopbackUrl();
542
+ const permissions = resolveSessionPermissions(codexArgs);
492
543
 
493
544
  mkdirSync(runtimeRoot, { recursive: true });
494
545
  const runId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -502,6 +553,8 @@ async function start(args: string[]): Promise<void> {
502
553
  AGENT_RELAY_CODEX_RUN_ID: runId,
503
554
  AGENT_RELAY_CODEX_RUNTIME_DIR: runDir,
504
555
  CODEX_APP_SERVER_URL: listenUrl,
556
+ CODEX_LIVE_APPROVAL_POLICY: permissions.approvalPolicy,
557
+ CODEX_LIVE_SANDBOX: permissions.sandbox,
505
558
  };
506
559
 
507
560
  const appLog = Bun.file(join(runDir, "app-server.log"));
package/codex/README.md CHANGED
@@ -52,6 +52,31 @@ starts Codex with
52
52
  `--remote`, lets the SessionStart hook attach a sidecar to the actual thread,
53
53
  and kills sidecars plus the app-server when Codex exits.
54
54
 
55
+ ## Approvals and prompts
56
+
57
+ Relay replies are usually sent with a shell command (`curl` to
58
+ `/api/messages`), so Codex can prompt for approval in stricter modes.
59
+
60
+ `codex-relay` forwards your launch mode to the sidecar, including
61
+ `--ask-for-approval`, `--sandbox`, `--full-auto`, and `--yolo`.
62
+
63
+ Example: no prompt loop, still workspace sandboxing:
64
+
65
+ ```bash
66
+ codex-relay -- --ask-for-approval never --sandbox workspace-write
67
+ ```
68
+
69
+ If you prefer prompts for everything else but want relay sends auto-approved,
70
+ add a rule in `~/.codex/rules/default.rules` (adjust URL when using a remote relay):
71
+
72
+ ```python
73
+ prefix_rule(
74
+ pattern = ["curl", "-sS", "-X", "POST", "http://127.0.0.1:4850/api/messages"],
75
+ decision = "allow",
76
+ justification = "Allow local Agent Relay message posts",
77
+ )
78
+ ```
79
+
55
80
  For local development from this repo:
56
81
 
57
82
  ```bash
@@ -20,6 +20,8 @@ interface Config {
20
20
  threadMode: "auto" | "resume" | "start";
21
21
  threadId?: string;
22
22
  model?: string;
23
+ approvalPolicy?: string;
24
+ sandbox?: string;
23
25
  }
24
26
 
25
27
  interface RuntimeState {
@@ -196,8 +198,7 @@ class CodexLiveSidecar {
196
198
  const resumed = await this.app.threadResume({
197
199
  threadId,
198
200
  cwd: this.config.cwd,
199
- approvalPolicy: "on-request",
200
- sandbox: "workspace-write",
201
+ ...this.threadPermissions(),
201
202
  persistExtendedHistory: false,
202
203
  });
203
204
  return normalizeThread(resumed.thread);
@@ -213,8 +214,7 @@ class CodexLiveSidecar {
213
214
  const resumed = await this.app.threadResume({
214
215
  threadId: this.config.threadId,
215
216
  cwd: this.config.cwd,
216
- approvalPolicy: "on-request",
217
- sandbox: "workspace-write",
217
+ ...this.threadPermissions(),
218
218
  persistExtendedHistory: false,
219
219
  });
220
220
  return normalizeThread(resumed.thread);
@@ -238,8 +238,7 @@ class CodexLiveSidecar {
238
238
  const resumed = await this.app.threadResume({
239
239
  threadId: latest.id,
240
240
  cwd: this.config.cwd,
241
- approvalPolicy: "on-request",
242
- sandbox: "workspace-write",
241
+ ...this.threadPermissions(),
243
242
  persistExtendedHistory: false,
244
243
  });
245
244
  return normalizeThread(resumed.thread);
@@ -248,8 +247,7 @@ class CodexLiveSidecar {
248
247
 
249
248
  const started = await this.app.threadStart({
250
249
  cwd: this.config.cwd,
251
- approvalPolicy: "on-request",
252
- sandbox: "workspace-write",
250
+ ...this.threadPermissions(),
253
251
  ephemeral: false,
254
252
  sessionStartSource: "startup",
255
253
  model: this.config.model ?? null,
@@ -257,6 +255,13 @@ class CodexLiveSidecar {
257
255
  return normalizeThread(started.thread);
258
256
  }
259
257
 
258
+ private threadPermissions(): Record<string, string> {
259
+ const payload: Record<string, string> = {};
260
+ if (this.config.approvalPolicy) payload.approvalPolicy = this.config.approvalPolicy;
261
+ if (this.config.sandbox) payload.sandbox = this.config.sandbox;
262
+ return payload;
263
+ }
264
+
260
265
  private async readThreadWithFallback(threadId: string): Promise<Thread> {
261
266
  try {
262
267
  const read = await this.app.threadRead(threadId, true);
@@ -595,6 +600,8 @@ function loadConfig(): Config {
595
600
  threadMode: (process.env.CODEX_THREAD_MODE as Config["threadMode"]) || "auto",
596
601
  threadId: process.env.CODEX_THREAD_ID || undefined,
597
602
  model: process.env.CODEX_MODEL || undefined,
603
+ approvalPolicy: process.env.CODEX_LIVE_APPROVAL_POLICY || undefined,
604
+ sandbox: process.env.CODEX_LIVE_SANDBOX || undefined,
598
605
  };
599
606
  }
600
607
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",