- 세션이 시작될 때 컨텍스트 초기화
- 세션이 종료되면 리소스 정리
- 세션 메트릭 및 분석 추적
- 동적으로 세션 동작 구성
세션 시작 후크
세션이 onSessionStart 시작될 때 후크가 호출됩니다(신규 또는 다시 시작).
후크 서명
코드 언어 navigation
type SessionStartHandler = (
input: SessionStartHookInput,
invocation: HookInvocation
) => Promise<SessionStartHookOutput | null | undefined>;
SessionStartHandler = Callable[
[SessionStartHookInput, dict[str, str]],
Awaitable[SessionStartHookOutput | None]
]
type SessionStartHandler func(
input SessionStartHookInput,
invocation HookInvocation,
) (*SessionStartHookOutput, error)
public delegate Task<SessionStartHookOutput?> SessionStartHandler(
SessionStartHookInput input,
HookInvocation invocation);
@FunctionalInterface
public interface SessionStartHandler {
CompletableFuture<SessionStartHookOutput> handle(
SessionStartHookInput input,
HookInvocation invocation);
}
입력
| Field | Type | 설명 |
|---|---|---|
timestamp | number | 후크가 트리거될 때의 Unix 타임스탬프 |
cwd | string | 현재 작업 디렉터리 |
source | ||
"startup" | ||
| | | ||
"resume" | ||
| | | ||
"new" | ||
| 세션이 시작된 방법 | ||
initialPrompt | 정의되지 않은 문자열 | | 제공된 경우 초기 프롬프트 |
출력
| Field | Type | 설명 |
|---|---|---|
additionalContext | string | 세션 시작 시 추가할 컨텍스트 |
modifiedConfig | object | 세션 구성 재정의 |
예제
시작 시 프로젝트 컨텍스트 추가
코드 언어 navigation
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(),
};
},
},
});
from copilot.session import PermissionHandler
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(on_permission_request=PermissionHandler.approve_all, hooks={"on_session_start": on_session_start})
세션 재시작 처리
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;
},
},
});
사용자 기본 설정 로드
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"),
};
},
},
});
세션 끝 후크
세션이 onSessionEnd 종료되면 후크가 호출됩니다.
독립 실행형 SDK 세션은 프롬프트 간에 활성 상태로 유지됩니다. 프롬프트를 완료해도 세션이 종료되지 않습니다. 종료 후크를 실행하려면 세션의 연결을 끊거나 해제합니다.
후크 서명
코드 언어 navigation
type SessionEndHandler = (
input: SessionEndHookInput,
invocation: HookInvocation
) => Promise<SessionEndHookOutput | null | undefined>;
SessionEndHandler = Callable[
[SessionEndHookInput, dict[str, str]],
Awaitable[SessionEndHookOutput | None]
]
type SessionEndHandler func(
input SessionEndHookInput,
invocation HookInvocation,
) (*SessionEndHookOutput, error)
public delegate Task<SessionEndHookOutput?> SessionEndHandler(
SessionEndHookInput input,
HookInvocation invocation);
@FunctionalInterface
public interface SessionEndHandler {
CompletableFuture<SessionEndHookOutput> handle(
SessionEndHookInput input,
HookInvocation invocation);
}
입력
| Field | Type | 설명 |
|---|---|---|
timestamp | number | 후크가 트리거될 때의 Unix 타임스탬프 |
cwd | string | 현재 작업 디렉터리 |
reason | string | 세션이 종료된 이유(아래 참조) |
finalMessage | 정의되지 않은 문자열 | | 세션의 마지막 메시지 |
error | 정의되지 않은 문자열 | | 오류로 인해 세션이 종료된 경우 오류 메시지 |
종료 이유
| Reason | 설명 |
|---|---|
"complete" | 세션이 정상적으로 완료됨 |
"error" | 오류로 인해 세션이 종료됨 |
"abort" | 사용자 또는 코드에 의해 세션이 중단되었습니다. |
"timeout" | 세션 시간이 초과됨 |
"user_exit" | 사용자가 세션을 명시적으로 종료했습니다. |
출력
| Field | Type | 설명 |
|---|---|---|
suppressOutput | boolean | 최종 세션 출력 표시 안 함 |
cleanupActions | string[] | 수행할 정리 작업 목록 |
sessionSummary | string | 로깅/분석을 위한 세션 요약 |
예제
세션 메트릭 추적
코드 언어 navigation
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;
},
},
});
from copilot.session import PermissionHandler
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(on_permission_request=PermissionHandler.approve_all, hooks={
"on_session_start": on_session_start,
"on_session_end": on_session_end,
})
자원을 정리하세요
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;
},
},
});
다시 시작에 대한 세션 상태 저장
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;
},
},
});
로그 세션 요약
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;
},
},
});
에이전트 중지 후크
에이전트 중지 후크는 최상위 에이전트가 자연스럽게 턴의 끝에 도달하면 실행됩니다. 이는 onSessionEnd와는 별개입니다: 세션은 계속 활성 상태로 유지되며, 후크는 다른 에이전트 턴을 요청할 수 있습니다.
| 언어 | 핸들러 |
|---|---|
| Node.js/ TypeScript | onAgentStop |
| Python | on_agent_stop |
| Go | OnAgentStop |
| .NET | OnAgentStop |
| 러스트 | on_agent_stop |
| Java | setOnAgentStop |
입력
공용 멤버 이름은 각 언어의 대/소문자 규칙을 따릅니다.
| Meaning | Node.js/Python | Go/.NET | 러스트 | Java |
|---|---|---|---|---|
에이전트가 중지된 이유(예: end_turn | stopReason | StopReason | stop_reason | getStopReason() |
| 디스크 내 세션 기록 경로 | transcriptPath | TranscriptPath | transcript_path | get |
| 이전 블록 결정이 이미 이 계속 진행을 강제했는지 여부 | stopHookActive | StopHookActive | stop_hook_active | get |
출력
에이전트를 중지할 출력을 반환하지 않습니다. 다른 사용자 메시지를 큐에 넣기 위해 블록 결정을 반환하고 계속합니다.
{
"decision": "block",
"reason": "Run the final validation and fix any failures."
}
이 후크로 인해 이미 계속된 에이전트를 반복적으로 차단하지 않도록 위에 나열된 활성 중지 멤버를 사용합니다. 또한 런타임은 연속 블록 결정의 한도를 지정합니다.
모범 사례
-
빠르게 유지
onSessionStart- 사용자가 세션이 준비되기를 기다리고 있습니다. -
모든 끝 이유 처리 - 세션이 완전히 종료된다고 가정하지 마세요. 오류 및 중단을 처리합니다.
-
리소스 정리 - 세션 중에 할당된 모든 리소스를 해제하는 데 사용합니다
onSessionEnd. -
최소 상태 저장 - 세션 데이터를 추적하는 경우 경량으로 유지합니다.
-
정리 작업이 멱등성을 갖도록 하세요 -
onSessionEnd프로세스가 충돌하면 호출되지 않을 수 있습니다.