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
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
test,
|
|
22
22
|
expect,
|
|
23
23
|
} from 'vitest'
|
|
24
|
-
import { ChannelType, Client, GatewayIntentBits, Partials, REST, Routes
|
|
24
|
+
import { ChannelType, Client, GatewayIntentBits, Partials, REST, Routes } from 'discord.js'
|
|
25
25
|
import { DigitalDiscord } from 'discord-digital-twin/src'
|
|
26
26
|
import {
|
|
27
27
|
buildDeterministicOpencodeConfig,
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
getSessionModel,
|
|
43
43
|
getSessionAgent,
|
|
44
44
|
getChannelAgent,
|
|
45
|
+
getChannelModel,
|
|
45
46
|
setSessionModel,
|
|
46
47
|
type VerbosityLevel,
|
|
47
48
|
} from './database.js'
|
|
@@ -54,10 +55,12 @@ import {
|
|
|
54
55
|
chooseLockPort,
|
|
55
56
|
cleanupTestSessions,
|
|
56
57
|
initTestGitRepo,
|
|
58
|
+
isFooterMessage,
|
|
57
59
|
waitForBotMessageContaining,
|
|
58
60
|
waitForFooterMessage,
|
|
59
61
|
} from './test-utils.js'
|
|
60
62
|
import { buildQuickAgentCommandDescription } from './commands/agent.js'
|
|
63
|
+
import { buildQuickAgentSlashCommand } from './discord-command-registration.js'
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
const TEST_USER_ID = '200000000000000920'
|
|
@@ -339,6 +342,9 @@ describe('agent model resolution', () => {
|
|
|
339
342
|
}
|
|
340
343
|
providerConfig.models[AGENT_MODEL] = { name: AGENT_MODEL }
|
|
341
344
|
providerConfig.models[PLAN_AGENT_MODEL] = { name: PLAN_AGENT_MODEL }
|
|
345
|
+
Object.assign(providerConfig.models[PLAN_AGENT_MODEL], {
|
|
346
|
+
variants: { high: {}, max: {} },
|
|
347
|
+
})
|
|
342
348
|
providerConfig.models[CHANNEL_MODEL] = { name: CHANNEL_MODEL }
|
|
343
349
|
|
|
344
350
|
fs.writeFileSync(
|
|
@@ -400,22 +406,13 @@ describe('agent model resolution', () => {
|
|
|
400
406
|
// Register quick agent slash commands so /plan-agent and /test-agent-agent
|
|
401
407
|
// are resolvable by handleQuickAgentCommand via guild.commands.fetch().
|
|
402
408
|
const agentCommands = ['test-agent', 'plan', 'plain'].map((agentName) => {
|
|
403
|
-
return
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
)
|
|
411
|
-
.setDMPermission(false)
|
|
412
|
-
.addStringOption((opt) =>
|
|
413
|
-
opt
|
|
414
|
-
.setName('prompt')
|
|
415
|
-
.setDescription('Send a prompt with this agent')
|
|
416
|
-
.setRequired(false),
|
|
417
|
-
)
|
|
418
|
-
.toJSON()
|
|
409
|
+
return buildQuickAgentSlashCommand({
|
|
410
|
+
commandName: `${agentName}-agent`,
|
|
411
|
+
description: buildQuickAgentCommandDescription({
|
|
412
|
+
agentName,
|
|
413
|
+
description: `Switch to ${agentName} agent`,
|
|
414
|
+
}),
|
|
415
|
+
}).toJSON()
|
|
419
416
|
})
|
|
420
417
|
const rest = new REST({ version: '10', api: discord.restUrl }).setToken(
|
|
421
418
|
discord.botToken,
|
|
@@ -500,7 +497,7 @@ describe('agent model resolution', () => {
|
|
|
500
497
|
const footerMessage = messages.find((message) => {
|
|
501
498
|
return (
|
|
502
499
|
message.author.id === discord.botUserId &&
|
|
503
|
-
message.
|
|
500
|
+
isFooterMessage({ message, botUserId: discord.botUserId })
|
|
504
501
|
)
|
|
505
502
|
})
|
|
506
503
|
|
|
@@ -508,9 +505,9 @@ describe('agent model resolution', () => {
|
|
|
508
505
|
"--- from: user (agent-model-tester)
|
|
509
506
|
Reply with exactly: agent-model-check
|
|
510
507
|
--- from: assistant (TestBot)
|
|
511
|
-
*using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
512
|
-
|
|
513
|
-
*project ⋅ main ⋅
|
|
508
|
+
> *using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
509
|
+
> ok
|
|
510
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ agent-model-v2 ⋅ **test-agent*** <@200000000000000920>"
|
|
514
511
|
`)
|
|
515
512
|
expect(footerMessage).toBeDefined()
|
|
516
513
|
if (!footerMessage) {
|
|
@@ -565,9 +562,9 @@ describe('agent model resolution', () => {
|
|
|
565
562
|
"--- from: user (agent-model-tester)
|
|
566
563
|
Reply with exactly: system-context-check
|
|
567
564
|
--- from: assistant (TestBot)
|
|
568
|
-
*using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
569
|
-
|
|
570
|
-
*project ⋅ main ⋅
|
|
565
|
+
> *using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
566
|
+
> system-context-ok
|
|
567
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ agent-model-v2 ⋅ **test-agent*** <@200000000000000920>"
|
|
571
568
|
`)
|
|
572
569
|
},
|
|
573
570
|
15_000,
|
|
@@ -659,7 +656,7 @@ describe('agent model resolution', () => {
|
|
|
659
656
|
const threadText = await discord.thread(thread.id).text()
|
|
660
657
|
expect(threadText).toContain('first message in thread')
|
|
661
658
|
expect(threadText).toContain('Reply with exactly: reply-context-check')
|
|
662
|
-
expect(threadText).toContain('
|
|
659
|
+
expect(threadText).toContain('ok')
|
|
663
660
|
},
|
|
664
661
|
15_000,
|
|
665
662
|
)
|
|
@@ -700,7 +697,7 @@ describe('agent model resolution', () => {
|
|
|
700
697
|
const footerMessage = messages.find((message) => {
|
|
701
698
|
return (
|
|
702
699
|
message.author.id === discord.botUserId &&
|
|
703
|
-
message.
|
|
700
|
+
isFooterMessage({ message, botUserId: discord.botUserId })
|
|
704
701
|
)
|
|
705
702
|
})
|
|
706
703
|
|
|
@@ -708,9 +705,9 @@ describe('agent model resolution', () => {
|
|
|
708
705
|
"--- from: user (agent-model-tester)
|
|
709
706
|
Reply with exactly: channel-model-check
|
|
710
707
|
--- from: assistant (TestBot)
|
|
711
|
-
*using deterministic-provider/channel-model-v2*
|
|
712
|
-
|
|
713
|
-
*project ⋅ main ⋅
|
|
708
|
+
> *using deterministic-provider/channel-model-v2*
|
|
709
|
+
> ok
|
|
710
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ channel-model-v2* <@200000000000000920>"
|
|
714
711
|
`)
|
|
715
712
|
expect(footerMessage).toBeDefined()
|
|
716
713
|
if (!footerMessage) {
|
|
@@ -730,71 +727,44 @@ describe('agent model resolution', () => {
|
|
|
730
727
|
)
|
|
731
728
|
|
|
732
729
|
test(
|
|
733
|
-
'channel model
|
|
730
|
+
'unsupported channel model variant falls back to the selected model',
|
|
734
731
|
async () => {
|
|
735
|
-
// Clear channel agent so model resolution falls through to channel model
|
|
736
732
|
const db = await getDb()
|
|
737
733
|
await db.delete(schema.channel_agents).where(orm.eq(schema.channel_agents.channel_id, TEXT_CHANNEL_ID))
|
|
738
|
-
|
|
739
|
-
// Set channel model with a variant (thinking level)
|
|
740
|
-
// The deterministic provider doesn't support thinking, so the variant
|
|
741
|
-
// is resolved but silently dropped (no matching thinking values).
|
|
742
|
-
// This test verifies the variant cascade code path runs without crashing
|
|
743
|
-
// and the correct model still appears in the footer.
|
|
744
734
|
await setChannelModel({
|
|
745
735
|
channelId: TEXT_CHANNEL_ID,
|
|
746
736
|
modelId: `${PROVIDER_NAME}/${CHANNEL_MODEL}`,
|
|
747
737
|
variant: 'high',
|
|
748
738
|
})
|
|
749
739
|
|
|
740
|
+
const prompt = 'Reply with exactly: variant-check'
|
|
750
741
|
await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
751
|
-
content:
|
|
742
|
+
content: prompt,
|
|
752
743
|
})
|
|
753
|
-
|
|
754
744
|
const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
755
745
|
timeout: 4_000,
|
|
756
|
-
predicate: (
|
|
757
|
-
return t.name === 'Reply with exactly: variant-check'
|
|
758
|
-
},
|
|
746
|
+
predicate: (candidate) => candidate.name === prompt,
|
|
759
747
|
})
|
|
760
|
-
|
|
761
|
-
await waitForBotMessageContaining({
|
|
748
|
+
const messages = await waitForFooterMessage({
|
|
762
749
|
discord,
|
|
763
750
|
threadId: thread.id,
|
|
764
|
-
userId: TEST_USER_ID,
|
|
765
|
-
text: '*project',
|
|
766
751
|
timeout: 4_000,
|
|
767
752
|
})
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
return (
|
|
772
|
-
message.author.id === discord.botUserId &&
|
|
773
|
-
message.content.startsWith('*')
|
|
774
|
-
)
|
|
753
|
+
const footer = messages.find((message) => {
|
|
754
|
+
return message.author.id === discord.botUserId
|
|
755
|
+
&& isFooterMessage({ message, botUserId: discord.botUserId })
|
|
775
756
|
})
|
|
776
757
|
|
|
777
758
|
expect(await discord.thread(thread.id).text()).toMatchInlineSnapshot(`
|
|
778
759
|
"--- from: user (agent-model-tester)
|
|
779
760
|
Reply with exactly: variant-check
|
|
780
761
|
--- from: assistant (TestBot)
|
|
781
|
-
*using deterministic-provider/channel-model-v2*
|
|
782
|
-
|
|
783
|
-
*project ⋅ main ⋅
|
|
762
|
+
> *using deterministic-provider/channel-model-v2*
|
|
763
|
+
> ok
|
|
764
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ channel-model-v2* <@200000000000000920>"
|
|
784
765
|
`)
|
|
785
|
-
expect(
|
|
786
|
-
|
|
787
|
-
throw new Error(
|
|
788
|
-
`Expected footer message but none found. Bot messages: ${messages
|
|
789
|
-
.filter((m) => m.author.id === discord.botUserId)
|
|
790
|
-
.map((m) => m.content.slice(0, 150))
|
|
791
|
-
.join(' | ')}`,
|
|
792
|
-
)
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// Footer should still contain the channel model (variant doesn't crash)
|
|
796
|
-
expect(footerMessage.content).toContain(CHANNEL_MODEL)
|
|
797
|
-
expect(footerMessage.content).not.toContain(DEFAULT_MODEL)
|
|
766
|
+
expect(footer?.content).toContain(CHANNEL_MODEL)
|
|
767
|
+
expect(footer?.content).not.toContain(DEFAULT_MODEL)
|
|
798
768
|
},
|
|
799
769
|
15_000,
|
|
800
770
|
)
|
|
@@ -876,8 +846,8 @@ describe('agent model resolution', () => {
|
|
|
876
846
|
"--- from: assistant (TestBot)
|
|
877
847
|
Reusing context from <#SOURCE_THREAD> to answer prompt...
|
|
878
848
|
Reply with exactly: btw-model-check
|
|
879
|
-
|
|
880
|
-
*project ⋅ main ⋅
|
|
849
|
+
> ok
|
|
850
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ plan-model-v2* <@200000000000000920>"
|
|
881
851
|
`)
|
|
882
852
|
expect(forkedSessionModel).toMatchInlineSnapshot(`
|
|
883
853
|
{
|
|
@@ -920,7 +890,7 @@ describe('agent model resolution', () => {
|
|
|
920
890
|
const firstMessages = await discord.thread(thread.id).getMessages()
|
|
921
891
|
const firstFooter = firstMessages.find((m) => {
|
|
922
892
|
return (
|
|
923
|
-
m
|
|
893
|
+
isFooterMessage({ message: m, botUserId: discord.botUserId })
|
|
924
894
|
)
|
|
925
895
|
})
|
|
926
896
|
expect(firstFooter).toBeDefined()
|
|
@@ -956,14 +926,14 @@ describe('agent model resolution', () => {
|
|
|
956
926
|
"--- from: user (agent-model-tester)
|
|
957
927
|
Reply with exactly: first-thread-msg
|
|
958
928
|
--- from: assistant (TestBot)
|
|
959
|
-
*using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
960
|
-
|
|
961
|
-
*project ⋅ main ⋅
|
|
929
|
+
> *using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
930
|
+
> ok
|
|
931
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ agent-model-v2 ⋅ **test-agent*** <@200000000000000920>
|
|
962
932
|
--- from: user (agent-model-tester)
|
|
963
933
|
Reply with exactly: second-thread-msg
|
|
964
934
|
--- from: assistant (TestBot)
|
|
965
|
-
|
|
966
|
-
*project ⋅ main ⋅
|
|
935
|
+
> ok
|
|
936
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ agent-model-v2 ⋅ **test-agent*** <@200000000000000920>"
|
|
967
937
|
`)
|
|
968
938
|
|
|
969
939
|
const secondMessages = await discord.thread(thread.id).getMessages()
|
|
@@ -971,7 +941,7 @@ describe('agent model resolution', () => {
|
|
|
971
941
|
.reverse()
|
|
972
942
|
.find((m) => {
|
|
973
943
|
return (
|
|
974
|
-
m
|
|
944
|
+
isFooterMessage({ message: m, botUserId: discord.botUserId })
|
|
975
945
|
)
|
|
976
946
|
})
|
|
977
947
|
expect(secondFooter).toBeDefined()
|
|
@@ -985,27 +955,30 @@ describe('agent model resolution', () => {
|
|
|
985
955
|
)
|
|
986
956
|
|
|
987
957
|
test(
|
|
988
|
-
'
|
|
958
|
+
'/plan-agent with prompt starts a session with the plan agent',
|
|
989
959
|
async () => {
|
|
990
|
-
|
|
991
|
-
const db = await getDb()
|
|
992
|
-
await db.delete(schema.channel_agents).where(orm.eq(schema.channel_agents.channel_id, TEXT_CHANNEL_ID))
|
|
993
|
-
// Also clear channel model so we get the pure default
|
|
994
|
-
await db.delete(schema.channel_models).where(orm.eq(schema.channel_models.channel_id, TEXT_CHANNEL_ID))
|
|
960
|
+
await setChannelAgent(TEXT_CHANNEL_ID, 'test-agent')
|
|
995
961
|
|
|
996
|
-
|
|
997
|
-
await discord
|
|
998
|
-
|
|
999
|
-
|
|
962
|
+
const prompt = 'Reply with exactly: inline-plan-agent-msg'
|
|
963
|
+
const { id: interactionId } = await discord
|
|
964
|
+
.channel(TEXT_CHANNEL_ID)
|
|
965
|
+
.user(TEST_USER_ID)
|
|
966
|
+
.runSlashCommand({
|
|
967
|
+
name: 'plan-agent',
|
|
968
|
+
options: [{ name: 'prompt', type: 3, value: prompt }],
|
|
969
|
+
})
|
|
970
|
+
|
|
971
|
+
await discord
|
|
972
|
+
.channel(TEXT_CHANNEL_ID)
|
|
973
|
+
.waitForInteractionAck({ interactionId, timeout: 4_000 })
|
|
1000
974
|
|
|
1001
975
|
const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
1002
976
|
timeout: 4_000,
|
|
1003
977
|
predicate: (t) => {
|
|
1004
|
-
return t.name ===
|
|
978
|
+
return t.name === prompt
|
|
1005
979
|
},
|
|
1006
980
|
})
|
|
1007
981
|
|
|
1008
|
-
// Wait for footer — should show the default model
|
|
1009
982
|
await waitForFooterMessage({
|
|
1010
983
|
discord,
|
|
1011
984
|
threadId: thread.id,
|
|
@@ -1014,119 +987,122 @@ describe('agent model resolution', () => {
|
|
|
1014
987
|
afterAuthorId: discord.botUserId,
|
|
1015
988
|
})
|
|
1016
989
|
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
990
|
+
const sessionId = await getThreadSession(thread.id)
|
|
991
|
+
expect(sessionId).toBeDefined()
|
|
992
|
+
expect(sessionId ? await getSessionAgent(sessionId) : undefined).toBe('plan')
|
|
993
|
+
expect(await getChannelAgent(TEXT_CHANNEL_ID)).toBe('test-agent')
|
|
994
|
+
expect(await discord.thread(thread.id).text()).toMatchInlineSnapshot(`
|
|
995
|
+
"--- from: assistant (TestBot)
|
|
996
|
+
» **agent-model-tester** (plan): Reply with exactly: inline-plan-agent-msg
|
|
997
|
+
> *using deterministic-provider/plan-model-v2 ⋅ plan*
|
|
998
|
+
> ok
|
|
999
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ plan-model-v2 ⋅ **plan*** <@200000000000000920>"
|
|
1000
|
+
`)
|
|
1001
|
+
},
|
|
1002
|
+
20_000,
|
|
1003
|
+
)
|
|
1027
1004
|
|
|
1028
|
-
|
|
1005
|
+
test(
|
|
1006
|
+
'/plan-agent with prompt persists a supported thinking variant',
|
|
1007
|
+
async () => {
|
|
1008
|
+
const prompt = 'Reply with exactly: inline-plan-agent-variant-msg'
|
|
1029
1009
|
const { id: interactionId } = await discord
|
|
1030
1010
|
.channel(TEXT_CHANNEL_ID)
|
|
1031
1011
|
.user(TEST_USER_ID)
|
|
1032
|
-
.runSlashCommand({
|
|
1012
|
+
.runSlashCommand({
|
|
1013
|
+
name: 'plan-agent',
|
|
1014
|
+
options: [
|
|
1015
|
+
{ name: 'prompt', type: 3, value: prompt },
|
|
1016
|
+
{ name: 'variant', type: 3, value: 'high' },
|
|
1017
|
+
],
|
|
1018
|
+
})
|
|
1033
1019
|
|
|
1034
1020
|
await discord
|
|
1035
1021
|
.channel(TEXT_CHANNEL_ID)
|
|
1036
1022
|
.waitForInteractionAck({ interactionId, timeout: 4_000 })
|
|
1037
1023
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
.sendMessage({
|
|
1043
|
-
content: 'Reply with exactly: default-second-msg',
|
|
1044
|
-
})
|
|
1024
|
+
const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
1025
|
+
timeout: 4_000,
|
|
1026
|
+
predicate: (t) => t.name === prompt,
|
|
1027
|
+
})
|
|
1045
1028
|
|
|
1046
1029
|
await waitForFooterMessage({
|
|
1047
1030
|
discord,
|
|
1048
1031
|
threadId: thread.id,
|
|
1049
1032
|
timeout: 4_000,
|
|
1050
|
-
afterMessageIncludes: '
|
|
1051
|
-
afterAuthorId:
|
|
1033
|
+
afterMessageIncludes: 'ok',
|
|
1034
|
+
afterAuthorId: discord.botUserId,
|
|
1052
1035
|
})
|
|
1053
1036
|
|
|
1037
|
+
const sessionId = await getThreadSession(thread.id)
|
|
1038
|
+
expect(sessionId).toBeDefined()
|
|
1054
1039
|
expect(await discord.thread(thread.id).text()).toMatchInlineSnapshot(`
|
|
1055
|
-
"--- from:
|
|
1056
|
-
Reply with exactly:
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1040
|
+
"--- from: assistant (TestBot)
|
|
1041
|
+
» **agent-model-tester** (plan): Reply with exactly: inline-plan-agent-variant-msg
|
|
1042
|
+
> *using deterministic-provider/plan-model-v2 ⋅ plan*
|
|
1043
|
+
> ok
|
|
1044
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ plan-model-v2 ⋅ **plan*** <@200000000000000920>"
|
|
1045
|
+
`)
|
|
1046
|
+
expect(sessionId ? await getSessionModel(sessionId) : undefined).toMatchInlineSnapshot(`
|
|
1047
|
+
{
|
|
1048
|
+
"modelId": "deterministic-provider/plan-model-v2",
|
|
1049
|
+
"variant": "high",
|
|
1050
|
+
}
|
|
1066
1051
|
`)
|
|
1067
|
-
|
|
1068
|
-
const secondMessages = await discord.thread(thread.id).getMessages()
|
|
1069
|
-
const secondFooter = [...secondMessages]
|
|
1070
|
-
.reverse()
|
|
1071
|
-
.find((m) => {
|
|
1072
|
-
return (
|
|
1073
|
-
m.author.id === discord.botUserId && m.content.startsWith('*')
|
|
1074
|
-
)
|
|
1075
|
-
})
|
|
1076
|
-
expect(secondFooter).toBeDefined()
|
|
1077
|
-
|
|
1078
|
-
// The existing thread should still use the DEFAULT model,
|
|
1079
|
-
// NOT the test-agent's model (AGENT_MODEL)
|
|
1080
|
-
expect(secondFooter!.content).toContain(DEFAULT_MODEL)
|
|
1081
|
-
expect(secondFooter!.content).not.toContain(AGENT_MODEL)
|
|
1082
1052
|
},
|
|
1083
1053
|
20_000,
|
|
1084
1054
|
)
|
|
1085
1055
|
|
|
1086
1056
|
test(
|
|
1087
|
-
'/plan-agent
|
|
1057
|
+
'/plan-agent variant persists on the channel without a prompt',
|
|
1088
1058
|
async () => {
|
|
1089
|
-
await
|
|
1059
|
+
const db = await getDb()
|
|
1060
|
+
await db.delete(schema.channel_models).where(orm.eq(schema.channel_models.channel_id, TEXT_CHANNEL_ID))
|
|
1090
1061
|
|
|
1091
|
-
const prompt = 'Reply with exactly: inline-plan-agent-msg'
|
|
1092
1062
|
const { id: interactionId } = await discord
|
|
1093
1063
|
.channel(TEXT_CHANNEL_ID)
|
|
1094
1064
|
.user(TEST_USER_ID)
|
|
1095
1065
|
.runSlashCommand({
|
|
1096
1066
|
name: 'plan-agent',
|
|
1097
|
-
options: [{ name: '
|
|
1067
|
+
options: [{ name: 'variant', type: 3, value: 'max' }],
|
|
1098
1068
|
})
|
|
1099
1069
|
|
|
1100
1070
|
await discord
|
|
1101
1071
|
.channel(TEXT_CHANNEL_ID)
|
|
1102
1072
|
.waitForInteractionAck({ interactionId, timeout: 4_000 })
|
|
1103
1073
|
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1074
|
+
const start = Date.now()
|
|
1075
|
+
let confirmation = ''
|
|
1076
|
+
while (Date.now() - start < 4_000) {
|
|
1077
|
+
const messages = await discord.channel(TEXT_CHANNEL_ID).getMessages()
|
|
1078
|
+
const match = [...messages].reverse().find((message) => {
|
|
1079
|
+
return (
|
|
1080
|
+
message.author.id === discord.botUserId &&
|
|
1081
|
+
message.content.includes('Variant: **max**')
|
|
1082
|
+
)
|
|
1083
|
+
})
|
|
1084
|
+
if (match) {
|
|
1085
|
+
confirmation = match.content
|
|
1086
|
+
break
|
|
1087
|
+
}
|
|
1088
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
1089
|
+
}
|
|
1118
1090
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
"--- from: assistant (TestBot)
|
|
1125
|
-
» **agent-model-tester** (plan): Reply with exactly: inline-plan-agent-msg
|
|
1126
|
-
*using deterministic-provider/plan-model-v2 ⋅ plan*
|
|
1127
|
-
⬥ ok
|
|
1128
|
-
*project ⋅ main ⋅ Ns ⋅ N% ⋅ plan-model-v2 ⋅ **plan***"
|
|
1091
|
+
expect(confirmation).toMatchInlineSnapshot(`
|
|
1092
|
+
"Switched to **plan** agent for this channel (was **test-agent**)
|
|
1093
|
+
Model: *deterministic-provider/plan-model-v2* (agent "plan")
|
|
1094
|
+
Variant: **max**
|
|
1095
|
+
All new sessions will use this agent."
|
|
1129
1096
|
`)
|
|
1097
|
+
expect(await getChannelAgent(TEXT_CHANNEL_ID)).toBe('plan')
|
|
1098
|
+
expect(await getChannelModel(TEXT_CHANNEL_ID)).toMatchInlineSnapshot(`
|
|
1099
|
+
{
|
|
1100
|
+
"modelId": "deterministic-provider/plan-model-v2",
|
|
1101
|
+
"variant": "max",
|
|
1102
|
+
}
|
|
1103
|
+
`)
|
|
1104
|
+
|
|
1105
|
+
await db.delete(schema.channel_models).where(orm.eq(schema.channel_models.channel_id, TEXT_CHANNEL_ID))
|
|
1130
1106
|
},
|
|
1131
1107
|
20_000,
|
|
1132
1108
|
)
|
|
@@ -1180,12 +1156,12 @@ describe('agent model resolution', () => {
|
|
|
1180
1156
|
"--- from: user (agent-model-tester)
|
|
1181
1157
|
Reply with exactly: inline-existing-first-msg
|
|
1182
1158
|
--- from: assistant (TestBot)
|
|
1183
|
-
*using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
1184
|
-
|
|
1185
|
-
*project ⋅ main ⋅
|
|
1159
|
+
> *using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
1160
|
+
> ok
|
|
1161
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ agent-model-v2 ⋅ **test-agent*** <@200000000000000920>
|
|
1186
1162
|
» **agent-model-tester** (plan): Reply with exactly: inline-existing-plan-msg
|
|
1187
|
-
|
|
1188
|
-
*project ⋅ main ⋅
|
|
1163
|
+
> ok
|
|
1164
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ plan-model-v2 ⋅ **plan*** <@200000000000000920>"
|
|
1189
1165
|
`)
|
|
1190
1166
|
},
|
|
1191
1167
|
20_000,
|
|
@@ -1220,7 +1196,7 @@ describe('agent model resolution', () => {
|
|
|
1220
1196
|
const firstFooter = (await discord.thread(thread.id).getMessages()).find(
|
|
1221
1197
|
(m) => {
|
|
1222
1198
|
return (
|
|
1223
|
-
m
|
|
1199
|
+
isFooterMessage({ message: m, botUserId: discord.botUserId })
|
|
1224
1200
|
)
|
|
1225
1201
|
},
|
|
1226
1202
|
)
|
|
@@ -1252,24 +1228,24 @@ describe('agent model resolution', () => {
|
|
|
1252
1228
|
"--- from: user (agent-model-tester)
|
|
1253
1229
|
Reply with exactly: switch-in-thread-msg
|
|
1254
1230
|
--- from: assistant (TestBot)
|
|
1255
|
-
*using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
1256
|
-
|
|
1257
|
-
*project ⋅ main ⋅
|
|
1231
|
+
> *using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
1232
|
+
> ok
|
|
1233
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ agent-model-v2 ⋅ **test-agent*** <@200000000000000920>
|
|
1258
1234
|
Switched to **plan** agent for this session (was **test-agent**)
|
|
1259
1235
|
Model: *deterministic-provider/plan-model-v2* (agent "plan")
|
|
1260
1236
|
The agent will change on the next message.
|
|
1261
1237
|
--- from: user (agent-model-tester)
|
|
1262
1238
|
Reply with exactly: after-switch-msg
|
|
1263
1239
|
--- from: assistant (TestBot)
|
|
1264
|
-
|
|
1265
|
-
*project ⋅ main ⋅
|
|
1240
|
+
> ok
|
|
1241
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ plan-model-v2 ⋅ **plan*** <@200000000000000920>"
|
|
1266
1242
|
`)
|
|
1267
1243
|
|
|
1268
1244
|
const secondFooter = [...(await discord.thread(thread.id).getMessages())]
|
|
1269
1245
|
.reverse()
|
|
1270
1246
|
.find((m) => {
|
|
1271
1247
|
return (
|
|
1272
|
-
m
|
|
1248
|
+
isFooterMessage({ message: m, botUserId: discord.botUserId })
|
|
1273
1249
|
)
|
|
1274
1250
|
})
|
|
1275
1251
|
expect(secondFooter).toBeDefined()
|
|
@@ -1281,6 +1257,131 @@ describe('agent model resolution', () => {
|
|
|
1281
1257
|
20_000,
|
|
1282
1258
|
)
|
|
1283
1259
|
|
|
1260
|
+
test(
|
|
1261
|
+
'/plan-agent then an immediate follow-up uses the new agent',
|
|
1262
|
+
async () => {
|
|
1263
|
+
await setChannelAgent(TEXT_CHANNEL_ID, 'test-agent')
|
|
1264
|
+
|
|
1265
|
+
await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
1266
|
+
content: 'Reply with exactly: race-switch-first-msg',
|
|
1267
|
+
})
|
|
1268
|
+
|
|
1269
|
+
const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
1270
|
+
timeout: 4_000,
|
|
1271
|
+
predicate: (t) => {
|
|
1272
|
+
return t.name === 'Reply with exactly: race-switch-first-msg'
|
|
1273
|
+
},
|
|
1274
|
+
})
|
|
1275
|
+
|
|
1276
|
+
await waitForFooterMessage({
|
|
1277
|
+
discord,
|
|
1278
|
+
threadId: thread.id,
|
|
1279
|
+
timeout: 4_000,
|
|
1280
|
+
afterMessageIncludes: 'ok',
|
|
1281
|
+
afterAuthorId: discord.botUserId,
|
|
1282
|
+
})
|
|
1283
|
+
|
|
1284
|
+
const th = discord.thread(thread.id)
|
|
1285
|
+
const { id: interactionId } = await th
|
|
1286
|
+
.user(TEST_USER_ID)
|
|
1287
|
+
.runSlashCommand({ name: 'plan-agent' })
|
|
1288
|
+
await th.user(TEST_USER_ID).sendMessage({
|
|
1289
|
+
content: 'Reply with exactly: race-switch-follow-up',
|
|
1290
|
+
})
|
|
1291
|
+
|
|
1292
|
+
await th.waitForInteractionAck({ interactionId, timeout: 4_000 })
|
|
1293
|
+
await waitForFooterMessage({
|
|
1294
|
+
discord,
|
|
1295
|
+
threadId: thread.id,
|
|
1296
|
+
timeout: 4_000,
|
|
1297
|
+
afterMessageIncludes: 'race-switch-follow-up',
|
|
1298
|
+
afterAuthorId: TEST_USER_ID,
|
|
1299
|
+
})
|
|
1300
|
+
|
|
1301
|
+
expect(await discord.thread(thread.id).text()).toMatchInlineSnapshot(`
|
|
1302
|
+
"--- from: user (agent-model-tester)
|
|
1303
|
+
Reply with exactly: race-switch-first-msg
|
|
1304
|
+
--- from: assistant (TestBot)
|
|
1305
|
+
> *using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
1306
|
+
> ok
|
|
1307
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ agent-model-v2 ⋅ **test-agent*** <@200000000000000920>
|
|
1308
|
+
--- from: user (agent-model-tester)
|
|
1309
|
+
Reply with exactly: race-switch-follow-up
|
|
1310
|
+
--- from: assistant (TestBot)
|
|
1311
|
+
Switched to **plan** agent for this session (was **test-agent**)
|
|
1312
|
+
Model: *deterministic-provider/plan-model-v2* (agent "plan")
|
|
1313
|
+
The agent will change on the next message.
|
|
1314
|
+
> ok
|
|
1315
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ plan-model-v2 ⋅ **plan*** <@200000000000000920>"
|
|
1316
|
+
`)
|
|
1317
|
+
|
|
1318
|
+
const secondFooter = [...(await discord.thread(thread.id).getMessages())]
|
|
1319
|
+
.reverse()
|
|
1320
|
+
.find((m) => {
|
|
1321
|
+
return (
|
|
1322
|
+
isFooterMessage({ message: m, botUserId: discord.botUserId })
|
|
1323
|
+
)
|
|
1324
|
+
})
|
|
1325
|
+
expect(secondFooter).toBeDefined()
|
|
1326
|
+
expect(secondFooter!.content).toContain(PLAN_AGENT_MODEL)
|
|
1327
|
+
expect(secondFooter!.content).not.toContain(AGENT_MODEL)
|
|
1328
|
+
},
|
|
1329
|
+
20_000,
|
|
1330
|
+
)
|
|
1331
|
+
|
|
1332
|
+
test(
|
|
1333
|
+
'/plan-agent in a channel then an immediate message uses the new agent',
|
|
1334
|
+
async () => {
|
|
1335
|
+
await setChannelAgent(TEXT_CHANNEL_ID, 'test-agent')
|
|
1336
|
+
|
|
1337
|
+
const { id: interactionId } = await discord
|
|
1338
|
+
.channel(TEXT_CHANNEL_ID)
|
|
1339
|
+
.user(TEST_USER_ID)
|
|
1340
|
+
.runSlashCommand({ name: 'plan-agent' })
|
|
1341
|
+
await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
1342
|
+
content: 'Reply with exactly: race-channel-follow-up',
|
|
1343
|
+
})
|
|
1344
|
+
|
|
1345
|
+
await discord
|
|
1346
|
+
.channel(TEXT_CHANNEL_ID)
|
|
1347
|
+
.waitForInteractionAck({ interactionId, timeout: 4_000 })
|
|
1348
|
+
|
|
1349
|
+
const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
1350
|
+
timeout: 4_000,
|
|
1351
|
+
predicate: (t) => {
|
|
1352
|
+
return t.name === 'Reply with exactly: race-channel-follow-up'
|
|
1353
|
+
},
|
|
1354
|
+
})
|
|
1355
|
+
|
|
1356
|
+
await waitForFooterMessage({
|
|
1357
|
+
discord,
|
|
1358
|
+
threadId: thread.id,
|
|
1359
|
+
timeout: 4_000,
|
|
1360
|
+
afterMessageIncludes: 'ok',
|
|
1361
|
+
afterAuthorId: discord.botUserId,
|
|
1362
|
+
})
|
|
1363
|
+
|
|
1364
|
+
expect(await discord.thread(thread.id).text()).toMatchInlineSnapshot(`
|
|
1365
|
+
"--- from: user (agent-model-tester)
|
|
1366
|
+
Reply with exactly: race-channel-follow-up
|
|
1367
|
+
--- from: assistant (TestBot)
|
|
1368
|
+
> *using deterministic-provider/plan-model-v2 ⋅ plan*
|
|
1369
|
+
> ok
|
|
1370
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ plan-model-v2 ⋅ **plan*** <@200000000000000920>"
|
|
1371
|
+
`)
|
|
1372
|
+
|
|
1373
|
+
const footer = [...(await discord.thread(thread.id).getMessages())]
|
|
1374
|
+
.reverse()
|
|
1375
|
+
.find((m) => {
|
|
1376
|
+
return isFooterMessage({ message: m, botUserId: discord.botUserId })
|
|
1377
|
+
})
|
|
1378
|
+
expect(footer).toBeDefined()
|
|
1379
|
+
expect(footer!.content).toContain(PLAN_AGENT_MODEL)
|
|
1380
|
+
expect(footer!.content).not.toContain(AGENT_MODEL)
|
|
1381
|
+
},
|
|
1382
|
+
20_000,
|
|
1383
|
+
)
|
|
1384
|
+
|
|
1284
1385
|
test(
|
|
1285
1386
|
'/plan-agent on the same agent refreshes a stale session model',
|
|
1286
1387
|
async () => {
|
|
@@ -1325,9 +1426,9 @@ describe('agent model resolution', () => {
|
|
|
1325
1426
|
"--- from: user (agent-model-tester)
|
|
1326
1427
|
Reply with exactly: refresh-agent-model-msg
|
|
1327
1428
|
--- from: assistant (TestBot)
|
|
1328
|
-
*using deterministic-provider/plan-model-v2 ⋅ plan*
|
|
1329
|
-
|
|
1330
|
-
*project ⋅ main ⋅
|
|
1429
|
+
> *using deterministic-provider/plan-model-v2 ⋅ plan*
|
|
1430
|
+
> ok
|
|
1431
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ plan-model-v2 ⋅ **plan*** <@200000000000000920>
|
|
1331
1432
|
Using **plan** agent for this session
|
|
1332
1433
|
Model: *deterministic-provider/plan-model-v2* (agent "plan")
|
|
1333
1434
|
The agent will change on the next message."
|
|
@@ -1378,9 +1479,9 @@ describe('agent model resolution', () => {
|
|
|
1378
1479
|
"--- from: user (agent-model-tester)
|
|
1379
1480
|
Reply with exactly: channel-vs-agent-msg
|
|
1380
1481
|
--- from: assistant (TestBot)
|
|
1381
|
-
*using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
1382
|
-
|
|
1383
|
-
*project ⋅ main ⋅
|
|
1482
|
+
> *using deterministic-provider/agent-model-v2 ⋅ test-agent*
|
|
1483
|
+
> ok
|
|
1484
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ agent-model-v2 ⋅ **test-agent*** <@200000000000000920>
|
|
1384
1485
|
Switched to **plan** agent for this session (was **test-agent**)
|
|
1385
1486
|
Model: *deterministic-provider/plan-model-v2* (agent "plan")
|
|
1386
1487
|
The agent will change on the next message."
|
|
@@ -1433,9 +1534,9 @@ describe('agent model resolution', () => {
|
|
|
1433
1534
|
"--- from: user (agent-model-tester)
|
|
1434
1535
|
Reply with exactly: plain-agent-override-msg
|
|
1435
1536
|
--- from: assistant (TestBot)
|
|
1436
|
-
*using deterministic-provider/channel-model-v2*
|
|
1437
|
-
|
|
1438
|
-
*project ⋅ main ⋅
|
|
1537
|
+
> *using deterministic-provider/channel-model-v2*
|
|
1538
|
+
> ok
|
|
1539
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ channel-model-v2* <@200000000000000920>
|
|
1439
1540
|
Switched to **plain** agent for this session
|
|
1440
1541
|
Model: *deterministic-provider/channel-model-v2* (channel override)
|
|
1441
1542
|
This model comes from a channel override. Use /model and press Clear override if you want this agent's model.
|