@sema-agent/core
Version:
Stateless, task-oriented AI agent core
501 lines (500 loc) • 32.7 kB
JavaScript
import { arbitrateByMode, classifyAsk } from "../../core/ask-origin.js";
import { deliverEngineNotice } from "../../core/engine-notice.js";
import { createHash } from "node:crypto";
import { Type } from "typebox";
import { ARTIFACT_ACTION_CONCURRENCY_SAFE, ARTIFACT_COMMIT_THROW_OUTCOME, ARTIFACT_FAILURE_RESULT_CODE, ARTIFACT_HOST_ERROR_CODE, ARTIFACT_LIST_SCOPES, ARTIFACT_PLANE_CONTENT_ORIGIN, ARTIFACT_PUBLISH_CONTEXTS, } from "../../core/artifact-host.js";
import { ARTIFACT_TOOL_NAME, toolFace } from "../../core/tool-catalog-entries.js";
import { resolveAsk } from "../../core/tool-policy.js";
import { errorResult } from "../../core/tools.js";
import { inlineUntrusted } from "../../core/untrusted-text.js";
import { actionDescription, artifactDescription, PARAM_TEXT, urlDescription } from "./artifact-text.js";
import { ARTIFACT_REJECT_BREAKER_THRESHOLD, artifactRejectBannerFor, artifactRejectKeyOf, createArtifactRejectBreaker } from "./reject-breaker.js";
import { thrownText } from "../../core/thrown-value.js";
export const MAX_FILES_PER_PUBLISH = 255;
const LIST_LIMIT_DEFAULT = 25;
const LIST_LIMIT_MAX = 100;
const BINARY_NOTE_MEDIA = new Set(["application/json", "application/javascript", "text/javascript", "image/svg+xml", "application/xml"]);
const MEDIA_BY_EXT = new Map(Object.entries({
html: "text/html", htm: "text/html", css: "text/css", js: "text/javascript", mjs: "text/javascript", json: "application/json",
md: "text/markdown", txt: "text/plain", csv: "text/csv", xml: "application/xml", svg: "image/svg+xml", png: "image/png",
jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", ico: "image/x-icon", woff: "font/woff",
woff2: "font/woff2", ttf: "font/ttf", wasm: "application/wasm", pdf: "application/pdf", mp3: "audio/mpeg", mp4: "video/mp4",
}));
export function mediaTypeOf(path) {
const dot = path.lastIndexOf(".");
return (dot >= 0 ? MEDIA_BY_EXT.get(path.slice(dot + 1).toLowerCase()) : undefined) ?? "application/octet-stream";
}
export function isTextMedia(mediaType) {
if (mediaType === undefined)
return false;
const essence = mediaType.split(";")[0].trim().toLowerCase();
return essence.startsWith("text/") || BINARY_NOTE_MEDIA.has(essence);
}
export function artifactAttemptId(runId, toolCallId, occurrence = 0) {
const h = createHash("sha256").update(runId).update("|").update(toolCallId).update("|").update(String(occurrence)).digest("hex");
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
}
const basenameOf = (p) => {
const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
return i >= 0 ? p.slice(i + 1) : p;
};
export function normalizePublishedPath(raw) {
if (raw.length === 0)
return { ok: false, reason: "is empty" };
if (raw.includes("\0"))
return { ok: false, reason: "contains a NUL byte" };
if (raw.includes("\\"))
return { ok: false, reason: "contains a backslash (use forward slashes)" };
if (raw.startsWith("/"))
return { ok: false, reason: "is root-relative (no leading slash)" };
const path = raw.normalize("NFC");
for (const seg of path.split("/"))
if (seg === "" || seg === "." || seg === "..")
return { ok: false, reason: "contains an empty, `.` or `..` segment" };
return { ok: true, path };
}
function renderSnapshotMeta(s) {
const meta = ["title", "description", "favicon", "label"].filter((k) => s[k] !== undefined).map((k) => `${k}: ${inlineUntrusted(s[k], 200)}`);
return meta.length === 0 ? "" : `${meta.join("\n")}\n`;
}
function renderFile(f) {
const head = `--- ${inlineUntrusted(f.path, 200)} (${inlineUntrusted(f.mediaType ?? "application/octet-stream", 80)}, ${String(f.content.byteLength).slice(0, 20)} bytes) ---`;
return isTextMedia(f.mediaType) ? `${head}\n${Buffer.from(f.content).toString("utf8")}` : `${head}\n[binary content, not shown]`;
}
const isRecord = (x) => x !== null && typeof x === "object";
const everyItem = (x, pred) => Array.isArray(x) && x.every(pred);
const optString = (v) => v === undefined || typeof v === "string";
const isRef = (x) => isRecord(x) && typeof x["url"] === "string" && typeof x["slug"] === "string" && typeof x["version"] === "string" && optString(x["title"]);
const isFile = (x) => isRecord(x) && typeof x["path"] === "string" && x["content"] instanceof Uint8Array && optString(x["mediaType"]);
const isSnapshot = (x) => isRecord(x) && everyItem(x["files"], isFile) && optString(x["title"]) && optString(x["description"]) && optString(x["favicon"]) && optString(x["label"]);
const failureWord = (x, allowed) => isRecord(x) && typeof x["code"] === "string" && allowed.includes(x["code"]);
const OUTCOME_SHAPE = {
publish: (o) => isRecord(o) &&
(o["ok"] === true
? isRef(o["ref"])
: o["ok"] === false &&
(o["code"] === "conflict"
? typeof o["currentVersion"] === "string" && (o["current"] === "reread" || isSnapshot(o["current"]))
: failureWord(o, ["url_foreign", "forbidden", "payload_too_large", "not_found", "force_refused", "refused"]) && typeof o["message"] === "string")),
list: (o) => isRecord(o) &&
(o["ok"] === true
? everyItem(o["rows"], (r) => isRef(r) && isRecord(r) && (r["owner"] === "mine" || r["owner"] === "shared") && typeof r["updatedAt"] === "string") && typeof o["truncated"] === "boolean"
: o["ok"] === false && failureWord(o, ["refused"]) && typeof o["message"] === "string"),
read: (o) => isRecord(o) && (o["ok"] === true ? isRef(o["ref"]) && typeof o["content"] === "string" && typeof o["isolatedSummary"] === "boolean" : o["ok"] === false && failureWord(o, ["not_found", "forbidden"]) && typeof o["message"] === "string"),
list_files: (o) => isRecord(o) && (o["ok"] === true ? everyItem(o["entries"], (e) => isRecord(e) && typeof e["path"] === "string" && typeof e["bytes"] === "number" && optString(e["mediaType"])) : o["ok"] === false && failureWord(o, ["not_found", "forbidden"]) && typeof o["message"] === "string"),
read_file: (o) => isRecord(o) && (o["ok"] === true ? isFile(o["file"]) : o["ok"] === false && failureWord(o, ["not_found", "forbidden"]) && typeof o["message"] === "string"),
};
function callHost(action, fn, signal) {
if (signal?.aborted)
return Promise.resolve({ ok: false, kind: "aborted", message: "the call was aborted before the host was reached" });
return new Promise((resolve) => {
const onAbort = () => resolve({ ok: false, kind: "aborted", message: "the call was aborted while the host was working" });
signal?.addEventListener("abort", onAbort, { once: true });
let p;
try {
p = Promise.resolve(fn());
}
catch (err) {
signal?.removeEventListener("abort", onAbort);
resolve({ ok: false, kind: "threw", message: thrownText(err) });
return;
}
p.then((value) => resolve(OUTCOME_SHAPE[action](value) ? { ok: true, value } : { ok: false, kind: "malformed", message: `the ${action} answer was outside the host contract` }), (err) => resolve({ ok: false, kind: "threw", message: thrownText(err) })).finally(() => signal?.removeEventListener("abort", onAbort));
});
}
function buildSchema(caps) {
const has = (a) => caps.actions.includes(a);
const props = {
action: Type.Optional(Type.Unsafe({ type: "string", enum: [...caps.actions], description: actionDescription(caps.actions) })),
file_path: Type.Optional(Type.String({ description: PARAM_TEXT.filePath })),
url: Type.Optional(Type.String({ maxLength: 2048, description: urlDescription(caps.actions) })),
title: Type.Optional(Type.String({ maxLength: 200, description: PARAM_TEXT.title })),
description: Type.Optional(Type.String({ maxLength: 1000, description: PARAM_TEXT.description })),
favicon: Type.Optional(Type.String({ maxLength: 32, description: PARAM_TEXT.favicon })),
label: Type.Optional(Type.String({ maxLength: 60, description: PARAM_TEXT.label })),
baseVersion: Type.Optional(Type.String({ maxLength: 200, description: PARAM_TEXT.baseVersion })),
force: Type.Optional(Type.Boolean({ description: PARAM_TEXT.force })),
publishContext: Type.Optional(Type.Unsafe({ type: "string", enum: [...ARTIFACT_PUBLISH_CONTEXTS], description: PARAM_TEXT.publishContext })),
};
if (caps.multiFile) {
props["files"] = Type.Optional(Type.Array(Type.Object({ path: Type.String(), file_path: Type.String() }), { maxItems: MAX_FILES_PER_PUBLISH, description: PARAM_TEXT.files }));
}
if (has("list")) {
props["scope"] = Type.Optional(Type.Unsafe({ type: "string", enum: [...ARTIFACT_LIST_SCOPES], description: PARAM_TEXT.scope }));
props["limit"] = Type.Optional(Type.Integer({ minimum: 1, maximum: LIST_LIMIT_MAX, description: PARAM_TEXT.limit }));
}
if (has("read_file"))
props["path"] = Type.Optional(Type.String({ maxLength: 1024, description: PARAM_TEXT.path }));
return Type.Object(props);
}
const invalid = (text) => errorResult(`Error (Artifact): ${text}`, { type: "artifact", invalidArguments: true });
const typed = (code, text, extra = {}) => errorResult(`Error (Artifact): ${text}`, { type: "artifact", code: ARTIFACT_FAILURE_RESULT_CODE[code], ...extra });
export function createArtifactTool(opts) {
const { host, capabilities: caps, runId, taskId, sessionId, agentName, readLocalFile, onAsk, permissionMode, onNotice } = opts;
let releaseAnnounced = false;
const versions = new Map();
const bindings = new Map();
const occurrences = new Map();
const breaker = createArtifactRejectBreaker();
const ownUrls = new Set();
const sharedUrls = new Set();
const sharedReadApproved = new Set();
const consentInFlight = new Map();
const activeTargets = new Map();
const hasAction = (a) => caps.actions.includes(a);
const rememberRef = (ref) => {
versions.set(ref.url, ref.version);
};
const rememberOwner = (url, owner) => {
(owner === "mine" ? ownUrls : sharedUrls).add(url);
};
const nextContext = (ctx) => {
const n = occurrences.get(ctx.toolCallId) ?? 0;
occurrences.set(ctx.toolCallId, n + 1);
return {
taskId,
...(sessionId !== undefined ? { sessionId } : {}),
...(agentName !== undefined ? { agentName } : {}),
runId,
attemptId: artifactAttemptId(runId, ctx.toolCallId, n),
};
};
const hostError = (verb, fault, url) => {
const outcome = fault.kind === "threw" ? ARTIFACT_COMMIT_THROW_OUTCOME[caps.commit] : "unknown";
const details = { type: "artifact", code: ARTIFACT_HOST_ERROR_CODE, outcome, plane: caps.plane, commit: caps.commit };
if (outcome === "not_executed") {
return errorResult(`Error (Artifact): the artifact host failed before committing (${inlineUntrusted(fault.message, 200)}). Nothing was published; the call can be repeated.`, details);
}
const verifiable = hasAction("read") && url !== "";
return errorResult(`Error (Artifact): the artifact host did not answer the ${verb.slice(0, 20)} (${inlineUntrusted(fault.message, 200)}). Whether it was applied is not known: it may already have been committed, so do not simply repeat the call.${verifiable ? ` Verify with action: "read" on ${url.slice(0, 200)} first.` : ""}`, details);
};
const notFoundOrForbidden = (code, url, message) => code === "not_found"
? typed(code, `no artifact at ${inlineUntrusted(url, 200)} (${inlineUntrusted(message, 200)}).`, { url })
: typed(code, `${inlineUntrusted(url, 200)} is not accessible to this session (${inlineUntrusted(message, 200)}).`, { url });
const readRefusal = (path, r) => {
switch (r.reason) {
case "refused":
return errorResult(r.text.slice(0, 4000), r.details);
case "too_large":
return typed("payload_too_large", `${inlineUntrusted(path, 200)} is ${String(r.size).slice(0, 20)} bytes; this host accepts at most ${String(r.limit).slice(0, 20)} bytes for it${r.judgedOn === "metadata" ? " (the file was not read)" : " (it grew past its declared size while being read)"}.`, { limit: r.limit, measured: r.size, judgedOn: r.judgedOn });
case "not_a_file":
return invalid(`${inlineUntrusted(path, 200)} is a ${inlineUntrusted(r.kind, 20)}, not a regular file.`);
default: {
const _exhaustive = r;
return _exhaustive;
}
}
};
const manifestOf = (a) => {
const mainNorm = normalizePublishedPath(basenameOf(a.file_path));
if (!mainNorm.ok)
return { ok: false, result: invalid(`the published name of ${inlineUntrusted(a.file_path, 200)} ${mainNorm.reason.slice(0, 80)}.`) };
const entries = [];
if (a.files === undefined || (Array.isArray(a.files) && a.files.length === 0))
return { ok: true, main: mainNorm.path, entries };
if (!caps.multiFile)
return { ok: false, result: invalid("this artifact host publishes single files only; `files` is not accepted.") };
if (!Array.isArray(a.files))
return { ok: false, result: invalid("`files` must be an array of { path, file_path } entries.") };
if (a.files.length > MAX_FILES_PER_PUBLISH)
return { ok: false, result: invalid(`\`files\` carries ${String(a.files.length).slice(0, 20)} entries; at most ${MAX_FILES_PER_PUBLISH} supporting files are accepted per publish.`) };
const seen = new Set([mainNorm.path.toLowerCase()]);
for (const entry of a.files) {
if (!isRecord(entry) || typeof entry["path"] !== "string" || typeof entry["file_path"] !== "string")
return { ok: false, result: invalid("every `files` entry must carry a string `path` and a string `file_path`.") };
const norm = normalizePublishedPath(entry["path"]);
if (!norm.ok)
return { ok: false, result: invalid(`files entry ${inlineUntrusted(entry["path"], 120)} ${norm.reason.slice(0, 80)}.`) };
const fold = norm.path.toLowerCase();
if (seen.has(fold))
return { ok: false, result: invalid(`files entry ${inlineUntrusted(norm.path, 120)} collides with another published path.`) };
seen.add(fold);
entries.push({ path: norm.path, file_path: entry["file_path"] });
}
return { ok: true, main: mainNorm.path, entries };
};
async function publish(a, ctx) {
if (typeof a.file_path !== "string")
return invalid("`file_path` is required to publish.");
const manifest = manifestOf(a);
if (!manifest.ok)
return manifest.result;
const files = [];
const budget = Math.min(caps.maxFileBytes, caps.maxTotalBytes);
const first = await readLocalFile(a.file_path, budget, ctx.signal);
if (!first.ok)
return readRefusal(a.file_path, first);
files.push({ path: manifest.main, content: first.bytes, mediaType: mediaTypeOf(manifest.main) });
let total = first.bytes.byteLength;
const url = a.url ?? bindings.get(first.key);
if (url === undefined && (a.force === true || a.baseVersion !== undefined)) {
return invalid("`force` and `baseVersion` only apply to an update — pass `url`, or republish the same file path this session already published.");
}
for (const entry of manifest.entries) {
const left = caps.maxTotalBytes - total;
if (left <= 0)
return typed("payload_too_large", `the files already total ${String(total).slice(0, 20)} bytes; this host accepts at most ${String(caps.maxTotalBytes).slice(0, 20)} bytes per version.`, { limit: caps.maxTotalBytes, measured: total });
const r = await readLocalFile(entry.file_path, Math.min(caps.maxFileBytes, left), ctx.signal);
if (!r.ok)
return readRefusal(entry.file_path, r);
total += r.bytes.byteLength;
files.push({ path: entry.path, content: r.bytes, mediaType: mediaTypeOf(entry.path) });
}
const baseVersion = a.baseVersion ?? (url !== undefined ? versions.get(url) : undefined);
const req = {
...(url !== undefined ? { url } : {}),
files,
...(a.title !== undefined ? { title: a.title } : {}),
...(a.description !== undefined ? { description: a.description } : {}),
...(a.favicon !== undefined ? { favicon: a.favicon } : {}),
...(a.label !== undefined ? { label: a.label } : {}),
...(baseVersion !== undefined ? { baseVersion } : {}),
...(a.force === true ? { force: true } : {}),
publishContext: a.publishContext ?? "plain",
};
const call = await callHost("publish", () => host.publish(req, nextContext(ctx)), ctx.signal);
if (!call.ok)
return hostError("publish", call, inlineUntrusted(url ?? "", 200));
const out = call.value;
if (out.ok) {
rememberRef(out.ref);
rememberOwner(out.ref.url, "mine");
bindings.set(first.key, out.ref.url);
const title = out.ref.title !== undefined ? ` — ${inlineUntrusted(out.ref.title, 120)}` : "";
return {
content: `Published ${inlineUntrusted(out.ref.url, 300)} (version ${inlineUntrusted(out.ref.version, 60)})${title}.`,
details: { type: "artifact", action: "publish", plane: caps.plane, ref: out.ref, update: url !== undefined },
};
}
if (out.code === "conflict") {
let snapshot = out.current;
if (snapshot !== "reread") {
const withheld = url === undefined || (await filesOwnership(url, ctx)) !== undefined;
if (withheld || (url !== undefined && sharedUrls.has(url) && !sharedReadApproved.has(url)))
snapshot = "reread";
}
if (snapshot !== "reread" && url !== undefined)
versions.set(url, out.currentVersion);
const handedBack = snapshot === "reread"
? hasAction("read")
? `The current content was not handed back; read it with action: "read" on ${inlineUntrusted(url ?? "", 200)}.`
: "The current content was not handed back by the host."
: `Current content follows.\n${renderSnapshotMeta(snapshot)}${snapshot.files.map(renderFile).join("\n")}`;
return errorResult(`Error (Artifact): publish conflict — ${inlineUntrusted(url ?? "", 200)} is at version ${inlineUntrusted(out.currentVersion, 60)}, this publish was based on ${inlineUntrusted(baseVersion ?? "", 60)}. Merge your changes onto the current content and publish again. ${handedBack}`, { type: "artifact", code: ARTIFACT_FAILURE_RESULT_CODE.conflict, url, currentVersion: out.currentVersion, current: snapshot === "reread" ? "reread" : { ...snapshot, files: snapshot.files.map((f) => ({ path: f.path, bytes: f.content.byteLength, mediaType: f.mediaType })) } });
}
switch (out.code) {
case "url_foreign":
return typed(out.code, `${inlineUntrusted(url ?? "", 200)} is not an artifact this host (or this run) published; nothing was published (${inlineUntrusted(out.message, 200)}).`, { url });
case "forbidden":
return typed(out.code, `${inlineUntrusted(url ?? "", 200)} is not this session's to update; nothing was published (${inlineUntrusted(out.message, 200)}).`, { url });
case "payload_too_large":
return typed(out.code, `the host refused the payload as too large; nothing was published (${inlineUntrusted(out.message, 200)}).`);
case "not_found":
return typed(out.code, `no artifact at ${inlineUntrusted(url ?? "", 200)}; nothing was published (${inlineUntrusted(out.message, 200)}).`, { url });
case "force_refused":
return typed(out.code, `the host refused to overwrite with force; nothing was published (${inlineUntrusted(out.message, 200)}). Merge onto the current version instead.`, { url });
case "refused":
return typed(out.code, `the host refused the publish; nothing was published (${inlineUntrusted(out.message, 200)}).`);
default: {
const _exhaustive = out;
return _exhaustive;
}
}
}
async function list(a, ctx) {
const scope = a.scope ?? "mine";
const limit = a.limit ?? LIST_LIMIT_DEFAULT;
const call = await callHost("list", () => host.list({ scope, limit }, nextContext(ctx)), ctx.signal);
if (!call.ok)
return hostError("list", call, "");
const out = call.value;
if (!out.ok)
return typed("refused", `the host refused the listing (${inlineUntrusted(out.message, 200)}).`);
for (const r of out.rows)
rememberOwner(r.url, r.owner);
const lines = out.rows.map((r) => `${inlineUntrusted(r.url, 300)} v${inlineUntrusted(r.version, 40)} ${inlineUntrusted(r.updatedAt, 40)} (${r.owner})${r.title !== undefined ? ` ${inlineUntrusted(r.title, 120)}` : ""}`);
const body = lines.length === 0 ? "nothing listed" : lines.join("\n");
return { content: `${body}${out.truncated ? "\n(more artifacts exist; raise `limit` to see them)" : ""}`, details: { type: "artifact", action: "list", plane: caps.plane, scope, rows: out.rows, truncated: out.truncated } };
}
const sharedReadConsent = (url, ref, ctx) => {
if (sharedReadApproved.has(url))
return Promise.resolve({ ok: true });
if (permissionMode !== undefined) {
const verdict = arbitrateByMode(permissionMode(), classifyAsk({ action: "ask", requiresRealApproval: true, guard: "artifact_shared_read" }, { contentQuestion: false, markedUnresolvable: false, org: undefined, tightened: undefined, mandateMarks: {} }));
if (verdict.kind === "released") {
if (!releaseAnnounced && onNotice !== undefined) {
releaseAnnounced = true;
deliverEngineNotice(onNotice, { code: "permission.ask_released_by_mode", message: `the ${verdict.mode} permission mode released a shared-artifact read (${inlineUntrusted(url, 300)}) without asking anyone; further shared reads of this run under the mode are released the same way`, detail: { mode: verdict.mode, askClass: verdict.askClass, count: 1, firstToolCallId: ctx.toolCallId, ...(sessionId !== undefined ? { sessionId } : {}), runId } });
}
return Promise.resolve({ ok: true });
}
if (verdict.kind === "denied_by_mode")
return Promise.resolve({ ok: false, reason: `the ${verdict.mode} permission mode refuses to read another person's artifact without approval` });
}
const pending = consentInFlight.get(url);
if (pending !== undefined)
return pending;
const ask = (async () => {
const title = ref?.title !== undefined ? ` (${inlineUntrusted(ref.title, 120)})` : "";
const version = ref !== undefined ? `, version ${inlineUntrusted(ref.version, 60)}` : "";
const resolved = await resolveAsk({
toolName: ARTIFACT_TOOL_NAME,
toolCallId: ctx.toolCallId,
args: { action: "read", url },
message: `Read a shared artifact: ${inlineUntrusted(url, 300)}${title}${version} — an artifact another person shared with this user; approving covers re-reads of it for the rest of this run.`,
requiresRealApproval: true,
}, onAsk, ctx.signal);
if (resolved.action !== "allow")
return { ok: false, reason: resolved.message ?? "not approved" };
if (resolved.updatedInput !== undefined)
return { ok: false, reason: "the approval edited the request; an edited approval does not authorize the original target" };
sharedReadApproved.add(url);
return { ok: true };
})().finally(() => {
if (!activeTargets.has(url))
consentInFlight.delete(url);
});
consentInFlight.set(url, ask);
return ask;
};
const filesOwnership = async (url, ctx) => {
if (!ownUrls.has(url) && !sharedUrls.has(url)) {
if (!hasAction("read"))
return typed("refused", `${inlineUntrusted(url, 200)} is not an artifact this run published or listed as its own, and this host declares no read to establish who owns it; its files were not delivered.`, { url, sharedRead: "ownership_unknown" });
const probe = await callHost("read", () => host.read({ url }, nextContext(ctx)), ctx.signal);
if (!probe.ok)
return hostError("read", probe, inlineUntrusted(url, 200));
if (!probe.value.ok)
return notFoundOrForbidden(probe.value.code, url, probe.value.message);
if (probe.value.ref.url !== url)
return hostError("read", { kind: "malformed", message: "read.ref.url does not match the requested target" }, inlineUntrusted(url, 200));
rememberOwner(url, probe.value.isolatedSummary ? "shared" : "mine");
}
if (!sharedUrls.has(url))
return undefined;
const consent = await sharedReadConsent(url, undefined, ctx);
return consent.ok ? undefined : typed("refused", `reading the shared artifact ${inlineUntrusted(url, 200)} was not approved (${inlineUntrusted(consent.reason, 200)}); its files were not delivered.`, { url, sharedRead: "not_approved" });
};
async function read(a, ctx) {
if (a.url === undefined)
return invalid("`url` is required to read an artifact.");
const call = await callHost("read", () => host.read({ url: a.url }, nextContext(ctx)), ctx.signal);
if (!call.ok)
return hostError("read", call, inlineUntrusted(a.url, 200));
const out = call.value;
if (!out.ok)
return notFoundOrForbidden(out.code, a.url, out.message);
if (out.ref.url !== a.url)
return hostError("read", { kind: "malformed", message: "read.ref.url does not match the requested target" }, inlineUntrusted(a.url, 200));
rememberOwner(a.url, out.isolatedSummary ? "shared" : "mine");
if (sharedUrls.has(a.url)) {
const consent = await sharedReadConsent(a.url, out.ref, ctx);
if (!consent.ok)
return typed("refused", `reading the shared artifact ${inlineUntrusted(a.url, 200)} was not approved (${inlineUntrusted(consent.reason, 200)}); its content was not delivered.`, { url: a.url, sharedRead: "not_approved" });
}
rememberRef(out.ref);
const head = out.isolatedSummary ? `[isolated summary of a shared artifact — ${inlineUntrusted(out.ref.url, 300)}, version ${inlineUntrusted(out.ref.version, 60)}]\n` : "";
return { content: `${head}${out.content}`, details: { type: "artifact", action: "read", plane: caps.plane, ref: out.ref, isolatedSummary: out.isolatedSummary } };
}
async function listFiles(a, ctx) {
if (a.url === undefined)
return invalid("`url` is required to list an artifact's files.");
const gated = await filesOwnership(a.url, ctx);
if (gated !== undefined)
return gated;
const call = await callHost("list_files", () => host.listFiles({ url: a.url }, nextContext(ctx)), ctx.signal);
if (!call.ok)
return hostError("list_files", call, inlineUntrusted(a.url, 200));
const out = call.value;
if (!out.ok)
return notFoundOrForbidden(out.code, a.url, out.message);
if (sharedUrls.has(a.url)) {
const denied = await filesOwnership(a.url, ctx);
if (denied !== undefined)
return denied;
}
const lines = out.entries.map((e) => `${inlineUntrusted(e.path, 200)} ${String(e.bytes).slice(0, 20)} bytes${e.mediaType !== undefined ? ` ${inlineUntrusted(e.mediaType, 80)}` : ""}`);
return { content: lines.length === 0 ? "no files" : lines.join("\n"), details: { type: "artifact", action: "list_files", plane: caps.plane, url: a.url, entries: out.entries } };
}
async function readFile(a, ctx) {
if (a.url === undefined || a.path === undefined)
return invalid("`url` and `path` are required to read a published file.");
const gated = await filesOwnership(a.url, ctx);
if (gated !== undefined)
return gated;
const call = await callHost("read_file", () => host.readFile({ url: a.url, path: a.path }, nextContext(ctx)), ctx.signal);
if (!call.ok)
return hostError("read_file", call, inlineUntrusted(a.url, 200));
const out = call.value;
if (!out.ok)
return notFoundOrForbidden(out.code, a.url, out.message);
if (sharedUrls.has(a.url)) {
const denied = await filesOwnership(a.url, ctx);
if (denied !== undefined)
return denied;
}
return { content: renderFile(out.file), details: { type: "artifact", action: "read_file", plane: caps.plane, url: a.url, path: out.file.path, bytes: out.file.content.byteLength, mediaType: out.file.mediaType } };
}
return {
name: ARTIFACT_TOOL_NAME,
...toolFace("artifact"),
contentOrigin: ARTIFACT_PLANE_CONTENT_ORIGIN[caps.plane],
description: artifactDescription(caps.plane, caps.actions),
parameters: buildSchema(caps),
prepareArguments: (args) => (isRecord(args) && args["action"] === undefined ? { ...args, action: "publish" } : args),
isConcurrencySafe: (args) => isRecord(args) && typeof args["action"] === "string" && ARTIFACT_ACTION_CONCURRENCY_SAFE[args["action"]] === true,
execute: async (args, ctx) => {
const a = args;
const action = a.action ?? "publish";
const target = typeof a.url === "string" ? a.url : undefined;
if (target !== undefined)
activeTargets.set(target, (activeTargets.get(target) ?? 0) + 1);
let result;
try {
result = await dispatch(action, a, ctx);
}
finally {
if (target !== undefined) {
const left = activeTargets.get(target) - 1;
if (left > 0)
activeTargets.set(target, left);
else {
activeTargets.delete(target);
consentInFlight.delete(target);
}
}
}
if (typeof result === "string" || result.isError !== true) {
breaker.clear();
return result;
}
const details = isRecord(result.details) ? result.details : {};
const reason = typeof details["code"] === "string" ? details["code"] : details["invalidArguments"] === true ? "invalid_arguments" : "error";
const text = typeof result.content === "string" ? result.content : "";
const count = breaker.hit(artifactRejectKeyOf(a), reason, text);
if (count < ARTIFACT_REJECT_BREAKER_THRESHOLD || typeof result.content !== "string")
return result;
return { ...result, content: `${artifactRejectBannerFor(reason)}\n\n${result.content}`, details: { ...details, rejectStreak: count } };
},
};
function dispatch(action, a, ctx) {
if (!hasAction(action))
return invalid(`action ${inlineUntrusted(String(action), 40)} is not available on this artifact host.`);
switch (action) {
case "publish":
return publish(a, ctx);
case "list":
return list(a, ctx);
case "read":
return read(a, ctx);
case "list_files":
return listFiles(a, ctx);
case "read_file":
return readFile(a, ctx);
default: {
const _exhaustive = action;
return _exhaustive;
}
}
}
}