@made-by-moonlight/athene-plugin-agent-claude-code 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Composio, Inc.
4
+ Copyright (c) 2026 slievr (Athene fork)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,92 @@
1
+ import { PROCESS_PROBE_INDETERMINATE, type ActivityDetection, type ActivityState, type ProcessProbeResult, type RuntimeHandle, type Session } from "@made-by-moonlight/athene-core";
2
+ /**
3
+ * Convert a workspace path to Claude's project directory path.
4
+ * Claude stores sessions at ~/.claude/projects/{encoded-path}/
5
+ *
6
+ * Verified against Claude Code's actual on-disk slugs: every non-alphanumeric
7
+ * character (other than `-`) is replaced with `-`. That includes `/`, `.`,
8
+ * `:`, and crucially `_` — AO's per-project data dirs are named like
9
+ * `<sanitized>_<hash>`, and without underscore folding the slug AO computes
10
+ * misses the directory Claude actually wrote (issue #1611).
11
+ *
12
+ * Windows: `C:\Users\dev\project` → `C--Users-dev-project` — Claude leaves the
13
+ * colon-position as a dash rather than stripping it. Verified via on-disk QA
14
+ * during the Windows port (commit 582c5373). Stripping the colon (as #1611
15
+ * inadvertently did) breaks JSONL lookup on Windows.
16
+ */
17
+ export declare function toClaudeProjectPath(workspacePath: string): string;
18
+ /**
19
+ * Resolve a workspace path through any symlinks BEFORE slugifying so AO's
20
+ * computed Claude project dir matches what Claude itself writes.
21
+ *
22
+ * Without this, if AO records `workspacePath` as a symlink (e.g.
23
+ * `/Users/me/symlinks/repo`) and Claude resolves it to the target
24
+ * (`/Users/me/code/repo`) before computing its on-disk slug, the two slugs
25
+ * diverge — AO looks in an empty `~/.claude/projects/<wrong-slug>/` dir
26
+ * forever and the session looks permanently `idle`. Falls back to the
27
+ * literal path on error (dangling symlink, race, etc.).
28
+ */
29
+ export declare function resolveWorkspaceForClaude(workspacePath: string): Promise<string>;
30
+ /** Reset the warned-paths dedupe set. Exported for testing only. */
31
+ export declare function resetWarnedReaddirPaths(): void;
32
+ /** Find Claude's JSONL session file for a project directory.
33
+ *
34
+ * When `preferredUuid` is provided (e.g. from `session.metadata.claudeSessionUuid`
35
+ * captured by getSessionInfo), prefer `<projectDir>/<preferredUuid>.jsonl`
36
+ * if it exists. This disambiguates the common case of multiple Claude
37
+ * sessions running in the same workspace, where newest-mtime would pick
38
+ * the WRONG session's JSONL whenever its sibling has just written.
39
+ *
40
+ * Falls back to newest-mtime when no UUID is given or the named file
41
+ * doesn't exist yet (e.g. fresh session that hasn't been introspected).
42
+ *
43
+ * ENOENT on the project dir is normal and silent. Other errors
44
+ * (EACCES, EPERM, EMFILE, ...) are logged via console.warn — once per
45
+ * path for the process lifetime — so a permission-denied or fd-exhausted
46
+ * misconfig doesn't silently mask the session as `idle` forever and
47
+ * doesn't flood logs on every poll. */
48
+ export declare function findLatestSessionFile(projectDir: string, preferredUuid?: string): Promise<string | null>;
49
+ /** Reset the ps cache. Exported for testing only. */
50
+ export declare function resetPsCache(): void;
51
+ /**
52
+ * Check if a process named "claude" is running in the given runtime handle's context.
53
+ * Uses ps to find processes by TTY (for tmux) or by PID.
54
+ */
55
+ export declare function findClaudeProcess(handle: RuntimeHandle): Promise<number | null | typeof PROCESS_PROBE_INDETERMINATE>;
56
+ export declare function isClaudeProcessAlive(handle: RuntimeHandle): Promise<ProcessProbeResult>;
57
+ /**
58
+ * Retained as a stable no-signal stub for the deprecated
59
+ * `Agent.detectActivity` method on the Claude plugin.
60
+ *
61
+ * Claude activity is now derived from platform-event hooks
62
+ * (PermissionRequest / StopFailure / Notification / Stop / PreToolUse / ...)
63
+ * which write directly to `{workspace}/.ao/activity.jsonl`. The previous
64
+ * implementation regex-matched Claude's rendered terminal output, which
65
+ * regressed every time Claude's UI footer or status-line wording changed
66
+ * (15-commit churn in #1932 motivated the rewrite).
67
+ *
68
+ * The function is preserved so the Claude agent's `detectActivity` can
69
+ * delegate to a stable export rather than inlining `() => "idle"`, and
70
+ * because the hard-deprecated `detectActivity` method on the `Agent`
71
+ * interface still has callers outside this plugin (lifecycle-manager's
72
+ * terminal-output fallback, used by agents that haven't moved to hooks).
73
+ */
74
+ export declare function classifyTerminalOutput(_terminalOutput: string): ActivityState;
75
+ /**
76
+ * Determine current activity state for a Claude Code session.
77
+ *
78
+ * Cascade:
79
+ * 1. Process check (returns null on INDETERMINATE, exited on dead)
80
+ * 2. Native JSONL: read last entry, map type+mtime → state
81
+ * 3. AO activity JSONL: `checkActivityLogState` for actionable states
82
+ * (waiting_input/blocked) terminal regex picked up
83
+ * 4. AO activity JSONL: `getActivityFallbackState` for age-decayed fallback
84
+ * 5. Stale native (entry predates session) returned only if nothing else
85
+ *
86
+ * Note: Claude does NOT emit `permission_request` or top-level `error`
87
+ * as JSONL types. `waiting_input` flows through the terminal regex →
88
+ * AO activity JSONL path. `blocked` is detected from native JSONL via
89
+ * `{type:"system", level:"error"}` (Claude's api_error shape).
90
+ */
91
+ export declare function getClaudeActivityState(session: Session, readyThresholdMs: number | undefined, isProcessAlive?: (handle: RuntimeHandle) => Promise<ProcessProbeResult>): Promise<ActivityDetection | null>;
92
+ //# sourceMappingURL=activity-detection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-detection.d.ts","sourceRoot":"","sources":["../src/activity-detection.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,2BAA2B,EAG3B,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,OAAO,EACb,MAAM,gCAAgC,CAAC;AAaxC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMtF;AAaD,oEAAoE;AACpE,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;;;;;;;;;;;;;wCAewC;AACxC,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6CxB;AAoBD,qDAAqD;AACrD,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAkCD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,OAAO,2BAA2B,CAAC,CA4D7D;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAI7F;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,MAAM,GAAG,aAAa,CAE7E;AA2CD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,EAChB,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,cAAc,GAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,kBAAkB,CAAwB,GAC5F,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CA2GnC"}
@@ -0,0 +1,409 @@
1
+ import { readLastJsonlEntry, readLastActivityEntry, checkActivityLogState, getActivityFallbackState, isWindows, PROCESS_PROBE_INDETERMINATE, DEFAULT_READY_THRESHOLD_MS, DEFAULT_ACTIVE_WINDOW_MS, } from "@made-by-moonlight/athene-core";
2
+ import { execFile } from "node:child_process";
3
+ import { readdir, realpath, stat } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { promisify } from "node:util";
7
+ const execFileAsync = promisify(execFile);
8
+ // =============================================================================
9
+ // Project-path slug
10
+ // =============================================================================
11
+ /**
12
+ * Convert a workspace path to Claude's project directory path.
13
+ * Claude stores sessions at ~/.claude/projects/{encoded-path}/
14
+ *
15
+ * Verified against Claude Code's actual on-disk slugs: every non-alphanumeric
16
+ * character (other than `-`) is replaced with `-`. That includes `/`, `.`,
17
+ * `:`, and crucially `_` — AO's per-project data dirs are named like
18
+ * `<sanitized>_<hash>`, and without underscore folding the slug AO computes
19
+ * misses the directory Claude actually wrote (issue #1611).
20
+ *
21
+ * Windows: `C:\Users\dev\project` → `C--Users-dev-project` — Claude leaves the
22
+ * colon-position as a dash rather than stripping it. Verified via on-disk QA
23
+ * during the Windows port (commit 582c5373). Stripping the colon (as #1611
24
+ * inadvertently did) breaks JSONL lookup on Windows.
25
+ */
26
+ export function toClaudeProjectPath(workspacePath) {
27
+ const normalized = workspacePath.replace(/\\/g, "/");
28
+ return normalized.replace(/[^a-zA-Z0-9-]/g, "-");
29
+ }
30
+ /**
31
+ * Resolve a workspace path through any symlinks BEFORE slugifying so AO's
32
+ * computed Claude project dir matches what Claude itself writes.
33
+ *
34
+ * Without this, if AO records `workspacePath` as a symlink (e.g.
35
+ * `/Users/me/symlinks/repo`) and Claude resolves it to the target
36
+ * (`/Users/me/code/repo`) before computing its on-disk slug, the two slugs
37
+ * diverge — AO looks in an empty `~/.claude/projects/<wrong-slug>/` dir
38
+ * forever and the session looks permanently `idle`. Falls back to the
39
+ * literal path on error (dangling symlink, race, etc.).
40
+ */
41
+ export async function resolveWorkspaceForClaude(workspacePath) {
42
+ try {
43
+ return await realpath(workspacePath);
44
+ }
45
+ catch {
46
+ return workspacePath;
47
+ }
48
+ }
49
+ // =============================================================================
50
+ // Session file discovery
51
+ // =============================================================================
52
+ /** Module-level dedupe so EACCES/EPERM on a project dir warns ONCE per path
53
+ * for the process lifetime, not on every poll cycle. getClaudeActivityState
54
+ * is called every few seconds per session — without this, a single denied
55
+ * path would flood logs at 60+ lines/minute indefinitely. Bounded by the
56
+ * number of unique workspace slugs, which is small. */
57
+ const warnedReaddirPaths = new Set();
58
+ /** Reset the warned-paths dedupe set. Exported for testing only. */
59
+ export function resetWarnedReaddirPaths() {
60
+ warnedReaddirPaths.clear();
61
+ }
62
+ /** Find Claude's JSONL session file for a project directory.
63
+ *
64
+ * When `preferredUuid` is provided (e.g. from `session.metadata.claudeSessionUuid`
65
+ * captured by getSessionInfo), prefer `<projectDir>/<preferredUuid>.jsonl`
66
+ * if it exists. This disambiguates the common case of multiple Claude
67
+ * sessions running in the same workspace, where newest-mtime would pick
68
+ * the WRONG session's JSONL whenever its sibling has just written.
69
+ *
70
+ * Falls back to newest-mtime when no UUID is given or the named file
71
+ * doesn't exist yet (e.g. fresh session that hasn't been introspected).
72
+ *
73
+ * ENOENT on the project dir is normal and silent. Other errors
74
+ * (EACCES, EPERM, EMFILE, ...) are logged via console.warn — once per
75
+ * path for the process lifetime — so a permission-denied or fd-exhausted
76
+ * misconfig doesn't silently mask the session as `idle` forever and
77
+ * doesn't flood logs on every poll. */
78
+ export async function findLatestSessionFile(projectDir, preferredUuid) {
79
+ // Prefer the UUID-named file when we know it — disambiguates multi-session.
80
+ if (preferredUuid) {
81
+ const preferred = join(projectDir, `${preferredUuid}.jsonl`);
82
+ try {
83
+ await stat(preferred);
84
+ return preferred;
85
+ }
86
+ catch {
87
+ // Fall through to newest-mtime — the UUID-named file may not exist
88
+ // yet (session just spawned, hasn't been introspected).
89
+ }
90
+ }
91
+ let entries;
92
+ try {
93
+ entries = await readdir(projectDir);
94
+ }
95
+ catch (err) {
96
+ if (err instanceof Error && "code" in err && err.code !== "ENOENT") {
97
+ if (!warnedReaddirPaths.has(projectDir)) {
98
+ warnedReaddirPaths.add(projectDir);
99
+ const code = err.code;
100
+ console.warn(`[claude-code] failed to read ${projectDir} (${code}): ${err.message}. Session activity will fall back to AO JSONL only. (This warning is shown once per path for the process lifetime.)`);
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+ const jsonlFiles = entries.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
106
+ if (jsonlFiles.length === 0)
107
+ return null;
108
+ const withStats = await Promise.all(jsonlFiles.map(async (f) => {
109
+ const fullPath = join(projectDir, f);
110
+ try {
111
+ const s = await stat(fullPath);
112
+ return { path: fullPath, mtime: s.mtimeMs };
113
+ }
114
+ catch {
115
+ return { path: fullPath, mtime: 0 };
116
+ }
117
+ }));
118
+ withStats.sort((a, b) => b.mtime - a.mtime);
119
+ return withStats[0]?.path ?? null;
120
+ }
121
+ let psCache = null;
122
+ const PS_CACHE_TTL_MS = 5_000;
123
+ /** Reset the ps cache. Exported for testing only. */
124
+ export function resetPsCache() {
125
+ psCache = null;
126
+ }
127
+ async function getCachedProcessList() {
128
+ // ps -eo is a Unix-only command; on Windows the tmux branch is never taken
129
+ // in normal operation, but guard here to avoid a spurious spawn error if
130
+ // a stale tmux handle is encountered.
131
+ if (isWindows())
132
+ return "";
133
+ const now = Date.now();
134
+ if (psCache && now - psCache.timestamp < PS_CACHE_TTL_MS) {
135
+ if (psCache.promise)
136
+ return psCache.promise;
137
+ return psCache.output;
138
+ }
139
+ const promise = execFileAsync("ps", ["-eo", "pid,tty,args"], {
140
+ timeout: 30_000,
141
+ })
142
+ .then(({ stdout }) => {
143
+ if (psCache?.promise === promise) {
144
+ psCache = { output: stdout || PROCESS_PROBE_INDETERMINATE, timestamp: Date.now() };
145
+ }
146
+ return stdout || PROCESS_PROBE_INDETERMINATE;
147
+ })
148
+ .catch(() => {
149
+ if (psCache?.promise === promise) {
150
+ psCache = { output: PROCESS_PROBE_INDETERMINATE, timestamp: Date.now() };
151
+ }
152
+ return PROCESS_PROBE_INDETERMINATE;
153
+ });
154
+ psCache = { output: "", timestamp: now, promise };
155
+ return promise;
156
+ }
157
+ /**
158
+ * Check if a process named "claude" is running in the given runtime handle's context.
159
+ * Uses ps to find processes by TTY (for tmux) or by PID.
160
+ */
161
+ export async function findClaudeProcess(handle) {
162
+ try {
163
+ if (handle.runtimeName === "tmux" && handle.id) {
164
+ if (isWindows())
165
+ return null;
166
+ const { stdout: ttyOut } = await execFileAsync("tmux", ["list-panes", "-t", handle.id, "-F", "#{pane_tty}"], { timeout: 30_000 });
167
+ const ttys = ttyOut
168
+ .trim()
169
+ .split("\n")
170
+ .map((t) => t.trim())
171
+ .filter(Boolean);
172
+ if (ttys.length === 0)
173
+ return null;
174
+ const psOut = await getCachedProcessList();
175
+ if (psOut === PROCESS_PROBE_INDETERMINATE)
176
+ return PROCESS_PROBE_INDETERMINATE;
177
+ const ttySet = new Set(ttys.map((t) => t.replace(/^\/dev\//, "")));
178
+ // Match "claude" plus common variants:
179
+ // - bare `claude` / `/usr/local/bin/claude`
180
+ // - dot-prefix shim `.claude`
181
+ // - file extensions like `claude.exe`, `claude.js`, `claude.cjs`
182
+ // - hyphenated names like `claude-code`
183
+ // - node-shim cases like `node /path/@anthropic-ai/claude-code/cli.js`
184
+ // (matches the path component containing "claude")
185
+ // Still anchored at `/` or start-of-line so `claudia` etc. don't match.
186
+ const processRe = /(?:^|\/)(?:\.)?claude(?:[-.][\w-]+)*(?:[\s/]|$)/;
187
+ for (const line of psOut.split("\n")) {
188
+ const cols = line.trimStart().split(/\s+/);
189
+ if (cols.length < 3 || !ttySet.has(cols[1] ?? ""))
190
+ continue;
191
+ const args = cols.slice(2).join(" ");
192
+ if (processRe.test(args)) {
193
+ return parseInt(cols[0] ?? "0", 10);
194
+ }
195
+ }
196
+ return null;
197
+ }
198
+ // For process runtime, check if the PID stored in handle data is alive
199
+ const rawPid = handle.data["pid"];
200
+ const pid = typeof rawPid === "number" ? rawPid : Number(rawPid);
201
+ if (Number.isFinite(pid) && pid > 0) {
202
+ try {
203
+ process.kill(pid, 0);
204
+ return pid;
205
+ }
206
+ catch (err) {
207
+ // EPERM means the process exists but we lack permission to signal it
208
+ if (err instanceof Error && "code" in err && err.code === "EPERM") {
209
+ return pid;
210
+ }
211
+ return null;
212
+ }
213
+ }
214
+ return null;
215
+ }
216
+ catch {
217
+ return PROCESS_PROBE_INDETERMINATE;
218
+ }
219
+ }
220
+ export async function isClaudeProcessAlive(handle) {
221
+ const pid = await findClaudeProcess(handle);
222
+ if (pid === PROCESS_PROBE_INDETERMINATE)
223
+ return PROCESS_PROBE_INDETERMINATE;
224
+ return pid !== null;
225
+ }
226
+ // =============================================================================
227
+ // Terminal output classification — retired (#1941)
228
+ // =============================================================================
229
+ /**
230
+ * Retained as a stable no-signal stub for the deprecated
231
+ * `Agent.detectActivity` method on the Claude plugin.
232
+ *
233
+ * Claude activity is now derived from platform-event hooks
234
+ * (PermissionRequest / StopFailure / Notification / Stop / PreToolUse / ...)
235
+ * which write directly to `{workspace}/.ao/activity.jsonl`. The previous
236
+ * implementation regex-matched Claude's rendered terminal output, which
237
+ * regressed every time Claude's UI footer or status-line wording changed
238
+ * (15-commit churn in #1932 motivated the rewrite).
239
+ *
240
+ * The function is preserved so the Claude agent's `detectActivity` can
241
+ * delegate to a stable export rather than inlining `() => "idle"`, and
242
+ * because the hard-deprecated `detectActivity` method on the `Agent`
243
+ * interface still has callers outside this plugin (lifecycle-manager's
244
+ * terminal-output fallback, used by agents that haven't moved to hooks).
245
+ */
246
+ export function classifyTerminalOutput(_terminalOutput) {
247
+ return "idle";
248
+ }
249
+ // =============================================================================
250
+ // Activity-state cascade
251
+ // =============================================================================
252
+ /**
253
+ * Claude writes these types as UI-state snapshots at random times: on session
254
+ * attach, on permission-mode change, on title regeneration, etc. They are
255
+ * NOT correlated with whether Claude is actively working — a 6-day-dormant
256
+ * session will still accumulate dozens of `permission-mode` and `ai-title`
257
+ * entries just from being inspected.
258
+ *
259
+ * When one of these is the literal last JSONL entry, treat it as a "no
260
+ * signal" — fall through to the AO activity-JSONL pipeline (terminal-
261
+ * derived signal) rather than letting noise mtime decide the activity.
262
+ *
263
+ * Concrete bug this prevents: ao-144 had 73 trailing `permission-mode` +
264
+ * 73 trailing `ai-title` entries written over 6 dormant days. Without
265
+ * this skip, dashboard oscillated between `ready` (recent noise mtime)
266
+ * and `idle` (old noise mtime) instead of staying `idle`.
267
+ *
268
+ * Conservative list — only the types that empirically run away. The other
269
+ * bookkeeping types (file-history-snapshot, attachment, pr-link,
270
+ * queue-operation, last-prompt) plausibly correlate with real activity
271
+ * and stay in the explicit ready/idle case.
272
+ */
273
+ const NOISE_JSONL_TYPES = new Set([
274
+ "permission-mode",
275
+ "ai-title",
276
+ "agent-color",
277
+ "agent-name",
278
+ "custom-title",
279
+ // pr-link is also re-snapshot noise — verified on ao-160's JSONL where the
280
+ // SAME PR (#1911) was written as a `pr-link` entry three times within
281
+ // minutes (count: 33 pr-link vs 21 user messages in the last 200 lines).
282
+ // The first emission is real; subsequent re-emissions are state snapshots.
283
+ // We can't distinguish first vs Nth from the last line alone, so treat
284
+ // all pr-link as noise. Real PR creation is still observable via the
285
+ // assistant message and the gh-tracker side.
286
+ "pr-link",
287
+ ]);
288
+ /**
289
+ * Determine current activity state for a Claude Code session.
290
+ *
291
+ * Cascade:
292
+ * 1. Process check (returns null on INDETERMINATE, exited on dead)
293
+ * 2. Native JSONL: read last entry, map type+mtime → state
294
+ * 3. AO activity JSONL: `checkActivityLogState` for actionable states
295
+ * (waiting_input/blocked) terminal regex picked up
296
+ * 4. AO activity JSONL: `getActivityFallbackState` for age-decayed fallback
297
+ * 5. Stale native (entry predates session) returned only if nothing else
298
+ *
299
+ * Note: Claude does NOT emit `permission_request` or top-level `error`
300
+ * as JSONL types. `waiting_input` flows through the terminal regex →
301
+ * AO activity JSONL path. `blocked` is detected from native JSONL via
302
+ * `{type:"system", level:"error"}` (Claude's api_error shape).
303
+ */
304
+ export async function getClaudeActivityState(session, readyThresholdMs, isProcessAlive = isClaudeProcessAlive) {
305
+ const threshold = readyThresholdMs ?? DEFAULT_READY_THRESHOLD_MS;
306
+ const exitedAt = new Date();
307
+ if (!session.runtimeHandle)
308
+ return { state: "exited", timestamp: exitedAt };
309
+ const running = await isProcessAlive(session.runtimeHandle);
310
+ if (running === PROCESS_PROBE_INDETERMINATE)
311
+ return null;
312
+ if (!running)
313
+ return { state: "exited", timestamp: exitedAt };
314
+ if (!session.workspacePath)
315
+ return null;
316
+ const projectPath = toClaudeProjectPath(await resolveWorkspaceForClaude(session.workspacePath));
317
+ const projectDir = join(homedir(), ".claude", "projects", projectPath);
318
+ // Prefer the UUID-named file when getSessionInfo has captured one — this
319
+ // disambiguates multi-session-per-worktree, where newest-mtime would pick
320
+ // the wrong session's JSONL whenever its sibling has just written.
321
+ const rawUuid = session.metadata?.["claudeSessionUuid"];
322
+ const preferredUuid = typeof rawUuid === "string" && rawUuid.trim() ? rawUuid.trim() : undefined;
323
+ const sessionFile = await findLatestSessionFile(projectDir, preferredUuid);
324
+ let staleNativeState = null;
325
+ if (sessionFile) {
326
+ const entry = await readLastJsonlEntry(sessionFile);
327
+ if (entry) {
328
+ // If the JSONL entry predates this session, it's from a previous session
329
+ // in the same worktree. Fall through to the AO safety net first: the
330
+ // terminal may have already surfaced waiting_input/blocked before
331
+ // Claude writes this session's first native JSONL entry.
332
+ if (session.createdAt && entry.modifiedAt < session.createdAt) {
333
+ staleNativeState = { state: "idle", timestamp: session.createdAt };
334
+ }
335
+ else if (entry.lastType && NOISE_JSONL_TYPES.has(entry.lastType)) {
336
+ // Last entry is UI-state noise (permission-mode / ai-title / etc.)
337
+ // that doesn't reflect actual activity. Fall through to the AO
338
+ // activity-JSONL pipeline for a terminal-derived answer; if that's
339
+ // also empty, the staleNativeState below returns idle.
340
+ staleNativeState = { state: "idle", timestamp: session.createdAt };
341
+ }
342
+ else {
343
+ const ageMs = Date.now() - entry.modifiedAt.getTime();
344
+ const timestamp = entry.modifiedAt;
345
+ const activeWindowMs = Math.min(DEFAULT_ACTIVE_WINDOW_MS, threshold);
346
+ switch (entry.lastType) {
347
+ // In-progress turn markers: very recent → active, older → ready/idle.
348
+ // Removed `tool_use` and `result` cases that were in the spec but
349
+ // never actually emitted by Claude (verified by disk survey for
350
+ // #1927). The `default` branch handles them with the same semantics
351
+ // if Claude ever introduces them.
352
+ case "user":
353
+ case "progress":
354
+ if (ageMs <= activeWindowMs)
355
+ return { state: "active", timestamp };
356
+ return { state: ageMs > threshold ? "idle" : "ready", timestamp };
357
+ case "system":
358
+ // Claude writes API errors as `{type:"system", subtype:"api_error",
359
+ // level:"error", cause:{...}}`. Require BOTH the subtype AND the
360
+ // level so a future error-level diagnostic that isn't actually
361
+ // fatal doesn't get silently classified as blocked. Other system
362
+ // subtypes (compact_boundary, local_command, turn_duration, etc.)
363
+ // are normal turn-end markers.
364
+ if (entry.lastSubtype === "api_error" && entry.lastLevel === "error") {
365
+ return { state: "blocked", timestamp };
366
+ }
367
+ return { state: ageMs > threshold ? "idle" : "ready", timestamp };
368
+ case "assistant":
369
+ case "summary":
370
+ return { state: ageMs > threshold ? "idle" : "ready", timestamp };
371
+ // Bookkeeping types Claude writes AFTER a real event (file edits,
372
+ // attachment context, queue housekeeping, prompt submit). Map to
373
+ // ready/idle by age, same as assistant/summary. The pure re-snapshot
374
+ // types (permission-mode, ai-title, agent-*, custom-title, pr-link)
375
+ // are filtered out earlier by NOISE_JSONL_TYPES — they get written
376
+ // continuously without indicating activity.
377
+ case "file-history-snapshot":
378
+ case "attachment":
379
+ case "queue-operation":
380
+ case "last-prompt":
381
+ return { state: ageMs > threshold ? "idle" : "ready", timestamp };
382
+ default:
383
+ if (ageMs <= activeWindowMs)
384
+ return { state: "active", timestamp };
385
+ return { state: ageMs > threshold ? "idle" : "ready", timestamp };
386
+ }
387
+ }
388
+ }
389
+ // Session file exists but no parseable entry — fall through to AO JSONL
390
+ // checks below instead of returning early, so terminal-derived
391
+ // waiting_input/blocked can still be detected.
392
+ }
393
+ // Fallback: check AO activity JSONL (terminal-derived) for
394
+ // waiting_input/blocked when Claude's native JSONL is unavailable.
395
+ const activityResult = await readLastActivityEntry(session.workspacePath);
396
+ const activityState = checkActivityLogState(activityResult);
397
+ if (activityState)
398
+ return activityState;
399
+ // Last fallback: use the AO entry with age-based decay when native
400
+ // session lookup is missing or unparseable (e.g. Claude project slug drift).
401
+ const activeWindowMs = Math.min(DEFAULT_ACTIVE_WINDOW_MS, threshold);
402
+ const fallback = getActivityFallbackState(activityResult, activeWindowMs, threshold);
403
+ if (fallback)
404
+ return fallback;
405
+ if (staleNativeState)
406
+ return staleNativeState;
407
+ return null;
408
+ }
409
+ //# sourceMappingURL=activity-detection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-detection.js","sourceRoot":"","sources":["../src/activity-detection.ts"],"names":[],"mappings":"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"}