kimaki 1.3.0 → 1.5.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/assets/fonts/Geist-Bold.ttf +0 -0
- package/assets/fonts/Geist-Regular.ttf +0 -0
- package/assets/fonts/GeistMono-Regular.ttf +0 -0
- package/assets/fonts/OFL.txt +92 -0
- package/dist/agent-ui.e2e.test.js +27 -13
- package/dist/agent-ui.e2e.test.js.map +1 -1
- package/dist/agent-ui.js +2 -1
- package/dist/agent-ui.js.map +1 -1
- package/dist/analytics.e2e.test.js +1 -1
- package/dist/analytics.e2e.test.js.map +1 -1
- package/dist/autostart.d.ts +4 -0
- package/dist/autostart.js +55 -10
- package/dist/autostart.js.map +1 -1
- package/dist/bot.d.ts +13 -6
- package/dist/bot.js +17 -15
- package/dist/bot.js.map +1 -1
- package/dist/btw.e2e.test.js +3 -3
- package/dist/builtin-skills.d.ts +3 -0
- package/dist/builtin-skills.js +29 -0
- package/dist/builtin-skills.js.map +1 -1
- package/dist/builtin-skills.test.js +72 -1
- package/dist/builtin-skills.test.js.map +1 -1
- package/dist/catalog-refresh.e2e.test.js +2 -2
- package/dist/cli/bot.d.ts +1 -1
- package/dist/cli/bot.js +33 -18
- package/dist/cli/bot.js.map +1 -1
- package/dist/cli/discord.js +32 -6
- package/dist/cli/discord.js.map +1 -1
- package/dist/cli/schedule.js +23 -9
- package/dist/cli/schedule.js.map +1 -1
- package/dist/cli/session.js +37 -12
- package/dist/cli/session.js.map +1 -1
- package/dist/cli/shared.d.ts +5 -3
- package/dist/cli/shared.js +45 -10
- package/dist/cli/shared.js.map +1 -1
- package/dist/cli-discord.e2e.test.js +13 -8
- package/dist/cli-discord.e2e.test.js.map +1 -1
- package/dist/cli-lock.e2e.test.js +87 -20
- package/dist/cli-lock.e2e.test.js.map +1 -1
- package/dist/cli-read.e2e.test.js +18 -9
- package/dist/cli-read.e2e.test.js.map +1 -1
- package/dist/cli-sessions.e2e.test.js +4 -3
- package/dist/cli-sessions.e2e.test.js.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/preference-commands.d.ts +4 -0
- package/dist/commands/preference-commands.js +38 -4
- package/dist/commands/preference-commands.js.map +1 -1
- package/dist/commands/session-commands.js +11 -8
- package/dist/commands/session-commands.js.map +1 -1
- package/dist/commands/thread-commands.js +94 -24
- package/dist/commands/thread-commands.js.map +1 -1
- package/dist/commands/worktree-commands.d.ts +5 -6
- package/dist/commands/worktree-commands.js +8 -7
- package/dist/commands/worktree-commands.js.map +1 -1
- package/dist/commands.e2e.test.js +25 -21
- package/dist/commands.e2e.test.js.map +1 -1
- package/dist/coordinator-agents.e2e.test.d.ts +1 -0
- package/dist/coordinator-agents.e2e.test.js +113 -0
- package/dist/coordinator-agents.e2e.test.js.map +1 -0
- package/dist/coordinator-idle.e2e.test.d.ts +1 -0
- package/dist/coordinator-idle.e2e.test.js +100 -0
- package/dist/coordinator-idle.e2e.test.js.map +1 -0
- package/dist/coordinator-queue.e2e.test.d.ts +1 -0
- package/dist/coordinator-queue.e2e.test.js +244 -0
- package/dist/coordinator-queue.e2e.test.js.map +1 -0
- package/dist/coordinator-warmup.e2e.test.d.ts +1 -0
- package/dist/coordinator-warmup.e2e.test.js +131 -0
- package/dist/coordinator-warmup.e2e.test.js.map +1 -0
- package/dist/coordinator.d.ts +6 -0
- package/dist/coordinator.e2e.test.d.ts +1 -0
- package/dist/coordinator.e2e.test.js +505 -0
- package/dist/coordinator.e2e.test.js.map +1 -0
- package/dist/coordinator.js +164 -0
- package/dist/coordinator.js.map +1 -0
- package/dist/db.d.ts +78 -3
- package/dist/db.test.js +1 -0
- package/dist/db.test.js.map +1 -1
- package/dist/diagram.d.ts +11 -0
- package/dist/diagram.js +232 -0
- package/dist/diagram.js.map +1 -0
- package/dist/diagram.test.d.ts +1 -0
- package/dist/diagram.test.js +52 -0
- package/dist/diagram.test.js.map +1 -0
- package/dist/duration.js +13 -5
- package/dist/duration.js.map +1 -1
- package/dist/effects.d.ts +6 -1
- package/dist/effects.js +97 -26
- package/dist/effects.js.map +1 -1
- package/dist/event-loop.d.ts +2 -3
- package/dist/event-loop.js +74 -44
- package/dist/event-loop.js.map +1 -1
- package/dist/format-parts.d.ts +21 -4
- package/dist/format-parts.js +40 -23
- package/dist/format-parts.js.map +1 -1
- package/dist/gateway-replay.e2e.test.js +4 -4
- package/dist/harness.e2e.test.js +2 -2
- package/dist/harness.e2e.test.js.map +1 -1
- package/dist/hello-thread.e2e.test.js +56 -5
- package/dist/hello-thread.e2e.test.js.map +1 -1
- package/dist/ingress.js +47 -12
- package/dist/ingress.js.map +1 -1
- package/dist/interaction-context.d.ts +4 -4
- package/dist/interaction-context.js +9 -7
- package/dist/interaction-context.js.map +1 -1
- package/dist/interrupt.e2e.test.js +8 -5
- package/dist/interrupt.e2e.test.js.map +1 -1
- package/dist/legacy.e2e.test.js +1 -1
- package/dist/lock-routes.d.ts +83 -31
- package/dist/lock-routes.js +68 -8
- package/dist/lock-routes.js.map +1 -1
- package/dist/lock-server.d.ts +3 -2
- package/dist/lock-server.js +23 -7
- package/dist/lock-server.js.map +1 -1
- package/dist/main.d.ts +6 -3
- package/dist/main.js +14 -4
- package/dist/main.js.map +1 -1
- package/dist/mainmast-idle.e2e.test.d.ts +1 -0
- package/dist/mainmast-idle.e2e.test.js +99 -0
- package/dist/mainmast-idle.e2e.test.js.map +1 -0
- package/dist/mainmast-queue.e2e.test.d.ts +1 -0
- package/dist/mainmast-queue.e2e.test.js +244 -0
- package/dist/mainmast-queue.e2e.test.js.map +1 -0
- package/dist/mainmast-warmup.e2e.test.d.ts +1 -0
- package/dist/mainmast-warmup.e2e.test.js +130 -0
- package/dist/mainmast-warmup.e2e.test.js.map +1 -0
- package/dist/mainmast.d.ts +6 -0
- package/dist/mainmast.e2e.test.d.ts +1 -0
- package/dist/mainmast.e2e.test.js +428 -0
- package/dist/mainmast.e2e.test.js.map +1 -0
- package/dist/mainmast.js +156 -0
- package/dist/mainmast.js.map +1 -0
- package/dist/markdown/markdown.test.js +14 -4
- package/dist/markdown/markdown.test.js.map +1 -1
- package/dist/markdown/render-markdown.js +9 -6
- package/dist/markdown/render-markdown.js.map +1 -1
- package/dist/onboarding-cli.e2e.test.js +7 -6
- package/dist/onboarding-cli.e2e.test.js.map +1 -1
- package/dist/onboarding.d.ts +3 -10
- package/dist/onboarding.e2e.test.js +8 -10
- package/dist/onboarding.e2e.test.js.map +1 -1
- package/dist/onboarding.js +14 -24
- package/dist/onboarding.js.map +1 -1
- package/dist/permissions.e2e.test.js +3 -3
- package/dist/plugin/coordinator.d.ts +4 -0
- package/dist/plugin/coordinator.js +389 -0
- package/dist/plugin/coordinator.js.map +1 -0
- package/dist/plugin/index.js +93 -61
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/mainmast.d.ts +4 -0
- package/dist/plugin/mainmast.js +379 -0
- package/dist/plugin/mainmast.js.map +1 -0
- package/dist/plugin.e2e.test.js +1 -1
- package/dist/preference-commands.e2e.test.js +103 -24
- package/dist/preference-commands.e2e.test.js.map +1 -1
- package/dist/project.d.ts +1 -1
- package/dist/prompt.d.ts +22 -9
- package/dist/prompt.js +64 -48
- package/dist/prompt.js.map +1 -1
- package/dist/questions.e2e.test.js +13 -13
- package/dist/queue-sources.e2e.test.d.ts +1 -0
- package/dist/queue-sources.e2e.test.js +168 -0
- package/dist/queue-sources.e2e.test.js.map +1 -0
- package/dist/queue.d.ts +9 -1
- package/dist/queue.e2e.test.js +48 -30
- package/dist/queue.e2e.test.js.map +1 -1
- package/dist/queue.js +97 -24
- package/dist/queue.js.map +1 -1
- package/dist/queue.test.d.ts +1 -0
- package/dist/queue.test.js +57 -0
- package/dist/queue.test.js.map +1 -0
- package/dist/remote-send.e2e.test.js +6 -6
- package/dist/remote-send.js +2 -1
- package/dist/remote-send.js.map +1 -1
- package/dist/renderer.e2e.test.js +7 -7
- package/dist/restart.e2e.test.js +2 -2
- package/dist/routines.e2e.test.js +2 -2
- package/dist/scheduler.d.ts +3 -3
- package/dist/scheduler.e2e.test.js +43 -20
- package/dist/scheduler.e2e.test.js.map +1 -1
- package/dist/scheduler.js +4 -2
- package/dist/scheduler.js.map +1 -1
- package/dist/schema-sql.d.ts +1 -1
- package/dist/schema-sql.js +1 -1
- package/dist/schema-sql.js.map +1 -1
- package/dist/schema.d.ts +152 -6
- package/dist/schema.js +12 -0
- package/dist/schema.js.map +1 -1
- package/dist/session-commands.e2e.test.js +36 -11
- package/dist/session-commands.e2e.test.js.map +1 -1
- package/dist/session-events.d.ts +18 -6
- package/dist/session-events.js +175 -27
- package/dist/session-events.js.map +1 -1
- package/dist/session-ops.e2e.test.js +63 -12
- package/dist/session-ops.e2e.test.js.map +1 -1
- package/dist/sessions.d.ts +44 -21
- package/dist/sessions.js +177 -65
- package/dist/sessions.js.map +1 -1
- package/dist/shell.e2e.test.js +4 -4
- package/dist/slash-commands.js +6 -4
- package/dist/slash-commands.js.map +1 -1
- package/dist/slash-commands.test.js +2 -0
- package/dist/slash-commands.test.js.map +1 -1
- package/dist/sleep.e2e.test.js +29 -14
- package/dist/sleep.e2e.test.js.map +1 -1
- package/dist/sleeps.d.ts +2 -1
- package/dist/sleeps.js +21 -3
- package/dist/sleeps.js.map +1 -1
- package/dist/store.d.ts +1 -0
- package/dist/store.js +3 -2
- package/dist/store.js.map +1 -1
- package/dist/system-prompt.d.ts +5 -15
- package/dist/system-prompt.js +180 -131
- package/dist/system-prompt.js.map +1 -1
- package/dist/test/generate-log-plugin/index.d.ts +3 -0
- package/dist/test/generate-log-plugin/index.js +15 -0
- package/dist/test/generate-log-plugin/index.js.map +1 -0
- package/dist/test/harness.d.ts +20 -2
- package/dist/test/harness.js +51 -11
- package/dist/test/harness.js.map +1 -1
- package/dist/test/replay.js +11 -8
- package/dist/test/replay.js.map +1 -1
- package/dist/thread-reducer.d.ts +29 -6
- package/dist/thread-reducer.fixtures.test.js +58 -51
- package/dist/thread-reducer.fixtures.test.js.map +1 -1
- package/dist/thread-reducer.js +109 -50
- package/dist/thread-reducer.js.map +1 -1
- package/dist/thread-reducer.test.js +190 -48
- package/dist/thread-reducer.test.js.map +1 -1
- package/dist/tunnel.d.ts +9 -0
- package/dist/tunnel.js +117 -28
- package/dist/tunnel.js.map +1 -1
- package/dist/tunnel.test.js +81 -5
- package/dist/tunnel.test.js.map +1 -1
- package/dist/voice-attachments.e2e.test.js +108 -18
- package/dist/voice-attachments.e2e.test.js.map +1 -1
- package/dist/voice-call.e2e.test.js +9 -9
- package/dist/voice-call.js +6 -6
- package/dist/voice-call.js.map +1 -1
- package/dist/voice-openai.e2e.test.js +59 -23
- package/dist/voice-openai.e2e.test.js.map +1 -1
- package/dist/voice.d.ts +1 -1
- package/dist/voice.js +72 -24
- package/dist/voice.js.map +1 -1
- package/dist/working-directory.e2e.test.js +11 -11
- package/dist/working-directory.e2e.test.js.map +1 -1
- package/package.json +11 -5
- package/skills/kimaki-tunnel/SKILL.md +3 -2
- package/src/agent-ui.e2e.test.ts +27 -13
- package/src/agent-ui.ts +2 -1
- package/src/analytics.e2e.test.ts +1 -1
- package/src/autostart.ts +55 -10
- package/src/bot.ts +36 -19
- package/src/btw.e2e.test.ts +3 -3
- package/src/builtin-skills.test.ts +73 -1
- package/src/builtin-skills.ts +33 -0
- package/src/catalog-refresh.e2e.test.ts +2 -2
- package/src/cli/bot.ts +30 -19
- package/src/cli/discord.ts +30 -6
- package/src/cli/schedule.ts +23 -9
- package/src/cli/session.ts +35 -12
- package/src/cli/shared.ts +43 -8
- package/src/cli-discord.e2e.test.ts +13 -8
- package/src/cli-lock.e2e.test.ts +88 -20
- package/src/cli-read.e2e.test.ts +17 -8
- package/src/cli-sessions.e2e.test.ts +4 -3
- package/src/cli.ts +2 -0
- package/src/commands/preference-commands.ts +38 -4
- package/src/commands/session-commands.ts +12 -7
- package/src/commands/thread-commands.ts +92 -24
- package/src/commands/worktree-commands.ts +10 -9
- package/src/commands.e2e.test.ts +25 -21
- package/src/coordinator-agents.e2e.test.ts +145 -0
- package/src/coordinator-idle.e2e.test.ts +122 -0
- package/src/coordinator-queue.e2e.test.ts +279 -0
- package/src/coordinator-warmup.e2e.test.ts +158 -0
- package/src/coordinator.e2e.test.ts +545 -0
- package/src/coordinator.ts +168 -0
- package/src/db.test.ts +1 -0
- package/src/diagram.test.ts +56 -0
- package/src/diagram.ts +243 -0
- package/src/duration.ts +13 -5
- package/src/effects.ts +99 -27
- package/src/event-loop.ts +71 -45
- package/src/format-parts.ts +53 -22
- package/src/gateway-replay.e2e.test.ts +4 -4
- package/src/harness.e2e.test.ts +2 -2
- package/src/hello-thread.e2e.test.ts +59 -5
- package/src/ingress.ts +44 -11
- package/src/interaction-context.ts +12 -11
- package/src/interrupt.e2e.test.ts +8 -5
- package/src/legacy.e2e.test.ts +1 -1
- package/src/lock-routes.ts +62 -8
- package/src/lock-server.ts +23 -7
- package/src/main.ts +26 -10
- package/src/markdown/markdown.test.ts +15 -4
- package/src/markdown/render-markdown.ts +9 -6
- package/src/onboarding-cli.e2e.test.ts +7 -6
- package/src/onboarding.e2e.test.ts +8 -10
- package/src/onboarding.ts +13 -29
- package/src/permissions.e2e.test.ts +3 -3
- package/src/plugin/coordinator.ts +401 -0
- package/src/plugin/index.ts +86 -59
- package/src/plugin.e2e.test.ts +1 -1
- package/src/preference-commands.e2e.test.ts +103 -23
- package/src/prompt.ts +87 -43
- package/src/questions.e2e.test.ts +13 -13
- package/src/queue-sources.e2e.test.ts +197 -0
- package/src/queue.e2e.test.ts +48 -30
- package/src/queue.test.ts +59 -0
- package/src/queue.ts +104 -24
- package/src/remote-send.e2e.test.ts +6 -6
- package/src/remote-send.ts +2 -1
- package/src/renderer.e2e.test.ts +7 -7
- package/src/restart.e2e.test.ts +2 -2
- package/src/routines.e2e.test.ts +2 -2
- package/src/scheduler.e2e.test.ts +45 -19
- package/src/scheduler.ts +4 -2
- package/src/schema-sql.ts +1 -1
- package/src/schema.ts +13 -0
- package/src/session-commands.e2e.test.ts +38 -11
- package/src/session-events.ts +168 -25
- package/src/session-ops.e2e.test.ts +64 -11
- package/src/sessions.ts +217 -67
- package/src/shell.e2e.test.ts +4 -4
- package/src/slash-commands.test.ts +2 -0
- package/src/slash-commands.ts +6 -4
- package/src/sleep.e2e.test.ts +30 -13
- package/src/sleeps.ts +20 -3
- package/src/store.ts +5 -2
- package/src/system-prompt.ts +190 -134
- package/src/test/generate-log-plugin/index.ts +16 -0
- package/src/test/harness.ts +77 -17
- package/src/test/replay.ts +12 -7
- package/src/thread-reducer.fixtures.test.ts +58 -51
- package/src/thread-reducer.test.ts +199 -49
- package/src/thread-reducer.ts +135 -66
- package/src/tunnel.test.ts +81 -5
- package/src/tunnel.ts +124 -25
- package/src/voice-attachments.e2e.test.ts +110 -18
- package/src/voice-call.e2e.test.ts +9 -9
- package/src/voice-call.ts +6 -6
- package/src/voice-openai.e2e.test.ts +64 -24
- package/src/voice.ts +93 -26
- package/src/working-directory.e2e.test.ts +11 -11
- package/src/message-tool.e2e.test.ts +0 -45
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
// Coordinator channels: the channel is the conversation of one long session, it
|
|
2
|
+
// starts worker threads with `kimaki send --parent-session`, and a finished
|
|
3
|
+
// worker wakes it with a <session-finished> note.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { afterAll, beforeAll, expect, test } from 'vitest';
|
|
7
|
+
import { send } from "./prompt.js";
|
|
8
|
+
import { readSessionMarkdown } from "./session-events.js";
|
|
9
|
+
import { TEST_USER_ID, isFooter, scriptedTurn, seedProjectChannel, startOpencodeTestServer, startTestBot, startTwin, tempDataDir, textParts, waitFor, waitForFooter, warmUp, } from "./test/harness.js";
|
|
10
|
+
let server;
|
|
11
|
+
let twin;
|
|
12
|
+
let bot;
|
|
13
|
+
const dataDir = tempDataDir();
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
// The twin first: the delegating matcher needs its channel ID.
|
|
16
|
+
twin = await startTwin();
|
|
17
|
+
server = await startOpencodeTestServer({
|
|
18
|
+
matchers: [
|
|
19
|
+
{ id: 'hello', priority: 100, when: { latestUserTextIncludes: 'hello-marker' }, then: { parts: textParts('Hello from the coordinator.') } },
|
|
20
|
+
// The coordinator delegates: one shell call to `kimaki send`, then IDLE (the new thread already shows).
|
|
21
|
+
...scriptedTurn({
|
|
22
|
+
marker: 'delegate-marker',
|
|
23
|
+
steps: [{
|
|
24
|
+
id: 'call-send-worker',
|
|
25
|
+
tool: 'shell',
|
|
26
|
+
input: {
|
|
27
|
+
command: `kimaki send --channel ${twin.channelId} --name 'Worker task' --prompt 'worker-marker do the job' --parent-session "$OPENCODE_SESSION_ID"`,
|
|
28
|
+
description: 'Start the worker thread',
|
|
29
|
+
hasSideEffect: true,
|
|
30
|
+
},
|
|
31
|
+
}],
|
|
32
|
+
finalText: 'IDLE',
|
|
33
|
+
}),
|
|
34
|
+
// The default: the coordinator waits for the worker in the same shell call.
|
|
35
|
+
...scriptedTurn({
|
|
36
|
+
marker: 'waitjob-marker',
|
|
37
|
+
steps: [{
|
|
38
|
+
id: 'call-send-wait',
|
|
39
|
+
tool: 'shell',
|
|
40
|
+
input: {
|
|
41
|
+
command: `kimaki send --channel ${twin.channelId} --name 'Waited task' --prompt 'waited-worker-marker do the job' --parent-session "$OPENCODE_SESSION_ID" --wait`,
|
|
42
|
+
description: 'Run the worker and wait',
|
|
43
|
+
hasSideEffect: true,
|
|
44
|
+
},
|
|
45
|
+
}],
|
|
46
|
+
finalText: 'The waited worker is done.',
|
|
47
|
+
}),
|
|
48
|
+
{ id: 'waited-worker', priority: 100, when: { latestUserTextIncludes: 'waited-worker-marker' }, then: { parts: textParts('Waited job done.') } },
|
|
49
|
+
// A bounded wait runs out; the coordinator is still busy when the worker ends, so the note must still come.
|
|
50
|
+
...scriptedTurn({
|
|
51
|
+
marker: 'boundedwait-marker',
|
|
52
|
+
steps: [
|
|
53
|
+
{
|
|
54
|
+
id: 'call-send-bounded',
|
|
55
|
+
tool: 'shell',
|
|
56
|
+
input: {
|
|
57
|
+
command: `kimaki send --channel ${twin.channelId} --name 'Bounded task' --prompt 'boundedwait-job-marker do the job' --parent-session "$OPENCODE_SESSION_ID" --wait 0.1`,
|
|
58
|
+
description: 'Start the worker, wait briefly',
|
|
59
|
+
hasSideEffect: true,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{ id: 'call-stay-busy', tool: 'shell', input: { command: 'sleep 2', description: 'Stay busy', hasSideEffect: false } },
|
|
63
|
+
],
|
|
64
|
+
finalText: 'IDLE',
|
|
65
|
+
}),
|
|
66
|
+
{ id: 'bounded-worker', priority: 100, when: { latestUserTextIncludes: 'boundedwait-job-marker' }, then: { parts: textParts('Bounded job done.'), defaultPartDelayMs: 100 } },
|
|
67
|
+
{ id: 'woken-by-bounded', priority: 345, when: { latestUserTextRegex: '<session-finished[\\s\\S]*Bounded job done' }, then: { parts: textParts('The bounded job finished.') } },
|
|
68
|
+
// Slow enough that the delegating run ends first: the note then wakes an idle coordinator.
|
|
69
|
+
{ id: 'worker', priority: 100, when: { latestUserTextIncludes: 'worker-marker' }, then: { parts: textParts('Job done in the worker.'), defaultPartDelayMs: 300 } },
|
|
70
|
+
// A worker that uploads a screenshot; session read lists its URL, the note does not.
|
|
71
|
+
...scriptedTurn({
|
|
72
|
+
marker: 'upload-marker',
|
|
73
|
+
steps: [{ id: 'call-upload', tool: 'shell', input: { command: 'kimaki upload-to-discord shot.png', description: 'Upload the screenshot', hasSideEffect: true } }],
|
|
74
|
+
finalText: 'Took the screenshot.',
|
|
75
|
+
}),
|
|
76
|
+
{ id: 'woken-with-media', priority: 320, when: { latestUserTextRegex: '<session-finished[\\s\\S]*Took the screenshot' }, then: { parts: textParts('Here is the screenshot.') } },
|
|
77
|
+
{ id: 'user-steer', priority: 100, when: { latestUserTextIncludes: 'user-steer-marker' }, then: { parts: textParts('Steered job done.') } },
|
|
78
|
+
{ id: 'follow-up', priority: 100, when: { latestUserTextIncludes: 'follow-up-marker' }, then: { parts: textParts('Follow-up done.') } },
|
|
79
|
+
{ id: 'woken-by-follow-up', priority: 340, when: { latestUserTextRegex: '<session-finished[\\s\\S]*Follow-up done' }, then: { parts: textParts('The follow-up finished.') } },
|
|
80
|
+
{ id: 'fork', priority: 100, when: { latestUserTextIncludes: 'fork-marker' }, then: { parts: textParts('Fork job done.') } },
|
|
81
|
+
{ id: 'slow-job', priority: 100, when: { latestUserTextIncludes: 'slow-job-marker' }, then: { parts: textParts('Slow job done.'), defaultPartDelayMs: 500 } },
|
|
82
|
+
{ id: 'woken-by-slow-job', priority: 330, when: { latestUserTextRegex: '<session-finished[\\s\\S]*Slow job done' }, then: { parts: textParts('The slow job finished.') } },
|
|
83
|
+
{ id: 'woken-by-fork', priority: 310, when: { latestUserTextIncludes: '<session-finished', rawPromptIncludes: 'Fork job done.' }, then: { parts: textParts('The fork finished the job.') } },
|
|
84
|
+
{ id: 'btw', priority: 100, when: { latestUserTextIncludes: 'btw-marker' }, then: { parts: textParts('Side answer.') } },
|
|
85
|
+
{ id: 'side', priority: 100, when: { latestUserTextIncludes: 'side-marker' }, then: { parts: textParts('Side check done.') } },
|
|
86
|
+
// The silent note of the side thread is in the history only when it arrived.
|
|
87
|
+
{ id: 'recap-seen', priority: 200, when: { latestUserTextIncludes: 'recap-marker', rawPromptIncludes: 'Side check done.' }, then: { parts: textParts('A side thread finished meanwhile.') } },
|
|
88
|
+
{ id: 'recap-unseen', priority: 150, when: { latestUserTextIncludes: 'recap-marker' }, then: { parts: textParts('Nothing new.') } },
|
|
89
|
+
{ id: 'woken', priority: 300, when: { latestUserTextIncludes: '<session-finished' }, then: { parts: textParts('The worker finished the job.') } },
|
|
90
|
+
{ id: 'stale-wake', priority: 350, when: { latestUserTextIncludes: 'stale-wake-marker' }, then: { parts: textParts('Woke on the stale role text.') } },
|
|
91
|
+
],
|
|
92
|
+
});
|
|
93
|
+
await seedProjectChannel({ dataDir, channelId: twin.channelId, guildId: twin.discord.guildId, directory: server.projectDirectory });
|
|
94
|
+
await warmUp({ server });
|
|
95
|
+
bot = await startTestBot({ dataDir, twin, server, coordinator: true });
|
|
96
|
+
}, 60_000);
|
|
97
|
+
// Role texts the kimaki plugin stored in a session (plugin/coordinator.ts), oldest first.
|
|
98
|
+
async function roleTexts(sessionID) {
|
|
99
|
+
const client = await server.client();
|
|
100
|
+
const messages = await client.message.list({ sessionID, order: 'desc', limit: 100 });
|
|
101
|
+
return [...messages.data].reverse().flatMap((message) => (message.type === 'synthetic' && message.text.includes('<kimaki-coordinator>') ? [message.text] : []));
|
|
102
|
+
}
|
|
103
|
+
// Token counts and run times differ between runs.
|
|
104
|
+
function usageless(note) {
|
|
105
|
+
return note.replace(/ context="[^"]+"/, ' context="N tokens"').replace(/ duration="[^"]+"/, ' duration="Ns"');
|
|
106
|
+
}
|
|
107
|
+
afterAll(async () => {
|
|
108
|
+
await bot?.stop();
|
|
109
|
+
await twin?.stop();
|
|
110
|
+
await server?.stop();
|
|
111
|
+
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
112
|
+
});
|
|
113
|
+
test('channel messages go to one session that replies in the channel', async () => {
|
|
114
|
+
const { discord, channelId } = twin;
|
|
115
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Say hi hello-marker' });
|
|
116
|
+
await waitForFooter({ discord, threadId: channelId });
|
|
117
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Again hello-marker once-marker' });
|
|
118
|
+
await waitForFooter({ discord, threadId: channelId, count: 2 });
|
|
119
|
+
expect(await discord.channel(channelId).text({ showReplies: true })).toMatchInlineSnapshot(`
|
|
120
|
+
"--- from: user (tommy)
|
|
121
|
+
Say hi hello-marker
|
|
122
|
+
--- from: assistant (TestBot)
|
|
123
|
+
-# *using deterministic-provider/deterministic-v2 ⋅ build*
|
|
124
|
+
-# Coordinator mode is on: this channel is one session that starts and manages project threads for you. Use /new-session for a thread without it. Turn it off with /toggle-coordinator or start kimaki with \`--coordinator false\`.
|
|
125
|
+
[reply to tommy: Say hi hello-marker]
|
|
126
|
+
Hello from the coordinator.
|
|
127
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
128
|
+
--- from: user (tommy)
|
|
129
|
+
Again hello-marker once-marker
|
|
130
|
+
--- from: assistant (TestBot)
|
|
131
|
+
[reply to tommy: Again hello-marker once-marker]
|
|
132
|
+
Hello from the coordinator.
|
|
133
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
134
|
+
`);
|
|
135
|
+
expect(await discord.channel(channelId).getThreads()).toHaveLength(0);
|
|
136
|
+
// The role text reaches the model once per request, also when the plugin checks the request.
|
|
137
|
+
const request = server.model.requests.findLast((item) => item.latestUserText.includes('once-marker'));
|
|
138
|
+
expect(request?.raw.split('## coordinator: you coordinate').length).toBe(2);
|
|
139
|
+
// The coordinator role comes from the plugin, once: not from the frozen instructions entry.
|
|
140
|
+
const root = bot.store.getState().roots[channelId];
|
|
141
|
+
const roles = await roleTexts(root);
|
|
142
|
+
expect(roles).toHaveLength(1);
|
|
143
|
+
expect(roles[0]).toContain('## coordinator: you coordinate this channel');
|
|
144
|
+
expect(roles[0]).toContain(`--parent-session ${root}`);
|
|
145
|
+
const entries = await (await server.client()).session.instructions.entry.list({ sessionID: root });
|
|
146
|
+
expect(JSON.stringify(entries)).not.toContain('## coordinator');
|
|
147
|
+
});
|
|
148
|
+
test('a worker started with --parent-session gets a thread and wakes the coordinator when done', async () => {
|
|
149
|
+
const { discord, channelId } = twin;
|
|
150
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter);
|
|
151
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Fix it delegate-marker' });
|
|
152
|
+
const thread = await discord.channel(channelId).waitForThread({ timeout: 8_000 });
|
|
153
|
+
await waitForFooter({ discord, threadId: thread.id });
|
|
154
|
+
await waitFor({
|
|
155
|
+
label: 'woken reply',
|
|
156
|
+
check: async () => (await discord.channel(channelId).text()).includes('The worker finished the job.') || null,
|
|
157
|
+
});
|
|
158
|
+
// The dispatch turn answered IDLE after its tool line: only the woken report gets a footer.
|
|
159
|
+
await waitForFooter({ discord, threadId: channelId, count: footers.length + 1 });
|
|
160
|
+
// Only the first answer text of a user prompt replies to it: never tool lines, the wake answer or a worker thread.
|
|
161
|
+
expect((await discord.channel(channelId).text({ showReplies: true })).replaceAll(thread.id, 'worker-thread')).toMatchInlineSnapshot(`
|
|
162
|
+
"--- from: user (tommy)
|
|
163
|
+
Say hi hello-marker
|
|
164
|
+
--- from: assistant (TestBot)
|
|
165
|
+
-# *using deterministic-provider/deterministic-v2 ⋅ build*
|
|
166
|
+
-# Coordinator mode is on: this channel is one session that starts and manages project threads for you. Use /new-session for a thread without it. Turn it off with /toggle-coordinator or start kimaki with \`--coordinator false\`.
|
|
167
|
+
[reply to tommy: Say hi hello-marker]
|
|
168
|
+
Hello from the coordinator.
|
|
169
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
170
|
+
--- from: user (tommy)
|
|
171
|
+
Again hello-marker once-marker
|
|
172
|
+
--- from: assistant (TestBot)
|
|
173
|
+
[reply to tommy: Again hello-marker once-marker]
|
|
174
|
+
Hello from the coordinator.
|
|
175
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
176
|
+
--- from: user (tommy)
|
|
177
|
+
Fix it delegate-marker
|
|
178
|
+
--- from: assistant (TestBot)
|
|
179
|
+
-# ┣ shell _Start the worker thread_
|
|
180
|
+
The worker finished the job.
|
|
181
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
182
|
+
`);
|
|
183
|
+
expect(await discord.thread(thread.id).text({ showReplies: true })).toMatchInlineSnapshot(`
|
|
184
|
+
"--- from: assistant (TestBot)
|
|
185
|
+
-# » **CLI:** worker-marker do the job
|
|
186
|
+
-# *using deterministic-provider/deterministic-v2 ⋅ build*
|
|
187
|
+
Job done in the worker.
|
|
188
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
189
|
+
`);
|
|
190
|
+
expect(thread.name).toBe('Worker task');
|
|
191
|
+
const workerRoles = await roleTexts(bot.store.getState().roots[thread.id]);
|
|
192
|
+
expect(workerRoles).toHaveLength(1);
|
|
193
|
+
expect(workerRoles[0]).toContain(`## worker of a coordinator`);
|
|
194
|
+
expect(workerRoles[0]).toContain(`kimaki session read ${channelId}`);
|
|
195
|
+
});
|
|
196
|
+
test('a btw fork of a worker sent with --parent-session wakes the coordinator when done', async () => {
|
|
197
|
+
const { discord, channelId } = twin;
|
|
198
|
+
const root = bot.store.getState().roots[channelId];
|
|
199
|
+
const worker = (await discord.channel(channelId).getThreads()).find((candidate) => candidate.name === 'Worker task');
|
|
200
|
+
if (!worker)
|
|
201
|
+
throw new Error('no Worker task thread');
|
|
202
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter).length;
|
|
203
|
+
// What the coordinator runs: kimaki send --thread <worker> --prompt '... btw' --parent-session <coordinator>
|
|
204
|
+
const forked = await send(bot, { threadId: worker.id, prompt: 'fork-marker related job. btw', parentSessionId: root, senderSessionId: root });
|
|
205
|
+
if (forked instanceof Error)
|
|
206
|
+
throw forked;
|
|
207
|
+
await waitForFooter({ discord, threadId: forked.threadId });
|
|
208
|
+
await waitForFooter({ discord, threadId: channelId, count: footers + 1 });
|
|
209
|
+
const text = await discord.channel(channelId).text();
|
|
210
|
+
expect(text.slice(text.lastIndexOf('The worker finished the job.'))).toMatchInlineSnapshot(`
|
|
211
|
+
"The worker finished the job.
|
|
212
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
213
|
+
The fork finished the job.
|
|
214
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
215
|
+
`);
|
|
216
|
+
const fork = await (await server.client()).session.get({ sessionID: forked.sessionId });
|
|
217
|
+
expect(Reflect.get(Object(fork.metadata?.['kimaki']), 'parentSessionId')).toBe(root);
|
|
218
|
+
expect((await roleTexts(fork.id)).at(-1)).toContain('## worker of a coordinator');
|
|
219
|
+
});
|
|
220
|
+
test('session read lists the Discord URL of a file a worker uploaded; its note does not', async () => {
|
|
221
|
+
const { discord, channelId } = twin;
|
|
222
|
+
const root = bot.store.getState().roots[channelId];
|
|
223
|
+
fs.writeFileSync(path.join(server.projectDirectory, 'shot.png'), 'png');
|
|
224
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter).length;
|
|
225
|
+
const started = await send(bot, { channelId, name: 'Screenshot task', prompt: 'upload-marker take a screenshot', parentSessionId: root, senderSessionId: root });
|
|
226
|
+
if (started instanceof Error)
|
|
227
|
+
throw started;
|
|
228
|
+
await waitForFooter({ discord, threadId: started.threadId });
|
|
229
|
+
await waitForFooter({ discord, threadId: channelId, count: footers + 1 });
|
|
230
|
+
const text = await discord.channel(channelId).text();
|
|
231
|
+
expect(text.slice(text.lastIndexOf('-# *main', text.lastIndexOf('-# *main') - 1))).toMatchInlineSnapshot(`
|
|
232
|
+
"-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
233
|
+
Here is the screenshot.
|
|
234
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
235
|
+
`);
|
|
236
|
+
const client = await server.client();
|
|
237
|
+
const messages = await client.message.list({ sessionID: root, order: 'desc', limit: 10 });
|
|
238
|
+
const note = messages.data.find((message) => message.type === 'synthetic' && message.text.includes('Screenshot task'));
|
|
239
|
+
expect(note?.type === 'synthetic' && usageless(note.text.replaceAll(started.sessionId, 'ses_worker').replaceAll(started.threadId, 'worker-thread'))).toMatchInlineSnapshot(`
|
|
240
|
+
"<session-finished sessionID="ses_worker" threadID="worker-thread" state="completed" title="Screenshot task" context="N tokens" duration="Ns" wake="true">
|
|
241
|
+
Took the screenshot.
|
|
242
|
+
More: kimaki session read ses_worker
|
|
243
|
+
The user sees nothing else about this. When it is work they asked for, a failure or a decision they must make, tell them the outcome in a few plain sentences, with the thread link <#worker-thread> once. Otherwise reply exactly IDLE.
|
|
244
|
+
</session-finished>"
|
|
245
|
+
`);
|
|
246
|
+
const read = await readSessionMarkdown({ client, sessionId: started.sessionId });
|
|
247
|
+
expect(read instanceof Error ? read : read.slice(read.indexOf('Took the screenshot.'), read.indexOf('Only prompts'))).toMatchInlineSnapshot(`
|
|
248
|
+
"Took the screenshot.
|
|
249
|
+
|
|
250
|
+
Media uploaded to Discord:
|
|
251
|
+
- data:application/octet-stream;base64,cG5n
|
|
252
|
+
|
|
253
|
+
"
|
|
254
|
+
`);
|
|
255
|
+
// --full and transcripts hide tool outputs but keep the uploaded media on the tool line.
|
|
256
|
+
const full = await readSessionMarkdown({ client, sessionId: started.sessionId, full: true, thinking: true });
|
|
257
|
+
expect(full instanceof Error ? full : full.slice(full.indexOf('tool shell'), full.indexOf('finish: tool-calls')).trim()).toMatchInlineSnapshot(`
|
|
258
|
+
"tool shell [completed] {"command":"kimaki upload-to-discord shot.png","description":"Upload the screenshot","hasSideEffect":true}
|
|
259
|
+
Media uploaded to Discord:
|
|
260
|
+
- data:application/octet-stream;base64,cG5n"
|
|
261
|
+
`);
|
|
262
|
+
});
|
|
263
|
+
test('a run the user prompts in a worker thread is a silent note; a follow-up the coordinator sends wakes it', async () => {
|
|
264
|
+
const { discord, channelId } = twin;
|
|
265
|
+
const root = bot.store.getState().roots[channelId];
|
|
266
|
+
const client = await server.client();
|
|
267
|
+
const worker = (await discord.channel(channelId).getThreads()).find((candidate) => candidate.name === 'Worker task');
|
|
268
|
+
if (!worker)
|
|
269
|
+
throw new Error('no Worker task thread');
|
|
270
|
+
const footers = () => discord.channel(channelId).getMessages().then((messages) => messages.filter(isFooter).length);
|
|
271
|
+
const before = await footers();
|
|
272
|
+
const workerFooters = (await discord.thread(worker.id).getMessages()).filter(isFooter).length;
|
|
273
|
+
await discord.thread(worker.id).user(TEST_USER_ID).sendMessage({ content: 'Also check the logs user-steer-marker' });
|
|
274
|
+
await waitForFooter({ discord, threadId: worker.id, count: workerFooters + 1 });
|
|
275
|
+
const note = await waitFor({
|
|
276
|
+
label: 'silent note of the steered run',
|
|
277
|
+
check: async () => {
|
|
278
|
+
const inbox = await client.session.inbox.list({ sessionID: root });
|
|
279
|
+
const item = inbox.find((candidate) => candidate.type === 'synthetic' && candidate.payload.text.includes('Steered job done.'));
|
|
280
|
+
return item?.type === 'synthetic' ? item.payload.text : null;
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
expect(usageless(note.replaceAll(worker.id, 'worker-thread').replace(/ses_\w+/g, 'ses_worker'))).toMatchInlineSnapshot(`
|
|
284
|
+
"<session-finished sessionID="ses_worker" threadID="worker-thread" state="completed" title="Worker task" context="N tokens" duration="Ns" wake="false">
|
|
285
|
+
<user-prompt username="tommy">
|
|
286
|
+
Also check the logs user-steer-marker
|
|
287
|
+
</user-prompt>
|
|
288
|
+
Steered job done.
|
|
289
|
+
More: kimaki session read ses_worker
|
|
290
|
+
</session-finished>"
|
|
291
|
+
`);
|
|
292
|
+
// Not woken: no new footer in the channel.
|
|
293
|
+
for (let i = 0; i < 10; i++) {
|
|
294
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
295
|
+
expect(await footers()).toBe(before);
|
|
296
|
+
}
|
|
297
|
+
// What the coordinator runs: kimaki send --thread <worker> --prompt '...', from its own shell.
|
|
298
|
+
const followed = await send(bot, { threadId: worker.id, prompt: 'Now the follow-up-marker', senderSessionId: root });
|
|
299
|
+
if (followed instanceof Error)
|
|
300
|
+
throw followed;
|
|
301
|
+
await waitForFooter({ discord, threadId: channelId, count: before + 1 });
|
|
302
|
+
const text = await discord.channel(channelId).text();
|
|
303
|
+
expect(text.slice(text.lastIndexOf('-# *main', text.lastIndexOf('-# *main') - 1))).toMatchInlineSnapshot(`
|
|
304
|
+
"-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
305
|
+
The follow-up finished.
|
|
306
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
307
|
+
`);
|
|
308
|
+
});
|
|
309
|
+
test('a worker started with --wait answers through the wait, with no note', async () => {
|
|
310
|
+
const { discord, channelId } = twin;
|
|
311
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter);
|
|
312
|
+
const threads = (await discord.channel(channelId).getThreads()).length;
|
|
313
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Fix it waitjob-marker' });
|
|
314
|
+
await waitForFooter({ discord, threadId: channelId, count: footers.length + 1 });
|
|
315
|
+
const thread = (await discord.channel(channelId).getThreads()).find((candidate) => candidate.name === 'Waited task');
|
|
316
|
+
if (!thread)
|
|
317
|
+
throw new Error('no Waited task thread');
|
|
318
|
+
await waitForFooter({ discord, threadId: thread.id });
|
|
319
|
+
const root = bot.store.getState().roots[channelId];
|
|
320
|
+
const client = await server.client();
|
|
321
|
+
// No note arrives: not in the inbox, no wake, no "finished" line.
|
|
322
|
+
for (let i = 0; i < 10; i++) {
|
|
323
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
324
|
+
const inbox = await client.session.inbox.list({ sessionID: root });
|
|
325
|
+
expect(inbox.filter((item) => item.type === 'synthetic' && item.payload.text.includes('Waited task'))).toHaveLength(0);
|
|
326
|
+
}
|
|
327
|
+
const text = (await discord.channel(channelId).text()).replaceAll(thread.id, 'worker-thread');
|
|
328
|
+
// The answer of the busy prompt, then the queued one starts: its IDLE answer posts nothing.
|
|
329
|
+
expect(text.slice(text.lastIndexOf('[reply to tommy: Take a while'))).toMatchInlineSnapshot(`"*"`);
|
|
330
|
+
expect(await discord.channel(channelId).getThreads()).toHaveLength(threads + 1);
|
|
331
|
+
const messages = await client.message.list({ sessionID: root, order: 'desc', limit: 20 });
|
|
332
|
+
expect(messages.data.some((message) => message.type === 'synthetic' && JSON.stringify(message).includes('Waited task'))).toBe(false);
|
|
333
|
+
});
|
|
334
|
+
test('a worker that outlives a bounded --wait wakes the coordinator with a note', async () => {
|
|
335
|
+
const { discord, channelId } = twin;
|
|
336
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter);
|
|
337
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Do it boundedwait-marker' });
|
|
338
|
+
await waitForFooter({ discord, threadId: channelId, count: footers.length + 1 });
|
|
339
|
+
const text = await discord.channel(channelId).text();
|
|
340
|
+
expect(text.slice(text.lastIndexOf('Do it boundedwait-marker'))).toMatchInlineSnapshot(`
|
|
341
|
+
"Do it boundedwait-marker
|
|
342
|
+
--- from: assistant (TestBot)
|
|
343
|
+
-# ┣ shell _Start the worker, wait briefly_
|
|
344
|
+
The bounded job finished.
|
|
345
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
346
|
+
`);
|
|
347
|
+
expect(server.model.requests.some((request) => request.raw.includes('is still running after 0.1s'))).toBe(true);
|
|
348
|
+
});
|
|
349
|
+
test('another thread of the channel leaves a silent note for the next prompt', async () => {
|
|
350
|
+
const { discord, channelId } = twin;
|
|
351
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter);
|
|
352
|
+
const started = await send(bot, { channelId, prompt: 'side-marker check something' });
|
|
353
|
+
if (started instanceof Error)
|
|
354
|
+
throw started;
|
|
355
|
+
await waitForFooter({ discord, threadId: started.threadId });
|
|
356
|
+
const client = await server.client();
|
|
357
|
+
const root = bot.store.getState().roots[channelId];
|
|
358
|
+
await waitFor({
|
|
359
|
+
label: 'silent note in the coordinator',
|
|
360
|
+
check: async () => {
|
|
361
|
+
// Not delivered yet: a note without wake waits in the inbox for the next run.
|
|
362
|
+
const inbox = await client.session.inbox.list({ sessionID: root });
|
|
363
|
+
return inbox.some((item) => item.type === 'synthetic' && item.payload.text.includes('wake="false"')) || null;
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
// Not woken: no new footer in the channel until the user writes.
|
|
367
|
+
expect((await discord.channel(channelId).getMessages()).filter(isFooter)).toHaveLength(footers.length);
|
|
368
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'What happened? recap-marker' });
|
|
369
|
+
await waitForFooter({ discord, threadId: channelId, count: footers.length + 1 });
|
|
370
|
+
const text = await discord.channel(channelId).text();
|
|
371
|
+
expect(text.slice(text.lastIndexOf('--- from: user'))).toMatchInlineSnapshot(`
|
|
372
|
+
"--- from: user (tommy)
|
|
373
|
+
What happened? recap-marker
|
|
374
|
+
--- from: assistant (TestBot)
|
|
375
|
+
A side thread finished meanwhile.
|
|
376
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
377
|
+
`);
|
|
378
|
+
});
|
|
379
|
+
test('a btw fork of the coordinator is a worker: no coordinator timers', async () => {
|
|
380
|
+
const { discord, channelId } = twin;
|
|
381
|
+
const before = (await discord.channel(channelId).getThreads()).length;
|
|
382
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Why that choice btw-marker. btw' });
|
|
383
|
+
const thread = await waitFor({
|
|
384
|
+
label: 'btw thread',
|
|
385
|
+
check: async () => (await discord.channel(channelId).getThreads()).slice(before).find((candidate) => candidate.name?.startsWith('btw:')) ?? null,
|
|
386
|
+
});
|
|
387
|
+
await waitForFooter({ discord, threadId: thread.id });
|
|
388
|
+
const client = await server.client();
|
|
389
|
+
const fork = await client.session.get({ sessionID: bot.store.getState().roots[thread.id] });
|
|
390
|
+
expect(Reflect.get(Object(fork.metadata?.['kimaki']), 'coordinator')).toBeUndefined();
|
|
391
|
+
// The fork copied the coordinator role; its own worker role comes after it.
|
|
392
|
+
const roles = await roleTexts(fork.id);
|
|
393
|
+
expect(roles.map((text) => (text.includes('## worker of a coordinator') ? 'worker' : 'coordinator'))).toEqual(['coordinator', 'worker']);
|
|
394
|
+
});
|
|
395
|
+
test('a stale coordinator role text is replaced: on a wake for that request, then stored at the next prompt', async () => {
|
|
396
|
+
const { discord, channelId } = twin;
|
|
397
|
+
const root = bot.store.getState().roots[channelId];
|
|
398
|
+
const client = await server.client();
|
|
399
|
+
const current = (await roleTexts(root)).at(-1);
|
|
400
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter).length;
|
|
401
|
+
// Like a role text of older plugin code, as the latest one in the history, waking the session.
|
|
402
|
+
await client.session.synthetic({ sessionID: root, text: '<system-reminder>\n<kimaki-coordinator>\nold role text stale-wake-marker\n</kimaki-coordinator>\n</system-reminder>' });
|
|
403
|
+
await waitForFooter({ discord, threadId: channelId, count: footers + 1 });
|
|
404
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Again hello-marker' });
|
|
405
|
+
await waitForFooter({ discord, threadId: channelId, count: footers + 2 });
|
|
406
|
+
const text = await discord.channel(channelId).text();
|
|
407
|
+
expect(text.slice(text.lastIndexOf('Session forked!')).replace(/<#\d+>/, '<#btw-thread>')).toMatchInlineSnapshot(`
|
|
408
|
+
"Session forked! Continue in <#btw-thread>
|
|
409
|
+
Woke on the stale role text.
|
|
410
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
411
|
+
--- from: user (tommy)
|
|
412
|
+
Again hello-marker
|
|
413
|
+
--- from: assistant (TestBot)
|
|
414
|
+
Hello from the coordinator.
|
|
415
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
416
|
+
`);
|
|
417
|
+
// The wake request already had the current role text after the stale one.
|
|
418
|
+
const wake = server.model.requests.findLast((item) => item.latestUserText.includes('stale-wake-marker'));
|
|
419
|
+
expect(wake?.latestUserText).toMatch(/stale-wake-marker[\s\S]*## coordinator: you coordinate this channel/);
|
|
420
|
+
const roles = await roleTexts(root);
|
|
421
|
+
expect(roles.at(-1)).toBe(current);
|
|
422
|
+
expect(roles.at(-2)).toContain('stale-wake-marker');
|
|
423
|
+
});
|
|
424
|
+
test('the coordinator shows typing while a worker runs in the background', async () => {
|
|
425
|
+
const { discord, channelId } = twin;
|
|
426
|
+
const root = bot.store.getState().roots[channelId];
|
|
427
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter).length;
|
|
428
|
+
discord.channel(channelId).clearTypingEvents();
|
|
429
|
+
const started = await send(bot, { channelId, name: 'Slow task', prompt: 'slow-job-marker take your time', parentSessionId: root, senderSessionId: root });
|
|
430
|
+
if (started instanceof Error)
|
|
431
|
+
throw started;
|
|
432
|
+
await discord.channel(channelId).waitForTypingEvent();
|
|
433
|
+
// Read when typing starts: it must come from the running worker, not from the wake after it.
|
|
434
|
+
const workerAtTyping = await discord.thread(started.threadId).text();
|
|
435
|
+
await waitForFooter({ discord, threadId: started.threadId });
|
|
436
|
+
await waitForFooter({ discord, threadId: channelId, count: footers + 1 });
|
|
437
|
+
const text = await discord.channel(channelId).text();
|
|
438
|
+
expect(text.slice(text.lastIndexOf('-# *main', text.lastIndexOf('-# *main') - 1))).toMatchInlineSnapshot(`
|
|
439
|
+
"-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
440
|
+
The slow job finished.
|
|
441
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
442
|
+
`);
|
|
443
|
+
expect(workerAtTyping).not.toContain('-# *main');
|
|
444
|
+
});
|
|
445
|
+
test('/toggle-coordinator turns coordinator mode off and on per channel; the choice survives a restart', async () => {
|
|
446
|
+
const { discord, channelId } = twin;
|
|
447
|
+
const root = bot.store.getState().roots[channelId];
|
|
448
|
+
const toggle = async () => {
|
|
449
|
+
const { id: interactionId } = await discord.channel(channelId).user(TEST_USER_ID).runSlashCommand({ name: 'toggle-coordinator' });
|
|
450
|
+
await discord.channel(channelId).waitForInteractionAck({ interactionId, timeout: 4_000 });
|
|
451
|
+
};
|
|
452
|
+
await toggle();
|
|
453
|
+
const threads = new Set((await discord.channel(channelId).getThreads()).map((thread) => thread.id));
|
|
454
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Mode off hello-marker' });
|
|
455
|
+
const thread = await discord.channel(channelId).waitForThread({ timeout: 8_000, predicate: (found) => !threads.has(found.id) });
|
|
456
|
+
await waitForFooter({ discord, threadId: thread.id });
|
|
457
|
+
expect(bot.store.getState().roots[channelId]).toBe(root);
|
|
458
|
+
await toggle();
|
|
459
|
+
// Started with coordinator mode off by default: the channel keeps its choice and its coordinator.
|
|
460
|
+
await bot.stop();
|
|
461
|
+
bot = await startTestBot({ dataDir, twin, server });
|
|
462
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter).length;
|
|
463
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Mode on hello-marker' });
|
|
464
|
+
await waitForFooter({ discord, threadId: channelId, count: footers + 1 });
|
|
465
|
+
expect(bot.store.getState().roots[channelId]).toBe(root);
|
|
466
|
+
const text = await discord.channel(channelId).text();
|
|
467
|
+
expect(text.slice(text.lastIndexOf('Coordinator mode **off**'))).toMatchInlineSnapshot(`
|
|
468
|
+
"Coordinator mode **off** for this channel. Each channel message starts a new thread.
|
|
469
|
+
--- from: user (tommy)
|
|
470
|
+
Mode off hello-marker
|
|
471
|
+
--- from: assistant (TestBot)
|
|
472
|
+
Coordinator mode **on** for this channel. Channel messages go to one long coordinator session that starts threads for the work. It continues the coordinator session this channel had before; /reset-coordinator starts a fresh one.
|
|
473
|
+
--- from: user (tommy)
|
|
474
|
+
Mode on hello-marker
|
|
475
|
+
--- from: assistant (TestBot)
|
|
476
|
+
Hello from the coordinator.
|
|
477
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
478
|
+
`);
|
|
479
|
+
});
|
|
480
|
+
test('the coordinator survives a bot restart; /reset-coordinator starts a fresh one', async () => {
|
|
481
|
+
const { discord, channelId } = twin;
|
|
482
|
+
const root = bot.store.getState().roots[channelId];
|
|
483
|
+
await bot.stop();
|
|
484
|
+
bot = await startTestBot({ dataDir, twin, server, coordinator: true });
|
|
485
|
+
expect(bot.store.getState().roots[channelId]).toBe(root);
|
|
486
|
+
const { id: interactionId } = await discord.channel(channelId).user(TEST_USER_ID).runSlashCommand({ name: 'reset-coordinator' });
|
|
487
|
+
await discord.channel(channelId).waitForInteractionAck({ interactionId, timeout: 4_000 });
|
|
488
|
+
expect(bot.store.getState().roots[channelId]).toBeUndefined();
|
|
489
|
+
const footers = (await discord.channel(channelId).getMessages()).filter(isFooter).length;
|
|
490
|
+
await discord.channel(channelId).user(TEST_USER_ID).sendMessage({ content: 'Fresh start hello-marker' });
|
|
491
|
+
await waitForFooter({ discord, threadId: channelId, count: footers + 1 });
|
|
492
|
+
const fresh = bot.store.getState().roots[channelId];
|
|
493
|
+
expect(fresh && fresh !== root).toBe(true);
|
|
494
|
+
const text = await discord.channel(channelId).text();
|
|
495
|
+
expect(text.slice(text.lastIndexOf('--- from: user'))).toMatchInlineSnapshot(`
|
|
496
|
+
"--- from: user (tommy)
|
|
497
|
+
Fresh start hello-marker
|
|
498
|
+
--- from: assistant (TestBot)
|
|
499
|
+
-# *using deterministic-provider/deterministic-v2 ⋅ build*
|
|
500
|
+
-# Coordinator mode is on: this channel is one session that starts and manages project threads for you. Use /new-session for a thread without it. Turn it off with /toggle-coordinator or start kimaki with \`--coordinator false\`.
|
|
501
|
+
Hello from the coordinator.
|
|
502
|
+
-# *main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
503
|
+
`);
|
|
504
|
+
});
|
|
505
|
+
//# sourceMappingURL=coordinator.e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinator.e2e.test.js","sourceRoot":"","sources":["../src/coordinator.e2e.test.ts"],"names":[],"mappings":"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"}
|