Skip to content

fix(mcp): install the Codex plugin, and report the real cause when Codex fails - #1295

Merged
gewenyu99 merged 7 commits into
mainfrom
fix/codex-install-failure-reporting
Sep 22, 2026
Merged

gewenyu99 merged 7 commits into
mainfrom
fix/codex-install-failure-reporting

Conversation

@gewenyu99

@gewenyu99 gewenyu99 commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Two defects on the Codex plugin install path. Same method, same client, and the second builds on helpers the first introduces.

1. The plugin never installed

installPlugin ran codex plugin marketplace add PostHog/ai-plugin and stopped. That registers the catalog; it does not install from it:

$ codex plugin marketplace add PostHog/ai-plugin
Added marketplace `posthog` from https://github.com/PostHog/ai-plugin.git.   # exit 0

$ codex plugin list -m posthog
PLUGIN           STATUS         VERSION  SOURCE
posthog@posthog  not installed           https://github.com/PostHog/ai-plugin.git

The wizard then reported ✔ Plugin installed for: Codex. The Codex plugin carries 164 skills and no MCP server (pluginBundlesMcpServer(): false), so every Codex user who ran mcp add has the server and none of the skills.

isPluginInstalled read [marketplaces.posthog] from config.toml — the catalog, not the plugin — so it confirmed the false success, and a re-run reported "already installed" and never corrected the state.

marketplace add exits 0, so this produced no exceptions at all. It appears nowhere in error tracking; it was found by inspecting a machine after a successful-looking run.

2. A failure reported nothing

spawnSync splits a failure across error, stderr and stdout, and sets none of them when the process merely exits non-zero. Three of four call sites read stderr alone, so a failure to spawn reported Codex plugin install failed: with an empty reason and filed an exception carrying nothing. 18 events across 18 users in 30 days, live on 5.52.4. removeServer already read result.error.

Of ~139 Codex install failures in 30 days (~132 users), roughly 100 are conditions the wizard cannot fix and reported anyway:

Cause Events / users
Codex wrapper spawn … ENOENT, broken install 63 / 63
Codex CLI too old — unexpected argument 'marketplace' 20 / 18
User's config.toml invalid — bad types, wire_api, model_catalog ~15
git clone failure, EPERM, ENOSPC, missing OPENAI_API_KEY ~8

Changes

  • Install the plugin. codex plugin add posthog@posthog after the marketplace is registered. Idempotent — twice exits 0.
  • Ask about the plugin, not the catalog. The install decision reads the STATUS column of codex plugin list -m posthog, via isPluginPresent. Users already in the broken state get the plugin on their next mcp add. Reading the CLI's own status rather than a filesystem proxy is deliberate: this defect was caused by trusting a proxy for the real thing. (Section 5 splits this from what discovery asks — the two are not the same question.)
  • Separate the two concerns. isMarketplaceRegistered keeps the config.toml check, used to skip a redundant marketplace add. Registration moved into registerMarketplace, carrying the stale-cache retry with it.
  • Remove both halves. removePlugin removes plugin and marketplace, acting when either is present, so a user left with a registered marketplace and no plugin still gets it cleared.
  • Read the whole failure. runCodex merges stderr and stdout, preferring stderr because that is what codex actually said, and falls back to describeExecFailure — the exit status, signal or timeout — when neither stream spoke or when the kill was ours. So a reason is never blank, at every call site that previously dropped error.
  • Classify environment failures. A CLI too old for plugins, a broken install, an unreadable config, permissions, a full disk, an unreachable GitHub each return a hint naming the fix instead of filing an issue nobody can action.
  • Group what remains. A constant exception message with stage and details in properties, and home directories replaced by ~, so one root cause is one issue instead of one per user.

expectedFailureHint and scrubHomePaths live in results.ts beside redactSecrets and summarizeFailure — the module that already owns turning raw CLI failure text into something reportable. The pattern table stays in codex.ts, since the wording is Codex's.

3. A frozen spinner

plugin marketplace add and plugin add clone git repositories and take seconds. Running them through spawnSync blocked the event loop, so the TUI spinner painted one frame and froze for the whole install — measured at 1 painted frame against 50 on an async path.

Every codex invocation now goes through runCodex, wrapping execFile. findCodexBinary stays synchronous: one fast call, and supportsPlugin is a synchronous contract. Verified against the real CLI: 28 event-loop ticks during an install/remove cycle that previously yielded none.

Reporting also prefers stderr over the error message. execFile leads its message with Command failed: <cmd>, and summarizeFailure shows the first line, so reporting the message would have shown the user our own invocation instead of what codex said. The message stays the fallback, since a process that never starts reports only there.

The spinner component itself rides along in #1294 (register the marketplace before the Claude Code plugin install), which carries it as one bullet — this PR makes it able to animate for Codex.

4. Hints that silenced our own failures

A hint is not a neutral reclassification — it takes the failure out of error tracking. So the cost of a pattern matching too much is asymmetric: one of our bugs becomes advice the user cannot act on, and the only evidence is that the exception count fell, which reads exactly like the fix working.

Three patterns claimed too much. Reproduced by running the table against the text each case produces:

Real cause What the user was told Reported?
Old CLI rejects --bearer-token-env-var during mcp add run codex plugin marketplace add PostHog/ai-plugin no
PostHog/ai-plugin's own manifest is broken (serde wording) fix your ~/.codex/config.toml no
PostHog/ai-plugin deleted or renamed check your network no

Every entry now declares the stages it applies to and the wording that disqualifies it (unless), and anything it cannot claim confidently goes back to being reported. Stage names are a union type, because a hint scoped to a stage nobody passes is silently dead — which is how marketplace add lost its hint while this was being written. A hinted failure emits wizard: mcp expected failure hinted, so over-matching stays countable once it leaves error tracking.

Two more reporting defects in the same layer:

  • runCodex fell back to error.message when neither stream spoke, and execFile always sets one — leading with Command failed: and the full binary path. The per-machine detail this PR exists to eliminate was reintroduced one layer down, and the exited with code fallback was unreachable. The guarding test passed new Error(''), a shape execFile never emits.
  • removeServer and the config.toml write path still interpolated their reason into the exception message, so a config path filed one issue per user. Both now report a constant message with the scrubbed detail in properties.

Also bounded: a 120s timeout, a 16MB output ceiling instead of execFile's 1MB default (a verbose clone hit it, and the kill read as codex rejecting the install), and the child's stdin closed, since a codex that waits on input never settles the promise. plugin add now reads codex's already-installed wording like every other spawn here.

5. One method, two questions

isPluginInstalled was asked two things by two callers, and one return value
cannot answer both.

getInstalledClients in index.ts asks whether a client has anything left to
remove — its comment says "for removal purposes". Section 1 narrowed the method
to the plugin alone, so a machine with our marketplace registered and no plugin
— the state this PR exists to correct — answered false. Codex never appeared
for removal, removePlugin never ran, and the orphaned catalog stayed for good.
removePlugin already knew how to clear it; nothing ever called it.

Widening the answer alone would have reintroduced the defect at the top of this
PR, because installPlugin asked the same method whether to skip the install.
So the two questions are now separate: isPluginPresent keeps the narrow answer
for the install decision and for removal's plugin step, and isPluginInstalled
reports either half for discovery.

This changes a contract section 1 chose on purpose. The behaviour it was chosen
for — a marketplace-only machine still getting the plugin installed — is intact
and still tested; only the question discovery asks has moved.

Also covers isMarketplaceRegistered's catch, which lost its test when
isPluginInstalled moved off config.toml. A fresh machine has no
~/.codex/config.toml, and an exception out of discovery would take down every
client's removal, not just Codex's.

6. Secrets in what a failed clone prints back

redactSecrets masked our own key shapes only, from a time when the text
reaching it came from CLIs we invoked ourselves. The plugin stages clone git
repositories and quote the user's own config, so the text is no longer only
ours: git echoes the remote it failed on, credentials and all, and runCodex
merges stdout into the details property that leaves the machine.

https://user:ghp_...@github.com/PostHog/ai-plugin.git matches no entry in
EXPECTED_FAILURES, so nothing turns it into a hint first and the token is
reported verbatim. Added: git remote userinfo, GitHub tokens, and the OpenAI
keys codex already reports on — each a shape a CLI here actually prints, not a
guess at every possible secret. A test pins that an ordinary URL survives
untouched, since over-redaction hides the cause we report for.

Not changed

url is not supported for stdio in mcp_servers.posthog (5 events / 4 users) looked like the config we write being malformed. Writing exactly what writeServerSection emits into a sandbox CODEX_HOME and running codex-cli 0.154.0 against it lists the server as enabled, so the config is valid — those events are Codex versions predating URL-based servers. Nothing to fix.

A failed probe reads as "not installed". isPluginInstalled returning false for a probe that merely failed lets removePlugin skip plugin remove, clear the marketplace under it, and report success. Left alone here: the same shape is at six sites, three outside this diff, including MCPClient.ts:59 which five clients inherit. Fixing one site would leave five clients lying and make this one inconsistent with the convention. Tracked in #1298.

Test plan

  • pnpm test — 3157 pass. pnpm typecheck, pnpm lint clean (the codex.test.ts non-null-assertion warning is gone).

  • results.test.ts: scrubHomePaths over unix, windows and no-home text; expectedFailureHint first-match, no-match and empty-text cases.

  • codex.test.ts install/removal: plugin installed after the marketplace is registered; a user who already has the marketplace still gets the plugin; an already-installed plugin left alone; a failing plugin add reported rather than claimed as success; removal clears plugin and marketplace; removal still clears a marketplace with no plugin; nothing to do when neither is present.

  • codex.test.ts reporting: a spawn error yields an actionable reason; an unrecognised spawn error reports its own text; a bare non-zero exit reports its status; stdout-only failures are read; one case per environment class hints without reporting; an unexpected failure reports under the constant message with ~-scrubbed detail.

  • The isPluginInstalled tests replace three that asserted the old contract. One of the replacements has since been re-pointed by section 5: a registered marketplace with no plugin now reports true, because that is what discovery needs, with the install decision covered separately. Four install tests were rewritten for the two-step flow: a blanket status: 1 mock now lands on the marketplace-add stage, so they route per-command to keep their original intent.

  • results.test.ts hint scoping: a scoped entry preferred on its stage, skipped on another, ignored when no stage is given, and reporting when only a mismatched scoped entry exists; unless hinting and disqualifying.

  • codex.test.ts what the table refuses to claim: no plugin advice when an old CLI rejects an mcp add flag; a broken plugin manifest reported rather than blamed on the config (3 wordings); a missing plugin repository reported rather than blamed on the network; an unrecognised failure that only mentions config.toml still reported; a genuine config failure still hinted; hinted failures counted, with ~-scrubbed detail.

  • codex.test.ts when neither stream spoke: the exit status reported instead of our own invocation; no home directory in the reported detail; a bearer token echoed back is redacted; every invocation bounded by a timeout and an output ceiling; our own kill outranking the output it interrupted.

  • Mutation checks: stubbing out the plugin add call fails 6 tests; restoring the old result.status !== 0 guard and stderr-only read fails the 4 covering that defect. For section 4, each of ten behaviours was reverted in turn — nine fail at least one test. The tenth, closing the child's stdin, is not covered: the mocked execFile never creates a child, so it rests on the mechanism and would need an integration test.

  • Section 5: a registered marketplace with no plugin reported for removal; nothing reported when neither is present; an unreadable config.toml treated as no marketplace rather than throwing. Section 6: credentials in a git remote, bare GitHub tokens, another provider's key, and an ordinary URL left intact.

  • Mutation checks for sections 5 and 6, each failing at least one test: narrowing discovery back to the plugin alone; widening removal's plugin step; widening the install check (the original defect); letting the config read throw; and restoring the two original redaction patterns.

  • Against the real codex-cli 0.155.1, in throwaway CODEX_HOME sandboxes, confirming the contracts this PR depends on: plugin add before marketplace add exits 1 (plugin \posthog` was not found in marketplace `posthog`), so the ordering is load-bearing; plugin addtwice exits 0;plugin removeon an absent plugin exits 0; and the STATUS column readsnot installed/installed, enabled`, lowercase.

  • Against the real codex-cli 0.154.0, in throwaway CODEX_HOME sandboxes:

    Starting state before installPlugin after re-run removePlugin final
    clean machine false {success:true} true alreadyInstalled {success:true} false
    marketplace registered, plugin missing false {success:true} true alreadyInstalled {success:true} false

    The second row is the state this fixes; the old code reported true for it and did nothing.

Acceptance

The install defect is not measurable from error tracking — it exits 0. Verify by inspection:

codex plugin list -m posthog   # expect: installed, enabled

The blank-reason defect is measurable:

SELECT count(), uniq(distinct_id) FROM events
WHERE event = '$exception' AND properties.team = 'docs-and-wizard'
  AND properties.build = 'prod'
  AND properties.$exception_values = '["Codex plugin install failed: "]'
  AND timestamp >= '<release date>'

Expect zero, and the remainder should collapse toward a single fingerprint.

Total Codex install exceptions will not fall by the full ~100 projected from the original table. Section 4 moved three classes back into reporting — a broken plugin manifest, a missing plugin repository, and any failure that merely mentions config.toml — so those now surface as Codex plugin install failed rather than being hinted away. That is the intended direction: a hint that hides our own bug is worse than an exception. Watch wizard: mcp expected failure hinted alongside the exception count; a hinted class growing without a matching support signal is the tell that a pattern has started over-matching again.

🤖 Generated with Claude Code

`spawnSync` splits the cause across `error`, `stderr` and `stdout`, and sets
none of them when the process merely exits non-zero. The install, MCP add and
plugin uninstall paths read `stderr` alone, so a failure to spawn reported
`Codex plugin install failed:` with an empty reason and filed an exception
carrying nothing. `removeServer` already read `error`.

Classify failures that live in the user's environment — a CLI too old for
plugins, a broken install, an unreadable config, permissions, a full disk, an
unreachable GitHub — and return a hint naming the fix instead of reporting
them.

Report what remains under a constant message, with the varying detail in
properties and home directories replaced by `~`, so one root cause stays one
issue rather than one per user.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gewenyu99
gewenyu99 requested a review from a team as a code owner September 22, 2026 01:10
@github-actions

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci ai-observability
  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci replay-vision
  • /wizard-ci revenue
  • /wizard-ci self-driving
  • /wizard-ci warehouse
  • /wizard-ci warehouse-seeded

Test an individual app:

  • /wizard-ci ai-observability/anthropic
  • /wizard-ci ai-observability/google-adk
  • /wizard-ci ai-observability/groq
Show more apps
  • /wizard-ci ai-observability/manual-capture
  • /wizard-ci ai-observability/openai
  • /wizard-ci ai-observability/openai-agents
  • /wizard-ci ai-observability/opentelemetry
  • /wizard-ci ai-observability/vercel-ai
  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/flutter
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci replay-vision/javascript-node
  • /wizard-ci replay-vision/next-js
  • /wizard-ci replay-vision/react-vite
  • /wizard-ci revenue/stripe
  • /wizard-ci self-driving/astro
  • /wizard-ci self-driving/fastapi
  • /wizard-ci self-driving/nuxt
  • /wizard-ci self-driving/react-router
  • /wizard-ci self-driving/sveltekit
  • /wizard-ci warehouse/monorepo-env
  • /wizard-ci warehouse/multi-source-next
  • /wizard-ci warehouse/stripe-node
  • /wizard-ci warehouse/zero-source
  • /wizard-ci warehouse-seeded/next-stripe
  • /wizard-ci warehouse-seeded/next-stripe-declined

Test against a Context Mill branch:

  • /wizard-ci all context-mill:my-branch

Add context-mill:<branch> to any command above to pin the Context Mill branch. It defaults to main.

Results will be posted here when complete.

`installPlugin` ran `codex plugin marketplace add` and stopped. That registers
the catalog; it does not install from it, so `codex plugin list` reported
`posthog@posthog  not installed` while the wizard reported success. The command
exits 0, so nothing was ever captured.

`isPluginInstalled` read `[marketplaces.posthog]` from config.toml — the
catalog, not the plugin — so a re-run reported "already installed" and never
corrected the state. It now asks `codex plugin list -m posthog`.

`removePlugin` removes the plugin and the marketplace, and acts when either is
present, so a user left with a registered marketplace and no plugin still gets
it cleared.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gewenyu99 gewenyu99 changed the title fix(mcp): report the real cause when a Codex command fails fix(mcp): install the Codex plugin, and report the real cause when Codex fails Sep 22, 2026
gewenyu99 and others added 2 commits September 21, 2026 21:33
`plugin marketplace add` and `plugin add` clone git repositories and take
seconds. Running them through `spawnSync` blocked the event loop, so the TUI
spinner painted one frame and froze for the whole install.

Every codex invocation now goes through `runCodex`, which wraps `execFile`.
`findCodexBinary` stays synchronous: it is one fast call, and `supportsPlugin`
is a synchronous contract.

Prefer stderr over the error message when reporting a failure. `execFile` leads
its message with `Command failed: <cmd>`, and the TUI shows the first line, so
reporting it would show the user our own invocation instead of what codex said.
The message remains the fallback, since a process that never starts reports
only there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hinting takes a failure out of error tracking, so a pattern that claims
too much retires one of our bugs into advice nobody can act on — and the
only evidence is that the exception count fell, which reads as the fix
working.

Three patterns claimed too much. Serde wording (`invalid type`, `is no
longer supported`, `must contain at least one`) says nothing about whose
file codex choked on, so a broken PostHog/ai-plugin manifest was blamed
on the user's config.toml. `git clone ... failed` matched a deleted or
renamed PostHog/ai-plugin and told the user to check their network. And
the "too old for plugins" hint fired during `mcp add`, where the rejected
argument is a flag we passed, naming a plugin command that has nothing to
do with what failed.

Each entry now says which stages it applies to and what disqualifies it,
and anything it cannot claim confidently goes back to being reported. The
stage names are a union, because a hint scoped to a stage nobody passes
is silently dead — which is how `marketplace add` lost its hint while
this was being written. A hinted failure now emits an event, so
over-matching stays countable after it leaves error tracking.

`runCodex` reported `error.message` when neither stream spoke, and
execFile always sets one: it leads with `Command failed:` and the full
binary path. That path contains the user's home directory, so the detail
this change exists to make constant was per-machine. Report the exit
status instead. A kill of ours now outranks whatever the streams got out
before we cut them off, rather than reading as codex rejecting the
install.

Bound every invocation: a 120s timeout, a 16MB output ceiling instead of
execFile's 1MB default, and stdin closed, since a codex that waits on
input never settles the promise. `plugin add` now reads codex's
already-installed wording like every other spawn here.

`removeServer` and the config.toml write path interpolated their reason
into the exception message, filing one issue per user; both now report a
constant message with the scrubbed detail in properties.

Nine of the ten behaviours above are mutation-verified: reverting each
one fails at least one test. Closing the child's stdin is not — the
mocked execFile never creates a child — so it rests on the mechanism.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

// Registering the catalog does not install from it. Skipping this left the
// user with a marketplace, no skills, and a wizard reporting success.
const result = await runCodex(binary, ['plugin', 'add', PLUGIN_REF]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: Unverified remote plugin installation

An attacker who compromises the marketplace repository can publish malicious skills and have the wizard automatically install them into Codex, where they are loaded with the user's Codex tool permissions. Resolve the marketplace and plugin to an immutable, reviewed revision or verify a trusted digest/signature before enabling the plugin.

@veria-ai

veria-ai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

PR overview

This pull request updates the Codex MCP setup flow to install the Codex plugin and surface the underlying cause when Codex installation fails.

One security issue remains open after one was addressed. The setup flow installs a plugin from a remote marketplace without pinning or verifying its contents, so a compromised marketplace could cause malicious code to be loaded with the user’s Codex tool permissions.

Open issues (1)

Fixed/addressed: 1 · PR risk: 8/10

`redactSecrets` masked our own key shapes only — `Bearer ...` and
`ph[xspc]_...` — from a time when the text reaching it came from CLIs we
invoked ourselves. The plugin stages clone git repositories and quote the
user's own config, so the text is no longer only ours: git echoes the
remote it failed on, credentials and all, and `runCodex` now merges stdout
into the `details` property that leaves the machine.

`https://user:ghp_...@github.com/PostHog/ai-plugin.git` matches no entry in
`EXPECTED_FAILURES`, so nothing turns it into a hint first and the token is
reported verbatim.

Each added pattern is a shape a CLI here actually prints — git remote
userinfo, GitHub tokens, and the OpenAI keys codex already reports on —
rather than a guess at every possible secret. A test pins that an ordinary
URL survives untouched, since over-redaction hides the cause we report for.

Mutation-verified: restoring the two original patterns fails 3 of the 4
new tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@sarahxsanders sarahxsanders left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

},
{
match:
/EACCES|EPERM|permission denied|read-only file system|not permitted/i,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not blocking, but my robot found this

permission denied here matches git's SSH clone failure, could ass an unless the way you did on the network entry below. more of a cheap guard than something to worry about

gewenyu99 and others added 2 commits September 22, 2026 10:47
`isPluginInstalled` answered about the plugin alone, and `index.ts` asks it
to decide whether a client has anything left to remove. A machine with our
marketplace registered and no plugin — the state this branch exists to
correct — answered false, so Codex never appeared for removal and
`removePlugin` never ran. The orphaned catalog stayed for good, and
`removePlugin` already knew how to clear it.

Widening that answer alone would have reintroduced the defect at the top of
this branch, because `installPlugin` asked the same method whether to skip
the install. They are two questions: what is left on the machine, and what
is actually serving. So `isPluginPresent` keeps the narrow answer for the
install decision and for removal's plugin step, and `isPluginInstalled`
reports either half for discovery.

This changes a contract this branch chose on purpose — a marketplace-only
machine reporting false so the install would run. That behaviour is intact
and still tested; only the question discovery asks has moved.

Also covers `isMarketplaceRegistered`'s catch, which lost its test when
`isPluginInstalled` moved off config.toml. A fresh machine has no
`~/.codex/config.toml`, and an exception out of discovery would take down
every client's removal, not just Codex's.

Mutation-verified, each one failing at least one test: narrowing discovery
back, widening removal's plugin step, widening the install check, and
letting the config read throw.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gewenyu99
gewenyu99 merged commit 8c65238 into main Sep 22, 2026
20 checks passed
@gewenyu99
gewenyu99 deleted the fix/codex-install-failure-reporting branch September 22, 2026 18:23
gewenyu99 added a commit that referenced this pull request Sep 23, 2026
Brings in the eight main fixes since beccc79 (#1275, #1280, #1284, #1294,
#1295, #1301, #1302, #1305). One conflict: agentic detection keeps main's
two-attempt retry loop from #1305 and adds A1's progress emitter to the
agent it initializes.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
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.

2 participants