kimaki 0.4.24 → 0.4.26

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 (86) hide show
  1. package/bin.js +6 -1
  2. package/dist/acp-client.test.js +149 -0
  3. package/dist/ai-tool-to-genai.js +3 -0
  4. package/dist/channel-management.js +14 -9
  5. package/dist/cli.js +148 -17
  6. package/dist/commands/abort.js +78 -0
  7. package/dist/commands/add-project.js +98 -0
  8. package/dist/commands/agent.js +152 -0
  9. package/dist/commands/ask-question.js +183 -0
  10. package/dist/commands/create-new-project.js +78 -0
  11. package/dist/commands/fork.js +186 -0
  12. package/dist/commands/model.js +313 -0
  13. package/dist/commands/permissions.js +126 -0
  14. package/dist/commands/queue.js +129 -0
  15. package/dist/commands/resume.js +145 -0
  16. package/dist/commands/session.js +142 -0
  17. package/dist/commands/share.js +80 -0
  18. package/dist/commands/types.js +2 -0
  19. package/dist/commands/undo-redo.js +161 -0
  20. package/dist/commands/user-command.js +145 -0
  21. package/dist/database.js +54 -0
  22. package/dist/discord-bot.js +35 -32
  23. package/dist/discord-utils.js +81 -15
  24. package/dist/format-tables.js +3 -0
  25. package/dist/genai-worker-wrapper.js +3 -0
  26. package/dist/genai-worker.js +3 -0
  27. package/dist/genai.js +3 -0
  28. package/dist/interaction-handler.js +89 -695
  29. package/dist/logger.js +46 -5
  30. package/dist/markdown.js +107 -0
  31. package/dist/markdown.test.js +31 -1
  32. package/dist/message-formatting.js +113 -28
  33. package/dist/message-formatting.test.js +73 -0
  34. package/dist/opencode.js +73 -16
  35. package/dist/session-handler.js +176 -63
  36. package/dist/system-message.js +7 -38
  37. package/dist/tools.js +3 -0
  38. package/dist/utils.js +3 -0
  39. package/dist/voice-handler.js +21 -8
  40. package/dist/voice.js +31 -12
  41. package/dist/worker-types.js +3 -0
  42. package/dist/xml.js +3 -0
  43. package/package.json +3 -3
  44. package/src/__snapshots__/compact-session-context-no-system.md +35 -0
  45. package/src/__snapshots__/compact-session-context.md +47 -0
  46. package/src/ai-tool-to-genai.ts +4 -0
  47. package/src/channel-management.ts +24 -8
  48. package/src/cli.ts +163 -18
  49. package/src/commands/abort.ts +94 -0
  50. package/src/commands/add-project.ts +139 -0
  51. package/src/commands/agent.ts +201 -0
  52. package/src/commands/ask-question.ts +276 -0
  53. package/src/commands/create-new-project.ts +111 -0
  54. package/src/{fork.ts → commands/fork.ts} +40 -7
  55. package/src/{model-command.ts → commands/model.ts} +31 -9
  56. package/src/commands/permissions.ts +146 -0
  57. package/src/commands/queue.ts +181 -0
  58. package/src/commands/resume.ts +230 -0
  59. package/src/commands/session.ts +184 -0
  60. package/src/commands/share.ts +96 -0
  61. package/src/commands/types.ts +25 -0
  62. package/src/commands/undo-redo.ts +213 -0
  63. package/src/commands/user-command.ts +178 -0
  64. package/src/database.ts +65 -0
  65. package/src/discord-bot.ts +40 -33
  66. package/src/discord-utils.ts +88 -14
  67. package/src/format-tables.ts +4 -0
  68. package/src/genai-worker-wrapper.ts +4 -0
  69. package/src/genai-worker.ts +4 -0
  70. package/src/genai.ts +4 -0
  71. package/src/interaction-handler.ts +111 -924
  72. package/src/logger.ts +51 -10
  73. package/src/markdown.test.ts +45 -1
  74. package/src/markdown.ts +136 -0
  75. package/src/message-formatting.test.ts +81 -0
  76. package/src/message-formatting.ts +143 -30
  77. package/src/opencode.ts +84 -21
  78. package/src/session-handler.ts +248 -91
  79. package/src/system-message.ts +8 -38
  80. package/src/tools.ts +4 -0
  81. package/src/utils.ts +4 -0
  82. package/src/voice-handler.ts +24 -9
  83. package/src/voice.ts +36 -13
  84. package/src/worker-types.ts +4 -0
  85. package/src/xml.ts +4 -0
  86. package/README.md +0 -48
@@ -1,3 +1,7 @@
1
+ // Discord-specific utility functions.
2
+ // Handles markdown splitting for Discord's 2000-char limit, code block escaping,
3
+ // thread message sending, and channel metadata extraction from topic tags.
4
+
1
5
  import {
2
6
  ChannelType,
3
7
  type Message,
@@ -12,6 +16,8 @@ import { createLogger } from './logger.js'
12
16
  const discordLogger = createLogger('DISCORD')
13
17
 
14
18
  export const SILENT_MESSAGE_FLAGS = 4 | 4096
19
+ // Same as SILENT but without SuppressNotifications - triggers badge/notification
20
+ export const NOTIFY_MESSAGE_FLAGS = 4
15
21
 
16
22
  export function escapeBackticksInCodeBlocks(markdown: string): string {
17
23
  const lexer = new Lexer()
@@ -79,31 +85,93 @@ export function splitMarkdownForDiscord({
79
85
  let currentChunk = ''
80
86
  let currentLang: string | null = null
81
87
 
88
+ // helper to split a long line into smaller pieces at word boundaries or hard breaks
89
+ const splitLongLine = (text: string, available: number, inCode: boolean): string[] => {
90
+ const pieces: string[] = []
91
+ let remaining = text
92
+
93
+ while (remaining.length > available) {
94
+ let splitAt = available
95
+ // for non-code, try to split at word boundary
96
+ if (!inCode) {
97
+ const lastSpace = remaining.lastIndexOf(' ', available)
98
+ if (lastSpace > available * 0.5) {
99
+ splitAt = lastSpace + 1
100
+ }
101
+ }
102
+ pieces.push(remaining.slice(0, splitAt))
103
+ remaining = remaining.slice(splitAt)
104
+ }
105
+ if (remaining) {
106
+ pieces.push(remaining)
107
+ }
108
+ return pieces
109
+ }
110
+
82
111
  for (const line of lines) {
83
112
  const wouldExceed = currentChunk.length + line.text.length > maxLength
84
113
 
85
- if (wouldExceed && currentChunk) {
86
- if (currentLang !== null) {
87
- currentChunk += '```\n'
88
- }
89
- chunks.push(currentChunk)
114
+ if (wouldExceed) {
115
+ // handle case where single line is longer than maxLength
116
+ if (line.text.length > maxLength) {
117
+ // first, flush current chunk if any
118
+ if (currentChunk) {
119
+ if (currentLang !== null) {
120
+ currentChunk += '```\n'
121
+ }
122
+ chunks.push(currentChunk)
123
+ currentChunk = ''
124
+ }
125
+
126
+ // calculate overhead for code block markers
127
+ const codeBlockOverhead = line.inCodeBlock ? ('```' + line.lang + '\n').length + '```\n'.length : 0
128
+ const availablePerChunk = maxLength - codeBlockOverhead - 50 // safety margin
129
+
130
+ const pieces = splitLongLine(line.text, availablePerChunk, line.inCodeBlock)
131
+
132
+ for (let i = 0; i < pieces.length; i++) {
133
+ const piece = pieces[i]!
134
+ if (line.inCodeBlock) {
135
+ chunks.push('```' + line.lang + '\n' + piece + '```\n')
136
+ } else {
137
+ chunks.push(piece)
138
+ }
139
+ }
90
140
 
91
- if (line.isClosingFence && currentLang !== null) {
92
- currentChunk = ''
93
141
  currentLang = null
94
142
  continue
95
143
  }
96
144
 
97
- if (line.inCodeBlock || line.isOpeningFence) {
98
- const lang = line.lang
99
- currentChunk = '```' + lang + '\n'
100
- if (!line.isOpeningFence) {
101
- currentChunk += line.text
145
+ // normal case: line fits in a chunk but current chunk would overflow
146
+ if (currentChunk) {
147
+ if (currentLang !== null) {
148
+ currentChunk += '```\n'
149
+ }
150
+ chunks.push(currentChunk)
151
+
152
+ if (line.isClosingFence && currentLang !== null) {
153
+ currentChunk = ''
154
+ currentLang = null
155
+ continue
156
+ }
157
+
158
+ if (line.inCodeBlock || line.isOpeningFence) {
159
+ const lang = line.lang
160
+ currentChunk = '```' + lang + '\n'
161
+ if (!line.isOpeningFence) {
162
+ currentChunk += line.text
163
+ }
164
+ currentLang = lang
165
+ } else {
166
+ currentChunk = line.text
167
+ currentLang = null
102
168
  }
103
- currentLang = lang
104
169
  } else {
170
+ // currentChunk is empty but line still exceeds - shouldn't happen after above check
105
171
  currentChunk = line.text
106
- currentLang = null
172
+ if (line.inCodeBlock || line.isOpeningFence) {
173
+ currentLang = line.lang
174
+ }
107
175
  }
108
176
  } else {
109
177
  currentChunk += line.text
@@ -125,12 +193,18 @@ export function splitMarkdownForDiscord({
125
193
  export async function sendThreadMessage(
126
194
  thread: ThreadChannel,
127
195
  content: string,
196
+ options?: { flags?: number }
128
197
  ): Promise<Message> {
129
198
  const MAX_LENGTH = 2000
130
199
 
131
200
  content = formatMarkdownTables(content)
132
201
  content = escapeBackticksInCodeBlocks(content)
133
202
 
203
+ // If custom flags provided, send as single message (no chunking)
204
+ if (options?.flags !== undefined) {
205
+ return thread.send({ content, flags: options.flags })
206
+ }
207
+
134
208
  const chunks = splitMarkdownForDiscord({ content, maxLength: MAX_LENGTH })
135
209
 
136
210
  if (chunks.length > 1) {
@@ -1,3 +1,7 @@
1
+ // Markdown table to code block converter.
2
+ // Discord doesn't render GFM tables, so this converts them to
3
+ // space-aligned code blocks for proper monospace display.
4
+
1
5
  import { Lexer, type Token, type Tokens } from 'marked'
2
6
 
3
7
  export function formatMarkdownTables(markdown: string): string {
@@ -1,3 +1,7 @@
1
+ // Main thread interface for the GenAI worker.
2
+ // Spawns and manages the worker thread, handling message passing for
3
+ // audio input/output, tool call completions, and graceful shutdown.
4
+
1
5
  import { Worker } from 'node:worker_threads'
2
6
  import type { WorkerInMessage, WorkerOutMessage } from './worker-types.js'
3
7
  import type { Tool as AITool } from 'ai'
@@ -1,3 +1,7 @@
1
+ // Worker thread for GenAI voice processing.
2
+ // Runs in a separate thread to handle audio encoding/decoding without blocking.
3
+ // Resamples 24kHz GenAI output to 48kHz stereo Opus packets for Discord.
4
+
1
5
  import { parentPort, threadId } from 'node:worker_threads'
2
6
  import { createWriteStream, type WriteStream } from 'node:fs'
3
7
  import { mkdir } from 'node:fs/promises'
package/src/genai.ts CHANGED
@@ -1,3 +1,7 @@
1
+ // Google GenAI Live session manager for real-time voice interactions.
2
+ // Establishes bidirectional audio streaming with Gemini, handles tool calls,
3
+ // and manages the assistant's audio output for Discord voice channels.
4
+
1
5
  import {
2
6
  GoogleGenAI,
3
7
  LiveServerMessage,