kimaki 0.26.0 → 0.28.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/agent-model.e2e.test.js +250 -164
- package/dist/anthropic-account-identity.js +9 -1
- package/dist/anthropic-auth-plugin.js +10 -13
- package/dist/anthropic-auth-plugin.test.js +17 -0
- package/dist/anthropic-auth-state.js +1 -0
- package/dist/bash-tool-schema-plugin.js +32 -0
- package/dist/bash-tool-schema-plugin.test.js +49 -0
- package/dist/bin.js +73 -19
- package/dist/cli-commands/maintenance.js +46 -7
- package/dist/cli-commands/project.js +11 -27
- package/dist/cli-commands/send.js +30 -1
- package/dist/cli-commands/session.js +322 -90
- package/dist/cli-commands/task.js +34 -15
- package/dist/cli-parsing.test.js +83 -4
- package/dist/cli-runner.js +44 -8
- package/dist/cli-runner.test.js +27 -0
- package/dist/cli-send-thread.e2e.test.js +9 -9
- package/dist/cli.js +32 -0
- package/dist/commands/abort.js +5 -1
- package/dist/commands/action-buttons.js +2 -1
- package/dist/commands/add-project.js +4 -7
- package/dist/commands/agent.js +164 -32
- package/dist/commands/ask-question.js +23 -33
- package/dist/commands/ask-question.test.js +43 -1
- package/dist/commands/btw.js +28 -6
- package/dist/commands/file-upload.js +2 -1
- package/dist/commands/fork-subagent.js +2 -4
- package/dist/commands/fork.js +4 -4
- package/dist/commands/last-sessions.js +17 -10
- package/dist/commands/login.js +6 -0
- package/dist/commands/merge-worktree.js +47 -14
- package/dist/commands/model-variant.js +8 -1
- package/dist/commands/model.js +16 -9
- package/dist/commands/multioauth.js +1 -0
- package/dist/commands/new-worktree.js +170 -102
- package/dist/commands/queue.js +3 -2
- package/dist/commands/resume.js +4 -4
- package/dist/commands/session.js +78 -63
- package/dist/commands/user-command.js +6 -8
- package/dist/commands/worktrees.js +5 -13
- package/dist/config.js +16 -1
- package/dist/cpu-profiler.js +211 -0
- package/dist/cpu-profiler.test.js +160 -0
- package/dist/database.js +152 -3
- package/dist/db.js +33 -0
- package/dist/db.test.js +267 -1
- package/dist/default-channel-provisioning.test.js +148 -0
- package/dist/discord-bot.js +517 -488
- package/dist/discord-command-registration.js +25 -10
- package/dist/discord-command-registration.test.js +17 -0
- package/dist/discord-utils.js +97 -5
- package/dist/discord-utils.test.js +151 -1
- package/dist/errors.js +10 -0
- package/dist/exec-async.js +17 -5
- package/dist/external-directory-permissions.e2e.test.js +11 -7
- package/dist/external-directory-project-deny.e2e.test.js +5 -3
- package/dist/external-opencode-sync.js +94 -17
- package/dist/external-opencode-sync.test.js +206 -0
- package/dist/extract-sdk-error-message.test.js +13 -0
- package/dist/file-edit-log.js +197 -0
- package/dist/file-edit-log.test.js +258 -0
- package/dist/format-tables.js +52 -0
- package/dist/format-tables.test.js +93 -0
- package/dist/gateway-proxy-reconnect.e2e.test.js +28 -8
- package/dist/gateway-proxy.e2e.test.js +18 -18
- package/dist/git-worktree-core.js +182 -17
- package/dist/interaction-handler.js +416 -398
- package/dist/ipc-tools-plugin.js +68 -3
- package/dist/kimaki-opencode-plugin-loading.e2e.test.js +122 -58
- package/dist/kimaki-opencode-plugin.js +9 -3
- package/dist/kimaki-workspace-adaptor.js +33 -10
- package/dist/logger.js +11 -1
- package/dist/markdown.js +3 -1
- package/dist/markdown.test.js +11 -3
- package/dist/message-formatting.js +181 -51
- package/dist/message-formatting.test.js +529 -22
- package/dist/message-preprocessing.js +105 -1
- package/dist/oauth-rotation-shared.js +10 -0
- package/dist/openai-auth-plugin.js +1 -0
- package/dist/openai-auth-state.js +1 -0
- package/dist/opencode-command.js +28 -0
- package/dist/opencode-command.test.js +109 -1
- package/dist/opencode-interrupt-plugin.js +4 -0
- package/dist/opencode-interrupt-plugin.test.js +24 -0
- package/dist/opencode.js +82 -15
- package/dist/project-add.test.js +62 -0
- package/dist/question-abort-clears-dropdown.e2e.test.js +89 -0
- package/dist/question-abort-retry-text-before-dropdown.e2e.test.js +150 -0
- package/dist/question-text-before-dropdown.e2e.test.js +52 -0
- package/dist/queue-advanced-abort.e2e.test.js +8 -10
- package/dist/queue-advanced-action-buttons.e2e.test.js +8 -8
- package/dist/queue-advanced-e2e-setup.js +78 -5
- package/dist/queue-advanced-footer.e2e.test.js +60 -269
- package/dist/queue-advanced-model-switch.e2e.test.js +9 -11
- package/dist/queue-advanced-permissions-typing.e2e.test.js +15 -15
- package/dist/queue-advanced-question.e2e.test.js +32 -27
- package/dist/queue-advanced-typing-interrupt.e2e.test.js +10 -12
- package/dist/queue-advanced-typing.e2e.test.js +7 -9
- package/dist/queue-delete-message.e2e.test.js +3 -3
- package/dist/queue-drain-after-interactive-ui.e2e.test.js +5 -5
- package/dist/queue-interrupt-drain.e2e.test.js +122 -12
- package/dist/queue-question-select-drain.e2e.test.js +34 -132
- package/dist/runtime-lifecycle.e2e.test.js +21 -51
- package/dist/schema.js +35 -1
- package/dist/session-handler/event-stream-state.js +391 -50
- package/dist/session-handler/event-stream-state.test.js +736 -8
- package/dist/session-handler/model-utils.js +172 -5
- package/dist/session-handler/model-utils.test.js +445 -0
- package/dist/session-handler/opencode-session-event-log.js +30 -1
- package/dist/session-handler/thread-ingress.test.js +41 -0
- package/dist/session-handler/thread-runtime-state.js +2 -0
- package/dist/session-handler/thread-session-runtime-summary.test.js +121 -0
- package/dist/session-handler/thread-session-runtime.js +574 -299
- package/dist/session-search.js +123 -4
- package/dist/session-search.test.js +248 -1
- package/dist/session-sleep.test.js +224 -0
- package/dist/session-title-rename.test.js +37 -39
- package/dist/sleep.e2e.test.js +494 -0
- package/dist/startup-time.e2e.test.js +2 -1
- package/dist/store.js +3 -0
- package/dist/system-message.js +102 -16
- package/dist/system-message.test.js +192 -15
- package/dist/task-runner.js +96 -5
- package/dist/task-schedule.js +87 -1
- package/dist/task-schedule.test.js +149 -1
- package/dist/test-utils.js +17 -9
- package/dist/thinking-utils.js +10 -0
- package/dist/thinking-utils.test.js +35 -0
- package/dist/thread-message-queue.e2e.test.js +65 -213
- package/dist/undo-redo.e2e.test.js +17 -9
- package/dist/voice-attachment.js +23 -1
- package/dist/voice-handler.js +21 -4
- package/dist/voice-message.e2e.test.js +144 -30
- package/dist/voice-routing-input.e2e.test.js +195 -0
- package/dist/voice-routing-question.e2e.test.js +125 -0
- package/dist/voice.js +28 -8
- package/dist/voice.test.js +96 -30
- package/dist/worktree-clone-isolation.e2e.test.js +188 -0
- package/dist/worktree-lifecycle.e2e.test.js +55 -36
- package/dist/worktrees.js +226 -443
- package/dist/worktrees.test.js +435 -58
- package/dist/xai-auth-plugin.js +1 -0
- package/dist/xai-auth-state.js +1 -0
- package/package.json +8 -6
- package/skills/bump-holocron-consumers/SKILL.md +137 -0
- package/skills/critique/SKILL.md +4 -4
- package/skills/errore/SKILL.md +60 -218
- package/skills/goke/SKILL.md +150 -10
- package/skills/holocron/SKILL.md +154 -17
- package/skills/npm-package/SKILL.md +55 -35
- package/skills/profano/SKILL.md +17 -2
- package/skills/spiceflow/SKILL.md +39 -3
- package/skills/strada/SKILL.md +39 -0
- package/skills/zele/SKILL.md +19 -0
- package/src/agent-model.e2e.test.ts +287 -186
- package/src/anthropic-account-identity.ts +20 -1
- package/src/anthropic-auth-plugin.test.ts +19 -0
- package/src/anthropic-auth-plugin.ts +12 -16
- package/src/anthropic-auth-state.ts +1 -0
- package/src/bash-tool-schema-plugin.test.ts +55 -0
- package/src/bash-tool-schema-plugin.ts +46 -0
- package/src/bin.ts +78 -26
- package/src/cli-commands/maintenance.ts +59 -13
- package/src/cli-commands/project.ts +14 -35
- package/src/cli-commands/send.ts +31 -1
- package/src/cli-commands/session.ts +374 -114
- package/src/cli-commands/task.ts +34 -15
- package/src/cli-parsing.test.ts +103 -4
- package/src/cli-runner.test.ts +49 -0
- package/src/cli-runner.ts +44 -7
- package/src/cli-send-thread.e2e.test.ts +9 -11
- package/src/cli.ts +53 -0
- package/src/commands/abort.ts +6 -1
- package/src/commands/action-buttons.ts +2 -1
- package/src/commands/add-project.ts +4 -8
- package/src/commands/agent.ts +205 -29
- package/src/commands/ask-question.test.ts +48 -1
- package/src/commands/ask-question.ts +23 -33
- package/src/commands/btw.ts +40 -5
- package/src/commands/file-upload.ts +2 -1
- package/src/commands/fork-subagent.ts +2 -3
- package/src/commands/fork.ts +4 -3
- package/src/commands/last-sessions.ts +10 -4
- package/src/commands/login.ts +6 -0
- package/src/commands/merge-worktree.ts +54 -16
- package/src/commands/model-variant.ts +9 -1
- package/src/commands/model.ts +20 -9
- package/src/commands/multioauth.ts +1 -0
- package/src/commands/new-worktree.ts +138 -55
- package/src/commands/queue.ts +3 -2
- package/src/commands/resume.ts +4 -3
- package/src/commands/session.ts +94 -70
- package/src/commands/user-command.ts +6 -6
- package/src/commands/worktrees.ts +5 -12
- package/src/config.ts +18 -1
- package/src/cpu-profiler.test.ts +183 -0
- package/src/cpu-profiler.ts +293 -0
- package/src/database.ts +235 -3
- package/src/db.test.ts +293 -0
- package/src/db.ts +38 -0
- package/src/default-channel-provisioning.test.ts +155 -0
- package/src/discord-bot.ts +78 -36
- package/src/discord-command-registration.test.ts +20 -0
- package/src/discord-command-registration.ts +42 -12
- package/src/discord-utils.test.ts +176 -0
- package/src/discord-utils.ts +152 -7
- package/src/errors.ts +12 -0
- package/src/exec-async.ts +20 -11
- package/src/external-directory-permissions.e2e.test.ts +11 -7
- package/src/external-directory-project-deny.e2e.test.ts +5 -3
- package/src/external-opencode-sync.test.ts +248 -0
- package/src/external-opencode-sync.ts +122 -19
- package/src/extract-sdk-error-message.test.ts +18 -0
- package/src/file-edit-log.test.ts +287 -0
- package/src/file-edit-log.ts +283 -0
- package/src/format-tables.test.ts +101 -0
- package/src/format-tables.ts +75 -0
- package/src/gateway-proxy-reconnect.e2e.test.ts +38 -8
- package/src/gateway-proxy.e2e.test.ts +18 -17
- package/src/git-worktree-core.ts +247 -20
- package/src/interaction-handler.ts +36 -13
- package/src/ipc-tools-plugin.ts +82 -5
- package/src/kimaki-opencode-plugin-loading.e2e.test.ts +188 -97
- package/src/kimaki-opencode-plugin.ts +9 -3
- package/src/kimaki-workspace-adaptor.ts +40 -11
- package/src/logger.ts +14 -4
- package/src/markdown.test.ts +13 -3
- package/src/markdown.ts +4 -1
- package/src/message-formatting.test.ts +672 -25
- package/src/message-formatting.ts +242 -53
- package/src/message-preprocessing.ts +116 -1
- package/src/oauth-rotation-shared.ts +10 -0
- package/src/openai-auth-plugin.ts +1 -0
- package/src/openai-auth-state.ts +1 -0
- package/src/opencode-command.test.ts +140 -0
- package/src/opencode-command.ts +38 -0
- package/src/opencode-interrupt-plugin.test.ts +32 -0
- package/src/opencode-interrupt-plugin.ts +3 -0
- package/src/opencode.ts +115 -15
- package/src/project-add.test.ts +80 -0
- package/src/question-abort-clears-dropdown.e2e.test.ts +109 -0
- package/src/question-abort-retry-text-before-dropdown.e2e.test.ts +172 -0
- package/src/question-text-before-dropdown.e2e.test.ts +67 -0
- package/src/queue-advanced-abort.e2e.test.ts +8 -9
- package/src/queue-advanced-action-buttons.e2e.test.ts +8 -8
- package/src/queue-advanced-e2e-setup.ts +83 -4
- package/src/queue-advanced-footer.e2e.test.ts +67 -315
- package/src/queue-advanced-model-switch.e2e.test.ts +9 -10
- package/src/queue-advanced-permissions-typing.e2e.test.ts +15 -15
- package/src/queue-advanced-question.e2e.test.ts +36 -30
- package/src/queue-advanced-typing-interrupt.e2e.test.ts +11 -11
- package/src/queue-advanced-typing.e2e.test.ts +8 -8
- package/src/queue-delete-message.e2e.test.ts +3 -3
- package/src/queue-drain-after-interactive-ui.e2e.test.ts +5 -5
- package/src/queue-interrupt-drain.e2e.test.ts +133 -12
- package/src/queue-question-select-drain.e2e.test.ts +36 -161
- package/src/runtime-lifecycle.e2e.test.ts +21 -54
- package/src/schema.sql +15 -2
- package/src/schema.ts +37 -1
- package/src/session-handler/event-stream-state.test.ts +790 -7
- package/src/session-handler/event-stream-state.ts +568 -53
- package/src/session-handler/model-utils.test.ts +537 -0
- package/src/session-handler/model-utils.ts +281 -5
- package/src/session-handler/opencode-session-event-log.ts +31 -1
- package/src/session-handler/thread-ingress.test.ts +54 -0
- package/src/session-handler/thread-runtime-state.ts +3 -0
- package/src/session-handler/thread-session-runtime-summary.test.ts +169 -0
- package/src/session-handler/thread-session-runtime.ts +712 -358
- package/src/session-search.test.ts +281 -0
- package/src/session-search.ts +241 -7
- package/src/session-sleep.test.ts +294 -0
- package/src/session-title-rename.test.ts +54 -48
- package/src/sleep.e2e.test.ts +565 -0
- package/src/startup-time.e2e.test.ts +5 -1
- package/src/store.ts +22 -0
- package/src/system-message.test.ts +226 -15
- package/src/system-message.ts +130 -16
- package/src/task-runner.ts +138 -3
- package/src/task-schedule.test.ts +170 -0
- package/src/task-schedule.ts +141 -1
- package/src/test-utils.ts +21 -10
- package/src/thinking-utils.test.ts +46 -0
- package/src/thinking-utils.ts +21 -0
- package/src/thread-message-queue.e2e.test.ts +65 -245
- package/src/undo-redo.e2e.test.ts +17 -9
- package/src/voice-attachment.ts +29 -1
- package/src/voice-handler.ts +23 -4
- package/src/voice-message.e2e.test.ts +149 -29
- package/src/voice-routing-input.e2e.test.ts +203 -0
- package/src/voice-routing-question.e2e.test.ts +131 -0
- package/src/voice.test.ts +107 -29
- package/src/voice.ts +34 -6
- package/src/worktree-clone-isolation.e2e.test.ts +213 -0
- package/src/worktree-lifecycle.e2e.test.ts +55 -35
- package/src/worktrees.test.ts +505 -64
- package/src/worktrees.ts +257 -603
- package/src/xai-auth-plugin.ts +1 -0
- package/src/xai-auth-state.ts +1 -0
- package/dist/message-finish-field.e2e.test.js +0 -169
- package/dist/subagent-rate-limit-plugin.js +0 -184
- package/src/message-finish-field.e2e.test.ts +0 -196
- package/src/subagent-rate-limit-plugin.ts +0 -228
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Split from queue-advanced-typing.e2e.test.ts for parallelization.
|
|
3
3
|
import { describe, test, expect } from 'vitest';
|
|
4
4
|
import { setupQueueAdvancedSuite, TEST_USER_ID, } from './queue-advanced-e2e-setup.js';
|
|
5
|
-
import { waitForBotMessageContaining, waitForFooterMessage, } from './test-utils.js';
|
|
5
|
+
import { getMessageVisibleText, isFooterMessage, waitForBotMessageContaining, waitForFooterMessage, } from './test-utils.js';
|
|
6
6
|
const TEXT_CHANNEL_ID = '200000000000001008';
|
|
7
7
|
const e2eTest = describe;
|
|
8
8
|
e2eTest('queue advanced: typing interrupt', () => {
|
|
@@ -68,32 +68,30 @@ e2eTest('queue advanced: typing interrupt', () => {
|
|
|
68
68
|
if (index <= finalUserIndex) {
|
|
69
69
|
return false;
|
|
70
70
|
}
|
|
71
|
-
return message.author.id === ctx.discord.botUserId && message.
|
|
71
|
+
return message.author.id === ctx.discord.botUserId && getMessageVisibleText(message).includes('ok');
|
|
72
72
|
});
|
|
73
73
|
const finalFooterIndex = messages.findIndex((message, index) => {
|
|
74
74
|
if (index <= finalReplyIndex) {
|
|
75
75
|
return false;
|
|
76
76
|
}
|
|
77
|
-
return message
|
|
78
|
-
&& message.content.startsWith('*')
|
|
79
|
-
&& message.content.includes('⋅');
|
|
77
|
+
return isFooterMessage({ message, botUserId: ctx.discord.botUserId });
|
|
80
78
|
});
|
|
81
79
|
expect(await th.text()).toMatchInlineSnapshot(`
|
|
82
80
|
"--- from: user (queue-advanced-tester)
|
|
83
81
|
Reply with exactly: typing-stop-interrupt-setup
|
|
84
82
|
--- from: assistant (TestBot)
|
|
85
|
-
*using deterministic-provider/deterministic-v2*
|
|
86
|
-
|
|
87
|
-
*project ⋅ main ⋅
|
|
83
|
+
> *using deterministic-provider/deterministic-v2*
|
|
84
|
+
> ok
|
|
85
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>
|
|
88
86
|
--- from: user (queue-advanced-tester)
|
|
89
87
|
PLUGIN_TIMEOUT_SLEEP_MARKER
|
|
90
88
|
--- from: assistant (TestBot)
|
|
91
|
-
|
|
89
|
+
> starting sleep 100
|
|
92
90
|
--- from: user (queue-advanced-tester)
|
|
93
91
|
Reply with exactly: typing-stop-interrupt-final
|
|
94
92
|
--- from: assistant (TestBot)
|
|
95
|
-
|
|
96
|
-
*project ⋅ main ⋅
|
|
93
|
+
> ok
|
|
94
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
97
95
|
`);
|
|
98
96
|
const timeline = await th.text({ showTyping: true });
|
|
99
97
|
expect(finalUserIndex).toBeGreaterThanOrEqual(0);
|
|
@@ -101,7 +99,7 @@ e2eTest('queue advanced: typing interrupt', () => {
|
|
|
101
99
|
expect(finalFooterIndex).toBeGreaterThan(finalReplyIndex);
|
|
102
100
|
expect(messages[finalFooterIndex]).toBeDefined();
|
|
103
101
|
const finalPromptPosition = timeline.indexOf('Reply with exactly: typing-stop-interrupt-final');
|
|
104
|
-
const finalReplyPosition = timeline.indexOf('--- from: assistant (TestBot)\n
|
|
102
|
+
const finalReplyPosition = timeline.indexOf('--- from: assistant (TestBot)\n> ok', finalPromptPosition);
|
|
105
103
|
const lastFooterPosition = timeline.lastIndexOf('*project ⋅');
|
|
106
104
|
expect(finalPromptPosition).toBeGreaterThanOrEqual(0);
|
|
107
105
|
expect(finalReplyPosition).toBeGreaterThan(finalPromptPosition);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Split from thread-queue-advanced.e2e.test.ts for parallelization.
|
|
3
3
|
import { describe, test, expect } from 'vitest';
|
|
4
4
|
import { setupQueueAdvancedSuite, TEST_USER_ID, } from './queue-advanced-e2e-setup.js';
|
|
5
|
-
import { waitForBotMessageContaining, waitForFooterMessage, } from './test-utils.js';
|
|
5
|
+
import { getMessageVisibleText, isFooterMessage, waitForBotMessageContaining, waitForFooterMessage, } from './test-utils.js';
|
|
6
6
|
const TEXT_CHANNEL_ID = '200000000000001002';
|
|
7
7
|
const e2eTest = describe;
|
|
8
8
|
e2eTest('queue advanced: typing lifecycle', () => {
|
|
@@ -41,19 +41,17 @@ e2eTest('queue advanced: typing lifecycle', () => {
|
|
|
41
41
|
afterAuthorId: ctx.discord.botUserId,
|
|
42
42
|
});
|
|
43
43
|
const replyIndex = messages.findIndex((message) => {
|
|
44
|
-
return message.author.id === ctx.discord.botUserId && message.
|
|
44
|
+
return message.author.id === ctx.discord.botUserId && getMessageVisibleText(message).includes('ok');
|
|
45
45
|
});
|
|
46
46
|
const footerIndex = messages.findIndex((message, index) => {
|
|
47
47
|
if (index <= replyIndex) {
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
|
-
return message
|
|
51
|
-
&& message.content.startsWith('*')
|
|
52
|
-
&& message.content.includes('⋅');
|
|
50
|
+
return isFooterMessage({ message, botUserId: ctx.discord.botUserId });
|
|
53
51
|
});
|
|
54
52
|
const timeline = await th.text({ showTyping: true });
|
|
55
53
|
expect(timeline).toContain('Reply with exactly: typing-stop-normal');
|
|
56
|
-
expect(timeline).toContain('
|
|
54
|
+
expect(timeline).toContain('ok');
|
|
57
55
|
expect(timeline).toContain('*project ⋅ main ⋅');
|
|
58
56
|
const typingCount = (timeline.match(/\[bot typing\]/g) || []).length;
|
|
59
57
|
expect(typingCount).toBeGreaterThanOrEqual(1);
|
|
@@ -110,7 +108,7 @@ e2eTest('queue advanced: typing lifecycle', () => {
|
|
|
110
108
|
return false;
|
|
111
109
|
}
|
|
112
110
|
return message.author.id === ctx.discord.botUserId
|
|
113
|
-
&& message.
|
|
111
|
+
&& getMessageVisibleText(message).includes('repulse-first');
|
|
114
112
|
});
|
|
115
113
|
if (!firstReply) {
|
|
116
114
|
throw new Error('Expected first bot reply after TYPING_REPULSE_MARKER');
|
|
@@ -132,7 +130,7 @@ e2eTest('queue advanced: typing lifecycle', () => {
|
|
|
132
130
|
});
|
|
133
131
|
const timeline = await th.text({ showTyping: true });
|
|
134
132
|
expect(timeline).toContain('TYPING_REPULSE_MARKER');
|
|
135
|
-
expect(timeline).toContain('
|
|
133
|
+
expect(timeline).toContain('repulse-first');
|
|
136
134
|
const typingCount = (timeline.match(/\[bot typing\]/g) || []).length;
|
|
137
135
|
expect(typingCount).toBeGreaterThanOrEqual(2);
|
|
138
136
|
const followupUserIndex = messages.findIndex((message) => {
|
|
@@ -144,7 +142,7 @@ e2eTest('queue advanced: typing lifecycle', () => {
|
|
|
144
142
|
return false;
|
|
145
143
|
}
|
|
146
144
|
return message.author.id === ctx.discord.botUserId
|
|
147
|
-
&& message.
|
|
145
|
+
&& getMessageVisibleText(message).includes('repulse-first');
|
|
148
146
|
});
|
|
149
147
|
expect(followupUserIndex).toBeGreaterThanOrEqual(0);
|
|
150
148
|
expect(followupReplyIndex).toBeGreaterThan(followupUserIndex);
|
|
@@ -63,11 +63,11 @@ e2eTest('queue delete message', () => {
|
|
|
63
63
|
"--- from: user (queue-delete-tester)
|
|
64
64
|
SLOW_BUSY_MARKER Reply with exactly: delete-queue-setup
|
|
65
65
|
--- from: assistant (TestBot)
|
|
66
|
-
*using deterministic-provider/deterministic-v2*
|
|
66
|
+
> *using deterministic-provider/deterministic-v2*
|
|
67
67
|
Queued at position 1. Edit or delete your message to update the queue
|
|
68
68
|
⬦ **queue-delete-tester** removed message from queue
|
|
69
|
-
|
|
70
|
-
*project ⋅ main ⋅
|
|
69
|
+
> slow-busy-reply
|
|
70
|
+
> *project ⋅ main ⋅ 2s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
71
71
|
`);
|
|
72
72
|
const finalText = await th.text();
|
|
73
73
|
expect(finalText).not.toContain('» **queue-delete-tester:** Reply with exactly: deleted-queue');
|
|
@@ -107,14 +107,14 @@ describe('queue drain with pending interactive UI', () => {
|
|
|
107
107
|
"--- from: user (drain-ui-tester)
|
|
108
108
|
Reply with exactly: drain-button-setup
|
|
109
109
|
--- from: assistant (TestBot)
|
|
110
|
-
*using deterministic-provider/deterministic-v2*
|
|
111
|
-
|
|
112
|
-
*project ⋅ main ⋅
|
|
110
|
+
> *using deterministic-provider/deterministic-v2*
|
|
111
|
+
> ok
|
|
112
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>
|
|
113
113
|
**Action Required**
|
|
114
114
|
[user interaction]
|
|
115
115
|
» **drain-ui-tester:** Reply with exactly: post-button-drain
|
|
116
|
-
|
|
117
|
-
*project ⋅ main ⋅
|
|
116
|
+
> ok
|
|
117
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
118
118
|
`);
|
|
119
119
|
}, 20_000);
|
|
120
120
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// 2. User queues a message via /queue (enters kimaki local queue)
|
|
8
8
|
// 3. User sends a normal message (interrupt)
|
|
9
9
|
// 4. Session aborts the slow task, processes the interrupt message immediately
|
|
10
|
-
// 5. Interrupt response appears in Discord with a
|
|
10
|
+
// 5. Interrupt response appears in Discord with a ok reply
|
|
11
11
|
// 6. When interrupt response completes, the queued message drains and runs
|
|
12
12
|
//
|
|
13
13
|
// Uses opencode-deterministic-provider (no real LLM calls).
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
import { describe, test, expect } from 'vitest';
|
|
16
16
|
import { setupQueueAdvancedSuite, TEST_USER_ID, } from './queue-advanced-e2e-setup.js';
|
|
17
17
|
import { waitForFooterMessage, waitForBotMessageContaining, waitForMessageById, } from './test-utils.js';
|
|
18
|
+
import { getThreadSession } from './database.js';
|
|
19
|
+
import { archiveThread } from './discord-utils.js';
|
|
20
|
+
import { getOpencodeClient } from './opencode.js';
|
|
18
21
|
const TEXT_CHANNEL_ID = '200000000000001099';
|
|
19
22
|
const e2eTest = describe;
|
|
20
23
|
e2eTest('queue + interrupt drain ordering', () => {
|
|
@@ -24,6 +27,113 @@ e2eTest('queue + interrupt drain ordering', () => {
|
|
|
24
27
|
dirName: 'qa-interrupt-drain-e2e',
|
|
25
28
|
username: 'interrupt-tester',
|
|
26
29
|
});
|
|
30
|
+
test('archiving keeps the active run alive so queued messages drain', async () => {
|
|
31
|
+
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
32
|
+
content: 'Reply with exactly: setup-archive-drain',
|
|
33
|
+
});
|
|
34
|
+
const thread = await ctx.discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
35
|
+
timeout: 4_000,
|
|
36
|
+
predicate: (candidate) => {
|
|
37
|
+
return candidate.name === 'Reply with exactly: setup-archive-drain';
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
const th = ctx.discord.thread(thread.id);
|
|
41
|
+
await th.waitForBotReply({ timeout: 4_000 });
|
|
42
|
+
await waitForFooterMessage({
|
|
43
|
+
discord: ctx.discord,
|
|
44
|
+
threadId: thread.id,
|
|
45
|
+
timeout: 4_000,
|
|
46
|
+
});
|
|
47
|
+
await th.user(TEST_USER_ID).sendMessage({
|
|
48
|
+
content: 'PLUGIN_TIMEOUT_SLEEP_MARKER archive queue drain',
|
|
49
|
+
});
|
|
50
|
+
await waitForBotMessageContaining({
|
|
51
|
+
discord: ctx.discord,
|
|
52
|
+
threadId: thread.id,
|
|
53
|
+
userId: TEST_USER_ID,
|
|
54
|
+
text: 'starting sleep',
|
|
55
|
+
afterUserMessageIncludes: 'PLUGIN_TIMEOUT_SLEEP_MARKER archive queue drain',
|
|
56
|
+
timeout: 4_000,
|
|
57
|
+
});
|
|
58
|
+
const { id: queueInteractionId } = await th.user(TEST_USER_ID)
|
|
59
|
+
.runSlashCommand({
|
|
60
|
+
name: 'queue',
|
|
61
|
+
options: [{
|
|
62
|
+
name: 'message',
|
|
63
|
+
type: 3,
|
|
64
|
+
value: 'Reply with exactly: archived-queue-survives',
|
|
65
|
+
}],
|
|
66
|
+
});
|
|
67
|
+
const queueAck = await th.waitForInteractionAck({
|
|
68
|
+
interactionId: queueInteractionId,
|
|
69
|
+
timeout: 4_000,
|
|
70
|
+
});
|
|
71
|
+
if (!queueAck.messageId) {
|
|
72
|
+
throw new Error('Expected /queue response message id');
|
|
73
|
+
}
|
|
74
|
+
const queueStatusMessage = await waitForMessageById({
|
|
75
|
+
discord: ctx.discord,
|
|
76
|
+
threadId: thread.id,
|
|
77
|
+
messageId: queueAck.messageId,
|
|
78
|
+
timeout: 4_000,
|
|
79
|
+
});
|
|
80
|
+
expect(queueStatusMessage.content).toContain('Queued message');
|
|
81
|
+
const sessionId = await getThreadSession(thread.id);
|
|
82
|
+
if (!sessionId)
|
|
83
|
+
throw new Error('Expected mapped OpenCode session');
|
|
84
|
+
const client = getOpencodeClient(ctx.directories.projectDirectory);
|
|
85
|
+
if (!client)
|
|
86
|
+
throw new Error('Expected initialized OpenCode client');
|
|
87
|
+
await archiveThread({
|
|
88
|
+
rest: ctx.botClient.rest,
|
|
89
|
+
threadId: thread.id,
|
|
90
|
+
parentChannelId: TEXT_CHANNEL_ID,
|
|
91
|
+
sessionId,
|
|
92
|
+
client,
|
|
93
|
+
});
|
|
94
|
+
const statusResponse = await client.session.status({
|
|
95
|
+
directory: ctx.directories.projectDirectory,
|
|
96
|
+
});
|
|
97
|
+
expect(statusResponse.data?.[sessionId]?.type).toBe('busy');
|
|
98
|
+
await th.user(TEST_USER_ID).sendMessage({
|
|
99
|
+
content: 'Reply with exactly: continue-after-archive',
|
|
100
|
+
});
|
|
101
|
+
await waitForFooterMessage({
|
|
102
|
+
discord: ctx.discord,
|
|
103
|
+
threadId: thread.id,
|
|
104
|
+
timeout: 12_000,
|
|
105
|
+
afterMessageIncludes: 'continue-after-archive',
|
|
106
|
+
afterAuthorId: TEST_USER_ID,
|
|
107
|
+
});
|
|
108
|
+
await waitForFooterMessage({
|
|
109
|
+
discord: ctx.discord,
|
|
110
|
+
threadId: thread.id,
|
|
111
|
+
timeout: 12_000,
|
|
112
|
+
afterMessageIncludes: 'archived-queue-survives',
|
|
113
|
+
afterAuthorId: ctx.discord.botUserId,
|
|
114
|
+
});
|
|
115
|
+
expect(await th.text()).toMatchInlineSnapshot(`
|
|
116
|
+
"--- from: user (interrupt-tester)
|
|
117
|
+
Reply with exactly: setup-archive-drain
|
|
118
|
+
--- from: assistant (TestBot)
|
|
119
|
+
> *using deterministic-provider/deterministic-v2*
|
|
120
|
+
> ok
|
|
121
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>
|
|
122
|
+
--- from: user (interrupt-tester)
|
|
123
|
+
PLUGIN_TIMEOUT_SLEEP_MARKER archive queue drain
|
|
124
|
+
--- from: assistant (TestBot)
|
|
125
|
+
> starting sleep 100
|
|
126
|
+
Queued message (position 1)
|
|
127
|
+
--- from: user (interrupt-tester)
|
|
128
|
+
Reply with exactly: continue-after-archive
|
|
129
|
+
--- from: assistant (TestBot)
|
|
130
|
+
> ok
|
|
131
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2*
|
|
132
|
+
» **interrupt-tester:** Reply with exactly: archived-queue-survives
|
|
133
|
+
> ok
|
|
134
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
135
|
+
`);
|
|
136
|
+
}, 20_000);
|
|
27
137
|
test('queued message via /queue + normal interrupt: interrupt reply should appear, then queue drains', async () => {
|
|
28
138
|
// 1. Establish session with a quick first message
|
|
29
139
|
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
@@ -84,7 +194,7 @@ e2eTest('queue + interrupt drain ordering', () => {
|
|
|
84
194
|
content: 'Reply with exactly: interrupt-now',
|
|
85
195
|
});
|
|
86
196
|
// 5. Wait for the final state: the interrupt message should get its own
|
|
87
|
-
//
|
|
197
|
+
// ok reply, then the queued message should drain and get processed.
|
|
88
198
|
// We wait for the queued message's footer as the final signal.
|
|
89
199
|
await waitForFooterMessage({
|
|
90
200
|
discord: ctx.discord,
|
|
@@ -98,24 +208,24 @@ e2eTest('queue + interrupt drain ordering', () => {
|
|
|
98
208
|
"--- from: user (interrupt-tester)
|
|
99
209
|
Reply with exactly: setup-interrupt-drain
|
|
100
210
|
--- from: assistant (TestBot)
|
|
101
|
-
*using deterministic-provider/deterministic-v2*
|
|
102
|
-
|
|
103
|
-
*project ⋅ main ⋅
|
|
211
|
+
> *using deterministic-provider/deterministic-v2*
|
|
212
|
+
> ok
|
|
213
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>
|
|
104
214
|
--- from: user (interrupt-tester)
|
|
105
215
|
PLUGIN_TIMEOUT_SLEEP_MARKER
|
|
106
216
|
--- from: assistant (TestBot)
|
|
107
|
-
|
|
217
|
+
> starting sleep 100
|
|
108
218
|
Queued message (position 1)
|
|
109
219
|
--- from: user (interrupt-tester)
|
|
110
220
|
Reply with exactly: interrupt-now
|
|
111
221
|
--- from: assistant (TestBot)
|
|
112
|
-
|
|
113
|
-
*project ⋅ main ⋅
|
|
222
|
+
> ok
|
|
223
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2*
|
|
114
224
|
» **interrupt-tester:** Reply with exactly: queued-behind-slow
|
|
115
|
-
|
|
116
|
-
*project ⋅ main ⋅
|
|
225
|
+
> ok
|
|
226
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
117
227
|
`);
|
|
118
|
-
// 7. Assert the interrupt message got its own
|
|
228
|
+
// 7. Assert the interrupt message got its own ok reply between the
|
|
119
229
|
// user's interrupt message and the queue dispatch indicator.
|
|
120
230
|
const text = await th.text();
|
|
121
231
|
const lines = text.split('\n');
|
|
@@ -129,7 +239,7 @@ e2eTest('queue + interrupt drain ordering', () => {
|
|
|
129
239
|
expect(queueDispatchLine).toBeGreaterThan(-1);
|
|
130
240
|
const linesBetween = lines.slice(interruptUserLine + 1, queueDispatchLine);
|
|
131
241
|
const hasInterruptReply = linesBetween.some((line) => {
|
|
132
|
-
return line.includes('
|
|
242
|
+
return line.includes('ok');
|
|
133
243
|
});
|
|
134
244
|
expect(hasInterruptReply).toBe(true);
|
|
135
245
|
}, 20_000);
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
// E2e test
|
|
2
|
-
// via the Discord dropdown select menu. Reproduces a bug where answering via
|
|
3
|
-
// select (not text) leaves queued messages stuck because the session continues
|
|
4
|
-
// processing after the answer and may enter another blocking state.
|
|
1
|
+
// E2e test for ordered queue drain after a pending question is answered via select.
|
|
5
2
|
import { describe, test, expect } from 'vitest';
|
|
6
3
|
import { setupQueueAdvancedSuite, TEST_USER_ID, } from './queue-advanced-e2e-setup.js';
|
|
7
4
|
import { waitForBotMessageContaining, waitForFooterMessage, } from './test-utils.js';
|
|
@@ -27,8 +24,7 @@ async function expectNoBotMessageContaining({ discord, threadId, text, timeout,
|
|
|
27
24
|
while (Date.now() - start < timeout) {
|
|
28
25
|
const messages = await discord.thread(threadId).getMessages();
|
|
29
26
|
const match = messages.find((message) => {
|
|
30
|
-
return
|
|
31
|
-
&& message.content.includes(text));
|
|
27
|
+
return message.author.id === discord.botUserId && message.content.includes(text);
|
|
32
28
|
});
|
|
33
29
|
if (match) {
|
|
34
30
|
throw new Error(`Unexpected bot message containing ${JSON.stringify(text)} while it should still be queued`);
|
|
@@ -45,110 +41,15 @@ describe('queue drain after question select answer', () => {
|
|
|
45
41
|
dirName: 'qa-question-select-drain',
|
|
46
42
|
username: 'question-select-tester',
|
|
47
43
|
});
|
|
48
|
-
test('queued
|
|
49
|
-
|
|
50
|
-
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
51
|
-
content: 'QUESTION_SELECT_QUEUE_MARKER',
|
|
52
|
-
});
|
|
53
|
-
const thread = await ctx.discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
54
|
-
timeout: 8_000,
|
|
55
|
-
predicate: (t) => {
|
|
56
|
-
return t.name === 'QUESTION_SELECT_QUEUE_MARKER';
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
const th = ctx.discord.thread(thread.id);
|
|
60
|
-
// 2. Wait for the question dropdown message to appear in Discord.
|
|
61
|
-
// Uses visible message wait instead of internal Map polling which
|
|
62
|
-
// is too timing-sensitive on CI.
|
|
63
|
-
const questionMessages = await waitForBotMessageContaining({
|
|
64
|
-
discord: ctx.discord,
|
|
65
|
-
threadId: thread.id,
|
|
66
|
-
text: 'How to proceed?',
|
|
67
|
-
timeout: 12_000,
|
|
68
|
-
});
|
|
69
|
-
// Get the pending question context hash from the internal map.
|
|
70
|
-
// By this point the question message is visible so the context must exist.
|
|
71
|
-
const pending = await waitForPendingQuestion({
|
|
72
|
-
threadId: thread.id,
|
|
73
|
-
timeoutMs: 8_000,
|
|
74
|
-
});
|
|
75
|
-
const questionMsg = questionMessages.find((m) => {
|
|
76
|
-
return m.content.includes('How to proceed?');
|
|
77
|
-
});
|
|
78
|
-
expect(questionMsg).toBeTruthy();
|
|
79
|
-
// 3. Queue a message while question is pending
|
|
80
|
-
const { id: queueInteractionId } = await th.user(TEST_USER_ID)
|
|
81
|
-
.runSlashCommand({
|
|
82
|
-
name: 'queue',
|
|
83
|
-
options: [{ name: 'message', type: 3, value: 'Reply with exactly: post-question-drain' }],
|
|
84
|
-
});
|
|
85
|
-
const queueAck = await th.waitForInteractionAck({
|
|
86
|
-
interactionId: queueInteractionId,
|
|
87
|
-
timeout: 8_000,
|
|
88
|
-
});
|
|
89
|
-
if (!queueAck.messageId) {
|
|
90
|
-
throw new Error('Expected /queue response message id');
|
|
91
|
-
}
|
|
92
|
-
// 4. The first queued item should be handed off immediately even while
|
|
93
|
-
// the question is still pending, so the visible dispatch indicator
|
|
94
|
-
// appears before the user answers the dropdown.
|
|
95
|
-
await waitForBotMessageContaining({
|
|
96
|
-
discord: ctx.discord,
|
|
97
|
-
threadId: thread.id,
|
|
98
|
-
text: '» **question-select-tester:** Reply with exactly: post-question-drain',
|
|
99
|
-
timeout: 8_000,
|
|
100
|
-
});
|
|
101
|
-
// 5. Answer the question via dropdown select (pick first option "Alpha")
|
|
102
|
-
const interaction = await th.user(TEST_USER_ID).selectMenu({
|
|
103
|
-
messageId: questionMsg.id,
|
|
104
|
-
customId: `ask_question:${pending.contextHash}:0`,
|
|
105
|
-
values: ['0'],
|
|
106
|
-
});
|
|
107
|
-
await th.waitForInteractionAck({
|
|
108
|
-
interactionId: interaction.id,
|
|
109
|
-
timeout: 8_000,
|
|
110
|
-
});
|
|
111
|
-
// 6. Wait for footer from the drained queued message
|
|
112
|
-
await waitForFooterMessage({
|
|
113
|
-
discord: ctx.discord,
|
|
114
|
-
threadId: thread.id,
|
|
115
|
-
timeout: 8_000,
|
|
116
|
-
afterMessageIncludes: '» **question-select-tester:**',
|
|
117
|
-
afterAuthorId: ctx.discord.botUserId,
|
|
118
|
-
});
|
|
119
|
-
const timeline = await th.text({ showInteractions: true });
|
|
120
|
-
expect(timeline).toMatchInlineSnapshot(`
|
|
121
|
-
"--- from: user (question-select-tester)
|
|
122
|
-
QUESTION_SELECT_QUEUE_MARKER
|
|
123
|
-
--- from: assistant (TestBot)
|
|
124
|
-
*using deterministic-provider/deterministic-v2*
|
|
125
|
-
**Select action**
|
|
126
|
-
How to proceed?
|
|
127
|
-
✓ _Alpha_
|
|
128
|
-
[user interaction]
|
|
129
|
-
» **question-select-tester:** Reply with exactly: post-question-drain
|
|
130
|
-
Queued message (position 1)
|
|
131
|
-
[user selects dropdown: 0]
|
|
132
|
-
» **question-select-tester:** Alpha
|
|
133
|
-
⬥ ok
|
|
134
|
-
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
135
|
-
`);
|
|
136
|
-
expect(timeline).toContain('QUESTION_SELECT_QUEUE_MARKER');
|
|
137
|
-
expect(timeline).toContain('How to proceed?');
|
|
138
|
-
expect(timeline).toContain('[user selects dropdown: 0]');
|
|
139
|
-
expect(timeline).toContain('» **question-select-tester:** Reply with exactly: post-question-drain');
|
|
140
|
-
expect(timeline).toContain('⬥ ok');
|
|
141
|
-
expect(timeline).toContain('*project ⋅ main ⋅');
|
|
142
|
-
}, 20_000);
|
|
143
|
-
test('only the first queued message is handed off after dropdown answer', async () => {
|
|
144
|
-
const marker = 'QUESTION_SELECT_QUEUE_MARKER second-test';
|
|
44
|
+
test('queued messages drain in order after answering via dropdown select', async () => {
|
|
45
|
+
const marker = 'QUESTION_SELECT_QUEUE_MARKER';
|
|
145
46
|
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
146
47
|
content: marker,
|
|
147
48
|
});
|
|
148
49
|
const thread = await ctx.discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
149
50
|
timeout: 8_000,
|
|
150
|
-
predicate: (
|
|
151
|
-
return
|
|
51
|
+
predicate: (candidate) => {
|
|
52
|
+
return candidate.name === marker;
|
|
152
53
|
},
|
|
153
54
|
});
|
|
154
55
|
const th = ctx.discord.thread(thread.id);
|
|
@@ -162,14 +63,13 @@ describe('queue drain after question select answer', () => {
|
|
|
162
63
|
threadId: thread.id,
|
|
163
64
|
timeoutMs: 8_000,
|
|
164
65
|
});
|
|
165
|
-
const
|
|
66
|
+
const questionMessage = questionMessages.find((message) => {
|
|
166
67
|
return message.content.includes('How to proceed?');
|
|
167
68
|
});
|
|
168
|
-
|
|
169
|
-
if (!questionMsg) {
|
|
69
|
+
if (!questionMessage) {
|
|
170
70
|
throw new Error('Expected question message');
|
|
171
71
|
}
|
|
172
|
-
const firstQueuedPrompt = '
|
|
72
|
+
const firstQueuedPrompt = 'QUESTION_SELECT_DRAIN_FIRST_MARKER';
|
|
173
73
|
const secondQueuedPrompt = 'Reply with exactly: post-question-second';
|
|
174
74
|
const { id: firstQueueInteractionId } = await th.user(TEST_USER_ID)
|
|
175
75
|
.runSlashCommand({
|
|
@@ -180,6 +80,12 @@ describe('queue drain after question select answer', () => {
|
|
|
180
80
|
interactionId: firstQueueInteractionId,
|
|
181
81
|
timeout: 8_000,
|
|
182
82
|
});
|
|
83
|
+
await waitForBotMessageContaining({
|
|
84
|
+
discord: ctx.discord,
|
|
85
|
+
threadId: thread.id,
|
|
86
|
+
text: `» **question-select-tester:** ${firstQueuedPrompt}`,
|
|
87
|
+
timeout: 8_000,
|
|
88
|
+
});
|
|
183
89
|
const { id: secondQueueInteractionId } = await th.user(TEST_USER_ID)
|
|
184
90
|
.runSlashCommand({
|
|
185
91
|
name: 'queue',
|
|
@@ -189,8 +95,14 @@ describe('queue drain after question select answer', () => {
|
|
|
189
95
|
interactionId: secondQueueInteractionId,
|
|
190
96
|
timeout: 8_000,
|
|
191
97
|
});
|
|
98
|
+
await expectNoBotMessageContaining({
|
|
99
|
+
discord: ctx.discord,
|
|
100
|
+
threadId: thread.id,
|
|
101
|
+
text: `» **question-select-tester:** ${secondQueuedPrompt}`,
|
|
102
|
+
timeout: 200,
|
|
103
|
+
});
|
|
192
104
|
const interaction = await th.user(TEST_USER_ID).selectMenu({
|
|
193
|
-
messageId:
|
|
105
|
+
messageId: questionMessage.id,
|
|
194
106
|
customId: `ask_question:${pending.contextHash}:0`,
|
|
195
107
|
values: ['0'],
|
|
196
108
|
});
|
|
@@ -198,18 +110,6 @@ describe('queue drain after question select answer', () => {
|
|
|
198
110
|
interactionId: interaction.id,
|
|
199
111
|
timeout: 8_000,
|
|
200
112
|
});
|
|
201
|
-
await waitForBotMessageContaining({
|
|
202
|
-
discord: ctx.discord,
|
|
203
|
-
threadId: thread.id,
|
|
204
|
-
text: `» **question-select-tester:** ${firstQueuedPrompt}`,
|
|
205
|
-
timeout: 8_000,
|
|
206
|
-
});
|
|
207
|
-
await expectNoBotMessageContaining({
|
|
208
|
-
discord: ctx.discord,
|
|
209
|
-
threadId: thread.id,
|
|
210
|
-
text: `» **question-select-tester:** ${secondQueuedPrompt}`,
|
|
211
|
-
timeout: 200,
|
|
212
|
-
});
|
|
213
113
|
await waitForFooterMessage({
|
|
214
114
|
discord: ctx.discord,
|
|
215
115
|
threadId: thread.id,
|
|
@@ -233,28 +133,30 @@ describe('queue drain after question select answer', () => {
|
|
|
233
133
|
const timeline = await th.text({ showInteractions: true });
|
|
234
134
|
expect(timeline).toMatchInlineSnapshot(`
|
|
235
135
|
"--- from: user (question-select-tester)
|
|
236
|
-
QUESTION_SELECT_QUEUE_MARKER
|
|
136
|
+
QUESTION_SELECT_QUEUE_MARKER
|
|
237
137
|
--- from: assistant (TestBot)
|
|
238
|
-
*using deterministic-provider/deterministic-v2*
|
|
138
|
+
> *using deterministic-provider/deterministic-v2*
|
|
239
139
|
**Select action**
|
|
240
140
|
How to proceed?
|
|
241
141
|
✓ _Alpha_
|
|
242
142
|
[user interaction]
|
|
243
|
-
» **question-select-tester:**
|
|
143
|
+
» **question-select-tester:** QUESTION_SELECT_DRAIN_FIRST_MARKER
|
|
244
144
|
Queued message (position 1)
|
|
245
145
|
[user interaction]
|
|
246
146
|
Queued message (position 1)
|
|
247
147
|
[user selects dropdown: 0]
|
|
248
148
|
» **question-select-tester:** Alpha
|
|
249
|
-
|
|
250
|
-
*project ⋅ main ⋅
|
|
149
|
+
> question-drain-first
|
|
150
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2*
|
|
251
151
|
» **question-select-tester:** Reply with exactly: post-question-second
|
|
252
|
-
|
|
253
|
-
*project ⋅ main ⋅
|
|
152
|
+
> ok
|
|
153
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
254
154
|
`);
|
|
155
|
+
expect(timeline).toContain('How to proceed?');
|
|
156
|
+
expect(timeline).toContain('[user selects dropdown: 0]');
|
|
255
157
|
expect(timeline).toContain(`» **question-select-tester:** ${firstQueuedPrompt}`);
|
|
256
|
-
expect(timeline).toContain('
|
|
158
|
+
expect(timeline).toContain('question-drain-first');
|
|
257
159
|
expect(timeline).toContain(`» **question-select-tester:** ${secondQueuedPrompt}`);
|
|
258
|
-
expect(timeline).toContain('
|
|
259
|
-
},
|
|
160
|
+
expect(timeline).toContain('ok');
|
|
161
|
+
}, 15_000);
|
|
260
162
|
});
|