cargo-hauler 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +59 -17
  3. package/dist/.claude-plugin/marketplace.json +1 -1
  4. package/dist/.claude-plugin/plugin.json +1 -1
  5. package/dist/.codex-plugin/plugin.json +1 -1
  6. package/dist/.cursor-plugin/plugin.json +1 -1
  7. package/dist/AGENTS.md +2 -0
  8. package/dist/INSTALL.md +10 -11
  9. package/dist/README.md +59 -17
  10. package/dist/agent-bundle.compile-evidence.json +1 -1
  11. package/dist/agent-bundle.manifest.json +1 -1
  12. package/dist/agent-bundle.package-compile-evidence.json +1 -1
  13. package/dist/bin/cargo-hauler-flight.mjs +525 -93
  14. package/dist/bin/cargo-hauler-install.js +399 -72
  15. package/dist/bin/cargo-hauler.mjs +879 -206
  16. package/dist/bin/hauler.js +735 -162
  17. package/dist/hooks/event-route-session-start.claude.mjs +183 -5
  18. package/dist/hooks/event-route-session-start.codex.mjs +183 -5
  19. package/dist/hooks/event-route-session-start.cursor.mjs +183 -5
  20. package/dist/hooks/event-route-stop.claude.mjs +183 -5
  21. package/dist/hooks/event-route-stop.codex.mjs +183 -5
  22. package/dist/hooks/event-route-stop.cursor.mjs +183 -5
  23. package/dist/hooks/event-route-tool-after.claude.execute.mjs +183 -5
  24. package/dist/hooks/event-route-tool-after.claude.mjs +551 -103
  25. package/dist/hooks/event-route-tool-after.codex.execute.mjs +183 -5
  26. package/dist/hooks/event-route-tool-after.codex.mjs +551 -103
  27. package/dist/hooks/event-route-tool-after.cursor.execute.mjs +183 -5
  28. package/dist/hooks/event-route-tool-after.cursor.mjs +551 -103
  29. package/dist/hooks/event-route-tool-before.claude.execute.mjs +183 -5
  30. package/dist/hooks/event-route-tool-before.claude.mjs +201 -23
  31. package/dist/hooks/event-route-tool-before.codex.execute.mjs +183 -5
  32. package/dist/hooks/event-route-tool-before.codex.mjs +201 -23
  33. package/dist/hooks/event-route-tool-before.cursor.execute.mjs +183 -5
  34. package/dist/hooks/event-route-tool-before.cursor.mjs +201 -23
  35. package/dist/hooks/hooks-flight.mjs +493 -160
  36. package/dist/install.mjs +2 -2
  37. package/dist/mcp/mcp-hauler-2b8242f9-flight.mjs +570 -172
  38. package/dist/mcp/mcp-hauler-2b8242f9.mjs +683 -98
  39. package/dist/mcp-apps/dashboard.html +7 -7
  40. package/dist/package.json +3 -3
  41. package/dist/plugin.json +1 -1
  42. package/dist/scripts/hauler.mjs +735 -162
  43. package/dist/skills/cargo-hauler/SKILL.md +4 -0
  44. package/dist/skills/hauler-dashboard/SKILL.md +1 -1
  45. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 4267d0c: Keep read-only commands and MCP tools on a protocol-compatible running daemon, and defer automatic replacement for submissions until the older daemon is idle (#209).
8
+
9
+ Initialize the dashboard on every MCP Apps host — Codex, Claude, ChatGPT, VS Code/Cursor, and Goose — without host-specific origin exceptions (#212).
10
+
11
+ ## 0.7.3
12
+
13
+ ### Patch Changes
14
+
15
+ - 5d14c4f: Initialize the dashboard in Codex Desktop when MCP Apps use a
16
+ `codex-sandbox://` origin (#206).
17
+ - 4ac68a9: Make `hauler daemon stop` and `cargo-hauler daemon stop` preserve typed shutdown outcomes and fail when the daemon refuses, times out, returns a protocol error, disconnects before acknowledgement, or remains alive (#200).
18
+ - bc2ed53: Make daemon state and relocated control sockets owner-private on Linux and
19
+ macOS. The state directory, `tickets/`, and the socket's runtime directory
20
+ are created `0700` and sensitive files — complete ticket output, the ledger
21
+ and its WAL sidecars, the daemon log, the passthrough spool, `hook-state.json`,
22
+ `hook-events.jsonl`, the pid lock, and the jobserver FIFO — `0600`,
23
+ independent of the invoking shell's umask, instead of inheriting `0755`/`0644`.
24
+ An existing state directory the running user owns is tightened in place; a
25
+ parent named by `CARGO_HAULER_STATE_DIR` is never chmod'ed, and a state path
26
+ that is a symlink, the wrong kind of entry, or owned by another user is
27
+ refused by name rather than followed, chmod'ed, or deleted. A state directory
28
+ too deep for `sun_path` now puts its socket in a `cargo-hauler-<uid>`
29
+ directory under `XDG_RUNTIME_DIR`/`TMPDIR`/the system temporary directory
30
+ instead of directly in a possibly shared temporary root, and the socket
31
+ digest no longer lowercases Unix paths, so case-distinct state directories no
32
+ longer share one control endpoint. A daemon left listening at the previous
33
+ relocated path is retired by the next client under the existing one-version
34
+ rule and stopped by `hauler daemon stop`, so the moved endpoint needs no
35
+ manual cleanup on upgrade. Windows keeps its named pipe and gains no POSIX
36
+ modes or uids. (#203)
37
+
3
38
  ## 0.7.2
4
39
 
5
40
  ### Patch Changes
package/README.md CHANGED
@@ -85,7 +85,7 @@ The CLI is `hauler` on PATH from `npm i -g cargo-hauler`. Never run
85
85
  | `hauler result <ticket> [--full]` | A stored ticket in full: the settled 16 KiB output tail, or the whole live in-memory tail while it runs (not the status preview). The document names the full on-disk output log (`Full output: <path> (size)`) and `--json` carries it as `request.outputPath`; `--full` prints that whole log instead of the tail (the last ~768 KiB when it does not fit, with the path for the rest). |
86
86
  | `hauler kill <ticket>` | Stop a ticket: drop it from the queue or SIGTERM (then SIGKILL) its cargo process group, freeing the lane. Riders return to their lane or fail with it. |
87
87
  | `hauler request [--session ID] [--host HOST] [--cwd DIR] [--after TICKET …] -- <cargo …>` | Submit a background request and return its ticket, with where it landed in its lane (`queued behind cc-3281 (~13m)`, `waiting for cc-3281`, or `attached to cc-3281`). `--cwd` overrides the current CLI workspace; `--after` works as for `exec`. |
88
- | `hauler daemon <run\|start\|stop\|status\|restart>` | Manage the daemon lifecycle. `restart` is the manual replacement: it sends the graceful stop, waits up to 5 s for the old pid to exit, then starts a daemon from this install and prints both (`restarted: pid 741314 (0.6.0) → pid 742001 (0.6.1)`); a daemon that has not exited by then is reported, not killed, and nothing is started (exit `1`). Tickets in flight are not handed over: the old daemon settles them itself as it shuts down — `killed`, error `daemon shutdown` — and callers resubmit (only rows a daemon that died without shutting down never marked are stamped `orphaned by daemon restart` by the next daemon's first ledger pass). After upgrading the package, every client entry — reads (`status`, `daemon status`, `log`, `last`, `await`, `result`, the dashboard and MCP tools), writes, and hooks — checks the daemon version before requesting a versioned payload and replaces a daemon from the previous install automatically. When the old daemon has not exited within the grace, the command fails with `` cargo-hauler daemon pid N (X.Y.Z) is still running 5s after the shutdown request; not restarted — retry once it has exited, or stop it with `hauler daemon stop` `` instead of parsing its payload or starting a second daemon. |
88
+ | `hauler daemon <run\|start\|stop\|status\|restart>` | Manage the daemon lifecycle. `stop` records its typed `shutdown` outcome in JSON and exits `0` only after a `shutting-down` acknowledgement followed by the original pid's exit, or when the daemon was already absent. Refusal, timeout, protocol error, disconnect before acknowledgement, and an acknowledged daemon still alive after 5 s exit `1`; `running` remains `true` when the original pid is alive and `null` when liveness could not be established. `restart` is the explicit replacement path: it sends the graceful stop, waits up to 5 s for the old pid to exit, then starts a daemon from this install and prints both (`restarted: pid 741314 (0.6.0) → pid 742001 (0.6.1)`). Tickets in flight are not handed over: the old daemon settles them itself as it shuts down — `killed`, error `daemon shutdown` — and callers resubmit. Automatic upgrades are gentler: read-only commands and MCP/dashboard reads never retire a daemon; a protocol-compatible older daemon serves them directly. Submission commands replace an older daemon only after an idle status check; daemons from 0.7.4 onward hold admission closed while confirming that state, while 0.7.1–0.7.3 receive a client preflight. A busy or slow-to-retire daemon keeps serving the submission and emits one line such as `daemon 0.7.1 will be replaced by 0.7.3 when idle`. A truly incompatible daemon is reported with its pid and version instead of having its payload parsed. |
89
89
  | `hauler install-shim [--dir DIR] [--real-cargo PATH] [--force]` | Install the optional PATH shim. |
90
90
  | `hauler web [--port N] [--no-open]` | Open the dashboard from the checkout, npm package, or installed plugin. Agent Bundle's generated web command serves the built App against the plugin's own `hauler` server, opens it populated by `hauler_status`, and stays in the foreground until Ctrl-C. In an MCP host, call `hauler_status` instead. |
91
91
 
@@ -448,9 +448,13 @@ ever flagged.
448
448
  Tickets do not survive a daemon stop; runs are never handed over to the next
449
449
  daemon. How a ticket ends depends on how the daemon went. A graceful stop —
450
450
  `hauler daemon restart`, `hauler daemon stop`, or the automatic replacement of
451
- a daemon from another install by the next `hauler exec`, `hauler request`,
452
- hook call, or `hauler daemon start` — is the shutdown request, and the old
453
- daemon settles every queued, running, and attached ticket itself as it exits:
451
+ an idle older daemon by the next `hauler exec`, `hauler request`, hook
452
+ submission, or `hauler daemon start` — is the shutdown request. Automatic
453
+ replacement checks for queued, running, executing, or attached work first;
454
+ 0.7.4 and later daemons hold admission closed through that decision, while
455
+ 0.7.1–0.7.3 receive the compatible client's status preflight. When a stop does
456
+ proceed, the old daemon settles every queued, running, and attached ticket
457
+ itself as it exits:
454
458
  its cargo processes are terminated (SIGTERM, then SIGKILL after
455
459
  `CARGO_HAULER_KILL_GRACE_MS`) and each row is marked `killed` with the error
456
460
  `daemon shutdown`, so `hauler result cc-N` shows the ticket `killed` with
@@ -635,7 +639,7 @@ Per-host notes and hook timeouts are in [docs/install.md](docs/install.md).
635
639
 
636
640
  | Variable | Default | Meaning |
637
641
  | --- | --- | --- |
638
- | `CARGO_HAULER_STATE_DIR` | Per-user cache directory | Unix socket or Windows named pipe source, SQLite ledger, daemon log, pid lock, `hook-state.json`, `hook-events.jsonl`, and the per-ticket output logs under `tickets/`. |
642
+ | `CARGO_HAULER_STATE_DIR` | Per-user cache directory | Unix socket or Windows named pipe source, SQLite ledger, daemon log, pid lock, `hook-state.json`, `hook-events.jsonl`, and the per-ticket output logs under `tickets/`. Owner-private on Linux and macOS: the directory is `0700` and its sensitive files `0600`, and an unsafe or unowned path is refused rather than reused. |
639
643
  | `CARGO_HAULER_CARGO_BIN` | `$CARGO_HOME/bin/cargo` | Cargo binary for daemon-started work; bare `cargo` is the last fallback. Never resolved through `PATH`. Read from the daemon's own environment (export it where the daemon starts, or before `hauler daemon start`); clients do not forward it. |
640
644
  | `CARGO_HAULER_MAX_CONCURRENT` | cores ÷ 8, clamped to 5–16 | Global admission permits for Cargo processes across all lanes; an integer >= 1. |
641
645
  | `CARGO_HAULER_OVERLAP_EXECUTION` | `1` | Hand a lane to its next request once a `test`/`nextest`/`bench`/`run` leader reports its build finished, overlapping the next compile with the leader's execution phase. `0` keeps a lane strictly one process at a time. |
@@ -672,11 +676,50 @@ as a warning (daemon log, or stderr for hand-run commands) and the default
672
676
  applies; only `0` or `off` disables an arm that documents that contract.
673
677
  The state directory defaults to `$XDG_CACHE_HOME/cargo-hauler`, otherwise
674
678
  `~/.cache/cargo-hauler` on Linux, `~/Library/Caches/cargo-hauler` on macOS, and
675
- `%LOCALAPPDATA%\cargo-hauler` on Windows. When `CARGO_HAULER_KACHE_INDEX` is
679
+ `%LOCALAPPDATA%\cargo-hauler` on Windows. Its contents are owner-private, and
680
+ the daemon is a single user's, not a shared service — see
681
+ [State directory ownership](#state-directory-ownership). When
682
+ `CARGO_HAULER_KACHE_INDEX` is
676
683
  unset, the daemon reads kache's configured local store from
677
684
  `$XDG_CONFIG_HOME/kache/config.toml` or `~/.config/kache/config.toml` and opens
678
685
  `<local_store>/index.db` read-only.
679
686
 
687
+ ### State directory ownership
688
+
689
+ The daemon is local control for the user who owns its state directory, not a
690
+ shared multi-user service. Everything under the state directory — complete
691
+ command output in `tickets/`, the ledger, the daemon log, the passthrough
692
+ spool, the hook records, the jobserver FIFO, the control socket — is that
693
+ one account's, and there is no mechanism for a second user to submit work to
694
+ another user's daemon.
695
+
696
+ On Linux and macOS the daemon enforces that boundary rather than inheriting
697
+ it from the umask:
698
+
699
+ - Directories cargo-hauler creates are `0700`, and sensitive files `0600`,
700
+ whatever umask the invoking shell had. An existing state directory that
701
+ the running user already owns is tightened in place on the next start;
702
+ its contents are preserved.
703
+ - Only the state directory and the entries cargo-hauler owns are changed. A
704
+ parent you configured through `CARGO_HAULER_STATE_DIR` — a shared volume,
705
+ a RAM disk — is never chmod'ed.
706
+ - A state path that is a symbolic link, is the wrong kind of entry, or is
707
+ owned by another user is refused by name instead of being followed,
708
+ chmod'ed, or deleted. Fix or remove the path and start again; cargo-hauler
709
+ will not act on another account's file on your behalf.
710
+ - When the state directory is too deep for the kernel's socket path limit,
711
+ the control socket moves to a `cargo-hauler-<uid>` directory (mode `0700`)
712
+ under `XDG_RUNTIME_DIR`, `TMPDIR`, or the system temporary directory —
713
+ never directly into a shared temporary root. Two accounts sharing one
714
+ temporary root get separate directories. A daemon from an earlier install
715
+ still listening at the previous relocated path is considered only by a
716
+ daemon-starting submission, never by a read; `hauler daemon stop` asks it
717
+ too before reporting nothing running.
718
+
719
+ Windows has neither POSIX modes nor uids, and its control endpoint is a
720
+ named pipe rather than a filesystem entry, so none of the above applies
721
+ there; state files keep the permissions the filesystem gives them.
722
+
680
723
  ## Runtime behavior and caveats
681
724
 
682
725
  - Hook and client transport failures fail open: a hook passes the original
@@ -689,14 +732,11 @@ unset, the daemon reads kache's configured local store from
689
732
  `hauler_await` fail loudly when the daemon is unreachable instead of
690
733
  reporting a ticket as not found; `hauler_status`, `hauler_log`, and
691
734
  `hauler_last` read the ledger with the daemon marked `stopped` or
692
- `unresponsive`. Before any live daemon reply is parsed, these reads apply
693
- the one-version rule and replace a daemon left running by a previous
694
- install. Replacement is directional: only a newer install replaces a
695
- daemon. A client older than the daemon it finds — a session still on a
696
- previous plugin — never shuts it down (the daemon refuses a shutdown from
697
- an older or unversioned client), reports the daemon as newer, and runs
698
- cargo directly. If replacement fails, the reads report that failure
699
- instead of reading the stale payload.
735
+ `unresponsive`. Reads never request daemon shutdown. They use an older
736
+ daemon when its wire-protocol identity is compatible; a truly incompatible
737
+ daemon is reported with its pid and version. A client older than the daemon
738
+ it finds — a session still on a previous plugin — keeps the directional
739
+ `DaemonNewer` behavior and never shuts it down.
700
740
  - The state directory is not migrated between installs. Every rendered
701
741
  document names the one in use (`state dir …` in the header; `stateRoot` in
702
742
  `--json`), so a `CARGO_HAULER_STATE_DIR` change is visible on the next
@@ -946,9 +986,11 @@ Workbench's MCP page preview the same App with live rebuilds. The repository
946
986
  ships no preview harness of its own.
947
987
 
948
988
  agent-bundle does not yet have an npm release; this repository pins the
949
- [pkg.pr.new](https://pkg.pr.new) preview of Agent Bundle PR #733 commit
950
- [`5064133`](https://github.com/ScriptedAlchemy/agent-bundle/commit/506413326359bff154ce4476eb0f47b0dabf4efe)
951
- for both `agent-bundle` and `@agent-bundle/runtime`. `inspect` reports the
989
+ [pkg.pr.new](https://pkg.pr.new) preview of Agent Bundle main commit
990
+ [`e040c32`](https://github.com/ScriptedAlchemy/agent-bundle/commit/e040c32591d586eba72da6fa5c1bc1c881993000),
991
+ built from [Agent Bundle PR #779](https://github.com/ScriptedAlchemy/agent-bundle/pull/779),
992
+ for `agent-bundle`, `@agent-bundle/runtime`, and their paired
993
+ `rsc-markdown-stream` preview. `inspect` reports the
952
994
  `agent` component kind as unavailable on every host (agent-bundle G5
953
995
  deferral); this plugin defines no agents.
954
996
 
@@ -1 +1 @@
1
- {"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler-marketplace","owner":{"name":"cargo-hauler"},"plugins":[{"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler","source":"./","version":"0.7.2"}]}
1
+ {"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler-marketplace","owner":{"name":"cargo-hauler"},"plugins":[{"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","source":"./","version":"0.7.4"}]}
@@ -1 +1 @@
1
- {"author":{"name":"cargo-hauler"},"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler","version":"0.7.2"}
1
+ {"author":{"name":"cargo-hauler"},"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler","version":"0.7.4"}
@@ -1 +1 @@
1
- {"author":{"name":"cargo-hauler"},"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","hooks":"./.codex-plugin/hooks.json","interface":{"capabilities":["mcp","hooks","skills"],"category":"Productivity","defaultPrompt":["Help me use cargo-hauler."],"developerName":"cargo-hauler","displayName":"cargo-hauler","longDescription":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","shortDescription":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks."},"mcpServers":"./.codex-plugin/mcp.json","name":"cargo-hauler","skills":"./skills/","version":"0.7.2"}
1
+ {"author":{"name":"cargo-hauler"},"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","hooks":"./.codex-plugin/hooks.json","interface":{"capabilities":["mcp","hooks","skills"],"category":"Productivity","defaultPrompt":["Help me use cargo-hauler."],"developerName":"cargo-hauler","displayName":"cargo-hauler","longDescription":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","shortDescription":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks."},"keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","mcpServers":"./.codex-plugin/mcp.json","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","skills":"./skills/","version":"0.7.4"}
@@ -1 +1 @@
1
- {"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","displayName":"cargo-hauler","hooks":"./.cursor-plugin/hooks.json","mcpServers":"./.cursor-plugin/mcp.json","name":"cargo-hauler","skills":"./skills/","version":"0.7.2"}
1
+ {"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","displayName":"cargo-hauler","hooks":"./.cursor-plugin/hooks.json","keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","mcpServers":"./.cursor-plugin/mcp.json","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","skills":"./skills/","version":"0.7.4"}
package/dist/AGENTS.md CHANGED
@@ -23,6 +23,8 @@
23
23
  component and a view-model.
24
24
  - Documents must stay honest: a daemon the probe could not reach renders as
25
25
  `stopped`/`unresponsive` with its typed reason, never as an empty success.
26
+ - Daemon stop results carry their typed shutdown outcome. Only confirmed exit
27
+ or confirmed prior absence is success; acknowledgement alone is not stopped.
26
28
  - Names are `hauler` / `cargo-hauler` / `CARGO_HAULER_*`. No other prefix is
27
29
  read; never add an alias.
28
30
 
package/dist/INSTALL.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.
4
4
 
5
- Version: `0.7.2`
5
+ Version: `0.7.4`
6
6
 
7
7
  Run these commands from this bundle directory. The bundle is self-contained: every command below is
8
8
  a host command or the bundled installer, and nothing requires the `agent-bundle` CLI. Where that CLI is
@@ -206,22 +206,21 @@ is configured but not runnable there.
206
206
  - Partial `skills`: 2026-09-06: the emitted package root is not a Cascade discovery root; each skill directory is copied into .windsurf/skills/, ~/.codeium/windsurf/skills/, or .agents/skills/ before Cascade sees it, and the copy is what loads.
207
207
  - **Cline** (@cline/cli 0.0.13 exercised 2026-09-06; docs.cline.bot retrieved 2026-09-06 (@cline/sdk 0.0.82)) loads the components it recognizes without reading the manifest. Reads: `skills`. Install: `cp -R skills/<skill> ~/.cline/skills/<skill>`. Not loaded: manifest, mcp, placeholders, hooks.
208
208
  - Partial `skills`: 2026-09-06: the skill tree must be copied into one of Cline's own roots; the emitted package is not a Cline install unit, and no CLI verb in @cline/cli 0.0.13 performs the copy.
209
- - **CodeWhale** (Hmbown/CodeWhale main 19d34a5fb6c07b34e0b7234beb74a1cf1969efb4, docs retrieved 2026-09-06 (native Agent Plugins v1.0.0 support since v0.9.4)) installs this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Install: `/plugin install ./<plugin directory>`. Not loaded: placeholders, hooks.
210
- - Partial `mcp`: 2026-09-06: CodeWhale narrows the standard's env rule — "Local stdio environment entries must use exact ${SOURCE_ENV} references" — so an emitted env value that is anything other than one whole variable reference is not accepted there.
211
- - **GitHub Copilot CLI** (@github/copilot 1.0.83, installed and exercised 2026-09-06) installs this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Install: `copilot plugin install <plugin directory>`. Not loaded: hooks.
212
- - Partial `placeholders`: 2026-09-06: the probe proved the configuration Copilot records, not a spawned server: `copilot mcp get` prints static configuration, and spawning the server requires a GitHub Copilot login that was not performed.
213
- - **Devin CLI** (Agent Plugins 1.0.0; docs retrieved 2026-09-06, plugins documented as closed beta) installs this bundle as one plugin, but this build also writes `.claude-plugin/plugin.json`, which it reads as the plugin instead.
209
+ - **CodeWhale** (Hmbown/CodeWhale main 19d34a5fb6c07b34e0b7234beb74a1cf1969efb4, docs retrieved 2026-09-06 (native Agent Plugins v1.0.0 support since v0.9.4)) loads this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Install: `/plugin install ./<plugin directory>`. Not loaded: placeholders, hooks.
210
+ - Partial `mcp`: 2026-09-06: CodeWhale narrows the standard at the plugin boundary. Its env rule — "Local stdio environment entries must use exact ${SOURCE_ENV} references" — rejects an emitted env value that is anything other than one whole variable reference. A remote server emitted into mcp.json is narrower still: the URL must be HTTPS (or explicit loopback HTTP) with no user information, query, or fragment, a literal header is rejected in favor of CodeWhale's own env_headers or bearer_token_env_var keys, redirects must stay on the reviewed origin, and the bundle must declare exactly the normalized endpoint host set in capabilities.network_hosts. That declaration rides in extensions["net.codewhale"], which this projection writes only when the author authors portable.extensions; a remote server emitted without it is a validation error, and "an active bundle must be … free of validation errors", so the whole bundle stays inactive there until the author declares the matching host set.
211
+ - **GitHub Copilot CLI** (@github/copilot 1.0.83, installed and exercised 2026-09-06) loads this bundle as one plugin. Reads: `plugin.json`, `skills`. Install: `copilot plugin install <plugin directory>`. Not loaded: hooks. This build also writes `.mcp.json`, which it uses for mcp instead. A root that also carries `.plugin/plugin.json` uses it for manifest and still reads the rest.
212
+ - **Devin CLI** (Agent Plugins 1.0.0; docs retrieved 2026-09-06, plugins documented as closed beta) loads this bundle as one plugin, but this build also writes `.claude-plugin/plugin.json`, which it reads as the plugin instead.
214
213
  - **Gemini CLI** (@google/gemini-cli 0.58.0, installed and exercised 2026-09-06) loads the components it recognizes without reading the manifest. Reads: `skills`. Install: `gemini skills install <plugin directory>/skills/<skill>`. Not loaded: manifest, mcp, placeholders, hooks.
215
- - **Grok Build** (xai-org/grok-build main 72a61251fcffb464bcc687aeb5a998e5a98ec0c9, docs retrieved 2026-09-06) loads the components it recognizes without reading the manifest. Reads: `skills`. Install (no local-directory install is verified for this artifact): `grok plugin install <marketplace plugin name> --trust`. Not loaded: manifest, mcp, placeholders, hooks.
216
- - **Hermes Agent** (hermes-agent.nousresearch.com developer guide retrieved 2026-09-06; no version is printed on the page) installs this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Install (no local-directory install is verified for this artifact): `hermes plugins install <owner>/<repository> --no-enable`. Not loaded: hooks.
214
+ - **Grok Build** (xai-org/grok-build main 72a61251fcffb464bcc687aeb5a998e5a98ec0c9, docs retrieved 2026-09-06) loads the components it recognizes without reading the manifest. Reads: `skills`. Install from a marketplace (no local-directory install is verified for this artifact): `grok plugin install <marketplace plugin name> --trust`. Not loaded: manifest, mcp, placeholders, hooks.
215
+ - **Hermes Agent** (hermes-agent.nousresearch.com developer guide retrieved 2026-09-06; no version is printed on the page) loads this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Install from a Git repository (no local-directory install is verified for this artifact): `hermes plugins install <owner>/<repository> --no-enable`. Not loaded: hooks.
217
216
  - Partial `manifest`: 2026-09-06: the validation rule set is not published: the page never states that the manifest root is treated as closed or what happens to an unknown root key.
218
217
  - Partial `placeholders`: 2026-09-06: the expansion sites are unpublished — the page does not say whether ${PLUGIN_ROOT} and ${PLUGIN_DATA} are expanded in args, env values, and cwd as §9.1 requires, only that the variables are provided.
219
218
  - **JetBrains Junie** (junie.jetbrains.com/docs retrieved 2026-09-06, agent-skills page dated 01 September 2026; no CLI version is published on the page) loads the components it recognizes without reading the manifest. Reads: `skills`. Register: `junie --skill-location <plugin directory>/skills`. Not loaded: manifest, mcp, placeholders, hooks.
220
219
  - Partial `skills`: 2026-09-06: the emitted skills/ root is not a default location, so it loads only once registered with --skill-location or the skill-locations config field, and "if a project-level and a user-level skills have the same name, the user-level skill will be skipped".
221
- - **Kiro (Powers)** (kiro.dev/docs/powers pages updated September 2, 2026 and August 4, 2026, retrieved 2026-09-06) installs this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Install: `Powers panel -> Add Custom Power -> Import power from a folder -> select <plugin directory> -> Install`. Not loaded: placeholders, hooks.
220
+ - **Kiro (Powers)** (kiro.dev/docs/powers pages updated September 2, 2026 and August 4, 2026, retrieved 2026-09-06) loads this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Install: `Powers panel -> Add Custom Power -> Import power from a folder -> select <plugin directory> -> Install`. Not loaded: placeholders, hooks.
222
221
  - Partial `manifest`: 2026-09-06: Kiro's "Required fields" table additionally requires version, description, author, and keywords, where the canonical schema requires only $schema and name — so a bundle that declares no portable author or keywords metadata does not meet Kiro's tightened manifest, and Kiro publishes no validation-error behavior to say what happens then.
223
222
  - Partial `mcp`: 2026-09-06: only stdio is documented for a power's mcp.json; no Kiro page states that a streamable-http server in that file is read, so an emitted remote server is unproven there.
224
- - **OpenClaw** (Agent Plugins 1.0.0; docs retrieved 2026-09-06) installs this bundle as one plugin, but this build also writes `.claude-plugin/plugin.json`, which it reads as the plugin instead.
223
+ - **OpenClaw** (Agent Plugins 1.0.0; docs retrieved 2026-09-06) loads this bundle as one plugin, but this build also writes `.claude-plugin/plugin.json`, which it reads as the plugin instead.
225
224
  - **OpenCode** (opencode-ai 1.18.29 exercised 2026-09-06; opencode.ai/docs retrieved 2026-09-06) loads the components it recognizes without reading the manifest. Reads: `skills`. Install: `cp -R skills/<skill> .agents/skills/<skill>`. Not loaded: manifest, mcp, placeholders, hooks.
226
225
  - Partial `skills`: 2026-09-06: the skill tree must be copied into one of OpenCode's own roots; the emitted package as a whole is not an OpenCode install unit, and skill names must be unique across all roots ("Ensure skill names are unique across all locations").
227
226
  - **Pi** (@mariozechner/pi-coding-agent 0.73.1 installed from npm 2026-09-07; packaged docs/skills.md and docs/packages.md read from that release) loads the components it recognizes without reading the manifest. Reads: `skills`. Register: `pi --skill <plugin directory>/skills`. Not loaded: manifest, mcp, placeholders, hooks.
@@ -229,7 +228,7 @@ is configured but not runnable there.
229
228
  - **Qoder CLI** (docs retrieved 2026-09-06; no CLI version is published on any page) loads the components it recognizes without reading the manifest. Reads: `skills`. Install: `qoder plugins install <plugin directory> --scope user`. Not loaded: manifest, placeholders, hooks. This build also writes `.mcp.json`, which it uses for mcp instead.
230
229
  - **Swival** (docs retrieved 2026-09-06; no product version is published on the documentation pages) loads the components it recognizes without reading the manifest. Reads: `skills`. Register: `swival --skills-dir <plugin directory>/skills "<task>"`. Not loaded: manifest, mcp, placeholders, hooks.
231
230
  - Partial `skills`: 2026-09-06: the emitted skills/ root is not a default location, so it loads only once registered with --skills-dir or the swival.toml skills_dir field, and it loses by name to the default roots: "If the same skill name exists in multiple locations, the first one in the precedence order wins", with .swival/skills/ and .agents/skills/ ahead of --skills-dir paths. A registered tree outside the project resolves as external, which Swival adds "as read-only roots".
232
- - **VS Code (Copilot agent plugins)** (code.visualstudio.com/docs/agent-customization/agent-plugins, page footer 9/2/2026, retrieved 2026-09-06) installs this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Install: `"chat.pluginLocations": { "<plugin directory>": true }`. Not loaded: placeholders, hooks.
231
+ - **VS Code (Copilot agent plugins)** (code.visualstudio.com/docs/agent-customization/agent-plugins, page footer 9/2/2026, retrieved 2026-09-06) loads this bundle as one plugin. Reads: `mcp.json`, `plugin.json`, `skills`. Register: `"chat.pluginLocations": { "<plugin directory>": true }`. Not loaded: placeholders, hooks.
233
232
  - **Zed Agent** (zed.dev/docs retrieved 2026-09-06; no page publishes a version or last-updated date) loads the components it recognizes without reading the manifest. Reads: `skills`. Install: `cp -R skills/<skill> ~/.agents/skills/<skill>`. Not loaded: manifest, mcp, placeholders, hooks.
234
233
  - Partial `skills`: 2026-09-06: only the skill folders load, one copy at a time, and the catalog is capped — "50KB catalog budget… Skills that don't fit are dropped from the catalog with a warning in the UI" — so a large emitted skill set is not guaranteed to be wholly visible.
235
234
 
package/dist/README.md CHANGED
@@ -85,7 +85,7 @@ The CLI is `hauler` on PATH from `npm i -g cargo-hauler`. Never run
85
85
  | `hauler result <ticket> [--full]` | A stored ticket in full: the settled 16 KiB output tail, or the whole live in-memory tail while it runs (not the status preview). The document names the full on-disk output log (`Full output: <path> (size)`) and `--json` carries it as `request.outputPath`; `--full` prints that whole log instead of the tail (the last ~768 KiB when it does not fit, with the path for the rest). |
86
86
  | `hauler kill <ticket>` | Stop a ticket: drop it from the queue or SIGTERM (then SIGKILL) its cargo process group, freeing the lane. Riders return to their lane or fail with it. |
87
87
  | `hauler request [--session ID] [--host HOST] [--cwd DIR] [--after TICKET …] -- <cargo …>` | Submit a background request and return its ticket, with where it landed in its lane (`queued behind cc-3281 (~13m)`, `waiting for cc-3281`, or `attached to cc-3281`). `--cwd` overrides the current CLI workspace; `--after` works as for `exec`. |
88
- | `hauler daemon <run\|start\|stop\|status\|restart>` | Manage the daemon lifecycle. `restart` is the manual replacement: it sends the graceful stop, waits up to 5 s for the old pid to exit, then starts a daemon from this install and prints both (`restarted: pid 741314 (0.6.0) → pid 742001 (0.6.1)`); a daemon that has not exited by then is reported, not killed, and nothing is started (exit `1`). Tickets in flight are not handed over: the old daemon settles them itself as it shuts down — `killed`, error `daemon shutdown` — and callers resubmit (only rows a daemon that died without shutting down never marked are stamped `orphaned by daemon restart` by the next daemon's first ledger pass). After upgrading the package, every client entry — reads (`status`, `daemon status`, `log`, `last`, `await`, `result`, the dashboard and MCP tools), writes, and hooks — checks the daemon version before requesting a versioned payload and replaces a daemon from the previous install automatically. When the old daemon has not exited within the grace, the command fails with `` cargo-hauler daemon pid N (X.Y.Z) is still running 5s after the shutdown request; not restarted — retry once it has exited, or stop it with `hauler daemon stop` `` instead of parsing its payload or starting a second daemon. |
88
+ | `hauler daemon <run\|start\|stop\|status\|restart>` | Manage the daemon lifecycle. `stop` records its typed `shutdown` outcome in JSON and exits `0` only after a `shutting-down` acknowledgement followed by the original pid's exit, or when the daemon was already absent. Refusal, timeout, protocol error, disconnect before acknowledgement, and an acknowledged daemon still alive after 5 s exit `1`; `running` remains `true` when the original pid is alive and `null` when liveness could not be established. `restart` is the explicit replacement path: it sends the graceful stop, waits up to 5 s for the old pid to exit, then starts a daemon from this install and prints both (`restarted: pid 741314 (0.6.0) → pid 742001 (0.6.1)`). Tickets in flight are not handed over: the old daemon settles them itself as it shuts down — `killed`, error `daemon shutdown` — and callers resubmit. Automatic upgrades are gentler: read-only commands and MCP/dashboard reads never retire a daemon; a protocol-compatible older daemon serves them directly. Submission commands replace an older daemon only after an idle status check; daemons from 0.7.4 onward hold admission closed while confirming that state, while 0.7.1–0.7.3 receive a client preflight. A busy or slow-to-retire daemon keeps serving the submission and emits one line such as `daemon 0.7.1 will be replaced by 0.7.3 when idle`. A truly incompatible daemon is reported with its pid and version instead of having its payload parsed. |
89
89
  | `hauler install-shim [--dir DIR] [--real-cargo PATH] [--force]` | Install the optional PATH shim. |
90
90
  | `hauler web [--port N] [--no-open]` | Open the dashboard from the checkout, npm package, or installed plugin. Agent Bundle's generated web command serves the built App against the plugin's own `hauler` server, opens it populated by `hauler_status`, and stays in the foreground until Ctrl-C. In an MCP host, call `hauler_status` instead. |
91
91
 
@@ -448,9 +448,13 @@ ever flagged.
448
448
  Tickets do not survive a daemon stop; runs are never handed over to the next
449
449
  daemon. How a ticket ends depends on how the daemon went. A graceful stop —
450
450
  `hauler daemon restart`, `hauler daemon stop`, or the automatic replacement of
451
- a daemon from another install by the next `hauler exec`, `hauler request`,
452
- hook call, or `hauler daemon start` — is the shutdown request, and the old
453
- daemon settles every queued, running, and attached ticket itself as it exits:
451
+ an idle older daemon by the next `hauler exec`, `hauler request`, hook
452
+ submission, or `hauler daemon start` — is the shutdown request. Automatic
453
+ replacement checks for queued, running, executing, or attached work first;
454
+ 0.7.4 and later daemons hold admission closed through that decision, while
455
+ 0.7.1–0.7.3 receive the compatible client's status preflight. When a stop does
456
+ proceed, the old daemon settles every queued, running, and attached ticket
457
+ itself as it exits:
454
458
  its cargo processes are terminated (SIGTERM, then SIGKILL after
455
459
  `CARGO_HAULER_KILL_GRACE_MS`) and each row is marked `killed` with the error
456
460
  `daemon shutdown`, so `hauler result cc-N` shows the ticket `killed` with
@@ -635,7 +639,7 @@ Per-host notes and hook timeouts are in [docs/install.md](docs/install.md).
635
639
 
636
640
  | Variable | Default | Meaning |
637
641
  | --- | --- | --- |
638
- | `CARGO_HAULER_STATE_DIR` | Per-user cache directory | Unix socket or Windows named pipe source, SQLite ledger, daemon log, pid lock, `hook-state.json`, `hook-events.jsonl`, and the per-ticket output logs under `tickets/`. |
642
+ | `CARGO_HAULER_STATE_DIR` | Per-user cache directory | Unix socket or Windows named pipe source, SQLite ledger, daemon log, pid lock, `hook-state.json`, `hook-events.jsonl`, and the per-ticket output logs under `tickets/`. Owner-private on Linux and macOS: the directory is `0700` and its sensitive files `0600`, and an unsafe or unowned path is refused rather than reused. |
639
643
  | `CARGO_HAULER_CARGO_BIN` | `$CARGO_HOME/bin/cargo` | Cargo binary for daemon-started work; bare `cargo` is the last fallback. Never resolved through `PATH`. Read from the daemon's own environment (export it where the daemon starts, or before `hauler daemon start`); clients do not forward it. |
640
644
  | `CARGO_HAULER_MAX_CONCURRENT` | cores ÷ 8, clamped to 5–16 | Global admission permits for Cargo processes across all lanes; an integer >= 1. |
641
645
  | `CARGO_HAULER_OVERLAP_EXECUTION` | `1` | Hand a lane to its next request once a `test`/`nextest`/`bench`/`run` leader reports its build finished, overlapping the next compile with the leader's execution phase. `0` keeps a lane strictly one process at a time. |
@@ -672,11 +676,50 @@ as a warning (daemon log, or stderr for hand-run commands) and the default
672
676
  applies; only `0` or `off` disables an arm that documents that contract.
673
677
  The state directory defaults to `$XDG_CACHE_HOME/cargo-hauler`, otherwise
674
678
  `~/.cache/cargo-hauler` on Linux, `~/Library/Caches/cargo-hauler` on macOS, and
675
- `%LOCALAPPDATA%\cargo-hauler` on Windows. When `CARGO_HAULER_KACHE_INDEX` is
679
+ `%LOCALAPPDATA%\cargo-hauler` on Windows. Its contents are owner-private, and
680
+ the daemon is a single user's, not a shared service — see
681
+ [State directory ownership](#state-directory-ownership). When
682
+ `CARGO_HAULER_KACHE_INDEX` is
676
683
  unset, the daemon reads kache's configured local store from
677
684
  `$XDG_CONFIG_HOME/kache/config.toml` or `~/.config/kache/config.toml` and opens
678
685
  `<local_store>/index.db` read-only.
679
686
 
687
+ ### State directory ownership
688
+
689
+ The daemon is local control for the user who owns its state directory, not a
690
+ shared multi-user service. Everything under the state directory — complete
691
+ command output in `tickets/`, the ledger, the daemon log, the passthrough
692
+ spool, the hook records, the jobserver FIFO, the control socket — is that
693
+ one account's, and there is no mechanism for a second user to submit work to
694
+ another user's daemon.
695
+
696
+ On Linux and macOS the daemon enforces that boundary rather than inheriting
697
+ it from the umask:
698
+
699
+ - Directories cargo-hauler creates are `0700`, and sensitive files `0600`,
700
+ whatever umask the invoking shell had. An existing state directory that
701
+ the running user already owns is tightened in place on the next start;
702
+ its contents are preserved.
703
+ - Only the state directory and the entries cargo-hauler owns are changed. A
704
+ parent you configured through `CARGO_HAULER_STATE_DIR` — a shared volume,
705
+ a RAM disk — is never chmod'ed.
706
+ - A state path that is a symbolic link, is the wrong kind of entry, or is
707
+ owned by another user is refused by name instead of being followed,
708
+ chmod'ed, or deleted. Fix or remove the path and start again; cargo-hauler
709
+ will not act on another account's file on your behalf.
710
+ - When the state directory is too deep for the kernel's socket path limit,
711
+ the control socket moves to a `cargo-hauler-<uid>` directory (mode `0700`)
712
+ under `XDG_RUNTIME_DIR`, `TMPDIR`, or the system temporary directory —
713
+ never directly into a shared temporary root. Two accounts sharing one
714
+ temporary root get separate directories. A daemon from an earlier install
715
+ still listening at the previous relocated path is considered only by a
716
+ daemon-starting submission, never by a read; `hauler daemon stop` asks it
717
+ too before reporting nothing running.
718
+
719
+ Windows has neither POSIX modes nor uids, and its control endpoint is a
720
+ named pipe rather than a filesystem entry, so none of the above applies
721
+ there; state files keep the permissions the filesystem gives them.
722
+
680
723
  ## Runtime behavior and caveats
681
724
 
682
725
  - Hook and client transport failures fail open: a hook passes the original
@@ -689,14 +732,11 @@ unset, the daemon reads kache's configured local store from
689
732
  `hauler_await` fail loudly when the daemon is unreachable instead of
690
733
  reporting a ticket as not found; `hauler_status`, `hauler_log`, and
691
734
  `hauler_last` read the ledger with the daemon marked `stopped` or
692
- `unresponsive`. Before any live daemon reply is parsed, these reads apply
693
- the one-version rule and replace a daemon left running by a previous
694
- install. Replacement is directional: only a newer install replaces a
695
- daemon. A client older than the daemon it finds — a session still on a
696
- previous plugin — never shuts it down (the daemon refuses a shutdown from
697
- an older or unversioned client), reports the daemon as newer, and runs
698
- cargo directly. If replacement fails, the reads report that failure
699
- instead of reading the stale payload.
735
+ `unresponsive`. Reads never request daemon shutdown. They use an older
736
+ daemon when its wire-protocol identity is compatible; a truly incompatible
737
+ daemon is reported with its pid and version. A client older than the daemon
738
+ it finds — a session still on a previous plugin — keeps the directional
739
+ `DaemonNewer` behavior and never shuts it down.
700
740
  - The state directory is not migrated between installs. Every rendered
701
741
  document names the one in use (`state dir …` in the header; `stateRoot` in
702
742
  `--json`), so a `CARGO_HAULER_STATE_DIR` change is visible on the next
@@ -946,9 +986,11 @@ Workbench's MCP page preview the same App with live rebuilds. The repository
946
986
  ships no preview harness of its own.
947
987
 
948
988
  agent-bundle does not yet have an npm release; this repository pins the
949
- [pkg.pr.new](https://pkg.pr.new) preview of Agent Bundle PR #733 commit
950
- [`5064133`](https://github.com/ScriptedAlchemy/agent-bundle/commit/506413326359bff154ce4476eb0f47b0dabf4efe)
951
- for both `agent-bundle` and `@agent-bundle/runtime`. `inspect` reports the
989
+ [pkg.pr.new](https://pkg.pr.new) preview of Agent Bundle main commit
990
+ [`e040c32`](https://github.com/ScriptedAlchemy/agent-bundle/commit/e040c32591d586eba72da6fa5c1bc1c881993000),
991
+ built from [Agent Bundle PR #779](https://github.com/ScriptedAlchemy/agent-bundle/pull/779),
992
+ for `agent-bundle`, `@agent-bundle/runtime`, and their paired
993
+ `rsc-markdown-stream` preview. `inspect` reports the
952
994
  `agent` component kind as unavailable on every host (agent-bundle G5
953
995
  deferral); this plugin defines no agents.
954
996