Skip to content

0.9.1 — 2026-05-11

Choose a tag to compare

@github-actions github-actions released this 11 May 14:48
05b3709

Release Notes

Patch release rolling up the post-0.9.0 daemon-stall fix (#55) and the
daemon-health deep-probe + SDK admin wrappers follow-up (#56), plus the
docs/CI groundwork from #51, #52, #54.

Added

  • daemon.health({deep: true}) shim probe. Adds ShimRequest::Health
    CBOR variant + shim-side handler returning
    ShimHealthInfo { uptime_ms, requests_served, last_request_at_ms }. The
    daemon fans out probes concurrently with per-shim and overall budgets
    (defaults 1 s / 3 s; env-overridable via LOOM_PROBE_TIMEOUT_MS and
    LOOM_DEEP_HEALTH_BUDGET_MS). Payload is now typed
    Vec<ShimDeepHealth> (replacing the prior placeholder
    Vec<serde_json::Value>) with a typed
    ProbeStatus { Ok, Timeout, Error } enum. The new shim variant
    requires same-tree daemon+shim shipping; version-mismatched shims
    surface as probe_status: "timeout". daemon.health continues to
    require the existing socket-auth token handshake — same auth surface
    as before; no anonymous reachability. Known gap: no per-connection
    rate limit on daemon.health itself (tracked as follow-up).
  • ShimState.restart_count / last_restart_at_ms. Daemon-side
    bookkeeping for shim respawn events, bumped in get_or_spawn only
    when a prior state entry exists (avoids overcounting under
    open-breaker rejection). Exposed via the deep-health payload.
  • SDK wrappers (TS + Python) for the admin RPCs. Hand-written
    Session.kill() (sync + async parity in Python), killSession()
    (TypeScript) / kill_session() (Python sync) admin free functions
    with documented "ADMIN ESCAPE HATCH — 5 s ceiling then SIGKILL"
    warnings, and daemonHealth() (TS) / daemon_health() (Python sync)
    free functions. JSON-RPC request id allocator switched from
    hardcoded 1 to monotonic per-connection; transports moved to
    id-keyed response demultiplexing (persistent reader task in async
    Python; persistent data listener in TS) to support request.cancel
    correlation while another call is in flight. Sync Python keeps
    single-in-flight and gains an AsyncSession-redirecting doc note for
    cancellation. TS adds AbortSignal support on call() via
    LoomAbortError (name === "AbortError"); Python async transport
    transparently emits request.cancel on asyncio.CancelledError and
    re-raises so asyncio.wait_for / TaskGroup / asyncio.timeout
    compose cleanly.
  • SessionScope primitive (loom-core/src/session_scope/).
    Per-session structured-concurrency parent: tokio_util::sync::CancellationToken
    paired with a tokio::task::JoinSet. All session-lifetime spawns become children;
    drain(grace) cancels cooperatively then force-aborts survivors. Replaces five
    fire-and-forget tokio::spawn sites that previously leaked JoinHandles and
    saturated the daemon runtime after 4–6 sequential client sessions.
  • Per-request server-side deadline. LOOM_REQUEST_TIMEOUT_MS (default
    30000) wraps router.dispatch so a hung shim or stuck dispatcher can't hold
    a connection task. Returns the typed request-timeout envelope on expiry.
  • request.cancel RPC. Connection-scoped: cancels an in-flight request on
    the same connection by JSON-RPC id. Returns {cancelled: bool}. The
    cancelled request returns the typed request-cancelled envelope.
  • session.kill RPC. Admin escape hatch for stuck sessions. Performs the
    abort flow plus blocks on shim teardown with a 5 s ceiling, then SIGKILL
    (shutdown_process already escalates SIGTERM(2s) → SIGKILL(1s) inside the
    ceiling).
  • daemon.health RPC. Operational snapshot: active_sessions,
    shim_breaker_states, otel_exporter status. Shallow path is
    non-blocking; {deep: true} slot reserved for a follow-up shim probe.
  • LoomErrorCode::RequestTimeout (wire string request-timeout) and
    LoomErrorCode::RequestCancelled (wire string request-cancelled).
    Both SemVer-minor additions per the existing wire-stability commitment.
  • Multi-session stress test at
    loom-core/tests/multi_session_stall_repro.rs
    — 100 sequential session create+close cycles complete in under 1 s; the
    user-facing success criterion from the original investigation prompt.

Changed

  • ShimManager::record_failure circuit-breaker eviction no longer fires
    tokio::spawn(shutdown_process(p)) fire-and-forget. Spawns into a
    cleanup_tasks: JoinSet<()> field with opportunistic try_join_next
    reaping on every record_failure and shutdown_session.
  • CoreBridge::close_session_raw (daemon-side) similarly tracks
    host.shutdown_session background tasks in a cleanup_tasks JoinSet
    instead of leaking them via bare tokio::spawn.
  • Session.budget_timer is now a SessionScope child with
    tokio::select! { _ = cancel.cancelled() => {} _ = sleep(budget_ms) => ... }
    so a closed session's timer exits cooperatively before its sleep
    completes, never tripping the kill callback after lifecycle exit.

Removed

  • Session.task_handle field — was dead code (declared but never
    populated). Replaced by the always-populated Session.scope: Arc<SessionScope>.
  • Session.budget_timer field — superseded by the SessionScope-owned
    cancellation-aware timer.
  • ReceiptMarshaller.queue_depth field + the docstring claiming
    "Background queue depth 256; full → synchronous append on the calling task".
    The field was dead and the docstring lied — there was no bounded channel,
    no try_send, no backpressure. Removed both.

Install loom-cli 0.9.1

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/mentiora-ai/loom/releases/download/v0.9.1/loom-cli-installer.sh | sh

Install prebuilt binaries via Homebrew

brew install mentiora-ai/loom/loom

Download loom-cli 0.9.1

File Platform Checksum
loom-cli-aarch64-apple-darwin.tar.xz Apple Silicon macOS checksum
loom-cli-x86_64-apple-darwin.tar.xz Intel macOS checksum
loom-cli-aarch64-unknown-linux-gnu.tar.xz ARM64 Linux checksum
loom-cli-x86_64-unknown-linux-gnu.tar.xz x64 Linux checksum