index.ts 6.5 KB

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