UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

201 lines (200 loc) • 12.1 kB
import { persistedRuleMandateOf, ruleClearanceOf } from "../ask-origin.js"; import {} from "../hooks.js"; import { adjudicatePersistedPathRules, adjudicatePersistedRules, callPathFaceOf, faceAskSpeaksFor, laneToolNamesOf, ruleToolGrammarOf, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js"; import { isAbsolutePathForm } from "../../tools/fs/safety.js"; import { effectivePathTargetOf } from "../effective-path-target.js"; import { declaredPathTargetOf, protectivePathTargetOf } from "../tool-registry.js"; import { heldBy, seatOf, wireOf } from "../tool-name-readings.js"; import { orgRuleVerdictFor, settleOrgVerdictWithin } from "../permission-rule-org.js"; import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js"; import { ownerStoreOf } from "../permission-rule-provider.js"; import { thrownText } from "../thrown-value.js"; import { strongestAnswer } from "../persisted-rule-arms.js"; import { gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js"; import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js"; export const PERSISTED_RULE_TOOL = "Bash"; const HANDOFF_BOUND = 256; export function inheritedAskRuleEvidence(provider) { const org = provider?.partitions.org === true ? "not_adjudicated" : "not_wired"; const personal = provider?.partitions.durable === true ? "not_adjudicated" : "not_wired"; return Object.freeze({ orgRevisionAbsent: org, orgRuleAbsent: org, personalRuleDotsAbsent: personal }); } export function orgRevisionEvidenceOf(resolution, onDefect) { const reported = resolution.revision; if (reported === undefined) return {}; if (typeof reported === "number" && Number.isFinite(reported)) return { revision: reported }; onDefect(`the org rule partition reported revision ${JSON.stringify(reported)} — a snapshot revision is a finite number; ` + `the adjudication stands, but the ask carries no revision evidence for this call`); return {}; } export function persistedRuleHitOf(verdict) { return verdict === undefined ? undefined : { behavior: verdict.behavior, rules: verdict.rules.map((r) => ({ rule: r.rule, dots: r.adds.map((a) => ({ actor: a.dot.actor, counter: a.dot.counter })) })) }; } export function persistedRuleAnswerOf(verdict) { if (verdict?.unreadable !== undefined) return { unreadable: true, reason: verdict.unreadable }; return persistedRuleHitOf(verdict); } export function pathRuleLaneAnswer(table, req, ctx) { const bases = { ...(ctx.root !== undefined ? { root: ctx.root } : {}), ...(ctx.liveCwd !== undefined ? { cwd: ctx.liveCwd } : {}), ...(ctx.home !== undefined ? { home: ctx.home } : {}) }; const tighten = effectivePathTargetOf(req, protectivePathTargetOf(req), bases); const allow = effectivePathTargetOf(req, declaredPathTargetOf(req), bases); if (tighten === undefined && allow === undefined) return undefined; return persistedRuleAnswerOf(adjudicatePersistedPathRules(table, { tool: req.toolName, cwd: ctx.root, sessionId: ctx.sessionId, face: callPathFaceOf(req) }, { ...(tighten !== undefined ? { tighten } : {}), ...(allow !== undefined ? { allow } : {}) }, bases)); } export function orgRuleLane(cfg) { return { adjudicate: async (req) => { if (wireOf("offer.orgQuestionExemption", req) === cfg.questionToolName && cfg.questionToolMounted) return { status: "available" }; let resolution; try { resolution = (await cfg.read(req.toolCallId)).org; } catch (err) { return { status: "unavailable", disclosures: [`the permission-rule store threw while resolving the org partition: ${thrownText(err)}`] }; } if (resolution.status === "unavailable" || resolution.status === "ungoverned") { return { status: "unavailable", disclosures: resolution.status === "ungoverned" ? ["the store reports no org partition while the provider declares one — unreadable governance, never an empty one"] : resolution.disclosures }; } const revisionCell = orgRevisionEvidenceOf(resolution, cfg.onRevisionDefect); const command = req.args?.command; if (!laneToolNamesOf(req).includes(PERSISTED_RULE_TOOL) || typeof command !== "string") return { status: "available", ...revisionCell }; const verdict = orgRuleVerdictFor(resolution.rules, { tool: PERSISTED_RULE_TOOL, command }); return verdict === undefined ? { status: "available", ...revisionCell } : { status: "available", verdict, ...revisionCell }; }, }; } export function orgVerdictBeforeClaim(cfg, req) { const unreadable = { status: "unavailable", disclosures: [] }; const read = () => { const store = ownerStoreOf(cfg.provider, cfg.principal, cfg.localOwnerDeclared); return orgRuleLane({ read: () => store.effective({ sessionId: cfg.sessionId }), questionToolMounted: true, questionToolName: ASK_USER_QUESTION_TOOL_NAME, onRevisionDefect: () => undefined }).adjudicate(req); }; return settleOrgVerdictWithin(Promise.resolve().then(read).catch(() => unreadable), unreadable, { timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS, ...(cfg.signal !== undefined ? { signal: cfg.signal } : {}) }); } export function createPermissionRuleLanes(cfg) { const provider = cfg.provider; if (cfg.home !== undefined && !isAbsolutePathForm(cfg.home)) { const e = new Error(`the execution environment declares homeDir ${JSON.stringify(cfg.home)}, which is not an absolute path — a \`~/\` permission rule could not be resolved against it; declare an absolute home directory or none`); e.code = "config.execution_env_home_dir_invalid"; throw e; } if (cfg.localOwnerDeclared) { if (provider === undefined || !provider.partitions.durable) { throw new Error("RunnerDeps.localOwnerRules is declared but no durable permission-rule partition is wired — there is no bucket for the local owner to hold rules in; refusing rather than running as if the declaration were absent"); } if (provider.forLocalOwner === undefined) { throw new Error("RunnerDeps.localOwnerRules is declared but the wired permission-rule provider implements no forLocalOwner() face — a provider without a local-owner bucket cannot honor the declaration; refusing rather than silently resolving zero rules"); } } if (provider === undefined) return { personal: undefined, org: undefined }; const anonymous = cfg.principal === undefined || cfg.principal === ""; const store = ownerStoreOf(provider, cfg.principal, cfg.localOwnerDeclared); const handed = new Map(); const read = () => store.effective({ sessionId: cfg.sessionId }).then((view) => { for (const line of view.disclosures) cfg.onDisclosure(line); return view; }); const hand = (toolCallId) => { const p = read(); handed.delete(toolCallId); handed.set(toolCallId, p); if (handed.size > HANDOFF_BOUND) { const oldest = handed.keys().next().value; if (oldest !== undefined) handed.delete(oldest); } p.catch(() => undefined); return p; }; const claim = (toolCallId) => { const p = handed.get(toolCallId); if (p === undefined) return read(); handed.delete(toolCallId); return p; }; const org = provider.partitions.org ? orgRuleLane({ read: hand, questionToolMounted: cfg.questionToolMounted, questionToolName: cfg.questionToolName, onRevisionDefect: cfg.onRevisionDefect }) : undefined; const personalAdjudicate = async (req) => { if (anonymous && !cfg.localOwnerDeclared) return undefined; const judged = laneToolNamesOf(req).map((n) => [n, ruleToolGrammarOf(n)]); if (!judged.some(([, g]) => g === "path") && faceAskSpeaksFor(callPathFaceOf(req))) judged.push([req.toolName, "path"]); if (judged.length === 0) return undefined; let view; try { view = await withGateTimeout(claim(req.toolCallId), ORG_ADJUDICATION_TIMEOUT_MS, cfg.signal, "persistedRules"); } catch (err) { return gateWaitFailureOf(err) === "aborted" ? { unreadable: true } : skipped(thrownText(err)); } if (view.unreadable !== undefined) skipped(view.unreadable, view.unreadableSources); try { const answer = strongestAnswer(judged.map(([n, grammar]) => judgeAgainst(view.rules.filter((r) => r.behavior !== "allow" || cfg.stripsAllow?.(r, req) !== true), { ...req, toolName: n }, grammar))); return view.unreadable !== undefined && (answer === undefined || "segmentCoverage" in answer) ? { unreadable: true } : answer; } catch (err) { const why = `judging this call against the rows threw: ${thrownText(err)}`; cfg.onDisclosure(`the persisted rule lane could not judge a ${req.toolName} call against the rows it read — ${why}`); return { unreadable: true, reason: why }; } }; const judgeAgainst = (table, req, grammar) => { const liveCwd = cfg.liveCwd(); if (grammar === "path") return pathRuleLaneAnswer(table, req, { root: cfg.root, sessionId: cfg.sessionId, liveCwd, home: cfg.home }); const command = req.args?.command; if (typeof command !== "string") return undefined; const execCwd = liveCwd !== undefined ? { execCwd: liveCwd } : {}; const verdict = adjudicatePersistedRules(table, { tool: req.toolName, command, cwd: cfg.root, sessionId: cfg.sessionId, ...execCwd }); if (verdict !== undefined) return persistedRuleAnswerOf(verdict); const coverage = segmentCoverageOf(command, { persisted: table }, { tool: req.toolName, cwd: cfg.root, sessionId: cfg.sessionId, ...execCwd }); return coverage !== undefined ? { segmentCoverage: coverage } : undefined; }; const skipped = (why, sources) => (cfg.onDisclosure(why), cfg.onStoreUnreadable("call", sources), { unreadable: true }); const personal = provider.partitions.durable ? { adjudicate: personalAdjudicate } : undefined; return { personal, org }; } export function createRuleOffersOf(cfg) { return (call, args, ask) => { const toolName = wireOf("offer.laneTool", call); if (!cfg.laneArmed || toolName !== PERSISTED_RULE_TOOL) return {}; if ((cfg.principal === undefined || cfg.principal === "") && !cfg.localOwnerDeclared) return {}; const tierSeat = seatOf("offer.marks", cfg.irreversibilityTier, call); const closedDoor = ruleClearanceOf(ask, { egress: heldBy("offer.marks.egress", cfg.egressTools, call), irreversibility: tierSeat?.value, shellGated: tierSeat?.shellGated(cfg.shellGatedTools, cfg.toolMarkSources) === true, }).word; if (closedDoor !== undefined) return { ruleOffersAbsence: closedDoor }; const command = args?.command; if (typeof command !== "string") return { ruleOffersAbsence: "lane_cannot_speak" }; const offers = suggestRulesForCommand(command, { ...(ask?.segmentCoverage !== undefined ? { coverage: ask.segmentCoverage } : {}), ...(cfg.taskRoot !== undefined ? { scope: { kind: "project", root: cfg.taskRoot }, cwd: cfg.taskRoot } : {}), ...(cfg.cwdRef?.current !== undefined ? { execCwd: cfg.cwdRef.current } : {}), ...(cfg.deniesDirectoryRead !== undefined ? { deniesDirectoryRead: cfg.deniesDirectoryRead } : {}), }); return offers.length > 0 ? { ruleOffers: offers, ...(cfg.cwdRef?.current !== undefined ? { execCwd: cfg.cwdRef.current } : {}) } : { ruleOffersAbsence: "lane_cannot_speak" }; }; }