The contract field placement is hostAddress, required on every remote() (type and declaration error); the
fallback for remote types without one is gone. invalid/placement is invalid/host_address. host/placement.ts
(Placement) is host/destination.ts (Destination); here is decided by address alone, and a host without an
address refuses to serve a remote type. A hold always carries its address on the wire (the binary codec's
ADDRESS flag is gone, the text codec requires it). Tests and examples declare their host addresses.
Closes#11360.
## Summary
- Cap combined stdout/stderr with `ExecOptions.maxBuffer`, defaulting to
the smaller of 256 MiB and the runtime string limit. Invalid explicit
limits reject before spawning.
- Terminate oversized commands and return `truncated: true`, `killed:
true`, and a nonzero exit code. Close output pipes so writing
descendants cannot keep the call pending, and discard incomplete UTF-8
suffixes rather than adding replacement characters.
- Fix SIGKILL escalation for children that ignore SIGTERM, and simplify
completion to `await` with one cleanup block and result.
- Document the API and add regressions for byte limits, shell
descendants, UTF-8 chunk boundaries, and 32-bit runtime limits.
`ExecResult.truncated` is required. Existing extensions reading
execution results continue working; typed mocks or wrappers constructing
results must add the field.
## Verification
- `npm run build` and the post-rebase `npm run build:offline`
- `npm run check`
- 39 focused execution and extension tests
- `./test.sh` passes after building the env daemon. Earlier runs hit
remote-cleanup and reftable-watcher timeouts; both passed in focused
reruns and the final full run.
Registries: shared tokens (defineRegistry), one provider per host (host.serve(R, { compose, accepts })),
contributions from facets and host-lifetime objects, plain values or followed Chord watchables, a table
sorted by plugin, lazy compose in the batch that changed the table, failures left out and reported with
the last good value kept; self.registry as a declared dependency, host.registry outside objects, older
readers mapped by accepts[old].value; keyedEntries and orderedEntries. Native sources: self.writable,
registry values and ref.state. self.retryOn wakes failed objects on registry values. host.root gives code
outside objects a call scope sealed at settle, abort and host.close.
Durable: registryReader(value, context); a Session close ends synchronous observers failed
("session closed"), watches still end ended.
Connection.close() ends stdin and sends SIGTERM at once. The daemon did not
handle SIGTERM, so it died before its end-of-input kill_all, and running
commands were reparented to init. The daemon now blocks SIGTERM, SIGINT and
SIGHUP before starting any thread and waits for them on its own thread, which
kills every process group it started and exits. On Windows the job object
already kills commands with the daemon.
A failed activation releases its references after its natives closed; a dependency going live while
the dependent activates retries it at once; a call on a local object runs with the host's principal
and incarnation; retiring an unloaded document checks its stored version and fingerprint, and a cut
retirement no longer fails its successor; ref.retry() re-runs a refused hold's start on the same id
(the provider keeps refused holds), and ends released/closed on a closed host; a relayed hold waits
while the parent link is down; a link drop while a dependency is still loading is no failure.
The pi-env daemon started an `exec`'s stdout and stderr readers and its
waiter with `thread::spawn`. `thread::spawn` panics when no thread can
be created, for example when a cgroup `TasksMax` or `RLIMIT_NPROC` limit
is reached. When that happened after the child was already running:
- only the request thread unwound, so the exec never got a reply;
- its control channel was dropped, so `cancel` and `kill` reached
nobody;
- the child kept running, even after the daemon exited.
`pty` started its reader, input writer and waiter threads the same way.
## Changes
- `exec.rs`: `spawn_reader` now uses `thread::Builder::spawn` and
returns an `io::Result`. If a reader or the waiter cannot start, `run`
sets `stop`, interrupts any readers already started, kills the process
group, reaps the child, removes it from `groups`, and returns
`spawn_error`.
- The waiter thread now gets the `Child` through a channel once it is
running. If the waiter cannot start, the child stays with the caller,
which can still reap it. Before, the failed thread's closure dropped the
child without waiting for it.
- `pty.rs`: the same handling for the reader, writer and waiter threads.
On failure the terminal is also removed from `terminals`.
## Testing
- Manual reproduction with a script that runs the daemon as `prlimit
--nproc=N -- pi-env`. It starts 30 `sleep 30` execs 200 ms apart, aborts
them all, then closes the connection. N ran from 37 to 70.
- Before: for most N, one or two execs stayed pending forever after
abort, and a `sleep 30` kept running after the daemon was closed.
- After: every exec settles, either `aborted` or `spawn_error`, and no
`sleep` is left running.
- `cargo build`, `cargo clippy --all-targets`, `cargo fmt --check` and
`cargo test` all pass. The `packages/env` vitest suite passes (78
passed), and `npm run check` passes.
- There is no automated regression test. A thread-creation failure
cannot be triggered reliably in a test: `RLIMIT_NPROC` counts all of the
user's tasks and does not apply to root, and the daemon has no
fault-injection hook.
- The Windows build was not compiled. The `exec.rs` change uses only
existing cross-platform calls, and the `pty.rs` change is Unix-only.
Fixes#11433
Requested by @petrroll
---------
Co-authored-by: triagent <triagent@localhost>
Co-authored-by: Petr Houška <houskape@gmail.com>
StoreStorage (Durable's storage on Chord v2's store, @earendil-works/pi-durable/storage/store) passes
Durable's storage conformance suite and runs Durable's harness tests (npm run test:store). The store
gets owner names and claims, one id allocator, Durable's Storage semantics for refused steps, history
none/latest/rewindable with reads at a point, archive revisions and applyBatch. self.follow follows
Durable's synchronous observers as Chord sources. Persisted jobs: records and state in the owner's
store, restarts as Durable's tasks, abort stored, deferred state. Chord's ./next export returns for
the adapter surface.
stripAnsi treated <, = and > after ESC [ as final bytes, so ESC[<35;59;1M
(SGR mouse report) left 35;59;1M in tool output. Accept one private prefix
byte only directly after a CSI introducer (ESC [ or 0x9B). After a bare ESC
these bytes stay final bytes, so ESC= and ESC> (keypad modes) keep the
digits of the text that follows.
closes#11377
tui.select.pageUp/pageDown were configurable but ignored by SelectList,
SettingsList, editor autocomplete, and most coding-agent selectors. In
fullscreen mode, the default PageUp/PageDown also scrolled the transcript
instead of reaching the focused list.
- add getSelectPageIndex() and handle page keys in SelectList, SettingsList,
editor autocomplete, and the model, scoped models, auth, user message,
settings submenu, llama search, and /mcp selectors
- add Component.capturesViewportInput: a focused component that sets it
receives fullscreen viewport keys instead of the transcript, as a focused
overlay already does
- let page keys move an open autocomplete menu before app actions, so a
remapped Ctrl+V pages the menu instead of pasting an image
closes#7629
Co-authored-by: muyiyr <940955635@qq.com>