@jackchen_me/open-multi-agent 0.1.0

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -0
  3. package/dist/agent/agent.d.ts +121 -0
  4. package/dist/agent/agent.d.ts.map +1 -0
  5. package/dist/agent/agent.js +294 -0
  6. package/dist/agent/agent.js.map +1 -0
  7. package/dist/agent/pool.d.ts +128 -0
  8. package/dist/agent/pool.d.ts.map +1 -0
  9. package/dist/agent/pool.js +236 -0
  10. package/dist/agent/pool.js.map +1 -0
  11. package/dist/agent/runner.d.ts +120 -0
  12. package/dist/agent/runner.d.ts.map +1 -0
  13. package/dist/agent/runner.js +274 -0
  14. package/dist/agent/runner.js.map +1 -0
  15. package/dist/index.d.ts +73 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +87 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/llm/adapter.d.ts +38 -0
  20. package/dist/llm/adapter.d.ts.map +1 -0
  21. package/dist/llm/adapter.js +46 -0
  22. package/dist/llm/adapter.js.map +1 -0
  23. package/dist/llm/anthropic.d.ts +56 -0
  24. package/dist/llm/anthropic.d.ts.map +1 -0
  25. package/dist/llm/anthropic.js +307 -0
  26. package/dist/llm/anthropic.js.map +1 -0
  27. package/dist/llm/openai.d.ts +62 -0
  28. package/dist/llm/openai.d.ts.map +1 -0
  29. package/dist/llm/openai.js +424 -0
  30. package/dist/llm/openai.js.map +1 -0
  31. package/dist/memory/shared.d.ts +86 -0
  32. package/dist/memory/shared.d.ts.map +1 -0
  33. package/dist/memory/shared.js +155 -0
  34. package/dist/memory/shared.js.map +1 -0
  35. package/dist/memory/store.d.ts +64 -0
  36. package/dist/memory/store.d.ts.map +1 -0
  37. package/dist/memory/store.js +103 -0
  38. package/dist/memory/store.js.map +1 -0
  39. package/dist/orchestrator/orchestrator.d.ts +173 -0
  40. package/dist/orchestrator/orchestrator.d.ts.map +1 -0
  41. package/dist/orchestrator/orchestrator.js +698 -0
  42. package/dist/orchestrator/orchestrator.js.map +1 -0
  43. package/dist/orchestrator/scheduler.d.ts +112 -0
  44. package/dist/orchestrator/scheduler.d.ts.map +1 -0
  45. package/dist/orchestrator/scheduler.js +282 -0
  46. package/dist/orchestrator/scheduler.js.map +1 -0
  47. package/dist/task/queue.d.ts +160 -0
  48. package/dist/task/queue.d.ts.map +1 -0
  49. package/dist/task/queue.js +337 -0
  50. package/dist/task/queue.js.map +1 -0
  51. package/dist/task/task.d.ts +86 -0
  52. package/dist/task/task.d.ts.map +1 -0
  53. package/dist/task/task.js +201 -0
  54. package/dist/task/task.js.map +1 -0
  55. package/dist/team/messaging.d.ts +106 -0
  56. package/dist/team/messaging.d.ts.map +1 -0
  57. package/dist/team/messaging.js +182 -0
  58. package/dist/team/messaging.js.map +1 -0
  59. package/dist/team/team.d.ts +141 -0
  60. package/dist/team/team.d.ts.map +1 -0
  61. package/dist/team/team.js +282 -0
  62. package/dist/team/team.js.map +1 -0
  63. package/dist/tool/built-in/bash.d.ts +12 -0
  64. package/dist/tool/built-in/bash.d.ts.map +1 -0
  65. package/dist/tool/built-in/bash.js +133 -0
  66. package/dist/tool/built-in/bash.js.map +1 -0
  67. package/dist/tool/built-in/file-edit.d.ts +14 -0
  68. package/dist/tool/built-in/file-edit.d.ts.map +1 -0
  69. package/dist/tool/built-in/file-edit.js +130 -0
  70. package/dist/tool/built-in/file-edit.js.map +1 -0
  71. package/dist/tool/built-in/file-read.d.ts +12 -0
  72. package/dist/tool/built-in/file-read.d.ts.map +1 -0
  73. package/dist/tool/built-in/file-read.js +82 -0
  74. package/dist/tool/built-in/file-read.js.map +1 -0
  75. package/dist/tool/built-in/file-write.d.ts +11 -0
  76. package/dist/tool/built-in/file-write.d.ts.map +1 -0
  77. package/dist/tool/built-in/file-write.js +70 -0
  78. package/dist/tool/built-in/file-write.js.map +1 -0
  79. package/dist/tool/built-in/grep.d.ts +15 -0
  80. package/dist/tool/built-in/grep.d.ts.map +1 -0
  81. package/dist/tool/built-in/grep.js +287 -0
  82. package/dist/tool/built-in/grep.js.map +1 -0
  83. package/dist/tool/built-in/index.d.ts +36 -0
  84. package/dist/tool/built-in/index.d.ts.map +1 -0
  85. package/dist/tool/built-in/index.js +45 -0
  86. package/dist/tool/built-in/index.js.map +1 -0
  87. package/dist/tool/executor.d.ts +71 -0
  88. package/dist/tool/executor.d.ts.map +1 -0
  89. package/dist/tool/executor.js +116 -0
  90. package/dist/tool/executor.js.map +1 -0
  91. package/dist/tool/framework.d.ts +143 -0
  92. package/dist/tool/framework.d.ts.map +1 -0
  93. package/dist/tool/framework.js +371 -0
  94. package/dist/tool/framework.js.map +1 -0
  95. package/dist/types.d.ts +285 -0
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +8 -0
  98. package/dist/types.js.map +1 -0
  99. package/dist/utils/semaphore.d.ts +47 -0
  100. package/dist/utils/semaphore.d.ts.map +1 -0
  101. package/dist/utils/semaphore.js +85 -0
  102. package/dist/utils/semaphore.js.map +1 -0
  103. package/examples/01-single-agent.ts +131 -0
  104. package/examples/02-team-collaboration.ts +167 -0
  105. package/examples/03-task-pipeline.ts +201 -0
  106. package/examples/04-multi-model-team.ts +261 -0
  107. package/package.json +49 -0
  108. package/src/agent/agent.ts +364 -0
  109. package/src/agent/pool.ts +278 -0
  110. package/src/agent/runner.ts +413 -0
  111. package/src/index.ts +166 -0
  112. package/src/llm/adapter.ts +74 -0
  113. package/src/llm/anthropic.ts +388 -0
  114. package/src/llm/openai.ts +522 -0
  115. package/src/memory/shared.ts +181 -0
  116. package/src/memory/store.ts +124 -0
  117. package/src/orchestrator/orchestrator.ts +851 -0
  118. package/src/orchestrator/scheduler.ts +352 -0
  119. package/src/task/queue.ts +394 -0
  120. package/src/task/task.ts +232 -0
  121. package/src/team/messaging.ts +230 -0
  122. package/src/team/team.ts +334 -0
  123. package/src/tool/built-in/bash.ts +187 -0
  124. package/src/tool/built-in/file-edit.ts +154 -0
  125. package/src/tool/built-in/file-read.ts +105 -0
  126. package/src/tool/built-in/file-write.ts +81 -0
  127. package/src/tool/built-in/grep.ts +362 -0
  128. package/src/tool/built-in/index.ts +50 -0
  129. package/src/tool/executor.ts +178 -0
  130. package/src/tool/framework.ts +557 -0
  131. package/src/types.ts +362 -0
  132. package/src/utils/semaphore.ts +89 -0
  133. package/tsconfig.json +25 -0
@@ -0,0 +1,285 @@
1
+ /**
2
+ * @fileoverview Core type definitions for the open-multi-agent orchestration framework.
3
+ *
4
+ * All public types are exported from this single module. Downstream modules
5
+ * import only what they need, keeping the dependency graph acyclic.
6
+ */
7
+ import type { ZodSchema } from 'zod';
8
+ /** Plain-text content produced by a model or supplied by the user. */
9
+ export interface TextBlock {
10
+ readonly type: 'text';
11
+ readonly text: string;
12
+ }
13
+ /**
14
+ * A request by the model to invoke a named tool with a structured input.
15
+ * The `id` is unique per turn and is referenced by {@link ToolResultBlock}.
16
+ */
17
+ export interface ToolUseBlock {
18
+ readonly type: 'tool_use';
19
+ readonly id: string;
20
+ readonly name: string;
21
+ readonly input: Record<string, unknown>;
22
+ }
23
+ /**
24
+ * The result of executing a tool, keyed back to the originating
25
+ * {@link ToolUseBlock} via `tool_use_id`.
26
+ */
27
+ export interface ToolResultBlock {
28
+ readonly type: 'tool_result';
29
+ readonly tool_use_id: string;
30
+ readonly content: string;
31
+ readonly is_error?: boolean;
32
+ }
33
+ /** A base64-encoded image passed to or returned from a model. */
34
+ export interface ImageBlock {
35
+ readonly type: 'image';
36
+ readonly source: {
37
+ readonly type: 'base64';
38
+ readonly media_type: string;
39
+ readonly data: string;
40
+ };
41
+ }
42
+ /** Union of all content block variants that may appear in a message. */
43
+ export type ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | ImageBlock;
44
+ /**
45
+ * A single message in a conversation thread.
46
+ * System messages are passed separately via {@link LLMChatOptions.systemPrompt}.
47
+ */
48
+ export interface LLMMessage {
49
+ readonly role: 'user' | 'assistant';
50
+ readonly content: ContentBlock[];
51
+ }
52
+ /** Token accounting for a single API call. */
53
+ export interface TokenUsage {
54
+ readonly input_tokens: number;
55
+ readonly output_tokens: number;
56
+ }
57
+ /** Normalised response returned by every {@link LLMAdapter} implementation. */
58
+ export interface LLMResponse {
59
+ readonly id: string;
60
+ readonly content: ContentBlock[];
61
+ readonly model: string;
62
+ readonly stop_reason: string;
63
+ readonly usage: TokenUsage;
64
+ }
65
+ /**
66
+ * A discrete event emitted during streaming generation.
67
+ *
68
+ * - `text` — incremental text delta
69
+ * - `tool_use` — the model has begun or completed a tool-use block
70
+ * - `tool_result` — a tool result has been appended to the stream
71
+ * - `done` — the stream has ended; `data` is the final {@link LLMResponse}
72
+ * - `error` — an unrecoverable error occurred; `data` is an `Error`
73
+ */
74
+ export interface StreamEvent {
75
+ readonly type: 'text' | 'tool_use' | 'tool_result' | 'done' | 'error';
76
+ readonly data: unknown;
77
+ }
78
+ /** The serialisable tool schema sent to the LLM provider. */
79
+ export interface LLMToolDef {
80
+ readonly name: string;
81
+ readonly description: string;
82
+ /** JSON Schema object describing the tool's `input` parameter. */
83
+ readonly inputSchema: Record<string, unknown>;
84
+ }
85
+ /**
86
+ * Context injected into every tool execution.
87
+ *
88
+ * Both `abortSignal` and `abortController` are provided so that tools and the
89
+ * executor can choose the most ergonomic API for their use-case:
90
+ *
91
+ * - Long-running shell commands that need to kill a child process can use
92
+ * `abortController.signal` directly.
93
+ * - Simple cancellation checks can read `abortSignal?.aborted`.
94
+ *
95
+ * When constructing a context, set `abortController` and derive `abortSignal`
96
+ * from it, or provide both independently.
97
+ */
98
+ export interface ToolUseContext {
99
+ /** High-level description of the agent invoking this tool. */
100
+ readonly agent: AgentInfo;
101
+ /** Team context, present when the tool runs inside a multi-agent team. */
102
+ readonly team?: TeamInfo;
103
+ /**
104
+ * Convenience reference to the abort signal.
105
+ * Equivalent to `abortController?.signal` when an `abortController` is set.
106
+ */
107
+ readonly abortSignal?: AbortSignal;
108
+ /**
109
+ * Full abort controller, available when the caller needs to inspect or
110
+ * programmatically abort the signal.
111
+ * Tools should prefer `abortSignal` for simple cancellation checks.
112
+ */
113
+ readonly abortController?: AbortController;
114
+ /** Working directory hint for file-system tools. */
115
+ readonly cwd?: string;
116
+ /** Arbitrary caller-supplied metadata (session ID, request ID, etc.). */
117
+ readonly metadata?: Readonly<Record<string, unknown>>;
118
+ }
119
+ /** Minimal descriptor for the agent that is invoking a tool. */
120
+ export interface AgentInfo {
121
+ readonly name: string;
122
+ readonly role: string;
123
+ readonly model: string;
124
+ }
125
+ /** Descriptor for a team of agents with shared memory. */
126
+ export interface TeamInfo {
127
+ readonly name: string;
128
+ readonly agents: readonly string[];
129
+ readonly sharedMemory: MemoryStore;
130
+ }
131
+ /** Value returned by a tool's `execute` function. */
132
+ export interface ToolResult {
133
+ readonly data: string;
134
+ readonly isError?: boolean;
135
+ }
136
+ /**
137
+ * A tool registered with the framework.
138
+ *
139
+ * `inputSchema` is a Zod schema used for validation before `execute` is called.
140
+ * At API call time it is converted to JSON Schema via {@link LLMToolDef}.
141
+ */
142
+ export interface ToolDefinition<TInput = Record<string, unknown>> {
143
+ readonly name: string;
144
+ readonly description: string;
145
+ readonly inputSchema: ZodSchema<TInput>;
146
+ execute(input: TInput, context: ToolUseContext): Promise<ToolResult>;
147
+ }
148
+ /** Static configuration for a single agent. */
149
+ export interface AgentConfig {
150
+ readonly name: string;
151
+ readonly model: string;
152
+ readonly provider?: 'anthropic' | 'openai';
153
+ readonly systemPrompt?: string;
154
+ /** Names of tools (from the tool registry) available to this agent. */
155
+ readonly tools?: readonly string[];
156
+ readonly maxTurns?: number;
157
+ readonly maxTokens?: number;
158
+ readonly temperature?: number;
159
+ }
160
+ /** Lifecycle state tracked during an agent run. */
161
+ export interface AgentState {
162
+ status: 'idle' | 'running' | 'completed' | 'error';
163
+ messages: LLMMessage[];
164
+ tokenUsage: TokenUsage;
165
+ error?: Error;
166
+ }
167
+ /** A single recorded tool invocation within a run. */
168
+ export interface ToolCallRecord {
169
+ readonly toolName: string;
170
+ readonly input: Record<string, unknown>;
171
+ readonly output: string;
172
+ /** Wall-clock duration in milliseconds. */
173
+ readonly duration: number;
174
+ }
175
+ /** The final result produced when an agent run completes (or fails). */
176
+ export interface AgentRunResult {
177
+ readonly success: boolean;
178
+ readonly output: string;
179
+ readonly messages: LLMMessage[];
180
+ readonly tokenUsage: TokenUsage;
181
+ readonly toolCalls: ToolCallRecord[];
182
+ }
183
+ /** Static configuration for a team of cooperating agents. */
184
+ export interface TeamConfig {
185
+ readonly name: string;
186
+ readonly agents: readonly AgentConfig[];
187
+ readonly sharedMemory?: boolean;
188
+ readonly maxConcurrency?: number;
189
+ }
190
+ /** Aggregated result for a full team run. */
191
+ export interface TeamRunResult {
192
+ readonly success: boolean;
193
+ /** Keyed by agent name. */
194
+ readonly agentResults: Map<string, AgentRunResult>;
195
+ readonly totalTokenUsage: TokenUsage;
196
+ }
197
+ /** Valid states for a {@link Task}. */
198
+ export type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'blocked';
199
+ /** A discrete unit of work tracked by the orchestrator. */
200
+ export interface Task {
201
+ readonly id: string;
202
+ readonly title: string;
203
+ readonly description: string;
204
+ status: TaskStatus;
205
+ /** Agent name responsible for executing this task. */
206
+ assignee?: string;
207
+ /** IDs of tasks that must complete before this one can start. */
208
+ dependsOn?: readonly string[];
209
+ result?: string;
210
+ readonly createdAt: Date;
211
+ updatedAt: Date;
212
+ }
213
+ /** Progress event emitted by the orchestrator during a run. */
214
+ export interface OrchestratorEvent {
215
+ readonly type: 'agent_start' | 'agent_complete' | 'task_start' | 'task_complete' | 'message' | 'error';
216
+ readonly agent?: string;
217
+ readonly task?: string;
218
+ readonly data?: unknown;
219
+ }
220
+ /** Top-level configuration for the orchestrator. */
221
+ export interface OrchestratorConfig {
222
+ readonly maxConcurrency?: number;
223
+ readonly defaultModel?: string;
224
+ readonly defaultProvider?: 'anthropic' | 'openai';
225
+ onProgress?: (event: OrchestratorEvent) => void;
226
+ }
227
+ /** A single key-value record stored in a {@link MemoryStore}. */
228
+ export interface MemoryEntry {
229
+ readonly key: string;
230
+ readonly value: string;
231
+ readonly metadata?: Readonly<Record<string, unknown>>;
232
+ readonly createdAt: Date;
233
+ }
234
+ /**
235
+ * Persistent (or in-memory) key-value store shared across agents.
236
+ * Implementations may be backed by Redis, SQLite, or plain objects.
237
+ */
238
+ export interface MemoryStore {
239
+ get(key: string): Promise<MemoryEntry | null>;
240
+ set(key: string, value: string, metadata?: Record<string, unknown>): Promise<void>;
241
+ list(): Promise<MemoryEntry[]>;
242
+ delete(key: string): Promise<void>;
243
+ clear(): Promise<void>;
244
+ }
245
+ /** Options shared by both chat and streaming calls. */
246
+ export interface LLMChatOptions {
247
+ readonly model: string;
248
+ readonly tools?: readonly LLMToolDef[];
249
+ readonly maxTokens?: number;
250
+ readonly temperature?: number;
251
+ readonly systemPrompt?: string;
252
+ readonly abortSignal?: AbortSignal;
253
+ }
254
+ /**
255
+ * Options for streaming calls.
256
+ * Extends {@link LLMChatOptions} without additional fields — the separation
257
+ * exists so callers can type-narrow and implementations can diverge later.
258
+ */
259
+ export interface LLMStreamOptions extends LLMChatOptions {
260
+ }
261
+ /**
262
+ * Provider-agnostic interface that every LLM backend must implement.
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * const adapter: LLMAdapter = createAdapter('anthropic')
267
+ * const response = await adapter.chat(messages, { model: 'claude-opus-4-6' })
268
+ * ```
269
+ */
270
+ export interface LLMAdapter {
271
+ /** Human-readable provider name, e.g. `'anthropic'` or `'openai'`. */
272
+ readonly name: string;
273
+ /**
274
+ * Send a chat request and return the complete response.
275
+ * Throws on non-retryable API errors.
276
+ */
277
+ chat(messages: LLMMessage[], options: LLMChatOptions): Promise<LLMResponse>;
278
+ /**
279
+ * Send a chat request and yield {@link StreamEvent}s incrementally.
280
+ * The final event in the sequence always has `type === 'done'` on success,
281
+ * or `type === 'error'` on failure.
282
+ */
283
+ stream(messages: LLMMessage[], options: LLMStreamOptions): AsyncIterable<StreamEvent>;
284
+ }
285
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @fileoverview Core type definitions for the open-multi-agent orchestration framework.
3
+ *
4
+ * All public types are exported from this single module. Downstream modules
5
+ * import only what they need, keeping the dependency graph acyclic.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @fileoverview Shared counting semaphore for concurrency control.
3
+ *
4
+ * Used by both {@link ToolExecutor} and {@link AgentPool} to cap the number of
5
+ * concurrent async operations without requiring any third-party dependencies.
6
+ *
7
+ * This is intentionally self-contained and tuned for Promise/async use —
8
+ * not a general OS-semaphore replacement.
9
+ */
10
+ /**
11
+ * Classic counting semaphore for concurrency control.
12
+ *
13
+ * `acquire()` resolves immediately if a slot is free, otherwise queues the
14
+ * caller. `release()` unblocks the next waiter in FIFO order.
15
+ *
16
+ * Node.js is single-threaded, so this is safe without atomics or mutex
17
+ * primitives — the semaphore gates concurrent async operations, not CPU threads.
18
+ */
19
+ export declare class Semaphore {
20
+ private readonly max;
21
+ private current;
22
+ private readonly queue;
23
+ /**
24
+ * @param max - Maximum number of concurrent holders. Must be >= 1.
25
+ */
26
+ constructor(max: number);
27
+ /**
28
+ * Acquire a slot. Resolves immediately when one is free, or waits until a
29
+ * holder calls `release()`.
30
+ */
31
+ acquire(): Promise<void>;
32
+ /**
33
+ * Release a previously acquired slot.
34
+ * If callers are queued, the next one is unblocked synchronously.
35
+ */
36
+ release(): void;
37
+ /**
38
+ * Run `fn` while holding one slot, automatically releasing it afterward
39
+ * even if `fn` throws.
40
+ */
41
+ run<T>(fn: () => Promise<T>): Promise<T>;
42
+ /** Number of slots currently in use. */
43
+ get active(): number;
44
+ /** Number of callers waiting for a slot. */
45
+ get pending(): number;
46
+ }
47
+ //# sourceMappingURL=semaphore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semaphore.d.ts","sourceRoot":"","sources":["../../src/utils/semaphore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;;;;;;GAQG;AACH,qBAAa,SAAS;IAOR,OAAO,CAAC,QAAQ,CAAC,GAAG;IANhC,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAE9C;;OAEG;gBAC0B,GAAG,EAAE,MAAM;IAMxC;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAWxB;;;OAGG;IACH,OAAO,IAAI,IAAI;IAWf;;;OAGG;IACG,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAS9C,wCAAwC;IACxC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,4CAA4C;IAC5C,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @fileoverview Shared counting semaphore for concurrency control.
3
+ *
4
+ * Used by both {@link ToolExecutor} and {@link AgentPool} to cap the number of
5
+ * concurrent async operations without requiring any third-party dependencies.
6
+ *
7
+ * This is intentionally self-contained and tuned for Promise/async use —
8
+ * not a general OS-semaphore replacement.
9
+ */
10
+ // ---------------------------------------------------------------------------
11
+ // Semaphore
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * Classic counting semaphore for concurrency control.
15
+ *
16
+ * `acquire()` resolves immediately if a slot is free, otherwise queues the
17
+ * caller. `release()` unblocks the next waiter in FIFO order.
18
+ *
19
+ * Node.js is single-threaded, so this is safe without atomics or mutex
20
+ * primitives — the semaphore gates concurrent async operations, not CPU threads.
21
+ */
22
+ export class Semaphore {
23
+ max;
24
+ current = 0;
25
+ queue = [];
26
+ /**
27
+ * @param max - Maximum number of concurrent holders. Must be >= 1.
28
+ */
29
+ constructor(max) {
30
+ this.max = max;
31
+ if (max < 1) {
32
+ throw new RangeError(`Semaphore max must be at least 1, got ${max}`);
33
+ }
34
+ }
35
+ /**
36
+ * Acquire a slot. Resolves immediately when one is free, or waits until a
37
+ * holder calls `release()`.
38
+ */
39
+ acquire() {
40
+ if (this.current < this.max) {
41
+ this.current++;
42
+ return Promise.resolve();
43
+ }
44
+ return new Promise(resolve => {
45
+ this.queue.push(resolve);
46
+ });
47
+ }
48
+ /**
49
+ * Release a previously acquired slot.
50
+ * If callers are queued, the next one is unblocked synchronously.
51
+ */
52
+ release() {
53
+ const next = this.queue.shift();
54
+ if (next !== undefined) {
55
+ // A queued caller is waiting — hand the slot directly to it.
56
+ // `current` stays the same: we consumed the slot immediately.
57
+ next();
58
+ }
59
+ else {
60
+ this.current--;
61
+ }
62
+ }
63
+ /**
64
+ * Run `fn` while holding one slot, automatically releasing it afterward
65
+ * even if `fn` throws.
66
+ */
67
+ async run(fn) {
68
+ await this.acquire();
69
+ try {
70
+ return await fn();
71
+ }
72
+ finally {
73
+ this.release();
74
+ }
75
+ }
76
+ /** Number of slots currently in use. */
77
+ get active() {
78
+ return this.current;
79
+ }
80
+ /** Number of callers waiting for a slot. */
81
+ get pending() {
82
+ return this.queue.length;
83
+ }
84
+ }
85
+ //# sourceMappingURL=semaphore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semaphore.js","sourceRoot":"","sources":["../../src/utils/semaphore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,OAAO,SAAS;IAOS;IANrB,OAAO,GAAG,CAAC,CAAA;IACF,KAAK,GAAsB,EAAE,CAAA;IAE9C;;OAEG;IACH,YAA6B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;QACtC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,MAAM,IAAI,UAAU,CAAC,yCAAyC,GAAG,EAAE,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,EAAE,CAAA;YACd,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;QAC1B,CAAC;QAED,OAAO,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE;YACjC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;OAGG;IACH,OAAO;QACL,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAC/B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,6DAA6D;YAC7D,8DAA8D;YAC9D,IAAI,EAAE,CAAA;QACR,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CAAI,EAAoB;QAC/B,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;QACpB,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;IACH,CAAC;IAED,wCAAwC;IACxC,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;IAED,4CAA4C;IAC5C,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;IAC1B,CAAC;CACF"}
@@ -0,0 +1,131 @@
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.')