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

PropertyTypeDescription
requestIdstringUnique request identifier
timestampDateRequest timestamp
buyerBuyerContextBuyer agent information
sessionSessionContextCurrent session state
traceTraceContextDistributed 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

OptionTypeDescription
topicstringPub/Sub topic (default: agent-events)
delaystringDelay before delivery (e.g., '5m', '1h', '7d')
attributesobjectCustom 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.