This commit is contained in:
Dax Raad 2026-01-01 16:48:50 -05:00
commit 82a8ba6822
6 changed files with 35 additions and 54 deletions

View file

@ -10,6 +10,7 @@
"options": {}, "options": {},
}, },
}, },
"permission": "ask",
"mcp": { "mcp": {
"context7": { "context7": {
"type": "remote", "type": "remote",

View file

@ -71,19 +71,19 @@ export namespace ACP {
this.config.sdk.event.subscribe({ directory }).then(async (events) => { this.config.sdk.event.subscribe({ directory }).then(async (events) => {
for await (const event of events.stream) { for await (const event of events.stream) {
switch (event.type) { switch (event.type) {
case "permission.updated": case "permission.asked":
try { try {
const permission = event.properties const permission = event.properties
const res = await this.connection const res = await this.connection
.requestPermission({ .requestPermission({
sessionId, sessionId,
toolCall: { toolCall: {
toolCallId: permission.callID ?? permission.id, toolCallId: permission.tool?.callID ?? permission.id,
status: "pending", status: "pending",
title: permission.message, title: permission.permission,
rawInput: permission.metadata, rawInput: permission.metadata,
kind: toToolKind(permission.type), kind: toToolKind(permission.permission),
locations: toLocations(permission.type, permission.metadata), locations: toLocations(permission.permission, permission.metadata),
}, },
options, options,
}) })
@ -93,28 +93,25 @@ export namespace ACP {
permissionID: permission.id, permissionID: permission.id,
sessionID: permission.sessionID, sessionID: permission.sessionID,
}) })
await this.config.sdk.permission.respond({ await this.config.sdk.permission.reply({
sessionID: permission.sessionID, requestID: permission.id,
permissionID: permission.id, reply: "reject",
response: "reject",
directory, directory,
}) })
return return
}) })
if (!res) return if (!res) return
if (res.outcome.outcome !== "selected") { if (res.outcome.outcome !== "selected") {
await this.config.sdk.permission.respond({ await this.config.sdk.permission.reply({
sessionID: permission.sessionID, requestID: permission.id,
permissionID: permission.id, reply: "reject",
response: "reject",
directory, directory,
}) })
return return
} }
await this.config.sdk.permission.respond({ await this.config.sdk.permission.reply({
sessionID: permission.sessionID, requestID: permission.id,
permissionID: permission.id, reply: res.outcome.optionId as "once" | "always" | "reject",
response: res.outcome.optionId as "once" | "always" | "reject",
directory, directory,
}) })
} catch (err) { } catch (err) {

View file

@ -202,7 +202,7 @@ export const RunCommand = cmd({
break break
} }
if (event.type === "permission.next.asked") { if (event.type === "permission.asked") {
const permission = event.properties const permission = event.properties
if (permission.sessionID !== sessionID) continue if (permission.sessionID !== sessionID) continue
const result = await select({ const result = await select({

View file

@ -97,7 +97,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
sdk.event.listen((e) => { sdk.event.listen((e) => {
const event = e.details const event = e.details
switch (event.type) { switch (event.type) {
case "permission.next.replied": { case "permission.replied": {
const requests = store.permission[event.properties.sessionID] const requests = store.permission[event.properties.sessionID]
if (!requests) break if (!requests) break
const match = Binary.search(requests, event.properties.requestID, (r) => r.id) const match = Binary.search(requests, event.properties.requestID, (r) => r.id)
@ -112,7 +112,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
break break
} }
case "permission.next.asked": { case "permission.asked": {
const request = event.properties const request = event.properties
const requests = store.permission[request.sessionID] const requests = store.permission[request.sessionID]
if (!requests) { if (!requests) {

View file

@ -107,8 +107,17 @@ export function Session() {
const { theme } = useTheme() const { theme } = useTheme()
const promptRef = usePromptRef() const promptRef = usePromptRef()
const session = createMemo(() => sync.session.get(route.sessionID)!) const session = createMemo(() => sync.session.get(route.sessionID)!)
const children = createMemo(() => {
const parentID = session()?.parentID ?? session()?.id
return sync.data.session
.filter((x) => x.parentID === parentID || x.id === parentID)
.toSorted((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
})
const messages = createMemo(() => sync.data.message[route.sessionID] ?? []) const messages = createMemo(() => sync.data.message[route.sessionID] ?? [])
const permissions = createMemo(() => sync.data.permission[route.sessionID] ?? []) const permissions = createMemo(() => {
if (session().parentID) return sync.data.permission[route.sessionID] ?? []
return children().flatMap((x) => sync.data.permission[x.id] ?? [])
})
const pending = createMemo(() => { const pending = createMemo(() => {
return messages().findLast((x) => x.role === "assistant" && !x.time.completed)?.id return messages().findLast((x) => x.role === "assistant" && !x.time.completed)?.id
@ -176,28 +185,6 @@ export function Session() {
} }
}) })
// Auto-navigate to whichever session currently needs permission input
createEffect(() => {
const currentSession = session()
if (!currentSession) return
const currentPermissions = permissions()
let targetID = currentPermissions.length > 0 ? currentSession.id : undefined
if (!targetID) {
const child = sync.data.session.find(
(x) => x.parentID === currentSession.id && (sync.data.permission[x.id]?.length ?? 0) > 0,
)
if (child) targetID = child.id
}
if (targetID && targetID !== currentSession.id) {
navigate({
type: "session",
sessionID: targetID,
})
}
})
let scroll: ScrollBoxRenderable let scroll: ScrollBoxRenderable
let prompt: PromptRef let prompt: PromptRef
const keybind = useKeybind() const keybind = useKeybind()
@ -257,18 +244,14 @@ export function Session() {
const local = useLocal() const local = useLocal()
function moveChild(direction: number) { function moveChild(direction: number) {
const parentID = session()?.parentID ?? session()?.id if (children().length === 1) return
let children = sync.data.session let next = children().findIndex((x) => x.id === session()?.id) + direction
.filter((x) => x.parentID === parentID || x.id === parentID) if (next >= children().length) next = 0
.toSorted((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) if (next < 0) next = children().length - 1
if (children.length === 1) return if (children()[next]) {
let next = children.findIndex((x) => x.id === session()?.id) + direction
if (next >= children.length) next = 0
if (next < 0) next = children.length - 1
if (children[next]) {
navigate({ navigate({
type: "session", type: "session",
sessionID: children[next].id, sessionID: children()[next].id,
}) })
} }
} }

View file

@ -646,7 +646,7 @@ export namespace SessionPrompt {
async ask(req) { async ask(req) {
await PermissionNext.ask({ await PermissionNext.ask({
...req, ...req,
sessionID: input.session.parentID ?? input.session.id, sessionID: input.session.id,
tool: { messageID: input.processor.message.id, callID: options.toolCallId }, tool: { messageID: input.processor.message.id, callID: options.toolCallId },
ruleset: PermissionNext.merge(input.agent.permission, input.session.permission ?? []), ruleset: PermissionNext.merge(input.agent.permission, input.session.permission ?? []),
}) })