Add tools.cron.command_allowed_remotes so remote channels can be
explicitly allowlisted for command-executing cron jobs. Entries support
channel names, channel:chat_id pairs, and a literal * to allow every
non-empty channel while preserving the default deny posture.
The existing allow_command and command_confirm checks still apply after the
channel gate. Update tests, config examples, and cron documentation for the
new option.
Two concurrency bugs identified in PR #2904 review:
1. Replace sync.WaitGroup with sync.Cond-based activeReqCount to avoid
the "WaitGroup is reused before previous Wait has returned" panic that
occurs when Add(1) races with a goroutine-launched Wait().
2. Make panic cleanup conditional: when runTurn panics, only delete the
session's activeTurnStates entry if it still points to our placeholder.
Previously, an unconditional delete could wipe a new message's slot
claimed between the panic and the deferred cleanup.
Replace silently discarded json.Marshal and json.Unmarshal errors with
explicit checks. If serialization fails, log a warning and either
return early (for the config-level marshal/unmarshal) or skip the
channel (for per-channel marshal). This prevents silent data loss
when channel configuration contains unexpected types.
The reviewer identified two bugs in the original PR:
1. PATCH /api/config leaves session.dimensions stale: LoadConfig()
derives dimensions from the old dm_scope, and the merge carries
those stale dimensions forward. ApplyDmScope() then exits early
because dimensions is already populated, causing a mismatch between
dm_scope (new) and dimensions (old).
2. Legacy/default configs omit dm_scope in GET response: configs with
explicit dimensions but no dm_scope (including DefaultConfig) return
no dm_scope field, causing the frontend to fall back to its default
('per-channel-peer'), which may not match the actual dimensions.
Fix:
- Add DeriveDmScope() to reverse-map known dimensions arrays to
dm_scope when dm_scope is empty.
- Call it in LoadConfig(), PUT handler, PATCH handler, and
ResetToDefaults() for consistent normalization.
- In PATCH handler, clear stale dimensions from the merge result when
the patch contains session.dm_scope but not session.dimensions,
allowing ApplyDmScope() to re-derive from the new scope.
- Add comprehensive unit tests for DeriveDmScope() and scope
transition scenarios.
* fix(launcher): hide console flashes in all Windows child processes
PR #2654 only applied HideWindow to child processes in gateway.go (powershell, tasklist, ps). Several other files still use exec.Command directly, causing visible console windows on Windows.
- startup.go: reg query/add/delete for autostart registry
- version.go: picoclaw version subcommand
- runtime.go: rundll32 for browser launch
- onboard.go: picoclaw onboard subcommand
Add launcherExecCommand to the utils package (matching the api package pattern) and replace all bare exec.Command calls on Windows paths.
* refactor: consolidate launcherExecCommand into utils package
Export LauncherExecCommand and ApplyLauncherProcAttrs from the utils
package as the single source of truth. The api package now imports
and delegates to these exported functions, eliminating code duplication.
Addresses review feedback from imguoguo on PR #3061.