Skip to content

feat: ship iOS simulator, signed CLI updates, and Chrome automation - #67

Merged
graseeel merged 45 commits into
devfrom
feat/independent-cli-updater
Aug 9, 2026
Merged

graseeel merged 45 commits into
devfrom
feat/independent-cli-updater

Conversation

@graseeel

@graseeel graseeel commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Scope

This PR intentionally carries the accumulated desktop work already present on the feature branch. It is not limited to the CLI updater.

  • adds the interactive iOS/iPadOS simulator panel on macOS, including live preview, touch/drag/typing, agent presence, annotations, media capture, system controls, preview suspension, and ownership-safe shutdown
  • adds provider login/model support and the related settings, transcript, window, and reliability fixes accumulated on the branch
  • embeds a verified Node.js runtime for every supported desktop target
  • installs and updates the Verboo CLI from separately signed upstream releases without bundling or modifying the CLI through the app updater
  • adds a guided first-install experience that blocks agent actions while the CLI is installed, keeps Settings available, and supports retry
  • keeps app and CLI update health independent while allowing both updates to be applied through one restart
  • renders turn failures as assistant transcript entries instead of losing their conversational context

The repository work is authorized by the Verboo owner. This PR does not present the desktop app as an official Verboo product.

Update and ownership boundaries

  • the app updater only replaces the application bundle
  • the CLI updater only writes under the app-data cli/ directory
  • the app release repository no longer selects or publishes CLI versions
  • production does not fall back to system Node, npm, or a globally installed CLI
  • CLI manifests are authenticated with the pinned upstream Minisign public key before parsing
  • archive target, compatibility, byte length, SHA-256, layout, and package metadata are validated before atomic activation
  • running turns retain immutable runtime leases; failed combined activation restores the previous CLI
  • runtime health, not only the presence of current.json, controls first-install gating and retry
  • macOS CLI-only updates relaunch the packaged app through LaunchServices with a visible main window

First-install behavior

  • missing or unhealthy CLI starts the dedicated bootstrap after the renderer is ready
  • progress is localized in Portuguese and English
  • prompt submission, side chat, and agent-start actions remain blocked during installation and the verified-success transition
  • Settings remains available from the installation card
  • offline, interrupted, or unhealthy installation can retry without an existing CLI or an app restart
  • later CLI updates are discovered automatically but never installed silently
  • a healthy CLI OAuth session and a validated API key remain supported ways to unlock the app

iOS simulator behavior

  • macOS only; simulator UI, backend commands, and MCP registration are absent on Windows and Linux
  • requires a compatible Xcode/iOS Simulator runtime
  • renderer consumes the local binary MJPEG stream directly instead of committing a new Base64 frame into React on every refresh
  • the backend retains stable raw frames for screenshots, annotations, and model vision
  • a cold WebDriverAgent startup has a dedicated 90-second allowance; normal readiness probes remain bounded at 45 seconds
  • generic app discovery, app launch, system actions, taps, drags, typing, and screenshots are available to the model without hardcoded app flows
  • screenshot images are emitted as MCP image content blocks, preventing CLI structured-content precedence from hiding vision input
  • simulator-use aurora and cursor remain visible from the first simulator tool until turn completion rather than flashing per action
  • hiding the panel suspends only preview work; reopening restores the attached session
  • detaching stops Verboo workers without shutting down externally booted devices
  • app exit shuts down only exact UDIDs in Verboo's ownership ledger
  • externally booted devices are labeled External and require an explicit named confirmation before shutdown
  • 30 fps remains the default; 60 fps is opt-in with the localized CPU/heat warning

Cross-platform authentication and credentials

  • primary Verboo CLI login is wired on macOS, Windows, and Linux
  • the CLI opens the browser with the platform handler and the app streams login URL/completion events without blocking the UI
  • Windows reads the upstream DPAPI ciphertext using the exact Base64 file format before calling Unprotect
  • Linux follows the upstream Secret Service/secret-tool lookup contract and retains the plaintext compatibility fallback
  • iOS simulator code remains macOS-exclusive; these CLI/API/provider fixes apply to every supported desktop platform

Verification

  • full renderer suite: 149 files, 1,592 tests passed
  • full Rust library suite: 1,250 passed, 1 ignored, 0 failed
  • simulator helper suites: 8 passed
  • Windows MSVC cross-check passed with cargo xwin check
  • TypeScript check, production Vite build, release Rust build, Developer ID signing, and DMG generation passed
  • packaged iOS 26.5 QA measured approximately 27-28 streamed fps
  • hidden preview parked its workers at 0.0% sampled CPU and reopened the existing session in 641 ms
  • cold WebDriverAgent readiness completed in about 49.7 seconds; a warm retry completed in about 36.2 seconds
  • direct user interaction appeared in under one second
  • the selected Ultra model discovered and launched Files through generic simulator tools
  • a one-screenshot visual test correctly identified the SpringBoard screen and visible dock/icons, proving the image reached the model path
  • simulator presence stayed continuous during tool use and cleared at turn completion
  • ending the simulation left no booted devices and no WebDriverAgent/xcodebuild worker
  • the reported iOS 27.0 beta Calendar crash was isolated to MobileCal: FrontBoard watchdog 0x8BADF00D after a 30-second startup stall in dyld_sim; Verboo remained alive and the stable iOS 26.5 runtime did not reproduce it
  • the latest commit passed renderer and Rust CI on macOS, Windows, and Linux, plus the Linux WebKitGTK build check

The local release script cannot sign the Tauri updater tarball without TAURI_SIGNING_PRIVATE_KEY; that private key remains only in protected CI. The locally produced .app and DMG are Developer ID signed, but the local tarball is intentionally not a publishable updater artifact.

Upstream readiness

  • upstream publisher PR merged: feat: publish signed desktop CLI artifacts verbeux-ai/code#17
  • signed upstream release assets are available for supported desktop targets
  • the CLI verification public key is pinned in the app; the private key remains only in the upstream repository's protected release environment

Chrome background automation and MCP

  • updates the Chrome extension to 0.3.2
  • runs agent browser work inside one durable unfocused workspace window instead of moving the user’s active tab or stealing focus
  • persists the workspace lease across extension service-worker restarts and scopes every tool, tab action, screenshot, and console read to that lease
  • raises the bounded browser-turn budget to 200 steps or 60 minutes, aborts the real executor when time expires, and preserves interrupted browser context for a real resume
  • adds one-task approval alongside one-shot and persistent approval choices, retaining the grant across native MCP actions until the real turn-complete boundary
  • exposes safe selectors for visible interactive controls so the model can complete forms without guessing selectors
  • routes native MCP actions through the same durable unfocused workspace and releases task state on completion or disconnect
  • stages the native helper and CLI MCP configuration atomically, distinguishes staged from live health, and reports the exact failing connection layer

Behavioral validation kept X in the foreground throughout a 56-step, approximately 175-second browser task, resumed and completed a stopped task after reopening the side panel, reused exactly one background window after extension reload, and completed form navigation, typing, clicking, reading, and screenshot capture under one task-scoped approval. The extension suite passes 410/410 tests; the live CLI doctor connected to verboo-in-chrome with no findings.

graseeel and others added 30 commits August 8, 2026 11:21
…uch and annotations

- macOS-only panel (Xcode 26/27) with MJPEG stream, low-cost simctl fallback,
  real rate measurement and controls: attach/detach, tap, drag, typing, keys,
  rotate, screenshot and screen recording
- element/area annotations from the simulator enter the chat as a visual
  attachment (the browser-annotation family became visualAttachments)
- sessionless WDA bridge (scripts/tauri/wda-sessionless) + simulator MCP
  registered in verboo-in-chrome; the verboo-ios-simulator sidecar is packaged
  and copied at build time (copy-wda-resource)
- panel suspension/restore on fullscreen, topbar button with recording
  indicator, clean shutdown on app exit (8s budget)

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
…licit risk acceptance

- integration cards on the Providers tab (connected/account/Connect/Disconnect)
  with per-provider state from the bridge (provider_auth_status)
- interactive login over PTY (provider-login:event: awaiting_browser | connected
  | error); neutral cwd for the CLI; explicit Claude risk dialog before the
  policy is accepted (never auto-accepts)
- model selector with per-provider groups, official icon and a "not connected"
  entry offering Connect; the verboo-only selector stays unchanged
- verboo-bridge bridge (providerLoginStart/Cancel/ConfirmRisk/AuthStatus);
  strip_terminal_controls becomes pub(crate) for the login PTY

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
- node_runtime: cfg(test)-only test hook (VERBOO_TEST_NO_NODE) — the
  missing-Node check can never leak into the production build (Cadinho remark
  2: the old check ran without cfg(test))
- child_signal/cli_spawn: interrupt and escalation target the whole process
  GROUP (kill(-pid)) — CLI subagents and forks die together with Stop; safe
  fallback to the direct child when it is not a group leader
- cli_credentials: credential protected via DPAPI on Windows
- turn_service: a new chat runs in a neutral workdir (app-data/chat-workdir)
  instead of scanning the user's cwd — the CLI no longer hangs on project read

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
…adation

- model_service: field-based dedup with merge and provider model attach
  (attach_provider_models); VerbooModel gains a provider field (omitted from
  the serialized form when absent — the current Verboo catalog stays intact)
- selector: when the selected model disappears from a transient catalog
  snapshot, the pill/row keep showing the last known model instead of falling
  back to the generic label; the listing stays faithful to the catalog

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
…der in the header

- API errors presented readably: usage limit with account/plan and renewal,
  provider retries shown as "retrying (N of M)" instead of a mute "Thinking…"
- stdout dedupe keyed on emitted result text (never swallows a repeated
  streaming delta); quota error suppressed on interrupt and recovery; turn
  errors get a subtle mark (thin border, no red card soiling the transcript)
- turn header: model/provider stamp on send (T10) with the canonical label —
  a Claude turn no longer announces itself as "Verboo"; offer a new
  conversation when a turn gets stuck in an empty thinking block
- persistence proof of the stamp (chatStore.test) and the started-event race
  fixed (App.turnModelStamp)

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
…l failure cause

- removes the "independent development version" screen (notice, support
  contacts and the sidebar devBuild) — the app opens straight into real login
- the raw cause of a rejected validateAccess appears behind a "Show technical
  details" toggle in the login warning; "Checking local session…" never stays
  stuck when validation rejects
- dedicated drag strip at the top of the login screen (does not swallow scroll)

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
- "Providers" tab with a Blocks icon (not the browser logo) — provider cards
  move out of Integrations, where they did not belong
- SettingsTab gains the providers value in the TS contract (mirror of Rust)

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
- context menu guard: the native menu no longer opens in the app (Ctrl+click
  preserved); the guard is installed at the renderer root
- browser controls get native tooltips (title) and a consistent aria-label:
  tabs (real URL), close, new tab, back/forward/reload, edit pencil/arrow and
  the editable URL field

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
…the repo

- README/INSTALL: the packaged app REQUIRES Node.js >=22 on the host
  (Homebrew, nvm, fnm, Volta or PATH) — the "self-contained" prose was lying;
  the JSON requirements contract was always honest
- requirements/macos-arm64.json: cli-package 0.14.5 -> 0.15.2
- removes the simulator planning documents (owner rule: planning material does
  not go into the repository)

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
- version bump 0.7.0-beta (package.json, Cargo.toml, tauri.conf.json) and
  @verboo/code 0.14.5 -> 0.15.2 — already in the worktree, Cadinho-approved;
  no new bump was created in this operation
- build-release-app.sh: the DMG is rebuilt from the signed .app (signing the
  existing image left the unsigned copy inside the DMG)
- ci-verify: permanent canary of the app<->CLI boundary (--list-models under
  isolated HOME) accepting only the two known CLI 0.15.2 contracts
- tauri-release: channel comment updated to v0.7.0-beta

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
- .gitignore: .obsidian/ (personal editor config) out of the repo
- comments citing a nonexistent agent (TORNO) fixed to the real fence
  (PERISCOPIO) in soundStorage, reservedSlashCommands and types.rs
- dead SettingsTab enum removed from Rust (the renderer already navigates by
  string)
- lib.rs: rustfmt reflow incidental to the cycle's edits

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
…mments

- remaining App.tsx imports left out of the theme 1 hunks (apiErrorPresentation,
  ProviderRiskDialog, visualAttachments family) — inert import lines, no
  behavior change
- remaining TORNO->PERISCOPIO comments in shared/types.ts (real fence)

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
@graseeel
graseeel marked this pull request as ready for review August 8, 2026 23:13
@graseeel graseeel changed the title feat: add independent signed CLI updater feat: ship interactive iOS simulator and signed CLI updates Aug 9, 2026
@graseeel graseeel changed the title feat: ship interactive iOS simulator and signed CLI updates feat: ship iOS simulator, signed CLI updates, and Chrome automation Aug 9, 2026
Keep native Chrome tasks in the durable background workspace, retain task-scoped approvals until the native turn boundary, enforce the 60-minute executor budget, and register iOS simulator runtime state only on macOS.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>
@graseeel
graseeel merged commit 5135993 into dev Aug 9, 2026
8 checks passed
@graseeel
graseeel deleted the feat/independent-cli-updater branch August 14, 2026 01:03
graseeel added a commit that referenced this pull request Aug 30, 2026
* chore(browser): log drained page messages to close the delivery diagnosis

Previous instrumentation proved the WebKit calls our handler, the
parsed envelope is accepted, and the messages enter the queue. Yet
wait_for_page_loaded does not recognize any as page-loaded — meaning
the payload shape does not match the predicate.

This commit logs the first drained message payload (truncated, non-empty
only). The next CI run will show whether the "type" field exists at the
top level and what its value is — the difference between fixing the
producer or the consumer of the message.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(browser): accept page-ready so the smoke works where frames are never composited

Root cause of the Linux bridge timeout: NOT a bridge bug. The full
multi-platform chain works end to end — instrumentation proved the
message arrives with correct url, title "Tab-One", and viewport.
The block was the gate itself.

The smoke waited for "page-loaded", which browser_inject.js emits
inside TWO nested requestAnimationFrame callbacks. rAF only fires
when a frame is composited, and CI headless runners do not compose
frames — not even under xvfb on Linux, and not without an interactive
session on Windows. It is the same limitation that already forced us
to tolerate missing snapshots.

Change: wait() now accepts "page-ready" in addition to
"page-loaded". The function and error messages were renamed (the old
name would start lying). A comment records why rAF has no place in
CI, so no one hardens it back.

This is not a relaxation: page-ready carries url, title, and viewport,
and the title proves the DOM was processed. The in-app annotation
capture (which needs a real composited frame) still requires
page-loaded — the smoke is the only path affected.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(browser): stabilize Windows packaged runtime smoke

* fix(ci): make packaged browser smoke tolerate cold starts

* fix(ci): stabilize headless multi-tab browser pacing

* fix(ci): launch browser smoke from active event loop

* fix(ci): avoid nested main-thread webview dispatch

* fix(ci): replace browser smoke delay with readiness

* fix(ci): keep browser smoke event loop active

* fix(windows): avoid nested WebView2 message pump

* fix(ci): synchronize browser smoke with UI turns

* fix(windows): preserve COM reentrancy during WebView2 waits

* fix(windows): use COM event waits for WebView2

* fix(windows): reuse WebView2 environment across tabs

* fix(windows): make WebView2 contract test CRLF-safe

* ci(macos): sign and notarize release artifacts

* ci(macos): sign and validate embedded native dependencies

* fix(release): unblock native browser and macOS signing

* test(windows): make browser contracts CRLF-safe

* fix(windows): defer WebView2 script completion

* fix(windows): restore sequential WebView2 script pump

* fix(windows): chain WebView2 document scripts

* fix(windows): batch WebView2 initialization scripts

* fix(windows): isolate embedded browser initialization

* fix(windows): keep browser environment on UI thread

* chore(windows): trace controller completion signaling

* fix(windows): restore WebView2 script completion helper

* fix(windows): pump browser bridge script registration

* ci(beta): skip packaged browser runtime smoke

* ci(macos): allow extended notarization queues

* test(manifests): make single-flight check deterministic

* ci(release): make macOS notarization resumable

* fix(release): bound macOS DMG creation

* ci(release): extract macOS signing identity as cert common name

The Tauri Release macOS bundling jobs failed because the signing
identity was extracted from `security find-identity` output using
whitespace-separated `print $2`, which captured the SHA-1 hash. The
output format is `1) <SHA1> "Developer ID Application: Name (TEAM)"`,
so cargo-tauri received the hash while it expects the certificate
common name, raising "certificate from APPLE_CERTIFICATE ... does not
match provided identity".

Switch the awk parser to field separator `"` so $2 yields the CN
exactly as required by cargo-tauri, e.g.
`Developer ID Application: Gabriel Grasel de Moura (6444BXPL32)`.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* chore(version): bump to 0.6.1-beta and @verboo/code 0.14.5

Bump desktop app version 0.6.0-beta.2 → 0.6.1-beta across
package.json, src-tauri/Cargo.toml and src-tauri/tauri.conf.json,
and bump the bundled @verboo/code CLI dependency 0.13.0 → 0.14.5
in package.json + package-lock.json.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(updates): probe Stable channel availability and enable chip dynamically

The Stable update chip was hard-disabled while the app was beta-only.
This adds a silent probe of STABLE_UPDATE_ENDPOINT alongside the
user's active channel check, and surfaces the result via
UpdateSnapshot.stable_channel_available.

- update_service.rs: set_stable_channel_available() + run_stable_probe()
  helper. Fail-closed: 404/network error/malformed manifest → false.
  Silent — does NOT call mark_error (missing stable is normal, not an
  error).
- lib.rs: check_for_updates() now probes the stable endpoint and emits
  the updated snapshot. Reuses active_check_ok when user is on Stable.
- tauri.conf.json + update_service.rs: STABLE_UPDATE_ENDPOINT moved to
  releases/download/updater-stable/latest.json (was releases/latest,
  which 404s for prerelease-only repos). Beta endpoint unchanged.
- types.rs: UpdateSnapshot.stable_channel_available: bool.
- SettingsView.tsx: Stable chip disabled={!stableChannelAvailable};
  hint only shown when stable is unavailable.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(goal): make the goal cycle actually run end-to-end in the packaged app

The goal feature never ran inside the packaged .app. Root cause and
nine chained defects, all fixed and verified in the field:

- G-C1 (root cause): maxTurns arrived as MAX_SAFE_INTEGER (JS) into a
  Rust u32, so serde rejected the invoke before the spawn. Migrated
  goal_evaluator.rs from cli_path::resolve() (broken global-fallback)
  to CliSpawn::new() — the same bundled-CLI route chat uses. cli_path.rs
  docstring rewritten to point new code at CliSpawn.
- G-C6: timeout 30s killed every real eval (~105s measured). Raised to
  240s (≈2.3× margin), env-configurable via VERBOO_GOAL_TIMEOUT_SECS
  with a 10s floor. CliTimeout carries timeout_secs.
- G-C2: loop detection (noProgressCount + recentFingerprints) was dead
  code — never written in the cycle. Now written post-evaluateGoal.
  Fingerprint is whitespace-normalized textual, not semantic.
- G-C2-FIX: App.tsx resume reset forgot recentFingerprints → re-block
  on resume. Added recentFingerprints: [] to the resume reset.
- G-C3: tokens were zero at the serde boundary (camelCase mismatch).
  Aligned field names across the Rust↔TS boundary.
- G-C4: goal completed prematurely with zero turns executed. Fixed
  completion predicate.
- G-C5: redundant confirmation turn caused by toolOutput existing in
  TS but not Rust. Removed the phantom field.
- G-C15-TS: usage line rendered in flow.css.
- Token accounting: tokenAccumulator.ts + tests; goalState tracks
  total/completed usage; App.tsx + chatStore wire the counters.
- Completion render: GoalActivePanel shows error/retry/usage states.
  goalCompletionRender.test.tsx covers the panels.
- Contract tests: rustSerdeContract.test.ts pins the Rust↔TS serde
  shape; goalState.contract.test.ts pins the state machine;
  goalScheduler.test.ts covers the cycle.

Measured: trivial goal 445s → 54s; 5-step goal closes in 1 turn with
arithmetic conferida. Suites: Rust 998, renderer 739, tsc zero.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(build): abort instead of signing a stale artifact, verify delivery by hash

build-release-app.sh used to exit non-zero on any `npm run tauri:build`
failure, which masked a benign case (updater-tarball signing without
TAURI_SIGNING_PRIVATE_KEY) and a dangerous case (cargo reusing a stale
.app) under the same error code.

- Snapshot the pre-build .app mtime. On tauri:build failure, look for
  the .app: missing → real abort, exit 1. Present with the same mtime
  → cargo did not recompile, exit 1 (refuse to sign a stale artifact).
  Present with a newer mtime → benign updater-signing failure, proceed.
- Delivery verification: extract the Vite index-<hash>.js literal from
  the binary with `strings` and compare to dist-renderer/assets/. If
  they diverge, the binary embeds a renderer from a different build —
  exit 1, refuse to sign.
- Resolve CARGO_TARGET_DIR once up front (env → src-tauri/.cargo/config.toml
  → $HOME/.cache/verboo-target fallback) so the mtime probe and the
  hash probe agree on where the artifact lives.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* ci(workflows): detect channel-only prerelease tags and bump Node to 22 LTS

tauri-release.yml: the prerelease regex `-(beta|alpha|rc)\.[0-9]+$` only
matched legacy tags (v0.6.0-beta.2) and missed the new channel-only
form (v0.6.1-beta), so those tags were not marked prerelease on GitHub
and the release workflow misrouted them. Generalize to
`-(beta|alpha|rc)(\.[0-9]+)?$` so both forms are detected.

verboo-cli-update.yml: bump Node 20 → 22 LTS. Corepack resolves pnpm
11.x by default, which fails with ERR_UNKNOWN_BUILTIN_MODULE on Node
20.x. Bumping Node is a 1-line fix; pinning pnpm would require adding
`packageManager` to package.json (broader scope). Node 22 LTS is
maintained until 2027.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* chore(cli-bundle): prune unused packages from the bundled cli-package closure

copy-cli-resource.mjs: add an explicit EXCLUDED_PACKAGES set so the
bundled cli-package ships only what dist/cli.mjs actually imports at
runtime. Each excluded package has empirical proof (P1 research +
C2 battery of 22 routes, rc map identical to baseline):

- @opentelemetry/* (11 packages): the CLI calls an external
  user-provided helper for OTEL headers; it does not import the
  @opentelemetry/* SDK itself.
- @grpc/grpc-js, @grpc/proto-loader: transitively pulled by the OTEL
  gRPC exporter; with OTEL removed, nothing references @grpc/*.
- type-fest, vscode-languageserver-protocol, code-excerpt, stack-utils:
  type-only or unreferenced utilities.

Why an explicit list (not a heuristic): a heuristic that auto-detects
"unused" packages by grepping the bundle for the package name will
eventually prune something essential — bundlers inline code under
minified names, dynamic imports construct strings at runtime, and
rare-path packages look "unused" until they aren't. An explicit,
commented, evidence-backed list keeps the blast radius of a mistake
to exactly the packages a human signed off on.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* chore(version): update Cargo.lock for verboo-desktop 0.6.1-beta

Bump verboo-desktop in Cargo.lock to match the 0.6.1-beta version
bump in Cargo.toml.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* ci: cross-platform verify gate + pin packageManager

Two cooperating fixes for cross-platform verification.

1. .github/workflows/ci-verify.yml (new): a separate verify pipeline
   runs cargo test --lib and the renderer build on macOS, Windows, and
   Linux. Distinct from the existing release workflow so verify signal
   stays decoupled from publish.

2. package.json: add packageManager: "npm@11.12.1". This is the actual
   root cause fix for the verboo-cli-update.yml freeze — when neither
   package-lock.json nor pnpm-lock.yaml pinned a manager, setup-node
   resolved pnpm 11.x by default, which fails with ERR_UNKNOWN_BUILTIN_MODULE
   on Node 20.x, so the CLI auto-update cron ran in silent failure and
   the app stayed on @verboo/code 0.13.0 while the registry had 0.14.5.

   With packageManager pinned, the dual-lockfile ambiguity is resolved
   and pnpm is no longer implicit. pnpm-lock.yaml is preserved (it is
   regenerated by the CLI auto-update cron via
   scripts/verify/update-cli-dependency.mjs) so removing it was never
   the correct fix.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* test(verify): run the full Linux suite and verify sidecars by hash

scripts/verify/browser-linux-check.sh: kill the 13 silent --skip flags
that hid real coverage gaps, replace them with a hard guard:

- Remove --skip on the 13 environment-dependent tests (git_service: 5
  require real git; video::prepare: 4 require ffmpeg; video::probe: 4
  require ffprobe). The container now ships git, ffmpeg, ffprobe, and
  Node 22, so all 13 run for real.
- The "filtered out" count is now asserted to be exactly 0. Any new
  silent skip becomes a gate failure (QA rule: a skip exists only when
  explicitly declared with a one-line justification).
- Three previously green-by-accident tests now execute and pass on the
  real Linux container.

Sidecar verification switches from executable-bit check to sha256
equality, before and after, with a fixed count of 5 binary fingerprints:

- macOS binaries were declared "OK" by bit de execução, which also
  declared stale copies OK after a partial rebuild. sha256 comparison
  catches drift across rebuilds and across developers' machines.
- The fixed-count 5 covers: ffmpeg, ffprobe (macOS), node-runtime,
  whisper-helper, cli-helper.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(video): host_target unified, maps Linux and Windows ARM

The function that maps a build triple to a sidecar target was
triplicated across probe.rs, prepare.rs, and router.rs. The non-macOS
arm64 branches returned the literal string "unsupported", which the
caller used to construct a sidecar path (e.g. verb-ffprobe-unsupported)
and proceeded to fail far from the cause.

This change:

- New module src-tauri/src/services/video/target.rs hosts a single
  host_target() -> Option<&'static str> covering linux-x86_64,
  linux-aarch64, macos-x86_64, macos-aarch64, windows-x86_64, and
  windows-aarch64. None maps to "unsupported" — absent platforms are
  represented by None at the type level, which forces the caller to
  handle them explicitly.
- New VideoValidationError::UnsupportedPlatform variant replaces the
  old string sentinel. The compiler now enforces the case.
- probe.rs, prepare.rs, router.rs all delegate to target::host_target().
  Each call site is a single match — no fallback string ever reaches
  the path builder.

Two guards against re-triplication:

1. A test in target.rs walks every other file under services/video at
   test time and asserts none defines host_target or executable_suffix.
2. The signature is non-defaultable (Option, no String fallback). A
   silent string return would still compile, but writing the same
   triplication twice already failed once.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(process): suppress Windows console windows on every CLI spawn

Without CREATE_NO_WINDOW, CREATE_NEW_PROCESS_GROUP alone still pops a
visible terminal window on Windows — every CLI spawn in the user's
session. Two outages this cycle were traced to it: a terminal window
stole focus mid-Goal cycle, and a git operation in the background
caused flakiness in user tests.

Chokepoint: cli_spawn.rs now exposes apply_creation_flags(&mut Command)
which sets CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW on Windows.
Every production spawn goes through it:

- cli_spawn.rs: new apply_creation_flags; Command builder applies both
  flags on Windows, no-op on macOS and Linux.
- workspace_files_service.rs: 3 spawn calls updated to call
  apply_creation_flags.
- chrome_integration/installer.rs: 1 spawn.
- goal_evaluator.rs: CliSpawn::new already calls it; comment added.
- plugins_service.rs: same; comment added.
- git_service.rs: spawn helper refactored to route through the
  chokepoint.
- child_signal.rs: docs updated to explain why CREATE_NEW_PROCESS_GROUP
  alone is insufficient.

A grep test in child_signal.rs walks all of services/ at test time and
asserts every spawn of a child command passes through cli_spawn
creation flags (directly or via apply_creation_flags). Any new spawn
that bypasses the chokepoint becomes a gate failure.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(auth): non-blocking CLI login with incremental read and login:event

Fixes issue #59 — CLI login hung on Linux Fedora because the Tauri
command awaited stdout in one blocking read while the helper streamed
its output line-by-line. On Fedora's pipe semantics the read blocked
forever instead of consuming what was already buffered.

The fix moves login to a single-shot spawn in cli_service.rs that
streams stderr/stdout incrementally:

- cli_service.rs: new login_command()/spawn_login() — builds the CLI
  login invocation, takes its stdin (so the user can paste credentials
  during OTP), emits a login:event with structured { kind, payload }
  for every meaningful line (auth/url/error/ready/done).
- auth_token.rs: helper that listens on the channel and persists the
  token once a kind: "ready" arrives.
- models/types.rs + shared/types.ts: LoginEvent + LoginEventKind are
  the canonical serde shape (camelCase, rename_all = "lowercase" on
  the Rust enum).
- rustSerdeContract.test.ts: cross-module string contract pin — the
  Rust enum declaration is read at test time and asserted to match
  the TS union declaration. Catches silent rename drift at CI.
- lib.rs: Tauri channel "login:event" registered on the global
  Channel<LoginEvent>.
- App.tsx: t() keys wired, LoginScreen receives the live stream, no
  visible UI freeze (Tauri invoke returns immediately, events flow
  in).
- LoginScreen.tsx + LoginScreen.test.tsx (new): the panel itself,
  covered by 12 cases including progress lines, OTP prompt, error,
  and ready. The two cases that previously failed on Linux Fedora
  now run and pass inside the Linux container, so the bug has a
  platform-correct regression test.
- login.css (new): styling extracted from the old inline layout.
- i18n.tsx: 16 keys for the login status progression (login.started,
  login.waitingOtp, login.error, login.ready, etc.) so non-English
  builds surface the same UX.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* chore(chrome-ext): parallel work committed by user order, NOT QA-gated

*** AVISO: ESTE COMMIT NAO PASSOU PELO GATE DO CADINHO. ***

O usuario trabalhou em paralelo na extensao do Chrome (PERISCÓPIO
fence, extensions/verboo-chrome/**) e no painel de integracao no
renderer (src/renderer/features/settings/ChromeIntegrationSettings.*).
O conteudo deste commit e de autoria do proprio usuario, commits
feitos por ordem expressa dele.

Escopo do commit:
- extensions/verboo-chrome/**: rotina store (routines/*), novos
  contratos de painel (approvalActions, optionsContract, panelContract),
  controller/tools/tabs.test.js, ajustes em loop/approvedExecute/
  untrustedContent/hardBlocks/oauthConfig/policy e i18n, manifest,
  docs (PERMISSIONS, PRIVACY, PUBLISHING, README, STORE_LISTING) e
  assets (store-assets/README).
- src/renderer/features/settings/ChromeIntegrationSettings.tsx +
  .test.tsx: ajustes no painel de integracao.

PENDENCIAS QUE NAO FORAM AUDITADAS (minha responsabilidade):
- diff vs meu plano de agrumamento — nao revisei arquivo por arquivo.
- seguranca do hardBlocks e untrustedContent — sem audit.
- serde/JS bridge na fronteira evaluateScript -> routerClient — sem
  audit.
- manifest permissions vs usage real — sem audit.
- testes novos (tabs.test.js, approvalActions.test.js, panelContract,
  optionsContract, routines/schema.test, slashCommands.test, store.test,
  temporaryDraftStore) — sem review de cobertura ou de mocks.
- ChromeIntegrationSettings.test.tsx — sem review.

CADINHO precisa auditar este commit antes de qualquer merge para main.
Para auditoria futura: arquivo-por-arquivo. Nao faco outra coisa
neste turno — esta commit e um snapshot literal.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(goal): T1 — per-task batch execution + action-evidence guard

Multi-task goals had a single end-state and a single turn counter.
The evaluator only saw "the goal", so each evaluation was a re-judgment
of the whole batch. There was no per-task progression, and the
completion predicate accepted a task that the agent finished in
prose without any whitelisted action on disk.

T1 introduces two cooperating changes, exclusively in the renderer
(no src-tauri touched):

1. Per-task batch execution. GoalState carries a GoalTask[] array
   (objective + optional completion criteria) and a per-task
   turnsRunThisTask counter alongside the global turnsRun.
   GoalScheduler.runGoalCycle passes a SNAPSHOT to the Rust evaluator
   with the CURRENT task's objective in `objective` and the per-task
   counter in `turnsRun`. The evaluator now judges one task at a time
   without any change on the Rust side — the same input/output
   contract, the same 240s timeout, the same fingerprint ring.

2. D1 action-evidence guard. A task is not considered complete if the
   owning conversation's transcript has zero whitelisted activityKind
   entries (whitelist: tool_use, file_write, cli_invoke, eval_run).
   Pure "thinking" / narration activity does NOT count as action.
   The guard reads the OWNER conversation's live transcript via the
   same resolution path as evaluateGoal, so batch goals do not
   regress single-task behavior (acceptance 4: single-task key
   turnsRunThisTask stays absent → falls back to the global
   turnsRun).

PROVENANCE LIMIT (declared in both a comment and an executable test):
the guard proves PRESENCE of an action activity, not CORRECTNESS of
its outcome. A task that ran the wrong tool still satisfies the
guard. Correctness remains the evaluator's job; the guard's only
claim is "did the agent do something, not just talk".

Test suites:
- renderer: 777 tests in 72 files (759 baseline + 18 new, 0 deleted).
  goalBatch.test.ts is the new file, covering 6 DISPARO→EFEITO
  scenarios (whitelist enforcement, provenance limit, single-task
  regression-free fallback, ring-feeding on prose-only, supervisor
  handoff shape, retry-from-current-task).
- tsc --noEmit: zero errors.
- npm run build:renderer: builds clean.
- cargo test --lib: 1006 passed (untouched, T1 stayed in the renderer).

Files in this commit:
  src/shared/types.ts                              (GoalTask, GoalBatchEvidence, ActivityKind)
  src/renderer/features/goal/goalState.ts          (GoalTask[] + turnsRunThisTask)
  src/renderer/features/goal/goalState.contract.test.ts
  src/renderer/features/goal/goalScheduler.ts      (SNAPSHOT + guard)
  src/renderer/features/goal/goalScheduler.test.ts
  src/renderer/features/goal/goalBatch.test.ts     (NEW)
  src/renderer/App.tsx                             (per-task resume + counter wiring)

CADINHO: APROVADO. Push deferred to user authorization.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(goal): T2 — batch state matrix + global stagnation guard

T1 introduced per-task progression and a per-task evidence guard. T2
plumbs the full batch into a state machine and adds a global guard so a
single broken task cannot silently poison the rest of the batch.

State matrix — 13 transitions, one test per row in
goalBatchMatrix.test.ts:

  per task:    pending → running → complete
                          → running (loop, no-progress feed)
                          → failed_action → skipped (user skip)
                          → failed_error (hard error)
  per batch:   active → completed (all tasks complete)
                     → paused (K guard trip)
                     → aborted (user abort OR safety trip)

Decisions, with the reasons they are the decisions:

- Safety alert is WHOLE-BATCH, not per-task. Safety is a property of
  the run, not of individual tasks (a destructive tool call in one
  task contaminates the whole owner conversation).

- Loop detection kills the CURRENT task but ADVANCES to the next. A
  task that loops forever must not take the rest of the batch with
  it. The advance carries the loop fingerprint into the per-task
  dead-letter slot, so the user can see what happened.

- Global K guard pauses the batch after 2 consecutive failures with
  reason batchStagnation. The counter resets on ANY success
  (including an unexpected one — proof the environment is alive) but
  SURVIVES advances after a failure (so a sequence "fail, fail, advance"
  still trips at the 3rd failure). This is the guard that detects
  environment-broken-ness, not user choice.

- Skipping a blocked task is a USER decision and is a distinct status
  (skipped), not a failure. It does NOT feed the K guard. Skipping is
  the user's escape hatch; if it counted, the guard would punish the
  user for using the only escape it provides.

PROVENANCE NOTE (declared in commit + docstring + test label):
the canonical repro test in goalBatch.test.ts changed its assertion
shape. This is strengthening, not regression. CADINHO audited the
diff line-by-line: the new assertion is strictly more specific
(witnesses the rust-stamped reason on the dead-letter, not just
"a reason exists"). The pre-T2 test would have passed silently
under the new code; the post-T2 test fails if the witness is dropped.

Test suites:
- renderer: 794 tests in 73 files (777 baseline + 17 new, 0 deleted).
  goalBatchMatrix.test.ts is the new file, covering 13 matrix rows.
- tsc --noEmit: zero errors.
- npm run build:renderer: 6.03s clean.
- cargo test --lib: 1006 passed (untouched, T2 stayed in the renderer).

Files in this commit:
  src/shared/types.ts                              (BatchStagnation, SkipReason, batchStatus)
  src/renderer/features/goal/goalState.ts          (K counter + batch state)
  src/renderer/features/goal/goalState.contract.test.ts
  src/renderer/features/goal/goalScheduler.ts      (advance on loop, K guard)
  src/renderer/features/goal/goalBatch.test.ts     (canonical repro: assertion strengthened)
  src/renderer/features/goal/goalBatchMatrix.test.ts  (NEW)

CADINHO: APROVADO without ressalvas. Push deferred to user authorization.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(goal): T3 — task frontier with /compact handoff

On task advance the batch emits the CLI's native /compact, awaits the
compaction turn to complete, and only then zeros the fingerprint ring,
no-progress counter, and per-task turn counter.

The compaction turn does not by itself satisfy the next task's
action-evidence guard, so the new task does not inherit a false
positive from the previous one.

Compaction tokens count exactly once (dedupe by turnId).

A failed /compact does not freeze the batch — the batch proceeds and
records that it did not compact.

Race between state writes eliminated by atomicity, not just ordering:
the index advance and counter zeroing now happen inside a single
updater.

Honesty note retained in code: reactive context-overflow recovery may
fire mid-task, outside the frontier's control, and disarms the loop
detector by construction. The global stagnation guard is the safety
net for that case.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(goal): T3b — compact at every task frontier with coalescence

Compaction now fires at every frontier, not only on success advance. A
task that died from a loop produced the dirtiest context of the batch;
carrying that into the next task hands it the inherited dead zone. The
uniform rule is: every task starts with a clean context, regardless of
how the previous one ended.

Coalescence: skip compaction when the exiting task ran zero turns —
nothing was added to the context since the last compaction. The skip
is declared in the log, never silent.

A user-initiated skip happens outside the cycle: the frontier is
stamped as DUE and executed at the start of the next cycle with the
full protocol, idempotently. The DUE stamp is not applied when the
skipped task was the last one.

Cost cap: a stagnation-pause does NOT compact, because the batch halts
and there is no next task to compact for.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(goal): T4 — final batch report with evidence, in-place progress, i18n

On batch close the report lists EACH task with the evidence that
sustained its conclusion — not just succeeded or failed. With N tasks
in a batch, a single lying task would contaminate the whole batch in
silence.

Evidence is stamped in state at the transition moment, captured
BEFORE the frontier reset; recomputing later would read zero because
of compaction.

A task marked no-tool gets its own line saying its evidence was
waived, instead of rendering zero actions and lying in the opposite
direction.

Progress renders as a discrete "task k of N" line updated in place,
with no badge and no box. The final report goes inline in the same
turn as the agent's message.

Nine new i18n keys across both locales, including batchStagnation
wired into the reason translator — paying off the T2 debt where the
stagnation pause rendered an unknown reason.

User product vetoes became negative test assertions: the test asserts
that the separate box and the second message do NOT exist. This
prevents experience regression the same way we prevent code
regression.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(goal): T5 — batch input with tolerant parser

A batch is one task per line: two or more content lines form a batch
while a single line remains the normal goal of always — no behavior
change for the single-task path.

Numbering and bullet markers are accepted and stripped (up to two
stacked per line, always requiring a trailing space), so a fragment
like "1.5 corrigir versao" stays intact. Blank lines are ignored;
lines that are only a marker are discarded.

The [toolless] flag in any position of the line, case-insensitive,
marks the task as legitimately tool-less and waives the evidence
requirement; the final report declares this. Mentioning the word
WITHOUT brackets does not activate anything.

During a batch, objective editing stays disabled: the buttons stay
VISIBLE but inert, with the reason as tooltip — a clear warning, not
a mysterious disappearance. Skip-locked-task and cancel remain
available.

Known caveat (cheap fix planned): a line that is only a number with
no punctuation, or only punctuation, currently becomes a task and
will visibly fail in the report.

An empty /goal command teaches the format instead of just failing,
and does not interrupt a goal already running.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(process): add missing CommandExt import at seven call sites

creation_flags is a method on the CommandExt trait. Without
`use std::os::windows::process::CommandExt` the compiler rejects the
call, so the previous console-window suppression did not actually
compile on Windows.

The import was missing at SEVEN call sites — the seven defects that
prior cross-platform testing missed.

Why this stayed hidden: 1006 tests passed on macOS and 1006 on Linux
because the compiler SKIPS cfg(windows) blocks on those targets.
The textual scan test that was supposed to guard this fix only
proves the FORM of the code (it greps for "creation_flags"); it does
not prove the code compiles.

Found by cross-compilation with `cargo xwin check
--target x86_64-pc-windows-msvc`, which runs in seconds on macOS.

child_signal.rs now carries a comment declaring its test as form-only
and pointing at the cross-compile gate as the real proof of
compilation.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* chore(verify): local Windows compile gate + Linux arch parametrization

The new scripts/verify/browser-windows-check.sh is the local mirror
of the Linux gate and runs `cargo xwin check
--target x86_64-pc-windows-msvc`. Its header declares the limit
explicitly: this proves COMPILATION, not behavior.

The script exists because cross-compilation surfaced seven real
defects in a commit that had already merged (the missing CommandExt
imports in fix(process)). Without a local compile gate, those defects
would have shipped because cfg(windows) code is invisible to the
macOS and Linux compilers.

scripts/verify/browser-linux-check.sh gained optional architecture
parametrization, with the host default unchanged. It also gained a
comment documenting a FAILED attempt to run the x86_64 gate under
QEMU emulation: gcc-13's collect2 segfaulted on proc-macro linking,
reducing parallelism did not help, and clang with lld broke at the
apt stage. The flags that had been added to disable apt signature
verification for the emulation attempt were removed in full. The
comment is there so the next person does not relive the night
thinking they had a new idea.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(goal): evaluator downgraded markdown-wrapped decisions silently

Pre-existing defect found by field test on the packaged app: when
the CLI returned the evaluation with the JSON wrapped in a markdown
code fence, the envelope extractor could not read it and FABRICATED
a synthetic continue decision with confidence 0.5, dumping the real
evaluator response into the reason field as text.

In the field this caused a task that was correctly completed (file
created, content verified by reading) to be repeatedly downgraded to
continue until it died by loop detection and was marked failed. The
evaluator had actually decided complete with confidence 1.

The defect is INTERMITTENT — it depends on whether the model wraps
the response — and affects ANY goal, including single-task.

Fix: reuse the existing parse_first_json_object helper, which already
subsumes fence tolerance by scanning the string counting brace depth
and ignoring braces inside string literals. This avoids adding a
fourth local implementation of fence tolerance — three already exist
in the repo.

When no parseable JSON is present, the function now returns a parse
error carrying the TRUNCATED raw string, instead of a synthetic
continue. This honors the contract documented in goalScheduler.ts
lines 111-117: the caller must not swallow errors into a false
continue decision.

CONTRACT CHANGE (declared per CADINHO requirement):
The change in lib.rs makes all evaluator error classes — timeout,
spawn, exit, and parse — retry up to three times with growing
backoff before pausing, instead of pausing on the first failure.
This is an improvement but is a behavior change for callers that
previously observed a single failure as terminal.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* refactor(goal): minimalist goal panel at user request

The user found the panel visually heavy and asked for it to be more
minimalist, compact, and clean.

The card shed visual weight, the status became a discrete per-state
indicator instead of an UPPERCASE box label, the redundant objective
label was removed, and the buttons became icons with accessible
labels.

Product vetoes became negative test assertions: the tests require
that the old captions and the removed label do NOT come back, and
the block comment quotes the user's veto textually. Reintroducing
the loud panel breaks the test with a message explaining why.

Incidental cleanup: a pair of pre-existing dead CSS classes with no
remaining consumers was removed alongside the redesign.

Limitation declared by the author inside the test: jsdom does not
evaluate CSS, so visual quietness is pinned via class-and-attribute
contracts, not pixels. Final visual judgment is reserved for when
the app is open.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(goal): restore batch final report with time+token, fix progress stamp

Field test on the packaged app with a direct user complaint: when a
batch finished, neither the per-task report, nor the elapsed time,
nor the token count was shown.

First cause: onComplete was not invoked when the last task reached
terminal state nor when the last task was skipped, so the batch
ended mute.

Second cause: the completion handler resolved the goal's deferred
BEFORE the summary item existed — item creation waits on an
asynchronous call. The stamp ran, found no target, returned
silently, and never retried for that turn.

Fix: the deferred is now resolved AFTER item creation. Stamp
failure is no longer silent: when the target is not found, this
now surfaces, with the declared distinction between "nothing to
stamp" and "stamp failed".

One test assertion changed by design: a prior pin asserted the
report was NOT emitted on that path, and its own comment said it
existed so that a conscious choice could be made later. The choice
was never made; the field complained; now it is made, with stronger
exactly-once, status, and timestamp assertions.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(process): reserved slash commands pass through to CLI unwrapped

build_prompt was prefixing every message with the working directory,
including on resume. The prompt therefore never STARTED with a slash,
the CLI command interception layer never fired, and /compact received
the response "this is an internal command, don't invoke via Skill".
The batch was the first consumer that depended on this path.

The defect is pre-existing: /compact was born in commit 7fdd56c,
already on origin/dev, and the wrapping was introduced by c5dae57
during the Tauri migration. The command has never worked through the
app since it was created.

Fix: reserved slash commands now pass to the CLI without the
directory prefix. The normal (non-slash) path retains the prefix,
proved by counterfactual.

Boundary test added: a contract test reads the list of reserved
commands from the Rust source and compares it against the
TypeScript command family, failing if a command appears on only one
side — because a forgotten command would be wrapped and die silently,
exactly as /compact did. The test lives in the renderer fence as a
punctual exception authorized case-by-case, without changing the
project fence rules.

manifest_cache: the deadlock test that depended on real spawn was
moved from gate to an ignored integration test. It was measuring
ENVIRONMENT and calling the result a verdict, failing 1 in 5 under
load and threatening to leave CI intermittently red. The guarantee
of absence of deadlock was not lost — it was redirected to another
test promoted as witness, with signs on both sides.

LIMITATION: this proves the message exits raw; that the CLI
actually compacts through this path will only be proven with the
packaged app open.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(goal): resumable pause-by-reply when task is impossible, plus two UI asks

The panel now shows the user's message LITERALLY instead of the
generic "Lote de N tarefas" label — the user said the generic was
not intuitive.

It gains genie-style entry and exit animation, rising from the
composer on start and descending on end, respecting
prefers-reduced-motion.

The taskImpossible reason now PAUSES the batch instead of falling
through. The task is stamped BLOCKED and never failed, because
resumability depends on this. The index does NOT advance while
blocked, so the batch does not skip the task to resolve.

Session re-hydration as its own important fix: the session
identifier lived only in a reference and was never recovered from
persisted state. Resuming after an app restart silently created a
NEW session — the user would answer thinking they were continuing
and the model would start from zero.

Replying in the composer now RESUMES the paused goal, detected by
the OWNING conversation, not by the active one.

Visible contract message: the pause tells the user, on screen and
in both locales, that replying there resumes the SAME task and that
to change task they must cancel and relaunch. Predictable behavior
by visible declaration, because someone will reply "forget this, do
Y" and the evaluator would judge against the old text.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(evaluator): pause when output is a symbolic artifact, never conclude

In a field test on the packaged app, a batch with two IMPOSSIBLE
tasks reported all four as completed. The agent honestly explained
it could not, but it still created copia.txt with ZERO bytes and
config.json with empty keys — and the evaluator accepted.

The observable-action guard does not catch this because creating an
empty file IS an action: it proves PRESENCE, not CORRECTNESS. That
limit is declared in the code itself.

New rule: a symbolic artifact is not delivery — empty, placeholder,
or stub outputs do not satisfy the objective. The output is PAUSE
with the new reason taskImpossible and a human-readable explanation,
never conclusion.

Counterfactual: the rule still lets REAL delivery conclude. A
rejection rule that rejects too much is worse than no rule.

This commit lands after the TS-side commit (2f7460e) so the new
reason exists on the renderer side first. With TS first, no point
in the repo history ever holds the dangerous state where Rust
emits taskImpossible and the renderer falls through to a silent
continue.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(process): propagate TodoWrite contents instead of discarding them

The bundled CLI already has TodoWrite and the agent already plans
steps and checks them off as it goes. The app received the event
but flattened it to the generic label "Updated tasks" with a
generic type, and the items and status of each were DISCARDED
before crossing the bridge — the data was never even stored.

Now TodoItem carries content, status, and activeForm, and
RuntimeActivity propagates the list.

TodoWrite emitted by a SUBAGENT is filtered, so it does not
overwrite the main turn's list.

SECOND CHANGE (declared per CADINHO): TodoWrite now has its own
planning type and ceases to count as an observable action for the
goal guard. The guard exists to prove something WAS DONE, and
writing the list of what you intend to do is the opposite.
Before this change, planning satisfied the guard, which would
allow an agent to close a task by merely announcing the plan.
A test BREAKS if planning is reintroduced to the action list,
with the expected answer documented as NO.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(checklist): agent task list with floating card and docked mode

The list appears whenever a checklist exists — it is not exclusive
to goal. Any multi-step request makes the agent plan and the list
spawns by itself, checking items off as the agent completes them.

Two display modes with user preference persisted: a compact floating
card in the upper-right corner, sized to content, draggable with
magnetic snap; or a docked row above the goal or composer, three
lines with no header or border, collapsing to one line when all
items are done. Goal stays closest to the composer; the list rises.

When the right side is occupied by terminal, web, review, or a
sub-agent panel, the list MIGRATES by flight animation to docked
mode, and returns when the panel closes.

The card follows the pointer over the conversation during drag but
has no resting position outside the right strip — releasing snaps
it back.

Positioning is a PURE FUNCTION with an exhaustive test matrix,
because the user demanded the UI not break under any combination.

The floating card is mounted OUTSIDE the fixed footer via a portal
to the body: a fixed-position element inside an ancestor with
transform has its containing block hijacked and stops orienting by
the window — exactly the class of bug that already dropped the
panel in this project. A test locks the portal so nobody simplifies
it back.

Multi-platform mitigations by construction: scrollbar width
measured at runtime, heights in line-height units with a test that
breaks if pixels return, pointer events with capture, and position
reclamping on both restore and resize.

LIMITATION: the real pixel on Windows and Linux remains unproven
until a push; Windows tests are geometry SIMULATION.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(checklist): stop card colliding with other floating elements, cut less text

Field test on the packaged app: the card spawned correctly and
checked items off by itself, but the text rendered truncated with
ellipsis eating almost everything, and the sub-agent indicator
OVERLAID the card — both disputing the same upper-right corner.

The fix is NOT to move the card to another corner, which would only
push the collision elsewhere. The positioning pure function now
carries a notion of free space at the top, so ANY floating element
in that region pushes the card BY RULE.

Property verified by CADINHO across the 256 combinations of the
matrix: the indicator is FORM-INVARIANT — it never decides whether
the card floats or docks, it only shifts geometry.

The step now breaks across two lines with the full text available
in a tooltip. A CSS pin locks the font property and breaks if
someone reverts the line wrap. Heights stay in relative units, not
pixels, preserving the multi-platform protection.

LIMITATIONS: jsdom does not lay out, so the real pixel of the
two-line wrap and the live geometry measured for the indicator are
only proven with the app open. Windows and Linux pixels remain
simulated in tests, unproven until a push.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(extension): rotinas, extracao estruturada e sessao de opcoes

Trabalho majoritariamente do usuario, feito em paralelo ao ciclo do
app. Descricao factual e neutra.

O controlador ganha browserTools.js com extracao estruturada de
pagina e uma leitura de pagina mais robusta, acompanhados de testes.

O painel de opcoes ganha uma sessao propria de configuracao
(optionsSession.js) com testes, separando o estado das opcoes da
logica de renderizacao.

Rotinas (routines) amadurecem: runner, runQueue, runStore e schema
completam o ciclo de execucao de rotinas, todos com testes.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(browser): ciclo de vida, visibilidade e User-Agent das abas, mais anotacoes e checklist

Fronte por fronte:

- navegador embutido: navegar a partir do estado vazio; audio para
  ao fechar aba (about:blank + sondagem ate confirmar, orcamento
  500ms); minimizar esconde de verdade e pausa midia; reabrir volta
  na mesma aba; teto de 8 abas vivas com despejo LRU declarado na UI;
  barra de URL derivada da aba ativa

- visibilidade virou ESTADO IRREPRESENTAVEL: campo privado em modulo
  privado, so alteravel por funcao que exige a transicao nativa;
  ativar aba com painel escondido nao mostra a webview

- User-Agent das abas: sufixo Safari que a WKWebView omite (o Google
  servia layout antigo); versao lida em runtime via NSBundle, com
  fallback declarado e registrado; Linux coberto de forma DEFENSIVA
  (nao verificado em maquina Linux); Windows dispensa (motor
  Chromium)

- anotacoes: selecionar trecho da resposta, comentar, e enviar como
  mensagem propria na ordem do transcript

- checklist: saida animada ao completar, dois sons, e fim da
  sobreposicao com o composer

- idioma dos passos do TodoWrite e limpeza de vazamento de </think>
  na exibicao

- varredura de fronteira renderer/Rust: teste que le o renderer
  atras de invoke() e falha se o comando nao existir do outro lado

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* chore: prototipos de rascunho do checklist

Mockups de rascunho do checklist, autocontidos em HTML. Foram usados
para o usuario escolher o desenho e ja cumpriram o papel; ficam no
historico para quem achar depois saber o que sao.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(ci): CARGO_TARGET_DIR fora do env de job, que invalidava o workflow

O contexto 'runner' nao e valido em env: de job — por isso o GitHub
recusava o arquivo inteiro na validacao (0 segundo, nenhum job
criado). O workflow tinha sido escrito na maquina e nunca havia
rodado.

A isolacao por job foi preservada via $RUNNER_TEMP escrito em
GITHUB_ENV num passo dedicado, o que propaga o valor para todos os
passos seguintes.

'shell: bash' e necessario porque o runner Windows usa PowerShell
por padrao, e o GITHUB_ENV via echo funciona em bash.

Limite honesto: isto prova que o workflow CARREGA, nao que ele
PASSA — a primeira execucao real de verdade nos tres sistemas ainda
vai acontecer.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(ci): hash do ffmpeg, recurso cli-package e shell do passo Windows

Tres defeitos reais que a primeira corrida de verdade expos, todos do
arreio de CI, nenhum do app:

1. Hash SHA-256 do ffmpeg no Windows com dois caracteres trocados
   (a0b em vez de b0a). O hash foi conferido no checksum publicado
   pelo fornecedor (gyan.dev) — nao copiado do valor observado na
   falha, que seria transformar a verificacao em teatro. O download
   era legitimo; o valor chumbado e que estava errado.

2. macOS e Linux nem chegavam a testar: build.rs abortava com
   "resource path 'resources/cli-package' doesn't exist".
   src-tauri/resources/ e gitignored e a CI nao rodava o passo que
   cria o recurso. O passo novo (copy-cli-resource.mjs) roda depois
   do npm ci e antes do build do renderer, no job core-tests.

3. Renderer no Windows: sintaxe POSIX rodando sob PowerShell
   (ParserError), faltava 'shell: bash' no passo de scripts .mjs.

Limite: isto corrige o que a primeira corrida real expos; se os
testes passam nos tres sistemas, so a proxima corrida diz. Nunca
rodamos a suite Rust no Windows nem no Linux.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(test): CLI falso descarregava stdout tarde demais no Windows

O teste a1_url_extracted_before_process_exits levava 26,6s no
Windows (limite 2s). O CLI falso fazia process.stdout.write(...) e
caia num laco ocupado de 30s; em pipe a escrita do Node e
assincrona e o laco bloqueava o laco de eventos, impedindo a
descarga. No macOS passava por acidente.

Correcao: usar o callback do write como barreira de descarga.

Nenhum leitor de producao foi alterado. A leitura incremental do
produto foi conferida no codigo: laco de read + extract a cada
pedaco, wait so depois.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(test): teste do Safari fixava a versao da maquina de desenvolvimento

O teste afirmava versao "27.0", que e a do Safari da maquina onde
foi escrito; falhava em qualquer outra, inclusive no runner
macos-15.

Agora afirma RELACAO (a leitura devolve valor, comeca com digito, e
atravessa ate o UA montado) em vez de numero.

A protecao original continua: a suite fica vermelha se alguem fizer
o resolvedor ignorar o valor de runtime.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(ci): ffmpeg moderno no Linux em vez do apt do Ubuntu 22.04

Dois testes de video falhavam so no Linux com "Unrecognized option
'fps_mode'" — opcao que existe do ffmpeg 5.1 em diante, e o apt do
Ubuntu 22.04 entrega 4.4.

Trocado por build estatico 8.1.2 (mesma linha que o app empacotado
embute), de release imutavel, com SHA-256 conferido contra o
arquivo de checksums PUBLICADO.

A verificacao de hash torna irrelevante a tag ser teoricamente
reescrivel: se o artefato mudar, a CI fica vermelha e barulhenta,
nunca silenciosamente diferente.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(scripts): caminho interno de bundle .app e sempre POSIX

resolveLaunch montava o caminho com o separador do HOST; como recebe
a plataforma por parametro, ela pode ser chamada com darwin de
qualquer maquina, e no Windows produzia barra invertida. Agora usa
POSIX.

Limite honesto: a prova contrafactual e CEGA no macOS (join e
posix.join produzem o mesmo resultado no darwin) — a falha so
aparece em host Windows, e a confirmacao vem da proxima corrida da
CI.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(test): CLI falso nao prende mais a CPU, e vivacidade do filho virou assercao

Dois pontos:

(a) o laco ocupado 'while (Date.now() - start < 30000) {}' prendia
um nucleo. No runner Windows, com poucos nucleos, isso atrasava a
thread leitora: o teste foi de 26,6s para 12,98s com a correcao
anterior e continuava acima do limite de 2s. Trocado por setTimeout,
que mantem o processo vivo os 30s sem monopolizar processador —
temporizador pendente segura o laco de eventos do Node.

(b) achado do QA: a premissa 'o filho ainda estava vivo quando a URL
apareceu' NUNCA foi afirmada, nem antes. Um pipe retem os bytes ja
escritos mesmo depois do processo sair, entao um falso futuro que
saisse cedo faria o teste passar pelo motivo errado. Agora ha
assercao de child.try_wait().is_none() no instante em que a URL e
encontrada.

Limite: a hipotese da CPU so se confirma na proxima corrida Windows.
Se o tempo continuar perto de 13s, a causa e outra.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(scripts): normalizar fim de linha ao ler arquivo em teste

Checkout do git no Windows traz CRLF; os testes liam arquivo e
comparavam/fatiavam com \n, entao quebravam so no Windows. Helper
readWorkflowText normaliza na leitura, aplicado em 7 pontos, mais a
leitura de fonte .rs no smoke do navegador. Mesmo padrao ja usado em
browser_panel.rs ao ler o wry vendorizado.

O QA varreu o repositorio e nao existe teste cujo proposito seja
detectar fim de linha — a normalizacao remove ruido de checkout,
nao sinal.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(test): teto de tempo derivado da vida do filho, em vez de numero absoluto

O '<2s' era proxy fraco e dependente de maquina: media partida do
Node junto com a leitura. Falhou por 2,32s no runner carregado do
macOS e do Linux.

Agora o teto sai da vida do filho: uma constante FAKE_CHILD_LIFETIME
de 30s, e o teto e um terco dela. A fracao tem justificativa
numerica, nao estetica: a regressao que importa (o leitor voltar a
esperar o processo sair) vale 30s, e 10s da 3x de separacao; o ruido
observado e ~2,3s, e 10s da 4x sobre ele.

As quatro literais soltas foram unificadas: o valor do setTimeout dos
DOIS scripts falsos e interpolado da mesma constante, e o prazo do
laco e o teto da assercao sao calculados dela. Sem isso, 'derivado'
seria so nome bonito — bastaria alguem mudar a vida do filho para o
teto virar palpite de novo.

A assercao de vivacidade (child.try_wait().is_none() no instante em
que a URL e encontrada) continua: ela prova a leitura de processo
VIVO; o tempo e teto de sanidade. As duas sao necessarias —
vivacidade sozinha nao pega leitor lento, porque o filho vive 30s.

PENDENCIA (achado do QA, nao bloqueante): o prazo do laco e o teto
da assercao usam o mesmo valor, entao uma URL que chegue logo acima
do teto produz 'nao achou' em vez de 'achou tarde' — mensagem de
falha enganosa. Correcao futura: laco em 2/3, assercao em 1/3.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* docs: o app nao e self-contained — exige Node do sistema

A documentacao prometia "self-contained" em 9 lugares; era falso. O
bundle traz o cli-package (@verboo/code 0.14.5) mas NAO traz o runtime
Node. node_runtime.rs resolve na ordem: env var -> Homebrew/nvm/fnm/
Volta -> PATH -> sidecar '(Future)' que nunca foi implementado.

Distincao preservada: npm nao e necessario, CLI global nao e necessario.
Node runtime >=22.0.0 exigido.

README citava 4 docs inexistentes (release-github-actions,
updater-signing, feedback-supabase, open-source-review) — referencias
removidas.

requirements/macos-arm64.json: cli-package 0.10.6 -> 0.14.5.

Nova secao cobre: navegador embutido, anotacoes no transcript, sidecars
midia, extensao Chrome.

User-Agent do navegador embutido: limite declarado honestamente —
override macOS+Linux, Windows default Chromium, Linux defensivo nao
verificado em runtime.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* docs(extension): politica de privacidade em conformidade com o manifest

Versao da documentacao 0.1.2 -> 0.2.1 para acompanhar o manifest 0.2.1.

Tres de 13 permissoes nao eram divulgadas:
- activeTab: screenshot.js captureVisibleTab fallback
- nativeMessaging: bridge.js host name
- <all_urls>: necessario para captureVisibleTab

file:// e chrome:// sao rejeitados pelo planner (navigate.js), nao
pela permisso <all_urls>.

Ponte nativa deixou de ser "planejada" — limites do protocolo
refletindo o codigo atual.

NENHUM codigo da extensao foi alterado, apenas documentacao.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(transcript): ferramentas do Chrome ganham icone e rotulo proprios

As ferramentas da extensao chegam como
'mcp__verboo-in-chrome__<tool>' e caiam no caso generico,
aparecendo como 'Usou ferramenta'. Agora cada uma tem rotulo
especifico ('Navegou no Chrome', 'Leu pagina no Chrome',
'Capturou tela no Chrome'...) e o tipo 'browser'.

Os nomes foram confirmados por TRES fontes independentes antes de
escrever o braco: o MCP_TOOL_PREFIX lido do cli.mjs empacotado, o
tools/list do proprio servidor MCP, e um turno real no app onde o
agente citou mcp__verboo-in-chrome__navigate.

MARCA: o icone e um globo generico, NAO o logo do Google Chrome.
A marca vive no rotulo em texto. Decisao deliberada — o app e
distribuido publicamente e assinado.

Canario novo (chrome_tools_canary.rs) le o browserTools.json REAL
da extensao e exige correspondencia NAS DUAS DIRECOES. Sem ele, um
rename do lado da extensao rebaixaria o passo a generico em
silencio. Provado por mutacao dupla.

O fallback 'Usou o Chrome' cobre a janela em que a extensao
INSTALADA diverge do manifest do repo (usuario com versao mais
nova da Store) — rotulo generico honesto e melhor que quebrar.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(chrome): avisar que o painel lateral precisa estar aberto

Descoberto em teste de campo: sem o painel lateral da extensao
aberto no Chrome, NENHUMA ferramenta executa — a chamada falha
com approval_ui_unavailable e o usuario so descobria lendo o
texto do agente.

Agora o cartao de Ajustes/Plugins avisa, e ha orientacao apos a
falha.

LIMITE DECLARADO, no relatorio e no codigo (models.rs): nao ha
deteccao PREVIA. Saber se o painel esta aberto antes de tentar
exigiria mexer no background.js da extensao, que e trabalho do
dono em paralelo. A limitacao esta escrita para o proximo leitor
saber que foi decisao, nao esquecimento.

A instrucao de como abrir o painel foi corrigida: dizia para
clicar no icone 'e escolher Abrir painel lateral', mas
background.js:181-190 mostra que o clique abre direto, sem menu.
O texto errado mandaria o usuario procurar opcao inexistente.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(transcript): cada passo do Chrome diz a propria acao

 - a feature entregue mostrava Usou o Chrome em todo passo, apesar da tabela de 8 rotulos especificos. O dono pediu, com print de referencia, que cada passo DISSESSE A ACAO — treze linhas identicas nao dizem acao nenhuma.

 - a causa: StepFlow.tsx:96 monta o rotulo com summarizeActions (turnBlocks.ts:114), que agrupa por TIPO usando PLURAL_KEYS. A primeira tentativa de correcao mexeu em activityDisplayLabel (App.tsx), que NAO e o caminho do transcript — o teste ficou verde numa funcao que a tela nao usa.

 - agora summarizeActions usa o rotulo proprio da acao para kind browser. Os outros tipos (read/edit/command) continuam achatando com contador, de proposito.

 - o teste passou a exercitar o CAMINHO REAL: chama summarizeActions com duas acoes de ferramentas diferentes e exige rotulos distintos. RED capturado antes: esperado Navegou no Chrome, Leu pagina no Chrome, recebido Usou o Chrome (2).

 - endurecido tambem o teste do Rust: assert!(label.contains("Chrome")) passava tanto para o rotulo especifico quanto para o fallback, ou seja nao distinguia nada. Agora afirma o rotulo especifico por ferramenta.

PENDENCIA nao bloqueante (achado do QA): com muitas acoes de Chrome consecutivas num mesmo bloco, o rotulo vira uma tira longa. Refinamento futuro: deduplicar com contador e/ou limitar a ~4 rotulos com e mais N no Chrome.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(transcript): cada acao do Chrome ganha sua propria linha

 - varias acoes de Chrome no mesmo turno colapsavam numa linha so ('Usou ferramentas (2)'). O dono mandou a referencia do produto concorrente, onde cada acao e uma linha com icone e rotulo proprio, e pediu esse estilo.

 - reusado o mecanismo que ja existia: 'isPerFileAction' (edicoes com caminho ganham linha propria em vez de 'Editou arquivos (2)') virou 'isOwnRowAction' e passou a incluir o kind browser. O comentario passou a explicar os DOIS motivos.

 - decisao registrada: browser NAO exige 'detail' para ganhar linha propria. Se exigisse, a maioria das acoes do Chrome continuaria colapsando e o pedido nao seria atendido.

 - agrupamento dos outros tipos preservado de proposito: dois 'read' continuam juntos, duas edicoes de arquivos diferentes continuam separadas.

 - os testes novos exercitam o caminho de RENDER (render do StepFlow + consulta ao DOM real), nao so a montagem de blocos.

OBSERVACAO DE CAMPO (achado do QA): uma acao nao-browser imediatamente apos uma sequencia de browser pode se anexar ao ultimo bloco; se aparecer no uso, o ajuste e a linha propria tambem fechar o bloco.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(chrome): apagar a moldura de presenca ao fim do turno via MCP

 - a moldura roxa de presenca continuava acesa depois que a tarefa do Chrome terminava. Causa: navigate.js acende em toda navegacao, mas a limpeza so existia no ciclo de vida do turno PROPRIO da extensao — o caminho dirigido por MCP nao tinha nenhuma chamada de presenca.

 - a primeira ancora cogitada (Drop do SessionGuard) foi DESCARTADA por evidencia: o host nativo e de vida longa, entao o Drop nao marca fim de turno.

 - ancora escolhida: EOF do stdio do servidor MCP, que e spawnado por cada execucao do CLI. complete_turn() so e chamado depois de service.waiting(), nunca entre chamadas de ferramenta — a luz nao apaga no meio do trabalho.

 - protocolo v2 com turnComplete/turnCompleteAck. Divergencia de versao falha limpo: extensao antiga responde protocol_version_mismatch com mensagem acionavel e o host segue; host antigo simplesmente nao manda o sinal, degradando ao comportamento anterior em vez de quebrar.

 - PROTOCOL.md atualizado — os tres lados (protocol.rs, bridge.js, documento) agora concordam na versao 2 e nos seis tipos.

LIMITES DECLARADOS (achados do QA, nenhum bloqueante):

 - cancelamento abrupto pode deixar residuo de moldura; e auto-curavel, porque o proximo turno de Chrome termina em EOF e limpa tudo.

 - dois turnos concorrentes dirigindo o Chrome: o que terminar primeiro apaga a moldura que o outro ainda usa; ele re-acende na proxima navegacao. Cosmetico e raro.

 - nao ha timeout de ACK no host: se a extensao travar sem responder nem desconectar, o relay fica pendente. Na pratica a porta desconecta e destrava.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat: ship side chats, settings redesign, and Chrome context

Add ephemeral side chats and selected-text Ask Verboo context, consolidate settings navigation, and render Chrome actions as individual transcript rows. Include protocol and presence cleanup, clearer interruption and error states, corrected translations, docs, tests, and the existing 0.6.2-beta metadata.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* docs: design interactive iOS simulator

* docs: plan interactive iOS simulator

* feat(chrome): intent classifier with serialized reclassification fallback and find/extract tools

- classify each turn by intent in the agent loop; when a conversation-classified
  turn emits a browser tool call, re-run it with browser tools forced, serialized
  through the browserControlQueue so concurrent turns never interleave actions
  (loop.js, background.js, routerClient.js)
- add find.js and extractPageContent.js tools: find returns real clickable
  references (text/tag/href/derived selector) for named targets; extract returns
  the full page text beyond read_page's truncation, chunked by the loop
- bump extension to 0.3.1 (manifest version/version_name, package.json)
- update PRIVACY.md and privacy.html to match the feature

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat: ship iOS simulator, signed CLI updates, and Chrome automation (#67)

Integrate the accumulated authorized desktop work: macOS iOS simulator tooling, independently signed CLI installation and updates, cross-platform authentication/provider support, and durable background Chrome automation with native MCP integration.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* feat(providers): add multi-account usage to desktop

* feat(context): add memory context with localOnly filtering

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(providers): harden account preflight with capability fallback, model cache, and CLI update notice

Addresses QA findings M1, M2, and M5: capability fallback, model cache, and CLI update notice.

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(feedback): fallback opens a GitHub issue with stable supabase codes

Co-Authored-By: Verboo Code <noreply@code.verboo.ai>

* fix(providers): serde default for turn account, spawn canary, and dead wrapper removal

Co-Authored-By: Verboo…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant