JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 32
  • Score
    100M100P100Q67827F
  • License MIT

Tool-agnostic watch runtime with transcript providers for coding agents.

Package Exports

  • @agentprobe/core
  • @agentprobe/core/core
  • @agentprobe/core/providers/cursor

Readme

@agentprobe/core

CI npm version License: MIT

@agentprobe/core is a TypeScript library for observing agent/session activity from transcript-like sources.

It is designed in layers:

  • core: generic runtime + lifecycle diffing (tool-agnostic)
  • providers/cursor: Cursor transcript discovery + parsing adapter

The core observer API is provider-injected and tool-agnostic. Cursor is currently the built-in provider, with support planned for Claude Code, Codex, OpenCode, and custom systems.

Install

npm install @agentprobe/core

Quick Start (Provider-Agnostic)

import { createObserver } from "@agentprobe/core";

const observer = createObserver({
  workspacePaths: ["/Users/me/my-project"],
});

observer.subscribeToSnapshots((event) => {
  console.log(event.snapshot.at, event.snapshot.agents.length);
});

observer.subscribeToAgentChanges((event) => {
  console.log(event.change.kind, event.agent.id);
});

await observer.start();

// later — stop() clears all subscriptions and resets state
await observer.stop();

createObserver defaults to the built-in Cursor provider. You can pass a custom provider, debounceMs, or checkIdleDelayMs as needed.

How Runtime Works

The watch runtime (used by createObserver) is built around a state machine and an internal typed event bus that processes events sequentially.

stateDiagram-v2
  [*] --> stopped
  stopped --> starting : start()
  starting --> started : connect + subscribe
  starting --> stopped : abort / error
  started --> stopping : stop()
  stopping --> stopped : disconnect

  state started {
    [*] --> waiting
    waiting --> processing : file‑changed / check‑idle / refresh‑requested
    processing --> waiting : snapshot + lifecycle emitted
    processing --> waiting : error emitted

    state waiting {
      [*] --> watchingFS
      watchingFS --> idleTimer : check‑idle scheduled
      idleTimer --> watchingFS : timer fires
    }
  }

Event bus

Inside the started state, all work flows through a sequential event bus with three event types:

  • file-changed — dispatched after debounced fs.watch events; reads the snapshot and schedules a check-idle timer
  • check-idle — fires on a timer (default 2s); re-reads the snapshot to catch time-based status transitions (e.g. runningidlecompleted) and self-reschedules while agents exist
  • refresh-requested — dispatched by refreshNow(); reads the snapshot and resolves waiting callers

Events are processed one at a time (no overlapping reads). Subscribers only receive events when agent statuses actually change (joined, statusChanged, left). Heartbeat-only cycles are silent — the internal polling is invisible to consumers.

Idle checking

Agent statuses depend on time elapsed since last activity. Without periodic re-evaluation, time-based transitions are missed when transcript files stop changing. The check-idle mechanism solves this:

  1. After every file-changed or refresh-requested, a check-idle timer is scheduled
  2. When it fires, the runtime re-reads the snapshot with the current timestamp
  3. If agents still exist, the timer self-reschedules
  4. Configurable via checkIdleDelayMs (default 2000, set to false to disable)

Lifecycle model

  • Internal states: stopped → starting → started → stopping
  • start() connects to the source, installs optional watch subscriptions, emits started, and dispatches an initial file-changed event
  • stop() clears timers/subscriptions/bus queue, rejects in-flight waiters, disconnects, and emits stopped

Concurrency and race safety

  • A monotonic lifecycle token guards all event dispatch
  • Every start/stop cycle advances the token
  • Events dispatched with a stale token are silently dropped
  • The event bus processes handlers sequentially — no overlapping async work

Error and stop semantics

  • Snapshot/read failures emit error and reject cycle waiters
  • Calling refreshNow() while not running rejects with NOT_RUNNING
  • Stopping during an active refresh rejects pending waiters with STOPPED_BEFORE_REFRESH_COMPLETED

Watch subscriptions

When a provider exposes subscribeToChanges, runtime subscriptions:

  • resolve configured/default watch paths
  • normalize paths (trim + drop empty + dedupe)
  • debounce bursty events before dispatching file-changed to the bus
  • resubscribe with exponential backoff on subscription failures

Public Entry Points

  • @agentprobe/core — full package (core + Cursor provider, createObserver defaults to Cursor)
  • @agentprobe/core/core — core runtime, lifecycle, model, and provider types only
  • @agentprobe/core/providers/cursor — Cursor transcript provider only

Development

npm install
npm run check
npm run build

Scripts

  • npm run format - format code with Biome
  • npm run lint - lint code with Biome
  • npm run typecheck - run TypeScript checking
  • npm run test - run Vitest suite
  • npm run build - produce dist bundles with tsup
  • npm run check - biome check + typecheck + test

Examples

See:

  • examples/provider-observer.ts (provider-injected API)

License

MIT. See LICENSE.