Aquesta és una versió anterior. La versió actual és la 0.56.1. Common Agent Runtime 0.56.1 · Data de publicació:
Fitxers d’instal·lació 0.49.0
| Arquitectura | Tipus | Àmbit | Mida del fitxer | Baixa | SHA256 |
|---|---|---|---|---|---|
| 64 bits | ZIP → Portable | — | — | github.com | VirusTotal |
L’enllaç i l’SHA256 provenen del manifest comunitari del Gestor de paquets del Windows. Convé comparar el hash del fitxer baixat abans d’executar-lo.
Notes de la versió
## v0.49.0
### Security
• **A web page you had open could drive the daemon's MCP endpoint.** `POST
/mcp` and `GET /mcp` accepted any request that reached the port, and browsers
will happily send a cross-origin POST to `http://127.0.0.1:9102/mcp` on
behalf of whatever page is loaded — so any site could call `tools/call` and
read or write the user's memory graph. Binding loopback does not prevent
this:the browser is *on* loopback, and DNS rebinding defeats the bind on its
own. The MCP HTTP transport requires `Origin` validation for exactly this
reason and CAR had none.
Both routes now validate `Origin`, absent-permissive and present-strict. **No
`Origin` header is allowed** — a browser always sends one on a cross-origin
request, so its absence cannot be a browser attack, and this is what keeps
every real client working (`curl`, Claude Desktop, Cursor, Claude Code
`--mcp-config`, Codex, and CAR's own connectors client all send none). An
`Origin` naming loopback — `http`/`https` on `localhost`, `127.0.0.0/8`, or
`::1`, any port — is allowed. Anything else, including the opaque literal
`null` sent by sandboxed iframes and `file://` pages, gets `403
{"error":"origin not allowed"}` before the request reaches the dispatcher, so
the tool never runs.
`GET /mcp/health` is deliberately exempt:no side effect, no secrets, and the
documented uptime probe has to keep working. The rule does not widen for a
non-loopback `--mcp-bind` — front the daemon with a reverse proxy that owns
its own CORS policy if you need browser traffic from another origin
(Parslee-ai/car#972 §3).
### Fixed
• **`car-inference` could not link on any Xcode older than 26.** `build.rs`
emitted `-Wl,-weak_framework,FoundationModels` unconditionally on every Apple
target. `-weak_framework` controls how the *load command* is written — it is
not a link-time "optional", so `ld` still resolves the name against the
active SDK and hard-fails with `framework 'FoundationModels' not found` when
it is absent. Pre-26 SDKs do not ship it.
The Swift shim already handled that case:every FoundationModels code path
sits behind `#if canImport(FoundationModels)`, so on such an SDK the compiled
object references no FM symbols and the framework is not needed at all — the
two guards had simply drifted apart. `build.rs` now probes the SDK and emits
the link arg only when the framework is really there, with a `cargo:warning`
otherwise. All six `@_cdecl` entry points are declared outside those blocks,
so the static library still exports the full C ABI, `car_fm_swift_built`
stays set, and `car_fm_is_available` reports 0 — the adaptive router falls
through to another backend exactly as it does on pre-26 *runtimes*.
Nothing caught this because nothing linked the crate on an older SDK:the
Linux `test` job compiles no Apple code and `apple-checks.yml`'s macOS job
ran `cargo check`, which executes `build.rs` but skips codegen and linking.
The `cargo test` step added in Parslee-ai/car#971 is the first one that
links, and it failed on its first real run.
• **Five daemon JSON-RPC methods were dispatched but documented nowhere.**
`assistants.invoke`, `events.stats`, `events.truncate`, `events.clear`, and
`memory.build_context_fast` all worked, and appeared in neither
`docs/websocket-protocol.md` nor the `car-bindings-api` skill that calls
itself the canonical list. External developers consume CAR through those two
and have no access to the source, so an undocumented method is an
unreachable one. Found by a release-time sweep of every dispatch arm against
the docs; each is now written up with its real parameter and return shape.
Two of them deserved the words more than most:`events.clear` is destructive
and unconditional — it drops the whole session log including the
`events.query` audit trail and any `events.chain.enable` hash chain, with no
undo — and `memory.build_context_fast` returns facts in **creation order,
not relevance order**, which is easy to reach for by name and wrong for
anything that reasons over the result.
`scripts/check-ffi-parity.sh` now covers all five so they cannot drift out
again:`build_context_fast` as a full cross-binding row (it ships on all four
FFI surfaces and had none), the other four under `WS_ONLY_CAPABILITIES`,
which still asserts dispatch + protocol doc + skill. 257 capabilities
checked, up from 252. The crate count in `README.md` and `CLAUDE.md` was
corrected from 66 to 68 in the same sweep; the README crate map itself was
already complete.
• **The daemon's MCP endpoint ignored the `MCP-Protocol-Version` request
header.** A client that had negotiated a revision on `initialize` and then
named it on every later request — which the HTTP transport asks it to do —
was answered identically whether it asked for `2024-11-05` or for a revision
the server has never heard of. `handle_mcp_post` and `handle_mcp_get` read no
version header at all.
Both routes now check it, absent-permissive and present-strict, the same
shape as the `Origin` guard above. **An absent header is allowed** — every
shipped client sends nothing today, and `car_mcp::SUPPORTED_VERSIONS` holds a
single entry, so the revision a silent client negotiated is necessarily the
one we would have demanded; requiring the header would break `curl`, Claude
Desktop, Cursor, and Claude Code for no gain. A value in
`SUPPORTED_VERSIONS` is allowed.
The one narrowing:a client that sends a version *outside*
`SUPPORTED_VERSIONS` now gets `400 {"error":"unsupported
MCP-Protocol-Version","requested":…,"supported":[…]}` instead of being
answered in a dialect it never agreed to. The rejection lands before the
JSON-RPC parse on `POST` and before the SSE session insert on `GET`, so the
tool does not run and no session is registered. `GET /mcp/health` stays
exempt — it is how a rejected client learns which revision the server speaks.
`PROTOCOL_VERSION` is unchanged (Parslee-ai/car#972 §3).
• **MCP `initialize` echoed our protocol version instead of negotiating, and a
notification POST answered `200 {}` instead of `202`.** The `initialize` arm
ignored `params` entirely and returned `PROTOCOL_VERSION` unconditionally.
That is benign today only by accident — `2024-11-05` is the oldest revision,
and a client that supports something newer must accept an older server — but
it makes bumping the constant a breaking change:every client asking for
`2024-11-05` would suddenly be handed a revision it never requested, which
the spec says it SHOULD refuse.
`initialize` now negotiates against a new `car_mcp::SUPPORTED_VERSIONS` list:
the requested version comes back when the server supports it, and absent,
non-string, and unsupported all fall back to `PROTOCOL_VERSION` as a normal
result (not a JSON-RPC error), leaving the client to proceed or disconnect.
The reply is always an entry from the list, never the client's own bytes.
Separately, `POST /mcp` answers a notification with `202 Accepted` and an
empty body, which is what the Streamable HTTP transport specifies — a `200`
carrying `{}` invented a reply where JSON-RPC 2.0 says there is none.
**No client's observed behavior changes today**:`SUPPORTED_VERSIONS` holds
one entry, so echo and fallback produce the same string, and a client sending
no `protocolVersion` is answered exactly as before. The point is that a
future revision bump becomes an append rather than a breaking change
(Parslee-ai/car#972 §3).
• **`car policy-check-hook` wedged every tool call when the installed CLI
predated it.** The subcommand documents a fail-open posture, and it held for
everything reachable from inside the hook — an unreadable payload, a missing
policy directory. It did not cover the failure most likely in the field:the
binary is present but the *subcommand* is not. clap rejects the argv and exits
**2** before the hook runs, and 2 is the one exit code Claude Code reads as
*block this tool call*. The collision is exact. Every `Bash`, `Write`, `Edit`,
`WebFetch` and `NotebookEdit` call in the session was refused with clap's usage
text as the stated policy reason — an install problem presented as a policy
denial, which is the thing the fail-open posture exists to prevent — and it
disabled the very tools an operator would reach for to investigate. A stale
`/usr/local/bin/car` is an ordinary state, documented by `car update` itself:
CarHost.app auto-updates its bundled daemon via Sparkle and leaves the CLI
behind.
`car` now parses its arguments **fallibly**. On a usage error whose argv names
`policy-check-hook`, it prints the reason on stderr — naming the gate as not
gating, and `car update` as the fix — and exits **0**. Every other subcommand
keeps clap's conventional 2; the branch is scoped to the hook argv, because
changing exit semantics CLI-wide to fix a hook-specific problem is the wrong
trade. The fail-open guarantee now covers the process not starting, not just
the evaluation not completing, which is the general rule for anything that can
refuse a tool call.
The plugin's hook command is `car policy-check-hook; exit 0`, which is what
helps a CLI *already* installed and therefore unpatchable. Lossless:no
decision travels in the exit code — a deny rides in
`hookSpecificOutput.permissionDecision` on stdout with exit 0 — so this
discards only the accidental codes (a usage error, a missing binary, a panic),
all of which mean *we could not check*. `; exit 0` rather than `|| exit 0` on purpose:Claude
Code runs a hook through a shell, and on Windows without Git Bash that shell is
Windows PowerShell 5.1, where `||` is a parser error that would have silently
disabled the gate on the platform the subcommand was introduced to support
(Parslee-ai/car#993).
• **The nightly Windows job was red on a test that hard-coded a `/`https://github.com/Parslee-ai/car-releases/releases/tag/v0.49.0