Skip to content

feat: Session State Machine#240

Open
danew wants to merge 36 commits intomainfrom
dane/existentialism
Open

feat: Session State Machine#240
danew wants to merge 36 commits intomainfrom
dane/existentialism

Conversation

@danew
Copy link
Contributor

@danew danew commented Dec 8, 2025

Overview

This introduces a new predictable, state-machine-based runtime to replace the ad-hoc CDPService orchestration.

Architecture

Components

  • SessionMachine: State machine managing lifecycle (Idle → Launching → Ready → Live → Draining → Closed)
  • BrowserDriver: Thin Puppeteer adapter that emits typed events
  • TaskScheduler: Manages critical vs background tasks with timeouts, drain, and cancellation
  • PluginAdapter: Bridges existing plugin hooks to state transitions
  • Orchestrator: Public facade providing CDPService-compatible API

States

  • Idle: Initial state, no browser
  • Launching: Browser process starting
  • Ready: Browser available, minimal setup complete
  • Live: Fully operational, handling requests
  • Draining: Shutting down, draining background tasks
  • Closed: Terminal state, all resources released

Events

  • launchSucceeded: Browser launched successfully
  • launchFailed: Browser launch failed
  • disconnected: Browser disconnected unexpectedly
  • targetCreated/Changed/Destroyed: Page lifecycle events
  • fileProtocolViolation: Fatal security violation

Commands

  • start(config): Start a new session
  • end(reason): End current session

@danew danew force-pushed the dane/existentialism branch from 057722e to 96608ac Compare December 8, 2025 15:54
@danew danew changed the title Session State Machine feat: Session State Machine Dec 8, 2025
commit a9209e2
Author: nas <[email protected]>
Date:   Fri Dec 12 05:11:10 2025 -0500

    chore: add more runtime tests for state machine (#243)

commit dd1113f
Author: junhsss <[email protected]>
Date:   Wed Dec 10 16:43:26 2025 +0900

    fix: test

commit b95d82e
Author: junhsss <[email protected]>
Date:   Wed Dec 10 16:37:34 2025 +0900

    feat: better state allocation

commit 65c7e65
Author: junhsss <[email protected]>
Date:   Wed Dec 10 16:37:22 2025 +0900

    feat: cleanup browser listeners

commit ad3666e
Author: junhsss <[email protected]>
Date:   Wed Dec 10 06:58:17 2025 +0900

    feat: handle browser crash

commit e3048ed
Author: junhsss <[email protected]>
Date:   Wed Dec 10 06:43:31 2025 +0900

    feat: idempotent launch

commit d72013e
Author: junhsss <[email protected]>
Date:   Wed Dec 10 06:34:09 2025 +0900

    feat: better browser close

commit 9684c37
Author: junhsss <[email protected]>
Date:   Wed Dec 10 06:25:33 2025 +0900

    feat: cleanup properly

commit 95e005a
Author: junhsss <[email protected]>
Date:   Wed Dec 10 06:19:00 2025 +0900

    chore: update lockfile

commit b289364
Author: junhsss <[email protected]>
Date:   Wed Dec 10 06:02:23 2025 +0900

    feat: mutex guard + error state

commit ba91c57
Author: junhsss <[email protected]>
Date:   Wed Dec 10 05:32:19 2025 +0900

    refactor: type state pattern
@github-actions github-actions bot added documentation Improvements or additions to documentation github-actions file-storage labels Dec 12, 2025
@danew danew marked this pull request as ready for review February 17, 2026 18:00
@danew danew requested a review from Copilot February 17, 2026 18:00
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Introduces a new state-machine-driven browser runtime (XState-based) intended to replace ad-hoc CDPService orchestration while preserving a CDPService-compatible API surface.

Changes:

  • Added XState-powered BrowserRuntime facade, machine, actors (proxy, data plane, logger, drain, task registry), and new logging/tracing utilities.
  • Introduced Vitest multi-project test setup (unit/jsdom/integration) with substantial new runtime test coverage.
  • Updated public service wiring to depend on a shared BrowserRuntime interface and added optional state transition instrumentation.

Reviewed changes

Copilot reviewed 67 out of 69 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
package.json Formatting-only adjustment at EOF.
api/vitest.config.ts Adds Vitest multi-project configuration (unit/jsdom/integration).
api/tsconfig.build.json Build tsconfig excludes tests from compilation output.
api/tests/integration/setup.ts Adds integration test preflight check for Chrome availability.
api/tests/integration/browser-runtime.integration.test.ts Adds end-to-end integration tests covering runtime lifecycle, proxying, events, and security.
api/src/types/enums.ts Adds StateTransition browser event type.
api/src/types/browser.ts Adds optional sessionId to launcher options.
api/src/types/browser-runtime.interface.ts Introduces common BrowserRuntime interface shared by CDPService and new runtime.
api/src/steel-browser-plugin.ts Switches Fastify decoration type from CDPService to BrowserRuntime.
api/src/services/session.service.ts Uses BrowserRuntime, adds 409 error on concurrent session creation, passes sessionId through launch options.
api/src/services/cdp/plugins/core/plugin-manager.ts Adds waitUntil helper to track async plugin tasks.
api/src/services/cdp/instrumentation/types.ts Adds StateTransitionEvent and extends union.
api/src/services/cdp/cdp.service.ts Implements BrowserRuntime interface and adds plugin accessors / waitUntil.
api/src/plugins/browser.ts Chooses between legacy CDPService and new XState runtime via env flags; sets up transition logging storage.
api/src/modules/sessions/sessions.routes.ts Makes instrumentation logger optional when recording.
api/src/modules/sessions/sessions.controller.ts Returns statusCode from thrown errors; uses runtime getTargetId.
api/src/modules/actions/actions.controller.ts Switches controller dependencies to BrowserRuntime.
api/src/env.ts Adds flags for runtime selection and state transition logging/tracing controls.
api/src/browser-runtime/utils/validation.ts Adds timezone validation helper with timeout.
api/src/browser-runtime/utils/timezone.ts Adds timezone lookup helper with proxy support and fallback.
api/src/browser-runtime/utils/extensions.ts Adds helper to resolve extension directories.
api/src/browser-runtime/utils/browser-utils.ts Adds Chrome path resolution and mouse helper injection utilities.
api/src/browser-runtime/utils.ts Adds deepMerge utility used by runtime config resolution.
api/src/browser-runtime/types.ts Re-exports driver types and SessionData for runtime package boundary.
api/src/browser-runtime/tracing/runtime-tracer.ts Adds OpenTelemetry tracing helpers with minimal vs detailed gating.
api/src/browser-runtime/tracing/index.ts Re-exports tracing helpers.
api/src/browser-runtime/storage/rolling-file-storage.ts Adds NDJSON rolling storage implementation for transition logs.
api/src/browser-runtime/storage/index.ts Re-exports rolling storage.
api/src/browser-runtime/services/proxy.service.ts Adds proxy-chain based proxy server with passthrough for internal bypass.
api/src/browser-runtime/services/fingerprint.service.ts Adds fingerprint generation + safe injection with fallback injector.
api/src/browser-runtime/services/config-resolver.ts Adds config resolution (timezone, userDataDir, user prefs merge, fingerprint generation).
api/src/browser-runtime/plugins/base-plugin.ts Defines new runtime plugin interface used by the machine.
api/src/browser-runtime/machine/browser.machine.ts Adds XState supervisor machine wiring boot/ready/drain/cleanup phases and event bridging.
api/src/browser-runtime/machine/browser.machine.test.ts Adds unit tests for the machine transitions and error paths.
api/src/browser-runtime/machine/actors/task-registry.actor.ts Adds background task registry actor with drain/cancel semantics.
api/src/browser-runtime/machine/actors/proxy-launcher.actor.ts Adds actor to launch/close proxy server.
api/src/browser-runtime/machine/actors/plugin-manager.actor.ts Adds actor to invoke plugin lifecycle hooks and page-created hooks.
api/src/browser-runtime/machine/actors/logger.actor.ts Adds actor to attach CDP instrumentation, inject fingerprint, and restore storage.
api/src/browser-runtime/machine/actors/drain.actor.ts Adds drain actor to drain tasks, notify plugins, flush logs, cancel remaining tasks.
api/src/browser-runtime/machine/actors/data-plane.actor.ts Adds HTTP+WS “data plane” to proxy CDP WebSocket and detect disconnects.
api/src/browser-runtime/machine/actors/cleanup-phases.actor.ts Adds cleanup phase helpers (close browser/proxy, flush logs, notify plugin shutdown).
api/src/browser-runtime/machine/actors/tests/proxy-launcher.actor.test.ts Tests proxy launcher behavior with mocked proxy service.
api/src/browser-runtime/machine/actors/tests/plugin-manager.actor.test.ts Tests plugin hooks invocation and error isolation.
api/src/browser-runtime/machine/actors/tests/logger-actor.test.ts Tests logger actor target attachment behavior.
api/src/browser-runtime/machine/actors/tests/drain.actor.test.ts Tests drain sequencing and plugin notifications.
api/src/browser-runtime/machine/actors/tests/data-plane.actor.test.ts Tests server startup, EADDRINUSE fatal error, disconnect handling.
api/src/browser-runtime/machine/actors/tests/cleanup-phases.actor.test.ts Tests each cleanup phase helper.
api/src/browser-runtime/logging/state-transition-logger.ts Adds state transition event recorder to logger/storage/span events.
api/src/browser-runtime/logging/index.ts Re-exports state transition logger.
api/src/browser-runtime/index.ts Public export surface for the runtime package.
api/src/browser-runtime/facade/browser-runtime.ts Adds runtime facade implementing BrowserRuntime via XState actor + hooks + proxy.
api/src/browser-runtime/facade/browser-runtime.test.ts Adds comprehensive facade tests (config reuse, hooks, ws proxy, plugins, keepAlive).
api/src/browser-runtime/drivers/types.ts Defines driver/runtime config types and supervisor events.
api/src/browser-runtime/drivers/simulated-launcher.ts Adds simulated launcher for load-testing.
api/src/browser-runtime/drivers/puppeteer-launcher.ts Adds Puppeteer-based launcher with proxy/extensions/fingerprint/timezone/security hardening.
api/src/browser-runtime/drivers/mock-launcher.ts Adds mock launcher for tests and crash simulation.
api/src/browser-runtime/drivers/index.ts Re-exports drivers.
api/src/browser-runtime/drivers/tests/puppeteer-launcher.test.ts Adds Puppeteer launcher tests including file:// blocking behavior.
api/src/browser-runtime/tests/task-registry.test.ts Adds task registry actor tests (drain/cancel/status).
api/src/browser-runtime/tests/state-transition-logger.test.ts Adds transition logger tests.
api/src/browser-runtime/tests/rolling-file-storage.test.ts Adds rolling storage tests (write/rotate/cleanup).
api/src/browser-runtime/tests/machine-with-mock.test.ts Adds runtime+machine tests using MockLauncher.
api/src/browser-runtime/tests/load.test.ts Adds load tests using SimulatedLauncher (concurrent + sequential sessions).
api/src/browser-runtime/tests/helpers.ts Adds shared mock helpers for runtime tests.
api/src/browser-runtime/tests/fingerprint.test.ts Adds fingerprint generation/injection tests.
api/src/browser-runtime/tests/disconnect-recovery.test.ts Adds keepAlive disconnect recovery tests.
api/package.json Adds Vitest scripts, build tsconfig, and runtime dependencies (xstate, async-mutex).
.husky/pre-commit Adds typecheck + tests to pre-commit, alongside formatting/lint/build.
Comments suppressed due to low confidence (5)

api/src/browser-runtime/machine/browser.machine.ts:1

  • The off("fileProtocolViolation", () => {}) call will not remove the listener because it passes a new function reference. Store the handler in a variable and use that same reference in both on and off to avoid leaking listeners across machine restarts.
    api/src/browser-runtime/machine/actors/plugin-manager.actor.ts:1
  • fromCallback cleanups are expected to be synchronous; returning an async cleanup means the promise is ignored and can mask shutdown issues. Make the cleanup a non-async function (and avoid awaiting inside it) so teardown is deterministic.
    api/src/browser-runtime/utils.ts:1
  • Iterating for...in over source can merge inherited/prototype properties and enables prototype-pollution vectors (e.g., __proto__, constructor, prototype) if any part of source is user-influenced (such as preferences/config). Iterate over Object.keys/Object.entries and explicitly block dangerous keys before assignment.
    api/vitest.config.ts:1
  • path is imported but not used in this config file. Removing it reduces noise and avoids lint warnings in stricter setups.
    api/src/modules/sessions/sessions.controller.ts:1
  • Correct typo in log message: "lauching" → "launching".

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +27 to +36
let timezone = "UTC";
try {
if (config.skipFingerprintInjection) {
console.log("[PuppeteerLauncher] Skipping timezone validation");
} else {
timezone = await validateTimezone(config.timezone);
}
} catch (error) {
console.warn(`[PuppeteerLauncher] Timezone validation failed: ${error}`);
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

When skipFingerprintInjection is true, this skips timezone validation and leaves timezone as "UTC", ignoring config.timezone. This will unintentionally force sessions into UTC even when a valid timezone was resolved. Consider always setting timezone from config.timezone (and optionally validating it), independent of fingerprint injection.

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +101
event: snapshot._nodes?.[0]?.key || "unknown",
context: { browser: !!snapshot.context.browser },
});
} else {
this.logger.info(
{ from: previousState, to: currentState, event: snapshot._nodes?.[0]?.key },
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

Using snapshot._nodes relies on XState internal/private fields that can change across releases, and it also doesn’t represent the triggering event (it’s a node key). Prefer capturing the actual triggering event via supported snapshot fields (if available) or by tracking the last sent event in the facade/machine and logging that instead.

Suggested change
event: snapshot._nodes?.[0]?.key || "unknown",
context: { browser: !!snapshot.context.browser },
});
} else {
this.logger.info(
{ from: previousState, to: currentState, event: snapshot._nodes?.[0]?.key },
event: snapshot.event?.type ?? "unknown",
context: { browser: !!snapshot.context.browser },
});
} else {
this.logger.info(
{ from: previousState, to: currentState, event: snapshot.event?.type },

Copilot uses AI. Check for mistakes.
Comment on lines +5 to 9
echo "🎨 Running code formatting..."
npm run typecheck -w api

# Code formatting and linting
echo "🎨 Running code formatting..."
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The echoed message says “Running code formatting...” but the command is typecheck. Update the message to reflect what’s actually running (and consider avoiding duplicate “formatting” echo lines) to make pre-commit output easier to interpret.

Suggested change
echo "🎨 Running code formatting..."
npm run typecheck -w api
# Code formatting and linting
echo "🎨 Running code formatting..."
echo "🔧 Running API type checking..."
npm run typecheck -w api
# Code formatting and linting
echo "🎨 Running code formatting and linting for API..."

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants