Commit graph

12255 commits

Author SHA1 Message Date
Sancho
c884ff64ea fix(agent): keep system-prompt model identity in sync across provider failover
The session-stable system prompt embeds Model:/Provider: identity lines,
but mid-turn failover (try_activate_fallback) swaps the runtime without
touching them, so a fallback model misreports itself as the primary when
asked "what model are you?".

rewrite_prompt_model_identity() rewrites the last occurrence of each line
on _cached_system_prompt when a fallback activates (and back on restore,
byte-identical so the primary's prefix cache still hits). The rewrite is
never persisted to the session DB. _sync_failover_system_message() patches
the in-flight api_messages[0] at all 8 failover sites so the current turn
ships the corrected identity. Cache-safe: the fallback's prefix cache is
cold on a model switch anyway.

Co-authored-by: Hermes Agent <noreply@nousresearch.com>
2026-06-20 10:46:01 -07:00
Teknium
11c6f4c7bc
feat(setup): Blank Slate setup mode — minimal agent, opt in to everything (#36733)
* feat(setup): Blank Slate setup mode — minimal agent, opt in to everything

Adds a third first-time setup option alongside Quick Setup and Full Setup.
Blank Slate forces ON only what an agent needs to run — provider & model,
the File Operations toolset, and the Terminal toolset — and turns
everything else OFF, then walks the user through opting each capability
back in.

What it does:
- platform_toolsets.cli = [file, terminal] (explicit, authoritative list)
- agent.disabled_toolsets = every other known toolset (web, browser,
  code_execution, vision, memory, delegation, cronjob, skills, image_gen,
  kanban, …). Applied last in the resolver, so it overrides the
  non-configurable platform-toolset recovery that would otherwise re-add
  toolsets like kanban — guaranteeing a true blank slate.
- Optional config features off: compression, memory + user-profile capture,
  checkpoints, smart model routing, auto session reset.
- Bundled skills default to NONE (reuses the .no-bundled-skills marker);
  offers to seed the full catalog.
- Walks through tools / plugins / MCP / messaging, all opt-in.

Proven end-to-end: with the Blank Slate config, model_tools.get_tool_definitions
emits exactly 6 schemas — patch, process, read_file, search_files, terminal,
write_file. Nothing else reaches the model.

Re-enable later via hermes tools / hermes skills opt-in --sync /
hermes setup agent.

Tests: tests/hermes_cli/test_setup_blank_slate.py (8 tests) pin the writers,
the resolver invariant ({file, terminal}), and the 6-schema end-to-end set.
Docs: getting-started/quickstart.md documents all three setup modes.

* feat(setup): Blank Slate fork — finish minimal, or walk through configs

After applying the minimal baseline (provider/model + file + terminal,
everything else off), Blank Slate now presents a choice instead of always
running the full walkthrough:

  1. Start with everything disabled — finish now with the minimal agent.
  2. Walk through all configurations — opt in to tools, skills, plugins, MCP,
     and messaging.

Provider/model and terminal are still configured first either way (the agent
can't run without them). The finish-now path records the bundled-skill opt-out
so future `hermes update` runs don't re-inject skills. The walkthrough body
moved to a separate _blank_slate_walkthrough() helper.

Tests: TestBlankSlateFork covers both branches (finish-now applies baseline +
skill opt-out and skips the walkthrough; walkthrough path invokes it). Docs
updated to describe the fork.
2026-06-20 10:45:55 -07:00
teknium
838daca9f4 chore(desktop): format tooltip indentation + author map for #49697
Re-indent the salvaged title= lines to spaces (prettier), and map
alelpoan@proton.me in the release author map.
2026-06-20 10:45:14 -07:00
alelpoan
404fe730b7 fix: add tooltips to right sidebar header buttons 2026-06-20 10:45:14 -07:00
Teknium
c329279482 test: retarget source-path refs to migrated plugin paths
test_telegram_webhook_secret reads telegram adapter source by path; point it
at plugins/platforms/telegram/adapter.py. test_windows_native_support
npm-spawn parametrization referenced gateway/platforms/whatsapp.py; point it at
plugins/platforms/whatsapp/adapter.py.
2026-06-20 10:26:45 -07:00
Teknium
5600105478 refactor(gateway): migrate slack/dingtalk/whatsapp/matrix/feishu/telegram/wecom/email/sms adapters to bundled plugins
Salvage of PR #41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
2026-06-20 10:26:45 -07:00
kshitij
2ab09a6c50
Merge pull request #49680 from NousResearch/fix/signal-quote-cache-eviction
fix(signal): FIFO-evict the quote-detection timestamp cache (follow-up to #49678)
2026-06-20 21:06:01 +05:30
kshitijk4poor
26d9a3c710 fix(signal): FIFO-evict the quote-detection timestamp cache
`_sent_message_timestamps` (the reply-to-own-message quote cache) used a
`set` evicted with `set.pop()`, which removes an ARBITRARY element — so once
more than the cap (500) outbound timestamps are tracked, a still-recent
timestamp could be dropped while older ones survive, missing a genuine
reply-to-own-message. Convert it to an OrderedDict with FIFO (oldest-first)
eviction, mirroring the recently-hardened echo ring (#31250). This closes the
same bug class on the sibling cache.

Adds a regression test asserting oldest-first eviction + MRU promotion.
2026-06-20 21:00:46 +05:30
kshitij
85ad7c9b0a
Merge pull request #49678 from NousResearch/salvage/signal-echo-ring
fix(signal): salvage echo-ring LRU+TTL hardening (#31250)
2026-06-20 20:56:43 +05:30
kshitijk4poor
e49272fe53 chore(release): map w31rdm4ch1nZ contributor email to GitHub login 2026-06-20 20:51:41 +05:30
kshitijk4poor
2f86283217 test(signal): update echo-discard test for OrderedDict ring
The hardened echo ring (#31250) changes _recent_sent_timestamps from a set
to an OrderedDict, so the reply-detection-cache regression test from the quote
salvage can no longer call .discard(); route it through the new
_consume_sent_timestamp() helper, which is the real echo-removal path.
2026-06-20 20:51:01 +05:30
w31rdm4ch1nZ
332f88f6a6 fix(signal): harden recently-sent echo ring with LRU + TTL 2026-06-20 20:50:52 +05:30
kshitij
b88d0007c9
Merge pull request #49583 from NousResearch/salvage/signal-mention-typing
fix(signal): salvage self-mention strip + explicit stop-typing RPC (batch of #31217, #40054)
2026-06-20 16:32:30 +05:30
kshitijk4poor
32a97a20af fix(signal): strip self-mention in all groups, not just require_mention
Review follow-up on the salvaged self-mention strip (#31217): the original
only stripped the bot's rendered @<number>/@<uuid> self-mention inside the
`require_mention=true` branch, so groups with require_mention=false still
leaked it into the agent text. Hoist the strip to run for every group message
(fixing the whole bug class), and collapse the doubled space a mid-sentence
removal leaves while preserving intentional newlines.
2026-06-20 16:27:28 +05:30
kshitijk4poor
ef7e716930 chore(release): map rratmansky contributor email to GitHub login 2026-06-20 16:24:15 +05:30
Kailigithub
40b6ac9ac7 fix(signal): send explicit stop-typing RPC when cancelling indicator 2026-06-20 16:23:41 +05:30
Rick Ratmansky
96b10327b6 fix(signal): strip bot self-mention from group messages before agent dispatch 2026-06-20 16:23:41 +05:30
kshitij
65561e9de6
Merge pull request #49563 from NousResearch/salvage/signal-quote-history
Some checks failed
Deploy Site / deploy-vercel (push) Waiting to run
Deploy Site / deploy-docs (push) Waiting to run
Docker Build and Publish / build-amd64 (push) Waiting to run
Docker Build and Publish / build-arm64 (push) Waiting to run
Docker Build and Publish / merge (push) Blocked by required conditions
Lint (ruff + ty) / ruff + ty diff (push) Waiting to run
Lint (ruff + ty) / ruff enforcement (blocking) (push) Waiting to run
Lint (ruff + ty) / Windows footguns (blocking) (push) Waiting to run
Tests / test (1) (push) Waiting to run
Tests / test (2) (push) Waiting to run
Tests / test (3) (push) Waiting to run
Tests / test (4) (push) Waiting to run
Tests / test (5) (push) Waiting to run
Tests / test (6) (push) Waiting to run
Tests / save-durations (push) Blocked by required conditions
Tests / e2e (push) Waiting to run
Typecheck / typecheck (apps/bootstrap-installer) (push) Waiting to run
Typecheck / typecheck (apps/desktop) (push) Waiting to run
Typecheck / typecheck (apps/shared) (push) Waiting to run
Typecheck / typecheck (ui-tui) (push) Waiting to run
Typecheck / typecheck (web) (push) Waiting to run
Typecheck / desktop-build (push) Waiting to run
OSV-Scanner / Scan lockfiles (push) Has been cancelled
uv.lock check / uv lock --check (push) Has been cancelled
fix(signal): salvage quoted-reply context (#46388)
2026-06-20 15:36:02 +05:30
lkz-de
96db7c6883 fix(signal): preserve quoted reply context
Carry Signal quote metadata through gateway events so replies to assistant messages include the quoted context without personalizing comments.
2026-06-20 15:16:53 +05:30
kshitij
ff50a88617
Merge pull request #49558 from NousResearch/salvage/env-var-guards-48735 2026-06-20 15:11:54 +05:30
kshitij
834bbae895
Merge pull request #49530 from NousResearch/salvage/signal-trio
refactor(signal): salvage AAC voice-note remux + shared markdown formatting (batch of #47766, #46386)
2026-06-20 15:08:23 +05:30
kshitijk4poor
467c879b2e chore(release): map lkz-de contributor email to GitHub login
The contributor-check CI auto-resolves only the +id form of GitHub noreply
emails; lkz-de's commits use the legacy plain form
(lkz-de@users.noreply.github.com), so add an explicit AUTHOR_MAP entry.
2026-06-20 15:03:29 +05:30
kshitijk4poor
a7dd98c860 fix(env): guard remaining malformed int/float env var casts with utils helpers
Widen the env_float() guard from #48735 across the whole bug class: a
non-numeric value (e.g. a stale .env "HERMES_API_TIMEOUT=abc" or a typo'd
port) raised an unhandled ValueError and crashed adapter/agent init.

Converts 22 genuinely-unguarded first-party int/float(os.getenv()) sites to
the canonical utils.env_int / utils.env_float helpers (the established house
pattern), instead of duplicating per-module helpers or inline try/except:

- gateway/config.py: WECOM_CALLBACK_PORT, BLUEBUBBLES_WEBHOOK_PORT
- gateway/platforms/email.py: EMAIL_IMAP/SMTP_PORT, EMAIL_POLL_INTERVAL
- gateway/platforms/feishu.py: dedup cache + text/media batch settings
- gateway/platforms/wecom.py, discord/adapter.py: text batch delays
- gateway/platforms/telegram.py: media batch delay, TELEGRAM_WEBHOOK_PORT
- gateway/platforms/whatsapp.py: WHATSAPP_NPM_INSTALL_TIMEOUT
- hermes_cli/auth.py: CODEX/XAI refresh timeouts
- agent/chat_completion_helpers.py: API/stream read/stale timeouts
- run_agent.py, agent/auxiliary_client.py: API + nous timeouts

Sites already guarded by try/except or local helpers are left untouched.
The HERMES_MAX_ITERATIONS sites are already guarded on main via
_current_max_iterations(), so they are not included.
2026-06-20 14:54:36 +05:30
xxxigm
7eb9678c54 test(desktop): cover link-title window audio muting
Verify createLinkTitleWindow mutes audio (regression guard for #49505) and
keeps the hardened offscreen defaults, and register the new test file in the
desktop platforms test script.
2026-06-20 14:53:05 +05:30
xxxigm
ae8db1ab53 fix(desktop): mute hidden link-title window so historical links don't autoplay audio
Tier-2 link-title resolution loads the URL in an offscreen BrowserWindow to
read its <title> when curl can't. That window was never muted, so pages that
autoplay media (e.g. YouTube `watch` URLs) leaked ~2s of audio every time a
session containing such links was re-rendered. Move the window creation into a
dedicated helper that calls `webContents.setAudioMuted(true)` immediately after
construction, so the offscreen probe can never emit sound.

Fixes #49505
2026-06-20 14:53:05 +05:30
kshitijk4poor
abafba0762 refactor(signal): correct STT-fallback comment, type the markdown wrapper, make AAC test portable
Review follow-up on the salvaged AAC + markdown changes:
- Fix an inaccurate comment claiming the STT layer has a sniff-and-remux
  fallback (verified: no such fallback exists; the ffmpeg-absent path caches
  raw ADTS and STT may reject it).
- Type the _markdown_to_signal wrapper as tuple[str, list[str]] to match the
  shared helper instead of a bare tuple.
- Replace the hardcoded /home/pi/... test fixture with a runtime-generated
  ADTS AAC sample so the remux round-trip actually runs in CI (skips only
  when ffmpeg is absent) instead of always-skipping.
2026-06-20 14:24:29 +05:30
annguyenNous
06ca1e9980 fix(utils): add env_float helper for safe float env var parsing
Mirrors the existing env_int() helper: returns the default when the
variable is unset or non-numeric instead of raising ValueError. Used by
the follow-up commit to guard malformed float env vars across the gateway.

Salvaged from #48735 (@annguyenNous). The PR's api_server.py change is
now redundant — main guards HERMES_MAX_ITERATIONS via
_current_max_iterations().
2026-06-20 14:00:07 +05:30
jasnoorgill
da34fca2bb fix(signal): detect ADTS AAC voice notes and remux to MP4
Android Signal delivers voice notes as raw ADTS AAC frames, which
share the `0xFF 0xFx` sync word with MPEG-1/2 Layer 3 (MP3). The
`_guess_extension` byte-signature test in gateway/platforms/signal.py
was matching both, so ADTS AAC was being misclassified as MP3 — saved
to disk with the wrong extension and rejected by every major STT API
(Groq, OpenAI) because their server-side format sniffers inspect the
actual codec, not the file extension.

Two changes:

1. Tighten the MP3 vs ADTS disambiguator. ADTS packs `ID`,
   `layer`, and `protection_absent` into bits 3-0 of byte 1, where
   `ID=0` and `layer=00` for AAC. Real MP3 has `ID=1` and
   `layer` in {01, 10, 11}. The mask `0xF6` against target `0xF0`
   cleanly separates them.

2. Remux raw ADTS AAC to MP4 container at the cache step via
   `ffmpeg -c:a copy`. Single demux/remux, no re-encode, no quality
   loss, sub-100ms on a Pi 5. The cached file is a normal `.m4a`
   that all major STT providers accept. ffmpeg is a transitive
   dependency of many other Hermes features (TTS, video skills) so
   this isn't a new install requirement; the remux degrades
   gracefully to a no-op if ffmpeg is missing.

The new helper `_remux_aac_to_m4a` is unit-tested with a real
Android voice note from the audio cache that originally triggered
the bug, plus synthetic ADTS frames for the byte-level
disambiguator and garbage-input graceful failure.

Closes the gap that broke transcription for any Android Signal user
sending voice messages to Hermes.
2026-06-20 13:48:05 +05:30
lkz-de
905820b59f fix(signal): share markdown formatting across send paths
Route Signal send paths through shared markdown formatting helpers and render markdown bullets consistently as Unicode bullets. Add coverage for Signal formatting and send_message integration.
2026-06-20 13:47:14 +05:30
brooklyn!
15852722d4
feat(desktop): pop the composer out into a draggable floating window (#49488)
* feat(desktop): pop the composer out into a draggable floating window

Gesture-driven: drag the docked composer up to peel it out, drag it back to
the bottom-center dock zone (radial glow ramps with proximity) to redock, and
double-click the grab area to toggle. Floating composer is compact, grows
upward as it wraps, and can be moved by its 5px transparent grab platform
(diagonal hatch on hover). Position + popped state persist; secondary windows
always start docked. rAF-coalesced drag, persisted only on release.

* fix(desktop): keep floating composer radius consistent with docked

* fix(desktop): composer popout polish — peel-off placement, panels, chip editing

- Peel-off undock drops the floating composer under the cursor (centered
  horizontally, preserving the vertical grab offset) instead of snapping to
  the docked corner.
- Unify the / · @ · ? completion drawer and the attach (+) menu onto one
  shared glassy panel primitive (composerPanelCard): smallest theme font,
  hairline border, nous shadow; floats off the composer, inset from the left.
- Directive chips: Backspace removes the chip + its auto-inserted trailing
  space atomically (no orphaned space), and a phantom trailing block left by
  contenteditable no longer falsely expands the composer to two rows.
- Model picker: scroll area capped at max(150px, 30dvh); footer rows aligned
  (matching icons, dropped a redundant margin).
- Composer focus shifts the border ~15% toward foreground (no fill change);
  input is cursor-text; trimmed control icon/button sizes.
2026-06-20 02:16:25 -05:00
Brooklyn Nicholson
eed78d6ebb fix(desktop): composer popout polish — peel-off placement, panels, chip editing
- Peel-off undock drops the floating composer under the cursor (centered
  horizontally, preserving the vertical grab offset) instead of snapping to
  the docked corner.
- Unify the / · @ · ? completion drawer and the attach (+) menu onto one
  shared glassy panel primitive (composerPanelCard): smallest theme font,
  hairline border, nous shadow; floats off the composer, inset from the left.
- Directive chips: Backspace removes the chip + its auto-inserted trailing
  space atomically (no orphaned space), and a phantom trailing block left by
  contenteditable no longer falsely expands the composer to two rows.
- Model picker: scroll area capped at max(150px, 30dvh); footer rows aligned
  (matching icons, dropped a redundant margin).
- Composer focus shifts the border ~15% toward foreground (no fill change);
  input is cursor-text; trimmed control icon/button sizes.
2026-06-20 02:10:38 -05:00
kshitijk4poor
a6f08ff0c8 docs(delegate): clarify subagent model is config-level, not per-call
delegate_task has never exposed a per-call model parameter (removed
intentionally in fb0f579b1). The tool description gave no hint about how
subagent model is actually controlled, so users kept expecting a model
arg and filing it as a dropped/ignored param (e.g. #49332, #23467).

Add one bullet to the dynamically-built tool description stating that
children inherit the parent model + fallback chain, and that pinning all
subagents to a specific model is done via delegation.provider /
delegation.model in config.yaml. No behavior change.
2026-06-20 12:13:39 +05:30
Brooklyn Nicholson
f697c97e02 fix(desktop): keep floating composer radius consistent with docked 2026-06-20 01:36:29 -05:00
Brooklyn Nicholson
236f0597e5 feat(desktop): pop the composer out into a draggable floating window
Gesture-driven: drag the docked composer up to peel it out, drag it back to
the bottom-center dock zone (radial glow ramps with proximity) to redock, and
double-click the grab area to toggle. Floating composer is compact, grows
upward as it wraps, and can be moved by its 5px transparent grab platform
(diagonal hatch on hover). Position + popped state persist; secondary windows
always start docked. rAF-coalesced drag, persisted only on release.
2026-06-20 01:35:30 -05:00
helix4u
c253b07380 fix(model): clear stale endpoint credentials across switches 2026-06-19 19:58:26 -07:00
helix4u
95a3affc2e fix(model): keep Nous picker from restoring stale custom keys 2026-06-19 19:58:26 -07:00
Harish Kukreja
1b7b4d138a
fix(desktop): handle slash exec dispatch payloads (#49358) 2026-06-19 21:11:16 -05:00
Gille
857d0244af
fix(tui): handle dispatch payloads from slash exec (#49337) 2026-06-19 20:05:58 -05:00
Teknium
cf58f1a520
feat(titles): support language-aware title generation (#45296)
Make auxiliary title prompts match the user language by default, with an optional pinned `auxiliary.title_generation.language` config.
2026-06-19 17:15:52 -07:00
ruangraung
8cf7df867e fix(plugins): silence raft check_fn log spam for users without raft CLI
The raft platform plugin's check_raft_requirements() logged a WARNING every
time it returned False. Since check_fn is called on every load_gateway_config()
(~every 10s during normal gateway operation), users who don't have the raft
CLI installed get their logs flooded with no way to suppress it — hermes plugins
disable doesn't work for bundled platform plugins, and platforms.raft.enabled:
false doesn't gate the check_fn call.

Fix: make check_raft_requirements() a silent predicate (return True/False
only, no logging), matching the convention documented and used by other
platform adapters (e.g. teams/adapter.py). The caller in
gateway/platform_registry.py create_adapter() already emits its own warning
when requirements aren't met and an adapter is actually requested — that's the
correct place for a user-facing warning (fires once per connect attempt, not
once per config load).

Fixes #49234
2026-06-19 17:12:58 -07:00
joaomarcos
75ed07ace8 fix(gateway): break the restart loop at the source on session resume
When a tool call itself restarts the gateway (docker restart, systemctl
restart, and similar), the process is terminated mid-call — before the
tool result is persisted and before the orderly drain rewind can run. The
transcript tail is left as an assistant(tool_calls) with no matching tool
answer. On resume the model re-issues the unanswered call, taking the
gateway down again — an infinite loop (#49201).

Source fix: _build_gateway_agent_history now strips a trailing
assistant(tool_calls) block that has no tool answers
(_strip_dangling_tool_call_tail), so there is nothing for the model to
re-execute. This complements _strip_interrupted_tool_tails, which only
handles the case where a tool result row exists with an interrupt marker.

Cognitive backstop: the resume-pending system note now states that any
restart command in the history already ran and must not be re-executed or
verified, and the empty-message auto-resume startup turn reports recovery
and asks for instructions instead of the nonsensical "address the user's
NEW message" (there is no new message on that turn).

Reimplements the intent of #49243 by @JoaoMarcos44 at the replay layer.

Fixes #49201
2026-06-19 16:59:58 -07:00
teknium1
6504f51cd5 chore: add @hakanpak to AUTHOR_MAP for PR #49282 salvage 2026-06-19 16:59:54 -07:00
hakanpak
d45addc2f1 fix(tools): never let a model whitelist strip the prompt / source images
_build_fal_payload and _build_fal_edit_payload assemble the request and then
filter it down to the model's supports / edit_supports whitelist. That filter
also covers prompt (and image_urls for edits), which every FAL endpoint
requires. Today all model configs happen to list those keys, but a single
config that omits one would silently produce a request with no prompt or no
source images — a broken generation with no error.

Always keep the mandatory keys regardless of the whitelist so a missing
whitelist entry can only drop optional knobs, never the prompt or the images.
2026-06-19 16:59:54 -07:00
sprmn24
8ebe37f6ad feat(desktop): notify renderer when GPU acceleration is disabled due to remote display
Remote displays (RDP/SSH/X11) silently disable GPU hardware acceleration with
only a console.log, leaving the user unaware that software rendering is
active. Expose the detected reason over IPC and surface a dismissible banner
in the renderer.
2026-06-19 16:59:47 -07:00
teknium1
64b21e50fb fix(cli): publish agent ref to cli module so memory on_session_end fires on exit
The god-file Phase 4 refactor (094aa85c37) moved agent construction into
CLIAgentSetupMixin, which set the atexit shutdown reference with a bare
`global _active_agent_ref`. After extraction that global binds the *mixin
module's* namespace, not cli.py's. cli._run_cleanup reads
cli._active_agent_ref to decide whether to fire the memory provider's
on_session_end hook — and it stayed None for the whole session, so the
`if _active_agent_ref:` branch was dead and on_session_end never ran on
/exit. Custom memory providers silently lost end-of-session extraction.

Fix: publish the reference onto the cli module explicitly
(`import cli as _cli; _cli._active_agent_ref = self.agent`), using the
deferred-import pattern already established in the mixin.

Regression test asserts cli._active_agent_ref is populated by the mixin's
publish line and guards against a relapse to the bare `global` form. The
existing shutdown tests passed only because they hand-assigned the ref,
which is exactly what masked this.
2026-06-19 16:59:43 -07:00
Gille
013f9c8750 fix(memory): log CLI shutdown hook failures
Makes the CLI memory-provider shutdown path observable: log when CLI
cleanup calls memory shutdown (with session id + message count), warn
instead of swallowing CLI memory-shutdown exceptions, warn on
on_session_end failures during agent shutdown, and raise the
MemoryManager provider-hook failure log from debug to warning with a
traceback.

Salvaged from PR #49287 (authored by Gille / @helix4u).
2026-06-19 16:59:43 -07:00
teknium1
c1a0b6a5f1 style: strip trailing whitespace in cron scheduler live-adapter block
Follow-up on salvaged PR #49280.
2026-06-19 16:59:38 -07:00
joaomarcos
3a6c171e9e fix(gateway): log signal transport response and bubble cron live adapter errors 2026-06-19 16:59:38 -07:00
joaomarcos
5649b8649a Fix silent delivery failures in Signal live adapter (#49260) 2026-06-19 16:59:38 -07:00
Teknium
5f55f0ff85
feat(teams): native send_video/send_voice/send_document attachments (#49308)
Teams overrode send_image/send_image_file but not send_video, send_voice,
or send_document — so when the gateway dispatched a video/voice/document
reply to a Teams chat it fell through to the base-class text fallback and
sent the local file path as plain text (same broken-UX class as the LINE
URL-image gap in #49298).

Extract the existing send_image attachment logic into a shared
_send_media_attachment helper (remote URL by reference, local file as a
base64 data URI, MIME guessed from the path) and route all four media
kinds through it. 5 new tests cover remote-URL, local-file base64,
no-app, and missing-file paths.
2026-06-19 16:20:59 -07:00