* fix(infra): guard channel ingress queue parseJson against corrupted JSON
* fix(infra): fix type assertion in ingress queue test
* fix(infra): use tagged parse result and validate payload before claiming
* fix(infra): remove unnecessary non-null assertion in ingress queue test
* fix(infra): scan corrupt ingress rows in claimNext
* test(gateway): avoid typed empty mock call tuple access
* fix(infra): tombstone corrupt ingress rows on duplicate enqueue and stale recovery
Two P1 gaps: enqueue() threw on duplicate when the existing row had corrupt
payload_json, and recoverStaleClaims() silently skipped corrupt claimed rows,
leaving them invisible to recovery. Both paths now tombstone the unrecoverable
row as failed with reason "corrupt_payload" and return a proper result.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(infra): resolve lint shadow and await-thenable in recoverStaleClaims
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(channels): tombstone corrupt ingress rows
* test(channels): use explicit placeholder claim tokens
* refactor(channels): name claim token values
* refactor(channels): keep claim projection direct
* fix(channels): preserve active ingress claims
* fix(channels): make corrupt recovery policy-aware
* refactor(channels): name corrupt claim token
* fix(channels): bound corrupt ingress reconciliation
* fix(channels): paginate pending ingress by key
* refactor(telegram): return live owner check directly
* build(plugin-sdk): refresh public export budget
---------
Co-authored-by: Pick-cat <huang.ting3@xydigit.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(cron): keep isolated run status ok when delivery fails
A successful isolated cron agentTurn whose post-run delivery phase returns
an error collapsed the execution status into the delivery error, so the
outer scheduled run persisted status=error even though the isolated session
ended successfully.
Decouple execution status from delivery outcome in finalizeCronRun: when the
agent payload is non-fatal and the run was not aborted, a delivery dispatch
error now keeps status=ok and records the failure separately via
delivered/deliveryAttempted plus delivery diagnostics, which drive the
already-decoupled run-log deliveryStatus (not-delivered). Aborted runs and
fatal agent payloads keep their error status.
Fixes#94058
* fix(cron): keep deliberate delivery-target refusals as error status
The #94058 fix kept isolated turn status=ok when post-run delivery
fails, but the broad `status === "error"` check also caught the
#91613 keyless implicit last-target refusal, which is a deliberate
delivery-target guard (errorKind "delivery-target") and must stay
status=error. Exclude that errorKind so genuine delivery dispatch
failures still become ok while target-guard refusals remain errors.
* fix(cron): surface successful run delivery errors to run logs
A successful isolated agent turn keeps status=ok when post-run delivery
fails (#94058), but the ok/not-delivered resolveRunOutcome branch only
forwarded delivery diagnostics and dropped the delivery dispatch error.
That left lastDeliveryError empty and the finished-event deliveryError
field blank, so CLI/UI/API run logs could not show why delivery did not
land for an otherwise successful run.
Thread the delivery error on a dedicated CronRunOutcome.deliveryError
field from resolveRunOutcome through executeDetachedCronJob and
applyJobResult into resolveDeliveryState, so it persists as
lastDeliveryError and reaches the finished event (and thus the run log)
without conflating it with a run-level error (lastError stays empty on a
successful run).
Add a service/run-log readback test proving the delivery error survives
the cron boundary into a persisted run-log entry, and extend the
isolated-turn regression to assert the dedicated deliveryError field.
* fix(cron): preserve successful delivery outcomes
* fix(cron): preserve startup delivery errors
* fix(cron): retain best-effort delivery errors
* fix(cron): preserve delivery errors on fallback paths
* fix(cron): preserve delivery errors on fallback paths
---------
Co-authored-by: Alix-007 <li.long15@xydigit.com>
Open Settings from anywhere in the Control UI with Shift-Command-Comma while preserving browser-owned Command-Comma and existing shortcuts.
Prepared head SHA: 769a4fec5f
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
* fix(discord): keep thread-binding persona UTF-16 safe
resolveThreadBindingPersona truncated the persona label with a raw .slice(0, THREAD_BINDING_PERSONA_MAX_CHARS). When an emoji or other astral code point straddles the 80-unit limit, the raw slice keeps a dangling high surrogate, so the Discord thread-binding persona (webhook display name) can carry a lone surrogate. Route the clamp through the shared truncateUtf16Safe primitive so the boundary code point is dropped whole. Adds a regression test.
* test(discord): assert exact persona boundary
* docs(changelog): credit Discord persona fix
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gateway): use truncateUtf16Safe for voice-wake trigger truncation
Replace naive .slice(0, 64) with truncateUtf16Safe() in
normalizeVoiceWakeTriggers to prevent surrogate pair splitting
in user-configured voice wake trigger phrases.
Voice wake triggers are user-configurable text strings that may
contain emoji or non-BMP characters. A naive .slice(0, 64) at
a surrogate pair boundary produces a lone surrogate, which
corrupts the trigger text.
Co-Authored-By: Claude <noreply@anthropic.com>
* test: add proof scripts for C1 sanitize and voice-wake UTF-16
* chore: remove unrelated proof script from voice-wake PR
ClawSweeper review: the console-sanitization proof script belongs
to #103226, not this gateway voice-wake fix. Remove it to keep the
branch clean.
Ref: #103210 review
* fix(gateway): harden voice wake unicode boundary
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(exec): auto-approve recognized read-only boolean flags on default safe bins
Default safe bins (cut, head, tail, tr, uniq, wc) auto-approve stdin-only
text-filter invocations, but the short-option validator only had an accept
path for value-consuming flags: a cluster of pure boolean short flags fell
through to a terminal reject, so common read-only forms like 'wc -l',
'tr -d', 'uniq -c' and 'sort -n' were force-routed to manual approval even
though the bins are stdin-only and the dangerous flags are already denied.
Add an allowedBooleanFlags allowlist to the safe-bin profile model and
populate it for the default bins with their read-only boolean flags. The
short-cluster validator now accepts recognized boolean flags and the long
validator reuses its existing boolean-flag accept branch (previously
unreachable). Unrecognized short flags (e.g. 'tr -S') stay fail-closed, and
denied flags are still rejected first.
* fix(exec): keep safe-bin allowedBooleanFlags off the config-facing fixture type
The boolean-flag allowlist for default safe bins leaked onto SafeBinProfileFixture, the type used for tools.exec.safeBinProfiles, while the strict zod schema and the config normalizer never accepted or preserved the key. Move allowedBooleanFlags to an internal BuiltinSafeBinProfileFixture used only for the curated built-in profiles; custom config profiles keep the allowedValueFlags/deniedFlags model.
Adds tests proving built-in profiles still honor the boolean allowlist and that a custom profile cannot widen it.
* fix(exec): keep tail follow approval-gated
* test(exec): stabilize safe-bin trust fixtures
* test(exec): isolate safe-bin argv fixtures
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(agents): keep model fallback turn-local instead of persisting over user pins
* fix(telegram): use live config snapshots per operation
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* test(telegram): fix config snapshot type coverage
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* fix(opencode-go): remove deprecated mimo-v2-omni and mimo-v2-pro model aliases
These deprecated aliases reject agent requests from the OpenCode Go gateway.
Remove them from the provider catalog and clean up all references in probe
skip lists, CI workflows, and tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(opencode-go): complete deprecated MiMo cleanup
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>