@sentropic/h2a-runtime 0.90.1 → 0.90.3

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/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { projectStatusForH2a } from "./status-projection.js";
3
+ import type { ProcView } from "./proc-cpu.js";
3
4
  import { type RegistryEntry } from "./registry.js";
4
- import { reclaimProposals, type SessionLease } from "./session-lease.js";
5
+ import { reclaimProposals, type SessionLease, type SessionLeaseFleetAudit, type SessionLeaseWorker } from "./session-lease.js";
5
6
  import { projectRemoteAgents } from "./agents-projection.js";
6
7
  export declare const packageName = "@sentropic/h2a-runtime";
7
8
  export declare const H2A_RUNTIME_CLI_API_VERSION = 1;
@@ -93,6 +94,7 @@ export declare function acquireSessionLease(sessionId: string, opts: {
93
94
  holder: string;
94
95
  workspace?: string;
95
96
  agentPane?: string;
97
+ worker?: SessionLeaseWorker;
96
98
  }): SessionLease | undefined;
97
99
  /** What one supervising pass over the session leases did. */
98
100
  export type SessionLeaseSupervision = {
@@ -113,6 +115,19 @@ export type SessionLeaseSupervision = {
113
115
  * Its only output beyond the store is a list of PROPOSALS.
114
116
  */
115
117
  export declare function superviseSessionLeases(sessions: ReadonlyArray<RegistryEntry>, now?: string): SessionLeaseSupervision;
118
+ /** Relaunch one managed session while its lease carries the bounded protection. */
119
+ export declare function relaunchSessionWithLease(input: {
120
+ sessionId: string;
121
+ tmuxSession: string;
122
+ command: string;
123
+ resolvingBoundMs?: number;
124
+ }): boolean;
125
+ /** One on-demand fleet-wide read-back audit; it never runs from rendering. */
126
+ export declare function auditLiveSessionLeases(sessions?: ReadonlyArray<RegistryEntry>, now?: string): SessionLeaseFleetAudit & {
127
+ reaped: string[];
128
+ liveLeaseCount: number;
129
+ procView: ProcView;
130
+ };
116
131
  /**
117
132
  * Reliability slice 1 — RESUME a throttled HEADLESS LOCAL job. Relaunch the SAME
118
133
  * job in the SAME `runCwd` it already ran in (its recorded `cwd` — NOT a fresh
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -15,17 +15,18 @@ import { transmittedSecrets, secretsSummary } from "./secrets.js";
15
15
  import { localConvStat, remoteConvStat, alignment } from "./convsync.js";
16
16
  import { gitAlignment, localAncestry, localGitStat, remoteGitStat, } from "./gitdiff.js";
17
17
  import { syncConversation } from "./sync.js";
18
- import { attachLocalSession, attachPodTmux, capturePane, capturePaneVisible, clearPaneComposer, conductorRunning, currentTmuxSessionName, currentTmuxSessionIs, ensureManagedTmuxProfile, ensureHeadlessTerminal, existingLocalSessionSlugs, fanoutLabels, findLocalSession, killLocalSession, localSessionGatewayEnvStatus, listLocalSessions, listLocalSessionsWithDiagnostics, localSessionIdle, localSessionPanePid, localSessionName, paneTreeCpuMs, paneWorkerPid, pasteLiteralBlock, submitPane, installH2aStatusSurface, managedSessionCandidates, parseManagedSessionName, openH2aStatusWindow, persistLaunchContext, readLaunchContext, relaunchInSession, resolveAgentPane, resolveAgentPaneForInstance, resolveLocalSession, runLocalCliForeground, sendKeysLiteral, sessionAttached, sessionAttachedCount, setLocalSessionDisplayName, slugify, cleanupHeadlessPromptFile, startH2aWindow, startH2aWindowVerified, startHeadlessSession, startLocalSession, tmuxAvailable, uninstallH2aStatusSurface, } from "./tmux.js";
18
+ import { attachLocalSession, attachPodTmux, capturePane, capturePaneVisible, clearPaneComposer, conductorRunning, currentTmuxSessionName, currentTmuxSessionIs, ensureManagedTmuxProfile, ensureHeadlessTerminal, existingLocalSessionSlugs, fanoutLabels, findLocalSession, killLocalSession, localSessionGatewayEnvStatus, listLocalSessions, listH2aStatusSurfaces, listLocalSessionsWithDiagnostics, localSessionIdle, localSessionPanePid, localSessionName, paneProcessObservation, paneTreeCpuMs, paneWorkerAttribution, paneWorkerAttributionFromView, paneWorkerPid, pasteLiteralBlock, submitPane, installH2aStatusSurface, managedSessionCandidates, parseManagedSessionName, openH2aStatusWindow, persistLaunchContext, readLaunchContext, readFleetProcView, relaunchInSession, resolveAgentPane, resolveAgentPaneForInstance, resolveLocalSession, sessionRelaunchSafety, runLocalCliForeground, sendKeysLiteral, sessionAttached, sessionAttachedCount, setLocalSessionDisplayName, slugify, cleanupHeadlessPromptFile, startH2aWindow, startH2aWindowVerified, startHeadlessSession, startLocalSession, tmuxAvailable, uninstallH2aStatusSurface, } from "./tmux.js";
19
19
  import { deliverInitialPrompt, sleepSync, } from "./prompt-delivery.js";
20
20
  import { buildLaunchContext } from "./launch-context.js";
21
21
  import { migrateTmuxNames } from "./tmux-name-migration.js";
22
22
  import { projectStatusForH2a } from "./status-projection.js";
23
23
  import { buildAgentLaunchArgs, isAgentLaunchEffort, isAgentLaunchProfile, } from "./agent-launch-args.js";
24
- import { planRelaunch } from "./relaunch.js";
25
- import { isHumanFacingSession, readConversationCustomTitle, readLastLayout, reconcileRunConvIds, restore as restoreLayout, } from "./restore.js";
24
+ import { deriveSessionClass } from "./session-class.js";
25
+ import { isRelaunchKillable, planRelaunch, relaunchContinuationPrompt, wakeRelaunchedSession, } from "./relaunch.js";
26
+ import { isHumanFacingSession, legacySessionEvidence, readConversationCustomTitle, readLastLayout, reconcileRunConvIds, restore as restoreLayout, } from "./restore.js";
26
27
  import { getLayoutConfig } from "./config.js";
27
28
  import { advanceJob, coerceRegistryTool, enroll, enrollFromRun, isLive, listJobs, listLocalForLs, loadRegistry, loadRegistryWithDiagnostics, resolveLocalTmuxSessionForName, tryClaimSlot, withRegistryLock, resolveRegistryPath, } from "./registry.js";
28
- import { SessionLeaseStore, decideSessionBeat, isSessionLeaseAbandoned, planLeaseSupervision, reclaimProposals, resolveSessionLeasePath, } from "./session-lease.js";
29
+ import { auditSessionLeaseFleet, countLiveSessionLeases, SessionLeaseStore, decideSessionBeat, isSessionLeaseAlive, isSessionLeaseAbandoned, isSessionLeaseDeadBoot, planLeaseSupervision, reclaimProposals, resolveSessionLeasePath, } from "./session-lease.js";
29
30
  import { aimdEffectiveCap, assertDelegateEffort, assertSafeName, buildDelegateArgs, buildJobRows, buildRemoteDelegate, buildThrottleResumeArgs, canDelegateAtDepth, childDepthEnvValue, clampDepth, clampRemoteDepthBudget, conductorAdvisory, DEFAULT_MAX_CONCURRENT, DEPTH_ENV, inheritedDepthBudget, isDelegateType, isThrottleResumeDue, jitteredBackoffMs, JOB_ID_ENV, jobDir, planNextStarts, planThrottleStep, readJobResult, THROTTLE_MAX_ATTEMPTS, reconcileRemoteJobs, renderJobsTable, resolveJobCwd, runTrackMirror, sweepStaleJobs, trackItemNewArgs, trackItemRealizeArgs, } from "./delegate.js";
30
31
  import { detectThrottle, THROTTLE_TAIL_LINES } from "./throttle-signatures.js";
31
32
  import { interactiveResumeNudge, planInteractiveResume, } from "./interactive-throttle.js";
@@ -1014,12 +1015,15 @@ function sessionLeaseStore() {
1014
1015
  * to compute a rate against.
1015
1016
  */
1016
1017
  export function acquireSessionLease(sessionId, opts) {
1017
- const cpuMs = opts.agentPane ? paneTreeCpuMs(opts.agentPane) : undefined;
1018
+ // CPU baseline and worker identity share one /proc walk at acquire time.
1019
+ const observation = opts.agentPane ? paneProcessObservation(opts.agentPane) : undefined;
1020
+ const worker = opts.worker ?? observation?.worker;
1018
1021
  return sessionLeaseStore().acquire({
1019
1022
  sessionId,
1020
1023
  holder: opts.holder,
1021
1024
  ...(opts.workspace !== undefined ? { workspace: opts.workspace } : {}),
1022
- ...(cpuMs !== undefined ? { cpuMs } : {}),
1025
+ ...(observation?.cpuMs !== undefined ? { cpuMs: observation.cpuMs } : {}),
1026
+ ...(worker !== undefined ? { worker } : {}),
1023
1027
  });
1024
1028
  }
1025
1029
  /**
@@ -1040,6 +1044,9 @@ export function superviseSessionLeases(sessions, now = new Date().toISOString())
1040
1044
  unreadable: [],
1041
1045
  proposals: [],
1042
1046
  };
1047
+ const procView = readFleetProcView();
1048
+ // R3: prior-boot corpses are reaped silently before any proposal pass.
1049
+ store.reapDeadBootLeases(procView);
1043
1050
  const leases = store.readAll();
1044
1051
  const byId = new Map(leases.map((lease) => [lease.sessionId, lease]));
1045
1052
  for (const step of planLeaseSupervision(leases, sessions)) {
@@ -1055,27 +1062,77 @@ export function superviseSessionLeases(sessions, now = new Date().toISOString())
1055
1062
  if (step.action === "leave")
1056
1063
  continue;
1057
1064
  const pane = step.tmuxSession ? resolveAgentPane(step.tmuxSession) : undefined;
1065
+ let currentLease = lease;
1066
+ // R1 lazy migration: old leases remain alive until this pass can resolve
1067
+ // their worker. Resolve from the pass-wide snapshot, never by spawning a
1068
+ // /proc walk for every lease.
1069
+ if (currentLease.worker === undefined && pane !== undefined) {
1070
+ const worker = paneWorkerAttributionFromView(pane, procView);
1071
+ if (worker !== undefined) {
1072
+ currentLease = store.populateWorker({
1073
+ sessionId: currentLease.sessionId,
1074
+ token: currentLease.token,
1075
+ worker,
1076
+ });
1077
+ }
1078
+ }
1079
+ if (!isSessionLeaseAlive(currentLease, procView, now)) {
1080
+ result.unreadable.push(currentLease.sessionId);
1081
+ continue;
1082
+ }
1058
1083
  const cpuMsNow = pane ? paneTreeCpuMs(pane) : undefined;
1059
- const decision = decideSessionBeat({ lease, cpuMsNow, now });
1084
+ const decision = decideSessionBeat({ lease: currentLease, cpuMsNow, now });
1060
1085
  if (decision.action === "beat" && cpuMsNow !== undefined) {
1061
- store.heartbeat({ sessionId: lease.sessionId, token: lease.token, cpuMs: cpuMsNow });
1062
- result.beaten.push(lease.sessionId);
1086
+ store.heartbeat({ sessionId: currentLease.sessionId, token: currentLease.token, cpuMs: cpuMsNow });
1087
+ result.beaten.push(currentLease.sessionId);
1063
1088
  }
1064
1089
  else if (decision.action === "observe" && cpuMsNow !== undefined) {
1065
- store.observe({ sessionId: lease.sessionId, token: lease.token, cpuMs: cpuMsNow });
1066
- result.observed.push(lease.sessionId);
1090
+ store.observe({ sessionId: currentLease.sessionId, token: currentLease.token, cpuMs: cpuMsNow });
1091
+ result.observed.push(currentLease.sessionId);
1067
1092
  }
1068
1093
  else {
1069
- result.unreadable.push(lease.sessionId);
1094
+ result.unreadable.push(currentLease.sessionId);
1070
1095
  }
1071
1096
  }
1072
1097
  catch {
1073
1098
  // a rejected write (raced re-acquisition, torn store) must not stop the pass
1074
1099
  }
1075
1100
  }
1076
- result.proposals = reclaimProposals(store.readAll(), now);
1101
+ result.proposals = reclaimProposals(store.readAll(), now, procView);
1077
1102
  return result;
1078
1103
  }
1104
+ /** Relaunch one managed session while its lease carries the bounded protection. */
1105
+ export function relaunchSessionWithLease(input) {
1106
+ const store = sessionLeaseStore();
1107
+ const lease = store.forSession(input.sessionId);
1108
+ if (lease === undefined)
1109
+ return relaunchInSession(input.tmuxSession, input.command);
1110
+ const pane = resolveAgentPane(input.tmuxSession);
1111
+ const outcome = store.relaunch({
1112
+ sessionId: lease.sessionId,
1113
+ token: lease.token,
1114
+ ...(input.resolvingBoundMs !== undefined
1115
+ ? { resolvingBoundMs: input.resolvingBoundMs }
1116
+ : {}),
1117
+ act: () => relaunchInSession(input.tmuxSession, input.command),
1118
+ resolveWorker: () => (pane ? paneWorkerAttribution(pane) : undefined),
1119
+ });
1120
+ return outcome.succeeded;
1121
+ }
1122
+ /** One on-demand fleet-wide read-back audit; it never runs from rendering. */
1123
+ export function auditLiveSessionLeases(sessions = listJobs(), now = new Date().toISOString()) {
1124
+ const store = sessionLeaseStore();
1125
+ const leases = store.readAll();
1126
+ const procView = readFleetProcView();
1127
+ const report = auditSessionLeaseFleet(sessions.map((session) => ({
1128
+ sessionId: session.id,
1129
+ ...(session.tmuxSession !== undefined ? { tmuxSession: session.tmuxSession } : {}),
1130
+ ...(session.jobState !== undefined ? { jobState: session.jobState } : {}),
1131
+ })), leases, listH2aStatusSurfaces(), procView, now);
1132
+ const liveLeaseCount = countLiveSessionLeases(leases, procView, now);
1133
+ const reaped = store.reapDeadBootLeases(procView);
1134
+ return { ...report, reaped, liveLeaseCount, procView };
1135
+ }
1079
1136
  /** Human-readable duration for lease reporting ("41m", "2h13m"). */
1080
1137
  function formatIdle(ms) {
1081
1138
  const minutes = Math.floor(ms / 60_000);
@@ -1449,13 +1506,14 @@ function registryEntriesForLocalTmuxTarget(target, local, entries = loadRegistry
1449
1506
  });
1450
1507
  }
1451
1508
  function registryEntryForResumeTarget(target, local) {
1509
+ const evidence = legacySessionEvidence(homedir(), local === undefined ? listLocalSessions() : [local]);
1452
1510
  const matches = registryEntriesForLocalTmuxTarget(target, local).filter((e) => {
1453
1511
  // Resume is a human-facing operation, not a promotion path. Share the
1454
1512
  // durable class test with restore so legacy human rows remain resumable,
1455
1513
  // while delegated jobs and explicit background launches stay excluded.
1456
1514
  if (e.role !== undefined)
1457
1515
  return false;
1458
- return isHumanFacingSession(e);
1516
+ return isHumanFacingSession(e, evidence);
1459
1517
  });
1460
1518
  const ids = new Set(matches.map((e) => e.id));
1461
1519
  if (ids.size !== 1)
@@ -3339,6 +3397,15 @@ export async function main(argv) {
3339
3397
  process.exitCode = 1;
3340
3398
  return;
3341
3399
  }
3400
+ let gatewayMode;
3401
+ try {
3402
+ gatewayMode = gatewayModeFromOptions(opts);
3403
+ }
3404
+ catch (error) {
3405
+ process.stderr.write(`[h2a] ${error.message}.\n`);
3406
+ process.exitCode = 1;
3407
+ return;
3408
+ }
3342
3409
  const target = slug ?? slugify(process.cwd());
3343
3410
  const localResolution = resolveLocalSession(target);
3344
3411
  if (localResolution.kind === "ambiguous") {
@@ -3404,15 +3471,6 @@ export async function main(argv) {
3404
3471
  return;
3405
3472
  }
3406
3473
  const profile = entry.tool;
3407
- let gatewayMode;
3408
- try {
3409
- gatewayMode = gatewayModeFromOptions(opts);
3410
- }
3411
- catch (error) {
3412
- process.stderr.write(`[h2a] ${error.message}.\n`);
3413
- process.exitCode = 1;
3414
- return;
3415
- }
3416
3474
  if (gatewayMode === "gateway" && !profileUsesLlmMeshGateway(profile)) {
3417
3475
  process.stderr.write(`[h2a] --llm-gateway/--gw is unsupported for ${profile}; this gateway is Anthropic-compatible and only Claude profiles consume it.\n`);
3418
3476
  process.exitCode = 2;
@@ -3535,13 +3593,19 @@ export async function main(argv) {
3535
3593
  }
3536
3594
  }
3537
3595
  gateway = await injectLlmMeshGatewayEnv(effectiveGatewayMode, true, localSessionName(resumeSlug));
3538
- const { name } = startLocalSession(profile, command, entry.cwd, args, resumeSlug, undefined, { sessionClass: "human", attachedTerminal: true });
3596
+ const sessionClass = deriveSessionClass({
3597
+ background: opts.attach !== true,
3598
+ humanTerminal: opts.attach === true &&
3599
+ process.stdin.isTTY === true &&
3600
+ process.stdout.isTTY === true,
3601
+ });
3602
+ const { name } = startLocalSession(profile, command, entry.cwd, args, resumeSlug, undefined, { sessionClass, attachedTerminal: true });
3539
3603
  enrollFromRun({
3540
3604
  profile,
3541
3605
  slug: resumeSlug,
3542
3606
  tmuxSession: name,
3543
3607
  cwd: entry.cwd,
3544
- sessionClass: "human",
3608
+ sessionClass,
3545
3609
  ...(entry.convId ? { convId: entry.convId } : {}),
3546
3610
  ...(gatewayMode !== "auto" ? { gatewayMode } : {}),
3547
3611
  });
@@ -3736,7 +3800,11 @@ export async function main(argv) {
3736
3800
  // A detached/background or run-once launch is a worker, even when its
3737
3801
  // agent later emits a Claude SessionStart hook. Stamp this through tmux
3738
3802
  // so that hook cannot reclassify it as a human session.
3739
- const sessionClass = opts.background || opts.headless ? "background" : "human";
3803
+ const sessionClass = deriveSessionClass({
3804
+ background: opts.background === true,
3805
+ headless: opts.headless === true,
3806
+ humanTerminal: opts.attachedTerminal !== false,
3807
+ });
3740
3808
  let activeGateway;
3741
3809
  const h2a = getH2aConfig();
3742
3810
  const h2aSidecar = opts.h2a ?? h2a.enabled;
@@ -4553,11 +4621,14 @@ export async function main(argv) {
4553
4621
  .command("leases")
4554
4622
  .description("Session slot leases (machine-scoped): who holds a concurrency slot, when it last proved it was working, and which slots are reclaimable. READ-ONLY — it kills nothing and frees nothing.")
4555
4623
  .option("--probe", "before reporting, run one supervising pass: beat the leases of sessions whose pane is burning CPU, record the others")
4624
+ .option("--audit", "run one on-demand fleet-wide read-back audit (one tmux FORMAT call and one /proc scan)")
4556
4625
  .action((opts) => {
4557
4626
  const now = new Date().toISOString();
4558
4627
  if (opts.probe === true)
4559
4628
  superviseSessionLeases(listJobs(), now);
4560
- const leases = sessionLeaseStore().readAll();
4629
+ const audit = opts.audit === true ? auditLiveSessionLeases(listJobs(), now) : undefined;
4630
+ const store = sessionLeaseStore();
4631
+ const leases = store.readAll();
4561
4632
  if (leases.length === 0) {
4562
4633
  process.stdout.write("no session leases\n");
4563
4634
  return;
@@ -4568,17 +4639,33 @@ export async function main(argv) {
4568
4639
  .sort((a, b) => Date.parse(a.heartbeatAt) - Date.parse(b.heartbeatAt))
4569
4640
  .map((lease) => {
4570
4641
  const idle = nowMs - Date.parse(lease.heartbeatAt);
4571
- const state = isSessionLeaseAbandoned(lease, now) ? "RECLAIMABLE" : "held";
4642
+ const state = audit
4643
+ ? isSessionLeaseAlive(lease, audit.procView, now)
4644
+ ? "held"
4645
+ : "RECLAIMABLE"
4646
+ : isSessionLeaseAbandoned(lease, now)
4647
+ ? "RECLAIMABLE"
4648
+ : "held";
4572
4649
  return (`${lease.sessionId.padEnd(24)} ${state.padEnd(12)} ` +
4573
4650
  `idle ${formatIdle(idle).padEnd(7)} ttl ${formatIdle(lease.ttlMs).padEnd(6)} ` +
4574
4651
  `holder ${lease.holder}`);
4575
4652
  });
4576
4653
  process.stdout.write(`${rows.join("\n")}\n`);
4577
- const reclaimable = reclaimProposals(leases, now).length;
4654
+ const reclaimable = audit
4655
+ ? reclaimProposals(leases, now, audit.procView).length
4656
+ : reclaimProposals(leases, now).length;
4578
4657
  if (reclaimable > 0) {
4579
4658
  process.stderr.write(`[h2a] ${reclaimable} slot(s) reclaimable — free one with: h2a jobs reclaim <id> ` +
4580
4659
  "(the session's tmux window is left running and can still be attached)\n");
4581
4660
  }
4661
+ if (audit) {
4662
+ if (audit.reaped.length > 0) {
4663
+ process.stderr.write(`[h2a] audit reaped prior-boot lease(s): ${audit.reaped.join(", ")}\n`);
4664
+ }
4665
+ if (audit.missingStatusSurface.length > 0) {
4666
+ process.stderr.write(`[h2a] audit status-surface drift: ${audit.missingStatusSurface.join(", ")}\n`);
4667
+ }
4668
+ }
4582
4669
  });
4583
4670
  jobsCommand
4584
4671
  .command("reclaim <id>")
@@ -4586,15 +4673,21 @@ export async function main(argv) {
4586
4673
  .action((id) => {
4587
4674
  const now = new Date().toISOString();
4588
4675
  const store = sessionLeaseStore();
4589
- const lease = store.forSession(id);
4676
+ let lease = store.forSession(id);
4590
4677
  if (!lease) {
4591
4678
  process.stderr.write(`[h2a] no session lease for "${id}" — nothing to reclaim (see: h2a jobs leases)\n`);
4592
4679
  process.exitCode = 1;
4593
4680
  return;
4594
4681
  }
4682
+ const procView = readFleetProcView();
4683
+ if (isSessionLeaseDeadBoot(lease, procView)) {
4684
+ store.reapDeadBootLeases(procView);
4685
+ process.stdout.write(`reaped ${id} — lease belonged to a prior boot\n`);
4686
+ return;
4687
+ }
4595
4688
  // The one guard that matters: a session still proving it works keeps its
4596
4689
  // slot, whatever the operator believes. Reclaiming is not arbitration.
4597
- if (!isSessionLeaseAbandoned(lease, now)) {
4690
+ if (isSessionLeaseAlive(lease, procView, now)) {
4598
4691
  const idle = Date.parse(now) - Date.parse(lease.heartbeatAt);
4599
4692
  process.stderr.write(`[h2a] refusing to reclaim ${id}: its lease still beats (last proof of work ` +
4600
4693
  `${formatIdle(idle)} ago, TTL ${formatIdle(lease.ttlMs)}). It is working.\n`);
@@ -5477,7 +5570,10 @@ export async function main(argv) {
5477
5570
  })(),
5478
5571
  slug: s.slug,
5479
5572
  name: s.name,
5480
- idle: localSessionIdle(s.name),
5573
+ ...(() => {
5574
+ const safety = sessionRelaunchSafety(s.name);
5575
+ return { ...safety, livenessReason: safety.reason };
5576
+ })(),
5481
5577
  })), {
5482
5578
  ...(forced ? { force: true } : {}),
5483
5579
  ...(opts.all && !opts.includeAgents
@@ -5510,7 +5606,15 @@ export async function main(argv) {
5510
5606
  if (!forced) {
5511
5607
  let ok = 0;
5512
5608
  for (const action of plan.actions) {
5513
- if (relaunchInSession(action.name, action.cmd)) {
5609
+ const sessionId = entryByTmuxSession.get(action.name)?.id;
5610
+ const relaunched = sessionId !== undefined
5611
+ ? relaunchSessionWithLease({
5612
+ sessionId,
5613
+ tmuxSession: action.name,
5614
+ command: action.cmd,
5615
+ })
5616
+ : relaunchInSession(action.name, action.cmd);
5617
+ if (relaunched) {
5514
5618
  ok += 1;
5515
5619
  process.stderr.write(`[h2a] relaunched ${action.slug}: ${action.cmd}\n`);
5516
5620
  }
@@ -5576,16 +5680,38 @@ export async function main(argv) {
5576
5680
  process.exitCode = 1;
5577
5681
  return;
5578
5682
  }
5683
+ const finalReady = [];
5684
+ for (const item of ready) {
5685
+ const safety = sessionRelaunchSafety(item.action.name);
5686
+ if (!isRelaunchKillable(safety)) {
5687
+ process.stderr.write(`[h2a] skipped ${item.action.slug}: ${safety.reason}\n`);
5688
+ continue;
5689
+ }
5690
+ finalReady.push(item);
5691
+ }
5579
5692
  let ok = 0;
5580
- for (const { action, entry, sessionClass } of ready) {
5581
- if (!killLocalSession(action.name)) {
5582
- process.stderr.write(`[h2a] FAILED to force-restart ${action.slug}: tmux session ${action.name} could not be killed.\n`);
5693
+ for (const { action, entry, sessionClass } of finalReady) {
5694
+ const safety = sessionRelaunchSafety(action.name);
5695
+ if (!isRelaunchKillable(safety)) {
5696
+ process.stderr.write(`[h2a] skipped ${action.slug}: became live before kill: ${safety.reason}\n`);
5697
+ continue;
5698
+ }
5699
+ if (!safety.identity) {
5700
+ process.stderr.write(`[h2a] skipped ${action.slug}: liveness identity was unreadable immediately before kill.\n`);
5701
+ continue;
5702
+ }
5703
+ if (!killLocalSession(action.name, safety.identity)) {
5704
+ process.stderr.write(`[h2a] skipped ${action.slug}: tmux session ${action.name} changed or was no longer proven dead immediately before kill.\n`);
5583
5705
  continue;
5584
5706
  }
5585
5707
  try {
5586
5708
  const gatewayMode = gatewayModeForProfile(action.profile, entry.gatewayMode ?? "auto");
5587
5709
  await injectLlmMeshGatewayEnv(gatewayMode, true, localSessionName(action.slug));
5588
- const { name } = startLocalSession(action.profile, action.command, entry.cwd, action.args, action.slug, undefined, { sessionClass });
5710
+ const { name, agentPane } = startLocalSession(action.profile, action.command, entry.cwd, action.args, action.slug, undefined, {
5711
+ sessionClass,
5712
+ resumeId: action.convId,
5713
+ attachedTerminal: true,
5714
+ });
5589
5715
  enrollFromRun({
5590
5716
  profile: action.profile,
5591
5717
  slug: action.slug,
@@ -5597,8 +5723,32 @@ export async function main(argv) {
5597
5723
  ? { gatewayMode: entry.gatewayMode }
5598
5724
  : {}),
5599
5725
  });
5726
+ const wake = wakeRelaunchedSession(relaunchContinuationPrompt(entry.task), {
5727
+ deliverContinuation: (prompt) => deliverInitialPrompt(agentPane, prompt, {
5728
+ capturePane: capturePaneVisible,
5729
+ clearComposer: clearPaneComposer,
5730
+ pasteBlock: pasteLiteralBlock,
5731
+ submit: submitPane,
5732
+ cpuMs: paneTreeCpuMs,
5733
+ sleep: sleepSync,
5734
+ now: () => Date.now(),
5735
+ }),
5736
+ submitConfirmation: () => submitPane(agentPane),
5737
+ capturePane: () => capturePaneVisible(agentPane),
5738
+ sleep: sleepSync,
5739
+ now: () => Date.now(),
5740
+ });
5741
+ if (wake.confirmation === "auto-passed") {
5742
+ process.stderr.write(`[h2a] ${action.slug}: Claude stale-session confirmation auto-passed once\n`);
5743
+ }
5744
+ if (wake.state !== "working") {
5745
+ process.stderr.write(`[h2a] FAILED to wake ${action.slug} after resume: ${wake.reason}\n` +
5746
+ (wake.capture ? `[h2a] last screen:\n${wake.capture}\n` : ""));
5747
+ continue;
5748
+ }
5600
5749
  ok += 1;
5601
- process.stderr.write(`[h2a] force-restarted ${action.slug}: ${action.cmd}\n`);
5750
+ process.stderr.write(`[h2a] force-restarted ${action.slug}: ${action.cmd}; objective re-injected; agent WORKING ` +
5751
+ `(${Math.round(wake.delivery.cpuDeltaMs)}ms CPU, ${wake.delivery.evidence})\n`);
5602
5752
  }
5603
5753
  catch (error) {
5604
5754
  process.stderr.write(`[h2a] FAILED to force-restart ${action.slug} after killing ${action.name}: ${error.message}\n`);
@@ -5701,6 +5851,9 @@ export async function main(argv) {
5701
5851
  });
5702
5852
  let nudged = 0;
5703
5853
  if (apply) {
5854
+ const leaseIdByTmuxSession = new Map(loadRegistry()
5855
+ .filter((entry) => entry.tmuxSession !== undefined)
5856
+ .map((entry) => [entry.tmuxSession, entry.id]));
5704
5857
  for (const r of plan.toResume) {
5705
5858
  // DOUBLE-CHECK the attached guard at the moment of action (the pure plan
5706
5859
  // already excluded attached panes, but re-read in case a human just
@@ -5709,7 +5862,14 @@ export async function main(argv) {
5709
5862
  process.stderr.write(`[h2a] ${r.name} (${r.type}) just got attached — skipping the nudge\n`);
5710
5863
  continue;
5711
5864
  }
5712
- const ok = relaunchInSession(r.name, interactiveResumeNudge(r.type));
5865
+ const sessionId = leaseIdByTmuxSession.get(r.name);
5866
+ const ok = sessionId !== undefined
5867
+ ? relaunchSessionWithLease({
5868
+ sessionId,
5869
+ tmuxSession: r.name,
5870
+ command: interactiveResumeNudge(r.type),
5871
+ })
5872
+ : relaunchInSession(r.name, interactiveResumeNudge(r.type));
5713
5873
  if (ok) {
5714
5874
  nudged += 1;
5715
5875
  throttleState.set(r.name, r.next);