Standalone per-card browser model/security/view modules for HUX-01..10 plus node+pytest suites that read the hux.v1 contract schemas directly. Reconciled drift found on integration: the activity model now accepts all 32 hux.event.v1 kinds (delegation.*, memory.suppressed, memory.retrieval_removed, budget.exhausted, side_effect.*), the autonomy model carries the external_side_effect capability, and the foundation boundary test now asserts the shipped static HUX surface exists on disk and that images never bake activated HUX_FLAGS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
/** Display-only redaction. Raw event payloads must never cross this boundary. */
|
|
|
|
const CONTROL_CHARACTERS = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g;
|
|
const PEM_BLOCK = /-----BEGIN [^-]+-----[\s\S]*?-----END [^-]+-----/gi;
|
|
const AUTH_VALUE =
|
|
/\b(password|passwd|token|api[-_ ]?key|secret|authorization|cookie)\b\s*[:=]\s*[^\s,;]+/gi;
|
|
const BEARER = /\bbearer\s+[A-Za-z0-9._~+/-]+=*/gi;
|
|
const JWT = /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g;
|
|
const CREDENTIAL_BLOB =
|
|
/\b(?=[A-Za-z0-9_+/#.-]{40,}\b)(?=[A-Za-z0-9_+/#.-]*[A-Za-z])(?=[A-Za-z0-9_+/#.-]*\d)[A-Za-z0-9_+/#.-]+\b/g;
|
|
const URI_USERINFO = /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi;
|
|
|
|
/** Return bounded plain text or a safe fallback; objects are never stringified. */
|
|
export function safeText(
|
|
value: unknown,
|
|
fallback: string,
|
|
maxLength = 280,
|
|
): string {
|
|
if (typeof value !== "string") return fallback;
|
|
const limit = Math.max(1, maxLength);
|
|
const redacted = value
|
|
.replace(PEM_BLOCK, "[redacted credential]")
|
|
.replace(AUTH_VALUE, "$1=[redacted]")
|
|
.replace(BEARER, "Bearer [redacted]")
|
|
.replace(JWT, "[redacted credential]")
|
|
.replace(URI_USERINFO, "$1[redacted]@")
|
|
.replace(CREDENTIAL_BLOB, "[redacted credential]")
|
|
.replace(CONTROL_CHARACTERS, " ")
|
|
.replace(/\s+/g, " ")
|
|
.trim();
|
|
if (!redacted) return fallback;
|
|
return redacted.length <= limit
|
|
? redacted
|
|
: limit === 1
|
|
? "…"
|
|
: `${redacted.slice(0, limit - 1).trimEnd()}…`;
|
|
}
|
|
|
|
const EVIDENCE_LABELS: Readonly<Record<string, string>> = {
|
|
message: "Message",
|
|
tool_call: "Tool call",
|
|
tool_result: "Tool result",
|
|
artifact_version: "Artifact version",
|
|
source: "Source",
|
|
passage: "Supporting passage",
|
|
memory: "Memory record",
|
|
approval: "Approval",
|
|
run: "Run",
|
|
url: "Website",
|
|
file: "File",
|
|
build: "Build",
|
|
flux: "Flux reconciliation",
|
|
pod: "Workload",
|
|
};
|
|
|
|
/** Evidence labels are deliberately detached from IDs, URIs, and payloads. */
|
|
export function safeEvidenceLabel(
|
|
kind: unknown,
|
|
): { kind: string; label: string } | null {
|
|
if (typeof kind !== "string" || !(kind in EVIDENCE_LABELS)) return null;
|
|
return { kind, label: EVIDENCE_LABELS[kind] };
|
|
}
|
|
|
|
export function isRfc3339Utc(value: unknown): value is string {
|
|
return (
|
|
typeof value === "string" &&
|
|
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/.test(value) &&
|
|
!Number.isNaN(Date.parse(value))
|
|
);
|
|
}
|
|
|
|
export function isOpaqueId(value: unknown, prefix?: string): value is string {
|
|
if (typeof value !== "string") return false;
|
|
if (!/^[a-z]{2,6}_[A-Za-z0-9._-]{4,80}$/.test(value)) return false;
|
|
return !prefix || value.startsWith(`${prefix}_`);
|
|
}
|