Changelog
May 30, 2026 · View on GitHub
[0.9.6] - 2026-05-30
Changed
- Plugin hook calls route through the bridge loopback proxy instead of the gateway directly. The per-plugin
hooks.jsonnow points Cowork at the proxy's loopback URL with the static loopback secret asAuthorization; the proxy verifies and strips that header, mints the plugin'saud:hookgateway token (resolved from theplugin_idquery parameter), and injects it before forwarding to the public hook endpoints. This replaces the per-plugin.env.pluginfile and the$SYSTEMPROMPT_PLUGIN_TOKENenv-var substitution, which Cowork's agent VM did not reliably propagate into the hook subprocess;allowedEnvVarsis now empty. A hook-route401rotates the per-plugin hook token rather than invalidating the shared bridge token cache. - Hook-scoped credentials issued by
admin keys issue-plugin-tokenno longer carry the minting admin's roles. A hook token (aud:hook) authorizes on scope andplugin_idonly, so the roles were inert. - The GUI marketplace lists managed MCP servers from the in-memory MCP registry — the same source that feeds the
managedMcpServerspolicy — rather than the removed synthetic-plugin.mcp.json.
[0.9.5] - 2026-05-29
Changed
- Managed MCP servers are registered with Cowork through the bridge's loopback proxy rather than the upstream gateway. Each entry points Cowork at the proxy's loopback URL with a static loopback-secret
Authorizationheader instead of carryingoauth: true; the proxy strips that header and injects the rotating, auto-refreshed gateway JWT before forwarding to the registered upstream. This sidesteps Cowork's OAuth flow entirely — it hard-rejects the gateway's non-HTTPS authorize URL on Connect — while every request still carries a live token. Applies to both the MDM managed-prefs writer (install::mdm) and the synthetic-plugin writer (sync::apply::synthetic_plugin); when the loopback secret is unavailable the managed server list is emitted empty rather than half-configured.
[0.9.4] - 2026-05-28
Breaking
bridge::manifest::AgentEntry.mcp_serversandAgentEntry.skillsare nowPluginComponentRef { source, include, exclude }instead ofVec<String>. The manifest envelope tracks the unifiedPluginComponentRefshape now applied across every entity-id reference list insystemprompt-models. Bridge / Cowork consumers that read these fields must traverse.includeinstead of treating the value as a flat list; serialised manifests authored against 0.9.3 are no longer accepted.
Changed
- Bridge no longer emits the
deploymentOrganizationUuidpolicy key into the Claude Desktop managed-prefs plist (macOS) orHKCU\…\Policies\Clauderegistry hive (Windows). Cowork's 3P custom-gateway contract is inference-only (POST /v1/messages+ optionalGET /v1/models, per docs/cowork/3p/gateway and gateway-sso); a custom gateway has no spec surface to assert theintegrations:managepermission that this key flips Cowork into checking. Emitting it locked the Install button under the "Contact an organization owner to install connectors" tooltip without recourse. Cowork now resolves throughmanageFromPersonal = trueand the Install button is live — MCP installation and use over the bridge proxy are unchanged. pick_targetno longer takes apolicy_uuidargument andresolve_targetno longer reads the now-absentdeploymentOrganizationUuidpolicy key; Cowork plugin sync resolves the personal-session org dir directly, falling back to newest-mtime when the personal dir is missing.- Bridge keeps its own working state (
.staging/, sync sentinel, version sentinel, user fragment) under a platform-specific user-writable directory (%LOCALAPPDATA%\systemprompt-bridgeon Windows,~/Library/Application Support/systemprompt-bridgeon macOS,$XDG_STATE_HOME/systemprompt-bridgeon Linux) instead of nesting them under the publishedorg_pluginstree. On Windows that tree lives underProgram Filesand is admin-write-only, so writing scratch state inside it raisedSync failed: io error in create staging: Access is deniedfor non-elevated bridge runs.paths::metadata_dir(_)/paths::staging_dir(_)and theMETADATA_DIR/STAGING_DIRconstants are gone; callers use the newpaths::bridge_working_dir()/bridge_staging_dir()/bridge_metadata_dir().
Added
bridge doctoradds ahook token mintcheck that exchanges the cached OAuth client credentials for a hook token against the gateway's token endpoint withplugin_id=__doctor__. Failures surface the gateway'serror_descriptionverbatim on a single line instead of waiting for the nextsyncPARTIAL output.bridge doctoradds apersonal-session sentinelcheck that scans Cowork's sessions root for an org dir matchingPERSONAL_SESSION_UUID(00000000-0000-4000-8000-000000000001). If Cowork sessions exist but none matches, the constant has drifted from Cowork's source of truth andpick_targetwill silently fall through to its mtime fallback — the check fails loud so the operator updates the bridge before sync misroutes plugins into the wrong session.
[0.9.3] - 2026-05-28
Changed
marketplace.json,known_marketplaces.json, andinstalled_plugins.jsonare written in the shape the current Cowork (Claude 1.5354) reader expects:marketplace.jsongains$schema,description,metadata { description, version, pluginRoot }, and per-pluginauthor/category, withplugins[].sourceflattened to a plain string path;known_marketplaces.jsonis a top-level object keyed by marketplace name withsource,installLocation, andlastUpdatedper entry;installed_plugins.jsonis{ "version": 2, "plugins": { "<plugin>@<marketplace>": [{ "scope", "installPath", "version", "installedAt", "lastUpdated" }] } }. Foreign sibling entries continue to be preserved verbatim.- Cache and marketplace path joins sanitise version strings before writing to the filesystem; RFC3339-shaped versions containing
:no longer trip Windows ERROR_INVALID_NAME duringbridge sync. syncpropagates per-host emit failures intoSyncSummary::host_failuresand the one-line summary now readssync PARTIAL (…) — N host(s) failed: …, so a silently half-published marketplace surfaces in the GUI Activity panel instead of being reported assync ok.- 403 "bad loopback secret" rejections log the resolved secret path, and
tracinglines on empty / missing / freshly minted secret files include the file path, giving operators a single line to follow when Claude Desktop has cached a stale loopback secret. GatewayError::HookTokenRejected { status, body }replaces the bareHttpStatusmapping formint_plugin_hook_tokennon-2xx responses; the gateway's error body is preserved sobridge syncPARTIAL lines carry the underlying RFC 6749 §5.2 reason instead of an opaque status code.
Added
bridge doctorcommand groups the bridge-side self-checks (config, credential source, mint JWT, gateway reachable, authenticated whoami, loopback secret, pinned pubkey, cowork marketplace registration) into a single one-line-per-check diagnostic surface; exits 11 on any failure.SyncError::GatewayUnauthorized { endpoint, status }represents gateway 401/403 from/manifestand/pubkeyas a distinct error with exit code 10 and an actionable "runsystemprompt-bridge login <sp-live-...>" message; the GUI surfaces it via the newsync-gateway-unauthorizedFluent string, and thesync-no-credentialsstring handles the no-PAT-configured case.- Typed wire-shape structs for the Cowork host adapter:
KnownMarketplacesFile,KnownMarketplaceValue,InstalledPluginsFile,InstalledPluginInstall, andMarketplaceMetadata, replacing the ad-hocserde_json::Valuetraversals. - Unit test coverage for the Cowork host adapter (
crates/tests/unit/bridge/cowork-plugins): canonical marketplace shape, known-marketplaces / installed-plugins / settings upsert behaviour, and path sanitisation.
[0.9.2] - 2026-05-27
Changed
- Track
systemprompt-identifiersandsystemprompt-models0.12.0 dependency pins.
[0.9.1] - 2026-05-25
Changed
- Internal lint and visibility cleanup. Bridge sources adopt the workspace's tightened clippy baseline (
unreachable_pub,allow_attributes_without_reason,redundant_pub_crate,let_underscore_must_use) — visibility narrowed frompubtopub(crate)where appropriate, MDM helpers cfg-gated to the OSes that consume them, best-effortResultdiscards justified withtracing::warn!. No user-visible behaviour change.
[0.9.0] - 2026-05-22
Fixed
- Session binding: bridge persists and binds its stable
x-session-id. The bridge now stores itsx-session-idand replays the same value across requests, so/v1/messagesand/bridge/heartbeatno longer return401 "Session missing or revoked"or"X-Session-ID does not match"after the first call. A regenerated session id per request previously orphaned the gateway-side session record.
Added
HostSynctrait + central dispatcher (sync/host_sync.rs). Every bridge integration that materialises manifest data on disk (Cowork synthetic plugin, Codex managed resources, Windows MDM, …) implements oneHostSynctrait withapply/clearmethods. The dispatcher insync::applywalks the staticregistry(), decides per-host whether to callapplyorclearbased on the manifest'senabled_hostsfield, and uniformly logs each outcome — emitter authors no longer reinvent the toggle-and-cleanup gate. Replaces the imperative pile of "callcowork::publishthenmdm::reconcilethen …" insync::apply::mod.- Codex CLI host emitter (
integration/codex_cli/managed_resources.rs). ImplementsHostSyncfor Codex by writing a single plugin bundle that matches Codex's documented discovery contract (verified against the published JSON schema anddevelopers.openai.com/codex/plugins/build). Skills and MCP servers land as one Codex plugin at~/.codex/plugins/cache/systemprompt/systemprompt-managed/current/, containing.codex-plugin/plugin.json(carrying the manifest version),skills/<id>/SKILL.md, and.mcp.json. A[plugins."systemprompt-managed@systemprompt"] enabled = true|falseblock in~/.codex/config.tomlis the user-facing toggle; every other key inconfig.toml(user MCP servers, sibling plugins, model providers) is preserved acrossapplyandclear. Earlier iterations wrote to~/.codex/skills/and to top-level[mcp_servers.sp_*]blocks — neither path is read by Codex, so the marketplace bundle was invisible inside the CLI. - Codex provider-profile install (
integration/codex_cli/install.rs) targets the documented system path and merges instead of overwriting. Linux/macOS now write to/etc/codex/config.toml(the prior/etc/codex/managed_config.tomlwas undocumented and not in Codex's config chain). The install reads the existing target, strips bridge-owned keys (model_provider,model_providers.systemprompt,otel,analytics), deep-merges the freshly generated TOML on top, and atomic-writes — so prior keys survive reinstall. NewCODEX_SYSTEM_CONFIGenv var overrides the system path for hermetic tests. - GUI: per-host enable toggle posts to gateway (
gui/handlers/agents.rs::on_set_enabled_host_requested). New IPC entrypoint sendsPOST /v1/bridge/enabled-hostswith the host id and desired state, then emitsUiEvent::SetEnabledHostFinished. The GUI no longer mutates localagents.jsondirectly — host enable state is a profile fact owned by the gateway and arrives back through the next signed manifest. Matches the broader rule that host enable state lives in the user profile, not local toggles.
Changed
integration/codex_cli/install.rs(326 lines, hand-rolled base64) split intoinstall/{mod,merge,render}.rs.mod.rskeepswrite_profile/install_profile/writable;merge.rsownsmerge::installplus theOWNED_*constants for bridge-owned keys;render.rsowns TOML + mobileconfig rendering. The 93-linerender_managed_tomlis now 16 lines, dispatching towrite_provider_block/write_otel_block/write_models_block. Hand-rolledbase64_encodereplaced withbase64::engine::general_purpose::STANDARD. WHAT-doc-comments onOWNED_*consts collapsed into a single module-level//!block.- Silent error sites in
sync/mod.rs::persist_last_syncandintegration/codex_cli/probe.rs::parse_into_keysnow log viatracing::warn!. Threelet _ = …/.unwrap_or_default()discards inpersist_last_syncand one.ok()?on TOML parse inprobe::parse_into_keyspreviously dropped errors silently; each now logs context (path, dir, source) before the best-effort fallback. - Bridge codex tests no longer use
unsafe { env::set_var }.crates/tests/unit/bridge/{sync,integration}/src/codex_*rewritten on top of thetemp-envcrate (added as workspace dev-dep) — each test scopesCODEX_HOME/CODEX_SYSTEM_CONFIGviatemp_env::with_var(s)instead of mutating process env, removing the manualMutex<()>lock and theunsafeblock. agents_statesimplified.migrate_from_existing_profiles(which probed every registered host on startup) andstore_existsare gone. Replaced bysave_from_manifest(enabled_hosts: &[String]), called fromsync::applywhenever a new signed manifest is applied.saveis nowpub(crate). The first-run "auto-enable everything that looks installed" migration is no longer needed because the manifest is authoritative.
Added
- Cowork plugin sync (
integration/cowork_plugins/). Per-plugin marketplace publish into the active<session>/<org>/cowork_plugins/tree: marketplace upsert, installed-plugin upsert, enabled-settings upsert (foreign-entry preservation throughout), plus a per-pluginclaude-plugin/plugin.jsonpatch that wireshooks/hooks.json. Reverseunpublishpath included. - OAuth hook-token client (
auth/plugin_oauth.rs). Per-tenant OAuth client + plugin-scoped hook-token cache.client_secretis stored in the OS keystore (Keychain on macOS, Credential Manager on Windows, Secret Service on Linux) via thekeyringcrate; onlyclient_id,token_endpoint, andscopesremain on disk. Legacy 0600 JSON files containingclient_secretare transparently migrated into the keystore on first read. - Typed
hooks.jsonschema (sync/apply/hooks_schema.rs).HooksFile/HookEntry/HookKindreplace the priorserde_json::json!literal insync/apply/hooks.rs::write_hooks_json. fsutilmodule. Single owner ofatomic_write_0600(parent dir 0o700, fsync before rename),copy_dir_recursive, andread_optional. Removes three duplicate implementations acrossauth/,sync/, andintegration/.mcp_registry(top-level). Cross-cutting registry consumed byproxy::forward,install::mdm::*, andsync::apply— relocated fromproxy::mcp_serversbecauseproxy::mis-suggested ownership.
Changed
gateway/split.gateway/mod.rs(489 → 79 lines) intomod(client) +errors+types+fetch+auth.integration/cowork_plugins/emit.rssplit (411 → 245 lines) intoemit(publish/unpublish orchestration) +upsert(registry/settings file plumbing). Visibility narrowed:mod {emit, marketplace, registry, settings}are nowpub(crate); onlyKNOWN_MARKETPLACES_FILE,publish,resolve_target,unpublish, and the test surface staypub.install/mod.rssplit (313 → 170 lines) by extracting orchestration glue (bootstrap_install,run_apply*,resolve_*) toinstall/apply.rs.sync/apply/plugin.rssplit (322 → 184 lines) by movingmaterialize_hook_token,write_hooks_json, andensure_plugin_json_hooks_fieldtosync/apply/hooks.rs.sync/apply/mod.rs::rewrite_loopback_urlsusesurl::Url::set_host/set_schemeagainstHost::Ipv4/Host::Ipv6loopback checks instead of string-splitting helpers (split_url,split_origin,is_loopback_hostdeleted).SignedManifestfamily moved to shared crate.SignedManifest,UserInfo,PluginEntry,PluginFile,SkillEntry,AgentEntry,ManagedMcpServer,ManifestVersion, plus the manifest-scoped typed IDs (PluginId,SkillId,Sha256Digest,ManifestSignature,ToolPolicy, etc.) now live insystemprompt_models::bridge::*. Bridge re-exports preserve every existing call site; the bridge-side ed25519verify(...)is provided via the newSignedManifestVerifyextension trait (orphan-rule workaround).
Fixed
-
Proxy:
/healthzand/otelno longer rejected by the loopback-secret gate (proxy/server.rs). The healthz short-circuit ran after the bearer check, and only matchedGET, so the bridge's ownHEAD /healthzprobe (and any external poller) flooded the activity log with403 (bad secret; presented_fp=<empty>)every 30 s. Codex's OTLP-HTTP exporter posting to/otelhit the same gate — OTLP has no clean way to inject the loopback bearer. Both paths are now handled by an explicitis_unauthenticated_path(method, path)predicate evaluated after the loopback-host check and before the bearer check:GET/HEAD /healthzshort-circuits in-process;POST /otel(and/otel/*) forwards throughforward::forward, which already strips the inboundAuthorizationand injects the upstream bearer fromTokenCache. Loopback-origin enforcement is unchanged. Same change folds the response building into a single sharedforward_to_gatewayhelper, collapseshealth_responseto two lines viasimple_response(""), replaces the unreachableResponse::builder().unwrap_or_elsefallback with infallibleResponse::new+HeaderValue::from_static, fixes a multi-strip bug in the bearer parser (trim_start_matches("Bearer ")could strip repeats; nowstrip_prefixonce), and tightenssha256_8andrecord_stats(callers now pass an already-computedlatency_msinstead ofInstant). -
///rustdoc and TODO/FIXME flags purged from binary modules (bin/bridge/**is a binary —///is banned). ~50 paraphrase blocks removed; ~20 load-bearing why-lines preserved as//. Theobs.rspanic-hook ordering note is retained as a// Why:comment; thegui/server.rsfocus-IPC FIXME was reworded as a deliberate-trade-off explanation (TCP+CSRF works identically across all three platforms in <100 lines). -
Breaking —
coworkrename completed end-to-end. Bridge sends canonicalx-session-id/x-context-idheaders (issued from the newSessionContext) and uses the renamed gateway routes (/v1/bridge/*,/v1/auth/bridge/*). Internal macros are nowbridge_define_id!/bridge_define_token!. Env vars:SP_COWORK_*→SP_BRIDGE_*. Config file:~/.config/systemprompt/systemprompt-cowork.toml→systemprompt-bridge.toml. A0.7.xbridge cannot talk to a0.8.0gateway and vice versa.
Added
- Heartbeat loop (
proxy/heartbeat.rs). Spawned next to the token-refresh loop inproxy/server.rs::start; POSTs/v1/bridge/heartbeatevery 30 s withsession_id,bridge_version, OS, hostname,last_activity_at, and a snapshot ofProxyStats(forwarded count, tokens in/out). The gateway records the row inbridge_sessions, making this bridge visible tosystemprompt admin bridge listeven between inference requests. On401the token cache invalidates so the next tick re-authenticates. SessionContext::touch_activity()is called on every successful messages-path forward, so the heartbeat distinguishes "alive but idle" from "alive and serving traffic".- Bridge sends canonical
x-session-idand content-derivedx-context-idheaders on every/v1/messagesforward, enforcing conversation grouping at the gateway.
Fixed
- Tech-debt sweep on the per-agent enabled feature.
auth::setup::clean()now also removes~/.config/systemprompt/agents.json. Previously acleanleft stale enabled state behind.- Existing users get a one-shot migration on first run after upgrade: when no
agents.jsonexists yet,gui::run_agents_migration_if_neededprobes every registered host and auto-enables those whoseprofile_stateis alreadyinstalled. The old "everything is silently disabled" behaviour after upgrade is gone. apply_host_snapshotno-ops (and removes any existing entry) when the host has been disabled mid-probe, so an in-flight probe that finishes after a disable can no longer re-insert the host intostate.hosts.agents.setEnabledis now idempotent: setting the same value twice returns{ changed: false }and skips both the activity-log line and the wasted manual probe.- Setup-wizard "Install profile" handler now records which step failed (
enable/generate/install) on the button'sdata-failed-stageand surfaces the underlying error message intitle, so partial failures stop being silent. proxy_probe::probedoes an actual HTTPHEAD /healthzafter the TCP connect and reports the status onProxyHealth.http_status, so a stray process listening on port 48217 no longer claimsListeningfor the bridge proxy.- Renamed
GatewayClient::fetch_cowork_profile→fetch_bridge_profileto finish the cowork→bridge rename on the bridge side. Server endpoint path andCoworkProfiletype are unchanged (server contract). - Moved
agents_statefromgui/to a top-level module so non-GUI builds (auth::setup::clean) can reference it withoutcfg-gates.
- Setup wizard's Install button was a no-op. It only called
host.profile.generate, which writes a profile file but does not copy it into the OS-managed location, so the host'sprofile_statenever flipped toinstalledand the UI stayed on "Install profile" forever. Now the setup-agents handler enables the host (so the new gating doesn't reject the call), generates the profile, and immediately installs the resulting path — three IPCs in sequence — matching the user's intent of "set up this agent". - Local proxy probe always returned
Unconfigureduntil at least one host had been installed, becauseAppState::first_configured_proxy_urlderived the probe target fromhost.profile_keys.inferenceGatewayBaseUrl(which only populates after install). The bridge owns the proxy and knows its port — nowfirst_configured_proxy_urlreturnshttp://127.0.0.1:<proxy.handle.port>when the proxy is running, falling back to the host-derived URL only if the proxy hasn't started yet. Cures the "awaiting first launch" badge that stuck even when Claude was actively routing through the proxy.
Added
- Per-agent enable/disable, persisted across runs. Every registered host (Claude Desktop, Codex CLI, …) now has an explicit
enabledflag stored in~/.config/systemprompt/agents.json. Hosts default to disabled so a fresh install never silently probes integrations the user hasn't opted into. New IPCagents.setEnabled({ hostId, enabled })toggles the flag, persists it, and (when re-enabling) fires a one-shot manual probe. The host card grows an Enable/Disable button; disabled cards render as a dimmed lede with the toggle and no action buttons.host.probe,host.profile.generate,host.profile.install,agent.uninstall, andagent.openConfigreject disabled hosts withConflict. Status summaries and the rail's agent count consider only enabled hosts.
Fixed
-
Codex (and every other registered host) was probed every 30 s even when not installed, spamming the activity drawer with
[codex-cli] re-verifying profile and process/re-verify complete — profile not installed, process not runningpairs forever. Two changes: (1) the periodic ticker (gui/hosts/tick.rs) now skips hosts that aren'tenabled, and (2) tick-driven probes are silent in the activity log unless the snapshot'sprofile_statekind orhost_runningactually flipped, in which case a single[host] state changed — …line is appended. User-triggered Re-verify clicks keep the existing verbose[host] re-verifying…/re-verify complete — …pair via a newProbeCause::{Tick,Manual}enum threaded throughHostUiEvent::Probe{Requested,Finished}. -
Sync failed with no visible reason — only the literal string
sync-failurein the activity drawer.i18n::t_args("sync-failure", ...)andi18n::t("sync-cancelled")had no matching keys inweb/i18n/en-US/bridge.ftl, so the fallback returned the bare key and the underlying error string was discarded. Addedsync-failure = Sync failed: { $error }andsync-cancelled = Sync cancelled., switched the error formatter ingui/handlers/sync.rsto{:#}to print the chain, and addedtracing::info!/tracing::error!so the same message lands in the log file as well as the UI. -
Sync hard-failed on a redundant directory-level hash check. After every per-file SHA-256 (signed by the gateway) was verified,
sync/apply/plugin.rsre-hashed the staging directory withdirectory_hashand compared againstplugin.sha256. The bridge's hash algorithm did not match the gateway's, so the staged-vs-manifest comparison always failed (hash mismatch for plugin enterprise-demo: expected …6930, got …495a), aborting the entire sync. Removed the directory-level check and the now-deaddirectory_hash/collect_fileshelpers fromsync/hash.rs. Per-file hash verification on a fresh staging dir already guarantees byte-identical contents from the signed manifest. -
External link clicks could fail silently.
gui/window/mod.rs::open_targetdiscarded theCommand::spawnresult, so whenxdg-open/cmd /C startwas missing or failed there was no record. Now logs the attempt at info level and the spawn error at error level. -
Footer links now open via an explicit IPC instead of
target="_blank". Added anopenExternalUrlIPC command ingui/command.rs(HTTPS-only allowlist viais_safe_external_url, dispatches through theopenercrate) and exposed it on the JS side asbridge.openExternalUrl(url).sp-footernow handles the docs/licensing clicks through adata-action="open-external"delegate that calls the IPC, so the path no longer depends on the WebView'swith_new_window_req_handlerfiring. -
Footer rendered
v0.7.0 (unknown, unknown)whenvergencould not read git state.hasBuildMetaonly suppressed the literal string"unknown". AddedisMissing()to also catch empty values and unreplaced__PLACEHOLDER__sentinels, so the parens block disappears when build metadata is missing instead of leaking the fallbacks into the UI. -
Help & Support section was poorly styled — buttons stretched to the drawer's right border with no breathing room. Restyled
.sp-activity__helpinweb/css/drawer.cssas a self-contained card: outer margin so it no longer touches the drawer borders, panel background and rounded border for separation, larger gap between title and buttons, and constrained.sp-btn-ghostwidth with left-aligned labels and consistent vertical rhythm. -
Windows GUI rendered a blank
about:blankwindow. wry 0.55 rewrites custom URI schemes tohttp://<scheme>.<host>/...on Windows/Android because WebView2 cannot register arbitrary schemes, so navigating tosp://app/index.htmlsilently failed. Usehttp://sp.app/index.htmlon those targets and allow the rewritten origin inallow_navigation. -
Native menu bar showed raw i18n keys (
menu-edit,menu-view,menu-help, …). The menu builder callsi18n::t("menu-*")butweb/i18n/en-US/bridge.ftlhad no matching entries, so the fallback returned the keys verbatim. Added the seven missing translations. -
Re-verify button looked broken — actually silent. Clicking "Re-verify" on a host card fired
host.probe, ran the probe, applied the snapshot, and emittedhost.changed, but appended nothing to the activity log. From the user's seat it looked like the click was lost. Added "[host] re-verifying…" before the spawn and "[host] re-verify complete — profile installed, process running" (or equivalent) when the snapshot is applied. -
Bridge silently continued when the local proxy failed to start.
gui::rundiscarded the result ofproxy::start_default()and proceeded to render the GUI even when the bind failed, so any profile generated afterwards pointed Claude Desktop / Codex at a dead127.0.0.1:48217(ERR_CONNECTION_REFUSED). Now: log success/failure to the activity drawer at startup, and refuse profile generation when the proxy isn't listening rather than handing out a profile that can't possibly work. -
Loopback secret could drift between proxy and host profiles. Both proxy startup and profile generation called
secret::load_or_mint, which silently re-minted on a missing key file. If the file disappeared between the proxy's startup read and a later profile-gen read (or vice-versa), the proxy and the host's installed profile ended up with different keys, producingforbidden: bad loopback secret(HTTP 403) on every host request. Replaced the dual API with a single source of truth:proxy_init()(proxy startup — loads or mints, caches in a process-globalOnceLock) andfor_profile()(profile generation and MDM templates — read-only; errors out if the proxy hasn't started). After this change the proxy and any profile generated within the same process can never disagree. -
Removed broken
__TOKEN__cache-buster. Every static asset URL carried?t=__TOKEN__, butassets.rssubstituted__TOKEN__with the empty string — leaving meaningless?t=query strings that did nothing AND created two distinct module identities whenever one file importedfoo.js?t=and another importedfoo.js. Modules ran twice, the secondcustomElements.definethrewNotSupportedError, and the GUI rendered an empty body. Stripped the placeholder fromweb/index.html,web/css/fonts.css, all 27 imports inweb/js/index.js, and the three.replace("__TOKEN__", "")call sites insrc/gui/assets.rs. Embedded assets only change on rebuild, so no cache-buster was ever needed.
Changed
- Bridge frontend rewritten off Lit.js — pure vanilla Web Components. All 22
sp-*components migrated fromLitElementto a 110-lineSpElementbase (web/js/components/sp-element.js) with reactive setters, microtask-batched re-render, anddata-action/data-inputevent delegation.vendor/lit-all.min.jsdeleted.js/atoms.jsdeleted (unused by components — bridge state subscription is the single source of truth). - State path unified. Components subscribe to
bridge.subscribe('state.changed', ...), mutate reactive setters, and re-render.hydrateAtomsremoved fromindex.js. The four parallel communication patterns (bridge sub, atoms, custom events, Lit reactive props) collapse to one. - Centralized event registry at
web/js/events/bridge-events.jsowns alldocument.addEventListenercalls (keydown, mkt:count, crumb:set, setup-open).theme.jsmodule-scope listeners wrapped ininitTheme(). Components subscribe viaonBridgeEvent(name, fn)instead of registering their own document listeners. - Oversized components split.
sp-setup-gateway211→117 lines (form rendering extracted toutils/gateway.js::renderGatewayForm),sp-marketplace161→138 (listing fetch logic moved toservices/marketplace-service.js),sp-cloud-status161→127,sp-rail160→119 (tab definitions extracted toutils/rail-tabs.js). Every JS file ≤150 lines, every CSS file ≤200. - Toast styles tokenised. Hardcoded hex (
#2a1a1a,#d97757, …) and px (20px,12px, …) inmain.cssreplaced with new--sp-toast-bg,--sp-toast-bg-error,--sp-toast-border,--sp-toast-fg,--sp-toast-shadow,--sp-radius-md,--sp-z-toasttokens. Toast block extracted toweb/css/toast.css. - Empty
.catch(() => {})handlers replaced with.catch((e) => console.warn("snapshot failed", e))across 19 component snapshot calls — visible failure logging instead of silent swallowing. assets.rsregistry updated to dropLIT_VENDOR,atoms,components/baseand registercomponents/sp-element,events/bridge-events,services/marketplace-service,utils/rail-tabs,utils/gateway,css/toast. The/assets/js/vendor/lit-all.jsroute removed.i18n.jsleading comment block deleted (4 lines).log-virtual.jsswitched fromfrag.appendChild(li)tofrag.append(li).
Fixed
- Clippy cleanup — zero warnings on
x86_64-pc-windows-gnuand host targets under-D warnings.- Removed dead
GuiApp.cancel: CancellationTokenfield; cancellation is owned byAppState.cancelsand per-handler tokens. - Collapsed 32 nested
if letblocks into stablelet_chains(autofix). - Switched four
needless_pass_by_valuesites to borrow:ipc_runtime::handle_inbound(&str),ipc_runtime::emit_sync_progress(Option<&str>),SettingsWindow::create(&EventLoopProxy, Option<&str>). - Removed unjustified
#[allow]attributes:clippy::unused_selfonInstallError::exit_code— replaced withInstallError::EXIT_CODEassociated constant.clippy::vec_init_then_push+unused_mutinintegration::registry— refactored to cfg-gated const slices chained into the registry vec.
- Audited remaining
#[allow]s — kept only well-justified FFI (unsafe_code), logger-bootstrap fallback (print_stderrinobs.rs), CLI entry-point output, project-wide stylistic opts inlib.rs,#[cfg(test)]scopes, and cross-platform signature parity (unnecessary_wrapson Linuxorg_plugins_system).
- Removed dead
Added
- Phase 3 frontend rewrite — full migration from HTTP polling + delegated dispatcher to Lit components + IPC channels. Every legacy panel under
web/js/is now ansp-*custom element extendingBridgeElement, hydrated fromstate.snapshotand refreshed by the appropriate channel (state.changed,host.changed,proxy.changed,proxy.stats,sync.progress,error,log).- 23 new Lit components in
bin/bridge/web/js/components/:- Stateless info panels (Phase 3a):
sp-proxy-status,sp-agent-presence,sp-agents-summary,sp-overall-badge,sp-sync-pill,sp-rail-profile,sp-footer,sp-crumb. - Interactive panels (Phase 3b):
sp-rail(replacestabs.js+rail-indicator.js, owns ⌘1–⌘4 and ⌘F shortcuts, persistscowork.tabtolocalStorage, broadcastscrumb:set),sp-toast,sp-activity-log,sp-host-card,sp-hosts-list,sp-settings. - Marketplace + setup wizards (Phase 3c):
sp-marketplace,sp-marketplace-list,sp-marketplace-detail,sp-setup,sp-setup-gateway,sp-setup-agents. - All components use light DOM (
createRenderRoot() { return this; }) so existing CSS class selectors apply unchanged.
- Stateless info panels (Phase 3a):
- Incremental host updates —
sp-hosts-listkeeps aMap<id, host>and merges per-host deltas from thehost.changedchannel without re-fetching the full snapshot.sp-agent-presence,sp-agents-summary, andsp-setup-agentslikewise merge per-host payloads in place. bridge.jsshims added:openLogFolder,diagnosticsExportBundle,diagnosticsInfo.setup-opencross-component event letssp-settingsreopen the setup wizard.crumb:setCustomEventdecouples breadcrumb updates fromtabs.js.mkt:countCustomEventletssp-marketplacepush the marketplace total intosp-railwithout a shared atom.
- 23 new Lit components in
Changed
- HTTP control-plane server cut to single-instance focus only.
gui::server::Serverreduced from a full HTTP router (state polling, log polling, marketplace listing, action dispatch, asset serving) to ~85 lines that handle exclusivelyPOST /api/focus_windowwith constant-time CSRF check. The webview already loads via thesp://app/custom protocol (window/native.rs::serve_custom_asset), so no asset serving needs the HTTP path. Second-launch instances still ping the focus endpoint viasingle_instance::ping_focus_running_instance. assets::lookup_pathno longer takes a CSRF-token argument — thesp://protocol bypasses it.__TOKEN__placeholders in CSS/JS modules are substituted with empty strings.last_action_messageremoved fromAppStateSnapshot,AppStateSnapshotBuilder, andStatePayload.AppState::set_messagedeleted along with all 8 call sites inhandlers/sync.rsandhandlers/auth.rs. Toast surfacing now flows exclusively through theerrorIPC channel (ipc_runtime::emit_error), which is structured ({scope, code, message}) rather than a free-form snapshot field.sp-toastsimplified to listen toerroronly;sp-setup-gatewaystops parsinglast_action_messagefor failure detection.- Marketplace install/uninstall buttons removed from
sp-marketplace-detail. Cloud sync (sync::run_once) is the install mechanism — signed manifests pulled from the gateway materialize plugins/skills/hooks/agents intoorg_plugins_effective(). Per-item buttons were redundant with sync. Dropped:marketplace.install/marketplace.uninstallIPC commands, theMarketplaceItemArgsstruct, and thebridge.marketplaceInstall/marketplaceUninstallshims. tabs.jsdecoupled fromcrumb.js:activateTabnow dispatchesdocument.dispatchEvent(new CustomEvent("crumb:set", { detail: { name } }))instead of importingsetCrumb. (Then both files were deleted entirely assp-railandsp-crumbtook over.)web/index.htmlreduced from ~485 to ~120 lines. Wholesale markup blocks for the rail nav, marketplace tab (categories list + items + detail + actions footer), agents tab, host-card<template>, settings panel, activity drawer, setup wizard, and footer all replaced with single<sp-*>tags that own their own rendering.web/js/index.jsreduced from 64 to 43 lines. No moreapplySnapshot,subscribePolling,subscribeLog, orinitEvents/initKeyboard/initTabs/initSetup/initMarketplace/initToast. Final form: theme + i18n init, side-effect imports for every component, atom hydration fromstate.changed.gui/command.rs: addedopenLogFolderas an alias fordiagnostics.openLogDirectory.
Removed
- Phase 4 — orphaned
http_localmodule deleted.bin/bridge/src/http_local/(mod, request, response, hop_by_hop) had zero remaining callers after Phase 3 cut the HTTP control plane.pub mod http_local;removed fromlib.rs. - Phase 4 dead-code cleanup:
Server::csrf_tokenfield inlined into the listener thread (the cloned token is sufficient);#[allow(dead_code)]markers removed fromgui/server.rs::Serverandgui/menu.rs::MenuBarHandles.ErrorScope::Setupvariant dropped — no remaining call sites. - 18 legacy frontend modules deleted:
agents.js,api.js,crumb.js,dom.js,drawer.js,events/keyboard.js,events/registry.js,footer.js,hosts.js,hosts/card.js,marketplace.js,marketplace/detail.js,marketplace/glyph.js,marketplace/list.js,marketplace/state.js,overall-badge.js,profile.js,proxy.js,rail-indicator.js,setup.js,setup/agents.js,setup/gateway.js,setup/mode.js,state.js,sync-pill.js,tabs.js. Subdirectoriesevents/,hosts/,marketplace/,setup/removed. - 2 backend Rust modules deleted:
gui/connection.rs(HTTP request parsing + CSRF validation + GET routing),gui/action_dispatch.rs(POST/api/<action>→UiEvent). gui/server_util.rstrimmed —parse_queryandnow_unixremoved; onlymint_csrf_tokenandconstant_time_eqremain.server_json::snapshot_to_jsonremoved (was used only by the deleted HTTP server).http_local-based connection handling,last_action_messagefield,set_messagesetter, builder methodwith_last_action_message, thelast_action_messagepayload field, and thecsrf_tokenquery-parameter validation on asset URLs.
Notes
- Single-instance focus continues to use a 127.0.0.1 TCP listener (loopback + CSRF). A FIXME in
gui/server.rstracks the future migration to Unix domain sockets / Windows named pipes. - The
sp://app/custom-protocol asset path remains the only way the webview loads HTML/CSS/JS; thelit-all.min.jsvendor bundle is served as-is and special-cased to skip__TOKEN__substitution. marketplace.listIPC command and listing payload retained — it surfaces what's already been synced to disk bysync::run_once. There is no separate "catalog vs installed" model.- Single-instance focus across platforms continues to work via the trimmed HTTP server.
Earlier in this Unreleased window
-
Phase 3 follow-ups (3F.A / 3F.B / 3F.C):
- Cross-platform menu bar —
gui::menu::attach_to_window(&MenuBarHandles, &Window)on Windows extracts the HWND viaraw-window-handleand calls mudainit_for_hwnd, attached after settings-window creation. macOS continues to use app-wideinit_for_nsapp. New direct dep onraw-window-handle = "0.6"for the Windows target. Native menu items now go throughi18n::t. - Cancellation plumbing + UI —
AppState::install_cancel/clear_cancel/cancel_scope/cancel_allkeyed by a newCancelScopeenum (Sync,Login,GatewayProbe).sync,login,set-gateway,logout, andgateway_probehandlers now wrap theirspawn_blockingfutures intokio::select!against a child token; on cancel the result is dropped and a sensible failure outcome is emitted.on_sync_finisheddistinguishescancelledfromfailedand emits acancelledsync.progressphase. NewUiEvent::CancelInFlight { scope, reply_to }+gui/handlers/cancel.rs. New IPC commandcancel(scopesync|login|gateway|all) +bridge.cancel(scope)JS helper. New Cancel button (#sync-cancel) in the sync pill, hidden by default, shown whensync_in_flight, wired tobridge.cancel("sync"). - Full i18n hydration —
web/i18n/en-US/bridge.ftlexpanded from ~30 to ~140 keys grouped by surface (setup-, sync-, login-, gateway-, validate-, marketplace-, agents-, status-, settings-, activity-, footer-, nav-, menu-, host-, proxy-).data-l10n-idadded to every visible static string inweb/index.html;web/js/i18n.jsextended to also hydratedata-l10n-placeholderanddata-l10n-ariaattributes. JS modules now route everytextContent =literal throught()/t_args:marketplace.js,marketplace/detail.js,marketplace/glyph.js,hosts.js,hosts/card.js,agents.js,proxy.js,setup/agents.js,setup/gateway.js,setup/mode.js,sync-pill.js. Rust handler messages (auth.rs,sync.rs,validate.rs) now usei18n::t/i18n::t_argsfor log lines and bridge errors. Translators can drop aweb/i18n/<locale>/bridge.ftlfile and the entire UI switches over.
- Cross-platform menu bar —
-
In-progress concurrent work staged alongside Phase 2 observability: i18n module + web translation assets, native menu, system process helpers, ipc runtime split, lit-based web components (
atoms,bridge,theme,components/), tokio-runtime handler refactor (app.runtimereplacingapp.pool.spawn_task), proxy/gateway/hosts/integration tweaks. Note: cross-target Windows/macOS build is currently broken in this snapshot pending the GuiAppruntimefield landing. -
Phase 2 observability: support-grade diagnostics surface.
- Daily log rotation via
tracing-appender(max 7 files, non-blocking writer). bridge diagnosticsandbridge --versionsubcommands print version, git SHA, build timestamp, profile, log/config paths.vergenbuild script embedsVERGEN_GIT_SHA,VERGEN_GIT_COMMIT_DATE,VERGEN_BUILD_TIMESTAMP,VERGEN_GIT_BRANCH.- Footer renders
vX.Y.Z (sha, date)alongside the version pill. - Panic hook writes
bridge-crash-{utc-ts}.logwith payload, location, and backtrace; emits atracing::error!event before abort. - Persistent activity log: JSONL writer subscribed to the activity emit hook, atomic byte counter, single rollover at 10 MB to
activity.jsonl.1. - GUI Help & Support drawer panel: "Open log folder" and "Export diagnostic bundle" actions. Bundle zips bridge logs, activity JSONL (+ rolled), crash dumps, redacted config TOML, and
diagnostics.txt; lands on Desktop and reveals in the OS file manager. - HTTP routes
/api/diagnostics/open_log_dir,/api/diagnostics/export_bundle,/api/focus_window. IPC commandsdiagnostics.openLogDirectory,diagnostics.exportBundle,diagnostics.info. - INFO-level
gui_dispatchspan withevent_kindand per-dispatchrequest_id(UUID v4); user-initiated handler entry points promoted from DEBUG → INFO. - Single-instance:
bridge.lock.jsonsidecar persists{pid, port, token}; second launch pings/api/focus_windowon the running instance (250 ms timeout) instead of silent-exiting. config::redaction::redacted_config()walks the loaded TOML and replaces values under sensitive keys (secret,credential,auth,pat,token,password,key,pubkey,session) with***REDACTED***.
- Daily log rotation via
-
New deps:
tracing-appender,backtrace,opener(withreveal),zip,uuid,serde_yaml. Build dep:vergen.
Changed
-
ActivityLog::set_emit_hook→add_emit_hook(now multi-subscriberVec<EmitHook>); existing IPC subscriber and the new persistent JSONL writer coexist. -
obs::tracing_initno longer threads file writes through a staticMutex<File>; uses aNonBlockingrolling appender behind aOnceLock<WorkerGuard>. -
Setup welcome page: drop redundant brand-mark icon from topbar (wordmark only); replace setup-card icon chip with the full systemprompt.io wordmark; hide topbar and footer entirely while in setup mode.
-
Primary button (
.sp-btn-primary) restyled with branded asymmetric corners (--sp-corners-sm) and a stable label — removedtransform: scale()andtranslateYso text size and position no longer shift on hover. Added an icon slot: gray default icon swaps to a rotating spinner via[aria-busy="true"]. -
Connect,Finish, andOpen systemprompt bridgebuttons restructured with<span class="sp-btn__icon">+<span class="sp-btn__label">.js/setup/gateway.jsnow toggles only the label text on busy, preserving the icon nodes. -
Inputs aligned to
--sp-corners-smso form fields share the branded corner profile with buttons and cards.
[0.7.0] - 2026-04-30
Added
integration::codex_cli— Codex CLI host integration (probe, config, install).cli::credential_helper— credential helper command surface.gui::handlers::agents— GUI handler module for agents.web/css/agents.css— agent presence cluster, setup-step machine, agents-list-empty, host-card kind chip.web/js/agents.js—renderAgentPresence,renderAgentsSummary,renderAgentsRailCount.web/js/events/registry.js— single document-level click registry dispatching[data-action].web/js/events/keyboard.js— single keydown listener for ⌘1/2/3.web/js/state.js,index.js,rail-indicator.js,crumb.js,sync-pill.js,profile.js,cloud.js,proxy.js,hosts.js,overall-badge.js,footer.js,marketplace/{detail,glyph,list,state}.js,drawer.js.
Changed
- Breaking: crate renamed from
bin/coworktobin/bridge(binary namesystemprompt-bridge). Workspaceexcludeand tests updated. gui::connection,gui::dispatch,gui::events,gui::hosts,gui::server_json,gui::state,gui::mod— refactored alongside new agents handler and Codex CLI integration.- GUI assets now serve as 22 modular CSS files and 24 JS ES modules from
/assets/css/*and/assets/js/*instead of inlined intoindex.htmlvia__STYLE__/__SCRIPT__. Each file isinclude_str!-bundled, served with?t=<csrf>token guard, and substituted with the per-request token. web/style.css(1572 lines, monolithic) split into 22 component files underweb/css/(tokens,fonts,reset,kbd,dot,badge,button,topbar,rail,shell,drawer,marketplace-{base,list,detail},status,settings,setup,agents,log,footer,responsive,main). All custom-property references use the--sp-*prefix.web/js/snapshot.jsandweb/js/marketplace.js(monolithic) replaced by 24 ES modules with named exports only. Single event registry,data-actiondelegation,<template>cloning, noinnerHTMLof multi-element strings, no early returns.
Removed
web/style.css— split into per-component files.web/js/snapshot.js,web/js/main.js,web/js/activity.js— carved into the new modules.STYLEconstant,style_concat(),__STYLE__substitution, and__SCRIPT__substitution ingui::connection.
[0.6.0] - 2026-04-30
Added
activity::ActivityLogring buffer (1000 entries) capturing live proxy/sync events for the GUI activity feed.proxy::usageresponse-stream tap:is_messages_path,wrap_response_stream. Counts/v1/messagescalls and sums input/output tokens from JSON and SSE bodies.ProxyStats::messages_total,tokens_in_total,tokens_out_totalcounters.sync::apply::synthetic_pluginwriter: managed skills, agents, and.mcp.jsonare now materialised as a single synthetic Claude plugin (systemprompt-managed) under the org plugins root, instead of separate fragments under.systemprompt-bridge/.paths::SYNTHETIC_PLUGIN_NAMEconstant (systemprompt-managed).ApplyError::ReservedPluginId— manifests containing a plugin with the reserved synthetic-plugin id are rejected.- GUI: split monolithic
web/app.jsinto ES modules underweb/js/(main,api,dom,tabs,setup,marketplace,activity,snapshot). - GUI:
assets/fonts/bundled fonts and an activity tab driven by the activity log.
Changed
- Breaking: managed assets layout. Skills, agents, and managed MCP servers no longer live under
.systemprompt-bridge/{skills,agents,managed-mcp.json}; they are written into the synthetic plugin directory<org-plugins>/systemprompt-managed/{skills,agents,.mcp.json}.installsummary,status, and GUI counters now read from the new location. install --uninstallremoves the synthetic plugin directory in addition to the metadata directory.- Plugin sync no longer prunes the synthetic plugin as a stale entry.
- Malformed-plugin counter accepts both
.claude-plugin/plugin.jsonandclaude-plugin/plugin.json, and excludes the synthetic plugin. - Proxy
forwardnow takesArc<ProxyStats>and wraps successful/v1/messagesresponses with the usage tap; counters update on the fly. - Proxy request handler appends every forwarded request (and client-disconnect / forward errors) to the activity log.
Removed
- Breaking:
paths::MANAGED_MCP_FRAGMENT,paths::SKILLS_DIR,paths::AGENTS_DIRconstants. - Breaking:
sync::apply::{agent, mcp, skill}modules. Replaced bysynthetic_plugin. gui::state::counters::read_index_count(the old skills/agentsindex.jsonreader).- Legacy
bin/cowork/web/app.js; replaced by ES modules underweb/js/.
[0.5.0] - 2026-04-29
Added
auth::ChainErrorenum (NoneSucceeded,PreferredTransient { provider, source }).auth::providers::AuthFailedSource::is_terminal()distinguishing permanent failures (PubkeyMissing,UnsafePath, decode errors,Serialize) from transient network failures.auth::evaluate_chain()— chain evaluator accepting an explicit provider list and preferred-provider hint.- Exit code
10oncli runandcli whoamifor a transient failure on the configured preferred provider (distinct from5for "no credential source succeeded").
Changed
- Breaking:
auth::acquire_bearerandauth::mint_freshreturnResult<HelperOutput, ChainError>(previouslyOption<HelperOutput>). - Breaking:
UiEvent::{SyncFinished, LoginFinished, LogoutFinished, SetGatewayFinished}andHostUiEvent::{ProfileGenerateFinished, ProfileInstallFinished}payloads now carryArc<GuiError>instead ofGuiError. - Breaking:
gateway::GatewayClientrequest timeout reduced from 30 s to 10 s. - Preferred mtls provider with a transient gateway failure no longer silently falls through to PAT.
Removed
- Breaking:
GuiError::Msgvariant and the manualCloneimpl onGuiError. - Breaking:
http_local::request::parse(&mut TcpStream). Useparse_from_read(anyRead) orparse_buffered(anyBufRead). - All inline (
//) and doc (///) comments underbin/cowork/src/. - Unused
CODE_DOMAINconstant inintegration::claude_desktop::shared.
Fixed
- Proxy dropped HTTP/1.1 trailers as silent empty data frames; non-data frames are now filtered out before the upstream body is forwarded.
- Proxy
io::Errorboundary preserves the source chain instead of stringifying viato_string(). - Tokio runtime initialiser returns
io::Erroron theOnceLockrace instead ofprocess::abort. - Proxy listener binds IPv4 loopback (
127.0.0.1) first and falls back to IPv6 loopback (::1); previously bound dual-stack[::]:port, exposing the proxy to non-loopback peers on hosts whereIPV6_V6ONLYwas off. - Windows Claude Desktop profile generator emits
inferenceModelsasREG_MULTI_SZ(hex(7):-encoded UTF-16LE) instead of a comma-joinedREG_SZ. auth::cache::writeandproxy::secret::load_or_mintlog atracing::warn!whenchmod 0600fails on the cached file, instead of swallowing the error.
[0.4.0] - 2026-04-27
Added
- Native GUI on Windows and macOS;
guisubcommand launches a branded settings window (gateway URL, PAT input, cached-JWT state, marketplace counters, plugins-directory path, last-sync timestamp, activity log). - Default routing falls through to
guiwhen launched without an attached terminal; terminal invocations continue to emit the JWT envelope to stdout. - Tray menu items: Sync now, Validate, Open settings, Open config folder, Quit.
sync::run_oncereturns a structuredSyncSummary/SyncError;validate::runreturns a structuredValidationReport.
Changed
- Linux
guiexits64withgui not supported on this platform.
[0.3.3] - 2026-04-23
Changed
- Release-only bump; no code changes vs 0.3.2.
[0.3.2] - 2026-04-23
Added
install --applyon macOS direct-writes/Library/Managed Preferences/com.anthropic.claudefordesktop.plistand restartscfprefsd(single sudo prompt, no MDM required).install --apply-mobileconfigbuilds a.mobileconfigand opens System Settings → Profiles for approval (MDM workflow).uninstallremoves both managed-prefs plists and kickscfprefsd.
Removed
profiles install/profiles removeinvocations (deprecated by Apple on macOS 11+).
Fixed
- Reject
http://for non-loopback gateways at install time.
[0.3.1] - 2026-04-23
Notes
- Superseded by 0.3.2; did not ship.
[0.3.0] - 2026-04-22
Added
whoamisubcommand prints authenticated identity from the gateway.syncmaterialisesuser.json,skills/<id>/{metadata.json, SKILL.md},agents/<name>.jsonunder.systemprompt-bridge/.statussurfaces identity and skill/agent counts from on-disk fragments.
Changed
- Breaking: signed-manifest wire format extended with
user,skills,agents.AgentEntry.card: objectreplaced withsystem_prompt: string?. 0.2.x clients cannot deserialise 0.3.x manifests. - Manifest signing primitive moved to
systemprompt-security::manifest_signing(signature semantics unchanged). - Per-user manifest assembly relocated from the gateway into the template admin extension.
[0.2.0] - 2026-04-22
Added
ed25519-dalekdependency for signed-manifest verification.- Plugin / MCP sync against Cowork's
org-plugins/mount.
Changed
- Breaking: crate renamed to
systemprompt-bridge(binarysystemprompt-bridge, libsystemprompt_bridge). - Manual release via
cargo-zigbuild+gh release createon tagcowork-v*(Linux x86_64 + Windows x86_64 binaries).
[0.1.0] - unreleased
Added
- Initial scaffold: JSON wire contract, cache, blocking HTTP client, platform keystore trait (macOS/Windows/Linux stubs), SSO assertion fetch, stdout JSON emission.