teleo-codex/ops/pipeline-v2/telegram/output_gate.py
m3taversal 7bfce6b706 commit telegram bot module from VPS — 20 files never previously in repo
Pulled from /opt/teleo-eval/telegram/ on VPS. Includes:
- bot.py (92K), kb_retrieval.py, kb_tools.py (agentic retrieval)
- retrieval.py (RRF merge, query decomposition, entity traversal)
- response.py (system prompt builder, response parser)
- agent_config.py, agent_runner.py (multi-agent template unit support)
- approval_stages.py, approvals.py, digest.py (approval workflow)
- eval_checks.py, eval.py (response quality checks)
- output_gate.py, x_publisher.py, x_client.py, x_search.py (X pipeline)
- market_data.py, worktree_lock.py (utilities)
- rio.yaml, theseus.yaml (agent configs)

These files were deployed to VPS but never committed to the repo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:02:32 +02:00

147 lines
5.6 KiB
Python

"""Output gate — classifies content as system/internal vs public-facing.
Blocks pipeline messages (extraction logs, merge notifications, diagnostics)
from ever reaching the tweet queue or any public-facing output.
This is a deterministic classifier — no LLM calls. Pattern matching on content.
Epimetheus owns this module.
"""
import re
# ─── System Message Patterns ─────────────────────────────────────────
# Content matching ANY of these is classified as system/internal.
_SYSTEM_PATTERNS = [
# Pipeline operations
re.compile(r"\b(PR\s*#\d+|pull request|merge|rebase|cherry.?pick)\b", re.IGNORECASE),
re.compile(r"\b(extraction|extracted|extractor|extract/)\b", re.IGNORECASE),
re.compile(r"\b(pipeline|cron|batch.?extract|systemd|teleo-pipeline)\b", re.IGNORECASE),
re.compile(r"\b(conflict.?permanent|conflict.?closed|merge.?conflict)\b", re.IGNORECASE),
# Infrastructure / ops
re.compile(r"\b(schema\s*v\d+|migration\s*v\d+|SCHEMA_VERSION)\b", re.IGNORECASE),
re.compile(r"\b(deploy|VPS|ssh|scp|systemctl|journalctl)\b", re.IGNORECASE),
re.compile(r"\b(Qdrant|embed.?on.?merge|vector.?gc|backfill)\b", re.IGNORECASE),
re.compile(r"\b(ReadWritePaths|ProtectSystem|ExecStartPre)\b", re.IGNORECASE),
# Diagnostics
re.compile(r"\b(vital.?signs|queue.?staleness|orphan.?ratio)\b", re.IGNORECASE),
re.compile(r"\b(approval.?rate|throughput|PRs?.?per.?hour)\b", re.IGNORECASE),
re.compile(r"\b(reviewer_count|reviewer.?backfill)\b", re.IGNORECASE),
# Agent coordination internals
re.compile(r"\b(Ganymede|Rhea|Oberon)\s+(review(?:ed)?|approv(?:ed|es?)|reject(?:ed|s)?)\b", re.IGNORECASE),
re.compile(r"\b(PIPELINE_OWNED_PREFIXES|AGENT_NAMES)\b"),
re.compile(r"\b(worktree|bare.?repo|forgejo|git\.livingip)\b", re.IGNORECASE),
# Code / technical
re.compile(r"\b(def\s+\w+|import\s+\w+|class\s+\w+)\b"),
re.compile(r"\b(\.py|\.yaml|\.json|\.md)\s", re.IGNORECASE),
re.compile(r"\b(sqlite3?|pipeline\.db|response_audit)\b", re.IGNORECASE),
# Internal metrics / debugging
re.compile(r"\b(cosine.?sim|threshold|PRIOR_ART_THRESHOLD)\b", re.IGNORECASE),
re.compile(r"\b(pre.?screen|Layer\s*[01234]|RRF|entity.?boost)\b", re.IGNORECASE),
# Paths
re.compile(r"/opt/teleo-eval/"),
re.compile(r"/Users/\w+/"),
re.compile(r"\.pentagon/"),
]
# ─── Public Content Signals ──────────────────────────────────────────
# Content matching these is MORE LIKELY to be public-facing.
# These don't override system classification — they're tiebreakers.
_PUBLIC_SIGNALS = [
re.compile(r"^(thread|tweet|post):", re.IGNORECASE | re.MULTILINE),
re.compile(r"\b(insight|analysis|take|perspective|argument)\b", re.IGNORECASE),
re.compile(r"\b(audience|followers|engagement|impression)\b", re.IGNORECASE),
]
class GateResult:
"""Result of output gate classification."""
__slots__ = ("is_public", "blocked_reasons", "confidence")
def __init__(self, is_public: bool, blocked_reasons: list[str], confidence: float):
self.is_public = is_public
self.blocked_reasons = blocked_reasons
self.confidence = confidence
def __bool__(self):
return self.is_public
def __repr__(self):
status = "PUBLIC" if self.is_public else "BLOCKED"
return f"GateResult({status}, reasons={self.blocked_reasons}, conf={self.confidence:.2f})"
def classify(content: str) -> GateResult:
"""Classify content as public-facing or system/internal.
Returns GateResult:
- is_public=True: safe for tweet queue / public output
- is_public=False: system content, blocked from public outputs
"""
if not content or not content.strip():
return GateResult(False, ["empty content"], 1.0)
# Count system pattern matches
system_hits = []
for pattern in _SYSTEM_PATTERNS:
match = pattern.search(content)
if match:
system_hits.append(match.group())
# Count public signals
public_hits = sum(1 for p in _PUBLIC_SIGNALS if p.search(content))
# Decision logic
if len(system_hits) >= 3:
# Strong system signal — definitely internal
return GateResult(False, system_hits[:5], 0.95)
if len(system_hits) >= 1 and public_hits == 0:
# Some system signal, no public signal — likely internal
return GateResult(False, system_hits, 0.75)
if len(system_hits) == 0:
# No system signal — public
return GateResult(True, [], 0.90 if public_hits > 0 else 0.70)
# Mixed signals (system hits + public signals) — default to blocking
# Better to block a borderline tweet than leak system info
return GateResult(False, system_hits, 0.50)
def gate_for_tweet_queue(content: str, agent: str = None) -> GateResult:
"""Gate specifically for the tweet queue. Stricter than general classify.
Additional checks:
- OPSEC filter (imported from approvals)
- Agent attribution check
"""
result = classify(content)
if not result.is_public:
return result
# Additional tweet-specific checks
blocked = []
# Must not be too short (probably a fragment or command)
stripped = content.strip()
if len(stripped) < 20:
blocked.append("content too short for tweet (<20 chars)")
# Must not contain raw URLs to internal systems
if re.search(r"https?://(?:localhost|127\.0\.0\.1|77\.42\.65\.182)", stripped):
blocked.append("contains internal URL")
if blocked:
return GateResult(False, blocked, 0.85)
return result