parallel-codex-tui 0.2.2 → 0.2.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.
@@ -3,7 +3,8 @@ defaultMode = "auto"
3
3
 
4
4
  [router.codex]
5
5
  command = "codex"
6
- args = ["exec", "--ephemeral", "--ignore-rules", "-c", "model_reasoning_effort=low", "--skip-git-repo-check", "--sandbox", "read-only", "--color", "never", "-"]
6
+ # Codex approval policy is a root option and must appear before `exec`.
7
+ args = ["-a", "never", "exec", "--ephemeral", "--ignore-rules", "-c", "model_reasoning_effort=low", "--skip-git-repo-check", "--sandbox", "read-only", "--color", "never", "-"]
7
8
  timeoutMs = 60000
8
9
  # Stop a silent Router before the total ceiling so one transient retry stays bounded.
9
10
  firstOutputTimeoutMs = 30000
@@ -37,8 +38,8 @@ maxConflictReplans = 2
37
38
 
38
39
  [workers.codex]
39
40
  command = "codex"
40
- # Automated Judge/Actor/Critic runs enforce workspace-write even if private args request a broader sandbox.
41
- args = ["exec", "--skip-git-repo-check", "--sandbox", "workspace-write", "--color", "never", "-"]
41
+ # Non-interactive runs never wait for an approval UI. Isolated coding roles also enforce workspace-write.
42
+ args = ["-a", "never", "exec", "--skip-git-repo-check", "--sandbox", "workspace-write", "--color", "never", "-"]
42
43
  # timeoutMs is the hard ceiling; first output owns silent startup and idle starts after actual output.
43
44
  # timeoutMs = 2700000
44
45
  # idleTimeoutMs = 300000
@@ -66,6 +67,7 @@ freshSessionArgs = []
66
67
 
67
68
  [workers.codex.interactive]
68
69
  command = "codex"
70
+ # Native attach stays interactive; do not add `-a never` here.
69
71
  args = ["resume", "{sessionId}"]
70
72
  # Used by the Task session detail view when branching from a recorded native session.
71
73
  forkArgs = ["fork", "{sessionId}"]
@@ -75,8 +77,8 @@ fallback = "new"
75
77
 
76
78
  [workers.claude]
77
79
  command = "claude"
78
- # Automated isolated roles enforce acceptEdits and remove permission-bypass flags.
79
- args = ["--print", "--permission-mode", "acceptEdits", "--output-format", "text"]
80
+ # Main uses Claude auto mode for non-interactive tools. Isolated roles are still clamped to acceptEdits.
81
+ args = ["--print", "--permission-mode", "auto", "--output-format", "text"]
80
82
  # timeoutMs is the hard ceiling; first output owns silent startup and idle starts after actual output.
81
83
  # timeoutMs = 2700000
82
84
  # idleTimeoutMs = 300000
package/README.md CHANGED
@@ -6,7 +6,7 @@ Built with Codex-assisted development.
6
6
 
7
7
  ## Current Release
8
8
 
9
- `v0.2.2` is available from [npm](https://www.npmjs.com/package/parallel-codex-tui/v/0.2.2) and as a [GitHub Release](https://github.com/allendred/parallel-codex-tui/releases/tag/v0.2.2). It automatically inherits the macOS system proxy when explicit proxy variables are absent, expands the default Router cold-start budget, and retains the single Up/Down request-history fix from `v0.2.1`.
9
+ `v0.2.4` is available from [npm](https://www.npmjs.com/package/parallel-codex-tui/v/0.2.4) and as a [GitHub Release](https://github.com/allendred/parallel-codex-tui/releases/tag/v0.2.4). Codex Router, Main, and automated Workers now place the non-interactive approval policy at the required root position as `-a never ... exec`, while retaining `read-only` routing and `workspace-write` coding sandboxes. Isolated Codex roles also replace unsafe or interactive approval arguments; native attach remains interactive. The Claude Main permission fix from `v0.2.3` remains included.
10
10
 
11
11
  The release keeps terminal scrolling and copying available at the same time without requiring Shift. Ordinary left-drag selection remains owned by the terminal while alternate-scroll delivers the wheel to chat, Worker logs, and structured views. `Ctrl+Y` copies the visible view as a fallback. It preserves the embedded native Agent scrollback across status-detail round trips and real PTY resizes.
12
12
 
@@ -20,7 +20,7 @@ Highlights:
20
20
  - Named Worker Providers support Codex-compatible, Claude-compatible, OpenAI-compatible, Anthropic-compatible, and custom generic commands with independent role, model, environment, permission, resume, and interactive settings.
21
21
  - Worker overview, Feature board, collaboration timeline, Task center, status details, rendered Markdown/Diff/error logs, Unicode search, keyboard navigation, mouse scrolling, and configurable themes share one terminal UI system.
22
22
 
23
- Release acceptance includes a real three-Feature Tetris task with parallel Actor/Critic waves and final integration review. Real Codex and Claude probes both proved fresh and same-session resume calls, the semantic Router completed a live classification, and one TUI completed Main calls in two workspaces before restoring the first workspace without leaking chat state. PTY coverage runs in Apple Terminal, tmux, and Zellij profiles at narrow and wide sizes, including status/log equivalence and preserving the native output tail across status-detail round trips. The deterministic repository suite passed 1,262 tests across 123 files; one quota-consuming real-Agent test is skipped by default and passes through `npm run test:real-agents`.
23
+ Release acceptance includes a real three-Feature Tetris task with parallel Actor/Critic waves and final integration review. Real Codex and Claude probes both proved fresh and same-session resume calls; Codex fresh and resume runs executed workspace writes with root-level `-a never`, and a Claude Main session executed safe Bash tools before and after resume with `auto` permissions. The semantic Router completed a live classification, and one TUI completed Main calls in two workspaces before restoring the first workspace without leaking chat state. PTY coverage runs in Apple Terminal, tmux, and Zellij profiles at narrow and wide sizes, including status/log equivalence and preserving the native output tail across status-detail round trips. The deterministic repository suite passed 1,265 tests across 123 files; one quota-consuming real-Agent test is skipped by default and passes through `npm run test:real-agents`.
24
24
 
25
25
  Real Provider probes depend on valid local CLI credentials. In particular, authenticate the Claude CLI before selecting a Claude-compatible Worker, then run `parallel-codex-tui --doctor --probe-agents` to prove fresh and resumed calls on that machine.
26
26
 
@@ -236,7 +236,7 @@ The doctor output includes `preview:` and `semantic:` ANSI swatch rows so you ca
236
236
  - Every numbered turn keeps its own immutable Judge, Actor, and Critic worker directories. Later turns use suffixed ids such as `judge-codex-0002`; the log viewer retains earlier workers, cycles them in turn order, and restores the same order after restart. The new worker record may reuse the prior native session id without overwriting the prior turn's prompt, status, log, or native-session metadata.
237
237
  - `Ctrl+N` leaves the active complex task intact on disk while clearing its live context, worker selection, retry state, and status so the next complex request creates an independent task from turn `0001`.
238
238
  - Single-feature follow-ups reuse the same Actor/Critic native sessions when available while moving them to the new turn's isolated workspace; prompts include up to five prior turn summaries as file-backed memory. A failed follow-up retry reuses a complete saved Judge plan, or restores Judge first when the earlier Judge run never produced one.
239
- - Automated Judge, Actor, Critic, Wave Actor, and Wave Critic runs enforce process-level isolation: Codex is clamped to `workspace-write`, and Claude is clamped to `acceptEdits`, even when private command arguments contain a broader bypass mode. Native attach remains an explicit interactive path.
239
+ - Automated Judge, Actor, Critic, Wave Actor, and Wave Critic runs enforce process-level isolation: Codex is clamped to root-level `-a never` plus `workspace-write`, and Claude is clamped to `acceptEdits`, even when private command arguments request interactive approval or contain a broader bypass mode. Native attach remains an explicit interactive path.
240
240
  - Pressing `Esc` while a request is running stops the router or active worker and records an interrupted complex task as `cancelled`; exiting the outer TUI also terminates the active run.
241
241
  - Failed and cancelled tasks expose `Ctrl+R` retry. Retry keeps the same task and turn, reuses recorded native worker sessions, preserves prior output behind a retry separator, does not route the request again, and reuses the persisted feature dependency plan. A complete Judge snapshot and fully integrated waves are skipped; an unchanged in-progress wave reuses successful Actor and Critic checkpoints and runs only unfinished workers. If the live workspace no longer matches the saved baseline, the stale wave checkpoint is rejected and rebuilt from the current project before workers continue.
242
242
  - The shared Main chat holds its own `sessions/main/run-owner.json` lease across prompt initialization, native-session reuse, and Worker completion. A second TUI is rejected before it can clear or overwrite the active Main prompt, log, status, or native session, and startup native-session reconciliation skips a Main session owned by another live TUI. After a hard exit, startup atomically claims the stale Main lease, terminates every verifiable orphan process group, marks active Main Workers `cancelled`, preserves native session ids, and records recovery before exposing the runtime. An unverifiable or still-running Main process blocks startup without changing its status or checkpoints.
@@ -263,7 +263,7 @@ defaultMode = "auto"
263
263
 
264
264
  [router.codex]
265
265
  command = "codex"
266
- args = ["exec", "--ephemeral", "--ignore-rules", "-c", "model_reasoning_effort=low", "--skip-git-repo-check", "--sandbox", "read-only", "--color", "never", "-"]
266
+ args = ["-a", "never", "exec", "--ephemeral", "--ignore-rules", "-c", "model_reasoning_effort=low", "--skip-git-repo-check", "--sandbox", "read-only", "--color", "never", "-"]
267
267
  timeoutMs = 60000
268
268
  firstOutputTimeoutMs = 30000
269
269
  idleTimeoutMs = 30000
@@ -328,7 +328,7 @@ Configure Codex and Claude commands in `.parallel-codex/config.toml`:
328
328
  ```toml
329
329
  [workers.codex]
330
330
  command = "codex"
331
- args = ["exec", "--skip-git-repo-check", "--sandbox", "workspace-write", "--color", "never", "-"]
331
+ args = ["-a", "never", "exec", "--skip-git-repo-check", "--sandbox", "workspace-write", "--color", "never", "-"]
332
332
 
333
333
  [workers.codex.model]
334
334
  name = "gpt-5"
@@ -350,7 +350,7 @@ forkArgs = ["fork", "{sessionId}"]
350
350
 
351
351
  [workers.claude]
352
352
  command = "claude"
353
- args = ["--print", "--permission-mode", "acceptEdits", "--output-format", "text"]
353
+ args = ["--print", "--permission-mode", "auto", "--output-format", "text"]
354
354
 
355
355
  [workers.claude.capabilities]
356
356
  profile = "claude"
@@ -363,7 +363,7 @@ args = ["--resume", "{sessionId}"]
363
363
  forkArgs = ["--resume", "{sessionId}", "--fork-session"]
364
364
  ```
365
365
 
366
- `model.args` and `model.env` apply to both automated worker runs and embedded native attach sessions. Native attach appends the rendered model arguments after `interactive.args`, so third-party `{model}` and `{provider}` selections remain active when you press `Ctrl+O`.
366
+ `model.args` and `model.env` apply to both automated worker runs and embedded native attach sessions. Native attach appends the rendered model arguments after `interactive.args`, so third-party `{model}` and `{provider}` selections remain active when you press `Ctrl+O`. Codex Router and non-interactive Codex Workers use the root option `-a never` before `exec`, so they cannot stall on an approval UI the outer TUI cannot deliver; Router remains `read-only`, while coding Workers stay inside `workspace-write`. Isolated Codex roles remove broader bypass flags and replace any configured approval mode with this bounded policy. The dangerous `--dangerously-bypass-approvals-and-sandbox` mode is never required. Native Codex attach remains interactive. Claude Main uses `auto` so safe tool calls can complete in print mode; isolated Claude coding roles are still clamped to `acceptEdits`. When a command genuinely needs interactive approval, Main points to `Ctrl+O` instead of claiming that a chat reply can grant permission.
367
367
 
368
368
  `model.provider` names the remote model service; `capabilities.profile` describes the local CLI protocol. Worker Provider IDs are lowercase names using letters, digits, or `_`; excluding `-` keeps generated Worker directory names unambiguous. Existing `codex`, `claude`, and `mock` IDs remain compatible, while additional profiles can inherit `codex`, `claude`, another named profile, or conservative `generic` defaults.
369
369
 
@@ -516,12 +516,12 @@ The release job installs npm `^11.5.1`, runs on Node `24.15.x`, publishes the pr
516
516
  To publish a release, update `package.json` and `src/version.ts` to the same version, then push a matching tag:
517
517
 
518
518
  ```bash
519
- VERSION=0.2.2
519
+ VERSION=0.2.4
520
520
  git tag "v$VERSION"
521
521
  git push origin "v$VERSION"
522
522
  ```
523
523
 
524
- You can also run the Release workflow manually and enter the same tag value. The release tag must match `package.json`; for example, package version `0.2.2` requires tag `v0.2.2`. Published tags such as `v0.2.1` are immutable and must not be moved or reused.
524
+ You can also run the Release workflow manually and enter the same tag value. The release tag must match `package.json`; for example, package version `0.2.4` requires tag `v0.2.4`. Published tags such as `v0.2.3` are immutable and must not be moved or reused.
525
525
 
526
526
  ## Publishing Hygiene
527
527
 
@@ -153,6 +153,8 @@ export function defaultConfig(projectRoot) {
153
153
  codex: {
154
154
  command: "codex",
155
155
  args: [
156
+ "-a",
157
+ "never",
156
158
  "exec",
157
159
  "--ephemeral",
158
160
  "--ignore-rules",
@@ -184,7 +186,7 @@ export function defaultConfig(projectRoot) {
184
186
  workers: {
185
187
  codex: {
186
188
  command: "codex",
187
- args: ["exec", "--skip-git-repo-check", "--sandbox", "workspace-write", "--color", "never", "-"],
189
+ args: ["-a", "never", "exec", "--skip-git-repo-check", "--sandbox", "workspace-write", "--color", "never", "-"],
188
190
  assignable: true,
189
191
  timeoutMs: 45 * 60 * 1000,
190
192
  idleTimeoutMs: 5 * 60 * 1000,
@@ -202,7 +204,7 @@ export function defaultConfig(projectRoot) {
202
204
  },
203
205
  nativeSession: {
204
206
  enabled: true,
205
- resumeArgs: ["exec", "resume", "{sessionId}", "--skip-git-repo-check", "-"],
207
+ resumeArgs: ["-a", "never", "exec", "resume", "{sessionId}", "--skip-git-repo-check", "-"],
206
208
  detectSessionId: true,
207
209
  fallback: "new"
208
210
  },
@@ -214,7 +216,7 @@ export function defaultConfig(projectRoot) {
214
216
  },
215
217
  claude: {
216
218
  command: "claude",
217
- args: ["--print", "--permission-mode", "acceptEdits", "--output-format", "text"],
219
+ args: ["--print", "--permission-mode", "auto", "--output-format", "text"],
218
220
  assignable: true,
219
221
  timeoutMs: 45 * 60 * 1000,
220
222
  idleTimeoutMs: 5 * 60 * 1000,
@@ -232,7 +234,7 @@ export function defaultConfig(projectRoot) {
232
234
  },
233
235
  nativeSession: {
234
236
  enabled: true,
235
- resumeArgs: ["--print", "--resume", "{sessionId}", "--permission-mode", "acceptEdits", "--output-format", "text"],
237
+ resumeArgs: ["--print", "--resume", "{sessionId}", "--permission-mode", "auto", "--output-format", "text"],
236
238
  detectSessionId: true,
237
239
  fallback: "new"
238
240
  },
@@ -6,6 +6,13 @@ export function buildMainPrompt(input) {
6
6
  `# Role: ${role.title}`,
7
7
  "",
8
8
  ...instructionLines(role.instructions),
9
+ "",
10
+ "# Non-interactive execution",
11
+ "",
12
+ "- Use available tools directly when the current permission mode allows them.",
13
+ "- Chat replies cannot grant CLI tool permissions. Never ask the user to approve a command by replying in chat.",
14
+ "- Never claim that a system permission dialog was shown unless a tool result explicitly confirms it.",
15
+ "- If a tool is denied because interactive approval is required, state that it was not run and direct the user to press Ctrl+O to continue in the native agent.",
9
16
  ...(input.context?.trim()
10
17
  ? ["", "# Active task context", "", input.context.trim()]
11
18
  : []),
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = "0.2.2";
1
+ export const version = "0.2.4";
@@ -114,6 +114,12 @@ function capabilityProbeSpecs(target) {
114
114
  }
115
115
  const specs = [];
116
116
  if (target.surfaces.has("automated")) {
117
+ specs.push({
118
+ args: ["--help"],
119
+ label: "approval policy",
120
+ requiredOptions: ["--ask-for-approval"],
121
+ usagePattern: /Usage:\s+codex\b/i
122
+ });
117
123
  specs.push({
118
124
  args: ["exec", "--help"],
119
125
  label: "exec sandbox/add-dir",
@@ -552,6 +552,13 @@ function enforceCodexWorkspaceSandbox(args) {
552
552
  if (arg === "--dangerously-bypass-approvals-and-sandbox") {
553
553
  continue;
554
554
  }
555
+ if (arg === "-a" || arg === "--ask-for-approval") {
556
+ index += 1;
557
+ continue;
558
+ }
559
+ if (arg.startsWith("-a=") || arg.startsWith("--ask-for-approval=")) {
560
+ continue;
561
+ }
555
562
  if (arg === "--sandbox" || arg === "-s") {
556
563
  result.push(arg, "workspace-write");
557
564
  hasSandbox = true;
@@ -565,10 +572,16 @@ function enforceCodexWorkspaceSandbox(args) {
565
572
  }
566
573
  result.push(arg);
567
574
  }
575
+ const execIndex = result.indexOf("exec");
576
+ if (execIndex >= 0) {
577
+ result.splice(execIndex, 0, "-a", "never");
578
+ }
568
579
  if (!hasSandbox) {
569
- const execIndex = result.indexOf("exec");
580
+ const normalizedExecIndex = result.indexOf("exec");
570
581
  const promptIndex = result.lastIndexOf("-");
571
- const insertAt = execIndex >= 0 ? execIndex + 1 : promptIndex >= 0 ? promptIndex : result.length;
582
+ const insertAt = normalizedExecIndex >= 0
583
+ ? normalizedExecIndex + 1
584
+ : promptIndex >= 0 ? promptIndex : result.length;
572
585
  result.splice(insertAt, 0, "--sandbox", "workspace-write");
573
586
  }
574
587
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "parallel-codex-tui",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "A TypeScript TUI wrapper for routed parallel coding with Codex and Claude workers.",
5
5
  "license": "MIT",
6
6
  "type": "module",