@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
package/bin.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import './dist/bin.js'
@@ -0,0 +1,233 @@
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
+ import { Type } from '@google/genai';
5
+ import { z, toJSONSchema } from 'zod';
6
+ /**
7
+ * Convert JSON Schema to GenAI Schema format
8
+ * Based on the actual implementation used by the GenAI package:
9
+ * https://github.com/googleapis/js-genai/blob/027f09db662ce6b30f737b10b4d2efcb4282a9b6/src/_transformers.ts#L294
10
+ */
11
+ function isRecord(value) {
12
+ return typeof value === 'object' && value !== null;
13
+ }
14
+ function jsonSchemaToGenAISchema(jsonSchema) {
15
+ const schema = {};
16
+ if (typeof jsonSchema === 'boolean') {
17
+ return schema;
18
+ }
19
+ const jsonSchemaType = (() => {
20
+ if (!jsonSchema.type) {
21
+ return undefined;
22
+ }
23
+ if (typeof jsonSchema.type === 'string') {
24
+ return jsonSchema.type;
25
+ }
26
+ if (Array.isArray(jsonSchema.type)) {
27
+ return jsonSchema.type.find((t) => t !== 'null') || jsonSchema.type[0];
28
+ }
29
+ return undefined;
30
+ })();
31
+ if (Array.isArray(jsonSchema.type) && jsonSchema.type.includes('null')) {
32
+ schema.nullable = true;
33
+ }
34
+ if (jsonSchemaType) {
35
+ switch (jsonSchemaType) {
36
+ case 'string':
37
+ schema.type = Type.STRING;
38
+ break;
39
+ case 'number':
40
+ schema.type = Type.NUMBER;
41
+ schema.format =
42
+ typeof jsonSchema.format === 'string' ? jsonSchema.format : 'float';
43
+ break;
44
+ case 'integer':
45
+ schema.type = Type.INTEGER;
46
+ schema.format =
47
+ typeof jsonSchema.format === 'string' ? jsonSchema.format : 'int32';
48
+ break;
49
+ case 'boolean':
50
+ schema.type = Type.BOOLEAN;
51
+ break;
52
+ case 'array': {
53
+ schema.type = Type.ARRAY;
54
+ const itemsSchema = (() => {
55
+ if (!jsonSchema.items) {
56
+ return undefined;
57
+ }
58
+ if (Array.isArray(jsonSchema.items)) {
59
+ return jsonSchema.items[0];
60
+ }
61
+ return jsonSchema.items;
62
+ })();
63
+ if (itemsSchema) {
64
+ schema.items = jsonSchemaToGenAISchema(itemsSchema);
65
+ }
66
+ if (typeof jsonSchema.minItems === 'number') {
67
+ schema.minItems = String(jsonSchema.minItems);
68
+ }
69
+ if (typeof jsonSchema.maxItems === 'number') {
70
+ schema.maxItems = String(jsonSchema.maxItems);
71
+ }
72
+ break;
73
+ }
74
+ case 'object':
75
+ schema.type = Type.OBJECT;
76
+ if (jsonSchema.properties) {
77
+ schema.properties = Object.fromEntries(Object.entries(jsonSchema.properties).map(([key, value]) => [
78
+ key,
79
+ jsonSchemaToGenAISchema(value),
80
+ ]));
81
+ }
82
+ if (Array.isArray(jsonSchema.required)) {
83
+ schema.required = jsonSchema.required;
84
+ }
85
+ break;
86
+ }
87
+ }
88
+ if (typeof jsonSchema.description === 'string') {
89
+ schema.description = jsonSchema.description;
90
+ }
91
+ if (Array.isArray(jsonSchema.enum)) {
92
+ schema.enum = jsonSchema.enum.map((x) => String(x));
93
+ }
94
+ if ('default' in jsonSchema) {
95
+ schema.default = jsonSchema.default;
96
+ }
97
+ if (Array.isArray(jsonSchema.examples) && jsonSchema.examples.length > 0) {
98
+ schema.example = jsonSchema.examples[0];
99
+ }
100
+ if (Array.isArray(jsonSchema.anyOf)) {
101
+ schema.anyOf = jsonSchema.anyOf.map((s) => jsonSchemaToGenAISchema(s));
102
+ }
103
+ else if (Array.isArray(jsonSchema.oneOf)) {
104
+ schema.anyOf = jsonSchema.oneOf.map((s) => jsonSchemaToGenAISchema(s));
105
+ }
106
+ if (typeof jsonSchema.minimum === 'number') {
107
+ schema.minimum = jsonSchema.minimum;
108
+ }
109
+ if (typeof jsonSchema.maximum === 'number') {
110
+ schema.maximum = jsonSchema.maximum;
111
+ }
112
+ if (typeof jsonSchema.minLength === 'number') {
113
+ schema.minLength = String(jsonSchema.minLength);
114
+ }
115
+ if (typeof jsonSchema.maxLength === 'number') {
116
+ schema.maxLength = String(jsonSchema.maxLength);
117
+ }
118
+ if (typeof jsonSchema.pattern === 'string') {
119
+ schema.pattern = jsonSchema.pattern;
120
+ }
121
+ return schema;
122
+ }
123
+ /**
124
+ * Convert AI SDK Tool to GenAI FunctionDeclaration
125
+ */
126
+ export function aiToolToGenAIFunction(tool) {
127
+ // Extract the input schema - assume it's a Zod schema
128
+ const inputSchema = tool.inputSchema;
129
+ // Get the tool name from the schema or generate one
130
+ let toolName = 'tool';
131
+ let jsonSchema = {};
132
+ if (inputSchema) {
133
+ // Convert Zod schema to JSON Schema
134
+ jsonSchema = toJSONSchema(inputSchema);
135
+ // Extract name from Zod description if available
136
+ const description = inputSchema.description;
137
+ if (description) {
138
+ const nameMatch = description.match(/name:\s*(\w+)/);
139
+ if (nameMatch) {
140
+ toolName = nameMatch[1] || '';
141
+ }
142
+ }
143
+ }
144
+ // Convert JSON Schema to GenAI Schema
145
+ const genAISchema = jsonSchemaToGenAISchema(jsonSchema);
146
+ // Create the FunctionDeclaration
147
+ const functionDeclaration = {
148
+ name: toolName,
149
+ description: tool.description || jsonSchema.description || 'Tool function',
150
+ parameters: genAISchema,
151
+ };
152
+ return functionDeclaration;
153
+ }
154
+ /**
155
+ * Convert AI SDK Tool to GenAI CallableTool
156
+ */
157
+ export function aiToolToCallableTool(tool, name) {
158
+ const toolName = name || 'tool';
159
+ return {
160
+ name,
161
+ async tool() {
162
+ const functionDeclaration = aiToolToGenAIFunction(tool);
163
+ if (name) {
164
+ functionDeclaration.name = name;
165
+ }
166
+ return {
167
+ functionDeclarations: [functionDeclaration],
168
+ };
169
+ },
170
+ async callTool(functionCalls) {
171
+ const parts = [];
172
+ for (const functionCall of functionCalls) {
173
+ // Check if this function call matches our tool
174
+ if (functionCall.name !== toolName &&
175
+ name &&
176
+ functionCall.name !== name) {
177
+ continue;
178
+ }
179
+ // Execute the tool if it has an execute function
180
+ if (tool.execute) {
181
+ try {
182
+ const args = isRecord(functionCall.args)
183
+ ? functionCall.args
184
+ : {};
185
+ const result = await tool.execute(args, {
186
+ toolCallId: functionCall.id || '',
187
+ messages: [],
188
+ });
189
+ // Convert the result to a Part
190
+ const part = {
191
+ functionResponse: {
192
+ id: functionCall.id,
193
+ name: functionCall.name || toolName,
194
+ response: {
195
+ output: result,
196
+ },
197
+ },
198
+ };
199
+ parts.push(part);
200
+ }
201
+ catch (error) {
202
+ // Handle errors
203
+ const part = {
204
+ functionResponse: {
205
+ id: functionCall.id,
206
+ name: functionCall.name || toolName,
207
+ response: {
208
+ error: error instanceof Error ? error.message : String(error),
209
+ },
210
+ },
211
+ };
212
+ parts.push(part);
213
+ }
214
+ }
215
+ }
216
+ return parts;
217
+ },
218
+ };
219
+ }
220
+ export function extractSchemaFromTool(tool) {
221
+ const inputSchema = tool.inputSchema;
222
+ if (!inputSchema) {
223
+ return {};
224
+ }
225
+ // Convert Zod schema to JSON Schema
226
+ return toJSONSchema(inputSchema);
227
+ }
228
+ /**
229
+ * Given an object of tools, creates an array of CallableTool
230
+ */
231
+ export function callableToolsFromObject(tools) {
232
+ return Object.entries(tools).map(([name, tool]) => aiToolToCallableTool(tool, name));
233
+ }
@@ -0,0 +1,267 @@
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 { aiToolToGenAIFunction, aiToolToCallableTool, extractSchemaFromTool, } from './ai-tool-to-genai.js';
6
+ describe('AI Tool to GenAI Conversion', () => {
7
+ it('should convert a simple Zod-based tool', () => {
8
+ const weatherTool = tool({
9
+ description: 'Get the current weather for a location',
10
+ inputSchema: z.object({
11
+ location: z.string().describe('The city name'),
12
+ unit: z.enum(['celsius', 'fahrenheit']).optional(),
13
+ }),
14
+ execute: async ({ location, unit }) => {
15
+ return {
16
+ temperature: 72,
17
+ unit: unit || 'fahrenheit',
18
+ condition: 'sunny',
19
+ };
20
+ },
21
+ });
22
+ const genAIFunction = aiToolToGenAIFunction(weatherTool);
23
+ expect(genAIFunction).toMatchInlineSnapshot(`
24
+ {
25
+ "description": "Get the current weather for a location",
26
+ "name": "tool",
27
+ "parameters": {
28
+ "properties": {
29
+ "location": {
30
+ "description": "The city name",
31
+ "type": "STRING",
32
+ },
33
+ "unit": {
34
+ "enum": [
35
+ "celsius",
36
+ "fahrenheit",
37
+ ],
38
+ "type": "STRING",
39
+ },
40
+ },
41
+ "required": [
42
+ "location",
43
+ ],
44
+ "type": "OBJECT",
45
+ },
46
+ }
47
+ `);
48
+ });
49
+ it('should handle complex nested schemas', () => {
50
+ const complexTool = tool({
51
+ description: 'Process complex data',
52
+ inputSchema: z.object({
53
+ user: z.object({
54
+ name: z.string(),
55
+ age: z.number().int().min(0).max(150),
56
+ email: z.string().email(),
57
+ }),
58
+ preferences: z.array(z.string()),
59
+ metadata: z.record(z.string(), z.unknown()).optional(),
60
+ }),
61
+ execute: async (input) => input,
62
+ });
63
+ const genAIFunction = aiToolToGenAIFunction(complexTool);
64
+ expect(genAIFunction.parameters).toMatchInlineSnapshot(`
65
+ {
66
+ "properties": {
67
+ "metadata": {
68
+ "type": "OBJECT",
69
+ },
70
+ "preferences": {
71
+ "items": {
72
+ "type": "STRING",
73
+ },
74
+ "type": "ARRAY",
75
+ },
76
+ "user": {
77
+ "properties": {
78
+ "age": {
79
+ "format": "int32",
80
+ "maximum": 150,
81
+ "minimum": 0,
82
+ "type": "INTEGER",
83
+ },
84
+ "email": {
85
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
86
+ "type": "STRING",
87
+ },
88
+ "name": {
89
+ "type": "STRING",
90
+ },
91
+ },
92
+ "required": [
93
+ "name",
94
+ "age",
95
+ "email",
96
+ ],
97
+ "type": "OBJECT",
98
+ },
99
+ },
100
+ "required": [
101
+ "user",
102
+ "preferences",
103
+ ],
104
+ "type": "OBJECT",
105
+ }
106
+ `);
107
+ });
108
+ it('should extract schema from tool', () => {
109
+ const testTool = tool({
110
+ inputSchema: z.object({
111
+ test: z.string(),
112
+ }),
113
+ execute: async () => { },
114
+ });
115
+ const schema = extractSchemaFromTool(testTool);
116
+ expect(schema).toMatchInlineSnapshot(`
117
+ {
118
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
119
+ "additionalProperties": false,
120
+ "properties": {
121
+ "test": {
122
+ "type": "string",
123
+ },
124
+ },
125
+ "required": [
126
+ "test",
127
+ ],
128
+ "type": "object",
129
+ }
130
+ `);
131
+ });
132
+ it('should handle tools with no input schema', () => {
133
+ const simpleTool = tool({
134
+ description: 'Simple tool with no inputs',
135
+ inputSchema: z.object({}),
136
+ execute: async () => ({ result: 'done' }),
137
+ });
138
+ const genAIFunction = aiToolToGenAIFunction(simpleTool);
139
+ expect(genAIFunction).toMatchInlineSnapshot(`
140
+ {
141
+ "description": "Simple tool with no inputs",
142
+ "name": "tool",
143
+ "parameters": {
144
+ "properties": {},
145
+ "type": "OBJECT",
146
+ },
147
+ }
148
+ `);
149
+ });
150
+ it('should handle union types', () => {
151
+ const unionTool = tool({
152
+ description: 'Tool with union types',
153
+ inputSchema: z.object({
154
+ value: z.union([z.string(), z.number(), z.boolean()]),
155
+ }),
156
+ execute: async ({ value }) => ({ received: value }),
157
+ });
158
+ const genAIFunction = aiToolToGenAIFunction(unionTool);
159
+ expect(genAIFunction.parameters?.properties?.value).toMatchInlineSnapshot(`
160
+ {
161
+ "anyOf": [
162
+ {
163
+ "type": "STRING",
164
+ },
165
+ {
166
+ "format": "float",
167
+ "type": "NUMBER",
168
+ },
169
+ {
170
+ "type": "BOOLEAN",
171
+ },
172
+ ],
173
+ }
174
+ `);
175
+ });
176
+ it('should create a CallableTool', async () => {
177
+ const weatherTool = tool({
178
+ description: 'Get weather',
179
+ inputSchema: z.object({
180
+ location: z.string(),
181
+ }),
182
+ execute: async ({ location }) => ({
183
+ temperature: 72,
184
+ location,
185
+ }),
186
+ });
187
+ const callableTool = aiToolToCallableTool(weatherTool, 'weather');
188
+ // Test tool() method
189
+ const genAITool = await callableTool.tool();
190
+ expect(genAITool.functionDeclarations).toMatchInlineSnapshot(`
191
+ [
192
+ {
193
+ "description": "Get weather",
194
+ "name": "weather",
195
+ "parameters": {
196
+ "properties": {
197
+ "location": {
198
+ "type": "STRING",
199
+ },
200
+ },
201
+ "required": [
202
+ "location",
203
+ ],
204
+ "type": "OBJECT",
205
+ },
206
+ },
207
+ ]
208
+ `);
209
+ // Test callTool() method
210
+ const functionCall = {
211
+ id: 'call_123',
212
+ name: 'weather',
213
+ args: { location: 'San Francisco' },
214
+ };
215
+ const parts = await callableTool.callTool([functionCall]);
216
+ expect(parts).toMatchInlineSnapshot(`
217
+ [
218
+ {
219
+ "functionResponse": {
220
+ "id": "call_123",
221
+ "name": "weather",
222
+ "response": {
223
+ "output": {
224
+ "location": "San Francisco",
225
+ "temperature": 72,
226
+ },
227
+ },
228
+ },
229
+ },
230
+ ]
231
+ `);
232
+ });
233
+ it('should handle tool execution errors', async () => {
234
+ const errorTool = tool({
235
+ description: 'Tool that throws',
236
+ inputSchema: z.object({
237
+ trigger: z.boolean(),
238
+ }),
239
+ execute: async ({ trigger }) => {
240
+ if (trigger) {
241
+ throw new Error('Tool execution failed');
242
+ }
243
+ return { success: true };
244
+ },
245
+ });
246
+ const callableTool = aiToolToCallableTool(errorTool, 'error_tool');
247
+ const functionCall = {
248
+ id: 'call_error',
249
+ name: 'error_tool',
250
+ args: { trigger: true },
251
+ };
252
+ const parts = await callableTool.callTool([functionCall]);
253
+ expect(parts).toMatchInlineSnapshot(`
254
+ [
255
+ {
256
+ "functionResponse": {
257
+ "id": "call_error",
258
+ "name": "error_tool",
259
+ "response": {
260
+ "error": "Tool execution failed",
261
+ },
262
+ },
263
+ },
264
+ ]
265
+ `);
266
+ });
267
+ });
@@ -0,0 +1,6 @@
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
+ export function tool(definition) {
5
+ return definition;
6
+ }
package/dist/bin.js ADDED
@@ -0,0 +1,87 @@
1
+ // Respawn wrapper for the kimaki bot process.
2
+ // When running the default command (no subcommand) with --auto-restart,
3
+ // spawns cli.js as a child process and restarts it on non-zero exit codes
4
+ // (crash, OOM kill, etc). Intentional exits (code 0 or EXIT_NO_RESTART=64)
5
+ // are not restarted.
6
+ //
7
+ // Subcommands (send, tunnel, project, etc.) run directly without the wrapper
8
+ // since they are short-lived and don't need crash recovery.
9
+ //
10
+ // When __KIMAKI_CHILD is set, we're the child process -- just run cli.js directly.
11
+ //
12
+ // V8 heap snapshot flags:
13
+ // Injects --heapsnapshot-near-heap-limit=3 and --diagnostic-dir so V8 writes
14
+ // heap snapshots internally as it approaches the heap limit. This catches OOM
15
+ // situations where SIGKILL (exit 137) would kill the process before our
16
+ // heap-monitor.ts polling can react. The polling monitor is kept as an early
17
+ // warning system at 85% usage; the V8 flag is the last-resort safety net.
18
+ import { spawn } from 'node:child_process';
19
+ import fs from 'node:fs';
20
+ import os from 'node:os';
21
+ import path from 'node:path';
22
+ const HEAP_SNAPSHOT_DIR = path.join(os.homedir(), '.kimaki', 'heap-snapshots');
23
+ // First arg after node + script is either a subcommand or a flag.
24
+ // If it doesn't start with '-', it's a subcommand (e.g. "send", "tunnel", "project").
25
+ const firstArg = process.argv[2];
26
+ const isSubcommand = firstArg && !firstArg.startsWith('-');
27
+ const hasAutoRestart = process.argv.includes('--auto-restart');
28
+ if (process.env.__KIMAKI_CHILD || isSubcommand || !hasAutoRestart) {
29
+ await import('./cli.js');
30
+ }
31
+ else {
32
+ const EXIT_NO_RESTART = 64;
33
+ const MAX_RAPID_RESTARTS = 5;
34
+ const RAPID_RESTART_WINDOW_MS = 60_000;
35
+ const RESTART_DELAY_MS = 2_000;
36
+ const restartTimestamps = [];
37
+ let child = null;
38
+ // Track when we forwarded a termination signal so we don't restart after graceful shutdown
39
+ let shutdownRequested = false;
40
+ function start() {
41
+ if (!fs.existsSync(HEAP_SNAPSHOT_DIR)) {
42
+ fs.mkdirSync(HEAP_SNAPSHOT_DIR, { recursive: true });
43
+ }
44
+ const heapArgs = [
45
+ `--heapsnapshot-near-heap-limit=3`,
46
+ `--diagnostic-dir=${HEAP_SNAPSHOT_DIR}`,
47
+ ];
48
+ child = spawn(process.argv[0], [...heapArgs, ...process.execArgv, ...process.argv.slice(1)], {
49
+ stdio: 'inherit',
50
+ env: { ...process.env, __KIMAKI_CHILD: '1' },
51
+ });
52
+ child.on('exit', (code, signal) => {
53
+ if (code === 0 || code === EXIT_NO_RESTART || shutdownRequested) {
54
+ process.exit(code ?? 0);
55
+ return;
56
+ }
57
+ const now = Date.now();
58
+ restartTimestamps.push(now);
59
+ while (restartTimestamps.length > 0 &&
60
+ restartTimestamps[0] < now - RAPID_RESTART_WINDOW_MS) {
61
+ restartTimestamps.shift();
62
+ }
63
+ if (restartTimestamps.length > MAX_RAPID_RESTARTS) {
64
+ console.error(`[kimaki] Crash loop detected (${MAX_RAPID_RESTARTS} crashes in ${RAPID_RESTART_WINDOW_MS / 1000}s), exiting`);
65
+ process.exit(1);
66
+ return;
67
+ }
68
+ const reason = signal ? `signal ${signal}` : `code ${code}`;
69
+ console.error(`[kimaki] Process exited with ${reason}, restarting in ${RESTART_DELAY_MS / 1000}s...`);
70
+ setTimeout(start, RESTART_DELAY_MS);
71
+ });
72
+ }
73
+ // Forward signals to child so graceful shutdown and heap snapshots work.
74
+ // SIGTERM/SIGINT mark shutdownRequested so we don't restart after graceful exit.
75
+ for (const sig of ['SIGTERM', 'SIGINT']) {
76
+ process.on(sig, () => {
77
+ shutdownRequested = true;
78
+ child?.kill(sig);
79
+ });
80
+ }
81
+ for (const sig of ['SIGUSR1', 'SIGUSR2']) {
82
+ process.on(sig, () => {
83
+ child?.kill(sig);
84
+ });
85
+ }
86
+ start();
87
+ }