feat(gateway): image generation offload - job-service routing, sync_id, poll progress, per-device auth - #86
feat(gateway): image generation offload - job-service routing, sync_id, poll progress, per-device auth#86siddWednesday wants to merge 202 commits into
Conversation
Research + decided direction: intents and MCP as primary action paths, vision-based agent loop as fallback, engine as @offgrid/use in shared, vision model as a downloadable catalog entry (GUI-Owl-1.5 / Qwen3-VL). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five phases, Aug 12 - Dec 2 2026, one demoable checkpoint per phase. Phase 1 (semantic rail) ships standalone value by Sep 9; the vision model install is only needed from phase 3 week 3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
16 weeks to 9 (Aug 12 - Oct 14). Code-heavy phases shrink the most (engine 4wk to 2wk); integration-heavy phases keep slack since review, TCC flows, and real-app iteration do not compress. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sibling shared checkout + file: consumption decision added to phase 0; mobile follows desktop as an adapter-only project on the same engine. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bile adapter Approach doc's open questions become a decision log (graph roles, eval harness, sequencing decided; model pick narrowed to install time; package name pending lead confirm). Plan gets an After v1 section with the mobile adapter's prerequisites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Timeline table now shows which repo each phase builds in; dependencies record the OGAD push-access ask (pull-only as of Aug 11, shared has push). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… re-phased timeline From the Clawbot/product-UX research: embedded agent browser (WebContentsView + webContents.debugger CDP, indexed snapshot, per-site cards, takeover with capture-kill) becomes phase 3, ahead of native GUI - zero OS permissions, no new models, shippable cut at Sep 23. Native adapter + vision moves to phase 4, hard targets to phase 5 (ends Oct 27). Direct-reuse list added (UI-TARS sdk/ScreenMarker, nanobrowser, nut-js fork, macos-automator-mcp, bytebot takeover pattern, Peekaboo). Brand guidelines (off-grid-ai/brand + @offgrid/design tokens + docs/DESIGN.md) bound as standing build rules. OpenClaw teardown informs the zero-setup bar and the safety avoid-list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 0 of computer use. Widens the MCP-only `mcp:proposeApproval` hook into a shared `actions:proposeApproval` seam every executor (MCP today, computer + browser next) can gate through. Adds an ActionRisk taxonomy (read/navigate/mutate/irreversible) with a single shouldGate() source of truth, and per-executor riskOf() classification - a GUI click's risk is not derivable from a tool name the way the old isActionTool regex assumed. Backward compatible: proposeActionApproval prefers the new hook but falls back to the legacy name via hasHook(), so a desktop-pro build that has not yet migrated keeps gating MCP writes instead of silently running them. MCP behaviour is unchanged (read-verb tools -> read, everything else -> mutate), pinned by the existing queue-vs-execute dbtest plus new risk tests. Verified: tsc (node + web), 41 unit tests, 7 dbtest, eslint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 0 packaging groundwork. Adds the Info.plist usage-description keys the semantic action rail needs — Apple Events (Messages/Mail/Notes), Calendars (+ pre-14 legacy key), Reminders, Contacts, Photos — so a hardened-runtime build is granted each capability instead of being refused before the OS prompt. The apple-events entitlement was already present. Copy follows off-grid-ai/brand: outcome first, privacy as proof, no em dashes. A source-reading test pins every key + the entitlement and enforces the no-em-dash rule on the strings added here. Verified: 8 new tests, YAML parses, config-reading tests green, eslint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1 semantic rail, first slice. Adds the single seam every native
capability goes through: a Swift one-shot helper (scripts/actions-helper)
that takes one JSON command, performs a scoped EventKit action, and prints
one JSON line; and a Node invoker (runNativeAction) that resolves the
binary packaged-vs-dev like ocr.ts and parses the reply. Handled failures
(denied permission, bad args) are in-band { ok: false } results, never
throws, so the tool loop has one shape to report.
Backend covers calendar create + list to start; the switch and command
namespacing leave reminders/contacts/photos as additive cases. Pure logic
(contract, path resolution, response parsing) is split into
native-helper-logic.ts and unit tested; the Swift compiles clean under
swiftc 6.2 targeting macos13. Not yet wired to a tool or shipped in CI -
that lands with the tool that calls it, so no dead binary ships early.
Verified: tsc node, 11 logic tests, swiftc build, eslint clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1 semantic rail, made reachable. Registers a native-action tool extension (macOS-only) exposing calendar_create_event and calendar_list_events to the model; create is a mutate that offers itself to the shared approval seam (queues in pro, runs in the free build), list is a read that runs directly. Both route through runNativeAction to the EventKit helper. Adds 'native' to ActionKind so the approval UI and audit can label semantic OS actions apart from GUI computer use. The release workflow now builds + stages the helper into resources/bin, so a packaged build ships it (self-contained; if it fails the tools report 'not available' and nothing else breaks). Boundary injection mirrors the MCP extension, so the gate-then-run contract is unit tested end to end without a real EventKit call. Verified: tsc node, 90 tests across actions+tools+hooks, release.yml parses, eslint clean on new files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
reminders_create (mutate, gated) and reminders_list (read) alongside the calendar tools, via EventKit reminder access in the helper. Extracts a shared formatCreated() so each create tool reuses one confirmation shape instead of re-encoding it. Verified: swiftc build, tsc node, 16 tool tests, eslint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
contacts_search (read) via the Contacts framework (CNContactStore), returning matching names with phones and emails - the lookup the message and event tools need to resolve a person. Read-only, so it runs without approval. Verified: swiftc build, tsc node, 82 tests across actions+tools, eslint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
messages_send (iMessage) and mail_send (Mail), both mutates gated through the approval seam - the 'message Ali' half of the rail that composes with contacts_search to resolve a name to a handle. Backed by AppleScript via NSAppleScript in the helper; user-supplied values are escaped before interpolation so a quote or backslash cannot break the script or inject statements. Uses the apple-events entitlement + NSAppleEventsUsageDescription already staged in phase 0. Verified: swiftc build, tsc node, 19 tool tests (incl. a messages_send gating case), eslint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
open_url opens a web page, mailto: draft, or app scheme (whatsapp://send) via NSWorkspace - the navigate that starts the WhatsApp path and the first use of the non-gating navigate risk class (it opens, it does not submit, so no approval). Verified: swiftc build, tsc node, 85 tests across actions+tools, eslint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
build-mac-local.sh now builds and copies actions-helper into resources/bin alongside the other native helpers, mirroring the release.yml step, so a local packaged build includes the semantic-rail backend (otherwise the calendar/messages/mail tools report 'not available'). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parseDate now tries full ISO 8601 (with timezone) first, then falls back to the local forms a model commonly emits - 2026-08-13T15:00:00, 2026-08-13T15:00, and 2026-08-13 - interpreted in the user's timezone. Without this a create-event/reminder call from the model would fail whenever it omitted the timezone. Verified: swiftc build; invalid dates still rejected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # electron-builder.yml # src/main/bootstrap/hookRegistry.ts # src/main/index.ts
Supersedes the mobile-use-replication framing. Leads with the product: a proactive, context-grounded local assistant. Two generators (routine: detected + demonstrated; reasoned: commitment + world-knowledge + gap-check) feed one gated spine (resolve slots from memory -> gate shows resolved values -> execute via rails cheapest-first). Rail hierarchy with vision correctly placed last; three-kinds-of-seeing distinction; the demonstration recorder; memory-grounded slot resolution; honest reliability tiers; the moat (knows you + proactive + private + routes to the cheapest reliable rail). Plan re-phased: reasoning engine first (the magic, memory-driven, safe), vision fallback last. Records rail 1 as already built. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phases 2-6 now run Aug 13-26 (phases 0-1 already done). Reasoning engine Aug 13-16, engine Aug 17-18, recorder+replay Aug 19-22, agent browser Aug 23-24, vision fallback + hard targets Aug 25-26. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Self-contained brief for generating the demo artifacts: the product vision, the inlined brand system (Menlo, emerald-on-black, brutalist, dense), a five-screen day-in-the-life story (assistant home, resolved approval card, flight nudge, record-a-routine, routines library), copy voice, deliverable format, and the anti-patterns to avoid. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Come-ups live in Day (Needs you section, ephemeral rows - never tabs); the gate is inline + the existing Actions queue; Routines is the one new tab; record is a modal from it; away-presence via toast + menu-bar count. Adds the general-engine point (no per-situation verticals - no Flights tab) and Screen 1 now shows a mix of situations (flight + deck + renewal + morning brief) so the generality reads at a glance. Uses the real left-rail nav names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Surveys who is already doing each piece of the proactive assistant: proactive surfacing (Magic Cue, Pulse, Recall), context resolution (Glean, Shortwave, Copilot), commitment detection (Gmail Nudges, notetakers, MSR speech-act research), routines/teach-by-demo (Automator, Shortcuts, RPA recorders + self-healing), and confirm-before-acting (Operator, Manus, the false-confirmation failure). Three strategic findings: local-first is open whitespace (Rewind/Limitless->Meta, Dot shutdown), context-resolution-with-evidence-before-acting is unshipped (our differentiator), and the GUI-automation reliability ceiling is real (Mariner/ChatGPT-travel killed) - validating cheapest-rail-first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Folds in the two parent researchers' consolidated syntheses. Routines section gains the academic build blueprint for slot induction + self- healing (Agent Workflow Memory, Alloy, SUGILITE/APPINITE, LUMOS AX-thesis, hierarchical-not-flat, action-effect verification, Morae consequential- step gating, tiered permissions, graduated trust, trycua background AX control). Adds two lessons: detect-completion-and-auto-retire commitments (only a local memory product can, our biggest anti-nag move) and bind the approval card to the exact executing payload (the Alexa+ read-back bug). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ence/confidence card Rewrites the brief from the actual app screenshots so generated artifacts look like Off Grid AI: the real left-rail nav (with Routines added after Actions) and its emerald active state, the real component vocabulary (outlined buttons, pill toggles, status tags, the bottom-CTA-card shape reused for come-ups/toasts), Menlo everywhere, flat + outlined + ~6px radius + dotted-grid, exact dark/light tokens. Corrects the earlier over-literal 'razor-sharp brutalist' framing that made output not look like the app. Screen 2 now shows each resolved slot with its evidence and a confidence tag, plus a low-confidence 'which deck did you mean' disambiguation variant - the screen no competitor ships. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pen questions The system design from the architecture discussion: the model proposes / durable queue guarantees split, the Action durable record + state machine, the reliability stack that survives a weak local model, the typed action-handler registry + scope map (two reliability tiers), and the one cross-platform core + adapters (mobile included). Section 7 locks the answered decisions (incl. scope and the model-agnostic pipeline); section 8 explains the six open questions for the team - exactly-once per rail, scheduling, trust graduation, mobile v1 target, open-core placement, verification depth - each with options, the decision, and our lean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two layers: the assistant (brain) and the rails (actuation, behind the DeviceController interface). Computer use = the vision rail specifically, not the whole layer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… adapters) An at-a-glance component diagram: generators -> the assistant (queue, resolver, gate, router, verify) -> DeviceController port -> the rails (semantic, browser, accessibility, vision) -> per-platform impls. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
In the Wednesday TRD + PRD house style: System Architecture as a C4 model (Context L1 + Container L2), a sequence/swimlane by actor for the send-the-deck flow, and the product user flows (Day + Chat entry -> gate -> verify, plus the two ways a routine is born). Mermaid so they render on GitHub / mermaid.live / an artifact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pro too The inline approval card (ActionGateDock, mounted in the chat) was only fed when no pro queue was listening - so in a pro build the gate always went to the pro queue (a notification) and the in-chat Approve/Reject card never appeared. That was flagged in the code as "pro queue wins until the migration". Do the migration: gateHost now parks AND emits the inline card whenever a human is needed (pro queue accepted it, or an inline surface is registered). The notification and the in-chat card are two views of the ONE engine gate - whichever the user acts on calls resolveActionGate for the same actionId (idempotent), and the other view settles on the outcome broadcast. A chat-initiated computer-use task is now approvable right where it was asked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add registerApprovalModeProvider / computerApprovalMode to gate-host so the user's "Auto-approve vs Ask every time" choice governs the gate. gateHost short-circuits to approve when the mode is 'auto' (the task still journals and its outcome shows in chat); 'ask' (the default when no provider is registered) parks for approval as before. Pro owns the persisted setting + the Sync-sharing toggle and registers the provider - core just exposes the seam, so the free build has no computer-use logic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d + mesh) The inline gate surface was a single slot, so only one consumer could render a parked gate. Mobile approvals need a second: pro forwarding the gate to paired phones over the mesh, alongside the desktop chat card. Make it a Set of subscribers - registerInlineGateSurface adds/removes, gateHost fans each parked gate to all of them. Each surface resolves the ONE engine gate via resolveActionGate; the first verdict wins, the rest are no-ops. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Explore surface now renders each capability as a bordered panel (icon
tile + teaches line) holding a dense card grid, adapting to its container
(two panel columns on the Explore screen, one in the chat empty state).
Preset titles become short capability labels - the prompt-phrased titles
("Find me a flight to book", "What did I work on today?") no longer leak
the seeded prompt onto the card. Robust ungated presets get a ready
marker; the Explore screen gains a real page header with catalog-computed
meta and hides the section's compact intro via showIntro.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Boots the free build on a fresh profile, walks the Explore screen and the chat empty state, and asserts the capability panels render with card labels while the seeded prompt never appears on the surface. Screenshots captured for evidence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every preset now carries its own Phosphor icon in the catalog SSOT (AirplaneTilt, SpotifyLogo, Crop, ...) so both placements show the same mark; the card leads with the icon and warms it emerald on hover. Each panel header gains a small runs-count meta. Tests cover the icon invariant, the per-card rendering, and the panel counts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…app quits The agent WebContentsView runs with backgroundThrottling off (so the agent can work while the user does other things), and it was only ever hidden - bounds set to zero - never muted or torn down. So a web_task that started a video kept its audio playing after the pane closed, the window was hidden, or the app was "quit" (on macOS a window close leaves the process alive). - Mute the view's audio whenever it's hidden, unmute when shown, so closing the browser pane actually silences it. - React to the window too (setRegion only fires while the pane is mounted): mute on window hide, fully dispose (remove + close the WebContents) on window close. - disposeBrowserHost() on before-quit stops media immediately rather than whenever the process finally exits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the amber "this model can't ground" warning (VisionGrounderNudge) - it added noise on every non-grounder computer-use run. Removed the render + its import. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The card sat on screen while the action ran and the outcome made its way back. resolve() now drops the pending card immediately on approve/reject/edit; the outcome still lands as its own row, and an edit re-gates as a fresh pending event. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ip notch The left-edge resize handle was covered by the native WebContentsView below the header, so it was only grabbable in a thin strip. Inset the web region (ml-4) to give the handle a real full-height gutter, and swap the faint bar for a centered DotsSixVertical grip so it clearly reads as draggable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The live step narration for a web task sat in a feed under the browser pane. Move it into the chat turn: MemoryChat subscribes to browser:onStep + onTaskState and renders the recent steps in the streaming assistant turn (a new task resets them, and they fall away when the turn ends). WatchedBrowserPane drops its step feed + summary and keeps just the goal, live browser view, and takeover prompt. Computer-use task steps are unchanged (they stay in their own small modal). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The card labels/blurbs are unchanged; the actual prompt each preset SENDS is now defined - it names the site or app and states the task plainly, so it routes cleanly and reads sensibly, e.g. find-flight now sends "Go to skyscanner.com and help me find a flight to book. I will give you the route, dates, and budget when you ask." Named targets: Skyscanner, Google Maps, Google Shopping, Spotify, Preview, Mail; the memory/phone prompts point at what was captured on the Mac. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolutions: - packages/models: accept main's de-vendoring (now file:../shared/packages/models, where the grounder work already landed). - pro: adopt main's submodule model (.gitmodules); pointer bumped to the synced desktop-pro feature commit in a follow-up once desktop-pro is synced. - MemoryChat.tsx: took main's rewrite, re-grafted the 12 chat-approvals surgical changes (ExploreSection seed handoff, ActionGateDock/VisionSupervisorOverlay, self-contained WebTaskStepFeed for the streaming turn); dropped the MessageNudge cutoff change (superseded by main's ResponseCutoffNotice). - ipc.ts: main's superset chat-stream-state imports. - tools.ts: widen the planning-path return to main's plural imageRequests contract. - vitest.db.config.ts + release.yml: union of both sides' exclusions / build steps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… by the sync - pro: bump submodule pointer to the synced desktop-pro feat/mesh-tool-grant (4b882d8). - docs: restore the directory name (an earlier commit accidentally renamed docs/ -> docshi/); main carries docs/, and the coverage ledger + release checklist read docs/. - WatchedBrowserPane: guard the browser IPC method calls (onTaskState?./onTakeover?.) so App integration tests (and a partial preload) don't crash on mount when the optional browser API isn't wired. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The vision rail's grounder returns click points in the captured screenshot's DIP space. macOS positions the cursor in points, so those went through raw and worked; a DPI-aware Windows process positions in physical pixels, so a click on a 125/150% display landed short. New pure coordinate-mapping module maps a DIP point -> actuation space (x scaleFactor on win32, x1 on mac) + the display origin offset for multi-monitor, wired into vision-host's actuate(). 12 unit tests cover mac Retina (no scale), Windows 100/125/150/200%, second-monitor offset, and per-verb mapping. Mixed-DPI multi-monitor is a documented follow-up (no physical-bounds API). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nut-tree-fork/nut-js drives the vision rail's cursor/keyboard via the prebuilt (N-API, ABI-stable) libnut-win32 addon - an optional dependency that npm can skip. When it is missing, loadActuation() returns null and the rail refuses every task with no crash and no obvious cause. Gate the Windows build on the prebuilt .node being present after npm ci, mirroring the fetch script's fail-on-missing-exe rule. Packaging + Electron-ABI handling already work via postinstall install-app-deps + electron-builder smartUnpack; this closes the silent-omission hole.
The browser/semantic/vision rails live in core and are per-platform via one pickByPlatform seam. Browser (CDP) and semantic (local Outlook COM: create + read-back + undo) are Windows-ready and tested; the vision computer-use rail is code-ready (nut.js actuation + DPI/scale coordinate mapping + CI presence gate) and needs a real-Windows actuation run. Graph online fallback + mixed-DPI multi-monitor are the noted follow-ups. permissions.ts already no-ops off darwin.
The Windows build failed at typecheck for two reasons, both in the shared setup: - @offgrid/models was never built (only sync + use were), so every 'file:' consumer hit 'Cannot find module @offgrid/models'. Add its build step (mirrors release.yml). - the shared checkout matches by BRANCH NAME (github.ref_name). This core branch depends on shared feat/use-approval-tiers (effectId/undo/computer_task in @offgrid/use) whose name does not match, so it fell back to shared main and the types were missing. Add a shared_ref dispatch input (mirrors pro_ref) so a build can pin the shared ref explicitly. To build this branch: dispatch with shared_ref=feat/use-approval-tiers and pro_ref=feat/mesh-tool-grant.
…ownloaded
Computer use swapped in the hard-coded grounder (UI-TARS) before running, and
setActiveModel doesn't check the files are present - so with UI-TARS not
downloaded, the llama-server load failed ('Models not found') and the whole task
died silently ('nothing happens after Approve'). Now the swap is gated on the
grounder actually being installed: if it is, swap as before; if not, run computer
use on the ACTIVE model and warn (vision-host already flags a non-grounder), never
hard-failing on a missing model. Pure decision extracted to grounder-plan.ts (3
tests); grounder-loader wires the real installed-check + does the fallback.
Note: a generic vision model is not a trained GUI grounder, so clicks are less
precise than UI-TARS - but the rail now runs end-to-end (capture -> model ->
actuate) on any vision model, which is what unblocks Windows testing.
Computer use on Windows had NO tier-1 accessibility rail - resolveNativeApp/ routingSnapshot were hard-gated to darwin, so every task fell to vision (with no app-open step and, without a grounder, floundering). Bring the mac AX rail's 'open the app, drive it by element LABEL with a normal chat model' path to Windows: - ax-uia-script.ts: pure PowerShell + UI Automation script builders (apps list / element tree / foreground), emitting the EXACT parseAxElements contract ([WINDOW_TITLE] + per-element JSON). UIA BoundingRectangle is physical px, which is nut.js's space on Windows, so no DIP scaling here. 9 tests (contract + injection safety). Mirrors the Windows semantic rail's PowerShell approach - no compiled binary to build or ship. - ax-win.ts: the Windows AxBackend - runs those scripts via powershell.exe, fail-closed to []/null so a dead UIA read falls through to vision. - ax-host.ts: refactored to a platform backend seam (macAxBackend = the Swift helper + open -a, unchanged; windowsAxBackend for win32). resolveNativeApp / routingSnapshot / runTask now go through backend.available()/listApps/activate/ snapshot instead of darwin-hardcoding. Mac path byte-for-byte the same. computer_task is already exposed + hinted on win32, so this activates tier-1 automatically. Needs on-Windows validation (UIA element quality, coords, app resolution, perf) - the PowerShell behavior can't be exercised from macOS.
Carries desktop-pro's 'a remote eviction must not delete the Pro license' fix, so a build from this branch lets you evict a phone and re-pair without the desktop going dark or losing its license. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Core's /mcp action gate no longer trusts a single global token. It delegates to a live-token provider that the pro sync layer registers (registerActiveActionTokens), so ONLY the tokens of currently paired + tools-allowed devices authorize; the legacy global token stays as the no-pro fallback. authorizeBearerAny matches a bearer against the live set - an empty set (no paired peer / un-paired) fails closed, which is what makes tool access actually revoke on un-pair. Bumps pro to d3bad1a (per-device token on the pairing row). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… Release) Dispatch with artifact_only=true to get the SAME signed + notarized macOS DMG uploaded as a repo-scoped, auto-expiring Actions artifact instead of a published Release. Gates off the release create/upload, the pro-mac.yml + latest/nightly aliases, Slack, and the whole Windows job - nothing lands on the releases page or the update feed. Normal releases are unchanged (default false). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…c bearer auth The three image POST routes now start jobs via ImageGenerationJobService instead of calling generateImage directly - restoring the single-job admission (busy = 429), giving gateway images a syncId + sidecar so they land in this Mac's gallery and ship to paired devices over the mesh like local generations. Responses carry sync_id so a paired requester can dedupe the synced copy; pending polls report live stage/step. A presented bearer is verified against the live per-device action tokens (401 on mismatch); no credentials keeps the documented open-LAN posture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|



What this does
Review note - merge order: this branch sits on top of
feat/windows-parity(it uses its per-device token infrastructure). Merge that to main first; this PR's diff then collapses to the single offload commit (617e982). Mobile half: off-grid-ai/OGAM#638.The desktop half of phone->Mac image-generation offload: a paired (or any LAN) phone POSTs the gateway's existing
/v1/images/generationsin async mode and receives a desktop-quality image; the mobile half is the matchingfeat/image-gen-offloadbranch in OGAM.ImageGenerationJobServiceinstead of callinggenerateImage()directly. That restores the single-job admission for gateway callers (second request = 429), and gives every gateway image a syncId + sidecar - so it appears in this Mac's own gallery and ships to paired devices over the existing mesh share, exactly like a locally started generation.sync_idso a paired requester dedupes the mesh copy against the HTTP result (the phone uses it as the local file id).{stage, step, total}) - the phone maps this into its normal generation card.Verification
Unit + integration: 5 pure tests (
image-route.test.ts: response shaping, sync_id, poll progress gating) + 4 against the real booted gateway (image-route-auth.integration.test.ts: open posture reaches 501, valid token accepted, invalid = 401 on both routes). Full suite green through the pre-push gate.Verified live on a dev build (2026-08-26): 501 honest reason with no model; SDXL pulled headlessly via
/v1/models/pull; sync generation returned b64 + sync_id with the sidecar landing ingenerated-images/; async 202 + poll showedstep 3/4 -> 4/4 -> completedwith the result held at the poll URL; concurrent request 429'd; bad bearer 401'd. End-to-end phone->Mac generation confirmed on a real iPhone against this branch (image generated on the Mac, delivered into the phone chat).No screenshots: the change is headless (HTTP surface); the live curl transcript above is the visual. The user-facing surface ships in the OGAM PR.
🤖 Generated with Claude Code