Skip to content

Repository files navigation

AgentPulse

npm versionCILicense: MITClaude CodeCursorCodexNo LLMLocal-only

A live trajectory dashboard for AI coding agent sessions. AgentPulse watches local Claude Code, Cursor, and Codex transcripts and classifies what each agent is doing right now: converging, exploring, stuck, done, drifting, or idle.

AgentPulse dashboard — a selected session cycles through converging, stuck, and drifting verdicts in real time

Drop it in a terminal next to your editor. You get an always-on read across local agent sessions without a model judge, hosted dashboard, telemetry, or outbound network calls.

Requires Node.js 22 or newer.

npx @conalh/agentpulse@latest live
flowchart LR
Claude["Claude Code<br/>transcripts"] --> Pulse
Cursor["Cursor<br/>transcripts"] --> Pulse
Codex["Codex<br/>sessions"] --> Pulse
Pulse[("AgentPulse<br/>local trajectory classifier")] --> TUI["Live TUI<br/>verdict + narrative"]
Pulse --> CI["CI snapshot<br/>--once --strict"]
TUI --> User["Human sees<br/>converging · stuck · drifting"]
classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px
classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe
class Claude,Cursor,Codex input
class Pulse engine
class TUI,CI,User output
Loading

Sample narrative on a converging session:

Your agent has been working on the login bug for 18 minutes. It focused
on `src/auth/`, made 3 changes to `session.ts`, and ran the tests after
each change. Tests went from failing to passing. Looks like it solved it.
Verdict: ● converging (confidence 0.85)

See also:warden for deterministic runtime policy, CapabilityEcho for PR-time capability drift, and agent-gov-core for the shared transcript and report primitives.

Where this fits

AgentPulse is the suite's live-observation layer — it watches a running session's trajectory rather than a diff or a finished transcript.

ToolInputCatches / decidesOutputUse when
wardenpolicy + tool actionallow / deny / askverdictyou need deterministic runtime policy decisions
CapabilityEchoPR diffnew executable capabilityannotations + reportcode gains network/subprocess/eval/lifecycle/workflow power
AgentPulselive session eventstrajectory stateterminal dashboardyou want live session observation
agent-gov-coreshared schemas/parserscommon Finding/Report modelnpm librarytools need shared report primitives

Why this exists

AI agents can run for a long time while the human is not staring at every tool call. Sometimes they converge. Sometimes they explore. Sometimes they get stuck in the same failed loop. Sometimes they touch a privileged path, pipe a network fetch straight into a shell, or write outside the repo.

AgentPulse exists to make that session state visible while the work is happening. It is deterministic local signal, not an LLM judging another LLM.

What it shows

SignalWhat it means
convergingFocused edits plus verification, often with tests moving toward passing.
exploringReading and orientation, no meaningful edit trajectory yet.
stuckRepeated edits/tests/failures, user pushback, or loop-like behavior.
doneCompletion language plus idle gap.
driftingA deterministic drift rule fired (privileged-path access, a shell-piped network fetch like curl … | sh, or a write outside the repo root). See Drift detection scope.
idleActivity has gone quiet or the window had no recent movement.

Drift detection scope.The drifting bucket is a deterministic first-pass detector, not a comprehensive agent-safety scanner. It currently fires on exactly three rule families:

  • Privileged-path access — a tool input path, or a path-shaped token inside a shell command (quote-aware, including bash -c "…" payloads), touching .ssh, .aws, .kube, /etc/shadow, or /private/var.
  • Shell-piped network fetchcurl/wget piped into sh/bash/zsh. Detection is tokenizer-based (agent-gov-core's quote-aware tokenizeShell), so quoted URLs (curl "https://…" | sh) are caught while a curl … | sh quoted inside another command's argument (e.g. gh release create --notes "…") is not a false positive.
  • Write outside the repo root — a Write/Edit to a path outside the session's cwd (or, with no repo root, outside /tmp, /var, ~). ./.. segments are resolved lexically before comparison and ~-prefixed paths count as outside; symlink escapes are not resolved.

So drifting means "a known risky pattern fired" — not "this session is safe." It does not yet cover bash <(curl …), curl -o … && sh …, bash -c "curl … | sh" nesting, PowerShell iwr | iex, python -c/node -e download-and-exec, package install hooks, credential exfiltration through ordinary files, or API-driven network actions. Treat a clean run as "none of the implemented rules matched," and pair AgentPulse with the rest of the agent-gov suite for deeper gating.

What makes it different

Several tools watch agent sessions. AgentPulse's wedge is the specific combination none of them cover:

Local-onlyNo LLMTrajectory verdictPer-session live dashboardPR gate
LangSmith / Langfuse / AgentOps❌ cloud❌ LLM-judge❌ traces only
Claude Code Session Memory❌ LLM⚠ structured
agenttrace❌ metrics only⚠ TUI
AgentPulse

The wedge is the combination. AgentPulse pairs naturally with agenttrace for cost/health metrics and with the rest of the agent-gov suite for PR-time gates.

agentpulse live — the live dashboard

agentpulse live [options]

Options:

FlagDefaultEffect
--window <duration>20mRecap window per session (5m, 1h, etc.)
--refresh <duration>30sBackground refresh cadence. Watcher fires sub-second on file changes regardless.
--roots <p1,p2,...>platform defaultsOverride discovery roots (comma-separated)
--stale <duration>1hSkip sessions older than this
--max-depth <N>unboundedCap discovery recursion depth below each root
--exclude <d1,d2,…>noneDirectory names to skip during discovery (e.g. node_modules,.git)
--hide-idleoffHide sessions with no activity in the window (also honored with --once)
--max-sessions <N>10Cap the list. With --once this is display-only — gating still considers every session.
--show-subagentsoffInclude agent-<hex> SDK-spawned subagent transcripts
--no-detectorsoffSkip the drifting bucket entirely
--onceoffHeadless snapshot mode. Runs once, prints, exits.
--format <fmt>textWith --once: text or json.
--strictoffWith --once: exit 1 if any session is drifting or stuck.
--fail-on-erroroffWith --once --strict: also exit 1 if a session failed to analyze (unreadable/corrupt transcript).
--redact <mode>noneRedact transcript-derived paths from --once output: none, paths, all.
--notify <mode>noneLocal notification on transition into drifting/stuck: none, bell, os, both.

Keyboard:

KeyAction
↑ ↓ / k j / w sMove selection
rForce refresh on selected session
aWhitelist current session's drift findings; preview first, confirm within 3s
nName / rename the selected session alias
?Toggle help overlay
q / Ctrl-CQuit

Agent aliases

When multiple agents work on the same project, the dashboard rows can look identical until you name them. Press n on a selected row, type an alias like CC1, frontend, or backend, and press Enter.

Aliases live in two optional JSON files:

  • <session.cwd>/.agentpulse-aliases.json — per-project, commit if you want team-shared conventions.
  • ~/.agentpulse/aliases.json — personal default.
{
"version": 1,
"aliases": {
"c3d4566ef4c5": "CC1",
"7a8b91234567": "CG1"
}
}

Exception baseline

Press a on a drifting session to preview the drift findings, then press a again within the confirmation window to append the selected fingerprints to <session.cwd>/.agentpulse-exceptions.json. AgentPulse refreshes and re-classifies the session immediately.

Commit the exception file to your repo when the behavior is intentionally approved. CI gating (agentpulse live --once --strict or the GitHub Action) honors the same baseline.

{
"version": 1,
"exceptions": [
{
"kind": "agent_pulse.live_drift_shell_exfil",
"fingerprint": "a1b2c3...",
"approvedAt": "2026-05-23T22:00:00.000Z",
"note": "approved by user via TUI"
}
]
}

Notifications

--notify <mode> fires a local notification when any session transitions into drifting or stuck.

ModeEffect
noneSilent
bellWrites \x07 to stderr
osNative notification: osascript on macOS, notify-send on Linux, BurntToast/NotifyIcon on Windows
bothBell + OS

Best-effort: missing OS notification utilities are a silent no-op rather than a crash.

CI integration

GitHub Action

- uses: Conalh/AgentPulse@v0.8.1with:
transcript-dirs: agentpulse-transcriptsstrict: 'true'redact: pathscomment-on-pr: 'true'github-token: ${{ github.token }}

The action runs agentpulse live --once against the provided transcript directory, writes a markdown summary to the GitHub step summary, optionally posts a sticky PR comment, and fails the workflow when strict: true and any session is drifting or stuck.

By default, the gate fails closed on analysis errors (fail-on-error: true) and hosted output reduces paths to basenames (redact: paths). Set fail-on-error: false to make unreadable or corrupt transcripts advisory. Use redact: none only for trusted private output, or redact: all when project labels, narratives, and topic keywords may also be sensitive. Other inputs include max-depth and exclude for bounded discovery, plus hide-idle, max-sessions, no-detectors, and show-subagents.

Sticky PR comments require pull-requests: write permission and an explicit github-token: ${{ github.token }} input. Omit both when the step summary is enough.

Supply chain. The Action builds and executes the checked-out Action source with npm ci --ignore-scripts, using the committed lockfile. Pin to a full commit SHA when you need AgentPulse's code and dependency graph to be immutable. npm remains the download source for the lockfile-pinned packages.

Privacy. The step summary and PR comment contain transcript-derived project labels, verdicts, drift counts, narratives, and topic keywords. Paths are reduced to basenames by default, but labels and narrative text remain. Use redact: all when those fields may be sensitive, and never publish raw transcript artifacts or unreviewed JSON snapshots.

Raw CLI

npx @conalh/agentpulse@latest live --once --strict --roots <transcript-dir>

Add --format json to pipe a structured snapshot into downstream tools.

agentpulse recap — single-transcript mode

agentpulse recap --transcript-dir ~/.claude/projects/<your-project>/ --format json

Same pipeline, narrower input. Use --watch for a polling re-emit loop.

What's intentionally not in scope

  • No LLM, anywhere. Not for summarization, not for classification.
  • No outbound network calls. Reads local transcript files, writes to terminal and optional local notifier.
  • No web UI / hosted dashboard. TUI for live view, GitHub Action for PRs, JSON output for everything else.
  • No multi-session memory. Each invocation reads the window and exits.
  • No “AI to review AI” loop. Detectors are deterministic.

Architecture

Deterministic pipeline. Each layer is pure where it can be; all layers share the src/types.ts contract.

LayerFileInput -> Output
1. Parseragent-gov-core/parsers/ (v1.1.0+)Claude Code / Cursor / Codex / Antigravity JSONL -> TranscriptEvent[]
2. Enrichmentsrc/enrich.tsevents -> keywords, cwd-relative path clusters, action classes
2.5. Sequencessrc/sequences.tsevents -> ordered-pattern signal (tdd_loop / stuck_loop / refuse_to_verify / exploratory_edit)
3. Outcomesrc/trajectory.tsevents -> verification trend, user tone, completion verbs, idle gap
4. Trajectorysrc/trajectory.tsenrichment + outcome + sequence + exceptions -> six-bucket verdict
5. Narrativesrc/narrative.tsverdict -> plain-English recap

Live infrastructure on top: src/sessions/ discovery/watcher, src/orchestrator.ts, src/exceptions.ts, src/notifications.ts, src/once.ts, and src/tui/.

Design choices worth flagging

  • Local by default. Zero network calls in any code path.
  • Deterministic. Same transcript window in, same verdict out. No model drift, API outages, or rate limits.
  • Live observation. AgentPulse reports the trajectory of active local sessions and provides a one-shot format for CI artifacts.
  • Substrate-built. Uses agent-gov-core primitives where the parser and report contracts overlap.
  • Tested. 296 tests (npm test), including hand-rolled property tests (a seeded, replayable PRNG with 200 iterations per invariant) over the pure classifier layers and a labeled golden-replay corpus of 13 transcript fixtures spanning all six trajectory buckets across Claude Code, Cursor, Codex, and Antigravity.

Windows terminal note

If you're running on Windows, prefer Windows Terminal over legacy cmd.exe. It works on cmd.exe, but Windows Terminal renders the dashboard more cleanly.

Related public tools

RepoWhat it catches
wardenDeterministic allow / deny / ask policy decisions.
CapabilityEchoCapability drift introduced by code, manifests, workflows, and Dockerfiles.
AgentPulse(this repo)Live local trajectory verdicts for active agent sessions.
agent-gov-coreShared parsers, the canonical Finding schema, and mergeFindings.

MIT.

About

Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - Conalh/AgentPulse: Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action. · GitHub
Skip to content

Repository files navigation

AgentPulse

npm versionCILicense: MITClaude CodeCursorCodexNo LLMLocal-only

A live trajectory dashboard for AI coding agent sessions. AgentPulse watches local Claude Code, Cursor, and Codex transcripts and classifies what each agent is doing right now: converging, exploring, stuck, done, drifting, or idle.

AgentPulse dashboard — a selected session cycles through converging, stuck, and drifting verdicts in real time

Drop it in a terminal next to your editor. You get an always-on read across local agent sessions without a model judge, hosted dashboard, telemetry, or outbound network calls.

Requires Node.js 22 or newer.

npx @conalh/agentpulse@latest live
flowchart LR
Claude["Claude Code<br/>transcripts"] --> Pulse
Cursor["Cursor<br/>transcripts"] --> Pulse
Codex["Codex<br/>sessions"] --> Pulse
Pulse[("AgentPulse<br/>local trajectory classifier")] --> TUI["Live TUI<br/>verdict + narrative"]
Pulse --> CI["CI snapshot<br/>--once --strict"]
TUI --> User["Human sees<br/>converging · stuck · drifting"]
classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px
classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe
class Claude,Cursor,Codex input
class Pulse engine
class TUI,CI,User output
Loading

Sample narrative on a converging session:

Your agent has been working on the login bug for 18 minutes. It focused
on `src/auth/`, made 3 changes to `session.ts`, and ran the tests after
each change. Tests went from failing to passing. Looks like it solved it.
Verdict: ● converging (confidence 0.85)

See also:warden for deterministic runtime policy, CapabilityEcho for PR-time capability drift, and agent-gov-core for the shared transcript and report primitives.

Where this fits

AgentPulse is the suite's live-observation layer — it watches a running session's trajectory rather than a diff or a finished transcript.

ToolInputCatches / decidesOutputUse when
wardenpolicy + tool actionallow / deny / askverdictyou need deterministic runtime policy decisions
CapabilityEchoPR diffnew executable capabilityannotations + reportcode gains network/subprocess/eval/lifecycle/workflow power
AgentPulselive session eventstrajectory stateterminal dashboardyou want live session observation
agent-gov-coreshared schemas/parserscommon Finding/Report modelnpm librarytools need shared report primitives

Why this exists

AI agents can run for a long time while the human is not staring at every tool call. Sometimes they converge. Sometimes they explore. Sometimes they get stuck in the same failed loop. Sometimes they touch a privileged path, pipe a network fetch straight into a shell, or write outside the repo.

AgentPulse exists to make that session state visible while the work is happening. It is deterministic local signal, not an LLM judging another LLM.

What it shows

SignalWhat it means
convergingFocused edits plus verification, often with tests moving toward passing.
exploringReading and orientation, no meaningful edit trajectory yet.
stuckRepeated edits/tests/failures, user pushback, or loop-like behavior.
doneCompletion language plus idle gap.
driftingA deterministic drift rule fired (privileged-path access, a shell-piped network fetch like curl … | sh, or a write outside the repo root). See Drift detection scope.
idleActivity has gone quiet or the window had no recent movement.

Drift detection scope.The drifting bucket is a deterministic first-pass detector, not a comprehensive agent-safety scanner. It currently fires on exactly three rule families:

  • Privileged-path access — a tool input path, or a path-shaped token inside a shell command (quote-aware, including bash -c "…" payloads), touching .ssh, .aws, .kube, /etc/shadow, or /private/var.
  • Shell-piped network fetchcurl/wget piped into sh/bash/zsh. Detection is tokenizer-based (agent-gov-core's quote-aware tokenizeShell), so quoted URLs (curl "https://…" | sh) are caught while a curl … | sh quoted inside another command's argument (e.g. gh release create --notes "…") is not a false positive.
  • Write outside the repo root — a Write/Edit to a path outside the session's cwd (or, with no repo root, outside /tmp, /var, ~). ./.. segments are resolved lexically before comparison and ~-prefixed paths count as outside; symlink escapes are not resolved.

So drifting means "a known risky pattern fired" — not "this session is safe." It does not yet cover bash <(curl …), curl -o … && sh …, bash -c "curl … | sh" nesting, PowerShell iwr | iex, python -c/node -e download-and-exec, package install hooks, credential exfiltration through ordinary files, or API-driven network actions. Treat a clean run as "none of the implemented rules matched," and pair AgentPulse with the rest of the agent-gov suite for deeper gating.

What makes it different

Several tools watch agent sessions. AgentPulse's wedge is the specific combination none of them cover:

Local-onlyNo LLMTrajectory verdictPer-session live dashboardPR gate
LangSmith / Langfuse / AgentOps❌ cloud❌ LLM-judge❌ traces only
Claude Code Session Memory❌ LLM⚠ structured
agenttrace❌ metrics only⚠ TUI
AgentPulse

The wedge is the combination. AgentPulse pairs naturally with agenttrace for cost/health metrics and with the rest of the agent-gov suite for PR-time gates.

agentpulse live — the live dashboard

agentpulse live [options]

Options:

FlagDefaultEffect
--window <duration>20mRecap window per session (5m, 1h, etc.)
--refresh <duration>30sBackground refresh cadence. Watcher fires sub-second on file changes regardless.
--roots <p1,p2,...>platform defaultsOverride discovery roots (comma-separated)
--stale <duration>1hSkip sessions older than this
--max-depth <N>unboundedCap discovery recursion depth below each root
--exclude <d1,d2,…>noneDirectory names to skip during discovery (e.g. node_modules,.git)
--hide-idleoffHide sessions with no activity in the window (also honored with --once)
--max-sessions <N>10Cap the list. With --once this is display-only — gating still considers every session.
--show-subagentsoffInclude agent-<hex> SDK-spawned subagent transcripts
--no-detectorsoffSkip the drifting bucket entirely
--onceoffHeadless snapshot mode. Runs once, prints, exits.
--format <fmt>textWith --once: text or json.
--strictoffWith --once: exit 1 if any session is drifting or stuck.
--fail-on-erroroffWith --once --strict: also exit 1 if a session failed to analyze (unreadable/corrupt transcript).
--redact <mode>noneRedact transcript-derived paths from --once output: none, paths, all.
--notify <mode>noneLocal notification on transition into drifting/stuck: none, bell, os, both.

Keyboard:

KeyAction
↑ ↓ / k j / w sMove selection
rForce refresh on selected session
aWhitelist current session's drift findings; preview first, confirm within 3s
nName / rename the selected session alias
?Toggle help overlay
q / Ctrl-CQuit

Agent aliases

When multiple agents work on the same project, the dashboard rows can look identical until you name them. Press n on a selected row, type an alias like CC1, frontend, or backend, and press Enter.

Aliases live in two optional JSON files:

  • <session.cwd>/.agentpulse-aliases.json — per-project, commit if you want team-shared conventions.
  • ~/.agentpulse/aliases.json — personal default.
{
"version": 1,
"aliases": {
"c3d4566ef4c5": "CC1",
"7a8b91234567": "CG1"
}
}

Exception baseline

Press a on a drifting session to preview the drift findings, then press a again within the confirmation window to append the selected fingerprints to <session.cwd>/.agentpulse-exceptions.json. AgentPulse refreshes and re-classifies the session immediately.

Commit the exception file to your repo when the behavior is intentionally approved. CI gating (agentpulse live --once --strict or the GitHub Action) honors the same baseline.

{
"version": 1,
"exceptions": [
{
"kind": "agent_pulse.live_drift_shell_exfil",
"fingerprint": "a1b2c3...",
"approvedAt": "2026-05-23T22:00:00.000Z",
"note": "approved by user via TUI"
}
]
}

Notifications

--notify <mode> fires a local notification when any session transitions into drifting or stuck.

ModeEffect
noneSilent
bellWrites \x07 to stderr
osNative notification: osascript on macOS, notify-send on Linux, BurntToast/NotifyIcon on Windows
bothBell + OS

Best-effort: missing OS notification utilities are a silent no-op rather than a crash.

CI integration

GitHub Action

- uses: Conalh/AgentPulse@v0.8.1with:
transcript-dirs: agentpulse-transcriptsstrict: 'true'redact: pathscomment-on-pr: 'true'github-token: ${{ github.token }}

The action runs agentpulse live --once against the provided transcript directory, writes a markdown summary to the GitHub step summary, optionally posts a sticky PR comment, and fails the workflow when strict: true and any session is drifting or stuck.

By default, the gate fails closed on analysis errors (fail-on-error: true) and hosted output reduces paths to basenames (redact: paths). Set fail-on-error: false to make unreadable or corrupt transcripts advisory. Use redact: none only for trusted private output, or redact: all when project labels, narratives, and topic keywords may also be sensitive. Other inputs include max-depth and exclude for bounded discovery, plus hide-idle, max-sessions, no-detectors, and show-subagents.

Sticky PR comments require pull-requests: write permission and an explicit github-token: ${{ github.token }} input. Omit both when the step summary is enough.

Supply chain. The Action builds and executes the checked-out Action source with npm ci --ignore-scripts, using the committed lockfile. Pin to a full commit SHA when you need AgentPulse's code and dependency graph to be immutable. npm remains the download source for the lockfile-pinned packages.

Privacy. The step summary and PR comment contain transcript-derived project labels, verdicts, drift counts, narratives, and topic keywords. Paths are reduced to basenames by default, but labels and narrative text remain. Use redact: all when those fields may be sensitive, and never publish raw transcript artifacts or unreviewed JSON snapshots.

Raw CLI

npx @conalh/agentpulse@latest live --once --strict --roots <transcript-dir>

Add --format json to pipe a structured snapshot into downstream tools.

agentpulse recap — single-transcript mode

agentpulse recap --transcript-dir ~/.claude/projects/<your-project>/ --format json

Same pipeline, narrower input. Use --watch for a polling re-emit loop.

What's intentionally not in scope

  • No LLM, anywhere. Not for summarization, not for classification.
  • No outbound network calls. Reads local transcript files, writes to terminal and optional local notifier.
  • No web UI / hosted dashboard. TUI for live view, GitHub Action for PRs, JSON output for everything else.
  • No multi-session memory. Each invocation reads the window and exits.
  • No “AI to review AI” loop. Detectors are deterministic.

Architecture

Deterministic pipeline. Each layer is pure where it can be; all layers share the src/types.ts contract.

LayerFileInput -> Output
1. Parseragent-gov-core/parsers/ (v1.1.0+)Claude Code / Cursor / Codex / Antigravity JSONL -> TranscriptEvent[]
2. Enrichmentsrc/enrich.tsevents -> keywords, cwd-relative path clusters, action classes
2.5. Sequencessrc/sequences.tsevents -> ordered-pattern signal (tdd_loop / stuck_loop / refuse_to_verify / exploratory_edit)
3. Outcomesrc/trajectory.tsevents -> verification trend, user tone, completion verbs, idle gap
4. Trajectorysrc/trajectory.tsenrichment + outcome + sequence + exceptions -> six-bucket verdict
5. Narrativesrc/narrative.tsverdict -> plain-English recap

Live infrastructure on top: src/sessions/ discovery/watcher, src/orchestrator.ts, src/exceptions.ts, src/notifications.ts, src/once.ts, and src/tui/.

Design choices worth flagging

  • Local by default. Zero network calls in any code path.
  • Deterministic. Same transcript window in, same verdict out. No model drift, API outages, or rate limits.
  • Live observation. AgentPulse reports the trajectory of active local sessions and provides a one-shot format for CI artifacts.
  • Substrate-built. Uses agent-gov-core primitives where the parser and report contracts overlap.
  • Tested. 296 tests (npm test), including hand-rolled property tests (a seeded, replayable PRNG with 200 iterations per invariant) over the pure classifier layers and a labeled golden-replay corpus of 13 transcript fixtures spanning all six trajectory buckets across Claude Code, Cursor, Codex, and Antigravity.

Windows terminal note

If you're running on Windows, prefer Windows Terminal over legacy cmd.exe. It works on cmd.exe, but Windows Terminal renders the dashboard more cleanly.

Related public tools

RepoWhat it catches
wardenDeterministic allow / deny / ask policy decisions.
CapabilityEchoCapability drift introduced by code, manifests, workflows, and Dockerfiles.
AgentPulse(this repo)Live local trajectory verdicts for active agent sessions.
agent-gov-coreShared parsers, the canonical Finding schema, and mergeFindings.

MIT.

About

Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - Conalh/AgentPulse: Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action. · GitHub
Skip to content

Repository files navigation

AgentPulse

npm versionCILicense: MITClaude CodeCursorCodexNo LLMLocal-only

A live trajectory dashboard for AI coding agent sessions. AgentPulse watches local Claude Code, Cursor, and Codex transcripts and classifies what each agent is doing right now: converging, exploring, stuck, done, drifting, or idle.

AgentPulse dashboard — a selected session cycles through converging, stuck, and drifting verdicts in real time

Drop it in a terminal next to your editor. You get an always-on read across local agent sessions without a model judge, hosted dashboard, telemetry, or outbound network calls.

Requires Node.js 22 or newer.

npx @conalh/agentpulse@latest live
flowchart LR
Claude["Claude Code<br/>transcripts"] --> Pulse
Cursor["Cursor<br/>transcripts"] --> Pulse
Codex["Codex<br/>sessions"] --> Pulse
Pulse[("AgentPulse<br/>local trajectory classifier")] --> TUI["Live TUI<br/>verdict + narrative"]
Pulse --> CI["CI snapshot<br/>--once --strict"]
TUI --> User["Human sees<br/>converging · stuck · drifting"]
classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px
classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe
class Claude,Cursor,Codex input
class Pulse engine
class TUI,CI,User output
Loading

Sample narrative on a converging session:

Your agent has been working on the login bug for 18 minutes. It focused
on `src/auth/`, made 3 changes to `session.ts`, and ran the tests after
each change. Tests went from failing to passing. Looks like it solved it.
Verdict: ● converging (confidence 0.85)

See also:warden for deterministic runtime policy, CapabilityEcho for PR-time capability drift, and agent-gov-core for the shared transcript and report primitives.

Where this fits

AgentPulse is the suite's live-observation layer — it watches a running session's trajectory rather than a diff or a finished transcript.

ToolInputCatches / decidesOutputUse when
wardenpolicy + tool actionallow / deny / askverdictyou need deterministic runtime policy decisions
CapabilityEchoPR diffnew executable capabilityannotations + reportcode gains network/subprocess/eval/lifecycle/workflow power
AgentPulselive session eventstrajectory stateterminal dashboardyou want live session observation
agent-gov-coreshared schemas/parserscommon Finding/Report modelnpm librarytools need shared report primitives

Why this exists

AI agents can run for a long time while the human is not staring at every tool call. Sometimes they converge. Sometimes they explore. Sometimes they get stuck in the same failed loop. Sometimes they touch a privileged path, pipe a network fetch straight into a shell, or write outside the repo.

AgentPulse exists to make that session state visible while the work is happening. It is deterministic local signal, not an LLM judging another LLM.

What it shows

SignalWhat it means
convergingFocused edits plus verification, often with tests moving toward passing.
exploringReading and orientation, no meaningful edit trajectory yet.
stuckRepeated edits/tests/failures, user pushback, or loop-like behavior.
doneCompletion language plus idle gap.
driftingA deterministic drift rule fired (privileged-path access, a shell-piped network fetch like curl … | sh, or a write outside the repo root). See Drift detection scope.
idleActivity has gone quiet or the window had no recent movement.

Drift detection scope.The drifting bucket is a deterministic first-pass detector, not a comprehensive agent-safety scanner. It currently fires on exactly three rule families:

  • Privileged-path access — a tool input path, or a path-shaped token inside a shell command (quote-aware, including bash -c "…" payloads), touching .ssh, .aws, .kube, /etc/shadow, or /private/var.
  • Shell-piped network fetchcurl/wget piped into sh/bash/zsh. Detection is tokenizer-based (agent-gov-core's quote-aware tokenizeShell), so quoted URLs (curl "https://…" | sh) are caught while a curl … | sh quoted inside another command's argument (e.g. gh release create --notes "…") is not a false positive.
  • Write outside the repo root — a Write/Edit to a path outside the session's cwd (or, with no repo root, outside /tmp, /var, ~). ./.. segments are resolved lexically before comparison and ~-prefixed paths count as outside; symlink escapes are not resolved.

So drifting means "a known risky pattern fired" — not "this session is safe." It does not yet cover bash <(curl …), curl -o … && sh …, bash -c "curl … | sh" nesting, PowerShell iwr | iex, python -c/node -e download-and-exec, package install hooks, credential exfiltration through ordinary files, or API-driven network actions. Treat a clean run as "none of the implemented rules matched," and pair AgentPulse with the rest of the agent-gov suite for deeper gating.

What makes it different

Several tools watch agent sessions. AgentPulse's wedge is the specific combination none of them cover:

Local-onlyNo LLMTrajectory verdictPer-session live dashboardPR gate
LangSmith / Langfuse / AgentOps❌ cloud❌ LLM-judge❌ traces only
Claude Code Session Memory❌ LLM⚠ structured
agenttrace❌ metrics only⚠ TUI
AgentPulse

The wedge is the combination. AgentPulse pairs naturally with agenttrace for cost/health metrics and with the rest of the agent-gov suite for PR-time gates.

agentpulse live — the live dashboard

agentpulse live [options]

Options:

FlagDefaultEffect
--window <duration>20mRecap window per session (5m, 1h, etc.)
--refresh <duration>30sBackground refresh cadence. Watcher fires sub-second on file changes regardless.
--roots <p1,p2,...>platform defaultsOverride discovery roots (comma-separated)
--stale <duration>1hSkip sessions older than this
--max-depth <N>unboundedCap discovery recursion depth below each root
--exclude <d1,d2,…>noneDirectory names to skip during discovery (e.g. node_modules,.git)
--hide-idleoffHide sessions with no activity in the window (also honored with --once)
--max-sessions <N>10Cap the list. With --once this is display-only — gating still considers every session.
--show-subagentsoffInclude agent-<hex> SDK-spawned subagent transcripts
--no-detectorsoffSkip the drifting bucket entirely
--onceoffHeadless snapshot mode. Runs once, prints, exits.
--format <fmt>textWith --once: text or json.
--strictoffWith --once: exit 1 if any session is drifting or stuck.
--fail-on-erroroffWith --once --strict: also exit 1 if a session failed to analyze (unreadable/corrupt transcript).
--redact <mode>noneRedact transcript-derived paths from --once output: none, paths, all.
--notify <mode>noneLocal notification on transition into drifting/stuck: none, bell, os, both.

Keyboard:

KeyAction
↑ ↓ / k j / w sMove selection
rForce refresh on selected session
aWhitelist current session's drift findings; preview first, confirm within 3s
nName / rename the selected session alias
?Toggle help overlay
q / Ctrl-CQuit

Agent aliases

When multiple agents work on the same project, the dashboard rows can look identical until you name them. Press n on a selected row, type an alias like CC1, frontend, or backend, and press Enter.

Aliases live in two optional JSON files:

  • <session.cwd>/.agentpulse-aliases.json — per-project, commit if you want team-shared conventions.
  • ~/.agentpulse/aliases.json — personal default.
{
"version": 1,
"aliases": {
"c3d4566ef4c5": "CC1",
"7a8b91234567": "CG1"
}
}

Exception baseline

Press a on a drifting session to preview the drift findings, then press a again within the confirmation window to append the selected fingerprints to <session.cwd>/.agentpulse-exceptions.json. AgentPulse refreshes and re-classifies the session immediately.

Commit the exception file to your repo when the behavior is intentionally approved. CI gating (agentpulse live --once --strict or the GitHub Action) honors the same baseline.

{
"version": 1,
"exceptions": [
{
"kind": "agent_pulse.live_drift_shell_exfil",
"fingerprint": "a1b2c3...",
"approvedAt": "2026-05-23T22:00:00.000Z",
"note": "approved by user via TUI"
}
]
}

Notifications

--notify <mode> fires a local notification when any session transitions into drifting or stuck.

ModeEffect
noneSilent
bellWrites \x07 to stderr
osNative notification: osascript on macOS, notify-send on Linux, BurntToast/NotifyIcon on Windows
bothBell + OS

Best-effort: missing OS notification utilities are a silent no-op rather than a crash.

CI integration

GitHub Action

- uses: Conalh/AgentPulse@v0.8.1with:
transcript-dirs: agentpulse-transcriptsstrict: 'true'redact: pathscomment-on-pr: 'true'github-token: ${{ github.token }}

The action runs agentpulse live --once against the provided transcript directory, writes a markdown summary to the GitHub step summary, optionally posts a sticky PR comment, and fails the workflow when strict: true and any session is drifting or stuck.

By default, the gate fails closed on analysis errors (fail-on-error: true) and hosted output reduces paths to basenames (redact: paths). Set fail-on-error: false to make unreadable or corrupt transcripts advisory. Use redact: none only for trusted private output, or redact: all when project labels, narratives, and topic keywords may also be sensitive. Other inputs include max-depth and exclude for bounded discovery, plus hide-idle, max-sessions, no-detectors, and show-subagents.

Sticky PR comments require pull-requests: write permission and an explicit github-token: ${{ github.token }} input. Omit both when the step summary is enough.

Supply chain. The Action builds and executes the checked-out Action source with npm ci --ignore-scripts, using the committed lockfile. Pin to a full commit SHA when you need AgentPulse's code and dependency graph to be immutable. npm remains the download source for the lockfile-pinned packages.

Privacy. The step summary and PR comment contain transcript-derived project labels, verdicts, drift counts, narratives, and topic keywords. Paths are reduced to basenames by default, but labels and narrative text remain. Use redact: all when those fields may be sensitive, and never publish raw transcript artifacts or unreviewed JSON snapshots.

Raw CLI

npx @conalh/agentpulse@latest live --once --strict --roots <transcript-dir>

Add --format json to pipe a structured snapshot into downstream tools.

agentpulse recap — single-transcript mode

agentpulse recap --transcript-dir ~/.claude/projects/<your-project>/ --format json

Same pipeline, narrower input. Use --watch for a polling re-emit loop.

What's intentionally not in scope

  • No LLM, anywhere. Not for summarization, not for classification.
  • No outbound network calls. Reads local transcript files, writes to terminal and optional local notifier.
  • No web UI / hosted dashboard. TUI for live view, GitHub Action for PRs, JSON output for everything else.
  • No multi-session memory. Each invocation reads the window and exits.
  • No “AI to review AI” loop. Detectors are deterministic.

Architecture

Deterministic pipeline. Each layer is pure where it can be; all layers share the src/types.ts contract.

LayerFileInput -> Output
1. Parseragent-gov-core/parsers/ (v1.1.0+)Claude Code / Cursor / Codex / Antigravity JSONL -> TranscriptEvent[]
2. Enrichmentsrc/enrich.tsevents -> keywords, cwd-relative path clusters, action classes
2.5. Sequencessrc/sequences.tsevents -> ordered-pattern signal (tdd_loop / stuck_loop / refuse_to_verify / exploratory_edit)
3. Outcomesrc/trajectory.tsevents -> verification trend, user tone, completion verbs, idle gap
4. Trajectorysrc/trajectory.tsenrichment + outcome + sequence + exceptions -> six-bucket verdict
5. Narrativesrc/narrative.tsverdict -> plain-English recap

Live infrastructure on top: src/sessions/ discovery/watcher, src/orchestrator.ts, src/exceptions.ts, src/notifications.ts, src/once.ts, and src/tui/.

Design choices worth flagging

  • Local by default. Zero network calls in any code path.
  • Deterministic. Same transcript window in, same verdict out. No model drift, API outages, or rate limits.
  • Live observation. AgentPulse reports the trajectory of active local sessions and provides a one-shot format for CI artifacts.
  • Substrate-built. Uses agent-gov-core primitives where the parser and report contracts overlap.
  • Tested. 296 tests (npm test), including hand-rolled property tests (a seeded, replayable PRNG with 200 iterations per invariant) over the pure classifier layers and a labeled golden-replay corpus of 13 transcript fixtures spanning all six trajectory buckets across Claude Code, Cursor, Codex, and Antigravity.

Windows terminal note

If you're running on Windows, prefer Windows Terminal over legacy cmd.exe. It works on cmd.exe, but Windows Terminal renders the dashboard more cleanly.

Related public tools

RepoWhat it catches
wardenDeterministic allow / deny / ask policy decisions.
CapabilityEchoCapability drift introduced by code, manifests, workflows, and Dockerfiles.
AgentPulse(this repo)Live local trajectory verdicts for active agent sessions.
agent-gov-coreShared parsers, the canonical Finding schema, and mergeFindings.

MIT.

About

Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - Conalh/AgentPulse: Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action. · GitHub
Skip to content

Repository files navigation

AgentPulse

npm versionCILicense: MITClaude CodeCursorCodexNo LLMLocal-only

A live trajectory dashboard for AI coding agent sessions. AgentPulse watches local Claude Code, Cursor, and Codex transcripts and classifies what each agent is doing right now: converging, exploring, stuck, done, drifting, or idle.

AgentPulse dashboard — a selected session cycles through converging, stuck, and drifting verdicts in real time

Drop it in a terminal next to your editor. You get an always-on read across local agent sessions without a model judge, hosted dashboard, telemetry, or outbound network calls.

Requires Node.js 22 or newer.

npx @conalh/agentpulse@latest live
flowchart LR
Claude["Claude Code<br/>transcripts"] --> Pulse
Cursor["Cursor<br/>transcripts"] --> Pulse
Codex["Codex<br/>sessions"] --> Pulse
Pulse[("AgentPulse<br/>local trajectory classifier")] --> TUI["Live TUI<br/>verdict + narrative"]
Pulse --> CI["CI snapshot<br/>--once --strict"]
TUI --> User["Human sees<br/>converging · stuck · drifting"]
classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px
classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe
class Claude,Cursor,Codex input
class Pulse engine
class TUI,CI,User output
Loading

Sample narrative on a converging session:

Your agent has been working on the login bug for 18 minutes. It focused
on `src/auth/`, made 3 changes to `session.ts`, and ran the tests after
each change. Tests went from failing to passing. Looks like it solved it.
Verdict: ● converging (confidence 0.85)

See also:warden for deterministic runtime policy, CapabilityEcho for PR-time capability drift, and agent-gov-core for the shared transcript and report primitives.

Where this fits

AgentPulse is the suite's live-observation layer — it watches a running session's trajectory rather than a diff or a finished transcript.

ToolInputCatches / decidesOutputUse when
wardenpolicy + tool actionallow / deny / askverdictyou need deterministic runtime policy decisions
CapabilityEchoPR diffnew executable capabilityannotations + reportcode gains network/subprocess/eval/lifecycle/workflow power
AgentPulselive session eventstrajectory stateterminal dashboardyou want live session observation
agent-gov-coreshared schemas/parserscommon Finding/Report modelnpm librarytools need shared report primitives

Why this exists

AI agents can run for a long time while the human is not staring at every tool call. Sometimes they converge. Sometimes they explore. Sometimes they get stuck in the same failed loop. Sometimes they touch a privileged path, pipe a network fetch straight into a shell, or write outside the repo.

AgentPulse exists to make that session state visible while the work is happening. It is deterministic local signal, not an LLM judging another LLM.

What it shows

SignalWhat it means
convergingFocused edits plus verification, often with tests moving toward passing.
exploringReading and orientation, no meaningful edit trajectory yet.
stuckRepeated edits/tests/failures, user pushback, or loop-like behavior.
doneCompletion language plus idle gap.
driftingA deterministic drift rule fired (privileged-path access, a shell-piped network fetch like curl … | sh, or a write outside the repo root). See Drift detection scope.
idleActivity has gone quiet or the window had no recent movement.

Drift detection scope.The drifting bucket is a deterministic first-pass detector, not a comprehensive agent-safety scanner. It currently fires on exactly three rule families:

  • Privileged-path access — a tool input path, or a path-shaped token inside a shell command (quote-aware, including bash -c "…" payloads), touching .ssh, .aws, .kube, /etc/shadow, or /private/var.
  • Shell-piped network fetchcurl/wget piped into sh/bash/zsh. Detection is tokenizer-based (agent-gov-core's quote-aware tokenizeShell), so quoted URLs (curl "https://…" | sh) are caught while a curl … | sh quoted inside another command's argument (e.g. gh release create --notes "…") is not a false positive.
  • Write outside the repo root — a Write/Edit to a path outside the session's cwd (or, with no repo root, outside /tmp, /var, ~). ./.. segments are resolved lexically before comparison and ~-prefixed paths count as outside; symlink escapes are not resolved.

So drifting means "a known risky pattern fired" — not "this session is safe." It does not yet cover bash <(curl …), curl -o … && sh …, bash -c "curl … | sh" nesting, PowerShell iwr | iex, python -c/node -e download-and-exec, package install hooks, credential exfiltration through ordinary files, or API-driven network actions. Treat a clean run as "none of the implemented rules matched," and pair AgentPulse with the rest of the agent-gov suite for deeper gating.

What makes it different

Several tools watch agent sessions. AgentPulse's wedge is the specific combination none of them cover:

Local-onlyNo LLMTrajectory verdictPer-session live dashboardPR gate
LangSmith / Langfuse / AgentOps❌ cloud❌ LLM-judge❌ traces only
Claude Code Session Memory❌ LLM⚠ structured
agenttrace❌ metrics only⚠ TUI
AgentPulse

The wedge is the combination. AgentPulse pairs naturally with agenttrace for cost/health metrics and with the rest of the agent-gov suite for PR-time gates.

agentpulse live — the live dashboard

agentpulse live [options]

Options:

FlagDefaultEffect
--window <duration>20mRecap window per session (5m, 1h, etc.)
--refresh <duration>30sBackground refresh cadence. Watcher fires sub-second on file changes regardless.
--roots <p1,p2,...>platform defaultsOverride discovery roots (comma-separated)
--stale <duration>1hSkip sessions older than this
--max-depth <N>unboundedCap discovery recursion depth below each root
--exclude <d1,d2,…>noneDirectory names to skip during discovery (e.g. node_modules,.git)
--hide-idleoffHide sessions with no activity in the window (also honored with --once)
--max-sessions <N>10Cap the list. With --once this is display-only — gating still considers every session.
--show-subagentsoffInclude agent-<hex> SDK-spawned subagent transcripts
--no-detectorsoffSkip the drifting bucket entirely
--onceoffHeadless snapshot mode. Runs once, prints, exits.
--format <fmt>textWith --once: text or json.
--strictoffWith --once: exit 1 if any session is drifting or stuck.
--fail-on-erroroffWith --once --strict: also exit 1 if a session failed to analyze (unreadable/corrupt transcript).
--redact <mode>noneRedact transcript-derived paths from --once output: none, paths, all.
--notify <mode>noneLocal notification on transition into drifting/stuck: none, bell, os, both.

Keyboard:

KeyAction
↑ ↓ / k j / w sMove selection
rForce refresh on selected session
aWhitelist current session's drift findings; preview first, confirm within 3s
nName / rename the selected session alias
?Toggle help overlay
q / Ctrl-CQuit

Agent aliases

When multiple agents work on the same project, the dashboard rows can look identical until you name them. Press n on a selected row, type an alias like CC1, frontend, or backend, and press Enter.

Aliases live in two optional JSON files:

  • <session.cwd>/.agentpulse-aliases.json — per-project, commit if you want team-shared conventions.
  • ~/.agentpulse/aliases.json — personal default.
{
"version": 1,
"aliases": {
"c3d4566ef4c5": "CC1",
"7a8b91234567": "CG1"
}
}

Exception baseline

Press a on a drifting session to preview the drift findings, then press a again within the confirmation window to append the selected fingerprints to <session.cwd>/.agentpulse-exceptions.json. AgentPulse refreshes and re-classifies the session immediately.

Commit the exception file to your repo when the behavior is intentionally approved. CI gating (agentpulse live --once --strict or the GitHub Action) honors the same baseline.

{
"version": 1,
"exceptions": [
{
"kind": "agent_pulse.live_drift_shell_exfil",
"fingerprint": "a1b2c3...",
"approvedAt": "2026-05-23T22:00:00.000Z",
"note": "approved by user via TUI"
}
]
}

Notifications

--notify <mode> fires a local notification when any session transitions into drifting or stuck.

ModeEffect
noneSilent
bellWrites \x07 to stderr
osNative notification: osascript on macOS, notify-send on Linux, BurntToast/NotifyIcon on Windows
bothBell + OS

Best-effort: missing OS notification utilities are a silent no-op rather than a crash.

CI integration

GitHub Action

- uses: Conalh/AgentPulse@v0.8.1with:
transcript-dirs: agentpulse-transcriptsstrict: 'true'redact: pathscomment-on-pr: 'true'github-token: ${{ github.token }}

The action runs agentpulse live --once against the provided transcript directory, writes a markdown summary to the GitHub step summary, optionally posts a sticky PR comment, and fails the workflow when strict: true and any session is drifting or stuck.

By default, the gate fails closed on analysis errors (fail-on-error: true) and hosted output reduces paths to basenames (redact: paths). Set fail-on-error: false to make unreadable or corrupt transcripts advisory. Use redact: none only for trusted private output, or redact: all when project labels, narratives, and topic keywords may also be sensitive. Other inputs include max-depth and exclude for bounded discovery, plus hide-idle, max-sessions, no-detectors, and show-subagents.

Sticky PR comments require pull-requests: write permission and an explicit github-token: ${{ github.token }} input. Omit both when the step summary is enough.

Supply chain. The Action builds and executes the checked-out Action source with npm ci --ignore-scripts, using the committed lockfile. Pin to a full commit SHA when you need AgentPulse's code and dependency graph to be immutable. npm remains the download source for the lockfile-pinned packages.

Privacy. The step summary and PR comment contain transcript-derived project labels, verdicts, drift counts, narratives, and topic keywords. Paths are reduced to basenames by default, but labels and narrative text remain. Use redact: all when those fields may be sensitive, and never publish raw transcript artifacts or unreviewed JSON snapshots.

Raw CLI

npx @conalh/agentpulse@latest live --once --strict --roots <transcript-dir>

Add --format json to pipe a structured snapshot into downstream tools.

agentpulse recap — single-transcript mode

agentpulse recap --transcript-dir ~/.claude/projects/<your-project>/ --format json

Same pipeline, narrower input. Use --watch for a polling re-emit loop.

What's intentionally not in scope

  • No LLM, anywhere. Not for summarization, not for classification.
  • No outbound network calls. Reads local transcript files, writes to terminal and optional local notifier.
  • No web UI / hosted dashboard. TUI for live view, GitHub Action for PRs, JSON output for everything else.
  • No multi-session memory. Each invocation reads the window and exits.
  • No “AI to review AI” loop. Detectors are deterministic.

Architecture

Deterministic pipeline. Each layer is pure where it can be; all layers share the src/types.ts contract.

LayerFileInput -> Output
1. Parseragent-gov-core/parsers/ (v1.1.0+)Claude Code / Cursor / Codex / Antigravity JSONL -> TranscriptEvent[]
2. Enrichmentsrc/enrich.tsevents -> keywords, cwd-relative path clusters, action classes
2.5. Sequencessrc/sequences.tsevents -> ordered-pattern signal (tdd_loop / stuck_loop / refuse_to_verify / exploratory_edit)
3. Outcomesrc/trajectory.tsevents -> verification trend, user tone, completion verbs, idle gap
4. Trajectorysrc/trajectory.tsenrichment + outcome + sequence + exceptions -> six-bucket verdict
5. Narrativesrc/narrative.tsverdict -> plain-English recap

Live infrastructure on top: src/sessions/ discovery/watcher, src/orchestrator.ts, src/exceptions.ts, src/notifications.ts, src/once.ts, and src/tui/.

Design choices worth flagging

  • Local by default. Zero network calls in any code path.
  • Deterministic. Same transcript window in, same verdict out. No model drift, API outages, or rate limits.
  • Live observation. AgentPulse reports the trajectory of active local sessions and provides a one-shot format for CI artifacts.
  • Substrate-built. Uses agent-gov-core primitives where the parser and report contracts overlap.
  • Tested. 296 tests (npm test), including hand-rolled property tests (a seeded, replayable PRNG with 200 iterations per invariant) over the pure classifier layers and a labeled golden-replay corpus of 13 transcript fixtures spanning all six trajectory buckets across Claude Code, Cursor, Codex, and Antigravity.

Windows terminal note

If you're running on Windows, prefer Windows Terminal over legacy cmd.exe. It works on cmd.exe, but Windows Terminal renders the dashboard more cleanly.

Related public tools

RepoWhat it catches
wardenDeterministic allow / deny / ask policy decisions.
CapabilityEchoCapability drift introduced by code, manifests, workflows, and Dockerfiles.
AgentPulse(this repo)Live local trajectory verdicts for active agent sessions.
agent-gov-coreShared parsers, the canonical Finding schema, and mergeFindings.

MIT.

About

Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - Conalh/AgentPulse: Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action. · GitHub
Skip to content

Repository files navigation

AgentPulse

npm versionCILicense: MITClaude CodeCursorCodexNo LLMLocal-only

A live trajectory dashboard for AI coding agent sessions. AgentPulse watches local Claude Code, Cursor, and Codex transcripts and classifies what each agent is doing right now: converging, exploring, stuck, done, drifting, or idle.

AgentPulse dashboard — a selected session cycles through converging, stuck, and drifting verdicts in real time

Drop it in a terminal next to your editor. You get an always-on read across local agent sessions without a model judge, hosted dashboard, telemetry, or outbound network calls.

Requires Node.js 22 or newer.

npx @conalh/agentpulse@latest live
flowchart LR
Claude["Claude Code<br/>transcripts"] --> Pulse
Cursor["Cursor<br/>transcripts"] --> Pulse
Codex["Codex<br/>sessions"] --> Pulse
Pulse[("AgentPulse<br/>local trajectory classifier")] --> TUI["Live TUI<br/>verdict + narrative"]
Pulse --> CI["CI snapshot<br/>--once --strict"]
TUI --> User["Human sees<br/>converging · stuck · drifting"]
classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px
classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe
class Claude,Cursor,Codex input
class Pulse engine
class TUI,CI,User output
Loading

Sample narrative on a converging session:

Your agent has been working on the login bug for 18 minutes. It focused
on `src/auth/`, made 3 changes to `session.ts`, and ran the tests after
each change. Tests went from failing to passing. Looks like it solved it.
Verdict: ● converging (confidence 0.85)

See also:warden for deterministic runtime policy, CapabilityEcho for PR-time capability drift, and agent-gov-core for the shared transcript and report primitives.

Where this fits

AgentPulse is the suite's live-observation layer — it watches a running session's trajectory rather than a diff or a finished transcript.

ToolInputCatches / decidesOutputUse when
wardenpolicy + tool actionallow / deny / askverdictyou need deterministic runtime policy decisions
CapabilityEchoPR diffnew executable capabilityannotations + reportcode gains network/subprocess/eval/lifecycle/workflow power
AgentPulselive session eventstrajectory stateterminal dashboardyou want live session observation
agent-gov-coreshared schemas/parserscommon Finding/Report modelnpm librarytools need shared report primitives

Why this exists

AI agents can run for a long time while the human is not staring at every tool call. Sometimes they converge. Sometimes they explore. Sometimes they get stuck in the same failed loop. Sometimes they touch a privileged path, pipe a network fetch straight into a shell, or write outside the repo.

AgentPulse exists to make that session state visible while the work is happening. It is deterministic local signal, not an LLM judging another LLM.

What it shows

SignalWhat it means
convergingFocused edits plus verification, often with tests moving toward passing.
exploringReading and orientation, no meaningful edit trajectory yet.
stuckRepeated edits/tests/failures, user pushback, or loop-like behavior.
doneCompletion language plus idle gap.
driftingA deterministic drift rule fired (privileged-path access, a shell-piped network fetch like curl … | sh, or a write outside the repo root). See Drift detection scope.
idleActivity has gone quiet or the window had no recent movement.

Drift detection scope.The drifting bucket is a deterministic first-pass detector, not a comprehensive agent-safety scanner. It currently fires on exactly three rule families:

  • Privileged-path access — a tool input path, or a path-shaped token inside a shell command (quote-aware, including bash -c "…" payloads), touching .ssh, .aws, .kube, /etc/shadow, or /private/var.
  • Shell-piped network fetchcurl/wget piped into sh/bash/zsh. Detection is tokenizer-based (agent-gov-core's quote-aware tokenizeShell), so quoted URLs (curl "https://…" | sh) are caught while a curl … | sh quoted inside another command's argument (e.g. gh release create --notes "…") is not a false positive.
  • Write outside the repo root — a Write/Edit to a path outside the session's cwd (or, with no repo root, outside /tmp, /var, ~). ./.. segments are resolved lexically before comparison and ~-prefixed paths count as outside; symlink escapes are not resolved.

So drifting means "a known risky pattern fired" — not "this session is safe." It does not yet cover bash <(curl …), curl -o … && sh …, bash -c "curl … | sh" nesting, PowerShell iwr | iex, python -c/node -e download-and-exec, package install hooks, credential exfiltration through ordinary files, or API-driven network actions. Treat a clean run as "none of the implemented rules matched," and pair AgentPulse with the rest of the agent-gov suite for deeper gating.

What makes it different

Several tools watch agent sessions. AgentPulse's wedge is the specific combination none of them cover:

Local-onlyNo LLMTrajectory verdictPer-session live dashboardPR gate
LangSmith / Langfuse / AgentOps❌ cloud❌ LLM-judge❌ traces only
Claude Code Session Memory❌ LLM⚠ structured
agenttrace❌ metrics only⚠ TUI
AgentPulse

The wedge is the combination. AgentPulse pairs naturally with agenttrace for cost/health metrics and with the rest of the agent-gov suite for PR-time gates.

agentpulse live — the live dashboard

agentpulse live [options]

Options:

FlagDefaultEffect
--window <duration>20mRecap window per session (5m, 1h, etc.)
--refresh <duration>30sBackground refresh cadence. Watcher fires sub-second on file changes regardless.
--roots <p1,p2,...>platform defaultsOverride discovery roots (comma-separated)
--stale <duration>1hSkip sessions older than this
--max-depth <N>unboundedCap discovery recursion depth below each root
--exclude <d1,d2,…>noneDirectory names to skip during discovery (e.g. node_modules,.git)
--hide-idleoffHide sessions with no activity in the window (also honored with --once)
--max-sessions <N>10Cap the list. With --once this is display-only — gating still considers every session.
--show-subagentsoffInclude agent-<hex> SDK-spawned subagent transcripts
--no-detectorsoffSkip the drifting bucket entirely
--onceoffHeadless snapshot mode. Runs once, prints, exits.
--format <fmt>textWith --once: text or json.
--strictoffWith --once: exit 1 if any session is drifting or stuck.
--fail-on-erroroffWith --once --strict: also exit 1 if a session failed to analyze (unreadable/corrupt transcript).
--redact <mode>noneRedact transcript-derived paths from --once output: none, paths, all.
--notify <mode>noneLocal notification on transition into drifting/stuck: none, bell, os, both.

Keyboard:

KeyAction
↑ ↓ / k j / w sMove selection
rForce refresh on selected session
aWhitelist current session's drift findings; preview first, confirm within 3s
nName / rename the selected session alias
?Toggle help overlay
q / Ctrl-CQuit

Agent aliases

When multiple agents work on the same project, the dashboard rows can look identical until you name them. Press n on a selected row, type an alias like CC1, frontend, or backend, and press Enter.

Aliases live in two optional JSON files:

  • <session.cwd>/.agentpulse-aliases.json — per-project, commit if you want team-shared conventions.
  • ~/.agentpulse/aliases.json — personal default.
{
"version": 1,
"aliases": {
"c3d4566ef4c5": "CC1",
"7a8b91234567": "CG1"
}
}

Exception baseline

Press a on a drifting session to preview the drift findings, then press a again within the confirmation window to append the selected fingerprints to <session.cwd>/.agentpulse-exceptions.json. AgentPulse refreshes and re-classifies the session immediately.

Commit the exception file to your repo when the behavior is intentionally approved. CI gating (agentpulse live --once --strict or the GitHub Action) honors the same baseline.

{
"version": 1,
"exceptions": [
{
"kind": "agent_pulse.live_drift_shell_exfil",
"fingerprint": "a1b2c3...",
"approvedAt": "2026-05-23T22:00:00.000Z",
"note": "approved by user via TUI"
}
]
}

Notifications

--notify <mode> fires a local notification when any session transitions into drifting or stuck.

ModeEffect
noneSilent
bellWrites \x07 to stderr
osNative notification: osascript on macOS, notify-send on Linux, BurntToast/NotifyIcon on Windows
bothBell + OS

Best-effort: missing OS notification utilities are a silent no-op rather than a crash.

CI integration

GitHub Action

- uses: Conalh/AgentPulse@v0.8.1with:
transcript-dirs: agentpulse-transcriptsstrict: 'true'redact: pathscomment-on-pr: 'true'github-token: ${{ github.token }}

The action runs agentpulse live --once against the provided transcript directory, writes a markdown summary to the GitHub step summary, optionally posts a sticky PR comment, and fails the workflow when strict: true and any session is drifting or stuck.

By default, the gate fails closed on analysis errors (fail-on-error: true) and hosted output reduces paths to basenames (redact: paths). Set fail-on-error: false to make unreadable or corrupt transcripts advisory. Use redact: none only for trusted private output, or redact: all when project labels, narratives, and topic keywords may also be sensitive. Other inputs include max-depth and exclude for bounded discovery, plus hide-idle, max-sessions, no-detectors, and show-subagents.

Sticky PR comments require pull-requests: write permission and an explicit github-token: ${{ github.token }} input. Omit both when the step summary is enough.

Supply chain. The Action builds and executes the checked-out Action source with npm ci --ignore-scripts, using the committed lockfile. Pin to a full commit SHA when you need AgentPulse's code and dependency graph to be immutable. npm remains the download source for the lockfile-pinned packages.

Privacy. The step summary and PR comment contain transcript-derived project labels, verdicts, drift counts, narratives, and topic keywords. Paths are reduced to basenames by default, but labels and narrative text remain. Use redact: all when those fields may be sensitive, and never publish raw transcript artifacts or unreviewed JSON snapshots.

Raw CLI

npx @conalh/agentpulse@latest live --once --strict --roots <transcript-dir>

Add --format json to pipe a structured snapshot into downstream tools.

agentpulse recap — single-transcript mode

agentpulse recap --transcript-dir ~/.claude/projects/<your-project>/ --format json

Same pipeline, narrower input. Use --watch for a polling re-emit loop.

What's intentionally not in scope

  • No LLM, anywhere. Not for summarization, not for classification.
  • No outbound network calls. Reads local transcript files, writes to terminal and optional local notifier.
  • No web UI / hosted dashboard. TUI for live view, GitHub Action for PRs, JSON output for everything else.
  • No multi-session memory. Each invocation reads the window and exits.
  • No “AI to review AI” loop. Detectors are deterministic.

Architecture

Deterministic pipeline. Each layer is pure where it can be; all layers share the src/types.ts contract.

LayerFileInput -> Output
1. Parseragent-gov-core/parsers/ (v1.1.0+)Claude Code / Cursor / Codex / Antigravity JSONL -> TranscriptEvent[]
2. Enrichmentsrc/enrich.tsevents -> keywords, cwd-relative path clusters, action classes
2.5. Sequencessrc/sequences.tsevents -> ordered-pattern signal (tdd_loop / stuck_loop / refuse_to_verify / exploratory_edit)
3. Outcomesrc/trajectory.tsevents -> verification trend, user tone, completion verbs, idle gap
4. Trajectorysrc/trajectory.tsenrichment + outcome + sequence + exceptions -> six-bucket verdict
5. Narrativesrc/narrative.tsverdict -> plain-English recap

Live infrastructure on top: src/sessions/ discovery/watcher, src/orchestrator.ts, src/exceptions.ts, src/notifications.ts, src/once.ts, and src/tui/.

Design choices worth flagging

  • Local by default. Zero network calls in any code path.
  • Deterministic. Same transcript window in, same verdict out. No model drift, API outages, or rate limits.
  • Live observation. AgentPulse reports the trajectory of active local sessions and provides a one-shot format for CI artifacts.
  • Substrate-built. Uses agent-gov-core primitives where the parser and report contracts overlap.
  • Tested. 296 tests (npm test), including hand-rolled property tests (a seeded, replayable PRNG with 200 iterations per invariant) over the pure classifier layers and a labeled golden-replay corpus of 13 transcript fixtures spanning all six trajectory buckets across Claude Code, Cursor, Codex, and Antigravity.

Windows terminal note

If you're running on Windows, prefer Windows Terminal over legacy cmd.exe. It works on cmd.exe, but Windows Terminal renders the dashboard more cleanly.

Related public tools

RepoWhat it catches
wardenDeterministic allow / deny / ask policy decisions.
CapabilityEchoCapability drift introduced by code, manifests, workflows, and Dockerfiles.
AgentPulse(this repo)Live local trajectory verdicts for active agent sessions.
agent-gov-coreShared parsers, the canonical Finding schema, and mergeFindings.

MIT.

About

Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - Conalh/AgentPulse: Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action. · GitHub
Skip to content

Repository files navigation

AgentPulse

npm versionCILicense: MITClaude CodeCursorCodexNo LLMLocal-only

A live trajectory dashboard for AI coding agent sessions. AgentPulse watches local Claude Code, Cursor, and Codex transcripts and classifies what each agent is doing right now: converging, exploring, stuck, done, drifting, or idle.

AgentPulse dashboard — a selected session cycles through converging, stuck, and drifting verdicts in real time

Drop it in a terminal next to your editor. You get an always-on read across local agent sessions without a model judge, hosted dashboard, telemetry, or outbound network calls.

Requires Node.js 22 or newer.

npx @conalh/agentpulse@latest live
flowchart LR
Claude["Claude Code<br/>transcripts"] --> Pulse
Cursor["Cursor<br/>transcripts"] --> Pulse
Codex["Codex<br/>sessions"] --> Pulse
Pulse[("AgentPulse<br/>local trajectory classifier")] --> TUI["Live TUI<br/>verdict + narrative"]
Pulse --> CI["CI snapshot<br/>--once --strict"]
TUI --> User["Human sees<br/>converging · stuck · drifting"]
classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px
classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe
class Claude,Cursor,Codex input
class Pulse engine
class TUI,CI,User output
Loading

Sample narrative on a converging session:

Your agent has been working on the login bug for 18 minutes. It focused
on `src/auth/`, made 3 changes to `session.ts`, and ran the tests after
each change. Tests went from failing to passing. Looks like it solved it.
Verdict: ● converging (confidence 0.85)

See also:warden for deterministic runtime policy, CapabilityEcho for PR-time capability drift, and agent-gov-core for the shared transcript and report primitives.

Where this fits

AgentPulse is the suite's live-observation layer — it watches a running session's trajectory rather than a diff or a finished transcript.

ToolInputCatches / decidesOutputUse when
wardenpolicy + tool actionallow / deny / askverdictyou need deterministic runtime policy decisions
CapabilityEchoPR diffnew executable capabilityannotations + reportcode gains network/subprocess/eval/lifecycle/workflow power
AgentPulselive session eventstrajectory stateterminal dashboardyou want live session observation
agent-gov-coreshared schemas/parserscommon Finding/Report modelnpm librarytools need shared report primitives

Why this exists

AI agents can run for a long time while the human is not staring at every tool call. Sometimes they converge. Sometimes they explore. Sometimes they get stuck in the same failed loop. Sometimes they touch a privileged path, pipe a network fetch straight into a shell, or write outside the repo.

AgentPulse exists to make that session state visible while the work is happening. It is deterministic local signal, not an LLM judging another LLM.

What it shows

SignalWhat it means
convergingFocused edits plus verification, often with tests moving toward passing.
exploringReading and orientation, no meaningful edit trajectory yet.
stuckRepeated edits/tests/failures, user pushback, or loop-like behavior.
doneCompletion language plus idle gap.
driftingA deterministic drift rule fired (privileged-path access, a shell-piped network fetch like curl … | sh, or a write outside the repo root). See Drift detection scope.
idleActivity has gone quiet or the window had no recent movement.

Drift detection scope.The drifting bucket is a deterministic first-pass detector, not a comprehensive agent-safety scanner. It currently fires on exactly three rule families:

  • Privileged-path access — a tool input path, or a path-shaped token inside a shell command (quote-aware, including bash -c "…" payloads), touching .ssh, .aws, .kube, /etc/shadow, or /private/var.
  • Shell-piped network fetchcurl/wget piped into sh/bash/zsh. Detection is tokenizer-based (agent-gov-core's quote-aware tokenizeShell), so quoted URLs (curl "https://…" | sh) are caught while a curl … | sh quoted inside another command's argument (e.g. gh release create --notes "…") is not a false positive.
  • Write outside the repo root — a Write/Edit to a path outside the session's cwd (or, with no repo root, outside /tmp, /var, ~). ./.. segments are resolved lexically before comparison and ~-prefixed paths count as outside; symlink escapes are not resolved.

So drifting means "a known risky pattern fired" — not "this session is safe." It does not yet cover bash <(curl …), curl -o … && sh …, bash -c "curl … | sh" nesting, PowerShell iwr | iex, python -c/node -e download-and-exec, package install hooks, credential exfiltration through ordinary files, or API-driven network actions. Treat a clean run as "none of the implemented rules matched," and pair AgentPulse with the rest of the agent-gov suite for deeper gating.

What makes it different

Several tools watch agent sessions. AgentPulse's wedge is the specific combination none of them cover:

Local-onlyNo LLMTrajectory verdictPer-session live dashboardPR gate
LangSmith / Langfuse / AgentOps❌ cloud❌ LLM-judge❌ traces only
Claude Code Session Memory❌ LLM⚠ structured
agenttrace❌ metrics only⚠ TUI
AgentPulse

The wedge is the combination. AgentPulse pairs naturally with agenttrace for cost/health metrics and with the rest of the agent-gov suite for PR-time gates.

agentpulse live — the live dashboard

agentpulse live [options]

Options:

FlagDefaultEffect
--window <duration>20mRecap window per session (5m, 1h, etc.)
--refresh <duration>30sBackground refresh cadence. Watcher fires sub-second on file changes regardless.
--roots <p1,p2,...>platform defaultsOverride discovery roots (comma-separated)
--stale <duration>1hSkip sessions older than this
--max-depth <N>unboundedCap discovery recursion depth below each root
--exclude <d1,d2,…>noneDirectory names to skip during discovery (e.g. node_modules,.git)
--hide-idleoffHide sessions with no activity in the window (also honored with --once)
--max-sessions <N>10Cap the list. With --once this is display-only — gating still considers every session.
--show-subagentsoffInclude agent-<hex> SDK-spawned subagent transcripts
--no-detectorsoffSkip the drifting bucket entirely
--onceoffHeadless snapshot mode. Runs once, prints, exits.
--format <fmt>textWith --once: text or json.
--strictoffWith --once: exit 1 if any session is drifting or stuck.
--fail-on-erroroffWith --once --strict: also exit 1 if a session failed to analyze (unreadable/corrupt transcript).
--redact <mode>noneRedact transcript-derived paths from --once output: none, paths, all.
--notify <mode>noneLocal notification on transition into drifting/stuck: none, bell, os, both.

Keyboard:

KeyAction
↑ ↓ / k j / w sMove selection
rForce refresh on selected session
aWhitelist current session's drift findings; preview first, confirm within 3s
nName / rename the selected session alias
?Toggle help overlay
q / Ctrl-CQuit

Agent aliases

When multiple agents work on the same project, the dashboard rows can look identical until you name them. Press n on a selected row, type an alias like CC1, frontend, or backend, and press Enter.

Aliases live in two optional JSON files:

  • <session.cwd>/.agentpulse-aliases.json — per-project, commit if you want team-shared conventions.
  • ~/.agentpulse/aliases.json — personal default.
{
"version": 1,
"aliases": {
"c3d4566ef4c5": "CC1",
"7a8b91234567": "CG1"
}
}

Exception baseline

Press a on a drifting session to preview the drift findings, then press a again within the confirmation window to append the selected fingerprints to <session.cwd>/.agentpulse-exceptions.json. AgentPulse refreshes and re-classifies the session immediately.

Commit the exception file to your repo when the behavior is intentionally approved. CI gating (agentpulse live --once --strict or the GitHub Action) honors the same baseline.

{
"version": 1,
"exceptions": [
{
"kind": "agent_pulse.live_drift_shell_exfil",
"fingerprint": "a1b2c3...",
"approvedAt": "2026-05-23T22:00:00.000Z",
"note": "approved by user via TUI"
}
]
}

Notifications

--notify <mode> fires a local notification when any session transitions into drifting or stuck.

ModeEffect
noneSilent
bellWrites \x07 to stderr
osNative notification: osascript on macOS, notify-send on Linux, BurntToast/NotifyIcon on Windows
bothBell + OS

Best-effort: missing OS notification utilities are a silent no-op rather than a crash.

CI integration

GitHub Action

- uses: Conalh/AgentPulse@v0.8.1with:
transcript-dirs: agentpulse-transcriptsstrict: 'true'redact: pathscomment-on-pr: 'true'github-token: ${{ github.token }}

The action runs agentpulse live --once against the provided transcript directory, writes a markdown summary to the GitHub step summary, optionally posts a sticky PR comment, and fails the workflow when strict: true and any session is drifting or stuck.

By default, the gate fails closed on analysis errors (fail-on-error: true) and hosted output reduces paths to basenames (redact: paths). Set fail-on-error: false to make unreadable or corrupt transcripts advisory. Use redact: none only for trusted private output, or redact: all when project labels, narratives, and topic keywords may also be sensitive. Other inputs include max-depth and exclude for bounded discovery, plus hide-idle, max-sessions, no-detectors, and show-subagents.

Sticky PR comments require pull-requests: write permission and an explicit github-token: ${{ github.token }} input. Omit both when the step summary is enough.

Supply chain. The Action builds and executes the checked-out Action source with npm ci --ignore-scripts, using the committed lockfile. Pin to a full commit SHA when you need AgentPulse's code and dependency graph to be immutable. npm remains the download source for the lockfile-pinned packages.

Privacy. The step summary and PR comment contain transcript-derived project labels, verdicts, drift counts, narratives, and topic keywords. Paths are reduced to basenames by default, but labels and narrative text remain. Use redact: all when those fields may be sensitive, and never publish raw transcript artifacts or unreviewed JSON snapshots.

Raw CLI

npx @conalh/agentpulse@latest live --once --strict --roots <transcript-dir>

Add --format json to pipe a structured snapshot into downstream tools.

agentpulse recap — single-transcript mode

agentpulse recap --transcript-dir ~/.claude/projects/<your-project>/ --format json

Same pipeline, narrower input. Use --watch for a polling re-emit loop.

What's intentionally not in scope

  • No LLM, anywhere. Not for summarization, not for classification.
  • No outbound network calls. Reads local transcript files, writes to terminal and optional local notifier.
  • No web UI / hosted dashboard. TUI for live view, GitHub Action for PRs, JSON output for everything else.
  • No multi-session memory. Each invocation reads the window and exits.
  • No “AI to review AI” loop. Detectors are deterministic.

Architecture

Deterministic pipeline. Each layer is pure where it can be; all layers share the src/types.ts contract.

LayerFileInput -> Output
1. Parseragent-gov-core/parsers/ (v1.1.0+)Claude Code / Cursor / Codex / Antigravity JSONL -> TranscriptEvent[]
2. Enrichmentsrc/enrich.tsevents -> keywords, cwd-relative path clusters, action classes
2.5. Sequencessrc/sequences.tsevents -> ordered-pattern signal (tdd_loop / stuck_loop / refuse_to_verify / exploratory_edit)
3. Outcomesrc/trajectory.tsevents -> verification trend, user tone, completion verbs, idle gap
4. Trajectorysrc/trajectory.tsenrichment + outcome + sequence + exceptions -> six-bucket verdict
5. Narrativesrc/narrative.tsverdict -> plain-English recap

Live infrastructure on top: src/sessions/ discovery/watcher, src/orchestrator.ts, src/exceptions.ts, src/notifications.ts, src/once.ts, and src/tui/.

Design choices worth flagging

  • Local by default. Zero network calls in any code path.
  • Deterministic. Same transcript window in, same verdict out. No model drift, API outages, or rate limits.
  • Live observation. AgentPulse reports the trajectory of active local sessions and provides a one-shot format for CI artifacts.
  • Substrate-built. Uses agent-gov-core primitives where the parser and report contracts overlap.
  • Tested. 296 tests (npm test), including hand-rolled property tests (a seeded, replayable PRNG with 200 iterations per invariant) over the pure classifier layers and a labeled golden-replay corpus of 13 transcript fixtures spanning all six trajectory buckets across Claude Code, Cursor, Codex, and Antigravity.

Windows terminal note

If you're running on Windows, prefer Windows Terminal over legacy cmd.exe. It works on cmd.exe, but Windows Terminal renders the dashboard more cleanly.

Related public tools

RepoWhat it catches
wardenDeterministic allow / deny / ask policy decisions.
CapabilityEchoCapability drift introduced by code, manifests, workflows, and Dockerfiles.
AgentPulse(this repo)Live local trajectory verdicts for active agent sessions.
agent-gov-coreShared parsers, the canonical Finding schema, and mergeFindings.

MIT.

About

Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - Conalh/AgentPulse: Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action. · GitHub
Skip to content

Repository files navigation

AgentPulse

npm versionCILicense: MITClaude CodeCursorCodexNo LLMLocal-only

A live trajectory dashboard for AI coding agent sessions. AgentPulse watches local Claude Code, Cursor, and Codex transcripts and classifies what each agent is doing right now: converging, exploring, stuck, done, drifting, or idle.

AgentPulse dashboard — a selected session cycles through converging, stuck, and drifting verdicts in real time

Drop it in a terminal next to your editor. You get an always-on read across local agent sessions without a model judge, hosted dashboard, telemetry, or outbound network calls.

Requires Node.js 22 or newer.

npx @conalh/agentpulse@latest live
flowchart LR
Claude["Claude Code<br/>transcripts"] --> Pulse
Cursor["Cursor<br/>transcripts"] --> Pulse
Codex["Codex<br/>sessions"] --> Pulse
Pulse[("AgentPulse<br/>local trajectory classifier")] --> TUI["Live TUI<br/>verdict + narrative"]
Pulse --> CI["CI snapshot<br/>--once --strict"]
TUI --> User["Human sees<br/>converging · stuck · drifting"]
classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px
classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe
class Claude,Cursor,Codex input
class Pulse engine
class TUI,CI,User output
Loading

Sample narrative on a converging session:

Your agent has been working on the login bug for 18 minutes. It focused
on `src/auth/`, made 3 changes to `session.ts`, and ran the tests after
each change. Tests went from failing to passing. Looks like it solved it.
Verdict: ● converging (confidence 0.85)

See also:warden for deterministic runtime policy, CapabilityEcho for PR-time capability drift, and agent-gov-core for the shared transcript and report primitives.

Where this fits

AgentPulse is the suite's live-observation layer — it watches a running session's trajectory rather than a diff or a finished transcript.

ToolInputCatches / decidesOutputUse when
wardenpolicy + tool actionallow / deny / askverdictyou need deterministic runtime policy decisions
CapabilityEchoPR diffnew executable capabilityannotations + reportcode gains network/subprocess/eval/lifecycle/workflow power
AgentPulselive session eventstrajectory stateterminal dashboardyou want live session observation
agent-gov-coreshared schemas/parserscommon Finding/Report modelnpm librarytools need shared report primitives

Why this exists

AI agents can run for a long time while the human is not staring at every tool call. Sometimes they converge. Sometimes they explore. Sometimes they get stuck in the same failed loop. Sometimes they touch a privileged path, pipe a network fetch straight into a shell, or write outside the repo.

AgentPulse exists to make that session state visible while the work is happening. It is deterministic local signal, not an LLM judging another LLM.

What it shows

SignalWhat it means
convergingFocused edits plus verification, often with tests moving toward passing.
exploringReading and orientation, no meaningful edit trajectory yet.
stuckRepeated edits/tests/failures, user pushback, or loop-like behavior.
doneCompletion language plus idle gap.
driftingA deterministic drift rule fired (privileged-path access, a shell-piped network fetch like curl … | sh, or a write outside the repo root). See Drift detection scope.
idleActivity has gone quiet or the window had no recent movement.

Drift detection scope.The drifting bucket is a deterministic first-pass detector, not a comprehensive agent-safety scanner. It currently fires on exactly three rule families:

  • Privileged-path access — a tool input path, or a path-shaped token inside a shell command (quote-aware, including bash -c "…" payloads), touching .ssh, .aws, .kube, /etc/shadow, or /private/var.
  • Shell-piped network fetchcurl/wget piped into sh/bash/zsh. Detection is tokenizer-based (agent-gov-core's quote-aware tokenizeShell), so quoted URLs (curl "https://…" | sh) are caught while a curl … | sh quoted inside another command's argument (e.g. gh release create --notes "…") is not a false positive.
  • Write outside the repo root — a Write/Edit to a path outside the session's cwd (or, with no repo root, outside /tmp, /var, ~). ./.. segments are resolved lexically before comparison and ~-prefixed paths count as outside; symlink escapes are not resolved.

So drifting means "a known risky pattern fired" — not "this session is safe." It does not yet cover bash <(curl …), curl -o … && sh …, bash -c "curl … | sh" nesting, PowerShell iwr | iex, python -c/node -e download-and-exec, package install hooks, credential exfiltration through ordinary files, or API-driven network actions. Treat a clean run as "none of the implemented rules matched," and pair AgentPulse with the rest of the agent-gov suite for deeper gating.

What makes it different

Several tools watch agent sessions. AgentPulse's wedge is the specific combination none of them cover:

Local-onlyNo LLMTrajectory verdictPer-session live dashboardPR gate
LangSmith / Langfuse / AgentOps❌ cloud❌ LLM-judge❌ traces only
Claude Code Session Memory❌ LLM⚠ structured
agenttrace❌ metrics only⚠ TUI
AgentPulse

The wedge is the combination. AgentPulse pairs naturally with agenttrace for cost/health metrics and with the rest of the agent-gov suite for PR-time gates.

agentpulse live — the live dashboard

agentpulse live [options]

Options:

FlagDefaultEffect
--window <duration>20mRecap window per session (5m, 1h, etc.)
--refresh <duration>30sBackground refresh cadence. Watcher fires sub-second on file changes regardless.
--roots <p1,p2,...>platform defaultsOverride discovery roots (comma-separated)
--stale <duration>1hSkip sessions older than this
--max-depth <N>unboundedCap discovery recursion depth below each root
--exclude <d1,d2,…>noneDirectory names to skip during discovery (e.g. node_modules,.git)
--hide-idleoffHide sessions with no activity in the window (also honored with --once)
--max-sessions <N>10Cap the list. With --once this is display-only — gating still considers every session.
--show-subagentsoffInclude agent-<hex> SDK-spawned subagent transcripts
--no-detectorsoffSkip the drifting bucket entirely
--onceoffHeadless snapshot mode. Runs once, prints, exits.
--format <fmt>textWith --once: text or json.
--strictoffWith --once: exit 1 if any session is drifting or stuck.
--fail-on-erroroffWith --once --strict: also exit 1 if a session failed to analyze (unreadable/corrupt transcript).
--redact <mode>noneRedact transcript-derived paths from --once output: none, paths, all.
--notify <mode>noneLocal notification on transition into drifting/stuck: none, bell, os, both.

Keyboard:

KeyAction
↑ ↓ / k j / w sMove selection
rForce refresh on selected session
aWhitelist current session's drift findings; preview first, confirm within 3s
nName / rename the selected session alias
?Toggle help overlay
q / Ctrl-CQuit

Agent aliases

When multiple agents work on the same project, the dashboard rows can look identical until you name them. Press n on a selected row, type an alias like CC1, frontend, or backend, and press Enter.

Aliases live in two optional JSON files:

  • <session.cwd>/.agentpulse-aliases.json — per-project, commit if you want team-shared conventions.
  • ~/.agentpulse/aliases.json — personal default.
{
"version": 1,
"aliases": {
"c3d4566ef4c5": "CC1",
"7a8b91234567": "CG1"
}
}

Exception baseline

Press a on a drifting session to preview the drift findings, then press a again within the confirmation window to append the selected fingerprints to <session.cwd>/.agentpulse-exceptions.json. AgentPulse refreshes and re-classifies the session immediately.

Commit the exception file to your repo when the behavior is intentionally approved. CI gating (agentpulse live --once --strict or the GitHub Action) honors the same baseline.

{
"version": 1,
"exceptions": [
{
"kind": "agent_pulse.live_drift_shell_exfil",
"fingerprint": "a1b2c3...",
"approvedAt": "2026-05-23T22:00:00.000Z",
"note": "approved by user via TUI"
}
]
}

Notifications

--notify <mode> fires a local notification when any session transitions into drifting or stuck.

ModeEffect
noneSilent
bellWrites \x07 to stderr
osNative notification: osascript on macOS, notify-send on Linux, BurntToast/NotifyIcon on Windows
bothBell + OS

Best-effort: missing OS notification utilities are a silent no-op rather than a crash.

CI integration

GitHub Action

- uses: Conalh/AgentPulse@v0.8.1with:
transcript-dirs: agentpulse-transcriptsstrict: 'true'redact: pathscomment-on-pr: 'true'github-token: ${{ github.token }}

The action runs agentpulse live --once against the provided transcript directory, writes a markdown summary to the GitHub step summary, optionally posts a sticky PR comment, and fails the workflow when strict: true and any session is drifting or stuck.

By default, the gate fails closed on analysis errors (fail-on-error: true) and hosted output reduces paths to basenames (redact: paths). Set fail-on-error: false to make unreadable or corrupt transcripts advisory. Use redact: none only for trusted private output, or redact: all when project labels, narratives, and topic keywords may also be sensitive. Other inputs include max-depth and exclude for bounded discovery, plus hide-idle, max-sessions, no-detectors, and show-subagents.

Sticky PR comments require pull-requests: write permission and an explicit github-token: ${{ github.token }} input. Omit both when the step summary is enough.

Supply chain. The Action builds and executes the checked-out Action source with npm ci --ignore-scripts, using the committed lockfile. Pin to a full commit SHA when you need AgentPulse's code and dependency graph to be immutable. npm remains the download source for the lockfile-pinned packages.

Privacy. The step summary and PR comment contain transcript-derived project labels, verdicts, drift counts, narratives, and topic keywords. Paths are reduced to basenames by default, but labels and narrative text remain. Use redact: all when those fields may be sensitive, and never publish raw transcript artifacts or unreviewed JSON snapshots.

Raw CLI

npx @conalh/agentpulse@latest live --once --strict --roots <transcript-dir>

Add --format json to pipe a structured snapshot into downstream tools.

agentpulse recap — single-transcript mode

agentpulse recap --transcript-dir ~/.claude/projects/<your-project>/ --format json

Same pipeline, narrower input. Use --watch for a polling re-emit loop.

What's intentionally not in scope

  • No LLM, anywhere. Not for summarization, not for classification.
  • No outbound network calls. Reads local transcript files, writes to terminal and optional local notifier.
  • No web UI / hosted dashboard. TUI for live view, GitHub Action for PRs, JSON output for everything else.
  • No multi-session memory. Each invocation reads the window and exits.
  • No “AI to review AI” loop. Detectors are deterministic.

Architecture

Deterministic pipeline. Each layer is pure where it can be; all layers share the src/types.ts contract.

LayerFileInput -> Output
1. Parseragent-gov-core/parsers/ (v1.1.0+)Claude Code / Cursor / Codex / Antigravity JSONL -> TranscriptEvent[]
2. Enrichmentsrc/enrich.tsevents -> keywords, cwd-relative path clusters, action classes
2.5. Sequencessrc/sequences.tsevents -> ordered-pattern signal (tdd_loop / stuck_loop / refuse_to_verify / exploratory_edit)
3. Outcomesrc/trajectory.tsevents -> verification trend, user tone, completion verbs, idle gap
4. Trajectorysrc/trajectory.tsenrichment + outcome + sequence + exceptions -> six-bucket verdict
5. Narrativesrc/narrative.tsverdict -> plain-English recap

Live infrastructure on top: src/sessions/ discovery/watcher, src/orchestrator.ts, src/exceptions.ts, src/notifications.ts, src/once.ts, and src/tui/.

Design choices worth flagging

  • Local by default. Zero network calls in any code path.
  • Deterministic. Same transcript window in, same verdict out. No model drift, API outages, or rate limits.
  • Live observation. AgentPulse reports the trajectory of active local sessions and provides a one-shot format for CI artifacts.
  • Substrate-built. Uses agent-gov-core primitives where the parser and report contracts overlap.
  • Tested. 296 tests (npm test), including hand-rolled property tests (a seeded, replayable PRNG with 200 iterations per invariant) over the pure classifier layers and a labeled golden-replay corpus of 13 transcript fixtures spanning all six trajectory buckets across Claude Code, Cursor, Codex, and Antigravity.

Windows terminal note

If you're running on Windows, prefer Windows Terminal over legacy cmd.exe. It works on cmd.exe, but Windows Terminal renders the dashboard more cleanly.

Related public tools

RepoWhat it catches
wardenDeterministic allow / deny / ask policy decisions.
CapabilityEchoCapability drift introduced by code, manifests, workflows, and Dockerfiles.
AgentPulse(this repo)Live local trajectory verdicts for active agent sessions.
agent-gov-coreShared parsers, the canonical Finding schema, and mergeFindings.

MIT.

About

Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - Conalh/AgentPulse: Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action. · GitHub
Skip to content

Repository files navigation

AgentPulse

npm versionCILicense: MITClaude CodeCursorCodexNo LLMLocal-only

A live trajectory dashboard for AI coding agent sessions. AgentPulse watches local Claude Code, Cursor, and Codex transcripts and classifies what each agent is doing right now: converging, exploring, stuck, done, drifting, or idle.

AgentPulse dashboard — a selected session cycles through converging, stuck, and drifting verdicts in real time

Drop it in a terminal next to your editor. You get an always-on read across local agent sessions without a model judge, hosted dashboard, telemetry, or outbound network calls.

Requires Node.js 22 or newer.

npx @conalh/agentpulse@latest live
flowchart LR
Claude["Claude Code<br/>transcripts"] --> Pulse
Cursor["Cursor<br/>transcripts"] --> Pulse
Codex["Codex<br/>sessions"] --> Pulse
Pulse[("AgentPulse<br/>local trajectory classifier")] --> TUI["Live TUI<br/>verdict + narrative"]
Pulse --> CI["CI snapshot<br/>--once --strict"]
TUI --> User["Human sees<br/>converging · stuck · drifting"]
classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0
classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px
classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe
class Claude,Cursor,Codex input
class Pulse engine
class TUI,CI,User output
Loading

Sample narrative on a converging session:

Your agent has been working on the login bug for 18 minutes. It focused
on `src/auth/`, made 3 changes to `session.ts`, and ran the tests after
each change. Tests went from failing to passing. Looks like it solved it.
Verdict: ● converging (confidence 0.85)

See also:warden for deterministic runtime policy, CapabilityEcho for PR-time capability drift, and agent-gov-core for the shared transcript and report primitives.

Where this fits

AgentPulse is the suite's live-observation layer — it watches a running session's trajectory rather than a diff or a finished transcript.

ToolInputCatches / decidesOutputUse when
wardenpolicy + tool actionallow / deny / askverdictyou need deterministic runtime policy decisions
CapabilityEchoPR diffnew executable capabilityannotations + reportcode gains network/subprocess/eval/lifecycle/workflow power
AgentPulselive session eventstrajectory stateterminal dashboardyou want live session observation
agent-gov-coreshared schemas/parserscommon Finding/Report modelnpm librarytools need shared report primitives

Why this exists

AI agents can run for a long time while the human is not staring at every tool call. Sometimes they converge. Sometimes they explore. Sometimes they get stuck in the same failed loop. Sometimes they touch a privileged path, pipe a network fetch straight into a shell, or write outside the repo.

AgentPulse exists to make that session state visible while the work is happening. It is deterministic local signal, not an LLM judging another LLM.

What it shows

SignalWhat it means
convergingFocused edits plus verification, often with tests moving toward passing.
exploringReading and orientation, no meaningful edit trajectory yet.
stuckRepeated edits/tests/failures, user pushback, or loop-like behavior.
doneCompletion language plus idle gap.
driftingA deterministic drift rule fired (privileged-path access, a shell-piped network fetch like curl … | sh, or a write outside the repo root). See Drift detection scope.
idleActivity has gone quiet or the window had no recent movement.

Drift detection scope.The drifting bucket is a deterministic first-pass detector, not a comprehensive agent-safety scanner. It currently fires on exactly three rule families:

  • Privileged-path access — a tool input path, or a path-shaped token inside a shell command (quote-aware, including bash -c "…" payloads), touching .ssh, .aws, .kube, /etc/shadow, or /private/var.
  • Shell-piped network fetchcurl/wget piped into sh/bash/zsh. Detection is tokenizer-based (agent-gov-core's quote-aware tokenizeShell), so quoted URLs (curl "https://…" | sh) are caught while a curl … | sh quoted inside another command's argument (e.g. gh release create --notes "…") is not a false positive.
  • Write outside the repo root — a Write/Edit to a path outside the session's cwd (or, with no repo root, outside /tmp, /var, ~). ./.. segments are resolved lexically before comparison and ~-prefixed paths count as outside; symlink escapes are not resolved.

So drifting means "a known risky pattern fired" — not "this session is safe." It does not yet cover bash <(curl …), curl -o … && sh …, bash -c "curl … | sh" nesting, PowerShell iwr | iex, python -c/node -e download-and-exec, package install hooks, credential exfiltration through ordinary files, or API-driven network actions. Treat a clean run as "none of the implemented rules matched," and pair AgentPulse with the rest of the agent-gov suite for deeper gating.

What makes it different

Several tools watch agent sessions. AgentPulse's wedge is the specific combination none of them cover:

Local-onlyNo LLMTrajectory verdictPer-session live dashboardPR gate
LangSmith / Langfuse / AgentOps❌ cloud❌ LLM-judge❌ traces only
Claude Code Session Memory❌ LLM⚠ structured
agenttrace❌ metrics only⚠ TUI
AgentPulse

The wedge is the combination. AgentPulse pairs naturally with agenttrace for cost/health metrics and with the rest of the agent-gov suite for PR-time gates.

agentpulse live — the live dashboard

agentpulse live [options]

Options:

FlagDefaultEffect
--window <duration>20mRecap window per session (5m, 1h, etc.)
--refresh <duration>30sBackground refresh cadence. Watcher fires sub-second on file changes regardless.
--roots <p1,p2,...>platform defaultsOverride discovery roots (comma-separated)
--stale <duration>1hSkip sessions older than this
--max-depth <N>unboundedCap discovery recursion depth below each root
--exclude <d1,d2,…>noneDirectory names to skip during discovery (e.g. node_modules,.git)
--hide-idleoffHide sessions with no activity in the window (also honored with --once)
--max-sessions <N>10Cap the list. With --once this is display-only — gating still considers every session.
--show-subagentsoffInclude agent-<hex> SDK-spawned subagent transcripts
--no-detectorsoffSkip the drifting bucket entirely
--onceoffHeadless snapshot mode. Runs once, prints, exits.
--format <fmt>textWith --once: text or json.
--strictoffWith --once: exit 1 if any session is drifting or stuck.
--fail-on-erroroffWith --once --strict: also exit 1 if a session failed to analyze (unreadable/corrupt transcript).
--redact <mode>noneRedact transcript-derived paths from --once output: none, paths, all.
--notify <mode>noneLocal notification on transition into drifting/stuck: none, bell, os, both.

Keyboard:

KeyAction
↑ ↓ / k j / w sMove selection
rForce refresh on selected session
aWhitelist current session's drift findings; preview first, confirm within 3s
nName / rename the selected session alias
?Toggle help overlay
q / Ctrl-CQuit

Agent aliases

When multiple agents work on the same project, the dashboard rows can look identical until you name them. Press n on a selected row, type an alias like CC1, frontend, or backend, and press Enter.

Aliases live in two optional JSON files:

  • <session.cwd>/.agentpulse-aliases.json — per-project, commit if you want team-shared conventions.
  • ~/.agentpulse/aliases.json — personal default.
{
"version": 1,
"aliases": {
"c3d4566ef4c5": "CC1",
"7a8b91234567": "CG1"
}
}

Exception baseline

Press a on a drifting session to preview the drift findings, then press a again within the confirmation window to append the selected fingerprints to <session.cwd>/.agentpulse-exceptions.json. AgentPulse refreshes and re-classifies the session immediately.

Commit the exception file to your repo when the behavior is intentionally approved. CI gating (agentpulse live --once --strict or the GitHub Action) honors the same baseline.

{
"version": 1,
"exceptions": [
{
"kind": "agent_pulse.live_drift_shell_exfil",
"fingerprint": "a1b2c3...",
"approvedAt": "2026-05-23T22:00:00.000Z",
"note": "approved by user via TUI"
}
]
}

Notifications

--notify <mode> fires a local notification when any session transitions into drifting or stuck.

ModeEffect
noneSilent
bellWrites \x07 to stderr
osNative notification: osascript on macOS, notify-send on Linux, BurntToast/NotifyIcon on Windows
bothBell + OS

Best-effort: missing OS notification utilities are a silent no-op rather than a crash.

CI integration

GitHub Action

- uses: Conalh/AgentPulse@v0.8.1with:
transcript-dirs: agentpulse-transcriptsstrict: 'true'redact: pathscomment-on-pr: 'true'github-token: ${{ github.token }}

The action runs agentpulse live --once against the provided transcript directory, writes a markdown summary to the GitHub step summary, optionally posts a sticky PR comment, and fails the workflow when strict: true and any session is drifting or stuck.

By default, the gate fails closed on analysis errors (fail-on-error: true) and hosted output reduces paths to basenames (redact: paths). Set fail-on-error: false to make unreadable or corrupt transcripts advisory. Use redact: none only for trusted private output, or redact: all when project labels, narratives, and topic keywords may also be sensitive. Other inputs include max-depth and exclude for bounded discovery, plus hide-idle, max-sessions, no-detectors, and show-subagents.

Sticky PR comments require pull-requests: write permission and an explicit github-token: ${{ github.token }} input. Omit both when the step summary is enough.

Supply chain. The Action builds and executes the checked-out Action source with npm ci --ignore-scripts, using the committed lockfile. Pin to a full commit SHA when you need AgentPulse's code and dependency graph to be immutable. npm remains the download source for the lockfile-pinned packages.

Privacy. The step summary and PR comment contain transcript-derived project labels, verdicts, drift counts, narratives, and topic keywords. Paths are reduced to basenames by default, but labels and narrative text remain. Use redact: all when those fields may be sensitive, and never publish raw transcript artifacts or unreviewed JSON snapshots.

Raw CLI

npx @conalh/agentpulse@latest live --once --strict --roots <transcript-dir>

Add --format json to pipe a structured snapshot into downstream tools.

agentpulse recap — single-transcript mode

agentpulse recap --transcript-dir ~/.claude/projects/<your-project>/ --format json

Same pipeline, narrower input. Use --watch for a polling re-emit loop.

What's intentionally not in scope

  • No LLM, anywhere. Not for summarization, not for classification.
  • No outbound network calls. Reads local transcript files, writes to terminal and optional local notifier.
  • No web UI / hosted dashboard. TUI for live view, GitHub Action for PRs, JSON output for everything else.
  • No multi-session memory. Each invocation reads the window and exits.
  • No “AI to review AI” loop. Detectors are deterministic.

Architecture

Deterministic pipeline. Each layer is pure where it can be; all layers share the src/types.ts contract.

LayerFileInput -> Output
1. Parseragent-gov-core/parsers/ (v1.1.0+)Claude Code / Cursor / Codex / Antigravity JSONL -> TranscriptEvent[]
2. Enrichmentsrc/enrich.tsevents -> keywords, cwd-relative path clusters, action classes
2.5. Sequencessrc/sequences.tsevents -> ordered-pattern signal (tdd_loop / stuck_loop / refuse_to_verify / exploratory_edit)
3. Outcomesrc/trajectory.tsevents -> verification trend, user tone, completion verbs, idle gap
4. Trajectorysrc/trajectory.tsenrichment + outcome + sequence + exceptions -> six-bucket verdict
5. Narrativesrc/narrative.tsverdict -> plain-English recap

Live infrastructure on top: src/sessions/ discovery/watcher, src/orchestrator.ts, src/exceptions.ts, src/notifications.ts, src/once.ts, and src/tui/.

Design choices worth flagging

  • Local by default. Zero network calls in any code path.
  • Deterministic. Same transcript window in, same verdict out. No model drift, API outages, or rate limits.
  • Live observation. AgentPulse reports the trajectory of active local sessions and provides a one-shot format for CI artifacts.
  • Substrate-built. Uses agent-gov-core primitives where the parser and report contracts overlap.
  • Tested. 296 tests (npm test), including hand-rolled property tests (a seeded, replayable PRNG with 200 iterations per invariant) over the pure classifier layers and a labeled golden-replay corpus of 13 transcript fixtures spanning all six trajectory buckets across Claude Code, Cursor, Codex, and Antigravity.

Windows terminal note

If you're running on Windows, prefer Windows Terminal over legacy cmd.exe. It works on cmd.exe, but Windows Terminal renders the dashboard more cleanly.

Related public tools

RepoWhat it catches
wardenDeterministic allow / deny / ask policy decisions.
CapabilityEchoCapability drift introduced by code, manifests, workflows, and Dockerfiles.
AgentPulse(this repo)Live local trajectory verdicts for active agent sessions.
agent-gov-coreShared parsers, the canonical Finding schema, and mergeFindings.

MIT.

About

Deterministic local-first trajectory monitor for AI coding sessions, with a live TUI, JSON snapshots, and a privacy-aware GitHub Action.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages