index.ts 6.9 KB

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