Releases: ThinkWatchProject/ThinkWatch-Core
Release list
ThinkWatch Core 0.56.0
This release keeps a conversation on the upstream that holds its prompt cache and decides a turn's route once, moves a request to the next upstream when a stream fails before its first content, sets an upstream aside for as long as the reason it gives calls for, answers token counts that no upstream can serve with a local estimate, and resends a request whose reasoning another account sealed.
Upgrade notes
- The control-plane protocol version (
CONTROL_API_VERSION) is now 30. ThinkWatch Lite connects only to a core with the same protocol version, and has to regenerate its protocol types from this release to use it. ThinkWatch Lite 2026.9.24 includes 0.55.2 (protocol 28) and does not connect to 0.56.0: a server used with it stays on 0.55.2 until the app is updated to a release that includes 0.56.0.sudo twcore upgrade --version 0.55.2 --restartswitches a server back to 0.55.2. - The request store's schema is unchanged (22): the request history is kept.
groups[].session_affinityis removed. A configuration that still sets it (onlysession_affinity: falsewas ever written) is refused; delete the line.- Changed in the protocol:
- Removed:
GroupView.session_affinity,GroupView.hurts_cache,GroupInput.session_affinity,DryRunResult.hurts_cache. RequestRouted.affinityandRoutingView.affinity(AffinityView,Stay): whether the request kept its turn's route and why it stayed with the upstream that answered before.Overview.failover(FailoverView).AttemptOutcomehasestimated.
- Removed:
- New in the configuration: the
failoversection (see Setting a failing upstream aside). - Message codes: new
config.failover_rangeandgw.upstream.stream_opening_error; removedgw.count_tokens.bedrock_model.
A conversation stays where its cache is. Routing rules used to be evaluated for every request, and only load-balance groups kept a session on one upstream. A rule on input_tokens or on images could therefore switch upstreams in the middle of an agent's turn, and a failover moved the conversation away from the upstream holding its prompt cache for good.
- A conversation is recognised by the client's session header (
x-claude-code-session-id,session_id,conversation_idand a few others) together with the content fingerprint, or by the fingerprint alone. A turn lasts from a user message until the next one; tool results belong to the turn. - The routing decision made at the start of a turn (rule, group, rewrites) holds for the rest of the turn. It is made again when the input approaches the smallest context window among the candidate models, where the price data gives one.
- The next request goes first to the upstream that last answered the conversation, including one that took over after a failover: always within a turn, and across turns while its last answer read or wrote at least 1024 cached tokens less than five minutes ago. An upstream that is set aside, not among the candidates or in another group is not held to.
load-balancetakes turns between new conversations only.session_affinityand the warnings that a group lowers the cache hit rate are gone, since no strategy drops a warm cache in the middle of a conversation any more.
Errors at the start of a stream fail over. An upstream can answer 200, open a stream and report an error as its first event: Anthropic's overloaded_error, the Codex backend's response.failed when the usage limit is reached, Bedrock's throttlingException, an error chunk from a Chat or Gemini upstream. The client had received nothing yet, and the error still reached it. A streamed answer is now held until its first content arrives, and an error before that point moves the request to the next candidate, as an error status does. What was read while waiting is passed on unchanged. The wait ends after failover.stream_start_wait_secs (15) or 1 MiB; the last candidate is not held.
Setting a failing upstream aside. Failures are sorted by the reason the upstream gives.
- 401, 403, 402 and 404 now move the request to the next candidate. A 400 or 422 does so only when its body names an insufficient balance, a used-up quota or an unavailable model; other 4xx still go to the client unchanged. The last candidate's own 4xx reaches the client as the upstream wrote it.
- An insufficient balance sets the upstream aside for
no_balance_pause_secs(1800). A used-up quota sets it aside until the reset time the upstream names in the body, in its quota headers or in a GLM 429, and forquota_pause_secs(3600) when it names none. A rate limit withRetry-After(or Gemini'sretryDelay) sets it aside for that long, at mostrate_limit_max_pause_secs(3600). A missing model moves on without counting against the upstream. - Failures without a stated reason pause the upstream after
failures_to_pause(3) in a row, forpause_secs(60), doubling with each further pause up tomax_pause_secs(600) and starting over after a success. - A request with a single candidate is never affected, and when every candidate is set aside they are all tried.
Counting tokens. /v1/messages/count_tokens and Gemini's :countTokens routed to an upstream of another format, or to a same-format upstream that answers 404 or 405, are answered by the gateway with an estimate of the system prompt, messages, tool calls and results, and tool definitions; nothing is sent to that upstream. The answer carries x-thinkwatch-local: 1, and the request is recorded as a local answer with no cost. A count does not fail over to another format or another model. Counting through an AWS Bedrock upstream still answers 501 not_supported, after which Claude Code counts precisely itself.
Reasoning sealed by another account. Reasoning items carry content encrypted or signed for the account that produced them. After a failover from one account to another, the upstream refuses them (Responses' invalid_encrypted_content, Anthropic's invalid signature in a thinking block). The request is now sent once more without them, and the refused items are left out up front on the conversation's later turns to that upstream.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.56.0:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.56.0An installation made with the script switches to 0.56.0 with:
sudo twcore upgrade --version 0.56.0 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- Keep a conversation on the upstream that answered it, and hold a turn's route by @fylorn in #237
- Estimate count_tokens locally; resend without reasoning sealed by another account by @fylorn in #238
- Fail over on errors at the start of a stream; pause upstreams by the reason they give by @fylorn in #239
- chore: v0.56.0 by @fylorn in #240
Full Changelog: v0.55.2...v0.56.0
ThinkWatch Core 0.55.2
This release stops a Bedrock API key that is not valid from passing the connection check.
Upgrade notes
- The control-plane protocol version is unchanged (28) and so is the request store's schema (22). ThinkWatch Lite 2026.9.23 includes 0.54.0 (protocol 27) and does not connect to 0.55.x: a server used with it stays on 0.54.0 until the app is updated to a release that includes 0.55.2.
- No message codes change.
Checking a Bedrock upstream. AWS answers a Bedrock API key that is not valid with AccessDeniedException ("Authentication failed: …", "Invalid API Key format: …"), the same exception it uses for a valid credential that may not list models. The check took every AccessDeniedException on the model listing to mean the credential works, so a mistyped key passed with "The credential works, and it has no permission to list models", and the background model fetch said the same. Only a refusal whose message says the identity is not authorized, which is how IAM denies an action, now counts as missing list permission; any other one reports the key as rejected. AWS's message is only inspected and never passed on, since it names the account.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.55.2:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.55.2An installation made with the script switches to 0.55.2 with:
sudo twcore upgrade --version 0.55.2 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- fix(gateway): report a Bedrock API key that is not valid as rejected by @fylorn in #235
- chore: v0.55.2 by @fylorn in #236
Full Changelog: v0.55.1...v0.55.2
ThinkWatch Core 0.55.1
This release stops a request that requires a server-side tool, such as Claude Code's web search, from being sent without it to an upstream of another format.
Upgrade notes
- The control-plane protocol version is unchanged (28) and so is the request store's schema (22). ThinkWatch Lite 2026.9.23 includes 0.54.0 (protocol 27) and does not connect to 0.55.x: a server used with it stays on 0.54.0 until the app is updated to a release that includes 0.55.1.
- New message codes:
gw.convert.tool_unsendableandgw.convert.tools_unsendable.
Requests that require a server-side tool. Claude Code runs each web search as a request that carries only the server-side web_search tool and forces the model to use it. Converted for an upstream of another format (AWS Bedrock, OpenAI, Gemini), the server tool was dropped, since only the provider it belongs to runs it: the model answered without searching, and Claude Code passed that answer on as the search results. When the tool a request forces cannot be sent in the upstream's format, that attempt is no longer made. The next upstream of the route is tried, and if none can take the request the client gets a 400 that names the tool and the upstream. Server-side tools offered alongside other tools, which the model is free to leave unused, are still dropped and listed in the request's details as before.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.55.1:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.55.1An installation made with the script switches to 0.55.1 with:
sudo twcore upgrade --version 0.55.1 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- fix(gateway): refuse a hop that would drop the tool a request requires by @fylorn in #233
- chore: v0.55.1 by @fylorn in #234
Full Changelog: v0.55.0...v0.55.1
ThinkWatch Core 0.55.0
This release adds AWS Bedrock upstreams. Requests to them are converted to Converse and signed per request, and prompt-cache breakpoints and Claude's thinking now survive that conversion. Each request is priced by the model it was actually sent as, long-context prices follow the thresholds the price data gives, and an error an upstream reports partway through a stream now counts as a failure.
Upgrade notes
- The control-plane protocol version (
CONTROL_API_VERSION) is now 28. ThinkWatch Lite connects only to a core with the same protocol version, and has to regenerate its protocol types from this release to use it. ThinkWatch Lite 2026.9.23 includes 0.54.0 (protocol 27) and does not connect to 0.55.0: a server used with it stays on 0.54.0 until the app is updated to a release that includes 0.55.0.sudo twcore upgrade --version 0.54.0 --restartswitches a server back to 0.54.0. - The request store's schema is unchanged (22): the request history is kept.
- Changed in the protocol:
Protocolhasbedrock.ProviderInput.awsandProviderView.aws(AwsKeys): the access keys, or the name of an AWS profile, and the region to sign for.ProviderView.regionandProviderPreview.region: the region of a Bedrock upstream.AttemptView.model: the model an attempt sent when a routing rule rewrote it.
- New in the configuration: the protocol
bedrockandproviders[].aws. The 200K long-context threshold of a price sheet'sinput_above_200kandoutput_above_200know counts cache reads and writes (see Priced by the model that was sent). - New message codes:
config.credential.:bedrock_oauth,key_and_aws,bedrock_no_credential,aws_not_bedrock,aws_empty,aws_profile_and_keys,aws_empty_profile,aws_signed_header,aws_no_region,aws_bad_region,aws_region_mismatchconfig.aws_profile.:no_home,unreadable,not_found,unsupported,no_keysgw.upstream.:aws_token_expired,aws_profile_expired,bedrock_refused,bedrock_refused_unnamed,sign_failed,eventstream_broken,stream_exception,stream_errorgw.probe.aws_token_expired,gw.probe.bedrock_list_denied,gw.count_tokens.bedrock_model,gw.count_tokens.bedrock_upstream,control.replay_bedrock,l3.sign_failed
AWS Bedrock upstreams. An upstream at https://bedrock-runtime.<region>.amazonaws.com is recognized as bedrock. It authenticates in one of three ways:
- A Bedrock API key in
key, sent asAuthorization: Bearer. - AWS access keys in
aws:access_key_id,secret_access_keyand, for temporary credentials,session_token, each of which can be${VAR}. Every request is signed with them (SigV4) once its body is final. aws.profile: the access keys of a profile in the AWS credential files (~/.aws/credentials,~/.aws/config, or the filesAWS_SHARED_CREDENTIALS_FILEandAWS_CONFIG_FILEname) on the machine core runs on. The files are read again when they change, so a tool that refreshes temporary keys there needs no restart. Nothing is run to obtain a credential: a profile that signs in through IAM Identity Center, runs acredential_processor assumes a role is refused, naming the setting.
Requests in every client format are converted to Converse and ConverseStream. The binary eventstream is turned into server-sent events as it arrives, so usage, the first token, output redaction and tool-call inspection work as they do for any other upstream. The anthropic-beta values Bedrock accepts for Claude are carried into the request body. The model list comes from the region's control plane: the foundation models that can be invoked on demand, the inference profiles AWS defines (us.anthropic.claude-…, global.…), and the account's application inference profiles, listed by the ARN they are invoked by. Checking the connection and the inference speed test go through the same signing. For a VPC endpoint or a proxy, write its address in base_url and the region in aws.region.
- AWS's text for a refused credential (HTTP 401 or 403) names the account and the IAM identity. The client receives the gateway's own sentence instead, and AWS's text is not recorded as the request's error.
- An expired session token is named as such; for a profile, the message says to refresh the profile.
- An exception AWS sends inside a stream ends the request as a failure.
count_tokensfor a model that only Bedrock upstreams serve is answered with 501not_supported, the answer Claude Code's guidance for gateways names; Claude Code then counts with a one-token request. Bedrock's own CountTokens covers only some older Claude models onbedrock-runtime.- A replay to a Bedrock upstream is refused: a replay sends the recorded request as it was, and Bedrock takes only the Converse format, which no client sends.
The code that speaks to Bedrock (signing, the eventstream, the endpoints and the model catalog) is the new layer-one crate tw-bedrock, which ThinkWatch Enterprise shares.
Converse keeps cache breakpoints and thinking. The conversion to Converse used to drop cache_control, so a client that cached its prompt never hit the cache through Bedrock. Cache breakpoints now become cachePoint blocks (at most four, as Bedrock allows), with the one-hour TTL where the client asked for it, and cache reads and writes, one-hour writes included, are read back. Claude's thinking and effort are sent in additionalModelRequestFields, and its reasoning comes back as reasoning content.
Priced by the model that was sent. A routing rule can send a request as another model, and the upstream charges for the model it received. Each attempt now records the model it sent when a rule rewrote it (AttemptView.model), and a request is priced by the model of the attempt that served it. The row still names the model the client asked for. The list of unpriced models names the model a price has to be set for.
- A Bedrock model the price data does not list borrows a price, always marked estimated: an inference profile from the model it is named after, and an Anthropic model on Bedrock from Anthropic's own name for it.
- Long-context tiers are read with the threshold the price data gives (200K for Claude Sonnet 4 and 4.5, 272K for GPT-5.4 and later), including their cache prices. A request's input counts its cache reads and writes when the tier is chosen. The 272K tier was not recognized before, and a request with a warm cache was priced as a short one.
- A price the data does not give is filled in, and the cost is marked estimated whenever it is used: a one-hour cache write costs twice the input price (it used to fall back to the five-minute price), and the missing cache prices of a long-context tier grow with its input price.
Errors partway through a stream. An upstream can answer 200, stream part of an answer and then report an error in the stream: Anthropic's overloaded_error, Responses' response.failed, an error in a Chat or Gemini frame. Such a request was recorded as finished; it is now a failure, with the usage the upstream reported before the error.
Listed models. GET /v1/models and GET /v1/models/:model no longer stamp each model with the time of the request. created and created_at are now the Unix epoch, since the gateway does not know when a model was released.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.55.0:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.55.0An installation made with the script switches to 0.55.0 with:
sudo twcore upgrade --version 0.55.0 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha2...ThinkWatch Core 0.54.0
This release records when each response's first token arrives and how fast the response is generated, and corrects the generation rate that GET /live reports.
Upgrade notes
- The control-plane protocol version (
CONTROL_API_VERSION) is now 27. ThinkWatch Lite connects only to a core with the same protocol version, and has to regenerate its protocol types from this release to use it. ThinkWatch Lite 2026.9.21 includes 0.53.0 (protocol 26) and does not connect to 0.54.0: a server used with it stays on 0.53.0 until the app is updated to a release that includes 0.54.0.sudo twcore upgrade --version 0.53.0 --restartswitches a server back to 0.53.0. - The format of the request history changed (request store schema 22, from 21). On its first start, 0.54.0 replaces the request history and the stored request and response bodies of an earlier version with an empty store; the configuration is kept. Switching back to an earlier version empties it again.
- Changed in the protocol: the new event
RequestFirstToken { id, ttft_ms },RequestFinished.tokens_per_sec,HistoryRow.ttft_msandHistoryRow.tokens_per_sec, and the new endpointsGET /token-rateandGET /token-rate/provider, which returnTokenRateView { model, p50, samples }.GET /latencyandGET /latency/providernow report the time to the first token. No message code changed.
First token. The headers of a streamed response usually come back as soon as the upstream receives the request. The upstream then queues the request and reads the prompt before it produces anything, and the time a user waits is the time to the first token, not to the headers. The gateway now reads each successful streamed response in the upstream's format and reports the moment the first text, reasoning or tool call arrives; opening frames such as message_start and response.created do not count. A response that is not streamed arrives whole and has no first token. GET /latency and GET /latency/provider now give percentiles of this time, so responses that are not streamed are left out of them. url-test groups still pick the upstream with the fastest response headers.
Generation speed. Each finished streamed request has a generation speed: the output produced after the first token, divided by the time from the first token to the end.
- When the model reasoned before the first token without streaming its reasoning, those reasoning tokens were generated outside that time. They are subtracted when the upstream reports how many there were, as OpenAI and Gemini do. When it does not, as with Anthropic, the request has no speed rather than one several times too high.
- Reasoning that is streamed as it happens is counted.
- Responses that are not streamed, cancelled and failed requests, and generations shorter than half a second have no speed.
GET /token-rate and GET /token-rate/provider give the median speed by model and by upstream, with the number of samples.
The live rate. GET /live used to divide each request's output by the time after its response headers. A response that is not streamed gets its headers only when generation is done, so that time was a few milliseconds, and one such request could push the rate to tens of thousands of tokens per second. The rate now combines the speeds of the requests that have one, weighted by their generation time.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.54.0:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.54.0An installation made with the script switches to 0.54.0 with:
sudo twcore upgrade --version 0.54.0 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- Record when the first token arrives and how fast a response is generated by @fylorn in #220
- chore: v0.54.0 by @fylorn in #221
Full Changelog: v0.53.0...v0.54.0
ThinkWatch Core 0.53.0
This release changes what ThinkWatch sends to upstreams. Each upstream now receives the request and only the headers it needs; the client's own identity stays with ThinkWatch unless an upstream asks for it. It also fixes two problems with ChatGPT account upstreams.
Upgrade notes
- The control-plane protocol version is now 26 (it was 25). The request store's schema is unchanged (21). ThinkWatch Lite 2026.9.20 includes 0.52.1 (protocol 25) and does not connect to 0.53.0, so a server used with it stays on 0.52.x until the app is updated to a release that includes 0.53.0.
- Upstreams now see ThinkWatch's
User-Agentinstead of the client's. An upstream that admits only certain clients, such as Kimi For Coding, Bailian Coding Plan or a relay restricted to official clients, needs the new provider fieldforward_client_identity: true. - New message code:
config.credential.chatgpt_client_identity.
Each upstream gets only what it needs. Client request headers used to reach the upstream almost unchanged. Only credentials, a few transport headers and, when a request was translated, the client format's own protocol headers were removed. Claude Code's User-Agent, x-app, x-stainless-* and session ID, Gemini CLI's installation ID and a browser client's Cookie and Origin all went through. Outbound headers now come from three sources only:
- Set by ThinkWatch: its own
User-Agent, andcontent-typewhen the body was translated. - The upstream's configuration: the credential and the headers written there.
OpenAI-OrganizationandOpenAI-Projectbelong here, because they belong to the upstream's key. - The client's request, only for the headers the upstream's protocol uses:
- Anthropic:
anthropic-*, exceptanthropic-dangerous-direct-browser-access. - OpenAI:
Idempotency-KeyandX-Client-Request-Id. - Gemini: none.
- Every protocol:
content-typeandacceptwhen the body passes through unchanged.
- Anthropic:
An Anthropic request without anthropic-version now gets the default one. When the body passes through unchanged, identity fields that clients fill in themselves are removed from it: Claude Code's metadata.user_id, and the Codex installation ID and turn metadata in client_metadata.
forward_client_identity. With this field on, an upstream also receives the client's own User-Agent, x-app, originator, version, session_id and x-codex-* headers, and the body's identity fields are kept. These are the client's own values; nothing is made up. The field is off by default. A ChatGPT account upstream refuses it: that upstream always names ThinkWatch as the sender.
ChatGPT account upstreams.
- New models were missing from the list. The Codex backend leaves a model out of the list when its minimum client version is above the
client_versionthat the caller reports. ThinkWatch reported0.0.0, so a model such as gpt-6-luna, which needs Codex 0.155.0, was not listed, and requests for it were refused with "No upstream serves model …" even though the account can use it. ThinkWatch now reports99999.0.0, so the list shows every model the account can use. Requests still name ThinkWatch as their origin. - The ChatGPT app's identity is no longer forwarded. The Codex inside the ChatGPT desktop app adds an attestation generated by the app (
x-oai-attestation), its installation ID and turn metadata. ThinkWatch forwarded them together with the token it had obtained itself, and on one account OpenAI revoked that token within seconds of the first such request.- From the client, the Codex backend now receives only
x-codex-turn-state,x-openai-internal-codex-responses-liteandx-codex-beta-features. - The attestation, installation ID and turn metadata cannot be set in an upstream's configured headers.
- From the client, the Codex backend now receives only
- Request bodies are compressed. Requests to the Codex backend are compressed with zstd, which is what Codex does when it talks to that backend itself.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.53.0:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.53.0An installation made with the script switches to 0.53.0 with:
sudo twcore upgrade --version 0.53.0 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- fix(gateway): list every model a ChatGPT account can use; stop forwarding the ChatGPT app's identity by @fylorn in #217
- feat(gateway): send each upstream only what it needs by @fylorn in #218
- chore: v0.53.0 by @fylorn in #219
Full Changelog: v0.52.1...v0.53.0
ThinkWatch Core 0.52.1
This release fixes two routing problems: a routing rule that rewrites the model was judged by the model the client asked for, and requests in the OpenAI Responses and Gemini formats were never recognized as part of a conversation.
Upgrade notes
- The control-plane protocol version is unchanged (25) and so is the request store's schema (21). ThinkWatch Lite 2026.9.18 includes 0.51.0 (protocol 24) and does not connect to 0.52.x: a server used with it stays on 0.51.0 until the app is updated to a release that includes 0.52.1.
- New message codes:
gw.model.no_upstream_rewrittenandgw.model.not_allowed_rewritten.
Rewriting the model. A rule that sets the model, for example to send the Claude-style names that Claude Desktop requires to a GLM or DeepSeek upstream, was refused with "No upstream serves model …" whenever the target upstream lists its models. Admission, skipping the candidates that cannot serve a request, and the cheapest group's price comparison all looked at the model the client wrote, before any rule rewrote it. They now look at the model each upstream will actually be asked for, including a rewrite that applies to one upstream only (provider_would_be), so admission runs after the rules have decided. When a rewritten model is not served, the error names both models. The routing dry run judges the candidates the same way.
Conversations in the Responses and Gemini formats. Requests from Codex and other clients that use the OpenAI Responses or Gemini format were never assigned to a session, because the fingerprint read only the Anthropic and Chat Completions fields. A load-balancing group with session affinity, which is the default, sent all of them to its first upstream, and the traffic view did not group them into sessions. The fingerprint now also reads instructions and input, and systemInstruction and contents, and uses the request's prompt_cache_key when it has one. Codex sends one per conversation, and Codex conversations started in the same repository open with identical text.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.52.1:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.52.1An installation made with the script switches to 0.52.1 with:
sudo twcore upgrade --version 0.52.1 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- fix(gateway): judge a rewritten model by the name it sends; tell Codex and Gemini conversations apart by @fylorn in #215
- chore: v0.52.1 by @fylorn in #216
Full Changelog: v0.52.0...v0.52.1
ThinkWatch Core 0.52.0
This release hands ThinkWatch Lite the upstream and proxy settings as they are written in the configuration, so its edit dialogs can show them, and stops repeating the version segment when an upstream's base URL ends with it.
Upgrade notes
- The control-plane protocol version (
CONTROL_API_VERSION) is now 25. ThinkWatch Lite connects only to a core with the same protocol version, and has to regenerate its protocol types from this release to use it. ThinkWatch Lite 2026.9.18 includes 0.51.0 (protocol 24) and does not connect to 0.52.0: a server used with it stays on 0.51.0 until the app is updated to a release that includes 0.52.0.sudo twcore upgrade --version 0.51.0 --restartswitches a server back to 0.51.0. - The request store's schema is unchanged (21). Upgrading from 0.50.x or 0.51.0 keeps the request history.
- Changed in the protocol:
ProviderView.base_url,ProviderView.key(now a string),HeaderView.value, the newOAuthView.refreshandOAuthView.client_secret, andProxyView.auth(which replaceshas_auth) carry the settings as written;base_url_masked,SecretViewandHeaderView.maskedare gone.ProviderInput.base_urlis required,ProviderInput.keyis an optional string and everyHeaderInputhas avalue;ProxyInput.authis optional.SecretChangeandProxyAuthInputare gone, and so is the message codecontrol.pass_has_expansion.
Settings as written. The upstream and proxy views used to mask what they showed. The base URL was cut down to its origin, so https://bedrock-mantle.us-east-1.api.aws/v1 was shown without /v1, with a note that credentials had been hidden when there were none. API keys and header values were masked, and OAuth refresh tokens, client secrets and proxy credentials were left out. The app could not fill its edit dialogs in, and every save carried "keep what is saved" states instead of values. The views now carry the settings as they are written in config.yaml, and a save carries the whole definition. The access token of an OAuth credential stays out of the view, and an OAuth credential can still be kept as it is on save: while a dialog is open the gateway may renew the token and write back a new refresh token. GET /config already returned the file unmasked. Diagnostics bundles and logs still mask.
Proxy passwords from the environment. A proxy password may be a ${NAME} reference, as the configuration reference shows (pass: ${PROXY_PASSWORD}). A password containing ${ used to be refused.
Base URLs that end with a version segment. A base URL written up to its version segment, as provider docs give it (https://api.openai.com/v1) and as the configuration reference says, got every request and every connection check sent to .../v1/v1/...: the client's path (/v1/chat/completions) was appended as it was. When the base URL's path ends with the version segment the request path starts with (v1, v2, v1beta, ...), that segment is now used once. Other segments are joined as before.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.52.0:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.52.0An installation made with the script switches to 0.52.0 with:
sudo twcore upgrade --version 0.52.0 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- fix(forward): don't repeat the version segment a base URL ends with by @fylorn in #212
- feat(control): hand the UI upstream and proxy settings as written by @fylorn in #213
- chore: v0.52.0 by @fylorn in #214
Full Changelog: v0.51.0...v0.52.0
ThinkWatch Core 0.51.0
This release stops counting the MCP tool calls of the older GLM Coding Plans as a quota, marks only the weekly window when a model request gets business code 1310, and tells the app at once when a GLM key turns out to have no plan.
Upgrade notes
- The control-plane protocol version (
CONTROL_API_VERSION) is now 24. ThinkWatch Lite connects only to a core with the same protocol version, and has to regenerate its protocol types from this release to use it. ThinkWatch Lite 2026.9.17 includes 0.49.0 (protocol 22) and no released version of the app includes 0.50.0, so a server used with the app stays on 0.49.x until the app is updated to a release that includes 0.51.0.sudo twcore upgrade --version 0.49.1 --restartswitches a server back to 0.49.1. - The request store's schema is unchanged (21). Upgrading from 0.50.0 keeps the request history. Upgrading from 0.49.x or earlier replaces the request history and the stored bodies with an empty store on the first start, as 0.50.0 does; the configuration is kept. Switching back to 0.49.x empties it again.
- Changed in the protocol: the quota window
monthlyis gone, and aQuotaSeenwith emptywindowsmeans the upstream's quota was taken down. No endpoint, type or message code changed.
MCP calls are not a quota. The quota endpoint of the older GLM Coding Plans also reports a count (TIME_LIMIT) of calls to Z.ai's own MCP tools: search-prime, web-reader and zread. Those calls do not go through the gateway, so the count says nothing about the requests it forwards. Core no longer reads it: there is no monthly window, and a used-up count no longer marks the upstream used up or emits QuotaExhausted. credits are reported only for the windows of credit-based plans (CREDIT_LIMIT).
1310 is the weekly quota. A 429 with business code 1310 on a model request marks the weekly window used up, whatever its message says. 1308 still marks the 5-hour window. For 1316 to 1321 the window still comes from the message when it names the 5-hour or weekly window; otherwise it is the fuller of the two known windows, or the weekly one when neither is known.
A key without a plan. When a GLM key is found to have no plan, core clears the quota it had stored for that upstream and emits a QuotaSeen with empty windows, so the app removes the quota at once instead of on its next read of /quota.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.51.0:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.51.0An installation made with the script switches to 0.51.0 with:
sudo twcore upgrade --version 0.51.0 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- fix(quota): ignore GLM MCP call counts and announce a cleared quota by @fylorn in #210
- chore: v0.51.0 by @fylorn in #211
Full Changelog: v0.50.0...v0.51.0
ThinkWatch Core 0.50.0
This release lets Claude Desktop's third-party mode use the gateway, cleans the extensions DeepSeek Harness adds to its requests before they reach an upstream other than DeepSeek, reads the GLM Coding Plan quota of Z.ai and BigModel upstreams, and recognises requests from Antigravity CLI. A gateway bound to a network interface now starts even when that interface is not there yet.
Upgrade notes
- The format of the request history changed (request store schema 21, from 20). On its first start, 0.50.0 replaces the request history and the stored request and response bodies of an earlier version with an empty store; the configuration is kept. Switching back to an earlier version empties it again.
- The control-plane protocol version (
CONTROL_API_VERSION) is now 23. ThinkWatch Lite connects only to a core with the same protocol version, and has to regenerate its protocol types from this release to use it. ThinkWatch Lite 2026.9.17 includes 0.49.0 (protocol 22) and does not connect to 0.50.0: a server used with it stays on 0.49.x until the app is updated to a release that includes 0.50.0.sudo twcore upgrade --version 0.49.1 --restartswitches a server back to 0.49.1. - New in the protocol:
QuotaWindow.credits(a new type,QuotaCredits { total, used, remaining }), the quota windowmonthly, andsession_log_bytesonRequestStartedandHistoryRow. New message code:gw.files.unsupported. /v1/filesand/filesnow answer 404 on the gateway instead of being forwarded to an upstream.
Claude Desktop. Claude Desktop in third-party inference mode, and the Claude Code engine it embeds, expect a few things of a gateway:
HEAD /api/hello, the probe Desktop sends at startup, is answered with 200 by the gateway itself, without a key and without reaching an upstream. It used to be refused with 401.- The client gives up on a stream that is silent for about five minutes. When a client speaking Anthropic Messages has received nothing for 15 seconds, the gateway writes
event: ping, so a converted upstream that is reasoning or queued at length no longer looks like a dead connection. The timer runs from the last bytes the client received, not from the upstream's, because conversion drops the upstream's own keep-alive comments. Pings go only between frames, are not recorded in the stored body or counted as output, and are never sent to clients of other formats. After ten minutes without a byte from the upstream the gateway stops pinging, so a half-open upstream connection ends on the client's timer instead of lasting forever. /v1/modelsanswers Anthropic clients in the Anthropic shape:type,display_name,created_at,has_more,first_idandlast_id, and, for models whose id reads as Claude,anthropic_family_tier, which Desktop uses to resolve aliases such assonnet. The OpenAI fields stay on the objects, because clients that send onlyx-api-keyare also read as Anthropic.
DeepSeek Harness. DeepSeek Harness sends DeepSeek's extensions to whichever base URL it is given: top-level dsh_* fields, among them a session log of the whole conversation of up to 8 MiB per request, role: system entries in messages, tool_addition and tool_removal blocks with defer_loading tools, thinking enabled without a budget, and x-deepseek-harness-* headers. A request is recognised by its User-Agent or a dsh_* field. To DeepSeek's official endpoint it goes through unchanged. To any other upstream, every request from it, including count_tokens, has the dsh_* fields removed, its system entries appended to system in order, the tool change blocks and defer_loading removed and listed on the hop as a conversion lists what it drops, and a thinking without a budget rewritten to what the model accepts. The x-deepseek-harness-* headers are not forwarded, and the tool changes beta is taken out of anthropic-beta, with every line of a header sent on several lines kept. session_log_bytes gives the size of the session log a request carried, so the app can show that it carried one. /v1/files and /files answer 404: a file uploaded to the upstream picked at that moment cannot be used by a later request routed to another, and the 404 makes DeepSeek Harness send images inline.
GLM Coding Plan quota. An upstream whose base_url host is api.z.ai or open.bigmodel.cn is read as a GLM Coding Plan upstream. Its responses carry no quota headers, so GET /quota asks the account's quota endpoint for each enabled one that is due (at most once a minute, waiting up to 5 seconds), and traffic to one asks at most every 5 minutes; failures back off from 30 seconds to 5 minutes. Windows are 5h, weekly and monthly (the monthly MCP calls of the older plans), told apart by their length, and a reset time beyond a window's length is dropped. Credit-based plans report credits with the three numbers the endpoint gives. A 429 carrying a used-up quota code marks the window used up and emits QuotaExhausted. A key without a plan is left alone for an hour, and a key that had a quota needs two no-plan answers in a row before it is believed, because a transient server error returns the same code.
Antigravity CLI. Antigravity CLI sends the Go genai SDK's default user agent. A user agent that starts with google-genai-sdk and carries Google's internal Go toolchain (gl-go/ followed by cl/) is now reported as the client antigravity-cli. Programs built on the SDK with a public Go release are not.
Listening on an interface that is not there yet. When listen.gateway.bind names a network interface that does not exist or is offline, or an address that is not on this machine yet, twcore used to exit at startup. The gateway now listens on 127.0.0.1 only and says why in the listen status (gw.listen.no_such_nic, gw.listen.nic_offline), asks the system again every three seconds, adds the interface once it appears, moves to its new address when it changes, and drops back to loopback when it goes away. This lets the desktop app on Windows bind the gateway to the WSL adapter, which appears only once WSL has started and changes address on every restart. A loopback that cannot be bound is still fatal at startup.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.50.0:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.50.0An installation made with the script switches to 0.50.0 with:
sudo twcore upgrade --version 0.50.0 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- feat(gateway): meet what Claude Desktop's third-party mode asks of a gateway by @fylorn in #203
- feat(quota): read the GLM Coding Plan quota for Z.ai and BigModel upstreams by @fylorn in #204
- feat(gateway): clean DeepSeek Harness requests for non-DeepSeek upstreams by @fylorn in #205
- fix(gateway): start on loopback when the interface to listen on is not there yet by @fylorn in #206
- feat(gateway): recognise Antigravity CLI requests as antigravity-cli by @fylorn in #207
- fix: pre-release review fixes for pings, DeepSeek Harness cleaning and GLM quota by @fylorn in #208
- chore: v0.50.0 by @fylorn in https://github.com/ThinkWatchProject/ThinkWatch-C...