Commit Graph

21216 Commits

Author SHA1 Message Date
teknium1 a9d9634678
perf(docs-site): split search index by section and stop indexing code blocks
The client-side search index had grown to 16.3 MB raw / ~4.5 MB wire
(4,500+ docs), giving 25-30 s of blank UI before first results — users
read the dead window as 'search is broken entirely' (the engine itself
returned correct results).

Three levers, all config-only:

- searchContextByPaths: per-section index chunks. Searching from any
  docs page now fetches only that section's index (e.g. Reference:
  1.4 MB / 387 KB gz) instead of the 16 MB monolith. The search page
  gains a section dropdown; landing-page searches still cover
  everything via useAllContextsWithNoSearchContext.
- ignoreCssSelectors: ['pre']: fenced code blocks no longer indexed.
  YAML/shell samples were generating huge high-cardinality lunr token
  dictionaries; inline code in prose stays searchable.
- ignoreFiles: user-stories excluded (527 index docs of scraped
  community quotes rendered by a React component).

Measured (npm run build, both locales):
- root 'Everywhere' index: 16.3 MB -> 13.2 MB raw (4.42 -> 3.57 MB gz)
- per-section indexes: 0.4-8.3 MB raw (103 KB-2.2 MB gz)
- zh-Hans root: 14.6 -> 12.6 MB raw
- local serve: first dropdown results in ~175-205 ms for both scoped
  and Everywhere queries ('telegram' -> 8 options, search page -> 100)
2026-08-08 05:39:32 -07:00
teknium1 ef9d5f8c06 chore(skills/github-issue-to-pr): de-router, fold in maintainer issue-to-PR discipline
Rewrote from a sibling-skill routing table into a skill that carries its
own procedure, and folded in generalized rules from maintainer practice:

- full-thread reads (gh issue view --comments; newest comment = live state)
- duplicate-PR sweep (issue number + keyword variants) before any code
- design-intent check via git log -p -S alongside premise reproduction
- fix the class: sweep sibling call sites into the same PR
- sabotage run: prove the regression test fails without the fix
- open the PR immediately (PR dispatches CI; CI latency is the long pole)
- close the loop: comment the issue with the PR link

Also: description 205 -> 59 chars, author credits Ben Barclay first,
modern section order, boilerplate trimmed, tests (10) incl. a
router-pattern guard, scoped docs regen.
2026-08-08 05:26:27 -07:00
Ben Barclay 29783634bd feat(skills): add github-issue-to-pr 2026-08-08 05:26:27 -07:00
Teknium a51a4cb096 fix(api-server): mark replayed tool calls completed in Responses output items
The non-streaming /v1/responses path built function_call and
function_call_output output items with no status field (and no item id),
while the SSE streaming path correctly emits status in_progress ->
completed. Spec-strict OpenAI clients reading the non-streaming output
array could interpret the status-less function_call items as pending
calls the CLIENT must execute — but these tools were already executed
server-side by the Hermes agent and are replayed for structured tool UI
only. Reported by a community user whose GPT-5.6 client concluded 'a
server should not tell an OpenAI client to execute a tool the server
already executed itself'.

- _extract_output_items now stamps status: completed and spec-shaped
  item ids (fc_/fco_) on replayed items, matching the streaming path
- test updated to pin status + id shape
- docs example updated + explicit note that output tool calls are
  replayed, never pending
2026-08-08 05:22:02 -07:00
Teknium 5dc0fa3889 fix: post-merge audit follow-ups for #81138/#81139/#81141/#81148
Four fix-forwards from the adversarial post-merge audit of the Aug 7
unreviewed merge batch:

- estop (#81148): is_engaged() now fails SAFE (engaged) on stat errors;
  the gateway estop gate lets recognized slash commands and replies owned
  by in-flight work (update prompts, clarify, slash-confirm, tool
  approvals, running sessions) through instead of consuming them; new
  gateway /pause [reason|off] command gives messaging-only operators an
  in-band engage/resume path (busy_policy=dispatch so it works mid-run).
- cron monitor mode (#81138): execution-mode invariants (monitor x
  no_agent, monitor_script x monitor_url, no_agent-requires-script) now
  have ONE owner (_validate_job_mode_invariants) called from BOTH
  create_job and update_job, so the create-time invariant can no longer
  be silently violated through the update door.
- cron notepad (#81139): remove_job now clears the job's notepad rows
  (clear_notepad was dead code -> orphaned KV state forever); clear is
  best-effort and no-ops without creating notepad.db.
- delegation batch gate (#81141): template-marker regex narrowed to
  multi-word placeholder shapes only (<feature name>, {file_path}) so
  generics (Vec<T>), HTML tags, JSON snippets, glob braces and f-string
  style no longer reject legitimate batches; duplicate-goal rejection
  removed (best-of-N fan-outs are legitimate).
2026-08-08 05:21:09 -07:00
Teknium c5f71f9a51 docs: document read_file document extraction and the scanned-PDF coverage warning 2026-08-08 05:13:46 -07:00
Teknium 765940df79 fix(read_extract): keep scanned-PDF coverage warning on the backend bytes path
The salvaged bytes path (_extract_anydoc_bytes) bypassed the coverage
check added in #81680. Materialize transferred PDF bytes in a host temp
file for the pdftotext scan, and name the backend-visible path in the
recovery command rather than the temp file.
2026-08-08 05:13:46 -07:00
fangliquanflq 8de3ddb9ef fix(tools): preserve document extraction boundaries 2026-08-08 05:13:46 -07:00
fangliquanflq fb4664f79d fix(learn): process large sources incrementally 2026-08-08 05:09:43 -07:00
fangliquanflq 57ca5995c6 fix(learn): extend existing skills during relearning 2026-08-08 05:09:43 -07:00
Teknium 1dee73400e Inspired by Cursor: fail-closed hook semantics + exit-code-2 blocking 2026-08-08 05:02:04 -07:00
Teknium 70c6cf8e7e feat: add new FAL video families and image models
Video (plugins/video_gen/fal): Seedance 2.5, MiniMax H3, Seedance 2.0
Mini, FLUX 3, Grok Imagine 1.5, Gemini Omni Flash (i2v-only). New
family capability flags:
- duration_int: endpoints that take duration as a JSON integer
- resolution_aliases: maps 720p/1080p-style values onto non-standard
  enums (H3's 768P/2K/4K)
- image_drop_keys: strips keys the family's i2v endpoint rejects
  (aspect_ratio on Seedance 2.5 / H3 / Grok 1.5)

Image (tools/image_generation_tool): Seedream 5.0 Pro (+edit) and
Lite, Ideogram V4 instant + fast, Qwen Image 3 (+edit), MAI Image 2.5
Pro, Nano Banana 2 Lite (+edit), Recraft V4.1.

Every new endpoint live-tested against fal.run through the real
payload builders + submit path: 18/18 pass (t2v, i2v, t2i, and edit
probes). Note: several new endpoints return HTTP 409 from the Nous
Portal FAL proxy allowlist until it is updated portal-side; BYOK
FAL_KEY works today and the existing 4xx guidance message covers it.
2026-08-08 04:31:55 -07:00
Teknium 2e2fcc09ff Port from superagent-ai/grok-cli: directory-chain AGENTS.md loading 2026-08-08 04:31:45 -07:00
Teknium 1405d330e7 Port from superagent-ai/grok-cli: description-aware slash-menu fuzzy scoring 2026-08-08 04:29:16 -07:00
Coy Geek 530d37820c fix(terminal-tool): redact terminal error result fields
Force-redact every terminal exception and traceback field before JSON serialization, including environment creation, background startup, exhausted foreground retries, and the outer catch-all. Preserve the current command-aware, opt-out-respecting redact_terminal_output(output, command) behavior for successful output.
2026-08-08 04:28:19 -07:00
Teknium 89c14aeb9e fix(read_file): warn when PDF pages yield no text (scanned-image coverage gap)
anydoc converts the PDF text layer only and emits no image placeholders
or page markers, so a mostly-scanned PDF extracts 'successfully' into
section headers with empty bodies — silent data loss the model cannot
detect. Count per-page text via poppler pdftotext and prepend an
EXTRACTION COVERAGE WARNING naming the empty pages and the recovery
path (pdftoppm + vision_analyze, or the ocr-and-documents skill).

Found on a 311-page HOA resale package where 198 scanned pages
(CC&Rs, Bylaws, Articles, insurance certs) vanished without a trace.
2026-08-08 04:25:27 -07:00
Teknium 72eda946be fix(security): redact terminal exception results and ACP stderr logs (#77484)
Closes the last two emission gaps from #77484:

- tools/terminal_tool.py: both exception paths (generic except and
  TERMINAL_DEGRADED_MODE=fail) returned raw str(e) + traceback.format_exc()
  to the model — only the logger copy was redacted. Exception text can
  embed the failing command line and any secrets inline in it; both fields
  now pass through redact_sensitive_text.
- acp_adapter/entry.py: _setup_logging cleared root handlers and installed
  a plain logging.Formatter, bypassing redaction entirely on ACP stderr.
  Now uses RedactingFormatter like every other logging surface.

The other three gaps from #77484 (process(list), *_KEY regex variants,
control-char splits) were fixed in #80964/#80965.
2026-08-08 04:19:49 -07:00
Teknium 2a743e5f43 fix(image_gen): confine generation source images to the terminal backend
image_generate and video_generate forwarded model-supplied local paths to
provider plugins, which read them off the HOST filesystem regardless of
terminal backend — inconsistent with the confinement boundary vision/video
analysis enforce (GHSA-gpxw-6wxv-w3qq), and broken for sandbox-only files.

New dispatch-layer chokepoint (_confine_source_images): under a non-local
backend, path-like image_url / reference_image_urls resolve through
tools.image_source (media-cache host reads, bounded in-sandbox exec-read,
lazy env bring-up, credential guard, 50MB cap) and reach every provider as
data: URLs — which all backends already accept. URLs/data: pass through;
local backend is a no-op. xai_video_edit/extend already require public
HTTPS URLs, so no change needed there.
2026-08-08 04:19:38 -07:00
teknium1 90badaa284 chore(skills/email-inbox-triage): tighten to hardline standards
- description 219 -> 58 chars
- author credits Ben Barclay (benbarclay) first
- modern section order; trimmed template safety boilerplate into
  step-local rules and a skill-specific verification checklist
- tests at tests/skills/test_email_inbox_triage_skill.py (9 passing)
- docs regen scoped: per-skill page + one catalog row + one sidebar line
2026-08-08 04:19:33 -07:00
Ben Barclay ebb242d813 feat(skills): add email-inbox-triage 2026-08-08 04:19:33 -07:00
Teknium c5f5fa40c3 feat: --resume latest keyword and --in DIR launch flag
--resume latest resolves the most recent session through the same
workspace-scoped MRU lookup as -c (TUI source first under --tui, with
classic-CLI fallback). --in DIR chdirs before session resolution so the
lookup keys off DIR's workspace, and pins the session there by skipping
the recorded-cwd restore.

Requested by @Jeff9James: hermes --tui --resume latest --in ./dir
2026-08-08 04:03:41 -07:00
Ahmett101 f46636bfe2 fix(vision): retry container exec-read for Docker cold-start, surface stderr (#76566)
Under the Docker terminal backend, vision_analyze's first exec-read
sometimes returned empty / non-zero against a freshly started container,
producing 'could not read <path> inside the sandbox' on a file the agent
could cat seconds later. Cold pipe setup on the first exec against a
new container, not a permissions or mount problem.

Retry once after a short delay (150 ms covers Docker exec warm-up
without making a real failure feel sluggish). When every attempt still
fails, fold the container's first stderr line into the raised error so
the user can tell 'no such file' from 'permission denied' instead of
staring at one opaque message.

Tests cover the retry-then-succeed path, the diagnostic-on-exhausted
path, and confirm the existing single-attempt raise is preserved.
2026-08-08 03:59:42 -07:00
Teknium 9eb3ac50fe fix(video): route terminal-backend reads through the shared media resolver
Follow-up on the salvaged commit: replace the hand-rolled file_ops python3
exec-read with tools.image_source.resolve_image_source(permitted=('video',)),
so video_analyze gets the same pipeline as vision_analyze — media-cache host
reads, bounded head -c sandbox exec (no python3 dependency in the sandbox
image, no unbounded base64 stream), lazy env bring-up (#62825), the
credential-read guard, and the 50MB ingest cap.
2026-08-08 03:59:39 -07:00
dsad f2e936dad5 fix(video): read analyze inputs through terminal backend 2026-08-08 03:59:39 -07:00
Teknium a978f769b1 Inspired by Cursor: MCP config context variables (${userHome}, ${workspaceFolder}, ...) 2026-08-08 03:57:00 -07:00
kshitij 52920747e1 perf(ci): build only en locale in docs-site-checks
The Docusaurus build step (198s) is 79% of the docs-site-checks job
wall time and is the CI critical path. The site has two locales (en +
zh-Hans, ~700 pages total); building only the default locale in PR
checks halves the build time.

Follows the same pattern Docusaurus uses internally: a build:fast
script that runs `docusaurus build --locale en` for CI/preview builds,
while the full bilingual build runs only in deploy-site.yml for
production deploys.

deploy-site.yml is unchanged — it still runs `npm run build` (all
locales) on push-to-main and release.
2026-08-08 05:53:52 -04:00
kshitij 973c14b57c refactor: fold simplify findings — 6th copy in update_cmd, drop dead wrapper + speculative kwarg, behavior-contract tests
- Migrate the missed 6th inline formatter (update_cmd.py backup-size
  display) to the shared helper.
- checkpoints._fmt_bytes: plain alias instead of a None-guard wrapper —
  every caller feeds ints from checkpoint_manager (all size fields
  initialize to 0), so the None path was dead defensive code.
- Drop the fallback= kwarg (zero production callers; '?' default is the
  real inherited contract and stays).
- curator_backup + context_references: call format_bytes directly (single
  internal call site each, zero external importers — alias was churn
  avoidance with nothing to avoid). backup/_format_size and
  doctor/_human_bytes keep their aliases (claw.py + tests pin the former;
  three call sites use the latter).
- Reshape the loop so the trailing TB return is reachable (no dead line).
- Tests: replace alias-identity assertions (ossified the delegation
  mechanism) with behavior-contract equality over a value sweep;
  mutation-checked red-green.
- update_cmd parity: byte-identical B-GB vs the old inline loop; gains
  the TB tier.
2026-08-08 15:10:35 +05:30
kshitij 7289898494 refactor: consolidate five duplicate byte formatters into hermes_cli.sizefmt
Five modules each carried a private near-identical human-readable byte
formatter (backup._format_size, checkpoints._fmt_bytes,
doctor._human_bytes, context_references._human_bytes,
curator_backup.format_size). Three of them silently topped out at GB and
rendered a 1 TiB value as '1024.0 GB'. All five now alias one shared
format_bytes in hermes_cli/sizefmt.py (sibling of timefmt.py, same
zero-dependency rationale), keeping each module's established local name
so no caller churns.

Deliberately NOT migrated (behavior differs on purpose):
- session_recovery._format_bytes: binary suffixes (KiB/MiB/GiB)
- qqbot chunked_upload.format_size: '100.0 B' one-decimal style, pinned
  by its protocol tests

Net -33 production LOC before the new module; parity verified over a
16-value corpus against all five verbatim originals (only divergence:
the TB tier fix). Contract tests mutation-checked red-green.
2026-08-08 15:10:35 +05:30
WolftacDigital 4af8fb2148 fix(ssl_guard): tolerate truststore SSLContext.get_ca_certs() NotImplementedError on Windows
On Windows, truststore.inject_into_ssl() replaces ssl.SSLContext with an
OS-trust-store-backed context whose get_ca_certs() raises NotImplementedError
(empty message). The ssl_guard's _validate_bundle_path() called get_ca_certs()
unguarded, crashing every fresh agent init with an opaque
'Failed to initialize OpenAI client:' error.

create_default_context(cafile=...) already validates that the bundle is
parseable, so we skip the post-load introspection rather than treat the
NotImplementedError as a failure.

Cherry-picked from PR #49945 with comment trimmed.

Co-authored-by: WolftacDigital <jonathan@wolftacdigital.com>
2026-08-08 15:10:24 +05:30
kshitij c8e558c72c fix(tools): keep non-bash -c invocations covered by the shell guard
The _bash_exec_payload delegation rejected short-option bundles with
letters outside bash's alphabet, so 'zsh -yc', 'dash -Vc' and 'ksh -Gc'
scripts stopped being scanned — a fail-open regression for shells the
guard's _SHELL_EXECUTABLES explicitly covers. Try the bash grammar
first (catches operand-hidden -c), then fall back to the permissive
positional scan; a block-guard fails closed.
2026-08-08 14:56:38 +05:30
kshitij df0a5c3ee4 refactor(doctor): reuse backup's size formatter for database listings
_format_db_size reimplemented human-readable size formatting two
imports away from backup._format_size, which doctor already leans on
for _QUICK_STATE_FILES. Delegate and keep only the stat-failure wrap.
Sizes now scale units (KB/GB) instead of pinning everything to MB.
2026-08-08 14:56:38 +05:30
kshitij 39db9d1114 refactor(tools): unify the tool-error cap on one constant
model_tools._TOOL_ERROR_MAX_LEN (2000, '...' marker) and the new
registry._MAX_TOOL_ERROR_CHARS (2048, '… [truncated]' marker) were two
caps for the same budget; text on the dispatch exception path passed
both. Alias the sanitizer's cap to the registry constant so the
tool-error budget lives in one home. model_tools already imports from
tools.registry at module level, so no import-cycle risk.
2026-08-08 14:56:38 +05:30
kshitij daa139c9e3 fix(tools): classify git bisect as a worktree mutation
bisect sat in _KNOWN_GIT_BUILTINS and was allowed in the running source
root, yet it repeatedly checks out commits — the exact module-version
skew this guard exists to prevent. Move it to _WORKTREE_MUTATIONS.
2026-08-08 14:56:38 +05:30
kshitij cd869f26f3 perf(tools): stop safe git commands spawning alias-lookup subprocesses
_KNOWN_GIT_BUILTINS omitted reset/stash/clean/restore (whose dangerous
forms _mutates_worktree already classifies first) and common read-only
porcelain (reflog, ls-files, cat-file, shortlog, show-ref, ls-tree,
ls-remote, merge-base). Every safe use inside the source repo — 'git
stash list', 'git reset --soft', 'git clean -n', 'git restore --staged'
— fell through to _read_git_alias and spawned a 'git config --get
alias.X' subprocess per terminal command (~10ms, 1s worst case on a
locked config). Complete the builtin set; the mutation classification
is unchanged and runs first.
2026-08-08 14:56:38 +05:30
kshitij bb311b3951 fix(tools): parse bash option grammar before extracting the -c script
The guard's _shell_script_arg treated any leading option containing 'c'
as -c and looked no further, so 'bash -o pipefail -c "git checkout
main"' returned None and the script was never scanned (fail-open).
approval.py's _bash_exec_payload already parses bash's real option
grammar (-O/-o consume operands, short-option bundles, --init-file);
delegate to it instead of keeping a second, weaker parser.
2026-08-08 14:56:38 +05:30
Erosika 4cc3ea01f6 fix(agent): separate continuation fragments so joined text does not glue
truncated_response_parts were joined with no separator at both the
ceiling exit and the success path, so a fragment ending mid-word ran
straight into the next one (#78577). insert a newline only when the
previous fragment ends non-whitespace and the next starts
non-whitespace, so existing separators are not doubled.
2026-08-08 14:56:38 +05:30
Erosika c8cf8bfdb6 fix(agent): strip length-continuation marks from outgoing api messages
the scaffolding marks are hermes bookkeeping. only the chat-completions
transport strips underscore keys, so anthropic and bedrock requests on
continuation attempts 2+ would send the marks to strict providers. pop
them in the central api_messages sanitization next to _thinking_prefill.

also pin that a mark reloaded from a mid-crash persist on a prior turn's
message is never deleted by a later turn's ceiling cleanup.
2026-08-08 14:56:38 +05:30
Erosika c5c040cb35 fix(agent): clean up the session tail when the continuation ceiling is exhausted
a turn that exhausts all 4 length-continuation attempts used to persist
its interim fragments and '[System: ... continue ...]' user nudges into
the session transcript. every later user turn replayed the unanswered
nudges, so the model resumed the oversized response, truncated again,
and re-exhausted the ceiling - wedging the session regardless of input.

at the ceiling exit, drop the fragment/nudge scaffolding from the turn's
tail and store one settled assistant turn carrying the stitched partial
text. the marks are cleared on continuation success and on the
content-filter rollback so cleanup can never delete fragments whose text
was already consumed.

also stop labeling a finish_reason='length' stub a network error: report
it as a truncation (stream ended before completion) and say the partial
response is kept when the ceiling is exhausted.
2026-08-08 14:56:38 +05:30
Erosika a96a4621fa feat(doctor): show database size and the repair command for exposed databases 2026-08-08 14:56:38 +05:30
Erosika 6583297086 feat(doctor): report per-database journal mode with WAL-reset exposure
hermes doctor already warns when the linked SQLite carries the WAL-reset
bug, but it never said which databases are actually exposed. A database
already in WAL mode on a vulnerable runtime can still corrupt; one on a
rollback journal cannot. Doctor now lists each Hermes-managed database
with its journal mode next to the SQLite version line and marks the WAL
ones as exposed when the runtime is vulnerable.

The probe reads the 20-byte file header and checks byte 18 (2 = WAL,
1 = rollback journal). It deliberately avoids the SQLite engine: even a
read-only open creates -wal/-shm sidecars next to a WAL database, needs
directory write access, and can wait on locks. The header read does none
of that. It cannot tell delete from truncate/persist, so doctor reports
'rollback journal mode' rather than an exact mode name. Missing files
are skipped; empty, unreadable, or corrupt files are reported as
unreadable without failing doctor.

The database list reuses backup.py's _QUICK_STATE_FILES plus per-board
kanban databases. Exposure uses hermes_state.is_sqlite_wal_reset_vulnerable,
so the 3.50.7 and 3.44.6 backports count as fixed.
2026-08-08 14:56:38 +05:30
Erosika 886092bc54 fix(tools): block worktree removal and moves of the running source root
`worktree` sat in _KNOWN_GIT_BUILTINS, so the guard returned safe for the
whole family. That allowed `git worktree remove [--force] <root>` and
`git worktree move <root> <dest>` against the very checkout this process
runs from, which the guard already treats as a source root when its .git
is a linked-worktree file.

Both name their target as an argument rather than acting on the cwd, so
they also slipped past the "is the cwd inside root" gate when run from
outside. Resolve the target against the command's cwd and block it when
it lands on the running root, from any directory. `worktree add`, list,
prune, lock, unlock, and operations on other worktrees stay allowed.
2026-08-08 14:56:38 +05:30
Erosika f0a3ef8bde fix(tools): harden live source checkout guard 2026-08-08 14:56:38 +05:30
Erosika a9f94022b0 fix(agent): bind finalize_turn at import time
The function-scoped import at the end of run_conversation loads
agent.turn_finalizer fresh from disk on the first turn that reaches it.
On a source/editable install whose checkout changed mid-session, that
pairs an old caller with a new callee at the exact seam where every turn's
work is persisted — the turn crashes on a signature mismatch after the
work is done. The lazy import was never cycle-forced: turn_finalizer
defers its own conversation_loop import.
2026-08-08 14:56:38 +05:30
Erosika ecbe6ef0dd feat(tools): hard-block self-repo git mutations in terminal_tool
Wire the self-repo guard in next to the gateway lifecycle hard-block,
before the force check — force=True cannot make the command safe, only
delay the crash. Local backend only: sandboxed backends cannot reach the
host checkout. The block message explains the version-skew mechanism and
redirects to git worktree add / a temp clone, or running the command
outside hermes with a restart after.
2026-08-08 14:56:38 +05:30
Erosika 206531a1e1 feat(tools): detect git mutations targeting the running source checkout
When hermes runs from a source/editable install, a git checkout/reset/pull
in its own repo swaps code on disk under the live interpreter. Modules
imported before the switch stay old while later lazy imports load new code,
producing delayed signature TypeErrors and tracebacks that don't match the
source, typically losing the in-flight turn.

New tools/self_repo_guard.py detects working-tree/ref mutations (checkout,
switch, reset, rebase, merge, pull, restore, stash, clean, cherry-pick,
revert) whose target repo is the source root the process runs from, via
cwd, git -C, cd chains, and subshell segments. Read-only git, commits,
fetch, and git worktree add stay allowed; packaged installs (no .git) are
inert.
2026-08-08 14:56:38 +05:30
Erosika ad59d55338 fix(tools): bound the exception text dispatch writes into its own log line
dispatch() called logger.exception with the exception interpolated into the
message. exc_info renders the same exception again in the traceback, so a
failing tool wrote its error body to the log twice. Every tool exception
passes through this one handler, so a large HTTP error body from any tool
landed here at full size.

Bound the message copy. The traceback still renders the exception once,
which is what an operator needs to place the failure.

Same double-write @arimu1 fixed in the vision, image, and TTS handlers in
#75938.
2026-08-08 14:56:38 +05:30
Erosika 84bc430073 fix(tools): bound the truncation log so it stops re-dumping the full body
The debug line that fires when an error body is truncated interpolated the
whole untruncated body, so capping the model-facing copy still wrote the
original to the log. A large HTTP error body — a Cloudflare challenge page
or a proxy 502 — reached the log at full size on every failed call.

Log a bounded prefix instead. It stays longer than the model-facing cap so
an operator still has something to diagnose with, but it no longer grows
with the size of the response body.

Reported for the logging handlers in #75938 by @arimu1; the same pattern
was present here.
2026-08-08 14:56:38 +05:30
Erosika 2181d2e7c2 fix(tools): bound tool error bodies at the dispatch boundary
tool_error() caps its message at _MAX_TOOL_ERROR_CHARS (2048), logging
the full body at DEBUG before trimming the context-bound copy.

Handlers that serialize exceptions directly -- json.dumps({"error":
str(exc), ...}) -- bypass that helper, so _normalize_handler_result
also runs every string result through _bound_json_error_result: if it
parses as a JSON object with an oversized string error field, only
that field is trimmed and the payload re-serialized. Non-error
results, non-JSON strings, and multimodal envelopes pass untouched.
2026-08-08 14:56:38 +05:30
kshitij 350f366a81
Merge pull request #81630 from kshitijk4poor/chore/author-map-wolftacdigital
chore: AUTHOR_MAP for WolftacDigital (PR #49945 salvage)
2026-08-08 14:52:21 +05:30
kshitij e3698fd8d3 chore: AUTHOR_MAP for WolftacDigital (PR #49945 salvage) 2026-08-08 14:49:17 +05:30