@sema-agent/core
Version:
Stateless, task-oriented AI agent core
93 lines (92 loc) • 3.3 kB
JavaScript
import { engineCardTypes } from "../tool-registry.js";
import { readMember } from "../thrown-value.js";
const MAX_TOOL_END_OUTPUT_CHARS = 16384;
const safeJsonLength = (v) => {
try {
return JSON.stringify(v)?.length;
}
catch {
return undefined;
}
};
const trunc = (s) => ({
text: s.slice(0, MAX_TOOL_END_OUTPUT_CHARS) + `…[+${s.length - MAX_TOOL_END_OUTPUT_CHARS} chars truncated]`,
totalChars: s.length,
});
export const toolOutputFrom = (result) => {
const content = result !== null && typeof result === "object" ? result.content : result;
if (content === undefined)
return undefined;
if (typeof content === "string") {
if (content.length <= MAX_TOOL_END_OUTPUT_CHARS)
return { output: content, truncated: false };
const t = trunc(content);
return { output: t.text, truncated: true, totalChars: t.totalChars };
}
let serialized;
try {
serialized = JSON.stringify(content) ?? "";
}
catch {
return { output: "[unserializable tool output]", truncated: false };
}
if (serialized.length <= MAX_TOOL_END_OUTPUT_CHARS)
return { output: content, truncated: false };
let anyNonText = false;
let trueChars = 0;
const raw = Array.isArray(content)
? content
.map((b) => {
const block = b;
if (block?.type === "text" && typeof block.text === "string") {
trueChars += block.text.length;
return block.text;
}
anyNonText = true;
trueChars += typeof block?.data === "string" ? block.data.length : (safeJsonLength(block) ?? 0);
return `[${typeof block?.type === "string" ? block.type : "block"}${typeof block?.mimeType === "string" ? `: ${block.mimeType}` : ""}]`;
})
.join("\n")
: serialized;
const totalChars = Array.isArray(content) ? trueChars : serialized.length;
if (raw.length > MAX_TOOL_END_OUTPUT_CHARS) {
const t = trunc(raw);
return { output: t.text, truncated: true, totalChars };
}
if (!anyNonText) {
return { output: raw, truncated: false };
}
return { output: raw, truncated: true, totalChars };
};
const CC_DETAIL_TYPES = engineCardTypes();
export function createStructuredProjector(opts = {}) {
const allowed = new Set([...CC_DETAIL_TYPES, ...(opts.declaredCards ?? [])]);
const announced = new Set();
return (result) => {
const details = readMember(result, "details");
if (details === null || typeof details !== "object")
return undefined;
const t = readMember(details, "type");
if (typeof t !== "string")
return undefined;
if (!allowed.has(t)) {
if (opts.onUndeclared !== undefined && !announced.has(t)) {
announced.add(t);
opts.onUndeclared(t);
}
return undefined;
}
return boundedCard(details);
};
}
function boundedCard(details) {
try {
const serialized = JSON.stringify(details) ?? "";
if (serialized.length > MAX_TOOL_END_OUTPUT_CHARS * 4)
return undefined;
}
catch {
return undefined;
}
return details;
}