swarmWorkerHandler.ts
hooks/toolPermission/handlers/swarmWorkerHandler.ts
160
Lines
5537
Bytes
1
Exports
12
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with tool-system, shell-safety, permissions. It contains 160 lines, 12 detected imports, and 1 detected exports.
Important relationships
Detected exports
handleSwarmWorkerPermission
Keywords
leaderrequestutilstooluseidswarmpermissioninputfeedbackpendingclassifiercheckpermissiondecision
Detected imports
bun:bundle@anthropic-ai/sdk/resources/messages.mjs../../../types/permissions.js../../../utils/agentSwarmsEnabled.js../../../utils/errors.js../../../utils/log.js../../../utils/permissions/PermissionResult.js../../../utils/permissions/PermissionUpdateSchema.js../../../utils/swarm/permissionSync.js../../useSwarmPermissionPoller.js../PermissionContext.js../PermissionContext.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
import { feature } from 'bun:bundle'
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'
import type { PendingClassifierCheck } from '../../../types/permissions.js'
import { isAgentSwarmsEnabled } from '../../../utils/agentSwarmsEnabled.js'
import { toError } from '../../../utils/errors.js'
import { logError } from '../../../utils/log.js'
import type { PermissionDecision } from '../../../utils/permissions/PermissionResult.js'
import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js'
import {
createPermissionRequest,
isSwarmWorker,
sendPermissionRequestViaMailbox,
} from '../../../utils/swarm/permissionSync.js'
import { registerPermissionCallback } from '../../useSwarmPermissionPoller.js'
import type { PermissionContext } from '../PermissionContext.js'
import { createResolveOnce } from '../PermissionContext.js'
type SwarmWorkerPermissionParams = {
ctx: PermissionContext
description: string
pendingClassifierCheck?: PendingClassifierCheck | undefined
updatedInput: Record<string, unknown> | undefined
suggestions: PermissionUpdate[] | undefined
}
/**
* Handles the swarm worker permission flow.
*
* When running as a swarm worker:
* 1. Tries classifier auto-approval for bash commands
* 2. Forwards the permission request to the leader via mailbox
* 3. Registers callbacks for when the leader responds
* 4. Sets the pending indicator while waiting
*
* Returns a PermissionDecision if the classifier auto-approves,
* or a Promise that resolves when the leader responds.
* Returns null if swarms are not enabled or this is not a swarm worker,
* so the caller can fall through to interactive handling.
*/
async function handleSwarmWorkerPermission(
params: SwarmWorkerPermissionParams,
): Promise<PermissionDecision | null> {
if (!isAgentSwarmsEnabled() || !isSwarmWorker()) {
return null
}
const { ctx, description, updatedInput, suggestions } = params
// For bash commands, try classifier auto-approval before forwarding to
// the leader. Agents await the classifier result (rather than racing it
// against user interaction like the main agent).
const classifierResult = feature('BASH_CLASSIFIER')
? await ctx.tryClassifier?.(params.pendingClassifierCheck, updatedInput)
: null
if (classifierResult) {
return classifierResult
}
// Forward permission request to the leader via mailbox
try {
const clearPendingRequest = (): void =>
ctx.toolUseContext.setAppState(prev => ({
...prev,
pendingWorkerRequest: null,
}))
const decision = await new Promise<PermissionDecision>(resolve => {
const { resolve: resolveOnce, claim } = createResolveOnce(resolve)
// Create the permission request
const request = createPermissionRequest({
toolName: ctx.tool.name,
toolUseId: ctx.toolUseID,
input: ctx.input,
description,
permissionSuggestions: suggestions,
})
// Register callback BEFORE sending the request to avoid race condition
// where leader responds before callback is registered
registerPermissionCallback({
requestId: request.id,
toolUseId: ctx.toolUseID,
async onAllow(
allowedInput: Record<string, unknown> | undefined,
permissionUpdates: PermissionUpdate[],
feedback?: string,
contentBlocks?: ContentBlockParam[],
) {
if (!claim()) return // atomic check-and-mark before await
clearPendingRequest()
// Merge the updated input with the original input
const finalInput =
allowedInput && Object.keys(allowedInput).length > 0
? allowedInput
: ctx.input
resolveOnce(
await ctx.handleUserAllow(
finalInput,
permissionUpdates,
feedback,
undefined,
contentBlocks,
),
)
},
onReject(feedback?: string, contentBlocks?: ContentBlockParam[]) {
if (!claim()) return
clearPendingRequest()
ctx.logDecision({
decision: 'reject',
source: { type: 'user_reject', hasFeedback: !!feedback },
})
resolveOnce(ctx.cancelAndAbort(feedback, undefined, contentBlocks))
},
})
// Now that callback is registered, send the request to the leader
void sendPermissionRequestViaMailbox(request)
// Show visual indicator that we're waiting for leader approval
ctx.toolUseContext.setAppState(prev => ({
...prev,
pendingWorkerRequest: {
toolName: ctx.tool.name,
toolUseId: ctx.toolUseID,
description,
},
}))
// If the abort signal fires while waiting for the leader response,
// resolve the promise with a cancel decision so it does not hang.
ctx.toolUseContext.abortController.signal.addEventListener(
'abort',
() => {
if (!claim()) return
clearPendingRequest()
ctx.logCancelled()
resolveOnce(ctx.cancelAndAbort(undefined, true))
},
{ once: true },
)
})
return decision
} catch (error) {
// If swarm permission submission fails, fall back to local handling
logError(toError(error))
// Continue to local UI handling below
return null
}
}
export { handleSwarmWorkerPermission }
export type { SwarmWorkerPermissionParams }