fix(daemon): the provider switch reads a custom-path Claude's running effort by its launched executable

The switch's effort read now runs today's observer first, and only when that finds no Claude process, observes the
pane again with the exact executable this daemon launched there. The Claude adapter exposes that frozen executable
read-only. Every shape the observer already accepts is unchanged. The launched executable is in memory only, so after
a daemon restart, or for a launch this daemon didn't make, the effort stays unreadable with today's disclosed fallback.
This commit is contained in:
dev60-review committed 2026-11-02 05:37:40 +00:00
1 parent 6e606fb1fe
commit 9aaa523cf4
2 files changed
+14 -1

No files matched your search

@@ -92,6 +92,13 @@ export class ClaudeCodeAdapter implements RuntimeAdapter {
private listProcesses?: NativeProcessLister;
private readinessTimeoutMs?: number;
private autoLaunches = new Map<string, { binding: NodeBinding; token: string; freshInPlace?: boolean; executable?: string; scriptLaunch?: boolean; fingerprint?: string; process?: ClaudeLaunchedProcess }>();
/** The frozen executable of this daemon's managed launch now in `pane`, for read-only process observation. It's in
* memory only: after a daemon restart, or for a launch this daemon didn't make, there's none. */
launchedExecutable(nodeId: string, pane: string): { executable: string; scriptLaunch?: boolean } | null {
const launch = this.autoLaunches.get(nodeId);
if (!launch?.executable || (launch.binding.tmuxPane && launch.binding.tmuxPane !== pane)) return null;
return { executable: launch.executable, ...(launch.scriptLaunch ? { scriptLaunch: true } : {}) };
}
readonly claudeManagedLaunch?: ClaudeManagedLaunch;
private activityRelayPath: string | null;
private claudeHooksManifestPath: string | null;
+7 -1
View File
@@ -106,7 +106,13 @@ function claudeSeatOf(c: Context): ClaudeSeatRelaunch {
const saved = readSeatEffort(rigRepo.db, nodeId);
if (!tmux || !saved) return null;
const listProcesses = c.get("listProcesses" as never) as NativeProcessLister | undefined;
const running = await verifyClaudePaneRuntime({ target: pane, tmux, ...(listProcesses ? { listProcesses } : {}) });
const observe = (frozen?: { executable: string; scriptLaunch?: boolean }) => verifyClaudePaneRuntime({ target: pane, tmux,
...(listProcesses ? { listProcesses } : {}), ...(frozen ? { selectedExecutable: frozen.executable, scriptLaunch: frozen.scriptLaunch } : {}) });
// Today's recognised shapes first; then the exact executable this daemon launched in the pane (a custom path).
const claude = ((c.get("runtimeAdapters" as never) as Record<string, unknown> | undefined) ?? {})["claude-code"] as
{ launchedExecutable?: (nodeId: string, pane: string) => { executable: string; scriptLaunch?: boolean } | null } | undefined;
const frozen = claude?.launchedExecutable?.(nodeId, pane) ?? null;
const running = await observe() ?? (frozen ? await observe(frozen) : null);
if (!running) return null;
const emitted = emittedEffortFromArgv("claude-code", processArgv(running.process.command));
// A switch's relaunch is restore's resume from a snapshot taken now: its effort is the seat's explicit choice, else