index.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import type {
  2. Event,
  3. createOpencodeClient,
  4. Project,
  5. Model,
  6. Provider,
  7. Permission,
  8. UserMessage,
  9. Message,
  10. Part,
  11. Config as SDKConfig,
  12. } from "@kirincode-ai/sdk"
  13. import type { Provider as ProviderV2, Model as ModelV2, Auth } from "@kirincode-ai/sdk/v2"
  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 WorkspaceInfo = {
  23. id: string
  24. type: string
  25. name: string
  26. branch: string | null
  27. directory: string | null
  28. extra: unknown | null
  29. projectID: string
  30. }
  31. export type WorkspaceTarget =
  32. | {
  33. type: "local"
  34. directory: string
  35. }
  36. | {
  37. type: "remote"
  38. url: string | URL
  39. headers?: HeadersInit
  40. }
  41. export type WorkspaceAdapter = {
  42. name: string
  43. description: string
  44. configure(config: WorkspaceInfo): WorkspaceInfo | Promise<WorkspaceInfo>
  45. create(config: WorkspaceInfo, env: Record<string, string | undefined>, from?: WorkspaceInfo): Promise<void>
  46. remove(config: WorkspaceInfo): Promise<void>
  47. target(config: WorkspaceInfo): WorkspaceTarget | Promise<WorkspaceTarget>
  48. }
  49. export type PluginInput = {
  50. client: ReturnType<typeof createOpencodeClient>
  51. project: Project
  52. directory: string
  53. worktree: string
  54. experimental_workspace: {
  55. register(type: string, adapter: WorkspaceAdapter): void
  56. }
  57. serverUrl: URL
  58. $: BunShell
  59. }
  60. export type PluginOptions = Record<string, unknown>
  61. export type Config = Omit<SDKConfig, "plugin"> & {
  62. plugin?: Array<string | [string, PluginOptions]>
  63. }
  64. export type Plugin = (input: PluginInput, options?: PluginOptions) => Promise<Hooks>
  65. export type PluginModule = {
  66. id?: string
  67. server: Plugin
  68. tui?: never
  69. }
  70. type Rule = {
  71. key: string
  72. op: "eq" | "neq"
  73. value: string
  74. }
  75. export type AuthHook = {
  76. provider: string
  77. loader?: (auth: () => Promise<Auth>, provider: Provider) => Promise<Record<string, any>>
  78. methods: (
  79. | {
  80. type: "oauth"
  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<AuthOAuthResult>
  108. }
  109. | {
  110. type: "api"
  111. label: string
  112. prompts?: Array<
  113. | {
  114. type: "text"
  115. key: string
  116. message: string
  117. placeholder?: string
  118. validate?: (value: string) => string | undefined
  119. /** @deprecated Use `when` instead */
  120. condition?: (inputs: Record<string, string>) => boolean
  121. when?: Rule
  122. }
  123. | {
  124. type: "select"
  125. key: string
  126. message: string
  127. options: Array<{
  128. label: string
  129. value: string
  130. hint?: string
  131. }>
  132. /** @deprecated Use `when` instead */
  133. condition?: (inputs: Record<string, string>) => boolean
  134. when?: Rule
  135. }
  136. >
  137. authorize?(inputs?: Record<string, string>): Promise<
  138. | {
  139. type: "success"
  140. key: string
  141. provider?: string
  142. metadata?: Record<string, string>
  143. }
  144. | {
  145. type: "failed"
  146. }
  147. >
  148. }
  149. )[]
  150. }
  151. export type AuthOAuthResult = { url: string; instructions: string } & (
  152. | {
  153. method: "auto"
  154. callback(): Promise<
  155. | ({
  156. type: "success"
  157. provider?: string
  158. } & (
  159. | {
  160. refresh: string
  161. access: string
  162. expires: number
  163. accountId?: string
  164. enterpriseUrl?: string
  165. }
  166. | { key: string; metadata?: Record<string, string> }
  167. ))
  168. | {
  169. type: "failed"
  170. }
  171. >
  172. }
  173. | {
  174. method: "code"
  175. callback(code: string): Promise<
  176. | ({
  177. type: "success"
  178. provider?: string
  179. } & (
  180. | {
  181. refresh: string
  182. access: string
  183. expires: number
  184. accountId?: string
  185. enterpriseUrl?: string
  186. }
  187. | { key: string; metadata?: Record<string, string> }
  188. ))
  189. | {
  190. type: "failed"
  191. }
  192. >
  193. }
  194. )
  195. export type ProviderHookContext = {
  196. auth?: Auth
  197. }
  198. export type ProviderHook = {
  199. id: string
  200. models?: (provider: ProviderV2, ctx: ProviderHookContext) => Promise<Record<string, ModelV2>>
  201. }
  202. /** @deprecated Use AuthOAuthResult instead. */
  203. export type AuthOuathResult = AuthOAuthResult
  204. export interface Hooks {
  205. dispose?: () => Promise<void>
  206. event?: (input: { event: Event }) => Promise<void>
  207. config?: (input: Config) => Promise<void>
  208. tool?: {
  209. [key: string]: ToolDefinition
  210. }
  211. auth?: AuthHook
  212. provider?: ProviderHook
  213. /**
  214. * Called when a new message is received
  215. */
  216. "chat.message"?: (
  217. input: {
  218. sessionID: string
  219. agent?: string
  220. model?: { providerID: string; modelID: string }
  221. messageID?: string
  222. variant?: string
  223. },
  224. output: { message: UserMessage; parts: Part[] },
  225. ) => Promise<void>
  226. /**
  227. * Modify parameters sent to LLM
  228. */
  229. "chat.params"?: (
  230. input: { sessionID: string; agent: string; model: Model; provider: ProviderContext; message: UserMessage },
  231. output: {
  232. temperature: number
  233. topP: number
  234. topK: number
  235. maxOutputTokens: number | undefined
  236. options: Record<string, any>
  237. },
  238. ) => Promise<void>
  239. "chat.headers"?: (
  240. input: { sessionID: string; agent: string; model: Model; provider: ProviderContext; message: UserMessage },
  241. output: { headers: Record<string, string> },
  242. ) => Promise<void>
  243. "permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise<void>
  244. "command.execute.before"?: (
  245. input: { command: string; sessionID: string; arguments: string },
  246. output: { parts: Part[] },
  247. ) => Promise<void>
  248. "tool.execute.before"?: (
  249. input: { tool: string; sessionID: string; callID: string },
  250. output: { args: any },
  251. ) => Promise<void>
  252. "shell.env"?: (
  253. input: { cwd: string; sessionID?: string; callID?: string },
  254. output: { env: Record<string, string> },
  255. ) => Promise<void>
  256. "tool.execute.after"?: (
  257. input: { tool: string; sessionID: string; callID: string; args: any },
  258. output: {
  259. title: string
  260. output: string
  261. metadata: any
  262. },
  263. ) => Promise<void>
  264. "experimental.chat.messages.transform"?: (
  265. input: {},
  266. output: {
  267. messages: {
  268. info: Message
  269. parts: Part[]
  270. }[]
  271. },
  272. ) => Promise<void>
  273. "experimental.chat.system.transform"?: (
  274. input: { sessionID?: string; model: Model },
  275. output: {
  276. system: string[]
  277. },
  278. ) => Promise<void>
  279. "experimental.provider.small_model"?: (input: { provider: ProviderV2 }, output: { model?: ModelV2 }) => Promise<void>
  280. /**
  281. * Called before session compaction starts. Allows plugins to customize
  282. * the compaction prompt.
  283. *
  284. * - `context`: Additional context strings appended to the default prompt
  285. * - `prompt`: If set, replaces the default compaction prompt entirely
  286. */
  287. "experimental.session.compacting"?: (
  288. input: { sessionID: string },
  289. output: { context: string[]; prompt?: string },
  290. ) => Promise<void>
  291. /**
  292. * Called after compaction succeeds and before a synthetic user
  293. * auto-continue message is added.
  294. *
  295. * - `enabled`: Defaults to `true`. Set to `false` to skip the synthetic
  296. * user "continue" turn.
  297. */
  298. "experimental.compaction.autocontinue"?: (
  299. input: {
  300. sessionID: string
  301. agent: string
  302. model: Model
  303. provider: ProviderContext
  304. message: UserMessage
  305. overflow: boolean
  306. },
  307. output: { enabled: boolean },
  308. ) => Promise<void>
  309. "experimental.text.complete"?: (
  310. input: { sessionID: string; messageID: string; partID: string },
  311. output: { text: string },
  312. ) => Promise<void>
  313. /**
  314. * Modify tool definitions (description and parameters) sent to LLM
  315. */
  316. "tool.definition"?: (input: { toolID: string }, output: { description: string; parameters: any }) => Promise<void>
  317. }