Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Dax
2025-12-07 19:04:14 -05:00
committed by GitHub
parent 6667856ba5
commit ea7ec60f51
59 changed files with 11182 additions and 1114 deletions

View File

@@ -29,7 +29,7 @@ import { MCP } from "@/mcp"
import { Todo } from "@/session/todo"
import { z } from "zod"
import { LoadAPIKeyError } from "ai"
import type { OpencodeClient } from "@opencode-ai/sdk"
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
export namespace ACP {
const log = Log.create({ service: "acp-agent" })
@@ -68,7 +68,7 @@ export namespace ACP {
{ optionId: "always", kind: "allow_always", name: "Always allow" },
{ optionId: "reject", kind: "reject_once", name: "Reject" },
]
this.config.sdk.event.subscribe({ query: { directory } }).then(async (events) => {
this.config.sdk.event.subscribe({ directory }).then(async (events) => {
for await (const event of events.stream) {
switch (event.type) {
case "permission.updated":
@@ -93,32 +93,29 @@ export namespace ACP {
permissionID: permission.id,
sessionID: permission.sessionID,
})
await this.config.sdk.postSessionIdPermissionsPermissionId({
path: { id: permission.sessionID, permissionID: permission.id },
body: {
response: "reject",
},
query: { directory },
await this.config.sdk.permission.respond({
sessionID: permission.sessionID,
permissionID: permission.id,
response: "reject",
directory,
})
return
})
if (!res) return
if (res.outcome.outcome !== "selected") {
await this.config.sdk.postSessionIdPermissionsPermissionId({
path: { id: permission.sessionID, permissionID: permission.id },
body: {
response: "reject",
},
query: { directory },
await this.config.sdk.permission.respond({
sessionID: permission.sessionID,
permissionID: permission.id,
response: "reject",
directory,
})
return
}
await this.config.sdk.postSessionIdPermissionsPermissionId({
path: { id: permission.sessionID, permissionID: permission.id },
body: {
response: res.outcome.optionId as "once" | "always" | "reject",
},
query: { directory },
await this.config.sdk.permission.respond({
sessionID: permission.sessionID,
permissionID: permission.id,
response: res.outcome.optionId as "once" | "always" | "reject",
directory,
})
} catch (err) {
log.error("unexpected error when handling permission", { error: err })
@@ -133,14 +130,14 @@ export namespace ACP {
const { part } = props
const message = await this.config.sdk.session
.message({
throwOnError: true,
path: {
id: part.sessionID,
.message(
{
sessionID: part.sessionID,
messageID: part.messageID,
directory,
},
query: { directory },
})
{ throwOnError: true },
)
.then((x) => x.data)
.catch((err) => {
log.error("unexpected error when fetching message", { error: err })
@@ -420,9 +417,7 @@ export namespace ACP {
const model = await defaultModel(this.config, directory)
const sessionId = params.sessionId
const providers = await this.sdk.config
.providers({ throwOnError: true, query: { directory } })
.then((x) => x.data.providers)
const providers = await this.sdk.config.providers({ directory }).then((x) => x.data!.providers)
const entries = providers.sort((a, b) => {
const nameA = a.name.toLowerCase()
const nameB = b.name.toLowerCase()
@@ -439,22 +434,22 @@ export namespace ACP {
})
const agents = await this.config.sdk.app
.agents({
throwOnError: true,
query: {
.agents(
{
directory,
},
})
.then((resp) => resp.data)
{ throwOnError: true },
)
.then((resp) => resp.data!)
const commands = await this.config.sdk.command
.list({
throwOnError: true,
query: {
.list(
{
directory,
},
})
.then((resp) => resp.data)
{ throwOnError: true },
)
.then((resp) => resp.data!)
const availableCommands = commands.map((command) => ({
name: command.name,
@@ -503,14 +498,14 @@ export namespace ACP {
await Promise.all(
Object.entries(mcpServers).map(async ([key, mcp]) => {
await this.sdk.mcp
.add({
throwOnError: true,
query: { directory },
body: {
.add(
{
directory,
name: key,
config: mcp,
},
})
{ throwOnError: true },
)
.catch((error) => {
log.error("failed to add mcp server", { name: key, error })
})
@@ -559,7 +554,7 @@ export namespace ACP {
async setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse | void> {
this.sessionManager.get(params.sessionId)
await this.config.sdk.app
.agents({ throwOnError: true })
.agents({}, { throwOnError: true })
.then((x) => x.data)
.then((agent) => {
if (!agent) throw new Error(`Agent not found: ${params.modeId}`)
@@ -651,50 +646,42 @@ export namespace ACP {
if (!cmd) {
await this.sdk.session.prompt({
path: { id: sessionID },
body: {
model: {
providerID: model.providerID,
modelID: model.modelID,
},
parts,
agent,
},
query: {
directory,
sessionID,
model: {
providerID: model.providerID,
modelID: model.modelID,
},
parts,
agent,
directory,
})
return done
}
const command = await this.config.sdk.command
.list({ throwOnError: true, query: { directory } })
.then((x) => x.data.find((c) => c.name === cmd.name))
.list({ directory }, { throwOnError: true })
.then((x) => x.data!.find((c) => c.name === cmd.name))
if (command) {
await this.sdk.session.command({
path: { id: sessionID },
body: {
command: command.name,
arguments: cmd.args,
model: model.providerID + "/" + model.modelID,
agent,
},
query: {
directory,
},
sessionID,
command: command.name,
arguments: cmd.args,
model: model.providerID + "/" + model.modelID,
agent,
directory,
})
return done
}
switch (cmd.name) {
case "compact":
await this.config.sdk.session.summarize({
path: { id: sessionID },
throwOnError: true,
query: {
await this.config.sdk.session.summarize(
{
sessionID,
directory,
},
})
{ throwOnError: true },
)
break
}
@@ -703,13 +690,13 @@ export namespace ACP {
async cancel(params: CancelNotification) {
const session = this.sessionManager.get(params.sessionId)
await this.config.sdk.session.abort({
path: { id: params.sessionId },
throwOnError: true,
query: {
await this.config.sdk.session.abort(
{
sessionID: params.sessionId,
directory: session.cwd,
},
})
{ throwOnError: true },
)
}
}
@@ -766,10 +753,10 @@ export namespace ACP {
if (configured) return configured
const model = await sdk.config
.get({ throwOnError: true, query: { directory: cwd } })
.get({ directory: cwd }, { throwOnError: true })
.then((resp) => {
const cfg = resp.data
if (!cfg.model) return undefined
if (!cfg || !cfg.model) return undefined
const parsed = Provider.parseModel(cfg.model)
return {
providerID: parsed.providerID,

View File

@@ -1,7 +1,7 @@
import { RequestError, type McpServer } from "@agentclientprotocol/sdk"
import type { ACPSessionState } from "./types"
import { Log } from "@/util/log"
import type { OpencodeClient } from "@opencode-ai/sdk"
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
const log = Log.create({ service: "acp-session-manager" })
@@ -15,16 +15,14 @@ export class ACPSessionManager {
async create(cwd: string, mcpServers: McpServer[], model?: ACPSessionState["model"]): Promise<ACPSessionState> {
const session = await this.sdk.session
.create({
body: {
.create(
{
title: `ACP Session ${crypto.randomUUID()}`,
},
query: {
directory: cwd,
},
throwOnError: true,
})
.then((x) => x.data)
{ throwOnError: true },
)
.then((x) => x.data!)
const sessionId = session.id
const resolvedModel = model

View File

@@ -1,5 +1,5 @@
import type { McpServer } from "@agentclientprotocol/sdk"
import type { OpencodeClient } from "@opencode-ai/sdk"
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
export interface ACPSessionState {
id: string

View File

@@ -4,7 +4,7 @@ import { cmd } from "./cmd"
import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
import { ACP } from "@/acp/agent"
import { Server } from "@/server/server"
import { createOpencodeClient } from "@opencode-ai/sdk"
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
const log = Log.create({ service: "acp-command" })

View File

@@ -7,7 +7,7 @@ import { bootstrap } from "../bootstrap"
import { Command } from "../../command"
import { EOL } from "os"
import { select } from "@clack/prompts"
import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk"
import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2"
import { Server } from "../../server/server"
import { Provider } from "../../provider/provider"
@@ -212,9 +212,10 @@ export const RunCommand = cmd({
initialValue: "once",
}).catch(() => "reject")
const response = (result.toString().includes("cancel") ? "reject" : result) as "once" | "always" | "reject"
await sdk.postSessionIdPermissionsPermissionId({
path: { id: sessionID, permissionID: permission.id },
body: { response },
await sdk.permission.respond({
sessionID,
permissionID: permission.id,
response,
})
}
}
@@ -222,23 +223,19 @@ export const RunCommand = cmd({
if (args.command) {
await sdk.session.command({
path: { id: sessionID },
body: {
agent: args.agent || "build",
model: args.model,
command: args.command,
arguments: message,
},
sessionID,
agent: args.agent || "build",
model: args.model,
command: args.command,
arguments: message,
})
} else {
const modelParam = args.model ? Provider.parseModel(args.model) : undefined
await sdk.session.prompt({
path: { id: sessionID },
body: {
agent: args.agent || "build",
model: modelParam,
parts: [...fileParts, { type: "text", text: message }],
},
sessionID,
agent: args.agent || "build",
model: modelParam,
parts: [...fileParts, { type: "text", text: message }],
})
}
@@ -263,7 +260,7 @@ export const RunCommand = cmd({
: args.title
: undefined
const result = await sdk.session.create({ body: title ? { title } : {} })
const result = await sdk.session.create(title ? { title } : {})
return result.data?.id
})()
@@ -274,7 +271,7 @@ export const RunCommand = cmd({
const cfgResult = await sdk.config.get()
if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) {
const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => {
const shareResult = await sdk.session.share({ sessionID }).catch((error) => {
if (error instanceof Error && error.message.includes("disabled")) {
UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message)
}
@@ -315,7 +312,7 @@ export const RunCommand = cmd({
: args.title
: undefined
const result = await sdk.session.create({ body: title ? { title } : {} })
const result = await sdk.session.create(title ? { title } : {})
return result.data?.id
})()
@@ -327,7 +324,7 @@ export const RunCommand = cmd({
const cfgResult = await sdk.config.get()
if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) {
const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => {
const shareResult = await sdk.session.share({ sessionID }).catch((error) => {
if (error instanceof Error && error.message.includes("disabled")) {
UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message)
}

View File

@@ -11,7 +11,7 @@ import {
} from "solid-js"
import { useKeyboard } from "@opentui/solid"
import { useKeybind } from "@tui/context/keybind"
import type { KeybindsConfig } from "@opencode-ai/sdk"
import type { KeybindsConfig } from "@opencode-ai/sdk/v2"
type Context = ReturnType<typeof init>
const ctx = createContext<Context>()

View File

@@ -7,7 +7,7 @@ import { useSDK } from "../context/sdk"
import { DialogPrompt } from "../ui/dialog-prompt"
import { useTheme } from "../context/theme"
import { TextAttributes } from "@opentui/core"
import type { ProviderAuthAuthorization } from "@opencode-ai/sdk"
import type { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2"
import { DialogModel } from "./dialog-model"
const PROVIDER_PRIORITY: Record<string, number> = {
@@ -64,12 +64,8 @@ export function createDialogProviderOptions() {
const method = methods[index]
if (method.type === "oauth") {
const result = await sdk.client.provider.oauth.authorize({
path: {
id: provider.id,
},
body: {
method: index,
},
providerID: provider.id,
method: index,
})
if (result.data?.method === "code") {
dialog.replace(() => (
@@ -111,12 +107,8 @@ function AutoMethod(props: AutoMethodProps) {
onMount(async () => {
const result = await sdk.client.provider.oauth.callback({
path: {
id: props.providerID,
},
body: {
method: props.index,
},
providerID: props.providerID,
method: props.index,
})
if (result.error) {
dialog.clear()
@@ -161,13 +153,9 @@ function CodeMethod(props: CodeMethodProps) {
placeholder="Authorization code"
onConfirm={async (value) => {
const { error } = await sdk.client.provider.oauth.callback({
path: {
id: props.providerID,
},
body: {
method: props.index,
code: value,
},
providerID: props.providerID,
method: props.index,
code: value,
})
if (!error) {
await sdk.client.instance.dispose()
@@ -219,10 +207,8 @@ function ApiMethod(props: ApiMethodProps) {
onConfirm={async (value) => {
if (!value) return
sdk.client.auth.set({
path: {
id: props.providerID,
},
body: {
providerID: props.providerID,
auth: {
type: "api",
key: value,
},

View File

@@ -74,9 +74,7 @@ export function DialogSessionList() {
onTrigger: async (option) => {
if (toDelete() === option.value) {
sdk.client.session.delete({
path: {
id: option.value,
},
sessionID: option.value,
})
setToDelete(undefined)
// dialog.clear()

View File

@@ -20,12 +20,8 @@ export function DialogSessionRename(props: DialogSessionRenameProps) {
value={session()?.title}
onConfirm={(value) => {
sdk.client.session.update({
path: {
id: props.session,
},
body: {
title: value,
},
sessionID: props.session,
title: value,
})
dialog.clear()
}}

View File

@@ -16,9 +16,7 @@ export function DialogTag(props: { onSelect?: (value: string) => void }) {
() => [store.filter],
async () => {
const result = await sdk.client.find.files({
query: {
query: store.filter,
},
query: store.filter,
})
if (result.error) return []
const sliced = (result.data ?? []).slice(0, 5)

View File

@@ -140,9 +140,7 @@ export function Autocomplete(props: {
// Get files from SDK
const result = await sdk.client.find.files({
query: {
query: query ?? "",
},
query: query ?? "",
})
const options: AutocompleteOption[] = []

View File

@@ -5,7 +5,7 @@ import { createStore, produce } from "solid-js/store"
import { clone } from "remeda"
import { createSimpleContext } from "../../context/helper"
import { appendFile, writeFile } from "fs/promises"
import type { AgentPart, FilePart, TextPart } from "@opencode-ai/sdk"
import type { AgentPart, FilePart, TextPart } from "@opencode-ai/sdk/v2"
export type PromptInfo = {
input: string

View File

@@ -17,7 +17,7 @@ import { useRenderer } from "@opentui/solid"
import { Editor } from "@tui/util/editor"
import { useExit } from "../../context/exit"
import { Clipboard } from "../../util/clipboard"
import type { FilePart } from "@opencode-ai/sdk"
import type { FilePart } from "@opencode-ai/sdk/v2"
import { TuiEvent } from "../../event"
import { iife } from "@/util/iife"
import { Locale } from "@/util/locale"
@@ -170,9 +170,7 @@ export function Prompt(props: PromptProps) {
if (store.interrupt >= 2) {
sdk.client.session.abort({
path: {
id: props.sessionID,
},
sessionID: props.sessionID,
})
setStore("interrupt", 0)
}
@@ -447,17 +445,13 @@ export function Prompt(props: PromptProps) {
if (store.mode === "shell") {
sdk.client.session.shell({
path: {
id: sessionID,
},
body: {
agent: local.agent.current().name,
model: {
providerID: selectedModel.providerID,
modelID: selectedModel.modelID,
},
command: inputText,
sessionID,
agent: local.agent.current().name,
model: {
providerID: selectedModel.providerID,
modelID: selectedModel.modelID,
},
command: inputText,
})
setStore("mode", "normal")
} else if (
@@ -470,39 +464,31 @@ export function Prompt(props: PromptProps) {
) {
let [command, ...args] = inputText.split(" ")
sdk.client.session.command({
path: {
id: sessionID,
},
body: {
command: command.slice(1),
arguments: args.join(" "),
agent: local.agent.current().name,
model: `${selectedModel.providerID}/${selectedModel.modelID}`,
messageID,
},
sessionID,
command: command.slice(1),
arguments: args.join(" "),
agent: local.agent.current().name,
model: `${selectedModel.providerID}/${selectedModel.modelID}`,
messageID,
})
} else {
sdk.client.session.prompt({
path: {
id: sessionID,
},
body: {
...selectedModel,
messageID,
agent: local.agent.current().name,
model: selectedModel,
parts: [
{
id: Identifier.ascending("part"),
type: "text",
text: inputText,
},
...nonTextParts.map((x) => ({
id: Identifier.ascending("part"),
...x,
})),
],
},
sessionID,
...selectedModel,
messageID,
agent: local.agent.current().name,
model: selectedModel,
parts: [
{
id: Identifier.ascending("part"),
type: "text",
text: inputText,
},
...nonTextParts.map((x) => ({
id: Identifier.ascending("part"),
...x,
})),
],
})
}
history.append(store.prompt)

View File

@@ -2,7 +2,7 @@ import { createMemo } from "solid-js"
import { useSync } from "@tui/context/sync"
import { Keybind } from "@/util/keybind"
import { pipe, mapValues } from "remeda"
import type { KeybindsConfig } from "@opencode-ai/sdk"
import type { KeybindsConfig } from "@opencode-ai/sdk/v2"
import type { ParsedKey, Renderable } from "@opentui/core"
import { createStore } from "solid-js/store"
import { useKeyboard, useRenderer } from "@opentui/solid"

View File

@@ -1,4 +1,4 @@
import { createOpencodeClient, type Event } from "@opencode-ai/sdk"
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2"
import { createSimpleContext } from "./helper"
import { createGlobalEmitter } from "@solid-primitives/event-bus"
import { batch, onCleanup, onMount } from "solid-js"
@@ -20,9 +20,12 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
onMount(async () => {
while (true) {
if (abort.signal.aborted) break
const events = await sdk.event.subscribe({
signal: abort.signal,
})
const events = await sdk.event.subscribe(
{},
{
signal: abort.signal,
},
)
let queue: Event[] = []
let timer: Timer | undefined
let last = 0

View File

@@ -15,7 +15,7 @@ import type {
ProviderListResponse,
ProviderAuthMethod,
VcsInfo,
} from "@opencode-ai/sdk"
} from "@opencode-ai/sdk/v2"
import { createStore, produce, reconcile } from "solid-js/store"
import { useSDK } from "@tui/context/sdk"
import { Binary } from "@opencode-ai/util/binary"
@@ -255,19 +255,19 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
async function bootstrap() {
// blocking
await Promise.all([
sdk.client.config.providers({ throwOnError: true }).then((x) => {
sdk.client.config.providers({}, { throwOnError: true }).then((x) => {
batch(() => {
setStore("provider", x.data!.providers)
setStore("provider_default", x.data!.default)
})
}),
sdk.client.provider.list({ throwOnError: true }).then((x) => {
sdk.client.provider.list({}, { throwOnError: true }).then((x) => {
batch(() => {
setStore("provider_next", x.data!)
})
}),
sdk.client.app.agents({ throwOnError: true }).then((x) => setStore("agent", x.data ?? [])),
sdk.client.config.get({ throwOnError: true }).then((x) => setStore("config", x.data!)),
sdk.client.app.agents({}, { throwOnError: true }).then((x) => setStore("agent", x.data ?? [])),
sdk.client.config.get({}, { throwOnError: true }).then((x) => setStore("config", x.data!)),
])
.then(() => {
if (store.status !== "complete") setStore("status", "partial")
@@ -333,10 +333,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
async sync(sessionID: string) {
if (fullSyncedSessions.has(sessionID)) return
const [session, messages, todo, diff] = await Promise.all([
sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }),
sdk.client.session.messages({ path: { id: sessionID }, query: { limit: 100 } }),
sdk.client.session.todo({ path: { id: sessionID } }),
sdk.client.session.diff({ path: { id: sessionID } }),
sdk.client.session.get({ sessionID }, { throwOnError: true }),
sdk.client.session.messages({ sessionID, limit: 100 }),
sdk.client.session.todo({ sessionID }),
sdk.client.session.diff({ sessionID }),
])
setStore(
produce((draft) => {

View File

@@ -29,12 +29,8 @@ export function DialogMessage(props: {
if (!msg) return
sdk.client.session.revert({
path: {
id: props.sessionID,
},
body: {
messageID: msg.id,
},
sessionID: props.sessionID,
messageID: msg.id,
})
if (props.setPrompt) {
@@ -81,12 +77,8 @@ export function DialogMessage(props: {
description: "create a new session",
onSelect: async (dialog) => {
const result = await sdk.client.session.fork({
path: {
id: props.sessionID,
},
body: {
messageID: props.messageID,
},
sessionID: props.sessionID,
messageID: props.messageID,
})
route.navigate({
sessionID: result.data!.id,

View File

@@ -1,7 +1,7 @@
import { createMemo, onMount } from "solid-js"
import { useSync } from "@tui/context/sync"
import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
import type { TextPart } from "@opencode-ai/sdk"
import type { TextPart } from "@opencode-ai/sdk/v2"
import { Locale } from "@/util/locale"
import { DialogMessage } from "./dialog-message"
import { useDialog } from "../../ui/dialog"

View File

@@ -4,7 +4,7 @@ import { useSync } from "@tui/context/sync"
import { pipe, sumBy } from "remeda"
import { useTheme } from "@tui/context/theme"
import { SplitBorder, EmptyBorder } from "@tui/component/border"
import type { AssistantMessage, Session } from "@opencode-ai/sdk"
import type { AssistantMessage, Session } from "@opencode-ai/sdk/v2"
import { useDirectory } from "../../context/directory"
import { useKeybind } from "../../context/keybind"

View File

@@ -25,7 +25,7 @@ import {
type ScrollAcceleration,
} from "@opentui/core"
import { Prompt, type PromptRef } from "@tui/component/prompt"
import type { AssistantMessage, Part, ToolPart, UserMessage, TextPart, ReasoningPart } from "@opencode-ai/sdk"
import type { AssistantMessage, Part, ToolPart, UserMessage, TextPart, ReasoningPart } from "@opencode-ai/sdk/v2"
import { useLocal } from "@tui/context/local"
import { Locale } from "@/util/locale"
import type { Tool } from "@/tool/tool"
@@ -150,7 +150,8 @@ export function Session() {
.then(() => {
if (scroll) scroll.scrollBy(100_000)
})
.catch(() => {
.catch((e) => {
console.error(e)
toast.show({
message: `Session not found: ${route.sessionID}`,
variant: "error",
@@ -202,14 +203,10 @@ export function Session() {
return
})
if (response) {
sdk.client.postSessionIdPermissionsPermissionId({
path: {
permissionID: first.id,
id: route.sessionID,
},
body: {
response: response,
},
sdk.client.permission.respond({
permissionID: first.id,
sessionID: route.sessionID,
response: response,
})
}
}
@@ -254,9 +251,7 @@ export function Session() {
onSelect: async (dialog: any) => {
await sdk.client.session
.share({
path: {
id: route.sessionID,
},
sessionID: route.sessionID,
})
.then((res) =>
Clipboard.copy(res.data!.share!.url).catch(() =>
@@ -314,13 +309,9 @@ export function Session() {
return
}
sdk.client.session.summarize({
path: {
id: route.sessionID,
},
body: {
modelID: selectedModel.modelID,
providerID: selectedModel.providerID,
},
sessionID: route.sessionID,
modelID: selectedModel.modelID,
providerID: selectedModel.providerID,
})
dialog.clear()
},
@@ -333,9 +324,7 @@ export function Session() {
category: "Session",
onSelect: (dialog) => {
sdk.client.session.unshare({
path: {
id: route.sessionID,
},
sessionID: route.sessionID,
})
dialog.clear()
},
@@ -347,18 +336,14 @@ export function Session() {
category: "Session",
onSelect: async (dialog) => {
const status = sync.data.session_status[route.sessionID]
if (status?.type !== "idle") await sdk.client.session.abort({ path: { id: route.sessionID } }).catch(() => {})
if (status?.type !== "idle") await sdk.client.session.abort({ sessionID: route.sessionID }).catch(() => {})
const revert = session().revert?.messageID
const message = messages().findLast((x) => (!revert || x.id < revert) && x.role === "user")
if (!message) return
sdk.client.session
.revert({
path: {
id: route.sessionID,
},
body: {
messageID: message.id,
},
sessionID: route.sessionID,
messageID: message.id,
})
.then(() => {
toBottom()
@@ -392,20 +377,14 @@ export function Session() {
const message = messages().find((x) => x.role === "user" && x.id > messageID)
if (!message) {
sdk.client.session.unrevert({
path: {
id: route.sessionID,
},
sessionID: route.sessionID,
})
prompt.set({ input: "", parts: [] })
return
}
sdk.client.session.revert({
path: {
id: route.sessionID,
},
body: {
messageID: message.id,
},
sessionID: route.sessionID,
messageID: message.id,
})
},
},
@@ -1066,7 +1045,7 @@ function UserMessage(props: {
</box>
</Show>
<text fg={theme.textMuted}>
{ctx.usernameVisible() ? `${sync.data.config.username ?? "You"}` : "You"}
{ctx.usernameVisible() ? `${sync.data.config.username ?? "You "}` : "You "}
<Show
when={queued()}
fallback={

View File

@@ -4,7 +4,7 @@ import { createStore } from "solid-js/store"
import { useTheme } from "../../context/theme"
import { Locale } from "@/util/locale"
import path from "path"
import type { AssistantMessage } from "@opencode-ai/sdk"
import type { AssistantMessage } from "@opencode-ai/sdk/v2"
import { Global } from "@/global"
import { Installation } from "@/installation"
import { useKeybind } from "../../context/keybind"

View File

@@ -61,10 +61,14 @@ export namespace Plugin {
for (const hook of await state().then((x) => x.hooks)) {
const fn = hook[name]
if (!fn) continue
// @ts-expect-error if you feel adventurous, please fix the typing, make sure to bump the try-counter if you
// give up.
// try-counter: 2
await fn(input, output)
try {
// @ts-expect-error if you feel adventurous, please fix the typing, make sure to bump the try-counter if you
// give up.
// try-counter: 2
await fn(input, output)
} catch (e) {
log.error("failed to trigger hook", { name, error: e })
}
}
return output
}

View File

@@ -209,7 +209,7 @@ export namespace Server {
},
)
.get(
"/pty/:id",
"/pty/:ptyID",
describeRoute({
description: "Get PTY session info",
operationId: "pty.get",
@@ -225,9 +225,9 @@ export namespace Server {
...errors(404),
},
}),
validator("param", z.object({ id: z.string() })),
validator("param", z.object({ ptyID: z.string() })),
async (c) => {
const info = Pty.get(c.req.valid("param").id)
const info = Pty.get(c.req.valid("param").ptyID)
if (!info) {
throw new Storage.NotFoundError({ message: "Session not found" })
}
@@ -235,7 +235,7 @@ export namespace Server {
},
)
.put(
"/pty/:id",
"/pty/:ptyID",
describeRoute({
description: "Update PTY session",
operationId: "pty.update",
@@ -251,15 +251,15 @@ export namespace Server {
...errors(400),
},
}),
validator("param", z.object({ id: z.string() })),
validator("param", z.object({ ptyID: z.string() })),
validator("json", Pty.UpdateInput),
async (c) => {
const info = await Pty.update(c.req.valid("param").id, c.req.valid("json"))
const info = await Pty.update(c.req.valid("param").ptyID, c.req.valid("json"))
return c.json(info)
},
)
.delete(
"/pty/:id",
"/pty/:ptyID",
describeRoute({
description: "Remove a PTY session",
operationId: "pty.remove",
@@ -275,14 +275,14 @@ export namespace Server {
...errors(404),
},
}),
validator("param", z.object({ id: z.string() })),
validator("param", z.object({ ptyID: z.string() })),
async (c) => {
await Pty.remove(c.req.valid("param").id)
await Pty.remove(c.req.valid("param").ptyID)
return c.json(true)
},
)
.get(
"/pty/:id/connect",
"/pty/:ptyID/connect",
describeRoute({
description: "Connect to a PTY session",
operationId: "pty.connect",
@@ -298,9 +298,9 @@ export namespace Server {
...errors(404),
},
}),
validator("param", z.object({ id: z.string() })),
validator("param", z.object({ ptyID: z.string() })),
upgradeWebSocket((c) => {
const id = c.req.param("id")
const id = c.req.param("ptyID")
let handler: ReturnType<typeof Pty.connect>
if (!Pty.get(id)) throw new Error("Session not found")
return {
@@ -557,7 +557,7 @@ export namespace Server {
},
)
.get(
"/session/:id",
"/session/:sessionID",
describeRoute({
description: "Get session",
operationId: "session.get",
@@ -576,17 +576,18 @@ export namespace Server {
validator(
"param",
z.object({
id: Session.get.schema,
sessionID: Session.get.schema,
}),
),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
log.info("SEARCH", { url: c.req.url })
const session = await Session.get(sessionID)
return c.json(session)
},
)
.get(
"/session/:id/children",
"/session/:sessionID/children",
describeRoute({
description: "Get a session's children",
operationId: "session.children",
@@ -605,17 +606,17 @@ export namespace Server {
validator(
"param",
z.object({
id: Session.children.schema,
sessionID: Session.children.schema,
}),
),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const session = await Session.children(sessionID)
return c.json(session)
},
)
.get(
"/session/:id/todo",
"/session/:sessionID/todo",
describeRoute({
description: "Get the todo list for a session",
operationId: "session.todo",
@@ -634,11 +635,11 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const todos = await Todo.get(sessionID)
return c.json(todos)
},
@@ -668,7 +669,7 @@ export namespace Server {
},
)
.delete(
"/session/:id",
"/session/:sessionID",
describeRoute({
description: "Delete a session and all its data",
operationId: "session.delete",
@@ -687,11 +688,11 @@ export namespace Server {
validator(
"param",
z.object({
id: Session.remove.schema,
sessionID: Session.remove.schema,
}),
),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
await Session.remove(sessionID)
await Bus.publish(TuiEvent.CommandExecute, {
command: "session.list",
@@ -700,7 +701,7 @@ export namespace Server {
},
)
.patch(
"/session/:id",
"/session/:sessionID",
describeRoute({
description: "Update session properties",
operationId: "session.update",
@@ -719,7 +720,7 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string(),
sessionID: z.string(),
}),
),
validator(
@@ -729,7 +730,7 @@ export namespace Server {
}),
),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const updates = c.req.valid("json")
const updatedSession = await Session.update(sessionID, (session) => {
@@ -742,7 +743,7 @@ export namespace Server {
},
)
.post(
"/session/:id/init",
"/session/:sessionID/init",
describeRoute({
description: "Analyze the app and create an AGENTS.md file",
operationId: "session.init",
@@ -761,19 +762,19 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
validator("json", Session.initialize.schema.omit({ sessionID: true })),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
await Session.initialize({ ...body, sessionID })
return c.json(true)
},
)
.post(
"/session/:id/fork",
"/session/:sessionID/fork",
describeRoute({
description: "Fork an existing session at a specific message",
operationId: "session.fork",
@@ -791,19 +792,19 @@ export namespace Server {
validator(
"param",
z.object({
id: Session.fork.schema.shape.sessionID,
sessionID: Session.fork.schema.shape.sessionID,
}),
),
validator("json", Session.fork.schema.omit({ sessionID: true })),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
const result = await Session.fork({ ...body, sessionID })
return c.json(result)
},
)
.post(
"/session/:id/abort",
"/session/:sessionID/abort",
describeRoute({
description: "Abort a session",
operationId: "session.abort",
@@ -822,16 +823,16 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string(),
sessionID: z.string(),
}),
),
async (c) => {
SessionPrompt.cancel(c.req.valid("param").id)
SessionPrompt.cancel(c.req.valid("param").sessionID)
return c.json(true)
},
)
.post(
"/session/:id/share",
"/session/:sessionID/share",
describeRoute({
description: "Share a session",
operationId: "session.share",
@@ -850,18 +851,18 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string(),
sessionID: z.string(),
}),
),
async (c) => {
const id = c.req.valid("param").id
await Session.share(id)
const session = await Session.get(id)
const sessionID = c.req.valid("param").sessionID
await Session.share(sessionID)
const session = await Session.get(sessionID)
return c.json(session)
},
)
.get(
"/session/:id/diff",
"/session/:sessionID/diff",
describeRoute({
description: "Get the diff that resulted from this user message",
operationId: "session.diff",
@@ -879,7 +880,7 @@ export namespace Server {
validator(
"param",
z.object({
id: SessionSummary.diff.schema.shape.sessionID,
sessionID: SessionSummary.diff.schema.shape.sessionID,
}),
),
validator(
@@ -892,14 +893,14 @@ export namespace Server {
const query = c.req.valid("query")
const params = c.req.valid("param")
const result = await SessionSummary.diff({
sessionID: params.id,
sessionID: params.sessionID,
messageID: query.messageID,
})
return c.json(result)
},
)
.delete(
"/session/:id/share",
"/session/:sessionID/share",
describeRoute({
description: "Unshare the session",
operationId: "session.unshare",
@@ -918,18 +919,18 @@ export namespace Server {
validator(
"param",
z.object({
id: Session.unshare.schema,
sessionID: Session.unshare.schema,
}),
),
async (c) => {
const id = c.req.valid("param").id
await Session.unshare(id)
const session = await Session.get(id)
const sessionID = c.req.valid("param").sessionID
await Session.unshare(sessionID)
const session = await Session.get(sessionID)
return c.json(session)
},
)
.post(
"/session/:id/summarize",
"/session/:sessionID/summarize",
describeRoute({
description: "Summarize the session",
operationId: "session.summarize",
@@ -948,7 +949,7 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
validator(
@@ -959,9 +960,9 @@ export namespace Server {
}),
),
async (c) => {
const id = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
const msgs = await Session.messages({ sessionID: id })
const msgs = await Session.messages({ sessionID })
let currentAgent = "build"
for (let i = msgs.length - 1; i >= 0; i--) {
const info = msgs[i].info
@@ -971,7 +972,7 @@ export namespace Server {
}
}
await SessionCompaction.create({
sessionID: id,
sessionID,
agent: currentAgent,
model: {
providerID: body.providerID,
@@ -979,12 +980,12 @@ export namespace Server {
},
auto: false,
})
await SessionPrompt.loop(id)
await SessionPrompt.loop(sessionID)
return c.json(true)
},
)
.get(
"/session/:id/message",
"/session/:sessionID/message",
describeRoute({
description: "List messages for a session",
operationId: "session.messages",
@@ -1003,7 +1004,7 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
validator(
@@ -1015,14 +1016,14 @@ export namespace Server {
async (c) => {
const query = c.req.valid("query")
const messages = await Session.messages({
sessionID: c.req.valid("param").id,
sessionID: c.req.valid("param").sessionID,
limit: query.limit,
})
return c.json(messages)
},
)
.get(
"/session/:id/diff",
"/session/:sessionID/diff",
describeRoute({
description: "Get the diff for this session",
operationId: "session.diff",
@@ -1041,16 +1042,16 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
async (c) => {
const diff = await Session.diff(c.req.valid("param").id)
const diff = await Session.diff(c.req.valid("param").sessionID)
return c.json(diff)
},
)
.get(
"/session/:id/message/:messageID",
"/session/:sessionID/message/:messageID",
describeRoute({
description: "Get a message from a session",
operationId: "session.message",
@@ -1074,21 +1075,21 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
messageID: z.string().meta({ description: "Message ID" }),
}),
),
async (c) => {
const params = c.req.valid("param")
const message = await MessageV2.get({
sessionID: params.id,
sessionID: params.sessionID,
messageID: params.messageID,
})
return c.json(message)
},
)
.post(
"/session/:id/message",
"/session/:sessionID/message",
describeRoute({
description: "Create and send a new message to a session",
operationId: "session.prompt",
@@ -1112,7 +1113,7 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
@@ -1120,7 +1121,7 @@ export namespace Server {
c.status(200)
c.header("Content-Type", "application/json")
return stream(c, async (stream) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
const msg = await SessionPrompt.prompt({ ...body, sessionID })
stream.write(JSON.stringify(msg))
@@ -1128,7 +1129,7 @@ export namespace Server {
},
)
.post(
"/session/:id/prompt_async",
"/session/:sessionID/prompt_async",
describeRoute({
description: "Create and send a new message to a session, start if needed and return immediately",
operationId: "session.prompt_async",
@@ -1142,7 +1143,7 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
@@ -1150,14 +1151,14 @@ export namespace Server {
c.status(204)
c.header("Content-Type", "application/json")
return stream(c, async () => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
SessionPrompt.prompt({ ...body, sessionID })
})
},
)
.post(
"/session/:id/command",
"/session/:sessionID/command",
describeRoute({
description: "Send a new command to a session",
operationId: "session.command",
@@ -1181,19 +1182,19 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
validator("json", SessionPrompt.CommandInput.omit({ sessionID: true })),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
const msg = await SessionPrompt.command({ ...body, sessionID })
return c.json(msg)
},
)
.post(
"/session/:id/shell",
"/session/:sessionID/shell",
describeRoute({
description: "Run a shell command",
operationId: "session.shell",
@@ -1212,19 +1213,19 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
sessionID: z.string().meta({ description: "Session ID" }),
}),
),
validator("json", SessionPrompt.ShellInput.omit({ sessionID: true })),
async (c) => {
const sessionID = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
const msg = await SessionPrompt.shell({ ...body, sessionID })
return c.json(msg)
},
)
.post(
"/session/:id/revert",
"/session/:sessionID/revert",
describeRoute({
description: "Revert a message",
operationId: "session.revert",
@@ -1243,22 +1244,22 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string(),
sessionID: z.string(),
}),
),
validator("json", SessionRevert.RevertInput.omit({ sessionID: true })),
async (c) => {
const id = c.req.valid("param").id
const sessionID = c.req.valid("param").sessionID
log.info("revert", c.req.valid("json"))
const session = await SessionRevert.revert({
sessionID: id,
sessionID,
...c.req.valid("json"),
})
return c.json(session)
},
)
.post(
"/session/:id/unrevert",
"/session/:sessionID/unrevert",
describeRoute({
description: "Restore all reverted messages",
operationId: "session.unrevert",
@@ -1277,19 +1278,20 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string(),
sessionID: z.string(),
}),
),
async (c) => {
const id = c.req.valid("param").id
const session = await SessionRevert.unrevert({ sessionID: id })
const sessionID = c.req.valid("param").sessionID
const session = await SessionRevert.unrevert({ sessionID })
return c.json(session)
},
)
.post(
"/session/:id/permissions/:permissionID",
"/session/:sessionID/permissions/:permissionID",
describeRoute({
description: "Respond to a permission request",
operationId: "permission.respond",
responses: {
200: {
description: "Permission processed successfully",
@@ -1305,17 +1307,17 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string(),
sessionID: z.string(),
permissionID: z.string(),
}),
),
validator("json", z.object({ response: Permission.Response })),
async (c) => {
const params = c.req.valid("param")
const id = params.id
const sessionID = params.sessionID
const permissionID = params.permissionID
Permission.respond({
sessionID: id,
sessionID,
permissionID,
response: c.req.valid("json").response,
})
@@ -1429,7 +1431,7 @@ export namespace Server {
},
)
.post(
"/provider/:id/oauth/authorize",
"/provider/:providerID/oauth/authorize",
describeRoute({
description: "Authorize a provider using OAuth",
operationId: "provider.oauth.authorize",
@@ -1448,7 +1450,7 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Provider ID" }),
providerID: z.string().meta({ description: "Provider ID" }),
}),
),
validator(
@@ -1458,17 +1460,17 @@ export namespace Server {
}),
),
async (c) => {
const id = c.req.valid("param").id
const providerID = c.req.valid("param").providerID
const { method } = c.req.valid("json")
const result = await ProviderAuth.authorize({
providerID: id,
providerID,
method,
})
return c.json(result)
},
)
.post(
"/provider/:id/oauth/callback",
"/provider/:providerID/oauth/callback",
describeRoute({
description: "Handle OAuth callback for a provider",
operationId: "provider.oauth.callback",
@@ -1487,7 +1489,7 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string().meta({ description: "Provider ID" }),
providerID: z.string().meta({ description: "Provider ID" }),
}),
),
validator(
@@ -1498,10 +1500,10 @@ export namespace Server {
}),
),
async (c) => {
const id = c.req.valid("param").id
const providerID = c.req.valid("param").providerID
const { method, code } = c.req.valid("json")
await ProviderAuth.callback({
providerID: id,
providerID,
method,
code,
})
@@ -2215,7 +2217,7 @@ export namespace Server {
)
.route("/tui/control", TuiRoute)
.put(
"/auth/:id",
"/auth/:providerID",
describeRoute({
description: "Set authentication credentials",
operationId: "auth.set",
@@ -2234,14 +2236,14 @@ export namespace Server {
validator(
"param",
z.object({
id: z.string(),
providerID: z.string(),
}),
),
validator("json", Auth.Info),
async (c) => {
const id = c.req.valid("param").id
const providerID = c.req.valid("param").providerID
const info = c.req.valid("json")
await Auth.set(id, info)
await Auth.set(providerID, info)
return c.json(true)
},
)

View File

@@ -6,7 +6,7 @@ import { Session } from "@/session"
import { MessageV2 } from "@/session/message-v2"
import { Storage } from "@/storage/storage"
import { Log } from "@/util/log"
import type * as SDK from "@opencode-ai/sdk"
import type * as SDK from "@opencode-ai/sdk/v2"
export namespace ShareNext {
const log = Log.create({ service: "share-next" })