hermes-agent/locales
Teknium cb6c47af08
feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518)
* feat(approvals): /deny <reason> relays denial reason to the agent

Port from qwibitai/nanoclaw#2832 (reject with reason).

Gateway /deny now accepts an optional trailing reason (/deny <reason>
or /deny all <reason>). The reason rides on the per-session approval
entry through resolve_gateway_approval -> _await_gateway_decision and is
appended to the BLOCKED tool result the agent receives, so a declined
agent can adapt instead of only hearing 'denied'.

Adapted to hermes-agent's synchronous single-command /deny model: no DB
state, no second-message capture step, no migration. Reason is capped at
280 chars and threaded through both the terminal-command guard and the
execute_code guard. Plain /deny and the approve paths are unchanged.

- tools/approval.py: _ApprovalEntry.reason; resolve_gateway_approval gains
  optional reason; _await_gateway_decision returns it; both gateway BLOCKED
  messages include it
- gateway/slash_commands.py: parse leading 'all' + trailing reason
- locales/en.yaml: deny.denied_reason_{singular,plural}
- hermes_cli/commands.py: /deny args_hint '[all] [reason]'
- tests: 3 new (with-reason, all+reason, plain-deny regression)

* fix(ci): localize deny-reason keys across all locales + update interrupt-path assertions

CI surfaced two enforced invariants broken by the deny-with-reason change:
- test_i18n catalog-parity requires every locale to carry the same keys as
  en.yaml with matching placeholders. Added deny.denied_reason_singular/plural
  (with {count}/{reason}) to all 15 non-English locales.
- test_approval_interrupt asserts the exact dict from _await_gateway_decision,
  which now carries a 'reason' key (None on the interrupt/timeout paths).
2026-07-05 02:22:08 -07:00
..
af.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
de.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
en.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
es.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
fr.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
ga.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
hu.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
it.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
ja.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
ko.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
pt.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
ru.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
tr.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
uk.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
zh-hant.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00
zh.yaml feat(approvals): /deny <reason> relays denial reason to the agent (port nanoclaw#2832) (#54518) 2026-07-05 02:22:08 -07:00