@nolag/agents 0.1.0 → 0.1.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 (2) hide show
  1. package/README.md +355 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,355 @@
1
+ # @nolag/agents
2
+
3
+ Multi-agent coordination SDK for [NoLag](https://nolag.app) — dispatch tasks, share state, observe decisions, gate actions with human approval, and invoke remote tools across connected agents.
4
+
5
+ ## How It Works with NoLag
6
+
7
+ NoLag is a real-time messaging platform that handles WebSocket connections, message routing, persistence, and scaling. This SDK wraps the low-level [@nolag/js-sdk](https://www.npmjs.com/package/@nolag/js-sdk) and gives you a purpose-built multi-agent coordination API — task handoff, shared blackboard state, tool invocation, approval gates, and observability — without needing to manage topics or subscriptions yourself.
8
+
9
+ ### Getting Your Token
10
+
11
+ 1. Sign up at [nolag.app](https://nolag.app)
12
+ 2. Create a new **project** in the portal
13
+ 3. Choose the **Agents** blueprint when creating an app — this pre-configures the topics (`tasks`, `results`, `state`, `events`, `inbox`, `tools`, `approval`), rooms, and lobbies your agent workflow needs
14
+ 4. Go to the app's **Tokens** page and generate an **actor token** for each agent
15
+ 5. Use that token when connecting with this SDK
16
+
17
+ Each token identifies a unique agent (actor) in NoLag. The blueprint handles all the infrastructure setup — you just write your agent logic.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install @nolag/js-sdk @nolag/agents
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ import { NoLagAgents, Handoff } from "@nolag/agents";
29
+
30
+ // --- Orchestrator ---
31
+ const orchestrator = new NoLagAgents("ORCHESTRATOR_TOKEN", {
32
+ agentId: "orchestrator-1",
33
+ presence: { name: "orchestrator-1", role: "orchestrator" },
34
+ });
35
+ await orchestrator.connect();
36
+
37
+ const room = orchestrator.room("default-workflow");
38
+ const handoff = new Handoff(room);
39
+
40
+ // Dispatch a task and wait for the result
41
+ const result = await handoff.dispatch("summarize", { text: "..." }, {
42
+ waitForResult: true,
43
+ timeout: 30_000,
44
+ });
45
+ console.log("Result:", result?.payload);
46
+
47
+ // --- Worker ---
48
+ const worker = new NoLagAgents("WORKER_TOKEN", {
49
+ agentId: "worker-1",
50
+ presence: {
51
+ name: "worker-1",
52
+ role: "agent",
53
+ capabilities: ["summarize"],
54
+ },
55
+ });
56
+ await worker.connect();
57
+
58
+ const workerRoom = worker.room("default-workflow");
59
+ const workerHandoff = new Handoff(workerRoom);
60
+
61
+ workerHandoff.onTask(["summarize"], async (task, respond) => {
62
+ const summary = await summarize(task.payload.text);
63
+ respond("success", { summary });
64
+ });
65
+ ```
66
+
67
+ ## Coordination Patterns
68
+
69
+ ### Handoff — Task Dispatch & Results
70
+
71
+ Dispatch tasks to agents by capability. The SDK uses presence-based service discovery to verify a capable agent is connected before dispatching.
72
+
73
+ ```typescript
74
+ import { Handoff } from "@nolag/agents";
75
+
76
+ const handoff = new Handoff(room);
77
+
78
+ // Orchestrator: dispatch work
79
+ const result = await handoff.dispatch("translate", { text, lang: "es" }, {
80
+ waitForResult: true,
81
+ priority: "high",
82
+ });
83
+
84
+ // Worker: handle tasks
85
+ handoff.onTask(["translate"], async (task, respond) => {
86
+ const translated = await translate(task.payload.text, task.payload.lang);
87
+ respond("success", { translated });
88
+ });
89
+
90
+ // Check who can handle a capability
91
+ const agents = handoff.getCapableAgents("translate");
92
+ ```
93
+
94
+ ### Blackboard — Shared State
95
+
96
+ Read and write key-value pairs visible to all agents in the room. State is retained so new agents receive current values on join.
97
+
98
+ ```typescript
99
+ import { Blackboard } from "@nolag/agents";
100
+
101
+ const board = new Blackboard(room, agentId);
102
+
103
+ // Write state
104
+ board.set("progress", { completed: 3, total: 10 });
105
+
106
+ // Read state
107
+ const progress = board.get("progress");
108
+
109
+ // React to changes
110
+ board.onChange("progress", (envelope) => {
111
+ console.log(`Progress updated by ${envelope.updatedBy}:`, envelope.value);
112
+ });
113
+ ```
114
+
115
+ ### Tools — Remote Tool Invocation
116
+
117
+ Register tool handlers on one agent, invoke them from another. Uses correlated request/response over pub/sub.
118
+
119
+ ```typescript
120
+ import { Tools } from "@nolag/agents";
121
+
122
+ const tools = new Tools(room, agentId);
123
+
124
+ // Tool server: register handlers
125
+ tools.register("web_search", async (args) => {
126
+ return await search(args.query as string);
127
+ });
128
+
129
+ // Caller: invoke a remote tool
130
+ const response = await tools.invoke("web_search", { query: "NoLag docs" });
131
+ console.log(response.result);
132
+ ```
133
+
134
+ ### Approve — Human-in-the-Loop Gates
135
+
136
+ Request approval before taking actions. Humans or supervisor agents approve, reject, or defer.
137
+
138
+ ```typescript
139
+ import { Approve } from "@nolag/agents";
140
+
141
+ const approve = new Approve(room, agentId);
142
+
143
+ // Agent: request approval
144
+ const response = await approve.request("delete_record", { recordId: 42 }, {
145
+ urgency: "high",
146
+ timeout: 60_000,
147
+ });
148
+ if (response.decision === "approved") {
149
+ await deleteRecord(42);
150
+ }
151
+
152
+ // Human/supervisor: handle approval requests
153
+ approve.onRequest((request, respond) => {
154
+ console.log(`Action: ${request.action}`, request.context);
155
+ respond("approved", "Looks good");
156
+ });
157
+ ```
158
+
159
+ ### Inbox — Direct Agent Messaging
160
+
161
+ Send messages directly to a specific agent. Messages are addressed by agent ID.
162
+
163
+ ```typescript
164
+ import { Inbox } from "@nolag/agents";
165
+
166
+ const inbox = new Inbox(room, agentId);
167
+
168
+ // Send a direct message
169
+ inbox.send("worker-2", { instruction: "re-process item 7" });
170
+
171
+ // Receive messages
172
+ inbox.onMessage((msg) => {
173
+ console.log(`From ${msg.from}:`, msg.payload);
174
+ });
175
+ ```
176
+
177
+ ### Observe — Observability Events
178
+
179
+ Emit structured events for monitoring dashboards. Events have severity, category, and agent attribution.
180
+
181
+ ```typescript
182
+ import { Observe } from "@nolag/agents";
183
+
184
+ const observe = new Observe(room, agentId);
185
+
186
+ // Emit events
187
+ observe.emit("task.completed", { taskId: "t-1", duration: 1200 }, "info");
188
+ observe.emit("rate_limit", { service: "openai" }, "warning");
189
+
190
+ // Listen for events (with optional filters)
191
+ observe.on((event) => {
192
+ console.log(`[${event.severity}] ${event.category}:`, event.payload);
193
+ }, { severity: "warning" });
194
+ ```
195
+
196
+ ## API Reference
197
+
198
+ ### `NoLagAgents`
199
+
200
+ #### Constructor
201
+
202
+ ```typescript
203
+ const agents = new NoLagAgents(token: string, options?: NoLagAgentsOptions);
204
+ ```
205
+
206
+ **Options:**
207
+
208
+ | Option | Type | Default | Description |
209
+ |--------|------|---------|-------------|
210
+ | `appName` | `string` | `"agents"` | App slug for the agents workflow |
211
+ | `agentId` | `string` | auto-generated | Unique agent ID |
212
+ | `debug` | `boolean` | `false` | Enable debug logging |
213
+ | `rooms` | `string[]` | `["default-workflow"]` | Rooms to auto-join on connect |
214
+ | `lobby` | `string` | — | Lobby slug for cross-room presence observation |
215
+ | `presence` | `AgentPresenceData` | — | Presence data advertised to other agents |
216
+ | `clientOptions` | `Partial<NoLagOptions>` | — | Additional options passed to `@nolag/js-sdk` |
217
+
218
+ #### Methods
219
+
220
+ | Method | Returns | Description |
221
+ |--------|---------|-------------|
222
+ | `connect()` | `Promise<void>` | Connect to NoLag and join configured rooms |
223
+ | `disconnect()` | `void` | Disconnect and clean up |
224
+ | `room(name)` | `AgentRoom` | Get or create a room (auto-joins if not already joined) |
225
+ | `subscribeLobby(slug)` | `Promise<Record>` | Subscribe to a lobby for cross-room presence |
226
+
227
+ #### Properties
228
+
229
+ | Property | Type | Description |
230
+ |----------|------|-------------|
231
+ | `agentId` | `string` | The agent's unique ID |
232
+ | `connected` | `boolean` | Whether currently connected |
233
+ | `rooms` | `ReadonlyMap<string, AgentRoom>` | All joined rooms |
234
+
235
+ #### Events
236
+
237
+ | Event | Payload | Description |
238
+ |-------|---------|-------------|
239
+ | `connected` | — | Connected to NoLag |
240
+ | `disconnected` | `reason: string` | Disconnected |
241
+ | `reconnected` | — | Reconnected after disconnect |
242
+ | `error` | `Error` | Connection or protocol error |
243
+
244
+ ### `AgentRoom`
245
+
246
+ #### Methods
247
+
248
+ | Method | Returns | Description |
249
+ |--------|---------|-------------|
250
+ | `getConnectedAgents()` | `ConnectedAgent[]` | Get all connected agents |
251
+ | `findAgents(capability)` | `ConnectedAgent[]` | Find agents with a capability |
252
+ | `hasCapability(capability)` | `boolean` | Check if any agent has a capability |
253
+ | `getAvailableCapabilities()` | `string[]` | Get all capabilities across connected agents |
254
+ | `setPresence(data)` | `void` | Update this agent's presence |
255
+ | `fetchPresence()` | `Promise<ConnectedAgent[]>` | Fetch current presence snapshot |
256
+ | `publishTask(envelope)` | `void` | Publish to tasks topic |
257
+ | `publishResult(envelope)` | `void` | Publish to results topic |
258
+ | `publishState(data)` | `void` | Publish to state topic (retained) |
259
+ | `publishEvent(data)` | `void` | Publish to events topic |
260
+ | `publishInbox(data)` | `void` | Publish to inbox topic |
261
+ | `publishTools(data)` | `void` | Publish to tools topic |
262
+ | `publishApproval(data)` | `void` | Publish to approval topic (retained) |
263
+
264
+ #### Properties
265
+
266
+ | Property | Type | Description |
267
+ |----------|------|-------------|
268
+ | `name` | `string` | Room name |
269
+ | `agentId` | `string` | This agent's ID |
270
+ | `context` | `RoomContext` | Underlying `@nolag/js-sdk` room context |
271
+
272
+ #### Events
273
+
274
+ | Event | Payload | Description |
275
+ |-------|---------|-------------|
276
+ | `task` | `TaskEnvelope` | Task dispatched |
277
+ | `result` | `ResultEnvelope` | Task result received |
278
+ | `stateChange` | `StateEnvelope` | Shared state updated |
279
+ | `event` | `EventEnvelope` | Observability event |
280
+ | `inbox` | `Record<string, unknown>` | Inbox message received |
281
+ | `approvalRequest` | `ApprovalRequestEnvelope` | Approval requested |
282
+ | `approvalResponse` | `ApprovalResponseEnvelope` | Approval decision received |
283
+ | `toolRequest` | `ToolRequestEnvelope` | Tool invocation requested |
284
+ | `toolResponse` | `ToolResponseEnvelope` | Tool result received |
285
+ | `presenceJoin` | `actorId, AgentPresenceData` | Agent joined |
286
+ | `presenceLeave` | `actorId` | Agent left |
287
+ | `presenceUpdate` | `actorId, AgentPresenceData` | Agent presence updated |
288
+
289
+ ## Types
290
+
291
+ ```typescript
292
+ interface AgentPresenceData {
293
+ name: string;
294
+ role: string; // "orchestrator" | "agent" | "observer" | "human" | "tool-server"
295
+ capabilities?: string[];
296
+ metadata?: Record<string, unknown>;
297
+ }
298
+
299
+ interface ConnectedAgent {
300
+ actorId: string;
301
+ name: string;
302
+ role: string;
303
+ capabilities: string[];
304
+ metadata?: Record<string, unknown>;
305
+ connectedAt: number;
306
+ }
307
+
308
+ interface TaskEnvelope {
309
+ type: "task";
310
+ taskId: string;
311
+ correlationId: string;
312
+ capability: string;
313
+ priority: "low" | "medium" | "high" | "critical";
314
+ payload: Record<string, unknown>;
315
+ tags?: string[];
316
+ metadata?: Record<string, unknown>;
317
+ createdAt: number;
318
+ createdBy?: string;
319
+ timeout?: number;
320
+ }
321
+
322
+ interface ResultEnvelope {
323
+ type: "result";
324
+ taskId: string;
325
+ correlationId: string;
326
+ status: "success" | "error" | "partial";
327
+ payload: Record<string, unknown>;
328
+ error?: { code: string; message: string };
329
+ completedAt: number;
330
+ completedBy?: string;
331
+ }
332
+
333
+ interface StateEnvelope {
334
+ type: "state";
335
+ key: string;
336
+ value: unknown;
337
+ version: number;
338
+ updatedAt: number;
339
+ updatedBy: string;
340
+ }
341
+
342
+ interface EventEnvelope {
343
+ type: "event";
344
+ eventId: string;
345
+ severity: "debug" | "info" | "warning" | "error" | "critical";
346
+ category: string;
347
+ payload: Record<string, unknown>;
348
+ timestamp: number;
349
+ emittedBy: string;
350
+ }
351
+ ```
352
+
353
+ ## License
354
+
355
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nolag/agents",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"