* fix(gmail): preserve bind errors across watcher output chunks
Carry a bounded stderr tail across data chunks so the address-in-use
marker is detected even when split across chunk boundaries. Without
this, the exit handler misses the bind failure and enters an endless
5-second respawn loop instead of stopping restarts.
* fix(gmail): decide watcher restarts after stderr close
Restart policy waited on exit while Node can still deliver the final
stderr fragment afterward, so a split bind marker could still respawn.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(gmail): classify untruncated combined stderr before bounded tail retention
* fix(gmail): avoid retries after watcher spawn failures
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(doctor): bound legacy launchd cleanup
Bound both legacy launchctl cleanup attempts to five seconds while keeping the existing bootout-then-unload order. Only move the legacy plist after a successful command or an explicit already-unloaded result; leave it in place when timeout or another failure prevents confirmation so a later doctor run can retry. Report filesystem cleanup failures as skipped instead of claiming removal.
* fix(doctor): verify launchd cleanup state before moving plist
Poll a bounded launchctl print postcondition after legacy bootout and unload. Treat loaded, unknown, and timed-out probes as unconfirmed so doctor leaves the plist available for retry.
* test(doctor): clarify launchd cleanup postcondition
* fix(doctor): reject timed-out launchd probes
Preserve timeout evidence from the command runner, including sanitized timeout messages, so partial not-loaded output cannot authorize plist removal.
* fix(doctor): classify no-output launchd timeouts
* test(doctor): trim launchd cleanup cases
Co-authored-by: Alix-007 <li.long15@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(plugin-sdk): bound Anthropic Vertex ADC credential read
canReadAnthropicVertexAdc read the entire GOOGLE_APPLICATION_CREDENTIALS
file via unbounded fs.readFileSync solely to check readability (the
contents are discarded). The provider-local copy of this logic in
extensions/anthropic-vertex/region.ts was already bounded with
tryReadSecretFileSync + a 1 MiB limit in #109260 ("reject oversized
credential files in remaining readers"); this standalone plugin-sdk
preflight helper is a duplicate that sweep missed.
Mirror the region.ts bound: replace the unbounded readFileSync with
tryReadSecretFileSync(..., { maxBytes: 1 MiB, rejectHardlinks: false }),
wrapped in try/catch (the helper throws FsSafeError on oversize) so an
oversized credential file is rejected instead of slurped into memory.
Presence-check semantics are unchanged for normal-sized files.
* refactor(plugin-sdk): remove orphaned vertex auth helper
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(doctor): bound legacy session store read with size cap
* fix(doctor): handle ENOTDIR in legacy store stat preflight
statSync with throwIfNoEntry: false returns undefined for paths whose
parent is a regular file (ENOTDIR) rather than throwing. When a store
appears missing but its parent directory is not a directory, report
store_unreadable instead of silently treating it as an absent store.
* fix(doctor): use descriptor-bound read for legacy session store files
Replace statSync-then-readFileSync with openSync/fstatSync/readFileSync on the
same file descriptor to eliminate the TOCTOU race where a file can change
between size validation and read.
* fix(doctor): bound descriptor read to validated byte count in session store migration
Replace readFileSync(fd, 'utf-8') — which reads to EOF — with a
descriptor-bound loop that reads at most the byte count validated by
fstatSync. This prevents same-inode growth after fstat from bypassing
the 50 MiB per-store cap and causing OOM.
* style(doctor): add braces for curly lint rule
* fix(doctor): remove hard 50 MiB cap to preserve legacy migration compatibility
* fix(doctor): open legacy session store with O_NONBLOCK to prevent FIFO blocking
* refactor(doctor): reuse bounded descriptor reader
* fix(doctor): report descriptor stat failures
---------
Co-authored-by: OpenClaw Agent <agent@openclaw.ai>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gateway): forward tool abort signals
Forward the tool execution cancellation signal to gateway config reads.
Refs #104093; credit @mikasa0818.
Co-authored-by: mikasa0818 <0668001030@xydigit.com>
* style(gateway): format abort signal test
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
A plugin manifest configSchema declaring a __proto__ property polluted
Object.prototype process-wide during ordinary plugin config validation.
applyObjectPropertyDefaults iterated schema-controlled property keys and
wrote into value[key] with no blocked-key guard; because manifests are
parsed JSON, __proto__ is an own key and value["__proto__"] dereferences
the inherited Object.prototype, onto which nested defaults were written.
Skip __proto__, prototype, and constructor at each write site in
applyObjectPropertyDefaults, reusing the existing isBlockedObjectKey
helper already used at other untrusted object-write boundaries.
Channel plugins declare package env triggers in manifests, and ambient
environment variables count as "channel configured". A dev gateway
(gateway run --dev) inherits the operator's shell, so real channel
credentials silently configured channels and could connect development
instances to live services (observed with reef and a Telegram-range
connection during isolated stress testing).
Dev mode now drops presence signals whose only sources are env or
manifest-env across activation planning, auto-enable, autostart, health
recovery, readiness, reloads, and startup warnings. Explicit
channels.<id> config still works, --dev-ambient-channels restores the
old behavior, and startup logs the suppressed channel ids once. Non-dev
gateways are unchanged.
npm 12 wraps `npm view --json` results in a singleton array and keys
`npm pack --json` results by package name instead of returning an entry
array. The install metadata readers assumed the npm <=11 shapes, so on
npm 12 plugin install/update metadata reads failed closed with
incomplete-metadata errors or silently dropped resolved metadata, and
worker release verification rejected published packages.
Normalize both shapes in one shared resolver and use it for plugin
install pack reads and worker bundle release verification.
Co-authored-by: chengzhichao-xydt <264300353+chengzhichao-xydt@users.noreply.github.com>
* fix(ui): actionable error and doctor warning for missing widget sandbox origin
* fix(ui): phrase sandbox-origin diagnostics as conditional guidance
* test(ui): satisfy deadcode and mock-factory gates for sandbox diagnostics
Final sweep of the media-placeholder program: with all eleven producers
migrated, the consumer regexes are dead input and are hard-deleted —
media-understanding token un-mixing (bodies are pure user text now),
the persisted-user-turn placeholder drop, the session-memory strip, and
the inbound-event mediaPlaceholder field with its replacement branch
(an intentional removal of shipped SDK surface per the program's
hard-delete decision: plugins still minting placeholder bodies render
them as literal text until updated). formatMediaPlaceholderText remains
the single owner of the <media:kind> vocabulary, used only for
text-only carriers. Legacy tokens in pre-migration history remain
ordinary text.
* feat(channels): batch 3 producers drop media placeholder bodies
Media-placeholder program batch 3: iMessage, Signal, and WhatsApp stop
minting <media:kind> placeholder bodies. Primary bodies carry captions
only with one aligned structured fact per native attachment (type-only
for missing/rejected/oversized). Echo, quote, and pending-history
records gain structured media facts so dedupe and previews work without
rendered text. Signal media-only outbound sends drop the placeholder
body entirely (signal-cli accepts attachment-only sends; recipients now
see just the media). WhatsApp audio preflight de-stringed; qa-lab
sticker recognizer moved to structured facts; WhatsApp gif maps to
video (presentation mode, no behavioral consumer).
* test(whatsapp): keep media classifier private
Route automatic dashboard titles through the effective session model and auth profile while preserving explicit cross-provider utility ownership and the existing generic title fallback.