@renxqoo/renx-code 0.0.3 → 0.0.4

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 (306) hide show
  1. package/README.md +58 -223
  2. package/bin/renx.cjs +34 -0
  3. package/package.json +27 -83
  4. package/src/App.tsx +297 -0
  5. package/src/agent/runtime/event-format.ts +258 -0
  6. package/src/agent/runtime/model-types.ts +13 -0
  7. package/src/agent/runtime/runtime.context-usage.test.ts +193 -0
  8. package/src/agent/runtime/runtime.error-handling.test.ts +236 -0
  9. package/src/agent/runtime/runtime.simple.test.ts +16 -0
  10. package/src/agent/runtime/runtime.test.ts +293 -0
  11. package/src/agent/runtime/runtime.ts +881 -0
  12. package/src/agent/runtime/runtime.usage-forwarding.test.ts +229 -0
  13. package/src/agent/runtime/source-modules.test.ts +57 -0
  14. package/src/agent/runtime/source-modules.ts +353 -0
  15. package/src/agent/runtime/tool-call-buffer.test.ts +65 -0
  16. package/src/agent/runtime/tool-call-buffer.ts +60 -0
  17. package/src/agent/runtime/tool-confirmation.test.ts +56 -0
  18. package/src/agent/runtime/tool-confirmation.ts +15 -0
  19. package/src/agent/runtime/types.ts +99 -0
  20. package/src/commands/slash-commands.test.ts +216 -0
  21. package/src/commands/slash-commands.ts +64 -0
  22. package/src/components/chat/assistant-reply.test.tsx +47 -0
  23. package/src/components/chat/assistant-reply.tsx +136 -0
  24. package/src/components/chat/assistant-segment.test.ts +99 -0
  25. package/src/components/chat/assistant-segment.tsx +125 -0
  26. package/src/components/chat/assistant-tool-group.tsx +900 -0
  27. package/src/components/chat/code-block.test.tsx +206 -0
  28. package/src/components/chat/code-block.tsx +313 -0
  29. package/src/components/chat/prompt-card.tsx +81 -0
  30. package/src/components/chat/segment-groups.test.ts +52 -0
  31. package/src/components/chat/segment-groups.ts +106 -0
  32. package/src/components/chat/turn-item.tsx +39 -0
  33. package/src/components/conversation-panel.tsx +43 -0
  34. package/src/components/file-mention-menu.tsx +77 -0
  35. package/src/components/file-picker-dialog.tsx +206 -0
  36. package/src/components/footer-hints.tsx +75 -0
  37. package/src/components/model-picker-dialog.tsx +248 -0
  38. package/src/components/prompt.tsx +233 -0
  39. package/src/components/slash-command-menu.tsx +65 -0
  40. package/src/components/tool-confirm-dialog-content.test.ts +103 -0
  41. package/src/components/tool-confirm-dialog-content.ts +186 -0
  42. package/src/components/tool-confirm-dialog.tsx +187 -0
  43. package/src/components/tool-display-config.ts +119 -0
  44. package/src/context-usage-regressions.test.ts +26 -0
  45. package/src/files/attachment-capabilities.test.ts +30 -0
  46. package/src/files/attachment-capabilities.ts +50 -0
  47. package/src/files/attachment-content.ts +153 -0
  48. package/src/files/file-mention-query.test.ts +34 -0
  49. package/src/files/file-mention-query.ts +32 -0
  50. package/src/files/prompt-display.ts +13 -0
  51. package/src/files/types.ts +5 -0
  52. package/src/files/workspace-files.ts +63 -0
  53. package/src/hooks/agent-event-handlers.test.ts +207 -0
  54. package/src/hooks/agent-event-handlers.ts +196 -0
  55. package/src/hooks/chat-local-replies.fixed.test.ts +119 -0
  56. package/src/hooks/chat-local-replies.test.ts +153 -0
  57. package/src/hooks/chat-local-replies.ts +63 -0
  58. package/src/hooks/turn-updater.test.ts +70 -0
  59. package/src/hooks/turn-updater.ts +166 -0
  60. package/src/hooks/use-agent-chat.context.test.ts +10 -0
  61. package/src/hooks/use-agent-chat.status.test.ts +14 -0
  62. package/src/hooks/use-agent-chat.test.ts +80 -0
  63. package/src/hooks/use-agent-chat.ts +621 -0
  64. package/src/hooks/use-file-mention-menu.ts +196 -0
  65. package/src/hooks/use-file-picker.ts +185 -0
  66. package/src/hooks/use-model-picker.ts +196 -0
  67. package/src/hooks/use-slash-command-menu.ts +154 -0
  68. package/src/index.tsx +55 -0
  69. package/src/runtime/clipboard.test.ts +43 -0
  70. package/src/runtime/clipboard.ts +89 -0
  71. package/src/runtime/exit.test.ts +177 -0
  72. package/src/runtime/exit.ts +98 -0
  73. package/src/runtime/runtime-support.test.ts +31 -0
  74. package/src/runtime/terminal-theme.test.ts +55 -0
  75. package/src/runtime/terminal-theme.ts +196 -0
  76. package/src/types/chat.ts +32 -0
  77. package/src/types/message-content.ts +48 -0
  78. package/src/ui/open-code-theme.ts +176 -0
  79. package/src/ui/opencode-markdown.ts +211 -0
  80. package/src/ui/theme.simple.test.ts +52 -0
  81. package/src/ui/theme.test.ts +151 -0
  82. package/src/ui/theme.ts +152 -0
  83. package/src/utils/time.test.ts +144 -0
  84. package/src/utils/time.ts +7 -0
  85. package/tsconfig.json +30 -0
  86. package/LICENSE +0 -21
  87. package/dist/App.d.ts +0 -2
  88. package/dist/App.d.ts.map +0 -1
  89. package/dist/App.js +0 -170
  90. package/dist/App.js.map +0 -1
  91. package/dist/agent/prompts/system.d.ts +0 -24
  92. package/dist/agent/prompts/system.d.ts.map +0 -1
  93. package/dist/agent/prompts/system.js +0 -222
  94. package/dist/agent/prompts/system.js.map +0 -1
  95. package/dist/agent/runtime/event-format.d.ts +0 -17
  96. package/dist/agent/runtime/event-format.d.ts.map +0 -1
  97. package/dist/agent/runtime/event-format.js +0 -194
  98. package/dist/agent/runtime/event-format.js.map +0 -1
  99. package/dist/agent/runtime/model-types.d.ts +0 -13
  100. package/dist/agent/runtime/model-types.d.ts.map +0 -1
  101. package/dist/agent/runtime/model-types.js +0 -1
  102. package/dist/agent/runtime/model-types.js.map +0 -1
  103. package/dist/agent/runtime/runtime.d.ts +0 -16
  104. package/dist/agent/runtime/runtime.d.ts.map +0 -1
  105. package/dist/agent/runtime/runtime.js +0 -691
  106. package/dist/agent/runtime/runtime.js.map +0 -1
  107. package/dist/agent/runtime/source-modules.d.ts +0 -176
  108. package/dist/agent/runtime/source-modules.d.ts.map +0 -1
  109. package/dist/agent/runtime/source-modules.js +0 -110
  110. package/dist/agent/runtime/source-modules.js.map +0 -1
  111. package/dist/agent/runtime/tool-call-buffer.d.ts +0 -12
  112. package/dist/agent/runtime/tool-call-buffer.d.ts.map +0 -1
  113. package/dist/agent/runtime/tool-call-buffer.js +0 -48
  114. package/dist/agent/runtime/tool-call-buffer.js.map +0 -1
  115. package/dist/agent/runtime/tool-confirmation.d.ts +0 -3
  116. package/dist/agent/runtime/tool-confirmation.d.ts.map +0 -1
  117. package/dist/agent/runtime/tool-confirmation.js +0 -9
  118. package/dist/agent/runtime/tool-confirmation.js.map +0 -1
  119. package/dist/agent/runtime/types.d.ts +0 -86
  120. package/dist/agent/runtime/types.d.ts.map +0 -1
  121. package/dist/agent/runtime/types.js +0 -1
  122. package/dist/agent/runtime/types.js.map +0 -1
  123. package/dist/cli.d.ts +0 -3
  124. package/dist/cli.d.ts.map +0 -1
  125. package/dist/cli.js +0 -12
  126. package/dist/cli.js.map +0 -1
  127. package/dist/commands/slash-commands.d.ts +0 -11
  128. package/dist/commands/slash-commands.d.ts.map +0 -1
  129. package/dist/commands/slash-commands.js +0 -48
  130. package/dist/commands/slash-commands.js.map +0 -1
  131. package/dist/components/chat/assistant-reply.d.ts +0 -13
  132. package/dist/components/chat/assistant-reply.d.ts.map +0 -1
  133. package/dist/components/chat/assistant-reply.js +0 -78
  134. package/dist/components/chat/assistant-reply.js.map +0 -1
  135. package/dist/components/chat/assistant-segment.d.ts +0 -8
  136. package/dist/components/chat/assistant-segment.d.ts.map +0 -1
  137. package/dist/components/chat/assistant-segment.js +0 -54
  138. package/dist/components/chat/assistant-segment.js.map +0 -1
  139. package/dist/components/chat/assistant-tool-group.d.ts +0 -7
  140. package/dist/components/chat/assistant-tool-group.d.ts.map +0 -1
  141. package/dist/components/chat/assistant-tool-group.js +0 -695
  142. package/dist/components/chat/assistant-tool-group.js.map +0 -1
  143. package/dist/components/chat/code-block.d.ts +0 -16
  144. package/dist/components/chat/code-block.d.ts.map +0 -1
  145. package/dist/components/chat/code-block.js +0 -194
  146. package/dist/components/chat/code-block.js.map +0 -1
  147. package/dist/components/chat/prompt-card.d.ts +0 -9
  148. package/dist/components/chat/prompt-card.d.ts.map +0 -1
  149. package/dist/components/chat/prompt-card.js +0 -18
  150. package/dist/components/chat/prompt-card.js.map +0 -1
  151. package/dist/components/chat/segment-groups.d.ts +0 -24
  152. package/dist/components/chat/segment-groups.d.ts.map +0 -1
  153. package/dist/components/chat/segment-groups.js +0 -69
  154. package/dist/components/chat/segment-groups.js.map +0 -1
  155. package/dist/components/chat/turn-item.d.ts +0 -9
  156. package/dist/components/chat/turn-item.d.ts.map +0 -1
  157. package/dist/components/chat/turn-item.js +0 -11
  158. package/dist/components/chat/turn-item.js.map +0 -1
  159. package/dist/components/conversation-panel.d.ts +0 -8
  160. package/dist/components/conversation-panel.d.ts.map +0 -1
  161. package/dist/components/conversation-panel.js +0 -8
  162. package/dist/components/conversation-panel.js.map +0 -1
  163. package/dist/components/file-mention-menu.d.ts +0 -11
  164. package/dist/components/file-mention-menu.d.ts.map +0 -1
  165. package/dist/components/file-mention-menu.js +0 -15
  166. package/dist/components/file-mention-menu.js.map +0 -1
  167. package/dist/components/file-picker-dialog.d.ts +0 -21
  168. package/dist/components/file-picker-dialog.d.ts.map +0 -1
  169. package/dist/components/file-picker-dialog.js +0 -48
  170. package/dist/components/file-picker-dialog.js.map +0 -1
  171. package/dist/components/footer-hints.d.ts +0 -7
  172. package/dist/components/footer-hints.d.ts.map +0 -1
  173. package/dist/components/footer-hints.js +0 -29
  174. package/dist/components/footer-hints.js.map +0 -1
  175. package/dist/components/model-picker-dialog.d.ts +0 -20
  176. package/dist/components/model-picker-dialog.d.ts.map +0 -1
  177. package/dist/components/model-picker-dialog.js +0 -72
  178. package/dist/components/model-picker-dialog.js.map +0 -1
  179. package/dist/components/prompt.d.ts +0 -18
  180. package/dist/components/prompt.d.ts.map +0 -1
  181. package/dist/components/prompt.js +0 -96
  182. package/dist/components/prompt.js.map +0 -1
  183. package/dist/components/slash-command-menu.d.ts +0 -9
  184. package/dist/components/slash-command-menu.d.ts.map +0 -1
  185. package/dist/components/slash-command-menu.js +0 -20
  186. package/dist/components/slash-command-menu.js.map +0 -1
  187. package/dist/components/tool-confirm-dialog-content.d.ts +0 -15
  188. package/dist/components/tool-confirm-dialog-content.d.ts.map +0 -1
  189. package/dist/components/tool-confirm-dialog-content.js +0 -143
  190. package/dist/components/tool-confirm-dialog-content.js.map +0 -1
  191. package/dist/components/tool-confirm-dialog.d.ts +0 -12
  192. package/dist/components/tool-confirm-dialog.d.ts.map +0 -1
  193. package/dist/components/tool-confirm-dialog.js +0 -21
  194. package/dist/components/tool-confirm-dialog.js.map +0 -1
  195. package/dist/components/tool-display-config.d.ts +0 -11
  196. package/dist/components/tool-display-config.d.ts.map +0 -1
  197. package/dist/components/tool-display-config.js +0 -94
  198. package/dist/components/tool-display-config.js.map +0 -1
  199. package/dist/config/paths.d.ts +0 -7
  200. package/dist/config/paths.d.ts.map +0 -1
  201. package/dist/config/paths.js +0 -24
  202. package/dist/config/paths.js.map +0 -1
  203. package/dist/files/attachment-capabilities.d.ts +0 -19
  204. package/dist/files/attachment-capabilities.d.ts.map +0 -1
  205. package/dist/files/attachment-capabilities.js +0 -26
  206. package/dist/files/attachment-capabilities.js.map +0 -1
  207. package/dist/files/attachment-content.d.ts +0 -5
  208. package/dist/files/attachment-content.d.ts.map +0 -1
  209. package/dist/files/attachment-content.js +0 -117
  210. package/dist/files/attachment-content.js.map +0 -1
  211. package/dist/files/file-mention-query.d.ts +0 -9
  212. package/dist/files/file-mention-query.d.ts.map +0 -1
  213. package/dist/files/file-mention-query.js +0 -23
  214. package/dist/files/file-mention-query.js.map +0 -1
  215. package/dist/files/prompt-display.d.ts +0 -3
  216. package/dist/files/prompt-display.d.ts.map +0 -1
  217. package/dist/files/prompt-display.js +0 -11
  218. package/dist/files/prompt-display.js.map +0 -1
  219. package/dist/files/types.d.ts +0 -6
  220. package/dist/files/types.d.ts.map +0 -1
  221. package/dist/files/types.js +0 -1
  222. package/dist/files/types.js.map +0 -1
  223. package/dist/files/workspace-files.d.ts +0 -3
  224. package/dist/files/workspace-files.d.ts.map +0 -1
  225. package/dist/files/workspace-files.js +0 -48
  226. package/dist/files/workspace-files.js.map +0 -1
  227. package/dist/hooks/agent-event-handlers.d.ts +0 -11
  228. package/dist/hooks/agent-event-handlers.d.ts.map +0 -1
  229. package/dist/hooks/agent-event-handlers.js +0 -137
  230. package/dist/hooks/agent-event-handlers.js.map +0 -1
  231. package/dist/hooks/chat-local-replies.d.ts +0 -9
  232. package/dist/hooks/chat-local-replies.d.ts.map +0 -1
  233. package/dist/hooks/chat-local-replies.js +0 -54
  234. package/dist/hooks/chat-local-replies.js.map +0 -1
  235. package/dist/hooks/turn-updater.d.ts +0 -9
  236. package/dist/hooks/turn-updater.d.ts.map +0 -1
  237. package/dist/hooks/turn-updater.js +0 -103
  238. package/dist/hooks/turn-updater.js.map +0 -1
  239. package/dist/hooks/use-agent-chat.d.ts +0 -29
  240. package/dist/hooks/use-agent-chat.d.ts.map +0 -1
  241. package/dist/hooks/use-agent-chat.js +0 -455
  242. package/dist/hooks/use-agent-chat.js.map +0 -1
  243. package/dist/hooks/use-file-mention-menu.d.ts +0 -22
  244. package/dist/hooks/use-file-mention-menu.d.ts.map +0 -1
  245. package/dist/hooks/use-file-mention-menu.js +0 -137
  246. package/dist/hooks/use-file-mention-menu.js.map +0 -1
  247. package/dist/hooks/use-file-picker.d.ts +0 -21
  248. package/dist/hooks/use-file-picker.d.ts.map +0 -1
  249. package/dist/hooks/use-file-picker.js +0 -145
  250. package/dist/hooks/use-file-picker.js.map +0 -1
  251. package/dist/hooks/use-model-picker.d.ts +0 -23
  252. package/dist/hooks/use-model-picker.d.ts.map +0 -1
  253. package/dist/hooks/use-model-picker.js +0 -151
  254. package/dist/hooks/use-model-picker.js.map +0 -1
  255. package/dist/hooks/use-slash-command-menu.d.ts +0 -19
  256. package/dist/hooks/use-slash-command-menu.d.ts.map +0 -1
  257. package/dist/hooks/use-slash-command-menu.js +0 -101
  258. package/dist/hooks/use-slash-command-menu.js.map +0 -1
  259. package/dist/index.d.ts +0 -2
  260. package/dist/index.d.ts.map +0 -1
  261. package/dist/index.js +0 -6
  262. package/dist/index.js.map +0 -1
  263. package/dist/run-cli-app.d.ts +0 -2
  264. package/dist/run-cli-app.d.ts.map +0 -1
  265. package/dist/run-cli-app.js +0 -41
  266. package/dist/run-cli-app.js.map +0 -1
  267. package/dist/runtime/clipboard.d.ts +0 -10
  268. package/dist/runtime/clipboard.d.ts.map +0 -1
  269. package/dist/runtime/clipboard.js +0 -64
  270. package/dist/runtime/clipboard.js.map +0 -1
  271. package/dist/runtime/exit.d.ts +0 -7
  272. package/dist/runtime/exit.d.ts.map +0 -1
  273. package/dist/runtime/exit.js +0 -85
  274. package/dist/runtime/exit.js.map +0 -1
  275. package/dist/runtime/runtime-support.d.ts +0 -4
  276. package/dist/runtime/runtime-support.d.ts.map +0 -1
  277. package/dist/runtime/runtime-support.js +0 -19
  278. package/dist/runtime/runtime-support.js.map +0 -1
  279. package/dist/runtime/terminal-theme.d.ts +0 -25
  280. package/dist/runtime/terminal-theme.d.ts.map +0 -1
  281. package/dist/runtime/terminal-theme.js +0 -148
  282. package/dist/runtime/terminal-theme.js.map +0 -1
  283. package/dist/types/chat.d.ts +0 -29
  284. package/dist/types/chat.d.ts.map +0 -1
  285. package/dist/types/chat.js +0 -1
  286. package/dist/types/chat.js.map +0 -1
  287. package/dist/types/message-content.d.ts +0 -38
  288. package/dist/types/message-content.d.ts.map +0 -1
  289. package/dist/types/message-content.js +0 -1
  290. package/dist/types/message-content.js.map +0 -1
  291. package/dist/ui/open-code-theme.d.ts +0 -58
  292. package/dist/ui/open-code-theme.d.ts.map +0 -1
  293. package/dist/ui/open-code-theme.js +0 -113
  294. package/dist/ui/open-code-theme.js.map +0 -1
  295. package/dist/ui/opencode-markdown.d.ts +0 -7
  296. package/dist/ui/opencode-markdown.d.ts.map +0 -1
  297. package/dist/ui/opencode-markdown.js +0 -169
  298. package/dist/ui/opencode-markdown.js.map +0 -1
  299. package/dist/ui/theme.d.ts +0 -68
  300. package/dist/ui/theme.d.ts.map +0 -1
  301. package/dist/ui/theme.js +0 -80
  302. package/dist/ui/theme.js.map +0 -1
  303. package/dist/utils/time.d.ts +0 -2
  304. package/dist/utils/time.d.ts.map +0 -1
  305. package/dist/utils/time.js +0 -7
  306. package/dist/utils/time.js.map +0 -1
@@ -0,0 +1,293 @@
1
+ import * as path from 'node:path';
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ vi.mock('./tool-confirmation', () => ({
5
+ resolveToolConfirmDecision: vi.fn().mockResolvedValue({ approved: true, message: 'Approved' }),
6
+ }));
7
+
8
+ vi.mock('./source-modules', () => ({
9
+ getSourceModules: vi.fn(),
10
+ resolveWorkspaceRoot: vi.fn(),
11
+ }));
12
+
13
+ vi.mock('../../../../src/agent/prompts/system', () => ({
14
+ buildSystemPrompt: vi.fn(() => 'Test system prompt'),
15
+ }));
16
+
17
+ import {
18
+ disposeAgentRuntime,
19
+ getAgentModelId,
20
+ getAgentModelLabel,
21
+ listAgentModels,
22
+ runAgentPrompt,
23
+ switchAgentModel,
24
+ } from './runtime';
25
+ import * as sourceModules from './source-modules';
26
+ import type { AgentEventHandlers } from './types';
27
+
28
+ const buildMockModules = () => {
29
+ class FakeToolManager {
30
+ private readonly tools: unknown[] = [];
31
+
32
+ registerTool = vi.fn((tool: unknown) => {
33
+ this.tools.push(tool);
34
+ });
35
+
36
+ getTools = vi.fn(() => this.tools);
37
+ }
38
+
39
+ const createNamedTool = (name: string) =>
40
+ class NamedFakeTool {
41
+ toToolSchema() {
42
+ return {
43
+ type: 'function',
44
+ function: {
45
+ name,
46
+ },
47
+ };
48
+ }
49
+ };
50
+
51
+ class FakeAgent {
52
+ on = vi.fn();
53
+ off = vi.fn();
54
+ }
55
+
56
+ class FakeAppService {
57
+ static lastRequest: unknown;
58
+
59
+ async listContextMessages() {
60
+ return [];
61
+ }
62
+
63
+ async runForeground(request: unknown) {
64
+ FakeAppService.lastRequest = request;
65
+ return {
66
+ executionId: 'exec_runtime',
67
+ conversationId: 'conv_runtime',
68
+ messages: [
69
+ {
70
+ messageId: 'msg_assistant',
71
+ role: 'assistant',
72
+ type: 'assistant-text',
73
+ content: 'done',
74
+ },
75
+ ],
76
+ finishReason: 'stop' as const,
77
+ steps: 1,
78
+ run: {},
79
+ };
80
+ }
81
+ }
82
+
83
+ class FakeAppStore {
84
+ static lastDbPath: string | undefined;
85
+
86
+ constructor(dbPath: string) {
87
+ FakeAppStore.lastDbPath = dbPath;
88
+ }
89
+
90
+ close = vi.fn().mockResolvedValue(undefined);
91
+ }
92
+
93
+ return {
94
+ ProviderRegistry: {
95
+ getModelIds: () => ['glm-5', 'claude-3.5-sonnet'],
96
+ getModelConfig: (modelId: string) => ({
97
+ name: modelId === 'glm-5' ? 'GLM-5' : 'Claude 3.5 Sonnet',
98
+ envApiKey: 'TEST_API_KEY',
99
+ provider: modelId === 'glm-5' ? 'zhipu' : 'anthropic',
100
+ model: modelId,
101
+ }),
102
+ createFromEnv: () => ({}),
103
+ },
104
+ loadEnvFiles: vi.fn().mockResolvedValue([]),
105
+ loadConfigToEnv: vi.fn().mockReturnValue([]),
106
+ createLoggerFromEnv: vi.fn(() => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn() })),
107
+ createAgentLoggerAdapter: vi.fn((logger: Record<string, unknown>) => ({
108
+ info: typeof logger.info === 'function' ? logger.info.bind(logger) : undefined,
109
+ warn: typeof logger.warn === 'function' ? logger.warn.bind(logger) : undefined,
110
+ error: typeof logger.error === 'function' ? logger.error.bind(logger) : undefined,
111
+ })),
112
+ StatelessAgent: FakeAgent,
113
+ AgentAppService: FakeAppService,
114
+ createSqliteAgentAppStore: (dbPath: string) => new FakeAppStore(dbPath),
115
+ DefaultToolManager: FakeToolManager,
116
+ BashTool: createNamedTool('bash'),
117
+ WriteFileTool: createNamedTool('write_file'),
118
+ FileReadTool: createNamedTool('file_read'),
119
+ FileEditTool: createNamedTool('file_edit'),
120
+ FileHistoryListTool: createNamedTool('file_history_list'),
121
+ FileHistoryRestoreTool: createNamedTool('file_history_restore'),
122
+ GlobTool: createNamedTool('glob'),
123
+ GrepTool: createNamedTool('grep'),
124
+ SkillTool: createNamedTool('skill'),
125
+ TaskTool: createNamedTool('agent'),
126
+ TaskCreateTool: createNamedTool('task_create'),
127
+ TaskGetTool: createNamedTool('task_get'),
128
+ TaskListTool: createNamedTool('task_list'),
129
+ TaskUpdateTool: createNamedTool('task_update'),
130
+ TaskStopTool: createNamedTool('task_stop'),
131
+ TaskOutputTool: createNamedTool('task_output'),
132
+ TaskStore: class {},
133
+ RealSubagentRunnerAdapter: class {},
134
+ };
135
+ };
136
+
137
+ describe('runtime', () => {
138
+ const mockGetSourceModules = sourceModules.getSourceModules as unknown as ReturnType<
139
+ typeof vi.fn
140
+ >;
141
+ const mockResolveWorkspaceRoot = sourceModules.resolveWorkspaceRoot as unknown as ReturnType<
142
+ typeof vi.fn
143
+ >;
144
+ const originalApiKey = process.env.TEST_API_KEY;
145
+ const originalRenxHome = process.env.RENX_HOME;
146
+ const originalPromptCacheKey = process.env.AGENT_PROMPT_CACHE_KEY;
147
+ const originalPromptCacheRetention = process.env.AGENT_PROMPT_CACHE_RETENTION;
148
+ const renxHome = path.join(process.cwd(), '.tmp-renx-home');
149
+
150
+ beforeEach(() => {
151
+ vi.clearAllMocks();
152
+ process.env.TEST_API_KEY = 'test-key';
153
+ process.env.RENX_HOME = renxHome;
154
+ delete process.env.AGENT_PROMPT_CACHE_KEY;
155
+ delete process.env.AGENT_PROMPT_CACHE_RETENTION;
156
+ mockResolveWorkspaceRoot.mockReturnValue('/test/workspace');
157
+ mockGetSourceModules.mockResolvedValue(buildMockModules());
158
+ });
159
+
160
+ afterEach(async () => {
161
+ await disposeAgentRuntime();
162
+ if (originalApiKey === undefined) {
163
+ delete process.env.TEST_API_KEY;
164
+ } else {
165
+ process.env.TEST_API_KEY = originalApiKey;
166
+ }
167
+ if (originalRenxHome === undefined) {
168
+ delete process.env.RENX_HOME;
169
+ } else {
170
+ process.env.RENX_HOME = originalRenxHome;
171
+ }
172
+ if (originalPromptCacheKey === undefined) {
173
+ delete process.env.AGENT_PROMPT_CACHE_KEY;
174
+ } else {
175
+ process.env.AGENT_PROMPT_CACHE_KEY = originalPromptCacheKey;
176
+ }
177
+ if (originalPromptCacheRetention === undefined) {
178
+ delete process.env.AGENT_PROMPT_CACHE_RETENTION;
179
+ } else {
180
+ process.env.AGENT_PROMPT_CACHE_RETENTION = originalPromptCacheRetention;
181
+ }
182
+ });
183
+
184
+ it('returns the active model label', async () => {
185
+ await expect(getAgentModelLabel()).resolves.toBe('GLM-5');
186
+ });
187
+
188
+ it('returns the active model id', async () => {
189
+ await expect(getAgentModelId()).resolves.toBe('glm-5');
190
+ });
191
+
192
+ it('lists models with current selection', async () => {
193
+ await expect(listAgentModels()).resolves.toEqual([
194
+ {
195
+ id: 'claude-3.5-sonnet',
196
+ name: 'Claude 3.5 Sonnet',
197
+ provider: 'anthropic',
198
+ apiKeyEnv: 'TEST_API_KEY',
199
+ configured: true,
200
+ current: false,
201
+ },
202
+ {
203
+ id: 'glm-5',
204
+ name: 'GLM-5',
205
+ provider: 'zhipu',
206
+ apiKeyEnv: 'TEST_API_KEY',
207
+ configured: true,
208
+ current: true,
209
+ },
210
+ ]);
211
+ });
212
+
213
+ it('switches model when the target is configured', async () => {
214
+ await expect(switchAgentModel('claude-3.5-sonnet')).resolves.toEqual({
215
+ modelId: 'claude-3.5-sonnet',
216
+ modelLabel: 'Claude 3.5 Sonnet',
217
+ });
218
+ });
219
+
220
+ it('runs a prompt and returns the assembled result', async () => {
221
+ const handlers: AgentEventHandlers = {
222
+ onTextDelta: vi.fn(),
223
+ onTextComplete: vi.fn(),
224
+ onToolUse: vi.fn(),
225
+ onToolStream: vi.fn(),
226
+ onToolResult: vi.fn(),
227
+ onToolConfirm: vi.fn(),
228
+ onUsage: vi.fn(),
229
+ };
230
+
231
+ await expect(runAgentPrompt('Test prompt', handlers)).resolves.toEqual(
232
+ expect.objectContaining({
233
+ text: 'done',
234
+ completionReason: 'stop',
235
+ modelLabel: 'GLM-5',
236
+ })
237
+ );
238
+ });
239
+
240
+ it('stores app state under RENX_HOME by default', async () => {
241
+ const modules = buildMockModules();
242
+ const appStoreClass = modules.createSqliteAgentAppStore('/ignore').constructor as {
243
+ lastDbPath?: string;
244
+ };
245
+ mockGetSourceModules.mockResolvedValue(
246
+ modules as unknown as Awaited<ReturnType<typeof sourceModules.getSourceModules>>
247
+ );
248
+
249
+ await disposeAgentRuntime();
250
+ await runAgentPrompt('Test prompt', {});
251
+
252
+ expect(appStoreClass.lastDbPath).toBe(path.join(renxHome, 'data.db'));
253
+ });
254
+
255
+ it('hides file history tools from the parent agent tool list', async () => {
256
+ const modules = buildMockModules();
257
+ const appServiceClass = modules.AgentAppService as {
258
+ lastRequest?: { tools?: Array<{ function?: { name?: string } }> };
259
+ };
260
+ mockGetSourceModules.mockResolvedValue(
261
+ modules as unknown as Awaited<ReturnType<typeof sourceModules.getSourceModules>>
262
+ );
263
+
264
+ await runAgentPrompt('Test prompt', {});
265
+
266
+ const toolNames =
267
+ appServiceClass.lastRequest?.tools?.map(tool => tool.function?.name).filter(Boolean) || [];
268
+
269
+ expect(toolNames).toContain('file_read');
270
+ expect(toolNames).toContain('file_edit');
271
+ expect(toolNames).not.toContain('file_history_list');
272
+ expect(toolNames).not.toContain('file_history_restore');
273
+ });
274
+
275
+ it('forwards prompt cache config from env to the app request', async () => {
276
+ const modules = buildMockModules();
277
+ const appServiceClass = modules.AgentAppService as {
278
+ lastRequest?: { config?: Record<string, unknown> };
279
+ };
280
+ mockGetSourceModules.mockResolvedValue(
281
+ modules as unknown as Awaited<ReturnType<typeof sourceModules.getSourceModules>>
282
+ );
283
+ process.env.AGENT_PROMPT_CACHE_KEY = 'cache-{conversationId}';
284
+ process.env.AGENT_PROMPT_CACHE_RETENTION = '24h';
285
+
286
+ await runAgentPrompt('Test prompt', {});
287
+
288
+ expect(appServiceClass.lastRequest?.config).toEqual({
289
+ prompt_cache_key: expect.stringMatching(/^cache-opentui-/),
290
+ prompt_cache_retention: '24h',
291
+ });
292
+ });
293
+ });