Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b455e0f
test(server): gate late VM completion in fixture (#2169)
jakequade Oct 3, 2026
9cf8f41
fix(chat): suppress empty digest receipts on desktop and mobile (#2164)
aivsomkar Oct 3, 2026
a151ffe
feat(ios): add a Jump to latest pill while reading scrollback (#1876)
AdityaUmale Oct 3, 2026
0478e04
fix(ios): keep chats responsive under busy fleet traffic (#2189)
milind-soni Oct 3, 2026
2ccf234
fix: restart desktop to complete pending workspace restore (#2201)
milind-soni Oct 3, 2026
b86e4cf
fix(mobile): stop counting expired proposals as waiting on you (MOCA-…
aivsomkar Oct 3, 2026
d7e5463
Integrate reviewed mobile steering, browser, Chinese and trust featur…
milind-soni Oct 3, 2026
6581b2c
test(decisions): export the days the rows were written on, not today …
zicochaos Oct 3, 2026
f129fa9
feat(desktop): liquid-glass sidebar head and foot, and chat headers (…
aivsomkar Oct 3, 2026
ad360d9
Repair integrated mobile/trust CI and preserve source history (#2217)
milind-soni Oct 3, 2026
790d361
Hosted desktop: keep the bot's own engine, and a failed place never b…
milind-soni Oct 3, 2026
bcffb6e
Stop re-reading config.json on every broadcast frame (#2215)
milind-soni Oct 3, 2026
f7a64a4
perf(chat): define the markdown element renderers once so messages st…
milind-soni Oct 3, 2026
e74fb7b
fix(routines): preserve detailed runtime errors (#2214)
buttonsjasper360-lang Oct 3, 2026
23b3cfb
fix: remove the status capsule nothing writes from the Chief prompt (…
milind-soni Oct 3, 2026
8657df5
chore(renderer): delete orphaned renderer files (#2210)
milind-soni Oct 3, 2026
003656d
Keep SQLite's active leaf equal to memory's after a late screenshot (…
milind-soni Oct 3, 2026
ec1fa4b
Delegation: follow-ups continue the same thread everywhere (no duplic…
milind-soni Oct 3, 2026
ea361ac
Memory: never lose a note at the size cap (#2199)
milind-soni Oct 3, 2026
4bd39d8
Delete dead server code, stale names and env knobs nobody sets (#2219)
milind-soni Oct 3, 2026
15bb55d
HC-01 review fixes: derive usesCloudComputer, drop refreshSystemPromp…
milind-soni Oct 3, 2026
809accc
fix(server): don't append the couldn't-deliver message for an already…
santhiprakash Oct 3, 2026
bc7503d
chore(renderer): drop what only the deleted tray and Tools menu used …
milind-soni Oct 3, 2026
e5c656c
refactor(chat): put the markdown plugin arrays back inline (#2223)
milind-soni Oct 3, 2026
5b622fa
fix(mobile): fold Updates lines by the Activity setting and show webh…
aivsomkar Oct 3, 2026
8495ac8
Keep pinned-circle threads reachable (#2205)
guylfe Oct 3, 2026
2632b30
test: wait for delta-context fixture readiness (#2198)
jakequade Oct 3, 2026
562e7dd
fix(mobile): put Stop in the composer while a turn runs (MOCA-148) (#…
aivsomkar Oct 3, 2026
5e8b252
chore(release): bump version to 0.1.95 (#2228)
milind-soni Oct 3, 2026
04a8bef
Hosted desktop review fixes: Auto never reaches a Boat, one cloud rul…
milind-soni Oct 3, 2026
691fb4a
Merge upstream OpenMausBot 0.1.95 (04a8bef8) into Sagax
Oct 3, 2026
123793e
test(merge): adapt tests to kept Sagax behavior (settings slot, no gl…
Oct 3, 2026
deadceb
Merge origin/main (#110, #111, #119) into the upstream sync
Oct 3, 2026
94fda78
Merge origin/main (#121) into the upstream sync
Oct 3, 2026
760668d
test(merge): Primary Bot roster wording, upstream trust-controls e2e …
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
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,7 @@ it after an upstream merge instead of renaming by hand.
## Upstream sync

Last sync: 2026-10-03, upstream `milind-soni/OpenMausBot` main at
`ff01be8a` (0.1.94) merged into Sagax; `baseVersion` follows it. To repeat:
`04a8bef8` (0.1.95) 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
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:localeConfig="@xml/locales_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.openmausbot.companion.ui

import androidx.compose.ui.res.stringResource

import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
Expand Down Expand Up @@ -221,10 +223,10 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
onClick = onDismiss,
modifier = Modifier.align(Alignment.CenterStart),
) {
Text("Done")
Text(stringResource(R.string.mobile_done_e9b450d1))
}
Text(
text = "Bot settings",
text = stringResource(R.string.mobile_bot_settings_7092a294),
fontSize = 17.sp,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.align(Alignment.Center),
Expand Down Expand Up @@ -280,7 +282,7 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
modifier = Modifier.fillMaxWidth().heightIn(min = MIN_TOUCH_TARGET),
verticalAlignment = Alignment.CenterVertically,
) {
Text(ModelRules.LOADING, fontSize = 15.sp, modifier = Modifier.weight(1f))
Text(localizedMobileCopy(ModelRules.LOADING), fontSize = 15.sp, modifier = Modifier.weight(1f))
CircularProgressIndicator(modifier = Modifier.size(20.dp))
}
} else if (instanceChoices.isEmpty()) {
Expand Down Expand Up @@ -309,7 +311,7 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
}
}
ChoicePicker(
label = "Provider",
label = stringResource(R.string.mobile_provider_7ceee3f3),
choices = providerRows,
selected = selectedInstanceId,
onSelect = { id ->
Expand All @@ -318,7 +320,7 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
},
)
ChoicePicker(
label = "Model",
label = stringResource(R.string.mobile_model_68c2cc7f),
choices = ModelRules.modelChoices(selectedInstance, selectedModelId).map {
VoiceChoice(id = it.id, label = it.label, detail = null, enabled = true)
},
Expand All @@ -329,7 +331,7 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
val effortLevels = ModelRules.effortLevels(selectedInstance)
if (effortLevels.isNotEmpty()) {
ChoicePicker(
label = "Reasoning effort",
label = stringResource(R.string.mobile_reasoning_effort_cd32c0f5),
choices = buildList {
add(VoiceChoice(id = "", label = ModelRules.DEFAULT_EFFORT_LABEL, detail = null, enabled = true))
effortLevels.forEach {
Expand Down Expand Up @@ -381,7 +383,7 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
bot = current,
size = 112.dp,
state = MausState.HAPPY,
contentDescription = "${current.name} avatar",
contentDescription = stringResource(R.string.mobile_current_name_avatar_de638954, current.name),
)
}

Expand All @@ -397,7 +399,7 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
count = AvatarCrop.entries.size,
),
) {
Text(ProfileRules.cropLabel(option))
Text(localizedMobileCopy(ProfileRules.cropLabel(option)))
}
}
}
Expand Down Expand Up @@ -447,7 +449,7 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
OutlinedTextField(
value = prompt,
onValueChange = { prompt = it },
label = { Text("Art direction") },
label = { Text(stringResource(R.string.mobile_art_direction_52d878a2)) },
minLines = 2,
maxLines = 5,
modifier = Modifier.fillMaxWidth(),
Expand Down Expand Up @@ -496,7 +498,7 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
OutlinedTextField(
value = form.name,
onValueChange = { form = form.copy(name = it) },
label = { Text("Name") },
label = { Text(stringResource(R.string.mobile_name_709a2322)) },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Words,
Expand All @@ -506,20 +508,20 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
OutlinedTextField(
value = form.title,
onValueChange = { form = form.copy(title = it) },
label = { Text("Title") },
label = { Text(stringResource(R.string.mobile_title_768e0c1c)) },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
)
OutlinedTextField(
value = form.description,
onValueChange = { form = form.copy(description = it) },
label = { Text("What this agent does") },
label = { Text(stringResource(R.string.mobile_what_this_agent_does_82aa1b56)) },
minLines = 3,
maxLines = 8,
modifier = Modifier.fillMaxWidth(),
)
SwitchRow(
label = "Agent notifications",
label = stringResource(R.string.mobile_agent_notifications_a3bd8bd0),
checked = form.notifications,
onCheckedChange = { form = form.copy(notifications = it) },
)
Expand Down Expand Up @@ -558,13 +560,13 @@ internal fun AgentProfileSheet(bot: Bot, onDismiss: () -> Unit, onOpenOverview:
},
) {
ChoicePicker(
label = "Voice",
label = stringResource(R.string.mobile_voice_3091c844),
choices = ProfileRules.voiceChoices(config, voices, form.voice),
selected = form.voice,
onSelect = { form = form.copy(voice = it) },
)
SwitchRow(
label = "Speak replies",
label = stringResource(R.string.mobile_speak_replies_90b05ae0),
checked = form.speakReplies,
enabled = ProfileRules.selectedVoiceCanSpeak(config, form.voice),
onCheckedChange = { form = form.copy(speakReplies = it) },
Expand Down Expand Up @@ -658,7 +660,7 @@ internal fun ChoicePicker(
enabled: Boolean = true,
) {
var expanded by remember { mutableStateOf(false) }
val selectedLabel = choices.firstOrNull { it.id == selected }?.label.orEmpty()
val selectedLabel = localizedMobileCopy(choices.firstOrNull { it.id == selected }?.label.orEmpty())
ExposedDropdownMenuBox(
expanded = expanded && enabled,
onExpandedChange = { if (enabled) expanded = it },
Expand All @@ -669,7 +671,7 @@ internal fun ChoicePicker(
onValueChange = {},
readOnly = true,
enabled = enabled,
label = { Text(label) },
label = { Text(localizedMobileCopy(label)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
modifier = Modifier
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable)
Expand All @@ -680,9 +682,9 @@ internal fun ChoicePicker(
DropdownMenuItem(
text = {
Column {
Text(choice.label)
Text(localizedMobileCopy(choice.label))
choice.detail?.let {
Text(it, fontSize = 13.sp, color = secondaryTint)
Text(localizedMobileCopy(it), fontSize = 13.sp, color = secondaryTint)
}
}
},
Expand Down Expand Up @@ -721,7 +723,7 @@ internal fun VoiceSection(
val copy = ProfileRules.voiceCopy(config)
FormSection(header = "Voice", footer = copy.footer) {
ChoicePicker(
label = "Voice engine",
label = stringResource(R.string.mobile_voice_engine_3b4d8dea),
choices = ProfileRules.providerChoices(),
selected = (config?.voiceProvider ?: VoiceProvider.ELEVENLABS).wire,
onSelect = { next -> onSwitchEngine(VoiceProvider.fromWire(next)) },
Expand Down Expand Up @@ -751,15 +753,15 @@ internal fun FormSection(
) {
header?.let {
Text(
text = it.uppercase(),
text = localizedMobileCopy(it).uppercase(),
fontSize = 12.sp,
fontWeight = FontWeight.SemiBold,
color = secondaryTint,
)
}
HorizontalDivider()
content()
footer?.let { Text(text = it, fontSize = 13.sp, color = secondaryTint) }
footer?.let { Text(text = localizedMobileCopy(it), fontSize = 13.sp, color = secondaryTint) }
}
}

Expand Down Expand Up @@ -800,7 +802,7 @@ internal fun ActionRow(
modifier = Modifier.size(20.dp),
)
}
Text(text = text, fontSize = 15.sp, color = tint)
Text(text = localizedMobileCopy(text), fontSize = 15.sp, color = tint)
}
}

Expand Down Expand Up @@ -862,7 +864,7 @@ internal fun IconNote(
modifier = Modifier.size(18.dp),
)
}
Text(text = text, fontSize = 13.sp, color = resolved)
Text(text = localizedMobileCopy(text), fontSize = 13.sp, color = resolved)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.res.stringResource
import com.openmausbot.companion.R
import com.openmausbot.companion.core.Bot
import com.openmausbot.companion.core.BotTask
import com.openmausbot.companion.core.CompanionState
Expand Down Expand Up @@ -94,6 +96,12 @@ private fun attentionRank(entry: AttentionEntry): Int = when {
/** Selection only: the row jumps; menus and management stay in the sheet. */
@Composable
internal fun AttentionRow(entry: AttentionEntry, onOpen: () -> Unit) {
val status = when {
entry.task.activity == "waiting-on-you" -> stringResource(R.string.mobile_waiting_on_you_edab5b72)
entry.task.busy == true || entry.task.activity == "working" -> stringResource(R.string.mobile_thread_working)
entry.queued -> stringResource(R.string.mobile_queued_6a599877)
else -> stringResource(R.string.mobile_unread_07b032b5)
}
Row(
modifier = Modifier
.fillMaxWidth()
Expand All @@ -115,7 +123,7 @@ internal fun AttentionRow(entry: AttentionEntry, onOpen: () -> Unit) {
overflow = TextOverflow.Ellipsis,
)
Text(
text = "${entry.botName} · ${entry.statusLine()}",
text = "${entry.botName} · $status",
fontSize = 12.sp,
color = secondaryTint,
maxLines = 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand Down Expand Up @@ -77,7 +78,7 @@ fun BotOverviewScreen(botId: String, onBack: () -> Unit) {
}

val name = overview?.who?.name ?: state.bot(botId)?.name.orEmpty()
val title = OverviewRules.title(name)
val title = stringResource(R.string.mobile_overview_title, name)

Column(modifier = Modifier.fillMaxSize()) {
Row(
Expand Down Expand Up @@ -114,7 +115,7 @@ fun BotOverviewScreen(botId: String, onBack: () -> Unit) {
}
current != null -> OverviewBody(current)
failed -> FormSection(header = null) {
Text(OverviewRules.FAILED, color = secondaryTint)
Text(localizedMobileCopy(OverviewRules.FAILED), color = secondaryTint)
}
}
}
Expand All @@ -139,7 +140,7 @@ private fun OverviewBody(overview: BotOverview) {

FormSection(header = OverviewRules.DOES) {
if (overview.does.isEmpty()) {
Text(OverviewRules.EMPTY_DOES, color = secondaryTint)
Text(localizedMobileCopy(OverviewRules.EMPTY_DOES), color = secondaryTint)
} else {
overview.does.forEach { line ->
IconNote(text = line, painter = R.drawable.ic_schedule)
Expand All @@ -164,7 +165,7 @@ private fun OverviewBody(overview: BotOverview) {

FormSection(header = OverviewRules.RECENT) {
if (overview.recent.isEmpty()) {
Text(OverviewRules.EMPTY_RECENT, color = secondaryTint)
Text(localizedMobileCopy(OverviewRules.EMPTY_RECENT), color = secondaryTint)
} else {
overview.recent.forEach { entry ->
Column {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.openmausbot.companion.ui

import androidx.compose.ui.res.stringResource

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand All @@ -21,6 +23,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.openmausbot.companion.R
import com.openmausbot.companion.core.BotTask
import com.openmausbot.companion.core.bylineLabel
import com.openmausbot.companion.core.displayTitle
Expand Down Expand Up @@ -53,13 +56,20 @@ internal fun BotThreadRow(
* it, so the row derives it here rather than parsing activity. */
queued: Boolean = false,
) {
val runtime = task.runtimeLabel(queued)
val runtimeLabel = task.runtimeLabel(queued)
val runtime = when (runtimeLabel) {
"Waiting on you" -> stringResource(R.string.mobile_waiting_on_you_edab5b72)
"Waiting on teammate" -> stringResource(R.string.mobile_waiting_on_teammate_158d309b)
"Working" -> stringResource(R.string.mobile_thread_working)
"Queued" -> stringResource(R.string.mobile_queued_6a599877)
else -> null
}
val snoozed = task.isSnoozed(now)
val dimmed = (task.isClosed || task.isArchived || snoozed) && runtime == null && task.unread != true
val foldedState = when {
task.isClosed -> "Closed"
task.isArchived -> "Archived"
snoozed -> "Snoozed"
task.isClosed -> stringResource(R.string.mobile_thread_closed)
task.isArchived -> stringResource(R.string.mobile_archived_eddc813f)
snoozed -> stringResource(R.string.mobile_thread_snoozed)
else -> null
}
Row(
Expand Down Expand Up @@ -89,17 +99,16 @@ internal fun BotThreadRow(
text = runtime,
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = when (runtime) {
color = when (runtimeLabel) {
"Waiting on you" -> MaterialTheme.colorScheme.error
"Waiting on teammate" -> secondaryTint
"Queued" -> secondaryTint
"Waiting on teammate", "Queued" -> secondaryTint
else -> MaterialTheme.colorScheme.primary
},
)
}
if (task.unread == true) {
Text(
"Unread",
stringResource(R.string.mobile_unread_07b032b5),
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.primary,
Expand All @@ -109,7 +118,7 @@ internal fun BotThreadRow(
}
val byline = listOfNotNull(
RelativeStamp.updated(task.listStamp).takeIf { it.isNotEmpty() },
"Pinned".takeIf { task.pinned == true },
stringResource(R.string.mobile_roster_pinned).takeIf { task.pinned == true },
task.bylineLabel(now),
).joinToString(" · ")
if (byline.isNotEmpty()) {
Expand Down
Loading
Loading