sync
This commit is contained in:
parent
f77e1d9734
commit
c5d1a51b1f
10 changed files with 94 additions and 21 deletions
|
|
@ -11,7 +11,7 @@ import PROMPT_EXPLORE from "./prompt/explore.txt"
|
|||
import PROMPT_SUMMARY from "./prompt/summary.txt"
|
||||
import PROMPT_TITLE from "./prompt/title.txt"
|
||||
import { PermissionNext } from "@/permission/next"
|
||||
import { mergeDeep } from "remeda"
|
||||
import { mergeDeep, pipe, sortBy, values } from "remeda"
|
||||
|
||||
export namespace Agent {
|
||||
export const Info = z
|
||||
|
|
@ -194,7 +194,12 @@ export namespace Agent {
|
|||
}
|
||||
|
||||
export async function list() {
|
||||
return state().then((x) => Object.values(x))
|
||||
const cfg = await Config.get()
|
||||
return pipe(
|
||||
await state(),
|
||||
values(),
|
||||
sortBy([(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "build"), "desc"]),
|
||||
)
|
||||
}
|
||||
|
||||
export async function defaultAgent() {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
const [agentStore, setAgentStore] = createStore<{
|
||||
current: string
|
||||
}>({
|
||||
current: agents().find((x) => x.default)?.name ?? agents()[0].name,
|
||||
current: agents()[0].name,
|
||||
})
|
||||
const { theme } = useTheme()
|
||||
const colors = createMemo(() => [
|
||||
|
|
|
|||
|
|
@ -125,11 +125,11 @@ export function PermissionPrompt(props: { request: PermissionRequest }) {
|
|||
body={
|
||||
<Switch>
|
||||
<Match when={props.request.always.length === 1 && props.request.always[0] === "*"}>
|
||||
<TextBody title={"Are you sure you want to always allow " + props.request.permission + "?"} />
|
||||
<TextBody title={"This will allow " + props.request.permission + " until OpenCode is restarted."} />
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<box paddingLeft={1} gap={1}>
|
||||
<text fg={theme.textMuted}>Applies to the following patterns</text>
|
||||
<text fg={theme.textMuted}>This will allow the following patterns until OpenCode is restarted</text>
|
||||
<For each={props.request.always}>
|
||||
{(pattern) => (
|
||||
<text fg={theme.text}>
|
||||
|
|
|
|||
|
|
@ -189,7 +189,9 @@ export namespace PermissionNext {
|
|||
action: "allow",
|
||||
})
|
||||
}
|
||||
await Storage.write(["permission", projectID], s.approved)
|
||||
// TODO: we don't save the permission ruleset to disk yet until there's
|
||||
// UI to manage it
|
||||
// await Storage.write(["permission", projectID], s.approved)
|
||||
existing.resolve()
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ export namespace Plugin {
|
|||
const hooks = await state().then((x) => x.hooks)
|
||||
const config = await Config.get()
|
||||
for (const hook of hooks) {
|
||||
// @ts-expect-error this is because we haven't moved plugin to sdk v2
|
||||
await hook.config?.(config)
|
||||
}
|
||||
Bus.subscribeAll(async (input) => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { Command } from "../command"
|
|||
import { Snapshot } from "@/snapshot"
|
||||
|
||||
import type { Provider } from "@/provider/provider"
|
||||
import { PermissionNext } from "@/permission/next"
|
||||
|
||||
export namespace Session {
|
||||
const log = Log.create({ service: "session" })
|
||||
|
|
@ -62,6 +63,7 @@ export namespace Session {
|
|||
compacting: z.number().optional(),
|
||||
archived: z.number().optional(),
|
||||
}),
|
||||
permission: PermissionNext.Ruleset.optional(),
|
||||
revert: z
|
||||
.object({
|
||||
messageID: z.string(),
|
||||
|
|
@ -126,6 +128,7 @@ export namespace Session {
|
|||
.object({
|
||||
parentID: Identifier.schema("session").optional(),
|
||||
title: z.string().optional(),
|
||||
permission: Info.shape.permission,
|
||||
})
|
||||
.optional(),
|
||||
async (input) => {
|
||||
|
|
@ -133,6 +136,7 @@ export namespace Session {
|
|||
parentID: input?.parentID,
|
||||
directory: Instance.directory,
|
||||
title: input?.title,
|
||||
permission: input?.permission,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
|
@ -174,7 +178,13 @@ export namespace Session {
|
|||
})
|
||||
})
|
||||
|
||||
export async function createNext(input: { id?: string; title?: string; parentID?: string; directory: string }) {
|
||||
export async function createNext(input: {
|
||||
id?: string
|
||||
title?: string
|
||||
parentID?: string
|
||||
directory: string
|
||||
permission?: PermissionNext.Ruleset
|
||||
}) {
|
||||
const result: Info = {
|
||||
id: Identifier.descending("session", input.id),
|
||||
version: Installation.VERSION,
|
||||
|
|
@ -182,6 +192,7 @@ export namespace Session {
|
|||
directory: input.directory,
|
||||
parentID: input.parentID,
|
||||
title: input.title ?? createDefaultTitle(!!input.parentID),
|
||||
permission: input.permission,
|
||||
time: {
|
||||
created: Date.now(),
|
||||
updated: Date.now(),
|
||||
|
|
|
|||
|
|
@ -89,7 +89,12 @@ export namespace SessionPrompt {
|
|||
.optional(),
|
||||
agent: z.string().optional(),
|
||||
noReply: z.boolean().optional(),
|
||||
tools: z.record(z.string(), z.boolean()).optional(),
|
||||
tools: z
|
||||
.record(z.string(), z.boolean())
|
||||
.optional()
|
||||
.describe(
|
||||
"@deprecated tools and permissions have been merged, you can set permissions on the session itself now",
|
||||
),
|
||||
system: z.string().optional(),
|
||||
variant: z.string().optional(),
|
||||
parts: z.array(
|
||||
|
|
@ -146,6 +151,23 @@ export namespace SessionPrompt {
|
|||
const message = await createUserMessage(input)
|
||||
await Session.touch(input.sessionID)
|
||||
|
||||
// this is backwards compatibility for allowing `tools` to be specified when
|
||||
// prompting
|
||||
const permissions: PermissionNext.Ruleset = []
|
||||
for (const [tool, enabled] of Object.entries(input.tools ?? {})) {
|
||||
permissions.push({
|
||||
permission: tool,
|
||||
action: enabled ? "allow" : "deny",
|
||||
pattern: "*",
|
||||
})
|
||||
}
|
||||
if (permissions.length > 0) {
|
||||
session.permission = permissions
|
||||
await Session.update(session.id, (draft) => {
|
||||
draft.permission = permissions
|
||||
})
|
||||
}
|
||||
|
||||
if (input.noReply === true) {
|
||||
return message
|
||||
}
|
||||
|
|
@ -372,7 +394,7 @@ export namespace SessionPrompt {
|
|||
await PermissionNext.ask({
|
||||
...req,
|
||||
sessionID: sessionID,
|
||||
ruleset: taskAgent.permission,
|
||||
ruleset: PermissionNext.merge(taskAgent.permission, session.permission ?? []),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
|
@ -626,7 +648,7 @@ export namespace SessionPrompt {
|
|||
...req,
|
||||
sessionID: input.session.parentID ?? input.session.id,
|
||||
tool: { messageID: input.processor.message.id, callID: options.toolCallId },
|
||||
ruleset: input.agent.permission,
|
||||
ruleset: PermissionNext.merge(input.agent.permission, input.session.permission ?? []),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -50,6 +50,28 @@ export const TaskTool = Tool.define("task", async () => {
|
|||
return await Session.create({
|
||||
parentID: ctx.sessionID,
|
||||
title: params.description + ` (@${agent.name} subagent)`,
|
||||
permission: [
|
||||
{
|
||||
permission: "todowrite",
|
||||
pattern: "*",
|
||||
action: "deny",
|
||||
},
|
||||
{
|
||||
permission: "todoread",
|
||||
pattern: "*",
|
||||
action: "deny",
|
||||
},
|
||||
{
|
||||
permission: "task",
|
||||
pattern: "*",
|
||||
action: "deny",
|
||||
},
|
||||
...(config.experimental?.primary_tools?.map((t) => ({
|
||||
pattern: "*",
|
||||
action: "allow" as const,
|
||||
permission: t,
|
||||
})) ?? []),
|
||||
],
|
||||
})
|
||||
})
|
||||
const msg = await MessageV2.get({ sessionID: ctx.sessionID, messageID: ctx.messageID })
|
||||
|
|
@ -112,7 +134,6 @@ export const TaskTool = Tool.define("task", async () => {
|
|||
todoread: false,
|
||||
task: false,
|
||||
...Object.fromEntries((config.experimental?.primary_tools ?? []).map((t) => [t, false])),
|
||||
...agent.tools,
|
||||
},
|
||||
parts: promptParts,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import type {
|
|||
PermissionReplyResponses,
|
||||
PermissionRespondErrors,
|
||||
PermissionRespondResponses,
|
||||
PermissionRuleset,
|
||||
ProjectCurrentResponses,
|
||||
ProjectListResponses,
|
||||
ProjectUpdateErrors,
|
||||
|
|
@ -730,6 +731,7 @@ export class Session extends HeyApiClient {
|
|||
directory?: string
|
||||
parentID?: string
|
||||
title?: string
|
||||
permission?: PermissionRuleset
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
|
|
@ -741,6 +743,7 @@ export class Session extends HeyApiClient {
|
|||
{ in: "query", key: "directory" },
|
||||
{ in: "body", key: "parentID" },
|
||||
{ in: "body", key: "title" },
|
||||
{ in: "body", key: "permission" },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -639,6 +639,16 @@ export type EventCommandExecuted = {
|
|||
}
|
||||
}
|
||||
|
||||
export type PermissionAction = "allow" | "deny" | "ask"
|
||||
|
||||
export type PermissionRule = {
|
||||
permission: string
|
||||
pattern: string
|
||||
action: PermissionAction
|
||||
}
|
||||
|
||||
export type PermissionRuleset = Array<PermissionRule>
|
||||
|
||||
export type Session = {
|
||||
id: string
|
||||
projectID: string
|
||||
|
|
@ -661,6 +671,7 @@ export type Session = {
|
|||
compacting?: number
|
||||
archived?: number
|
||||
}
|
||||
permission?: PermissionRuleset
|
||||
revert?: {
|
||||
messageID: string
|
||||
partID?: string
|
||||
|
|
@ -1895,16 +1906,6 @@ export type File = {
|
|||
status: "added" | "deleted" | "modified"
|
||||
}
|
||||
|
||||
export type PermissionAction = "allow" | "deny" | "ask"
|
||||
|
||||
export type PermissionRule = {
|
||||
permission: string
|
||||
pattern: string
|
||||
action: PermissionAction
|
||||
}
|
||||
|
||||
export type PermissionRuleset = Array<PermissionRule>
|
||||
|
||||
export type Agent = {
|
||||
name: string
|
||||
description?: string
|
||||
|
|
@ -2467,6 +2468,7 @@ export type SessionCreateData = {
|
|||
body?: {
|
||||
parentID?: string
|
||||
title?: string
|
||||
permission?: PermissionRuleset
|
||||
}
|
||||
path?: never
|
||||
query?: {
|
||||
|
|
@ -2982,6 +2984,9 @@ export type SessionPromptData = {
|
|||
}
|
||||
agent?: string
|
||||
noReply?: boolean
|
||||
/**
|
||||
* @deprecated tools and permissions have been merged, you can set permissions on the session itself now
|
||||
*/
|
||||
tools?: {
|
||||
[key: string]: boolean
|
||||
}
|
||||
|
|
@ -3166,6 +3171,9 @@ export type SessionPromptAsyncData = {
|
|||
}
|
||||
agent?: string
|
||||
noReply?: boolean
|
||||
/**
|
||||
* @deprecated tools and permissions have been merged, you can set permissions on the session itself now
|
||||
*/
|
||||
tools?: {
|
||||
[key: string]: boolean
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue