UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

144 lines (143 loc) • 10.3 kB
import { deliverEngineNotice } from "../../core/types.js"; import { createTaskOutputTool, createTaskStopTool } from "../../core/task-registry.js"; import { hasBackgroundShell } from "../../core/background-shell.js"; import {} from "./safety.js"; import { createRepoMapTool } from "./repo-map.js"; export * from "./fs-shared.js"; export * from "./fs-pdf.js"; export * from "./fs-read.js"; export * from "./fs-write.js"; export * from "./fs-search-tools.js"; export * from "./bash-readonly-classifier.js"; export * from "./fs-bash.js"; export * from "./read-deny.js"; export { readDenyHomesOf } from "./read-deny-home.js"; import { BASH_READONLY_DEFAULT_ALLOW } from "./bash-readonly-classifier.js"; export { BASH_CLASSIFY_EXTRA_ALLOW, bashClassifyDefaultAllow } from "./bash-readonly-classifier.js"; import { checkedReadDenyHomes, compileReadDeny, compileWriteDeny, readDenyBuiltinConfigOf, readDenyPatternRun } from "./read-deny.js"; import { withGateTimeout } from "../../core/gate-bounded-await.js"; import { carryHomeViews, homeViewsAt, placeReadDenyAtHome, placementViewsOf, readDenyPlacedLater, sayPlacementLost } from "./read-deny-home.js"; import { deploymentFaceClampNotice, resolveContainmentFace } from "./containment-face.js"; export * from "./containment-face.js"; import {} from "./fs-shared.js"; import { createReadFileTool } from "./fs-read.js"; import { createEditFileTool, createWriteFileTool, createNotebookEditTool } from "./fs-write.js"; import { createGrepTool, createGlobTool } from "./fs-search-tools.js"; import { createBashTool, createBashReadonlyTool, createEnvTaskOutputTool, createEnvTaskStopTool } from "./fs-bash.js"; export function handsWriteHalfMounts(opts) { return opts.readOnly !== true; } export function backgroundOutputReadExemption(registry, seat, env) { if (registry === undefined) return undefined; return (canonicalKey, c) => registry.isBackgroundOutputFile(canonicalKey, { owner: c?.taskId ?? seat.taskOwner, scope: c?.principal ?? seat.taskScope, ...(seat.sessionId !== undefined ? { sessionId: seat.sessionId } : {}) }, env); } export function createHandsToolkit(env, readFileState, rootCanonical, opts = {}) { const { includeShell = false, readOnly = false, bashReadonlyAllow, commitCoAuthor = false, mountBackgroundTaskTools = true, additionalRoots, additionalReadRoots, } = opts; const readFaceRoots = additionalReadRoots === undefined ? additionalRoots : () => (additionalReadRoots.length === 0 ? additionalRoots : [...(additionalRoots ?? []), ...additionalReadRoots]); const cwdRef = opts.cwdRef ?? { current: rootCanonical }; const bgOutput = backgroundOutputReadExemption(opts.taskRegistry, opts, env); const readAdmission = opts.readAdmission; const bgOutputReadExemption = bgOutput === undefined && readAdmission === undefined ? undefined : (canonicalKey, c) => readAdmission?.(c.toolCallId, canonicalKey) === true || (bgOutput?.(canonicalKey, c) ?? false); const readDenyAdditions = (opts.readDenyPatterns ?? []).map((e) => (typeof e === "object" && e !== null ? { ...e } : e)); const readDenyRules = opts.pathDenyRules === undefined ? undefined : [...opts.pathDenyRules]; const readDenyBuiltinCfg = readDenyBuiltinConfigOf({ tiers: opts.readDenyBuiltinTiers, exclude: opts.readDenyBuiltinExclude }); const readDenyHomes = opts.readDenyHomes === undefined ? undefined : [...checkedReadDenyHomes(opts.readDenyHomes)]; const compileAt = (homes, linked) => compileReadDeny(readDenyAdditions, "HandsToolkitOptions.readDenyPatterns", readDenyBuiltinCfg, readDenyRules, homes, linked); const unplaced = compileAt([]); const carried = readDenyHomes !== undefined && opts.readDenyHomeLinks !== undefined; const carriedViews = carried ? placementViewsOf(opts.readDenyHomes) : undefined; if (carried && carriedViews === undefined) sayPlacementLost(); const placesNothing = !unplaced.entries.some((e) => readDenyPatternRun(e.pattern).homeAnchored); const late = carried || placesNothing ? undefined : readDenyPlacedLater(carryHomeViews(unplaced, homeViewsAt(env, [])), () => placeReadDenyAtHome(env, compileAt, readDenyHomes)); const readDeny = late?.matcher ?? (carried ? carriedViews !== undefined ? carryHomeViews(compileAt(readDenyHomes, opts.readDenyHomeLinks), carriedViews) : carryHomeViews(unplaced, homeViewsAt(env, [])) : carryHomeViews(unplaced, homeViewsAt(env, readDenyHomes ?? []))); const afterPlacement = (tool) => late === undefined ? tool : { ...tool, execute: async (...args) => { await withGateTimeout(late.ready(), undefined, args[2], "readDenyPlacement").catch(() => undefined); return await tool.execute(...args); }, }; const writeDenyCompiled = compileWriteDeny(readDenyRules); const writeDeny = writeDenyCompiled.ruleRows.length > 0 ? carryHomeViews(writeDenyCompiled, readDeny.homeViews) : undefined; const readFace = resolveContainmentFace({ kind: "read", depsFace: opts.readFace, readOnlyMount: readOnly, orgGoverned: false, fullShellReachable: includeShell && !readOnly, onDeploymentClamp: () => deliverEngineNotice(opts.onNotice, deploymentFaceClampNotice("read")), }); const writeFace = resolveContainmentFace({ kind: "write", depsFace: opts.writeFace, readOnlyMount: readOnly, orgGoverned: false, fullShellReachable: includeShell && !readOnly, onDeploymentClamp: () => deliverEngineNotice(opts.onNotice, deploymentFaceClampNotice("write")), }); const writeFaceOf = opts.writeFaceLive ?? (() => writeFace); const tools = [ afterPlacement(createReadFileTool(env, readFileState, rootCanonical, readOnly ? undefined : cwdRef, readFaceRoots, opts.readImageDownsampler, opts.pdfModelCapabilities, bgOutputReadExemption, readDeny, readFace, opts.reminderMark, opts.reminderDisclosureCounts)), ]; if (handsWriteHalfMounts({ readOnly })) { tools.push(createEditFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited, writeFaceOf, writeDeny), createWriteFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited, writeFaceOf, writeDeny), createNotebookEditTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited, writeFaceOf, writeDeny)); } tools.push(afterPlacement(createGrepTool(env, rootCanonical, readFaceRoots, readDeny, readFace, cwdRef, readAdmission)), afterPlacement(createGlobTool(env, rootCanonical, readFaceRoots, readDeny, readFace, cwdRef, readAdmission)), afterPlacement(createRepoMapTool(env, rootCanonical, readFaceRoots, readDeny, readFace))); if (includeShell) { tools.push(readOnly ? createBashReadonlyTool(env, rootCanonical, new Set(bashReadonlyAllow ?? BASH_READONLY_DEFAULT_ALLOW), { ...(opts.bashDefaultTimeoutMs !== undefined ? { bashDefaultTimeoutMs: opts.bashDefaultTimeoutMs } : {}), ...(opts.bashMaxTimeoutMs !== undefined ? { bashMaxTimeoutMs: opts.bashMaxTimeoutMs } : {}), ...(opts.onNotice !== undefined ? { onNotice: opts.onNotice } : {}), ...(opts.shellReadAdmission !== undefined ? { shellReadAdmission: opts.shellReadAdmission } : {}), ...(opts.onToolProgress !== undefined ? { onToolProgress: opts.onToolProgress } : {}), }) : createBashTool(env, rootCanonical, commitCoAuthor, cwdRef, { taskRegistry: opts.taskRegistry, taskOwner: opts.taskOwner, taskScope: opts.taskScope, ...(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}), ...(opts.retainBackgroundProcesses !== undefined ? { retainBackgroundProcesses: opts.retainBackgroundProcesses } : {}), onTaskNotification: opts.taskNotification, detachHub: opts.detachHub, ...(opts.autoBackgroundOnTimeout !== undefined ? { autoBackgroundOnTimeout: opts.autoBackgroundOnTimeout } : {}), ...(opts.onToolProgress !== undefined ? { onToolProgress: opts.onToolProgress } : {}), ...(opts.queuedHumanInput !== undefined ? { queuedHumanInput: opts.queuedHumanInput } : {}), ...(opts.oneShot !== undefined ? { oneShot: opts.oneShot } : {}), ...(additionalRoots !== undefined ? { additionalRoots } : {}), ...(opts.shellReadAdmission !== undefined ? { shellReadAdmission: opts.shellReadAdmission } : {}), ...(opts.bashDefaultTimeoutMs !== undefined ? { bashDefaultTimeoutMs: opts.bashDefaultTimeoutMs } : {}), ...(opts.bashMaxTimeoutMs !== undefined ? { bashMaxTimeoutMs: opts.bashMaxTimeoutMs } : {}), ...(opts.onNotice !== undefined ? { onNotice: opts.onNotice } : {}), ...(opts.monitorToolActive !== undefined ? { monitorToolActive: opts.monitorToolActive } : {}), ...(opts.reminderMark !== undefined ? { reminderMark: opts.reminderMark } : {}), })); if (!readOnly && mountBackgroundTaskTools && hasBackgroundShell(env)) { const sessionAxis = opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}; tools.push(opts.taskRegistry ? createTaskOutputTool({ registry: opts.taskRegistry, owner: opts.taskOwner, scope: opts.taskScope, ...sessionAxis, ...(opts.oneShot !== undefined ? { oneShot: opts.oneShot } : {}), notificationWired: opts.taskNotification !== undefined, }) : createEnvTaskOutputTool(env), opts.taskRegistry ? createTaskStopTool({ registry: opts.taskRegistry, owner: opts.taskOwner, scope: opts.taskScope, ...sessionAxis }) : createEnvTaskStopTool(env)); } } return tools; }