@@ -70,7 +70,6 @@ import kotlinx.serialization.json.JsonArray
7070import kotlinx.serialization.json.JsonObject
7171import kotlinx.serialization.json.JsonPrimitive
7272import kotlinx.serialization.json.buildJsonObject
73- import java.time.Instant
7473import java.util.UUID
7574import java.util.concurrent.atomic.AtomicLong
7675
@@ -323,8 +322,6 @@ class NodeRuntime(
323322 val cronJobs: StateFlow <List <GatewayCronJobSummary >> = _cronJobs .asStateFlow()
324323 private val _cronRefreshing = MutableStateFlow (false )
325324 val cronRefreshing: StateFlow <Boolean > = _cronRefreshing .asStateFlow()
326- private val _cronSaving = MutableStateFlow (false )
327- val cronSaving: StateFlow <Boolean > = _cronSaving .asStateFlow()
328325 private val _cronErrorText = MutableStateFlow <String ?>(null )
329326 val cronErrorText: StateFlow <String ?> = _cronErrorText .asStateFlow()
330327 private val _usageSummary = MutableStateFlow (GatewayUsageSummary (updatedAtMs = null , providers = emptyList()))
@@ -413,6 +410,8 @@ class NodeRuntime(
413410 _gatewayVersion .value = null
414411 _gatewayUpdateAvailable .value = null
415412 _seamColorArgb .value = DEFAULT_SEAM_COLOR_ARGB
413+ _gatewayDefaultAgentId .value = null
414+ _gatewayAgents .value = emptyList()
416415 _modelCatalog .value = emptyList()
417416 _modelAuthProviders .value = emptyList()
418417 _cronStatus .value = GatewayCronStatus (enabled = false , jobs = 0 , nextWakeAtMs = null )
@@ -722,22 +721,6 @@ class NodeRuntime(
722721 }
723722 }
724723
725- fun createCronJob (
726- name : String ,
727- message : String ,
728- scheduleKind : String ,
729- scheduleValue : String ,
730- ) {
731- scope.launch {
732- createCronJobOnGateway(
733- name = name,
734- message = message,
735- scheduleKind = scheduleKind,
736- scheduleValue = scheduleValue,
737- )
738- }
739- }
740-
741724 fun refreshUsage () {
742725 scope.launch {
743726 refreshUsageFromGateway()
@@ -1102,6 +1085,12 @@ class NodeRuntime(
11021085 setVoiceCaptureMode(if (value) VoiceCaptureMode .ManualMic else VoiceCaptureMode .Off )
11031086 }
11041087
1088+ fun cancelMicCapture () {
1089+ micCapture.cancelMicCapture()
1090+ setVoiceCaptureMode(VoiceCaptureMode .Off , persistManualMic = false )
1091+ prefs.setVoiceMicEnabled(false )
1092+ }
1093+
11051094 fun setTalkModeEnabled (value : Boolean ) {
11061095 setVoiceCaptureMode(if (value) VoiceCaptureMode .TalkMode else VoiceCaptureMode .Off )
11071096 }
@@ -1763,119 +1752,6 @@ class NodeRuntime(
17631752 }
17641753 }
17651754
1766- private suspend fun createCronJobOnGateway (
1767- name : String ,
1768- message : String ,
1769- scheduleKind : String ,
1770- scheduleValue : String ,
1771- ) {
1772- _cronSaving .value = true
1773- _cronErrorText .value = null
1774- if (! operatorConnected) {
1775- _cronErrorText .value = " Connect the gateway before creating a cron job."
1776- _cronSaving .value = false
1777- return
1778- }
1779- try {
1780- val cleanName = name.trim()
1781- val cleanMessage = message.trim()
1782- if (cleanName.isEmpty()) {
1783- throw IllegalArgumentException (" Add a job name." )
1784- }
1785- if (cleanMessage.isEmpty()) {
1786- throw IllegalArgumentException (" Add the message OpenClaw should run." )
1787- }
1788- val schedule = buildCronCreateSchedule(scheduleKind = scheduleKind, scheduleValue = scheduleValue.trim())
1789- val params =
1790- buildJsonObject {
1791- put(" name" , JsonPrimitive (cleanName))
1792- put(" enabled" , JsonPrimitive (true ))
1793- put(" deleteAfterRun" , JsonPrimitive (scheduleKind == " Once" ))
1794- put(" sessionTarget" , JsonPrimitive (" isolated" ))
1795- put(" wakeMode" , JsonPrimitive (" now" ))
1796- put(" schedule" , schedule)
1797- put(
1798- " payload" ,
1799- buildJsonObject {
1800- put(" kind" , JsonPrimitive (" agentTurn" ))
1801- put(" message" , JsonPrimitive (cleanMessage))
1802- },
1803- )
1804- put(
1805- " delivery" ,
1806- buildJsonObject {
1807- put(" mode" , JsonPrimitive (" announce" ))
1808- put(" channel" , JsonPrimitive (" last" ))
1809- put(" bestEffort" , JsonPrimitive (true ))
1810- },
1811- )
1812- }
1813- operatorSession.request(" cron.add" , params.toString())
1814- refreshCronFromGateway()
1815- } catch (err: IllegalArgumentException ) {
1816- _cronErrorText .value = err.message ? : " Could not create cron job."
1817- } catch (_: Throwable ) {
1818- _cronErrorText .value = " Could not create cron job."
1819- } finally {
1820- _cronSaving .value = false
1821- }
1822- }
1823-
1824- private fun buildCronCreateSchedule (
1825- scheduleKind : String ,
1826- scheduleValue : String ,
1827- ): JsonObject =
1828- when (scheduleKind) {
1829- " Every" ->
1830- buildJsonObject {
1831- put(" kind" , JsonPrimitive (" every" ))
1832- put(" everyMs" , JsonPrimitive (parseCronDurationMs(scheduleValue)))
1833- }
1834- " Once" ->
1835- buildJsonObject {
1836- put(" kind" , JsonPrimitive (" at" ))
1837- put(" at" , JsonPrimitive (resolveCronAtIso(scheduleValue)))
1838- }
1839- " Cron" ->
1840- buildJsonObject {
1841- val expr = scheduleValue.takeIf { it.isNotEmpty() } ? : throw IllegalArgumentException (" Add a cron expression." )
1842- put(" kind" , JsonPrimitive (" cron" ))
1843- put(" expr" , JsonPrimitive (expr))
1844- put(" staggerMs" , JsonPrimitive (0 ))
1845- }
1846- else -> throw IllegalArgumentException (" Choose a schedule." )
1847- }
1848-
1849- private fun resolveCronAtIso (value : String ): String {
1850- val clean = value.trim()
1851- if (clean.startsWith(" +" )) {
1852- return Instant .ofEpochMilli(System .currentTimeMillis() + parseCronDurationMs(clean.drop(1 ))).toString()
1853- }
1854- return try {
1855- Instant .parse(clean).toString()
1856- } catch (_: Throwable ) {
1857- throw IllegalArgumentException (" Use ISO time or +30m." )
1858- }
1859- }
1860-
1861- private fun parseCronDurationMs (value : String ): Long {
1862- val match =
1863- Regex (""" ^(\d+)\s*([mhd])$""" )
1864- .matchEntire(value.trim().lowercase())
1865- ? : throw IllegalArgumentException (" Use 15m, 1h, or 1d." )
1866- val amount = match.groupValues[1 ].toLong()
1867- if (amount <= 0 ) {
1868- throw IllegalArgumentException (" Use a duration greater than zero." )
1869- }
1870- val unit = match.groupValues[2 ]
1871- return amount *
1872- when (unit) {
1873- " m" -> 60_000L
1874- " h" -> 3_600_000L
1875- else -> 86_400_000L
1876- }
1877- }
1878-
18791755 private suspend fun refreshUsageFromGateway () {
18801756 _usageRefreshing .value = true
18811757 _usageErrorText .value = null
@@ -2151,12 +2027,7 @@ class NodeRuntime(
21512027 scheduleLabel = cronScheduleLabel(schedule),
21522028 promptPreview = cronPayloadPreview(payload),
21532029 nextRunAtMs = state.long(" nextRunAtMs" ),
2154- lastRunStatus =
2155- state
2156- ?.get(" lastRunStatus" )
2157- .asStringOrNull()
2158- ?.trim()
2159- ?.takeIf { it.isNotEmpty() },
2030+ lastRunStatus = cronJobLastRunStatus(state),
21602031 )
21612032 }.orEmpty()
21622033
@@ -2649,11 +2520,7 @@ internal fun resolveOperatorSessionConnectAuth(
26492520
26502521 val explicitBootstrapToken = auth.bootstrapToken?.trim()?.takeIf { it.isNotEmpty() }
26512522 if (explicitBootstrapToken != null ) {
2652- return NodeRuntime .GatewayConnectAuth (
2653- token = null ,
2654- bootstrapToken = explicitBootstrapToken,
2655- password = null ,
2656- )
2523+ return null
26572524 }
26582525
26592526 return NodeRuntime .GatewayConnectAuth (
@@ -2867,6 +2734,18 @@ private fun JsonObject?.double(key: String): Double? = (this?.get(key) as? JsonP
28672734
28682735private fun JsonObject?.boolean (key : String ): Boolean = (this ?.get(key) as ? JsonPrimitive )?.content?.trim() == " true"
28692736
2737+ internal fun cronJobLastRunStatus (state : JsonObject ? ): String? =
2738+ state
2739+ .cronStatus(" lastStatus" )
2740+ ? : state.cronStatus(" lastRunStatus" )
2741+
2742+ private fun JsonObject?.cronStatus (key : String ): String? =
2743+ this
2744+ ?.get(key)
2745+ .asStringOrNull()
2746+ ?.trim()
2747+ ?.takeIf { it.isNotEmpty() }
2748+
28702749fun providerDisplayName (provider : String ): String =
28712750 when (provider.trim().lowercase()) {
28722751 " openai" -> " OpenAI"
0 commit comments