diff --git a/Cargo.lock b/Cargo.lock index ad3c92ed..ff1d8dee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3143,7 +3143,7 @@ dependencies = [ [[package]] name = "tw-api" -version = "0.64.1" +version = "0.65.0" dependencies = [ "serde", "serde_json", @@ -3154,7 +3154,7 @@ dependencies = [ [[package]] name = "tw-bedrock" -version = "0.64.1" +version = "0.65.0" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -3175,7 +3175,7 @@ dependencies = [ [[package]] name = "tw-breaker" -version = "0.64.1" +version = "0.65.0" dependencies = [ "serde", "serde_json", @@ -3183,7 +3183,7 @@ dependencies = [ [[package]] name = "tw-config" -version = "0.64.1" +version = "0.65.0" dependencies = [ "blake3", "libc", @@ -3208,7 +3208,7 @@ dependencies = [ [[package]] name = "tw-control" -version = "0.64.1" +version = "0.65.0" dependencies = [ "axum", "base64", @@ -3250,7 +3250,7 @@ dependencies = [ [[package]] name = "tw-dialect" -version = "0.64.1" +version = "0.65.0" dependencies = [ "memchr", "serde", @@ -3259,7 +3259,7 @@ dependencies = [ [[package]] name = "tw-engine" -version = "0.64.1" +version = "0.65.0" dependencies = [ "serde", "serde_json", @@ -3272,7 +3272,7 @@ dependencies = [ [[package]] name = "tw-gateway" -version = "0.64.1" +version = "0.65.0" dependencies = [ "arc-swap", "async-stream", @@ -3322,7 +3322,7 @@ dependencies = [ [[package]] name = "tw-guard" -version = "0.64.1" +version = "0.65.0" dependencies = [ "base64", "bytes", @@ -3338,7 +3338,7 @@ dependencies = [ [[package]] name = "tw-link" -version = "0.64.1" +version = "0.65.0" dependencies = [ "serde", "serde_json", @@ -3352,7 +3352,7 @@ dependencies = [ [[package]] name = "tw-observe" -version = "0.64.1" +version = "0.65.0" dependencies = [ "tokio", "tracing", @@ -3361,7 +3361,7 @@ dependencies = [ [[package]] name = "tw-plugin" -version = "0.64.1" +version = "0.65.0" dependencies = [ "libc", "rand 0.10.2", @@ -3376,7 +3376,7 @@ dependencies = [ [[package]] name = "tw-pricing" -version = "0.64.1" +version = "0.65.0" dependencies = [ "arc-swap", "flate2", @@ -3389,14 +3389,14 @@ dependencies = [ [[package]] name = "tw-secret" -version = "0.64.1" +version = "0.65.0" dependencies = [ "thiserror", ] [[package]] name = "tw-store" -version = "0.64.1" +version = "0.65.0" dependencies = [ "blake3", "bytes", @@ -3416,7 +3416,7 @@ dependencies = [ [[package]] name = "tw-types" -version = "0.64.1" +version = "0.65.0" dependencies = [ "serde", "serde_json", @@ -3425,7 +3425,7 @@ dependencies = [ [[package]] name = "tw-watch" -version = "0.64.1" +version = "0.65.0" dependencies = [ "libc", "notify", @@ -3436,7 +3436,7 @@ dependencies = [ [[package]] name = "tw-yaml" -version = "0.64.1" +version = "0.65.0" dependencies = [ "saphyr-parser", "serde_yaml_ng", @@ -3446,7 +3446,7 @@ dependencies = [ [[package]] name = "twcore" -version = "0.64.1" +version = "0.65.0" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index b1ad8111..faa7deca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ categories = ["network-programming", "web-programming::http-server"] # 二进制走 CalVer,crate 走 SemVer —— 两者是两回事,卖给不同的人。 # 这里是 crate 的版本。 -version = "0.64.1" +version = "0.65.0" [workspace.dependencies] # ── 内部 crate ─────────────────────────────────────────── diff --git a/release-notes/0.65.0.md b/release-notes/0.65.0.md new file mode 100644 index 00000000..6c195f51 --- /dev/null +++ b/release-notes/0.65.0.md @@ -0,0 +1,25 @@ +This release makes the gateway do much less work per request, keeps large requests from holding up other streams, fixes two ways stored request bodies and sessions could go missing, and stops forwarded bodies from having their keys reordered. On macOS, watching configuration files no longer wakes the process for every file change in the home folder. + +**Upgrade notes** + +- The control-plane protocol version (`CONTROL_API_VERSION`) goes from 40 to 41. ThinkWatch Lite connects only to a core with the same protocol version: ThinkWatch Lite 2026.10.8 includes 0.64.1 (protocol 40) and does not connect to 0.65.0. A server used with it stays on 0.64.1 until the app is updated to a release that includes 0.65.0; `sudo twcore upgrade --version 0.64.1 --restart` switches a server back. +- The request store's schema is unchanged: upgrading from 0.64.1 keeps the request history. The first start on an existing store adds three indexes, which takes about a second for a store of 270,000 requests and grows the file by about 15%. +- The configuration format is unchanged. +- Changed in the protocol: + - `GET /sessions/{id}/transcript` takes `TranscriptQuery` (`from_turn`, optional: only the turns from that index on). A client written for protocol 40 that sends `null` is refused. + - `Transcript` gains `total_turns` and `settled_turns`, the number of leading turns that will not change any more; asking from `settled_turns` next time returns only what is new. + - `DryRunCandidate` gains `share`: the part of a load-balance group's requests the candidate gets with the current weights and factors. It is null outside load-balance groups and for members sitting the round out. +- Message codes are unchanged from 0.64.1. + +**Less work per request.** With the default settings, a 2 MiB request from Claude Code used to cost about 45 ms of processing before its first byte went upstream, and an 8 MiB one about 180 ms; they now cost about 11 and 46 ms. The body is parsed once instead of three times, the checks for known client requests and for keys in the text search bytes directly, the content filter skips plain ASCII, and the scan that finds keys to redact no longer copies the body just to note what it found. Bodies of 1 MiB or more are analysed on a thread of their own, so answers streaming on the same worker no longer pause: the longest gap between relayed chunks while 8 MiB requests arrived fell from about half a second to under 40 ms. + +**Forwarded bodies keep the client's bytes.** Taking out `metadata.user_id` (sent by Claude Code on every request) and applying a routing rule or alias that changes the model, output limit or thinking used to rewrite the whole body with every object's keys in alphabetical order, tool schemas and tool inputs included. Only the changed fields are now edited; everything else reaches the upstream as the client sent it. + +**Stored bodies and sessions.** +- Body clean-up no longer deletes the current day. When one day held more than `body_max_bytes` of bodies, the hourly clean-up emptied that day's folder, including the session being looked at. Older days are still removed whole; the current day loses its oldest requests until it fits. +- A session older than the newest 500 opens again; it used to be reported as not found. +- The session list, the last-seen time of each client and the routing page's counts use indexes instead of reading every request: the session list went from about 180 ms to about 14 ms on a store of 270,000 requests. +- A session's conversation is built once and then extended with new requests, instead of rereading every stored body each time: 830 ms → 6 ms for a 300-turn session. +- Requests are recorded on a thread of their own with a queue separate from the app's live updates, so a slow app window can no longer cause requests to go unrecorded. + +**File watching on macOS.** Watching a folder used to deliver every change anywhere below it, so watching the home folder for `~/.claude.json` woke the process several times a second. Folders are now watched with kqueue and only their own entries count, with the files of interest watched directly for changes in place. Idle over 30 seconds, the watch plan ThinkWatch Lite uses went from 105 wake-ups to 4. Linux and Windows are unchanged.