@axlsdk/axl 0.17.2 → 0.17.4

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 CHANGED
@@ -309,11 +309,21 @@ for await (const event of stream) {
309
309
  if (event.type === 'done') console.log('Result:', event.data.result);
310
310
  }
311
311
 
312
- // Sessions
312
+ // Sessions — multi-turn conversations with persisted history.
313
+ // `send`/`stream`/`end`/`fork` are serialized per session id within
314
+ // the runtime; concurrent calls on the same id queue FIFO. Cross-process
315
+ // locking is NOT provided — see docs/api-reference.md for details.
313
316
  const session = runtime.session('user-123');
314
317
  await session.send('my-workflow', { query: 'Hello' });
315
318
  await session.send('my-workflow', { query: 'Follow-up' });
316
319
 
320
+ // In multi-agent workflows, each committed assistant message carries
321
+ // `ChatMessage.agent` (the originating agent's name) so consumers can
322
+ // attribute history. Surfaced as a clickable badge in Studio.
323
+ const history = await session.history();
324
+ // [{ role: 'user', content: '...' },
325
+ // { role: 'assistant', content: '...', agent: 'triage' }, ...]
326
+
317
327
  // Stream a session turn
318
328
  const sessionStream = await session.stream('my-workflow', { query: 'Hello' });
319
329
  for await (const event of sessionStream) {
@@ -321,6 +331,58 @@ for await (const event of sessionStream) {
321
331
  }
322
332
  ```
323
333
 
334
+ #### Observing inside a workflow handler with `ctx.events`
335
+
336
+ To observe events between `ctx.ask()` calls — e.g., streaming `partial_object` snapshots to a UI as a multi-step structured-output workflow runs — read `ctx.events`. Same `AxlEvent` union and curated views (`.text`, `.lifecycle`, `.textByAsk`, `.partialObjects`) as `AxlStream`, scoped to the current context (and its children — agent-as-tool nested asks bubble up).
337
+
338
+ ```typescript
339
+ // Schemas + agents the example references — declared so the snippet
340
+ // compiles standalone.
341
+ const outlineSchema = z.object({ outline: z.array(z.string()) });
342
+ const draftSchema = z.object({ draft: z.string() });
343
+ const planner = agent({ model: 'openai:gpt-4o', system: 'Plan an outline.' });
344
+ const writer = agent({ model: 'openai:gpt-4o', system: 'Write a draft.' });
345
+
346
+ const wf = workflow({
347
+ name: 'two-step',
348
+ input: z.object({ topic: z.string() }),
349
+ handler: async (ctx) => {
350
+ // Allocate the bus first — `ctx.events` is a lazy getter; the
351
+ // streaming code path inside ctx.ask() only activates when an
352
+ // observer was present at the time the ask started. Touching the
353
+ // getter synchronously here wires every ask in this handler.
354
+ // Defensive `void ctx.events;` (or `const events = ctx.events;`) is
355
+ // the unambiguous pattern — relying on the IIFE alone is correct
356
+ // but subtle, since the synchronous `for await (...ctx.events.partialObjects)`
357
+ // expression evaluates the getter before the first suspension.
358
+ const events = ctx.events;
359
+ // Background observer. The IIFE returns a promise; attaching a `.catch`
360
+ // ensures consumer errors surface in your logs instead of being
361
+ // swallowed by the unhandled-rejection handler. The bus
362
+ // auto-finishes on `workflow_end` / `error`, so the iterator
363
+ // terminates with the run.
364
+ void (async () => {
365
+ for await (const partial of events.partialObjects) {
366
+ // .partialObjects is the coalescing view: yields the LATEST
367
+ // payload per askId, with the 1-indexed `attempt` (UIs can flag
368
+ // a regenerating draft when it bumps to 2). Memory bounded by
369
+ // O(active asks), not O(events). Designed for
370
+ // streaming-structured-output UIs.
371
+ console.log(`[ask ${partial.askId} attempt ${partial.attempt}]`, partial.object);
372
+ }
373
+ })().catch((err) => ctx.log('observer.failed', { error: String(err) }));
374
+
375
+ const outline = await ctx.ask(planner, ctx.input.topic, { schema: outlineSchema });
376
+ // ctx.ask's second arg is a string prompt — serialize the
377
+ // structured outline for the next agent.
378
+ const draft = await ctx.ask(writer, JSON.stringify(outline), { schema: draftSchema });
379
+ return draft;
380
+ },
381
+ });
382
+ ```
383
+
384
+ Subscribe before the first `ctx.ask()` — the streaming code path inside `ctx.ask()` only activates when an observer is present at the time the ask starts. The bus auto-terminates on `workflow_end` / `error` (and on signal abort, for ad-hoc `runtime.createContext({ signal })` flows). Configure the iterator-queue cap and overflow policy via `runtime.execute(..., { events: { maxQueued, onOverflow } })` — defaults (`maxQueued: 10_000`, `onOverflow: 'drop-oldest-non-terminal'`) are a default-on safety net against slow consumers. See [`docs/observability.md`](../../docs/observability.md#observation-paths) for the full Observation paths comparison, [`docs/api-reference.md`](../../docs/api-reference.md#ctxevents) for the type table, and [`docs/migration/stream-first-observation.md`](../../docs/migration/stream-first-observation.md) for upgrade notes.
385
+
324
386
  ### Context Primitives
325
387
 
326
388
  All available on `ctx` inside workflow handlers. See the [API Reference](../../docs/api-reference.md) for complete option types, valid values, and defaults.