Node 24.13+ sealed the require property on ESM module objects, breaking
Jiti's sync CJS path when tryNative is false. Switch to async .import().
Also update GatewayClient id from "control-ui" to "openclaw-control-ui"
to match the upstream rename.
When a gateway caller supplies a callerSessionKey it is explicitly
requesting session-scoped access (multi-agent / multi-user deployments).
Previously, resolveCronCallerOptions unconditionally set ownerOverride
to true whenever the client held ADMIN_SCOPE, which meant the
service-layer ownership check was a no-op for every mutation
(cron.update, cron.remove, cron.run) since those methods all require
ADMIN_SCOPE.
Now ownerOverride is only true when the client is an admin that did NOT
supply a session key — the typical local-CLI / control-UI case. When a
session key is present the ownership check fires as intended.
Also exports resolveCronCallerOptions and adds direct unit tests
covering admin + sessionKey, admin without sessionKey, non-admin, and
null client scenarios.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The per-caller ownership enforcement introduced for issue #35447 was
silently bypassed: all four mutation/list schemas used
additionalProperties:false but did not declare callerSessionKey, causing
AJV to strip the field before the handler could read it. As a result
resolveCronCallerOptions always received an empty caller and fell back to
allow-all behaviour.
Fix:
- Add optional callerSessionKey (NonEmptyString) to CronListParamsSchema,
CronUpdateParamsSchema, CronRemoveParamsSchema and CronRunParamsSchema.
- Update the four handlers in server-methods/cron.ts to read
p.callerSessionKey instead of the previous p.sessionKey (which was
never populated through these schemas).
- Add validator tests covering acceptance of the new field and rejection
of empty strings across all four operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes#35447
In multi-user deployments (Telegram, Slack, DingTalk) the cron service
exposed all jobs to all callers. Any session could list, remove, update,
or trigger jobs created by a different agent/session.
Changes:
- service/ops.ts: Add `CronMutationCallerOptions` type (callerAgentId,
callerSessionKey, ownerOverride). Add `callerOwnsJob()` helper that
matches by agentId or sessionKey and falls back to allow when no
owner metadata is present (backward compat). Thread the caller opts
through `listPage`, `remove`, `update`, `enqueueRun`, `run`, and the
internal `inspectManualRunPreflight`/`prepareManualRun` helpers.
Mutations on a job owned by a different session throw a structured
error with code CRON_PERMISSION_DENIED.
- service.ts: Expose the new optional caller parameter on the public
CronService methods (update, remove, run, enqueueRun).
- gateway/server-methods/cron.ts: Add `resolveCronCallerOptions()` that
extracts the caller sessionKey from request params and sets
ownerOverride=true when the client holds the operator.admin scope.
Pass the resolved caller opts into cron.list, cron.update, cron.remove,
and cron.run. Respond with PERMISSION_DENIED on CRON_PERMISSION_DENIED.
- gateway/protocol/schema/error-codes.ts: Add PERMISSION_DENIED error code.
- service.session-isolation.test.ts: 19 new tests covering listPage
filtering, and remove/update/enqueueRun ownership enforcement including
admin bypass (ownerOverride) and legacy job backward compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(gateway): increase WS handshake timeout from 3s to 10s
The 3-second default is too aggressive when the event loop is under load
(concurrent sessions, compaction, agent turns), causing spurious
'gateway closed (1000)' errors on CLI commands like `openclaw cron list`.
Changes:
- Increase DEFAULT_HANDSHAKE_TIMEOUT_MS from 3_000 to 10_000
- Add OPENCLAW_HANDSHAKE_TIMEOUT_MS env var for user override (no VITEST gate)
- Keep OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS as fallback for existing tests
Fixes#46892
* fix: restore VITEST guard on test env var, use || for empty-string fallback, fix formatting
* fix: cover gateway handshake timeout env override (#49262) (thanks @fuller-stack-dev)
---------
Co-authored-by: Wilfred <wilfred@Wilfreds-Mac-mini.local>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* fix: make cleanup "keep" persist subagent sessions indefinitely
* feat: expose subagent session metadata in sessions list
* fix: include status and timing in sessions_list tool
* fix: hide injected timestamp prefixes in chat ui
* feat: push session list updates over websocket
* feat: expose child subagent sessions in subagents list
* feat: add admin http endpoint to kill sessions
* Emit session.message websocket events for transcript updates
* Estimate session costs in sessions list
* Add direct session history HTTP and SSE endpoints
* Harden dashboard session events and history APIs
* Add session lifecycle gateway methods
* Add dashboard session API improvements
* Add dashboard session model and parent linkage support
* fix: tighten dashboard session API metadata
* Fix dashboard session cost metadata
* Persist accumulated session cost
* fix: stop followup queue drain cfg crash
* Fix dashboard session create and model metadata
* fix: stop guessing session model costs
* Gateway: cache OpenRouter pricing for configured models
* Gateway: add timeout session status
* Fix subagent spawn test config loading
* Gateway: preserve operator scopes without device identity
* Emit user message transcript events and deduplicate plugin warnings
* feat: emit sessions.changed lifecycle event on subagent spawn
Adds a session-lifecycle-events module (similar to transcript-events)
that emits create events when subagents are spawned. The gateway
server.impl.ts listens for these events and broadcasts sessions.changed
with reason=create to SSE subscribers, so dashboards can pick up new
subagent sessions without polling.
* Gateway: allow persistent dashboard orchestrator sessions
* fix: preserve operator scopes for token-authenticated backend clients
Backend clients (like agent-dashboard) that authenticate with a valid gateway
token but don't present a device identity were getting their scopes stripped.
The scope-clearing logic ran before checking the device identity decision,
so even when evaluateMissingDeviceIdentity returned 'allow' (because
roleCanSkipDeviceIdentity passed for token-authed operators), scopes were
already cleared.
Fix: also check decision.kind before clearing scopes, so token-authenticated
operators keep their requested scopes.
* Gateway: allow operator-token session kills
* Fix stale active subagent status after follow-up runs
* Fix dashboard image attachments in sessions send
* Fix completed session follow-up status updates
* feat: stream session tool events to operator UIs
* Add sessions.steer gateway coverage
* Persist subagent timing in session store
* Fix subagent session transcript event keys
* Fix active subagent session status in gateway
* bump session label max to 512
* Fix gateway send session reactivation
* fix: publish terminal session lifecycle state
* feat: change default session reset to effectively never
- Change DEFAULT_RESET_MODE from "daily" to "idle"
- Change DEFAULT_IDLE_MINUTES from 60 to 0 (0 = disabled/never)
- Allow idleMinutes=0 through normalization (don't clamp to 1)
- Treat idleMinutes=0 as "no idle expiry" in evaluateSessionFreshness
- Default behavior: mode "idle" + idleMinutes 0 = sessions never auto-reset
- Update test assertion for new default mode
* fix: prep session management followups (#50101) (thanks @clay-datacurve)
---------
Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
* MiniMax: add M2.7 models and update default to M2.7
- Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to provider catalog and model definitions
- Update default model from MiniMax-M2.5 to MiniMax-M2.7 across onboard, portal, and provider configs
- Update isModernMiniMaxModel to recognize M2.7 prefix
- Update all test fixtures to reflect M2.7 as default
Made-with: Cursor
* MiniMax: add extension test for model definitions
* update 2.7
* feat: add MiniMax M2.7 models and update default (#49691) (thanks @liyuan97)
---------
Co-authored-by: George Zhang <georgezhangtj97@gmail.com>