Commit Graph
2820 Commits
Author SHA1 Message Date
Codeman maintainer f07cbccb0b Merge #804: a library of models on this Mac, with sizes, delete, add and switch
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:15:34 +01:00
Codeman maintainer c9f84c443d Merge #803: list every model that fits, and add Muse Glimmer 30B and Ornith 1.0 9B
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:15:30 +01:00
Codeman maintainer 3eb0689f6d fix(omp): an omp alert stays up until omp says the dialog closed (#798)
The four edits promised in the review of #798:

- An omp alert could clear while omp's dialog was still up. omp items got
  the same pane `capture` as Claude's, which the inbox re-reads with
  Claude's numbered-dialog parser. omp's dialog has no numbered rows, but an
  agent's plan above it does, so the list was parsed as the item's options,
  and once it scrolled out of the frame the item was resolved as answered in
  the terminal. An approval-only item now takes the frame once as `context`
  and gets no `capture`, like a screen-read item (web/screen-blocked.ts), so
  the module's `elicitation_complete` is the only thing that clears it. A
  route test puts a numbered list above an omp-shaped dialog and fails on
  the old code at both steps (options parsed, alert cleared).
- The reboot-restore install is dropped: the restore plan skips every CLI
  whose transcript is not `claude-jsonl` (omp's is `omp-jsonl`), so it never
  ran, and it sat after the start, when omp has already scanned its hooks.
- The docs that say which CLIs raise `blocked` and open alert-only items now
  name omp: api-reference, CLAUDE.md, architecture-invariants, an entry for
  `approvalHookFile` in cli-registry, and the skill with its plugin mirror
  (verbs.md listed omp among the modes where `blocked` is a 400, one line
  above the paragraph that says it works).
- Two comments: the schema's file-name rule is a pattern, not a closed list,
  and the marker check takes the marker anywhere in the file.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 10:27:38 +01:00
Codeman maintainer 13f1b986af Merge #798: omp tool approvals raise an alert in Codeman 2026-11-11 10:23:02 +01:00
Codeman maintainer fbcf8eba72 test(paste): pin the main pane's keydown hand-off, and say which engines still get two pastes (#799)
The two edits promised in the review of #799:

- The main pane's Ctrl+V branch hands the keydown to the paste trap, and
  nothing tested that call: with it reverted to `_handleImagePaste()` every
  test still passed and Firefox would paste twice again. A source pin now
  fails on that, like the Ctrl+C one in terminal-copy-clean.test.ts.
- architecture-invariants and src/web/CLAUDE.md described the key's own paste
  as reaching the trap in every browser. Since #799 that is Chromium and
  WebKit only: in Firefox the trap cancels the keydown.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 10:23:02 +01:00
Codeman maintainer 1d7288dc4f Merge #799: cancel the Ctrl+V keydown once the paste trap has the clipboard (Firefox double paste) 2026-11-11 10:21:44 +01:00
Codeman maintainer 8bedbbe793 chore: version packages 2026-11-10 23:59:42 +01:00
Codeman maintainer 7bdef0d86a fix(local-model): a switch or a new model from a stopped model undoes the Stop
A job started from a model the person had stopped runs the runtime, and
the model it ends on is in use and running. The stored "stopped" flag
stayed set, so that model would not have started with the server again.
Both verbs now set it back once their job started, the way Start does.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:54:43 +01:00
Codeman maintainer 2f17b8067a docs(local-model): the models on this Mac, add, switch and delete
The guide, the API reference, the invariants and the two CLAUDE files
describe the library with one model in use: the state file's list, the
three verbs and their refusals, the two cached listings of the view, and
the window's list with its row actions.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:52:14 +01:00
Codeman maintainer 70dc42b248 feat(local-model): a library of models, with one in use
The local model window could hold one model. A second install was refused
(409 already-installed), and a model a Remove kept could never be freed
from the window again. The store can now hold several models, and exactly
one of them is the model in use: the one the endpoint record lists, the
runtime is started for and a session may be launched on.

Server
- LocalModelStateFile.library lists every model that passed the check
  (id, label, context, digest, size, coding agent). The service writes
  the entry when verify passes; the reducer carries it and remove drops
  it. An installed file of an earlier build counts its one model.
- POST /api/local-model/install is allowed from installed: it adds a
  model, and the earlier one stays. A replaced job no longer deletes a
  model of the library, only an unchecked or partial download.
- POST /api/local-model/use switches to a checked model: a job of the
  two steps verify and connect. The stored digest is compared before
  anything is loaded (model-changed otherwise). Nothing is downloaded.
- DELETE /api/local-model/models deletes one model by its name, through
  the runtime's own delete, never by a path, never the model in use.
- Adding and switching answer 409 sessions-open while a live session
  uses the endpoint. Until such a job ends installed, a session start is
  refused with the new code job-unfinished.
- The view carries library (the state's entries joined with what the
  running runtime lists: ready, changed, missing, unverified) and store
  (the size of the blob folder). Both are cached and refreshed in the
  background, never read on the one-second poll.
- With nothing installed, remove is offered while the store still holds
  models an earlier Remove kept.

Window
- The installed screen lists the models on this Mac with the context
  each was checked with, its state and its size on disk, with Use and
  Delete per row (Delete asks once, in the row) and Add a model.
- In the chooser a model that is on this Mac already says so and is
  switched to without a download or a confirm screen.
- A paused or failed job that has a checked model beside it shows the
  way back to that model.

Every route is admin only in multi-user mode, like the existing ones.

Not run on a Mac: the tests use fakes and the fake runtime, and the
listing, store size and delete were checked against a real Ollama 0.13.0
on Linux, not the pinned 0.40.15 build for macOS.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:52:14 +01:00
Codeman maintainer d25cc08606 feat(local-model): Ornith 1.0 9B, the first model a 16 GB Mac can hold
A 16 GB Mac got no model: the two 9B builds of the catalogue need 10.2 GB at
65,536 tokens, 0.987 of the estimated budget, just past the 0.98 a tight fit
may reach.

New row `ornith:9b-q4_k_m` (Ornith 1.0 9B, MIT), a fine-tune of Qwen3.5-9B for
coding agents whose GGUF build has no vision projector: 5.63 GB against 6.55 GB,
so 9.3 GB at 65,536 tokens, 0.898 of that budget. SWE-bench Verified 69.4,
SWE-bench Pro 42.9, Terminal-Bench 2.1 43.1 on Ornith's card. Its config.json
and its GGUF header are those of Qwen3.5-9B (no prediction layer), so it shares
the figures of the two other 9B rows. Never run on a Mac: `estimated`.

- What changes for a Mac: 16 GB goes from no model to `limited` with this row,
  tight. That is the only pick that changes: the row is ranked under MiMo V2.6
  9B, so every Mac that could already run a 9B keeps its pick and gains an
  alternative. With the budget a 16 GB Mac mini measured (10.7 GB) the pick is
  MiMo as before.
- Why under MiMo although its own card's numbers are higher: the two vendors'
  tables put their common base 7 points apart, and runtime issue 16992 (open)
  was filed on the 35B sibling, which has the same renderer and parser. The
  35B stays set aside.
- The 16 GB rule itself is untouched: same budget estimate, same 0.98, same
  eight context checkpoints.
- Ranks renumbered 1 to 10; the pins of test/local-model-recommend.test.ts and
  the table of docs/local-models.md follow.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:36:45 +01:00
Codeman maintainer 9608a17d99 feat(local-model): list every model that fits, and add Muse Glimmer 30B
The "Run a local model" chooser showed the pick and four more models. It now
lists every catalogue model that fits the Mac, and the install accepts every
listed row.

- MAX_ALTERNATIVES goes from 4 to 11, above what the catalogue holds. It stays
  a bound: test/local-model-catalog.test.ts fails for a catalogue that outgrows
  it, so nothing drops off the list unseen.
- New row `muse-glimmer:30b-q4_k_m` (Meta, Apache-2.0): SWE-bench Verified 76.0,
  SWE-bench Pro 51.2, Terminal-Bench 2.1 51.7 on Meta's card. Its K/V cache is
  the smallest of the 27B class, so it is the first model of that class a 32 GB
  Mac holds: 21.2 GB at 65,536 tokens, 0.970 of the estimated budget. Figures
  computed from its config.json and its GGUF header by the llamacpp rule (13
  full-attention layers, 39 sliding-window layers of 2,048 tokens, no
  prediction layer); the sliding-window cache rule was read again in llama.cpp
  b12111 (src/llama-kv-cache-iswa.cpp). Never run on a Mac: `estimated`.
- What changes for a Mac: 32 GB is no longer `limited`. On an M1 Max or M2 Max
  the pick is Muse Glimmer; on a base or Pro chip the dense model is slow and
  the pick stays MiMo V2.6 9B, now with the small-pick note. Every other pick
  is unchanged.
- The "set aside" list was read again at its sources on 2026-11-10 and gained
  ripple-preview:65b (every build requires runtime 0.40.16, one above the pin),
  Ornith 1.0 (runtime issue 16992), nemotron-3.5-super:120b, qwen3.5:4b,
  qwen3-coder-next and the MLX and DFlash tags of Muse Glimmer.
- The header and the guide now say that runtime issue 16992 has one diagnosed
  cause in the tool parser every Qwen-family row shares (a tool argument that
  quotes a whole tool call is cut off; the fix is runtime PR 17021, open).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:33:59 +01:00
timkjrandClaude Opus 5.5 27d6e2e30a fix(omp): report approvals without claiming Claude's hook set
Review follow-up. omp declared `hooks: 'always'`, which every consumer reads
as Claude's full hook set, so an omp session picked up behaviour it cannot
back. omp goes back to `hooks: 'none'`; `capabilities.approvalHookFile` is now
a separate, narrower capability with its own predicate,
`approvalHookAvailableForMode()` (local, non-adopted sessions only).

- An explicit `stop` wait on omp is a 400 again; `blocked` still works.
- omp inbox items are alert-only (`answerInTerminal`, no `approvalId` on the
  push), and only `permission_prompt` / `elicitation_complete` are accepted for
  an approval-only session.
- The module is installed from POST /api/quick-start too (new and existing
  cases, not Docker or remote) and on reboot-restore, after the launch
  pre-flight on both create paths. Claude's settings.local.json block and the
  Docker CLAUDE.md no longer land in omp workspaces.
- The generated module passes Bun's `tls: { rejectUnauthorized: false }` for an
  https CODEMAN_API_URL (checked against a `--https` server), clears the alert
  only when the last pending toolCallId resolves, sends reports in order,
  carries omp's policy `reason` as `message`, and imports HookAPI from
  `@oh-my-pi/pi-coding-agent/extensibility/hooks`.
- Every path component and the target file are lstat-checked.
- docs/omp-integration.md, verbs.md and the stock.ts comments updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 10:03:38 -06:00
Michael GrundbergandClaude Opus 5.5 6c5d53ebfe fix(paste): cancel the Ctrl+V keydown once the paste trap has the clipboard
Ctrl+V in Firefox still pasted the clipboard twice some of the time.
Right-click → Paste never did.

`_handleImagePaste()` focuses a hidden contenteditable trap, calls
`document.execCommand('paste')`, and reads the clipboard from the paste
event that lands on the trap. Firefox dispatches that event synchronously,
inside the `execCommand` call. It then delivers the key's own paste as a
separate task, about 10 ms later in Playwright's Firefox. The one-shot flag
from b87bc687 drops that second event, but only if it still reaches the
trap. The trap removes itself and refocuses the terminal on a
`setTimeout(0)` queued by the first event, and that timer races the second
paste. When the timer wins, the second paste lands on xterm's textarea,
xterm's own `handlePasteEvent` sends the clipboard again, and the flag
never sees it.

The trap now cancels the keydown when its paste listener has already run
by the time `execCommand` returns, so Firefox never issues the second
paste. Both callers pass the keydown as `target.keyEvent`: the main pane's
key handler in terminal-ui.js and a tile's in terminal-tile.js. Chromium
and WebKit refuse the command and dispatch nothing from it, so their
keydown is left alone, because its default action delivers their only
paste event.

Measured in Playwright's Firefox with the trap removed before the key's
paste arrived: the paste reached xterm's textarea and the text went in
twice. With the keydown cancelled, the key's paste never fired. End to end
on an isolated instance with a shell session, 20 Ctrl+V presses gave 20
pastes in both Firefox and Chromium.

test/image-paste-trap.test.ts gains a harness option in which
`execCommand` delivers the paste from inside the call, as Firefox does.
New cases cover the keydown being cancelled for the main pane and for a
tile, being left alone when `execCommand` delivers nothing, and a caller
that passes no keydown. The two cancel cases fail without this change.

test/terminal-tile-input.test.ts now expects the tile to pass its keydown
along with its terminal and session.

Docs: the Terminal paste section of docs/architecture-invariants.md and
the paste-trap entry in src/web/CLAUDE.md now describe the race and the
rule. The section's old claim that the key's default action alone always
reaches the trap is replaced, since in Firefox it can miss it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 08:20:41 +01:00
timkjr 0bbf483a35 fix(omp): report tool approvals to Codeman so an omp pane alerts
An omp pane asking for tool approval raised no tab alert, no desktop
notification and no Approvals Inbox item. omp inherited hooks:'none' from
agentDefaults(), so hooksAvailableForMode() answered false and
hook-event-routes never opened an item; there was nothing to resolve either.

omp is one of the few non-claude CLIs with real approval events
(tool_approval_requested / tool_approval_resolved), so Codeman can install a
hook module into <workspace>/.claude/hooks/pre/ - the directory omp scans -
on session create. It forwards them as permission_prompt and
elicitation_complete, the names the server already routes on, so the existing
alert, notification and inbox pipeline works unchanged.

The file name comes from the new capabilities.approvalHookFile field rather
than a branch on the CLI id (cli-registry-no-id-branching). stop/idle_prompt
are deliberately NOT reported: omp's idle stays screen-read via workDetect,
which the agent skill's verbs.md now says explicitly.
2026-11-09 18:51:42 -06:00
Codeman maintainer 0e13d9b12f fix(voice): edits from the #705 review
- docs/api-reference.md and the voice-stream.ts overview now say a
  transcript frame is the current utterance so far and a final frame
  ends it, as the relay's own comment and the invariants doc do.
- VoiceInput.cleanup() hides the Send button before stop(true), so the
  button stays up for the words stop(true) salvaged and inserted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 18:19:05 +01:00
Codeman maintainer 7f35f798ee Merge #705: fix(voice): preserve thinking pauses and Android dictation 2026-11-09 18:16:12 +01:00
Ludo FourrageandClaude Opus 5.5 308de1fd4d fix(voice): 30 s and Manual outlast silent Web Speech runs
_onWebSpeechEnd counted every run that ended without a result toward the
two-retry cap, however long the run lasted. Under 30 s or Manual, each
no-speech window of the engine is one silent run, so the third ended the
recording at about 18 s of silence, before the deadline, and Manual
stopped on its own. Both recognition.start() sites now stamp the run start;
an empty run counts toward the cap only when it ended within a second of
starting (a recognizer that cannot listen), and any other run resets it.
The 250 ms backoff is unchanged.

The vm harness in test/voice-silence-timeout.test.ts now passes the test's
Date into the context, whose own Date ignores fake timers.

Also from review: the VoiceInput stubs of the four settings-ui.js tests
carry SILENCE_TIMEOUT_SECONDS; Settings-Reference lists Stop after silence;
the touchscreen Linux fallback leaves out ChromeOS and says a touch Linux
laptop still matches; the dead _webSpeechStream teardown is gone; the two
voice tests use vitest expect instead of node:assert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 07:57:14 -08:00
Ludo Fourrage 2d7cd46565 fix(voice): preserve Android transcripts and Claude utterances 2026-11-09 07:27:02 -08:00
Ludo Fourrage 0cedeb2657 fix(voice): make dictation silence timeout configurable 2026-11-09 07:26:44 -08:00
Codeman maintainer 4f29b0187a Merge #732: feat(settings): sync case options and selection across devices 2026-11-09 08:01:43 +01:00
687c75b490 Merge #720: fix(log-viewer): stop reconnecting after a startup error
The floating log viewer reconnected every few seconds when its tail-file
stream ended with a startup error (path refused, file missing, too large,
stream limit, tail spawn failure). It now closes the EventSource on an
`error` message that arrives before `connected`; an established stream
keeps its reconnect.

Squashed from #720 (johoja12). Maintainer edits while landing, as the
review said: the onerror close is dropped (it stopped the browser's own
retry after a transport failure, leaving the window on "Connecting"),
the test's check for a button nothing renders is gone, and the stubbed
stream sets `retry: 200` so a reconnect, if one came, shows within the
shorter wait.

Co-authored-by: Codeman maintainer <kay@stutz.science>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 07:58:33 +01:00
Codeman maintainer 8dc81d0089 Merge #792: feat(files): delete a file from the Files panel (from #529) 2026-11-09 07:57:02 +01:00
Codeman maintainer 5e80c0a1ca Merge #791: feat(path-picker): pinch, pan and wheel zoom for the image preview (from #608) 2026-11-09 07:57:01 +01:00
Codeman maintainer 20cd2eccc9 Merge #790: fix(home): swap the home surface on a fold whatever width the page loaded at 2026-11-09 07:57:00 +01:00
Devvyn 607c88afd9 fix(settings): a tab-return read never undoes a pick or save made while it was out
- session-ui.js: drop the answer when the case-sync chain moved during the read; settings-ui.js calls _applyServerCaseSettings with ?.
- i18n.js: zh-CN for the failed case-options save toast, case name passed through
- tests: regression for the stale read, zh-CN toast row
2026-11-09 14:01:09 +08:00
Devvyn bb088bcd3e fix(settings): case sync tests follow lastPickedCase, a failed pick is not undone, rollback is covered
- sync-case-state browser and stale-refresh tests use lastPickedCase (the stale test now fails without its guard)
- saveLastUsedCase runs on the case-sync chain, and the follow is skipped while this device's own pick was not stored
- _caseStateSyncOn tolerates a harness without loadAppSettingsFromStorage
- lastPickedCase added to the config-backup volatile-key test and docs; rename rollback tests cover caseSettings and lastPickedCase
- wiki row moved to System
2026-11-09 12:31:30 +08:00
Devvyn ea27adf104 fix(settings): case sync follows only a picked case and keeps its popover and undo honest
- follow lastPickedCase (written by saveLastUsedCase while sync is on), not lastUsedCase, so a quick-start or an agent's spawned worker no longer moves other devices
- close the open case-options popover when the follow or an import changes what it shows; follow through selectQuickStartCase(save:false)
- failed save reuses _noteSettingsWriteResult and says the choice lasts until the next sync
- rename undo renames the shared entry back instead of restoring the whole map; lastPickedCase follows a rename
- caseSettings and lastPickedCase added to the redaction, rename and backup-volatile lists and the docs
2026-11-09 11:17:16 +08:00
Codeman maintainer 078bd74bd1 fix(files): the review's fixes for the Files panel delete
The fixes #529's review asked for, each with a test.

- No inline handler. The button quoted the path inside
  onclick="...decodeURIComponent('<path>')", and encodeURIComponent leaves
  ' alone, so a file named x'-alert(1)-'.txt ran script in Codeman's
  origin and it's.txt was a syntax error. It is now bound in the existing
  .file-tree-item loops from the row's escaped data-path.
- Symbolic links are refused, never followed. validateSessionFilePath()
  returns the realpath, so deleting AGENTS.md -> CLAUDE.md deleted
  CLAUDE.md and left the link dangling. The path as named is lstat'ed,
  a link answers 400, and the inode is compared again before the unlink.
- The edit route's confinement whole: validateLocalSessionFilePath()
  behind withGuardedOutsideWorktree(), so an absolute path in a followed
  outside worktree obeys the same rules, and the sensitive, blocked and
  .git checks run on both the realpath and the path as named.
- Adopted sessions are refused like remote ones (the /tmp exception of
  the older file surfaces is not copied to a new one); Docker workspaces
  are allowed, as on the edit route. The workspace is probed bounded
  first (#516). Errors are thrown with their status.
- Deleting from search results re-runs the search, the row buttons stay
  visible on touch screens, the confirm and toasts have zh-CN text, and
  no button is drawn for remote or adopted sessions.
- Docs: CLAUDE.md, architecture-invariants, api-reference and the
  Working With Files wiki page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 03:09:46 +01:00
shenlvkang-collab dfe1af2e5f feat(files): delete a file from the Files panel
A delete button on each file row of the Files panel (tree and search
results) and a DELETE /api/sessions/:id/files?path= route that removes one
regular file from the session workspace, refused for remote (SSH)
sessions and behind the same sensitive-path, attachment-guard and .git
checks as the edit route.

Salvaged from #529.
2026-11-09 03:09:46 +01:00
Codeman maintainer 572ff202bb fix(path-picker): finish the image preview zoom from #608
The review items offered on #608, applied on top of the contributor's
zoom:

- `object-fit: scale-down` instead of `contain`, so a small image keeps
  its own size instead of being stretched to fill the preview. The pan
  clamp now uses the size the image is actually drawn at (natural size
  fitted with scale-down) rather than the image box, which let a small
  or letterboxed image be panned entirely out of view.
- Wheel zoom is proportional to deltaY (d3-zoom's scale: about 15% per
  100px notch, lines and ctrl+wheel trackpad pinches scaled up), capped
  at sqrt(2) per event, and a wheel event with no vertical delta zooms
  nothing. A trackpad scroll no longer jumps to 600%.
- The zoom controls show only once the image has loaded; on an error
  they are hidden and the gesture listeners are dropped with the image.
  Both handlers now check the request sequence like the text branch, so
  a late load or error from a replaced preview no longer resets the
  current preview's zoom or paints its error over the current image.
- test/path-picker-image-zoom.test.ts drives the real listeners in
  jsdom (focal point under the cursor and the pinch centre, pan clamp,
  wheel scaling, error, close and stale events) in place of the
  source-string checks in test/path-picker-ui.test.ts. 7 of its 10
  tests fail against the contributor's commit alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 03:09:45 +01:00
shenlvkang-collab ba6bff388a feat(path-picker): pinch, pan and wheel zoom for the image preview
The path picker's image preview gets zoom controls (out, fit/percentage,
in), a two-finger pinch that keeps the pinch centre in place, one-finger
pan while zoomed (clamped to the image bounds), double-click to toggle
2x, and wheel zoom on desktop. closePreview() removes every listener and
drops the zoom state.

Salvaged from #608.
2026-11-09 03:09:45 +01:00
Devvyn f8ff431d30 fix(settings): case sync asks and merges only while it is on
- session-ui.js: no GET /api/settings on a tab return unless this device's cached syncCaseState is true
- system-routes.ts: caseSettings is merged only while syncCaseState is true (as stored or set by the same PUT)
- sync-case-state tests cover both; wiki notes the stored options stay when sync is turned off
2026-11-09 10:03:08 +08:00
Codeman maintainer f62c1f7e2f fix(local-model): a view read while the job ends no longer shows paused
The install window polls GET /api/local-model every second. A read of the
state file that was still out while the job wrote its last state and let
go of this.job came back with the old `installing`, and settling that with
no job in the process read it as an interrupted job: `paused` with Resume
for one tick before `failed`. Under CI load this made
test/local-model-install-e2e.test.ts fail on a different failure case per
run (master 86502b69, PR #790).

currentState() now reads again when the job ended during the read. The job
writes before it lets go, so the second read has the final state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 02:51:07 +01:00
Codeman maintainer 7602b6db21 fix(home): swap the home surface on a fold whatever width the page loaded at
Folding a phone on the home screen left the welcome screen up when the page
had loaded wide (#702): the phone overview's breakpoint listener was wired
from its first show, which a wide-loaded page never reaches. Both home
screens' breakpoint listeners are now installed once from init() and act
only while a home surface is up.

A rail render queued just before the fold could also unhide the desktop
rail behind the phone overview; the debounced refresh now asks again when
it fires.

Tests: test/home-breakpoint-listeners.test.ts (gate) and
test/home-breakpoint-swap.browser.test.ts (browser suite: wide-first and
narrow-first across 599/600 and 767/768, plus a height-only resize).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 02:29:51 +01:00
Devvyn SatieandClaude Sonnet 5.5 a50d0db283 fix(settings): harden case sync (privacy, concurrent saves, failures, enable/disable)
- Redact caseSettings for non-admins and never apply a shared map on a read-only page (CASE-SYNC-001)
- Send one case per save and merge it inside mutateSettingsFile, so devices cannot discard each other's cases; rapid saves keep their order (CASE-SYNC-002)
- Check HTTP status and the response body, keep the local choice, report an unsynced save and reuse the forbidden-write handling (CASE-SYNC-003)
- Let the server decide whether sync is on: refresh the cached switch, discard stale refreshes (CASE-SYNC-004)
- Carry shared options through case rename and drop them on delete or unlink
- Give the switch an accessible name; add server, client and two-browser tests

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 08:43:46 +08:00
Devvyn d62b11586b feat(settings): optional sync of case options and selected case across devices 2026-11-09 08:25:38 +08:00
Codeman maintainer 236fecf6b9 chore: version packages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 11:11:06 +01:00
Codeman maintainer 8a54ded78b Merge #784: a local model catalogue for every Mac memory size, and its weekly check 2026-11-08 11:10:13 +01:00
Codeman maintainer 8fa2884f48 Merge #787: turn the Codeman agent skill on by default 2026-11-08 10:41:11 +01:00
Codeman maintainer 83fb523979 fix(local-model): rollback copies follow each tag's own draft length
The previous commit counted 4 rollback copies of the recurrent state for every GGUF with a
prediction layer, the runtime's default `draft_num_predict`. A tag can set its own in its
params layer, and the runtime then passes that as `--spec-draft-n-max`, which is what
`need_n_rs_seq()` turns into rollback copies. Read on 2026-11-08:

- qwen3.6:35b-a3b-mtp-q4_k_m: draft_num_predict 2, so 2 copies (was counted as 4).
- qwen3.8:27b-mtp-q4_k_m: draft_num_predict 4.
- qwen3.8:27b-q8_0: none in its params, so the runtime's 4.

The 35B-A3B GGUF row's overhead drops by 132 MB: 0.971 instead of 0.976 on a 36 GB M3 Pro or
M3 Max, the same pick. test/local-model-catalog.test.ts now carries the drafted tokens per row
instead of a yes/no prediction layer.

Also states what was verified about llama.cpp's checkpoint default: 8 from b6800 to b8200,
32 from b8250 together with a checkpoint every 8,192 prompt tokens (common/common.h at those
tags), in place of an inferred reason. Checked in the b12111 source as well: the draft context
of a Qwen-family MTP model caches the prediction block alone (`mtp_on_hybrid_qwen`, layers at
or above n_layer()), and the main context leaves it out, so the row counts that layer once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 10:38:12 +01:00
Codeman maintainer 2cd3cbd6a0 fix(local-model): count what llama-server keeps beside the K/V cache, and bound its checkpoints
The llama.cpp memory rule of the catalogue left out two allocations that the llama.cpp the
runtime bundles (b12111, the runtime's LLAMA_CPP_VERSION) makes for every GGUF row:

- Context checkpoints. For a model whose state cannot be rolled back (the recurrent state of
  the Qwen-family rows, the sliding window of North Mini Code), llama-server keeps host copies
  of that state, two or three per request, up to 32 per slot by default
  (tools/server/server-context.cpp create_checkpoint). For the 27B that is 32 x 157 MB = 5 GB,
  none of which was counted. The runtime now sets LLAMA_ARG_CTX_CHECKPOINTS=8 (read by
  llama-server from its environment like LLAMA_ARG_CACHE_RAM; 8 was llama.cpp's own default
  before it went to 32), and every llamacpp row counts 8 copies of its state.
- Multi-token prediction. The two -mtp tags and qwen3.8:27b-q8_0 carry a prediction layer
  (nextn_predict_layers 1, read from the first bytes of each GGUF; the 9B tags and North Mini
  Code have none). The runtime then always passes --spec-type draft-mtp --spec-draft-n-max 4,
  so llama.cpp keeps 4 rollback copies of the recurrent state (need_n_rs_seq(),
  llama-memory-recurrent.cpp) and the prediction layer's cache in the draft context's cache
  type, f16 by default. The rows counted one state and that layer at q8_0.

What moves (test/local-model-recommend.test.ts, "every memory size", re-pinned):

- 16 GB: limited -> none. MiMo 9B at 65,536 tokens needs 10.2 GB, 0.987 of the 10.4 GB
  estimate. With the budget the Mac mini measured (10.7 GB after the reserve) it is offered,
  0.954, tight; that test is kept. With 4 checkpoints instead of 8 it would be 0.966 on the
  estimate: this size sits on the edge, and only a run on such a Mac settles it.
- 18 GB: the same pick, 0.964 instead of 0.928.
- 36 GB: the same picks, 0.966 (27B on M4/M5 Max) and 0.976 (35B-A3B GGUF on M3).
- 48 GB on a Max chip: the 27B at 262,144 tokens is tight (0.853) instead of good.
- 32 GB with a measured budget of 0.74: North Mini Code at 131,072 tokens (was 262,144), the
  27B at 65,536 on a Max chip (was 131,072).
- Every MLX row, and everything from 64 GB up, is unchanged.

test/local-model-catalog.test.ts recomputes the new rule from the architecture table and holds
the catalogue to LLAMA_ARG_CTX_CHECKPOINTS. The catalogue header, runtime.ts, the types,
docs/local-models.md, CLAUDE.md and architecture-invariants say the same. Read in the source,
not measured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 10:26:57 +01:00
Codeman maintainer 235db69c1e fix(agent-skill): the migration's log line says it switched the setting on
It stores true rather than clearing the key, so the line now says what happened.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 10:11:36 +01:00
Codeman maintainer cda4184e42 docs(agent-skill): say what the migration does after a refused first start
The migration's file comment and the invariants said a restored settings.json
stays as restored and that a refused start leaves the migration to the next
start. Both hold, but the first only once the migration has run: after a
first 1.50.0 start that found settings.json unparseable, no marker exists,
so an off saved between the fix and the next start is switched on with the
rest. That is the same "cannot tell a chosen off from the old default" trade
the release note already states, in a narrower window. Both texts now say
so. Comment and docs only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 10:09:34 +01:00
Codeman maintainer f1cfe61950 docs(agent-skill): the settings schema comment still said default off
The JSDoc on `agentSkillEnabled` in SettingsUpdateSchema said the skill is
injected into `<case>/.claude/skills/` on Claude session create and is
"SYNCED, default OFF ... so it is opt-in". Since this branch it is on when
absent, goes into whichever directory the session's CLI scans for skills,
and a false stored by 1.49.0 or earlier is switched on once at the first
1.50.0 start. Comment only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 10:09:30 +01:00
Codeman maintainer bc9c3c1639 docs(local-model): two catalogue header claims made exact
The Q8 27B row counts a prediction layer its GGUF may or may not carry
(not checked, careful side), and the macOS notice on the runtime's page is
about Laguna XS 2.1; Laguna S 2.1 is set aside for its open runtime issues.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 09:39:48 +01:00
Codeman maintainer 97f10a1033 docs(local-model): the long-session caveat of the tested pair, and what the check proves
The 48 GB row now says the test was a short task and that MLX may keep up
to 8 GiB more in a long session, which brings the pair to the edge of the
budget. The check after the download says what it proves for each runner,
from the runtime's source: for a GGUF build both of its figures are the
buffers llama-server reports allocating, so the check covers the whole
load whatever the cache type; for an MLX build only the weights. And the
docs say where a new row's architecture goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 09:39:27 +01:00
Codeman maintainer edfc2ed667 docs(agent-skill): say what the setting UI test pins, not that it proves the deleted-key shape
The migration's file comment said test/agent-skill-setting-ui.test.ts proves
both shapes. It pins the shipped one (a stored true wins on a device holding the
old false); the deleted-key shape was measured with the same harness and is not
kept as a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 09:38:15 +01:00
Codeman maintainer 989fcd778a feat(local-model): a catalogue for every Mac from 8 to 512 GB, each row with its own figures
The catalogue had seven Qwen-family rows that all carried the memory
figures measured on one model (Qwen3.6 35B-A3B on the MLX runner), so a
9B or a dense 27B was judged with figures that do not describe it, two
rows had the wrong Mac download size, and every Mac below 32 GB got
"none".

Now eight rows, each with the runner a Mac really uses for its tag (a new
`runner` field: `mlx` or `llamacpp`) and memory figures of its own:

- the 35B-A3B MLX row keeps its measured figures and the tested pair;
- every llama.cpp row is computed from its model's config.json: a q8_0
  K/V cache for the whole context (what the runtime now sets), its
  recurrent or sliding-window state, 1.0 GB of runner and the 1 GiB host
  prompt cache;
- the one other MLX row (Ornith 1.5 122B) is computed the same way,
  scaled by what the measured row showed, plus the runner's 8 GiB of
  prefix snapshots.

Sizes are the registry's Mac pull totals (2026-11-08, lowercase ids
verified there). What each memory size gets: 8 GB none; 16 to 32 GB a 9B
(limited); 36 GB Qwen3.8 27B or the 35B-A3B as GGUF; 48 GB the tested
pair (or the 27B on a Max chip); 64 and 96 GB the 27B or the 35B-A3B;
128 to 512 GB Ornith 1.5 122B. Nothing but the one row was measured.

The catalogue test recomputes every computed figure from a table of
architectures and lets two rows share figures only when they share the
architecture and the runner. The rule tests of recommend() now run on a
frozen copy of the 2026-11-06 catalogue, so a weekly catalogue update
changes only the per-memory table and the real-Mac tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 09:33:48 +01:00
Codeman maintainer f60b63c005 feat(local-model): q8_0 K/V cache, flash attention and a bounded prompt cache for llama.cpp
runtimeEnv() now also sets OLLAMA_KV_CACHE_TYPE=q8_0, OLLAMA_FLASH_ATTENTION=1
and LLAMA_ARG_CACHE_RAM=1024. Read in the v0.40.15 source of the pinned
runtime: the first becomes --cache-type-k/-v of llama-server (17/32 of the
f16 cache), the second --flash-attn on (a quantized V cache needs it, and
"auto" or "off" would fail such a load), and llama-server inherits the third
from ollama serve's environment, which bounds its host prompt cache to
1 GiB instead of letting it grow by gigabytes in a session. The MLX runner
reads none of them.

The catalogue's llama.cpp rows are computed for these values, so the two
change together. Not yet measured on a Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 09:18:51 +01:00