@sema-agent/core
Version:
Stateless, task-oriented AI agent core
113 lines (112 loc) • 4.59 kB
JavaScript
import { delimitUntrusted, neutralizeForFence } from "./untrusted-text.js";
import { scrubSecrets, SECRET_PASSES, runRedactionPasses } from "./arg-summary.js";
import { sliceHeadSafe } from "./surrogate-safe-slice.js";
export { summarizeRedactions } from "./arg-summary.js";
const URI_SCHEME = String.raw `[A-Za-z][A-Za-z0-9+.\-_]{0,63}`;
const CREDENTIALS_IN_AUTHORITY = new RegExp(String.raw `(${URI_SCHEME}:\/\/)((?:[^/\s"'@]*@)+)`, "gi");
const URL_OF_ANY_PROTOCOL = new RegExp(String.raw `${URI_SCHEME}:\/\/[^\s"'\`]+`, "gi");
const BARE_USERNAME_MAX_LEN = 20;
function redactUserinfo(scheme, userinfoWithAt) {
const userinfo = userinfoWithAt.slice(0, -1);
if (userinfo.includes("@"))
return `${scheme}[redacted-credentials]@`;
const colonIdx = userinfo.indexOf(":");
if (colonIdx === -1) {
return userinfo.length >= BARE_USERNAME_MAX_LEN ? `${scheme}[redacted-credentials]@` : `${scheme}${userinfo}@`;
}
return `${scheme}${userinfo.slice(0, colonIdx)}:[redacted-credentials]@`;
}
const SECRET_TIER_PASSES = [
...SECRET_PASSES,
{
kind: "url-credentials",
confidence: "medium",
marker: "[redacted-credentials]",
source: "untrusted-egress",
re: CREDENTIALS_IN_AUTHORITY,
replace: (_m, scheme, userinfo) => redactUserinfo(scheme, userinfo),
},
{ kind: "signed-url-param", confidence: "high", marker: "[redacted]", source: "untrusted-egress", re: /([?&#](?:sig|signature|sas|code)=)[^&\s"'#]+/gi, replace: "$1[redacted]" },
];
export function redactSecrets(s, report) {
return runRedactionPasses(s, SECRET_TIER_PASSES, report);
}
const HOST_LEAK_PASSES = [
...SECRET_TIER_PASSES,
{ kind: "url", confidence: "medium", marker: "[redacted-url]", source: "untrusted-egress", re: URL_OF_ANY_PROTOCOL, replace: "[redacted-url]" },
{ kind: "fs-path", confidence: "low", marker: "[redacted-path]", source: "untrusted-egress", re: /(?:\/[A-Za-z0-9_.-]+){2,}/g, replace: "[redacted-path]" },
];
export function redactHostLeaks(s, report) {
return runRedactionPasses(s, HOST_LEAK_PASSES, report);
}
const REDACTION_MARKER_RE = /\[redacted(?:-[a-z]+)?\]/g;
function countRedactionMarkers(s) {
return (s.match(REDACTION_MARKER_RE) ?? []).length;
}
function safeEllipsisTruncate(s, max) {
if (max < 1)
return "";
const budget = max - 1;
let out = "";
for (const codePoint of s) {
if (out.length + codePoint.length > budget)
break;
out += codePoint;
}
return out + "…";
}
function boundedString(value, max, redact) {
let s;
try {
s = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
}
catch {
s = "[unserializable result]";
}
s = redact(s);
if (s.length <= max)
return s;
const totalMarkers = countRedactionMarkers(s);
const plainSuffix = (kept) => `…[+${s.length - kept.length} chars]`;
const enrichedSuffix = (kept) => {
const lost = totalMarkers - countRedactionMarkers(kept);
return lost > 0 ? `…[+${s.length - kept.length} chars, ${lost} redaction marker(s) among them]` : plainSuffix(kept);
};
const converge = (suffixFor) => {
let keep = max;
let out = "";
for (let i = 0; i < 4; i++) {
const kept = sliceHeadSafe(s, keep);
out = `${kept}${suffixFor(kept)}`;
if (out.length <= max)
return out;
if (keep === 0)
return undefined;
keep = Math.max(0, keep - (out.length - max));
}
return out.length <= max ? out : undefined;
};
const out = (totalMarkers > 0 ? converge(enrichedSuffix) : undefined) ?? converge(plainSuffix);
if (out === undefined) {
return safeEllipsisTruncate(s, max);
}
if (out.length >= s.length)
return s;
return out;
}
export function boundedRedactedSummary(value, max) {
return boundedString(value, max, redactSecrets);
}
export function untrustedEgressForHuman(value, opts) {
const source = boundedString(value, Number.POSITIVE_INFINITY, redactHostLeaks);
let budget = opts.max;
let bounded = boundedString(source, budget, (s) => s);
for (let i = 0; i < 4 && budget > 0; i++) {
const overflow = [...neutralizeForFence(bounded)].length - opts.max;
if (overflow <= 0)
break;
budget = Math.max(0, budget - overflow);
bounded = boundedString(source, budget, (s) => s);
}
return delimitUntrusted(opts.label, bounded, opts.max);
}