Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
238 commits
Select commit Hold shift + click to select a range
2b05efb
fix(composer): give the editor its own line when the composer is narrow
aivsomkar Sep 15, 2026
2de2351
fix(layout): keep the chat usable at the default window size with pan…
aivsomkar Sep 15, 2026
b32a7e7
feat(chat): fold the header into one hover menu, and keep the chat us…
aivsomkar Sep 15, 2026
b6671c5
feat(chat): name the Share group in the header menu and keep the old …
aivsomkar Sep 15, 2026
b2c2426
Merge origin/main into fix/composer-narrow-wrap
aivsomkar Sep 27, 2026
46c3cac
Merge main into #1289 and preserve responsive chat controls
milind-soni Oct 1, 2026
b7477a7
Merge pull request #1289 from milind-soni/fix/composer-narrow-wrap
milind-soni Oct 1, 2026
63e3049
Control plane: reclaim idle tunnels, honest capacity error, alert at …
milind-soni Oct 1, 2026
9f0c33d
Role names: Super admin, Admin, User (#2124)
milind-soni Oct 1, 2026
cb0ffc4
feat: reveal completed chat downloads in file manager
jakequade Oct 2, 2026
bcaa5ae
fix(acp): stop killing healthy agents that go quiet for 3 minutes
aivsomkar Oct 2, 2026
8583190
feat(acp): tell the person what a quiet agent is doing
aivsomkar Oct 2, 2026
8b05584
Merge pull request #2145 from milind-soni/fix/acp-stall-timeout
milind-soni Oct 2, 2026
d775f15
chore(release): bump version to 0.1.93
github-actions[bot] Oct 2, 2026
101a89b
Merge pull request #2153 from milind-soni/release/v0.1.93
milind-soni Oct 2, 2026
1cafc3e
fix(digest): count shell commands as one tool, keep command lines out…
aivsomkar Oct 2, 2026
873de86
feat(ios): take control of a bot's Local VM from the phone (#2135)
ruigomeseu Oct 2, 2026
8e882b2
fix(ios): keep the chat back button round without a badge (#2128)
ruigomeseu Oct 2, 2026
8cd2ea1
test(server): clean up shared VM fixture rooms between cases
BitL8-ByteShort Oct 1, 2026
7a2f343
test(server): finish all room cleanup before reporting failures
BitL8-ByteShort Oct 2, 2026
2056f3a
test(ui): wait for room preview readiness
BitL8-ByteShort Oct 1, 2026
ee00612
fix(server): find Docker Desktop's bin dir on Windows for local VM im…
kshivam4781 Oct 1, 2026
97fa284
fix(ios): open conversations on the newest message when it has a tabl…
ruigomeseu Oct 2, 2026
492b47c
ci: skip the broker deploy when CLOUDFLARE_API_TOKEN is not set
asasemahmed Oct 1, 2026
e5d4393
feat(sidebar): add a cross-bot Pinned threads section (#2150)
virtuousityai Oct 2, 2026
151f1ee
fix(android): copy and select a failed routine run's error (#2110)
buttonsjasper360-lang Oct 2, 2026
25c72d8
Merge pull request #2143 from jakequade/feat/reveal-chat-downloads
milind-soni Oct 2, 2026
9e3ec30
fix(browser): let the Browser panel's full screen button lead back ou…
AdityaUmale Oct 2, 2026
0c8d4d5
Show date and time in routine run thread titles (#2112)
jakequade Oct 2, 2026
5ddc6fd
Show live computer/browser session indicator in rooms (#2119)
virtuousityai Oct 2, 2026
cc6122c
fix(server): release stalled direct Local VM claims safely (#2138)
jakequade Oct 2, 2026
32c2dfc
Merge remote-tracking branch 'origin/main' into codex/oct2-pr-triage
milind-soni Oct 2, 2026
208bccc
Add an option to refresh a thread's permissions (#2132)
guylfe Oct 2, 2026
858c1a2
test(acp): keep quiet-agent checks deterministic on every platform
milind-soni Oct 2, 2026
cdc70b9
Merge remote-tracking branch 'origin/main' into codex/oct2-pr-triage
milind-soni Oct 2, 2026
5550aa1
fix(engines): find Cursor installed on Windows without a restart
AdityaUmale Oct 1, 2026
970fae4
fix(rooms): take a deleted bot out of the rooms it was in
AdityaUmale Oct 1, 2026
a207c34
fix(claude): detect --autocompact from claude --help instead of trust…
asasemahmed Oct 1, 2026
38ae67e
fix(runtime): preserve room erasure and align provider fixtures
milind-soni Oct 2, 2026
21ad4d9
Preserve idle Local VMs and offer guarded resume with startup progress
ruigomeseu Oct 1, 2026
89f6eb7
Name the idle policy as a stop in Settings
ruigomeseu Oct 1, 2026
28ccf74
Add before-and-after VM proof and record startup progress
ruigomeseu Oct 1, 2026
ed5f766
Align idle-stop assertion and document crash recovery evidence
ruigomeseu Oct 1, 2026
0d219dd
Reuse atomic writes and status refresh in Local VM resume
ruigomeseu Oct 1, 2026
d837644
Derive Local VM resumability on the server
ruigomeseu Oct 1, 2026
2b5ac7f
Choose the Local VM wake action in one place
ruigomeseu Oct 1, 2026
a0b1ab8
Start the shared Local VM through its own route
ruigomeseu Oct 1, 2026
f0d9935
Poll Local VM readiness every 2 seconds
ruigomeseu Oct 1, 2026
d23fb6c
feat(cerebras): Cerebras API engine with its own key row in Settings …
aivsomkar Oct 1, 2026
ab6223a
fix(cerebras): replay tool-turn reasoning as `reasoning`; Cerebras re…
aivsomkar Oct 1, 2026
37ceabe
feat(cerebras): built-in browser and computer tools; text-only models…
aivsomkar Oct 1, 2026
2160e32
fix(chat-tools): a blank url on the built-in browser's read means the…
aivsomkar Oct 1, 2026
c0ccaf8
feat(cerebras): nudge once when the model announces a step but calls …
aivsomkar Oct 1, 2026
2eb2461
fix(cursor): resolve the plain model slug to the non-fast ACP variant
asasemahmed Oct 1, 2026
caeca87
fix(browser): stop blaming the network when human input overloads a s…
asasemahmed Oct 1, 2026
2cd1015
fix(acp): redeliver the full prompt when the agent compacts its session
pauldupuyjr Sep 30, 2026
bab5632
fix: require per-bot Local VMs for phone control
milind-soni Oct 2, 2026
4880741
fix: avoid counting destroyed desktop viewers twice
milind-soni Oct 2, 2026
7f79473
fix(verification): fit resume browser sockets within macOS limits
milind-soni Oct 2, 2026
bab606a
Keep prices out of math rendering
guilimasp Oct 1, 2026
772a88b
fix(chat): retain image authorization offsets around currency and math
milind-soni Oct 2, 2026
b76feb4
feat(sidebar): live status icon and per-row unpin on Pinned threads
virtuousityai Oct 2, 2026
dcb0148
chore(chat): document intentional normalization sentinels
milind-soni Oct 2, 2026
efc91dc
fix(sidebar): derive pinned room status from room activity
milind-soni Oct 2, 2026
f1faa9f
feat(sidebar): make the Active Threads panel collapsible
virtuousityai Oct 2, 2026
d985dac
fix(ios): bound untrusted VNC updates
milind-soni Oct 2, 2026
7522753
test(acp): make the quiet-agent tests pass on Linux and Windows (#2154)
milind-soni Oct 2, 2026
d49eb89
Merge main; retain portable quiet-agent assertions on all hosts
milind-soni Oct 2, 2026
92e1d2e
fix(desktop-viewer): don't re-close or re-count a viewer that is alre…
milind-soni Oct 2, 2026
d98d256
test(api): assert persisted Local VM idle timeout
milind-soni Oct 2, 2026
f08c9e6
fix(chat): preserve CommonMark syntax while normalizing currency
milind-soni Oct 2, 2026
0799f30
test(ios): clarify fixed VNC receive budget
milind-soni Oct 2, 2026
875dd24
fix(acp): retain usage peaks and invalidate compacted failed prompts
milind-soni Oct 2, 2026
55c7912
fix(claude): omit autocompact until CLI support is confirmed
milind-soni Oct 2, 2026
9487301
fix(cerebras): compact against the free-tier context window
milind-soni Oct 2, 2026
4438f8f
fix(ui): guard search toggles and contain long formulas
milind-soni Oct 2, 2026
8e1f1a3
fix(chat): preserve GFM URL destinations during math normalization
milind-soni Oct 2, 2026
0693554
Cloud plans: read the plan tier, plan-neutral Cloud wording (#2157)
milind-soni Oct 2, 2026
5a74d92
Pro card: launch price without a struck-through former price
milind-soni Oct 2, 2026
63d8a65
Merge pull request #2156 from milind-soni/fix/desktop-viewer-windows-…
milind-soni Oct 2, 2026
667579e
fix(ios): resolve chat entities without trapping on duplicate rows
bradhallett Sep 28, 2026
be0fab3
refactor(server): one direct-send path for a person's message
nevilhulspas Sep 27, 2026
f3886bb
feat(calls): Live calls on the harness, with GPT-Live as the voice an…
nevilhulspas Sep 27, 2026
04b812e
feat(calls): the desktop Live call bar, with the chat open
nevilhulspas Sep 27, 2026
79a76e7
docs(calls): Live calls in the voice docs, and the design
nevilhulspas Sep 27, 2026
bf86983
fix(calls): leave the voice's own words out of a spoken answer to a q…
nevilhulspas Sep 28, 2026
c18b65e
test(calls): call spokenApprovalPrompt with its one argument
nevilhulspas Sep 28, 2026
33cbb23
fix(calls): close the OpenAI session when a call fails to set up
nevilhulspas Sep 28, 2026
cb72977
fix(calls): keep the desktop's Live call state current for lookups an…
nevilhulspas Sep 28, 2026
f3d34c7
fix(calls): draw the Live call's quiet text in the tertiary ink
nevilhulspas Sep 29, 2026
d9c47d7
fix(live): stop microphone capture immediately on hang-up
milind-soni Oct 1, 2026
7007d9c
feat(calls): the companion relays Live calls and reports an unpaired …
nevilhulspas Sep 27, 2026
0b848fa
feat(ios): Live calls in CompanionCore: wire types, client and state …
nevilhulspas Sep 27, 2026
e21c77b
feat(ios): Live calls on the iPhone, with the chat open
nevilhulspas Sep 27, 2026
85ab756
docs(ios): Live calls in the companion README and the testing guide
nevilhulspas Sep 27, 2026
f1e3b2c
fix(ios): keep the call bar's clock in view
nevilhulspas Sep 28, 2026
ced1ecd
fix(ios): hang up a Live call on its computer before the phone leaves it
nevilhulspas Sep 28, 2026
a5e6efb
fix(ios): let only the connected computer mark this phone unpaired af…
nevilhulspas Sep 28, 2026
1a2b7af
fix(ios): scope a Live start's and a settings change's refusal to the…
nevilhulspas Sep 28, 2026
baba3fb
feat(android): Live calls in :core: wire types, the four routes and t…
nevilhulspas Sep 27, 2026
399dcd3
feat(android): Live calls on Android, with the chat open
nevilhulspas Sep 27, 2026
ac723df
docs(android): how to verify Android Live calls
nevilhulspas Sep 27, 2026
1c5c4ac
fix(android): keep the newest message and the call bar's clock in view
nevilhulspas Sep 28, 2026
5068bb7
fix(android): end a Live call that cannot take the audio from another…
nevilhulspas Sep 29, 2026
3682a54
Merge current main preserving isolated phone viewer controls
milind-soni Oct 2, 2026
da69221
Merge pull request #2158 from milind-soni/fix/pro-popup-launch-price
milind-soni Oct 2, 2026
dd4ee5d
Control plane: reclaim tunnels offline 14+ days (on)
milind-soni Oct 2, 2026
3601615
Merge pull request #2159 from milind-soni/ops/tunnel-reclaim-on
milind-soni Oct 2, 2026
fb33f0f
fix(calls): refuse settled spoken actions after hang-up
milind-soni Oct 2, 2026
93b5869
fix(ios): bind late call starts to their computer and mute before close
milind-soni Oct 2, 2026
4d960b0
fix(android): preserve call provenance and private audio routes
milind-soni Oct 2, 2026
438e1ea
docs: add a worked example for connecting the You.com search MCP server
mouse-value-add Sep 30, 2026
074be5e
docs: document the dedicated /mcp/research server for you-research
mouse-value-add Sep 30, 2026
ddf97bf
Docs: using OpenRouter, Fireworks, DeepSeek, Cline and custom providers
milind-soni Sep 30, 2026
4d4bc39
Docs: correct the Claude Code, OpenCode 2.x and server-key advice
milind-soni Sep 30, 2026
e6f095e
docs: align provider guidance with shipped ACP discovery
milind-soni Oct 2, 2026
3cd167a
test: accept Windows line endings in provider docs fixtures
milind-soni Oct 2, 2026
86f25cc
feat(server): skills library store, bot assignments, and per-bot migr…
bradhallett Sep 26, 2026
6c446c9
fix(server): accept features.skillsLibrary in stored config and corre…
bradhallett Sep 26, 2026
abab65a
fix(server): strip assignedSkills from wire payloads and assign migra…
bradhallett Sep 26, 2026
20ad914
fix(skills): read assigned library skills on the single-skill GET route
bradhallett Sep 28, 2026
38218a3
fix(skills): scope library fallback in readBotSkillFile to assigned s…
bradhallett Sep 28, 2026
2f77ec0
test(skills): reproduce assignment loss after failed migration save
milind-soni Oct 2, 2026
b84f8e0
fix(server): scope skill export fallback to assigned library skills
bradhallett Sep 27, 2026
f2cba9e
fix(server): read assigned skills from the refreshed bot records
bradhallett Sep 27, 2026
f5a9dc3
fix(server): keep per-bot enablement and durable assignments in migra…
bradhallett Sep 27, 2026
554cd79
Shadow broken private skills and record assignments before archiving
bradhallett Sep 27, 2026
37d17ab
fix(skills): preserve disabled assignments through durable migration
milind-soni Oct 2, 2026
d24004f
fix(onboarding): let the approval tip go once the approval is answered
aivsomkar Sep 28, 2026
d1a1b25
docs(verification): before and after screenshots for ui-approval-tip
aivsomkar Sep 28, 2026
3158e56
fix(onboarding): keep the engines step footer in view on short windows
aivsomkar Sep 28, 2026
351371c
fix(onboarding): show one row of dots on the reel step
aivsomkar Sep 28, 2026
bc92d5e
fix(onboarding): open the hands scene on a connected screen
aivsomkar Sep 28, 2026
ac630f7
fix(onboarding): drop the em dash from the engines step intro
aivsomkar Sep 28, 2026
d67f67e
fix(team-map): say "1 bot" in the header count
aivsomkar Sep 28, 2026
8604ef8
docs(verification): before and after screenshots for ui-onboarding-fixes
aivsomkar Sep 28, 2026
d8b32e3
fix(sidebar): close header menus on Escape and let outside clicks thr…
aivsomkar Sep 28, 2026
a682e59
fix(routines): trap focus and close on Escape in the calendar dialogs
aivsomkar Sep 28, 2026
8d96691
fix(ui): name the computer panel and option card close buttons
aivsomkar Sep 28, 2026
ee5bd74
docs(verification): before and after screenshots for ui-keyboard-escape
aivsomkar Sep 28, 2026
69ddf7b
test(sidebar): adapt dismissal coverage to current density and motion
milind-soni Oct 2, 2026
74c3238
fix(chat): highlight matched text when opening search results
jakequade Oct 1, 2026
777a7c1
fix(chat): ring searched message bubble
jakequade Oct 1, 2026
e6f6970
Show provider plan usage and per-model percent used
guylfe Sep 26, 2026
06fd8dc
Fix plan usage sign-in, model rows, and reset countdown
guylfe Sep 27, 2026
c878c86
Do not reuse the default Claude login for a custom account
guylfe Sep 27, 2026
7f719b8
test(ui): preserve current menu hooks and search navigation
milind-soni Oct 2, 2026
729aeda
Add optional pinned-bot circles and a resizable sidebar
guylfe Sep 26, 2026
0c96d7c
test(usage): cover unavailable quota and provider display
milind-soni Oct 2, 2026
7471171
fix(skills): keep auxiliary skill packages intact during migration
milind-soni Oct 2, 2026
c91725e
fix(ios): resolve chat entities without trapping on duplicate rows
bradhallett Sep 28, 2026
0b5eaf8
Add cross-bot one-way sends
jakequade Sep 28, 2026
70fd576
fix: deduplicate one-way failure notices
jakequade Sep 30, 2026
e844742
Keep unanswered question cards open across run end and restart
tranhieu204-web Sep 30, 2026
29f4d9e
feat(skills): app-level Skills surface over the library
bradhallett Sep 26, 2026
df48089
fix(skills): close library index, archive, tag and assignment gaps
bradhallett Sep 28, 2026
d806149
fix(settings): guard SkillsSection mount on the feature flag
bradhallett Sep 28, 2026
085c580
fix(skills): require source review and serialize assignments
milind-soni Oct 2, 2026
984c992
fix(skills): expose the library opt-in to settings clients
milind-soni Oct 2, 2026
e65d9ee
fix(server): fence idle release during in-flight computer calls
bradhallett Sep 27, 2026
de4cc34
fix(server): pair computer-call fences across unkeyed ACP ids and laz…
bradhallett Sep 28, 2026
7e72234
fix(test): give the cleanup fixture the pendingComputerCallStarts map
bradhallett Sep 29, 2026
91a663b
fix(backup): leave out node_modules and links in conversation work fo…
milind-soni Oct 2, 2026
23f8599
fix(evals): initialize library skills in generated scenarios
milind-soni Oct 2, 2026
e61b347
test(evals): include the empty library in the pinned scenario
milind-soni Oct 2, 2026
376bd0e
Merge pull request #2161 from milind-soni/fix/backup-skip-task-worksp…
milind-soni Oct 2, 2026
3e55827
Merge pull request #2155 from milind-soni/codex/oct2-pr-triage
milind-soni Oct 2, 2026
c845980
fix(questions): keep late room answers bound to the original asker
milind-soni Oct 2, 2026
0746781
docs: design per-bot tool selection for Pi and Grok
BitL8-ByteShort Sep 30, 2026
65a0a7e
docs: record engine coverage and tool-selection validation requirements
BitL8-ByteShort Sep 30, 2026
2fe7112
docs: plan per-bot tool-selection implementation and validation
BitL8-ByteShort Sep 30, 2026
49496d8
feat: define strict per-bot tool selection semantics
BitL8-ByteShort Sep 30, 2026
46adb83
feat: enforce tool selection at the stdio MCP boundary
BitL8-ByteShort Sep 30, 2026
b228b1a
feat: enforce selected tools on HTTP and SSE MCP servers
BitL8-ByteShort Sep 30, 2026
5139f61
feat: persist owner tool selection and snapshot it into every turn
BitL8-ByteShort Sep 30, 2026
4cf823e
feat(tools): filter shared API catalogs and execution by bot selection
BitL8-ByteShort Sep 30, 2026
aaf8a42
feat(pi): enforce native and custom MCP tool selection
BitL8-ByteShort Sep 30, 2026
d3b2f08
feat(drivers): enforce tool selection across engine boundaries
BitL8-ByteShort Sep 30, 2026
4610fb8
feat(settings): edit owner tool selection and preserve creation restr…
BitL8-ByteShort Sep 30, 2026
d48083c
test(tools): verify real local turns and preserve scoped Grok model p…
BitL8-ByteShort Oct 1, 2026
fa8e332
fix(tools): isolate Codex gates and retain inherited Grok restrictions
BitL8-ByteShort Oct 1, 2026
7236cff
docs: record reviewed Grok live-model verification
BitL8-ByteShort Oct 1, 2026
ebd718c
test: use portable cleanup for tool-selection fixtures
BitL8-ByteShort Oct 1, 2026
1687287
docs: record completed tool-selection validation and drafts
BitL8-ByteShort Oct 1, 2026
7a02874
fix(tools): address review gaps in scoped turns and verification
BitL8-ByteShort Oct 2, 2026
e5cbe5e
fix(tools): persist scope widening before activating it
BitL8-ByteShort Oct 2, 2026
e672824
docs(tools): clarify failed selection save behavior
milind-soni Oct 2, 2026
262ddb1
Merge remote-tracking branch 'origin/main' into codex/oct2-second-pr-…
milind-soni Oct 2, 2026
9d9b89c
test(markdown): verify currency and multiline TeX overlap
milind-soni Oct 2, 2026
35ae4e0
fix(computer): park the wait ceiling instead of failing the turn
bradhallett Sep 26, 2026
1742d46
fix(computer): route goal runs around a parked member
bradhallett Sep 26, 2026
596e315
feat(computer): arrival-ordered wait queue with position and estimate
bradhallett Sep 26, 2026
0f87590
test(vps): accept the queue-position infix in the wait chip assertion
bradhallett Sep 26, 2026
5551449
fix(computer): keep granting re-claims to the holder behind a waitlist
bradhallett Sep 26, 2026
d4778c5
docs(computer): park-ceiling and FIFO wait wording
bradhallett Sep 27, 2026
ac8bafb
test(wait-queue): interrupt the holder thread in the test's cleanup path
bradhallett Sep 27, 2026
fe5ff1b
fix(server): expire idle claims when checking seat availability
bradhallett Sep 28, 2026
123f5c8
fix(computer): cancel parked resumes when conversations stop
milind-soni Oct 2, 2026
5df3504
fix(skills): narrow library assignments on confirmed tightening
milind-soni Oct 2, 2026
c2d5d5c
Companion: recover a reclaimed tunnel without the user, even while "r…
milind-soni Oct 2, 2026
3c0aa51
test(settings): assert the disabled skills library default
milind-soni Oct 2, 2026
9557dbf
Merge pull request #2162 from milind-soni/fix/managed-endpoint-reclai…
milind-soni Oct 2, 2026
c9b1f1a
fix: close integration admission and delivery edge cases
milind-soni Oct 2, 2026
d7be86a
fix(computer): preserve eager Auto VM parked resumes
milind-soni Oct 2, 2026
166a81c
fix(rooms): release settled question task fences
milind-soni Oct 2, 2026
6bd97ee
fix(search): ignore whitespace-only match ranges
milind-soni Oct 2, 2026
e7fe81f
test(ui): check dialog focus across unmount and replacement
milind-soni Oct 2, 2026
59d1b0e
test: finish fixture cleanup and keep parked assertions typed
milind-soni Oct 2, 2026
c8efaf5
test: preserve room question failures during cleanup
milind-soni Oct 2, 2026
fa444f1
fix: repair second triage batch CI regressions
milind-soni Oct 2, 2026
683d359
ci: keep main to one running CI, and give releases their own CI lane
milind-soni Oct 2, 2026
caba935
Merge pull request #2168 from milind-soni/ci/bounded-main-and-release…
milind-soni Oct 2, 2026
033fd71
Merge pull request #2163 from milind-soni/codex/oct2-second-pr-triage
milind-soni Oct 2, 2026
c2f49b6
Cloud after purchase: steady plan status, calm sign-in expiry, no buy…
milind-soni Oct 2, 2026
af60bfd
Cloud review fixes: plan on Cloud homes only, Admin-only sign-in end,…
milind-soni Oct 2, 2026
5d241e3
feat(approvals): a Chief of Staff's level flows down to the threads i…
milind-soni Oct 2, 2026
607bb5b
Merge pull request #2175 from milind-soni/feat/chief-level-flows-down
milind-soni Oct 2, 2026
700a4f3
Merge latest main into reviewed Live calls candidate
milind-soni Oct 2, 2026
a635170
Merge pull request #2172 from milind-soni/fix/cloud-purchase-flow-app
milind-soni Oct 2, 2026
68db3cd
fix(live): reuse question recovery and guard media lifecycle
milind-soni Oct 2, 2026
12f5a3f
Merge remote-tracking branch 'origin/main' into codex/oct2-second-mobile
milind-soni Oct 2, 2026
ac927af
test(live): retain delayed key-save renderer regression
milind-soni Oct 2, 2026
f74125c
test(android): await rendered Live voice-preview guard
milind-soni Oct 2, 2026
b9ab44b
feat(desktop): Simple mode by default, an Advanced mode switch, and t…
aivsomkar Oct 2, 2026
c684794
test(live): retain credential boundary and allow Windows suite growth
milind-soni Oct 2, 2026
f9b4edb
fix(ios): reuse owned audio player for voice previews
milind-soni Oct 2, 2026
f613c4a
Merge remote-tracking branch 'origin/main' into codex/oct2-second-mobile
milind-soni Oct 2, 2026
20d0d90
test(live): keep composer modes and voice setup compatible
milind-soni Oct 2, 2026
be51217
Merge pull request #2160 from milind-soni/codex/oct2-second-mobile
milind-soni Oct 2, 2026
a1baea6
fix(ci): stabilize release verification on macOS and Windows (#2179)
milind-soni Oct 2, 2026
b7a7035
chore(release): bump version to 0.1.94 (#2182)
github-actions[bot] Oct 2, 2026
85c10b4
fix(vps): accept operator resource and restart settings (#2180)
milind-soni Oct 2, 2026
ff01be8
Connect your phone: one menu entry, to this computer or your Cloud (#…
milind-soni Oct 2, 2026
f65c69a
Merge upstream OpenMausBot 0.1.94 (ff01be8a) into Sagax
Oct 3, 2026
e4cd737
Gate upstream Live calls behind SAGAX_LIVE_CALLS (off by default)
Oct 3, 2026
76d2f7a
Merge origin/main (#117) into the upstream sync
Oct 3, 2026
2b104d6
fix(merge): question cards stay answerable, Claude keeps per-tool age…
Oct 3, 2026
4e4a628
test(cloud-personal): no reachable container daemon, so a prepared Lo…
Oct 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
45 changes: 31 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ permissions:
contents: read

concurrency:
# A new PR push supersedes that PR's older checks. Every main commit gets
# its own group and finishes: when main pushes cancelled each other, no
# main run completed for a day and a release shipped with no verdict
# (release.yml now waits for this workflow's `CI` check on its commit).
# Merge-queue runs stay protected on their own refs.
group: ci-${{ github.event_name == 'merge_group' && github.event.merge_group.head_ref || github.event_name == 'push' && github.sha || github.ref }}
# A new PR push supersedes that PR's older checks. Main keeps one run going
# and one waiting: each merge replaces the waiting run, so a burst of
# merges costs two full runs, not one per merge (fifteen queued behind each
# other in October 2026). The running one always finishes. A release runs
# CI on its own commit in a release-ci/v<version> lane when main's run for
# it was replaced (scripts/release-ci.mjs). Merge-queue runs stay on their
# own refs.
group: ci-${{ github.event_name == 'merge_group' && github.event.merge_group.head_ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
Expand Down Expand Up @@ -91,10 +93,9 @@ jobs:
shard: [1, 2, 3, 4]
runs-on: ${{ matrix.os }}
# vitest splits by file count, not by duration, so the shard holding the
# heavy e2e files ran 13 minutes on macOS/Ubuntu and 19 on Windows with
# three shards; four keeps the heaviest near 10 and 15. The cap catches a
# hang, not normal growth: raise it with the suite, or add a shard.
timeout-minutes: ${{ matrix.os == 'windows-latest' && 25 || 20 }}
# heavy e2e files exceeds 20 minutes on macOS and 25 on Windows with
# tests still progressing. The cap catches a hang, not normal suite growth.
timeout-minutes: ${{ matrix.os == 'ubuntu-latest' && 20 || 35 }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@ff378ebe6b225b0680b81c1ad4498ae0d1d3a5e3 # v6.0.10
Expand Down Expand Up @@ -352,16 +353,32 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# A repo without the secret (a fork, or before the token is created)
# skips the deploy with a warning instead of failing every main push.
- id: token
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: |
if [ -n "$CLOUDFLARE_API_TOKEN" ]; then
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "::warning title=Composio broker not deployed::CLOUDFLARE_API_TOKEN is not set, so the broker Worker was not deployed."
fi
- if: steps.token.outputs.present == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: pnpm/action-setup@ff378ebe6b225b0680b81c1ad4498ae0d1d3a5e3 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
- if: steps.token.outputs.present == 'true'
uses: pnpm/action-setup@ff378ebe6b225b0680b81c1ad4498ae0d1d3a5e3 # v6.0.10
- if: steps.token.outputs.present == 'true'
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- if: steps.token.outputs.present == 'true'
run: pnpm install --frozen-lockfile
- name: Deploy the composio broker Worker via Wrangler
if: steps.token.outputs.present == 'true'
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Expand Down
50 changes: 20 additions & 30 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,48 +105,38 @@ jobs:

ci:
# Ship only a commit whose own `CI` check (ci.yml's gate) passed. The wait
# overlaps the platform builds, so a green commit costs no time; a red,
# cancelled or missing check stops the release before any draft exists.
# 0.1.79 and 0.1.90 shipped commits whose main runs never finished.
# overlaps the platform builds, so a green commit costs no time; a red or
# missing verdict stops the release before any draft exists. Main keeps
# one CI run going and one waiting, so a burst of merges can replace the
# release commit's own run: scripts/release-ci.mjs then runs CI on the
# commit in its own lane (a release-ci/v<version> branch no merge can
# touch) and deletes that branch afterwards.
name: CI passed on the release commit
needs: prepare
if: needs.prepare.outputs.should_release == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 180
permissions:
actions: read
# read CI runs and start CI in the release lane
actions: write
# create and delete the release-ci/v<version> branch
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
sparse-checkout: scripts/release-ci.mjs
sparse-checkout-cone-mode: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Wait for the CI check on the pinned commit
if: ${{ !inputs.ship_without_ci }}
env:
GH_TOKEN: ${{ github.token }}
SHA: ${{ needs.prepare.outputs.sha }}
run: |
# push = a merge on main; merge_group = the queue's commit that
# becomes main; workflow_dispatch = a manual CI run of this commit.
# A pull_request run tests a merge ref, not this exact commit.
started=$SECONDS
while :; do
run=$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/ci.yml/runs?head_sha=$SHA&per_page=50" \
--jq '[.workflow_runs[] | select(.event == "push" or .event == "merge_group" or .event == "workflow_dispatch")] | sort_by(.created_at) | last | select(. != null) | "\(.id) \(.status)"')
if [ -z "$run" ]; then
if [ $((SECONDS - started)) -gt 900 ]; then
echo "::error::No CI run exists for $SHA. Run CI on it (Actions → CI → Run workflow on main while main is at this commit), then re-run this job."
exit 1
fi
else
set -- $run
if [ "$2" = completed ]; then
gate=$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$1/jobs?per_page=100" --jq '.jobs[] | select(.name == "CI") | .conclusion')
echo "CI run $1 on $SHA: gate ${gate:-missing}"
[ "$gate" = success ] && exit 0
echo "::error::The CI check on $SHA did not pass (${gate:-no gate job}). Fix main or re-run CI ($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$1), then re-run this job."
exit 1
fi
echo "CI run $1 on $SHA is $2; waiting"
fi
sleep 60
done
VERSION: ${{ needs.prepare.outputs.version }}
run: node scripts/release-ci.mjs

mac:
name: macOS arm64 + x64 (sign, notarize, staple)
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1118,8 +1118,8 @@ it after an upstream merge instead of renaming by hand.

## Upstream sync

Last sync: 2026-10-01, upstream `milind-soni/OpenMausBot` main at
`4ed952aa` (0.1.92) merged into Sagax; `baseVersion` follows it. To repeat:
Last sync: 2026-10-03, upstream `milind-soni/OpenMausBot` main at
`ff01be8a` (0.1.94) merged into Sagax; `baseVersion` follows it. To repeat:

- Keep the `upstream` remote fetch-only (`git remote set-url --push
upstream no_push`). Never push, open a pull request or file an issue
Expand Down
20 changes: 20 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ directory was removed and none of its source or license check is included;
files across the tree were modified. These changes are licensed under the
PolyForm Noncommercial License 1.0.0, not the Apache License 2.0.

The Android companion app bundles a prebuilt WebRTC library for Live calls
(io.github.webrtc-sdk:android), built from the WebRTC project, Copyright (c)
2011, The WebRTC project authors, used under the BSD 3-Clause License. WebRTC
includes third-party components under their own licenses, listed with the
library.

Selected provider icon paths are from Simple Icons, used under CC0 1.0.
The Cohere provider icon path is from Lobe Icons, Copyright 2023 LobeHub,
used under the MIT License.

The OpenMausBot name and mascot are trademarks of Milind Soni. This product
does not use those marks as its name. Apache License 2.0 section 6 does not
grant trademark rights.
Expand All @@ -31,3 +41,13 @@ quantized to int8 by Pulsatrix Technologies inc. Licenses are in
third_party/onnxruntime-web, third_party/silero-vad, third_party/3d-speaker
and third_party/sherpa-onnx; details and checksums in
src/lib/voice-mode/models/NOTICE.txt.

The browser desktop viewer bundles the unmodified noVNC 1.7.0 library,
Copyright the noVNC authors, under MPL-2.0 and its accompanying licenses.
Attribution, license terms and the corresponding source location are in
public/novnc-NOTICE.txt (novnc-NOTICE.txt in the built UI).

The iOS companion app bundles a prebuilt WebRTC library for Live calls
(stasel/WebRTC), built from the WebRTC project, Copyright (c) 2011, The WebRTC
project authors, used under the BSD 3-Clause License. WebRTC includes
third-party components under their own licenses, listed with the library.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,13 @@ server"). For a solo server on a VPS, see

Most settings live in the app (Settings). The server also reads environment
variables. Names start with `SAGAX_`; the old `OMB_*`, `OPENMAUSBOT_*` and
`OPENMAUS_*` names are still read and moved onto `SAGAX_*` at start, for one
`SAGAX_*` names are still read and moved onto `SAGAX_*` at start, for one
release. Some deployment files (`.env.example`, `compose.yaml`, `deploy/`)
still use the `OMB_*` names on purpose.
still use the `SAGAX_*` names on purpose.

| Variable | Default | What it does |
|---|---|---|
| `SAGAX_DATA_DIR` | `~/.sagax` | Where bots, conversations and settings live. An old `~/.openmausbot` moves there on first start. |
| `SAGAX_DATA_DIR` | `~/.sagax` | Where bots, conversations and settings live. An old `~/.sagax` moves there on first start. |
| `SAGAX_PORT` | `8799` | Port of the Sagax server (127.0.0.1). |
| `SAGAX_WEBHOOK_PORT` | `SAGAX_PORT` + 1 | Port of the webhook receiver for routines. |
| `SAGAX_PUBLIC_URL` | none | The server's public address, used in links and sign-in. |
Expand Down Expand Up @@ -362,11 +362,11 @@ bundling it, needs a license from Pulsatrix Technologies inc. See

Sagax is based on OpenMausBot, Copyright 2026 Milind Soni and OpenMausBot
contributors, under the [Apache License 2.0](LICENSE-APACHE). The parts that
come unchanged from OpenMausBot stay under Apache 2.0; the changes and
come unchanged from Sagax stay under Apache 2.0; the changes and
additions made by Pulsatrix Technologies inc. are under the PolyForm
Noncommercial License only. Copies published before 2026-10-02 under Apache
2.0 keep that license. The OpenMausBot notices are kept in [NOTICE](NOTICE).
The OpenMausBot name and mascot are trademarks of Milind Soni and are not this
2.0 keep that license. The Sagax notices are kept in [NOTICE](NOTICE).
The Sagax name and mascot are trademarks of Milind Soni and are not this
product's name. The source-available `enterprise/` directory of the original
project is not part of this tree.

Expand Down
6 changes: 6 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ kotlin {

dependencies {
implementation(project(":core"))
// Live calls: a prebuilt libwebrtc (BSD-3), the phone's own audio path to
// OpenAI GPT-Live. Only `audio/WebRtcLiveCallTransport.kt` may import
// `org.webrtc` (LiveCallNativeIsolationTest pins it), so the JVM suite
// never loads libjingle_peerconnection_so. Measured 2026-09-25: +53 MB on
// the universal debug APK (four ABIs); +12 MB on an arm64 phone.
implementation("io.github.webrtc-sdk:android:150.7871.01")

val composeBom = platform("androidx.compose:compose-bom:2025.10.01")
implementation(composeBom)
Expand Down
13 changes: 10 additions & 3 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@
android:name="android.hardware.camera.autofocus"
android:required="false" />
<!--
Composer dictation only. Asked at the mic button, never at startup — the
app is fully usable without it, and the IME's own mic is not a substitute
for this lifecycle (frozen base, partials, teardown).
Composer dictation and Live calls. Asked at the mic button or when a
call starts, never at startup — the app is fully usable without it, and
the IME's own mic is not a substitute for this lifecycle (frozen base,
partials, teardown).
-->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--
Live calls route the voice to the speaker or the earpiece through
AudioManager (MODE_IN_COMMUNICATION, setCommunicationDevice). A normal
permission, granted at install; the WebRTC AAR declares none itself.
-->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature
android:name="android.hardware.microphone"
android:required="false" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ class MainActivity : ComponentActivity() {
avatars = app.avatars,
voicePreview = app.voicePreview,
voiceNotes = app.voiceNotes,
liveCalls = app.liveCalls,
dictation = dictation,
chatDrafts = chatDrafts,
requestPermissions = { permissions -> permissionRequests.request(permissions) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.openmausbot.companion

import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import com.openmausbot.companion.audio.LiveCallManager
import com.openmausbot.companion.audio.VoicePreviewPlayer
import com.openmausbot.companion.audio.VoiceNotePlayer
import com.openmausbot.companion.avatar.AvatarImageStore
Expand Down Expand Up @@ -57,6 +58,8 @@ class OpenMausApp : Application() {
private set
lateinit var voiceNotes: VoiceNotePlayer
private set
lateinit var liveCalls: LiveCallManager
private set
lateinit var linger: SessionLingerController
private set
lateinit var shareInbox: ShareInbox
Expand Down Expand Up @@ -89,8 +92,18 @@ class OpenMausApp : Application() {
notificationSink = notifications,
)
avatars = AvatarImageStore(fetch = session::avatarData)
voicePreview = VoicePreviewPlayer(this)
voiceNotes = VoiceNotePlayer(this)
// Live calls hold media across screens, and end when the process
// leaves the foreground — not when a rotation recreates the Activity —
// so the manager is app-scoped and observes the process lifecycle, as
// the linger controller below does.
liveCalls = LiveCallManager(this, session, appScope)
ProcessLifecycleOwner.get().lifecycle.addObserver(liveCalls)
// A voice note or a voice preview asks for the audio focus, and the
// call ends when it loses that focus: both players refuse while the
// call holds the audio, whatever asks them to play.
val liveCallHoldsAudio = { liveCalls.state.value.holdsMedia }
voicePreview = VoicePreviewPlayer(this, liveCallHoldsAudio = liveCallHoldsAudio)
voiceNotes = VoiceNotePlayer(this, liveCallHoldsAudio = liveCallHoldsAudio)

// iOS resets the avatar cache inside signOut. Observe Unpaired here so
// the platform cache cannot outlive the pairing that minted its URLs.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.openmausbot.companion.audio

import com.openmausbot.companion.core.LiveCallStart
import com.openmausbot.companion.core.LiveCallState
import com.openmausbot.companion.core.Session
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map

/** The two calls and the reports a Live call needs from the computer. [Session] in production, a fake in tests. */
interface LiveCallApi {
suspend fun start(botId: String, threadId: String, sdp: String): LiveCallStart

suspend fun end(callId: String)

/** The computer's current call as the `live.call` frames and `GET /api/live/call` report it. */
val serverCall: Flow<LiveCallState?>

/**
* Which computer this phone follows, and whether its pairing still
* stands. A call belongs to the computer it started on: losing the
* pairing hangs it up, and switching computers leaves it behind.
*/
val link: Flow<LiveCallLink>
}

/**
* [computerId] is the paired computer's connection id, null while none is
* selected; [signedIn] is false once the pairing is gone or its token is
* refused (`Session.Status.Unpaired` or `Unauthorized`).
*/
data class LiveCallLink(val computerId: String?, val signedIn: Boolean)

internal class SessionLiveCallApi(private val session: Session) : LiveCallApi {
override suspend fun start(botId: String, threadId: String, sdp: String): LiveCallStart =
session.startLiveCall(botId, threadId, sdp)

override suspend fun end(callId: String) {
session.endLiveCall(callId)
}

override val serverCall: Flow<LiveCallState?> = session.state.map { it.liveCall }.distinctUntilChanged()

override val link: Flow<LiveCallLink> = combine(session.connection, session.status) { connection, status ->
LiveCallLink(
computerId = connection?.id,
signedIn = status !is Session.Status.Unpaired && status !is Session.Status.Unauthorized,
)
}.distinctUntilChanged()
}
Loading
Loading