kimaki 0.4.45 → 0.4.46
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.
- package/dist/commands/create-new-project.js +2 -0
- package/dist/commands/fork.js +2 -0
- package/dist/commands/resume.js +2 -0
- package/dist/commands/session.js +2 -0
- package/dist/commands/user-command.js +2 -0
- package/dist/commands/worktree.js +2 -0
- package/dist/discord-bot.js +2 -0
- package/dist/session-handler.js +40 -1
- package/package.json +1 -1
- package/src/commands/create-new-project.ts +3 -0
- package/src/commands/fork.ts +3 -0
- package/src/commands/resume.ts +3 -0
- package/src/commands/session.ts +3 -0
- package/src/commands/user-command.ts +3 -0
- package/src/commands/worktree.ts +3 -0
- package/src/discord-bot.ts +3 -0
- package/src/session-handler.ts +43 -1
|
@@ -63,6 +63,8 @@ export async function handleCreateNewProjectCommand({ command, appId, }) {
|
|
|
63
63
|
autoArchiveDuration: 1440,
|
|
64
64
|
reason: 'New project session',
|
|
65
65
|
});
|
|
66
|
+
// Add user to thread so it appears in their sidebar
|
|
67
|
+
await thread.members.add(command.user.id);
|
|
66
68
|
await handleOpencodeSession({
|
|
67
69
|
prompt: 'The project was just initialized. Say hi and ask what the user wants to build.',
|
|
68
70
|
thread,
|
package/dist/commands/fork.js
CHANGED
|
@@ -162,6 +162,8 @@ export async function handleForkSelectMenu(interaction) {
|
|
|
162
162
|
autoArchiveDuration: ThreadAutoArchiveDuration.OneDay,
|
|
163
163
|
reason: `Forked from session ${sessionId}`,
|
|
164
164
|
});
|
|
165
|
+
// Add user to thread so it appears in their sidebar
|
|
166
|
+
await thread.members.add(interaction.user.id);
|
|
165
167
|
getDatabase()
|
|
166
168
|
.prepare('INSERT OR REPLACE INTO thread_sessions (thread_id, session_id) VALUES (?, ?)')
|
|
167
169
|
.run(thread.id, forkedSession.id);
|
package/dist/commands/resume.js
CHANGED
|
@@ -51,6 +51,8 @@ export async function handleResumeCommand({ command, appId }) {
|
|
|
51
51
|
autoArchiveDuration: ThreadAutoArchiveDuration.OneDay,
|
|
52
52
|
reason: `Resuming session ${sessionId}`,
|
|
53
53
|
});
|
|
54
|
+
// Add user to thread so it appears in their sidebar
|
|
55
|
+
await thread.members.add(command.user.id);
|
|
54
56
|
getDatabase()
|
|
55
57
|
.prepare('INSERT OR REPLACE INTO thread_sessions (thread_id, session_id) VALUES (?, ?)')
|
|
56
58
|
.run(thread.id, sessionId);
|
package/dist/commands/session.js
CHANGED
|
@@ -58,6 +58,8 @@ export async function handleSessionCommand({ command, appId }) {
|
|
|
58
58
|
autoArchiveDuration: 1440,
|
|
59
59
|
reason: 'OpenCode session',
|
|
60
60
|
});
|
|
61
|
+
// Add user to thread so it appears in their sidebar
|
|
62
|
+
await thread.members.add(command.user.id);
|
|
61
63
|
await command.editReply(`Created new session in ${thread.toString()}`);
|
|
62
64
|
await handleOpencodeSession({
|
|
63
65
|
prompt: fullPrompt,
|
|
@@ -105,6 +105,8 @@ export const handleUserCommand = async ({ command, appId }) => {
|
|
|
105
105
|
autoArchiveDuration: 1440,
|
|
106
106
|
reason: `OpenCode command: ${commandName}`,
|
|
107
107
|
});
|
|
108
|
+
// Add user to thread so it appears in their sidebar
|
|
109
|
+
await newThread.members.add(command.user.id);
|
|
108
110
|
await command.editReply(`Started /${commandName} in ${newThread.toString()}`);
|
|
109
111
|
await handleOpencodeSession({
|
|
110
112
|
prompt: '', // Not used when command is set
|
|
@@ -167,6 +167,8 @@ export async function handleNewWorktreeCommand({ command, appId, }) {
|
|
|
167
167
|
autoArchiveDuration: 1440,
|
|
168
168
|
reason: 'Worktree session',
|
|
169
169
|
});
|
|
170
|
+
// Add user to thread so it appears in their sidebar
|
|
171
|
+
await thread.members.add(command.user.id);
|
|
170
172
|
return { thread, starterMessage };
|
|
171
173
|
},
|
|
172
174
|
catch: (e) => new WorktreeError('Failed to create thread', { cause: e }),
|
package/dist/discord-bot.js
CHANGED
|
@@ -309,6 +309,8 @@ export async function startDiscordBot({ token, appId, discordClient, useWorktree
|
|
|
309
309
|
autoArchiveDuration: ThreadAutoArchiveDuration.OneDay,
|
|
310
310
|
reason: 'Start Claude session',
|
|
311
311
|
});
|
|
312
|
+
// Add user to thread so it appears in their sidebar
|
|
313
|
+
await thread.members.add(message.author.id);
|
|
312
314
|
discordLogger.log(`Created thread "${thread.name}" (${thread.id})`);
|
|
313
315
|
// Create worktree if worktrees are enabled (CLI flag OR channel setting)
|
|
314
316
|
let sessionDirectory = projectDirectory;
|
package/dist/session-handler.js
CHANGED
|
@@ -29,6 +29,7 @@ function buildPermissionDedupeKey({ permission, directory, }) {
|
|
|
29
29
|
// Queue of messages waiting to be sent after current response finishes
|
|
30
30
|
// Key is threadId, value is array of queued messages
|
|
31
31
|
export const messageQueue = new Map();
|
|
32
|
+
const activeEventHandlers = new Map();
|
|
32
33
|
export function addToQueue({ threadId, message, }) {
|
|
33
34
|
const queue = messageQueue.get(threadId) || [];
|
|
34
35
|
queue.push(message);
|
|
@@ -176,6 +177,15 @@ export async function handleOpencodeSession({ prompt, thread, projectDirectory,
|
|
|
176
177
|
if (existingController) {
|
|
177
178
|
voiceLogger.log(`[ABORT] Cancelling existing request for session: ${session.id}`);
|
|
178
179
|
existingController.abort(new Error('New request started'));
|
|
180
|
+
const abortResult = await errore.tryAsync(() => {
|
|
181
|
+
return getClient().session.abort({
|
|
182
|
+
path: { id: session.id },
|
|
183
|
+
query: { directory: sdkDirectory },
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
if (abortResult instanceof Error) {
|
|
187
|
+
sessionLogger.log(`[ABORT] Server abort failed (may be already done):`, abortResult);
|
|
188
|
+
}
|
|
179
189
|
}
|
|
180
190
|
// Auto-reject ALL pending permissions for this thread
|
|
181
191
|
const threadPermissions = pendingPermissions.get(thread.id);
|
|
@@ -230,6 +240,16 @@ export async function handleOpencodeSession({ prompt, thread, projectDirectory,
|
|
|
230
240
|
sessionLogger.log(`[DEBOUNCE] Aborted before subscribe, exiting`);
|
|
231
241
|
return;
|
|
232
242
|
}
|
|
243
|
+
const previousHandler = activeEventHandlers.get(thread.id);
|
|
244
|
+
if (previousHandler) {
|
|
245
|
+
sessionLogger.log(`[EVENT] Waiting for previous handler to finish`);
|
|
246
|
+
await Promise.race([
|
|
247
|
+
previousHandler,
|
|
248
|
+
new Promise((resolve) => {
|
|
249
|
+
setTimeout(resolve, 1000);
|
|
250
|
+
}),
|
|
251
|
+
]);
|
|
252
|
+
}
|
|
233
253
|
// Use v2 client for event subscription (has proper types for question.asked events)
|
|
234
254
|
const clientV2 = getOpencodeClientV2(directory);
|
|
235
255
|
if (!clientV2) {
|
|
@@ -254,6 +274,7 @@ export async function handleOpencodeSession({ prompt, thread, projectDirectory,
|
|
|
254
274
|
let lastDisplayedContextPercentage = 0;
|
|
255
275
|
let modelContextLimit;
|
|
256
276
|
let assistantMessageId;
|
|
277
|
+
let handlerPromise = null;
|
|
257
278
|
let typingInterval = null;
|
|
258
279
|
function startTyping() {
|
|
259
280
|
if (abortController.signal.aborted) {
|
|
@@ -509,6 +530,10 @@ export async function handleOpencodeSession({ prompt, thread, projectDirectory,
|
|
|
509
530
|
}
|
|
510
531
|
};
|
|
511
532
|
const handleSubtaskPart = async (part, subtaskInfo) => {
|
|
533
|
+
// In text-only mode, skip all subtask output (they're tool-related)
|
|
534
|
+
if (verbosity === 'text-only') {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
512
537
|
if (part.type === 'step-start' || part.type === 'step-finish') {
|
|
513
538
|
return;
|
|
514
539
|
}
|
|
@@ -840,7 +865,13 @@ export async function handleOpencodeSession({ prompt, thread, projectDirectory,
|
|
|
840
865
|
}
|
|
841
866
|
};
|
|
842
867
|
const promptResult = await errore.tryAsync(async () => {
|
|
843
|
-
const
|
|
868
|
+
const newHandlerPromise = eventHandler().finally(() => {
|
|
869
|
+
if (activeEventHandlers.get(thread.id) === newHandlerPromise) {
|
|
870
|
+
activeEventHandlers.delete(thread.id);
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
activeEventHandlers.set(thread.id, newHandlerPromise);
|
|
874
|
+
handlerPromise = newHandlerPromise;
|
|
844
875
|
if (abortController.signal.aborted) {
|
|
845
876
|
sessionLogger.log(`[DEBOUNCE] Aborted before prompt, exiting`);
|
|
846
877
|
return;
|
|
@@ -940,6 +971,14 @@ export async function handleOpencodeSession({ prompt, thread, projectDirectory,
|
|
|
940
971
|
}
|
|
941
972
|
return { sessionID: session.id, result: response.data, port };
|
|
942
973
|
});
|
|
974
|
+
if (handlerPromise) {
|
|
975
|
+
await Promise.race([
|
|
976
|
+
handlerPromise,
|
|
977
|
+
new Promise((resolve) => {
|
|
978
|
+
setTimeout(resolve, 1000);
|
|
979
|
+
}),
|
|
980
|
+
]);
|
|
981
|
+
}
|
|
943
982
|
if (!errore.isError(promptResult)) {
|
|
944
983
|
return promptResult;
|
|
945
984
|
}
|
package/package.json
CHANGED
|
@@ -88,6 +88,9 @@ export async function handleCreateNewProjectCommand({
|
|
|
88
88
|
reason: 'New project session',
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
+
// Add user to thread so it appears in their sidebar
|
|
92
|
+
await thread.members.add(command.user.id)
|
|
93
|
+
|
|
91
94
|
await handleOpencodeSession({
|
|
92
95
|
prompt: 'The project was just initialized. Say hi and ask what the user wants to build.',
|
|
93
96
|
thread,
|
package/src/commands/fork.ts
CHANGED
|
@@ -215,6 +215,9 @@ export async function handleForkSelectMenu(
|
|
|
215
215
|
reason: `Forked from session ${sessionId}`,
|
|
216
216
|
})
|
|
217
217
|
|
|
218
|
+
// Add user to thread so it appears in their sidebar
|
|
219
|
+
await thread.members.add(interaction.user.id)
|
|
220
|
+
|
|
218
221
|
getDatabase()
|
|
219
222
|
.prepare('INSERT OR REPLACE INTO thread_sessions (thread_id, session_id) VALUES (?, ?)')
|
|
220
223
|
.run(thread.id, forkedSession.id)
|
package/src/commands/resume.ts
CHANGED
|
@@ -73,6 +73,9 @@ export async function handleResumeCommand({ command, appId }: CommandContext): P
|
|
|
73
73
|
reason: `Resuming session ${sessionId}`,
|
|
74
74
|
})
|
|
75
75
|
|
|
76
|
+
// Add user to thread so it appears in their sidebar
|
|
77
|
+
await thread.members.add(command.user.id)
|
|
78
|
+
|
|
76
79
|
getDatabase()
|
|
77
80
|
.prepare('INSERT OR REPLACE INTO thread_sessions (thread_id, session_id) VALUES (?, ?)')
|
|
78
81
|
.run(thread.id, sessionId)
|
package/src/commands/session.ts
CHANGED
|
@@ -75,6 +75,9 @@ export async function handleSessionCommand({ command, appId }: CommandContext):
|
|
|
75
75
|
reason: 'OpenCode session',
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
+
// Add user to thread so it appears in their sidebar
|
|
79
|
+
await thread.members.add(command.user.id)
|
|
80
|
+
|
|
78
81
|
await command.editReply(`Created new session in ${thread.toString()}`)
|
|
79
82
|
|
|
80
83
|
await handleOpencodeSession({
|
|
@@ -136,6 +136,9 @@ export const handleUserCommand: CommandHandler = async ({ command, appId }: Comm
|
|
|
136
136
|
reason: `OpenCode command: ${commandName}`,
|
|
137
137
|
})
|
|
138
138
|
|
|
139
|
+
// Add user to thread so it appears in their sidebar
|
|
140
|
+
await newThread.members.add(command.user.id)
|
|
141
|
+
|
|
139
142
|
await command.editReply(`Started /${commandName} in ${newThread.toString()}`)
|
|
140
143
|
|
|
141
144
|
await handleOpencodeSession({
|
package/src/commands/worktree.ts
CHANGED
|
@@ -228,6 +228,9 @@ export async function handleNewWorktreeCommand({
|
|
|
228
228
|
reason: 'Worktree session',
|
|
229
229
|
})
|
|
230
230
|
|
|
231
|
+
// Add user to thread so it appears in their sidebar
|
|
232
|
+
await thread.members.add(command.user.id)
|
|
233
|
+
|
|
231
234
|
return { thread, starterMessage }
|
|
232
235
|
},
|
|
233
236
|
catch: (e) => new WorktreeError('Failed to create thread', { cause: e }),
|
package/src/discord-bot.ts
CHANGED
|
@@ -425,6 +425,9 @@ export async function startDiscordBot({
|
|
|
425
425
|
reason: 'Start Claude session',
|
|
426
426
|
})
|
|
427
427
|
|
|
428
|
+
// Add user to thread so it appears in their sidebar
|
|
429
|
+
await thread.members.add(message.author.id)
|
|
430
|
+
|
|
428
431
|
discordLogger.log(`Created thread "${thread.name}" (${thread.id})`)
|
|
429
432
|
|
|
430
433
|
// Create worktree if worktrees are enabled (CLI flag OR channel setting)
|
package/src/session-handler.ts
CHANGED
|
@@ -86,6 +86,8 @@ export type QueuedMessage = {
|
|
|
86
86
|
// Key is threadId, value is array of queued messages
|
|
87
87
|
export const messageQueue = new Map<string, QueuedMessage[]>()
|
|
88
88
|
|
|
89
|
+
const activeEventHandlers = new Map<string, Promise<void>>()
|
|
90
|
+
|
|
89
91
|
export function addToQueue({
|
|
90
92
|
threadId,
|
|
91
93
|
message,
|
|
@@ -301,6 +303,15 @@ export async function handleOpencodeSession({
|
|
|
301
303
|
if (existingController) {
|
|
302
304
|
voiceLogger.log(`[ABORT] Cancelling existing request for session: ${session.id}`)
|
|
303
305
|
existingController.abort(new Error('New request started'))
|
|
306
|
+
const abortResult = await errore.tryAsync(() => {
|
|
307
|
+
return getClient().session.abort({
|
|
308
|
+
path: { id: session.id },
|
|
309
|
+
query: { directory: sdkDirectory },
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
if (abortResult instanceof Error) {
|
|
313
|
+
sessionLogger.log(`[ABORT] Server abort failed (may be already done):`, abortResult)
|
|
314
|
+
}
|
|
304
315
|
}
|
|
305
316
|
|
|
306
317
|
// Auto-reject ALL pending permissions for this thread
|
|
@@ -363,6 +374,17 @@ export async function handleOpencodeSession({
|
|
|
363
374
|
return
|
|
364
375
|
}
|
|
365
376
|
|
|
377
|
+
const previousHandler = activeEventHandlers.get(thread.id)
|
|
378
|
+
if (previousHandler) {
|
|
379
|
+
sessionLogger.log(`[EVENT] Waiting for previous handler to finish`)
|
|
380
|
+
await Promise.race([
|
|
381
|
+
previousHandler,
|
|
382
|
+
new Promise((resolve) => {
|
|
383
|
+
setTimeout(resolve, 1000)
|
|
384
|
+
}),
|
|
385
|
+
])
|
|
386
|
+
}
|
|
387
|
+
|
|
366
388
|
// Use v2 client for event subscription (has proper types for question.asked events)
|
|
367
389
|
const clientV2 = getOpencodeClientV2(directory)
|
|
368
390
|
if (!clientV2) {
|
|
@@ -398,6 +420,7 @@ export async function handleOpencodeSession({
|
|
|
398
420
|
let lastDisplayedContextPercentage = 0
|
|
399
421
|
let modelContextLimit: number | undefined
|
|
400
422
|
let assistantMessageId: string | undefined
|
|
423
|
+
let handlerPromise: Promise<void> | null = null
|
|
401
424
|
|
|
402
425
|
let typingInterval: NodeJS.Timeout | null = null
|
|
403
426
|
|
|
@@ -726,6 +749,10 @@ export async function handleOpencodeSession({
|
|
|
726
749
|
part: Part,
|
|
727
750
|
subtaskInfo: { label: string; assistantMessageId?: string },
|
|
728
751
|
) => {
|
|
752
|
+
// In text-only mode, skip all subtask output (they're tool-related)
|
|
753
|
+
if (verbosity === 'text-only') {
|
|
754
|
+
return
|
|
755
|
+
}
|
|
729
756
|
if (part.type === 'step-start' || part.type === 'step-finish') {
|
|
730
757
|
return
|
|
731
758
|
}
|
|
@@ -1151,7 +1178,13 @@ export async function handleOpencodeSession({
|
|
|
1151
1178
|
|
|
1152
1179
|
const promptResult: Error | { sessionID: string; result: any; port?: number } | undefined =
|
|
1153
1180
|
await errore.tryAsync(async () => {
|
|
1154
|
-
const
|
|
1181
|
+
const newHandlerPromise = eventHandler().finally(() => {
|
|
1182
|
+
if (activeEventHandlers.get(thread.id) === newHandlerPromise) {
|
|
1183
|
+
activeEventHandlers.delete(thread.id)
|
|
1184
|
+
}
|
|
1185
|
+
})
|
|
1186
|
+
activeEventHandlers.set(thread.id, newHandlerPromise)
|
|
1187
|
+
handlerPromise = newHandlerPromise
|
|
1155
1188
|
|
|
1156
1189
|
if (abortController.signal.aborted) {
|
|
1157
1190
|
sessionLogger.log(`[DEBOUNCE] Aborted before prompt, exiting`)
|
|
@@ -1273,6 +1306,15 @@ export async function handleOpencodeSession({
|
|
|
1273
1306
|
return { sessionID: session.id, result: response.data, port }
|
|
1274
1307
|
})
|
|
1275
1308
|
|
|
1309
|
+
if (handlerPromise) {
|
|
1310
|
+
await Promise.race([
|
|
1311
|
+
handlerPromise,
|
|
1312
|
+
new Promise((resolve) => {
|
|
1313
|
+
setTimeout(resolve, 1000)
|
|
1314
|
+
}),
|
|
1315
|
+
])
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1276
1318
|
if (!errore.isError(promptResult)) {
|
|
1277
1319
|
return promptResult
|
|
1278
1320
|
}
|