Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
d409a44
fix(model): avoid bedrock credential probe in provider picker
helix4u May 2, 2026
4f37669
fix(tools): reconfigure enabled unconfigured toolsets
helix4u May 1, 2026
e26f9b2
fix(acp): route Zed thoughts to reasoning callbacks
HenkDz May 1, 2026
ef9a08a
fix(acp): polish Zed context and tool rendering
HenkDz May 2, 2026
72c8037
fix(acp): polish common tool rendering
HenkDz May 2, 2026
b294d1d
fix(acp): keep read-file starts compact
HenkDz May 2, 2026
eb612f5
fix(acp): keep web extract rendering compact
HenkDz May 2, 2026
19854c7
Schedule ACP history replay and fence file output
HenkDz May 2, 2026
9987f3d
fix(acp): compact Zed tool replay rendering
HenkDz May 2, 2026
a22465e
fix(weixin): send_weixin_direct cross-loop session check
MottledShadow May 2, 2026
9b5b88b
chore: add MottledShadow to AUTHOR_MAP
teknium1 May 3, 2026
457c7b7
feat(openrouter): add response caching support (#19132)
kshitijk4poor May 3, 2026
c4c0e5a
fix: After _clamp_command_names truncates skill names to fit the 32-cha…
CharlieKerfoot May 2, 2026
5d5b891
test: add tests for cmd_key preservation through name clamping
kshitijk4poor May 3, 2026
19ba9e4
fix(gateway/discord): require allowlist auth on slash commands
0xyg3n May 3, 2026
c14bf44
chore: add 0xyg3n noreply email to AUTHOR_MAP
kshitijk4poor May 3, 2026
6c1322b
fix(slack): close previous handler in connect() to prevent zombie Soc…
nftpoetrist May 2, 2026
0a97ce6
chore: add nftpoetrist to AUTHOR_MAP
kshitijk4poor May 3, 2026
f1e0292
fix(gateway): resume sessions after crash/restart instead of blanket …
millerc79 May 3, 2026
bf32394
chore: add millerc79 to AUTHOR_MAP
kshitijk4poor May 3, 2026
9341034
fix(gateway): send /new response before cancel_session_processing to …
May 2, 2026
7a22c63
chore: add shellybotmoyer to AUTHOR_MAP
kshitijk4poor May 3, 2026
1148c46
fix(gateway): correct ws scheme conversion for https urls
CharlieKerfoot May 2, 2026
6f2dab2
fix: update tests for resume_pending semantics + add AUTHOR_MAP entries
kshitijk4poor May 3, 2026
55647a5
fix(whatsapp): pin protobufjs >=7.5.5 via npm overrides to clear 3 cr…
teknium1 May 3, 2026
d87fd9f
fix(goals): make /goal work in TUI and fix gateway verdict delivery (…
teknium1 May 3, 2026
b59bb4e
fix(gateway): preserve home-channel thread targets across restart not…
leprincep35700 May 1, 2026
3c59566
chore(release): map leprincep35700 email for PR #18440 salvage
teknium1 May 3, 2026
69dd0f7
fix(approval): extend sensitive write target to cover shell RC and cr…
JasonOA888 May 3, 2026
6b4fb9f
fix(cron): treat non-dict origin as missing instead of crashing tick
Tranquil-Flow May 2, 2026
e527240
fix(tools): write_file handler now rejects missing 'content'/'path' a…
Bartok9 May 3, 2026
279b656
fix(tui): clear Apple Terminal resize artifacts
OutThisLife May 3, 2026
e97a999
Merge pull request #19307 from NousResearch/bb/fix-terminal-resize-ju…
OutThisLife May 3, 2026
511add7
feat(skill): add video-orchestrator optional creative skill
SHL0MS May 3, 2026
0dd8e3f
rename: video-orchestrator → kanban-video-orchestrator
SHL0MS May 3, 2026
c9a3f36
feat: add video_analyze tool for native video understanding (#19301)
alt-glitch May 3, 2026
b8ae8cc
fix(debug): redact log content at upload time in hermes debug share
GodsBoy May 3, 2026
9eaddfa
fix(cli): CLI/TUI on local backend always uses launch directory, igno…
alt-glitch May 3, 2026
167b564
Revert "fix(cli): CLI/TUI on local backend always uses launch directo…
alt-glitch May 3, 2026
8a1cb11
Merge upstream/main into sync/upstream-20260503
bot-ted May 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 82 additions & 7 deletions hermes_cli/debug.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
"""``hermes debug`` debug tools for Hermes Agent.
"""``hermes debug`` debug tools for Hermes Agent.

Currently supports:
hermes debug share Upload debug report (system info + logs) to a
paste service and print a shareable URL.
By default, log content is run through
``agent.redact.redact_sensitive_text`` with
``force=True`` before upload so credentials in
``~/.hermes/logs/*.log`` are not leaked into
the public paste service. Pass ``--no-redact``
to disable.
"""

import io
import json
import logging
import sys
import time
import urllib.error
Expand All @@ -19,6 +26,16 @@
from hermes_constants import get_hermes_home
from utils import atomic_replace

logger = logging.getLogger(__name__)

# Banner prepended to upload-bound log content when redaction is enabled.
# Visible in the public paste so reviewers know the content was sanitized.
# Kept short; the trailing newline guarantees the banner sits on its own line.
_REDACTION_BANNER = (
"[hermes debug share: log content redacted at upload time. "
"run with --no-redact to disable]\n"
)


# ---------------------------------------------------------------------------
# Paste services — try paste.rs first, dpaste.com as fallback.
Expand Down Expand Up @@ -368,17 +385,40 @@ def _resolve_log_path(log_name: str) -> Optional[Path]:
return None


def _redact_log_text(text: str) -> str:
"""Run ``redact_sensitive_text`` with ``force=True`` over upload-bound text.

Uses ``force=True`` so redaction fires regardless of the operator's
``security.redact_secrets`` setting. The local on-disk log file is
not modified; only the in-memory copy headed for the public paste
service is sanitized. Returns the redacted text (or the original
when empty / non-string).
"""
if not text:
return text
from agent.redact import redact_sensitive_text

return redact_sensitive_text(text, force=True)


def _capture_log_snapshot(
log_name: str,
*,
tail_lines: int,
max_bytes: int = _MAX_LOG_BYTES,
redact: bool = True,
) -> LogSnapshot:
"""Capture a log once and derive summary/full-log views from it.

The report tail and standalone log upload must come from the same file
snapshot. Otherwise a rotation/truncate between reads can make the report
look newer than the uploaded ``agent.log`` paste.

When ``redact`` is True (the default), both ``tail_text`` and
``full_text`` are run through ``_redact_log_text`` so the snapshot
returned is upload-safe. The on-disk log file is never modified.
Pass ``redact=False`` to capture original log content (used by
``hermes debug share --no-redact``).
"""
log_path = _resolve_log_path(log_name)
if log_path is None:
Expand Down Expand Up @@ -438,18 +478,34 @@ def _capture_log_snapshot(
if truncated:
full_text = f"[... truncated — showing last ~{max_bytes // 1024}KB ...]\n{full_text}"

if redact:
tail_text = _redact_log_text(tail_text)
full_text = _redact_log_text(full_text)

return LogSnapshot(path=log_path, tail_text=tail_text, full_text=full_text)
except Exception as exc:
return LogSnapshot(path=log_path, tail_text=f"(error reading: {exc})", full_text=None)


def _capture_default_log_snapshots(log_lines: int) -> dict[str, LogSnapshot]:
"""Capture all logs used by debug-share exactly once."""
def _capture_default_log_snapshots(
log_lines: int, *, redact: bool = True
) -> dict[str, LogSnapshot]:
"""Capture all logs used by debug-share exactly once.

``redact`` is forwarded to each ``_capture_log_snapshot`` call so all
captured logs share the same redaction policy for a given run.
"""
errors_lines = min(log_lines, 100)
return {
"agent": _capture_log_snapshot("agent", tail_lines=log_lines),
"errors": _capture_log_snapshot("errors", tail_lines=errors_lines),
"gateway": _capture_log_snapshot("gateway", tail_lines=errors_lines),
"agent": _capture_log_snapshot(
"agent", tail_lines=log_lines, redact=redact
),
"errors": _capture_log_snapshot(
"errors", tail_lines=errors_lines, redact=redact
),
"gateway": _capture_log_snapshot(
"gateway", tail_lines=errors_lines, redact=redact
),
}


Expand Down Expand Up @@ -532,15 +588,24 @@ def run_debug_share(args):
log_lines = getattr(args, "lines", 200)
expiry = getattr(args, "expire", 7)
local_only = getattr(args, "local", False)
redact = not getattr(args, "no_redact", False)

if not local_only:
print(_PRIVACY_NOTICE)

print("Collecting debug report...")

# Capture dump once — prepended to every paste for context.
# The dump is already redacted at extract time via dump.py:_redact;
# log_snapshots are redacted by _capture_default_log_snapshots when
# redact=True so credentials never reach the public paste service.
dump_text = _capture_dump()
log_snapshots = _capture_default_log_snapshots(log_lines)
log_snapshots = _capture_default_log_snapshots(log_lines, redact=redact)

if redact:
logger.info(
"hermes debug share: applied force-mode redaction to log snapshots before upload"
)

report = collect_debug_report(
log_lines=log_lines,
Expand All @@ -556,6 +621,15 @@ def run_debug_share(args):
if gateway_log:
gateway_log = dump_text + "\n\n--- full gateway.log ---\n" + gateway_log

# Visible banner so reviewers reading the public paste know redaction
# was applied at upload time. Banner is omitted under --no-redact.
if redact:
report = _REDACTION_BANNER + report
if agent_log:
agent_log = _REDACTION_BANNER + agent_log
if gateway_log:
gateway_log = _REDACTION_BANNER + gateway_log

if local_only:
print(report)
if agent_log:
Expand Down Expand Up @@ -666,6 +740,7 @@ def run_debug(args):
print(" --lines N Number of log lines to include (default: 200)")
print(" --expire N Paste expiry in days (default: 7)")
print(" --local Print report locally instead of uploading")
print(" --no-redact Disable upload-time secret redaction (default: redact)")
print()
print("Options (delete):")
print(" <url> ... One or more paste URLs to delete")
11 changes: 11 additions & 0 deletions hermes_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8891,6 +8891,7 @@ def main():
hermes debug share --lines 500 Include more log lines
hermes debug share --expire 30 Keep paste for 30 days
hermes debug share --local Print report locally (no upload)
hermes debug share --no-redact Disable upload-time secret redaction
hermes debug delete <url> Delete a previously uploaded paste
""",
)
Expand All @@ -8916,6 +8917,16 @@ def main():
action="store_true",
help="Print the report locally instead of uploading",
)
share_parser.add_argument(
"--no-redact",
action="store_true",
help=(
"Disable upload-time secret redaction (default: redact). Logs "
"are normally run through agent.redact.redact_sensitive_text "
"with force=True before upload so credentials are not leaked "
"into the public paste service."
),
)
delete_parser = debug_sub.add_parser(
"delete",
help="Delete a paste uploaded by 'hermes debug share'",
Expand Down
2 changes: 2 additions & 0 deletions scripts/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,8 @@
"ztzheng@163.com": "chengoak", # PR #17467
"24110240104@m.fudan.edu.cn": "YuShu", # co-author only
"charliekerfoot@gmail.com": "CharlieKerfoot", # PR #18951
# Debug share upload-time redaction (May 2026)
"dhuysamen@gmail.com": "GodsBoy", # PR #19318
}


Expand Down
Loading
Loading