Context & Events
Access request context and communicate between agents via events.
Overview
The SDK provides rich context objects for every request and a Pub/Sub-based event system for agent-to-agent communication. Context includes request metadata, session state, and buyer information. Events enable loose coupling between agents.
Request Context
Every ACP event handler receives a RequestContext object with metadata about the current request:
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { HyperfoldAgent, OnACPEvent, RequestContext } from '@hyperfold/actions-sdk'; @HyperfoldAgent({ name: 'context-aware-agent', type: 'negotiator' })export class ContextAwareAgent { @OnACPEvent('quote') async handleQuote( productId: string, offer: number, context: RequestContext // Automatically injected ) { // Request metadata console.log('Request ID:', context.requestId); console.log('Timestamp:', context.timestamp); console.log('Source IP:', context.sourceIp); // Buyer agent information console.log('Buyer Agent:', context.buyer.agent_id); console.log('Buyer Provider:', context.buyer.provider); // e.g., 'openai', 'anthropic' // Session information console.log('Session ID:', context.session.id); console.log('Session Start:', context.session.started_at); // Trace context for distributed tracing console.log('Trace ID:', context.trace.traceId); console.log('Span ID:', context.trace.spanId); // Use context in business logic const pricing = await this.calculatePrice(productId, { buyerTier: context.buyer.loyalty_tier, sessionHistory: context.session.interaction_count, }); return { price: pricing.suggested }; }}Context Properties
| Property | Type | Description |
|---|---|---|
requestId | string | Unique request identifier |
timestamp | Date | Request timestamp |
buyer | BuyerContext | Buyer agent information |
session | SessionContext | Current session state |
trace | TraceContext | Distributed tracing IDs |
Session Context
Session context provides state management across multiple interactions within a single buyer session:
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import { SessionContext } from '@hyperfold/actions-sdk'; @HyperfoldAgent({ name: 'session-agent', type: 'negotiator' })export class SessionAgent { // Session state management @OnACPEvent('search') async handleSearch(query: string, filters: any, context: RequestContext) { // Get session state const session = context.session as SessionContext; // Read previous interactions const previousSearches = await session.get('searches') || []; // Store current search await session.set('searches', [...previousSearches, query]); // Access accumulated cart const cart = await session.get('cart') || []; // Session metadata console.log('Interactions:', session.interaction_count); console.log('Duration:', session.duration_ms); return await this.search(query, filters); } // Session lifecycle async onSessionStart(session: SessionContext) { // Initialize session state await session.set('started_at', new Date().toISOString()); await session.set('cart', []); await session.set('viewed_products', []); // Load customer context if available if (session.customer_id) { const customer = await this.crm.getCustomer(session.customer_id); await session.set('customer', customer); } } async onSessionEnd(session: SessionContext, outcome: SessionOutcome) { // Log session metrics await this.analytics.logSession({ session_id: session.id, duration: session.duration_ms, interactions: session.interaction_count, outcome: outcome.status, revenue: outcome.revenue, }); // Clean up session state (optional - auto-cleaned after TTL) await session.clear(); }}Session state is automatically persisted to Firestore. Default TTL is 24 hours but can be configured per-agent.
Event Publishing
Publish events to notify other agents of important actions:
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { HyperfoldAgent, Events } from '@hyperfold/actions-sdk'; @HyperfoldAgent({ name: 'event-publisher', type: 'negotiator' })export class EventPublisher { constructor(private events: Events) {} @OnACPEvent('finalize') async handleFinalize(checkoutId: string, paymentToken: string) { // Process payment... const order = await this.createOrder(checkoutId, paymentToken); // Publish event for other agents await this.events.publish('order.created', { order_id: order.id, customer_id: order.customer_id, items: order.items, total: order.total, timestamp: new Date().toISOString(), }); // Publish with specific topic await this.events.publish('fulfillment.required', { order_id: order.id, shipping_address: order.shipping_address, items: order.items, }, { topic: 'fulfillment-events' }); // Publish with delay await this.events.publish('reminder.send', { customer_id: order.customer_id, order_id: order.id, type: 'review_request', }, { delay: '7d' }); // Send 7 days later return { order_id: order.id }; } // Publish custom business events async trackProductView(productId: string, customerId: string) { await this.events.publish('product.viewed', { product_id: productId, customer_id: customerId, timestamp: new Date().toISOString(), }); }}Publish Options
| Option | Type | Description |
|---|---|---|
topic | string | Pub/Sub topic (default: agent-events) |
delay | string | Delay before delivery (e.g., '5m', '1h', '7d') |
attributes | object | Custom Pub/Sub attributes |
Event Subscription
Subscribe to events from other agents using @OnEvent:
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import { HyperfoldAgent, OnEvent } from '@hyperfold/actions-sdk'; @HyperfoldAgent({ name: 'event-subscriber', type: 'fulfillment' })export class EventSubscriber { // Subscribe to events from other agents @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent) { console.log('New order:', event.order_id); // Start fulfillment process await this.startFulfillment(event.order_id, event.items); } @OnEvent('order.cancelled') async handleOrderCancelled(event: OrderCancelledEvent) { // Cancel pending shipments await this.cancelFulfillment(event.order_id); } // Subscribe to specific topics @OnEvent('inventory.low', { topic: 'inventory-events' }) async handleLowInventory(event: LowInventoryEvent) { await this.notifications.sendAlert({ type: 'low_inventory', product_id: event.product_id, current_quantity: event.quantity, reorder_threshold: event.threshold, }); } // Subscribe with filtering @OnEvent('order.created', { filter: (event) => event.total > 1000, // High-value orders only }) async handleHighValueOrder(event: OrderCreatedEvent) { await this.notifications.sendSlack({ channel: '#high-value-orders', message: `High-value order: $${event.total} from ${event.customer_id}`, }); } // Subscribe with retry configuration @OnEvent('payment.failed', { maxRetries: 3, retryDelay: '5m', deadLetterTopic: 'failed-payment-events', }) async handlePaymentFailed(event: PaymentFailedEvent) { // Attempt recovery... await this.payments.retryPayment(event.payment_id); }}Event handlers should be idempotent. Pub/Sub guarantees at-least-once delivery, so the same event may be delivered multiple times.
Learn about built-in tools in Tools Reference.