Skip to content

feat(moa): add optional privacy redaction filter for reference outputs (#59959) - #60463

Closed
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:feat/59959-moa-privacy
Closed

feat(moa): add optional privacy redaction filter for reference outputs (#59959)#60463
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:feat/59959-moa-privacy

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Add optional privacy/redaction filter for MoA reference outputs. When moa.privacy_filter: true, sensitive data (emails, phones, API keys) in reference outputs is redacted. Closes #59959

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Jul 7, 2026
(cherry picked from commit fd184b037d226f8107426d4289efbdc5c4386edc)
@webtecnica
webtecnica force-pushed the feat/59959-moa-privacy branch from fd184b0 to 3428e70 Compare July 11, 2026 12:49

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding an opt-in privacy control for MoA references. The current main path does expose raw reference output, so the feature premise is valid.

Problems

  • agent/moa_loop.py:1019 redacts after the persistent MoA path has already stored raw tuples in _pending_trace (agent/moa_loop.py:1033-1038) and emitted raw text to the UI callback (agent/moa_loop.py:1047-1054). agent/moa_trace.py:143-145 serializes acct.output, which remains raw. The filter therefore does not protect the display or moa.save_traces paths described in #59959.
  • The PR adds no regression tests, despite tests/run_agent/test_moa_loop_mode.py:940-1015 already exercising real trace persistence.
  • agent/redact.py:70-305 is the established secret scrubber and covers formats absent from the new local regex list. Reusing it avoids divergent secret coverage.

Suggested changes

  • Apply redaction before caching, tracing, and moa.reference emission; preserve redacted values in the trace payload as well as aggregator guidance.
  • Reuse redact_sensitive_text(..., force=True) for secrets and add focused email/phone handling around it.
  • Add enabled/disabled tests for display events, aggregator context, cache reuse, and a saved trace.

Automated hermes-sweeper review.

Comment thread agent/moa_loop.py
@@ -957,6 +1018,13 @@ def create(self, **api_kwargs: Any) -> Any:
ref_count=_ref_count,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runs after the surrounding create() path has already populated _pending_trace and emitted every moa.reference callback. Consequently the UI and moa.save_traces still receive the original text (_RefAccounting.output is what save_moa_turn serializes). Redact immediately after the fan-out result is created, before cache/trace/callback handling, and make the trace payload use the redacted value.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
teknium1 added a commit that referenced this pull request Jul 24, 2026
Adds moa.privacy_filter ('' | display | full, default off — issue #59959):

- display: redact user-visible surfaces only (reference blocks emitted to
  the UI + saved MoA trace records, including per-advisor full input/output
  and the aggregator-input copy); the aggregator sees raw advisor text so
  synthesis quality is unaffected.
- full: additionally redact the advisor text injected into the aggregator
  prompt, on both the persistent facade path and the one-shot /moa
  synthesis path (the issue's literal ask). Legacy boolean true maps here.

Secret/credential shapes (API-key prefixes, JWTs, private keys, DB
connection strings) are delegated to the central redactor
(agent.redact.redact_sensitive_text, force=True + code_file=True); the MoA
filter adds only email and clearly delimited phone-number patterns. No
bare 10-digit matching: line numbers, timestamps, epoch values, git SHAs,
IPs, versions, and source-code assignments in code-review-shaped advisory
text pass through byte-identical. The reference cache always holds raw
text — redaction happens at each consuming surface, so a mid-session mode
change never leaks or double-redacts.

Reworked from PR #60463: replaced its hand-rolled pattern list (which
matched bare digit runs and re-implemented key shapes) with central-
redactor reuse + safe patterns, and split the single boolean into
display/full modes. Credited for the feature framing.

Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>
teknium1 added a commit that referenced this pull request Jul 24, 2026
Adds moa.privacy_filter ('' | display | full, default off — issue #59959):

- display: redact user-visible surfaces only (reference blocks emitted to
  the UI + saved MoA trace records, including per-advisor full input/output
  and the aggregator-input copy); the aggregator sees raw advisor text so
  synthesis quality is unaffected.
- full: additionally redact the advisor text injected into the aggregator
  prompt, on both the persistent facade path and the one-shot /moa
  synthesis path (the issue's literal ask). Legacy boolean true maps here.

Secret/credential shapes (API-key prefixes, JWTs, private keys, DB
connection strings) are delegated to the central redactor
(agent.redact.redact_sensitive_text, force=True + code_file=True); the MoA
filter adds only email and clearly delimited phone-number patterns. No
bare 10-digit matching: line numbers, timestamps, epoch values, git SHAs,
IPs, versions, and source-code assignments in code-review-shaped advisory
text pass through byte-identical. The reference cache always holds raw
text — redaction happens at each consuming surface, so a mid-session mode
change never leaks or double-redacts.

Reworked from PR #60463: replaced its hand-rolled pattern list (which
matched bare digit runs and re-implemented key shapes) with central-
redactor reuse + safe patterns, and split the single boolean into
display/full modes. Credited for the feature framing.

Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>
@teknium1

Copy link
Copy Markdown
Contributor

Closing with credit — the privacy filter shipped via cluster PR #70284 (commit f7b90e6) as a redesigned implementation with you credited (Co-authored-by). Changes from your version: the repo's central redaction helpers are reused for secret/credential shapes, the bare 10-digit phone pattern was replaced with delimited-format matching (line numbers/timestamps/IDs in code advisories no longer get mangled), and the filter has display/full modes — full matches your issue #59959's literal ask of redacting the aggregator input. 12 filter tests included. Thanks for pushing on this!

@teknium1 teknium1 closed this Jul 24, 2026
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
Adds moa.privacy_filter ('' | display | full, default off — issue NousResearch#59959):

- display: redact user-visible surfaces only (reference blocks emitted to
  the UI + saved MoA trace records, including per-advisor full input/output
  and the aggregator-input copy); the aggregator sees raw advisor text so
  synthesis quality is unaffected.
- full: additionally redact the advisor text injected into the aggregator
  prompt, on both the persistent facade path and the one-shot /moa
  synthesis path (the issue's literal ask). Legacy boolean true maps here.

Secret/credential shapes (API-key prefixes, JWTs, private keys, DB
connection strings) are delegated to the central redactor
(agent.redact.redact_sensitive_text, force=True + code_file=True); the MoA
filter adds only email and clearly delimited phone-number patterns. No
bare 10-digit matching: line numbers, timestamps, epoch values, git SHAs,
IPs, versions, and source-code assignments in code-review-shaped advisory
text pass through byte-identical. The reference cache always holds raw
text — redaction happens at each consuming surface, so a mid-session mode
change never leaks or double-redacts.

Reworked from PR NousResearch#60463: replaced its hand-rolled pattern list (which
matched bare digit runs and re-implemented key shapes) with central-
redactor reuse + safe patterns, and split the single boolean into
display/full modes. Credited for the feature framing.

Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add optional privacy filter for MoA reference outputs

3 participants