Skip to content

Latest commit

 

History

History
509 lines (401 loc) · 12.3 KB

File metadata and controls

509 lines (401 loc) · 12.3 KB

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
import type { SessionStartHookInput, HookInvocation, SessionStartHookOutput } from "@github/copilot-sdk";
type SessionStartHandler = (
  input: SessionStartHookInput,
  invocation: HookInvocation
) => Promise<SessionStartHookOutput | null | undefined>;
type SessionStartHandler = (
  input: SessionStartHookInput,
  invocation: HookInvocation
) => Promise<SessionStartHookOutput | null | undefined>;
Python
from copilot.types import SessionStartHookInput, HookInvocation, SessionStartHookOutput
from typing import Callable, Awaitable

SessionStartHandler = Callable[
    [SessionStartHookInput, HookInvocation],
    Awaitable[SessionStartHookOutput | None]
]
SessionStartHandler = Callable[
    [SessionStartHookInput, HookInvocation],
    Awaitable[SessionStartHookOutput | None]
]
Go
package main

import copilot "github.com/github/copilot-sdk/go"

type SessionStartHandler func(
    input copilot.SessionStartHookInput,
    invocation copilot.HookInvocation,
) (*copilot.SessionStartHookOutput, error)

func main() {}
type SessionStartHandler func(
    input SessionStartHookInput,
    invocation HookInvocation,
) (*SessionStartHookOutput, error)
.NET
using GitHub.Copilot.SDK;

public delegate Task<SessionStartHookOutput?> SessionStartHandler(
    SessionStartHookInput input,
    HookInvocation invocation);
public delegate Task<SessionStartHookOutput?> 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
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
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

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

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
type SessionEndHandler = (
  input: SessionEndHookInput,
  invocation: HookInvocation
) => Promise<SessionEndHookOutput | null | undefined>;
Python
from copilot.types import SessionEndHookInput, HookInvocation
from typing import Callable, Awaitable

SessionEndHandler = Callable[
    [SessionEndHookInput, HookInvocation],
    Awaitable[None]
]
SessionEndHandler = Callable[
    [SessionEndHookInput, HookInvocation],
    Awaitable[SessionEndHookOutput | None]
]
Go
package main

import copilot "github.com/github/copilot-sdk/go"

type SessionEndHandler func(
    input copilot.SessionEndHookInput,
    invocation copilot.HookInvocation,
) error

func main() {}
type SessionEndHandler func(
    input SessionEndHookInput,
    invocation HookInvocation,
) (*SessionEndHookOutput, error)
.NET
public delegate Task<SessionEndHookOutput?> 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
const sessionStartTimes = new Map<string, number>();

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
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

const sessionResources = new Map<string, { tempFiles: string[] }>();

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

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

const sessionData: Record<string, { prompts: number; tools: number; startTime: number }> = {};

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