bingocode 1.0.27 → 1.0.29

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 (54) hide show
  1. package/package.json +1 -2
  2. package/.github/FUNDING.yml +0 -1
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -44
  4. package/.github/ISSUE_TEMPLATE/config.yml +0 -1
  5. package/.github/ISSUE_TEMPLATE/question.md +0 -40
  6. package/.github/workflows/build-desktop-dev.yml +0 -210
  7. package/.github/workflows/deploy-docs.yml +0 -59
  8. package/.github/workflows/release-desktop.yml +0 -162
  9. package/.spine/user.yaml +0 -5
  10. package/.spine/workspace.yaml +0 -1
  11. package/adapters/common/__tests__/chat-queue.test.ts +0 -61
  12. package/adapters/common/__tests__/format.test.ts +0 -148
  13. package/adapters/common/__tests__/http-client.test.ts +0 -105
  14. package/adapters/common/__tests__/message-buffer.test.ts +0 -84
  15. package/adapters/common/__tests__/message-dedup.test.ts +0 -57
  16. package/adapters/common/__tests__/session-store.test.ts +0 -62
  17. package/adapters/common/__tests__/ws-bridge.test.ts +0 -177
  18. package/adapters/common/attachment/__tests__/attachment-limits.test.ts +0 -52
  19. package/adapters/common/attachment/__tests__/attachment-store.test.ts +0 -108
  20. package/adapters/common/attachment/__tests__/image-block-watcher.test.ts +0 -115
  21. package/adapters/feishu/__tests__/card-errors.test.ts +0 -194
  22. package/adapters/feishu/__tests__/cardkit.test.ts +0 -295
  23. package/adapters/feishu/__tests__/extract-payload.test.ts +0 -77
  24. package/adapters/feishu/__tests__/feishu.test.ts +0 -907
  25. package/adapters/feishu/__tests__/flush-controller.test.ts +0 -290
  26. package/adapters/feishu/__tests__/markdown-style.test.ts +0 -353
  27. package/adapters/feishu/__tests__/media.test.ts +0 -120
  28. package/adapters/feishu/__tests__/streaming-card.test.ts +0 -914
  29. package/adapters/telegram/__tests__/media.test.ts +0 -86
  30. package/adapters/telegram/__tests__/telegram.test.ts +0 -115
  31. package/src/server/__tests__/conversation-service.test.ts +0 -173
  32. package/src/server/__tests__/conversations.test.ts +0 -458
  33. package/src/server/__tests__/cron-scheduler.test.ts +0 -575
  34. package/src/server/__tests__/e2e/business-flow.test.ts +0 -841
  35. package/src/server/__tests__/e2e/full-flow.test.ts +0 -357
  36. package/src/server/__tests__/fixtures/mock-sdk-cli.ts +0 -123
  37. package/src/server/__tests__/haha-oauth-api.test.ts +0 -146
  38. package/src/server/__tests__/haha-oauth-service.test.ts +0 -185
  39. package/src/server/__tests__/providers-real.test.ts +0 -244
  40. package/src/server/__tests__/providers.test.ts +0 -579
  41. package/src/server/__tests__/proxy-streaming.test.ts +0 -317
  42. package/src/server/__tests__/proxy-transform.test.ts +0 -469
  43. package/src/server/__tests__/real-llm-test.ts +0 -526
  44. package/src/server/__tests__/scheduled-tasks.test.ts +0 -371
  45. package/src/server/__tests__/sessions.test.ts +0 -786
  46. package/src/server/__tests__/settings.test.ts +0 -376
  47. package/src/server/__tests__/skills.test.ts +0 -125
  48. package/src/server/__tests__/tasks.test.ts +0 -171
  49. package/src/server/__tests__/team-watcher.test.ts +0 -400
  50. package/src/server/__tests__/teams.test.ts +0 -627
  51. package/src/server/middleware/cors.test.ts +0 -27
  52. package/src/utils/__tests__/cronFrequency.test.ts +0 -153
  53. package/src/utils/__tests__/cronTasks.test.ts +0 -204
  54. package/src/utils/computerUse/permissions.test.ts +0 -44
@@ -1,469 +0,0 @@
1
- /**
2
- * Unit tests for proxy protocol transformation
3
- */
4
-
5
- import { describe, test, expect } from 'bun:test'
6
- import { anthropicToOpenaiChat } from '../proxy/transform/anthropicToOpenaiChat.js'
7
- import { anthropicToOpenaiResponses } from '../proxy/transform/anthropicToOpenaiResponses.js'
8
- import { openaiChatToAnthropic } from '../proxy/transform/openaiChatToAnthropic.js'
9
- import { openaiResponsesToAnthropic } from '../proxy/transform/openaiResponsesToAnthropic.js'
10
- import type { AnthropicRequest, OpenAIChatResponse, OpenAIResponsesResponse } from '../proxy/transform/types.js'
11
-
12
- // ─── anthropicToOpenaiChat ──────────────────────────────────────
13
-
14
- describe('anthropicToOpenaiChat', () => {
15
- test('basic text message', () => {
16
- const req: AnthropicRequest = {
17
- model: 'gpt-4',
18
- max_tokens: 1024,
19
- messages: [{ role: 'user', content: 'Hello' }],
20
- }
21
- const result = anthropicToOpenaiChat(req)
22
- expect(result.model).toBe('gpt-4')
23
- expect(result.max_tokens).toBeUndefined()
24
- expect(result.messages).toEqual([{ role: 'user', content: 'Hello' }])
25
- })
26
-
27
- test('system prompt string', () => {
28
- const req: AnthropicRequest = {
29
- model: 'gpt-4',
30
- max_tokens: 100,
31
- system: 'You are helpful',
32
- messages: [{ role: 'user', content: 'Hi' }],
33
- }
34
- const result = anthropicToOpenaiChat(req)
35
- expect(result.messages[0]).toEqual({ role: 'system', content: 'You are helpful' })
36
- expect(result.messages[1]).toEqual({ role: 'user', content: 'Hi' })
37
- })
38
-
39
- test('system prompt array', () => {
40
- const req: AnthropicRequest = {
41
- model: 'gpt-4',
42
- max_tokens: 100,
43
- system: [{ type: 'text', text: 'Part 1' }, { type: 'text', text: 'Part 2' }],
44
- messages: [{ role: 'user', content: 'Hi' }],
45
- }
46
- const result = anthropicToOpenaiChat(req)
47
- expect(result.messages[0]).toEqual({ role: 'system', content: 'Part 1\nPart 2' })
48
- })
49
-
50
- test('stop_sequences → stop', () => {
51
- const req: AnthropicRequest = {
52
- model: 'gpt-4',
53
- max_tokens: 100,
54
- stop_sequences: ['END', 'STOP'],
55
- messages: [{ role: 'user', content: 'Hi' }],
56
- }
57
- const result = anthropicToOpenaiChat(req)
58
- expect(result.stop).toEqual(['END', 'STOP'])
59
- })
60
-
61
- test('tools conversion', () => {
62
- const req: AnthropicRequest = {
63
- model: 'gpt-4',
64
- max_tokens: 100,
65
- messages: [{ role: 'user', content: 'Hi' }],
66
- tools: [{
67
- name: 'get_weather',
68
- description: 'Get weather',
69
- input_schema: { type: 'object', properties: { city: { type: 'string' } } },
70
- }],
71
- }
72
- const result = anthropicToOpenaiChat(req)
73
- expect(result.tools).toHaveLength(1)
74
- expect(result.tools![0].type).toBe('function')
75
- expect(result.tools![0].function.name).toBe('get_weather')
76
- expect(result.tools![0].function.parameters).toEqual({ type: 'object', properties: { city: { type: 'string' } } })
77
- })
78
-
79
- test('filters BatchTool', () => {
80
- const req: AnthropicRequest = {
81
- model: 'gpt-4',
82
- max_tokens: 100,
83
- messages: [{ role: 'user', content: 'Hi' }],
84
- tools: [
85
- { name: 'BatchTool', input_schema: {} },
86
- { name: 'real_tool', input_schema: {} },
87
- ],
88
- }
89
- const result = anthropicToOpenaiChat(req)
90
- expect(result.tools).toHaveLength(1)
91
- expect(result.tools![0].function.name).toBe('real_tool')
92
- })
93
-
94
- test('tool_choice conversion', () => {
95
- const req: AnthropicRequest = {
96
- model: 'gpt-4',
97
- max_tokens: 100,
98
- messages: [{ role: 'user', content: 'Hi' }],
99
- tool_choice: { type: 'any' },
100
- }
101
- const result = anthropicToOpenaiChat(req)
102
- expect(result.tool_choice).toBe('required')
103
- })
104
-
105
- test('tool_choice type=tool', () => {
106
- const req: AnthropicRequest = {
107
- model: 'gpt-4',
108
- max_tokens: 100,
109
- messages: [{ role: 'user', content: 'Hi' }],
110
- tool_choice: { type: 'tool', name: 'get_weather' },
111
- }
112
- const result = anthropicToOpenaiChat(req)
113
- expect(result.tool_choice).toEqual({ type: 'function', function: { name: 'get_weather' } })
114
- })
115
-
116
- test('thinking budget → reasoning_effort', () => {
117
- const lowReq: AnthropicRequest = {
118
- model: 'gpt-4',
119
- max_tokens: 100,
120
- messages: [{ role: 'user', content: 'Hi' }],
121
- thinking: { type: 'enabled', budget_tokens: 512 },
122
- }
123
- expect(anthropicToOpenaiChat(lowReq).reasoning_effort).toBe('low')
124
-
125
- const medReq: AnthropicRequest = {
126
- model: 'gpt-4',
127
- max_tokens: 100,
128
- messages: [{ role: 'user', content: 'Hi' }],
129
- thinking: { type: 'enabled', budget_tokens: 4096 },
130
- }
131
- expect(anthropicToOpenaiChat(medReq).reasoning_effort).toBe('medium')
132
-
133
- const highReq: AnthropicRequest = {
134
- model: 'gpt-4',
135
- max_tokens: 100,
136
- messages: [{ role: 'user', content: 'Hi' }],
137
- thinking: { type: 'enabled', budget_tokens: 16000 },
138
- }
139
- expect(anthropicToOpenaiChat(highReq).reasoning_effort).toBe('high')
140
- })
141
-
142
- test('assistant message with tool_use', () => {
143
- const req: AnthropicRequest = {
144
- model: 'gpt-4',
145
- max_tokens: 100,
146
- messages: [{
147
- role: 'assistant',
148
- content: [
149
- { type: 'text', text: 'Let me check' },
150
- { type: 'tool_use', id: 'tc_1', name: 'get_weather', input: { city: 'NYC' } },
151
- ],
152
- }],
153
- }
154
- const result = anthropicToOpenaiChat(req)
155
- const msg = result.messages[0]
156
- expect(msg.role).toBe('assistant')
157
- expect(msg.content).toBe('Let me check')
158
- expect(msg.tool_calls).toHaveLength(1)
159
- expect(msg.tool_calls![0].id).toBe('tc_1')
160
- expect(msg.tool_calls![0].function.name).toBe('get_weather')
161
- expect(msg.tool_calls![0].function.arguments).toBe('{"city":"NYC"}')
162
- })
163
-
164
- test('user message with tool_result', () => {
165
- const req: AnthropicRequest = {
166
- model: 'gpt-4',
167
- max_tokens: 100,
168
- messages: [{
169
- role: 'user',
170
- content: [
171
- { type: 'tool_result', tool_use_id: 'tc_1', content: 'Sunny, 72°F' },
172
- ],
173
- }],
174
- }
175
- const result = anthropicToOpenaiChat(req)
176
- expect(result.messages[0].role).toBe('tool')
177
- expect(result.messages[0].tool_call_id).toBe('tc_1')
178
- expect(result.messages[0].content).toBe('Sunny, 72°F')
179
- })
180
-
181
- test('image content conversion', () => {
182
- const req: AnthropicRequest = {
183
- model: 'gpt-4',
184
- max_tokens: 100,
185
- messages: [{
186
- role: 'user',
187
- content: [
188
- { type: 'image', source: { type: 'base64', media_type: 'image/png', data: 'abc123' } },
189
- ],
190
- }],
191
- }
192
- const result = anthropicToOpenaiChat(req)
193
- const content = result.messages[0].content as Array<{ type: string; image_url?: { url: string } }>
194
- expect(content[0].type).toBe('image_url')
195
- expect(content[0].image_url!.url).toBe('data:image/png;base64,abc123')
196
- })
197
- })
198
-
199
- // ─── openaiChatToAnthropic ──────────────────────────────────────
200
-
201
- describe('openaiChatToAnthropic', () => {
202
- test('basic text response', () => {
203
- const res: OpenAIChatResponse = {
204
- id: 'chatcmpl-1',
205
- object: 'chat.completion',
206
- created: 1234567890,
207
- model: 'gpt-4',
208
- choices: [{
209
- index: 0,
210
- message: { role: 'assistant', content: 'Hello!' },
211
- finish_reason: 'stop',
212
- }],
213
- usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
214
- }
215
- const result = openaiChatToAnthropic(res, 'gpt-4')
216
- expect(result.type).toBe('message')
217
- expect(result.role).toBe('assistant')
218
- expect(result.content).toEqual([{ type: 'text', text: 'Hello!' }])
219
- expect(result.stop_reason).toBe('end_turn')
220
- expect(result.usage.input_tokens).toBe(10)
221
- expect(result.usage.output_tokens).toBe(5)
222
- })
223
-
224
- test('tool_calls response', () => {
225
- const res: OpenAIChatResponse = {
226
- id: 'chatcmpl-2',
227
- object: 'chat.completion',
228
- created: 1234567890,
229
- model: 'gpt-4',
230
- choices: [{
231
- index: 0,
232
- message: {
233
- role: 'assistant',
234
- content: null,
235
- tool_calls: [{
236
- id: 'call_1',
237
- type: 'function',
238
- function: { name: 'get_weather', arguments: '{"city":"NYC"}' },
239
- }],
240
- },
241
- finish_reason: 'tool_calls',
242
- }],
243
- }
244
- const result = openaiChatToAnthropic(res, 'gpt-4')
245
- expect(result.stop_reason).toBe('tool_use')
246
- expect(result.content).toHaveLength(1)
247
- expect(result.content[0].type).toBe('tool_use')
248
- if (result.content[0].type === 'tool_use') {
249
- expect(result.content[0].id).toBe('call_1')
250
- expect(result.content[0].name).toBe('get_weather')
251
- expect(result.content[0].input).toEqual({ city: 'NYC' })
252
- }
253
- })
254
-
255
- test('finish_reason mapping', () => {
256
- const make = (reason: string) => ({
257
- id: 'x', object: 'chat.completion', created: 0, model: 'gpt-4',
258
- choices: [{ index: 0, message: { role: 'assistant', content: 'hi' }, finish_reason: reason }],
259
- } as OpenAIChatResponse)
260
-
261
- expect(openaiChatToAnthropic(make('stop'), 'gpt-4').stop_reason).toBe('end_turn')
262
- expect(openaiChatToAnthropic(make('length'), 'gpt-4').stop_reason).toBe('max_tokens')
263
- expect(openaiChatToAnthropic(make('tool_calls'), 'gpt-4').stop_reason).toBe('tool_use')
264
- expect(openaiChatToAnthropic(make('content_filter'), 'gpt-4').stop_reason).toBe('end_turn')
265
- })
266
-
267
- test('empty choices', () => {
268
- const res: OpenAIChatResponse = {
269
- id: 'x', object: 'chat.completion', created: 0, model: 'gpt-4',
270
- choices: [],
271
- }
272
- const result = openaiChatToAnthropic(res, 'gpt-4')
273
- expect(result.content).toEqual([{ type: 'text', text: '' }])
274
- expect(result.stop_reason).toBe('end_turn')
275
- })
276
-
277
- test('cached tokens mapping', () => {
278
- const res: OpenAIChatResponse = {
279
- id: 'x', object: 'chat.completion', created: 0, model: 'gpt-4',
280
- choices: [{ index: 0, message: { role: 'assistant', content: 'hi' }, finish_reason: 'stop' }],
281
- usage: {
282
- prompt_tokens: 100,
283
- completion_tokens: 50,
284
- total_tokens: 150,
285
- prompt_tokens_details: { cached_tokens: 80 },
286
- },
287
- }
288
- const result = openaiChatToAnthropic(res, 'gpt-4')
289
- expect(result.usage.cache_read_input_tokens).toBe(80)
290
- })
291
- })
292
-
293
- // ─── anthropicToOpenaiResponses ─────────────────────────────────
294
-
295
- describe('anthropicToOpenaiResponses', () => {
296
- test('basic message', () => {
297
- const req: AnthropicRequest = {
298
- model: 'gpt-4o',
299
- max_tokens: 1024,
300
- system: 'Be helpful',
301
- messages: [{ role: 'user', content: 'Hello' }],
302
- }
303
- const result = anthropicToOpenaiResponses(req)
304
- expect(result.model).toBe('gpt-4o')
305
- expect(result.instructions).toBe('Be helpful')
306
- expect(result.max_output_tokens).toBeUndefined()
307
- expect(result.input).toEqual([{ type: 'message', role: 'user', content: 'Hello' }])
308
- })
309
-
310
- test('tool_use lifted to function_call', () => {
311
- const req: AnthropicRequest = {
312
- model: 'gpt-4o',
313
- max_tokens: 100,
314
- messages: [{
315
- role: 'assistant',
316
- content: [
317
- { type: 'tool_use', id: 'tc_1', name: 'search', input: { q: 'test' } },
318
- ],
319
- }],
320
- }
321
- const result = anthropicToOpenaiResponses(req)
322
- const fc = result.input.find((i) => i.type === 'function_call')
323
- expect(fc).toBeDefined()
324
- if (fc && fc.type === 'function_call') {
325
- expect(fc.call_id).toBe('tc_1')
326
- expect(fc.name).toBe('search')
327
- expect(fc.arguments).toBe('{"q":"test"}')
328
- }
329
- })
330
-
331
- test('tool_result lifted to function_call_output', () => {
332
- const req: AnthropicRequest = {
333
- model: 'gpt-4o',
334
- max_tokens: 100,
335
- messages: [{
336
- role: 'user',
337
- content: [
338
- { type: 'tool_result', tool_use_id: 'tc_1', content: 'found it' },
339
- ],
340
- }],
341
- }
342
- const result = anthropicToOpenaiResponses(req)
343
- const fco = result.input.find((i) => i.type === 'function_call_output')
344
- expect(fco).toBeDefined()
345
- if (fco && fco.type === 'function_call_output') {
346
- expect(fco.call_id).toBe('tc_1')
347
- expect(fco.output).toBe('found it')
348
- }
349
- })
350
-
351
- test('thinking → reasoning', () => {
352
- const req: AnthropicRequest = {
353
- model: 'gpt-4o',
354
- max_tokens: 100,
355
- messages: [{ role: 'user', content: 'Hi' }],
356
- thinking: { type: 'enabled', budget_tokens: 10000 },
357
- }
358
- const result = anthropicToOpenaiResponses(req)
359
- expect(result.reasoning).toEqual({ effort: 'high' })
360
- })
361
-
362
- test('stop_sequences dropped', () => {
363
- const req: AnthropicRequest = {
364
- model: 'gpt-4o',
365
- max_tokens: 100,
366
- messages: [{ role: 'user', content: 'Hi' }],
367
- stop_sequences: ['END'],
368
- }
369
- const result = anthropicToOpenaiResponses(req)
370
- expect((result as Record<string, unknown>).stop).toBeUndefined()
371
- expect((result as Record<string, unknown>).stop_sequences).toBeUndefined()
372
- })
373
- })
374
-
375
- // ─── openaiResponsesToAnthropic ─────────────────────────────────
376
-
377
- describe('openaiResponsesToAnthropic', () => {
378
- test('basic text response', () => {
379
- const res: OpenAIResponsesResponse = {
380
- id: 'resp_1',
381
- object: 'response',
382
- created_at: 1234567890,
383
- model: 'gpt-4o',
384
- status: 'completed',
385
- output: [{
386
- type: 'message',
387
- role: 'assistant',
388
- content: [{ type: 'output_text', text: 'Hello!' }],
389
- }],
390
- usage: { input_tokens: 10, output_tokens: 5, total_tokens: 15 },
391
- }
392
- const result = openaiResponsesToAnthropic(res, 'gpt-4o')
393
- expect(result.content).toEqual([{ type: 'text', text: 'Hello!' }])
394
- expect(result.stop_reason).toBe('end_turn')
395
- expect(result.usage.input_tokens).toBe(10)
396
- expect(result.usage.output_tokens).toBe(5)
397
- })
398
-
399
- test('function_call → tool_use', () => {
400
- const res: OpenAIResponsesResponse = {
401
- id: 'resp_2',
402
- object: 'response',
403
- created_at: 0,
404
- model: 'gpt-4o',
405
- status: 'completed',
406
- output: [{
407
- type: 'function_call',
408
- id: 'fc_1',
409
- call_id: 'call_1',
410
- name: 'search',
411
- arguments: '{"q":"test"}',
412
- }],
413
- }
414
- const result = openaiResponsesToAnthropic(res, 'gpt-4o')
415
- expect(result.stop_reason).toBe('tool_use')
416
- expect(result.content[0].type).toBe('tool_use')
417
- if (result.content[0].type === 'tool_use') {
418
- expect(result.content[0].id).toBe('call_1')
419
- expect(result.content[0].input).toEqual({ q: 'test' })
420
- }
421
- })
422
-
423
- test('reasoning → thinking', () => {
424
- const res: OpenAIResponsesResponse = {
425
- id: 'resp_3',
426
- object: 'response',
427
- created_at: 0,
428
- model: 'gpt-4o',
429
- status: 'completed',
430
- output: [
431
- { type: 'reasoning', id: 'r_1', summary: [{ type: 'text', text: 'Thinking...' }] },
432
- { type: 'message', role: 'assistant', content: [{ type: 'output_text', text: 'Result' }] },
433
- ],
434
- }
435
- const result = openaiResponsesToAnthropic(res, 'gpt-4o')
436
- expect(result.content).toHaveLength(2)
437
- expect(result.content[0].type).toBe('thinking')
438
- if (result.content[0].type === 'thinking') {
439
- expect(result.content[0].thinking).toBe('Thinking...')
440
- }
441
- expect(result.content[1].type).toBe('text')
442
- })
443
-
444
- test('status incomplete → max_tokens', () => {
445
- const res: OpenAIResponsesResponse = {
446
- id: 'resp_4',
447
- object: 'response',
448
- created_at: 0,
449
- model: 'gpt-4o',
450
- status: 'incomplete',
451
- output: [{ type: 'message', role: 'assistant', content: [{ type: 'output_text', text: 'partial' }] }],
452
- }
453
- const result = openaiResponsesToAnthropic(res, 'gpt-4o')
454
- expect(result.stop_reason).toBe('max_tokens')
455
- })
456
-
457
- test('empty output', () => {
458
- const res: OpenAIResponsesResponse = {
459
- id: 'resp_5',
460
- object: 'response',
461
- created_at: 0,
462
- model: 'gpt-4o',
463
- status: 'completed',
464
- output: [],
465
- }
466
- const result = openaiResponsesToAnthropic(res, 'gpt-4o')
467
- expect(result.content).toEqual([{ type: 'text', text: '' }])
468
- })
469
- })