index.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import type {
  2. Event,
  3. createOpencodeClient,
  4. Project,
  5. Model,
  6. Provider,
  7. Permission,
  8. UserMessage,
  9. Message,
  10. Part,
  11. Auth,
  12. Config as SDKConfig,
  13. } from "@opencode-ai/sdk"
  14. import type { Provider as ProviderV2, Model as ModelV2 } from "@opencode-ai/sdk/v2"
  15. import type { BunShell } from "./shell.js"
  16. import { type ToolDefinition } from "./tool.js"
  17. export * from "./tool.js"
  18. export type ProviderContext = {
  19. source: "env" | "config" | "custom" | "api"
  20. info: Provider
  21. options: Record<string, any>
  22. }
  23. export type PluginInput = {
  24. client: ReturnType<typeof createOpencodeClient>
  25. project: Project
  26. directory: string
  27. worktree: string
  28. serverUrl: URL
  29. $: BunShell
  30. }
  31. export type PluginOptions = Record<string, unknown>
  32. export type Config = Omit<SDKConfig, "plugin"> & {
  33. plugin?: Array<string | [string, PluginOptions]>
  34. }
  35. export type Plugin = (input: PluginInput, options?: PluginOptions) => Promise<Hooks>
  36. export type PluginModule = {
  37. id?: string
  38. server: Plugin
  39. tui?: never
  40. }
  41. type Rule = {
  42. key: string
  43. op: "eq" | "neq"
  44. value: string
  45. }
  46. export type AuthHook = {
  47. provider: string
  48. loader?: (auth: () => Promise<Auth>, provider: Provider) => Promise<Record<string, any>>
  49. methods: (
  50. | {
  51. type: "oauth"
  52. label: string
  53. prompts?: Array<
  54. | {
  55. type: "text"
  56. key: string
  57. message: string
  58. placeholder?: string
  59. validate?: (value: string) => string | undefined
  60. /** @deprecated Use `when` instead */
  61. condition?: (inputs: Record<string, string>) => boolean
  62. when?: Rule
  63. }
  64. | {
  65. type: "select"
  66. key: string
  67. message: string
  68. options: Array<{
  69. label: string
  70. value: string
  71. hint?: string
  72. }>
  73. /** @deprecated Use `when` instead */
  74. condition?: (inputs: Record<string, string>) => boolean
  75. when?: Rule
  76. }
  77. >
  78. authorize(inputs?: Record<string, string>): Promise<AuthOAuthResult>
  79. }
  80. | {
  81. type: "api"
  82. label: string
  83. prompts?: Array<
  84. | {
  85. type: "text"
  86. key: string
  87. message: string
  88. placeholder?: string
  89. validate?: (value: string) => string | undefined
  90. /** @deprecated Use `when` instead */
  91. condition?: (inputs: Record<string, string>) => boolean
  92. when?: Rule
  93. }
  94. | {
  95. type: "select"
  96. key: string
  97. message: string
  98. options: Array<{
  99. label: string
  100. value: string
  101. hint?: string
  102. }>
  103. /** @deprecated Use `when` instead */
  104. condition?: (inputs: Record<string, string>) => boolean
  105. when?: Rule
  106. }
  107. >
  108. authorize?(inputs?: Record<string, string>): Promise<
  109. | {
  110. type: "success"
  111. key: string
  112. provider?: string
  113. }
  114. | {
  115. type: "failed"
  116. }
  117. >
  118. }
  119. )[]
  120. }
  121. export type AuthOAuthResult = { url: string; instructions: string } & (
  122. | {
  123. method: "auto"
  124. callback(): Promise<
  125. | ({
  126. type: "success"
  127. provider?: string
  128. } & (
  129. | {
  130. refresh: string
  131. access: string
  132. expires: number
  133. accountId?: string
  134. enterpriseUrl?: string
  135. }
  136. | { key: string }
  137. ))
  138. | {
  139. type: "failed"
  140. }
  141. >
  142. }
  143. | {
  144. method: "code"
  145. callback(code: string): Promise<
  146. | ({
  147. type: "success"
  148. provider?: string
  149. } & (
  150. | {
  151. refresh: string
  152. access: string
  153. expires: number
  154. accountId?: string
  155. enterpriseUrl?: string
  156. }
  157. | { key: string }
  158. ))
  159. | {
  160. type: "failed"
  161. }
  162. >
  163. }
  164. )
  165. export type ProviderHookContext = {
  166. auth?: Auth
  167. }
  168. export type ProviderHook = {
  169. id: string
  170. models?: (provider: ProviderV2, ctx: ProviderHookContext) => Promise<Record<string, ModelV2>>
  171. }
  172. /** @deprecated Use AuthOAuthResult instead. */
  173. export type AuthOuathResult = AuthOAuthResult
  174. export interface Hooks {
  175. event?: (input: { event: Event }) => Promise<void>
  176. config?: (input: Config) => Promise<void>
  177. tool?: {
  178. [key: string]: ToolDefinition
  179. }
  180. auth?: AuthHook
  181. provider?: ProviderHook
  182. /**
  183. * Called when a new message is received
  184. */
  185. "chat.message"?: (
  186. input: {
  187. sessionID: string
  188. agent?: string
  189. model?: { providerID: string; modelID: string }
  190. messageID?: string
  191. variant?: string
  192. },
  193. output: { message: UserMessage; parts: Part[] },
  194. ) => Promise<void>
  195. /**
  196. * Modify parameters sent to LLM
  197. */
  198. "chat.params"?: (
  199. input: { sessionID: string; agent: string; model: Model; provider: ProviderContext; message: UserMessage },
  200. output: {
  201. temperature: number
  202. topP: number
  203. topK: number
  204. maxOutputTokens: number | undefined
  205. options: Record<string, any>
  206. },
  207. ) => Promise<void>
  208. "chat.headers"?: (
  209. input: { sessionID: string; agent: string; model: Model; provider: ProviderContext; message: UserMessage },
  210. output: { headers: Record<string, string> },
  211. ) => Promise<void>
  212. "permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise<void>
  213. "command.execute.before"?: (
  214. input: { command: string; sessionID: string; arguments: string },
  215. output: { parts: Part[] },
  216. ) => Promise<void>
  217. "tool.execute.before"?: (
  218. input: { tool: string; sessionID: string; callID: string },
  219. output: { args: any },
  220. ) => Promise<void>
  221. "shell.env"?: (
  222. input: { cwd: string; sessionID?: string; callID?: string },
  223. output: { env: Record<string, string> },
  224. ) => Promise<void>
  225. "tool.execute.after"?: (
  226. input: { tool: string; sessionID: string; callID: string; args: any },
  227. output: {
  228. title: string
  229. output: string
  230. metadata: any
  231. },
  232. ) => Promise<void>
  233. "experimental.chat.messages.transform"?: (
  234. input: {},
  235. output: {
  236. messages: {
  237. info: Message
  238. parts: Part[]
  239. }[]
  240. },
  241. ) => Promise<void>
  242. "experimental.chat.system.transform"?: (
  243. input: { sessionID?: string; model: Model },
  244. output: {
  245. system: string[]
  246. },
  247. ) => Promise<void>
  248. /**
  249. * Called before session compaction starts. Allows plugins to customize
  250. * the compaction prompt.
  251. *
  252. * - `context`: Additional context strings appended to the default prompt
  253. * - `prompt`: If set, replaces the default compaction prompt entirely
  254. */
  255. "experimental.session.compacting"?: (
  256. input: { sessionID: string },
  257. output: { context: string[]; prompt?: string },
  258. ) => Promise<void>
  259. "experimental.text.complete"?: (
  260. input: { sessionID: string; messageID: string; partID: string },
  261. output: { text: string },
  262. ) => Promise<void>
  263. /**
  264. * Modify tool definitions (description and parameters) sent to LLM
  265. */
  266. "tool.definition"?: (input: { toolID: string }, output: { description: string; parameters: any }) => Promise<void>
  267. }