* fix(linux): restore Ubuntu 20.04 launch by pinning node-pty glibc symbols (#9902)
The bundled node-pty pty.node is compiled from source in release CI on
ubuntu-latest (glibc 2.39). glibc's 2.32-2.34 libpthread/libutil merge
relocated openpty/forkpty (GLIBC_2.34) and pthread_sigmask (GLIBC_2.32)
into libc under new symbol versions, so the from-source build bound to
versions absent on Ubuntu 20.04 (glibc 2.31). The main process imports
node-pty at startup, so the app crashed on launch. pty.node is the sole
blocker (Electron needs GLIBC_2.25; other native modules <= 2.17).
- Patch node-pty: a .symver shim pins the 3 symbols to their pre-merge
version (GLIBC_2.2.5 x64 / GLIBC_2.17 arm64), and Linux-only ldflags
force libutil.so.1/libpthread.so.0 back into DT_NEEDED. Guarded to
Linux; macOS/Windows untouched.
- Add a packaging gate (verify-linux-glibc-floor.cjs, afterPack): reads
each bundled native binary's objdump -p version needs and fails the
Linux build if any strong GLIBC_/GLIBCXX_/CXXABI_ node exceeds stock
Ubuntu 20.04 (glibc 2.31 / GLIBCXX_3.4.28 / CXXABI_1.3.12). Catches
GLIBC_ABI_DT_RELR, rejects GLIBC_PRIVATE, skips weak needs, fail-closed.
- Docs + tests; the lazy sherpa-onnx speech prebuilt (GLIBCXX_3.4.29,
never loaded at launch) is a documented libstdc++-floor exemption.
* fix(linux): assert DT_NEEDED provider deps in the glibc-floor gate
Harden the packaging gate (flagged in adversarial re-eval): the version-floor
check alone can false-pass if the patch's forced `-l:libutil.so.1` ever silently
drops — the pinned openpty@GLIBC_2.2.5 still resolves from libc's compat alias at
build time, but fails to load on Ubuntu 20.04 where openpty/forkpty live only in
libutil. The gate now also asserts that any binary importing openpty/forkpty
keeps libutil.so.1 in DT_NEEDED. Validated on a real symver-pinned .so with
libutil dropped (now fails) vs. present (passes). Documents the recommended
real-host smoke-test follow-up.
* docs(headless-server): add upgrade SOP for orca serve on Linux
The headless Linux guide covered install/run/systemd but had no upgrade
section, leaving operators to guess how to move to a new AppImage without
losing state.
Add an "Upgrade" section documenting the manual SOP (serve mode never
auto-updates) and one troubleshooting bullet:
- State lives under the service user's ~/.config (orca + Orca dirs),
independent of /opt/orca, and orca-data.json is forward-migrated on load,
so a forward upgrade is safe.
- Replace the binary with an atomic same-filesystem rename (download to
.new, verify, mv) — never curl -o over the FUSE-mounted live binary.
- Back up the whole .config before upgrading, because rollback is NOT
binary-only safe: an older build strips newer orca-data.json fields it
doesn't recognize, and the .bak.* ring is corruption-recovery, not a
pre-upgrade copy.
- Note there is no headless version command; track the release tag instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(headless-server): harden the orca serve upgrade/rollback runbook
Address CodeRabbit review on #9575:
- Fail closed: run the upgrade block under `set -euo pipefail`, remove any stale
`.new` file before download, and gate the atomic `mv` on an explicit ELF check
so a failed/partial/non-ELF download can never be promoted.
- Keep /opt/orca/VERSION tied to the installed binary: a single `TAG` variable
drives both the download URL and the recorded VERSION, saved as VERSION.prev on
upgrade and restored on rollback so the audit file never drifts.
- Crash-loop troubleshooting now points to Roll back first (restores the
pre-upgrade orca-data.json) instead of re-running Upgrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(headless): harden server upgrade SOP
---------
Co-authored-by: fanyunqian.1 <fanyunqian.1@bytedance.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
badgen.net rate-limits against GitHub and paints "429" into the badge.
Switch stars to shields.io (repo stargazers_count), use a static MIT
license badge, and drop /stargazers links which now 404 for the public
after GitHub's July 2026 stargazer access restrictions.
* Add native chat skill and command picker with host-aware discovery
Adds a unified, keyboard-first skill and command picker to native chat that:
- Uses agent-native invocation syntax (slash for Claude/OpenClaude/Grok, dollar for Codex)
- Discovers skills only on the pane's execution host (local, WSL, SSH-unavailable, or runtime)
- Groups or separates commands and skills per agent configuration
- Deduplicates by canonical path but preserves visibility through all contributing roots
- Handles IME composition, loading states, and errors without claiming PTY-level control
- Records picker telemetry (open, item accepted, send classification, discovery outcomes)
- Extends shared agent profiles to define per-agent skill grammars and source ownership
* Remove obsolete reference and design documentation
Clean up stale design specs, implementation plans, and investigation notes from
docs/reference/. These documents predate the current implementation and are no
longer actively maintained or referenced by the codebase.
* Extract shared skill discovery utilities and add skill invocation envelo
- Move skill comparison and source classification to shared module for native/WSL reuse
- Extract display text sanitization to prevent control/zero-width character spoofing
- Add native-chat command envelope parser and surfacer for skill invocations
- Extend discovery timeout backstop to account for WSL metadata read sequence
* Localize skill picker UI for Spanish, Japanese, Korean, Chinese
Translate skill picker UI strings including commands, skills, loading
states, error messages, and scope labels for the new skill picker feature
across four language locales.
* Fix skill picker bugs and improve code robustness
- Fix i18n plural handling: rename `count` to `sourceCount` to prevent unintended plural-key resolution in localized strings
- Fix skill discovery array mutations: copy `root.providers` to prevent bugs during dedup merge
- Fix image attachments being silently dropped when message text starts with /skill or agent prefix
- Extract `quoteBashString` utility for WSL command code reuse across builders
- Add line-separator safety characters (0x2028/0x2029) to skill display filter
- Remove stale doc reference links and clarify inline comments
* Add reference docs for git compatibility and headless Linux server setup
Track previously untracked operational guides in `docs/reference/` that
explain Git binary compatibility requirements across host types and how to
run `orca serve` on headless Linux. Update AGENTS.md and README.md to link
to these references.
The previous commit updated the README to docs/assets/wechat-qr.jpg and
removed the old dual-QR assets, but never added the new image file, so
GitHub could not load it.
- Replace outdated dual-QR (primary + backup) layout with one QR code, since the first group filled up and members should join the second group instead
- Applies to both README.md and the zh-CN translation
- Remove the now-unused backup/png QR image assets
- Refresh star-history chart and backup WeChat QR image assets
- Add spacing between the primary and backup WeChat QR codes in both README.md and README.zh-CN.md
* docs(readme): fix Korean translation issues
* docs(readme): align agent list with English README across translations
Replace OpenClaude with Pi in Spanish, Japanese, and Chinese READMEs
to match the English README's agent list (Codex, ClaudeCode, OpenCode, Pi).
Portuguese already had the correct list.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
* fix(worktrees): stop surfacing prunable git worktrees as live workspaces
A worktree still registered in git but whose directory was deleted
(git's `prunable` state) was enumerated as a normal workspace,
producing repeated pty:spawn DaemonProtocolError / fs:readDir ENOENT
loops and a blank pane.
- Parse the `prunable` porcelain field (Git >= 2.36) in both the main
and relay worktree-list parsers.
- For Git < 2.36 (no `prunable` field), probe each linked worktree
path for existence on the fallback line-block path, skipping locked
registrations to mirror git's own prunable rules.
- Omit prunable worktrees from the detected-workspace enumeration only;
removal/cleanup flows keep seeing them.
- Extend the real-binary compatibility contract with the 2.36
`prunable` boundary.
Fixes#8389
Claude-Session: https://claude.ai/code/session_018Rg1Bpq4GGwmz613hq6RSD
* fix(worktrees): pin the prunable/locked porcelain annotations to their real Git 2.31 boundary
The prunable and locked annotations landed in Git 2.31, five releases
before `worktree list -z` (2.36); only -z defines the capability
fallback boundary. Correct the compatibility contract so a future
matrix entry in the 2.31-2.35 range passes, and reword the fallback
comments: on 2.31-2.35 the annotations still parse and the existence
probe is a backstop; only Git <2.31 relies on it outright.
* fix(worktrees): omit prunable registrations from the Space scan
A prunable registration has no directory to size or reclaim, so Space
rendered it as a dead "Missing" row whose checkbox stayed disabled with
no prune/remove affordance (reported on macOS after a reboot cleared
/private/tmp under 16 registrations). Skip prunable entries in the scan,
matching the workspace enumeration; removal flows list worktrees
separately and still see them.
---------
Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(terminal): kill agent descendant processes on session teardown (STA-1800)
Agent CLIs spawn tool children in detached process groups that PTY
SIGHUP can never reach. Killing an agent session (tab close, retire,
sleep) left those children running as orphans — eight orphaned git
processes burned ~8 cores for up to 11.5h under the agents-running
keep-awake and drained a battery to 8%.
New pty-descendant-termination module: snapshot the ppid tree BEFORE
signalling (a dead root's descendants reparent to pid 1 and become
unfindable), SIGTERM the root group and every descendant, then after a
2s grace SIGKILL survivors gated on a pid+start-time identity re-check
so a recycled pid is never signalled. Snapshot is bounded and never
rejects; failures degrade to today's shell-only kill.
Wired for agent sessions only (plain terminals keep nohup semantics) at
all three POSIX kill sites: local provider shutdown, daemon
TerminalHost immediate kill (the pty:kill path — force-kill bypassed
Session.kill entirely), and daemon Session graceful kill.
Verified live in the built app: an agent pane with a detached-pgid
child; the child survived on the unwired build (three control runs) and
dies within ~5s with the fix. Windows ConPTY and SSH-hosted PTYs keep
the previous foreground-tree contract (documented follow-ups).
* fix(terminal): harden descendant teardown
* fix(terminal): require fresh process snapshots
* fix(terminal): close descendant teardown races
* refactor(terminal): preserve teardown line budget
* fix(terminal): keep descendant teardown fresh and identity-safe
* docs(reliability): record integrated descendant E2E
* fix(terminal): bound descendant teardown work
* fix(terminal): share descendant snapshot indexes
* docs(reliability): record descendant review evidence
* revert: remove speculative descendant hardening
* feat(ssh): support Kerberos/GSSAPI hosts via the system OpenSSH transport
ssh2 has no gssapi-with-mic support, and adding it would mean forking its
protocol layer plus packaging the kerberos native module for three
platforms. Instead, route GSSAPI hosts through the existing system-OpenSSH
transport, which delegates Kerberos (tickets, SSPI on Windows) to the
platform ssh binary.
Two tiers, because RHEL-family distros enable GSSAPIAuthentication
globally in /etc/ssh/ssh_config and ssh -G therefore reports it for every
host:
- Targets whose ~/.ssh/config Host block explicitly sets
GSSAPIAuthentication yes (imported as target.gssapiAuthentication) try
system ssh first, falling through to ssh2 so key auth and credential
prompts still work when no ticket is available.
- When ssh2 exhausts key/agent auth and the ssh -G-resolved config
enables GSSAPI, retry over system ssh before prompting for credentials,
so Kerberos-only hosts on distro-default configs connect without a
password prompt. Hosts where keys work never leave the ssh2 path.
Manual targets flagged for GSSAPI pass -o GSSAPIAuthentication=yes
explicitly since they bypass ssh_config. Both tiers work headless (no
credential callbacks required).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(ssh): harden GSSAPI transport selection (review fixes for PR #7507)
Review fixes on top of the Kerberos/GSSAPI feature branch (s546126/kerberos-ssh):
- HIGH: reset useSystemSshTransport on the ssh2 fall-through. doSystemSshProbe
sets the flag before spawnSystemSshCommand, which throws synchronously when no
system ssh binary is on PATH (outside the probe try/catch). The proactive
fall-through previously reset only 2 of 3 transport fields, so exec/sftp kept
routing through the failed transport - breaking GSSAPI on Windows-with-Git-ssh
and headless Linux.
- MEDIUM: throw a cancellation error (not the stale ssh2 authError) when a
disconnect supersedes the reactive probe mid-flight, and guard connect()'s
catch on disposed, so a deliberate disconnect is not overwritten with
auth-failed.
- MEDIUM: skip the encrypted-key passphrase prompt when the GSSAPI fallback
applies, so a Kerberos ticket is tried before prompting; the general prompt
still fires if the probe fails.
Adds 3 mutation-verified regression tests and hardens two existing tests to
assert the probe actually ran. Not connected to any PR remote.
Co-authored-by: Orca <help@stably.ai>
* fix(ssh): isolate GSSAPI system transport
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: s546126 <268420947+s546126@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>