Skip to content

Native chat: say when Codex is signed out or the agent isn't installed, without disabling Send - #25666

Merged
brennanb2025 merged 50 commits into
mainfrom
brennanb2025/native-chat-send-gate
Oct 8, 2026
Merged

brennanb2025 merged 50 commits into
mainfrom
brennanb2025/native-chat-send-gate

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 28 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1378 $\color{#cf222e}{\Huge{\mathbf{−}}}$​27 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1351
Prod 62 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​953 $\color{#cf222e}{\Huge{\mathbf{−}}}$​104 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​849

ELI5

When Claude or Codex can't run because you're signed out or the program can't be found, a native chat let you type and press Send, and only then failed. Claude's message after the fact said "Sign in first." without saying how, and Codex said nothing about sign-in at all before you sent.

Now, when the computer running the chat has found that Codex isn't signed in, or that the Claude or Codex program can't be found, the chat shows a notice saying so and how to fix it, with an X to dismiss it. Send is never disabled by that check: the check can be wrong while the chat would actually work (details below), so you can always try. If a send does fail for one of these reasons, the failure stays on screen in clearer words, and the same reason is not said twice.

What Changed

What you see

  • A notice in the chat's notice card (the card right above the message box, where Orca already shows "Chat could not be started." and other chat errors):
    • Codex, your own login: "Codex isn't signed in. Run codex login."
    • Codex, an Orca-managed account: "This Codex account isn't signed in. Sign in again in Codex Accounts settings."
    • Program not found: "Claude wasn't found on the computer running this chat. Install it, or check its Command in Settings → Agents." (or Codex).
  • The notice has a Dismiss (X) button. Dismissing hides that exact message; any change of the problem (for example, from "not signed in" to "not found", or it clearing) spends the dismissal, so whatever shows next, including the first problem coming back, shows again.
  • It goes away on its own once the problem is fixed: while a notice is showing, the chat checks again when you come back to the Orca window and when a turn starts or ends, and a chat that starts successfully makes the computer running it re-check sooner. A successful start proves the program exists; for a Codex sign-in it only prompts that earlier re-check, and the check decides.
  • Send stays enabled in every case. Nothing about the composer changes.
  • Claude gets no "not signed in" notice before sending. You learn it when a send fails to start, as today, but in clearer words that depend on the account:
    • Your own login: "Claude isn't signed in. Run claude and sign in with /login, or choose an account in Claude Accounts settings."
    • An Orca-managed account: "This Claude account isn't signed in. Sign in again in Claude Accounts settings."
    • If the failed start was for a slash command, the message adds "Run /compact again." (with that command).
  • After a send that fails to start for one of these reasons, the failure is shown as before (the "Chat could not be started." line with Retry, or the failed-start row in the transcript). If the notice would say the same reason, the notice steps aside, so the reason appears once. A failed start that a later turn has run past no longer counts. Each failed attempt still gets its own line: a Claude chat that fails to start when opened and then again on each send shows the sign-in sentence once per attempt, because each is a separate failure.
  • If the check finds Codex signed out but still gets the model list (for example a gateway that works without an OpenAI login), the model picker keeps that list; only the notice shows.
  • The phone app is unchanged.

How it works

Orca already runs a short-lived check on the computer that runs the chat to list the agent's models. That check now also reports a typed reason when it can tell the agent can't run, carried as a new optional field on the model-list reply the chat already reads:

  • Codex: the check asks Codex for its account alongside the model list. Only "no account, and OpenAI sign-in required" counts as not signed in, and a model list it got anyway is still kept for the picker. When Orca keeps a shared copy of your Codex login, the check first refreshes that copy the same way starting a chat does, so a codex login done in another terminal is seen.
  • Missing program (Claude and Codex): only a real "file not found" from starting the exact program a chat would start counts.
  • Claude: no sign-in verdict from the check. The chat start's own refusal (unchanged rule) is the only Claude sign-in signal; it now carries which kind of account it was, for the clearer words.
  • The reason lives on the model-list failure record Orca already keeps in memory. It is never saved to disk, lasts 30 seconds, and the next read after that runs the check again. Only the check can set or clear it. Two things mark it stale early so the next read checks again: changing or re-signing an account in Settings, and a new chat under that account proving its start (a reconnect to a chat already running does not count). Plain listing failures (no reason) are dropped once they expire, so the record can't grow without bound.
  • While the check re-runs past those 30 seconds, the host answers at once with the reason it still holds, marked as being re-checked. A chat already showing that notice keeps it (no flicker), but a chat that isn't showing it waits for the check's answer instead of showing it. Without this, a brand-new chat opened right after signing in to Codex flashed the old "isn't signed in" notice for about half a second.
  • A program missing at chat start is now its own failure type, so the words after sending match the notice. Older Orca versions show the host's sentence unchanged.
  • Desktop: the chat keeps the latest reason and shows it as one more entry in its existing notice card. The entry is left out when the chat's failed start (the "could not be started" line, or the latest failed-start row in the transcript) already states the same reason.

Why not disable Send

An earlier version of this PR disabled Send while the check said "not signed in" or "not installed". It was dropped because the check can be wrong while the chat would work, and then Send would stay disabled with no way past it:

  • Saved chat Arguments never reach the check. A Codex chat configured to use a local model provider, or a Claude chat whose Arguments point at a settings file with an API key, works fine but the check says "not signed in".
  • The Claude check runs with no project folder, so a project's .claude/settings.json credentials are invisible to it. A per-account check can't answer a per-folder question.
  • Codex itself sends a signed-out chat (some gateways accept it); Orca never refused it before, so disabling Send would have been a new refusal.
  • Claude gateway setups that authenticate only through custom headers report "no login" while working.
  • After signing in, Send would stay disabled for up to 30 seconds and until the next re-check.

A notice you can dismiss tells you early when the check is right and costs nothing when it is wrong. This PR deletes the Send gate entirely (the gate, its Enter and goal-mode checks, the Send tooltip reason, the hidden-line rule for these two causes, and the Claude startup probe), so it is much smaller than the previous version.

Differences from the common pattern

The common pattern shows a dismissible provider-status error at the top of the chat for "Codex not signed in" and "program not found", keyed by the message so a changed status shows again, never disables Send for it, and has no pre-send Claude sign-in check. This PR follows that mechanism. Differences:

  • Intended: the notice sits in Orca's existing chat notice card above the message box instead of at the top of the transcript. That card is where Orca already says a chat could not start, so one surface holds both and the "say it once" rule compares them directly. Easy to move if you'd rather have it at the top.
  • Intended: any change of the verdict, including it clearing, resets the dismissal, so a problem that comes back shows again.
  • Intended: the notice has no button of its own (no "Sign in" or "Retry"); its words carry the fix, and Send is always available to try again.
  • Intended: a chat that starts successfully marks the held reason stale instead of clearing it. A start proves the program exists, but for a Codex sign-in it is only a prompt to re-check sooner; the next read's check decides, so a wrong "fixed" is never trusted either.
  • Intended: the notice steps aside when a failed start already states the same reason; post-send failures are never hidden.
  • Intended: a chat's own model listing can't clear the reason; only the check can (a signed-out Codex chat can still list models).
  • Intended: changing an account marks the reason stale rather than deleting it, so the next read re-checks.
  • Intended: while a notice is showing, the chat re-reads when the Orca window regains focus and when a turn starts or ends; Orca has no live "status changed" push for this yet. No polling.
  • Intended: a sign-out that happens after a healthy check is noticed only when the model list next refreshes (up to 10 minutes, the existing cadence); until then the send shows Codex's own error. A notice is a hint, and healthy chats get no extra checks.
  • Intended: the headless Node host (orcad) doesn't mark reasons stale on an account change; the 30-second lifetime corrects it.
  • Intended: the check runs without the saved Arguments, the same as the chat itself: native chat does not use Settings → Agents → Arguments (they apply when the CLI is launched in a terminal). fix(native-chat): chats ignore saved Arguments; they are for terminal launches #26506 removes chat's remaining use of them.

Linked Issue

Maintainer work; no linked issue.

Visual Proof

Hidden-window QA on Windows, build 058f39269b0, stand-in Claude and Codex (never the real programs), real config files unchanged before and after.

What Screenshot
Codex not signed in: notice above the message box, Send enabled with a draft notice
After Dismiss: gone, stays gone across tab switches and focus dismissed
After codex login (stand-in signed in): cleared, next send answered cleared
Claude signed out: no notice; the start that runs when the chat opens fails and says so once claude open
Claude still signed out, sent again: a second failed start, its own line claude again
Claude signed in: the same chat starts and replies claude started
Unknown status: no notice, both agents reply unknown
Orca-managed Codex account: managed wording managed
Notice held 60 s: no flicker (60 of 60 samples) hold

Re-check on the fixed build 8a5045f7ac9 (same machine and setup):

What Screenshot
Signed out, brand-new chat: notice appears (177 ms after the tab exists) new chat notice
Signed out, existing chat through a background re-check: never blinks off (99 of 99 samples) held
Signed in, brand-new chat 210 s after the last signed-out check: no flash at ~1 s and ~10 s (0 of 101 samples, while the host re-checked and found "signed in") no flash 1s no flash 10s

Testing

  • I manually tested these changes (hidden-window QA on Windows with stand-in agents; see Visual Proof)

  • Automated tests added/updated

  • New: the notice shows for Codex signed out and for a missing Claude or Codex, Send still sends with a verdict present, dismissal holds for the same verdict and resets on any change, the notice steps aside when the failed start or the transcript's latest failed-start row states the same reason (but not a different one, and not once a turn has run since), the Codex check keeps its model list beside a signed-out reason, a chat proving its start (at acquire, or by its later "started" event) marks the reason stale so the next read re-checks while a reconnect to a running chat does not, the chat re-reads when a turn starts and when it ends, and expired reason-less failures are dropped. The Claude check sends only the model-list request and reports no sign-in verdict.

  • Rules confirmed by removing each one and seeing its test fail (14): a reason the host is still re-checking is never newly shown (the flash QA found); start-proof at acquire tells the catalog; only an acquiring attach counts, not a re-attach; start-proof on the "started" event tells the catalog; the catalog marks the reason stale on a start; the chat re-reads on turn start and end while a notice shows; the store keeps the reason beside a probe's list; the Codex check returns its list when signed out; dismissal resets on any verdict change; a turn after a failed start voids it for say-once; expired reason-less failures are dropped; the say-once rule itself; the notice is wired into the card; the window-focus re-read.

  • Kept from before: the host rules (only the check sets or clears a reason, 30-second lifetime, account-change staleness, Codex account read, missing-program typing) and the clearer post-send words.

  • Typecheck (node, web, cli), lint, the changed-code quality gate and the localization checks pass.

  • The repo's real-Claude test suite passed on the final head 5239f312b73 (12 of 12), after the main merge.

  • Manual QA found one bug, now fixed: a brand-new chat opened right after signing in to Codex flashed the old notice for about half a second. The fix has a test that fails without it, and the live re-check on 8a5045f7ac9 passed (see Visual Proof).

  • Not verified: "program not found" live (the QA machine has real Claude and Codex installed that Orca would find, so it is covered by unit tests only), the real Codex program, Linux, macOS rendered UI, a remote host over SSH, the phone app.

Agent skill upstream boundary

  • Not applicable: no agent skill changes.

Notes

Author: @BrennanKB5.

Ensure no issues in: security (the Codex login file is only read for presence, never logged or sent), cross-platform (missing-program detection only trusts a real "file not found"), remote/SSH (the computer that runs the chat answers for itself; an older one simply never sends a reason, so no notice), mobile (unchanged; it ignores the new optional field), backwards compatibility (all new fields are optional), performance (no new processes for healthy chats; while a verdict is held, even after Dismiss, the chat re-reads on window focus and on each turn start and end, but a read is answered from memory and the check itself runs at most once per 30 seconds per account, plus once after each new chat start).

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@brennanb2025
brennanb2025 marked this pull request as ready for review October 5, 2026 22:36
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Claude and Codex catalog probes now identify signed-out accounts and missing executables. Catalog reads expose expiring availability observations while preserving cached model listings. Account kind is carried through structured session failures and provider-specific messages. Native chat uses availability to block sending, present guidance, and adjust duplicate start-failure and retry notices.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to 4560f

Run the required real Claude CLI tests before merging. The reported automated checks do not validate this changed startup path against a real CLI.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 351a4

The inspected changes preserve existing account isolation and do not show a new security issue. Remaining uncertainty concerns compatibility and side effects across supported software versions, rather than a confirmed authorization bypass.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The established changed exposure is account-specific status influencing sending and retry presentation for the selected host context. Inspected consumers do not use the new account label to select credentials, grant privileges, or redirect execution into another account.

Trust Boundaries and Controls

  • observed — Host-owned launch resolution retains durable-record, provider, execution-host, account-home, managed-account, and resume-identity checks. Account kind is derived from the existing authentication policy rather than supplied as launch authority by an acquisition caller.
  • inferred — Provider output crosses into bounded status guidance, not a new authentication authority. The desktop gate and fallback text-dispatch check supplement provider/session authentication; explicit commands and goal-mode behavior must not be interpreted as authenticated solely because this observation is absent.

Resilience and Maintainability Implications

  • observed — Observation recovery has explicit lifecycle boundaries: expiry clears and refreshes status, account changes revise request identity, hidden views invalidate generations, and late replies must pass generation checks. This limits stale-account control drift during concurrent requests and recovery.

Hardening Proposals

  • proposed — Validate the newly added initialization request against supported CLI versions for hook or configuration side effects and API-key-only authentication metadata before describing discovery as side-effect-free. This is a dependency-boundary assurance proposal, not an observed vulnerability.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 60 functions across 79 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main user-facing change: availability notices for signed-out or missing agents while keeping Send enabled.
Description check ✅ Passed The description is complete and follows the template. It explains the user impact, implementation, rationale, testing, visual proof, limitations, and checklist status.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The new account-aware sign-in copy is lost on one path: a message the host records and then rejects drops its account before the desktop words it, so a managed account is shown the system instructions. Details below.

Reviewed changes

  • Composer availability gate — the host catalog's unavailable disables Send (click, keyboard, and dispatch) and explains itself in the Send tooltip; the send-state extraction into native-chat-composer-send-state.ts is behavior-preserving.
  • Host-side evidence — the Claude and Codex model probes now report signed-out / missing-CLI as typed AgentModelCatalogUnavailableErrors; the store keeps that evidence for 30s under an origin-keyed refresh and the service surfaces it as an optional unavailable field.
  • Account-aware failure copy — notSignedIn now selects managed/system Claude and Codex instructions, with account carried through refusal details, the failure fact, and the durable refusal.
  • Hidden automatic rows — desktop filters signed-out / missing-CLI start-failure rows and the automatic launch Retry line, leaving the unsent message's own rejection reason.

⚠️ Managed-account sign-in copy is dropped on the outbox copy of a rejection

structuredAgentSessionRejectedFailure reads the whole rejection fact (which now carries account) but rebuilds it as { kind, attachment }, and StructuredAgentSessionRejectionFact picks only 'kind' | 'attachment'. When the desktop words a rejected message before its journal row loads, structuredAgentSessionAttemptFailureParts has no recorded fact and notSignedIn is not in WORDED_FROM_WHOLE_FACT, so agentSessionSignInCopyId(provider, undefined) chooses the system copy. A managed Claude or Codex account is therefore told to "Run claude and sign in with /login…" instead of "This account isn't signed in. Sign in again in Claude/Codex Accounts settings." — the exact distinction this PR adds.

Technical details
# Managed account loses its sign-in copy on the outbox rejection

## Affected sites
- `src/shared/structured-agent-session-outbox.ts:54` — `StructuredAgentSessionRejectionFact` picks only `'kind' | 'attachment'`, omitting the new `account`.
- `src/shared/structured-agent-session-outbox.ts:67` — `structuredAgentSessionRejectedFailure` reads `account` via `readWholeAgentSessionFailureFact` then rebuilds `{ kind, attachment }`, discarding it.
- `src/renderer/src/components/native-chat/structured-agent-session-delivery-notices.ts:132` — `deliveryNoticeText` calls `structuredAgentSessionAttemptFailureParts(entry.lastFailure, context)` with no `recorded` fact.
- `src/shared/structured-agent-session-send-disposition.ts:191` — with no `recorded`, `fact = failure.rejection` is account-less; `:193` only short-circuits for `providerRejected`/`startFailed`/`restartFailed`, so `notSignedIn` falls through to `:196`.
- `src/shared/agent-session-failure-words.ts:208` — `agentSessionSignInCopyId(provider, fact.account)` with `account === undefined` returns the `*SystemNotSignedIn` key.

## Required outcome
A managed account's rejected-message notice must show the managed sign-in copy, matching what `hostRejectionNoticeText` shows once the journal row loads and what the disabled Send tooltip shows.

## Suggested approach (optional)
Add `account` to `StructuredAgentSessionRejectionFact`'s `Pick` and to the object rebuilt in `structuredAgentSessionRejectedFailure`; or add `notSignedIn` to `WORDED_FROM_WHOLE_FACT` so the host's already-correct `failure.reason` sentence is printed when the whole fact is unavailable.

## Reachability
A `notSignedIn` start failure rejects the queued submission with the account-bearing fact (`structured-agent-session-failure-text.ts:70`, `structured-agent-session-start-failure-row.ts`), the reconcile/`disposeStructuredAgentSessionSendResult` stores it via `structuredAgentSessionRejectedFailure`, and desktop draws it through `deliveryNoticeText` until the journal row loads.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

} | null>(null)
// oxlint-disable-next-line react-doctor/effect-needs-cleanup -- The replaceable expiry handle is cleared before rearming and by the returned cleanup.
useEffect(() => {
if (!enabled || !optionCatalog || (agent !== 'claude' && agent !== 'codex')) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This early return skips clear(), and the cleanup only cancels the expiry timer, so observation survives a hide. Since waitKey does not change on hide/show, the first render after a hidden pane is revealed re-applies the stale blocker (and its stale account) before the passive effect clears it. Calling clear() here (or resetting observation in the cleanup) keeps the 30s lifetime enforced across a hide.

Re-applies the send gate onto main's catalog store, which now keys in-flight
listings by the lister (probe vs a chat's own connection) and lets a waiting
picker answer from whichever catalog lands first:

- Availability evidence moves to agent-model-catalog-availability.ts; only a
  probe lister sets or clears it, derived from the lister instead of an origin key.
- A waiting picker with no catalog also answers once the account is known
  signed out or missing its CLI; with a catalog it waits on the probe, and a
  catalog that lands before the account check reports the check still running
  so the renderer waits once more.
- The background Codex catalog refresh records its failures as a live listing.
- Main's API-key rule for Claude moves into the shared signed-out check, so the
  probe never disables Send for an API-key user.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8e20d6ea-8334-4b51-b8d5-160fd9ea9976
📥 Commits

Reviewing files that changed from the base of the PR and between ca80376 and 70502f6.

📒 Files selected for processing (50)
  • src/main/claude/claude-model-catalog-probe.test.ts
  • src/main/claude/claude-model-catalog-probe.ts
  • src/main/claude/claude-structured-init-proof.ts
  • src/main/claude/claude-structured-launch-resolution.ts
  • src/main/claude/claude-structured-session-acquisition.ts
  • src/main/codex/codex-model-catalog-probe.test.ts
  • src/main/codex/codex-model-catalog-probe.ts
  • src/main/codex/codex-structured-background-catalog.ts
  • src/main/codex/codex-structured-session-options-catalog.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-availability.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-live-availability.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-service.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-service.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-store.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-store.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-failure-text.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-start-failure-writer.test.ts
  • src/main/native-chat/agent-session-wire/structured-conversation-command.test.ts
  • src/main/runtime/claude-structured-session-integration.test.ts
  • src/main/runtime/orca-runtime-get-worktree-ps.ts
  • src/main/runtime/rpc/methods/structured-agent-session-options-read.test.ts
  • src/main/runtime/structured-agent-model-catalog-wiring.ts
  • src/main/text-generation/commit-message-model-discovery.ts
  • src/renderer/src/components/native-chat/NativeChatComposer.tsx
  • src/renderer/src/components/native-chat/NativeChatComposerActions.test.tsx
  • src/renderer/src/components/native-chat/NativeChatComposerActions.tsx
  • src/renderer/src/components/native-chat/NativeChatLaunchRetry.tsx
  • src/renderer/src/components/native-chat/NativeChatStructuredSession.launch-lifecycle.test.tsx
  • src/renderer/src/components/native-chat/NativeChatStructuredSession.test-harness.tsx
  • src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx
  • src/renderer/src/components/native-chat/native-chat-composer-types.ts
  • src/renderer/src/components/native-chat/structured-agent-session-message-projection.availability.test.ts
  • src/renderer/src/components/native-chat/structured-agent-session-message-projection.ts
  • src/renderer/src/components/native-chat/use-host-model-catalog-upgrade.test.tsx
  • src/renderer/src/components/native-chat/use-host-model-catalog-upgrade.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-options.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session.ts
  • src/renderer/src/i18n/en-runtime-required.json
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/shared/agent-session-availability.test.ts
  • src/shared/agent-session-availability.ts
  • src/shared/agent-session-failure.ts
  • src/shared/agent-session-refusal-details.ts
  • src/shared/agent-session-refusal-notice.test.ts
  • src/shared/agent-session-wire.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/main/native-chat/agent-session-wire/structured-agent-session-start-failure-writer.test.ts
  • src/main/native-chat/agent-session-wire/structured-conversation-command.test.ts
  • src/renderer/src/i18n/en-runtime-required.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/ja.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.

Comment on lines +75 to +82
initialization: unknown,
accountKind?: AgentSessionAccountKind
): AgentSessionAcquisitionRefusal | null {
const account =
isRecord(initialization) && isRecord(initialization.account) ? initialization.account : {}
// An API key (ANTHROPIC_API_KEY or a Console /login key) reports tokenSource "none".
const apiKeySource = readClaudeFrameString(account, 'apiKeySource')
return readClaudeFrameString(account, 'tokenSource') === 'none' &&
(apiKeySource === null || apiKeySource === 'none')
return claudeInitializationSignedOut(initialization)
? new AgentSessionAcquisitionRefusal(
'Claude is not signed in for the selected account. Sign in with the Claude CLI for this CLAUDE_CONFIG_DIR, then retry.',
'notSignedIn'
'notSignedIn',
accountKind

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

git diff --name-status b922342199bb4c816072deb42cc58cf755420488 70502f6ecfd3cc7ff16dedf171bad2f931d094f1 -- 'src/main/claude/claude-structured-*' && git diff --unified=5 b922342199bb4c816072deb42cc58cf755420488 70502f6ecfd3cc7ff16dedf171bad2f931d094f1 -- src/main/claude/claude-structured-init-proof.ts

Repository: stablyai/orca

Length of output: 2136


🏁 Script executed:

if ! command -v gh >/dev/null 2>&1; then
  echo "gh CLI unavailable"
  exit 0
fi
numbers=$(gh api repos/stablyai/orca/commits/70502f6ecfd3cc7ff16dedf171bad2f931d094f1/pulls --jq '.[].number') || exit $?
if [ -z "$numbers" ]; then
  echo "No associated pull request returned for reviewed commit"
  exit 0
fi
while IFS= read -r number; do
  [ -n "$number" ] || continue
  gh api "repos/stablyai/orca/pulls/$number" --jq '{number: .number, title: .title, body: .body}' || exit $?
done <<EOF
$numbers
EOF

Repository: stablyai/orca

Length of output: 7991


Run the required real Claude CLI tests.

This PR changes files under src/main/claude/claude-structured-*, and its description states that no real Claude CLI was run. Run ORCA_REAL_CLAUDE_CLI_TEST=1 pnpm test src/main/claude/claude-structured-real-cli.test.ts src/main/claude/claude-structured-real-cli-fold.test.ts.

Source: Coding guidelines

…ure record

The probe's "signed out" / "CLI missing" verdict now lives on the account's
existing model-catalog failure record instead of two extra maps, with the
failure TTL as its only lifetime:

- Only the probe sets or clears the verdict; a chat's own model listing
  (which works while signed out) never clears or renews it. An aged-out
  verdict makes the next read re-probe, however fresh the catalog.
- A healthy fresh catalog is no longer re-probed every 30 s; it keeps the
  normal 10 min fresh window. A Claude start refused as signed out ages the
  catalog so the next read re-probes.
- A waiting picker again answers from whichever catalog lands first and never
  waits on a probe. A read that still owes the probe's verdict says so, and the
  composer waits for it once with the new optional waitForAvailability param.
- The open-time start-failure line and its transcript row are hidden only
  while Send is disabled for the same reason; otherwise they show as before.
- A host verdict lifetime longer than the client's hold is clamped, not
  dropped. The catalog read watches only the account settings and the paired
  host's contact instead of every store write.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0e672af1-110f-4777-a4af-4136f1818572
📥 Commits

Reviewing files that changed from the base of the PR and between 70502f6 and 4560f22.

📒 Files selected for processing (35)
  • src/main/claude/claude-model-catalog-probe.test.ts
  • src/main/claude/claude-model-catalog-probe.ts
  • src/main/claude/claude-structured-session-acquisition.ts
  • src/main/claude/claude-structured-session-startup.test.ts
  • src/main/claude/claude-structured-session-startup.ts
  • src/main/claude/claude-structured-session-test-support.ts
  • src/main/codex/codex-model-catalog-probe.test.ts
  • src/main/codex/codex-model-catalog-probe.ts
  • src/main/codex/codex-structured-session-options-catalog.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-failures.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-live-availability.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-service.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-service.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-store.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-store.ts
  • src/renderer/src/components/native-chat/NativeChatLaunchRetry.tsx
  • src/renderer/src/components/native-chat/NativeChatMessageList.tool-stream-cost.test.tsx
  • src/renderer/src/components/native-chat/NativeChatStructuredSession.launch-lifecycle.test.tsx
  • src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx
  • src/renderer/src/components/native-chat/native-chat-start-failure-presentation.ts
  • src/renderer/src/components/native-chat/structured-agent-session-delivery-notices.ts
  • src/renderer/src/components/native-chat/structured-agent-session-message-projection.availability.test.ts
  • src/renderer/src/components/native-chat/structured-agent-session-message-projection.ts
  • src/renderer/src/components/native-chat/use-host-model-catalog-upgrade.test.tsx
  • src/renderer/src/components/native-chat/use-host-model-catalog-upgrade.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-delivery-notices.test.tsx
  • src/renderer/src/components/native-chat/use-structured-agent-session-delivery-notices.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-messages.test.tsx
  • src/renderer/src/components/native-chat/use-structured-agent-session-messages.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-start-failure-facts.test.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-start-failure-facts.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session.ts
  • src/shared/agent-session-availability.test.ts
  • src/shared/agent-session-availability.ts
  • src/shared/rpc-contract/structured-agent-session-params.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.

session,
facts: readClaudeStartupFacts({
account: launch.account,
catalogAccess: session.catalogAccess,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

git diff --name-status b922342199bb4c816072deb42cc58cf755420488 4560f220f604b2c445f6df55d4d8e9c117074ec4 && printf '\\n--- targeted diff ---\\n' && git diff --unified=12 b922342199bb4c816072deb42cc58cf755420488 4560f220f604b2c445f6df55d4d8e9c117074ec4 -- src/main/claude/claude-structured-session-acquisition.ts

Repository: stablyai/orca

Length of output: 7710


🏁 Script executed:

if command -v gh >/dev/null 2>&1; then gh pr list --repo stablyai/orca --state all --search 4560f220f604b2c445f6df55d4d8e9c117074ec4 --json number,title,body,url; else printf 'gh CLI unavailable\\n'; fi

Repository: stablyai/orca

Length of output: 8015


Run the real Claude CLI tests before merge.

The PR changes Claude structured-session startup code and reports that no real Claude CLI was run. The path instruction requires this test command:

ORCA_REAL_CLAUDE_CLI_TEST=1 pnpm test src/main/claude/claude-structured-real-cli.test.ts src/main/claude/claude-structured-real-cli-fold.test.ts

Source: Coding guidelines

…ilure

- The sign-in/CLI verdict is gated once where the session hands it to the
  composer: while a turn runs it neither shows nor refuses, so a follow-up
  queues as before; goal mode is held by the same gate. The start-failure line
  and row hide only while that same value is up.
- A start whose CLI was never found (a direct spawn's ENOENT, or the one a
  provider supervisor reports) is now a `cliMissing` failure with the disabled
  Send's sentence, instead of a generic "couldn't start" beside it. An older
  client shows the host's sentence for the new kind.
- Signed-out words for a slash command keep "Run /x again".
- The Claude catalog reads its models only from the reply to its own
  list_models request, never from the initialize reply.
- The Codex probe lists models while its account check runs; a signed-out
  verdict still wins.
- The missing-executable check moves to provider-process, out of native chat.
- Before the catalog probe's read-only app-server reads Orca's shared Codex
  home, it runs the same sync a launch and the usage poll run, so a
  `codex login` made in ~/.codex is seen at once instead of leaving
  "Run codex login" up until something else re-syncs (forever on a headless
  host). Per-account homes are left alone.
- A "no account" answer counts as signed out only when the home's auth.json is
  absent or holds no credential; one caught mid-write by a token refresh, or
  holding a credential, is unknown.
…ces it

- No gap while signed out: the host serves an aged verdict while the probe that
  re-derives it runs, and the composer never clears its last answer before a
  read; only an answer without a verdict, an error, lost contact or an account
  change does. Send stays disabled with its reason through every re-check.
- After sign-in the stale "isn't signed in" start-failure line stays hidden:
  a probe that found the account fine now says so (`accountVerified`), and the
  line shows only when the host has no answer at all.
- A Claude start refused as signed out, or any start whose CLI was never
  found, is recorded as that home's verdict, and the chat reads it once when
  that start failure arrives.
- The re-probe mark is read only where the catalog read starts its probe, so a
  Codex chat's own picker no longer re-lists for it on every read.
- Choosing an account or signing in again in Accounts settings re-derives the
  verdict at the next read; the account key now compares settings by value.
- A re-read that brings an identical catalog keeps the option state, so focus
  re-reads re-render nothing.
…blocks this chat

A chat is pinned to the Codex home it started in, so choosing another account
in Codex Accounts settings never unblocks it. The system-account sentence now
says only to run `codex login`, in every locale.
The sign-in verdict lived inside the model catalog's failure records, so
the store needed guards for who wrote a failure, which listing may clear
it, and whether a verdict was served while re-probing. Each guard fixed one
way a chat's own listing or an older probe overwrote the account's answer.

The answer now lives in AgentAccountStatuses: one in-memory, capped status
per account home (ready, not signed in, or CLI missing). Only the
session-less probe, a refused start and an account change write it. A
blocked answer older than 30 s, or one an account change marked, makes the
next read start the probe whatever else is listing, and it is served until
that probe answers. Older evidence never replaces newer. The catalog's
failure code is main's again, and agent-model-catalog-failures.ts is gone.

The wire carries one optional `availability` field in place of
`unavailable` and `accountVerified`. The renderer holds its answer per
host, agent and chat, re-reads only on that agent's account inputs, and
re-reads on a timer only while blocked. After an account change, Send
stays disabled with the previous words until the re-probe answers, then
changes once. Account switches and re-sign-ins mark the status before the
settings change a chat's read follows.
…2025/native-chat-send-gate

# Conflicts:
#	src/main/native-chat/agent-session-wire/structured-agent-session-failed-create-refusal.ts
#	src/main/native-chat/agent-session-wire/structured-agent-session-failure-text.test.ts
#	src/main/native-chat/agent-session-wire/structured-agent-session-failure-text.ts
#	src/renderer/src/components/native-chat/NativeChatLaunchRetry.tsx
#	src/renderer/src/components/native-chat/NativeChatStructuredSession.launch-lifecycle.test.tsx
#	src/shared/agent-session-refusal-details.ts
#	src/shared/agent-session-write-failure.ts
…e mid-probe, and back off while blocked

Round-4 review findings on the account status owner:

- An account switch or re-sign-in made while a probe was running left the
  old answer up for up to 30 s: the next read joined that probe, whose
  answer was then dropped as older than the change. A read now chains
  exactly one fresh probe after a probe that began before the change.
- Removing the selected Claude account swapped another sign-in into the
  shared config dir without marking the answer; it now marks it first.
- Evidence was ordered by wall-clock time, so a clock that stepped back
  froze a blocked answer. It is now ordered by a sequence and aged on a
  monotonic clock; a negative age reads as due.
- A chat left open while signed out started a CLI probe every 30 s.
  Probes that keep finding the same block now space out (30 s, 60 s,
  2 min, 4 min, then every 5 min), carried to the client as recheckInMs.
  An account change or a different refusal starts the hold over.
- The renderer dropped no settled wait from its leave set; it now does.
- A failed Claude probe logged its whole stdout, which now includes the
  initialize reply naming the account. Control replies are dropped from
  the log and the failure message.
- The account services take the status re-check as an injected
  onSignInChanged hook instead of importing the native-chat store.
- Imports moved below file headers; comments no longer say "ready"
  means re-checked after the failure.
…igned out or missing

What you see: in a native Claude or Codex chat whose CLI is signed out, or not installed on the
computer running the chat, Send is disabled and its tooltip (hover or keyboard focus) says how to
fix it, in words that match the account ("Codex isn't signed in. Run `codex login`.", "This
Claude account isn't signed in. Sign in again in Claude Accounts settings.", "Claude isn't
installed on the computer running this chat."). A message refused after sending gets the same
words. On desktop the open-time "Chat could not be started" line, its Retry and its transcript row
no longer repeat these two reasons, unless a message of yours is still waiting on that start.
An unknown answer never disables Send, and while a turn runs Send stays Stop and Enter queues.

How: the host's existing session-less model-catalog probe also says why no chat can start.
Claude's probe child sends initialize before list_models and applies the rule chat start already
uses (token source none with no API key); Codex's probe asks account/read beside model/list; a
spawn that finds no executable is "CLI missing". The probe's failure record in the catalog store
carries that reason, and every catalog reply carries it as an optional `unavailable` field. Only
a probe writes or clears it: a live chat's own listing (which a signed-out Codex still answers)
never does. It lasts the store's 30 s failure lifetime; after that the next read starts or joins
the probe, serving the held reason meanwhile, and only a read that already asks to wait gets the
probe's answer. A change to the managed-account settings expires it. Before probing the shared
Codex mirror home, the probe runs the same sync a launch does. The renderer keeps the latest
answer until the next one replaces it, and only while blocked re-reads on window focus and when
the chat gains focus. No status store, timers or new request params.
…2025/native-chat-send-gate

# Conflicts:
#	src/main/runtime/structured-agent-session-runtime.ts
#	src/renderer/src/components/native-chat/NativeChatComposerActions.tsx
#	src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx
#	src/renderer/src/components/native-chat/native-chat-composer-types.ts
…2025/native-chat-send-gate

# Conflicts:
#	src/main/claude/claude-structured-launch-resolution.ts
#	src/main/claude/claude-structured-session-startup.ts
#	src/main/native-chat/agent-session-wire/structured-agent-session-start-failure-writer.test.ts
…nd plain keyboard focus on the disabled Send

A chat whose account is signed out and has no model list no longer shows the model picker as
loading each time it re-checks the sign-in after 30 s: the re-check is for Send, so a reply that
already carries the verdict ends the picker's wait. The disabled Send's wrapper takes keyboard
focus (so its tooltip opens) without becoming a second button; the inner button keeps its label.
Trims tests that repeated rules other tests already prove.
…2025/native-chat-send-gate

# Conflicts:
#	src/main/claude/claude-structured-launch-resolution.ts
#	src/main/runtime/structured-agent-account-home.ts
#	src/renderer/src/components/native-chat/NativeChatComposer.tsx
#	src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx
#	src/renderer/src/components/native-chat/agent-session-failure-words-text.ts
#	src/renderer/src/components/native-chat/native-chat-structured-session-notices.ts
…hile a queued card is about to send

The composer shows Stop while the host's queue is about to send its next card, but the sign-in
gate stayed on in that window, so Enter was refused with no message.
@brennanb2025

brennanb2025 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Review summary (replaces the earlier summary, which described a version that disabled Send)

The problem

Native Claude and Codex chats gave no warning when the agent couldn't run. You typed, pressed Send, and only then got an error. Claude's error said "Sign in first." without saying how; Codex had no sign-in check at all. An earlier version of this PR disabled Send when a background check said "not signed in" or "not installed". A review of that version found the check can be wrong while the chat would work (saved Arguments and project-folder credentials never reach the check; Codex itself sends signed-out chats; some proxy setups report "no login"; and Send stayed disabled up to 30 seconds after signing in). So it was rebuilt.

What changes for you

  • Send is never disabled by this check.
  • When Codex isn't signed in, or the Claude or Codex program can't be found, a dismissible notice appears in the existing card above the message box, saying what to do.
  • Claude gets no early sign-in notice: the real chat start decides, and its failure now says how to sign in (different words for your own login and an Orca-managed account).
  • Each reason is said once: the notice steps aside when a failed start already says the same thing.

Review

  • Round 1 (rebuilt version): no P0/P1. Fixed: the notice could outlive the fix (a successful chat start now makes the next read re-check); a signed-out Codex check threw away the model list (kept now); dismissal reset only on clearing (now on any change); a Claude sign-in branch in the notice (removed); "isn't installed" copy gave no step (now "wasn't found … Install it, or check its Command in Settings → Agents"); the say-once rule counted old failures; expired failures could pile up in memory; the PR text overstated the tests.
  • Round 2: no P0–P2. Fixed: a reconnect to a running chat no longer counts as a start; comments and PR text now say a start proves the program exists, not a Codex sign-in; added the turn-end re-read test; removed a filter that could never fire.
  • Manual QA (below) found one bug, fixed: a new chat opened right after signing in to Codex flashed the old notice for about half a second. A reason the host is still re-checking is no longer newly shown; a chat already showing the notice keeps it.
  • Accepted as is: a signed-out Codex chat that sends can show both the notice and Codex's own error in the transcript (the notice carries the fix); a Claude chat that fails to start on open and on each send shows the sign-in sentence once per attempt (each is its own failure).
  • Deferred: the Codex check sees the chat's saved Arguments only once fix(native-chat): apply or refuse saved Codex Arguments instead of silently dropping them #26059 lands; until then a Codex user whose Arguments pick a non-OpenAI provider can see a wrong, dismissible notice.

What was verified

  • Typecheck (node, web, cli), lint, code-quality and localization checks; 843 tests in the 81 affected files; 14 rules each shown to fail its test when removed.
  • The repo's real-Claude test suite on the final head 5239f312b73: 12 of 12.
  • Full CI on 5239f312b73 after the one main merge: every job passed.
  • Hidden-window QA on Windows with stand-in agents (screenshots in the PR body): Codex notice wording, Send enabled and sending, dismiss holding, clearing after sign-in, Claude failing at start once per attempt and recovering after sign-in, unknown status showing nothing, managed-account wording, 60 s with no flicker. Real config files unchanged throughout.
  • Live re-check on 8a5045f7ac9: a brand-new chat after signing in shows no flash (0 of 101 samples while the host re-checked); a signed-out new chat still gets the notice; an existing chat keeps it through a re-check (99 of 99).
  • Known and accepted: a sign-out after a healthy check is noticed only at the next model-list refresh (up to 10 minutes, the existing cadence); the send shows Codex's own error meanwhile.

Not verified

  • "Program not found" live (the QA machine has real Claude and Codex installed that Orca would find); covered by unit tests.
  • The real Codex program, Linux, macOS rendered UI, remote hosts over SSH, the phone app.

…dict; show a dismissible notice instead

The host's pre-send check can be wrong while the chat would work (saved
Arguments it never sees, project settings it can't read, gateways that
accept a signed-out Codex), and a disabled Send left no way past it.

- Delete the Send gate: the composer, Enter and goal mode no longer read
  the verdict, and the Send tooltip reason and focusable wrapper go back
  to main.
- Show Codex not-signed-in and Claude/Codex not-installed as one entry in
  the chat's existing notice card. Dismissed per verdict; shows again
  when the verdict changes or clears and returns; steps aside when the
  failed start (launch line or latest failed-start row) says the same.
- Claude: drop the probe's signed-out verdict (initialize-on-probe stdin,
  request-id filtering, control-reply log scrubbing). The chat start's
  refusal, with its account kind, is the only Claude sign-in signal.
  Missing-CLI from the probe stays.
- Restore the failed-start line and transcript row for these causes
  (no more hiding on desktop) and drop the chat-root focus re-check; a
  window-focus re-read stays while a notice is showing.
@brennanb2025 brennanb2025 changed the title Disable native chat Send when the agent is signed out or missing Native chat: say when Codex is signed out or the agent isn't installed, without disabling Send Oct 7, 2026
@brennanb2025
brennanb2025 marked this pull request as draft October 7, 2026 19:11
…eep a signed-out Codex's model list

Review round 1 of the notice rework.

- A chat that proves its start (at acquire, or on its later "started"
  event) marks the catalog's held reason for that account due, so the
  next read re-probes instead of trusting it until the TTL; the chat
  re-reads when a turn starts or ends while a notice shows.
- The Codex probe keeps a model list it got beside a signed-out reason,
  so a gateway user with a wrong verdict keeps the picker list.
- Failure records move to their own module; an expired failure with no
  reason is dropped, and expiry drops reason-less failures.
- Notice: any verdict change resets the dismissal; signed-out is
  Codex-only; the say-once rule ignores a failed start a turn has run
  past.
- Missing-CLI copy: "{{agent}} wasn't found on the computer running this
  chat. Install it, or check its Command in Settings → Agents." (6
  locales).
…n; drop the unreachable Claude filter

Review round 2 of the notice rework.

- A re-attach to a live child proved nothing new, so only an attach that
  acquired a child which proved its start marks the reason due.
- Reword: a start proves the program exists; for a Codex sign-in it only
  prompts an earlier re-check.
- Drop the notice's Claude signed-out filter: no host sends that verdict.
- Tests: a re-attach does not notify; a turn ending re-reads once more.
… re-checking

After signing in to Codex, a new chat flashed the old "isn't signed in"
notice for about half a second: past its 30 s lifetime the host answers a
plain read with the held reason while its probe re-checks. A chat now
adopts such a reason only when it already shows it; otherwise the joined
read's answer decides.
…hat-send-gate

# Conflicts:
#	src/main/claude/claude-structured-launch-resolution.ts
#	src/main/runtime/orca-runtime-get-worktree-ps.ts
#	src/main/runtime/structured-agent-session-runtime.ts
#	src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx
#	src/renderer/src/components/native-chat/native-chat-structured-session-notices.ts
#	src/renderer/src/components/native-chat/structured-agent-session-delivery-notices.test.ts
#	src/renderer/src/i18n/en-runtime-required.json
@brennanb2025
brennanb2025 marked this pull request as ready for review October 8, 2026 07:47
@brennanb2025
brennanb2025 merged commit 5252884 into main Oct 8, 2026
37 checks passed
brennanb2025 added a commit that referenced this pull request Oct 8, 2026
Resolve against #25666 (signed-out / missing-CLI notice): a proven start still
tells the model catalog to re-check its held reason.

- Attach: an acquire mints the startup attempt, so the published block notes a
  start the acquire already proved (phase ready); a re-attach notes nothing.
- An accepted `started` readies the attempt, wakes delivery, then notes it. A
  stale child's `started` notes nothing.
- Main's persistStartedOptions is dropped: persistReportedOptions writes the
  same fields after delivery, only while the report is the newest word.
- endReleasedChild moves beside the post-acquire failure it serves, keeping the
  attach file within its line limit.
brennanb2025 added a commit that referenced this pull request Oct 8, 2026
…del-catalog-all-agents

Brings in #25666 (a probe's "not signed in" / "CLI missing" verdict on the
catalog's failure record). Both sides kept:

- Store: the verdict rides only discovery listings; a chat's live listing
  touches neither a plain failure nor a verdict. A superseded probe answer
  still settles the verdict. The listing-to-entry builder moves to the entry
  module to keep the store under its line budget.
- Service: every answer carries the verdict; a verdict past its TTL is
  re-probed through the same host-lifetime listing prewarm uses, and prewarm
  counts an aged verdict as due.
- Claude probe keeps its own list_models runner; a spawn whose executable is
  missing now says so, and the probe turns that into `cliMissing`.
- Codex probe registration passes the account kind and launch-home sync.
- Renderer: the verdict is reported without bringing back the picker wait.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant