chore: initialize recovered claude workspace
This commit is contained in:
127
src/utils/permissions/PermissionPromptToolResultSchema.ts
Normal file
127
src/utils/permissions/PermissionPromptToolResultSchema.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import type { Tool, ToolUseContext } from 'src/Tool.js'
|
||||
import z from 'zod/v4'
|
||||
import { logForDebugging } from '../debug.js'
|
||||
import { lazySchema } from '../lazySchema.js'
|
||||
import type {
|
||||
PermissionDecision,
|
||||
PermissionDecisionReason,
|
||||
} from './PermissionResult.js'
|
||||
import {
|
||||
applyPermissionUpdates,
|
||||
persistPermissionUpdates,
|
||||
} from './PermissionUpdate.js'
|
||||
import { permissionUpdateSchema } from './PermissionUpdateSchema.js'
|
||||
|
||||
export const inputSchema = lazySchema(() =>
|
||||
z.object({
|
||||
tool_name: z
|
||||
.string()
|
||||
.describe('The name of the tool requesting permission'),
|
||||
input: z.record(z.string(), z.unknown()).describe('The input for the tool'),
|
||||
tool_use_id: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('The unique tool use request ID'),
|
||||
}),
|
||||
)
|
||||
|
||||
export type Input = z.infer<ReturnType<typeof inputSchema>>
|
||||
|
||||
// Zod schema for permission results
|
||||
// This schema is used to validate the MCP permission prompt tool
|
||||
// so we maintain it as a subset of the real PermissionDecision type
|
||||
|
||||
// Matches PermissionDecisionClassificationSchema in entrypoints/sdk/coreSchemas.ts.
|
||||
// Malformed values fall through to undefined (same pattern as updatedPermissions
|
||||
// below) so a bad string from the SDK host doesn't reject the whole decision.
|
||||
const decisionClassificationField = lazySchema(() =>
|
||||
z
|
||||
.enum(['user_temporary', 'user_permanent', 'user_reject'])
|
||||
.optional()
|
||||
.catch(undefined),
|
||||
)
|
||||
|
||||
const PermissionAllowResultSchema = lazySchema(() =>
|
||||
z.object({
|
||||
behavior: z.literal('allow'),
|
||||
updatedInput: z.record(z.string(), z.unknown()),
|
||||
// SDK hosts may send malformed entries; fall back to undefined rather
|
||||
// than rejecting the entire allow decision (anthropics/claude-code#29440)
|
||||
updatedPermissions: z
|
||||
.array(permissionUpdateSchema())
|
||||
.optional()
|
||||
.catch(ctx => {
|
||||
logForDebugging(
|
||||
`Malformed updatedPermissions from SDK host ignored: ${ctx.error.issues[0]?.message ?? 'unknown'}`,
|
||||
{ level: 'warn' },
|
||||
)
|
||||
return undefined
|
||||
}),
|
||||
toolUseID: z.string().optional(),
|
||||
decisionClassification: decisionClassificationField(),
|
||||
}),
|
||||
)
|
||||
|
||||
const PermissionDenyResultSchema = lazySchema(() =>
|
||||
z.object({
|
||||
behavior: z.literal('deny'),
|
||||
message: z.string(),
|
||||
interrupt: z.boolean().optional(),
|
||||
toolUseID: z.string().optional(),
|
||||
decisionClassification: decisionClassificationField(),
|
||||
}),
|
||||
)
|
||||
|
||||
export const outputSchema = lazySchema(() =>
|
||||
z.union([PermissionAllowResultSchema(), PermissionDenyResultSchema()]),
|
||||
)
|
||||
|
||||
export type Output = z.infer<ReturnType<typeof outputSchema>>
|
||||
|
||||
/**
|
||||
* Normalizes the result of a permission prompt tool to a PermissionDecision.
|
||||
*/
|
||||
export function permissionPromptToolResultToPermissionDecision(
|
||||
result: Output,
|
||||
tool: Tool,
|
||||
input: { [key: string]: unknown },
|
||||
toolUseContext: ToolUseContext,
|
||||
): PermissionDecision {
|
||||
const decisionReason: PermissionDecisionReason = {
|
||||
type: 'permissionPromptTool',
|
||||
permissionPromptToolName: tool.name,
|
||||
toolResult: result,
|
||||
}
|
||||
if (result.behavior === 'allow') {
|
||||
const updatedPermissions = result.updatedPermissions
|
||||
if (updatedPermissions) {
|
||||
toolUseContext.setAppState(prev => ({
|
||||
...prev,
|
||||
toolPermissionContext: applyPermissionUpdates(
|
||||
prev.toolPermissionContext,
|
||||
updatedPermissions,
|
||||
),
|
||||
}))
|
||||
persistPermissionUpdates(updatedPermissions)
|
||||
}
|
||||
// Mobile clients responding from a push notification don't have the
|
||||
// original tool input, so they send `{}` to satisfy the schema. Treat an
|
||||
// empty object as "use original" so the tool doesn't run with no args.
|
||||
const updatedInput =
|
||||
Object.keys(result.updatedInput).length > 0 ? result.updatedInput : input
|
||||
return {
|
||||
...result,
|
||||
updatedInput,
|
||||
decisionReason,
|
||||
}
|
||||
} else if (result.behavior === 'deny' && result.interrupt) {
|
||||
logForDebugging(
|
||||
`SDK permission prompt deny+interrupt: tool=${tool.name} message=${result.message}`,
|
||||
)
|
||||
toolUseContext.abortController.abort()
|
||||
}
|
||||
return {
|
||||
...result,
|
||||
decisionReason,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user