pi-herdsman 0.22.0 → 0.22.2

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.js CHANGED
@@ -8,14 +8,14 @@ import {
8
8
  SessionManager as SessionManager2
9
9
  } from "@earendil-works/pi-coding-agent";
10
10
  import { readFileSync as readFileSync9, statSync as statSync7, unlinkSync as unlinkSync9 } from "node:fs";
11
- import { basename as basename6, dirname as dirname7, resolve as resolve6 } from "node:path";
11
+ import { basename as basename6, dirname as dirname7, resolve as resolve7 } from "node:path";
12
12
  import { setTimeout as delay2 } from "node:timers/promises";
13
13
  import { fileURLToPath as fileURLToPath2 } from "node:url";
14
14
 
15
15
  // package.json
16
16
  var package_default = {
17
17
  name: "pi-herdsman",
18
- version: "0.22.0",
18
+ version: "0.22.2",
19
19
  type: "module",
20
20
  description: "Asynchronous Pi subagents and agent fleet orchestration for parallel coding agents with nested delegation, background work, and supervision in herdr.",
21
21
  license: "Apache-2.0",
@@ -302,6 +302,7 @@ import {
302
302
  rmdirSync as rmdirSync2,
303
303
  statSync,
304
304
  unlinkSync as unlinkSync2,
305
+ watch,
305
306
  writeSync as writeSync2
306
307
  } from "node:fs";
307
308
  import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
@@ -568,6 +569,64 @@ var LIMITS = {
568
569
  result: 4 * 1024 * 1024
569
570
  };
570
571
  var root = join3(herdsmanDataRoot(), "runtime", "mailboxes-v5");
572
+ function observeMailbox(directory, relevant, reconcile, onError) {
573
+ let watcher;
574
+ let scheduled;
575
+ let stopped = false;
576
+ let errorReported = false;
577
+ const schedule = () => {
578
+ if (stopped || scheduled) return;
579
+ scheduled = setImmediate(() => {
580
+ scheduled = void 0;
581
+ if (stopped) return;
582
+ try {
583
+ reconcile();
584
+ } catch (error2) {
585
+ onError(error2);
586
+ }
587
+ });
588
+ };
589
+ const attach = () => {
590
+ if (stopped || watcher) return;
591
+ try {
592
+ const next = watch(
593
+ directory,
594
+ { persistent: false },
595
+ (_event, filename) => {
596
+ const name = filename?.toString();
597
+ if (!name || relevant(name)) schedule();
598
+ }
599
+ );
600
+ next.on("error", (error2) => {
601
+ next.close();
602
+ if (watcher === next) watcher = void 0;
603
+ if (!stopped && !errorReported) {
604
+ errorReported = true;
605
+ onError(error2);
606
+ }
607
+ });
608
+ watcher = next;
609
+ } catch (error2) {
610
+ if (!errorReported) {
611
+ errorReported = true;
612
+ onError(error2);
613
+ }
614
+ }
615
+ };
616
+ attach();
617
+ const timer = setInterval(() => {
618
+ attach();
619
+ schedule();
620
+ }, 1e3);
621
+ timer.unref?.();
622
+ return () => {
623
+ stopped = true;
624
+ clearInterval(timer);
625
+ if (scheduled) clearImmediate(scheduled);
626
+ watcher?.close();
627
+ watcher = void 0;
628
+ };
629
+ }
571
630
  function agentMailboxPath(workspaceId, agentLabel) {
572
631
  return join3(
573
632
  root,
@@ -1076,7 +1135,7 @@ function parseControlMarker(text) {
1076
1135
  return UUID2.test(id) && id.length === 36 ? id : void 0;
1077
1136
  }
1078
1137
  function waitForState(path, predicate, options) {
1079
- return new Promise((resolve7, reject) => {
1138
+ return new Promise((resolve8, reject) => {
1080
1139
  let done = false;
1081
1140
  let abortListener;
1082
1141
  const finish = (error2, state) => {
@@ -1086,7 +1145,7 @@ function waitForState(path, predicate, options) {
1086
1145
  clearInterval(interval);
1087
1146
  if (abortListener && options.signal)
1088
1147
  options.signal.removeEventListener("abort", abortListener);
1089
- error2 ? reject(error2) : resolve7(state);
1148
+ error2 ? reject(error2) : resolve8(state);
1090
1149
  };
1091
1150
  const check = () => {
1092
1151
  try {
@@ -2144,16 +2203,51 @@ var MAX_PROCESS_CMDLINE_BYTES = 4 * 1024;
2144
2203
  var POLL_INTERVAL = 75;
2145
2204
  var INITIAL_RATIO = 0.65;
2146
2205
  var AGENT_SPLIT_RATIO = 0.5;
2147
- var LIFECYCLE_SUBSCRIPTIONS = [
2206
+ var HERDR_EVENT_SUBSCRIPTIONS = [
2207
+ { type: "workspace.created" },
2208
+ { type: "workspace.updated" },
2209
+ { type: "workspace.metadata_updated" },
2210
+ { type: "workspace.renamed" },
2211
+ { type: "pane.created" },
2148
2212
  { type: "pane.closed" },
2149
2213
  { type: "pane.exited" },
2150
2214
  { type: "pane.moved" },
2215
+ { type: "pane.updated" },
2216
+ { type: "pane.agent_detected" },
2217
+ { type: "pane.agent_status_changed" },
2218
+ { type: "tab.created" },
2151
2219
  { type: "tab.closed" },
2220
+ { type: "tab.renamed" },
2152
2221
  { type: "workspace.closed" },
2222
+ { type: "worktree.created" },
2223
+ { type: "worktree.opened" },
2153
2224
  { type: "worktree.removed" }
2154
2225
  ];
2155
- var LIFECYCLE_SUBSCRIPTION_ID = "pi-herdsman:lifecycle";
2156
- var LIFECYCLE_RECONNECT_MS = 1e3;
2226
+ var HERDR_EVENT_SUBSCRIPTION_ID = "pi-herdsman:events";
2227
+ var HERDR_EVENT_RECONNECT_MS = 1e3;
2228
+ var PRESENTATION_MIN_INTERVAL_MS = 2e3;
2229
+ var LIFECYCLE_EVENTS = /* @__PURE__ */ new Set([
2230
+ "workspace_created",
2231
+ "workspace_closed",
2232
+ "pane_created",
2233
+ "pane_closed",
2234
+ "pane_exited",
2235
+ "pane_moved",
2236
+ "tab_created",
2237
+ "tab_closed",
2238
+ "worktree_created",
2239
+ "worktree_opened",
2240
+ "worktree_removed"
2241
+ ]);
2242
+ var PRESENTATION_EVENTS = /* @__PURE__ */ new Set([
2243
+ "workspace_updated",
2244
+ "workspace_metadata_updated",
2245
+ "workspace_renamed",
2246
+ "pane_updated",
2247
+ "pane_agent_detected",
2248
+ "pane_agent_status_changed",
2249
+ "tab_renamed"
2250
+ ]);
2157
2251
  var MAX_EVENT_BUFFER_BYTES = 1024 * 1024;
2158
2252
  var HERDR_AGENT_STATE_EXTENSION = join5(
2159
2253
  getAgentDir3(),
@@ -2493,10 +2587,31 @@ async function herdrSessionSnapshot(pi, ctx, signal) {
2493
2587
  function watchHerdrLifecycle(socketPath2, signal, onChange) {
2494
2588
  let socket;
2495
2589
  let retry;
2590
+ let presentationTimer;
2591
+ let nextPresentationAt = 0;
2592
+ const invalidatePresentation = () => {
2593
+ if (presentationTimer || signal.aborted) return;
2594
+ presentationTimer = setTimeout(
2595
+ () => {
2596
+ presentationTimer = void 0;
2597
+ if (signal.aborted) return;
2598
+ nextPresentationAt = Date.now() + PRESENTATION_MIN_INTERVAL_MS;
2599
+ onChange(void 0, "presentation");
2600
+ },
2601
+ Math.max(0, nextPresentationAt - Date.now())
2602
+ );
2603
+ presentationTimer.unref?.();
2604
+ };
2605
+ const flushPresentation = () => {
2606
+ if (presentationTimer) clearTimeout(presentationTimer);
2607
+ presentationTimer = void 0;
2608
+ nextPresentationAt = Date.now() + PRESENTATION_MIN_INTERVAL_MS;
2609
+ };
2496
2610
  const connect = () => {
2497
2611
  if (signal.aborted) return;
2498
2612
  let buffer = "";
2499
2613
  let subscribed = false;
2614
+ let disconnectReported = false;
2500
2615
  const current = createConnection(socketPath2);
2501
2616
  socket = current;
2502
2617
  current.setEncoding("utf8");
@@ -2508,9 +2623,9 @@ function watchHerdrLifecycle(socketPath2, signal, onChange) {
2508
2623
  current.once("connect", () => {
2509
2624
  current.write(
2510
2625
  `${JSON.stringify({
2511
- id: LIFECYCLE_SUBSCRIPTION_ID,
2626
+ id: HERDR_EVENT_SUBSCRIPTION_ID,
2512
2627
  method: "events.subscribe",
2513
- params: { subscriptions: LIFECYCLE_SUBSCRIPTIONS }
2628
+ params: { subscriptions: HERDR_EVENT_SUBSCRIPTIONS }
2514
2629
  })}
2515
2630
  `
2516
2631
  );
@@ -2534,23 +2649,45 @@ function watchHerdrLifecycle(socketPath2, signal, onChange) {
2534
2649
  drop();
2535
2650
  return;
2536
2651
  }
2652
+ if (message?.id === HERDR_EVENT_SUBSCRIPTION_ID && message?.error) {
2653
+ if (message.error.code === "events_lost" && !signal.aborted) {
2654
+ disconnectReported = true;
2655
+ flushPresentation();
2656
+ onChange(void 0, "reconcile");
2657
+ }
2658
+ drop();
2659
+ return;
2660
+ }
2537
2661
  if (!subscribed) {
2538
- if (message?.id !== LIFECYCLE_SUBSCRIPTION_ID || message?.error || !message?.result) {
2662
+ if (signal.aborted || message?.id !== HERDR_EVENT_SUBSCRIPTION_ID || message?.result?.type !== "subscription_started") {
2539
2663
  drop();
2540
2664
  return;
2541
2665
  }
2542
2666
  subscribed = true;
2543
- onChange();
2667
+ flushPresentation();
2668
+ onChange(void 0, "reconcile");
2544
2669
  continue;
2545
2670
  }
2546
- onChange(removedHerdrWorktree(message));
2671
+ if (signal.aborted) return;
2672
+ const event = message?.event;
2673
+ const category = LIFECYCLE_EVENTS.has(event) ? "lifecycle" : PRESENTATION_EVENTS.has(event) ? "presentation" : void 0;
2674
+ if (category === "presentation") invalidatePresentation();
2675
+ else if (category) {
2676
+ flushPresentation();
2677
+ onChange(removedHerdrWorktree(message), category);
2678
+ }
2547
2679
  }
2548
2680
  });
2549
2681
  current.on("error", drop);
2550
2682
  current.once("close", () => {
2551
2683
  if (socket === current) socket = void 0;
2552
2684
  if (signal.aborted) return;
2553
- retry = setTimeout(connect, LIFECYCLE_RECONNECT_MS);
2685
+ if (subscribed && !disconnectReported) {
2686
+ disconnectReported = true;
2687
+ flushPresentation();
2688
+ onChange(void 0, "reconcile");
2689
+ }
2690
+ retry = setTimeout(connect, HERDR_EVENT_RECONNECT_MS);
2554
2691
  retry.unref?.();
2555
2692
  });
2556
2693
  };
@@ -2558,6 +2695,7 @@ function watchHerdrLifecycle(socketPath2, signal, onChange) {
2558
2695
  "abort",
2559
2696
  () => {
2560
2697
  if (retry) clearTimeout(retry);
2698
+ if (presentationTimer) clearTimeout(presentationTimer);
2561
2699
  socket?.destroy();
2562
2700
  },
2563
2701
  { once: true }
@@ -3895,7 +4033,7 @@ import {
3895
4033
  } from "@earendil-works/pi-coding-agent";
3896
4034
  import { contentText as contentText2 } from "@earendil-works/pi-ai";
3897
4035
  import { realpathSync as realpathSync3, statSync as statSync5 } from "node:fs";
3898
- import { watchFile, unwatchFile, unlinkSync as unlinkSync7 } from "node:fs";
4036
+ import { unlinkSync as unlinkSync7 } from "node:fs";
3899
4037
  import { createHash as createHash7, randomUUID as randomUUID8 } from "node:crypto";
3900
4038
  import { homedir as homedir3 } from "node:os";
3901
4039
  import { join as join9, resolve as resolve4 } from "node:path";
@@ -8090,7 +8228,7 @@ function projectSupervision(options) {
8090
8228
  // extension/agent-controller.ts
8091
8229
  function buildAgentStatusSnapshot(view, ctx, host) {
8092
8230
  const ownerSessionId = ctx.sessionManager.getSessionId();
8093
- const unresolvedMailboxState = host.scope.kind === "lead" && host.hasStateIssues?.();
8231
+ const unresolvedMailboxState = host.scope.kind === "lead" && view.stateIssues.length > 0;
8094
8232
  const listed = view.visible.map(
8095
8233
  (snapshot) => host.listedAgentRecord(
8096
8234
  view,
@@ -8262,6 +8400,7 @@ function createAgentStatusRuntime() {
8262
8400
  if (ctx.mode !== "tui" || !ctx.hasUI) return;
8263
8401
  const primed = statusPrimed && ctx === statusContext;
8264
8402
  const generation = ctx === statusContext && requestActive ? statusGeneration : ++statusGeneration;
8403
+ clearTimer();
8265
8404
  statusContext = ctx;
8266
8405
  ctx.ui.setWidget("pi-herdsman", (tui, theme) => {
8267
8406
  const widget = createStatusWidget(() => tui.requestRender(), theme);
@@ -8273,7 +8412,8 @@ function createAgentStatusRuntime() {
8273
8412
  return widget;
8274
8413
  });
8275
8414
  requestActive = true;
8276
- statusTimer = setInterval(() => void refresh(ctx, generation), 2e3);
8415
+ statusTimer = setInterval(() => void refresh(ctx, generation), 1e4);
8416
+ statusTimer.unref?.();
8277
8417
  statusPrimed = false;
8278
8418
  if (!primed) void refresh(ctx, generation);
8279
8419
  },
@@ -8506,11 +8646,6 @@ pending-result agent work still blocks escalation.`;
8506
8646
  function durableIdentityKey(state) {
8507
8647
  return `${state.workspaceId}\0${state.piSessionId}`;
8508
8648
  }
8509
- function durableParentCandidates(snapshot, child) {
8510
- return snapshot.filter(
8511
- ({ state }) => state.workspaceId === child.workspaceId && state.piSessionId === child.ownerSessionId
8512
- );
8513
- }
8514
8649
  function directChildStates(parent) {
8515
8650
  return listAgentStates().filter(
8516
8651
  ({ state }) => state.workspaceId === parent.workspaceId && state.ownerSessionId === parent.piSessionId
@@ -8635,37 +8770,31 @@ function visibleAgentSnapshots(snapshot, scope, ownerSessionId) {
8635
8770
  );
8636
8771
  if (scope.kind === "managed-agent") return direct;
8637
8772
  const visible = [...direct];
8638
- const visibleIdentities = new Set(
8639
- direct.map(({ state }) => durableIdentityKey(state))
8640
- );
8641
- const pending = agents.filter(
8642
- ({ state }) => state.ownerSessionId !== ownerSessionId
8643
- );
8644
- while (pending.length) {
8645
- let progressed = false;
8646
- for (let index = pending.length - 1; index >= 0; index--) {
8647
- const agent = pending[index];
8648
- const parents = durableParentCandidates(agents, agent.state);
8649
- if (parents.length !== 1) continue;
8650
- const parent = parents[0];
8651
- if (!visibleIdentities.has(durableIdentityKey(parent.state))) continue;
8652
- visible.push({ ...agent, parentLabel: parent.state.agentLabel });
8653
- visibleIdentities.add(durableIdentityKey(agent.state));
8654
- pending.splice(index, 1);
8655
- progressed = true;
8656
- }
8657
- if (!progressed) break;
8773
+ const byIdentity = /* @__PURE__ */ new Map();
8774
+ const childrenByOwner = /* @__PURE__ */ new Map();
8775
+ for (const agent of agents) {
8776
+ const identity = durableIdentityKey(agent.state);
8777
+ const owner = `${agent.state.workspaceId}\0${agent.state.ownerSessionId}`;
8778
+ const identityCandidates = byIdentity.get(identity) ?? [];
8779
+ identityCandidates.push(agent);
8780
+ byIdentity.set(identity, identityCandidates);
8781
+ const children = childrenByOwner.get(owner) ?? [];
8782
+ children.push(agent);
8783
+ childrenByOwner.set(owner, children);
8784
+ }
8785
+ const seen = new Set(direct);
8786
+ for (let index = 0; index < visible.length; index++) {
8787
+ const parent = visible[index];
8788
+ const identity = durableIdentityKey(parent.state);
8789
+ if (byIdentity.get(identity)?.length !== 1) continue;
8790
+ for (const child of childrenByOwner.get(identity) ?? []) {
8791
+ if (seen.has(child)) continue;
8792
+ seen.add(child);
8793
+ visible.push({ ...child, parentLabel: parent.state.agentLabel });
8794
+ }
8658
8795
  }
8659
8796
  return visible;
8660
8797
  }
8661
- function unknownAgentRecords() {
8662
- return listAgentStateIssues().map(({ diagnostic }) => ({
8663
- state: "unknown",
8664
- available_tools: [],
8665
- managed: true,
8666
- diagnostic
8667
- }));
8668
- }
8669
8798
  function statusBreadcrumb(snapshot, candidate, identity, sameIdentity) {
8670
8799
  if (!candidate) return ["?"];
8671
8800
  const current = snapshot.agents.find(
@@ -9417,11 +9546,25 @@ function assertManagedAgentCascadeSafe(snapshots, sameIdentity, deliveredRootRes
9417
9546
  async function managedAgentSnapshots(pi, ctx, dependencies, signal, proveLead = false, allWorkspaces = false, suppliedInventory, allowTranscriptDefinitionFallback = true) {
9418
9547
  const inventory = suppliedInventory ?? await herdrSessionSnapshot(pi, ctx, signal);
9419
9548
  const currentWorkspaceId = process.env.HERDR_WORKSPACE_ID ?? ctx.cwd;
9420
- const allMailboxes = listAgentStates();
9549
+ const { states: allMailboxes, issues: stateIssues } = scanAgentStates();
9421
9550
  const mailboxes = allWorkspaces ? allMailboxes : allMailboxes.filter(
9422
9551
  ({ state }) => state.workspaceId === currentWorkspaceId
9423
9552
  );
9424
- const agents = mailboxes.map(({ path, state }) => {
9553
+ const pendingOwners = /* @__PURE__ */ new Set();
9554
+ const completedResultsPending = mailboxes.map(({ path, state }) => {
9555
+ let pending = false;
9556
+ if (state.completedRequestId) {
9557
+ try {
9558
+ pending = !!readResult(path, state.completedRequestId);
9559
+ } catch {
9560
+ pending = true;
9561
+ }
9562
+ }
9563
+ if (state.resultError || state.activeRequestId || pending)
9564
+ pendingOwners.add(`${state.workspaceId}\0${state.ownerSessionId}`);
9565
+ return pending;
9566
+ });
9567
+ const agents = mailboxes.map(({ path, state }, index) => {
9425
9568
  const presence = managedAgentPresence(state, inventory);
9426
9569
  const agent = presence.kind === "live" ? presence.agent : void 0;
9427
9570
  let agentDefinition;
@@ -9440,14 +9583,7 @@ async function managedAgentSnapshots(pi, ctx, dependencies, signal, proveLead =
9440
9583
  const piSessionId = session?.kind === "id" ? session.value : state.piSessionId;
9441
9584
  const piSessionPath = session?.kind === "path" ? session.value : state.piSessionFile;
9442
9585
  const lifecycleState = agent ? normalizeHerdrLifecycleState(agent) : "unknown";
9443
- let completionPending = false;
9444
- if (state.completedRequestId) {
9445
- try {
9446
- completionPending = !!readResult(path, state.completedRequestId);
9447
- } catch {
9448
- completionPending = true;
9449
- }
9450
- }
9586
+ const completionPending = completedResultsPending[index];
9451
9587
  const handoffPending = unacknowledgedRequestExists(path, state);
9452
9588
  const liveState = agent ? agentControlState(
9453
9589
  lifecycleState,
@@ -9457,19 +9593,7 @@ async function managedAgentSnapshots(pi, ctx, dependencies, signal, proveLead =
9457
9593
  !!state.pendingAskId,
9458
9594
  !!state.resultError
9459
9595
  ) : "unknown";
9460
- const pendingDirectChildWork = mailboxes.some(
9461
- ({ path: childPath, state: child }) => {
9462
- if (child.workspaceId !== state.workspaceId || child.ownerSessionId !== state.piSessionId)
9463
- return false;
9464
- if (child.resultError || child.activeRequestId) return true;
9465
- if (!child.completedRequestId) return false;
9466
- try {
9467
- return !!readResult(childPath, child.completedRequestId);
9468
- } catch {
9469
- return true;
9470
- }
9471
- }
9472
- );
9596
+ const pendingDirectChildWork = pendingOwners.has(durableIdentityKey(state));
9473
9597
  const waitingForChildren = liveState === "settling" && !!state.activeRequestId && !completionPending && !handoffPending && !state.resultError && pendingDirectChildWork;
9474
9598
  const existingLiveProjection = waitingForChildren ? "blocked" : liveState;
9475
9599
  const projectedState = completionPending || state.resultError ? "settling" : presence.kind === "lost" ? "lost" : presence.kind === "unknown" ? "unknown" : existingLiveProjection;
@@ -9512,7 +9636,13 @@ async function managedAgentSnapshots(pi, ctx, dependencies, signal, proveLead =
9512
9636
  } catch {
9513
9637
  }
9514
9638
  }
9515
- return { agents, mailboxes, liveAgents: inventory.agents, leadSessionIds };
9639
+ return {
9640
+ agents,
9641
+ mailboxes,
9642
+ stateIssues,
9643
+ liveAgents: inventory.agents,
9644
+ leadSessionIds
9645
+ };
9516
9646
  }
9517
9647
  function listedAgentRecord(view, snapshot, ownerSessionId, scope, unresolvedMailboxState, dependencies) {
9518
9648
  const { listed, state, presence, parentLabel } = snapshot;
@@ -9581,9 +9711,7 @@ function createAgentController(pi, options) {
9581
9711
  const runtimes = /* @__PURE__ */ new Map();
9582
9712
  const pendingStarts = /* @__PURE__ */ new Map();
9583
9713
  const resultWatchers = /* @__PURE__ */ new Map();
9584
- const resultWatchRetries = /* @__PURE__ */ new Map();
9585
9714
  const askWatchers = /* @__PURE__ */ new Map();
9586
- const askWatchRetries = /* @__PURE__ */ new Map();
9587
9715
  const resultDeliveryInFlight = /* @__PURE__ */ new Set();
9588
9716
  const askDeliveryInFlight = /* @__PURE__ */ new Set();
9589
9717
  const resultDeliveryRetries = /* @__PURE__ */ new Map();
@@ -10279,7 +10407,8 @@ function createAgentController(pi, options) {
10279
10407
  runtime.startedAt = Date.now();
10280
10408
  runtime.contextPercent = void 0;
10281
10409
  watchResult(runtime, context, signal);
10282
- watchAsk(runtime, context, signal);
10410
+ if (runtime.activeRequestId === requestId)
10411
+ watchAsk(runtime, context, signal);
10283
10412
  }
10284
10413
  return requestId;
10285
10414
  } finally {
@@ -10520,7 +10649,12 @@ function createAgentController(pi, options) {
10520
10649
  scope,
10521
10650
  context.sessionManager.getSessionId()
10522
10651
  );
10523
- const unknown = scope?.kind === "lead" ? unknownAgentRecords() : [];
10652
+ const unknown = scope?.kind === "lead" ? snapshot2.stateIssues.map(({ diagnostic }) => ({
10653
+ state: "unknown",
10654
+ available_tools: [],
10655
+ managed: true,
10656
+ diagnostic
10657
+ })) : [];
10524
10658
  const unresolved = unknown.length > 0;
10525
10659
  const agents = visible.map(
10526
10660
  (item) => listRecord(
@@ -10630,7 +10764,7 @@ function createAgentController(pi, options) {
10630
10764
  "Agent belongs to another owner session",
10631
10765
  "transcript"
10632
10766
  );
10633
- const unresolved = scope?.kind === "lead" && listAgentStateIssues().length > 0;
10767
+ const unresolved = scope?.kind === "lead" && view.stateIssues.length > 0;
10634
10768
  const actions = listedAgentRecord(
10635
10769
  view,
10636
10770
  candidate,
@@ -11106,21 +11240,13 @@ function createAgentController(pi, options) {
11106
11240
  if (!id) return;
11107
11241
  cancelResultRetry(resultDeliveryRetries, runtime, id);
11108
11242
  cancelResultRetry(resultCleanupRetries, runtime, id);
11109
- const path = `${runtime.mailboxPath}/result-${id}.json`;
11110
- const retry = resultWatchRetries.get(path);
11111
- if (retry) clearTimeout(retry);
11112
- resultWatchRetries.delete(path);
11113
- const watcher = resultWatchers.get(path);
11114
- if (watcher) unwatchFile(path, watcher);
11243
+ const path = join9(runtime.mailboxPath, `result-${id}.json`);
11244
+ resultWatchers.get(path)?.();
11115
11245
  resultWatchers.delete(path);
11116
11246
  };
11117
11247
  const stopAskWatcher = (runtime) => {
11118
11248
  const path = agentStatePath(runtime.mailboxPath);
11119
- const retry = askWatchRetries.get(path);
11120
- if (retry) clearTimeout(retry);
11121
- askWatchRetries.delete(path);
11122
- const watcher = askWatchers.get(path);
11123
- if (watcher) unwatchFile(path, watcher);
11249
+ askWatchers.get(path)?.();
11124
11250
  askWatchers.delete(path);
11125
11251
  };
11126
11252
  const invalidateRuntime = (label) => {
@@ -11141,29 +11267,10 @@ function createAgentController(pi, options) {
11141
11267
  runtime.startedAt = void 0;
11142
11268
  runtimes.delete(label);
11143
11269
  };
11144
- const watch = (path, watchers, retries, context, check, retry) => {
11145
- watchers.set(path, check);
11146
- try {
11147
- check();
11148
- if (watchers.get(path) !== check) return;
11149
- watchFile(path, { interval: 250 }, check);
11150
- } catch (error2) {
11151
- watchers.delete(path);
11152
- options.reportWatcherError(context, error2);
11153
- if (!retries.has(path)) {
11154
- retries.set(
11155
- path,
11156
- setTimeout(() => {
11157
- retries.delete(path);
11158
- retry();
11159
- }, 250)
11160
- );
11161
- }
11162
- }
11163
- };
11164
11270
  const watchResult = (runtime, context, signal, requestId = runtime.activeRequestId) => {
11165
11271
  if (!requestId) return;
11166
- const path = `${runtime.mailboxPath}/result-${requestId}.json`;
11272
+ const filename = `result-${requestId}.json`;
11273
+ const path = join9(runtime.mailboxPath, filename);
11167
11274
  stopResultWatcher(runtime, requestId);
11168
11275
  const check = () => {
11169
11276
  if (active && runtimes.get(runtime.label) === runtime)
@@ -11173,10 +11280,14 @@ function createAgentController(pi, options) {
11173
11280
  } catch {
11174
11281
  }
11175
11282
  };
11176
- watch(path, resultWatchers, resultWatchRetries, context, check, () => {
11177
- if (runtime.activeRequestId === requestId && active)
11178
- watchResult(runtime, context, signal, requestId);
11179
- });
11283
+ const stop = observeMailbox(
11284
+ runtime.mailboxPath,
11285
+ (name) => name === filename,
11286
+ check,
11287
+ (error2) => options.reportWatcherError(context, error2)
11288
+ );
11289
+ resultWatchers.set(path, stop);
11290
+ check();
11180
11291
  };
11181
11292
  const watchAsk = (runtime, context, signal) => {
11182
11293
  const path = agentStatePath(runtime.mailboxPath);
@@ -11189,21 +11300,16 @@ function createAgentController(pi, options) {
11189
11300
  if (error2 instanceof OperationError && error2.detail.category === "incompatible_build")
11190
11301
  return;
11191
11302
  options.reportWatcherError(context, error2);
11192
- if (!askWatchRetries.has(path))
11193
- askWatchRetries.set(
11194
- path,
11195
- setTimeout(() => {
11196
- askWatchRetries.delete(path);
11197
- if (active && runtimes.get(runtime.label) === runtime)
11198
- watchAsk(runtime, context, signal);
11199
- }, 250)
11200
- );
11201
11303
  }
11202
11304
  };
11203
- watch(path, askWatchers, askWatchRetries, context, check, () => {
11204
- if (active && runtimes.get(runtime.label) === runtime)
11205
- watchAsk(runtime, context, signal);
11206
- });
11305
+ const stop = observeMailbox(
11306
+ runtime.mailboxPath,
11307
+ (name) => name === "state.json" || name === "ask.json",
11308
+ check,
11309
+ (error2) => options.reportWatcherError(context, error2)
11310
+ );
11311
+ askWatchers.set(path, stop);
11312
+ check();
11207
11313
  };
11208
11314
  const resultIdentity = (runtime, requestId) => ({
11209
11315
  runId: runtime.runId,
@@ -11717,6 +11823,8 @@ function createAgentController(pi, options) {
11717
11823
  runtime.task = void 0;
11718
11824
  runtime.startedAt = void 0;
11719
11825
  runtime.contextPercent = void 0;
11826
+ if (pendingStarts.get(runtime.label)?.requestId === result.requestId)
11827
+ pendingStarts.delete(runtime.label);
11720
11828
  if (!await finalizeDeliveredResult(runtime, result, context, signal))
11721
11829
  scheduleResultCleanupRetry(runtime, result, context, signal);
11722
11830
  clearCleanupError(runtime, "Result delivery failed; retrying:");
@@ -12025,6 +12133,7 @@ function createAgentController(pi, options) {
12025
12133
  const pendingStart = {
12026
12134
  label,
12027
12135
  definition: agentDefinition,
12136
+ requestId: assignment.requestId,
12028
12137
  ...p.task !== void 0 ? { task: p.task } : {},
12029
12138
  startedAt: Date.now(),
12030
12139
  ...scope.kind === "managed-agent" && process.env.PI_HERDSMAN_LABEL ? { parentLabel: process.env.PI_HERDSMAN_LABEL } : {}
@@ -12265,7 +12374,6 @@ ${result.stderr ?? ""}`.trim(),
12265
12374
  p.action,
12266
12375
  assignmentInput.resultBindings
12267
12376
  );
12268
- pendingStart.requestId = requestId;
12269
12377
  accepted = true;
12270
12378
  options.onChanged();
12271
12379
  return {
@@ -12277,7 +12385,7 @@ ${result.stderr ?? ""}`.trim(),
12277
12385
  pane_id: runtime.paneId,
12278
12386
  session_id: runtime.piSessionId,
12279
12387
  session_path: runtime.piSessionFile,
12280
- request_id: runtime.activeRequestId
12388
+ request_id: requestId
12281
12389
  };
12282
12390
  } catch (caught) {
12283
12391
  if (promptWriteFailed) throw caught;
@@ -12943,7 +13051,7 @@ ${result.stderr ?? ""}`.trim(),
12943
13051
  ...snapshot2,
12944
13052
  visible: visibleAgentSnapshots(snapshot2, options.scope, ownerSessionId)
12945
13053
  };
12946
- const unresolvedMailboxState = options.scope?.kind === "lead" && listAgentStateIssues().length > 0;
13054
+ const unresolvedMailboxState = options.scope?.kind === "lead" && snapshot2.stateIssues.length > 0;
12947
13055
  const now = Date.now();
12948
13056
  const ownedRuns = new Set(
12949
13057
  snapshot2.agents.filter(({ state }) => state.ownerSessionId === ownerSessionId).map(({ state }) => state.runId)
@@ -13418,11 +13526,15 @@ ${result.stderr ?? ""}`.trim(),
13418
13526
  };
13419
13527
  requestHealthScan();
13420
13528
  if (process.env.HERDR_SOCKET_PATH) {
13421
- watchHerdrLifecycle(process.env.HERDR_SOCKET_PATH, signal, (removed) => {
13422
- options.onChanged();
13423
- requestHealthScan();
13424
- if (removed) options.onWorktreeRemoved?.(removed, ctx, signal);
13425
- });
13529
+ watchHerdrLifecycle(
13530
+ process.env.HERDR_SOCKET_PATH,
13531
+ signal,
13532
+ (removed, category) => {
13533
+ options.onChanged();
13534
+ if (category !== "presentation") requestHealthScan();
13535
+ if (removed) options.onWorktreeRemoved?.(removed, ctx, signal);
13536
+ }
13537
+ );
13426
13538
  }
13427
13539
  schedule();
13428
13540
  };
@@ -13524,14 +13636,10 @@ ${result.stderr ?? ""}`.trim(),
13524
13636
  },
13525
13637
  shutdown() {
13526
13638
  active = false;
13527
- for (const [path, watcher] of resultWatchers) unwatchFile(path, watcher);
13528
- for (const [path, watcher] of askWatchers) unwatchFile(path, watcher);
13639
+ for (const watcher of resultWatchers.values()) watcher();
13640
+ for (const watcher of askWatchers.values()) watcher();
13529
13641
  resultWatchers.clear();
13530
13642
  askWatchers.clear();
13531
- for (const timer of resultWatchRetries.values()) clearTimeout(timer);
13532
- for (const timer of askWatchRetries.values()) clearTimeout(timer);
13533
- resultWatchRetries.clear();
13534
- askWatchRetries.clear();
13535
13643
  for (const timer of resultDeliveryRetries.values()) clearTimeout(timer);
13536
13644
  for (const timer of resultCleanupRetries.values()) clearTimeout(timer);
13537
13645
  for (const timer of askDeliveryRetries.values()) clearTimeout(timer);
@@ -13874,7 +13982,7 @@ function createManagedAgentExecutionState() {
13874
13982
  resultWriteAttempts: 0,
13875
13983
  retryTimer: void 0,
13876
13984
  stateRetryTimer: void 0,
13877
- requestPumpTimer: void 0,
13985
+ stopRequestObserver: void 0,
13878
13986
  stateErrorReported: false,
13879
13987
  resultErrorReported: false,
13880
13988
  agentContext: void 0,
@@ -14202,7 +14310,7 @@ function createManagedAgentLeafStatus(options) {
14202
14310
  else next.dispose();
14203
14311
  return next;
14204
14312
  });
14205
- timer = setInterval(() => void refresh(ctx, activeGeneration), 2e3);
14313
+ timer = setInterval(() => void refresh(ctx, activeGeneration), 1e4);
14206
14314
  void refresh(ctx, activeGeneration);
14207
14315
  }
14208
14316
  };
@@ -15007,8 +15115,8 @@ This is the session-start definition snapshot. Use list_agents for live Agent st
15007
15115
  }));
15008
15116
  };
15009
15117
  const resetRequestPump = () => {
15010
- if (execution.requestPumpTimer) clearInterval(execution.requestPumpTimer);
15011
- execution.requestPumpTimer = void 0;
15118
+ execution.stopRequestObserver?.();
15119
+ execution.stopRequestObserver = void 0;
15012
15120
  requestState.reset();
15013
15121
  };
15014
15122
  const clearAgentRuntimes = () => controller?.clearRuntimes();
@@ -15573,12 +15681,13 @@ function registerManagedAgentSessionStartHandler(pi, execution, options) {
15573
15681
  if (options.delegationEnabled)
15574
15682
  options.startControllerHealthScanner(ctx, metadataSignal);
15575
15683
  execution.initialized = true;
15576
- options.pumpRequest(ctx);
15577
- execution.requestPumpTimer = setInterval(
15684
+ execution.stopRequestObserver = observeMailbox(
15685
+ mailbox,
15686
+ (name) => /^request-.*\.json$/.test(name),
15578
15687
  () => options.pumpRequest(ctx),
15579
- 250
15688
+ (error2) => options.appendError(ctx, error2)
15580
15689
  );
15581
- execution.requestPumpTimer.unref?.();
15690
+ options.pumpRequest(ctx);
15582
15691
  if (!options.delegationEnabled && ctx.mode === "tui" && ctx.hasUI)
15583
15692
  options.startLeafStatus(ctx);
15584
15693
  const model = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : void 0;
@@ -15596,6 +15705,7 @@ function registerManagedAgentSessionStartHandler(pi, execution, options) {
15596
15705
  );
15597
15706
  } catch (error2) {
15598
15707
  execution.initialized = false;
15708
+ options.resetRequestPump();
15599
15709
  options.statusRuntime.clear();
15600
15710
  options.setControllerReady(false);
15601
15711
  execution.assignment = void 0;
@@ -15612,6 +15722,7 @@ import { visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
15612
15722
  import { contentText as contentText4, getSupportedThinkingLevels } from "@earendil-works/pi-ai";
15613
15723
  import { randomUUID as randomUUID10 } from "node:crypto";
15614
15724
  import { realpathSync as realpathSync5, statSync as statSync6, unlinkSync as unlinkSync8 } from "node:fs";
15725
+ import { isAbsolute as isAbsolute5, resolve as resolve6 } from "node:path";
15615
15726
  var LEAD_INSTANCE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
15616
15727
  function createLeadRuntimeState() {
15617
15728
  return {
@@ -15866,6 +15977,13 @@ function createLeadSessionStartRuntime(host) {
15866
15977
  ...host.ownToolsSnapshot()
15867
15978
  });
15868
15979
  const activeManager = host.isLead() && host.roleTransitions.activeManager();
15980
+ if (activeManager && !sessionSignal.aborted) {
15981
+ try {
15982
+ await host.roleTransitions.reconcileCurrentManagerAssignments(ctx);
15983
+ } catch (error2) {
15984
+ host.appendRoleError(ctx, error2);
15985
+ }
15986
+ }
15869
15987
  if (host.isLead() && (host.roleTransitions.chiefModeActive() || activeManager)) {
15870
15988
  host.startSupervisionUI(ctx);
15871
15989
  } else host.leadStatusRuntime.start(ctx);
@@ -16107,19 +16225,21 @@ function createLeadCommandRuntime(host) {
16107
16225
  async openDefinitionsMenu(ctx) {
16108
16226
  let selectedDefinition;
16109
16227
  const loadDefinitions = async () => {
16110
- const { definitions, projectTrusted } = await host.contextAgentDefinitions(ctx);
16228
+ const { definitions, leadDefinitions } = await host.contextAgentDefinitions(ctx);
16111
16229
  return {
16112
16230
  definitions,
16113
16231
  leadDefinitions: [
16114
16232
  FLEXIBLE_LEAD_DEFINITION_NAME,
16115
16233
  ORCHESTRATOR_LEAD_DEFINITION_NAME,
16116
16234
  MANAGED_LEAD_DEFINITION_NAME
16117
- ].map(
16118
- (name) => host.discoverLeadDefinition(
16119
- name,
16120
- projectTrusted ? { projectRoot: ctx.cwd } : {}
16121
- )
16122
- )
16235
+ ].map((name) => {
16236
+ const definition = leadDefinitions.find(
16237
+ (candidate) => candidate.name === name
16238
+ );
16239
+ if (!definition)
16240
+ throw new Error(`Lead definition ${name} not found`);
16241
+ return definition;
16242
+ })
16123
16243
  };
16124
16244
  };
16125
16245
  const resolveDefinition = async (name) => {
@@ -16777,6 +16897,7 @@ function registerLeadRuntime(pi, options) {
16777
16897
  pi,
16778
16898
  isCurrentChief: (ctx) => options.leadRuntime.chiefMode === "active" && !!options.leadRuntime.chiefLease && options.leadRuntime.chiefLease.descriptor.piSessionId === ctx.sessionManager.getSessionId(),
16779
16899
  currentManager: (...args) => roleTransitions.currentManager(...args),
16900
+ reconcileProjectAssignments: (...args) => roleTransitions.reconcileProjectAssignments(...args),
16780
16901
  currentChief: (...args) => roleTransitions.currentChief(...args),
16781
16902
  currentChiefAuthority: (...args) => roleTransitions.currentChiefAuthority(...args),
16782
16903
  latestMessageText: (ctx) => coordinationRuntime.latestCustomMessageText(
@@ -16790,6 +16911,14 @@ function registerLeadRuntime(pi, options) {
16790
16911
  return !!file2?.isFile() && file2.size > 0;
16791
16912
  })
16792
16913
  });
16914
+ const writeProjectAssignment2 = (...args) => {
16915
+ options.projectHost.writeProjectAssignment(...args);
16916
+ supervisionUiRuntime.requestRefresh();
16917
+ };
16918
+ const removeProjectAssignment2 = (...args) => {
16919
+ options.roleTransitionHost.removeProjectAssignment(...args);
16920
+ supervisionUiRuntime.requestRefresh();
16921
+ };
16793
16922
  const clearSupervisionUI = (removeWidget = true) => {
16794
16923
  supervisionUiRuntime.stopPeriodic();
16795
16924
  if (removeWidget && leadRuntime.leadContext)
@@ -16939,6 +17068,7 @@ function registerLeadRuntime(pi, options) {
16939
17068
  };
16940
17069
  const roleTransitionServices = {
16941
17070
  ...roleHost,
17071
+ removeProjectAssignment: removeProjectAssignment2,
16942
17072
  controllerScope: { kind: "lead" },
16943
17073
  activationGuard,
16944
17074
  advancePeerPresenceGeneration: () => coordinationRuntime.advancePresenceGeneration(),
@@ -17018,7 +17148,10 @@ function registerLeadRuntime(pi, options) {
17018
17148
  ...options.controllerServices,
17019
17149
  scope: { kind: "lead" },
17020
17150
  build: options.build,
17021
- onChanged: () => statusRuntime.requestRefresh(),
17151
+ onChanged: () => {
17152
+ statusRuntime.requestRefresh();
17153
+ supervisionUiRuntime.requestRefresh();
17154
+ },
17022
17155
  onWorkChanged: (ctx) => herdRunRuntime?.maybeFinish(ctx),
17023
17156
  onWorktreeRemoved: (removed, ctx, signal) => {
17024
17157
  void roleTransitions.retireRemovedProjectWork(removed, ctx, signal).then(async () => {
@@ -17111,9 +17244,11 @@ function registerLeadRuntime(pi, options) {
17111
17244
  const projectHost = options.projectHost;
17112
17245
  const projectServices = {
17113
17246
  ...projectHost,
17247
+ writeProjectAssignment: writeProjectAssignment2,
17114
17248
  liveAgent,
17115
17249
  liveLead,
17116
17250
  currentManager: (...args) => roleTransitions.currentManager(...args),
17251
+ reconcileProjectAssignments: (...args) => roleTransitions.reconcileProjectAssignments(...args),
17117
17252
  sameManagerDescriptor: projectHost.sameManagerDescriptor,
17118
17253
  withProjectWorkLock: (...args) => roleTransitions.withProjectWorkLock(...args),
17119
17254
  withProjectAssignmentLock: (...args) => roleTransitions.withProjectAssignmentLock(...args),
@@ -17588,6 +17723,8 @@ function createLeadSupervisionRuntime(host) {
17588
17723
  manager.workspaceId,
17589
17724
  ctx.signal
17590
17725
  );
17726
+ if (group.repoKey !== manager.repoKey || group.primaryWorkspaceId !== manager.workspaceId)
17727
+ throw new Error("Manager workspace is not the assigned project");
17591
17728
  const [leads, topology] = await Promise.all([
17592
17729
  directReports(ctx, suppliedInventory, suppliedAgents),
17593
17730
  host.runHerdr(
@@ -17597,8 +17734,15 @@ function createLeadSupervisionRuntime(host) {
17597
17734
  { signal: ctx.signal }
17598
17735
  )
17599
17736
  ]);
17600
- if (topology?.source?.repo_key !== group.repoKey || !Array.isArray(topology?.worktrees))
17737
+ if (topology?.source?.repo_key !== group.repoKey || topology?.source?.source_workspace_id !== group.primaryWorkspaceId || !Array.isArray(topology?.worktrees))
17601
17738
  throw new Error("Herdr worktree topology is not authoritative");
17739
+ if (typeof topology.source.source_checkout_path === "string")
17740
+ await host.reconcileProjectAssignments?.(
17741
+ ctx,
17742
+ group.repoKey,
17743
+ group.primaryWorkspaceId,
17744
+ topology.source.source_checkout_path
17745
+ );
17602
17746
  const assignments = host.listProjectAssignments(
17603
17747
  host.supervisionRuntime(),
17604
17748
  group.repoKey
@@ -17891,6 +18035,8 @@ function createLeadSupervisionRuntime(host) {
17891
18035
  let generationId;
17892
18036
  let stale = false;
17893
18037
  let timer;
18038
+ let redrawTimer;
18039
+ let uiContext;
17894
18040
  let inFlight;
17895
18041
  let pending;
17896
18042
  let overviewGeneration = 0;
@@ -18355,15 +18501,35 @@ function createLeadSupervisionRuntime(host) {
18355
18501
  },
18356
18502
  startPeriodic: (ctx) => {
18357
18503
  if (timer) clearInterval(timer);
18358
- timer = setInterval(() => void refresh(ctx), 2e3);
18504
+ if (redrawTimer) clearInterval(redrawTimer);
18505
+ timer = void 0;
18506
+ redrawTimer = void 0;
18507
+ uiContext = void 0;
18508
+ if (ctx.mode !== "tui" || !ctx.hasUI) return;
18509
+ uiContext = ctx;
18510
+ timer = setInterval(() => {
18511
+ if (uiContext === ctx) void refresh(ctx);
18512
+ }, 1e4);
18359
18513
  timer.unref?.();
18514
+ if (host.activeRole() === "manager") {
18515
+ redrawTimer = setInterval(() => {
18516
+ if (uiContext === ctx) renderWidget?.();
18517
+ }, 2e3);
18518
+ redrawTimer.unref?.();
18519
+ }
18520
+ },
18521
+ requestRefresh: () => {
18522
+ if (uiContext) void refresh(uiContext);
18360
18523
  },
18361
18524
  stopPeriodic: () => {
18362
18525
  pending = void 0;
18526
+ uiContext = void 0;
18363
18527
  renderOverview = void 0;
18364
18528
  renderWidget = void 0;
18365
18529
  if (timer) clearInterval(timer);
18366
18530
  timer = void 0;
18531
+ if (redrawTimer) clearInterval(redrawTimer);
18532
+ redrawTimer = void 0;
18367
18533
  }
18368
18534
  };
18369
18535
  }
@@ -18613,7 +18779,8 @@ function createLeadProjectRuntime(host) {
18613
18779
  stopHerdrAgentPreservingPane: stopHerdrAgentPreservingPane2,
18614
18780
  expectedSession: expectedSession3,
18615
18781
  liveLead,
18616
- withProjectWorkLock
18782
+ withProjectWorkLock,
18783
+ withProjectAssignmentLock
18617
18784
  } = host;
18618
18785
  const {
18619
18786
  activeRole,
@@ -18683,35 +18850,44 @@ function createLeadProjectRuntime(host) {
18683
18850
  );
18684
18851
  const primaryWorkspaceId = group.primaryWorkspaceId;
18685
18852
  const runtime = supervisionRuntime2();
18686
- const existing = readProjectAssignment2(
18687
- runtime,
18688
- manager.repoKey,
18689
- operation.branch
18853
+ const branch = operation.branch;
18854
+ const topology = await runHerdr2(
18855
+ pi,
18856
+ ctx,
18857
+ ["worktree", "list", "--workspace", primaryWorkspaceId],
18858
+ { signal }
18690
18859
  );
18860
+ if (topology?.source?.source_workspace_id !== primaryWorkspaceId || topology?.source?.repo_key !== manager.repoKey || !Array.isArray(topology?.worktrees))
18861
+ throw new Error("Herdr worktree topology is not authoritative");
18862
+ if (operation.action === "resume")
18863
+ await host.reconcileProjectAssignments(
18864
+ ctx,
18865
+ manager.repoKey,
18866
+ primaryWorkspaceId,
18867
+ typeof topology.source.source_checkout_path === "string" ? topology.source.source_checkout_path : "",
18868
+ true,
18869
+ branch
18870
+ );
18871
+ const existing = readProjectAssignment2(runtime, manager.repoKey, branch);
18691
18872
  if (existing && existing.repoKey !== manager.repoKey)
18692
18873
  throw new Error("Project assignment belongs to another repository");
18693
18874
  if (operation.action === "delegate" && existing)
18694
18875
  throw new Error(
18695
- `Work already exists on ${operation.branch}; resume it with resume_project.`
18876
+ `Work already exists on ${branch}; resume it with resume_project.`
18696
18877
  );
18697
18878
  if (operation.action === "resume" && !existing)
18698
18879
  throw new Error(
18699
- `No existing work was found on ${operation.branch}; start it with delegate_project and a task.`
18880
+ `No existing work was found on ${branch}; start it with delegate_project and a task.`
18700
18881
  );
18701
18882
  const presentationTask = operation.action === "resume" ? projectTaskSummary(existing.text) : void 0;
18702
18883
  const id = operation.action === "resume" ? existing.id : operation.id;
18703
- const branch = operation.branch;
18704
- const topology = await runHerdr2(
18705
- pi,
18706
- ctx,
18707
- ["worktree", "list", "--workspace", primaryWorkspaceId],
18708
- { signal }
18709
- );
18710
- if (topology?.source?.source_workspace_id !== primaryWorkspaceId || topology?.source?.repo_key !== manager.repoKey || !Array.isArray(topology?.worktrees))
18711
- throw new Error("Herdr worktree topology is not authoritative");
18712
18884
  const branchWorktrees = topology.worktrees.filter(
18713
18885
  (worktree) => worktree?.branch === branch
18714
18886
  );
18887
+ if (operation.action === "resume" && branchWorktrees.length === 0)
18888
+ throw new Error(
18889
+ "Worktree availability could not be verified; the existing assignment was preserved. Retry when repository access is available."
18890
+ );
18715
18891
  if (branchWorktrees.length > 1)
18716
18892
  throw new Error(`Multiple Herdr worktrees match branch ${branch}`);
18717
18893
  const live = operation.action === "resume" ? await liveAgent(ctx, existing.id) : [];
@@ -18830,7 +19006,6 @@ function createLeadProjectRuntime(host) {
18830
19006
  text: prepared.text,
18831
19007
  ...prepared.resultBindings.length ? { resultBindings: prepared.resultBindings } : {}
18832
19008
  };
18833
- writeProjectAssignment2(runtime, assignment);
18834
19009
  }
18835
19010
  const promptPaths = [];
18836
19011
  try {
@@ -18877,6 +19052,8 @@ function createLeadProjectRuntime(host) {
18877
19052
  );
18878
19053
  }
18879
19054
  } else {
19055
+ if (operation.action === "resume")
19056
+ throw new Error("The existing project checkout is unavailable");
18880
19057
  const args = [
18881
19058
  "worktree",
18882
19059
  "create",
@@ -18886,44 +19063,7 @@ function createLeadProjectRuntime(host) {
18886
19063
  assignment.branch,
18887
19064
  "--no-focus"
18888
19065
  ];
18889
- if (operation.action === "resume") {
18890
- let savedCwd;
18891
- if (assignment.piSessionFile) {
18892
- const saved = SessionManager3.open(assignment.piSessionFile);
18893
- if (saved.getSessionId() !== assignment.id)
18894
- throw new Error(
18895
- "Persisted project session identity does not match"
18896
- );
18897
- savedCwd = saved.getCwd();
18898
- } else {
18899
- const saved = (await SessionManager3.listAll()).filter(
18900
- (session) => session.id === assignment.id
18901
- );
18902
- if (saved.length > 1)
18903
- throw new Error(
18904
- `Pi session ${assignment.id} is ambiguous; project work was preserved.`
18905
- );
18906
- if (saved.length === 1) {
18907
- const sessionFile = saved[0].path;
18908
- if (typeof sessionFile !== "string" || !sessionFile)
18909
- throw new Error(
18910
- "Legacy project session has no exact file path"
18911
- );
18912
- const session = SessionManager3.open(sessionFile);
18913
- if (session.getSessionId() !== assignment.id)
18914
- throw new Error(
18915
- "Persisted project session identity does not match"
18916
- );
18917
- savedCwd = session.getCwd();
18918
- assignment = await persistLeadSessionFile(
18919
- assignment,
18920
- realpathSync5(sessionFile)
18921
- );
18922
- }
18923
- }
18924
- args.push("--base", assignment.branch);
18925
- if (savedCwd) args.push("--path", savedCwd);
18926
- } else args.push("--base", operation.base ?? "HEAD");
19066
+ args.push("--base", operation.base ?? "HEAD");
18927
19067
  const created = await runHerdr2(pi, ctx, args, { signal });
18928
19068
  workspaceId = created?.workspace?.workspace_id;
18929
19069
  paneId = created?.root_pane?.pane_id;
@@ -18938,6 +19078,40 @@ function createLeadProjectRuntime(host) {
18938
19078
  (value2) => typeof value2 === "string" && value2
18939
19079
  ))
18940
19080
  throw new Error("Herdr did not return exact worktree identities");
19081
+ if (operation.action === "delegate") {
19082
+ const [common, branchResult] = await Promise.all([
19083
+ pi.exec(
19084
+ "git",
19085
+ ["rev-parse", "--path-format=absolute", "--git-common-dir"],
19086
+ { cwd, signal, timeout: 1e4 }
19087
+ ),
19088
+ pi.exec("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], {
19089
+ cwd,
19090
+ signal,
19091
+ timeout: 1e4
19092
+ })
19093
+ ]);
19094
+ if (common.code !== 0 || common.killed || common.stderr?.trim() || branchResult.code !== 0 || branchResult.killed || branchResult.stderr?.trim() || !isAbsolute5(String(common.stdout ?? "").trim()) || realpathSync5(String(common.stdout).trim()) !== realpathSync5(manager.repoKey) || String(branchResult.stdout ?? "").trim() !== assignment.branch)
19095
+ throw new Error(
19096
+ "Herdr checkout does not match the assigned repository and branch"
19097
+ );
19098
+ const fresh = await currentManager(ctx);
19099
+ if (!fresh || !sameManagerDescriptor2(fresh, manager))
19100
+ throw new Error(
19101
+ "Manager changed before project assignment was published"
19102
+ );
19103
+ await withProjectAssignmentLock(
19104
+ manager.repoKey,
19105
+ assignment.branch,
19106
+ () => {
19107
+ if (readProjectAssignment2(runtime, manager.repoKey, assignment.branch))
19108
+ throw new Error(
19109
+ `Work already exists on ${assignment.branch}; resume it with resume_project.`
19110
+ );
19111
+ writeProjectAssignment2(runtime, assignment);
19112
+ }
19113
+ );
19114
+ }
18941
19115
  await assertUnoccupied(workspaceId);
18942
19116
  let lead;
18943
19117
  let startedLead = false;
@@ -19909,8 +20083,8 @@ function createLeadCoordinationRuntime(host) {
19909
20083
  const enterCoordinationPublication = async () => {
19910
20084
  const previous = coordinationPublication;
19911
20085
  let release;
19912
- coordinationPublication = new Promise((resolve7) => {
19913
- release = resolve7;
20086
+ coordinationPublication = new Promise((resolve8) => {
20087
+ release = resolve8;
19914
20088
  });
19915
20089
  await previous;
19916
20090
  return release;
@@ -20966,12 +21140,75 @@ async function resolveLeadControllerRole(state, ctx, requestedRole, deps, option
20966
21140
  );
20967
21141
  }
20968
21142
  }
21143
+ function gitPathStatus(path) {
21144
+ try {
21145
+ return statSync6(path).isDirectory() ? "present" : "unknown";
21146
+ } catch (error2) {
21147
+ const code = error2.code;
21148
+ return code === "ENOENT" || code === "ENOTDIR" ? "absent" : "unknown";
21149
+ }
21150
+ }
21151
+ function parseGitWorktreeInventory(output) {
21152
+ if (!output.endsWith("\0\0")) return void 0;
21153
+ const fields = output.split("\0");
21154
+ if (fields.pop() !== "") return void 0;
21155
+ const records = [];
21156
+ let record = [];
21157
+ for (const field of fields) {
21158
+ if (field) record.push(field);
21159
+ else if (record.length) {
21160
+ records.push(record);
21161
+ record = [];
21162
+ }
21163
+ }
21164
+ if (record.length) records.push(record);
21165
+ if (!records.length) return void 0;
21166
+ const worktrees = [];
21167
+ for (const values of records) {
21168
+ if (!values[0]?.startsWith("worktree ")) return void 0;
21169
+ let path;
21170
+ let head;
21171
+ let branch;
21172
+ let bare = false;
21173
+ let detached = false;
21174
+ let locked = false;
21175
+ let prunable = false;
21176
+ const seen = /* @__PURE__ */ new Set();
21177
+ for (const value2 of values) {
21178
+ const key = value2.startsWith("worktree ") ? "worktree" : value2.startsWith("HEAD ") ? "HEAD" : value2.startsWith("branch refs/heads/") ? "branch" : value2 === "detached" || value2 === "bare" ? value2 : /^locked(?: |$)/.test(value2) ? "locked" : /^prunable(?: |$)/.test(value2) ? "prunable" : void 0;
21179
+ if (key && seen.has(key)) return void 0;
21180
+ if (key) seen.add(key);
21181
+ if (value2.startsWith("worktree ") && !path) path = value2.slice(9);
21182
+ else if (value2.startsWith("HEAD ") && !head) head = value2.slice(5);
21183
+ else if (value2.startsWith("branch refs/heads/") && !branch)
21184
+ branch = value2.slice("branch refs/heads/".length);
21185
+ else if (value2 === "detached" || value2 === "bare") {
21186
+ if (value2 === "bare") bare = true;
21187
+ else detached = true;
21188
+ } else if (/^locked(?: .*)?$/.test(value2)) locked = true;
21189
+ else if (/^prunable(?: .*)?$/.test(value2)) prunable = true;
21190
+ else if (!/^[^\s]+(?: .*)?$/.test(value2)) return void 0;
21191
+ }
21192
+ if (!path || !isAbsolute5(path) || (bare ? !!branch || detached : !head || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(head) || !branch && !detached) || branch !== void 0 && !branch)
21193
+ return void 0;
21194
+ worktrees.push({
21195
+ path,
21196
+ ...branch ? { branch } : {},
21197
+ ...detached ? { detached: true } : {},
21198
+ ...locked ? { locked: true } : {},
21199
+ ...prunable ? { prunable: true } : {}
21200
+ });
21201
+ }
21202
+ if (new Set(worktrees.map((worktree) => worktree.path)).size !== worktrees.length)
21203
+ return void 0;
21204
+ return worktrees;
21205
+ }
20969
21206
  function createLeadRoleTransitions(state, host) {
20970
21207
  const projectWorkLocks = /* @__PURE__ */ new Map();
20971
21208
  const withProjectWorkLock = async (key, operation) => {
20972
21209
  const previous = projectWorkLocks.get(key);
20973
21210
  let release;
20974
- const current = new Promise((resolve7) => release = resolve7);
21211
+ const current = new Promise((resolve8) => release = resolve8);
20975
21212
  projectWorkLocks.set(key, current);
20976
21213
  await previous;
20977
21214
  try {
@@ -21184,29 +21421,177 @@ function createLeadRoleTransitions(state, host) {
21184
21421
  const assignment = scope ? projectAssignmentForScope(scope, ctx.sessionManager.getSessionId()) : void 0;
21185
21422
  return assignment ? currentManager(ctx, scope) : host.currentChiefAuthority(ctx);
21186
21423
  };
21187
- const removeProjectAssignmentState = (assignment, ctx) => {
21424
+ const removeProjectAssignmentState = (assignment, _ctx) => {
21188
21425
  const runtime = host.supervisionRuntime();
21189
21426
  const current = host.readProjectAssignment(
21190
21427
  runtime,
21191
21428
  assignment.repoKey,
21192
21429
  assignment.branch
21193
21430
  );
21194
- if (!current || current.id !== assignment.id) return false;
21431
+ if (!current || current.repoKey !== assignment.repoKey || current.branch !== assignment.branch || current.id !== assignment.id)
21432
+ return false;
21433
+ host.removeProjectMessages(runtime, assignment.repoKey, assignment.branch);
21195
21434
  host.removeProjectAssignment(
21196
21435
  runtime,
21197
21436
  assignment.repoKey,
21198
21437
  assignment.branch
21199
21438
  );
21439
+ return true;
21440
+ };
21441
+ const gitWorktreeInventory = async (ctx, repoKey, primaryWorkspaceId, sourceCheckoutPath) => {
21442
+ if (!isAbsolute5(sourceCheckoutPath)) return void 0;
21200
21443
  try {
21201
- host.removeProjectMessages(
21202
- runtime,
21203
- assignment.repoKey,
21204
- assignment.branch
21444
+ const scope = await host.worktreeGroupScope(
21445
+ ctx,
21446
+ primaryWorkspaceId,
21447
+ ctx.signal
21205
21448
  );
21206
- } catch (error2) {
21207
- host.appendDurableError(host.pi, ctx, "pi_herdsman_state_error", error2);
21449
+ if (scope.repoKey !== repoKey || scope.primaryWorkspaceId !== primaryWorkspaceId)
21450
+ return void 0;
21451
+ const rootResult = await host.pi.exec(
21452
+ "git",
21453
+ ["rev-parse", "--path-format=absolute", "--git-common-dir"],
21454
+ { cwd: sourceCheckoutPath, signal: ctx.signal, timeout: 1e4 }
21455
+ );
21456
+ if (rootResult.code !== 0 || rootResult.killed || rootResult.stderr?.trim())
21457
+ return void 0;
21458
+ const commonDir = String(rootResult.stdout ?? "").trim();
21459
+ if (!commonDir || !isAbsolute5(commonDir)) return void 0;
21460
+ const result = await host.pi.exec(
21461
+ "git",
21462
+ ["worktree", "list", "--porcelain", "-z"],
21463
+ { cwd: sourceCheckoutPath, signal: ctx.signal, timeout: 1e4 }
21464
+ );
21465
+ if (result.code !== 0 || result.killed || result.stderr?.trim())
21466
+ return void 0;
21467
+ const inventory = parseGitWorktreeInventory(String(result.stdout ?? ""));
21468
+ if (!inventory || realpathSync5(commonDir) !== realpathSync5(repoKey) || gitPathStatus(sourceCheckoutPath) !== "present")
21469
+ return void 0;
21470
+ return inventory;
21471
+ } catch {
21472
+ return void 0;
21208
21473
  }
21209
- return true;
21474
+ };
21475
+ const reconcileProjectAssignments = async (ctx, repoKey, primaryWorkspaceId, sourceCheckoutPath, projectWorkLocked = false, onlyBranch, retryLockContention = false) => {
21476
+ const manager = await currentManager(ctx);
21477
+ if (!manager || manager.repoKey !== repoKey || manager.workspaceId !== primaryWorkspaceId)
21478
+ return;
21479
+ const assignments = listProjectAssignments(
21480
+ host.supervisionRuntime(),
21481
+ repoKey
21482
+ );
21483
+ const initialInventory = await gitWorktreeInventory(
21484
+ ctx,
21485
+ repoKey,
21486
+ primaryWorkspaceId,
21487
+ sourceCheckoutPath
21488
+ );
21489
+ if (!initialInventory) return;
21490
+ const knownCheckoutPath = (assignment) => {
21491
+ if (!assignment.piSessionFile) return void 0;
21492
+ try {
21493
+ const session = host.SessionManager.open(assignment.piSessionFile);
21494
+ if (session.getSessionId() !== assignment.id) return void 0;
21495
+ return resolve6(session.getCwd());
21496
+ } catch {
21497
+ return void 0;
21498
+ }
21499
+ };
21500
+ const checkoutMaySurvive = (assignment, inventory) => {
21501
+ const cwd = knownCheckoutPath(assignment);
21502
+ if (cwd)
21503
+ return inventory.some(
21504
+ (worktree) => resolve6(worktree.path) === cwd && gitPathStatus(worktree.path) !== "absent"
21505
+ );
21506
+ return inventory.some(
21507
+ (worktree) => resolve6(worktree.path) !== resolve6(sourceCheckoutPath) && gitPathStatus(worktree.path) !== "absent"
21508
+ );
21509
+ };
21510
+ for (const observed of assignments) {
21511
+ if (onlyBranch && observed.branch !== onlyBranch) continue;
21512
+ if (checkoutMaySurvive(observed, initialInventory)) continue;
21513
+ const initialMatches = initialInventory.filter(
21514
+ (worktree) => worktree.branch === observed.branch
21515
+ );
21516
+ if (initialMatches.length > 1 || initialMatches.some(
21517
+ (worktree) => worktree.locked || !worktree.prunable || gitPathStatus(worktree.path) !== "absent"
21518
+ ))
21519
+ continue;
21520
+ const retireIfAbsent = async () => {
21521
+ const latestManager = await currentManager(ctx);
21522
+ if (!latestManager || !host.sameManagerDescriptor(latestManager, manager))
21523
+ return;
21524
+ const retireUnderAssignmentLock = async () => {
21525
+ const latestManager2 = await currentManager(ctx);
21526
+ if (!latestManager2 || !host.sameManagerDescriptor(latestManager2, manager))
21527
+ return;
21528
+ const current = host.readProjectAssignment(
21529
+ host.supervisionRuntime(),
21530
+ repoKey,
21531
+ observed.branch
21532
+ );
21533
+ if (!current || current.repoKey !== repoKey || current.branch !== observed.branch || current.id !== observed.id)
21534
+ return;
21535
+ const inventory = await gitWorktreeInventory(
21536
+ ctx,
21537
+ repoKey,
21538
+ primaryWorkspaceId,
21539
+ sourceCheckoutPath
21540
+ );
21541
+ if (!inventory) return;
21542
+ if (checkoutMaySurvive(current, inventory)) return;
21543
+ const matches = inventory.filter(
21544
+ (worktree) => worktree.branch === observed.branch
21545
+ );
21546
+ if (matches.length > 1 || matches.some(
21547
+ (worktree) => worktree.locked || !worktree.prunable || gitPathStatus(worktree.path) !== "absent"
21548
+ ))
21549
+ return;
21550
+ removeProjectAssignmentState(current, ctx);
21551
+ };
21552
+ const deadline = retryLockContention ? Date.now() + 3e4 : 0;
21553
+ for (; ; ) {
21554
+ try {
21555
+ await withProjectAssignmentLock(
21556
+ repoKey,
21557
+ observed.branch,
21558
+ retireUnderAssignmentLock
21559
+ );
21560
+ return;
21561
+ } catch (error2) {
21562
+ if (!(error2 instanceof ProcessLockOccupiedError)) throw error2;
21563
+ if (!retryLockContention) return;
21564
+ if (ctx.signal.aborted) throw ctx.signal.reason;
21565
+ if (Date.now() >= deadline) throw error2;
21566
+ await host.delay(50, void 0, { signal: ctx.signal });
21567
+ }
21568
+ }
21569
+ };
21570
+ if (projectWorkLocked) await retireIfAbsent();
21571
+ else
21572
+ await withProjectWorkLock(
21573
+ `${repoKey}\0${observed.branch}`,
21574
+ retireIfAbsent
21575
+ );
21576
+ }
21577
+ };
21578
+ const reconcileCurrentManagerAssignments = async (ctx) => {
21579
+ const manager = await currentManager(ctx);
21580
+ if (!manager) return;
21581
+ const topology = await host.runHerdr(
21582
+ host.pi,
21583
+ ctx,
21584
+ ["worktree", "list", "--workspace", manager.workspaceId],
21585
+ { signal: ctx.signal }
21586
+ );
21587
+ if (topology?.source?.repo_key !== manager.repoKey || topology?.source?.source_workspace_id !== manager.workspaceId || typeof topology?.source?.source_checkout_path !== "string")
21588
+ throw new Error("Manager worktree topology is not authoritative");
21589
+ await reconcileProjectAssignments(
21590
+ ctx,
21591
+ manager.repoKey,
21592
+ manager.workspaceId,
21593
+ topology.source.source_checkout_path
21594
+ );
21210
21595
  };
21211
21596
  const takeover = async (ctx) => {
21212
21597
  if (activeLeadRole(state) !== "lead" || state.chiefMode === "active" || state.roleSuspended)
@@ -21245,38 +21630,31 @@ function createLeadRoleTransitions(state, host) {
21245
21630
  return true;
21246
21631
  };
21247
21632
  const retireRemovedProjectWork = async (removed, ctx, signal) => {
21248
- const runtime = host.supervisionRuntime();
21249
- const observed = host.readProjectAssignment(
21250
- runtime,
21251
- removed.repoKey,
21252
- removed.branch
21253
- );
21254
- if (!observed) return;
21255
- const scope = await currentWorktreeScope(ctx);
21633
+ let scope;
21634
+ try {
21635
+ scope = await currentWorktreeScope(ctx);
21636
+ } catch (error2) {
21637
+ if (error2 instanceof OperationError && error2.detail.details?.herdrCode === "workspace_not_found")
21638
+ return;
21639
+ throw error2;
21640
+ }
21256
21641
  if (!scope || scope.repoKey !== removed.repoKey) return;
21257
- await withProjectWorkLock(
21258
- `${removed.repoKey}\0${removed.branch}`,
21259
- async () => {
21260
- const deadline = Date.now() + 3e4;
21261
- for (; ; ) {
21262
- if (signal.aborted)
21263
- throw signal.reason ?? new Error("operation aborted");
21264
- try {
21265
- await withProjectAssignmentLock(
21266
- removed.repoKey,
21267
- removed.branch,
21268
- async () => {
21269
- removeProjectAssignmentState(observed, ctx);
21270
- }
21271
- );
21272
- return;
21273
- } catch (error2) {
21274
- if (!(error2 instanceof ProcessLockOccupiedError) || Date.now() >= deadline)
21275
- throw error2;
21276
- await host.delay(50, void 0, { signal });
21277
- }
21278
- }
21279
- }
21642
+ const topology = await host.runHerdr(
21643
+ host.pi,
21644
+ ctx,
21645
+ ["worktree", "list", "--workspace", scope.primaryWorkspaceId],
21646
+ { signal }
21647
+ );
21648
+ if (topology?.source?.repo_key !== removed.repoKey || topology?.source?.source_workspace_id !== scope.primaryWorkspaceId || typeof topology?.source?.source_checkout_path !== "string")
21649
+ return;
21650
+ await reconcileProjectAssignments(
21651
+ ctx,
21652
+ removed.repoKey,
21653
+ scope.primaryWorkspaceId,
21654
+ topology.source.source_checkout_path,
21655
+ false,
21656
+ removed.branch,
21657
+ true
21280
21658
  );
21281
21659
  };
21282
21660
  const beginShutdownRole = () => {
@@ -21545,6 +21923,11 @@ function createLeadRoleTransitions(state, host) {
21545
21923
  host.advancePeerPresenceGeneration();
21546
21924
  if (host.coordinationHealthy()) await host.schedulePeerPresence(ctx);
21547
21925
  await host.publishLeadRole(ctx, "inactive", state.chiefModeGeneration);
21926
+ try {
21927
+ await reconcileCurrentManagerAssignments(ctx);
21928
+ } catch (error2) {
21929
+ host.appendDurableError(host.pi, ctx, "pi_herdsman_state_error", error2);
21930
+ }
21548
21931
  return "Manager mode active.";
21549
21932
  } catch (error2) {
21550
21933
  state.controllerRole = "lead";
@@ -21886,11 +22269,13 @@ function createLeadRoleTransitions(state, host) {
21886
22269
  withProjectAssignmentLock,
21887
22270
  publishProjectMessage,
21888
22271
  currentManager,
22272
+ reconcileCurrentManagerAssignments,
21889
22273
  currentChief,
21890
22274
  currentChiefAuthority,
21891
22275
  currentSupervisor,
21892
22276
  takeover,
21893
22277
  retireRemovedProjectWork,
22278
+ reconcileProjectAssignments,
21894
22279
  clearLeadContext: () => {
21895
22280
  state.leadContext = void 0;
21896
22281
  }
@@ -22560,9 +22945,9 @@ Use reply_agent with agent="${ask.agentLabel}" to answer this question.`,
22560
22945
  const reconcileRoleTools = () => roleTransitions.reconcileRoleTools();
22561
22946
  const isCurrentChief = (ctx) => leadRuntime.chiefMode === "active" && !!leadRuntime.chiefLease && leadRuntime.chiefLease.descriptor.piSessionId === ctx.sessionManager.getSessionId();
22562
22947
  const chiefSystemPrompt = (options) => {
22563
- const agentDir = resolve6(getAgentDir4());
22948
+ const agentDir = resolve7(getAgentDir4());
22564
22949
  const globalInstructions = (options?.contextFiles ?? []).filter(
22565
- ({ path, content }) => typeof path === "string" && typeof content === "string" && resolve6(dirname7(path)) === agentDir && content.length > 0
22950
+ ({ path, content }) => typeof path === "string" && typeof content === "string" && resolve7(dirname7(path)) === agentDir && content.length > 0
22566
22951
  ).map(({ content }) => content).join("\n\n");
22567
22952
  return [
22568
22953
  CHIEF_ROLE_CHARTER,
@@ -22597,6 +22982,7 @@ ${globalInstructions}
22597
22982
  paneId: () => process.env.HERDR_PANE_ID,
22598
22983
  getSessionName: () => pi.getSessionName(),
22599
22984
  runHerdr,
22985
+ SessionManager: SessionManager2,
22600
22986
  reportLeadMetadata,
22601
22987
  workspaceId: () => process.env.HERDR_WORKSPACE_ID,
22602
22988
  identity: {
@@ -23310,7 +23696,6 @@ ${summary}`,
23310
23696
  appendDurableError
23311
23697
  },
23312
23698
  statusSnapshotHost: {
23313
- hasStateIssues: () => listAgentStateIssues().length > 0,
23314
23699
  environmentIdentity: (ctx) => managedAgentEnvironmentIdentity(ctx, HERDSMAN_BUILD),
23315
23700
  identityFromEnvironment: () => ({
23316
23701
  definition: process.env.PI_HERDSMAN_AGENT_DEFINITION,