Files
composio-skills-reference/composio-sdk/rules/tr-session-lifecycle.md
sohamganatra b8b711dff6 Add Composio SDK skill with rules and agent config
Adds composio-sdk/ with SKILL.md, AGENTS.md, and 18 rule files
covering Tool Router, direct execution, triggers, and auth patterns.

Source: composiohq/skills

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 22:54:21 -08:00

386 lines
10 KiB
Markdown

---
title: Treat Sessions as Short-Lived and Disposable
impact: CRITICAL
description: Create new sessions frequently for better logging, debugging, and configuration management
tags: [tool-router, session, lifecycle, best-practices, logging]
---
# Treat Sessions as Short-Lived and Disposable
Tool Router sessions should be **short-lived and disposable**. Create new sessions frequently rather than caching or reusing them across different contexts.
## ❌ Incorrect
```typescript
// DON'T: Cache and reuse sessions across messages
class AgentService {
private sessionCache = new Map<string, ToolRouterSession>();
async handleMessage(userId: string, message: string) {
// BAD: Reusing cached session
let session = this.sessionCache.get(userId);
if (!session) {
session = await composio.create(userId, {
toolkits: ['gmail', 'slack']
});
this.sessionCache.set(userId, session);
}
// ❌ Configuration changes won't be reflected
// ❌ Logs mixed across different conversations
// ❌ Stale toolkit connections
const tools = await session.tools();
}
}
```
```python
# DON'T: Cache and reuse sessions across messages
class AgentService:
def __init__(self):
self.session_cache = {}
async def handle_message(self, user_id: str, message: str):
# BAD: Reusing cached session
if user_id not in self.session_cache:
session = composio.tool_router.create(
user_id=user_id,
toolkits=["gmail", "slack"]
)
self.session_cache[user_id] = session
session = self.session_cache[user_id]
# ❌ Configuration changes won't be reflected
# ❌ Logs mixed across different conversations
# ❌ Stale toolkit connections
tools = session.tools()
```
## ✅ Correct - Create New Session Per Message
```typescript
// DO: Create fresh session for each message
import { Composio } from '@composio/core';
import { VercelProvider } from '@composio/vercel';
const composio = new Composio({
provider: new VercelProvider()
});
async function handleUserMessage(
userId: string,
message: string,
config: { toolkits: string[] }
) {
// Create new session for this message
const session = await composio.create(userId, {
toolkits: config.toolkits,
manageConnections: true
});
const tools = await session.tools();
// Use tools with agent...
const response = await runAgent(message, tools);
// ✅ Fresh configuration
// ✅ Clean logs grouped by session
// ✅ Latest connection states
return response;
}
// Each message gets a new session
await handleUserMessage('user_123', 'Check my emails', { toolkits: ['gmail'] });
await handleUserMessage('user_123', 'Send a slack message', { toolkits: ['slack'] });
```
```python
# DO: Create fresh session for each message
from composio import Composio
from composio_openai import OpenAIProvider
composio = Composio(provider=OpenAIProvider())
async def handle_user_message(
user_id: str,
message: str,
config: dict
):
# Create new session for this message
session = composio.tool_router.create(
user_id=user_id,
toolkits=config["toolkits"],
manage_connections=True
)
tools = session.tools()
# Use tools with agent...
response = await run_agent(message, tools)
# ✅ Fresh configuration
# ✅ Clean logs grouped by session
# ✅ Latest connection states
return response
# Each message gets a new session
await handle_user_message("user_123", "Check my emails", {"toolkits": ["gmail"]})
await handle_user_message("user_123", "Send a slack message", {"toolkits": ["slack"]})
```
## ✅ Correct - Single Session Per Conversation (When Config Stable)
```typescript
// DO: Use one session for entire conversation if config doesn't change
async function handleConversation(
userId: string,
conversationId: string,
config: { toolkits: string[] }
) {
// Create ONE session for this conversation/thread
const session = await composio.create(userId, {
toolkits: config.toolkits,
manageConnections: true
});
const tools = await session.tools();
console.log(`Session ${session.sessionId} for conversation ${conversationId}`);
// Use the same session for all messages in this conversation
for await (const message of conversationStream) {
const response = await runAgent(message, tools);
// ✅ All tool executions logged under same session
// ✅ Easy to debug entire conversation flow
// ✅ Grouped logs in monitoring tools
}
}
```
```python
# DO: Use one session for entire conversation if config doesn't change
async def handle_conversation(
user_id: str,
conversation_id: str,
config: dict
):
# Create ONE session for this conversation/thread
session = composio.tool_router.create(
user_id=user_id,
toolkits=config["toolkits"],
manage_connections=True
)
tools = session.tools()
print(f"Session {session.session_id} for conversation {conversation_id}")
# Use the same session for all messages in this conversation
async for message in conversation_stream:
response = await run_agent(message, tools)
# ✅ All tool executions logged under same session
# ✅ Easy to debug entire conversation flow
# ✅ Grouped logs in monitoring tools
```
## When to Create New Sessions
### ✅ Always Create New Session When:
1. **Configuration Changes**
```typescript
// User connects new toolkit
if (userConnectedSlack) {
// Create new session with updated toolkits
const session = await composio.create(userId, {
toolkits: ['gmail', 'slack'] // Added slack
});
}
```
2. **Connected Accounts Change**
```typescript
// User disconnected and reconnected Gmail
const session = await composio.create(userId, {
toolkits: ['gmail'],
// Will use latest connection
});
```
3. **Different Toolkit Requirements**
```typescript
// Message needs different toolkits
const emailSession = await composio.create(userId, {
toolkits: ['gmail']
});
const codeSession = await composio.create(userId, {
toolkits: ['github', 'linear']
});
```
4. **New Conversation/Thread**
```typescript
// Starting a new conversation thread
const session = await composio.create(userId, {
toolkits: config.toolkits,
// Fresh session for clean log grouping
});
```
### ✅ Can Reuse Session When:
1. **Same conversation/thread**
2. **Configuration unchanged**
3. **No toolkit connections changed**
4. **Actively ongoing interaction**
## Benefits of Short-Lived Sessions
### 1. **Clean Log Grouping**
```typescript
// All tool executions in one session are grouped together
const session = await composio.create(userId, {
toolkits: ['gmail', 'slack']
});
// These executions are grouped under session.sessionId
await agent.run('Check emails'); // Logs: session_abc123
await agent.run('Send slack message'); // Logs: session_abc123
// Easy to trace entire conversation flow in monitoring
console.log(`View logs: /sessions/${session.sessionId}`);
```
### 2. **Fresh Configuration**
```typescript
// Always get latest toolkit connections and auth states
const session = await composio.create(userId, {
toolkits: ['gmail']
});
// ✅ Uses current connected account
// ✅ Reflects any new connections user made
// ✅ Picks up toolkit updates
```
### 3. **Easier Debugging**
```typescript
// Session ID becomes your debug trace ID
console.log(`Processing message in session ${session.sessionId}`);
// All logs tagged with session ID:
// [session_abc123] Executing GMAIL_FETCH_EMAILS
// [session_abc123] Executed GMAIL_FETCH_EMAILS
// [session_abc123] Executing SLACK_SEND_MESSAGE
// Filter all logs for this specific interaction
```
### 4. **Simplified Error Tracking**
```typescript
try {
const session = await composio.create(userId, config);
const result = await runAgent(message, session);
} catch (error) {
// Session ID in error context
logger.error('Agent failed', {
sessionId: session.sessionId,
userId,
error
});
}
```
## Pattern: Per-Message Sessions
```typescript
// Recommended pattern for most applications
export async function handleAgentRequest(
userId: string,
message: string,
toolkits: string[]
) {
// 1. Create fresh session
const session = await composio.create(userId, {
toolkits,
manageConnections: true
});
// 2. Log session start
logger.info('Session started', {
sessionId: session.sessionId,
userId,
toolkits
});
try {
// 3. Get tools and run agent
const tools = await session.tools();
const response = await agent.run(message, tools);
// 4. Log session completion
logger.info('Session completed', {
sessionId: session.sessionId
});
return response;
} catch (error) {
// 5. Log session error
logger.error('Session failed', {
sessionId: session.sessionId,
error
});
throw error;
}
}
```
## Pattern: Per-Conversation Sessions
```typescript
// For long-running conversations with stable config
export class ConversationSession {
private session: ToolRouterSession;
async start(userId: string, config: SessionConfig) {
// Create session once for conversation
this.session = await composio.create(userId, config);
logger.info('Conversation session started', {
sessionId: this.session.sessionId
});
}
async handleMessage(message: string) {
// Reuse session for all messages
const tools = await this.session.tools();
return await agent.run(message, tools);
}
async end() {
logger.info('Conversation session ended', {
sessionId: this.session.sessionId
});
}
}
```
## Key Principles
1. **Don't cache sessions** - Create new ones as needed
2. **Session = Unit of work** - One session per task or conversation
3. **Short-lived is better** - Fresh state, clean logs, easier debugging
4. **Session ID = Trace ID** - Use for log correlation and debugging
5. **Create on demand** - No need to pre-create or warm up sessions
## Reference
- [Tool Router Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions)
- [Session Properties](https://docs.composio.dev/sdk/typescript/api/tool-router#session-properties)
- [Best Practices](https://docs.composio.dev/sdk/typescript/api/tool-router#best-practices)