UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

378 lines (377 loc) • 25.4 kB
import { isSyntheticApiErrorMessage } from "../../internal/harness.js"; import { createHash } from "node:crypto"; import { childThinkingSeat, deploymentSubagentThinking, deploymentSubagentTierModel, forkGovernanceDenial } from "../../agents/subagent.js"; import { inheritedParentModel } from "../../agents/child-model-seat.js"; import { CROSS_SESSION_CLASSIFIER_RULE } from "../../agents/cross-session-envelope.js"; import { createRunWorkflowTool } from "../../orchestration/run-workflow-tool.js"; import { isSelfOrchestrationActive } from "../../orchestration/workflow-script-runner.js"; import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js"; import { autoModeArmingRecipeOf } from "../auto-mode-arming.js"; import { AUTO_MODE_CLASSIFIER_MAX_RETRIES } from "../auto-mode-defaults.js"; import { buildAutoModePrompt, readDenyClassifierRules, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js"; import { autoModeFaceOf } from "../mode-policy.js"; import { autoModeClassifierFallbackOf, createAutoModeDecider, createAutoModeDenialTracker, screenAutoModeKnobs } from "../auto-mode.js"; import { checkpointStoreOfSeat } from "../checkpoint-seat.js"; import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, resolveComplianceDenies } from "../compliance.js"; import { createPresentPlanTool, createEnterPlanModeTool } from "../present-plan-tool.js"; import { brainToRuntime } from "../runtime.js"; import { defaultTaskRegistry } from "../task-registry.js"; import { defineTool } from "../tools.js"; const MOUNT_REMEDY = "author it as a plain-object ToolSpec (own `name` and `execute`), hand the defineTool product itself, or wrap a product through the supported wrapper form (a fresh object re-stamped with `stampDefineToolBrand` whose rebind re-wraps the rebound product)"; import { isAutoModeDenySource } from "../runtime-caps.js"; import { unionAddOnly } from "../child-clamp-seats.js"; import { derivedRouteFallsBack } from "./derived-route-fallback.js"; import { CLASSIFIER_ATTEMPT_SUFFIX, CLASSIFIER_THINK_SEAT, announceClassifierRoleDefaulted, classifierLadderRungs, classifierReplyAsksAgain, classifierReplyFailure, classifierReplyVerdictText, classifierRoundObserver, classifierRungSeat, classifierSeatModel } from "./classifier-ladder.js"; import { REPORT_FINDINGS_TOOL_NAME, createReportBlockedTool, createReportFindingsTool } from "./synthetic-tools.js"; import { RosterBuilder, callerMountSource } from "../tool-roster.js"; import { asThrownError, thrownText } from "../thrown-value.js"; import { callerEntryOrigin, rebindProductEntry, specArmCanBuild } from "./caller-roster.js"; function assembleParentCaptureState(o, i, ctl, ancestors) { const build = (optedOut, indeterminate) => ({ optedOut: optedOut === true, indeterminate: indeterminate === true, ...(ctl !== undefined ? { controlDir: ctl } : {}), ancestors, }); return o instanceof Promise || i instanceof Promise ? Promise.all([o, i]).then(([ov, iv]) => build(ov, iv)) : build(o, i); } const RUNTIME_CAP_KEYS = { allowWorkflows: true, allowFork: true, allowObservers: true, forceDurableGate: true, autoMode: true, autoModeDenySource: true, bypassPermissions: true, allowMemoryOptOut: true, }; function snapshotRuntimeCaps(record) { const out = {}; for (const key of Object.keys(RUNTIME_CAP_KEYS)) { const v = record[key]; if (v !== undefined) out[key] = v; } return out; } function screenAutoModeCap(caps, onError, sessionId) { if (caps === undefined) return { caps, faulted: false }; if (typeof caps !== "object" || caps === null || Array.isArray(caps)) { onError?.(new Error(`runtimeCapsResolver returned a non-record value (${JSON.stringify(caps)}) — read as a resolver fault: every per-principal capability is DENIED for this run (auto mode is not armed; asks flow the original chain). Return a RuntimeCaps object or undefined.`), { phase: "config", sessionId }); return { caps: { allowWorkflows: false, allowFork: false, autoMode: false, bypassPermissions: false }, faulted: true }; } let record; try { record = snapshotRuntimeCaps(caps); } catch (err) { onError?.(new Error(`runtimeCapsResolver's record threw while being read (${thrownText(err)}) — read as a resolver fault: every per-principal capability is DENIED for this run (auto mode is not armed; asks flow the original chain).`), { phase: "config", sessionId }); return { caps: { allowWorkflows: false, allowFork: false, autoMode: false, bypassPermissions: false }, faulted: true }; } let screened = record; let faulted = false; const fault = (message) => { onError?.(new Error(`runtimeCapsResolver returned ${message}`), { phase: "config", sessionId }); faulted = true; }; const { autoMode, autoModeDenySource } = record; if (autoModeDenySource !== undefined && (!isAutoModeDenySource(autoModeDenySource) || autoMode !== false)) { fault(`autoModeDenySource=${JSON.stringify(autoModeDenySource)} beside autoMode=${JSON.stringify(autoMode)} — read as a resolver fault: auto mode is DENIED for this run (the classifier is not armed; asks flow to the approver / park)`); const { autoModeDenySource: _refused, ...rest } = screened; screened = { ...rest, autoMode: false }; } else if (autoMode !== undefined && typeof autoMode !== "boolean") { fault(`a non-boolean autoMode (${JSON.stringify(autoMode)}) — read as a resolver fault: auto mode is DENIED for this run (the classifier is not armed; asks flow the same way they do with no auto-mode face)`); screened = { ...screened, autoMode: false }; } if (record.bypassPermissions !== undefined && typeof record.bypassPermissions !== "boolean") { fault(`a non-boolean bypassPermissions (${JSON.stringify(record.bypassPermissions)}) — read as a resolver fault: bypassPermissions is DENIED for this run (a requested bypass folds back to default)`); screened = { ...screened, bypassPermissions: false }; } return { caps: screened, faulted }; } function mintPersistedArming(am, laneRule, classifierSystemPrompt, derivedDenyRules, onError, sessionId) { const arming = autoModeArmingRecipeOf(laneRule ? { ...am, crossSessionMessagesRule: true } : am, { promptDigest: `apv1:${createHash("sha256").update(classifierSystemPrompt).digest("hex")}`, derivedDenyRules, }); if (arming === undefined) { onError?.(new Error("RunnerDeps.autoMode.persistArming is set but the auto-mode face did not canonicalize into an arming recipe " + "(a rule list carrying a non-string, or a non-finite/out-of-range timeoutMs / failureThreshold / window bound) — " + "NO arming recipe is recorded on this run's parked constraint chain, and a cross-process redemption keeps the " + "conservative behavior (the ancestor classifier answers unavailable and the inherited ask flows to a human)"), { phase: "config", sessionId }); } return arming; } function autoModeArmReasonOf(intent, facePresent, caps, capsFaulted) { if (!intent) return { armed: false, reason: "no_intent" }; if (!facePresent) return { armed: false, reason: "no_face" }; if (caps?.autoMode === false) { if (capsFaulted) return { armed: false, reason: "resolver_fault" }; return { armed: false, reason: "denied", ...(caps.autoModeDenySource !== undefined ? { deniedSource: caps.autoModeDenySource } : {}) }; } return { armed: true, reason: "armed" }; } export async function prepareCapsAndWorkflow(input) { const { spec, deps, parentSpecSeat, checkpointSeat, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, askSeat, runClampSource, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, permissionMode, readDenyEntries, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, delegated, sideCallBookRef } = input; const roster = new RosterBuilder(toolEffects); for (const t of spec.tools ?? []) { const mounted = mountCallerTool(t); roster.mount(mounted, { source: callerMountSource(mounted), mountedBy: "scenario" }); } function mountCallerTool(t) { const rebound = rebindProductEntry(t, enrichSpecToolCtx); if (rebound !== undefined) return maybeOffload(rebound, t); if (!specArmCanBuild(t)) { const shown = Object.getOwnPropertyDescriptor(t, "name")?.value; const e = new Error(`spec.tools entry ${typeof shown === "string" ? JSON.stringify(shown) : "(no own name)"}: the ToolSpec arm rebuilds a caller tool from its OWN enumerable members, and this entry does not carry a string \`name\` and a function \`execute\` as own properties (a class instance, a prototype child of a spec or of a defineTool product, a function, or a Proxy over one) — ${MOUNT_REMEDY}`); e.code = "config.tool_mount_denied"; throw e; } const execute = t.execute, self = callerEntryOrigin(t); return maybeOffload(defineTool({ ...t, execute: (args, ctx) => execute.call(self, args, enrichSpecToolCtx(ctx)), }), t); } const blockedRef = {}; if (spec.enableBlockedReport !== false) { roster.mount(createReportBlockedTool(blockedRef), { source: "builtin", mountedBy: "always" }); } if (!(spec.tools ?? []).some((t) => t.name === REPORT_FINDINGS_TOOL_NAME || t.aliases?.includes(REPORT_FINDINGS_TOOL_NAME))) { roster.mount(createReportFindingsTool(), { source: "builtin", mountedBy: "always" }); } if (spec.enablePlanMode === true && spec.interactiveTools !== false) { const planReviewFace = checkpointStoreOfSeat(checkpointSeat) !== undefined; if (spec.interactiveTools === true || planReviewFace) { roster.mount(defineTool(createPresentPlanTool(requestReview)), { source: "builtin", mountedBy: "optional" }); if (planReviewFace) { roster.mount(defineTool(createEnterPlanModeTool(enterPlanMode)), { source: "builtin", mountedBy: "optional" }); } } } let runtimeCaps; let runtimeCapsFaulted = false; if (deps.runtimeCapsResolver) { try { runtimeCaps = (await deps.runtimeCapsResolver(spec.principal)) ?? undefined; } catch (err) { deps.onError?.(asThrownError(err), { phase: "config", sessionId }); runtimeCaps = { allowWorkflows: false, allowFork: false, autoMode: false, bypassPermissions: false }; runtimeCapsFaulted = true; } const screened = screenAutoModeCap(runtimeCaps, deps.onError, sessionId); runtimeCaps = screened.caps; runtimeCapsFaulted ||= screened.faulted; } let deploymentDenies = new Set(); let complianceDegraded = false; if (deps.compliancePostureResolver) { try { const posture = (await deps.compliancePostureResolver(spec.principal)) ?? undefined; if (posture !== undefined) deploymentDenies = resolveComplianceDenies(posture); } catch (err) { deps.onError?.(asThrownError(err), { phase: "config", sessionId }); deploymentDenies = new Set(COMPLIANCE_CAPABILITIES); complianceDegraded = true; } } const complianceDenies = new Set(unionAddOnly("complianceAdditionalDenies", [...deploymentDenies], spec.complianceAdditionalDenies)); if (complianceDenies.size > 0) { const complianceRefuse = (capability, requested) => { const e = new Error(`${requested} is denied for this principal by the compliance posture (capability "${capability}"` + `${complianceDegraded ? "; the posture resolver is currently failing, so every managed capability is denied fail-closed" : ""}) — ` + `the task is refused rather than silently narrowed.`); e.code = complianceDegraded ? "config.compliance_required" : "config.compliance_denied"; throw e; }; if (complianceDenies.has("mcp_servers") && lockedPreflight.mcp?.length) { complianceRefuse("mcp_servers", "TaskSpec.mcp (MCP server materialization)"); } if (complianceDenies.has("workflows") && spec.selfOrchestration === true) { complianceRefuse("workflows", "TaskSpec.selfOrchestration (workflow self-orchestration)"); } if (complianceDenies.has("web_fetch")) { const webTool = (spec.tools ?? []).find((t) => t.name === WEB_FETCH_TOOL_NAME || (t.aliases ?? []).includes(WEB_FETCH_TOOL_NAME)); if (webTool !== undefined) { complianceRefuse("web_fetch", `TaskSpec.tools["${webTool.name}"] (the WebFetch tool face)`); } } } const agentForkDenial = forkGovernanceDenial(spec.enableFork, runtimeCaps?.allowFork); const observersActive = runtimeCaps?.allowObservers === true; const autoModeDeciderRef = { current: undefined }; const autoModeDenialTrackingRef = { current: undefined }; const autoModeArmingRef = { current: undefined }; const autoModeFace = autoModeFaceOf(deps, internals?.inheritedGate); const autoModeArmReason = () => autoModeArmReasonOf(permissionMode.declared === "auto", autoModeFace !== undefined, runtimeCaps, runtimeCapsFaulted); if (autoModeFace !== undefined) screenAutoModeKnobs(autoModeFace); const armAutoMode = async () => { if (autoModeDeciderRef.current !== undefined) return; if (permissionMode.declared !== "auto" || autoModeFace === undefined || runtimeCaps?.autoMode === false) return; const am = autoModeFace; const classifierSeat = classifierSeatModel({ deps, main: model }); let classifierModel = classifierSeat.model; if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId, runId, onBrokenPairing: autoModeClassifierFallbackOf(am.classifierFallback) === "none" ? "refuse" : "fall_back" })) { classifierModel = model; } announceClassifierRoleDefaulted({ onNotice: deps.onNotice, seat: { ...classifierSeat, model: classifierModel }, ...(sessionId !== undefined ? { sessionId } : {}), ...(runId !== undefined ? { runId } : {}) }); const classifierLaneRule = peerLaneActive && peerSendMessageBuiltIn; const table = await readDenyEntries(); const derivedDenyRules = readDenyClassifierRules(table.entries, table.ruleRows); let classifierSystemPromptMemo; const classifierSystemPrompt = () => (classifierSystemPromptMemo ??= buildAutoModePrompt({ ...am, derivedDenyRules, ...(classifierLaneRule ? { crossSessionMessagesRule: CROSS_SESSION_CLASSIFIER_RULE } : {}) })); const classifierRuntime = brainToRuntime(deps.brain); const classifierRungs = classifierLadderRungs(am.classifierFallback, classifierModel, model); const denialTracking = createAutoModeDenialTracker(am.denialLimit); const decider = createAutoModeDecider({ ...(am.timeoutMs !== undefined ? { timeoutMs: am.timeoutMs } : {}), candidates: classifierRungs.map((m) => m.id), onClassified: classifierRoundObserver({ tracer: deps.tracer, onNotice: deps.onNotice, taskId: hostTaskId, seatModelId: classifierModel.id, ...(sessionId !== undefined ? { sessionId } : {}), ...(runId !== undefined ? { runId } : {}) }), assemble: async (input) => { const ctx = await session.buildContext(); const known = ctx.messages.filter((m) => !isSyntheticApiErrorMessage(m) && (m.role === "user" || m.role === "assistant" || m.role === "toolResult")); return renderAutoModeWindow(known, am.window, { delegated }) + renderAutoModeAction(input); }, classify: async (_input, signal, rung, userPrompt) => { const rungModel = rung !== undefined ? classifierRungs[rung.index] : undefined; if (rung === undefined || rungModel === undefined) { throw new Error(`the auto-mode classifier was asked at ladder position ${JSON.stringify(rung?.index)}, which this arming did not build`); } if (userPrompt === undefined) { throw new Error("the auto-mode classifier was asked with no assembled question, which this arming builds once per classification"); } const classifierAuth = await spec.getApiKeyAndHeaders?.(rungModel); const request = async (seat, suffix) => { const book = sideCallBookRef.book; if (book === undefined) throw new Error("the auto-mode classifier was asked before the run body bound the leg's usage account, which it does before any tool call"); let replied = false; const cut = () => { if (!replied) book("classifier", rungModel, undefined); }; signal?.addEventListener("abort", cut, { once: true }); let response; try { response = await classifierRuntime.completeSimple(rungModel, { systemPrompt: classifierSystemPrompt(), messages: [{ role: "user", content: userPrompt + suffix, timestamp: Date.now() }] }, { signal, maxRetries: AUTO_MODE_CLASSIFIER_MAX_RETRIES, ...seat, ...(classifierAuth?.apiKey !== undefined ? { apiKey: classifierAuth.apiKey } : {}), ...(classifierAuth?.headers !== undefined ? { headers: classifierAuth.headers } : {}), }); } finally { replied = true; signal?.removeEventListener("abort", cut); } book("classifier", rungModel, response); const failure = classifierReplyFailure(response); if (failure !== undefined) throw failure; return response; }; let response = await request(classifierRungSeat(rungModel), CLASSIFIER_ATTEMPT_SUFFIX.first); if (classifierReplyAsksAgain(response) && signal?.aborted !== true) response = await request(CLASSIFIER_THINK_SEAT, CLASSIFIER_ATTEMPT_SUFFIX.reask); return classifierReplyVerdictText(response); }, }); const arming = am.persistArming === true ? mintPersistedArming(am, classifierLaneRule, classifierSystemPrompt(), derivedDenyRules, deps.onError, sessionId) : undefined; if (autoModeDeciderRef.current !== undefined) return; autoModeDeciderRef.current = decider; autoModeDenialTrackingRef.current = denialTracking; autoModeArmingRef.current = arming; }; permissionMode.bindArming({ caps: runtimeCaps, autoFacePresent: autoModeFace !== undefined, armAuto: armAutoMode, onArmFault: (e) => deps.onError?.(asThrownError(e), { phase: "config", sessionId }) }); const deploymentWorkflowReady = runnerSelf !== undefined && isSelfOrchestrationActive(spec, deps); const selfOrchestrationActive = deploymentWorkflowReady && runtimeCaps?.allowWorkflows !== false; if (deploymentWorkflowReady && runtimeCaps?.allowWorkflows === false) { deps.onError?.(new Error(`self-orchestration DENIED for principal "${spec.principal ?? ""}" by runtimeCaps.allowWorkflows=false ` + `(per-principal entitlement governance, not a misconfiguration — the run_workflow tool is not mounted)`), { phase: "config", sessionId }); } let workflowToolsActive = false; let workflowSizeGuideline; if (selfOrchestrationActive && runnerSelf && deps.workflowScriptRunner && deps.workflowGovernanceBaseline) { workflowToolsActive = true; const currentSizeGuideline = () => resolveWorkflowSizeGuideline(deps.workflowLimits?.sizeGuideline).size; workflowSizeGuideline = { legGuideline: currentSizeGuideline(), current: currentSizeGuideline }; roster.mount(await createRunWorkflowTool({ runner: runnerSelf, scriptRunner: deps.workflowScriptRunner, governanceBaseline: deps.workflowGovernanceBaseline, parentToolCeiling: toolFaceSnapshot.selector.layers, parentDeferTools: toolFaceSnapshot.defer, parentAlwaysLoadTools: toolFaceSnapshot.alwaysLoad, ...(toolFaceSnapshot.restoreGated !== undefined ? { parentRestoreGatedTools: toolFaceSnapshot.restoreGated } : {}), parentPromptProfile: promptProfile, models: deps.models, agents: deps.agents, builtinAgents: deps.builtinAgents, store: deps.workflowRunStore, journalStore: deps.workflowJournalStore, ...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}), scriptStore: deps.workflowScriptStore, onAgentSpawn: deps.onWorkflowAgentSpawn, scope: taskScope, notifier: deps.workflowCompletionNotifier, originatingSessionId: sessionId, rootSessionId: internals?.rootSessionId ?? sessionId, ...(internals?.placementRoot !== undefined ? { placementRoot: internals.placementRoot } : {}), taskRegistry: defaultTaskRegistry, taskNotification: internals?.onTaskNotification, taskOwner: hostTaskId, limits: deps.workflowLimits, sourceTaskId: hostTaskId, parentModel: () => { const stated = deploymentSubagentTierModel(runnerSelf.agentCatalog); if (stated !== undefined) return stated; const ref = harnessRef.current?.getModelRef(); const model = inheritedParentModel(ref, runnerSelf.agentCatalog.models); return model === undefined ? undefined : { model, ...(typeof ref === "string" ? { modelRef: ref } : {}) }; }, ...(spec.getApiKeyAndHeaders !== undefined ? { parentGetApiKeyAndHeaders: spec.getApiKeyAndHeaders } : {}), ...(askSeat.value !== undefined ? { parentOnAsk: askSeat.value } : {}), ...(spec.durableApproval !== undefined ? { parentDurableApproval: { ...spec.durableApproval } } : {}), ...(internals?.workflowParkedResume !== undefined ? { parkedResume: (resumeFromRunId) => { const mine = internals.workflowParkedResume.filter((d) => d.runId === resumeFromRunId).map(({ runId: _r, ...rest }) => rest); return mine.length > 0 ? mine : undefined; } } : {}), parentSpecSeat, ...(internals?.questionFaceStripped === true ? { parentQuestionFaceStripped: true } : {}), ...(resolvedInteractionPosture !== undefined ? { parentInteractionPosture: resolvedInteractionPosture } : {}), parentMemoryCaptureState: () => { const memoryEngineSession = input.memoryEngineSession(); const ctl = memoryEngineSession?.engine.controlPlaneDir; const co = memoryEngineSession?.captureOptOut; return assembleParentCaptureState(co?.optedOut() ?? false, co?.indeterminate() ?? false, ctl, [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }]); }, autoModeReview: () => (autoModeDeciderRef.current !== undefined ? { decider: autoModeDeciderRef.current } : undefined), workflowDepth: internals?.workflowDepth, parentCwd: taskRootFinal, parentThinking: () => childThinkingSeat(undefined, deploymentSubagentThinking(runnerSelf.agentCatalog?.roles), harnessRef.current?.getThinkingLevel() ?? thinking).thinking, parentClamps: runClampSource, onNotice: deps.onNotice, ...(checkpointSeat !== undefined ? { parentCheckpointStore: checkpointSeat } : {}), parentCenterArtifactDigest: () => input.centerAdoption()?.artifact.artifactDigest, parentCenterSourceRevision: () => input.centerAdoption()?.sourceRevision, ...(forwardEvent ? { forwardEvent } : {}), inheritedGateForChildren, }), { source: "builtin", mountedBy: "workflow" }); } return { roster, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDeciderRef, autoModeDenialTrackingRef, autoModeArmingRef, armAutoMode, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline }; }