feat(ipc): runner-aware tools — protocol + transport (partial)#346
Draft
branchseer wants to merge 13 commits intomainfrom
Draft
feat(ipc): runner-aware tools — protocol + transport (partial)#346branchseer wants to merge 13 commits intomainfrom
branchseer wants to merge 13 commits intomainfrom
Conversation
Pure Rust vite_task_client library with napi-rs wrapper in vite_task_client_napi, exposing a demo `add` function as a Node.js native module. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> # Conflicts: # Cargo.toml # Conflicts: # Cargo.lock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Defines the shared IPC message types between the task runner and tools (Request, Response) using wincode's zero-copy schemas. Also documents the README.md convention for new crates and packages in CLAUDE.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vite_task_server exposes a `Handler` trait and a `serve(&handler, shutdown)` free function. The returned `!Send` future accepts clients until `shutdown` resolves, then stops accepting and drains in-flight per-client futures to completion. Uses `FuturesUnordered` (not `spawn_local`) so the handler can be borrowed and hold `!Send` state (`Rc`, `RefCell`). vite_task_client is a sync, blocking client with `&mut self` methods. `from_env()` reads `VP_RUN_IPC`; `from_name(&OsStr)` is the explicit form used by tests. Also: change `ResponseBody::Env(&NativeStr)` to `Env(Option<&NativeStr>)` so the server can signal env-not-found. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The driver now takes ownership of the handler (via internal `Rc<H>`), and returns it by move when drain completes. Callers no longer need to keep a separate reference or unwrap an `Rc` themselves. The external `shutdown` parameter is replaced by a `StopAccepting` handle exposed in `ServerHandle` — the internal `CancellationToken` is hidden from the public API. Also adds a design doc (docs/runner-task-ipc/server-design.md) covering the API, driver lifecycle, and the planned integration into `execute_spawn`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The driver future owns the handler via `Rc<H>` and only needs `H` to outlive the future. Parameterize `ServerHandle` and `serve` over a lifetime so callers with non-`'static` handlers are supported. For `'static` handlers (the expected common case), nothing changes at the call site — inference picks `'static`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The driver's async fn owns `handler: H` as a local. Per-client futures borrow `&handler` from the same async state — Rust's async-fn state machine makes the self-borrow sound (state is pinned, never moves). When drain completes, the outer async returns `handler` by move. Removes the internal `Rc<H>` + `Rc::try_unwrap` path (and the related panic doc). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add `IPC_ENV_NAME` constant in `vite_task_ipc_shared` as the single source of truth for the IPC env var name (shared between server and client, not exposed to callers). - `serve()` now returns `impl Iterator<Item = (&'static OsStr, OsString)>` instead of a bare `OsString` name. Callers chain the iterator directly into spawn's envs without knowing the env var name. - `Client::from_env()` → `from_envs(envs)`: takes an env-pair iterator, scans for the IPC env. Symmetric with server output. - Unify temp socket prefix to `vite_task_ipc_` on both platforms. - Drop lingering `VP_RUN_IPC` / `VP_RUN_CLIENT_NAPI` references from docs; the env var names are now internal to server/client crates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Recorder is a generic `Handler` impl parameterized by an env-lookup closure. It records: - ignored inputs / outputs (FxHashSet<Arc<AbsolutePath>>) - cache_disabled flag - env_records: name -> (tracked, resolved value), with `tracked` monotonically OR-ed across repeated get_env calls (once true, stays true) The runtime caller (vite_task in step 5) constructs `Recorder::new(|name| ...)` with a lookup closure over its own env source; after the driver resolves, `recorder.into_reports()` yields the `Reports` for the cache-update phase. Tests swap the ad-hoc `RecordingHandler` for `Recorder` — exercising the same code path runtime code will use. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Handler trait methods now take `&mut self`. The server wraps `H` in `RefCell` internally so the N per-client futures that coexist in `FuturesUnordered` can each call &mut-self methods; on the single-threaded runtime the borrow check can't fail because handler methods are sync (no borrow spans an await). Recorder drops the generic env-lookup closure in favor of a concrete `FxHashMap<Str, Arc<OsStr>>` taken at construction. Handler impl uses plain owned fields (no interior mutability inside the handler itself). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop the `id: u32` field from `Request::GetEnv` and the `Response`
wrapper. Each IPC connection is request-response sequential with a
single in-flight reply, so correlation adds no value. `Response` +
`ResponseBody` collapse into a single `GetEnvResponse { env_value }`.
- Switch env-var names in requests from `&str` to `&NativeStr`, and
`Handler::get_env` / `Client::get_env` to `&OsStr`. Handles non-UTF-8
env names correctly on both platforms.
- `Client::get_env` returns `Arc<OsStr>` (was `OsString`); `Client::recv`
is now generic over `T: SchemaRead` so future response types plug in
without touching the framing layer.
- `Recorder` keys `env_map` / `env_records` by `Arc<OsStr>` (was `Str`).
- Windows socket name: build via `format!` directly (name always exceeds
`Str` inline capacity, so the extra machinery provided no benefit).
- Drop `vite_str` dep from `vite_task_server`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splits Step 4 of the runner-aware-tools plan: replaces the napi crate stub with real module-level functions, adds the `@voidzero-dev/vite-task-client` JS package that lazily loads the addon and falls back to no-ops outside a `vp run` task, and renames the runner env vars to the `VP_RUN_` prefix (`VITE_TASK_IPC_NAME` -> `VP_RUN_IPC_NAME`, new `VP_RUN_NODE_CLIENT_PATH`). `vite_task_client::Client` now uses `&self` with `RefCell`s so the napi layer can store it as per-env instance data without a Mutex. Adds an end-to-end `#[ignore]` test driving the addon from Node against a live `vite_task_server`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Heads up: experimental, partial work
This is a large, exploratory undertaking to make tools runner-aware — i.e. let tasks communicate with the runner over IPC so they can report ignored inputs/outputs, requested env vars, or opt out of caching. Rather than land it as one giant PR, this opens the work early with only the foundational pieces, so design choices can be reviewed incrementally.
Tracking plan:
docs/runner-task-ipc/plan.md.Goal
Give task runners a bidirectional IPC channel with the processes they spawn, so:
What's in this PR (done)
vite_task_ipc_shared): message types + serialization shared by both ends.vite_task_server+vite_task_client): async server/client, tested Rust-to-Rust. Includes aRecorderhandler that will be the runtimeHandlerused byvite_task.docs/runner-task-ipc/.What's coming in future PRs
fspy(prerequisite for dylib embedding). Note: already landed onmainvia refactor: extract materialized_artifact crate out of fspy #344 asmaterialized_artifact— plan.md will be updated.vite_task_client_napiimpl +@voidzero-dev/vite-task-clientJS wrapper (withfetchEnvslogic).serve()'s returned iterator.Test plan
cargo testpasses for new crateshttps://claude.ai/code/session_01LKpCFB3asEF9LXjR4KttKU