# Session Lifecycle Hooks
Session lifecycle hooks let you respond to session start and end events. Use them to:
- Initialize context when sessions begin
- Clean up resources when sessions end
- Track session metrics and analytics
- Configure session behavior dynamically
## Session Start Hook {#session-start}
The `onSessionStart` hook is called when a session begins (new or resumed).
### Hook Signature
Node.js / TypeScript
```typescript
type SessionStartHandler = (
input: SessionStartHookInput,
invocation: HookInvocation
) => Promise;
```
Python
```python
SessionStartHandler = Callable[
[SessionStartHookInput, HookInvocation],
Awaitable[SessionStartHookOutput | None]
]
```
Go
```go
type SessionStartHandler func(
input SessionStartHookInput,
invocation HookInvocation,
) (*SessionStartHookOutput, error)
```
.NET
```csharp
public delegate Task SessionStartHandler(
SessionStartHookInput input,
HookInvocation invocation);
```
### Input
| Field | Type | Description |
|-------|------|-------------|
| `timestamp` | number | Unix timestamp when the hook was triggered |
| `cwd` | string | Current working directory |
| `source` | `"startup"` \| `"resume"` \| `"new"` | How the session was started |
| `initialPrompt` | string \| undefined | The initial prompt if provided |
### Output
| Field | Type | Description |
|-------|------|-------------|
| `additionalContext` | string | Context to add at session start |
| `modifiedConfig` | object | Override session configuration |
### Examples
#### Add Project Context at Start
Node.js / TypeScript
```typescript
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
console.log(`Session ${invocation.sessionId} started (${input.source})`);
const projectInfo = await detectProjectType(input.cwd);
return {
additionalContext: `
This is a ${projectInfo.type} project.
Main language: ${projectInfo.language}
Package manager: ${projectInfo.packageManager}
`.trim(),
};
},
},
});
```
Python
```python
async def on_session_start(input_data, invocation):
print(f"Session {invocation['session_id']} started ({input_data['source']})")
project_info = await detect_project_type(input_data["cwd"])
return {
"additionalContext": f"""
This is a {project_info['type']} project.
Main language: {project_info['language']}
Package manager: {project_info['packageManager']}
""".strip()
}
session = await client.create_session({
"hooks": {"on_session_start": on_session_start}
})
```
#### Handle Session Resume
```typescript
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
if (input.source === "resume") {
// Load previous session state
const previousState = await loadSessionState(invocation.sessionId);
return {
additionalContext: `
Session resumed. Previous context:
- Last topic: ${previousState.lastTopic}
- Open files: ${previousState.openFiles.join(", ")}
`.trim(),
};
}
return null;
},
},
});
```
#### Load User Preferences
```typescript
const session = await client.createSession({
hooks: {
onSessionStart: async () => {
const preferences = await loadUserPreferences();
const contextParts = [];
if (preferences.language) {
contextParts.push(`Preferred language: ${preferences.language}`);
}
if (preferences.codeStyle) {
contextParts.push(`Code style: ${preferences.codeStyle}`);
}
if (preferences.verbosity === "concise") {
contextParts.push("Keep responses brief and to the point.");
}
return {
additionalContext: contextParts.join("\n"),
};
},
},
});
```
---
## Session End Hook {#session-end}
The `onSessionEnd` hook is called when a session ends.
### Hook Signature
Node.js / TypeScript
```typescript
type SessionEndHandler = (
input: SessionEndHookInput,
invocation: HookInvocation
) => Promise;
```
Python
```python
SessionEndHandler = Callable[
[SessionEndHookInput, HookInvocation],
Awaitable[SessionEndHookOutput | None]
]
```
Go
```go
type SessionEndHandler func(
input SessionEndHookInput,
invocation HookInvocation,
) (*SessionEndHookOutput, error)
```
.NET
```csharp
public delegate Task SessionEndHandler(
SessionEndHookInput input,
HookInvocation invocation);
```
### Input
| Field | Type | Description |
|-------|------|-------------|
| `timestamp` | number | Unix timestamp when the hook was triggered |
| `cwd` | string | Current working directory |
| `reason` | string | Why the session ended (see below) |
| `finalMessage` | string \| undefined | The last message from the session |
| `error` | string \| undefined | Error message if session ended due to error |
#### End Reasons
| Reason | Description |
|--------|-------------|
| `"complete"` | Session completed normally |
| `"error"` | Session ended due to an error |
| `"abort"` | Session was aborted by user or code |
| `"timeout"` | Session timed out |
| `"user_exit"` | User explicitly ended the session |
### Output
| Field | Type | Description |
|-------|------|-------------|
| `suppressOutput` | boolean | Suppress the final session output |
| `cleanupActions` | string[] | List of cleanup actions to perform |
| `sessionSummary` | string | Summary of the session for logging/analytics |
### Examples
#### Track Session Metrics
Node.js / TypeScript
```typescript
const sessionStartTimes = new Map();
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
sessionStartTimes.set(invocation.sessionId, input.timestamp);
return null;
},
onSessionEnd: async (input, invocation) => {
const startTime = sessionStartTimes.get(invocation.sessionId);
const duration = startTime ? input.timestamp - startTime : 0;
await recordMetrics({
sessionId: invocation.sessionId,
duration,
endReason: input.reason,
});
sessionStartTimes.delete(invocation.sessionId);
return null;
},
},
});
```
Python
```python
session_start_times = {}
async def on_session_start(input_data, invocation):
session_start_times[invocation["session_id"]] = input_data["timestamp"]
return None
async def on_session_end(input_data, invocation):
start_time = session_start_times.get(invocation["session_id"])
duration = input_data["timestamp"] - start_time if start_time else 0
await record_metrics({
"session_id": invocation["session_id"],
"duration": duration,
"end_reason": input_data["reason"],
})
session_start_times.pop(invocation["session_id"], None)
return None
session = await client.create_session({
"hooks": {
"on_session_start": on_session_start,
"on_session_end": on_session_end,
}
})
```
#### Clean Up Resources
```typescript
const sessionResources = new Map();
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
sessionResources.set(invocation.sessionId, { tempFiles: [] });
return null;
},
onSessionEnd: async (input, invocation) => {
const resources = sessionResources.get(invocation.sessionId);
if (resources) {
// Clean up temp files
for (const file of resources.tempFiles) {
await fs.unlink(file).catch(() => {});
}
sessionResources.delete(invocation.sessionId);
}
console.log(`Session ${invocation.sessionId} ended: ${input.reason}`);
return null;
},
},
});
```
#### Save Session State for Resume
```typescript
const session = await client.createSession({
hooks: {
onSessionEnd: async (input, invocation) => {
if (input.reason !== "error") {
// Save state for potential resume
await saveSessionState(invocation.sessionId, {
endTime: input.timestamp,
cwd: input.cwd,
reason: input.reason,
});
}
return null;
},
},
});
```
#### Log Session Summary
```typescript
const sessionData: Record = {};
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
sessionData[invocation.sessionId] = {
prompts: 0,
tools: 0,
startTime: input.timestamp
};
return null;
},
onUserPromptSubmitted: async (_, invocation) => {
sessionData[invocation.sessionId].prompts++;
return null;
},
onPreToolUse: async (_, invocation) => {
sessionData[invocation.sessionId].tools++;
return { permissionDecision: "allow" };
},
onSessionEnd: async (input, invocation) => {
const data = sessionData[invocation.sessionId];
console.log(`
Session Summary:
ID: ${invocation.sessionId}
Duration: ${(input.timestamp - data.startTime) / 1000}s
Prompts: ${data.prompts}
Tool calls: ${data.tools}
End reason: ${input.reason}
`.trim());
delete sessionData[invocation.sessionId];
return null;
},
},
});
```
## Best Practices
1. **Keep `onSessionStart` fast** - Users are waiting for the session to be ready.
2. **Handle all end reasons** - Don't assume sessions end cleanly; handle errors and aborts.
3. **Clean up resources** - Use `onSessionEnd` to free any resources allocated during the session.
4. **Store minimal state** - If tracking session data, keep it lightweight.
5. **Make cleanup idempotent** - `onSessionEnd` might not be called if the process crashes.
## See Also
- [Hooks Overview](./overview.md)
- [Error Handling Hook](./error-handling.md)
- [Debugging Guide](../debugging.md)