UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

74 lines (73 loc) • 4.56 kB
import { deliverEngineNotice } from "../types.js"; import { READ_ROOT_CANDIDATE_DIR_MAX } from "../read-root-candidate.js"; import { DEFAULT_GATE_AWAIT_TIMEOUT_MS } from "../gate-bounded-await.js"; import { thrownText } from "../thrown-value.js"; import { rootAdmittingArtifactStore } from "../../tools/fs/safety.js"; export function readRootGrantOver(seats) { const { env, writeRoots, readRoots, artifactStore } = seats; return async (candidate, signal) => { const c = await env.canonicalPath(candidate.dir); if (signal.aborted) return { ok: false, reason: "unresolvable", cause: "the lookup was cut" }; if (!c.ok) return { ok: false, reason: "unresolvable", cause: `${c.error.code}: ${c.error.message}` }; if (c.value !== candidate.dir) return { ok: false, reason: "resolves_elsewhere", cause: `it resolves to ${c.value}` }; const store = artifactStore.current; if (store !== undefined && rootAdmittingArtifactStore(store, [c.value]) !== undefined) return { ok: false, reason: "admits_artifact_store", cause: "the Artifact tool's ledger and stored files stay outside every root the file tools reach" }; if (!writeRoots.includes(c.value) && !readRoots.includes(c.value)) readRoots.push(c.value); return { ok: true, root: c.value }; }; } export function settleReadRootGrant(screen, grant, at) { if (screen === undefined) return undefined; const ids = { toolName: at.toolName, toolCallId: at.toolCallId, sessionId: at.sessionId, runId: at.runId, ...(at.taskId !== undefined ? { taskId: at.taskId } : {}) }; const reject = (reason, offered, requested, why) => deliverEngineNotice(at.onNotice, { code: "approval.read_root_grant_rejected", message: `the approval for "${at.toolName}" asked to add a read directory, and nothing was added: ${why}. The approval's own verdict is unchanged.`, detail: { reason, ...(offered !== undefined ? { offered } : {}), ...(requested !== undefined ? { requested: requested.slice(0, READ_ROOT_CANDIDATE_DIR_MAX) } : {}), ...ids }, }); if (screen.verdict === "rejected") { const why = { on_refusal: "a directory grant rides an approval, and this answer refused the call", not_a_string: "the grant is not a string", no_candidate: "this approval card offered no directory", mismatch: "the grant is not the directory the card offered (it must be sent back exactly as offered)", }[screen.reason]; reject(screen.reason, screen.offered, screen.requested, why); return undefined; } const { candidate } = screen; if (grant === undefined) { reject("no_read_roots", candidate.dir, candidate.dir, "this run has no read directories to widen"); return undefined; } const cut = AbortSignal.any([...(at.signal !== undefined ? [at.signal] : []), AbortSignal.timeout(DEFAULT_GATE_AWAIT_TIMEOUT_MS)]); const cutShort = new Promise((resolve) => { const onCut = () => resolve({ ok: false, reason: "unresolvable", cause: at.signal?.aborted === true ? "the call ended before it resolved" : `it did not resolve within ${DEFAULT_GATE_AWAIT_TIMEOUT_MS}ms` }); if (cut.aborted) onCut(); else cut.addEventListener("abort", onCut, { once: true }); }); const attempt = grant(candidate, cut).catch((err) => ({ ok: false, reason: "unresolvable", cause: thrownText(err) })); return Promise.race([attempt, cutShort]).then((applied) => { if (!applied.ok) { const why = { unresolvable: `the directory could not be resolved (${applied.cause})`, resolves_elsewhere: `the offered path no longer resolves to itself (${applied.cause})`, admits_artifact_store: `the directory would admit the artifact store (${applied.cause})`, }[applied.reason]; reject(applied.reason, candidate.dir, candidate.dir, why); return; } deliverEngineNotice(at.onNotice, { code: "approval.read_root_granted", message: `the approval for "${at.toolName}" added ${candidate.covers === "exact" ? "the path" : "the directory"} ${candidate.dir} to this run's read directories (reads only; for the rest of this run).`, detail: { dir: candidate.dir, ...(candidate.covers !== undefined ? { covers: candidate.covers } : {}), root: applied.root, ...ids }, }); }); }