@jackchen_me/open-multi-agent 0.2.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.
- package/README.md +87 -20
- package/dist/agent/agent.d.ts +15 -1
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +144 -10
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/loop-detector.d.ts +39 -0
- package/dist/agent/loop-detector.d.ts.map +1 -0
- package/dist/agent/loop-detector.js +122 -0
- package/dist/agent/loop-detector.js.map +1 -0
- package/dist/agent/pool.d.ts +2 -1
- package/dist/agent/pool.d.ts.map +1 -1
- package/dist/agent/pool.js +4 -2
- package/dist/agent/pool.js.map +1 -1
- package/dist/agent/runner.d.ts +23 -1
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +113 -12
- package/dist/agent/runner.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/adapter.d.ts +4 -1
- package/dist/llm/adapter.d.ts.map +1 -1
- package/dist/llm/adapter.js +11 -0
- package/dist/llm/adapter.js.map +1 -1
- package/dist/llm/copilot.d.ts.map +1 -1
- package/dist/llm/copilot.js +2 -1
- package/dist/llm/copilot.js.map +1 -1
- package/dist/llm/gemini.d.ts +65 -0
- package/dist/llm/gemini.d.ts.map +1 -0
- package/dist/llm/gemini.js +317 -0
- package/dist/llm/gemini.js.map +1 -0
- package/dist/llm/grok.d.ts +21 -0
- package/dist/llm/grok.d.ts.map +1 -0
- package/dist/llm/grok.js +24 -0
- package/dist/llm/grok.js.map +1 -0
- package/dist/llm/openai-common.d.ts +8 -1
- package/dist/llm/openai-common.d.ts.map +1 -1
- package/dist/llm/openai-common.js +35 -2
- package/dist/llm/openai-common.js.map +1 -1
- package/dist/llm/openai.d.ts +1 -1
- package/dist/llm/openai.d.ts.map +1 -1
- package/dist/llm/openai.js +20 -2
- package/dist/llm/openai.js.map +1 -1
- package/dist/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator.js +89 -9
- package/dist/orchestrator/orchestrator.js.map +1 -1
- package/dist/task/queue.d.ts +31 -2
- package/dist/task/queue.d.ts.map +1 -1
- package/dist/task/queue.js +69 -2
- package/dist/task/queue.js.map +1 -1
- package/dist/tool/text-tool-extractor.d.ts +32 -0
- package/dist/tool/text-tool-extractor.d.ts.map +1 -0
- package/dist/tool/text-tool-extractor.js +187 -0
- package/dist/tool/text-tool-extractor.js.map +1 -0
- package/dist/types.d.ts +139 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/trace.d.ts +12 -0
- package/dist/utils/trace.d.ts.map +1 -0
- package/dist/utils/trace.js +30 -0
- package/dist/utils/trace.js.map +1 -0
- package/package.json +18 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
- package/.github/pull_request_template.md +0 -14
- package/.github/workflows/ci.yml +0 -23
- package/CLAUDE.md +0 -72
- package/CODE_OF_CONDUCT.md +0 -48
- package/CONTRIBUTING.md +0 -72
- package/DECISIONS.md +0 -43
- package/README_zh.md +0 -217
- package/SECURITY.md +0 -17
- package/examples/01-single-agent.ts +0 -131
- package/examples/02-team-collaboration.ts +0 -167
- package/examples/03-task-pipeline.ts +0 -201
- package/examples/04-multi-model-team.ts +0 -261
- package/examples/05-copilot-test.ts +0 -49
- package/examples/06-local-model.ts +0 -199
- package/examples/07-fan-out-aggregate.ts +0 -209
- package/examples/08-gemma4-local.ts +0 -203
- package/examples/09-gemma4-auto-orchestration.ts +0 -162
- package/src/agent/agent.ts +0 -473
- package/src/agent/pool.ts +0 -278
- package/src/agent/runner.ts +0 -413
- package/src/agent/structured-output.ts +0 -126
- package/src/index.ts +0 -167
- package/src/llm/adapter.ts +0 -87
- package/src/llm/anthropic.ts +0 -389
- package/src/llm/copilot.ts +0 -551
- package/src/llm/openai-common.ts +0 -255
- package/src/llm/openai.ts +0 -272
- package/src/memory/shared.ts +0 -181
- package/src/memory/store.ts +0 -124
- package/src/orchestrator/orchestrator.ts +0 -977
- package/src/orchestrator/scheduler.ts +0 -352
- package/src/task/queue.ts +0 -394
- package/src/task/task.ts +0 -239
- package/src/team/messaging.ts +0 -232
- package/src/team/team.ts +0 -334
- package/src/tool/built-in/bash.ts +0 -187
- package/src/tool/built-in/file-edit.ts +0 -154
- package/src/tool/built-in/file-read.ts +0 -105
- package/src/tool/built-in/file-write.ts +0 -81
- package/src/tool/built-in/grep.ts +0 -362
- package/src/tool/built-in/index.ts +0 -50
- package/src/tool/executor.ts +0 -178
- package/src/tool/framework.ts +0 -557
- package/src/types.ts +0 -391
- package/src/utils/semaphore.ts +0 -89
- package/tests/semaphore.test.ts +0 -57
- package/tests/shared-memory.test.ts +0 -122
- package/tests/structured-output.test.ts +0 -331
- package/tests/task-queue.test.ts +0 -244
- package/tests/task-retry.test.ts +0 -368
- package/tests/task-utils.test.ts +0 -155
- package/tests/tool-executor.test.ts +0 -193
- package/tsconfig.json +0 -25
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example 01 — Single Agent
|
|
3
|
-
*
|
|
4
|
-
* The simplest possible usage: one agent with bash and file tools, running
|
|
5
|
-
* a coding task. Then shows streaming output using the Agent class directly.
|
|
6
|
-
*
|
|
7
|
-
* Run:
|
|
8
|
-
* npx tsx examples/01-single-agent.ts
|
|
9
|
-
*
|
|
10
|
-
* Prerequisites:
|
|
11
|
-
* ANTHROPIC_API_KEY env var must be set.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { OpenMultiAgent, Agent, ToolRegistry, ToolExecutor, registerBuiltInTools } from '../src/index.js'
|
|
15
|
-
import type { OrchestratorEvent } from '../src/types.js'
|
|
16
|
-
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
// Part 1: Single agent via OpenMultiAgent (simplest path)
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
|
|
21
|
-
const orchestrator = new OpenMultiAgent({
|
|
22
|
-
defaultModel: 'claude-sonnet-4-6',
|
|
23
|
-
onProgress: (event: OrchestratorEvent) => {
|
|
24
|
-
if (event.type === 'agent_start') {
|
|
25
|
-
console.log(`[start] agent=${event.agent}`)
|
|
26
|
-
} else if (event.type === 'agent_complete') {
|
|
27
|
-
console.log(`[complete] agent=${event.agent}`)
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
console.log('Part 1: runAgent() — single one-shot task\n')
|
|
33
|
-
|
|
34
|
-
const result = await orchestrator.runAgent(
|
|
35
|
-
{
|
|
36
|
-
name: 'coder',
|
|
37
|
-
model: 'claude-sonnet-4-6',
|
|
38
|
-
systemPrompt: `You are a focused TypeScript developer.
|
|
39
|
-
When asked to implement something, write clean, minimal code with no extra commentary.
|
|
40
|
-
Use the bash tool to run commands and the file tools to read/write files.`,
|
|
41
|
-
tools: ['bash', 'file_read', 'file_write'],
|
|
42
|
-
maxTurns: 8,
|
|
43
|
-
},
|
|
44
|
-
`Create a small TypeScript utility function in /tmp/greet.ts that:
|
|
45
|
-
1. Exports a function named greet(name: string): string
|
|
46
|
-
2. Returns "Hello, <name>!"
|
|
47
|
-
3. Adds a brief usage comment at the top of the file.
|
|
48
|
-
Then add a default call greet("World") at the bottom and run the file with: npx tsx /tmp/greet.ts`,
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
if (result.success) {
|
|
52
|
-
console.log('\nAgent output:')
|
|
53
|
-
console.log('─'.repeat(60))
|
|
54
|
-
console.log(result.output)
|
|
55
|
-
console.log('─'.repeat(60))
|
|
56
|
-
} else {
|
|
57
|
-
console.error('Agent failed:', result.output)
|
|
58
|
-
process.exit(1)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
console.log('\nToken usage:')
|
|
62
|
-
console.log(` input: ${result.tokenUsage.input_tokens}`)
|
|
63
|
-
console.log(` output: ${result.tokenUsage.output_tokens}`)
|
|
64
|
-
console.log(` tool calls made: ${result.toolCalls.length}`)
|
|
65
|
-
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
// Part 2: Streaming via Agent directly
|
|
68
|
-
//
|
|
69
|
-
// OpenMultiAgent.runAgent() is a convenient wrapper. When you need streaming, use
|
|
70
|
-
// the Agent class directly with an injected ToolRegistry + ToolExecutor.
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
72
|
-
|
|
73
|
-
console.log('\n\nPart 2: Agent.stream() — incremental text output\n')
|
|
74
|
-
|
|
75
|
-
// Build a registry with all built-in tools registered
|
|
76
|
-
const registry = new ToolRegistry()
|
|
77
|
-
registerBuiltInTools(registry)
|
|
78
|
-
const executor = new ToolExecutor(registry)
|
|
79
|
-
|
|
80
|
-
const streamingAgent = new Agent(
|
|
81
|
-
{
|
|
82
|
-
name: 'explainer',
|
|
83
|
-
model: 'claude-sonnet-4-6',
|
|
84
|
-
systemPrompt: 'You are a concise technical writer. Keep explanations brief.',
|
|
85
|
-
maxTurns: 3,
|
|
86
|
-
},
|
|
87
|
-
registry,
|
|
88
|
-
executor,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
process.stdout.write('Streaming: ')
|
|
92
|
-
|
|
93
|
-
for await (const event of streamingAgent.stream(
|
|
94
|
-
'In two sentences, explain what a TypeScript generic constraint is.',
|
|
95
|
-
)) {
|
|
96
|
-
if (event.type === 'text' && typeof event.data === 'string') {
|
|
97
|
-
process.stdout.write(event.data)
|
|
98
|
-
} else if (event.type === 'done') {
|
|
99
|
-
process.stdout.write('\n')
|
|
100
|
-
} else if (event.type === 'error') {
|
|
101
|
-
console.error('\nStream error:', event.data)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
// Part 3: Multi-turn conversation via Agent.prompt()
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
|
|
109
|
-
console.log('\nPart 3: Agent.prompt() — multi-turn conversation\n')
|
|
110
|
-
|
|
111
|
-
const conversationAgent = new Agent(
|
|
112
|
-
{
|
|
113
|
-
name: 'tutor',
|
|
114
|
-
model: 'claude-sonnet-4-6',
|
|
115
|
-
systemPrompt: 'You are a TypeScript tutor. Give short, direct answers.',
|
|
116
|
-
maxTurns: 2,
|
|
117
|
-
},
|
|
118
|
-
new ToolRegistry(), // no tools needed for this conversation
|
|
119
|
-
new ToolExecutor(new ToolRegistry()),
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
const turn1 = await conversationAgent.prompt('What is a type guard in TypeScript?')
|
|
123
|
-
console.log('Turn 1:', turn1.output.slice(0, 200))
|
|
124
|
-
|
|
125
|
-
const turn2 = await conversationAgent.prompt('Give me one concrete code example of what you just described.')
|
|
126
|
-
console.log('\nTurn 2:', turn2.output.slice(0, 300))
|
|
127
|
-
|
|
128
|
-
// History is retained between prompt() calls
|
|
129
|
-
console.log(`\nConversation history length: ${conversationAgent.getHistory().length} messages`)
|
|
130
|
-
|
|
131
|
-
console.log('\nDone.')
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example 02 — Multi-Agent Team Collaboration
|
|
3
|
-
*
|
|
4
|
-
* Three specialised agents (architect, developer, reviewer) collaborate on a
|
|
5
|
-
* shared goal. The OpenMultiAgent orchestrator breaks the goal into tasks, assigns
|
|
6
|
-
* them to the right agents, and collects the results.
|
|
7
|
-
*
|
|
8
|
-
* Run:
|
|
9
|
-
* npx tsx examples/02-team-collaboration.ts
|
|
10
|
-
*
|
|
11
|
-
* Prerequisites:
|
|
12
|
-
* ANTHROPIC_API_KEY env var must be set.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { OpenMultiAgent } from '../src/index.js'
|
|
16
|
-
import type { AgentConfig, OrchestratorEvent } from '../src/types.js'
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Agent definitions
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
const architect: AgentConfig = {
|
|
23
|
-
name: 'architect',
|
|
24
|
-
model: 'claude-sonnet-4-6',
|
|
25
|
-
provider: 'anthropic',
|
|
26
|
-
systemPrompt: `You are a software architect with deep experience in Node.js and REST API design.
|
|
27
|
-
Your job is to design clear, production-quality API contracts and file/directory structures.
|
|
28
|
-
Output concise plans in markdown — no unnecessary prose.`,
|
|
29
|
-
tools: ['bash', 'file_write'],
|
|
30
|
-
maxTurns: 5,
|
|
31
|
-
temperature: 0.2,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const developer: AgentConfig = {
|
|
35
|
-
name: 'developer',
|
|
36
|
-
model: 'claude-sonnet-4-6',
|
|
37
|
-
provider: 'anthropic',
|
|
38
|
-
systemPrompt: `You are a TypeScript/Node.js developer. You implement what the architect specifies.
|
|
39
|
-
Write clean, runnable code with proper error handling. Use the tools to write files and run tests.`,
|
|
40
|
-
tools: ['bash', 'file_read', 'file_write', 'file_edit'],
|
|
41
|
-
maxTurns: 12,
|
|
42
|
-
temperature: 0.1,
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const reviewer: AgentConfig = {
|
|
46
|
-
name: 'reviewer',
|
|
47
|
-
model: 'claude-sonnet-4-6',
|
|
48
|
-
provider: 'anthropic',
|
|
49
|
-
systemPrompt: `You are a senior code reviewer. Review code for correctness, security, and clarity.
|
|
50
|
-
Provide a structured review with: LGTM items, suggestions, and any blocking issues.
|
|
51
|
-
Read files using the tools before reviewing.`,
|
|
52
|
-
tools: ['bash', 'file_read', 'grep'],
|
|
53
|
-
maxTurns: 5,
|
|
54
|
-
temperature: 0.3,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Progress tracking
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
|
|
61
|
-
const startTimes = new Map<string, number>()
|
|
62
|
-
|
|
63
|
-
function handleProgress(event: OrchestratorEvent): void {
|
|
64
|
-
const ts = new Date().toISOString().slice(11, 23) // HH:MM:SS.mmm
|
|
65
|
-
|
|
66
|
-
switch (event.type) {
|
|
67
|
-
case 'agent_start':
|
|
68
|
-
startTimes.set(event.agent ?? '', Date.now())
|
|
69
|
-
console.log(`[${ts}] AGENT START → ${event.agent}`)
|
|
70
|
-
break
|
|
71
|
-
|
|
72
|
-
case 'agent_complete': {
|
|
73
|
-
const elapsed = Date.now() - (startTimes.get(event.agent ?? '') ?? Date.now())
|
|
74
|
-
console.log(`[${ts}] AGENT DONE ← ${event.agent} (${elapsed}ms)`)
|
|
75
|
-
break
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
case 'task_start':
|
|
79
|
-
console.log(`[${ts}] TASK START ↓ ${event.task}`)
|
|
80
|
-
break
|
|
81
|
-
|
|
82
|
-
case 'task_complete':
|
|
83
|
-
console.log(`[${ts}] TASK DONE ↑ ${event.task}`)
|
|
84
|
-
break
|
|
85
|
-
|
|
86
|
-
case 'message':
|
|
87
|
-
console.log(`[${ts}] MESSAGE • ${event.agent} → (team)`)
|
|
88
|
-
break
|
|
89
|
-
|
|
90
|
-
case 'error':
|
|
91
|
-
console.error(`[${ts}] ERROR ✗ agent=${event.agent} task=${event.task}`)
|
|
92
|
-
if (event.data instanceof Error) {
|
|
93
|
-
console.error(` ${event.data.message}`)
|
|
94
|
-
}
|
|
95
|
-
break
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
// Orchestrate
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
|
|
103
|
-
const orchestrator = new OpenMultiAgent({
|
|
104
|
-
defaultModel: 'claude-sonnet-4-6',
|
|
105
|
-
maxConcurrency: 1, // run agents sequentially so output is readable
|
|
106
|
-
onProgress: handleProgress,
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
const team = orchestrator.createTeam('api-team', {
|
|
110
|
-
name: 'api-team',
|
|
111
|
-
agents: [architect, developer, reviewer],
|
|
112
|
-
sharedMemory: true,
|
|
113
|
-
maxConcurrency: 1,
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
console.log(`Team "${team.name}" created with agents: ${team.getAgents().map(a => a.name).join(', ')}`)
|
|
117
|
-
console.log('\nStarting team run...\n')
|
|
118
|
-
console.log('='.repeat(60))
|
|
119
|
-
|
|
120
|
-
const goal = `Create a minimal Express.js REST API in /tmp/express-api/ with:
|
|
121
|
-
- GET /health → { status: "ok" }
|
|
122
|
-
- GET /users → returns a hardcoded array of 2 user objects
|
|
123
|
-
- POST /users → accepts { name, email } body, logs it, returns 201
|
|
124
|
-
- Proper error handling middleware
|
|
125
|
-
- The server should listen on port 3001
|
|
126
|
-
- Include a package.json with the required dependencies`
|
|
127
|
-
|
|
128
|
-
const result = await orchestrator.runTeam(team, goal)
|
|
129
|
-
|
|
130
|
-
console.log('\n' + '='.repeat(60))
|
|
131
|
-
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
// Results
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
|
|
136
|
-
console.log('\nTeam run complete.')
|
|
137
|
-
console.log(`Success: ${result.success}`)
|
|
138
|
-
console.log(`Total tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`)
|
|
139
|
-
|
|
140
|
-
console.log('\nPer-agent results:')
|
|
141
|
-
for (const [agentName, agentResult] of result.agentResults) {
|
|
142
|
-
const status = agentResult.success ? 'OK' : 'FAILED'
|
|
143
|
-
const tools = agentResult.toolCalls.length
|
|
144
|
-
console.log(` ${agentName.padEnd(12)} [${status}] tool_calls=${tools}`)
|
|
145
|
-
if (!agentResult.success) {
|
|
146
|
-
console.log(` Error: ${agentResult.output.slice(0, 120)}`)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Print the developer's final output (the actual code) as a sample
|
|
151
|
-
const developerResult = result.agentResults.get('developer')
|
|
152
|
-
if (developerResult?.success) {
|
|
153
|
-
console.log('\nDeveloper output (last 600 chars):')
|
|
154
|
-
console.log('─'.repeat(60))
|
|
155
|
-
const out = developerResult.output
|
|
156
|
-
console.log(out.length > 600 ? '...' + out.slice(-600) : out)
|
|
157
|
-
console.log('─'.repeat(60))
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Print the reviewer's findings
|
|
161
|
-
const reviewerResult = result.agentResults.get('reviewer')
|
|
162
|
-
if (reviewerResult?.success) {
|
|
163
|
-
console.log('\nReviewer output:')
|
|
164
|
-
console.log('─'.repeat(60))
|
|
165
|
-
console.log(reviewerResult.output)
|
|
166
|
-
console.log('─'.repeat(60))
|
|
167
|
-
}
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example 03 — Explicit Task Pipeline with Dependencies
|
|
3
|
-
*
|
|
4
|
-
* Demonstrates how to define tasks with explicit dependency chains
|
|
5
|
-
* (design → implement → test → review) using runTasks(). The TaskQueue
|
|
6
|
-
* automatically blocks downstream tasks until their dependencies complete.
|
|
7
|
-
*
|
|
8
|
-
* Run:
|
|
9
|
-
* npx tsx examples/03-task-pipeline.ts
|
|
10
|
-
*
|
|
11
|
-
* Prerequisites:
|
|
12
|
-
* ANTHROPIC_API_KEY env var must be set.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { OpenMultiAgent } from '../src/index.js'
|
|
16
|
-
import type { AgentConfig, OrchestratorEvent, Task } from '../src/types.js'
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Agents
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
const designer: AgentConfig = {
|
|
23
|
-
name: 'designer',
|
|
24
|
-
model: 'claude-sonnet-4-6',
|
|
25
|
-
systemPrompt: `You are a software designer. Your output is always a concise technical spec
|
|
26
|
-
in markdown. Focus on interfaces, data shapes, and file structure. Be brief.`,
|
|
27
|
-
tools: ['file_write'],
|
|
28
|
-
maxTurns: 4,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const implementer: AgentConfig = {
|
|
32
|
-
name: 'implementer',
|
|
33
|
-
model: 'claude-sonnet-4-6',
|
|
34
|
-
systemPrompt: `You are a TypeScript developer. Read the design spec written by the designer,
|
|
35
|
-
then implement it. Write all files to /tmp/pipeline-output/. Use the tools.`,
|
|
36
|
-
tools: ['bash', 'file_read', 'file_write'],
|
|
37
|
-
maxTurns: 10,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const tester: AgentConfig = {
|
|
41
|
-
name: 'tester',
|
|
42
|
-
model: 'claude-sonnet-4-6',
|
|
43
|
-
systemPrompt: `You are a QA engineer. Read the implemented files and run them to verify correctness.
|
|
44
|
-
Report: what passed, what failed, and any bugs found.`,
|
|
45
|
-
tools: ['bash', 'file_read', 'grep'],
|
|
46
|
-
maxTurns: 6,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const reviewer: AgentConfig = {
|
|
50
|
-
name: 'reviewer',
|
|
51
|
-
model: 'claude-sonnet-4-6',
|
|
52
|
-
systemPrompt: `You are a code reviewer. Read all files and produce a brief structured review.
|
|
53
|
-
Sections: Summary, Strengths, Issues (if any), Verdict (SHIP / NEEDS WORK).`,
|
|
54
|
-
tools: ['file_read', 'grep'],
|
|
55
|
-
maxTurns: 4,
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ---------------------------------------------------------------------------
|
|
59
|
-
// Progress handler — shows dependency blocking/unblocking
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
|
-
|
|
62
|
-
const taskTimes = new Map<string, number>()
|
|
63
|
-
|
|
64
|
-
function handleProgress(event: OrchestratorEvent): void {
|
|
65
|
-
const ts = new Date().toISOString().slice(11, 23)
|
|
66
|
-
|
|
67
|
-
switch (event.type) {
|
|
68
|
-
case 'task_start': {
|
|
69
|
-
taskTimes.set(event.task ?? '', Date.now())
|
|
70
|
-
const task = event.data as Task | undefined
|
|
71
|
-
console.log(`[${ts}] TASK READY "${task?.title ?? event.task}" (assignee: ${task?.assignee ?? 'any'})`)
|
|
72
|
-
break
|
|
73
|
-
}
|
|
74
|
-
case 'task_complete': {
|
|
75
|
-
const elapsed = Date.now() - (taskTimes.get(event.task ?? '') ?? Date.now())
|
|
76
|
-
const task = event.data as Task | undefined
|
|
77
|
-
console.log(`[${ts}] TASK DONE "${task?.title ?? event.task}" in ${elapsed}ms`)
|
|
78
|
-
break
|
|
79
|
-
}
|
|
80
|
-
case 'agent_start':
|
|
81
|
-
console.log(`[${ts}] AGENT START ${event.agent}`)
|
|
82
|
-
break
|
|
83
|
-
case 'agent_complete':
|
|
84
|
-
console.log(`[${ts}] AGENT DONE ${event.agent}`)
|
|
85
|
-
break
|
|
86
|
-
case 'error': {
|
|
87
|
-
const task = event.data as Task | undefined
|
|
88
|
-
console.error(`[${ts}] ERROR ${event.agent ?? ''} task="${task?.title ?? event.task}"`)
|
|
89
|
-
break
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ---------------------------------------------------------------------------
|
|
95
|
-
// Build the pipeline
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
|
|
98
|
-
const orchestrator = new OpenMultiAgent({
|
|
99
|
-
defaultModel: 'claude-sonnet-4-6',
|
|
100
|
-
maxConcurrency: 2, // allow test + review to potentially run in parallel later
|
|
101
|
-
onProgress: handleProgress,
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
const team = orchestrator.createTeam('pipeline-team', {
|
|
105
|
-
name: 'pipeline-team',
|
|
106
|
-
agents: [designer, implementer, tester, reviewer],
|
|
107
|
-
sharedMemory: true,
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
// Task IDs — use stable strings so dependsOn can reference them
|
|
111
|
-
// (IDs will be generated by the framework; we capture the returned Task objects)
|
|
112
|
-
const SPEC_FILE = '/tmp/pipeline-output/design-spec.md'
|
|
113
|
-
|
|
114
|
-
const tasks: Array<{
|
|
115
|
-
title: string
|
|
116
|
-
description: string
|
|
117
|
-
assignee?: string
|
|
118
|
-
dependsOn?: string[]
|
|
119
|
-
}> = [
|
|
120
|
-
{
|
|
121
|
-
title: 'Design: URL shortener data model',
|
|
122
|
-
description: `Design a minimal in-memory URL shortener service.
|
|
123
|
-
Write a markdown spec to ${SPEC_FILE} covering:
|
|
124
|
-
- TypeScript interfaces for Url and ShortenRequest
|
|
125
|
-
- The shortening algorithm (hash approach is fine)
|
|
126
|
-
- API contract: POST /shorten, GET /:code
|
|
127
|
-
Keep the spec under 30 lines.`,
|
|
128
|
-
assignee: 'designer',
|
|
129
|
-
// no dependencies — this is the root task
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
title: 'Implement: URL shortener',
|
|
133
|
-
description: `Read the design spec at ${SPEC_FILE}.
|
|
134
|
-
Implement the URL shortener in /tmp/pipeline-output/src/:
|
|
135
|
-
- shortener.ts: core logic (shorten, resolve functions)
|
|
136
|
-
- server.ts: tiny HTTP server using Node's built-in http module (no Express)
|
|
137
|
-
- POST /shorten body: { url: string } → { code: string, short: string }
|
|
138
|
-
- GET /:code → redirect (301) or 404
|
|
139
|
-
- index.ts: entry point that starts the server on port 3002
|
|
140
|
-
No external dependencies beyond Node built-ins.`,
|
|
141
|
-
assignee: 'implementer',
|
|
142
|
-
dependsOn: ['Design: URL shortener data model'],
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
title: 'Test: URL shortener',
|
|
146
|
-
description: `Run the URL shortener implementation:
|
|
147
|
-
1. Start the server: node /tmp/pipeline-output/src/index.ts (or tsx)
|
|
148
|
-
2. POST a URL to shorten it using curl
|
|
149
|
-
3. Verify the GET redirect works
|
|
150
|
-
4. Report what passed and what (if anything) failed.
|
|
151
|
-
Kill the server after testing.`,
|
|
152
|
-
assignee: 'tester',
|
|
153
|
-
dependsOn: ['Implement: URL shortener'],
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
title: 'Review: URL shortener',
|
|
157
|
-
description: `Read all .ts files in /tmp/pipeline-output/src/ and the design spec.
|
|
158
|
-
Produce a structured code review with sections:
|
|
159
|
-
- Summary (2 sentences)
|
|
160
|
-
- Strengths (bullet list)
|
|
161
|
-
- Issues (bullet list, or "None" if clean)
|
|
162
|
-
- Verdict: SHIP or NEEDS WORK`,
|
|
163
|
-
assignee: 'reviewer',
|
|
164
|
-
dependsOn: ['Implement: URL shortener'], // runs in parallel with Test after Implement completes
|
|
165
|
-
},
|
|
166
|
-
]
|
|
167
|
-
|
|
168
|
-
// ---------------------------------------------------------------------------
|
|
169
|
-
// Run
|
|
170
|
-
// ---------------------------------------------------------------------------
|
|
171
|
-
|
|
172
|
-
console.log('Starting 4-stage task pipeline...\n')
|
|
173
|
-
console.log('Pipeline: design → implement → test + review (parallel)')
|
|
174
|
-
console.log('='.repeat(60))
|
|
175
|
-
|
|
176
|
-
const result = await orchestrator.runTasks(team, tasks)
|
|
177
|
-
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
|
-
// Summary
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
|
-
|
|
182
|
-
console.log('\n' + '='.repeat(60))
|
|
183
|
-
console.log('Pipeline complete.\n')
|
|
184
|
-
console.log(`Overall success: ${result.success}`)
|
|
185
|
-
console.log(`Tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`)
|
|
186
|
-
|
|
187
|
-
console.log('\nPer-agent summary:')
|
|
188
|
-
for (const [name, r] of result.agentResults) {
|
|
189
|
-
const icon = r.success ? 'OK ' : 'FAIL'
|
|
190
|
-
const toolCount = r.toolCalls.map(c => c.toolName).join(', ')
|
|
191
|
-
console.log(` [${icon}] ${name.padEnd(14)} tools used: ${toolCount || '(none)'}`)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Print the reviewer's verdict
|
|
195
|
-
const review = result.agentResults.get('reviewer')
|
|
196
|
-
if (review?.success) {
|
|
197
|
-
console.log('\nCode review:')
|
|
198
|
-
console.log('─'.repeat(60))
|
|
199
|
-
console.log(review.output)
|
|
200
|
-
console.log('─'.repeat(60))
|
|
201
|
-
}
|