Commit graph

13314 commits

Author SHA1 Message Date
x7peeps
df4015bbc1 docs: session lifecycle documentation 2026-06-20 23:23:47 -07:00
e10552
2609bcccca feat(i18n): add complete Spanish translation
- Complete README.es.md (full Spanish translation of README)
- Add CONTRIBUTING.es.md (Spanish contributing guide)
- Add SECURITY.es.md (Spanish security policy)
- Fix remaining English strings in locales/es.yaml (resume Matrix section)
- Add Spanish badge to README.md

All 47 i18n tests pass, including catalog key parity and placeholder parity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 23:23:47 -07:00
Sworntech-dev
38756f2d55 docs(docker): document gateway tool-loop hard stops 2026-06-20 23:23:47 -07:00
GauravPatil2515
cc30e0b659 docs(config): document auxiliary task fallback_chain 2026-06-20 23:23:47 -07:00
Greg DeYoung
5eb158e317 docs(hermes-agent skill): document project context files and their discovery rules
Adds a new 'Project Context Files' section to the hermes-agent skill
explaining the priority order and discovery rules for .hermes.md,
AGENTS.md, CLAUDE.md, and .cursorrules. Specifically clarifies:

- .hermes.md walks parents up to the git root (good for monorepos)
- AGENTS.md / agents.md is cwd-only (portable to other agents)
- The 20K cap and head+tail truncation strategy
- The threat-pattern scanner behavior (blocks content, not file)
- What --ignore-rules actually skips (everything)

Also fixes an inaccurate docstring in agent/agent_init.py for
skip_context_files — the previous text only mentioned SOUL.md,
AGENTS.md, and .cursorrules, but the actual behavior (per
build_context_files_prompt and the --ignore-rules CLI flag) skips
all of them plus .hermes.md and CLAUDE.md.

Refs: https://github.com/NousResearch/hermes-agent/issues/46775
2026-06-20 23:23:47 -07:00
Andres Sommerhoff
97563ab821 fix: warn on line-oriented newline search patterns 2026-06-20 23:23:47 -07:00
Andres Sommerhoff
eb9a002284 docs: clarify search_files newline regex behavior 2026-06-20 23:23:47 -07:00
lkz-de
6403ed06b3 docs(session-search): document source-first retrieval limits
Clarify that session_search is secondary context and direct source identifiers must be inspected first when accessible. Add regression coverage for the tool description.
2026-06-20 23:23:47 -07:00
BBCrypto-web
1eb2959309 docs(.env.example): add missing ELEVENLABS_API_KEY placeholder 2026-06-20 23:23:47 -07:00
skyc1e
46cc0345ae docs(skills): add hermes-agent verification rule 2026-06-20 23:23:47 -07:00
teknium1
8ac5e90ec2 fix(gateway): dedup image_generate media across the compression boundary
After context compression, the agent re-sent an already-delivered
generated image on every subsequent turn (#46627). The auto-append
fallback rescans full history when the message list shrinks (compression-
safe path), deduping against _history_media_paths — but that set was built
by scanning ONLY MEDIA: text tags in tool results. image_generate returns
its path in a JSON payload field (host_image/image/agent_visible_image),
never a MEDIA: tag, so generated-image paths never entered the dedup set
and were re-emitted after the boundary.

Extract the history-path collection into _collect_history_media_paths(),
which now covers BOTH delivery shapes: MEDIA: text tags AND image_generate
JSON-payload paths (mirroring what _collect_auto_append_media_tags
extracts). The inline block in _handle_message is replaced with a call to
the helper.

Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
2026-06-20 23:20:16 -07:00
teknium1
1f874dfe44 fix(compression): stop fallback summary triplicating the latest user ask
When LLM summarization fails, the deterministic fallback summary rendered
the latest user ask (active_task = "User asked: '<ask>'") verbatim under
THREE headings — Historical Task Snapshot, Historical In-Progress State,
and Historical Pending User Asks. Re-presenting an already-handled ask as
unresolved in-progress/pending work made the model re-answer it AND treat
the resurrected ask as the active turn, burying the genuinely-new
post-compaction user message (#49307: answer repetition + new-instruction
loss, P1).

Keep the latest ask once, under Task Snapshot, as historical context only.
The In-Progress and Pending-Asks sections now say 'Unknown / None
recoverable from deterministic fallback' (consistent with the Active
State / Key Decisions / Resolved Questions sections) and explicitly note
the ask is historical, not outstanding. The raw turn text still appears in
the verbatim 'Last Dropped Turns' transcript — that's the dropped-turn
record, not a re-labeled instruction.

Note: the separate role=assistant standalone-summary regurgitation
(#33256) is left as-is — that role choice is constrained by strict message
alternation (user collides with a user-ending head) and is already
mitigated by the summary end-marker; forcing the role would risk the
alternation invariant.

Co-authored-by: r266-tech <r2668940489@gmail.com>
Co-authored-by: kyssta-exe <kyssta-exe@users.noreply.github.com>
2026-06-20 23:19:27 -07:00
teknium1
2f3177adf4 fix(compression): protect the summary call from mid-flight interrupts
Context compression is atomic, but a gateway interrupt (an incoming user
message while the agent is busy) could abort the in-flight summary call.
The Codex Responses aux stream polls the thread interrupt flag and raised
InterruptedError unconditionally — so compression fell back to a degraded
static 'summary unavailable' marker, losing the real handoff (#23975).

Add a thread-local interrupt-protection flag (aux_interrupt_protection
context manager) in auxiliary_client; the Codex stream's cancellation
check honors it. The compressor wraps its summary call_llm in the context
manager. Timeouts still fire (a hung call must die) and all other aux
tasks (vision, web_extract, title_generation, …) stay interruptible.
Re-entrant, so the main-model retry recursion is safe.

Co-authored-by: konsisumer <der@konsi.org>
2026-06-20 21:32:30 -07:00
Teknium
4b7f9a4d30
test(matrix): make voice-detection tests hermetic against mention gating (#49946)
test_matrix_voice flaked in CI (6/7 failing on some shards, passing on
others and on main) depending on leaked MATRIX_REQUIRE_MENTION env state.

Root cause: the adapter defaults require_mention=True (falling back to the
MATRIX_REQUIRE_MENTION env var). These tests fire a group-room audio event
with no @mention, so _resolve_message_context drops it before dispatch
('No event was captured') whenever require_mention resolves True — which
happens in a clean shard, but an earlier test in another shard can leave
MATRIX_REQUIRE_MENTION=false in os.environ and mask it. The plugin
migration (#5600105478 adapter→bundled plugin) shifted shard composition
and exposed it.

Pin require_mention: False in the test adapter config so these media-TYPE
detection tests are no longer gated by the mention requirement, regardless
of ambient env. Verified: 7/7 pass with MATRIX_REQUIRE_MENTION=true (the
failing condition) AND with the env unset.
2026-06-20 21:22:11 -07:00
teknium1
4c349e85f8 fix(gateway): preserve transcript when hygiene auto-compress can't rotate
Gateway Session Hygiene auto-compression destroyed the original transcript
when the throwaway hygiene agent couldn't rotate the session (#21301, P1).

The _hyg_agent is built WITHOUT a session_db, so _compress_context cannot
end-and-fork the session (its rotate block is gated on agent._session_db).
The session_id stays unchanged, and the rewrite_transcript() call ran
UNCONDITIONALLY — replacing the full original transcript with just the
head+summary list. Permanent data loss on every hygiene compaction.

Guard the rewrite behind 'rotated OR in-place' exactly like the /compress
path already does (#44794/#39704): only overwrite when a new session id
was minted or in-place compaction succeeded; otherwise preserve the
original transcript and log a warning. The token/count bookkeeping that
followed the rewrite is moved inside the guard, with no-change values in
the preserve branch.

Co-authored-by: SandroHub013 <sandrohub013@gmail.com>
Co-authored-by: WuTianyi123 <wtyopenclaw@gmail.com>
Co-authored-by: kyssta-exe <kyssta-exe@users.noreply.github.com>
2026-06-20 21:07:11 -07:00
teknium1
79f297834a fix(gateway): widen cron namespace-collision fix to all migrated adapters
#49431 corrected parents[2]->parents[3] for discord + raft only. The same
bug existed in slack, whatsapp, and telegram adapters (migrated from
gateway/platforms/ in 5600105478): each inserts parents[2] = plugins/ onto
sys.path[0], shadowing the real cron/ package with plugins/cron/ so
'import cron.scheduler_provider' raises ModuleNotFoundError on gateway start.

Fixes #49410, #49824.
2026-06-20 20:45:12 -07:00
kyssta-exe
4c206b972d fix(gateway): correct sys.path insertion in plugins to prevent cron namespace collision (#49410) 2026-06-20 20:45:12 -07:00
teknium
e5e173eefd chore(release): add AUTHOR_MAP entries for docs PR salvage cluster 2026-06-20 20:42:49 -07:00
mintybasil
5d05415292 Expand .gitignore example 2026-06-20 20:42:49 -07:00
mintybasil
094d9cba6c Update docs to clarify requirement for gitignore 2026-06-20 20:42:49 -07:00
Railway9784
a9602d27e7 docs(skill): document context_length auto-detection resolution chain
When model.context_length is set in config.yaml, it blocks auto-detection
from the server's /v1/models endpoint. The skill incorrectly implied a
hard fallback to 131072. Add the resolution chain and the fix command
(hermes config set model.context_length "") to both the config table
and a new troubleshooting section.
2026-06-20 20:42:49 -07:00
yapsrubricsz0
abfbd618bd fix(docs): regenerate skill docs to fix stale cross-links, add tool-search to sidebar 2026-06-20 20:42:49 -07:00
graphanov
e1a717a6d8 docs: add Open Scaffold MCP workflow 2026-06-20 20:42:49 -07:00
Antimatter543
f6275a59e7 docs(contributing): add "search first" guidance to cut duplicate PRs
CONTRIBUTING.md had no pre-work search step; the only duplicate-check is a
PR-template checkbox that fires at review time, after the work is already done.
Add a "Before You Start: Search First" section near the top so contributors
search open and merged PRs and issues (and the source, since the tracker can
lag the code) before building. References #38284 (the agent-side analog).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:42:49 -07:00
mkslzk
9e4348f28a docs(windows): document uv.exe AV false positive 2026-06-20 20:42:49 -07:00
baolingao
2b08a4295a docs(README.zh-CN): update Windows install from 'not supported' to native PowerShell
The Chinese README still told Windows users to install WSL2 and run
the Linux installer. Hermes now ships a native PowerShell install
script, so replace the outdated WSL2-only note with the direct
PowerShell one-liner.

Fixes: documentation accuracy / Windows onboarding
2026-06-20 20:42:49 -07:00
Bartok9
31bdb60013 docs(skills): fix himalaya CLI arg order and download flag
Closes #48835

The bundled himalaya skill and its website docs documented command
syntax that does not match Himalaya CLI v1.2.0.

Verified against pimalaya/himalaya v1.2.0 source:
- message move: MessageMoveCommand declares target_folder BEFORE
  envelopes (src/email/message/command/move.rs) -> usage is
  '<TARGET> <ID>...', so 'move 42 "Archive"' is wrong; correct is
  'move "Archive" 42'.
- message copy: same ordering in copy.rs.
- attachment download: AttachmentDownloadCommand exposes the flag as
  '-d, --downloads-dir <PATH>' (src/email/message/attachment/command/
  download.rs), not '--dir'.

Fixed in all three surfaces that carried the wrong examples:
- skills/email/himalaya/SKILL.md
- website/docs/.../email-himalaya.md
- website/i18n/zh-Hans/.../email-himalaya.md
2026-06-20 20:42:49 -07:00
liuhao1024
4711936a3b fix(docs): remove non-existent conversation_entity setting from homeassistant troubleshooting 2026-06-20 20:42:49 -07:00
teknium1
7ace96ba40 fix(compression): preserve goal, platform, and session indexing across rotation
Three state-loss bugs at the compression rotation boundary, fixed together
because they all live in the same ~80-line rotation block:

- #33618: a persistent /goal did not follow the rotation. load_goal does a
  flat per-session lookup with no lineage walk, so a goal silently died when
  compression minted a fresh child id. Added migrate_goal_to_session() and
  call it after the child session is created (move-not-copy: the parent row
  is archived as cleared so exactly one active goal row exists).

- #33906/#33907: if the child create_session raised (FK constraint,
  contended write), the outer handler only warned and let the agent continue
  on the NEW id — which has no row in state.db — producing an orphan session.
  Now the rotation rolls agent.session_id back to the still-indexed parent
  (reopening it) instead of stranding the conversation on a phantom id.

- #27633: the compaction-boundary on_session_start notification omitted the
  platform kwarg, so context-engine plugins saw source=unknown for every
  message after the boundary. Forward platform (matching the initial
  session-start call in agent_init.py).

Co-authored-by: denisqq <21260182+denisqq@users.noreply.github.com>
Co-authored-by: zccyman <16263913+zccyman@users.noreply.github.com>
Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
2026-06-20 20:06:24 -07:00
teknium1
b4b512c507 test(gateway): assert queued outcome, not merge_pending_message_event call
The subagent-demotion busy-handler test asserted the internal
merge_pending_message_event call, which the FIFO refactor replaced with
_queue_or_replace_pending_event. Assert the behavioral outcome (the
follow-up lands in the pending slot for the next turn) instead — same
fix already applied to the two steer-fallback tests.
2026-06-20 20:05:37 -07:00
teknium1
c11c510b42 fix(gateway): FIFO busy-mode text follow-ups instead of newline-merging them
When the agent is busy and the user sends multiple text follow-ups, the
interrupt-mode and steer-fallback path stored them via
merge_pending_message_event(merge_text=True), which newline-joins
consecutive TEXT messages into a SINGLE pending turn — collapsing two
separate user messages into one mashed-together turn and destroying the
message boundaries the user sees (#43066 sub-bug 2).

Route that storage through _queue_or_replace_pending_event (the same FIFO
infrastructure used by busy queue-mode and /queue) so each follow-up gets
its own next-turn slot in arrival order, while still preserving
photo-burst / album merge semantics for media. Pure queue-mode already
used FIFO; this brings the interrupt/steer-fallback path in line.

The sibling defect in #43066 (assistant messages lost after compaction)
was already fixed on main by the identity-tracking flush rewrite (#46053)
plus the pre-rotation flush (#47202), so this only addresses the
remaining busy-message-merge half.

Co-authored-by: KiruyaMomochi <65301509+KiruyaMomochi@users.noreply.github.com>
2026-06-20 20:05:37 -07:00
Teknium
170ef24c8f
fix(doctor): audit WhatsApp bridge at its resolved (HERMES_HOME) dir (#49890)
doctor's npm audit hardcoded PROJECT_ROOT/scripts/whatsapp-bridge. In
read-only Docker installs the bridge deps live in the writable HERMES_HOME
mirror (#49561), so node_modules was never found there and the bridge audit
silently skipped. Resolve the dir through the shared
resolve_whatsapp_bridge_dir() helper so doctor audits where deps actually
install. Falls back to the install-tree path if the helper is unavailable.
2026-06-20 19:55:12 -07:00
joaomarcos
67523fae7c test(web_server): make profile-wrapper alias test OS-aware
On Windows, hermes writes writer.bat (@echo off / hermes -p writer %*)
with CRLF endings instead of the POSIX writer shell script. The test
hardcoded the POSIX path and exact bytes, so it failed on Windows hosts.
Assert on stripped non-empty lines per platform, making it line-ending-
and OS-independent.
2026-06-20 19:12:26 -07:00
teknium1
15cfc2836f fix(kanban): anchor no-path worktree tasks on board default_workdir
Follow-up to the salvaged worktree-materialization fix. When a worktree
task has no explicit workspace_path, resolve the anchor from the board's
default_workdir (a git repo) and materialize <repo>/.worktrees/<id> per
task, instead of silently rooting under the dispatcher's CWD (whatever
directory launched the gateway, e.g. the Hermes checkout). If no
default_workdir is configured, raise with a clear message rather than
guessing from CWD.

Adds AUTHOR_MAP entry for the salvaged commit.
2026-06-20 19:12:23 -07:00
Ahmad Ashfaq
d79f67fda6 fix(kanban): materialize and reuse linked worktrees for worktree tasks
The dispatcher treated workspace_kind=worktree as metadata only and never
ran 'git worktree add', so every worktree task ran in the main repo checkout
instead of an isolated worktree — concurrent tasks silently shared one tree
and contaminated each other.

This materializes a real linked worktree at <repo>/.worktrees/<task_id> on
branch wt/<task_id> when resolve_workspace() handles a worktree task, treats a
repo-root workspace_path as shorthand for that location, persists the derived
workspace/branch back onto the task row, and — on rerun/redispatch — detects an
already-materialized linked worktree (via git-common-dir) and reuses it instead
of nesting a second .worktrees/<id> inside it.
2026-06-20 19:12:23 -07:00
Teknium
37fa3c58b4
docs(kanban-worker): document kanban_complete artifacts deliverable param (#49854)
The kanban-worker skill taught kanban_complete with three full examples but
never mentioned the artifacts=[...] parameter added in #27813 — so a worker
reading the skill had no way to learn it can ship a chart/PDF/image as a
native upload to the subscriber's chat.

Adds a 'Shipping deliverables' section covering absolute-path rules, the
inline-vs-file extension behavior, and the trap that the notifier reads the
top-level artifacts list (NOT metadata.*).
2026-06-20 19:12:15 -07:00
teknium
2213ea9fa7 test(whatsapp): cover read-only bridge dir mirror; add author map
Follow-up for salvaged #49654: unit tests for resolve_whatsapp_bridge_dir()
(writable passthrough, read-only mirror, existing-mirror reuse) and the
AUTHOR_MAP entry for the contributor.
2026-06-20 17:05:27 -07:00
Zheng Tao
491579fa05 fix(whatsapp): resolve bridge dir with HERMES_HOME mirror in Docker
In Docker the install tree (/opt/hermes) is read-only, so npm install for
the WhatsApp bridge fails with EACCES. Add resolve_whatsapp_bridge_dir() in
whatsapp_common.py: when the install dir is read-only, mirror the bridge
source into a writable HERMES_HOME location and use that. Both the
adapter and the 'hermes whatsapp' CLI resolve through the shared helper so
the install and runtime paths agree.

Fixes #49561
2026-06-20 17:05:27 -07:00
teknium1
0a2b712965 test(chat-completions): cover timestamp strip + add AUTHOR_MAP entry
Add a regression test for #47868 asserting convert_messages strips the
internal per-message timestamp field, plus the identity-return path for
timestamp-free message lists. Map x7peeps for the release attribution gate.
2026-06-20 17:05:17 -07:00
x7peeps
4467c22c8f fix(chat-completions): strip timestamp from messages before sending to strict providers
Per-message timestamp metadata injected by _apply_persist_user_message_override
leaks into the Chat Completions payload sent to the provider. Strict OpenAI-compatible
providers (e.g. Fireworks-backed endpoints like OpenCode Go 'glm-5.2', Mistral, Kimi)
reject this schema-foreign field with HTTP 400:

  Extra inputs are not permitted, field: 'messages[0].timestamp'

The ChatCompletionsTransport.convert_messages already strips known internal-only
fields (tool_name, _-prefixed scaffolding keys, codex_reasoning_items, etc.) — add
timestamp to that list.

Closes #47868
2026-06-20 17:05:17 -07:00
xxxigm
ac83365d96 fix(install): expand 8.3 short %TEMP% so Windows Node/Electron stages don't abort
On a Windows profile whose folder name contains a space (e.g. "First Last"),
Windows can expose %TEMP%/%TMP% as an 8.3 short path
(C:\Users\FIRST~1.LAS\AppData\Local\Temp). PowerShell's FileSystem provider
mishandles the "~1.ext" component when the path reaches a provider cmdlet such
as `Tee-Object -FilePath`, throwing:

  An object at the specified path C:\Users\FIRST~1.LAS does not exist.

Every Node/Electron install+build stage streams its log to %TEMP% via
Tee-Object, so they all abort with that error (browser-tools npm, Playwright,
TUI npm, and the hard-failing desktop build), while the Python/uv stages --
which never write a side log to %TEMP% through a provider cmdlet -- succeed.

Normalize %TEMP%/%TMP% to their long form once, up front, so every downstream
cmdlet and child process sees a path the provider can resolve.

Fixes #39308
2026-06-20 16:24:52 -07:00
xxxigm
e74033b39b test(install): add ConvertTo-LongPath helper for 8.3 short paths
Adds a ConvertTo-LongPath helper to install.ps1 that expands a Windows 8.3
short path (e.g. C:\Users\FIRST~1.LAS) back to its long form via
Scripting.FileSystemObject. Paths without a "~<digit>" component are returned
unchanged (no COM round-trip), and any COM failure falls back to the input.

Adds an AST-loaded unit test that exercises the helper without executing the
installer body (pass-through, null/empty, and graceful fallback).
2026-06-20 16:24:52 -07:00
Brooklyn Nicholson
6fd839ac84 docs(pets): feature guide, petdex skill + catalog
Add the pets feature guide and the petdex skill (SKILL.md + bundled doc),
and register them in the website sidebar and skills catalog.
2026-06-20 14:18:43 -05:00
Brooklyn Nicholson
86b990fe0f feat(desktop): floating pet, pop-out overlay + Cmd+K picker
Add the in-window floating pet (sprite, speech bubble, contact shadow,
profile-scoped, resize-safe) and a pop-out always-on-top overlay window
with gestures and notifications. Add the Cmd+K pet picker page plus the
appearance gallery and size slider in settings. Includes the pet stores,
electron overlay wiring, i18n strings, and store tests.
2026-06-20 14:18:40 -05:00
Brooklyn Nicholson
75b36a138f feat(pets): TUI pet pane, picker + gateway RPCs
Add the Ink pet sprite pane, the interactive /pet picker overlay, and live
pet switching/rescale driven by new tui_gateway RPCs (pet state, pet.scale,
per-state frames). Wires pet flash state and the picker into the TUI layout
and slash handler. Covered by the slash-handler test.
2026-06-20 14:18:36 -05:00
Brooklyn Nicholson
83aa84ae3b feat(pets): CLI pet pane + /pet command
Render the reactive pet pane in the classic CLI (steady redraw,
right-aligned) and wire the /pet command to list and switch pets, plus an
enable/disable toggle. Backed by hermes_cli/pets.py and the CLI commands
mixin, registered in the central command registry. Covered by the CLI pet
pane and toggle tests.
2026-06-20 14:18:33 -05:00
Brooklyn Nicholson
e7dbfdaad7 feat(pets): pet engine + display.pet config
Add the shared pet engine under agent/pet/: spritesheet manifest loading
and in-process caching, six-state animation model, frame rendering, and
the persistent pet store. Register the display.pet config block (pet,
scale, enabled, etc.) that every surface reads from. Covered by
tests/agent/test_pet_engine.py.
2026-06-20 14:18:30 -05:00
teknium1
5a53e0f0f4 fix(compression): abort on auth failure instead of rotating into a degraded session
When the auxiliary summary call fails with an authentication/permission
error (HTTP 401/403), context compression now ABORTS and preserves the
session unchanged instead of rotating into a child session with a
placeholder summary.

Before: a 401 (invalid/blocked key, or a token pointed at the wrong
inference host) fell through every transient-error check to 'return
None', and because compression.abort_on_summary_failure defaults False,
compress() took the static-fallback path and rotated the session anyway
(messages N->N). The user landed on a fresh-but-broken session that kept
failing the same way — paying for a full-context API call each turn with
no useful compression.

After: _generate_summary classifies 401/403 as a non-recoverable auth
failure (_last_summary_auth_failure) and compress() aborts on it
regardless of abort_on_summary_failure. A distinct auxiliary summary_model
that 401s still retries once on the main model first (its dedicated creds
may be the only broken thing); the abort only sticks when the main model
itself auth-fails or the fallback also auth-fails. The existing
_last_compress_aborted handling in conversation_compression.py already
skips rotation and emits a warning, so no session rotation occurs.

Tests: TestAuthFailureAborts — 401/403 flagging, compress() aborts despite
flag=False, non-auth failures keep the historical fallback path, and
aux-model auth failure recovers on main without aborting.
2026-06-20 11:38:21 -07:00
teknium1
f22dd8a75a fix(agent): fail over to fallback provider on persistent auth failure (401/403)
When the active provider returns a 401/403 that survives its per-provider
credential-refresh attempt (revoked OAuth, blocked/expired key, or an
account pinned to a dead/staging inference endpoint), the conversation
loop now escalates to the configured fallback chain instead of dead-ending.

Before: the generic failover dispatch fired only for {rate_limit, billing};
auth/auth_permanent fell through to 'switch providers manually' advice and
never called _try_activate_fallback(). A user whose primary credential was
broken kept thrashing on the same dead credential every turn — the main
agent appeared 'stuck in fallback mode' while never actually failing over.
This also affected auxiliary tasks (compression, vision, title-gen), since
auto-resolved aux follows the main provider.

After: a persistent auth failure with a configured fallback chain switches
to the next provider (mirroring the rate-limit/billing failover path),
guarded one-shot per attempt by TurnRetryState.auth_failover_attempted.
When no fallback is configured the behavior is unchanged — it falls through
to the existing terminal handling and provider-specific troubleshooting
guidance.

Tests: test_auth_provider_failover.py — 401/403 classify as auth, the
gating condition fires only with a chain present + guard unset, the guard
blocks repeats, and non-auth (500) errors do not trigger auth failover.
2026-06-20 11:38:01 -07:00
Teknium
ea8a8b4af8
feat(delegation): background fan-out — parallel subagents, one consolidated return (#49734)
* feat(delegation): single-task delegate_task always runs in the background

The model no longer decides whether a subagent runs in the background — a
single-task delegate_task from the top-level agent is now always dispatched
async, so the parent turn returns immediately and the subagent's result
re-enters the conversation when it finishes.

- run_agent._dispatch_delegate_task (the live model path) forces
  background=True for top-level single-task calls; the schema-level
  `background` param is ignored.
- A batch (tasks with >1 item) stays synchronous (fan-out can't go async).
- A delegation from an orchestrator subagent (depth > 0) stays synchronous —
  it needs its workers' results within its own turn.
- The function-level default is unchanged, so direct Python callers/tests keep
  the historical synchronous behavior.
- On async-pool capacity rejection, single-task now falls through to a
  synchronous run instead of erroring (the child stays attached for interrupt
  propagation; detach happens only on a successful dispatch).
- Schema `background` param marked deprecated/ignored; tool description
  updated to state the always-background single-task rule.

* feat(delegation): all delegate_task fan-out runs in the background

Extend the always-background behavior to the full fan-out. A batch is now
dispatched as N independent async subagents (one handle each), instead of
running synchronously. Single task and batch both return immediately; each
subagent's result re-enters the conversation as its own message when it
finishes.

- delegate_task: when background is set, loop over ALL built children and
  dispatch each via dispatch_async_delegation; return a combined handle block
  (count + per-task delegation_ids). Children the async pool rejects (at
  capacity) run synchronously inline and are reported alongside the dispatched
  handles, so nothing is silently dropped.
- run_agent._dispatch_delegate_task + registry handler: force background for
  any top-level model delegation (single OR batch); orchestrator subagents
  (depth > 0) still run synchronously since they need workers' results within
  their own turn.
- Removed the v1 'batch async not supported' rejection.
- Tool description updated: BOTH MODES RUN IN THE BACKGROUND.
- Tests updated to assert batch fan-out dispatches each task async (verified
  E2E: 3-task batch -> 3 independent completion-queue events).

* fix(delegation): background fan-out joins and returns one consolidated block

Correct the fan-out semantics: a backgrounded batch is dispatched as ONE
async unit (one handle, one async-pool slot), not N independent dispatches.
The unit runs all children in parallel, waits on every one, and emits a
SINGLE completion event carrying the consolidated per-task results. The chat
is never blocked; when all subagents finish, their full summaries re-enter
the conversation together as one message.

- async_delegation.dispatch_async_delegation_batch + _finalize_batch: a batch
  occupies one slot; its runner returns the combined {results:[...]} dict and
  one event with the full results list is pushed to the completion queue.
- delegate_tool: extract the sync execution+aggregation into
  _execute_and_aggregate(); background dispatches it via the batch unit and
  returns one handle; on pool-capacity rejection it runs the batch inline.
- process_registry._format_async_delegation: render a consolidated multi-task
  block (TASK i/N + per-task summary) when the event carries is_batch/results.
- Tests updated; E2E verified: 3-task batch -> immediate return -> one combined
  completion block with all three summaries.
2026-06-20 11:27:12 -07:00