Ver Fonte

wip: app permissions changes

Adam há 3 meses atrás
pai
commit
16c615fd47

+ 5 - 5
packages/app/src/context/global-sync.tsx

@@ -15,7 +15,7 @@ import {
   type McpStatus,
   type LspStatus,
   type VcsInfo,
-  type Permission,
+  type PermissionRequest,
   createOpencodeClient,
 } from "@opencode-ai/sdk/v2/client"
 import { createStore, produce, reconcile } from "solid-js/store"
@@ -46,7 +46,7 @@ type State = {
     [sessionID: string]: Todo[]
   }
   permission: {
-    [sessionID: string]: Permission[]
+    [sessionID: string]: PermissionRequest[]
   }
   mcp: {
     [name: string]: McpStatus
@@ -168,7 +168,7 @@ function createGlobalSync() {
       vcs: () => sdk.vcs.get().then((x) => setStore("vcs", x.data)),
       permission: () =>
         sdk.permission.list().then((x) => {
-          const grouped: Record<string, Permission[]> = {}
+          const grouped: Record<string, PermissionRequest[]> = {}
           for (const perm of x.data ?? []) {
             if (!perm?.id || !perm.sessionID) continue
             const existing = grouped[perm.sessionID]
@@ -349,7 +349,7 @@ function createGlobalSync() {
         setStore("vcs", { branch: event.properties.branch })
         break
       }
-      case "permission.updated": {
+      case "permission.asked": {
         const sessionID = event.properties.sessionID
         const permissions = store.permission[sessionID]
         if (!permissions) {
@@ -375,7 +375,7 @@ function createGlobalSync() {
       case "permission.replied": {
         const permissions = store.permission[event.properties.sessionID]
         if (!permissions) break
-        const result = Binary.search(permissions, event.properties.permissionID, (p) => p.id)
+        const result = Binary.search(permissions, event.properties.requestID, (p) => p.id)
         if (!result.found) break
         setStore(
           "permission",

+ 6 - 8
packages/app/src/context/permission.tsx

@@ -1,7 +1,7 @@
 import { createMemo, onCleanup } from "solid-js"
 import { createStore } from "solid-js/store"
 import { createSimpleContext } from "@opencode-ai/ui/context"
-import type { Permission } from "@opencode-ai/sdk/v2/client"
+import type { PermissionRequest } from "@opencode-ai/sdk/v2/client"
 import { persisted } from "@/utils/persist"
 import { useGlobalSDK } from "@/context/global-sdk"
 import { useGlobalSync } from "./global-sync"
@@ -14,10 +14,8 @@ type PermissionRespondFn = (input: {
   directory?: string
 }) => void
 
-const AUTO_ACCEPT_TYPES = new Set(["edit", "write"])
-
-function shouldAutoAccept(perm: Permission) {
-  return AUTO_ACCEPT_TYPES.has(perm.type)
+function shouldAutoAccept(perm: PermissionRequest) {
+  return perm.permission === "edit"
 }
 
 export const { use: usePermission, provider: PermissionProvider } = createSimpleContext({
@@ -48,7 +46,7 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
       })
     }
 
-    function respondOnce(permission: Permission, directory?: string) {
+    function respondOnce(permission: PermissionRequest, directory?: string) {
       if (responded.has(permission.id)) return
       responded.add(permission.id)
       respond({
@@ -65,7 +63,7 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
 
     const unsubscribe = globalSDK.event.listen((e) => {
       const event = e.details
-      if (event?.type !== "permission.updated") return
+      if (event?.type !== "permission.asked") return
 
       const perm = event.properties
       if (!isAutoAccepting(perm.sessionID)) return
@@ -98,7 +96,7 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
     return {
       ready,
       respond,
-      autoResponds(permission: Permission) {
+      autoResponds(permission: PermissionRequest) {
         return isAutoAccepting(permission.sessionID) && shouldAutoAccept(permission)
       },
       isAutoAccepting,

+ 1 - 1
packages/app/src/pages/layout.tsx

@@ -175,7 +175,7 @@ export default function Layout(props: ParentProps) {
     const permissionAlertCooldownMs = 5000
 
     const unsub = globalSDK.event.listen((e) => {
-      if (e.details?.type !== "permission.updated") return
+      if (e.details?.type !== "permission.asked") return
       const directory = e.name
       const perm = e.details.properties
       if (permission.autoResponds(perm)) return

+ 2 - 2
packages/opencode/src/permission/next.ts

@@ -83,9 +83,9 @@ export namespace PermissionNext {
   })
 
   export const Event = {
-    Asked: BusEvent.define("permission.next.asked", Request),
+    Asked: BusEvent.define("permission.asked", Request),
     Replied: BusEvent.define(
-      "permission.next.replied",
+      "permission.replied",
       z.object({
         sessionID: z.string(),
         requestID: z.string(),

+ 2 - 0
packages/sdk/js/src/v2/gen/sdk.gen.ts

@@ -1596,6 +1596,8 @@ export class Permission extends HeyApiClient {
    * Respond to permission
    *
    * Approve or deny a permission request from the AI assistant.
+   *
+   * @deprecated
    */
   public respond<ThrowOnError extends boolean = false>(
     parameters: {

+ 7 - 38
packages/sdk/js/src/v2/gen/types.gen.ts

@@ -466,47 +466,17 @@ export type PermissionRequest = {
   }
 }
 
-export type EventPermissionNextAsked = {
-  type: "permission.next.asked"
+export type EventPermissionAsked = {
+  type: "permission.asked"
   properties: PermissionRequest
 }
 
-export type EventPermissionNextReplied = {
-  type: "permission.next.replied"
-  properties: {
-    sessionID: string
-    requestID: string
-    reply: "once" | "always" | "reject"
-  }
-}
-
-export type Permission = {
-  id: string
-  type: string
-  pattern?: string | Array<string>
-  sessionID: string
-  messageID: string
-  callID?: string
-  message: string
-  metadata: {
-    [key: string]: unknown
-  }
-  time: {
-    created: number
-  }
-}
-
-export type EventPermissionUpdated = {
-  type: "permission.updated"
-  properties: Permission
-}
-
 export type EventPermissionReplied = {
   type: "permission.replied"
   properties: {
     sessionID: string
-    permissionID: string
-    response: string
+    requestID: string
+    reply: "once" | "always" | "reject"
   }
 }
 
@@ -796,9 +766,7 @@ export type Event =
   | EventMessageRemoved
   | EventMessagePartUpdated
   | EventMessagePartRemoved
-  | EventPermissionNextAsked
-  | EventPermissionNextReplied
-  | EventPermissionUpdated
+  | EventPermissionAsked
   | EventPermissionReplied
   | EventSessionStatus
   | EventSessionIdle
@@ -1248,6 +1216,7 @@ export type PermissionConfig =
       webfetch?: PermissionActionConfig
       websearch?: PermissionActionConfig
       codesearch?: PermissionActionConfig
+      lsp?: PermissionRuleConfig
       doom_loop?: PermissionActionConfig
       [key: string]: PermissionRuleConfig | PermissionActionConfig | undefined
     }
@@ -3457,7 +3426,7 @@ export type PermissionListResponses = {
   /**
    * List of pending permissions
    */
-  200: Array<Permission>
+  200: Array<PermissionRequest>
 }
 
 export type PermissionListResponse = PermissionListResponses[keyof PermissionListResponses]

+ 10 - 9
packages/ui/src/components/message-part.tsx

@@ -455,8 +455,8 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
 
   const permission = createMemo(() => {
     const next = data.store.permission?.[props.message.sessionID]?.[0]
-    if (!next) return undefined
-    if (next.callID !== part.callID) return undefined
+    if (!next || !next.tool) return undefined
+    if (next.tool!.callID !== part.callID) return undefined
     return next
   })
 
@@ -732,19 +732,20 @@ ToolRegistry.register({
 
     const childToolPart = createMemo(() => {
       const perm = childPermission()
-      if (!perm) return undefined
+      if (!perm || !perm.tool) return undefined
       const sessionId = childSessionId()
       if (!sessionId) return undefined
       // Find the tool part that matches the permission's callID
       const messages = data.store.message[sessionId] ?? []
-      for (const msg of messages) {
-        const parts = data.store.part[msg.id] ?? []
-        for (const part of parts) {
-          if (part.type === "tool" && (part as ToolPart).callID === perm.callID) {
-            return { part: part as ToolPart, message: msg }
-          }
+      const message = messages.findLast((m) => m.id === perm.tool!.messageID)
+      if (!message) return undefined
+      const parts = data.store.part[message.id] ?? []
+      for (const part of parts) {
+        if (part.type === "tool" && (part as ToolPart).callID === perm.tool!.callID) {
+          return { part: part as ToolPart, message }
         }
       }
+
       return undefined
     })
 

+ 13 - 11
packages/ui/src/components/session-turn.tsx

@@ -2,7 +2,7 @@ import {
   AssistantMessage,
   Message as MessageType,
   Part as PartType,
-  type Permission,
+  type PermissionRequest,
   TextPart,
   ToolPart,
 } from "@opencode-ai/sdk/v2/client"
@@ -132,7 +132,7 @@ export function SessionTurn(
   const emptyMessages: MessageType[] = []
   const emptyParts: PartType[] = []
   const emptyAssistant: AssistantMessage[] = []
-  const emptyPermissions: Permission[] = []
+  const emptyPermissions: PermissionRequest[] = []
   const emptyPermissionParts: { part: ToolPart; message: AssistantMessage }[] = []
   const idle = { type: "idle" as const }
 
@@ -235,16 +235,18 @@ export function SessionTurn(
     if (props.stepsExpanded) return emptyPermissionParts
 
     const next = nextPermission()
-    if (!next) return emptyPermissionParts
-
-    for (const message of assistantMessages()) {
-      const parts = data.store.part[message.id] ?? emptyParts
-      for (const part of parts) {
-        if (part?.type !== "tool") continue
-        const tool = part as ToolPart
-        if (tool.callID === next.callID) return [{ part: tool, message }]
-      }
+    if (!next || !next.tool) return emptyPermissionParts
+
+    const message = assistantMessages().findLast((m) => m.id === next.tool!.messageID)
+    if (!message) return emptyPermissionParts
+
+    const parts = data.store.part[message.id] ?? emptyParts
+    for (const part of parts) {
+      if (part?.type !== "tool") continue
+      const tool = part as ToolPart
+      if (tool.callID === next.tool?.callID) return [{ part: tool, message }]
     }
+
     return emptyPermissionParts
   })
 

+ 2 - 2
packages/ui/src/context/data.tsx

@@ -1,4 +1,4 @@
-import type { Message, Session, Part, FileDiff, SessionStatus, Permission } from "@opencode-ai/sdk/v2"
+import type { Message, Session, Part, FileDiff, SessionStatus, PermissionRequest } from "@opencode-ai/sdk/v2"
 import { createSimpleContext } from "./helper"
 import { PreloadMultiFileDiffResult } from "@pierre/diffs/ssr"
 
@@ -14,7 +14,7 @@ type Data = {
     [sessionID: string]: PreloadMultiFileDiffResult<any>[]
   }
   permission?: {
-    [sessionID: string]: Permission[]
+    [sessionID: string]: PermissionRequest[]
   }
   message: {
     [sessionID: string]: Message[]