Four review iterations surfaced correctness issues in the new Codex hook
trust path. Notably: CRLF-terminated headers slipped past the upsert
regex (Windows would accumulate duplicate blocks), readHookTrustEntries
falsely matched headers inside multi-line strings, install() ignored
trust-write failures and reported green status, and parseTrustKey
accepted malformed keys because Number('') === 0.
- TOML scanner: handle CRLF, escape-aware triple-quote counting,
literal-string and basic-string quoted keys with embedded ']',
and [[array.of.tables]] block boundaries.
- escapeTomlString covers \b\f\n\r\t and unescapeTomlString preserves
unknown escapes.
- New exports: parseTrustKey (strict canonical-int validation),
readHookTrustEntries, removeHookTrustEntries.
- install(): catch trust-write failures and surface as 'error' state.
- getStatus(): verify trust hash + enabled flag for the LAST matching
managed group; surface unreadable config.toml as 'partial' so
hooks.json health is still visible to the user.
- remove(): clean up our [hooks.state."..."] blocks via parseTrustKey.
Tests: 46 passing (up from 12), covering CRLF, multi-line strings,
literal-string keys, array-of-tables boundaries, parseTrustKey edge
cases, and the new read/remove APIs.
Co-authored-by: Orca <help@stably.ai>
Codex 0.129 added a per-hook trust gate: every command in `~/.codex/hooks.json`
needs a matching `[hooks.state."<key>"]` entry with `trusted_hash = "sha256:..."`
in `~/.codex/config.toml`, otherwise the hook sits in the "review required"
pile and silently doesn't fire — which means the Orca agent-status sidebar
goes blank for Codex sessions until the user runs `/hooks` and approves
every entry by hand on every install/upgrade.
The fix reproduces Codex's hash algorithm (SHA-256 over canonical-JSON-sorted
NormalizedHookIdentity, see codex-rs/hooks/src/engine/discovery.rs and
codex-rs/config/src/fingerprint.rs) and writes the trust entries from
`install()` alongside the existing `hooks.json` write. Verified bit-exact
against a real `/hooks` approval. Older Codex builds parse the new
`[hooks.state]` table as unknown TOML and ignore it; verified working on
0.128.0 as well.
The TOML edit is regex-based byte-preserving rather than parser-based, so
hand edits, comments, and key ordering in `config.toml` survive an install.
Atomic-rename + .bak rotation matches the existing `writeHooksJson` pattern.
Co-authored-by: Orca <help@stably.ai>
Run pnpm update in root + mobile to pull patched versions of vite, hono,
@hono/node-server, dompurify, uuid, picomatch, lodash, brace-expansion,
path-to-regexp, postcss, @xmldom/xmldom, and other transitive packages
flagged by Dependabot.
Co-authored-by: Orca <help@stably.ai>
* feat(feedback): let anonymous users opt in to PR tag + contact
When 'Submit anonymously' is checked the feedback dialog now smoothly
expands to reveal two optional fields:
- GitHub username \u2014 we'll @-mention them on the fix PR
- Email or x.com handle \u2014 we'll reach out when it's fixed
The animation uses a grid-rows 0fr\u21921fr transition so it expands
naturally without measuring DOM. New fields are forwarded through the
IPC + main-process proxy as 'anonymousGithubLogin' and 'anonymousContact'
so the backend can tell self-typed handles apart from verified gh
identity. Backend changes (slack message + route) ship in
orca-marketing-website.
Co-authored-by: Orca <help@stably.ai>
* feat(feedback): split anon contact into email + x.com, tighten copy
Address review:
- move 'all optional' wording up to the top header line
- drop the inline 'we'll tag you on the PR' helper (placeholder says it)
- split the single contact input into two separate inputs (email +
x.com handle), each with its own placeholder
IPC field rename: anonymousContact \u2192 anonymousEmail + anonymousX. Backend
PR is updated to match before either side ships.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
When pairing fails (e.g. broken Tailscale route, firewall blocking the
WS, expired token, etc.) the mobile app previously got stuck on
'Connecting…' forever with no signal about *where* it stalled.
Adds a structured connection-log stream from the rpc client and renders
it under the 'Connecting…' spinner on both pair-scan and pair-confirm.
Each phase emits a timestamped, color-coded entry: Opening WebSocket,
WebSocket open, Sent e2ee_hello, Received e2ee_ready, Authenticated,
WebSocket closed / Reconnect scheduled / Connect timeout / Handshake
timeout, etc. The log auto-scrolls and stays visible after a failure so
the user can see the last successful step.
Also fixes the silent infinite spinner: pairing now has a 25s overall
timeout — rpc-client retries forever by design (right behaviour for
live sessions), but for the *initial* pair we want a hard ceiling that
surfaces an actionable error with the log visible instead of spinning.
- Adds ConnectionLogEntry / ConnectionLogSink to transport/types.ts
- connect() now accepts { onStateChange, onLog } (legacy fn form kept)
- New ConnectionLog component (mono, level-coloured, +Xs elapsed)
- Pair flows track logs in a ref and render them in connecting/error
- 25s pairing-overall timeout that closes the client and surfaces a
'see log below for where it stalled' error
Co-authored-by: Orca <help@stably.ai>
Extended removeRepo in repos.ts to drop lastVisitedAtByWorktreeId entries belonging to the removed repo, preventing monotonic growth of timestamps for deleted repos.
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
Fixed pty-connection passphrase wait gate: added 'disconnected' as terminal status (only after seeing a non-disconnected state) so cancel resolves; added waitTeardowns registry so dispose() actively u
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
Rebased buf0-bot #1461 onto current main — the original branch was
71 commits stale and would have reverted #1485 (Cmd/Ctrl+F search) and
#1429 (instant theme transitions).
Co-authored-by: nwparker <nwparker@users.noreply.github.com>
Closes the race where siblings awaiting parentFieldProbeInFlight wake
between resolveProbe() and the deferred parentFieldRetriedByOwner.add()
in the outer block, see parentFieldProbeInFlight=null +
parentFieldRetriedByOwner without scopeKey, and issue duplicate
with-parent probes.
Originally raised in #1456, but that PR branched off pre-#1465 main and
its diff against current main reverted #1465's owner-keyed parent-field
caches back to global flags. This re-applies the fix on current main.
Refs #1456
Co-authored-by: nwparker <nwparker@stably.ai>
Adds 'tab profile list', 'tab profile create', 'tab profile delete' to the
supportsBrowserPageFlag exclusion list. These commands operate on profile
identifiers, not browser pages, so accepting --page silently was misleading.
Supersedes #1458 (which was branched off stale main pre-#1396 and would
have reverted 'tab current' out of the exclusion list).
Co-authored-by: Hermes Agent <hermes@orca>
Replace ProfileCreate.scope coerce-to-isolated transform with strict
z.enum(['isolated', 'imported']) so unknown scopes surface validation
errors instead of being silently rewritten.
Supersedes #1455 (which was branched off stale main and would have
reverted #1396).
Co-authored-by: Jinjing <jinjing@stably.ai>
Picks up the icon update from #1490. Apple requires monotonically
increasing build numbers — 7 was already uploaded to TestFlight.
Co-authored-by: Orca <help@stably.ai>