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
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
setupQueueAdvancedSuite,
|
|
7
7
|
TEST_USER_ID,
|
|
8
8
|
} from './queue-advanced-e2e-setup.js'
|
|
9
|
+
import { store } from './store.js'
|
|
9
10
|
import {
|
|
11
|
+
getMessageVisibleText,
|
|
12
|
+
isFooterMessage,
|
|
10
13
|
waitForFooterMessage,
|
|
11
14
|
waitForBotMessageContaining,
|
|
12
15
|
waitForBotReplyAfterUserMessage,
|
|
@@ -50,20 +53,58 @@ e2eTest('queue advanced: footer emission', () => {
|
|
|
50
53
|
"--- from: user (queue-advanced-tester)
|
|
51
54
|
Reply with exactly: footer-check
|
|
52
55
|
--- from: assistant (TestBot)
|
|
53
|
-
*using deterministic-provider/deterministic-v2*
|
|
54
|
-
|
|
55
|
-
*project ⋅ main ⋅
|
|
56
|
+
> *using deterministic-provider/deterministic-v2*
|
|
57
|
+
> ok
|
|
58
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
56
59
|
`)
|
|
57
60
|
const foundFooter = footerMessages.some((m) => {
|
|
58
61
|
return m.author.id === ctx.discord.botUserId
|
|
59
|
-
&& m.
|
|
60
|
-
&& m.content.includes('⋅')
|
|
62
|
+
&& isFooterMessage({ message: m, botUserId: ctx.discord.botUserId })
|
|
61
63
|
})
|
|
62
64
|
expect(foundFooter).toBe(true)
|
|
63
65
|
},
|
|
64
66
|
8_000,
|
|
65
67
|
)
|
|
66
68
|
|
|
69
|
+
test(
|
|
70
|
+
'skip-footer-mentions omits the thread creator mention',
|
|
71
|
+
async () => {
|
|
72
|
+
store.setState({ footerMentionsEnabled: false })
|
|
73
|
+
try {
|
|
74
|
+
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
75
|
+
content: 'Reply with exactly: footer-without-mention',
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const thread = await ctx.discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
79
|
+
timeout: 4_000,
|
|
80
|
+
predicate: (t) => {
|
|
81
|
+
return t.name === 'Reply with exactly: footer-without-mention'
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const th = ctx.discord.thread(thread.id)
|
|
86
|
+
await th.waitForBotReply({ timeout: 4_000 })
|
|
87
|
+
await waitForFooterMessage({
|
|
88
|
+
discord: ctx.discord,
|
|
89
|
+
threadId: thread.id,
|
|
90
|
+
timeout: 4_000,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
expect(await th.text()).toMatchInlineSnapshot(`
|
|
94
|
+
"--- from: user (queue-advanced-tester)
|
|
95
|
+
Reply with exactly: footer-without-mention
|
|
96
|
+
--- from: assistant (TestBot)
|
|
97
|
+
> *using deterministic-provider/deterministic-v2*
|
|
98
|
+
> ok
|
|
99
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2*"
|
|
100
|
+
`)
|
|
101
|
+
} finally {
|
|
102
|
+
store.setState({ footerMentionsEnabled: true })
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
8_000,
|
|
106
|
+
)
|
|
107
|
+
|
|
67
108
|
test(
|
|
68
109
|
'footer appears after second message in same session',
|
|
69
110
|
async () => {
|
|
@@ -112,21 +153,20 @@ e2eTest('queue advanced: footer emission', () => {
|
|
|
112
153
|
const msgs = await th.getMessages()
|
|
113
154
|
const footerCount = msgs.filter((m) => {
|
|
114
155
|
return m.author.id === ctx.discord.botUserId
|
|
115
|
-
&& m.
|
|
116
|
-
&& m.content.includes('⋅')
|
|
156
|
+
&& isFooterMessage({ message: m, botUserId: ctx.discord.botUserId })
|
|
117
157
|
}).length
|
|
118
158
|
expect(await th.text()).toMatchInlineSnapshot(`
|
|
119
159
|
"--- from: user (queue-advanced-tester)
|
|
120
160
|
Reply with exactly: footer-multi-setup
|
|
121
161
|
--- from: assistant (TestBot)
|
|
122
|
-
*using deterministic-provider/deterministic-v2*
|
|
123
|
-
|
|
124
|
-
*project ⋅ main ⋅
|
|
162
|
+
> *using deterministic-provider/deterministic-v2*
|
|
163
|
+
> ok
|
|
164
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>
|
|
125
165
|
--- from: user (queue-advanced-tester)
|
|
126
166
|
Reply with exactly: footer-multi-second
|
|
127
167
|
--- from: assistant (TestBot)
|
|
128
|
-
|
|
129
|
-
*project ⋅ main ⋅
|
|
168
|
+
> ok
|
|
169
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
130
170
|
`)
|
|
131
171
|
if (footerCount >= 2) {
|
|
132
172
|
expect(footerCount).toBeGreaterThanOrEqual(2)
|
|
@@ -141,9 +181,7 @@ e2eTest('queue advanced: footer emission', () => {
|
|
|
141
181
|
})
|
|
142
182
|
const latestMsgs = await th.getMessages()
|
|
143
183
|
const count = latestMsgs.filter((m) => {
|
|
144
|
-
return m
|
|
145
|
-
&& m.content.startsWith('*')
|
|
146
|
-
&& m.content.includes('⋅')
|
|
184
|
+
return isFooterMessage({ message: m, botUserId: ctx.discord.botUserId })
|
|
147
185
|
}).length
|
|
148
186
|
if (count >= 2) {
|
|
149
187
|
found = true
|
|
@@ -218,7 +256,7 @@ e2eTest('queue advanced: footer emission', () => {
|
|
|
218
256
|
if (idx <= followupUserIdx) {
|
|
219
257
|
return false
|
|
220
258
|
}
|
|
221
|
-
return m.author.id === ctx.discord.botUserId && m.
|
|
259
|
+
return m.author.id === ctx.discord.botUserId && getMessageVisibleText(m).includes('ok')
|
|
222
260
|
})
|
|
223
261
|
|
|
224
262
|
await waitForFooterMessage({
|
|
@@ -233,18 +271,18 @@ e2eTest('queue advanced: footer emission', () => {
|
|
|
233
271
|
"--- from: user (queue-advanced-tester)
|
|
234
272
|
Reply with exactly: interrupt-footer-setup
|
|
235
273
|
--- from: assistant (TestBot)
|
|
236
|
-
*using deterministic-provider/deterministic-v2*
|
|
237
|
-
|
|
238
|
-
*project ⋅ main ⋅
|
|
274
|
+
> *using deterministic-provider/deterministic-v2*
|
|
275
|
+
> ok
|
|
276
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>
|
|
239
277
|
--- from: user (queue-advanced-tester)
|
|
240
278
|
PLUGIN_TIMEOUT_SLEEP_MARKER
|
|
241
279
|
--- from: assistant (TestBot)
|
|
242
|
-
|
|
280
|
+
> starting sleep 100
|
|
243
281
|
--- from: user (queue-advanced-tester)
|
|
244
282
|
Reply with exactly: interrupt-footer-followup
|
|
245
283
|
--- from: assistant (TestBot)
|
|
246
|
-
|
|
247
|
-
*project ⋅ main ⋅
|
|
284
|
+
> ok
|
|
285
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
248
286
|
`)
|
|
249
287
|
expect(followupUserIdx).toBeGreaterThanOrEqual(0)
|
|
250
288
|
expect(okReplyIdx).toBeGreaterThan(followupUserIdx)
|
|
@@ -254,8 +292,7 @@ e2eTest('queue advanced: footer emission', () => {
|
|
|
254
292
|
return false
|
|
255
293
|
}
|
|
256
294
|
return m.author.id === ctx.discord.botUserId
|
|
257
|
-
&& m.
|
|
258
|
-
&& m.content.includes('⋅')
|
|
295
|
+
&& isFooterMessage({ message: m, botUserId: ctx.discord.botUserId })
|
|
259
296
|
})
|
|
260
297
|
expect(footerBetween).toBe(false)
|
|
261
298
|
},
|
|
@@ -263,120 +300,8 @@ e2eTest('queue advanced: footer emission', () => {
|
|
|
263
300
|
)
|
|
264
301
|
|
|
265
302
|
test(
|
|
266
|
-
'
|
|
303
|
+
'tool-call turn emits a footer after the final follow-up',
|
|
267
304
|
async () => {
|
|
268
|
-
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
269
|
-
content: 'Reply with exactly: plugin-timeout-setup',
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
const thread = await ctx.discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
273
|
-
timeout: 4_000,
|
|
274
|
-
predicate: (t) => {
|
|
275
|
-
return t.name === 'Reply with exactly: plugin-timeout-setup'
|
|
276
|
-
},
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
const th = ctx.discord.thread(thread.id)
|
|
280
|
-
await th.waitForBotReply({ timeout: 4_000 })
|
|
281
|
-
await waitForBotMessageContaining({
|
|
282
|
-
discord: ctx.discord,
|
|
283
|
-
threadId: thread.id,
|
|
284
|
-
userId: TEST_USER_ID,
|
|
285
|
-
text: '*project',
|
|
286
|
-
timeout: 4_000,
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
await th.user(TEST_USER_ID).sendMessage({
|
|
290
|
-
content: 'PLUGIN_TIMEOUT_SLEEP_MARKER',
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
await waitForBotMessageContaining({
|
|
294
|
-
discord: ctx.discord,
|
|
295
|
-
threadId: thread.id,
|
|
296
|
-
userId: TEST_USER_ID,
|
|
297
|
-
text: 'starting sleep 100',
|
|
298
|
-
afterUserMessageIncludes: 'PLUGIN_TIMEOUT_SLEEP_MARKER',
|
|
299
|
-
timeout: 4_000,
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
await th.user(TEST_USER_ID).sendMessage({
|
|
303
|
-
content: 'Reply with exactly: plugin-timeout-after',
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
const messages = await waitForBotMessageContaining({
|
|
307
|
-
discord: ctx.discord,
|
|
308
|
-
threadId: thread.id,
|
|
309
|
-
userId: TEST_USER_ID,
|
|
310
|
-
text: 'ok',
|
|
311
|
-
afterUserMessageIncludes: 'plugin-timeout-after',
|
|
312
|
-
timeout: 12_000,
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
const messagesWithFooter = await waitForFooterMessage({
|
|
316
|
-
discord: ctx.discord,
|
|
317
|
-
threadId: thread.id,
|
|
318
|
-
timeout: 12_000,
|
|
319
|
-
afterMessageIncludes: 'plugin-timeout-after',
|
|
320
|
-
afterAuthorId: TEST_USER_ID,
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
const afterIndex = messagesWithFooter.findIndex((message) => {
|
|
324
|
-
return (
|
|
325
|
-
message.author.id === TEST_USER_ID
|
|
326
|
-
&& message.content.includes('plugin-timeout-after')
|
|
327
|
-
)
|
|
328
|
-
})
|
|
329
|
-
expect(await th.text()).toMatchInlineSnapshot(`
|
|
330
|
-
"--- from: user (queue-advanced-tester)
|
|
331
|
-
Reply with exactly: plugin-timeout-setup
|
|
332
|
-
--- from: assistant (TestBot)
|
|
333
|
-
*using deterministic-provider/deterministic-v2*
|
|
334
|
-
⬥ ok
|
|
335
|
-
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
336
|
-
--- from: user (queue-advanced-tester)
|
|
337
|
-
PLUGIN_TIMEOUT_SLEEP_MARKER
|
|
338
|
-
--- from: assistant (TestBot)
|
|
339
|
-
⬥ starting sleep 100
|
|
340
|
-
--- from: user (queue-advanced-tester)
|
|
341
|
-
Reply with exactly: plugin-timeout-after
|
|
342
|
-
--- from: assistant (TestBot)
|
|
343
|
-
⬥ ok
|
|
344
|
-
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
345
|
-
`)
|
|
346
|
-
expect(afterIndex).toBeGreaterThanOrEqual(0)
|
|
347
|
-
|
|
348
|
-
const okReplyIndex = messagesWithFooter.findIndex((message, index) => {
|
|
349
|
-
if (index <= afterIndex) {
|
|
350
|
-
return false
|
|
351
|
-
}
|
|
352
|
-
return message.author.id === ctx.discord.botUserId && message.content.includes('ok')
|
|
353
|
-
})
|
|
354
|
-
expect(okReplyIndex).toBeGreaterThan(afterIndex)
|
|
355
|
-
|
|
356
|
-
const footerBeforeReply = messagesWithFooter.some((message, index) => {
|
|
357
|
-
if (index <= afterIndex || index >= okReplyIndex) {
|
|
358
|
-
return false
|
|
359
|
-
}
|
|
360
|
-
if (message.author.id !== ctx.discord.botUserId) {
|
|
361
|
-
return false
|
|
362
|
-
}
|
|
363
|
-
return message.content.startsWith('*') && message.content.includes('⋅')
|
|
364
|
-
})
|
|
365
|
-
expect(footerBeforeReply).toBe(false)
|
|
366
|
-
},
|
|
367
|
-
15_000,
|
|
368
|
-
)
|
|
369
|
-
|
|
370
|
-
test(
|
|
371
|
-
'tool-call assistant message gets footer when it completes normally',
|
|
372
|
-
async () => {
|
|
373
|
-
// Reproduces the bug: model responds with text + tool call,
|
|
374
|
-
// finish="tool-calls", message gets completed timestamp. Then the tool
|
|
375
|
-
// result triggers a follow-up text response in a second assistant message.
|
|
376
|
-
// The second message gets a footer, but the first (tool-call) message
|
|
377
|
-
// should ALSO get a footer since it completed normally.
|
|
378
|
-
// This matches the real-world scenario where an agent calls a bash tool
|
|
379
|
-
// (e.g. `kimaki send`) and then follows up with a summary text.
|
|
380
305
|
const existingThreadIds = new Set(
|
|
381
306
|
(await ctx.discord.channel(TEXT_CHANNEL_ID).getThreads()).map((thread) => {
|
|
382
307
|
return thread.id
|
|
@@ -413,193 +338,20 @@ e2eTest('queue advanced: footer emission', () => {
|
|
|
413
338
|
timeout: 4_000,
|
|
414
339
|
})
|
|
415
340
|
|
|
416
|
-
// Poll until both footers have arrived — the first footer (after the
|
|
417
|
-
// tool-call step) and the second (after the text follow-up) are emitted
|
|
418
|
-
// by sequential handleNaturalAssistantCompletion calls but the second
|
|
419
|
-
// may not have hit the Discord thread by the time we first check.
|
|
420
|
-
const deadline = Date.now() + 4_000
|
|
421
|
-
let footerCount = 0
|
|
422
|
-
while (Date.now() < deadline) {
|
|
423
|
-
const msgs = await th.getMessages()
|
|
424
|
-
footerCount = msgs.filter((m) => {
|
|
425
|
-
return m.author.id === ctx.discord.botUserId
|
|
426
|
-
&& m.content.startsWith('*')
|
|
427
|
-
&& m.content.includes('⋅')
|
|
428
|
-
}).length
|
|
429
|
-
if (footerCount >= 2) {
|
|
430
|
-
break
|
|
431
|
-
}
|
|
432
|
-
await new Promise((resolve) => {
|
|
433
|
-
setTimeout(resolve, 100)
|
|
434
|
-
})
|
|
435
|
-
}
|
|
436
|
-
|
|
437
341
|
expect(await th.text()).toMatchInlineSnapshot(`
|
|
438
342
|
"--- from: user (queue-advanced-tester)
|
|
439
343
|
TOOL_CALL_FOOTER_MARKER
|
|
440
344
|
--- from: assistant (TestBot)
|
|
441
|
-
*using deterministic-provider/deterministic-v2*
|
|
442
|
-
|
|
443
|
-
┣ bash _echo tool-call-footer-test_
|
|
444
|
-
⬥ tool call completed
|
|
445
|
-
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
446
|
-
`)
|
|
447
|
-
|
|
448
|
-
// Only ONE footer at the end — the tool-call step's footer is NOT
|
|
449
|
-
// emitted mid-turn. The final text follow-up gets the footer.
|
|
450
|
-
expect(footerCount).toBe(1)
|
|
451
|
-
},
|
|
452
|
-
10_000,
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
test(
|
|
456
|
-
'multi-step tool chain should only have one footer at the end',
|
|
457
|
-
async () => {
|
|
458
|
-
// Model does 3 sequential tool calls (each a separate assistant message
|
|
459
|
-
// with finish="tool-calls") then a final text response. Only the final
|
|
460
|
-
// text response should get a footer — intermediate tool-call steps
|
|
461
|
-
// should NOT get footers since they're mid-turn work.
|
|
462
|
-
const existingThreadIds = new Set(
|
|
463
|
-
(await ctx.discord.channel(TEXT_CHANNEL_ID).getThreads()).map((thread) => {
|
|
464
|
-
return thread.id
|
|
465
|
-
}),
|
|
466
|
-
)
|
|
467
|
-
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
468
|
-
content: 'MULTI_TOOL_FOOTER_MARKER',
|
|
469
|
-
})
|
|
345
|
+
> *using deterministic-provider/deterministic-v2*
|
|
346
|
+
> running tool
|
|
470
347
|
|
|
471
|
-
|
|
472
|
-
timeout: 6_000,
|
|
473
|
-
predicate: (t) => {
|
|
474
|
-
return !existingThreadIds.has(t.id)
|
|
475
|
-
},
|
|
476
|
-
})
|
|
477
|
-
|
|
478
|
-
const th = ctx.discord.thread(thread.id)
|
|
479
|
-
|
|
480
|
-
// Wait for the final text response after all 3 tool steps
|
|
481
|
-
await waitForBotMessageContaining({
|
|
482
|
-
discord: ctx.discord,
|
|
483
|
-
threadId: thread.id,
|
|
484
|
-
userId: TEST_USER_ID,
|
|
485
|
-
text: 'all done, fixed 3 files',
|
|
486
|
-
timeout: 6_000,
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
// Wait for the footer after the final response
|
|
490
|
-
await waitForFooterMessage({
|
|
491
|
-
discord: ctx.discord,
|
|
492
|
-
threadId: thread.id,
|
|
493
|
-
timeout: 6_000,
|
|
494
|
-
})
|
|
495
|
-
|
|
496
|
-
// Give any spurious extra footers time to arrive
|
|
497
|
-
await new Promise((resolve) => {
|
|
498
|
-
setTimeout(resolve, 500)
|
|
499
|
-
})
|
|
500
|
-
|
|
501
|
-
const messages = await th.getMessages()
|
|
502
|
-
const footerCount = messages.filter((m) => {
|
|
503
|
-
return m.author.id === ctx.discord.botUserId
|
|
504
|
-
&& m.content.startsWith('*')
|
|
505
|
-
&& m.content.includes('⋅')
|
|
506
|
-
}).length
|
|
348
|
+
┣ bash _echo tool-call-footer-test_
|
|
507
349
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
MULTI_TOOL_FOOTER_MARKER
|
|
511
|
-
--- from: assistant (TestBot)
|
|
512
|
-
*using deterministic-provider/deterministic-v2*
|
|
513
|
-
⬥ investigating the issue
|
|
514
|
-
┣ bash _echo search-done_
|
|
515
|
-
┣ bash _echo read-done_
|
|
516
|
-
┣ bash _echo fix-done_
|
|
517
|
-
⬥ all done, fixed 3 files
|
|
518
|
-
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
350
|
+
> tool call completed
|
|
351
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
519
352
|
`)
|
|
520
|
-
|
|
521
|
-
// Only ONE footer should appear — after the final text response.
|
|
522
|
-
// Intermediate tool-call steps should NOT get footers.
|
|
523
|
-
expect(footerCount).toBe(1)
|
|
524
353
|
},
|
|
525
354
|
10_000,
|
|
526
355
|
)
|
|
527
356
|
|
|
528
|
-
test(
|
|
529
|
-
'3 sequential tool-call steps produce exactly 1 footer, not 3',
|
|
530
|
-
async () => {
|
|
531
|
-
// This is the most obvious reproduction of the multi-footer bug:
|
|
532
|
-
// the model runs 3 sequential tool-call steps (each a SEPARATE
|
|
533
|
-
// assistant message with finish="tool-calls"), then a final text.
|
|
534
|
-
// With a naive fix that treats tool-calls as natural completions,
|
|
535
|
-
// you'd see 4 footers (one per assistant message). Only the final
|
|
536
|
-
// text response should produce a footer.
|
|
537
|
-
const existingThreadIds = new Set(
|
|
538
|
-
(await ctx.discord.channel(TEXT_CHANNEL_ID).getThreads()).map((thread) => {
|
|
539
|
-
return thread.id
|
|
540
|
-
}),
|
|
541
|
-
)
|
|
542
|
-
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
543
|
-
content: 'MULTI_STEP_CHAIN_MARKER',
|
|
544
|
-
})
|
|
545
|
-
|
|
546
|
-
const thread = await ctx.discord.channel(TEXT_CHANNEL_ID).waitForThread({
|
|
547
|
-
timeout: 6_000,
|
|
548
|
-
predicate: (t) => {
|
|
549
|
-
return !existingThreadIds.has(t.id)
|
|
550
|
-
},
|
|
551
|
-
})
|
|
552
|
-
|
|
553
|
-
const th = ctx.discord.thread(thread.id)
|
|
554
|
-
|
|
555
|
-
// Wait for the final text after all 3 sequential tool steps
|
|
556
|
-
await waitForBotMessageContaining({
|
|
557
|
-
discord: ctx.discord,
|
|
558
|
-
threadId: thread.id,
|
|
559
|
-
userId: TEST_USER_ID,
|
|
560
|
-
text: 'chain complete: all 3 steps done',
|
|
561
|
-
timeout: 10_000,
|
|
562
|
-
})
|
|
563
|
-
|
|
564
|
-
// Wait for footer
|
|
565
|
-
await waitForFooterMessage({
|
|
566
|
-
discord: ctx.discord,
|
|
567
|
-
threadId: thread.id,
|
|
568
|
-
timeout: 6_000,
|
|
569
|
-
})
|
|
570
|
-
|
|
571
|
-
// Give any spurious extra footers time to arrive
|
|
572
|
-
await new Promise((resolve) => {
|
|
573
|
-
setTimeout(resolve, 500)
|
|
574
|
-
})
|
|
575
|
-
|
|
576
|
-
const messages = await th.getMessages()
|
|
577
|
-
const footerCount = messages.filter((m) => {
|
|
578
|
-
return m.author.id === ctx.discord.botUserId
|
|
579
|
-
&& m.content.startsWith('*')
|
|
580
|
-
&& m.content.includes('⋅')
|
|
581
|
-
}).length
|
|
582
|
-
|
|
583
|
-
expect(await th.text()).toMatchInlineSnapshot(`
|
|
584
|
-
"--- from: user (queue-advanced-tester)
|
|
585
|
-
MULTI_STEP_CHAIN_MARKER
|
|
586
|
-
--- from: assistant (TestBot)
|
|
587
|
-
*using deterministic-provider/deterministic-v2*
|
|
588
|
-
⬥ chain step 1: reading config
|
|
589
|
-
┣ bash _echo chain-step-1-output_
|
|
590
|
-
⬥ chain step 2: analyzing results
|
|
591
|
-
┣ bash _echo chain-step-2-output_
|
|
592
|
-
⬥ chain step 3: applying fix
|
|
593
|
-
┣ bash _echo chain-step-3-output_
|
|
594
|
-
⬥ chain complete: all 3 steps done
|
|
595
|
-
*project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
596
|
-
`)
|
|
597
|
-
|
|
598
|
-
// The critical assertion: only 1 footer at the very end.
|
|
599
|
-
// With the naive "allow tool-calls as natural completion" fix,
|
|
600
|
-
// this would be 4 (one per assistant message). We want 1.
|
|
601
|
-
expect(footerCount).toBe(1)
|
|
602
|
-
},
|
|
603
|
-
15_000,
|
|
604
|
-
)
|
|
605
357
|
})
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
TEST_USER_ID,
|
|
12
12
|
} from './queue-advanced-e2e-setup.js'
|
|
13
13
|
import {
|
|
14
|
+
isFooterMessage,
|
|
14
15
|
waitForBotMessageContaining,
|
|
15
16
|
waitForBotReplyAfterUserMessage,
|
|
16
17
|
waitForFooterMessage,
|
|
@@ -320,17 +321,15 @@ describe('queue advanced: /model with interrupt recovery', () => {
|
|
|
320
321
|
})
|
|
321
322
|
|
|
322
323
|
const footer = [...finalMessages].reverse().find((message) => {
|
|
323
|
-
return message
|
|
324
|
-
&& message.content.startsWith('*')
|
|
325
|
-
&& message.content.includes('⋅')
|
|
324
|
+
return isFooterMessage({ message, botUserId: ctx.discord.botUserId })
|
|
326
325
|
})
|
|
327
326
|
expect(await th.text()).toMatchInlineSnapshot(`
|
|
328
327
|
"--- from: user (queue-model-switch-tester)
|
|
329
328
|
Reply with exactly: model-switcher-setup
|
|
330
329
|
--- from: assistant (TestBot)
|
|
331
|
-
*using deterministic-provider/deterministic-v2*
|
|
332
|
-
|
|
333
|
-
*project ⋅ main ⋅
|
|
330
|
+
> *using deterministic-provider/deterministic-v2*
|
|
331
|
+
> ok
|
|
332
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>
|
|
334
333
|
Model set for this session:
|
|
335
334
|
**Deterministic Provider** / **deterministic-v3**
|
|
336
335
|
\`deterministic-provider/deterministic-v3\`
|
|
@@ -339,13 +338,13 @@ describe('queue advanced: /model with interrupt recovery', () => {
|
|
|
339
338
|
--- from: user (queue-model-switch-tester)
|
|
340
339
|
PLUGIN_TIMEOUT_SLEEP_MARKER
|
|
341
340
|
--- from: assistant (TestBot)
|
|
342
|
-
|
|
343
|
-
|
|
341
|
+
> ok
|
|
342
|
+
> starting sleep 100
|
|
344
343
|
--- from: user (queue-model-switch-tester)
|
|
345
344
|
Reply with exactly: model-switcher-followup
|
|
346
345
|
--- from: assistant (TestBot)
|
|
347
|
-
|
|
348
|
-
*project ⋅ main ⋅
|
|
346
|
+
> ok
|
|
347
|
+
> *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v3* <@200000000000000991>"
|
|
349
348
|
`)
|
|
350
349
|
|
|
351
350
|
expect(footer).toBeDefined()
|
|
@@ -51,7 +51,7 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
51
51
|
})
|
|
52
52
|
|
|
53
53
|
test(
|
|
54
|
-
'permission prompt pauses typing until user click, then typing resumes
|
|
54
|
+
'permission prompt pauses typing until user click, then typing resumes',
|
|
55
55
|
async () => {
|
|
56
56
|
await ctx.discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
|
|
57
57
|
content: 'PERMISSION_TYPING_MARKER',
|
|
@@ -83,7 +83,7 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
83
83
|
|
|
84
84
|
th.clearTypingEvents()
|
|
85
85
|
|
|
86
|
-
await th.waitForTypingEvent({ timeout:
|
|
86
|
+
await th.waitForTypingEvent({ timeout: 700 }).then(
|
|
87
87
|
() => {
|
|
88
88
|
throw new Error('Typing should stay paused while permission UI is pending')
|
|
89
89
|
},
|
|
@@ -102,7 +102,7 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
102
102
|
timeout: 4_000,
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
-
const resumedTyping = await th.waitForTypingEvent({ timeout:
|
|
105
|
+
const resumedTyping = await th.waitForTypingEvent({ timeout: 2_000 })
|
|
106
106
|
expect(resumedTyping).toBeDefined()
|
|
107
107
|
|
|
108
108
|
await waitForBotMessageContaining({
|
|
@@ -110,13 +110,13 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
110
110
|
threadId: thread.id,
|
|
111
111
|
userId: TEST_USER_ID,
|
|
112
112
|
text: 'permission-flow-done',
|
|
113
|
-
timeout:
|
|
113
|
+
timeout: 4_000,
|
|
114
114
|
})
|
|
115
115
|
|
|
116
116
|
await waitForFooterMessage({
|
|
117
117
|
discord: ctx.discord,
|
|
118
118
|
threadId: thread.id,
|
|
119
|
-
timeout:
|
|
119
|
+
timeout: 4_000,
|
|
120
120
|
afterMessageIncludes: 'permission-flow-done',
|
|
121
121
|
afterAuthorId: ctx.discord.botUserId,
|
|
122
122
|
})
|
|
@@ -125,8 +125,9 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
125
125
|
"--- from: user (queue-permission-tester)
|
|
126
126
|
PERMISSION_TYPING_MARKER
|
|
127
127
|
--- from: assistant (TestBot)
|
|
128
|
-
*using deterministic-provider/deterministic-v2*
|
|
129
|
-
|
|
128
|
+
> *using deterministic-provider/deterministic-v2*
|
|
129
|
+
> requesting external read permission
|
|
130
|
+
|
|
130
131
|
┣ read *.zprofile*
|
|
131
132
|
⚠️ **Permission Required**
|
|
132
133
|
**Type:** \`external_directory\`
|
|
@@ -134,8 +135,9 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
134
135
|
**Pattern:** \`/Users/morse/*\`
|
|
135
136
|
✅ Permission **accepted**
|
|
136
137
|
[user clicks button]
|
|
137
|
-
|
|
138
|
-
|
|
138
|
+
|
|
139
|
+
> permission-flow-done
|
|
140
|
+
> *project ⋅ main ⋅ 2s ⋅ 0% ⋅ deterministic-v2* <@200000000000000991>"
|
|
139
141
|
`)
|
|
140
142
|
|
|
141
143
|
const timeline = await th.text({
|
|
@@ -143,19 +145,17 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
143
145
|
showInteractions: true,
|
|
144
146
|
})
|
|
145
147
|
const clickPosition = timeline.indexOf('[user clicks button]')
|
|
146
|
-
const donePosition = timeline.indexOf('
|
|
148
|
+
const donePosition = timeline.indexOf('permission-flow-done')
|
|
147
149
|
const footerPosition = timeline.lastIndexOf('*project ⋅')
|
|
148
150
|
expect(clickPosition).toBeGreaterThanOrEqual(0)
|
|
149
151
|
expect(donePosition).toBeGreaterThan(clickPosition)
|
|
150
152
|
expect(footerPosition).toBeGreaterThan(donePosition)
|
|
151
153
|
|
|
152
154
|
const afterClick = timeline.slice(clickPosition, donePosition)
|
|
153
|
-
const afterDone = timeline.slice(donePosition, footerPosition)
|
|
154
155
|
expect(afterClick).toContain('[bot typing]')
|
|
155
|
-
expect(afterDone).toContain('[bot typing]')
|
|
156
156
|
expect(timeline.slice(footerPosition)).not.toContain('[bot typing]')
|
|
157
157
|
},
|
|
158
|
-
|
|
158
|
+
12_000,
|
|
159
159
|
)
|
|
160
160
|
|
|
161
161
|
test(
|
|
@@ -232,7 +232,7 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
232
232
|
|
|
233
233
|
const timeline = await th.text({ showInteractions: true })
|
|
234
234
|
const normalizedTimeline = timeline.replace(
|
|
235
|
-
'
|
|
235
|
+
'requesting external read permission\n',
|
|
236
236
|
'',
|
|
237
237
|
)
|
|
238
238
|
expect(normalizedTimeline).toContain('PERMISSION_TYPING_MARKER dismiss-flow')
|
|
@@ -242,7 +242,7 @@ describe('queue advanced: typing around permissions', () => {
|
|
|
242
242
|
const followupUserPosition = normalizedTimeline.indexOf(
|
|
243
243
|
'Reply with exactly: post-permission-user-message',
|
|
244
244
|
)
|
|
245
|
-
const followupReplyPosition = normalizedTimeline.indexOf('
|
|
245
|
+
const followupReplyPosition = normalizedTimeline.indexOf('ok', followupUserPosition)
|
|
246
246
|
const followupFooterPosition = normalizedTimeline.indexOf(
|
|
247
247
|
'*project ⋅',
|
|
248
248
|
followupReplyPosition,
|