@mozaik-ai/core 3.8.4 → 3.8.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 (2) hide show
  1. package/README.md +72 -106
  2. package/package.json +3 -1
package/README.md CHANGED
@@ -93,12 +93,25 @@ The environment fans every item out to every subscriber synchronously and withou
93
93
 
94
94
  ---
95
95
 
96
- ## Agent Loop Implementation
96
+ ## Reactive agent
97
97
 
98
- The easiest way to build **and control** an agent loop is to override three handlers on `BaseAgentParticipant`:
98
+ A reactive agent extends `BaseAgentParticipant` and overrides the handlers it wants to react on. Each handler is already a no-op in the base class, so only the relevant ones need bodies:
99
99
 
100
100
  ```ts
101
- export class CustomAgent extends BaseAgentParticipant {
101
+ import {
102
+ BaseAgentParticipant,
103
+ Participant,
104
+ UserMessageItem,
105
+ FunctionCallItem,
106
+ AgenticEnvironment,
107
+ ModelContext,
108
+ GenerativeModel,
109
+ InputStream,
110
+ InferenceRunner,
111
+ FunctionCallRunner,
112
+ } from "@mozaik-ai/core"
113
+
114
+ export class ReactiveAgent extends BaseAgentParticipant {
102
115
  constructor(
103
116
  inputSource: InputStream,
104
117
  inferenceRunner: InferenceRunner,
@@ -110,30 +123,48 @@ export class CustomAgent extends BaseAgentParticipant {
110
123
  super(inputSource, inferenceRunner, functionCallRunner)
111
124
  }
112
125
 
126
+ // A message from a human (or any other participant) → record it and think.
113
127
  async onMessage(message: string): Promise<void> {
114
128
  this.context.addContextItem(UserMessageItem.create(message))
115
129
  this.runInference(this.environment, this.context, this.model)
116
130
  }
117
131
 
132
+ // The agent just produced a function call → execute it.
118
133
  async onFunctionCall(item: FunctionCallItem): Promise<void> {
119
134
  this.context.addContextItem(item)
120
135
  this.executeFunctionCall(this.environment, item)
121
136
  }
122
137
 
138
+ // The tool just produced an output → feed it back and run inference again.
123
139
  async onFunctionCallOutput(item: FunctionCallOutputItem): Promise<void> {
124
140
  this.context.addContextItem(item)
125
141
  this.runInference(this.environment, this.context, this.model)
126
142
  }
143
+
144
+ // Keep the local context in sync with model-emitted reasoning and replies.
145
+ async onReasoning(item: ReasoningItem): Promise<void> {
146
+ this.context.addContextItem(item)
147
+ }
148
+
149
+ async onModelMessage(item: ModelMessageItem): Promise<void> {
150
+ this.context.addContextItem(item)
151
+ }
127
152
  }
128
153
  ```
129
154
 
155
+ Three things to note:
156
+
157
+ 1. The split between self handlers and `onExternal*` handlers means a participant can encode "act on my own outputs" separately from "observe others", without inspecting `source` by hand.
158
+ 2. The agent never `await`s its own capability calls inside the handlers — those methods are non-blocking, so the environment keeps delivering events while inference and tool execution run in the background.
159
+ 3. Behaviors compose by **reaction**, not orchestration. Add a second agent that overrides `onExternalModelMessage` and you get a critique loop. Add a `TranscriptLogger` and you get a UI stream. Neither change touches the existing participants.
160
+
130
161
  ---
131
162
 
132
163
  ## Reacting to external events
133
164
 
134
165
  Participants can listen to external events and react by overriding methods like `onMessage`, `onExternalFunctionCall`, `onExternalFunctionCallOutput`, `onExternalReasoning`, and `onExternalModelMessage`.
135
166
 
136
- ### Passive observer
167
+ ## Passive observer
137
168
 
138
169
  You can create observers that don't run inference themselves but watch what's happening in the conversation and take side actions (logging, metrics, persistence, etc.). Subclass `Participant` and override only the handlers you care about:
139
170
 
@@ -169,71 +200,6 @@ export class TranscriptLogger extends Participant {
169
200
  }
170
201
  ```
171
202
 
172
- ### Reactive agent
173
-
174
- A reactive agent extends `BaseAgentParticipant` and overrides the handlers it wants to react on. Each handler is already a no-op in the base class, so only the relevant ones need bodies:
175
-
176
- ```ts
177
- import {
178
- BaseAgentParticipant,
179
- Participant,
180
- UserMessageItem,
181
- FunctionCallItem,
182
- AgenticEnvironment,
183
- ModelContext,
184
- GenerativeModel,
185
- InputStream,
186
- InferenceRunner,
187
- FunctionCallRunner,
188
- } from "@mozaik-ai/core"
189
-
190
- export class ReactiveAgent extends BaseAgentParticipant {
191
- constructor(
192
- inputSource: InputStream,
193
- inferenceRunner: InferenceRunner,
194
- functionCallRunner: FunctionCallRunner,
195
- private readonly environment: AgenticEnvironment,
196
- private readonly context: ModelContext,
197
- private readonly model: GenerativeModel,
198
- ) {
199
- super(inputSource, inferenceRunner, functionCallRunner)
200
- }
201
-
202
- // A message from a human (or any other participant) → record it and think.
203
- async onMessage(message: string): Promise<void> {
204
- this.context.addContextItem(UserMessageItem.create(message))
205
- this.runInference(this.environment, this.context, this.model)
206
- }
207
-
208
- // The agent just produced a function call → execute it.
209
- async onFunctionCall(item: FunctionCallItem): Promise<void> {
210
- this.context.addContextItem(item)
211
- this.executeFunctionCall(this.environment, item)
212
- }
213
-
214
- // The tool just produced an output → feed it back and run inference again.
215
- async onFunctionCallOutput(item: FunctionCallOutputItem): Promise<void> {
216
- this.context.addContextItem(item)
217
- this.runInference(this.environment, this.context, this.model)
218
- }
219
-
220
- // Keep the local context in sync with model-emitted reasoning and replies.
221
- async onReasoning(item: ReasoningItem): Promise<void> {
222
- this.context.addContextItem(item)
223
- }
224
-
225
- async onModelMessage(item: ModelMessageItem): Promise<void> {
226
- this.context.addContextItem(item)
227
- }
228
- }
229
- ```
230
-
231
- Three things to note:
232
-
233
- 1. The split between self handlers and `onExternal*` handlers means a participant can encode "act on my own outputs" separately from "observe others", without inspecting `source` by hand.
234
- 2. The agent never `await`s its own capability calls inside the handlers — those methods are non-blocking, so the environment keeps delivering events while inference and tool execution run in the background.
235
- 3. Behaviors compose by **reaction**, not orchestration. Add a second agent that overrides `onExternalModelMessage` and you get a critique loop. Add a `TranscriptLogger` and you get a UI stream. Neither change touches the existing participants.
236
-
237
203
  ---
238
204
 
239
205
  ## Context and models (reference)
@@ -264,51 +230,28 @@ const response = await runtime.infer(new InferenceRequest(new Gpt54(), context))
264
230
 
265
231
  ---
266
232
 
267
- ## Advanced: overriding generators
233
+ ## Overriding Generators
268
234
 
269
- `BaseAgentParticipant` and `BaseHumanParticipant` are deliberately thin shells around three generator interfaces. Swap any of them to change _how_ events are produced without touching the environment, the participants, or any consumers.
235
+ Mozaik uses generators for inference, function calls, and message streams — that's what lets the system emit events incrementally so participants can react to them as they arrive. Swap any generator to change _how_ events are produced.
270
236
 
271
237
  ### Custom `InputStream`
272
238
 
273
- An `InputStream` is the minimal text-source contract a participant uses to feed messages into the environment:
274
-
275
- ```ts
276
- export interface InputStream {
277
- stream(signal?: AbortSignal): AsyncIterable<string>
278
- }
279
- ```
280
-
281
- Each yielded string is delivered to every other participant through `onMessage(message: string)`. This keeps the wire format dead simple: participants exchange text, and each one decides how to turn that text into a `ContextItem` for its own `ModelContext`.
282
-
283
239
  ```ts
284
240
  import { InputStream } from "@mozaik-ai/core"
285
241
 
286
- export class QueueInputStream implements InputStream {
287
- private readonly queue: string[] = []
288
- private resolveNext?: () => void
289
-
290
- push(message: string) {
291
- this.queue.push(message)
292
- this.resolveNext?.()
293
- this.resolveNext = undefined
294
- }
295
-
296
- async *stream(signal?: AbortSignal): AsyncIterable<string> {
297
- while (!signal?.aborted) {
298
- while (this.queue.length > 0) {
299
- yield this.queue.shift()!
300
- }
301
- await new Promise<void>((resolve) => (this.resolveNext = resolve))
302
- }
242
+ export class HelloInputStream implements InputStream {
243
+ async *stream(): AsyncIterable<string> {
244
+ yield "Hello"
245
+ yield "World"
303
246
  }
304
247
  }
305
248
  ```
306
249
 
307
- Use it for stdin, websockets, an HTTP queue, or anything that produces text over time. A reactive agent typically wraps the incoming string in a `UserMessageItem` inside `onMessage` before adding it to its `ModelContext` — see the [Reactive agent](#reactive-agent) example above.
250
+ Each yielded string is delivered to other participants through `onMessage(message: string)`.
308
251
 
309
252
  ### Custom `InferenceRunner`
310
253
 
311
- Wrap any model runtime — including `OpenAIResponses` — and decide how its output becomes a stream of items. Here we expand a single `InferenceResponse` into per-item delivery:
254
+ An `InferenceRunner` can yield `ReasoningItem`, `FunctionCallItem`, and `ModelMessageItem`.
312
255
 
313
256
  ```ts
314
257
  import {
@@ -336,11 +279,9 @@ export class OpenAIInferenceRunner implements InferenceRunner {
336
279
  }
337
280
  ```
338
281
 
339
- Replace the body with a streaming runtime and items will flow into the environment as soon as the model produces them.
340
-
341
282
  ### Custom `FunctionCallRunner`
342
283
 
343
- Resolve a `FunctionCallItem` against a tool registry and yield its output:
284
+ A `FunctionCallRunner` can only produce `FunctionCallOutputItem`.
344
285
 
345
286
  ```ts
346
287
  import { FunctionCallRunner, FunctionCallItem, FunctionCallOutputItem, Tool } from "@mozaik-ai/core"
@@ -364,7 +305,7 @@ export class ToolRegistryFunctionCallRunner implements FunctionCallRunner {
364
305
  import { BaseAgentParticipant, AgenticEnvironment } from "@mozaik-ai/core"
365
306
 
366
307
  const agent = new BaseAgentParticipant(
367
- new QueueInputStream(),
308
+ new HelloInputStream(),
368
309
  new OpenAIInferenceRunner(),
369
310
  new ToolRegistryFunctionCallRunner(tools),
370
311
  )
@@ -372,16 +313,41 @@ const agent = new BaseAgentParticipant(
372
313
  agent.join(new AgenticEnvironment())
373
314
  ```
374
315
 
375
- You now own input, inference, and tool execution end-to-end while keeping the same `Participant` contract — and any other participant in the environment can still observe and react to everything the agent emits.
376
-
377
316
  ---
378
317
 
379
318
  ## Made with Mozaik
380
319
 
381
320
  - **[baro](https://github.com/Lotus015/baro)** — a Claude agent orchestrator where ten specialized participants (planner, executors, reviewer, fixer, librarian, auditor, and more) work fully concurrently on the same goal, like a team collaborating in real time instead of a single agent doing everything alone.
382
321
 
322
+ ```mermaid
323
+ flowchart LR
324
+ Conductor[Conductor] -->|"RunStart / LevelCompute / StorySpawn"| Bus(("Mozaik Bus"))
325
+ Factory[StoryFactory] -->|"spawn StoryAgent"| Bus
326
+ Story[StoryAgent] -->|"StoryResult / retries"| Bus
327
+ Librarian[Librarian] -->|"index exploration outputs"| Bus
328
+ Sentry[Sentry] -->|"flag file conflicts"| Bus
329
+ Critic[Critic] -->|"per-turn verdict"| Bus
330
+ Surgeon[Surgeon] -->|"emit ReplanItem"| Bus
331
+ Operator[Operator] -->|"bridge TUI commands"| Bus
332
+ Auditor[Auditor] -->|"write JSONL log"| Bus
333
+ Cartographer[Cartographer] -->|"emit UI frames"| Bus
334
+ Bus -->|"StorySpawnRequest"| Factory
335
+ Bus -->|"StoryResult / LevelCompleted"| Conductor
336
+ Bus -->|"tool calls"| Librarian
337
+ Bus -->|"Edit/Write calls"| Sentry
338
+ Bus -->|"agent turns"| Critic
339
+ Bus -->|"terminal failure"| Surgeon
340
+ Bus -->|"user input"| Operator
341
+ Bus -->|"all events"| Auditor
342
+ Bus -->|"all events"| Cartographer
343
+ ```
344
+
383
345
  ---
384
346
 
347
+ ## Contributing
348
+
349
+ Contributions are welcome. Please read the [Contributing Guidelines](./CONTRIBUTING.md) before opening an issue or pull request.
350
+
385
351
  ## Author & License
386
352
 
387
353
  Created by the [JigJoy](https://jigjoy.io) team.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mozaik-ai/core",
3
- "version": "3.8.4",
3
+ "version": "3.8.6",
4
4
  "description": "A TypeScript framework for building reactive agents",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -36,11 +36,13 @@
36
36
  "keywords": [
37
37
  "agentic-environment",
38
38
  "reactive-agents",
39
+ "collaborative-agents",
39
40
  "non-blocking agents",
40
41
  "event-driven agents",
41
42
  "ai-infrastructure",
42
43
  "ai-developer-tools",
43
44
  "context-management",
45
+ "agent-swarms",
44
46
  "llm-framework",
45
47
  "openai",
46
48
  "typescript framework"