Skip to content

Log the start of model reasoning in background job logs - #480

Closed
cubicj wants to merge 30 commits into
openai:mainfrom
cubicj:log-reasoning-start
Closed

cubicj wants to merge 30 commits into
openai:mainfrom
cubicj:log-reasoning-start

Conversation

@cubicj

@cubicj cubicj commented Jul 12, 2026

Copy link
Copy Markdown

Summary

Background job logs (state/<slug>-<hash>/jobs/<id>.log) go silent during reasoning stretches. Every other item type already logs a start line via describeStartedItem in plugins/codex/scripts/lib/codex.mjs: commandExecution ("Running command: ..."), fileChange ("Applying N file change(s)."), mcpToolCall, dynamicToolCall, collabAgentToolCall, and webSearch. reasoning is the only started item type that falls through to default: return null, so nothing is written and anyone tailing a job log cannot tell whether the model is thinking or the job is stuck.

Change

Add a reasoning case to describeStartedItem that emits Thinking. with phase: null. The message follows the existing period-terminated declarative style ("Turn completed.", "File changes completed."). phase stays null deliberately: none of the existing phase values (starting/running/editing/investigating/verifying/reviewing/finalizing) describes reasoning, and inventing a new phase would widen the change beyond logging.

Verification

  • Live-verified against plugin v1.0.6 (WSL2, Codex CLI 0.145.0-alpha.2): with this case patched into the installed plugin, job logs gain a real-time [<iso-ts>] Thinking. line at the start of each reasoning block. This confirms the app-server delivers item/started for reasoning items; the stock plugin currently drops them.
  • npm test: 91/91 passing. The fake-codex fixture only emits completed reasoning items, so no existing test output changes.
  • npm run build: clean.

Noise consideration

Reasoning blocks complete every few seconds during long thinking stretches, so this adds one short line per block rather than one line per turn. Consumers that want a quieter view can collapse consecutive markers; the downstream log watcher @cubicj/codex-watch 0.3.0 already renders them as a single collapsed "Thinking..." indicator.

Related

dkundel-openai and others added 30 commits March 30, 2026 09:42
Co-authored-by: Codex <[email protected]>
Added instructions for reloading plugins and setup command.
…#13)

On Windows, Node.js `spawnSync` without `shell: true` uses
`CreateProcess`, which only resolves `.exe` files. npm installs global
tools (like `codex`) as `.cmd` shims, so `spawnSync("codex", ...)`
returns ENOENT even when codex is correctly installed and on PATH.

Adding `shell: process.platform === "win32"` routes through `cmd.exe`
on Windows, which properly resolves `.cmd`, `.bat`, and PATHEXT entries.
No behavior change on macOS/Linux since the condition evaluates to false.
* chore: bump plugin version to 1.0.1

* chore: bump plugin metadata version to 1.0.1
* fix: add shell and windowsHide options for Windows spawn in app-server

On Windows, spawn("codex", ["app-server"]) fails with ENOENT because
Node.js cannot resolve .cmd shims without shell: true. This adds
platform-gated shell and windowsHide options to the app-server spawn
call, and uses terminateProcessTree for proper process tree cleanup
since shell: true wraps the child in cmd.exe.

Without terminateProcessTree, plain SIGTERM only kills cmd.exe and
leaves the actual codex node process orphaned — verified with 274+
zombie node.exe processes accumulating on Windows.

Fixes #32
Fixes #46


* fix: guard terminateProcessTree against PID reuse after process exit

ChildProcess.killed only reflects whether .kill() was called by this
process — it stays false when the child exits on its own. On Windows,
where PIDs are recycled quickly, the 50 ms timer could fire after
cmd.exe has exited and its PID has been reassigned, causing taskkill
to terminate an unrelated process.

Add an exitCode === null check so the tree-kill path is skipped once
the child has already exited.


---------
* fix: make test suite portable across platforms

Replace hardcoded macOS path with fileURLToPath for cross-platform
compatibility. Add .cmd wrapper creation and platform-aware PATH
separator in test fixtures so the fake codex binary is discoverable
on Windows. Add shell and windowsHide options to the test helper
run() function to match production behavior.

Test results on Windows improve from 12/64 pass to 59/64 pass.


* fix: skip shell for absolute-path commands to avoid Windows space-in-path breakage

When `process.execPath` resolves to a path with spaces (e.g.,
`C:\Program Files\nodejs\node.exe`), `shell: true` causes cmd.exe
to split the path at the space. Guard with `path.isAbsolute()` so
only bare command names (which need `.cmd` shim resolution) use the
shell.


---------

Co-authored-by: Dominik Kundel <[email protected]>
* fix: skip untracked directories in review context

* fix: skip broken untracked symlinks in reviews
* Use app-server auth status for Codex readiness

* fix: reuse existing app server for auth checks
…provided (#159)

`SpawnedCodexAppServerClient.initialize()` passes `this.options.env` to
`spawn()`, but no caller ever sets `env` in options. In Node.js, passing
`undefined` for `env` gives the child process **no** environment variables,
breaking any model provider that relies on env vars (e.g. DATABRICKS_TOKEN).

Fall back to `process.env` when `this.options.env` is not set, matching the
existing pattern in `broker-lifecycle.mjs` and `codex-companion.mjs`.

Co-authored-by: Isaac

Co-authored-by: Bhuvanesh Sridharan <[email protected]>
…#126)

* fix: gracefully handle unsupported thread/name/set on older Codex CLI

Codex CLI v0.118.0 does not recognize the thread/name/set JSON-RPC method,
causing startThread() to throw. Thread naming is cosmetic (for job log
labels) and should not block thread creation. Wraps the call in try/catch
so it fails silently on older CLI versions.

Fixes #119

* refactor: only suppress unsupported-method errors for thread/name/set

Address Codex review feedback: the bare catch swallowed all errors
including auth, network, and server failures. Now only suppresses
errors containing 'unknown variant' or 'unknown method' (the specific
error older CLI versions return) and rethrows everything else.
* fix: avoid embedding large adversarial review diffs

* fix: preserve untracked content in lightweight review

* address comments

* fix: handle ENOBUFS type check in git diff sizing
* codex: honor --cwd when reporting session runtime

* codex: keep session runtime lookup scoped to callers

---------

Co-authored-by: VOIDXAI <[email protected]>
* fix: declare model in codex-rescue agent frontmatter

The codex-rescue agent had no model field, leaving Claude Code to assign
whatever default it chooses. As a thin forwarding wrapper that issues a
single Bash call, this agent is well-suited to the haiku tier; declaring
it explicitly ensures a predictable cost profile and tier guarantee.


* Update codex-rescue.md

---------

Co-authored-by: Dominik Kundel <[email protected]>
Closes #77

Updated README to replace unsupported 'xhigh' value with 'high' to prevent configuration errors.
Unquoted \$ARGUMENTS in the ! shell commands allowed shell metacharacters
in user-supplied job IDs to be expanded before Node received them (e.g.,
`task-123; malicious-cmd` would execute the trailing command). This is
inconsistent with review.md and adversarial-review.md, which both wrap
"$ARGUMENTS" in double quotes.
…on (#234) (#235)

* fix: route /codex:rescue through the Agent tool to stop Skill recursion (#234)

`/codex:rescue` previously combined two things that together caused a hang:

- `context: fork` in the frontmatter, which spawns a `general-purpose`
  subagent for the command body.
- Body prose "Route this request to the `codex:codex-rescue` subagent."
  without naming the transport.

When the main agent called `Skill(codex:rescue)` programmatically, the
fork resolved the ambiguous prose by trying `Skill(codex:codex-rescue)`
(unknown skill) and then falling back to `Skill(codex:rescue)`, which
re-entered this command and hung the session until the user cancelled.
No Codex job was ever created.

Naming the transport as `Agent(codex:codex-rescue)` alone is not enough:
forked general-purpose subagents do not expose the `Agent` tool, so the
forked runner cannot reach the subagent that way either. The minimal fix
is therefore two coordinated changes:

- Drop `context: fork` so the command body runs inline in the calling
  agent's context, where `Agent` is in scope.
- Say explicitly "use the `Agent` tool with `subagent_type:
  "codex:codex-rescue"`", and call out that `Skill(codex:codex-rescue)`
  and `Skill(codex:rescue)` are not valid routing paths. Add `Agent`
  to `allowed-tools` so the call does not prompt for permission.

Everything else in rescue.md (resume-candidate check, flag handling,
background/foreground semantics, operating rules) is unchanged. The
`codex:codex-rescue` subagent itself is unchanged.

Tests pin the new allow-list, the explicit `subagent_type`, the ban on
`Skill(codex:codex-rescue)`, and the absence of `context: fork`. The
existing "run the `codex:codex-rescue` subagent in the background"
assertion continues to hold since that sentence still reads correctly
with the Agent-tool transport.

Fixes #234

* test: match quoted result and cancel command arguments

---------

Co-authored-by: Dominik Kundel <[email protected]>
* Remove shell expansion for git commands

* Version bump
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.