chutes-build 1.0.3 → 1.1.0

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 (2) hide show
  1. package/CHANGELOG.md +195 -36
  2. package/package.json +7 -7
package/CHANGELOG.md CHANGED
@@ -6,42 +6,151 @@ project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.1.0] - 2026-08-18
10
+
11
+ ### Added
12
+
13
+ - **Tool calls emitted as plain text are recovered.** An open-weight model served through vLLM or SGLang can emit a well-formed tool call in its own chat-template syntax while the server-side parser returns an empty `tool_calls` array, so the turn ends having done nothing and the user sees raw markup. The runtime now recognises the Hermes/Qwen `<tool_call>`, Kimi K2 and Llama inline-function shapes (plus a strict fenced-JSON form) and turns them back into real calls only when the name resolves to a registered tool and the arguments pass that tool's own parser. `CHUTES_DISABLE_TOOL_TEXT_RECOVERY=1` disables it.
14
+ - **`/usage` reports cache effectiveness.** A `cache_read_ratio` (cached read tokens divided by input tokens, clamped to `[0, 1]`) and per-call `cache_hit_calls` / `cache_miss_calls` counters join the token totals; the TUI usage block now shows the cached percentage and the hit count.
15
+
16
+ ### Changed
17
+
18
+ - **Models without tool calling no longer receive a `tools` array.** The live catalog's `supported_features` now gates tool specs: a model that does not advertise `tools` gets an empty tool list instead of a request its endpoint rejects. The `StructuredOutput` tool is gated the same way and falls back to validating the final answer text directly. The default stays `true` for models absent from the catalog.
19
+
9
20
  ### Fixed
10
21
 
11
- - **Only streaming requests could survive a busy model.** The Chutes fallback
12
- chain — selected model, then `CHUTES_FALLBACK_MODELS`, then `model-router` —
13
- was wired into `chat_completion_stream` and never into `chat_completion`. The
14
- two paths had diverged by omission rather than by any decision, so an
15
- interactive turn recovered quietly from `429 Infrastructure is at maximum
16
- capacity` while compaction, title generation and the advisor surfaced it raw.
17
- Both paths now share one chain and one policy. Nothing on the non-streaming
18
- path can be mid-stream, so the "never switch models once bytes have shipped"
19
- rule is satisfied trivially there.
20
- - The chain's ordering is now testable without touching process-wide
21
- environment variables, which no test could do safely while the rest of the
22
- suite runs in parallel — the env reads moved to a wrapper around a pure core.
23
- - **`CHUTES_BUILD_OAUTH2_PRINCIPAL_TYPE=Team` could never complete a flow.**
24
- `default_team_oauth2_scopes()` was still upstream's — `grok-cli:access`,
25
- `api:access`, `team:read`, `conversations:*`, `workspaces:*`. The user-facing
26
- list was corrected during the re-base cleanup and this one was missed.
27
- `api.chutes.ai` advertises no team principal at all: its `scopes_supported` is
28
- entirely user-level, so an authorization server was being asked for scopes it
29
- has never heard of. The default is now the user list; a deployment that really
30
- does define a team principal says so through `CHUTES_BUILD_OAUTH2_SCOPES`.
31
- Established by reading the IdP's own discovery document rather than the prose
32
- documentation, which does not cover team principals either way.
22
+ - **Parallel tool calls from providers that omit the chunk index no longer fuse.** `ToolCallDelta.index` is now optional, and the stream correlates by tool-call `id` when the index is absent; previously a missing index defaulted to `0`, merging every parallel call into one accumulator and concatenating their arguments into invalid JSON.
23
+ - **Non-streaming requests now apply the Chutes reasoning plan.** Compaction, title generation and the advisor sent the bare payload to the configured inference base, dropping the `chat_template_kwargs` thinking switches and never routing `model-router` to the router endpoint, the same asymmetry the streaming path already handled.
24
+ - **Stream deserialization errors name the offending chute.** A failed chunk now carries a short excerpt of the SSE payload alongside the serde error, so "expected value at line 1 column 1" says which backend produced what.
25
+
26
+ ## [1.0.4] - 2026-08-12
27
+
28
+ Two threads. The manual port from upstream `b13fa526` begins — one area per
29
+ commit, each landing with its own tests green and measured against a pre-port
30
+ baseline rather than an absolute count. And the verification system itself was
31
+ overhauled, because three of the previous release's problems were failures to
32
+ *notice*, not failures of code: 188 tests were failing where nobody looked, a
33
+ pager suite reported failures that were the terminal rather than the product,
34
+ and a kill switch that documents a privacy guarantee turned out to gate nothing.
35
+
36
+ ### Security
37
+
38
+ - **`webbrowser` updated for RUSTSEC-2026-0257**, argument injection through the
39
+ Unix `BROWSER` template: an attacker-controlled non-HTTP(S) URL retaining
40
+ spaces could become extra browser arguments, reproduced upstream by injecting
41
+ `--remote-debugging-port` and `--proxy-server`. Chutes Build opens a browser on
42
+ the OAuth login path, so this is a path we use. Fixed in 1.2.2; the lock now
43
+ resolves 1.2.4. The advisory was published while this release was being
44
+ prepared and turned a commit red that had passed an hour earlier — which is
45
+ what `cargo deny` reading the live database is for.
46
+ - **The self-update kill switch gated nothing.**
47
+ `product::AUTOMATIC_SELF_UPDATE` documents that a running Chutes Build never
48
+ downloads its own update, and it appeared exactly once in the tree — inside a
49
+ comment. What held the line was narrower and accidental: the `internal`
50
+ installer's base URLs are deadened to `127.0.0.1:9`. But `get_installer()`
51
+ honours `CHUTES_BUILD_INSTALLER=npm` and `[cli] installer` in `config.toml`,
52
+ and neither the npm path (`npm view`) nor gh-release (the GitHub API) is
53
+ deadened, so one line of user configuration turned a release binary into one
54
+ that contacts a registry at startup and can spawn a download child. The gate
55
+ is read now, and a test asserts it is read rather than merely present.
56
+
57
+ ### Added
58
+
59
+ - **A failed `SKILL.md` read now says where that skill is actually registered**,
60
+ instead of a bare "does not exist". Ambiguous names, disabled skills, stale
61
+ registrations and reads that are not `SKILL.md` deliberately suggest nothing,
62
+ so a wrong pointer is never preferred to no pointer.
63
+ - **Every workspace RPC declares whether it reads or writes** (`ACTIVITY`), and
64
+ the proxy client can report the server binary's version.
65
+
66
+ ### Changed
67
+
68
+ - The terminal runtime sizes its worker pool against per-user ceilings
69
+ (`pids.max` / `RLIMIT_NPROC`) using upstream's reworked logic, with 329 lines
70
+ of tests it did not have before — including the EAGAIN path.
71
+ - Sandbox hook write-deny is compiled only where it enforces. Its identity
72
+ capture, symlink and rename-race errors and JSON snapshot check move from
73
+ `#[cfg(unix)]` to `#[cfg(any(target_os = "linux", all(unix, test)))]`: the
74
+ rules are applied through Linux namespaces, so on macOS that code compiled and
75
+ never ran. Linux behaviour is unchanged.
76
+
77
+ ### Fixed
78
+
79
+ - **A busy model is waited for, not replaced.** A capacity 429 used to swap the
80
+ model you chose for `model-router` on the first attempt, instantly, discarding
81
+ the `Retry-After` the server had just sent — the header was parsed and then
82
+ dropped at that layer. A short delay (≤5s) on a 429 or 503 is now honoured on
83
+ the selected model before any switch; a long one still switches, because that
84
+ chute is busy for a while and another candidate can answer now. A server
85
+ sending `x-should-retry: false` is obeyed rather than retried against.
86
+ - **Esc worked only once a stream existed.** Everything before the first chunk
87
+ ignored the cancellation token, so a slow connect left the key dead until the
88
+ request returned on its own. All three backends now race the request against
89
+ cancellation, with the cancel arm biased first — the request goes out on the
90
+ future's first poll, so an already-cancelled turn was previously winning only
91
+ about half the time.
92
+ - **`scripts/rebrand.py` rewrote the sentences that name upstream on purpose.**
93
+ Running `--apply` over the tree changed six files with nothing to do with the
94
+ port in hand, every one of them wrongly: the README attribution became
95
+ "SpaceXAI's Chutes Build", `AGENTS.md` came to say the re-base replaced our own
96
+ wordmark with our own, and the getting-started guide gained a fork link to
97
+ `github.com/xai-org/chutes-build`, which does not exist. `--check` reported the
98
+ correct sentences as violations, so it would have pushed the next person to
99
+ "fix" them. The rules cannot tell "Grok Build, the thing we forked" from "Grok
100
+ Build, a name to replace"; those six are now pinned by exact text, and the
101
+ script fails loudly if one is reworded rather than silently exempting a file.
102
+
103
+ ### Verification
104
+
105
+ - **Those two crates' failing tests are not a fixed set, and that is the
106
+ finding.** CI ran narrow filters over `xai-grok-tools` and
107
+ `xai-grok-workspace`, leaving ~188 tests neither passing nor watched, so a new
108
+ failure among them was indistinguishable from the rest. The intended fix was a
109
+ committed per-test baseline that CI compares against — and it does not work
110
+ here, because the set moves: of 53 `xai-grok-workspace` failures recorded on a
111
+ local Windows machine, 17 pass on the Windows runner and 2 others fail; a
112
+ `daemonize` test failed on Linux in one run and not in the run 90 minutes
113
+ before. Same commit, same platform.
114
+
115
+ So `scripts/known_failures.py` and the two baselines ship as a **local
116
+ diagnostic**, not a CI gate, and the CI steps are removed. A gate is possible
117
+ once these suites are deterministic, and making them so is the actual work
118
+ this uncovered — the baseline is not a substitute for it. Separately, the full
119
+ `xai-grok-tools --lib` suite never completes on the Windows runner at all: 107
120
+ minutes, then 140, then a 40-minute timeout, against six minutes on Linux.
121
+ Both are recorded in `docs/upstream-sync.md` under what is still open.
122
+ - **The pager suite reported seven failures that were the terminal.**
123
+ `default_actions()` reads the process environment, so inside a VS Code terminal
124
+ Quit binds to Ctrl+D, interject moves to Ctrl+L, and a terminal with native
125
+ link hover owns link clicks. Each test asserted one side of a rule that has
126
+ two. They use the pinned registries that already existed, and the link test now
127
+ asserts both sides — covering something nothing covered: with native link
128
+ hover, *not* intercepting is correct. 8275 pass in both configurations.
129
+ - `scripts/port_assist.py` sorts an upstream delta into what needs a judgement
130
+ call and what does not — files absent here, files we never touched, and files
131
+ whose divergence *is* the rebrand, verifiable because
132
+ `rebrand(upstream@base)` reproduces our version character for character. 127 of
133
+ the 208 outstanding files need no human decision. It leaves genuine divergence
134
+ alone, refuses to resurrect files we deleted on purpose, and skips binaries.
135
+ - A notification-hook test waited five seconds for a forked shell and went red on
136
+ a loaded runner. `run_hook` returns its join handle now, so the test waits for
137
+ the fact rather than the clock.
33
138
 
34
139
  ### Documentation
35
140
 
36
- - `docs/upstream-sync.md` records the pending port measured rather than
37
- estimated: 3 commits, 238 files, of which 147 overlap work of ours and 91 do
38
- not. It also records that `git diff HEAD upstream/main` is the wrong
39
- instrument — it reports 1424 files, because the fork is not a descendant of
40
- upstream and the diff is mostly our own re-base read backwards.
41
- - `AGENTS.md` records that `xai-grok-pager --lib` reports seven failures when
42
- run from a VS Code terminal, and that they are the terminal rather than the
43
- code: `default_actions()` reads the process environment, so Quit binds to
44
- Ctrl+D there and the `ctrl_q` tests fail against a correct app.
141
+ - `docs/upstream-sync.md` records what the first areas taught, for the eighty-six
142
+ files still to come: an area is always bigger than the module it started as and
143
+ clippy's `dead_code` is what proves it, since `dead_modules.py` cannot see a
144
+ module that *is* reachable; measure the baseline failure set before attributing
145
+ anything; and upstream's fixtures assume POSIX absolute paths, which on Windows
146
+ are drive-relative, so eleven tests failed here that pass on upstream's CI.
147
+ - `AGENTS.md` gains the three rules the port produced — check `git status` after
148
+ every rebrand run, finish an area on clippy rather than on green tests, and
149
+ measure the baseline failure set before attributing anything — and
150
+ `docs/releasing.md` records that a cancelled job ships nothing even when it
151
+ built everything.
152
+
153
+
45
154
 
46
155
  ## [1.0.3] - 2026-08-11
47
156
 
@@ -70,9 +179,10 @@ did, and one contradicted an earlier fix in this same release.
70
179
  against the endpoint before shipping it — the first version of the change would
71
180
  have broken a model that previously worked.
72
181
  - **An endpoint override reached inference but not the catalogue.**
73
- `docs/configuration.md` documents `CHUTES_ROUTER_BASE_URL`,
74
- `CHUTES_INFERENCE_BASE_URL` and `CHUTES_MODELS_BASE_URL`, and
75
- `chutes-build-core` honours them, but the re-base left the agent's
182
+ `docs/configuration.md` documents `CHUTES_ROUTER_BASE_URL` and
183
+ `CHUTES_INFERENCE_BASE_URL` — `CHUTES_MODELS_BASE_URL` was supported and
184
+ undocumented, and is in the table now — and `chutes-build-core` honours them,
185
+ but the re-base left the agent's
76
186
  `EndpointsConfig` reading only the `CHUTES_BUILD_*` spellings. The documented
77
187
  names come first now, with the others kept as fallbacks.
78
188
  - **The system prompt introduced the agent as xAI's.** The re-base overwrote
@@ -87,8 +197,57 @@ did, and one contradicted an earlier fix in this same release.
87
197
  — a security instruction, not a stylistic one — never to put API keys,
88
198
  credentials, private code or repository contents into a search query or
89
199
  outbound request. Both restored from 0.4.3 and the encrypted templates
90
- regenerated. Present in the 1.0.3 binaries; this entry was written after the
91
- build was dispatched, so the 1.0.3 tag does not carry it.
200
+ regenerated.
201
+ - **Only streaming requests could survive a busy model.** The Chutes fallback
202
+ chain — selected model, then `CHUTES_FALLBACK_MODELS`, then `model-router` —
203
+ was wired into `chat_completion_stream` and never into `chat_completion`. The
204
+ two paths had diverged by omission rather than by any decision, so an
205
+ interactive turn recovered quietly from `429 Infrastructure is at maximum
206
+ capacity` while compaction, title generation and the advisor surfaced it raw.
207
+ Both paths now share one chain and one policy. Nothing on the non-streaming
208
+ path can be mid-stream, so the "never switch models once bytes have shipped"
209
+ rule is satisfied trivially there. The chain's ordering also became testable
210
+ without touching process-wide environment variables, which no test could do
211
+ safely while the rest of the suite runs in parallel — the env reads moved to a
212
+ wrapper around a pure core.
213
+ - **`CHUTES_BUILD_OAUTH2_PRINCIPAL_TYPE=Team` could never complete a flow.**
214
+ `default_team_oauth2_scopes()` was still upstream's — `grok-cli:access`,
215
+ `api:access`, `team:read`, `conversations:*`, `workspaces:*`. The user-facing
216
+ list was corrected during the re-base cleanup and this one was missed.
217
+ `api.chutes.ai` advertises no team principal at all: its `scopes_supported` is
218
+ entirely user-level, so an authorization server was being asked for scopes it
219
+ has never heard of. The default is now the user list; a deployment that really
220
+ does define a team principal says so through `CHUTES_BUILD_OAUTH2_SCOPES`.
221
+ Established by reading the IdP's own discovery document rather than the prose
222
+ documentation, which does not cover team principals either way.
223
+
224
+ ### Release engineering
225
+
226
+ - **The first 1.0.3 built every artifact and shipped none.** `darwin-arm64`
227
+ finished in 118m40s of a 120-minute budget: binary, strip, smoke test,
228
+ package, checksum and upload all succeeded, and the wall then killed the
229
+ cache-save step eighty seconds later. A cancelled job fails the run, so
230
+ `Publish GitHub release` and `Publish npm packages` were skipped. Not a
231
+ regression — the macOS jobs of the previous three releases ran 67, 74, 91, 84,
232
+ 96 and 102 minutes, so the budget had been within ~15% of the slowest observed
233
+ build for several releases. Raised to 180.
234
+ - **`main` had been red since 06:40 and nothing showed it.** A rebranded string
235
+ grew three characters past 100 columns, `Check formatting` is the first step of
236
+ the Linux job, and its failure skipped the six test steps and clippy behind it.
237
+ It stayed invisible because `cancel-in-progress: true` plus a run of quick
238
+ pushes cancelled every CI run in between.
239
+
240
+ ### Documentation
241
+
242
+ - `docs/upstream-sync.md` records the pending port measured rather than
243
+ estimated: 3 commits, 238 files, of which 147 overlap work of ours and 91 do
244
+ not. It also records that `git diff HEAD upstream/main` is the wrong
245
+ instrument — it reports 1424 files, because the fork is not a descendant of
246
+ upstream and the diff is mostly our own re-base read backwards.
247
+ - `AGENTS.md` records that `xai-grok-pager --lib` reports seven failures when
248
+ run from a VS Code terminal, and that they are the terminal rather than the
249
+ code: `default_actions()` reads the process environment, so Quit binds to
250
+ Ctrl+D there and the `ctrl_q` tests fail against a correct app.
92
251
 
93
252
  ### Notes on tool compatibility
94
253
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chutes-build",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Privacy-first, open-source coding agent for the Chutes ecosystem",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Michael Gasperini (Mikesoft)",
@@ -37,12 +37,12 @@
37
37
  "check": "npm test && npm run verify:release && npm pack --dry-run"
38
38
  },
39
39
  "optionalDependencies": {
40
- "chutes-build-darwin-arm64": "1.0.3",
41
- "chutes-build-darwin-x64": "1.0.3",
42
- "chutes-build-linux-arm64-gnu": "1.0.3",
43
- "chutes-build-linux-x64-gnu": "1.0.3",
44
- "chutes-build-win32-arm64": "1.0.3",
45
- "chutes-build-win32-x64": "1.0.3"
40
+ "chutes-build-darwin-arm64": "1.1.0",
41
+ "chutes-build-darwin-x64": "1.1.0",
42
+ "chutes-build-linux-arm64-gnu": "1.1.0",
43
+ "chutes-build-linux-x64-gnu": "1.1.0",
44
+ "chutes-build-win32-arm64": "1.1.0",
45
+ "chutes-build-win32-x64": "1.1.0"
46
46
  },
47
47
  "keywords": [
48
48
  "ai",