Commit Graph

26176 Commits

Author SHA1 Message Date
kshitijk4poor 0d02f0d1dc fix: re-derive the live busy text mode after a non-profile /busy change
Review finding (quality pass): on a single-profile gateway,
_handle_busy_command set _busy_input_mode but left _busy_text_mode
stale, so the adapter refresh a line later re-read the old value —
/busy queue persisted to config but live text messages kept
interrupting until restart. The profile path already re-derives both
from the fresh config; the non-profile path now does the same via
_load_busy_text_mode() (busy_input_mode is the source of truth,
run.py:9877). Regression assertion added to test_set_mode_persists;
verified red without the production fix.
2026-08-29 20:03:48 +05:30
kshitijk4poor 740994a903 test: assert /update dispatch via the handler table, not getsource
test_update_is_known_command grepped _handle_message's source for the
literal '"update"' — a banned source-reading test (AGENTS.md), broken
by the if-chain -> _gateway_plain_command_handlers() refactor. Assert
the actual dispatch contract instead: the shared handler table maps
'update' to _handle_update_command.
2026-08-29 20:03:48 +05:30
Samuel Odio f75f24e92e docs(commands): clarify busy gateway behavior 2026-08-29 20:03:48 +05:30
Samuel Odio 8c23824609 docs: align busy command references 2026-08-29 20:03:48 +05:30
Samuel Odio 597d2230ea refactor(gateway): share plain command dispatch 2026-08-29 20:03:48 +05:30
Samuel Odio fe828188d2 docs(test): clarify busy scope coverage 2026-08-29 20:03:48 +05:30
Samuel Odio e8591ae509 test(gateway): verify routed busy persistence 2026-08-29 20:03:48 +05:30
Samuel Odio 8d1d193f11 fix(gateway): apply busy mode per profile 2026-08-29 20:03:48 +05:30
Samuel Odio ce22b689c4 fix: route /insights through /hermes on Slack 2026-08-29 20:03:48 +05:30
Abdias Joel 5b8074dad4 fix: use event.get_command_args() and add persistence tests
Teknium review items:
1. Parse with event.get_command_args() instead of raw event.text
   (matching _handle_fast_command pattern at line 2842)
2. Add mocked persistence tests for queue/steer/interrupt setter
   success, save-failure, and exception branches (5 new tests)
2026-08-29 20:03:48 +05:30
Abdias Joel c64feb23f3 fix: make /busy command available on gateway platforms
Removes cli_only=True from /busy CommandDef and adds gateway
handler with subcommand dispatch (status/queue/steer/interrupt).

Applied on top of latest upstream/main while preserving original
commit intent from PR #18366.

Also adds smoke tests for the gateway /busy command handler.
2026-08-29 20:03:48 +05:30
kshitijk4poor 5e8ff7ce4b chore: map abdi.moya@gmail.com -> AxDSan, sam@odio.email -> srosro (attribution for #97875) 2026-08-29 20:03:48 +05:30
hermes-seaeye[bot] 9115c9b4fd fmt(js): npm run fix on merge (#97946)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-29 14:31:46 +00:00
Teknium 74a95a3ddf feat: /btw now answers side questions with conversation context; /background renamed to /bg
/bg (formerly /background, which is retired) keeps the existing semantics:
spawn a fresh, independent agent session in the background.

/btw is now its own command matching the convention other harnesses use:
ask a quick side question ABOUT the current conversation without
interrupting it. A one-shot auxiliary LLM call (main model by default,
overridable via auxiliary.side_question.* in config.yaml) answers from a
read-only transcript snapshot — the live session's history, role
alternation, and prompt cache are untouched, and the current turn keeps
running.

Surfaces wired: CLI (inline mid-run dispatch), gateway (all messengers,
busy-dispatch table + idle dispatch, i18n across all 17 locales), TUI
(prompt.btw RPC + btw.complete event), Discord native slash, relay
command manifest, desktop exec routing, docs (EN + zh-Hans).
2026-08-29 07:25:17 -07:00
Teknium b6bd681e89 feat(todo): nested subtasks via optional parent field
The todo tool now supports hierarchical task lists: an item's optional
'parent' field points at another item's id, making it a subtask.

- tools/todo_tool.py: parent validated (self-ref dropped), dangling refs
  and cycles sanitized; merge mode can set/clear parent; post-compression
  injection renders the tree indented and keeps a finished parent visible
  while any descendant is still active; the in-progress reorder pass is
  skipped for nested lists (a flat move would tear subtasks from parents).
- Schema cost: ~45 tokens added to the cached tool schema (one string
  property + one behavior sentence).
- acp_adapter/tools.py: todo result markdown indents by parent depth.
- Desktop: TodoItem carries parent; todoTree() DFS helper; composer
  status stack renders subtask rows indented (depth-capped), stabilizer
  compares depth.
- Docs: tools-reference todo entry mentions nesting.

Hydration/replay paths (gateway fresh-agent, API-server history) work
unchanged: parent rides inside the same todos array.
2026-08-29 07:25:12 -07:00
fangliquanflq d5632392c7 fix(update): ignore unrelated transitional SCM services 2026-08-29 19:38:36 +05:30
kshitijk4poor 154fd10af0 fix: name the preserved snapshot path in the ROLLBACK FAILED payload
Folded from #97748 (the competing fix by @lEWFkRAD): when a rollback
restore fails, the error note now points the operator at the surviving
snapshot directory instead of leaving them to find it in tempdir.
2026-08-29 19:38:31 +05:30
Adolanium 1315e65a52 fix(skills): a failed rollback restore keeps the skill and the snapshots
Rollback removed the live skill directory before restoring its
snapshot. When copytree then failed (disk full, locked file, path too
long on Windows) the except only added a note, and the finally deleted
the snapshot directory too, so nothing survived: the skill was gone
with a success-shaped error payload.

The broken state is now renamed aside first and deleted only after the
snapshot is restored. If the restore still fails, the broken state is
renamed back, so the worst outcome is the half applied batch instead of
no skill at all. When rollback reports any failure the snapshots are
kept on disk and their location is logged, instead of being deleted by
the finally.

Follow-up to #97692, same batch executor.
2026-08-29 19:38:31 +05:30
Teknium 11b98a1429 feat(system_prompt): two-line conversation clock — anchored start + rebuild-day line (salvages #96224) (#97930)
* test(system_prompt): cover session-start anchoring + fix Windows-portable expectation

- Add TestSessionStartLike unit tests for _session_start_like(): session-id
  embedded timestamp, session_start fallback, now fallback, non-matching id.
- Add a build-level regression: a session started Jan 1 must still render
  'Conversation started: Thursday, January 01' when the prompt is rebuilt
  on Jan 2 (the rebuild-drift bug).
- test_coding_prompt_preserves_legacy_workspace_order hardcoded '/hermes'
  while production renders str(Path('/hermes')) — backslash on Windows made
  the suite fail on Windows (CI runs Linux, so it was never caught). Build
  the expectation via str(Path()) to match production on every platform.

* fix(system_prompt): anchor 'Conversation started' to the real session start

The timestamp line stamped hermes_time.now() at system-prompt build time.
The prompt is rebuilt on compression, fresh-agent gateway turns, and
resume-without-stored-prompt, so the date silently advanced to whatever
day the prompt was last rebuilt — a chat that started on Wednesday read
as 'Conversation started: Thursday' after a Thursday-morning resume,
contradicting the fresh per-turn time hint.

Resolve the true start via _session_start_like(): the timestamp embedded
in the session id (YYYYMMDD_HHMMSS_..., immutable for the session life)
-> agent.session_start -> now() only as last resort. Box-local stamps are
attached to the box's local zone then converted to the rendered zone so
the date is consistent with the per-turn clock. The line stays date-only
and is now byte-stable for the whole session (never moves on rebuild),
preserving prefix-cache KV. The zone suffix and _bot_chat_timeless_prompt
behaviour are untouched.

* feat(system_prompt): two-line conversation clock — anchored start (salvaged #96224, credit @bobaba76) + as-of-last-rebuild date for multi-day sessions

---------

Co-authored-by: bobaba76 <79245850+bobaba76@users.noreply.github.com>
2026-08-29 07:06:54 -07:00
Teknium f89f0a2eaa feat(prompt): default identity rewritten as a behavior spec — sizing rule, named prohibitions, anti-sycophancy, earned depth; exploration-thrift line deliberately removed (models under-explore) (#97926) 2026-08-29 07:00:53 -07:00
Teknium 5241df3d4a fix(prompt): skills-section cleanup — drop '(mandatory)' header, delete hermes-agent paragraph duplicating the help guidance, gate the skill pointer on the skill actually being installed, cut the 'when the two differ' dead clause (#97918) 2026-08-29 06:53:02 -07:00
Teknium e387cbc0aa refactor(prompt): platform-hint diet — 7 heavies compressed, −657 tok across the map (facts probe-pinned) (#97899)
* refactor(prompt): platform-hint diet — shared _MEDIA_NATIVE spine; seven heavies compressed with every verified fact intact (3,175 -> ~2,520 map total, -657)

* refactor(prompt): steer-channel note diet 225 -> 155 — marker is self-describing since its own provenance+replay clauses; prompt keeps only anti-lookalike + authority + latest-results scope (#40240/#76805 archaeology in comment)
2026-08-29 06:37:06 -07:00
Teknium 2c232761e4 fix(skills): restore grill-me name, keep frontier-rounds upgrade
Reverts the plan-interrogation rename from #97831 per maintainer decision —
the skill keeps its original grill-me name. The content upgrade (design-tree
frontier-rounds interview mechanic from mattpocock/skills' grilling) stays.
Docs page, catalog row, and sidebar entry renamed back.
2026-08-29 06:30:12 -07:00
hermes-seaeye[bot] 5831d8365a fmt(js): npm run fix on merge (#97896)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-29 13:11:00 +00:00
Hukla 447217b4cc fix(desktop): reserve space for pane tab close button (#96880) 2026-08-29 06:05:34 -07:00
Teknium ccc367dce0 fix(prompt)+feat(gateway): platform-hint truth pass + universal voice-bubble transcode (all 22 hints source-verified) (#97873)
* fix(prompt): platform-hint truth pass — CLI/TUI file-delivery reality (paths/URLs only, MEDIA: prints literally), CLI no-markdown verified live, Slack/Discord markdown+tables truth, shared local-cron constant

* feat(gateway): universal voice-bubble delivery — shared transcode_to_ogg_opus; telegram [[audio_as_voice]] any-format; feishu native voice; hints to new truth

* chore: delete the webui ghost hint (tombstone comment, audit-verified); sync send_voice signature pin in tts routing test
2026-08-29 05:57:13 -07:00
Teknium b1ff8722a5 feat(optional-skills): add decision-questionnaire — turn a blocked decision into an async questionnaire
Ports the MIT-licensed 'to-questionnaire' skill from mattpocock/skills as
decision-questionnaire (optional-skills/productivity). Interviews the user
about the send only (recipient + needed outcomes), then drafts a
most-important-first discovery questionnaire with answer stubs to
decision-questionnaire-<slug>.md. Includes staging tests for frontmatter
standards, template sections, and de-upstreaming.
2026-08-29 05:04:13 -07:00
Teknium 7b17d02a09 feat(optional-skills): add setup-wizard-generator — bash wizard for human-only setup steps
Ports the MIT-licensed 'wizard' skill from mattpocock/skills as
setup-wizard-generator (optional-skills/devops). Generates an interactive
bash wizard that walks a human through manual procedures: opens dashboard
URLs, captures values (hidden entry for secrets), writes .env / GitHub
secrets idempotently, and confirms each stage. Vendors upstream's
template.sh library verbatim (bash -n verified) plus a staging test suite
covering frontmatter, template integrity, and de-upstreaming.
2026-08-29 05:03:52 -07:00
Teknium bfeeb50257 feat(skills): rename grill-me to plan-interrogation, fold in frontier-rounds interview mechanic
Renames the optional grill-me skill to the more descriptive plan-interrogation
and upgrades it with the design-tree frontier-rounds mechanic from
mattpocock/skills' MIT-licensed 'grilling' skill: batch all currently-askable
questions per round in dependency order, agent finds facts itself, decisions
stay with the user. Docs page, catalog row, and sidebar entry renamed.
2026-08-29 05:03:20 -07:00
Teknium e2037a6c71 fix: follow-up for salvaged PR #95943
- Widen opencode_zen_free_runtime healing to the union of the static floor,
  the in-process live memo, and the SWR disk cache — a newly-live free model
  now heals opencode-go/zen selections without a release (sibling site the
  original PR missed).
- Memoize _fetch_opencode_free_models() in-process (5 min, negative caching
  included) so direct provider_model_ids() validation callers don't each
  block on a network round-trip or timeout.
- Drop delisted x-preview-f-free from the offline floor and setup.py sample
  list (offline fallback must not offer a model that 401s); add the newly
  live deepseek-v4-flash-free / mimo-v2.5-free to setup.py.
- Update stale test fixtures to a live exemplar; add regression tests for
  memoization, negative caching, and union healing; docs note in providers.md.
2026-08-29 05:02:54 -07:00
Jackal991 d9d6112aa7 fix(opencode): revalidate keyless opencode-free catalog live against the Zen relay
opencode-free (keyless) models were served exclusively from a hardcoded
in-repo snapshot (_PROVIDER_MODELS["opencode-free"]). The SWR disk cache
only revalidated AUTHED providers — its entries were keyed by a credential
fingerprint, which keyless providers have none of — so the catalog never
refreshed against GET /zen/v1/models. When the relay delisted a free model
(e.g. x-preview-f-free, 2026-08-26) the picker kept offering it and
selecting it 401'd: "Model x-preview-f-free is not supported".

Now provider_model_ids("opencode-free") fetches the live /zen/v1/models
catalog anonymously, filters it to the anonymous-servable free tier
(excluding KEYED suffix-fakes like Go's ox-alpha-free), and falls back to
the curated static floor only when the live fetch fails or is empty. The
keyless provider gets a stable disk-cache fingerprint so the picker's SWR
path serves stale immediately while refreshing off-thread — the same
behavior authed providers already get.

Regression tests prove the fix: the delisted/newly-live model assertions
fail when the live-fetch wiring is reverted.

Closes #95914
2026-08-29 05:02:54 -07:00
Teknium 9d9f44d638 refactor(prompt): desktop hint diet — recipe-first widget teaching verified against the renderer source; setup_mcp sentence delegated to its schema (442 -> 307 tok) (#97850) 2026-08-29 04:35:08 -07:00
witcheer 299c652a66 docs(user-stories): drop the Tokyo itinerary entry
The quote in x-azed-tokyo-trip names no agent, and the linked post
credits the itinerary to MyClaw, mentioning Hermes only as another agent
that account manages. The collage promises stories about how people use
Hermes, so an entry that cannot be attributed to Hermes does not belong
in it.

Removed rather than reworded: the source does not support a
Hermes-attributed version of this story. 64 new entries remain.
2026-08-29 03:45:57 -07:00
witcheer 067e58238e docs(user-stories): add 65 new community stories
Adds 65 user stories gathered from Reddit and X, prepended to the front of
the list so the newest entries surface first.

Every quote was re-fetched from its live source and programmatically verified
to be a verbatim substring of the source text. No existing entries are
modified, reordered, or removed - this is a pure prepend.

45 Reddit, 20 X. All categories and sources already exist in the file; no
schema change.
2026-08-29 03:45:57 -07:00
Teknium 57746cbb84 fix(desktop): ::preview inline frame works on remote/URL connections — read via the mode-aware fs bridge instead of bailing to a card (vestigial gate predated /api/fs) (#97829) 2026-08-29 03:34:08 -07:00
Teknium 3b362acf48 feat(desktop): Download button on preview file cards — save any delivered file via the authenticated backend bridge (works local and remote) (#97816) 2026-08-29 03:34:04 -07:00
teknium1 71c823bbf7 refactor(skills): move grill-me to optional-skills
Per the 'when in doubt, optional' rule — plan-interview is an
on-request capability, not a weekly daily-driver for most users.

Install via: hermes skills install official/software-development/grill-me
2026-08-29 03:12:20 -07:00
teknium1 260adeb075 chore(skills/grill-me): contributor mapping + docs catalog/sidebar regen 2026-08-29 03:12:20 -07:00
rafaumeu a0590001b1 fix: shorten description to 47 chars, reformat to modern outline, add author 2026-08-29 03:12:20 -07:00
rafaumeu 21f86655bc feat: add grill-me skill — adversarial plan interview before coding
New bundled skill that stress-tests plans through structured adversarial
questioning. One question at a time, each with a recommendation, resolving
the full decision tree before any code is written.

Four-phase structure: Understanding -> Technical Decisions -> Edge Cases -> Synthesis.
Integrates with plan, subagent-driven-development, and requesting-code-review.
2026-08-29 03:12:20 -07:00
Teknium fae063fc74 fix(desktop): MEDIA: non-media files get the preview file card, not a degraded 'Open' anchor (#97812)
* fix(desktop): MEDIA:-delivered non-media files route to the preview pipeline — PDFs/data files get the file card instead of a dead 'Open' anchor (extends #84951 to every extension)

* docs(prompt): desktop guidance aligned with any-file MEDIA: delivery — preview card truth, local-markdown-image block warning
2026-08-29 02:53:42 -07:00
Teknium a2e19d484c refactor(prompt): memory/skills guidance — one builder, positive posture, session-accurate wording (537 → 255 served, −282/call) (#97760)
* refactor(prompt): diet the memory/skills guidance block — schema-taught curricula removed, form rule + pruning contract kept (537 -> 223 tok in the combined block)

* polish: literal check-glyphs in source; memory capacity posture — save proactively, replace/consolidate when full

* refactor: single spine for memory/profile guidance — form rule + capacity posture written once, variants differ only in opening frame

* refactor: ONE memory-guidance builder — frame adapts to enabled stores, body written once, positive posture leads (maintainer direction)

* fix wording: memory is loaded per SESSION, not injected per turn (maintainer correction)
2026-08-29 02:30:44 -07:00
Osham Wahab aff5125f8e fix(gateway): document turn-hold commit overshoot + route deferred-notice through i18n (#92318 review)
Reviewer feedback on #92318:
1. Bounded overshoot in the commit-in-flight branch is now documented at
   the await site — the turn can exceed _hyg_max_turn_hold_seconds by up
   to commit duration, and aborting mid-commit would corrupt the
   message-store transaction. Prevents a future 'fix' into mid-commit
   cancellation.
2. The user-facing 'Context compression deferred' bubble now routes
   through t() as gateway.compress.turnhold_deferred (en.yaml entry
   added), coordinating with the i18n surface expanding in #92338 so
   non-English users don't get hardcoded English copy.
2026-08-29 13:02:13 +05:30
Kshitij Kapoor 2abf72ad97 fix(gateway): register hygiene_max_turn_hold_seconds + flat retry-after on turn-hold abandonment
Review follow-ups on the salvaged #90845:

- hygiene_max_turn_hold_seconds registered in config_defaults next to
  its sibling hygiene knobs (run.py already read it; the key was
  undiscoverable).
- Turn-hold abandonment now records a flat 60s retry-after via the
  existing cooldown column. Without it, sustained traffic re-spawned,
  held, and cancelled a fresh compressor on EVERY turn — a per-turn
  summary-model token burn that never commits. Deliberately outside the
  x1/x3/x9 failure ladder: the compressor is healthy, so the failure
  streak must not advance (witness updated to assert exactly that
  boundary: no streak increment, flat <=120s spacing, turn-hold reason).
2026-08-29 13:02:13 +05:30
Machan-Army 31b974dfb7 fix(gateway): split turn-hold expiry from idle-timeout failure path
Introduce HygieneTurnHoldExceeded exception so turn-hold budget expiry
no longer collapses into the generic asyncio.TimeoutError handler.

- Add HygieneTurnHoldExceeded exception (availability boundary, not a failure)
- Add dedicated handler: stamps AGENT_COMPRESSION_TURNHOLD provenance,
  sends deferral notice, does NOT increment failure cooldown
- Preserve #87011 contract: idle timeout still sends 'no output' message
  and takes failure path
- Add behavior witnesses: turn-hold ≠ idle timeout, cooldown untouched

Fixes semantic boundary collapse flagged in PR #90845 review.
2026-08-29 13:02:13 +05:30
Machan-Army 543c85acbf fix(gateway): bound the hygiene-compression turn-hold so a streaming summary cannot freeze the turn
Session hygiene auto-compression runs inline on the incoming-message path
and awaits the summary worker with a progress-aware inactivity budget
(hygiene_timeout_seconds) that extends up to hygiene_total_ceiling_seconds
(default 600s). A summary model that keeps streaming tokens keeps resetting
the inactivity slice, so the wait can stretch toward the ceiling while zero
bytes reach the user — chat transports (Telegram ~30s idle-timeout) drop the
connection and the turn appears frozen, even though the gateway is healthy.

Add hygiene_max_turn_hold_seconds (default 10), a turn-hold budget that caps
the wall-clock the incoming message waits on hygiene compression. The wait
slice is additionally capped at the remaining budget so the budget is
re-evaluated even when the worker keeps the inactivity slice large. On
exceeding the budget the gateway abandons the inline wait and proceeds on
the uncompressed transcript via the existing timeout path, which revokes the
worker's commit admission (CompressionCommitFence) and defers cleanup — so a
stale compression finishing later can never overwrite the turns appended
after the wait was abandoned.

Well under the typical transport idle-timeout, this guarantees the message
is answered promptly while the detached compression completes in the
background. Configurable via compression.hygiene_max_turn_hold_seconds.

Adds a regression test: a worker that streams progress continuously (so the
inactivity slice never fires) must be abandoned once it exceeds the
turn-hold budget, the turn proceeds uncompressed, and the stale commit is
fenced (no session mutation, role alternation intact).
2026-08-29 13:02:13 +05:30
Jakub Wolniewicz 23bae43cfa fix(agent): normalize list-shaped streaming content deltas 2026-08-29 12:45:43 +05:30
kshitijk4poor c03c72a19e refactor(vertex): fold _creds_cache_key into _sa_snapshot (simplify pass)
Final /simplify-code pass on the full diff: after the content-digest
rework, _creds_cache_key had become a wrapper with zero production
callers — get_vertex_credentials inlined the same read/except dance —
kept alive only by its own tests. One helper now owns the
(bytes, cache_key) resolution for all three cases (ADC sentinel,
readable file, unreadable fallback); prod calls it, tests target it.
No behavior change: 10/10 green, same mutation-check results.
2026-08-29 12:33:44 +05:30
kshitijk4poor 91608eb20e docs: note the MEASURED_1H_PROVIDERS exception in effective_cache_ttl's docstring
Review finding: the docstring still claimed all Qwen/Alibaba routes clamp
to 5m, contradicting the new allow-list one paragraph of code below.
2026-08-29 12:23:41 +05:30
kshitijk4poor 264ae5b2c8 chore: map jack@powries.com -> powriej (attribution for #97582) 2026-08-29 12:23:41 +05:30