Commit Graph

5938 Commits

Author SHA1 Message Date
brooklyn!
8b17394cc6 fix: abort reasoning-field length stops and surface close taskkill failures
finish_reason=length with empty visible content and a non-empty reasoning
or reasoning_content field uses the existing thinking-budget abort. No
model id is consulted. Empty content with no side channel still continues.

Desktop close/stop no longer discards Windows taskkill failures. After the
same tree-kill, owned PIDs are inventoried and only unheld gateway locks
are cleared.
2026-09-25 01:08:23 -05:00
ethernet
39c0a39100 fix(providers): report why a lazy SDK install did not land
_get_anthropic_sdk() swallowed every ensure_import("anthropic") failure and
_require_sdk() then told the user to "Install it with: hermes pm install
--extra anthropic". PM often HAS installed it: sync_venv succeeds into a new
dependency environment that only activates at process boot, and
ensure_import raises "installed; restart Hermes". A lazy-install guard
("this process is not running from the install's dependency environment")
was flattened the same way. Users were told to install something that was
installed, or given a command that doesn't address the actual refusal.

Keep the import as the decider, but remember the InstallError and put its
text in the ImportError. bedrock_adapter._require_boto3 had the identical
shape; azure_identity_adapter already propagates str(exc) and is the model.
2026-09-24 23:37:27 -04:00
ethernet
3c18ea542d Merge remote-tracking branch 'origin/main' into ethie/pm-clean 2026-09-24 15:32:42 -04:00
brooklyn!
f213f755ee fix(ci): join continuation fragments as pairs
The retry path stored fragment text as strings, and the joiner unpacks
(text, stub) pairs. Also sort the composer-clamp props so desktop lint
stops failing every merge.
2026-09-24 15:31:44 -04:00
ethernet
05a78671a7 Merge remote-tracking branch 'origin/main' into ethie/pm-clean 2026-09-24 13:13:35 -04:00
kshitijk4poor
c43eb76d86 refactor(streaming): share interim visible/streamed normalization between prefix and exact verdicts
Both interim predicates now compare one (visible, streamed) pair from
_interim_visible_and_streamed so their normalization cannot drift; the two
#88954 codex tests collapse into one parametrized test.
2026-09-24 22:41:10 +05:30
kshitijk4poor
1f61f7087b docs(agent): pin prefix semantics at the previewed-mark call site (#88954)
Co-authored-by: joaomarcos <joaomarcosdias444@gmail.com>
2026-09-24 22:41:10 +05:30
liuhao1024
167f0ef5fa fix(streaming): stop losing the tail of commentary truncated at a tool-call boundary
With streaming enabled on Telegram, the streamed commentary is routinely
cut mid-text when the tool_calls finish arrives; the partial stream is a
prefix of the full commentary. The interim-message path used the
prefix-based _interim_content_was_streamed verdict, so the gateway's
_interim_assistant_cb called on_segment_break() — finalizing the
truncated bubble — and the tail ("...pick it u" vs "...pick it up") was
permanently lost (#88954).

That prefix semantics stays correct for the conversation-loop
"previewed" marks (the streamed prefix IS on the user's screen there,
and the contract test pins it per the #65919 review). The gateway
decision needs the stricter test: add _interim_content_fully_streamed
(exact normalized equality) and use it for the interim-message verdict.
Only an exact match may skip the full-text resend; a partial prefix
falls through to on_commentary() and re-delivers the complete text —
a benign duplicate, never lost text.

(cherry picked from commit 0b06a6660a1a4d47b4974f21ae42d7aeb1cbea15)
2026-09-24 22:41:10 +05:30
kshitijk4poor
2ab2270b32 fix(agent): hide MiniMax-M3 Chinese reasoning tags (#43827)
Add 思考/反思/推理/推敲 to THINK_TAG_NAMES so the streaming scrubber, CLI and
gateway stream filters and the final-response stripper all hide them, and
derive the auxiliary-client reasoning strip from the same list instead of a
hard-coded copy. Bare bracketless markers (unverified) are not covered.

Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
2026-09-24 22:41:10 +05:30
ethernet
0f65d698d0 Merge remote-tracking branch 'origin/main' into ethie/pm-clean 2026-09-24 13:10:14 -04:00
kshitijk4poor
7fb8736a1e refactor(codex): single source for app-server delta methods and delta text
The event bridge listed the text-delta methods twice (display handlers and
the #118410 liveness set) and duplicated _fire_delta's text extraction.
Derive both from _CODEX_TEXT_DELTA_METHODS, share _delta_text(), and hoist
the progress item-type set to a module constant (no per-event frozenset).
2026-09-24 22:38:35 +05:30
kshitijk4poor
6c94f52ef1 fix(agent): keep local Responses endpoints out of hosted codex watchdog clamps
#86444 widened the large-context stale floor, the 1500s hard ceiling and the
TTFB scale-up/cap gate from OpenAI-Codex to every codex_responses route. That
made the #92302 local-endpoint TTFB branch unreachable (local TTFB fell back to
120s instead of agent.local_stream_stale_timeout) and silently clamped a local
server's configured stale timeout to 1500s. Evaluate is_local_endpoint once and
exclude local endpoints from the hosted clamps; xAI keeps the #86444 behaviour.

Note: xAI large requests now get the raised stale floor but keep first-event
idle semantics (progress gating stays OpenAI-Codex only).
2026-09-24 22:38:35 +05:30
Kevin Yin
fe14267769 fix(cron): bound Codex max-iteration summaries
(cherry picked from commit f8d6ae81fd73ae56d95e84672a7cc9d0365d5c3c)
2026-09-24 22:38:35 +05:30
Christopher
606eb2382c fix(agent): apply large-context watchdog accommodations to all codex_responses transports
(cherry picked from commit 51a60661a4c777e20df7edfab5af9f3c9956aac5)
2026-09-24 22:38:35 +05:30
zxd
94727893b2 fix(codex): refresh turn liveness from app-server progress
(cherry picked from commit 677fde44763a0db4cba457de831bfd049f6c2b17)
2026-09-24 22:38:35 +05:30
kshitijk4poor
572446308e fix(agent): feed inline <think> text to the live reasoning pane (#89647)
The desktop/TUI reasoning pane is driven by reasoning.delta via
reasoning_callback; the scrubber-side collector only filled the final
reasoning_content, which extract_reasoning already recovers from the raw
content, so the pane stayed dead.

- Drop the scrubber reasoning collector (_reasoning_parts, reasoning(),
  clear_reasoning(), \x00 sentinel, _THINK_TAG_RE) and its per-request
  reset hook.
- StreamingThinkScrubber.feed() exposes the text it stripped from inside
  think blocks as last_hidden; _fire_stream_delta forwards it through
  _fire_reasoning_delta(inline=True) while no native reasoning delta has
  arrived for this model response (reset per request) — no double
  reasoning. CLI gating is unchanged: its reasoning_callback is None
  unless show_reasoning/verbose.
- _finish_chat_stream fills reasoning_content from the raw content via
  the existing extract_reasoning when no reasoning delta arrived.
- Replace the collector tests with two guards (live forwarding + native
  suppression; _finish_chat_stream fallback), both red on origin/main.

Co-authored-by: SayHell0W0rld <852938468@qq.com>
2026-09-24 22:37:10 +05:30
kshitijk4poor
363a998f7d fix(agent): scope recovered inline reasoning per model response
Rejoin streamed pieces of one <think> block verbatim (the per-delta newline
join split sentences on every token) and clear collected reasoning in the
per-request _reset_stream_delivery_tracking so a tool-loop's earlier API call
does not bleed into the next call's reasoning_content (the scrubber is only
reset per turn). Follow-up to #90417 (#89647).
2026-09-24 22:37:10 +05:30
SayHell0W0rld
e0c3006f72 fix(agent): recover inline reasoning stripped by the think scrubber
Providers that inline reasoning (MiniMax-M3 streams <think>…</think> in
content) never send a reasoning delta, so the desktop reasoning pane stays
dead even though the reasoning happened (#89647).

StreamingThinkScrubber (#17924) already strips inline blocks from streamed
content but discarded the text. It now collects the stripped text
(reasoning() accessor, tag markup removed), and the stream finisher
populates reasoning_content from the scrubber when the provider returned
no reasoning delta.

Hand-grafted from 759b65a8a4 (PR #90417) onto main's refactored scrubber and
_StreamingCall._finish_chat_stream; credential_pool.py commit dropped (out of scope).
2026-09-24 22:37:10 +05:30
kshitijk4poor
12a9c4c694 fix(aux): detect the SDK's real in-stream error body shape (#101538)
openai/_streaming.py raises APIError(body=data["error"]), so exc.body is the
inner error object or a bare string, never {"error": ...}; the predicate was
False for every real stream error. Accept the inner object (any of
type/code/message), a non-empty string, or the wrapper. Share one
_exc_http_status helper with _is_transient_transport_error.

Tests now build the error through openai.OpenAI + httpx.MockTransport (HTTP 200
SSE error event) and exercise the parameter-strip rung's fall-through.
2026-09-24 22:36:08 +05:30
kshitijk4poor
763646c30d fix(agent): only 4xx/5xx body codes count as a stream error's HTTP status (#121270)
A relay wrapping an error as {"code": 200, "success": false} must not route to
_by_status as a 200. Also reuse _error_obj for the error.* candidate walk and
point at the string-parsing text-SSE sibling.
2026-09-24 22:36:08 +05:30
kshitijk4poor
c9a1c3d6f5 refactor(agent): route status-less render errors through _MESSAGE_HEAD_RULES (#62662)
Drop the bespoke _streaming_render_error stage and its hand-built verdict: the
status-less message path already has a head table and _V_FORMAT_ERROR is the
canonical format_error/no-retry/fallback verdict. _by_status returns None when
there is no status, so the stage position bought nothing.
2026-09-24 22:36:08 +05:30
TheBlueHouse75
1ba8ebe50e fix(aux): fall back on status-less structured provider errors (#101538)
OpenAI-compatible relays can commit SSE with HTTP 200 and then emit an
OpenAI-style error event; the SDK raises a status-less APIError with the
event only on exc.body. Classify that shape as a route failure in the
recovery ladder's provider-fallback rung (_FALLBACK_REASONS, reason
"structured provider error") and let it fall through the parameter-strip
rung. Grafted from #101540 onto main's recovery-ladder layer.
2026-09-24 22:36:08 +05:30
AlexFucuson9
d0dfea836b fix(agent): classify status-less streaming render errors as format_error
LM Studio / llama.cpp raise a bare status-less APIError when chat-template
Jinja rendering fails mid-stream; classify as format_error with fallback
instead of burning same-provider retries. Stage fires only when status_code
is None. Hand-applied from #64834 (supersedes #62673) onto the staged
classifier pipeline.

Fixes #62662
2026-09-24 22:36:08 +05:30
liuhao1024
0a661b7c94 fix(agent): an in-stream SSE error object's numeric code is its HTTP status
An aggregator can deliver an upstream failure as {"error": {"code": 403}}
inside an HTTP-200 SSE stream; the SDK then raises a status-less APIError
whose body carries the status. _extract_status_code only read
exc.status_code/.status, so the ban climbed the transient retry ladder
(api_max_retries resends, then 'temporarily unavailable, wait a minute')
which can never succeed for e.g. a banned account.

Fall back to a body-carried numeric code (100-599, int only — symbolic
string codes stay with _code_from_payload), mirroring the text-SSE path's
_status_code_from_payload. A 403 now classifies as auth: exactly one
request, fallback-eligible, no wait-and-retry advice.

Fixes #121270

(cherry picked from commit 8a09fc53614ea3b954faebb4bfcccc176116338c)
2026-09-24 22:36:08 +05:30
kshitijk4poor
bfeafa2d68 fix(agent): collapse orphan continuation trail on retry exhaustion (#119001)
Ports #119027::finalize_continuation_partial: when a stream drop entered the
continuation path and the next request exhausted retries before a token, the
_length_continuation_fragment/_nudge rows were persisted as-is, so resume
replayed a dangling synthetic user nudge. Collapse them into one assistant row
before persistence and feed the collapsed text to the #119081 partial-retention
path (the fragment rows are gone by then).

Co-authored-by: fangliquan <fangliquan@qq.com>
2026-09-24 22:33:58 +05:30
finn763
e947f60689 fix(agent): retain delivered partial output on retry-exhausted 429/network errors
Closes #119001

(cherry picked from commit 360adad2eefb4ac144b91ea18c2a220310f53963)
2026-09-24 22:33:58 +05:30
kshitijk4poor
9e2444e763 fix(agent): gate stop-path repetition abort on runaway shape
Use is_runaway_repetition on completed replies so asked-for repetition with
distinct lines is delivered; stamp the truncated/retryable failure verdict,
log as diagnostic, and derive both repetition copies from one builder.
2026-09-24 22:32:05 +05:30
kshitijk4poor
d38af0fcab fix(agent): keep window-count repetition scan alongside periodic runs
The periodic-run detector only matched exact repeats, so counter/noise loops
(#86581 shape) passed. OR it with main's window-count scan, and memoise
rejected runs so anchors inside them skip multiple-period re-expansion.
2026-09-24 22:32:05 +05:30
itsflownium
34624eea44 fix(agent): reject repetitive arguments from dropped tool streams
(cherry picked from commit e044b1666308bf8b7310b4a818270a7cef1de770)
2026-09-24 22:32:05 +05:30
fangliquanflq
54d81e783c fix(agent): detect repetition across completion paths
(cherry picked from commit 481d49ef7292c791a3a4d5a7d0ab51c529efe385)
2026-09-24 22:32:05 +05:30
kshitijk4poor
44f6d3bbee fix(streaming): switch main turn to non-streaming on out-of-order Anthropic SSE (#72833)
_maybe_disable_streaming now reuses anthropic_adapter._is_stream_unavailable_error
(its stream-unsupported + Bedrock IAM checks were a verbatim duplicate), so a custom
anthropic_messages provider raising 'Unexpected event order' retries without
streaming instead of repeating the same broken stream. Bedrock keeps
turn_recovery's sticky Converse switch.

Also drive the #60683 usage:null test through _call_anthropic so dropping the
normalize_stream_usage wiring in _open_anthropic_stream turns it red.
2026-09-24 22:29:32 +05:30
kshitijk4poor
2cd25c9094 fix(anthropic): normalize usage:null on the aux stream path instead of re-issuing create() (#60683)
create_anthropic_message now runs normalize_stream_usage on the entered
MessageStream, so MiniMax usage:null no longer crashes the SDK after the
full generation and re-bills the request via messages.create(). Drops the
AttributeError/'nonetype'+'usage' substring classifier, which also turned
unrelated AttributeErrors into silent create() retries.
2026-09-24 22:29:32 +05:30
kshitijk4poor
4e566e456f fix(streaming): normalize usage:null on Anthropic main-turn stream events (#60683)
MiniMax-style Anthropic-compatible endpoints send usage: null on message_start
and message_delta; the SDK's accumulate_event() then raises AttributeError on
usage.output_tokens mid-iteration in _call_anthropic. Patch the raw SSE events
before the SDK accumulates them.

Co-authored-by: hejuntt1014 <252818347@qq.com>
2026-09-24 22:29:32 +05:30
LeonSGP43
fca694c3e7 fix(streaming): shape anthropic partial stubs correctly
Hand-applied from 5ab566fea1 (#45919): _build_partial_stream_stub now
takes api_mode and returns a Messages-shaped stub in anthropic_messages
mode, including the overflow_terminal and content-filter stub sites.

(cherry picked from commit 5ab566fea1, grafted onto the split helper)
2026-09-24 22:29:32 +05:30
hejuntt1014
d26909c99e fix(agent): fall back to messages.create on Anthropic stream usage aggregation errors
When Anthropic-compatible providers (e.g. MiniMax) return usage: null on
message_start, the SDK crashes in get_final_message() with AttributeError
on output_tokens. Extend the existing stream-unavailable classifier and
apply the same create() fallback in the main streaming path.

Fixes #60683

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit d7194c282a5359dc69436c84615997377d7d21c0)
2026-09-24 22:29:32 +05:30
webtecnica
d7aea5b3d6 fix(streaming): fall back to non-stream on custom provider SSE order error (#72833)
(cherry picked from commit 1361c61e1ce3a0b59fceb12269f00f639876f83d)
2026-09-24 22:29:32 +05:30
brooklyn!
3a894f8c01 fix(compression): do not stamp an empty absorbed-id list
An empty incoming turn still merges, and it names no row. Recording an empty
list changed a message that absorbed nothing.
2026-09-24 11:57:56 -05:00
brooklyn!
1674499d00 fix(compression): archive the rows the compressor held, not every row up to the newest
A gap below the newest held id, and turns appended above an unpersisted current
turn, were summarized away without being read. Name those held ids and clone
the rest.
2026-09-24 11:57:56 -05:00
kshitijk4poor
3ac22a309b refactor(agent): module-level _refund_api_call import; int verdict count (#77305)
turn_context_compaction imports neither conversation_loop nor
turn_empty_response, so the function-local import is not guarding a
cycle. The helper's docstring now also covers the provider-switch
fallback hop that reached the provider.
2026-09-24 22:27:18 +05:30
kshitijk4poor
f477c13d53 fix(agent): share the truncation-retry output boost; clamp to model limit (#72770, #79715)
The 2^n output-budget ladder was open-coded in three places. Only the
tool-call retry got the #72770 fix; the length-continuation retry
(ceiling max(32768, cap)) and the Codex-incomplete retry (ceiling
max(32768, base)) still re-sent the same budget once the cap reached
32768.

boosted_output_cap() now serves all three sites: it ladders from the
budget actually in use, its ceiling is max(32768, 2×cap), and it is
clamped to the model's known output limit (_get_anthropic_max_output on
anthropic_messages). When the request already sits at that limit it is
not doubled, so the retry no longer earns a provider 400 (#79715).

The length-continuation fix mirrors #72802.

Co-authored-by: webtecnica <contato@webtecnica.com.br>
2026-09-24 22:27:18 +05:30
Axl Ibiza, MBA
bc6808fbf5 fix(agent): refund the API call when an empty response activates fallback (#77305)
Fallback activation after empty-response retries re-entered the outer loop
without refunding the provisional call, so a provider hop consumed an
iteration. Refund via _refund_api_call and carry the count back through
EmptyResponseVerdict -> FinalResponseVerdict -> s.api_call_count.

Salvaged from #88867.

Co-authored-by: RelaxJonh <92573950+RelaxJonh@users.noreply.github.com>
2026-09-24 22:27:18 +05:30
kshitijk4poor
b7f91258b9 fix(agent): tools-available note on dropped-tools continuation; keep legacy stub recognized (#74990)
The dropped-tools partial-stream continuation now also states the cut was a
transport interruption and tools remain available. The pre-rewording
network-stub text stays in the compressor's synthetic-turn set so
crash-persisted nudges from older sessions are not mistaken for user turns.
2026-09-24 22:27:18 +05:30
Ugo Enyioha
eaf0fd95cb fix(agent): re-assert tool availability on partial-stream continuation
Re-port of 6f01f7476b (PR #72273) onto the _LENGTH_CONTINUATION_NETWORK_STUB
constant that _get_continuation_prompt now returns: state the cut was a
transport interruption, that tools remain available, and drop 'Finish the
answer directly' which read as a text-only instruction.
2026-09-24 22:27:18 +05:30
webtecnica
8a9bc6e18e fix: preserve max_tokens boost on truncated tool-call retries (#72770)
Re-port of f3c6307d22 (PR #72802) formula hunk into agent/turn_truncation.py:
ladder from the requested cap when max_tokens is unset, and cap at 2x that cap
so a request already at >=32768 is not re-sent unchanged. Bundled
skills/email/himalaya files from the PR are dropped.
2026-09-24 22:27:18 +05:30
kshitijk4poor
7f94891c35 fix(anthropic): drop dead Iterable guard; fold eventless-stream message into message_stop gate (#121320)
The real SDK MessageStream is always iterable, so the isinstance(stream,
Iterable) fallback only served get_final_message-only shims, which the
message_stop gate now rejects anyway. Iterate the stream directly; such
shims are unsupported.

In _call_anthropic an eventless stream never reaches get_final_message()
any more (no message_stop -> gate raises first), so the AssertionError
branch was dead. Use saw_stream_event to pick the more specific
'empty stream with no events' message inside the gate instead.
2026-09-24 22:26:32 +05:30
kshitijk4poor
c277f39a1f fix(bedrock): keep IAM-denied converse() fallback working with the messageStop gate (#109988) 2026-09-24 22:26:32 +05:30
kshitijk4poor
49569c83a9 fix(bedrock): raise EmptyStreamError on missing messageStop; tolerate it in relay finalizer (#109988)
RuntimeError is not classified by the stream retry loop; EmptyStreamError is.
The Relay finalizer replays the same events, so it now returns None instead
of raising and the live consumer surfaces the error.
2026-09-24 22:26:32 +05:30
kshitijk4poor
c587c44ef4 fix(anthropic): keep live delta streaming with the message_stop gate (#121320)
Drop the contributor's text/thinking delta buffering: it withheld every
token until stream end, killing live streaming. The message_stop gate
alone is enough — a dropped stream raises EmptyStreamError and the retry
loop's existing partial-delivery handling applies. Restore the partial
tool-name test to main's live-streaming contract.
2026-09-24 22:26:32 +05:30
liuzikaii
7985ea4599 fix(bedrock): reject streams missing messageStop
(cherry picked from commit b35be7ec5f28772f13c2ae70b54d71b67496ab69)
2026-09-24 22:26:32 +05:30
KoNit-K
c1281fff2a fix(anthropic): require message stop for streams
(cherry picked from commit 8bca0d97c602624f6acaaa3073a8fbb43f718e3c)
2026-09-24 22:26:32 +05:30