kimaki 1.5.0 → 1.6.0
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/worktree-commands.d.ts +2 -2
- package/dist/coordinator-agents.e2e.test.js +24 -2
- package/dist/coordinator-agents.e2e.test.js.map +1 -1
- package/dist/db.d.ts +3 -3
- package/dist/ingress.js +23 -3
- package/dist/ingress.js.map +1 -1
- package/dist/lock-routes.d.ts +33 -33
- package/dist/project.d.ts +1 -1
- package/dist/prompt.d.ts +1 -1
- package/dist/scheduler.d.ts +3 -3
- package/dist/schema.d.ts +6 -6
- package/dist/session-events.d.ts +1 -1
- package/dist/session-ops.e2e.test.js +26 -0
- package/dist/session-ops.e2e.test.js.map +1 -1
- package/dist/sessions.d.ts +9 -9
- package/dist/slash-commands.js +6 -2
- package/dist/slash-commands.js.map +1 -1
- package/dist/sleeps.d.ts +1 -1
- package/dist/voice.d.ts +1 -1
- package/dist/voice.js +10 -2
- package/dist/voice.js.map +1 -1
- package/package.json +1 -1
- package/src/coordinator-agents.e2e.test.ts +25 -2
- package/src/ingress.ts +19 -2
- package/src/session-ops.e2e.test.ts +27 -0
- package/src/slash-commands.ts +6 -2
- package/src/voice.ts +10 -2
- package/dist/mainmast-idle.e2e.test.d.ts +0 -1
- package/dist/mainmast-idle.e2e.test.js +0 -99
- package/dist/mainmast-idle.e2e.test.js.map +0 -1
- package/dist/mainmast-queue.e2e.test.d.ts +0 -1
- package/dist/mainmast-queue.e2e.test.js +0 -244
- package/dist/mainmast-queue.e2e.test.js.map +0 -1
- package/dist/mainmast-warmup.e2e.test.d.ts +0 -1
- package/dist/mainmast-warmup.e2e.test.js +0 -130
- package/dist/mainmast-warmup.e2e.test.js.map +0 -1
- package/dist/mainmast.d.ts +0 -6
- package/dist/mainmast.e2e.test.d.ts +0 -1
- package/dist/mainmast.e2e.test.js +0 -428
- package/dist/mainmast.e2e.test.js.map +0 -1
- package/dist/mainmast.js +0 -156
- package/dist/mainmast.js.map +0 -1
- package/dist/message-tool.e2e.test.d.ts +0 -1
- package/dist/message-tool.e2e.test.js +0 -39
- package/dist/message-tool.e2e.test.js.map +0 -1
- package/dist/plugin/mainmast.d.ts +0 -4
- package/dist/plugin/mainmast.js +0 -379
- package/dist/plugin/mainmast.js.map +0 -1
- package/dist/plugin/read-media.d.ts +0 -23
- package/dist/plugin/read-media.js +0 -72
- package/dist/plugin/read-media.js.map +0 -1
- package/dist/plugin/session-id/index.d.ts +0 -3
- package/dist/plugin/session-id/index.js +0 -18
- package/dist/plugin/session-id/index.js.map +0 -1
- package/dist/probe-live.tmp.d.ts +0 -1
- package/dist/probe-live.tmp.js +0 -82
- package/dist/probe-live.tmp.js.map +0 -1
- package/dist/tasks.e2e.test.d.ts +0 -1
- package/dist/tasks.e2e.test.js +0 -173
- package/dist/tasks.e2e.test.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Agent picks in a coordinator channel: `/<agent>-agent
|
|
2
|
-
// agent start a new thread with that agent, and `kimaki send --agent --model`
|
|
1
|
+
// Agent picks in a coordinator channel: `/<agent>-agent` (with or without a prompt)
|
|
2
|
+
// and a spoken agent start a new thread with that agent, and `kimaki send --agent --model`
|
|
3
3
|
// to the coordinator (workers pass --agent <current_agent>) keeps its agent and model.
|
|
4
4
|
|
|
5
5
|
import fs from 'node:fs'
|
|
@@ -36,6 +36,7 @@ const matchers: DeterministicMatcher[] = [
|
|
|
36
36
|
reply('hello-marker', 'hello'),
|
|
37
37
|
reply('slash-agent-marker', 'slash agent ok'),
|
|
38
38
|
reply('voice-agent-marker', 'voice agent ok'),
|
|
39
|
+
reply('empty-agent-marker', 'empty agent ok'),
|
|
39
40
|
reply('worker-note-marker', 'IDLE'),
|
|
40
41
|
]
|
|
41
42
|
|
|
@@ -107,6 +108,28 @@ test('/<agent>-agent with a prompt starts a thread with that agent; the coordina
|
|
|
107
108
|
expect(await coordinator()).toEqual(initial)
|
|
108
109
|
})
|
|
109
110
|
|
|
111
|
+
test('/<agent>-agent without a prompt starts an idle thread with that agent; the coordinator keeps its agent and model', async () => {
|
|
112
|
+
const { discord, channelId } = twin
|
|
113
|
+
const before = new Set((await discord.channel(channelId).getThreads()).map((thread) => thread.id))
|
|
114
|
+
await discord.channel(channelId).user(TEST_USER_ID).runSlashCommand({ name: 'thinker-agent' })
|
|
115
|
+
const thread = await discord.channel(channelId).waitForThread({ timeout: 8_000, predicate: (candidate) => !before.has(candidate.id) })
|
|
116
|
+
await waitForBotMessageContaining({ discord, threadId: thread.id, text: 'Send a message to start working.' })
|
|
117
|
+
expect(await coordinator()).toEqual(initial)
|
|
118
|
+
await discord.thread(thread.id).user(TEST_USER_ID).sendMessage({ content: 'Think empty-agent-marker' })
|
|
119
|
+
await waitForFooter({ discord, threadId: thread.id })
|
|
120
|
+
expect(await discord.thread(thread.id).text()).toMatchInlineSnapshot(`
|
|
121
|
+
"--- from: assistant (TestBot)
|
|
122
|
+
Send a message to start working.
|
|
123
|
+
--- from: user (tommy)
|
|
124
|
+
Think empty-agent-marker
|
|
125
|
+
--- from: assistant (TestBot)
|
|
126
|
+
-# *using deterministic-provider/deterministic-thinker ⋅ thinker*
|
|
127
|
+
empty agent ok
|
|
128
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-thinker ⋅ thinker*"
|
|
129
|
+
`)
|
|
130
|
+
expect(await coordinator()).toEqual(initial)
|
|
131
|
+
})
|
|
132
|
+
|
|
110
133
|
test('a spoken agent starts a thread with that agent; the coordinator keeps its agent and model', async () => {
|
|
111
134
|
const thread = await newThread(() =>
|
|
112
135
|
twin.discord.channel(twin.channelId).user(TEST_USER_ID).sendVoiceMessage({ url: voiceUrl({ transcription: 'Think voice-agent-marker', route: 'steer', agent: 'thinker' }) }),
|
package/src/ingress.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Discord ingress (spec 4): gates every messageCreate, parses it into a Route
|
|
2
2
|
// and calls actions. Gates, in order: webhooks and our own messages (except
|
|
3
|
-
// remote send envelopes),
|
|
3
|
+
// remote send envelopes), system messages (a thread rename only sets the
|
|
4
|
+
// session title), channel ownership (only channels mapped in this
|
|
4
5
|
// machine's SQLite), permission (other bots need the Kimaki role).
|
|
5
6
|
// Messages of one channel are handled in arrival order, after OpenCode is ready.
|
|
6
7
|
//
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
Events,
|
|
22
23
|
GuildMember,
|
|
23
24
|
MessageFlags,
|
|
25
|
+
MessageType,
|
|
24
26
|
PermissionFlagsBits,
|
|
25
27
|
type Attachment,
|
|
26
28
|
type Guild,
|
|
@@ -28,7 +30,7 @@ import {
|
|
|
28
30
|
} from 'discord.js'
|
|
29
31
|
import * as errore from 'errore'
|
|
30
32
|
|
|
31
|
-
import { isCoordinatorChannel, projectOf, sessionDirectory, type Bot, type PromptFile } from './bot.ts'
|
|
33
|
+
import { isCoordinatorChannel, oc, projectOf, sessionDirectory, type Bot, type PromptFile } from './bot.ts'
|
|
32
34
|
import { formatError, NOTIFY_MESSAGE_FLAGS, SILENT_MESSAGE_FLAGS } from './format-parts.ts'
|
|
33
35
|
import { createLogger } from './logger.ts'
|
|
34
36
|
import { parseInput, sendInput } from './lock-routes.ts'
|
|
@@ -320,6 +322,18 @@ async function handleMessage(bot: Bot, message: Message) {
|
|
|
320
322
|
await message.reply({ content: note, allowedMentions: { repliedUser: false }, flags: SILENT_MESSAGE_FLAGS }).catch(() => undefined)
|
|
321
323
|
}
|
|
322
324
|
|
|
325
|
+
// A rename in the Discord UI posts a CHANNEL_NAME_CHANGE message with the new
|
|
326
|
+
// name as content (issue #258): the session title follows it, nothing is prompted.
|
|
327
|
+
async function handleThreadRename(bot: Bot, message: Message) {
|
|
328
|
+
if (!message.inGuild() || !message.channel.isThread()) return
|
|
329
|
+
const sessionId = bot.store.getState().roots[message.channel.id]
|
|
330
|
+
if (!sessionId || !(await authorCanUseKimaki(bot, message))) return
|
|
331
|
+
const title = message.content
|
|
332
|
+
const renamed = await oc(bot, 'session.update', (client) => client.session.update({ sessionID: sessionId, title }))
|
|
333
|
+
if (renamed instanceof Error) return logger.warn(`title of ${sessionId} from thread rename`, renamed)
|
|
334
|
+
logger.log(`session ${sessionId} renamed to "${title}" by ${message.author.username}`)
|
|
335
|
+
}
|
|
336
|
+
|
|
323
337
|
// Registers the message listeners. Messages of one channel are handled in arrival order:
|
|
324
338
|
// a text sent while a voice message is transcribed waits for it (voice-attachments.e2e.test.ts).
|
|
325
339
|
export function registerIngress(bot: Bot) {
|
|
@@ -341,6 +355,9 @@ export function registerIngress(bot: Bot) {
|
|
|
341
355
|
// Webhooks have no member to check. Own messages only matter as remote send envelopes.
|
|
342
356
|
if (message.webhookId) return
|
|
343
357
|
if (message.author.id === bot.discord.user?.id && !message.embeds[0]?.footer?.text.startsWith(REMOTE_SEND_PREFIX)) return
|
|
358
|
+
if (message.type === MessageType.ChannelNameChange) return serialize(message.channelId, () => handleThreadRename(bot, message))
|
|
359
|
+
// Pins, joins, boosts and other system messages are never prompts.
|
|
360
|
+
if (message.type !== MessageType.Default && message.type !== MessageType.Reply) return
|
|
344
361
|
serialize(message.channelId, () => handleMessage(bot, message))
|
|
345
362
|
})
|
|
346
363
|
// Deleting a queued Discord message removes it from the queue.
|
|
@@ -164,6 +164,33 @@ test('/compact summarizes the history without a footer', async () => {
|
|
|
164
164
|
`)
|
|
165
165
|
})
|
|
166
166
|
|
|
167
|
+
// Issue #258: the rename's CHANNEL_NAME_CHANGE message holds only the new name.
|
|
168
|
+
test('renaming the thread in Discord sets the session title without a prompt', async () => {
|
|
169
|
+
const thread = await newThread('Hello rename-marker')
|
|
170
|
+
const sessionId = bot.store.getState().roots[thread.id]!
|
|
171
|
+
const client = await server.client()
|
|
172
|
+
const requests = server.model.requests.length
|
|
173
|
+
await twin.discord.thread(thread.id).user(TEST_USER_ID).renameThread({ name: 'Renamed by hand' })
|
|
174
|
+
await waitFor({
|
|
175
|
+
label: 'session title from the thread name',
|
|
176
|
+
check: async () => (await client.session.get({ sessionID: sessionId })).title === 'Renamed by hand' || null,
|
|
177
|
+
})
|
|
178
|
+
for (let i = 0; i < 10; i++) {
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, 20))
|
|
180
|
+
expect(server.model.requests.length).toBe(requests)
|
|
181
|
+
}
|
|
182
|
+
expect(await twin.discord.thread(thread.id).text()).toMatchInlineSnapshot(`
|
|
183
|
+
"--- from: user (tommy)
|
|
184
|
+
Hello rename-marker
|
|
185
|
+
--- from: assistant (TestBot)
|
|
186
|
+
-# *using deterministic-provider/deterministic-v2 ⋅ build*
|
|
187
|
+
ok
|
|
188
|
+
-# *main ⋅ Ns ⋅ deterministic-v2*
|
|
189
|
+
--- from: user (tommy)
|
|
190
|
+
[renamed thread to: Renamed by hand]"
|
|
191
|
+
`)
|
|
192
|
+
})
|
|
193
|
+
|
|
167
194
|
test('/undo hides the last turn but keeps its files, /redo restores the turn', async () => {
|
|
168
195
|
const file = path.join(server.projectDirectory, 'undo.txt')
|
|
169
196
|
const thread = await newThread('Create a file write-marker')
|
package/src/slash-commands.ts
CHANGED
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
} from 'discord.js'
|
|
50
50
|
|
|
51
51
|
import { agentUiRoutes } from './agent-ui.ts'
|
|
52
|
-
import { oc, projectOf, type Bot, type ModelChoice } from './bot.ts'
|
|
52
|
+
import { isCoordinatorChannel, oc, projectOf, routeText, type Bot, type ModelChoice } from './bot.ts'
|
|
53
53
|
import { createLoginRoutes } from './commands/login-commands.ts'
|
|
54
54
|
import { applyAgent, createPreferenceRoutes, switchModel, variantChoices, variantModel } from './commands/preference-commands.ts'
|
|
55
55
|
import { sessionRoutes } from './commands/session-commands.ts'
|
|
@@ -348,6 +348,8 @@ async function sendInput(
|
|
|
348
348
|
messageId: interaction.id,
|
|
349
349
|
startMessageId: null,
|
|
350
350
|
...(model && { model }),
|
|
351
|
+
// An agent pick without a prompt: the session waits in the thread for its first message.
|
|
352
|
+
run: Boolean(routeText(route)),
|
|
351
353
|
})
|
|
352
354
|
if (started instanceof Error) return replyError(interaction, started)
|
|
353
355
|
await interaction.editReply({ content: `Started a new session in <#${started.threadId}>` })
|
|
@@ -361,7 +363,9 @@ async function handleDynamic(bot: Bot, { interaction, target }: { interaction: C
|
|
|
361
363
|
if (where instanceof Error) return replyError(interaction, where)
|
|
362
364
|
const model = variant ? await variantModel(bot, { target: where, variant, agent: target.name }) : null
|
|
363
365
|
if (model instanceof Error) return replyError(interaction, model)
|
|
364
|
-
|
|
366
|
+
// In a coordinator channel the pick starts a new thread, with or without a prompt (sendInput).
|
|
367
|
+
const coordinatorChannel = !where.conversation?.isThread() && isCoordinatorChannel(bot, where.channelId)
|
|
368
|
+
if (!text && !coordinatorChannel) return applyAgent(bot, { interaction, target: where, agent: target.name, model })
|
|
365
369
|
const label = model ? `${target.name}, ${model.variant}` : target.name
|
|
366
370
|
return sendInput(bot, { interaction, route: { kind: 'steer', text, agent: target.name }, echo: `(${label}) ${text}`, model })
|
|
367
371
|
}
|
package/src/voice.ts
CHANGED
|
@@ -51,6 +51,13 @@ const RETRY_DELAY_MS = 500
|
|
|
51
51
|
// Later messages of the conversation wait for the transcription (ingress.ts): cap all providers and retries.
|
|
52
52
|
const TRANSCRIPTION_TIMEOUT_MS = 2 * 60_000
|
|
53
53
|
|
|
54
|
+
// KIMAKI_WHISPER_TIMEOUT (seconds) raises the cap for a slow local Whisper server (CPU only).
|
|
55
|
+
function transcriptionTimeoutMs(entries: Transcriber[]) {
|
|
56
|
+
const seconds = Number(process.env['KIMAKI_WHISPER_TIMEOUT'])
|
|
57
|
+
if (!entries.some((entry) => entry.kind === 'local-whisper') || !(seconds > 0)) return TRANSCRIPTION_TIMEOUT_MS
|
|
58
|
+
return seconds * 1000
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
export class TranscriptionError extends errore.createTaggedError({
|
|
55
62
|
name: 'TranscriptionError',
|
|
56
63
|
message: 'Transcription failed: $reason',
|
|
@@ -699,7 +706,8 @@ export async function transcribe({
|
|
|
699
706
|
const prompt = list.some((entry) => entry.kind === 'openai' || entry.kind === 'gemini')
|
|
700
707
|
? transcriptionPrompt({ fileTree: await projectFileTree(directory), agents })
|
|
701
708
|
: ''
|
|
702
|
-
const
|
|
709
|
+
const timeoutMs = transcriptionTimeoutMs(list)
|
|
710
|
+
const signal = AbortSignal.timeout(timeoutMs)
|
|
703
711
|
const run = (entry: Transcriber) => {
|
|
704
712
|
if (entry.kind === 'gateway') return transcribeViaGateway({ audio, mediaType, ...entry, signal })
|
|
705
713
|
if (entry.kind === 'local-whisper') return withRetries({ signal, attempt: () => transcribeViaLocalWhisper({ whisper: entry.whisper, audio, mediaType, signal }) })
|
|
@@ -712,7 +720,7 @@ export async function transcribe({
|
|
|
712
720
|
}
|
|
713
721
|
let last: TranscriptionFailure | NoTranscriptionKeyError = new NoTranscriptionKeyError()
|
|
714
722
|
for (const entry of list) {
|
|
715
|
-
if (signal.aborted) return new TranscriptionError({ reason: `no answer within ${
|
|
723
|
+
if (signal.aborted) return new TranscriptionError({ reason: `no answer within ${timeoutMs / 60_000} minutes` })
|
|
716
724
|
const result = await run(entry)
|
|
717
725
|
if (!(result instanceof Error) || result instanceof EmptyTranscriptionError) return result
|
|
718
726
|
logger.warn(`${entry.kind} transcription failed${entry === list.at(-1) ? '' : ', trying the next provider'}`, result)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
// The mainmast plugin warmup (plugin/mainmast.ts) with a short window: an idle
|
|
2
|
-
// mainmast with no running threads ends its warmup at the first check and is compacted.
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { afterAll, beforeAll, expect, test } from 'vitest';
|
|
6
|
-
import { TEST_MODEL, TEST_USER_ID, seedProjectChannel, startOpencodeTestServer, startTestBot, startTwin, tempDataDir, textParts, waitFor, waitForFooter, warmUp, } from "./test/harness.js";
|
|
7
|
-
let server;
|
|
8
|
-
let twin;
|
|
9
|
-
let bot;
|
|
10
|
-
const dataDir = tempDataDir();
|
|
11
|
-
// A large context, so the compaction is worth it (plugin/mainmast.ts MIN_COMPACT_TOKENS),
|
|
12
|
-
// in a 1M window like Claude Opus: only 12% of it, far below automatic compaction.
|
|
13
|
-
const bigUsage = { inputTokens: 120_000, outputTokens: 1, totalTokens: 120_001 };
|
|
14
|
-
beforeAll(async () => {
|
|
15
|
-
;
|
|
16
|
-
[server, twin] = await Promise.all([
|
|
17
|
-
startOpencodeTestServer({
|
|
18
|
-
models: { [TEST_MODEL]: { name: TEST_MODEL, limit: { context: 1_000_000, output: 32_000 } } },
|
|
19
|
-
matchers: [
|
|
20
|
-
// OpenCode rejects a summary without the template headings.
|
|
21
|
-
{ id: 'compaction-summary', priority: 900, when: { latestUserTextIncludes: 'You MUST use this format for your response' }, then: { parts: textParts('## Objective\n- Keep coordinating') } },
|
|
22
|
-
{
|
|
23
|
-
id: 'hello',
|
|
24
|
-
priority: 100,
|
|
25
|
-
when: { latestUserTextIncludes: 'idle-marker' },
|
|
26
|
-
then: { parts: [{ type: 'text-start', id: 't' }, { type: 'text-delta', id: 't', delta: 'Hi there.' }, { type: 'text-end', id: 't' }, { type: 'finish', finishReason: 'stop', usage: bigUsage }] },
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
}),
|
|
30
|
-
startTwin(),
|
|
31
|
-
]);
|
|
32
|
-
await seedProjectChannel({ dataDir, channelId: twin.channelId, guildId: twin.discord.guildId, directory: server.projectDirectory });
|
|
33
|
-
await warmUp({ server });
|
|
34
|
-
bot = await startTestBot({ dataDir, twin, server, mainmast: true, mainmastTimers: { warmIntervalMs: 1_200, warmMaxMs: 60_000 } });
|
|
35
|
-
}, 60_000);
|
|
36
|
-
afterAll(async () => {
|
|
37
|
-
await bot?.stop();
|
|
38
|
-
await twin?.stop();
|
|
39
|
-
await server?.stop();
|
|
40
|
-
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
41
|
-
});
|
|
42
|
-
test('an idle mainmast gets compacted when its warmup ends', async () => {
|
|
43
|
-
const { discord, channelId } = twin;
|
|
44
|
-
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Hello idle-marker' });
|
|
45
|
-
await waitForFooter({ discord, threadId: channelId });
|
|
46
|
-
const client = await server.client();
|
|
47
|
-
const root = bot.store.getState().roots[channelId];
|
|
48
|
-
await waitFor({
|
|
49
|
-
label: 'compaction of the idle mainmast',
|
|
50
|
-
check: async () => (await client.message.list({ sessionID: root, order: 'desc', limit: 10 })).data.some((message) => message.type === 'compaction') || null,
|
|
51
|
-
});
|
|
52
|
-
expect(await discord.channel(channelId).text()).toMatchInlineSnapshot(`
|
|
53
|
-
"--- from: user (tommy)
|
|
54
|
-
Hello idle-marker
|
|
55
|
-
--- from: assistant (TestBot)
|
|
56
|
-
-# *using deterministic-provider/deterministic-v2 ⋅ build*
|
|
57
|
-
Hi there.
|
|
58
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
59
|
-
`);
|
|
60
|
-
const messages = await client.message.list({ sessionID: root, order: 'asc', limit: 20 });
|
|
61
|
-
expect(messages.data.map((message) => message.type)).toMatchInlineSnapshot(`
|
|
62
|
-
[
|
|
63
|
-
"synthetic",
|
|
64
|
-
"synthetic",
|
|
65
|
-
"user",
|
|
66
|
-
"assistant",
|
|
67
|
-
"idle",
|
|
68
|
-
"compaction",
|
|
69
|
-
"idle",
|
|
70
|
-
]
|
|
71
|
-
`);
|
|
72
|
-
});
|
|
73
|
-
// OpenCode reloads the plugin when its files change (every Kimaki build or upgrade).
|
|
74
|
-
test('a plugin reload keeps the pending compaction', async () => {
|
|
75
|
-
const { discord, channelId } = twin;
|
|
76
|
-
const client = await server.client();
|
|
77
|
-
const root = bot.store.getState().roots[channelId];
|
|
78
|
-
const compactions = async () => (await client.message.list({ sessionID: root, order: 'desc', limit: 20 })).data.filter((message) => message.type === 'compaction').length;
|
|
79
|
-
expect(await compactions()).toBe(1);
|
|
80
|
-
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Again idle-marker' });
|
|
81
|
-
await waitForFooter({ discord, threadId: channelId, count: 2 });
|
|
82
|
-
fs.appendFileSync(path.join(server.configDir, 'plugins', 'kimaki', 'index.js'), '// reload\n');
|
|
83
|
-
await waitFor({ label: 'compaction after the plugin reload', check: async () => (await compactions()) === 2 || null });
|
|
84
|
-
const messages = await client.message.list({ sessionID: root, order: 'asc', limit: 30 });
|
|
85
|
-
const firstCompaction = messages.data.findIndex((message) => message.type === 'compaction');
|
|
86
|
-
expect(messages.data.slice(firstCompaction + 1).map((message) => message.type)).toMatchInlineSnapshot(`
|
|
87
|
-
[
|
|
88
|
-
"idle",
|
|
89
|
-
"synthetic",
|
|
90
|
-
"synthetic",
|
|
91
|
-
"user",
|
|
92
|
-
"assistant",
|
|
93
|
-
"idle",
|
|
94
|
-
"compaction",
|
|
95
|
-
"idle",
|
|
96
|
-
]
|
|
97
|
-
`);
|
|
98
|
-
});
|
|
99
|
-
//# sourceMappingURL=mainmast-idle.e2e.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mainmast-idle.e2e.test.js","sourceRoot":"","sources":["../src/mainmast-idle.e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
// The queue in a mainmast channel: the channel itself is the conversation, so
|
|
2
|
-
// acks and "Queued message sent" replies land in the channel. Every queued
|
|
3
|
-
// prompt that got an ack is marked when it starts, also when the coordinator
|
|
4
|
-
// answers IDLE; its answer is then not a second reply to the same message.
|
|
5
|
-
import { execFile } from 'node:child_process';
|
|
6
|
-
import fs from 'node:fs';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import { promisify } from 'node:util';
|
|
9
|
-
import { afterAll, beforeAll, expect, test } from 'vitest';
|
|
10
|
-
import { TEST_USER_ID, seedProjectChannel, slowTextMatcher, startOpencodeTestServer, startTestBot, startTwin, tempDataDir, textParts, waitFor, waitForBotMessageContaining, waitForFooter, warmUp, } from "./test/harness.js";
|
|
11
|
-
const exec = promisify(execFile);
|
|
12
|
-
function reply(marker, text) {
|
|
13
|
-
return { id: marker, priority: 50, when: { latestUserTextIncludes: marker }, then: { parts: textParts(text) } };
|
|
14
|
-
}
|
|
15
|
-
const matchers = [
|
|
16
|
-
slowTextMatcher({ marker: 'slow-marker', text: 'slow-done', delayMs: 1_200 }),
|
|
17
|
-
reply('queued-idle', 'IDLE'),
|
|
18
|
-
reply('queued-one', 'queued one ok'),
|
|
19
|
-
reply('queued-two', 'queued two ok'),
|
|
20
|
-
reply('steer-marker', 'steer ok'),
|
|
21
|
-
reply('idle-marker', 'idle ok'),
|
|
22
|
-
reply('hello-marker', 'hello'),
|
|
23
|
-
];
|
|
24
|
-
let server;
|
|
25
|
-
let twin;
|
|
26
|
-
let bot;
|
|
27
|
-
const dataDir = tempDataDir();
|
|
28
|
-
beforeAll(async () => {
|
|
29
|
-
;
|
|
30
|
-
[server, twin] = await Promise.all([startOpencodeTestServer({ matchers }), startTwin()]);
|
|
31
|
-
await seedProjectChannel({ dataDir, channelId: twin.channelId, guildId: twin.discord.guildId, directory: server.projectDirectory });
|
|
32
|
-
await warmUp({ server });
|
|
33
|
-
bot = await startTestBot({ dataDir, twin, server, mainmast: true });
|
|
34
|
-
// The mainmast session exists before any test: its banner is not part of one test's snapshot.
|
|
35
|
-
await twin.discord.channel(twin.channelId).user(TEST_USER_ID).sendMessage({ content: 'Start hello-marker' });
|
|
36
|
-
await waitForFooter({ discord: twin.discord, threadId: twin.channelId });
|
|
37
|
-
}, 60_000);
|
|
38
|
-
afterAll(async () => {
|
|
39
|
-
await bot?.stop();
|
|
40
|
-
await twin?.stop();
|
|
41
|
-
await server?.stop();
|
|
42
|
-
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
43
|
-
});
|
|
44
|
-
// The channel holds every test's turns: the text from the user message that starts with `content`.
|
|
45
|
-
async function channelTextFrom(content) {
|
|
46
|
-
const text = await twin.discord.channel(twin.channelId).text({ showReplies: true });
|
|
47
|
-
return text.slice(text.lastIndexOf('--- from:', text.lastIndexOf(`\n${content}`)));
|
|
48
|
-
}
|
|
49
|
-
function footers() {
|
|
50
|
-
return twin.discord.channel(twin.channelId).getMessages().then((messages) => messages.filter((message) => message.content.startsWith('-# *project')).length);
|
|
51
|
-
}
|
|
52
|
-
// Sends a slow prompt and waits until the coordinator runs it.
|
|
53
|
-
async function startSlowTurn(content) {
|
|
54
|
-
const channel = twin.discord.channel(twin.channelId);
|
|
55
|
-
channel.clearTypingEvents();
|
|
56
|
-
await channel.user(TEST_USER_ID).sendMessage({ content: `${content} slow-marker` });
|
|
57
|
-
await channel.waitForTypingEvent();
|
|
58
|
-
}
|
|
59
|
-
async function waitForIdle() {
|
|
60
|
-
await waitFor({ label: 'mainmast idle', check: async () => !bot.store.getState().threads[twin.channelId]?.turn || null });
|
|
61
|
-
}
|
|
62
|
-
function rootSession() {
|
|
63
|
-
const sessionID = bot.store.getState().roots[twin.channelId];
|
|
64
|
-
if (!sessionID)
|
|
65
|
-
throw new Error('no mainmast session');
|
|
66
|
-
return sessionID;
|
|
67
|
-
}
|
|
68
|
-
test('. queue in a busy mainmast: each queued message gets a reply when it starts, also when it answers IDLE', async () => {
|
|
69
|
-
const before = await footers();
|
|
70
|
-
await startSlowTurn('First a long one');
|
|
71
|
-
const user = twin.discord.channel(twin.channelId).user(TEST_USER_ID);
|
|
72
|
-
const silent = await user.sendMessage({ content: 'Start a worker queued-idle. queue' });
|
|
73
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'position 1' });
|
|
74
|
-
const answered = await user.sendMessage({ content: 'Then answer queued-two. queue' });
|
|
75
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'queued two ok' });
|
|
76
|
-
// The IDLE turn posts no footer: slow-done and queued two get one each.
|
|
77
|
-
await waitForFooter({ discord: twin.discord, threadId: twin.channelId, count: before + 2 });
|
|
78
|
-
expect(await channelTextFrom('First a long one')).toMatchInlineSnapshot(`
|
|
79
|
-
"--- from: user (tommy)
|
|
80
|
-
First a long one slow-marker
|
|
81
|
-
Start a worker queued-idle. queue
|
|
82
|
-
--- from: assistant (TestBot)
|
|
83
|
-
[reply to tommy: Start a worker queued-idle. queue]
|
|
84
|
-
-# Queued
|
|
85
|
-
--- from: user (tommy)
|
|
86
|
-
Then answer queued-two. queue
|
|
87
|
-
--- from: assistant (TestBot)
|
|
88
|
-
[reply to tommy: Then answer queued-two. queue]
|
|
89
|
-
-# Queued
|
|
90
|
-
[reply to tommy: First a long one slow-marker]
|
|
91
|
-
slow-done
|
|
92
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
93
|
-
[reply to tommy: Start a worker queued-idle. queue]
|
|
94
|
-
-# Queued message sent
|
|
95
|
-
[reply to tommy: Then answer queued-two. queue]
|
|
96
|
-
-# Queued message sent
|
|
97
|
-
queued two ok
|
|
98
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
99
|
-
`);
|
|
100
|
-
const messages = await twin.discord.channel(twin.channelId).getMessages();
|
|
101
|
-
const sent = messages.filter((message) => message.content === '-# Queued message sent');
|
|
102
|
-
expect(sent.map((message) => message.message_reference?.message_id)).toEqual([silent.id, answered.id]);
|
|
103
|
-
});
|
|
104
|
-
test('/queue and /clear-queue in a mainmast', async () => {
|
|
105
|
-
const before = await footers();
|
|
106
|
-
await startSlowTurn('Slash queue here');
|
|
107
|
-
const user = twin.discord.channel(twin.channelId).user(TEST_USER_ID);
|
|
108
|
-
await user.runSlashCommand({ name: 'queue', options: [{ name: 'message', type: 3, value: 'Slash queued-one' }] });
|
|
109
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'position 1' });
|
|
110
|
-
await user.runSlashCommand({ name: 'queue', options: [{ name: 'message', type: 3, value: 'Slash queued-two' }] });
|
|
111
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'position 2' });
|
|
112
|
-
await user.runSlashCommand({ name: 'clear-queue', options: [{ name: 'position', type: 4, value: 1 }] });
|
|
113
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'queued two ok' });
|
|
114
|
-
await waitForFooter({ discord: twin.discord, threadId: twin.channelId, count: before + 2 });
|
|
115
|
-
expect(await channelTextFrom('Slash queue here')).toMatchInlineSnapshot(`
|
|
116
|
-
"--- from: user (tommy)
|
|
117
|
-
Slash queue here slow-marker
|
|
118
|
-
--- from: assistant (TestBot)
|
|
119
|
-
-# » **tommy:** Slash queued-one
|
|
120
|
-
[reply to TestBot: -# » **tommy:** Slash queued-one]
|
|
121
|
-
-# Removed from queue
|
|
122
|
-
-# » **tommy:** Slash queued-two
|
|
123
|
-
[reply to TestBot: -# » **tommy:** Slash queued-two]
|
|
124
|
-
-# Queued
|
|
125
|
-
-# Cleared 1 queued message
|
|
126
|
-
[reply to tommy: Slash queue here slow-marker]
|
|
127
|
-
slow-done
|
|
128
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
129
|
-
[reply to TestBot: -# » **tommy:** Slash queued-two]
|
|
130
|
-
-# Queued message sent
|
|
131
|
-
queued two ok
|
|
132
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
133
|
-
`);
|
|
134
|
-
});
|
|
135
|
-
test('deleting a queued mainmast message removes it from the queue', async () => {
|
|
136
|
-
const before = await footers();
|
|
137
|
-
await startSlowTurn('Delete in mainmast');
|
|
138
|
-
const user = twin.discord.channel(twin.channelId).user(TEST_USER_ID);
|
|
139
|
-
const queued = await user.sendMessage({ content: 'Never mind queued-one. queue' });
|
|
140
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'position 1' });
|
|
141
|
-
await user.deleteMessage({ messageId: queued.id });
|
|
142
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'Removed from queue' });
|
|
143
|
-
await waitForFooter({ discord: twin.discord, threadId: twin.channelId, count: before + 1 });
|
|
144
|
-
await waitForIdle();
|
|
145
|
-
expect(await channelTextFrom('Delete in mainmast')).toMatchInlineSnapshot(`
|
|
146
|
-
"--- from: user (tommy)
|
|
147
|
-
Delete in mainmast slow-marker
|
|
148
|
-
--- from: assistant (TestBot)
|
|
149
|
-
[reply to unknown message]
|
|
150
|
-
-# Removed from queue
|
|
151
|
-
[reply to tommy: Delete in mainmast slow-marker]
|
|
152
|
-
slow-done
|
|
153
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
154
|
-
`);
|
|
155
|
-
});
|
|
156
|
-
test('kimaki send . queue and a TUI prompt queued on the mainmast show their text, then a reply when they run', async () => {
|
|
157
|
-
const before = await footers();
|
|
158
|
-
await startSlowTurn('Other sources');
|
|
159
|
-
await exec(process.execPath, ['--import', 'tsx', path.resolve('src/cli.ts'), 'send', '--session', rootSession(), '--prompt', 'From the CLI queued-one. queue', '--data-dir', dataDir], {
|
|
160
|
-
env: { ...process.env, KIMAKI_LOCK_PORT: String(bot.lock.port) },
|
|
161
|
-
});
|
|
162
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'position 1' });
|
|
163
|
-
await (await server.client()).session.prompt({ sessionID: rootSession(), text: 'From the TUI queued-two', delivery: 'queue' });
|
|
164
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'queued two ok' });
|
|
165
|
-
await waitForFooter({ discord: twin.discord, threadId: twin.channelId, count: before + 3 });
|
|
166
|
-
expect(await channelTextFrom('Other sources')).toMatchInlineSnapshot(`
|
|
167
|
-
"--- from: user (tommy)
|
|
168
|
-
Other sources slow-marker
|
|
169
|
-
--- from: assistant (TestBot)
|
|
170
|
-
-# » **CLI:** From the CLI queued-one
|
|
171
|
-
-# » **user:** From the TUI queued-two
|
|
172
|
-
[reply to tommy: Other sources slow-marker]
|
|
173
|
-
slow-done
|
|
174
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
175
|
-
[reply to TestBot: -# » **CLI:** From the CLI queued-one]
|
|
176
|
-
-# Queued message sent
|
|
177
|
-
queued one ok
|
|
178
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
179
|
-
[reply to TestBot: -# » **user:** From the TUI queued-two]
|
|
180
|
-
-# Queued message sent
|
|
181
|
-
queued two ok
|
|
182
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
183
|
-
`);
|
|
184
|
-
});
|
|
185
|
-
test('a plain message in a busy mainmast runs first, then the queued one gets its reply', async () => {
|
|
186
|
-
const before = await footers();
|
|
187
|
-
await startSlowTurn('Steer the mainmast');
|
|
188
|
-
const user = twin.discord.channel(twin.channelId).user(TEST_USER_ID);
|
|
189
|
-
await user.sendMessage({ content: 'Afterwards queued-one. queue' });
|
|
190
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'position 1' });
|
|
191
|
-
await user.sendMessage({ content: 'Right now steer-marker' });
|
|
192
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'queued one ok' });
|
|
193
|
-
await waitForFooter({ discord: twin.discord, threadId: twin.channelId, count: before + 2 });
|
|
194
|
-
expect(await channelTextFrom('Steer the mainmast')).toMatchInlineSnapshot(`
|
|
195
|
-
"--- from: user (tommy)
|
|
196
|
-
Steer the mainmast slow-marker
|
|
197
|
-
Afterwards queued-one. queue
|
|
198
|
-
--- from: assistant (TestBot)
|
|
199
|
-
[reply to tommy: Afterwards queued-one. queue]
|
|
200
|
-
-# Queued
|
|
201
|
-
--- from: user (tommy)
|
|
202
|
-
Right now steer-marker
|
|
203
|
-
--- from: assistant (TestBot)
|
|
204
|
-
[reply to tommy: Right now steer-marker]
|
|
205
|
-
steer ok
|
|
206
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
207
|
-
[reply to tommy: Afterwards queued-one. queue]
|
|
208
|
-
-# Queued message sent
|
|
209
|
-
queued one ok
|
|
210
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
211
|
-
`);
|
|
212
|
-
});
|
|
213
|
-
test('/abort in a mainmast stops the run and clears the queue', async () => {
|
|
214
|
-
await startSlowTurn('Abort the mainmast');
|
|
215
|
-
const user = twin.discord.channel(twin.channelId).user(TEST_USER_ID);
|
|
216
|
-
await user.sendMessage({ content: 'Never runs queued-one. queue' });
|
|
217
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'position 1' });
|
|
218
|
-
await user.runSlashCommand({ name: 'abort' });
|
|
219
|
-
await waitForBotMessageContaining({ discord: twin.discord, threadId: twin.channelId, text: 'Removed from queue' });
|
|
220
|
-
await waitForIdle();
|
|
221
|
-
expect(await channelTextFrom('Abort the mainmast')).toMatchInlineSnapshot(`
|
|
222
|
-
"--- from: user (tommy)
|
|
223
|
-
Abort the mainmast slow-marker
|
|
224
|
-
Never runs queued-one. queue
|
|
225
|
-
--- from: assistant (TestBot)
|
|
226
|
-
[reply to tommy: Never runs queued-one. queue]
|
|
227
|
-
-# Removed from queue
|
|
228
|
-
Request **aborted**, cleared 1 queued message"
|
|
229
|
-
`);
|
|
230
|
-
});
|
|
231
|
-
test('. queue on an idle mainmast runs at once: the answer replies to the message', async () => {
|
|
232
|
-
const before = await footers();
|
|
233
|
-
await twin.discord.channel(twin.channelId).user(TEST_USER_ID).sendMessage({ content: 'Nothing waits idle-marker. queue' });
|
|
234
|
-
await waitForFooter({ discord: twin.discord, threadId: twin.channelId, count: before + 1 });
|
|
235
|
-
expect(await channelTextFrom('Nothing waits idle-marker')).toMatchInlineSnapshot(`
|
|
236
|
-
"--- from: user (tommy)
|
|
237
|
-
Nothing waits idle-marker. queue
|
|
238
|
-
--- from: assistant (TestBot)
|
|
239
|
-
[reply to tommy: Nothing waits idle-marker. queue]
|
|
240
|
-
idle ok
|
|
241
|
-
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
242
|
-
`);
|
|
243
|
-
});
|
|
244
|
-
//# sourceMappingURL=mainmast-queue.e2e.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mainmast-queue.e2e.test.js","sourceRoot":"","sources":["../src/mainmast-queue.e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|