The four Telegram dispatch controllers were partitions of one closure: ~75 factory parameter slots, 7 post-construction back-edge setters, a shared mutable state bag, and load-bearing construction order. One turn record now carries the once-resolved config and all state; the four files remain as implementation with a hand-written leaf type contract (four state-slice types). Rides along: dead generation fence deleted (constant-0 from birth), queuedFinal ||= fix with regression (suppressed exec-approval turns no longer trigger a spurious fallback), collapse resolver/mutator split. Dispatch tests and harness byte-identical to main; live E2E lifecycle proof on the PR.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Remove program-argument runtime rediscovery so managed Gateway and node-host services consume only the canonical validated Node path, while executable wrappers remain supported.\n\nRefs #122061.
A 401 challenge pointing at new protected-resource metadata let the MCP SDK rediscover
a different authorization server and POST the stored refresh token to it. Token issuer
is now persisted with the tokens and disclosure is gated on exact issuer identity;
legacy rows without recoverable issuer provenance fail closed to an explicit re-login.
Admin-merged past checks-node-compact-small-1 by maintainer decision: five runs of
attribution show the failure is src/gateway/server-kernel.test.ts (added in #121981)
hanging at HTTP-listen under this merge ref's shard composition — unrelated to this
diff and green on main. Evidence on the PR.
Co-authored-by: yetval <yetvald@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Schema versions are global v6 and agent v17 (src/state/openclaw-state-db-contract.ts:6; src/state/openclaw-agent-db-contract.ts:22). State v5/v6 provenance comes from 7a7d6bb and 509a5f0, matching the current contract comments at src/state/openclaw-state-db-contract.ts:4.
QMD is retired and Doctor migrates its paths to builtin memory (src/commands/doctor/shared/legacy-config-migrations.runtime.retired-memory-qmd.ts:94). Device auth is SQLite-backed in TypeScript and Swift while Android remains SecurePrefs-backed (src/infra/device-auth-store.ts:115; apps/shared/OpenClawKit/Sources/OpenClawKit/DeviceAuthStore.swift:26; apps/android/app/src/main/java/ai/openclaw/app/gateway/DeviceAuthStore.kt:55).
Opt-in cache, payload, raw-stream, timeline, and macOS diagnostics remain JSONL artifacts (src/agents/cache-trace.ts:93; src/agents/anthropic-payload-log.ts:46; src/agents/embedded-agent-subscribe.raw-stream.ts:16; src/infra/diagnostics-timeline.ts:96; apps/macos/Sources/OpenClaw/DiagnosticsFileLog.swift:3).
The canonical agent schema currently has cache_entries but no VFS/tool/run artifact tables, and subagent attachments materialize in child workspaces (src/state/openclaw-agent-schema.sql:398; src/agents/subagents/spawn/subagent-attachments.ts:308). Current Doctor/session owners replace deleted prototype paths (src/infra/state-migrations.doctor.ts:66; src/config/sessions/session-accessor.sqlite-entry.ts:165; src/config/sessions/paths.ts:320).
Stage test registries without retiring the worker's ambient registry, then restore its exact snapshot after each producer. This prevents later non-isolated gateway tests from inheriting plugin runtime state.
* fix(cli): validate webhooks gmail --tailscale mode at parse time
--tailscale <mode> was stored unchecked and cast to the tailscale union
type, so a typo like --tailscale offf (truthy and !== "off") was treated
as enabled and only failed deep inside tailscale setup with a raw CLI
usage error. Reject modes other than funnel|serve|off in
parseGmailCommonOptions, mirroring the strict numberOption pattern, and
export the setup/run parsers for direct testing.
* fix(cli): reject blank Gmail tailscale modes
* fix(cli): preserve tailscale mode type
---------
Co-authored-by: Dallin Romney <dallinromney@gmail.com>
Centralize Telegram text delivery: page planning and reply-target progression move into telegram-text-delivery.ts, shared by the durable send, edit, and streaming-final paths; bot/reply-threading.ts and per-path split/page logic are deleted. Formatted-to-plain recovery routes through withTelegramPlainFallback everywhere. Restores the projection-cursor invalidation contract on terminal multi-page failures (with regression). Net -321 production LOC. Live E2E on the PR: formatted entity path, plain-fallback recovery, and 2-page chaining.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(infra): use Buffer.byteLength for JSON string byte-limit fast-path guard
jsonStringByteLengthUpToLimit used value.length (UTF-16 code units)
as a fast-path estimate of JSON-encoded byte size before calling
the expensive jsonUtf8BytesOrInfinity fallback. For CJK text,
.length underestimates the actual UTF-8 byte count by up to 3x,
so the guard incorrectly passes strings that should bail early.
The accurate fallback still catches the overflow, so no data
corruption occurs, but every such miss triggers an unnecessary
JSON.stringify allocation.
Replace value.length + 2 with Buffer.byteLength(value, 'utf8') + 2
so the fast path works correctly across all Unicode content.
Buffer.byteLength on a string is O(n) without allocation — still
cheaper than the JSON.stringify fallback it guards against.
ASCII behaviour is unchanged (byteLength == length).
* fix(infra): preserve O(1) ASCII fast-path before UTF-8 byte-length guard
Restore the O(1) value.length lower-bound guard before the O(n)
Buffer.byteLength UTF-8 guard. Oversized ASCII strings (where
.code-unit count already exceeds the budget) now bail immediately
without scanning the full string. CJK and emoji content that
passes the O(1) guard still gets the accurate Buffer.byteLength
check.
The two-layer bailout is:
1. value.length + 2 > remainingBytes (O(1), catches clearly oversized)
2. Buffer.byteLength + 2 > remainingBytes (O(n), catches CJK/emoji)
3. jsonUtf8BytesOrInfinity (fallback, full JSON.stringify)
* fix(infra): add O(1) upper-bound guard to avoid Buffer.byteLength scan on fitting strings
Three-tier guard eliminates scan overhead on the common path:
T1 O(1) lower-bound bail (oversized ASCII, 0 scans)
T2 O(1) upper-bound pass (fitting strings, 0 scans, this is new)
T3 O(n) Buffer.byteLength (CJK borderline only, 1 scan)
* fix(infra): avoid serializing oversized Unicode strings
Punchcard-Session: coral-cedar-brook-8x
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Resolve the deliberately non-cancelled handler after asserting its client deadline, then wait for root admission to drain before leaving the non-isolated test.
Use normalized tool-policy semantics for both system-prompt and Code Mode skill projections so wildcard allowlists remain unrestricted while finite allowlists still suppress skills.\n\nRefs #121950.
Close the barrel gap (SessionEntryListScope), migrate the 14 production
type-bypass imports and the sessions-rewind raw-op imports through the
accessor barrel, and document session-accessor.sqlite-scope as the
sanctioned low-level entry point for doctor/migrations/infra. Net +8
production LOC: the barrel gains its missing surface. Part 3 (final) of
the session-accessor dual-layer collapse (#121316, #121536).
Inbound document attachments could vanish silently (DOCX and other Office files, read failures, disabled URL sources). File intake now classifies every attachment into a closed outcome union rendered through one exhaustiveness-checked path: unsupported formats, policy-rejected types, unreadable files, and disabled URL sources all produce bounded model-visible markers (max five per message plus one overflow summary), with strict MIME-token and URL-basename sanitization so attachment metadata cannot inject prompt text or leak signed-URL credentials. Media-stage outcome custody is tracked separately in #122044.
Proof: focused media suites (1855 tests), pre-fix regression, exact-head ci-gate green, ClawSweeper local review clean, and a live Telegram E2E showing the DOCX marker in the model payload.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Unset heartbeat.target now resolves "owner": elected heartbeat notifications deliver to the operator's DM resolved from commands.ownerAllowFrom or the channel allowFrom (first concrete entry; wildcards and channel-scoped wildcards excluded; configured owners exhausted across channels before any channel-local fallback). Delivery requires the channel's own classifier to positively prove a direct destination — every bundled messaging plugin now ships an inferTargetChatType contract — and unproven or group-shaped destinations fail closed to the visible no-route state. The first implicitly-routed delivery carries a one-line self-explanation naming the target: "none" opt-out. Explicit target "last" remains as the follow-the-conversation opt-in. Refines the unreleased #121892 default before it ships; refs #121880.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
#121734 landed 982 B of startup JS, leaving merge-ref builds 1 B over the
baseline ceiling; every open PR's build-artifacts job fails inherited red.
CI-measured bytes per the updater contract.
#121600 (4c951398ef) made default command progress status-only — raw
command text stays out of chat previews. This assertion still expected
the pre-#121600 preview with the raw command line; bisect-confirmed
first-bad at 4c951398ef. Pre-existing failure surfaced by the
dispatch-suite CI gap (#122029). Dispatch suite now 247/247.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
resolveDeliveryTarget (src/channels/turn/durable-delivery.ts:57) reads
OriginatingTo/To — channel-prefixed refs — as its own fallback chain, so
telegram:-100555 is the contract shape for the durable 'to', not the
bare chat id this assertion expected. Pre-existing failure surfaced by
the dispatch-suite CI gap (#122029).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(ui): show a workspace's own project icon in the chat workspace chip
Resolve the conventional project icon inside a session's workspace root once
per Gateway process and serve it over a strictly scoped authenticated route so
the Control UI can render real project identity instead of a folder glyph.
Refs #121723
* test(gateway): keep the workspace icon server teardown out of a promise executor return
* fix(ui): keep flat project icons legible on the workspace chip in every theme
* feat(ui): lead the chat header with the project, then the session title
Reorder the header into an identity trail — project chip, muted slash, session
title — rendered from one segment list so a later parent-session segment
extends it. Drop the project icon halo and record the flat-dark-icon contrast
tradeoff at the style site.
Refs #121723
* fix(ui): type the header identity trail segments without a lit result predicate
* fix(gateway): scope the workspace icon route to visible local sessions
Owner-gate the route so it cannot answer for sessions the caller's own
sessions.list would hide, withhold the workspace root of exec-node sessions so
a remote project never resolves to this host's icon, bound SVG icons before
they reach a renderer, and retry ordered Control UI credentials so a stale
saved token no longer silently drops the icon.
Refs #121723
* fix(ui): render the header project as a flat breadcrumb segment, not a badge
Drop the pill border, fill, and radius from the project name so it reads as a
quiet trail segment inline with the session title: one type size across the
trail, hierarchy carried by colour, hover and focus reusing the sibling title
button's tint so removing the border keeps a visible focus indicator.
Refs #121723
* fix(ui): balance the header trail separator spacing after the title pull-back
* test(ui): migrate the shared avatar loader tests to ordered credential arrays
* fix(ui): center the chat header identity trail
* refactor(ui): reuse workspace icon credentials
* test(ui): scope deleted-session recovery to the visible pane
* fix(ui): cache missing workspace icons across renders
* chore: refresh plugin SDK API baseline
#121908 (210aca6de3) added the onPlatformSendDispatch options bag to
final draft-stream updates and updated draft-failures-progress.test.ts,
but missed 19 sibling assertion sites across 6 dispatch test files.
Those files do not currently execute in any CI lane, so main's local
dispatch suite has been red since. Two remaining failures
(context-recovery session-target prefix, draft-failures-progress
preview shape) predate #121908 and are tracked separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>