Skip to content

feat: add SessionStart/SessionEnd hooks for lifecycle tracking#359

Merged
sjnims merged 2 commits intomainfrom
feature/session-lifecycle-hooks
Jan 31, 2026
Merged

feat: add SessionStart/SessionEnd hooks for lifecycle tracking#359
sjnims merged 2 commits intomainfrom
feature/session-lifecycle-hooks

Conversation

@sjnims
Copy link
Owner

@sjnims sjnims commented Jan 31, 2026

Description

Add SessionStart and SessionEnd capture hooks to Stage 3 execution, enabling session lifecycle timing data for diagnostics. These purely observational hooks follow the existing stateful hook pattern (like Stop), using per-scenario callbacks to push timing data to scenario-scoped arrays.

New types: SessionStartCapture, SessionEndCapture, SessionTimingCapture — captured via SDK hooks and stored as session_timing? on ExecutionResult.

Key design decisions:

  • Stateful hooks — callbacks push to per-scenario arrays (not stateless/reusable) since each scenario needs its own timing data
  • starts is an array/clear triggers SessionStart with source: 'clear', so multiple starts per session are possible in batch mode
  • No capture map needed — unlike tool Pre/Post correlation, each event is self-contained

Type of Change

  • New feature (non-breaking change that adds functionality)

Component(s) Affected

Pipeline Stages

  • Stage 3: Execution (src/stages/3-execution/)

Core Infrastructure

  • Types (src/types/)

Other

  • Tests (tests/)

Motivation and Context

Session timing currently relies on manual Date.now() calls that conflate subprocess startup, plugin loading, MCP initialization, and actual LLM execution into a single duration metric. SDK v0.2.25 provides SessionStart and SessionEnd hooks that fire at precise lifecycle boundaries, enabling accurate timing breakdown.

Closes #349

How Has This Been Tested?

Test Configuration:

  • Node.js version: 20+
  • OS: macOS (Darwin 25.2.0)

Test Steps:

  1. npm run build — compiles successfully
  2. npm run typecheck — no type errors
  3. npm run lint — no lint issues
  4. npm run format:check — all files formatted
  5. npm run knip — no dead code
  6. npm test — 1650/1650 tests pass

Checklist

General

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings or errors

TypeScript / Code Quality

  • All functions have explicit return types
  • Strict TypeScript checks pass (npm run typecheck)
  • ESM import/export patterns used correctly
  • Unused parameters prefixed with _
  • No any types without justification

Documentation

  • I have updated CLAUDE.md if behavior or commands changed — N/A, no behavior change
  • I have updated inline JSDoc comments where applicable
  • I have verified all links work correctly

Linting

  • I have run npm run lint and fixed all issues
  • I have run npm run format:check

Testing

  • I have run npm test and all tests pass
  • I have added tests for new functionality
  • Test coverage meets thresholds (78% lines, 75% functions, 65% branches)

Stage-Specific Checks

Stage 3: Execution (click to expand)
  • Claude Agent SDK integration works correctly
  • Tool capture via PreToolUse hooks functions properly
  • Timeout handling works as expected
  • Session isolation prevents cross-contamination
  • Permission bypass works for automated execution

API & SDK Changes

SDK Integration (click to expand)
  • Claude Agent SDK integration maintains compatibility
  • SessionStartHookInput and SessionEndHookInput types imported from SDK v0.2.25
  • Hooks registered via existing Partial<Record<HookEvent, HookCallbackMatcher[]>> pattern

Files Changed (9 files, +625/-2)

File Changes
src/types/transcript.ts Added SessionStartCapture, SessionEndCapture, SessionTimingCapture types; session_timing? on ExecutionResult
src/types/index.ts Export new types
src/stages/3-execution/sdk-client.ts Import/re-export SessionStartHookInput, SessionEndHookInput; add config type aliases
src/stages/3-execution/tool-capture-hooks.ts Type guards, callback types, createSessionStartHook/createSessionEndHook factories
src/stages/3-execution/hooks-factory.ts Extended SDKHooksConfig, CaptureHooksOptions, StatefulHooks, StatefulHooksOptions; updated all factory functions
src/stages/3-execution/agent-executor.ts Wired hooks into single-execution path; session timing in ExecutionContext and buildExecutionResult
src/stages/3-execution/session-batching.ts Wired hooks into batch-execution path; session timing in all query/result builders
tests/.../tool-capture-hooks.test.ts 12 new tests for SessionStart/End hook creators
tests/.../hooks-factory.test.ts Updated all existing tests + 2 new integration tests

Reviewer Notes

Areas that need special attention:

Known limitations or trade-offs:

  • In batched_by_component mode, the /clear command between scenarios is sent via a separate query without capture hooks, so SessionStart with source: 'clear' from /clear won't be captured. This is expected — /clear is infrastructure, not part of the scenario being tested.
  • If running against an older SDK that doesn't fire these hooks, session_timing stays undefined. No special handling needed.

🤖 Generated with Claude Code

Add SessionStart and SessionEnd capture hooks to Stage 3 execution,
enabling session lifecycle timing data for diagnostics. Hooks follow
the existing stateful hook pattern with per-scenario callbacks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sjnims sjnims self-assigned this Jan 31, 2026
@claude
Copy link
Contributor

claude bot commented Jan 31, 2026

Claude finished @sjnims's task in 1m 46s —— View job


PR Review: feat: add SessionStart/SessionEnd hooks for lifecycle tracking

  • Run quality checks
  • Review diff and changed files
  • Post review feedback

Verdict: ✅ Looks good

This is a clean, well-structured addition that follows the existing hook patterns precisely.

Quality Checks

Check Result
npm run typecheck ✅ Pass
npm run lint ✅ Pass
npm run format:check ✅ Pass
npm run knip ✅ Pass

What's Good

  • Consistent pattern: The new SessionStart/SessionEnd hooks exactly mirror the existing Stop hook pattern — stateful per-scenario callbacks, type guards, factory functions, and wiring. Easy to review because it's predictable.
  • Type guards are thorough: isSessionStartInput and isSessionEndInput validate both hook_event_name and the discriminating field (source/reason), matching the style of existing guards.
  • starts as an array is the right call: The PR description clearly explains why — /clear triggers additional SessionStart events, so a single value would lose data.
  • Good test coverage: 12 new tests for the hook creators covering happy paths, cross-event rejection, invalid input, optional fields, and return values. The hooks-factory tests are updated consistently.
  • Forward-compatible design: session_timing stays undefined when hooks don't fire (older SDK), and it's populated but not consumed yet — clean separation with follow-up tracked in [Feature]: Expose session timing metrics in Stage 4 evaluation reports #357/[Feature]: Add session timing breakdown to dry-run cost estimation #358.

Suggestions

See inline comments for specific items:

  1. SessionEndCapture.reason is typed as string but the SDK defines it as ExitReason (a 5-value union). Using the specific union type would improve type safety. (See inline comment)

  2. Duplicated session timing construction — the sessionStarts.length > 0 || sessionEnd ? { starts, end } : undefined logic appears identically in both agent-executor.ts and session-batching.ts. Not blocking, but worth noting for future refactoring. (See inline comment)

Neither issue is blocking — overall this is a solid, focused addition.

@claude claude bot added enhancement New feature or request effort:large > 4 hours stage:execution Stage 3: Agent SDK execution with tool capture component:types TypeScript interfaces (src/types/) scope:hooks Hook evaluation (Phase 2) sdk:agent Claude Agent SDK usage (Stage 3) labels Jan 31, 2026
@codecov
Copy link

codecov bot commented Jan 31, 2026

Codecov Report

❌ Patch coverage is 55.17241% with 13 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/stages/3-execution/agent-executor.ts 0.00% 5 Missing and 2 partials ⚠️
src/stages/3-execution/session-batching.ts 16.66% 3 Missing and 2 partials ⚠️
src/stages/3-execution/tool-capture-hooks.ts 93.75% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Address PR review feedback — use the SDK's ExitReason type instead of
generic string for better type safety.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sjnims
Copy link
Owner Author

sjnims commented Jan 31, 2026

Addressed the type safety suggestion — changed from to the SDK's union type for compile-time validation.

The duplication note is acknowledged but I'm leaving it as-is since it's only 2 call sites and marked as non-blocking. If we add more session timing construction logic later, we can extract a helper.

@claude
Copy link
Contributor

claude bot commented Jan 31, 2026

Claude finished @sjnims's task in 1m 4s —— View job


Re-review after update

  • Run quality checks
  • Review updated diff
  • Post review feedback

Verdict: ✅ Looks good

The previous suggestion has been addressed correctly.

Quality Checks

Check Result
npm run typecheck ✅ Pass
npm run lint ✅ Pass
npm run format:check ✅ Pass
npm run knip ✅ Pass

Changes Verified

The new commit f827a5f correctly updates SessionEndCapture.reason from string to the SDK's ExitReason union type (src/types/transcript.ts:149), and re-exports ExitReason from the SDK for convenience (src/types/transcript.ts:9). This provides compile-time validation that only valid exit reasons are captured.

No other changes were introduced — the rest of the diff is identical to the previous review. The previous approval stands.

@sjnims
Copy link
Owner Author

sjnims commented Jan 31, 2026

Addressed the ExitReason type safety suggestion — changed SessionEndCapture.reason from string to the SDK ExitReason union type for compile-time validation.

The duplication note is acknowledged but leaving as-is since it is only 2 call sites and marked as non-blocking. If we add more session timing construction logic later, we can extract a helper.

@sjnims sjnims merged commit 485a111 into main Jan 31, 2026
12 of 13 checks passed
@sjnims sjnims deleted the feature/session-lifecycle-hooks branch January 31, 2026 05:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:types TypeScript interfaces (src/types/) effort:large > 4 hours enhancement New feature or request scope:hooks Hook evaluation (Phase 2) sdk:agent Claude Agent SDK usage (Stage 3) stage:execution Stage 3: Agent SDK execution with tool capture

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add SessionStart/SessionEnd hooks for lifecycle tracking

1 participant