@brianli/kimaki 0.4.72-brianli.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/bin.js +2 -0
  2. package/dist/ai-tool-to-genai.js +233 -0
  3. package/dist/ai-tool-to-genai.test.js +267 -0
  4. package/dist/ai-tool.js +6 -0
  5. package/dist/bin.js +87 -0
  6. package/dist/bot-token.js +121 -0
  7. package/dist/bot-token.test.js +134 -0
  8. package/dist/channel-management.js +101 -0
  9. package/dist/cli-parsing.test.js +89 -0
  10. package/dist/cli.js +2529 -0
  11. package/dist/commands/abort.js +82 -0
  12. package/dist/commands/action-buttons.js +257 -0
  13. package/dist/commands/add-project.js +114 -0
  14. package/dist/commands/agent.js +291 -0
  15. package/dist/commands/ask-question.js +223 -0
  16. package/dist/commands/compact.js +120 -0
  17. package/dist/commands/context-usage.js +140 -0
  18. package/dist/commands/create-new-project.js +118 -0
  19. package/dist/commands/diff.js +128 -0
  20. package/dist/commands/file-upload.js +275 -0
  21. package/dist/commands/fork.js +217 -0
  22. package/dist/commands/gemini-apikey.js +70 -0
  23. package/dist/commands/login.js +490 -0
  24. package/dist/commands/mention-mode.js +51 -0
  25. package/dist/commands/merge-worktree.js +124 -0
  26. package/dist/commands/model.js +694 -0
  27. package/dist/commands/permissions.js +163 -0
  28. package/dist/commands/queue.js +217 -0
  29. package/dist/commands/remove-project.js +115 -0
  30. package/dist/commands/restart-opencode-server.js +116 -0
  31. package/dist/commands/resume.js +159 -0
  32. package/dist/commands/run-command.js +79 -0
  33. package/dist/commands/session-id.js +78 -0
  34. package/dist/commands/session.js +192 -0
  35. package/dist/commands/share.js +80 -0
  36. package/dist/commands/types.js +2 -0
  37. package/dist/commands/undo-redo.js +159 -0
  38. package/dist/commands/unset-model.js +152 -0
  39. package/dist/commands/upgrade.js +42 -0
  40. package/dist/commands/user-command.js +148 -0
  41. package/dist/commands/verbosity.js +60 -0
  42. package/dist/commands/worktree-settings.js +50 -0
  43. package/dist/commands/worktree.js +299 -0
  44. package/dist/condense-memory.js +33 -0
  45. package/dist/config.js +110 -0
  46. package/dist/database.js +1050 -0
  47. package/dist/db.js +159 -0
  48. package/dist/db.test.js +49 -0
  49. package/dist/discord-api.js +28 -0
  50. package/dist/discord-auth.js +231 -0
  51. package/dist/discord-auth.test.js +80 -0
  52. package/dist/discord-bot.js +997 -0
  53. package/dist/discord-utils.js +560 -0
  54. package/dist/discord-utils.test.js +115 -0
  55. package/dist/errors.js +167 -0
  56. package/dist/escape-backticks.test.js +429 -0
  57. package/dist/format-tables.js +122 -0
  58. package/dist/format-tables.test.js +199 -0
  59. package/dist/forum-sync/config.js +79 -0
  60. package/dist/forum-sync/discord-operations.js +154 -0
  61. package/dist/forum-sync/index.js +5 -0
  62. package/dist/forum-sync/markdown.js +117 -0
  63. package/dist/forum-sync/sync-to-discord.js +417 -0
  64. package/dist/forum-sync/sync-to-files.js +190 -0
  65. package/dist/forum-sync/types.js +53 -0
  66. package/dist/forum-sync/watchers.js +307 -0
  67. package/dist/gateway-consumer.js +232 -0
  68. package/dist/gateway-consumer.test.js +18 -0
  69. package/dist/genai-worker-wrapper.js +111 -0
  70. package/dist/genai-worker.js +311 -0
  71. package/dist/genai.js +232 -0
  72. package/dist/generated/browser.js +17 -0
  73. package/dist/generated/client.js +35 -0
  74. package/dist/generated/commonInputTypes.js +10 -0
  75. package/dist/generated/enums.js +30 -0
  76. package/dist/generated/internal/class.js +41 -0
  77. package/dist/generated/internal/prismaNamespace.js +239 -0
  78. package/dist/generated/internal/prismaNamespaceBrowser.js +209 -0
  79. package/dist/generated/models/bot_api_keys.js +1 -0
  80. package/dist/generated/models/bot_tokens.js +1 -0
  81. package/dist/generated/models/channel_agents.js +1 -0
  82. package/dist/generated/models/channel_directories.js +1 -0
  83. package/dist/generated/models/channel_mention_mode.js +1 -0
  84. package/dist/generated/models/channel_models.js +1 -0
  85. package/dist/generated/models/channel_verbosity.js +1 -0
  86. package/dist/generated/models/channel_worktrees.js +1 -0
  87. package/dist/generated/models/forum_sync_configs.js +1 -0
  88. package/dist/generated/models/global_models.js +1 -0
  89. package/dist/generated/models/ipc_requests.js +1 -0
  90. package/dist/generated/models/part_messages.js +1 -0
  91. package/dist/generated/models/scheduled_tasks.js +1 -0
  92. package/dist/generated/models/session_agents.js +1 -0
  93. package/dist/generated/models/session_models.js +1 -0
  94. package/dist/generated/models/session_start_sources.js +1 -0
  95. package/dist/generated/models/thread_sessions.js +1 -0
  96. package/dist/generated/models/thread_worktrees.js +1 -0
  97. package/dist/generated/models.js +1 -0
  98. package/dist/heap-monitor.js +95 -0
  99. package/dist/hrana-server.js +416 -0
  100. package/dist/hrana-server.test.js +368 -0
  101. package/dist/image-utils.js +112 -0
  102. package/dist/interaction-handler.js +327 -0
  103. package/dist/ipc-polling.js +251 -0
  104. package/dist/kimaki-digital-twin.e2e.test.js +165 -0
  105. package/dist/limit-heading-depth.js +25 -0
  106. package/dist/limit-heading-depth.test.js +105 -0
  107. package/dist/logger.js +160 -0
  108. package/dist/markdown.js +342 -0
  109. package/dist/markdown.test.js +253 -0
  110. package/dist/message-formatting.js +433 -0
  111. package/dist/message-formatting.test.js +73 -0
  112. package/dist/openai-realtime.js +228 -0
  113. package/dist/opencode-plugin-loading.e2e.test.js +91 -0
  114. package/dist/opencode-plugin.js +536 -0
  115. package/dist/opencode-plugin.test.js +98 -0
  116. package/dist/opencode.js +409 -0
  117. package/dist/privacy-sanitizer.js +105 -0
  118. package/dist/runtime-mode.js +51 -0
  119. package/dist/runtime-mode.test.js +115 -0
  120. package/dist/sentry.js +127 -0
  121. package/dist/session-handler/state.js +151 -0
  122. package/dist/session-handler.js +1874 -0
  123. package/dist/session-search.js +100 -0
  124. package/dist/session-search.test.js +40 -0
  125. package/dist/startup-service.js +153 -0
  126. package/dist/system-message.js +499 -0
  127. package/dist/task-runner.js +282 -0
  128. package/dist/task-schedule.js +191 -0
  129. package/dist/task-schedule.test.js +71 -0
  130. package/dist/thinking-utils.js +35 -0
  131. package/dist/thread-message-queue.e2e.test.js +781 -0
  132. package/dist/tools.js +359 -0
  133. package/dist/unnest-code-blocks.js +136 -0
  134. package/dist/unnest-code-blocks.test.js +641 -0
  135. package/dist/upgrade.js +114 -0
  136. package/dist/utils.js +109 -0
  137. package/dist/voice-handler.js +606 -0
  138. package/dist/voice.js +304 -0
  139. package/dist/voice.test.js +187 -0
  140. package/dist/wait-session.js +94 -0
  141. package/dist/worker-types.js +4 -0
  142. package/dist/worktree-utils.js +727 -0
  143. package/dist/xml.js +92 -0
  144. package/dist/xml.test.js +32 -0
  145. package/package.json +82 -0
  146. package/schema.prisma +246 -0
  147. package/skills/batch/SKILL.md +87 -0
  148. package/skills/critique/SKILL.md +129 -0
  149. package/skills/errore/SKILL.md +589 -0
  150. package/skills/goke/.prettierrc +5 -0
  151. package/skills/goke/CHANGELOG.md +40 -0
  152. package/skills/goke/LICENSE +21 -0
  153. package/skills/goke/README.md +666 -0
  154. package/skills/goke/SKILL.md +458 -0
  155. package/skills/goke/package.json +43 -0
  156. package/skills/goke/src/__test__/coerce.test.ts +411 -0
  157. package/skills/goke/src/__test__/index.test.ts +1798 -0
  158. package/skills/goke/src/__test__/types.test-d.ts +111 -0
  159. package/skills/goke/src/coerce.ts +547 -0
  160. package/skills/goke/src/goke.ts +1362 -0
  161. package/skills/goke/src/index.ts +16 -0
  162. package/skills/goke/src/mri.ts +164 -0
  163. package/skills/goke/tsconfig.json +15 -0
  164. package/skills/jitter/EDITOR.md +219 -0
  165. package/skills/jitter/EXPORT-INTERNALS.md +309 -0
  166. package/skills/jitter/SKILL.md +158 -0
  167. package/skills/jitter/jitter-clipboard.json +1042 -0
  168. package/skills/jitter/package.json +14 -0
  169. package/skills/jitter/tsconfig.json +15 -0
  170. package/skills/jitter/utils/actions.ts +212 -0
  171. package/skills/jitter/utils/export.ts +114 -0
  172. package/skills/jitter/utils/index.ts +141 -0
  173. package/skills/jitter/utils/snapshot.ts +154 -0
  174. package/skills/jitter/utils/traverse.ts +246 -0
  175. package/skills/jitter/utils/types.ts +279 -0
  176. package/skills/jitter/utils/wait.ts +133 -0
  177. package/skills/playwriter/SKILL.md +31 -0
  178. package/skills/security-review/SKILL.md +208 -0
  179. package/skills/simplify/SKILL.md +58 -0
  180. package/skills/termcast/SKILL.md +945 -0
  181. package/skills/tuistory/SKILL.md +250 -0
  182. package/skills/zustand-centralized-state/SKILL.md +582 -0
  183. package/src/__snapshots__/compact-session-context-no-system.md +35 -0
  184. package/src/__snapshots__/compact-session-context.md +41 -0
  185. package/src/__snapshots__/first-session-no-info.md +17 -0
  186. package/src/__snapshots__/first-session-with-info.md +23 -0
  187. package/src/__snapshots__/session-1.md +17 -0
  188. package/src/__snapshots__/session-2.md +5871 -0
  189. package/src/__snapshots__/session-3.md +17 -0
  190. package/src/__snapshots__/session-with-tools.md +5871 -0
  191. package/src/ai-tool-to-genai.test.ts +296 -0
  192. package/src/ai-tool-to-genai.ts +282 -0
  193. package/src/ai-tool.ts +39 -0
  194. package/src/bin.ts +108 -0
  195. package/src/bot-token.test.ts +171 -0
  196. package/src/bot-token.ts +159 -0
  197. package/src/channel-management.ts +172 -0
  198. package/src/cli-parsing.test.ts +132 -0
  199. package/src/cli.ts +3605 -0
  200. package/src/commands/abort.ts +112 -0
  201. package/src/commands/action-buttons.ts +376 -0
  202. package/src/commands/add-project.ts +152 -0
  203. package/src/commands/agent.ts +404 -0
  204. package/src/commands/ask-question.ts +330 -0
  205. package/src/commands/compact.ts +157 -0
  206. package/src/commands/context-usage.ts +199 -0
  207. package/src/commands/create-new-project.ts +179 -0
  208. package/src/commands/diff.ts +165 -0
  209. package/src/commands/file-upload.ts +389 -0
  210. package/src/commands/fork.ts +320 -0
  211. package/src/commands/gemini-apikey.ts +104 -0
  212. package/src/commands/login.ts +634 -0
  213. package/src/commands/mention-mode.ts +77 -0
  214. package/src/commands/merge-worktree.ts +177 -0
  215. package/src/commands/model.ts +961 -0
  216. package/src/commands/permissions.ts +261 -0
  217. package/src/commands/queue.ts +296 -0
  218. package/src/commands/remove-project.ts +155 -0
  219. package/src/commands/restart-opencode-server.ts +162 -0
  220. package/src/commands/resume.ts +242 -0
  221. package/src/commands/run-command.ts +123 -0
  222. package/src/commands/session-id.ts +109 -0
  223. package/src/commands/session.ts +250 -0
  224. package/src/commands/share.ts +106 -0
  225. package/src/commands/types.ts +25 -0
  226. package/src/commands/undo-redo.ts +221 -0
  227. package/src/commands/unset-model.ts +189 -0
  228. package/src/commands/upgrade.ts +52 -0
  229. package/src/commands/user-command.ts +193 -0
  230. package/src/commands/verbosity.ts +88 -0
  231. package/src/commands/worktree-settings.ts +79 -0
  232. package/src/commands/worktree.ts +431 -0
  233. package/src/condense-memory.ts +36 -0
  234. package/src/config.ts +148 -0
  235. package/src/database.ts +1530 -0
  236. package/src/db.test.ts +60 -0
  237. package/src/db.ts +190 -0
  238. package/src/discord-api.ts +35 -0
  239. package/src/discord-bot.ts +1316 -0
  240. package/src/discord-utils.test.ts +132 -0
  241. package/src/discord-utils.ts +767 -0
  242. package/src/errors.ts +213 -0
  243. package/src/escape-backticks.test.ts +469 -0
  244. package/src/format-tables.test.ts +223 -0
  245. package/src/format-tables.ts +145 -0
  246. package/src/forum-sync/config.ts +92 -0
  247. package/src/forum-sync/discord-operations.ts +241 -0
  248. package/src/forum-sync/index.ts +9 -0
  249. package/src/forum-sync/markdown.ts +176 -0
  250. package/src/forum-sync/sync-to-discord.ts +595 -0
  251. package/src/forum-sync/sync-to-files.ts +294 -0
  252. package/src/forum-sync/types.ts +175 -0
  253. package/src/forum-sync/watchers.ts +454 -0
  254. package/src/genai-worker-wrapper.ts +164 -0
  255. package/src/genai-worker.ts +386 -0
  256. package/src/genai.ts +321 -0
  257. package/src/generated/browser.ts +109 -0
  258. package/src/generated/client.ts +131 -0
  259. package/src/generated/commonInputTypes.ts +512 -0
  260. package/src/generated/enums.ts +46 -0
  261. package/src/generated/internal/class.ts +362 -0
  262. package/src/generated/internal/prismaNamespace.ts +2251 -0
  263. package/src/generated/internal/prismaNamespaceBrowser.ts +308 -0
  264. package/src/generated/models/bot_api_keys.ts +1288 -0
  265. package/src/generated/models/bot_tokens.ts +1577 -0
  266. package/src/generated/models/channel_agents.ts +1256 -0
  267. package/src/generated/models/channel_directories.ts +2104 -0
  268. package/src/generated/models/channel_mention_mode.ts +1300 -0
  269. package/src/generated/models/channel_models.ts +1288 -0
  270. package/src/generated/models/channel_verbosity.ts +1224 -0
  271. package/src/generated/models/channel_worktrees.ts +1308 -0
  272. package/src/generated/models/forum_sync_configs.ts +1452 -0
  273. package/src/generated/models/global_models.ts +1288 -0
  274. package/src/generated/models/ipc_requests.ts +1485 -0
  275. package/src/generated/models/part_messages.ts +1302 -0
  276. package/src/generated/models/scheduled_tasks.ts +2320 -0
  277. package/src/generated/models/session_agents.ts +1086 -0
  278. package/src/generated/models/session_models.ts +1114 -0
  279. package/src/generated/models/session_start_sources.ts +1408 -0
  280. package/src/generated/models/thread_sessions.ts +1599 -0
  281. package/src/generated/models/thread_worktrees.ts +1352 -0
  282. package/src/generated/models.ts +29 -0
  283. package/src/heap-monitor.ts +121 -0
  284. package/src/hrana-server.test.ts +428 -0
  285. package/src/hrana-server.ts +547 -0
  286. package/src/image-utils.ts +149 -0
  287. package/src/interaction-handler.ts +461 -0
  288. package/src/ipc-polling.ts +325 -0
  289. package/src/kimaki-digital-twin.e2e.test.ts +201 -0
  290. package/src/limit-heading-depth.test.ts +116 -0
  291. package/src/limit-heading-depth.ts +26 -0
  292. package/src/logger.ts +203 -0
  293. package/src/markdown.test.ts +360 -0
  294. package/src/markdown.ts +410 -0
  295. package/src/message-formatting.test.ts +81 -0
  296. package/src/message-formatting.ts +549 -0
  297. package/src/openai-realtime.ts +362 -0
  298. package/src/opencode-plugin-loading.e2e.test.ts +112 -0
  299. package/src/opencode-plugin.test.ts +108 -0
  300. package/src/opencode-plugin.ts +652 -0
  301. package/src/opencode.ts +554 -0
  302. package/src/privacy-sanitizer.ts +142 -0
  303. package/src/schema.sql +158 -0
  304. package/src/sentry.ts +137 -0
  305. package/src/session-handler/state.ts +232 -0
  306. package/src/session-handler.ts +2668 -0
  307. package/src/session-search.test.ts +50 -0
  308. package/src/session-search.ts +148 -0
  309. package/src/startup-service.ts +200 -0
  310. package/src/system-message.ts +568 -0
  311. package/src/task-runner.ts +425 -0
  312. package/src/task-schedule.test.ts +84 -0
  313. package/src/task-schedule.ts +287 -0
  314. package/src/thinking-utils.ts +61 -0
  315. package/src/thread-message-queue.e2e.test.ts +997 -0
  316. package/src/tools.ts +432 -0
  317. package/src/unnest-code-blocks.test.ts +679 -0
  318. package/src/unnest-code-blocks.ts +168 -0
  319. package/src/upgrade.ts +127 -0
  320. package/src/utils.ts +145 -0
  321. package/src/voice-handler.ts +852 -0
  322. package/src/voice.test.ts +219 -0
  323. package/src/voice.ts +444 -0
  324. package/src/wait-session.ts +147 -0
  325. package/src/worker-types.ts +64 -0
  326. package/src/worktree-utils.ts +988 -0
  327. package/src/xml.test.ts +38 -0
  328. package/src/xml.ts +121 -0
@@ -0,0 +1,296 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { tool } from './ai-tool.js'
3
+ import { z } from 'zod'
4
+ import { Type } from '@google/genai'
5
+ import type { FunctionDeclaration, FunctionCall } from '@google/genai'
6
+ import {
7
+ aiToolToGenAIFunction,
8
+ aiToolToCallableTool,
9
+ extractSchemaFromTool,
10
+ } from './ai-tool-to-genai.js'
11
+
12
+ describe('AI Tool to GenAI Conversion', () => {
13
+ it('should convert a simple Zod-based tool', () => {
14
+ const weatherTool = tool({
15
+ description: 'Get the current weather for a location',
16
+ inputSchema: z.object({
17
+ location: z.string().describe('The city name'),
18
+ unit: z.enum(['celsius', 'fahrenheit']).optional(),
19
+ }),
20
+ execute: async ({ location, unit }) => {
21
+ return {
22
+ temperature: 72,
23
+ unit: unit || 'fahrenheit',
24
+ condition: 'sunny',
25
+ }
26
+ },
27
+ })
28
+
29
+ const genAIFunction = aiToolToGenAIFunction(weatherTool)
30
+
31
+ expect(genAIFunction).toMatchInlineSnapshot(`
32
+ {
33
+ "description": "Get the current weather for a location",
34
+ "name": "tool",
35
+ "parameters": {
36
+ "properties": {
37
+ "location": {
38
+ "description": "The city name",
39
+ "type": "STRING",
40
+ },
41
+ "unit": {
42
+ "enum": [
43
+ "celsius",
44
+ "fahrenheit",
45
+ ],
46
+ "type": "STRING",
47
+ },
48
+ },
49
+ "required": [
50
+ "location",
51
+ ],
52
+ "type": "OBJECT",
53
+ },
54
+ }
55
+ `)
56
+ })
57
+
58
+ it('should handle complex nested schemas', () => {
59
+ const complexTool = tool({
60
+ description: 'Process complex data',
61
+ inputSchema: z.object({
62
+ user: z.object({
63
+ name: z.string(),
64
+ age: z.number().int().min(0).max(150),
65
+ email: z.string().email(),
66
+ }),
67
+ preferences: z.array(z.string()),
68
+ metadata: z.record(z.string(), z.unknown()).optional(),
69
+ }),
70
+ execute: async (input) => input,
71
+ })
72
+
73
+ const genAIFunction = aiToolToGenAIFunction(complexTool)
74
+
75
+ expect(genAIFunction.parameters).toMatchInlineSnapshot(`
76
+ {
77
+ "properties": {
78
+ "metadata": {
79
+ "type": "OBJECT",
80
+ },
81
+ "preferences": {
82
+ "items": {
83
+ "type": "STRING",
84
+ },
85
+ "type": "ARRAY",
86
+ },
87
+ "user": {
88
+ "properties": {
89
+ "age": {
90
+ "format": "int32",
91
+ "maximum": 150,
92
+ "minimum": 0,
93
+ "type": "INTEGER",
94
+ },
95
+ "email": {
96
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
97
+ "type": "STRING",
98
+ },
99
+ "name": {
100
+ "type": "STRING",
101
+ },
102
+ },
103
+ "required": [
104
+ "name",
105
+ "age",
106
+ "email",
107
+ ],
108
+ "type": "OBJECT",
109
+ },
110
+ },
111
+ "required": [
112
+ "user",
113
+ "preferences",
114
+ ],
115
+ "type": "OBJECT",
116
+ }
117
+ `)
118
+ })
119
+
120
+ it('should extract schema from tool', () => {
121
+ const testTool = tool({
122
+ inputSchema: z.object({
123
+ test: z.string(),
124
+ }),
125
+ execute: async () => {},
126
+ })
127
+
128
+ const schema = extractSchemaFromTool(testTool)
129
+
130
+ expect(schema).toMatchInlineSnapshot(`
131
+ {
132
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
133
+ "additionalProperties": false,
134
+ "properties": {
135
+ "test": {
136
+ "type": "string",
137
+ },
138
+ },
139
+ "required": [
140
+ "test",
141
+ ],
142
+ "type": "object",
143
+ }
144
+ `)
145
+ })
146
+
147
+ it('should handle tools with no input schema', () => {
148
+ const simpleTool = tool({
149
+ description: 'Simple tool with no inputs',
150
+ inputSchema: z.object({}),
151
+ execute: async () => ({ result: 'done' }),
152
+ })
153
+
154
+ const genAIFunction = aiToolToGenAIFunction(simpleTool)
155
+
156
+ expect(genAIFunction).toMatchInlineSnapshot(`
157
+ {
158
+ "description": "Simple tool with no inputs",
159
+ "name": "tool",
160
+ "parameters": {
161
+ "properties": {},
162
+ "type": "OBJECT",
163
+ },
164
+ }
165
+ `)
166
+ })
167
+
168
+ it('should handle union types', () => {
169
+ const unionTool = tool({
170
+ description: 'Tool with union types',
171
+ inputSchema: z.object({
172
+ value: z.union([z.string(), z.number(), z.boolean()]),
173
+ }),
174
+ execute: async ({ value }) => ({ received: value }),
175
+ })
176
+
177
+ const genAIFunction = aiToolToGenAIFunction(unionTool)
178
+
179
+ expect(genAIFunction.parameters?.properties?.value).toMatchInlineSnapshot(`
180
+ {
181
+ "anyOf": [
182
+ {
183
+ "type": "STRING",
184
+ },
185
+ {
186
+ "format": "float",
187
+ "type": "NUMBER",
188
+ },
189
+ {
190
+ "type": "BOOLEAN",
191
+ },
192
+ ],
193
+ }
194
+ `)
195
+ })
196
+
197
+ it('should create a CallableTool', async () => {
198
+ const weatherTool = tool({
199
+ description: 'Get weather',
200
+ inputSchema: z.object({
201
+ location: z.string(),
202
+ }),
203
+ execute: async ({ location }) => ({
204
+ temperature: 72,
205
+ location,
206
+ }),
207
+ })
208
+
209
+ const callableTool = aiToolToCallableTool(weatherTool, 'weather')
210
+
211
+ // Test tool() method
212
+ const genAITool = await callableTool.tool()
213
+ expect(genAITool.functionDeclarations).toMatchInlineSnapshot(`
214
+ [
215
+ {
216
+ "description": "Get weather",
217
+ "name": "weather",
218
+ "parameters": {
219
+ "properties": {
220
+ "location": {
221
+ "type": "STRING",
222
+ },
223
+ },
224
+ "required": [
225
+ "location",
226
+ ],
227
+ "type": "OBJECT",
228
+ },
229
+ },
230
+ ]
231
+ `)
232
+
233
+ // Test callTool() method
234
+ const functionCall: FunctionCall = {
235
+ id: 'call_123',
236
+ name: 'weather',
237
+ args: { location: 'San Francisco' },
238
+ }
239
+
240
+ const parts = await callableTool.callTool([functionCall])
241
+ expect(parts).toMatchInlineSnapshot(`
242
+ [
243
+ {
244
+ "functionResponse": {
245
+ "id": "call_123",
246
+ "name": "weather",
247
+ "response": {
248
+ "output": {
249
+ "location": "San Francisco",
250
+ "temperature": 72,
251
+ },
252
+ },
253
+ },
254
+ },
255
+ ]
256
+ `)
257
+ })
258
+
259
+ it('should handle tool execution errors', async () => {
260
+ const errorTool = tool({
261
+ description: 'Tool that throws',
262
+ inputSchema: z.object({
263
+ trigger: z.boolean(),
264
+ }),
265
+ execute: async ({ trigger }) => {
266
+ if (trigger) {
267
+ throw new Error('Tool execution failed')
268
+ }
269
+ return { success: true }
270
+ },
271
+ })
272
+
273
+ const callableTool = aiToolToCallableTool(errorTool, 'error_tool')
274
+
275
+ const functionCall: FunctionCall = {
276
+ id: 'call_error',
277
+ name: 'error_tool',
278
+ args: { trigger: true },
279
+ }
280
+
281
+ const parts = await callableTool.callTool([functionCall])
282
+ expect(parts).toMatchInlineSnapshot(`
283
+ [
284
+ {
285
+ "functionResponse": {
286
+ "id": "call_error",
287
+ "name": "error_tool",
288
+ "response": {
289
+ "error": "Tool execution failed",
290
+ },
291
+ },
292
+ },
293
+ ]
294
+ `)
295
+ })
296
+ })
@@ -0,0 +1,282 @@
1
+ // Tool definition to Google GenAI tool converter.
2
+ // Transforms Kimaki's minimal Tool definitions into Google GenAI CallableTool format
3
+ // for use with Gemini's function calling in the voice assistant.
4
+
5
+ import type { AnyTool } from './ai-tool.js'
6
+ import type {
7
+ FunctionDeclaration,
8
+ Schema,
9
+ Tool as GenAITool,
10
+ CallableTool,
11
+ FunctionCall,
12
+ Part,
13
+ } from '@google/genai'
14
+ import { Type } from '@google/genai'
15
+ import { z, toJSONSchema } from 'zod'
16
+ import type { JSONSchema7, JSONSchema7Definition } from 'json-schema'
17
+
18
+ /**
19
+ * Convert JSON Schema to GenAI Schema format
20
+ * Based on the actual implementation used by the GenAI package:
21
+ * https://github.com/googleapis/js-genai/blob/027f09db662ce6b30f737b10b4d2efcb4282a9b6/src/_transformers.ts#L294
22
+ */
23
+ function isRecord(value: unknown): value is Record<string, unknown> {
24
+ return typeof value === 'object' && value !== null
25
+ }
26
+
27
+ function jsonSchemaToGenAISchema(jsonSchema: JSONSchema7Definition): Schema {
28
+ const schema: Schema = {}
29
+
30
+ if (typeof jsonSchema === 'boolean') {
31
+ return schema
32
+ }
33
+
34
+ const jsonSchemaType: string | undefined = (() => {
35
+ if (!jsonSchema.type) {
36
+ return undefined
37
+ }
38
+ if (typeof jsonSchema.type === 'string') {
39
+ return jsonSchema.type
40
+ }
41
+ if (Array.isArray(jsonSchema.type)) {
42
+ return jsonSchema.type.find((t) => t !== 'null') || jsonSchema.type[0]
43
+ }
44
+ return undefined
45
+ })()
46
+
47
+ if (Array.isArray(jsonSchema.type) && jsonSchema.type.includes('null')) {
48
+ schema.nullable = true
49
+ }
50
+
51
+ if (jsonSchemaType) {
52
+ switch (jsonSchemaType) {
53
+ case 'string':
54
+ schema.type = Type.STRING
55
+ break
56
+ case 'number':
57
+ schema.type = Type.NUMBER
58
+ schema.format =
59
+ typeof jsonSchema.format === 'string' ? jsonSchema.format : 'float'
60
+ break
61
+ case 'integer':
62
+ schema.type = Type.INTEGER
63
+ schema.format =
64
+ typeof jsonSchema.format === 'string' ? jsonSchema.format : 'int32'
65
+ break
66
+ case 'boolean':
67
+ schema.type = Type.BOOLEAN
68
+ break
69
+ case 'array': {
70
+ schema.type = Type.ARRAY
71
+ const itemsSchema: JSONSchema7Definition | undefined = (() => {
72
+ if (!jsonSchema.items) {
73
+ return undefined
74
+ }
75
+ if (Array.isArray(jsonSchema.items)) {
76
+ return jsonSchema.items[0]
77
+ }
78
+ return jsonSchema.items
79
+ })()
80
+ if (itemsSchema) {
81
+ schema.items = jsonSchemaToGenAISchema(itemsSchema)
82
+ }
83
+ if (typeof jsonSchema.minItems === 'number') {
84
+ schema.minItems = String(jsonSchema.minItems)
85
+ }
86
+ if (typeof jsonSchema.maxItems === 'number') {
87
+ schema.maxItems = String(jsonSchema.maxItems)
88
+ }
89
+ break
90
+ }
91
+ case 'object':
92
+ schema.type = Type.OBJECT
93
+ if (jsonSchema.properties) {
94
+ schema.properties = Object.fromEntries(
95
+ Object.entries(jsonSchema.properties).map(([key, value]) => [
96
+ key,
97
+ jsonSchemaToGenAISchema(value),
98
+ ]),
99
+ )
100
+ }
101
+ if (Array.isArray(jsonSchema.required)) {
102
+ schema.required = jsonSchema.required
103
+ }
104
+ break
105
+ }
106
+ }
107
+
108
+ if (typeof jsonSchema.description === 'string') {
109
+ schema.description = jsonSchema.description
110
+ }
111
+ if (Array.isArray(jsonSchema.enum)) {
112
+ schema.enum = jsonSchema.enum.map((x) => String(x))
113
+ }
114
+ if ('default' in jsonSchema) {
115
+ schema.default = jsonSchema.default as unknown
116
+ }
117
+ if (Array.isArray(jsonSchema.examples) && jsonSchema.examples.length > 0) {
118
+ schema.example = jsonSchema.examples[0] as unknown
119
+ }
120
+
121
+ if (Array.isArray(jsonSchema.anyOf)) {
122
+ schema.anyOf = jsonSchema.anyOf.map((s) => jsonSchemaToGenAISchema(s))
123
+ } else if (Array.isArray(jsonSchema.oneOf)) {
124
+ schema.anyOf = jsonSchema.oneOf.map((s) => jsonSchemaToGenAISchema(s))
125
+ }
126
+
127
+ if (typeof jsonSchema.minimum === 'number') {
128
+ schema.minimum = jsonSchema.minimum
129
+ }
130
+ if (typeof jsonSchema.maximum === 'number') {
131
+ schema.maximum = jsonSchema.maximum
132
+ }
133
+ if (typeof jsonSchema.minLength === 'number') {
134
+ schema.minLength = String(jsonSchema.minLength)
135
+ }
136
+ if (typeof jsonSchema.maxLength === 'number') {
137
+ schema.maxLength = String(jsonSchema.maxLength)
138
+ }
139
+ if (typeof jsonSchema.pattern === 'string') {
140
+ schema.pattern = jsonSchema.pattern
141
+ }
142
+
143
+ return schema
144
+ }
145
+
146
+ /**
147
+ * Convert AI SDK Tool to GenAI FunctionDeclaration
148
+ */
149
+ export function aiToolToGenAIFunction(tool: AnyTool): FunctionDeclaration {
150
+ // Extract the input schema - assume it's a Zod schema
151
+ const inputSchema = tool.inputSchema as z.ZodType<unknown>
152
+
153
+ // Get the tool name from the schema or generate one
154
+ let toolName = 'tool'
155
+ let jsonSchema: JSONSchema7 = {}
156
+
157
+ if (inputSchema) {
158
+ // Convert Zod schema to JSON Schema
159
+ jsonSchema = toJSONSchema(inputSchema) as JSONSchema7
160
+
161
+ // Extract name from Zod description if available
162
+ const description = inputSchema.description
163
+ if (description) {
164
+ const nameMatch = description.match(/name:\s*(\w+)/)
165
+ if (nameMatch) {
166
+ toolName = nameMatch[1] || ''
167
+ }
168
+ }
169
+ }
170
+
171
+ // Convert JSON Schema to GenAI Schema
172
+ const genAISchema = jsonSchemaToGenAISchema(jsonSchema)
173
+
174
+ // Create the FunctionDeclaration
175
+ const functionDeclaration: FunctionDeclaration = {
176
+ name: toolName,
177
+ description: tool.description || jsonSchema.description || 'Tool function',
178
+ parameters: genAISchema,
179
+ }
180
+
181
+ return functionDeclaration
182
+ }
183
+
184
+ /**
185
+ * Convert AI SDK Tool to GenAI CallableTool
186
+ */
187
+ export function aiToolToCallableTool(
188
+ tool: AnyTool,
189
+ name: string,
190
+ ): CallableTool & { name: string } {
191
+ const toolName = name || 'tool'
192
+
193
+ return {
194
+ name,
195
+ async tool(): Promise<GenAITool> {
196
+ const functionDeclaration = aiToolToGenAIFunction(tool)
197
+ if (name) {
198
+ functionDeclaration.name = name
199
+ }
200
+
201
+ return {
202
+ functionDeclarations: [functionDeclaration],
203
+ }
204
+ },
205
+
206
+ async callTool(functionCalls: FunctionCall[]): Promise<Part[]> {
207
+ const parts: Part[] = []
208
+
209
+ for (const functionCall of functionCalls) {
210
+ // Check if this function call matches our tool
211
+ if (
212
+ functionCall.name !== toolName &&
213
+ name &&
214
+ functionCall.name !== name
215
+ ) {
216
+ continue
217
+ }
218
+
219
+ // Execute the tool if it has an execute function
220
+ if (tool.execute) {
221
+ try {
222
+ const args: unknown = isRecord(functionCall.args)
223
+ ? functionCall.args
224
+ : {}
225
+ const result = await tool.execute(args, {
226
+ toolCallId: functionCall.id || '',
227
+ messages: [],
228
+ })
229
+
230
+ // Convert the result to a Part
231
+ const part: Part = {
232
+ functionResponse: {
233
+ id: functionCall.id,
234
+ name: functionCall.name || toolName,
235
+ response: {
236
+ output: result,
237
+ },
238
+ },
239
+ }
240
+ parts.push(part)
241
+ } catch (error) {
242
+ // Handle errors
243
+ const part: Part = {
244
+ functionResponse: {
245
+ id: functionCall.id,
246
+ name: functionCall.name || toolName,
247
+ response: {
248
+ error: error instanceof Error ? error.message : String(error),
249
+ },
250
+ },
251
+ }
252
+ parts.push(part)
253
+ }
254
+ }
255
+ }
256
+
257
+ return parts
258
+ },
259
+ }
260
+ }
261
+
262
+ export function extractSchemaFromTool(tool: AnyTool): JSONSchema7 {
263
+ const inputSchema = tool.inputSchema as z.ZodType<unknown>
264
+
265
+ if (!inputSchema) {
266
+ return {}
267
+ }
268
+
269
+ // Convert Zod schema to JSON Schema
270
+ return toJSONSchema(inputSchema) as JSONSchema7
271
+ }
272
+
273
+ /**
274
+ * Given an object of tools, creates an array of CallableTool
275
+ */
276
+ export function callableToolsFromObject(
277
+ tools: Record<string, AnyTool>,
278
+ ): Array<CallableTool & { name: string }> {
279
+ return Object.entries(tools).map(([name, tool]) =>
280
+ aiToolToCallableTool(tool, name),
281
+ )
282
+ }
package/src/ai-tool.ts ADDED
@@ -0,0 +1,39 @@
1
+ // Minimal tool definition helper used by Kimaki.
2
+ // This replaces the Vercel AI SDK `tool()` helper so Kimaki can define typed
3
+ // tools (Zod input schema + execute) without depending on the full `ai` package.
4
+
5
+ import type { z } from 'zod'
6
+
7
+ export type ToolExecuteOptions = {
8
+ toolCallId?: string
9
+ abortSignal?: AbortSignal
10
+ messages?: unknown[]
11
+ }
12
+
13
+ type BivariantCallback<Args extends unknown[], Return> = {
14
+ bivarianceHack(...args: Args): Return
15
+ }['bivarianceHack']
16
+
17
+ export type Tool<Input, Output> = {
18
+ description?: string
19
+ inputSchema: z.ZodType<Input>
20
+ execute?: BivariantCallback<
21
+ [input: Input, options: ToolExecuteOptions],
22
+ Promise<Output> | Output
23
+ >
24
+ }
25
+
26
+ export type AnyTool = {
27
+ description?: string
28
+ inputSchema: z.ZodTypeAny
29
+ execute?: BivariantCallback<
30
+ [input: unknown, options: ToolExecuteOptions],
31
+ Promise<unknown> | unknown
32
+ >
33
+ }
34
+
35
+ export function tool<Input, Output>(
36
+ definition: Tool<Input, Output>,
37
+ ): Tool<Input, Output> {
38
+ return definition
39
+ }