Skip to content

feat(runtime): session-setup hook (register_session_setup)#781

Open
seanturner83 wants to merge 2 commits into
usestrix:mainfrom
seanturner83:feat/session-setup-hook
Open

feat(runtime): session-setup hook (register_session_setup)#781
seanturner83 wants to merge 2 commits into
usestrix:mainfrom
seanturner83:feat/session-setup-hook

Conversation

@seanturner83

Copy link
Copy Markdown
Contributor

Summary

Adds a generic extension point — register_session_setup(callback) — that runs a coroutine once per scan, right after the sandbox session is ready and before the agent's first turn, with the live session and the scan config.

from strix.runtime.session_manager import register_session_setup

async def prepare(session, scan_config):
    await session.exec(...)          # prepare in-sandbox state the agent will use

register_session_setup(prepare)

It mirrors the existing backends.register_backend and agents.factory.register_agent_tools seams.

Why

A @function_tool runs runner-side and only receives a RunContextWrapper — never the sandbox session. So any setup that must session.exec inside the container before the agent starts (build an index against the freshly-materialised target, warm a cache, install a probe) currently has nowhere to hook. This adds that one seam.

run_strix_scan invokes the registered callbacks after create_or_reuse returns and before the root agent is built — the window where the target is materialised but analysis hasn't begun.

Contract

  • Callbacks are awaited in registration order.
  • Best-effort: an exception in a callback is logged and swallowed, so an optional setup step can never fail the scan.
  • Duplicate registrations are ignored (repeated imports don't double-run).

Motivating consumer

A SCIP code-graph addon (separate) that indexes the materialised target in-sandbox and copies the index out for its runner-side query tools. But the hook is deliberately generic — cache warming, sidecar readiness probes, etc. — and adds nothing to the default path (no callbacks registered → a no-op).

Tests

tests/test_session_setup_hooks.py: registration order, session/config passthrough, dedup, failure-is-swallowed, empty-registry no-op. +142 lines across session_manager.py (registry + invoker), runner.py (invocation), and the test.

🤖 Generated with Claude Code

Adds an extension point that runs a callback once per scan, right after the
sandbox session is ready and before the agent's first turn, with the live
session and the scan config:

    from strix.runtime.session_manager import register_session_setup

    async def prepare(session, scan_config):
        await session.exec(...)   # set up in-sandbox state the agent will use

    register_session_setup(prepare)

Mirrors backends.register_backend / agents.factory.register_agent_tools. The
gap it fills: a @function_tool runs runner-side and only receives a
RunContextWrapper, never the sandbox session — so setup that must session.exec
inside the container (build an index, warm a cache, install a probe) has no
seam to hook. run_strix_scan invokes the callbacks after create_or_reuse and
before building the root agent.

Best-effort by contract: callbacks are awaited in registration order, and an
exception in one is logged and swallowed so an optional setup step can never
fail the scan. Duplicate registrations are ignored.

Motivating consumer: a SCIP code-graph addon that indexes the freshly
materialised target in-sandbox and copies the index out for its query tools —
but the hook is generic (cache warming, sidecar probes, etc.).

Tests: registration order, session/config passthrough, dedup, failure-is-
swallowed, empty-registry no-op.
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a session-setup extension point before agent execution. The main changes are:

  • A process-wide registry for asynchronous setup callbacks.
  • Ordered, deduplicated callback execution with best-effort error handling.
  • Runner integration after sandbox creation and before agent construction.
  • Unit tests for registry behavior and callback failures.

Confidence Score: 4/5

The cached-session resume path can repeat non-idempotent setup and needs a fix before merging.

  • Initial setup runs at the intended point before agent construction.
  • Callback ordering, duplicate registration, and failure isolation are covered.
  • Resuming a cached scan invokes the hooks again against an already-prepared sandbox.

strix/core/runner.py

Important Files Changed

Filename Overview
strix/core/runner.py Invokes setup hooks at the intended lifecycle point, but repeats them when resuming a cached session.
strix/runtime/session_manager.py Adds the callback registry, duplicate suppression, ordered execution, and per-callback error isolation.
tests/test_session_setup_hooks.py Covers direct registry behavior but not initial-run versus resume lifecycle behavior.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
strix/core/runner.py:218
**Resume Repeats Session Setup**

When a scan resumes in the same process, `create_or_reuse` returns its cached sandbox and this unconditional call runs every setup callback against that already-prepared session. Non-idempotent hooks can start duplicate services, append configuration twice, or fail after partial changes; the swallowed exception then lets the resumed agent continue with invalid sandbox state. Track setup completion with the cached session so each materialized session is prepared only once.

Reviews (1): Last reviewed commit: "feat(runtime): session-setup hook (regis..." | Re-trigger Greptile

Comment thread strix/core/runner.py Outdated
# the target is materialised, but before the agent's first turn — the
# window an addon needs to prepare in-sandbox state (e.g. a code-graph
# index). Best-effort: a hook failure is logged, never fatal.
await session_manager.run_session_setups(bundle["session"], scan_config)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Resume Repeats Session Setup

When a scan resumes in the same process, create_or_reuse returns its cached sandbox and this unconditional call runs every setup callback against that already-prepared session. Non-idempotent hooks can start duplicate services, append configuration twice, or fail after partial changes; the swallowed exception then lets the resumed agent continue with invalid sandbox state. Track setup completion with the cached session so each materialized session is prepared only once.

Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/core/runner.py
Line: 218

Comment:
**Resume Repeats Session Setup**

When a scan resumes in the same process, `create_or_reuse` returns its cached sandbox and this unconditional call runs every setup callback against that already-prepared session. Non-idempotent hooks can start duplicate services, append configuration twice, or fail after partial changes; the swallowed exception then lets the resumed agent continue with invalid sandbox state. Track setup completion with the cached session so each materialized session is prepared only once.

How can I resolve this? If you propose a fix, please make it concise.

…ile usestrix#781)

create_or_reuse caches + reuses the session bundle across in-process resumes
(the cred-boundary iteration loop). run_session_setups was called
unconditionally, so a resume re-ran every setup callback against an
already-prepared sandbox — double-applying non-idempotent hooks (start a service
twice, re-append config) or failing mid-way and, since failures are swallowed,
leaving the resumed agent on invalid state.

Take the bundle (not the raw session) and stamp it once setups have run; skip on
reuse. Stamp BEFORE running so a partial/failed setup doesn't retry-loop. A
fresh session (new bundle) — including a cross-push PR resume in a new process —
gets a fresh run, so setup-derived state is never stale: the reused bundle is
the same container with the same materialised source.

Tests: run-once-on-reuse, fresh-bundle-reruns, marked-done-even-on-failure.
@seanturner83

Copy link
Copy Markdown
Contributor Author

Good catch — fixed in 33eb270.

create_or_reuse caches + reuses the session bundle across in-process resumes (the credential-boundary iteration loop), so the unconditional run_session_setups call re-ran every callback against an already-prepared sandbox. run_session_setups now takes the bundle (not the raw session) and stamps it once setups have run, skipping on reuse — stamped before running so a partial/failed hook can't retry-loop either.

On staleness (since setup-derived state like an index is what this is for): the reused bundle is the same container with the same materialised source, so nothing's stale — the code hasn't changed between in-process iterations. A cross-push PR resume runs in a new process against a freshly materialised sandbox → new bundle → setups re-run → the index is rebuilt against the new code. So skipping on reuse never serves a stale artifact.

Added tests: run-once-on-reuse, fresh-bundle-reruns-afresh, and marked-done-even-when-a-callback-fails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant