@jackchen_me/open-multi-agent 1.0.0 → 1.0.1

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 (80) hide show
  1. package/package.json +8 -2
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
  4. package/.github/pull_request_template.md +0 -14
  5. package/.github/workflows/ci.yml +0 -23
  6. package/CLAUDE.md +0 -80
  7. package/CODE_OF_CONDUCT.md +0 -48
  8. package/CONTRIBUTING.md +0 -72
  9. package/DECISIONS.md +0 -43
  10. package/README_zh.md +0 -277
  11. package/SECURITY.md +0 -17
  12. package/examples/01-single-agent.ts +0 -131
  13. package/examples/02-team-collaboration.ts +0 -167
  14. package/examples/03-task-pipeline.ts +0 -201
  15. package/examples/04-multi-model-team.ts +0 -261
  16. package/examples/05-copilot-test.ts +0 -49
  17. package/examples/06-local-model.ts +0 -200
  18. package/examples/07-fan-out-aggregate.ts +0 -209
  19. package/examples/08-gemma4-local.ts +0 -192
  20. package/examples/09-structured-output.ts +0 -73
  21. package/examples/10-task-retry.ts +0 -132
  22. package/examples/11-trace-observability.ts +0 -133
  23. package/examples/12-grok.ts +0 -154
  24. package/examples/13-gemini.ts +0 -48
  25. package/src/agent/agent.ts +0 -622
  26. package/src/agent/loop-detector.ts +0 -137
  27. package/src/agent/pool.ts +0 -285
  28. package/src/agent/runner.ts +0 -542
  29. package/src/agent/structured-output.ts +0 -126
  30. package/src/index.ts +0 -182
  31. package/src/llm/adapter.ts +0 -98
  32. package/src/llm/anthropic.ts +0 -389
  33. package/src/llm/copilot.ts +0 -552
  34. package/src/llm/gemini.ts +0 -378
  35. package/src/llm/grok.ts +0 -29
  36. package/src/llm/openai-common.ts +0 -294
  37. package/src/llm/openai.ts +0 -292
  38. package/src/memory/shared.ts +0 -181
  39. package/src/memory/store.ts +0 -124
  40. package/src/orchestrator/orchestrator.ts +0 -1071
  41. package/src/orchestrator/scheduler.ts +0 -352
  42. package/src/task/queue.ts +0 -464
  43. package/src/task/task.ts +0 -239
  44. package/src/team/messaging.ts +0 -232
  45. package/src/team/team.ts +0 -334
  46. package/src/tool/built-in/bash.ts +0 -187
  47. package/src/tool/built-in/file-edit.ts +0 -154
  48. package/src/tool/built-in/file-read.ts +0 -105
  49. package/src/tool/built-in/file-write.ts +0 -81
  50. package/src/tool/built-in/grep.ts +0 -362
  51. package/src/tool/built-in/index.ts +0 -50
  52. package/src/tool/executor.ts +0 -178
  53. package/src/tool/framework.ts +0 -557
  54. package/src/tool/text-tool-extractor.ts +0 -219
  55. package/src/types.ts +0 -542
  56. package/src/utils/semaphore.ts +0 -89
  57. package/src/utils/trace.ts +0 -34
  58. package/tests/agent-hooks.test.ts +0 -473
  59. package/tests/agent-pool.test.ts +0 -212
  60. package/tests/approval.test.ts +0 -464
  61. package/tests/built-in-tools.test.ts +0 -393
  62. package/tests/gemini-adapter.test.ts +0 -97
  63. package/tests/grok-adapter.test.ts +0 -74
  64. package/tests/llm-adapters.test.ts +0 -357
  65. package/tests/loop-detection.test.ts +0 -456
  66. package/tests/openai-fallback.test.ts +0 -159
  67. package/tests/orchestrator.test.ts +0 -281
  68. package/tests/scheduler.test.ts +0 -221
  69. package/tests/semaphore.test.ts +0 -57
  70. package/tests/shared-memory.test.ts +0 -122
  71. package/tests/structured-output.test.ts +0 -331
  72. package/tests/task-queue.test.ts +0 -244
  73. package/tests/task-retry.test.ts +0 -368
  74. package/tests/task-utils.test.ts +0 -155
  75. package/tests/team-messaging.test.ts +0 -329
  76. package/tests/text-tool-extractor.test.ts +0 -170
  77. package/tests/tool-executor.test.ts +0 -193
  78. package/tests/trace.test.ts +0 -453
  79. package/tsconfig.json +0 -25
  80. package/vitest.config.ts +0 -9
@@ -1,357 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { createAdapter } from '../src/llm/adapter.js'
3
- import {
4
- toOpenAITool,
5
- toOpenAIMessages,
6
- fromOpenAICompletion,
7
- normalizeFinishReason,
8
- buildOpenAIMessageList,
9
- } from '../src/llm/openai-common.js'
10
- import type {
11
- ContentBlock,
12
- LLMMessage,
13
- LLMToolDef,
14
- } from '../src/types.js'
15
- import type { ChatCompletion } from 'openai/resources/chat/completions/index.js'
16
-
17
- // ===========================================================================
18
- // createAdapter factory
19
- // ===========================================================================
20
-
21
- describe('createAdapter', () => {
22
- it('creates an anthropic adapter', async () => {
23
- const adapter = await createAdapter('anthropic', 'test-key')
24
- expect(adapter.name).toBe('anthropic')
25
- })
26
-
27
- it('creates an openai adapter', async () => {
28
- const adapter = await createAdapter('openai', 'test-key')
29
- expect(adapter.name).toBe('openai')
30
- })
31
-
32
- it('creates a grok adapter', async () => {
33
- const adapter = await createAdapter('grok', 'test-key')
34
- expect(adapter.name).toBe('grok')
35
- })
36
-
37
- it('creates a gemini adapter', async () => {
38
- const adapter = await createAdapter('gemini', 'test-key')
39
- expect(adapter.name).toBe('gemini')
40
- })
41
-
42
- it('throws on unknown provider', async () => {
43
- await expect(
44
- createAdapter('unknown' as any, 'test-key'),
45
- ).rejects.toThrow('Unsupported')
46
- })
47
- })
48
-
49
- // ===========================================================================
50
- // OpenAI common helpers
51
- // ===========================================================================
52
-
53
- describe('normalizeFinishReason', () => {
54
- it('maps stop → end_turn', () => {
55
- expect(normalizeFinishReason('stop')).toBe('end_turn')
56
- })
57
-
58
- it('maps tool_calls → tool_use', () => {
59
- expect(normalizeFinishReason('tool_calls')).toBe('tool_use')
60
- })
61
-
62
- it('maps length → max_tokens', () => {
63
- expect(normalizeFinishReason('length')).toBe('max_tokens')
64
- })
65
-
66
- it('maps content_filter → content_filter', () => {
67
- expect(normalizeFinishReason('content_filter')).toBe('content_filter')
68
- })
69
-
70
- it('passes through unknown reasons', () => {
71
- expect(normalizeFinishReason('custom_reason')).toBe('custom_reason')
72
- })
73
- })
74
-
75
- describe('toOpenAITool', () => {
76
- it('converts framework tool def to OpenAI format', () => {
77
- const tool: LLMToolDef = {
78
- name: 'search',
79
- description: 'Search the web',
80
- inputSchema: {
81
- type: 'object',
82
- properties: { query: { type: 'string' } },
83
- },
84
- }
85
-
86
- const result = toOpenAITool(tool)
87
-
88
- expect(result.type).toBe('function')
89
- expect(result.function.name).toBe('search')
90
- expect(result.function.description).toBe('Search the web')
91
- expect(result.function.parameters).toEqual(tool.inputSchema)
92
- })
93
- })
94
-
95
- describe('toOpenAIMessages', () => {
96
- it('converts a simple user text message', () => {
97
- const msgs: LLMMessage[] = [
98
- { role: 'user', content: [{ type: 'text', text: 'hello' }] },
99
- ]
100
-
101
- const result = toOpenAIMessages(msgs)
102
-
103
- expect(result).toHaveLength(1)
104
- expect(result[0]).toEqual({ role: 'user', content: 'hello' })
105
- })
106
-
107
- it('converts assistant message with text', () => {
108
- const msgs: LLMMessage[] = [
109
- { role: 'assistant', content: [{ type: 'text', text: 'hi' }] },
110
- ]
111
-
112
- const result = toOpenAIMessages(msgs)
113
-
114
- expect(result[0]).toEqual({ role: 'assistant', content: 'hi', tool_calls: undefined })
115
- })
116
-
117
- it('converts assistant message with tool_use into tool_calls', () => {
118
- const msgs: LLMMessage[] = [
119
- {
120
- role: 'assistant',
121
- content: [
122
- { type: 'tool_use', id: 'tc1', name: 'search', input: { query: 'AI' } },
123
- ],
124
- },
125
- ]
126
-
127
- const result = toOpenAIMessages(msgs)
128
-
129
- expect(result).toHaveLength(1)
130
- const msg = result[0]! as any
131
- expect(msg.role).toBe('assistant')
132
- expect(msg.tool_calls).toHaveLength(1)
133
- expect(msg.tool_calls[0].function.name).toBe('search')
134
- })
135
-
136
- it('splits tool_result blocks into separate tool-role messages', () => {
137
- const msgs: LLMMessage[] = [
138
- {
139
- role: 'user',
140
- content: [
141
- { type: 'tool_result', tool_use_id: 'tc1', content: 'result data' },
142
- ],
143
- },
144
- ]
145
-
146
- const result = toOpenAIMessages(msgs)
147
-
148
- expect(result).toHaveLength(1)
149
- expect(result[0]).toEqual({
150
- role: 'tool',
151
- tool_call_id: 'tc1',
152
- content: 'result data',
153
- })
154
- })
155
-
156
- it('handles mixed user message with text and tool_result', () => {
157
- const msgs: LLMMessage[] = [
158
- {
159
- role: 'user',
160
- content: [
161
- { type: 'text', text: 'context' },
162
- { type: 'tool_result', tool_use_id: 'tc1', content: 'data' },
163
- ],
164
- },
165
- ]
166
-
167
- const result = toOpenAIMessages(msgs)
168
-
169
- // Should produce a user message for text, then a tool message for result
170
- expect(result.length).toBeGreaterThanOrEqual(2)
171
- expect(result[0]).toEqual({ role: 'user', content: 'context' })
172
- expect(result[1]).toEqual({
173
- role: 'tool',
174
- tool_call_id: 'tc1',
175
- content: 'data',
176
- })
177
- })
178
-
179
- it('handles image blocks in user messages', () => {
180
- const msgs: LLMMessage[] = [
181
- {
182
- role: 'user',
183
- content: [
184
- { type: 'text', text: 'describe this' },
185
- {
186
- type: 'image',
187
- source: { type: 'base64', media_type: 'image/png', data: 'abc123' },
188
- },
189
- ],
190
- },
191
- ]
192
-
193
- const result = toOpenAIMessages(msgs)
194
-
195
- expect(result).toHaveLength(1)
196
- const content = (result[0] as any).content
197
- expect(content).toHaveLength(2)
198
- expect(content[1].type).toBe('image_url')
199
- expect(content[1].image_url.url).toContain('data:image/png;base64,abc123')
200
- })
201
- })
202
-
203
- describe('fromOpenAICompletion', () => {
204
- function makeCompletion(overrides?: Partial<ChatCompletion>): ChatCompletion {
205
- return {
206
- id: 'comp-1',
207
- object: 'chat.completion',
208
- created: Date.now(),
209
- model: 'gpt-4',
210
- choices: [
211
- {
212
- index: 0,
213
- message: { role: 'assistant', content: 'Hello!', refusal: null },
214
- finish_reason: 'stop',
215
- logprobs: null,
216
- },
217
- ],
218
- usage: { prompt_tokens: 10, completion_tokens: 20, total_tokens: 30 },
219
- ...overrides,
220
- }
221
- }
222
-
223
- it('converts a simple text completion', () => {
224
- const result = fromOpenAICompletion(makeCompletion())
225
-
226
- expect(result.id).toBe('comp-1')
227
- expect(result.model).toBe('gpt-4')
228
- expect(result.stop_reason).toBe('end_turn') // 'stop' → 'end_turn'
229
- expect(result.content).toHaveLength(1)
230
- expect(result.content[0]).toEqual({ type: 'text', text: 'Hello!' })
231
- expect(result.usage.input_tokens).toBe(10)
232
- expect(result.usage.output_tokens).toBe(20)
233
- })
234
-
235
- it('converts tool_calls into tool_use blocks', () => {
236
- const completion = makeCompletion({
237
- choices: [
238
- {
239
- index: 0,
240
- message: {
241
- role: 'assistant',
242
- content: null,
243
- refusal: null,
244
- tool_calls: [
245
- {
246
- id: 'tc1',
247
- type: 'function',
248
- function: {
249
- name: 'search',
250
- arguments: '{"query":"test"}',
251
- },
252
- },
253
- ],
254
- },
255
- finish_reason: 'tool_calls',
256
- logprobs: null,
257
- },
258
- ],
259
- })
260
-
261
- const result = fromOpenAICompletion(completion)
262
-
263
- expect(result.stop_reason).toBe('tool_use')
264
- expect(result.content).toHaveLength(1)
265
- expect(result.content[0]).toEqual({
266
- type: 'tool_use',
267
- id: 'tc1',
268
- name: 'search',
269
- input: { query: 'test' },
270
- })
271
- })
272
-
273
- it('throws when completion has no choices', () => {
274
- const completion = makeCompletion({ choices: [] })
275
- expect(() => fromOpenAICompletion(completion)).toThrow('no choices')
276
- })
277
-
278
- it('handles malformed tool arguments gracefully', () => {
279
- const completion = makeCompletion({
280
- choices: [
281
- {
282
- index: 0,
283
- message: {
284
- role: 'assistant',
285
- content: null,
286
- refusal: null,
287
- tool_calls: [
288
- {
289
- id: 'tc1',
290
- type: 'function',
291
- function: {
292
- name: 'search',
293
- arguments: 'not-valid-json',
294
- },
295
- },
296
- ],
297
- },
298
- finish_reason: 'tool_calls',
299
- logprobs: null,
300
- },
301
- ],
302
- })
303
-
304
- const result = fromOpenAICompletion(completion)
305
-
306
- // Should not throw; input defaults to {}
307
- expect(result.content[0]).toEqual({
308
- type: 'tool_use',
309
- id: 'tc1',
310
- name: 'search',
311
- input: {},
312
- })
313
- })
314
-
315
- it('handles missing usage gracefully', () => {
316
- const completion = makeCompletion({ usage: undefined })
317
-
318
- const result = fromOpenAICompletion(completion)
319
-
320
- expect(result.usage.input_tokens).toBe(0)
321
- expect(result.usage.output_tokens).toBe(0)
322
- })
323
- })
324
-
325
- describe('buildOpenAIMessageList', () => {
326
- it('prepends system prompt when provided', () => {
327
- const msgs: LLMMessage[] = [
328
- { role: 'user', content: [{ type: 'text', text: 'hi' }] },
329
- ]
330
-
331
- const result = buildOpenAIMessageList(msgs, 'You are helpful.')
332
-
333
- expect(result[0]).toEqual({ role: 'system', content: 'You are helpful.' })
334
- expect(result).toHaveLength(2)
335
- })
336
-
337
- it('omits system message when systemPrompt is undefined', () => {
338
- const msgs: LLMMessage[] = [
339
- { role: 'user', content: [{ type: 'text', text: 'hi' }] },
340
- ]
341
-
342
- const result = buildOpenAIMessageList(msgs, undefined)
343
-
344
- expect(result).toHaveLength(1)
345
- expect(result[0]).toEqual({ role: 'user', content: 'hi' })
346
- })
347
-
348
- it('omits system message when systemPrompt is empty string', () => {
349
- const msgs: LLMMessage[] = [
350
- { role: 'user', content: [{ type: 'text', text: 'hi' }] },
351
- ]
352
-
353
- const result = buildOpenAIMessageList(msgs, '')
354
-
355
- expect(result).toHaveLength(1)
356
- })
357
- })