Commit Graph
2148 Commits
Author SHA1 Message Date
Codeman maintainer d4928baa36 Merge the Mac-run fixes of #804 and #806: a switch ends on the list of models, and a session without a name is named in the refusal
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 02:20:09 +01:00
Codeman maintainer b724f2d6ea Merge branch 'feat/local-model-library' into feat/local-model-harness-choice
# Conflicts:
#	src/web/CLAUDE.md
2026-11-12 00:11:10 +01:00
Codeman maintainer 9c2ba35f38 fix(local-model): a switch ends on the list of models, not on the one-button screen
Seen on a MacBook (M5 Pro, 2026-11-12): pressing Use on a model in "Models on this Mac"
ran the switch and then showed the screen that ends an install, "X is ready on this Mac"
with Start a session alone. The list was gone, so deleting the model just switched away
from meant closing the window and opening it again.

That screen is right after a download: the person installed a model and wants a session.
A switch is pressed IN the list by someone who is tidying it. A switch is a job without a
`model` step, so the window now tells the two apart from the job it just watched and ends a
switch on the whole installed screen: the list with "In use" on the other row, Add a model,
Stop and Remove.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 00:10:55 +01:00
Codeman maintainer db81d55fc9 fix(local-model): name a session without a name in the "still using this model" refusal
Seen on a MacBook (M5 Pro, 2026-11-12): with a worker of the codeman skill open on the
local model, Stop, Remove and a switch of models answered

    Sessions are still using this model: . Close them first.

A session an agent starts has no name of its own (a quick-start without sessionName), and
the refusal listed session.name. The tab of such a session shows the last folder of its
working directory, else the first eight characters of its id, so the sentence now uses the
same label (sessionLabel() beside sessionNamesUsingEndpoint()).

The sentence has shipped since 1.50.0 for Stop and Remove. This branch adds it to Use and
to the install of another model, which is where a person with local workers open meets it.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 00:08:59 +01:00
Codeman maintainer d192eb6825 test(mobile): the gate checks the File Viewer button's phone default again (#809)
Until #809 a display:none rule in mobile.css and its KNOWN_PHONE_HIDDEN
entry kept the File Viewer button off phones, and the gate checked both.
With that rule gone, the handheld default in getDefaultSettings() and the
html.mobile-init startup rule are the whole lock, and only the browser
test saw them, which npm test does not run.

The policy test now loads the real getDefaultSettings() on a handheld and
on a desktop, checks that every reader of the setting goes through the
device default, and that mobile.css keeps the startup hide.

Docs: architecture-invariants, src/web/CLAUDE.md and the wiki say the
button is off on a phone until it is switched on there, and that a window
narrower than 600px on a device that is not a handheld now shows it.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 23:07:46 +01:00
Codeman maintainer b587ac71f4 Merge #809: the File Viewer header button follows its setting on phones
Head 6c2e257d (DodgyBadger). The phone half of #759: the button stays off
by default on a phone and shows once the setting is turned on.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 23:03:01 +01:00
DodgyBadger 81c7708353 fix(files): honor File Viewer header opt-in on phones 2026-11-11 20:16:19 +00:00
Codeman maintainer d45b1ff248 Merge #650: jcode as a run mode
Conflict in skills/codeman/SKILL.md (and its plugin mirror): the list of
modes with neither stop nor blocked gained jcode here and the omp
exception from #798 on the release branch; both are kept.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:27:26 +01:00
Codeman maintainer 59d4bbca2d fix(files): the editor never reaches Codeman's own .env by another spelling, and a save keeps the file's group (#797)
The four review items of #797, applied at merge.

The editor's name rule lowercases, the data dir's .env pattern did not:
.codeman/.ENV passed both, and on a case-insensitive filesystem (the macOS
default) that is ~/.codeman/.env, the login file codeman attach reads. A
session whose workspace is the home folder could open and save it. The
pattern is case-insensitive now, pinned in the blocklist table and on the
three editor routes.

A save writes a new file and renames it over the old one, so the file took
the server's own group: a 0640 .env owned you:www-data came back you:you
and the web server lost read access. The save now puts the old user and
group back where the account may (best effort), and the wiki says mode
and group instead of permissions.

Tests: an .env in an outside worktree is refused on edit=1 and PUT too,
now that the editor's own gate lets dotenv through; the comment on why
.env.example is blocked for downloads and attachments is back.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:25:37 +01:00
Codeman maintainer 18b309fc02 Merge #797: the File Viewer edits workspace dotenv files
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:23:25 +01:00
Codeman maintainer 68e010b887 fix(auto-compact): decide again when the command goes out, and send nothing into a usage-limit pause (#777)
The review items of #777, finished here so the PR can land.

A statusline report fires the trigger in the middle of a turn, and the
command then waits for that turn to end. Until now it went out whatever
had happened meanwhile: with auto-clear at 140k, a report of 150k fired
while the session worked, Claude Code compacted by itself, the report
fell to 20k, the session went idle and /clear wiped the conversation.
Both actions now ask again at that moment and return unsent when the
context is no longer at the threshold, when the option was switched off,
or when its threshold was raised past the context. The skipped command
stamps nothing, so the next report is judged like any other, and the
autoCompact and autoClear events carry the size at the moment of sending.

With auto-resume on, a trigger that fired before a usage limit hit sent
/clear once the pane went idle, and auto-resume then typed continue into
an empty conversation. The checks start nothing from the limit message
until the resume prompt went out, and an action that waited through the
start of a pause returns unsent.

Also: the threshold cap never drops below the 1,000 minimum (a reported
window of 500 stored a threshold of 500), a test pins that a conversation
switch drops the report, and docs/api-reference.md names the new
contextWindowSize session field.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:23:09 +01:00
Codeman maintainer 6fa2dfa549 Merge #777: auto-compact and auto-clear from the statusLine context size
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:18:51 +01:00
Codeman maintainer 560377b228 Merge #807: the skill spawns a local model worker with the coding agent of your choice (local:pi, local:deepseek)
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:15:43 +01:00
Codeman maintainer f7fccc5dfc Merge #806: pick the coding agent per session, with Pi and DeepSeek Harness, and install a missing one
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:15:38 +01:00
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 2da3cf3853 Merge the lid keep-awake without the administrator password (desktop)
The Mac app keeps a closed MacBook awake through the kernel's lid-sleep
switch, held by a child process of the app, instead of the root helper that
1.50.1 installed behind an administrator prompt. The old helper is no longer
installed or used; Keep This Mac Awake offers to remove it while its files
exist.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:15:24 +01:00
Codeman maintainer be8b96fa5d feat(desktop): keep a closed MacBook awake without the administrator password
Keep This Mac Awake > With the Lid Closed is a plain switch now. 1.50.1
installed a root LaunchDaemon behind the administrator prompt, which set
`pmset disablesleep` while the app asked for it. The app now turns off lid
sleep in the kernel's power management root itself
(`kPMSetClamshellSleepState`, a call any process may make), the method
LogoLight ships as Lit with Lid Closed.

That switch has no owner (read from Apple's sources of the kernel and of
powerd): the kernel keeps it after the process that set it has gone and
honours it on battery, macOS's power daemon and LogoLight write the same
bit, and clearing it under a closed lid puts the Mac to sleep at once. On an
Apple silicon Mac it is also what keeps a docked MacBook awake with its lid
closed, set by the power daemon once, when a display comes. So a child
process holds it (`src/lid_awake.rs`, this binary with `--keep-awake-lid`):

- it sets the switch again every second and holds two assertions with it
  (system sleep and idle sleep), the three LogoLight measured together;
- it reads the power source itself and holds nothing on battery;
- it writes nothing at all while an external display is connected: a zero
  from here would put a docked Mac to sleep, or leave it without its
  closed-lid mode until the display is plugged in again. What the child set
  before the display came is cleared once the display has been gone for
  five seconds, and the child outlives the app for that if it has to;
- it lets go when the pipe from the app closes (quit, crash, Force Quit) and
  when the app has not written for a minute (a hang), the assertions first,
  then the switch;
- it ignores SIGHUP, SIGINT and SIGTERM, so the pipe is its one way out;
- it leaves the switch alone at its end while LogoLight holds it for itself
  (LogoLight sets it once and would not notice it gone).

One child at a time holds a lock on `lid-awake` next to `desktop.json`, and
the file is non-empty while the switch may be set. A child that was killed
leaves it marked and unlocked: the app turns lid sleep back on when it sees a
child end and at its next launch, and the next child takes the mark over.

The status line says what came of it: the kernel took the call, the lid is
left to macOS at a display, on battery, refused, or that the Mac slept under
the lid all the same and until when. A display has its own wording so a Mac
that sleeps there never reads as covered.

The helper of 1.50.1 is no longer installed or used. Its request file is
never written and is deleted at launch, so a helper that is still there does
nothing. While it is installed the menu has Remove the Old Lid Helper…, the
one item left behind the administrator prompt. `macos/keep-awake-lid.sh` and
its vitest suite go with the install path.

Checked on a Mac mini (macOS 27.2, no lid, no display): 276 tests, clippy and
fmt; the kernel takes the call from an unprivileged process; the real child
holds its assertions, lets go on a closed pipe and after a minute of silence,
ignores SIGTERM, waits for a child before it, writes nothing when told a
display is connected, and a SIGKILLed child is replaced by the app and its
mark undone. 34 deliberate breaks of these rules each fail a test. Not run
yet: the app itself with this change (the mini has no session to open it
in), a real closed lid, the menu items, and the removal of the old helper.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 10:36:23 +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 d04fb63837 Merge branch 'feat/local-model-harness-choice' into feat/local-model-skill-agents 2026-11-11 10:24:06 +01:00
Codeman maintainer bd362f736b fix(local-model): a chip that was only looked at never hides "Start a session"
Selecting a chip stored it for the device at once. Someone who clicked
"DeepSeek" (Needs setup) or "Pi" (Not installed) just to see what it
says then got a window that reopened on that chip: "Install Pi", or no
main button at all, with "Start a session" out of sight.

The device now remembers the agent a session was STARTED with, an
opening of the window selects it only while it can be started, and a
selection made in one opening does not carry into the next. Otherwise
the server's default agent is selected, as before.

Also: a real-browser test for the chips (they wrap inside the window at
1440, 390 and 320 px, the install command included, one tab stop), and
one sentence in the API reference: a readiness mark says the composer is
there, not that nothing else is (DeepSeek Harness's setup guide draws
the same glyph).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 10:24:03 +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 aa8afdf2f6 fix(skill): a DeepSeek Harness worker on its setup guide is refused, not typed into
dsh-TUI 0.19.0 opens a setup guide ("Step 1 of 4") in every session
until a person has finished it once, and the guide's cursor is the same
glyph the skill waits for as the composer: the readiness mark matched on
it in 2 of 2 launches. An Enter there acts on the highlighted step,
which is a sign-in. A spawned worker was handed back "ready" on that
screen, and the first task prompt would have been typed into it.

After the mark, the rendered pane is now read once: a worker that shows
the guide is deleted and the spawn fails with what to do (finish the
guide once from the Run menu). For `local:deepseek` and for a plain
`deepseek` worker alike.

Also says which verb reads a local DeepSeek worker's answer: `sendmark`.
The harness reports the end of a turn, but dsh 0.2.2 renamed its
transcript file and Codeman reads an empty answer from it for now.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 10:16:18 +01:00
Codeman maintainer bdd882bce8 feat(skill): a local worker with the coding agent of your choice
`spawn_worker <name> local` started the one agent the model was
installed with. The server now lists every agent the model can be run
with, so the skill passes the choice on:

- `local_model` adds `agents` (each with its status, whether it is the
  default, and the readiness mark the server measured) and `models`
  (what is on the Mac; only the active one runs). A new reason,
  `no-agent`: installed, but nothing that can run the model is there.
- `spawn_worker <name> local:<cli>` (`local:pi`, `local:deepseek`,
  `local:opencode`, `local:claude`, also as `name:local:pi` in
  `spawn_workers`) starts an agent whose status is `ready`. Anything
  else fails the spawn before a session exists and names the agents
  that are startable. No agent is ever installed from here: that stays
  a person's click in the window. A Codeman that lists no agents knows
  one, and only that one can be named.
- The readiness mark comes from the server when it names one, else from
  what this skill measured (OpenCode `┃`, Pi `extended-keys`), and
  `LOCAL_READY_MARK` overrides both.
- A Pi worker on the local model is started with
  `piConfig.approveProjectTrust: false` (--no-approve). On a custom
  model Pi keeps no home, so it asks to trust the folder in every
  session with Trust preselected, and the task prompt's Enter would
  answer that.

Pi's mark is the last line of its startup, a warning about tmux's
`extended-keys` option. Its composer rule is painted in the first frame,
before Pi takes Enter: a prompt sent at the rule stayed unsent, 2 of 2,
and one sent at the warning was submitted, 4 of 4 (pi 1.5.2, Linux). It
is a side effect, not a contract, so a Pi that stops printing it fails
the spawn and the worker is deleted, never handed back on a guess.

The preamble changed, so CODEMAN_PREAMBLE moves to 1.51.0 in the
preamble, the SKILL.md copy of it and every version check, and the
plugin mirror is synced.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 10:11:51 +01:00
Codeman maintainer 0442b4cdbf feat(local-model): pick the coding agent in the window, and install one that is missing
The installed screen gets one more block, "Start a session with": a chip
per coding agent the model in use can be run with, as a radio group, the
server's default selected. A chip that is not on the Mac says so.

The main button follows the selected chip:
- an agent that can be started: "Start a session", with that agent;
- an agent that is not on the Mac: its literal install command above the
  button, and "Install <agent>". The click is the consent; the server
  runs its own stock command, and the window shows "Installing" on the
  chip while the model stays ready and another agent can be started;
- an agent that needs a setup of its own (DeepSeek Harness without a
  terminal profile): one line that says where, nothing to press.

A failed install shows the server's sentence under the chip with the
installer's last words folded away; the model's own headline is
untouched. On a stopped model Start stays the one main button. The
choice is remembered on the device. A server that lists no agents gets
the screen it had.

Rendered in Chromium at 1440, 390 and 320 px on a dark and a light
skin: the chips wrap, nothing leaves the window.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 10:00:03 +01:00
Codeman maintainer d22bfaaaaf feat(local-model): the coding agent is chosen per session, with Pi and DeepSeek Harness
An installed model knew one coding agent: the one it was installed with.
The launch check never asked about the agent, only about the endpoint
and the model, so nothing stopped a second one. What was missing is the
list, and a way to get an agent that is not on the Mac yet.

The install view gains `harnesses`: every catalogue harness the model in
use can be run with, each with how it stands (`ready`, `installable`,
`installing`, `setup-needed`), the literal install command, a readiness
mark for scripts, and the actions the server accepts for it. `cli` stays
the default agent, and names one that can be started whenever one can,
so a client that reads only `cli` never starts an agent that is gone.

`POST /api/local-model/agent { cli }` installs one more agent beside an
installed model. It is not a job and not an event of the reducer: the
state stays `installed` whatever the installer does, so a failed npm
install never blocks a session on a model that works. Same install slot
and same compiled stock command as the `agent` step, one at a time, the
outcome in memory only.

The catalogue:
- `pi` is no longer gated. Its two open questions were answered by a run
  against a real model (the `name:tag` id reaches the runtime unchanged;
  its first request is 2,096 tokens), and Codeman now passes it the
  window. Its minimum is 65536 like the others: nothing below was run,
  and no model row offers less, so no Mac's pick changes.
- `deepseek` is new. It is a launcher: its install command leaves it
  with nothing to launch, so it is never reported installable (read off
  `discovery.launcherProfile`, not the id) and a Mac is offered it only
  where it was already set up. Its tool round trip was seen on a
  scripted endpoint only; the header says how to gate it again.
- a row may carry `readyMark`; `opencode` has the measured one.

A switch no longer refuses for the agent a model was checked with when
another agent that can run it is on the Mac.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 09:53:54 +01:00
Codeman maintainer a5a1b6f9a2 fix(custom-model): pi is told the endpoint's context window
A pi session on a custom endpoint got a model list entry with only the
model id. pi then takes the model for a 128,000-token one (its footer
reads "/128k") and asks for 16,384 reply tokens, so on a 65,536 or
32,768 window it never compacts before the server's real window is full.

The entry now carries `contextWindow` (the full window) and `maxTokens`
(a quarter of it, 8,192 at most) whenever the endpoint's context length
is known: the same two figures the DeepSeek Harness patch already
carries, now from one helper. An unknown length writes neither, as
before.

Measured on pi 1.5.2: with both fields `pi --list-models` shows the
window, and the request carries `max_tokens: 8192`.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 09:53:12 +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 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
JD 8b15c96c0f feat(files): let the File Viewer edit workspace dotenv files
The plain preview already serves a workspace dotenv file in full to the same user, so refusing the edit protected nothing. The edit gate now passes allowDotenv to the sensitive-path check, which skips only the two dotenv patterns; every other credential pattern, a dotenv file inside a blocked tree and Codeman's own data-dir login file stay blocked. Downloads and attachments are unchanged.
2026-11-09 16:44:27 -05: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 8d7d0c5c7f Merge #795: feat(desktop): keep the Mac awake while agents work, lid closed included
# Conflicts:
#	packages/desktop/README.md
2026-11-09 07:57:41 +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
Codeman maintainer 79a6c03363 feat(desktop): keep the Mac awake while agents work, lid closed included
Codeman > Keep This Mac Awake in the macOS app: While Agents Work (an
agent at work, plus 5 minutes after the last one stops) or Always While
Codeman Is Open. Off by default, and only for a Codeman on this Mac.

- Idle sleep needs no password: the app holds an NSProcessInfo activity
  with IdleSystemSleepDisabled, which the kernel drops with the process.
  While it only watches for work, an activity that still allows sleep
  keeps App Nap from slowing its look at the server.
- On a MacBook, Only on Power Adapter (on by default) pauses it on
  battery, and Also With the Lid Closed installs a small LaunchDaemon
  behind macOS's administrator prompt. It sets pmset disablesleep only
  while the app keeps a fresh request file with its live pid, only on
  the power adapter, and undoes only what it set itself. The app writes
  the script and plist as root from the copies built into it.
- A status line at the bottom of the submenu says what it does now.

This brings the macOS part of the keep-awake idea from PR #595 into the
app; the server-side setting and the Linux route are left out for now.

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