@shareai-lab/kode 1.1.14 → 1.1.16-dev.2

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 (289) hide show
  1. package/cli.js +77 -82
  2. package/dist/entrypoints/cli.js +59 -38
  3. package/dist/entrypoints/cli.js.map +3 -3
  4. package/dist/index.js +5 -26
  5. package/dist/package.json +4 -1
  6. package/package.json +11 -104
  7. package/dist/test/testAdapters.js +0 -88
  8. package/dist/test/testAdapters.js.map +0 -1
  9. package/src/ProjectOnboarding.tsx +0 -198
  10. package/src/Tool.ts +0 -83
  11. package/src/commands/agents.tsx +0 -3416
  12. package/src/commands/approvedTools.ts +0 -53
  13. package/src/commands/bug.tsx +0 -20
  14. package/src/commands/clear.ts +0 -43
  15. package/src/commands/compact.ts +0 -120
  16. package/src/commands/config.tsx +0 -19
  17. package/src/commands/cost.ts +0 -18
  18. package/src/commands/ctx_viz.ts +0 -209
  19. package/src/commands/doctor.ts +0 -24
  20. package/src/commands/help.tsx +0 -19
  21. package/src/commands/init.ts +0 -37
  22. package/src/commands/listen.ts +0 -42
  23. package/src/commands/login.tsx +0 -51
  24. package/src/commands/logout.tsx +0 -40
  25. package/src/commands/mcp.ts +0 -41
  26. package/src/commands/model.tsx +0 -40
  27. package/src/commands/modelstatus.tsx +0 -20
  28. package/src/commands/onboarding.tsx +0 -34
  29. package/src/commands/pr_comments.ts +0 -59
  30. package/src/commands/refreshCommands.ts +0 -54
  31. package/src/commands/release-notes.ts +0 -34
  32. package/src/commands/resume.tsx +0 -31
  33. package/src/commands/review.ts +0 -49
  34. package/src/commands/terminalSetup.ts +0 -221
  35. package/src/commands.ts +0 -139
  36. package/src/components/ApproveApiKey.tsx +0 -93
  37. package/src/components/AsciiLogo.tsx +0 -13
  38. package/src/components/AutoUpdater.tsx +0 -148
  39. package/src/components/Bug.tsx +0 -367
  40. package/src/components/Config.tsx +0 -293
  41. package/src/components/ConsoleOAuthFlow.tsx +0 -327
  42. package/src/components/Cost.tsx +0 -23
  43. package/src/components/CostThresholdDialog.tsx +0 -46
  44. package/src/components/CustomSelect/option-map.ts +0 -42
  45. package/src/components/CustomSelect/select-option.tsx +0 -78
  46. package/src/components/CustomSelect/select.tsx +0 -152
  47. package/src/components/CustomSelect/theme.ts +0 -45
  48. package/src/components/CustomSelect/use-select-state.ts +0 -414
  49. package/src/components/CustomSelect/use-select.ts +0 -35
  50. package/src/components/FallbackToolUseRejectedMessage.tsx +0 -15
  51. package/src/components/FileEditToolUpdatedMessage.tsx +0 -66
  52. package/src/components/Help.tsx +0 -215
  53. package/src/components/HighlightedCode.tsx +0 -33
  54. package/src/components/InvalidConfigDialog.tsx +0 -113
  55. package/src/components/Link.tsx +0 -32
  56. package/src/components/LogSelector.tsx +0 -86
  57. package/src/components/Logo.tsx +0 -170
  58. package/src/components/MCPServerApprovalDialog.tsx +0 -100
  59. package/src/components/MCPServerDialogCopy.tsx +0 -25
  60. package/src/components/MCPServerMultiselectDialog.tsx +0 -109
  61. package/src/components/Message.tsx +0 -221
  62. package/src/components/MessageResponse.tsx +0 -15
  63. package/src/components/MessageSelector.tsx +0 -211
  64. package/src/components/ModeIndicator.tsx +0 -88
  65. package/src/components/ModelConfig.tsx +0 -301
  66. package/src/components/ModelListManager.tsx +0 -227
  67. package/src/components/ModelSelector.tsx +0 -3387
  68. package/src/components/ModelStatusDisplay.tsx +0 -230
  69. package/src/components/Onboarding.tsx +0 -274
  70. package/src/components/PressEnterToContinue.tsx +0 -11
  71. package/src/components/PromptInput.tsx +0 -760
  72. package/src/components/SentryErrorBoundary.ts +0 -39
  73. package/src/components/Spinner.tsx +0 -129
  74. package/src/components/StickerRequestForm.tsx +0 -16
  75. package/src/components/StructuredDiff.tsx +0 -191
  76. package/src/components/TextInput.tsx +0 -259
  77. package/src/components/TodoItem.tsx +0 -47
  78. package/src/components/TokenWarning.tsx +0 -31
  79. package/src/components/ToolUseLoader.tsx +0 -40
  80. package/src/components/TrustDialog.tsx +0 -106
  81. package/src/components/binary-feedback/BinaryFeedback.tsx +0 -63
  82. package/src/components/binary-feedback/BinaryFeedbackOption.tsx +0 -111
  83. package/src/components/binary-feedback/BinaryFeedbackView.tsx +0 -172
  84. package/src/components/binary-feedback/utils.ts +0 -220
  85. package/src/components/messages/AssistantBashOutputMessage.tsx +0 -22
  86. package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +0 -49
  87. package/src/components/messages/AssistantRedactedThinkingMessage.tsx +0 -19
  88. package/src/components/messages/AssistantTextMessage.tsx +0 -144
  89. package/src/components/messages/AssistantThinkingMessage.tsx +0 -40
  90. package/src/components/messages/AssistantToolUseMessage.tsx +0 -132
  91. package/src/components/messages/TaskProgressMessage.tsx +0 -32
  92. package/src/components/messages/TaskToolMessage.tsx +0 -58
  93. package/src/components/messages/UserBashInputMessage.tsx +0 -28
  94. package/src/components/messages/UserCommandMessage.tsx +0 -30
  95. package/src/components/messages/UserKodingInputMessage.tsx +0 -28
  96. package/src/components/messages/UserPromptMessage.tsx +0 -35
  97. package/src/components/messages/UserTextMessage.tsx +0 -39
  98. package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +0 -12
  99. package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +0 -36
  100. package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +0 -31
  101. package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +0 -57
  102. package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +0 -35
  103. package/src/components/messages/UserToolResultMessage/utils.tsx +0 -56
  104. package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +0 -121
  105. package/src/components/permissions/FallbackPermissionRequest.tsx +0 -153
  106. package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +0 -182
  107. package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +0 -77
  108. package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +0 -164
  109. package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +0 -83
  110. package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +0 -240
  111. package/src/components/permissions/PermissionRequest.tsx +0 -101
  112. package/src/components/permissions/PermissionRequestTitle.tsx +0 -69
  113. package/src/components/permissions/hooks.ts +0 -44
  114. package/src/components/permissions/toolUseOptions.ts +0 -59
  115. package/src/components/permissions/utils.ts +0 -23
  116. package/src/constants/betas.ts +0 -5
  117. package/src/constants/claude-asterisk-ascii-art.tsx +0 -238
  118. package/src/constants/figures.ts +0 -4
  119. package/src/constants/keys.ts +0 -3
  120. package/src/constants/macros.ts +0 -11
  121. package/src/constants/modelCapabilities.ts +0 -179
  122. package/src/constants/models.ts +0 -1025
  123. package/src/constants/oauth.ts +0 -18
  124. package/src/constants/product.ts +0 -17
  125. package/src/constants/prompts.ts +0 -168
  126. package/src/constants/releaseNotes.ts +0 -7
  127. package/src/context/PermissionContext.tsx +0 -149
  128. package/src/context.ts +0 -278
  129. package/src/cost-tracker.ts +0 -84
  130. package/src/entrypoints/cli.tsx +0 -1561
  131. package/src/entrypoints/mcp.ts +0 -175
  132. package/src/history.ts +0 -25
  133. package/src/hooks/useApiKeyVerification.ts +0 -59
  134. package/src/hooks/useArrowKeyHistory.ts +0 -55
  135. package/src/hooks/useCanUseTool.ts +0 -138
  136. package/src/hooks/useCancelRequest.ts +0 -39
  137. package/src/hooks/useDoublePress.ts +0 -41
  138. package/src/hooks/useExitOnCtrlCD.ts +0 -31
  139. package/src/hooks/useInterval.ts +0 -25
  140. package/src/hooks/useLogMessages.ts +0 -16
  141. package/src/hooks/useLogStartupTime.ts +0 -12
  142. package/src/hooks/useNotifyAfterTimeout.ts +0 -65
  143. package/src/hooks/usePermissionRequestLogging.ts +0 -44
  144. package/src/hooks/useTerminalSize.ts +0 -49
  145. package/src/hooks/useTextInput.ts +0 -317
  146. package/src/hooks/useUnifiedCompletion.ts +0 -1405
  147. package/src/index.ts +0 -34
  148. package/src/messages.ts +0 -38
  149. package/src/permissions.ts +0 -268
  150. package/src/query.ts +0 -720
  151. package/src/screens/ConfigureNpmPrefix.tsx +0 -197
  152. package/src/screens/Doctor.tsx +0 -219
  153. package/src/screens/LogList.tsx +0 -68
  154. package/src/screens/REPL.tsx +0 -813
  155. package/src/screens/ResumeConversation.tsx +0 -68
  156. package/src/services/adapters/base.ts +0 -38
  157. package/src/services/adapters/chatCompletions.ts +0 -90
  158. package/src/services/adapters/responsesAPI.ts +0 -170
  159. package/src/services/browserMocks.ts +0 -66
  160. package/src/services/claude.ts +0 -2197
  161. package/src/services/customCommands.ts +0 -704
  162. package/src/services/fileFreshness.ts +0 -377
  163. package/src/services/gpt5ConnectionTest.ts +0 -340
  164. package/src/services/mcpClient.ts +0 -564
  165. package/src/services/mcpServerApproval.tsx +0 -50
  166. package/src/services/mentionProcessor.ts +0 -273
  167. package/src/services/modelAdapterFactory.ts +0 -69
  168. package/src/services/notifier.ts +0 -40
  169. package/src/services/oauth.ts +0 -357
  170. package/src/services/openai.ts +0 -1359
  171. package/src/services/responseStateManager.ts +0 -90
  172. package/src/services/sentry.ts +0 -3
  173. package/src/services/statsig.ts +0 -172
  174. package/src/services/statsigStorage.ts +0 -86
  175. package/src/services/systemReminder.ts +0 -507
  176. package/src/services/vcr.ts +0 -161
  177. package/src/test/testAdapters.ts +0 -96
  178. package/src/tools/ArchitectTool/ArchitectTool.tsx +0 -135
  179. package/src/tools/ArchitectTool/prompt.ts +0 -15
  180. package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +0 -576
  181. package/src/tools/BashTool/BashTool.tsx +0 -243
  182. package/src/tools/BashTool/BashToolResultMessage.tsx +0 -38
  183. package/src/tools/BashTool/OutputLine.tsx +0 -49
  184. package/src/tools/BashTool/prompt.ts +0 -174
  185. package/src/tools/BashTool/utils.ts +0 -56
  186. package/src/tools/FileEditTool/FileEditTool.tsx +0 -319
  187. package/src/tools/FileEditTool/prompt.ts +0 -51
  188. package/src/tools/FileEditTool/utils.ts +0 -58
  189. package/src/tools/FileReadTool/FileReadTool.tsx +0 -404
  190. package/src/tools/FileReadTool/prompt.ts +0 -7
  191. package/src/tools/FileWriteTool/FileWriteTool.tsx +0 -301
  192. package/src/tools/FileWriteTool/prompt.ts +0 -10
  193. package/src/tools/GlobTool/GlobTool.tsx +0 -119
  194. package/src/tools/GlobTool/prompt.ts +0 -8
  195. package/src/tools/GrepTool/GrepTool.tsx +0 -147
  196. package/src/tools/GrepTool/prompt.ts +0 -11
  197. package/src/tools/MCPTool/MCPTool.tsx +0 -107
  198. package/src/tools/MCPTool/prompt.ts +0 -3
  199. package/src/tools/MemoryReadTool/MemoryReadTool.tsx +0 -127
  200. package/src/tools/MemoryReadTool/prompt.ts +0 -3
  201. package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +0 -89
  202. package/src/tools/MemoryWriteTool/prompt.ts +0 -3
  203. package/src/tools/MultiEditTool/MultiEditTool.tsx +0 -388
  204. package/src/tools/MultiEditTool/prompt.ts +0 -45
  205. package/src/tools/NotebookEditTool/NotebookEditTool.tsx +0 -298
  206. package/src/tools/NotebookEditTool/prompt.ts +0 -3
  207. package/src/tools/NotebookReadTool/NotebookReadTool.tsx +0 -258
  208. package/src/tools/NotebookReadTool/prompt.ts +0 -3
  209. package/src/tools/StickerRequestTool/StickerRequestTool.tsx +0 -107
  210. package/src/tools/StickerRequestTool/prompt.ts +0 -19
  211. package/src/tools/TaskTool/TaskTool.tsx +0 -438
  212. package/src/tools/TaskTool/constants.ts +0 -1
  213. package/src/tools/TaskTool/prompt.ts +0 -92
  214. package/src/tools/ThinkTool/ThinkTool.tsx +0 -54
  215. package/src/tools/ThinkTool/prompt.ts +0 -12
  216. package/src/tools/TodoWriteTool/TodoWriteTool.tsx +0 -313
  217. package/src/tools/TodoWriteTool/prompt.ts +0 -63
  218. package/src/tools/URLFetcherTool/URLFetcherTool.tsx +0 -178
  219. package/src/tools/URLFetcherTool/cache.ts +0 -55
  220. package/src/tools/URLFetcherTool/htmlToMarkdown.ts +0 -55
  221. package/src/tools/URLFetcherTool/prompt.ts +0 -17
  222. package/src/tools/WebSearchTool/WebSearchTool.tsx +0 -103
  223. package/src/tools/WebSearchTool/prompt.ts +0 -13
  224. package/src/tools/WebSearchTool/searchProviders.ts +0 -66
  225. package/src/tools/lsTool/lsTool.tsx +0 -272
  226. package/src/tools/lsTool/prompt.ts +0 -2
  227. package/src/tools.ts +0 -67
  228. package/src/types/PermissionMode.ts +0 -120
  229. package/src/types/RequestContext.ts +0 -72
  230. package/src/types/common.d.ts +0 -2
  231. package/src/types/conversation.ts +0 -51
  232. package/src/types/logs.ts +0 -58
  233. package/src/types/modelCapabilities.ts +0 -64
  234. package/src/types/notebook.ts +0 -87
  235. package/src/utils/Cursor.ts +0 -436
  236. package/src/utils/PersistentShell.ts +0 -552
  237. package/src/utils/advancedFuzzyMatcher.ts +0 -290
  238. package/src/utils/agentLoader.ts +0 -278
  239. package/src/utils/agentStorage.ts +0 -97
  240. package/src/utils/array.ts +0 -3
  241. package/src/utils/ask.tsx +0 -99
  242. package/src/utils/auth.ts +0 -13
  243. package/src/utils/autoCompactCore.ts +0 -223
  244. package/src/utils/autoUpdater.ts +0 -458
  245. package/src/utils/betas.ts +0 -20
  246. package/src/utils/browser.ts +0 -14
  247. package/src/utils/cleanup.ts +0 -72
  248. package/src/utils/commands.ts +0 -261
  249. package/src/utils/commonUnixCommands.ts +0 -161
  250. package/src/utils/config.ts +0 -945
  251. package/src/utils/conversationRecovery.ts +0 -55
  252. package/src/utils/debugLogger.ts +0 -1235
  253. package/src/utils/diff.ts +0 -42
  254. package/src/utils/env.ts +0 -57
  255. package/src/utils/errors.ts +0 -21
  256. package/src/utils/exampleCommands.ts +0 -109
  257. package/src/utils/execFileNoThrow.ts +0 -51
  258. package/src/utils/expertChatStorage.ts +0 -136
  259. package/src/utils/file.ts +0 -405
  260. package/src/utils/fileRecoveryCore.ts +0 -71
  261. package/src/utils/format.tsx +0 -44
  262. package/src/utils/fuzzyMatcher.ts +0 -328
  263. package/src/utils/generators.ts +0 -62
  264. package/src/utils/git.ts +0 -92
  265. package/src/utils/globalLogger.ts +0 -77
  266. package/src/utils/http.ts +0 -10
  267. package/src/utils/imagePaste.ts +0 -38
  268. package/src/utils/json.ts +0 -13
  269. package/src/utils/log.ts +0 -382
  270. package/src/utils/markdown.ts +0 -213
  271. package/src/utils/messageContextManager.ts +0 -294
  272. package/src/utils/messages.tsx +0 -945
  273. package/src/utils/model.ts +0 -914
  274. package/src/utils/permissions/filesystem.ts +0 -127
  275. package/src/utils/responseState.ts +0 -23
  276. package/src/utils/ripgrep.ts +0 -167
  277. package/src/utils/secureFile.ts +0 -564
  278. package/src/utils/sessionState.ts +0 -49
  279. package/src/utils/state.ts +0 -25
  280. package/src/utils/style.ts +0 -29
  281. package/src/utils/terminal.ts +0 -50
  282. package/src/utils/theme.ts +0 -127
  283. package/src/utils/thinking.ts +0 -144
  284. package/src/utils/todoStorage.ts +0 -431
  285. package/src/utils/tokens.ts +0 -43
  286. package/src/utils/toolExecutionController.ts +0 -163
  287. package/src/utils/unaryLogging.ts +0 -26
  288. package/src/utils/user.ts +0 -37
  289. package/src/utils/validate.ts +0 -165
@@ -1,945 +0,0 @@
1
- import { randomUUID, UUID } from 'crypto'
2
- import { Box } from 'ink'
3
- import {
4
- AssistantMessage,
5
- Message,
6
- ProgressMessage,
7
- UserMessage,
8
- } from '../query.js'
9
- import { getCommand, hasCommand } from '../commands'
10
- import { MalformedCommandError } from './errors'
11
- import { logError } from './log'
12
- import { resolve } from 'path'
13
- import { last, memoize } from 'lodash-es'
14
- import { logEvent } from '../services/statsig'
15
- import type { SetToolJSXFn, Tool, ToolUseContext } from '../Tool'
16
- import { lastX } from '../utils/generators'
17
- import { NO_CONTENT_MESSAGE } from '../services/claude'
18
- import {
19
- ImageBlockParam,
20
- TextBlockParam,
21
- ToolResultBlockParam,
22
- ToolUseBlockParam,
23
- Message as APIMessage,
24
- ContentBlockParam,
25
- ContentBlock,
26
- } from '@anthropic-ai/sdk/resources/index.mjs'
27
- import { setCwd } from './state'
28
- import { getCwd } from './state'
29
- import chalk from 'chalk'
30
- import * as React from 'react'
31
- import { UserBashInputMessage } from '../components/messages/UserBashInputMessage'
32
- import { Spinner } from '../components/Spinner'
33
- import { BashTool } from '../tools/BashTool/BashTool'
34
- import { ToolUseBlock } from '@anthropic-ai/sdk/resources/index.mjs'
35
-
36
- // NOTE: Dynamic content processing for custom commands has been moved to
37
- // src/services/customCommands.ts for better organization and reusability.
38
- // The functions executeBashCommands and resolveFileReferences are no longer
39
- // duplicated here but are imported when needed for custom command processing.
40
-
41
- export const INTERRUPT_MESSAGE = '[Request interrupted by user]'
42
- export const INTERRUPT_MESSAGE_FOR_TOOL_USE =
43
- '[Request interrupted by user for tool use]'
44
- export const CANCEL_MESSAGE =
45
- "The user doesn't want to take this action right now. STOP what you are doing and wait for the user to tell you how to proceed."
46
- export const REJECT_MESSAGE =
47
- "The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed."
48
- export const NO_RESPONSE_REQUESTED = 'No response requested.'
49
-
50
- export const SYNTHETIC_ASSISTANT_MESSAGES = new Set([
51
- INTERRUPT_MESSAGE,
52
- INTERRUPT_MESSAGE_FOR_TOOL_USE,
53
- CANCEL_MESSAGE,
54
- REJECT_MESSAGE,
55
- NO_RESPONSE_REQUESTED,
56
- ])
57
-
58
- function baseCreateAssistantMessage(
59
- content: ContentBlock[],
60
- extra?: Partial<AssistantMessage>,
61
- ): AssistantMessage {
62
- return {
63
- type: 'assistant',
64
- costUSD: 0,
65
- durationMs: 0,
66
- uuid: randomUUID(),
67
- message: {
68
- id: randomUUID(),
69
- model: '<synthetic>',
70
- role: 'assistant',
71
- stop_reason: 'stop_sequence',
72
- stop_sequence: '',
73
- type: 'message',
74
- usage: {
75
- input_tokens: 0,
76
- output_tokens: 0,
77
- cache_creation_input_tokens: 0,
78
- cache_read_input_tokens: 0,
79
- },
80
- content,
81
- },
82
- ...extra,
83
- }
84
- }
85
-
86
- export function createAssistantMessage(content: string): AssistantMessage {
87
- return baseCreateAssistantMessage([
88
- {
89
- type: 'text' as const,
90
- text: content === '' ? NO_CONTENT_MESSAGE : content,
91
- citations: [],
92
- },
93
- ])
94
- }
95
-
96
- export function createAssistantAPIErrorMessage(
97
- content: string,
98
- ): AssistantMessage {
99
- return baseCreateAssistantMessage(
100
- [
101
- {
102
- type: 'text' as const,
103
- text: content === '' ? NO_CONTENT_MESSAGE : content,
104
- citations: [],
105
- },
106
- ],
107
- { isApiErrorMessage: true },
108
- )
109
- }
110
-
111
- export type FullToolUseResult = {
112
- data: unknown // Matches tool's `Output` type
113
- resultForAssistant: ToolResultBlockParam['content']
114
- }
115
-
116
- export function createUserMessage(
117
- content: string | ContentBlockParam[],
118
- toolUseResult?: FullToolUseResult,
119
- ): UserMessage {
120
- const m: UserMessage = {
121
- type: 'user',
122
- message: {
123
- role: 'user',
124
- content,
125
- },
126
- uuid: randomUUID(),
127
- toolUseResult,
128
- }
129
- return m
130
- }
131
-
132
- export function createProgressMessage(
133
- toolUseID: string,
134
- siblingToolUseIDs: Set<string>,
135
- content: AssistantMessage,
136
- normalizedMessages: NormalizedMessage[],
137
- tools: Tool[],
138
- ): ProgressMessage {
139
- return {
140
- type: 'progress',
141
- content,
142
- normalizedMessages,
143
- siblingToolUseIDs,
144
- tools,
145
- toolUseID,
146
- uuid: randomUUID(),
147
- }
148
- }
149
-
150
- export function createToolResultStopMessage(
151
- toolUseID: string,
152
- ): ToolResultBlockParam {
153
- return {
154
- type: 'tool_result',
155
- content: CANCEL_MESSAGE,
156
- is_error: true,
157
- tool_use_id: toolUseID,
158
- }
159
- }
160
-
161
- export async function processUserInput(
162
- input: string,
163
- mode: 'bash' | 'prompt' | 'koding',
164
- setToolJSX: SetToolJSXFn,
165
- context: ToolUseContext & {
166
- setForkConvoWithMessagesOnTheNextRender: (
167
- forkConvoWithMessages: Message[],
168
- ) => void
169
- options?: {
170
- isKodingRequest?: boolean
171
- kodingContext?: string
172
- }
173
- },
174
- pastedImage: string | null,
175
- ): Promise<Message[]> {
176
- // Bash commands
177
- if (mode === 'bash') {
178
- logEvent('tengu_input_bash', {})
179
-
180
- const userMessage = createUserMessage(`<bash-input>${input}</bash-input>`)
181
-
182
- // Special case: cd
183
- if (input.startsWith('cd ')) {
184
- const oldCwd = getCwd()
185
- const newCwd = resolve(oldCwd, input.slice(3))
186
- try {
187
- await setCwd(newCwd)
188
- return [
189
- userMessage,
190
- createAssistantMessage(
191
- `<bash-stdout>Changed directory to ${chalk.bold(`${newCwd}/`)}</bash-stdout>`,
192
- ),
193
- ]
194
- } catch (e) {
195
- logError(e)
196
- return [
197
- userMessage,
198
- createAssistantMessage(
199
- `<bash-stderr>cwd error: ${e instanceof Error ? e.message : String(e)}</bash-stderr>`,
200
- ),
201
- ]
202
- }
203
- }
204
-
205
- // All other bash commands
206
- setToolJSX({
207
- jsx: (
208
- <Box flexDirection="column" marginTop={1}>
209
- <UserBashInputMessage
210
- addMargin={false}
211
- param={{ text: `<bash-input>${input}</bash-input>`, type: 'text' }}
212
- />
213
- <Spinner />
214
- </Box>
215
- ),
216
- shouldHidePromptInput: false,
217
- })
218
- try {
219
- const validationResult = await BashTool.validateInput({
220
- command: input,
221
- })
222
- if (!validationResult.result) {
223
- return [userMessage, createAssistantMessage(validationResult.message)]
224
- }
225
- const { data } = await lastX(BashTool.call({ command: input }, context))
226
- return [
227
- userMessage,
228
- createAssistantMessage(
229
- `<bash-stdout>${data.stdout}</bash-stdout><bash-stderr>${data.stderr}</bash-stderr>`,
230
- ),
231
- ]
232
- } catch (e) {
233
- return [
234
- userMessage,
235
- createAssistantMessage(
236
- `<bash-stderr>Command failed: ${e instanceof Error ? e.message : String(e)}</bash-stderr>`,
237
- ),
238
- ]
239
- } finally {
240
- setToolJSX(null)
241
- }
242
- }
243
- // Koding mode - special wrapper for display
244
- else if (mode === 'koding') {
245
- logEvent('tengu_input_koding', {})
246
-
247
- const userMessage = createUserMessage(
248
- `<koding-input>${input}</koding-input>`,
249
- )
250
- // Add the Koding flag to the message
251
- userMessage.options = {
252
- ...userMessage.options,
253
- isKodingRequest: true,
254
- }
255
-
256
- // Rest of koding processing is handled separately to capture assistant response
257
- return [userMessage]
258
- }
259
-
260
- // Slash commands
261
- if (input.startsWith('/')) {
262
- const words = input.slice(1).split(' ')
263
- let commandName = words[0]
264
- if (words.length > 1 && words[1] === '(MCP)') {
265
- commandName = commandName + ' (MCP)'
266
- }
267
- if (!commandName) {
268
- logEvent('tengu_input_slash_missing', { input })
269
- return [
270
- createAssistantMessage('Commands are in the form `/command [args]`'),
271
- ]
272
- }
273
-
274
- // Check if it's a real command before processing
275
- if (!hasCommand(commandName, context.options.commands)) {
276
- // If not a real command, treat it as a regular user input
277
- logEvent('tengu_input_prompt', {})
278
- return [createUserMessage(input)]
279
- }
280
-
281
- const args = input.slice(commandName.length + 2)
282
- const newMessages = await getMessagesForSlashCommand(
283
- commandName,
284
- args,
285
- setToolJSX,
286
- context,
287
- )
288
-
289
- // Local JSX commands
290
- if (newMessages.length === 0) {
291
- logEvent('tengu_input_command', { input })
292
- return []
293
- }
294
-
295
- // For invalid commands, preserve both the user message and error
296
- if (
297
- newMessages.length === 2 &&
298
- newMessages[0]!.type === 'user' &&
299
- newMessages[1]!.type === 'assistant' &&
300
- typeof newMessages[1]!.message.content === 'string' &&
301
- newMessages[1]!.message.content.startsWith('Unknown command:')
302
- ) {
303
- logEvent('tengu_input_slash_invalid', { input })
304
- return newMessages
305
- }
306
-
307
- // User-Assistant pair (eg. local commands)
308
- if (newMessages.length === 2) {
309
- logEvent('tengu_input_command', { input })
310
- return newMessages
311
- }
312
-
313
- // A valid command
314
- logEvent('tengu_input_command', { input })
315
- return newMessages
316
- }
317
-
318
- // Regular user prompt
319
- logEvent('tengu_input_prompt', {})
320
-
321
- // Check if this is a Koding request that needs special handling
322
- const isKodingRequest = context.options?.isKodingRequest === true
323
- const kodingContextInfo = context.options?.kodingContext
324
-
325
- // Create base message
326
- let userMessage: UserMessage
327
-
328
- if (pastedImage) {
329
- userMessage = createUserMessage([
330
- {
331
- type: 'image',
332
- source: {
333
- type: 'base64',
334
- media_type: 'image/png',
335
- data: pastedImage,
336
- },
337
- },
338
- {
339
- type: 'text',
340
- text:
341
- isKodingRequest && kodingContextInfo
342
- ? `${kodingContextInfo}\n\n${input}`
343
- : input,
344
- },
345
- ])
346
- } else {
347
- let processedInput =
348
- isKodingRequest && kodingContextInfo
349
- ? `${kodingContextInfo}\n\n${input}`
350
- : input
351
-
352
- // Process dynamic content for custom commands with ! and @ prefixes
353
- // This uses the same processing functions as custom commands to maintain consistency
354
- if (input.includes('!`') || input.includes('@')) {
355
- try {
356
- // Import functions from customCommands service to avoid code duplication
357
- const { executeBashCommands } = await import(
358
- '../services/customCommands'
359
- )
360
-
361
- // Execute bash commands if present
362
- if (input.includes('!`')) {
363
- // Note: This function is not exported from customCommands.ts, so we need to expose it
364
- // For now, we'll keep the local implementation until we refactor the service
365
- processedInput = await executeBashCommands(processedInput)
366
- }
367
-
368
- // Process mentions for system reminder integration
369
- // Note: We don't call resolveFileReferences here anymore -
370
- // @file mentions should trigger Read tool usage via reminders, not embed content
371
- if (input.includes('@')) {
372
- const { processMentions } = await import('../services/mentionProcessor')
373
- await processMentions(input)
374
- }
375
- } catch (error) {
376
- console.warn('Dynamic content processing failed:', error)
377
- // Continue with original input if processing fails
378
- }
379
- }
380
-
381
- userMessage = createUserMessage(processedInput)
382
- }
383
-
384
- // Add the Koding flag to the message if needed
385
- if (isKodingRequest) {
386
- userMessage.options = {
387
- ...userMessage.options,
388
- isKodingRequest: true,
389
- }
390
- }
391
-
392
- return [userMessage]
393
- }
394
-
395
- async function getMessagesForSlashCommand(
396
- commandName: string,
397
- args: string,
398
- setToolJSX: SetToolJSXFn,
399
- context: ToolUseContext & {
400
- setForkConvoWithMessagesOnTheNextRender: (
401
- forkConvoWithMessages: Message[],
402
- ) => void
403
- },
404
- ): Promise<Message[]> {
405
- try {
406
- const command = getCommand(commandName, context.options.commands)
407
- switch (command.type) {
408
- case 'local-jsx': {
409
- return new Promise(resolve => {
410
- command
411
- .call(r => {
412
- setToolJSX(null)
413
- resolve([
414
- createUserMessage(`<command-name>${command.userFacingName()}</command-name>
415
- <command-message>${command.userFacingName()}</command-message>
416
- <command-args>${args}</command-args>`),
417
- r
418
- ? createAssistantMessage(r)
419
- : createAssistantMessage(NO_RESPONSE_REQUESTED),
420
- ])
421
- }, context)
422
- .then(jsx => {
423
- setToolJSX({
424
- jsx,
425
- shouldHidePromptInput: true,
426
- })
427
- })
428
- })
429
- }
430
- case 'local': {
431
- const userMessage =
432
- createUserMessage(`<command-name>${command.userFacingName()}</command-name>
433
- <command-message>${command.userFacingName()}</command-message>
434
- <command-args>${args}</command-args>`)
435
-
436
- try {
437
- // Use the context's abortController for local commands
438
- const result = await command.call(args, {
439
- ...context,
440
- options: {
441
- commands: context.options.commands || [],
442
- tools: context.options.tools || [],
443
- slowAndCapableModel: context.options.slowAndCapableModel || 'main'
444
- }
445
- })
446
-
447
- return [
448
- userMessage,
449
- createAssistantMessage(
450
- `<local-command-stdout>${result}</local-command-stdout>`,
451
- ),
452
- ]
453
- } catch (e) {
454
- logError(e)
455
- return [
456
- userMessage,
457
- createAssistantMessage(
458
- `<local-command-stderr>${String(e)}</local-command-stderr>`,
459
- ),
460
- ]
461
- }
462
- }
463
- case 'prompt': {
464
- // For custom commands, process them naturally instead of wrapping in command-contents
465
- const prompt = await command.getPromptForCommand(args)
466
- return prompt.map(msg => {
467
- // Create a normal user message from the custom command content
468
- const userMessage = createUserMessage(
469
- typeof msg.content === 'string'
470
- ? msg.content
471
- : msg.content
472
- .map(block => (block.type === 'text' ? block.text : ''))
473
- .join('\n'),
474
- )
475
-
476
- // Add metadata for tracking but don't wrap in special tags
477
- userMessage.options = {
478
- ...userMessage.options,
479
- isCustomCommand: true,
480
- commandName: command.userFacingName(),
481
- commandArgs: args,
482
- }
483
-
484
- return userMessage
485
- })
486
- }
487
- }
488
- } catch (e) {
489
- if (e instanceof MalformedCommandError) {
490
- return [createAssistantMessage(e.message)]
491
- }
492
- throw e
493
- }
494
- }
495
-
496
- export function extractTagFromMessage(
497
- message: Message,
498
- tagName: string,
499
- ): string | null {
500
- if (message.type === 'progress') {
501
- return null
502
- }
503
- if (typeof message.message.content !== 'string') {
504
- return null
505
- }
506
- return extractTag(message.message.content, tagName)
507
- }
508
-
509
- export function extractTag(html: string, tagName: string): string | null {
510
- if (!html.trim() || !tagName.trim()) {
511
- return null
512
- }
513
-
514
- // Escape special characters in the tag name
515
- const escapedTag = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
516
-
517
- // Create regex pattern that handles:
518
- // 1. Self-closing tags
519
- // 2. Tags with attributes
520
- // 3. Nested tags of the same type
521
- // 4. Multiline content
522
- const pattern = new RegExp(
523
- `<${escapedTag}(?:\\s+[^>]*)?>` + // Opening tag with optional attributes
524
- '([\\s\\S]*?)' + // Content (non-greedy match)
525
- `<\\/${escapedTag}>`, // Closing tag
526
- 'gi',
527
- )
528
-
529
- let match
530
- let depth = 0
531
- let lastIndex = 0
532
- const openingTag = new RegExp(`<${escapedTag}(?:\\s+[^>]*?)?>`, 'gi')
533
- const closingTag = new RegExp(`<\\/${escapedTag}>`, 'gi')
534
-
535
- while ((match = pattern.exec(html)) !== null) {
536
- // Check for nested tags
537
- const content = match[1]
538
- const beforeMatch = html.slice(lastIndex, match.index)
539
-
540
- // Reset depth counter
541
- depth = 0
542
-
543
- // Count opening tags before this match
544
- openingTag.lastIndex = 0
545
- while (openingTag.exec(beforeMatch) !== null) {
546
- depth++
547
- }
548
-
549
- // Count closing tags before this match
550
- closingTag.lastIndex = 0
551
- while (closingTag.exec(beforeMatch) !== null) {
552
- depth--
553
- }
554
-
555
- // Only include content if we're at the correct nesting level
556
- if (depth === 0 && content) {
557
- return content
558
- }
559
-
560
- lastIndex = match.index + match[0].length
561
- }
562
-
563
- return null
564
- }
565
-
566
- export function isNotEmptyMessage(message: Message): boolean {
567
- if (message.type === 'progress') {
568
- return true
569
- }
570
-
571
- if (typeof message.message.content === 'string') {
572
- return message.message.content.trim().length > 0
573
- }
574
-
575
- if (message.message.content.length === 0) {
576
- return false
577
- }
578
-
579
- // Skip multi-block messages for now
580
- if (message.message.content.length > 1) {
581
- return true
582
- }
583
-
584
- if (message.message.content[0]!.type !== 'text') {
585
- return true
586
- }
587
-
588
- return (
589
- message.message.content[0]!.text.trim().length > 0 &&
590
- message.message.content[0]!.text !== NO_CONTENT_MESSAGE &&
591
- message.message.content[0]!.text !== INTERRUPT_MESSAGE_FOR_TOOL_USE
592
- )
593
- }
594
-
595
- // TODO: replace this with plain UserMessage if/when PR #405 lands
596
- type NormalizedUserMessage = {
597
- message: {
598
- content: [
599
- | TextBlockParam
600
- | ImageBlockParam
601
- | ToolUseBlockParam
602
- | ToolResultBlockParam,
603
- ]
604
- role: 'user'
605
- }
606
- type: 'user'
607
- uuid: UUID
608
- }
609
-
610
- export type NormalizedMessage =
611
- | NormalizedUserMessage
612
- | AssistantMessage
613
- | ProgressMessage
614
-
615
- // Split messages, so each content block gets its own message
616
- export function normalizeMessages(messages: Message[]): NormalizedMessage[] {
617
- return messages.flatMap(message => {
618
- if (message.type === 'progress') {
619
- return [message] as NormalizedMessage[]
620
- }
621
- if (typeof message.message.content === 'string') {
622
- return [message] as NormalizedMessage[]
623
- }
624
- return message.message.content.map(_ => {
625
- switch (message.type) {
626
- case 'assistant':
627
- return {
628
- type: 'assistant',
629
- uuid: randomUUID(),
630
- message: {
631
- ...message.message,
632
- content: [_],
633
- },
634
- costUSD:
635
- (message as AssistantMessage).costUSD /
636
- message.message.content.length,
637
- durationMs: (message as AssistantMessage).durationMs,
638
- } as NormalizedMessage
639
- case 'user':
640
- // It seems like the line below was a no-op before, but I'm not sure.
641
- // To check, we could throw an error if any of the following are true:
642
- // - message `role` does isn't `user` -- this possibility is allowed by MCP tools,
643
- // though isn't supposed to happen in practice (we should fix this)
644
- // - message `content` is not an array -- this one is more concerning because it's
645
- // not allowed by the `NormalizedUserMessage` type, but if it's happening that was
646
- // probably a bug before.
647
- // Maybe I'm missing something? -(ab)
648
- // return createUserMessage([_]) as NormalizedMessage
649
- return message as NormalizedUserMessage
650
- }
651
- })
652
- })
653
- }
654
-
655
- type ToolUseRequestMessage = AssistantMessage & {
656
- message: { content: ToolUseBlock[] }
657
- }
658
-
659
- function isToolUseRequestMessage(
660
- message: Message,
661
- ): message is ToolUseRequestMessage {
662
- return (
663
- message.type === 'assistant' &&
664
- 'costUSD' in message &&
665
- // Note: stop_reason === 'tool_use' is unreliable -- it's not always set correctly
666
- message.message.content.some(_ => _.type === 'tool_use')
667
- )
668
- }
669
-
670
- // Re-order, to move result messages to be after their tool use messages
671
- export function reorderMessages(
672
- messages: NormalizedMessage[],
673
- ): NormalizedMessage[] {
674
- const ms: NormalizedMessage[] = []
675
- const toolUseMessages: ToolUseRequestMessage[] = []
676
-
677
- for (const message of messages) {
678
- // track tool use messages we've seen
679
- if (isToolUseRequestMessage(message)) {
680
- toolUseMessages.push(message)
681
- }
682
-
683
- // if it's a tool progress message...
684
- if (message.type === 'progress') {
685
- // replace any existing progress messages with this one
686
- const existingProgressMessage = ms.find(
687
- _ => _.type === 'progress' && _.toolUseID === message.toolUseID,
688
- )
689
- if (existingProgressMessage) {
690
- ms[ms.indexOf(existingProgressMessage)] = message
691
- continue
692
- }
693
- // otherwise, insert it after its tool use
694
- const toolUseMessage = toolUseMessages.find(
695
- _ => _.message.content[0]?.id === message.toolUseID,
696
- )
697
- if (toolUseMessage) {
698
- ms.splice(ms.indexOf(toolUseMessage) + 1, 0, message)
699
- continue
700
- }
701
- }
702
-
703
- // if it's a tool result, insert it after its tool use and progress messages
704
- if (
705
- message.type === 'user' &&
706
- Array.isArray(message.message.content) &&
707
- message.message.content[0]?.type === 'tool_result'
708
- ) {
709
- const toolUseID = (message.message.content[0] as ToolResultBlockParam)
710
- ?.tool_use_id
711
-
712
- // First check for progress messages
713
- const lastProgressMessage = ms.find(
714
- _ => _.type === 'progress' && _.toolUseID === toolUseID,
715
- )
716
- if (lastProgressMessage) {
717
- ms.splice(ms.indexOf(lastProgressMessage) + 1, 0, message)
718
- continue
719
- }
720
-
721
- // If no progress messages, check for tool use messages
722
- const toolUseMessage = toolUseMessages.find(
723
- _ => _.message.content[0]?.id === toolUseID,
724
- )
725
- if (toolUseMessage) {
726
- ms.splice(ms.indexOf(toolUseMessage) + 1, 0, message)
727
- continue
728
- }
729
- }
730
-
731
- // otherwise, just add it to the list
732
- else {
733
- ms.push(message)
734
- }
735
- }
736
-
737
- return ms
738
- }
739
-
740
- const getToolResultIDs = memoize(
741
- (normalizedMessages: NormalizedMessage[]): { [toolUseID: string]: boolean } =>
742
- Object.fromEntries(
743
- normalizedMessages.flatMap(_ =>
744
- _.type === 'user' && _.message.content[0]?.type === 'tool_result'
745
- ? [
746
- [
747
- _.message.content[0]!.tool_use_id,
748
- _.message.content[0]!.is_error ?? false,
749
- ],
750
- ]
751
- : ([] as [string, boolean][]),
752
- ),
753
- ),
754
- )
755
-
756
- export function getUnresolvedToolUseIDs(
757
- normalizedMessages: NormalizedMessage[],
758
- ): Set<string> {
759
- const toolResults = getToolResultIDs(normalizedMessages)
760
- return new Set(
761
- normalizedMessages
762
- .filter(
763
- (
764
- _,
765
- ): _ is AssistantMessage & {
766
- message: { content: [ToolUseBlockParam] }
767
- } =>
768
- _.type === 'assistant' &&
769
- Array.isArray(_.message.content) &&
770
- _.message.content[0]?.type === 'tool_use' &&
771
- !(_.message.content[0]?.id in toolResults),
772
- )
773
- .map(_ => _.message.content[0].id),
774
- )
775
- }
776
-
777
- /**
778
- * Tool uses are in flight if either:
779
- * 1. They have a corresponding progress message and no result message
780
- * 2. They are the first unresoved tool use
781
- *
782
- * TODO: Find a way to harden this logic to make it more explicit
783
- */
784
- export function getInProgressToolUseIDs(
785
- normalizedMessages: NormalizedMessage[],
786
- ): Set<string> {
787
- const unresolvedToolUseIDs = getUnresolvedToolUseIDs(normalizedMessages)
788
- const toolUseIDsThatHaveProgressMessages = new Set(
789
- normalizedMessages.filter(_ => _.type === 'progress').map(_ => _.toolUseID),
790
- )
791
- return new Set(
792
- (
793
- normalizedMessages.filter(_ => {
794
- if (_.type !== 'assistant') {
795
- return false
796
- }
797
- if (_.message.content[0]?.type !== 'tool_use') {
798
- return false
799
- }
800
- const toolUseID = _.message.content[0].id
801
- if (toolUseID === unresolvedToolUseIDs.values().next().value) {
802
- return true
803
- }
804
-
805
- if (
806
- toolUseIDsThatHaveProgressMessages.has(toolUseID) &&
807
- unresolvedToolUseIDs.has(toolUseID)
808
- ) {
809
- return true
810
- }
811
-
812
- return false
813
- }) as AssistantMessage[]
814
- ).map(_ => (_.message.content[0]! as ToolUseBlockParam).id),
815
- )
816
- }
817
-
818
- export function getErroredToolUseMessages(
819
- normalizedMessages: NormalizedMessage[],
820
- ): AssistantMessage[] {
821
- const toolResults = getToolResultIDs(normalizedMessages)
822
- return normalizedMessages.filter(
823
- _ =>
824
- _.type === 'assistant' &&
825
- Array.isArray(_.message.content) &&
826
- _.message.content[0]?.type === 'tool_use' &&
827
- _.message.content[0]?.id in toolResults &&
828
- toolResults[_.message.content[0]?.id],
829
- ) as AssistantMessage[]
830
- }
831
-
832
- export function normalizeMessagesForAPI(
833
- messages: Message[],
834
- ): (UserMessage | AssistantMessage)[] {
835
- const result: (UserMessage | AssistantMessage)[] = []
836
- messages
837
- .filter(_ => _.type !== 'progress')
838
- .forEach(message => {
839
- switch (message.type) {
840
- case 'user': {
841
- // If the current message is not a tool result, add it to the result
842
- if (
843
- !Array.isArray(message.message.content) ||
844
- message.message.content[0]?.type !== 'tool_result'
845
- ) {
846
- result.push(message)
847
- return
848
- }
849
-
850
- // If the last message is not a tool result, add it to the result
851
- const lastMessage = last(result)
852
- if (
853
- !lastMessage ||
854
- lastMessage?.type === 'assistant' ||
855
- !Array.isArray(lastMessage.message.content) ||
856
- lastMessage.message.content[0]?.type !== 'tool_result'
857
- ) {
858
- result.push(message)
859
- return
860
- }
861
-
862
- // Otherwise, merge the current message with the last message
863
- result[result.indexOf(lastMessage)] = {
864
- ...lastMessage,
865
- message: {
866
- ...lastMessage.message,
867
- content: [
868
- ...lastMessage.message.content,
869
- ...message.message.content,
870
- ],
871
- },
872
- }
873
- return
874
- }
875
- case 'assistant':
876
- result.push(message)
877
- return
878
- }
879
- })
880
- return result
881
- }
882
-
883
- // Sometimes the API returns empty messages (eg. "\n\n"). We need to filter these out,
884
- // otherwise they will give an API error when we send them to the API next time we call query().
885
- export function normalizeContentFromAPI(
886
- content: APIMessage['content'],
887
- ): APIMessage['content'] {
888
- const filteredContent = content.filter(
889
- _ => _.type !== 'text' || _.text.trim().length > 0,
890
- )
891
-
892
- if (filteredContent.length === 0) {
893
- return [{ type: 'text', text: NO_CONTENT_MESSAGE, citations: [] }]
894
- }
895
-
896
- return filteredContent
897
- }
898
-
899
- export function isEmptyMessageText(text: string): boolean {
900
- return (
901
- stripSystemMessages(text).trim() === '' ||
902
- text.trim() === NO_CONTENT_MESSAGE
903
- )
904
- }
905
- const STRIPPED_TAGS = [
906
- 'commit_analysis',
907
- 'context',
908
- 'function_analysis',
909
- 'pr_analysis',
910
- ]
911
-
912
- export function stripSystemMessages(content: string): string {
913
- const regex = new RegExp(`<(${STRIPPED_TAGS.join('|')})>.*?</\\1>\n?`, 'gs')
914
- return content.replace(regex, '').trim()
915
- }
916
-
917
- export function getToolUseID(message: NormalizedMessage): string | null {
918
- switch (message.type) {
919
- case 'assistant':
920
- if (message.message.content[0]?.type !== 'tool_use') {
921
- return null
922
- }
923
- return message.message.content[0].id
924
- case 'user':
925
- if (message.message.content[0]?.type !== 'tool_result') {
926
- return null
927
- }
928
- return message.message.content[0].tool_use_id
929
- case 'progress':
930
- return message.toolUseID
931
- }
932
- }
933
-
934
- export function getLastAssistantMessageId(
935
- messages: Message[],
936
- ): string | undefined {
937
- // Iterate from the end of the array to find the last assistant message
938
- for (let i = messages.length - 1; i >= 0; i--) {
939
- const message = messages[i]
940
- if (message && message.type === 'assistant') {
941
- return message.message.id
942
- }
943
- }
944
- return undefined
945
- }