@sema-agent/core 7.44.0 → 7.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +172 -0
- package/dist/agents/subagent.js +33 -14
- package/dist/agents/tool-filter.d.ts +65 -4
- package/dist/agents/tool-filter.js +127 -4
- package/dist/core/agent-definition.d.ts +2 -2
- package/dist/core/allow-entry.d.ts +25 -1
- package/dist/core/allow-entry.js +29 -4
- package/dist/core/ask-origin.d.ts +23 -4
- package/dist/core/ask-origin.js +10 -4
- package/dist/core/checkpoint-store.d.ts +2 -2
- package/dist/core/decision-receipt.js +1 -1
- package/dist/core/effective-path-target.d.ts +7 -6
- package/dist/core/effective-path-target.js +3 -7
- package/dist/core/engine-notice.d.ts +20 -3
- package/dist/core/engine-notice.js +12 -6
- package/dist/core/gate-lanes.js +2 -0
- package/dist/core/gate-outcome.d.ts +5 -0
- package/dist/core/gate-outcome.js +2 -0
- package/dist/core/hooks.d.ts +2 -0
- package/dist/core/memory-engine/content-origin.d.ts +0 -34
- package/dist/core/memory-engine/content-origin.js +9 -4
- package/dist/core/memory-engine/layout.d.ts +5 -1
- package/dist/core/memory-engine/layout.js +1 -0
- package/dist/core/permission-rules.d.ts +4 -0
- package/dist/core/permission-rules.js +5 -2
- package/dist/core/real-approval.d.ts +9 -6
- package/dist/core/real-approval.js +5 -5
- package/dist/core/runner/child-tree.d.ts +13 -0
- package/dist/core/runner/child-tree.js +3 -0
- package/dist/core/runner/contracts.d.ts +8 -2
- package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
- package/dist/core/runner/prepare-ask-lane.js +2 -2
- package/dist/core/runner/prepare-config-doors.js +3 -1
- package/dist/core/runner/prepare-execution-env.js +2 -1
- package/dist/core/runner/prepare-gate-stations.d.ts +3 -0
- package/dist/core/runner/prepare-gate-stations.js +2 -1
- package/dist/core/runner/prepare-listings.d.ts +4 -4
- package/dist/core/runner/prepare-listings.js +2 -22
- package/dist/core/runner/prepare-park-ask.d.ts +2 -0
- package/dist/core/runner/prepare-park-ask.js +2 -2
- package/dist/core/runner/prepare-policy-chain.d.ts +2 -0
- package/dist/core/runner/prepare-policy-chain.js +4 -2
- package/dist/core/runner/prepare-task.js +10 -6
- package/dist/core/runner/resume-policy-outcome.js +2 -1
- package/dist/core/runner/run-leg.js +1 -1
- package/dist/core/runner/run-notification-lane.js +2 -2
- package/dist/core/runner/runtask.d.ts +2 -0
- package/dist/core/runner/runtask.js +2 -0
- package/dist/core/runner/stream-steer-verb.js +2 -2
- package/dist/core/runner/sub-agent-deny-lists.d.ts +32 -0
- package/dist/core/runner/sub-agent-deny-lists.js +121 -0
- package/dist/core/runner/synthetic-tools.d.ts +1 -1
- package/dist/core/runner/synthetic-tools.js +3 -2
- package/dist/core/session-policy-store.d.ts +6 -2
- package/dist/core/session-policy-store.js +2 -1
- package/dist/core/skill-grant.d.ts +10 -2
- package/dist/core/skill-grant.js +20 -4
- package/dist/core/task-notification.d.ts +12 -1
- package/dist/core/task-notification.js +13 -1
- package/dist/core/task-spec.d.ts +5 -3
- package/dist/core/tool-policy.d.ts +15 -4
- package/dist/core/tool-policy.js +9 -13
- package/dist/core/tool-spec.d.ts +20 -2
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.js +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +5 -3
- package/dist/orchestration/workflow.js +2 -1
- package/dist/tools/fs/removal-targets.d.ts +82 -0
- package/dist/tools/fs/removal-targets.js +236 -0
- package/dist/tools/fs/shell-read-write-targets.d.ts +27 -0
- package/dist/tools/fs/shell-read-write-targets.js +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +33 -1
- package/wire-changes/core@7.45.0.json +1084 -0
|
@@ -26,6 +26,7 @@ import { AllowEntryTable, screenAllowedToolsValue } from "../allow-entry.js";
|
|
|
26
26
|
import { compileOutputSchema } from "./strict-output-schema.js";
|
|
27
27
|
import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
|
|
28
28
|
import { foldAdmissionFreeze } from "../memory-admission.js";
|
|
29
|
+
import { deploymentCanRerootChild } from "./child-tree.js";
|
|
29
30
|
import { prepareMemory } from "./prepare-memory.js";
|
|
30
31
|
import { prepareMemoryEngineSession } from "./prepare-memory-engine-session.js";
|
|
31
32
|
import { prepareDeferClassify } from "./prepare-defer-classify.js";
|
|
@@ -363,9 +364,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
363
364
|
e.code = "isolation.unavailable";
|
|
364
365
|
throw e;
|
|
365
366
|
};
|
|
366
|
-
if (
|
|
367
|
+
if (!deploymentCanRerootChild(deps)) {
|
|
367
368
|
failIsolation("no executionEnvFactory is configured (a static/shared execution env cannot mint a per-agent worktree)");
|
|
368
369
|
}
|
|
370
|
+
else if (ownedEnvSeat.current === undefined) {
|
|
371
|
+
failIsolation("the executionEnvFactory returned no env instead of a worktree-rooted env");
|
|
372
|
+
}
|
|
369
373
|
else if (ownedEnvSeat.current === deps.executionEnv) {
|
|
370
374
|
ownedEnvSeat.current = undefined;
|
|
371
375
|
failIsolation("the executionEnvFactory returned the shared static env instead of a worktree-rooted env");
|
|
@@ -733,7 +737,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
733
737
|
releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
|
|
734
738
|
}
|
|
735
739
|
}
|
|
736
|
-
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
|
|
740
|
+
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, readFace: () => resolvedReadFace, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
|
|
737
741
|
const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtection, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, askSeat: doors.askSeat, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, shellReadStationArmed: handsReadFace.shellReadStationArmed, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, permissionMode, lockedPreflight, peerLaneRefs, frameSettlement, harness, pausedRef, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, fileHistoryFact, sessionBox });
|
|
738
742
|
const writeProtectionCheck = writeProtection?.check;
|
|
739
743
|
writeProtectionJudgeRef.current = writeProtection?.judgeTarget;
|
|
@@ -743,18 +747,18 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
743
747
|
return undefined;
|
|
744
748
|
});
|
|
745
749
|
}
|
|
746
|
-
const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking: autoModeDenialTrackingRef, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
|
|
750
|
+
const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, handsRoot: attachmentRootCanonical, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking: autoModeDenialTrackingRef, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
|
|
747
751
|
const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, skillGrantCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
|
|
748
752
|
const { suspendForResource, suspendForPlatformLimit, suspendForReview } = prepareBoundaryParks({ saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
|
|
749
|
-
const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, grantBindingOf, now, humanReviewRef, abortController, harness, pausedRef });
|
|
753
|
+
const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, handsRoot: attachmentRootCanonical, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, grantBindingOf, now, humanReviewRef, abortController, harness, pausedRef });
|
|
750
754
|
const seatInputsFor = skillGateWiring({ tools, skillToolName: SKILL_TOOL_NAME, seatOf: skillSeatOf, mode: () => permissionMode.current, lane: skillGrantLane, onAllowed: (info) => emitTrace(deps.tracer, () => ({ kind: "permission.skill_allowed", version: 1, taskId: hostTaskId, ...info, entries: [...info.entries], ts: Date.now() })), allowEntries: { deploymentAllows: { ...allowEntryLanes.deployment, onAllowed: (info) => emitTrace(deps.tracer, () => ({ kind: "permission.allowed_tools_allowed", version: 1, taskId: hostTaskId, source: "deployment", ...info, entries: [...info.entries], ts: Date.now() })) }, requestAllows: { ...allowEntryLanes.request, onAllowed: (info) => emitTrace(deps.tracer, () => ({ kind: "permission.allowed_tools_allowed", version: 1, taskId: hostTaskId, source: "request", ...info, entries: [...info.entries], ts: Date.now() })) } } });
|
|
751
|
-
const { approvedCallScreen } = prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, readDenyInForce: () => ({ tiers: handsReadFace.readDenyBuiltinTiersActive, rows: [...readDenyAdditionsNormalized.map((e) => e.pattern), ...handsReadFaceInput.pathDenyRules.filter((r) => r.faces.includes("read")).map((r) => r.rule)] }), hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider: autoModeDeciderRef, autoModeDenialTracking: autoModeDenialTrackingRef, askRoute: { ...wiringManifest.ask, effective: askSeatEffective(wiringManifest) }, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, seatInputsFor, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
|
|
755
|
+
const { approvedCallScreen } = prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, readDenyInForce: () => ({ tiers: handsReadFace.readDenyBuiltinTiersActive, rows: [...readDenyAdditionsNormalized.map((e) => e.pattern), ...handsReadFaceInput.pathDenyRules.filter((r) => r.faces.includes("read")).map((r) => r.rule)] }), hookEnvFace, writeProtectionCheck, shellReadCheck, readFace: () => resolvedReadFace, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider: autoModeDeciderRef, autoModeDenialTracking: autoModeDenialTrackingRef, askRoute: { ...wiringManifest.ask, effective: askSeatEffective(wiringManifest) }, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, seatInputsFor, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
|
|
752
756
|
const postureFrameRef = { report: (err) => deps.onError?.(err, { phase: "degraded", sessionId }) };
|
|
753
757
|
const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, toolRosterDeltas: rosterSeat, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
|
|
754
758
|
const preparedHolder = {};
|
|
755
759
|
const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, skillGrants: skillGrantLane, callerPolicy: lockedPreflight.toolPolicy, inheritedPolicies: (inheritedParentConstraints ?? []).map((c) => c.policy), persistedRuleLane: permissionRuleLane, orgRuleLane: permissionRuleOrgLane, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
|
|
756
760
|
await onceLedger.settle(session, announcedListingsRef);
|
|
757
|
-
const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: askSeatEffective(wiringManifest) }, ...(outputStyleFrame !== undefined ? { outputStyleFrame } : {}), permissionMode, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, skillGrants: skillGrantLane, skillSpecs, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, endModeCeiling, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, stopForDenialLimit, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, frameSettlement, ...(peerLaneRefs.boundaryRound !== undefined ? { peerBoundaryRound: peerLaneRefs.boundaryRound } : {}), gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}), ...(approvedCallScreen !== undefined ? { approvedCallScreen } : {}) });
|
|
761
|
+
const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: askSeatEffective(wiringManifest) }, ...(outputStyleFrame !== undefined ? { outputStyleFrame } : {}), permissionMode, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(attachmentRootCanonical !== undefined ? { handsRoot: attachmentRootCanonical } : {}), toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, skillGrants: skillGrantLane, skillSpecs, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, endModeCeiling, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, stopForDenialLimit, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, frameSettlement, ...(peerLaneRefs.boundaryRound !== undefined ? { peerBoundaryRound: peerLaneRefs.boundaryRound } : {}), gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}), ...(approvedCallScreen !== undefined ? { approvedCallScreen } : {}) });
|
|
758
762
|
rollback.commit();
|
|
759
763
|
commitModeSeats(abortController.signal);
|
|
760
764
|
const prepared = buildPrepared();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { canonicalizeCapturedPlainData, classifyQuestionOutcome, isLiveQuestionFace } from "../ask-question.js";
|
|
2
|
+
import { isHostDecider } from "../gate-outcome.js";
|
|
2
3
|
import { CHECKPOINT_FEATURES, CheckpointError, checkpointVersionOf, winnerFromOutcome } from "../checkpoint-store.js";
|
|
3
4
|
import { policyCannotAnswer, screenApproverAttribution } from "../tool-policy.js";
|
|
4
5
|
import { sanitizeUntrustedText } from "../untrusted-text.js";
|
|
@@ -52,7 +53,7 @@ export function resumePolicyOutcome(input) {
|
|
|
52
53
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries no hostDecision — a policy_ask decide names who decided it ({ decidedBy: "person" | "sla_timeout" | "policy", approver? }); refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
53
54
|
}
|
|
54
55
|
const decidedBy = hostDecisionRaw.decidedBy;
|
|
55
|
-
if (decidedBy
|
|
56
|
+
if (!isHostDecider(decidedBy)) {
|
|
56
57
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume hostDecision.decidedBy "${describeSuppliedValue(decidedBy)}" is outside the closed set — it is exactly "person", "sla_timeout" or "policy"; refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
57
58
|
}
|
|
58
59
|
const cannotAllow = decision !== "allow"
|
|
@@ -614,7 +614,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
614
614
|
}
|
|
615
615
|
const executedArgs = humanRewrite !== undefined ? resolvedArgs : screened !== undefined ? screened.args : resolvedArgs;
|
|
616
616
|
const unseen = !deepJsonEqual(executedArgs, pendingAction.args);
|
|
617
|
-
const beltReq = () => ({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)), budget: beltBudget, ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}), ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs) });
|
|
617
|
+
const beltReq = () => ({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)), budget: beltBudget, ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}), ...(prepared.handsRoot !== undefined ? { root: prepared.handsRoot } : {}), ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs) });
|
|
618
618
|
const unjudgeable = unseen ? chainCannotJudge(resume.cp.state.inheritedGate) : undefined;
|
|
619
619
|
if (unjudgeable !== undefined) {
|
|
620
620
|
const chainDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its input changed after the approval and the inherited ancestor chain cannot judge the changed input (${unjudgeable}). This approval is spent — the call has to be re-issued.`, prepared.reminderMark);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createPeerInboundChainRef, createPeerSelfRef } from "../../agents/peer-admission.js";
|
|
2
2
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
3
|
-
import { createLiveNotificationLane, DurableAppendObligations, isDelegatedAgentTerminal, isTerminalTaskNotification, renderTaskNotificationXml, routeOrParkNotification, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
3
|
+
import { createLiveNotificationLane, DurableAppendObligations, injectionLaneOf, isDelegatedAgentTerminal, isTerminalTaskNotification, renderTaskNotificationXml, routeOrParkNotification, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
4
4
|
import { ToolDetachHub } from "../tool-detach.js";
|
|
5
5
|
import { pendingContentAskCallId } from "./decide-continuation.js";
|
|
6
6
|
export function runNotificationLane(input) {
|
|
@@ -76,7 +76,7 @@ async function runNotificationLaneAs(input, obligations) {
|
|
|
76
76
|
engineNoteKey: taskNotificationDedupKey(item.payload),
|
|
77
77
|
...(item.payload.task_type !== "external" && isTerminalTaskNotification(item.payload) ? { capPreferred: true } : {}),
|
|
78
78
|
};
|
|
79
|
-
const deliver = item.priority === "
|
|
79
|
+
const deliver = injectionLaneOf(item.priority) === "followUp"
|
|
80
80
|
? notificationHarness.followUp(xml, noteOptions)
|
|
81
81
|
: notificationHarness.steer(xml, { ...noteOptions, ...(item.priority === "now" ? { immediate: true } : {}) });
|
|
82
82
|
void deliver.then(() => item.onDisposition?.("queued"), () => {
|
|
@@ -68,6 +68,8 @@ export declare class Runner implements RunnerSelfSeat {
|
|
|
68
68
|
* thin compositions OVER the Runner (`runGoal`) can emit at their terminal state without reaching
|
|
69
69
|
* into private deps. */
|
|
70
70
|
emitTaskOutcome(outcome: TaskOutcome): void;
|
|
71
|
+
/** Whether a child this Runner runs can be placed in another tree: {@link RunnerSelfSeat.canRerootChild}. */
|
|
72
|
+
canRerootChild(): boolean;
|
|
71
73
|
/** The deployment agent catalog (RunnerDeps.agents/builtinAgents), exposed READ-ONLY
|
|
72
74
|
* so the Agent-tool lane (`createSubagentTool`) defaults to the SAME registry the workflow lane
|
|
73
75
|
* consumes (single source; the types.ts "pass the same array" follow-on made structural). The array
|
|
@@ -62,6 +62,7 @@ import { runCompactionMachinery } from "./run-compaction-machinery.js";
|
|
|
62
62
|
import { runStopAndFinalVerify } from "./run-stop-and-final-verify.js";
|
|
63
63
|
import { runRecoveryLanes } from "./run-recovery-lanes.js";
|
|
64
64
|
import { runGitLane } from "./run-git-lane.js";
|
|
65
|
+
import { deploymentCanRerootChild } from "./child-tree.js";
|
|
65
66
|
import { thrownText } from "../thrown-value.js";
|
|
66
67
|
export class Runner {
|
|
67
68
|
deps;
|
|
@@ -125,6 +126,7 @@ export class Runner {
|
|
|
125
126
|
emitTaskOutcome(outcome) {
|
|
126
127
|
emitTaskOutcome(this.deps, outcome);
|
|
127
128
|
}
|
|
129
|
+
canRerootChild() { return deploymentCanRerootChild(this.deps); }
|
|
128
130
|
get agentCatalog() {
|
|
129
131
|
return {
|
|
130
132
|
...(this.deps.agents ? { agents: [...this.deps.agents] } : {}),
|
|
@@ -3,7 +3,7 @@ import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
|
3
3
|
import { MAX_STEER_INPUT_ID_CHARS } from "../checkpoint-store.js";
|
|
4
4
|
import { formatHookFeedback, hookSeatExpiredError, runHookSeat } from "../hooks.js";
|
|
5
5
|
import { buildHumanInputEvent, frameMidTurnUserInput, projectHumanInput } from "../human-input-projection.js";
|
|
6
|
-
import { isSystemInjectionPriority, steerPriorityOf, SYSTEM_INJECTION_PRIORITIES } from "../task-notification.js";
|
|
6
|
+
import { injectionLaneOf, isSystemInjectionPriority, steerPriorityOf, SYSTEM_INJECTION_PRIORITIES } from "../task-notification.js";
|
|
7
7
|
import { deliverEngineNotice } from "../types.js";
|
|
8
8
|
import { inlineUntrusted, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
9
9
|
import { announceAcceptance, armAcceptance, nextHumanInputSeq } from "./steer-admission.js";
|
|
@@ -133,7 +133,7 @@ export function streamSteerVerb(input) {
|
|
|
133
133
|
const noteOptions = { provenance: "engine-note", callerAuthored: true, parkRecord, ...(actor !== undefined ? { actor } : {}) };
|
|
134
134
|
if (mintsAFrame)
|
|
135
135
|
armAcceptance(parkRecord, () => publishQueued(h));
|
|
136
|
-
if (priority === "
|
|
136
|
+
if (injectionLaneOf(priority) === "followUp") {
|
|
137
137
|
await h.harness.followUp(payload, noteOptions);
|
|
138
138
|
noteAccepted();
|
|
139
139
|
return;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sub-agent tool lists of one prepared task: the allow advisory for `TaskSpec.agents` and THE judgment of every denied-tools
|
|
3
|
+
* list a deployment writes — the task's own definitions (`TaskSpec.agents`), the definitions a mounted delegation tool carries
|
|
4
|
+
* (the `authored` faces — the engine's built-ins name tools of other vocabularies and are not judged) and its tool-level list.
|
|
5
|
+
* Universe = the task's assembled roster plus every mounted delegation tool's pool, names and aliases; a pool the engine cannot
|
|
6
|
+
* list (an `extraTools` factory) turns the refusals that need a roster into host notices. A tool's own lists are judged where
|
|
7
|
+
* the tool is mounted: only a tool a deployment mounted (depth 0) publishes them (`authored`, `agentToolLevelAllowTools`, `agentToolLevelDenyTools`), the
|
|
8
|
+
* engine's nested copy for a delegated child carries neither.
|
|
9
|
+
*
|
|
10
|
+
* Throws `config.agent.unknown_tool` (`refused`, the one site) — synchronous, like the phase that calls it.
|
|
11
|
+
*/
|
|
12
|
+
import type { AgentTool } from "../../internal/harness.js";
|
|
13
|
+
import type { TaskSpec } from "../types.js";
|
|
14
|
+
import type { AnnounceOnceSink } from "./contracts.js";
|
|
15
|
+
/**
|
|
16
|
+
* The names a definition's denied-tools entries stand for, for a lane that has neither a roster nor a notice sink at the point
|
|
17
|
+
* it builds its own policy (a workflow leg's agentType): each entry is read by the one reading, so `Tool(content)` denies the
|
|
18
|
+
* whole tool there too, and an entry that names no tool is refused by the same refusal. The stale-name and told dispositions
|
|
19
|
+
* need the leg's roster and a sink; the leg's own policy-name audit is what reads them.
|
|
20
|
+
*/
|
|
21
|
+
export declare function deniedToolNamesOf(where: string, value: unknown): string[];
|
|
22
|
+
export interface SubAgentToolListsInput {
|
|
23
|
+
/** borrowed-readonly — the REBOUND spec: `agents` (the task's definitions) and `tools` (the mounted delegation tools). */
|
|
24
|
+
spec: Pick<TaskSpec, "agents" | "tools">;
|
|
25
|
+
/** borrowed-readonly — the FULLY ASSEMBLED mount array (names + aliases = the roster half of the universe). */
|
|
26
|
+
tools: ReadonlyArray<AgentTool>;
|
|
27
|
+
/** borrowed-readonly — the once-per-session ledger whose operator sink carries the notices. */
|
|
28
|
+
onceLedger: Pick<AnnounceOnceSink, "onError">;
|
|
29
|
+
/** borrowed-readonly — the acquired session's id (operator-line context). */
|
|
30
|
+
sessionId: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function auditSubAgentToolLists(input: SubAgentToolListsInput): void;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { notAListSentence, notAStringSentence, readDeniedEntry, readToolNameList, valueTypeLabel } from "../../agents/tool-filter.js";
|
|
2
|
+
import { compileAllowList, inertEntryPhrase, renderListEntry } from "../allow-entry.js";
|
|
3
|
+
import { inlineUntrusted } from "../untrusted-text.js";
|
|
4
|
+
const DENY_LIST_NAMES_ONLY = "To block by path or command, write a deny rule in the caller's permission policy: a denied-tools list only takes tool names (it narrows which tools the agent is handed).";
|
|
5
|
+
function refused(message) {
|
|
6
|
+
const e = new Error(message);
|
|
7
|
+
e.code = "config.agent.unknown_tool";
|
|
8
|
+
return e;
|
|
9
|
+
}
|
|
10
|
+
function refusal(where, flaw) {
|
|
11
|
+
return refused(`${where} declares tool ${inertEntryPhrase(flaw)} in its denied tools, but no tool of that spelling exists in this deployment — fix the entry or mount the tool.${flaw.reason === "grammar" ? ` ${DENY_LIST_NAMES_ONLY}` : ""}`);
|
|
12
|
+
}
|
|
13
|
+
function refusalNotAString(where, side, index, type) {
|
|
14
|
+
return refused(notAStringSentence(where, side, index, type));
|
|
15
|
+
}
|
|
16
|
+
function refusalNotAList(where, side, list) {
|
|
17
|
+
return refused(notAListSentence(where, side, list));
|
|
18
|
+
}
|
|
19
|
+
export function deniedToolNamesOf(where, value) {
|
|
20
|
+
const list = readToolNameList(value);
|
|
21
|
+
if (list.kind !== "entries")
|
|
22
|
+
throw refusalNotAList(where, "denied", list);
|
|
23
|
+
return list.entries.map((entry, index) => {
|
|
24
|
+
if (entry === "*")
|
|
25
|
+
return entry;
|
|
26
|
+
const read = readDeniedEntry(entry);
|
|
27
|
+
if (read.kind === "not_a_string")
|
|
28
|
+
throw refusalNotAString(where, "denied", index, read.type);
|
|
29
|
+
const flaw = compileAllowList([read.name], "deny").inert[0];
|
|
30
|
+
if (flaw !== undefined && flaw.reason === "grammar")
|
|
31
|
+
throw refusal(where, { ...flaw, entry: read.name });
|
|
32
|
+
return read.name;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function judgeDeniedToolNames(where, value, universe, advise) {
|
|
36
|
+
if (value === undefined || value === null)
|
|
37
|
+
return;
|
|
38
|
+
const list = readToolNameList(value);
|
|
39
|
+
if (list.kind !== "entries")
|
|
40
|
+
throw refusalNotAList(where, "denied", list);
|
|
41
|
+
let index = -1;
|
|
42
|
+
for (const entry of list.entries) {
|
|
43
|
+
index += 1;
|
|
44
|
+
if (entry === "*")
|
|
45
|
+
continue;
|
|
46
|
+
const read = readDeniedEntry(entry);
|
|
47
|
+
if (read.kind === "not_a_string")
|
|
48
|
+
throw refusalNotAString(where, "denied", index, read.type);
|
|
49
|
+
const { name, content } = read;
|
|
50
|
+
const raw = entry;
|
|
51
|
+
const { rows, inert } = compileAllowList([name], "deny", universe.known);
|
|
52
|
+
const flaw = inert[0] === undefined ? undefined : { ...inert[0], entry: raw };
|
|
53
|
+
const stale = flaw !== undefined && (flaw.reason === "tool_name" || flaw.reason === "near_miss");
|
|
54
|
+
if (flaw !== undefined && (flaw.reason === "grammar" || (stale && universe.complete)))
|
|
55
|
+
throw refusal(where, flaw);
|
|
56
|
+
if (flaw === undefined && universe.known.has(name)) {
|
|
57
|
+
if (content)
|
|
58
|
+
advise(new Error(`${where} declares ${renderListEntry(raw)} in its denied tools: this list reads tool names, not content, so the whole tool "${inlineUntrusted(name, 80)}" is denied to the agent. ${DENY_LIST_NAMES_ONLY}`));
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
advise(new Error(`${where} declares tool ${flaw !== undefined ? inertEntryPhrase(flaw) : renderListEntry(raw)} in its denied tools, a name this task's roster and delegation pools do not carry — the entry hides nothing here. Advisory only: a list shared across deployments is not wrong because one lacks a tool, but a misspelling guards nothing.` +
|
|
62
|
+
(rows[0]?.compiled.kind === "namespace" ? " This list matches tool names exactly: a server-level entry hides none of that server's tools; name them, or write a deny rule in the caller's permission policy." : "") +
|
|
63
|
+
(universe.complete ? "" : " A delegation tool this task mounts injects tools the engine cannot enumerate (extraTools), so the name cannot be checked here.")));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function judgeAllowedToolList(where, value, known, advise) {
|
|
67
|
+
if (value === undefined || value === null)
|
|
68
|
+
return;
|
|
69
|
+
const list = readToolNameList(value);
|
|
70
|
+
if (list.kind !== "entries")
|
|
71
|
+
throw refusalNotAList(where, "allowed", list);
|
|
72
|
+
const unknownNames = [];
|
|
73
|
+
let index = -1;
|
|
74
|
+
for (const entry of list.entries) {
|
|
75
|
+
index += 1;
|
|
76
|
+
if (typeof entry !== "string")
|
|
77
|
+
throw refusalNotAString(where, "allowed", index, valueTypeLabel(entry));
|
|
78
|
+
if (known !== undefined && entry !== "*" && !known.has(entry))
|
|
79
|
+
unknownNames.push(entry);
|
|
80
|
+
}
|
|
81
|
+
if (unknownNames.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
advise(new Error(`${where} allows tool(s) ${unknownNames.map(renderListEntry).join(", ")} not present in this task's assembled roster — likely a typo (the entry would be item-filtered at spawn; the agent stays usable). Advisory only: the delegation pool can differ from this roster, so a tool mounted only on the delegation tool or produced by per-spawn extraTools makes this spurious.`));
|
|
84
|
+
}
|
|
85
|
+
export function auditSubAgentToolLists(input) {
|
|
86
|
+
const { spec, tools, onceLedger, sessionId } = input;
|
|
87
|
+
const advise = (err) => {
|
|
88
|
+
try {
|
|
89
|
+
onceLedger.onError(err, { phase: "config", sessionId });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const delegationTools = (spec.tools ?? []).filter((t) => t.agentToolFaces !== undefined);
|
|
95
|
+
const taskAgents = spec.agents ?? [];
|
|
96
|
+
if (taskAgents.length === 0 && delegationTools.length === 0)
|
|
97
|
+
return;
|
|
98
|
+
const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
99
|
+
const denyKnown = new Set(known);
|
|
100
|
+
for (const t of delegationTools)
|
|
101
|
+
for (const p of t.agentToolPool ?? [])
|
|
102
|
+
for (const n of [p.name, ...(p.aliases ?? [])])
|
|
103
|
+
denyKnown.add(n);
|
|
104
|
+
const universe = { known: denyKnown, complete: delegationTools.every((t) => t.agentToolPool !== undefined) };
|
|
105
|
+
for (const def of taskAgents) {
|
|
106
|
+
judgeAllowedToolList(`TaskSpec.agents: agent "${def.name}"`, def.allowTools, known, advise);
|
|
107
|
+
judgeDeniedToolNames(`TaskSpec.agents: agent "${def.name}"`, def.denyTools, universe, advise);
|
|
108
|
+
}
|
|
109
|
+
const replaced = new Set(taskAgents.map((d) => d.name));
|
|
110
|
+
for (const t of delegationTools) {
|
|
111
|
+
for (const face of t.agentToolFaces ?? []) {
|
|
112
|
+
if (face.authored === true && !replaced.has(face.name)) {
|
|
113
|
+
const faceWhere = `delegation tool "${t.name}": agent definition "${face.name}"`;
|
|
114
|
+
judgeAllowedToolList(faceWhere, face.allowTools, undefined, advise);
|
|
115
|
+
judgeDeniedToolNames(faceWhere, face.denyTools, universe, advise);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
judgeAllowedToolList(`delegation tool "${t.name}": the tool-level list`, t.agentToolLevelAllowTools, undefined, advise);
|
|
119
|
+
judgeDeniedToolNames(`delegation tool "${t.name}": the tool-level list`, t.agentToolLevelDenyTools, universe, advise);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -110,7 +110,7 @@ export interface SkillListingEntry {
|
|
|
110
110
|
* and the tool, so what the prompt promises is exactly what the tool serves. Each field is READ ONCE, BY NAME, and
|
|
111
111
|
* both consumers see the copy: the gate screens this list while the invoke substitutes from it, so a `get baseDir()`
|
|
112
112
|
* answering those two reads differently passed a screen that measured a value the model never received — and a rest
|
|
113
|
-
* copy would silently drop an INHERITED or non-enumerable list
|
|
113
|
+
* copy would silently drop an INHERITED or non-enumerable list. The two tables are read by {@link readSkillTable}. */
|
|
114
114
|
export declare function normalizeSkills(skills: SkillLike[] | undefined): SkillLike[];
|
|
115
115
|
/** [c209-C] The skills listing's lead line — single-sourced so the run loop's durable-resume
|
|
116
116
|
* transcript probe (MED-3② skills arm, runtask) matches EXACTLY what the initial frame carries
|
|
@@ -2,7 +2,7 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { defineTool } from "../tools.js";
|
|
3
3
|
import { sanitizeUntrustedText, SHELLED_BODY_ENVELOPE_TAGS } from "../untrusted-text.js";
|
|
4
4
|
import { SKILL_LISTING_DEFAULT_BUDGET, SKILL_LISTING_DESC_MAX_CHARS_DEFAULT } from "../skill-listing-budget.js";
|
|
5
|
-
import { skillNamedBy } from "../skill-grant.js";
|
|
5
|
+
import { readSkillTable, skillNamedBy } from "../skill-grant.js";
|
|
6
6
|
import { toolFace } from "../tool-catalog-entries.js";
|
|
7
7
|
import { substituteSkillRoot, substituteSkillRootBounded } from "./skill-base-dir.js";
|
|
8
8
|
import { foldListingWithin } from "./listing-fold.js";
|
|
@@ -116,7 +116,8 @@ export function normalizeSkills(skills) {
|
|
|
116
116
|
seen.add(s.name);
|
|
117
117
|
const { name, description, content, allowedTools, disallowedTools, files, baseDir, ...rest } = s;
|
|
118
118
|
void rest;
|
|
119
|
-
|
|
119
|
+
const [allowed, denied] = [readSkillTable(allowedTools), readSkillTable(disallowedTools)];
|
|
120
|
+
out.push({ name, description, content, ...(allowed !== undefined ? { allowedTools: allowed } : {}), ...(denied !== undefined ? { disallowedTools: denied } : {}), ...(files !== undefined ? { files } : {}), ...(baseDir !== undefined ? { baseDir } : {}) });
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
return out.toSorted((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
@@ -52,12 +52,16 @@ export interface PutRulesOptions {
|
|
|
52
52
|
* this from a verified, role-checked principal (mirrors `steer.trusted`). */
|
|
53
53
|
operator?: boolean;
|
|
54
54
|
}
|
|
55
|
-
/** The {@link SessionPolicyError.detail} of a tool-name refusal: which bucket, which entry, the judgment's word,
|
|
56
|
-
* spelling to write instead when the judgment has one (a retired name's 1:1
|
|
55
|
+
/** The {@link SessionPolicyError.detail} of a tool-name refusal: which bucket, which entry, the judgment's word, the judgment's
|
|
56
|
+
* one-line sentence when it has one, and the spelling to write instead when the judgment has one (a retired name's 1:1
|
|
57
|
+
* successor, an unmintable segment's minted name). */
|
|
57
58
|
export interface SessionRuleNameRefusal {
|
|
58
59
|
field: SessionRuleToolNameField;
|
|
59
60
|
name: string;
|
|
60
61
|
verdict: Exclude<RuleToolNameVerdict["kind"], "current">;
|
|
62
|
+
/** The `guidance` of the judgment ({@link judgeRuleToolName}), byte for byte — the same string the refusal's sentence quotes. Present for
|
|
63
|
+
* `retired` and `unmintable_segment`, the two verdicts that carry one; absent for `unprefixed_namespace`, whose judgment has no sentence of its own. */
|
|
64
|
+
guidance?: string;
|
|
61
65
|
currentName?: string;
|
|
62
66
|
}
|
|
63
67
|
/** Typed error from {@link SessionPolicyStore.putRules}. `conflict` = stale `expectedRev`; `loosen_forbidden`
|
|
@@ -41,6 +41,7 @@ export function assertSessionRuleNamesCurrent(rules, mountedToolNames = NO_DECLA
|
|
|
41
41
|
continue;
|
|
42
42
|
const quoted = JSON.stringify(name);
|
|
43
43
|
const successor = verdict.kind === "unprefixed_namespace" ? undefined : verdict.currentName;
|
|
44
|
+
const guidance = verdict.kind === "unprefixed_namespace" ? undefined : verdict.guidance;
|
|
44
45
|
let message;
|
|
45
46
|
switch (verdict.kind) {
|
|
46
47
|
case "retired":
|
|
@@ -57,7 +58,7 @@ export function assertSessionRuleNamesCurrent(rules, mountedToolNames = NO_DECLA
|
|
|
57
58
|
return exhaustive;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
|
-
throw new SessionPolicyError("legacy_tool_name", message, { field, name, verdict: verdict.kind, ...(successor !== undefined ? { currentName: successor } : {}) });
|
|
61
|
+
throw new SessionPolicyError("legacy_tool_name", message, { field, name, verdict: verdict.kind, ...(guidance !== undefined ? { guidance } : {}), ...(successor !== undefined ? { currentName: successor } : {}) });
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
}
|
|
@@ -76,8 +76,16 @@ export declare function parkedSkillApprovalRefusal(row: {
|
|
|
76
76
|
readonly args: unknown;
|
|
77
77
|
readonly boundGrantDigest?: string;
|
|
78
78
|
}, args: unknown, skills: readonly SkillGrantSource[], skillToolName: string, site?: "claim" | "execution"): string | undefined;
|
|
79
|
-
/**
|
|
80
|
-
*
|
|
79
|
+
/**
|
|
80
|
+
* A skill's table (`allowed-tools` / `disallowed-tools`) READ ONCE, the one reading every later reader takes instead of the host's
|
|
81
|
+
* value: absent (`undefined`, or `null` — a frontmatter's empty key) is no table; a list is its dense copy (by index, no iterator
|
|
82
|
+
* of the host's runs; a member that is not a string stays as it is); a value that is not a list, or a list whose read throws, is
|
|
83
|
+
* ONE entry that reads as nothing ({@link nonListValueLabel}), so each table applies the law it already has to an entry that reads
|
|
84
|
+
* as nothing — a deny table keeps its Skill from activating, an allow table is announced and grants nothing. A string is not spread.
|
|
85
|
+
*/
|
|
86
|
+
export declare function readSkillTable(value: unknown): string[] | undefined;
|
|
87
|
+
/** A resume's skills read ONCE: each definition copied with its two tables ({@link readSkillTable}), so the parked Skill approval's
|
|
88
|
+
* binding and the resumed leg's prepare read one value — a later edit to the caller's own objects or arrays reaches neither. */
|
|
81
89
|
export declare function detachSkillLists<T extends SkillGrantSource>(skills: readonly T[]): T[];
|
|
82
90
|
/** Does this skill declare a list (either, non-empty)? The one test of "the Skill card is asked for this skill". */
|
|
83
91
|
export declare function declaresSkillLists(skill: Pick<SkillGrantSource, "allowedTools" | "disallowedTools">): boolean;
|
package/dist/core/skill-grant.js
CHANGED
|
@@ -3,7 +3,8 @@ import { parsePermissionRule } from "./permission-rules.js";
|
|
|
3
3
|
import { mountedToolPermissionSeat } from "./mode-policy.js";
|
|
4
4
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
5
5
|
import { boundInputHashOf } from "./canonical-json.js";
|
|
6
|
-
import {
|
|
6
|
+
import { readToolNameList } from "../agents/tool-filter.js";
|
|
7
|
+
import { allowingRows, autoStripped, compileAllowEntry, compileAllowList, inertEntryPhrase, judgeAllowEntries, nonListValueLabel, nonStringMemberLabel, renderListEntry } from "./allow-entry.js";
|
|
7
8
|
export const SKILL_GRANT_ORIGINS = Object.freeze(["model", "user", "inherited"]);
|
|
8
9
|
const FROZEN_SKILL_GRANT_ORIGINS = new Set(["model", "inherited"]);
|
|
9
10
|
export function skillNamedBy(args, skills) {
|
|
@@ -12,6 +13,8 @@ export function skillNamedBy(args, skills) {
|
|
|
12
13
|
}
|
|
13
14
|
function skillGrantBinding(skill) {
|
|
14
15
|
const canonical = (entries) => [...new Set((entries ?? []).map((e) => {
|
|
16
|
+
if (typeof e !== "string")
|
|
17
|
+
return nonStringMemberLabel(e);
|
|
15
18
|
const p = parsePermissionRule(e);
|
|
16
19
|
return p.ruleContent === undefined ? p.toolName : `${p.toolName}(${p.ruleContent})`;
|
|
17
20
|
}))].sort();
|
|
@@ -44,8 +47,21 @@ export function parkedSkillApprovalRefusal(row, args, skills, skillToolName, sit
|
|
|
44
47
|
? `${why} — refusing pre-CAS (nothing ran; the checkpoint stays pending). Decide this checkpoint as deny and re-activate it: the new card shows the current lists.`
|
|
45
48
|
: `The approved Skill call was not executed: ${why}. This approval is spent — to use "${chosen}", have the model activate it again: the new card shows its lists.`;
|
|
46
49
|
}
|
|
50
|
+
export function readSkillTable(value) {
|
|
51
|
+
if (value === undefined || value === null)
|
|
52
|
+
return undefined;
|
|
53
|
+
const read = readToolNameList(value);
|
|
54
|
+
return read.kind === "entries" ? read.entries : [nonListValueLabel(read)];
|
|
55
|
+
}
|
|
47
56
|
export function detachSkillLists(skills) {
|
|
48
|
-
return skills.map((s) =>
|
|
57
|
+
return skills.map((s) => {
|
|
58
|
+
if (typeof s !== "object" || s === null)
|
|
59
|
+
return s;
|
|
60
|
+
const { allowedTools: rawAllowed, disallowedTools: rawDenied, ...rest } = s;
|
|
61
|
+
const allowedTools = readSkillTable(rawAllowed);
|
|
62
|
+
const disallowedTools = readSkillTable(rawDenied);
|
|
63
|
+
return { ...rest, ...(allowedTools !== undefined ? { allowedTools } : {}), ...(disallowedTools !== undefined ? { disallowedTools } : {}) };
|
|
64
|
+
});
|
|
49
65
|
}
|
|
50
66
|
export function declaresSkillLists(skill) {
|
|
51
67
|
return (skill.allowedTools?.length ?? 0) > 0 || (skill.disallowedTools?.length ?? 0) > 0;
|
|
@@ -74,7 +90,7 @@ export function skillDenyDecision(hit) {
|
|
|
74
90
|
action: "deny",
|
|
75
91
|
message: hit.readsAsNothing !== undefined
|
|
76
92
|
? skillDenyUnreadable(hit.skill, hit.readsAsNothing, "activation").message
|
|
77
|
-
: `the active skill "${inlineUntrusted(hit.skill, 120)}" lists this tool in disallowed-tools (${hit.entries.map(
|
|
93
|
+
: `the active skill "${inlineUntrusted(hit.skill, 120)}" lists this tool in disallowed-tools (${hit.entries.map(renderListEntry).join(", ")}) — refused for the rest of this run`,
|
|
78
94
|
decisionReason: "skill",
|
|
79
95
|
};
|
|
80
96
|
}
|
|
@@ -88,7 +104,7 @@ export function skillRowRefusal(lane, req) {
|
|
|
88
104
|
export function skillAllowDecision(hit, updatedInput) {
|
|
89
105
|
return {
|
|
90
106
|
action: "allow",
|
|
91
|
-
message: `the active skill "${inlineUntrusted(hit.skill, 120)}" lists this tool in allowed-tools (${hit.entries.map(
|
|
107
|
+
message: `the active skill "${inlineUntrusted(hit.skill, 120)}" lists this tool in allowed-tools (${hit.entries.map(renderListEntry).join(", ")})`,
|
|
92
108
|
decisionReason: "skill",
|
|
93
109
|
...(updatedInput !== undefined ? { updatedInput } : {}),
|
|
94
110
|
};
|
|
@@ -32,10 +32,21 @@ export declare const SYSTEM_INJECTION_PRIORITIES: readonly SystemInjectionPriori
|
|
|
32
32
|
export declare function isSystemInjectionPriority(value: unknown): value is SystemInjectionPriority;
|
|
33
33
|
/** The ONE reading of a steer's priority: a parked row or an accepted input that carries none means `"next"`
|
|
34
34
|
* (the default the live admission has always applied) — so a retry that spells the default and a row parked
|
|
35
|
-
* without it are the same instruction, whichever side normalized.
|
|
35
|
+
* without it are the same instruction, whichever side normalized. A tier outside the closed set throws
|
|
36
|
+
* (`steering.invalid_content`, the sentence names the set); `null` is such a tier, not an absent one. */
|
|
36
37
|
export declare function steerPriorityOf(x: {
|
|
37
38
|
readonly priority?: SystemInjectionPriority;
|
|
38
39
|
}): SystemInjectionPriority;
|
|
40
|
+
/** The two queues a person's input rides on a live run: the `steer` lane (the running turn's next boundary) and the
|
|
41
|
+
* `followUp` lane (the run's would-otherwise-stop seat). They are also the two families of the stranded-input account — an
|
|
42
|
+
* input's account is the one of the lane it was queued on (`USER_INPUT_UNDRAINED_FAMILIES`). */
|
|
43
|
+
export type UserInputLane = "steer" | "followUp";
|
|
44
|
+
/** The ONE reading of "which lane carries this tier": `"later"` rides the follow-up lane (never folded into work in
|
|
45
|
+
* progress), `"next"` and `"now"` ride the steer lane (`"now"` additionally cuts the running turn). Both injection
|
|
46
|
+
* seats — the steer verb and the notification lane — branch on it. A parked row that carries no tier reads through
|
|
47
|
+
* {@link steerPriorityOf} first: `injectionLaneOf(steerPriorityOf(row))`. A tier outside the closed set throws
|
|
48
|
+
* (`steering.invalid_content`, the same check and sentence as {@link steerPriorityOf}). */
|
|
49
|
+
export declare function injectionLaneOf(priority: SystemInjectionPriority): UserInputLane;
|
|
39
50
|
export interface TaskNotificationPayload {
|
|
40
51
|
task_id: string;
|
|
41
52
|
/** design/144 §2: `"external"` is CORE-MINTED ONLY — `TaskStream.notify()` casts it onto every
|
|
@@ -10,8 +10,20 @@ export const SYSTEM_INJECTION_PRIORITIES = ["now", "next", "later"];
|
|
|
10
10
|
export function isSystemInjectionPriority(value) {
|
|
11
11
|
return typeof value === "string" && SYSTEM_INJECTION_PRIORITIES.includes(value);
|
|
12
12
|
}
|
|
13
|
+
function requireSystemInjectionPriority(value) {
|
|
14
|
+
if (isSystemInjectionPriority(value))
|
|
15
|
+
return value;
|
|
16
|
+
const shown = typeof value === "string" ? JSON.stringify(value.length > 24 ? `${value.slice(0, 24)}…` : value) : value === null ? "null" : typeof value;
|
|
17
|
+
const refusal = new Error(`a steer priority must be one of ${SYSTEM_INJECTION_PRIORITIES.join("/")}, got ${shown}`);
|
|
18
|
+
refusal.code = "steering.invalid_content";
|
|
19
|
+
throw refusal;
|
|
20
|
+
}
|
|
13
21
|
export function steerPriorityOf(x) {
|
|
14
|
-
return x.priority
|
|
22
|
+
return x.priority === undefined ? "next" : requireSystemInjectionPriority(x.priority);
|
|
23
|
+
}
|
|
24
|
+
const LANE_OF_PRIORITY = { now: "steer", next: "steer", later: "followUp" };
|
|
25
|
+
export function injectionLaneOf(priority) {
|
|
26
|
+
return LANE_OF_PRIORITY[requireSystemInjectionPriority(priority)];
|
|
15
27
|
}
|
|
16
28
|
const TASK_NOTIFICATION_TAGS = [
|
|
17
29
|
"task-notification",
|
package/dist/core/task-spec.d.ts
CHANGED
|
@@ -551,9 +551,11 @@ export interface TaskSpec {
|
|
|
551
551
|
* upstream): an `allowTools` entry naming a tool that does not exist is ITEM-FILTERED at spawn (the
|
|
552
552
|
* agent stays listed and delegable; the only host disclosure is a prepare-time HEURISTIC advisory
|
|
553
553
|
* via `RunnerDeps.onError` phase `"config"` for entries unknown to the parent's assembled roster —
|
|
554
|
-
* zero model-facing warning, the verified upstream shape); a `denyTools` entry
|
|
555
|
-
*
|
|
556
|
-
*
|
|
554
|
+
* zero model-facing warning, the verified upstream shape); a `denyTools` entry is read as the
|
|
555
|
+
* reference reads it (`Tool(content)` hides the whole `Tool`, with a host notice) and judged once, here and for the
|
|
556
|
+
* delegation tool's own lists alike: a spelling that names no tool, or a stale one (another case of a mounted tool, a
|
|
557
|
+
* retired name), is REJECTED at task start (`config.agent.unknown_tool`), any other name no tool carries is told to the
|
|
558
|
+
* host and hides nothing; and
|
|
557
559
|
* providing this field with NO delegation tool mounted is rejected too
|
|
558
560
|
* (`config.agents.no_delegation_tool`) — never a silent no-op. POLICY-NEUTRAL: a
|
|
559
561
|
* roster listing only; the 1.294 tighten-only parent-policy inheritance is applied at spawn time,
|
|
@@ -4,6 +4,7 @@ import { type Settlement } from "./gate-outcome.js";
|
|
|
4
4
|
export { policyCannotAnswer, rowDemandsAPerson } from "./real-approval.js";
|
|
5
5
|
import { type DecisionReason } from "./decision-receipt.js";
|
|
6
6
|
export { isEngineRefusal } from "./decision-receipt.js";
|
|
7
|
+
import { type RemovalGuardSeat } from "../tools/fs/removal-targets.js";
|
|
7
8
|
/** A tool call presented to a policy before it executes. */
|
|
8
9
|
/** design/388 B6 — the per-call face (see {@link ToolCallRequest.face}). */
|
|
9
10
|
export interface ToolCallFace {
|
|
@@ -69,6 +70,15 @@ export interface ToolCallRequest {
|
|
|
69
70
|
* window — at that declaration, not here.
|
|
70
71
|
*/
|
|
71
72
|
cwd?: string;
|
|
73
|
+
/**
|
|
74
|
+
* The root this leg's tools resolve a ROOT-relative spelling against (a search tool's relative `path`, a
|
|
75
|
+
* pattern with no `path`), stamped beside {@link cwd} at every Runner adjudication seat — the same
|
|
76
|
+
* canonical spelling as `cwd` (the hands' own root, the value `cwd` starts at). Present whenever the leg
|
|
77
|
+
* mounts file hands, read-only hands included; absent on a hands-less leg and on a bare request. A path
|
|
78
|
+
* guard whose deployment supplied no base of its own resolves the call's target against `root` / `cwd`.
|
|
79
|
+
* Read-only, like `cwd`.
|
|
80
|
+
*/
|
|
81
|
+
root?: string;
|
|
72
82
|
/**
|
|
73
83
|
* The CANONICAL directories THIS RUN'S PROMPT ADVERTISES AS WRITABLE — the admitted list of the root
|
|
74
84
|
* fence's advertised-writable-directory seat (the memory engine's write home, the deployment's
|
|
@@ -397,12 +407,12 @@ export type PermissionResult = {
|
|
|
397
407
|
/** The ask's whole class set, in {@link import("./ask-origin.js").ASK_CLASSES} order (see
|
|
398
408
|
* {@link AskRequest.classes}). Stamped beside {@link askClass}, under the same rule. */
|
|
399
409
|
classes?: readonly import("./ask-origin.js").AskClass[];
|
|
400
|
-
/** WHICH integrity guard minted this ask —
|
|
401
|
-
* write. ENGINE-STAMPED by the
|
|
402
|
-
* ({@link import("./ask-origin.js").classifyAsk}) so the
|
|
410
|
+
/** WHICH integrity guard minted this ask — a removal of a protected location, an unverifiable recursive delete
|
|
411
|
+
* or the session-transcript write. ENGINE-STAMPED by the guard policies; a fold-internal fact the class
|
|
412
|
+
* derivation reads ({@link import("./ask-origin.js").classifyAsk}) so the guards, which mint the same
|
|
403
413
|
* `requiresRealApproval` ask, are told apart by a fact and not by their prose. Never copied onto the
|
|
404
414
|
* approval request or the park row. */
|
|
405
|
-
guard?: "unverifiable_delete" | "transcript_write" | "artifact_shared_read";
|
|
415
|
+
guard?: "dangerous_delete" | "unverifiable_delete" | "transcript_write" | "artifact_shared_read";
|
|
406
416
|
/** The call could not be READ against a rule that may reach it — a content-form deny row, a content-form
|
|
407
417
|
* ask row, or a persisted-rule row (the lexer's word). ENGINE-STAMPED at the three unreadable mint sites;
|
|
408
418
|
* the class derivation reads it as `rule_unreadable` (a mode may release it, a person may clear it, no
|
|
@@ -1082,6 +1092,7 @@ export declare function createUnverifiableDeletePolicy(opts?: {
|
|
|
1082
1092
|
safeVars?: readonly string[];
|
|
1083
1093
|
/** Override the shell tool names this gate parses (default `Bash`). */
|
|
1084
1094
|
tools?: string[];
|
|
1095
|
+
removalSeat?: RemovalGuardSeat;
|
|
1085
1096
|
}): ToolPolicy;
|
|
1086
1097
|
export declare function createTranscriptIntegrityPolicy(opts?: {
|
|
1087
1098
|
/** Transcript directories to protect. Default: `($AGENT_DATA_DIR ?? ~/.ai-agent)/sessions`. */
|