kimaki 0.28.0 → 0.30.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.
Files changed (179) hide show
  1. package/dist/agent-model.e2e.test.js +33 -33
  2. package/dist/anthropic-account-identity.js +1 -1
  3. package/dist/anthropic-auth-plugin.js +5 -5
  4. package/dist/anthropic-auth-plugin.test.js +30 -2
  5. package/dist/anthropic-auth-state.js +1 -1
  6. package/dist/bash-tool-schema-plugin.js +18 -0
  7. package/dist/bash-tool-schema-plugin.test.js +20 -1
  8. package/dist/bin.js +7 -3
  9. package/dist/bot-lifecycle.test.js +206 -0
  10. package/dist/cache-drift-plugin.js +25 -18
  11. package/dist/cache-rewrite.js +39 -0
  12. package/dist/cache-rewrite.test.js +41 -0
  13. package/dist/channel-management.js +113 -51
  14. package/dist/cli-commands/misc.js +4 -1
  15. package/dist/cli-commands/project.js +7 -4
  16. package/dist/cli-commands/session.js +118 -36
  17. package/dist/cli-commands/thread.js +63 -0
  18. package/dist/cli-parsing.test.js +3 -3
  19. package/dist/cli-runner.js +11 -1
  20. package/dist/cli-send-thread.e2e.test.js +2 -2
  21. package/dist/cli.js +6 -4
  22. package/dist/commands/ask-question.js +72 -23
  23. package/dist/commands/queue.js +84 -36
  24. package/dist/commands/queue.test.js +15 -0
  25. package/dist/commands/restart-opencode-server.js +7 -1
  26. package/dist/commands/session-id.js +5 -5
  27. package/dist/database.js +149 -6
  28. package/dist/db.js +34 -0
  29. package/dist/db.test.js +199 -1
  30. package/dist/default-channel-provisioning.test.js +2 -1
  31. package/dist/discord-bot.js +220 -146
  32. package/dist/discord-command-registration.js +1 -1
  33. package/dist/external-directory-permissions.e2e.test.js +3 -3
  34. package/dist/external-directory-project-deny.e2e.test.js +1 -1
  35. package/dist/forum-sync/watchers.js +15 -0
  36. package/dist/gateway-proxy.e2e.test.js +9 -9
  37. package/dist/guild-categories.test.js +225 -0
  38. package/dist/hrana-server.js +68 -14
  39. package/dist/interaction-handler.js +5 -1
  40. package/dist/kimaki-opencode-plugin.js +2 -0
  41. package/dist/kimaki-opencode-plugin.test.js +28 -0
  42. package/dist/list-channel-threads.js +163 -0
  43. package/dist/list-channel-threads.test.js +80 -0
  44. package/dist/markdown.js +164 -102
  45. package/dist/markdown.test.js +88 -20
  46. package/dist/message-formatting.js +29 -18
  47. package/dist/message-formatting.test.js +111 -3
  48. package/dist/opencode.js +27 -12
  49. package/dist/question-abort-clears-dropdown.e2e.test.js +1 -1
  50. package/dist/question-abort-resume.e2e.test.js +100 -0
  51. package/dist/question-abort-retry-text-before-dropdown.e2e.test.js +1 -1
  52. package/dist/question-text-before-dropdown.e2e.test.js +1 -1
  53. package/dist/queue-advanced-abort.e2e.test.js +3 -3
  54. package/dist/queue-advanced-action-buttons.e2e.test.js +5 -5
  55. package/dist/queue-advanced-e2e-setup.js +63 -0
  56. package/dist/queue-advanced-footer.e2e.test.js +18 -18
  57. package/dist/queue-advanced-model-switch.e2e.test.js +3 -4
  58. package/dist/queue-advanced-permissions-typing.e2e.test.js +2 -2
  59. package/dist/queue-advanced-typing-interrupt.e2e.test.js +3 -3
  60. package/dist/queue-delete-message.e2e.test.js +4 -4
  61. package/dist/queue-drain-after-interactive-ui.e2e.test.js +3 -3
  62. package/dist/queue-interrupt-drain.e2e.test.js +10 -10
  63. package/dist/queue-question-select-drain.e2e.test.js +5 -5
  64. package/dist/runtime-lifecycle.e2e.test.js +95 -11
  65. package/dist/schema.js +21 -0
  66. package/dist/session-handler/event-stream-state.js +240 -6
  67. package/dist/session-handler/event-stream-state.test.js +1116 -130
  68. package/dist/session-handler/thread-runtime-state.js +4 -0
  69. package/dist/session-handler/thread-session-runtime.js +499 -89
  70. package/dist/sleep.e2e.test.js +7 -7
  71. package/dist/store.js +1 -1
  72. package/dist/system-message.js +64 -23
  73. package/dist/system-message.test.js +69 -23
  74. package/dist/task-id-plugin.js +16 -0
  75. package/dist/task-runner.js +7 -0
  76. package/dist/task-schedule.test.js +1 -1
  77. package/dist/test-utils.js +5 -3
  78. package/dist/thread-message-queue.e2e.test.js +30 -31
  79. package/dist/undo-redo.e2e.test.js +3 -3
  80. package/dist/voice-message.e2e.test.js +64 -26
  81. package/dist/voice-routing-input.e2e.test.js +4 -4
  82. package/dist/voice-routing-question.e2e.test.js +4 -4
  83. package/dist/voice.js +107 -21
  84. package/dist/voice.test.js +52 -1
  85. package/dist/wait-session.js +24 -2
  86. package/dist/worktree-lifecycle.e2e.test.js +8 -8
  87. package/package.json +7 -7
  88. package/skills/egaki/SKILL.md +14 -0
  89. package/skills/holocron/SKILL.md +125 -42
  90. package/skills/opensrc/SKILL.md +20 -3
  91. package/skills/spiceflow/SKILL.md +53 -172
  92. package/skills/strada/SKILL.md +61 -232
  93. package/src/agent-model.e2e.test.ts +33 -33
  94. package/src/anthropic-account-identity.ts +1 -1
  95. package/src/anthropic-auth-plugin.test.ts +37 -2
  96. package/src/anthropic-auth-plugin.ts +9 -7
  97. package/src/anthropic-auth-state.ts +1 -0
  98. package/src/bash-tool-schema-plugin.test.ts +29 -0
  99. package/src/bash-tool-schema-plugin.ts +31 -0
  100. package/src/bin.ts +7 -3
  101. package/src/bot-lifecycle.test.ts +252 -0
  102. package/src/cache-drift-plugin.ts +34 -24
  103. package/src/cache-rewrite.test.ts +49 -0
  104. package/src/cache-rewrite.ts +96 -0
  105. package/src/channel-management.ts +144 -65
  106. package/src/cli-commands/misc.ts +4 -1
  107. package/src/cli-commands/project.ts +9 -4
  108. package/src/cli-commands/session.ts +167 -38
  109. package/src/cli-commands/thread.ts +86 -0
  110. package/src/cli-parsing.test.ts +3 -3
  111. package/src/cli-runner.ts +11 -0
  112. package/src/cli-send-thread.e2e.test.ts +2 -2
  113. package/src/cli.ts +8 -6
  114. package/src/commands/ask-question.ts +88 -25
  115. package/src/commands/queue.test.ts +19 -0
  116. package/src/commands/queue.ts +111 -45
  117. package/src/commands/restart-opencode-server.ts +12 -1
  118. package/src/commands/session-id.ts +5 -5
  119. package/src/database.ts +219 -6
  120. package/src/db.test.ts +236 -0
  121. package/src/db.ts +38 -0
  122. package/src/default-channel-provisioning.test.ts +2 -1
  123. package/src/discord-bot.ts +272 -185
  124. package/src/discord-command-registration.ts +3 -1
  125. package/src/external-directory-permissions.e2e.test.ts +3 -3
  126. package/src/external-directory-project-deny.e2e.test.ts +1 -1
  127. package/src/forum-sync/watchers.ts +20 -0
  128. package/src/gateway-proxy.e2e.test.ts +9 -9
  129. package/src/guild-categories.test.ts +260 -0
  130. package/src/hrana-server.ts +96 -16
  131. package/src/interaction-handler.ts +7 -0
  132. package/src/kimaki-opencode-plugin.test.ts +34 -0
  133. package/src/kimaki-opencode-plugin.ts +2 -0
  134. package/src/list-channel-threads.test.ts +90 -0
  135. package/src/list-channel-threads.ts +252 -0
  136. package/src/markdown.test.ts +108 -22
  137. package/src/markdown.ts +216 -103
  138. package/src/message-formatting.test.ts +129 -2
  139. package/src/message-formatting.ts +28 -14
  140. package/src/opencode.ts +28 -13
  141. package/src/question-abort-clears-dropdown.e2e.test.ts +1 -1
  142. package/src/question-abort-resume.e2e.test.ts +121 -0
  143. package/src/question-abort-retry-text-before-dropdown.e2e.test.ts +1 -1
  144. package/src/question-text-before-dropdown.e2e.test.ts +1 -1
  145. package/src/queue-advanced-abort.e2e.test.ts +3 -3
  146. package/src/queue-advanced-action-buttons.e2e.test.ts +5 -5
  147. package/src/queue-advanced-e2e-setup.ts +65 -0
  148. package/src/queue-advanced-footer.e2e.test.ts +18 -18
  149. package/src/queue-advanced-model-switch.e2e.test.ts +3 -4
  150. package/src/queue-advanced-permissions-typing.e2e.test.ts +2 -2
  151. package/src/queue-advanced-typing-interrupt.e2e.test.ts +3 -3
  152. package/src/queue-delete-message.e2e.test.ts +4 -4
  153. package/src/queue-drain-after-interactive-ui.e2e.test.ts +3 -3
  154. package/src/queue-interrupt-drain.e2e.test.ts +10 -10
  155. package/src/queue-question-select-drain.e2e.test.ts +5 -5
  156. package/src/runtime-lifecycle.e2e.test.ts +104 -10
  157. package/src/schema.sql +16 -0
  158. package/src/schema.ts +23 -0
  159. package/src/session-handler/event-stream-state.test.ts +1184 -133
  160. package/src/session-handler/event-stream-state.ts +322 -8
  161. package/src/session-handler/thread-runtime-state.ts +5 -0
  162. package/src/session-handler/thread-session-runtime.ts +597 -95
  163. package/src/sleep.e2e.test.ts +7 -7
  164. package/src/store.ts +2 -2
  165. package/src/system-message.test.ts +70 -23
  166. package/src/system-message.ts +67 -22
  167. package/src/task-id-plugin.ts +17 -0
  168. package/src/task-runner.ts +8 -0
  169. package/src/task-schedule.test.ts +1 -1
  170. package/src/test-utils.ts +5 -3
  171. package/src/thread-message-queue.e2e.test.ts +30 -31
  172. package/src/undo-redo.e2e.test.ts +3 -3
  173. package/src/voice-message.e2e.test.ts +70 -26
  174. package/src/voice-routing-input.e2e.test.ts +4 -4
  175. package/src/voice-routing-question.e2e.test.ts +4 -4
  176. package/src/voice.test.ts +56 -0
  177. package/src/voice.ts +135 -25
  178. package/src/wait-session.ts +25 -2
  179. package/src/worktree-lifecycle.e2e.test.ts +8 -8
package/src/voice.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  // Audio transcription service using AI SDK providers.
2
2
  // Both providers use LanguageModelV3 (chat model) with audio file parts + tool calling,
3
3
  // so we can pass full context (file tree, session info) for better word recognition.
4
- // - OpenAI: gpt-4o-audio-preview via .chat() (Chat Completions API). MUST use .chat()
5
- // because the default Responses API doesn't support audio file parts. The Chat
6
- // Completions handler converts audio/mpeg file parts to input_audio format.
7
- // - Gemini: gemini-2.5-flash natively accepts audio file parts in chat.
4
+ // - OpenAI: plain fetch to the Chat Completions API. The AI SDK chat schema
5
+ // rejects message.audio, so we parse transcript and tool_calls ourselves.
6
+ // https://github.com/vercel/ai/issues/21289
7
+ // - Gemini: gemini-flash-latest natively accepts audio file parts in chat.
8
8
  // Calls model.doGenerate() directly without the `ai` npm package.
9
9
  // Uses errore for type-safe error handling.
10
10
 
@@ -35,7 +35,78 @@ import {
35
35
 
36
36
  const voiceLogger = createLogger(LogPrefix.VOICE)
37
37
 
38
- // OpenAI input_audio only supports wav and mp3. Other formats (OGG Opus, etc)
38
+ const OPENAI_AUDIO_CHAT_MODEL = 'gpt-audio-1.5'
39
+ // https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create
40
+ const OPENAI_CHAT_COMPLETIONS_URL = 'https://api.openai.com/v1/chat/completions'
41
+
42
+ export async function requestOpenAIAudioTranscription({
43
+ apiKey,
44
+ prompt,
45
+ audioBase64,
46
+ mediaType,
47
+ temperature,
48
+ tool,
49
+ }: {
50
+ apiKey: string
51
+ prompt: string
52
+ audioBase64: string
53
+ mediaType: string
54
+ temperature: number
55
+ tool: LanguageModelV3FunctionTool
56
+ }): Promise<TranscriptionLoopError | TranscriptionResult> {
57
+ const audioFormat = mediaType.includes('wav') ? 'wav' : 'mp3'
58
+ const response = await fetch(OPENAI_CHAT_COMPLETIONS_URL, {
59
+ method: 'POST',
60
+ headers: {
61
+ Authorization: `Bearer ${apiKey}`,
62
+ 'Content-Type': 'application/json',
63
+ },
64
+ body: JSON.stringify({
65
+ model: OPENAI_AUDIO_CHAT_MODEL,
66
+ temperature,
67
+ max_completion_tokens: 2048,
68
+ user: 'kimaki:voice-transcription',
69
+ safety_identifier: 'kimaki:voice-transcription',
70
+ tools: [
71
+ {
72
+ type: 'function',
73
+ function: {
74
+ name: tool.name,
75
+ description: tool.description,
76
+ parameters: tool.inputSchema,
77
+ },
78
+ },
79
+ ],
80
+ tool_choice: { type: 'function', function: { name: 'transcriptionResult' } },
81
+ messages: [
82
+ {
83
+ role: 'user',
84
+ content: [
85
+ { type: 'text', text: prompt },
86
+ {
87
+ type: 'input_audio',
88
+ input_audio: { data: audioBase64, format: audioFormat },
89
+ },
90
+ ],
91
+ },
92
+ ],
93
+ }),
94
+ }).catch(
95
+ (cause) =>
96
+ new TranscriptionError({
97
+ reason: `API call failed: ${String(cause)}`,
98
+ cause,
99
+ }),
100
+ )
101
+ if (response instanceof Error) return response
102
+ const raw = await response.text()
103
+ if (!response.ok) {
104
+ return new TranscriptionError({ reason: `API call failed: ${response.status} ${raw.slice(0, 300)}` })
105
+ }
106
+ return parseOpenAIAudioChatResponse(raw)
107
+ }
108
+
109
+ // OpenAI input_audio supports only wav and mp3. Other formats (OGG Opus, etc)
39
110
  // must be converted before sending.
40
111
  const OPENAI_SUPPORTED_AUDIO_TYPES = new Set([
41
112
  'audio/mpeg',
@@ -438,6 +509,45 @@ async function runTranscriptionOnce({
438
509
  return extractTranscription(response.content)
439
510
  }
440
511
 
512
+ type OpenAIAudioChatMessage = {
513
+ content?: string | null
514
+ audio?: { transcript?: string | null } | null
515
+ tool_calls?: Array<{
516
+ id?: string
517
+ function?: { name?: string; arguments?: string }
518
+ }> | null
519
+ }
520
+
521
+ // Response shape: https://developers.openai.com/api/docs/guides/audio-chat-completions
522
+ export function parseOpenAIAudioChatResponse(
523
+ body: string,
524
+ ): TranscriptionLoopError | TranscriptionResult {
525
+ let parsed: { choices?: Array<{ message?: OpenAIAudioChatMessage }> }
526
+ try {
527
+ parsed = JSON.parse(body) as { choices?: Array<{ message?: OpenAIAudioChatMessage }> }
528
+ } catch (cause) {
529
+ return new TranscriptionError({ reason: 'Invalid JSON response', cause })
530
+ }
531
+ const message = parsed.choices?.[0]?.message
532
+ if (!message) return new NoResponseContentError()
533
+ const toolCall = message.tool_calls?.find((call) => {
534
+ return call.function?.name === 'transcriptionResult'
535
+ })
536
+ if (toolCall?.function?.arguments) {
537
+ return extractTranscription([
538
+ {
539
+ type: 'tool-call',
540
+ toolCallId: toolCall.id || 'transcriptionResult',
541
+ toolName: 'transcriptionResult',
542
+ input: toolCall.function.arguments,
543
+ },
544
+ ])
545
+ }
546
+ const transcript = message.audio?.transcript?.trim() || message.content?.trim() || ''
547
+ if (!transcript) return new NoResponseContentError()
548
+ return { transcription: transcript, queueMessage: false }
549
+ }
550
+
441
551
  export type TranscribeAudioErrors =
442
552
  | ApiKeyMissingError
443
553
  | InvalidAudioFormatError
@@ -446,32 +556,17 @@ export type TranscribeAudioErrors =
446
556
  export type TranscriptionProvider = 'openai' | 'gemini'
447
557
 
448
558
  /**
449
- * Create a LanguageModelV3 for transcription.
450
- * Both providers use chat models that accept audio file parts, so we get full
451
- * context (prompt, session info, tool calling) for better word recognition.
452
- *
453
- * OpenAI: must use .chat() to get the Chat Completions API model, because the
454
- * default callable (Responses API) doesn't support audio file parts. Use the
455
- * GA audio model instead of older gpt-4o audio preview snapshots.
456
- * Gemini: language models natively accept audio in chat.
559
+ * Create the Gemini chat model for transcription.
560
+ * OpenAI does not use this. Its chat schema rejects message.audio.
457
561
  */
458
562
  export function createTranscriptionModel({
459
563
  apiKey,
460
- provider,
461
564
  }: {
462
565
  apiKey: string
463
566
  provider?: TranscriptionProvider
464
567
  }): LanguageModelV3 {
465
- const resolvedProvider: TranscriptionProvider =
466
- provider || (apiKey.startsWith('sk-') ? 'openai' : 'gemini')
467
-
468
- if (resolvedProvider === 'openai') {
469
- const openai = createOpenAI({ apiKey })
470
- return openai.chat('gpt-audio')
471
- }
472
-
473
568
  const google = createGoogleGenerativeAI({ apiKey })
474
- return google('gemini-2.5-flash')
569
+ return google('gemini-flash-latest')
475
570
  }
476
571
 
477
572
  export async function transcribeAudio({
@@ -520,7 +615,7 @@ export async function transcribeAudio({
520
615
  return 'gemini'
521
616
  })()
522
617
 
523
- const languageModel: LanguageModelV3 =
618
+ const languageModel =
524
619
  model || createTranscriptionModel({ apiKey: apiKey!, provider: resolvedProvider })
525
620
 
526
621
  // Convert audio to Buffer for potential format conversion
@@ -653,6 +748,21 @@ Note: "critique" is a CLI tool for showing diffs in the browser.`
653
748
  const agentNames = agents
654
749
  ?.map((a) => { return a.name })
655
750
  .filter((name) => { return name.length > 0 })
751
+ const resolvedAgentNames = agentNames && agentNames.length > 0 ? agentNames : undefined
752
+
753
+ if (resolvedProvider === 'openai') {
754
+ return requestOpenAIAudioTranscription({
755
+ apiKey: apiKey!,
756
+ prompt: transcriptionPrompt,
757
+ audioBase64: finalAudioBase64,
758
+ mediaType,
759
+ temperature: temperature ?? 0.3,
760
+ tool: buildTranscriptionTool({
761
+ agentNames: resolvedAgentNames,
762
+ canForkSession,
763
+ }),
764
+ })
765
+ }
656
766
 
657
767
  return runTranscriptionOnce({
658
768
  model: languageModel,
@@ -660,7 +770,7 @@ Note: "critique" is a CLI tool for showing diffs in the browser.`
660
770
  audioBase64: finalAudioBase64,
661
771
  mediaType,
662
772
  temperature: temperature ?? 0.3,
663
- agentNames: agentNames && agentNames.length > 0 ? agentNames : undefined,
773
+ agentNames: resolvedAgentNames,
664
774
  canForkSession,
665
775
  provider: resolvedProvider,
666
776
  })
@@ -1,6 +1,8 @@
1
1
  // Wait utilities for polling session completion.
2
2
  // Used by `kimaki send --wait` and `kimaki session wait` to block until a
3
- // session is idle, interactive prompts are resolved, and output is stable.
3
+ // session completes (idle, latest turn finished naturally, no pending
4
+ // permission) OR pauses for a user question. A session parked on a `question`
5
+ // tool never completes on its own, so it is treated as done for automation.
4
6
 
5
7
  import type { Message as OpenCodeMessage } from '@opencode-ai/sdk/v2'
6
8
  import { getSessionEventSnapshot, getThreadSession } from './database.js'
@@ -49,7 +51,9 @@ export async function waitForSessionId({
49
51
 
50
52
  /**
51
53
  * Poll the OpenCode SDK and persisted Kimaki events until the session is idle,
52
- * its latest user turn completed naturally, and no interactive UI is pending.
54
+ * its latest user turn completed naturally, and no permission prompt is pending
55
+ * -- or until the session pauses on a user question (which never completes on
56
+ * its own and is treated as done for automation).
53
57
  */
54
58
  export async function waitForSessionComplete({
55
59
  projectDirectory,
@@ -84,6 +88,25 @@ export async function waitForSessionComplete({
84
88
  throw new Error('Failed to check session status')
85
89
  }
86
90
  const sessionStatus = statusResponse.data?.[sessionId]
91
+ const isBusy = Boolean(sessionStatus && sessionStatus.type !== 'idle')
92
+
93
+ // A session parked on a `question` tool reports busy but will never complete
94
+ // on its own, so treat a live question as done for automation and stop
95
+ // waiting. Guard on `busy`: an orphaned question left after an abort
96
+ // (session idle) must fall through to the normal idle/completion checks
97
+ // instead of returning early.
98
+ if (isBusy) {
99
+ const questionsResponse = await getClient().question
100
+ .list({ directory: projectDirectory })
101
+ .catch(() => null)
102
+ const hasPendingQuestion = (questionsResponse?.data || []).some((request) => {
103
+ return request.sessionID === sessionId
104
+ })
105
+ if (hasPendingQuestion) {
106
+ waitLogger.log(`Session ${sessionId} is showing a user question; treating as complete`)
107
+ return
108
+ }
109
+ }
87
110
 
88
111
  const messagesResponse = await getClient().session.messages({
89
112
  sessionID: sessionId,
@@ -545,15 +545,15 @@ describe('worktree lifecycle', () => {
545
545
  "--- from: user (worktree-tester)
546
546
  Reply with exactly: before-worktree
547
547
  --- from: assistant (TestBot)
548
- > *using deterministic-provider/deterministic-v2*
548
+ -# *using deterministic-provider/deterministic-v2*
549
549
  > ok
550
550
  Creating worktree in <#THREAD_ID>
551
- > *project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000901>
551
+ -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
552
552
  --- from: user (worktree-tester)
553
553
  Reply with exactly: after-source-thread
554
554
  --- from: assistant (TestBot)
555
555
  > ok
556
- > *project ⋅ main ⋅ <1s ⋅ 0% ⋅ source-model-v2* <@200000000000000901>"
556
+ -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ source-model-v2*"
557
557
  `)
558
558
  expect(sourceText).toContain('Reply with exactly: before-worktree')
559
559
  expect(sourceText).toContain('Reply with exactly: after-source-thread')
@@ -571,7 +571,7 @@ describe('worktree lifecycle', () => {
571
571
  Reply with exactly: after-worktree-thread
572
572
  --- from: assistant (TestBot)
573
573
  > ok
574
- > *WORKTREE_NAME ⋅ opencode/kimaki-WORKTREE_NAME ⋅ <1s ⋅ 0% ⋅ source-model-v2* <@200000000000000901>"
574
+ -# *WORKTREE_NAME ⋅ opencode/kimaki-WORKTREE_NAME ⋅ Ns ⋅ N% ⋅ source-model-v2*"
575
575
  `)
576
576
  expect(worktreeText).toContain('Worktree:')
577
577
  expect(worktreeText).toContain('Branch:')
@@ -666,7 +666,7 @@ describe('worktree lifecycle', () => {
666
666
  --- from: user (worktree-tester)
667
667
  Reply with exactly: channel-worktree-msg
668
668
  --- from: assistant (TestBot)
669
- > *using deterministic-provider/deterministic-v2*
669
+ -# *using deterministic-provider/deterministic-v2*
670
670
  > ok"
671
671
  `)
672
672
  expect(worktreeText).toContain('Branch:')
@@ -797,12 +797,12 @@ describe('worktree lifecycle', () => {
797
797
  "--- from: user (worktree-tester)
798
798
  Reply with exactly: non-git-first
799
799
  --- from: assistant (TestBot)
800
- > *using deterministic-provider/deterministic-v2*
800
+ -# *using deterministic-provider/deterministic-v2*
801
801
  > ok
802
802
  --- from: user (worktree-tester)
803
803
  Reply with exactly: non-git-second
804
804
  --- from: assistant (TestBot)
805
- > *non-git-project ⋅ main ⋅ <1s ⋅ 0% ⋅ deterministic-v2* <@200000000000000901>
805
+ -# *non-git-project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
806
806
  > ok"
807
807
  `)
808
808
  expect(text).toContain('Reply with exactly: non-git-first')
@@ -880,7 +880,7 @@ describe('worktree lifecycle', () => {
880
880
  🌳 **Worktree: AUTO_WORKTREE_BRANCH**
881
881
  📁 \`/tmp/worktrees/WORKTREE_NAME\`
882
882
  🌿 Branch: \`AUTO_WORKTREE_BRANCH\`
883
- > *using deterministic-provider/deterministic-v2*
883
+ -# *using deterministic-provider/deterministic-v2*
884
884
  > ok"
885
885
  `)
886
886