Commit Graph

1617 Commits

Author SHA1 Message Date
mzkarami 9eec86923c docs: align Ollama tool-calling guidance 2026-08-08 20:21:30 -07:00
teknium1 9e19931770 docs(telegram): explain rich draft final delivery
Document the actual transport split: legacy editable drafts by default, optional rich drafts, persistent rich final sends, and in-place rich final edits for edit-based streams.
2026-08-08 17:15:45 -07:00
Teknium 5a16635f40 feat(cli): show session titles in status bars 2026-08-08 17:02:55 -07:00
teknium1 55982159dd feat(tests): CI-enforce skill authoring standards; clear all remaining debt
New tests/skills/test_authoring_standards.py parametrizes every bundled +
optional SKILL.md (1148 checks) against the mechanically-verifiable subset
of the hardline standards:
- required frontmatter fields (name/description/version/author/license/
  platforms) + tags
- frontmatter name == directory name
- description <= 60 chars, ends with period, no marketing words
- related_skills resolve in-repo
- no machine-local paths
- <= 100k chars
Grandfather dict for legacy debt ships EMPTY — all pre-existing violations
fixed in this PR:

- 13 frontmatter names canonicalized to their directory names (the install
  identifier); all related_skills references updated (comfyui -> stable-
  diffusion). Fixes the class behind PR #42788's report; also fixes
  here.now's invalid dot-name.
- optional-skills/devops/cli -> inference-sh-cli (dir was the generic
  'cli'; fm name was right) incl. docs pages (en + zh-Hans), catalog row,
  sidebar entry.
- pytorch-fsdp: 157k generated 'Quick Reference' dump moved to
  references/common-patterns.md; SKILL.md 159k -> 2.5k with a pointer.
- research-paper-writing: 31.7k Phase 5 drafting section moved to
  references/phase5-paper-drafting.md; SKILL.md 103k -> 71k.

Docs regenerated with scope discipline.
2026-08-08 15:43:00 -07:00
teknium1 1c9433897c chore(skills): standards sweep — bring 42 bundled/optional skills up to hardline
Audited all 191 in-repo skills (77 bundled, 114 optional) against the
authoring standards (AGENTS.md hardline + PR #80800). Fixed 42:

- 8 overlong descriptions rewritten to <= 60 chars, one sentence, period
  (one-three-one-rule 525ch, drug-discovery 405ch, web-pentest 358ch,
  fitness-nutrition 352ch, neuroskill-bci 332ch, oss-forensics 320ch,
  computer-use 307ch, memento-flashcards 252ch)
- 24 skills with missing frontmatter fields: author (credited from git
  history: f-trycua, SHL0MS, teyrebaz33, haileymarshall, FurkanL0,
  teknium1), license, version, platforms, tags
- 7 machine-local paths scrubbed (/home/bb, /home/user, /home/ubuntu ->
  portable placeholders)
- 11 marketing-word intros reworded (Comprehensive/state-of-the-art)
- docs catalogs + per-skill pages regenerated, scope-disciplined

Deferred (not in this PR):
- 14 frontmatter/dir name mismatches — open PR #42788 already proposes
  the dir-rename approach for 4 of them; resolve there as one class
- 2 skills over 100k chars (pytorch-fsdp 159k, research-paper-writing
  103k) — need content splits into references/, separate PRs
- comfyui dangling related_skill resolves after the name-mismatch class
2026-08-08 15:27:29 -07:00
Teknium 66ea4e686d feat(media): default-on upscaling for sub-2MP image models (FAL + Krea)
Per review: upscaling should be the default behavior (like the original
flux-2-pro chain), not agent opt-in. Policy: every image model whose
native output is below ~2MP now sets upscale=True in its catalog —
users never silently get low-res images. Native hi-res models
(Seedream 5 Pro/Lite, Krea 2 Large) stay off to avoid paying to
upscale already-large output.

- FAL catalog: 16 models flipped to upscale=True (klein, z-image,
  nano-banana pro/2/2-lite, gpt-image 1.5/2, ideogram v3/v4, recraft
  v4/v4.1, qwen image/3, krea-2 medium on FAL, MAI 2.5 pro).
- Krea plugin: per-model upscale defaults (medium + medium-turbo ON at
  1.5K native; large OFF at 2K native), precedence explicit kwarg >
  image_gen.krea.upscale config > catalog default.
- The 'upscale' tool param remains as a per-call override in both
  directions (false = fast draft, true = force on hi-res/edits).
- Video unchanged: opt-in only (default-on would double every video's
  cost and latency).
- Sibling tests updated: routing/payload tests pass upscale=False where
  the assertion targets the generation submit; catalog test now pins
  the native-resolution policy instead of the flux-2-pro snapshot.
2026-08-08 14:49:28 -07:00
Teknium 137960c9aa feat(media): opt-in upscale pass for image_generate and video_generate across FAL and Krea
The generated-media surface previously had almost no upscaler coverage:
only fal-ai/flux-2-pro chained Clarity Upscaler (hardcoded catalog
default), every other image model returned ~1MP output with no high-res
path, and video had no upscaler at all. Krea's API treats the enhancer
as a standard second pass; this brings the same shape to Hermes.

- image_generate: new optional 'upscale' boolean in the tool schema.
  Explicit true chains the backend upscaler on ANY model (including
  edits); explicit false disables flux-2-pro's automatic default;
  omitted keeps per-model catalog behavior. Response now reports
  'upscaled' so the agent knows which resolution it got.
- FAL image path: explicit flag overrides the catalog 'upscale' default
  (Clarity Upscaler, 2x). Failure falls back to the native image.
- Krea plugin: upscale=true chains Krea Enhance
  (/generate/enhance/krea/enhance, 2x, prompt-guided) through the same
  BYO/managed base URL + auth as generation, with a best-effort poll
  loop that never fails a successful generation.
- video_generate: new optional 'upscale' boolean; FAL video plugin
  chains ByteDance SeedVR2 (fal-ai/seedvr/upscale/video, 2x factor
  mode). Providers without upscalers ignore the kwarg per the ABC
  contract (documented in both ABCs).

Validation: targeted suites green (123 tests across 6 files, including
new coverage for override-wins/default-kept/failure-fallback on all
three paths); live E2E on direct FAL verified both chains end-to-end
(klein 9b + Clarity upscaled image; pixverse-v6 1s 360p + SeedVR2
upscaled video).
2026-08-08 14:49:28 -07:00
teknium1 65710ca186 chore(skills/competitor-news-monitor): cron-recipe shape + competitor-watch blueprint
Skill polish (hardline standards):
- description 247 -> 55 chars; author credits Ben Barclay (benbarclay) first
- restructured into Setup (foreground, once) / Tick (each scheduled run)
  phases with explicit cronjob(action='create') wiring and a state file
  at ~/.hermes/competitor-watches/
- dropped dangling 'change-monitor-and-notify' related_skills entry
- Hermes-tool framing (web_search, web_extract, blogwatcher for feeds)
- coverage honesty: source failure = unknown coverage, cutoff advances
  only on success

Blueprint half:
- new 'competitor-watch' Automation Blueprint (companies/categories/time/
  recurrence/deliver slots) loading the skill, [SILENT] no-news path,
  catalog now 16 blueprints; blueprints index regenerated

Tests: 12 skill tests incl. setup/tick split, coverage-honesty guards,
blueprint registration, and the catalog-wide skills-resolve invariant.
2026-08-08 14:09:22 -07:00
Teknium 81413f0077 docs: explain model refusal attribution 2026-08-08 12:44:18 -07:00
teknium1 91a545ab1e chore(skills/social-media-content-calendar): tighten to hardline standards, ship optional
- description 210 -> 57 chars; author credits Ben Barclay (benbarclay) first
- optional-skills/creative/ (marketing vertical, narrowest audience of
  the batch)
- dropped phantom 'image-generation-workflow' ref; visuals via the
  image_generate tool
- honest handoff language: platforms without connectors end at approved
  drafts marked handed-off, never claimed as published
- tests (10) incl. phantom-ref and honest-handoff guards
- docs regen scoped: per-skill page + one catalog row + one sidebar line
2026-08-08 12:05:19 -07:00
teknium1 99fa93035d chore(skills/weekly-review-planning): hardline polish + wire task blueprints to their skills
Skill polish:
- description 208 -> 57 chars; author credits Ben Barclay (benbarclay) first
- connector framing (google-workspace, obsidian, notion, email-inbox-triage)
- modern section order; boilerplate folded into step-local rules

Blueprint wiring (completes the batch's recipe integration):
- weekly-review blueprint loads weekly-review-planning; prompt follows the
  skill's seven-section shape, drafts-only
- morning-brief blueprint loads google-workspace; prompt points at
  references/daily-brief.md when connected
- important-mail blueprint loads email-inbox-triage
- blueprints index regenerated

Tests: 13 skill tests + two catalog invariants (every blueprint skills=
entry resolves to a real bundled skill; the four task blueprints are wired
to their procedure skills). 32 green across both files.
2026-08-08 11:53:51 -07:00
Teknium 5e1b50115f feat(compression): native OpenAI Responses server-side compaction for gpt-5.6
Opt-in via compression.codex_responses_native (default: false). When enabled,
gpt-5.6-family models on the direct OpenAI API (api.openai.com) or a ChatGPT
Codex subscription send context_management=[{type: compaction,
compact_threshold: N}] on Responses requests. OpenAI compacts server-side and
returns an encrypted compaction output item; Hermes captures it into the
existing codex_reasoning_items sidecar and replays it on later turns in place
of the pruned history — inheriting persistence, session replay, the
cross-issuer guard, and the encrypted-replay kill switch with zero new state.

Scope is deliberately hard-gated (agent/native_compaction.py, re-checked per
request): gpt-5.6 family only — gpt-5.1/5.2 fail server-side on the field
(HTTP 500 / stream stall, no structured rejection; live-verified) — and
direct OpenAI/Codex routes only; xAI, GitHub/Copilot, OpenRouter, relays,
and local servers never see the field.

Hermes' local compression stays armed as the fallback owner: the native
threshold is clamped ~8K tokens below the local trigger so the server
compacts first, and a structured provider rejection of context_management
disables native compaction for the session and retries without it
(one-shot guard in TurnRetryState).

Live-verified E2E on api.openai.com/gpt-5.6: server compaction fired at a
4K threshold, checkpoints captured and replayed, recall preserved across
3 turns; gpt-5.1 with the flag enabled stays clean (field never sent).

Direction credit: PR #76950 by @laryhorb explored native Responses
compaction; this is a minimal reimplementation on current main.
2026-08-08 11:24:45 -07:00
teknium1 20fece3b42 chore(skills/product-price-monitor): cron-recipe shape + price-watch blueprint
Skill polish (hardline standards):
- description 199 -> 58 chars; author credits Ben Barclay (benbarclay) first
- moved research/ -> productivity/ (consumer task, not research)
- restructured into Setup (foreground, once) / Tick (each scheduled run)
  phases with explicit cronjob(action='create') wiring and a state file
  at ~/.hermes/price-watches/
- dropped phantom 'flight-research' related_skills/prose refs
- Hermes-tool framing (web_extract, browser_navigate)

Blueprint half:
- new 'price-watch' Automation Blueprint (item/condition/interval_h/
  deliver slots) loading the skill via skills=(...), [SILENT] no-alert
  path, catalog now 15 blueprints; blueprints index regenerated

Tests: 12 skill tests incl. setup/tick split, state discipline, blueprint
registration + schedule resolution; existing blueprint catalog suite green
(33 total across both files).
2026-08-08 11:19:31 -07:00
Teknium 51570f4da7 feat: replace Anthropic office document skills with clean-room MIT implementations
The bundled docx, xlsx, powerpoint, and pdf skills were adapted from
Anthropic's document skills and carried their proprietary LICENSE.txt
(no derivatives, no redistribution). Flagged as critical license
findings by the SkillEvaluator Tier 1 scan of our skill tree.

This replaces all four with clean-room rewrites:

- Authored from scratch against library knowledge only (python-docx,
  openpyxl, python-pptx, pypdf/reportlab/pdfplumber — all MIT/BSD) by
  isolated subagents given functional specs, with an explicit
  prohibition on reading the prior skill content or anthropics/skills;
  session transcripts retained as provenance evidence.
- MIT licensed (LICENSE file per skill), author: Nous Research.
- Each skill: SKILL.md to house standards + argparse helper scripts
  with UTF-8-explicit I/O + its own e2e pytest suite (fixtures built
  on the fly, non-ASCII round-trips run under LC_ALL=C).
- All four pass SkillEvaluator Tier 1 pii+unicode+lint 3/3.

tests/skills/test_office_document_skills.py rewritten against the new
contracts: MIT/no-Anthropic-text invariants, scripts documented in
SKILL.md, argparse CLI shape, and a no-locale-default-open() check
(which caught and fixed a real gap: pdfplumber text reads are fine,
but the invariant scan now guards every future script).

Docs pages regenerated for the four skills (scoped; unrelated
generator drift excluded).

Honest capability deltas vs the old versions are documented per
SKILL.md (e.g. tracked-changes accept/reject and OOXML XSD validation
are not reimplemented; form flattening limits stated).
2026-08-08 10:46:20 -07:00
kshitij 206f74baac Revert "feat: add DCP context engine"
This reverts commit d7072ab914.
2026-08-08 23:09:43 +05:30
Jack Maloney d7072ab914 feat: add DCP context engine
Cherry-picked from PR #20774 by @jmmaloney4 (jmmaloney4@gmail.com).
Original commits: 4420e0b0, 44247545, bac2955d.

DCP-style model-guided context engine behind context.engine: dcp.
Adds compress tool, outbound API-call transforms, automatic dedup/purge,
and DCP-compatible config surface.

Closes #20717
Co-Authored-By: Jack Maloney <jmmaloney4@gmail.com>
2026-08-08 23:08:49 +05:30
kshitij 73997c41bb fix(tts): split long speech by provider and platform limits
Salvage of PR #17973 by @TKCen (Sebastian Hänisch), re-implemented on
current main to preserve speed/instructions/provider params,
prepare_spoken_text normalization, OPUS_VOICE_PLATFORMS, is_write_denied
path security, microsecond timestamps, and the streaming-TTS gate.

- Split long TTS text into provider-safe chunks instead of truncating
- Pack generated audio against platform upload limits (Discord 10MB,
  Telegram 50MB, configurable via tts.delivery_profiles)
- Combine chunks with ffmpeg (OGG/Opus re-encoded, MP3 stream-copied)
- Multi-file delivery when combination fails or would exceed limits
- Remove hard [:4000] truncation from all callers (cli.py, voice.py,
  gateway/run.py, gateway/platforms/base.py)
- Gemini TTS raises ValueError instead of silently truncating when
  composed prompt exceeds the provider limit

Simplify-code fixes: removed dead all_touched_paths set, added
try/finally for scratch file cleanup on exception, clean error response
on chunk failure instead of leaking stale file_path.
2026-08-08 22:54:20 +05:30
Brooklyn Nicholson 0c2cdccccc fix(build): win32 get-windows staging must skip the tarball's bundled darwin binding
The published tarball ships lib/binding/napi-9-darwin-unknown-arm64 on every
platform, so a real Windows host has both it and the downloaded win32 binding
— the classify-everything gate threw on the darwin dir and killed every
Windows pack. Stage only bindings naming the target platform (classify still
rejects impostors), stop copyGlobByExt from recursing into lib/binding, and
add a version tripwire so a get-windows bump fails the build until the
lib/windows.js rewrite is re-verified.

Also from review: the renderer answers window.read.respond with empty text
when the IPC invoke rejects (older shell / main-side throw) instead of
stalling the tool's 30s timeout; the tool schema discloses that sibling
Hermes windows are skipped; docs gain read_window_below in both references.
2026-08-08 12:17:50 -05:00
teknium1 a6ede70c2a chore(skills/meeting-action-items): tighten to hardline standards
- description 178 -> 59 chars
- author credits Ben Barclay (benbarclay) first
- dropped phantom 'Linear' connector from prose (points at notion/
  github-issues/user's tracker instead)
- Hermes-tool framing (read_file for transcripts)
- template boilerplate folded into step-local rules and skill-specific
  verification
- tests at tests/skills/test_meeting_action_items_skill.py (10 passing,
  incl. phantom-connector guard and reconcile-before-create discipline)
- docs regen scoped: per-skill page + one catalog row + one sidebar line
2026-08-08 10:06:39 -07:00
teknium1 ac662c3f71 chore(skills/google-workspace): fold daily-brief into references/, not a sibling skill
The brief is single-connector (every command comes from google-workspace),
so it ships as references/daily-brief.md with a pointer + load trigger in
SKILL.md — progressive disclosure instead of a new skill-index entry.
Contributor's procedure preserved (half-open day windows, mail-to-meeting
linking with fuzzy-match discipline, 7-section brief, bounded actions);
credit noted in the reference header. Tests (8) guard the wiring and
disciplines. Version 1.1.0 -> 1.2.0.
2026-08-08 05:59:37 -07:00
Teknium 7c2bc87f81 feat(read_extract): label each unreadable PDF gap with its preceding section text
The coverage warning listed bare page ranges, which tells the agent
WHERE the gaps are but not WHAT they contain — its only options were
guessing or OCRing everything. Each gap is now labeled with the last
text extracted before it (usually a section divider page), so the agent
can decide which gaps it actually needs and render/OCR only those.
Gap list capped at 20 entries with a summary line for pathological
alternating documents.
2026-08-08 05:51:33 -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
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 c5f71f9a51 docs: document read_file document extraction and the scanned-PDF coverage warning 2026-08-08 05:13:46 -07:00
Teknium 1dee73400e Inspired by Cursor: fail-closed hook semantics + exit-code-2 blocking 2026-08-08 05:02:04 -07:00
Teknium 2e2fcc09ff Port from superagent-ai/grok-cli: directory-chain AGENTS.md loading 2026-08-08 04:31:45 -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
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
Teknium a978f769b1 Inspired by Cursor: MCP config context variables (${userHome}, ${workspaceFolder}, ...) 2026-08-08 03:57:00 -07:00
Ken Weiner b68a532295 fix: handle replacement transforms after CLI streaming 2026-08-08 14:06:30 +05:30
kinsolee c750d5354a fix(sessions): prevent oversized transcripts from exhausting memory 2026-08-08 13:36:08 +05:30
teknium1 78bc9acdf1 chore(skills/document-to-action-items): promote to bundled tier
Fleet audit showed these task skills are commonly needed across users;
shipping bundled per Teknium's direction. Docs and tests follow the
bundled paths.
2026-08-07 10:35:42 -07:00
teknium1 7b8d0d800c chore(skills/document-to-action-items): tighten to hardline standards, move to optional
- description 214 -> 59 chars
- author credits Ben Barclay (benbarclay) first
- moved skills/productivity -> optional-skills/productivity (not a daily driver)
- dropped dangling 'linear' related_skills entry; prose points at approved destinations
- framed steps through Hermes tools (read_file, web_extract, xlsx, notion)
- trimmed template safety/verification boilerplate to doc-specific rules
- modern section order (When to Use / Procedure / Pitfalls / Verification)
- tests at tests/skills/test_document_to_action_items_skill.py (8 passing)
- docs regen scoped: per-skill page + one catalog row + one sidebar line
2026-08-07 10:35:42 -07:00
GodsBoy e288d93fc1 fix(review): harden portable plugin boundaries 2026-08-07 09:44:21 -07:00
GodsBoy ca78c6d7a6 feat(plugins): load portable agent components 2026-08-07 09:44:21 -07:00
Teknium c8369e37f4 feat(mcp): trust-tier gating for write-capable MCP tools via readOnlyHint
Adds a per-server `trust: full|untrusted` config key
(mcp_servers.<name>.trust). On an untrusted server, every write-capable
tool call — any tool whose discovery-time annotations do not carry
readOnlyHint=True — routes through the existing approval surface
(tools.approval.request_elicitation_consent, same lazy-import +
surface-routing pattern the MCP elicitation handler uses) before the RPC
fires. Denied/cancelled/errored approvals fail closed: the RPC never
runs, including the lazy first-use server spawn.

Design points:
- Classification happens at CALL TIME from metadata captured at
  DISCOVERY (_record_tool_trust_metadata in _register_server_tools and
  the lazy cache-registration path). No toolset/schema mutation, so the
  toolset stays byte-stable and prompt caching is preserved.
- readOnlyHint is a server-supplied HINT: on an untrusted server a lying
  server can at most skip approval for tools it claims read-only — it
  can never widen access. Trust tiering itself is operator config.
- Missing/malformed annotations => write-capable (fail closed).
- Unrecognized trust values => untrusted (fail closed); missing key =>
  full (backward compatible, documented in mcp-config-reference).
- The schema cache now persists readOnlyHint so lazy-registered servers
  gate identically on next startup without spawning.

Tests: tests/tools/test_mcp_trust_gating.py (11 tests, TDD red->green):
approval invoked + accept proceeds, deny/cancel blocks RPC, readOnlyHint
=true skips gate, trusted/unconfigured servers skip gate, explicit
readOnlyHint=false gated, approval exception fails closed, trust
normalization, discovery-time capture (SDK objects and cached dicts).

Ported from: cloudflare-os classifyTool() (Apache-2.0), corroborated by
Claude Cowork (idea-level).
2026-08-07 08:58:32 -07:00
Teknium 9fad45fcda feat(kanban,mcp): orphaned-card reconciliation + per-server MCP identity header
Two small config-gated features:

1. Kanban orphaned-card reconciliation (kanban.reconcile_orphans, default
   true, config.yaml): a running card with broken claim bookkeeping
   (claim_lock or claim_expires NULL — crash mid-claim, manual SQL, DB
   restore) is invisible to all existing recovery paths
   (release_stale_claims requires claim_expires NOT NULL,
   detect_crashed_workers requires host-local lock + pid,
   detect_stale_running is config-disabled by default) and shows Running
   forever. New reconcile_orphaned_running() pass in kanban_db.py runs
   each dispatch_once tick: requeues orphans to ready with an explanatory
   comment, closes any leaked run, emits a 'reconciled' event, and defers
   when the recorded PID is still alive on this host (never requeue
   beside a live worker). Surfaced via DispatchResult.reconciled_orphans.

2. Per-server MCP identity header (mcp_servers.<name>.identity_header,
   config.yaml): optional {name, value_from: static|profile, value}
   mapping; the header is attached to that server's HTTP/SSE transport
   requests. 'static' sends the config value; 'profile' resolves the
   active Hermes profile name once at connect time (no per-call
   mutation). Explicit per-server headers of the same name (any casing)
   win. Invalid blocks warn-and-ignore; stdio servers warn-and-ignore.

Tests: tests/gateway/test_kanban_reconcile_orphans.py (9),
tests/tools/test_mcp_identity_header.py (13), all written first (RED)
then implemented (GREEN). No new HERMES_* env vars.

Inspired by: openai/symphony tracker reconciliation (Apache-2.0) +
Poke per-user MCP identity (idea-level).
2026-08-07 08:58:20 -07:00
Teknium 5396da844a docs: DX sweep — 7 verified-absent documentation items
- developer-guide/codebase-ownership.md (new): subsystem -> source dirs ->
  docs entry point map; complements the narrow CODEOWNERS proposal in #23751
  (docs table only, no .github/CODEOWNERS).
- contributing.md: document the .agents/checks/*.md repo-local review
  checklist convention (idea from goose, Apache-2.0).
- integrations/index.md: "Quick connect links" table with prefilled
  create-your-app deep links (Telegram BotFather, Discord
  ?new_application=true, Slack ?new_app=1, LINE, Feishu). Poke-inspired.
- guides/agent-email-address.md (new): dedicated agent mailbox via the
  bundled himalaya skill — setup, cron polling pattern, prompt-injection
  safety notes. Poke-inspired.
- user-guide/features/browser.md: Chrome 136+ silently refuses
  --remote-debugging-port on the default user-data-dir; dedicated profile is
  now mandatory (diagnosis from oh-my-pi, MIT).
- developer-guide/adding-providers.md: "Tool-call wire format" section
  linking the OpenAI chat-completions reference as the canonical shape for
  convert_messages/convert_tools.
- user-guide/features/tools.md: shell-init pitfall — heavy/interactive rc
  files (nvm, TTY-expecting blocks) break non-interactive agent terminal
  calls; interactive-guard pattern documented (from cline, Apache-2.0).

Both new pages registered in sidebars.ts. Validated with npx docusaurus
build (en + zh-Hans green; zh-Hans relative-link warnings are the known
pre-existing untranslated-page noise).
2026-08-07 08:58:08 -07:00
Teknium ed903f953e feat(cron): pre-dispatch configuration validation (blocked_config + alert-once)
Validate a job's configuration BEFORE any agent machinery is constructed:

- missing provider API key (AuthError from a read-only
  resolve_runtime_provider probe; skipped when a fallback_providers chain
  is configured, since auth-fallback may rescue the run)
- attached skill not ready (skill_view readiness_status=setup_needed —
  missing required env vars / commands / credential files)
- delivery platform unknown or unconnected (deliver=local/origin/all are
  never checked; gateway-config load failures fail open)

On a failing check run_job returns a [blocked_config]-marked error without
constructing AIAgent/MCP/etc, so a misconfigured job never burns an LLM
call. run_one_job records last_status='blocked_config' and delivers the
alert exactly ONCE across ticks (persisted preflight_alerted bit — the
alert-once shape from the #73506 dead-pin auto-pause); the next healthy
run clears the marker so a future break re-alerts. Every preflight check
fails open: only an affirmative misconfiguration verdict blocks.

Config: cron.preflight (default true); `cron.preflight: false` restores
the old fail-during-run behavior. Documented in the cron user guide and
config defaults.

mark_job_run gains an optional status= override (unblocked call shape
unchanged) and drops preflight_alerted on any successful run.

Tests: tests/cron/test_preflight_config.py (blocked_config + no agent +
single alert across two ticks, healthy job unaffected, recovery clears
dedup, fallback-chain rescue, opt-out restores old behavior, skill
readiness miss, unknown delivery platform, deliver=local never loads
gateway config). Full tests/cron/ + cronjob tool suite green (525 tests).

Ported from: paperclipai/paperclip execution-semantics §5 (MIT);
in-repo precedent: #27948, #73506
2026-08-07 08:57:53 -07:00
HexLab98 2d0c2682c7 docs: document the gateway agent cache memory budget
Record why the cache needs a third bound and what the pressure pass will and
will not shed, so an operator tuning agent.agent_cache knows which knob to
reach for. Adds the config keys to the session-lifecycle appendix and a user
guide section covering the "auto" cgroup-derived budget.
2026-08-07 21:02:33 +05:30
kshitij e7667e56df docs(stt): honest memory-behavior wording for idle unload
The docs promised 'frees ~370MB RAM' — measured behavior on macOS/CPU
is that ctranslate2's allocator keeps the freed pages (RSS doesn't
visibly shrink); the concrete win is VRAM release on CUDA hosts and
process-internal reuse on CPU. Say exactly that instead.
2026-08-07 19:26:12 +05:30
kshitij 7b006ea6e8 feat(stt): idle unload for local whisper model
The local faster-whisper model singleton (_local_model) is loaded once
and never released — the 'base' model holds ~370 MB of RAM/VRAM for
the entire lifetime of the process, even when no voice messages arrive
for hours or days. On long-running gateway processes (especially with
local LLMs competing for the same GPU) this is wasteful.

Add a config-driven idle unload: after stt.local.unload_after_idle_seconds
(default 0 = never) of no transcription activity, a lightweight daemon
thread sets _local_model = None so the Python GC can reclaim the
ctranslate2 objects. The next voice message reloads the model
transparently (the existing lazy-load path handles it).

The watcher:
  - Checks every 30s whether idle time exceeds the configured threshold
  - Acquires _local_model_lock before unloading (prevents races with
    concurrent transcriptions that are mid-load)
  - Exits immediately if the model is already None (unloaded by another
    path, e.g. the CUDA fallback eviction)
  - Is restarted by each transcription with the current config value,
    so changing stt.local.unload_after_idle_seconds in config.yaml takes
    effect on the next voice message without a process restart

Default is 0 (never unload) — zero behavior change for existing users.
Recommended value for gateway processes: 300 (5 minutes).

15 tests: config resolution (garbage/negative/None fallbacks), unload
safety (already-None, lock acquisition), touch timestamp, watcher
lifecycle (unload after timeout, no unload within timeout, exits when
model already None, stopped on new start). Existing STT test suite
unchanged.
2026-08-07 19:26:12 +05:30
kshitij 5cff192b39 docs(stt): document the 12s short-clip gate for the cloud trim
The review-fold commit added the input-duration gate but the user-facing
docs and config example still implied every cloud clip gets trimmed.
One-line additions to both.
2026-08-07 19:26:04 +05:30
kshitij a683ef95d2 feat(stt): pre-upload silence trim for cloud providers
Local faster-whisper gets Silero VAD (bf8004e3a) so silence never
reaches the model. Cloud providers got no such protection: the raw
file uploads untouched, so every second of silence in a voice note is
paid for twice — upload time and per-audio-minute billing — and cloud
Whisper hallucinates junk tokens on silent stretches exactly like
local Whisper did before the VAD hardening. A 13s voice note with two
long pauses is billed as 13s of audio to transcribe ~6s of speech.

Close the gap client-side: before uploading to a built-in cloud
provider (groq/openai/mistral/xai/elevenlabs/deepinfra), collapse long
pauses with ffmpeg's silenceremove filter, keeping
stt.cloud_trim_keep_ms (default 300) of every pause so word boundaries
and natural pacing survive. Uses ffmpeg, already a dependency of this
exact path via _transcode_audio_for_stt — no new dependency.

The trim is strictly best-effort — ALL of these upload the original
untouched, transcription never fails because of the trim:
  - stt.cloud_trim_silence: false
  - ffmpeg/ffprobe missing, trim failure, or timeout
  - trimmed result ~empty (mostly-silence clip: the provider, not a
    client-side dB heuristic, decides whether it contains speech)
  - trim saves <10% (re-encoding for nothing)

Command-type and plugin providers are deliberately NOT trimmed: they
may wrap local CLIs that want the original bytes or run their own VAD.

E2E (real ffmpeg + faster-whisper): 13.2s voice note with 7s pause ->
6.2s upload (-53%); transcript of trimmed audio matches the original
on both utterances. Dense-speech and all-silence WAVs correctly fall
back to the original. 22 unit+E2E tests; STT/voice suite failures
identical to upstream/main baseline (all pre-existing).
2026-08-07 19:26:04 +05:30
texasich b3e9e91709 fix(gateway): configure turn lease timeout via yaml 2026-08-07 18:23:57 +05:30
texasich b2b681fefd fix(gateway): harden turn-lease timeout rejection 2026-08-07 18:23:57 +05:30
HexLab98 2ef294f020 docs: spell out the rewind contract on prompt.submit
External hosts speak this protocol directly, so the parameter that
rewrites a session's stored transcript should not be folklore. Document
what each truncation field means, that an ordinal without
confirm_truncate is refused, and that a client must never hold the
ordinal in state across ordinary submits.
2026-08-07 18:20:19 +05:30
HexLab98 d7fb503c27 docs: note that the non-stream stale budget covers cron and subagents
The timeout table already lists the stale non-stream detector, but the
prose read as if it only guarded the interactive path. Spell out that it
bounds the inline cron / delegated-subagent calls too, and name the
accepted-then-silent failure mode it recovers from.
2026-08-07 15:32:37 +05:30
kshitij f5784617e8 refactor: fold simplify-code review findings
- matrix/dingtalk: extract deps-only installers (ensure_matrix_deps,
  ensure_dingtalk_deps) and register THOSE as ensure_deps_fn — the prior
  check_*_requirements combined credential env checks with the install,
  so a platform configured via PlatformConfig.extra (which is_connected
  accepts) would pass enablement, reach create_adapter(), and have the
  'installer' veto on env-var grounds before installing anything —
  re-creating the #79812 deadlock for extra-configured setups.  The
  combined deps+credentials functions remain for setup/status callers.
- matrix/feishu passive probes: use the existing lazy_deps.is_available()
  instead of hand-rolling 'not feature_missing(...)' (reuse finding).
- teams: module docstring no longer recommends bare system pip (the
  PEP 668 trap purged everywhere else); docs troubleshooting row updated
  to match the new hint text.
- wecom_callback: drop dead 'global ET, DEFUSEDXML_AVAILABLE'
  (ensure_and_bind mutates the module dict directly; nothing assigns).
- tests: parametrized wiring contract for all 8 lazy-installable
  platforms — ensure_deps_fn present and distinct from check_fn
  (behavior contract, not identity snapshot, so renames don't churn it).
2026-08-07 13:28:43 +05:30
kshitij a658dfe509 fix: address self-review findings on the check_fn/ensure_deps_fn split
- gateway/config.py: rewrite the stale enablement-pass header comment that
  still described check_fn as 'the single source of truth for are-my-env-
  vars-set' / 'lazy-installs it' — both false under the new contract.
- teams: check_requirements docstring wrongly claimed credential checks
  (body checks only SDK/aiohttp presence); derive install_hint from the
  canonical LAZY_DEPS pins + sys.executable instead of hardcoding
  '~/.hermes/hermes-agent/venv/bin/pip' and version pins (wrong under
  HERMES_HOME overrides / profile installs; pins go stale on CVE bumps);
  connect() fatal-error hints now point at the venv pip instead of bare
  system pip (the PEP 668 trap the docs warn about).
- teams docs: drop exact version pins from the two manual-install commands
  (LAZY_DEPS is the source of truth; unpinned installs still work and the
  text can't go stale).
- hermes_cli/status.py: per-entry exception guard around check_fn so one
  raising probe can't abort the listing of all remaining plugin platforms
  (aligns with the other three call sites).
- tests: rename test_register_check_fn_is_active_lazy_installer ->
  test_register_splits_passive_probe_from_active_installer (name said the
  opposite of what it verifies).
2026-08-07 13:28:43 +05:30