Integrate upstream model catalog and preserve fork workspace - #2
Merged
lightcloud00 merged 100 commits intoAug 23, 2026
Conversation
…ss per session
Verified against claude 2.1.221 with --input-format stream-json: the CLI
settles a turn with `result` while stdin stays OPEN (EOF is the exit
signal, not the turn signal); the next user message on the same stdin is
a new turn in the same process; a message that arrives MID-turn is
delivered before the model's next call and folded into the same turn's
one result. That last behaviour is exactly the "steer" the plan wanted.
- claude.ts keeps one live process per thread across turns: reused while
idle, unchanged in spawn contract, and the session the harness wants;
otherwise closed and respawned with --resume. `result` settles the
turn, not the process; the process closes after 10 minutes idle
(OMB_CLAUDE_SESSION_IDLE_MS). steer() writes into the open stdin.
- contract: capabilities.queueing and an optional adapter.steer() — the
one-file driver promise holds; every other driver keeps the 409.
- harness: POST /messages while busy on a queueing engine steers instead
of 409ing; the message is appended in order and marked `steered`. The
composer stays open on such engines ("Enter sends this into the running
turn"); a "sent mid-turn" tag on the bubble says the model saw it.
- 3.1 remainder: injected local models carry contextWindow from Ollama's
/api/ps context_length when the model is running, so a small model's
rebuild is sized to what it can hold instead of a name-based guess.
- fake claude rewritten line-driven (steer folding, `slow` mode).
Items 3.2 (and the 3.1 remainder) of docs/plans/agent-harness-upgrades-v2.md.
Answers the plan's open question 3.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Analytics stay on by default — the counts keep working — but there is now a switch in Settings → General, and the choice survives restarts. The guard sits before init(), not just around capture(): an install that opted out never calls posthog.init(), so nothing leaves the machine, not even the library's own bootstrap request. Flipping it while the app is running takes effect immediately through opt_out_capturing(), which also drops what is already queued. identifyEmail() carries a second, explicit check, because it is the one call that would send a personal identifier; the address is still stored locally in the profile either way — opting out stops it being reported, not being used. Why bother, when autocapture is already off and the event list is short: the people who worry about this cannot read the source to find that out. Naming what is sent, next to a switch, is what makes the existing restraint legible — and docs/ios-privacy.md already promises exactly this posture for the companion app, so the desktop side now matches. optAction() carries the decision as a plain function, so the four cases are checked without standing up a client to observe (no module mocking, per the anti-slop rule). 9 tests, including the one that matters: opting out before init must not reach PostHog to tell it so.
`pnpm check:contrast` parses src/styles.css and measures 21 pairs. It reads the stylesheet rather than keeping a second copy of the values, so it cannot pass against a palette that is no longer the shipped one. Two things it does that reading the hex values does not: - It composites alpha. --color-ink-secondary is #fcfcfc99, and measuring it as opaque overstates every secondary-text pair in the app by a wide margin (2.86:1 vs the 15:1 the raw value suggests on --color-app). - It measures white on filled surfaces, because that is what the components render — `bg-accent … text-white` — rather than the token against the page. Three pairs sit below AA today. They are listed in KNOWN with their call sites, so this lands without changing a colour in the same commit and fails only on something new: white on accent 3.65:1 every primary button, 12-13px (28 sites) white on danger 3.10:1 the hang-up buttons, 14px accent on card 4.15:1 accent links inside a Card (11.5-12px) The shape matters more than the numbers: --color-accent is fine as text on the page ground (5.33:1 on --color-app) and only falls short on the lighter card, while white falls short ON the accent. Darkening the token fixes the buttons and hurts the links, so the fix is a separate fill colour rather than a nudge. That is a design call and yours to make — this only measures, and deleting a line from KNOWN is how a fix gets locked in. Verified both ways: exit 0 as shipped; drop --color-ink-secondary to #fcfcfc55 and it exits 1 naming all five surfaces.
With the Computer or Inspector panel open the chat column drops to ~700px and the header row — Stop, + Task, usage, working folder, model, icons — wrapped onto three lines and crushed the bot avatar and name to nothing. The header is now a CSS container (@container/chathead); below 4xl each chip folds to an icon-only shape and the right group stops shrinking so the name truncates instead: - Stop → round bubble with the square - + Task → round bubble with the plus (count-only bubble once there are several tasks) - usage → one short figure: cost when known, else tokens - working folder → rounded square with the folder icon - model → rounded square with the provider mark Full labels still ride the tooltips. Wide headers are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- parseTokens strips CSS comments first. A declaration left in a comment reads exactly like a live one, so a removed-but-still-mentioned token was measured at its stale value instead of reported as undefined. - KNOWN is a Map of ratios rather than a Set of names. It was a licence, not a floor: white-on-accent could have slid from 3.65:1 to 2:1 and the run stayed green. A carried pair that gets worse now fails like anything else, with 0.01 of rounding headroom since the floors are quoted to two decimals. - A known pair that climbs past AA is announced so the line gets deleted, rather than sitting in KNOWN shielding a pair that no longer needs it. - The header said two pairs; there are three. Verified all three ways: accent → #0a5db3 fails with "WORSE than the recorded 4.15:1"; accent → #0d5aa8 prints "now measures 6.90:1 — remove it from KNOWN"; commenting out --color-warning fails with "undefined token" instead of quietly measuring the dead value.
…real Both findings were right. An opt-out was only as durable as the write that persisted it. With storage rejecting the write, the setter swallowed the error, the next read found nothing and answered "enabled", and a later initAnalytics() would start the client the user had just switched off — the one failure this feature exists to prevent. The choice now lives in module state, set before the write is attempted; storage is how it survives a restart, not where it lives. The cold-start test also proved nothing: an earlier test had already set the module-scoped `ready` flag, so initAnalytics() returned on that rather than on the opt-out. It now loads the module fresh — resetModules, not module mocking: nothing is replaced, the real module is simply loaded again. Verified by removing the `!analyticsEnabled()` guard from initAnalytics: the test fails. It did not before. 10 tests, full suite green.
Kimi ACP session/new checks default_model, not -m. A missing or expired login then becomes "Authentication required" even when the picker is a local host. Overlay Kimi's official KIMI_MODEL_* env on inject turns so the child has an in-memory default, and write protocol plus max_context_size on the on-disk alias so 0.36+ will bind it.
Aliases written before this PR were left as-is, so Kimi 0.36+ skipped default-model binding. Fill in protocol and max_context_size when they are missing, and leave any values the user already set. The applyTurnEnv test now checks both the resolved model and the picker id.
Line-based heading and key checks missed quoted keys, headings with comments, and bracket lines inside multiline strings. Walk the file outside of strings so existing aliases are patched once, and document the helpers the coverage check was counting.
Droid ACP session/new requires a Factory login or FACTORY_API_KEY even when the picker is a BYOK custom host. The CLI only checks that the variable is set, then uses the custom row's own key. On a local inject turn, fill a placeholder if the user has no Factory key. Cloud models are unchanged.
A usage chip on first paint called toFixed on undefined for bots.json rows written before cost tracking. The packaged window rendered black.
Current Studio stores keys as servers[url].minted instead of a top-level api_key. Without that, /v1/models returns 401 and Custom never lists Unsloth models.
Skip the Droid FACTORY_API_KEY placeholder when a Factory auth file already exists. Prefer localhost minted Unsloth tokens over a stale top-level api_key. Treat NaN/Infinity costs as missing in the chip and settings. Trim whitespace around dotted TOML headings and ignore """ inside comments or single-line strings.
Skip # comments in tomlTables so an apostrophe in a comment cannot open a phantom string and hide the real model heading. Treat [[array]] headings as table boundaries without patching them, so protocol keys land in the model table instead of the following hooks array.
Upstream blocked Auto while a bot was on the local computer. On macOS the user can now confirm a warning and let the bot click and type here; destructive and sensitive actions still stop. CUA can fall back to the standalone CuaDriver.app so existing Accessibility grants keep working. Also: Claude turns rewrite leftover Custom slugs onto a live local host so the picker does not demand /login when Unsloth is already serving the model.
\u0035 in a quoted table key is 5, not the letters u0035, so an existing [models."omlx/GLM-\u0035.2-fp8"] matches the inject alias and is patched instead of duplicating the table.
\u0035 in a quoted table key is 5, not the letters u0035, so an existing [models."omlx/GLM-\u0035.2-fp8"] matches the inject alias and is patched instead of duplicating the table.
- Destroy a failed embedded CUA host before falling back to standalone - Refuse a one-arch CUA stage unless PARTIAL=1, matching dual-arch packaging - Open System Settings and Retry are separate; retry after the window refocuses - Sort usage by finite cost only (missing/NaN/Infinity last) - Reset TOML string mode at newlines so a stray quote cannot hide later tables Unclassified GUI clicks still auto-approve when Auto is on after the warning; default-denying every click would restore the ban this PR removes. Destructive and sensitive actions still stop.
…, one-shot retry. Reject unknown/malformed/surrogate unicode escapes so they cannot canonicalize to another alias. Empty OPENMAUSBOT_CUA_ARCHES throws even with PARTIAL=1. Settings-return retry runs at most once if focus and visibility both fire.
`POST /api/teams/import?mode=project` adds the team and opens a room for it, optionally on a folder: `&cwd=<path>`, `&room=<name>`. Without it, setting up a project is three steps — import the team, create a room, pick its members — and the third one is tedious once a team has more than a few people. **The manifest still describes only people.** Room name and folder come from the caller, never from the file. That is deliberate and preserves what v2 established when it dropped its `room` block: a manifest fetched from the library must not be able to create structure in someone's workspace. As a parameter, a local caller gets the one-step setup without opening the format to a remote one. It reads the same way as the neighbouring guards — `seedMessages: false`, `composio: false` — a shared team brings people, not reach. Details worth knowing: - The folder goes to `cwd`, not `pinnedCwd`. It is what the room WANTS; the store pins it on the first turn, which is its call to make, not ours. - The path is validated with the same `validateBotCwd` a bot's folder uses, before anything is created — a bad path is a 400 with no bots and no room left behind. - The room is created last, so any failure above leaves nothing pointing at half-built state. - `add` and `replace` are untouched: they still return no `group`, and the existing test asserting that still passes. Verified by disabling the room creation: the new test fails. Full suite green.
The download+extract step in scripts/prepare-android-tools.mjs assumed a
native Windows shell where tar is bsdtar (zip-capable). On git-bash it is
GNU tar, which (a) reads the C: drive prefix in the -C path as a remote
host ('Cannot connect to C:') and (b) cannot read .zip at all. That broke
pnpm package:win out of the box on Windows.
- Prefer unzip, fall back to tar on Windows (covers git-bash AND native
cmd/PowerShell).
- Normalize absolute Windows paths to MSYS/POSIX form so git-bash's tar
-C never treats the drive letter as a host.
Verified: pnpm build:android-tools now stages adb.exe via the real
download+extract path; pnpm typecheck passes.
settle() closed the permission broker and forgot the turn, but never killed the spawned claude -p --resume child. A one-shot process is expected to exit right after printing result, but a backgrounded MCP grandchild can keep it alive with a live broker connection. Adds killCliTree(child) to settle(), unconditionally, on every terminal path. A no-op when the process already exited. Adds a result-then-hang fake-CLI mode (prints result but never exits) to reproduce and test the leak, and exposes the fake CLI's pid via the existing FAKE_CLAUDE_DUMP mechanism so the test can confirm the process is actually gone, not just that turn.completed fired. Part of milind-soni#211
net.Server.close() only stops accepting new connections — it does not touch a connection that's already open. A still-alive child's MCP proxy could keep sending asks on such a connection after the turn ended, and the connection's data handler stayed fully wired to it, adding new pending entries and emitting request.opened cards for a turn the driver had already forgotten (active.delete(threadId) already ran). That card could then never be answered. Adds a closed flag set at the top of close(); any ask received while closed is always replied to directly on the connection (mirroring close()'s existing pending-ask handling) instead of registering a new pending entry or notifying onAsk — never a silent drop, since permission-proxy.ts's MCP tool call only resolves on an explicit answer or the connection's own error/close. Fixes milind-soni#211
… late-ask path
The late-ask reply and close()'s pending-drain loop derived the same
kind -> {behavior, message} mapping independently in two places.
systemEndedReply(kind) branches on question vs permission, but only the permission/deny arm was exercised by the existing late-ask test.
Keep closed-broker handling terminal ahead of active-turn duplicate ask checks, preserve exact late replies, and settle in broker-close/process-kill/cleanup order.\n\nFixes behavior from milind-soni#211 while retaining the original milind-soni#229 author commits.
pi (@earendil-works/pi-coding-agent) exposes a JSON-RPC mode over stdio (`pi --mode rpc --no-session`) rather than ACP, so — like the Claude Code and Codex CLIs — it gets a native driver that speaks its own protocol and emits canonical RuntimeEvents. pi is a BYOK agent: credentials live in ~/.pi/agent/auth.json and are injected by the pi binary, so the driver holds no API key and needs no sign-in. - server/drivers/pi.ts: native ProviderDriver. Per-turn it spawns `pi --mode rpc --no-session`, resumes the prior session via switch_session (the sessionFile from session.started is the resumeCursor), pins the chosen model with set_model (splitting the picker's provider/modelId composite), and translates the RPC event stream (message_update text/thinking deltas, tool_execution_*, turn_end) into canonical events. toolUse turns are not settled — pi auto-continues to synthesize the reply, and settling early drops it. extension_ui_request select/confirm/input become request.opened; respondToRequest answers via extension_ui_response. A missing CLI surfaces as snapshot unavailable. - server/drivers/pi.test.ts + server/testing/fake-pi-cli.ts: contract tests against a scripted fake pi CLI — catalog parsing, the full happy turn, the toolUse auto-continue, permission brokering, interrupt, and snapshot. - builtIn.ts registers PiDriver; config.ts seeds pi into DEFAULT_FLEET and CUSTOM_ONLY so the engine instance appears (and merges into existing configs via PRODUCT_FLEET_ADDITIONS). `pnpm typecheck` and `pnpm test` pass; the live catalog is flagged `custom` so the model picker's Local pane lists pi's BYOK models.
…e-contract-ios feat(profile): add paired-safe agent profiles and avatars
…rtial text A room's approval/question notification carries the asker bot with the GROUP's thread id, so the exact-task click path asked the bot to switch to a thread it does not own — a 404 error banner on desktop and, on the phone, an error with no navigation at all. Clicking now opens the room itself; a thread that is neither a room nor one of the bot's own lands on a plain bot select, and the phone tolerates a vanished task the same way. The duplicate-done suppression was guarded by a test whose fixture crashed before streaming anything — with an empty reply the pre-existing quiet-done rule suppressed the duplicate on its own, so the guard could be deleted without failing anything. The new fail-after-text fake mode streams half an answer and then fails the turn: a non-empty reply makes the routine-failed/done dedup the only thing standing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e-desktop feat(profile): add persistent desktop profiles and avatar roster
…es-notifications feat(routines): clarify scheduled tasks and exact notifications
Adds a comprehensive Fumadocs site, aligns it with openmausbot.com, includes clean product screenshots, redirects the root to the docs reader, and documents current shipped behavior.
…catalog-20260822 # Conflicts: # src/components/ModelPicker.tsx
…ars (milind-soni#375) Groups notification replacement by bot, carries bot avatars into desktop banners, and preserves exact click-through targets.
Prevents duplicate desktop launches from spawning a second harness and restores/focuses the existing app window.
…#371) * fix(grok): local host:: picks must not require grok.com login Same hole Kimi and Droid already closed: a loopback inject talks to Unsloth/oMLX with its own key. Subscription cached_token must not fail the turn before the local model is asked. * docs(acp): name the local-inject auth skip CodeRabbit's pre-merge check wanted docstring coverage on the touched ACP driver. The skip is now a documented helper with a unit case for host:: vs cloud ids. --------- Co-authored-by: Max <ajsdaksfjhs@gmail.com>
Co-authored-by: jayfunkdown <jasonramalho@gmail.com>
* fix(cursor): send the session's ACP model id, not the argv slug
Cursor keeps two model namespaces that do not match. `cursor-agent models`
and the `--model` flag speak flat slugs (`auto`, `gpt-5.3-codex`), while an
ACP session advertises parameterised ids (`default[]`,
`gpt-5.3-codex[reasoning=medium,fast=false]`) and `session/set_model`
accepts only those.
configureSession passed `turn.model` -- an argv slug -- straight through, so
every model earned -32602 Invalid params, not merely unknown ones. `auto` is
OMB's default for Cursor, so a stock install with nothing customised could
not run a single Cursor turn. The error text blamed the CLI version and the
account's entitlements, which sends people to check a subscription over an
id-format mismatch.
configureSession could not have fixed this alone: its ctx had request,
sessionId, config and turn, but not session/new's result. core.ts now passes
sessionModels (already in scope as sessionResult), and cursor.ts resolves the
slug against it -- exact id, then base before `[`, then display name, then
the auto/default special case. No match returns null and the raw slug is sent
as before, so a CLI that advertises no models behaves identically.
Also treat -32602 like -32601 in the catch. spawnArgs already pinned
`--model`, so the turn runs the right model regardless; throwing there
refused a request that would have succeeded.
Verified against cursor-agent 2026.08.11-e8db854: set_model("auto") and
set_model("gpt-5.3-codex") both return -32602, while the resolved
"default[]" and "gpt-5.3-codex[reasoning=medium,fast=false]" both return OK.
Tests: five unit cases for the resolver; a wiring case asserting set_model
receives `default[]` while argv keeps `--model auto`; and a regression case
that a -32602 still completes the turn. The fake ACP CLI gains
FAKE_ACP_SESSION_MODELS and a set-model-invalid-params mode, both off by
default so existing modes stay byte-identical.
* test(cursor): cover ACP display-name resolution
---------
Co-authored-by: gmuniz21 <66831691+gmuniz21@users.noreply.github.com>
* feat(desktop): one-click diagnostics export * fix(diagnostics): protect exported reports * fix(diagnostics): harden log and file boundaries --------- Co-authored-by: Will Sigmon <wjsigmon@gmail.com>
* fix(hermes): offer the model Hermes' own config names, not only local hosts Hermes' model picker listed local inference hosts exclusively (Ollama, LM Studio, EXO, oMLX, Unsloth). Hermes is a BYOK harness and its own `hermes setup` stores a hosted provider key in ~/.hermes/.env, so a user who configured one had no selectable model at all: the picker said "No local models found" and greyed the agent out, while `hermes` was installed, authenticated, and answering fine from its own config. resolveModels now also offers the model that config names, when a hosted key is actually configured. The option is deliberately NOT a local-inject id, so hermesAcpModelId returns null, configureSession sends no session/set_model, and spawnArgs passes no -m -- Hermes falls through to its own configured provider, which is the whole point. Read-only by design. ensureHermesInjectProvider writes config.yaml, and doing that from a catalog probe would rewrite the user's real Hermes config as a side effect of opening a menu. Returns null when no hosted key is set, so local-only installs keep exactly the catalog they have today. Verified against Hermes Agent v0.20.5 with an OpenRouter key: the picker offers "anthropic/claude-opus-4.6 (Hermes config)", a turn completes, and the spend registers on the provider side. Tests: a configured key is offered; a commented-out key is not (the shipped .env ships `# OPENROUTER_API_KEY=`, and reading that as configured would offer a model that cannot authenticate); a missing .env leaves local-only setups unchanged; an unreadable config.yaml still yields a usable option with a generic label; and the id maps to no ACP model id. * fix(hermes): offer the whole catalog Hermes advertises, not one model Follow-up to the same bug. Offering only the model config.yaml names fixed "greyed out" but still hid everything else the account can reach: Hermes advertises its full catalog on session/new -- 48 models here -- and the driver never asked, so a user paying for OpenRouter could see exactly one. resolveModels now reads that list from a short-lived `hermes acp` session. There is no `hermes models` subcommand, so the spawn is the only route; it runs only when a hosted provider is configured, and any failure returns [] rather than making the agent unselectable. hermesAcpModelId forwards Hermes' own `<provider>:<model>` ids untouched (`openrouter:qwen/qwen3.8-max`). Previously it returned null for anything that was not a local inject id, so configureSession sent no set_model and the model could not be chosen even if it were listed. Local inject ids keep mapping to `custom:<host>:<model>`, and the config sentinel still returns null so Hermes keeps its own default. Adds `custom: true` to the configured-model option. ModelPicker renders a custom-only agent's custom pane exclusively, and that pane lists only flagged options -- without it the option reached the API but stayed invisible in the UI. Verified against Hermes Agent v0.20.5 with an OpenRouter key: 49 options including qwen/qwen3.8-max and deepseek/deepseek-v4-flash, and a real turn on Qwen completes. * fix(hermes): harden remote model discovery * fix(hermes): bound and terminate catalog probes --------- Co-authored-by: gmuniz21 <66831691+gmuniz21@users.noreply.github.com>
* feat(rooms): manage a room's members after it exists A room's roster was fixed at creation: the only way to add a bot to a team or drop one was to delete the room and rebuild it, losing the transcript. The server already accepted memberIds on PATCH — nothing in the app ever sent it. The member mauses in the room header are now the way in. Clicking them opens the same picker "New Room" uses, pre-ticked with who is already in; Save sends the new roster. A departing bot keeps every message it already sent — the transcript renders those from the message's own name and colour, not from the roster. Two guards close behind it, both at the API boundary: - an empty or all-unknown memberIds list was silently ignored, so "remove the last bot" looked like it worked and did nothing. It now answers 400, the same wording room creation uses. - direct-message channels are the pair they were opened for, so they refuse membership edits the way they already refuse a working folder. Removing the room's lead needs no special handling: patchGroup already re-normalizes defaultResponder against the new roster, so the lead falls back to a bot that is still in the room. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(rooms): harden member management * fix(rooms): reject stale member drafts --------- Co-authored-by: aivsomkar <aivsomkar@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(drivers): flush assistant text before tools ACP, Pi, and Box concatenated every assistant_text delta until turn settle, so a message → tool → message turn persisted as tools first and one late bubble. Flush the pending text at tool and permission boundaries, matching Claude/Codex. Box accumulates emitted deltas in pendingText so a non-prefix follow-up is not sliced away. Closes milind-soni#352 * fix(boxagent): flush assistant text on interrupt and errors Cancelled and thrown poll loops were emitting turn.completed without finalizing pendingText, so a mid-stream interrupt dropped the last assistant bubble. Status-failed already flushed; the loop-break and catch paths now do too. * docs(drivers): document flush helpers touched by the text-order fix CodeRabbit's pre-merge docstring check is scoped to functions in the diff and requires 80% coverage. These JSDocs describe the new flush, ingest, and interleave-fixture helpers. * test(box): pin assistant flush ordering * fix(drivers): clear whitespace-only text buffers --------- Co-authored-by: donggyun112 <ssddgg99@daum.net>
* feat: file rooms under sidebar sections Bots learned sections; rooms never did. A project usually IS a room — its people plus a working folder — so the sidebar heading that says "Clients" should be able to hold the client's room next to the client's people. Same contract as bot sections end to end: PATCH accepts null/"" to clear and 60 chars max, the picker is shared (rooms and bots draw from one namespace), and a section renders its rooms above its bots under one divider. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: open the room menu from the keyboard Shift+F10 and the dedicated ContextMenu key open the room context menu centered on the row — before this, "Move to section" was only reachable by pointer (and the ContextMenu key's native event carries no useful coordinates). Flagged by CodeRabbit on milind-soni#338. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Gökhan Köse <goekhan.koese@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: scout a project folder into a suggested team Point the Teams panel at a folder and it reads what is in there — README, dependencies, layout — and proposes a team for it: a lead plus one member per detected role (frontend, backend, mobile, data, testing, infra, docs), each carrying the evidence that argued for it. One click sends the suggestion through the existing project import (milind-soni#316): the bots are created, a room opens on the folder. The scout only ever suggests — GET /api/teams/scout is read-only and offline; nothing exists until the human imports. Community bots from botdirectory.ai arrive on a separate lazy route and are folded in as ordinary manifest members, so the persona-only import boundary applies to them like to any shared team file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: address the review — bind scout state to its folder, bound the directory read CodeRabbit's findings on milind-soni#339, all confirmed against the code: - The directory fetch buffered the whole response before checking the 1 MB cap. It now reads in bounded chunks and bails (cancelling the stream) the moment the cap is crossed; content-length is checked up front. Regression test with an endless stream proves the bail-out. - Scout results are bound to the folder that produced them: a request token drops late responses (including the lazy directory call), and the import pins the room to the folder that was actually scouted, not to whatever the input field says by then. - Two- and three-letter stack terms ("go", "php") no longer feed the directory matcher — "google" and "django" are not Go affinity. - The candidate row is a plain container again: the label wraps only the checkbox and its text, the detail-page button sits outside with a real accessible name. - Truthful comment on ProjectSignal.evidence (it does reach the provider as persona text), basename() in the folder-name test, scratch-folder cleanup in the API test, and a logged reason when the directory lookup fails. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…#385) * fix(rooms): explain when archived members cannot respond * fix(rooms): handle archived responders consistently --------- Co-authored-by: xuyi <ilikexff@gmail.com>
* feat(drivers): add OpenAI-compatible driver for free models (OpenRouter/Groq)
- New server/drivers/openai-compat.ts: talks to any OpenAI-compatible
/v1/chat/completions endpoint, dynamic /models catalog, SSE streaming,
per-instance config + abort handling.
- Registered in builtIn.ts and DEFAULT_FLEET/instance fleet.
- config.ts: openaiCompat.{key,url} schema, AppConfig field, and
env injection (OPENAI_COMPAT_API_KEY / OPENAI_COMPAT_URL).
- Tests: 4/4 pass (vitest).
* fix(drivers): harden OpenAI-compatible provider
---------
Co-authored-by: Ansygroup <livepm8@gmail.com>
…ilind-soni#387) * fix(steer): keep queued 1:1 sends off the transcript until drain Mid-turn queue fallback was appendMessage + queued:true, which became the active leaf so remaining tool/assistant events of the current turn hung off a user line the model had not seen. The queue now holds text in memory only; drain appends the lines and starts the follow-up turn. The composer shows the same pending chip rooms already use. Closes milind-soni#355 * fix(steer): omit drained lines from replay and confirm queue in the UI Drain now passes every appended queued id into startTurn so transcript-replay adapters do not see earlier queued texts both in transcript and in the joined prompt. The 202 body uses queueId, not a transcript messageId. The pending chip is set only after the server returns queued:true, so a live steer is not labelled as waiting. * fix(steer): key pending queue chips by thread, keep message boundaries Pending fallback lines were a newline-joined string on botId, so a Shift+Enter message could not be consumed and a task switch left the chip on the new thread. Store an ordered array per threadId, and return that threadId with queueId so the client uses the server-selected task. * fix(steer): match pending chips by queue id, not text Identical queued text from another client, or a drain frame that arrives before the POST continuation, could clear or resurrect the wrong chip. Store pending entries with the server queueId, consume that id, and ignore a late POST for an already-drained entry. * fix(steer): bound consumed queue tombstones --------- Co-authored-by: donggyun112 <ssddgg99@daum.net>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Integrates current upstream main 0353c0e and exact upstream PR milind-soni#364 head 06bb384 while preserving the fork-only main commit 83922de. Conflict resolution keeps the current upstream driver behavior, the milind-soni#364 fleet model catalog, and the fork dual-VM desktop workspace bridge.
Verification:
OMB_COMPOSIO_BROKER_URL remains the backend interface. This PR creates no Worker, D1 database, namespace, DNS record, secret, or deployment. Upstream milind-soni#364 remains open for its maintainers; its Vercel status is intentionally not reproduced or bypassed.