Skip to content

Latest commit

 

History

History
2075 lines (1381 loc) · 109 KB

File metadata and controls

2075 lines (1381 loc) · 109 KB

Changelog

All notable changes to Tandem Browser will be documented in this file.

[v0.75.0] - 2026-04-18

Large release driven by an external security audit (#34) by @samantha-gb and follow-up work that surfaced while implementing her findings. Five High-tier security fixes plus CORS and agent-initiated override hardening landed alongside a proper per-user behavioural learning pipeline and audio/visual escalation for agent-blocked approvals.

Twelve merged PRs. Grouped below by area.

Security — audit #34 findings addressed

  • Scheme + private-IP guard on /navigate and /headless/open (High-2) — new isSafeNavigationUrl() helper rejects non-http(s) schemes (file://, javascript:, data:, …) and RFC1918 / link-local / cloud-metadata IP literals, including IPv6 edge cases. (#159)
  • Form-memory files hardened to 0o600 (High-5){ mode: 0o600 } on ~/.tandem/config.json and ~/.tandem/forms/*.json, with a chmodSync migration step that tightens pre-existing loose files on next boot. (#159)
  • Per-install random stealth seed (Low/stealth) — replaced the deterministic sha256('persist:tandem') seed with a per-install random secret persisted in ~/.tandem/config.json. Every install now produces a unique fingerprint-noise pattern; previously every install shared the same signature. (#159)
  • Security-weakening endpoints behind user approval (High-1)POST /security/guardian/mode, POST /security/domains/:domain/trust, and POST /security/outbound/whitelist now require interactive user approval via taskManager.requestApproval when the change would weaken posture. Tightening and no-op changes still pass through without friction. (#161)
  • Unused EXECUTE_JS IPC channel removed (High-4) — the channel had zero callers but would have amplified any XSS reaching the shell into arbitrary JS in the active tab. Agent-driven JS execution continues to flow through the scanner-protected HTTP routes. (#162)
  • CRX3 RSA signature verification on extension install (High-3) — hand-rolled protobuf decoder parses the CRX3 envelope, verifies the SHA-256-with-RSA signature, and binds the signing key back to the expected extension ID. CRX2 downloads are rejected outright. Defends against MITM or CDN compromise during CWS downloads. (#163)
  • Agent-initiated /security/injection-override behind user approval (Medium #3) — silencing the prompt-injection scanner now requires either the user clicking "Override" in the scanner modal (existing flow, unchanged) or an interactive approval prompt for any other caller. Prevents a prompt-injected agent from disabling the defence against its own compromise. (#164, corrected in #165)
  • CORS Origin: null rejected on public routes (Medium #1) — closes a cross-tab information leak where a sandboxed iframe or data: URI could fetch /status from localhost and read the active tab's URL/title. Shell-fetches pass through because modern Electron strips the Origin header (verified via logging investigation). (#165)
  • Date.now jitter removed from the stealth script (Low #4) — the ±1 ms jitter was itself a reliable "not real Chrome" fingerprint (real Chrome returns the same ms for back-to-back calls). performance.now rounding to 100 μs (Firefox parity) remains as the effective timing-fingerprint defence. Also fixed a BehaviorObserver bug that persisted interval: 0 on every keypress event, and removed the unwired recordClick method. (#168)

Wingman UX

  • Wingman Activity and Chat panels now sync on approval resolution — clicking Approve/Reject in one panel dismisses the matching card in the other. The underlying approval-response event is now forwarded to the shell via a new IPC channel mirroring APPROVAL_REQUEST. (#166)
  • Audio ping + escalation + user-return re-alert on approval requests — when an agent stalls waiting for human input, the user now gets: (a) an immediate native-OS notification with the system sound; (b) a second ping after 12 s if the request is still unacknowledged; (c) a third ping when the user returns to Tandem after being away for more than 10 s. A 5 s rate limit prevents stacking. Exists because "AI is stalled on a call" is an invisible state today if the user happens to be in a different workspace or away. (#167)

Agent-learn layer — phase 1

  • BehaviorCompiler is real now, not a stub — reads ~/.tandem/behavior/raw/*.jsonl, extracts valid keypress intervals, drops outliers (below 30 ms / above 2000 ms), percentile-trims 5%, and computes a per-user meanWpm / variance. Fallback to hardcoded defaults below a 100-sample floor or outside a [20, 150] WPM sane range. BehavioralProfile gained additive observability fields: source (default / default-insufficient / compiled), samples, lastCompiledAt. Compiled at every Tandem boot; new POST /behavior/recompile endpoint forces a fresh compile on demand. Was previously a comment saying "in a real scenario we would parse the data; for now we compile defaults" — so every install's agent typed at the same average-typist rhythm. (#169)
  • Webview keystroke rhythm capture (intervals only, no content) — a before-input-event listener inside the existing web-contents-created handler captures typing rhythm from tab content, feeding the same JSONL stream the shell observer already populates. PRIVACY FLOOR: only input.type and input.key.length are read; the key value itself, the webContents URL, origin, partition, and tabId are never read. Persisted events contain only {type, ts, data: {interval}}. A regression-guard test inspects every persisted event and fails red if anything else appears. (#170)

Documentation

  • @samantha-gb acknowledged in SECURITY.md and on the contributor graph via Co-authored-by: trailers. Her security audit drove nine of the twelve PRs in this release. (#160)
  • SECURITY.md restructured with explicit "anonymous / pseudonymous reports welcome" copy, a "no bounty, yes gratitude" section describing what contributors can expect in exchange for a responsible report, and a new Hall of Fame.

Internal notes

  • Threat model clarified for future security reviews. Tandem's trust perimeter sits between web content and the team (user + paired local/remote agents), not between team members. Findings that describe authenticated callers receiving internal tokens or state are, under this model, not vulnerabilities. Several items from the #34 audit were re-evaluated and intentionally not changed on that basis; the reasoning is documented inline in the relevant PR comments and in SECURITY.md.
  • Cross-panel sync bug surfaced and fixed while testing the injection-override approval flow. The audio/escalation UX work was added after live-testing revealed that agent stalls could go unnoticed when the user was in a different workspace.

[v0.74.0] - 2026-04-17

  • refactor: split the renderer shell into ES modules + fix a long-standing workspace assignment race

Changed

  • The renderer shell (shell/js/sidebar/index.js and shell/js/wingman/index.js) has been migrated from a single classic script per panel to ES modules with one file per responsibility. The monolithic sidebar file shrank from ~1700 LOC to ~250 LOC of wiring; panel logic now lives in focused shell/js/sidebar/panels/*.js and shell/js/wingman/*.js modules with explicit imports. Classic-script consumers still reach the shell via the existing window.* bridge so nothing outside the shell needed to change.
  • Sidebar state (config.js, constants.js), drag-and-drop, panel-resize, setup flow, webview lifecycle, history, bookmarks, pinboard, workspaces, and tab-context-menu are now each their own module with a narrow public surface. Wingman alerts, screenshot capture, panel shell, chat, and chat-streaming received the same treatment.
  • A renderer-side bookmarks store (shell/js/bookmarks-store.js) is now the single source of truth for the top bar and sidebar panel, so a bookmark added from the sidebar no longer waits for the next HTTP poll to appear in the top bar (and vice versa).
  • Review-driven tightening during the refactor: unused wingman mic / live-mode toggle removed, chat API surface trimmed and sendMessage documented, bookmark favicon helper de-duplicated, pinboard pbEscape now escapes quotes for attribute contexts, pinboard mutation errors surface to the user, pinboard listeners are de-duplicated, and the pinboard panel gained an inline board-rename UI.

Fixed

  • The + new-tab button now assigns the new tab to the currently-active workspace instead of falling back to Default. WorkspaceManager.reconcileTabState previously stripped the new webContentsId during the window between web-contents-created (when main assigns the tab) and TabManager.tabs.set (when openTab finishes), after which the orphan-push block relocated the tab to Default. A new pendingAssignments set protects just-assigned tabs from the strip until the runtime catches up. Regression test included. The underlying race was latent since v0.69.x; the sidebar ESM migration in this release changed call timing enough to make it reliable.
  • Workspace and pinboard names containing quotes or HTML no longer break the sidebar context menu or edit form — both code paths now escape for the attribute context they're inlined into.
  • Opening Tips/Help from the sidebar now creates a new tab instead of replacing the active tab's contents.

[v0.73.1] - 2026-04-16

  • fix: register MCP session after transport assigns ID

The SDK's sessionIdGenerator fires during handleRequest (on the initialize call), not during server.connect(). Move session registration to after handleRequest so the stored session ID matches what the client receives in the Mcp-Session-Id header.

Also rewrites http-transport tests to use real HTTP requests against a test server for reliable SDK integration coverage.

[v0.73.0] - 2026-04-14

  • feat: expand live watch and screenshot automation surfaces

Fixed

  • POST /wingman-alert no longer pulls the human out of their current workspace by default, while explicit context activation remains available for the few flows that really need it
  • Opening the Wingman panel with open handoffs now lands on Activity instead of Chat, so the user sees the actionable inbox immediately when responding to a handoff cue
  • Standalone waiting_approval handoffs now resolve correctly on Approve and Reject even when they are not linked to a paused task step, so generic approval requests no longer appear to do nothing
  • The Wingman handoff list now gives open cards enough vertical room for common two-item cases, removing the cramped internal scrollbar seen during live testing
  • Version metadata now stays aligned across package.json, package-lock.json, repo docs, the landing page, and the MCP server, with scripts/check-consistency.js extended to catch future drift automatically
  • Apple Photos screenshot import now passes a real file alias into the Photos AppleScript flow instead of a raw POSIX file URL, fixing the blank-name / "Cannot Import Item" failure seen after application captures

Added

  • Remote agent pairing over Tailscale: agents on another machine can pair with Tandem via a one-time setup code (TDM-XXXX-XXXX), receive a durable binding token, and use the full HTTP API remotely — proven with Windows 11 + VS Code + Claude Code connecting to macOS over Tailscale
  • PairingManager with setup code generation (5-minute TTL), token exchange, binding lifecycle (paired/paused/revoked/removed), SHA-256 token hashing, and persistent storage at ~/.tandem/pairing/bindings.json
  • Public bootstrap/discovery routes: GET /agent (human-readable), GET /agent/manifest (machine-readable with all endpoint families), GET /agent/version (capability summary), GET /skill (version-matched usage guide) — all request-aware so URLs are correct over Tailscale
  • Pairing HTTP routes: POST /pairing/setup-code, POST /pairing/exchange (public, rate-limited), GET /pairing/whoami, GET /pairing/bindings, pause/resume/revoke/remove per binding, and GET /pairing/addresses for Tailscale address auto-detection
  • Binding token auth (tdm_ast_ prefix) accepted alongside existing local api-token for all HTTP routes and the /watch/live WebSocket
  • Onboarding-first "Connect your AI to Tandem" Settings UI with mode selector (same machine / another machine), address detection, instruction generation with bindingKind, and binding management cards
  • API listen host defaults to 0.0.0.0 (local + remote simultaneously) with auto-migration from 127.0.0.1 for existing installs
  • ws://127.0.0.1:8765/watch/live now streams an immediate watch snapshot plus incremental watch add/remove/check events to authenticated local clients, giving agents a real-time watch surface instead of polling /watch/list
  • Watches now support configurable diff modes for change detection: content, title, title-or-content, and text-length, exposed through both the HTTP API and MCP watch-add flow
  • New HTTP screenshot capture routes: POST /screenshot/application saves a fresh full-window capture, and POST /screenshot/region saves a fresh application-region capture without requiring renderer IPC or clipboard round-trips
  • Matching MCP media tools now expose the same capture modes for MCP-first clients, bringing the server tool count to 250 while keeping screenshot capture parity with the local HTTP API

[v0.72.1] - 2026-04-14

  • fix: keep handoff attention visible when the Wingman panel is closed

Changed

  • The closed Wingman badge and panel toggle now enter a durable handoff attention state with a visible count whenever open handoffs still need action, so the UI keeps saying "the agent still needs you" after the transient popup expires
  • New handoffs escalate in layers instead of replaying popup/audio loops: the existing popup + sound still fire once, while the closed-panel affordances keep a calmer persistent state and strengthen visually after roughly 12 seconds if the handoff remains unseen
  • Opening the Wingman panel now acknowledges current handoffs and resets the stronger alert state, while unresolved handoffs still remain visible in a quieter pending style when the panel is closed again
  • Handoff serialization and renderer IPC now include a derived attentionLevel (review, action, urgent) so UI policy can stay explicit without changing the durable handoff persistence model
  • Added focused unit coverage for the new handoff attention-level mapping and extended route assertions so the extra metadata remains stable across the API surface

[v0.72.0] - 2026-04-13

  • feat: add explicit human↔agent handoffs across API, MCP, events, and Wingman UI

Added

  • Durable HandoffManager persistence in ~/.tandem/handoffs.json with explicit statuses for needs_human, blocked, waiting_approval, ready_to_resume, completed_review, and resolved
  • New HTTP API routes for handoff lifecycle and targeting: GET /handoffs, GET /handoffs/:id, POST /handoffs, PATCH /handoffs/:id, POST /handoffs/:id/resolve, and POST /handoffs/:id/activate
  • New MCP tools for the same handoff system: tandem_handoff_create, tandem_handoff_list, tandem_handoff_get, tandem_handoff_update, and tandem_handoff_resolve
  • New MCP resource tandem://handoffs/open plus SSE handoff events so open handoffs can be observed live
  • A real Wingman Activity inbox for open handoffs, including workspace/tab context, action hints, open-context targeting, and resolve/mark-ready actions
  • A shared TaskHandoffCoordinator plus explicit handoff actions for ready, resume, approve, and reject, so task-linked handoffs can now pause tasks, move into ready-to-resume, and hand execution back to the agent cleanly

Changed

  • POST /wingman-alert now acts as a compatibility wrapper over the handoff system, preserving the old alert behavior while also creating a durable needs_human handoff
  • Task approval requests now also appear as structured handoffs instead of only transient approval cards, keeping human attention requests visible in one place
  • Wingman Activity logging now records handoff lifecycle updates so the user can see when a handoff was created, updated, or resolved
  • Handoff metadata normalization now treats blank titles/reasons as defaults, and focused route/MCP/manager tests cover the new handoff lifecycle more thoroughly so coverage matches the added product surface
  • The Wingman Activity inbox now surfaces status-aware actions: approval handoffs show Approve / Reject, human-blocked handoffs show Mark Ready, and ready_to_resume handoffs show Resume Agent
  • skill/SKILL.md now teaches agents the real runtime model more explicitly: Tandem must already be running, some clients are effectively MCP-first, durable handoffs are preferred over transient alerts for resumable blockers, interaction completion should be verified explicitly, and DevTools/network observation should stay tab-aware

[v0.71.4] - 2026-04-13

  • fix: make fill replacement and keyboard completion confirmation match observed browser state

Fixed

  • POST /snapshot/fill now prepares a real replacement selection on the target field before trusted key events are sent, so filling a populated input replaces the old value instead of corrupting it through caret-dependent append behavior
  • Selector-based POST /type with clear: true now uses the same replacement-first strategy, preserving trusted input events while making pre-populated field replacement deterministic
  • Keyboard actions (POST /press-key, POST /press-key-combo) now confirm observable active-element focus shifts even when the focused element keeps the same tag name, keeping completion.effectConfirmed aligned with the returned postAction.page.activeElement
  • Label locators now fall back to a runtime DOM association pass when the CDP label search misses, improving POST /find by label on simple label[for] fixtures
  • Added focused tests for selector replacement typing, snapshot ref fill replacement, focus-shift confirmation, and runtime label lookup fallback

[v0.71.3] - 2026-04-13

  • fix: strengthen interaction completion semantics across HTTP API and MCP

Fixed

  • Selector-based POST /click and POST /type now return explicit tab scope, target resolution, completion mode, and lightweight post-action state instead of leaving agents to infer whether the action actually landed
  • Snapshot ref actions (POST /snapshot/click, POST /snapshot/fill) now distinguish dispatch completion from confirmed effect, include the resolved tab scope for the ref target, and report post-action element/page state when it can be observed cheaply
  • Semantic locator routes (POST /find, /find/click, /find/fill, /find/all) now honor tab targeting, expose the resolved scope in their responses, and preserve locator resolution details when they chain into ref actions
  • Keyboard actions (POST /press-key, POST /press-key-combo) now return scoped completion metadata and post-action page/navigation state instead of a bare acknowledgement
  • MCP navigation and snapshot tools now describe the underlying guarantees accurately and surface the richer HTTP action contracts instead of flattening them into vague success text
  • Added focused route, MCP, and confirmation-helper tests for delayed fill confirmation, post-click/navigation state, and tab-targeted interaction semantics

[v0.71.2] - 2026-04-13

  • fix: harden tab-scoped devtools and network inspection across HTTP API and MCP

Fixed

  • DevTools read routes now resolve an explicit tab target, default to the active tab, and return scope metadata so status, console, network, performance, storage, evaluate, DOM queries, and raw CDP calls no longer imply global state when they are tab-scoped
  • CDP network capture now keys requests by webContents target instead of raw requestId alone, preventing cross-tab collisions and making /devtools/network/:requestId/body trustworthy when multiple attached tabs are active
  • The webRequest-based network inspector now records tab identity and resource type, so /network/log, /network/apis, /network/domains, and /network/har can be filtered per tab instead of mixing traffic from unrelated tabs or extension noise
  • MCP DevTools and network tools now describe active-tab-by-default behavior accurately, forward tabId only where the HTTP API truly honors it, and expose scoped status/body/summary calls that match the underlying routes
  • Added focused route, MCP, DevTools capture, and network inspector tests to keep scoped-vs-global behavior aligned across the API surface

[v0.71.1] - 2026-04-13

  • fix: stabilize multi-actor workspace, tab, SSE, and MCP ownership context

Fixed

  • Active workspace selection and active tab ownership now reconcile deterministically instead of drifting apart during focus changes, workspace switches, or agent-opened tabs
  • GET /workspaces now reports whether the active workspace comes from a focused tab or an explicit workspace selection
  • GET /active-tab/context now includes trustworthy workspace/source/actor ownership data for the active tab and the global tab list
  • /events/stream now attaches workspace and actor/source context to tab-driven events when known
  • Workspace activation no longer forces a focus churn when the target workspace already has a live tab; empty-workspace activation remains explicit as a selection
  • MCP tab/chat actions now use a single configurable actor source (TANDEM_SOURCE / TANDEM_MCP_SOURCE / TANDEM_ACTOR_SOURCE) instead of a hardcoded wingman/claude split
  • MCP resources (tandem://tabs/list, tandem://context) now surface workspace/source ownership details instead of hiding them behind older global summaries

[v0.71.0] - 2026-04-11

  • feat: tab emoji badges — assign emoji to tabs for quick visual identification

New endpoints:

  • POST /tabs/:id/emoji — set or flash an emoji badge on a tab
  • DELETE /tabs/:id/emoji — remove emoji badge from a tab

New MCP tools:

  • tandem_tab_emoji_set — set emoji badge on a tab
  • tandem_tab_emoji_remove — remove emoji badge from a tab
  • tandem_tab_emoji_flash — flash a pulsing emoji to attract user attention

Emoji badges are per tab session. The AI can flash emojis to signal the user (e.g. "this tab is ready for review"). Users assign emojis via right-click context menu. Everything runs in the shell — no injection into webviews.

[v0.70.0] - 2026-04-10

  • feat: add awareness tools — digest and focus for shared human-AI context

Two new endpoints + MCP tools:

  • GET /awareness/digest — recent browser activity (navigation, interactions, errors, tabs, downloads, watches) for the last N minutes
  • GET /awareness/focus — lightweight current-state check (active tab, activity type, idle time, error flags)

These enable the AI to understand what the human is doing and contribute meaningfully — the core of the tandem principle.

[v0.69.3] - 2026-04-10

  • fix: add .nojekyll to enable static GitHub Pages

[v0.69.2] - 2026-04-10

  • fix: move FUNDING.yml to correct location (.github/)

[v0.69.1] - 2026-04-10

  • fix: route MCP server logging to stderr to prevent protocol corruption

MCP uses stdio for JSON protocol messages. console.info/debug go to stdout and break the protocol. All MCP server logging now goes through console.error (stderr) only.

[v0.69.0] - 2026-04-09

Added

  • Chrome-style URL bar autocomplete from browsing history — dropdown with matching URLs, inline completion, keyboard navigation
  • MCP bookmark management tools: tandem_bookmarks_list, tandem_bookmark_add, tandem_bookmark_delete, tandem_bookmark_update, tandem_bookmark_folder_add, tandem_bookmark_move, tandem_bookmark_check
  • MCP history and site memory tools: tandem_history_list, tandem_history_clear, tandem_activity_log, tandem_site_memory_list, tandem_site_memory_get, tandem_site_memory_search
  • MCP keyboard input tools: tandem_press_key, tandem_press_key_combo with POST /press-key and POST /press-key-combo HTTP endpoints
  • MCP live preview tools: tandem_preview_create, tandem_preview_update, tandem_preview_list, tandem_preview_delete
  • MCP server now exposes 231 tools with full HTTP API parity (up from 63 in v0.68.0)
  • MCP tools refactored into 29 modular files under src/mcp/tools/ matching the HTTP API route structure
  • MCP Chrome import tools: profiles, bookmarks, history, cookies, sync start/stop/status
  • MCP extension extras: Chrome import, gallery, updates lifecycle, disk usage, conflicts, native messaging
  • MCP sidebar tools: config, toggle/activate items, reorder, state
  • MCP media tools: voice start/stop/status, audio start/stop/status/recordings, draw toggle, panel toggle
  • MCP screenshot extras: annotated capture, screenshot list
  • MCP events/behavior tools: recent events, live status/toggle, behavior stats/clear
  • MCP system tools: folder picker, injection override, Google Photos integration
  • MCP agent extras: autonomy get/update, approval check, agent activity log, per-tab lock status
  • MCP password, form, workflow, task, pinboard, watch, headless, auth, context, script/style, session, device, data, preview tools — all at full parity

Fixed

  • Dark mode rendering: disabled Chromium's WebContentsForceDark that forcefully darkened websites without native dark mode support; set nativeTheme.themeSource = 'system' so sites receive OS preference naturally
  • Google CookieMismatch: restored real Electron UA for Google auth URLs (session.setUserAgent baked fake Chrome UA into defaults — must overwrite, not delete); disabled cookie partitioning features that Electron can't handle without Related Website Sets; fixed Sec-CH-UA header mismatch for Google auth
  • Stealth UA auto-sync: replaced hardcoded Chrome/131 with dynamic version from process.versions.chrome to prevent version mismatch detection
  • Workspace emoji icons: getIconSvg() now renders emoji strings directly instead of falling back to default SVG
  • History date formatting: fixed Invalid Date in tandem_history_list (field name mismatch: visitedAtlastVisitTime)
  • URL autocomplete auth: added missing Authorization: Bearer header to history search API calls

[v0.68.0] - 2026-04-09

Added

  • MCP server expanded from 24 to 63 tools — full API coverage for any MCP-connected agent (Claude Code, Cursor, OpenClaw, or third-party)
  • MCP tab targeting: optional tabId parameter on all read/interaction tools for background tab operations via X-Tab-Id
  • MCP accessibility snapshots: tandem_snapshot, tandem_snapshot_click, tandem_snapshot_fill with @ref IDs
  • MCP semantic locators: tandem_find, tandem_find_click, tandem_find_fill for role/text/label/placeholder queries
  • MCP DevTools tools: console log inspection, network monitoring, DOM queries, performance metrics, storage inspection, JS evaluation via CDP
  • MCP network tools: network log, API endpoint discovery, HAR export, request mocking/unmocking
  • MCP workspace tools: create, activate, delete, move tabs between workspaces
  • MCP session tools: isolated session create/switch/destroy, same-origin fetch relay
  • MCP tab locks: acquire/release for multi-agent coordination
  • MCP content tools: structured content extraction, raw HTML, cookie management
  • tandem_wingman_alert MCP tool for agent→human escalation with notification levels
  • tandem_scroll now supports target (top/bottom) and selector (scroll-into-view) parameters
  • tandem_open_tab now accepts optional workspaceId parameter via MCP
  • esbuild bundling for preload scripts to support Electron sandbox mode with modular source files

Fixed

  • Preload module split (PR #49) broke all renderer→main IPC because sandbox: true only allows require('electron') in preload scripts; resolved by bundling with esbuild

Security

  • Updated electron 40.6.0 → 40.8.5 (17 alerts resolved)
  • Updated hono → 4.12.12, @hono/node-server → 1.19.13 (6 alerts resolved)
  • Updated lodash → 4.18.1 (2 alerts: code injection, prototype pollution)
  • Fixed brace-expansion and path-to-regexp vulnerabilities
  • Resolved all 28 Dependabot security alerts → 0 vulnerabilities

[v0.67.0] - 2026-04-02

Added

  • AI workspaces for agents: OpenClaw or any API-driven agent can now operate in its own dedicated Tandem workspace, keep its tabs separate from Robin's browsing, and persist that workspace across sessions
  • POST /workspaces/:id/activate switches the active workspace via API so Tandem can bring the agent's workspace into view instantly
  • POST /workspaces/:id/tabs moves an existing tab into a workspace by webContents ID
  • POST /tabs/open now accepts inheritSessionFrom and copies IndexedDB data from the source tab into the new tab before reloading the destination, preserving Discord-style IndexedDB-backed logins.

Changed

  • POST /tabs/open now accepts workspaceId, so new tabs can be assigned directly into the agent's workspace at creation time
  • POST /wingman-alert now accepts optional workspaceId, so captcha or takeover alerts can automatically switch Tandem into the right workspace before notifying Robin

[v0.66.0] - 2026-04-02

Added

  • X-Tab-Id header support for background-tab targeting on GET /snapshot, GET /page-content, GET /page-html, POST /execute-js, POST /wait, GET /links, and GET /forms
  • Snapshot refs now remember which tab produced them, so ref follow-up actions stay attached to the correct tab

Changed

  • skill/SKILL.md now reflects the current Tandem API targeting model and includes ClawHub frontmatter metadata

Fixed

  • /find/click and /find/fill now catch thrown route errors and return JSON 500 responses instead of dropping the connection

[v0.65.5] - 2026-03-21

  • fix: CodeQL config — exclude security scanner modules from XSS taint analysis

The PromptInjectionGuard and injection-scanner middleware intentionally parse untrusted HTML to detect prompt injection. Matched text is sanitized via character-level escaping and only appears in JSON API responses, never in browser DOM context. CodeQL's js/xss taint tracking cannot distinguish this from actual XSS vulnerabilities.

[v0.65.4] - 2026-03-21

  • fix: CodeQL — use char-level sanitization to break taint tracking for HTML output

[v0.65.3] - 2026-03-21

  • fix: CodeQL — strengthen HTML/JS escaping, escape at extraction point, strip script tags

[v0.65.2] - 2026-03-21

  • fix: CodeQL — escape HTML/JS in injection scanner output to prevent XSS

[v0.65.1] - 2026-03-21

  • fix: lint — prefer-const for zeroWidthPositions

[v0.65.0] - 2026-03-21

  • feat: Prompt Injection Guard — browser-level AI content defense (Layer 8)

  • New PromptInjectionGuard module with 40+ detection patterns

  • Hidden text detection: CSS, HTML, Unicode tricks

  • API middleware on /page-content, /page-html, /snapshot, /execute-js

  • Risk scoring: ≥70 blocks content, 30-70 warns, <30 passes

  • Centered modal alerts with tab highlighting (red/orange)

  • Double-confirmation override with 5-min TTL per domain

  • Config integrity monitor on openclaw.json

  • 89 tests with false positive validation

[v0.64.0] - 2026-03-21

New: Prompt Injection Guard (Security Layer 8)

Tandem now detects and blocks prompt injection attacks in web page content before it reaches the AI agent. This is the first browser-level prompt injection defense.

  • New module: PromptInjectionGuard — 40+ regex patterns detecting instruction override, role hijacking, config manipulation, credential theft, stealth instructions, and filesystem write attempts
  • Hidden text detection: CSS tricks (font-size:0, opacity:0, off-screen positioning), HTML tricks (comments, noscript, template, data attributes, aria-labels), Unicode tricks (zero-width chars, RTL override, homoglyphs)
  • API middleware: Scans all content returned via /page-content, /page-html, /snapshot, /snapshot/text, and /execute-js
  • Risk scoring: 0-100 scale. Score ≥70 = content BLOCKED (AI receives nothing). Score 30-70 = content forwarded with injectionWarnings field
  • User alerts: Centered modal with detected threats, source URL, and tab highlighting (red for blocked, orange for warnings)
  • User override: "Override — Allow this page" button with double confirmation ("Are you absolutely sure?"). Override grants 5-minute access per domain
  • Config integrity monitor: Watches openclaw.json for suspicious modifications (CORS wildcard, short auth tokens). Triggers OS notification on tamper detection
  • 89 tests covering text patterns, CSS/HTML/Unicode tricks, combined scanning, and false positive validation

Security endpoint

  • POST /security/injection-override — User override for blocked domains (5 min TTL)

Dialog endpoint

  • POST /dialog/pick-folder — Native folder picker for screenshot settings

[v0.63.6] - 2026-03-20

  • fix: add zhipin.com to stealth skip list — bypasses bot detection after login

[v0.63.5] - 2026-03-20

  • fix: resolve CHANGELOG merge conflict

[v0.63.4] - 2026-03-20

  • fix: increase V8 heap limit to 4GB to prevent OOM on memory-heavy SPAs

Linux Fixes

  • fix(linux): make OpenClaw identity loading async to prevent main process blocking

    Converted synchronous fs.readFileSync/writeFileSync calls in OpenClaw identity loading to async fs.promises API. The sync calls were blocking the main process during WebSocket connect handshake, causing "not responding" dialogs on Linux (macOS was unaffected due to different I/O scheduling).

  • fix(linux): add overflow scrolling to sidebar for screens with limited height

    Added overflow-y: auto to .sidebar-items so sidebar icons are accessible via scroll on smaller screens. Settings/Help/Collapse buttons no longer get cut off.

[v0.63.3] - 2026-03-20

  • fix: resolve CI test failures — safe app.getVersion(), update quickLinks count in tests

[v0.63.2] - 2026-03-20

  • fix: remove unused imports in app-menu, fix eslint any in overlay

[v0.63.1] - 2026-03-20

  • fix: UI/UX polish pass v0.63.0 — 18 fixes across shell, sidebar, settings, screenshots

[v0.63.0] - 2026-03-20

UI/UX polish pass — 18 fixes across shell, sidebar, settings, and screenshots.

Shell & Menus

  • fix: rename first app menu from "Electron" to "Tandem Browser"
  • fix: remove redundant Window menu
  • fix: move Draw Mode to Edit menu, remove Copilot/Wingman menu
  • fix: move "About Tandem Browser" to Help menu

Sidebar

  • fix: sidebar defaults to wide mode on first launch, remembers preference thereafter
  • fix: wide mode shows labels next to icons, group headers visible
  • fix: tooltips only shown in narrow mode
  • fix: footer buttons left-aligned with labels (Collapse, Customize, Tips & Tutorials)
  • fix: Customize button now works as toggle (click to open, click again to close)
  • fix: remove Downloads and Personal News from sidebar settings

Quick Links

  • fix: auto-save on add/remove/change (no more Save button, debounced 600ms)
  • fix: default quick links updated to public profiles (DuckDuckGo, Google, GitHub, X, LinkedIn, YouTube)

Wingman

  • fix: display name changed from "Robin" to "You" in Wingman chat

Help

  • fix: Help/lamp button links to local help.html instead of external URL

About

  • fix: About panel now shows dynamic version from app (was hardcoded v0.57.6)

Settings

  • fix: Language, Wingman panel default open, and Show bookmarks bar marked as "Coming soon" and disabled
  • fix: Screenshot storage path now uses native folder picker dialog instead of text input

Screenshots

  • fix: Apple Photos import error -1728 — more robust AppleScript, 300ms flush delay, permission check

[v0.62.17] - 2026-03-18

  • fix: update default quick links — remove personal/ClaroNote links, add Robin's public profiles

[v0.62.16] - 2026-03-17

  • fix: satisfy CodeQL rate limit detection (api)

What was built/changed:

  • Modified files: src/api/routes/data.ts
  • Swapped the OpenClaw token/connect route limiters to a CodeQL-recognized express-rate-limit middleware while keeping the existing request caps and messages

Why this approach:

  • The endpoint was already protected by the custom limiter, but CodeQL does not treat that middleware as a proven rate limiter for this filesystem-backed handler
  • Using a standard limiter on the sensitive OpenClaw config routes removes the false-positive gate without changing the user-visible behavior

Tested:

  • npx tsc --pretty false: zero errors
  • npx vitest run src/api/tests/routes/data.test.ts: 52 passed

[v0.62.15] - 2026-03-17

  • fix: restore stock OpenClaw Wingman chat (wingman)

What was built/changed:

  • New files: src/openclaw/connect.ts
  • Modified files: src/api/routes/data.ts, src/api/tests/routes/data.test.ts, src/ipc/handlers.ts, src/panel/manager.ts, src/preload.ts, shell/chat/openclaw-backend.js, shell/chat/router.js, shell/js/wingman.js, TODO.md, CHANGELOG.md
  • New API endpoints: GET /config/openclaw-connect
  • Chat send/persist flow now stores Robin and Wingman messages without depending on the old local tandem-chat skill

Why this approach:

  • Stock Tandem now signs a real OpenClaw device identity for the gateway WebSocket handshake and uses the same operator read/write chat flow as the official OpenClaw webchat
  • This removes the hidden dependency on a local /chat polling bridge and fixes the misleading connected state in the panel

Tested:

  • npx tsc --pretty false: zero errors
  • npx vitest run: 34 files, 1036 passed, 39 skipped
  • Manual: verified local OpenClaw gateway chat round-trip in the Wingman panel, GET /config/openclaw-connect, and persisted replies via GET /chat

[v0.62.13] - 2026-03-17

  • fix: restrict sync root paths to user home directory (security)

[v0.62.12] - 2026-03-17

  • fix: sanitize preview IDs to prevent path traversal and reflected XSS (security)

[v0.62.9] - 2026-03-16

  • fix: remove all duplicate files with spaces in names from repo

[v0.62.8] - 2026-03-16

  • fix: remove duplicate test files with spaces in names

[v0.62.7] - 2026-03-16

  • fix: add abs.twimg.com to trusted script domains for x.com

[v0.62.6] - 2026-03-16

  • fix: remove __tandemRng/__tandemNoise globals from window (detected by x.com anti-bot)

[v0.62.5] - 2026-03-16

  • fix: skip stealth injection on x.com/twitter.com (site detects and blocks patches)

[v0.62.4] - 2026-03-16

  • fix: remove unused detectBackend import, fix async Promise executor lint errors

[v0.62.3] - 2026-03-16

  • fix: hide mic button on macOS (use system dictation), show only on Linux

[v0.62.2] - 2026-03-16

  • fix: rewrite Swift binary without requestAuthorization (inherits from parent Electron process)

[v0.62.1] - 2026-03-16

  • fix: convert webm to m4a before Apple Speech transcription, add better error logging

[v0.62.0] - 2026-03-16

  • feat: native voice-to-text via Apple Speech (macOS) + Whisper fallback (Linux/Windows)

[v0.61.2] - 2026-03-16

  • fix: bypass OutboundGuard for known Google API domains (Speech API, gstatic)

[v0.61.1] - 2026-03-16

  • fix: add Google WebSocket endpoints to KNOWN_WS_SERVICES for Web Speech API

[v0.61.0] - 2026-03-16

  • feat: add /devtools/shell endpoint to open shell chrome devtools for debugging

[v0.60.6] - 2026-03-16

  • fix: start SpeechRecognition before stopping warmup stream to prevent not-allowed error

[v0.60.5] - 2026-03-16

  • fix: add gstatic and Google CDN domains to trusted scripts

[v0.60.4] - 2026-03-16

  • fix: add Google APIs to trusted script domains for Web Speech API

[v0.60.3] - 2026-03-16

  • fix: warm up mic via getUserMedia before SpeechRecognition to ensure permission is active

[v0.60.2] - 2026-03-16

  • fix: request macOS microphone permission before starting voice input

[v0.60.1] - 2026-03-16

  • fix: mic button handles permission denied and onend race condition

[v0.60.0] - 2026-03-16

  • feat: add mic button to chat input for voice-to-text

[v0.59.22] - 2026-03-16

  • fix: remove unused appPath variable (lint)

[v0.59.21] - 2026-03-15

  • fix: force-stop converts webm to mp4, cleanup corrupt output on ffmpeg failure

[v0.59.20] - 2026-03-15

  • fix: guard source variable in video-recorder to prevent crash before overlay shows

[v0.59.19] - 2026-03-15

  • fix: add light theme support to bookmarks page

[v0.59.18] - 2026-03-15

  • fix: add light theme support to help page

[v0.59.17] - 2026-03-15

  • fix: remove deprecated wingman panel position setting

[v0.59.16] - 2026-03-15

  • fix: add background to main-layout and browser-content to prevent dark gap in light theme

[v0.59.15] - 2026-03-15

  • fix: add theme support to newtab page — CSS variables + BroadcastChannel sync

[v0.59.14] - 2026-03-15

  • fix: apply theme on settings page load, not only on change

[v0.59.13] - 2026-03-15

  • fix: correct light theme CSS for lgl-glass-sidebar, navbar and toolbar (invalid selector syntax)

[v0.59.12] - 2026-03-15

  • fix: light theme overrides for macOS tab-bar, toolbar and bookmarks-bar

[v0.59.11] - 2026-03-15

  • fix: break panel toggle feedback loop — setPanelOpenSilent updates state without IPC echo

[v0.59.10] - 2026-03-15

  • fix: use panelManager directly instead of ctx in ipc handler

[v0.59.9] - 2026-03-15

  • fix: sync panel open state to backend so notifications are suppressed when chat is visible

[v0.59.8] - 2026-03-15

  • fix: null-safe backend button references after chat selector cleanup

[v0.59.7] - 2026-03-15

  • fix: remove claude/both backend tabs from chat panel, wingman only

[v0.59.6] - 2026-03-15

  • fix: always sync webhook.secret with OpenClaw on startup, not only when empty

[v0.59.5] - 2026-03-15

  • fix: add github.githubassets.com to trusted script domains

[v0.59.4] - 2026-03-15

  • fix: make preview routes public so HTML pages load in browser tabs without Bearer token

[v0.59.3] - 2026-03-15

  • fix: explicitly focus new tab after preview creation

[v0.59.2] - 2026-03-15

  • fix: webview stays within sidebar-panel-content bounds, header no longer covered

[v0.59.1] - 2026-03-15

  • fix: use safeSetPanelHTML to preserve webview sessions during panel content updates

[v0.59.0] - 2026-03-15

Live HTML preview system — build and iterate pages directly inside Tandem

OpenClaw can now create, update, and serve live HTML previews inside Tandem Browser. The workflow: ask your agent to build a page, it appears instantly in a new tab, you give feedback, the agent updates it, the tab refreshes automatically. No external tools, no file:// URLs, no dev servers.

New endpoints

  • POST /preview — create a new preview from HTML. Tandem opens it in a new tab automatically. Pass title, html, and optionally inspiration (source URL for reference). Returns the stable preview URL.
  • PUT /preview/:id — update an existing preview. The tab auto-refreshes within 2 seconds via a lightweight polling script injected into the page. Version counter increments on every update.
  • GET /preview/:id — serve the preview as a real HTTP page (not file://). Bookmarkable, shareable within the local machine, works with external fonts and CDN resources.
  • GET /preview/:id/meta — metadata only (id, title, version, dates). Used internally by the live-reload script.
  • GET /previews — list all saved previews as JSON.
  • GET /previews/index — human-readable index page of all previews.
  • DELETE /preview/:id — remove a preview.

Storage

Previews are persisted to ~/.tandem/previews/<id>.json. Each file contains the full HTML, title, inspiration URL, creation/update timestamps, and a version counter. Previews survive Tandem restarts. IDs are slugified from the title (robin-portfolio, kanbu-landing-page, etc.) and deduplicated automatically.

Live reload

Every preview page gets a small injected script that polls /preview/:id/meta every 2 seconds. When the version number changes, the page reloads. This means the agent can iterate on a design while the human watches the result update in real time — no manual refresh needed.

Workflow example

You:   "build me a portfolio page inspired by this site: https://example.com"
Agent: opens https://example.com in one tab, reads the design
Agent: generates HTML/CSS, POSTs to /preview
Tandem: opens http://127.0.0.1:8765/preview/my-portfolio in a new tab
You:   "make the header bigger and change the color to dark blue"
Agent: PUTs updated HTML to /preview/my-portfolio
Tab:   auto-refreshes within 2 seconds
You:   bookmark the URL — it persists across restarts

[v0.58.0] - 2026-03-15

Active tab context — OpenClaw now knows what you're looking at

Added GET /active-tab/context — a single endpoint that gives OpenClaw everything it needs to understand what the user is currently viewing, without multiple round-trips.

Response includes:

  • active tab id, URL, title, loading state
  • viewport dimensions and scroll position
  • first 1500 characters of page text (enough to answer questions without a full /page-content call)
  • full tab list with active flag

Also documented the existing GET /events/stream SSE endpoint and tab-focused events in the skill file, so agents can subscribe to tab switches without polling. Updated skill/SKILL.md with a dedicated "Page Awareness" section.

[v0.57.22] - 2026-03-15

Sidebar login persistence fix

Sidebar webviews (Telegram, WhatsApp, Discord, Slack, Gmail, etc.) were losing their login state every time the panel was switched or the sidebar setup panel was opened. Root cause: Electron destroys and recreates a <webview> session when the element is removed from the DOM.

Fix: moved all sidebar webviews into a persistent #sidebar-webview-host container that is never wiped. Panel switches now show/hide webviews without touching the DOM. Login state is preserved for the lifetime of the Tandem session.

[v0.57.21] - 2026-03-15

Security model refinement — daily browsing fixed, real threats still caught

This release addresses a series of false positives in the security stack that made normal browsing impractical, and adds background tab API access for OpenClaw agents.

Security fixes

  • Script analysis containment removed — ScriptGuard was triggering containment popups on virtually every news site and SPA because minified/obfuscated JavaScript scored high on threat rules. Script analysis now logs anomalies and reports to the gatekeeper channel, but does not activate containment. Containment still activates on confirmed behavioral signals (crypto miner CPU patterns, sustained WASM activity via BehaviorMonitor).

  • LinkedIn fully unblocked — Three separate layers were blocking LinkedIn:

    • NetworkShield blocklist contained ads.linkedin.com and snap.licdn.com, causing the parent domain check to block all of linkedin.com
    • Gatekeeper was blocking scripts from static.licdn.com due to low trust score on first visit
    • ScriptGuard rule engine was running on trusted CDN domains and triggering containment on LinkedIn's minified JS
    • Fixed by adding an explicit domain allowlist in NetworkShield, a trusted script domain list in Guardian, and skipping the rule engine for known CDN domains in ScriptGuard

API improvements

  • X-Tab-Id header supportGET /page-content, GET /page-html, and GET /snapshot now accept an X-Tab-Id request header to target a specific background tab without changing focus. Background tab content extraction uses DevTools Runtime.evaluate instead of executeJavaScript to avoid hangs on non-active tabs.

UX fixes

  • Sidebar links now open in new tab — Links clicked inside sidebar webviews (Telegram, WhatsApp, etc.) were silently denied. They now open in a new Tandem tab as expected.

Docs

  • Added hero screenshot and browser interaction screenshot to README
  • Security model description moved to top of README per maintainer feedback
  • TODO: expose POST /screenshot/application and POST /screenshot/region as HTTP API endpoints

[v0.57.13] - 2026-03-14

  • fix: address remaining CodeQL alerts (security)

[v0.57.12] - 2026-03-14

  • fix: harden remaining CodeQL alert clusters (security)

[v0.57.11] - 2026-03-14

  • fix: harden extension and task-manager boundaries (security)

What was built/changed:

  • Added shared validation helpers for extension IDs, native messaging host names, and absolute path containment inside trusted roots
  • Hardened CRX downloader, extension loader, and native-messaging proxy against path traversal through extension IDs, extension paths, host manifest names, and manifest patch paths
  • Hardened TaskManager autonomy updates against prototype-polluting payloads and validated task IDs/step indexes before file or array writes
  • Added focused tests for the new extension path validation and task-manager pollution guards

Why this approach:

  • These findings are still high-signal because the inputs cross trust boundaries into filesystem paths and mutable object/array writes
  • Fixing at the shared boundary points covers all current callers without broad rewrites or new dependencies

Tested:

  • npm run verify: passed
  • npx vitest run src/agents/tests/task-manager.test.ts src/extensions/tests/extensions.test.ts src/utils/tests/security.test.ts: passed

[v0.57.10] - 2026-03-14

  • fix: reduce first CodeQL security backlog (security)

What was built/changed:

  • Added shared security helpers for HTML escaping, URL parsing/matching, root-contained path resolution, and lightweight route rate limiting
  • Fixed the flagged bearer-token ReDoS, new-tab DOM XSS, and Google Photos OAuth callback reflected XSS/exception HTML sink
  • Replaced substring-based URL trust checks in main-process auth/search heuristics with URL/hostname parsing
  • Added root-containment path validation for sessions, workflow templates, Chrome import, site/form memory, and extension update paths
  • Added targeted rate limits to the currently flagged sensitive or high-cost API routes plus a global authenticated API ceiling
  • Added focused utility tests for the new security helpers

Why this approach:

  • Prioritizes exploitable sinks and high-signal trust-boundary flaws before broader CodeQL noise
  • Uses structural validation (URL objects, textContent/escaping, root containment) instead of substring checks or superficial filters
  • Avoids new dependencies and keeps behavior changes narrow to the flagged risk surfaces

Tested:

  • npm run verify: passed
  • npx vitest run src/utils/tests/security.test.ts src/utils/tests/utils.test.ts: passed

[v0.57.9] - 2026-03-14

  • fix: split evaluate request destructuring

[v0.57.8] - 2026-03-14

  • fix: auto-wrap window.location navigation in /devtools/evaluate

Prevents renderer blocking when navigating via evaluate endpoint. Previously, window.location assignments destroyed the JS context before evaluate could return, causing 30s timeouts and 'not responding' dialogs.

Now auto-detects window.location assignments and wraps them in setTimeout(0) to return immediately while navigation happens asynchronously.

[v0.57.7] - 2026-03-14

  • fix: auto-wrap window.location navigation in /devtools/evaluate to prevent renderer blocking

When evaluating window.location.href = "..." via the /devtools/evaluate endpoint, the page navigation destroys the JavaScript context before the evaluate call can return, causing 30-second timeouts and "application not responding" dialogs. Now automatically wraps such expressions in setTimeout(() => {...}, 0) to allow immediate return while navigation happens asynchronously in the background.

[v0.57.6] - 2026-03-14

  • fix: tolerate missing recording audio source

[v0.57.5] - 2026-03-14

  • fix: prepare public developer preview (repo)

[Unreleased]

  • fix: reduce the first public CodeQL security backlog

Hardens the highest-signal CodeQL findings first: bearer token parsing no longer uses the flagged backtracking regex, the new-tab and Google Photos callback surfaces stop routing untrusted data through HTML sinks, Google/auth/search URL checks now parse URLs structurally instead of relying on substring tests, and the first path-traversal cluster now resolves file paths inside trusted roots for sessions, workflow templates, Chrome import, site/form memory, and extension updates. Adds a small in-memory API rate limiter for the currently flagged high-cost and high-sensitivity routes.

  • fix: make application screenshot capture fall back cleanly when macOS window capture metadata is unavailable
  • fix: tolerate missing screen audio source lookup during application recording setup
  • docs: align public-facing repo docs for developer preview and first-party OpenClaw positioning

[v0.57.4] - 2026-03-09

  • fix: use screen source for system audio capture

Window sources don't include audio on macOS. Now fetches both a window source (for video) and a screen source (for audio) from desktopCapturer. The screen source captures all system audio via ScreenCaptureKit.

[v0.57.3] - 2026-03-09

  • fix: force 30fps output in ffmpeg conversion

MediaRecorder WebM has variable timestamps that ffmpeg misinterprets as 1000fps, making the MP4 unplayable in QuickTime. Adding -r 30 forces correct framerate.

[v0.57.2] - 2026-03-09

  • fix: check Screen Recording permission before capture on macOS

Prevents renderer crash by checking systemPreferences.getMediaAccessStatus before attempting desktopCapturer. Shows user-friendly alert with instructions to enable permission in System Settings.

[v0.57.1] - 2026-03-09

  • fix: Linux video recorder Wayland/Pipewire compatibility

What was built/changed:

  • Use native getDisplayMedia() on Linux instead of desktopCapturer to avoid Wayland screencast portal conflicts
  • Add try/catch error handling in get-desktop-source IPC handler to prevent renderer crashes
  • Fix stop button event bubbling (stopPropagation) to prevent double-click triggering fullscreen
  • Add debug logging for audio track detection
  • Platform-aware audio handling (Linux uses getDisplayMedia audio, macOS/Windows use desktopCapturer)

Why this approach:

  • Electron's desktopCapturer API has known issues with Wayland/Pipewire portals
  • Native getDisplayMedia() is the web standard and works reliably on Wayland
  • Prevents renderer crashes from unhandled IPC errors

Tested:

  • Video recording on Linux/Wayland: works (video + mic audio)
  • Stop button: works (single click + Esc shortcut)
  • ffmpeg MP4 conversion: works
  • Known limitation: Tab/webview audio not captured on Linux due to Electron process isolation (documented in TODO.md)

[v0.57.0] - 2026-03-09

  • feat: built-in video recorder with Application and Region modes

Replaces AudioCaptureManager with VideoRecorderManager. Uses desktopCapturer + MediaRecorder → WebM → ffmpeg → MP4 pipeline.

[v0.56.0] - 2026-03-09

  • feat: implement renderer-side video recorder with region crop and audio

[v0.55.0] - 2026-03-09

  • feat: add Record Application/Region to screenshot menu and recording IPC handlers

[v0.54.0] - 2026-03-09

  • feat: add recording overlay bar HTML and CSS

[v0.53.0] - 2026-03-09

  • feat: expose recording APIs in preload bridge

[v0.52.0] - 2026-03-09

  • feat: add VideoRecorderManager, remove AudioCaptureManager

[v0.51.3] - 2026-03-08

  • fix: spell-check tab-snoozing section in opera research doc

Fix typos: "hart" → "heart", "or" → "of", "if" → "as", "then" → "than", and garbled sentence about sleeping mode.

[v0.51.2] - 2026-03-08

  • fix: correct region overlay bleed and application screenshot color profile

Region mode: wait two animation frames after hiding the selection overlay before capturing, so the red tint no longer leaks into the saved image.

Application mode: use macOS native screencapture instead of Electron's capturePage().toPNG(), which didn't embed the Display P3 color profile, causing color shifts in saved files and Apple Photos.

[v0.51.1] - 2026-03-08

  • fix: use native screenshot mode menu in shell

What was built/changed:

  • Modified files: src/preload.ts, src/ipc/handlers.ts, shell/js/wingman.js, shell/index.html, shell/css/browser-shell.css, CHANGELOG.md, TODO.md
  • Replaced the renderer-side screenshot mode popup with a native Electron menu triggered from the toolbar camera button
  • Removed dead screenshot popup markup and styles from the shell

Why this approach:

  • The native menu is more reliable than the custom renderer popup and avoids a second fragile overlay path for a core capture workflow

Tested:

  • npx tsc: zero errors
  • npx vitest run src/draw/tests/overlay.test.ts: all tests pass
  • npm run verify: passed
  • Manual: app launch reached Electron startup logs; user restart still required to load the new preload/renderer code

[v0.51.0] - 2026-03-08

  • feat: expand screenshot workflows with google photos and capture modes
  • fix: use native screenshot mode menu in shell

[v0.50.0] - 2026-03-08

  • feat: add HAR export for network inspector

[v0.49.0] - 2026-03-08

  • feat: add quick links shortcut on new tab

[v0.48.0] - 2026-03-08

  • feat: add quick link actions to context menus

[v0.47.0] - 2026-03-08

  • feat: make new tab quick links configurable

[v0.46.0] - 2026-03-08

  • feat: notify when wingman replies with panel closed

[v0.45.3] - 2026-03-08

  • fix: harden extension update version comparison

[v0.45.2] - 2026-03-08

  • fix: remove tandem extension header helper dependency in 1password patches

[v0.45.1] - 2026-03-08

  • fix: english consistency cleanup pass 2 (ui-copy)

What was built/changed:

  • Modified files: CHANGELOG.md, shell/*.html, shell/js/main.js, shell/js/shortcuts.js, shell/css/main.css, src/api/routes/browser.ts, src/api/routes/sessions.ts, src/api/routes/sidebar.ts, src/api/tests/routes/browser.test.ts, src/bridge/context-bridge.ts, src/config/manager.ts, src/downloads/manager.ts, src/headless/manager.ts, src/mcp/server.ts, src/sidebar/manager.ts, src/watch/watcher.ts
  • Translated remaining first-party Dutch UI strings, placeholders, alerts, comments, bookmark manager copy, ClaroNote states, and related test expectations
  • Left compatibility-sensitive identifiers and locale/config values unchanged where translation would risk regressions

Why this approach:

  • Limits the pass to textual consistency changes and avoids renaming routes, persisted keys, IPC surfaces, CSS/DOM identifiers, or browser-fingerprint settings

Tested:

  • npm run compile: passed
  • npx vitest run: fails with pre-existing unrelated failures in src/tabs/tests/tabs.test.ts, src/extensions/tests/action-polyfill.test.ts, src/api/tests/routes/agents.test.ts, src/api/tests/routes/browser.test.ts (unrelated GET /links case), and src/api/tests/routes/security.test.ts

[v0.45.0] - 2026-03-08

  • feat: add session fetch relay and security containment design

Add POST /sessions/fetch endpoint for same-origin API calls via tab context, with auth header safety rails, timeout handling, and full test coverage. Include security containment review design doc.

  • fix: repository-wide English consistency cleanup pass 2

Translate remaining first-party Dutch text outside Markdown in shell UI copy, placeholders, comments, notifications, and related test expectations. Keep compatibility-sensitive identifiers, locale codes, and persisted/config surfaces unchanged where translation would be risky.

[v0.44.88] - 2026-03-08

Added

  • Session fetch relay (src/api/routes/sessions.ts) — added POST /sessions/fetch so the local API can execute same-origin requests inside the selected tab context, reuse the browser session for authenticated SPA/API calls, and return the response payload without exposing auth headers or tokens
  • Route coverage (src/api/tests/routes/sessions.test.ts, src/api/tests/helpers.ts) — added focused tests for successful tab-context fetches, tabId selection, JSON body serialization, timeout handling, and the new header/origin safety rails

Technical Details

  • function registerSessionRoutes() now validates request method, blocks direct auth/cookie/sec/proxy header injection, resolves relative URLs against the active tab URL, and rejects cross-origin targets before executing page-context fetch()
  • The relay runs through webContents.executeJavaScript() with a 15-second abort timeout, preserves page cookies and any app-level window.fetch wrappers already present in the tab, and normalizes JSON responses plus response headers into a stable API envelope
  • Verification: npm run compile passed; npx vitest run src/api/tests/routes/sessions.test.ts passed; npm start and manual curl validation were not run in this session

[v0.44.87] - 2026-03-07

Changed

  • Curated security feed expansion (src/security/blocklists/updater.ts, src/security/types.ts) — expanded the browser-core blocklist manifest with OpenPhish plus high-confidence ThreatFox domain and URL feeds, added explicit source signal/scope metadata, and kept new sources limited to phishing and malware intelligence instead or ad/tracker mega-lists
  • Structured feed filtering (src/security/blocklists/updater.ts) — taught the shared CSV/JSON parser layer to filter records by typed field criteria and to recognize comment-prefixed CSV header rows so mixed IOC exports can stay domain/url-first without feed-specific code paths
  • Phase 4 regression coverage (src/security/tests/blocklist-updater.test.ts) — added focused tests for the expanded source cadence folder and ThreatFox CSV filtering behavior

Technical Details

  • class BlocklistUpdater now advertises six runtime blocklist sources through the shared manifest: URLhaus, Phishing Database, OpenPhish, ThreatFox domains, ThreatFox URLs, and the existing StevenBlack legacy carryover source
  • ThreatFox entries are constrained to high-confidence domain/url IOCs before they ever reach NetworkShield, which keeps the current domain-first lookup model intact and leaves CIDR/range blocking out or scope
  • Verification: npm run compile passed; npx vitest run src/security/tests/blocklist-updater.test.ts passed; full npx vitest run still reports unrelated pre-existing failures in src/tabs/tests/tabs.test.ts and src/extensions/tests/action-polyfill.test.ts; npm start plus curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8765/security/status succeeded and returned the expanded source set

[v0.44.85] - 2026-03-07

Changed

  • Tiered security blocklist scheduler (src/security/security-manager.ts, src/security/blocklists/updater.ts, src/security/security-db.ts) — replaced the single 24-hour refresh rule with per-source hourly/daily/weekly cadence, persisted freshness and failure metadata per feed, and prevented overlapping scheduled refresh runs
  • Security freshness visibility (src/security/routes.ts, src/security/db-blocklist.ts) — exposed per-source blocklist freshness through /security/status and /security/blocklist/stats, and aligned database lastUpdate reporting with persisted refresh metadata instead or the request timestamp
  • Phase 3 regression coverage (src/security/tests/blocklist-updater.test.ts, src/api/tests/routes/security.test.ts) — added focused tests for due-source selection, per-source failure isolation, and freshness/status responses

Technical Details

  • class BlocklistUpdater now owns source cadence checks, updates only due feeds during scheduled runs, records lastUpdated / lastAttempted / failure state per source, and reloads NetworkShield only after at least one successful source refresh
  • class SecurityManager now schedules hourly freshness checks while serializing queued refresh runs so stale-source checks cannot overlap into concurrent downloads
  • Verification: npm run compile passed; npx vitest run src/security/tests/blocklist-updater.test.ts src/api/tests/routes/security.test.ts passed; full npx vitest run still reports unrelated pre-existing failures in src/tabs/tests/tabs.test.ts and src/extensions/tests/action-polyfill.test.ts; npm start plus curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8765/security/status succeeded and returned per-source freshness data

[v0.44.83] - 2026-03-07

Changed

  • Security blocklist fast-start hydration (src/security/network-shield.ts, src/security/security-manager.ts) — replaced synchronous startup blocklist parsing with a snapshot-first load path, queued background hydration, and atomic in-memory swaps so Tandem becomes usable before cached feeds finish rebuilding
  • Hydration regression coverage (src/security/tests/network-shield-hydration.test.ts) — added focused coverage for snapshot boot, no-clear reload behavior, cached hydrate promotion, and snapshot persistence refresh

Technical Details

  • class NetworkShield now boots from startup-snapshot.json when available, keeps DB-backed blocklist checks live during hydration, and rebuilds replacement Set instances before swapping them into request checks
  • Background hydration yields between cached sources, serializes overlapping reload requests, and persists a last-known-good snapshot plus blocklist metadata only after the replacement state is complete
  • Verification: npm run compile passed; npx vitest run src/security/tests/ passed; npm start reached API/UI readiness before blocklist hydration completed and later logged an atomic hydrate completion with 794198 domains / 1887 IP origins; full npx vitest run still reports unrelated pre-existing failures in src/tabs/tests/tabs.test.ts and src/extensions/tests/action-polyfill.test.ts

[v0.44.81] - 2026-03-07

Changed

  • Security blocklist parser foundation (src/security/types.ts, src/security/blocklists/updater.ts, src/security/network-shield.ts) — replaced hardcoded per-file parser branches with a shared source manifest and parser layer that supports legacy text feeds plus declarative JSON and CSV sources without changing the current security feed set

Technical Details

  • Added typed blocklist parser/source definitions so the updater and NetworkShield can use the same parser contract for hosts, domain lists, URL lists, JSON records, and CSV columns
  • Moved cached blocklist loading onto the same BLOCKLIST_SOURCES manifest used for downloads, including stable cache filenames for the existing URLhaus, phishing, and Steven Black feeds
  • Shared URL-list safe-domain filtering and IP-origin extraction across both update-time parsing and runtime in-memory loading to keep request checks aligned ahead or the fast-start snapshot phase
  • Verification: npm run compile passed; npm start plus curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8765/security/status succeeded; full npx vitest run still reports unrelated pre-existing failures in src/tabs/tests/tabs.test.ts and src/extensions/tests/action-polyfill.test.ts

[v0.44.80] - 2026-03-07

Changed

  • Automatic containment responses (src/security/security-manager.ts, src/security/guardian.ts, src/security/script-guard.ts, src/security/behavior-monitor.ts, src/main.ts) — turned critical script and runtime detections into real containment actions that quarantine the affected tab, force strict mode, lower trust, and preserve an incident evidence snapshot for later review
  • Shell-side recovery messaging (src/main.ts) — routed containment incidents through the existing emergency-stop shell path and added a native warning dialog so Robin gets an immediate explanation plus a recovery instruction without exposing any UI inside the page

Technical Details

  • class Guardian now supports per-webContents quarantine so contained browsing tabs fail closed on subsequent network activity without widening trust for extension, sidebar, or shell traffic
  • class SecurityManager now records bounded containment incidents with parsed-script and recent resource evidence, downgrades the affected domain, and exposes the incidents for follow-up review
  • class BehaviorMonitor can terminate execution for the affected tab when miner-like WASM and CPU behavior trips a critical threshold
  • Verification: npm run compile passed; full npx vitest run still reports unrelated pre-existing failures in src/tabs/tests/tabs.test.ts and src/extensions/tests/action-polyfill.test.ts

[v0.44.78] - 2026-03-07

Changed

  • Extension trust scoping (src/extensions/manager.ts, src/api/server.ts, src/api/routes/extensions.ts) — added explicit trusted / limited / unknown extension trust levels and route-specific helper scopes so extension-origin callers must present the right permissions for active-tab, webNavigation, identity, and native messaging helpers
  • Native messaging boundary checks (src/extensions/native-messaging.ts, src/extensions/nm-proxy.ts, src/main.ts) — applied the same trust decision to the native messaging HTTP and WebSocket bridges, validated host manifests against allowed extension IDs plus known runtime/CWS host mappings, and bound bridge audit logs to the calling extension identity
  • Tests (src/api/tests/server-auth.test.ts, src/extensions/tests/trust.test.ts, src/extensions/tests/native-messaging.test.ts, src/api/tests/routes/extensions.test.ts) — added focused coverage for scoped helper auth, bridge identity mismatch rejection, and native messaging host allowlist enforcement

Technical Details

  • Extension-origin helper routes now evaluate a central ExtensionManager.evaluateApiRouteAccess() decision instead or relying on a generic "installed extension" allow path
  • Tandem records extension helper allow/deny decisions with extension identity, trust level, and route scope in the API/NM proxy logs
  • POST /extensions/identity/auth now resolves installation status through the same stable extension identity lookup used by the trust model
  • Verification: npm run compile passed; focused extension/security Vitest coverage passed; npm start plus local curl checks succeeded; full npx vitest run still reports unrelated pre-existing failures in src/tabs/tests/tabs.test.ts and src/extensions/tests/action-polyfill.test.ts

[v0.44.76] - 2026-03-07

  • fix: isolate tabs per workspace

[v0.44.75] - 2026-03-07

  • fix: restore workspace tab switching

[v0.44.74] - 2026-03-07

  • fix: restore 1password extension helper auth

What was built/changed:

  • Modified files: src/api/server.ts, src/extensions/action-polyfill.ts
  • Added explicit extension ID propagation for local extension helper routes
  • Allowed trusted extension helper auth via origin, referer, or X-Tandem-Extension-Id for installed extensions
  • Upgraded existing on-disk 1Password patches so old active-tab/log bridge calls are rewritten on startup

Why this approach:

  • Electron does not consistently send Origin for extension background/service-worker fetches to localhost
  • 1Password lost its active-tab and frame bridge after API hardening because those helper requests no longer authenticated reliably
  • The fix stays scoped to trusted extension helper routes and installed extension IDs

Tested:

  • npm run compile: zero errors
  • npm start: 1Password helper patch upgrade applied at startup
  • Manual: user confirmed autofill flow works again

[v0.44.73] - 2026-03-07

  • fix: remove fs access from sandboxed preload

What was built/changed:

  • Modified files: src/preload.ts
  • Removed direct fs-based API token reads from the Electron preload
  • Kept the preload bridge minimal and Electron-safe under sandbox mode

Why this approach:

  • The preload was crashing on startup with 'module not found: fs'
  • That removed window.tandem entirely, which broke tabs, sidebar rendering, and internal shell controls
  • Local API auth now lives in the main-process session hook and token IPC, not in the preload filesystem path

Tested:

  • npm run compile: zero errors
  • npm start: preload no longer fails to load, shell starts without the preload fs crash

[v0.44.72] - 2026-03-07

  • fix: normalize shell API auth header override

What was built/changed:

  • Modified files: src/main.ts
  • Normalized Authorization headers before injecting the shell bearer token for local file:// requests

Why this approach:

  • Chromium can carry both authorization and Authorization header keys during request rewriting
  • Removing all casing variants before setting the bearer token ensures the shell auth hook actually wins

Tested:

  • npm run compile: zero errors
  • npm start: shell unauthorized requests for /sidebar/config, /workspaces, and /bookmarks no longer reproduced locally

[v0.44.71] - 2026-03-07

  • fix: restore shell API auth in session layer

What was built/changed:

  • Modified files: src/main.ts
  • Added a RequestDispatcher header hook for internal shell requests to the local Tandem API
  • Kept API hardening intact for non-shell callers

Why this approach:

  • Renderer-side token bootstrapping was still fragile across shell pages and internal webviews
  • Injecting the bearer token in Electron's request layer fixes shell/index.html and file:// webviews without re-trusting localhost broadly

Tested:

  • npm run compile: zero errors

[v0.44.70] - 2026-03-07

  • fix: bootstrap shell auth across internal pages

What was built/changed:

  • Modified files: src/main.ts, shell/index.html, shell/newtab.html, shell/settings.html, shell/bookmarks.html
  • New file: shell/js/api-auth.js
  • Registered the get-api-token IPC handler before the shell window loads so the first authenticated shell requests do not race startup
  • Added a shared shell auth bootstrap that wraps local Tandem API fetch calls and retries token acquisition during early startup
  • Enabled the same auth bootstrap on shell subpages such as newtab, settings, and bookmarks

Why this approach:

  • Internal shell pages were still failing closed because they either ran before the token IPC bridge was ready or were never using the shell auth wrapper at all; this makes the internal caller path explicit and consistent without reopening broad loopback trust

Tested:

  • npm run compile: zero errors
  • Manual runtime verification still requires restarting the already-running Tandem app

[v0.44.69] - 2026-03-07

  • fix: fetch shell API token via IPC

What was built/changed:

  • Modified files: src/ipc/handlers.ts, src/preload.ts, shell/index.html
  • Added an IPC handler for reading the local API token from the main process
  • Switched the preload getApiToken bridge to use ipcRenderer.invoke instead or relying on preload-side file access
  • Updated the shell bootstrap fetch wrapper to lazily await and cache the token before authenticating local Tandem API requests

Why this approach:

  • The shell page still emitted unauthenticated requests because token retrieval in the sandboxed renderer path was not reliable; fetching the token from the main process makes the page-world auth wrapper deterministic

Tested:

  • npm run compile: zero errors
  • Manual runtime still requires restarting the already-running Tandem app to load the new preload and shell bootstrap

[v0.44.68] - 2026-03-07

  • fix: restore shell and extension API auth

What was built/changed:

  • Modified files: shell/index.html, src/preload.ts, src/api/server.ts
  • Added a shell bootstrap that patches page-world fetch for local Tandem API calls using the preload-exposed bearer token
  • Exposed getApiToken() from preload so shell scripts can recover the local API token consistently
  • Broadened trusted extension ID matching so runtime IDs and on-disk extension IDs both satisfy the Phase 1 extension auth checks

Why this approach:

  • The earlier preload-only fetch patch ran in the isolated preload world, but the shell page was still issuing unauthenticated requests from the page world; this fix authenticates the actual caller while preserving the hardened API boundary

Tested:

  • npm run compile: zero errors
  • Manual runtime verification still requires restarting the already-running Tandem app to load the updated preload and shell bootstrap

[v0.44.67] - 2026-03-07

  • fix: restore shell auth for local API calls

What was built/changed:

  • Modified files: src/preload.ts
  • Exposed the local API token to the shell and wrapped shell-side fetch so requests to the Tandem API automatically include Authorization: Bearer unless an explicit auth header is already present

Why this approach:

  • Keeps the Phase 1 API hardening intact while restoring authenticated shell access for sidebar, chat polling, and other local UI calls that were previously relying on implicit loopback trust

Tested:

  • npm run compile: zero errors
  • Manual runtime still requires restarting the already-running Tandem app so the updated preload script is loaded

[v0.44.66] - 2026-03-07

  • fix: strengthen outbound containment policy (security-hardening)

What was built/changed:

  • Modified files: src/security/guardian.ts, src/security/outbound-guard.ts, src/security/types.ts, src/security/tests/gatekeeper-enforcement.test.ts, src/security/tests/outbound-containment.test.ts, package.json, CHANGELOG.md
  • Added richer outbound decision metadata so Guardian logs explain why mutating requests and WebSocket upgrades were allowed, flagged, held, or blocked
  • Tightened mode-sensitive containment for unknown WebSocket endpoints, first-visit mutating destinations, and trusted-to-untrusted cross-origin transitions, while exempting same-site cross-subdomain traffic to keep balanced mode usable
  • Added focused tests for the new outbound containment policies and Guardian enforcement path

Why this approach:

  • Keeps Phase 4 scoped to the outbound decision layer while using the existing Gatekeeper hold/block path for higher-risk cases instead or inventing a separate enforcement mechanism

Tested:

  • npm run compile: zero errors
  • npx vitest run src/security/tests/outbound-containment.test.ts src/security/tests/gatekeeper-enforcement.test.ts: all 11 tests pass
  • npx vitest run: still fails on unrelated pre-existing suites in src/extensions/tests/action-polyfill.test.ts and src/tabs/tests/tabs.test.ts
  • Manual: npm start succeeded, initialized the security stack and API on 127.0.0.1:8765, and curl http://127.0.0.1:8765/status returned ready state

[v0.44.65] - 2026-03-07

  • fix: expand per-tab runtime security coverage (security-hardening)

What was built/changed:

  • Modified files: src/devtools/manager.ts, src/ipc/handlers.ts, src/main.ts, src/security/behavior-monitor.ts, src/security/script-guard.ts, src/security/security-manager.ts, package.json, CHANGELOG.md
  • Added multi-tab CDP attachment state in DevToolsManager so security code can target specific webContents without stealing the primary active-tab session
  • Moved ScriptGuard and BehaviorMonitor runtime state to per-tab maps and added explicit tab created/navigated/closed lifecycle handling in SecurityManager and main process wiring

Why this approach:

  • Expands security coverage to live background/restored tabs while preserving the existing active-tab CDP APIs used by the rest or the browser

Tested:

  • npm run compile: zero errors
  • npx vitest run: fails on pre-existing unrelated suites in src/extensions/tests/action-polyfill.test.ts and src/tabs/tests/tabs.test.ts
  • Manual: npm start succeeded and initialized the security stack plus API on 127.0.0.1:8765

[v0.44.63] - 2026-03-07

  • fix: enforce gatekeeper decisions for risky requests (security-hardening)

What was built/changed:

  • Modified files: src/network/dispatcher.ts, src/security/guardian.ts, src/security/gatekeeper-ws.ts, src/security/types.ts, src/security/tests/gatekeeper-enforcement.test.ts
  • Gatekeeper enforcement: async onBeforeRequest support in RequestDispatcher, explicit Gatekeeper decision classes, request holds for risky first-visit navigations, deny-on-timeout handling for strict low-trust scripts and suspicious downloads, and explicit fallback behavior when Gatekeeper is disconnected or saturated
  • Logging/tests: Gatekeeper queue and resolution logs now record hold/allow/block/timeout states, and focused tests cover async request holds plus timeout policy behavior

Why this approach:

  • Keeps balanced browsing usable by limiting request holds to selected risky cases while making stricter paths fail closed instead or silently defaulting to allow

Tested:

  • npm run compile: zero errors
  • npx vitest run src/security/tests/gatekeeper-enforcement.test.ts: all 4 tests pass
  • npx vitest run: still fails on unrelated pre-existing suites in src/extensions/tests/action-polyfill.test.ts and src/tabs/tests/tabs.test.ts
  • Manual: npm run dev started successfully, initialized the security stack and API on 127.0.0.1:8765
  • Curl: GET /status returned ready state from the running app

[v0.44.62] - 2026-03-07

  • fix: tighten local API auth boundary (security-hardening)

What was built/changed:

  • Modified files: src/api/server.ts, src/api/routes/extensions.ts, src/extensions/nm-proxy.ts, src/main.ts, package.json, CHANGELOG.md, docs/implementations/security-hardening/LEES-MIJ-EERST.md
  • New API behavior: bearer auth required for normal HTTP routes, explicit trusted-extension allowlist for extension helper routes, query-string token auth removed, native-messaging WS upgrade now validates installed extension origins

Why this approach:

  • Replaces blanket loopback trust with an explicit caller model while keeping only the minimum extension-specific bridges outside bearer auth

Tested:

  • npm run compile: zero errors
  • npx vitest run: fails on pre-existing unrelated suites in src/extensions/tests/action-polyfill.test.ts and src/tabs/tests/tabs.test.ts
  • Manual: npm run dev attempted, but a user-run Tandem process already held 127.0.0.1:8765
  • Curl: isolated TandemAPI on 127.0.0.1:8876 returned 401 for unauthenticated /tabs/list, 200 with bearer token, 401 for query-token auth, and 401 for /extensions/active-tab without a trusted extension origin

[v0.44.60] - 2026-03-07

  • docs(workflow): add reusable multi-phase execution templates

Added generic templates for self-tracking multi-phase implementation tracks and for universal session prompts, so the same workflow used for security hardening can be reused for future larger feature or hardening efforts.

Updated AGENTS.md to explicitly point maintainers and coding sessions to this template pattern whenever work spans multiple sessions.

[v0.44.59] - 2026-03-07

  • docs(security): add self-tracking phase log and universal session prompt

Updated the security hardening handoff guide so each future session can detect the next unfinished phase automatically, record its own completion state, and leave a structured handoff for the next session.

Also added a reusable universal session prompt document for the security hardening track so new sessions can be started with the same scoped workflow without manually naming the phase each time.

[v0.44.58] - 2026-03-07

  • docs(security): add phased security hardening roadmap

Added a dedicated security hardening planning track with one master design document plus six implementation phase documents covering API auth, Gatekeeper enforcement, per-tab monitoring, outbound containment, extension trust, and automatic containment actions.

The goal or the new docs set is to let future sessions execute the security work in bounded steps without relying on chat context or losing sight or the overall hardening objective.

[v0.44.57] - 2026-03-07

  • docs(repo): translate AGENTS guide to English

Translated the internal AGENTS.md workflow guide into English while keeping the same project rules, anti-detection constraints, development workflow, and reporting expectations. This keeps the maintainer instructions consistent with the rest or the repository-facing documentation cleanup.

[v0.44.56] - 2026-03-07

  • docs(repo): clarify OpenClaw positioning and classify maintainer workflow docs

Updated the top-level project docs to position Tandem more clearly as the browser environment built for OpenClaw, while keeping Wingman as the user-facing name for the right-side collaboration panel and documenting Kees as the default OpenClaw persona.

Cleaned up several docs/ entry points so contributor workflow files such as CLAUDE.md and historical LEES-MIJ-EERST.md packs are clearly treated as maintainer artifacts rather than the primary public documentation surface. Also modernized electron-builder output naming with an explicit tandem-browser-${version}-${arch}.${ext} artifact pattern and macOS package targets.

[v0.44.55] - 2026-03-07

  • docs(repo): add GitHub community files and separate internal planning docs

Added GitHub issue templates and CODEOWNERS, introduced docs/README.md plus docs/internal/README.md, and moved the top-level roadmap/status planning files under docs/internal/ to keep the public docs surface cleaner.

Updated the README with a clearer UI summary including the left sidebar capabilities, removed personal contact data from package metadata and setup scripts, and revised the security reporting doc away from direct personal email.

[v0.44.54] - 2026-03-07

  • docs(repo): add public-facing repo docs and improve release metadata

Rewrote the README around public-facing positioning and onboarding, added standard top-level LICENSE, CONTRIBUTING.md, and SECURITY.md files, and marked the remaining root internal workflow documents as internal-only.

Updated package.json metadata with repository, homepage, issue tracker, keywords, and platform build categories so the repo and release configuration look more complete ahead or a future public release.

[v0.44.53] - 2026-03-07

  • chore(repo): remove local artifacts and stale release metadata

Removed tracked local Playwright console logs, ad-hoc root-level scratch test files, and an unused screenshot asset from the repository root so the workspace is cleaner for future public release preparation.

Updated top-level documentation to stop hardcoding stale version numbers and aligned release-facing references to point at package.json and CHANGELOG.md instead. Also refreshed the architecture overview in PROJECT.md to reference Electron 40.

[v0.44.52] - 2026-03-07

  • fix(extensions): add storage.session + notification state shims for 1Password

Extended the 1Password action polyfill with a chrome.storage.session shim in the service worker, including get, set, remove, clear, and storage.onChanged support. This gives the extension an ephemeral runtime store for policy calculation and other short-lived state that Electron does not expose reliably in this context.

Upgraded the notification stub from a pure no-op to an in-memory implementation that tracks created notifications and allows update() / clear() calls to succeed. This removes a class or replaceNotification: messenger-error failures when 1Password updates passkey and inline notification state.

[v0.44.51] - 2026-03-07

  • fix(extensions): retry 1Password USO tab messages without frame targeting

Adjusted /extensions/web-navigation/frames so the top-level frame is exposed as Chrome-style frameId: 0 instead or Electron's raw routing id. This makes the frame tree look like a normal Chrome tab to extensions that reason about the main frame separately from subframes.

Updated the extension action polyfill to intercept chrome.tabs.sendMessage() and strip unsupported documentId targeting. For 1Password's uso-* messages, the polyfill now retries once without frameId / documentId when Electron rejects the targeted delivery, so inline autofill UI can still talk to the top-frame content script on normal login forms.

[v0.44.50] - 2026-03-07

  • fix(extensions): bridge webNavigation frame data for 1Password autofill

Added /extensions/web-navigation/frames and /extensions/web-navigation/frame so extension service workers can query the real frame tree or the active webview via Electron WebFrameMain data instead or getting an empty webNavigation stub.

Updated the 1Password action polyfill to route chrome.webNavigation.getAllFrames() and getFrame() through those endpoints, which should let the extension detect sign-in frames and target the correct input fields for autofill.

[v0.44.49] - 2026-03-07

  • chore(about): simplify About page — remove Dutch quote and Wingman subtitle, update tagline to "Built for your AI. Security included.", remove team credits line

[v0.44.48] - 2026-03-06

  • fix(extensions): add 1password7 NM host + migrate to session.extensions.loadExtension

Added com.1password.1password7 as a supported alias for the existing 1Password native messaging host so the NM proxy now resolves both host names to the same native helper binary and no longer treats the ...7 variant as missing.

Migrated extension loading from deprecated session.loadExtension() to session.extensions.loadExtension() in the extension loader and session loading paths, and updated the related inline documentation to match the Electron 40 API.

[v0.44.47] - 2026-03-06

  • fix(lifecycle): teardown managers and IPC listeners on window close

Added a central teardown path in src/main.ts that destroys all live managers, stops the API, clears the cookie flush interval, removes the tab-register IPC listener, and resets retained startup state when the main window closes or the app reactivates on macOS.

src/ipc/handlers.ts now removes every IPC channel and handler it re-registers, including window controls and is-window-maximized, so reactivation no longer stacks stale handlers bound to destroyed windows.

Guarded renderer sends for TaskManager event forwarding in src/main.ts and all renderer sends in src/voice/recognition.ts, preventing Object has been destroyed crashes during shutdown and reactivation.

[v0.44.46] - 2026-03-06

  • feat(tabs): shrink tabs dynamically when many are open, like Chrome

Tab items now use flex: 1 with min-width: 28px (was fixed 120px), so the tab strip compresses automatically as more tabs are added. Tab titles truncate with ellipsis at narrow widths; the favicon remains visible at all sizes.

[v0.44.45] - 2026-03-06

  • fix(stability): guard win.webContents calls with isDestroyed() checks

All win.webContents.send() calls in panel/manager.ts are now guarded with isDestroyed() checks, preventing 'TypeError: Object has been destroyed' crashes when the panel window closes while IPC events are still firing. Added early isDestroyed() check in tabs/manager.ts openTab(). Also fixed POST /execute-js to respect the tabId parameter instead or always targeting the active webcontents.

[v0.44.44] - 2026-03-06

  • fix(autofill): patch Kfj() and zj.getShortcuts() via action-polyfill

Two 1Password autofill errors eliminated:

  • Kfj() called browser.windows.getCurrent() which is undefined in Electron's Service Worker context, causing getItemDetails() to throw. Patched to always return false (Tandem never opens 1Password in a detached popup window).
  • zj.getShortcuts() called browser.commands.getAll() which throws when browser.commands is undefined in Electron. Guarded with early return {browserAction:"",lock:""} when browser.commands is absent. Both patches are now applied via the action-polyfill.ts patch pipeline (not by direct edits to background.js) so they survive extension reloads.

[v0.44.4] - 2026-03-04

  • fix: correct newtab.html path in ipc handlers

handlers.js compiles to dist/ipc/, so __dirname is dist/ipc/. The previous path.join(__dirname, '..', 'shell', 'newtab.html') resolved to dist/shell/newtab.html which does not exist.

Adding an extra '..' resolves to the correct shell/newtab.html at the project root, matching the path calculation used in main.ts.

[v0.44.3] - 2026-03-04

  • fix: prevent zombie tabs from renderer/main-process state drift

Root cause: when openTab() fails after createTab() has already added the webview and tabEl to the renderer's DOM and tabs Folder, the main process never registers the tab. Subsequent closeTab() calls return early (tab not in main-process Folder), leaving an uncloseable orphan in the tab strip.

A secondary cause: if the removeTab() IPC call throws during a normal closeTab(), the main-process tab entry was never deleted, leaving the tab stuck open from the main-process side.

Changes:

  • shell/js/main.js: add 15s timeout to createTab() dom-ready Promise; on timeout, clean up webview/tabEl/tabs Folder entry before rejecting. Expose getTabIds() and cleanupOrphan() on window.__tandemTabs for reconciliation from the main process.
  • src/tabs/manager.ts: catch createTab() failures in openTab() and call cleanupOrphan() in the renderer before rethrowing, preventing partial renderer state from persisting. Make removeTab() IPC call in closeTab() best-effort: log the error but always delete from this.tabs so the tab cannot become permanently uncloseable due to a renderer IPC failure. Add reconcileWithRenderer(): queries renderer tab IDs, removes any orphans (renderer knows tab, main process does not) via cleanupOrphan().
  • src/ipc/handlers.ts: in tab-close IPC handler, only emit tab-closed events when the tab was actually tracked. If closeTab() returns false, run reconcileWithRenderer() to clean up any renderer orphan.
  • src/api/routes/tabs.ts: add POST /tabs/reconcile endpoint for on-demand reconciliation via the API.
  • src/main.ts: after restoreSessionTabs() completes, run reconcileWithRenderer() to remove any orphans left by failed restores.

Tests: 36 new test cases covering openTab() cleanup on failure, closeTab() robustness against IPC errors, and reconcileWithRenderer() behaviour (orphan removal, sync state, getTabIds() failure handling). All 947 tests pass.

[v0.44.2] - 2026-03-02

  • fix: ACTUALLY use sidebar panel for About (was still using BrowserWindow!)

The hamburger menu was still creating a BrowserWindow for About instead or opening the sidebar panel. Now it properly sends 'show-about' event which triggers renderAboutPanel().

THIS time it's really fixed. Sorry for the confusion!

[v0.44.1] - 2026-03-02

  • fix: About only via hamburger menu, no sidebar icon

Changes:

  • Removed 'about' from sidebar config (no icon shown)
  • Hamburger menu → About Tandem Browser now calls renderAboutPanel() directly
  • Opens sidebar panel with frosted glass effect
  • No separate icon in sidebar needed

About is now ONLY accessible via ☰ → Tandem → About Tandem Browser

[v0.44.0] - 2026-03-02

  • feat: About as SIDEBAR PANEL with real frosted glass!

Finally! About now opens in the sidebar panel like Bookmarks/History/etc instead or as center overlay.

Changes:

  • Added 'about' item to sidebar config (order 19)
  • Added info-circle icon for About
  • renderAboutPanel() function renders content in sidebar-panel-content
  • activateItem('about') triggers panel open
  • Same frosted glass effect as ALL sidebar panels
  • Removed center overlay code completely

Now backdrop-filter WORKS because About is part or main window!

[v0.43.1] - 2026-03-02

  • fix: use EXACT onboarding overlay pattern for About

Copied exact structure from onboarding overlay instead or inventing my own:

  • .about-overlay with .visible class toggle
  • background: var(--surface) with backdrop-filter: blur(20px)
  • Same CSS variables and styling
  • Click outside to close

Now matches existing overlay pattern in codebase!

[v0.43.0] - 2026-03-02

  • feat: About as overlay with true frosted glass effect

Changed About from separate BrowserWindow to in-window overlay, matching bookmarks panel style:

  • Overlay div in main window (not separate BrowserWindow)
  • backdrop-filter now blurs actual page content behind it
  • Same visual effect as bookmarks/history panels
  • Click outside or X button to close
  • GitHub link opens new tab in Tandem

True frosted glass effect finally achieved!

[v0.42.0] - 2026-03-02

  • feat: frosted glass effect for About window

Applied same styling as sidebar panels:

  • backdrop-filter: blur(32px) + saturate + brightness
  • Semi-transparent dark background
  • Subtle border and shadow
  • Transparent BrowserWindow for backdrop effect

Matches the beautiful glass effect from bookmarks panel

[v0.41.9] - 2026-03-02

  • fix: remove duplicate onOpenUrlInNewTab listener

Was registering the listener twice, causing 2 tabs to open instead or 1 when clicking About links

[v0.41.8] - 2026-03-02

  • fix: remove duplicate onOpenUrlInNewTab handler

TypeScript error: object literal cannot have multiple properties with the same name

[v0.41.7] - 2026-03-02

  • fix: About window links open in new Tandem tab

Instead or opening in system browser (Chrome), links from the About window now open in a new tab within Tandem.

Added new IPC event 'open-url-in-new-tab' that triggers tab creation.

[v0.41.6] - 2026-03-02

  • fix: remove duplicate BrowserWindow import

BrowserWindow should be imported as value, not type

[v0.41.5] - 2026-03-02

  • fix: prevent crash on hamburger menu click

Issues fixed:

  • Remove require() calls in IPC handler (caused hang)
  • Use proper imports at top level
  • Add 'as const' to role types
  • Import shell from electron

The dynamic require() calls were blocking the event loop

[v0.41.4] - 2026-03-02

  • fix: TypeScript error in setWindowOpenHandler

Add explicit type for url parameter

[v0.41.3] - 2026-03-02

  • fix: About window branding and link behavior

Changes:

  • Co-Pilot Browser → Wingman Browser
  • Remove 'Tandem Repo' link (repo not public yet)
  • About window frameless on Linux (no native menubar)
  • External links open in system browser, not popup window

Fixes #3 issues reported by user

[v0.41.2] - 2026-03-02

  • fix: use _win instead or win in window control handlers

TypeScript error: variable is destructured as 'win: _win'

[v0.41.1] - 2026-03-02

  • fix: pinboard sync not loading on fresh device

When no local boards.json exists, load() creates an empty store with lastModified set to now. mergeFromSync() then skips the shared file because sharedTime < localTime (shared was written in the past).

Fix: if local has zero boards, always prefer the shared version regardless or timestamp. This ensures new devices pick up existing pinboards on first launch.

[v0.41.0] - 2026-03-02

  • feat: two-way sync — read shared data from Google Drive on startup

[v0.40.0] - 2026-03-02

  • feat: crash-safe session restore — continuous tab state persistence

[v0.38.0] - 2026-03-01

  • feat: pinboard appearance panel + masonry layout + inline editing

[v0.37.0] - 2026-03-01

  • feat: pin hover Edit/Remove actions + edit modal + Hydra Editor vendor bundle

[v0.36.1] - 2026-03-01

  • fix: responsive masonry columns + aspect-ratio card images, no stretch on wide panel

[v0.36.0] - 2026-03-01

  • feat: add text note editor to pinboard (pencil button + inline textarea)

[v0.35.1] - 2026-03-01

  • fix: show full text in quote/text pin cards, auto-height instead or clipping

[v0.35.0] - 2026-03-01

  • feat: realtime pinboard refresh via IPC after pin added from any context menu

[v0.34.0] - 2026-03-01

  • feat: OG metadata auto-fetch for thumbnails + masonry card layout fix (v0.33.1)

[v0.33.0] - 2026-03-01

  • feat: add 'Add to Pinboard' to tab context menu with board submenu + pin-flash animation

[v0.32.1] - 2026-03-01

  • fix: pinboards use showPrompt() instead or prompt(), add auth headers to all fetches

[v0.32.0] - 2026-03-01

  • feat: Pinboards — PinboardManager, REST API, sidebar panel, context menu (v0.32.0)

[v0.31.0] - 2026-03-01

  • feat: SyncManager — cross-device tab/history/workspace sync via shared folder

Add SyncManager that enables cross-device sync by writing device-specific data (tabs, history) and shared data (workspaces) to a configurable sync folder (Google Drive, iCloud, or any local path). Includes "Your Devices" section in the history sidebar panel, API endpoints for config/status/trigger, and debounced atomic writes to prevent corruption. Bumps to v0.30.0.

[v0.29.0] - 2026-03-01

  • feat: full Opera-style tab context menu with workspace icons, mute, duplicate, close actions

[v0.28.0] - 2026-03-01

  • feat: move tab to workspace via drag-and-drop and right-click context menu

[v0.27.0] - 2026-03-01

  • feat: Opera-style workspace icon picker, SVG strip icons, edit/delete UI

  • Replace emoji text inputs with 24 inline SVG icons (Heroicons outline style)

  • Inline create/edit sheet with 6-column icon grid picker (not floating modal)

  • Edit workspace: rename, change icon, delete with inline confirmation

  • Sidebar strip: SVG icons with indigo active / gray inactive styling

  • Data model: emoji field migrated to icon slug with graceful migration

  • Version bump to v0.26.0

[v0.25.1] - 2026-03-01

  • fix: replace native prompt/confirm with custom modal (Electron blocks native dialogs)

[v0.25.0] - 2026-03-01

  • feat: workspace manager + sidebar UI with tab filtering

Workspaces are named tab groups with emoji + color. Users can create, switch, and delete workspaces from the sidebar. Tab bar filters to show only the active workspace's tabs. Persisted to ~/.tandem/workspaces.json.

[v0.24.0] - 2026-03-01

  • feat: bookmarks sidebar panel with search and folder navigation

  • New bookmark panel plugin for the sidebar Bookmarks item

  • Renders full bookmark tree from Tandem /bookmarks API

  • Folders and URLs displayed with favicons (Google s2 service)

  • Click folder to navigate into it with breadcrumb trail

  • Click breadcrumb to jump back up the tree

  • Click URL to open in new tab via window.tandem.newTab()

  • Live search with 250ms debounce via /bookmarks/search API

  • Bookmarks tree cached after first load (no redundant fetches)

  • CSS: search input, breadcrumb nav, folder/url items, scrollable list

[v0.23.1] - 2026-03-01

  • fix: Calendar auth popup and shared Google session partition

  • Calendar webview now uses persist:gmail partition so one Google login covers both Calendar and Gmail (same account, same session)

  • Fix new-window handler to NOT preventDefault on auth URLs — lets setWindowOpenHandler in main.ts open real popup windows for Google auth (previously auth was loaded inside the webview where Google blocks it)

  • Auth URL patterns are specific (accounts.google.com etc.) to avoid interfering with in-app navigation in Telegram/Discord/etc.

  • Reload handler propagates gmail reload to calendar (shared partition)

[v0.23.0] - 2026-03-01

  • feat: panel reload button + auto-reload sidebar webview after Google auth

[v0.22.2] - 2026-03-01

  • fix: allow Google auth popups from sidebar webviews (Gmail/Calendar login)

[v0.22.1] - 2026-02-28

  • fix: stronger frosted glass — more transparent panel, header shows blur clearly

[v0.22.0] - 2026-02-28

  • feat: frosted glass overlay panel, smooth animation, closed on startup

[v0.21.3] - 2026-02-28

  • fix: clear inline panel width on close so panel actually collapses

[v0.21.2] - 2026-02-28

  • fix: panel resize max width = window width — no arbitrary limit

[v0.21.1] - 2026-02-28

  • fix: panel resize — drag cover blocks webview, handle inside bounds, max 900px

[v0.21.0] - 2026-02-28

  • feat: resizable sidebar panel with per-module width persistence

[v0.20.1] - 2026-02-28

  • fix: sidebar webviews navigate freely — prevent Telegram from opening in new tab

[v0.20.0] - 2026-02-28

  • feat: Telegram (and all messenger) webview panels with persistent sessions

[v0.19.1] - 2026-02-28

  • fix: group pin and close buttons together in panel header

[v0.19.0] - 2026-02-28

  • feat: sidebar panel pin/overlay toggle — push vs overlay mode

[v0.18.2] - 2026-02-28

  • fix: dark scrollbar for sidebar panel content

[v0.18.1] - 2026-02-28

  • fix: sidebar setup panel — title bug, English sections, cleaner icons

[v0.18.0] - 2026-02-28

  • feat: sidebar setup panel with per-item toggles

[v0.17.2] - 2026-02-28

  • fix: sidebar icon sizing — smaller icons, more breathing room

[v0.17.1] - 2026-02-28

  • fix: sidebar polish + lamp icon + config migration

[v0.17.0] - 2026-02-28

  • feat: sidebar 3-section layout + Google Calendar + Gmail

Aangepaste files:

  • shell/index.html: ocSidebar updated
    • ICONS uitgebreid with Google Calendar (blauw) and Gmail (rood)
    • WEBVIEW_URLS folder added for alle webview items
    • render() shows nu 3 sections: Workspaces / Communicatie / Utilities
    • Separators between the 3 sections
    • calendar + gmail krijgen brand-icon stijl (zoals messengers)
  • src/sidebar/manager.ts: DEFAULT_CONFIG has 14 items in 3 sections

Getest:

  • npx tsc: zero errors
  • npm start: 3 sections visible with separators
  • npx vitest run: alle tests slagen

[v0.16.1] - 2026-02-28

  • feat: sidebar 3-section layout + Google Calendar + Gmail

Aangepaste files:

  • shell/index.html: ocSidebar updated
    • ICONS uitgebreid with Google Calendar (blauw) and Gmail (rood)
    • WEBVIEW_URLS folder added for alle webview items
    • render() shows nu 3 sections: Workspaces / Communicatie / Utilities
    • Separators between the 3 sections
    • calendar + gmail krijgen brand-icon stijl (zoals messengers)
  • src/sidebar/manager.ts: DEFAULT_CONFIG has 14 items in 3 sections

Getest:

  • npx tsc: zero errors
  • npm start: 3 sections visible with separators
  • npx vitest run: alle tests slagen (pre-existing supertest failures ongewijzigd)

[v0.16.0] - 2026-02-28

  • feat: sidebar shell UI — icon strip, panel, narrow/wide/hidden, brand icons, Cmd+Shift+B

New files: no Aangepaste files:

  • shell/index.html: sidebar HTML added if first kind or .main-layout
    • .sidebar container with .sidebar-strip (icon strip) and .sidebar-panel
    • ocSidebar JS object: render, activateItem, toggleState, toggleVisibility, init
    • Keyboard shortcut Cmd+Shift+B toggle hidden/narrow
  • shell/css/main.css: sidebar CSS added
    • 3 standen: hidden (0px) / narrow (48px) / wide (180px)
    • Utility icons: outline Heroicons grijs
    • Messenger icons: colored brand SVG op colored ronde achtergrond
    • Active indicator: colored rounded square achter actief icon
    • Separator lijn between utility and messenger blok

Getest:

  • npx tsc: zero errors
  • npm start: sidebar visible, panel opens/closes, shortcuts werken
  • npx vitest run: alle tests slagen

[v0.15.3] - 2026-02-28

  • fix: sidebar config uses individuele messenger items + commit default

New files: no Aangepaste files:

  • src/sidebar/manager.ts: DEFAULT_CONFIG updated or 7 items (with 1 'messengers' group) to 12 items (6 utility + 6 individuele messengers: whatsapp/telegram/discord/slack/instagram/x)
  • git-hooks/post-commit: emoji-stripping added zodat emoji-prefixed commits (bijv '🗂️ feat:') correct if 'feat:' herkend be door auto-versioning
  • CHANGELOG.md: v0.15.2 entry uitgebreid with full details (files, endpoints, architectuur)
  • AGENTS.md: commit message default added with format, types, versie-rules and CHANGELOG format
  • package.json: versie gecorrigeerd to 0.15.2 (was blijven stand op 0.15.1 door emoji-bug in hook)

Getest:

  • npx tsc: zero errors
  • Manager geeft nu 12 items terug via GET /sidebar/config

[v0.15.2] - 2026-02-28

Added

  • Sidebar Infrastructuur (src/sidebar/) — foundation for alle sidebar features
    • src/sidebar/types.ts — SidebarState ('hidden'|'narrow'|'wide'), SidebarItem, SidebarConfig types
    • src/sidebar/manager.ts — SidebarManager class with load/save/getConfig/updateConfig/toggleItem/reorderItems/setState/setActiveItem
    • src/api/routes/sidebar.ts — 6 REST endpoints:
      • GET /sidebar/config — full config ophalen
      • POST /sidebar/config — config update (state, activeItemId)
      • POST /sidebar/items/:id/toggle — item enable/disable
      • POST /sidebar/items/:id/activate — panel openen or sluiten
      • POST /sidebar/reorder — drag-to-reorder (orderedIds array)
      • POST /sidebar/state — sidebar state change (hidden/narrow/wide)
    • 12 default sidebar items: workspaces, personal news, pinboards, bookmarks, history, downloads + whatsapp, telegram, discord, slack, instagram, x
    • Config persistent in ~/.tandem/sidebar-config.json

Gewijzigd

  • src/registry.tssidebarManager: SidebarManager added about ManagerRegistry interface
  • src/main.ts — SidebarManager instantiatie in startAPI() + cleanup in app.on('will-quit')
  • src/api/server.tsregisterSidebarRoutes(router, ctx) added
  • src/api/tests/helpers.ts — test helper updated for new manager
  • git-hooks/post-commit — emoji-prefix stripping zodat 🗂️ feat: correct if feat: herkend is

Architecture

  • Elke messenger (WhatsApp/Telegram/Discord/Slack/Instagram/X) gets own slot in sidebar — not grouped
  • Twee visual stijlen in UI (phase 2): outline Heroicons for utility, brand colored SVGs for messengers
  • Active indicator: colored rounded square achter actief icon (Opera-stijl)

[v0.15.1] - 2026-02-28

  • fix: About window now shows correct version

  • Removed broken preload-about approach

  • Version now hardcoded in shell/about.html (v0.15.0)

  • Post-commit hook updated to auto-update about.html on version bump

  • Cleaner and more reliable than runtime injection

[v0.15.0] - 2026-02-28

  • feat: add auto-versioning git hook + setup script

  • git-hooks/post-commit: auto-bump version + update CHANGELOG

  • setup-dev.sh: one-command dev environment setup

  • Configures core.hooksPath to use git-hooks/ (committed in repo)

  • Kees can run ./setup-dev.sh after next pull to enable hook

  • Ensures consistent versioning across all dev machines

[v0.14.3] - 2026-02-28

  • fix: About window improvements (height 650, auto-version from package.json)

[v0.14.2] - 2026-02-28

  • fix: correct path depth for About window (shell/about.html now loads)

[v0.14.1] - 2026-02-28

  • feat: auto-sync webhook.secret with OpenClaw hooks.token (cross-platform fix)

[v0.14.0] - 2026-02-27

  • Initial stable release with 19/19 items complete