@sema-agent/core 7.41.0 → 7.42.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 +90 -0
- package/dist/agents/subagent.js +2 -0
- package/dist/core/agent-definition.d.ts +1 -1
- package/dist/core/allow-entry.d.ts +241 -0
- package/dist/core/allow-entry.js +340 -0
- package/dist/core/allow-rule-shadow.d.ts +18 -0
- package/dist/core/allow-rule-shadow.js +1 -0
- package/dist/core/ask-origin.d.ts +5 -5
- package/dist/core/ask-origin.js +1 -1
- package/dist/core/auto-mode.d.ts +3 -2
- package/dist/core/checkpoint-store.d.ts +11 -19
- package/dist/core/checkpoint-store.js +1 -1
- package/dist/core/decision-receipt.d.ts +4 -2
- package/dist/core/decision-receipt.js +1 -1
- package/dist/core/engine-notice.d.ts +8 -5
- package/dist/core/gate-fold.js +1 -1
- package/dist/core/gate-lanes.d.ts +1 -49
- package/dist/core/gate-lanes.js +85 -223
- package/dist/core/governance-codes.d.ts +3 -3
- package/dist/core/governance-codes.js +3 -3
- package/dist/core/hooks.d.ts +13 -10
- package/dist/core/hooks.js +5 -3
- package/dist/core/mode-policy.d.ts +3 -3
- package/dist/core/mode-policy.js +2 -2
- package/dist/core/permission-rules.d.ts +24 -23
- package/dist/core/permission-rules.js +37 -33
- package/dist/core/reminder-disclosure.d.ts +53 -2
- package/dist/core/reminder-disclosure.js +49 -36
- package/dist/core/rule-lane-reads.d.ts +84 -0
- package/dist/core/rule-lane-reads.js +164 -0
- package/dist/core/runner/contracts.d.ts +4 -2
- package/dist/core/runner/gate-exit.d.ts +3 -3
- package/dist/core/runner/image.js +4 -3
- package/dist/core/runner/inline-image.d.ts +14 -0
- package/dist/core/runner/inline-image.js +26 -0
- package/dist/core/runner/permission-rule-lanes.d.ts +3 -1
- package/dist/core/runner/permission-rule-lanes.js +1 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +0 -19
- package/dist/core/runner/prepare-ask-lane.js +2 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
- package/dist/core/runner/prepare-inherited-gate.js +1 -0
- package/dist/core/runner/prepare-park-ask.js +3 -3
- package/dist/core/runner/prepare-policy-chain.d.ts +12 -0
- package/dist/core/runner/prepare-policy-chain.js +10 -9
- package/dist/core/runner/prepare-project-context.d.ts +13 -3
- package/dist/core/runner/prepare-project-context.js +5 -4
- package/dist/core/runner/prepare-task.js +8 -5
- package/dist/core/runner/prepare-turn-wiring.d.ts +16 -3
- package/dist/core/runner/prepare-turn-wiring.js +19 -18
- package/dist/core/runner/run-leg.js +1 -1
- package/dist/core/runner/runtask.js +2 -1
- package/dist/core/runner-deps.d.ts +11 -0
- package/dist/core/skill-grant.d.ts +8 -47
- package/dist/core/skill-grant.js +15 -136
- package/dist/core/synthetic-read-verdict.d.ts +108 -0
- package/dist/core/synthetic-read-verdict.js +111 -0
- package/dist/core/task-spec.d.ts +18 -0
- package/dist/core/tool-name-readings.d.ts +1 -1
- package/dist/core/tool-name-readings.js +1 -1
- package/dist/core/tool-policy.d.ts +25 -24
- package/dist/core/tool-policy.js +26 -17
- package/dist/core/tool-spec.d.ts +7 -0
- package/dist/core/trace.d.ts +16 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -0
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/tools/fs/dir-entries.d.ts +69 -0
- package/dist/tools/fs/dir-entries.js +60 -0
- package/dist/tools/fs/fs-pdf.d.ts +7 -11
- package/dist/tools/fs/fs-pdf.js +5 -14
- package/dist/tools/fs/fs-read.d.ts +173 -2
- package/dist/tools/fs/fs-read.js +399 -286
- package/dist/tools/fs/image-admission.d.ts +51 -0
- package/dist/tools/fs/image-admission.js +36 -0
- package/dist/tools/fs/search.js +3 -7
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +29 -1
- package/wire-changes/core@7.41.1.json +159 -0
- package/wire-changes/core@7.42.0.json +1107 -0
|
@@ -22,6 +22,7 @@ import { STALL_FIRST_BYTE_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCall
|
|
|
22
22
|
import { OFFLOAD_TOOL_NAME } from "../tool-result-store.js";
|
|
23
23
|
import { OUTPUT_TOOL_NAME, SKILL_TOOL_NAME, createOutputTool } from "./synthetic-tools.js";
|
|
24
24
|
import { skillGateWiring } from "../skill-grant.js";
|
|
25
|
+
import { AllowEntryTable, screenAllowedToolsValue } from "../allow-entry.js";
|
|
25
26
|
import { compileOutputSchema } from "./strict-output-schema.js";
|
|
26
27
|
import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
|
|
27
28
|
import { foldAdmissionFreeze } from "../memory-admission.js";
|
|
@@ -279,6 +280,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
279
280
|
const checkpointStore = resolveCheckpointStore(spec, deps), checkpointSeat = checkpointSeatOf(spec, checkpointStore);
|
|
280
281
|
const permissionModeSeat = refusePermissionModeDoors(spec);
|
|
281
282
|
const outputStyleFrame = resolveOutputStyleFrame(spec.outputStyle);
|
|
283
|
+
const allowedTools = screenAllowedToolsValue(spec.allowedTools, "TaskSpec.allowedTools");
|
|
282
284
|
const declaredModeAtDoor = (() => {
|
|
283
285
|
const requested = modeRequestAfterResume(permissionModeSeat, recordedModeSeed(resume?.seed.inheritedGate), resume?.outcome);
|
|
284
286
|
const ceiling = internals?.inheritedGate?.permissionModeCeiling?.current ?? internals?.inheritedGate?.permissionMode;
|
|
@@ -398,13 +400,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
398
400
|
const { canonical: taskRootCanonical } = await resolveTaskRootCanonical({ env: executionEnv, given: taskRootFinal, onNotice: deps.onNotice, sessionId, runId });
|
|
399
401
|
const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, request: fileHistoryRequest, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
|
|
400
402
|
const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillGrants, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
|
|
403
|
+
const allowedToolTables = { request: new AllowEntryTable(), deployment: new AllowEntryTable() };
|
|
401
404
|
const legContextOverlay = () => runnerSelf?.contextOverlayOf?.(harnessRef.current?.getModelRef() ?? resolvedRole.modelRef);
|
|
402
405
|
let resolvedReadFace;
|
|
403
406
|
let resolvedWriteFace;
|
|
404
407
|
let readDenyAdditionsNormalized = [];
|
|
405
408
|
let handsLessResolvedFace;
|
|
406
409
|
let handsLessResolvedWriteFace;
|
|
407
|
-
const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, skillGrantCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDeciderRef.current, autoModeDenialTracking: () => autoModeDenialTrackingRef.current, autoModeArming: () => autoModeArmingRef.current, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
410
|
+
const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, skillGrantCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, requestAllowedTools: allowedToolTables.request, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDeciderRef.current, autoModeDenialTracking: () => autoModeDenialTrackingRef.current, autoModeArming: () => autoModeArmingRef.current, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
408
411
|
bindPermissionModeSeat(permissionMode);
|
|
409
412
|
const announceRuleStoreSkipped = ruleStoreUnreadableNotice(deps.onNotice, { sessionId, runId });
|
|
410
413
|
const persistedPathRowsRead = persistedPathRowsOfTask({ deps, principal: spec.principal, sessionId, root: taskRootFinal, home: executionEnv.homeDir, taskId: hostTaskId, handsEnabled, signals: [abortController.signal, ...(spec.signal !== undefined ? [spec.signal] : [])], onUnread: announceRuleStoreSkipped });
|
|
@@ -616,7 +619,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
616
619
|
deps.onError?.(asThrownError(err), { phase: "config", sessionId });
|
|
617
620
|
}
|
|
618
621
|
}
|
|
619
|
-
const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillGrants, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget: doors.skillListingBudget });
|
|
622
|
+
const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillGrants, allowedTools, allowedToolTables, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget: doors.skillListingBudget });
|
|
620
623
|
const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, skillSpecs, skillSeatOf, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
|
|
621
624
|
const { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, dateAnchor, gitStatusRef } = await preparePromptInputs({ spec, deps, contextOverlay: legContextOverlay(), failClosedReason: selfOrchestrationFailClosedReason(spec, deps), lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, resolvedWriteFace: resolvedWriteFace?.inForce(), toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId, session, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}) });
|
|
622
625
|
const { deferred, memoryBlock, toolSearch: toolSearchPolicy } = prepareDeferClassify({ spec, deps, roster, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryBlock: memoryBlockWithProject, memoryRecallSegment, onceLedger, sessionId });
|
|
@@ -730,7 +733,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
730
733
|
releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
|
|
731
734
|
}
|
|
732
735
|
}
|
|
733
|
-
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, 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, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
|
|
736
|
+
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, 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, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
|
|
734
737
|
const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtection, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, 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, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, fileHistoryFact, sessionBox });
|
|
735
738
|
const writeProtectionCheck = writeProtection?.check;
|
|
736
739
|
writeProtectionJudgeRef.current = writeProtection?.judgeTarget;
|
|
@@ -744,12 +747,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
744
747
|
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 });
|
|
745
748
|
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 });
|
|
746
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, skillSpecs, now, humanReviewRef, abortController, harness, pausedRef });
|
|
747
|
-
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() })) });
|
|
750
|
+
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() })) } } });
|
|
748
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 });
|
|
749
752
|
const postureFrameRef = { report: (err) => deps.onError?.(err, { phase: "degraded", sessionId }) };
|
|
750
753
|
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 } : {}) });
|
|
751
754
|
const preparedHolder = {};
|
|
752
|
-
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, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
|
|
755
|
+
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 });
|
|
753
756
|
await onceLedger.settle(session, announcedListingsRef);
|
|
754
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 } : {}) });
|
|
755
758
|
rollback.commit();
|
|
@@ -6,7 +6,8 @@ import type { EpochDeclaredSections } from "../../prompt-assembly/epoch.js";
|
|
|
6
6
|
import { type TurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js";
|
|
7
7
|
import type { PromptTextDeclaration } from "../../prompts/default.js";
|
|
8
8
|
import { CacheBreakDetector, type ToolFingerprintInput } from "../cache-break-detector.js";
|
|
9
|
-
import {
|
|
9
|
+
import type { ToolPolicy } from "../tool-policy.js";
|
|
10
|
+
import { type SyntheticReadJudge } from "../synthetic-read-verdict.js";
|
|
10
11
|
import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
11
12
|
import type { CwdRef } from "../../tools/fs/fs-shared.js";
|
|
12
13
|
import { type ContainmentFace } from "../../tools/fs/index.js";
|
|
@@ -78,10 +79,19 @@ export interface PrepareTurnWiringInput {
|
|
|
78
79
|
/** borrowed-mutable — the live read-file state, or undefined. Writers here, at RUN time: `detectExternalChanges` evicts a
|
|
79
80
|
* not-found entry; `onCompactionApplied` applies a landed compaction to it. The hands phase mints it. */
|
|
80
81
|
readFileStateForCheckpoint: ReadFileState | undefined;
|
|
81
|
-
/** borrowed-readonly — the deny-narrowing fold, or undefined (
|
|
82
|
+
/** borrowed-readonly — the deny-narrowing fold, or undefined (a layer of the synthetic-Read judge). */
|
|
82
83
|
denyNarrowingPolicy: ToolPolicy | undefined;
|
|
83
|
-
/** borrowed-readonly — the skill lane: the run's skill deny rows (
|
|
84
|
+
/** borrowed-readonly — the skill lane: the run's skill deny rows (a layer of the judge; only `denying` is read). */
|
|
84
85
|
skillGrants: Prepared["skillGrants"];
|
|
86
|
+
/** borrowed-readonly — the deployment's caller policy as resolved through the lock layer, or undefined (a layer of the
|
|
87
|
+
* judge, consulted with an aborted signal — never an approver). */
|
|
88
|
+
callerPolicy: ToolPolicy | undefined;
|
|
89
|
+
/** borrowed-readonly — each live inherited parent policy (a layer of the judge, consulted like the caller policy). */
|
|
90
|
+
inheritedPolicies: readonly ToolPolicy[];
|
|
91
|
+
/** borrowed-readonly — the person's persisted-rule lane, or undefined (a layer of the judge). */
|
|
92
|
+
persistedRuleLane: Prepared["persistedRuleLane"];
|
|
93
|
+
/** borrowed-readonly — the org rule lane, or undefined (a layer of the judge). */
|
|
94
|
+
orgRuleLane: Prepared["permissionRuleOrg"];
|
|
85
95
|
/** borrowed-readonly — the run's abort controller; only `signal` (the deny door's and the stat's signal). */
|
|
86
96
|
abortController: Pick<AbortController, "signal">;
|
|
87
97
|
/** borrowed-readonly — the per-task identity (the background listing's owner). */
|
|
@@ -142,6 +152,9 @@ export interface PrepareTurnWiringResult {
|
|
|
142
152
|
onCompactionApplied: Prepared["onCompactionApplied"];
|
|
143
153
|
/** owned — the boundary-time changed-files scan, or undefined without read state. */
|
|
144
154
|
detectExternalChanges: Prepared["detectExternalChanges"];
|
|
155
|
+
/** owned — the run's ONE synthetic-Read deny judge over every layer that can refuse a model's `Read` (the
|
|
156
|
+
* changed-files door reads it; a face that delivers a file the model did not ask for reads the same one). */
|
|
157
|
+
syntheticReadVerdict: SyntheticReadJudge;
|
|
145
158
|
/** owned — the post-compact background-task listing (`Prepared.listBackgroundTasks`). */
|
|
146
159
|
listBackgroundTasks: Prepared["listBackgroundTasks"];
|
|
147
160
|
/** owned — the compaction-boundary center adoption candidate, or undefined (no source, or no adoption). */
|
|
@@ -4,7 +4,8 @@ import { artifactDeclarations } from "../../prompt-assembly/artifact.js";
|
|
|
4
4
|
import { buildTurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js";
|
|
5
5
|
import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
|
|
6
6
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
7
|
-
import {
|
|
7
|
+
import { createSyntheticReadJudge } from "../synthetic-read-verdict.js";
|
|
8
|
+
import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js";
|
|
8
9
|
import { decodeTextBytes } from "../../tools/fs/encoding.js";
|
|
9
10
|
import { applyContextShrinkToReadFileState, isReadDedupStubResult } from "../../tools/fs/index.js";
|
|
10
11
|
import { resolveKey } from "../../tools/fs/safety.js";
|
|
@@ -12,7 +13,7 @@ import { PROMPT_HASH_SALT } from "./prompt-hash-salt.js";
|
|
|
12
13
|
import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
|
|
13
14
|
import { thrownText } from "../thrown-value.js";
|
|
14
15
|
export function prepareTurnWiring(input) {
|
|
15
|
-
const { 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, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder } = input;
|
|
16
|
+
const { 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, callerPolicy, inheritedPolicies, persistedRuleLane, orgRuleLane, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder } = input;
|
|
16
17
|
const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
|
|
17
18
|
const cacheFingerprint = cacheBreakDetector
|
|
18
19
|
? { systemPrompt: systemPromptSeat.current, tools: toolsToFingerprintInputs(harnessTools) }
|
|
@@ -101,19 +102,18 @@ export function prepareTurnWiring(input) {
|
|
|
101
102
|
const onCompactionApplied = handsEnabled && readFileStateForCheckpoint
|
|
102
103
|
? (attachedComplete, preserveReadState) => applyContextShrinkToReadFileState(readFileStateForCheckpoint, { kind: "summarized", attachedComplete, ...(preserveReadState !== undefined ? { preserveKeys: preserveReadState } : {}) })
|
|
103
104
|
: undefined;
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
105
|
+
const syntheticReadVerdict = createSyntheticReadJudge({
|
|
106
|
+
skillGrants,
|
|
107
|
+
...(denyNarrowingPolicy !== undefined ? { denyNarrowingPolicy } : {}),
|
|
108
|
+
...(callerPolicy !== undefined ? { callerPolicy } : {}),
|
|
109
|
+
inheritedPolicies,
|
|
110
|
+
...(persistedRuleLane !== undefined ? { persistedRuleLane } : {}),
|
|
111
|
+
...(orgRuleLane !== undefined ? { orgRuleLane } : {}),
|
|
112
|
+
signal: abortController.signal,
|
|
113
|
+
});
|
|
114
|
+
const changedFilesReadDenied = async (paths) => {
|
|
115
|
+
const deadlineAt = Date.now() + ORG_ADJUDICATION_TIMEOUT_MS;
|
|
116
|
+
return Promise.all(paths.map(async (path) => (await syntheticReadVerdict({ file_path: path }, "changed-files", { deadlineAt })).verdict === "deny"));
|
|
117
117
|
};
|
|
118
118
|
const detectExternalChanges = handsEnabled && readFileStateForCheckpoint
|
|
119
119
|
? async (maxFiles) => {
|
|
@@ -124,8 +124,9 @@ export function prepareTurnWiring(input) {
|
|
|
124
124
|
.slice(0, Math.max(0, maxFiles));
|
|
125
125
|
const changed = [];
|
|
126
126
|
const evicted = [];
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
const denied = await changedFilesReadDenied(candidates.map(([path]) => path));
|
|
128
|
+
for (const [i, [path, entry]] of candidates.entries()) {
|
|
129
|
+
if (denied[i] === true)
|
|
129
130
|
continue;
|
|
130
131
|
try {
|
|
131
132
|
const info = await executionEnv.fileInfo(path, abortController.signal);
|
|
@@ -202,5 +203,5 @@ export function prepareTurnWiring(input) {
|
|
|
202
203
|
const effectiveReadFaceObserved = carrierReadFace();
|
|
203
204
|
const effectiveWriteFaceObserved = carrierWriteFace();
|
|
204
205
|
const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
|
|
205
|
-
return { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved };
|
|
206
|
+
return { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, syntheticReadVerdict, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved };
|
|
206
207
|
}
|
|
@@ -2,7 +2,7 @@ import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
|
2
2
|
import { redactThenCut } from "../../agents/subagent-steps.js";
|
|
3
3
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
|
|
4
4
|
import { CheckpointError, readPendingSteerQueue, realApprovalOrgFact, remainingBudgetMicroUsd } from "../checkpoint-store.js";
|
|
5
|
-
import { normalizePersistedRuleHit } from "../
|
|
5
|
+
import { normalizePersistedRuleHit } from "../rule-lane-reads.js";
|
|
6
6
|
import { decidersNote, SETTLEMENT_NOBODY_JUDGED } from "../gate-outcome.js";
|
|
7
7
|
import { formatHookFeedback, hookSeatExpiredError, runHookSeat } from "../hooks.js";
|
|
8
8
|
import { buildAppliedHumanInputEvent, buildHumanInputEvent, projectHumanInput } from "../human-input-projection.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deliverEngineNotice } from "../types.js";
|
|
2
|
+
import { withDeploymentAllowedTools } from "../allow-entry.js";
|
|
2
3
|
import { uuidv7 } from "../../internal/harness.js";
|
|
3
4
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
4
5
|
import { CheckpointError } from "../checkpoint-store.js";
|
|
@@ -77,7 +78,7 @@ export class Runner {
|
|
|
77
78
|
assertNoRetiredRunnerDeps(deps);
|
|
78
79
|
const runner = this;
|
|
79
80
|
this.depsSeat = { get deps() { return runner.deps; } };
|
|
80
|
-
this.deps = deps;
|
|
81
|
+
this.deps = withDeploymentAllowedTools(deps);
|
|
81
82
|
if (this.deps.tiers) {
|
|
82
83
|
this.deps = { ...this.deps, models: expandTiers(this.deps.models, this.deps.tiers) };
|
|
83
84
|
}
|
|
@@ -609,6 +609,17 @@ export interface RunnerDeps {
|
|
|
609
609
|
* adopted principal's bucket forever.
|
|
610
610
|
*/
|
|
611
611
|
localOwnerRules?: boolean;
|
|
612
|
+
/**
|
|
613
|
+
* The DEPLOYMENT's allow rules — standing yeses its operator writes for every run of this Runner, in the grammar of
|
|
614
|
+
* {@link import("./task-spec.js").TaskSpec.allowedTools} (the same compiler, matcher and auto-mode strip). One of the allow
|
|
615
|
+
* layer's members, read beside the request's list, a person's stored rules and a skill's list under one predicate: deny
|
|
616
|
+
* and ask rules still win, a question no allow rule clears is still put to a person (the entry disclosed as the
|
|
617
|
+
* deployment's), and a call it clears is attributed to it (`decisionReason: "deployment_rule"`). Lives for the Runner —
|
|
618
|
+
* read once at construction: a value that is not a list of strings refuses construction (`config.invalid_permission_rules`,
|
|
619
|
+
* with its issues), and the entries that read as nothing are announced once, when the Runner is built
|
|
620
|
+
* (`permission.allowed_tool_not_granted`, `detail.entries[].source: "deployment"`).
|
|
621
|
+
*/
|
|
622
|
+
allowedTools?: readonly string[];
|
|
612
623
|
/**
|
|
613
624
|
* design/182 §9 — DECLARE that this deployment drives cloud sync (`syncPermissionRules`) for the
|
|
614
625
|
* wired rule store. Purely a disclosure input: it is reported as `permissionRules.syncWired` on the
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { PermissionResult, ToolCallRequest } from "./tool-policy.js";
|
|
2
|
-
import { type PersistedRule } from "./permission-rule-model.js";
|
|
3
2
|
import type { PermissionMode } from "./ask-origin.js";
|
|
4
3
|
import { type PermissionSeatInputs, type PermissionSeatReading } from "./mode-policy.js";
|
|
5
4
|
import type { AgentTool } from "../internal/harness-types.js";
|
|
6
|
-
import type
|
|
5
|
+
import { type AllowEntryGateSeats, type AllowEntryInertReason, type AllowEntryJudgeContext, type CompiledAllowEntry } from "./allow-entry.js";
|
|
7
6
|
/** Who wrote a row: the `Skill` tool on a successful activation, prepare for a skill the person invoked by name, or a
|
|
8
7
|
* delegated child's seed from its parent. */
|
|
9
8
|
export declare const SKILL_GRANT_ORIGINS: readonly ["model", "user", "inherited"];
|
|
@@ -17,45 +16,20 @@ export interface SkillGrantRecord {
|
|
|
17
16
|
readonly entry: string;
|
|
18
17
|
readonly origin?: SkillGrantOrigin;
|
|
19
18
|
}
|
|
20
|
-
/** Why an entry grants (or refuses) nothing — the closed set the start-of-run notice names. */
|
|
21
|
-
export declare const SKILL_ENTRY_INERT_REASONS: readonly ["wildcard_all", "namespace_wildcard", "path_pattern", "domain", "content_unsupported", "grammar"];
|
|
22
|
-
export type SkillEntryInertReason = (typeof SKILL_ENTRY_INERT_REASONS)[number];
|
|
23
|
-
/** One entry the grammar reads (kind closed): a whole non-shell tool; a protocol namespace; the whole shell tool; a shell
|
|
24
|
-
* command rule (`interpreter` = an interpreter-headed open prefix); the Read directory form; a deny-side path pattern. */
|
|
25
|
-
type CompiledEntry = {
|
|
26
|
-
readonly kind: "tool";
|
|
27
|
-
readonly name: string;
|
|
28
|
-
} | {
|
|
29
|
-
readonly kind: "namespace";
|
|
30
|
-
readonly pattern: string;
|
|
31
|
-
} | {
|
|
32
|
-
readonly kind: "shell";
|
|
33
|
-
readonly name: string;
|
|
34
|
-
} | {
|
|
35
|
-
readonly kind: "command";
|
|
36
|
-
readonly rule: PersistedRule;
|
|
37
|
-
readonly interpreter: boolean;
|
|
38
|
-
} | {
|
|
39
|
-
readonly kind: "read_dir";
|
|
40
|
-
readonly rule: PersistedRule;
|
|
41
|
-
} | {
|
|
42
|
-
readonly kind: "path";
|
|
43
|
-
readonly rule: PersistedRule;
|
|
44
|
-
};
|
|
45
19
|
/** One row of the table. */
|
|
46
20
|
export interface SkillGrantRow {
|
|
47
21
|
readonly skill: string;
|
|
48
22
|
readonly origin: SkillGrantOrigin;
|
|
49
23
|
readonly behavior: "allow" | "deny";
|
|
50
24
|
readonly entry: string;
|
|
51
|
-
readonly compiled:
|
|
25
|
+
readonly compiled: CompiledAllowEntry;
|
|
52
26
|
}
|
|
53
27
|
/** An entry the grammar reads as granting or refusing nothing. */
|
|
54
28
|
export interface SkillInertEntry {
|
|
55
29
|
readonly skill: string;
|
|
56
30
|
readonly list: "allowed-tools" | "disallowed-tools";
|
|
57
31
|
readonly entry: string;
|
|
58
|
-
readonly reason:
|
|
32
|
+
readonly reason: AllowEntryInertReason;
|
|
59
33
|
}
|
|
60
34
|
/** The two lists a skill declares (the shape of a `SkillSpec`, read by name). */
|
|
61
35
|
export interface SkillGrantSource {
|
|
@@ -120,13 +94,6 @@ export declare function compileSkillGrant(skill: SkillGrantSource, origin: Skill
|
|
|
120
94
|
rows: SkillGrantRow[];
|
|
121
95
|
inert: SkillInertEntry[];
|
|
122
96
|
};
|
|
123
|
-
/** The call facts a content row is judged against — the persisted lane's own bases. */
|
|
124
|
-
export interface SkillGrantJudgeContext {
|
|
125
|
-
readonly root: string | undefined;
|
|
126
|
-
readonly sessionId: string | undefined;
|
|
127
|
-
readonly liveCwd: () => string | undefined;
|
|
128
|
-
readonly home: string | undefined;
|
|
129
|
-
}
|
|
130
97
|
/** What spoke for a call: the skill and the entries it stands on. */
|
|
131
98
|
export interface SkillGrantHit {
|
|
132
99
|
readonly skill: string;
|
|
@@ -178,7 +145,7 @@ export declare function skillSeatReader(skills: readonly SkillGrantSource[]): Sk
|
|
|
178
145
|
/**
|
|
179
146
|
* The gate's seat inputs for one call, composed in ONE place: the mounted instance's static seat (`calledName`), the `Skill`
|
|
180
147
|
* tool's per-call reader in the leg's live mode (only when the call's wire name is the engine's `Skill` — `skillToolName`),
|
|
181
|
-
*
|
|
148
|
+
* the run's skill rows with their attribution sink, and the request's and the deployment's entry lists with theirs.
|
|
182
149
|
*/
|
|
183
150
|
export declare function skillGateWiring(opts: {
|
|
184
151
|
tools: readonly AgentTool[];
|
|
@@ -187,7 +154,9 @@ export declare function skillGateWiring(opts: {
|
|
|
187
154
|
mode: () => PermissionMode | undefined;
|
|
188
155
|
lane: SkillGrantLane;
|
|
189
156
|
onAllowed: NonNullable<SkillGateSeats["skillGrants"]>["onAllowed"];
|
|
190
|
-
|
|
157
|
+
/** The two entry lists that are not a skill's (the request's, the deployment's), with their attribution sinks. */
|
|
158
|
+
allowEntries: AllowEntryGateSeats;
|
|
159
|
+
}): (calledName: string, wireName: string) => PermissionSeatInputs & SkillGateSeats & AllowEntryGateSeats;
|
|
191
160
|
/**
|
|
192
161
|
* Seed a task's table at prepare, before any call is gated: the rows from outside this leg's own writers (`inherited` — a
|
|
193
162
|
* parent's capture, held until this child ends, and a resumed leg's frozen rows, which keep the word `model` where the
|
|
@@ -217,7 +186,7 @@ export declare class SkillGrantTable {
|
|
|
217
186
|
/** The rows as portable records (a copy). */
|
|
218
187
|
records(): readonly SkillGrantRecord[];
|
|
219
188
|
/** The lane over this table, bound to the leg's call facts. */
|
|
220
|
-
lane(ctx:
|
|
189
|
+
lane(ctx: AllowEntryJudgeContext): SkillGrantLane;
|
|
221
190
|
}
|
|
222
191
|
/** Read a delegated child's capture ({@link readCarriedRecords}): `undefined` refuses the delegation. */
|
|
223
192
|
export declare function readSkillGrantCapture(snapshot: readonly unknown[] | undefined): readonly SkillGrantRecord[] | undefined;
|
|
@@ -227,13 +196,5 @@ export declare function frozenSkillGrants(table: Pick<SkillGrantTable, "records"
|
|
|
227
196
|
/** Read a park's frozen section ({@link readCarriedRecords}): absent ⇒ none; `undefined` ⇒ damaged — the resume refuses
|
|
228
197
|
* it before the claim and prepare never seeds from it. */
|
|
229
198
|
export declare function readFrozenSkillGrants(section: unknown): readonly SkillGrantRecord[] | undefined;
|
|
230
|
-
/** The start-of-run notice for entries that grant (or refuse) nothing. */
|
|
231
|
-
export declare const SKILL_ENTRY_NOT_GRANTED_CODE = "skills.allowed_tool_not_granted";
|
|
232
199
|
/** The refusal for a `TaskSpec.userInvokedSkills` name the run does not carry (or a value that is not a list of names). */
|
|
233
200
|
export declare const USER_INVOKED_SKILL_UNKNOWN_CODE = "config.user_invoked_skill_unknown";
|
|
234
|
-
/**
|
|
235
|
-
* The notice for entries this grammar reads as nothing — one per prepared task, delivered only when one exists. The
|
|
236
|
-
* deployment's operator owns the skill files; the run goes on (an entry that grants nothing makes it ask more).
|
|
237
|
-
*/
|
|
238
|
-
export declare function skillEntriesNotGrantedNotice(inert: readonly SkillInertEntry[], sessionId: string): EngineNotice;
|
|
239
|
-
export {};
|
package/dist/core/skill-grant.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { protocolOf } from "./protocol-table.js";
|
|
4
|
-
import { COARSE_SHELL_TOOLS } from "./tool-policy.js";
|
|
5
|
-
import { COMMAND_RULE_TOOL, READ_RULE_TOOL, findAdmittingRule, laneToolNamesOf, parseRuleText, programRunReachOf, ruleToolGrammarOf } from "./permission-rule-model.js";
|
|
6
|
-
import { pathRuleLaneAnswer } from "./runner/permission-rule-lanes.js";
|
|
1
|
+
import { wireOf } from "./tool-name-readings.js";
|
|
2
|
+
import { parsePermissionRule } from "./permission-rules.js";
|
|
7
3
|
import { mountedToolPermissionSeat } from "./mode-policy.js";
|
|
8
4
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
9
5
|
import { boundInputHashOf } from "./canonical-json.js";
|
|
6
|
+
import { allowingRows, autoStripped, compileAllowEntry, compileAllowList, judgeAllowEntries } from "./allow-entry.js";
|
|
10
7
|
export const SKILL_GRANT_ORIGINS = Object.freeze(["model", "user", "inherited"]);
|
|
11
8
|
const FROZEN_SKILL_GRANT_ORIGINS = new Set(["model", "inherited"]);
|
|
12
|
-
export const SKILL_ENTRY_INERT_REASONS = Object.freeze(["wildcard_all", "namespace_wildcard", "path_pattern", "domain", "content_unsupported", "grammar"]);
|
|
13
9
|
export function skillNamedBy(args, skills) {
|
|
14
10
|
const named = args?.skill;
|
|
15
11
|
return typeof named === "string" ? skills.find((s) => s.name === named) : undefined;
|
|
@@ -47,50 +43,6 @@ export function detachSkillLists(skills) {
|
|
|
47
43
|
export function declaresSkillLists(skill) {
|
|
48
44
|
return (skill.allowedTools?.length ?? 0) > 0 || (skill.disallowedTools?.length ?? 0) > 0;
|
|
49
45
|
}
|
|
50
|
-
function ruleRowOf(parsed) {
|
|
51
|
-
return { ...parsed, scope: { kind: "global" }, adds: [{ dot: { actor: "skill", counter: 1 }, origin: "user", createdAt: "" }] };
|
|
52
|
-
}
|
|
53
|
-
function compileEntry(raw, behavior) {
|
|
54
|
-
const parsed = parsePermissionRule(raw);
|
|
55
|
-
const name = parsed.toolName;
|
|
56
|
-
if (name === "*")
|
|
57
|
-
return "wildcard_all";
|
|
58
|
-
if (parsed.ruleContent === undefined) {
|
|
59
|
-
const ns = protocolOf(name);
|
|
60
|
-
if (ns !== undefined) {
|
|
61
|
-
if (name.slice(ns.prefix.length).split("__")[0]?.includes("*") === true)
|
|
62
|
-
return "namespace_wildcard";
|
|
63
|
-
if (isNamespacedCoveringRuleName(name))
|
|
64
|
-
return { kind: "namespace", pattern: name };
|
|
65
|
-
return { kind: "tool", name };
|
|
66
|
-
}
|
|
67
|
-
return COARSE_SHELL_TOOLS.includes(name) ? { kind: "shell", name } : { kind: "tool", name };
|
|
68
|
-
}
|
|
69
|
-
if (name === COMMAND_RULE_TOOL) {
|
|
70
|
-
const allowRead = parseRuleText(raw, behavior);
|
|
71
|
-
if ("rule" in allowRead)
|
|
72
|
-
return { kind: "command", rule: ruleRowOf(allowRead.rule), interpreter: false };
|
|
73
|
-
if (behavior === "allow" && allowRead.reject.code === "invalid.bare_interpreter_prefix") {
|
|
74
|
-
const wide = parseRuleText(raw, "deny");
|
|
75
|
-
if ("rule" in wide)
|
|
76
|
-
return { kind: "command", rule: ruleRowOf({ ...wide.rule, behavior: "allow" }), interpreter: true };
|
|
77
|
-
}
|
|
78
|
-
return "grammar";
|
|
79
|
-
}
|
|
80
|
-
if (ruleToolGrammarOf(name) === "path") {
|
|
81
|
-
const read = parseRuleText(raw, behavior);
|
|
82
|
-
if ("rule" in read) {
|
|
83
|
-
if (read.rule.match === "subpath" && read.rule.tool === READ_RULE_TOOL && behavior === "allow")
|
|
84
|
-
return { kind: "read_dir", rule: ruleRowOf(read.rule) };
|
|
85
|
-
if (behavior === "deny")
|
|
86
|
-
return { kind: "path", rule: ruleRowOf(read.rule) };
|
|
87
|
-
}
|
|
88
|
-
return "path_pattern";
|
|
89
|
-
}
|
|
90
|
-
if (/^domain:/.test(parsed.ruleContent))
|
|
91
|
-
return "domain";
|
|
92
|
-
return "content_unsupported";
|
|
93
|
-
}
|
|
94
46
|
export function compileSkillGrant(skill, origin) {
|
|
95
47
|
const rows = [];
|
|
96
48
|
const inert = [];
|
|
@@ -98,79 +50,17 @@ export function compileSkillGrant(skill, origin) {
|
|
|
98
50
|
["allowed-tools", "allow", skill.allowedTools ?? []],
|
|
99
51
|
["disallowed-tools", "deny", skill.disallowedTools ?? []],
|
|
100
52
|
]) {
|
|
101
|
-
const
|
|
102
|
-
for (const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
else
|
|
107
|
-
rows.push({ skill: skill.name, origin, behavior, entry, compiled });
|
|
108
|
-
}
|
|
53
|
+
const compiled = compileAllowList(entries, behavior);
|
|
54
|
+
for (const r of compiled.rows)
|
|
55
|
+
rows.push({ skill: skill.name, origin, behavior, entry: r.entry, compiled: r.compiled });
|
|
56
|
+
for (const i of compiled.inert)
|
|
57
|
+
inert.push({ skill: skill.name, list, entry: i.entry, reason: i.reason });
|
|
109
58
|
}
|
|
110
59
|
return { rows, inert };
|
|
111
60
|
}
|
|
112
|
-
function standsDownIn(row, mode, req) {
|
|
113
|
-
if (mode !== "auto")
|
|
114
|
-
return false;
|
|
115
|
-
if (row.compiled.kind === "shell" || (row.compiled.kind === "command" && row.compiled.interpreter))
|
|
116
|
-
return true;
|
|
117
|
-
return row.compiled.kind === "tool" && req !== undefined && answersTo(req, (n) => COARSE_SHELL_TOOLS.includes(n));
|
|
118
|
-
}
|
|
119
|
-
function answersTo(req, has) {
|
|
120
|
-
return heldBy("skillGrant.names", { has }, req);
|
|
121
|
-
}
|
|
122
|
-
function nameRowReaches(compiled, req) {
|
|
123
|
-
const covers = compiled.kind === "namespace" ? (n) => namespacedRuleNameCovers(compiled.pattern, n) : compiled.kind === "tool" || compiled.kind === "shell" ? (n) => n === compiled.name : undefined;
|
|
124
|
-
return covers !== undefined && answersTo(req, covers);
|
|
125
|
-
}
|
|
126
|
-
function commandOf(req) {
|
|
127
|
-
const tool = laneToolNamesOf(req).find((n) => ruleToolGrammarOf(n) === "command");
|
|
128
|
-
const command = req.args?.command;
|
|
129
|
-
return tool !== undefined && typeof command === "string" ? { tool, command } : undefined;
|
|
130
|
-
}
|
|
131
61
|
function judge(rows, req, behavior, ctx) {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
return undefined;
|
|
135
|
-
for (const row of live)
|
|
136
|
-
if (nameRowReaches(row.compiled, req))
|
|
137
|
-
return { skill: row.skill, entries: [row.entry] };
|
|
138
|
-
const call = commandOf(req);
|
|
139
|
-
const liveCwd = ctx.liveCwd();
|
|
140
|
-
if (call !== undefined) {
|
|
141
|
-
const byRule = new Map();
|
|
142
|
-
for (const r of live)
|
|
143
|
-
if (r.compiled.kind === "command")
|
|
144
|
-
byRule.set(r.compiled.rule, r);
|
|
145
|
-
if (behavior === "allow") {
|
|
146
|
-
const covering = findAdmittingRule([...byRule.keys()], { tool: call.tool, command: call.command, cwd: ctx.root, ...(ctx.sessionId !== undefined ? { sessionId: ctx.sessionId } : {}), ...(liveCwd !== undefined ? { execCwd: liveCwd } : {}) });
|
|
147
|
-
const hits = (covering ?? []).flatMap((rule) => byRule.get(rule) ?? []);
|
|
148
|
-
const first = hits[0];
|
|
149
|
-
if (first !== undefined)
|
|
150
|
-
return { skill: first.skill, entries: hits.map((r) => r.entry) };
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
for (const [rule, row] of byRule) {
|
|
154
|
-
if (programRunReachOf(rule, call.command).reach !== "not_reached")
|
|
155
|
-
return { skill: row.skill, entries: [row.entry] };
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const pathKind = behavior === "allow" ? "read_dir" : "path";
|
|
160
|
-
const pathRows = live.filter((r) => r.compiled.kind === pathKind);
|
|
161
|
-
if (pathRows.length > 0) {
|
|
162
|
-
const table = pathRows.map((r) => r.compiled.rule);
|
|
163
|
-
for (const name of laneToolNamesOf(req).filter((n) => ruleToolGrammarOf(n) === "path")) {
|
|
164
|
-
const answer = pathRuleLaneAnswer(table, { ...req, toolName: name }, { root: ctx.root, sessionId: ctx.sessionId, liveCwd, home: ctx.home });
|
|
165
|
-
const spoke = answer !== undefined && ("unreadable" in answer ? behavior === "deny" : "behavior" in answer && answer.behavior === behavior);
|
|
166
|
-
if (spoke) {
|
|
167
|
-
const rule = answer !== undefined && "rules" in answer ? answer.rules[0]?.rule : undefined;
|
|
168
|
-
const row = pathRows.find((r) => r.compiled.rule.rule === rule) ?? pathRows[0];
|
|
169
|
-
return { skill: row.skill, entries: [row.entry] };
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return undefined;
|
|
62
|
+
const hit = judgeAllowEntries(rows, req, behavior, ctx);
|
|
63
|
+
return hit === undefined || hit[0] === undefined ? undefined : { skill: hit[0].skill, entries: hit.map((r) => r.entry) };
|
|
174
64
|
}
|
|
175
65
|
export function skillDenyDecision(hit) {
|
|
176
66
|
return {
|
|
@@ -195,7 +85,7 @@ const CARD_ENTRIES_MAX = 10;
|
|
|
195
85
|
const CARD_ENTRY_MAX_CHARS = 120;
|
|
196
86
|
export function skillActivationQuestion(skill, mode) {
|
|
197
87
|
const head = `Execute skill: ${inlineUntrusted(skill.name, 200)}`;
|
|
198
|
-
const granting = compileSkillGrant(skill, "model").rows.filter((r) => r.behavior === "allow" && !
|
|
88
|
+
const granting = compileSkillGrant(skill, "model").rows.filter((r) => r.behavior === "allow" && !(mode === "auto" && autoStripped(r.compiled, undefined)));
|
|
199
89
|
if (granting.length === 0)
|
|
200
90
|
return head;
|
|
201
91
|
const shown = granting.slice(0, CARD_ENTRIES_MAX).map((r) => inlineUntrusted(r.entry, CARD_ENTRY_MAX_CHARS));
|
|
@@ -216,6 +106,7 @@ export function skillGateWiring(opts) {
|
|
|
216
106
|
...(mountedToolPermissionSeat(opts.tools, calledName) === "passthrough" ? { permissionSeat: "passthrough" } : {}),
|
|
217
107
|
...(opts.seatOf !== undefined && wireName === opts.skillToolName ? { permissionSeatOf: (args) => opts.seatOf?.(args, opts.mode()) } : {}),
|
|
218
108
|
skillGrants,
|
|
109
|
+
...opts.allowEntries,
|
|
219
110
|
});
|
|
220
111
|
}
|
|
221
112
|
export function seedSkillGrants(table, opts) {
|
|
@@ -248,7 +139,7 @@ export class SkillGrantTable {
|
|
|
248
139
|
}
|
|
249
140
|
seed(records, origin) {
|
|
250
141
|
for (const r of records) {
|
|
251
|
-
const compiled =
|
|
142
|
+
const compiled = compileAllowEntry(r.entry, r.behavior);
|
|
252
143
|
if (typeof compiled === "string")
|
|
253
144
|
continue;
|
|
254
145
|
this.hold({ skill: r.skill, origin: r.origin === "model" ? "model" : origin, behavior: r.behavior, entry: r.entry, compiled });
|
|
@@ -259,7 +150,7 @@ export class SkillGrantTable {
|
|
|
259
150
|
}
|
|
260
151
|
lane(ctx) {
|
|
261
152
|
return {
|
|
262
|
-
allowing: (req, mode) => judge(this.rowsHeld
|
|
153
|
+
allowing: (req, mode) => judge(allowingRows(this.rowsHeld, req, mode), req, "allow", ctx),
|
|
263
154
|
denying: (req) => judge(this.rowsHeld, req, "deny", ctx),
|
|
264
155
|
};
|
|
265
156
|
}
|
|
@@ -276,7 +167,7 @@ function readCarriedRecords(raw, carrier) {
|
|
|
276
167
|
const { skill, behavior, entry, origin } = r;
|
|
277
168
|
if (typeof skill !== "string" || typeof entry !== "string" || (behavior !== "allow" && behavior !== "deny"))
|
|
278
169
|
return undefined;
|
|
279
|
-
if (typeof
|
|
170
|
+
if (typeof compileAllowEntry(entry, behavior) === "string")
|
|
280
171
|
return undefined;
|
|
281
172
|
if (carrier === "capture") {
|
|
282
173
|
out.push({ skill, behavior, entry });
|
|
@@ -298,16 +189,4 @@ export function frozenSkillGrants(table) {
|
|
|
298
189
|
export function readFrozenSkillGrants(section) {
|
|
299
190
|
return readCarriedRecords(section, "frozen");
|
|
300
191
|
}
|
|
301
|
-
export const SKILL_ENTRY_NOT_GRANTED_CODE = "skills.allowed_tool_not_granted";
|
|
302
192
|
export const USER_INVOKED_SKILL_UNKNOWN_CODE = "config.user_invoked_skill_unknown";
|
|
303
|
-
const NOTICE_ENTRIES_SHOWN = 5;
|
|
304
|
-
export function skillEntriesNotGrantedNotice(inert, sessionId) {
|
|
305
|
-
const shown = inert.slice(0, NOTICE_ENTRIES_SHOWN).map((e) => `${inlineUntrusted(e.skill, 80)}: ${e.list} "${inlineUntrusted(e.entry, 120)}" (${e.reason})`);
|
|
306
|
-
const rest = inert.length - shown.length;
|
|
307
|
-
return {
|
|
308
|
-
code: SKILL_ENTRY_NOT_GRANTED_CODE,
|
|
309
|
-
message: `${inert.length} skill list entr${inert.length === 1 ? "y grants" : "ies grant"} or refuse${inert.length === 1 ? "s" : ""} nothing — ${shown.join("; ")}${rest > 0 ? `; and ${rest} more` : ""}. ` +
|
|
310
|
-
"An allowed-tools list holding `*` voids the list, `mcp__*` names no server, and a write-path or domain entry has no allow reading; name the tools (or `mcp__<server>`) instead.",
|
|
311
|
-
detail: { sessionId, entries: inert.map((e) => ({ skill: e.skill, list: e.list, entry: e.entry, reason: e.reason })) },
|
|
312
|
-
};
|
|
313
|
-
}
|