Commit Graph

66940 Commits

Author SHA1 Message Date
Vincent Koc
e9756f9e71 refactor(android): remove stale canvas and overlay helpers 2026-06-23 05:13:13 +08:00
Vincent Koc
2e0dd66d39 refactor(android): remove orphan runtime accessors 2026-06-23 05:05:41 +08:00
Vincent Koc
1423487351 refactor(android): remove stale UI helpers 2026-06-23 04:58:26 +08:00
Vincent Koc
01d212bfa3 refactor(docs-i18n): remove unreachable chunk helpers 2026-06-23 04:58:21 +08:00
Vincent Koc
3d787b5181 refactor(types): remove stale internal contract aliases 2026-06-23 04:48:02 +08:00
Vincent Koc
89c90210fb refactor(infra): trim unused fs-safe facade exports 2026-06-23 04:39:05 +08:00
Dallin Romney
65a20ca4c5 fix: allow sqlite user version guardrail (#95857) 2026-06-22 13:36:42 -07:00
Vincent Koc
d5d9a8256d fix(crabbox): route native Windows hydrate jobs 2026-06-23 04:34:03 +08:00
Vincent Koc
5dfbb9d1e0 test(ui): scope quota pill e2e selector 2026-06-23 04:29:27 +08:00
zw-xysk
3a32d24395 fix(cron): trim trailing whitespace from recognized job object keys (#95674)
* fix(cron): trim trailing whitespace from recognized job object keys (#95407)

Some tool-call extraction/serialization pipelines can produce cron object
keys with trailing spaces (e.g. 'schedule ' instead of 'schedule'), causing
gateway validation to reject the job.

Add repairPaddedCronKeys() to canonicalizeCronToolObject() that trims only
recognized CRON_RECOVERABLE_OBJECT_KEYS. Non-recognized keys (including
special ones like '__proto__') are never trimmed, preventing prototype
pollution. When both padded and canonical forms exist, the canonical key
wins.

Tests:
- add job with trailing-space keys -> trimmed
- update patch with trailing-space keys -> trimmed
- non-recognized padded keys left intact (safety)
- canonical key preserved over padded duplicate
- clean keys unchanged

133 tests pass (128 existing + 5 new).

* fix(cron): preserve padded duplicate keys when canonical form already exists (#95407)

When both a padded key (e.g. 'schedule ') and its canonical form
('schedule') exist, the padded key is now preserved so strict gateway
validation rejects the ambiguous input rather than silently picking one
value. Only padded keys without a canonical counterpart are trimmed.
2026-06-22 20:24:59 +00:00
miorbnli
90fb2ee4e1 fix(gateway.tls): reject empty/whitespace certPath and keyPath (#94054)
* fix(gateway.tls): reject empty/whitespace certPath and keyPath

gateway.tls.certPath and keyPath both accept "" and whitespace-only
strings at the schema layer (z.string().optional() with no .min(1)), and
the runtime fallback cfg.certPath ?? path.join(baseDir, "...") only
triggers on null/undefined, so empty strings reach generateSelfSignedCert
unchanged. From there path.dirname("") === "." and openssl receives
"-out "" -keyout """, producing a cryptic error.

Sibling field caPath already guards against this via truthy check, so
this brings certPath/keyPath to the same defensive style.

Three changes:
1. Schema: certPath/keyPath tightened to z.string().trim().min(1).optional()
2. Runtime: replace ?? with explicit truthy check, aligning with caPath
3. chmod errors now throw instead of .catch(() => {})

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: add :unknown type to catch callback variables

* fix(gateway.tls): restore best-effort chmod for generated cert/key

* fix(gateway.tls): preserve non-empty cert/key path bytes

Schema z.string().trim().min(1) and the runtime cfg.certPath.trim() both
trimmed non-empty paths. The schema trim silently rewrote validated config
data, and the runtime trim duplicated resolveUserPath, which already trims
and expands ~ in resolveHomeRelativePath.

Keep blank/whitespace rejection, drop the transformation: schema uses
.refine (validate only), runtime passes the original string to resolveUserPath.
Non-empty paths keep exact bytes; blank values are still rejected/defaulted.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-22 20:23:52 +00:00
Sash Zats
5d9daea2b0 fix(ios): centralize app accent colors (#94627)
Move iOS accent and status colors through design tokens so raw SwiftUI color literals are blocked outside token definitions.

Set the app-wide tint in SwiftUI and UIKit from code, without relying on Assets.xcassets AccentColor.
2026-06-22 20:20:37 +00:00
zhang-guiping
2dc2d73b07 fix(webchat): sessions persist after reconnects (#89017)
* fix(gateway): preserve asserted webchat sessions

* test(gateway): cover stale asserted webchat sessions

* fix(gateway): scope webchat session resume

* chore(protocol): refresh chat send models

* fix: document reconnect session resume protocol

* fix(gateway): keep reconnect resume internal

* gateway: keep reconnect resume options internal

* test(ui): avoid private resume marker lint access
2026-06-22 20:02:58 +00:00
Vincent Koc
9122e762d8 refactor(records): reuse canonical object guard 2026-06-23 03:58:08 +08:00
zhang-guiping
769579bcf0 fix(opencode-go): streaming completes when provider ends responses (#93965)
* fix(opencode-go): abort stalled SSE streams at provider-owned raw boundary

opencode-go routes through the shared OpenAI-compatible completions provider,
where a stalled SSE socket (provider emits tokens then never closes the stream)
hangs the gateway until stuckSessionAbortMs (~622s) and surfaces as
'LLM request failed' / 'Request was aborted'. Issue #93610 reports ~90% of
opencode-go cron jobs failing intermittently this way.

Add a provider-owned stream wrapper at the opencode-go raw SSE boundary that
injects an AbortController into the underlying OpenAI SDK request and aborts
it after a configurable idle window (default 30s, far below 622s) elapses
without any forward-progress event. The wrapper is:

- Provider-scoped: only applies when model.provider === 'opencode-go'; the
  shared openai-completions.ts path is untouched.
- Abortable: calls controller.abort() on the injected AbortSignal, which
  propagates through OpenAI SDK requestOptions.signal and genuinely
  interrupts the underlying fetch/stream (not just iterator return()).
- Idle-based: every event (text/tool/thinking delta, including delayed
  usage-only chunks) refreshes the timer; natural completion (done/error)
  cancels it. Normal delayed usage-only completion is preserved.
- Boundary-terminal: pushes a terminal { type: 'error', reason: 'aborted' }
  event downstream so consumers do not hang.

TDD: stream-termination.test.ts covers (a) stalled stream after first
progress is aborted within the idle window with a downstream 'aborted'
terminal event, and (b) normal delayed completion within the idle window
is not aborted and the done event is forwarded unchanged.

* fix(opencode-go): align stalled-stream idle default with runtime (120s)

Match the runtime's shared `DEFAULT_LLM_IDLE_TIMEOUT_MS` (120s) so
non-cron interactive opencode-go runs see no behavior change versus the
existing watchdog. Cron runs — for which the runtime disables its idle
watchdog entirely (`resolveLlmIdleTimeoutMs` returns 0 when trigger is
cron and no explicit timeout is set) — still get provider-owned
termination well before the ~622s stuck-session recovery.

Refs #93610

* fix(opencode-go): satisfy CI lint and test type checks

- Remove unnecessary `?? {}` fallback in spread (oxlint
  no-useless-fallback-in-spread).
- Drop non-narrowing `!` on the wrapper return type; use
  `await Promise.resolve(...)` to collapse the
  `StreamLike | Promise<StreamLike>` union before `for await`.

Refs #93610

* fix(opencode-go): arm stalled-stream idle timer only after first event

The wrapper armed the idle timer before the first upstream event, which
would mis-abort slow time-to-first-byte requests — including the
opencode-go cron runs that the runtime deliberately leaves uncapped via
resolveLlmIdleTimeoutMs. Arm only after the first forwarded event, and
add regression coverage for the slow-first-event path.

* fix(opencode-go): cover stalled stream first event

* fix(opencode-go): respect explicit stream timeout

* fix(opencode-go): preserve first-event timer after synthetic start

* fix(opencode-go): satisfy stream termination test lint

* fix(opencode-go): distinguish synthetic stream preambles

* fix(opencode-go): route stalled streams through failover
2026-06-22 19:57:21 +00:00
Vincent Koc
056e5b6b07 refactor(routing): share optional agent id normalization 2026-06-23 03:53:45 +08:00
NIO
8fdb1b61db fix(agents): classify generic LLM-request-failed error as transient timeout (#94062)
The generic assistant error text "LLM request failed." (GENERIC_ASSISTANT_ERROR_TEXT) is
produced by formatUserFacingAssistantErrorText when the underlying provider error cannot
be formatted into a specific category. For local providers (LM Studio, Ollama) this wraps
connection/availability failures when the model is not loaded or the endpoint is unreachable.

Without this match, the error is not classified as any transient type (rate_limit, overloaded,
network, server_error, timeout), so cron retry and payload.fallbacks never engage — even
though the configured fallback chain should handle provider availability failures.

Add /^llm request failed\.$/i as an exact-match regex in the timeout error patterns. This
strictly matches only the bare "LLM request failed." string, not variants like
"LLM request failed: provider rejected the request schema or tool payload." (which is a
format/schema error, not transient). Variants with specific transient reasons (connection
refused, network error, etc.) are classified through their own existing patterns.

Closes #93931
2026-06-22 19:53:26 +00:00
ly-wang19
a2d7882100 fix(cli): expose --count on infer image edit, matching image generate (#95300)
The `image edit` CLI command could not request multiple edited images while
the sibling `image generate` could, even though the shared runImageGenerate
action and generateImage thread `count` for both capabilities and providers
(xai, litellm, openai) honor edit-mode count (edit.maxCount 4). PR #94156
added --quality/--openai-moderation to both commands but left --count off
edit only. Add --count to the edit command registration, action, and
CAPABILITY_METADATA, mirroring image generate exactly.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 19:52:32 +00:00
Parvesh Saini
e33760c9df fix(model-catalog): strip manifest model-id prefixes by the matched length (#95744) 2026-06-22 19:52:13 +00:00
Vincent Koc
392377e7e4 chore(plugin-sdk): refresh API baseline hash 2026-06-22 21:49:53 +02:00
Vincent Koc
0a338147a5 refactor(numbers): share non-negative finite guard 2026-06-23 03:46:22 +08:00
Vincent Koc
013e33c6d3 fix(telegram): avoid duplicate progress headings 2026-06-22 21:43:47 +02:00
Hoi Hin Adrian Ip
dbd4c98b02 Handle Codex toolResult blocks in truncation (#87912)
Co-authored-by: Hoi Hin Adrian Ip <255652477+AdrianIp0204@users.noreply.github.com>
2026-06-22 19:41:30 +00:00
Vincent Koc
0529281430 refactor(sqlite): share numeric column decoding 2026-06-23 03:38:18 +08:00
Vincent Koc
284e514e19 refactor(logging): share log file path primitives 2026-06-23 03:34:43 +08:00
Vincent Koc
066700bdd0 refactor(anthropic): share Foundry bearer auth policy 2026-06-23 03:31:32 +08:00
Vincent Koc
470a0f80b6 refactor(plugins): reuse optional string normalization 2026-06-23 03:28:01 +08:00
Vincent Koc
b31bf811cb refactor(providers): share bounded error body reader 2026-06-23 03:24:54 +08:00
Yzx
1662b07810 fix(cron): expose per-job fallbacks in CLI (#93369) 2026-06-22 19:22:20 +00:00
pick-cat
cf31689a03 fix(control-ui): restore provider usage quota pill in sidebar session switcher (fixes #93041) (#94219)
* fix(control-ui): restore provider usage quota pill in sidebar session switcher

* ci: re-trigger flaky cron-service shard

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Pick-cat <266665499+Pick-cat@users.noreply.github.com>
Co-authored-by: Pick-cat <Pick-cat@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:21:38 +00:00
Vincent Koc
a07d92ff4f refactor(net): share FormData shape guard 2026-06-23 03:20:18 +08:00
Vincent Koc
858fd2c5a2 refactor(sqlite): share user version probe 2026-06-23 03:19:45 +08:00
Vincent Koc
7c4ab782cb refactor(providers): reuse capability provider registry maps 2026-06-23 03:17:22 +08:00
Moeed Ahmed
5cafe4b0cf fix(telegram): keep bot reply answers anchored to current message (#90475)
Co-authored-by: Moeed Ahmed <moeedahmed@Moeed-Mac-mini.local>
2026-06-22 19:17:07 +00:00
Darren2030
c4cac33af6 fix(openrouter): expand short canonical model IDs to upstream API slugs (fixes #95198) (#95268)
- Add OPENROUTER_SHORT_TO_API_MODEL_ID map for short model refs like
  openrouter/deepseek-v4-flash that OpenClaw surfaces but OpenRouter API
  expects as deepseek/deepseek-v4-flash.
- In normalizeOpenRouterApiModelId, expand short refs before falling back
  to the existing namespaced strip logic.
- Add unit tests covering short refs, long refs, native routes, and
  pass-through cases.
- Add standalone reproduction script that verifies all normalization cases.
2026-06-22 19:15:25 +00:00
Masato Hoshino
965d1fff3f fix(providers): strip cache-boundary marker from non-Anthropic prompts (#89716) 2026-06-22 19:14:31 +00:00
snowzlmbot
23f94bfa78 fix(reply): normalize persisted model overrides before reset (#94752)
Co-authored-by: snowzlm <snowzlm@noreply.codeberg.org>
2026-06-22 19:14:25 +00:00
Yzx
a0ed4273ee fix(agents): resolve bound route agent for inbound sessions (#95118) 2026-06-22 19:14:17 +00:00
areslp
bfbf25e234 fix(feishu): show voice message duration via upload duration (#89172)
Voice/audio messages sent to Feishu (opus) play fine but show no duration
on the bubble. Feishu derives the voice-bubble duration from the `duration`
parameter of the file upload API (`im/v1/files`); the audio message content
only carries `{file_key}` and has no duration field, so the duration was
never set.

`sendMediaFeishu` now probes the outgoing audio with `ffprobe` and passes the
result as the upload `duration` (ms). It probes the buffer that is actually
sent (after the existing voice transcode, which caps length via
`MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS`), so the reported length matches what
is played. Probing is best-effort: on failure it logs and omits the duration,
and the message still sends. The audio message content is unchanged.

Fixes #53798

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 19:13:14 +00:00
Gavin Lee
8c366bfefd test(cli): add banner emission reset helper (#87121) 2026-06-22 19:12:07 +00:00
Vincent Koc
b4bc1f20c9 fix(agents): repair OpenAI responses replay pairing 2026-06-23 03:11:33 +08:00
Vincent Koc
c782fa98aa refactor(delivery): share recovery primitives 2026-06-23 03:10:30 +08:00
ly-wang19
81e1ec467c fix(imessage): strip leading echo corruption markers in the persisted echo cache (#94442)
The persisted iMessage echo-dedupe cache normalized text with CRLF->LF + trim only, not the leading attributedBody corruption-marker stripping the in-memory echo cache applies (#93511). The persisted 12h cache is the only matcher once the 4s in-memory text TTL expires, so a delayed reflected own-message echo whose text decoded with a leading NUL/replacement/BOM marker did not match the clean stored send -- the agent's own message was re-ingested as fresh inbound, causing a self-reply loop.

Extract the marker-stripping into a leaf module shared by both echo caches (the in-memory cache already imports the persisted one, so importing back would be a cycle) and apply it in the persisted normalizeText, so both caches strip identically.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 19:07:45 +00:00
snowzlm
10113b2c9f fix(daemon): keep systemd gateway running after child OOM (#93585)
Co-authored-by: snowzlm <snowzlm@noreply.codeberg.org>
2026-06-22 18:54:21 +00:00
jase-283
f8df80646b chore: sync yuanbao plugin catalog to 2.15.0 (#94470) 2026-06-22 18:50:07 +00:00
Vincent Koc
541f7ffc65 fix(doctor): handle unknown tool profiles in preview warnings 2026-06-22 20:41:02 +02:00
Vincent Koc
43f134ff55 refactor(security): share tool policy layering 2026-06-23 02:40:29 +08:00
Ayaan Zaidi
780f83bcfb test(agents): distill media lifecycle fixture 2026-06-23 00:09:20 +05:30
Peter Steinberger
37714f185f fix(media): pin canonical requester route 2026-06-23 00:09:20 +05:30
Peter Steinberger
9d1ba36f6b test(media): allow partial session fixtures 2026-06-23 00:09:20 +05:30