@mmmbuto/nexuscrew 0.9.77 → 0.9.78

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to NexusCrew are tracked here.
4
4
 
5
+ ## 0.9.78 — 2026-10-22
6
+
7
+ - **A `vl` engine can name its binary, its profile catalog, its profile and its effort level.** Four optional fields on a `vl` engine, all paths and names — never secrets. `vlBinary` is an absolute path on the node that runs the cell: when it is declared it is the only binary considered, the `PATH` is not consulted as a fallback, and a declared binary that is missing, is not a regular file or is not executable refuses the start with a declared reason instead of quietly launching something else. The same resolved file answers the version probes and the spawn, so a gate never interrogates a different executable than the one launched; spaces in the path are legal. Without the field the `PATH` is used exactly as before. `vlConfigFile` points at a shared `config.toml` catalog of profiles: every launch copies it into the cell's own `config/vl/config.toml` (atomic write, mode 0600), the cell's `selected.toml` is never created nor touched, and no `secrets.env` is ever written — keys travel by name only. `vlProfile` is a single `-p <name>` argument, valid on `vl.native` only (the remote variants already compose provider and endpoint, and the two choices exclude each other); a profile absent from the catalog refuses the start explicitly, and in profile mode `VL_PROVIDER` and `VL_BASE_URL` are neutralized — as are `VL_MODEL` and `VL_EFFORT` when NexusCrew carries no explicit choice — with the precedence explicit operator choice > profile > the cell's own `selected.toml`. `effort` is accepted only among the levels declared for the model and only when the selected binary is new enough to read `VL_EFFORT`; an unsupported or unknown level refuses the start with a declared error instead of being ignored or promoted to another level.
8
+ - **Every `vl` cell keeps its own identity, state and configuration.** Each cell gets a private directory under the NexusCrew state tree, keyed by owner instance id and cell id, with its own `XDG_STATE_HOME`, `XDG_DATA_HOME` and `XDG_CONFIG_HOME` — two cells on the same engine are isolated down to the host, not just in their environment. The paths are computed on the node that runs the cell, never from a remote viewer, and they do not depend on the engine, the model, the display name, the tmux session or the PID. An unsafe cell id or a hostile symlink refuses the start with a declared reason rather than degrading to a shared folder.
9
+ - **A new session is an explicit, one-shot request, and the launches of a cell are serialized.** An ordinary start or restart never passes `--new`. The explicit action writes a persistent request that the launch cycle consumes exactly once, atomically and before the spawn, so a double click does not stack a second request and a retry or a supervised restart does not repeat the rotation. An uncertain outcome is reported, never retried with a second automatic `--new`. Two concurrent starts of the same cell are serialized, and a minimum pause between two launches of the same cell is enforced on an injected clock, so a failing launch does not become a storm. The cell prompt still travels only through `VL_SYSTEM_APPEND_FILE`; NexusCrew does not read, rewrite, archive, rotate or delete `vl` sessions, does not delete a lock and never puts a credential on the command line.
10
+ - **The old shared `vl` state directories are inventoried, never migrated.** A read-only diagnostic lists the legacy `vl` and `vl-*` directories under the local state root with the sessions found in them. Nothing is moved, copied or deleted, and no shared folder is ever handed to a cell automatically: the attribution of sessions that used one is ambiguous, and that decision belongs to the operator.
11
+ - **VL fleet routes enforce the peer's grants and resolved cell scope.** The explicit new-session request and its read-only state view require operator access and reject a target outside the peer's resolved scope. The legacy inventory describes shared node state that cannot be attributed reliably to individual cells: a federated peer needs operator access and full cell visibility to read it. A restricted scope receives no inventory, even when another hop has broader visibility. These routes are explicitly classified and allowlisted with their declared HTTP methods.
12
+ - **The built-in Ollama catalog knows `minimax-m3.1-flash`.** The model joins the hosted Ollama list with the context its card declares — 1M — and with the output limit the same card carries, so the fields shown for it are the declared ones and not the values that belong to its predecessor. The same id is offered by the gateway families that already carry the Minimax line, so an engine that reaches them does not show a model the fleet catalog would deny. The catalog stays static: the id is the operator's choice, and nothing is fetched to learn that the model exists.
13
+ - **A cell message can no longer close its own envelope.** The body of a message was copied into the envelope as it came: a line that began with a delimiter marker closed the envelope early and made the rest of the text look like a message from another sender, and a lone carriage return — a line break for a terminal — could forge such a line without any newline in the payload. The envelope now has one contract for every delivery path, the send route and the outbox of a node: the line endings are normalized and a line that starts with a marker is escaped, so the leading bracket is no longer a delimiter. What counts as the start of a line is the whitespace a reader trims, the same rule that reader applies, and the escape is placed where it survives that trim. No message text is dropped. Apart from normalizing CRLF and lone CR to LF, only delimiter-like lines are escaped; all other body content is preserved.
14
+ - **A paired VL node can send a bounded text message to an authorized local cell through its poll outbox.** Delivery is denied unless the node's cell allowlist permits the target and the local cell is active and can receive messages. Operators can read and update that allowlist per node. A plain cell name addresses the local owner; cross-owner delivery is refused. The broker processes at most one item per poll, with a 2 KiB UTF-8 text limit and per-node limits of six deliveries per minute and twenty per hour. Concurrent submissions of the same message id share one result, and recently settled ids return their stored result within the bounded retention window. The poll carries an outcome cursor; `submitted` confirms submission to the cell, not execution or completion.
15
+ - **The outbox of a node obeys the read-only gate, and re-reads it at every delivery.** The send route refused every delivery on a read-only server, the outbox did not: a message waiting in the outbox of a paired node was pasted into the cell with the gate raised. The outbox now passes the same gate before anything else, and the outcome is the same refusal the other refusals of the outbox use. The gate is read from its source at every point where the decision matters — at the entry, right after the fleet directory resolves, and immediately before the delivery — so a server that becomes read-only while the directory is resolving ends in a refusal, with no delivery and no rate limit consumed. It is the gate the rest of the server uses, not a second one with a different meaning, and the other checks keep their place: the dedup, the access list and the rate limit are untouched. The text of an outbox message is validated with the rule of the send route, so the two paths agree on what a cell message must be to be delivered at all.
16
+ - **A chat tile is available, behind a build-time flag that is off by default.** The tile shows a session as a conversation instead of a terminal: the transcript, the status of the run, the tools it used, the approvals it asks for and the composer. The flag is a build-time environment variable — there is no runtime override in the bundle — so the tile is off in every ordinary build and nothing changes for the operator who does not ask for it. A hermetic demo surface comes with it: a self-contained grid that needs no authentication, no service worker and no network, reachable only when the flag is on and the page asks for it. The demo renders synthetic sessions only and never touches a live one.
17
+ - **The state of a new-session request is readable per cell, without mutating anything.** A consumed request was observable only by asking for another one: a mutation, and another possible rotation, just to find out that the request had already been consumed. The state is now a read-only view with the cell as the declared target, so a peer outside the cell scope gets a refusal instead of the state, and the route is classified like the other fleet reads. Every managed cell carries the state in the fleet status, and a launch that fails after the consumption says so in its error and in the fleet diagnostics, with a bounded marker. Reading the state creates nothing on disk: a read that leaves a directory behind is not a read.
18
+ - **A profile catalog is read only from a regular file, and never from the cell's own overlay.** The source of the catalog is now checked before any read: it must be a regular file, so a fifo, a socket, a device or a directory is refused with a declared reason instead of blocking the thread on a read, and it cannot be the generated overlay of the cell nor an alias of it, compared by device and inode. The bytes copied into the cell are the ones verified on the descriptor, so a file that changes between the verification and the read is refused instead of copied. The refusal is explicit: there is no previous catalog and no implicit default to fall back on.
19
+ - **The VL command bridge can carry the operator's text to a device session.** A `prompt` command joins the bounded verbs of the bridge: the text travels as an argument, bounded to 4 KiB of UTF-8, and is validated at the bridge — an empty or oversized text raises a readable error there instead of departing as a delivery that the device would refuse. The submission confirms the delivery of the command, not the execution of the text, as for every other verb.
20
+
5
21
  ## 0.9.77 — 2026-10-19
6
22
 
7
23
  - **An engine that exposes no model listing still gets its known ids and their catalog defaults.** The discovery proposal is now composed for every engine, not only those with an HTTP listing: the ids the operator declared, the profile backup, and — when it is the only known id — the engine's own active model are merged with the public catalog, so an engine whose client owns its own endpoint — the `pi` family included — shows its models with the same field-by-field sources, the same default badge and the same edit/confirm actions. The API layer is explicitly unavailable in that case: `supported` and `source: unsupported` stay truthful, no collector and no request are invented, the authentication diagnosis reports no attempt and no cause, and `inApi` is absent rather than false because no API answered. The precedence is unchanged (user, then API, then the public catalog, then the backup); an explicit user `false` still wins, no model id is ever added from the catalog, and with no known id the snapshot is not fetched at all. The missing-data dialog also works on this proposal, so a default can be confirmed into a user value without any automatic write.
package/docs/FLEET.md CHANGED
@@ -55,6 +55,46 @@ Activity hooks are injected only into CLI versions that have been verified
55
55
  against them. On any other version no hook is installed and the cell runs
56
56
  without them — a declared limitation, not a failure.
57
57
 
58
+ ### VL engine fields (per-cell identity, binary, profile, effort)
59
+
60
+ A `vl` engine accepts four optional fields. They are paths and names — never
61
+ secrets — and they are validated twice: the *shape* when the definition is
62
+ saved, the *truth on the node* at every launch.
63
+
64
+ - `vlBinary` — absolute path of the `vl` executable on the **node that runs
65
+ the cell**. When declared it is the only binary considered: NexusCrew does
66
+ not fall back to the `PATH`. A declared binary that is missing, is not a
67
+ regular file, or is not executable refuses the start with a declared reason.
68
+ The same resolved file is used for the version probes and for the spawn, so
69
+ the gate never interrogates a different executable than the one launched.
70
+ Spaces in the path are legal. Without the field, the `PATH` is used exactly
71
+ as before.
72
+ - `vlConfigFile` — absolute path of a shared `config.toml` catalog of `vl`
73
+ profiles. At every launch NexusCrew copies it into the cell's own
74
+ `config/vl/config.toml` (atomic write, mode 0600). The cell's
75
+ `selected.toml` is never created nor touched, and no `secrets.env` is ever
76
+ written: keys travel by name only.
77
+ - `vlProfile` — a single `-p <name>` argument, valid on `vl.native` only (the
78
+ remote variants already compose provider and endpoint, and the two choices
79
+ exclude each other). A profile that is not in the catalog refuses the start
80
+ explicitly. In profile mode `VL_PROVIDER` and `VL_BASE_URL` are neutralized,
81
+ as are `VL_MODEL` and `VL_EFFORT` when NexusCrew carries no explicit
82
+ choice. Precedence: explicit operator choice > profile > the cell's own
83
+ `selected.toml`.
84
+ - `effort` — accepted only among the levels declared for the model, and only
85
+ when the selected binary is new enough to read `VL_EFFORT`. An unsupported
86
+ or unknown level refuses the start with a declared error instead of being
87
+ ignored or promoted to another level.
88
+
89
+ Every `vl` cell also gets its own identity directory under the NexusCrew state
90
+ tree, keyed by owner instance id and cell id, with private `XDG_STATE_HOME`,
91
+ `XDG_DATA_HOME` and `XDG_CONFIG_HOME`. Two cells on the same engine are
92
+ isolated down to the host, not just in their environment.
93
+
94
+ The cell prompt travels only through `VL_SYSTEM_APPEND_FILE`. NexusCrew never
95
+ reads, rewrites, archives, rotates or deletes `vl` sessions, never deletes a
96
+ lock, and never puts a credential on the command line.
97
+
58
98
  ### Bootstrap prompt delivery (Kimi engines)
59
99
 
60
100
  For `kimi.native` and the Claude Code "Kimi Code" provider the cell prompt is
package/docs/MCP.md CHANGED
@@ -126,7 +126,10 @@ credential-redacted.
126
126
  VL mutation tools likewise require an active local Fleet caller. Use
127
127
  `nc_vl_nodes` immediately before mutation and the full owner-qualified target.
128
128
  `nc_vl_command` receipts are transport-only until `lastAck` carries the same
129
- ID. See [VL micro-device nodes](VL_MICRO_NODES.md).
129
+ ID. The `prompt` kind is the only command that carries user text: its single
130
+ argument is `args.text`, a non-empty UTF-8 string bounded to 4 KiB, and the
131
+ bridge rejects an over-4-KiB text with a readable error rather than a silent
132
+ `submitted`. See [VL micro-device nodes](VL_MICRO_NODES.md).
130
133
 
131
134
  ### Audio Share
132
135
 
@@ -56,9 +56,16 @@ in its current authorized topology. A stale or revoked owner disappears; it is
56
56
  not guessed from cached routes.
57
57
 
58
58
  The exact command allowlist is: `status`, `health`, `start`, `stop`, `restart`,
59
- `version`, `capabilities`, `logs`, `update_candidate`, and `unpair`. `logs` is
60
- bounded to 1–100 redacted records. An update candidate is bounded to 2.5 MiB,
61
- hash verified and staged only; the protocol cannot activate it.
59
+ `version`, `capabilities`, `logs`, `update_candidate`, `unpair`, and `prompt`.
60
+ `logs` is bounded to 1–100 redacted records. An update candidate is bounded to
61
+ 2.5 MiB, hash verified and staged only; the protocol cannot activate it. `prompt`
62
+ is the only verb that carries user text to the device session: its single
63
+ argument is `args.text`, a non-empty UTF-8 string bounded to 4 KiB (4096 bytes,
64
+ the same `MAX_PROMPT_TEXT` the device enforces). A `prompt` receipt is
65
+ transport-only like every other command; completion is confirmed only when
66
+ `lastAck` carries the same id. The bridge rejects an over-4-KiB text at the MCP
67
+ layer with a readable error rather than letting it depart as a silent
68
+ `submitted` that the device would refuse.
62
69
 
63
70
  ## HTTP surface
64
71