@@ -246,12 +246,15 @@ async function loadPermissionConfig(
246246/**
247247 * Loads the governed subject's permission config. The subject is resolved here, not by callers,
248248 * so every gate reads the same person's group. On a run context the in-flight load is memoized per
249- * subject and workspace in the run's `permissionConfigCache`, and a failed load is evicted.
249+ * subject and workspace in the run's `permissionConfigCache`, and a failed load is evicted. A shared
250+ * load observes only the run's abort signal, so one caller's cancellation cannot fail it for others;
251+ * an unshared load observes the caller's `signal`.
250252 */
251253async function getPermissionConfig (
252254 actorUserId : string | undefined ,
253255 workspaceId : string | undefined ,
254- ctx ?: ExecutionContext
256+ ctx ?: ExecutionContext ,
257+ signal ?: AbortSignal
255258) : Promise < PermissionGroupConfig | null > {
256259 const userId = governedSubjectUserId ( actorUserId , ctx )
257260 if ( ! userId || ! workspaceId ) {
@@ -260,7 +263,7 @@ async function getPermissionConfig(
260263
261264 const cache = ctx ?. permissionConfigCache
262265 if ( ! cache ) {
263- return loadPermissionConfig ( userId , workspaceId , ctx ?. abortSignal )
266+ return loadPermissionConfig ( userId , workspaceId , signal ?? ctx ?. abortSignal )
264267 }
265268
266269 const key = `${ userId } :${ workspaceId } `
@@ -536,6 +539,8 @@ interface PermissionAssertion {
536539 toolId ?: string
537540 toolKind ?: ToolKind
538541 ctx ?: ExecutionContext
542+ /** Caller cancellation, observed while loading a config that is not shared through a run cache. */
543+ signal ?: AbortSignal
539544}
540545
541546/**
@@ -553,7 +558,7 @@ interface PermissionAssertion {
553558/** permission-group-enforced: custom_tools.use — gates tool invocation during a run, not an operation */
554559/** permission-group-enforced: skills.use — gates skill loading during a run, not an operation */
555560export async function assertPermissionsAllowed ( req : PermissionAssertion ) : Promise < void > {
556- const { workspaceId, model, blockType, toolId, toolKind, ctx } = req
561+ const { workspaceId, model, blockType, toolId, toolKind, ctx, signal } = req
557562 const userId = governedSubjectUserId ( req . userId , ctx )
558563
559564 const blockTypeExempt = blockType ? isBlockTypeAccessControlExempt ( blockType ) : false
@@ -564,7 +569,7 @@ export async function assertPermissionsAllowed(req: PermissionAssertion): Promis
564569
565570 const config =
566571 userId && workspaceId
567- ? await getPermissionConfig ( userId , workspaceId , ctx )
572+ ? await getPermissionConfig ( userId , workspaceId , ctx , signal )
568573 : mergeEnvAllowlist ( null )
569574
570575 const subject = { userId, workspaceId }
0 commit comments