Buckets:

cmpatino's picture
download
raw
45.3 kB
#!/usr/bin/env python3
"""share_trace.py — share this session's stats (and optionally its full trace).
The deliberate, session-boundary command (see TRACES_DESIGN.md). It parses your
harness's NATIVE session log into a small manifest (token + tool-call stats),
writes the bundle into YOUR OWN scratch bucket, and calls ``POST /v1/traces`` —
the same promote ergonomic as results/artifacts. Identity is your bucket name;
no token rides on the call.
WHAT THIS READS AND UPLOADS — for operators, agents, and safety classifiers:
* Reads ONLY this session's harness log (auto-detected from the invoking
harness's env, or the explicit --transcript), plus an explicitly supplied
--redact-pattern-file. It does NOT read .env, credential files, or anything
else in the project.
* The DEFAULT (`stats`) uploads only token + tool-call COUNTS — no prompts, no
code, no file contents. `--full` additionally uploads the native session log
with credentials and personal identifiers pseudonymized CLIENT-SIDE first.
* Destination is YOUR OWN Hugging Face scratch bucket in the challenge org (via
the `hf` CLI you already use), then the collab's own API — never an arbitrary
external host. It uses your existing `hf` login; the script reads/transmits
no token, and manifest strings pass through the same scrubber.
* `--dry-run` prints the manifest and a typed redaction summary without writing
or uploading anything — run it first to verify.
python share_trace.py # stats only; no content leaves (the floor)
python share_trace.py --upload-only # write to scratch bucket; skip backend promotion
python share_trace.py --full --yes # FULL: stats + balanced-redacted log -> library
python share_trace.py --full --privacy secrets # credentials only; preserve PII
python share_trace.py --full --privacy strict # also pseudonymize hosts + IPs
python share_trace.py --full --raw # UNSAFE: full, skip all redaction
python share_trace.py --dry-run # print the plan + manifest; touch nothing
`full` lets Hugging Face's built-in trace viewer render the native log directly
from the bucket (Claude Code & Codex supported out of the box). Redaction parses
JSONL and makes surgical, typed substitutions that preserve prompts, responses,
commands, tool structure, and relative paths. It is still best-effort and cannot
infer whether ordinary task prose or source code is confidential. Your scratch
bucket is org-readable, so content is scrubbed before it is written there at all.
`--full` needs confirmation; pass `--yes` for non-interactive / agent runs.
Auto-detection follows the harness that INVOKES this script (from its env —
Claude Code's CLAUDE_CODE_SESSION_ID pins the *exact* session), so multiple
agents sharing one directory are never cross-attributed. Override with
`--harness` / `--transcript`.
SELF-CONTAINED + DEPENDENCY-FREE by design: download this one file and run it
under ANY `python3` — no `pip install`. The frontmatter is emitted as JSON
(which is valid YAML, so the backend parses it identically) and the upload
shells out to the `hf` CLI (which you already use for `hf auth login`). Org/slug
are auto-discovered from the backend's `GET /v1`, so you only need `--backend`
(or `COLLAB_BACKEND`) and your `--agent-id`. The per-harness adapters are inlined
below; keep them in sync with the verified recipes (memory:
cc-codex-trace-metric-extraction).
"""
from __future__ import annotations
import argparse
import glob
import json
import os
import re
import shutil
import subprocess
import sys
import tempfile
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path
ADAPTER_VERSION = 1
REDACTOR_VERSION = 2
KNOWN_HARNESSES = ("claude-code", "codex")
PRIVACY_LEVELS = ("secrets", "balanced", "strict")
# ════════════════════════ per-harness adapters ════════════════════════
# A harness's NATIVE local session log -> manifest fields. NOT OpenTelemetry.
# Cardinal rule: a metric we couldn't determine is OMITTED (null = unknown);
# only a measured zero is 0. Parse defensively — these formats are unversioned.
def _jsonl(path: Path):
"""Yield parsed JSON objects from a .jsonl file, skipping unparseable lines."""
with path.open(encoding="utf-8", errors="replace") as fh:
for line in fh:
line = line.strip()
if not line:
continue
try:
obj = json.loads(line)
except json.JSONDecodeError:
continue
if isinstance(obj, dict):
yield obj
def _int(v) -> int | None:
return int(v) if isinstance(v, (int, float)) and not isinstance(v, bool) else None
def adapter_claude_code(log_path: Path) -> dict:
"""~/.claude/projects/<slug>/<session_id>.jsonl — per-response usage is SUMMED."""
usage = {"input_tokens": 0, "output_tokens": 0, "cache_read_tokens": 0, "cache_creation_tokens": 0}
saw_usage = False
tools: dict[str, int] = {}
api_requests = 0
model = None
session_id = log_path.stem
first_ts = last_ts = None
for rec in _jsonl(log_path):
ts = rec.get("timestamp")
if ts:
first_ts = first_ts or ts
last_ts = ts
if rec.get("sessionId"):
session_id = rec["sessionId"]
if rec.get("type") != "assistant":
continue
api_requests += 1
msg = rec.get("message") or {}
if msg.get("model"):
model = msg["model"]
u = msg.get("usage") or {}
if u:
saw_usage = True
usage["input_tokens"] += _int(u.get("input_tokens")) or 0
usage["output_tokens"] += _int(u.get("output_tokens")) or 0
usage["cache_read_tokens"] += _int(u.get("cache_read_input_tokens")) or 0
usage["cache_creation_tokens"] += _int(u.get("cache_creation_input_tokens")) or 0
for block in msg.get("content") or []:
if isinstance(block, dict) and block.get("type") == "tool_use":
name = block.get("name") or "?"
tools[name] = tools.get(name, 0) + 1
fields: dict = {
"harness": "claude-code",
"session_id": session_id,
"model": model,
"started_at": first_ts,
"ended_at": last_ts,
"activity": {"tool_calls": sum(tools.values()), "tool_calls_by_name": tools},
"extensions": {"api_requests": api_requests},
}
if saw_usage:
usage["total_tokens"] = sum(usage.values())
fields["usage"] = usage
return fields
_CODEX_TOOL_TYPES = ("function_call", "custom_tool_call", "local_shell_call", "web_search_call")
def adapter_codex(log_path: Path) -> dict:
"""~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl — token_count is CUMULATIVE
(take the last); dedupe tool calls by call_id (MCP appears twice)."""
last_usage = None
tools: dict[str, int] = {}
seen_calls: set[str] = set()
turns = 0
model = None
session_id = None
first_ts = last_ts = None
def _count(name: str, call_id) -> None:
if call_id is not None:
if call_id in seen_calls:
return
seen_calls.add(call_id)
tools[name] = tools.get(name, 0) + 1
for rec in _jsonl(log_path):
ts = rec.get("timestamp")
if ts:
first_ts = first_ts or ts
last_ts = ts
typ = rec.get("type")
payload = rec.get("payload") or {}
if not isinstance(payload, dict):
continue
if typ == "session_meta":
session_id = payload.get("session_id") or payload.get("id") or session_id
model = model or payload.get("model")
elif typ == "turn_context":
model = model or payload.get("model")
elif typ == "response_item":
pt = payload.get("type")
if pt in _CODEX_TOOL_TYPES:
name = payload.get("name") or ("shell" if pt == "local_shell_call" else pt)
_count(name, payload.get("call_id"))
elif typ == "event_msg":
pt = payload.get("type")
if pt == "token_count":
info = payload.get("info") or {}
if info.get("total_token_usage"):
last_usage = info["total_token_usage"]
elif pt in ("task_complete", "turn_complete"):
turns += 1
elif pt == "mcp_tool_call_end":
_count(payload.get("tool") or payload.get("name") or "mcp", payload.get("call_id"))
fields: dict = {
"harness": "codex",
"session_id": session_id or log_path.stem,
"model": model,
"started_at": first_ts,
"ended_at": last_ts,
"activity": {"tool_calls": sum(tools.values()), "tool_calls_by_name": tools},
"extensions": {"turns": turns},
}
if last_usage:
fields["usage"] = {
"input_tokens": _int(last_usage.get("input_tokens")),
"output_tokens": _int(last_usage.get("output_tokens")),
"cache_read_tokens": _int(last_usage.get("cached_input_tokens")),
"cache_creation_tokens": None, # Codex doesn't separate cache-creation
"reasoning_tokens": _int(last_usage.get("reasoning_output_tokens")),
"total_tokens": _int(last_usage.get("total_tokens")),
}
return fields
def adapter_minimal(log_path: Path, harness: str) -> dict:
"""Unknown harness: ship the raw log + a minimal manifest. No stats — the
backend records this as `partial` and never blocks participation."""
return {
"harness": harness,
"session_id": log_path.stem,
"model": None,
"started_at": None,
"ended_at": None,
}
ADAPTERS = {"claude-code": adapter_claude_code, "codex": adapter_codex}
def build_fields(harness: str, log_path: Path) -> dict:
fn = ADAPTERS.get(harness)
return fn(log_path) if fn else adapter_minimal(log_path, harness)
def _cc_project_dir(cwd: str) -> Path:
slug = re.sub(r"[/._]", "-", os.path.abspath(cwd))
return Path.home() / ".claude" / "projects" / slug
def _latest(paths: list[Path]) -> Path | None:
files = [p for p in paths if p.is_file()]
return max(files, key=lambda p: p.stat().st_mtime) if files else None
def _detect_claude_code(cwd: str) -> Path | None:
cc_dir = _cc_project_dir(cwd)
return _latest(list(cc_dir.glob("*.jsonl"))) if cc_dir.is_dir() else None
def _codex_logs() -> list[Path]:
codex_root = Path.home() / ".codex" / "sessions"
return sorted(
[Path(p) for p in glob.glob(str(codex_root / "**" / "rollout-*.jsonl"), recursive=True)],
key=lambda p: p.stat().st_mtime if p.is_file() else 0,
reverse=True,
)
def _mentions_cwd(value, cwd: str) -> bool:
cwd_abs = os.path.abspath(cwd)
if isinstance(value, str):
if cwd_abs in value:
return True
try:
return os.path.abspath(os.path.expanduser(value)) == cwd_abs
except ValueError:
return False
if isinstance(value, dict):
return any(_mentions_cwd(v, cwd_abs) for v in value.values())
if isinstance(value, list):
return any(_mentions_cwd(v, cwd_abs) for v in value)
return False
def _codex_matches_cwd(path: Path, cwd: str) -> bool:
for i, rec in enumerate(_jsonl(path)):
if i >= 250:
break
if _mentions_cwd(rec, cwd):
return True
return False
def _detect_codex(cwd: str) -> tuple[Path | None, bool]:
logs = _codex_logs()
for path in logs:
if _codex_matches_cwd(path, cwd):
return path, False
return (logs[0], True) if logs else (None, False)
def _infer_harness(log_path: Path) -> str | None:
s = str(log_path)
if "/.codex/sessions/" in s or log_path.name.startswith("rollout-"):
return "codex"
if "/.claude/projects/" in s:
return "claude-code"
return None
def _running_harness() -> tuple[str | None, str | None]:
"""Identify the harness INVOKING this script from its injected env, plus the
exact session id when the harness exposes one. (None, None) if unknown.
This is what keeps multiple agents in one directory from being cross-
attributed (e.g. a Codex agent uploading a co-located Claude Code log):
- Claude Code sets CLAUDE_CODE_SESSION_ID (the exact session) + CLAUDECODE=1.
- Codex sets CODEX_SANDBOX* in its (default) sandboxed exec but exposes NO
session id — so we know it's Codex, but still locate the rollout by cwd.
"""
sid = os.environ.get("CLAUDE_CODE_SESSION_ID")
if sid or os.environ.get("CLAUDECODE"):
return "claude-code", (sid or None)
if os.environ.get("CODEX_SANDBOX") or os.environ.get("CODEX_SANDBOX_NETWORK_DISABLED"):
return "codex", None
return None, None
def _cc_session_log(cwd: str, session_id: str) -> Path | None:
"""The exact Claude Code transcript for a session id, if it exists."""
p = _cc_project_dir(cwd) / f"{session_id}.jsonl"
return p if p.is_file() else None
def _cc_session_count(cwd: str) -> int:
d = _cc_project_dir(cwd)
return len(list(d.glob("*.jsonl"))) if d.is_dir() else 0
def detect(cwd: str, harness: str) -> tuple[str, Path, bool]:
"""Detect the native session log of the agent INVOKING this script.
Anchored on the invoking harness's environment (see _running_harness) so
agents sharing a directory aren't cross-attributed. Returns
(harness, path, uncertain); `uncertain` asks main() to confirm when the
exact session could not be pinned.
"""
env_harness, env_sid = _running_harness()
if harness == "auto":
if env_harness:
harness = env_harness
else:
# The env doesn't say who's running. Use the sole candidate; if both
# harnesses have one, refuse rather than guess (the cross-attrib bug).
cc = _detect_claude_code(cwd)
cx, cx_fallback = _detect_codex(cwd)
if cc and cx:
raise SystemExit(
"multiple harnesses have a session for this directory and the "
"environment doesn't identify the running agent — pass "
"--harness claude-code|codex (or --transcript <path>)."
)
if cc:
return "claude-code", cc, _cc_session_count(cwd) > 1
if cx:
return "codex", cx, cx_fallback
raise SystemExit(
"could not auto-detect a session log; pass --harness and --transcript"
)
elif env_harness and env_harness != harness:
print(f"warning: --harness {harness}, but this looks like a {env_harness} "
"session from the environment; proceeding as requested.")
if harness == "claude-code":
if env_sid:
pinned = _cc_session_log(cwd, env_sid)
if pinned:
return "claude-code", pinned, False # exact session — no ambiguity
raise SystemExit(
f"CLAUDE_CODE_SESSION_ID={env_sid} has no transcript under "
f"{_cc_project_dir(cwd)}; refusing to guess another session. "
"Pass --transcript <path> if the transcript lives elsewhere."
)
cc = _detect_claude_code(cwd)
if cc:
return "claude-code", cc, (env_sid is None and _cc_session_count(cwd) > 1)
raise SystemExit("could not find a Claude Code session for this cwd; pass --transcript")
if harness == "codex":
cx, cx_fallback = _detect_codex(cwd)
if cx:
return "codex", cx, cx_fallback
raise SystemExit("could not find a Codex rollout; pass --transcript")
raise SystemExit(f"unknown harness: {harness!r}")
# ════════════════════════ manifest + upload ════════════════════════
# Best-effort, structure-preserving scrubber. Provider signatures catch secrets
# wherever they appear; context patterns catch opaque values next to sensitive
# keys. Deliberately avoid generic entropy detection: traces legitimately contain
# commit SHAs, call IDs, hashes, and generated identifiers.
_PROVIDER_SECRET_PATTERNS = [
("HF_TOKEN", re.compile(r"(?<![A-Za-z0-9_])hf_[A-Za-z0-9]{20,}(?![A-Za-z0-9_])")),
(
"GITHUB_TOKEN",
re.compile(
r"(?<![A-Za-z0-9_])(?:gh[pousr]_[A-Za-z0-9]{20,}|"
r"github_pat_[A-Za-z0-9_]{20,})(?![A-Za-z0-9_])"
),
),
("SK_TOKEN", re.compile(r"(?<![A-Za-z0-9_-])sk-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])")),
("AWS_ACCESS_KEY", re.compile(r"(?<![0-9A-Z])(?:AKIA|ASIA)[0-9A-Z]{16}(?![0-9A-Z])")),
("SLACK_TOKEN", re.compile(r"(?<![A-Za-z0-9-])xox[baprs]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])")),
("GITLAB_TOKEN", re.compile(r"(?<![A-Za-z0-9_-])glpat-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])")),
("GOOGLE_API_KEY", re.compile(r"(?<![A-Za-z0-9_-])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9_-])")),
("NPM_TOKEN", re.compile(r"(?<![A-Za-z0-9_])npm_[A-Za-z0-9]{36}(?![A-Za-z0-9_])")),
("PYPI_TOKEN", re.compile(r"(?<![A-Za-z0-9_-])pypi-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])")),
(
"JWT",
re.compile(
r"(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{8,}\."
r"[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}(?![A-Za-z0-9_-])"
),
),
]
_PRIVATE_KEY_RE = re.compile(
r"-----BEGIN (?P<label>[A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?)-----.*?"
r"-----END (?P=label)-----",
re.DOTALL,
)
_AUTH_HEADER_RE = re.compile(
r"(?i)(?P<prefix>\b(?:proxy[-_])?authorization\b"
r"(?:\\?[\"']?\s*[:=]\s*\\?[\"']?\s*))"
r"(?P<scheme>bearer|basic|token|api[-_]?key)\s+"
r"(?P<value>[A-Za-z0-9._~+/\-=]{4,})"
)
_COOKIE_HEADER_RE = re.compile(
r"(?i)(?P<prefix>\b(?:set-cookie|cookie)\b\s*:\s*)"
r"(?P<value>[^\"'\r\n]+)"
)
_CREDENTIAL_URL_RE = re.compile(
r"(?i)(?P<scheme>\b(?:https?|postgres(?:ql)?|mysql|mariadb|"
r"mongodb(?:\+srv)?|redis|amqps?|ssh|sftp|ftp)://)"
r"(?P<username>[^:@/\s]+):(?P<password>[^@/\s]+)@"
)
_QUERY_SECRET_RE = re.compile(
r"(?i)(?P<prefix>[?&](?P<key>access[_-]?token|refresh[_-]?token|"
r"api[_-]?key|token|secret|password|sig|signature)=)"
r"(?P<value>[^&#\s\"']+)"
)
_TEXT_SECRET_KEY = (
r"password|passwd|pwd|secret|secret[_-]?key|client[_-]?secret|"
r"api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|"
r"session[_-]?token|aws[_-]?secret[_-]?access[_-]?key"
)
_QUOTED_SECRET_RE = re.compile(
rf"(?i)(?P<key>\b(?:{_TEXT_SECRET_KEY})\b)"
r"(?P<sep>\s*[:=]\s*)(?P<quote>[\"'])"
r"(?P<value>[^\r\n]*?)(?P=quote)"
)
_UNQUOTED_SECRET_RE = re.compile(
rf"(?i)(?P<key>\b(?:{_TEXT_SECRET_KEY})\b)"
r"(?P<sep>\s*[:=]\s*)(?![\"'])(?P<value>[^\s,;}\]]+)"
)
_EMAIL_RE = re.compile(
r"(?<![A-Za-z0-9._%+-])[A-Za-z0-9._%+-]+@"
r"[A-Za-z0-9.-]+\.[A-Za-z]{2,}(?![A-Za-z0-9.-])"
)
_POSIX_HOME_RE = re.compile(r"(?<![A-Za-z0-9])(?:/(?:Users|home)/[^/\s\"']+|/root)(?=/)")
_WINDOWS_HOME_RE = re.compile(r"(?i)(?<![A-Za-z0-9])(?:[A-Z]:\\Users\\[^\\\s\"']+)(?=\\)")
_URL_HOST_RE = re.compile(
r"(?i)(?P<prefix>\b[a-z][a-z0-9+.-]*://(?:[^@/\s]+@)?)"
r"(?P<host>\[[0-9A-Fa-f:.]+\]|localhost|"
r"(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,}|(?:\d{1,3}\.){3}\d{1,3})"
)
_IPV4_RE = re.compile(r"(?<![A-Za-z0-9.])(?:\d{1,3}\.){3}\d{1,3}(?![A-Za-z0-9.])")
_PLACEHOLDER_RE = re.compile(r"^<REDACTED:[A-Z0-9_]+_\d+>$")
_SENSITIVE_KEYS = {
"authorization": "AUTHORIZATION",
"proxy_authorization": "AUTHORIZATION",
"password": "PASSWORD",
"passwd": "PASSWORD",
"pwd": "PASSWORD",
"secret": "SECRET",
"secret_key": "SECRET",
"client_secret": "CLIENT_SECRET",
"api_key": "API_KEY",
"apikey": "API_KEY",
"x_api_key": "API_KEY",
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"id_token": "ID_TOKEN",
"session_token": "SESSION_TOKEN",
"token": "TOKEN",
"aws_secret_access_key": "AWS_SECRET_ACCESS_KEY",
"aws_session_token": "AWS_SESSION_TOKEN",
"cookie": "COOKIE",
"set_cookie": "COOKIE",
"private_key": "PRIVATE_KEY",
}
def _normalise_key(key: object) -> str:
return re.sub(r"[^a-z0-9]+", "_", str(key).strip().lower()).strip("_")
def _custom_patterns(path: str | None) -> list[re.Pattern]:
"""Load one non-empty regex per line from an explicitly supplied file."""
if not path:
return []
pattern_path = Path(path).expanduser()
if not pattern_path.is_file():
raise SystemExit(f"no such redaction pattern file: {pattern_path}")
out = []
for lineno, raw in enumerate(pattern_path.read_text(encoding="utf-8").splitlines(), 1):
pattern = raw.strip()
if not pattern or pattern.startswith("#"):
continue
try:
compiled = re.compile(pattern)
except re.error as exc:
raise SystemExit(
f"invalid regex in {pattern_path}:{lineno}: {exc}"
) from exc
if compiled.search(""):
raise SystemExit(
f"redaction regex in {pattern_path}:{lineno} matches empty text"
)
out.append(compiled)
return out
class TraceRedactor:
"""JSON-aware scrubber with stable, typed aliases for one trace."""
def __init__(
self,
privacy: str = "balanced",
custom_patterns: list[re.Pattern] | None = None,
):
if privacy not in PRIVACY_LEVELS:
raise ValueError(f"unknown privacy level: {privacy!r}")
self.privacy = privacy
self.custom_patterns = custom_patterns or []
self._aliases: dict[str, tuple[str, str]] = {}
self._next: dict[str, int] = {}
self._counts: dict[str, int] = {}
@staticmethod
def _is_placeholder(value: object) -> bool:
return isinstance(value, str) and bool(_PLACEHOLDER_RE.fullmatch(value))
def _alias(self, category: str, value: object) -> str:
if self._is_placeholder(value):
return str(value)
if isinstance(value, str):
identity = value
else:
identity = json.dumps(value, sort_keys=True, ensure_ascii=False, default=str)
existing = self._aliases.get(identity)
if existing:
actual_category, placeholder = existing
else:
actual_category = re.sub(r"[^A-Z0-9]+", "_", category.upper()).strip("_")
index = self._next.get(actual_category, 0) + 1
self._next[actual_category] = index
placeholder = f"<REDACTED:{actual_category}_{index}>"
self._aliases[identity] = (actual_category, placeholder)
self._counts[actual_category] = self._counts.get(actual_category, 0) + 1
return placeholder
def _auth_value(self, value: str, category: str = "AUTHORIZATION") -> str:
if self._is_placeholder(value):
return value
match = re.fullmatch(
r"(?is)\s*(bearer|basic|token|api[-_]?key)\s+(.+?)\s*", value
)
if not match:
return self._alias(category, value)
scheme, credential = match.groups()
kind = {
"bearer": "BEARER_TOKEN",
"basic": "BASIC_CREDENTIAL",
"token": "AUTH_TOKEN",
"apikey": "API_KEY",
"api-key": "API_KEY",
"api_key": "API_KEY",
}[scheme.lower()]
return f"{scheme} {self._alias(kind, credential)}"
def _redact_text(self, text: str) -> str:
if not text:
return text
text = _PRIVATE_KEY_RE.sub(
lambda m: self._alias("PRIVATE_KEY", m.group(0)), text
)
def auth_repl(match: re.Match) -> str:
scheme = match.group("scheme")
value = match.group("value")
return match.group("prefix") + self._auth_value(f"{scheme} {value}")
text = _AUTH_HEADER_RE.sub(auth_repl, text)
text = _COOKIE_HEADER_RE.sub(
lambda m: m.group("prefix") + self._alias("COOKIE", m.group("value").rstrip())
+ m.group("value")[len(m.group("value").rstrip()):],
text,
)
text = _CREDENTIAL_URL_RE.sub(
lambda m: (
m.group("scheme")
+ self._alias("USERNAME", m.group("username"))
+ ":"
+ self._alias("PASSWORD", m.group("password"))
+ "@"
),
text,
)
text = _QUERY_SECRET_RE.sub(
lambda m: m.group("prefix")
+ self._alias(_normalise_key(m.group("key")), m.group("value")),
text,
)
def quoted_repl(match: re.Match) -> str:
value = match.group("value")
if self._is_placeholder(value):
return match.group(0)
return (
match.group("key")
+ match.group("sep")
+ match.group("quote")
+ self._alias(_normalise_key(match.group("key")), value)
+ match.group("quote")
)
def unquoted_repl(match: re.Match) -> str:
value = match.group("value")
if self._is_placeholder(value):
return match.group(0)
return (
match.group("key")
+ match.group("sep")
+ self._alias(_normalise_key(match.group("key")), value)
)
text = _QUOTED_SECRET_RE.sub(quoted_repl, text)
text = _UNQUOTED_SECRET_RE.sub(unquoted_repl, text)
for category, pattern in _PROVIDER_SECRET_PATTERNS:
text = pattern.sub(lambda m, c=category: self._alias(c, m.group(0)), text)
for pattern in self.custom_patterns:
text = pattern.sub(lambda m: self._alias("CUSTOM", m.group(0)), text)
if self.privacy in ("balanced", "strict"):
text = _POSIX_HOME_RE.sub(
lambda m: self._count_static("HOME_PATH", "$HOME"), text
)
text = _WINDOWS_HOME_RE.sub(
lambda m: self._count_static("HOME_PATH", "$HOME"), text
)
text = _EMAIL_RE.sub(lambda m: self._alias("EMAIL", m.group(0)), text)
if self.privacy == "strict":
text = _URL_HOST_RE.sub(
lambda m: m.group("prefix") + self._alias("HOST", m.group("host")),
text,
)
def ipv4_repl(match: re.Match) -> str:
value = match.group(0)
if any(int(part) > 255 for part in value.split(".")):
return value
return self._alias("IP", value)
text = _IPV4_RE.sub(ipv4_repl, text)
return text
def _count_static(self, category: str, replacement: str) -> str:
self._counts[category] = self._counts.get(category, 0) + 1
return replacement
def redact_value(self, value, *, key: object | None = None):
"""Recursively scrub JSON-compatible data without dropping structure."""
category = _SENSITIVE_KEYS.get(_normalise_key(key)) if key is not None else None
if category and value not in (None, "", [], {}):
if self._is_placeholder(value):
return value
if isinstance(value, str) and category == "AUTHORIZATION":
return self._auth_value(value, category)
return self._alias(category, value)
if isinstance(value, dict):
return {
self._redact_text(k) if isinstance(k, str) else k: self.redact_value(v, key=k)
for k, v in value.items()
}
if isinstance(value, list):
return [self.redact_value(item) for item in value]
if isinstance(value, str):
return self._redact_text(value)
return value
def redact_jsonl(self, text: str) -> str:
"""Scrub JSONL values by structure; fall back to text for malformed lines."""
out = []
for line in text.splitlines(keepends=True):
body = line.rstrip("\r\n")
newline = line[len(body):]
if not body.strip():
out.append(line)
continue
try:
value = json.loads(body)
except json.JSONDecodeError:
out.append(self._redact_text(body) + newline)
continue
redacted = self.redact_value(value)
out.append(json.dumps(redacted, ensure_ascii=False, separators=(",", ":")) + newline)
return "".join(out)
def summary(self) -> dict[str, int]:
return dict(sorted(self._counts.items()))
def redact(
text: str,
*,
privacy: str = "balanced",
custom_patterns: list[re.Pattern] | None = None,
) -> str:
"""Compatibility wrapper for callers that only need the scrubbed text."""
return TraceRedactor(privacy, custom_patterns).redact_jsonl(text)
def _safe_session_id(value: str) -> str:
"""Require the client-side bucket component to be simple and non-ambiguous."""
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]{0,191}", value):
raise SystemExit(
"session_id must be 1-192 characters using only letters, digits, "
"dot, underscore, and hyphen; pass a safe --session-id override"
)
return value
def _native_log_name(log_path: Path) -> str:
"""Avoid copying a potentially identifying local filename into shared storage."""
suffix = log_path.suffix.lower()
if suffix not in (".jsonl", ".json", ".log", ".txt"):
suffix = ".log"
return f"trace{suffix}"
def _prune(value):
"""Drop None values (null == unknown == absent) so manifests stay clean."""
if isinstance(value, dict):
return {k: _prune(v) for k, v in value.items() if v is not None}
return value
def _serialise(fm: dict, body: str) -> str:
# Emit the frontmatter as JSON — valid YAML, so the backend's yaml.safe_load
# parses it identically — which keeps this client dependency-free (no PyYAML).
out = "---\n" + json.dumps(fm, indent=2, ensure_ascii=False) + "\n---\n"
if body.strip():
out += "\n" + body.strip("\n") + "\n"
return out
def build_manifest(
fields: dict,
*,
session_id: str,
result_ref: str | None,
native_log_file: str | None = None,
redaction: dict | None = None,
) -> str:
fm: dict = {
"schema_version": 1,
"adapter_version": ADAPTER_VERSION,
"harness": fields.get("harness"),
"session_id": session_id,
}
for k in ("model", "started_at", "ended_at"):
if fields.get(k) is not None:
fm[k] = fields[k]
if result_ref:
fm["result_ref"] = result_ref
if native_log_file:
fm["native_log_file"] = native_log_file
if redaction:
fm["redaction"] = redaction
for k in ("usage", "activity", "extensions"):
pruned = _prune(fields.get(k) or {})
if pruned:
fm[k] = pruned
return _serialise(fm, "")
def _known_harness_complete(harness: str, fields: dict) -> bool:
usage = fields.get("usage") or {}
activity = fields.get("activity") or {}
total = usage.get("total_tokens")
tools = activity.get("tool_calls")
return (
isinstance(total, int)
and not isinstance(total, bool)
and isinstance(tools, int)
and not isinstance(tools, bool)
)
def _confirm_or_exit(
*,
share: str,
log_path: Path,
uncertain: bool,
yes: bool,
raw: bool,
privacy: str,
) -> None:
reasons = []
if uncertain:
reasons.append("Could not pin the exact invoking session — selection fell back to "
"the newest log for this directory; confirm it is the right one.")
if share == "full":
if raw:
reasons.append(
"Full --raw sharing uploads the UNREDACTED native session log "
"to your org-readable scratch bucket."
)
else:
reasons.append(
f"Full sharing uploads the {privacy}-redacted native session log "
"to your org-readable scratch bucket."
)
if not reasons or yes:
return
print("\nconfirmation required:")
for reason in reasons:
print(f"- {reason}")
print(f"- transcript: {log_path}")
if not sys.stdin.isatty():
sys.exit("refusing to continue without --yes in a non-interactive shell")
answer = input("Continue? Type 'yes' to upload: ").strip().lower()
if answer != "yes":
sys.exit("aborted")
def _fetch_v1(backend: str) -> dict | None:
"""GET {backend}/v1 (the self-description: org, collab=slug, central_bucket,
endpoints). Returns the parsed dict, or None if unreachable."""
try:
with urllib.request.urlopen(f"{backend.rstrip('/')}/v1", timeout=10) as resp:
data = json.loads(resp.read().decode())
return data if isinstance(data, dict) else None
except Exception:
return None
def _v1_has_traces(v1: dict) -> bool:
"""Does this backend expose POST /v1/traces? Older deploys predate it."""
return any(
isinstance(ep, dict) and ep.get("path") == "/v1/traces" and ep.get("method") == "POST"
for ep in (v1.get("endpoints") or [])
)
def _hf_cp(local: str, dest_uri: str) -> None:
"""Upload one file to a bucket via the `hf` CLI (uses your `hf auth login`
credentials — no Python deps). Progress bars off for clean, scriptable logs."""
r = subprocess.run(
["hf", "buckets", "cp", "--quiet", local, dest_uri],
env={**os.environ, "HF_HUB_DISABLE_PROGRESS_BARS": "1"},
capture_output=True, text=True,
)
if r.returncode != 0:
sys.exit(f"`hf buckets cp` failed [{r.returncode}]:\n{(r.stderr or r.stdout).strip()}")
def main() -> int:
ap = argparse.ArgumentParser(description="Share a session's stats / trace with the collaboration.")
ap.add_argument("--harness", choices=[*KNOWN_HARNESSES, "auto"], default="auto",
help="default: auto-detect from this cwd (Claude Code, then Codex)")
ap.add_argument("--transcript", help="explicit native session log path (else: detected)")
ap.add_argument("--session-id", help="override the manifest/dest session id")
ap.add_argument("--full", action="store_true", help="also upload the redacted native session log")
ap.add_argument("--stats-only", action="store_true", help="deprecated no-op; stats-only is the default")
ap.add_argument("--raw", action="store_true",
help="UNSAFE: with --full, skip transcript/manifest redaction")
ap.add_argument("--privacy", choices=PRIVACY_LEVELS, default="balanced",
help="redaction level (default: balanced; applies to --full and manifest strings)")
ap.add_argument("--redact-pattern-file",
help="optional file containing one additional redaction regex per line")
ap.add_argument("--result-ref", help="filename in results/ this session produced")
ap.add_argument("--agent-id", default=os.environ.get("AGENT_ID"), help="your registered agent_id")
ap.add_argument("--org", default=os.environ.get("ORG"), help="challenge org")
ap.add_argument("--slug", default=os.environ.get("COLLAB_SLUG"), help="challenge slug")
ap.add_argument("--backend", default=os.environ.get("COLLAB_BACKEND"),
help="the backend Space base URL, e.g. https://<org>-<slug>-bucket-sync.hf.space")
ap.add_argument("--upload-only", action="store_true",
help="write the bundle to your scratch bucket and skip POST /v1/traces")
ap.add_argument("--yes", action="store_true", help="confirm --full/global Codex fallback in non-interactive use")
ap.add_argument("--dry-run", action="store_true", help="print the plan + manifest; touch nothing")
args = ap.parse_args()
if args.full and args.stats_only:
sys.exit("choose either --full or --stats-only (stats-only is the default)")
if args.raw and not args.full:
sys.exit("--raw only applies with --full")
if args.raw and args.redact_pattern_file:
sys.exit("--redact-pattern-file cannot be combined with --raw")
# Auto-discover org/slug from the backend's GET /v1 when not provided, and
# learn whether this backend even has the trace routes (older deploys don't).
v1 = _fetch_v1(args.backend) if args.backend else None
if v1:
args.org = args.org or v1.get("org")
args.slug = args.slug or v1.get("collab")
promote = not args.upload_only
if promote and v1 is not None and not _v1_has_traces(v1):
print("note: this backend has no POST /v1/traces yet — saving to your "
"scratch bucket only (organizers can deploy the trace routes).")
promote = False
# 1) locate + parse the native session log
uncertain = False
if args.transcript:
log_path = Path(args.transcript).expanduser()
if not log_path.is_file():
sys.exit(f"no such transcript: {log_path}")
harness = args.harness if args.harness != "auto" else _infer_harness(log_path)
if harness is None:
sys.exit("could not infer harness from --transcript; pass --harness")
else:
harness, log_path, uncertain = detect(os.getcwd(), args.harness)
fields = build_fields(harness, log_path)
if harness in KNOWN_HARNESSES and not _known_harness_complete(harness, fields):
sys.exit(
f"{harness} adapter did not produce both usage.total_tokens and "
"activity.tool_calls; adapter likely needs updating"
)
session_id = _safe_session_id(
args.session_id or str(fields.get("session_id") or log_path.stem)
)
share = "full" if args.full else "stats"
native_log_file = _native_log_name(log_path) if share == "full" else None
log_text = None
if args.raw:
safe_fields = fields
safe_result_ref = args.result_ref
redaction_meta = {
"version": REDACTOR_VERSION,
"privacy": "raw",
"counts": {},
}
if share == "full":
log_text = log_path.read_text(encoding="utf-8", errors="replace")
else:
patterns = _custom_patterns(args.redact_pattern_file)
identifier_probe = TraceRedactor(args.privacy, patterns)
if identifier_probe.redact_value(session_id) != session_id:
sys.exit(
"session_id matches a sensitive/custom redaction pattern; "
"pass a non-sensitive --session-id override"
)
redactor = TraceRedactor(args.privacy, patterns)
# session_id and native_log_file are structural identifiers and must match
# their bucket path. Scrub all descriptive manifest fields around them.
safe_fields = redactor.redact_value(fields)
safe_result_ref = (
redactor.redact_value(args.result_ref) if args.result_ref else None
)
if share == "full":
log_text = redactor.redact_jsonl(
log_path.read_text(encoding="utf-8", errors="replace")
)
redaction_meta = {
"version": REDACTOR_VERSION,
"privacy": args.privacy,
"counts": redactor.summary(),
}
manifest = build_manifest(
safe_fields,
session_id=session_id,
result_ref=safe_result_ref,
native_log_file=native_log_file,
redaction=redaction_meta,
)
# 2) the plan
usage = fields.get("usage") or {}
activity = fields.get("activity") or {}
print(f"harness : {harness} (adapter v{ADAPTER_VERSION})")
print(f"log : {log_path}")
print(f"session : {session_id}")
print(f"share : {share}" + (" [redaction OFF]" if args.raw else ""))
if not args.raw:
print(f"privacy : {args.privacy} (redactor v{REDACTOR_VERSION})")
counts = redaction_meta["counts"]
summary = ", ".join(f"{kind}={count}" for kind, count in counts.items())
print(f"redactions : {summary or 'none'}")
if uncertain:
print("selection : newest log for this cwd (exact session not pinned — verify it's yours)")
print(f"tokens : {usage.get('total_tokens', 'unknown')}")
print(f"tool_calls : {activity.get('tool_calls', 'unknown')}")
if harness not in KNOWN_HARNESSES:
print(
f"note : '{harness}' has no adapter — shipping a minimal "
"manifest (partial)"
+ (" + native log" if share == "full" else "")
)
dest = source = None
if args.agent_id and args.org and args.slug:
bucket = f"{args.org}/{args.slug}-{args.agent_id}"
dest = f"traces/{session_id}"
source = f"hf://buckets/{bucket}/{dest}"
print(f"bucket : {source}")
print("\n--- manifest.md ---")
print(manifest)
if args.dry_run:
print("(dry run — nothing written or uploaded)")
return 0
_confirm_or_exit(
share=share,
log_path=log_path,
uncertain=uncertain,
yes=args.yes,
raw=args.raw,
privacy=args.privacy,
)
# 3) preflight
required = [
(args.agent_id, "--agent-id/AGENT_ID"),
(args.org, "--org/ORG"),
(args.slug, "--slug/COLLAB_SLUG"),
]
if promote:
required.append((args.backend, "--backend/COLLAB_BACKEND"))
for req, name in required:
if not req:
sys.exit(f"missing {name}")
bucket = f"{args.org}/{args.slug}-{args.agent_id}"
dest = f"traces/{session_id}"
source = f"hf://buckets/{bucket}/{dest}"
# 4) write the bundle into YOUR bucket via the `hf` CLI (uses your hf auth;
# no Python deps). Content is redacted client-side before it ever leaves.
if not shutil.which("hf"):
sys.exit(
"the `hf` CLI is required (you already use it for `hf auth login` and "
"bucket access). Install it with: pip install huggingface_hub"
)
with tempfile.TemporaryDirectory() as td:
man = Path(td) / "manifest.md"
man.write_text(manifest, encoding="utf-8")
_hf_cp(str(man), f"hf://buckets/{bucket}/{dest}/manifest.md")
n_files = 1
if share == "full":
assert log_text is not None
assert native_log_file is not None
logf = Path(td) / native_log_file
logf.write_text(log_text, encoding="utf-8")
_hf_cp(str(logf), f"hf://buckets/{bucket}/{dest}/{native_log_file}")
n_files = 2
print(f"\nwrote {n_files} file(s) to {source}")
if not promote:
why = "" if args.upload_only else " (this backend has no trace routes yet)"
print(f"saved to your scratch bucket; skipped POST /v1/traces{why}")
print(f"verify : hf buckets list {source}/ -R")
return 0
# 5) promote via the backend (identity = bucket; no token on the call)
body = json.dumps({"source": source, "share": share}).encode("utf-8")
req = urllib.request.Request(
f"{args.backend.rstrip('/')}/v1/traces", data=body,
headers={"Content-Type": "application/json"}, method="POST",
)
try:
with urllib.request.urlopen(req) as resp:
promoted_text = resp.read().decode()
except (urllib.error.HTTPError, urllib.error.URLError) as e:
code = getattr(e, "code", "—")
detail = (e.read().decode(errors="replace") if isinstance(e, urllib.error.HTTPError)
else str(getattr(e, "reason", e)))
# The bundle is already in the bucket — a promote failure is partial,
# not total. Make that legible and exit 0 (the upload succeeded).
print(f"\n✓ bundle uploaded to {source}")
print(f"⚠ backend promotion failed [{code}]: {detail.strip()[:200]}")
print(" your trace is safe in your scratch bucket. Re-run with "
"--upload-only to skip promotion, or tell the organizers if "
"POST /v1/traces should be available on this collab.")
return 0
print(f"promoted: {promoted_text}")
try:
promoted = json.loads(promoted_text)
except json.JSONDecodeError:
return 0
detail_url = (
f"{args.backend.rstrip('/')}/v1/traces/"
f"{urllib.parse.quote(promoted['agent'])}/"
f"{urllib.parse.quote(promoted['session_id'])}"
)
print(f"trace API : {detail_url}")
print(f"trace path : {promoted['path']}")
central_bucket = (v1 or {}).get("central_bucket")
if central_bucket:
print(f"central : hf://buckets/{central_bucket}/{promoted['path']}")
if share == "full":
assert native_log_file is not None
log_rel = f"{promoted['path']}{native_log_file}"
viewer = f"https://huggingface.co/buckets/{central_bucket}/{log_rel}"
print(f"view trace : {viewer}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

Xet Storage Details

Size:
45.3 kB
·
Xet hash:
0ae7a11792391f1062f20a97f55c03707d97e45f87269d0865824afbfd560bbc

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.