Skip to content

Honor YAS_AUDIO=0 and YAS_AUDIO_BITRATE when PipeWire runs for media - #67

Open
mgasner wants to merge 30 commits into
mainfrom
fix/audio-disable-bitrate
Open

mgasner wants to merge 30 commits into
mainfrom
fix/audio-disable-bitrate

Conversation

@mgasner

@mgasner mgasner commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #66. Replaces #38, which was opened from indent-com/yas.

  • YAS_AUDIO=0: desktop audio is no longer offered to viewers, even while PipeWire is running for the camera, microphone, or portals.
  • YAS_AUDIO_BITRATE: a viewer set to "Desktop default" now gets the configured bitrate. Before this change it got a built-in 64 kbps.
  • Unavailable audio: when audio can't start, the server prints one line saying why.

View in Indent
Tag @indent to continue the conversation here.

🤖 Generated with Claude Code

@indent

indent Bot commented Sep 28, 2026

Copy link
Copy Markdown

Warning

This organization's free trial has ended, so Indent couldn't start this review. Manage billing to resume reviews on this repository.

handle, or before the Exit handler recorded its replay, left WAIT
answering NotFound (the intermittent
uplink_cli_remote_execution_and_authentication failure on
`yas run sh -c 'exit 7'`). This adds the regression test: spawn
`exit 7` 50 times and check WAIT returns code 7 after the exit event.
AbstractUnixSocketTransport length-prefixed every send, including the
8-byte preface, and left yasFraming unset, so YasConnection used message
framing and the server closed the link on the bad preface. The Node, Bun
and Deno Unix socket transports never reached HELLO.

The socket is a byte stream: the raw preface followed by u32
length-prefixed frames. Declare yasFraming = "stream" and pass bytes
through unchanged in both directions, as the WebTransport, WebRTC and
Relay transports do; YasConnection owns the framing.

Bun's socket.write accepts only what fits in the kernel buffer and drops
the rest, which corrupts a byte stream on large writes. Queue the
remainder and flush it on drain.
tsc with moduleResolution "bundler" emitted relative imports exactly as
written, without extensions, so importing the published @yas-run/core or
@yas-run/react dist from Node ESM failed with ERR_MODULE_NOT_FOUND.

Write explicit .js (or /index.js) extensions on every relative import and
compile with module/moduleResolution NodeNext, so tsc rejects an
extensionless relative import instead of shipping it. Solid's Vite build
already rewrote its JS; its emitted .d.ts now resolve under NodeNext too.
The Nix build's local @yas-run/browser package has no "type": "module", so
under NodeNext its default export types as the module namespace and tsc
rejects init() as not callable. Using the default export from the dynamic
import types the same way in both layouts.
@yas-run/core declared a peer on @yas-run/browser >=0.9.0, but the
browser package is versioned with each YAS release (0.3.0 today), so a
plain npm install failed with ERESOLVE.

Use ^0.3.0 and have prepare-release stamp ^<version> alongside the
package versions. The browser package is built from crates/browser
outside the pnpm workspace, so workspace:* cannot do this at publish.

Document the install and wait for the workspace to be ready before
creating a session in the Node example; createSession throws until the
Terminal family is negotiated.
YasSurfaceCanvas and both views default touchMode to "direct"; the view
prop comments claimed pointer emulation.
`yas remote add` writes the home server's `remotes` KV key, but
`--on NAME`, YAS_TARGET and `yas.target` were looked up in the legacy
yas.remotes file, so a freshly added remote was an unknown target and
`yas remote set-default NAME` broke every command.

Bare names now resolve against the home server (YAS_SOCK, else the
default local instance), following name-to-name entries with cycle
detection and refusing disabled entries. `yas remote` verbs without
--on also edit the home server rather than the default target, so
`toggle` after `set-default` reaches the catalogue that defines the
name. Help text and docs describe the catalogue instead of the file.
read_remotes_full wrote `local = local` to a missing yas.remotes, so a
lookup created a file. A missing file now reads as an empty list.
A SEQUENCE cursor beyond the terminal's output produced an OutputResult
whose start lay after its next cursor. Encoding rejected it and the
server answered Internal, so `yas terminal history ID --since SEQ` with
a future cursor failed. seq_text now clamps such a cursor to the end and
returns no text.
The CLI capped the collected OUTPUT reply at the requested text size, but
the reply is an encoded OutputResult with 36 bytes of fixed fields ahead
of the text. The cursor probe asks for 1 byte of text, so every probe
failed with "query returned 36 bytes; collection limit is 1". That broke
`yas terminal wait --pattern` and `yas terminal history --since now`
outright, and a full-size `output`/`history` page could trip the same
check. The collection limit now adds the framing.

With the probe working, `wait --pattern` again takes the cursor before
reading and matches only output produced after the wait began.
A command wait that expired before any command it could report on had
started answered NOT_FOUND, so `output --wait` without an index exited 1
with a NotFound error instead of the documented 124. The server now
answers TIMEOUT in that case (NOT_FOUND stays for an exited terminal or
an evicted index), and the CLI maps it to exit 124 with a "timed out"
message. A command that is still running at the deadline also prints a
timeout note alongside the existing 124.

The index-less wait keeps waiting for the running command, or for the
next one to start when none is running, so `send` followed by `output
--wait` cannot race to the previous command's result. The help,
learn.md, and design docs now say so.

Adds a server test covering this and the past-end OUTPUT cursor.
`yas terminal start --wait CMD | head -1` panicked with "failed printing
to stdout: Broken pipe" once head exited, because the terminal commands
printed with println!. They now write through a helper that treats
EPIPE as success, so the command finishes normally and exits with its
own status (the command's exit code for `start --wait`). Other stdout
errors are reported on stderr.
WRITE is a fire-and-forget event, so sending to an exited terminal (or
a nonexistent id) silently did nothing and exited 0. The CLI now looks
the terminal up first and fails with a clear error and exit 1 when it
does not exist or has exited, pointing at `yas terminal restart`.
Restart: the replayable launch record lived in a per-connection map, so
RESTART with REPLAY only worked on the connection that created the
terminal. Every CLI command opens a new connection, so `yas terminal
restart` always answered Unsupported. The launch record now lives on the
terminal itself and is replaced by a successful REPLACE restart.

Environment: --export-sock / YAS_EXPORT_SOCK and --inject-path /
YAS_INJECT_PATH only applied to the Derived environment base, which
native launches never use. They now apply to the Server base as well.
A Server-base terminal also no longer inherits a YAS_SOCK that names a
different server (for example when the server was started from a shell
inside another YAS server); an inherited YAS_SOCK equal to this server's
own socket is kept, and --export-sock always sets it.

Missing program: a CREATE whose argv program is not on the child's PATH
answered ResourceExhausted, and a RESTART with one answered Io. Both now
answer NotFound, and `yas terminal start` prints "PROGRAM: command not
found".
… them

follow_until_terminal returned as soon as a snapshot showed the definition
stopped, skipping any attempt it had not followed yet. A short attempt that
started and finished between two snapshots printed nothing. Follow every
new attempt before honoring a terminal phase; FOLLOW replays retained output
for a stopped attempt.

Human output now also names a trapped or failed attempt and its detail on
stderr, and a DEPLOY conflict on a name still held by another definition says
which one and what to do, instead of 'Conflict: no detail'.
It only accepts https:// or loopback http:// URLs, never a directory.
yas clipboard set offers text/plain;charset=utf-8, but get asks for exactly
text/plain by default, so set-then-get failed with NotFound. Get now resolves
a plain-text request (text/plain with or without a charset, or UTF8_STRING)
to whichever plain-text type the owner offered.
- capture --scale now resizes the surface with the RESIZE scale extension
  before capturing instead of being rejected.
- capture --quality is removed: Surface CAPTURE has no quality input.
- record --codec h264-444/av1-444 announce 4:4:4 through the view color
  capabilities extension instead of being rejected.
- record help names the real default files (.h264, or .obu for AV1).
- surface scroll accepts a negative amount, as learn.md shows.
- YAS_AUDIO=0 only kept PipeWire from starting when media input and portals
  were also off. With either on, the runtime started and desktop audio was
  still offered and streamed. The output device is now unavailable and
  OPEN_OUTPUT is refused under YAS_AUDIO=0 regardless of why PipeWire runs.
- A viewer at "Desktop default" (0 kbps) reset the encoder to a built-in
  64 kbps, overriding YAS_AUDIO_BITRATE. The configured bitrate is now what
  a server-default viewer, the last unsubscribe, and a pipeline restart use.
- When audio cannot start and was not disabled, print one line naming the
  reason at normal verbosity; -v keeps the detailed lines.
@mgasner
mgasner force-pushed the fix/audio-disable-bitrate branch from e7afe8d to 82908a4 Compare September 29, 2026 00:01
@mgasner
mgasner force-pushed the fix/surface-capture-record-flags branch from 0379371 to 4f4945f Compare September 29, 2026 00:01
@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Coverage

Crate Lines Functions Regions
alacritty-driver 85.6% (1081/1263) 89.4% (84/94) 89.3% (1746/1955)
browser 25.5% (309/1212) 30.4% (34/112) 27.7% (605/2183)
cli 34.1% (6733/19768) 36.2% (638/1762) 35.2% (10006/28433)
client 68.3% (5800/8497) 68.5% (708/1033) 66.4% (7525/11331)
composite-transport 96.3% (526/546) 98.4% (60/61) 96.2% (884/919)
compositor 55.0% (11454/20820) 66.2% (837/1265) 54.9% (15867/28909)
desktop 78.4% (4460/5691) 71.6% (393/549) 75.1% (6211/8267)
edge 62.5% (793/1268) 50.3% (81/161) 57.1% (1027/1799)
fonts 77.3% (1257/1626) 82.7% (129/156) 78.9% (2424/3071)
fssync 85.6% (1602/1872) 85.4% (181/212) 86.7% (2822/3255)
git 70.5% (4472/6344) 68.5% (337/492) 67.3% (6332/9407)
guest 67.3% (6829/10148) 67.0% (488/728) 66.6% (8935/13416)
lsp 78.7% (3573/4542) 80.8% (336/416) 76.7% (5408/7054)
proxy 63.1% (1902/3012) 56.3% (184/327) 63.4% (2931/4626)
runtime-dir 93.6% (117/125) 100.0% (14/14) 94.8% (218/230)
sd-notify 73.9% (68/92) 100.0% (6/6) 83.2% (109/131)
server 71.1% (85723/120546) 73.7% (6225/8446) 69.1% (114954/166252)
ssh 67.1% (707/1054) 75.9% (85/112) 67.1% (1103/1645)
terminal-model 49.9% (314/629) 62.3% (38/61) 50.3% (505/1004)
uplink 94.2% (582/618) 92.6% (50/54) 93.1% (1062/1141)
webrtc-forwarder 33.6% (1321/3932) 44.9% (146/325) 36.0% (2279/6336)
webserver 80.7% (1490/1846) 81.1% (193/238) 83.2% (2551/3067)
website 35.1% (355/1012) 34.4% (53/154) 35.8% (607/1694)
xtask 0.0% (0/5149) 0.0% (0/131) 0.0% (0/9157)
yas 88.9% (28036/31522) 94.1% (2151/2286) 84.1% (44035/52388)
Total 67.0% (169504/253134) 70.1% (13451/19195) 65.3% (240146/367670)

mgasner and others added 3 commits September 29, 2026 10:12
yas clipboard set offers text/plain;charset=utf-8, but get asks for exactly
text/plain by default, so set-then-get failed with NotFound. Get now resolves
a plain-text request (text/plain with or without a charset, or UTF8_STRING)
to whichever plain-text type the owner offered.

@ghost ghost left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ultimator's review of 6903a67

Gates desktop audio output on YAS_AUDIO separately from the PipeWire runtime, so YAS_AUDIO=0 no longer offers audio when PipeWire runs for media. Makes YAS_AUDIO_BITRATE the "server default" bitrate everywhere, and prints one line when audio can't start. The logic looks right; I found one misplaced doc comment.

1 issue: 1 nit

The open issues and Fix are in the review of 0f93884.

Reviewed in 21s · Session · @ultimator-agent review reviews again · @ultimator-agent fix fixes the open issues

/// Returns the list of required PipeWire / D-Bus binaries that are not
/// found on `$PATH`. Empty list means audio can run (provided
/// libpipewire is also loadable at runtime; see `pipewire_available`).
/// One-line reason desktop audio could not start, for normal verbosity.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚪ Nit · New function split missing_pipewire_binaries from its doc comment

unavailable_reason was inserted between the existing doc comment of missing_pipewire_binaries ("Returns the list of required PipeWire / D-Bus binaries that are not found on $PATH...", lines 499-501) and that function. That comment now documents unavailable_reason, with a second doc line stacked under it, and missing_pipewire_binaries has none.

Ultimator · issue #1 · reply @ultimator-agent fix 1 to have it fixed

Stacked on #67. Replaces #39, which was opened from indent-com/yas.

The help overlay now labels Ctrl+B r "Preview panel" and Tab "Prev / Next pane", which matches what those bindings do.

[![View in Indent](https://assets.indent.com/view-in-indent.svg)](https://app.indent.com/c/01a0db15-330c-717e-9ec3-032c80770b1c)
Tag `@indent` to continue the conversation here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Stacked on #69. Replaces #41, which was opened from indent-com/yas.

**Restacked onto #25.** #25 moved the native client into `yas-client`, so `Error::GoAway` gained `reason: Option<String>`. Its `Display` prints `disconnected by the YAS server: REASON` when the reason is set. The test is in `crates/client/src/error.rs`.

The reason given when a client is kicked now reaches that client. It travels as a new optional Core extension in GOAWAY detail (`GOAWAY_REASON_EXTENSION`, regenerated schema). The CLI prints `yas: disconnected by the YAS server: REASON`.

[![View in Indent](https://assets.indent.com/view-in-indent.svg)](https://app.indent.com/c/01a0db15-330c-717e-9ec3-032c80770b1c)
Tag `@indent` to continue the conversation here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

@ghost ghost left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ultimator's review of 0f93884

The new commit 1916484 is #68 (help overlay labels, yas open flag refusal) merged into this branch, and it doesn't touch the audio changes. The misplaced doc comment in audio.rs is still there, so issue #1 stays open.

No new issues.

The open issues and Fix are in the review of 0f93884.

Reviewed in 10s · Session · @ultimator-agent review reviews again · @ultimator-agent fix fixes the open issues

Base automatically changed from fix/surface-capture-record-flags to fix/ext-run-output October 10, 2026 15:10

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ultimator's review of 0f93884

The two new commits are other PRs' work merged into this branch (#68 help overlay and yas open flags, #70 GOAWAY disconnect reason), and the base moved to fix/ext-run-output. The audio changes are untouched, so issue #1 (misplaced doc comment at audio.rs:502) is still open.

No new issues.

Open issues: check the ones to fix, then Fix.

  • ⚪ #1 New function split missing_pipewire_binaries from its doc comment · crates/server/src/audio.rs:502
  • 🔧 Fix the checked issues (all of them if none is checked)

Reviewed in 9s · Session · @ultimator-agent review reviews again · @ultimator-agent fix fixes the open issues

Base automatically changed from fix/ext-run-output to main October 10, 2026 16:34

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants