@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 +22 -0
- package/dist/activity-detection.d.ts +92 -0
- package/dist/activity-detection.d.ts.map +1 -0
- package/dist/activity-detection.js +409 -0
- package/dist/activity-detection.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1127 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
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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"}
|