* feat(status): render /status as native rich tables on channels that support them
* feat(status): tighten rich /status layout into a titled native table card
* feat(status): trim rich /status tail to one clock-and-uptime context line
* feat(status): context meter, hot-window warning, and default-noise trim in rich card
* test(status): satisfy SessionEntry sessionId in meter fixture
* fix(telegram): gate rich table islands off legacy HTML sends and cover payload sends
* test(telegram): split outbound-adapter presentation tests to satisfy max-lines
* fix(outbound): make presentation capability resolution formatting-aware
* fix(telegram): defer presentation canonicalization on rich accounts until send
* feat(status): one fact per line in the plain status body
* feat(status): group the plain status body into blank-line sections
* feat(status): lead the rich status card with the version title
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* [AI] fix(diagnostics-otel): keep telemetry exporting across in-process restarts
Owned diagnostics-otel generations previously registered their OpenTelemetry
providers globally and only shut the providers down on stop. The pinned
sdk-node registers globals without override and never unregisters them, so a
second in-process generation (config-watcher reload, OPENCLAW_NO_RESPAWN=1,
containers) kept exporting through the first, already-shutdown providers and
telemetry silently stopped.
Switch owned mode to private BasicTracerProvider + MeterProvider instances
whose handles are injected directly into the existing recorder runtime, and
keep the preloaded (OPENCLAW_OTEL_PRELOADED=1) mode on the host's global
providers without ever registering or replacing globals. Resource detection
honors the pinned NodeSDK OTEL_NODE_RESOURCE_DETECTORS contract (unset
defaults to env+process+host; none/subset/all are respected), and the unused
@opentelemetry/sdk-node dependency is removed with a minimal lockfile update.
Adds a real-SDK two-generation restart regression, real-SDK resource-detector
selection coverage, migrates the unit mocks to the provider lifecycle, and
updates the OpenTelemetry docs.
Fixes#119997
Co-Authored-By: glm-5.2 <noreply@anthropic.com>
* [AI] test(diagnostics-otel): restore OpenTelemetry globals after restart test
The restart regression disables global context, metrics, propagation, and
trace APIs and forces OPENCLAW_OTEL_PRELOADED=0, but its cleanup only reset
diagnostic events, so a later test in the same worker could inherit no-op
providers. Snapshot the prior global registrations and preloaded env at
module load, and re-register/restore them in afterEach, matching the
existing exporter-health integration test pattern.
Co-Authored-By: glm-5.2 <noreply@anthropic.com>
* [AI] fix(diagnostics-otel): reconcile private providers with OTEL_SDK_DISABLED
Rebase onto main after #119961 (honor OTEL_SDK_DISABLED) changed the
disabled admission path to return before SDK construction. Restore the
removed getBooleanFromEnv import, drop the stale ownedNodeSdkDisabled
guard, and update disabled-mode tests to the new semantics (all routes
off, disabled runtime registered) plus the private-provider signal
path for the integration suite.
Related to #119997
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(diagnostics-otel): isolate restartable provider generations
* test(diagnostics-otel): prove same-pid generation routing
* test(diagnostics-otel): use supported watcher reload mode
* test(diagnostics-otel): satisfy ownership proof gates
* test(diagnostics-otel): use managed proof cleanup
* test(diagnostics-otel): verify injected trace ancestry
---------
Co-authored-by: glm-5.2 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(slack): scope thread-starter cache per workspace and persist channel-id migration durably
Thread-starter cache keys now always include accountId+teamId so multi-workspace
installs cannot cross-read cached thread starters. channel_id_changed migration
previews against the persisted config snapshot and only mutates the in-memory
monitor config after the durable write succeeds; new-channel ingress traffic
serializes behind the migration lane via new_channel_id.
* chore: re-fire CI
* chore: re-fire CI against fixed main baseline
* fix(acpx): stop/start races cannot resurrect a stopped runtime backend
The deferred acpx runtime service published its lazily started backend without
lifecycle ownership checks: a stop during in-flight activation let the stale
service become the active runtime, and stop cleared shared state while startup
still used it. Start/stop now use a lifecycle revision; only the current
revision may publish a runtime, stop invalidates every deferred proxy before
awaiting in-flight startup, and restart waits for the previous stop to settle.
* chore: re-fire CI
* fix(acpx): fence deferred runtime ownership
* refactor(acpx): centralize backend lifecycle ownership
* fix(acpx): annotate deferred runtime cycle
* fix(qqbot): recover credentials from backup deterministically at startup
Credential recovery previously keyed off a loose truthiness check that
misclassified partially configured accounts; recovery state is now a closed
discriminated union (configured/recoverable/partial/missing) so a live-config
wipe restores from the per-account backup only when the account is actually
recoverable, and partial configs surface a clear diagnostic instead of a
silent skip.
* chore: re-fire CI
* refactor(test): exercise real Teams monitor lifecycle
* refactor(test): use real gateway lifecycle state
* refactor(test): use real restart delivery queue
* test: type Teams SDK boundary fixture
* fix(google): accept base64url in Live audio
* fix(google): normalize base64url in one pass
* fix(google): normalize base64url at every Google media boundary
* fix(google): normalize image media and avoid double music validation
* test(google): exercise URL-safe base64 fixtures
---------
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
* fix(gateway): make doctor dreaming timestamp comparators NaN-safe
compareDreamingEntryByRecency and compareDreamingEntryByPromotion used
`Number.isFinite(aMs) || Number.isFinite(bMs)` then `return bMs - aMs`.
When exactly one side parsed to NaN (non-empty malformed timestamp), the
guard still entered the branch and returned NaN - bMs, producing NaN and
leaving Array.sort order undefined for the doctor.memory.status dreaming
entry list. Coerce unparseable timestamps to -Infinity via a shared
parseDreamingTimestampMs helper and return a finite -1/0/1 from the
timestamp branch so the sort stays deterministic.
* fix(memory-core): incorporate producer-side NaN-safe timestamp comparator
ClawSweeper [P1] finding: gateway-only fix is incomplete because Memory Core
has the same mixed-NaN comparator BEFORE the gateway merges lists. A valid
entry discarded upstream cannot be recovered at the gateway boundary.
Incorporate the producer-side fix:
- Export compareStoreTimestampDesc from short-term-promotion-utils.ts
- Replace the buggy raw Date.parse + bMs-aMs pattern in
short-term-promotion-stats.ts with the NaN-safe compareStoreTimestampDesc
This makes #118749 self-contained: both gateway doctor.ts AND memory-core
stats.ts comparators are NaN-safe. The companion #118750 now only needs to
cover the recency ranking fix (ageDays/recency calculation).
* fix(memory): preserve valid dreaming diagnostics
* style(memory): format dreaming imports
---------
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
Preserve native Telegram quote attribution in agent-visible input and terminate quotes before following ordinary text.
Co-authored-by: Peter Steinberger <steipete@macos.shared>
* fix(test): close zalo agent state databases before lifecycle temp-dir removal (#119796)
* fix(test): close zalouser agent state databases before ingress temp-dir removal (#119796)
* fix(test): close zalouser fixture databases before restoring parent state env (#119796)
ClawSweeper re-review finding: the ingress fixture restored
OPENCLAW_STATE_DIR before closing cached per-agent databases, so lease
release could open a shared-state write against the parent fixture env.
Move both closes ahead of the env restore (canonical fixture order) and
add a nested-environment regression asserting the parent state dir stays
untouched.
Keep Telegram native and typed callback routes under their owning handlers. Expired or malformed typed buttons now clear their controls and return visible unavailable feedback while legacy raw plugin callbacks remain compatible.