Commit Graph
18698 Commits
Author SHA1 Message Date
Neil 4f0aaccc5f fix(repos): a catalog answer sent before an add no longer drops the project that add made (#37384) 2026-11-01 23:16:37 -08:00
OrcaWinandOrcaWin 5c27709ca3 test: remove 21 low-value test declarations (#37373)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-01 23:07:04 -08:00
Jinjing e6a9ce50e2 Preserve bounded tool evidence in chat handoffs (#36842)
* Preserve bounded tool evidence in chat handoffs

- Keep recent conversation and useful tool failure context without exporting huge outputs
- Safely summarize tool inputs and retain transcript order

* Preserve tool call IDs and block order in handoffs

* Preserve cross-message tool results in handoff context

* Prioritize latest tool results in handoff context

* Preserve bounded saved tool failures in handoff context

* Avoid reflective tool preview reads
2026-11-02 00:03:46 -07:00
Brennan Benson 4343dc212a fix(native-chat): report missing saved conversations once (#37189)
* fix(native-chat): report missing saved conversations once

* fix(native-chat): keep failures of answered Claude sends and translate the missing-conversation row

A Claude result settles its own send before the journal sees it, so a
failed send with no echo yet lost its owner and its failure row. The
result now carries the send it answered. Desktop and phone rows also
show the missing saved conversation sentence in the reader's language.

* fix(native-chat): keep failures of Claude results that name a user message

A root failure result that names a user message answered accepted input, so it keeps its row even when that send's waiter was retired, in doubt or ambiguously written. Only a result with no turn, no started send and no named user message (the pre-init missing-resume capture) is left to the host's final startup row. This replaces the per-frame settlement carry, so the acquisition and sign-in tests return to main.

Also ship the new missing-conversation sentence in the runtime English catalog.

* fix(native-chat): leave only a starting Claude session's failure to the startup row

A Claude failure result with no turn now writes no row only while its own
session is still opening (initialize and resume catch-up). Once open
succeeds, every failure result is reported as before, whatever send
evidence it carries, so per-send ownership checks are removed.

* fix(native-chat): fail a starting Claude session with the startup failure it reports

A turnless failure result that names no user input or resumed turn, arriving
while its own acquisition is still starting, now fails that acquisition's open
(initialize and resume catch-up) with Claude's own diagnostic; only then is its
provider row left to the host's final startup row. Results naming input, and
every result once open succeeds, keep main's reporting. The missing-conversation
classifier is shared by stderr and the result's errors, so the typed refusal
ends in one attempt whichever arrives first; other reported startup failures
stay retryable through the existing provider-request error.

* fix(native-chat): keep a Claude startup result's row once another failure ended the start

A Claude startup failure result was still taken by its acquisition's startup
outcome after initialize, a transport fault or an unexpected exit had already
failed that start, so its diagnostic was neither shown nor kept in the final
failure. The outcome now declines once the open fails by another route or the
exit is observed (synchronously, before buffered frames drain), so such a
result keeps its row as on main. It still takes a repeated result it already
accepted, and a result whose refusal the ending exit already carries; a Stop
still owns its start.

* fix(native-chat): report a Claude start's first ending cause from its open

The startup outcome now records the first cause that ends a start: an
accepted startup result, the raced initialize or catch-up rejecting, a
transport fault or unexpected exit, or any other open failure. That cause
rejects the pending race, open() refuses success after it, and the open
rejects with it, so a hidden result is always the open's error and any
other result keeps its row.

* test(native-chat): check the startup rejection with a type guard instead of an assertion
2026-11-01 22:46:57 -08:00
Neil 8be3cb963e Revert "ci: full checks on main pushes, changed-file lint on PRs, Slack alert when main breaks (#37282)" (#37341)
Main went red right away on the full main-push runs. Reverting to the previous PR-only checks.

This reverts commit a7ae68fc6f.
2026-11-01 22:45:18 -08:00
Neil 0fad93e2ad test: stop unit tests from waiting out real 5 s fallbacks; fix two flaky tests (#37362)
The ConPTY prelude mock delivered its DA1 query on a microtask, which reached only
the physical owner's early onData listener (added in #36422); the startup watch
subscribes after an await, never saw it, and every Windows-path spawn waited out
CONPTY_STARTUP_DECISION_LIMIT_MS (5 s). Deliver it on a macrotask like real output.

Also give two fixtures the readiness their production peers provide, and fix the
counter-file race in structured-agent-shell-environment and the async sign-in
marker race in claude-status-sign-in.
2026-11-01 22:44:16 -08:00
Neil cb031c4517 fix(plugins): merge disk writes into a custom editor's unsaved edits (#37343)
* fix(plugins): merge disk writes into a custom editor's unsaved edits

A write to the file on disk while a custom editor tab had unsaved edits
never reached the editor: the buffer kept the edits, Orca showed its
Reload/Overwrite banner, and the session's merge never ran. Now a frame
whose document session has merge announces it (editor.merges), and Orca
sends it the new disk text with the text the edits started from
(editor.document with ancestor). The session merges, posts the result,
and the tab stays unsaved with it; the banner is skipped. Without merge
the banner decides as before.

* test(editor): an explicit save under the changed-on-disk banner keeps the edits

* fix(plugins): make the outside merge exact, and narrow it to where it is

- The host settles a merge only on the session's edit made against the
  merge's own document version, and always commits that result to the
  buffer (even when it equals the disk text); silence past the 1 s flush
  deadline, a remount or a throwing merge leave the banner and the
  suspended autosave in place.
- Orca stamps exactly the disk snapshot the acknowledged merge used and
  records it as the next ancestor; a write that landed meanwhile puts the
  conflict back for its own merge.
- The ancestor is the tab's disk text recorded while clean and advanced on
  acknowledged merges, and must match the tab's disk baseline.
- Only a local file in one merging editor view, with no other tab on its
  buffer, takes this path; anything else gets the banner.
- Frame events keep reaching their handlers after the merge capability.

* fix(plugins): move nothing on a failed disk merge; advance the saved text on success

A merge that throws now leaves the session's document, baseline and
version untouched, so the user's next edit goes out against the old
version: Orca refuses it and keeps the banner instead of taking it as the
merge's acknowledgement. After an acknowledged merge the tab's saved text
moves to the disk text it used, so typing back to the old ancestor no
longer looks clean.

* chore(plugins): regenerate the testing bundle on main
2026-11-02 06:35:01 +00:00
Brennan Benson d712485fe2 Restore main unit checks for plugin routing and prompt receipts (#37345)
* test: restore plugin routing and prompt receipt checks on main

* test: flush pending receipts before releasing the prompt write

* test: route screenshot CLI sources to the journeys that run them
2026-11-01 22:34:12 -08:00
NeilandClaude 5846caf2ba plugins: editor.save.before hook (format on save, or refuse the save) (#37347)
* plugins: editor.save.before hook (format on save, or refuse the save)

Explicit saves of text files from an editor tab run plugins' editor.save.before
in main before the write (local, SSH, WSL and folder workspaces all save through
that one renderer path). A plugin can return changed text or deny with a reason,
shown in the save error. The buffer takes the hook's text only if it is unchanged
since the hook started; edits made meanwhile stay dirty. Autosave, restart saves
and bytes documents are not hooked, and with no live plugin listing the hook the
save path adds no await.

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

* test(plugins): editor.save.before text reaches the visible Monaco model

The hook writes its text into the tab's draft, which reaches the model through
the same content-sync bridge as Reload from disk. A model-level test covers the
formatted text showing, the next keystroke editing it, and edits typed during the
hook staying in the model.

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

* plugins: editor.save.before review fixes: flush held typing, update the surviving view, keep the denial

- After the hook answers, pending view text (rich Markdown's serialize timer) is
  flushed before deciding whether the buffer still holds the hook's input.
- The hook's text goes to whichever view of the buffer record is still open, so
  closing the saving view keeps a sibling view in step with disk.
- The save-request bridge passes Errors through whole, so the toast shows the
  plugin and its reason instead of a generic failure.

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

* chore(plugins): regenerate after rebasing the save hook on main

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-02 06:19:50 +00:00
Brennan Benson 8ddc35b8e9 test: import the conversation view from its new location (#37346)
#37191 added an import of the old path after #37289 moved the module, breaking
main's typecheck for every PR.
2026-11-01 22:10:22 -08:00
Neil 60e25bde45 feat(plugins): make the editor template the rich-editor recipe (#37349)
orca plugin create --template editor wrote an inline-script HTML editor
whose extension came from the folder name squashed to 12 letters. It now
writes the recipe: editor/main.ts (the view), editor/sync.ts (the
document session with a stand-in text merge), editor/sync.test.ts (a
mockEditorHost test of an outside write merged during a save), a
tsconfig on the generated typings, frame isolated, engines.orca
>=1.4.248, and extension .<id>. It passes plugin check and plugin test
as generated.
2026-11-01 22:04:31 -08:00
Neil c8112e643f docs(plugins): regenerate the manifest reference and schema for the priority wording (#37351) 2026-11-01 22:03:34 -08:00
Neil 999160b3db feat(plugins): commands[].replaces lets a mod take over a built-in action (#37316)
* plugins: commands[].replaces lets a worker command take over a built-in action

A worker command with replaces: <action> (the declarative action list,
engines >=1.4.248, needs main) runs in place of that built-in from its
shortcut, plugin aliases and the sidebar menus; a failed or silent (2 s)
worker falls back to Orca's own. The install screen names each replacement,
the plugin page has a per-action switch (settings.pluginReplacementsOff), and
when two plugins replace one action the one installed first wins and the
other's log says so. Update review counts replaces as new access.

* fix(plugins): narrow replaced action ids before looking up their titles

* plugins: harden command replaces after review

- Consent includes the replaced actions, so adding replaces to a dev folder
  needs a new approval; CLI approval and update review name each takeover.
- Shortcuts main intercepts (Tasks, Workspace Board, history), search with a
  selection and floating maximize now go through the replacement too.
- A worker that only starts after the 2 s fallback no longer runs the
  command as well: main refuses to start it past the deadline.
- A workspace-only command does not replace an action without a workspace.
- An update keeps the plugin's first install time, so it keeps its takeover.
- Drop the commands.run promise until Orca offers it.

* plugins: commands.run always runs the built-in, never a replacement

A5b's commands.run dispatches with the 'plugin-host' source; the app
dispatcher now builds handlers that run Orca's own action for it, so a
plugin that replaces an action and calls commands.run cannot loop. Docs and
schema describe wrapping the built-in with commands.run; regenerated.
2026-11-01 21:59:27 -08:00
OrcaWinandOrcaWin 8d1453fba9 test: remove 17 low-value test declarations (#37327)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-01 21:51:45 -08:00
Neil 3e52963a9a docs(plugins): fix custom-editor authoring papercuts from dogfood (#37337)
- shot --act: say edits only dirty the tab; end with Cmd/Ctrl+S to save
- manifest: compound suffixes like .sketch.json open by default
- cli.md: list plugin check's exit codes, including plugin_check_failed
- workflow: how to run and stop the dev app from a source checkout
- plugin create --help: real descriptions for --template/--publisher/--id/--name
- recipes: declaration file for vendored ESM libraries
2026-11-01 21:48:46 -08:00
NeilandClaude 2fb3a0975e feat(plugins): workers can open files and run built-in actions (#37323)
* feat(plugins): add editor.open and commands.run for full-access workers

Both ride the existing plugin view request channel (the one plugin open and
plugin shot use) to the one main window's renderer; headless serve and a
missing window answer unavailable. editor.open takes a path that stays
inside the workspace (Git worktrees and folders) and opens it as a tab
without switching what the person sees. commands.run takes only the built-in
contributes.commands action list and dispatches Orca's own handler, never a
plugin replacement. Both need workspace:write.

workspace.list now also lists project-group folder workspaces.

* fix(plugins): open folder workspaces and host-naming ids through Orca's window

workspace.open of a project-group folder asks the window to show it
(setActiveFolderWorkspace), since the runtime activates Git worktrees only.
editor.open resolves the identity: ids workspace.list now returns. commands.run
documents that it must call the raw built-in, never a replacement wrapper.

* plugins: regenerate the testing bundle and skill guides on the rebased host actions

Generated files only (lint-staged's oxlint refuses a commit with no lintable files).

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

* chore(plugins): regenerate the testing bundle on main

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-02 05:47:35 +00:00
NeilandClaude 544577fe02 feat(plugins): workers can list and open workspaces, create and read terminals, launch agents (#37312)
* feat(plugins): let full-access workers act on workspaces, terminals and agents

Host API minor 3 adds worker-only rows: workspace.list (workspace:read),
workspace.open (workspace:write), terminal.create (terminal:send),
terminal.read (terminal:read, at most 2,000 lines) and agent.launch
(agent:launch). Each dispatches the runtime RPC method the orca CLI calls,
in process with the CLI's identity, so it runs on the host that owns the
workspace (local, WSL or SSH) and covers folder workspaces. The new
capabilities arrive in 1.4.248; typings, docs and skill references are
generated from the table.

* fix(plugins): make workspace host actions safe to revoke, cancel and target

- Each runtime effect re-checks the plugin's approval right before the
  runtime acts (a dispatcher beforeInvoke hook, after its own waits), and a
  worker's cancel or exit now aborts the call's signal through to the runtime.
- agent.launch starts the agent's CLI in a background terminal and returns
  its handle at once; a prompt is delivered afterwards through the runtime's
  own reuse-terminal path, so the host call never waits for the composer.
- workspace.list adds project-group folder workspaces and returns ids that
  name their host (identity: keys), so twins on two hosts are never ambiguous.
- terminal.create passes presentation background; terminal.read counts a
  runtime-limited read as truncated. workspace.open refuses folders as
  unavailable until a window-side open lands.

* plugins: keep both API extensions and host-call tracking under the line limits after rebasing on the hooks

Worker-only status bar and workspace methods spread into PLUGIN_HOST_API through
one module, the in-flight host call map moves into PluginWorkerHostCalls, and
bindPluginRuntime builds the runtime RPC so main-process-plugins.ts keeps its
size; regenerated docs and typings.

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

* chore(plugins): regenerate after rebasing on the editor recipe and shots

* chore(plugins): regenerate after rebasing on the plugin shot recipe

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-02 05:44:29 +00:00
Brennan BensonandClaude Opus 5.5 1783cf58b1 Show agent sign-in notices before the first chat message (#37191)
* Show explicit agent sign-out facts before sending a chat

* Drop the Grok status process; keep listed models when Pi or Claude replies arrive late

Grok goes back to one listing process with no Grok-specific sign-out guess;
an agent-authored auth-required reply still reports notSignedIn. Pi reports
notSignedIn only when it has no provider and nothing listable. Once Claude's
required replies are in, the listing deadline returns them instead of failing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Drop the runner tests' timeout bumps, which sat below the default

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Type the endpoint test's launch env as a string map

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Drive the signed-out Claude re-probe trigger in the scaling contract

N and 10N distinct chats (distinct read claims, one account-scoped
Claude fingerprint) read with waitForListing against the real probe and
store on an injected clock: one listing before the held-reason TTL,
exactly one more once it is due (held until every read has decided),
none within the next TTL, each result carrying notSignedIn and models.

Checked locally that it bites: making heldReasonDue ignore the TTL fails
it (2 listings inside the TTL). Removing the in-flight dedup (the
probeDue isListing guard plus the lister's early return) does not: store
refresh then runs once per chat, but a newer flight replaces each queued
one behind the two admission slots, so still one process per TTL.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Accept Claude control replies as main did, without a type field

The shared reader required type 'control_response' and prefiltered on
that text, dropping success frames main's model and slash-command
parsers accepted (the released-phone wire test's fake listing).
It now prefilters on request_id and keeps the structure limit and
success-only reading.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Narrow the scaling contract's catalog result before reading models

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-01 21:42:07 -08:00
Brennan BensonandClaude Opus 5.5 06b8f7dc37 Keep Windows and Linux chat switching separate from worktree shortcuts (#37199)
* fix(shortcuts): give Windows and Linux chat switching a working default

* test(shortcuts): verify the worktree and chat chords independently

* fix(shortcuts): preserve agent newline keys while switching chat views

* fix(shortcuts): use Ctrl+Shift+F11 for Windows and Linux chat switching

The Ctrl+Alt+Shift+J default could not match non-Latin layouts or IME
input, because Ctrl+Alt chords skip the physical-key fallback that
protects AltGr characters. A function-key primary matches on every
layout; Ctrl+Shift+J stays as the alias where Switch worktree doesn't
claim it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(shortcuts): keep the chat toggle's own default alias when its shortcuts are edited

Saving any change to Switch between Chat UI and terminal on Windows and
Linux kept its untouched Ctrl+Shift+J alias, which the conflict check
then refused against the worktree palette's default, so replacing or
removing the primary failed. The check now exempts a chord per binding
when it is the action's own default and every other owner also holds it
as a default, so non-default explicit claims are still refused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(shortcuts): expect the new Windows/Linux chat switch label

The context menus show the toggle's primary binding, which is now
Ctrl+Shift+F11 on Windows and Linux.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-01 21:41:39 -08:00
Brennan Benson f9a4ba6225 Make phone Send follow the paired desktop setting (#37315)
* Make phone follow-up delivery follow paired desktop

* Keep the paired desktop's follow-up setting live through a server outage

The phone now keeps listening to the paired desktop's Queue follow-ups
setting while a chat is open, even when the workspace's server relay
drops, so a desktop toggle during the outage is used by the next Send.
The chat reuses the route's existing paired-host connection instead of
acquiring it again per chat, and the follow-up tests load and typecheck.

* Keep the chat lock-reason helper out of modules the desktop tests import

The overlay lock-reason helper moves to its own file so the shared
eligibility module no longer reaches the chat view, which pulled the
phone's UI types into the desktop end-to-end typecheck. The reconnect
test now drops and restores the client's own connection, and checks the
preference stream closes with it and reopens with one fresh read.
2026-11-01 21:40:47 -08:00
Brennan Benson 5d3dfe943e Tasks source summary, repo link and Filters row follow the list's actual source (#37226)
* Show the active GitHub list source in Tasks summary

* Tasks header, repo link and source row follow the list's resolved source

The Tasks summary could name the saved upstream project while the list came
from the fork, and the "Open in GitHub" link could open the other kind's
repository when the active kind's source was unresolved. A new search or an
Issues/PRs switch also blanked the source until the new fetch landed.

Compute the source summary once, after mode and item kind are known, from
the list's resolved sources (saved project identity no longer reaches the
formatter). The summary, the single-project repo link (strict active kind)
and the Filters source row share one display-only read: the current query's
entry, else the latest same-scope sibling while the query is fetchable.
Notices, errors, Retry, filter metadata and page rows still read only the
current query's entry. Projects, GitLab, Linear and Jira output is unchanged.

* Tasks source carries the shown entry across queries, read live

While a new search or Issues/PRs switch has no cached result yet, the Tasks
summary, repository link and Filters source row keep showing the source of
the entry they last showed for that project copy, read live from the cache,
instead of picking the newest cached sibling. A refresh that changes that
entry is followed, other queries' results never take over, a preference
change's eviction ends the claim, and a present unresolved result or a
too-large query makes no claim.

The display read now happens in the existing current-entry selection pass
(at most two cache reads per selected project, no cache scan), and the
summary re-translates when the language changes. Adds a SourceBar fixture
with a long fork slug at normal and narrow widths.

* Tasks source display perf contract measures each update on its own

Bound every measured update (another host's patch, a non-cache store write,
current and sibling fetches, a real preference eviction) at 10 and 100
selected rows and 50 or 500 cache entries: no cache enumeration, at most two
reads per row per selector call, a fixed selector-call budget equal across
sizes, no key rebuild on cache writes, linear row visits including pointer
upkeep, and reused output references for writes that change nothing shown.

* Use typed reads in the Tasks source display perf instrumentation

The static-analysis rule rejects Reflect.get; the cache and row proxies now
read by typed keys and count exactly the same reads and enumerations.

* Drop type assertions from the Tasks source display changes

Stage hooks return Object.assign(model, {...}) instead of casting the model
(same object, same writes, same type). The fixture and test stubs keep their
partial-model casts on one line each with a SAFETY reason, the row proxy uses
a type guard, and the filler work item is a typed fixture.
2026-11-01 21:40:33 -08:00
Neil 3b5eed8ca4 docs(plugins): point the editor and status bar recipes at plugin shot (#37332) 2026-11-01 21:40:07 -08:00
Brennan Benson 9ce35be4ff fix(native-chat): effort, speed and fast controls stay usable while the model list refreshes; dotted versions in id-derived model labels (#36870)
* fix(native-chat): running chats keep effort menus during model discovery; dotted versions in id-derived model labels

* fix(native-chat): options stay usable while a model list refreshes; dated preview ids keep their dashes

A running host listing no longer disables effort, speed or fast mode. Those
controls follow the data they draw from: before any list exists they show
the placeholder or a read-only saved effort, and once a list or the chat's
own answer is shown they stay usable through every refresh, for every agent.
The listing flag is now only the host's raw fact, which keeps a seed from
settling to the built-in list while discovery runs.

Id-derived labels no longer dot a zero-led part, so MM-DD preview dates
(gemini-2.5-flash-preview-05-20) stay dates.

* fix(native-chat): a model list refresh keeps the verified models and default effort already shown

While the host refreshes a Claude or Codex list, the chat keeps the model
ids an earlier finished listing verified that the refreshing rows still
contain, so a running Codex chat's CLI-default effort ("Medium") no longer
blanks from the pill and menu until the listing lands. Ids no finished
listing verified stay unverified, and a finished listing still replaces
the set.

* test(native-chat): type the listing-wait check without an assertion

* fix(model-labels): month-day dates after October stay undotted

* refactor(native-chat): listing-verified model ids get their own module
2026-11-01 21:39:37 -08:00
Brennan Benson 0bf96e0d7a Show a GitLab MR's pipeline result even when it has no jobs (#37177)
* Show failed pipelines and check suites without jobs

* Use the parent pipeline as the GitLab checks verdict

The MR's own pipeline status now leads the Source Control Checks panel,
the MR dialog and the phone checks badge whenever the details reply
carries it, even when the returned jobs are empty, partial or all
passing. Job rows and counts stay a separate breakdown. The parent
follows pipeline-level policy (required manual is pending, canceled and
skipped are not failures, mergeable manual is neutral) from one shared
rule, and it is a provider link rather than an expandable job, so it
never shows a details error with Retry. A failed parent leads the
triage strip and joins the Fix prompt by name and link.

Drops the GitHub zero-run failed check-suite rows from this change;
existing approval-required suite rows are unchanged.

* Name the GitLab pipeline outcome and keep its verdict in one rule

The host now settles the parent pipeline's verdict with the same rule
and reply that produce the MR status, so a manual pipeline on older
GitLab reads pending everywhere. The phone checks badge names the
pipeline outcome (Pipeline passed/failed/pending) instead of an
unlabeled one-item count; job wording without a parent is unchanged.
The desktop parent row's status is localized, and a failed MR-details
read still shows its error beside a kept parent with no jobs.

* Keep Fix for failed GitLab jobs and treat waiting_for_callback as pending

The triage strip shows again when a returned job failed under a passing
parent pipeline, so its Fix stays available; a failed parent still
leads. A waiting_for_callback pipeline now reads pending instead of
neutral, in the MR status and the parent pipeline alike.

* Mark the checks-panel action test fixture cast as safe

* Pause the pipeline row spinner offscreen and under reduced motion
2026-11-01 21:39:18 -08:00
Neil f908d2f40a feat(plugins): plugin shot --editor to screenshot and drive custom editors (#37302)
* plugins: orca plugin shot --editor, and plugin open --editor opens the file's folder

plugin open --editor now opens the file in the workspace that holds it,
matching its real path too (/tmp vs /private/tmp), and otherwise adds the
file's folder as a folder workspace instead of failing with no_workspace.

plugin shot <dir|key> --editor <file> [--theme] [--width --height]
[--act] loads the editor afresh in a hidden tab, waits for it to get its
document and ask for it (a new editor.subscribed frame event), captures
the editor's guest with the panel shot's capture path, and sends --act
clicks, drags and keys as real input via sendInputEvent. It exits 1 with
the frame's errors when the editor throws, is blocked by its content
policy, or never asks for its document within 5s; theme and size are put
back. New RPC method plugins.shotEditor so older hosts fail cleanly.

* plugins: shot --editor never changes the author's file

With --act the shot copies the file into a temporary folder, shoots the
copy in a temporary folder workspace, then closes its tab and removes the
workspace and folder; --in-place acts on the file itself. Without --act
it opens the real file, leaves its buffer alone and closes the tab if the
shot opened it. Afterwards Orca shows the workspace it showed before.
Regenerates the skill's CLI reference.

* plugins: fix editor shot typecheck (refusal next step, RPC test mock)

* plugins: editor shot targets its own guest, flushes before reload, scopes cleanup per capture

- Main captures the exact guest WebContents the shot's tab mounted, not the first with its URL.
- An open editor's pending edits are flushed before the shot reloads it; the shot fails if not.
- --act --in-place keeps the acted tab and its unsaved edits.
- end-capture undoes only its own capture (per-capture id), so overlapping shots don't clash.
- The file's workspace lookup includes local folder workspaces.

* plugins: shot --status-bar/--window, key acts focus the editor guest, panel shot selects a workspace

- Editor --act keys first give the guest's <webview> DOM focus and refocus its target, so a canvas
  that keeps focus no longer drops later keys; never webContents.focus(), which focuses the window.
- orca plugin shot <dir|key> --status-bar | --window [--theme]: captures Orca's status bar or the
  whole hidden window (plugins.shotChrome) with the existing capture helpers.
- With no workspace showing, a panel shot selects this machine's most recent workspace in the
  hidden window instead of failing.

* plugins: a shot's end restores only its own capture overrides

* plugins: regenerate the testing runtime with the editor subscribed signal

* plugins: editor capture refusals go through one CLI-message helper

* fix(plugins): give the chrome shot refusal a next step
2026-11-01 21:38:04 -08:00
Neil 9b39188021 refactor: organize session option and conversation owners (#37289) 2026-11-01 21:33:42 -08:00
Neil 591f2acbc8 docs(plugins): add a rich-editor recipe and list frame theme variables in the skill (#37306)
recipes.md gains "A rich editor with a library" beside the hooks and status
bar recipes: an isolated-frame custom editor that vendors its library behind
an import map, syncs through orca.document.session with a merge by id, and
tests that sync with mockEditorHost. host-api.md lists every --orca-* frame
variable and what it paints, generated from the frame token allowlist.
2026-11-01 21:30:37 -08:00
Neil fa7c3f95fe feat(plugins): check custom editors and test them with mockEditorHost (#37293)
* feat(plugins): check custom editors statically and add mockEditorHost

orca plugin check now follows each custom editor entry (local script src,
inline module scripts, relative imports), transpiled and resolved as the
frame loader does, and fails at file:line:column on syntax errors, missing
files, refused imports, string eval/Function and files over their size
cap, with no running Orca. The check ships in the test hooks bundle.

orca/testing gains mockEditorHost: it runs the frame shell's real
window.orca (document API and session) over an in-memory text or bytes
file, with edits, replace, conflictNext, flush, setTheme and dispose.
Typings, docs and the skill guide cover both.

* feat(plugins): warn on editor eval and check editors in dev watch

editor_eval comes from a lexical scan that can misread rare code, so it is
now a warning that does not fail orca plugin check. dev watch --check runs
the editor check beside the panel check and reports it under editors.

* fix(plugins): check editors by the frame protocol's rules; mock blocks lost saves

The static editor check now reads files through the loader's contained
reader (symlinks out of the plugin are refused), runs only JavaScript
script types, honors the page's import map for native modules, classifies
files by the protocol's content types (JSON imports, extensionless HTML
entries), decodes escaped script URLs, and uses the protocol's caps: 10 MiB
entry, 1 MiB transpiled module, 16 MiB checked dependency.

mockEditorHost refuses an oversized edit before reading it and then rejects
flush() until a later edit lands, as Orca blocks the save.

* test(plugins): type mockEditorHost's frame API instead of reflecting on it

* fix(plugins): keep main's case-spelling check and hook typings through the rebase

The rebase onto main resolved plugin-artifact-validation.ts and
orca-testing.d.ts to B4's side; this puts back main's exact-path-spelling
check and hooks typings beside B4's contained reader and mockEditorHost
types, and regenerates the testing bundle.
2026-11-01 21:28:13 -08:00
NeilandClaude 7989c05ad3 feat(plugins): agent tool-call hooks (Claude Code can block or rewrite) (#37303)
* plugins: agent.tool.before and agent.tool.after hooks; Claude Code PreToolUse blocks

The hook server runs plugins' tool hooks after recording status. Claude Code's
POSIX hook script now asks for an answer on PreToolUse (X-Orca-Hook-Decision),
waits at most 8 s (Orca answers within 6 s) and prints the decision: a deny
with the plugin's reason, or updatedInput without approving it. Timeouts,
errors and an unreachable Orca print {}, so the call proceeds. Other events
still print {} before posting. Every other agent, Windows scripts and SSH/WSL
relays only observe or are not hooked yet (per-agent table in the
agent-hooks README).

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

* plugins: verify PreToolUse answers against the real Claude CLI; 204 at once when no tool hook listens

A real-CLI test (ORCA_REAL_CLAUDE_CLI_TEST=1) shows Claude Code 2.1.316 runs
updatedInput sent without permissionDecision, still asks for approval of the
rewritten call, and refuses a deny. With no plugin listing a tool hook, the
server answers 204 before any await and runs no chain.

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

* test(claude): type the real-CLI PreToolUse answer

* plugins: harden agent tool hooks (partial replies, fenced posts, OpenClaude, Amp/Copilot/Cursor)

- Drop a failed curl's partial body and print only a complete PreToolUse decision.
- Run tool plugins only for posts status ingestion accepted; name the owner's terminal.
- Enable PreToolUse decision mode only for Claude Code, not CLIs reusing its script.
- Decode Amp tool.call/tool.result, Copilot envelope/implied events and Cursor shell/MCP calls.

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

* plugins: regenerate the testing bundle on the rebased tool hooks

Generated file only (lint-staged's oxlint refuses a commit with no lintable files).

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-01 21:24:49 -08:00
NeilandClaude 60d262ebee feat(plugins): hooks for agent launches and prompts, plus plugin dev hook (#37295)
* plugins: agent.launch.before and agent.prompt.before hooks; orca plugin dev hook

agent.launch.before runs at the PTY spawn every agent terminal goes through
(window tabs, runtime/CLI agent launch, automations, orchestration), before
dispatch to its host: change the command line, add env (Orca's own ORCA_*
variables and the agent's environment stay hidden), or deny. Restores and
resumes are not hooked. agent.prompt.before runs in sendTerminalAgentPrompt
(launch prompts, orchestration dispatch, terminal send to an agent) with the
agent named: rewrite or deny. The terminal hooks move into one runtime layer.
orca plugin dev hook runs a hook through one dev plugin and prints the decision.

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

* plugins: recheck rewritten launch commands, forward hook env to WSL, keep prompt delivery outside the hook order

- A command agent.launch.before rewrote passes the AI Vault resume check again
  on both spawn paths.
- Variables a hook adds are named in WSLENV on Windows so WSL agents get them.
- agent.prompt.before holds the terminal's order only while it decides; a
  delivery waiting on a permission answer no longer blocks later input.

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

* plugins: plugin dev hook honours onError: deny

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

* plugins: regenerate the testing bundle and skill guides on the rebased hooks

Generated files only (lint-staged's oxlint refuses a commit with no lintable files).

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-01 21:20:29 -08:00
Neil d9a070aa09 test(plugins): read the CSP probe's result path from its config, not argv's tail (#37329)
On Linux the probe launcher appends --no-sandbox, so argv[length - 2] is the --user-data-dir flag rather than the config path. The page then wrote result.json under a directory named after that flag, threw, never exited, and the launcher timed it out at 30s on every Linux run since #37300. The sibling Electron probes already pass the result path in config.json; this one now does too.
2026-11-01 21:13:26 -08:00
NeilandClaude ac8c1125f0 feat(plugins): hooks that can change or block terminal sends and workspace creation (#37287)
* plugins: contributes.hooks runtime with terminal.send and workspace.create hooks

Workers register orca.hooks.on(hook, (event, next) => ...). Orca runs listed
plugins in order before it acts: next(event) continues with allowed fields
changed, { deny } stops, nothing passes. A plugin past its 2 s deadline, or
one that throws, crashes or is disabled, counts as passing and is logged with
plugin, hook and duration. First hooks: terminal.send (runtime sendTerminal),
workspace.create.before (IPC and runtime creates, folders included) and
observe-only workspace.create.after. orca/testing mountWorker gains
hooks.fire. Needs main + trusted-worker and engines.orca >=1.4.248.

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

* plugins: keep hook plugins' workers started; bound start-up separately from the hook deadline

Workers of enabled plugins that list hooks start as soon as they are approved
and are never idle-reaped; crash restarts follow the manager's policy. A hook
waits up to the worker ready timeout for start-up, then gives the handler its
full 2 s, so a cold worker no longer lets the first hooked action through.

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

* plugins: give hook workers their own soft cap instead of the active-worker cap

Always-running hook workers no longer hold slots of the shared 5-worker cap, so
many hook plugins cannot starve the rest. They have a soft cap of 16; past it a
hook worker waits for another to stop, logged once.

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

* test(plugins): type the hook warmer's ensureWorker mock

* plugins: address hook review: ordering, human input, overflow, revalidation, folder creates

- Hook workers past the soft cap start as overflow (logged once) instead of
  waiting, so reconciliation, disable and uninstall never hang.
- Writes to one terminal stay in order while terminal.send decides, so a later
  Enter cannot submit the previous line.
- terminal.send runs only for programmatic sends (main, plugins, the CLI);
  desktop, mobile and legacy client input is a person's typing.
- Text a hook rewrote passes the AI Vault resume check again.
- Project-group folder workspace creates (RPC, agent launch, window) run the
  workspace.create hooks through one boundary.
- A decision from a plugin disabled or changed while it ran is discarded.

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

* test(plugins): drive hook start-up timing with fake-timer deferreds, no real sleeps

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

* plugins: only the hook decision holds a terminal's write order

A delivery that waits (a permission prompt, the launch composer) no longer
blocks later writes, and a person's input, interrupts and query replies never
wait behind a pending decision.

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

* plugins: hook DX: onError deny, denied_by_plugin, a Hooks reference, --template hook

- contributes.hooks[].onError: "deny" makes a throw, timeout, crash or bad
  answer refuse the action, naming the plugin; the default still passes.
  orca/testing mountWorker honours it, and mockHost answers fire-and-forget
  methods (result { ok: true }) with success by default.
- A plugin denial reaches RPC clients as denied_by_plugin with the plugin and
  reason, and the CLI no longer suggests a retry for it.
- The skill's host-api reference gains a generated Hooks section (event, what
  next may change, stop result); the recipe passes sends through unchanged and
  warns against awaiting other calls before deciding.
- orca plugin create --template hook: a worker-only terminal.send guard that
  fails closed, with a test.

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

* plugins: regenerate hook docs on main; bind the plugin runtime and its hook runner in one call

Keeps main-process-plugins.ts under 300 lines now that status bar items landed.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-01 21:12:57 -08:00
Neil 08813758b6 fix(themes): keep Match Terminal's host pill label at 4.5:1 on its fill (#37309)
* fix(themes): keep Match Terminal's host pill label at 4.5:1 on its fill

* fix(themes): clarify host pill comment
2026-11-01 20:56:41 -08:00
Neil 756c08724e fix(plugins): reject declared artifact paths whose case differs from the file (#37307)
* fix(plugins): reject declared artifact paths whose case differs from the file

* fix(plugins): name Unicode normalization in the path spelling error
2026-11-01 20:56:34 -08:00
Neil cddeccd161 test(plugins): drive the CSP refusal probe from script so it cannot hang on Linux (#37326)
The probe waited for an exact count of refusal lines, but markup-only fonts and media load
only on layout, which a hidden Linux CI window may skip, so it timed out at 30s. The page
now requests each kind from script, settles each on its own outcome, and logs a done line
the main process waits on.
2026-11-01 20:50:44 -08:00
Neil ac48e72d77 fix(plugins): import custom editor host types from their own module to restore main lint (#37319) 2026-11-01 20:45:26 -08:00
Neil 892e8009f8 test(plugins): give the RPC approval fixture a statusBarItems list (#37324) 2026-11-01 20:44:58 -08:00
Brennan Benson 764bd0be44 Continue Fix checks and project actions through setup confirmation (#36539)
* Resume direct work item launches after setup confirmation

* Preserve setup review and settle cancelled work item preparation

* Fix setup test types and synchronize localization artifacts

* Keep accepted setup launches owned through completion

* fix: declare accepted create dialog result

* fix: report direct launch preparation failures

* Reuse workspace setup preparation and section

* Use compact dialog description for setup help

* Construct omitted setup decision fixture through serialization

* Match optional signal in background setup trust test
2026-11-01 20:39:09 -08:00
OrcaWinandOrcaWin 98d22faa8d test: remove 96 low-value test cases (#37296)
* test: remove 96 low-value test declarations

* test: update timeout baseline after audit903

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-01 20:33:06 -08:00
Neil a7ae68fc6f ci: full checks on main pushes, changed-file lint on PRs, Slack alert when main breaks (#37282)
* ci: full checks on main pushes, changed-file lint on PRs, Slack alert when main breaks

PR Checks now also runs on every push to main, with each commit in its own
concurrency group so main runs never cancel one another. On a push the path
router gets an empty diff, which already fails closed to every job, and the
input-scoped steps (localization extraction, VM rollback replay, orcad smoke)
run in full. Advisory E2E lanes stay PR-only.

On PRs the per-file passes (root oxlint, JS-plugin oxlint, anti-slop, ast-grep
and oxfmt) check only the changed paths. Everything is checked when the diff
cannot be resolved, a lint toolchain input changes (oxlint/oxfmt/ast-grep
configs and plugins, package.json, the lockfile, .gitignore, pr.yml), or more
than 1500 paths change. Whole-program checks stay unscoped.

A new workflow follows completed main runs and posts to the release Slack
webhook only when main turns red, and once when it turns green again.

* ci: alert once per main health streak and tolerate a malformed Slack mapping
2026-11-01 20:30:03 -08:00
Neil a9b1041ecc feat(plugins): status bar items plugins can update live (#37291)
* plugins: contributes.statusBarItems with worker updates (statusBar.update)

A plugin declares status bar items { id, text?, icon?, tone?, tooltip?,
command? } (declarative profile, engines >=1.4.247); pressing one runs
its own command. A trusted worker changes them with the new worker-only
host method statusBar.update; main keeps the session's changes and
pushes snapshots to desktop windows over plugins:statusBarChanged.
Regenerates schemas, references, typings and the authoring skill.

* plugins: statusBarItems ship in 1.4.248, beside contributes.hooks

* plugins: clear a plugin's status bar changes when it is disabled, removed or updated

* plugins: list status bar items in the plugin page's Adds section

* plugins: count statusBarItems' since in the host's newest readable version

* i18n: add the plugin page's status bar row label to en.json

* plugins: status bar items respect workspace commands, dev reloads and catalog summaries

- An item whose command needs a workspace is disabled with no open workspace.
- A dev code edit (new devRevision) drops the old worker's status bar changes.
- Catalog summaries list status bar items, and marketplace details show them.

* plugins: authoring skill lists status bar items as buildable; approval lists them

The skill no longer calls status bar items reserved and gains a row for them. The
dev-load/inspect approval already lists them via the contribution summary; a test pins
that and the fingerprint binding.

* chore(plugins): regenerate the testing bundle after rebasing on main
2026-11-02 04:26:55 +00:00
Neil fbe891ce8b fix(plugins): skip nested packages in dev folders and summarize blocked frame requests (#37300)
* fix(plugins): skip nested dev packages and count blocked frame requests

Dev folders and their watcher now skip .git, .orca and node_modules at any
depth, so a nested build/node_modules no longer trips the 2,000-file limit
or re-reviews the plugin; installs still hash everything but the root .git.

Requests a plugin frame makes off its own files (session-cancelled or
refused by its content policy) are counted per kind and origin and logged
every 5 s and on teardown, outside the 20-lines-per-10-s console budget:
[frame] blocked font-src https://unpkg.com (x48): plugin frames have no network

* fix(plugins): read current CSP refusal wording and bound blocked-request logging

- Electron 43 prints "Loading the font '…' violates …" (and Connecting to / Framing /
  Fetching content from); read it alongside the older "Refused to …" wording, name the
  unset directive from the fallback note, and treat fetch's repeat as an echo. An Electron
  test captures the bundled runtime's real messages.
- Keep at most 32 kind-and-origin counts and log at most 8 lines per flush, folding the
  rest into one overflow count line.
2026-11-02 04:21:03 +00:00
Neil 4a0ee07bd2 feat(plugins): orca.document.session() for custom editors (#37285)
* feat(plugins): add orca.document.session() for custom editors

One call replaces the document sync every editor rebuilds: decode/encode
(plain text by default), optional merge, debounce. One edit in flight with
later changes coalesced; the baseline moves only on accept; a refused edit
keeps the draft and rejects flush(); own accepted edits never reach
onReplace; a conflict merges once against the last accepted value and
resubmits, or keeps the draft without merge. The session answers
editor.flush itself and resolves after hello and the first document.

The --template editor now uses it. Docs and typings state the session and
the undo model (the editor owns undo).

* fix(plugins): let the outside document win a session conflict without merge

Agents write files while people edit them, so keeping the draft lost the
agent's write on the next keystroke. Without merge the session now drops the
draft, fires onReplace with the outside value and rejects flush() with
conflict; editors that want both sides pass merge.

* fix(plugins): serialize session edits through saves and settle before answering

- One edit in flight at all times, saves included; editor.flush is answered
  only once every change is accepted or refused with nothing newer to send.
- The editor host commits a held edit when a save starts and admits edits
  during the save at once, so waiting fits the 1 s deadline.
- A change generation replaces identity checks, and sent bytes are
  snapshotted, so a reused buffer is never mistaken for an accepted edit.
- A waiting flush keeps its conflict rejection when the outside document
  wins, and nothing merges or sends after dispose.

* test(plugins): read session test edits through typed helpers

* fix(plugins): drain session flushes, reject dropped drafts, snapshot the baseline

- flush() keeps going until no change made during it is left unsent.
- A waiting flush rejects with conflict whenever the outside document
  replaced a draft, newer ones included.
- The accepted baseline is decoded from the sent snapshot, so a reused value
  buffer cannot change merge's base.

* test(plugins): name the session test option and message types
2026-11-02 04:20:01 +00:00
Neil bdd997f71f test(cross-version): load moved modules from wherever each release defines them (#37297)
* test(cross-version): load moved modules from wherever each release defines them

v1.4.246 is the first release cut after #36909, #36979, #37036 and #37070 moved
the agent-session modules, so suites that imported the old paths from the
newest release failed with ERR_MODULE_NOT_FOUND for every PR running
cross-version wire. importReleaseCheckoutModule now resolves a moved module to
the path the checked-out release ships, from one table of current and earlier
locations, so both the rolling baseline and older pinned refs load. The
released-chat writers drop their own per-module existsSync fallbacks for it.

v1.4.246 also ships outcome storage, so it reads chats this build writes and
closes its own rows with the outcomes marker. The downgrade test pins its
refusing window to the two releases before outcome storage, and the upgrade
test leaves the marker to the outcome reader, which restoreJournal checks.

* test(cross-version): find a moved module by name in the release instead of a path table

A table of earlier paths is a list of stale src literals the path-literal ratchet rightly refuses, and needs an entry for every future move. The release checkout itself says where it ships each module: the one file with that name under the same source root. Prunes the three released-chat-writers entries the ratchet baseline grandfathered.

* test(cross-version): pin the chat-upgrade writers to the releases before outcome storage

v1.4.246 keeps a chat in outcome tables, so its rows hold only the marker and there is no row upgrade to carry on; the newest release stays the reader this build must match.
2026-11-01 20:19:25 -08:00
Neil 51f302167e feat(plugins): let a custom editor require the desktop app's isolated frame (#37298)
* feat(plugins): let a custom editor require isolated frames

customEditors[].frame: "isolated" | "any" (default any, since 1.4.248).
An isolated-only editor never runs in the srcdoc fallback: main serves its
fallback shell without the entry, and when this window has no isolated
guest (browser clients, or desktop with isolated frames off) the file opens
in Source with "This editor needs the Orca desktop app". Picking the
editor shows the same notice with an Open Source button.

* i18n: add the needs-desktop editor notice strings

* plugins: refresh frame isolation when its setting changes; browser clients open Source

- The learned isolated-frames answer is tied to the setting it was learned under, so turning
  isolated frames back on lets isolated-only editors open by default again.
- A browser client's plugin bridge has no list, so the plugin store settles on no plugins
  instead of erroring and retrying; files open in Source.
2026-11-01 20:15:41 -08:00
Neil e74b40bd4e fix(relay): dead-man boot grace for a freshly booted reserve cell (#37225)
* fix(relay): give a freshly booted reserve cell a dead-man boot grace until its first director contact

A reserve cell's dead-man starts its 60-120 s window when the switch file applies, but no
placing director can reach a new process until its load balancer routes to it. On the
2026-11-01 production same-cap rolls that took up to about 110 s after the switch file
applied; c10 (104 s window) and c16 (71 s) tripped 1-4 s before the first director poll
landed, latched into db, and failed their roll after a clean drain and restart.

Until the process's first contact (a reserver=1 feed poll or a real booking), the window is
the hashed window plus 180 s, matching autohealing's initial delay. After the first contact
nothing changes. A process that never hears from a placing director still falls back to db,
up to three minutes later than before.

* test(relay): assert the server wires the dead-man boot grace

* docs(relay): state the boot gap behind the dead-man grace as inferred, not measured
2026-11-01 20:15:37 -08:00
Neil 1cc843eef4 refactor(plugins): move custom editor host types to their own file (#37314) 2026-11-01 20:08:01 -08:00
Brennan Benson a08ac11607 Show draft and restricted GitHub Project items (#37224)
* Preserve repositoryless GitHub Project items

* Keep only draft and restricted Project items without a repository

Key the repository-scope exemption on item kind on desktop and phone so
an older host's content-less Issue/PR rows stay hidden as before. Draft
titles, cards and bars stay non-interactive with no row browser icon;
the toolbar's Open view in GitHub is the Project-level action. Phone
labels restricted items "Restricted" to match desktop. Normalization
assertions move to a main-process test so renderer tests no longer
import main code.

* Cover draft Status normalization and phone restricted-item copy

Restore the check that a draft's single-select Status survives
normalization, and render the phone Project list and detail drawer with
a draft and a restricted item using the real status labels.
2026-11-01 20:05:48 -08:00
Brennan Benson 54b496cef1 fix(cli): open ordinary directories as folder projects (#37232)
* fix(cli): open ordinary directories as folder projects

* fix(cli): explain the non-Git layout in the repo add next step
2026-11-01 19:49:07 -08:00
Brennan Benson 8cbd822e4c Keep readable conversations out of Session History warnings (#37211)
* Keep recovered conversations out of history warnings

* Name the agent when a History conversation is too large to load

A conversation whose only records exceed the transcript record limit now reports its own coded reason naming the agent and suggesting resuming it in that agent, instead of the generic session-metadata message. Adds a rendered banner test and a non-user Codex case.

* Build History banner test values without type assertions

* Name the conversation's writer in the too-large History reason

Labels the reason with the parsed agent so an OMP transcript found under Pi's folder names OMP, ships the new key in the English runtime catalog, and renders the reason in the phone's History panel test.

* Cover the phone History notice for readable results and Spanish

Test-only: a ready result without issues shows no skipped notice, and the too-large case keeps its English reason after switching the phone to Spanish while the count is translated.
2026-11-01 19:48:40 -08:00