Skip to content
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
809bf5a
fix(preview): an ogcapture URL is parsed, not sliced, so Windows find…
alichherawalla Aug 12, 2026
69f5c13
test(preview): the drive letter, the POSIX path, and the escaping in …
alichherawalla Aug 12, 2026
3046ac9
chore(llama): one owner for the engine version, moved to b10369
alichherawalla Aug 12, 2026
5e165a8
fix(sidebar): a row's hover colour comes from the text token, not the…
alichherawalla Aug 12, 2026
f2b47c1
refactor(chat): one decoder and one content rule for an image-bearing…
alichherawalla Aug 12, 2026
501118f
fix(chat): a turn's attachments are read from the turn, not from the …
alichherawalla Aug 12, 2026
84c7e8b
feat(models): Nemotron 3.5 Lightning and Muse Glimmer, tagged new + c…
alichherawalla Aug 12, 2026
610e633
chore(llama): package.json owns the engine version, and both platform…
alichherawalla Aug 12, 2026
bbe6e41
docs(gaps): MTP belongs in @offgrid/models, not in one app's screens
alichherawalla Aug 12, 2026
051302b
fix(chat): the thinking controls come from the loaded template, not f…
alichherawalla Aug 12, 2026
0b3f418
fix(models): one size rule, so a card stops printing the same file in…
alichherawalla Aug 12, 2026
f04edbb
fix(imagegen): a generated image previews on Windows
alichherawalla Aug 13, 2026
511fd2d
fix(files): every attachment failed on Windows, and sharp took the blame
alichherawalla Aug 13, 2026
33bb2a0
fix(chat): a streamed reply is stored under the id its peers already …
alichherawalla Aug 13, 2026
79bb06f
feat(chat): a file still on its way draws a loader on its own turn
alichherawalla Aug 13, 2026
6ec7b84
feat(chat): publish and render generation phases
alichherawalla Aug 13, 2026
8e05797
fix(chat): preview synced image attachments
alichherawalla Aug 13, 2026
d37f0f3
fix(chat): present synced generation results
alichherawalla Aug 14, 2026
bbe3cc7
fix(chat): tighten consecutive tool rows
alichherawalla Aug 14, 2026
ae5883a
fix(chat): align live image state and markdown rendering
alichherawalla Aug 14, 2026
282523d
refactor(models): consume shared catalog package
alichherawalla Aug 14, 2026
075917a
fix(desktop): resolve static analysis findings
alichherawalla Aug 14, 2026
b960f6b
refactor(chat): split message presentation sections
alichherawalla Aug 14, 2026
e1cbac5
test(desktop): verify runtime boundaries through behavior
alichherawalla Aug 14, 2026
76b370a
fix(coverage): keep DB report on service code
alichherawalla Aug 14, 2026
834bdf5
fix(imagegen): preserve the active stream on busy requests
alichherawalla Aug 14, 2026
20b7187
fix(llm): apply the loaded thinking dialect everywhere
alichherawalla Aug 14, 2026
d93b750
fix(chat): close synced lifecycle review gaps
alichherawalla Aug 14, 2026
5299706
fix(capture): reject malformed capture URLs
alichherawalla Aug 14, 2026
1438e3f
docs(windows): state runtime revision parity
alichherawalla Aug 14, 2026
86ecb01
fix(coverage): keep renderer code out of DB reports
alichherawalla Aug 14, 2026
996cd58
fix(chat): one rhythm for reasoning and tool rows in a turn
alichherawalla Aug 15, 2026
52eb21f
test(build): teach the whisper fake the load command the script gates on
alichherawalla Aug 15, 2026
184a49b
test(packaging): name the pro renderer by what it delivers
alichherawalla Aug 15, 2026
046387c
fix(chat): an attached image is a thumbnail, not a full-width preview
alichherawalla Aug 16, 2026
98030a8
Revert "fix(chat): an attached image is a thumbnail, not a full-width…
alichherawalla Aug 16, 2026
3e33a29
fix(chat): an attached picture fills its bubble and crops, like WhatsApp
alichherawalla Aug 16, 2026
4302f54
fix(chat): only a filling picture gets a filling click target
alichherawalla Aug 16, 2026
e22a158
docs(gaps): non-image attachments sync to desktop as "text" and previ…
alichherawalla Aug 16, 2026
9664273
fix(chat): render a document's bytes, and let sync decide what an att…
alichherawalla Aug 16, 2026
83ca547
fix(chat): serve a PDF over the media origin, and let frame-src allow it
alichherawalla Aug 16, 2026
9773d51
fix(media): serve a PDF as application/pdf, not as a download
alichherawalla Aug 16, 2026
94cbfd2
fix(desktop): play a synced voice note instead of speaking it back
alichherawalla Aug 16, 2026
05412e6
track pro as a submodule, so desktop-pro owns every file under pro/
alichherawalla Aug 18, 2026
dd7be6c
stop ignoring /pro/, now that it is a tracked submodule
alichherawalla Aug 18, 2026
d13f8f3
docs: require explicit architecture checks
Aug 18, 2026
3542625
fix(sync): update Pro replay ordering
Aug 18, 2026
d6ee531
chore(pro): update licensing activation behavior
Aug 19, 2026
8b1df67
chore(pro): update protected storage pairing behavior
Aug 19, 2026
48c55c3
fix(secrets): avoid unavailable OS encryption
Aug 19, 2026
46b6d7f
chore(pro): update protected license cache behavior
Aug 19, 2026
a4d06a7
fix(macos): recover secure storage from rewritten home
Aug 19, 2026
b9d9685
chore(pro): update attachment sync identity
Aug 19, 2026
526a452
chore(pro): update shared model package adapter
Aug 19, 2026
19fd4a0
chore(pro): update desktop sync ordering
Aug 20, 2026
ec12a33
fix(chat): reattach active desktop streams
Aug 20, 2026
d815540
fix(chat): preserve thinking during sync refresh
Aug 20, 2026
df935e7
fix(chat): remove stale web search placeholder
Aug 20, 2026
115367e
fix(chat): make Gallery and history panel controllable
alichherawalla Aug 20, 2026
2681be9
fix(chat): place collapse control in conversation list
alichherawalla Aug 20, 2026
ee56786
fix(chat): keep conversation opener at left edge
alichherawalla Aug 20, 2026
5929d01
fix(chat): keep one history toggle in header
alichherawalla Aug 20, 2026
1613b36
fix(chat): smooth history panel sizing
alichherawalla Aug 20, 2026
95ba4da
assets: bundle generated image style previews
alichherawalla Aug 20, 2026
f6c8f4d
feat: show bundled style previews in every image composer
alichherawalla Aug 20, 2026
49fe656
fix(image): preserve enhanced generation prompt
alichherawalla Aug 20, 2026
894b621
fix(chat): expand inline image styles
alichherawalla Aug 20, 2026
be93a7d
fix(image): unify live generation state
alichherawalla Aug 20, 2026
df267d9
fix(image): separate prompt stream from preview
alichherawalla Aug 20, 2026
f20f86f
fix(image): restore live preview updates
alichherawalla Aug 20, 2026
03e0698
fix(image): expand live preview layout
alichherawalla Aug 20, 2026
fc6dd67
fix(models): accept transferred vision variants
alichherawalla Aug 20, 2026
9a8c607
fix(models): preserve transferred variants
alichherawalla Aug 20, 2026
c01b970
fix(models): activate transferred vision variant
alichherawalla Aug 21, 2026
e3bf1fc
test(chat): stabilize project scope selection
alichherawalla Aug 21, 2026
7c9e721
test(chat): stabilize composer input setup
alichherawalla Aug 21, 2026
4c8ae05
chore(pro): update desktop transfer gates
alichherawalla Aug 21, 2026
f98afdd
fix(models): delete transferred variants
alichherawalla Aug 21, 2026
0e44b00
test(files): cover optional image decode probe
alichherawalla Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: '24' # node:sqlite (used by integration tests) is available unflagged
# @offgrid/sync is a file: dependency on the SIBLING shared monorepo ("file:../shared/packages/sync").
# It arrived with the sync work and CI never provisioned it, so every job that installs would fail on the
# same missing module. actions/checkout refuses a path outside the workspace, so it lands inside and is
# moved up one level - exactly where the file: specifier points.
# Shared packages are file: dependencies on the SIBLING shared monorepo. CI must provision it before
# installing Desktop. actions/checkout refuses a path outside the workspace, so it lands inside and is
# moved up one level - exactly where the file: specifiers point.
#
# Matching branch first, main as the fallback, the same shape as the pro checkout above: a PR that changes
# the app and the shared package together must be tested against the package it expects.
Expand All @@ -93,7 +92,7 @@ jobs:
- name: Put shared beside this checkout
run: |
if [ ! -d _shared ]; then
echo "::error::off-grid-ai/shared was not checked out - @offgrid/sync cannot resolve. Check CI_CROSS_REPO_TOKEN."
echo "::error::off-grid-ai/shared was not checked out - shared packages cannot resolve. Check CI_CROSS_REPO_TOKEN."
exit 1
fi
rm -rf ../shared
Expand All @@ -107,6 +106,7 @@ jobs:
# later gates run against dependencies nobody committed. A drifted lock should stop the build.
npm --prefix ../shared ci
npm --prefix ../shared/packages/sync run build
npm --prefix ../shared/packages/models run build
- run: npm ci
# Hard gates: types + the full test suite.
- name: Typecheck (core)
Expand Down
12 changes: 7 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ jobs:
- name: Build llama-server (pinned deployment target)
run: |
command -v cmake >/dev/null || brew install cmake
MACOS_DEPLOYMENT_TARGET=13.0 LLAMA_REF=b9838 bash scripts/build-llama.sh
MACOS_DEPLOYMENT_TARGET=13.0 bash scripts/build-llama.sh
otool -l resources/bin/llama/llama-server | awk '/LC_BUILD_VERSION/{f=1} f&&/minos/{print "[ci] bundled engine minos="$2; exit}'
# Rebuild the one-shot Whisper CLI instead of trusting the committed LFS
# payload. The production script disables OpenMP/BLAS auto-discovery and
Expand Down Expand Up @@ -189,7 +189,7 @@ jobs:
with:
node-version: '20'
cache: 'npm'
# `@offgrid/sync` is a file: dependency on the shared monorepo, so it must sit BESIDE this
# Shared packages are file: dependencies on the shared monorepo, so it must sit BESIDE this
# checkout before `npm ci` runs - an install cannot resolve a path that does not exist, and the
# typecheck inside `npm run build` then fails on every import of it. `ci.yml` has done this since
# the sync work landed; this workflow did not, which is why no release has built since 2026-07-30.
Expand Down Expand Up @@ -218,7 +218,7 @@ jobs:
- name: Put shared beside this checkout
run: |
if [ ! -d _shared ]; then
echo "::error::off-grid-ai/shared was not checked out - @offgrid/sync cannot resolve. Check CI_CROSS_REPO_TOKEN."
echo "::error::off-grid-ai/shared was not checked out - shared packages cannot resolve. Check CI_CROSS_REPO_TOKEN."
exit 1
fi
rm -rf ../shared
Expand All @@ -228,6 +228,7 @@ jobs:
# drifted lock stops the release instead of resolving a graph nobody committed.
npm --prefix ../shared ci
npm --prefix ../shared/packages/sync run build
npm --prefix ../shared/packages/models run build
- name: Install dependencies
run: npm ci
# Stamp the resolved version into package.json so electron-builder picks the
Expand Down Expand Up @@ -426,7 +427,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: '3.12'
# `@offgrid/sync` is a file: dependency on the shared monorepo, so it must sit BESIDE this
# Shared packages are file: dependencies on the shared monorepo, so it must sit BESIDE this
# checkout before `npm ci` runs - an install cannot resolve a path that does not exist, and the
# typecheck inside `npm run build` then fails on every import of it. `ci.yml` has done this since
# the sync work landed; this workflow did not, which is why no release has built since 2026-07-30.
Expand Down Expand Up @@ -458,7 +459,7 @@ jobs:
shell: bash
run: |
if [ ! -d _shared ]; then
echo "::error::off-grid-ai/shared was not checked out - @offgrid/sync cannot resolve. Check CI_CROSS_REPO_TOKEN."
echo "::error::off-grid-ai/shared was not checked out - shared packages cannot resolve. Check CI_CROSS_REPO_TOKEN."
exit 1
fi
rm -rf ../shared
Expand All @@ -468,6 +469,7 @@ jobs:
# drifted lock stops the release instead of resolving a graph nobody committed.
npm --prefix ../shared ci
npm --prefix ../shared/packages/sync run build
npm --prefix ../shared/packages/models run build
- name: Install dependencies
run: npm ci
- name: Fetch Windows native binaries (llama/whisper/sd/ffmpeg)
Expand Down
42 changes: 42 additions & 0 deletions docs/GAPS_BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ how to reproduce, and the fix direction. Close with evidence; never hide.

## OPEN

### SYN-004 (P1) - Late-pair full graph is not verified between the real Desktop and Mobile apps

**Evidence (2026-08-13):** the production send paths now backfill state records, generated images,
message attachments, and knowledge documents when a device pairs after the data exists. Shared tests
prove byte-bounded anti-entropy. Desktop tests use the real service, SQLite, and temporary files.
Mobile tests use two real sync engines and prove exact file bytes and durable controls. These tests do
not start both actual apps and verify final receiver materialization and UI in one journey.

**Evidence required to close:** create a project, settings, chat text, enhanced prompt, reasoning,
completed tool, generated image, attachment, and knowledge document on device A before device B is
paired. Pair the real apps through the production connect path. Verify every record, relationship,
and byte on B, restart B, and verify that the same graph returns without duplicate transfers or
detached files. Run Desktop-to-Mobile and Mobile-to-Desktop on physical macOS, iOS, and Android.

### DEF-001 (P1) - Replay capture control reports a state that is not factual

**Evidence (2026-08-08):** macOS Settings reported `Screen access: denied` and `Permission
Expand Down Expand Up @@ -1181,3 +1195,31 @@ Independently: `0 failed` next to a logged `request.failed` is its own bug - the
reaching Activity even for the file that DID have a row.

---

## MTP is a mobile feature, and it should be a `@offgrid/models` one

**Status:** open. Filed 2026-08-12. Deliberately NOT in the current PR - it is a package extraction,
not a fix, and folding it in would bloat a release branch that is about sync feedback.

**The gap.** MTP support exists on mobile and does not exist on desktop. Both apps consume the same
model layer, so a capability that lives in one app's screens is a rule with one home and two
audiences - the shape this repo keeps finding at the root of its bugs.

**Why it is a shared-package problem, not a desktop one.** Copying the mobile implementation across
would make it a rule with TWO homes, which is worse than having it once. The model layer is the owner:
whether a model needs MTP, which artefacts it implies, and how a device advertises support are all
properties of the MODEL, and every surface should be asking `@offgrid/models` rather than each app
deciding for itself. Desktop then gets it by consuming the package, and so does any future surface.

**Fix shape.**

1. Move the MTP capability rule into `shared/packages/models` - one definition of what MTP is and which
models require it, keyed off the catalog entry, with no app-specific branching inside it.
2. Mobile stops deciding and starts asking. Its current behaviour is the reference for what the rule
must produce, so it is the regression check that the extraction changed nothing.
3. Desktop consumes the same rule. No `if (platform)` in either app: a platform that cannot serve MTP
reports that through the same interface, it does not get a special case at the call site.

**Watch for:** the mobile implementation almost certainly carries assumptions that are really about the
phone runtime rather than about MTP. Those belong on the platform adapter, not in the shared rule - and
the tell is any code in the extracted package that names a platform.
2 changes: 1 addition & 1 deletion docs/WINDOWS_SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ at the bottom.
| -------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Windows CI build | 🟢 | `.github/workflows/windows-build.yml` (`windows-2022`) builds + packages a branch artifact; `release.yml`'s `build-win` job publishes the installer + updater feed to the release. Verified by installing a build on real Windows hardware. |
| Native-module compile (node-gyp) | 🟢 | Pinned toolchain: `windows-2022` (VS 2022) + Python 3.12. `windows-latest`/VS 2026 + Python 3.13 break node-gyp 11 — documented in the workflow. Covers `better-sqlite3-multiple-ciphers`, `node-llama-cpp`, `sharp`. |
| Windows runtime binaries fetch | 🟢 | `scripts/fetch-win-binaries.ps1` pulls win64 `llama-server` / `whisper-cli` / `sd-cli` / `ffmpeg` (+ DLLs) from upstream GitHub releases at build time. **`llama-server` is pinned to `b9838`** (byte-for-byte parity with the macOS engine); `whisper-cli` / `sd-cli` / `ffmpeg` resolve dynamically from their latest upstream releases. Repo LFS binaries are macOS-only and skipped (`lfs: false`). Fails loud if `llama-server.exe` is missing. |
| Windows runtime binaries fetch | 🟢 | `scripts/fetch-win-binaries.ps1` pulls win64 `llama-server` / `whisper-cli` / `sd-cli` / `ffmpeg` (+ DLLs) from upstream GitHub releases at build time. **`llama-server` is pinned by `package.json `offgrid.llamaRef``** — one owner, shared with the macOS source build, so the two are byte-for-byte parity; `whisper-cli` / `sd-cli` / `ffmpeg` resolve dynamically from their latest upstream releases. Repo LFS binaries are macOS-only and skipped (`lfs: false`). Fails loud if `llama-server.exe` is missing. |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
| NSIS installer | 🟢 | `electron-builder.yml` → `win.executableName`, `nsis` block (desktop shortcut, uninstall name). Untested end-to-end. |
| Code signing | 🟡 | Optional via `WIN_CSC_LINK` / `WIN_CSC_KEY_PASSWORD` secrets; **unset → unsigned build → SmartScreen will warn** on install. No cert configured yet. |
| Auto-update | 🟢 | `electron-updater` is cross-platform (`src/main/updater.ts`); `release.yml`'s `build-win` job publishes `latest.yml` (stable) / `beta.yml` (nightly) to the release, so Windows installs self-update like macOS. |
Expand Down
2 changes: 1 addition & 1 deletion e2e/devices-sync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
let syntheticPeer: SyncEngine | null = null
let syntheticState: StateSync | null = null
let syntheticFiles: FileTransferManager | null = null
let syntheticImportedEntitlement: unknown

Check failure on line 48 in e2e/devices-sync.spec.ts

View workflow job for this annotation

GitHub Actions / ci

'syntheticImportedEntitlement' is assigned a value but never used
let syntheticEntitlementCommitted = false

Check failure on line 49 in e2e/devices-sync.spec.ts

View workflow job for this annotation

GitHub Actions / ci

'syntheticEntitlementCommitted' is assigned a value but never used
let syntheticClipboard: ClipboardSyncCoordinator | null = null
const syntheticClipboardRecords = new Map<string, ClipboardHistoryRecord>()
/** The peer's pending clipboard deliveries, keyed the way the coordinator keys them: record × device. */
Expand Down Expand Up @@ -518,7 +518,7 @@
await page.getByRole('button', { name: 'Rescan network' }).click()
await expect(page.getByRole('button', { name: 'Rescan network' })).toBeEnabled()
await expect(page.getByText('Synthetic Android').first()).toBeVisible()
await expect(page.getByText('Scanning LAN and Nearby routes.')).toHaveCount(0)
await expect(page.getByText('Scanning WiFi and Nearby routes.')).toHaveCount(0)
await expect(page.getByRole('alert')).toHaveCount(0)

const timestamp = new Date().toISOString()
Expand Down
Loading
Loading