Compare commits

...
1 Commits
Author SHA1 Message Date
James Long 2fb385c6e8 feat(cli): continue run after rejecting a permission ask 2026-09-29 00:11:03 +00:00
2 changed files with 10 additions and 3 deletions
+9 -3
View File
@@ -67,6 +67,9 @@ type FormRequest = Extract<V2Event, { type: "form.created" }>["data"]["form"]
// attached client must not cancel input that may belong to another session.
const GLOBAL_FORM_SESSION_ID = "global"
const PERMISSION_REJECTED_FEEDBACK =
"This non-interactive run cannot ask the user for permission, so the request was rejected. Continue without this action."
export async function runNonInteractivePrompt(input: Input) {
const controller = new AbortController()
const stream = input.client.event.subscribe({ signal: controller.signal })[Symbol.asyncIterator]()
@@ -133,9 +136,11 @@ export async function runNonInteractivePrompt(input: Input) {
}
const replyPermission = async (request: { id: string; action: string; resources: ReadonlyArray<string> }) => {
// Nobody can approve here. Outside V1 compatibility, reject with feedback so the tool fails
// as ordinary model-visible output and the model continues without the action.
const continuing = !input.auto && input.compatibility !== "v1"
if (!input.auto) {
permissionRejected = true
if (input.compatibility !== "v1") process.exitCode = 1
if (!continuing) permissionRejected = true
UI.println(
UI.Style.TEXT_WARNING_BOLD + "!",
UI.Style.TEXT_NORMAL +
@@ -147,9 +152,10 @@ export async function runNonInteractivePrompt(input: Input) {
sessionID: input.sessionID,
requestID: request.id,
decision: input.auto ? "once" : "reject",
...(continuing ? { message: PERMISSION_REJECTED_FEEDBACK } : {}),
})
.catch(() => {})
if (!input.auto) {
if (!input.auto && !continuing) {
await input.client.session.interrupt({ sessionID: input.sessionID }).catch(() => {})
}
}
@@ -41,6 +41,7 @@ export function toSessionError(cause: unknown): SessionError.Error {
}
}
if (cause instanceof Permission.BlockedError) return { type: "permission.rejected", message: cause.message }
if (cause instanceof Permission.CorrectedError) return { type: "permission.rejected", message: cause.feedback }
if (cause instanceof ToolFailure || cause instanceof Tool.Error) {
if (cause.error === undefined) return { type: "tool.execution", message: cause.message }
// The canonical error is the sole model-visible representation, so a cause