@renxqoo/renx-code 0.0.4 → 0.0.6

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 (210) hide show
  1. package/README.md +82 -51
  2. package/bin/renx.cjs +16 -0
  3. package/package.json +2 -45
  4. package/src/agent/runtime/runtime.context-usage.test.ts +4 -5
  5. package/src/agent/runtime/runtime.error-handling.test.ts +4 -5
  6. package/src/agent/runtime/runtime.test.ts +7 -4
  7. package/src/agent/runtime/runtime.ts +3 -9
  8. package/src/agent/runtime/runtime.usage-forwarding.test.ts +4 -5
  9. package/src/agent/runtime/source-modules.test.ts +16 -35
  10. package/src/agent/runtime/source-modules.ts +17 -0
  11. package/vendor/agent-root/src/agent/ENTERPRISE_ACCEPTANCE_CHECKLIST.md +95 -0
  12. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.html +1345 -0
  13. package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.md +1353 -0
  14. package/vendor/agent-root/src/agent/ERROR_CONTRACT.md +60 -0
  15. package/vendor/agent-root/src/agent/TEST_COVERAGE_ANALYSIS.md +278 -0
  16. package/vendor/agent-root/src/agent/__test__/error-contract.test.ts +72 -0
  17. package/vendor/agent-root/src/agent/__test__/types.test.ts +137 -0
  18. package/vendor/agent-root/src/agent/agent/__test__/abort-runtime.test.ts +83 -0
  19. package/vendor/agent-root/src/agent/agent/__test__/callback-safety.test.ts +34 -0
  20. package/vendor/agent-root/src/agent/agent/__test__/compaction.test.ts +323 -0
  21. package/vendor/agent-root/src/agent/agent/__test__/concurrency.test.ts +290 -0
  22. package/vendor/agent-root/src/agent/agent/__test__/error-normalizer.test.ts +377 -0
  23. package/vendor/agent-root/src/agent/agent/__test__/error.test.ts +212 -0
  24. package/vendor/agent-root/src/agent/agent/__test__/fault-injection.test.ts +295 -0
  25. package/vendor/agent-root/src/agent/agent/__test__/index.test.ts +3607 -0
  26. package/vendor/agent-root/src/agent/agent/__test__/logger.test.ts +35 -0
  27. package/vendor/agent-root/src/agent/agent/__test__/message-utils.test.ts +517 -0
  28. package/vendor/agent-root/src/agent/agent/__test__/telemetry.test.ts +97 -0
  29. package/vendor/agent-root/src/agent/agent/__test__/timeout-budget.test.ts +479 -0
  30. package/vendor/agent-root/src/agent/agent/__test__/tool-call-merge.test.ts +80 -0
  31. package/vendor/agent-root/src/agent/agent/__test__/tool-execution-ledger.test.ts +76 -0
  32. package/vendor/agent-root/src/agent/agent/__test__/write-buffer.test.ts +173 -0
  33. package/vendor/agent-root/src/agent/agent/__test__/write-file-session.test.ts +109 -0
  34. package/vendor/agent-root/src/agent/agent/abort-runtime.ts +71 -0
  35. package/vendor/agent-root/src/agent/agent/callback-safety.ts +33 -0
  36. package/vendor/agent-root/src/agent/agent/compaction.ts +291 -0
  37. package/vendor/agent-root/src/agent/agent/concurrency.ts +103 -0
  38. package/vendor/agent-root/src/agent/agent/error-normalizer.ts +190 -0
  39. package/vendor/agent-root/src/agent/agent/error.ts +198 -0
  40. package/vendor/agent-root/src/agent/agent/index.ts +1772 -0
  41. package/vendor/agent-root/src/agent/agent/logger.ts +65 -0
  42. package/vendor/agent-root/src/agent/agent/message-utils.ts +101 -0
  43. package/vendor/agent-root/src/agent/agent/stream-events.ts +61 -0
  44. package/vendor/agent-root/src/agent/agent/telemetry.ts +123 -0
  45. package/vendor/agent-root/src/agent/agent/timeout-budget.ts +227 -0
  46. package/vendor/agent-root/src/agent/agent/tool-call-merge.ts +111 -0
  47. package/vendor/agent-root/src/agent/agent/tool-execution-ledger.ts +164 -0
  48. package/vendor/agent-root/src/agent/agent/write-buffer.ts +188 -0
  49. package/vendor/agent-root/src/agent/agent/write-file-session.ts +238 -0
  50. package/vendor/agent-root/src/agent/app/__test__/agent-app-service.test.ts +1053 -0
  51. package/vendor/agent-root/src/agent/app/__test__/minimal-agent-application.test.ts +158 -0
  52. package/vendor/agent-root/src/agent/app/__test__/sqlite-agent-app-store.test.ts +437 -0
  53. package/vendor/agent-root/src/agent/app/agent-app-service.ts +748 -0
  54. package/vendor/agent-root/src/agent/app/contracts.ts +109 -0
  55. package/vendor/agent-root/src/agent/app/index.ts +5 -0
  56. package/vendor/agent-root/src/agent/app/minimal-agent-application.ts +151 -0
  57. package/vendor/agent-root/src/agent/app/ports.ts +72 -0
  58. package/vendor/agent-root/src/agent/app/sqlite-agent-app-store.ts +1182 -0
  59. package/vendor/agent-root/src/agent/app/sqlite-client.ts +177 -0
  60. package/vendor/agent-root/src/agent/docs/cli-app-layer/00-README.md +36 -0
  61. package/vendor/agent-root/src/agent/docs/cli-app-layer/01-scope-and-goals.md +33 -0
  62. package/vendor/agent-root/src/agent/docs/cli-app-layer/02-architecture-overview.md +40 -0
  63. package/vendor/agent-root/src/agent/docs/cli-app-layer/03-domain-model-and-contracts.md +91 -0
  64. package/vendor/agent-root/src/agent/docs/cli-app-layer/04-ports-and-interfaces.md +116 -0
  65. package/vendor/agent-root/src/agent/docs/cli-app-layer/05-run-orchestration-and-state-machine.md +52 -0
  66. package/vendor/agent-root/src/agent/docs/cli-app-layer/06-cli-commands-and-ux.md +53 -0
  67. package/vendor/agent-root/src/agent/docs/cli-app-layer/07-storage-design-local.md +52 -0
  68. package/vendor/agent-root/src/agent/docs/cli-app-layer/08-error-and-observability.md +40 -0
  69. package/vendor/agent-root/src/agent/docs/cli-app-layer/09-security-and-policy-boundary.md +19 -0
  70. package/vendor/agent-root/src/agent/docs/cli-app-layer/10-test-plan-and-acceptance.md +28 -0
  71. package/vendor/agent-root/src/agent/docs/cli-app-layer/11-implementation-phases.md +26 -0
  72. package/vendor/agent-root/src/agent/docs/cli-app-layer/12-open-questions-and-risks.md +30 -0
  73. package/vendor/agent-root/src/agent/docs/cli-app-layer/13-sqlite-schema-fields-and-rationale.md +567 -0
  74. package/vendor/agent-root/src/agent/docs/cli-app-layer/14-project-flow-mermaid.md +583 -0
  75. package/vendor/agent-root/src/agent/docs/cli-app-layer/15-openclaw-style-project-blueprint.md +972 -0
  76. package/vendor/agent-root/src/agent/error-contract.ts +154 -0
  77. package/vendor/agent-root/src/agent/prompts/system.ts +246 -0
  78. package/vendor/agent-root/src/agent/prompts/system1.ts +208 -0
  79. package/vendor/agent-root/src/agent/storage/__test__/file-history-store.test.ts +98 -0
  80. package/vendor/agent-root/src/agent/storage/file-history-store.ts +313 -0
  81. package/vendor/agent-root/src/agent/storage/file-storage-config.ts +94 -0
  82. package/vendor/agent-root/src/agent/storage/file-system.ts +31 -0
  83. package/vendor/agent-root/src/agent/storage/file-write-service.ts +21 -0
  84. package/vendor/agent-root/src/agent/tool/__test__/base-tool.test.ts +413 -0
  85. package/vendor/agent-root/src/agent/tool/__test__/bash-policy.test.ts +356 -0
  86. package/vendor/agent-root/src/agent/tool/__test__/bash.mocked-coverage.test.ts +375 -0
  87. package/vendor/agent-root/src/agent/tool/__test__/bash.test.ts +372 -0
  88. package/vendor/agent-root/src/agent/tool/__test__/error.test.ts +108 -0
  89. package/vendor/agent-root/src/agent/tool/__test__/file-edit-tool.test.ts +258 -0
  90. package/vendor/agent-root/src/agent/tool/__test__/file-history-tools.test.ts +121 -0
  91. package/vendor/agent-root/src/agent/tool/__test__/file-read-tool.test.ts +210 -0
  92. package/vendor/agent-root/src/agent/tool/__test__/glob.test.ts +139 -0
  93. package/vendor/agent-root/src/agent/tool/__test__/grep.mocked-coverage.test.ts +456 -0
  94. package/vendor/agent-root/src/agent/tool/__test__/grep.test.ts +192 -0
  95. package/vendor/agent-root/src/agent/tool/__test__/lsp.test.ts +300 -0
  96. package/vendor/agent-root/src/agent/tool/__test__/outside-workspace-confirmation.test.ts +214 -0
  97. package/vendor/agent-root/src/agent/tool/__test__/path-security.test.ts +336 -0
  98. package/vendor/agent-root/src/agent/tool/__test__/skill-loader.test.ts +494 -0
  99. package/vendor/agent-root/src/agent/tool/__test__/skill-parser.test.ts +543 -0
  100. package/vendor/agent-root/src/agent/tool/__test__/skill-tool.test.ts +172 -0
  101. package/vendor/agent-root/src/agent/tool/__test__/task-concurrency-and-version.test.ts +116 -0
  102. package/vendor/agent-root/src/agent/tool/__test__/task-create-get-list-update.test.ts +267 -0
  103. package/vendor/agent-root/src/agent/tool/__test__/task-create.test.ts +519 -0
  104. package/vendor/agent-root/src/agent/tool/__test__/task-errors.test.ts +225 -0
  105. package/vendor/agent-root/src/agent/tool/__test__/task-output-blocking.test.ts +223 -0
  106. package/vendor/agent-root/src/agent/tool/__test__/task-output.test.ts +184 -0
  107. package/vendor/agent-root/src/agent/tool/__test__/task-parent-abort.test.ts +287 -0
  108. package/vendor/agent-root/src/agent/tool/__test__/task-real-runner-adapter.test.ts +190 -0
  109. package/vendor/agent-root/src/agent/tool/__test__/task-run-lifecycle.test.ts +352 -0
  110. package/vendor/agent-root/src/agent/tool/__test__/task-store-runner-branches.test.ts +395 -0
  111. package/vendor/agent-root/src/agent/tool/__test__/task-store.test.ts +391 -0
  112. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config-integration.test.ts +176 -0
  113. package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config.test.ts +68 -0
  114. package/vendor/agent-root/src/agent/tool/__test__/task-tools-core-edges.test.ts +630 -0
  115. package/vendor/agent-root/src/agent/tool/__test__/task-tools-runtime-edges.test.ts +732 -0
  116. package/vendor/agent-root/src/agent/tool/__test__/task-types.test.ts +494 -0
  117. package/vendor/agent-root/src/agent/tool/__test__/task-utils-branches.test.ts +175 -0
  118. package/vendor/agent-root/src/agent/tool/__test__/tool-manager.test.ts +505 -0
  119. package/vendor/agent-root/src/agent/tool/__test__/types.test.ts +55 -0
  120. package/vendor/agent-root/src/agent/tool/__test__/web-fetch.test.ts +244 -0
  121. package/vendor/agent-root/src/agent/tool/__test__/web-search.test.ts +290 -0
  122. package/vendor/agent-root/src/agent/tool/__test__/write-file.test.ts +368 -0
  123. package/vendor/agent-root/src/agent/tool/base-tool.ts +345 -0
  124. package/vendor/agent-root/src/agent/tool/bash-policy.ts +636 -0
  125. package/vendor/agent-root/src/agent/tool/bash.ts +688 -0
  126. package/vendor/agent-root/src/agent/tool/error.ts +131 -0
  127. package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +264 -0
  128. package/vendor/agent-root/src/agent/tool/file-history-list.ts +103 -0
  129. package/vendor/agent-root/src/agent/tool/file-history-restore.ts +149 -0
  130. package/vendor/agent-root/src/agent/tool/file-read-tool.ts +211 -0
  131. package/vendor/agent-root/src/agent/tool/glob.ts +171 -0
  132. package/vendor/agent-root/src/agent/tool/grep.ts +496 -0
  133. package/vendor/agent-root/src/agent/tool/lsp.ts +481 -0
  134. package/vendor/agent-root/src/agent/tool/path-security.ts +117 -0
  135. package/vendor/agent-root/src/agent/tool/search/common.ts +153 -0
  136. package/vendor/agent-root/src/agent/tool/skill/index.ts +13 -0
  137. package/vendor/agent-root/src/agent/tool/skill/loader.ts +229 -0
  138. package/vendor/agent-root/src/agent/tool/skill/parser.ts +124 -0
  139. package/vendor/agent-root/src/agent/tool/skill/types.ts +27 -0
  140. package/vendor/agent-root/src/agent/tool/skill-tool.ts +143 -0
  141. package/vendor/agent-root/src/agent/tool/task-create.ts +186 -0
  142. package/vendor/agent-root/src/agent/tool/task-errors.ts +42 -0
  143. package/vendor/agent-root/src/agent/tool/task-get.ts +116 -0
  144. package/vendor/agent-root/src/agent/tool/task-graph.ts +78 -0
  145. package/vendor/agent-root/src/agent/tool/task-list.ts +141 -0
  146. package/vendor/agent-root/src/agent/tool/task-mock-runner-adapter.ts +232 -0
  147. package/vendor/agent-root/src/agent/tool/task-output.ts +223 -0
  148. package/vendor/agent-root/src/agent/tool/task-parent-abort.ts +115 -0
  149. package/vendor/agent-root/src/agent/tool/task-real-runner-adapter.ts +336 -0
  150. package/vendor/agent-root/src/agent/tool/task-runner-adapter.ts +55 -0
  151. package/vendor/agent-root/src/agent/tool/task-stop.ts +187 -0
  152. package/vendor/agent-root/src/agent/tool/task-store.ts +217 -0
  153. package/vendor/agent-root/src/agent/tool/task-subagent-config.ts +149 -0
  154. package/vendor/agent-root/src/agent/tool/task-types.ts +264 -0
  155. package/vendor/agent-root/src/agent/tool/task-update.ts +315 -0
  156. package/vendor/agent-root/src/agent/tool/task.ts +209 -0
  157. package/vendor/agent-root/src/agent/tool/tool-manager.ts +362 -0
  158. package/vendor/agent-root/src/agent/tool/tool-prompts.ts +242 -0
  159. package/vendor/agent-root/src/agent/tool/types.ts +116 -0
  160. package/vendor/agent-root/src/agent/tool/web-fetch.ts +227 -0
  161. package/vendor/agent-root/src/agent/tool/web-search.ts +208 -0
  162. package/vendor/agent-root/src/agent/tool/write-file.ts +497 -0
  163. package/vendor/agent-root/src/agent/types.ts +232 -0
  164. package/vendor/agent-root/src/agent/utils/__tests__/index.test.ts +18 -0
  165. package/vendor/agent-root/src/agent/utils/__tests__/message-utils.test.ts +610 -0
  166. package/vendor/agent-root/src/agent/utils/__tests__/message.test.ts +223 -0
  167. package/vendor/agent-root/src/agent/utils/__tests__/token.test.ts +42 -0
  168. package/vendor/agent-root/src/agent/utils/index.ts +16 -0
  169. package/vendor/agent-root/src/agent/utils/message.ts +171 -0
  170. package/vendor/agent-root/src/agent/utils/token.ts +28 -0
  171. package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +129 -0
  172. package/vendor/agent-root/src/config/__tests__/loader.test.ts +247 -0
  173. package/vendor/agent-root/src/config/__tests__/runtime.test.ts +88 -0
  174. package/vendor/agent-root/src/config/index.ts +54 -0
  175. package/vendor/agent-root/src/config/loader.ts +431 -0
  176. package/vendor/agent-root/src/config/paths.ts +30 -0
  177. package/vendor/agent-root/src/config/runtime.ts +163 -0
  178. package/vendor/agent-root/src/config/types.ts +70 -0
  179. package/vendor/agent-root/src/logger/index.ts +57 -0
  180. package/vendor/agent-root/src/logger/logger.ts +819 -0
  181. package/vendor/agent-root/src/logger/types.ts +150 -0
  182. package/vendor/agent-root/src/providers/__tests__/errors.test.ts +441 -0
  183. package/vendor/agent-root/src/providers/__tests__/index.test.ts +16 -0
  184. package/vendor/agent-root/src/providers/__tests__/openai-compatible.options.test.ts +318 -0
  185. package/vendor/agent-root/src/providers/__tests__/openai-compatible.test.ts +600 -0
  186. package/vendor/agent-root/src/providers/__tests__/registry.test.ts +449 -0
  187. package/vendor/agent-root/src/providers/__tests__/responses-adapter.test.ts +298 -0
  188. package/vendor/agent-root/src/providers/adapters/__tests__/anthropic.test.ts +354 -0
  189. package/vendor/agent-root/src/providers/adapters/__tests__/kimi.test.ts +58 -0
  190. package/vendor/agent-root/src/providers/adapters/__tests__/standard.test.ts +261 -0
  191. package/vendor/agent-root/src/providers/adapters/anthropic.ts +572 -0
  192. package/vendor/agent-root/src/providers/adapters/base.ts +131 -0
  193. package/vendor/agent-root/src/providers/adapters/kimi.ts +48 -0
  194. package/vendor/agent-root/src/providers/adapters/responses.ts +732 -0
  195. package/vendor/agent-root/src/providers/adapters/standard.ts +120 -0
  196. package/vendor/agent-root/src/providers/http/__tests__/client.timeout.test.ts +313 -0
  197. package/vendor/agent-root/src/providers/http/client.ts +289 -0
  198. package/vendor/agent-root/src/providers/http/stream-parser.ts +109 -0
  199. package/vendor/agent-root/src/providers/index.ts +76 -0
  200. package/vendor/agent-root/src/providers/kimi-headers.ts +177 -0
  201. package/vendor/agent-root/src/providers/openai-compatible.ts +387 -0
  202. package/vendor/agent-root/src/providers/registry/model-config.ts +230 -0
  203. package/vendor/agent-root/src/providers/registry/provider-factory.ts +123 -0
  204. package/vendor/agent-root/src/providers/registry.ts +135 -0
  205. package/vendor/agent-root/src/providers/types/api.ts +284 -0
  206. package/vendor/agent-root/src/providers/types/config.ts +58 -0
  207. package/vendor/agent-root/src/providers/types/errors.ts +323 -0
  208. package/vendor/agent-root/src/providers/types/index.ts +72 -0
  209. package/vendor/agent-root/src/providers/types/provider.ts +45 -0
  210. package/vendor/agent-root/src/providers/types/registry.ts +88 -0
@@ -0,0 +1,610 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ contentToText,
4
+ stringifyContentPart,
5
+ getAssistantToolCalls,
6
+ getToolCallId,
7
+ isSummaryMessage,
8
+ splitMessages,
9
+ processToolCallPairs,
10
+ rebuildMessages,
11
+ } from '../message';
12
+ import type { Message } from '../../types';
13
+
14
+ const createMessage = (overrides: Partial<Message> = {}): Message => ({
15
+ messageId: 'msg_1',
16
+ type: 'user',
17
+ role: 'user',
18
+ content: '',
19
+ timestamp: Date.now(),
20
+ ...overrides,
21
+ });
22
+
23
+ describe('contentToText', () => {
24
+ it('returns empty string for undefined content', () => {
25
+ expect(contentToText(undefined)).toBe('');
26
+ });
27
+
28
+ it('returns string content as-is', () => {
29
+ expect(contentToText('Hello')).toBe('Hello');
30
+ });
31
+
32
+ it('returns empty string for empty string', () => {
33
+ expect(contentToText('')).toBe('');
34
+ });
35
+
36
+ it('converts array of content parts', () => {
37
+ const content = [
38
+ { type: 'text' as const, text: 'Hello' },
39
+ { type: 'text' as const, text: 'World' },
40
+ ];
41
+ expect(contentToText(content)).toBe('Hello\nWorld');
42
+ });
43
+
44
+ it('filters out empty parts', () => {
45
+ const content = [
46
+ { type: 'text' as const, text: 'Hello' },
47
+ { type: 'text' as const, text: '' },
48
+ { type: 'text' as const, text: 'World' },
49
+ ];
50
+ expect(contentToText(content)).toBe('Hello\nWorld');
51
+ });
52
+
53
+ it('handles mixed content types', () => {
54
+ const content = [
55
+ { type: 'text' as const, text: 'Text' },
56
+ { type: 'image_url' as const, image_url: { url: 'http://example.com/image.png' } },
57
+ ];
58
+ expect(contentToText(content)).toBe('Text\n[image] http://example.com/image.png');
59
+ });
60
+
61
+ it('returns empty string for non-string, non-array content', () => {
62
+ expect(contentToText(123 as any)).toBe('');
63
+ expect(contentToText({} as any)).toBe('');
64
+ expect(contentToText(null as any)).toBe('');
65
+ });
66
+
67
+ it('handles empty array', () => {
68
+ expect(contentToText([])).toBe('');
69
+ });
70
+ });
71
+
72
+ describe('stringifyContentPart', () => {
73
+ it('stringifies text part', () => {
74
+ expect(stringifyContentPart({ type: 'text', text: 'Hello' })).toBe('Hello');
75
+ });
76
+
77
+ it('handles text part with empty text', () => {
78
+ expect(stringifyContentPart({ type: 'text', text: '' })).toBe('');
79
+ });
80
+
81
+ it('handles text part with undefined text', () => {
82
+ expect(stringifyContentPart({ type: 'text', text: undefined as any })).toBe('');
83
+ });
84
+
85
+ it('stringifies image_url part', () => {
86
+ expect(
87
+ stringifyContentPart({
88
+ type: 'image_url',
89
+ image_url: { url: 'http://example.com/image.png' },
90
+ })
91
+ ).toBe('[image] http://example.com/image.png');
92
+ });
93
+
94
+ it('handles image_url part with empty url', () => {
95
+ expect(
96
+ stringifyContentPart({
97
+ type: 'image_url',
98
+ image_url: { url: '' },
99
+ })
100
+ ).toBe('[image]');
101
+ });
102
+
103
+ it('handles image_url part with undefined url', () => {
104
+ expect(
105
+ stringifyContentPart({
106
+ type: 'image_url',
107
+ image_url: { url: undefined as any },
108
+ })
109
+ ).toBe('[image]');
110
+ });
111
+
112
+ it('handles image_url part with undefined image_url', () => {
113
+ expect(
114
+ stringifyContentPart({
115
+ type: 'image_url',
116
+ image_url: undefined as any,
117
+ })
118
+ ).toBe('[image]');
119
+ });
120
+
121
+ it('stringifies file part with filename', () => {
122
+ expect(
123
+ stringifyContentPart({
124
+ type: 'file',
125
+ file: { filename: 'document.pdf' },
126
+ })
127
+ ).toBe('[file] document.pdf');
128
+ });
129
+
130
+ it('stringifies file part with file_id', () => {
131
+ expect(
132
+ stringifyContentPart({
133
+ type: 'file',
134
+ file: { file_id: 'file_123' },
135
+ })
136
+ ).toBe('[file] file_123');
137
+ });
138
+
139
+ it('prefers filename over file_id', () => {
140
+ expect(
141
+ stringifyContentPart({
142
+ type: 'file',
143
+ file: { filename: 'document.pdf', file_id: 'file_123' },
144
+ })
145
+ ).toBe('[file] document.pdf');
146
+ });
147
+
148
+ it('handles file part with empty file', () => {
149
+ expect(
150
+ stringifyContentPart({
151
+ type: 'file',
152
+ file: {} as any,
153
+ })
154
+ ).toBe('[file]');
155
+ });
156
+
157
+ it('handles file part with undefined file', () => {
158
+ expect(
159
+ stringifyContentPart({
160
+ type: 'file',
161
+ file: undefined as any,
162
+ })
163
+ ).toBe('[file]');
164
+ });
165
+
166
+ it('stringifies input_audio part', () => {
167
+ expect(
168
+ stringifyContentPart({ type: 'input_audio', input_audio: { data: 'test', format: 'wav' } })
169
+ ).toBe('[audio]');
170
+ });
171
+
172
+ it('stringifies input_video part with url', () => {
173
+ expect(
174
+ stringifyContentPart({
175
+ type: 'input_video',
176
+ input_video: { url: 'http://example.com/video.mp4' },
177
+ })
178
+ ).toBe('[video] http://example.com/video.mp4');
179
+ });
180
+
181
+ it('stringifies input_video part with file_id', () => {
182
+ expect(
183
+ stringifyContentPart({
184
+ type: 'input_video',
185
+ input_video: { file_id: 'video_123' },
186
+ })
187
+ ).toBe('[video] video_123');
188
+ });
189
+
190
+ it('prefers url over file_id for video', () => {
191
+ expect(
192
+ stringifyContentPart({
193
+ type: 'input_video',
194
+ input_video: { url: 'http://example.com/video.mp4', file_id: 'video_123' },
195
+ })
196
+ ).toBe('[video] http://example.com/video.mp4');
197
+ });
198
+
199
+ it('handles input_video part with empty input_video', () => {
200
+ expect(
201
+ stringifyContentPart({
202
+ type: 'input_video',
203
+ input_video: {} as any,
204
+ })
205
+ ).toBe('[video]');
206
+ });
207
+
208
+ it('handles input_video part with undefined input_video', () => {
209
+ expect(
210
+ stringifyContentPart({
211
+ type: 'input_video',
212
+ input_video: undefined as any,
213
+ })
214
+ ).toBe('[video]');
215
+ });
216
+
217
+ it('returns empty string for unknown type', () => {
218
+ expect(stringifyContentPart({ type: 'unknown' } as any)).toBe('');
219
+ });
220
+ });
221
+
222
+ describe('getAssistantToolCalls', () => {
223
+ it('returns empty array for non-assistant messages', () => {
224
+ expect(getAssistantToolCalls(createMessage({ role: 'user', content: 'Hello' }))).toEqual([]);
225
+ expect(getAssistantToolCalls(createMessage({ role: 'system', content: 'System' }))).toEqual([]);
226
+ expect(
227
+ getAssistantToolCalls(
228
+ createMessage({ role: 'tool', content: 'Result', tool_call_id: 'call_1' })
229
+ )
230
+ ).toEqual([]);
231
+ });
232
+
233
+ it('returns empty array for assistant message without tool_calls', () => {
234
+ expect(getAssistantToolCalls(createMessage({ role: 'assistant', content: 'Hello' }))).toEqual(
235
+ []
236
+ );
237
+ });
238
+
239
+ it('returns empty array for assistant message with null tool_calls', () => {
240
+ expect(
241
+ getAssistantToolCalls(
242
+ createMessage({ role: 'assistant', content: '', tool_calls: null as any })
243
+ )
244
+ ).toEqual([]);
245
+ });
246
+
247
+ it('returns empty array for assistant message with non-array tool_calls', () => {
248
+ expect(
249
+ getAssistantToolCalls(
250
+ createMessage({ role: 'assistant', content: '', tool_calls: 'invalid' as any })
251
+ )
252
+ ).toEqual([]);
253
+ });
254
+
255
+ it('returns empty array for assistant message with empty tool_calls', () => {
256
+ expect(
257
+ getAssistantToolCalls(createMessage({ role: 'assistant', content: '', tool_calls: [] }))
258
+ ).toEqual([]);
259
+ });
260
+
261
+ it('returns tool calls for assistant message', () => {
262
+ const message = createMessage({
263
+ role: 'assistant',
264
+ content: '',
265
+ tool_calls: [
266
+ { id: 'call_1', type: 'function', index: 0, function: { name: 'test', arguments: '{}' } },
267
+ { id: 'call_2', type: 'function', index: 1, function: { name: 'test2', arguments: '{}' } },
268
+ ],
269
+ });
270
+
271
+ const result = getAssistantToolCalls(message);
272
+ expect(result).toEqual([{ id: 'call_1' }, { id: 'call_2' }]);
273
+ });
274
+
275
+ it('handles tool calls without id', () => {
276
+ const message = createMessage({
277
+ role: 'assistant',
278
+ content: '',
279
+ tool_calls: [
280
+ {
281
+ id: undefined as any,
282
+ type: 'function',
283
+ index: 0,
284
+ function: { name: 'test', arguments: '{}' },
285
+ },
286
+ ],
287
+ });
288
+
289
+ const result = getAssistantToolCalls(message);
290
+ expect(result).toEqual([{ id: undefined }]);
291
+ });
292
+ });
293
+
294
+ describe('getToolCallId', () => {
295
+ it('returns undefined for non-tool messages', () => {
296
+ expect(getToolCallId(createMessage({ role: 'user', content: 'Hello' }))).toBeUndefined();
297
+ expect(getToolCallId(createMessage({ role: 'assistant', content: 'Hello' }))).toBeUndefined();
298
+ expect(getToolCallId(createMessage({ role: 'system', content: 'System' }))).toBeUndefined();
299
+ });
300
+
301
+ it('returns tool_call_id for tool message', () => {
302
+ expect(
303
+ getToolCallId(createMessage({ role: 'tool', content: 'Result', tool_call_id: 'call_1' }))
304
+ ).toBe('call_1');
305
+ });
306
+
307
+ it('returns undefined for tool message with non-string tool_call_id', () => {
308
+ expect(
309
+ getToolCallId(createMessage({ role: 'tool', content: 'Result', tool_call_id: 123 as any }))
310
+ ).toBeUndefined();
311
+ });
312
+
313
+ it('returns undefined for tool message with undefined tool_call_id', () => {
314
+ expect(
315
+ getToolCallId(createMessage({ role: 'tool', content: 'Result', tool_call_id: undefined }))
316
+ ).toBeUndefined();
317
+ });
318
+
319
+ it('returns undefined for tool message with null tool_call_id', () => {
320
+ expect(
321
+ getToolCallId(createMessage({ role: 'tool', content: 'Result', tool_call_id: null as any }))
322
+ ).toBeUndefined();
323
+ });
324
+ });
325
+
326
+ describe('isSummaryMessage', () => {
327
+ it('returns true for message starting with [Conversation Summary]', () => {
328
+ expect(
329
+ isSummaryMessage(
330
+ createMessage({ role: 'assistant', content: '[Conversation Summary] This is a summary' })
331
+ )
332
+ ).toBe(true);
333
+ });
334
+
335
+ it('returns true for message starting with [对话摘要]', () => {
336
+ expect(
337
+ isSummaryMessage(createMessage({ role: 'assistant', content: '[对话摘要] 这是一个摘要' }))
338
+ ).toBe(true);
339
+ });
340
+
341
+ it('returns false for regular message', () => {
342
+ expect(isSummaryMessage(createMessage({ role: 'assistant', content: 'Hello' }))).toBe(false);
343
+ });
344
+
345
+ it('returns false for empty message', () => {
346
+ expect(isSummaryMessage(createMessage({ role: 'assistant', content: '' }))).toBe(false);
347
+ });
348
+
349
+ it('returns false for message with summary text in the middle', () => {
350
+ expect(
351
+ isSummaryMessage(
352
+ createMessage({ role: 'assistant', content: 'This is [Conversation Summary] text' })
353
+ )
354
+ ).toBe(false);
355
+ });
356
+
357
+ it('handles array content', () => {
358
+ expect(
359
+ isSummaryMessage(
360
+ createMessage({
361
+ role: 'assistant',
362
+ content: [{ type: 'text', text: '[Conversation Summary] Summary' }],
363
+ })
364
+ )
365
+ ).toBe(true);
366
+ });
367
+
368
+ it('handles undefined content', () => {
369
+ expect(isSummaryMessage(createMessage({ role: 'assistant', content: undefined as any }))).toBe(
370
+ false
371
+ );
372
+ });
373
+ });
374
+
375
+ describe('splitMessages', () => {
376
+ it('splits messages correctly', () => {
377
+ const messages = [
378
+ createMessage({ role: 'system', content: 'System' }),
379
+ createMessage({ role: 'user', content: 'User 1' }),
380
+ createMessage({ role: 'assistant', content: 'Assistant 1' }),
381
+ createMessage({ role: 'user', content: 'User 2' }),
382
+ createMessage({ role: 'assistant', content: 'Assistant 2' }),
383
+ ];
384
+
385
+ const result = splitMessages(messages, 2);
386
+
387
+ expect(result.systemMessage).toEqual(createMessage({ role: 'system', content: 'System' }));
388
+ expect(result.pending).toHaveLength(2);
389
+ expect(result.active).toHaveLength(2);
390
+ });
391
+
392
+ it('handles messages without system message', () => {
393
+ const messages = [
394
+ createMessage({ role: 'user', content: 'User 1' }),
395
+ createMessage({ role: 'assistant', content: 'Assistant 1' }),
396
+ createMessage({ role: 'user', content: 'User 2' }),
397
+ ];
398
+
399
+ const result = splitMessages(messages, 2);
400
+
401
+ expect(result.systemMessage).toBeUndefined();
402
+ expect(result.pending).toHaveLength(1);
403
+ expect(result.active).toHaveLength(2);
404
+ });
405
+
406
+ it('handles empty messages', () => {
407
+ const result = splitMessages([], 5);
408
+
409
+ expect(result.systemMessage).toBeUndefined();
410
+ expect(result.pending).toEqual([]);
411
+ expect(result.active).toEqual([]);
412
+ });
413
+
414
+ it('handles keepMessagesNum larger than messages', () => {
415
+ const messages = [
416
+ createMessage({ role: 'user', content: 'User 1' }),
417
+ createMessage({ role: 'assistant', content: 'Assistant 1' }),
418
+ ];
419
+
420
+ const result = splitMessages(messages, 10);
421
+
422
+ expect(result.pending).toEqual([]);
423
+ expect(result.active).toHaveLength(2);
424
+ });
425
+
426
+ it('handles keepMessagesNum of 0', () => {
427
+ const messages = [
428
+ createMessage({ role: 'user', content: 'User 1' }),
429
+ createMessage({ role: 'assistant', content: 'Assistant 1' }),
430
+ ];
431
+
432
+ const result = splitMessages(messages, 0);
433
+
434
+ expect(result.pending).toEqual([]);
435
+ expect(result.active).toHaveLength(2);
436
+ });
437
+
438
+ it('adjusts split point to not split user message pairs', () => {
439
+ const messages = [
440
+ createMessage({ role: 'user', content: 'User 1' }),
441
+ createMessage({ role: 'assistant', content: 'Assistant 1' }),
442
+ createMessage({ role: 'user', content: 'User 2' }),
443
+ createMessage({ role: 'assistant', content: 'Assistant 2' }),
444
+ ];
445
+
446
+ const result = splitMessages(messages, 1);
447
+
448
+ // Should keep the last user-assistant pair together
449
+ expect(result.active).toHaveLength(2);
450
+ expect(result.active[0].content).toBe('User 2');
451
+ expect(result.active[1].content).toBe('Assistant 2');
452
+ });
453
+ });
454
+
455
+ describe('processToolCallPairs', () => {
456
+ const createAssistantMessage = (_id: string, toolCallIds: string[]): Message =>
457
+ createMessage({
458
+ role: 'assistant',
459
+ content: '',
460
+ tool_calls: toolCallIds.map((callId, index) => ({
461
+ id: callId,
462
+ type: 'function',
463
+ index,
464
+ function: { name: 'test', arguments: '{}' },
465
+ })),
466
+ });
467
+
468
+ const createToolMessage = (toolCallId: string): Message =>
469
+ createMessage({
470
+ role: 'tool',
471
+ content: 'Result',
472
+ tool_call_id: toolCallId,
473
+ });
474
+
475
+ it('returns unchanged when no tool calls need pairing', () => {
476
+ const pending: Message[] = [];
477
+ const active: Message[] = [
478
+ createMessage({ role: 'user', content: 'Hello' }),
479
+ createMessage({ role: 'assistant', content: 'Hi' }),
480
+ ];
481
+
482
+ const result = processToolCallPairs(pending, active);
483
+
484
+ expect(result.pending).toEqual(pending);
485
+ expect(result.active).toEqual(active);
486
+ });
487
+
488
+ it('moves assistant and tool messages together', () => {
489
+ const assistant = createAssistantMessage('msg_1', ['call_1']);
490
+ const tool = createToolMessage('call_1');
491
+
492
+ const pending: Message[] = [assistant];
493
+ const active: Message[] = [createMessage({ role: 'user', content: 'Hello' }), tool];
494
+
495
+ const result = processToolCallPairs(pending, active);
496
+
497
+ expect(result.pending).toHaveLength(0);
498
+ expect(result.active).toContain(assistant);
499
+ expect(result.active).toContain(tool);
500
+ });
501
+
502
+ it('handles multiple tool calls', () => {
503
+ const assistant = createAssistantMessage('msg_1', ['call_1', 'call_2']);
504
+ const tool1 = createToolMessage('call_1');
505
+ const tool2 = createToolMessage('call_2');
506
+
507
+ const pending: Message[] = [assistant];
508
+ const active: Message[] = [tool1, tool2];
509
+
510
+ const result = processToolCallPairs(pending, active);
511
+
512
+ expect(result.pending).toHaveLength(0);
513
+ expect(result.active).toContain(assistant);
514
+ expect(result.active).toContain(tool1);
515
+ expect(result.active).toContain(tool2);
516
+ });
517
+
518
+ it('handles tool message without matching assistant', () => {
519
+ const pending: Message[] = [];
520
+ const active: Message[] = [createToolMessage('call_1')];
521
+
522
+ const result = processToolCallPairs(pending, active);
523
+
524
+ expect(result.pending).toEqual([]);
525
+ expect(result.active).toEqual(active);
526
+ });
527
+
528
+ it('handles empty arrays', () => {
529
+ const result = processToolCallPairs([], []);
530
+
531
+ expect(result.pending).toEqual([]);
532
+ expect(result.active).toEqual([]);
533
+ });
534
+ });
535
+
536
+ describe('rebuildMessages', () => {
537
+ it('rebuilds messages with all components', () => {
538
+ const systemMessage = createMessage({ role: 'system', content: 'System' });
539
+ const summaryMessage = createMessage({
540
+ role: 'assistant',
541
+ content: '[Conversation Summary] Summary',
542
+ });
543
+ const active: Message[] = [
544
+ createMessage({ role: 'user', content: 'Hello' }),
545
+ createMessage({ role: 'assistant', content: 'Hi' }),
546
+ ];
547
+
548
+ const result = rebuildMessages(systemMessage, summaryMessage, active);
549
+
550
+ expect(result).toHaveLength(4);
551
+ expect(result[0]).toBe(systemMessage);
552
+ expect(result[1]).toBe(summaryMessage);
553
+ expect(result[2]).toBe(active[0]);
554
+ expect(result[3]).toBe(active[1]);
555
+ });
556
+
557
+ it('rebuilds messages without system message', () => {
558
+ const summaryMessage = createMessage({
559
+ role: 'assistant',
560
+ content: '[Conversation Summary] Summary',
561
+ });
562
+ const active: Message[] = [createMessage({ role: 'user', content: 'Hello' })];
563
+
564
+ const result = rebuildMessages(undefined, summaryMessage, active);
565
+
566
+ expect(result).toHaveLength(2);
567
+ expect(result[0]).toBe(summaryMessage);
568
+ expect(result[1]).toBe(active[0]);
569
+ });
570
+
571
+ it('rebuilds messages without summary message', () => {
572
+ const systemMessage = createMessage({ role: 'system', content: 'System' });
573
+ const active: Message[] = [createMessage({ role: 'user', content: 'Hello' })];
574
+
575
+ const result = rebuildMessages(systemMessage, null, active);
576
+
577
+ expect(result).toHaveLength(2);
578
+ expect(result[0]).toBe(systemMessage);
579
+ expect(result[1]).toBe(active[0]);
580
+ });
581
+
582
+ it('rebuilds messages with only active messages', () => {
583
+ const active: Message[] = [createMessage({ role: 'user', content: 'Hello' })];
584
+
585
+ const result = rebuildMessages(undefined, null, active);
586
+
587
+ expect(result).toHaveLength(1);
588
+ expect(result[0]).toBe(active[0]);
589
+ });
590
+
591
+ it('handles empty active messages', () => {
592
+ const systemMessage = createMessage({ role: 'system', content: 'System' });
593
+ const summaryMessage = createMessage({
594
+ role: 'assistant',
595
+ content: '[Conversation Summary] Summary',
596
+ });
597
+
598
+ const result = rebuildMessages(systemMessage, summaryMessage, []);
599
+
600
+ expect(result).toHaveLength(2);
601
+ expect(result[0]).toBe(systemMessage);
602
+ expect(result[1]).toBe(summaryMessage);
603
+ });
604
+
605
+ it('handles all undefined/null', () => {
606
+ const result = rebuildMessages(undefined, null, []);
607
+
608
+ expect(result).toEqual([]);
609
+ });
610
+ });